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..f7188ef9eba 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -144,11 +144,6 @@ export default defineConfig( { // Note: there should be no other properties in this object ignores: [ - // All Packages - 'packages/*/node_modules/**', - 'packages/**/build/', - 'packages/**/dist/', - // Core Build artifacts // All Packages 'packages/*/node_modules/**', 'packages/**/build/', @@ -157,6 +152,7 @@ export default defineConfig( '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/nx.json b/nx.json index d609231eb9e..d227e673a03 100644 --- a/nx.json +++ b/nx.json @@ -37,16 +37,12 @@ "outputs": ["{projectRoot}/build/msg"], "cache": true }, - "minify": { - "outputs": ["{projectRoot}/dist"], - "cache": true - }, "package": { "outputs": ["{projectRoot}/dist", "{projectRoot}/build"], "cache": true }, "prepareDemos": { - "outputs": ["{projectRoot}/../_deploy"], + "outputs": ["{projectRoot}/_deploy"], "cache": true }, "tsc": { diff --git a/package-lock.json b/package-lock.json index 44e6017f8bb..1498fc0d710 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", @@ -12491,13 +12398,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", @@ -12805,17 +12705,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", @@ -14208,19 +14097,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", @@ -14738,19 +14614,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", @@ -16703,18 +16566,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", @@ -17160,20 +17011,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", @@ -17241,28 +17078,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", @@ -17296,16 +17111,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", @@ -17562,16 +17367,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", @@ -18108,62 +17903,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", @@ -18649,22 +18388,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", @@ -18887,17 +18610,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", @@ -19202,16 +18914,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", @@ -20862,76 +20564,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", @@ -22490,23 +22122,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", @@ -23955,16 +23570,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", @@ -24462,33 +24067,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", @@ -25947,13 +25525,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", @@ -30536,68 +30107,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", @@ -31802,18 +31311,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", @@ -32588,19 +32085,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", @@ -32617,77 +32101,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", @@ -33024,13 +32443,6 @@ "node": "^22.22.2 || ^24.15.0 || >=26.0.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", @@ -35513,16 +34925,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", @@ -35870,9 +35272,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/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 2d209dc66b1..817f4c50549 100644 --- a/packages/blockly/package.json +++ b/packages/blockly/package.json @@ -72,20 +72,14 @@ }, "test-compile-advanced-browser": { "dependsOn": [ - "test-build-compile-advanced" + "test-compile-advanced" ] }, - "test-build-compile-advanced": { + "test-compile-advanced": { "dependsOn": [ "tsc" ] }, - "docs": { - "dependsOn": [ - "package", - "^build" - ] - }, "start": { "dependsOn": [ { @@ -101,11 +95,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\"", + "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", @@ -113,7 +107,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\"", @@ -126,8 +119,7 @@ "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-build-compile-advanced": "gulp buildAdvancedCompilationTest --debug", - "test-compile-advanced": "nx run blockly:test-build-compile-advanced", + "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/test-types\"", @@ -199,9 +191,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/scripts/gulpfiles/build_tasks.mjs b/packages/blockly/scripts/build.mjs similarity index 65% rename from packages/blockly/scripts/gulpfiles/build_tasks.mjs rename to packages/blockly/scripts/build.mjs index 5bcaf4b2a0f..434408892d3 100644 --- a/packages/blockly/scripts/gulpfiles/build_tasks.mjs +++ b/packages/blockly/scripts/build.mjs @@ -1,24 +1,26 @@ /** * @license - * Copyright 2018 Google LLC + * Copyright 2026 Raspberry Pi Foundation * SPDX-License-Identifier: Apache-2.0 */ /** - * @fileoverview Gulp script to build Blockly for Node & NPM. + * @fileoverview Script to build the blockly package. */ -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 { + appendFile, + mkdir, + readFile, + rename, + rm, + unlink, + writeFile, +} from 'node:fs/promises'; import {globSync} from 'glob'; -import {gulp as closureCompiler} from 'google-closure-compiler'; +import * as path from 'path'; import yargs from 'yargs'; import {hideBin} from 'yargs/helpers'; @@ -26,22 +28,14 @@ import { BUILD_DIR, RELEASE_DIR, TSC_OUTPUT_DIR, -} from './config.mjs'; - -import {posixPath, quote} from '../helpers.js'; -import {getPackageJson} from '../lib/package_json.mjs'; + TSC_OUTPUT_DIR_POSIX, +} from './build_constants.mjs'; +import {posixPath, quote} from './helpers.js'; +import {compile} from './lib/build_utils.mjs'; +import {getPackageJson} from './lib/package_json.mjs'; const argv = yargs(hideBin(process.argv)).parse(); -//////////////////////////////////////////////////////////// -// Build // -//////////////////////////////////////////////////////////// - -/** - * Posix version of TSC_OUTPUT_DIR - */ -const TSC_OUTPUT_DIR_POSIX = posixPath(TSC_OUTPUT_DIR); - /** * Suffix to add to compiled output files. */ @@ -182,9 +176,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,127 +194,28 @@ const licenseRegex = `\\/\\*\\* /** * Helper method for stripping the Google's and MIT's Apache Licenses. - */ -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. -} - -/** - * 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.) + * @param {!Array} filePaths Files to rewrite. */ -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', -]; +async function stripApacheLicenses(filePaths) { + await Promise.all( + filePaths.map(async (filePath) => { + const source = await readFile(filePath, 'utf8'); + // 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'), + '\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); + }), + ); +} /** * Return the path to the generated chunk exporter for the given @@ -327,7 +223,7 @@ const JSCOMP_OFF = [ * * 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 +289,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 +297,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 +316,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 @@ -453,7 +352,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};`, @@ -550,41 +449,16 @@ function getChunkOptions() { return {chunk: chunkOptions, js: allFiles, chunk_wrapper: chunkWrappers}; } -/** - * 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. - */ -function compile(options) { - 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'); - } - } - // Extra options for Closure Compiler gulp plugin. - const platform = ['native']; - - return closureCompiler({...defaultOptions, ...options}, {platform}); -} - /** * This task compiles the core library, blocks and generators, creating * blockly_compressed.js, blocks_compressed.js, etc. */ -function buildCompiled() { +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. @@ -600,23 +474,58 @@ 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. 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, }; - // 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); + // 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, argv); + for (const chunk of chunks) { + const compiledPath = path.join(compiledDir, `${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. 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); + 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); + } + sourceMap.sourcesContent = await Promise.all( + sourceMap.sources.map((source) => + readFile(source, 'utf8').catch(() => null), + ), + ); + + await writeFile(`${outputPath}.map`, JSON.stringify(sourceMap)); + await unlink(`${compiledPath}.map`); + + await rename(compiledPath, outputPath); + await appendFile( + outputPath, + `\n\n//# sourceMappingURL=${outputName}.map\n`, + ); + } } /** @@ -635,13 +544,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 +558,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 +570,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 +592,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 +609,26 @@ ${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. + * Executes the langfiles command. + */ +function execLangFiles() { + execSync('npm run langfiles', {stdio: 'inherit'}); +} + +/** + * Build all chunks, compressed files, sourcemaps, shims, and langfiles. * * Prerequisite: tsc. */ -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. - } - - const srcs = [ - TSC_OUTPUT_DIR + '/**/*.js', - 'tests/compile/main.js', - 'tests/compile/test_blocks.js', - ]; - - // 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/')); +async function build() { + await buildChunkExporters(); + await buildCompiled(); + await buildShims(); + execLangFiles(); } -// function tsc, above -export const minify = gulp.series( - buildChunkExporters, - buildCompiled, - buildShims, -); -export const build = minify; - -// Manually-invokable targets, with prerequisites where required. -// function messages, above -export const buildAdvancedCompilationTest = gulp.series( - compileAdvancedCompilationTest, -); +await build(); diff --git a/packages/blockly/scripts/build_constants.mjs b/packages/blockly/scripts/build_constants.mjs index b07c593fec1..f0bc4ca2da2 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. @@ -19,6 +21,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/scripts/gulpfiles/appengine_tasks.mjs b/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs index ff207bc7910..6223e095ad7 100644 --- a/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs +++ b/packages/blockly/scripts/gulpfiles/appengine_tasks.mjs @@ -12,13 +12,13 @@ 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'; -const demoTmpDir = '../_deploy'; -const demoStaticTmpDir = '../_deploy/static'; +const demoTmpDir = '_deploy'; +const demoStaticTmpDir = '_deploy/static'; +const demoStaticBlocklyDir = '_deploy/static/packages/blockly'; /** * Cleans and creates the tmp directory used for deploying. @@ -50,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)); } /** @@ -75,7 +75,7 @@ function copyCompressedToOldLocation(done) { */ function copyMessagesToOldLocation(done) { return gulp.src(['build/msg/*']) - .pipe(gulp.dest(demoStaticTmpDir + '/msg/js')); + .pipe(gulp.dest(`${demoStaticTmpDir}/msg/js`)); } /** @@ -83,8 +83,8 @@ function copyMessagesToOldLocation(done) { */ function copyAppengineSrc() { const appengineSrc = [ - `${demoStaticTmpDir}/appengine/**/*`, - `${demoStaticTmpDir}/appengine/.gcloudignore`, + `${demoStaticBlocklyDir}/appengine/**/*`, + `${demoStaticBlocklyDir}/appengine/.gcloudignore`, ]; return gulp.src(appengineSrc).pipe(gulp.dest(demoTmpDir)); } @@ -94,12 +94,11 @@ 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)); + return gulp.src(playgroundDeps, {base: '../..'}).pipe(gulp.dest(demoStaticBlocklyDir)); } /** @@ -157,6 +156,14 @@ function deployBetaAndClean(done) { done(); } +/** + * Executes the build command. + */ +function execBuild(done) { + execSync('npm run build', {stdio: 'inherit'}); + done() +} + /** * Prepares demos. * @@ -171,7 +178,7 @@ export const prepareDemos = gulp.series( gulp.series( gulp.parallel(cleanBuildDir, cleanReleaseDir), - buildTasks.build, + execBuild, gulp.parallel(copyBuilt, copyCompressedToOldLocation, copyMessagesToOldLocation)), diff --git a/packages/blockly/scripts/lib/build_utils.mjs b/packages/blockly/scripts/lib/build_utils.mjs new file mode 100644 index 00000000000..db7830082b7 --- /dev/null +++ b/packages/blockly/scripts/lib/build_utils.mjs @@ -0,0 +1,199 @@ +/** + * @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. + * + * @param {string[]} argv The arguments passed in from the command line. + */ +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. + * @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. + */ +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 new file mode 100644 index 00000000000..36e145bcf41 --- /dev/null +++ b/packages/blockly/tests/compile/test_compile_advanced.mjs @@ -0,0 +1,87 @@ +/** + * @license + * Copyright 2026 Raspberry Pi Foundation + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Test compile for closure compiler's advanced optimizations. + */ + +import { + appendFile, + readFile, + unlink, + writeFile, +} from 'node:fs/promises'; + +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'; +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 + */ +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 + * 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 = [ + ...(globToArray(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, argv); + + 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`, + ); +} + +await compileAdvancedCompilationTest();