From 38245500fde2ac10c993c1690057cacd6b2b0383 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Fri, 13 Oct 2023 12:11:29 +0200 Subject: [PATCH] Output JavaScript modules for the `LIB` build-target (PR 17055 follow-up) This *finally* allows us to mark the entire PDF.js library as a "module", which should thus conclude the (multi-year) effort to re-factor and improve how we import files/resources in the code-base. This also means that the `gulp ci-test` target, which is what's run in GitHub Actions, now uses JavaScript modules since that's supported in modern Node.js versions. --- extensions/firefox/.eslintrc | 4 --- gulpfile.mjs | 42 +++++++++-------------- package-lock.json | 7 ---- package.json | 2 +- src/display/node_stream.js | 18 ++++++---- src/display/node_utils.js | 63 +++++++++-------------------------- test/stats/statcmp.js | 5 +-- test/unit/node_stream_spec.js | 10 +++--- test/unit/test_utils.js | 9 +++-- 9 files changed, 58 insertions(+), 102 deletions(-) diff --git a/extensions/firefox/.eslintrc b/extensions/firefox/.eslintrc index 84a962caf..e5ae68046 100644 --- a/extensions/firefox/.eslintrc +++ b/extensions/firefox/.eslintrc @@ -6,10 +6,6 @@ "plugin:mozilla/recommended", ], - "parserOptions": { - "sourceType": "module", - }, - "plugins": [ "mozilla" ], diff --git a/gulpfile.mjs b/gulpfile.mjs index 405374f68..7da95fea1 100644 --- a/gulpfile.mjs +++ b/gulpfile.mjs @@ -222,9 +222,7 @@ function createWebpackConfig( }, ], ]; - const babelPlugins = isModule - ? [] - : ["@babel/plugin-transform-modules-commonjs"]; + const babelPlugins = []; const plugins = []; if (!disableLicenseHeader) { @@ -1522,18 +1520,11 @@ gulp.task("types", function (done) { }); function buildLibHelper(bundleDefines, inputStream, outputDir) { - // When we create a bundle, webpack is run on the source and it will replace - // require with __webpack_require__. When we want to use the real require, - // __non_webpack_require__ has to be used. - // In this target, we don't create a bundle, so we have to replace the - // occurrences of __non_webpack_require__ ourselves. - function babelPluginReplaceNonWebpackImports(b) { + function babelPluginReplaceNonWebpackImport(b) { return { visitor: { Identifier(curPath, state) { - if (curPath.node.name === "__non_webpack_require__") { - curPath.replaceWith(b.types.identifier("require")); - } else if (curPath.node.name === "__non_webpack_import__") { + if (curPath.node.name === "__non_webpack_import__") { curPath.replaceWith(b.types.identifier("import")); } }, @@ -1545,18 +1536,15 @@ function buildLibHelper(bundleDefines, inputStream, outputDir) { content = preprocessPDFJSCode(ctx, content); content = babel.transform(content, { sourceType: "module", - presets: skipBabel ? undefined : ["@babel/preset-env"], - plugins: [ - "@babel/plugin-transform-modules-commonjs", - babelPluginReplaceNonWebpackImports, - ], + presets: skipBabel + ? undefined + : [["@babel/preset-env", { loose: false, modules: false }]], + plugins: [babelPluginReplaceNonWebpackImport], targets: BABEL_TARGETS, }).code; - const removeCjsSrc = - /^(var\s+\w+\s*=\s*(_interopRequireDefault\()?require\(".*?)(?:\/src)(\/[^"]*"\)\)?;)$/gm; content = content.replaceAll( - removeCjsSrc, - (all, prefix, interop, suffix) => prefix + suffix + /(\sfrom\s".*?)(?:\/src)(\/[^"]*"?;)$/gm, + (all, prefix, suffix) => prefix + suffix ); return licenseHeaderLibre + content; } @@ -1565,12 +1553,12 @@ function buildLibHelper(bundleDefines, inputStream, outputDir) { saveComments: false, defines: bundleDefines, map: { - "pdfjs-lib": "../pdf", - "display-fetch_stream": "./fetch_stream", - "display-l10n_utils": "../web/l10n_utils", - "display-network": "./network", - "display-node_stream": "./node_stream", - "display-node_utils": "./node_utils", + "pdfjs-lib": "../pdf.js", + "display-fetch_stream": "./fetch_stream.js", + "display-l10n_utils": "../web/l10n_utils.js", + "display-network": "./network.js", + "display-node_stream": "./node_stream.js", + "display-node_utils": "./node_utils.js", }, }; const licenseHeaderLibre = fs diff --git a/package-lock.json b/package-lock.json index 0ce9e8f1c..0d5b4e100 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "license": "Apache-2.0", "devDependencies": { "@babel/core": "^7.22.20", - "@babel/plugin-transform-modules-commonjs": "^7.22.15", "@babel/preset-env": "^7.22.20", "@babel/runtime": "^7.22.15", "@javascript-obfuscator/escodegen": "2.3.0", @@ -13727,7 +13726,6 @@ }, "node_modules/npm/node_modules/lodash._baseindexof": { "version": "3.1.0", - "dev": true, "inBundle": true, "license": "MIT" }, @@ -13743,19 +13741,16 @@ }, "node_modules/npm/node_modules/lodash._bindcallback": { "version": "3.0.1", - "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/lodash._cacheindexof": { "version": "3.0.2", - "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/lodash._createcache": { "version": "3.1.2", - "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -13770,7 +13765,6 @@ }, "node_modules/npm/node_modules/lodash._getnative": { "version": "3.9.1", - "dev": true, "inBundle": true, "license": "MIT" }, @@ -13788,7 +13782,6 @@ }, "node_modules/npm/node_modules/lodash.restparam": { "version": "3.6.1", - "dev": true, "inBundle": true, "license": "MIT" }, diff --git a/package.json b/package.json index e15cf726a..d4571294d 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "pdf.js", + "type": "module", "devDependencies": { "@babel/core": "^7.22.20", - "@babel/plugin-transform-modules-commonjs": "^7.22.15", "@babel/preset-env": "^7.22.20", "@babel/runtime": "^7.22.15", "@javascript-obfuscator/escodegen": "2.3.0", diff --git a/src/display/node_stream.js b/src/display/node_stream.js index 3a8c34d09..ea182fff3 100644 --- a/src/display/node_stream.js +++ b/src/display/node_stream.js @@ -12,10 +12,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/* globals __non_webpack_import__ */ import { AbortException, assert, + isNodeJS, MissingPDFException, PromiseCapability, } from "../shared/util.js"; @@ -30,10 +32,18 @@ if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { ); } +let fs, http, https, url; +if (isNodeJS) { + // Native packages. + fs = await __non_webpack_import__("fs"); + http = await __non_webpack_import__("http"); + https = await __non_webpack_import__("https"); + url = await __non_webpack_import__("url"); +} + const fileUriRegex = /^file:\/\/\/[a-zA-Z]:\//; function parseUrl(sourceUrl) { - const { url } = globalThis.__pdfjsPackages__; const parsedUrl = url.parse(sourceUrl); if (parsedUrl.protocol === "file:" || parsedUrl.host) { return parsedUrl; @@ -339,13 +349,11 @@ class PDFNodeStreamFullReader extends BaseFullReader { this._request = null; if (this._url.protocol === "http:") { - const { http } = globalThis.__pdfjsPackages__; this._request = http.request( createRequestOptions(this._url, stream.httpHeaders), handleResponse ); } else { - const { https } = globalThis.__pdfjsPackages__; this._request = https.request( createRequestOptions(this._url, stream.httpHeaders), handleResponse @@ -388,13 +396,11 @@ class PDFNodeStreamRangeReader extends BaseRangeReader { this._request = null; if (this._url.protocol === "http:") { - const { http } = globalThis.__pdfjsPackages__; this._request = http.request( createRequestOptions(this._url, this._httpHeaders), handleResponse ); } else { - const { https } = globalThis.__pdfjsPackages__; this._request = https.request( createRequestOptions(this._url, this._httpHeaders), handleResponse @@ -419,7 +425,6 @@ class PDFNodeStreamFsFullReader extends BaseFullReader { path = path.replace(/^\//, ""); } - const { fs } = globalThis.__pdfjsPackages__; fs.lstat(path, (error, stat) => { if (error) { if (error.code === "ENOENT") { @@ -449,7 +454,6 @@ class PDFNodeStreamFsRangeReader extends BaseRangeReader { path = path.replace(/^\//, ""); } - const { fs } = globalThis.__pdfjsPackages__; this._setReadableStream(fs.createReadStream(path, { start, end: end - 1 })); } } diff --git a/src/display/node_utils.js b/src/display/node_utils.js index 07abc6cd2..c436a6435 100644 --- a/src/display/node_utils.js +++ b/src/display/node_utils.js @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals __non_webpack_import__, __non_webpack_require__ */ +/* globals __non_webpack_import__ */ import { BaseCanvasFactory, @@ -28,46 +28,17 @@ if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { ); } -if (isNodeJS && !globalThis.__pdfjsPackages__) { - let fs, http, https, url, canvas, path2d_polyfill; - - if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("LIB")) { - // Native packages. - fs = __non_webpack_require__("fs"); - http = __non_webpack_require__("http"); - https = __non_webpack_require__("https"); - url = __non_webpack_require__("url"); - // Optional, third-party, packages. - try { - canvas = __non_webpack_require__("canvas"); - } catch {} - try { - path2d_polyfill = __non_webpack_require__("path2d-polyfill"); - } catch {} - } else { - // Native packages. - fs = await __non_webpack_import__("fs"); - http = await __non_webpack_import__("http"); - https = await __non_webpack_import__("https"); - url = await __non_webpack_import__("url"); - // Optional, third-party, packages. - try { - canvas = await __non_webpack_import__("canvas"); - } catch {} - try { - path2d_polyfill = await __non_webpack_import__("path2d-polyfill"); - } catch {} - } - globalThis.__pdfjsPackages__ = { - CanvasRenderingContext2D: canvas?.CanvasRenderingContext2D, - createCanvas: canvas?.createCanvas, - DOMMatrix: canvas?.DOMMatrix, - fs, - http, - https, - polyfillPath2D: path2d_polyfill?.polyfillPath2D, - url, - }; +let fs, canvas, path2d_polyfill; +if (isNodeJS) { + // Native packages. + fs = await __non_webpack_import__("fs"); + // Optional, third-party, packages. + try { + canvas = await __non_webpack_import__("canvas"); + } catch {} + try { + path2d_polyfill = await __non_webpack_import__("path2d-polyfill"); + } catch {} } if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("SKIP_BABEL")) { @@ -75,7 +46,7 @@ if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("SKIP_BABEL")) { if (globalThis.DOMMatrix || !isNodeJS) { return; } - const { DOMMatrix } = globalThis.__pdfjsPackages__; + const DOMMatrix = canvas?.DOMMatrix; if (DOMMatrix) { globalThis.DOMMatrix = DOMMatrix; @@ -88,8 +59,8 @@ if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("SKIP_BABEL")) { if (globalThis.Path2D || !isNodeJS) { return; } - const { CanvasRenderingContext2D, polyfillPath2D } = - globalThis.__pdfjsPackages__; + const CanvasRenderingContext2D = canvas?.CanvasRenderingContext2D; + const polyfillPath2D = path2d_polyfill?.polyfillPath2D; if (CanvasRenderingContext2D && polyfillPath2D) { globalThis.CanvasRenderingContext2D = CanvasRenderingContext2D; @@ -102,7 +73,6 @@ if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("SKIP_BABEL")) { const fetchData = function (url) { return new Promise((resolve, reject) => { - const { fs } = globalThis.__pdfjsPackages__; fs.readFile(url, (error, data) => { if (error || !data) { reject(new Error(error)); @@ -120,8 +90,7 @@ class NodeCanvasFactory extends BaseCanvasFactory { * @ignore */ _createCanvas(width, height) { - const { createCanvas } = globalThis.__pdfjsPackages__; - return createCanvas(width, height); + return canvas.createCanvas(width, height); } } diff --git a/test/stats/statcmp.js b/test/stats/statcmp.js index 1ddf9d2c0..bb5a2226d 100644 --- a/test/stats/statcmp.js +++ b/test/stats/statcmp.js @@ -1,6 +1,7 @@ -"use strict"; +import { createRequire } from "module"; +import fs from "fs"; -const fs = require("fs"); +const require = createRequire(import.meta.url); const ttest = require("ttest"); const VALID_GROUP_BYS = ["browser", "pdf", "page", "round", "stat"]; diff --git a/test/unit/node_stream_spec.js b/test/unit/node_stream_spec.js index e70ff4b2b..a538bf1ab 100644 --- a/test/unit/node_stream_spec.js +++ b/test/unit/node_stream_spec.js @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals __non_webpack_require__ */ +/* globals __non_webpack_import__ */ import { AbortException, isNodeJS } from "../../src/shared/util.js"; import { PDFNodeStream } from "../../src/display/node_stream.js"; @@ -24,10 +24,10 @@ if (!isNodeJS) { ); } -const path = __non_webpack_require__("path"); -const url = __non_webpack_require__("url"); -const http = __non_webpack_require__("http"); -const fs = __non_webpack_require__("fs"); +const path = await __non_webpack_import__("path"); +const url = await __non_webpack_import__("url"); +const http = await __non_webpack_import__("http"); +const fs = await __non_webpack_import__("fs"); describe("node_stream", function () { let server = null; diff --git a/test/unit/test_utils.js b/test/unit/test_utils.js index 16db2f28c..8ff955c2b 100644 --- a/test/unit/test_utils.js +++ b/test/unit/test_utils.js @@ -12,12 +12,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/* globals __non_webpack_import__ */ import { NullStream, StringStream } from "../../src/core/stream.js"; import { Page, PDFDocument } from "../../src/core/document.js"; import { isNodeJS } from "../../src/shared/util.js"; import { Ref } from "../../src/core/primitives.js"; +let fs; +if (isNodeJS) { + // Native packages. + fs = await __non_webpack_import__("fs"); +} + const TEST_PDFS_PATH = isNodeJS ? "./test/pdfs/" : "../pdfs/"; const CMAP_URL = isNodeJS ? "./external/bcmaps/" : "../../external/bcmaps/"; @@ -38,8 +45,6 @@ class DOMFileReaderFactory { class NodeFileReaderFactory { static async fetch(params) { - const fs = require("fs"); - return new Promise((resolve, reject) => { fs.readFile(params.path, (error, data) => { if (error || !data) {