mirror of
https://github.com/google/blockly.git
synced 2026-01-10 10:27:08 +01:00
Use the freshly-built build/*_compresssed.js files when bootstrapping in compressed mode, rather than using the checked-in files in the repository root. This helps ensure that compressed and uncompressed mode will be testing (as closely as possible) the same code.
238 lines
8.9 KiB
JavaScript
238 lines
8.9 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2021 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @fileoverview Bootstrap code to load Blockly, typically in
|
|
* uncompressed mode.
|
|
*
|
|
* Load this file in a <script> tag in a web page to use the
|
|
* Closure Library debug module loader to load Blockly in
|
|
* uncompressed mode.
|
|
*
|
|
* You must use a <script> tag to load this script first, then
|
|
* import bootstrap_done.mjs in a <script type=module> to wait for
|
|
* bootstrapping to finish.
|
|
*
|
|
* See tests/playground.html for example usage.
|
|
*
|
|
* Exception: for speed and compatibility reasons, if this is
|
|
* script is loaded in Internet Explorer or from a host other than
|
|
* localhost, blockly_compressed.js (et al.) will be loaded
|
|
* instead. IE 11 doesn't understand modern JavaScript, and
|
|
* because of the sequential module loading carried out by the
|
|
*
|
|
* (Note also that this file eschews certain modern JS constructs,
|
|
* like template literals, for compatibility with IE 11.)
|
|
*
|
|
* The bootstrap code will consult a BLOCKLY_BOOTSTRAP_OPTIONS
|
|
* global variable to determine what to load. See further
|
|
* documentation inline.
|
|
*
|
|
*/
|
|
'use strict';
|
|
|
|
(function() {
|
|
// Values usedd to compute default bootstrap options.
|
|
const isIe = navigator.userAgent.indexOf('MSIE') !== -1 ||
|
|
navigator.appVersion.indexOf('Trident/') > -1;
|
|
const localhosts = ['localhost', '127.0.0.1', '[::1]'];
|
|
const isLocalhost = localhosts.includes(location.hostname);
|
|
const isFileUrl = location.origin === 'file://';
|
|
|
|
// Default bootstrap options.
|
|
const options = {
|
|
// Decide whether to use compmiled mode or not. Please see issue
|
|
// #5557 for more information.
|
|
loadCompressed: isIe || !(isLocalhost || isFileUrl),
|
|
|
|
// URL of the blockly repository. This is needed for a few reasons:
|
|
//
|
|
// - We need an absolute path instead of relative path because the
|
|
// advanced_playground the and regular playground are in
|
|
// different folders.
|
|
// - We need to get the root directory for blockly because it is
|
|
// different for github.io, appspot and local.
|
|
//
|
|
// Default value will work so long as top-level page is loaded
|
|
// from somewhere in tests/.
|
|
root: window.location.href.replace(/\/tests\/.*$/, '/'),
|
|
|
|
// List of deps files to load. Paths relative to root.
|
|
depsFiles: [
|
|
'build/deps.js',
|
|
],
|
|
|
|
// List of goog.modules to goog.require.
|
|
requires: [
|
|
'Blockly',
|
|
'Blockly.libraryBlocks',
|
|
'Blockly.Dart.all',
|
|
'Blockly.JavaScript.all',
|
|
'Blockly.Lua.all',
|
|
'Blockly.PHP.all',
|
|
'Blockly.Python.all',
|
|
],
|
|
|
|
// List of scripts to load in compressed mode, instead of
|
|
// requires. Paths relative to root.
|
|
compressedScripts: [
|
|
'build/blockly_compressed.js',
|
|
'build/blocks_compressed.js',
|
|
'build/dart_compressed.js',
|
|
'build/javascript_compressed.js',
|
|
'build/lua_compressed.js',
|
|
'build/php_compressed.js',
|
|
'build/python_compressed.js',
|
|
],
|
|
|
|
// Additional scripts to be loaded after Blockly is loaded,
|
|
// whether Blockly is loaded from compressed or uncompressed.
|
|
// Paths relative to root.
|
|
additionalScripts: [
|
|
'msg/messages.js',
|
|
],
|
|
};
|
|
if (typeof window.BLOCKLY_BOOTSTRAP_OPTIONS === 'object') {
|
|
Object.assign(options, window.BLOCKLY_BOOTSTRAP_OPTIONS);
|
|
}
|
|
|
|
/* eslint-disable-next-line no-undef */
|
|
if (typeof module === 'object' && typeof module.exports === 'object') {
|
|
// Running in node.js. Maybe we wish to support this.
|
|
// blockly_uncompressed formerly did, though it appears that the
|
|
// code had not been working for some time (at least since PR
|
|
// #5718 back in December 2021. For now just throw an error.
|
|
throw new Error('Bootstrapping in node.js not implemented.');
|
|
}
|
|
|
|
if (!options.loadCompressed) {
|
|
// We can load Blockly in uncompressed mode. Note that this section
|
|
// needs to parse in IE11 (mostly ES5.1, but allowing e.g. const),
|
|
// but it does not need to be _executable_ in IE11 - it is safe to
|
|
// use ES6 builtins.
|
|
|
|
// Disable loading of closure/goog/deps.js (which doesn't exist).
|
|
window.CLOSURE_NO_DEPS = true;
|
|
// Load the Closure Library's base.js (the only part of the
|
|
// libary we use, mainly for goog.require / goog.provide /
|
|
// goog.module).
|
|
document.write(
|
|
'<script src="' + options.root + '/closure/goog/base.js"></script>');
|
|
|
|
// Prevent spurious transpilation warnings.
|
|
document.write('<script>goog.TRANSPILE = "never";</script>');
|
|
|
|
// Load dependency graph info from the specified deps files -
|
|
// typically just build/deps.js. To update deps after changing
|
|
// any module's goog.requires / imports, run `npm run build:deps`.
|
|
for (let i = 0; i < options.depsFiles.length; i++) {
|
|
document.write(
|
|
'<script src="' + options.root + options.depsFiles[i] + '">' +
|
|
'</script>');
|
|
}
|
|
|
|
// Create a global variable to remember some stat that will be
|
|
// needed by later scripts.
|
|
window.BlocklyLoader = {
|
|
requires: options.requires,
|
|
done: null,
|
|
};
|
|
|
|
// Assemble a list of module targets to bootstrap.
|
|
//
|
|
// The first group of targets are those listed in
|
|
// options.requires.
|
|
//
|
|
// The next target is a fake one that will load
|
|
// bootstrap_helper.js. For each we generate a call to
|
|
// goog.addDependency to tell the debug module loader that it can
|
|
// be loaded via a fake module name, and that it depends on all
|
|
// the targets in the first group (and indeed it will make a call
|
|
// to goog.require for each one).
|
|
//
|
|
// We then create another target for each of
|
|
// options.additionalScripts, again generating calls to
|
|
// goog.addDependency for each one making it dependent on the
|
|
// previous one.
|
|
let requires = options.requires.slice();
|
|
const scripts =
|
|
['tests/bootstrap_helper.js'].concat(options.additionalScripts);
|
|
const scriptDeps = [];
|
|
for (let script, i = 0; script = scripts[i]; i++) {
|
|
const fakeModuleName = 'script.' + script.replace(/[./]/g, "-");
|
|
// requires.push(fakeModuleName);
|
|
scriptDeps.push(' goog.addDependency(' +
|
|
quote('../../' + script) + ', [' + quote(fakeModuleName) +
|
|
'], [' + requires.map(quote).join() + "], {'lang': 'es6'});\n");
|
|
requires = [fakeModuleName];
|
|
}
|
|
|
|
// Finally, write out a script containing the genrated
|
|
// goog.addDependency calls and a call to goog.bootstrap
|
|
// requesting the loading of the final target, which will cause
|
|
// all the previous ones to be loaded recursively. Wrap this in a
|
|
// promise and save it so it can be awaited in bootstrap_done.mjs.
|
|
document.write(
|
|
'<script>\n' + scriptDeps.join('') +
|
|
' window.BlocklyLoader.done = new Promise((resolve, reject) => {\n' +
|
|
' goog.bootstrap([' + requires.map(quote).join() + '], resolve);\n' +
|
|
' });\n' +
|
|
'</script>\n');
|
|
} else {
|
|
// We need to load Blockly in compressed mode. Load
|
|
// blockly_compressed.js et al. using <script> tags.
|
|
const scripts =
|
|
options.compressedScripts.concat(options.additionalScripts);
|
|
for (let i = 0; i < scripts.length; i++) {
|
|
document.write(
|
|
'<script src="' + options.root + scripts[i] + '"></script>');
|
|
}
|
|
}
|
|
|
|
return; // All done. Only helper functions after this point.
|
|
|
|
/**
|
|
* Convert a string into a string literal. Strictly speaking we
|
|
* only need to escape backslash, \r, \n, \u2028 (line separator),
|
|
* \u2029 (paragraph separator) and whichever quote character we're
|
|
* using, but for simplicity we escape all the control characters.
|
|
*
|
|
* Based on https://github.com/google/CodeCity/blob/master/server/code.js
|
|
*
|
|
* @param {string} str The string to convert.
|
|
* @return {string} The value s as a eval-able string literal.
|
|
*/
|
|
function quote(str) {
|
|
/* eslint-disable no-control-regex, no-multi-spaces */
|
|
/** Regexp for characters to be escaped in a single-quoted string. */
|
|
const singleRE = /[\x00-\x1f\\\u2028\u2029']/g;
|
|
|
|
/** Map of control character replacements. */
|
|
const replacements = {
|
|
'\x00': '\\0', '\x01': '\\x01', '\x02': '\\x02', '\x03': '\\x03',
|
|
'\x04': '\\x04', '\x05': '\\x05', '\x06': '\\x06', '\x07': '\\x07',
|
|
'\x08': '\\b', '\x09': '\\t', '\x0a': '\\n', '\x0b': '\\v',
|
|
'\x0c': '\\f', '\x0d': '\\r', '\x0e': '\\x0e', '\x0f': '\\x0f',
|
|
'"': '\\"', "'": "\\'", '\\': '\\\\',
|
|
'\u2028': '\\u2028', '\u2029': '\\u2029',
|
|
};
|
|
/* eslint-enable no-control-regex, no-multi-spaces */
|
|
|
|
/**
|
|
* Replacer function.
|
|
* @param {string} c Single UTF-16 code unit ("character") string to
|
|
* be replaced.
|
|
* @return {string} Multi-character string containing escaped
|
|
* representation of c.
|
|
*/
|
|
function replace(c) {
|
|
return replacements[c];
|
|
}
|
|
|
|
return "'" + str.replace(singleRE, replace) + "'";
|
|
}
|
|
})();
|