[RFC / Experiment] Port jsPDF to TypeScript 7 - #4003
Open
MrRio wants to merge 10 commits into
Open
Conversation
Faithful mechanical conversion of all 6179 lines: function-style constructors, closure-scoped API, PubSub, @if MODULE_FORMAT directive blocks and the "0.0.0" version literal all preserved verbatim. Minimal type annotations (any-typed containers, optional trailing params, casts on catch variables) only where the TypeScript 7 compiler required them. Unit coverage of the converted file is identical to the JS baseline.
…x.ts Adds test/utils/api-parity.js which diffs named exports, jsPDF statics, jsPDF.API keys and instance own+prototype keys between a reference surface dump and a candidate cjs bundle. Verified identical to the pre-port master build.
- remove now-empty .js coverage preprocessor entries - CONTRIBUTING.md: TS7 toolchain, .js-specifier convention, erasable-syntax rule, vendored-file exceptions, api-parity usage
src/polyfills.js imports ./libs/Blob (extensionless) and ./libs/globalObject.js, both now TypeScript. The two polyfill bundle configs never got the tsResolve/type-stripping plugins, so the build broke once those libs converted. Extend tsResolve() to handle extensionless specifiers and share one babelStripTypes() helper across all bundles.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
This is an experiment / RFC, not a merge-ready proposal. It explores what a full TypeScript port of jsPDF looks like with the TypeScript 7 native compiler. It may well never be merged — feedback on whether this direction is worth pursuing is the point.
What this does
Ports all of
src/(the 6,179-line core, all 29 plugin modules, all libs) from JavaScript to TypeScript, type-checked by TypeScript 7 (typescript@7.0.2, the native Go compiler), while keeping the public API surface byte-for-byte identical and the file/module structure unchanged.Because TS 7 no longer exposes a JS compiler API,
@rollup/plugin-typescriptandkarma-typescriptcan't be used. Instead:npm run typecheck(tsc --noEmit, native binary — the full-repo check runs in well under a second).@babel/preset-typescript, wired into all three rollup bundles and into karma (a custom preprocessor serves transpiled.tsunder its original.jsURL, so browser-native ES-module imports keep resolving)..js(import { jsPDF } from "../jspdf.js"), resolved to.tsby tsc natively and by a 10-line rollup plugin — meaning no churn in specs or import sites.The conversion is deliberately mechanical: function-style constructors, prototype assignment, the IIFE plugin pattern and the
// @if MODULE_FORMATpreprocess directives are preserved verbatim. Type annotations were added only where the compiler required them (pragmaticanyat dynamic sites);strictNullChecks/noImplicitAnyare off for now and can be ratcheted up incrementally later. Two vendored/generated files (WebPDecoder,ttffont) are@ts-nocheckwith justification.Verification
test/utils/api-parity.jsdiffs named exports,jsPDFstatics,jsPDF.APIkeys, and instance+prototype keys against a pre-port master build — zero drift (17 exports / 5 statics / 65 API keys / 170 instance keys).test-typingsagainst the unchanged hand-writtentypes/index.d.ts.PDFSecurity's invalid-permission check reads the literal key"perm"and can never throw); all are preserved as-is for behavior parity and flagged with comments.Pros of merging
types/index.d.ts(1,471 lines) is maintained by hand; long-term, declarations could be generated from source and the hand-written file retired..jsspecifiers and file layout unchanged, so git history survives via renames.strictNullChecks,noImplicitAny) without another big-bang migration.Cons of merging
.tstree by hand. This is the biggest cost, and it only gets cheaper by merging quickly or never.anyannotations and casts at dynamic sites, plus two@ts-nocheckvendored files. Real type-safety gains need follow-up strictness work; until then the types are mostly scaffolding.tsc-emit path. Anything that parsessrc/**/*.jsby convention (external tooling, jsdoc pipelines) may need adjusting.git blameacross the conversion commit needs-w --follow/.git-blame-ignore-revshygiene..d.tsstill shipped, giving some maintenance duplication until declaration generation is done.If this direction is rejected
The branch still yielded standalone value that could be cherry-picked: the ~90 new lib unit tests, the API-parity checker, and the latent-bug findings.
What stays JavaScript, and why
A sweep of the remaining
.jsfiles (excludingnode_modules,dist,docs, generated coverage):src/exceptionslibs/fflate.js,libs/fast-png.js(1–2-line re-export shims),polyfills.js,license.jspolyfills.jsis a separate entry of barecore-jsimports — nothing to type.license.jsis a banner template, not a module.rollup.config.js,modules.conf.js,cli.js,deletedocs.js,.eslintrc.js.tsconfig, eslint requires a JS config, and typing ~700 lines of build scripts buys nothing for library correctness.test/specs/*.spec.js+.spec.mjstypes/index.d.tswould turn every test into a typings test and catch declaration drift. But it's ~50 files of churn (doubling the open-PR conflict problem this RFC already has), and both karma and the Node jasmine run would need preprocessor/glob work. Best done as its own incremental effort after a decision on this PR, converting specs opportunistically as they're touched.test/utils/*, deployment harnessesexamples/,fontconverter/Net:
src/is 100% TypeScript except four deliberate, documented exceptions; everything else that remains.jsis tooling, tests, or docs where conversion has negative or deferred value.