From 7a3da6792fc57dbc6e309a939c9f86879130a76b Mon Sep 17 00:00:00 2001 From: Tony Mobily Date: Sun, 24 Mar 2024 10:06:15 +0100 Subject: [PATCH 1/7] js-interpreter including ESM and in the npm ecosystem --- .gitignore | 3 ++ acorn.js | 27 +++++++---- createEsmFiles.js | 25 +++++++++++ index-esm.html | 112 ++++++++++++++++++++++++++++++++++++++++++++++ interpreter.js | 51 ++++++++++++++------- package.json | 25 +++++++++++ 6 files changed, 219 insertions(+), 24 deletions(-) create mode 100644 createEsmFiles.js create mode 100644 index-esm.html create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 900d891..6bd64f4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ test262-es5-tests/ test262.js /compiler.jar + +interpreter-esm.js +acorn-esm.js diff --git a/acorn.js b/acorn.js index c3a6931..66baab7 100644 --- a/acorn.js +++ b/acorn.js @@ -26,14 +26,25 @@ // [dammit]: acorn_loose.js // [walk]: util/walk.js -(function(root, mod) { - if (typeof exports === "object" && typeof module === "object") return mod(exports); // CommonJS - if (typeof define === "function" && define.amd) return define(["exports"], mod); // AMD - mod(root.acorn || (root.acorn = {})); // Plain browser env -})((typeof globalThis === 'undefined') ? this || window : globalThis, function(exports) { +var parse +var version + +(function (root, factory) { + if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { + // CommonJS. The commonJS module will export 'parse' and 'version' + factory(exports); + } else if (typeof asEsm !== 'undefined'){ + // ESM + factory({}) + } else { + // Browser globals. In this case, only window.acorn will be set, + // which will contain 'parse' and 'version' + factory(root.acorn || (root.acorn = {})); + } +}(typeof self !== 'undefined' ? self : this, function (exports) { "use strict"; - exports.version = "0.5.0"; + exports.version = version = "0.5.0"; // Plus additional edits marked with 'JS-Interpreter change' comments. // JS-Interpreter change: @@ -73,7 +84,7 @@ * @param {Object=} opts * @returns */ - exports.parse = function(inpt, opts) { + exports.parse = parse = function(inpt, opts) { input = String(inpt); inputLen = input.length; setOptions(opts); @@ -2280,4 +2291,4 @@ return finishNode(node, "Identifier"); } -}); +})) diff --git a/createEsmFiles.js b/createEsmFiles.js new file mode 100644 index 0000000..d48093b --- /dev/null +++ b/createEsmFiles.js @@ -0,0 +1,25 @@ +const fs = require('fs'); + +// File paths and content to add +const filesToModify = [ + { original: 'interpreter.js', new: 'interpreter-esm.js', insertionAnchor: 'var Interpreter', contentToAdd: "import { parse, version } from './acorn-esm.js';\nvar asEsm = true;\nexport { Interpreter, parse, version as acornVersion };\n\n" }, + { original: 'acorn.js', new: 'acorn-esm.js', insertionAnchor: 'var version', contentToAdd: "export { parse, version };\nvar asEsm = true;\n\n" } +]; + +// Loop through each file to modify +filesToModify.forEach(file => { + const originalContent = fs.readFileSync(file.original, 'utf8'); + const insertionIndex = originalContent.indexOf(file.insertionAnchor); + + if (insertionIndex !== -1) { + const insertionPoint = insertionIndex + file.insertionAnchor.length; + const newContent = originalContent.substring(0, insertionPoint) + '\n' + // Add newline character + file.contentToAdd + + originalContent.substring(insertionPoint); + + fs.writeFileSync(file.new, newContent); + console.log(`Modified ${file.original} and saved as ${file.new}`); + } else { + console.error(`Insertion anchor not found in ${file.original}`); + } +}); diff --git a/index-esm.html b/index-esm.html new file mode 100644 index 0000000..f013cb2 --- /dev/null +++ b/index-esm.html @@ -0,0 +1,112 @@ + + + + + JS-Interpreter Demo + + + + + +

JS-Interpreter Demo

+ +

Enter JavaScript code below, then click Parse. To execute, either + click Step repeatedly, or click Run once. + Open your browser's console for errors.

+


+ + + +

+ +

Read the JS-Interpreter documentation.

+

Get the source code.

+ + + diff --git a/interpreter.js b/interpreter.js index 3614dd0..530011d 100644 --- a/interpreter.js +++ b/interpreter.js @@ -4,6 +4,30 @@ * SPDX-License-Identifier: Apache-2.0 */ +var Interpreter + +(function (root, factory) { + if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { + // CommonJS + // + // A one-liner to test commonJS is to run node and feed this to it: + // int = require('./interpreter.js');i = new int.Interpreter('a = new String(); 4+4;');i.run();i.value; + debugger + var acorn = require('./acorn.js') + exports.parse = acorn.parse + exports.acornVersion = acorn.version + factory(exports); + } else if (typeof asEsm !== 'undefined'){ + // ESM + // No need to define acorn, since it will be defined in an injected line + factory({ parse, acornVersion: version }) + } else { + // Browser globals + // if (!root.acorn) throw new Error('Acorn needs to be in the `window` namespace') + factory(root); + } +}(typeof self !== 'undefined' ? self : this, function (exports) { + /** * @fileoverview Interpreting JavaScript in JavaScript. * @author fraser@google.com (Neil Fraser) @@ -18,7 +42,7 @@ * global scope object. * @constructor */ -var Interpreter = function(code, opt_initFunc) { +Interpreter = function(code, opt_initFunc) { if (typeof code === 'string') { code = this.parse_(code, 'code'); } @@ -200,13 +224,6 @@ Interpreter.vm = null; */ Interpreter.currentInterpreter_ = null; -/** - * The global object. Ideally use `globalThis`. Failing that try `this` or - * `window`. Other options to consider are `self` and `global`. - * Same logic as in Acorn. - */ -Interpreter.nativeGlobal = - (typeof globalThis === 'undefined') ? this || window : globalThis; /** * Code for executing regular expressions in a thread. @@ -364,7 +381,7 @@ Interpreter.prototype.parse_ = function(code, sourceFile) { options[name] = Interpreter.PARSE_OPTIONS[name]; } options.sourceFile = sourceFile; - return Interpreter.nativeGlobal.acorn.parse(code, options); + return exports.parse(code, options); }; /** @@ -1558,7 +1575,7 @@ Interpreter.prototype.initString = function(globalObject) { var wrapper; // String constructor. wrapper = function String(value) { - value = arguments.length ? Interpreter.nativeGlobal.String(value) : ''; + value = arguments.length ? String(value) : ''; if (thisInterpreter.calledWithNew()) { // Called as `new String()`. this.data = value; @@ -1804,7 +1821,7 @@ Interpreter.prototype.initBoolean = function(globalObject) { var wrapper; // Boolean constructor. wrapper = function Boolean(value) { - value = Interpreter.nativeGlobal.Boolean(value); + value = Boolean(value); if (thisInterpreter.calledWithNew()) { // Called as `new Boolean()`. this.data = value; @@ -1828,7 +1845,7 @@ Interpreter.prototype.initNumber = function(globalObject) { var wrapper; // Number constructor. wrapper = function Number(value) { - value = arguments.length ? Interpreter.nativeGlobal.Number(value) : 0; + value = arguments.length ? Number(value) : 0; if (thisInterpreter.calledWithNew()) { // Called as `new Number()`. this.data = value; @@ -1916,12 +1933,12 @@ Interpreter.prototype.initDate = function(globalObject) { if (!thisInterpreter.calledWithNew()) { // Called as `Date()`. // Calling Date() as a function returns a string, no arguments are heeded. - return Interpreter.nativeGlobal.Date(); + return Date(); } // Called as `new Date(...)`. var args = [null].concat(Array.from(arguments)); this.data = new (Function.prototype.bind.apply( - Interpreter.nativeGlobal.Date, args)); + Date, args)); return this; }; this.DATE = this.createNativeFunction(wrapper, true); @@ -2011,7 +2028,7 @@ Interpreter.prototype.initRegExp = function(globalObject) { 'Invalid regexp flag: ' + flags); } try { - var nativeRegExp = new Interpreter.nativeGlobal.RegExp(pattern, flags) + var nativeRegExp = new RegExp(pattern, flags) } catch (e) { // Throws if flags are repeated. thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR, e.message); @@ -4803,7 +4820,7 @@ Interpreter.prototype['stepWhileStatement'] = // Preserve top-level API functions from being pruned/renamed by JS compilers. // Add others as needed. -Interpreter.nativeGlobal['Interpreter'] = Interpreter; +exports.Interpreter = Interpreter; Interpreter.prototype['step'] = Interpreter.prototype.step; Interpreter.prototype['run'] = Interpreter.prototype.run; Interpreter.prototype['appendCode'] = Interpreter.prototype.appendCode; @@ -4824,3 +4841,5 @@ Interpreter.prototype['getStateStack'] = Interpreter.prototype.getStateStack; Interpreter.prototype['setStateStack'] = Interpreter.prototype.setStateStack; Interpreter['VALUE_IN_DESCRIPTOR'] = Interpreter.VALUE_IN_DESCRIPTOR; Interpreter['Status'] = Interpreter.Status; +})) + diff --git a/package.json b/package.json new file mode 100644 index 0000000..a9744aa --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "js-interpreter-esm", + "version": "1.0.0", + "description": "Neil Fraser's official JS-Interpreter", + "main": "js-interpreter.js", + "module": "js-interpreter-esm.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "postinstall": "node createEsmFiles.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/mobily-enterprises/JS-Interpreter-esm.git" + }, + "keywords": [ + "JS-Interpreter", + "acorn" + ], + "author": "Neil Fraser", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/mobily-enterprises/JS-Interpreter-esm/issues" + }, + "homepage": "https://github.com/mobily-enterprises/JS-Interpreter-esm#readme" +} From 1dab23a6a4534a4acc49e6f91e63219c7d187c77 Mon Sep 17 00:00:00 2001 From: Tony Mobily Date: Sun, 24 Mar 2024 10:38:27 +0100 Subject: [PATCH 2/7] Accidental debugger out; fixed one word in a comment --- interpreter.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interpreter.js b/interpreter.js index 530011d..6a38401 100644 --- a/interpreter.js +++ b/interpreter.js @@ -12,14 +12,13 @@ var Interpreter // // A one-liner to test commonJS is to run node and feed this to it: // int = require('./interpreter.js');i = new int.Interpreter('a = new String(); 4+4;');i.run();i.value; - debugger var acorn = require('./acorn.js') exports.parse = acorn.parse exports.acornVersion = acorn.version factory(exports); } else if (typeof asEsm !== 'undefined'){ // ESM - // No need to define acorn, since it will be defined in an injected line + // No need to define `parse`, since it will be defined in an injected line factory({ parse, acornVersion: version }) } else { // Browser globals From 35b48b813d680e21d62cac72bfd3718362dd5acf Mon Sep 17 00:00:00 2001 From: Tony Mobily Date: Sun, 24 Mar 2024 10:40:34 +0100 Subject: [PATCH 3/7] Correct URLs in package.json --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index a9744aa..56e9afd 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/mobily-enterprises/JS-Interpreter-esm.git" + "url": "git+https://github.com/NeilFraser/JS-Interpreter.git" }, "keywords": [ "JS-Interpreter", @@ -19,7 +19,7 @@ "author": "Neil Fraser", "license": "Apache-2.0", "bugs": { - "url": "https://github.com/mobily-enterprises/JS-Interpreter-esm/issues" + "url": "https://github.com/NeilFraser/JS-Interpreter/issues" }, - "homepage": "https://github.com/mobily-enterprises/JS-Interpreter-esm#readme" + "homepage": "https://github.com/NeilFraser/JS-Interpreter#readme" } From d9e81838ed027888e3c811713771e482e6e97674 Mon Sep 17 00:00:00 2001 From: Tony Mobily Date: Sun, 24 Mar 2024 10:42:31 +0100 Subject: [PATCH 4/7] Version bump for NPM --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 56e9afd..48df6fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "js-interpreter-esm", - "version": "1.0.0", + "version": "1.0.1", "description": "Neil Fraser's official JS-Interpreter", "main": "js-interpreter.js", "module": "js-interpreter-esm.js", From 0438121ebe5e9d645ce990defc89194777687cf9 Mon Sep 17 00:00:00 2001 From: Tony Mobily Date: Sun, 24 Mar 2024 10:49:34 +0100 Subject: [PATCH 5/7] Fixed entry points in package.json --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 48df6fe..1f04b3e 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "js-interpreter-esm", - "version": "1.0.1", + "version": "1.0.2", "description": "Neil Fraser's official JS-Interpreter", - "main": "js-interpreter.js", - "module": "js-interpreter-esm.js", + "main": "interpreter.js", + "module": "interpreter-esm.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "postinstall": "node createEsmFiles.js" From 3375594b27effe5e962ed8365290f84c1f2f1ec5 Mon Sep 17 00:00:00 2001 From: Tony Mobily Date: Mon, 25 Mar 2024 08:41:30 +0100 Subject: [PATCH 6/7] Important fixes -- yes, I know understand why we were calling Interpreter.nativeGlobal.Number and not Number... --- interpreter.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/interpreter.js b/interpreter.js index 6a38401..2d12453 100644 --- a/interpreter.js +++ b/interpreter.js @@ -1573,8 +1573,9 @@ Interpreter.prototype.initString = function(globalObject) { var thisInterpreter = this; var wrapper; // String constructor. + var OriginalString = String wrapper = function String(value) { - value = arguments.length ? String(value) : ''; + value = arguments.length ? OriginalString(value) : ''; if (thisInterpreter.calledWithNew()) { // Called as `new String()`. this.data = value; @@ -1819,8 +1820,9 @@ Interpreter.prototype.initBoolean = function(globalObject) { var thisInterpreter = this; var wrapper; // Boolean constructor. + var OriginalBoolean = Boolean wrapper = function Boolean(value) { - value = Boolean(value); + value = OriginalBoolean(value); if (thisInterpreter.calledWithNew()) { // Called as `new Boolean()`. this.data = value; @@ -1843,8 +1845,9 @@ Interpreter.prototype.initNumber = function(globalObject) { var thisInterpreter = this; var wrapper; // Number constructor. + var OriginalNumber = Number wrapper = function Number(value) { - value = arguments.length ? Number(value) : 0; + value = arguments.length ? OriginalNumber(value) : 0; if (thisInterpreter.calledWithNew()) { // Called as `new Number()`. this.data = value; @@ -1927,17 +1930,18 @@ Interpreter.prototype.initNumber = function(globalObject) { Interpreter.prototype.initDate = function(globalObject) { var thisInterpreter = this; var wrapper; + var OriginalDate = Date // Date constructor. wrapper = function Date(_value, var_args) { if (!thisInterpreter.calledWithNew()) { // Called as `Date()`. // Calling Date() as a function returns a string, no arguments are heeded. - return Date(); + return OriginalDate(); } // Called as `new Date(...)`. var args = [null].concat(Array.from(arguments)); this.data = new (Function.prototype.bind.apply( - Date, args)); + OriginalDate, args)); return this; }; this.DATE = this.createNativeFunction(wrapper, true); @@ -2006,6 +2010,7 @@ Interpreter.prototype.initRegExp = function(globalObject) { var thisInterpreter = this; var wrapper; // RegExp constructor. + var OriginalRegExp = RegExp wrapper = function RegExp(pattern, flags) { if (thisInterpreter.calledWithNew()) { // Called as `new RegExp()`. @@ -2027,7 +2032,7 @@ Interpreter.prototype.initRegExp = function(globalObject) { 'Invalid regexp flag: ' + flags); } try { - var nativeRegExp = new RegExp(pattern, flags) + var nativeRegExp = new OriginalRegExp(pattern, flags) } catch (e) { // Throws if flags are repeated. thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR, e.message); From 8f204007173ac628539606a8988541a12c24fd31 Mon Sep 17 00:00:00 2001 From: Tony Mobily Date: Mon, 25 Mar 2024 08:42:52 +0100 Subject: [PATCH 7/7] Versionm bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1f04b3e..468d48d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "js-interpreter-esm", - "version": "1.0.2", + "version": "1.0.3", "description": "Neil Fraser's official JS-Interpreter", "main": "interpreter.js", "module": "interpreter-esm.js",