Published 2 months ago

TypeScript Refactor of a 50,000-Star JavaScript PDF Library: A 100-Day Journey

Software Development
TypeScript Refactor of a 50,000-Star JavaScript PDF Library: A 100-Day Journey

TypeScript Refactor of a 50,000-Star JavaScript PDF Library: A 100-Day Journey

Displaying PDFs on the web is a common task, but existing libraries often fall short when integrating with backend systems. This post details a 100-day project refactoring a popular JavaScript PDF library (pdf.js) with TypeScript to enhance its developer experience and address common integration challenges.

Challenges of Using pdf.js in Application Systems

Many developers face issues when integrating pdf.js into application systems, including:

  • Difficult backend interaction
  • Limited programmatic control over PDF reader functions
  • Complex, hard-to-maintain source code
  • Insufficient documentation

Project Goals and Approach

This project aimed to create an advanced version of pdf.js, focusing on two key goals: strengthening the API and documentation for seamless integration, and developing missing or imperfect features (such as richer annotation systems and PDF permission management).

Initial attempts at minor improvements proved ineffective due to pdf.js's large codebase (~100,000 lines of code). A major refactor using TypeScript, pnpm, vite, and a monorepo structure was adopted to address the challenges.

Refactoring with TypeScript, pnpm, and Vite

The refactoring involved a complete overhaul of the codebase, shifting to TypeScript for improved type safety and maintainability. The build process was modernized using pnpm and Vite, and the project structure was reorganized as a monorepo for better modularity and collaboration.

The image below illustrates the new monorepo structure:

/uploads/image_4cf50ec8fa.png

Illustrative Example: Simple PDF Viewer Integration

The refactored library provides a clean API for developers to integrate a PDF viewer into their applications. Here's a basic example demonstrating how to initialize, open, and control a PDF reader:


const viewer = WebSerenViewer.init('app', { viewerScale: 0.7 });

viewer.open({
  url: 'compressed.tracemonkey-pldi-09.pdf',
  verbosity: VerbosityLevel.WARNINGS
}).then(() => {
  const controller = viewer.getViewController();
  bindEvents(controller);
})

function bindEvents(controller: WebViewerController) {
  document.getElementById("pdf-page-up")?.addEventListener("click", () => {
    controller.pageUp();
  })
  document.getElementById("pdf-page-down")?.addEventListener("click", () => {
    controller.pageDown();
  })
}

This example showcases a simplified approach to integrating a PDF viewer compared to using an iframe.

Here is an animation demonstrating the first working example:

/uploads/image_9e5e362861.gif

Addressing Code Quality Issues

The refactoring process involved significant improvements to code quality. This included:

  • Replacing JavaScript with TypeScript
  • Eliminating hard-coded values
  • Improving function parameter clarity
  • Converting process-oriented code to object-oriented code

Here's a before-and-after comparison illustrating the improved code clarity and maintainability:

Before Refactor (JavaScript):


handler.on("GetPage", function (data) {
  return pdfManager.getPage(data.pageIndex).then(function (page) {
    return Promise.all([
      pdfManager.ensure(page, "rotate"),
      pdfManager.ensure(page, "ref"),
      pdfManager.ensure(page, "userUnit"),
      pdfManager.ensure(page, "view"),
    ]).then(function ([rotate, ref, userUnit, view]) {
      return {
        rotate,
        ref,
        refStr: ref?.toString() ?? null,
        userUnit,
        view,
      };
    });
  });
});

After Refactor (TypeScript):


handler.onGetPage(async data => {
  return pdfManager!.getPage(data.pageIndex).then(async page => {
    return Promise.all([
      pdfManager!.ensure(page, page => page.rotate),
      pdfManager!.ensure(page, page => page.ref),
      pdfManager!.ensure(page, page => page.userUnit),
      pdfManager!.ensure(page, page => page.view),
    ]).then(([rotate, ref, userUnit, view]) => {
      return { rotate, ref, refStr: ref?.toString() ?? null, userUnit, view };
    });
  });
});

Conclusion and Next Steps

This 100-day project successfully refactored a large JavaScript library, resulting in a more maintainable, extensible, and developer-friendly codebase. The next steps include comprehensive testing and the completion of API and callback event rewriting. The project code is hosted on GitHub: https://github.com/xingshen24/seren-pdf

To start using the library, clone the repository, run pnpm recursive install, navigate to the packages-private/seren-viewer-develop/ directory, and run pnpm run dev.

Hashtags: #TypeScript # JavaScript # PDF # Refactoring # Monorepo # Pnpm # Vite # WebViewer # API # Documentation # Software Development # Codebase

Related Articles

thumb_nail_Unveiling the Haiku License: A Fair Code Revolution

Software Development

Unveiling the Haiku License: A Fair Code Revolution

Dive into the innovative Haiku License, a game-changer in open-source licensing that balances open access with fair compensation for developers. Learn about its features, challenges, and potential to reshape the software development landscape. Explore now!

Read More
thumb_nail_Leetcode - 1. Two Sum

Software Development

Leetcode - 1. Two Sum

Master LeetCode's Two Sum problem! Learn two efficient JavaScript solutions: the optimal hash map approach and a practical two-pointer technique. Improve your coding skills today!

Read More
thumb_nail_The Future of Digital Credentials in 2025: Trends, Challenges, and Opportunities

Business, Software Development

The Future of Digital Credentials in 2025: Trends, Challenges, and Opportunities

Digital credentials are transforming industries in 2025! Learn about blockchain's role, industry adoption trends, privacy enhancements, and the challenges and opportunities shaping this exciting field. Discover how AI and emerging technologies are revolutionizing identity verification and workforce management. Explore the future of digital credentials today!

Read More
Your Job, Your Community
logo
© All rights reserved 2024