summaryrefslogtreecommitdiff
path: root/node_modules/node-retrieve-globals
diff options
context:
space:
mode:
authorShipwreckt <me@shipwreckt.co.uk>2025-10-31 20:02:14 +0000
committerShipwreckt <me@shipwreckt.co.uk>2025-10-31 20:02:14 +0000
commit7a52ddeba2a68388b544f529d2d92104420f77b0 (patch)
tree15ddd47457a2cb4a96060747437d36474e4f6b4e /node_modules/node-retrieve-globals
parent53d6ae2b5568437afa5e4995580a3fb679b7b91b (diff)
Changed from static to 11ty!
Diffstat (limited to 'node_modules/node-retrieve-globals')
-rw-r--r--node_modules/node-retrieve-globals/LICENSE21
-rw-r--r--node_modules/node-retrieve-globals/README.md91
-rw-r--r--node_modules/node-retrieve-globals/package.json30
-rw-r--r--node_modules/node-retrieve-globals/retrieveGlobals.js376
-rw-r--r--node_modules/node-retrieve-globals/util/getWorkingDirectory.js18
-rw-r--r--node_modules/node-retrieve-globals/util/vmModules.js23
6 files changed, 559 insertions, 0 deletions
diff --git a/node_modules/node-retrieve-globals/LICENSE b/node_modules/node-retrieve-globals/LICENSE
new file mode 100644
index 0000000..8706985
--- /dev/null
+++ b/node_modules/node-retrieve-globals/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Zach Leatherman @zachleat
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/node_modules/node-retrieve-globals/README.md b/node_modules/node-retrieve-globals/README.md
new file mode 100644
index 0000000..eaf8f1e
--- /dev/null
+++ b/node_modules/node-retrieve-globals/README.md
@@ -0,0 +1,91 @@
+# node-retrieve-globals
+
+Execute a string of JavaScript using Node.js and return the global variable values and functions.
+
+* Supported on Node.js 16 and newer.
+* Uses `var`, `let`, `const`, `function`, Array and Object [destructuring assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment).
+* Async-only as of v5.0.
+* Can return any valid JS data type (including functions).
+* Can provide an external data object as context to the local execution scope
+* Transforms ESM import statements to work with current CommonJS limitations in Node’s `vm`.
+* Uses [Node’s `vm` module to execute JavaScript](https://nodejs.org/api/vm.html#vmruninthiscontextcode-options)
+ * ⚠️ The `node:vm` module is not a security mechanism. Do not use it to run untrusted code.
+ * `codeGeneration` (e.g. `eval`) is disabled by default; use `setCreateContextOptions({codeGeneration: { strings: true, wasm: true } })` to re-enable.
+ * Works _with or without_ `--experimental-vm-modules` flag (for `vm.Module` support). _(v5.0.0 and newer)_
+ * Future-friendly feature tests for when `vm.Module` is stable and `--experimental-vm-modules` is no longer necessary. _(v5.0.0 and newer)_
+* In use on:
+ * [JavaScript in Eleventy Front Matter](https://www.11ty.dev/docs/data-frontmatter-customize/#example-use-javascript-in-your-front-matter) (and [Demo](https://github.com/11ty/demo-eleventy-js-front-matter))
+ * [WebC’s `<script webc:setup>`](https://www.11ty.dev/docs/languages/webc/#using-java-script-to-setup-your-component)
+
+## Installation
+
+Available on [npm](https://www.npmjs.com/package/node-retrieve-globals)
+
+```
+npm install node-retrieve-globals
+```
+
+## Usage
+
+Works from Node.js with ESM and CommonJS:
+
+```js
+import { RetrieveGlobals } from "node-retrieve-globals";
+// const { RetrieveGlobals } = await import("node-retrieve-globals");
+```
+
+And then:
+
+```js
+let code = `var a = 1;
+const b = "hello";
+
+function hello() {}`;
+
+let vm = new RetrieveGlobals(code);
+
+await vm.getGlobalContext();
+```
+
+Returns:
+
+```js
+{ a: 1, b: "hello", hello: function hello() {} }
+```
+
+### Pass in your own Data and reference it in the JavaScript code
+
+```js
+let code = `let ref = myData;`;
+
+let vm = new RetrieveGlobals(code);
+
+await vm.getGlobalContext({ myData: "hello" });
+```
+
+Returns:
+
+```js
+{ ref: "hello" }
+```
+
+### Advanced options
+
+```js
+// Defaults shown
+let options = {
+ reuseGlobal: false, // re-use Node.js `global`, important if you want `console.log` to log to your console as expected.
+ dynamicImport: false, // allows `import()`
+ addRequire: false, // allows `require()`
+ experimentalModuleApi: false, // uses Module#_compile instead of `vm` (you probably don’t want this and it is bypassed by default when vm.Module is supported)
+};
+
+await vm.getGlobalContext({}, options);
+```
+
+## Changelog
+
+* `v6.0.0` Changes `import` and `require` to be project relative (not relative to this package on the file system).
+* `v5.0.0` Removes sync API, swap to async-only. Better compatibility with `--experimental-vm-modules` Node flag.
+* `v4.0.0` Swap to use `Module._compile` as a workaround for #2 (Node regression with experimental modules API in Node v20.10+)
+* `v3.0.0` ESM-only package. Node 16+ \ No newline at end of file
diff --git a/node_modules/node-retrieve-globals/package.json b/node_modules/node-retrieve-globals/package.json
new file mode 100644
index 0000000..b882306
--- /dev/null
+++ b/node_modules/node-retrieve-globals/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "node-retrieve-globals",
+ "version": "6.0.1",
+ "description": "Execute a string of JavaScript using Node.js and return the global variable values and functions.",
+ "type": "module",
+ "main": "retrieveGlobals.js",
+ "scripts": {
+ "test": "npx ava && cross-env NODE_OPTIONS='--experimental-vm-modules' npx ava"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/zachleat/node-retrieve-globals.git"
+ },
+ "author": {
+ "name": "Zach Leatherman",
+ "email": "zachleatherman@gmail.com",
+ "url": "https://zachleat.com/"
+ },
+ "license": "MIT",
+ "devDependencies": {
+ "@zachleat/noop": "^1.0.4",
+ "ava": "^6.2.0",
+ "cross-env": "^7.0.3"
+ },
+ "dependencies": {
+ "acorn": "^8.14.1",
+ "acorn-walk": "^8.3.4",
+ "esm-import-transformer": "^3.0.3"
+ }
+}
diff --git a/node_modules/node-retrieve-globals/retrieveGlobals.js b/node_modules/node-retrieve-globals/retrieveGlobals.js
new file mode 100644
index 0000000..64571a8
--- /dev/null
+++ b/node_modules/node-retrieve-globals/retrieveGlobals.js
@@ -0,0 +1,376 @@
+import vm from "vm";
+import * as acorn from "acorn";
+import * as walk from "acorn-walk";
+import { ImportTransformer } from "esm-import-transformer";
+import { createRequire, Module } from "module";
+
+import { getWorkingDirectory } from "./util/getWorkingDirectory.js";
+import { isSupported } from "./util/vmModules.js";
+
+const IS_VM_MODULES_SUPPORTED = isSupported();
+
+// `import` and `require` should both be relative to working directory (not this file)
+const WORKING_DIRECTORY = getWorkingDirectory();
+
+// TODO (feature) option to change `require` home base
+const customRequire = createRequire(WORKING_DIRECTORY);
+
+class RetrieveGlobals {
+ constructor(code, options) {
+ this.originalCode = code;
+
+ // backwards compat
+ if(typeof options === "string") {
+ options = {
+ filePath: options
+ };
+ }
+
+ this.options = Object.assign({
+ filePath: null,
+ transformEsmImports: false,
+ }, options);
+
+ if(IS_VM_MODULES_SUPPORTED) {
+ // Override: no code transformations if vm.Module works
+ this.options.transformEsmImports = false;
+ }
+
+ // set defaults
+ let acornOptions = {};
+ if(IS_VM_MODULES_SUPPORTED || this.options.transformEsmImports) {
+ acornOptions.sourceType = "module";
+ }
+
+ this.setAcornOptions(acornOptions);
+ this.setCreateContextOptions();
+
+ // transform `import ___ from ___` to `const ___ = await import(___)` to emulate *some* import syntax.
+ // Doesn’t currently work with aliases (mod as name) or namespaced imports (* as name).
+ if(this.options.transformEsmImports) {
+ this.code = this.transformer.transformToDynamicImport();
+ } else {
+ this.code = this.originalCode;
+ }
+ }
+
+ get transformer() {
+ if(!this._transformer) {
+ this._transformer = new ImportTransformer(this.originalCode);
+ }
+ return this._transformer;
+ }
+
+ setAcornOptions(acornOptions) {
+ this.acornOptions = Object.assign({
+ ecmaVersion: "latest",
+ }, acornOptions );
+ }
+
+ setCreateContextOptions(contextOptions) {
+ this.createContextOptions = Object.assign({
+ codeGeneration: {
+ strings: false,
+ wasm: false,
+ }
+ }, contextOptions );
+ }
+
+ static _getProxiedContext(context = {}, options = {}) {
+ return new Proxy(context, {
+ get(target, propertyName) {
+ if(Reflect.has(target, propertyName)) {
+ return Reflect.get(target, propertyName);
+ }
+
+ if(options.reuseGlobal && Reflect.has(global, propertyName)) {
+ return global[propertyName];
+ }
+ if(options.addRequire && propertyName === "require") {
+ return customRequire;
+ }
+ }
+ });
+ }
+
+ // We prune function and variable declarations that aren’t globally declared
+ // (our acorn walker could be improved to skip non-global declarations, but this method is easier for now)
+ static _getGlobalVariablesReturnString(names, mode = "cjs") {
+ let s = [`let __globals = {};`];
+ for(let name of names) {
+ s.push(`if( typeof ${name} !== "undefined") { __globals.${name} = ${name}; }`);
+ }
+ return `${s.join("\n")};${mode === "esm" ? "\nexport default __globals;" : "return __globals;"}`
+ }
+
+ _setContextPrototype(context) {
+ // Context will fail isPlainObject and won’t be merged in the data cascade properly without this prototype set
+ // See https://github.com/11ty/eleventy-utils/blob/main/src/IsPlainObject.js
+ if(!context || typeof context !== "object" || Array.isArray(context)) {
+ return;
+ }
+ if(context instanceof Date) {
+ return;
+ }
+
+ if(!Object.getPrototypeOf(context).isPrototypeOf(Object.create({}))) {
+ Object.setPrototypeOf(context, Object.prototype);
+ // Go deep
+ for(let key in context) {
+ this._setContextPrototype(context[key]);
+ }
+ }
+ }
+
+ _getCode(code, options) {
+ let { async: isAsync, globalNames, experimentalModuleApi, data } = Object.assign({
+ async: true
+ }, options);
+
+ if(IS_VM_MODULES_SUPPORTED) {
+ return `${code}
+
+${globalNames ? RetrieveGlobals._getGlobalVariablesReturnString(globalNames, "esm") : ""}`;
+ }
+
+ let prefix = [];
+ let argKeys = "";
+ let argValues = "";
+
+ // Don’t use this when vm.Module is stable (or if the code doesn’t have any imports!)
+ if(experimentalModuleApi) {
+ prefix = "module.exports = ";
+
+ if(typeof data === "object") {
+ let dataKeys = Object.keys(data);
+ if(dataKeys) {
+ argKeys = `{${dataKeys.join(",")}}`;
+ argValues = JSON.stringify(data, function replacer(key, value) {
+ if(typeof value === "function") {
+ throw new Error(`When using \`experimentalModuleApi\`, context data must be JSON.stringify friendly. The "${key}" property was type \`function\`.`);
+ }
+ return value;
+ });
+ }
+ }
+ }
+
+ return `${prefix}(${isAsync ? "async " : ""}function(${argKeys}) {
+ ${code}
+ ${globalNames ? RetrieveGlobals._getGlobalVariablesReturnString(globalNames, "cjs") : ""}
+})(${argValues});`;
+ }
+
+ getGlobalNames(parsedAst) {
+ let globalNames = new Set();
+
+ let types = {
+ FunctionDeclaration(node) {
+ globalNames.add(node.id.name);
+ },
+ VariableDeclarator(node) {
+ // destructuring assignment Array
+ if(node.id.type === "ArrayPattern") {
+ for(let prop of node.id.elements) {
+ if(prop.type === "Identifier") {
+ globalNames.add(prop.name);
+ }
+ }
+ } else if(node.id.type === "ObjectPattern") {
+ // destructuring assignment Object
+ for(let prop of node.id.properties) {
+ if(prop.type === "Property") {
+ globalNames.add(prop.value.name);
+ }
+ }
+ } else if(node.id.name) {
+ globalNames.add(node.id.name);
+ }
+ },
+ // if imports aren’t being transformed to variables assignment, we need those too
+ ImportSpecifier(node) {
+ globalNames.add(node.imported.name);
+ }
+ };
+
+ walk.simple(parsedAst, types);
+
+ return globalNames;
+ }
+
+ _getParseError(code, err) {
+ // Acorn parsing error on script
+ let metadata = [];
+ if(this.options.filePath) {
+ metadata.push(`file: ${this.options.filePath}`);
+ }
+ if(err?.loc?.line) {
+ metadata.push(`line: ${err.loc.line}`);
+ }
+ if(err?.loc?.column) {
+ metadata.push(`column: ${err.loc.column}`);
+ }
+
+ return new Error(`Had trouble parsing with "acorn"${metadata.length ? ` (${metadata.join(", ")})` : ""}:
+Message: ${err.message}
+
+${code}`);
+ }
+
+ async _getGlobalContext(data, options) {
+ let {
+ async: isAsync,
+ reuseGlobal,
+ dynamicImport,
+ addRequire,
+ experimentalModuleApi,
+ } = Object.assign({
+ // defaults
+ async: true,
+
+ reuseGlobal: false,
+
+ // adds support for `require`
+ addRequire: false,
+
+ // allows dynamic import in `vm` (requires --experimental-vm-modules in Node v20.10+)
+ // https://github.com/nodejs/node/issues/51154
+ // TODO Another workaround possibility: We could use `import` outside of `vm` and inject the dependencies into context `data`
+ dynamicImport: false,
+
+ // Use Module._compile instead of vm
+ // Workaround for: https://github.com/zachleat/node-retrieve-globals/issues/2
+ // Warning: This method requires input `data` to be JSON stringify friendly.
+ // Don’t use this if vm.Module is supported
+ // Don’t use this if the code does not contain `import`s
+ experimentalModuleApi: !IS_VM_MODULES_SUPPORTED && this.transformer.hasImports(),
+ }, options);
+
+ if(IS_VM_MODULES_SUPPORTED) {
+ // Override: don’t use this when modules are allowed.
+ experimentalModuleApi = false;
+ }
+
+ // These options are already supported by Module._compile
+ if(experimentalModuleApi) {
+ addRequire = false;
+ dynamicImport = false;
+ }
+
+ if(reuseGlobal || addRequire) {
+ // Re-use the parent `global` https://nodejs.org/api/globals.html
+ data = RetrieveGlobals._getProxiedContext(data || {}, {
+ reuseGlobal,
+ addRequire,
+ });
+ }
+
+ if(!data) {
+ data = {};
+ }
+
+ let context;
+ if(experimentalModuleApi || vm.isContext(data)) {
+ context = data;
+ } else {
+ context = vm.createContext(data, this.createContextOptions);
+ }
+
+ let parseCode;
+ let globalNames;
+
+ try {
+ parseCode = this._getCode(this.code, {
+ async: isAsync,
+ });
+
+ let parsedAst = acorn.parse(parseCode, this.acornOptions);
+ globalNames = this.getGlobalNames(parsedAst);
+ } catch(e) {
+ throw this._getParseError(parseCode, e);
+ }
+
+ try {
+ let execCode = this._getCode(this.code, {
+ async: isAsync,
+ globalNames,
+ experimentalModuleApi,
+ data: context,
+ });
+
+ if(experimentalModuleApi) {
+ let m = new Module();
+ m._compile(execCode, WORKING_DIRECTORY);
+ return m.exports;
+ }
+
+ let execOptions = {};
+ if(dynamicImport) {
+ // Warning: this option is part of the experimental modules API
+ execOptions.importModuleDynamically = (specifier) => import(specifier);
+ }
+
+ if(IS_VM_MODULES_SUPPORTED) {
+ // options.initializeImportMeta
+ let m = new vm.SourceTextModule(execCode, {
+ context,
+ initializeImportMeta: (meta, module) => {
+ meta.url = this.options.filePath || WORKING_DIRECTORY || module.identifier;
+ },
+ ...execOptions,
+ });
+
+ // Thank you! https://stackoverflow.com/a/73282303/16711
+ await m.link(async (specifier, referencingModule) => {
+ const mod = await import(specifier);
+ const exportNames = Object.keys(mod);
+ return new vm.SyntheticModule(
+ exportNames,
+ function () {
+ exportNames.forEach(key => {
+ this.setExport(key, mod[key])
+ });
+ },
+ {
+ identifier: specifier,
+ context: referencingModule.context
+ }
+ );
+ });
+
+ await m.evaluate();
+
+ // TODO (feature) incorporate other esm `exports` here
+ return m.namespace.default;
+ }
+
+ return vm.runInContext(execCode, context, execOptions);
+ } catch(e) {
+ let type = "cjs";
+ if(IS_VM_MODULES_SUPPORTED) {
+ type = "esm";
+ } else if(experimentalModuleApi) {
+ type = "cjs-experimental";
+ }
+
+ throw new Error(`Had trouble executing Node script (type: ${type}):
+Message: ${e.message}
+
+${this.code}`);
+ }
+ }
+
+ async getGlobalContext(data, options) {
+ let ret = await this._getGlobalContext(data, Object.assign({
+ // whether or not the target code is executed asynchronously
+ // note that vm.Module will always be async-friendly
+ async: true,
+ }, options));
+
+ this._setContextPrototype(ret);
+
+ return ret;
+ }
+}
+
+export { RetrieveGlobals };
diff --git a/node_modules/node-retrieve-globals/util/getWorkingDirectory.js b/node_modules/node-retrieve-globals/util/getWorkingDirectory.js
new file mode 100644
index 0000000..2231341
--- /dev/null
+++ b/node_modules/node-retrieve-globals/util/getWorkingDirectory.js
@@ -0,0 +1,18 @@
+import path from "node:path";
+import { pathToFileURL } from "node:url";
+
+function addTrailingSlash(path) {
+ if(path.endsWith("/")) {
+ return path;
+ }
+ return path + "/";
+}
+
+
+function getWorkingDirectory() {
+ // Trailing slash required
+ // `import` and `require` should both be relative to working directory (not this file)
+ return addTrailingSlash(pathToFileURL(path.resolve(".")).toString());
+}
+
+export { getWorkingDirectory }; \ No newline at end of file
diff --git a/node_modules/node-retrieve-globals/util/vmModules.js b/node_modules/node-retrieve-globals/util/vmModules.js
new file mode 100644
index 0000000..7643eff
--- /dev/null
+++ b/node_modules/node-retrieve-globals/util/vmModules.js
@@ -0,0 +1,23 @@
+import vm from "vm";
+
+function isSupported() {
+ // node --experimental-vm-modules …
+ if(process.execArgv.find(entry => entry == "--experimental-vm-modules")) {
+ return true;
+ }
+ // NODE_OPTIONS='--experimental-vm-modules' node …
+ if((process.env?.NODE_OPTIONS || "").split(" ").find(entry => entry == "--experimental-vm-modules")) {
+ return true;
+ }
+
+ // Feature test for a future when --experimental-vm-modules is not needed
+ // and vm.Module is stable:
+ try {
+ new vm.SourceTextModule(`/* hi */`);
+ return true;
+ } catch(e) {}
+
+ return false;
+}
+
+export { isSupported }; \ No newline at end of file