From 5b5a56586c21e0f6a4db0fd476170b3ab239de18 Mon Sep 17 00:00:00 2001 From: Christopher Allen Date: Mon, 14 Aug 2023 22:47:19 +0100 Subject: [PATCH] refactor(tests): Introduce loading shims, use in playgrounds (#7380) * refactor(build): Simplify implementation of posixPath * fix(closure): Make safe to import in node.js Make the implementation declareModlueId safe to import and execute in node.js, which does not provide a window global. (N.B. because this is an ESM and therefore automatically strict, using window?.goog?.declareModuleId doesn't work because window being undefined is an early error.) * feat(tests): Introduce chunk loading shims - Add a buildShims task to build_tasks.js that, for each chunk, creates a correspondingly-named build/.mjs that will either (in uncompressed mode) import and reexport that chunk's entry point module (e.g. core/blockly.js) or (in compressed mode) load dist/_compressed.js using a - - - - - - - - - -

Shared Procedures Playground

- -
-
- - diff --git a/tests/scripts/load.mjs b/tests/scripts/load.mjs new file mode 100644 index 000000000..728a4fd2c --- /dev/null +++ b/tests/scripts/load.mjs @@ -0,0 +1,140 @@ +/** + * @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 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