5687326046
After recent PRs the size and scope of the CI workflow is now reduced, and this patch tries to simplify things further. More specifically we can directly specify the gulp-tasks in the workflow, and thus clean-up the `gulpfile` a tiny bit. Note that this will technically be slower, since the tests are now run in series (rather than in parallel), however `gulp externaltest` runs so quickly that it really won't matter in practice.
2439 lines
68 KiB
JavaScript
2439 lines
68 KiB
JavaScript
/* Copyright 2016 Mozilla Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
/* eslint-env node */
|
|
|
|
import { exec, spawn, spawnSync } from "child_process";
|
|
import autoprefixer from "autoprefixer";
|
|
import babel from "@babel/core";
|
|
import crypto from "crypto";
|
|
import { fileURLToPath } from "url";
|
|
import fs from "fs";
|
|
import gulp from "gulp";
|
|
import merge from "merge-stream";
|
|
import { mkdirp } from "mkdirp";
|
|
import path from "path";
|
|
import postcss from "gulp-postcss";
|
|
import postcssDirPseudoClass from "postcss-dir-pseudo-class";
|
|
import postcssDiscardComments from "postcss-discard-comments";
|
|
import postcssNesting from "postcss-nesting";
|
|
import { preprocess } from "./external/builder/builder.mjs";
|
|
import { preprocessPDFJSCode } from "./external/builder/preprocessor2.mjs";
|
|
import rename from "gulp-rename";
|
|
import replace from "gulp-replace";
|
|
import rimraf from "rimraf";
|
|
import stream from "stream";
|
|
import streamqueue from "streamqueue";
|
|
import through from "through2";
|
|
import Vinyl from "vinyl";
|
|
import webpack2 from "webpack";
|
|
import webpackStream from "webpack-stream";
|
|
import zip from "gulp-zip";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
|
const BUILD_DIR = "build/";
|
|
const L10N_DIR = "l10n/";
|
|
const TEST_DIR = "test/";
|
|
const EXTENSION_SRC_DIR = "extensions/";
|
|
|
|
const BASELINE_DIR = BUILD_DIR + "baseline/";
|
|
const MOZCENTRAL_BASELINE_DIR = BUILD_DIR + "mozcentral.baseline/";
|
|
const GENERIC_DIR = BUILD_DIR + "generic/";
|
|
const GENERIC_LEGACY_DIR = BUILD_DIR + "generic-legacy/";
|
|
const COMPONENTS_DIR = BUILD_DIR + "components/";
|
|
const COMPONENTS_LEGACY_DIR = BUILD_DIR + "components-legacy/";
|
|
const IMAGE_DECODERS_DIR = BUILD_DIR + "image_decoders/";
|
|
const IMAGE_DECODERS_LEGACY_DIR = BUILD_DIR + "image_decoders-legacy/";
|
|
const DEFAULT_PREFERENCES_DIR = BUILD_DIR + "default_preferences/";
|
|
const MINIFIED_DIR = BUILD_DIR + "minified/";
|
|
const MINIFIED_LEGACY_DIR = BUILD_DIR + "minified-legacy/";
|
|
const JSDOC_BUILD_DIR = BUILD_DIR + "jsdoc/";
|
|
const GH_PAGES_DIR = BUILD_DIR + "gh-pages/";
|
|
const DIST_DIR = BUILD_DIR + "dist/";
|
|
const TYPES_DIR = BUILD_DIR + "types/";
|
|
const TMP_DIR = BUILD_DIR + "tmp/";
|
|
const TYPESTEST_DIR = BUILD_DIR + "typestest/";
|
|
const COMMON_WEB_FILES = [
|
|
"web/images/*.{png,svg,gif}",
|
|
"web/debugger.{css,mjs}",
|
|
];
|
|
const MOZCENTRAL_DIFF_FILE = "mozcentral.diff";
|
|
|
|
const DIST_REPO_URL = "https://github.com/mozilla/pdfjs-dist";
|
|
|
|
const CONFIG_FILE = "pdfjs.config";
|
|
const config = JSON.parse(fs.readFileSync(CONFIG_FILE).toString());
|
|
|
|
const ENV_TARGETS = [
|
|
"last 2 versions",
|
|
"Chrome >= 92",
|
|
"Firefox ESR",
|
|
"Safari >= 15.4",
|
|
"Node >= 18",
|
|
"> 1%",
|
|
"not IE > 0",
|
|
"not dead",
|
|
];
|
|
|
|
// Default Autoprefixer config used for generic, components, minified-pre
|
|
const AUTOPREFIXER_CONFIG = {
|
|
overrideBrowserslist: ENV_TARGETS,
|
|
};
|
|
// Default Babel targets used for generic, components, minified-pre
|
|
const BABEL_TARGETS = ENV_TARGETS.join(", ");
|
|
|
|
const DEFINES = Object.freeze({
|
|
SKIP_BABEL: true,
|
|
TESTING: undefined,
|
|
// The main build targets:
|
|
GENERIC: false,
|
|
MOZCENTRAL: false,
|
|
GECKOVIEW: false,
|
|
CHROME: false,
|
|
MINIFIED: false,
|
|
COMPONENTS: false,
|
|
LIB: false,
|
|
IMAGE_DECODERS: false,
|
|
});
|
|
|
|
function transform(charEncoding, transformFunction) {
|
|
return through.obj(function (vinylFile, enc, done) {
|
|
const transformedFile = vinylFile.clone();
|
|
transformedFile.contents = Buffer.from(
|
|
transformFunction(transformedFile.contents),
|
|
charEncoding
|
|
);
|
|
done(null, transformedFile);
|
|
});
|
|
}
|
|
|
|
function safeSpawnSync(command, parameters, options) {
|
|
// Execute all commands in a shell.
|
|
options = options || {};
|
|
options.shell = true;
|
|
// `options.shell = true` requires parameters to be quoted.
|
|
parameters = parameters.map(param => {
|
|
if (!/[\s`~!#$*(){[|\\;'"<>?]/.test(param)) {
|
|
return param;
|
|
}
|
|
return '"' + param.replaceAll(/([$\\"`])/g, "\\$1") + '"';
|
|
});
|
|
|
|
const result = spawnSync(command, parameters, options);
|
|
if (result.status !== 0) {
|
|
console.log(
|
|
'Error: command "' +
|
|
command +
|
|
'" with parameters "' +
|
|
parameters +
|
|
'" exited with code ' +
|
|
result.status
|
|
);
|
|
process.exit(result.status);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function startNode(args, options) {
|
|
// Node.js decreased the maximum header size from 80 KB to 8 KB in newer
|
|
// releases, which is not sufficient for some of our reference test files
|
|
// (such as `issue6360.pdf`), so we need to restore this value. Note that
|
|
// this argument needs to be before all other arguments as it needs to be
|
|
// passed to the Node.js process itself and not to the script that it runs.
|
|
args.unshift("--max-http-header-size=80000");
|
|
return spawn("node", args, options);
|
|
}
|
|
|
|
function createStringSource(filename, content) {
|
|
const source = stream.Readable({ objectMode: true });
|
|
source._read = function () {
|
|
this.push(
|
|
new Vinyl({
|
|
path: filename,
|
|
contents: Buffer.from(content),
|
|
})
|
|
);
|
|
this.push(null);
|
|
};
|
|
return source;
|
|
}
|
|
|
|
function createWebpackConfig(
|
|
defines,
|
|
output,
|
|
{
|
|
disableVersionInfo = false,
|
|
disableSourceMaps = false,
|
|
disableLicenseHeader = false,
|
|
defaultPreferencesDir = null,
|
|
} = {}
|
|
) {
|
|
const versionInfo = !disableVersionInfo
|
|
? getVersionJSON()
|
|
: { version: 0, commit: 0 };
|
|
const bundleDefines = {
|
|
...defines,
|
|
BUNDLE_VERSION: versionInfo.version,
|
|
BUNDLE_BUILD: versionInfo.commit,
|
|
TESTING: defines.TESTING ?? process.env.TESTING === "true",
|
|
BROWSER_PREFERENCES: defaultPreferencesDir
|
|
? getBrowserPreferences(defaultPreferencesDir)
|
|
: {},
|
|
DEFAULT_PREFERENCES: defaultPreferencesDir
|
|
? getDefaultPreferences(defaultPreferencesDir)
|
|
: {},
|
|
DEFAULT_FTL: defines.GENERIC ? getDefaultFtl() : "",
|
|
};
|
|
const licenseHeaderLibre = fs
|
|
.readFileSync("./src/license_header_libre.js")
|
|
.toString();
|
|
const enableSourceMaps =
|
|
!bundleDefines.MOZCENTRAL &&
|
|
!bundleDefines.CHROME &&
|
|
!bundleDefines.LIB &&
|
|
!bundleDefines.TESTING &&
|
|
!disableSourceMaps;
|
|
const isModule = output.library?.type === "module";
|
|
const skipBabel = bundleDefines.SKIP_BABEL;
|
|
|
|
// `core-js`, see https://github.com/zloirock/core-js/issues/514,
|
|
// should be excluded from processing.
|
|
const babelExcludes = ["node_modules[\\\\\\/]core-js"];
|
|
const babelExcludeRegExp = new RegExp(`(${babelExcludes.join("|")})`);
|
|
|
|
const babelPresets = skipBabel
|
|
? undefined
|
|
: [
|
|
[
|
|
"@babel/preset-env",
|
|
{
|
|
corejs: "3.33.2",
|
|
exclude: ["web.structured-clone"],
|
|
shippedProposals: true,
|
|
useBuiltIns: "usage",
|
|
},
|
|
],
|
|
];
|
|
const babelPlugins = [];
|
|
|
|
const plugins = [];
|
|
if (!disableLicenseHeader) {
|
|
plugins.push(
|
|
new webpack2.BannerPlugin({ banner: licenseHeaderLibre, raw: true })
|
|
);
|
|
}
|
|
|
|
const experiments = isModule ? { outputModule: true } : undefined;
|
|
|
|
// Required to expose e.g., the `window` object.
|
|
output.globalObject = "globalThis";
|
|
|
|
const basicAlias = {
|
|
pdfjs: "src",
|
|
"pdfjs-web": "web",
|
|
"pdfjs-lib": "web/pdfjs",
|
|
"fluent-bundle": "node_modules/@fluent/bundle/esm/index.js",
|
|
"fluent-dom": "node_modules/@fluent/dom/esm/index.js",
|
|
};
|
|
const libraryAlias = {
|
|
"display-fetch_stream": "src/display/stubs.js",
|
|
"display-network": "src/display/stubs.js",
|
|
"display-node_stream": "src/display/stubs.js",
|
|
"display-node_utils": "src/display/stubs.js",
|
|
};
|
|
const viewerAlias = {
|
|
"web-alt_text_manager": "web/alt_text_manager.js",
|
|
"web-annotation_editor_params": "web/annotation_editor_params.js",
|
|
"web-com": "",
|
|
"web-l10n_utils": "web/stubs.js",
|
|
"web-pdf_attachment_viewer": "web/pdf_attachment_viewer.js",
|
|
"web-pdf_cursor_tools": "web/pdf_cursor_tools.js",
|
|
"web-pdf_document_properties": "web/pdf_document_properties.js",
|
|
"web-pdf_find_bar": "web/pdf_find_bar.js",
|
|
"web-pdf_layer_viewer": "web/pdf_layer_viewer.js",
|
|
"web-pdf_outline_viewer": "web/pdf_outline_viewer.js",
|
|
"web-pdf_presentation_mode": "web/pdf_presentation_mode.js",
|
|
"web-pdf_sidebar": "web/pdf_sidebar.js",
|
|
"web-pdf_thumbnail_viewer": "web/pdf_thumbnail_viewer.js",
|
|
"web-print_service": "",
|
|
"web-secondary_toolbar": "web/secondary_toolbar.js",
|
|
"web-toolbar": "web/toolbar.js",
|
|
};
|
|
if (bundleDefines.CHROME) {
|
|
libraryAlias["display-fetch_stream"] = "src/display/fetch_stream.js";
|
|
libraryAlias["display-network"] = "src/display/network.js";
|
|
|
|
viewerAlias["web-com"] = "web/chromecom.js";
|
|
viewerAlias["web-print_service"] = "web/pdf_print_service.js";
|
|
} else if (bundleDefines.GENERIC) {
|
|
// Aliases defined here must also be replicated in the paths section of
|
|
// the tsconfig.json file for the type generation to work.
|
|
// In the tsconfig.json files, the .js extension must be omitted.
|
|
libraryAlias["display-fetch_stream"] = "src/display/fetch_stream.js";
|
|
libraryAlias["display-network"] = "src/display/network.js";
|
|
libraryAlias["display-node_stream"] = "src/display/node_stream.js";
|
|
libraryAlias["display-node_utils"] = "src/display/node_utils.js";
|
|
|
|
viewerAlias["web-com"] = "web/genericcom.js";
|
|
viewerAlias["web-l10n_utils"] = "web/l10n_utils.js";
|
|
viewerAlias["web-print_service"] = "web/pdf_print_service.js";
|
|
} else if (bundleDefines.MOZCENTRAL) {
|
|
if (bundleDefines.GECKOVIEW) {
|
|
const gvAlias = {
|
|
"web-l10n_utils": "web/stubs.js",
|
|
"web-toolbar": "web/toolbar-geckoview.js",
|
|
};
|
|
for (const key in viewerAlias) {
|
|
viewerAlias[key] = gvAlias[key] || "web/stubs-geckoview.js";
|
|
}
|
|
}
|
|
viewerAlias["web-com"] = "web/firefoxcom.js";
|
|
viewerAlias["web-print_service"] = "web/firefox_print_service.js";
|
|
}
|
|
const alias = { ...basicAlias, ...libraryAlias, ...viewerAlias };
|
|
for (const key in alias) {
|
|
alias[key] = path.join(__dirname, alias[key]);
|
|
}
|
|
|
|
return {
|
|
mode: "production",
|
|
optimization: {
|
|
mangleExports: false,
|
|
minimize: false,
|
|
},
|
|
experiments,
|
|
output,
|
|
performance: {
|
|
hints: false, // Disable messages about larger file sizes.
|
|
},
|
|
plugins,
|
|
resolve: {
|
|
alias,
|
|
},
|
|
devtool: enableSourceMaps ? "source-map" : undefined,
|
|
module: {
|
|
rules: [
|
|
{
|
|
loader: "babel-loader",
|
|
exclude: babelExcludeRegExp,
|
|
options: {
|
|
presets: babelPresets,
|
|
plugins: babelPlugins,
|
|
targets: BABEL_TARGETS,
|
|
},
|
|
},
|
|
{
|
|
loader: path.join(__dirname, "external/webpack/pdfjsdev-loader.mjs"),
|
|
options: {
|
|
rootPath: __dirname,
|
|
saveComments: false,
|
|
defines: bundleDefines,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
// Avoid shadowing actual Node.js variables with polyfills, by disabling
|
|
// polyfills/mocks - https://webpack.js.org/configuration/node/
|
|
node: false,
|
|
};
|
|
}
|
|
|
|
function webpack2Stream(webpackConfig) {
|
|
// Replacing webpack1 to webpack2 in the webpack-stream.
|
|
return webpackStream(webpackConfig, webpack2);
|
|
}
|
|
|
|
function getVersionJSON() {
|
|
return JSON.parse(fs.readFileSync(BUILD_DIR + "version.json").toString());
|
|
}
|
|
|
|
function checkChromePreferencesFile(chromePrefsPath, webPrefs) {
|
|
const chromePrefs = JSON.parse(fs.readFileSync(chromePrefsPath).toString());
|
|
const chromePrefsKeys = Object.keys(chromePrefs.properties).filter(key => {
|
|
const description = chromePrefs.properties[key].description;
|
|
// Deprecated keys are allowed in the managed preferences file.
|
|
// The code maintainer is responsible for adding migration logic to
|
|
// extensions/chromium/options/migration.js and web/chromecom.js .
|
|
return !description || !description.startsWith("DEPRECATED.");
|
|
});
|
|
|
|
let ret = true;
|
|
// Verify that every entry in webPrefs is also in preferences_schema.json.
|
|
for (const [key, value] of Object.entries(webPrefs)) {
|
|
if (!chromePrefsKeys.includes(key)) {
|
|
// Note: this would also reject keys that are present but marked as
|
|
// DEPRECATED. A key should not be marked as DEPRECATED if it is still
|
|
// listed in webPrefs.
|
|
ret = false;
|
|
console.log(
|
|
`Warning: ${chromePrefsPath} does not contain an entry for pref: ${key}`
|
|
);
|
|
} else if (chromePrefs.properties[key].default !== value) {
|
|
ret = false;
|
|
console.log(
|
|
`Warning: not the same values (for "${key}"): ` +
|
|
`${chromePrefs.properties[key].default} !== ${value}`
|
|
);
|
|
}
|
|
}
|
|
|
|
// Verify that preferences_schema.json does not contain entries that are not
|
|
// in webPrefs (app_options.js).
|
|
for (const key of chromePrefsKeys) {
|
|
if (!(key in webPrefs)) {
|
|
ret = false;
|
|
console.log(
|
|
`Warning: ${chromePrefsPath} contains an unrecognized pref: ${key}. ` +
|
|
`Remove it, or prepend "DEPRECATED. " and add migration logic to ` +
|
|
`extensions/chromium/options/migration.js and web/chromecom.js.`
|
|
);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
function tweakWebpackOutput(jsName) {
|
|
const replacer = ["__non_webpack_import__\\("];
|
|
|
|
if (jsName) {
|
|
replacer.push(
|
|
" __webpack_exports__ = {};",
|
|
" __webpack_exports__ = await __webpack_exports__;"
|
|
);
|
|
}
|
|
const regex = new RegExp(`(${replacer.join("|")})`, "gm");
|
|
|
|
return replace(regex, match => {
|
|
switch (match) {
|
|
case "__non_webpack_import__(":
|
|
return "import(/* webpackIgnore: true */ ";
|
|
case " __webpack_exports__ = {};":
|
|
return ` __webpack_exports__ = globalThis.${jsName} = {};`;
|
|
case " __webpack_exports__ = await __webpack_exports__;":
|
|
return ` __webpack_exports__ = globalThis.${jsName} = await (globalThis.${jsName}Promise = __webpack_exports__);`;
|
|
}
|
|
return match;
|
|
});
|
|
}
|
|
|
|
function createMainBundle(defines) {
|
|
const mainFileConfig = createWebpackConfig(defines, {
|
|
filename: "pdf.mjs",
|
|
library: {
|
|
type: "module",
|
|
},
|
|
});
|
|
return gulp
|
|
.src("./src/pdf.js")
|
|
.pipe(webpack2Stream(mainFileConfig))
|
|
.pipe(tweakWebpackOutput("pdfjsLib"));
|
|
}
|
|
|
|
function createScriptingBundle(defines, extraOptions = undefined) {
|
|
const scriptingFileConfig = createWebpackConfig(
|
|
defines,
|
|
{
|
|
filename: "pdf.scripting.mjs",
|
|
library: {
|
|
type: "module",
|
|
},
|
|
},
|
|
extraOptions
|
|
);
|
|
return gulp
|
|
.src("./src/pdf.scripting.js")
|
|
.pipe(webpack2Stream(scriptingFileConfig))
|
|
.pipe(tweakWebpackOutput());
|
|
}
|
|
|
|
function createSandboxExternal(defines) {
|
|
const licenseHeader = fs.readFileSync("./src/license_header.js").toString();
|
|
|
|
const ctx = {
|
|
saveComments: false,
|
|
defines,
|
|
};
|
|
return gulp
|
|
.src("./src/pdf.sandbox.external.js")
|
|
.pipe(rename("pdf.sandbox.external.sys.mjs"))
|
|
.pipe(
|
|
transform("utf8", content => {
|
|
content = preprocessPDFJSCode(ctx, content);
|
|
return `${licenseHeader}\n${content}`;
|
|
})
|
|
);
|
|
}
|
|
|
|
function createTemporaryScriptingBundle(defines, extraOptions = undefined) {
|
|
return createScriptingBundle(defines, {
|
|
disableVersionInfo: !!(extraOptions && extraOptions.disableVersionInfo),
|
|
disableSourceMaps: true,
|
|
disableLicenseHeader: true,
|
|
}).pipe(gulp.dest(TMP_DIR));
|
|
}
|
|
|
|
function createSandboxBundle(defines, extraOptions = undefined) {
|
|
const scriptingPath = TMP_DIR + "pdf.scripting.mjs";
|
|
// Insert the source as a string to be `eval`-ed in the sandbox.
|
|
const sandboxDefines = {
|
|
...defines,
|
|
PDF_SCRIPTING_JS_SOURCE: fs.readFileSync(scriptingPath).toString(),
|
|
};
|
|
fs.unlinkSync(scriptingPath);
|
|
|
|
const sandboxFileConfig = createWebpackConfig(
|
|
sandboxDefines,
|
|
{
|
|
filename: "pdf.sandbox.mjs",
|
|
library: {
|
|
type: "module",
|
|
},
|
|
},
|
|
extraOptions
|
|
);
|
|
|
|
return gulp
|
|
.src("./src/pdf.sandbox.js")
|
|
.pipe(webpack2Stream(sandboxFileConfig))
|
|
.pipe(tweakWebpackOutput("pdfjsSandbox"));
|
|
}
|
|
|
|
function createWorkerBundle(defines) {
|
|
const workerFileConfig = createWebpackConfig(defines, {
|
|
filename: "pdf.worker.mjs",
|
|
library: {
|
|
type: "module",
|
|
},
|
|
});
|
|
return gulp
|
|
.src("./src/pdf.worker.js")
|
|
.pipe(webpack2Stream(workerFileConfig))
|
|
.pipe(tweakWebpackOutput("pdfjsWorker"));
|
|
}
|
|
|
|
function createWebBundle(defines, options) {
|
|
const viewerFileConfig = createWebpackConfig(
|
|
defines,
|
|
{
|
|
filename: "viewer.mjs",
|
|
library: {
|
|
type: "module",
|
|
},
|
|
},
|
|
{
|
|
defaultPreferencesDir: options.defaultPreferencesDir,
|
|
}
|
|
);
|
|
return gulp
|
|
.src("./web/viewer.js")
|
|
.pipe(webpack2Stream(viewerFileConfig))
|
|
.pipe(tweakWebpackOutput());
|
|
}
|
|
|
|
function createGVWebBundle(defines, options) {
|
|
const viewerFileConfig = createWebpackConfig(
|
|
defines,
|
|
{
|
|
filename: "viewer-geckoview.mjs",
|
|
library: {
|
|
type: "module",
|
|
},
|
|
},
|
|
{
|
|
defaultPreferencesDir: options.defaultPreferencesDir,
|
|
}
|
|
);
|
|
return gulp
|
|
.src("./web/viewer-geckoview.js")
|
|
.pipe(webpack2Stream(viewerFileConfig))
|
|
.pipe(tweakWebpackOutput());
|
|
}
|
|
|
|
function createComponentsBundle(defines) {
|
|
const componentsFileConfig = createWebpackConfig(defines, {
|
|
filename: "pdf_viewer.mjs",
|
|
library: {
|
|
type: "module",
|
|
},
|
|
});
|
|
return gulp
|
|
.src("./web/pdf_viewer.component.js")
|
|
.pipe(webpack2Stream(componentsFileConfig))
|
|
.pipe(tweakWebpackOutput("pdfjsViewer"));
|
|
}
|
|
|
|
function createImageDecodersBundle(defines) {
|
|
const componentsFileConfig = createWebpackConfig(defines, {
|
|
filename: "pdf.image_decoders.mjs",
|
|
library: {
|
|
type: "module",
|
|
},
|
|
});
|
|
return gulp
|
|
.src("./src/pdf.image_decoders.js")
|
|
.pipe(webpack2Stream(componentsFileConfig))
|
|
.pipe(tweakWebpackOutput("pdfjsImageDecoders"));
|
|
}
|
|
|
|
function createCMapBundle() {
|
|
return gulp.src(["external/bcmaps/*.bcmap", "external/bcmaps/LICENSE"], {
|
|
base: "external/bcmaps",
|
|
});
|
|
}
|
|
|
|
function createStandardFontBundle() {
|
|
return gulp.src(
|
|
[
|
|
"external/standard_fonts/*.pfb",
|
|
"external/standard_fonts/*.ttf",
|
|
"external/standard_fonts/LICENSE_FOXIT",
|
|
"external/standard_fonts/LICENSE_LIBERATION",
|
|
],
|
|
{
|
|
base: "external/standard_fonts",
|
|
}
|
|
);
|
|
}
|
|
|
|
function checkFile(filePath) {
|
|
try {
|
|
const stat = fs.lstatSync(filePath);
|
|
return stat.isFile();
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function checkDir(dirPath) {
|
|
try {
|
|
const stat = fs.lstatSync(dirPath);
|
|
return stat.isDirectory();
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function replaceInFile(filePath, find, replacement) {
|
|
let content = fs.readFileSync(filePath).toString();
|
|
content = content.replace(find, replacement);
|
|
fs.writeFileSync(filePath, content);
|
|
}
|
|
|
|
function getTempFile(prefix, suffix) {
|
|
mkdirp.sync(BUILD_DIR + "tmp/");
|
|
const bytes = crypto.randomBytes(6).toString("hex");
|
|
const filePath = BUILD_DIR + "tmp/" + prefix + bytes + suffix;
|
|
fs.writeFileSync(filePath, "");
|
|
return filePath;
|
|
}
|
|
|
|
function createTestSource(testsName, { bot = false, xfaOnly = false } = {}) {
|
|
const source = stream.Readable({ objectMode: true });
|
|
source._read = function () {
|
|
console.log();
|
|
console.log("### Running " + testsName + " tests");
|
|
|
|
const PDF_TEST = process.env.PDF_TEST || "test_manifest.json";
|
|
let forceNoChrome = false;
|
|
const args = ["test.mjs"];
|
|
switch (testsName) {
|
|
case "browser":
|
|
if (!bot) {
|
|
args.push("--reftest");
|
|
} else {
|
|
const os = process.env.OS;
|
|
if (/windows/i.test(os)) {
|
|
// The browser-tests are too slow in Google Chrome on the Windows
|
|
// bot, causing a timeout, hence disabling them for now.
|
|
forceNoChrome = true;
|
|
}
|
|
}
|
|
if (xfaOnly) {
|
|
args.push("--xfaOnly");
|
|
}
|
|
args.push("--manifestFile=" + PDF_TEST);
|
|
break;
|
|
case "unit":
|
|
args.push("--unitTest");
|
|
break;
|
|
case "font":
|
|
args.push("--fontTest");
|
|
break;
|
|
case "integration":
|
|
args.push("--integration");
|
|
break;
|
|
default:
|
|
this.emit("error", new Error("Unknown name: " + testsName));
|
|
return null;
|
|
}
|
|
if (bot) {
|
|
args.push("--strictVerify");
|
|
}
|
|
if (process.argv.includes("--noChrome") || forceNoChrome) {
|
|
args.push("--noChrome");
|
|
}
|
|
|
|
const testProcess = startNode(args, { cwd: TEST_DIR, stdio: "inherit" });
|
|
testProcess.on("close", function (code) {
|
|
source.push(null);
|
|
});
|
|
return undefined;
|
|
};
|
|
return source;
|
|
}
|
|
|
|
function makeRef(done, bot) {
|
|
console.log();
|
|
console.log("### Creating reference images");
|
|
|
|
let forceNoChrome = false;
|
|
const args = ["test.mjs", "--masterMode"];
|
|
if (bot) {
|
|
const os = process.env.OS;
|
|
if (/windows/i.test(os)) {
|
|
// The browser-tests are too slow in Google Chrome on the Windows
|
|
// bot, causing a timeout, hence disabling them for now.
|
|
forceNoChrome = true;
|
|
}
|
|
args.push("--noPrompts", "--strictVerify");
|
|
}
|
|
if (process.argv.includes("--noChrome") || forceNoChrome) {
|
|
args.push("--noChrome");
|
|
}
|
|
|
|
const testProcess = startNode(args, { cwd: TEST_DIR, stdio: "inherit" });
|
|
testProcess.on("close", function (code) {
|
|
done();
|
|
});
|
|
}
|
|
|
|
gulp.task("default", function (done) {
|
|
console.log("Available tasks:");
|
|
const tasks = Object.keys(gulp.registry().tasks());
|
|
for (const taskName of tasks.sort()) {
|
|
if (taskName.endsWith("-pre")) {
|
|
continue;
|
|
}
|
|
console.log(" " + taskName);
|
|
}
|
|
done();
|
|
});
|
|
|
|
function createBuildNumber(done) {
|
|
console.log();
|
|
console.log("### Getting extension build number");
|
|
|
|
exec(
|
|
"git log --format=oneline " + config.baseVersion + "..",
|
|
function (err, stdout, stderr) {
|
|
let buildNumber = 0;
|
|
if (!err) {
|
|
// Build number is the number of commits since base version
|
|
buildNumber = stdout ? stdout.match(/\n/g).length : 0;
|
|
} else {
|
|
console.log(
|
|
"This is not a Git repository; using default build number."
|
|
);
|
|
}
|
|
|
|
console.log("Extension build number: " + buildNumber);
|
|
|
|
const version = config.versionPrefix + buildNumber;
|
|
|
|
exec('git log --format="%h" -n 1', function (err2, stdout2, stderr2) {
|
|
let buildCommit = "";
|
|
if (!err2) {
|
|
buildCommit = stdout2.replace("\n", "");
|
|
}
|
|
|
|
createStringSource(
|
|
"version.json",
|
|
JSON.stringify(
|
|
{
|
|
version,
|
|
build: buildNumber,
|
|
commit: buildCommit,
|
|
},
|
|
null,
|
|
2
|
|
)
|
|
)
|
|
.pipe(gulp.dest(BUILD_DIR))
|
|
.on("end", done);
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
function buildDefaultPreferences(defines, dir) {
|
|
console.log();
|
|
console.log("### Building default preferences");
|
|
|
|
const bundleDefines = {
|
|
...defines,
|
|
LIB: true,
|
|
TESTING: defines.TESTING ?? process.env.TESTING === "true",
|
|
};
|
|
|
|
const defaultPreferencesConfig = createWebpackConfig(
|
|
bundleDefines,
|
|
{
|
|
filename: "app_options.mjs",
|
|
library: {
|
|
type: "module",
|
|
},
|
|
},
|
|
{
|
|
disableVersionInfo: true,
|
|
}
|
|
);
|
|
return gulp
|
|
.src("web/app_options.js")
|
|
.pipe(webpack2Stream(defaultPreferencesConfig))
|
|
.pipe(gulp.dest(DEFAULT_PREFERENCES_DIR + dir));
|
|
}
|
|
|
|
async function parseDefaultPreferences(dir) {
|
|
console.log();
|
|
console.log("### Parsing default preferences");
|
|
|
|
// eslint-disable-next-line no-unsanitized/method
|
|
const { AppOptions, OptionKind } = await import(
|
|
"./" + DEFAULT_PREFERENCES_DIR + dir + "app_options.mjs"
|
|
);
|
|
|
|
const browserPrefs = AppOptions.getAll(OptionKind.BROWSER);
|
|
if (Object.keys(browserPrefs).length === 0) {
|
|
throw new Error("No browser preferences found.");
|
|
}
|
|
const prefs = AppOptions.getAll(OptionKind.PREFERENCE);
|
|
if (Object.keys(prefs).length === 0) {
|
|
throw new Error("No default preferences found.");
|
|
}
|
|
|
|
fs.writeFileSync(
|
|
DEFAULT_PREFERENCES_DIR + dir + "browser_preferences.json",
|
|
JSON.stringify(browserPrefs)
|
|
);
|
|
fs.writeFileSync(
|
|
DEFAULT_PREFERENCES_DIR + dir + "default_preferences.json",
|
|
JSON.stringify(prefs)
|
|
);
|
|
}
|
|
|
|
function getBrowserPreferences(dir) {
|
|
const str = fs
|
|
.readFileSync(DEFAULT_PREFERENCES_DIR + dir + "browser_preferences.json")
|
|
.toString();
|
|
return JSON.parse(str);
|
|
}
|
|
|
|
function getDefaultPreferences(dir) {
|
|
const str = fs
|
|
.readFileSync(DEFAULT_PREFERENCES_DIR + dir + "default_preferences.json")
|
|
.toString();
|
|
return JSON.parse(str);
|
|
}
|
|
|
|
function getDefaultFtl() {
|
|
const content = fs.readFileSync("l10n/en-US/viewer.ftl").toString(),
|
|
stringBuf = [];
|
|
|
|
// Strip out comments and line-breaks.
|
|
const regExp = /^\s*#/;
|
|
for (const line of content.split("\n")) {
|
|
if (!line || regExp.test(line)) {
|
|
continue;
|
|
}
|
|
stringBuf.push(line);
|
|
}
|
|
return stringBuf.join("\n");
|
|
}
|
|
|
|
gulp.task("locale", function () {
|
|
const VIEWER_LOCALE_OUTPUT = "web/locale/";
|
|
|
|
console.log();
|
|
console.log("### Building localization files");
|
|
|
|
rimraf.sync(VIEWER_LOCALE_OUTPUT);
|
|
mkdirp.sync(VIEWER_LOCALE_OUTPUT);
|
|
|
|
const subfolders = fs.readdirSync(L10N_DIR);
|
|
subfolders.sort();
|
|
const viewerOutput = Object.create(null);
|
|
const locales = [];
|
|
for (const locale of subfolders) {
|
|
const dirPath = L10N_DIR + locale;
|
|
if (!checkDir(dirPath)) {
|
|
continue;
|
|
}
|
|
if (!/^[a-z][a-z]([a-z])?(-[A-Z][A-Z])?$/.test(locale)) {
|
|
console.log("Skipping invalid locale: " + locale);
|
|
continue;
|
|
}
|
|
|
|
mkdirp.sync(VIEWER_LOCALE_OUTPUT + "/" + locale);
|
|
|
|
locales.push(locale);
|
|
|
|
if (checkFile(dirPath + "/viewer.ftl")) {
|
|
viewerOutput[locale] = `${locale}/viewer.ftl`;
|
|
}
|
|
}
|
|
|
|
const glob = locales.length === 1 ? locales[0] : `{${locales.join(",")}}`;
|
|
|
|
return merge([
|
|
createStringSource("locale.json", JSON.stringify(viewerOutput)).pipe(
|
|
gulp.dest(VIEWER_LOCALE_OUTPUT)
|
|
),
|
|
gulp
|
|
.src(`${L10N_DIR}/${glob}/viewer.ftl`, {
|
|
base: L10N_DIR,
|
|
})
|
|
.pipe(gulp.dest(VIEWER_LOCALE_OUTPUT)),
|
|
]);
|
|
});
|
|
|
|
gulp.task("cmaps", async function () {
|
|
const CMAP_INPUT = "external/cmaps";
|
|
const VIEWER_CMAP_OUTPUT = "external/bcmaps";
|
|
|
|
console.log();
|
|
console.log("### Building cmaps");
|
|
|
|
// Testing a file that usually present.
|
|
if (!checkFile(CMAP_INPUT + "/UniJIS-UCS2-H")) {
|
|
console.log("./external/cmaps has no cmap files, download them from:");
|
|
console.log(" https://github.com/adobe-type-tools/cmap-resources");
|
|
throw new Error("cmap files were not found");
|
|
}
|
|
|
|
// Remove old bcmap files.
|
|
fs.readdirSync(VIEWER_CMAP_OUTPUT).forEach(function (file) {
|
|
if (/\.bcmap$/i.test(file)) {
|
|
fs.unlinkSync(VIEWER_CMAP_OUTPUT + "/" + file);
|
|
}
|
|
});
|
|
|
|
const { compressCmaps } = await import(
|
|
"./external/cmapscompress/compress.mjs"
|
|
);
|
|
compressCmaps(CMAP_INPUT, VIEWER_CMAP_OUTPUT, true);
|
|
});
|
|
|
|
function preprocessCSS(source, defines) {
|
|
const outName = getTempFile("~preprocess", ".css");
|
|
preprocess(source, outName, defines);
|
|
const out = fs.readFileSync(outName).toString();
|
|
fs.unlinkSync(outName);
|
|
|
|
const i = source.lastIndexOf("/");
|
|
return createStringSource(source.substr(i + 1), out);
|
|
}
|
|
|
|
function discardCommentsCSS() {
|
|
let copyrightNum = 0;
|
|
|
|
function remove(comment) {
|
|
// Remove all comments, except the *first* license header.
|
|
if (comment.startsWith("Copyright") && copyrightNum++ === 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return postcssDiscardComments({ remove });
|
|
}
|
|
|
|
function preprocessHTML(source, defines) {
|
|
const outName = getTempFile("~preprocess", ".html");
|
|
preprocess(source, outName, defines);
|
|
const out = fs.readFileSync(outName).toString();
|
|
fs.unlinkSync(outName);
|
|
|
|
const i = source.lastIndexOf("/");
|
|
return createStringSource(source.substr(i + 1), `${out.trimEnd()}\n`);
|
|
}
|
|
|
|
function buildGeneric(defines, dir) {
|
|
rimraf.sync(dir);
|
|
|
|
return merge([
|
|
createMainBundle(defines).pipe(gulp.dest(dir + "build")),
|
|
createWorkerBundle(defines).pipe(gulp.dest(dir + "build")),
|
|
createSandboxBundle(defines).pipe(gulp.dest(dir + "build")),
|
|
createWebBundle(defines, {
|
|
defaultPreferencesDir: defines.SKIP_BABEL
|
|
? "generic/"
|
|
: "generic-legacy/",
|
|
}).pipe(gulp.dest(dir + "web")),
|
|
gulp.src(COMMON_WEB_FILES, { base: "web/" }).pipe(gulp.dest(dir + "web")),
|
|
gulp.src("LICENSE").pipe(gulp.dest(dir)),
|
|
gulp
|
|
.src(["web/locale/*/viewer.ftl", "web/locale/locale.json"], {
|
|
base: "web/",
|
|
})
|
|
.pipe(gulp.dest(dir + "web")),
|
|
createCMapBundle().pipe(gulp.dest(dir + "web/cmaps")),
|
|
createStandardFontBundle().pipe(gulp.dest(dir + "web/standard_fonts")),
|
|
|
|
preprocessHTML("web/viewer.html", defines).pipe(gulp.dest(dir + "web")),
|
|
preprocessCSS("web/viewer.css", defines)
|
|
.pipe(
|
|
postcss([
|
|
postcssDirPseudoClass(),
|
|
discardCommentsCSS(),
|
|
postcssNesting(),
|
|
autoprefixer(AUTOPREFIXER_CONFIG),
|
|
])
|
|
)
|
|
.pipe(gulp.dest(dir + "web")),
|
|
|
|
gulp
|
|
.src("web/compressed.tracemonkey-pldi-09.pdf")
|
|
.pipe(gulp.dest(dir + "web")),
|
|
]);
|
|
}
|
|
|
|
// Builds the generic production viewer that is only compatible with up-to-date
|
|
// HTML5 browsers, which implement modern ECMAScript features.
|
|
gulp.task(
|
|
"generic",
|
|
gulp.series(
|
|
createBuildNumber,
|
|
"locale",
|
|
function scriptingGeneric() {
|
|
const defines = { ...DEFINES, GENERIC: true };
|
|
return merge([
|
|
buildDefaultPreferences(defines, "generic/"),
|
|
createTemporaryScriptingBundle(defines),
|
|
]);
|
|
},
|
|
async function prefsGeneric() {
|
|
await parseDefaultPreferences("generic/");
|
|
},
|
|
function createGeneric() {
|
|
console.log();
|
|
console.log("### Creating generic viewer");
|
|
const defines = { ...DEFINES, GENERIC: true };
|
|
|
|
return buildGeneric(defines, GENERIC_DIR);
|
|
}
|
|
)
|
|
);
|
|
|
|
// Builds the generic production viewer that should be compatible with most
|
|
// older HTML5 browsers.
|
|
gulp.task(
|
|
"generic-legacy",
|
|
gulp.series(
|
|
createBuildNumber,
|
|
"locale",
|
|
function scriptingGenericLegacy() {
|
|
const defines = { ...DEFINES, GENERIC: true, SKIP_BABEL: false };
|
|
return merge([
|
|
buildDefaultPreferences(defines, "generic-legacy/"),
|
|
createTemporaryScriptingBundle(defines),
|
|
]);
|
|
},
|
|
async function prefsGenericLegacy() {
|
|
await parseDefaultPreferences("generic-legacy/");
|
|
},
|
|
function createGenericLegacy() {
|
|
console.log();
|
|
console.log("### Creating generic (legacy) viewer");
|
|
const defines = { ...DEFINES, GENERIC: true, SKIP_BABEL: false };
|
|
|
|
return buildGeneric(defines, GENERIC_LEGACY_DIR);
|
|
}
|
|
)
|
|
);
|
|
|
|
function buildComponents(defines, dir) {
|
|
rimraf.sync(dir);
|
|
|
|
const COMPONENTS_IMAGES = [
|
|
"web/images/annotation-*.svg",
|
|
"web/images/loading-icon.gif",
|
|
"web/images/altText_*.svg",
|
|
"web/images/editor-toolbar-*.svg",
|
|
];
|
|
|
|
return merge([
|
|
createComponentsBundle(defines).pipe(gulp.dest(dir)),
|
|
gulp.src(COMPONENTS_IMAGES).pipe(gulp.dest(dir + "images")),
|
|
preprocessCSS("web/pdf_viewer.css", defines)
|
|
.pipe(
|
|
postcss([
|
|
postcssDirPseudoClass(),
|
|
discardCommentsCSS(),
|
|
postcssNesting(),
|
|
autoprefixer(AUTOPREFIXER_CONFIG),
|
|
])
|
|
)
|
|
.pipe(gulp.dest(dir)),
|
|
]);
|
|
}
|
|
|
|
gulp.task(
|
|
"components",
|
|
gulp.series(createBuildNumber, function createComponents() {
|
|
console.log();
|
|
console.log("### Creating generic components");
|
|
const defines = { ...DEFINES, COMPONENTS: true, GENERIC: true };
|
|
|
|
return buildComponents(defines, COMPONENTS_DIR);
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"components-legacy",
|
|
gulp.series(createBuildNumber, function createComponentsLegacy() {
|
|
console.log();
|
|
console.log("### Creating generic (legacy) components");
|
|
const defines = {
|
|
...DEFINES,
|
|
COMPONENTS: true,
|
|
GENERIC: true,
|
|
SKIP_BABEL: false,
|
|
};
|
|
|
|
return buildComponents(defines, COMPONENTS_LEGACY_DIR);
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"image_decoders",
|
|
gulp.series(createBuildNumber, function createImageDecoders() {
|
|
console.log();
|
|
console.log("### Creating image decoders");
|
|
const defines = { ...DEFINES, GENERIC: true, IMAGE_DECODERS: true };
|
|
|
|
return createImageDecodersBundle(defines).pipe(
|
|
gulp.dest(IMAGE_DECODERS_DIR)
|
|
);
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"image_decoders-legacy",
|
|
gulp.series(createBuildNumber, function createImageDecodersLegacy() {
|
|
console.log();
|
|
console.log("### Creating (legacy) image decoders");
|
|
const defines = {
|
|
...DEFINES,
|
|
GENERIC: true,
|
|
IMAGE_DECODERS: true,
|
|
SKIP_BABEL: false,
|
|
};
|
|
|
|
return createImageDecodersBundle(defines).pipe(
|
|
gulp.dest(IMAGE_DECODERS_LEGACY_DIR)
|
|
);
|
|
})
|
|
);
|
|
|
|
function buildMinified(defines, dir) {
|
|
rimraf.sync(dir);
|
|
|
|
return merge([
|
|
createMainBundle(defines).pipe(gulp.dest(dir + "build")),
|
|
createWorkerBundle(defines).pipe(gulp.dest(dir + "build")),
|
|
createSandboxBundle(defines).pipe(gulp.dest(dir + "build")),
|
|
createImageDecodersBundle({ ...defines, IMAGE_DECODERS: true }).pipe(
|
|
gulp.dest(dir + "image_decoders")
|
|
),
|
|
]);
|
|
}
|
|
|
|
async function parseMinified(dir) {
|
|
const pdfFile = fs.readFileSync(dir + "build/pdf.mjs").toString();
|
|
const pdfWorkerFile = fs
|
|
.readFileSync(dir + "build/pdf.worker.mjs")
|
|
.toString();
|
|
const pdfSandboxFile = fs
|
|
.readFileSync(dir + "build/pdf.sandbox.mjs")
|
|
.toString();
|
|
const pdfImageDecodersFile = fs
|
|
.readFileSync(dir + "image_decoders/pdf.image_decoders.mjs")
|
|
.toString();
|
|
|
|
console.log();
|
|
console.log("### Minifying js files");
|
|
|
|
const { minify } = await import("terser");
|
|
const options = {
|
|
compress: {
|
|
// V8 chokes on very long sequences, work around that.
|
|
sequences: false,
|
|
},
|
|
keep_classnames: true,
|
|
keep_fnames: true,
|
|
module: true,
|
|
};
|
|
|
|
fs.writeFileSync(
|
|
dir + "build/pdf.min.mjs",
|
|
(await minify(pdfFile, options)).code
|
|
);
|
|
fs.writeFileSync(
|
|
dir + "build/pdf.worker.min.mjs",
|
|
(await minify(pdfWorkerFile, options)).code
|
|
);
|
|
fs.writeFileSync(
|
|
dir + "build/pdf.sandbox.min.mjs",
|
|
(await minify(pdfSandboxFile, options)).code
|
|
);
|
|
fs.writeFileSync(
|
|
dir + "image_decoders/pdf.image_decoders.min.mjs",
|
|
(await minify(pdfImageDecodersFile, options)).code
|
|
);
|
|
|
|
console.log();
|
|
console.log("### Cleaning js files");
|
|
|
|
fs.unlinkSync(dir + "build/pdf.mjs");
|
|
fs.unlinkSync(dir + "build/pdf.worker.mjs");
|
|
fs.unlinkSync(dir + "build/pdf.sandbox.mjs");
|
|
|
|
fs.renameSync(dir + "build/pdf.min.mjs", dir + "build/pdf.mjs");
|
|
fs.renameSync(dir + "build/pdf.worker.min.mjs", dir + "build/pdf.worker.mjs");
|
|
fs.renameSync(
|
|
dir + "build/pdf.sandbox.min.mjs",
|
|
dir + "build/pdf.sandbox.mjs"
|
|
);
|
|
fs.renameSync(
|
|
dir + "image_decoders/pdf.image_decoders.min.mjs",
|
|
dir + "image_decoders/pdf.image_decoders.mjs"
|
|
);
|
|
}
|
|
|
|
gulp.task(
|
|
"minified",
|
|
gulp.series(
|
|
createBuildNumber,
|
|
"locale",
|
|
function scriptingMinified() {
|
|
const defines = { ...DEFINES, MINIFIED: true, GENERIC: true };
|
|
return merge([
|
|
buildDefaultPreferences(defines, "minified/"),
|
|
createTemporaryScriptingBundle(defines),
|
|
]);
|
|
},
|
|
async function prefsMinified() {
|
|
await parseDefaultPreferences("minified/");
|
|
},
|
|
function createMinified() {
|
|
console.log();
|
|
console.log("### Creating minified viewer");
|
|
const defines = { ...DEFINES, MINIFIED: true, GENERIC: true };
|
|
|
|
return buildMinified(defines, MINIFIED_DIR);
|
|
},
|
|
async function compressMinified(done) {
|
|
await parseMinified(MINIFIED_DIR);
|
|
done();
|
|
}
|
|
)
|
|
);
|
|
|
|
gulp.task(
|
|
"minified-legacy",
|
|
gulp.series(
|
|
createBuildNumber,
|
|
"locale",
|
|
function scriptingMinifiedLegacy() {
|
|
const defines = {
|
|
...DEFINES,
|
|
MINIFIED: true,
|
|
GENERIC: true,
|
|
SKIP_BABEL: false,
|
|
};
|
|
return merge([
|
|
buildDefaultPreferences(defines, "minified-legacy/"),
|
|
createTemporaryScriptingBundle(defines),
|
|
]);
|
|
},
|
|
async function prefsMinifiedLegacy() {
|
|
await parseDefaultPreferences("minified-legacy/");
|
|
},
|
|
function createMinifiedLegacy() {
|
|
console.log();
|
|
console.log("### Creating minified (legacy) viewer");
|
|
const defines = {
|
|
...DEFINES,
|
|
MINIFIED: true,
|
|
GENERIC: true,
|
|
SKIP_BABEL: false,
|
|
};
|
|
|
|
return buildMinified(defines, MINIFIED_LEGACY_DIR);
|
|
},
|
|
async function compressMinifiedLegacy(done) {
|
|
await parseMinified(MINIFIED_LEGACY_DIR);
|
|
done();
|
|
}
|
|
)
|
|
);
|
|
|
|
function preprocessDefaultPreferences(content) {
|
|
const licenseHeader = fs.readFileSync("./src/license_header.js").toString();
|
|
|
|
const MODIFICATION_WARNING =
|
|
"//\n// THIS FILE IS GENERATED AUTOMATICALLY, DO NOT EDIT MANUALLY!\n//\n";
|
|
|
|
const bundleDefines = {
|
|
...DEFINES,
|
|
DEFAULT_PREFERENCES: getDefaultPreferences("mozcentral/"),
|
|
};
|
|
|
|
content = preprocessPDFJSCode(
|
|
{
|
|
rootPath: __dirname,
|
|
defines: bundleDefines,
|
|
},
|
|
content
|
|
);
|
|
|
|
return licenseHeader + "\n" + MODIFICATION_WARNING + "\n" + content + "\n";
|
|
}
|
|
|
|
function replaceMozcentralCSS() {
|
|
return replace(/var\(--(inline-(?:start|end))\)/g, "$1");
|
|
}
|
|
|
|
gulp.task(
|
|
"mozcentral",
|
|
gulp.series(
|
|
createBuildNumber,
|
|
function scriptingMozcentral() {
|
|
const defines = { ...DEFINES, MOZCENTRAL: true };
|
|
return buildDefaultPreferences(defines, "mozcentral/");
|
|
},
|
|
async function prefsMozcentral() {
|
|
await parseDefaultPreferences("mozcentral/");
|
|
},
|
|
function createMozcentral() {
|
|
console.log();
|
|
console.log("### Building mozilla-central extension");
|
|
const defines = { ...DEFINES, MOZCENTRAL: true };
|
|
const gvDefines = { ...defines, GECKOVIEW: true };
|
|
|
|
const MOZCENTRAL_DIR = BUILD_DIR + "mozcentral/",
|
|
MOZCENTRAL_EXTENSION_DIR = MOZCENTRAL_DIR + "browser/extensions/pdfjs/",
|
|
MOZCENTRAL_CONTENT_DIR = MOZCENTRAL_EXTENSION_DIR + "content/",
|
|
MOZCENTRAL_L10N_DIR =
|
|
MOZCENTRAL_DIR + "browser/locales/en-US/pdfviewer/",
|
|
FIREFOX_CONTENT_DIR = EXTENSION_SRC_DIR + "/firefox/content/";
|
|
|
|
const MOZCENTRAL_WEB_FILES = [
|
|
...COMMON_WEB_FILES,
|
|
"!web/images/toolbarButton-openFile.svg",
|
|
];
|
|
const MOZCENTRAL_AUTOPREFIXER_CONFIG = {
|
|
overrideBrowserslist: ["last 1 firefox versions"],
|
|
};
|
|
|
|
// Clear out everything in the firefox extension build directory
|
|
rimraf.sync(MOZCENTRAL_DIR);
|
|
|
|
return merge([
|
|
createMainBundle(defines).pipe(
|
|
gulp.dest(MOZCENTRAL_CONTENT_DIR + "build")
|
|
),
|
|
createScriptingBundle(defines).pipe(
|
|
gulp.dest(MOZCENTRAL_CONTENT_DIR + "build")
|
|
),
|
|
createSandboxExternal(defines).pipe(
|
|
gulp.dest(MOZCENTRAL_CONTENT_DIR + "build")
|
|
),
|
|
createWorkerBundle(defines).pipe(
|
|
gulp.dest(MOZCENTRAL_CONTENT_DIR + "build")
|
|
),
|
|
createWebBundle(defines, { defaultPreferencesDir: "mozcentral/" }).pipe(
|
|
gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")
|
|
),
|
|
createGVWebBundle(gvDefines, {
|
|
defaultPreferencesDir: "mozcentral/",
|
|
}).pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")),
|
|
gulp
|
|
.src(MOZCENTRAL_WEB_FILES, { base: "web/" })
|
|
.pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")),
|
|
createCMapBundle().pipe(
|
|
gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/cmaps")
|
|
),
|
|
createStandardFontBundle().pipe(
|
|
gulp.dest(MOZCENTRAL_CONTENT_DIR + "web/standard_fonts")
|
|
),
|
|
|
|
preprocessHTML("web/viewer.html", defines).pipe(
|
|
gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")
|
|
),
|
|
preprocessHTML("web/viewer-geckoview.html", gvDefines).pipe(
|
|
gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")
|
|
),
|
|
|
|
preprocessCSS("web/viewer.css", defines)
|
|
.pipe(
|
|
postcss([
|
|
discardCommentsCSS(),
|
|
autoprefixer(MOZCENTRAL_AUTOPREFIXER_CONFIG),
|
|
])
|
|
)
|
|
.pipe(replaceMozcentralCSS())
|
|
.pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")),
|
|
|
|
preprocessCSS("web/viewer-geckoview.css", gvDefines)
|
|
.pipe(
|
|
postcss([
|
|
discardCommentsCSS(),
|
|
autoprefixer(MOZCENTRAL_AUTOPREFIXER_CONFIG),
|
|
])
|
|
)
|
|
.pipe(replaceMozcentralCSS())
|
|
.pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR + "web")),
|
|
|
|
gulp.src("l10n/en-US/*.ftl").pipe(gulp.dest(MOZCENTRAL_L10N_DIR)),
|
|
gulp.src("LICENSE").pipe(gulp.dest(MOZCENTRAL_EXTENSION_DIR)),
|
|
gulp
|
|
.src(FIREFOX_CONTENT_DIR + "PdfJsDefaultPreferences.sys.mjs")
|
|
.pipe(transform("utf8", preprocessDefaultPreferences))
|
|
.pipe(gulp.dest(MOZCENTRAL_CONTENT_DIR)),
|
|
]);
|
|
}
|
|
)
|
|
);
|
|
|
|
gulp.task(
|
|
"chromium",
|
|
gulp.series(
|
|
createBuildNumber,
|
|
"locale",
|
|
function scriptingChromium() {
|
|
const defines = { ...DEFINES, CHROME: true, SKIP_BABEL: false };
|
|
return merge([
|
|
buildDefaultPreferences(defines, "chromium/"),
|
|
createTemporaryScriptingBundle(defines),
|
|
]);
|
|
},
|
|
async function prefsChromium() {
|
|
await parseDefaultPreferences("chromium/");
|
|
},
|
|
function createChromium() {
|
|
console.log();
|
|
console.log("### Building Chromium extension");
|
|
const defines = { ...DEFINES, CHROME: true, SKIP_BABEL: false };
|
|
|
|
const CHROME_BUILD_DIR = BUILD_DIR + "/chromium/",
|
|
CHROME_BUILD_CONTENT_DIR = CHROME_BUILD_DIR + "/content/";
|
|
|
|
const CHROME_WEB_FILES = [
|
|
...COMMON_WEB_FILES,
|
|
"!web/images/toolbarButton-openFile.svg",
|
|
];
|
|
|
|
// Clear out everything in the chrome extension build directory
|
|
rimraf.sync(CHROME_BUILD_DIR);
|
|
|
|
const version = getVersionJSON().version;
|
|
|
|
return merge([
|
|
createMainBundle(defines).pipe(
|
|
gulp.dest(CHROME_BUILD_CONTENT_DIR + "build")
|
|
),
|
|
createWorkerBundle(defines).pipe(
|
|
gulp.dest(CHROME_BUILD_CONTENT_DIR + "build")
|
|
),
|
|
createSandboxBundle(defines).pipe(
|
|
gulp.dest(CHROME_BUILD_CONTENT_DIR + "build")
|
|
),
|
|
createWebBundle(defines, { defaultPreferencesDir: "chromium/" }).pipe(
|
|
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")
|
|
),
|
|
gulp
|
|
.src(CHROME_WEB_FILES, { base: "web/" })
|
|
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")),
|
|
|
|
gulp
|
|
.src(["web/locale/*/viewer.ftl", "web/locale/locale.json"], {
|
|
base: "web/",
|
|
})
|
|
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")),
|
|
createCMapBundle().pipe(
|
|
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/cmaps")
|
|
),
|
|
createStandardFontBundle().pipe(
|
|
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web/standard_fonts")
|
|
),
|
|
|
|
preprocessHTML("web/viewer.html", defines).pipe(
|
|
gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")
|
|
),
|
|
preprocessCSS("web/viewer.css", defines)
|
|
.pipe(
|
|
postcss([
|
|
postcssDirPseudoClass(),
|
|
discardCommentsCSS(),
|
|
postcssNesting(),
|
|
autoprefixer(AUTOPREFIXER_CONFIG),
|
|
])
|
|
)
|
|
.pipe(gulp.dest(CHROME_BUILD_CONTENT_DIR + "web")),
|
|
|
|
gulp.src("LICENSE").pipe(gulp.dest(CHROME_BUILD_DIR)),
|
|
gulp
|
|
.src("extensions/chromium/manifest.json")
|
|
.pipe(replace(/\bPDFJSSCRIPT_VERSION\b/g, version))
|
|
.pipe(gulp.dest(CHROME_BUILD_DIR)),
|
|
gulp
|
|
.src(
|
|
[
|
|
"extensions/chromium/**/*.{html,js,css,png}",
|
|
"extensions/chromium/preferences_schema.json",
|
|
],
|
|
{ base: "extensions/chromium/" }
|
|
)
|
|
.pipe(gulp.dest(CHROME_BUILD_DIR)),
|
|
]);
|
|
}
|
|
)
|
|
);
|
|
|
|
gulp.task("jsdoc", function (done) {
|
|
console.log();
|
|
console.log("### Generating documentation (JSDoc)");
|
|
|
|
const JSDOC_FILES = ["src/display/api.js"];
|
|
|
|
rimraf(JSDOC_BUILD_DIR, function () {
|
|
mkdirp(JSDOC_BUILD_DIR).then(function () {
|
|
const command =
|
|
'"node_modules/.bin/jsdoc" -d ' +
|
|
JSDOC_BUILD_DIR +
|
|
" " +
|
|
JSDOC_FILES.join(" ");
|
|
exec(command, done);
|
|
});
|
|
});
|
|
});
|
|
|
|
gulp.task("types", function (done) {
|
|
console.log("### Generating TypeScript definitions using `tsc`");
|
|
exec(
|
|
`"node_modules/.bin/tsc" --outDir ${TYPES_DIR} --project .`,
|
|
function () {
|
|
exec(`"node_modules/.bin/tsc-alias" --outDir ${TYPES_DIR}`, done);
|
|
}
|
|
);
|
|
});
|
|
|
|
function buildLibHelper(bundleDefines, inputStream, outputDir) {
|
|
function babelPluginReplaceNonWebpackImport(b) {
|
|
return {
|
|
visitor: {
|
|
Identifier(curPath, state) {
|
|
if (curPath.node.name === "__non_webpack_import__") {
|
|
curPath.replaceWith(b.types.identifier("import"));
|
|
}
|
|
},
|
|
},
|
|
};
|
|
}
|
|
function preprocessLib(content) {
|
|
const skipBabel = bundleDefines.SKIP_BABEL;
|
|
content = preprocessPDFJSCode(ctx, content);
|
|
content = babel.transform(content, {
|
|
sourceType: "module",
|
|
presets: skipBabel
|
|
? undefined
|
|
: [["@babel/preset-env", { loose: false, modules: false }]],
|
|
plugins: [babelPluginReplaceNonWebpackImport],
|
|
targets: BABEL_TARGETS,
|
|
}).code;
|
|
content = content.replaceAll(
|
|
/(\sfrom\s".*?)(?:\/src)(\/[^"]*"?;)$/gm,
|
|
(all, prefix, suffix) => prefix + suffix
|
|
);
|
|
return licenseHeaderLibre + content;
|
|
}
|
|
const ctx = {
|
|
rootPath: __dirname,
|
|
saveComments: false,
|
|
defines: bundleDefines,
|
|
map: {
|
|
"pdfjs-lib": "../pdf.js",
|
|
"display-fetch_stream": "./fetch_stream.js",
|
|
"display-network": "./network.js",
|
|
"display-node_stream": "./node_stream.js",
|
|
"display-node_utils": "./node_utils.js",
|
|
"fluent-bundle": "../../../node_modules/@fluent/bundle/esm/index.js",
|
|
"fluent-dom": "../../../node_modules/@fluent/dom/esm/index.js",
|
|
"web-l10n_utils": "../web/l10n_utils.js",
|
|
},
|
|
};
|
|
const licenseHeaderLibre = fs
|
|
.readFileSync("./src/license_header_libre.js")
|
|
.toString();
|
|
return inputStream
|
|
.pipe(transform("utf8", preprocessLib))
|
|
.pipe(gulp.dest(outputDir));
|
|
}
|
|
|
|
function buildLib(defines, dir) {
|
|
const versionInfo = getVersionJSON();
|
|
|
|
const bundleDefines = {
|
|
...defines,
|
|
BUNDLE_VERSION: versionInfo.version,
|
|
BUNDLE_BUILD: versionInfo.commit,
|
|
TESTING: defines.TESTING ?? process.env.TESTING === "true",
|
|
BROWSER_PREFERENCES: getBrowserPreferences(
|
|
defines.SKIP_BABEL ? "lib/" : "lib-legacy/"
|
|
),
|
|
DEFAULT_PREFERENCES: getDefaultPreferences(
|
|
defines.SKIP_BABEL ? "lib/" : "lib-legacy/"
|
|
),
|
|
DEFAULT_FTL: getDefaultFtl(),
|
|
};
|
|
|
|
const inputStream = merge([
|
|
gulp.src(
|
|
[
|
|
"src/{core,display,shared}/**/*.js",
|
|
"src/{pdf,pdf.image_decoders,pdf.worker}.js",
|
|
],
|
|
{ base: "src/" }
|
|
),
|
|
gulp.src(["web/*.js", "!web/{pdfjs,viewer}.js"], { base: "." }),
|
|
gulp.src("test/unit/*.js", { base: "." }),
|
|
]);
|
|
|
|
return buildLibHelper(bundleDefines, inputStream, dir);
|
|
}
|
|
|
|
gulp.task(
|
|
"lib",
|
|
gulp.series(
|
|
createBuildNumber,
|
|
function scriptingLib() {
|
|
const defines = { ...DEFINES, GENERIC: true, LIB: true };
|
|
return merge([
|
|
buildDefaultPreferences(defines, "lib/"),
|
|
createTemporaryScriptingBundle(defines),
|
|
]);
|
|
},
|
|
async function prefsLib() {
|
|
await parseDefaultPreferences("lib/");
|
|
},
|
|
function createLib() {
|
|
const defines = { ...DEFINES, GENERIC: true, LIB: true };
|
|
|
|
return merge([
|
|
buildLib(defines, "build/lib/"),
|
|
createSandboxBundle(defines).pipe(gulp.dest("build/lib/")),
|
|
]);
|
|
}
|
|
)
|
|
);
|
|
|
|
gulp.task(
|
|
"lib-legacy",
|
|
gulp.series(
|
|
createBuildNumber,
|
|
function scriptingLibLegacy() {
|
|
const defines = {
|
|
...DEFINES,
|
|
GENERIC: true,
|
|
LIB: true,
|
|
SKIP_BABEL: false,
|
|
};
|
|
return merge([
|
|
buildDefaultPreferences(defines, "lib-legacy/"),
|
|
createTemporaryScriptingBundle(defines),
|
|
]);
|
|
},
|
|
async function prefsLibLegacy() {
|
|
await parseDefaultPreferences("lib-legacy/");
|
|
},
|
|
function createLibLegacy() {
|
|
const defines = {
|
|
...DEFINES,
|
|
GENERIC: true,
|
|
LIB: true,
|
|
SKIP_BABEL: false,
|
|
};
|
|
|
|
return merge([
|
|
buildLib(defines, "build/lib-legacy/"),
|
|
createSandboxBundle(defines).pipe(gulp.dest("build/lib-legacy/")),
|
|
]);
|
|
}
|
|
)
|
|
);
|
|
|
|
function compressPublish(targetName, dir) {
|
|
return gulp
|
|
.src(dir + "**")
|
|
.pipe(zip(targetName))
|
|
.pipe(gulp.dest(BUILD_DIR))
|
|
.on("end", function () {
|
|
console.log("Built distribution file: " + targetName);
|
|
});
|
|
}
|
|
|
|
gulp.task(
|
|
"publish",
|
|
gulp.series("generic", "generic-legacy", function createPublish(done) {
|
|
const version = JSON.parse(
|
|
fs.readFileSync(BUILD_DIR + "version.json").toString()
|
|
).version;
|
|
|
|
config.stableVersion = version;
|
|
|
|
return merge([
|
|
createStringSource(CONFIG_FILE, JSON.stringify(config, null, 2)).pipe(
|
|
gulp.dest(".")
|
|
),
|
|
compressPublish("pdfjs-" + version + "-dist.zip", GENERIC_DIR),
|
|
compressPublish(
|
|
"pdfjs-" + version + "-legacy-dist.zip",
|
|
GENERIC_LEGACY_DIR
|
|
),
|
|
]);
|
|
})
|
|
);
|
|
|
|
function setTestEnv(done) {
|
|
process.env.TESTING = "true";
|
|
// TODO: Re-write the relevant unit-tests, which are using `new Date(...)`,
|
|
// to not required the following time-zone hack since it doesn't work
|
|
// when the unit-tests are run directly in the browser.
|
|
process.env.TZ = "UTC";
|
|
done();
|
|
}
|
|
|
|
gulp.task(
|
|
"test",
|
|
gulp.series(setTestEnv, "generic", "components", function runTest() {
|
|
return streamqueue(
|
|
{ objectMode: true },
|
|
createTestSource("unit"),
|
|
createTestSource("browser"),
|
|
createTestSource("integration")
|
|
);
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"bottest",
|
|
gulp.series(setTestEnv, "generic", "components", function runBotTest() {
|
|
return streamqueue(
|
|
{ objectMode: true },
|
|
createTestSource("unit", { bot: true }),
|
|
createTestSource("font", { bot: true }),
|
|
createTestSource("browser", { bot: true }),
|
|
createTestSource("integration")
|
|
);
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"xfatest",
|
|
gulp.series(setTestEnv, "generic", "components", function runXfaTest() {
|
|
return streamqueue(
|
|
{ objectMode: true },
|
|
createTestSource("unit"),
|
|
createTestSource("browser", { xfaOnly: true }),
|
|
createTestSource("integration")
|
|
);
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"botxfatest",
|
|
gulp.series(setTestEnv, "generic", "components", function runBotXfaTest() {
|
|
return streamqueue(
|
|
{ objectMode: true },
|
|
createTestSource("unit", { bot: true }),
|
|
createTestSource("font", { bot: true }),
|
|
createTestSource("browser", { bot: true, xfaOnly: true }),
|
|
createTestSource("integration")
|
|
);
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"browsertest",
|
|
gulp.series(setTestEnv, "generic", "components", function runBrowserTest() {
|
|
return createTestSource("browser");
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"botbrowsertest",
|
|
gulp.series(
|
|
setTestEnv,
|
|
"generic",
|
|
"components",
|
|
function runBotBrowserTest() {
|
|
return streamqueue(
|
|
{ objectMode: true },
|
|
createTestSource("browser", { bot: true })
|
|
);
|
|
}
|
|
)
|
|
);
|
|
|
|
gulp.task(
|
|
"unittest",
|
|
gulp.series(setTestEnv, "generic", function runUnitTest() {
|
|
return createTestSource("unit");
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"integrationtest",
|
|
gulp.series(setTestEnv, "generic", function runIntegrationTest() {
|
|
return createTestSource("integration");
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"fonttest",
|
|
gulp.series(setTestEnv, function runFontTest() {
|
|
return createTestSource("font");
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"makeref",
|
|
gulp.series(setTestEnv, "generic", "components", function runMakeref(done) {
|
|
makeRef(done);
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"botmakeref",
|
|
gulp.series(
|
|
setTestEnv,
|
|
"generic",
|
|
"components",
|
|
function runBotMakeref(done) {
|
|
makeRef(done, true);
|
|
}
|
|
)
|
|
);
|
|
|
|
gulp.task(
|
|
"typestest",
|
|
gulp.series(
|
|
setTestEnv,
|
|
"generic",
|
|
"types",
|
|
function createTypesTest() {
|
|
return merge([
|
|
packageJson().pipe(gulp.dest(TYPESTEST_DIR)),
|
|
gulp
|
|
.src("external/dist/**/*", {
|
|
base: "external/dist",
|
|
removeBOM: false,
|
|
})
|
|
.pipe(gulp.dest(TYPESTEST_DIR)),
|
|
gulp
|
|
.src(TYPES_DIR + "**/*", { base: TYPES_DIR })
|
|
.pipe(gulp.dest(TYPESTEST_DIR + "types/")),
|
|
]);
|
|
},
|
|
function runTypesTest(done) {
|
|
exec('"node_modules/.bin/tsc" -p test/types', function (err, stdout) {
|
|
if (err) {
|
|
console.log(`Couldn't compile TypeScript test: ${stdout}`);
|
|
}
|
|
done(err);
|
|
});
|
|
}
|
|
)
|
|
);
|
|
|
|
function createBaseline(done) {
|
|
console.log();
|
|
console.log("### Creating baseline environment");
|
|
|
|
const baselineCommit = process.env.BASELINE;
|
|
if (!baselineCommit) {
|
|
done(new Error("Missing baseline commit. Specify the BASELINE variable."));
|
|
return;
|
|
}
|
|
|
|
let initializeCommand = "git fetch origin";
|
|
if (!checkDir(BASELINE_DIR)) {
|
|
mkdirp.sync(BASELINE_DIR);
|
|
initializeCommand = "git clone ../../ .";
|
|
}
|
|
|
|
const workingDirectory = path.resolve(process.cwd(), BASELINE_DIR);
|
|
exec(initializeCommand, { cwd: workingDirectory }, function (error) {
|
|
if (error) {
|
|
done(new Error("Baseline clone/fetch failed."));
|
|
return;
|
|
}
|
|
|
|
exec(
|
|
"git checkout " + baselineCommit,
|
|
{ cwd: workingDirectory },
|
|
function (error2) {
|
|
if (error2) {
|
|
done(new Error("Baseline commit checkout failed."));
|
|
return;
|
|
}
|
|
|
|
console.log('Baseline commit "' + baselineCommit + '" checked out.');
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
gulp.task(
|
|
"unittestcli",
|
|
gulp.series(setTestEnv, "lib-legacy", function runUnitTestCli(done) {
|
|
const options = [
|
|
"node_modules/jasmine/bin/jasmine",
|
|
"JASMINE_CONFIG_PATH=test/unit/clitests.json",
|
|
];
|
|
const jasmineProcess = startNode(options, { stdio: "inherit" });
|
|
jasmineProcess.on("close", function (code) {
|
|
if (code !== 0) {
|
|
done(new Error("Unit tests failed."));
|
|
return;
|
|
}
|
|
done();
|
|
});
|
|
})
|
|
);
|
|
|
|
gulp.task("lint", function (done) {
|
|
console.log();
|
|
console.log("### Linting JS/CSS files");
|
|
|
|
// Ensure that we lint the Firefox specific *.jsm files too.
|
|
const esLintOptions = [
|
|
"node_modules/eslint/bin/eslint",
|
|
"--ext",
|
|
".js,.jsm,.mjs,.json",
|
|
".",
|
|
"--report-unused-disable-directives",
|
|
];
|
|
if (process.argv.includes("--fix")) {
|
|
esLintOptions.push("--fix");
|
|
}
|
|
|
|
const styleLintOptions = [
|
|
"node_modules/stylelint/bin/stylelint.mjs",
|
|
"**/*.css",
|
|
"--report-needless-disables",
|
|
];
|
|
if (process.argv.includes("--fix")) {
|
|
styleLintOptions.push("--fix");
|
|
}
|
|
|
|
const esLintProcess = startNode(esLintOptions, { stdio: "inherit" });
|
|
esLintProcess.on("close", function (esLintCode) {
|
|
if (esLintCode !== 0) {
|
|
done(new Error("ESLint failed."));
|
|
return;
|
|
}
|
|
|
|
const styleLintProcess = startNode(styleLintOptions, { stdio: "inherit" });
|
|
styleLintProcess.on("close", function (styleLintCode) {
|
|
if (styleLintCode !== 0) {
|
|
done(new Error("Stylelint failed."));
|
|
return;
|
|
}
|
|
console.log("files checked, no errors found");
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
gulp.task(
|
|
"lint-chromium",
|
|
gulp.series(
|
|
function scriptingLintChromium() {
|
|
const defines = {
|
|
...DEFINES,
|
|
CHROME: true,
|
|
SKIP_BABEL: false,
|
|
TESTING: false,
|
|
};
|
|
return buildDefaultPreferences(defines, "lint-chromium/");
|
|
},
|
|
async function prefsLintChromium() {
|
|
await parseDefaultPreferences("lint-chromium/");
|
|
},
|
|
function runLintChromium(done) {
|
|
console.log();
|
|
console.log("### Checking supplemental Chromium files");
|
|
|
|
if (
|
|
!checkChromePreferencesFile(
|
|
"extensions/chromium/preferences_schema.json",
|
|
getDefaultPreferences("lint-chromium/")
|
|
)
|
|
) {
|
|
done(new Error("chromium/preferences_schema is not in sync."));
|
|
return;
|
|
}
|
|
done();
|
|
}
|
|
)
|
|
);
|
|
|
|
gulp.task(
|
|
"dev-sandbox",
|
|
gulp.series(
|
|
function scriptingDevSandbox() {
|
|
const defines = { ...DEFINES, GENERIC: true, TESTING: true };
|
|
return createTemporaryScriptingBundle(defines, {
|
|
disableVersionInfo: true,
|
|
});
|
|
},
|
|
function createDevSandbox() {
|
|
console.log();
|
|
console.log("### Building development sandbox");
|
|
|
|
const defines = { ...DEFINES, GENERIC: true, TESTING: true };
|
|
const sandboxDir = BUILD_DIR + "dev-sandbox/";
|
|
|
|
rimraf.sync(sandboxDir);
|
|
|
|
return createSandboxBundle(defines, {
|
|
disableVersionInfo: true,
|
|
}).pipe(gulp.dest(sandboxDir));
|
|
}
|
|
)
|
|
);
|
|
|
|
gulp.task(
|
|
"server",
|
|
gulp.parallel(
|
|
function watchLocale() {
|
|
gulp.watch(
|
|
"l10n/**/*.ftl",
|
|
{ ignoreInitial: false },
|
|
gulp.series("locale")
|
|
);
|
|
},
|
|
function watchDevSandbox() {
|
|
gulp.watch(
|
|
[
|
|
"src/pdf.{sandbox,sandbox.external,scripting}.js",
|
|
"src/scripting_api/*.js",
|
|
"src/shared/scripting_utils.js",
|
|
"external/quickjs/*.js",
|
|
],
|
|
{ ignoreInitial: false },
|
|
gulp.series("dev-sandbox")
|
|
);
|
|
},
|
|
async function createServer() {
|
|
console.log();
|
|
console.log("### Starting local server");
|
|
|
|
const { WebServer } = await import("./test/webserver.mjs");
|
|
const server = new WebServer();
|
|
server.port = 8888;
|
|
server.start();
|
|
}
|
|
)
|
|
);
|
|
|
|
gulp.task("clean", function (done) {
|
|
console.log();
|
|
console.log("### Cleaning up project builds");
|
|
|
|
rimraf(BUILD_DIR, done);
|
|
});
|
|
|
|
gulp.task("importl10n", async function () {
|
|
const { downloadL10n } = await import("./external/importL10n/locales.mjs");
|
|
|
|
console.log();
|
|
console.log("### Importing translations from mozilla-central");
|
|
|
|
if (!fs.existsSync(L10N_DIR)) {
|
|
fs.mkdirSync(L10N_DIR);
|
|
}
|
|
await downloadL10n(L10N_DIR);
|
|
});
|
|
|
|
function ghPagesPrepare() {
|
|
console.log();
|
|
console.log("### Creating web site");
|
|
|
|
rimraf.sync(GH_PAGES_DIR);
|
|
|
|
return merge([
|
|
gulp
|
|
.src(GENERIC_DIR + "**/*", { base: GENERIC_DIR, removeBOM: false })
|
|
.pipe(gulp.dest(GH_PAGES_DIR)),
|
|
gulp
|
|
.src(GENERIC_LEGACY_DIR + "**/*", {
|
|
base: GENERIC_LEGACY_DIR,
|
|
removeBOM: false,
|
|
})
|
|
.pipe(gulp.dest(GH_PAGES_DIR + "legacy/")),
|
|
gulp
|
|
.src(JSDOC_BUILD_DIR + "**/*", { base: JSDOC_BUILD_DIR })
|
|
.pipe(gulp.dest(GH_PAGES_DIR + "api/draft/")),
|
|
]);
|
|
}
|
|
|
|
gulp.task("wintersmith", async function () {
|
|
const { default: wintersmith } = await import("wintersmith");
|
|
const env = wintersmith("docs/config.json");
|
|
|
|
return new Promise((resolve, reject) => {
|
|
env.build(GH_PAGES_DIR, function (error) {
|
|
if (error) {
|
|
reject(error);
|
|
return;
|
|
}
|
|
|
|
replaceInFile(
|
|
GH_PAGES_DIR + "/getting_started/index.html",
|
|
/STABLE_VERSION/g,
|
|
config.stableVersion
|
|
);
|
|
|
|
console.log("Done building with wintersmith.");
|
|
resolve();
|
|
});
|
|
});
|
|
});
|
|
|
|
gulp.task(
|
|
"web",
|
|
gulp.series(
|
|
"generic",
|
|
"generic-legacy",
|
|
"jsdoc",
|
|
ghPagesPrepare,
|
|
"wintersmith"
|
|
)
|
|
);
|
|
|
|
function packageJson() {
|
|
const VERSION = getVersionJSON().version;
|
|
|
|
const DIST_NAME = "pdfjs-dist";
|
|
const DIST_DESCRIPTION = "Generic build of Mozilla's PDF.js library.";
|
|
const DIST_KEYWORDS = ["Mozilla", "pdf", "pdf.js"];
|
|
const DIST_HOMEPAGE = "http://mozilla.github.io/pdf.js/";
|
|
const DIST_BUGS_URL = "https://github.com/mozilla/pdf.js/issues";
|
|
const DIST_LICENSE = "Apache-2.0";
|
|
|
|
const npmManifest = {
|
|
name: DIST_NAME,
|
|
version: VERSION,
|
|
main: "build/pdf.mjs",
|
|
types: "types/src/pdf.d.ts",
|
|
description: DIST_DESCRIPTION,
|
|
keywords: DIST_KEYWORDS,
|
|
homepage: DIST_HOMEPAGE,
|
|
bugs: DIST_BUGS_URL,
|
|
license: DIST_LICENSE,
|
|
optionalDependencies: {
|
|
canvas: "^2.11.2",
|
|
"path2d-polyfill": "^2.0.1",
|
|
},
|
|
browser: {
|
|
canvas: false,
|
|
fs: false,
|
|
http: false,
|
|
https: false,
|
|
url: false,
|
|
},
|
|
repository: {
|
|
type: "git",
|
|
url: DIST_REPO_URL,
|
|
},
|
|
engines: {
|
|
node: ">=18",
|
|
},
|
|
};
|
|
|
|
return createStringSource(
|
|
"package.json",
|
|
JSON.stringify(npmManifest, null, 2)
|
|
);
|
|
}
|
|
|
|
gulp.task(
|
|
"dist-pre",
|
|
gulp.series(
|
|
"generic",
|
|
"generic-legacy",
|
|
"components",
|
|
"components-legacy",
|
|
"image_decoders",
|
|
"image_decoders-legacy",
|
|
"minified",
|
|
"minified-legacy",
|
|
"types",
|
|
function createDist() {
|
|
console.log();
|
|
console.log("### Cloning baseline distribution");
|
|
|
|
rimraf.sync(DIST_DIR);
|
|
mkdirp.sync(DIST_DIR);
|
|
safeSpawnSync("git", ["clone", "--depth", "1", DIST_REPO_URL, DIST_DIR]);
|
|
|
|
console.log();
|
|
console.log("### Overwriting all files");
|
|
rimraf.sync(path.join(DIST_DIR, "*"));
|
|
|
|
return merge([
|
|
packageJson().pipe(gulp.dest(DIST_DIR)),
|
|
gulp
|
|
.src("external/dist/**/*", {
|
|
base: "external/dist",
|
|
removeBOM: false,
|
|
})
|
|
.pipe(gulp.dest(DIST_DIR)),
|
|
gulp.src(GENERIC_DIR + "LICENSE").pipe(gulp.dest(DIST_DIR)),
|
|
gulp
|
|
.src(GENERIC_DIR + "web/cmaps/**/*", { base: GENERIC_DIR + "web" })
|
|
.pipe(gulp.dest(DIST_DIR)),
|
|
gulp
|
|
.src(GENERIC_DIR + "web/standard_fonts/**/*", {
|
|
base: GENERIC_DIR + "web",
|
|
})
|
|
.pipe(gulp.dest(DIST_DIR)),
|
|
gulp
|
|
.src([
|
|
GENERIC_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.mjs",
|
|
GENERIC_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.mjs.map",
|
|
])
|
|
.pipe(gulp.dest(DIST_DIR + "build/")),
|
|
gulp
|
|
.src([
|
|
GENERIC_LEGACY_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.mjs",
|
|
GENERIC_LEGACY_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.mjs.map",
|
|
])
|
|
.pipe(gulp.dest(DIST_DIR + "legacy/build/")),
|
|
gulp
|
|
.src(MINIFIED_DIR + "build/pdf.mjs")
|
|
.pipe(rename("pdf.min.mjs"))
|
|
.pipe(gulp.dest(DIST_DIR + "build/")),
|
|
gulp
|
|
.src(MINIFIED_DIR + "build/pdf.worker.mjs")
|
|
.pipe(rename("pdf.worker.min.mjs"))
|
|
.pipe(gulp.dest(DIST_DIR + "build/")),
|
|
gulp
|
|
.src(MINIFIED_DIR + "build/pdf.sandbox.mjs")
|
|
.pipe(rename("pdf.sandbox.min.mjs"))
|
|
.pipe(gulp.dest(DIST_DIR + "build/")),
|
|
gulp
|
|
.src(MINIFIED_DIR + "image_decoders/pdf.image_decoders.mjs")
|
|
.pipe(rename("pdf.image_decoders.min.mjs"))
|
|
.pipe(gulp.dest(DIST_DIR + "image_decoders/")),
|
|
gulp
|
|
.src(MINIFIED_LEGACY_DIR + "build/pdf.mjs")
|
|
.pipe(rename("pdf.min.mjs"))
|
|
.pipe(gulp.dest(DIST_DIR + "legacy/build/")),
|
|
gulp
|
|
.src(MINIFIED_LEGACY_DIR + "build/pdf.worker.mjs")
|
|
.pipe(rename("pdf.worker.min.mjs"))
|
|
.pipe(gulp.dest(DIST_DIR + "legacy/build/")),
|
|
gulp
|
|
.src(MINIFIED_LEGACY_DIR + "build/pdf.sandbox.mjs")
|
|
.pipe(rename("pdf.sandbox.min.mjs"))
|
|
.pipe(gulp.dest(DIST_DIR + "legacy/build/")),
|
|
gulp
|
|
.src(MINIFIED_LEGACY_DIR + "image_decoders/pdf.image_decoders.mjs")
|
|
.pipe(rename("pdf.image_decoders.min.mjs"))
|
|
.pipe(gulp.dest(DIST_DIR + "legacy/image_decoders/")),
|
|
gulp
|
|
.src(COMPONENTS_DIR + "**/*", { base: COMPONENTS_DIR })
|
|
.pipe(gulp.dest(DIST_DIR + "web/")),
|
|
gulp
|
|
.src(COMPONENTS_LEGACY_DIR + "**/*", { base: COMPONENTS_LEGACY_DIR })
|
|
.pipe(gulp.dest(DIST_DIR + "legacy/web/")),
|
|
gulp
|
|
.src(IMAGE_DECODERS_DIR + "**/*", { base: IMAGE_DECODERS_DIR })
|
|
.pipe(gulp.dest(DIST_DIR + "image_decoders/")),
|
|
gulp
|
|
.src(IMAGE_DECODERS_LEGACY_DIR + "**/*", {
|
|
base: IMAGE_DECODERS_LEGACY_DIR,
|
|
})
|
|
.pipe(gulp.dest(DIST_DIR + "legacy/image_decoders/")),
|
|
gulp
|
|
.src(TYPES_DIR + "**/*", { base: TYPES_DIR })
|
|
.pipe(gulp.dest(DIST_DIR + "types/")),
|
|
]);
|
|
}
|
|
)
|
|
);
|
|
|
|
gulp.task(
|
|
"dist-install",
|
|
gulp.series("dist-pre", function createDistInstall(done) {
|
|
let distPath = DIST_DIR;
|
|
const opts = {};
|
|
const installPath = process.env.PDFJS_INSTALL_PATH;
|
|
if (installPath) {
|
|
opts.cwd = installPath;
|
|
distPath = path.relative(installPath, distPath);
|
|
}
|
|
safeSpawnSync("npm", ["install", distPath], opts);
|
|
done();
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"dist",
|
|
gulp.series("dist-pre", function createDist(done) {
|
|
const VERSION = getVersionJSON().version;
|
|
|
|
console.log();
|
|
console.log("### Committing changes");
|
|
|
|
let reason = process.env.PDFJS_UPDATE_REASON;
|
|
// Attempt to work-around the broken link, see https://github.com/mozilla/pdf.js/issues/10391
|
|
if (typeof reason === "string") {
|
|
const reasonParts =
|
|
/^(See )(mozilla\/pdf\.js)@tags\/(v\d+\.\d+\.\d+)\s*$/.exec(reason);
|
|
|
|
if (reasonParts) {
|
|
reason =
|
|
reasonParts[1] +
|
|
"https://github.com/" +
|
|
reasonParts[2] +
|
|
"/releases/tag/" +
|
|
reasonParts[3];
|
|
}
|
|
}
|
|
const message =
|
|
"PDF.js version " + VERSION + (reason ? " - " + reason : "");
|
|
safeSpawnSync("git", ["add", "*"], { cwd: DIST_DIR });
|
|
safeSpawnSync("git", ["commit", "-am", message], { cwd: DIST_DIR });
|
|
safeSpawnSync("git", ["tag", "-a", "v" + VERSION, "-m", message], {
|
|
cwd: DIST_DIR,
|
|
});
|
|
|
|
console.log();
|
|
console.log("Done. Push with");
|
|
console.log(
|
|
" cd " + DIST_DIR + "; git push --tags " + DIST_REPO_URL + " master"
|
|
);
|
|
console.log();
|
|
done();
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"mozcentralbaseline",
|
|
gulp.series(createBaseline, function createMozcentralBaseline(done) {
|
|
console.log();
|
|
console.log("### Creating mozcentral baseline environment");
|
|
|
|
// Create a mozcentral build.
|
|
rimraf.sync(BASELINE_DIR + BUILD_DIR);
|
|
|
|
const workingDirectory = path.resolve(process.cwd(), BASELINE_DIR);
|
|
safeSpawnSync("gulp", ["mozcentral"], {
|
|
env: process.env,
|
|
cwd: workingDirectory,
|
|
stdio: "inherit",
|
|
});
|
|
|
|
// Copy the mozcentral build to the mozcentral baseline directory.
|
|
rimraf.sync(MOZCENTRAL_BASELINE_DIR);
|
|
mkdirp.sync(MOZCENTRAL_BASELINE_DIR);
|
|
|
|
gulp
|
|
.src([BASELINE_DIR + BUILD_DIR + "mozcentral/**/*"])
|
|
.pipe(gulp.dest(MOZCENTRAL_BASELINE_DIR))
|
|
.on("end", function () {
|
|
// Commit the mozcentral baseline.
|
|
safeSpawnSync("git", ["init"], { cwd: MOZCENTRAL_BASELINE_DIR });
|
|
safeSpawnSync("git", ["add", "."], { cwd: MOZCENTRAL_BASELINE_DIR });
|
|
safeSpawnSync("git", ["commit", "-m", '"mozcentral baseline"'], {
|
|
cwd: MOZCENTRAL_BASELINE_DIR,
|
|
});
|
|
done();
|
|
});
|
|
})
|
|
);
|
|
|
|
gulp.task(
|
|
"mozcentraldiff",
|
|
gulp.series(
|
|
"mozcentral",
|
|
"mozcentralbaseline",
|
|
function createMozcentralDiff(done) {
|
|
console.log();
|
|
console.log("### Creating mozcentral diff");
|
|
|
|
// Create the diff between the current mozcentral build and the
|
|
// baseline mozcentral build, which both exist at this point.
|
|
// The mozcentral baseline directory is a Git repository, so we
|
|
// remove all files and copy the current mozcentral build files
|
|
// into it to create the diff.
|
|
rimraf.sync(MOZCENTRAL_BASELINE_DIR + "*");
|
|
|
|
gulp
|
|
.src([BUILD_DIR + "mozcentral/**/*"])
|
|
.pipe(gulp.dest(MOZCENTRAL_BASELINE_DIR))
|
|
.on("end", function () {
|
|
safeSpawnSync("git", ["add", "-A"], { cwd: MOZCENTRAL_BASELINE_DIR });
|
|
const diff = safeSpawnSync(
|
|
"git",
|
|
["diff", "--binary", "--cached", "--unified=8"],
|
|
{ cwd: MOZCENTRAL_BASELINE_DIR }
|
|
).stdout;
|
|
|
|
createStringSource(MOZCENTRAL_DIFF_FILE, diff)
|
|
.pipe(gulp.dest(BUILD_DIR))
|
|
.on("end", function () {
|
|
console.log(
|
|
"Result diff can be found at " +
|
|
BUILD_DIR +
|
|
MOZCENTRAL_DIFF_FILE
|
|
);
|
|
done();
|
|
});
|
|
});
|
|
}
|
|
)
|
|
);
|
|
|
|
gulp.task("externaltest", function (done) {
|
|
console.log();
|
|
console.log("### Running test-fixtures.js");
|
|
safeSpawnSync("node", ["external/builder/test-fixtures.mjs"], {
|
|
stdio: "inherit",
|
|
});
|
|
|
|
console.log();
|
|
console.log("### Running test-fixtures_esprima.js");
|
|
safeSpawnSync("node", ["external/builder/test-fixtures_esprima.mjs"], {
|
|
stdio: "inherit",
|
|
});
|
|
done();
|
|
});
|