diff --git a/lib/internal/repl/completion.js b/lib/internal/repl/completion.js index 8587e5e5b2333b..e5b317bbe989cd 100644 --- a/lib/internal/repl/completion.js +++ b/lib/internal/repl/completion.js @@ -33,7 +33,7 @@ const { const { kContextId, getREPLResourceName, - globalBuiltins, + getGlobalBuiltins, getReplBuiltinLibs, fixReplRequire, } = require('internal/repl/utils'); @@ -61,13 +61,6 @@ const { getOwnNonIndexProperties, } = internalBinding('util'); -const { - isIdentifierStart, - isIdentifierChar, - parse: acornParse, -} = require('internal/deps/acorn/acorn/dist/acorn'); -const acornWalk = require('internal/deps/acorn/acorn-walk/dist/walk'); - const importRE = /\bimport\s*\(\s*['"`](([\w@./:-]+\/)?(?:[\w@./:-]*))(?![^'"`])$/; const requireRE = /\brequire\s*\(\s*['"`](([\w@./:-]+\/)?(?:[\w@./:-]*))(?![^'"`])$/; const fsAutoCompleteRE = /fs(?:\.promises)?\.\s*[a-z][a-zA-Z]+\(\s*["'](.*)/; @@ -87,6 +80,8 @@ function isIdentifier(str) { if (str === '') { return false; } + const { isIdentifierStart, isIdentifierChar } = + require('internal/deps/acorn/acorn/dist/acorn'); const first = StringPrototypeCodePointAt(str, 0); if (!isIdentifierStart(first)) { return false; @@ -374,8 +369,8 @@ function complete(line, callback) { if (!this.useGlobal) { // When the context is not `global`, builtins are not own // properties of it. - // `globalBuiltins` is a `SafeSet`, not an Array-like. - ArrayPrototypePush(contextOwnNames, ...globalBuiltins); + // `getGlobalBuiltins()` is a `SafeSet`, not an Array-like. + ArrayPrototypePush(contextOwnNames, ...getGlobalBuiltins()); } ArrayPrototypePush(completionGroups, contextOwnNames); if (filter !== '') addCommonWords(completionGroups); @@ -387,6 +382,7 @@ function complete(line, callback) { // so in order to make it correct we add an identifier to its end (e.g. `obj.foo.x`) const parsableCompleteTarget = completeTarget.endsWith('.') ? `${completeTarget}x` : completeTarget; + const { parse: acornParse } = require('internal/deps/acorn/acorn/dist/acorn'); let completeTargetAst; try { completeTargetAst = acornParse( @@ -551,6 +547,7 @@ function findExpressionCompleteTarget(code) { return !result ? result : `${result}.`; } + const { parse: acornParse } = require('internal/deps/acorn/acorn/dist/acorn'); let ast; try { ast = acornParse(code, { __proto__: null, sourceType: 'module', ecmaVersion: 'latest' }); @@ -625,6 +622,7 @@ function findExpressionCompleteTarget(code) { // Walk the AST for the current block of code, and check whether it contains any // statement or expression type that would potentially have side effects if evaluated. + const acornWalk = require('internal/deps/acorn/acorn-walk/dist/walk'); let isAllowed = true; const disallow = () => isAllowed = false; acornWalk.simple(lastBodyStatement, { diff --git a/lib/internal/repl/utils.js b/lib/internal/repl/utils.js index 1e6c71271755d9..653e14cbb0c071 100644 --- a/lib/internal/repl/utils.js +++ b/lib/internal/repl/utils.js @@ -20,10 +20,8 @@ const { Symbol, } = primordials; -const { tokTypes: tt, Parser: AcornParser } = - require('internal/deps/acorn/acorn/dist/acorn'); - const { sendInspectorCommand } = require('internal/util/inspector'); +const { getLazy } = require('internal/util'); const { ERR_INSPECTOR_NOT_AVAILABLE, @@ -80,6 +78,9 @@ function isRecoverableError(e, code) { isRecoverableError(e, `(${code}`)) return true; + const { tokTypes: tt, Parser: AcornParser } = + require('internal/deps/acorn/acorn/dist/acorn'); + let recoverable = false; // Determine if the point of any error raised is at the end of the input. @@ -756,6 +757,8 @@ function setupReverseSearch(repl) { const startsWithBraceRegExp = /^\s*{/; const endsWithSemicolonRegExp = /;\s*$/; function isValidSyntax(input) { + const { Parser: AcornParser } = + require('internal/deps/acorn/acorn/dist/acorn'); try { AcornParser.parse(input, { ecmaVersion: 'latest', @@ -815,8 +818,9 @@ function getREPLResourceName() { return `REPL${nextREPLResourceNumber++}`; } -const globalBuiltins = - new SafeSet(vm.runInNewContext('Object.getOwnPropertyNames(globalThis)')); +// Creating a new context is expensive, so only do it on first use. +const getGlobalBuiltins = getLazy(() => + new SafeSet(vm.runInNewContext('Object.getOwnPropertyNames(globalThis)'))); let _builtinLibs = ArrayPrototypeFilter( CJSModule.builtinModules, @@ -848,7 +852,7 @@ module.exports = { isValidSyntax, kContextId, getREPLResourceName, - globalBuiltins, + getGlobalBuiltins, getReplBuiltinLibs, setReplBuiltinLibs, fixReplRequire, diff --git a/lib/repl.js b/lib/repl.js index 17aab1c409beca..a00b7b3f372f38 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -89,10 +89,6 @@ const { makeRequireFunction, addBuiltinLibsToObject, } = require('internal/modules/helpers'); -const { - parse: acornParse, -} = require('internal/deps/acorn/acorn/dist/acorn'); -const acornWalk = require('internal/deps/acorn/acorn-walk/dist/walk'); const { decorateErrorStack, isError, @@ -153,7 +149,7 @@ const { isValidSyntax, kContextId, getREPLResourceName, - globalBuiltins, + getGlobalBuiltins, getReplBuiltinLibs, setReplBuiltinLibs, fixReplRequire, @@ -249,6 +245,8 @@ writer.options = { ...inspect.defaultOptions, showProxy: true }; // Converts static import statement to dynamic import statement const toDynamicImport = (codeLine) => { + const { parse: acornParse } = require('internal/deps/acorn/acorn/dist/acorn'); + const acornWalk = require('internal/deps/acorn/acorn-walk/dist/walk'); let dynamicImportStatement = ''; const ast = acornParse(codeLine, { __proto__: null, sourceType: 'module', ecmaVersion: 'latest' }); acornWalk.ancestor(ast, { @@ -1139,7 +1137,7 @@ class REPLServer extends Interface { }); ArrayPrototypeForEach(ObjectGetOwnPropertyNames(globalThis), (name) => { // Only set properties that do not already exist as a global builtin. - if (!globalBuiltins.has(name)) { + if (!getGlobalBuiltins().has(name)) { ObjectDefineProperty(context, name, { __proto__: null,