refactor(build)!: Provide all generator exports when loaded as script (#7169)

Previously, when loading a generator chunk (e.g.,
javascript_compressed.js) as a script (e.g., in a browser using
a <SCRIPT> tag), only a single named export from that chunk would
be made available (e.g, javascriptGenerator would be made availabe
as Blockly.JavaScript).

Until recently, that was fine because each generator chunk had only
a single named export, but now each one additionally has a
<Lang>Generator class and Order enum export.

To allow these new exports to be accessed by script users, the
chunk wrappers are modified to provide the whole export object
at a correspondingly-named global variable—e.g., when loaded as
a script javascript_compressed.js creates a global variable named
javascript, so the named exports can be accessed as
javascript.javascriptGenerator, javascript.JavascriptGenerator
and javascript.Order, as if the user had imported them via

    import * as javascript from 'blockly/javascript';

This PR includes a breaking change and a deprecation, both of
which are only applicable when loading generators as scripts
(e.g. via a <SCRIPT> tag):

BREAKING CHANGE: The generator chunks will, when loaded as scripts
(e.g. via a <SCRIPT> tag, now clobber any existing global variable
of the corresponding name:

- dart_compresed.js will set dart
- javascript_compresed.js will set javascript
- lua_compresed.js will set lua
- php_compresed.js will set php
- python_compresed.js will set python

DEPRECATION: Accessing the generator instances at their previous
locations (Blockly.Dart, Blockly.JavaScript, Blockly.Lua,
Blockly.PHP, and Blockly.Python) is deprecated and may cease
to work in a future version of Blockly.
This commit is contained in:
Christopher Allen
2023-06-15 16:52:51 +01:00
committed by GitHub
parent a3458871db
commit 2d97e5aaf1

View File

@@ -75,12 +75,15 @@ const NAMESPACE_PROPERTY = '__namespace__';
* chunk. * chunk.
* - .exports: an expression evaluating to the exports/Module object * - .exports: an expression evaluating to the exports/Module object
* of module that is the chunk's entrypoint / top level module. * of module that is the chunk's entrypoint / top level module.
* - .reexport: if running in a browser, save the chunk's exports * - .scriptExport: When the chunk is loaded as a script (e.g., via a
* object (or a single export of it; see reexportOnly, below) at * <SCRIPT> tag), the chunk's exports object will be made available
* this location in the global namespace. * at the specified location (which must be a variable name or the
* - .reexportOnly: if reexporting and this property is set, * name of a property on an already-existing object) in the global
* save only the correspondingly-named export. Otherwise * namespace.
* save the whole export object. * - .scriptNamedExports: A map of {location: namedExport} pairs; when
* loaded as a script, the specified named exports will be saved at
* the specified locations (which again must be global variables or
* properties on already-existing objects). Optional.
* *
* The function getChunkOptions will, after running * The function getChunkOptions will, after running
* closure-calculate-chunks, update each chunk to add the following * closure-calculate-chunks, update each chunk to add the following
@@ -97,48 +100,48 @@ const chunks = [
name: 'blockly', name: 'blockly',
entry: path.join(TSC_OUTPUT_DIR, 'core', 'main.js'), entry: path.join(TSC_OUTPUT_DIR, 'core', 'main.js'),
exports: 'module$build$src$core$blockly', exports: 'module$build$src$core$blockly',
reexport: 'Blockly', scriptExport: 'Blockly',
}, },
{ {
name: 'blocks', name: 'blocks',
entry: path.join(TSC_OUTPUT_DIR, 'blocks', 'blocks.js'), entry: path.join(TSC_OUTPUT_DIR, 'blocks', 'blocks.js'),
exports: 'module$exports$Blockly$libraryBlocks', exports: 'module$exports$Blockly$libraryBlocks',
reexport: 'Blockly.libraryBlocks', scriptExport: 'Blockly.libraryBlocks',
}, },
{ {
name: 'javascript', name: 'javascript',
entry: path.join(TSC_OUTPUT_DIR, 'generators', 'javascript', 'all.js'), entry: path.join(TSC_OUTPUT_DIR, 'generators', 'javascript', 'all.js'),
exports: 'module$build$src$generators$javascript$all', exports: 'module$build$src$generators$javascript$all',
reexport: 'Blockly.JavaScript', scriptExport: 'javascript',
reexportOnly: 'javascriptGenerator', scriptNamedExports: {'Blockly.Javascript': 'javascriptGenerator'},
}, },
{ {
name: 'python', name: 'python',
entry: path.join(TSC_OUTPUT_DIR, 'generators', 'python', 'all.js'), entry: path.join(TSC_OUTPUT_DIR, 'generators', 'python', 'all.js'),
exports: 'module$build$src$generators$python$all', exports: 'module$build$src$generators$python$all',
reexport: 'Blockly.Python', scriptExport: 'python',
reexportOnly: 'pythonGenerator', scriptNamedExports: {'Blockly.Python': 'pythonGenerator'},
}, },
{ {
name: 'php', name: 'php',
entry: path.join(TSC_OUTPUT_DIR, 'generators', 'php', 'all.js'), entry: path.join(TSC_OUTPUT_DIR, 'generators', 'php', 'all.js'),
exports: 'module$build$src$generators$php$all', exports: 'module$build$src$generators$php$all',
reexport: 'Blockly.PHP', scriptExport: 'php',
reexportOnly: 'phpGenerator', scriptNamedExports: {'Blockly.PHP': 'phpGenerator'},
}, },
{ {
name: 'lua', name: 'lua',
entry: path.join(TSC_OUTPUT_DIR, 'generators', 'lua', 'all.js'), entry: path.join(TSC_OUTPUT_DIR, 'generators', 'lua', 'all.js'),
exports: 'module$build$src$generators$lua$all', exports: 'module$build$src$generators$lua$all',
reexport: 'Blockly.Lua', scriptExport: 'lua',
reexportOnly: 'luaGenerator', scriptNameExports: {'Blockly.Lua': 'luaGenerator'},
}, },
{ {
name: 'dart', name: 'dart',
entry: path.join(TSC_OUTPUT_DIR, 'generators', 'dart', 'all.js'), entry: path.join(TSC_OUTPUT_DIR, 'generators', 'dart', 'all.js'),
exports: 'module$build$src$generators$dart$all', exports: 'module$build$src$generators$dart$all',
reexport: 'Blockly.Dart', scriptExport: 'dart',
reexportOnly: 'dartGenerator', scriptNameExports: {'Blockly.Dart': 'dartGenerator'},
} }
]; ];
@@ -441,7 +444,7 @@ function chunkWrapper(chunk) {
// JavaScript expressions for the amd, cjs and browser dependencies. // JavaScript expressions for the amd, cjs and browser dependencies.
let amdDepsExpr = ''; let amdDepsExpr = '';
let cjsDepsExpr = ''; let cjsDepsExpr = '';
let browserDepsExpr = ''; let scriptDepsExpr = '';
// Arguments for the factory function. // Arguments for the factory function.
let factoryArgs = ''; let factoryArgs = '';
// Expression to get or create the namespace object. // Expression to get or create the namespace object.
@@ -452,25 +455,22 @@ function chunkWrapper(chunk) {
JSON.stringify(`./${chunk.parent.name}${COMPILED_SUFFIX}.js`); JSON.stringify(`./${chunk.parent.name}${COMPILED_SUFFIX}.js`);
amdDepsExpr = parentFilename; amdDepsExpr = parentFilename;
cjsDepsExpr = `require(${parentFilename})`; cjsDepsExpr = `require(${parentFilename})`;
browserDepsExpr = `root.${chunk.parent.reexport}`; scriptDepsExpr = `root.${chunk.parent.scriptExport}`;
factoryArgs = '__parent__'; factoryArgs = '__parent__';
namespaceExpr = `${factoryArgs}.${NAMESPACE_PROPERTY}`; namespaceExpr = `${factoryArgs}.${NAMESPACE_PROPERTY}`;
} }
// Code to assign the result of the factory function to the desired // Code to save the chunk's exports object at chunk.scriptExport and
// export location when running in a browser. When // additionally save individual named exports as directed by
// chunk.reexportOnly is set, this additionally does two other // chunk.scriptNamedExports.
// things: const scriptExportStatements = [
// - It ensures that only the desired property of the exports object `root.${chunk.scriptExport} = factory(${scriptDepsExpr});`,
// is assigned to the specified reexport location. ];
// - It ensures that the namesspace object is accessible via the for (var location in chunk.scriptNamedExports) {
// selected sub-object, so that any dependent modules can obtain const namedExport = chunk.scriptNamedExports[location];
// it. scriptExportStatements.push(
const browserExportStatements = chunk.reexportOnly ? `root.${location} = root.${chunk.scriptExport}.${namedExport};`);
`root.${chunk.reexport} = factoryExports.${chunk.reexportOnly};\n` + }
` root.${chunk.reexport}.${NAMESPACE_PROPERTY} = ` +
`factoryExports.${NAMESPACE_PROPERTY};` :
`root.${chunk.reexport} = factoryExports;`;
// Note that when loading in a browser the base of the exported path // Note that when loading in a browser the base of the exported path
// (e.g. Blockly.blocks.all - see issue #5932) might not exist // (e.g. Blockly.blocks.all - see issue #5932) might not exist
@@ -485,9 +485,8 @@ function chunkWrapper(chunk) {
define([${amdDepsExpr}], factory); define([${amdDepsExpr}], factory);
} else if (typeof exports === 'object') { // Node.js } else if (typeof exports === 'object') { // Node.js
module.exports = factory(${cjsDepsExpr}); module.exports = factory(${cjsDepsExpr});
} else { // Browser } else { // Script
var factoryExports = factory(${browserDepsExpr}); ${scriptExportStatements.join('\n ')}
${browserExportStatements}
} }
}(this, function(${factoryArgs}) { }(this, function(${factoryArgs}) {
var ${NAMESPACE_VARIABLE}=${namespaceExpr}; var ${NAMESPACE_VARIABLE}=${namespaceExpr};