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..2d12453 100644 --- a/interpreter.js +++ b/interpreter.js @@ -4,6 +4,29 @@ * 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; + 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 `parse`, 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 +41,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 +223,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 +380,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); }; /** @@ -1557,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 ? Interpreter.nativeGlobal.String(value) : ''; + value = arguments.length ? OriginalString(value) : ''; if (thisInterpreter.calledWithNew()) { // Called as `new String()`. this.data = value; @@ -1803,8 +1820,9 @@ Interpreter.prototype.initBoolean = function(globalObject) { var thisInterpreter = this; var wrapper; // Boolean constructor. + var OriginalBoolean = Boolean wrapper = function Boolean(value) { - value = Interpreter.nativeGlobal.Boolean(value); + value = OriginalBoolean(value); if (thisInterpreter.calledWithNew()) { // Called as `new Boolean()`. this.data = value; @@ -1827,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 ? Interpreter.nativeGlobal.Number(value) : 0; + value = arguments.length ? OriginalNumber(value) : 0; if (thisInterpreter.calledWithNew()) { // Called as `new Number()`. this.data = value; @@ -1911,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 Interpreter.nativeGlobal.Date(); + return OriginalDate(); } // Called as `new Date(...)`. var args = [null].concat(Array.from(arguments)); this.data = new (Function.prototype.bind.apply( - Interpreter.nativeGlobal.Date, args)); + OriginalDate, args)); return this; }; this.DATE = this.createNativeFunction(wrapper, true); @@ -1990,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()`. @@ -2011,7 +2032,7 @@ Interpreter.prototype.initRegExp = function(globalObject) { 'Invalid regexp flag: ' + flags); } try { - var nativeRegExp = new Interpreter.nativeGlobal.RegExp(pattern, flags) + var nativeRegExp = new OriginalRegExp(pattern, flags) } catch (e) { // Throws if flags are repeated. thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR, e.message); @@ -4803,7 +4824,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 +4845,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..468d48d --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "js-interpreter-esm", + "version": "1.0.3", + "description": "Neil Fraser's official JS-Interpreter", + "main": "interpreter.js", + "module": "interpreter-esm.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "postinstall": "node createEsmFiles.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/NeilFraser/JS-Interpreter.git" + }, + "keywords": [ + "JS-Interpreter", + "acorn" + ], + "author": "Neil Fraser", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/NeilFraser/JS-Interpreter/issues" + }, + "homepage": "https://github.com/NeilFraser/JS-Interpreter#readme" +}