mirror of
https://github.com/google/blockly.git
synced 2026-01-07 00:50:27 +01:00
refactor(tests): Use goog.bootstrap to allow loading ES modules in playground (#5931)
* chore(deps): Update closure/goog/base.js, add goog.js
* Update base.js from the latest version (20220104.0.0).
* Also copy over goog.js, which provides access to asuitable subset
of goog.* via an importable module).
* refactor(tests): Have playground.html load Blockly as a module
N.B.:
* We still need a preparation step, in order to load base.js and
deps.js via <script> tags in uncompiled mode; in compiled mode
it will instead load all the *_compressed.js files via <script>
tags.
Acess to the Blockly object is via:
import Blockly from './playgrounds/blockly.mjs';
(N.B: no "* as", since blockly.mjs has only a default export.)
* There remain two serious defects when running in uncompiled mode:
* It does not attempt to load msg/messages.js, causing startup to
fail.
* Module loading only works if there are no ES Modules; if there
are, something goes wrong with base.js's attempt to sequence
module loads causing goog.modules that import ES modules to get
a null exports object for that import. X-(
* fix(tests): Have playground.html load messages.js before generators
This fixes the issue caused by missing messages when loading
the generators.
* fix(tests): Move bootsrap calls to prepare.js
Move the calls to goog.bootstrap from blockly.mjs to prepare.mjs.
This is needed to work around a bug in the Cosure Library debug
loader (https://github.com/google/closure-library/issues/1152).
This gets a bit ugly because most of the code has to go in a
<script> (because it needs goog.bootstrap, which was loaded by
an earlier <script> tag).
* fix(documentation): Minor comment corrections for PR #5931
This commit is contained in:
committed by
GitHub
parent
4e64d0eba4
commit
b1f0a6a867
@@ -91,7 +91,14 @@ goog.global.CLOSURE_UNCOMPILED_DEFINES;
|
||||
* var CLOSURE_DEFINES = {'goog.DEBUG': false} ;
|
||||
* </pre>
|
||||
*
|
||||
* @type {Object<string, (string|number|boolean)>|undefined}
|
||||
* Currently the Closure Compiler will only recognize very simple definitions of
|
||||
* this value when looking for values to apply to compiled code and ignore all
|
||||
* other references. Specifically, it looks the value defined at the variable
|
||||
* declaration, as with the example above.
|
||||
*
|
||||
* TODO(user): Improve the recognized definitions.
|
||||
*
|
||||
* @type {!Object<string, (string|number|boolean)>|null|undefined}
|
||||
*/
|
||||
goog.global.CLOSURE_DEFINES;
|
||||
|
||||
@@ -3175,23 +3182,10 @@ if (!COMPILED && goog.DEPENDENCIES_ENABLED) {
|
||||
scriptEl.nonce = nonce;
|
||||
}
|
||||
|
||||
if (goog.DebugLoader_.IS_OLD_IE_) {
|
||||
// Execution order is not guaranteed on old IE, halt loading and write
|
||||
// these scripts one at a time, after each loads.
|
||||
controller.pause();
|
||||
scriptEl.onreadystatechange = function() {
|
||||
if (scriptEl.readyState == 'loaded' ||
|
||||
scriptEl.readyState == 'complete') {
|
||||
controller.loaded();
|
||||
controller.resume();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
scriptEl.onload = function() {
|
||||
scriptEl.onload = null;
|
||||
controller.loaded();
|
||||
};
|
||||
}
|
||||
scriptEl.onload = function() {
|
||||
scriptEl.onload = null;
|
||||
controller.loaded();
|
||||
};
|
||||
|
||||
scriptEl.src = goog.TRUSTED_TYPES_POLICY_ ?
|
||||
goog.TRUSTED_TYPES_POLICY_.createScriptURL(this.path) :
|
||||
@@ -3502,13 +3496,6 @@ if (!COMPILED && goog.DEPENDENCIES_ENABLED) {
|
||||
// If one thing is pending it is this.
|
||||
var anythingElsePending = controller.pending().length > 1;
|
||||
|
||||
// If anything else is loading we need to lazy load due to bugs in old IE.
|
||||
// Specifically script tags with src and script tags with contents could
|
||||
// execute out of order if document.write is used, so we cannot use
|
||||
// document.write. Do not pause here; it breaks old IE as well.
|
||||
var useOldIeWorkAround =
|
||||
anythingElsePending && goog.DebugLoader_.IS_OLD_IE_;
|
||||
|
||||
// Additionally if we are meant to defer scripts but the page is still
|
||||
// loading (e.g. an ES6 module is loading) then also defer. Or if we are
|
||||
// meant to defer and anything else is pending then defer (those may be
|
||||
@@ -3517,7 +3504,7 @@ if (!COMPILED && goog.DEPENDENCIES_ENABLED) {
|
||||
var needsAsyncLoading = goog.Dependency.defer_ &&
|
||||
(anythingElsePending || goog.isDocumentLoading_());
|
||||
|
||||
if (useOldIeWorkAround || needsAsyncLoading) {
|
||||
if (needsAsyncLoading) {
|
||||
// Note that we only defer when we have to rather than 100% of the time.
|
||||
// Always defering would work, but then in theory the order of
|
||||
// goog.require calls would then matter. We want to enforce that most of
|
||||
@@ -3561,8 +3548,7 @@ if (!COMPILED && goog.DEPENDENCIES_ENABLED) {
|
||||
};
|
||||
} else {
|
||||
// Always eval on old IE.
|
||||
if (goog.DebugLoader_.IS_OLD_IE_ || !goog.inHtmlDocument_() ||
|
||||
!goog.isDocumentLoading_()) {
|
||||
if (!goog.inHtmlDocument_() || !goog.isDocumentLoading_()) {
|
||||
load();
|
||||
} else {
|
||||
fetchInOwnScriptThenLoad();
|
||||
@@ -3706,15 +3692,6 @@ if (!COMPILED && goog.DEPENDENCIES_ENABLED) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether the browser is IE9 or earlier, which needs special handling
|
||||
* for deferred modules.
|
||||
* @const @private {boolean}
|
||||
*/
|
||||
goog.DebugLoader_.IS_OLD_IE_ = !!(
|
||||
!goog.global.atob && goog.global.document && goog.global.document['all']);
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} relPath
|
||||
* @param {!Array<string>|undefined} provides
|
||||
|
||||
99
closure/goog/goog.js
Normal file
99
closure/goog/goog.js
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright 2018 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview ES6 module that exports symbols from base.js so that ES6
|
||||
* modules do not need to use globals and so that is clear if a project is using
|
||||
* Closure's base.js file. It is also a subset of properties in base.js, meaning
|
||||
* it should be clearer what should not be used in ES6 modules
|
||||
* (goog.module/provide are not exported here, for example). Though that is not
|
||||
* to say that everything in this file should be used in an ES6 module; some
|
||||
* depreciated functions are exported to make migration easier (e.g.
|
||||
* goog.scope).
|
||||
*
|
||||
* Note that this does not load Closure's base.js file, it is still up to the
|
||||
* programmer to include it. Nor does the fact that this is an ES6 module mean
|
||||
* that projects no longer require deps.js files for debug loading - they do.
|
||||
* Closure will need to load your ES6 modules for you if you have any Closure
|
||||
* file (goog.provide/goog.module) dependencies, as they need to be available
|
||||
* before the ES6 module evaluates.
|
||||
*
|
||||
* Also note that this file has special compiler handling! It is okay to export
|
||||
* anything from this file, but the name also needs to exist on the global goog.
|
||||
* This special compiler pass enforces that you always import this file as
|
||||
* `import * as goog`, as many tools use regex based parsing to find
|
||||
* goog.require calls.
|
||||
*/
|
||||
|
||||
export const global = goog.global;
|
||||
export const require = goog.require;
|
||||
export const define = goog.define;
|
||||
export const DEBUG = goog.DEBUG;
|
||||
export const LOCALE = goog.LOCALE;
|
||||
export const TRUSTED_SITE = goog.TRUSTED_SITE;
|
||||
export const DISALLOW_TEST_ONLY_CODE = goog.DISALLOW_TEST_ONLY_CODE;
|
||||
export const getGoogModule = goog.module.get;
|
||||
export const setTestOnly = goog.setTestOnly;
|
||||
export const forwardDeclare = goog.forwardDeclare;
|
||||
export const getObjectByName = goog.getObjectByName;
|
||||
export const basePath = goog.basePath;
|
||||
export const addSingletonGetter = goog.addSingletonGetter;
|
||||
export const typeOf = goog.typeOf;
|
||||
export const isArrayLike = goog.isArrayLike;
|
||||
export const isDateLike = goog.isDateLike;
|
||||
export const isObject = goog.isObject;
|
||||
export const getUid = goog.getUid;
|
||||
export const hasUid = goog.hasUid;
|
||||
export const removeUid = goog.removeUid;
|
||||
export const mixin = goog.mixin;
|
||||
export const now = Date.now;
|
||||
export const globalEval = goog.globalEval;
|
||||
export const getCssName = goog.getCssName;
|
||||
export const setCssNameMapping = goog.setCssNameMapping;
|
||||
export const getMsg = goog.getMsg;
|
||||
export const getMsgWithFallback = goog.getMsgWithFallback;
|
||||
export const exportSymbol = goog.exportSymbol;
|
||||
export const exportProperty = goog.exportProperty;
|
||||
export const nullFunction = goog.nullFunction;
|
||||
export const abstractMethod = goog.abstractMethod;
|
||||
export const cloneObject = goog.cloneObject;
|
||||
export const bind = goog.bind;
|
||||
export const partial = goog.partial;
|
||||
export const inherits = goog.inherits;
|
||||
export const scope = goog.scope;
|
||||
export const defineClass = goog.defineClass;
|
||||
export const declareModuleId = goog.declareModuleId;
|
||||
|
||||
// Export select properties of module. Do not export the function itself or
|
||||
// goog.module.declareLegacyNamespace.
|
||||
export const module = {
|
||||
get: goog.module.get,
|
||||
};
|
||||
|
||||
// Omissions include:
|
||||
// goog.ENABLE_DEBUG_LOADER - define only used in base.
|
||||
// goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING - define only used in base.
|
||||
// goog.provide - ES6 modules do not provide anything.
|
||||
// goog.module - ES6 modules cannot be goog.modules.
|
||||
// goog.module.declareLegacyNamespace - ES6 modules cannot declare namespaces.
|
||||
// goog.addDependency - meant to only be used by dependency files.
|
||||
// goog.DEPENDENCIES_ENABLED - constant only used in base.
|
||||
// goog.TRANSPILE - define only used in base.
|
||||
// goog.TRANSPILER - define only used in base.
|
||||
// goog.loadModule - should not be called by any ES6 module; exists for
|
||||
// generated bundles.
|
||||
// goog.LOAD_MODULE_USING_EVAL - define only used in base.
|
||||
// goog.SEAL_MODULE_EXPORTS - define only used in base.
|
||||
// goog.DebugLoader - used rarely, only outside of compiled code.
|
||||
// goog.Transpiler - used rarely, only outside of compiled code.
|
||||
@@ -323,4 +323,5 @@ goog.addDependency('../../generators/python/variables.js', ['Blockly.Python.vari
|
||||
goog.addDependency('../../generators/python/variables_dynamic.js', ['Blockly.Python.variablesDynamic'], ['Blockly.Python', 'Blockly.Python.variables'], {'lang': 'es6', 'module': 'goog'});
|
||||
goog.addDependency('base.js', [], []);
|
||||
goog.addDependency('base_minimal.js', [], []);
|
||||
goog.addDependency('goog.js', [], [], {'lang': 'es6', 'module': 'es6'});
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
<title>Blockly Playground</title>
|
||||
|
||||
<!-- This script loads uncompressed when on localhost and loads compressed when it is being hosted.
|
||||
Please add any other dependencies to playgrounds/load_all.js.-->
|
||||
<script src="playgrounds/load_all.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
Please add any other dependencies to playgrounds/prepare.js.-->
|
||||
<script src="playgrounds/prepare.js"></script>
|
||||
<script type=module>
|
||||
import Blockly from './playgrounds/blockly.mjs';
|
||||
|
||||
const IS_UNCOMPRESSED = !!document.getElementById('blockly-uncompressed-script');
|
||||
const IS_UNCOMPRESSED = Boolean(window.BlocklyLoader); // See prepare.js
|
||||
var workspace = null;
|
||||
|
||||
function start() {
|
||||
@@ -381,6 +381,12 @@ var spaghettiXml = [
|
||||
'next': { }
|
||||
});
|
||||
|
||||
// Call start(). Because this <script> has type=module, it is
|
||||
// automatically deferred, so it will not be run until after the
|
||||
// document has been parsed, but before firing DOMContentLoaded.
|
||||
|
||||
start();
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -430,7 +436,7 @@ var spaghettiXml = [
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="start()">
|
||||
<body>
|
||||
|
||||
<div id="blocklyDiv"></div>
|
||||
|
||||
|
||||
38
tests/playgrounds/blockly.mjs
Normal file
38
tests/playgrounds/blockly.mjs
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2022 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Finishes loading Blockly and exports it as this
|
||||
* module's default export.
|
||||
*
|
||||
* It is exported as the default export to avoid having to
|
||||
* re-export each property on Blockly individually, because you
|
||||
* can't do:
|
||||
*
|
||||
* export * from <dynamically computed source>; // SYNTAX ERROR
|
||||
*
|
||||
* You must use a <script> tag to load prepare.js first, before
|
||||
* importing this module in a <script type=module> to obtain the
|
||||
* loaded value.
|
||||
*
|
||||
* See tests/playground.html for example usage.
|
||||
*/
|
||||
|
||||
let Blockly;
|
||||
|
||||
if (window.BlocklyLoader) {
|
||||
// Uncompiled mode. Use top-level await
|
||||
// (https://v8.dev/features/top-level-await) to block loading of
|
||||
// this module until goog.bootstrap()ping of Blockly is finished.
|
||||
Blockly = await window.BlocklyLoader;
|
||||
} else if (window.Blockly) {
|
||||
// Compiled mode. Retrieve the pre-installed Blockly global.
|
||||
Blockly = globalThis.Blockly;
|
||||
} else {
|
||||
throw new Error('neither window.Blockly nor window.BlocklyLoader found');
|
||||
}
|
||||
|
||||
export default Blockly;
|
||||
85
tests/playgrounds/prepare.js
Normal file
85
tests/playgrounds/prepare.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2021 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Load this file in a <script> tag to prepare for
|
||||
* importing Blockly into a web page.
|
||||
*
|
||||
* You must use a <script> tag to load this script first, then
|
||||
* import blockly.mjs in a <script type=module> to obtain the
|
||||
* loaded value.
|
||||
*
|
||||
* See tests/playground.html for example usage.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
(function() {
|
||||
// Decide whether we can load Blockly uncompiled, or must load the
|
||||
// compiled version. Please see issue #5557 for more information.
|
||||
const isIe = navigator.userAgent.indexOf('MSIE') !== -1 ||
|
||||
navigator.appVersion.indexOf('Trident/') > -1;
|
||||
const localhosts = ['localhost', '127.0.0.1', '[::1]'];
|
||||
|
||||
if (localhosts.includes(location.hostname) && !isIe) {
|
||||
// We can load Blockly in uncompiled mode.
|
||||
|
||||
// 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="../../closure/goog/base.js"></script>');
|
||||
// Load dependency graph info from test/deps.js. To update
|
||||
// deps.js, run `npm run build:deps`.
|
||||
document.write('<script src="../../tests/deps.js"></script>');
|
||||
|
||||
// Msg loading kludge. This should go away once #5409 and/or
|
||||
// #1895 are fixed.
|
||||
|
||||
// Load messages into a temporary Blockly.Msg object, deleting it
|
||||
// afterwards (after saving the messages!)
|
||||
window.Blockly = {Msg: Object.create(null)};
|
||||
document.write('<script src="../../msg/messages.js"></script>');
|
||||
document.write(`
|
||||
<script>
|
||||
window.BlocklyMsg = window.Blockly.Msg;
|
||||
delete window.Blockly;
|
||||
</script>`);
|
||||
|
||||
document.write(`
|
||||
<script>
|
||||
window.BlocklyLoader = new Promise((resolve, reject) => {
|
||||
goog.bootstrap(
|
||||
[
|
||||
'Blockly',
|
||||
'Blockly.blocks.all',
|
||||
'Blockly.Dart.all',
|
||||
'Blockly.JavaScript.all',
|
||||
'Blockly.Lua.all',
|
||||
'Blockly.PHP.all',
|
||||
'Blockly.Python.all',
|
||||
], resolve);
|
||||
}).then(() => {
|
||||
// Copy Messages from temporary Blockly.Msg object to the real one:
|
||||
Object.assign(goog.module.get('Blockly').Msg, window.BlocklyMsg);
|
||||
}).then(() => {
|
||||
return goog.module.get('Blockly');
|
||||
});
|
||||
</script>`);
|
||||
} else {
|
||||
// We need to load Blockly in compiled mode.
|
||||
|
||||
// Load blockly_compressed.js et al. using <script> tags.
|
||||
document.write('<script src="../../blockly_compressed.js"></script>');
|
||||
document.write('<script src="../../dart_compressed.js"></script>');
|
||||
document.write('<script src="../../javascript_compressed.js"></script>');
|
||||
document.write('<script src="../../lua_compressed.js"></script>');
|
||||
document.write('<script src="../../php_compressed.js"></script>');
|
||||
document.write('<script src="../../python_compressed.js"></script>');
|
||||
document.write('<script src="../../blocks_compressed.js"></script>');
|
||||
document.write('<script src="../../msg/messages.js"></script>');
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user