mirror of
https://github.com/google/blockly.git
synced 2026-01-04 07:30:08 +01:00
141 lines
4.8 KiB
JavaScript
141 lines
4.8 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2023 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @fileoverview Helper functions for loading Blockly in tests.
|
|
*
|
|
* Used by playgrounds and test harnesses, both directly and via the
|
|
* shims generated by the buildShims function in
|
|
* scripts/gulpfiles/build_tasks.js.
|
|
*
|
|
*/
|
|
|
|
if (typeof window !== 'object') {
|
|
// Not running in a browser. Maybe we wish to support this?
|
|
// blockly_uncompressed formerly supported node.js, 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 without window is not supported.');
|
|
}
|
|
|
|
/**
|
|
* 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 and the 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.
|
|
*
|
|
* The formulation given here will work so long as top-level page is loaded from
|
|
* somewhere in tests/.
|
|
*/
|
|
export const ROOT = window.location.href.replace(/\/tests\/.*$/, '/');
|
|
|
|
/**
|
|
* Decide whether to use compressed mode or not.
|
|
*
|
|
* Honours a "?compressed=" query parameter if present; otherwise uses
|
|
* uncompressed for when loading from local machine and compressed
|
|
* otherwise. See issue #5557 for additional background.
|
|
*
|
|
* @return {boolean} True if should load in compressed mode.
|
|
*/
|
|
function compressed() {
|
|
const param = location.search.match(/compressed=([^&]+)/)?.[1];
|
|
if (param) {
|
|
if (['y', 'yes', 'true', '1'].includes(param)) return true;
|
|
if (['n', 'no', 'false', '0'].includes(param)) return false;
|
|
console.error(`Unrecognised compressed parameter "${param}"`);
|
|
}
|
|
|
|
const LOCALHOSTS = ['localhost', '127.0.0.1', '[::1]'];
|
|
const isLocalhost = LOCALHOSTS.includes(location.hostname);
|
|
const isFileUrl = location.origin === 'file://';
|
|
return !(isLocalhost || isFileUrl);
|
|
}
|
|
|
|
/** @type {boolean} Load in compressed mode. */
|
|
export const COMPRESSED = compressed();
|
|
|
|
/**
|
|
* Load a chunk, either by importing its ESM entrypoint or loading the
|
|
* compressed chunk script.
|
|
*
|
|
* When loading in uncompressed mode, if scriptExports is a simple
|
|
* variable name (e.g. 'Blockly') then globalThis[scriptExports] will
|
|
* be set to the chunk's Module object. This attempts to provide
|
|
* backward compatibility with loading the compressed chunk as a
|
|
* script, where this is done by the compressed chunk's UMD wrapper.
|
|
* The compatibility is not complete, however: since Module objects
|
|
* are immutable, it is not possible to set (e.g.)
|
|
* Blockly.libraryBlocks.
|
|
*
|
|
* The intention is to allow the chunk to be accessed either via
|
|
* the returned Module / exports object (preferred) or via the global
|
|
* scope (needed by dev-tools) regardless of whether it was loaded
|
|
* compressed or uncompressed.
|
|
*
|
|
* Paths should be relative to the repository root.
|
|
*
|
|
* @param {string} modulePath Path to the chunk's ESM entry point.
|
|
* @param {string} scriptPath Path to the chunk's _compressed.js file.
|
|
* @param {string} scriptExports The global variable name (or
|
|
* dotted-identifier path relative from a global variable) in
|
|
* which the compressed chunk's UMD wrapper will save the module's
|
|
* exports object. Should be the same as the correspodning
|
|
* chunks[].scriptExport property in
|
|
* scripts/gulpfiles/build_tasks.js.
|
|
* @return {Module} The module's Module or exports object.
|
|
*/
|
|
export async function loadChunk(modulePath, scriptPath, scriptExports) {
|
|
if (COMPRESSED) {
|
|
await loadScript(`${ROOT}${scriptPath}`);
|
|
return get(scriptExports);
|
|
} else {
|
|
const exports = await import(`../../${modulePath}`);
|
|
if (!scriptExports.includes('.')) globalThis[scriptExports] = exports;
|
|
return exports;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load a given URL using a <script> tag.
|
|
*
|
|
* Unlike document.write('<script src=...>'), this is safe to use even
|
|
* after page loading is complete.
|
|
*
|
|
* @param {string} src The src attribute for the <script> tag.
|
|
* @return {Promise} A promise that resolves (or rejects) once the
|
|
* script is loaded.
|
|
*/
|
|
export function loadScript(src) {
|
|
return new Promise((resolve, reject) => {
|
|
const script = document.createElement('script');
|
|
script.src = src;
|
|
script.onload = resolve;
|
|
script.onerror = reject;
|
|
document.getElementsByTagName('head')[0].appendChild(script);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get the value referred to by a dotted-identifier path
|
|
* (e.g. foo.bar.baz). Throws TypeError if path is is not valid,
|
|
* i.e., if any component but the last does not exist).
|
|
*
|
|
* @param {string} path The path referring to the desired value.
|
|
* @return {string|null} The value referred to.
|
|
*/
|
|
function get(path) {
|
|
let obj = globalThis;
|
|
for (const part of path.split('.')) {
|
|
obj = obj[part];
|
|
}
|
|
return obj;
|
|
}
|