Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .babelrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"presets": [["@babel/env"]],
"presets": [
["@babel/env"],
["@babel/preset-typescript", { "allowDeclareFields": true }]
],
"exclude": ["node_modules/**"]
}
26 changes: 26 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,32 @@ npm run test-unit # will run only unit tests
npm run test-local # will also run deployment tests for different module formats using the files in the dist folder
```

### TypeScript

The source in `src/` is TypeScript, type-checked by the native TypeScript 7
compiler (`npm run typecheck`). Because TS 7 has no JavaScript compiler API,
bundling and test runs strip types with `@babel/preset-typescript` instead:
rollup does this for the dist bundles and karma does it (via the `babelTS`
custom preprocessor) when serving sources to the browser.

Conventions:

- Relative imports keep their historical `.js` extension even though the
files are `.ts` (`import { jsPDF } from "../jspdf.js"`). tsc resolves these
natively, rollup maps them via the `tsResolve()` plugin, and karma serves
transpiled `.ts` files under their `.js` URL.
- Only erasable TypeScript syntax is allowed (`erasableSyntaxOnly`): no enums,
namespaces or parameter properties. Type-stripping must never change
runtime behavior.
- `src/libs/fflate.js`, `src/libs/fast-png.js`, `src/license.js` and
`src/polyfills.js` intentionally stay JavaScript.
- `src/libs/WebPDecoder.ts` and `src/libs/ttffont.ts` are vendored/generated
code and are excluded from type-checking with `@ts-nocheck`.
- The `// @if MODULE_FORMAT` comment directives are processed by
rollup-plugin-preprocess at build time and must be preserved verbatim.
- `node test/utils/api-parity.js <reference> <candidate>` compares the public
API surface of two builds; CI-facing changes should keep it identical.

The tests live in the `test` folder and are a set of `specs` that sometimes compare the result with checked-in
reference PDF files. New reference PDFs can be created by running `npm run test-training` in the background.

Expand Down
2,430 changes: 648 additions & 1,782 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@
"@babel/core": "^7.10.4",
"@babel/plugin-transform-runtime": "^7.14.5",
"@babel/preset-env": "^7.10.4",
"@babel/preset-typescript": "^7.27.1",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-replace": "^2.3.3",
"@rollup/plugin-terser": "^1.0.0",
"@rollup/plugin-typescript": "^8.0.0",
"@types/jasmine": "^3.5.11",
"@types/node": "^20.16.5",
"@typescript-eslint/eslint-plugin": "^3.6.0",
Expand Down Expand Up @@ -78,7 +78,6 @@
"karma-jasmine-matchers": "4.0.2",
"karma-mocha-reporter": "2.2.5",
"karma-rollup-preprocessor": "^7.0.7",
"karma-typescript": "^5.5.4",
"karma-verbose-reporter": "0.0.6",
"local-web-server": "^5.4.0",
"log-utils": "^1.0.0",
Expand All @@ -92,7 +91,7 @@
"rollup-plugin-license": "^2.1.0",
"rollup-plugin-node-resolve": "5.2.0",
"rollup-plugin-preprocess": "0.0.4",
"typescript": "^5.6.2",
"typescript": "~7.0.2",
"yarpm": "^0.2.1"
},
"scripts": {
Expand All @@ -107,11 +106,12 @@
"test-amd": "karma start test/deployment/amd/karma.conf.js --single-run",
"test-esm": "karma start test/deployment/esm/karma.conf.js --single-run",
"test-globals": "karma start test/deployment/globals/karma.conf.js --single-run",
"test-typescript": "karma start test/deployment/typescript/karma.conf.js --single-run",
"test-typescript": "tsc --noEmit -p test/deployment/typescript/tsconfig.json && karma start test/deployment/typescript/karma.conf.js --single-run",
"test-webworker": "karma start test/deployment/webworker/karma.conf.js --single-run",
"test-node": "jasmine --config=test/deployment/node/jasmine.json",
"test-training": "node test/utils/reference-server.js",
"test-typings": "tsc -p types/tsconfig.json && tsc -p types/tsconfig-node.json",
"typecheck": "tsc --noEmit -p tsconfig.json",
"prettier": "prettier --write \"*.{js,ts,md,css,json}\" \"{spec,examples,src,types}/**/*.{js,ts,md,css,json}\"",
"lint": "prettier --check \"*.{js,ts,md,css,json}\" \"{spec,examples,src,types}/**/*.{js,ts,md,css,json}\"",
"pregenerate-docs": "node deletedocs.js",
Expand Down
77 changes: 68 additions & 9 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,51 @@ import commonjs from "rollup-plugin-commonjs";
import replace from "@rollup/plugin-replace";
import license from "rollup-plugin-license";
import pkg from "./package.json";
import fs from "fs";
import path from "path";

// TypeScript sources keep their original ".js" (or extensionless) import
// specifiers; resolve them to the ".ts" file when it exists.
function tsResolve() {
return {
name: "ts-resolve",
resolveId(source, importer) {
if (!importer || !/^\.\.?\//.test(source)) {
return null;
}
const base = source.endsWith(".js") ? source.slice(0, -3) : source;
const ts = path.resolve(path.dirname(importer), base + ".ts");
return fs.existsSync(ts) ? ts : null;
}
};
}

// Type-stripping only, and only for .ts files — .js sources pass through
// untouched.
function babelStripTypes() {
return babel({
babelHelpers: "bundled",
babelrc: false,
configFile: false,
include: ["**/*.ts"],
presets: [["@babel/preset-typescript", { allowDeclareFields: true }]],
extensions: BABEL_EXTENSIONS,
skipPreflightCheck: true
});
}

const BABEL_EXTENSIONS = [".js", ".mjs", ".ts"];

// rollup-plugin-preprocess defaults to include: ["**/*.js"] and infers the
// preprocess rule set from the file extension (there is no "ts" rule), so
// both must be set explicitly or directives in .ts files are silently skipped.
function preprocessPlugin(format) {
return RollupPluginPreprocess({
include: ["**/*.js", "**/*.ts"],
options: { type: "js" },
context: { MODULE_FORMAT: format }
});
}

function replaceVersion() {
return replace({
Expand Down Expand Up @@ -57,7 +102,7 @@ const externals = matchSubmodules([
]);

const umd = {
input: "src/index.js",
input: "src/index.ts",
output: [
{
file: "dist/jspdf.umd.js",
Expand All @@ -77,17 +122,22 @@ const umd = {
],
external: umdExternals,
plugins: [
tsResolve(),
resolve(),
commonjs(),
RollupPluginPreprocess({ context: { MODULE_FORMAT: "umd" } }),
preprocessPlugin("umd"),
replaceVersion(),
babel({ babelHelpers: "bundled", configFile: "./.babelrc.json" }),
babel({
babelHelpers: "bundled",
configFile: "./.babelrc.json",
extensions: BABEL_EXTENSIONS
}),
licenseBanner()
]
};

const es = {
input: "src/index.js",
input: "src/index.ts",
output: [
{
file: pkg.module.replace(".min", ""),
Expand All @@ -106,15 +156,20 @@ const es = {
],
external: externals,
plugins: [
tsResolve(),
resolve(),
RollupPluginPreprocess({ context: { MODULE_FORMAT: "es" } }),
preprocessPlugin("es"),
replaceVersion(),
babel({ babelHelpers: "runtime", configFile: "./.babelrc.esm.json" }),
babel({
babelHelpers: "runtime",
configFile: "./.babelrc.esm.json",
extensions: BABEL_EXTENSIONS
}),
licenseBanner()
]
};
const node = {
input: "src/index.js",
input: "src/index.ts",
output: [
{
file: pkg.main.replace(".min", ""),
Expand All @@ -135,9 +190,11 @@ const node = {
],
external: externals,
plugins: [
tsResolve(),
resolve(),
RollupPluginPreprocess({ context: { MODULE_FORMAT: "cjs" } }),
preprocessPlugin("cjs"),
replaceVersion(),
babelStripTypes(),
licenseBanner()
]
};
Expand All @@ -154,8 +211,10 @@ const umdPolyfills = {
],
external: [],
plugins: [
tsResolve(),
resolve(),
commonjs(),
babelStripTypes(),
license({
banner: {
content: { file: "./node_modules/core-js/LICENSE" }
Expand All @@ -176,7 +235,7 @@ const esPolyfills = {
}
],
external: externals,
plugins: [licenseBanner()]
plugins: [tsResolve(), babelStripTypes(), licenseBanner()]
};

function matchSubmodules(externals) {
Expand Down
File renamed without changes.
Loading