From 6b961c424f4c1bced0ad6704222f83b1cdc7636b Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 13 Apr 2025 16:15:07 +0200 Subject: [PATCH] Update Webpack to version `5.99.5` (issue 19808) In Webpack version `5.99.0` the way that `export` statements are handled was changed slightly, with much less boilerplate code being generated, which unfortunately breaks our `tweakWebpackOutput` function that's used to expose the exported properties globally and that e.g. the viewer depends upon. Given that we were depending on formatting that should most likely be viewed as nothing more than an internal implementation detail in Webpack, we instead work-around this by manually defining the structures that were previously generated. Obviously this will lead to a tiny bit more manual work in the future, however we don't change the API-surface often enough that it should be a big issue *and* the relevant unit-tests are updated such that it shouldn't be possible to break this. *NOTE:* In the future we might want to consider no longer using global properties like this, and instead rely only on proper `export`s throughout the code-base. However changing this would likely be non-trivial (given edge-cases), and it'd be an `api-major` change, so let's just do the minimal amount of work to unblock Webpack updates for now. --- gulpfile.mjs | 26 +++---------- package-lock.json | 8 ++-- package.json | 2 +- src/display/api.js | 7 ++++ src/pdf.image_decoders.js | 12 ++++++ src/pdf.js | 53 +++++++++++++++++++++++++ src/pdf.sandbox.js | 4 ++ src/pdf.worker.js | 4 ++ test/unit/pdf.image_decoders_spec.js | 28 +++++++------ test/unit/pdf.worker_spec.js | 19 +++++++-- test/unit/pdf_spec.js | 4 ++ test/unit/pdf_viewer.component_spec.js | 54 ++++++++++++++------------ web/pdf_viewer.component.js | 25 ++++++++++++ 13 files changed, 182 insertions(+), 64 deletions(-) diff --git a/gulpfile.mjs b/gulpfile.mjs index 6a4d51b03..a99a104bc 100644 --- a/gulpfile.mjs +++ b/gulpfile.mjs @@ -362,10 +362,6 @@ function createWebpackConfig( // V8 chokes on very long sequences, work around that. sequences: false, }, - mangle: { - // Ensure that the `tweakWebpackOutput` function works. - reserved: ["__webpack_exports__"], - }, keep_classnames: true, keep_fnames: true, module: isModule, @@ -463,13 +459,6 @@ function checkChromePreferencesFile(chromePrefsPath, webPrefs) { return ret; } -function tweakWebpackOutput(jsName) { - return replace( - /((?:\s|,)__webpack_exports__)(?:\s?)=(?:\s?)({};)/gm, - (match, p1, p2) => `${p1} = globalThis.${jsName} = ${p2}` - ); -} - function createMainBundle(defines) { const mainFileConfig = createWebpackConfig(defines, { filename: defines.MINIFIED ? "pdf.min.mjs" : "pdf.mjs", @@ -479,8 +468,7 @@ function createMainBundle(defines) { }); return gulp .src("./src/pdf.js", { encoding: false }) - .pipe(webpack2Stream(mainFileConfig)) - .pipe(tweakWebpackOutput("pdfjsLib")); + .pipe(webpack2Stream(mainFileConfig)); } function createScriptingBundle(defines, extraOptions = undefined) { @@ -548,8 +536,7 @@ function createSandboxBundle(defines, extraOptions = undefined) { return gulp .src("./src/pdf.sandbox.js", { encoding: false }) - .pipe(webpack2Stream(sandboxFileConfig)) - .pipe(tweakWebpackOutput("pdfjsSandbox")); + .pipe(webpack2Stream(sandboxFileConfig)); } function createWorkerBundle(defines) { @@ -561,8 +548,7 @@ function createWorkerBundle(defines) { }); return gulp .src("./src/pdf.worker.js", { encoding: false }) - .pipe(webpack2Stream(workerFileConfig)) - .pipe(tweakWebpackOutput("pdfjsWorker")); + .pipe(webpack2Stream(workerFileConfig)); } function createWebBundle(defines, options) { @@ -610,8 +596,7 @@ function createComponentsBundle(defines) { }); return gulp .src("./web/pdf_viewer.component.js", { encoding: false }) - .pipe(webpack2Stream(componentsFileConfig)) - .pipe(tweakWebpackOutput("pdfjsViewer")); + .pipe(webpack2Stream(componentsFileConfig)); } function createImageDecodersBundle(defines) { @@ -625,8 +610,7 @@ function createImageDecodersBundle(defines) { }); return gulp .src("./src/pdf.image_decoders.js", { encoding: false }) - .pipe(webpack2Stream(componentsFileConfig)) - .pipe(tweakWebpackOutput("pdfjsImageDecoders")); + .pipe(webpack2Stream(componentsFileConfig)); } function createCMapBundle() { diff --git a/package-lock.json b/package-lock.json index 5f3d8950c..22ced23e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,7 +58,7 @@ "ttest": "^4.0.0", "typescript": "^5.8.3", "vinyl": "^3.0.0", - "webpack": "^5.98.0", + "webpack": "^5.99.5", "webpack-stream": "^7.0.0", "yargs": "^17.7.2" }, @@ -12388,9 +12388,9 @@ } }, "node_modules/webpack": { - "version": "5.98.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", - "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", + "version": "5.99.5", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.5.tgz", + "integrity": "sha512-q+vHBa6H9qwBLUlHL4Y7L0L1/LlyBKZtS9FHNCQmtayxjI5RKC9yD8gpvLeqGv5lCQp1Re04yi0MF40pf30Pvg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 323d3ba3f..27eee246d 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "ttest": "^4.0.0", "typescript": "^5.8.3", "vinyl": "^3.0.0", - "webpack": "^5.98.0", + "webpack": "^5.99.5", "webpack-stream": "^7.0.0", "yargs": "^17.7.2" }, diff --git a/src/display/api.js b/src/display/api.js index a223d5e6f..e210118b3 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -2136,6 +2136,13 @@ class PDFWorker { ); }; } + + if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { + this._resetGlobalState = () => { + this.#isWorkerDisabled = false; + delete globalThis.pdfjsWorker; + }; + } } constructor({ diff --git a/src/pdf.image_decoders.js b/src/pdf.image_decoders.js index 633d51105..2631f46f7 100644 --- a/src/pdf.image_decoders.js +++ b/src/pdf.image_decoders.js @@ -29,6 +29,18 @@ const pdfjsVersion = const pdfjsBuild = typeof PDFJSDev !== "undefined" ? PDFJSDev.eval("BUNDLE_BUILD") : void 0; +globalThis.pdfjsImageDecoders = { + getVerbosityLevel, + Jbig2Error, + Jbig2Image, + JpegError, + JpegImage, + JpxError, + JpxImage, + setVerbosityLevel, + VerbosityLevel, +}; + export { getVerbosityLevel, Jbig2Error, diff --git a/src/pdf.js b/src/pdf.js index e9a1b974c..2a3acbaaf 100644 --- a/src/pdf.js +++ b/src/pdf.js @@ -93,6 +93,59 @@ if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("TESTING || GENERIC")) { }; } +globalThis.pdfjsLib = { + AbortException, + AnnotationEditorLayer, + AnnotationEditorParamsType, + AnnotationEditorType, + AnnotationEditorUIManager, + AnnotationLayer, + AnnotationMode, + AnnotationType, + build, + ColorPicker, + createValidAbsoluteUrl, + DOMSVGFactory, + DrawLayer, + FeatureTest, + fetchData, + getDocument, + getFilenameFromUrl, + getPdfFilenameFromUrl, + getUuid, + getXfaPageViewport, + GlobalWorkerOptions, + ImageKind, + InvalidPDFException, + isDataScheme, + isPdfFile, + isValidExplicitDest, + MathClamp, + noContextMenu, + normalizeUnicode, + OPS, + OutputScale, + PasswordResponses, + PDFDataRangeTransport, + PDFDateString, + PDFWorker, + PermissionFlag, + PixelsPerInch, + RenderingCancelledException, + ResponseException, + setLayerDimensions, + shadow, + SignatureExtractor, + stopEvent, + SupportedImageMimeTypes, + TextLayer, + TouchManager, + Util, + VerbosityLevel, + version, + XfaLayer, +}; + export { AbortException, AnnotationEditorLayer, diff --git a/src/pdf.sandbox.js b/src/pdf.sandbox.js index 79cb05ec1..4752f9f59 100644 --- a/src/pdf.sandbox.js +++ b/src/pdf.sandbox.js @@ -145,4 +145,8 @@ function QuickJSSandbox() { return ModuleLoader().then(module => new Sandbox(window, module)); } +globalThis.pdfjsSandbox = { + QuickJSSandbox, +}; + export { QuickJSSandbox }; diff --git a/src/pdf.worker.js b/src/pdf.worker.js index 900c0b02a..5d6bdf0aa 100644 --- a/src/pdf.worker.js +++ b/src/pdf.worker.js @@ -22,4 +22,8 @@ const pdfjsVersion = const pdfjsBuild = typeof PDFJSDev !== "undefined" ? PDFJSDev.eval("BUNDLE_BUILD") : void 0; +globalThis.pdfjsWorker = { + WorkerMessageHandler, +}; + export { WorkerMessageHandler }; diff --git a/test/unit/pdf.image_decoders_spec.js b/test/unit/pdf.image_decoders_spec.js index d12210e3d..ebd583fd3 100644 --- a/test/unit/pdf.image_decoders_spec.js +++ b/test/unit/pdf.image_decoders_spec.js @@ -22,6 +22,18 @@ import { Jbig2Error, Jbig2Image } from "../../src/core/jbig2.js"; import { JpegError, JpegImage } from "../../src/core/jpg.js"; import { JpxError, JpxImage } from "../../src/core/jpx.js"; +const expectedAPI = Object.freeze({ + getVerbosityLevel, + Jbig2Error, + Jbig2Image, + JpegError, + JpegImage, + JpxError, + JpxImage, + setVerbosityLevel, + VerbosityLevel, +}); + describe("pdfimage_api", function () { it("checks that the *official* PDF.js-image decoders API exposes the expected functionality", async function () { // eslint-disable-next-line no-unsanitized/method @@ -33,16 +45,10 @@ describe("pdfimage_api", function () { // The imported Object contains an (automatically) inserted Symbol, // hence we copy the data to allow using a simple comparison below. - expect({ ...pdfimageAPI }).toEqual({ - getVerbosityLevel, - Jbig2Error, - Jbig2Image, - JpegError, - JpegImage, - JpxError, - JpxImage, - setVerbosityLevel, - VerbosityLevel, - }); + expect({ ...pdfimageAPI }).toEqual(expectedAPI); + + expect(Object.keys(globalThis.pdfjsImageDecoders).sort()).toEqual( + Object.keys(expectedAPI).sort() + ); }); }); diff --git a/test/unit/pdf.worker_spec.js b/test/unit/pdf.worker_spec.js index f90498662..b02fc6746 100644 --- a/test/unit/pdf.worker_spec.js +++ b/test/unit/pdf.worker_spec.js @@ -13,9 +13,20 @@ * limitations under the License. */ +import { PDFWorker } from "../../src/display/api.js"; import { WorkerMessageHandler } from "../../src/core/worker.js"; +const expectedAPI = Object.freeze({ + WorkerMessageHandler, +}); + describe("pdfworker_api", function () { + afterEach(function () { + // Avoid interfering with other unit-tests, since `globalThis.pdfjsWorker` + // being defined will impact loading and usage of the worker. + PDFWorker._resetGlobalState(); + }); + it("checks that the *official* PDF.js-worker API exposes the expected functionality", async function () { // eslint-disable-next-line no-unsanitized/method const pdfworkerAPI = await import( @@ -26,8 +37,10 @@ describe("pdfworker_api", function () { // The imported Object contains an (automatically) inserted Symbol, // hence we copy the data to allow using a simple comparison below. - expect({ ...pdfworkerAPI }).toEqual({ - WorkerMessageHandler, - }); + expect({ ...pdfworkerAPI }).toEqual(expectedAPI); + + expect(Object.keys(globalThis.pdfjsWorker).sort()).toEqual( + Object.keys(expectedAPI).sort() + ); }); }); diff --git a/test/unit/pdf_spec.js b/test/unit/pdf_spec.js index 184afaecb..714753502 100644 --- a/test/unit/pdf_spec.js +++ b/test/unit/pdf_spec.js @@ -135,6 +135,10 @@ describe("pdfjs_api", function () { // The imported Object contains an (automatically) inserted Symbol, // hence we copy the data to allow using a simple comparison below. expect({ ...pdfjsAPI }).toEqual(expectedAPI); + + expect(Object.keys(globalThis.pdfjsLib).sort()).toEqual( + Object.keys(expectedAPI).sort() + ); }); }); diff --git a/test/unit/pdf_viewer.component_spec.js b/test/unit/pdf_viewer.component_spec.js index 1785a4959..a511a353f 100644 --- a/test/unit/pdf_viewer.component_spec.js +++ b/test/unit/pdf_viewer.component_spec.js @@ -39,35 +39,41 @@ import { StructTreeLayerBuilder } from "../../web/struct_tree_layer_builder.js"; import { TextLayerBuilder } from "../../web/text_layer_builder.js"; import { XfaLayerBuilder } from "../../web/xfa_layer_builder.js"; +const expectedAPI = Object.freeze({ + AnnotationLayerBuilder, + DownloadManager, + EventBus, + FindState, + GenericL10n, + LinkTarget, + parseQueryString, + PDFFindController, + PDFHistory, + PDFLinkService, + PDFPageView, + PDFScriptingManager, + PDFSinglePageViewer, + PDFViewer, + ProgressBar, + RenderingStates, + ScrollMode, + SimpleLinkService, + SpreadMode, + StructTreeLayerBuilder, + TextLayerBuilder, + XfaLayerBuilder, +}); + describe("pdfviewer_api", function () { it("checks that the *official* PDF.js-viewer API exposes the expected functionality", async function () { const pdfviewerAPI = await import("../../web/pdf_viewer.component.js"); // The imported Object contains an (automatically) inserted Symbol, // hence we copy the data to allow using a simple comparison below. - expect({ ...pdfviewerAPI }).toEqual({ - AnnotationLayerBuilder, - DownloadManager, - EventBus, - FindState, - GenericL10n, - LinkTarget, - parseQueryString, - PDFFindController, - PDFHistory, - PDFLinkService, - PDFPageView, - PDFScriptingManager, - PDFSinglePageViewer, - PDFViewer, - ProgressBar, - RenderingStates, - ScrollMode, - SimpleLinkService, - SpreadMode, - StructTreeLayerBuilder, - TextLayerBuilder, - XfaLayerBuilder, - }); + expect({ ...pdfviewerAPI }).toEqual(expectedAPI); + + expect(Object.keys(globalThis.pdfjsViewer).sort()).toEqual( + Object.keys(expectedAPI).sort() + ); }); }); diff --git a/web/pdf_viewer.component.js b/web/pdf_viewer.component.js index e3f9e8db8..fca58eb3b 100644 --- a/web/pdf_viewer.component.js +++ b/web/pdf_viewer.component.js @@ -46,6 +46,31 @@ const pdfjsVersion = const pdfjsBuild = typeof PDFJSDev !== "undefined" ? PDFJSDev.eval("BUNDLE_BUILD") : void 0; +globalThis.pdfjsViewer = { + AnnotationLayerBuilder, + DownloadManager, + EventBus, + FindState, + GenericL10n, + LinkTarget, + parseQueryString, + PDFFindController, + PDFHistory, + PDFLinkService, + PDFPageView, + PDFScriptingManager, + PDFSinglePageViewer, + PDFViewer, + ProgressBar, + RenderingStates, + ScrollMode, + SimpleLinkService, + SpreadMode, + StructTreeLayerBuilder, + TextLayerBuilder, + XfaLayerBuilder, +}; + export { AnnotationLayerBuilder, DownloadManager,