From 5ccf70bfb95413896f207a4128af97edd3fb7627 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Tue, 29 Sep 2026 15:42:41 -0700 Subject: [PATCH 01/17] chore: pull build and test-compile-advanced task out of gulp --- nx.json | 5 +- package-lock.json | 1 + packages/blockly/gulpfile.mjs | 18 - packages/blockly/package.json | 22 +- .../{gulpfiles/build_tasks.mjs => build.mjs} | 359 ++++++++++++------ packages/blockly/scripts/build_constants.mjs | 7 + .../tests/compile/test_compile_advanced.mjs | 77 ++++ 7 files changed, 345 insertions(+), 144 deletions(-) rename packages/blockly/scripts/{gulpfiles/build_tasks.mjs => build.mjs} (71%) create mode 100644 packages/blockly/tests/compile/test_compile_advanced.mjs diff --git a/nx.json b/nx.json index ff1462653a7..e2ae55d9ae3 100644 --- a/nx.json +++ b/nx.json @@ -62,9 +62,6 @@ "outputs": ["{projectRoot}/dist"], "cache": true }, - "clean": { - "cache": true - }, "lint": { "cache": true }, @@ -79,9 +76,11 @@ "cache": true }, "test-mocha-node": { + "dependsOn": ["tsc"], "cache": true }, "test-generators": { + "dependsOn": ["package"], "cache": true }, "test-mocha-interactive": { diff --git a/package-lock.json b/package-lock.json index d93e95c1e79..c4e7f590fa7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35946,6 +35946,7 @@ "concurrently": "^10.0.4", "esbuild": "^0.28.2", "glob": "^13.0.6", + "glob-stream": "8.0.3", "google-closure-compiler": "^20260920.0.0", "gulp": "^5.0.1", "gulp-rename": "^2.1.0", diff --git a/packages/blockly/gulpfile.mjs b/packages/blockly/gulpfile.mjs index d21e4874c82..b6aaf9e5e7a 100644 --- a/packages/blockly/gulpfile.mjs +++ b/packages/blockly/gulpfile.mjs @@ -21,14 +21,6 @@ import { deployDemosBeta, prepareDemos, } from './scripts/gulpfiles/appengine_tasks.mjs'; -import { - build, - buildAdvancedCompilationTest, - minify, -} from './scripts/gulpfiles/build_tasks.mjs'; - -// Default target if gulp invoked without specifying. -export default build; // Main sequence targets. They already invoke prerequisites. Listed // in typical order of invocation, and strictly listing prerequisites @@ -36,17 +28,7 @@ export default build; // // prettier-ignore export { - minify, - build, prepareDemos, deployDemosBeta, deployDemos, } - -// Manually-invokable targets that also invoke prerequisites where -// required. -// -// prettier-ignore -export { - buildAdvancedCompilationTest, -} diff --git a/packages/blockly/package.json b/packages/blockly/package.json index 5a79792e44e..192a36e34ff 100644 --- a/packages/blockly/package.json +++ b/packages/blockly/package.json @@ -73,12 +73,6 @@ } ] }, - "docs": { - "dependsOn": [ - "package", - "^build" - ] - }, "start": { "dependsOn": [ { @@ -94,11 +88,11 @@ } }, "scripts": { - "build": "npm run tsc && gulp build && npm run langfiles", - "build-debug": "gulp build --verbose --debug", - "build-debug-log": "npm run build:debug > build-debug.log 2>&1 && tail -3 build-debug.log", - "build-strict": "gulp build --verbose --strict", - "build-strict-log": "npm run build:strict > build-debug.log 2>&1 && tail -3 build-debug.log", + "build": "npm run tsc && node \"scripts/build.mjs\" && npm run langfiles", + "build-debug": "npm run build --verbose --debug", + "build-debug-log": "npm run build-debug > build-debug.log 2>&1 && tail -3 build-debug.log", + "build-strict": "npm run build --verbose --strict", + "build-strict-log": "npm run build-strict > build-debug.log 2>&1 && tail -3 build-debug.log", "clean": "node \"scripts/clean.mjs\"", "deployDemos": "npm ci && gulp deployDemos", "deployDemos:beta": "npm ci && gulp deployDemosBeta", @@ -106,7 +100,6 @@ "lint": "eslint .", "lint-fix": "eslint . --fix", "langfiles": "node \"scripts/langfiles.mjs\"", - "minify": "gulp minify", "package": "node scripts/package.mjs", "prepareDemos": "gulp prepareDemos", "start": "npm run package && nx run @blockly/dev-tools:build && node \"scripts/prepare_advanced_playground.mjs\" && concurrently -n devtools-watch,blockly-watch,server \"nx watch --projects=@blockly/dev-tools --includeDependencies -- nx run rebuildAdvancedPlayground\" \"nx watch --projects=blockly -- nx run rebuildBlockly\" \"http-server ./ -s -o /tests/playground.html -c-1\"", @@ -119,10 +112,10 @@ "test-mocha-typecheck": "tsc -p tests/mocha/tsconfig.json", "test-generators": "node \"tests/generators/test_generators.mjs\"", "test-mocha-interactive": "npm run build && node scripts/prepare_mocha_bundle.mjs && concurrently -n tsc,tsc:tests,esbuild,python3 \"tsc --watch --preserveWatchOutput --outDir \"build/src\" --declarationDir \"build/declarations\"\" \"npm run test-mocha-typecheck -- --watch --preserveWatchOutput\" \"esbuild build/tests/bundle-entry.js --bundle --sourcemap --alias:blockly/core=./build/src/core/blockly.js --alias:blockly/blocks=./build/src/blocks/blocks.js --alias:blockly=./build/src/core/blockly.js --outfile=build/tests/mocha-bundle.js --servedir=. --serve=127.0.0.1:8080 --watch=forever --log-level=warning\" \"python3 -m webbrowser 'http://localhost:8080/tests/mocha/index.html'\"", - "test-compile-advanced": "npm run tsc && gulp buildAdvancedCompilationTest --debug", + "test-compile-advanced": "npm run tsc && node \"tests/compile/test_compile_advanced.mjs\" --debug", "test-compile-advanced-browser": "node \"tests/compile/webdriver.js\"", "test-renamings": "node \"tests/migration/validate-renamings.mjs\"", - "test-type-definitions": "tsc -p ./tests/typescript/tsconfig.json -outDir \"build/src\"", + "test-type-definitions": "tsc -p ./tests/typescript/tsconfig.json -outDir \"build/test-types\"", "updateGithubPages": "node scripts/update_github_pages.mjs --upstream", "updateGithubPages-staging": "node scripts/update_github_pages.mjs --use-local" }, @@ -189,6 +182,7 @@ "concurrently": "^10.0.4", "esbuild": "^0.28.2", "glob": "^13.0.6", + "glob-stream": "8.0.3", "google-closure-compiler": "^20260920.0.0", "gulp": "^5.0.1", "gulp-rename": "^2.1.0", diff --git a/packages/blockly/scripts/gulpfiles/build_tasks.mjs b/packages/blockly/scripts/build.mjs similarity index 71% rename from packages/blockly/scripts/gulpfiles/build_tasks.mjs rename to packages/blockly/scripts/build.mjs index 5bcaf4b2a0f..ac19e5a5abe 100644 --- a/packages/blockly/scripts/gulpfiles/build_tasks.mjs +++ b/packages/blockly/scripts/build.mjs @@ -1,41 +1,39 @@ -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview Gulp script to build Blockly for Node & NPM. - */ - -import * as gulp from 'gulp'; -import rename from 'gulp-rename'; -import replace from 'gulp-replace'; -import sourcemaps from 'gulp-sourcemaps'; - -import * as fs from 'fs'; -import * as fsPromises from 'fs/promises'; -import * as path from 'path'; +import {execSync} from 'child_process'; +import {mkdirSync, readdirSync} from 'node:fs'; +import { + appendFile, + mkdir, + readFile, + rename, + rm, + unlink, + writeFile, +} from 'node:fs/promises'; import {globSync} from 'glob'; -import {gulp as closureCompiler} from 'google-closure-compiler'; +import {compiler as ClosureCompiler} from 'google-closure-compiler'; +import {getNativeImagePath} from 'google-closure-compiler/lib/utils.js'; +import * as path from 'path'; import yargs from 'yargs'; import {hideBin} from 'yargs/helpers'; import { BUILD_DIR, + LANG_BUILD_DIR, RELEASE_DIR, TSC_OUTPUT_DIR, -} from './config.mjs'; - -import {posixPath, quote} from '../helpers.js'; -import {getPackageJson} from '../lib/package_json.mjs'; +} from './build_constants.mjs'; +import {posixPath, quote} from './helpers.js'; +import {getPackageJson} from './lib/package_json.mjs'; const argv = yargs(hideBin(process.argv)).parse(); -//////////////////////////////////////////////////////////// -// Build // -//////////////////////////////////////////////////////////// +/** + * Path to the python runtime. + * This will normalize the command across platforms (e.g. python3 on Linux and + * Mac, python on Windows). + */ +const PYTHON = process.platform === 'win32' ? 'python' : 'python3'; /** * Posix version of TSC_OUTPUT_DIR @@ -182,9 +180,10 @@ for (let i = 1; i < chunks.length; i++) { * with an arbitrary, very short name. * * Nevertheless, this function can still be used to compute the - * location of @defined variables, because --define directives are + * location of `@define`d variables, because --define directives are * processed before the final renaming occurs. - */ + * @param {{entry: string}} chunk The chunk whose module path to compute. + */ function modulePath(chunk) { const entryPath = path.posix.join(TSC_OUTPUT_DIR_POSIX, chunk.entry); return 'module$' + entryPath.replace(/\.js$/, '').replaceAll('/', '$'); @@ -199,14 +198,26 @@ const licenseRegex = `\\/\\*\\* /** * Helper method for stripping the Google's and MIT's Apache Licenses. + * + * @param {!Array} filePaths Files to rewrite. */ -function stripApacheLicense() { - // Strip out Google's and MIT's Apache licences. - // Closure Compiler preserves dozens of Apache licences in the Blockly code. - // Remove these if they belong to Google or MIT. - // MIT's permission to do this is logged in Blockly issue #2412. - return replace(new RegExp(licenseRegex, 'g'), '\n\n\n\n'); - // Replace with the same number of lines so that source-maps are not affected. +async function stripApacheLicenses(filePaths) { + await Promise.all( + filePaths.map(async (filePath) => { + const source = await readFile(filePath, 'utf8'); + // Strip out Google's and MIT's Apache licences. + // Closure Compiler preserves dozens of Apache licences in the Blockly code. + // Remove these if they belong to Google or MIT. + // MIT's permission to do this is logged in Blockly issue #2412. + const stripped = source.replace( + new RegExp(licenseRegex, 'g'), + '\n\n\n\n', + ); + // Replace with the same number of lines so that source-maps are + // not affected. + if (stripped !== source) await writeFile(filePath, stripped); + }), + ); } /** @@ -321,13 +332,54 @@ const JSCOMP_OFF = [ 'visibility', ]; +var languages = null; + +/** + * Get list of languages to build langfiles and/or shims for, based on .json + * files in msg/json/, skipping certain entries that do not correspond to an + * actual language). Results are cached as this is called from both + * buildLangfiles and buildLangfileShims. + */ +function getLanguages() { + if (!languages) { + const skip = /^(keys|synonyms|qqq|constants)\.json$/; + languages = readdirSync(path.join('msg', 'json')) + .filter((file) => file.endsWith('json') && !skip.test(file)) + .map((file) => file.replace(/\.json$/, '')); + } + return languages; +} + +/** + * This task builds Blockly's lang files. + * msg/*.js + */ +function buildLangfiles() { + // Create output directory. + mkdirSync(LANG_BUILD_DIR, {recursive: true}); + + // Run create_messages.py. + const inputFiles = getLanguages().map((lang) => + path.join('msg', 'json', `${lang}.json`), + ); + + const createMessagesCmd = `${PYTHON} ./scripts/i18n/create_messages.py \ + --source_lang_file ${path.join('msg', 'json', 'en.json')} \ + --source_synonym_file ${path.join('msg', 'json', 'synonyms.json')} \ + --source_constants_file ${path.join('msg', 'json', 'constants.json')} \ + --key_file ${path.join('msg', 'json', 'keys.json')} \ + --output_dir ${LANG_BUILD_DIR} \ + --quiet ${inputFiles.join(' ')}`; + execSync(createMessagesCmd, {stdio: 'inherit'}); +} + /** * Return the path to the generated chunk exporter for the given * chunk, relative to TSC_OUTPUT_DIR. * * See buildChunkExporters for additional information. * - * @param {{name: string}} chunk + * @param {{name: string}} chunk The chunk whose exporter path to compute. * @return {string} */ function chunkExporterPath(chunk) { @@ -393,7 +445,7 @@ function chunkExporterPath(chunk) { */ async function buildChunkExporters() { const outDir = path.join(TSC_OUTPUT_DIR, CHUNK_EXPORTERS_DIR); - await fsPromises.mkdir(outDir, {recursive: true}); + await mkdir(outDir, {recursive: true}); await Promise.all( chunks.map(async (chunk) => { @@ -401,7 +453,7 @@ async function buildChunkExporters() { const importPath = posixPath( path.posix.relative(path.posix.dirname(filename), chunk.entry), ); - await fsPromises.writeFile( + await writeFile( path.join(TSC_OUTPUT_DIR, filename), // Suppress undefined-variable diagnostics, since Closure // Compiler can't see the declaration of NAMESPACE_VARIABLE @@ -420,6 +472,9 @@ ${NAMESPACE_VARIABLE}['${CHUNK_EXPORTS_PREFIX}${chunk.name}'] = exports; * A helper method to return an Closure Compiler chunk wrapper that * wraps the compiler output for the given chunk in a Universal Module * Definition. + * @param {{name: string, parent: ?object, scriptExport: string, + * scriptNamedExports: ({[key: string]: string}|undefined)}} chunk + * The chunk to generate a UMD wrapper for. */ function chunkWrapper(chunk) { // Each chunk can have only a single dependency, which is its parent @@ -551,12 +606,9 @@ function getChunkOptions() { } /** - * Helper method for calling the Closure Compiler, establishing - * default options (that can be overridden by the caller). - * @param {*} options Caller-supplied options that will override the - * defaultOptions. + * Helper method for fetching the default closure compiler options. */ -function compile(options) { +function getDefaultCompileOptions() { const defaultOptions = { compilation_level: 'SIMPLE_OPTIMIZATIONS', warning_level: argv.verbose ? 'VERBOSE' : 'DEFAULT', @@ -574,17 +626,62 @@ function compile(options) { defaultOptions.jscomp_error.push('strictCheckTypes'); } } - // Extra options for Closure Compiler gulp plugin. - const platform = ['native']; + return defaultOptions; +} - return closureCompiler({...defaultOptions, ...options}, {platform}); +/** + * Helper method for calling the Closure Compiler, establishing + * default options (that can be overridden by the caller). + * + * The compiler reads its inputs from and writes its outputs to disk, + * so the caller is expected to supply --js and either --js_output_file + * or --chunk_output_path_prefix. + * + * @param {{[flag: string]: string|boolean|!Array}} options + * Caller-supplied options that will override the defaultOptions. + * @return {!Promise} Resolves with the compiler's stdout once + * it exits successfully; rejects if it exits non-zero. + */ +export function compile(options) { + const compilerInstance = new ClosureCompiler({ + ...getDefaultCompileOptions(), + ...options, + }); + + // google-closure-compiler's node API always shells out to `java` + // unless JAR_PATH is cleared; only its gulp and grunt plugins know + // about the platform-native binaries. Do what those plugins do, so + // that a JRE isn't needed on platforms we ship a binary for. See + // getFirstSupportedPlatform() in google-closure-compiler/lib/utils.js. + const nativeImagePath = getNativeImagePath(); + if (nativeImagePath) { + compilerInstance.JAR_PATH = null; + compilerInstance.javaPath = nativeImagePath; + } else if (!process.env.JAVA_HOME) { + throw new Error( + 'No native Closure Compiler binary for this platform and no JRE ' + + 'found; set JAVA_HOME or install google-closure-compiler-.', + ); + } + + return new Promise((resolve, reject) => { + compilerInstance.run((exitCode, stdout, stderr) => { + // Diagnostics go to stderr even when compilation succeeds. + if (stderr) process.stderr.write(stderr); + if (exitCode === 0) { + resolve(stdout); + } else { + reject(new Error(`Closure Compiler exited with code ${exitCode}.`)); + } + }); + }); } /** * This task compiles the core library, blocks and generators, creating * blockly_compressed.js, blocks_compressed.js, etc. */ -function buildCompiled() { +async function buildCompiled() { // Get chunking. const chunkOptions = getChunkOptions(); // Closure Compiler options. @@ -600,23 +697,68 @@ function buildCompiled() { // by the time --define is processed.) See // https://github.com/google/closure-compiler/issues/1601#issuecomment-483452226 define: `VERSION$$${modulePath(chunks[0])}='${packageJson.version}'`, + js: chunkOptions.js, chunk: chunkOptions.chunk, chunk_wrapper: chunkOptions.chunk_wrapper, - // Don't supply the list of source files in chunkOptions.js as an - // option to Closure Compiler; instead feed them as input via gulp.src. + // Closure writes one file per chunk, named after the chunk, so + // this produces e.g. dist/blockly.js; the COMPILED_SUFFIX is added + // below. + chunk_output_path_prefix: `${RELEASE_DIR}/`, + create_source_map: '%outname%.map', rename_prefix_namespace: NAMESPACE_VARIABLE, assume_function_wrapper: true, }; - // Fire up compilation pipline. - return gulp - .src(chunkOptions.js, {base: './'}) - .pipe(stripApacheLicense()) - .pipe(sourcemaps.init()) - .pipe(compile(options)) - .pipe(rename({suffix: COMPILED_SUFFIX})) - .pipe(sourcemaps.write('.')) - .pipe(gulp.dest(RELEASE_DIR)); + await stripApacheLicenses(chunkOptions.js); + // Ensure the destination directory exists + await mkdir(RELEASE_DIR, {recursive: true}); + await compile(options); + for (const chunk of chunks) { + const compiledPath = path.join(RELEASE_DIR, `${chunk.name}.js`); + const outputName = `${chunk.name}${COMPILED_SUFFIX}.js`; + const outputPath = path.join(RELEASE_DIR, outputName); + + // Closure's --chunk_output_path_prefix write path appends one extra + // trailing blank line beyond what's in chunk_wrapper; the JSON-streams + // protocol gulp used didn't do this. Trim it here so the .js and its + // .map stay consistent with each other and match the old gulp output. + let code = await readFile(compiledPath, 'utf8'); + if (code.endsWith('\n\n')) { + code = code.slice(0, -1); + await writeFile(compiledPath, code); + } + + const sourceMap = JSON.parse(await readFile(`${compiledPath}.map`, 'utf8')); + sourceMap.file = outputName; + if (sourceMap.mappings.endsWith(';')) { + sourceMap.mappings = sourceMap.mappings.slice(0, -1); + } + if (typeof sourceMap.lineCount === 'number') { + delete sourceMap.lineCount; + } + sourceMap.sourcesContent = await Promise.all( + sourceMap.sources.map((source) => + readFile(source, 'utf8').catch(() => null), + ), + ); + + const ordered = { + version: sourceMap.version, + sources: sourceMap.sources, + names: sourceMap.names, + mappings: sourceMap.mappings, + file: sourceMap.file, + sourcesContent: sourceMap.sourcesContent, + }; + await writeFile(`${outputPath}.map`, JSON.stringify(ordered)); + await unlink(`${compiledPath}.map`); + + await rename(compiledPath, outputPath); + await appendFile( + outputPath, + `\n\n//# sourceMappingURL=${outputName}.map\n`, + ); + } } /** @@ -635,13 +777,13 @@ async function buildShims() { // .js files therein are ESM not CJS, so we can import the // entrypoints to enumerate their exported names. const TMP_PACKAGE_JSON = path.join(BUILD_DIR, 'package.json'); - await fsPromises.writeFile(TMP_PACKAGE_JSON, '{"type": "module"}'); + await writeFile(TMP_PACKAGE_JSON, '{"type": "module"}'); await Promise.all( chunks.map(async (chunk) => { // Import chunk entrypoint to get names of exports for chunk. const entryPath = path.posix.join(TSC_OUTPUT_DIR_POSIX, chunk.entry); - const exportedNames = Object.keys(await import(`../../${entryPath}`)); + const exportedNames = Object.keys(await import(`../${entryPath}`)); // Write an ESM wrapper that imports the CJS module and re-exports // its named exports. @@ -649,7 +791,7 @@ async function buildShims() { const wrapperPath = path.join(RELEASE_DIR, `${chunk.name}.mjs`); const importName = chunk.scriptExport.replace(/.*\./, ''); - await fsPromises.writeFile( + await writeFile( wrapperPath, `import ${importName} from '${cjsPath}'; export const { @@ -661,7 +803,7 @@ ${exportedNames.map((name) => ` ${name},`).join('\n')} // For first chunk, write an additional ESM wrapper for 'blockly' // entrypoint since it has the same exports as 'blockly/core'. if (chunk.name === 'blockly') { - await fsPromises.writeFile( + await writeFile( path.join(RELEASE_DIR, `index.mjs`), `import Blockly from './index.js'; export const { @@ -683,7 +825,7 @@ ${exportedNames.map((name) => ` ${name},`).join('\n')} ? `import ${quote(`./${chunk.parent.name}.loader.mjs`)};` : ''; - await fsPromises.writeFile( + await writeFile( shimPath, `import {loadChunk} from '../tests/scripts/load.mjs'; ${parentImport} @@ -700,58 +842,57 @@ ${exportedNames.map((name) => ` ${name},`).join('\n')} }), ); - await fsPromises.rm(TMP_PACKAGE_JSON); + await rm(TMP_PACKAGE_JSON); } /** - * This task uses Closure Compiler's ADVANCED_OPTIMIZATIONS mode to - * compile together Blockly core, blocks and generators with a simple - * test app; the purpose is to verify that Blockly is compatible with - * the ADVANCED_OPTIMIZATIONS mode. - * - * Prerequisite: tsc. + * This task builds the ESM wrappers used by the langfiles "import" + * entrypoints declared in package.json. */ -function compileAdvancedCompilationTest() { - // If main_compressed.js exists (from a previous run) delete it so that - // a later browser-based test won't check it should the compile fail. - try { - fs.unlinkSync('./tests/compile/main_compressed.js'); - } catch { - // Probably it didn't exist. - } +async function buildLangfileShims() { + // Create output directory. + mkdirSync(path.join(RELEASE_DIR, 'msg'), {recursive: true}); + + // Get the names of the exports from the langfile by importing + // msg/messages.js and letting it mutate the (global) Blockly.Msg. + // (We have to do it this way because messages.js is a script and + // not a CJS module with exports.) + globalThis.Blockly = {Msg: {}}; + await import('../msg/messages.js'); + const exportedNames = Object.keys(globalThis.Blockly.Msg); + delete globalThis.Blockly; - const srcs = [ - TSC_OUTPUT_DIR + '/**/*.js', - 'tests/compile/main.js', - 'tests/compile/test_blocks.js', - ]; + await Promise.all( + getLanguages().map(async (lang) => { + // Write an ESM wrapper that imports the CJS module and re-exports + // its named exports. + const cjsPath = `./${lang}.js`; + const wrapperPath = path.join(RELEASE_DIR, 'msg', `${lang}.mjs`); + const safeLang = lang.replace(/-/g, '_'); - // Closure Compiler options. - const options = { - dependency_mode: 'PRUNE', - compilation_level: 'ADVANCED_OPTIMIZATIONS', - entry_point: './tests/compile/main.js', - js_output_file: 'main_compressed.js', - }; - return gulp - .src(srcs, {base: './'}) - .pipe(stripApacheLicense()) - .pipe(sourcemaps.init()) - .pipe(compile(options)) - .pipe(sourcemaps.write('.', {includeContent: false, sourceRoot: '../../'})) - .pipe(gulp.dest('./tests/compile/')); + await writeFile( + wrapperPath, + `import ${safeLang} from '${cjsPath}'; +export const { +${exportedNames.map((name) => ` ${name},`).join('\n')} +} = ${safeLang}; +`, + ); + }), + ); } -// function tsc, above -export const minify = gulp.series( - buildChunkExporters, - buildCompiled, - buildShims, -); -export const build = minify; +/** + * Build all chunks, compressed files, sourcemaps, shims, and langfiles. + * + * Prerequisite: tsc (handled by nx). + */ +async function build() { + await buildChunkExporters(); + await buildCompiled(); + await buildShims(); + buildLangfiles(); + await buildLangfileShims(); +} -// Manually-invokable targets, with prerequisites where required. -// function messages, above -export const buildAdvancedCompilationTest = gulp.series( - compileAdvancedCompilationTest, -); +build(); diff --git a/packages/blockly/scripts/build_constants.mjs b/packages/blockly/scripts/build_constants.mjs index 1b2ae1339f0..c73123d44ca 100644 --- a/packages/blockly/scripts/build_constants.mjs +++ b/packages/blockly/scripts/build_constants.mjs @@ -1,5 +1,7 @@ import * as path from 'path'; +import {posixPath} from './helpers.js'; + // Paths are all relative to the repository root. Do not include // trailing slash. // @@ -25,6 +27,11 @@ export const TSC_OUTPUT_DIR = path.join(BUILD_DIR, 'src'); // Directory for files generated by compiling test code. export const TEST_TSC_OUTPUT_DIR = path.join(BUILD_DIR, 'tests'); +/** + * Posix version of TSC_OUTPUT_DIR + */ +export const TSC_OUTPUT_DIR_POSIX = posixPath(TSC_OUTPUT_DIR); + // Directory in which to assemble (and from which to publish) the // blockly npm package. export const RELEASE_DIR = 'dist'; diff --git a/packages/blockly/tests/compile/test_compile_advanced.mjs b/packages/blockly/tests/compile/test_compile_advanced.mjs new file mode 100644 index 00000000000..0df0a961ca7 --- /dev/null +++ b/packages/blockly/tests/compile/test_compile_advanced.mjs @@ -0,0 +1,77 @@ +import { + appendFile, + readFile, + unlink, + writeFile, +} from 'node:fs/promises'; + +import globStream from 'glob-stream'; +import * as path from 'path'; + +import {posixPath} from '../../scripts/helpers.js'; +import {compile} from '../../scripts/build.mjs'; +import {TSC_OUTPUT_DIR_POSIX} from '../../scripts/build_constants.mjs'; + +/** + * Resolve glob patterns to file paths using glob-stream + */ +function globStreamToArray(patterns) { + return new Promise((resolve, reject) => { + const paths = []; + globStream(patterns) + .on('data', (file) => paths.push(posixPath(path.relative(process.cwd(), file.path)))) + .on('error', reject) + .on('end', () => resolve(paths)); + }); +} +/** + * This task uses Closure Compiler's ADVANCED_OPTIMIZATIONS mode to + * compile together Blockly core, blocks and generators with a simple + * test app; the purpose is to verify that Blockly is compatible with + * the ADVANCED_OPTIMIZATIONS mode. + * + * Prerequisite: tsc. + */ +async function compileAdvancedCompilationTest() { + const outputPath = path.join('tests', 'compile', 'main_compressed.js'); + const mapPath = `${outputPath}.map`; + + // If main_compressed.js exists (from a previous run) delete it so that + // a later browser-based test won't check it should the compile fail. + try { + await unlink(outputPath); + } catch { + // Probably it didn't exist. + } + + const mainPath = 'tests/compile/main.js'; + const srcs = [ + ...(await globStreamToArray(TSC_OUTPUT_DIR_POSIX + '/**/*.js')), + mainPath, + 'tests/compile/test_blocks.js', + ]; + + const options = { + js: srcs, + dependency_mode: 'PRUNE', + compilation_level: 'ADVANCED_OPTIMIZATIONS', + entry_point: './' + mainPath, + js_output_file: outputPath, + create_source_map: mapPath, + }; + + await compile(options); + + const sourceMap = JSON.parse(await readFile(mapPath, 'utf8')); + sourceMap.file = path.basename(outputPath); + sourceMap.sourceRoot = '../../'; + delete sourceMap.sourcesContent; + + await writeFile(mapPath, JSON.stringify(sourceMap)); + await appendFile( + outputPath, + `\n//# sourceMappingURL=${path.basename(mapPath)}\n`, + ); +} + +compileAdvancedCompilationTest(); \ No newline at end of file From 16c8e48504ac63f462c764c67dfa3c819ab429ac Mon Sep 17 00:00:00 2001 From: lizschwab Date: Wed, 30 Sep 2026 09:57:31 -0700 Subject: [PATCH 02/17] chore: resolve build shim file differences --- packages/blockly/scripts/build.mjs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/blockly/scripts/build.mjs b/packages/blockly/scripts/build.mjs index ac19e5a5abe..06816d7b3c5 100644 --- a/packages/blockly/scripts/build.mjs +++ b/packages/blockly/scripts/build.mjs @@ -682,6 +682,11 @@ export function compile(options) { * blockly_compressed.js, blocks_compressed.js, etc. */ async function buildCompiled() { + // Closure writes each chunk's bare output here rather than straight into + // RELEASE_DIR, so it can't overwrite files already in dist/ (package.mjs + // writes legacy shims named blocks.js, dart.js, etc., which collide with + // Closure's bare chunk output). + const compiledDir = path.join(BUILD_DIR, 'compiled'); // Get chunking. const chunkOptions = getChunkOptions(); // Closure Compiler options. @@ -701,20 +706,22 @@ async function buildCompiled() { chunk: chunkOptions.chunk, chunk_wrapper: chunkOptions.chunk_wrapper, // Closure writes one file per chunk, named after the chunk, so - // this produces e.g. dist/blockly.js; the COMPILED_SUFFIX is added - // below. - chunk_output_path_prefix: `${RELEASE_DIR}/`, + // this produces e.g. build/compiled/blockly.js; the file is then + // renamed with COMPILED_SUFFIX and moved into RELEASE_DIR below. + chunk_output_path_prefix: `${compiledDir}/`, create_source_map: '%outname%.map', rename_prefix_namespace: NAMESPACE_VARIABLE, assume_function_wrapper: true, }; await stripApacheLicenses(chunkOptions.js); - // Ensure the destination directory exists + // Start from an empty scratch directory and ensure the destination exists. + await rm(compiledDir, {recursive: true, force: true}); + await mkdir(compiledDir, {recursive: true}); await mkdir(RELEASE_DIR, {recursive: true}); await compile(options); for (const chunk of chunks) { - const compiledPath = path.join(RELEASE_DIR, `${chunk.name}.js`); + const compiledPath = path.join(compiledDir, `${chunk.name}.js`); const outputName = `${chunk.name}${COMPILED_SUFFIX}.js`; const outputPath = path.join(RELEASE_DIR, outputName); From 8992464bba6ad83c2634898846da9af582874e05 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Tue, 13 Oct 2026 12:36:08 -0700 Subject: [PATCH 03/17] chore: small code quality fixes --- nx.json | 4 -- packages/blockly/scripts/build.mjs | 42 +++++++++---------- .../tests/compile/test_compile_advanced.mjs | 12 +++++- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/nx.json b/nx.json index e2ae55d9ae3..a10ff1df3b3 100644 --- a/nx.json +++ b/nx.json @@ -38,10 +38,6 @@ "outputs": ["{projectRoot}/build/msg"], "cache": true }, - "minify": { - "outputs": ["{projectRoot}/dist"], - "cache": true - }, "package": { "outputs": ["{projectRoot}/dist"], "cache": true diff --git a/packages/blockly/scripts/build.mjs b/packages/blockly/scripts/build.mjs index 06816d7b3c5..5c514a136d4 100644 --- a/packages/blockly/scripts/build.mjs +++ b/packages/blockly/scripts/build.mjs @@ -1,3 +1,13 @@ +/** + * @license + * Copyright 2026 Raspberry Pi Foundation + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Script to build the blockly package. + */ + import {execSync} from 'child_process'; import {mkdirSync, readdirSync} from 'node:fs'; import { @@ -20,26 +30,16 @@ import {hideBin} from 'yargs/helpers'; import { BUILD_DIR, LANG_BUILD_DIR, + PYTHON, RELEASE_DIR, TSC_OUTPUT_DIR, + TSC_OUTPUT_DIR_POSIX, } from './build_constants.mjs'; import {posixPath, quote} from './helpers.js'; import {getPackageJson} from './lib/package_json.mjs'; const argv = yargs(hideBin(process.argv)).parse(); -/** - * Path to the python runtime. - * This will normalize the command across platforms (e.g. python3 on Linux and - * Mac, python on Windows). - */ -const PYTHON = process.platform === 'win32' ? 'python' : 'python3'; - -/** - * Posix version of TSC_OUTPUT_DIR - */ -const TSC_OUTPUT_DIR_POSIX = posixPath(TSC_OUTPUT_DIR); - /** * Suffix to add to compiled output files. */ @@ -205,9 +205,10 @@ async function stripApacheLicenses(filePaths) { await Promise.all( filePaths.map(async (filePath) => { const source = await readFile(filePath, 'utf8'); - // Strip out Google's and MIT's Apache licences. - // Closure Compiler preserves dozens of Apache licences in the Blockly code. - // Remove these if they belong to Google or MIT. + // Strip out Google's, MIT's, and Raspberry Pi Foundation's Apache + // licences. Closure Compiler preserves dozens of Apache licences in the + // Blockly code. Remove these if they belong to Google, MIT, or Raspberry + // Pi Foundation. // MIT's permission to do this is logged in Blockly issue #2412. const stripped = source.replace( new RegExp(licenseRegex, 'g'), @@ -332,7 +333,7 @@ const JSCOMP_OFF = [ 'visibility', ]; -var languages = null; +let languages = null; /** * Get list of languages to build langfiles and/or shims for, based on .json @@ -508,7 +509,7 @@ function chunkWrapper(chunk) { const scriptExportStatements = [ `root.${chunk.scriptExport} = factory(${scriptDepsExpr});`, ]; - for (var location in chunk.scriptNamedExports) { + for (let location in chunk.scriptNamedExports) { const namedExport = chunk.scriptNamedExports[location]; scriptExportStatements.push( `root.${location} = root.${chunk.scriptExport}.${namedExport};`, @@ -726,9 +727,8 @@ async function buildCompiled() { const outputPath = path.join(RELEASE_DIR, outputName); // Closure's --chunk_output_path_prefix write path appends one extra - // trailing blank line beyond what's in chunk_wrapper; the JSON-streams - // protocol gulp used didn't do this. Trim it here so the .js and its - // .map stay consistent with each other and match the old gulp output. + // trailing blank line beyond what's in chunk_wrapper. Trim it here so the + // .js and its .map stay consistent with each other. let code = await readFile(compiledPath, 'utf8'); if (code.endsWith('\n\n')) { code = code.slice(0, -1); @@ -902,4 +902,4 @@ async function build() { await buildLangfileShims(); } -build(); +await build(); diff --git a/packages/blockly/tests/compile/test_compile_advanced.mjs b/packages/blockly/tests/compile/test_compile_advanced.mjs index 0df0a961ca7..c4d1226f1c7 100644 --- a/packages/blockly/tests/compile/test_compile_advanced.mjs +++ b/packages/blockly/tests/compile/test_compile_advanced.mjs @@ -1,3 +1,13 @@ +/** + * @license + * Copyright 2026 Raspberry Pi Foundation + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Test compile for closure compiler's advanced optimizations. + */ + import { appendFile, readFile, @@ -74,4 +84,4 @@ async function compileAdvancedCompilationTest() { ); } -compileAdvancedCompilationTest(); \ No newline at end of file +await compileAdvancedCompilationTest(); From 6224651559cdebbc3a3bc63632f20184b9df9e61 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Tue, 13 Oct 2026 13:58:07 -0700 Subject: [PATCH 04/17] chore: remove langfile redundancies --- packages/blockly/package.json | 6 +-- packages/blockly/scripts/build.mjs | 84 ++---------------------------- 2 files changed, 6 insertions(+), 84 deletions(-) diff --git a/packages/blockly/package.json b/packages/blockly/package.json index 192a36e34ff..0a75c4d79a0 100644 --- a/packages/blockly/package.json +++ b/packages/blockly/package.json @@ -88,10 +88,10 @@ } }, "scripts": { - "build": "npm run tsc && node \"scripts/build.mjs\" && npm run langfiles", - "build-debug": "npm run build --verbose --debug", + "build": "npm run tsc && node \"scripts/build.mjs\"", + "build-debug": "npm run build -- --verbose --debug", "build-debug-log": "npm run build-debug > build-debug.log 2>&1 && tail -3 build-debug.log", - "build-strict": "npm run build --verbose --strict", + "build-strict": "npm run build -- --verbose --strict", "build-strict-log": "npm run build-strict > build-debug.log 2>&1 && tail -3 build-debug.log", "clean": "node \"scripts/clean.mjs\"", "deployDemos": "npm ci && gulp deployDemos", diff --git a/packages/blockly/scripts/build.mjs b/packages/blockly/scripts/build.mjs index 5c514a136d4..e314e8c3468 100644 --- a/packages/blockly/scripts/build.mjs +++ b/packages/blockly/scripts/build.mjs @@ -9,7 +9,6 @@ */ import {execSync} from 'child_process'; -import {mkdirSync, readdirSync} from 'node:fs'; import { appendFile, mkdir, @@ -29,8 +28,6 @@ import {hideBin} from 'yargs/helpers'; import { BUILD_DIR, - LANG_BUILD_DIR, - PYTHON, RELEASE_DIR, TSC_OUTPUT_DIR, TSC_OUTPUT_DIR_POSIX, @@ -333,47 +330,6 @@ const JSCOMP_OFF = [ 'visibility', ]; -let languages = null; - -/** - * Get list of languages to build langfiles and/or shims for, based on .json - * files in msg/json/, skipping certain entries that do not correspond to an - * actual language). Results are cached as this is called from both - * buildLangfiles and buildLangfileShims. - */ -function getLanguages() { - if (!languages) { - const skip = /^(keys|synonyms|qqq|constants)\.json$/; - languages = readdirSync(path.join('msg', 'json')) - .filter((file) => file.endsWith('json') && !skip.test(file)) - .map((file) => file.replace(/\.json$/, '')); - } - return languages; -} - -/** - * This task builds Blockly's lang files. - * msg/*.js - */ -function buildLangfiles() { - // Create output directory. - mkdirSync(LANG_BUILD_DIR, {recursive: true}); - - // Run create_messages.py. - const inputFiles = getLanguages().map((lang) => - path.join('msg', 'json', `${lang}.json`), - ); - - const createMessagesCmd = `${PYTHON} ./scripts/i18n/create_messages.py \ - --source_lang_file ${path.join('msg', 'json', 'en.json')} \ - --source_synonym_file ${path.join('msg', 'json', 'synonyms.json')} \ - --source_constants_file ${path.join('msg', 'json', 'constants.json')} \ - --key_file ${path.join('msg', 'json', 'keys.json')} \ - --output_dir ${LANG_BUILD_DIR} \ - --quiet ${inputFiles.join(' ')}`; - execSync(createMessagesCmd, {stdio: 'inherit'}); -} - /** * Return the path to the generated chunk exporter for the given * chunk, relative to TSC_OUTPUT_DIR. @@ -852,41 +808,8 @@ ${exportedNames.map((name) => ` ${name},`).join('\n')} await rm(TMP_PACKAGE_JSON); } -/** - * This task builds the ESM wrappers used by the langfiles "import" - * entrypoints declared in package.json. - */ -async function buildLangfileShims() { - // Create output directory. - mkdirSync(path.join(RELEASE_DIR, 'msg'), {recursive: true}); - - // Get the names of the exports from the langfile by importing - // msg/messages.js and letting it mutate the (global) Blockly.Msg. - // (We have to do it this way because messages.js is a script and - // not a CJS module with exports.) - globalThis.Blockly = {Msg: {}}; - await import('../msg/messages.js'); - const exportedNames = Object.keys(globalThis.Blockly.Msg); - delete globalThis.Blockly; - - await Promise.all( - getLanguages().map(async (lang) => { - // Write an ESM wrapper that imports the CJS module and re-exports - // its named exports. - const cjsPath = `./${lang}.js`; - const wrapperPath = path.join(RELEASE_DIR, 'msg', `${lang}.mjs`); - const safeLang = lang.replace(/-/g, '_'); - - await writeFile( - wrapperPath, - `import ${safeLang} from '${cjsPath}'; -export const { -${exportedNames.map((name) => ` ${name},`).join('\n')} -} = ${safeLang}; -`, - ); - }), - ); +function execLangFiles() { + execSync('npm run langfiles', {stdio: 'inherit'}); } /** @@ -898,8 +821,7 @@ async function build() { await buildChunkExporters(); await buildCompiled(); await buildShims(); - buildLangfiles(); - await buildLangfileShims(); + execLangFiles(); } await build(); From ee55cf8d5b0a2eac93d64ed59ea79d501f1d47d6 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Tue, 13 Oct 2026 14:59:09 -0700 Subject: [PATCH 05/17] chore: move shared function, stop altering sourcemap for parity --- packages/blockly/scripts/build.mjs | 202 +----------------- packages/blockly/scripts/lib/build_utils.mjs | 196 +++++++++++++++++ .../tests/compile/test_compile_advanced.mjs | 2 +- 3 files changed, 200 insertions(+), 200 deletions(-) create mode 100644 packages/blockly/scripts/lib/build_utils.mjs diff --git a/packages/blockly/scripts/build.mjs b/packages/blockly/scripts/build.mjs index e314e8c3468..b797f6122ee 100644 --- a/packages/blockly/scripts/build.mjs +++ b/packages/blockly/scripts/build.mjs @@ -20,8 +20,6 @@ import { } from 'node:fs/promises'; import {globSync} from 'glob'; -import {compiler as ClosureCompiler} from 'google-closure-compiler'; -import {getNativeImagePath} from 'google-closure-compiler/lib/utils.js'; import * as path from 'path'; import yargs from 'yargs'; import {hideBin} from 'yargs/helpers'; @@ -34,6 +32,7 @@ import { } from './build_constants.mjs'; import {posixPath, quote} from './helpers.js'; import {getPackageJson} from './lib/package_json.mjs'; +import {compile} from './lib/build_utils.mjs'; const argv = yargs(hideBin(process.argv)).parse(); @@ -218,118 +217,6 @@ async function stripApacheLicenses(filePaths) { ); } -/** - * Closure Compiler diagnostic groups we want to be treated as errors. - * These are effected when the --debug or --strict flags are passed. - * For a full list of Closure Compiler groups, consult the output of - * google-closure-compiler --help or look in the source here: - * https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/DiagnosticGroups.java#L117 - * - * The list in JSCOMP_ERROR contains all the diagnostic groups we know - * about, but some are commented out if we don't want them, and may - * appear in JSCOMP_WARNING or JSCOMP_OFF instead. Items not - * appearing on any list will default to setting provided by the - * compiler, which may vary depending on compilation level. - */ -const JSCOMP_ERROR = [ - // 'accessControls', // Deprecated; means same as visibility. - // 'checkPrototypalTypes', // override annotations are stripped by tsc. - 'checkRegExp', - // 'checkTypes', // Disabled; see note in JSCOMP_OFF. - 'checkVars', - 'conformanceViolations', - 'const', - 'constantProperty', - 'duplicateMessage', - 'es5Strict', - 'externsValidation', - 'extraRequire', // Undocumented but valid. - 'functionParams', - // 'globalThis', // This types are stripped by tsc. - 'invalidCasts', - 'misplacedTypeAnnotation', - // 'missingOverride', // There are many of these, which should be fixed. - 'missingPolyfill', - // 'missingProperties', // Unset static properties are stripped by tsc. - 'missingProvide', - 'missingRequire', - 'missingReturn', - // 'missingSourcesWarnings', // Group of several other options. - 'moduleLoad', - 'msgDescriptions', - // 'nonStandardJsDocs', // Disabled; see note in JSCOMP_OFF. - // 'partialAlias', // Don't want this to be an error yet; only warning. - // 'polymer', // Not applicable. - // 'reportUnknownTypes', // VERY verbose. - // 'strictCheckTypes', // Use --strict to enable. - // 'strictMissingProperties', // Part of strictCheckTypes. - 'strictModuleChecks', // Undocumented but valid. - 'strictModuleDepCheck', - // 'strictPrimitiveOperators', // Part of strictCheckTypes. - 'suspiciousCode', - 'typeInvalidation', - 'undefinedVars', - 'underscore', - 'unknownDefines', - // 'unusedLocalVariables', // Disabled; see note in JSCOMP_OFF. - 'uselessCode', - 'untranspilableFeatures', - // 'visibility', // Disabled; see note in JSCOMP_OFF. -]; - -/** - * Closure Compiler diagnostic groups we want to be treated as warnings. - * These are effected when the --debug or --strict flags are passed. - * - * For most (all?) diagnostic groups this is the default level, so - * it's generally sufficient to remove them from JSCOMP_ERROR. - */ -const JSCOMP_WARNING = ['deprecated', 'deprecatedAnnotations']; - -/** - * Closure Compiler diagnostic groups we want to be ignored. These - * suppressions are always effected by default. - * - * Make sure that anything added here is commented out of JSCOMP_ERROR - * above, as that takes precedence.) - */ -const JSCOMP_OFF = [ - /* The removal of Closure type system types from our JSDoc - * annotations means that the Closure Compiler now generates certain - * diagnostics because it no longer has enough information to be - * sure that the input code is correct. The following diagnostic - * groups are turned off to suppress such errors. - * - * When adding additional items to this list it may be helpful to - * search the compiler source code - * (https://github.com/google/closure-compiler/) for the JSC_* - * diagnostic name (omitting the JSC_ prefix) to find the corresponding - * DiagnosticGroup. - */ - 'checkTypes', - 'nonStandardJsDocs', // Due to @internal - 'unusedLocalVariables', // Due to code generated for merged namespaces. - - /* In order to transition to ES modules, modules will need to import - * one another by relative paths. This means that the previous - * practice of moving all source files into the same directory for - * compilation would break imports. - * - * Not flattening files in this way breaks our usage - * of @package however: files were flattened so that all Blockly - * source files are in the same directory and can use @package to - * mark methods that are only allowed for use by Blockly, while - * still allowing access between e.g. core/events/* and - * core/utils/*. We were downgrading access control violations - * (including @private) to warnings, but this ends up being so - * spammy that it makes the compiler output nearly useless. - * - * Once ES module migration is complete, they will be re-enabled and - * an alternative to @package will be established. - */ - 'visibility', -]; - /** * Return the path to the generated chunk exporter for the given * chunk, relative to TSC_OUTPUT_DIR. @@ -562,78 +449,6 @@ function getChunkOptions() { return {chunk: chunkOptions, js: allFiles, chunk_wrapper: chunkWrappers}; } -/** - * Helper method for fetching the default closure compiler options. - */ -function getDefaultCompileOptions() { - const defaultOptions = { - compilation_level: 'SIMPLE_OPTIMIZATIONS', - warning_level: argv.verbose ? 'VERBOSE' : 'DEFAULT', - language_in: 'ECMASCRIPT_2020', - language_out: 'ECMASCRIPT_2015', - jscomp_off: [...JSCOMP_OFF], - rewrite_polyfills: true, - hide_warnings_for: ['node_modules'], - define: ['COMPILED=true'], - }; - if (argv.debug || argv.strict) { - defaultOptions.jscomp_error = [...JSCOMP_ERROR]; - defaultOptions.jscomp_warning = [...JSCOMP_WARNING]; - if (argv.strict) { - defaultOptions.jscomp_error.push('strictCheckTypes'); - } - } - return defaultOptions; -} - -/** - * Helper method for calling the Closure Compiler, establishing - * default options (that can be overridden by the caller). - * - * The compiler reads its inputs from and writes its outputs to disk, - * so the caller is expected to supply --js and either --js_output_file - * or --chunk_output_path_prefix. - * - * @param {{[flag: string]: string|boolean|!Array}} options - * Caller-supplied options that will override the defaultOptions. - * @return {!Promise} Resolves with the compiler's stdout once - * it exits successfully; rejects if it exits non-zero. - */ -export function compile(options) { - const compilerInstance = new ClosureCompiler({ - ...getDefaultCompileOptions(), - ...options, - }); - - // google-closure-compiler's node API always shells out to `java` - // unless JAR_PATH is cleared; only its gulp and grunt plugins know - // about the platform-native binaries. Do what those plugins do, so - // that a JRE isn't needed on platforms we ship a binary for. See - // getFirstSupportedPlatform() in google-closure-compiler/lib/utils.js. - const nativeImagePath = getNativeImagePath(); - if (nativeImagePath) { - compilerInstance.JAR_PATH = null; - compilerInstance.javaPath = nativeImagePath; - } else if (!process.env.JAVA_HOME) { - throw new Error( - 'No native Closure Compiler binary for this platform and no JRE ' + - 'found; set JAVA_HOME or install google-closure-compiler-.', - ); - } - - return new Promise((resolve, reject) => { - compilerInstance.run((exitCode, stdout, stderr) => { - // Diagnostics go to stderr even when compilation succeeds. - if (stderr) process.stderr.write(stderr); - if (exitCode === 0) { - resolve(stdout); - } else { - reject(new Error(`Closure Compiler exited with code ${exitCode}.`)); - } - }); - }); -} - /** * This task compiles the core library, blocks and generators, creating * blockly_compressed.js, blocks_compressed.js, etc. @@ -676,7 +491,7 @@ async function buildCompiled() { await rm(compiledDir, {recursive: true, force: true}); await mkdir(compiledDir, {recursive: true}); await mkdir(RELEASE_DIR, {recursive: true}); - await compile(options); + await compile(options, argv); for (const chunk of chunks) { const compiledPath = path.join(compiledDir, `${chunk.name}.js`); const outputName = `${chunk.name}${COMPILED_SUFFIX}.js`; @@ -696,24 +511,13 @@ async function buildCompiled() { if (sourceMap.mappings.endsWith(';')) { sourceMap.mappings = sourceMap.mappings.slice(0, -1); } - if (typeof sourceMap.lineCount === 'number') { - delete sourceMap.lineCount; - } sourceMap.sourcesContent = await Promise.all( sourceMap.sources.map((source) => readFile(source, 'utf8').catch(() => null), ), ); - const ordered = { - version: sourceMap.version, - sources: sourceMap.sources, - names: sourceMap.names, - mappings: sourceMap.mappings, - file: sourceMap.file, - sourcesContent: sourceMap.sourcesContent, - }; - await writeFile(`${outputPath}.map`, JSON.stringify(ordered)); + await writeFile(`${outputPath}.map`, JSON.stringify(sourceMap)); await unlink(`${compiledPath}.map`); await rename(compiledPath, outputPath); diff --git a/packages/blockly/scripts/lib/build_utils.mjs b/packages/blockly/scripts/lib/build_utils.mjs new file mode 100644 index 00000000000..06c2d28c06d --- /dev/null +++ b/packages/blockly/scripts/lib/build_utils.mjs @@ -0,0 +1,196 @@ +/** + * @license + * Copyright 2026 Raspberry Pi Foundation + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Helpers for building/compiling using Googles Closure Compiler. + */ + +import {compiler as ClosureCompiler} from 'google-closure-compiler'; +import {getNativeImagePath} from 'google-closure-compiler/lib/utils.js'; + +/** + * Closure Compiler diagnostic groups we want to be treated as errors. + * These are effected when the --debug or --strict flags are passed. + * For a full list of Closure Compiler groups, consult the output of + * google-closure-compiler --help or look in the source here: + * https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/DiagnosticGroups.java#L117 + * + * The list in JSCOMP_ERROR contains all the diagnostic groups we know + * about, but some are commented out if we don't want them, and may + * appear in JSCOMP_WARNING or JSCOMP_OFF instead. Items not + * appearing on any list will default to setting provided by the + * compiler, which may vary depending on compilation level. + */ +const JSCOMP_ERROR = [ + // 'accessControls', // Deprecated; means same as visibility. + // 'checkPrototypalTypes', // override annotations are stripped by tsc. + 'checkRegExp', + // 'checkTypes', // Disabled; see note in JSCOMP_OFF. + 'checkVars', + 'conformanceViolations', + 'const', + 'constantProperty', + 'duplicateMessage', + 'es5Strict', + 'externsValidation', + 'extraRequire', // Undocumented but valid. + 'functionParams', + // 'globalThis', // This types are stripped by tsc. + 'invalidCasts', + 'misplacedTypeAnnotation', + // 'missingOverride', // There are many of these, which should be fixed. + 'missingPolyfill', + // 'missingProperties', // Unset static properties are stripped by tsc. + 'missingProvide', + 'missingRequire', + 'missingReturn', + // 'missingSourcesWarnings', // Group of several other options. + 'moduleLoad', + 'msgDescriptions', + // 'nonStandardJsDocs', // Disabled; see note in JSCOMP_OFF. + // 'partialAlias', // Don't want this to be an error yet; only warning. + // 'polymer', // Not applicable. + // 'reportUnknownTypes', // VERY verbose. + // 'strictCheckTypes', // Use --strict to enable. + // 'strictMissingProperties', // Part of strictCheckTypes. + 'strictModuleChecks', // Undocumented but valid. + 'strictModuleDepCheck', + // 'strictPrimitiveOperators', // Part of strictCheckTypes. + 'suspiciousCode', + 'typeInvalidation', + 'undefinedVars', + 'underscore', + 'unknownDefines', + // 'unusedLocalVariables', // Disabled; see note in JSCOMP_OFF. + 'uselessCode', + 'untranspilableFeatures', + // 'visibility', // Disabled; see note in JSCOMP_OFF. +]; + +/** + * Closure Compiler diagnostic groups we want to be treated as warnings. + * These are effected when the --debug or --strict flags are passed. + * + * For most (all?) diagnostic groups this is the default level, so + * it's generally sufficient to remove them from JSCOMP_ERROR. + */ +const JSCOMP_WARNING = ['deprecated', 'deprecatedAnnotations']; + +/** + * Closure Compiler diagnostic groups we want to be ignored. These + * suppressions are always effected by default. + * + * Make sure that anything added here is commented out of JSCOMP_ERROR + * above, as that takes precedence.) + */ +const JSCOMP_OFF = [ + /* The removal of Closure type system types from our JSDoc + * annotations means that the Closure Compiler now generates certain + * diagnostics because it no longer has enough information to be + * sure that the input code is correct. The following diagnostic + * groups are turned off to suppress such errors. + * + * When adding additional items to this list it may be helpful to + * search the compiler source code + * (https://github.com/google/closure-compiler/) for the JSC_* + * diagnostic name (omitting the JSC_ prefix) to find the corresponding + * DiagnosticGroup. + */ + 'checkTypes', + 'nonStandardJsDocs', // Due to @internal + 'unusedLocalVariables', // Due to code generated for merged namespaces. + + /* In order to transition to ES modules, modules will need to import + * one another by relative paths. This means that the previous + * practice of moving all source files into the same directory for + * compilation would break imports. + * + * Not flattening files in this way breaks our usage + * of @package however: files were flattened so that all Blockly + * source files are in the same directory and can use @package to + * mark methods that are only allowed for use by Blockly, while + * still allowing access between e.g. core/events/* and + * core/utils/*. We were downgrading access control violations + * (including @private) to warnings, but this ends up being so + * spammy that it makes the compiler output nearly useless. + * + * Once ES module migration is complete, they will be re-enabled and + * an alternative to @package will be established. + */ + 'visibility', +]; + +/** + * Helper method for fetching the default closure compiler options. + */ +function getDefaultCompileOptions(argv) { + const defaultOptions = { + compilation_level: 'SIMPLE_OPTIMIZATIONS', + warning_level: argv?.verbose ? 'VERBOSE' : 'DEFAULT', + language_in: 'ECMASCRIPT_2020', + language_out: 'ECMASCRIPT_2015', + jscomp_off: [...JSCOMP_OFF], + rewrite_polyfills: true, + hide_warnings_for: ['node_modules'], + define: ['COMPILED=true'], + }; + if (argv?.debug || argv?.strict) { + defaultOptions.jscomp_error = [...JSCOMP_ERROR]; + defaultOptions.jscomp_warning = [...JSCOMP_WARNING]; + if (argv?.strict) { + defaultOptions.jscomp_error.push('strictCheckTypes'); + } + } + return defaultOptions; +} + +/** + * Helper method for calling the Closure Compiler, establishing + * default options (that can be overridden by the caller). + * + * The compiler reads its inputs from and writes its outputs to disk, + * so the caller is expected to supply --js and either --js_output_file + * or --chunk_output_path_prefix. + * + * @param {{[flag: string]: string|boolean|!Array}} options + * Caller-supplied options that will override the defaultOptions. + * @return {!Promise} Resolves with the compiler's stdout once + * it exits successfully; rejects if it exits non-zero. + */ +export function compile(options, argv) { + const compilerInstance = new ClosureCompiler({ + ...getDefaultCompileOptions(argv), + ...options, + }); + + // google-closure-compiler's node API always shells out to `java` + // unless JAR_PATH is cleared; only its gulp and grunt plugins know + // about the platform-native binaries. Do what those plugins do, so + // that a JRE isn't needed on platforms we ship a binary for. See + // getFirstSupportedPlatform() in google-closure-compiler/lib/utils.js. + const nativeImagePath = getNativeImagePath(); + if (nativeImagePath) { + compilerInstance.JAR_PATH = null; + compilerInstance.javaPath = nativeImagePath; + } else if (!process.env.JAVA_HOME) { + throw new Error( + 'No native Closure Compiler binary for this platform and no JRE ' + + 'found; set JAVA_HOME or install google-closure-compiler-.', + ); + } + + return new Promise((resolve, reject) => { + compilerInstance.run((exitCode, stdout, stderr) => { + // Diagnostics go to stderr even when compilation succeeds. + if (stderr) process.stderr.write(stderr); + if (exitCode === 0) { + resolve(stdout); + } else { + reject(new Error(`Closure Compiler exited with code ${exitCode}.`)); + } + }); + }); +} diff --git a/packages/blockly/tests/compile/test_compile_advanced.mjs b/packages/blockly/tests/compile/test_compile_advanced.mjs index c4d1226f1c7..9e5583e787f 100644 --- a/packages/blockly/tests/compile/test_compile_advanced.mjs +++ b/packages/blockly/tests/compile/test_compile_advanced.mjs @@ -19,7 +19,7 @@ import globStream from 'glob-stream'; import * as path from 'path'; import {posixPath} from '../../scripts/helpers.js'; -import {compile} from '../../scripts/build.mjs'; +import {compile} from '../../scripts/lib/build_utils.mjs'; import {TSC_OUTPUT_DIR_POSIX} from '../../scripts/build_constants.mjs'; /** From 27db46431d8de2a2e637fbbec718e9ae37d74b82 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Wed, 14 Oct 2026 09:48:50 -0700 Subject: [PATCH 06/17] chore: replace glob-stream with globSync --- package-lock.json | 1 - packages/blockly/package.json | 1 - .../tests/compile/test_compile_advanced.mjs | 16 ++++++---------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index c4e7f590fa7..d93e95c1e79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35946,7 +35946,6 @@ "concurrently": "^10.0.4", "esbuild": "^0.28.2", "glob": "^13.0.6", - "glob-stream": "8.0.3", "google-closure-compiler": "^20260920.0.0", "gulp": "^5.0.1", "gulp-rename": "^2.1.0", diff --git a/packages/blockly/package.json b/packages/blockly/package.json index 0a75c4d79a0..028a37b375d 100644 --- a/packages/blockly/package.json +++ b/packages/blockly/package.json @@ -182,7 +182,6 @@ "concurrently": "^10.0.4", "esbuild": "^0.28.2", "glob": "^13.0.6", - "glob-stream": "8.0.3", "google-closure-compiler": "^20260920.0.0", "gulp": "^5.0.1", "gulp-rename": "^2.1.0", diff --git a/packages/blockly/tests/compile/test_compile_advanced.mjs b/packages/blockly/tests/compile/test_compile_advanced.mjs index 9e5583e787f..07594ab1365 100644 --- a/packages/blockly/tests/compile/test_compile_advanced.mjs +++ b/packages/blockly/tests/compile/test_compile_advanced.mjs @@ -15,7 +15,7 @@ import { writeFile, } from 'node:fs/promises'; -import globStream from 'glob-stream'; +import {globSync} from 'glob'; import * as path from 'path'; import {posixPath} from '../../scripts/helpers.js'; @@ -25,14 +25,10 @@ import {TSC_OUTPUT_DIR_POSIX} from '../../scripts/build_constants.mjs'; /** * Resolve glob patterns to file paths using glob-stream */ -function globStreamToArray(patterns) { - return new Promise((resolve, reject) => { - const paths = []; - globStream(patterns) - .on('data', (file) => paths.push(posixPath(path.relative(process.cwd(), file.path)))) - .on('error', reject) - .on('end', () => resolve(paths)); - }); +function globToArray(patterns) { + return globSync(patterns, {absolute: true}) + .map((file) => posixPath(path.relative(process.cwd(), file))) + .sort(); } /** * This task uses Closure Compiler's ADVANCED_OPTIMIZATIONS mode to @@ -56,7 +52,7 @@ async function compileAdvancedCompilationTest() { const mainPath = 'tests/compile/main.js'; const srcs = [ - ...(await globStreamToArray(TSC_OUTPUT_DIR_POSIX + '/**/*.js')), + ...(globToArray(TSC_OUTPUT_DIR_POSIX + '/**/*.js')), mainPath, 'tests/compile/test_blocks.js', ]; From 841e54ef27c66554b6723549849e0f7337f81e82 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Wed, 14 Oct 2026 11:19:34 -0700 Subject: [PATCH 07/17] chore: update appengine gulp command to use shell for build --- packages/blockly/scripts/build.mjs | 3 +++ packages/blockly/scripts/gulpfiles/appengine_tasks.mjs | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/blockly/scripts/build.mjs b/packages/blockly/scripts/build.mjs index b797f6122ee..6cbe7927e9b 100644 --- a/packages/blockly/scripts/build.mjs +++ b/packages/blockly/scripts/build.mjs @@ -612,6 +612,9 @@ ${exportedNames.map((name) => ` ${name},`).join('\n')} await rm(TMP_PACKAGE_JSON); } +/** + * Executes the langfiles command. + */ function execLangFiles() { execSync('npm run langfiles', {stdio: 'inherit'}); } diff --git a/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs b/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs index ff207bc7910..c32466ca142 100644 --- a/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs +++ b/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs @@ -12,7 +12,6 @@ import * as gulp from 'gulp'; import * as fs from 'fs'; import {execSync} from 'child_process'; -import * as buildTasks from './build_tasks.mjs'; import {getPackageJson} from '../lib/package_json.mjs'; import {cleanBuildDir, cleanReleaseDir} from '../lib/fs_utils.mjs'; import {rimraf} from 'rimraf'; @@ -157,6 +156,13 @@ function deployBetaAndClean(done) { done(); } +/** + * Executes the build command. + */ +function execBuild() { + execSync('npm run build', {stdio: 'inherit'}); +} + /** * Prepares demos. * @@ -171,7 +177,7 @@ export const prepareDemos = gulp.series( gulp.series( gulp.parallel(cleanBuildDir, cleanReleaseDir), - buildTasks.build, + execBuild, gulp.parallel(copyBuilt, copyCompressedToOldLocation, copyMessagesToOldLocation)), From 7f8dbfddc3f7ca9c86b599c1f69f0bf3e2e2ae53 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Wed, 14 Oct 2026 12:27:58 -0700 Subject: [PATCH 08/17] chore: update outdated file paths in appengine script --- .../scripts/gulpfiles/appengine_tasks.mjs | 18 +++++++++--------- .../tests/compile/test_compile_advanced.mjs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs b/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs index c32466ca142..b1bfc8cd8d6 100644 --- a/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs +++ b/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs @@ -16,8 +16,8 @@ import {getPackageJson} from '../lib/package_json.mjs'; import {cleanBuildDir, cleanReleaseDir} from '../lib/fs_utils.mjs'; import {rimraf} from 'rimraf'; -const demoTmpDir = '../_deploy'; -const demoStaticTmpDir = '../_deploy/static'; +const demoTmpDir = '_deploy'; +const demoStaticTmpDir = '_deploy/static'; /** * Cleans and creates the tmp directory used for deploying. @@ -82,8 +82,8 @@ function copyMessagesToOldLocation(done) { */ function copyAppengineSrc() { const appengineSrc = [ - `${demoStaticTmpDir}/appengine/**/*`, - `${demoStaticTmpDir}/appengine/.gcloudignore`, + `${demoStaticTmpDir}/packages/blockly/appengine/**/*`, + `${demoStaticTmpDir}/packages/blockly/appengine/.gcloudignore`, ]; return gulp.src(appengineSrc).pipe(gulp.dest(demoTmpDir)); } @@ -93,10 +93,9 @@ function copyAppengineSrc() { */ function copyPlaygroundDeps() { const playgroundDeps = [ - './node_modules/@blockly/dev-tools/dist/index.js', - './node_modules/@blockly/theme-modern/dist/index.js', - './node_modules/@blockly/block-test/dist/index.js', - './node_modules/@blockly/keyboard-navigation/dist/index.js', + '../../node_modules/@blockly/dev-tools/dist/index.js', + '../../node_modules/@blockly/theme-modern/dist/index.js', + '../../node_modules/@blockly/block-test/dist/index.js', ]; return gulp.src(playgroundDeps, {base: '.'}).pipe(gulp.dest(demoStaticTmpDir)); } @@ -159,8 +158,9 @@ function deployBetaAndClean(done) { /** * Executes the build command. */ -function execBuild() { +function execBuild(done) { execSync('npm run build', {stdio: 'inherit'}); + done() } /** diff --git a/packages/blockly/tests/compile/test_compile_advanced.mjs b/packages/blockly/tests/compile/test_compile_advanced.mjs index 07594ab1365..224e895c082 100644 --- a/packages/blockly/tests/compile/test_compile_advanced.mjs +++ b/packages/blockly/tests/compile/test_compile_advanced.mjs @@ -23,7 +23,7 @@ import {compile} from '../../scripts/lib/build_utils.mjs'; import {TSC_OUTPUT_DIR_POSIX} from '../../scripts/build_constants.mjs'; /** - * Resolve glob patterns to file paths using glob-stream + * Resolve glob patterns to file paths using globSync */ function globToArray(patterns) { return globSync(patterns, {absolute: true}) From a5be679d979a11aa00248f220a55a4413e559e8e Mon Sep 17 00:00:00 2001 From: lizschwab Date: Wed, 14 Oct 2026 12:28:30 -0700 Subject: [PATCH 09/17] chore: remove unused gulp deps --- package-lock.json | 601 ---------------------------------- packages/blockly/package.json | 3 - 2 files changed, 604 deletions(-) diff --git a/package-lock.json b/package-lock.json index d93e95c1e79..20e31056a11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7650,99 +7650,6 @@ "@shikijs/vscode-textmate": "^10.0.2" } }, - "node_modules/@gulp-sourcemaps/identity-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", - "integrity": "sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^6.4.1", - "normalize-path": "^3.0.0", - "postcss": "^7.0.16", - "source-map": "^0.6.0", - "through2": "^3.0.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/picocolors": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", - "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", - "dev": true, - "license": "ISC" - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/postcss": { - "version": "7.0.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", - "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picocolors": "^0.2.1", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - } - }, - "node_modules/@gulp-sourcemaps/identity-map/node_modules/through2": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", - "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "2 || 3" - } - }, - "node_modules/@gulp-sourcemaps/map-sources": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", - "integrity": "sha512-o/EatdaGt8+x2qpb0vFLC/2Gug/xYPRXb6a+ET1wGYKozKN3krDWC/zZFZAtrzxJHuDL12mwdfEFKcKMNvc55A==", - "dev": true, - "license": "MIT", - "dependencies": { - "normalize-path": "^2.0.1", - "through2": "^2.0.3" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/@gulp-sourcemaps/map-sources/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@gulpjs/messages": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@gulpjs/messages/-/messages-1.1.0.tgz", @@ -12476,13 +12383,6 @@ "@types/estree": "*" } }, - "node_modules/@types/expect": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", - "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/express": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", @@ -12783,17 +12683,6 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, - "node_modules/@types/vinyl": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.12.tgz", - "integrity": "sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/expect": "^1.20.4", - "@types/node": "*" - } - }, "node_modules/@types/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", @@ -14186,19 +14075,6 @@ "dev": true, "license": "MIT" }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, - "license": "(MIT OR Apache-2.0)", - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, "node_modules/autoprefixer": { "version": "10.5.4", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.4.tgz", @@ -14763,19 +14639,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/binaryextensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.3.0.tgz", - "integrity": "sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - }, - "funding": { - "url": "https://bevry.me/fund" - } - }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -16758,18 +16621,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/css": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", - "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.4", - "source-map": "^0.6.1", - "source-map-resolve": "^0.6.0" - } - }, "node_modules/css-blank-pseudo": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-7.0.1.tgz", @@ -17215,20 +17066,6 @@ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, - "node_modules/d": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", - "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", - "dev": true, - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.64", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/dat.gui": { "version": "0.7.9", "resolved": "https://registry.npmjs.org/dat.gui/-/dat.gui-0.7.9.tgz", @@ -17296,28 +17133,6 @@ } } }, - "node_modules/debug-fabulous": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", - "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "3.X", - "memoizee": "0.4.X", - "object-assign": "4.X" - } - }, - "node_modules/debug-fabulous/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/decamelize": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.1.tgz", @@ -17351,16 +17166,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -17624,16 +17429,6 @@ "node": ">=8" } }, - "node_modules/detect-newline": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", - "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/detect-node": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", @@ -18195,62 +17990,6 @@ "tests/browser-compat" ] }, - "node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", - "dev": true, - "hasInstallScript": true, - "license": "ISC", - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", - "dev": true, - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "node_modules/es6-symbol": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", - "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", - "dev": true, - "license": "ISC", - "dependencies": { - "d": "^1.0.2", - "ext": "^1.7.0" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/es6-weak-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", - "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", - "dev": true, - "license": "ISC", - "dependencies": { - "d": "1", - "es5-ext": "^0.10.46", - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.1" - } - }, "node_modules/esast-util-from-estree": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", @@ -18730,22 +18469,6 @@ "dev": true, "license": "MIT" }, - "node_modules/esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", - "dev": true, - "license": "ISC", - "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/espree": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", @@ -18968,17 +18691,6 @@ "node": ">= 0.8" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -19155,16 +18867,6 @@ "node": ">= 0.6" } }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", - "dev": true, - "license": "ISC", - "dependencies": { - "type": "^2.7.2" - } - }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -20802,76 +20504,6 @@ "node": ">=10" } }, - "node_modules/gulp-rename": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.1.0.tgz", - "integrity": "sha512-dGuzuH8jQGqCMqC544IEPhs5+O2l+IkdoSZsgd4kY97M1CxQeI3qrmweQBIrxLBbjbe/8uEWK8HHcNBc3OCy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/gulp-replace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.1.4.tgz", - "integrity": "sha512-SVSF7ikuWKhpAW4l4wapAqPPSToJoiNKsbDoUnRrSgwZHH7lH8pbPeQj1aOVYQrbZKhfSVBxVW+Py7vtulRktw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/vinyl": "^2.0.4", - "istextorbinary": "^3.0.0", - "replacestream": "^4.0.3", - "yargs-parser": ">=5.0.0-security.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gulp-sourcemaps": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz", - "integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "@gulp-sourcemaps/identity-map": "^2.0.1", - "@gulp-sourcemaps/map-sources": "^1.0.0", - "acorn": "^6.4.1", - "convert-source-map": "^1.0.0", - "css": "^3.0.0", - "debug-fabulous": "^1.0.0", - "detect-newline": "^2.0.0", - "graceful-fs": "^4.0.0", - "source-map": "^0.6.0", - "strip-bom-string": "^1.0.0", - "through2": "^2.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/gulp-sourcemaps/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/gulp-sourcemaps/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true, - "license": "MIT" - }, "node_modules/gulplog": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-2.2.0.tgz", @@ -22432,23 +22064,6 @@ "node": ">=0.10.0" } }, - "node_modules/istextorbinary": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-3.3.0.tgz", - "integrity": "sha512-Tvq1W6NAcZeJ8op+Hq7tdZ434rqnMx4CCZ7H0ff83uEloDvVbqAwaMTZcafKGJT0VHkYzuXUiCY4hlXQg6WfoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "binaryextensions": "^2.2.0", - "textextensions": "^3.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://bevry.me/fund" - } - }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -23879,16 +23494,6 @@ "yallist": "^3.0.2" } }, - "node_modules/lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es5-ext": "~0.10.2" - } - }, "node_modules/lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", @@ -24385,33 +23990,6 @@ "node": ">= 4.0.0" } }, - "node_modules/memoizee": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", - "integrity": "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==", - "dev": true, - "license": "ISC", - "dependencies": { - "d": "^1.0.2", - "es5-ext": "^0.10.64", - "es6-weak-map": "^2.0.3", - "event-emitter": "^0.3.5", - "is-promise": "^2.2.2", - "lru-queue": "^0.1.0", - "next-tick": "^1.1.0", - "timers-ext": "^0.1.7" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/memoizee/node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "dev": true, - "license": "MIT" - }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -25878,13 +25456,6 @@ "node": ">= 0.4.0" } }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "dev": true, - "license": "ISC" - }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -30588,68 +30159,6 @@ "node": ">= 10.13.0" } }, - "node_modules/replacestream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz", - "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "escape-string-regexp": "^1.0.3", - "object-assign": "^4.0.1", - "readable-stream": "^2.0.2" - } - }, - "node_modules/replacestream/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/replacestream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/replacestream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/replacestream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/replacestream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -31854,18 +31363,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-resolve": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", - "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", - "dev": true, - "license": "MIT", - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0" - } - }, "node_modules/source-map-support": { "version": "0.5.19", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", @@ -32650,19 +32147,6 @@ "b4a": "^1.6.4" } }, - "node_modules/textextensions": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-3.3.0.tgz", - "integrity": "sha512-mk82dS8eRABNbeVJrEiN5/UMSCliINAuz8mkUwH4SwslkNP//gbEzlWNS5au0z5Dpx40SQxzqZevZkn+WYJ9Dw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://bevry.me/fund" - } - }, "node_modules/thingies": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.6.1.tgz", @@ -32679,77 +32163,12 @@ "tslib": "^2" } }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/through2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/through2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/through2/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/through2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "license": "MIT" }, - "node_modules/timers-ext": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", - "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", - "dev": true, - "license": "ISC", - "dependencies": { - "es5-ext": "^0.10.64", - "next-tick": "^1.1.0" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", @@ -33086,13 +32505,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/type": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", - "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", - "dev": true, - "license": "ISC" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -35592,16 +35004,6 @@ "dev": true, "license": "MIT" }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -35948,9 +35350,6 @@ "glob": "^13.0.6", "google-closure-compiler": "^20260920.0.0", "gulp": "^5.0.1", - "gulp-rename": "^2.1.0", - "gulp-replace": "^1.1.4", - "gulp-sourcemaps": "^3.0.0", "http-server": "^14.1.1", "jsdom": "30.1.0", "json5": "^2.2.3", diff --git a/packages/blockly/package.json b/packages/blockly/package.json index 028a37b375d..f40ee9d807b 100644 --- a/packages/blockly/package.json +++ b/packages/blockly/package.json @@ -184,9 +184,6 @@ "glob": "^13.0.6", "google-closure-compiler": "^20260920.0.0", "gulp": "^5.0.1", - "gulp-rename": "^2.1.0", - "gulp-replace": "^1.1.4", - "gulp-sourcemaps": "^3.0.0", "http-server": "^14.1.1", "jsdom": "30.1.0", "json5": "^2.2.3", From 7d007973c37db9918ca70cff7554a00b12645ae9 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Wed, 14 Oct 2026 13:49:11 -0700 Subject: [PATCH 10/17] chore: linting/formatting --- packages/blockly/scripts/build.mjs | 2 +- packages/blockly/scripts/lib/build_utils.mjs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/blockly/scripts/build.mjs b/packages/blockly/scripts/build.mjs index 6cbe7927e9b..f5a07e2fc9c 100644 --- a/packages/blockly/scripts/build.mjs +++ b/packages/blockly/scripts/build.mjs @@ -31,8 +31,8 @@ import { TSC_OUTPUT_DIR_POSIX, } from './build_constants.mjs'; import {posixPath, quote} from './helpers.js'; -import {getPackageJson} from './lib/package_json.mjs'; import {compile} from './lib/build_utils.mjs'; +import {getPackageJson} from './lib/package_json.mjs'; const argv = yargs(hideBin(process.argv)).parse(); diff --git a/packages/blockly/scripts/lib/build_utils.mjs b/packages/blockly/scripts/lib/build_utils.mjs index 06c2d28c06d..7c4626ede9e 100644 --- a/packages/blockly/scripts/lib/build_utils.mjs +++ b/packages/blockly/scripts/lib/build_utils.mjs @@ -125,6 +125,8 @@ const JSCOMP_OFF = [ /** * Helper method for fetching the default closure compiler options. + * + * @param {string[]} argv The arguments passed in from the command line. */ function getDefaultCompileOptions(argv) { const defaultOptions = { @@ -157,6 +159,7 @@ function getDefaultCompileOptions(argv) { * * @param {{[flag: string]: string|boolean|!Array}} options * Caller-supplied options that will override the defaultOptions. + * @param {string[]} argv The arguments passed in from the command line. * @return {!Promise} Resolves with the compiler's stdout once * it exits successfully; rejects if it exits non-zero. */ From 9bcb315568cbb473ffa54ffab8e32dec5b45dced Mon Sep 17 00:00:00 2001 From: lizschwab Date: Wed, 14 Oct 2026 13:53:08 -0700 Subject: [PATCH 11/17] chore: formatting --- packages/blockly/scripts/lib/build_utils.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/blockly/scripts/lib/build_utils.mjs b/packages/blockly/scripts/lib/build_utils.mjs index 7c4626ede9e..db7830082b7 100644 --- a/packages/blockly/scripts/lib/build_utils.mjs +++ b/packages/blockly/scripts/lib/build_utils.mjs @@ -125,7 +125,7 @@ const JSCOMP_OFF = [ /** * Helper method for fetching the default closure compiler options. - * + * * @param {string[]} argv The arguments passed in from the command line. */ function getDefaultCompileOptions(argv) { From 04469f04a52b36616542b57c3d18d89e20816c84 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Wed, 14 Oct 2026 14:20:21 -0700 Subject: [PATCH 12/17] chore: fixed nx dep issue --- packages/blockly/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/blockly/package.json b/packages/blockly/package.json index 92c3a5ff35d..817f4c50549 100644 --- a/packages/blockly/package.json +++ b/packages/blockly/package.json @@ -72,10 +72,10 @@ }, "test-compile-advanced-browser": { "dependsOn": [ - "test-build-compile-advanced" + "test-compile-advanced" ] }, - "test-build-compile-advanced": { + "test-compile-advanced": { "dependsOn": [ "tsc" ] From deabc1437f517dfc5c10d4693e77185badef392c Mon Sep 17 00:00:00 2001 From: lizschwab Date: Fri, 16 Oct 2026 09:46:40 -0700 Subject: [PATCH 13/17] chore: update appengine paths --- nx.json | 2 +- .../blockly/scripts/gulpfiles/appengine_tasks.mjs | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/nx.json b/nx.json index dd9267e0272..d227e673a03 100644 --- a/nx.json +++ b/nx.json @@ -42,7 +42,7 @@ "cache": true }, "prepareDemos": { - "outputs": ["{projectRoot}/../_deploy"], + "outputs": ["{projectRoot}/_deploy"], "cache": true }, "tsc": { diff --git a/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs b/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs index b1bfc8cd8d6..7306f004efe 100644 --- a/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs +++ b/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs @@ -18,6 +18,7 @@ import {rimraf} from 'rimraf'; const demoTmpDir = '_deploy'; const demoStaticTmpDir = '_deploy/static'; +const demoStaticBlocklyDir = '_deploy/static/packages/blockly'; /** * Cleans and creates the tmp directory used for deploying. @@ -49,8 +50,8 @@ function copyStaticSrc(done) { * Prerequisite: clean, build. */ function copyBuilt(done) { - return gulp.src(['build/msg/*', 'dist/*_compressed.js*', 'build/*.loader.mjs'], {base: '.'}) - .pipe(gulp.dest(demoStaticTmpDir)); + return gulp.src(['build/src/**/*', 'build/msg/*', 'dist/*_compressed.js*', 'build/*.loader.mjs'], {base: '.'}) + .pipe(gulp.dest(demoStaticBlocklyDir)); } /** @@ -62,7 +63,7 @@ function copyBuilt(done) { */ function copyCompressedToOldLocation(done) { return gulp.src(['dist/*_compressed.js*']) - .pipe(gulp.dest(demoStaticTmpDir)); + .pipe(gulp.dest(demoStaticBlocklyDir)); } /** @@ -74,7 +75,7 @@ function copyCompressedToOldLocation(done) { */ function copyMessagesToOldLocation(done) { return gulp.src(['build/msg/*']) - .pipe(gulp.dest(demoStaticTmpDir + '/msg/js')); + .pipe(gulp.dest(`${demoStaticBlocklyDir}/msg/js`)); } /** @@ -82,8 +83,8 @@ function copyMessagesToOldLocation(done) { */ function copyAppengineSrc() { const appengineSrc = [ - `${demoStaticTmpDir}/packages/blockly/appengine/**/*`, - `${demoStaticTmpDir}/packages/blockly/appengine/.gcloudignore`, + `${demoStaticBlocklyDir}/appengine/**/*`, + `${demoStaticBlocklyDir}/appengine/.gcloudignore`, ]; return gulp.src(appengineSrc).pipe(gulp.dest(demoTmpDir)); } @@ -97,7 +98,7 @@ function copyPlaygroundDeps() { '../../node_modules/@blockly/theme-modern/dist/index.js', '../../node_modules/@blockly/block-test/dist/index.js', ]; - return gulp.src(playgroundDeps, {base: '.'}).pipe(gulp.dest(demoStaticTmpDir)); + return gulp.src(playgroundDeps, {base: '../..'}).pipe(gulp.dest(demoStaticBlocklyDir)); } /** From c6a1947ededce9e5b8640a732a631fe931199459 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Fri, 16 Oct 2026 11:01:13 -0700 Subject: [PATCH 14/17] chore: address feedback --- .gitignore | 1 + eslint.config.mjs | 5 +---- packages/blockly/scripts/build.mjs | 2 +- packages/blockly/tests/compile/test_compile_advanced.mjs | 4 +++- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 10986068e31..15d176078e6 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ packages/blockly/tests/compile/main_compressed.js packages/blockly/tests/compile/main_compressed.js.map packages/blockly/tests/mocha/test-modules.generated.mjs packages/blockly/temp/ +packages/blockly/_deploy # Docs: # Autogenerated reference docs, do not check in diff --git a/eslint.config.mjs b/eslint.config.mjs index f721f993b94..fae9ccb725d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -149,14 +149,11 @@ export default defineConfig( 'packages/**/build/', 'packages/**/dist/', // Core Build artifacts - // All Packages - 'packages/*/node_modules/**', - 'packages/**/build/', - 'packages/**/dist/', // Core Build artifacts 'packages/blockly/msg/*', 'packages/blockly/typings/*', 'packages/blockly/docs/*', + 'packages/blockly/_deploy/*', // Core Tests other than mocha unit tests 'packages/blockly/tests/blocks/*', 'packages/blockly/tests/themes/*', diff --git a/packages/blockly/scripts/build.mjs b/packages/blockly/scripts/build.mjs index f5a07e2fc9c..434408892d3 100644 --- a/packages/blockly/scripts/build.mjs +++ b/packages/blockly/scripts/build.mjs @@ -622,7 +622,7 @@ function execLangFiles() { /** * Build all chunks, compressed files, sourcemaps, shims, and langfiles. * - * Prerequisite: tsc (handled by nx). + * Prerequisite: tsc. */ async function build() { await buildChunkExporters(); diff --git a/packages/blockly/tests/compile/test_compile_advanced.mjs b/packages/blockly/tests/compile/test_compile_advanced.mjs index 224e895c082..466102a5e95 100644 --- a/packages/blockly/tests/compile/test_compile_advanced.mjs +++ b/packages/blockly/tests/compile/test_compile_advanced.mjs @@ -22,6 +22,8 @@ import {posixPath} from '../../scripts/helpers.js'; import {compile} from '../../scripts/lib/build_utils.mjs'; import {TSC_OUTPUT_DIR_POSIX} from '../../scripts/build_constants.mjs'; +const argv = yargs(hideBin(process.argv)).parse(); + /** * Resolve glob patterns to file paths using globSync */ @@ -66,7 +68,7 @@ async function compileAdvancedCompilationTest() { create_source_map: mapPath, }; - await compile(options); + await compile(options, argv); const sourceMap = JSON.parse(await readFile(mapPath, 'utf8')); sourceMap.file = path.basename(outputPath); From 373d6480c34a9b356d126914db548b11941e8d89 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Fri, 16 Oct 2026 11:02:11 -0700 Subject: [PATCH 15/17] chore: remove duplicated line --- eslint.config.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index fae9ccb725d..f7188ef9eba 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -149,7 +149,6 @@ export default defineConfig( 'packages/**/build/', 'packages/**/dist/', // Core Build artifacts - // Core Build artifacts 'packages/blockly/msg/*', 'packages/blockly/typings/*', 'packages/blockly/docs/*', From d78b6dfe6cde1f946769bcb6c681247dc16f9edb Mon Sep 17 00:00:00 2001 From: lizschwab Date: Fri, 16 Oct 2026 11:18:17 -0700 Subject: [PATCH 16/17] chore: fix test imports --- packages/blockly/tests/compile/test_compile_advanced.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/blockly/tests/compile/test_compile_advanced.mjs b/packages/blockly/tests/compile/test_compile_advanced.mjs index 466102a5e95..36e145bcf41 100644 --- a/packages/blockly/tests/compile/test_compile_advanced.mjs +++ b/packages/blockly/tests/compile/test_compile_advanced.mjs @@ -17,6 +17,8 @@ import { import {globSync} from 'glob'; import * as path from 'path'; +import yargs from 'yargs'; +import {hideBin} from 'yargs/helpers'; import {posixPath} from '../../scripts/helpers.js'; import {compile} from '../../scripts/lib/build_utils.mjs'; From 11eb13a702afc7ea7278b4d89a5dce86d45dd1e8 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Fri, 16 Oct 2026 14:19:02 -0700 Subject: [PATCH 17/17] chore: reverted paths in old location functions --- packages/blockly/scripts/gulpfiles/appengine_tasks.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs b/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs index 7306f004efe..6223e095ad7 100644 --- a/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs +++ b/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs @@ -63,7 +63,7 @@ function copyBuilt(done) { */ function copyCompressedToOldLocation(done) { return gulp.src(['dist/*_compressed.js*']) - .pipe(gulp.dest(demoStaticBlocklyDir)); + .pipe(gulp.dest(demoStaticTmpDir)); } /** @@ -75,7 +75,7 @@ function copyCompressedToOldLocation(done) { */ function copyMessagesToOldLocation(done) { return gulp.src(['build/msg/*']) - .pipe(gulp.dest(`${demoStaticBlocklyDir}/msg/js`)); + .pipe(gulp.dest(`${demoStaticTmpDir}/msg/js`)); } /**