From ec274c8ed14fce9d30d239d02847f8eb6d037187 Mon Sep 17 00:00:00 2001 From: Ciro Spaciari MacBook Date: Wed, 3 Jun 2026 19:03:47 -0700 Subject: [PATCH 01/73] node:repl: port Node v26.3.0 REPL + readline stack, vendor node repl tests Replaces the node:repl stub with a full port of Node v26.3.0's repl implementation, including the internal/readline stack it depends on: ported repl.js + internal/repl/{utils,completion,history,await}, the v26 internal/readline stack, vendored acorn (vm-evaluated), Node-internals shims, an --interactive CLI flag booting the Node-compatible REPL, and debug-build exposure of internal/repl* for the vendored Node test suite. Also: vm per-call displayErrors gates stack decoration; inspect recognizes cross-realm errors; vendored all 107 node repl tests. Known adaptations: contextified sandbox instead of DONT_CONTEXTIFY (var persistence), eager parse via createCachedData (lazy vm syntax errors), decorateErrorStack reproduces Node's REPL frame trimming (eager JSC stacks). --- src/js/eval/node-repl.ts | 64 + src/js/internal/readline/callbacks.js | 139 + .../internal/readline/emitKeypressEvents.js | 106 + src/js/internal/readline/interface.js | 1626 ++++++++++ src/js/internal/readline/promises.js | 151 + src/js/internal/readline/utils.js | 428 +++ src/js/internal/repl.js | 67 + src/js/internal/repl/acorn-walk.js | 11 + src/js/internal/repl/acorn.js | 11 + src/js/internal/repl/await.js | 238 ++ src/js/internal/repl/completion.js | 809 +++++ src/js/internal/repl/history.js | 446 +++ src/js/internal/repl/node-errors.js | 162 + src/js/internal/repl/node-inspect.js | 30 + src/js/internal/repl/node-primordials.js | 104 + src/js/internal/repl/node-shims.js | 459 +++ src/js/internal/repl/utils.js | 862 ++++++ src/js/internal/util/inspect.js | 7 +- src/js/node/readline.js | 529 ++++ src/js/node/readline.promises.js | 76 + src/js/node/readline.promises.ts | 2 - src/js/node/readline.ts | 2732 ----------------- src/js/node/repl.js | 1512 +++++++++ src/js/node/repl.ts | 124 - src/jsc/bindings/NodeVMScript.cpp | 8 +- src/options_types/context.rs | 4 + src/resolve_builtins/HardcodedModule.rs | 20 + src/runtime/cli/Arguments.rs | 4 + src/runtime/cli/mod.rs | 7 + src/runtime/cli/run_command.rs | 11 + src/runtime/jsc_hooks.rs | 9 +- test/js/node/test/common/repl.js | 25 + .../test-repl-array-prototype-tempering.js | 66 + .../test/parallel/test-repl-async-iife.js | 10 + .../test/parallel/test-repl-autocomplete.js | 219 ++ .../node/test/parallel/test-repl-autolibs.js | 70 + .../test-repl-clear-immediate-crash.js | 2 +- .../node/test/parallel/test-repl-cli-eval.js | 22 + .../js/node/test/parallel/test-repl-colors.js | 33 + ...est-repl-completion-on-getters-disabled.js | 188 ++ .../node/test/parallel/test-repl-context.js | 76 + .../test-repl-custom-eval-previews.js | 92 + .../test/parallel/test-repl-custom-eval.js | 111 + .../test/parallel/test-repl-definecommand.js | 44 + .../js/node/test/parallel/test-repl-domain.js | 49 + .../js/node/test/parallel/test-repl-editor.js | 115 + test/js/node/test/parallel/test-repl-empty.js | 23 + .../test/parallel/test-repl-end-emits-exit.js | 64 + .../node/test/parallel/test-repl-envvars.js | 87 + .../test-repl-eval-error-after-close.js | 35 + ...test-repl-function-definition-edge-case.js | 19 + .../node/test/parallel/test-repl-harmony.js | 50 + .../test-repl-history-dedup-multiline.js | 44 + .../test-repl-history-init-fail-leak.js | 56 + .../parallel/test-repl-history-navigation.js | 933 ++++++ .../test/parallel/test-repl-history-perm.js | 57 + .../parallel/test-repl-import-referrer.js | 26 + .../parallel/test-repl-inspect-defaults.js | 29 + .../node/test/parallel/test-repl-inspector.js | 28 + .../test/parallel/test-repl-let-process.js | 7 + .../test-repl-load-multiline-from-history.js | 96 + ...repl-load-multiline-no-trailing-newline.js | 33 + .../test/parallel/test-repl-load-multiline.js | 30 + test/js/node/test/parallel/test-repl-mode.js | 57 + ...-repl-multiline-navigation-while-adding.js | 312 ++ .../test-repl-multiline-navigation.js | 261 ++ .../node/test/parallel/test-repl-multiline.js | 29 + ...est-repl-multiple-instances-async-error.js | 69 + ...l-no-terminal-restore-process-listeners.js | 17 + .../test/parallel/test-repl-no-terminal.js | 9 + .../test/parallel/test-repl-null-thrown.js | 13 + test/js/node/test/parallel/test-repl-null.js | 13 + .../node/test/parallel/test-repl-options.js | 140 + .../parallel/test-repl-permission-model.js | 137 + .../parallel/test-repl-persistent-history.js | 266 ++ .../test-repl-preprocess-top-level-await.js | 154 + .../parallel/test-repl-pretty-custom-stack.js | 77 + .../test-repl-pretty-stack-custom-writer.js | 15 + .../test/parallel/test-repl-pretty-stack.js | 70 + .../parallel/test-repl-preview-newlines.js | 19 + .../parallel/test-repl-preview-timeout.js | 17 + .../node/test/parallel/test-repl-preview.js | 272 ++ ...repl-programmatic-history-setup-history.js | 279 ++ .../test-repl-programmatic-history.js | 264 ++ .../test/parallel/test-repl-recoverable.js | 41 + .../parallel/test-repl-require-after-write.js | 30 + .../test/parallel/test-repl-require-cache.js | 34 + .../parallel/test-repl-require-context.js | 23 + .../test-repl-require-self-referential.js | 26 + .../node/test/parallel/test-repl-require.js | 73 + .../test/parallel/test-repl-reset-event.js | 63 + .../test/parallel/test-repl-reverse-search.js | 365 +++ .../test-repl-save-load-editor-mode.js | 35 + .../test-repl-save-load-invalid-save.js | 25 + .../parallel/test-repl-save-load-load-dir.js | 23 + .../test-repl-save-load-load-non-existent.js | 23 + .../test-repl-save-load-load-without-name.js | 21 + .../test-repl-save-load-save-without-name.js | 21 + .../node/test/parallel/test-repl-save-load.js | 78 + .../node/test/parallel/test-repl-setprompt.js | 49 + .../parallel/test-repl-sigint-nested-eval.js | 53 + .../js/node/test/parallel/test-repl-sigint.js | 53 + .../parallel/test-repl-stdin-push-null.js | 9 + .../test-repl-strict-mode-previews.js | 50 + .../parallel/test-repl-syntax-error-stack.js | 32 + .../parallel/test-repl-tab-complete-buffer.js | 62 + .../test-repl-tab-complete-computed-props.js | 142 + .../parallel/test-repl-tab-complete-crash.js | 21 + ...test-repl-tab-complete-custom-completer.js | 65 + .../parallel/test-repl-tab-complete-files.js | 70 + .../parallel/test-repl-tab-complete-import.js | 144 + .../test-repl-tab-complete-nested-repls.js | 23 + .../test-repl-tab-complete-new-expression.js | 41 + .../test-repl-tab-complete-no-warn.js | 17 + .../test-repl-tab-complete-nosideeffects.js | 39 + .../test-repl-tab-complete-on-editor-mode.js | 35 + .../test-repl-tab-complete-require.js | 196 ++ ...est-repl-tab-complete-unary-expressions.js | 116 + .../test/parallel/test-repl-tab-complete.js | 565 ++++ test/js/node/test/parallel/test-repl-tab.js | 13 + .../test-repl-throw-null-or-undefined.js | 13 + .../parallel/test-repl-top-level-await.js | 230 ++ ...pl-uncaught-exception-after-input-ended.js | 23 + .../test-repl-uncaught-exception-async.js | 36 + ...st-repl-uncaught-exception-evalcallback.js | 22 + ...test-repl-uncaught-exception-standalone.js | 37 + .../parallel/test-repl-uncaught-exception.js | 70 + .../test/parallel/test-repl-underscore.js | 212 ++ .../test-repl-unexpected-token-recoverable.js | 33 + .../test-repl-unsafe-array-iteration.js | 68 + .../parallel/test-repl-unsupported-option.js | 11 + .../test/parallel/test-repl-use-global.js | 81 + .../parallel/test-repl-user-error-handler.js | 84 + test/js/node/test/parallel/test-repl.js | 1053 +++++++ .../sequential/test-repl-timeout-throw.js | 59 + 135 files changed, 17830 insertions(+), 2865 deletions(-) create mode 100644 src/js/eval/node-repl.ts create mode 100644 src/js/internal/readline/callbacks.js create mode 100644 src/js/internal/readline/emitKeypressEvents.js create mode 100644 src/js/internal/readline/interface.js create mode 100644 src/js/internal/readline/promises.js create mode 100644 src/js/internal/readline/utils.js create mode 100644 src/js/internal/repl.js create mode 100644 src/js/internal/repl/acorn-walk.js create mode 100644 src/js/internal/repl/acorn.js create mode 100644 src/js/internal/repl/await.js create mode 100644 src/js/internal/repl/completion.js create mode 100644 src/js/internal/repl/history.js create mode 100644 src/js/internal/repl/node-errors.js create mode 100644 src/js/internal/repl/node-inspect.js create mode 100644 src/js/internal/repl/node-primordials.js create mode 100644 src/js/internal/repl/node-shims.js create mode 100644 src/js/internal/repl/utils.js create mode 100644 src/js/node/readline.js create mode 100644 src/js/node/readline.promises.js delete mode 100644 src/js/node/readline.promises.ts delete mode 100644 src/js/node/readline.ts create mode 100644 src/js/node/repl.js delete mode 100644 src/js/node/repl.ts create mode 100644 test/js/node/test/common/repl.js create mode 100644 test/js/node/test/parallel/test-repl-array-prototype-tempering.js create mode 100644 test/js/node/test/parallel/test-repl-async-iife.js create mode 100644 test/js/node/test/parallel/test-repl-autocomplete.js create mode 100644 test/js/node/test/parallel/test-repl-autolibs.js create mode 100644 test/js/node/test/parallel/test-repl-cli-eval.js create mode 100644 test/js/node/test/parallel/test-repl-colors.js create mode 100644 test/js/node/test/parallel/test-repl-completion-on-getters-disabled.js create mode 100644 test/js/node/test/parallel/test-repl-context.js create mode 100644 test/js/node/test/parallel/test-repl-custom-eval-previews.js create mode 100644 test/js/node/test/parallel/test-repl-custom-eval.js create mode 100644 test/js/node/test/parallel/test-repl-definecommand.js create mode 100644 test/js/node/test/parallel/test-repl-domain.js create mode 100644 test/js/node/test/parallel/test-repl-editor.js create mode 100644 test/js/node/test/parallel/test-repl-empty.js create mode 100644 test/js/node/test/parallel/test-repl-end-emits-exit.js create mode 100644 test/js/node/test/parallel/test-repl-envvars.js create mode 100644 test/js/node/test/parallel/test-repl-eval-error-after-close.js create mode 100644 test/js/node/test/parallel/test-repl-function-definition-edge-case.js create mode 100644 test/js/node/test/parallel/test-repl-harmony.js create mode 100644 test/js/node/test/parallel/test-repl-history-dedup-multiline.js create mode 100644 test/js/node/test/parallel/test-repl-history-init-fail-leak.js create mode 100644 test/js/node/test/parallel/test-repl-history-navigation.js create mode 100644 test/js/node/test/parallel/test-repl-history-perm.js create mode 100644 test/js/node/test/parallel/test-repl-import-referrer.js create mode 100644 test/js/node/test/parallel/test-repl-inspect-defaults.js create mode 100644 test/js/node/test/parallel/test-repl-inspector.js create mode 100644 test/js/node/test/parallel/test-repl-let-process.js create mode 100644 test/js/node/test/parallel/test-repl-load-multiline-from-history.js create mode 100644 test/js/node/test/parallel/test-repl-load-multiline-no-trailing-newline.js create mode 100644 test/js/node/test/parallel/test-repl-load-multiline.js create mode 100644 test/js/node/test/parallel/test-repl-mode.js create mode 100644 test/js/node/test/parallel/test-repl-multiline-navigation-while-adding.js create mode 100644 test/js/node/test/parallel/test-repl-multiline-navigation.js create mode 100644 test/js/node/test/parallel/test-repl-multiline.js create mode 100644 test/js/node/test/parallel/test-repl-multiple-instances-async-error.js create mode 100644 test/js/node/test/parallel/test-repl-no-terminal-restore-process-listeners.js create mode 100644 test/js/node/test/parallel/test-repl-no-terminal.js create mode 100644 test/js/node/test/parallel/test-repl-null-thrown.js create mode 100644 test/js/node/test/parallel/test-repl-null.js create mode 100644 test/js/node/test/parallel/test-repl-options.js create mode 100644 test/js/node/test/parallel/test-repl-permission-model.js create mode 100644 test/js/node/test/parallel/test-repl-persistent-history.js create mode 100644 test/js/node/test/parallel/test-repl-preprocess-top-level-await.js create mode 100644 test/js/node/test/parallel/test-repl-pretty-custom-stack.js create mode 100644 test/js/node/test/parallel/test-repl-pretty-stack-custom-writer.js create mode 100644 test/js/node/test/parallel/test-repl-pretty-stack.js create mode 100644 test/js/node/test/parallel/test-repl-preview-newlines.js create mode 100644 test/js/node/test/parallel/test-repl-preview-timeout.js create mode 100644 test/js/node/test/parallel/test-repl-preview.js create mode 100644 test/js/node/test/parallel/test-repl-programmatic-history-setup-history.js create mode 100644 test/js/node/test/parallel/test-repl-programmatic-history.js create mode 100644 test/js/node/test/parallel/test-repl-recoverable.js create mode 100644 test/js/node/test/parallel/test-repl-require-after-write.js create mode 100644 test/js/node/test/parallel/test-repl-require-cache.js create mode 100644 test/js/node/test/parallel/test-repl-require-context.js create mode 100644 test/js/node/test/parallel/test-repl-require-self-referential.js create mode 100644 test/js/node/test/parallel/test-repl-require.js create mode 100644 test/js/node/test/parallel/test-repl-reset-event.js create mode 100644 test/js/node/test/parallel/test-repl-reverse-search.js create mode 100644 test/js/node/test/parallel/test-repl-save-load-editor-mode.js create mode 100644 test/js/node/test/parallel/test-repl-save-load-invalid-save.js create mode 100644 test/js/node/test/parallel/test-repl-save-load-load-dir.js create mode 100644 test/js/node/test/parallel/test-repl-save-load-load-non-existent.js create mode 100644 test/js/node/test/parallel/test-repl-save-load-load-without-name.js create mode 100644 test/js/node/test/parallel/test-repl-save-load-save-without-name.js create mode 100644 test/js/node/test/parallel/test-repl-save-load.js create mode 100644 test/js/node/test/parallel/test-repl-setprompt.js create mode 100644 test/js/node/test/parallel/test-repl-sigint-nested-eval.js create mode 100644 test/js/node/test/parallel/test-repl-sigint.js create mode 100644 test/js/node/test/parallel/test-repl-stdin-push-null.js create mode 100644 test/js/node/test/parallel/test-repl-strict-mode-previews.js create mode 100644 test/js/node/test/parallel/test-repl-syntax-error-stack.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-buffer.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-computed-props.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-crash.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-custom-completer.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-files.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-import.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-nested-repls.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-new-expression.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-no-warn.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-nosideeffects.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-on-editor-mode.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-require.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-unary-expressions.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete.js create mode 100644 test/js/node/test/parallel/test-repl-tab.js create mode 100644 test/js/node/test/parallel/test-repl-throw-null-or-undefined.js create mode 100644 test/js/node/test/parallel/test-repl-top-level-await.js create mode 100644 test/js/node/test/parallel/test-repl-uncaught-exception-after-input-ended.js create mode 100644 test/js/node/test/parallel/test-repl-uncaught-exception-async.js create mode 100644 test/js/node/test/parallel/test-repl-uncaught-exception-evalcallback.js create mode 100644 test/js/node/test/parallel/test-repl-uncaught-exception-standalone.js create mode 100644 test/js/node/test/parallel/test-repl-uncaught-exception.js create mode 100644 test/js/node/test/parallel/test-repl-underscore.js create mode 100644 test/js/node/test/parallel/test-repl-unexpected-token-recoverable.js create mode 100644 test/js/node/test/parallel/test-repl-unsafe-array-iteration.js create mode 100644 test/js/node/test/parallel/test-repl-unsupported-option.js create mode 100644 test/js/node/test/parallel/test-repl-use-global.js create mode 100644 test/js/node/test/parallel/test-repl-user-error-handler.js create mode 100644 test/js/node/test/parallel/test-repl.js create mode 100644 test/js/node/test/sequential/test-repl-timeout-throw.js diff --git a/src/js/eval/node-repl.ts b/src/js/eval/node-repl.ts new file mode 100644 index 000000000000..478b4c29cd71 --- /dev/null +++ b/src/js/eval/node-repl.ts @@ -0,0 +1,64 @@ +// Entry script for `bun -i` / `bun --interactive`: starts the Node.js-compatible +// REPL (the ported node:repl) the way Node's internal/main/repl.js does, using +// only public node:repl APIs (this file runs as a regular entrypoint, so it +// cannot require internal modules). + +const REPL = require('node:repl') + +console.log( + `Welcome to Node.js ${process.version}.\n` + + 'Type ".help" for more information.', +) + +const opts: Record = { + ignoreUndefined: false, + useGlobal: true, + breakEvalOnSigint: true, +} + +if (parseInt(process.env.NODE_NO_READLINE!)) { + opts.terminal = false +} + +if (process.env.NODE_REPL_MODE) { + opts.replMode = { + strict: REPL.REPL_MODE_STRICT, + sloppy: REPL.REPL_MODE_SLOPPY, + }[process.env.NODE_REPL_MODE.toLowerCase().trim()] +} + +if (opts.replMode === undefined) { + opts.replMode = REPL.REPL_MODE_SLOPPY +} + +const size = Number(process.env.NODE_REPL_HISTORY_SIZE) +if (!Number.isNaN(size) && size > 0) { + opts.size = size +} else { + opts.size = 1000 +} + +const term = 'terminal' in opts ? opts.terminal : process.stdout.isTTY +const filePath = term ? process.env.NODE_REPL_HISTORY : '' + +const replServer = REPL.start(opts) + +replServer.setupHistory({ + filePath, + size: opts.size, + onHistoryFileLoaded: (err: Error | null) => { + if (err) { + throw err + } + }, +}) + +replServer.on('exit', () => { + if (replServer.historyManager?.isFlushing) { + replServer.once('flushHistory', () => { + process.exit() + }) + return + } + process.exit() +}) diff --git a/src/js/internal/readline/callbacks.js b/src/js/internal/readline/callbacks.js new file mode 100644 index 000000000000..b4b3282d81b8 --- /dev/null +++ b/src/js/internal/readline/callbacks.js @@ -0,0 +1,139 @@ +// Ported from Node.js v26.3.0 lib/internal/readline/callbacks.js for Bun's node:repl. +// Attribution: derived from Node.js, MIT licensed (Node.js contributors). +// prettier-ignore +const primordials = require("internal/repl/node-primordials"); +var __node_module__ = { exports: {} }; +'use strict'; + +const { + NumberIsNaN, +} = primordials; + +const { + codes: { + ERR_INVALID_ARG_VALUE, + ERR_INVALID_CURSOR_POS, + }, +} = require("internal/repl/node-errors"); + +const { + validateFunction, +} = require("internal/validators"); +const { + CSI, +} = require("internal/readline/utils"); + +const { + kClearLine, + kClearScreenDown, + kClearToLineBeginning, + kClearToLineEnd, +} = CSI; + + +/** + * moves the cursor to the x and y coordinate on the given stream + */ + +function cursorTo(stream, x, y, callback) { + if (callback !== undefined) { + validateFunction(callback, 'callback'); + } + + if (typeof y === 'function') { + callback = y; + y = undefined; + } + + if (NumberIsNaN(x)) throw new ERR_INVALID_ARG_VALUE('x', x); + if (NumberIsNaN(y)) throw new ERR_INVALID_ARG_VALUE('y', y); + + if (stream == null || (typeof x !== 'number' && typeof y !== 'number')) { + if (typeof callback === 'function') process.nextTick(callback, null); + return true; + } + + if (typeof x !== 'number') throw new ERR_INVALID_CURSOR_POS(); + + const data = typeof y !== 'number' ? CSI`${x + 1}G` : CSI`${y + 1};${x + 1}H`; + return stream.write(data, callback); +} + +/** + * moves the cursor relative to its current location + */ + +function moveCursor(stream, dx, dy, callback) { + if (callback !== undefined) { + validateFunction(callback, 'callback'); + } + + if (stream == null || !(dx || dy)) { + if (typeof callback === 'function') process.nextTick(callback, null); + return true; + } + + let data = ''; + + if (dx < 0) { + data += CSI`${-dx}D`; + } else if (dx > 0) { + data += CSI`${dx}C`; + } + + if (dy < 0) { + data += CSI`${-dy}A`; + } else if (dy > 0) { + data += CSI`${dy}B`; + } + + return stream.write(data, callback); +} + +/** + * clears the current line the cursor is on: + * -1 for left of the cursor + * +1 for right of the cursor + * 0 for the entire line + */ + +function clearLine(stream, dir, callback) { + if (callback !== undefined) { + validateFunction(callback, 'callback'); + } + + if (stream === null || stream === undefined) { + if (typeof callback === 'function') process.nextTick(callback, null); + return true; + } + + const type = + dir < 0 ? kClearToLineBeginning : dir > 0 ? kClearToLineEnd : kClearLine; + return stream.write(type, callback); +} + +/** + * clears the screen from the current position of the cursor down + */ + +function clearScreenDown(stream, callback) { + if (callback !== undefined) { + validateFunction(callback, 'callback'); + } + + if (stream === null || stream === undefined) { + if (typeof callback === 'function') process.nextTick(callback, null); + return true; + } + + return stream.write(kClearScreenDown, callback); +} + +__node_module__.exports = { + clearLine, + clearScreenDown, + cursorTo, + moveCursor, +}; + +export default __node_module__.exports; diff --git a/src/js/internal/readline/emitKeypressEvents.js b/src/js/internal/readline/emitKeypressEvents.js new file mode 100644 index 000000000000..b35fe351d080 --- /dev/null +++ b/src/js/internal/readline/emitKeypressEvents.js @@ -0,0 +1,106 @@ +// Ported from Node.js v26.3.0 lib/internal/readline/emitKeypressEvents.js for Bun's node:repl. +// Attribution: derived from Node.js, MIT licensed (Node.js contributors). +// prettier-ignore +const primordials = require("internal/repl/node-primordials"); +var __node_module__ = { exports: {} }; +'use strict'; + +const { + SafeStringIterator, + Symbol, +} = primordials; + +const { + charLengthAt, + CSI, + emitKeys, +} = require("internal/readline/utils"); +const { + kSawKeyPress, +} = require("internal/readline/interface"); + +const { clearTimeout, setTimeout } = require("node:timers"); +const { + kEscape, +} = CSI; + +const { StringDecoder } = require("node:string_decoder"); + +const KEYPRESS_DECODER = Symbol('keypress-decoder'); +const ESCAPE_DECODER = Symbol('escape-decoder'); + +// GNU readline library - keyseq-timeout is 500ms (default) +const ESCAPE_CODE_TIMEOUT = 500; + +/** + * accepts a readable Stream instance and makes it emit "keypress" events + */ + +function emitKeypressEvents(stream, iface = {}) { + if (stream[KEYPRESS_DECODER]) return; + + stream[KEYPRESS_DECODER] = new StringDecoder('utf8'); + + stream[ESCAPE_DECODER] = emitKeys(stream); + stream[ESCAPE_DECODER].next(); + + const triggerEscape = () => stream[ESCAPE_DECODER].next(''); + const { escapeCodeTimeout = ESCAPE_CODE_TIMEOUT } = iface; + let timeoutId; + + function onData(input) { + if (stream.listenerCount('keypress') > 0) { + const string = stream[KEYPRESS_DECODER].write(input); + if (string) { + clearTimeout(timeoutId); + + // This supports characters of length 2. + iface[kSawKeyPress] = charLengthAt(string, 0) === string.length; + iface.isCompletionEnabled = false; + + let length = 0; + for (const character of new SafeStringIterator(string)) { + length += character.length; + if (length === string.length) { + iface.isCompletionEnabled = true; + } + + try { + stream[ESCAPE_DECODER].next(character); + // Escape letter at the tail position + if (length === string.length && character === kEscape) { + timeoutId = setTimeout(triggerEscape, escapeCodeTimeout); + } + } catch (err) { + // If the generator throws (it could happen in the `keypress` + // event), we need to restart it. + stream[ESCAPE_DECODER] = emitKeys(stream); + stream[ESCAPE_DECODER].next(); + throw err; + } + } + } + } else { + // Nobody's watching anyway + stream.removeListener('data', onData); + stream.on('newListener', onNewListener); + } + } + + function onNewListener(event) { + if (event === 'keypress') { + stream.on('data', onData); + stream.removeListener('newListener', onNewListener); + } + } + + if (stream.listenerCount('keypress') > 0) { + stream.on('data', onData); + } else { + stream.on('newListener', onNewListener); + } +} + +__node_module__.exports = emitKeypressEvents; + +export default __node_module__.exports; diff --git a/src/js/internal/readline/interface.js b/src/js/internal/readline/interface.js new file mode 100644 index 000000000000..ba495b60aaef --- /dev/null +++ b/src/js/internal/readline/interface.js @@ -0,0 +1,1626 @@ +// Ported from Node.js v26.3.0 lib/internal/readline/interface.js for Bun's node:repl. +// Attribution: derived from Node.js, MIT licensed (Node.js contributors). +// prettier-ignore +const primordials = require("internal/repl/node-primordials"); +var __node_module__ = { exports: {} }; +'use strict'; + +const { + ArrayFrom, + ArrayPrototypeFilter, + ArrayPrototypeJoin, + ArrayPrototypeMap, + ArrayPrototypePop, + ArrayPrototypePush, + ArrayPrototypeReverse, + ArrayPrototypeShift, + ArrayPrototypeUnshift, + DateNow, + FunctionPrototypeCall, + MathCeil, + MathFloor, + MathMax, + MathMaxApply, + NumberIsFinite, + ObjectDefineProperty, + ObjectSetPrototypeOf, + RegExpPrototypeExec, + SafeStringIterator, + StringPrototypeCodePointAt, + StringPrototypeEndsWith, + StringPrototypeIncludes, + StringPrototypeRepeat, + StringPrototypeReplaceAll, + StringPrototypeSlice, + StringPrototypeSplit, + StringPrototypeStartsWith, + Symbol, + SymbolAsyncIterator, + SymbolDispose, +} = primordials; + +const { + AbortError, + codes: { + ERR_INVALID_ARG_VALUE, + ERR_USE_AFTER_CLOSE, + }, +} = require("internal/repl/node-errors"); + +const { + validateAbortSignal, + validateString, + validateUint32, +} = require("internal/validators"); +const { + assignFunctionName, + kEmptyObject, +} = require("internal/repl/node-shims"); +const { + inspect, + getStringWidth, + stripVTControlCharacters, +} = require("internal/repl/node-inspect"); +const EventEmitter = require("node:events"); +const { addAbortListener } = require("internal/repl/node-shims"); +const { + charLengthAt, + charLengthLeft, + commonPrefix, + kSubstringSearch, +} = require("internal/readline/utils"); +let emitKeypressEvents; +let kFirstEventParam; +const { + clearScreenDown, + cursorTo, + moveCursor, +} = require("internal/readline/callbacks"); + +const { StringDecoder } = require("node:string_decoder"); +const { ReplHistory } = require("internal/repl/history"); + +const kMaxUndoRedoStackSize = 2048; +const kMincrlfDelay = 100; +/** + * The end of a line is signaled by either one of the following: + * - \r\n + * - \n + * - \r followed by something other than \n + * - \u2028 (Unicode 'LINE SEPARATOR') + * - \u2029 (Unicode 'PARAGRAPH SEPARATOR') + */ +const lineEnding = /\r?\n|\r(?!\n)|\u2028|\u2029/g; + +const kLineObjectStream = Symbol('line object stream'); +const kQuestionCancel = Symbol('kQuestionCancel'); +const kQuestion = Symbol('kQuestion'); + +// GNU readline library - keyseq-timeout is 500ms (default) +const ESCAPE_CODE_TIMEOUT = 500; + +// Max length of the kill ring +const kMaxLengthOfKillRing = 32; + +const kMultilinePrompt = Symbol('| '); + +const kAddHistory = Symbol('_addHistory'); +const kBeforeEdit = Symbol('_beforeEdit'); +const kDecoder = Symbol('_decoder'); +const kDeleteLeft = Symbol('_deleteLeft'); +const kDeleteLineLeft = Symbol('_deleteLineLeft'); +const kDeleteLineRight = Symbol('_deleteLineRight'); +const kDeleteRight = Symbol('_deleteRight'); +const kDeleteWordLeft = Symbol('_deleteWordLeft'); +const kDeleteWordRight = Symbol('_deleteWordRight'); +const kGetDisplayPos = Symbol('_getDisplayPos'); +const kHistoryNext = Symbol('_historyNext'); +const kMoveDownOrHistoryNext = Symbol('_moveDownOrHistoryNext'); +const kHistoryPrev = Symbol('_historyPrev'); +const kMoveUpOrHistoryPrev = Symbol('_moveUpOrHistoryPrev'); +const kInsertString = Symbol('_insertString'); +const kLine = Symbol('_line'); +const kLine_buffer = Symbol('_line_buffer'); +const kKillRing = Symbol('_killRing'); +const kKillRingCursor = Symbol('_killRingCursor'); +const kMoveCursor = Symbol('_moveCursor'); +const kNormalWrite = Symbol('_normalWrite'); +const kOldPrompt = Symbol('_oldPrompt'); +const kOnLine = Symbol('_onLine'); +const kSetLine = Symbol('_setLine'); +const kPreviousKey = Symbol('_previousKey'); +const kPrompt = Symbol('_prompt'); +const kPushToKillRing = Symbol('_pushToKillRing'); +const kPushToUndoStack = Symbol('_pushToUndoStack'); +const kQuestionCallback = Symbol('_questionCallback'); +const kLastCommandErrored = Symbol('_lastCommandErrored'); +const kQuestionReject = Symbol('_questionReject'); +const kRedo = Symbol('_redo'); +const kRedoStack = Symbol('_redoStack'); +const kRefreshLine = Symbol('_refreshLine'); +const kSawKeyPress = Symbol('_sawKeyPress'); +const kSawReturnAt = Symbol('_sawReturnAt'); +const kSetRawMode = Symbol('_setRawMode'); +const kTabComplete = Symbol('_tabComplete'); +const kTabCompleter = Symbol('_tabCompleter'); +const kTtyWrite = Symbol('_ttyWrite'); +const kUndo = Symbol('_undo'); +const kUndoStack = Symbol('_undoStack'); +const kIsMultiline = Symbol('_isMultiline'); +const kWordLeft = Symbol('_wordLeft'); +const kWordRight = Symbol('_wordRight'); +const kWriteToOutput = Symbol('_writeToOutput'); +const kYank = Symbol('_yank'); +const kYanking = Symbol('_yanking'); +const kYankPop = Symbol('_yankPop'); +const kSavePreviousState = Symbol('_savePreviousState'); +const kRestorePreviousState = Symbol('_restorePreviousState'); +const kPreviousLine = Symbol('_previousLine'); +const kPreviousCursor = Symbol('_previousCursor'); +const kPreviousCursorCols = Symbol('_previousCursorCols'); +const kMultilineMove = Symbol('_multilineMove'); +const kPreviousPrevRows = Symbol('_previousPrevRows'); +const kAddNewLineOnTTY = Symbol('_addNewLineOnTTY'); + +function InterfaceConstructor(input, output, completer, terminal) { + this[kSawReturnAt] = 0; + // TODO(BridgeAR): Document this property. The name is not ideal, so we + // might want to expose an alias and document that instead. + this.isCompletionEnabled = true; + this[kSawKeyPress] = false; + this[kPreviousKey] = null; + this.escapeCodeTimeout = ESCAPE_CODE_TIMEOUT; + this.tabSize = 8; + + FunctionPrototypeCall(EventEmitter, this); + + let crlfDelay; + let prompt = '> '; + let signal; + + if (input?.input) { + // An options object was given + output = input.output; + completer = input.completer; + terminal = input.terminal; + signal = input.signal; + + // It is possible to configure the history through the input object + const historySize = input.historySize; + const history = input.history; + const removeHistoryDuplicates = input.removeHistoryDuplicates; + + if (input.tabSize !== undefined) { + validateUint32(input.tabSize, 'tabSize', true); + this.tabSize = input.tabSize; + } + if (input.prompt !== undefined) { + prompt = input.prompt; + } + if (input.escapeCodeTimeout !== undefined) { + if (NumberIsFinite(input.escapeCodeTimeout)) { + this.escapeCodeTimeout = input.escapeCodeTimeout; + } else { + throw new ERR_INVALID_ARG_VALUE( + 'input.escapeCodeTimeout', + this.escapeCodeTimeout, + ); + } + } + + if (signal) { + validateAbortSignal(signal, 'options.signal'); + } + + crlfDelay = input.crlfDelay; + input = input.input; + + input.size = historySize; + input.history = history; + input.removeHistoryDuplicates = removeHistoryDuplicates; + } + + this.setupHistoryManager(input); + + if (completer !== undefined && typeof completer !== 'function') { + throw new ERR_INVALID_ARG_VALUE('completer', completer); + } + + // Backwards compat; check the isTTY prop of the output stream + // when `terminal` was not specified + if (terminal === undefined && !(output === null || output === undefined)) { + terminal = !!output.isTTY; + } + + const self = this; + + this.line = ''; + this[kIsMultiline] = false; + this[kSubstringSearch] = null; + this.output = output; + this.input = input; + this[kUndoStack] = []; + this[kRedoStack] = []; + this[kPreviousCursorCols] = -1; + + // The kill ring is a global list of blocks of text that were previously + // killed (deleted). If its size exceeds kMaxLengthOfKillRing, the oldest + // element will be removed to make room for the latest deletion. With kill + // ring, users are able to recall (yank) or cycle (yank pop) among previously + // killed texts, quite similar to the behavior of Emacs. + this[kKillRing] = []; + this[kKillRingCursor] = 0; + + this.crlfDelay = crlfDelay ? + MathMax(kMincrlfDelay, crlfDelay) : + kMincrlfDelay; + this.completer = completer; + + this.setPrompt(prompt); + + this.terminal = !!terminal; + + function onerror(err) { + self.emit('error', err); + } + + function ondata(data) { + self[kNormalWrite](data); + } + + function onend() { + if ( + typeof self[kLine_buffer] === 'string' && + self[kLine_buffer].length > 0 + ) { + self.emit('line', self[kLine_buffer]); + } + self.close(); + } + + function ontermend() { + if (typeof self.line === 'string' && self.line.length > 0) { + self.emit('line', self.line); + } + self.close(); + } + + function onkeypress(s, key) { + self[kTtyWrite](s, key); + if (key?.sequence) { + // If the key.sequence is half of a surrogate pair + // (>= 0xd800 and <= 0xdfff), refresh the line so + // the character is displayed appropriately. + const ch = StringPrototypeCodePointAt(key.sequence, 0); + if (ch >= 0xd800 && ch <= 0xdfff) self[kRefreshLine](); + } + } + + function onresize() { + self[kRefreshLine](); + } + + this[kLineObjectStream] = undefined; + + input.on('error', onerror); + + if (!this.terminal) { + function onSelfCloseWithoutTerminal() { + input.removeListener('data', ondata); + input.removeListener('error', onerror); + input.removeListener('end', onend); + } + + input.on('data', ondata); + input.on('end', onend); + self.once('close', onSelfCloseWithoutTerminal); + this[kDecoder] = new StringDecoder('utf8'); + } else { + function onSelfCloseWithTerminal() { + input.removeListener('keypress', onkeypress); + input.removeListener('error', onerror); + input.removeListener('end', ontermend); + if (output !== null && output !== undefined) { + output.removeListener('resize', onresize); + } + } + + emitKeypressEvents ??= require("internal/readline/emitKeypressEvents"); + emitKeypressEvents(input, this); + + // `input` usually refers to stdin + input.on('keypress', onkeypress); + input.on('end', ontermend); + + this[kSetRawMode](true); + this.terminal = true; + + // Cursor position on the line. + this.cursor = 0; + + if (output !== null && output !== undefined) + output.on('resize', onresize); + + self.once('close', onSelfCloseWithTerminal); + } + + if (signal) { + const onAborted = () => self.close(); + if (signal.aborted) { + process.nextTick(onAborted); + } else { + const disposable = addAbortListener(signal, onAborted); + self.once('close', disposable[SymbolDispose]); + } + } + + // Current line + this[kSetLine](''); + + input.resume(); +} + +$toClass(InterfaceConstructor, "InterfaceConstructor", EventEmitter); + +class Interface extends InterfaceConstructor { + get columns() { + if (this.output?.columns) return this.output.columns; + return Infinity; + } + + /** + * Sets the prompt written to the output. + * @param {string} prompt + * @returns {void} + */ + setPrompt(prompt) { + this[kPrompt] = prompt; + } + + /** + * Returns the current prompt used by `rl.prompt()`. + * @returns {string} + */ + getPrompt() { + return this[kPrompt]; + } + + setupHistoryManager(options) { + this.historyManager = new ReplHistory(this, options); + + if (options.onHistoryFileLoaded) { + this.historyManager.initialize(options.onHistoryFileLoaded); + } + + ObjectDefineProperty(this, 'history', { + __proto__: null, configurable: true, enumerable: true, + get() { return this.historyManager.history; }, + set(newHistory) { return this.historyManager.history = newHistory; }, + }); + + ObjectDefineProperty(this, 'historyIndex', { + __proto__: null, configurable: true, enumerable: true, + get() { return this.historyManager.index; }, + set(historyIndex) { return this.historyManager.index = historyIndex; }, + }); + + ObjectDefineProperty(this, 'historySize', { + __proto__: null, configurable: true, enumerable: true, + get() { return this.historyManager.size; }, + }); + + ObjectDefineProperty(this, 'isFlushing', { + __proto__: null, configurable: true, enumerable: true, + get() { return this.historyManager.isFlushing; }, + }); + } + + [kSetRawMode](mode) { + const wasInRawMode = this.input.isRaw; + + if (typeof this.input.setRawMode === 'function') { + this.input.setRawMode(mode); + } + + return wasInRawMode; + } + + /** + * Writes the configured `prompt` to a new line in `output`. + * @param {boolean} [preserveCursor] + * @returns {void} + */ + prompt(preserveCursor) { + if (this.paused) this.resume(); + if (this.terminal && process.env.TERM !== 'dumb') { + if (!preserveCursor) this.cursor = 0; + this[kRefreshLine](); + } else { + this[kWriteToOutput](this[kPrompt]); + } + } + + [kQuestion](query, cb) { + if (this.closed) { + throw new ERR_USE_AFTER_CLOSE('readline'); + } + if (this[kQuestionCallback]) { + this.prompt(); + } else { + this[kOldPrompt] = this[kPrompt]; + this.setPrompt(query); + this[kQuestionCallback] = cb; + this.prompt(); + } + } + + [kSetLine](line = '') { + this.line = line; + this[kIsMultiline] = StringPrototypeIncludes(line, '\n'); + } + + [kOnLine](line) { + if (this[kQuestionCallback]) { + const cb = this[kQuestionCallback]; + this[kQuestionCallback] = null; + this.setPrompt(this[kOldPrompt]); + cb(line); + } else { + this.emit('line', line); + } + } + + [kBeforeEdit](oldText, oldCursor) { + this[kPushToUndoStack](oldText, oldCursor); + } + + [kQuestionCancel]() { + if (this[kQuestionCallback]) { + this[kQuestionCallback] = null; + this.setPrompt(this[kOldPrompt]); + this.clearLine(); + } + } + + [kWriteToOutput](stringToWrite) { + validateString(stringToWrite, 'stringToWrite'); + + if (this.output !== null && this.output !== undefined) { + this.output.write(stringToWrite); + } + } + + [kAddHistory]() { + return this.historyManager.addHistory(this[kIsMultiline], this[kLastCommandErrored]); + } + + [kRefreshLine]() { + // line length + const line = this[kPrompt] + this.line; + const dispPos = this[kGetDisplayPos](line); + const lineCols = dispPos.cols; + const lineRows = dispPos.rows; + + // cursor position + const cursorPos = this.getCursorPos(); + + // First move to the bottom of the current line, based on cursor pos + const prevRows = this.prevRows || 0; + if (prevRows > 0) { + moveCursor(this.output, 0, -prevRows); + } + + // Cursor to left edge. + cursorTo(this.output, 0); + // erase data + clearScreenDown(this.output); + + if (this[kIsMultiline]) { + const lines = StringPrototypeSplit(this.line, '\n'); + // Write first line with normal prompt + this[kWriteToOutput](this[kPrompt] + lines[0]); + + // For continuation lines, add the "|" prefix + for (let i = 1; i < lines.length; i++) { + this[kWriteToOutput](`\n${kMultilinePrompt.description}` + lines[i]); + } + } else { + // Write the prompt and the current buffer content. + this[kWriteToOutput](line); + } + + // Force terminal to allocate a new line + if (lineCols === 0) { + this[kWriteToOutput](' '); + } + + // Move cursor to original position. + cursorTo(this.output, cursorPos.cols); + + const diff = lineRows - cursorPos.rows; + if (diff > 0) { + moveCursor(this.output, 0, -diff); + } + + this.prevRows = cursorPos.rows; + } + + /** + * Closes the `readline.Interface` instance. + * @returns {void} + */ + close() { + if (this.closed) return; + this.pause(); + if (this.terminal) { + this[kSetRawMode](false); + } + this.closed = true; + this.emit('close'); + } + + /** + * Pauses the `input` stream. + * @returns {void | Interface} + */ + pause() { + if (this.closed) { + throw new ERR_USE_AFTER_CLOSE('readline'); + } + if (this.paused) return; + this.input.pause(); + this.paused = true; + this.emit('pause'); + return this; + } + + /** + * Resumes the `input` stream if paused. + * @returns {void | Interface} + */ + resume() { + if (this.closed) { + throw new ERR_USE_AFTER_CLOSE('readline'); + } + if (!this.paused) return; + this.input.resume(); + this.paused = false; + this.emit('resume'); + return this; + } + + /** + * Writes either `data` or a `key` sequence identified by + * `key` to the `output`. + * @param {string} d + * @param {{ + * ctrl?: boolean; + * meta?: boolean; + * shift?: boolean; + * name?: string; + * }} [key] + * @returns {void} + */ + write(d, key) { + if (this.closed) { + throw new ERR_USE_AFTER_CLOSE('readline'); + } + if (this.paused) this.resume(); + if (this.terminal) { + this[kTtyWrite](d, key); + } else { + this[kNormalWrite](d); + } + } + + [kNormalWrite](b) { + if (b === undefined) { + return; + } + let string = this[kDecoder].write(b); + if ( + this[kSawReturnAt] && + DateNow() - this[kSawReturnAt] <= this.crlfDelay + ) { + if (StringPrototypeCodePointAt(string) === 10) string = StringPrototypeSlice(string, 1); + this[kSawReturnAt] = 0; + } + + // Run test() on the new string chunk, not on the entire line buffer. + let newPartContainsEnding = RegExpPrototypeExec(lineEnding, string); + if (newPartContainsEnding !== null) { + if (this[kLine_buffer]) { + string = this[kLine_buffer] + string; + this[kLine_buffer] = null; + lineEnding.lastIndex = 0; // Start the search from the beginning of the string. + newPartContainsEnding = RegExpPrototypeExec(lineEnding, string); + } + this[kSawReturnAt] = StringPrototypeEndsWith(string, '\r') ? + DateNow() : + 0; + + const indexes = [0, newPartContainsEnding.index, lineEnding.lastIndex]; + let nextMatch; + while ((nextMatch = RegExpPrototypeExec(lineEnding, string)) !== null) { + ArrayPrototypePush(indexes, nextMatch.index, lineEnding.lastIndex); + } + const lastIndex = indexes.length - 1; + // Either '' or (conceivably) the unfinished portion of the next line + this[kLine_buffer] = StringPrototypeSlice(string, indexes[lastIndex]); + for (let i = 1; i < lastIndex; i += 2) { + this[kOnLine](StringPrototypeSlice(string, indexes[i - 1], indexes[i])); + } + } else if (string) { + // No newlines this time, save what we have for next time + if (this[kLine_buffer]) { + this[kLine_buffer] += string; + } else { + this[kLine_buffer] = string; + } + } + } + + [kInsertString](c) { + this[kBeforeEdit](this.line, this.cursor); + if (!this.isCompletionEnabled) { + if (this.cursor < this.line.length) { + const beg = StringPrototypeSlice(this.line, 0, this.cursor); + const end = StringPrototypeSlice( + this.line, + this.cursor, + this.line.length, + ); + this.line = beg + c + end; + } else { + this.line += c; + } + this.cursor += c.length; + this[kWriteToOutput](c); + return; + } + if (this.cursor < this.line.length) { + const beg = StringPrototypeSlice(this.line, 0, this.cursor); + const end = StringPrototypeSlice( + this.line, + this.cursor, + this.line.length, + ); + this[kSetLine](beg + c + end); + this.cursor += c.length; + this[kRefreshLine](); + } else { + const oldPos = this.getCursorPos(); + this.line += c; + this.cursor += c.length; + const newPos = this.getCursorPos(); + + if (oldPos.rows < newPos.rows) { + this[kRefreshLine](); + } else { + this[kWriteToOutput](c); + } + } + } + + async [kTabComplete](lastKeypressWasTab) { + this.pause(); + const string = StringPrototypeSlice(this.line, 0, this.cursor); + let value; + try { + value = await this.completer(string); + } catch (err) { + this[kWriteToOutput](`Tab completion error: ${inspect(err)}`); + return; + } finally { + this.resume(); + } + this[kTabCompleter](lastKeypressWasTab, value); + } + + [kTabCompleter](lastKeypressWasTab, { 0: completions, 1: completeOn }) { + // Result and the text that was completed. + + if (!completions || completions.length === 0) { + return; + } + + // If there is a common prefix to all matches, then apply that portion. + const prefix = commonPrefix( + ArrayPrototypeFilter(completions, (e) => e !== ''), + ); + if (StringPrototypeStartsWith(prefix, completeOn) && + prefix.length > completeOn.length) { + this[kInsertString](StringPrototypeSlice(prefix, completeOn.length)); + return; + } else if (!StringPrototypeStartsWith(completeOn, prefix)) { + this[kSetLine](StringPrototypeSlice(this.line, + 0, + this.cursor - completeOn.length) + + prefix + + StringPrototypeSlice(this.line, + this.cursor, + this.line.length)); + this.cursor = this.cursor - completeOn.length + prefix.length; + this[kRefreshLine](); + return; + } + + if (!lastKeypressWasTab) { + return; + } + + this[kBeforeEdit](this.line, this.cursor); + + // Apply/show completions. + const completionsWidth = ArrayPrototypeMap(completions, (e) => + getStringWidth(e), + ); + const width = MathMaxApply(completionsWidth) + 2; // 2 space padding + let maxColumns = MathFloor(this.columns / width) || 1; + if (maxColumns === Infinity) { + maxColumns = 1; + } + let output = '\r\n'; + let lineIndex = 0; + let whitespace = 0; + for (let i = 0; i < completions.length; i++) { + const completion = completions[i]; + if (completion === '' || lineIndex === maxColumns) { + output += '\r\n'; + lineIndex = 0; + whitespace = 0; + } else { + output += StringPrototypeRepeat(' ', whitespace); + } + if (completion !== '') { + output += completion; + whitespace = width - completionsWidth[i]; + lineIndex++; + } else { + output += '\r\n'; + } + } + if (lineIndex !== 0) { + output += '\r\n\r\n'; + } + this[kWriteToOutput](output); + this[kRefreshLine](); + } + + [kWordLeft]() { + if (this.cursor > 0) { + // Reverse the string and match a word near beginning + // to avoid quadratic time complexity + const leading = StringPrototypeSlice(this.line, 0, this.cursor); + const reversed = ArrayPrototypeJoin( + ArrayPrototypeReverse(ArrayFrom(leading)), + '', + ); + const match = RegExpPrototypeExec(/^\s*(?:[^\w\s]+|\w+)?/, reversed); + this[kMoveCursor](-match[0].length); + } + } + + [kWordRight]() { + if (this.cursor < this.line.length) { + const trailing = StringPrototypeSlice(this.line, this.cursor); + const match = RegExpPrototypeExec(/^(?:\s+|[^\w\s]+|\w+)\s*/, trailing); + this[kMoveCursor](match[0].length); + } + } + + [kDeleteLeft]() { + if (this.cursor > 0 && this.line.length > 0) { + this[kBeforeEdit](this.line, this.cursor); + // The number of UTF-16 units comprising the character to the left + const charSize = charLengthLeft(this.line, this.cursor); + this.line = + StringPrototypeSlice(this.line, 0, this.cursor - charSize) + + StringPrototypeSlice(this.line, this.cursor, this.line.length); + + this.cursor -= charSize; + this[kRefreshLine](); + } + } + + [kDeleteRight]() { + if (this.cursor < this.line.length) { + this[kBeforeEdit](this.line, this.cursor); + // The number of UTF-16 units comprising the character to the left + const charSize = charLengthAt(this.line, this.cursor); + this.line = + StringPrototypeSlice(this.line, 0, this.cursor) + + StringPrototypeSlice( + this.line, + this.cursor + charSize, + this.line.length, + ); + this[kRefreshLine](); + } + } + + [kDeleteWordLeft]() { + if (this.cursor > 0) { + this[kBeforeEdit](this.line, this.cursor); + // Reverse the string and match a word near beginning + // to avoid quadratic time complexity + let leading = StringPrototypeSlice(this.line, 0, this.cursor); + const reversed = ArrayPrototypeJoin( + ArrayPrototypeReverse(ArrayFrom(leading)), + '', + ); + const match = RegExpPrototypeExec(/^\s*(?:[^\w\s]+|\w+)?/, reversed); + leading = StringPrototypeSlice( + leading, + 0, + leading.length - match[0].length, + ); + this.line = + leading + + StringPrototypeSlice(this.line, this.cursor, this.line.length); + this.cursor = leading.length; + this[kRefreshLine](); + } + } + + [kDeleteWordRight]() { + if (this.cursor < this.line.length) { + this[kBeforeEdit](this.line, this.cursor); + const trailing = StringPrototypeSlice(this.line, this.cursor); + const match = RegExpPrototypeExec(/^(?:\s+|\W+|\w+)\s*/, trailing); + this.line = + StringPrototypeSlice(this.line, 0, this.cursor) + + StringPrototypeSlice(trailing, match[0].length); + this[kRefreshLine](); + } + } + + [kDeleteLineLeft]() { + this[kBeforeEdit](this.line, this.cursor); + const del = StringPrototypeSlice(this.line, 0, this.cursor); + this[kSetLine](StringPrototypeSlice(this.line, this.cursor)); + this.cursor = 0; + this[kPushToKillRing](del); + this[kRefreshLine](); + } + + [kDeleteLineRight]() { + this[kBeforeEdit](this.line, this.cursor); + const del = StringPrototypeSlice(this.line, this.cursor); + this[kSetLine](StringPrototypeSlice(this.line, 0, this.cursor)); + this[kPushToKillRing](del); + this[kRefreshLine](); + } + + [kPushToKillRing](del) { + if (!del || del === this[kKillRing][0]) return; + ArrayPrototypeUnshift(this[kKillRing], del); + this[kKillRingCursor] = 0; + while (this[kKillRing].length > kMaxLengthOfKillRing) + ArrayPrototypePop(this[kKillRing]); + } + + [kYank]() { + if (this[kKillRing].length > 0) { + this[kYanking] = true; + this[kInsertString](this[kKillRing][this[kKillRingCursor]]); + } + } + + [kYankPop]() { + if (!this[kYanking]) { + return; + } + if (this[kKillRing].length > 1) { + const lastYank = this[kKillRing][this[kKillRingCursor]]; + this[kKillRingCursor]++; + if (this[kKillRingCursor] >= this[kKillRing].length) { + this[kKillRingCursor] = 0; + } + const currentYank = this[kKillRing][this[kKillRingCursor]]; + const head = + StringPrototypeSlice(this.line, 0, this.cursor - lastYank.length); + const tail = + StringPrototypeSlice(this.line, this.cursor); + this[kSetLine](head + currentYank + tail); + this.cursor = head.length + currentYank.length; + this[kRefreshLine](); + } + } + + [kSavePreviousState]() { + this[kPreviousLine] = this.line; + this[kPreviousCursor] = this.cursor; + this[kPreviousPrevRows] = this.prevRows; + } + + [kRestorePreviousState]() { + this[kSetLine](this[kPreviousLine]); + this.cursor = this[kPreviousCursor]; + this.prevRows = this[kPreviousPrevRows]; + } + + clearLine() { + this[kMoveCursor](+Infinity); + this[kWriteToOutput]('\r\n'); + this[kSetLine](''); + this.cursor = 0; + this.prevRows = 0; + } + + [kLine]() { + this[kSavePreviousState](); + const line = this[kAddHistory](); + this[kUndoStack] = []; + this[kRedoStack] = []; + this.clearLine(); + this[kOnLine](line); + } + + + // TODO(puskin94): edit [kTtyWrite] to make call this function on a new key combination + // to make it add a new line in the middle of a "complete" multiline. + // I tried with shift + enter but it is not detected. Find a new one. + // Make sure to call this[kSavePreviousState](); && this.clearLine(); + // before calling this[kAddNewLineOnTTY] to simulate what [kLine] is doing. + + // When this function is called, the actual cursor is at the very end of the whole string, + // No matter where the new line was entered. + // This function should only be used when the output is a TTY + [kAddNewLineOnTTY]() { + // Restore terminal state and store current line + this[kRestorePreviousState](); + const originalLine = this.line; + + // Split the line at the current cursor position + const beforeCursor = StringPrototypeSlice(this.line, 0, this.cursor); + let afterCursor = StringPrototypeSlice(this.line, this.cursor, this.line.length); + + // Add the new line where the cursor is at + this[kSetLine](`${beforeCursor}\n${afterCursor}`); + + // To account for the new line + this.cursor += 1; + + const hasContentAfterCursor = afterCursor.length > 0; + const cursorIsNotOnFirstLine = this.prevRows > 0; + let needsRewriteFirstLine = false; + + // Handle cursor positioning based on different scenarios + if (hasContentAfterCursor) { + const splitBeg = StringPrototypeSplit(beforeCursor, '\n'); + // Determine if we need to rewrite the first line + needsRewriteFirstLine = splitBeg.length < 2; + + // If the cursor is not on the first line + if (cursorIsNotOnFirstLine) { + const splitEnd = StringPrototypeSplit(afterCursor, '\n'); + + // If the cursor when I pressed enter was at least on the second line + // I need to completely erase the line where the cursor was pressed because it is possible + // That it was pressed in the middle of the line, hence I need to write the whole line. + // To achieve that, I need to reach the line above the current line coming from the end + const dy = splitEnd.length + 1; + + // Calculate how many Xs we need to move on the right to get to the end of the line + const dxEndOfLineAbove = (splitBeg[splitBeg.length - 2] || '').length + kMultilinePrompt.description.length; + moveCursor(this.output, dxEndOfLineAbove, -dy); + + // This is the line that was split in the middle + // Just add it to the rest of the line that will be printed later + afterCursor = `${splitBeg[splitBeg.length - 1]}\n${afterCursor}`; + } else { + // Otherwise, go to the very beginning of the first line and erase everything + const dy = StringPrototypeSplit(originalLine, '\n').length; + moveCursor(this.output, 0, -dy); + } + + // Erase from the cursor to the end of the line + clearScreenDown(this.output); + + if (cursorIsNotOnFirstLine) { + this[kWriteToOutput]('\n'); + } + } + + if (needsRewriteFirstLine) { + this[kWriteToOutput](`${this[kPrompt]}${beforeCursor}\n${kMultilinePrompt.description}`); + } else { + this[kWriteToOutput](kMultilinePrompt.description); + } + + // Write the rest and restore the cursor to where the user left it + if (hasContentAfterCursor) { + // Save the cursor pos, we need to come back here + const oldCursor = this.getCursorPos(); + + // Write everything after the cursor which has been deleted by clearScreenDown + const formattedEndContent = StringPrototypeReplaceAll( + afterCursor, + '\n', + `\n${kMultilinePrompt.description}`, + ); + + this[kWriteToOutput](formattedEndContent); + + const newCursor = this[kGetDisplayPos](this.line); + + // Go back to where the cursor was, with relative movement + moveCursor(this.output, oldCursor.cols - newCursor.cols, oldCursor.rows - newCursor.rows); + + // Setting how many rows we have on top of the cursor + // Necessary for kRefreshLine + this.prevRows = oldCursor.rows; + } else { + // Setting how many rows we have on top of the cursor + // Necessary for kRefreshLine + this.prevRows = StringPrototypeSplit(this.line, '\n').length - 1; + } + } + + [kPushToUndoStack](text, cursor) { + if (ArrayPrototypePush(this[kUndoStack], { text, cursor }) > + kMaxUndoRedoStackSize) { + ArrayPrototypeShift(this[kUndoStack]); + } + } + + [kUndo]() { + if (this[kUndoStack].length <= 0) return; + + ArrayPrototypePush( + this[kRedoStack], + { text: this.line, cursor: this.cursor }, + ); + + const entry = ArrayPrototypePop(this[kUndoStack]); + this[kSetLine](entry.text); + this.cursor = entry.cursor; + + this[kRefreshLine](); + } + + [kRedo]() { + if (this[kRedoStack].length <= 0) return; + + ArrayPrototypePush( + this[kUndoStack], + { text: this.line, cursor: this.cursor }, + ); + + const entry = ArrayPrototypePop(this[kRedoStack]); + this[kSetLine](entry.text); + this.cursor = entry.cursor; + + this[kRefreshLine](); + } + + [kMultilineMove](direction, splitLines, { rows, cols }) { + const curr = splitLines[rows]; + const down = direction === 1; + const adj = splitLines[rows + direction]; + const promptLen = kMultilinePrompt.description.length; + let amountToMove; + // Clamp distance to end of current + prompt + next/prev line + newline + const clamp = down ? + curr.length - cols + promptLen + adj.length + 1 : + -cols + 1; + const shouldClamp = cols > adj.length + 1; + + if (shouldClamp) { + if (this[kPreviousCursorCols] === -1) { + this[kPreviousCursorCols] = cols; + } + amountToMove = clamp; + } else { + if (down) { + amountToMove = curr.length + 1; + } else { + amountToMove = -adj.length - 1; + } + if (this[kPreviousCursorCols] !== -1) { + if (this[kPreviousCursorCols] <= adj.length) { + amountToMove += this[kPreviousCursorCols] - cols; + this[kPreviousCursorCols] = -1; + } else { + amountToMove = clamp; + } + } + } + + this[kMoveCursor](amountToMove); + } + + [kMoveDownOrHistoryNext]() { + const cursorPos = this.getCursorPos(); + const splitLines = StringPrototypeSplit(this.line, '\n'); + if (this[kIsMultiline] && cursorPos.rows < splitLines.length - 1) { + this[kMultilineMove](1, splitLines, cursorPos); + return; + } + this[kPreviousCursorCols] = -1; + this[kHistoryNext](); + } + + // TODO(BridgeAR): Add underscores to the search part and a red background in + // case no match is found. This should only be the visual part and not the + // actual line content! + // TODO(BridgeAR): In case the substring based search is active and the end is + // reached, show a comment how to search the history as before. E.g., using + // + N. Only show this after two/three UPs or DOWNs, not on the first + // one. + [kHistoryNext]() { + if (!this.historyManager.canNavigateToNext()) { return; } + + this[kBeforeEdit](this.line, this.cursor); + this[kSetLine](this.historyManager.navigateToNext(this[kSubstringSearch])); + this.cursor = this.line.length; // Set cursor to end of line. + this[kRefreshLine](); + } + + [kMoveUpOrHistoryPrev]() { + const cursorPos = this.getCursorPos(); + if (this[kIsMultiline] && cursorPos.rows > 0) { + const splitLines = StringPrototypeSplit(this.line, '\n'); + this[kMultilineMove](-1, splitLines, cursorPos); + return; + } + this[kPreviousCursorCols] = -1; + this[kHistoryPrev](); + } + + [kHistoryPrev]() { + if (!this.historyManager.canNavigateToPrevious()) { return; } + + this[kBeforeEdit](this.line, this.cursor); + this[kSetLine](this.historyManager.navigateToPrevious(this[kSubstringSearch])); + this.cursor = this.line.length; // Set cursor to end of line. + this[kRefreshLine](); + } + + // Returns the last character's display position of the given string + [kGetDisplayPos](str) { + let offset = 0; + const col = this.columns; + let rows = 0; + str = stripVTControlCharacters(str); + + for (const char of new SafeStringIterator(str)) { + if (char === '\n') { + // Rows must be incremented by 1 even if offset = 0 or col = +Infinity. + rows += MathCeil(offset / col) || 1; + // Only add prefix offset for continuation lines in user input (not prompts) + offset = this[kIsMultiline] ? kMultilinePrompt.description.length : 0; + continue; + } + // Tabs must be aligned by an offset of the tab size. + if (char === '\t') { + offset += this.tabSize - (offset % this.tabSize); + continue; + } + const width = getStringWidth(char, false /* stripVTControlCharacters */); + if (width === 0 || width === 1) { + offset += width; + } else { + // width === 2 + if ((offset + 1) % col === 0) { + offset++; + } + offset += 2; + } + } + + const cols = offset % col; + rows += (offset - cols) / col; + + return { cols, rows }; + } + + /** + * Returns the real position of the cursor in relation + * to the input prompt + string. + * @returns {{ + * rows: number; + * cols: number; + * }} + */ + getCursorPos() { + const strBeforeCursor = this[kPrompt] + StringPrototypeSlice(this.line, 0, this.cursor); + + return this[kGetDisplayPos](strBeforeCursor); + } + + // This function moves cursor dx places to the right + // (-dx for left) and refreshes the line if it is needed. + [kMoveCursor](dx) { + if (dx === 0) { + return; + } + const oldPos = this.getCursorPos(); + this.cursor += dx; + + // Bounds check + if (this.cursor < 0) { + this.cursor = 0; + } else if (this.cursor > this.line.length) { + this.cursor = this.line.length; + } + + const newPos = this.getCursorPos(); + + // Check if cursor stayed on the line. + if (oldPos.rows === newPos.rows) { + const diffWidth = newPos.cols - oldPos.cols; + moveCursor(this.output, diffWidth, 0); + } else { + this[kRefreshLine](); + } + } + + // Handle a write from the tty + [kTtyWrite](s, key) { + const previousKey = this[kPreviousKey]; + key ||= kEmptyObject; + this[kPreviousKey] = key; + let shouldResetPreviousCursorCols = true; + + if (!key.meta || key.name !== 'y') { + // Reset yanking state unless we are doing yank pop. + this[kYanking] = false; + } + + // Activate or deactivate substring search. + if ( + (key.name === 'up' || key.name === 'down') && + !key.ctrl && + !key.meta && + !key.shift + ) { + if (this[kSubstringSearch] === null && !this[kIsMultiline]) { + this[kSubstringSearch] = StringPrototypeSlice( + this.line, + 0, + this.cursor, + ); + } + } else if (this[kSubstringSearch] !== null) { + this[kSubstringSearch] = null; + // Reset the index in case there's no match. + if (this.history.length === this.historyIndex) { + this.historyIndex = -1; + } + } + + // Undo & Redo + if (typeof key.sequence === 'string') { + switch (StringPrototypeCodePointAt(key.sequence, 0)) { + case 0x1f: + this[kUndo](); + return; + case 0x1e: + this[kRedo](); + return; + default: + break; + } + } + + // Ignore escape key, fixes + // https://github.com/nodejs/node-v0.x-archive/issues/2876. + if (key.name === 'escape') return; + + if (key.ctrl && key.shift) { + /* Control and shift pressed */ + switch (key.name) { + // TODO(BridgeAR): The transmitted escape sequence is `\b` and that is + // identical to -h. It should have a unique escape sequence. + case 'backspace': + this[kDeleteLineLeft](); + break; + + case 'delete': + this[kDeleteLineRight](); + break; + } + } else if (key.ctrl) { + /* Control key pressed */ + + switch (key.name) { + case 'c': + if (this.listenerCount('SIGINT') > 0) { + this.emit('SIGINT'); + } else { + // This readline instance is finished + this.close(); + this[kQuestionReject]?.(new AbortError('Aborted with Ctrl+C')); + } + break; + + case 'h': // delete left + this[kDeleteLeft](); + break; + + case 'd': // delete right or EOF + if (this.cursor === 0 && this.line.length === 0) { + // This readline instance is finished + this.close(); + this[kQuestionReject]?.(new AbortError('Aborted with Ctrl+D')); + } else if (this.cursor < this.line.length) { + this[kDeleteRight](); + } + break; + + case 'u': // Delete from current to start of line + this[kDeleteLineLeft](); + break; + + case 'k': // Delete from current to end of line + this[kDeleteLineRight](); + break; + + case 'a': // Go to the start of the line + this[kMoveCursor](-Infinity); + break; + + case 'e': // Go to the end of the line + this[kMoveCursor](+Infinity); + break; + + case 'b': // back one character + this[kMoveCursor](-charLengthLeft(this.line, this.cursor)); + break; + + case 'f': // Forward one character + this[kMoveCursor](+charLengthAt(this.line, this.cursor)); + break; + + case 'l': // Clear the whole screen + cursorTo(this.output, 0, 0); + clearScreenDown(this.output); + this[kRefreshLine](); + break; + + case 'n': // next history item + this[kHistoryNext](); + break; + + case 'p': // Previous history item + this[kHistoryPrev](); + break; + + case 'y': // Yank killed string + this[kYank](); + break; + + case 'z': + if (process.platform === 'win32') break; + if (this.listenerCount('SIGTSTP') > 0) { + this.emit('SIGTSTP'); + } else { + process.once('SIGCONT', () => { + // Don't raise events if stream has already been abandoned. + if (!this.paused) { + // Stream must be paused and resumed after SIGCONT to catch + // SIGINT, SIGTSTP, and EOF. + this.pause(); + this.emit('SIGCONT'); + } + // Explicitly re-enable "raw mode" and move the cursor to + // the correct position. + // See https://github.com/joyent/node/issues/3295. + this[kSetRawMode](true); + this[kRefreshLine](); + }); + this[kSetRawMode](false); + process.kill(process.pid, 'SIGTSTP'); + } + break; + + case 'w': // Delete backwards to a word boundary + // TODO(BridgeAR): The transmitted escape sequence is `\b` and that is + // identical to -h. It should have a unique escape sequence. + // Falls through + case 'backspace': + this[kDeleteWordLeft](); + break; + + case 'delete': // Delete forward to a word boundary + this[kDeleteWordRight](); + break; + + case 'left': + this[kWordLeft](); + break; + + case 'right': + this[kWordRight](); + break; + } + } else if (key.meta) { + /* Meta key pressed */ + + switch (key.name) { + case 'b': // backward word + this[kWordLeft](); + break; + + case 'f': // forward word + this[kWordRight](); + break; + + case 'd': // delete forward word + case 'delete': + this[kDeleteWordRight](); + break; + + case 'backspace': // Delete backwards to a word boundary + this[kDeleteWordLeft](); + break; + + case 'y': // Doing yank pop + this[kYankPop](); + break; + } + } else { + /* No modifier keys used */ + + // \r bookkeeping is only relevant if a \n comes right after. + if (this[kSawReturnAt] && key.name !== 'enter') this[kSawReturnAt] = 0; + + switch (key.name) { + case 'return': // Carriage return, i.e. \r + this[kSawReturnAt] = DateNow(); + this[kLine](); + break; + + case 'enter': + // When key interval > crlfDelay + if ( + this[kSawReturnAt] === 0 || + DateNow() - this[kSawReturnAt] > this.crlfDelay + ) { + this[kLine](); + } + this[kSawReturnAt] = 0; + break; + + case 'backspace': + this[kDeleteLeft](); + break; + + case 'delete': + this[kDeleteRight](); + break; + + case 'left': + // Obtain the code point to the left + this[kMoveCursor](-charLengthLeft(this.line, this.cursor)); + break; + + case 'right': + this[kMoveCursor](+charLengthAt(this.line, this.cursor)); + break; + + case 'home': + this[kMoveCursor](-Infinity); + break; + + case 'end': + this[kMoveCursor](+Infinity); + break; + + case 'up': + shouldResetPreviousCursorCols = false; + this[kMoveUpOrHistoryPrev](); + break; + + case 'down': + shouldResetPreviousCursorCols = false; + this[kMoveDownOrHistoryNext](); + break; + + case 'tab': + // If tab completion enabled, do that... + if ( + typeof this.completer === 'function' && + this.isCompletionEnabled + ) { + const lastKeypressWasTab = + previousKey && previousKey.name === 'tab'; + this[kTabComplete](lastKeypressWasTab); + break; + } + // falls through + default: + if (typeof s === 'string' && s) { + // Erase state of previous searches. + lineEnding.lastIndex = 0; + let nextMatch; + // Keep track of the end of the last match. + let lastIndex = 0; + while ((nextMatch = RegExpPrototypeExec(lineEnding, s)) !== null) { + this[kInsertString](StringPrototypeSlice(s, lastIndex, nextMatch.index)); + ({ lastIndex } = lineEnding); + this[kLine](); + // Restore lastIndex as the call to kLine could have mutated it. + lineEnding.lastIndex = lastIndex; + } + // This ensures that the last line is written if it doesn't end in a newline. + // Note that the last line may be the first line, in which case this still works. + this[kInsertString](StringPrototypeSlice(s, lastIndex)); + } + } + } + if (shouldResetPreviousCursorCols) { + this[kPreviousCursorCols] = -1; + } + } + + /** + * Creates an `AsyncIterator` object that iterates through + * each line in the input stream as a string. + * @returns {AsyncIterableIterator} + */ + [SymbolAsyncIterator]() { + if (this[kLineObjectStream] === undefined) { + kFirstEventParam ??= Symbol.for("nodejs.kFirstEventParam"); + this[kLineObjectStream] = EventEmitter.on( + this, 'line', { + close: ['close'], + highWaterMark: 1024, + [kFirstEventParam]: true, + }); + } + return this[kLineObjectStream]; + } +} +Interface.prototype[SymbolDispose] = assignFunctionName(SymbolDispose, function() { + this.close(); +}); + +__node_module__.exports = { + Interface, + InterfaceConstructor, + kAddHistory, + kDecoder, + kDeleteLeft, + kDeleteLineLeft, + kDeleteLineRight, + kDeleteRight, + kDeleteWordLeft, + kDeleteWordRight, + kGetDisplayPos, + kHistoryNext, + kHistoryPrev, + kInsertString, + kIsMultiline, + kLine, + kLine_buffer, + kMoveCursor, + kNormalWrite, + kOldPrompt, + kOnLine, + kSetLine, + kPreviousKey, + kPrompt, + kQuestion, + kQuestionCallback, + kQuestionCancel, + kQuestionReject, + kRefreshLine, + kSawKeyPress, + kSawReturnAt, + kSetRawMode, + kTabComplete, + kTabCompleter, + kTtyWrite, + kWordLeft, + kWordRight, + kWriteToOutput, + kMultilinePrompt, + kRestorePreviousState, + kAddNewLineOnTTY, + kLastCommandErrored, +}; + +export default __node_module__.exports; diff --git a/src/js/internal/readline/promises.js b/src/js/internal/readline/promises.js new file mode 100644 index 000000000000..b88abc692f2e --- /dev/null +++ b/src/js/internal/readline/promises.js @@ -0,0 +1,151 @@ +// Ported from Node.js v26.3.0 lib/internal/readline/promises.js for Bun's node:repl. +// Attribution: derived from Node.js, MIT licensed (Node.js contributors). +// prettier-ignore +const primordials = require("internal/repl/node-primordials"); +var __node_module__ = { exports: {} }; +'use strict'; + +const { + ArrayPrototypeJoin, + ArrayPrototypePush, + Promise, +} = primordials; + +const { CSI } = require("internal/readline/utils"); +const { validateBoolean, validateInteger } = require("internal/validators"); +const { isWritable } = require("internal/repl/node-shims"); +const { codes: { + ERR_INVALID_ARG_TYPE, +} } = require("internal/repl/node-errors"); + +const { + kClearToLineBeginning, + kClearToLineEnd, + kClearLine, + kClearScreenDown, +} = CSI; + +class Readline { + #autoCommit = false; + #stream; + #todo = []; + + constructor(stream, options = undefined) { + if (!isWritable(stream)) + throw new ERR_INVALID_ARG_TYPE('stream', 'Writable', stream); + this.#stream = stream; + if (options?.autoCommit != null) { + validateBoolean(options.autoCommit, 'options.autoCommit'); + this.#autoCommit = options.autoCommit; + } + } + + /** + * Moves the cursor to the x and y coordinate on the given stream. + * @param {integer} x + * @param {integer} [y] + * @returns {Readline} this + */ + cursorTo(x, y = undefined) { + validateInteger(x, 'x'); + if (y != null) validateInteger(y, 'y'); + + const data = y == null ? CSI`${x + 1}G` : CSI`${y + 1};${x + 1}H`; + if (this.#autoCommit) process.nextTick(() => this.#stream.write(data)); + else ArrayPrototypePush(this.#todo, data); + + return this; + } + + /** + * Moves the cursor relative to its current location. + * @param {integer} dx + * @param {integer} dy + * @returns {Readline} this + */ + moveCursor(dx, dy) { + if (dx || dy) { + validateInteger(dx, 'dx'); + validateInteger(dy, 'dy'); + + let data = ''; + + if (dx < 0) { + data += CSI`${-dx}D`; + } else if (dx > 0) { + data += CSI`${dx}C`; + } + + if (dy < 0) { + data += CSI`${-dy}A`; + } else if (dy > 0) { + data += CSI`${dy}B`; + } + if (this.#autoCommit) process.nextTick(() => this.#stream.write(data)); + else ArrayPrototypePush(this.#todo, data); + } + return this; + } + + /** + * Clears the current line the cursor is on. + * @param {-1|0|1} dir Direction to clear: + * -1 for left of the cursor + * +1 for right of the cursor + * 0 for the entire line + * @returns {Readline} this + */ + clearLine(dir) { + validateInteger(dir, 'dir', -1, 1); + + const data = + dir < 0 ? kClearToLineBeginning : + dir > 0 ? kClearToLineEnd : + kClearLine; + if (this.#autoCommit) process.nextTick(() => this.#stream.write(data)); + else ArrayPrototypePush(this.#todo, data); + return this; + } + + /** + * Clears the screen from the current position of the cursor down. + * @returns {Readline} this + */ + clearScreenDown() { + if (this.#autoCommit) { + process.nextTick(() => this.#stream.write(kClearScreenDown)); + } else { + ArrayPrototypePush(this.#todo, kClearScreenDown); + } + return this; + } + + /** + * Sends all the pending actions to the associated `stream` and clears the + * internal list of pending actions. + * @returns {Promise} Resolves when all pending actions have been + * flushed to the associated `stream`. + */ + commit() { + return new Promise((resolve) => { + this.#stream.write(ArrayPrototypeJoin(this.#todo, ''), resolve); + this.#todo = []; + }); + } + + /** + * Clears the internal list of pending actions without sending it to the + * associated `stream`. + * @returns {Readline} this + */ + rollback() { + this.#todo = []; + return this; + } +} + +__node_module__.exports = { + Readline, +}; + +export default __node_module__.exports; diff --git a/src/js/internal/readline/utils.js b/src/js/internal/readline/utils.js new file mode 100644 index 000000000000..6eb7a85f6b7b --- /dev/null +++ b/src/js/internal/readline/utils.js @@ -0,0 +1,428 @@ +// Ported from Node.js v26.3.0 lib/internal/readline/utils.js for Bun's node:repl. +// Attribution: derived from Node.js, MIT licensed (Node.js contributors). +// prettier-ignore +const primordials = require("internal/repl/node-primordials"); +var __node_module__ = { exports: {} }; +'use strict'; + +const { + ArrayPrototypeToSorted, + RegExpPrototypeExec, + StringFromCharCode, + StringPrototypeCharCodeAt, + StringPrototypeCodePointAt, + StringPrototypeSlice, + StringPrototypeSplit, + StringPrototypeToLowerCase, + Symbol, +} = primordials; + +const kUTF16SurrogateThreshold = 0x10000; // 2 ** 16 +const kEscape = '\x1b'; +const kSubstringSearch = Symbol('kSubstringSearch'); + +function CSI(strings, ...args) { + let ret = `${kEscape}[`; + for (let n = 0; n < strings.length; n++) { + ret += strings[n]; + if (n < args.length) + ret += args[n]; + } + return ret; +} + +CSI.kEscape = kEscape; +CSI.kClearToLineBeginning = CSI`1K`; +CSI.kClearToLineEnd = CSI`0K`; +CSI.kClearLine = CSI`2K`; +CSI.kClearScreenDown = CSI`0J`; + +// TODO(BridgeAR): Treat combined characters as single character, i.e, +// 'a\u0301' and '\u0301a' (both have the same visual output). +// Check Canonical_Combining_Class in +// http://userguide.icu-project.org/strings/properties +function charLengthLeft(str, i) { + if (i <= 0) + return 0; + if ((i > 1 && + StringPrototypeCodePointAt(str, i - 2) >= kUTF16SurrogateThreshold) || + StringPrototypeCodePointAt(str, i - 1) >= kUTF16SurrogateThreshold) { + return 2; + } + return 1; +} + +function charLengthAt(str, i) { + if (str.length <= i) { + // Pretend to move to the right. This is necessary to autocomplete while + // moving to the right. + return 1; + } + return StringPrototypeCodePointAt(str, i) >= kUTF16SurrogateThreshold ? 2 : 1; +} + +/* + Some patterns seen in terminal key escape codes, derived from combos seen + at http://www.midnight-commander.org/browser/lib/tty/key.c + + ESC letter + ESC [ letter + ESC [ modifier letter + ESC [ 1 ; modifier letter + ESC [ num char + ESC [ num ; modifier char + ESC O letter + ESC O modifier letter + ESC O 1 ; modifier letter + ESC N letter + ESC [ [ num ; modifier char + ESC [ [ 1 ; modifier letter + ESC ESC [ num char + ESC ESC O letter + + - char is usually ~ but $ and ^ also happen with rxvt + - modifier is 1 + + (shift * 1) + + (left_alt * 2) + + (ctrl * 4) + + (right_alt * 8) + - two leading ESCs apparently mean the same as one leading ESC +*/ +function* emitKeys(stream) { + while (true) { + let ch = yield; + let s = ch; + let escaped = false; + const key = { + sequence: null, + name: undefined, + ctrl: false, + meta: false, + shift: false, + }; + + if (ch === kEscape) { + escaped = true; + s += (ch = yield); + + if (ch === kEscape) { + s += (ch = yield); + } + } + + if (escaped && (ch === 'O' || ch === '[')) { + // ANSI escape sequence + let code = ch; + let modifier = 0; + + if (ch === 'O') { + // ESC O letter + // ESC O modifier letter + s += (ch = yield); + + if (ch >= '0' && ch <= '9') { + modifier = (ch >> 0) - 1; + s += (ch = yield); + } + + code += ch; + } else if (ch === '[') { + // ESC [ letter + // ESC [ modifier letter + // ESC [ [ modifier letter + // ESC [ [ num char + s += (ch = yield); + + if (ch === '[') { + // \x1b[[A + // ^--- escape codes might have a second bracket + code += ch; + s += (ch = yield); + } + + /* + * Here and later we try to buffer just enough data to get + * a complete ascii sequence. + * + * We have basically two classes of ascii characters to process: + * + * + * 1. `\x1b[24;5~` should be parsed as { code: '[24~', modifier: 5 } + * + * This particular example is featuring Ctrl+F12 in xterm. + * + * - `;5` part is optional, e.g. it could be `\x1b[24~` + * - first part can contain one or two digits + * - there is also special case when there can be 3 digits + * but without modifier. They are the case of paste bracket mode + * + * So the generic regexp is like /^(?:\d\d?(;\d)?[~^$]|\d{3}~)$/ + * + * + * 2. `\x1b[1;5H` should be parsed as { code: '[H', modifier: 5 } + * + * This particular example is featuring Ctrl+Home in xterm. + * + * - `1;5` part is optional, e.g. it could be `\x1b[H` + * - `1;` part is optional, e.g. it could be `\x1b[5H` + * + * So the generic regexp is like /^((\d;)?\d)?[A-Za-z]$/ + * + */ + const cmdStart = s.length - 1; + + // Skip one or two leading digits + if (ch >= '0' && ch <= '9') { + s += (ch = yield); + + if (ch >= '0' && ch <= '9') { + s += (ch = yield); + + if (ch >= '0' && ch <= '9') { + s += (ch = yield); + } + } + } + + // skip modifier + if (ch === ';') { + s += (ch = yield); + + if (ch >= '0' && ch <= '9') { + s += yield; + } + } + + /* + * We buffered enough data, now trying to extract code + * and modifier from it + */ + const cmd = StringPrototypeSlice(s, cmdStart); + let match; + + if ((match = RegExpPrototypeExec(/^(?:(\d\d?)(?:;(\d))?([~^$])|(\d{3}~))$/, cmd))) { + if (match[4]) { + code += match[4]; + } else { + code += match[1] + match[3]; + modifier = (match[2] || 1) - 1; + } + } else if ( + (match = RegExpPrototypeExec(/^((\d;)?(\d))?([A-Za-z])$/, cmd)) + ) { + code += match[4]; + modifier = (match[3] || 1) - 1; + } else { + code += cmd; + } + } + + // Parse the key modifier + key.ctrl = !!(modifier & 4); + key.meta = !!(modifier & 10); + key.shift = !!(modifier & 1); + key.code = code; + + // Parse the key itself + switch (code) { + /* xterm/gnome ESC [ letter (with modifier) */ + case '[P': key.name = 'f1'; break; + case '[Q': key.name = 'f2'; break; + case '[R': key.name = 'f3'; break; + case '[S': key.name = 'f4'; break; + + /* xterm/gnome ESC O letter (without modifier) */ + case 'OP': key.name = 'f1'; break; + case 'OQ': key.name = 'f2'; break; + case 'OR': key.name = 'f3'; break; + case 'OS': key.name = 'f4'; break; + + /* xterm/rxvt ESC [ number ~ */ + case '[11~': key.name = 'f1'; break; + case '[12~': key.name = 'f2'; break; + case '[13~': key.name = 'f3'; break; + case '[14~': key.name = 'f4'; break; + + /* paste bracket mode */ + case '[200~': key.name = 'paste-start'; break; + case '[201~': key.name = 'paste-end'; break; + + /* from Cygwin and used in libuv */ + case '[[A': key.name = 'f1'; break; + case '[[B': key.name = 'f2'; break; + case '[[C': key.name = 'f3'; break; + case '[[D': key.name = 'f4'; break; + case '[[E': key.name = 'f5'; break; + + /* common */ + case '[15~': key.name = 'f5'; break; + case '[17~': key.name = 'f6'; break; + case '[18~': key.name = 'f7'; break; + case '[19~': key.name = 'f8'; break; + case '[20~': key.name = 'f9'; break; + case '[21~': key.name = 'f10'; break; + case '[23~': key.name = 'f11'; break; + case '[24~': key.name = 'f12'; break; + + /* xterm ESC [ letter */ + case '[A': key.name = 'up'; break; + case '[B': key.name = 'down'; break; + case '[C': key.name = 'right'; break; + case '[D': key.name = 'left'; break; + case '[E': key.name = 'clear'; break; + case '[F': key.name = 'end'; break; + case '[H': key.name = 'home'; break; + + /* xterm/gnome ESC O letter */ + case 'OA': key.name = 'up'; break; + case 'OB': key.name = 'down'; break; + case 'OC': key.name = 'right'; break; + case 'OD': key.name = 'left'; break; + case 'OE': key.name = 'clear'; break; + case 'OF': key.name = 'end'; break; + case 'OH': key.name = 'home'; break; + + /* xterm/rxvt ESC [ number ~ */ + case '[1~': key.name = 'home'; break; + case '[2~': key.name = 'insert'; break; + case '[3~': key.name = 'delete'; break; + case '[4~': key.name = 'end'; break; + case '[5~': key.name = 'pageup'; break; + case '[6~': key.name = 'pagedown'; break; + + /* putty */ + case '[[5~': key.name = 'pageup'; break; + case '[[6~': key.name = 'pagedown'; break; + + /* rxvt */ + case '[7~': key.name = 'home'; break; + case '[8~': key.name = 'end'; break; + + /* rxvt keys with modifiers */ + case '[a': key.name = 'up'; key.shift = true; break; + case '[b': key.name = 'down'; key.shift = true; break; + case '[c': key.name = 'right'; key.shift = true; break; + case '[d': key.name = 'left'; key.shift = true; break; + case '[e': key.name = 'clear'; key.shift = true; break; + + case '[2$': key.name = 'insert'; key.shift = true; break; + case '[3$': key.name = 'delete'; key.shift = true; break; + case '[5$': key.name = 'pageup'; key.shift = true; break; + case '[6$': key.name = 'pagedown'; key.shift = true; break; + case '[7$': key.name = 'home'; key.shift = true; break; + case '[8$': key.name = 'end'; key.shift = true; break; + + case 'Oa': key.name = 'up'; key.ctrl = true; break; + case 'Ob': key.name = 'down'; key.ctrl = true; break; + case 'Oc': key.name = 'right'; key.ctrl = true; break; + case 'Od': key.name = 'left'; key.ctrl = true; break; + case 'Oe': key.name = 'clear'; key.ctrl = true; break; + + case '[2^': key.name = 'insert'; key.ctrl = true; break; + case '[3^': key.name = 'delete'; key.ctrl = true; break; + case '[5^': key.name = 'pageup'; key.ctrl = true; break; + case '[6^': key.name = 'pagedown'; key.ctrl = true; break; + case '[7^': key.name = 'home'; key.ctrl = true; break; + case '[8^': key.name = 'end'; key.ctrl = true; break; + + /* misc. */ + case '[Z': key.name = 'tab'; key.shift = true; break; + default: key.name = 'undefined'; break; + } + } else if (ch === '\r') { + // carriage return + key.name = 'return'; + key.meta = escaped; + } else if (ch === '\n') { + // Enter, should have been called linefeed + key.name = 'enter'; + key.meta = escaped; + } else if (ch === '\t') { + // tab + key.name = 'tab'; + key.meta = escaped; + } else if (ch === '\b' || ch === '\x7f') { + // backspace or ctrl+h + key.name = 'backspace'; + key.meta = escaped; + } else if (ch === kEscape) { + // escape key + key.name = 'escape'; + key.meta = escaped; + } else if (ch === ' ') { + key.name = 'space'; + key.meta = escaped; + } else if (!escaped && ch <= '\x1a') { + // ctrl+letter + key.name = StringFromCharCode( + StringPrototypeCharCodeAt(ch) + StringPrototypeCharCodeAt('a') - 1, + ); + key.ctrl = true; + } else if (RegExpPrototypeExec(/^[0-9A-Za-z]$/, ch) !== null) { + // Letter, number, shift+letter + key.name = StringPrototypeToLowerCase(ch); + key.shift = RegExpPrototypeExec(/^[A-Z]$/, ch) !== null; + key.meta = escaped; + } else if (escaped) { + // Escape sequence timeout + key.name = ch.length ? undefined : 'escape'; + key.meta = true; + } + + key.sequence = s; + + if (s.length !== 0 && (key.name !== undefined || escaped)) { + /* Named character or sequence */ + stream.emit('keypress', escaped ? undefined : s, key); + } else if (charLengthAt(s, 0) === s.length) { + /* Single unnamed character, e.g. "." */ + stream.emit('keypress', s, key); + } + /* Unrecognized or broken escape sequence, don't emit anything */ + } +} + +// This runs in O(n log n). +function commonPrefix(strings) { + if (strings.length === 0) { + return ''; + } + if (strings.length === 1) { + return strings[0]; + } + const sorted = ArrayPrototypeToSorted(strings); + const min = sorted[0]; + const max = sorted[sorted.length - 1]; + for (let i = 0; i < min.length; i++) { + if (min[i] !== max[i]) { + return StringPrototypeSlice(min, 0, i); + } + } + return min; +} + +function reverseString(line, from = '\r', to = '\r') { + const parts = StringPrototypeSplit(line, from); + + // This implementation should be faster than + // ArrayPrototypeJoin(ArrayPrototypeReverse(StringPrototypeSplit(line, from)), to); + let result = ''; + for (let i = parts.length - 1; i > 0; i--) { + result += parts[i] + to; + } + result += parts[0]; + + return result; +} + +__node_module__.exports = { + charLengthAt, + charLengthLeft, + commonPrefix, + emitKeys, + reverseString, + kSubstringSearch, + CSI, +}; + +export default __node_module__.exports; diff --git a/src/js/internal/repl.js b/src/js/internal/repl.js new file mode 100644 index 000000000000..90cc29772170 --- /dev/null +++ b/src/js/internal/repl.js @@ -0,0 +1,67 @@ +// Ported from Node.js v26.3.0 lib/internal/repl.js for Bun's node:repl. +// Attribution: derived from Node.js, MIT licensed (Node.js contributors). +// prettier-ignore +const primordials = require("internal/repl/node-primordials"); +var __node_module__ = { exports: {} }; +'use strict'; + +const { + Number, + NumberIsNaN, + NumberParseInt, +} = primordials; + +const REPL = require("node:repl"); +const { kStandaloneREPL } = require("internal/repl/utils"); + +__node_module__.exports = { __proto__: REPL }; +__node_module__.exports.createInternalRepl = createRepl; + +function createRepl(env, opts, cb) { + if (typeof opts === 'function') { + cb = opts; + opts = null; + } + opts = { + [kStandaloneREPL]: true, + ignoreUndefined: false, + useGlobal: true, + breakEvalOnSigint: true, + ...opts, + }; + + if (NumberParseInt(env.NODE_NO_READLINE)) { + opts.terminal = false; + } + + if (env.NODE_REPL_MODE) { + opts.replMode = { + 'strict': REPL.REPL_MODE_STRICT, + 'sloppy': REPL.REPL_MODE_SLOPPY, + }[env.NODE_REPL_MODE.toLowerCase().trim()]; + } + + if (opts.replMode === undefined) { + opts.replMode = REPL.REPL_MODE_SLOPPY; + } + + const size = Number(env.NODE_REPL_HISTORY_SIZE); + if (!NumberIsNaN(size) && size > 0) { + opts.size = size; + } else { + opts.size = 1000; + } + + const term = 'terminal' in opts ? opts.terminal : process.stdout.isTTY; + opts.filePath = term ? env.NODE_REPL_HISTORY : ''; + + const repl = REPL.start(opts); + + repl.setupHistory({ + filePath: opts.filePath, + size: opts.size, + onHistoryFileLoaded: cb, + }); +} + +export default __node_module__.exports; diff --git a/src/js/internal/repl/acorn-walk.js b/src/js/internal/repl/acorn-walk.js new file mode 100644 index 000000000000..2c0dc5f240af --- /dev/null +++ b/src/js/internal/repl/acorn-walk.js @@ -0,0 +1,11 @@ +// Vendored from Node.js v26.3.0 deps (acorn-walk.js, MIT licensed). +// The dist uses ES5 function+prototype constructors, which JSC builtin +// semantics forbid (builtin functions are non-constructors). Evaluate the +// source via vm.Script so it runs with full JavaScript semantics. +// prettier-ignore +const vm = require("node:vm"); +const exportsObj = {}; +const moduleObj = { exports: exportsObj }; +const factory = new vm.Script("(function(exports, module){(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n typeof define === 'function' && define.amd ? define(['exports'], factory) :\n (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.acorn = global.acorn || {}, global.acorn.walk = {})));\n})(this, (function (exports) { 'use strict';\n\n // AST walker module for ESTree compatible trees\n\n // A simple walk is one where you simply specify callbacks to be\n // called on specific nodes. The last two arguments are optional. A\n // simple use would be\n //\n // walk.simple(myTree, {\n // Expression: function(node) { ... }\n // });\n //\n // to do something with all expressions. All ESTree node types\n // can be used to identify node types, as well as Expression and\n // Statement, which denote categories of nodes.\n //\n // The base argument can be used to pass a custom (recursive)\n // walker, and state can be used to give this walked an initial\n // state.\n\n function simple(node, visitors, baseVisitor, state, override) {\n if (!baseVisitor) { baseVisitor = base\n ; }(function c(node, st, override) {\n var type = override || node.type;\n visitNode(baseVisitor, type, node, st, c);\n if (visitors[type]) { visitors[type](node, st); }\n })(node, state, override);\n }\n\n // An ancestor walk keeps an array of ancestor nodes (including the\n // current node) and passes them to the callback as third parameter\n // (and also as state parameter when no other state is present).\n function ancestor(node, visitors, baseVisitor, state, override) {\n var ancestors = [];\n if (!baseVisitor) { baseVisitor = base\n ; }(function c(node, st, override) {\n var type = override || node.type;\n var isNew = node !== ancestors[ancestors.length - 1];\n if (isNew) { ancestors.push(node); }\n visitNode(baseVisitor, type, node, st, c);\n if (visitors[type]) { visitors[type](node, st || ancestors, ancestors); }\n if (isNew) { ancestors.pop(); }\n })(node, state, override);\n }\n\n // A recursive walk is one where your functions override the default\n // walkers. They can modify and replace the state parameter that's\n // threaded through the walk, and can opt how and whether to walk\n // their child nodes (by calling their third argument on these\n // nodes).\n function recursive(node, state, funcs, baseVisitor, override) {\n var visitor = funcs ? make(funcs, baseVisitor || undefined) : baseVisitor\n ;(function c(node, st, override) {\n visitor[override || node.type](node, st, c);\n })(node, state, override);\n }\n\n function makeTest(test) {\n if (typeof test === \"string\")\n { return function (type) { return type === test; } }\n else if (!test)\n { return function () { return true; } }\n else\n { return test }\n }\n\n var Found = function Found(node, state) { this.node = node; this.state = state; };\n\n // A full walk triggers the callback on each node\n function full(node, callback, baseVisitor, state, override) {\n if (!baseVisitor) { baseVisitor = base; }\n var last\n ;(function c(node, st, override) {\n var type = override || node.type;\n visitNode(baseVisitor, type, node, st, c);\n if (last !== node) {\n callback(node, st, type);\n last = node;\n }\n })(node, state, override);\n }\n\n // An fullAncestor walk is like an ancestor walk, but triggers\n // the callback on each node\n function fullAncestor(node, callback, baseVisitor, state) {\n if (!baseVisitor) { baseVisitor = base; }\n var ancestors = [], last\n ;(function c(node, st, override) {\n var type = override || node.type;\n var isNew = node !== ancestors[ancestors.length - 1];\n if (isNew) { ancestors.push(node); }\n visitNode(baseVisitor, type, node, st, c);\n if (last !== node) {\n callback(node, st || ancestors, ancestors, type);\n last = node;\n }\n if (isNew) { ancestors.pop(); }\n })(node, state);\n }\n\n // Find a node with a given start, end, and type (all are optional,\n // null can be used as wildcard). Returns a {node, state} object, or\n // undefined when it doesn't find a matching node.\n function findNodeAt(node, start, end, test, baseVisitor, state) {\n if (!baseVisitor) { baseVisitor = base; }\n test = makeTest(test);\n try {\n (function c(node, st, override) {\n var type = override || node.type;\n if ((start == null || node.start <= start) &&\n (end == null || node.end >= end))\n { visitNode(baseVisitor, type, node, st, c); }\n if ((start == null || node.start === start) &&\n (end == null || node.end === end) &&\n test(type, node))\n { throw new Found(node, st) }\n })(node, state);\n } catch (e) {\n if (e instanceof Found) { return e }\n throw e\n }\n }\n\n // Find the innermost node of a given type that contains the given\n // position. Interface similar to findNodeAt.\n function findNodeAround(node, pos, test, baseVisitor, state) {\n test = makeTest(test);\n if (!baseVisitor) { baseVisitor = base; }\n try {\n (function c(node, st, override) {\n var type = override || node.type;\n if (node.start > pos || node.end < pos) { return }\n visitNode(baseVisitor, type, node, st, c);\n if (test(type, node)) { throw new Found(node, st) }\n })(node, state);\n } catch (e) {\n if (e instanceof Found) { return e }\n throw e\n }\n }\n\n // Find the outermost matching node after a given position.\n function findNodeAfter(node, pos, test, baseVisitor, state) {\n test = makeTest(test);\n if (!baseVisitor) { baseVisitor = base; }\n try {\n (function c(node, st, override) {\n if (node.end < pos) { return }\n var type = override || node.type;\n if (node.start >= pos && test(type, node)) { throw new Found(node, st) }\n visitNode(baseVisitor, type, node, st, c);\n })(node, state);\n } catch (e) {\n if (e instanceof Found) { return e }\n throw e\n }\n }\n\n // Find the outermost matching node before a given position.\n function findNodeBefore(node, pos, test, baseVisitor, state) {\n test = makeTest(test);\n if (!baseVisitor) { baseVisitor = base; }\n var max\n ;(function c(node, st, override) {\n if (node.start > pos) { return }\n var type = override || node.type;\n if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))\n { max = new Found(node, st); }\n visitNode(baseVisitor, type, node, st, c);\n })(node, state);\n return max\n }\n\n // Used to create a custom walker. Will fill in all missing node\n // type properties with the defaults.\n function make(funcs, baseVisitor) {\n var visitor = Object.create(baseVisitor || base);\n for (var type in funcs) { visitor[type] = funcs[type]; }\n return visitor\n }\n\n function skipThrough(node, st, c) { c(node, st); }\n function ignore(_node, _st, _c) {}\n\n function visitNode(baseVisitor, type, node, st, c) {\n if (baseVisitor[type] == null) { throw new Error((\"No walker function defined for node type \" + type)) }\n baseVisitor[type](node, st, c);\n }\n\n // Node walkers.\n\n var base = {};\n\n base.Program = base.BlockStatement = base.StaticBlock = function (node, st, c) {\n for (var i = 0, list = node.body; i < list.length; i += 1)\n {\n var stmt = list[i];\n\n c(stmt, st, \"Statement\");\n }\n };\n base.Statement = skipThrough;\n base.EmptyStatement = ignore;\n base.ExpressionStatement = base.ParenthesizedExpression = base.ChainExpression =\n function (node, st, c) { return c(node.expression, st, \"Expression\"); };\n base.IfStatement = function (node, st, c) {\n c(node.test, st, \"Expression\");\n c(node.consequent, st, \"Statement\");\n if (node.alternate) { c(node.alternate, st, \"Statement\"); }\n };\n base.LabeledStatement = function (node, st, c) { return c(node.body, st, \"Statement\"); };\n base.BreakStatement = base.ContinueStatement = ignore;\n base.WithStatement = function (node, st, c) {\n c(node.object, st, \"Expression\");\n c(node.body, st, \"Statement\");\n };\n base.SwitchStatement = function (node, st, c) {\n c(node.discriminant, st, \"Expression\");\n for (var i = 0, list = node.cases; i < list.length; i += 1) {\n var cs = list[i];\n\n c(cs, st);\n }\n };\n base.SwitchCase = function (node, st, c) {\n if (node.test) { c(node.test, st, \"Expression\"); }\n for (var i = 0, list = node.consequent; i < list.length; i += 1)\n {\n var cons = list[i];\n\n c(cons, st, \"Statement\");\n }\n };\n base.ReturnStatement = base.YieldExpression = base.AwaitExpression = function (node, st, c) {\n if (node.argument) { c(node.argument, st, \"Expression\"); }\n };\n base.ThrowStatement = base.SpreadElement =\n function (node, st, c) { return c(node.argument, st, \"Expression\"); };\n base.TryStatement = function (node, st, c) {\n c(node.block, st, \"Statement\");\n if (node.handler) { c(node.handler, st); }\n if (node.finalizer) { c(node.finalizer, st, \"Statement\"); }\n };\n base.CatchClause = function (node, st, c) {\n if (node.param) { c(node.param, st, \"Pattern\"); }\n c(node.body, st, \"Statement\");\n };\n base.WhileStatement = base.DoWhileStatement = function (node, st, c) {\n c(node.test, st, \"Expression\");\n c(node.body, st, \"Statement\");\n };\n base.ForStatement = function (node, st, c) {\n if (node.init) { c(node.init, st, \"ForInit\"); }\n if (node.test) { c(node.test, st, \"Expression\"); }\n if (node.update) { c(node.update, st, \"Expression\"); }\n c(node.body, st, \"Statement\");\n };\n base.ForInStatement = base.ForOfStatement = function (node, st, c) {\n c(node.left, st, \"ForInit\");\n c(node.right, st, \"Expression\");\n c(node.body, st, \"Statement\");\n };\n base.ForInit = function (node, st, c) {\n if (node.type === \"VariableDeclaration\") { c(node, st); }\n else { c(node, st, \"Expression\"); }\n };\n base.DebuggerStatement = ignore;\n\n base.FunctionDeclaration = function (node, st, c) { return c(node, st, \"Function\"); };\n base.VariableDeclaration = function (node, st, c) {\n for (var i = 0, list = node.declarations; i < list.length; i += 1)\n {\n var decl = list[i];\n\n c(decl, st);\n }\n };\n base.VariableDeclarator = function (node, st, c) {\n c(node.id, st, \"Pattern\");\n if (node.init) { c(node.init, st, \"Expression\"); }\n };\n\n base.Function = function (node, st, c) {\n if (node.id) { c(node.id, st, \"Pattern\"); }\n for (var i = 0, list = node.params; i < list.length; i += 1)\n {\n var param = list[i];\n\n c(param, st, \"Pattern\");\n }\n c(node.body, st, node.expression ? \"Expression\" : \"Statement\");\n };\n\n base.Pattern = function (node, st, c) {\n if (node.type === \"Identifier\")\n { c(node, st, \"VariablePattern\"); }\n else if (node.type === \"MemberExpression\")\n { c(node, st, \"MemberPattern\"); }\n else\n { c(node, st); }\n };\n base.VariablePattern = ignore;\n base.MemberPattern = skipThrough;\n base.RestElement = function (node, st, c) { return c(node.argument, st, \"Pattern\"); };\n base.ArrayPattern = function (node, st, c) {\n for (var i = 0, list = node.elements; i < list.length; i += 1) {\n var elt = list[i];\n\n if (elt) { c(elt, st, \"Pattern\"); }\n }\n };\n base.ObjectPattern = function (node, st, c) {\n for (var i = 0, list = node.properties; i < list.length; i += 1) {\n var prop = list[i];\n\n if (prop.type === \"Property\") {\n if (prop.computed) { c(prop.key, st, \"Expression\"); }\n c(prop.value, st, \"Pattern\");\n } else if (prop.type === \"RestElement\") {\n c(prop.argument, st, \"Pattern\");\n }\n }\n };\n\n base.Expression = skipThrough;\n base.ThisExpression = base.Super = base.MetaProperty = ignore;\n base.ArrayExpression = function (node, st, c) {\n for (var i = 0, list = node.elements; i < list.length; i += 1) {\n var elt = list[i];\n\n if (elt) { c(elt, st, \"Expression\"); }\n }\n };\n base.ObjectExpression = function (node, st, c) {\n for (var i = 0, list = node.properties; i < list.length; i += 1)\n {\n var prop = list[i];\n\n c(prop, st);\n }\n };\n base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;\n base.SequenceExpression = function (node, st, c) {\n for (var i = 0, list = node.expressions; i < list.length; i += 1)\n {\n var expr = list[i];\n\n c(expr, st, \"Expression\");\n }\n };\n base.TemplateLiteral = function (node, st, c) {\n for (var i = 0, list = node.quasis; i < list.length; i += 1)\n {\n var quasi = list[i];\n\n c(quasi, st);\n }\n\n for (var i$1 = 0, list$1 = node.expressions; i$1 < list$1.length; i$1 += 1)\n {\n var expr = list$1[i$1];\n\n c(expr, st, \"Expression\");\n }\n };\n base.TemplateElement = ignore;\n base.UnaryExpression = base.UpdateExpression = function (node, st, c) {\n c(node.argument, st, \"Expression\");\n };\n base.BinaryExpression = base.LogicalExpression = function (node, st, c) {\n c(node.left, st, \"Expression\");\n c(node.right, st, \"Expression\");\n };\n base.AssignmentExpression = base.AssignmentPattern = function (node, st, c) {\n c(node.left, st, \"Pattern\");\n c(node.right, st, \"Expression\");\n };\n base.ConditionalExpression = function (node, st, c) {\n c(node.test, st, \"Expression\");\n c(node.consequent, st, \"Expression\");\n c(node.alternate, st, \"Expression\");\n };\n base.NewExpression = base.CallExpression = function (node, st, c) {\n c(node.callee, st, \"Expression\");\n if (node.arguments)\n { for (var i = 0, list = node.arguments; i < list.length; i += 1)\n {\n var arg = list[i];\n\n c(arg, st, \"Expression\");\n } }\n };\n base.MemberExpression = function (node, st, c) {\n c(node.object, st, \"Expression\");\n if (node.computed) { c(node.property, st, \"Expression\"); }\n };\n base.ExportNamedDeclaration = base.ExportDefaultDeclaration = function (node, st, c) {\n if (node.declaration)\n { c(node.declaration, st, node.type === \"ExportNamedDeclaration\" || node.declaration.id ? \"Statement\" : \"Expression\"); }\n if (node.source) { c(node.source, st, \"Expression\"); }\n if (node.attributes)\n { for (var i = 0, list = node.attributes; i < list.length; i += 1)\n {\n var attr = list[i];\n\n c(attr, st);\n } }\n };\n base.ExportAllDeclaration = function (node, st, c) {\n if (node.exported)\n { c(node.exported, st); }\n c(node.source, st, \"Expression\");\n if (node.attributes)\n { for (var i = 0, list = node.attributes; i < list.length; i += 1)\n {\n var attr = list[i];\n\n c(attr, st);\n } }\n };\n base.ImportAttribute = function (node, st, c) {\n c(node.value, st, \"Expression\");\n };\n base.ImportDeclaration = function (node, st, c) {\n for (var i = 0, list = node.specifiers; i < list.length; i += 1)\n {\n var spec = list[i];\n\n c(spec, st);\n }\n c(node.source, st, \"Expression\");\n if (node.attributes)\n { for (var i$1 = 0, list$1 = node.attributes; i$1 < list$1.length; i$1 += 1)\n {\n var attr = list$1[i$1];\n\n c(attr, st);\n } }\n };\n base.ImportExpression = function (node, st, c) {\n c(node.source, st, \"Expression\");\n if (node.options) { c(node.options, st, \"Expression\"); }\n };\n base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.PrivateIdentifier = base.Literal = ignore;\n\n base.TaggedTemplateExpression = function (node, st, c) {\n c(node.tag, st, \"Expression\");\n c(node.quasi, st, \"Expression\");\n };\n base.ClassDeclaration = base.ClassExpression = function (node, st, c) { return c(node, st, \"Class\"); };\n base.Class = function (node, st, c) {\n if (node.id) { c(node.id, st, \"Pattern\"); }\n if (node.superClass) { c(node.superClass, st, \"Expression\"); }\n c(node.body, st);\n };\n base.ClassBody = function (node, st, c) {\n for (var i = 0, list = node.body; i < list.length; i += 1)\n {\n var elt = list[i];\n\n c(elt, st);\n }\n };\n base.MethodDefinition = base.PropertyDefinition = base.Property = function (node, st, c) {\n if (node.computed) { c(node.key, st, \"Expression\"); }\n if (node.value) { c(node.value, st, \"Expression\"); }\n };\n\n exports.ancestor = ancestor;\n exports.base = base;\n exports.findNodeAfter = findNodeAfter;\n exports.findNodeAround = findNodeAround;\n exports.findNodeAt = findNodeAt;\n exports.findNodeBefore = findNodeBefore;\n exports.full = full;\n exports.fullAncestor = fullAncestor;\n exports.make = make;\n exports.recursive = recursive;\n exports.simple = simple;\n\n}));\n\n})", { filename: "acorn-walk.js" }).runInThisContext(); +factory(exportsObj, moduleObj); +export default moduleObj.exports; diff --git a/src/js/internal/repl/acorn.js b/src/js/internal/repl/acorn.js new file mode 100644 index 000000000000..e379b49eb041 --- /dev/null +++ b/src/js/internal/repl/acorn.js @@ -0,0 +1,11 @@ +// Vendored from Node.js v26.3.0 deps (acorn.js, MIT licensed). +// The dist uses ES5 function+prototype constructors, which JSC builtin +// semantics forbid (builtin functions are non-constructors). Evaluate the +// source via vm.Script so it runs with full JavaScript semantics. +// prettier-ignore +const vm = require("node:vm"); +const exportsObj = {}; +const moduleObj = { exports: exportsObj }; +const factory = new vm.Script("(function(exports, module){(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n typeof define === 'function' && define.amd ? define(['exports'], factory) :\n (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.acorn = {}));\n})(this, (function (exports) { 'use strict';\n\n // This file was generated. Do not modify manually!\n var astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 574, 3, 9, 9, 7, 9, 32, 4, 318, 1, 78, 5, 71, 10, 50, 3, 123, 2, 54, 14, 32, 10, 3, 1, 11, 3, 46, 10, 8, 0, 46, 9, 7, 2, 37, 13, 2, 9, 6, 1, 45, 0, 13, 2, 49, 13, 9, 3, 2, 11, 83, 11, 7, 0, 3, 0, 158, 11, 6, 9, 7, 3, 56, 1, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 68, 8, 2, 0, 3, 0, 2, 3, 2, 4, 2, 0, 15, 1, 83, 17, 10, 9, 5, 0, 82, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 7, 19, 58, 14, 5, 9, 243, 14, 166, 9, 71, 5, 2, 1, 3, 3, 2, 0, 2, 1, 13, 9, 120, 6, 3, 6, 4, 0, 29, 9, 41, 6, 2, 3, 9, 0, 10, 10, 47, 15, 199, 7, 137, 9, 54, 7, 2, 7, 17, 9, 57, 21, 2, 13, 123, 5, 4, 0, 2, 1, 2, 6, 2, 0, 9, 9, 49, 4, 2, 1, 2, 4, 9, 9, 55, 9, 266, 3, 10, 1, 2, 0, 49, 6, 4, 4, 14, 10, 5350, 0, 7, 14, 11465, 27, 2343, 9, 87, 9, 39, 4, 60, 6, 26, 9, 535, 9, 470, 0, 2, 54, 8, 3, 82, 0, 12, 1, 19628, 1, 4178, 9, 519, 45, 3, 22, 543, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 513, 54, 5, 49, 9, 0, 15, 0, 23, 4, 2, 14, 1361, 6, 2, 16, 3, 6, 2, 1, 2, 4, 101, 0, 161, 6, 10, 9, 357, 0, 62, 13, 499, 13, 245, 1, 2, 9, 233, 0, 3, 0, 8, 1, 6, 0, 475, 6, 110, 6, 6, 9, 4759, 9, 787719, 239];\n\n // This file was generated. Do not modify manually!\n var astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 14, 29, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 19, 35, 5, 35, 5, 39, 9, 51, 13, 10, 2, 14, 2, 6, 2, 1, 2, 10, 2, 14, 2, 6, 2, 1, 4, 51, 13, 310, 10, 21, 11, 7, 25, 5, 2, 41, 2, 8, 70, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 66, 18, 2, 1, 11, 21, 11, 25, 7, 25, 39, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 28, 43, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 56, 50, 14, 50, 14, 35, 39, 27, 10, 22, 251, 41, 7, 1, 17, 5, 57, 28, 11, 0, 9, 21, 43, 17, 47, 20, 28, 22, 13, 52, 58, 1, 3, 0, 14, 44, 33, 24, 27, 35, 30, 0, 3, 0, 9, 34, 4, 0, 13, 47, 15, 3, 22, 0, 2, 0, 36, 17, 2, 24, 20, 1, 64, 6, 2, 0, 2, 3, 2, 14, 2, 9, 8, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 19, 0, 13, 4, 31, 9, 2, 0, 3, 0, 2, 37, 2, 0, 26, 0, 2, 0, 45, 52, 19, 3, 21, 2, 31, 47, 21, 1, 2, 0, 185, 46, 42, 3, 37, 47, 21, 0, 60, 42, 14, 0, 72, 26, 38, 6, 186, 43, 117, 63, 32, 7, 3, 0, 3, 7, 2, 1, 2, 23, 16, 0, 2, 0, 95, 7, 3, 38, 17, 0, 2, 0, 29, 0, 11, 39, 8, 0, 22, 0, 12, 45, 20, 0, 19, 72, 200, 32, 32, 8, 2, 36, 18, 0, 50, 29, 113, 6, 2, 1, 2, 37, 22, 0, 26, 5, 2, 1, 2, 31, 15, 0, 24, 43, 261, 18, 16, 0, 2, 12, 2, 33, 125, 0, 80, 921, 103, 110, 18, 195, 2637, 96, 16, 1071, 18, 5, 26, 3994, 6, 582, 6842, 29, 1763, 568, 8, 30, 18, 78, 18, 29, 19, 47, 17, 3, 32, 20, 6, 18, 433, 44, 212, 63, 33, 24, 3, 24, 45, 74, 6, 0, 67, 12, 65, 1, 2, 0, 15, 4, 10, 7381, 42, 31, 98, 114, 8702, 3, 2, 6, 2, 1, 2, 290, 16, 0, 30, 2, 3, 0, 15, 3, 9, 395, 2309, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 1845, 30, 7, 5, 262, 61, 147, 44, 11, 6, 17, 0, 322, 29, 19, 43, 485, 27, 229, 29, 3, 0, 208, 30, 2, 2, 2, 1, 2, 6, 3, 4, 10, 1, 225, 6, 2, 3, 2, 1, 2, 14, 2, 196, 60, 67, 8, 0, 1205, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42719, 33, 4381, 3, 5773, 3, 7472, 16, 621, 2467, 541, 1507, 4938, 6, 8489];\n\n // This file was generated. Do not modify manually!\n var nonASCIIidentifierChars = \"\\u200c\\u200d\\xb7\\u0300-\\u036f\\u0387\\u0483-\\u0487\\u0591-\\u05bd\\u05bf\\u05c1\\u05c2\\u05c4\\u05c5\\u05c7\\u0610-\\u061a\\u064b-\\u0669\\u0670\\u06d6-\\u06dc\\u06df-\\u06e4\\u06e7\\u06e8\\u06ea-\\u06ed\\u06f0-\\u06f9\\u0711\\u0730-\\u074a\\u07a6-\\u07b0\\u07c0-\\u07c9\\u07eb-\\u07f3\\u07fd\\u0816-\\u0819\\u081b-\\u0823\\u0825-\\u0827\\u0829-\\u082d\\u0859-\\u085b\\u0897-\\u089f\\u08ca-\\u08e1\\u08e3-\\u0903\\u093a-\\u093c\\u093e-\\u094f\\u0951-\\u0957\\u0962\\u0963\\u0966-\\u096f\\u0981-\\u0983\\u09bc\\u09be-\\u09c4\\u09c7\\u09c8\\u09cb-\\u09cd\\u09d7\\u09e2\\u09e3\\u09e6-\\u09ef\\u09fe\\u0a01-\\u0a03\\u0a3c\\u0a3e-\\u0a42\\u0a47\\u0a48\\u0a4b-\\u0a4d\\u0a51\\u0a66-\\u0a71\\u0a75\\u0a81-\\u0a83\\u0abc\\u0abe-\\u0ac5\\u0ac7-\\u0ac9\\u0acb-\\u0acd\\u0ae2\\u0ae3\\u0ae6-\\u0aef\\u0afa-\\u0aff\\u0b01-\\u0b03\\u0b3c\\u0b3e-\\u0b44\\u0b47\\u0b48\\u0b4b-\\u0b4d\\u0b55-\\u0b57\\u0b62\\u0b63\\u0b66-\\u0b6f\\u0b82\\u0bbe-\\u0bc2\\u0bc6-\\u0bc8\\u0bca-\\u0bcd\\u0bd7\\u0be6-\\u0bef\\u0c00-\\u0c04\\u0c3c\\u0c3e-\\u0c44\\u0c46-\\u0c48\\u0c4a-\\u0c4d\\u0c55\\u0c56\\u0c62\\u0c63\\u0c66-\\u0c6f\\u0c81-\\u0c83\\u0cbc\\u0cbe-\\u0cc4\\u0cc6-\\u0cc8\\u0cca-\\u0ccd\\u0cd5\\u0cd6\\u0ce2\\u0ce3\\u0ce6-\\u0cef\\u0cf3\\u0d00-\\u0d03\\u0d3b\\u0d3c\\u0d3e-\\u0d44\\u0d46-\\u0d48\\u0d4a-\\u0d4d\\u0d57\\u0d62\\u0d63\\u0d66-\\u0d6f\\u0d81-\\u0d83\\u0dca\\u0dcf-\\u0dd4\\u0dd6\\u0dd8-\\u0ddf\\u0de6-\\u0def\\u0df2\\u0df3\\u0e31\\u0e34-\\u0e3a\\u0e47-\\u0e4e\\u0e50-\\u0e59\\u0eb1\\u0eb4-\\u0ebc\\u0ec8-\\u0ece\\u0ed0-\\u0ed9\\u0f18\\u0f19\\u0f20-\\u0f29\\u0f35\\u0f37\\u0f39\\u0f3e\\u0f3f\\u0f71-\\u0f84\\u0f86\\u0f87\\u0f8d-\\u0f97\\u0f99-\\u0fbc\\u0fc6\\u102b-\\u103e\\u1040-\\u1049\\u1056-\\u1059\\u105e-\\u1060\\u1062-\\u1064\\u1067-\\u106d\\u1071-\\u1074\\u1082-\\u108d\\u108f-\\u109d\\u135d-\\u135f\\u1369-\\u1371\\u1712-\\u1715\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17b4-\\u17d3\\u17dd\\u17e0-\\u17e9\\u180b-\\u180d\\u180f-\\u1819\\u18a9\\u1920-\\u192b\\u1930-\\u193b\\u1946-\\u194f\\u19d0-\\u19da\\u1a17-\\u1a1b\\u1a55-\\u1a5e\\u1a60-\\u1a7c\\u1a7f-\\u1a89\\u1a90-\\u1a99\\u1ab0-\\u1abd\\u1abf-\\u1add\\u1ae0-\\u1aeb\\u1b00-\\u1b04\\u1b34-\\u1b44\\u1b50-\\u1b59\\u1b6b-\\u1b73\\u1b80-\\u1b82\\u1ba1-\\u1bad\\u1bb0-\\u1bb9\\u1be6-\\u1bf3\\u1c24-\\u1c37\\u1c40-\\u1c49\\u1c50-\\u1c59\\u1cd0-\\u1cd2\\u1cd4-\\u1ce8\\u1ced\\u1cf4\\u1cf7-\\u1cf9\\u1dc0-\\u1dff\\u200c\\u200d\\u203f\\u2040\\u2054\\u20d0-\\u20dc\\u20e1\\u20e5-\\u20f0\\u2cef-\\u2cf1\\u2d7f\\u2de0-\\u2dff\\u302a-\\u302f\\u3099\\u309a\\u30fb\\ua620-\\ua629\\ua66f\\ua674-\\ua67d\\ua69e\\ua69f\\ua6f0\\ua6f1\\ua802\\ua806\\ua80b\\ua823-\\ua827\\ua82c\\ua880\\ua881\\ua8b4-\\ua8c5\\ua8d0-\\ua8d9\\ua8e0-\\ua8f1\\ua8ff-\\ua909\\ua926-\\ua92d\\ua947-\\ua953\\ua980-\\ua983\\ua9b3-\\ua9c0\\ua9d0-\\ua9d9\\ua9e5\\ua9f0-\\ua9f9\\uaa29-\\uaa36\\uaa43\\uaa4c\\uaa4d\\uaa50-\\uaa59\\uaa7b-\\uaa7d\\uaab0\\uaab2-\\uaab4\\uaab7\\uaab8\\uaabe\\uaabf\\uaac1\\uaaeb-\\uaaef\\uaaf5\\uaaf6\\uabe3-\\uabea\\uabec\\uabed\\uabf0-\\uabf9\\ufb1e\\ufe00-\\ufe0f\\ufe20-\\ufe2f\\ufe33\\ufe34\\ufe4d-\\ufe4f\\uff10-\\uff19\\uff3f\\uff65\";\n\n // This file was generated. Do not modify manually!\n var nonASCIIidentifierStartChars = \"\\xaa\\xb5\\xba\\xc0-\\xd6\\xd8-\\xf6\\xf8-\\u02c1\\u02c6-\\u02d1\\u02e0-\\u02e4\\u02ec\\u02ee\\u0370-\\u0374\\u0376\\u0377\\u037a-\\u037d\\u037f\\u0386\\u0388-\\u038a\\u038c\\u038e-\\u03a1\\u03a3-\\u03f5\\u03f7-\\u0481\\u048a-\\u052f\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05d0-\\u05ea\\u05ef-\\u05f2\\u0620-\\u064a\\u066e\\u066f\\u0671-\\u06d3\\u06d5\\u06e5\\u06e6\\u06ee\\u06ef\\u06fa-\\u06fc\\u06ff\\u0710\\u0712-\\u072f\\u074d-\\u07a5\\u07b1\\u07ca-\\u07ea\\u07f4\\u07f5\\u07fa\\u0800-\\u0815\\u081a\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086a\\u0870-\\u0887\\u0889-\\u088f\\u08a0-\\u08c9\\u0904-\\u0939\\u093d\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098c\\u098f\\u0990\\u0993-\\u09a8\\u09aa-\\u09b0\\u09b2\\u09b6-\\u09b9\\u09bd\\u09ce\\u09dc\\u09dd\\u09df-\\u09e1\\u09f0\\u09f1\\u09fc\\u0a05-\\u0a0a\\u0a0f\\u0a10\\u0a13-\\u0a28\\u0a2a-\\u0a30\\u0a32\\u0a33\\u0a35\\u0a36\\u0a38\\u0a39\\u0a59-\\u0a5c\\u0a5e\\u0a72-\\u0a74\\u0a85-\\u0a8d\\u0a8f-\\u0a91\\u0a93-\\u0aa8\\u0aaa-\\u0ab0\\u0ab2\\u0ab3\\u0ab5-\\u0ab9\\u0abd\\u0ad0\\u0ae0\\u0ae1\\u0af9\\u0b05-\\u0b0c\\u0b0f\\u0b10\\u0b13-\\u0b28\\u0b2a-\\u0b30\\u0b32\\u0b33\\u0b35-\\u0b39\\u0b3d\\u0b5c\\u0b5d\\u0b5f-\\u0b61\\u0b71\\u0b83\\u0b85-\\u0b8a\\u0b8e-\\u0b90\\u0b92-\\u0b95\\u0b99\\u0b9a\\u0b9c\\u0b9e\\u0b9f\\u0ba3\\u0ba4\\u0ba8-\\u0baa\\u0bae-\\u0bb9\\u0bd0\\u0c05-\\u0c0c\\u0c0e-\\u0c10\\u0c12-\\u0c28\\u0c2a-\\u0c39\\u0c3d\\u0c58-\\u0c5a\\u0c5c\\u0c5d\\u0c60\\u0c61\\u0c80\\u0c85-\\u0c8c\\u0c8e-\\u0c90\\u0c92-\\u0ca8\\u0caa-\\u0cb3\\u0cb5-\\u0cb9\\u0cbd\\u0cdc-\\u0cde\\u0ce0\\u0ce1\\u0cf1\\u0cf2\\u0d04-\\u0d0c\\u0d0e-\\u0d10\\u0d12-\\u0d3a\\u0d3d\\u0d4e\\u0d54-\\u0d56\\u0d5f-\\u0d61\\u0d7a-\\u0d7f\\u0d85-\\u0d96\\u0d9a-\\u0db1\\u0db3-\\u0dbb\\u0dbd\\u0dc0-\\u0dc6\\u0e01-\\u0e30\\u0e32\\u0e33\\u0e40-\\u0e46\\u0e81\\u0e82\\u0e84\\u0e86-\\u0e8a\\u0e8c-\\u0ea3\\u0ea5\\u0ea7-\\u0eb0\\u0eb2\\u0eb3\\u0ebd\\u0ec0-\\u0ec4\\u0ec6\\u0edc-\\u0edf\\u0f00\\u0f40-\\u0f47\\u0f49-\\u0f6c\\u0f88-\\u0f8c\\u1000-\\u102a\\u103f\\u1050-\\u1055\\u105a-\\u105d\\u1061\\u1065\\u1066\\u106e-\\u1070\\u1075-\\u1081\\u108e\\u10a0-\\u10c5\\u10c7\\u10cd\\u10d0-\\u10fa\\u10fc-\\u1248\\u124a-\\u124d\\u1250-\\u1256\\u1258\\u125a-\\u125d\\u1260-\\u1288\\u128a-\\u128d\\u1290-\\u12b0\\u12b2-\\u12b5\\u12b8-\\u12be\\u12c0\\u12c2-\\u12c5\\u12c8-\\u12d6\\u12d8-\\u1310\\u1312-\\u1315\\u1318-\\u135a\\u1380-\\u138f\\u13a0-\\u13f5\\u13f8-\\u13fd\\u1401-\\u166c\\u166f-\\u167f\\u1681-\\u169a\\u16a0-\\u16ea\\u16ee-\\u16f8\\u1700-\\u1711\\u171f-\\u1731\\u1740-\\u1751\\u1760-\\u176c\\u176e-\\u1770\\u1780-\\u17b3\\u17d7\\u17dc\\u1820-\\u1878\\u1880-\\u18a8\\u18aa\\u18b0-\\u18f5\\u1900-\\u191e\\u1950-\\u196d\\u1970-\\u1974\\u1980-\\u19ab\\u19b0-\\u19c9\\u1a00-\\u1a16\\u1a20-\\u1a54\\u1aa7\\u1b05-\\u1b33\\u1b45-\\u1b4c\\u1b83-\\u1ba0\\u1bae\\u1baf\\u1bba-\\u1be5\\u1c00-\\u1c23\\u1c4d-\\u1c4f\\u1c5a-\\u1c7d\\u1c80-\\u1c8a\\u1c90-\\u1cba\\u1cbd-\\u1cbf\\u1ce9-\\u1cec\\u1cee-\\u1cf3\\u1cf5\\u1cf6\\u1cfa\\u1d00-\\u1dbf\\u1e00-\\u1f15\\u1f18-\\u1f1d\\u1f20-\\u1f45\\u1f48-\\u1f4d\\u1f50-\\u1f57\\u1f59\\u1f5b\\u1f5d\\u1f5f-\\u1f7d\\u1f80-\\u1fb4\\u1fb6-\\u1fbc\\u1fbe\\u1fc2-\\u1fc4\\u1fc6-\\u1fcc\\u1fd0-\\u1fd3\\u1fd6-\\u1fdb\\u1fe0-\\u1fec\\u1ff2-\\u1ff4\\u1ff6-\\u1ffc\\u2071\\u207f\\u2090-\\u209c\\u2102\\u2107\\u210a-\\u2113\\u2115\\u2118-\\u211d\\u2124\\u2126\\u2128\\u212a-\\u2139\\u213c-\\u213f\\u2145-\\u2149\\u214e\\u2160-\\u2188\\u2c00-\\u2ce4\\u2ceb-\\u2cee\\u2cf2\\u2cf3\\u2d00-\\u2d25\\u2d27\\u2d2d\\u2d30-\\u2d67\\u2d6f\\u2d80-\\u2d96\\u2da0-\\u2da6\\u2da8-\\u2dae\\u2db0-\\u2db6\\u2db8-\\u2dbe\\u2dc0-\\u2dc6\\u2dc8-\\u2dce\\u2dd0-\\u2dd6\\u2dd8-\\u2dde\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303c\\u3041-\\u3096\\u309b-\\u309f\\u30a1-\\u30fa\\u30fc-\\u30ff\\u3105-\\u312f\\u3131-\\u318e\\u31a0-\\u31bf\\u31f0-\\u31ff\\u3400-\\u4dbf\\u4e00-\\ua48c\\ua4d0-\\ua4fd\\ua500-\\ua60c\\ua610-\\ua61f\\ua62a\\ua62b\\ua640-\\ua66e\\ua67f-\\ua69d\\ua6a0-\\ua6ef\\ua717-\\ua71f\\ua722-\\ua788\\ua78b-\\ua7dc\\ua7f1-\\ua801\\ua803-\\ua805\\ua807-\\ua80a\\ua80c-\\ua822\\ua840-\\ua873\\ua882-\\ua8b3\\ua8f2-\\ua8f7\\ua8fb\\ua8fd\\ua8fe\\ua90a-\\ua925\\ua930-\\ua946\\ua960-\\ua97c\\ua984-\\ua9b2\\ua9cf\\ua9e0-\\ua9e4\\ua9e6-\\ua9ef\\ua9fa-\\ua9fe\\uaa00-\\uaa28\\uaa40-\\uaa42\\uaa44-\\uaa4b\\uaa60-\\uaa76\\uaa7a\\uaa7e-\\uaaaf\\uaab1\\uaab5\\uaab6\\uaab9-\\uaabd\\uaac0\\uaac2\\uaadb-\\uaadd\\uaae0-\\uaaea\\uaaf2-\\uaaf4\\uab01-\\uab06\\uab09-\\uab0e\\uab11-\\uab16\\uab20-\\uab26\\uab28-\\uab2e\\uab30-\\uab5a\\uab5c-\\uab69\\uab70-\\uabe2\\uac00-\\ud7a3\\ud7b0-\\ud7c6\\ud7cb-\\ud7fb\\uf900-\\ufa6d\\ufa70-\\ufad9\\ufb00-\\ufb06\\ufb13-\\ufb17\\ufb1d\\ufb1f-\\ufb28\\ufb2a-\\ufb36\\ufb38-\\ufb3c\\ufb3e\\ufb40\\ufb41\\ufb43\\ufb44\\ufb46-\\ufbb1\\ufbd3-\\ufd3d\\ufd50-\\ufd8f\\ufd92-\\ufdc7\\ufdf0-\\ufdfb\\ufe70-\\ufe74\\ufe76-\\ufefc\\uff21-\\uff3a\\uff41-\\uff5a\\uff66-\\uffbe\\uffc2-\\uffc7\\uffca-\\uffcf\\uffd2-\\uffd7\\uffda-\\uffdc\";\n\n // These are a run-length and offset encoded representation of the\n // >0xffff code points that are a valid part of identifiers. The\n // offset starts at 0x10000, and each pair of numbers represents an\n // offset to the next range, and then a size of the range.\n\n // Reserved word lists for various dialects of the language\n\n var reservedWords = {\n 3: \"abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile\",\n 5: \"class enum extends super const export import\",\n 6: \"enum\",\n strict: \"implements interface let package private protected public static yield\",\n strictBind: \"eval arguments\"\n };\n\n // And the keywords\n\n var ecma5AndLessKeywords = \"break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this\";\n\n var keywords$1 = {\n 5: ecma5AndLessKeywords,\n \"5module\": ecma5AndLessKeywords + \" export import\",\n 6: ecma5AndLessKeywords + \" const class extends export import super\"\n };\n\n var keywordRelationalOperator = /^in(stanceof)?$/;\n\n // ## Character categories\n\n var nonASCIIidentifierStart = new RegExp(\"[\" + nonASCIIidentifierStartChars + \"]\");\n var nonASCIIidentifier = new RegExp(\"[\" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + \"]\");\n\n // This has a complexity linear to the value of the code. The\n // assumption is that looking up astral identifier characters is\n // rare.\n function isInAstralSet(code, set) {\n var pos = 0x10000;\n for (var i = 0; i < set.length; i += 2) {\n pos += set[i];\n if (pos > code) { return false }\n pos += set[i + 1];\n if (pos >= code) { return true }\n }\n return false\n }\n\n // Test whether a given character code starts an identifier.\n\n function isIdentifierStart(code, astral) {\n if (code < 65) { return code === 36 }\n if (code < 91) { return true }\n if (code < 97) { return code === 95 }\n if (code < 123) { return true }\n if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)) }\n if (astral === false) { return false }\n return isInAstralSet(code, astralIdentifierStartCodes)\n }\n\n // Test whether a given character is part of an identifier.\n\n function isIdentifierChar(code, astral) {\n if (code < 48) { return code === 36 }\n if (code < 58) { return true }\n if (code < 65) { return false }\n if (code < 91) { return true }\n if (code < 97) { return code === 95 }\n if (code < 123) { return true }\n if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)) }\n if (astral === false) { return false }\n return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes)\n }\n\n // ## Token types\n\n // The assignment of fine-grained, information-carrying type objects\n // allows the tokenizer to store the information it has about a\n // token in a way that is very cheap for the parser to look up.\n\n // All token type variables start with an underscore, to make them\n // easy to recognize.\n\n // The `beforeExpr` property is used to disambiguate between regular\n // expressions and divisions. It is set on all token types that can\n // be followed by an expression (thus, a slash after them would be a\n // regular expression).\n //\n // The `startsExpr` property is used to check if the token ends a\n // `yield` expression. It is set on all token types that either can\n // directly start an expression (like a quotation mark) or can\n // continue an expression (like the body of a string).\n //\n // `isLoop` marks a keyword as starting a loop, which is important\n // to know when parsing a label, in order to allow or disallow\n // continue jumps to that label.\n\n var TokenType = function TokenType(label, conf) {\n if ( conf === void 0 ) conf = {};\n\n this.label = label;\n this.keyword = conf.keyword;\n this.beforeExpr = !!conf.beforeExpr;\n this.startsExpr = !!conf.startsExpr;\n this.isLoop = !!conf.isLoop;\n this.isAssign = !!conf.isAssign;\n this.prefix = !!conf.prefix;\n this.postfix = !!conf.postfix;\n this.binop = conf.binop || null;\n this.updateContext = null;\n };\n\n function binop(name, prec) {\n return new TokenType(name, {beforeExpr: true, binop: prec})\n }\n var beforeExpr = {beforeExpr: true}, startsExpr = {startsExpr: true};\n\n // Map keyword names to token types.\n\n var keywords = {};\n\n // Succinct definitions of keyword token types\n function kw(name, options) {\n if ( options === void 0 ) options = {};\n\n options.keyword = name;\n return keywords[name] = new TokenType(name, options)\n }\n\n var types$1 = {\n num: new TokenType(\"num\", startsExpr),\n regexp: new TokenType(\"regexp\", startsExpr),\n string: new TokenType(\"string\", startsExpr),\n name: new TokenType(\"name\", startsExpr),\n privateId: new TokenType(\"privateId\", startsExpr),\n eof: new TokenType(\"eof\"),\n\n // Punctuation token types.\n bracketL: new TokenType(\"[\", {beforeExpr: true, startsExpr: true}),\n bracketR: new TokenType(\"]\"),\n braceL: new TokenType(\"{\", {beforeExpr: true, startsExpr: true}),\n braceR: new TokenType(\"}\"),\n parenL: new TokenType(\"(\", {beforeExpr: true, startsExpr: true}),\n parenR: new TokenType(\")\"),\n comma: new TokenType(\",\", beforeExpr),\n semi: new TokenType(\";\", beforeExpr),\n colon: new TokenType(\":\", beforeExpr),\n dot: new TokenType(\".\"),\n question: new TokenType(\"?\", beforeExpr),\n questionDot: new TokenType(\"?.\"),\n arrow: new TokenType(\"=>\", beforeExpr),\n template: new TokenType(\"template\"),\n invalidTemplate: new TokenType(\"invalidTemplate\"),\n ellipsis: new TokenType(\"...\", beforeExpr),\n backQuote: new TokenType(\"`\", startsExpr),\n dollarBraceL: new TokenType(\"${\", {beforeExpr: true, startsExpr: true}),\n\n // Operators. These carry several kinds of properties to help the\n // parser use them properly (the presence of these properties is\n // what categorizes them as operators).\n //\n // `binop`, when present, specifies that this operator is a binary\n // operator, and will refer to its precedence.\n //\n // `prefix` and `postfix` mark the operator as a prefix or postfix\n // unary operator.\n //\n // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as\n // binary operators with a very low precedence, that should result\n // in AssignmentExpression nodes.\n\n eq: new TokenType(\"=\", {beforeExpr: true, isAssign: true}),\n assign: new TokenType(\"_=\", {beforeExpr: true, isAssign: true}),\n incDec: new TokenType(\"++/--\", {prefix: true, postfix: true, startsExpr: true}),\n prefix: new TokenType(\"!/~\", {beforeExpr: true, prefix: true, startsExpr: true}),\n logicalOR: binop(\"||\", 1),\n logicalAND: binop(\"&&\", 2),\n bitwiseOR: binop(\"|\", 3),\n bitwiseXOR: binop(\"^\", 4),\n bitwiseAND: binop(\"&\", 5),\n equality: binop(\"==/!=/===/!==\", 6),\n relational: binop(\"/<=/>=\", 7),\n bitShift: binop(\"<>/>>>\", 8),\n plusMin: new TokenType(\"+/-\", {beforeExpr: true, binop: 9, prefix: true, startsExpr: true}),\n modulo: binop(\"%\", 10),\n star: binop(\"*\", 10),\n slash: binop(\"/\", 10),\n starstar: new TokenType(\"**\", {beforeExpr: true}),\n coalesce: binop(\"??\", 1),\n\n // Keyword token types.\n _break: kw(\"break\"),\n _case: kw(\"case\", beforeExpr),\n _catch: kw(\"catch\"),\n _continue: kw(\"continue\"),\n _debugger: kw(\"debugger\"),\n _default: kw(\"default\", beforeExpr),\n _do: kw(\"do\", {isLoop: true, beforeExpr: true}),\n _else: kw(\"else\", beforeExpr),\n _finally: kw(\"finally\"),\n _for: kw(\"for\", {isLoop: true}),\n _function: kw(\"function\", startsExpr),\n _if: kw(\"if\"),\n _return: kw(\"return\", beforeExpr),\n _switch: kw(\"switch\"),\n _throw: kw(\"throw\", beforeExpr),\n _try: kw(\"try\"),\n _var: kw(\"var\"),\n _const: kw(\"const\"),\n _while: kw(\"while\", {isLoop: true}),\n _with: kw(\"with\"),\n _new: kw(\"new\", {beforeExpr: true, startsExpr: true}),\n _this: kw(\"this\", startsExpr),\n _super: kw(\"super\", startsExpr),\n _class: kw(\"class\", startsExpr),\n _extends: kw(\"extends\", beforeExpr),\n _export: kw(\"export\"),\n _import: kw(\"import\", startsExpr),\n _null: kw(\"null\", startsExpr),\n _true: kw(\"true\", startsExpr),\n _false: kw(\"false\", startsExpr),\n _in: kw(\"in\", {beforeExpr: true, binop: 7}),\n _instanceof: kw(\"instanceof\", {beforeExpr: true, binop: 7}),\n _typeof: kw(\"typeof\", {beforeExpr: true, prefix: true, startsExpr: true}),\n _void: kw(\"void\", {beforeExpr: true, prefix: true, startsExpr: true}),\n _delete: kw(\"delete\", {beforeExpr: true, prefix: true, startsExpr: true})\n };\n\n // Matches a whole line break (where CRLF is considered a single\n // line break). Used to count lines.\n\n var lineBreak = /\\r\\n?|\\n|\\u2028|\\u2029/;\n var lineBreakG = new RegExp(lineBreak.source, \"g\");\n\n function isNewLine(code) {\n return code === 10 || code === 13 || code === 0x2028 || code === 0x2029\n }\n\n function nextLineBreak(code, from, end) {\n if ( end === void 0 ) end = code.length;\n\n for (var i = from; i < end; i++) {\n var next = code.charCodeAt(i);\n if (isNewLine(next))\n { return i < end - 1 && next === 13 && code.charCodeAt(i + 1) === 10 ? i + 2 : i + 1 }\n }\n return -1\n }\n\n var nonASCIIwhitespace = /[\\u1680\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]/;\n\n var skipWhiteSpace = /(?:\\s|\\/\\/.*|\\/\\*[^]*?\\*\\/)*/g;\n\n var ref = Object.prototype;\n var hasOwnProperty = ref.hasOwnProperty;\n var toString = ref.toString;\n\n var hasOwn = Object.hasOwn || (function (obj, propName) { return (\n hasOwnProperty.call(obj, propName)\n ); });\n\n var isArray = Array.isArray || (function (obj) { return (\n toString.call(obj) === \"[object Array]\"\n ); });\n\n var regexpCache = Object.create(null);\n\n function wordsRegexp(words) {\n return regexpCache[words] || (regexpCache[words] = new RegExp(\"^(?:\" + words.replace(/ /g, \"|\") + \")$\"))\n }\n\n function codePointToString(code) {\n // UTF-16 Decoding\n if (code <= 0xFFFF) { return String.fromCharCode(code) }\n code -= 0x10000;\n return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00)\n }\n\n var loneSurrogate = /(?:[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF])/;\n\n // These are used when `options.locations` is on, for the\n // `startLoc` and `endLoc` properties.\n\n var Position = function Position(line, col) {\n this.line = line;\n this.column = col;\n };\n\n Position.prototype.offset = function offset (n) {\n return new Position(this.line, this.column + n)\n };\n\n var SourceLocation = function SourceLocation(p, start, end) {\n this.start = start;\n this.end = end;\n if (p.sourceFile !== null) { this.source = p.sourceFile; }\n };\n\n // The `getLineInfo` function is mostly useful when the\n // `locations` option is off (for performance reasons) and you\n // want to find the line/column position for a given character\n // offset. `input` should be the code string that the offset refers\n // into.\n\n function getLineInfo(input, offset) {\n for (var line = 1, cur = 0;;) {\n var nextBreak = nextLineBreak(input, cur, offset);\n if (nextBreak < 0) { return new Position(line, offset - cur) }\n ++line;\n cur = nextBreak;\n }\n }\n\n // A second argument must be given to configure the parser process.\n // These options are recognized (only `ecmaVersion` is required):\n\n var defaultOptions = {\n // `ecmaVersion` indicates the ECMAScript version to parse. Must be\n // either 3, 5, 6 (or 2015), 7 (2016), 8 (2017), 9 (2018), 10\n // (2019), 11 (2020), 12 (2021), 13 (2022), 14 (2023), or `\"latest\"`\n // (the latest version the library supports). This influences\n // support for strict mode, the set of reserved words, and support\n // for new syntax features.\n ecmaVersion: null,\n // `sourceType` indicates the mode the code should be parsed in.\n // Can be either `\"script\"`, `\"module\"` or `\"commonjs\"`. This influences global\n // strict mode and parsing of `import` and `export` declarations.\n sourceType: \"script\",\n // `onInsertedSemicolon` can be a callback that will be called when\n // a semicolon is automatically inserted. It will be passed the\n // position of the inserted semicolon as an offset, and if\n // `locations` is enabled, it is given the location as a `{line,\n // column}` object as second argument.\n onInsertedSemicolon: null,\n // `onTrailingComma` is similar to `onInsertedSemicolon`, but for\n // trailing commas.\n onTrailingComma: null,\n // By default, reserved words are only enforced if ecmaVersion >= 5.\n // Set `allowReserved` to a boolean value to explicitly turn this on\n // an off. When this option has the value \"never\", reserved words\n // and keywords can also not be used as property names.\n allowReserved: null,\n // When enabled, a return at the top level is not considered an\n // error.\n allowReturnOutsideFunction: false,\n // When enabled, import/export statements are not constrained to\n // appearing at the top of the program, and an import.meta expression\n // in a script isn't considered an error.\n allowImportExportEverywhere: false,\n // By default, await identifiers are allowed to appear at the top-level scope only if ecmaVersion >= 2022.\n // When enabled, await identifiers are allowed to appear at the top-level scope,\n // but they are still not allowed in non-async functions.\n allowAwaitOutsideFunction: null,\n // When enabled, super identifiers are not constrained to\n // appearing in methods and do not raise an error when they appear elsewhere.\n allowSuperOutsideMethod: null,\n // When enabled, hashbang directive in the beginning of file is\n // allowed and treated as a line comment. Enabled by default when\n // `ecmaVersion` >= 2023.\n allowHashBang: false,\n // By default, the parser will verify that private properties are\n // only used in places where they are valid and have been declared.\n // Set this to false to turn such checks off.\n checkPrivateFields: true,\n // When `locations` is on, `loc` properties holding objects with\n // `start` and `end` properties in `{line, column}` form (with\n // line being 1-based and column 0-based) will be attached to the\n // nodes.\n locations: false,\n // A function can be passed as `onToken` option, which will\n // cause Acorn to call that function with object in the same\n // format as tokens returned from `tokenizer().getToken()`. Note\n // that you are not allowed to call the parser from the\n // callback\u2014that will corrupt its internal state.\n onToken: null,\n // A function can be passed as `onComment` option, which will\n // cause Acorn to call that function with `(block, text, start,\n // end)` parameters whenever a comment is skipped. `block` is a\n // boolean indicating whether this is a block (`/* */`) comment,\n // `text` is the content of the comment, and `start` and `end` are\n // character offsets that denote the start and end of the comment.\n // When the `locations` option is on, two more parameters are\n // passed, the full `{line, column}` locations of the start and\n // end of the comments. Note that you are not allowed to call the\n // parser from the callback\u2014that will corrupt its internal state.\n // When this option has an array as value, objects representing the\n // comments are pushed to it.\n onComment: null,\n // Nodes have their start and end characters offsets recorded in\n // `start` and `end` properties (directly on the node, rather than\n // the `loc` object, which holds line/column data. To also add a\n // [semi-standardized][range] `range` property holding a `[start,\n // end]` array with the same numbers, set the `ranges` option to\n // `true`.\n //\n // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678\n ranges: false,\n // It is possible to parse multiple files into a single AST by\n // passing the tree produced by parsing the first file as\n // `program` option in subsequent parses. This will add the\n // toplevel forms of the parsed file to the `Program` (top) node\n // of an existing parse tree.\n program: null,\n // When `locations` is on, you can pass this to record the source\n // file in every node's `loc` object.\n sourceFile: null,\n // This value, if given, is stored in every node, whether\n // `locations` is on or off.\n directSourceFile: null,\n // When enabled, parenthesized expressions are represented by\n // (non-standard) ParenthesizedExpression nodes\n preserveParens: false\n };\n\n // Interpret and default an options object\n\n var warnedAboutEcmaVersion = false;\n\n function getOptions(opts) {\n var options = {};\n\n for (var opt in defaultOptions)\n { options[opt] = opts && hasOwn(opts, opt) ? opts[opt] : defaultOptions[opt]; }\n\n if (options.ecmaVersion === \"latest\") {\n options.ecmaVersion = 1e8;\n } else if (options.ecmaVersion == null) {\n if (!warnedAboutEcmaVersion && typeof console === \"object\" && console.warn) {\n warnedAboutEcmaVersion = true;\n console.warn(\"Since Acorn 8.0.0, options.ecmaVersion is required.\\nDefaulting to 2020, but this will stop working in the future.\");\n }\n options.ecmaVersion = 11;\n } else if (options.ecmaVersion >= 2015) {\n options.ecmaVersion -= 2009;\n }\n\n if (options.allowReserved == null)\n { options.allowReserved = options.ecmaVersion < 5; }\n\n if (!opts || opts.allowHashBang == null)\n { options.allowHashBang = options.ecmaVersion >= 14; }\n\n if (isArray(options.onToken)) {\n var tokens = options.onToken;\n options.onToken = function (token) { return tokens.push(token); };\n }\n if (isArray(options.onComment))\n { options.onComment = pushComment(options, options.onComment); }\n\n if (options.sourceType === \"commonjs\" && options.allowAwaitOutsideFunction)\n { throw new Error(\"Cannot use allowAwaitOutsideFunction with sourceType: commonjs\") }\n\n return options\n }\n\n function pushComment(options, array) {\n return function(block, text, start, end, startLoc, endLoc) {\n var comment = {\n type: block ? \"Block\" : \"Line\",\n value: text,\n start: start,\n end: end\n };\n if (options.locations)\n { comment.loc = new SourceLocation(this, startLoc, endLoc); }\n if (options.ranges)\n { comment.range = [start, end]; }\n array.push(comment);\n }\n }\n\n // Each scope gets a bitset that may contain these flags\n var\n SCOPE_TOP = 1,\n SCOPE_FUNCTION = 2,\n SCOPE_ASYNC = 4,\n SCOPE_GENERATOR = 8,\n SCOPE_ARROW = 16,\n SCOPE_SIMPLE_CATCH = 32,\n SCOPE_SUPER = 64,\n SCOPE_DIRECT_SUPER = 128,\n SCOPE_CLASS_STATIC_BLOCK = 256,\n SCOPE_CLASS_FIELD_INIT = 512,\n SCOPE_SWITCH = 1024,\n SCOPE_VAR = SCOPE_TOP | SCOPE_FUNCTION | SCOPE_CLASS_STATIC_BLOCK;\n\n function functionFlags(async, generator) {\n return SCOPE_FUNCTION | (async ? SCOPE_ASYNC : 0) | (generator ? SCOPE_GENERATOR : 0)\n }\n\n // Used in checkLVal* and declareName to determine the type of a binding\n var\n BIND_NONE = 0, // Not a binding\n BIND_VAR = 1, // Var-style binding\n BIND_LEXICAL = 2, // Let- or const-style binding\n BIND_FUNCTION = 3, // Function declaration\n BIND_SIMPLE_CATCH = 4, // Simple (identifier pattern) catch binding\n BIND_OUTSIDE = 5; // Special case for function names as bound inside the function\n\n var Parser = function Parser(options, input, startPos) {\n this.options = options = getOptions(options);\n this.sourceFile = options.sourceFile;\n this.keywords = wordsRegexp(keywords$1[options.ecmaVersion >= 6 ? 6 : options.sourceType === \"module\" ? \"5module\" : 5]);\n var reserved = \"\";\n if (options.allowReserved !== true) {\n reserved = reservedWords[options.ecmaVersion >= 6 ? 6 : options.ecmaVersion === 5 ? 5 : 3];\n if (options.sourceType === \"module\") { reserved += \" await\"; }\n }\n this.reservedWords = wordsRegexp(reserved);\n var reservedStrict = (reserved ? reserved + \" \" : \"\") + reservedWords.strict;\n this.reservedWordsStrict = wordsRegexp(reservedStrict);\n this.reservedWordsStrictBind = wordsRegexp(reservedStrict + \" \" + reservedWords.strictBind);\n this.input = String(input);\n\n // Used to signal to callers of `readWord1` whether the word\n // contained any escape sequences. This is needed because words with\n // escape sequences must not be interpreted as keywords.\n this.containsEsc = false;\n\n // Set up token state\n\n // The current position of the tokenizer in the input.\n if (startPos) {\n this.pos = startPos;\n this.lineStart = this.input.lastIndexOf(\"\\n\", startPos - 1) + 1;\n this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length;\n } else {\n this.pos = this.lineStart = 0;\n this.curLine = 1;\n }\n\n // Properties of the current token:\n // Its type\n this.type = types$1.eof;\n // For tokens that include more information than their type, the value\n this.value = null;\n // Its start and end offset\n this.start = this.end = this.pos;\n // And, if locations are used, the {line, column} object\n // corresponding to those offsets\n this.startLoc = this.endLoc = this.curPosition();\n\n // Position information for the previous token\n this.lastTokEndLoc = this.lastTokStartLoc = null;\n this.lastTokStart = this.lastTokEnd = this.pos;\n\n // The context stack is used to superficially track syntactic\n // context to predict whether a regular expression is allowed in a\n // given position.\n this.context = this.initialContext();\n this.exprAllowed = true;\n\n // Figure out if it's a module code.\n this.inModule = options.sourceType === \"module\";\n this.strict = this.inModule || this.strictDirective(this.pos);\n\n // Used to signify the start of a potential arrow function\n this.potentialArrowAt = -1;\n this.potentialArrowInForAwait = false;\n\n // Positions to delayed-check that yield/await does not exist in default parameters.\n this.yieldPos = this.awaitPos = this.awaitIdentPos = 0;\n // Labels in scope.\n this.labels = [];\n // Thus-far undefined exports.\n this.undefinedExports = Object.create(null);\n\n // If enabled, skip leading hashbang line.\n if (this.pos === 0 && options.allowHashBang && this.input.slice(0, 2) === \"#!\")\n { this.skipLineComment(2); }\n\n // Scope tracking for duplicate variable names (see scope.js)\n this.scopeStack = [];\n this.enterScope(\n this.options.sourceType === \"commonjs\"\n // In commonjs, the top-level scope behaves like a function scope\n ? SCOPE_FUNCTION\n : SCOPE_TOP\n );\n\n // For RegExp validation\n this.regexpState = null;\n\n // The stack of private names.\n // Each element has two properties: 'declared' and 'used'.\n // When it exited from the outermost class definition, all used private names must be declared.\n this.privateNameStack = [];\n };\n\n var prototypeAccessors = { inFunction: { configurable: true },inGenerator: { configurable: true },inAsync: { configurable: true },canAwait: { configurable: true },allowReturn: { configurable: true },allowSuper: { configurable: true },allowDirectSuper: { configurable: true },treatFunctionsAsVar: { configurable: true },allowNewDotTarget: { configurable: true },allowUsing: { configurable: true },inClassStaticBlock: { configurable: true } };\n\n Parser.prototype.parse = function parse () {\n var node = this.options.program || this.startNode();\n this.nextToken();\n return this.parseTopLevel(node)\n };\n\n prototypeAccessors.inFunction.get = function () { return (this.currentVarScope().flags & SCOPE_FUNCTION) > 0 };\n\n prototypeAccessors.inGenerator.get = function () { return (this.currentVarScope().flags & SCOPE_GENERATOR) > 0 };\n\n prototypeAccessors.inAsync.get = function () { return (this.currentVarScope().flags & SCOPE_ASYNC) > 0 };\n\n prototypeAccessors.canAwait.get = function () {\n for (var i = this.scopeStack.length - 1; i >= 0; i--) {\n var ref = this.scopeStack[i];\n var flags = ref.flags;\n if (flags & (SCOPE_CLASS_STATIC_BLOCK | SCOPE_CLASS_FIELD_INIT)) { return false }\n if (flags & SCOPE_FUNCTION) { return (flags & SCOPE_ASYNC) > 0 }\n }\n return (this.inModule && this.options.ecmaVersion >= 13) || this.options.allowAwaitOutsideFunction\n };\n\n prototypeAccessors.allowReturn.get = function () {\n if (this.inFunction) { return true }\n if (this.options.allowReturnOutsideFunction && this.currentVarScope().flags & SCOPE_TOP) { return true }\n return false\n };\n\n prototypeAccessors.allowSuper.get = function () {\n var ref = this.currentThisScope();\n var flags = ref.flags;\n return (flags & SCOPE_SUPER) > 0 || this.options.allowSuperOutsideMethod\n };\n\n prototypeAccessors.allowDirectSuper.get = function () { return (this.currentThisScope().flags & SCOPE_DIRECT_SUPER) > 0 };\n\n prototypeAccessors.treatFunctionsAsVar.get = function () { return this.treatFunctionsAsVarInScope(this.currentScope()) };\n\n prototypeAccessors.allowNewDotTarget.get = function () {\n for (var i = this.scopeStack.length - 1; i >= 0; i--) {\n var ref = this.scopeStack[i];\n var flags = ref.flags;\n if (flags & (SCOPE_CLASS_STATIC_BLOCK | SCOPE_CLASS_FIELD_INIT) ||\n ((flags & SCOPE_FUNCTION) && !(flags & SCOPE_ARROW))) { return true }\n }\n return false\n };\n\n prototypeAccessors.allowUsing.get = function () {\n var ref = this.currentScope();\n var flags = ref.flags;\n if (flags & SCOPE_SWITCH) { return false }\n if (!this.inModule && flags & SCOPE_TOP) { return false }\n return true\n };\n\n prototypeAccessors.inClassStaticBlock.get = function () {\n return (this.currentVarScope().flags & SCOPE_CLASS_STATIC_BLOCK) > 0\n };\n\n Parser.extend = function extend () {\n var plugins = [], len = arguments.length;\n while ( len-- ) plugins[ len ] = arguments[ len ];\n\n var cls = this;\n for (var i = 0; i < plugins.length; i++) { cls = plugins[i](cls); }\n return cls\n };\n\n Parser.parse = function parse (input, options) {\n return new this(options, input).parse()\n };\n\n Parser.parseExpressionAt = function parseExpressionAt (input, pos, options) {\n var parser = new this(options, input, pos);\n parser.nextToken();\n return parser.parseExpression()\n };\n\n Parser.tokenizer = function tokenizer (input, options) {\n return new this(options, input)\n };\n\n Object.defineProperties( Parser.prototype, prototypeAccessors );\n\n var pp$9 = Parser.prototype;\n\n // ## Parser utilities\n\n var literal = /^(?:'((?:\\\\[^]|[^'\\\\])*?)'|\"((?:\\\\[^]|[^\"\\\\])*?)\")/;\n pp$9.strictDirective = function(start) {\n if (this.options.ecmaVersion < 5) { return false }\n for (;;) {\n // Try to find string literal.\n skipWhiteSpace.lastIndex = start;\n start += skipWhiteSpace.exec(this.input)[0].length;\n var match = literal.exec(this.input.slice(start));\n if (!match) { return false }\n if ((match[1] || match[2]) === \"use strict\") {\n skipWhiteSpace.lastIndex = start + match[0].length;\n var spaceAfter = skipWhiteSpace.exec(this.input), end = spaceAfter.index + spaceAfter[0].length;\n var next = this.input.charAt(end);\n return next === \";\" || next === \"}\" ||\n (lineBreak.test(spaceAfter[0]) &&\n !(/[(`.[+\\-/*%<>=,?^&]/.test(next) || next === \"!\" && this.input.charAt(end + 1) === \"=\"))\n }\n start += match[0].length;\n\n // Skip semicolon, if any.\n skipWhiteSpace.lastIndex = start;\n start += skipWhiteSpace.exec(this.input)[0].length;\n if (this.input[start] === \";\")\n { start++; }\n }\n };\n\n // Predicate that tests whether the next token is of the given\n // type, and if yes, consumes it as a side effect.\n\n pp$9.eat = function(type) {\n if (this.type === type) {\n this.next();\n return true\n } else {\n return false\n }\n };\n\n // Tests whether parsed token is a contextual keyword.\n\n pp$9.isContextual = function(name) {\n return this.type === types$1.name && this.value === name && !this.containsEsc\n };\n\n // Consumes contextual keyword if possible.\n\n pp$9.eatContextual = function(name) {\n if (!this.isContextual(name)) { return false }\n this.next();\n return true\n };\n\n // Asserts that following token is given contextual keyword.\n\n pp$9.expectContextual = function(name) {\n if (!this.eatContextual(name)) { this.unexpected(); }\n };\n\n // Test whether a semicolon can be inserted at the current position.\n\n pp$9.canInsertSemicolon = function() {\n return this.type === types$1.eof ||\n this.type === types$1.braceR ||\n lineBreak.test(this.input.slice(this.lastTokEnd, this.start))\n };\n\n pp$9.insertSemicolon = function() {\n if (this.canInsertSemicolon()) {\n if (this.options.onInsertedSemicolon)\n { this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc); }\n return true\n }\n };\n\n // Consume a semicolon, or, failing that, see if we are allowed to\n // pretend that there is a semicolon at this position.\n\n pp$9.semicolon = function() {\n if (!this.eat(types$1.semi) && !this.insertSemicolon()) { this.unexpected(); }\n };\n\n pp$9.afterTrailingComma = function(tokType, notNext) {\n if (this.type === tokType) {\n if (this.options.onTrailingComma)\n { this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc); }\n if (!notNext)\n { this.next(); }\n return true\n }\n };\n\n // Expect a token of a given type. If found, consume it, otherwise,\n // raise an unexpected token error.\n\n pp$9.expect = function(type) {\n this.eat(type) || this.unexpected();\n };\n\n // Raise an unexpected token error.\n\n pp$9.unexpected = function(pos) {\n this.raise(pos != null ? pos : this.start, \"Unexpected token\");\n };\n\n var DestructuringErrors = function DestructuringErrors() {\n this.shorthandAssign =\n this.trailingComma =\n this.parenthesizedAssign =\n this.parenthesizedBind =\n this.doubleProto =\n -1;\n };\n\n pp$9.checkPatternErrors = function(refDestructuringErrors, isAssign) {\n if (!refDestructuringErrors) { return }\n if (refDestructuringErrors.trailingComma > -1)\n { this.raiseRecoverable(refDestructuringErrors.trailingComma, \"Comma is not permitted after the rest element\"); }\n var parens = isAssign ? refDestructuringErrors.parenthesizedAssign : refDestructuringErrors.parenthesizedBind;\n if (parens > -1) { this.raiseRecoverable(parens, isAssign ? \"Assigning to rvalue\" : \"Parenthesized pattern\"); }\n };\n\n pp$9.checkExpressionErrors = function(refDestructuringErrors, andThrow) {\n if (!refDestructuringErrors) { return false }\n var shorthandAssign = refDestructuringErrors.shorthandAssign;\n var doubleProto = refDestructuringErrors.doubleProto;\n if (!andThrow) { return shorthandAssign >= 0 || doubleProto >= 0 }\n if (shorthandAssign >= 0)\n { this.raise(shorthandAssign, \"Shorthand property assignments are valid only in destructuring patterns\"); }\n if (doubleProto >= 0)\n { this.raiseRecoverable(doubleProto, \"Redefinition of __proto__ property\"); }\n };\n\n pp$9.checkYieldAwaitInDefaultParams = function() {\n if (this.yieldPos && (!this.awaitPos || this.yieldPos < this.awaitPos))\n { this.raise(this.yieldPos, \"Yield expression cannot be a default value\"); }\n if (this.awaitPos)\n { this.raise(this.awaitPos, \"Await expression cannot be a default value\"); }\n };\n\n pp$9.isSimpleAssignTarget = function(expr) {\n if (expr.type === \"ParenthesizedExpression\")\n { return this.isSimpleAssignTarget(expr.expression) }\n return expr.type === \"Identifier\" || expr.type === \"MemberExpression\"\n };\n\n var pp$8 = Parser.prototype;\n\n // ### Statement parsing\n\n // Parse a program. Initializes the parser, reads any number of\n // statements, and wraps them in a Program node. Optionally takes a\n // `program` argument. If present, the statements will be appended\n // to its body instead of creating a new node.\n\n pp$8.parseTopLevel = function(node) {\n var exports = Object.create(null);\n if (!node.body) { node.body = []; }\n while (this.type !== types$1.eof) {\n var stmt = this.parseStatement(null, true, exports);\n node.body.push(stmt);\n }\n if (this.inModule)\n { for (var i = 0, list = Object.keys(this.undefinedExports); i < list.length; i += 1)\n {\n var name = list[i];\n\n this.raiseRecoverable(this.undefinedExports[name].start, (\"Export '\" + name + \"' is not defined\"));\n } }\n this.adaptDirectivePrologue(node.body);\n this.next();\n node.sourceType = this.options.sourceType === \"commonjs\" ? \"script\" : this.options.sourceType;\n return this.finishNode(node, \"Program\")\n };\n\n var loopLabel = {kind: \"loop\"}, switchLabel = {kind: \"switch\"};\n\n pp$8.isLet = function(context) {\n if (this.options.ecmaVersion < 6 || !this.isContextual(\"let\")) { return false }\n skipWhiteSpace.lastIndex = this.pos;\n var skip = skipWhiteSpace.exec(this.input);\n var next = this.pos + skip[0].length, nextCh = this.fullCharCodeAt(next);\n // For ambiguous cases, determine if a LexicalDeclaration (or only a\n // Statement) is allowed here. If context is not empty then only a Statement\n // is allowed. However, `let [` is an explicit negative lookahead for\n // ExpressionStatement, so special-case it first.\n if (nextCh === 91 || nextCh === 92) { return true } // '[', '\\'\n if (context) { return false }\n\n if (nextCh === 123) { return true } // '{'\n if (isIdentifierStart(nextCh)) {\n var start = next;\n do { next += nextCh <= 0xffff ? 1 : 2; }\n while (isIdentifierChar(nextCh = this.fullCharCodeAt(next)))\n if (nextCh === 92) { return true }\n var ident = this.input.slice(start, next);\n if (!keywordRelationalOperator.test(ident)) { return true }\n }\n return false\n };\n\n // check 'async [no LineTerminator here] function'\n // - 'async /*foo*/ function' is OK.\n // - 'async /*\\n*/ function' is invalid.\n pp$8.isAsyncFunction = function() {\n if (this.options.ecmaVersion < 8 || !this.isContextual(\"async\"))\n { return false }\n\n skipWhiteSpace.lastIndex = this.pos;\n var skip = skipWhiteSpace.exec(this.input);\n var next = this.pos + skip[0].length, after;\n return !lineBreak.test(this.input.slice(this.pos, next)) &&\n this.input.slice(next, next + 8) === \"function\" &&\n (next + 8 === this.input.length ||\n !(isIdentifierChar(after = this.fullCharCodeAt(next + 8)) || after === 92 /* '\\' */))\n };\n\n pp$8.isUsingKeyword = function(isAwaitUsing, isFor) {\n if (this.options.ecmaVersion < 17 || !this.isContextual(isAwaitUsing ? \"await\" : \"using\"))\n { return false }\n\n skipWhiteSpace.lastIndex = this.pos;\n var skip = skipWhiteSpace.exec(this.input);\n var next = this.pos + skip[0].length;\n\n if (lineBreak.test(this.input.slice(this.pos, next))) { return false }\n\n if (isAwaitUsing) {\n var usingEndPos = next + 5 /* using */, after;\n if (this.input.slice(next, usingEndPos) !== \"using\" ||\n usingEndPos === this.input.length ||\n isIdentifierChar(after = this.fullCharCodeAt(usingEndPos)) ||\n after === 92 /* '\\' */\n ) { return false }\n\n skipWhiteSpace.lastIndex = usingEndPos;\n var skipAfterUsing = skipWhiteSpace.exec(this.input);\n next = usingEndPos + skipAfterUsing[0].length;\n if (skipAfterUsing && lineBreak.test(this.input.slice(usingEndPos, next))) { return false }\n }\n\n var ch = this.fullCharCodeAt(next);\n if (!isIdentifierStart(ch) && ch !== 92 /* '\\' */) { return false }\n var idStart = next;\n do { next += ch <= 0xffff ? 1 : 2; }\n while (isIdentifierChar(ch = this.fullCharCodeAt(next)))\n if (ch === 92) { return true }\n var id = this.input.slice(idStart, next);\n if (keywordRelationalOperator.test(id) || isFor && id === \"of\") { return false }\n return true\n };\n\n pp$8.isAwaitUsing = function(isFor) {\n return this.isUsingKeyword(true, isFor)\n };\n\n pp$8.isUsing = function(isFor) {\n return this.isUsingKeyword(false, isFor)\n };\n\n // Parse a single statement.\n //\n // If expecting a statement and finding a slash operator, parse a\n // regular expression literal. This is to handle cases like\n // `if (foo) /blah/.exec(foo)`, where looking at the previous token\n // does not help.\n\n pp$8.parseStatement = function(context, topLevel, exports) {\n var starttype = this.type, node = this.startNode(), kind;\n\n if (this.isLet(context)) {\n starttype = types$1._var;\n kind = \"let\";\n }\n\n // Most types of statements are recognized by the keyword they\n // start with. Many are trivial to parse, some require a bit of\n // complexity.\n\n switch (starttype) {\n case types$1._break: case types$1._continue: return this.parseBreakContinueStatement(node, starttype.keyword)\n case types$1._debugger: return this.parseDebuggerStatement(node)\n case types$1._do: return this.parseDoStatement(node)\n case types$1._for: return this.parseForStatement(node)\n case types$1._function:\n // Function as sole body of either an if statement or a labeled statement\n // works, but not when it is part of a labeled statement that is the sole\n // body of an if statement.\n if ((context && (this.strict || context !== \"if\" && context !== \"label\")) && this.options.ecmaVersion >= 6) { this.unexpected(); }\n return this.parseFunctionStatement(node, false, !context)\n case types$1._class:\n if (context) { this.unexpected(); }\n return this.parseClass(node, true)\n case types$1._if: return this.parseIfStatement(node)\n case types$1._return: return this.parseReturnStatement(node)\n case types$1._switch: return this.parseSwitchStatement(node)\n case types$1._throw: return this.parseThrowStatement(node)\n case types$1._try: return this.parseTryStatement(node)\n case types$1._const: case types$1._var:\n kind = kind || this.value;\n if (context && kind !== \"var\") { this.unexpected(); }\n return this.parseVarStatement(node, kind)\n case types$1._while: return this.parseWhileStatement(node)\n case types$1._with: return this.parseWithStatement(node)\n case types$1.braceL: return this.parseBlock(true, node)\n case types$1.semi: return this.parseEmptyStatement(node)\n case types$1._export:\n case types$1._import:\n if (this.options.ecmaVersion > 10 && starttype === types$1._import) {\n skipWhiteSpace.lastIndex = this.pos;\n var skip = skipWhiteSpace.exec(this.input);\n var next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next);\n if (nextCh === 40 || nextCh === 46) // '(' or '.'\n { return this.parseExpressionStatement(node, this.parseExpression()) }\n }\n\n if (!this.options.allowImportExportEverywhere) {\n if (!topLevel)\n { this.raise(this.start, \"'import' and 'export' may only appear at the top level\"); }\n if (!this.inModule)\n { this.raise(this.start, \"'import' and 'export' may appear only with 'sourceType: module'\"); }\n }\n return starttype === types$1._import ? this.parseImport(node) : this.parseExport(node, exports)\n\n // If the statement does not start with a statement keyword or a\n // brace, it's an ExpressionStatement or LabeledStatement. We\n // simply start parsing an expression, and afterwards, if the\n // next token is a colon and the expression was a simple\n // Identifier node, we switch to interpreting it as a label.\n default:\n if (this.isAsyncFunction()) {\n if (context) { this.unexpected(); }\n this.next();\n return this.parseFunctionStatement(node, true, !context)\n }\n\n var usingKind = this.isAwaitUsing(false) ? \"await using\" : this.isUsing(false) ? \"using\" : null;\n if (usingKind) {\n if (!this.allowUsing) {\n this.raise(this.start, \"Using declaration cannot appear in the top level when source type is `script` or in the bare case statement\");\n }\n if (usingKind === \"await using\") {\n if (!this.canAwait) {\n this.raise(this.start, \"Await using cannot appear outside of async function\");\n }\n this.next();\n }\n this.next();\n this.parseVar(node, false, usingKind);\n this.semicolon();\n return this.finishNode(node, \"VariableDeclaration\")\n }\n\n var maybeName = this.value, expr = this.parseExpression();\n if (starttype === types$1.name && expr.type === \"Identifier\" && this.eat(types$1.colon))\n { return this.parseLabeledStatement(node, maybeName, expr, context) }\n else { return this.parseExpressionStatement(node, expr) }\n }\n };\n\n pp$8.parseBreakContinueStatement = function(node, keyword) {\n var isBreak = keyword === \"break\";\n this.next();\n if (this.eat(types$1.semi) || this.insertSemicolon()) { node.label = null; }\n else if (this.type !== types$1.name) { this.unexpected(); }\n else {\n node.label = this.parseIdent();\n this.semicolon();\n }\n\n // Verify that there is an actual destination to break or\n // continue to.\n var i = 0;\n for (; i < this.labels.length; ++i) {\n var lab = this.labels[i];\n if (node.label == null || lab.name === node.label.name) {\n if (lab.kind != null && (isBreak || lab.kind === \"loop\")) { break }\n if (node.label && isBreak) { break }\n }\n }\n if (i === this.labels.length) { this.raise(node.start, \"Unsyntactic \" + keyword); }\n return this.finishNode(node, isBreak ? \"BreakStatement\" : \"ContinueStatement\")\n };\n\n pp$8.parseDebuggerStatement = function(node) {\n this.next();\n this.semicolon();\n return this.finishNode(node, \"DebuggerStatement\")\n };\n\n pp$8.parseDoStatement = function(node) {\n this.next();\n this.labels.push(loopLabel);\n node.body = this.parseStatement(\"do\");\n this.labels.pop();\n this.expect(types$1._while);\n node.test = this.parseParenExpression();\n if (this.options.ecmaVersion >= 6)\n { this.eat(types$1.semi); }\n else\n { this.semicolon(); }\n return this.finishNode(node, \"DoWhileStatement\")\n };\n\n // Disambiguating between a `for` and a `for`/`in` or `for`/`of`\n // loop is non-trivial. Basically, we have to parse the init `var`\n // statement or expression, disallowing the `in` operator (see\n // the second parameter to `parseExpression`), and then check\n // whether the next token is `in` or `of`. When there is no init\n // part (semicolon immediately after the opening parenthesis), it\n // is a regular `for` loop.\n\n pp$8.parseForStatement = function(node) {\n this.next();\n var awaitAt = (this.options.ecmaVersion >= 9 && this.canAwait && this.eatContextual(\"await\")) ? this.lastTokStart : -1;\n this.labels.push(loopLabel);\n this.enterScope(0);\n this.expect(types$1.parenL);\n if (this.type === types$1.semi) {\n if (awaitAt > -1) { this.unexpected(awaitAt); }\n return this.parseFor(node, null)\n }\n var isLet = this.isLet();\n if (this.type === types$1._var || this.type === types$1._const || isLet) {\n var init$1 = this.startNode(), kind = isLet ? \"let\" : this.value;\n this.next();\n this.parseVar(init$1, true, kind);\n this.finishNode(init$1, \"VariableDeclaration\");\n return this.parseForAfterInit(node, init$1, awaitAt)\n }\n var startsWithLet = this.isContextual(\"let\"), isForOf = false;\n\n var usingKind = this.isUsing(true) ? \"using\" : this.isAwaitUsing(true) ? \"await using\" : null;\n if (usingKind) {\n var init$2 = this.startNode();\n this.next();\n if (usingKind === \"await using\") {\n if (!this.canAwait) {\n this.raise(this.start, \"Await using cannot appear outside of async function\");\n }\n this.next();\n }\n this.parseVar(init$2, true, usingKind);\n this.finishNode(init$2, \"VariableDeclaration\");\n return this.parseForAfterInit(node, init$2, awaitAt)\n }\n var containsEsc = this.containsEsc;\n var refDestructuringErrors = new DestructuringErrors;\n var initPos = this.start;\n var init = awaitAt > -1\n ? this.parseExprSubscripts(refDestructuringErrors, \"await\")\n : this.parseExpression(true, refDestructuringErrors);\n if (this.type === types$1._in || (isForOf = this.options.ecmaVersion >= 6 && this.isContextual(\"of\"))) {\n if (awaitAt > -1) { // implies `ecmaVersion >= 9` (see declaration of awaitAt)\n if (this.type === types$1._in) { this.unexpected(awaitAt); }\n node.await = true;\n } else if (isForOf && this.options.ecmaVersion >= 8) {\n if (init.start === initPos && !containsEsc && init.type === \"Identifier\" && init.name === \"async\") { this.unexpected(); }\n else if (this.options.ecmaVersion >= 9) { node.await = false; }\n }\n if (startsWithLet && isForOf) { this.raise(init.start, \"The left-hand side of a for-of loop may not start with 'let'.\"); }\n this.toAssignable(init, false, refDestructuringErrors);\n this.checkLValPattern(init);\n return this.parseForIn(node, init)\n } else {\n this.checkExpressionErrors(refDestructuringErrors, true);\n }\n if (awaitAt > -1) { this.unexpected(awaitAt); }\n return this.parseFor(node, init)\n };\n\n // Helper method to parse for loop after variable initialization\n pp$8.parseForAfterInit = function(node, init, awaitAt) {\n if ((this.type === types$1._in || (this.options.ecmaVersion >= 6 && this.isContextual(\"of\"))) && init.declarations.length === 1) {\n if (this.options.ecmaVersion >= 9) {\n if (this.type === types$1._in) {\n if (awaitAt > -1) { this.unexpected(awaitAt); }\n } else { node.await = awaitAt > -1; }\n }\n return this.parseForIn(node, init)\n }\n if (awaitAt > -1) { this.unexpected(awaitAt); }\n return this.parseFor(node, init)\n };\n\n pp$8.parseFunctionStatement = function(node, isAsync, declarationPosition) {\n this.next();\n return this.parseFunction(node, FUNC_STATEMENT | (declarationPosition ? 0 : FUNC_HANGING_STATEMENT), false, isAsync)\n };\n\n pp$8.parseIfStatement = function(node) {\n this.next();\n node.test = this.parseParenExpression();\n // allow function declarations in branches, but only in non-strict mode\n node.consequent = this.parseStatement(\"if\");\n node.alternate = this.eat(types$1._else) ? this.parseStatement(\"if\") : null;\n return this.finishNode(node, \"IfStatement\")\n };\n\n pp$8.parseReturnStatement = function(node) {\n if (!this.allowReturn)\n { this.raise(this.start, \"'return' outside of function\"); }\n this.next();\n\n // In `return` (and `break`/`continue`), the keywords with\n // optional arguments, we eagerly look for a semicolon or the\n // possibility to insert one.\n\n if (this.eat(types$1.semi) || this.insertSemicolon()) { node.argument = null; }\n else { node.argument = this.parseExpression(); this.semicolon(); }\n return this.finishNode(node, \"ReturnStatement\")\n };\n\n pp$8.parseSwitchStatement = function(node) {\n this.next();\n node.discriminant = this.parseParenExpression();\n node.cases = [];\n this.expect(types$1.braceL);\n this.labels.push(switchLabel);\n this.enterScope(SCOPE_SWITCH);\n\n // Statements under must be grouped (by label) in SwitchCase\n // nodes. `cur` is used to keep the node that we are currently\n // adding statements to.\n\n var cur;\n for (var sawDefault = false; this.type !== types$1.braceR;) {\n if (this.type === types$1._case || this.type === types$1._default) {\n var isCase = this.type === types$1._case;\n if (cur) { this.finishNode(cur, \"SwitchCase\"); }\n node.cases.push(cur = this.startNode());\n cur.consequent = [];\n this.next();\n if (isCase) {\n cur.test = this.parseExpression();\n } else {\n if (sawDefault) { this.raiseRecoverable(this.lastTokStart, \"Multiple default clauses\"); }\n sawDefault = true;\n cur.test = null;\n }\n this.expect(types$1.colon);\n } else {\n if (!cur) { this.unexpected(); }\n cur.consequent.push(this.parseStatement(null));\n }\n }\n this.exitScope();\n if (cur) { this.finishNode(cur, \"SwitchCase\"); }\n this.next(); // Closing brace\n this.labels.pop();\n return this.finishNode(node, \"SwitchStatement\")\n };\n\n pp$8.parseThrowStatement = function(node) {\n this.next();\n if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start)))\n { this.raise(this.lastTokEnd, \"Illegal newline after throw\"); }\n node.argument = this.parseExpression();\n this.semicolon();\n return this.finishNode(node, \"ThrowStatement\")\n };\n\n // Reused empty array added for node fields that are always empty.\n\n var empty$1 = [];\n\n pp$8.parseCatchClauseParam = function() {\n var param = this.parseBindingAtom();\n var simple = param.type === \"Identifier\";\n this.enterScope(simple ? SCOPE_SIMPLE_CATCH : 0);\n this.checkLValPattern(param, simple ? BIND_SIMPLE_CATCH : BIND_LEXICAL);\n this.expect(types$1.parenR);\n\n return param\n };\n\n pp$8.parseTryStatement = function(node) {\n this.next();\n node.block = this.parseBlock();\n node.handler = null;\n if (this.type === types$1._catch) {\n var clause = this.startNode();\n this.next();\n if (this.eat(types$1.parenL)) {\n clause.param = this.parseCatchClauseParam();\n } else {\n if (this.options.ecmaVersion < 10) { this.unexpected(); }\n clause.param = null;\n this.enterScope(0);\n }\n clause.body = this.parseBlock(false);\n this.exitScope();\n node.handler = this.finishNode(clause, \"CatchClause\");\n }\n node.finalizer = this.eat(types$1._finally) ? this.parseBlock() : null;\n if (!node.handler && !node.finalizer)\n { this.raise(node.start, \"Missing catch or finally clause\"); }\n return this.finishNode(node, \"TryStatement\")\n };\n\n pp$8.parseVarStatement = function(node, kind, allowMissingInitializer) {\n this.next();\n this.parseVar(node, false, kind, allowMissingInitializer);\n this.semicolon();\n return this.finishNode(node, \"VariableDeclaration\")\n };\n\n pp$8.parseWhileStatement = function(node) {\n this.next();\n node.test = this.parseParenExpression();\n this.labels.push(loopLabel);\n node.body = this.parseStatement(\"while\");\n this.labels.pop();\n return this.finishNode(node, \"WhileStatement\")\n };\n\n pp$8.parseWithStatement = function(node) {\n if (this.strict) { this.raise(this.start, \"'with' in strict mode\"); }\n this.next();\n node.object = this.parseParenExpression();\n node.body = this.parseStatement(\"with\");\n return this.finishNode(node, \"WithStatement\")\n };\n\n pp$8.parseEmptyStatement = function(node) {\n this.next();\n return this.finishNode(node, \"EmptyStatement\")\n };\n\n pp$8.parseLabeledStatement = function(node, maybeName, expr, context) {\n for (var i$1 = 0, list = this.labels; i$1 < list.length; i$1 += 1)\n {\n var label = list[i$1];\n\n if (label.name === maybeName)\n { this.raise(expr.start, \"Label '\" + maybeName + \"' is already declared\");\n } }\n var kind = this.type.isLoop ? \"loop\" : this.type === types$1._switch ? \"switch\" : null;\n for (var i = this.labels.length - 1; i >= 0; i--) {\n var label$1 = this.labels[i];\n if (label$1.statementStart === node.start) {\n // Update information about previous labels on this node\n label$1.statementStart = this.start;\n label$1.kind = kind;\n } else { break }\n }\n this.labels.push({name: maybeName, kind: kind, statementStart: this.start});\n node.body = this.parseStatement(context ? context.indexOf(\"label\") === -1 ? context + \"label\" : context : \"label\");\n this.labels.pop();\n node.label = expr;\n return this.finishNode(node, \"LabeledStatement\")\n };\n\n pp$8.parseExpressionStatement = function(node, expr) {\n node.expression = expr;\n this.semicolon();\n return this.finishNode(node, \"ExpressionStatement\")\n };\n\n // Parse a semicolon-enclosed block of statements, handling `\"use\n // strict\"` declarations when `allowStrict` is true (used for\n // function bodies).\n\n pp$8.parseBlock = function(createNewLexicalScope, node, exitStrict) {\n if ( createNewLexicalScope === void 0 ) createNewLexicalScope = true;\n if ( node === void 0 ) node = this.startNode();\n\n node.body = [];\n this.expect(types$1.braceL);\n if (createNewLexicalScope) { this.enterScope(0); }\n while (this.type !== types$1.braceR) {\n var stmt = this.parseStatement(null);\n node.body.push(stmt);\n }\n if (exitStrict) { this.strict = false; }\n this.next();\n if (createNewLexicalScope) { this.exitScope(); }\n return this.finishNode(node, \"BlockStatement\")\n };\n\n // Parse a regular `for` loop. The disambiguation code in\n // `parseStatement` will already have parsed the init statement or\n // expression.\n\n pp$8.parseFor = function(node, init) {\n node.init = init;\n this.expect(types$1.semi);\n node.test = this.type === types$1.semi ? null : this.parseExpression();\n this.expect(types$1.semi);\n node.update = this.type === types$1.parenR ? null : this.parseExpression();\n this.expect(types$1.parenR);\n node.body = this.parseStatement(\"for\");\n this.exitScope();\n this.labels.pop();\n return this.finishNode(node, \"ForStatement\")\n };\n\n // Parse a `for`/`in` and `for`/`of` loop, which are almost\n // same from parser's perspective.\n\n pp$8.parseForIn = function(node, init) {\n var isForIn = this.type === types$1._in;\n this.next();\n\n if (\n init.type === \"VariableDeclaration\" &&\n init.declarations[0].init != null &&\n (\n !isForIn ||\n this.options.ecmaVersion < 8 ||\n this.strict ||\n init.kind !== \"var\" ||\n init.declarations[0].id.type !== \"Identifier\"\n )\n ) {\n this.raise(\n init.start,\n ((isForIn ? \"for-in\" : \"for-of\") + \" loop variable declaration may not have an initializer\")\n );\n }\n node.left = init;\n node.right = isForIn ? this.parseExpression() : this.parseMaybeAssign();\n this.expect(types$1.parenR);\n node.body = this.parseStatement(\"for\");\n this.exitScope();\n this.labels.pop();\n return this.finishNode(node, isForIn ? \"ForInStatement\" : \"ForOfStatement\")\n };\n\n // Parse a list of variable declarations.\n\n pp$8.parseVar = function(node, isFor, kind, allowMissingInitializer) {\n node.declarations = [];\n node.kind = kind;\n for (;;) {\n var decl = this.startNode();\n this.parseVarId(decl, kind);\n if (this.eat(types$1.eq)) {\n decl.init = this.parseMaybeAssign(isFor);\n } else if (!allowMissingInitializer && kind === \"const\" && !(this.type === types$1._in || (this.options.ecmaVersion >= 6 && this.isContextual(\"of\")))) {\n this.unexpected();\n } else if (!allowMissingInitializer && (kind === \"using\" || kind === \"await using\") && this.options.ecmaVersion >= 17 && this.type !== types$1._in && !this.isContextual(\"of\")) {\n this.raise(this.lastTokEnd, (\"Missing initializer in \" + kind + \" declaration\"));\n } else if (!allowMissingInitializer && decl.id.type !== \"Identifier\" && !(isFor && (this.type === types$1._in || this.isContextual(\"of\")))) {\n this.raise(this.lastTokEnd, \"Complex binding patterns require an initialization value\");\n } else {\n decl.init = null;\n }\n node.declarations.push(this.finishNode(decl, \"VariableDeclarator\"));\n if (!this.eat(types$1.comma)) { break }\n }\n return node\n };\n\n pp$8.parseVarId = function(decl, kind) {\n decl.id = kind === \"using\" || kind === \"await using\"\n ? this.parseIdent()\n : this.parseBindingAtom();\n\n this.checkLValPattern(decl.id, kind === \"var\" ? BIND_VAR : BIND_LEXICAL, false);\n };\n\n var FUNC_STATEMENT = 1, FUNC_HANGING_STATEMENT = 2, FUNC_NULLABLE_ID = 4;\n\n // Parse a function declaration or literal (depending on the\n // `statement & FUNC_STATEMENT`).\n\n // Remove `allowExpressionBody` for 7.0.0, as it is only called with false\n pp$8.parseFunction = function(node, statement, allowExpressionBody, isAsync, forInit) {\n this.initFunction(node);\n if (this.options.ecmaVersion >= 9 || this.options.ecmaVersion >= 6 && !isAsync) {\n if (this.type === types$1.star && (statement & FUNC_HANGING_STATEMENT))\n { this.unexpected(); }\n node.generator = this.eat(types$1.star);\n }\n if (this.options.ecmaVersion >= 8)\n { node.async = !!isAsync; }\n\n if (statement & FUNC_STATEMENT) {\n node.id = (statement & FUNC_NULLABLE_ID) && this.type !== types$1.name ? null : this.parseIdent();\n if (node.id && !(statement & FUNC_HANGING_STATEMENT))\n // If it is a regular function declaration in sloppy mode, then it is\n // subject to Annex B semantics (BIND_FUNCTION). Otherwise, the binding\n // mode depends on properties of the current scope (see\n // treatFunctionsAsVar).\n { this.checkLValSimple(node.id, (this.strict || node.generator || node.async) ? this.treatFunctionsAsVar ? BIND_VAR : BIND_LEXICAL : BIND_FUNCTION); }\n }\n\n var oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n this.yieldPos = 0;\n this.awaitPos = 0;\n this.awaitIdentPos = 0;\n this.enterScope(functionFlags(node.async, node.generator));\n\n if (!(statement & FUNC_STATEMENT))\n { node.id = this.type === types$1.name ? this.parseIdent() : null; }\n\n this.parseFunctionParams(node);\n this.parseFunctionBody(node, allowExpressionBody, false, forInit);\n\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n this.awaitIdentPos = oldAwaitIdentPos;\n return this.finishNode(node, (statement & FUNC_STATEMENT) ? \"FunctionDeclaration\" : \"FunctionExpression\")\n };\n\n pp$8.parseFunctionParams = function(node) {\n this.expect(types$1.parenL);\n node.params = this.parseBindingList(types$1.parenR, false, this.options.ecmaVersion >= 8);\n this.checkYieldAwaitInDefaultParams();\n };\n\n // Parse a class declaration or literal (depending on the\n // `isStatement` parameter).\n\n pp$8.parseClass = function(node, isStatement) {\n this.next();\n\n // ecma-262 14.6 Class Definitions\n // A class definition is always strict mode code.\n var oldStrict = this.strict;\n this.strict = true;\n\n this.parseClassId(node, isStatement);\n this.parseClassSuper(node);\n var privateNameMap = this.enterClassBody();\n var classBody = this.startNode();\n var hadConstructor = false;\n classBody.body = [];\n this.expect(types$1.braceL);\n while (this.type !== types$1.braceR) {\n var element = this.parseClassElement(node.superClass !== null);\n if (element) {\n classBody.body.push(element);\n if (element.type === \"MethodDefinition\" && element.kind === \"constructor\") {\n if (hadConstructor) { this.raiseRecoverable(element.start, \"Duplicate constructor in the same class\"); }\n hadConstructor = true;\n } else if (element.key && element.key.type === \"PrivateIdentifier\" && isPrivateNameConflicted(privateNameMap, element)) {\n this.raiseRecoverable(element.key.start, (\"Identifier '#\" + (element.key.name) + \"' has already been declared\"));\n }\n }\n }\n this.strict = oldStrict;\n this.next();\n node.body = this.finishNode(classBody, \"ClassBody\");\n this.exitClassBody();\n return this.finishNode(node, isStatement ? \"ClassDeclaration\" : \"ClassExpression\")\n };\n\n pp$8.parseClassElement = function(constructorAllowsSuper) {\n if (this.eat(types$1.semi)) { return null }\n\n var ecmaVersion = this.options.ecmaVersion;\n var node = this.startNode();\n var keyName = \"\";\n var isGenerator = false;\n var isAsync = false;\n var kind = \"method\";\n var isStatic = false;\n\n if (this.eatContextual(\"static\")) {\n // Parse static init block\n if (ecmaVersion >= 13 && this.eat(types$1.braceL)) {\n this.parseClassStaticBlock(node);\n return node\n }\n if (this.isClassElementNameStart() || this.type === types$1.star) {\n isStatic = true;\n } else {\n keyName = \"static\";\n }\n }\n node.static = isStatic;\n if (!keyName && ecmaVersion >= 8 && this.eatContextual(\"async\")) {\n if ((this.isClassElementNameStart() || this.type === types$1.star) && !this.canInsertSemicolon()) {\n isAsync = true;\n } else {\n keyName = \"async\";\n }\n }\n if (!keyName && (ecmaVersion >= 9 || !isAsync) && this.eat(types$1.star)) {\n isGenerator = true;\n }\n if (!keyName && !isAsync && !isGenerator) {\n var lastValue = this.value;\n if (this.eatContextual(\"get\") || this.eatContextual(\"set\")) {\n if (this.isClassElementNameStart()) {\n kind = lastValue;\n } else {\n keyName = lastValue;\n }\n }\n }\n\n // Parse element name\n if (keyName) {\n // 'async', 'get', 'set', or 'static' were not a keyword contextually.\n // The last token is any of those. Make it the element name.\n node.computed = false;\n node.key = this.startNodeAt(this.lastTokStart, this.lastTokStartLoc);\n node.key.name = keyName;\n this.finishNode(node.key, \"Identifier\");\n } else {\n this.parseClassElementName(node);\n }\n\n // Parse element value\n if (ecmaVersion < 13 || this.type === types$1.parenL || kind !== \"method\" || isGenerator || isAsync) {\n var isConstructor = !node.static && checkKeyName(node, \"constructor\");\n var allowsDirectSuper = isConstructor && constructorAllowsSuper;\n // Couldn't move this check into the 'parseClassMethod' method for backward compatibility.\n if (isConstructor && kind !== \"method\") { this.raise(node.key.start, \"Constructor can't have get/set modifier\"); }\n node.kind = isConstructor ? \"constructor\" : kind;\n this.parseClassMethod(node, isGenerator, isAsync, allowsDirectSuper);\n } else {\n this.parseClassField(node);\n }\n\n return node\n };\n\n pp$8.isClassElementNameStart = function() {\n return (\n this.type === types$1.name ||\n this.type === types$1.privateId ||\n this.type === types$1.num ||\n this.type === types$1.string ||\n this.type === types$1.bracketL ||\n this.type.keyword\n )\n };\n\n pp$8.parseClassElementName = function(element) {\n if (this.type === types$1.privateId) {\n if (this.value === \"constructor\") {\n this.raise(this.start, \"Classes can't have an element named '#constructor'\");\n }\n element.computed = false;\n element.key = this.parsePrivateIdent();\n } else {\n this.parsePropertyName(element);\n }\n };\n\n pp$8.parseClassMethod = function(method, isGenerator, isAsync, allowsDirectSuper) {\n // Check key and flags\n var key = method.key;\n if (method.kind === \"constructor\") {\n if (isGenerator) { this.raise(key.start, \"Constructor can't be a generator\"); }\n if (isAsync) { this.raise(key.start, \"Constructor can't be an async method\"); }\n } else if (method.static && checkKeyName(method, \"prototype\")) {\n this.raise(key.start, \"Classes may not have a static property named prototype\");\n }\n\n // Parse value\n var value = method.value = this.parseMethod(isGenerator, isAsync, allowsDirectSuper);\n\n // Check value\n if (method.kind === \"get\" && value.params.length !== 0)\n { this.raiseRecoverable(value.start, \"getter should have no params\"); }\n if (method.kind === \"set\" && value.params.length !== 1)\n { this.raiseRecoverable(value.start, \"setter should have exactly one param\"); }\n if (method.kind === \"set\" && value.params[0].type === \"RestElement\")\n { this.raiseRecoverable(value.params[0].start, \"Setter cannot use rest params\"); }\n\n return this.finishNode(method, \"MethodDefinition\")\n };\n\n pp$8.parseClassField = function(field) {\n if (checkKeyName(field, \"constructor\")) {\n this.raise(field.key.start, \"Classes can't have a field named 'constructor'\");\n } else if (field.static && checkKeyName(field, \"prototype\")) {\n this.raise(field.key.start, \"Classes can't have a static field named 'prototype'\");\n }\n\n if (this.eat(types$1.eq)) {\n // To raise SyntaxError if 'arguments' exists in the initializer.\n this.enterScope(SCOPE_CLASS_FIELD_INIT | SCOPE_SUPER);\n field.value = this.parseMaybeAssign();\n this.exitScope();\n } else {\n field.value = null;\n }\n this.semicolon();\n\n return this.finishNode(field, \"PropertyDefinition\")\n };\n\n pp$8.parseClassStaticBlock = function(node) {\n node.body = [];\n\n var oldLabels = this.labels;\n this.labels = [];\n this.enterScope(SCOPE_CLASS_STATIC_BLOCK | SCOPE_SUPER);\n while (this.type !== types$1.braceR) {\n var stmt = this.parseStatement(null);\n node.body.push(stmt);\n }\n this.next();\n this.exitScope();\n this.labels = oldLabels;\n\n return this.finishNode(node, \"StaticBlock\")\n };\n\n pp$8.parseClassId = function(node, isStatement) {\n if (this.type === types$1.name) {\n node.id = this.parseIdent();\n if (isStatement)\n { this.checkLValSimple(node.id, BIND_LEXICAL, false); }\n } else {\n if (isStatement === true)\n { this.unexpected(); }\n node.id = null;\n }\n };\n\n pp$8.parseClassSuper = function(node) {\n node.superClass = this.eat(types$1._extends) ? this.parseExprSubscripts(null, false) : null;\n };\n\n pp$8.enterClassBody = function() {\n var element = {declared: Object.create(null), used: []};\n this.privateNameStack.push(element);\n return element.declared\n };\n\n pp$8.exitClassBody = function() {\n var ref = this.privateNameStack.pop();\n var declared = ref.declared;\n var used = ref.used;\n if (!this.options.checkPrivateFields) { return }\n var len = this.privateNameStack.length;\n var parent = len === 0 ? null : this.privateNameStack[len - 1];\n for (var i = 0; i < used.length; ++i) {\n var id = used[i];\n if (!hasOwn(declared, id.name)) {\n if (parent) {\n parent.used.push(id);\n } else {\n this.raiseRecoverable(id.start, (\"Private field '#\" + (id.name) + \"' must be declared in an enclosing class\"));\n }\n }\n }\n };\n\n function isPrivateNameConflicted(privateNameMap, element) {\n var name = element.key.name;\n var curr = privateNameMap[name];\n\n var next = \"true\";\n if (element.type === \"MethodDefinition\" && (element.kind === \"get\" || element.kind === \"set\")) {\n next = (element.static ? \"s\" : \"i\") + element.kind;\n }\n\n // `class { get #a(){}; static set #a(_){} }` is also conflict.\n if (\n curr === \"iget\" && next === \"iset\" ||\n curr === \"iset\" && next === \"iget\" ||\n curr === \"sget\" && next === \"sset\" ||\n curr === \"sset\" && next === \"sget\"\n ) {\n privateNameMap[name] = \"true\";\n return false\n } else if (!curr) {\n privateNameMap[name] = next;\n return false\n } else {\n return true\n }\n }\n\n function checkKeyName(node, name) {\n var computed = node.computed;\n var key = node.key;\n return !computed && (\n key.type === \"Identifier\" && key.name === name ||\n key.type === \"Literal\" && key.value === name\n )\n }\n\n // Parses module export declaration.\n\n pp$8.parseExportAllDeclaration = function(node, exports) {\n if (this.options.ecmaVersion >= 11) {\n if (this.eatContextual(\"as\")) {\n node.exported = this.parseModuleExportName();\n this.checkExport(exports, node.exported, this.lastTokStart);\n } else {\n node.exported = null;\n }\n }\n this.expectContextual(\"from\");\n if (this.type !== types$1.string) { this.unexpected(); }\n node.source = this.parseExprAtom();\n if (this.options.ecmaVersion >= 16)\n { node.attributes = this.parseWithClause(); }\n this.semicolon();\n return this.finishNode(node, \"ExportAllDeclaration\")\n };\n\n pp$8.parseExport = function(node, exports) {\n this.next();\n // export * from '...'\n if (this.eat(types$1.star)) {\n return this.parseExportAllDeclaration(node, exports)\n }\n if (this.eat(types$1._default)) { // export default ...\n this.checkExport(exports, \"default\", this.lastTokStart);\n node.declaration = this.parseExportDefaultDeclaration();\n return this.finishNode(node, \"ExportDefaultDeclaration\")\n }\n // export var|const|let|function|class ...\n if (this.shouldParseExportStatement()) {\n node.declaration = this.parseExportDeclaration(node);\n if (node.declaration.type === \"VariableDeclaration\")\n { this.checkVariableExport(exports, node.declaration.declarations); }\n else\n { this.checkExport(exports, node.declaration.id, node.declaration.id.start); }\n node.specifiers = [];\n node.source = null;\n if (this.options.ecmaVersion >= 16)\n { node.attributes = []; }\n } else { // export { x, y as z } [from '...']\n node.declaration = null;\n node.specifiers = this.parseExportSpecifiers(exports);\n if (this.eatContextual(\"from\")) {\n if (this.type !== types$1.string) { this.unexpected(); }\n node.source = this.parseExprAtom();\n if (this.options.ecmaVersion >= 16)\n { node.attributes = this.parseWithClause(); }\n } else {\n for (var i = 0, list = node.specifiers; i < list.length; i += 1) {\n // check for keywords used as local names\n var spec = list[i];\n\n this.checkUnreserved(spec.local);\n // check if export is defined\n this.checkLocalExport(spec.local);\n\n if (spec.local.type === \"Literal\") {\n this.raise(spec.local.start, \"A string literal cannot be used as an exported binding without `from`.\");\n }\n }\n\n node.source = null;\n if (this.options.ecmaVersion >= 16)\n { node.attributes = []; }\n }\n this.semicolon();\n }\n return this.finishNode(node, \"ExportNamedDeclaration\")\n };\n\n pp$8.parseExportDeclaration = function(node) {\n return this.parseStatement(null)\n };\n\n pp$8.parseExportDefaultDeclaration = function() {\n var isAsync;\n if (this.type === types$1._function || (isAsync = this.isAsyncFunction())) {\n var fNode = this.startNode();\n this.next();\n if (isAsync) { this.next(); }\n return this.parseFunction(fNode, FUNC_STATEMENT | FUNC_NULLABLE_ID, false, isAsync)\n } else if (this.type === types$1._class) {\n var cNode = this.startNode();\n return this.parseClass(cNode, \"nullableID\")\n } else {\n var declaration = this.parseMaybeAssign();\n this.semicolon();\n return declaration\n }\n };\n\n pp$8.checkExport = function(exports, name, pos) {\n if (!exports) { return }\n if (typeof name !== \"string\")\n { name = name.type === \"Identifier\" ? name.name : name.value; }\n if (hasOwn(exports, name))\n { this.raiseRecoverable(pos, \"Duplicate export '\" + name + \"'\"); }\n exports[name] = true;\n };\n\n pp$8.checkPatternExport = function(exports, pat) {\n var type = pat.type;\n if (type === \"Identifier\")\n { this.checkExport(exports, pat, pat.start); }\n else if (type === \"ObjectPattern\")\n { for (var i = 0, list = pat.properties; i < list.length; i += 1)\n {\n var prop = list[i];\n\n this.checkPatternExport(exports, prop);\n } }\n else if (type === \"ArrayPattern\")\n { for (var i$1 = 0, list$1 = pat.elements; i$1 < list$1.length; i$1 += 1) {\n var elt = list$1[i$1];\n\n if (elt) { this.checkPatternExport(exports, elt); }\n } }\n else if (type === \"Property\")\n { this.checkPatternExport(exports, pat.value); }\n else if (type === \"AssignmentPattern\")\n { this.checkPatternExport(exports, pat.left); }\n else if (type === \"RestElement\")\n { this.checkPatternExport(exports, pat.argument); }\n };\n\n pp$8.checkVariableExport = function(exports, decls) {\n if (!exports) { return }\n for (var i = 0, list = decls; i < list.length; i += 1)\n {\n var decl = list[i];\n\n this.checkPatternExport(exports, decl.id);\n }\n };\n\n pp$8.shouldParseExportStatement = function() {\n return this.type.keyword === \"var\" ||\n this.type.keyword === \"const\" ||\n this.type.keyword === \"class\" ||\n this.type.keyword === \"function\" ||\n this.isLet() ||\n this.isAsyncFunction()\n };\n\n // Parses a comma-separated list of module exports.\n\n pp$8.parseExportSpecifier = function(exports) {\n var node = this.startNode();\n node.local = this.parseModuleExportName();\n\n node.exported = this.eatContextual(\"as\") ? this.parseModuleExportName() : node.local;\n this.checkExport(\n exports,\n node.exported,\n node.exported.start\n );\n\n return this.finishNode(node, \"ExportSpecifier\")\n };\n\n pp$8.parseExportSpecifiers = function(exports) {\n var nodes = [], first = true;\n // export { x, y as z } [from '...']\n this.expect(types$1.braceL);\n while (!this.eat(types$1.braceR)) {\n if (!first) {\n this.expect(types$1.comma);\n if (this.afterTrailingComma(types$1.braceR)) { break }\n } else { first = false; }\n\n nodes.push(this.parseExportSpecifier(exports));\n }\n return nodes\n };\n\n // Parses import declaration.\n\n pp$8.parseImport = function(node) {\n this.next();\n\n // import '...'\n if (this.type === types$1.string) {\n node.specifiers = empty$1;\n node.source = this.parseExprAtom();\n } else {\n node.specifiers = this.parseImportSpecifiers();\n this.expectContextual(\"from\");\n node.source = this.type === types$1.string ? this.parseExprAtom() : this.unexpected();\n }\n if (this.options.ecmaVersion >= 16)\n { node.attributes = this.parseWithClause(); }\n this.semicolon();\n return this.finishNode(node, \"ImportDeclaration\")\n };\n\n // Parses a comma-separated list of module imports.\n\n pp$8.parseImportSpecifier = function() {\n var node = this.startNode();\n node.imported = this.parseModuleExportName();\n\n if (this.eatContextual(\"as\")) {\n node.local = this.parseIdent();\n } else {\n this.checkUnreserved(node.imported);\n node.local = node.imported;\n }\n this.checkLValSimple(node.local, BIND_LEXICAL);\n\n return this.finishNode(node, \"ImportSpecifier\")\n };\n\n pp$8.parseImportDefaultSpecifier = function() {\n // import defaultObj, { x, y as z } from '...'\n var node = this.startNode();\n node.local = this.parseIdent();\n this.checkLValSimple(node.local, BIND_LEXICAL);\n return this.finishNode(node, \"ImportDefaultSpecifier\")\n };\n\n pp$8.parseImportNamespaceSpecifier = function() {\n var node = this.startNode();\n this.next();\n this.expectContextual(\"as\");\n node.local = this.parseIdent();\n this.checkLValSimple(node.local, BIND_LEXICAL);\n return this.finishNode(node, \"ImportNamespaceSpecifier\")\n };\n\n pp$8.parseImportSpecifiers = function() {\n var nodes = [], first = true;\n if (this.type === types$1.name) {\n nodes.push(this.parseImportDefaultSpecifier());\n if (!this.eat(types$1.comma)) { return nodes }\n }\n if (this.type === types$1.star) {\n nodes.push(this.parseImportNamespaceSpecifier());\n return nodes\n }\n this.expect(types$1.braceL);\n while (!this.eat(types$1.braceR)) {\n if (!first) {\n this.expect(types$1.comma);\n if (this.afterTrailingComma(types$1.braceR)) { break }\n } else { first = false; }\n\n nodes.push(this.parseImportSpecifier());\n }\n return nodes\n };\n\n pp$8.parseWithClause = function() {\n var nodes = [];\n if (!this.eat(types$1._with)) {\n return nodes\n }\n this.expect(types$1.braceL);\n var attributeKeys = {};\n var first = true;\n while (!this.eat(types$1.braceR)) {\n if (!first) {\n this.expect(types$1.comma);\n if (this.afterTrailingComma(types$1.braceR)) { break }\n } else { first = false; }\n\n var attr = this.parseImportAttribute();\n var keyName = attr.key.type === \"Identifier\" ? attr.key.name : attr.key.value;\n if (hasOwn(attributeKeys, keyName))\n { this.raiseRecoverable(attr.key.start, \"Duplicate attribute key '\" + keyName + \"'\"); }\n attributeKeys[keyName] = true;\n nodes.push(attr);\n }\n return nodes\n };\n\n pp$8.parseImportAttribute = function() {\n var node = this.startNode();\n node.key = this.type === types$1.string ? this.parseExprAtom() : this.parseIdent(this.options.allowReserved !== \"never\");\n this.expect(types$1.colon);\n if (this.type !== types$1.string) {\n this.unexpected();\n }\n node.value = this.parseExprAtom();\n return this.finishNode(node, \"ImportAttribute\")\n };\n\n pp$8.parseModuleExportName = function() {\n if (this.options.ecmaVersion >= 13 && this.type === types$1.string) {\n var stringLiteral = this.parseLiteral(this.value);\n if (loneSurrogate.test(stringLiteral.value)) {\n this.raise(stringLiteral.start, \"An export name cannot include a lone surrogate.\");\n }\n return stringLiteral\n }\n return this.parseIdent(true)\n };\n\n // Set `ExpressionStatement#directive` property for directive prologues.\n pp$8.adaptDirectivePrologue = function(statements) {\n for (var i = 0; i < statements.length && this.isDirectiveCandidate(statements[i]); ++i) {\n statements[i].directive = statements[i].expression.raw.slice(1, -1);\n }\n };\n pp$8.isDirectiveCandidate = function(statement) {\n return (\n this.options.ecmaVersion >= 5 &&\n statement.type === \"ExpressionStatement\" &&\n statement.expression.type === \"Literal\" &&\n typeof statement.expression.value === \"string\" &&\n // Reject parenthesized strings.\n (this.input[statement.start] === \"\\\"\" || this.input[statement.start] === \"'\")\n )\n };\n\n var pp$7 = Parser.prototype;\n\n // Convert existing expression atom to assignable pattern\n // if possible.\n\n pp$7.toAssignable = function(node, isBinding, refDestructuringErrors) {\n if (this.options.ecmaVersion >= 6 && node) {\n switch (node.type) {\n case \"Identifier\":\n if (this.inAsync && node.name === \"await\")\n { this.raise(node.start, \"Cannot use 'await' as identifier inside an async function\"); }\n break\n\n case \"ObjectPattern\":\n case \"ArrayPattern\":\n case \"AssignmentPattern\":\n case \"RestElement\":\n break\n\n case \"ObjectExpression\":\n node.type = \"ObjectPattern\";\n if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }\n for (var i = 0, list = node.properties; i < list.length; i += 1) {\n var prop = list[i];\n\n this.toAssignable(prop, isBinding);\n // Early error:\n // AssignmentRestProperty[Yield, Await] :\n // `...` DestructuringAssignmentTarget[Yield, Await]\n //\n // It is a Syntax Error if |DestructuringAssignmentTarget| is an |ArrayLiteral| or an |ObjectLiteral|.\n if (\n prop.type === \"RestElement\" &&\n (prop.argument.type === \"ArrayPattern\" || prop.argument.type === \"ObjectPattern\")\n ) {\n this.raise(prop.argument.start, \"Unexpected token\");\n }\n }\n break\n\n case \"Property\":\n // AssignmentProperty has type === \"Property\"\n if (node.kind !== \"init\") { this.raise(node.key.start, \"Object pattern can't contain getter or setter\"); }\n this.toAssignable(node.value, isBinding);\n break\n\n case \"ArrayExpression\":\n node.type = \"ArrayPattern\";\n if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }\n this.toAssignableList(node.elements, isBinding);\n break\n\n case \"SpreadElement\":\n node.type = \"RestElement\";\n this.toAssignable(node.argument, isBinding);\n if (node.argument.type === \"AssignmentPattern\")\n { this.raise(node.argument.start, \"Rest elements cannot have a default value\"); }\n break\n\n case \"AssignmentExpression\":\n if (node.operator !== \"=\") { this.raise(node.left.end, \"Only '=' operator can be used for specifying default value.\"); }\n node.type = \"AssignmentPattern\";\n delete node.operator;\n this.toAssignable(node.left, isBinding);\n break\n\n case \"ParenthesizedExpression\":\n this.toAssignable(node.expression, isBinding, refDestructuringErrors);\n break\n\n case \"ChainExpression\":\n this.raiseRecoverable(node.start, \"Optional chaining cannot appear in left-hand side\");\n break\n\n case \"MemberExpression\":\n if (!isBinding) { break }\n\n default:\n this.raise(node.start, \"Assigning to rvalue\");\n }\n } else if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }\n return node\n };\n\n // Convert list of expression atoms to binding list.\n\n pp$7.toAssignableList = function(exprList, isBinding) {\n var end = exprList.length;\n for (var i = 0; i < end; i++) {\n var elt = exprList[i];\n if (elt) { this.toAssignable(elt, isBinding); }\n }\n if (end) {\n var last = exprList[end - 1];\n if (this.options.ecmaVersion === 6 && isBinding && last && last.type === \"RestElement\" && last.argument.type !== \"Identifier\")\n { this.unexpected(last.argument.start); }\n }\n return exprList\n };\n\n // Parses spread element.\n\n pp$7.parseSpread = function(refDestructuringErrors) {\n var node = this.startNode();\n this.next();\n node.argument = this.parseMaybeAssign(false, refDestructuringErrors);\n return this.finishNode(node, \"SpreadElement\")\n };\n\n pp$7.parseRestBinding = function() {\n var node = this.startNode();\n this.next();\n\n // RestElement inside of a function parameter must be an identifier\n if (this.options.ecmaVersion === 6 && this.type !== types$1.name)\n { this.unexpected(); }\n\n node.argument = this.parseBindingAtom();\n\n return this.finishNode(node, \"RestElement\")\n };\n\n // Parses lvalue (assignable) atom.\n\n pp$7.parseBindingAtom = function() {\n if (this.options.ecmaVersion >= 6) {\n switch (this.type) {\n case types$1.bracketL:\n var node = this.startNode();\n this.next();\n node.elements = this.parseBindingList(types$1.bracketR, true, true);\n return this.finishNode(node, \"ArrayPattern\")\n\n case types$1.braceL:\n return this.parseObj(true)\n }\n }\n return this.parseIdent()\n };\n\n pp$7.parseBindingList = function(close, allowEmpty, allowTrailingComma, allowModifiers) {\n var elts = [], first = true;\n while (!this.eat(close)) {\n if (first) { first = false; }\n else { this.expect(types$1.comma); }\n if (allowEmpty && this.type === types$1.comma) {\n elts.push(null);\n } else if (allowTrailingComma && this.afterTrailingComma(close)) {\n break\n } else if (this.type === types$1.ellipsis) {\n var rest = this.parseRestBinding();\n this.parseBindingListItem(rest);\n elts.push(rest);\n if (this.type === types$1.comma) { this.raiseRecoverable(this.start, \"Comma is not permitted after the rest element\"); }\n this.expect(close);\n break\n } else {\n elts.push(this.parseAssignableListItem(allowModifiers));\n }\n }\n return elts\n };\n\n pp$7.parseAssignableListItem = function(allowModifiers) {\n var elem = this.parseMaybeDefault(this.start, this.startLoc);\n this.parseBindingListItem(elem);\n return elem\n };\n\n pp$7.parseBindingListItem = function(param) {\n return param\n };\n\n // Parses assignment pattern around given atom if possible.\n\n pp$7.parseMaybeDefault = function(startPos, startLoc, left) {\n left = left || this.parseBindingAtom();\n if (this.options.ecmaVersion < 6 || !this.eat(types$1.eq)) { return left }\n var node = this.startNodeAt(startPos, startLoc);\n node.left = left;\n node.right = this.parseMaybeAssign();\n return this.finishNode(node, \"AssignmentPattern\")\n };\n\n // The following three functions all verify that a node is an lvalue \u2014\n // something that can be bound, or assigned to. In order to do so, they perform\n // a variety of checks:\n //\n // - Check that none of the bound/assigned-to identifiers are reserved words.\n // - Record name declarations for bindings in the appropriate scope.\n // - Check duplicate argument names, if checkClashes is set.\n //\n // If a complex binding pattern is encountered (e.g., object and array\n // destructuring), the entire pattern is recursively checked.\n //\n // There are three versions of checkLVal*() appropriate for different\n // circumstances:\n //\n // - checkLValSimple() shall be used if the syntactic construct supports\n // nothing other than identifiers and member expressions. Parenthesized\n // expressions are also correctly handled. This is generally appropriate for\n // constructs for which the spec says\n //\n // > It is a Syntax Error if AssignmentTargetType of [the production] is not\n // > simple.\n //\n // It is also appropriate for checking if an identifier is valid and not\n // defined elsewhere, like import declarations or function/class identifiers.\n //\n // Examples where this is used include:\n // a += \u2026;\n // import a from '\u2026';\n // where a is the node to be checked.\n //\n // - checkLValPattern() shall be used if the syntactic construct supports\n // anything checkLValSimple() supports, as well as object and array\n // destructuring patterns. This is generally appropriate for constructs for\n // which the spec says\n //\n // > It is a Syntax Error if [the production] is neither an ObjectLiteral nor\n // > an ArrayLiteral and AssignmentTargetType of [the production] is not\n // > simple.\n //\n // Examples where this is used include:\n // (a = \u2026);\n // const a = \u2026;\n // try { \u2026 } catch (a) { \u2026 }\n // where a is the node to be checked.\n //\n // - checkLValInnerPattern() shall be used if the syntactic construct supports\n // anything checkLValPattern() supports, as well as default assignment\n // patterns, rest elements, and other constructs that may appear within an\n // object or array destructuring pattern.\n //\n // As a special case, function parameters also use checkLValInnerPattern(),\n // as they also support defaults and rest constructs.\n //\n // These functions deliberately support both assignment and binding constructs,\n // as the logic for both is exceedingly similar. If the node is the target of\n // an assignment, then bindingType should be set to BIND_NONE. Otherwise, it\n // should be set to the appropriate BIND_* constant, like BIND_VAR or\n // BIND_LEXICAL.\n //\n // If the function is called with a non-BIND_NONE bindingType, then\n // additionally a checkClashes object may be specified to allow checking for\n // duplicate argument names. checkClashes is ignored if the provided construct\n // is an assignment (i.e., bindingType is BIND_NONE).\n\n pp$7.checkLValSimple = function(expr, bindingType, checkClashes) {\n if ( bindingType === void 0 ) bindingType = BIND_NONE;\n\n var isBind = bindingType !== BIND_NONE;\n\n switch (expr.type) {\n case \"Identifier\":\n if (this.strict && this.reservedWordsStrictBind.test(expr.name))\n { this.raiseRecoverable(expr.start, (isBind ? \"Binding \" : \"Assigning to \") + expr.name + \" in strict mode\"); }\n if (isBind) {\n if (bindingType === BIND_LEXICAL && expr.name === \"let\")\n { this.raiseRecoverable(expr.start, \"let is disallowed as a lexically bound name\"); }\n if (checkClashes) {\n if (hasOwn(checkClashes, expr.name))\n { this.raiseRecoverable(expr.start, \"Argument name clash\"); }\n checkClashes[expr.name] = true;\n }\n if (bindingType !== BIND_OUTSIDE) { this.declareName(expr.name, bindingType, expr.start); }\n }\n break\n\n case \"ChainExpression\":\n this.raiseRecoverable(expr.start, \"Optional chaining cannot appear in left-hand side\");\n break\n\n case \"MemberExpression\":\n if (isBind) { this.raiseRecoverable(expr.start, \"Binding member expression\"); }\n break\n\n case \"ParenthesizedExpression\":\n if (isBind) { this.raiseRecoverable(expr.start, \"Binding parenthesized expression\"); }\n return this.checkLValSimple(expr.expression, bindingType, checkClashes)\n\n default:\n this.raise(expr.start, (isBind ? \"Binding\" : \"Assigning to\") + \" rvalue\");\n }\n };\n\n pp$7.checkLValPattern = function(expr, bindingType, checkClashes) {\n if ( bindingType === void 0 ) bindingType = BIND_NONE;\n\n switch (expr.type) {\n case \"ObjectPattern\":\n for (var i = 0, list = expr.properties; i < list.length; i += 1) {\n var prop = list[i];\n\n this.checkLValInnerPattern(prop, bindingType, checkClashes);\n }\n break\n\n case \"ArrayPattern\":\n for (var i$1 = 0, list$1 = expr.elements; i$1 < list$1.length; i$1 += 1) {\n var elem = list$1[i$1];\n\n if (elem) { this.checkLValInnerPattern(elem, bindingType, checkClashes); }\n }\n break\n\n default:\n this.checkLValSimple(expr, bindingType, checkClashes);\n }\n };\n\n pp$7.checkLValInnerPattern = function(expr, bindingType, checkClashes) {\n if ( bindingType === void 0 ) bindingType = BIND_NONE;\n\n switch (expr.type) {\n case \"Property\":\n // AssignmentProperty has type === \"Property\"\n this.checkLValInnerPattern(expr.value, bindingType, checkClashes);\n break\n\n case \"AssignmentPattern\":\n this.checkLValPattern(expr.left, bindingType, checkClashes);\n break\n\n case \"RestElement\":\n this.checkLValPattern(expr.argument, bindingType, checkClashes);\n break\n\n default:\n this.checkLValPattern(expr, bindingType, checkClashes);\n }\n };\n\n // The algorithm used to determine whether a regexp can appear at a\n // given point in the program is loosely based on sweet.js' approach.\n // See https://github.com/mozilla/sweet.js/wiki/design\n\n\n var TokContext = function TokContext(token, isExpr, preserveSpace, override, generator) {\n this.token = token;\n this.isExpr = !!isExpr;\n this.preserveSpace = !!preserveSpace;\n this.override = override;\n this.generator = !!generator;\n };\n\n var types = {\n b_stat: new TokContext(\"{\", false),\n b_expr: new TokContext(\"{\", true),\n b_tmpl: new TokContext(\"${\", false),\n p_stat: new TokContext(\"(\", false),\n p_expr: new TokContext(\"(\", true),\n q_tmpl: new TokContext(\"`\", true, true, function (p) { return p.tryReadTemplateToken(); }),\n f_stat: new TokContext(\"function\", false),\n f_expr: new TokContext(\"function\", true),\n f_expr_gen: new TokContext(\"function\", true, false, null, true),\n f_gen: new TokContext(\"function\", false, false, null, true)\n };\n\n var pp$6 = Parser.prototype;\n\n pp$6.initialContext = function() {\n return [types.b_stat]\n };\n\n pp$6.curContext = function() {\n return this.context[this.context.length - 1]\n };\n\n pp$6.braceIsBlock = function(prevType) {\n var parent = this.curContext();\n if (parent === types.f_expr || parent === types.f_stat)\n { return true }\n if (prevType === types$1.colon && (parent === types.b_stat || parent === types.b_expr))\n { return !parent.isExpr }\n\n // The check for `tt.name && exprAllowed` detects whether we are\n // after a `yield` or `of` construct. See the `updateContext` for\n // `tt.name`.\n if (prevType === types$1._return || prevType === types$1.name && this.exprAllowed)\n { return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) }\n if (prevType === types$1._else || prevType === types$1.semi || prevType === types$1.eof || prevType === types$1.parenR || prevType === types$1.arrow)\n { return true }\n if (prevType === types$1.braceL)\n { return parent === types.b_stat }\n if (prevType === types$1._var || prevType === types$1._const || prevType === types$1.name)\n { return false }\n return !this.exprAllowed\n };\n\n pp$6.inGeneratorContext = function() {\n for (var i = this.context.length - 1; i >= 1; i--) {\n var context = this.context[i];\n if (context.token === \"function\")\n { return context.generator }\n }\n return false\n };\n\n pp$6.updateContext = function(prevType) {\n var update, type = this.type;\n if (type.keyword && prevType === types$1.dot)\n { this.exprAllowed = false; }\n else if (update = type.updateContext)\n { update.call(this, prevType); }\n else\n { this.exprAllowed = type.beforeExpr; }\n };\n\n // Used to handle edge cases when token context could not be inferred correctly during tokenization phase\n\n pp$6.overrideContext = function(tokenCtx) {\n if (this.curContext() !== tokenCtx) {\n this.context[this.context.length - 1] = tokenCtx;\n }\n };\n\n // Token-specific context update code\n\n types$1.parenR.updateContext = types$1.braceR.updateContext = function() {\n if (this.context.length === 1) {\n this.exprAllowed = true;\n return\n }\n var out = this.context.pop();\n if (out === types.b_stat && this.curContext().token === \"function\") {\n out = this.context.pop();\n }\n this.exprAllowed = !out.isExpr;\n };\n\n types$1.braceL.updateContext = function(prevType) {\n this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr);\n this.exprAllowed = true;\n };\n\n types$1.dollarBraceL.updateContext = function() {\n this.context.push(types.b_tmpl);\n this.exprAllowed = true;\n };\n\n types$1.parenL.updateContext = function(prevType) {\n var statementParens = prevType === types$1._if || prevType === types$1._for || prevType === types$1._with || prevType === types$1._while;\n this.context.push(statementParens ? types.p_stat : types.p_expr);\n this.exprAllowed = true;\n };\n\n types$1.incDec.updateContext = function() {\n // tokExprAllowed stays unchanged\n };\n\n types$1._function.updateContext = types$1._class.updateContext = function(prevType) {\n if (prevType.beforeExpr && prevType !== types$1._else &&\n !(prevType === types$1.semi && this.curContext() !== types.p_stat) &&\n !(prevType === types$1._return && lineBreak.test(this.input.slice(this.lastTokEnd, this.start))) &&\n !((prevType === types$1.colon || prevType === types$1.braceL) && this.curContext() === types.b_stat))\n { this.context.push(types.f_expr); }\n else\n { this.context.push(types.f_stat); }\n this.exprAllowed = false;\n };\n\n types$1.colon.updateContext = function() {\n if (this.curContext().token === \"function\") { this.context.pop(); }\n this.exprAllowed = true;\n };\n\n types$1.backQuote.updateContext = function() {\n if (this.curContext() === types.q_tmpl)\n { this.context.pop(); }\n else\n { this.context.push(types.q_tmpl); }\n this.exprAllowed = false;\n };\n\n types$1.star.updateContext = function(prevType) {\n if (prevType === types$1._function) {\n var index = this.context.length - 1;\n if (this.context[index] === types.f_expr)\n { this.context[index] = types.f_expr_gen; }\n else\n { this.context[index] = types.f_gen; }\n }\n this.exprAllowed = true;\n };\n\n types$1.name.updateContext = function(prevType) {\n var allowed = false;\n if (this.options.ecmaVersion >= 6 && prevType !== types$1.dot) {\n if (this.value === \"of\" && !this.exprAllowed ||\n this.value === \"yield\" && this.inGeneratorContext())\n { allowed = true; }\n }\n this.exprAllowed = allowed;\n };\n\n // A recursive descent parser operates by defining functions for all\n // syntactic elements, and recursively calling those, each function\n // advancing the input stream and returning an AST node. Precedence\n // of constructs (for example, the fact that `!x[1]` means `!(x[1])`\n // instead of `(!x)[1]` is handled by the fact that the parser\n // function that parses unary prefix operators is called first, and\n // in turn calls the function that parses `[]` subscripts \u2014 that\n // way, it'll receive the node for `x[1]` already parsed, and wraps\n // *that* in the unary operator node.\n //\n // Acorn uses an [operator precedence parser][opp] to handle binary\n // operator precedence, because it is much more compact than using\n // the technique outlined above, which uses different, nesting\n // functions to specify precedence, for all of the ten binary\n // precedence levels that JavaScript defines.\n //\n // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser\n\n\n var pp$5 = Parser.prototype;\n\n // Check if property name clashes with already added.\n // Object/class getters and setters are not allowed to clash \u2014\n // either with each other or with an init property \u2014 and in\n // strict mode, init properties are also not allowed to be repeated.\n\n pp$5.checkPropClash = function(prop, propHash, refDestructuringErrors) {\n if (this.options.ecmaVersion >= 9 && prop.type === \"SpreadElement\")\n { return }\n if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand))\n { return }\n var key = prop.key;\n var name;\n switch (key.type) {\n case \"Identifier\": name = key.name; break\n case \"Literal\": name = String(key.value); break\n default: return\n }\n var kind = prop.kind;\n if (this.options.ecmaVersion >= 6) {\n if (name === \"__proto__\" && kind === \"init\") {\n if (propHash.proto) {\n if (refDestructuringErrors) {\n if (refDestructuringErrors.doubleProto < 0) {\n refDestructuringErrors.doubleProto = key.start;\n }\n } else {\n this.raiseRecoverable(key.start, \"Redefinition of __proto__ property\");\n }\n }\n propHash.proto = true;\n }\n return\n }\n name = \"$\" + name;\n var other = propHash[name];\n if (other) {\n var redefinition;\n if (kind === \"init\") {\n redefinition = this.strict && other.init || other.get || other.set;\n } else {\n redefinition = other.init || other[kind];\n }\n if (redefinition)\n { this.raiseRecoverable(key.start, \"Redefinition of property\"); }\n } else {\n other = propHash[name] = {\n init: false,\n get: false,\n set: false\n };\n }\n other[kind] = true;\n };\n\n // ### Expression parsing\n\n // These nest, from the most general expression type at the top to\n // 'atomic', nondivisible expression types at the bottom. Most of\n // the functions will simply let the function(s) below them parse,\n // and, *if* the syntactic construct they handle is present, wrap\n // the AST node that the inner parser gave them in another node.\n\n // Parse a full expression. The optional arguments are used to\n // forbid the `in` operator (in for loops initalization expressions)\n // and provide reference for storing '=' operator inside shorthand\n // property assignment in contexts where both object expression\n // and object pattern might appear (so it's possible to raise\n // delayed syntax error at correct position).\n\n pp$5.parseExpression = function(forInit, refDestructuringErrors) {\n var startPos = this.start, startLoc = this.startLoc;\n var expr = this.parseMaybeAssign(forInit, refDestructuringErrors);\n if (this.type === types$1.comma) {\n var node = this.startNodeAt(startPos, startLoc);\n node.expressions = [expr];\n while (this.eat(types$1.comma)) { node.expressions.push(this.parseMaybeAssign(forInit, refDestructuringErrors)); }\n return this.finishNode(node, \"SequenceExpression\")\n }\n return expr\n };\n\n // Parse an assignment expression. This includes applications of\n // operators like `+=`.\n\n pp$5.parseMaybeAssign = function(forInit, refDestructuringErrors, afterLeftParse) {\n if (this.isContextual(\"yield\")) {\n if (this.inGenerator) { return this.parseYield(forInit) }\n // The tokenizer will assume an expression is allowed after\n // `yield`, but this isn't that kind of yield\n else { this.exprAllowed = false; }\n }\n\n var ownDestructuringErrors = false, oldParenAssign = -1, oldTrailingComma = -1, oldDoubleProto = -1;\n if (refDestructuringErrors) {\n oldParenAssign = refDestructuringErrors.parenthesizedAssign;\n oldTrailingComma = refDestructuringErrors.trailingComma;\n oldDoubleProto = refDestructuringErrors.doubleProto;\n refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = -1;\n } else {\n refDestructuringErrors = new DestructuringErrors;\n ownDestructuringErrors = true;\n }\n\n var startPos = this.start, startLoc = this.startLoc;\n if (this.type === types$1.parenL || this.type === types$1.name) {\n this.potentialArrowAt = this.start;\n this.potentialArrowInForAwait = forInit === \"await\";\n }\n var left = this.parseMaybeConditional(forInit, refDestructuringErrors);\n if (afterLeftParse) { left = afterLeftParse.call(this, left, startPos, startLoc); }\n if (this.type.isAssign) {\n var node = this.startNodeAt(startPos, startLoc);\n node.operator = this.value;\n if (this.type === types$1.eq)\n { left = this.toAssignable(left, false, refDestructuringErrors); }\n if (!ownDestructuringErrors) {\n refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = refDestructuringErrors.doubleProto = -1;\n }\n if (refDestructuringErrors.shorthandAssign >= left.start)\n { refDestructuringErrors.shorthandAssign = -1; } // reset because shorthand default was used correctly\n if (this.type === types$1.eq)\n { this.checkLValPattern(left); }\n else\n { this.checkLValSimple(left); }\n node.left = left;\n this.next();\n node.right = this.parseMaybeAssign(forInit);\n if (oldDoubleProto > -1) { refDestructuringErrors.doubleProto = oldDoubleProto; }\n return this.finishNode(node, \"AssignmentExpression\")\n } else {\n if (ownDestructuringErrors) { this.checkExpressionErrors(refDestructuringErrors, true); }\n }\n if (oldParenAssign > -1) { refDestructuringErrors.parenthesizedAssign = oldParenAssign; }\n if (oldTrailingComma > -1) { refDestructuringErrors.trailingComma = oldTrailingComma; }\n return left\n };\n\n // Parse a ternary conditional (`?:`) operator.\n\n pp$5.parseMaybeConditional = function(forInit, refDestructuringErrors) {\n var startPos = this.start, startLoc = this.startLoc;\n var expr = this.parseExprOps(forInit, refDestructuringErrors);\n if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }\n if (this.eat(types$1.question)) {\n var node = this.startNodeAt(startPos, startLoc);\n node.test = expr;\n node.consequent = this.parseMaybeAssign();\n this.expect(types$1.colon);\n node.alternate = this.parseMaybeAssign(forInit);\n return this.finishNode(node, \"ConditionalExpression\")\n }\n return expr\n };\n\n // Start the precedence parser.\n\n pp$5.parseExprOps = function(forInit, refDestructuringErrors) {\n var startPos = this.start, startLoc = this.startLoc;\n var expr = this.parseMaybeUnary(refDestructuringErrors, false, false, forInit);\n if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }\n return expr.start === startPos && expr.type === \"ArrowFunctionExpression\" ? expr : this.parseExprOp(expr, startPos, startLoc, -1, forInit)\n };\n\n // Parse binary operators with the operator precedence parsing\n // algorithm. `left` is the left-hand side of the operator.\n // `minPrec` provides context that allows the function to stop and\n // defer further parser to one of its callers when it encounters an\n // operator that has a lower precedence than the set it is parsing.\n\n pp$5.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, forInit) {\n var prec = this.type.binop;\n if (prec != null && (!forInit || this.type !== types$1._in)) {\n if (prec > minPrec) {\n var logical = this.type === types$1.logicalOR || this.type === types$1.logicalAND;\n var coalesce = this.type === types$1.coalesce;\n if (coalesce) {\n // Handle the precedence of `tt.coalesce` as equal to the range of logical expressions.\n // In other words, `node.right` shouldn't contain logical expressions in order to check the mixed error.\n prec = types$1.logicalAND.binop;\n }\n var op = this.value;\n this.next();\n var startPos = this.start, startLoc = this.startLoc;\n var right = this.parseExprOp(this.parseMaybeUnary(null, false, false, forInit), startPos, startLoc, prec, forInit);\n var node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical || coalesce);\n if ((logical && this.type === types$1.coalesce) || (coalesce && (this.type === types$1.logicalOR || this.type === types$1.logicalAND))) {\n this.raiseRecoverable(this.start, \"Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses\");\n }\n return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, forInit)\n }\n }\n return left\n };\n\n pp$5.buildBinary = function(startPos, startLoc, left, right, op, logical) {\n if (right.type === \"PrivateIdentifier\") { this.raise(right.start, \"Private identifier can only be left side of binary expression\"); }\n var node = this.startNodeAt(startPos, startLoc);\n node.left = left;\n node.operator = op;\n node.right = right;\n return this.finishNode(node, logical ? \"LogicalExpression\" : \"BinaryExpression\")\n };\n\n // Parse unary operators, both prefix and postfix.\n\n pp$5.parseMaybeUnary = function(refDestructuringErrors, sawUnary, incDec, forInit) {\n var startPos = this.start, startLoc = this.startLoc, expr;\n if (this.isContextual(\"await\") && this.canAwait) {\n expr = this.parseAwait(forInit);\n sawUnary = true;\n } else if (this.type.prefix) {\n var node = this.startNode(), update = this.type === types$1.incDec;\n node.operator = this.value;\n node.prefix = true;\n this.next();\n node.argument = this.parseMaybeUnary(null, true, update, forInit);\n this.checkExpressionErrors(refDestructuringErrors, true);\n if (update) { this.checkLValSimple(node.argument); }\n else if (this.strict && node.operator === \"delete\" && isLocalVariableAccess(node.argument))\n { this.raiseRecoverable(node.start, \"Deleting local variable in strict mode\"); }\n else if (node.operator === \"delete\" && isPrivateFieldAccess(node.argument))\n { this.raiseRecoverable(node.start, \"Private fields can not be deleted\"); }\n else { sawUnary = true; }\n expr = this.finishNode(node, update ? \"UpdateExpression\" : \"UnaryExpression\");\n } else if (!sawUnary && this.type === types$1.privateId) {\n if ((forInit || this.privateNameStack.length === 0) && this.options.checkPrivateFields) { this.unexpected(); }\n expr = this.parsePrivateIdent();\n // only could be private fields in 'in', such as #x in obj\n if (this.type !== types$1._in) { this.unexpected(); }\n } else {\n expr = this.parseExprSubscripts(refDestructuringErrors, forInit);\n if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }\n while (this.type.postfix && !this.canInsertSemicolon()) {\n var node$1 = this.startNodeAt(startPos, startLoc);\n node$1.operator = this.value;\n node$1.prefix = false;\n node$1.argument = expr;\n this.checkLValSimple(expr);\n this.next();\n expr = this.finishNode(node$1, \"UpdateExpression\");\n }\n }\n\n if (!incDec && this.eat(types$1.starstar)) {\n if (sawUnary)\n { this.unexpected(this.lastTokStart); }\n else\n { return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(null, false, false, forInit), \"**\", false) }\n } else {\n return expr\n }\n };\n\n function isLocalVariableAccess(node) {\n return (\n node.type === \"Identifier\" ||\n node.type === \"ParenthesizedExpression\" && isLocalVariableAccess(node.expression)\n )\n }\n\n function isPrivateFieldAccess(node) {\n return (\n node.type === \"MemberExpression\" && node.property.type === \"PrivateIdentifier\" ||\n node.type === \"ChainExpression\" && isPrivateFieldAccess(node.expression) ||\n node.type === \"ParenthesizedExpression\" && isPrivateFieldAccess(node.expression)\n )\n }\n\n // Parse call, dot, and `[]`-subscript expressions.\n\n pp$5.parseExprSubscripts = function(refDestructuringErrors, forInit) {\n var startPos = this.start, startLoc = this.startLoc;\n var expr = this.parseExprAtom(refDestructuringErrors, forInit);\n if (expr.type === \"ArrowFunctionExpression\" && this.input.slice(this.lastTokStart, this.lastTokEnd) !== \")\")\n { return expr }\n var result = this.parseSubscripts(expr, startPos, startLoc, false, forInit);\n if (refDestructuringErrors && result.type === \"MemberExpression\") {\n if (refDestructuringErrors.parenthesizedAssign >= result.start) { refDestructuringErrors.parenthesizedAssign = -1; }\n if (refDestructuringErrors.parenthesizedBind >= result.start) { refDestructuringErrors.parenthesizedBind = -1; }\n if (refDestructuringErrors.trailingComma >= result.start) { refDestructuringErrors.trailingComma = -1; }\n }\n return result\n };\n\n pp$5.parseSubscripts = function(base, startPos, startLoc, noCalls, forInit) {\n var maybeAsyncArrow = this.options.ecmaVersion >= 8 && base.type === \"Identifier\" && base.name === \"async\" &&\n this.lastTokEnd === base.end && !this.canInsertSemicolon() && base.end - base.start === 5 &&\n this.potentialArrowAt === base.start;\n var optionalChained = false;\n\n while (true) {\n var element = this.parseSubscript(base, startPos, startLoc, noCalls, maybeAsyncArrow, optionalChained, forInit);\n\n if (element.optional) { optionalChained = true; }\n if (element === base || element.type === \"ArrowFunctionExpression\") {\n if (optionalChained) {\n var chainNode = this.startNodeAt(startPos, startLoc);\n chainNode.expression = element;\n element = this.finishNode(chainNode, \"ChainExpression\");\n }\n return element\n }\n\n base = element;\n }\n };\n\n pp$5.shouldParseAsyncArrow = function() {\n return !this.canInsertSemicolon() && this.eat(types$1.arrow)\n };\n\n pp$5.parseSubscriptAsyncArrow = function(startPos, startLoc, exprList, forInit) {\n return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, true, forInit)\n };\n\n pp$5.parseSubscript = function(base, startPos, startLoc, noCalls, maybeAsyncArrow, optionalChained, forInit) {\n var optionalSupported = this.options.ecmaVersion >= 11;\n var optional = optionalSupported && this.eat(types$1.questionDot);\n if (noCalls && optional) { this.raise(this.lastTokStart, \"Optional chaining cannot appear in the callee of new expressions\"); }\n\n var computed = this.eat(types$1.bracketL);\n if (computed || (optional && this.type !== types$1.parenL && this.type !== types$1.backQuote) || this.eat(types$1.dot)) {\n var node = this.startNodeAt(startPos, startLoc);\n node.object = base;\n if (computed) {\n node.property = this.parseExpression();\n this.expect(types$1.bracketR);\n } else if (this.type === types$1.privateId && base.type !== \"Super\") {\n node.property = this.parsePrivateIdent();\n } else {\n node.property = this.parseIdent(this.options.allowReserved !== \"never\");\n }\n node.computed = !!computed;\n if (optionalSupported) {\n node.optional = optional;\n }\n base = this.finishNode(node, \"MemberExpression\");\n } else if (!noCalls && this.eat(types$1.parenL)) {\n var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n this.yieldPos = 0;\n this.awaitPos = 0;\n this.awaitIdentPos = 0;\n var exprList = this.parseExprList(types$1.parenR, this.options.ecmaVersion >= 8, false, refDestructuringErrors);\n if (maybeAsyncArrow && !optional && this.shouldParseAsyncArrow()) {\n this.checkPatternErrors(refDestructuringErrors, false);\n this.checkYieldAwaitInDefaultParams();\n if (this.awaitIdentPos > 0)\n { this.raise(this.awaitIdentPos, \"Cannot use 'await' as identifier inside an async function\"); }\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n this.awaitIdentPos = oldAwaitIdentPos;\n return this.parseSubscriptAsyncArrow(startPos, startLoc, exprList, forInit)\n }\n this.checkExpressionErrors(refDestructuringErrors, true);\n this.yieldPos = oldYieldPos || this.yieldPos;\n this.awaitPos = oldAwaitPos || this.awaitPos;\n this.awaitIdentPos = oldAwaitIdentPos || this.awaitIdentPos;\n var node$1 = this.startNodeAt(startPos, startLoc);\n node$1.callee = base;\n node$1.arguments = exprList;\n if (optionalSupported) {\n node$1.optional = optional;\n }\n base = this.finishNode(node$1, \"CallExpression\");\n } else if (this.type === types$1.backQuote) {\n if (optional || optionalChained) {\n this.raise(this.start, \"Optional chaining cannot appear in the tag of tagged template expressions\");\n }\n var node$2 = this.startNodeAt(startPos, startLoc);\n node$2.tag = base;\n node$2.quasi = this.parseTemplate({isTagged: true});\n base = this.finishNode(node$2, \"TaggedTemplateExpression\");\n }\n return base\n };\n\n // Parse an atomic expression \u2014 either a single token that is an\n // expression, an expression started by a keyword like `function` or\n // `new`, or an expression wrapped in punctuation like `()`, `[]`,\n // or `{}`.\n\n pp$5.parseExprAtom = function(refDestructuringErrors, forInit, forNew) {\n // If a division operator appears in an expression position, the\n // tokenizer got confused, and we force it to read a regexp instead.\n if (this.type === types$1.slash) { this.readRegexp(); }\n\n var node, canBeArrow = this.potentialArrowAt === this.start;\n switch (this.type) {\n case types$1._super:\n if (!this.allowSuper)\n { this.raise(this.start, \"'super' keyword outside a method\"); }\n node = this.startNode();\n this.next();\n if (this.type === types$1.parenL && !this.allowDirectSuper)\n { this.raise(node.start, \"super() call outside constructor of a subclass\"); }\n // The `super` keyword can appear at below:\n // SuperProperty:\n // super [ Expression ]\n // super . IdentifierName\n // SuperCall:\n // super ( Arguments )\n if (this.type !== types$1.dot && this.type !== types$1.bracketL && this.type !== types$1.parenL)\n { this.unexpected(); }\n return this.finishNode(node, \"Super\")\n\n case types$1._this:\n node = this.startNode();\n this.next();\n return this.finishNode(node, \"ThisExpression\")\n\n case types$1.name:\n var startPos = this.start, startLoc = this.startLoc, containsEsc = this.containsEsc;\n var id = this.parseIdent(false);\n if (this.options.ecmaVersion >= 8 && !containsEsc && id.name === \"async\" && !this.canInsertSemicolon() && this.eat(types$1._function)) {\n this.overrideContext(types.f_expr);\n return this.parseFunction(this.startNodeAt(startPos, startLoc), 0, false, true, forInit)\n }\n if (canBeArrow && !this.canInsertSemicolon()) {\n if (this.eat(types$1.arrow))\n { return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], false, forInit) }\n if (this.options.ecmaVersion >= 8 && id.name === \"async\" && this.type === types$1.name && !containsEsc &&\n (!this.potentialArrowInForAwait || this.value !== \"of\" || this.containsEsc)) {\n id = this.parseIdent(false);\n if (this.canInsertSemicolon() || !this.eat(types$1.arrow))\n { this.unexpected(); }\n return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], true, forInit)\n }\n }\n return id\n\n case types$1.regexp:\n var value = this.value;\n node = this.parseLiteral(value.value);\n node.regex = {pattern: value.pattern, flags: value.flags};\n return node\n\n case types$1.num: case types$1.string:\n return this.parseLiteral(this.value)\n\n case types$1._null: case types$1._true: case types$1._false:\n node = this.startNode();\n node.value = this.type === types$1._null ? null : this.type === types$1._true;\n node.raw = this.type.keyword;\n this.next();\n return this.finishNode(node, \"Literal\")\n\n case types$1.parenL:\n var start = this.start, expr = this.parseParenAndDistinguishExpression(canBeArrow, forInit);\n if (refDestructuringErrors) {\n if (refDestructuringErrors.parenthesizedAssign < 0 && !this.isSimpleAssignTarget(expr))\n { refDestructuringErrors.parenthesizedAssign = start; }\n if (refDestructuringErrors.parenthesizedBind < 0)\n { refDestructuringErrors.parenthesizedBind = start; }\n }\n return expr\n\n case types$1.bracketL:\n node = this.startNode();\n this.next();\n node.elements = this.parseExprList(types$1.bracketR, true, true, refDestructuringErrors);\n return this.finishNode(node, \"ArrayExpression\")\n\n case types$1.braceL:\n this.overrideContext(types.b_expr);\n return this.parseObj(false, refDestructuringErrors)\n\n case types$1._function:\n node = this.startNode();\n this.next();\n return this.parseFunction(node, 0)\n\n case types$1._class:\n return this.parseClass(this.startNode(), false)\n\n case types$1._new:\n return this.parseNew()\n\n case types$1.backQuote:\n return this.parseTemplate()\n\n case types$1._import:\n if (this.options.ecmaVersion >= 11) {\n return this.parseExprImport(forNew)\n } else {\n return this.unexpected()\n }\n\n default:\n return this.parseExprAtomDefault()\n }\n };\n\n pp$5.parseExprAtomDefault = function() {\n this.unexpected();\n };\n\n pp$5.parseExprImport = function(forNew) {\n var node = this.startNode();\n\n // Consume `import` as an identifier for `import.meta`.\n // Because `this.parseIdent(true)` doesn't check escape sequences, it needs the check of `this.containsEsc`.\n if (this.containsEsc) { this.raiseRecoverable(this.start, \"Escape sequence in keyword import\"); }\n this.next();\n\n if (this.type === types$1.parenL && !forNew) {\n return this.parseDynamicImport(node)\n } else if (this.type === types$1.dot) {\n var meta = this.startNodeAt(node.start, node.loc && node.loc.start);\n meta.name = \"import\";\n node.meta = this.finishNode(meta, \"Identifier\");\n return this.parseImportMeta(node)\n } else {\n this.unexpected();\n }\n };\n\n pp$5.parseDynamicImport = function(node) {\n this.next(); // skip `(`\n\n // Parse node.source.\n node.source = this.parseMaybeAssign();\n\n if (this.options.ecmaVersion >= 16) {\n if (!this.eat(types$1.parenR)) {\n this.expect(types$1.comma);\n if (!this.afterTrailingComma(types$1.parenR)) {\n node.options = this.parseMaybeAssign();\n if (!this.eat(types$1.parenR)) {\n this.expect(types$1.comma);\n if (!this.afterTrailingComma(types$1.parenR)) {\n this.unexpected();\n }\n }\n } else {\n node.options = null;\n }\n } else {\n node.options = null;\n }\n } else {\n // Verify ending.\n if (!this.eat(types$1.parenR)) {\n var errorPos = this.start;\n if (this.eat(types$1.comma) && this.eat(types$1.parenR)) {\n this.raiseRecoverable(errorPos, \"Trailing comma is not allowed in import()\");\n } else {\n this.unexpected(errorPos);\n }\n }\n }\n\n return this.finishNode(node, \"ImportExpression\")\n };\n\n pp$5.parseImportMeta = function(node) {\n this.next(); // skip `.`\n\n var containsEsc = this.containsEsc;\n node.property = this.parseIdent(true);\n\n if (node.property.name !== \"meta\")\n { this.raiseRecoverable(node.property.start, \"The only valid meta property for import is 'import.meta'\"); }\n if (containsEsc)\n { this.raiseRecoverable(node.start, \"'import.meta' must not contain escaped characters\"); }\n if (this.options.sourceType !== \"module\" && !this.options.allowImportExportEverywhere)\n { this.raiseRecoverable(node.start, \"Cannot use 'import.meta' outside a module\"); }\n\n return this.finishNode(node, \"MetaProperty\")\n };\n\n pp$5.parseLiteral = function(value) {\n var node = this.startNode();\n node.value = value;\n node.raw = this.input.slice(this.start, this.end);\n if (node.raw.charCodeAt(node.raw.length - 1) === 110)\n { node.bigint = node.value != null ? node.value.toString() : node.raw.slice(0, -1).replace(/_/g, \"\"); }\n this.next();\n return this.finishNode(node, \"Literal\")\n };\n\n pp$5.parseParenExpression = function() {\n this.expect(types$1.parenL);\n var val = this.parseExpression();\n this.expect(types$1.parenR);\n return val\n };\n\n pp$5.shouldParseArrow = function(exprList) {\n return !this.canInsertSemicolon()\n };\n\n pp$5.parseParenAndDistinguishExpression = function(canBeArrow, forInit) {\n var startPos = this.start, startLoc = this.startLoc, val, allowTrailingComma = this.options.ecmaVersion >= 8;\n if (this.options.ecmaVersion >= 6) {\n this.next();\n\n var innerStartPos = this.start, innerStartLoc = this.startLoc;\n var exprList = [], first = true, lastIsComma = false;\n var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, spreadStart;\n this.yieldPos = 0;\n this.awaitPos = 0;\n // Do not save awaitIdentPos to allow checking awaits nested in parameters\n while (this.type !== types$1.parenR) {\n first ? first = false : this.expect(types$1.comma);\n if (allowTrailingComma && this.afterTrailingComma(types$1.parenR, true)) {\n lastIsComma = true;\n break\n } else if (this.type === types$1.ellipsis) {\n spreadStart = this.start;\n exprList.push(this.parseParenItem(this.parseRestBinding()));\n if (this.type === types$1.comma) {\n this.raiseRecoverable(\n this.start,\n \"Comma is not permitted after the rest element\"\n );\n }\n break\n } else {\n exprList.push(this.parseMaybeAssign(false, refDestructuringErrors, this.parseParenItem));\n }\n }\n var innerEndPos = this.lastTokEnd, innerEndLoc = this.lastTokEndLoc;\n this.expect(types$1.parenR);\n\n if (canBeArrow && this.shouldParseArrow(exprList) && this.eat(types$1.arrow)) {\n this.checkPatternErrors(refDestructuringErrors, false);\n this.checkYieldAwaitInDefaultParams();\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n return this.parseParenArrowList(startPos, startLoc, exprList, forInit)\n }\n\n if (!exprList.length || lastIsComma) { this.unexpected(this.lastTokStart); }\n if (spreadStart) { this.unexpected(spreadStart); }\n this.checkExpressionErrors(refDestructuringErrors, true);\n this.yieldPos = oldYieldPos || this.yieldPos;\n this.awaitPos = oldAwaitPos || this.awaitPos;\n\n if (exprList.length > 1) {\n val = this.startNodeAt(innerStartPos, innerStartLoc);\n val.expressions = exprList;\n this.finishNodeAt(val, \"SequenceExpression\", innerEndPos, innerEndLoc);\n } else {\n val = exprList[0];\n }\n } else {\n val = this.parseParenExpression();\n }\n\n if (this.options.preserveParens) {\n var par = this.startNodeAt(startPos, startLoc);\n par.expression = val;\n return this.finishNode(par, \"ParenthesizedExpression\")\n } else {\n return val\n }\n };\n\n pp$5.parseParenItem = function(item) {\n return item\n };\n\n pp$5.parseParenArrowList = function(startPos, startLoc, exprList, forInit) {\n return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, false, forInit)\n };\n\n // New's precedence is slightly tricky. It must allow its argument to\n // be a `[]` or dot subscript expression, but not a call \u2014 at least,\n // not without wrapping it in parentheses. Thus, it uses the noCalls\n // argument to parseSubscripts to prevent it from consuming the\n // argument list.\n\n var empty = [];\n\n pp$5.parseNew = function() {\n if (this.containsEsc) { this.raiseRecoverable(this.start, \"Escape sequence in keyword new\"); }\n var node = this.startNode();\n this.next();\n if (this.options.ecmaVersion >= 6 && this.type === types$1.dot) {\n var meta = this.startNodeAt(node.start, node.loc && node.loc.start);\n meta.name = \"new\";\n node.meta = this.finishNode(meta, \"Identifier\");\n this.next();\n var containsEsc = this.containsEsc;\n node.property = this.parseIdent(true);\n if (node.property.name !== \"target\")\n { this.raiseRecoverable(node.property.start, \"The only valid meta property for new is 'new.target'\"); }\n if (containsEsc)\n { this.raiseRecoverable(node.start, \"'new.target' must not contain escaped characters\"); }\n if (!this.allowNewDotTarget)\n { this.raiseRecoverable(node.start, \"'new.target' can only be used in functions and class static block\"); }\n return this.finishNode(node, \"MetaProperty\")\n }\n var startPos = this.start, startLoc = this.startLoc;\n node.callee = this.parseSubscripts(this.parseExprAtom(null, false, true), startPos, startLoc, true, false);\n if (this.eat(types$1.parenL)) { node.arguments = this.parseExprList(types$1.parenR, this.options.ecmaVersion >= 8, false); }\n else { node.arguments = empty; }\n return this.finishNode(node, \"NewExpression\")\n };\n\n // Parse template expression.\n\n pp$5.parseTemplateElement = function(ref) {\n var isTagged = ref.isTagged;\n\n var elem = this.startNode();\n if (this.type === types$1.invalidTemplate) {\n if (!isTagged) {\n this.raiseRecoverable(this.start, \"Bad escape sequence in untagged template literal\");\n }\n elem.value = {\n raw: this.value.replace(/\\r\\n?/g, \"\\n\"),\n cooked: null\n };\n } else {\n elem.value = {\n raw: this.input.slice(this.start, this.end).replace(/\\r\\n?/g, \"\\n\"),\n cooked: this.value\n };\n }\n this.next();\n elem.tail = this.type === types$1.backQuote;\n return this.finishNode(elem, \"TemplateElement\")\n };\n\n pp$5.parseTemplate = function(ref) {\n if ( ref === void 0 ) ref = {};\n var isTagged = ref.isTagged; if ( isTagged === void 0 ) isTagged = false;\n\n var node = this.startNode();\n this.next();\n node.expressions = [];\n var curElt = this.parseTemplateElement({isTagged: isTagged});\n node.quasis = [curElt];\n while (!curElt.tail) {\n if (this.type === types$1.eof) { this.raise(this.pos, \"Unterminated template literal\"); }\n this.expect(types$1.dollarBraceL);\n node.expressions.push(this.parseExpression());\n this.expect(types$1.braceR);\n node.quasis.push(curElt = this.parseTemplateElement({isTagged: isTagged}));\n }\n this.next();\n return this.finishNode(node, \"TemplateLiteral\")\n };\n\n pp$5.isAsyncProp = function(prop) {\n return !prop.computed && prop.key.type === \"Identifier\" && prop.key.name === \"async\" &&\n (this.type === types$1.name || this.type === types$1.num || this.type === types$1.string || this.type === types$1.bracketL || this.type.keyword || (this.options.ecmaVersion >= 9 && this.type === types$1.star)) &&\n !lineBreak.test(this.input.slice(this.lastTokEnd, this.start))\n };\n\n // Parse an object literal or binding pattern.\n\n pp$5.parseObj = function(isPattern, refDestructuringErrors) {\n var node = this.startNode(), first = true, propHash = {};\n node.properties = [];\n this.next();\n while (!this.eat(types$1.braceR)) {\n if (!first) {\n this.expect(types$1.comma);\n if (this.options.ecmaVersion >= 5 && this.afterTrailingComma(types$1.braceR)) { break }\n } else { first = false; }\n\n var prop = this.parseProperty(isPattern, refDestructuringErrors);\n if (!isPattern) { this.checkPropClash(prop, propHash, refDestructuringErrors); }\n node.properties.push(prop);\n }\n return this.finishNode(node, isPattern ? \"ObjectPattern\" : \"ObjectExpression\")\n };\n\n pp$5.parseProperty = function(isPattern, refDestructuringErrors) {\n var prop = this.startNode(), isGenerator, isAsync, startPos, startLoc;\n if (this.options.ecmaVersion >= 9 && this.eat(types$1.ellipsis)) {\n if (isPattern) {\n prop.argument = this.parseIdent(false);\n if (this.type === types$1.comma) {\n this.raiseRecoverable(this.start, \"Comma is not permitted after the rest element\");\n }\n return this.finishNode(prop, \"RestElement\")\n }\n // Parse argument.\n prop.argument = this.parseMaybeAssign(false, refDestructuringErrors);\n // To disallow trailing comma via `this.toAssignable()`.\n if (this.type === types$1.comma && refDestructuringErrors && refDestructuringErrors.trailingComma < 0) {\n refDestructuringErrors.trailingComma = this.start;\n }\n // Finish\n return this.finishNode(prop, \"SpreadElement\")\n }\n if (this.options.ecmaVersion >= 6) {\n prop.method = false;\n prop.shorthand = false;\n if (isPattern || refDestructuringErrors) {\n startPos = this.start;\n startLoc = this.startLoc;\n }\n if (!isPattern)\n { isGenerator = this.eat(types$1.star); }\n }\n var containsEsc = this.containsEsc;\n this.parsePropertyName(prop);\n if (!isPattern && !containsEsc && this.options.ecmaVersion >= 8 && !isGenerator && this.isAsyncProp(prop)) {\n isAsync = true;\n isGenerator = this.options.ecmaVersion >= 9 && this.eat(types$1.star);\n this.parsePropertyName(prop);\n } else {\n isAsync = false;\n }\n this.parsePropertyValue(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors, containsEsc);\n return this.finishNode(prop, \"Property\")\n };\n\n pp$5.parseGetterSetter = function(prop) {\n var kind = prop.key.name;\n this.parsePropertyName(prop);\n prop.value = this.parseMethod(false);\n prop.kind = kind;\n var paramCount = prop.kind === \"get\" ? 0 : 1;\n if (prop.value.params.length !== paramCount) {\n var start = prop.value.start;\n if (prop.kind === \"get\")\n { this.raiseRecoverable(start, \"getter should have no params\"); }\n else\n { this.raiseRecoverable(start, \"setter should have exactly one param\"); }\n } else {\n if (prop.kind === \"set\" && prop.value.params[0].type === \"RestElement\")\n { this.raiseRecoverable(prop.value.params[0].start, \"Setter cannot use rest params\"); }\n }\n };\n\n pp$5.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors, containsEsc) {\n if ((isGenerator || isAsync) && this.type === types$1.colon)\n { this.unexpected(); }\n\n if (this.eat(types$1.colon)) {\n prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refDestructuringErrors);\n prop.kind = \"init\";\n } else if (this.options.ecmaVersion >= 6 && this.type === types$1.parenL) {\n if (isPattern) { this.unexpected(); }\n prop.method = true;\n prop.value = this.parseMethod(isGenerator, isAsync);\n prop.kind = \"init\";\n } else if (!isPattern && !containsEsc &&\n this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === \"Identifier\" &&\n (prop.key.name === \"get\" || prop.key.name === \"set\") &&\n (this.type !== types$1.comma && this.type !== types$1.braceR && this.type !== types$1.eq)) {\n if (isGenerator || isAsync) { this.unexpected(); }\n this.parseGetterSetter(prop);\n } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === \"Identifier\") {\n if (isGenerator || isAsync) { this.unexpected(); }\n this.checkUnreserved(prop.key);\n if (prop.key.name === \"await\" && !this.awaitIdentPos)\n { this.awaitIdentPos = startPos; }\n if (isPattern) {\n prop.value = this.parseMaybeDefault(startPos, startLoc, this.copyNode(prop.key));\n } else if (this.type === types$1.eq && refDestructuringErrors) {\n if (refDestructuringErrors.shorthandAssign < 0)\n { refDestructuringErrors.shorthandAssign = this.start; }\n prop.value = this.parseMaybeDefault(startPos, startLoc, this.copyNode(prop.key));\n } else {\n prop.value = this.copyNode(prop.key);\n }\n prop.kind = \"init\";\n prop.shorthand = true;\n } else { this.unexpected(); }\n };\n\n pp$5.parsePropertyName = function(prop) {\n if (this.options.ecmaVersion >= 6) {\n if (this.eat(types$1.bracketL)) {\n prop.computed = true;\n prop.key = this.parseMaybeAssign();\n this.expect(types$1.bracketR);\n return prop.key\n } else {\n prop.computed = false;\n }\n }\n return prop.key = this.type === types$1.num || this.type === types$1.string ? this.parseExprAtom() : this.parseIdent(this.options.allowReserved !== \"never\")\n };\n\n // Initialize empty function node.\n\n pp$5.initFunction = function(node) {\n node.id = null;\n if (this.options.ecmaVersion >= 6) { node.generator = node.expression = false; }\n if (this.options.ecmaVersion >= 8) { node.async = false; }\n };\n\n // Parse object or class method.\n\n pp$5.parseMethod = function(isGenerator, isAsync, allowDirectSuper) {\n var node = this.startNode(), oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n\n this.initFunction(node);\n if (this.options.ecmaVersion >= 6)\n { node.generator = isGenerator; }\n if (this.options.ecmaVersion >= 8)\n { node.async = !!isAsync; }\n\n this.yieldPos = 0;\n this.awaitPos = 0;\n this.awaitIdentPos = 0;\n this.enterScope(functionFlags(isAsync, node.generator) | SCOPE_SUPER | (allowDirectSuper ? SCOPE_DIRECT_SUPER : 0));\n\n this.expect(types$1.parenL);\n node.params = this.parseBindingList(types$1.parenR, false, this.options.ecmaVersion >= 8);\n this.checkYieldAwaitInDefaultParams();\n this.parseFunctionBody(node, false, true, false);\n\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n this.awaitIdentPos = oldAwaitIdentPos;\n return this.finishNode(node, \"FunctionExpression\")\n };\n\n // Parse arrow function expression with given parameters.\n\n pp$5.parseArrowExpression = function(node, params, isAsync, forInit) {\n var oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n\n this.enterScope(functionFlags(isAsync, false) | SCOPE_ARROW);\n this.initFunction(node);\n if (this.options.ecmaVersion >= 8) { node.async = !!isAsync; }\n\n this.yieldPos = 0;\n this.awaitPos = 0;\n this.awaitIdentPos = 0;\n\n node.params = this.toAssignableList(params, true);\n this.parseFunctionBody(node, true, false, forInit);\n\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n this.awaitIdentPos = oldAwaitIdentPos;\n return this.finishNode(node, \"ArrowFunctionExpression\")\n };\n\n // Parse function body and check parameters.\n\n pp$5.parseFunctionBody = function(node, isArrowFunction, isMethod, forInit) {\n var isExpression = isArrowFunction && this.type !== types$1.braceL;\n var oldStrict = this.strict, useStrict = false;\n\n if (isExpression) {\n node.body = this.parseMaybeAssign(forInit);\n node.expression = true;\n this.checkParams(node, false);\n } else {\n var nonSimple = this.options.ecmaVersion >= 7 && !this.isSimpleParamList(node.params);\n if (!oldStrict || nonSimple) {\n useStrict = this.strictDirective(this.end);\n // If this is a strict mode function, verify that argument names\n // are not repeated, and it does not try to bind the words `eval`\n // or `arguments`.\n if (useStrict && nonSimple)\n { this.raiseRecoverable(node.start, \"Illegal 'use strict' directive in function with non-simple parameter list\"); }\n }\n // Start a new scope with regard to labels and the `inFunction`\n // flag (restore them to their old value afterwards).\n var oldLabels = this.labels;\n this.labels = [];\n if (useStrict) { this.strict = true; }\n\n // Add the params to varDeclaredNames to ensure that an error is thrown\n // if a let/const declaration in the function clashes with one of the params.\n this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && !isMethod && this.isSimpleParamList(node.params));\n // Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval'\n if (this.strict && node.id) { this.checkLValSimple(node.id, BIND_OUTSIDE); }\n node.body = this.parseBlock(false, undefined, useStrict && !oldStrict);\n node.expression = false;\n this.adaptDirectivePrologue(node.body.body);\n this.labels = oldLabels;\n }\n this.exitScope();\n };\n\n pp$5.isSimpleParamList = function(params) {\n for (var i = 0, list = params; i < list.length; i += 1)\n {\n var param = list[i];\n\n if (param.type !== \"Identifier\") { return false\n } }\n return true\n };\n\n // Checks function params for various disallowed patterns such as using \"eval\"\n // or \"arguments\" and duplicate parameters.\n\n pp$5.checkParams = function(node, allowDuplicates) {\n var nameHash = Object.create(null);\n for (var i = 0, list = node.params; i < list.length; i += 1)\n {\n var param = list[i];\n\n this.checkLValInnerPattern(param, BIND_VAR, allowDuplicates ? null : nameHash);\n }\n };\n\n // Parses a comma-separated list of expressions, and returns them as\n // an array. `close` is the token type that ends the list, and\n // `allowEmpty` can be turned on to allow subsequent commas with\n // nothing in between them to be parsed as `null` (which is needed\n // for array literals).\n\n pp$5.parseExprList = function(close, allowTrailingComma, allowEmpty, refDestructuringErrors) {\n var elts = [], first = true;\n while (!this.eat(close)) {\n if (!first) {\n this.expect(types$1.comma);\n if (allowTrailingComma && this.afterTrailingComma(close)) { break }\n } else { first = false; }\n\n var elt = (void 0);\n if (allowEmpty && this.type === types$1.comma)\n { elt = null; }\n else if (this.type === types$1.ellipsis) {\n elt = this.parseSpread(refDestructuringErrors);\n if (refDestructuringErrors && this.type === types$1.comma && refDestructuringErrors.trailingComma < 0)\n { refDestructuringErrors.trailingComma = this.start; }\n } else {\n elt = this.parseMaybeAssign(false, refDestructuringErrors);\n }\n elts.push(elt);\n }\n return elts\n };\n\n pp$5.checkUnreserved = function(ref) {\n var start = ref.start;\n var end = ref.end;\n var name = ref.name;\n\n if (this.inGenerator && name === \"yield\")\n { this.raiseRecoverable(start, \"Cannot use 'yield' as identifier inside a generator\"); }\n if (this.inAsync && name === \"await\")\n { this.raiseRecoverable(start, \"Cannot use 'await' as identifier inside an async function\"); }\n if (!(this.currentThisScope().flags & SCOPE_VAR) && name === \"arguments\")\n { this.raiseRecoverable(start, \"Cannot use 'arguments' in class field initializer\"); }\n if (this.inClassStaticBlock && (name === \"arguments\" || name === \"await\"))\n { this.raise(start, (\"Cannot use \" + name + \" in class static initialization block\")); }\n if (this.keywords.test(name))\n { this.raise(start, (\"Unexpected keyword '\" + name + \"'\")); }\n if (this.options.ecmaVersion < 6 &&\n this.input.slice(start, end).indexOf(\"\\\\\") !== -1) { return }\n var re = this.strict ? this.reservedWordsStrict : this.reservedWords;\n if (re.test(name)) {\n if (!this.inAsync && name === \"await\")\n { this.raiseRecoverable(start, \"Cannot use keyword 'await' outside an async function\"); }\n this.raiseRecoverable(start, (\"The keyword '\" + name + \"' is reserved\"));\n }\n };\n\n // Parse the next token as an identifier. If `liberal` is true (used\n // when parsing properties), it will also convert keywords into\n // identifiers.\n\n pp$5.parseIdent = function(liberal) {\n var node = this.parseIdentNode();\n this.next(!!liberal);\n this.finishNode(node, \"Identifier\");\n if (!liberal) {\n this.checkUnreserved(node);\n if (node.name === \"await\" && !this.awaitIdentPos)\n { this.awaitIdentPos = node.start; }\n }\n return node\n };\n\n pp$5.parseIdentNode = function() {\n var node = this.startNode();\n if (this.type === types$1.name) {\n node.name = this.value;\n } else if (this.type.keyword) {\n node.name = this.type.keyword;\n\n // To fix https://github.com/acornjs/acorn/issues/575\n // `class` and `function` keywords push new context into this.context.\n // But there is no chance to pop the context if the keyword is consumed as an identifier such as a property name.\n // If the previous token is a dot, this does not apply because the context-managing code already ignored the keyword\n if ((node.name === \"class\" || node.name === \"function\") &&\n (this.lastTokEnd !== this.lastTokStart + 1 || this.input.charCodeAt(this.lastTokStart) !== 46)) {\n this.context.pop();\n }\n this.type = types$1.name;\n } else {\n this.unexpected();\n }\n return node\n };\n\n pp$5.parsePrivateIdent = function() {\n var node = this.startNode();\n if (this.type === types$1.privateId) {\n node.name = this.value;\n } else {\n this.unexpected();\n }\n this.next();\n this.finishNode(node, \"PrivateIdentifier\");\n\n // For validating existence\n if (this.options.checkPrivateFields) {\n if (this.privateNameStack.length === 0) {\n this.raise(node.start, (\"Private field '#\" + (node.name) + \"' must be declared in an enclosing class\"));\n } else {\n this.privateNameStack[this.privateNameStack.length - 1].used.push(node);\n }\n }\n\n return node\n };\n\n // Parses yield expression inside generator.\n\n pp$5.parseYield = function(forInit) {\n if (!this.yieldPos) { this.yieldPos = this.start; }\n\n var node = this.startNode();\n this.next();\n if (this.type === types$1.semi || this.canInsertSemicolon() || (this.type !== types$1.star && !this.type.startsExpr)) {\n node.delegate = false;\n node.argument = null;\n } else {\n node.delegate = this.eat(types$1.star);\n node.argument = this.parseMaybeAssign(forInit);\n }\n return this.finishNode(node, \"YieldExpression\")\n };\n\n pp$5.parseAwait = function(forInit) {\n if (!this.awaitPos) { this.awaitPos = this.start; }\n\n var node = this.startNode();\n this.next();\n node.argument = this.parseMaybeUnary(null, true, false, forInit);\n return this.finishNode(node, \"AwaitExpression\")\n };\n\n var pp$4 = Parser.prototype;\n\n // This function is used to raise exceptions on parse errors. It\n // takes an offset integer (into the current `input`) to indicate\n // the location of the error, attaches the position to the end\n // of the error message, and then raises a `SyntaxError` with that\n // message.\n\n pp$4.raise = function(pos, message) {\n var loc = getLineInfo(this.input, pos);\n message += \" (\" + loc.line + \":\" + loc.column + \")\";\n if (this.sourceFile) {\n message += \" in \" + this.sourceFile;\n }\n var err = new SyntaxError(message);\n err.pos = pos; err.loc = loc; err.raisedAt = this.pos;\n throw err\n };\n\n pp$4.raiseRecoverable = pp$4.raise;\n\n pp$4.curPosition = function() {\n if (this.options.locations) {\n return new Position(this.curLine, this.pos - this.lineStart)\n }\n };\n\n var pp$3 = Parser.prototype;\n\n var Scope = function Scope(flags) {\n this.flags = flags;\n // A list of var-declared names in the current lexical scope\n this.var = [];\n // A list of lexically-declared names in the current lexical scope\n this.lexical = [];\n // A list of lexically-declared FunctionDeclaration names in the current lexical scope\n this.functions = [];\n };\n\n // The functions in this module keep track of declared variables in the current scope in order to detect duplicate variable names.\n\n pp$3.enterScope = function(flags) {\n this.scopeStack.push(new Scope(flags));\n };\n\n pp$3.exitScope = function() {\n this.scopeStack.pop();\n };\n\n // The spec says:\n // > At the top level of a function, or script, function declarations are\n // > treated like var declarations rather than like lexical declarations.\n pp$3.treatFunctionsAsVarInScope = function(scope) {\n return (scope.flags & SCOPE_FUNCTION) || !this.inModule && (scope.flags & SCOPE_TOP)\n };\n\n pp$3.declareName = function(name, bindingType, pos) {\n var redeclared = false;\n if (bindingType === BIND_LEXICAL) {\n var scope = this.currentScope();\n redeclared = scope.lexical.indexOf(name) > -1 || scope.functions.indexOf(name) > -1 || scope.var.indexOf(name) > -1;\n scope.lexical.push(name);\n if (this.inModule && (scope.flags & SCOPE_TOP))\n { delete this.undefinedExports[name]; }\n } else if (bindingType === BIND_SIMPLE_CATCH) {\n var scope$1 = this.currentScope();\n scope$1.lexical.push(name);\n } else if (bindingType === BIND_FUNCTION) {\n var scope$2 = this.currentScope();\n if (this.treatFunctionsAsVar)\n { redeclared = scope$2.lexical.indexOf(name) > -1; }\n else\n { redeclared = scope$2.lexical.indexOf(name) > -1 || scope$2.var.indexOf(name) > -1; }\n scope$2.functions.push(name);\n } else {\n for (var i = this.scopeStack.length - 1; i >= 0; --i) {\n var scope$3 = this.scopeStack[i];\n if (scope$3.lexical.indexOf(name) > -1 && !((scope$3.flags & SCOPE_SIMPLE_CATCH) && scope$3.lexical[0] === name) ||\n !this.treatFunctionsAsVarInScope(scope$3) && scope$3.functions.indexOf(name) > -1) {\n redeclared = true;\n break\n }\n scope$3.var.push(name);\n if (this.inModule && (scope$3.flags & SCOPE_TOP))\n { delete this.undefinedExports[name]; }\n if (scope$3.flags & SCOPE_VAR) { break }\n }\n }\n if (redeclared) { this.raiseRecoverable(pos, (\"Identifier '\" + name + \"' has already been declared\")); }\n };\n\n pp$3.checkLocalExport = function(id) {\n // scope.functions must be empty as Module code is always strict.\n if (this.scopeStack[0].lexical.indexOf(id.name) === -1 &&\n this.scopeStack[0].var.indexOf(id.name) === -1) {\n this.undefinedExports[id.name] = id;\n }\n };\n\n pp$3.currentScope = function() {\n return this.scopeStack[this.scopeStack.length - 1]\n };\n\n pp$3.currentVarScope = function() {\n for (var i = this.scopeStack.length - 1;; i--) {\n var scope = this.scopeStack[i];\n if (scope.flags & (SCOPE_VAR | SCOPE_CLASS_FIELD_INIT | SCOPE_CLASS_STATIC_BLOCK)) { return scope }\n }\n };\n\n // Could be useful for `this`, `new.target`, `super()`, `super.property`, and `super[property]`.\n pp$3.currentThisScope = function() {\n for (var i = this.scopeStack.length - 1;; i--) {\n var scope = this.scopeStack[i];\n if (scope.flags & (SCOPE_VAR | SCOPE_CLASS_FIELD_INIT | SCOPE_CLASS_STATIC_BLOCK) &&\n !(scope.flags & SCOPE_ARROW)) { return scope }\n }\n };\n\n var Node = function Node(parser, pos, loc) {\n this.type = \"\";\n this.start = pos;\n this.end = 0;\n if (parser.options.locations)\n { this.loc = new SourceLocation(parser, loc); }\n if (parser.options.directSourceFile)\n { this.sourceFile = parser.options.directSourceFile; }\n if (parser.options.ranges)\n { this.range = [pos, 0]; }\n };\n\n // Start an AST node, attaching a start offset.\n\n var pp$2 = Parser.prototype;\n\n pp$2.startNode = function() {\n return new Node(this, this.start, this.startLoc)\n };\n\n pp$2.startNodeAt = function(pos, loc) {\n return new Node(this, pos, loc)\n };\n\n // Finish an AST node, adding `type` and `end` properties.\n\n function finishNodeAt(node, type, pos, loc) {\n node.type = type;\n node.end = pos;\n if (this.options.locations)\n { node.loc.end = loc; }\n if (this.options.ranges)\n { node.range[1] = pos; }\n return node\n }\n\n pp$2.finishNode = function(node, type) {\n return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc)\n };\n\n // Finish node at given position\n\n pp$2.finishNodeAt = function(node, type, pos, loc) {\n return finishNodeAt.call(this, node, type, pos, loc)\n };\n\n pp$2.copyNode = function(node) {\n var newNode = new Node(this, node.start, this.startLoc);\n for (var prop in node) { newNode[prop] = node[prop]; }\n return newNode\n };\n\n // This file was generated by \"bin/generate-unicode-script-values.js\". Do not modify manually!\n var scriptValuesAddedInUnicode = \"Berf Beria_Erfe Gara Garay Gukh Gurung_Khema Hrkt Katakana_Or_Hiragana Kawi Kirat_Rai Krai Nag_Mundari Nagm Ol_Onal Onao Sidetic Sidt Sunu Sunuwar Tai_Yo Tayo Todhri Todr Tolong_Siki Tols Tulu_Tigalari Tutg Unknown Zzzz\";\n\n // This file contains Unicode properties extracted from the ECMAScript specification.\n // The lists are extracted like so:\n // $$('#table-binary-unicode-properties > figure > table > tbody > tr > td:nth-child(1) code').map(el => el.innerText)\n\n // #table-binary-unicode-properties\n var ecma9BinaryProperties = \"ASCII ASCII_Hex_Digit AHex Alphabetic Alpha Any Assigned Bidi_Control Bidi_C Bidi_Mirrored Bidi_M Case_Ignorable CI Cased Changes_When_Casefolded CWCF Changes_When_Casemapped CWCM Changes_When_Lowercased CWL Changes_When_NFKC_Casefolded CWKCF Changes_When_Titlecased CWT Changes_When_Uppercased CWU Dash Default_Ignorable_Code_Point DI Deprecated Dep Diacritic Dia Emoji Emoji_Component Emoji_Modifier Emoji_Modifier_Base Emoji_Presentation Extender Ext Grapheme_Base Gr_Base Grapheme_Extend Gr_Ext Hex_Digit Hex IDS_Binary_Operator IDSB IDS_Trinary_Operator IDST ID_Continue IDC ID_Start IDS Ideographic Ideo Join_Control Join_C Logical_Order_Exception LOE Lowercase Lower Math Noncharacter_Code_Point NChar Pattern_Syntax Pat_Syn Pattern_White_Space Pat_WS Quotation_Mark QMark Radical Regional_Indicator RI Sentence_Terminal STerm Soft_Dotted SD Terminal_Punctuation Term Unified_Ideograph UIdeo Uppercase Upper Variation_Selector VS White_Space space XID_Continue XIDC XID_Start XIDS\";\n var ecma10BinaryProperties = ecma9BinaryProperties + \" Extended_Pictographic\";\n var ecma11BinaryProperties = ecma10BinaryProperties;\n var ecma12BinaryProperties = ecma11BinaryProperties + \" EBase EComp EMod EPres ExtPict\";\n var ecma13BinaryProperties = ecma12BinaryProperties;\n var ecma14BinaryProperties = ecma13BinaryProperties;\n\n var unicodeBinaryProperties = {\n 9: ecma9BinaryProperties,\n 10: ecma10BinaryProperties,\n 11: ecma11BinaryProperties,\n 12: ecma12BinaryProperties,\n 13: ecma13BinaryProperties,\n 14: ecma14BinaryProperties\n };\n\n // #table-binary-unicode-properties-of-strings\n var ecma14BinaryPropertiesOfStrings = \"Basic_Emoji Emoji_Keycap_Sequence RGI_Emoji_Modifier_Sequence RGI_Emoji_Flag_Sequence RGI_Emoji_Tag_Sequence RGI_Emoji_ZWJ_Sequence RGI_Emoji\";\n\n var unicodeBinaryPropertiesOfStrings = {\n 9: \"\",\n 10: \"\",\n 11: \"\",\n 12: \"\",\n 13: \"\",\n 14: ecma14BinaryPropertiesOfStrings\n };\n\n // #table-unicode-general-category-values\n var unicodeGeneralCategoryValues = \"Cased_Letter LC Close_Punctuation Pe Connector_Punctuation Pc Control Cc cntrl Currency_Symbol Sc Dash_Punctuation Pd Decimal_Number Nd digit Enclosing_Mark Me Final_Punctuation Pf Format Cf Initial_Punctuation Pi Letter L Letter_Number Nl Line_Separator Zl Lowercase_Letter Ll Mark M Combining_Mark Math_Symbol Sm Modifier_Letter Lm Modifier_Symbol Sk Nonspacing_Mark Mn Number N Open_Punctuation Ps Other C Other_Letter Lo Other_Number No Other_Punctuation Po Other_Symbol So Paragraph_Separator Zp Private_Use Co Punctuation P punct Separator Z Space_Separator Zs Spacing_Mark Mc Surrogate Cs Symbol S Titlecase_Letter Lt Unassigned Cn Uppercase_Letter Lu\";\n\n // #table-unicode-script-values\n var ecma9ScriptValues = \"Adlam Adlm Ahom Anatolian_Hieroglyphs Hluw Arabic Arab Armenian Armn Avestan Avst Balinese Bali Bamum Bamu Bassa_Vah Bass Batak Batk Bengali Beng Bhaiksuki Bhks Bopomofo Bopo Brahmi Brah Braille Brai Buginese Bugi Buhid Buhd Canadian_Aboriginal Cans Carian Cari Caucasian_Albanian Aghb Chakma Cakm Cham Cham Cherokee Cher Common Zyyy Coptic Copt Qaac Cuneiform Xsux Cypriot Cprt Cyrillic Cyrl Deseret Dsrt Devanagari Deva Duployan Dupl Egyptian_Hieroglyphs Egyp Elbasan Elba Ethiopic Ethi Georgian Geor Glagolitic Glag Gothic Goth Grantha Gran Greek Grek Gujarati Gujr Gurmukhi Guru Han Hani Hangul Hang Hanunoo Hano Hatran Hatr Hebrew Hebr Hiragana Hira Imperial_Aramaic Armi Inherited Zinh Qaai Inscriptional_Pahlavi Phli Inscriptional_Parthian Prti Javanese Java Kaithi Kthi Kannada Knda Katakana Kana Kayah_Li Kali Kharoshthi Khar Khmer Khmr Khojki Khoj Khudawadi Sind Lao Laoo Latin Latn Lepcha Lepc Limbu Limb Linear_A Lina Linear_B Linb Lisu Lisu Lycian Lyci Lydian Lydi Mahajani Mahj Malayalam Mlym Mandaic Mand Manichaean Mani Marchen Marc Masaram_Gondi Gonm Meetei_Mayek Mtei Mende_Kikakui Mend Meroitic_Cursive Merc Meroitic_Hieroglyphs Mero Miao Plrd Modi Mongolian Mong Mro Mroo Multani Mult Myanmar Mymr Nabataean Nbat New_Tai_Lue Talu Newa Newa Nko Nkoo Nushu Nshu Ogham Ogam Ol_Chiki Olck Old_Hungarian Hung Old_Italic Ital Old_North_Arabian Narb Old_Permic Perm Old_Persian Xpeo Old_South_Arabian Sarb Old_Turkic Orkh Oriya Orya Osage Osge Osmanya Osma Pahawh_Hmong Hmng Palmyrene Palm Pau_Cin_Hau Pauc Phags_Pa Phag Phoenician Phnx Psalter_Pahlavi Phlp Rejang Rjng Runic Runr Samaritan Samr Saurashtra Saur Sharada Shrd Shavian Shaw Siddham Sidd SignWriting Sgnw Sinhala Sinh Sora_Sompeng Sora Soyombo Soyo Sundanese Sund Syloti_Nagri Sylo Syriac Syrc Tagalog Tglg Tagbanwa Tagb Tai_Le Tale Tai_Tham Lana Tai_Viet Tavt Takri Takr Tamil Taml Tangut Tang Telugu Telu Thaana Thaa Thai Thai Tibetan Tibt Tifinagh Tfng Tirhuta Tirh Ugaritic Ugar Vai Vaii Warang_Citi Wara Yi Yiii Zanabazar_Square Zanb\";\n var ecma10ScriptValues = ecma9ScriptValues + \" Dogra Dogr Gunjala_Gondi Gong Hanifi_Rohingya Rohg Makasar Maka Medefaidrin Medf Old_Sogdian Sogo Sogdian Sogd\";\n var ecma11ScriptValues = ecma10ScriptValues + \" Elymaic Elym Nandinagari Nand Nyiakeng_Puachue_Hmong Hmnp Wancho Wcho\";\n var ecma12ScriptValues = ecma11ScriptValues + \" Chorasmian Chrs Diak Dives_Akuru Khitan_Small_Script Kits Yezi Yezidi\";\n var ecma13ScriptValues = ecma12ScriptValues + \" Cypro_Minoan Cpmn Old_Uyghur Ougr Tangsa Tnsa Toto Vithkuqi Vith\";\n var ecma14ScriptValues = ecma13ScriptValues + \" \" + scriptValuesAddedInUnicode;\n\n var unicodeScriptValues = {\n 9: ecma9ScriptValues,\n 10: ecma10ScriptValues,\n 11: ecma11ScriptValues,\n 12: ecma12ScriptValues,\n 13: ecma13ScriptValues,\n 14: ecma14ScriptValues\n };\n\n var data = {};\n function buildUnicodeData(ecmaVersion) {\n var d = data[ecmaVersion] = {\n binary: wordsRegexp(unicodeBinaryProperties[ecmaVersion] + \" \" + unicodeGeneralCategoryValues),\n binaryOfStrings: wordsRegexp(unicodeBinaryPropertiesOfStrings[ecmaVersion]),\n nonBinary: {\n General_Category: wordsRegexp(unicodeGeneralCategoryValues),\n Script: wordsRegexp(unicodeScriptValues[ecmaVersion])\n }\n };\n d.nonBinary.Script_Extensions = d.nonBinary.Script;\n\n d.nonBinary.gc = d.nonBinary.General_Category;\n d.nonBinary.sc = d.nonBinary.Script;\n d.nonBinary.scx = d.nonBinary.Script_Extensions;\n }\n\n for (var i = 0, list = [9, 10, 11, 12, 13, 14]; i < list.length; i += 1) {\n var ecmaVersion = list[i];\n\n buildUnicodeData(ecmaVersion);\n }\n\n var pp$1 = Parser.prototype;\n\n // Track disjunction structure to determine whether a duplicate\n // capture group name is allowed because it is in a separate branch.\n var BranchID = function BranchID(parent, base) {\n // Parent disjunction branch\n this.parent = parent;\n // Identifies this set of sibling branches\n this.base = base || this;\n };\n\n BranchID.prototype.separatedFrom = function separatedFrom (alt) {\n // A branch is separate from another branch if they or any of\n // their parents are siblings in a given disjunction\n for (var self = this; self; self = self.parent) {\n for (var other = alt; other; other = other.parent) {\n if (self.base === other.base && self !== other) { return true }\n }\n }\n return false\n };\n\n BranchID.prototype.sibling = function sibling () {\n return new BranchID(this.parent, this.base)\n };\n\n var RegExpValidationState = function RegExpValidationState(parser) {\n this.parser = parser;\n this.validFlags = \"gim\" + (parser.options.ecmaVersion >= 6 ? \"uy\" : \"\") + (parser.options.ecmaVersion >= 9 ? \"s\" : \"\") + (parser.options.ecmaVersion >= 13 ? \"d\" : \"\") + (parser.options.ecmaVersion >= 15 ? \"v\" : \"\");\n this.unicodeProperties = data[parser.options.ecmaVersion >= 14 ? 14 : parser.options.ecmaVersion];\n this.source = \"\";\n this.flags = \"\";\n this.start = 0;\n this.switchU = false;\n this.switchV = false;\n this.switchN = false;\n this.pos = 0;\n this.lastIntValue = 0;\n this.lastStringValue = \"\";\n this.lastAssertionIsQuantifiable = false;\n this.numCapturingParens = 0;\n this.maxBackReference = 0;\n this.groupNames = Object.create(null);\n this.backReferenceNames = [];\n this.branchID = null;\n };\n\n RegExpValidationState.prototype.reset = function reset (start, pattern, flags) {\n var unicodeSets = flags.indexOf(\"v\") !== -1;\n var unicode = flags.indexOf(\"u\") !== -1;\n this.start = start | 0;\n this.source = pattern + \"\";\n this.flags = flags;\n if (unicodeSets && this.parser.options.ecmaVersion >= 15) {\n this.switchU = true;\n this.switchV = true;\n this.switchN = true;\n } else {\n this.switchU = unicode && this.parser.options.ecmaVersion >= 6;\n this.switchV = false;\n this.switchN = unicode && this.parser.options.ecmaVersion >= 9;\n }\n };\n\n RegExpValidationState.prototype.raise = function raise (message) {\n this.parser.raiseRecoverable(this.start, (\"Invalid regular expression: /\" + (this.source) + \"/: \" + message));\n };\n\n // If u flag is given, this returns the code point at the index (it combines a surrogate pair).\n // Otherwise, this returns the code unit of the index (can be a part of a surrogate pair).\n RegExpValidationState.prototype.at = function at (i, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n var s = this.source;\n var l = s.length;\n if (i >= l) {\n return -1\n }\n var c = s.charCodeAt(i);\n if (!(forceU || this.switchU) || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l) {\n return c\n }\n var next = s.charCodeAt(i + 1);\n return next >= 0xDC00 && next <= 0xDFFF ? (c << 10) + next - 0x35FDC00 : c\n };\n\n RegExpValidationState.prototype.nextIndex = function nextIndex (i, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n var s = this.source;\n var l = s.length;\n if (i >= l) {\n return l\n }\n var c = s.charCodeAt(i), next;\n if (!(forceU || this.switchU) || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l ||\n (next = s.charCodeAt(i + 1)) < 0xDC00 || next > 0xDFFF) {\n return i + 1\n }\n return i + 2\n };\n\n RegExpValidationState.prototype.current = function current (forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n return this.at(this.pos, forceU)\n };\n\n RegExpValidationState.prototype.lookahead = function lookahead (forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n return this.at(this.nextIndex(this.pos, forceU), forceU)\n };\n\n RegExpValidationState.prototype.advance = function advance (forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n this.pos = this.nextIndex(this.pos, forceU);\n };\n\n RegExpValidationState.prototype.eat = function eat (ch, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n if (this.current(forceU) === ch) {\n this.advance(forceU);\n return true\n }\n return false\n };\n\n RegExpValidationState.prototype.eatChars = function eatChars (chs, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n var pos = this.pos;\n for (var i = 0, list = chs; i < list.length; i += 1) {\n var ch = list[i];\n\n var current = this.at(pos, forceU);\n if (current === -1 || current !== ch) {\n return false\n }\n pos = this.nextIndex(pos, forceU);\n }\n this.pos = pos;\n return true\n };\n\n /**\n * Validate the flags part of a given RegExpLiteral.\n *\n * @param {RegExpValidationState} state The state to validate RegExp.\n * @returns {void}\n */\n pp$1.validateRegExpFlags = function(state) {\n var validFlags = state.validFlags;\n var flags = state.flags;\n\n var u = false;\n var v = false;\n\n for (var i = 0; i < flags.length; i++) {\n var flag = flags.charAt(i);\n if (validFlags.indexOf(flag) === -1) {\n this.raise(state.start, \"Invalid regular expression flag\");\n }\n if (flags.indexOf(flag, i + 1) > -1) {\n this.raise(state.start, \"Duplicate regular expression flag\");\n }\n if (flag === \"u\") { u = true; }\n if (flag === \"v\") { v = true; }\n }\n if (this.options.ecmaVersion >= 15 && u && v) {\n this.raise(state.start, \"Invalid regular expression flag\");\n }\n };\n\n function hasProp(obj) {\n for (var _ in obj) { return true }\n return false\n }\n\n /**\n * Validate the pattern part of a given RegExpLiteral.\n *\n * @param {RegExpValidationState} state The state to validate RegExp.\n * @returns {void}\n */\n pp$1.validateRegExpPattern = function(state) {\n this.regexp_pattern(state);\n\n // The goal symbol for the parse is |Pattern[~U, ~N]|. If the result of\n // parsing contains a |GroupName|, reparse with the goal symbol\n // |Pattern[~U, +N]| and use this result instead. Throw a *SyntaxError*\n // exception if _P_ did not conform to the grammar, if any elements of _P_\n // were not matched by the parse, or if any Early Error conditions exist.\n if (!state.switchN && this.options.ecmaVersion >= 9 && hasProp(state.groupNames)) {\n state.switchN = true;\n this.regexp_pattern(state);\n }\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Pattern\n pp$1.regexp_pattern = function(state) {\n state.pos = 0;\n state.lastIntValue = 0;\n state.lastStringValue = \"\";\n state.lastAssertionIsQuantifiable = false;\n state.numCapturingParens = 0;\n state.maxBackReference = 0;\n state.groupNames = Object.create(null);\n state.backReferenceNames.length = 0;\n state.branchID = null;\n\n this.regexp_disjunction(state);\n\n if (state.pos !== state.source.length) {\n // Make the same messages as V8.\n if (state.eat(0x29 /* ) */)) {\n state.raise(\"Unmatched ')'\");\n }\n if (state.eat(0x5D /* ] */) || state.eat(0x7D /* } */)) {\n state.raise(\"Lone quantifier brackets\");\n }\n }\n if (state.maxBackReference > state.numCapturingParens) {\n state.raise(\"Invalid escape\");\n }\n for (var i = 0, list = state.backReferenceNames; i < list.length; i += 1) {\n var name = list[i];\n\n if (!state.groupNames[name]) {\n state.raise(\"Invalid named capture referenced\");\n }\n }\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Disjunction\n pp$1.regexp_disjunction = function(state) {\n var trackDisjunction = this.options.ecmaVersion >= 16;\n if (trackDisjunction) { state.branchID = new BranchID(state.branchID, null); }\n this.regexp_alternative(state);\n while (state.eat(0x7C /* | */)) {\n if (trackDisjunction) { state.branchID = state.branchID.sibling(); }\n this.regexp_alternative(state);\n }\n if (trackDisjunction) { state.branchID = state.branchID.parent; }\n\n // Make the same message as V8.\n if (this.regexp_eatQuantifier(state, true)) {\n state.raise(\"Nothing to repeat\");\n }\n if (state.eat(0x7B /* { */)) {\n state.raise(\"Lone quantifier brackets\");\n }\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Alternative\n pp$1.regexp_alternative = function(state) {\n while (state.pos < state.source.length && this.regexp_eatTerm(state)) {}\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-Term\n pp$1.regexp_eatTerm = function(state) {\n if (this.regexp_eatAssertion(state)) {\n // Handle `QuantifiableAssertion Quantifier` alternative.\n // `state.lastAssertionIsQuantifiable` is true if the last eaten Assertion\n // is a QuantifiableAssertion.\n if (state.lastAssertionIsQuantifiable && this.regexp_eatQuantifier(state)) {\n // Make the same message as V8.\n if (state.switchU) {\n state.raise(\"Invalid quantifier\");\n }\n }\n return true\n }\n\n if (state.switchU ? this.regexp_eatAtom(state) : this.regexp_eatExtendedAtom(state)) {\n this.regexp_eatQuantifier(state);\n return true\n }\n\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-Assertion\n pp$1.regexp_eatAssertion = function(state) {\n var start = state.pos;\n state.lastAssertionIsQuantifiable = false;\n\n // ^, $\n if (state.eat(0x5E /* ^ */) || state.eat(0x24 /* $ */)) {\n return true\n }\n\n // \\b \\B\n if (state.eat(0x5C /* \\ */)) {\n if (state.eat(0x42 /* B */) || state.eat(0x62 /* b */)) {\n return true\n }\n state.pos = start;\n }\n\n // Lookahead / Lookbehind\n if (state.eat(0x28 /* ( */) && state.eat(0x3F /* ? */)) {\n var lookbehind = false;\n if (this.options.ecmaVersion >= 9) {\n lookbehind = state.eat(0x3C /* < */);\n }\n if (state.eat(0x3D /* = */) || state.eat(0x21 /* ! */)) {\n this.regexp_disjunction(state);\n if (!state.eat(0x29 /* ) */)) {\n state.raise(\"Unterminated group\");\n }\n state.lastAssertionIsQuantifiable = !lookbehind;\n return true\n }\n }\n\n state.pos = start;\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Quantifier\n pp$1.regexp_eatQuantifier = function(state, noError) {\n if ( noError === void 0 ) noError = false;\n\n if (this.regexp_eatQuantifierPrefix(state, noError)) {\n state.eat(0x3F /* ? */);\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-QuantifierPrefix\n pp$1.regexp_eatQuantifierPrefix = function(state, noError) {\n return (\n state.eat(0x2A /* * */) ||\n state.eat(0x2B /* + */) ||\n state.eat(0x3F /* ? */) ||\n this.regexp_eatBracedQuantifier(state, noError)\n )\n };\n pp$1.regexp_eatBracedQuantifier = function(state, noError) {\n var start = state.pos;\n if (state.eat(0x7B /* { */)) {\n var min = 0, max = -1;\n if (this.regexp_eatDecimalDigits(state)) {\n min = state.lastIntValue;\n if (state.eat(0x2C /* , */) && this.regexp_eatDecimalDigits(state)) {\n max = state.lastIntValue;\n }\n if (state.eat(0x7D /* } */)) {\n // SyntaxError in https://www.ecma-international.org/ecma-262/8.0/#sec-term\n if (max !== -1 && max < min && !noError) {\n state.raise(\"numbers out of order in {} quantifier\");\n }\n return true\n }\n }\n if (state.switchU && !noError) {\n state.raise(\"Incomplete quantifier\");\n }\n state.pos = start;\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Atom\n pp$1.regexp_eatAtom = function(state) {\n return (\n this.regexp_eatPatternCharacters(state) ||\n state.eat(0x2E /* . */) ||\n this.regexp_eatReverseSolidusAtomEscape(state) ||\n this.regexp_eatCharacterClass(state) ||\n this.regexp_eatUncapturingGroup(state) ||\n this.regexp_eatCapturingGroup(state)\n )\n };\n pp$1.regexp_eatReverseSolidusAtomEscape = function(state) {\n var start = state.pos;\n if (state.eat(0x5C /* \\ */)) {\n if (this.regexp_eatAtomEscape(state)) {\n return true\n }\n state.pos = start;\n }\n return false\n };\n pp$1.regexp_eatUncapturingGroup = function(state) {\n var start = state.pos;\n if (state.eat(0x28 /* ( */)) {\n if (state.eat(0x3F /* ? */)) {\n if (this.options.ecmaVersion >= 16) {\n var addModifiers = this.regexp_eatModifiers(state);\n var hasHyphen = state.eat(0x2D /* - */);\n if (addModifiers || hasHyphen) {\n for (var i = 0; i < addModifiers.length; i++) {\n var modifier = addModifiers.charAt(i);\n if (addModifiers.indexOf(modifier, i + 1) > -1) {\n state.raise(\"Duplicate regular expression modifiers\");\n }\n }\n if (hasHyphen) {\n var removeModifiers = this.regexp_eatModifiers(state);\n if (!addModifiers && !removeModifiers && state.current() === 0x3A /* : */) {\n state.raise(\"Invalid regular expression modifiers\");\n }\n for (var i$1 = 0; i$1 < removeModifiers.length; i$1++) {\n var modifier$1 = removeModifiers.charAt(i$1);\n if (\n removeModifiers.indexOf(modifier$1, i$1 + 1) > -1 ||\n addModifiers.indexOf(modifier$1) > -1\n ) {\n state.raise(\"Duplicate regular expression modifiers\");\n }\n }\n }\n }\n }\n if (state.eat(0x3A /* : */)) {\n this.regexp_disjunction(state);\n if (state.eat(0x29 /* ) */)) {\n return true\n }\n state.raise(\"Unterminated group\");\n }\n }\n state.pos = start;\n }\n return false\n };\n pp$1.regexp_eatCapturingGroup = function(state) {\n if (state.eat(0x28 /* ( */)) {\n if (this.options.ecmaVersion >= 9) {\n this.regexp_groupSpecifier(state);\n } else if (state.current() === 0x3F /* ? */) {\n state.raise(\"Invalid group\");\n }\n this.regexp_disjunction(state);\n if (state.eat(0x29 /* ) */)) {\n state.numCapturingParens += 1;\n return true\n }\n state.raise(\"Unterminated group\");\n }\n return false\n };\n // RegularExpressionModifiers ::\n // [empty]\n // RegularExpressionModifiers RegularExpressionModifier\n pp$1.regexp_eatModifiers = function(state) {\n var modifiers = \"\";\n var ch = 0;\n while ((ch = state.current()) !== -1 && isRegularExpressionModifier(ch)) {\n modifiers += codePointToString(ch);\n state.advance();\n }\n return modifiers\n };\n // RegularExpressionModifier :: one of\n // `i` `m` `s`\n function isRegularExpressionModifier(ch) {\n return ch === 0x69 /* i */ || ch === 0x6d /* m */ || ch === 0x73 /* s */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ExtendedAtom\n pp$1.regexp_eatExtendedAtom = function(state) {\n return (\n state.eat(0x2E /* . */) ||\n this.regexp_eatReverseSolidusAtomEscape(state) ||\n this.regexp_eatCharacterClass(state) ||\n this.regexp_eatUncapturingGroup(state) ||\n this.regexp_eatCapturingGroup(state) ||\n this.regexp_eatInvalidBracedQuantifier(state) ||\n this.regexp_eatExtendedPatternCharacter(state)\n )\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-InvalidBracedQuantifier\n pp$1.regexp_eatInvalidBracedQuantifier = function(state) {\n if (this.regexp_eatBracedQuantifier(state, true)) {\n state.raise(\"Nothing to repeat\");\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-SyntaxCharacter\n pp$1.regexp_eatSyntaxCharacter = function(state) {\n var ch = state.current();\n if (isSyntaxCharacter(ch)) {\n state.lastIntValue = ch;\n state.advance();\n return true\n }\n return false\n };\n function isSyntaxCharacter(ch) {\n return (\n ch === 0x24 /* $ */ ||\n ch >= 0x28 /* ( */ && ch <= 0x2B /* + */ ||\n ch === 0x2E /* . */ ||\n ch === 0x3F /* ? */ ||\n ch >= 0x5B /* [ */ && ch <= 0x5E /* ^ */ ||\n ch >= 0x7B /* { */ && ch <= 0x7D /* } */\n )\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-PatternCharacter\n // But eat eager.\n pp$1.regexp_eatPatternCharacters = function(state) {\n var start = state.pos;\n var ch = 0;\n while ((ch = state.current()) !== -1 && !isSyntaxCharacter(ch)) {\n state.advance();\n }\n return state.pos !== start\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ExtendedPatternCharacter\n pp$1.regexp_eatExtendedPatternCharacter = function(state) {\n var ch = state.current();\n if (\n ch !== -1 &&\n ch !== 0x24 /* $ */ &&\n !(ch >= 0x28 /* ( */ && ch <= 0x2B /* + */) &&\n ch !== 0x2E /* . */ &&\n ch !== 0x3F /* ? */ &&\n ch !== 0x5B /* [ */ &&\n ch !== 0x5E /* ^ */ &&\n ch !== 0x7C /* | */\n ) {\n state.advance();\n return true\n }\n return false\n };\n\n // GroupSpecifier ::\n // [empty]\n // `?` GroupName\n pp$1.regexp_groupSpecifier = function(state) {\n if (state.eat(0x3F /* ? */)) {\n if (!this.regexp_eatGroupName(state)) { state.raise(\"Invalid group\"); }\n var trackDisjunction = this.options.ecmaVersion >= 16;\n var known = state.groupNames[state.lastStringValue];\n if (known) {\n if (trackDisjunction) {\n for (var i = 0, list = known; i < list.length; i += 1) {\n var altID = list[i];\n\n if (!altID.separatedFrom(state.branchID))\n { state.raise(\"Duplicate capture group name\"); }\n }\n } else {\n state.raise(\"Duplicate capture group name\");\n }\n }\n if (trackDisjunction) {\n (known || (state.groupNames[state.lastStringValue] = [])).push(state.branchID);\n } else {\n state.groupNames[state.lastStringValue] = true;\n }\n }\n };\n\n // GroupName ::\n // `<` RegExpIdentifierName `>`\n // Note: this updates `state.lastStringValue` property with the eaten name.\n pp$1.regexp_eatGroupName = function(state) {\n state.lastStringValue = \"\";\n if (state.eat(0x3C /* < */)) {\n if (this.regexp_eatRegExpIdentifierName(state) && state.eat(0x3E /* > */)) {\n return true\n }\n state.raise(\"Invalid capture group name\");\n }\n return false\n };\n\n // RegExpIdentifierName ::\n // RegExpIdentifierStart\n // RegExpIdentifierName RegExpIdentifierPart\n // Note: this updates `state.lastStringValue` property with the eaten name.\n pp$1.regexp_eatRegExpIdentifierName = function(state) {\n state.lastStringValue = \"\";\n if (this.regexp_eatRegExpIdentifierStart(state)) {\n state.lastStringValue += codePointToString(state.lastIntValue);\n while (this.regexp_eatRegExpIdentifierPart(state)) {\n state.lastStringValue += codePointToString(state.lastIntValue);\n }\n return true\n }\n return false\n };\n\n // RegExpIdentifierStart ::\n // UnicodeIDStart\n // `$`\n // `_`\n // `\\` RegExpUnicodeEscapeSequence[+U]\n pp$1.regexp_eatRegExpIdentifierStart = function(state) {\n var start = state.pos;\n var forceU = this.options.ecmaVersion >= 11;\n var ch = state.current(forceU);\n state.advance(forceU);\n\n if (ch === 0x5C /* \\ */ && this.regexp_eatRegExpUnicodeEscapeSequence(state, forceU)) {\n ch = state.lastIntValue;\n }\n if (isRegExpIdentifierStart(ch)) {\n state.lastIntValue = ch;\n return true\n }\n\n state.pos = start;\n return false\n };\n function isRegExpIdentifierStart(ch) {\n return isIdentifierStart(ch, true) || ch === 0x24 /* $ */ || ch === 0x5F /* _ */\n }\n\n // RegExpIdentifierPart ::\n // UnicodeIDContinue\n // `$`\n // `_`\n // `\\` RegExpUnicodeEscapeSequence[+U]\n // \n // \n pp$1.regexp_eatRegExpIdentifierPart = function(state) {\n var start = state.pos;\n var forceU = this.options.ecmaVersion >= 11;\n var ch = state.current(forceU);\n state.advance(forceU);\n\n if (ch === 0x5C /* \\ */ && this.regexp_eatRegExpUnicodeEscapeSequence(state, forceU)) {\n ch = state.lastIntValue;\n }\n if (isRegExpIdentifierPart(ch)) {\n state.lastIntValue = ch;\n return true\n }\n\n state.pos = start;\n return false\n };\n function isRegExpIdentifierPart(ch) {\n return isIdentifierChar(ch, true) || ch === 0x24 /* $ */ || ch === 0x5F /* _ */ || ch === 0x200C /* */ || ch === 0x200D /* */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-AtomEscape\n pp$1.regexp_eatAtomEscape = function(state) {\n if (\n this.regexp_eatBackReference(state) ||\n this.regexp_eatCharacterClassEscape(state) ||\n this.regexp_eatCharacterEscape(state) ||\n (state.switchN && this.regexp_eatKGroupName(state))\n ) {\n return true\n }\n if (state.switchU) {\n // Make the same message as V8.\n if (state.current() === 0x63 /* c */) {\n state.raise(\"Invalid unicode escape\");\n }\n state.raise(\"Invalid escape\");\n }\n return false\n };\n pp$1.regexp_eatBackReference = function(state) {\n var start = state.pos;\n if (this.regexp_eatDecimalEscape(state)) {\n var n = state.lastIntValue;\n if (state.switchU) {\n // For SyntaxError in https://www.ecma-international.org/ecma-262/8.0/#sec-atomescape\n if (n > state.maxBackReference) {\n state.maxBackReference = n;\n }\n return true\n }\n if (n <= state.numCapturingParens) {\n return true\n }\n state.pos = start;\n }\n return false\n };\n pp$1.regexp_eatKGroupName = function(state) {\n if (state.eat(0x6B /* k */)) {\n if (this.regexp_eatGroupName(state)) {\n state.backReferenceNames.push(state.lastStringValue);\n return true\n }\n state.raise(\"Invalid named reference\");\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-CharacterEscape\n pp$1.regexp_eatCharacterEscape = function(state) {\n return (\n this.regexp_eatControlEscape(state) ||\n this.regexp_eatCControlLetter(state) ||\n this.regexp_eatZero(state) ||\n this.regexp_eatHexEscapeSequence(state) ||\n this.regexp_eatRegExpUnicodeEscapeSequence(state, false) ||\n (!state.switchU && this.regexp_eatLegacyOctalEscapeSequence(state)) ||\n this.regexp_eatIdentityEscape(state)\n )\n };\n pp$1.regexp_eatCControlLetter = function(state) {\n var start = state.pos;\n if (state.eat(0x63 /* c */)) {\n if (this.regexp_eatControlLetter(state)) {\n return true\n }\n state.pos = start;\n }\n return false\n };\n pp$1.regexp_eatZero = function(state) {\n if (state.current() === 0x30 /* 0 */ && !isDecimalDigit(state.lookahead())) {\n state.lastIntValue = 0;\n state.advance();\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ControlEscape\n pp$1.regexp_eatControlEscape = function(state) {\n var ch = state.current();\n if (ch === 0x74 /* t */) {\n state.lastIntValue = 0x09; /* \\t */\n state.advance();\n return true\n }\n if (ch === 0x6E /* n */) {\n state.lastIntValue = 0x0A; /* \\n */\n state.advance();\n return true\n }\n if (ch === 0x76 /* v */) {\n state.lastIntValue = 0x0B; /* \\v */\n state.advance();\n return true\n }\n if (ch === 0x66 /* f */) {\n state.lastIntValue = 0x0C; /* \\f */\n state.advance();\n return true\n }\n if (ch === 0x72 /* r */) {\n state.lastIntValue = 0x0D; /* \\r */\n state.advance();\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ControlLetter\n pp$1.regexp_eatControlLetter = function(state) {\n var ch = state.current();\n if (isControlLetter(ch)) {\n state.lastIntValue = ch % 0x20;\n state.advance();\n return true\n }\n return false\n };\n function isControlLetter(ch) {\n return (\n (ch >= 0x41 /* A */ && ch <= 0x5A /* Z */) ||\n (ch >= 0x61 /* a */ && ch <= 0x7A /* z */)\n )\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-RegExpUnicodeEscapeSequence\n pp$1.regexp_eatRegExpUnicodeEscapeSequence = function(state, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n var start = state.pos;\n var switchU = forceU || state.switchU;\n\n if (state.eat(0x75 /* u */)) {\n if (this.regexp_eatFixedHexDigits(state, 4)) {\n var lead = state.lastIntValue;\n if (switchU && lead >= 0xD800 && lead <= 0xDBFF) {\n var leadSurrogateEnd = state.pos;\n if (state.eat(0x5C /* \\ */) && state.eat(0x75 /* u */) && this.regexp_eatFixedHexDigits(state, 4)) {\n var trail = state.lastIntValue;\n if (trail >= 0xDC00 && trail <= 0xDFFF) {\n state.lastIntValue = (lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000;\n return true\n }\n }\n state.pos = leadSurrogateEnd;\n state.lastIntValue = lead;\n }\n return true\n }\n if (\n switchU &&\n state.eat(0x7B /* { */) &&\n this.regexp_eatHexDigits(state) &&\n state.eat(0x7D /* } */) &&\n isValidUnicode(state.lastIntValue)\n ) {\n return true\n }\n if (switchU) {\n state.raise(\"Invalid unicode escape\");\n }\n state.pos = start;\n }\n\n return false\n };\n function isValidUnicode(ch) {\n return ch >= 0 && ch <= 0x10FFFF\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-IdentityEscape\n pp$1.regexp_eatIdentityEscape = function(state) {\n if (state.switchU) {\n if (this.regexp_eatSyntaxCharacter(state)) {\n return true\n }\n if (state.eat(0x2F /* / */)) {\n state.lastIntValue = 0x2F; /* / */\n return true\n }\n return false\n }\n\n var ch = state.current();\n if (ch !== 0x63 /* c */ && (!state.switchN || ch !== 0x6B /* k */)) {\n state.lastIntValue = ch;\n state.advance();\n return true\n }\n\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-DecimalEscape\n pp$1.regexp_eatDecimalEscape = function(state) {\n state.lastIntValue = 0;\n var ch = state.current();\n if (ch >= 0x31 /* 1 */ && ch <= 0x39 /* 9 */) {\n do {\n state.lastIntValue = 10 * state.lastIntValue + (ch - 0x30 /* 0 */);\n state.advance();\n } while ((ch = state.current()) >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */)\n return true\n }\n return false\n };\n\n // Return values used by character set parsing methods, needed to\n // forbid negation of sets that can match strings.\n var CharSetNone = 0; // Nothing parsed\n var CharSetOk = 1; // Construct parsed, cannot contain strings\n var CharSetString = 2; // Construct parsed, can contain strings\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-CharacterClassEscape\n pp$1.regexp_eatCharacterClassEscape = function(state) {\n var ch = state.current();\n\n if (isCharacterClassEscape(ch)) {\n state.lastIntValue = -1;\n state.advance();\n return CharSetOk\n }\n\n var negate = false;\n if (\n state.switchU &&\n this.options.ecmaVersion >= 9 &&\n ((negate = ch === 0x50 /* P */) || ch === 0x70 /* p */)\n ) {\n state.lastIntValue = -1;\n state.advance();\n var result;\n if (\n state.eat(0x7B /* { */) &&\n (result = this.regexp_eatUnicodePropertyValueExpression(state)) &&\n state.eat(0x7D /* } */)\n ) {\n if (negate && result === CharSetString) { state.raise(\"Invalid property name\"); }\n return result\n }\n state.raise(\"Invalid property name\");\n }\n\n return CharSetNone\n };\n\n function isCharacterClassEscape(ch) {\n return (\n ch === 0x64 /* d */ ||\n ch === 0x44 /* D */ ||\n ch === 0x73 /* s */ ||\n ch === 0x53 /* S */ ||\n ch === 0x77 /* w */ ||\n ch === 0x57 /* W */\n )\n }\n\n // UnicodePropertyValueExpression ::\n // UnicodePropertyName `=` UnicodePropertyValue\n // LoneUnicodePropertyNameOrValue\n pp$1.regexp_eatUnicodePropertyValueExpression = function(state) {\n var start = state.pos;\n\n // UnicodePropertyName `=` UnicodePropertyValue\n if (this.regexp_eatUnicodePropertyName(state) && state.eat(0x3D /* = */)) {\n var name = state.lastStringValue;\n if (this.regexp_eatUnicodePropertyValue(state)) {\n var value = state.lastStringValue;\n this.regexp_validateUnicodePropertyNameAndValue(state, name, value);\n return CharSetOk\n }\n }\n state.pos = start;\n\n // LoneUnicodePropertyNameOrValue\n if (this.regexp_eatLoneUnicodePropertyNameOrValue(state)) {\n var nameOrValue = state.lastStringValue;\n return this.regexp_validateUnicodePropertyNameOrValue(state, nameOrValue)\n }\n return CharSetNone\n };\n\n pp$1.regexp_validateUnicodePropertyNameAndValue = function(state, name, value) {\n if (!hasOwn(state.unicodeProperties.nonBinary, name))\n { state.raise(\"Invalid property name\"); }\n if (!state.unicodeProperties.nonBinary[name].test(value))\n { state.raise(\"Invalid property value\"); }\n };\n\n pp$1.regexp_validateUnicodePropertyNameOrValue = function(state, nameOrValue) {\n if (state.unicodeProperties.binary.test(nameOrValue)) { return CharSetOk }\n if (state.switchV && state.unicodeProperties.binaryOfStrings.test(nameOrValue)) { return CharSetString }\n state.raise(\"Invalid property name\");\n };\n\n // UnicodePropertyName ::\n // UnicodePropertyNameCharacters\n pp$1.regexp_eatUnicodePropertyName = function(state) {\n var ch = 0;\n state.lastStringValue = \"\";\n while (isUnicodePropertyNameCharacter(ch = state.current())) {\n state.lastStringValue += codePointToString(ch);\n state.advance();\n }\n return state.lastStringValue !== \"\"\n };\n\n function isUnicodePropertyNameCharacter(ch) {\n return isControlLetter(ch) || ch === 0x5F /* _ */\n }\n\n // UnicodePropertyValue ::\n // UnicodePropertyValueCharacters\n pp$1.regexp_eatUnicodePropertyValue = function(state) {\n var ch = 0;\n state.lastStringValue = \"\";\n while (isUnicodePropertyValueCharacter(ch = state.current())) {\n state.lastStringValue += codePointToString(ch);\n state.advance();\n }\n return state.lastStringValue !== \"\"\n };\n function isUnicodePropertyValueCharacter(ch) {\n return isUnicodePropertyNameCharacter(ch) || isDecimalDigit(ch)\n }\n\n // LoneUnicodePropertyNameOrValue ::\n // UnicodePropertyValueCharacters\n pp$1.regexp_eatLoneUnicodePropertyNameOrValue = function(state) {\n return this.regexp_eatUnicodePropertyValue(state)\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-CharacterClass\n pp$1.regexp_eatCharacterClass = function(state) {\n if (state.eat(0x5B /* [ */)) {\n var negate = state.eat(0x5E /* ^ */);\n var result = this.regexp_classContents(state);\n if (!state.eat(0x5D /* ] */))\n { state.raise(\"Unterminated character class\"); }\n if (negate && result === CharSetString)\n { state.raise(\"Negated character class may contain strings\"); }\n return true\n }\n return false\n };\n\n // https://tc39.es/ecma262/#prod-ClassContents\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ClassRanges\n pp$1.regexp_classContents = function(state) {\n if (state.current() === 0x5D /* ] */) { return CharSetOk }\n if (state.switchV) { return this.regexp_classSetExpression(state) }\n this.regexp_nonEmptyClassRanges(state);\n return CharSetOk\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-NonemptyClassRanges\n // https://www.ecma-international.org/ecma-262/8.0/#prod-NonemptyClassRangesNoDash\n pp$1.regexp_nonEmptyClassRanges = function(state) {\n while (this.regexp_eatClassAtom(state)) {\n var left = state.lastIntValue;\n if (state.eat(0x2D /* - */) && this.regexp_eatClassAtom(state)) {\n var right = state.lastIntValue;\n if (state.switchU && (left === -1 || right === -1)) {\n state.raise(\"Invalid character class\");\n }\n if (left !== -1 && right !== -1 && left > right) {\n state.raise(\"Range out of order in character class\");\n }\n }\n }\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ClassAtom\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ClassAtomNoDash\n pp$1.regexp_eatClassAtom = function(state) {\n var start = state.pos;\n\n if (state.eat(0x5C /* \\ */)) {\n if (this.regexp_eatClassEscape(state)) {\n return true\n }\n if (state.switchU) {\n // Make the same message as V8.\n var ch$1 = state.current();\n if (ch$1 === 0x63 /* c */ || isOctalDigit(ch$1)) {\n state.raise(\"Invalid class escape\");\n }\n state.raise(\"Invalid escape\");\n }\n state.pos = start;\n }\n\n var ch = state.current();\n if (ch !== 0x5D /* ] */) {\n state.lastIntValue = ch;\n state.advance();\n return true\n }\n\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ClassEscape\n pp$1.regexp_eatClassEscape = function(state) {\n var start = state.pos;\n\n if (state.eat(0x62 /* b */)) {\n state.lastIntValue = 0x08; /* */\n return true\n }\n\n if (state.switchU && state.eat(0x2D /* - */)) {\n state.lastIntValue = 0x2D; /* - */\n return true\n }\n\n if (!state.switchU && state.eat(0x63 /* c */)) {\n if (this.regexp_eatClassControlLetter(state)) {\n return true\n }\n state.pos = start;\n }\n\n return (\n this.regexp_eatCharacterClassEscape(state) ||\n this.regexp_eatCharacterEscape(state)\n )\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetExpression\n // https://tc39.es/ecma262/#prod-ClassUnion\n // https://tc39.es/ecma262/#prod-ClassIntersection\n // https://tc39.es/ecma262/#prod-ClassSubtraction\n pp$1.regexp_classSetExpression = function(state) {\n var result = CharSetOk, subResult;\n if (this.regexp_eatClassSetRange(state)) ; else if (subResult = this.regexp_eatClassSetOperand(state)) {\n if (subResult === CharSetString) { result = CharSetString; }\n // https://tc39.es/ecma262/#prod-ClassIntersection\n var start = state.pos;\n while (state.eatChars([0x26, 0x26] /* && */)) {\n if (\n state.current() !== 0x26 /* & */ &&\n (subResult = this.regexp_eatClassSetOperand(state))\n ) {\n if (subResult !== CharSetString) { result = CharSetOk; }\n continue\n }\n state.raise(\"Invalid character in character class\");\n }\n if (start !== state.pos) { return result }\n // https://tc39.es/ecma262/#prod-ClassSubtraction\n while (state.eatChars([0x2D, 0x2D] /* -- */)) {\n if (this.regexp_eatClassSetOperand(state)) { continue }\n state.raise(\"Invalid character in character class\");\n }\n if (start !== state.pos) { return result }\n } else {\n state.raise(\"Invalid character in character class\");\n }\n // https://tc39.es/ecma262/#prod-ClassUnion\n for (;;) {\n if (this.regexp_eatClassSetRange(state)) { continue }\n subResult = this.regexp_eatClassSetOperand(state);\n if (!subResult) { return result }\n if (subResult === CharSetString) { result = CharSetString; }\n }\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetRange\n pp$1.regexp_eatClassSetRange = function(state) {\n var start = state.pos;\n if (this.regexp_eatClassSetCharacter(state)) {\n var left = state.lastIntValue;\n if (state.eat(0x2D /* - */) && this.regexp_eatClassSetCharacter(state)) {\n var right = state.lastIntValue;\n if (left !== -1 && right !== -1 && left > right) {\n state.raise(\"Range out of order in character class\");\n }\n return true\n }\n state.pos = start;\n }\n return false\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetOperand\n pp$1.regexp_eatClassSetOperand = function(state) {\n if (this.regexp_eatClassSetCharacter(state)) { return CharSetOk }\n return this.regexp_eatClassStringDisjunction(state) || this.regexp_eatNestedClass(state)\n };\n\n // https://tc39.es/ecma262/#prod-NestedClass\n pp$1.regexp_eatNestedClass = function(state) {\n var start = state.pos;\n if (state.eat(0x5B /* [ */)) {\n var negate = state.eat(0x5E /* ^ */);\n var result = this.regexp_classContents(state);\n if (state.eat(0x5D /* ] */)) {\n if (negate && result === CharSetString) {\n state.raise(\"Negated character class may contain strings\");\n }\n return result\n }\n state.pos = start;\n }\n if (state.eat(0x5C /* \\ */)) {\n var result$1 = this.regexp_eatCharacterClassEscape(state);\n if (result$1) {\n return result$1\n }\n state.pos = start;\n }\n return null\n };\n\n // https://tc39.es/ecma262/#prod-ClassStringDisjunction\n pp$1.regexp_eatClassStringDisjunction = function(state) {\n var start = state.pos;\n if (state.eatChars([0x5C, 0x71] /* \\q */)) {\n if (state.eat(0x7B /* { */)) {\n var result = this.regexp_classStringDisjunctionContents(state);\n if (state.eat(0x7D /* } */)) {\n return result\n }\n } else {\n // Make the same message as V8.\n state.raise(\"Invalid escape\");\n }\n state.pos = start;\n }\n return null\n };\n\n // https://tc39.es/ecma262/#prod-ClassStringDisjunctionContents\n pp$1.regexp_classStringDisjunctionContents = function(state) {\n var result = this.regexp_classString(state);\n while (state.eat(0x7C /* | */)) {\n if (this.regexp_classString(state) === CharSetString) { result = CharSetString; }\n }\n return result\n };\n\n // https://tc39.es/ecma262/#prod-ClassString\n // https://tc39.es/ecma262/#prod-NonEmptyClassString\n pp$1.regexp_classString = function(state) {\n var count = 0;\n while (this.regexp_eatClassSetCharacter(state)) { count++; }\n return count === 1 ? CharSetOk : CharSetString\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetCharacter\n pp$1.regexp_eatClassSetCharacter = function(state) {\n var start = state.pos;\n if (state.eat(0x5C /* \\ */)) {\n if (\n this.regexp_eatCharacterEscape(state) ||\n this.regexp_eatClassSetReservedPunctuator(state)\n ) {\n return true\n }\n if (state.eat(0x62 /* b */)) {\n state.lastIntValue = 0x08; /* */\n return true\n }\n state.pos = start;\n return false\n }\n var ch = state.current();\n if (ch < 0 || ch === state.lookahead() && isClassSetReservedDoublePunctuatorCharacter(ch)) { return false }\n if (isClassSetSyntaxCharacter(ch)) { return false }\n state.advance();\n state.lastIntValue = ch;\n return true\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetReservedDoublePunctuator\n function isClassSetReservedDoublePunctuatorCharacter(ch) {\n return (\n ch === 0x21 /* ! */ ||\n ch >= 0x23 /* # */ && ch <= 0x26 /* & */ ||\n ch >= 0x2A /* * */ && ch <= 0x2C /* , */ ||\n ch === 0x2E /* . */ ||\n ch >= 0x3A /* : */ && ch <= 0x40 /* @ */ ||\n ch === 0x5E /* ^ */ ||\n ch === 0x60 /* ` */ ||\n ch === 0x7E /* ~ */\n )\n }\n\n // https://tc39.es/ecma262/#prod-ClassSetSyntaxCharacter\n function isClassSetSyntaxCharacter(ch) {\n return (\n ch === 0x28 /* ( */ ||\n ch === 0x29 /* ) */ ||\n ch === 0x2D /* - */ ||\n ch === 0x2F /* / */ ||\n ch >= 0x5B /* [ */ && ch <= 0x5D /* ] */ ||\n ch >= 0x7B /* { */ && ch <= 0x7D /* } */\n )\n }\n\n // https://tc39.es/ecma262/#prod-ClassSetReservedPunctuator\n pp$1.regexp_eatClassSetReservedPunctuator = function(state) {\n var ch = state.current();\n if (isClassSetReservedPunctuator(ch)) {\n state.lastIntValue = ch;\n state.advance();\n return true\n }\n return false\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetReservedPunctuator\n function isClassSetReservedPunctuator(ch) {\n return (\n ch === 0x21 /* ! */ ||\n ch === 0x23 /* # */ ||\n ch === 0x25 /* % */ ||\n ch === 0x26 /* & */ ||\n ch === 0x2C /* , */ ||\n ch === 0x2D /* - */ ||\n ch >= 0x3A /* : */ && ch <= 0x3E /* > */ ||\n ch === 0x40 /* @ */ ||\n ch === 0x60 /* ` */ ||\n ch === 0x7E /* ~ */\n )\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ClassControlLetter\n pp$1.regexp_eatClassControlLetter = function(state) {\n var ch = state.current();\n if (isDecimalDigit(ch) || ch === 0x5F /* _ */) {\n state.lastIntValue = ch % 0x20;\n state.advance();\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-HexEscapeSequence\n pp$1.regexp_eatHexEscapeSequence = function(state) {\n var start = state.pos;\n if (state.eat(0x78 /* x */)) {\n if (this.regexp_eatFixedHexDigits(state, 2)) {\n return true\n }\n if (state.switchU) {\n state.raise(\"Invalid escape\");\n }\n state.pos = start;\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-DecimalDigits\n pp$1.regexp_eatDecimalDigits = function(state) {\n var start = state.pos;\n var ch = 0;\n state.lastIntValue = 0;\n while (isDecimalDigit(ch = state.current())) {\n state.lastIntValue = 10 * state.lastIntValue + (ch - 0x30 /* 0 */);\n state.advance();\n }\n return state.pos !== start\n };\n function isDecimalDigit(ch) {\n return ch >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-HexDigits\n pp$1.regexp_eatHexDigits = function(state) {\n var start = state.pos;\n var ch = 0;\n state.lastIntValue = 0;\n while (isHexDigit(ch = state.current())) {\n state.lastIntValue = 16 * state.lastIntValue + hexToInt(ch);\n state.advance();\n }\n return state.pos !== start\n };\n function isHexDigit(ch) {\n return (\n (ch >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */) ||\n (ch >= 0x41 /* A */ && ch <= 0x46 /* F */) ||\n (ch >= 0x61 /* a */ && ch <= 0x66 /* f */)\n )\n }\n function hexToInt(ch) {\n if (ch >= 0x41 /* A */ && ch <= 0x46 /* F */) {\n return 10 + (ch - 0x41 /* A */)\n }\n if (ch >= 0x61 /* a */ && ch <= 0x66 /* f */) {\n return 10 + (ch - 0x61 /* a */)\n }\n return ch - 0x30 /* 0 */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-LegacyOctalEscapeSequence\n // Allows only 0-377(octal) i.e. 0-255(decimal).\n pp$1.regexp_eatLegacyOctalEscapeSequence = function(state) {\n if (this.regexp_eatOctalDigit(state)) {\n var n1 = state.lastIntValue;\n if (this.regexp_eatOctalDigit(state)) {\n var n2 = state.lastIntValue;\n if (n1 <= 3 && this.regexp_eatOctalDigit(state)) {\n state.lastIntValue = n1 * 64 + n2 * 8 + state.lastIntValue;\n } else {\n state.lastIntValue = n1 * 8 + n2;\n }\n } else {\n state.lastIntValue = n1;\n }\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-OctalDigit\n pp$1.regexp_eatOctalDigit = function(state) {\n var ch = state.current();\n if (isOctalDigit(ch)) {\n state.lastIntValue = ch - 0x30; /* 0 */\n state.advance();\n return true\n }\n state.lastIntValue = 0;\n return false\n };\n function isOctalDigit(ch) {\n return ch >= 0x30 /* 0 */ && ch <= 0x37 /* 7 */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Hex4Digits\n // https://www.ecma-international.org/ecma-262/8.0/#prod-HexDigit\n // And HexDigit HexDigit in https://www.ecma-international.org/ecma-262/8.0/#prod-HexEscapeSequence\n pp$1.regexp_eatFixedHexDigits = function(state, length) {\n var start = state.pos;\n state.lastIntValue = 0;\n for (var i = 0; i < length; ++i) {\n var ch = state.current();\n if (!isHexDigit(ch)) {\n state.pos = start;\n return false\n }\n state.lastIntValue = 16 * state.lastIntValue + hexToInt(ch);\n state.advance();\n }\n return true\n };\n\n // Object type used to represent tokens. Note that normally, tokens\n // simply exist as properties on the parser object. This is only\n // used for the onToken callback and the external tokenizer.\n\n var Token = function Token(p) {\n this.type = p.type;\n this.value = p.value;\n this.start = p.start;\n this.end = p.end;\n if (p.options.locations)\n { this.loc = new SourceLocation(p, p.startLoc, p.endLoc); }\n if (p.options.ranges)\n { this.range = [p.start, p.end]; }\n };\n\n // ## Tokenizer\n\n var pp = Parser.prototype;\n\n // Move to the next token\n\n pp.next = function(ignoreEscapeSequenceInKeyword) {\n if (!ignoreEscapeSequenceInKeyword && this.type.keyword && this.containsEsc)\n { this.raiseRecoverable(this.start, \"Escape sequence in keyword \" + this.type.keyword); }\n if (this.options.onToken)\n { this.options.onToken(new Token(this)); }\n\n this.lastTokEnd = this.end;\n this.lastTokStart = this.start;\n this.lastTokEndLoc = this.endLoc;\n this.lastTokStartLoc = this.startLoc;\n this.nextToken();\n };\n\n pp.getToken = function() {\n this.next();\n return new Token(this)\n };\n\n // If we're in an ES6 environment, make parsers iterable\n if (typeof Symbol !== \"undefined\")\n { pp[Symbol.iterator] = function() {\n var this$1$1 = this;\n\n return {\n next: function () {\n var token = this$1$1.getToken();\n return {\n done: token.type === types$1.eof,\n value: token\n }\n }\n }\n }; }\n\n // Toggle strict mode. Re-reads the next number or string to please\n // pedantic tests (`\"use strict\"; 010;` should fail).\n\n // Read a single token, updating the parser object's token-related\n // properties.\n\n pp.nextToken = function() {\n var curContext = this.curContext();\n if (!curContext || !curContext.preserveSpace) { this.skipSpace(); }\n\n this.start = this.pos;\n if (this.options.locations) { this.startLoc = this.curPosition(); }\n if (this.pos >= this.input.length) { return this.finishToken(types$1.eof) }\n\n if (curContext.override) { return curContext.override(this) }\n else { this.readToken(this.fullCharCodeAtPos()); }\n };\n\n pp.readToken = function(code) {\n // Identifier or keyword. '\\uXXXX' sequences are allowed in\n // identifiers, so '\\' also dispatches to that.\n if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\\' */)\n { return this.readWord() }\n\n return this.getTokenFromCode(code)\n };\n\n pp.fullCharCodeAt = function(pos) {\n var code = this.input.charCodeAt(pos);\n if (code <= 0xd7ff || code >= 0xdc00) { return code }\n var next = this.input.charCodeAt(pos + 1);\n return next <= 0xdbff || next >= 0xe000 ? code : (code << 10) + next - 0x35fdc00\n };\n\n pp.fullCharCodeAtPos = function() {\n return this.fullCharCodeAt(this.pos)\n };\n\n pp.skipBlockComment = function() {\n var startLoc = this.options.onComment && this.curPosition();\n var start = this.pos, end = this.input.indexOf(\"*/\", this.pos += 2);\n if (end === -1) { this.raise(this.pos - 2, \"Unterminated comment\"); }\n this.pos = end + 2;\n if (this.options.locations) {\n for (var nextBreak = (void 0), pos = start; (nextBreak = nextLineBreak(this.input, pos, this.pos)) > -1;) {\n ++this.curLine;\n pos = this.lineStart = nextBreak;\n }\n }\n if (this.options.onComment)\n { this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos,\n startLoc, this.curPosition()); }\n };\n\n pp.skipLineComment = function(startSkip) {\n var start = this.pos;\n var startLoc = this.options.onComment && this.curPosition();\n var ch = this.input.charCodeAt(this.pos += startSkip);\n while (this.pos < this.input.length && !isNewLine(ch)) {\n ch = this.input.charCodeAt(++this.pos);\n }\n if (this.options.onComment)\n { this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos,\n startLoc, this.curPosition()); }\n };\n\n // Called at the start of the parse and after every token. Skips\n // whitespace and comments, and.\n\n pp.skipSpace = function() {\n loop: while (this.pos < this.input.length) {\n var ch = this.input.charCodeAt(this.pos);\n switch (ch) {\n case 32: case 160: // ' '\n ++this.pos;\n break\n case 13:\n if (this.input.charCodeAt(this.pos + 1) === 10) {\n ++this.pos;\n }\n case 10: case 8232: case 8233:\n ++this.pos;\n if (this.options.locations) {\n ++this.curLine;\n this.lineStart = this.pos;\n }\n break\n case 47: // '/'\n switch (this.input.charCodeAt(this.pos + 1)) {\n case 42: // '*'\n this.skipBlockComment();\n break\n case 47:\n this.skipLineComment(2);\n break\n default:\n break loop\n }\n break\n default:\n if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {\n ++this.pos;\n } else {\n break loop\n }\n }\n }\n };\n\n // Called at the end of every token. Sets `end`, `val`, and\n // maintains `context` and `exprAllowed`, and skips the space after\n // the token, so that the next one's `start` will point at the\n // right position.\n\n pp.finishToken = function(type, val) {\n this.end = this.pos;\n if (this.options.locations) { this.endLoc = this.curPosition(); }\n var prevType = this.type;\n this.type = type;\n this.value = val;\n\n this.updateContext(prevType);\n };\n\n // ### Token reading\n\n // This is the function that is called to fetch the next token. It\n // is somewhat obscure, because it works in character codes rather\n // than characters, and because operator parsing has been inlined\n // into it.\n //\n // All in the name of speed.\n //\n pp.readToken_dot = function() {\n var next = this.input.charCodeAt(this.pos + 1);\n if (next >= 48 && next <= 57) { return this.readNumber(true) }\n var next2 = this.input.charCodeAt(this.pos + 2);\n if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.'\n this.pos += 3;\n return this.finishToken(types$1.ellipsis)\n } else {\n ++this.pos;\n return this.finishToken(types$1.dot)\n }\n };\n\n pp.readToken_slash = function() { // '/'\n var next = this.input.charCodeAt(this.pos + 1);\n if (this.exprAllowed) { ++this.pos; return this.readRegexp() }\n if (next === 61) { return this.finishOp(types$1.assign, 2) }\n return this.finishOp(types$1.slash, 1)\n };\n\n pp.readToken_mult_modulo_exp = function(code) { // '%*'\n var next = this.input.charCodeAt(this.pos + 1);\n var size = 1;\n var tokentype = code === 42 ? types$1.star : types$1.modulo;\n\n // exponentiation operator ** and **=\n if (this.options.ecmaVersion >= 7 && code === 42 && next === 42) {\n ++size;\n tokentype = types$1.starstar;\n next = this.input.charCodeAt(this.pos + 2);\n }\n\n if (next === 61) { return this.finishOp(types$1.assign, size + 1) }\n return this.finishOp(tokentype, size)\n };\n\n pp.readToken_pipe_amp = function(code) { // '|&'\n var next = this.input.charCodeAt(this.pos + 1);\n if (next === code) {\n if (this.options.ecmaVersion >= 12) {\n var next2 = this.input.charCodeAt(this.pos + 2);\n if (next2 === 61) { return this.finishOp(types$1.assign, 3) }\n }\n return this.finishOp(code === 124 ? types$1.logicalOR : types$1.logicalAND, 2)\n }\n if (next === 61) { return this.finishOp(types$1.assign, 2) }\n return this.finishOp(code === 124 ? types$1.bitwiseOR : types$1.bitwiseAND, 1)\n };\n\n pp.readToken_caret = function() { // '^'\n var next = this.input.charCodeAt(this.pos + 1);\n if (next === 61) { return this.finishOp(types$1.assign, 2) }\n return this.finishOp(types$1.bitwiseXOR, 1)\n };\n\n pp.readToken_plus_min = function(code) { // '+-'\n var next = this.input.charCodeAt(this.pos + 1);\n if (next === code) {\n if (next === 45 && !this.inModule && this.input.charCodeAt(this.pos + 2) === 62 &&\n (this.lastTokEnd === 0 || lineBreak.test(this.input.slice(this.lastTokEnd, this.pos)))) {\n // A `-->` line comment\n this.skipLineComment(3);\n this.skipSpace();\n return this.nextToken()\n }\n return this.finishOp(types$1.incDec, 2)\n }\n if (next === 61) { return this.finishOp(types$1.assign, 2) }\n return this.finishOp(types$1.plusMin, 1)\n };\n\n pp.readToken_lt_gt = function(code) { // '<>'\n var next = this.input.charCodeAt(this.pos + 1);\n var size = 1;\n if (next === code) {\n size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2;\n if (this.input.charCodeAt(this.pos + size) === 61) { return this.finishOp(types$1.assign, size + 1) }\n return this.finishOp(types$1.bitShift, size)\n }\n if (next === 33 && code === 60 && !this.inModule && this.input.charCodeAt(this.pos + 2) === 45 &&\n this.input.charCodeAt(this.pos + 3) === 45) {\n // `\n'); +}; + +process.on('uncaughtException', (e) => { + Error.prepareStackTrace = origPrepareStackTrace; + throw e; +}); + +const tests = [ + { + // test .load for a file that throws + command: `.load ${fixtures.path('repl-pretty-stack.js')}`, + expected: 'Uncaught Error: Whoops!--->\nREPL1:*:*--->\nd (REPL1:*:*)' + + '--->\nc (REPL1:*:*)--->\nb (REPL1:*:*)--->\na (REPL1:*:*)\n' + }, + { + command: 'let x y;', + expected: /let x y;\n {6}\^\n\nUncaught SyntaxError: Unexpected identifier.*\n/ + }, + { + command: 'throw new Error(\'Whoops!\')', + expected: 'Uncaught Error: Whoops!\n' + }, + { + command: 'foo = bar;', + expected: 'Uncaught ReferenceError: bar is not defined\n' + }, + // test anonymous IIFE + { + command: '(function() { throw new Error(\'Whoops!\'); })()', + expected: 'Uncaught Error: Whoops!--->\nREPL5:*:*\n' + }, +]; + +tests.forEach(run); + +// Verify that the stack can be generated when Error.prepareStackTrace is deleted. +delete Error.prepareStackTrace; +run({ + command: 'throw new TypeError(\'Whoops!\')', + expected: 'Uncaught TypeError: Whoops!\n' +}); diff --git a/test/js/node/test/parallel/test-repl-pretty-stack-custom-writer.js b/test/js/node/test/parallel/test-repl-pretty-stack-custom-writer.js new file mode 100644 index 000000000000..e31460dbc93e --- /dev/null +++ b/test/js/node/test/parallel/test-repl-pretty-stack-custom-writer.js @@ -0,0 +1,15 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +const testingReplPrompt = '_REPL_TESTING_PROMPT_>'; + +const { replServer, output } = startNewREPLServer({ prompt: testingReplPrompt }); + +replServer.write('throw new Error("foo[a]")\n'); + +assert.strictEqual( + output.accumulator.split('\n').filter((line) => !line.includes(testingReplPrompt)).join(''), + 'Uncaught Error: foo[a]' +); diff --git a/test/js/node/test/parallel/test-repl-pretty-stack.js b/test/js/node/test/parallel/test-repl-pretty-stack.js new file mode 100644 index 000000000000..b2f9cc82c08d --- /dev/null +++ b/test/js/node/test/parallel/test-repl-pretty-stack.js @@ -0,0 +1,70 @@ +'use strict'; +require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +const stackRegExp = /(at .*REPL\d+:)[0-9]+:[0-9]+/g; + +function run({ command, expected, ...extraREPLOptions }, i) { + const { replServer, output } = startNewREPLServer({ + terminal: false, + useColors: false, + ...extraREPLOptions + }); + + replServer.write(`${command}\n`); + if (typeof expected === 'string') { + assert.strictEqual( + output.accumulator.replace(stackRegExp, '$1*:*'), + expected.replace(stackRegExp, '$1*:*') + ); + } else { + assert.match( + output.accumulator.replace(stackRegExp, '$1*:*'), + expected + ); + } + replServer.close(); +} + +const tests = [ + { + // Test .load for a file that throws. + command: `.load ${fixtures.path('repl-pretty-stack.js')}`, + expected: 'Uncaught Error: Whoops!\n at REPL1:*:*\n' + + ' at d (REPL1:*:*)\n at c (REPL1:*:*)\n' + + ' at b (REPL1:*:*)\n at a (REPL1:*:*)\n' + }, + { + command: 'let x y;', + expected: /^let x y;\n {6}\^\n\nUncaught SyntaxError: Unexpected identifier.*\n/ + }, + { + command: 'throw new Error(\'Whoops!\')', + expected: 'Uncaught Error: Whoops!\n' + }, + { + command: '(() => { const err = Error(\'Whoops!\'); ' + + 'err.foo = \'bar\'; throw err; })()', + expected: "Uncaught Error: Whoops!\n at REPL4:*:* {\n foo: 'bar'\n}\n", + }, + { + command: '(() => { const err = Error(\'Whoops!\'); ' + + 'err.foo = \'bar\'; throw err; })()', + expected: 'Uncaught Error: Whoops!\n at REPL5:*:* {\n foo: ' + + "\u001b[32m'bar'\u001b[39m\n}\n", + useColors: true + }, + { + command: 'foo = bar;', + expected: 'Uncaught ReferenceError: bar is not defined\n' + }, + // Test anonymous IIFE. + { + command: '(function() { throw new Error(\'Whoops!\'); })()', + expected: 'Uncaught Error: Whoops!\n at REPL7:*:*\n' + }, +]; + +tests.forEach(run); diff --git a/test/js/node/test/parallel/test-repl-preview-newlines.js b/test/js/node/test/parallel/test-repl-preview-newlines.js new file mode 100644 index 000000000000..34a944beb538 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-preview-newlines.js @@ -0,0 +1,19 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +common.skipIfInspectorDisabled(); + +const { input, output } = startNewREPLServer({ useColors: true }); + +output.accumulator = ''; + +for (const char of ['\\n', '\\v', '\\r']) { + input.emit('data', `"${char}"()`); + // Make sure the output is on a single line + assert.strictEqual(output.accumulator, `"${char}"()\n\x1B[90mTypeError: "\x1B[39m\x1B[7G\x1B[1A`); + input.run(['']); + output.accumulator = ''; +} diff --git a/test/js/node/test/parallel/test-repl-preview-timeout.js b/test/js/node/test/parallel/test-repl-preview-timeout.js new file mode 100644 index 000000000000..cf2f244c8147 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-preview-timeout.js @@ -0,0 +1,17 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +common.skipIfInspectorDisabled(); + +const { output, input } = startNewREPLServer(); + +output.accumulator = ''; + +// Input without '\n' triggering actual run. +const inputStr = 'while (true) {}'; +input.emit('data', inputStr); +// No preview available when timed out. +assert.strictEqual(output.accumulator, inputStr); diff --git a/test/js/node/test/parallel/test-repl-preview.js b/test/js/node/test/parallel/test-repl-preview.js new file mode 100644 index 000000000000..9ab84b5c9f3a --- /dev/null +++ b/test/js/node/test/parallel/test-repl-preview.js @@ -0,0 +1,272 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); +const { REPLServer } = require('repl'); +const { Stream } = require('stream'); +const { inspect } = require('util'); + +common.skipIfInspectorDisabled(); + +// Ignore terminal settings. This is so the test can be run intact if TERM=dumb. +process.env.TERM = ''; +const PROMPT = 'repl > '; + +class REPLStream extends Stream { + readable = true; + writable = true; + + constructor() { + super(); + this.lines = ['']; + } + run(data) { + for (const entry of data) { + this.emit('data', entry); + } + this.emit('data', '\n'); + } + write(chunk) { + const chunkLines = chunk.toString('utf8').split('\n'); + this.lines[this.lines.length - 1] += chunkLines[0]; + if (chunkLines.length > 1) { + this.lines.push(...chunkLines.slice(1)); + } + this.emit('line', this.lines[this.lines.length - 1]); + return true; + } + async wait() { + this.lines = ['']; + for await (const [line] of events.on(this, 'line')) { + if (line.includes(PROMPT)) { + return this.lines; + } + } + } + pause() {} + resume() {} +} + +function runAndWait(cmds, repl) { + const promise = repl.inputStream.wait(); + for (const cmd of cmds) { + repl.inputStream.run(cmd); + } + return promise; +} + +async function tests(options) { + const repl = new REPLServer({ + prompt: PROMPT, + stream: new REPLStream(), + ignoreUndefined: true, + useColors: true, + ...options + }); + + repl.inputStream.run([ + 'function foo(x) { return x; }', + 'function koo() { console.log("abc"); }', + 'a = undefined;', + ]); + + const testCases = [{ + input: 'foo', + noPreview: '[Function: foo]', + preview: [ + 'foo', + '\x1B[90m[Function: foo]\x1B[39m\x1B[11G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[36m[Function: foo]\x1B[39m', + ] + }, { + input: 'koo', + noPreview: '[Function: koo]', + preview: [ + 'k\x1B[90moo\x1B[39m\x1B[9G', + '\x1B[90m[Function: koo]\x1B[39m\x1B[9G\x1B[1A\x1B[1B\x1B[2K\x1B[1A' + + '\x1B[0Ko\x1B[90mo\x1B[39m\x1B[10G', + '\x1B[90m[Function: koo]\x1B[39m\x1B[10G\x1B[1A\x1B[1B\x1B[2K\x1B[1A' + + '\x1B[0Ko', + '\x1B[90m[Function: koo]\x1B[39m\x1B[11G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[36m[Function: koo]\x1B[39m', + ] + }, { + input: 'a', + noPreview: 'repl > ', // No "undefined" output. + preview: ['a\r'] // No "undefined" preview. + }, { + input: " { b: 1 }['b'] === 1", + noPreview: '\x1B[33mtrue\x1B[39m', + preview: [ + " { b: 1 }['b']", + '\x1B[90m1\x1B[39m\x1B[22G\x1B[1A\x1B[1B\x1B[2K\x1B[1A ', + '\x1B[90m1\x1B[39m\x1B[23G\x1B[1A\x1B[1B\x1B[2K\x1B[1A=== 1', + '\x1B[90mtrue\x1B[39m\x1B[28G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33mtrue\x1B[39m', + ] + }, { + input: "{ b: 1 }['b'] === 1;", + noPreview: '\x1B[33mfalse\x1B[39m', + preview: [ + "{ b: 1 }['b']", + '\x1B[90m1\x1B[39m\x1B[21G\x1B[1A\x1B[1B\x1B[2K\x1B[1A ', + '\x1B[90m1\x1B[39m\x1B[22G\x1B[1A\x1B[1B\x1B[2K\x1B[1A=== 1', + '\x1B[90mtrue\x1B[39m\x1B[27G\x1B[1A\x1B[1B\x1B[2K\x1B[1A;', + '\x1B[90mfalse\x1B[39m\x1B[28G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33mfalse\x1B[39m', + ] + }, { + input: '{ a: true }', + noPreview: '{ a: \x1B[33mtrue\x1B[39m }', + preview: [ + '{ a: tru\x1B[90me\x1B[39m\x1B[16G\x1B[0Ke }\r', + '{ a: \x1B[33mtrue\x1B[39m }', + ] + }, { + input: '{ a: true };', + noPreview: '\x1B[33mtrue\x1B[39m', + preview: [ + '{ a: tru\x1B[90me\x1B[39m\x1B[16G\x1B[0Ke };', + '\x1B[90mtrue\x1B[39m\x1B[20G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33mtrue\x1B[39m', + ] + }, { + input: ' \t { a: true};', + noPreview: '\x1B[33mtrue\x1B[39m', + preview: [ + ' { a: tru\x1B[90me\x1B[39m\x1B[18G\x1B[0Ke}', + '\x1B[90m{ a: true }\x1B[39m\x1B[20G\x1B[1A\x1B[1B\x1B[2K\x1B[1A;', + '\x1B[90mtrue\x1B[39m\x1B[21G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33mtrue\x1B[39m', + ] + }, { + input: '1n + 2n', + noPreview: '\x1B[33m3n\x1B[39m', + preview: [ + '1n + 2', + '\x1B[90mType[39m\x1B[14G\x1B[1A\x1B[1B\x1B[2K\x1B[1An', + '\x1B[90m3n\x1B[39m\x1B[15G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33m3n\x1B[39m', + ] + }, { + input: '{};1', + noPreview: '\x1B[33m1\x1B[39m', + preview: [ + '{};1', + '\x1B[90m1\x1B[39m\x1B[12G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33m1\x1B[39m', + ] + }, { + input: 'aaaa', + noPreview: 'Uncaught ReferenceError: aaaa is not defined', + preview: [ + 'aaaa\r', + 'Uncaught ReferenceError: aaaa is not defined', + ] + }, { + input: '/0', + noPreview: '/0', + preview: [ + '/0\r', + '/0', + '^', + '', + 'Uncaught SyntaxError: Invalid regular expression: missing /', + ] + }, { + input: '{})', + noPreview: '{})', + preview: [ + '{})\r', + '{})', + ' ^', + '', + "Uncaught SyntaxError: Unexpected token ')'", + ], + }, { + input: "{ a: '{' }", + noPreview: "{ a: \x1B[32m'{'\x1B[39m }", + preview: [ + "{ a: '{' }\r", + "{ a: \x1B[32m'{'\x1B[39m }", + ], + }, { + input: "{'{':0}", + noPreview: "{ \x1B[32m'{'\x1B[39m: \x1B[33m0\x1B[39m }", + preview: [ + "{'{':0}", + "\x1B[90m{ '{': 0 }\x1B[39m\x1B[15G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r", + "{ \x1B[32m'{'\x1B[39m: \x1B[33m0\x1B[39m }", + ], + }, { + input: '{[Symbol.for("{")]: 0 }', + noPreview: '{ \x1B[32mSymbol({)\x1B[39m: \x1B[33m0\x1B[39m }', + preview: [ + '{[Symbol.for("{")]: 0 }\r', + '{ \x1B[32mSymbol({)\x1B[39m: \x1B[33m0\x1B[39m }', + ], + }, { + input: '{},{}', + noPreview: '{}', + preview: [ + '{},{}', + '\x1B[90m{}\x1B[39m\x1B[13G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '{}', + ], + }, { + input: '{} //', + noPreview: 'repl > ', + preview: [ + '{} //\r', + ], + }, { + input: '{} //;', + noPreview: 'repl > ', + preview: [ + '{} //;\r', + ], + }, { + input: '{throw 0}', + noPreview: 'Uncaught \x1B[33m0\x1B[39m', + preview: [ + '{throw 0}', + '\x1B[90m0\x1B[39m\x1B[17G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + 'Uncaught \x1B[33m0\x1B[39m', + ], + }]; + + const hasPreview = repl.terminal && + (options.preview !== undefined ? !!options.preview : true); + + for (const { input, noPreview, preview } of testCases) { + console.log(`Testing ${input}`); + + const toBeRun = input.split('\n'); + let lines = await runAndWait(toBeRun, repl); + + if (hasPreview) { + // Remove error messages. That allows the code to run in different + // engines. + // eslint-disable-next-line no-control-regex + lines = lines.map((line) => line.replace(/Error: .+?\x1B/, '')); + assert.strictEqual(lines.pop(), '\x1B[1G\x1B[0Jrepl > \x1B[8G'); + assert.deepStrictEqual(lines, preview); + } else { + assert.ok(lines[0].includes(noPreview), lines.map(inspect)); + if (preview.length !== 1 || preview[0] !== `${input}\r`) { + if (preview[preview.length - 1].includes('Uncaught SyntaxError')) { + assert.strictEqual(lines.length, 5); + } else { + assert.strictEqual(lines.length, 2); + } + } + } + } +} + +tests({ terminal: false }); // No preview +tests({ terminal: true }); // Preview +tests({ terminal: false, preview: false }); // No preview +tests({ terminal: false, preview: true }); // No preview +tests({ terminal: true, preview: true }); // Preview diff --git a/test/js/node/test/parallel/test-repl-programmatic-history-setup-history.js b/test/js/node/test/parallel/test-repl-programmatic-history-setup-history.js new file mode 100644 index 000000000000..544f3994ef33 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-programmatic-history-setup-history.js @@ -0,0 +1,279 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const stream = require('stream'); +const REPL = require('repl'); +const assert = require('assert'); +const fs = require('fs'); +const os = require('os'); + +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Mock os.homedir() +os.homedir = function() { + return tmpdir.path; +}; + +// Create an input stream specialized for testing an array of actions +class ActionStream extends stream.Stream { + run(data) { + const _iter = data[Symbol.iterator](); + const doAction = () => { + const next = _iter.next(); + if (next.done) { + // Close the repl. Note that it must have a clean prompt to do so. + setImmediate(() => { + this.emit('keypress', '', { ctrl: true, name: 'd' }); + }); + return; + } + const action = next.value; + + if (typeof action === 'object') { + this.emit('keypress', '', action); + } else { + this.emit('data', action); + } + setImmediate(doAction); + }; + doAction(); + } + resume() {} + pause() {} +} +ActionStream.prototype.readable = true; + + +// Mock keys +const UP = { name: 'up' }; +const DOWN = { name: 'down' }; +const ENTER = { name: 'enter' }; +const CLEAR = { ctrl: true, name: 'u' }; + +// File paths +const historyFixturePath = fixtures.path('.node_repl_history'); +const historyPath = tmpdir.resolve('.fixture_copy_repl_history'); +const historyPathFail = fixtures.path('nonexistent_folder', 'filename'); +const defaultHistoryPath = tmpdir.resolve('.node_repl_history'); +const emptyHiddenHistoryPath = fixtures.path('.empty-hidden-repl-history-file'); +const devNullHistoryPath = tmpdir.resolve('.dev-null-repl-history-file'); +// Common message bits +const prompt = '> '; +const replDisabled = '\nPersistent history support disabled. Set the ' + + 'NODE_REPL_HISTORY environment\nvariable to a valid, ' + + 'user-writable path to enable.\n'; +const homedirErr = '\nError: Could not get the home directory.\n' + + 'REPL session history will not be persisted.\n'; +const replFailedRead = '\nError: Could not open history file.\n' + + 'REPL session history will not be persisted.\n'; + +const tests = [ + // Makes sure that, if the history file is empty, the history is disabled + { + env: { NODE_REPL_HISTORY: '' }, + test: [UP], + expected: [prompt, replDisabled, prompt] + }, + // Makes sure that, if the history file is empty (when trimmed), the history is disabled + { + env: { NODE_REPL_HISTORY: ' ' }, + test: [UP], + expected: [prompt, replDisabled, prompt] + }, + // Properly loads the history file + { + env: { NODE_REPL_HISTORY: historyPath }, + test: [UP, CLEAR], + expected: [prompt, `${prompt}'you look fabulous today'`, prompt] + }, + // Properly navigates newly added history items + { + env: {}, + test: [UP, '21', ENTER, "'42'", ENTER], + expected: [ + prompt, + '2', '1', '21\n', prompt, + "'", '4', '2', "'", "'42'\n", prompt, + ], + clean: false + }, + { // Requires the above test case, because navigating old history + env: {}, + test: [UP, UP, UP, DOWN, ENTER], + expected: [ + prompt, + `${prompt}'42'`, + `${prompt}21`, + prompt, + `${prompt}21`, + '21\n', + prompt, + ] + }, + // Making sure that only the configured number of history items are kept + { + env: { NODE_REPL_HISTORY: historyPath, + NODE_REPL_HISTORY_SIZE: 1 }, + test: [UP, UP, DOWN, CLEAR], + expected: [ + prompt, + `${prompt}'you look fabulous today'`, + prompt, + `${prompt}'you look fabulous today'`, + prompt, + ] + }, + // Making sure that the history file is not written to if it is not writable + { + env: { NODE_REPL_HISTORY: historyPathFail, + NODE_REPL_HISTORY_SIZE: 1 }, + test: [UP], + expected: [prompt, replFailedRead, prompt, replDisabled, prompt] + }, + // Checking the history file permissions + { + before: common.mustCall(function before() { + if (common.isWindows) { + const execSync = require('child_process').execSync; + execSync(`ATTRIB +H "${emptyHiddenHistoryPath}"`); + } + }), + env: { NODE_REPL_HISTORY: emptyHiddenHistoryPath }, + test: [UP], + expected: [prompt] + }, + // Checking failures when os.homedir() fails + { + before: function before() { + // Mock os.homedir() failure + os.homedir = function() { + throw new Error('os.homedir() failure'); + }; + }, + env: {}, + test: [UP], + expected: [prompt, homedirErr, prompt, replDisabled, prompt] + }, + // Checking that the history file can be set to /dev/null + { + before: function before() { + if (!common.isWindows) + fs.symlinkSync('/dev/null', devNullHistoryPath); + }, + env: { NODE_REPL_HISTORY: devNullHistoryPath }, + test: [UP], + expected: [prompt] + }, +]; +const numtests = tests.length; + + +function cleanupTmpFile() { + try { + // Write over the file, clearing any history + fs.writeFileSync(defaultHistoryPath, ''); + } catch (err) { + if (err.code === 'ENOENT') return true; + throw err; + } + return true; +} + +// Copy our fixture to the tmp directory +fs.createReadStream(historyFixturePath) + .pipe(fs.createWriteStream(historyPath)).on('unpipe', () => runTest()); + +const runTestWrap = common.mustCall(runTest, numtests); + +function runTest(assertCleaned) { + const opts = tests.shift(); + if (!opts) return; // All done + + if (assertCleaned) { + try { + assert.strictEqual(fs.readFileSync(defaultHistoryPath, 'utf8'), ''); + } catch (e) { + if (e.code !== 'ENOENT') { + console.error(`Failed test # ${numtests - tests.length}`); + throw e; + } + } + } + + const test = opts.test; + const expected = opts.expected; + const clean = opts.clean; + const before = opts.before; + const size = opts.env.NODE_REPL_HISTORY_SIZE; + const filePath = opts.env.NODE_REPL_HISTORY; + + if (before) before(); + + const repl = REPL.start({ + input: new ActionStream(), + output: new stream.Writable({ + write: common.mustCallAtLeast((chunk, _, next) => { + const output = chunk.toString(); + + // Ignore escapes and blank lines + if (output.charCodeAt(0) === 27 || /^[\r\n]+$/.test(output)) + return next(); + + try { + assert.strictEqual(output, expected.shift()); + } catch (err) { + console.error(`Failed test # ${numtests - tests.length}`); + throw err; + } + next(); + }), + }), + prompt: prompt, + useColors: false, + terminal: true, + }); + + repl.setupHistory({ + size, + filePath, + onHistoryFileLoaded, + removeHistoryDuplicates: false + }); + + function onHistoryFileLoaded(err, repl) { + if (err) { + console.error(`Failed test # ${numtests - tests.length}`); + throw err; + } + + repl.once('close', () => { + if (repl.historyManager.isFlushing) { + repl.once('flushHistory', onClose); + return; + } + + onClose(); + }); + + function onClose() { + const cleaned = clean === false ? false : cleanupTmpFile(); + + try { + // Ensure everything that we expected was output + assert.strictEqual(expected.length, 0); + setImmediate(runTestWrap, cleaned); + } catch (err) { + console.error(`Failed test # ${numtests - tests.length}`); + throw err; + } + } + + repl.inputStream.run(test); + } +} diff --git a/test/js/node/test/parallel/test-repl-programmatic-history.js b/test/js/node/test/parallel/test-repl-programmatic-history.js new file mode 100644 index 000000000000..c2bb6c88e52e --- /dev/null +++ b/test/js/node/test/parallel/test-repl-programmatic-history.js @@ -0,0 +1,264 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const stream = require('stream'); +const REPL = require('repl'); +const assert = require('assert'); +const fs = require('fs'); +const os = require('os'); + +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Mock os.homedir() +os.homedir = function() { + return tmpdir.path; +}; + +// Create an input stream specialized for testing an array of actions +class ActionStream extends stream.Stream { + run(data) { + const _iter = data[Symbol.iterator](); + const doAction = () => { + const next = _iter.next(); + if (next.done) { + // Close the repl. Note that it must have a clean prompt to do so. + setImmediate(() => { + this.emit('keypress', '', { ctrl: true, name: 'd' }); + }); + return; + } + const action = next.value; + + if (typeof action === 'object') { + this.emit('keypress', '', action); + } else { + this.emit('data', action); + } + setImmediate(doAction); + }; + doAction(); + } + resume() {} + pause() {} +} +ActionStream.prototype.readable = true; + + +// Mock keys +const UP = { name: 'up' }; +const DOWN = { name: 'down' }; +const ENTER = { name: 'enter' }; +const CLEAR = { ctrl: true, name: 'u' }; + +// File paths +const historyFixturePath = fixtures.path('.node_repl_history'); +const historyPath = tmpdir.resolve('.fixture_copy_repl_history'); +const historyPathFail = fixtures.path('nonexistent_folder', 'filename'); +const defaultHistoryPath = tmpdir.resolve('.node_repl_history'); +const emptyHiddenHistoryPath = fixtures.path('.empty-hidden-repl-history-file'); +const devNullHistoryPath = tmpdir.resolve('.dev-null-repl-history-file'); +// Common message bits +const prompt = '> '; +const replDisabled = '\nPersistent history support disabled. Set the ' + + 'NODE_REPL_HISTORY environment\nvariable to a valid, ' + + 'user-writable path to enable.\n'; +const homedirErr = '\nError: Could not get the home directory.\n' + + 'REPL session history will not be persisted.\n'; +const replFailedRead = '\nError: Could not open history file.\n' + + 'REPL session history will not be persisted.\n'; + +const tests = [ + { + env: { NODE_REPL_HISTORY: '' }, + test: [UP], + expected: [prompt, replDisabled, prompt] + }, + { + env: { NODE_REPL_HISTORY: ' ' }, + test: [UP], + expected: [prompt, replDisabled, prompt] + }, + { + env: { NODE_REPL_HISTORY: historyPath }, + test: [UP, CLEAR], + expected: [prompt, `${prompt}'you look fabulous today'`, prompt] + }, + { + env: {}, + test: [UP, '21', ENTER, "'42'", ENTER], + expected: [ + prompt, + '2', '1', '21\n', prompt, + "'", '4', '2', "'", "'42'\n", prompt, + ], + clean: false + }, + { // Requires the above test case + env: {}, + test: [UP, UP, UP, DOWN, ENTER], + expected: [ + prompt, + `${prompt}'42'`, + `${prompt}21`, + prompt, + `${prompt}21`, + '21\n', + prompt, + ] + }, + { + env: { NODE_REPL_HISTORY: historyPath, + NODE_REPL_HISTORY_SIZE: 1 }, + test: [UP, UP, DOWN, CLEAR], + expected: [ + prompt, + `${prompt}'you look fabulous today'`, + prompt, + `${prompt}'you look fabulous today'`, + prompt, + ] + }, + { + env: { NODE_REPL_HISTORY: historyPathFail, + NODE_REPL_HISTORY_SIZE: 1 }, + test: [UP], + expected: [prompt, replFailedRead, prompt, replDisabled, prompt] + }, + { + before: common.mustCall(function before() { + if (common.isWindows) { + const execSync = require('child_process').execSync; + execSync(`ATTRIB +H "${emptyHiddenHistoryPath}"`); + } + }), + env: { NODE_REPL_HISTORY: emptyHiddenHistoryPath }, + test: [UP], + expected: [prompt] + }, + { + before: function before() { + if (!common.isWindows) + fs.symlinkSync('/dev/null', devNullHistoryPath); + }, + env: { NODE_REPL_HISTORY: devNullHistoryPath }, + test: [UP], + expected: [prompt] + }, + { // Make sure this is always the last test, since we change os.homedir() + before: function before() { + // Mock os.homedir() failure + os.homedir = function() { + throw new Error('os.homedir() failure'); + }; + }, + env: {}, + test: [UP], + expected: [prompt, homedirErr, prompt, replDisabled, prompt] + }, +]; +const numtests = tests.length; + + +function cleanupTmpFile() { + try { + // Write over the file, clearing any history + fs.writeFileSync(defaultHistoryPath, ''); + } catch (err) { + if (err.code === 'ENOENT') return true; + throw err; + } + return true; +} + +// Copy our fixture to the tmp directory +fs.createReadStream(historyFixturePath) + .pipe(fs.createWriteStream(historyPath)).on('unpipe', () => runTest()); + +const runTestWrap = common.mustCall(runTest, numtests); + +function runTest(assertCleaned) { + const opts = tests.shift(); + if (!opts) return; // All done + + if (assertCleaned) { + try { + assert.strictEqual(fs.readFileSync(defaultHistoryPath, 'utf8'), ''); + } catch (e) { + if (e.code !== 'ENOENT') { + console.error(`Failed test # ${numtests - tests.length}`); + throw e; + } + } + } + + const test = opts.test; + const expected = opts.expected; + const clean = opts.clean; + const before = opts.before; + const historySize = opts.env.NODE_REPL_HISTORY_SIZE; + const file = opts.env.NODE_REPL_HISTORY; + + if (before) before(); + + const repl = REPL.start({ + input: new ActionStream(), + output: new stream.Writable({ + write: common.mustCallAtLeast((chunk, _, next) => { + const output = chunk.toString(); + + // Ignore escapes and blank lines + if (output.charCodeAt(0) === 27 || /^[\r\n]+$/.test(output)) + return next(); + + try { + assert.strictEqual(output, expected.shift()); + } catch (err) { + console.error(`Failed test # ${numtests - tests.length}`); + throw err; + } + next(); + }), + }), + prompt: prompt, + useColors: false, + terminal: true, + historySize + }); + + repl.setupHistory(file, common.mustCall((err, repl) => { + if (err) { + console.error(`Failed test # ${numtests - tests.length}`); + throw err; + } + + repl.once('close', () => { + if (repl.historyManager.isFlushing) { + repl.once('flushHistory', onClose); + return; + } + + onClose(); + }); + + const onClose = common.mustCall(() => { + const cleaned = clean === false ? false : cleanupTmpFile(); + + try { + // Ensure everything that we expected was output + assert.strictEqual(expected.length, 0); + setImmediate(runTestWrap, cleaned); + } catch (err) { + console.error(`Failed test # ${numtests - tests.length}`); + throw err; + } + }); + + repl.inputStream.run(test); + })); +} diff --git a/test/js/node/test/parallel/test-repl-recoverable.js b/test/js/node/test/parallel/test-repl-recoverable.js new file mode 100644 index 000000000000..74dcd5dfbf58 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-recoverable.js @@ -0,0 +1,41 @@ +'use strict'; + +require('../common'); +const ArrayStream = require('../common/arraystream'); +const assert = require('assert'); +const repl = require('repl'); + +let evalCount = 0; +let recovered = false; +let rendered = false; + +function customEval(code, context, file, cb) { + evalCount++; + + return cb(evalCount === 1 ? new repl.Recoverable() : null, true); +} + +const putIn = new ArrayStream(); + +putIn.write = function(msg) { + if (msg === '| ') { + recovered = true; + } + + if (msg === 'true\n') { + rendered = true; + } +}; + +repl.start('', putIn, customEval); + +// https://github.com/nodejs/node/issues/2939 +// Expose recoverable errors to the consumer. +putIn.emit('data', '1\n'); +putIn.emit('data', '2\n'); + +process.on('exit', function() { + assert(recovered, 'REPL never recovered'); + assert(rendered, 'REPL never rendered the result'); + assert.strictEqual(evalCount, 2); +}); diff --git a/test/js/node/test/parallel/test-repl-require-after-write.js b/test/js/node/test/parallel/test-repl-require-after-write.js new file mode 100644 index 000000000000..519a024dfb0f --- /dev/null +++ b/test/js/node/test/parallel/test-repl-require-after-write.js @@ -0,0 +1,30 @@ +'use strict'; + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const spawn = require('child_process').spawn; + +tmpdir.refresh(); + +const requirePath = JSON.stringify(tmpdir.resolve('non-existent.json')); + +// Use -i to force node into interactive mode, despite stdout not being a TTY +const child = spawn(process.execPath, ['--interactive']); + +let out = ''; +const input = `try { require(${requirePath}); } catch {} ` + + `require('fs').writeFileSync(${requirePath}, '1');` + + `require(${requirePath});`; + +child.stderr.on('data', common.mustNotCall()); + +child.stdout.setEncoding('utf8'); +child.stdout.on('data', (c) => { + out += c; +}); +child.stdout.on('end', common.mustCall(() => { + assert.ok(out.endsWith('> 1\n> ')); +})); + +child.stdin.end(input); diff --git a/test/js/node/test/parallel/test-repl-require-cache.js b/test/js/node/test/parallel/test-repl-require-cache.js new file mode 100644 index 000000000000..b8fe3a753759 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-require-cache.js @@ -0,0 +1,34 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +'use strict'; +require('../common'); +const assert = require('assert'); +const repl = require('repl'); + +// https://github.com/joyent/node/issues/3226 + +require.cache.something = 1; +assert.strictEqual(require.cache.something, 1); + +repl.start({ useGlobal: false }).close(); + +assert.strictEqual(require.cache.something, 1); diff --git a/test/js/node/test/parallel/test-repl-require-context.js b/test/js/node/test/parallel/test-repl-require-context.js new file mode 100644 index 000000000000..070ec727537f --- /dev/null +++ b/test/js/node/test/parallel/test-repl-require-context.js @@ -0,0 +1,23 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const child = cp.spawn(process.execPath, ['--interactive']); +const fixtures = require('../common/fixtures'); +const fixture = fixtures.path('is-object.js').replace(/\\/g, '/'); +let output = ''; + +child.stdout.setEncoding('utf8'); +child.stdout.on('data', (data) => { + output += data; +}); + +child.on('exit', common.mustCall(() => { + const results = output.replace(/^> /mg, '').split('\n').slice(2); + assert.deepStrictEqual(results, ['undefined', 'true', 'true', '']); +})); + +child.stdin.write('const isObject = (obj) => obj.constructor === Object;\n'); +child.stdin.write('isObject({});\n'); +child.stdin.write(`require(${JSON.stringify(fixture)}).isObject({});\n`); +child.stdin.write('.exit\n'); diff --git a/test/js/node/test/parallel/test-repl-require-self-referential.js b/test/js/node/test/parallel/test-repl-require-self-referential.js new file mode 100644 index 000000000000..e22e2cfe883d --- /dev/null +++ b/test/js/node/test/parallel/test-repl-require-self-referential.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { spawn } = require('child_process'); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('process.chdir is not available in Workers'); +} + +const selfRefModule = fixtures.path('self_ref_module'); +const child = spawn(process.execPath, + ['--interactive'], + { cwd: selfRefModule } +); +let output = ''; +child.stdout.on('data', (chunk) => output += chunk); +child.on('exit', common.mustCall(() => { + const results = output.replace(/^> /mg, '').split('\n').slice(2); + assert.deepStrictEqual(results, [ "'Self resolution working'", '' ]); +})); + +child.stdin.write('require("self_ref");\n'); +child.stdin.write('.exit\n'); diff --git a/test/js/node/test/parallel/test-repl-require.js b/test/js/node/test/parallel/test-repl-require.js new file mode 100644 index 000000000000..e740acef08b0 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-require.js @@ -0,0 +1,73 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const net = require('net'); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('process.chdir is not available in Workers'); +} + +process.chdir(fixtures.fixturesDir); +const repl = require('repl'); + +{ + const server = net.createServer((conn) => { + repl.start('', conn).on('exit', () => { + conn.destroy(); + server.close(); + }); + }); + + const host = common.localhostIPv4; + const port = 0; + const options = { host, port }; + + let answer = ''; + server.listen(options, function() { + options.port = this.address().port; + const conn = net.connect(options); + conn.setEncoding('utf8'); + conn.on('data', (data) => answer += data); + conn.write('require("baz")\nrequire("./baz")\n.exit\n'); + }); + + process.on('exit', function() { + assert.doesNotMatch(answer, /Cannot find module/); + assert.doesNotMatch(answer, /Error/); + assert.strictEqual(answer, '\'eye catcher\'\n\'perhaps I work\'\n'); + }); +} + +// Test for https://github.com/nodejs/node/issues/30808 +// In REPL, we shouldn't look up relative modules from 'node_modules'. +{ + const server = net.createServer((conn) => { + repl.start('', conn).on('exit', () => { + conn.destroy(); + server.close(); + }); + }); + + const host = common.localhostIPv4; + const port = 0; + const options = { host, port }; + + let answer = ''; + server.listen(options, function() { + options.port = this.address().port; + const conn = net.connect(options); + conn.setEncoding('utf8'); + conn.on('data', (data) => answer += data); + conn.write('require("./bar")\n.exit\n'); + }); + + process.on('exit', function() { + assert.match(answer, /Uncaught Error: Cannot find module '\.\/bar'/); + + assert.match(answer, /code: 'MODULE_NOT_FOUND'/); + assert.match(answer, /requireStack: \[ '' \]/); + }); +} diff --git a/test/js/node/test/parallel/test-repl-reset-event.js b/test/js/node/test/parallel/test-repl-reset-event.js new file mode 100644 index 000000000000..195ff581fe78 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-reset-event.js @@ -0,0 +1,63 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const util = require('util'); +const { startNewREPLServer } = require('../common/repl'); + +common.allowGlobals(42); + +function testReset(cb) { + const { replServer } = startNewREPLServer(); + replServer.context.foo = 42; + replServer.on('reset', common.mustCall(function(context) { + assert(!!context, 'REPL did not emit a context with reset event'); + assert.strictEqual(context, replServer.context, 'REPL emitted incorrect context. ' + + `context is ${util.inspect(context)}, expected ${util.inspect(replServer.context)}`); + assert.strictEqual( + context.foo, + undefined, + 'REPL emitted the previous context and is not using global as context. ' + + `context.foo is ${context.foo}, expected undefined.` + ); + context.foo = 42; + cb(); + })); + replServer.resetContext(); +} + +function testResetGlobal() { + const { replServer } = startNewREPLServer({ useGlobal: true }); + replServer.context.foo = 42; + replServer.on('reset', common.mustCall(function(context) { + assert.strictEqual( + context.foo, + 42, + '"foo" property is different from REPL using global as context. ' + + `context.foo is ${context.foo}, expected 42.` + ); + })); + replServer.resetContext(); +} + +testReset(common.mustCall(testResetGlobal)); diff --git a/test/js/node/test/parallel/test-repl-reverse-search.js b/test/js/node/test/parallel/test-repl-reverse-search.js new file mode 100644 index 000000000000..cbe848afee08 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-reverse-search.js @@ -0,0 +1,365 @@ +'use strict'; + +// Flags: --expose-internals + +const common = require('../common'); +const stream = require('stream'); +const REPL = require('internal/repl'); +const assert = require('assert'); +const fs = require('fs'); +const { inspect } = require('util'); + +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} + +common.allowGlobals('aaaa'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const defaultHistoryPath = tmpdir.resolve('.node_repl_history'); + +// Create an input stream specialized for testing an array of actions +class ActionStream extends stream.Stream { + run(data) { + const _iter = data[Symbol.iterator](); + const doAction = () => { + const next = _iter.next(); + if (next.done) { + // Close the repl. Note that it must have a clean prompt to do so. + setImmediate(() => { + this.emit('keypress', '', { ctrl: true, name: 'd' }); + }); + return; + } + const action = next.value; + + if (typeof action === 'object') { + this.emit('keypress', '', action); + } else { + this.emit('data', action); + } + setImmediate(doAction); + }; + doAction(); + } + resume() {} + pause() {} +} +ActionStream.prototype.readable = true; + +// Mock keys +const ENTER = { name: 'enter' }; +const UP = { name: 'up' }; +const DOWN = { name: 'down' }; +const BACKSPACE = { name: 'backspace' }; +const SEARCH_BACKWARDS = { name: 'r', ctrl: true }; +const SEARCH_FORWARDS = { name: 's', ctrl: true }; +const ESCAPE = { name: 'escape' }; +const CTRL_C = { name: 'c', ctrl: true }; +const DELETE_WORD_LEFT = { name: 'w', ctrl: true }; + +const prompt = '> '; + +// TODO(BridgeAR): Add tests for lines that exceed the maximum columns. +const tests = [ + { // Creates few history to navigate for + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + test: [ + 'console.log("foo")', ENTER, + 'ab = "aaaa"', ENTER, + 'repl.repl.historyIndex', ENTER, + 'console.log("foo")', ENTER, + 'let ba = 9', ENTER, + 'ab = "aaaa"', ENTER, + '555 - 909', ENTER, + '{key : {key2 :[] }}', ENTER, + 'Array(100).fill(1)', ENTER, + ], + expected: [], + clean: false + }, + { + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + showEscapeCodes: true, + checkTotal: true, + useColors: true, + test: [ + '7', // 1 + SEARCH_FORWARDS, + SEARCH_FORWARDS, // 3 + 'a', + SEARCH_BACKWARDS, // 5 + SEARCH_FORWARDS, + SEARCH_BACKWARDS, // 7 + 'a', + BACKSPACE, // 9 + DELETE_WORD_LEFT, + 'aa', // 11 + SEARCH_BACKWARDS, + SEARCH_BACKWARDS, // 13 + SEARCH_BACKWARDS, + SEARCH_BACKWARDS, // 15 + SEARCH_FORWARDS, + ESCAPE, // 17 + ENTER, + ], + // A = Cursor n up + // B = Cursor n down + // C = Cursor n forward + // D = Cursor n back + // G = Cursor to column n + // J = Erase in screen; 0 = right; 1 = left; 2 = total + // K = Erase in line; 0 = right; 1 = left; 2 = total + expected: [ + // 0. Start + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + // 1. '7' + '7', + // 2. SEARCH FORWARDS + '\nfwd-i-search: _', '\x1B[1A', '\x1B[4G', + // 3. SEARCH FORWARDS + '\x1B[3G', '\x1B[0J', + '7\nfwd-i-search: _', '\x1B[1A', '\x1B[4G', + // 4. 'a' + '\x1B[3G', '\x1B[0J', + '7\nfailed-fwd-i-search: a_', '\x1B[1A', '\x1B[4G', + // 5. SEARCH BACKWARDS + '\x1B[3G', '\x1B[0J', + 'Arr\x1B[4ma\x1B[24my(100).fill(1)\nbck-i-search: a_', + '\x1B[1A', '\x1B[6G', + // 6. SEARCH FORWARDS + '\x1B[3G', '\x1B[0J', + '7\nfailed-fwd-i-search: a_', '\x1B[1A', '\x1B[4G', + // 7. SEARCH BACKWARDS + '\x1B[3G', '\x1B[0J', + 'Arr\x1B[4ma\x1B[24my(100).fill(1)\nbck-i-search: a_', + '\x1B[1A', '\x1B[6G', + // 8. 'a' + '\x1B[3G', '\x1B[0J', + 'ab = "aa\x1B[4maa\x1B[24m"\nbck-i-search: aa_', + '\x1B[1A', '\x1B[11G', + // 9. BACKSPACE + '\x1B[3G', '\x1B[0J', + 'Arr\x1B[4ma\x1B[24my(100).fill(1)\nbck-i-search: a_', + '\x1B[1A', '\x1B[6G', + // 10. DELETE WORD LEFT (works as backspace) + '\x1B[3G', '\x1B[0J', + '7\nbck-i-search: _', '\x1B[1A', '\x1B[4G', + // 11. 'a' + '\x1B[3G', '\x1B[0J', + 'Arr\x1B[4ma\x1B[24my(100).fill(1)\nbck-i-search: a_', + '\x1B[1A', '\x1B[6G', + // 11. 'aa' - continued + '\x1B[3G', '\x1B[0J', + 'ab = "aa\x1B[4maa\x1B[24m"\nbck-i-search: aa_', + '\x1B[1A', '\x1B[11G', + // 12. SEARCH BACKWARDS + '\x1B[3G', '\x1B[0J', + 'ab = "a\x1B[4maa\x1B[24ma"\nbck-i-search: aa_', + '\x1B[1A', '\x1B[10G', + // 13. SEARCH BACKWARDS + '\x1B[3G', '\x1B[0J', + 'ab = "\x1B[4maa\x1B[24maa"\nbck-i-search: aa_', + '\x1B[1A', '\x1B[9G', + // 14. SEARCH BACKWARDS + '\x1B[3G', '\x1B[0J', + '7\nfailed-bck-i-search: aa_', '\x1B[1A', '\x1B[4G', + // 15. SEARCH BACKWARDS + '\x1B[3G', '\x1B[0J', + '7\nfailed-bck-i-search: aa_', '\x1B[1A', '\x1B[4G', + // 16. SEARCH FORWARDS + '\x1B[3G', '\x1B[0J', + 'ab = "\x1B[4maa\x1B[24maa"\nfwd-i-search: aa_', + '\x1B[1A', '\x1B[9G', + // 17. ESCAPE + '\x1B[3G', '\x1B[0J', + '7', + // 18. ENTER + '\r\n', + '\x1B[33m7\x1B[39m\n', + '\x1B[1G', '\x1B[0J', + prompt, + '\x1B[3G', + '\r\n', + ], + clean: false + }, + { + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + showEscapeCodes: true, + skip: !process.features.inspector, + checkTotal: true, + useColors: false, + test: [ + 'fu', // 1 + SEARCH_BACKWARDS, + '}', // 3 + SEARCH_BACKWARDS, + CTRL_C, // 5 + CTRL_C, + '1+1', // 7 + ENTER, + SEARCH_BACKWARDS, // 9 + '+', + '\r', // 11 + '2', + SEARCH_BACKWARDS, // 13 + 're', + UP, // 15 + DOWN, + SEARCH_FORWARDS, // 17 + '\n', + ], + expected: [ + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + 'f', 'u', '\nbck-i-search: _', '\x1B[1A', '\x1B[5G', + '\x1B[3G', '\x1B[0J', + '{key : {key2 :[] }}\nbck-i-search: }_', '\x1B[1A', '\x1B[21G', + '\x1B[3G', '\x1B[0J', + '{key : {key2 :[] }}\nbck-i-search: }_', '\x1B[1A', '\x1B[20G', + '\x1B[3G', '\x1B[0J', + 'fu', + '\r\n', + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + '1', '+', '1', '\n// 2', '\x1B[6G', '\x1B[1A', + '\x1B[1B', '\x1B[2K', '\x1B[1A', + '\r\n', + '2\n', + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + '\nbck-i-search: _', '\x1B[1A', + '\x1B[3G', '\x1B[0J', + '1+1\nbck-i-search: +_', '\x1B[1A', '\x1B[4G', + '\x1B[3G', '\x1B[0J', + '1+1', '\x1B[4G', + '\x1B[2C', + '\r\n', + '2\n', + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + '2', + '\nbck-i-search: _', '\x1B[1A', '\x1B[4G', + '\x1B[3G', '\x1B[0J', + 'Array(100).fill(1)\nbck-i-search: r_', '\x1B[1A', '\x1B[5G', + '\x1B[3G', '\x1B[0J', + 'repl.repl.historyIndex\nbck-i-search: re_', '\x1B[1A', '\x1B[8G', + '\x1B[3G', '\x1B[0J', + 'repl.repl.historyIndex', '\x1B[8G', + '\x1B[1G', '\x1B[0J', + `${prompt}ab = "aaaa"`, '\x1B[14G', + '\x1B[1G', '\x1B[0J', + `${prompt}repl.repl.historyIndex`, '\x1B[25G', '\n// 8', + '\x1B[25G', '\x1B[1A', + '\x1B[1B', '\x1B[2K', '\x1B[1A', + '\nfwd-i-search: _', '\x1B[1A', '\x1B[25G', + '\x1B[3G', '\x1B[0J', + 'repl.repl.historyIndex', + '\r\n', + '-1\n', + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + '\r\n', + ], + clean: false + }, +]; +const numtests = tests.length; + +const runTestWrap = common.mustCall(runTest, numtests); + +function cleanupTmpFile() { + try { + // Write over the file, clearing any history + fs.writeFileSync(defaultHistoryPath, ''); + } catch (err) { + if (err.code === 'ENOENT') return true; + throw err; + } + return true; +} + +function runTest() { + const opts = tests.shift(); + if (!opts) return; // All done + + const { expected, skip } = opts; + + // Test unsupported on platform. + if (skip) { + setImmediate(runTestWrap, true); + return; + } + + const lastChunks = []; + let i = 0; + + REPL.createInternalRepl(opts.env, { + input: new ActionStream(), + output: new stream.Writable({ + write: common.mustCallAtLeast((chunk, _, next) => { + const output = chunk.toString(); + + if (!opts.showEscapeCodes && + (output[0] === '\x1B' || /^[\r\n]+$/.test(output))) { + return next(); + } + + lastChunks.push(output); + + if (expected.length && !opts.checkTotal) { + try { + assert.strictEqual(output, expected[i]); + } catch (e) { + console.error(`Failed test # ${numtests - tests.length}`); + console.error('Last outputs: ' + inspect(lastChunks, { + breakLength: 5, colors: true + })); + throw e; + } + i++; + } + + next(); + }), + }), + completer: opts.completer, + prompt, + useColors: opts.useColors || false, + terminal: true + }, common.mustCall((err, repl) => { + if (err) { + console.error(`Failed test # ${numtests - tests.length}`); + throw err; + } + + repl.once('close', common.mustCall(() => { + if (opts.clean) + cleanupTmpFile(); + + if (opts.checkTotal) { + assert.deepStrictEqual(lastChunks, expected); + } else if (expected.length !== i) { + console.error(tests[numtests - tests.length - 1]); + throw new Error(`Failed test # ${numtests - tests.length}`); + } + + setImmediate(runTestWrap, true); + })); + + if (opts.columns) { + Object.defineProperty(repl, 'columns', { + value: opts.columns, + enumerable: true + }); + } + repl.inputStream.run(opts.test); + })); +} + +// run the tests +runTest(); diff --git a/test/js/node/test/parallel/test-repl-save-load-editor-mode.js b/test/js/node/test/parallel/test-repl-save-load-editor-mode.js new file mode 100644 index 000000000000..83a57cdaa55a --- /dev/null +++ b/test/js/node/test/parallel/test-repl-save-load-editor-mode.js @@ -0,0 +1,35 @@ +'use strict'; + +require('../common'); +const assert = require('node:assert'); +const fs = require('node:fs'); +const path = require('node:path'); +const { startNewREPLServer } = require('../common/repl'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Test for saving a REPL session in editor mode + +const { replServer, input } = startNewREPLServer(); + +input.run(['.editor']); + +const commands = [ + 'function testSave() {', + 'return "saved";', + '}', +]; + +input.run(commands); + +replServer.write('', { ctrl: true, name: 'd' }); + +const filePath = path.resolve(tmpdir.path, 'test.save.js'); + +input.run([`.save ${filePath}`]); + +assert.strictEqual(fs.readFileSync(filePath, 'utf8'), + `${commands.join('\n')}\n`); + +replServer.close(); diff --git a/test/js/node/test/parallel/test-repl-save-load-invalid-save.js b/test/js/node/test/parallel/test-repl-save-load-invalid-save.js new file mode 100644 index 000000000000..60039ee5b15a --- /dev/null +++ b/test/js/node/test/parallel/test-repl-save-load-invalid-save.js @@ -0,0 +1,25 @@ +'use strict'; + +const common = require('../common'); +const assert = require('node:assert'); +const { startNewREPLServer } = require('../common/repl'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Test for the appropriate handling of cases in which REPL saves fail + +const { replServer, input, output } = startNewREPLServer({ terminal: false }); + +// NUL (\0) is disallowed in filenames in UNIX-like operating systems and +// Windows so we can use that to test failed saves. +const invalidFilePath = tmpdir.resolve('\0\0\0\0\0'); + +output.write = common.mustCall(function(data) { + assert.strictEqual(data, `Failed to save: ${invalidFilePath}\n`); + output.write = () => {}; +}); + +input.run([`.save ${invalidFilePath}`]); + +replServer.close(); diff --git a/test/js/node/test/parallel/test-repl-save-load-load-dir.js b/test/js/node/test/parallel/test-repl-save-load-load-dir.js new file mode 100644 index 000000000000..20bfae436a9c --- /dev/null +++ b/test/js/node/test/parallel/test-repl-save-load-load-dir.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const { startNewREPLServer } = require('../common/repl'); +const assert = require('node:assert'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Tests that an appropriate error is displayed if the user tries to load a directory instead of a file + +const { replServer, input, output } = startNewREPLServer({ terminal: false }); + +const dirPath = tmpdir.path; + +output.write = common.mustCall(function(data) { + assert.strictEqual(data, `Failed to load: ${dirPath} is not a valid file\n`); + output.write = () => {}; +}); + +input.run([`.load ${dirPath}`]); + +replServer.close(); diff --git a/test/js/node/test/parallel/test-repl-save-load-load-non-existent.js b/test/js/node/test/parallel/test-repl-save-load-load-non-existent.js new file mode 100644 index 000000000000..1456316ae447 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-save-load-load-non-existent.js @@ -0,0 +1,23 @@ +'use strict'; + +const common = require('../common'); +const { startNewREPLServer } = require('../common/repl'); +const assert = require('node:assert'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Tests that an appropriate error is displayed if the user tries to load a non existent file + +const { replServer, input, output } = startNewREPLServer({ terminal: false }); + +const filePath = tmpdir.resolve('file.does.not.exist'); + +output.write = common.mustCall(function(data) { + assert.strictEqual(data, `Failed to load: ${filePath}\n`); + output.write = () => {}; +}); + +input.run([`.load ${filePath}`]); + +replServer.close(); diff --git a/test/js/node/test/parallel/test-repl-save-load-load-without-name.js b/test/js/node/test/parallel/test-repl-save-load-load-without-name.js new file mode 100644 index 000000000000..b52b7f37015a --- /dev/null +++ b/test/js/node/test/parallel/test-repl-save-load-load-without-name.js @@ -0,0 +1,21 @@ +'use strict'; + +const common = require('../common'); +const { startNewREPLServer } = require('../common/repl'); +const assert = require('node:assert'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Tests that an appropriate error is displayed if .load is called without a filename + +const { replServer, input, output } = startNewREPLServer({ terminal: false }); + +output.write = common.mustCall(function(data) { + assert.strictEqual(data, 'The "file" argument must be specified\n'); + output.write = () => {}; +}); + +input.run(['.load']); + +replServer.close(); diff --git a/test/js/node/test/parallel/test-repl-save-load-save-without-name.js b/test/js/node/test/parallel/test-repl-save-load-save-without-name.js new file mode 100644 index 000000000000..d12e5f2dd459 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-save-load-save-without-name.js @@ -0,0 +1,21 @@ +'use strict'; + +const common = require('../common'); +const { startNewREPLServer } = require('../common/repl'); +const assert = require('node:assert'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Tests that an appropriate error is displayed if .save is called without a filename + +const { replServer, input, output } = startNewREPLServer({ terminal: false }); + +output.write = common.mustCall(function(data) { + assert.strictEqual(data, 'The "file" argument must be specified\n'); + output.write = () => {}; +}); + +input.run(['.save']); + +replServer.close(); diff --git a/test/js/node/test/parallel/test-repl-save-load.js b/test/js/node/test/parallel/test-repl-save-load.js new file mode 100644 index 000000000000..d8401c2d4278 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-save-load.js @@ -0,0 +1,78 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +'use strict'; + +const common = require('../common'); +const { startNewREPLServer } = require('../common/repl'); +const assert = require('node:assert'); +const fs = require('node:fs'); +const path = require('node:path'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +// Tests that a REPL session data can be saved to and loaded from a file + +const { replServer, input } = startNewREPLServer({ terminal: false }); + +const filePath = path.resolve(tmpdir.path, 'test.save.js'); + +const testFileContents = [ + 'let inner = (function() {', + ' return {one:1};', + '})()', +]; + +input.run(testFileContents); +input.run([`.save ${filePath}`]); + +assert.strictEqual(fs.readFileSync(filePath, 'utf8'), + testFileContents.join('\n')); + +const innerOCompletions = [['inner.one'], 'inner.o']; + +// Double check that the data is still present in the repl after the save +replServer.completer('inner.o', common.mustSucceed((data) => { + assert.deepStrictEqual(data, innerOCompletions); +})); + +// Clear the repl context +input.run(['.clear']); + +// Double check that the data is no longer present in the repl +replServer.completer('inner.o', common.mustSucceed((data) => { + assert.deepStrictEqual(data, [[], 'inner.o']); +})); + +// Load the file back in. +input.run([`.load ${filePath}`]); + +// Make sure loading doesn't insert extra indentation +// https://github.com/nodejs/node/issues/47673 +assert.strictEqual(replServer.line, ''); + +// Make sure that the loaded data is present +replServer.complete('inner.o', common.mustSucceed((data) => { + assert.deepStrictEqual(data, innerOCompletions); +})); + +replServer.close(); diff --git a/test/js/node/test/parallel/test-repl-setprompt.js b/test/js/node/test/parallel/test-repl-setprompt.js new file mode 100644 index 000000000000..9901f8f974f6 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-setprompt.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const spawn = require('child_process').spawn; +const os = require('os'); + +const args = [ + '-e', + 'var e = new (require("repl")).REPLServer("foo.. "); e.context.e = e;', +]; + +const p = 'bar.. '; + +const child = spawn(process.execPath, args); + +child.stdout.setEncoding('utf8'); + +let data = ''; +child.stdout.on('data', function(d) { data += d; }); + +child.stdin.end(`e.setPrompt("${p}");${os.EOL}`); + +child.on('close', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.ok(!signal); + const lines = data.split('\n'); + assert.strictEqual(lines.pop(), p); +})); diff --git a/test/js/node/test/parallel/test-repl-sigint-nested-eval.js b/test/js/node/test/parallel/test-repl-sigint-nested-eval.js new file mode 100644 index 000000000000..ecc532f31ede --- /dev/null +++ b/test/js/node/test/parallel/test-repl-sigint-nested-eval.js @@ -0,0 +1,53 @@ +'use strict'; +const common = require('../common'); +if (common.isWindows) { + // No way to send CTRL_C_EVENT to processes from JS right now. + common.skip('platform not supported'); +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('No signal handling available in Workers'); +} + +const assert = require('assert'); +const spawn = require('child_process').spawn; + +const child = spawn(process.execPath, [ '--interactive' ], { + stdio: [null, null, 2, 'ipc'] +}); + +let stdout = ''; +child.stdout.setEncoding('utf8'); +child.stdout.on('data', function(c) { + stdout += c; +}); + +child.stdout.once('data', common.mustCall(() => { + child.on('message', common.mustCall((msg) => { + assert.strictEqual(msg, 'repl is busy'); + process.kill(child.pid, 'SIGINT'); + child.stdout.once('data', common.mustCall(() => { + // Make sure REPL still works. + child.stdin.end('"foobar"\n'); + })); + })); + + child.stdin.write( + 'vm.runInThisContext("process.send(\'repl is busy\'); while(true){}", ' + + '{ breakOnSigint: true });\n' + ); +})); + +child.on('close', common.mustCall((code) => { + const expected = 'Script execution was interrupted by `SIGINT`'; + assert.ok( + stdout.includes(expected), + `Expected stdout to contain "${expected}", got ${stdout}` + ); + assert.ok( + stdout.includes('foobar'), + `Expected stdout to contain "foobar", got ${stdout}` + ); +})); diff --git a/test/js/node/test/parallel/test-repl-sigint.js b/test/js/node/test/parallel/test-repl-sigint.js new file mode 100644 index 000000000000..8db02db886fd --- /dev/null +++ b/test/js/node/test/parallel/test-repl-sigint.js @@ -0,0 +1,53 @@ +'use strict'; +const common = require('../common'); +if (common.isWindows) { + // No way to send CTRL_C_EVENT to processes from JS right now. + common.skip('platform not supported'); +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('No signal handling available in Workers'); +} + +const assert = require('assert'); +const spawn = require('child_process').spawn; + +process.env.REPL_TEST_PPID = process.pid; +const child = spawn(process.execPath, [ '--interactive' ], { + stdio: [null, null, 2] +}); + +let stdout = ''; +child.stdout.setEncoding('utf8'); +child.stdout.on('data', function(c) { + stdout += c; +}); + +child.stdout.once('data', common.mustCall(() => { + process.on('SIGUSR2', common.mustCall(() => { + process.kill(child.pid, 'SIGINT'); + child.stdout.once('data', common.mustCall(() => { + // Make sure state from before the interruption is still available. + child.stdin.end('a*2*3*7\n'); + })); + })); + + child.stdin.write('a = 1001;' + + 'process.kill(+process.env.REPL_TEST_PPID, "SIGUSR2");' + + 'while(true){}\n'); +})); + +child.on('close', common.mustCall((code) => { + assert.strictEqual(code, 0); + const expected = 'Script execution was interrupted by `SIGINT`'; + assert.ok( + stdout.includes(expected), + `Expected stdout to contain "${expected}", got ${stdout}` + ); + assert.ok( + stdout.includes('42042\n'), + `Expected stdout to contain "42042", got ${stdout}` + ); +})); diff --git a/test/js/node/test/parallel/test-repl-stdin-push-null.js b/test/js/node/test/parallel/test-repl-stdin-push-null.js new file mode 100644 index 000000000000..53ba9ff7c331 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-stdin-push-null.js @@ -0,0 +1,9 @@ +'use strict'; +const common = require('../common'); + +if (!process.stdin.isTTY) { + common.skip('does not apply on non-TTY stdin'); +} + +process.stdin.destroy(); +process.stdin.setRawMode(true); diff --git a/test/js/node/test/parallel/test-repl-strict-mode-previews.js b/test/js/node/test/parallel/test-repl-strict-mode-previews.js new file mode 100644 index 000000000000..e7fc1ea5191e --- /dev/null +++ b/test/js/node/test/parallel/test-repl-strict-mode-previews.js @@ -0,0 +1,50 @@ +// Previews in strict mode should indicate ReferenceErrors. + +'use strict'; + +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} + +if (process.argv[2] === 'child') { + const stream = require('stream'); + const repl = require('repl'); + class ActionStream extends stream.Stream { + readable = true; + run(data) { + this.emit('data', `${data}`); + this.emit('keypress', '', { ctrl: true, name: 'd' }); + } + resume() {} + pause() {} + } + + repl.start({ + input: new ActionStream(), + output: new stream.Writable({ + write(chunk, _, next) { + console.log(chunk.toString()); + next(); + } + }), + useColors: false, + terminal: true + }).inputStream.run('xyz'); +} else { + const assert = require('assert'); + const { spawnSync } = require('child_process'); + + const result = spawnSync( + process.execPath, + ['--use-strict', `${__filename}`, 'child'] + ); + + assert.match( + result.stdout.toString(), + /\/\/ ReferenceError: xyz is not defined/ + ); +} diff --git a/test/js/node/test/parallel/test-repl-syntax-error-stack.js b/test/js/node/test/parallel/test-repl-syntax-error-stack.js new file mode 100644 index 000000000000..16bf27d045bc --- /dev/null +++ b/test/js/node/test/parallel/test-repl-syntax-error-stack.js @@ -0,0 +1,32 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +let found = false; + +process.on('exit', () => { + assert.strictEqual(found, true); +}); + +const { input, output } = startNewREPLServer(); + +output.write = (data) => { + // Matching only on a minimal piece of the stack because the string will vary + // greatly depending on the JavaScript engine. V8 includes `;` because it + // displays the line of code (`var foo bar;`) that is causing a problem. + // ChakraCore does not display the line of code but includes `;` in the phrase + // `Expected ';' `. + if (/;/.test(data)) + found = true; +}; + +let file = fixtures.path('syntax', 'bad_syntax'); + +if (common.isWindows) + file = file.replace(/\\/g, '\\\\'); + +input.run(['.clear']); +input.run([`require('${file}');`]); diff --git a/test/js/node/test/parallel/test-repl-tab-complete-buffer.js b/test/js/node/test/parallel/test-repl-tab-complete-buffer.js new file mode 100644 index 000000000000..25a5dc6fe6c8 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-buffer.js @@ -0,0 +1,62 @@ +'use strict'; + +const common = require('../common'); +const { hijackStderr, restoreStderr } = require('../common/hijackstdio'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +const { replServer, input } = startNewREPLServer(); + +for (const type of [ + Array, + Buffer, + + Uint8Array, + Uint16Array, + Uint32Array, + + Uint8ClampedArray, + Int8Array, + Int16Array, + Int32Array, + Float32Array, + Float64Array, +]) { + input.run(['.clear']); + + if (type === Array) { + input.run([ + 'var ele = [];', + 'for (let i = 0; i < 1e6 + 1; i++) ele[i] = 0;', + 'ele.biu = 1;', + ]); + } else if (type === Buffer) { + input.run(['var ele = Buffer.alloc(1e6 + 1); ele.biu = 1;']); + } else { + input.run([`var ele = new ${type.name}(1e6 + 1); ele.biu = 1;`]); + } + + hijackStderr(common.mustNotCall()); + replServer.complete( + 'ele.', + common.mustCall((err, data) => { + restoreStderr(); + assert.ifError(err); + + const ele = + type === Array ? [] : type === Buffer ? Buffer.alloc(0) : new type(0); + + assert.strictEqual(data[0].includes('ele.biu'), true); + + for (const key of data[0]) { + if (!key || key === 'ele.biu') return; + assert.notStrictEqual(ele[key.slice(4)], undefined); + } + }) + ); +} + +// check Buffer.prototype.length not crashing. +// Refs: https://github.com/nodejs/node/pull/11961 +input.run(['.clear']); +replServer.complete('Buffer.prototype.', common.mustCall()); diff --git a/test/js/node/test/parallel/test-repl-tab-complete-computed-props.js b/test/js/node/test/parallel/test-repl-tab-complete-computed-props.js new file mode 100644 index 000000000000..418dc5059e91 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-computed-props.js @@ -0,0 +1,142 @@ +'use strict'; + +const common = require('../common'); +const { startNewREPLServer } = require('../common/repl'); +const { describe, it, before, after } = require('node:test'); +const assert = require('assert'); + +function testCompletion(replServer, { input, expectedCompletions }) { + replServer.complete( + input, + common.mustCall((_error, data) => { + assert.deepStrictEqual(data, [expectedCompletions, input]); + }), + ); +}; + +describe('REPL tab object completion on computed properties', () => { + describe('simple string cases', () => { + let replServer; + + before(() => { + const { replServer: server, input } = startNewREPLServer(); + replServer = server; + + input.run([ + ` + const obj = { + one: 1, + innerObj: { two: 2 }, + 'inner object': { three: 3 }, + }; + + const oneStr = 'one'; + `, + ]); + }); + + after(() => { + replServer.close(); + }); + + it('works with double quoted strings', () => testCompletion(replServer, { + input: 'obj["one"].toFi', + expectedCompletions: ['obj["one"].toFixed'], + })); + + it('works with single quoted strings', () => testCompletion(replServer, { + input: "obj['one'].toFi", + expectedCompletions: ["obj['one'].toFixed"], + })); + + it('works with template strings', () => testCompletion(replServer, { + input: 'obj[`one`].toFi', + expectedCompletions: ['obj[`one`].toFixed'], + })); + + it('works with nested objects', () => { + testCompletion(replServer, { + input: 'obj["innerObj"].tw', + expectedCompletions: ['obj["innerObj"].two'], + }); + testCompletion(replServer, { + input: 'obj["innerObj"].two.tofi', + expectedCompletions: ['obj["innerObj"].two.toFixed'], + }); + }); + + it('works with nested objects combining different type of strings', () => testCompletion(replServer, { + input: 'obj["innerObj"][`two`].tofi', + expectedCompletions: ['obj["innerObj"][`two`].toFixed'], + })); + + it('works with strings with spaces', () => testCompletion(replServer, { + input: 'obj["inner object"].th', + expectedCompletions: ['obj["inner object"].three'], + })); + }); + + describe('variables as indexes', () => { + let replServer; + + before(() => { + const { replServer: server, input } = startNewREPLServer(); + replServer = server; + + input.run([ + ` + const oneStr = 'One'; + const helloWorldStr = 'Hello' + ' ' + 'World'; + + const obj = { + [oneStr]: 1, + ['Hello World']: 'hello world!', + }; + + const lookupObj = { + stringLookup: helloWorldStr, + ['number lookup']: oneStr, + }; + `, + ]); + }); + + after(() => { + replServer.close(); + }); + + it('works with a simple variable', () => testCompletion(replServer, { + input: 'obj[oneStr].toFi', + expectedCompletions: ['obj[oneStr].toFixed'], + })); + + it('works with a computed variable', () => testCompletion(replServer, { + input: 'obj[helloWorldStr].tolocaleup', + expectedCompletions: ['obj[helloWorldStr].toLocaleUpperCase'], + })); + + it('works with a simple inlined computed property', () => testCompletion(replServer, { + input: 'obj["Hello " + "World"].tolocaleup', + expectedCompletions: ['obj["Hello " + "World"].toLocaleUpperCase'], + })); + + it('works with a ternary inlined computed property', () => testCompletion(replServer, { + input: 'obj[(1 + 2 > 5) ? oneStr : "Hello " + "World"].toLocaleUpperCase', + expectedCompletions: ['obj[(1 + 2 > 5) ? oneStr : "Hello " + "World"].toLocaleUpperCase'], + })); + + it('works with an inlined computed property with a nested property lookup', () => + testCompletion(replServer, { + input: 'obj[lookupObj.stringLookup].tolocaleupp', + expectedCompletions: ['obj[lookupObj.stringLookup].toLocaleUpperCase'], + }) + ); + + it('works with an inlined computed property with a nested inlined computer property lookup', () => + testCompletion(replServer, { + input: 'obj[lookupObj["number" + " lookup"]].toFi', + expectedCompletions: ['obj[lookupObj["number" + " lookup"]].toFixed'], + }) + ); + }); +}); diff --git a/test/js/node/test/parallel/test-repl-tab-complete-crash.js b/test/js/node/test/parallel/test-repl-tab-complete-crash.js new file mode 100644 index 000000000000..29f75028bdac --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-crash.js @@ -0,0 +1,21 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +const { replServer, input } = startNewREPLServer(); + +// https://github.com/nodejs/node/issues/3346 +// Tab-completion should be empty +input.run(['.clear', 'function () {']); +replServer.complete('arguments.', common.mustCall((err, completions) => { + assert.strictEqual(err, null); + assert.deepStrictEqual(completions, [[], 'arguments.']); +})); + +input.run(['.clear', 'function () {', 'undef;']); +replServer.complete('undef.', common.mustCall((err, completions) => { + assert.strictEqual(err, null); + assert.deepStrictEqual(completions, [[], 'undef.']); +})); diff --git a/test/js/node/test/parallel/test-repl-tab-complete-custom-completer.js b/test/js/node/test/parallel/test-repl-tab-complete-custom-completer.js new file mode 100644 index 000000000000..1599331f2a2b --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-custom-completer.js @@ -0,0 +1,65 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +// To test custom completer function. +// Sync mode. +{ + const customCompletions = 'aaa aa1 aa2 bbb bb1 bb2 bb3 ccc ddd eee'.split(' '); + const { replServer } = startNewREPLServer({ + completer: function completer(line) { + const hits = customCompletions.filter((c) => c.startsWith(line)); + // Show all completions if none found. + return [hits.length ? hits : customCompletions, line]; + } + }); + + // On empty line should output all the custom completions + // without complete anything. + replServer.complete('', common.mustCall((error, data) => { + assert.deepStrictEqual(data, [ + customCompletions, + '', + ]); + })); + + // On `a` should output `aaa aa1 aa2` and complete until `aa`. + replServer.complete('a', common.mustCall((error, data) => { + assert.deepStrictEqual(data, [ + 'aaa aa1 aa2'.split(' '), + 'a', + ]); + })); +} + +// To test custom completer function. +// Async mode. +{ + const customCompletions = 'aaa aa1 aa2 bbb bb1 bb2 bb3 ccc ddd eee'.split(' '); + const { replServer } = startNewREPLServer({ + completer: function completer(line, callback) { + const hits = customCompletions.filter((c) => c.startsWith(line)); + // Show all completions if none found. + callback(null, [hits.length ? hits : customCompletions, line]); + } + }); + + // On empty line should output all the custom completions + // without complete anything. + replServer.complete('', common.mustCall((error, data) => { + assert.deepStrictEqual(data, [ + customCompletions, + '', + ]); + })); + + // On `a` should output `aaa aa1 aa2` and complete until `aa`. + replServer.complete('a', common.mustCall((error, data) => { + assert.deepStrictEqual(data, [ + 'aaa aa1 aa2'.split(' '), + 'a', + ]); + })); +} diff --git a/test/js/node/test/parallel/test-repl-tab-complete-files.js b/test/js/node/test/parallel/test-repl-tab-complete-files.js new file mode 100644 index 000000000000..ddb3df07176b --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-files.js @@ -0,0 +1,70 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); +const { startNewREPLServer } = require('../common/repl'); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('process.chdir is not available in Workers'); +} + +const { replServer } = startNewREPLServer(); + +// Tab completion for files/directories +{ + process.chdir(__dirname); + + const readFileSyncs = ['fs.readFileSync("', 'fs.promises.readFileSync("']; + if (!common.isWindows) { + readFileSyncs.forEach((readFileSync) => { + const fixturePath = `${readFileSync}../fixtures/test-repl-tab-completion`; + replServer.complete( + fixturePath, + common.mustCall((err, data) => { + assert.strictEqual(err, null); + assert.ok(data[0][0].includes('.hiddenfiles')); + assert.ok(data[0][1].includes('hellorandom.txt')); + assert.ok(data[0][2].includes('helloworld.js')); + }) + ); + + replServer.complete( + `${fixturePath}/hello`, + common.mustCall((err, data) => { + assert.strictEqual(err, null); + assert.ok(data[0][0].includes('hellorandom.txt')); + assert.ok(data[0][1].includes('helloworld.js')); + }) + ); + + replServer.complete( + `${fixturePath}/.h`, + common.mustCall((err, data) => { + assert.strictEqual(err, null); + assert.ok(data[0][0].includes('.hiddenfiles')); + }) + ); + + replServer.complete( + `${readFileSync}./xxxRandom/random`, + common.mustCall((err, data) => { + assert.strictEqual(err, null); + assert.strictEqual(data[0].length, 0); + }) + ); + + const testPath = fixturePath.slice(0, -1); + replServer.complete( + testPath, + common.mustCall((err, data) => { + assert.strictEqual(err, null); + assert.ok(data[0][0].includes('test-repl-tab-completion')); + assert.strictEqual(data[1], path.basename(testPath)); + }) + ); + }); + } +} diff --git a/test/js/node/test/parallel/test-repl-tab-complete-import.js b/test/js/node/test/parallel/test-repl-tab-complete-import.js new file mode 100644 index 000000000000..ed8a6c2de5ef --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-import.js @@ -0,0 +1,144 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { builtinModules } = require('module'); +const publicUnprefixedModules = builtinModules.filter((lib) => !lib.startsWith('_') && !lib.startsWith('node:')); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('process.chdir is not available in Workers'); +} + +// We have to change the directory to ../fixtures before requiring repl +// in order to make the tests for completion of node_modules work properly +// since repl modifies module.paths. +process.chdir(fixtures.fixturesDir); + +const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); + +const { replServer, input } = startNewREPLServer(); + +// Tab complete provides built in libs for import() +replServer.complete('import(\'', common.mustSucceed((data) => { + publicUnprefixedModules.forEach((lib) => { + assert( + data[0].includes(lib) && data[0].includes(`node:${lib}`), + `${lib} not found`, + ); + }); + const newModule = 'foobar'; + assert(!builtinModules.includes(newModule)); + repl.builtinModules.push(newModule); + replServer.complete('import(\'', common.mustSucceed(([modules]) => { + assert.strictEqual(data[0].length + 1, modules.length); + assert(modules.includes(newModule) && + !modules.includes(`node:${newModule}`)); + })); +})); + +replServer.complete("import\t( 'n", common.mustSucceed((data) => { + assert.strictEqual(data.length, 2); + assert.strictEqual(data[1], 'n'); + const completions = data[0]; + // import(...) completions include `node:` URL modules: + let lastIndex = -1; + + publicUnprefixedModules.forEach((lib, index) => { + lastIndex = completions.indexOf(`node:${lib}`); + assert.notStrictEqual(lastIndex, -1); + }); + assert.strictEqual(completions[lastIndex + 1], ''); + // There is only one Node.js module that starts with n: + assert.strictEqual(completions[lastIndex + 2], 'net'); + assert.strictEqual(completions[lastIndex + 3], ''); + // It's possible to pick up non-core modules too + for (const completion of completions.slice(lastIndex + 4)) { + assert.match(completion, /^n/); + } +})); + +{ + const expected = ['@nodejsscope', '@nodejsscope/']; + // Import calls should handle all types of quotation marks. + for (const quotationMark of ["'", '"', '`']) { + input.run(['.clear']); + replServer.complete('import(`@nodejs', common.mustSucceed((data) => { + assert.deepStrictEqual(data, [expected, '@nodejs']); + })); + + input.run(['.clear']); + // Completions should not be greedy in case the quotation ends. + replServer.complete(`import(${quotationMark}@nodejsscope${quotationMark}`, common.mustSucceed((data) => { + assert.deepStrictEqual(data, [[], undefined]); + })); + } +} + +{ + input.run(['.clear']); + // Completions should find modules and handle whitespace after the opening + // bracket. + replServer.complete('import \t("no_ind', common.mustSucceed((data) => { + assert.deepStrictEqual(data, [['no_index', 'no_index/'], 'no_ind']); + })); +} + +// Test tab completion for import() relative to the current directory +{ + input.run(['.clear']); + + const cwd = process.cwd(); + process.chdir(__dirname); + + ['import(\'.', 'import(".'].forEach((input) => { + replServer.complete(input, common.mustSucceed((data) => { + assert.strictEqual(data.length, 2); + assert.strictEqual(data[1], '.'); + assert.strictEqual(data[0].length, 2); + assert.ok(data[0].includes('./')); + assert.ok(data[0].includes('../')); + })); + }); + + ['import(\'..', 'import("..'].forEach((input) => { + replServer.complete(input, common.mustSucceed((data) => { + assert.deepStrictEqual(data, [['../'], '..']); + })); + }); + + ['./', './test-'].forEach((path) => { + [`import('${path}`, `import("${path}`].forEach((input) => { + replServer.complete(input, common.mustSucceed((data) => { + assert.strictEqual(data.length, 2); + assert.strictEqual(data[1], path); + assert.ok(data[0].includes('./test-repl-tab-complete.js')); + })); + }); + }); + + ['../parallel/', '../parallel/test-'].forEach((path) => { + [`import('${path}`, `import("${path}`].forEach((input) => { + replServer.complete(input, common.mustSucceed((data) => { + assert.strictEqual(data.length, 2); + assert.strictEqual(data[1], path); + assert.ok(data[0].includes('../parallel/test-repl-tab-complete.js')); + })); + }); + }); + + { + const path = '../fixtures/repl-folder-extensions/f'; + replServer.complete(`import('${path}`, common.mustSucceed((data) => { + assert.strictEqual(data.length, 2); + assert.strictEqual(data[1], path); + assert.ok(data[0].includes( + '../fixtures/repl-folder-extensions/foo.js/')); + })); + } + + process.chdir(cwd); +} diff --git a/test/js/node/test/parallel/test-repl-tab-complete-nested-repls.js b/test/js/node/test/parallel/test-repl-tab-complete-nested-repls.js new file mode 100644 index 000000000000..3cac02f20562 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-nested-repls.js @@ -0,0 +1,23 @@ +// Tab completion sometimes uses a separate REPL instance under the hood. +// That REPL instance has its own domain. Make sure domain errors trickle back +// up to the main REPL. +// +// Ref: https://github.com/nodejs/node/issues/21586 + +'use strict'; + +require('../common'); +const fixtures = require('../common/fixtures'); + +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +const testFile = fixtures.path('repl-tab-completion-nested-repls.js'); +const result = spawnSync(process.execPath, [testFile]); + +// The spawned process will fail. In Node.js 10.11.0, it will fail silently. The +// test here is to make sure that the error information bubbles up to the +// calling process. +assert.ok(result.status, 'testFile swallowed its error'); +const err = result.stderr.toString(); +assert.ok(err.includes('fhqwhgads'), err); diff --git a/test/js/node/test/parallel/test-repl-tab-complete-new-expression.js b/test/js/node/test/parallel/test-repl-tab-complete-new-expression.js new file mode 100644 index 000000000000..ef269f44b769 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-new-expression.js @@ -0,0 +1,41 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); +const { describe, it } = require('node:test'); + +// This test verifies that tab completion works correctly with `new` operator +// for a class. Property access has higher precedence than `new` so the properties +// should be displayed as autocompletion result. + +describe('REPL tab completion with new expressions', () => { + it('should output completion of class properties', () => { + const { replServer, input } = startNewREPLServer({ terminal: false }); + + input.run([ + ` + class X { x = 1 }; + X.Y = class Y { y = 2 }; + `, + ]); + + // Handle completion for property of root class. + replServer.complete( + 'new X.', + common.mustSucceed((completions) => { + assert.strictEqual(completions[1], 'X.'); + }) + ); + + // Handle completion for property with another class as value. + replServer.complete( + 'new X.Y.', + common.mustSucceed((completions) => { + assert.strictEqual(completions[1], 'X.Y.'); + }) + ); + + replServer.close(); + }); +}); diff --git a/test/js/node/test/parallel/test-repl-tab-complete-no-warn.js b/test/js/node/test/parallel/test-repl-tab-complete-no-warn.js new file mode 100644 index 000000000000..df995aba6a4e --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-no-warn.js @@ -0,0 +1,17 @@ +'use strict'; + +const common = require('../common'); +const { startNewREPLServer } = require('../common/repl'); +const DEFAULT_MAX_LISTENERS = require('events').defaultMaxListeners; + +const { replServer, input } = startNewREPLServer(); + +// https://github.com/nodejs/node/issues/18284 +// Tab-completion should not repeatedly add the +// `Runtime.executionContextCreated` listener +process.on('warning', common.mustNotCall()); + +input.run(['async function test() {']); +for (let i = 0; i < DEFAULT_MAX_LISTENERS; i++) { + replServer.complete('await Promise.resolve()', () => {}); +} diff --git a/test/js/node/test/parallel/test-repl-tab-complete-nosideeffects.js b/test/js/node/test/parallel/test-repl-tab-complete-nosideeffects.js new file mode 100644 index 000000000000..54562e2f1d29 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-nosideeffects.js @@ -0,0 +1,39 @@ +'use strict'; + +const common = require('../common'); +const { describe, it } = require('node:test'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +function getNoResultsFunction() { + return common.mustSucceed((data) => { + assert.deepStrictEqual(data[0], []); + }); +} + +describe('REPL tab completion without side effects', () => { + const setup = [ + 'globalThis.counter = 0;', + 'function incCounter() { return counter++; }', + 'const arr = [{ bar: "baz" }];', + ]; + // None of these expressions should affect the value of `counter` + for (const code of [ + 'incCounter().', + 'a=(counter+=1).foo.', + 'a=(counter++).foo.', + 'for((counter)of[1])foo.', + 'for((counter)in{1:1})foo.', + 'arr[incCounter()].b', + ]) { + it(`does not evaluate with side effects (${code})`, async () => { + const { replServer, input } = startNewREPLServer(); + input.run(setup); + + replServer.complete(code, getNoResultsFunction()); + + assert.strictEqual(replServer.context.counter, 0); + replServer.close(); + }); + } +}); diff --git a/test/js/node/test/parallel/test-repl-tab-complete-on-editor-mode.js b/test/js/node/test/parallel/test-repl-tab-complete-on-editor-mode.js new file mode 100644 index 000000000000..6e2ef8b5670d --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-on-editor-mode.js @@ -0,0 +1,35 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +// Tab completion in editor mode +{ + const { replServer, input } = startNewREPLServer(); + + input.run(['.clear', '.editor']); + + replServer.completer('Uin', common.mustCall((_error, data) => { + assert.deepStrictEqual(data, [['Uint'], 'Uin']); + })); + + input.run(['.clear', '.editor']); + + replServer.completer('var log = console.l', common.mustCall((_error, data) => { + assert.deepStrictEqual(data, [['console.log'], 'console.l']); + })); +} + +// Regression test for https://github.com/nodejs/node/issues/43528 +{ + const { replServer } = startNewREPLServer(); + + // Editor mode + replServer.write('.editor\n'); + + replServer.write('a'); + replServer.write(null, { name: 'tab' }); // Should not throw + + replServer.close(); +} diff --git a/test/js/node/test/parallel/test-repl-tab-complete-require.js b/test/js/node/test/parallel/test-repl-tab-complete-require.js new file mode 100644 index 000000000000..47c03d8d5990 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-require.js @@ -0,0 +1,196 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const { builtinModules } = require('module'); +const publicModules = builtinModules.filter((lib) => !lib.startsWith('_')); + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('process.chdir is not available in Workers'); +} + +// We have to change the directory to ../fixtures before requiring repl +// in order to make the tests for completion of node_modules work properly +// since repl modifies module.paths. +process.chdir(fixtures.fixturesDir); + +const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); + +// Tab completion on require on builtin modules works +{ + const { replServer } = startNewREPLServer(); + + replServer.complete( + "require('", + common.mustCall(function(error, data) { + assert.strictEqual(error, null); + publicModules.forEach((lib) => { + assert( + data[0].includes(lib) && + (lib.startsWith('node:') || data[0].includes(`node:${lib}`)), + `${lib} not found` + ); + }); + const newModule = 'foobar'; + assert(!builtinModules.includes(newModule)); + repl.builtinModules.push(newModule); + replServer.complete( + "require('", + common.mustCall((_, [modules]) => { + assert.strictEqual(data[0].length + 1, modules.length); + assert(modules.includes(newModule)); + }) + ); + }) + ); +} + +// Tab completion on require on builtin modules works (with extra spaces and "n" prefix) +{ + const { replServer } = startNewREPLServer(); + + replServer.complete( + "require\t( 'n", + common.mustCall(function(error, data) { + assert.strictEqual(error, null); + assert.strictEqual(data.length, 2); + assert.strictEqual(data[1], 'n'); + // require(...) completions include `node:`-prefixed modules: + let lastIndex = -1; + + for (const lib of publicModules.filter((lib) => !lib.startsWith('node:'))) { + lastIndex = data[0].indexOf(`node:${lib}`); + assert.notStrictEqual(lastIndex, -1); + } + assert.strictEqual(data[0][lastIndex + 1], ''); + // There is only one Node.js module that starts with n: + assert.strictEqual(data[0][lastIndex + 2], 'net'); + assert.strictEqual(data[0][lastIndex + 3], ''); + // It's possible to pick up non-core modules too + for (const completion of data[0].slice(lastIndex + 4)) { + assert.match(completion, /^n/); + } + }) + ); +} + +// Tab completion on require on external modules works +{ + const expected = ['@nodejsscope', '@nodejsscope/']; + + const { replServer } = startNewREPLServer(); + + // Require calls should handle all types of quotation marks. + for (const quotationMark of ["'", '"', '`']) { + replServer.complete( + 'require(`@nodejs', + common.mustCall((err, data) => { + assert.strictEqual(err, null); + assert.deepStrictEqual(data, [expected, '@nodejs']); + }) + ); + + // Completions should not be greedy in case the quotation ends. + const input = `require(${quotationMark}@nodejsscope${quotationMark}`; + replServer.complete( + input, + common.mustCall((err, data) => { + assert.strictEqual(err, null); + assert.deepStrictEqual(data, [[], undefined]); + }) + ); + } +} + +{ + // Completions should find modules and handle whitespace after the opening bracket. + const { replServer } = startNewREPLServer(); + + replServer.complete( + 'require \t("no_ind', + common.mustCall((err, data) => { + assert.strictEqual(err, null); + assert.deepStrictEqual(data, [['no_index', 'no_index/'], 'no_ind']); + }) + ); +} + +// Test tab completion for require() relative to the current directory +{ + const { replServer } = startNewREPLServer(); + + const cwd = process.cwd(); + process.chdir(__dirname); + + ["require('.", 'require(".'].forEach((input) => { + replServer.complete( + input, + common.mustCall((err, data) => { + assert.strictEqual(err, null); + assert.strictEqual(data.length, 2); + assert.strictEqual(data[1], '.'); + assert.strictEqual(data[0].length, 2); + assert.ok(data[0].includes('./')); + assert.ok(data[0].includes('../')); + }) + ); + }); + + ["require('..", 'require("..'].forEach((input) => { + replServer.complete( + input, + common.mustCall((err, data) => { + assert.strictEqual(err, null); + assert.deepStrictEqual(data, [['../'], '..']); + }) + ); + }); + + ['./', './test-'].forEach((path) => { + [`require('${path}`, `require("${path}`].forEach((input) => { + replServer.complete( + input, + common.mustCall((err, data) => { + assert.strictEqual(err, null); + assert.strictEqual(data.length, 2); + assert.strictEqual(data[1], path); + assert.ok(data[0].includes('./test-repl-tab-complete')); + }) + ); + }); + }); + + ['../parallel/', '../parallel/test-'].forEach((path) => { + [`require('${path}`, `require("${path}`].forEach((input) => { + replServer.complete( + input, + common.mustCall((err, data) => { + assert.strictEqual(err, null); + assert.strictEqual(data.length, 2); + assert.strictEqual(data[1], path); + assert.ok(data[0].includes('../parallel/test-repl-tab-complete')); + }) + ); + }); + }); + + { + const path = '../fixtures/repl-folder-extensions/f'; + replServer.complete( + `require('${path}`, + common.mustSucceed((data) => { + assert.strictEqual(data.length, 2); + assert.strictEqual(data[1], path); + assert.ok( + data[0].includes('../fixtures/repl-folder-extensions/foo.js') + ); + }) + ); + } + + process.chdir(cwd); +} diff --git a/test/js/node/test/parallel/test-repl-tab-complete-unary-expressions.js b/test/js/node/test/parallel/test-repl-tab-complete-unary-expressions.js new file mode 100644 index 000000000000..2b09ae651d25 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-unary-expressions.js @@ -0,0 +1,116 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); +const { describe, it } = require('node:test'); + +// This test verifies that tab completion works correctly with unary expressions +// like delete, typeof, void, etc. This is a regression test for the issue where +// typing "delete globalThis._" and then backspacing and typing "globalThis" +// would cause "globalThis is not defined" error. + +describe('REPL tab completion with unary expressions', () => { + it('should handle delete operator correctly', (t, done) => { + const { replServer } = startNewREPLServer({ terminal: false }); + + // Test delete with member expression + replServer.complete( + 'delete globalThis._', + common.mustSucceed((completions) => { + assert.strictEqual(completions[1], 'globalThis._'); + + // Test delete with identifier + replServer.complete( + 'delete globalThis', + common.mustSucceed((completions) => { + assert.strictEqual(completions[1], 'globalThis'); + replServer.close(); + done(); + }) + ); + }) + ); + }); + + it('should handle typeof operator correctly', (t, done) => { + const { replServer } = startNewREPLServer({ terminal: false }); + + replServer.complete( + 'typeof globalThis', + common.mustSucceed((completions) => { + assert.strictEqual(completions[1], 'globalThis'); + replServer.close(); + done(); + }) + ); + }); + + it('should handle void operator correctly', (t, done) => { + const { replServer } = startNewREPLServer({ terminal: false }); + + replServer.complete( + 'void globalThis', + common.mustSucceed((completions) => { + assert.strictEqual(completions[1], 'globalThis'); + replServer.close(); + done(); + }) + ); + }); + + it('should handle other unary operators correctly', (t, done) => { + const { replServer } = startNewREPLServer({ terminal: false }); + + const unaryOperators = [ + '!globalThis', + '+globalThis', + '-globalThis', + '~globalThis', + ]; + + let testIndex = 0; + + function testNext() { + if (testIndex >= unaryOperators.length) { + replServer.close(); + done(); + return; + } + + const testCase = unaryOperators[testIndex++]; + replServer.complete( + testCase, + common.mustSucceed((completions) => { + assert.strictEqual(completions[1], 'globalThis'); + testNext(); + }) + ); + } + + testNext(); + }); + + it('should still evaluate globalThis correctly after unary expression completion', (t, done) => { + const { replServer } = startNewREPLServer({ terminal: false }); + + // First trigger completion with delete + replServer.complete( + 'delete globalThis._', + common.mustSucceed(() => { + // Then evaluate globalThis + replServer.eval( + 'globalThis', + replServer.context, + 'test.js', + common.mustSucceed((result) => { + assert.strictEqual(typeof result, 'object'); + assert.ok(result !== null); + replServer.close(); + done(); + }) + ); + }) + ); + }); +}); diff --git a/test/js/node/test/parallel/test-repl-tab-complete.js b/test/js/node/test/parallel/test-repl-tab-complete.js new file mode 100644 index 000000000000..d4df6c317879 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete.js @@ -0,0 +1,565 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +'use strict'; + +const common = require('../common'); +const { startNewREPLServer } = require('../common/repl'); +const { describe, it } = require('node:test'); +const assert = require('assert'); + +function getNoResultsFunction() { + return common.mustSucceed((data) => { + assert.deepStrictEqual(data[0], []); + }); +} + +describe('REPL tab completion (core functionality)', () => { + it('does not break with variable declarations without an initialization', () => { + const { replServer } = startNewREPLServer(); + replServer.complete('let a', getNoResultsFunction()); + replServer.close(); + }); + + it('does not break in an object literal', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var inner = {', 'one:1']); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.complete( + 'console.lo', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['console.log'], 'console.lo']); + }) + ); + + replServer.close(); + }); + + it('works with optional chaining', () => { + const { replServer } = startNewREPLServer(); + + replServer.complete( + 'console?.lo', + common.mustCall((_error, data) => { + assert.deepStrictEqual(data, [['console?.log'], 'console?.lo']); + }) + ); + + replServer.complete( + 'console?.zzz', + common.mustCall((_error, data) => { + assert.deepStrictEqual(data, [[], 'console?.zzz']); + }) + ); + + replServer.complete( + 'console?.', + common.mustCall((_error, data) => { + assert(data[0].includes('console?.log')); + assert.strictEqual(data[1], 'console?.'); + }) + ); + + replServer.close(); + }); + + it('returns object completions', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var inner = {', 'one:1']); + + input.run(['};']); + + replServer.complete( + 'inner.o', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['inner.one'], 'inner.o']); + }) + ); + + replServer.close(); + }); + + it('does not break in a ternary operator with ()', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var inner = ( true ', '?', '{one: 1} : ']); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.close(); + }); + + it('works on literals', () => { + const { replServer } = startNewREPLServer(); + + replServer.complete( + '``.a', + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes('``.at'), true); + }) + ); + replServer.complete( + "''.a", + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes("''.at"), true); + }) + ); + replServer.complete( + '"".a', + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes('"".at'), true); + }) + ); + replServer.complete( + '("").a', + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes('("").at'), true); + }) + ); + replServer.complete( + '[].a', + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes('[].at'), true); + }) + ); + replServer.complete( + '{}.a', + common.mustCall((err, data) => { + assert.deepStrictEqual(data[0], []); + }) + ); + + replServer.close(); + }); + + it("does not return a function's local variable", () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var top = function() {', 'var inner = {one:1};', '}']); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.close(); + }); + + it("does not return a function's local variable even when the function has parameters", () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'var top = function(one, two) {', + 'var inner = {', + ' one:1', + '};', + ]); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.close(); + }); + + it("does not return a function's local variable" + + 'even if the scope is nested inside an immediately executed function', () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'var top = function() {', + '(function test () {', + 'var inner = {', + ' one:1', + '};', + ]); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.close(); + }); + + it("does not return a function's local variable" + + 'even if the scope is nested inside an immediately executed function' + + '(the definition has the params and { on a separate line)', () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'var top = function() {', + 'r = function test (', + ' one, two) {', + 'var inner = {', + ' one:1', + '};', + ]); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.close(); + }); + + it('currently does not work, but should not break (local inner)', () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'var top = function() {', + 'r = function test ()', + '{', + 'var inner = {', + ' one:1', + '};', + ]); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.close(); + }); + + it('currently does not work, but should not break (local inner parens next line)', () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'var top = function() {', + 'r = function test (', + ')', + '{', + 'var inner = {', + ' one:1', + '};', + ]); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.close(); + }); + + it('works on non-Objects', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var str = "test";']); + + replServer.complete( + 'str.len', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['str.length'], 'str.len']); + }) + ); + + replServer.close(); + }); + + it('should be case-insensitive if member part is lower-case', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };']); + + replServer.complete( + 'foo.b', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [ + ['foo.BARbuz', 'foo.barBLA', 'foo.barBar'], + 'foo.b', + ]); + }) + ); + + replServer.close(); + }); + + it('should be case-insensitive if member part is upper-case', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };']); + + replServer.complete( + 'foo.B', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [ + ['foo.BARbuz', 'foo.barBLA', 'foo.barBar'], + 'foo.B', + ]); + }) + ); + + replServer.close(); + }); + + it('should not break on spaces', () => { + const { replServer } = startNewREPLServer(); + + const spaceTimeout = setTimeout(function() { + throw new Error('timeout'); + }, 1000); + + replServer.complete( + ' ', + common.mustSucceed((data) => { + assert.strictEqual(data[1], ''); + assert.ok(data[0].includes('globalThis')); + clearTimeout(spaceTimeout); + }) + ); + + replServer.close(); + }); + + it(`should pick up the global "toString" object, and any other properties up the "global" object's prototype chain`, () => { + const { replServer } = startNewREPLServer(); + + replServer.complete( + 'toSt', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['toString'], 'toSt']); + }) + ); + + replServer.close(); + }); + + it('should make own properties shadow properties on the prototype', () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'var x = Object.create(null);', + 'x.a = 1;', + 'x.b = 2;', + 'var y = Object.create(x);', + 'y.a = 3;', + 'y.c = 4;', + ]); + + replServer.complete( + 'y.', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['y.b', '', 'y.a', 'y.c'], 'y.']); + }) + ); + + replServer.close(); + }); + + it('works on context properties', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var custom = "test";']); + + replServer.complete( + 'cus', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['CustomEvent', 'custom'], 'cus']); + }) + ); + + replServer.close(); + }); + + it("doesn't crash REPL with half-baked proxy objects", () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'var proxy = new Proxy({}, {ownKeys: () => { throw new Error(); }});', + ]); + + replServer.complete( + 'proxy.', + common.mustCall(function(error, data) { + assert.strictEqual(error, null); + assert(Array.isArray(data)); + }) + ); + + replServer.close(); + }); + + it('does not include integer members of an Array', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var ary = [1,2,3];']); + + replServer.complete( + 'ary.', + common.mustCall(function(_error, data) { + assert.strictEqual(data[0].includes('ary.0'), false); + assert.strictEqual(data[0].includes('ary.1'), false); + assert.strictEqual(data[0].includes('ary.2'), false); + }) + ); + + replServer.close(); + }); + + it('does not include integer keys in an object', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var obj = {1:"a","1a":"b",a:"b"};']); + + replServer.complete( + 'obj.', + common.mustCall(function(_error, data) { + assert.strictEqual(data[0].includes('obj.1'), false); + assert.strictEqual(data[0].includes('obj.1a'), false); + assert(data[0].includes('obj.a')); + }) + ); + + replServer.close(); + }); + + it('does not try to complete results of non-simple expressions', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['function a() {}']); + + replServer.complete('a().b.', getNoResultsFunction()); + + replServer.close(); + }); + + it('works when prefixed with spaces', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var obj = {1:"a","1a":"b",a:"b"};']); + + replServer.complete( + ' obj.', + common.mustCall((_error, data) => { + assert.strictEqual(data[0].includes('obj.1'), false); + assert.strictEqual(data[0].includes('obj.1a'), false); + assert(data[0].includes('obj.a')); + }) + ); + + replServer.close(); + }); + + it('works inside assignments', () => { + const { replServer } = startNewREPLServer(); + + replServer.complete( + 'var log = console.lo', + common.mustCall((_error, data) => { + assert.deepStrictEqual(data, [['console.log'], 'console.lo']); + }) + ); + + replServer.close(); + }); + + it('works for defined commands', () => { + const { replServer, input } = startNewREPLServer(); + + replServer.complete( + '.b', + common.mustCall((error, data) => { + assert.deepStrictEqual(data, [['break'], 'b']); + }) + ); + + input.run(['var obj = {"hello, world!": "some string", "key": 123}']); + + replServer.complete( + 'obj.', + common.mustCall((error, data) => { + assert.strictEqual(data[0].includes('obj.hello, world!'), false); + assert(data[0].includes('obj.key')); + }) + ); + + replServer.close(); + }); + + it('does not include __defineSetter__ and friends', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var obj = {};']); + + replServer.complete( + 'obj.', + common.mustCall(function(error, data) { + assert.strictEqual(data[0].includes('obj.__defineGetter__'), false); + assert.strictEqual(data[0].includes('obj.__defineSetter__'), false); + assert.strictEqual(data[0].includes('obj.__lookupGetter__'), false); + assert.strictEqual(data[0].includes('obj.__lookupSetter__'), false); + assert.strictEqual(data[0].includes('obj.__proto__'), true); + }) + ); + + replServer.close(); + }); + + it('works with builtin values', () => { + const { replServer } = startNewREPLServer(); + + replServer.complete( + 'I', + common.mustCall((error, data) => { + assert.deepStrictEqual(data, [ + [ + 'if', + 'import', + 'in', + 'instanceof', + '', + 'Infinity', + 'Int16Array', + 'Int32Array', + 'Int8Array', + ...(common.hasIntl ? ['Intl'] : []), + 'Iterator', + 'inspector', + 'isFinite', + 'isNaN', + '', + 'isPrototypeOf', + ], + 'I', + ]); + }) + ); + + replServer.close(); + }); + + it('works with lexically scoped variables', () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'let lexicalLet = true;', + 'const lexicalConst = true;', + 'class lexicalKlass {}', + ]); + + ['Let', 'Const', 'Klass'].forEach((type) => { + const query = `lexical${type[0]}`; + const hasInspector = process.features.inspector; + const expected = hasInspector ? + [[`lexical${type}`], query] : + [[], `lexical${type[0]}`]; + replServer.complete( + query, + common.mustCall((error, data) => { + assert.deepStrictEqual(data, expected); + }) + ); + }); + + replServer.close(); + }); +}); diff --git a/test/js/node/test/parallel/test-repl-tab.js b/test/js/node/test/parallel/test-repl-tab.js new file mode 100644 index 000000000000..710fca9fae2d --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab.js @@ -0,0 +1,13 @@ +'use strict'; +const common = require('../common'); +const repl = require('repl'); +const zlib = require('zlib'); + +// Just use builtin stream inherited from Duplex +const putIn = zlib.createGzip(); +const testMe = repl.start('', putIn, function(cmd, context, filename, + callback) { + callback(null, cmd); +}); + +testMe.complete('', common.mustSucceed()); diff --git a/test/js/node/test/parallel/test-repl-throw-null-or-undefined.js b/test/js/node/test/parallel/test-repl-throw-null-or-undefined.js new file mode 100644 index 000000000000..3b4657ce98c0 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-throw-null-or-undefined.js @@ -0,0 +1,13 @@ +'use strict'; +require('../common'); + +// This test ensures that the repl does not +// crash or emit error when throwing `null|undefined` +// ie `throw null` or `throw undefined`. + +const r = require('repl').start(); + +// Should not throw. +r.write('throw null\n'); +r.write('throw undefined\n'); +r.write('.exit\n'); diff --git a/test/js/node/test/parallel/test-repl-top-level-await.js b/test/js/node/test/parallel/test-repl-top-level-await.js new file mode 100644 index 000000000000..a94ff8e48984 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-top-level-await.js @@ -0,0 +1,230 @@ +'use strict'; + +const common = require('../common'); +const ArrayStream = require('../common/arraystream'); +const assert = require('assert'); +const events = require('events'); +const { stripVTControlCharacters } = require('internal/util/inspect'); +const repl = require('repl'); + +common.skipIfInspectorDisabled(); + +// Flags: --expose-internals + +const PROMPT = 'await repl > '; + +class REPLStream extends ArrayStream { + constructor() { + super(); + this.waitingForResponse = false; + this.lines = ['']; + } + write(chunk, encoding, callback) { + if (Buffer.isBuffer(chunk)) { + chunk = chunk.toString(encoding); + } + const chunkLines = stripVTControlCharacters(chunk).split('\n'); + this.lines[this.lines.length - 1] += chunkLines[0]; + if (chunkLines.length > 1) { + this.lines.push(...chunkLines.slice(1)); + } + this.emit('line', this.lines[this.lines.length - 1]); + if (callback) callback(); + return true; + } + + async wait() { + if (this.waitingForResponse) { + throw new Error('Currently waiting for response to another command'); + } + this.lines = ['']; + for await (const [line] of events.on(this, 'line')) { + if (line.includes(PROMPT)) { + return this.lines; + } + } + } +} + +const putIn = new REPLStream(); +const testMe = repl.start({ + prompt: PROMPT, + stream: putIn, + terminal: true, + useColors: true, + breakEvalOnSigint: true +}); + +function runAndWait(cmds) { + const promise = putIn.wait(); + for (const cmd of cmds) { + if (typeof cmd === 'string') { + putIn.run([cmd]); + } else { + testMe.write('', cmd); + } + } + return promise; +} + +async function ordinaryTests() { + // These tests were created based on + // https://cs.chromium.org/chromium/src/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-top-level-await.js?rcl=5d0ea979f0ba87655b7ef0e03b58fa3c04986ba6 + putIn.run([ + 'function foo(x) { return x; }', + 'function koo() { return Promise.resolve(4); }', + ]); + const testCases = [ + ['await Promise.resolve(0)', '0'], + ['{ a: await Promise.resolve(1) }', '{ a: 1 }'], + ['_', '{ a: 1 }'], + ['let { aa, bb } = await Promise.resolve({ aa: 1, bb: 2 }), f = 5;'], + ['aa', '1'], + ['bb', '2'], + ['f', '5'], + ['let cc = await Promise.resolve(2)'], + ['cc', '2'], + ['let dd;'], + ['dd'], + ['let [ii, { abc: { kk } }] = [0, { abc: { kk: 1 } }];'], + ['ii', '0'], + ['kk', '1'], + ['var ll = await Promise.resolve(2);'], + ['ll', '2'], + ['foo(await koo())', '4'], + ['_', '4'], + ['const m = foo(await koo());'], + ['m', '4'], + ['const n = foo(await\nkoo());', + ['const n = foo(await\r', '| koo());\r', 'undefined']], + ['n', '4'], + // eslint-disable-next-line no-template-curly-in-string + ['`status: ${(await Promise.resolve({ status: 200 })).status}`', + "'status: 200'"], + ['for (let i = 0; i < 2; ++i) await i'], + ['for (let i = 0; i < 2; ++i) { await i }'], + ['await 0', '0'], + ['await 0; function foo() {}'], + ['foo', '[Function: foo]'], + ['class Foo {}; await 1;', '1'], + ['Foo', '[class Foo]'], + ['if (await true) { function bar() {}; }'], + ['bar', '[Function: bar]'], + ['if (await true) { class Bar {}; }'], + ['Bar', 'Uncaught ReferenceError: Bar is not defined'], + ['await 0; function* gen(){}'], + ['for (var i = 0; i < 10; ++i) { await i; }'], + ['i', '10'], + ['for (let j = 0; j < 5; ++j) { await j; }'], + ['j', 'Uncaught ReferenceError: j is not defined', { line: 0 }], + ['gen', '[GeneratorFunction: gen]'], + ['return 42; await 5;', 'Uncaught SyntaxError: Illegal return statement', + { line: 3 }], + ['let o = await 1, p'], + ['p'], + ['let q = 1, s = await 2'], + ['s', '2'], + ['for await (let i of [1,2,3]) console.log(i)', + [ + 'for await (let i of [1,2,3]) console.log(i)\r', + '1', + '2', + '3', + 'undefined', + ], + ], + ['await Promise..resolve()', + [ + 'await Promise..resolve()\r', + 'Uncaught SyntaxError: ', + 'await Promise..resolve()', + ' ^', + '', + 'Unexpected token \'.\'', + ], + ], + ['for (const x of [1,2,3]) {\nawait x\n}', [ + 'for (const x of [1,2,3]) {\r', + '| await x\r', + '| }\r', + 'undefined', + ]], + ['for (const x of [1,2,3]) {\nawait x;\n}', [ + 'for (const x of [1,2,3]) {\r', + '| await x;\r', + '| }\r', + 'undefined', + ]], + ['for await (const x of [1,2,3]) {\nconsole.log(x)\n}', [ + 'for await (const x of [1,2,3]) {\r', + '| console.log(x)\r', + '| }\r', + '1', + '2', + '3', + 'undefined', + ]], + ['for await (const x of [1,2,3]) {\nconsole.log(x);\n}', [ + 'for await (const x of [1,2,3]) {\r', + '| console.log(x);\r', + '| }\r', + '1', + '2', + '3', + 'undefined', + ]], + // Testing documented behavior of `const`s (see: https://github.com/nodejs/node/issues/45918) + ['const k = await Promise.resolve(123)'], + ['k', '123'], + ['k = await Promise.resolve(234)', '234'], + ['k', '234'], + ['const k = await Promise.resolve(345)', "Uncaught SyntaxError: Identifier 'k' has already been declared"], + // Regression test for https://github.com/nodejs/node/issues/43777. + ['await Promise.resolve(123), Promise.resolve(456)', 'Promise { 456 }'], + ['await Promise.resolve(123), await Promise.resolve(456)', '456'], + ['await (Promise.resolve(123), Promise.resolve(456))', '456'], + ]; + + for (const [input, expected = [`${input}\r`], options = {}] of testCases) { + console.log(`Testing ${input}`); + const toBeRun = input.split('\n'); + const lines = await runAndWait(toBeRun); + if (Array.isArray(expected)) { + if (expected.length === 1) + expected.push('undefined'); + if (lines[0] === input) + lines.shift(); + assert.deepStrictEqual(lines, [...expected, PROMPT]); + } else if ('line' in options) { + assert.strictEqual(lines[toBeRun.length + options.line], expected); + } else { + const echoed = toBeRun.map((a, i) => `${i > 0 ? '| ' : ''}${a}\r`); + assert.deepStrictEqual(lines, [...echoed, expected, PROMPT]); + } + } +} + +async function ctrlCTest() { + console.log('Testing Ctrl+C'); + const output = await runAndWait([ + 'await new Promise(() => {})', + { ctrl: true, name: 'c' }, + ]); + assert.deepStrictEqual(output.slice(0, 3), [ + 'await new Promise(() => {})\r', + 'Uncaught:', + '[Error [ERR_SCRIPT_EXECUTION_INTERRUPTED]: ' + + 'Script execution was interrupted by `SIGINT`] {', + ]); + assert.deepStrictEqual(output.slice(-2), [ + '}', + PROMPT, + ]); +} + +async function main() { + await ordinaryTests(); + await ctrlCTest(); +} + +main().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-repl-uncaught-exception-after-input-ended.js b/test/js/node/test/parallel/test-repl-uncaught-exception-after-input-ended.js new file mode 100644 index 000000000000..1e2ca86a9f07 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-uncaught-exception-after-input-ended.js @@ -0,0 +1,23 @@ +'use strict'; +const common = require('../common'); +const { start } = require('node:repl'); +const { PassThrough } = require('node:stream'); +const assert = require('node:assert'); + +// This test verifies that uncaught exceptions in the REPL +// do not bring down the process, even if stdin may already +// have been ended at that point (and the REPL closed as +// a result of that). +const input = new PassThrough(); +const output = new PassThrough().setEncoding('utf8'); +start({ + input, + output, + terminal: false, +}); + +input.end('setImmediate(() => { throw new Error("test"); });\n'); + +setImmediate(common.mustCall(() => { + assert.match(output.read(), /Uncaught Error: test/); +})); diff --git a/test/js/node/test/parallel/test-repl-uncaught-exception-async.js b/test/js/node/test/parallel/test-repl-uncaught-exception-async.js new file mode 100644 index 000000000000..e5373cdaca4d --- /dev/null +++ b/test/js/node/test/parallel/test-repl-uncaught-exception-async.js @@ -0,0 +1,36 @@ +'use strict'; + +// This verifies that adding an `uncaughtException` listener in an REPL instance +// does not suppress errors in the whole application. Adding such listener +// should throw. + +const common = require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +const { replServer, output } = startNewREPLServer({ + prompt: '', + terminal: false, + useColors: false, + global: false, +}); + +replServer.write( + 'process.nextTick(() => {\n' + + ' process.on("uncaughtException", () => console.log("Foo"));\n' + + ' throw new TypeError("foobar");\n' + + '});\n' +); +replServer.write( + 'setTimeout(() => {\n' + + ' throw new RangeError("abc");\n' + + '}, 1);console.log()\n' +); + +setTimeout(common.mustCall(() => { + replServer.close(); + const len = process.listenerCount('uncaughtException'); + process.removeAllListeners('uncaughtException'); + assert.strictEqual(len, 0); + assert.match(output.accumulator, /ERR_INVALID_REPL_INPUT.*(?!Type)RangeError: abc/s); +}), 2); diff --git a/test/js/node/test/parallel/test-repl-uncaught-exception-evalcallback.js b/test/js/node/test/parallel/test-repl-uncaught-exception-evalcallback.js new file mode 100644 index 000000000000..844fce6995aa --- /dev/null +++ b/test/js/node/test/parallel/test-repl-uncaught-exception-evalcallback.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +const { replServer, output } = startNewREPLServer({ + prompt: '', + terminal: false, + useColors: false, + global: false, + eval: common.mustCall((code, context, filename, cb) => { + replServer.setPrompt('prompt! '); + cb(new Error('err')); + }) +}); + +replServer.write('foo\n'); + +// The output includes exactly one post-error prompt. +assert.match(output.accumulator, /prompt!/); +assert.doesNotMatch(output.accumulator, /prompt![\S\s]*prompt!/); +output.on('data', common.mustNotCall()); diff --git a/test/js/node/test/parallel/test-repl-uncaught-exception-standalone.js b/test/js/node/test/parallel/test-repl-uncaught-exception-standalone.js new file mode 100644 index 000000000000..63d619160ba3 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-uncaught-exception-standalone.js @@ -0,0 +1,37 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const child = cp.spawn(process.execPath, ['--interactive']); +let output = ''; + +child.stdout.setEncoding('utf8'); +child.stdout.on('data', (data) => { + output += data; +}); + +child.on('exit', common.mustCall(() => { + const results = output.split('\n'); + results.shift(); + assert.deepStrictEqual( + results, + [ + 'Type ".help" for more information.', + // x\n + '> Uncaught ReferenceError: x is not defined', + // Added `uncaughtException` listener. + '> short', + 'undefined', + // x\n + '> Foobar', + '> ', + ] + ); +})); + +child.stdin.write('x\n'); +child.stdin.write( + 'process.on("uncaughtException", () => console.log("Foobar"));' + + 'console.log("short")\n'); +child.stdin.write('x\n'); +child.stdin.end(); diff --git a/test/js/node/test/parallel/test-repl-uncaught-exception.js b/test/js/node/test/parallel/test-repl-uncaught-exception.js new file mode 100644 index 000000000000..012c7f59ebc8 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-uncaught-exception.js @@ -0,0 +1,70 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +let count = 0; + +function run({ command, expected, useColors = false }) { + const { replServer, output } = startNewREPLServer({ + prompt: '', + terminal: false, + useColors, + }); + + replServer.write(`${command}\n`); + + if (typeof expected === 'string') { + assert.strictEqual(output.accumulator, expected); + } else { + assert.match(output.accumulator, expected); + } + + // Verify that the repl is still working as expected. + output.accumulator = ''; + replServer.write('1 + 1\n'); + // eslint-disable-next-line no-control-regex + assert.strictEqual(output.accumulator.replace(/\u001b\[[0-9]+m/g, ''), '2\n'); + replServer.close(); + count++; +} + +const tests = [ + { + useColors: true, + command: 'x', + expected: 'Uncaught ReferenceError: x is not defined\n' + }, + { + useColors: true, + command: 'throw { foo: "test" }', + expected: "Uncaught { foo: \x1B[32m'test'\x1B[39m }\n" + }, + { + command: 'process.on("uncaughtException", () => console.log("Foobar"));\n', + expected: /^Uncaught:\nTypeError \[ERR_INVALID_REPL_INPUT]: Listeners for `/ + }, + { + command: 'x;\n', + expected: 'Uncaught ReferenceError: x is not defined\n' + }, + { + command: 'process.on("uncaughtException", () => console.log("Foobar"));' + + 'console.log("Baz");\n', + expected: /^Uncaught:\nTypeError \[ERR_INVALID_REPL_INPUT]: Listeners for `/ + }, + { + command: 'console.log("Baz");' + + 'process.on("uncaughtException", () => console.log("Foobar"));\n', + expected: /^Baz\nUncaught:\nTypeError \[ERR_INVALID_REPL_INPUT]:.*uncaughtException/ + }, +]; + +process.on('exit', () => { + // To actually verify that the test passed we have to make sure no + // `uncaughtException` listeners exist anymore. + process.removeAllListeners('uncaughtException'); + assert.strictEqual(count, tests.length); +}); + +tests.forEach(run); diff --git a/test/js/node/test/parallel/test-repl-underscore.js b/test/js/node/test/parallel/test-repl-underscore.js new file mode 100644 index 000000000000..c9ae7ca0e7ca --- /dev/null +++ b/test/js/node/test/parallel/test-repl-underscore.js @@ -0,0 +1,212 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const repl = require('repl'); +const { startNewREPLServer } = require('../common/repl'); + +const testingReplPrompt = '_REPL_TESTING_PROMPT_>'; + +testSloppyMode(); +testStrictMode(); +testResetContext(); +testResetContextGlobal(); +testError(); + +function testSloppyMode() { + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + mode: repl.REPL_MODE_SLOPPY, + }); + + // Cannot use `let` in sloppy mode + replServer.write(`_; // initial value undefined + var x = 10; // evaluates to undefined + _; // still undefined + y = 10; // evaluates to 10 + _; // 10 from last eval + _ = 20; // explicitly set to 20 + _; // 20 from user input + _ = 30; // make sure we can set it twice and no prompt + _; // 30 from user input + y = 40; // make sure eval doesn't change _ + _; // remains 30 from user input + `); + + assertOutput(output, [ + 'undefined', + 'undefined', + 'undefined', + '10', + '10', + 'Expression assignment to _ now disabled.', + '20', + '20', + '30', + '30', + '40', + '30', + ]); +} + +function testStrictMode() { + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + mode: repl.REPL_MODE_STRICT, + }); + + replServer.write(`_; // initial value undefined + var x = 10; // evaluates to undefined + _; // still undefined + let _ = 20; // use 'let' only in strict mode - evals to undefined + _; // 20 from user input + _ = 30; // make sure we can set it twice and no prompt + _; // 30 from user input + var y = 40; // make sure eval doesn't change _ + _; // remains 30 from user input + function f() { let _ = 50; } // undefined + f(); // undefined + _; // remains 30 from user input + `); + + assertOutput(output, [ + 'undefined', + 'undefined', + 'undefined', + 'undefined', + '20', + '30', + '30', + 'undefined', + '30', + 'undefined', + 'undefined', + '30', + ]); +} + +function testResetContext() { + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + }); + + replServer.write(`_ = 10; // explicitly set to 10 + _; // 10 from user input + .clear // Clearing context... + _; // remains 10 + x = 20; // but behavior reverts to last eval + _; // expect 20 + `); + + assertOutput(output, [ + 'Expression assignment to _ now disabled.', + '10', + '10', + 'Clearing context...', + '10', + '20', + '20', + ]); +} + +function testResetContextGlobal() { + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + useGlobal: true, + }); + + replServer.write(`_ = 10; // explicitly set to 10 + _; // 10 from user input + .clear // No output because useGlobal is true + _; // remains 10 + `); + + assertOutput(output, [ + 'Expression assignment to _ now disabled.', + '10', + '10', + '10', + ]); + + // Delete globals leaked by REPL when `useGlobal` is `true` + delete globalThis.module; + delete globalThis.require; +} + +function testError() { + const { replServer, output } = startNewREPLServer({ + prompt: testingReplPrompt, + replMode: repl.REPL_MODE_STRICT, + preview: false, + }); + + replServer.write(`_error; // initial value undefined + throw new Error('foo'); // throws error + _error; // shows error + fs.readdirSync('/nonexistent?'); // throws error, sync + _error.code; // shows error code + _error.syscall; // shows error syscall + setImmediate(() => { throw new Error('baz'); }); undefined; + // throws error, async + `); + + setImmediate(common.mustCall(() => { + const lines = output.accumulator.trim().split('\n').filter( + (line) => !line.includes(testingReplPrompt) || line.includes('Uncaught Error') + ); + const expectedLines = [ + 'undefined', + + // The error, both from the original throw and the `_error` echo. + 'Uncaught Error: foo', + '[Error: foo]', + + // The sync error, with individual property echoes + /^Uncaught Error: ENOENT: no such file or directory, scandir '.*nonexistent\?'/, + /Object\.readdirSync/, + /^ {2}errno: -(2|4058),$/, + " code: 'ENOENT',", + " syscall: 'scandir',", + /^ {2}path: '*'/, + '}', + "'ENOENT'", + "'scandir'", + + // Dummy 'undefined' from the explicit silencer + one from the comment + 'undefined', + 'undefined', + + // The message from the original throw + /Uncaught Error: baz/, + ]; + for (const line of lines) { + const expected = expectedLines.shift(); + if (typeof expected === 'string') + assert.strictEqual(line, expected); + else + assert.match(line, expected); + } + assert.strictEqual(expectedLines.length, 0); + + // Reset output, check that '_error' is the asynchronously caught error. + output.accumulator = ''; + replServer.write(`_error.message // show the message + _error = 0; // disable auto-assignment + throw new Error('quux'); // new error + _error; // should not see the new error + `); + + assertOutput(output, [ + "'baz'", + 'Expression assignment to _error now disabled.', + '0', + 'Uncaught Error: quux', + '0', + ]); + })); +} + +function assertOutput(output, expected) { + const lines = output.accumulator.trim().split('\n').filter((line) => !line.includes(testingReplPrompt)); + assert.deepStrictEqual(lines, expected); +} diff --git a/test/js/node/test/parallel/test-repl-unexpected-token-recoverable.js b/test/js/node/test/parallel/test-repl-unexpected-token-recoverable.js new file mode 100644 index 000000000000..373f5fcf61cb --- /dev/null +++ b/test/js/node/test/parallel/test-repl-unexpected-token-recoverable.js @@ -0,0 +1,33 @@ +'use strict'; + +// This is a regression test for https://github.com/joyent/node/issues/8874. + +const common = require('../common'); +const assert = require('assert'); + +const spawn = require('child_process').spawn; +// Use -i to force node into interactive mode, despite stdout not being a TTY +const args = [ '--interactive' ]; +const child = spawn(process.execPath, args); + +const input = 'const foo = "bar\\\nbaz"'; +// Match '|' as well since it marks a multi-line statement +const expectOut = /> \| undefined\n/; + +child.stderr.setEncoding('utf8'); +child.stderr.on('data', (d) => { + throw new Error('child.stderr be silent'); +}); + +child.stdout.setEncoding('utf8'); +let out = ''; +child.stdout.on('data', (d) => { + out += d; +}); + +child.stdout.on('end', common.mustCall(() => { + assert.match(out, expectOut); + console.log('ok'); +})); + +child.stdin.end(input); diff --git a/test/js/node/test/parallel/test-repl-unsafe-array-iteration.js b/test/js/node/test/parallel/test-repl-unsafe-array-iteration.js new file mode 100644 index 000000000000..3fc65f54cf1f --- /dev/null +++ b/test/js/node/test/parallel/test-repl-unsafe-array-iteration.js @@ -0,0 +1,68 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { spawn } = require('child_process'); + +const replProcess = spawn(process.argv0, ['--interactive'], { + stdio: ['pipe', 'pipe', 'inherit'], + windowsHide: true, +}); + +replProcess.on('error', common.mustNotCall()); + +const replReadyState = (async function* () { + let ready; + const SPACE = ' '.charCodeAt(); + const BRACKET = '>'.charCodeAt(); + const DOT = '.'.charCodeAt(); + replProcess.stdout.on('data', (data) => { + ready = data[data.length - 1] === SPACE && ( + data[data.length - 2] === BRACKET || ( + data[data.length - 2] === DOT && + data[data.length - 3] === DOT && + data[data.length - 4] === DOT + )); + }); + + const processCrashed = new Promise((resolve, reject) => + replProcess.on('exit', reject) + ); + while (true) { + await Promise.race([new Promise(setImmediate), processCrashed]); + if (ready) { + ready = false; + yield; + } + } +})(); +async function writeLn(data, expectedOutput) { + await replReadyState.next(); + if (expectedOutput) { + replProcess.stdout.once('data', common.mustCall((data) => + assert.match(data.toString('utf8'), expectedOutput) + )); + } + await new Promise((resolve, reject) => replProcess.stdin.write( + `${data}\n`, + (err) => (err ? reject(err) : resolve()) + )); +} + +async function main() { + await writeLn( + 'const ArrayIteratorPrototype =' + + ' Object.getPrototypeOf(Array.prototype[Symbol.iterator]());' + ); + await writeLn('delete Array.prototype[Symbol.iterator];'); + await writeLn('delete ArrayIteratorPrototype.next;'); + + await writeLn( + 'for(const x of [3, 2, 1]);', + /Uncaught TypeError: \[3,2,1\] is not iterable/ + ); + await writeLn('.exit'); + + assert(!replProcess.connected); +} + +main().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-repl-unsupported-option.js b/test/js/node/test/parallel/test-repl-unsupported-option.js new file mode 100644 index 000000000000..16de512a7692 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-unsupported-option.js @@ -0,0 +1,11 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +const result = spawnSync(process.execPath, ['--interactive', '--input-type=module']); + +assert.strictEqual(result.stderr.toString(), 'Cannot specify --input-type for REPL\n'); +assert.notStrictEqual(result.exitCode, 0); diff --git a/test/js/node/test/parallel/test-repl-use-global.js b/test/js/node/test/parallel/test-repl-use-global.js new file mode 100644 index 000000000000..06cda54f4d6f --- /dev/null +++ b/test/js/node/test/parallel/test-repl-use-global.js @@ -0,0 +1,81 @@ +'use strict'; + +// Flags: --expose-internals + +const common = require('../common'); +const stream = require('stream'); +const repl = require('internal/repl'); +const assert = require('assert'); + +// Array of [useGlobal, expectedResult] pairs +const globalTestCases = [ + [false, 'undefined'], + [true, '\'tacos\''], + [undefined, 'undefined'], +]; + +const globalTest = (useGlobal, cb, output) => (err, repl) => { + if (err) + return cb(err); + + let str = ''; + output.on('data', (data) => (str += data)); + globalThis.lunch = 'tacos'; + repl.write('globalThis.lunch;\n'); + repl.close(); + delete globalThis.lunch; + cb(null, str.trim()); +}; + +// Test how the global object behaves in each state for useGlobal +for (const [option, expected] of globalTestCases) { + runRepl(option, globalTest, common.mustSucceed((output) => { + assert.strictEqual(output, expected); + })); +} + +// Test how shadowing the process object via `let` +// behaves in each useGlobal state. Note: we can't +// actually test the state when useGlobal is true, +// because the exception that's generated is caught +// (see below), but errors are printed, and the test +// suite is aware of it, causing a failure to be flagged. +// +const processTestCases = [false, undefined]; +const processTest = (useGlobal, cb, output) => (err, repl) => { + if (err) + return cb(err); + + let str = ''; + output.on('data', (data) => (str += data)); + + // If useGlobal is false, then `let process` should work + repl.write('let process;\n'); + repl.write('21 * 2;\n'); + repl.close(); + cb(null, str.trim()); +}; + +for (const option of processTestCases) { + runRepl(option, processTest, common.mustSucceed((output) => { + assert.strictEqual(output, 'undefined\n42'); + })); +} + +function runRepl(useGlobal, testFunc, cb) { + const inputStream = new stream.PassThrough(); + const outputStream = new stream.PassThrough(); + const opts = { + input: inputStream, + output: outputStream, + useGlobal: useGlobal, + useColors: false, + terminal: false, + prompt: '' + }; + + repl.createInternalRepl( + process.env, + opts, + testFunc(useGlobal, cb, opts.output)); +} diff --git a/test/js/node/test/parallel/test-repl-user-error-handler.js b/test/js/node/test/parallel/test-repl-user-error-handler.js new file mode 100644 index 000000000000..31bd46b13d36 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-user-error-handler.js @@ -0,0 +1,84 @@ +'use strict'; +const common = require('../common'); +const { start } = require('node:repl'); +const assert = require('node:assert'); +const { PassThrough } = require('node:stream'); +const { once } = require('node:events'); +const test = require('node:test'); +const { spawn } = require('node:child_process'); + +function* generateCases() { + for (const async of [false, true]) { + for (const handleErrorReturn of ['ignore', 'print', 'unhandled', 'badvalue']) { + if (handleErrorReturn === 'badvalue' && async) { + // Handled through a separate test using a child process + continue; + } + yield { async, handleErrorReturn }; + } + } +} + +for (const { async, handleErrorReturn } of generateCases()) { + test(`async: ${async}, handleErrorReturn: ${handleErrorReturn}`, async () => { + let err; + const options = { + input: new PassThrough(), + output: new PassThrough().setEncoding('utf8'), + handleError: common.mustCall((e) => { + err = e; + queueMicrotask(() => repl.emit('handled-error')); + return handleErrorReturn; + }) + }; + + let uncaughtExceptionEvent; + if (handleErrorReturn === 'unhandled' && async) { + process.removeAllListeners('uncaughtException'); // Remove the test runner's handler + uncaughtExceptionEvent = once(process, 'uncaughtException'); + } + + const repl = start(options); + const inputString = async ? + 'setImmediate(() => { throw new Error("testerror") })\n42\n' : + 'throw new Error("testerror")\n42\n'; + if (handleErrorReturn === 'badvalue') { + assert.throws(() => options.input.end(inputString), /ERR_INVALID_STATE/); + return; + } + options.input.end(inputString); + + await once(repl, 'handled-error'); + assert.strictEqual(err.message, 'testerror'); + const outputString = options.output.read(); + assert.match(outputString, /42/); + + if (handleErrorReturn === 'print') { + assert.match(outputString, /testerror/); + } else { + assert.doesNotMatch(outputString, /testerror/); + } + + if (uncaughtExceptionEvent) { + const [uncaughtErr] = await uncaughtExceptionEvent; + assert.strictEqual(uncaughtErr, err); + } + }); +} + +test('async: true, handleErrorReturn: badvalue', async () => { + // Can't test this the same way as the other combinations + // since this will take the process down in a way that + // cannot be caught. + const proc = spawn(process.execPath, ['-e', ` + require('node:repl').start({ + handleError: () => 'badvalue' + }) + `], { encoding: 'utf8', stdio: 'pipe' }); + proc.stdin.end('throw new Error("foo");'); + let stderr = ''; + proc.stderr.setEncoding('utf8').on('data', (data) => stderr += data); + const [exit] = await once(proc, 'close'); + assert.strictEqual(exit, 1); + assert.match(stderr, /ERR_INVALID_STATE.+badvalue/); +}); diff --git a/test/js/node/test/parallel/test-repl.js b/test/js/node/test/parallel/test-repl.js new file mode 100644 index 000000000000..c325abb6b4ec --- /dev/null +++ b/test/js/node/test/parallel/test-repl.js @@ -0,0 +1,1053 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const net = require('net'); +const repl = require('repl'); +const { inspect } = require('util'); + +const message = 'Read, Eval, Print Loop'; +const prompt_unix = 'node via Unix socket> '; +const prompt_tcp = 'node via TCP socket> '; + +// Absolute path to test/fixtures/a.js +const moduleFilename = fixtures.path('a'); + +// Function for REPL to run +globalThis.invoke_me = function(arg) { + return `invoked ${arg}`; +}; + +// Helpers for describing the expected output: +const kArrow = /^ *\^+ *$/; // Arrow of ^ pointing to syntax error location +const kSource = Symbol('kSource'); // Placeholder standing for input readback + +async function runReplTests(socket, prompt, tests) { + let lineBuffer = ''; + + for (const { send, expect } of tests) { + // Expect can be a single line or multiple lines + const expectedLines = Array.isArray(expect) ? expect : [ expect ]; + + console.error('\n------------'); + console.error('out:', JSON.stringify(send)); + socket.write(`${send}\n`); + + for (let expectedLine of expectedLines) { + // Special value: kSource refers to last sent source text + if (expectedLine === kSource) + expectedLine = send; + + while (!lineBuffer.includes('\n')) { + lineBuffer += await event(socket, expect); + + // Cut away the initial prompt + while (lineBuffer.startsWith(prompt)) + lineBuffer = lineBuffer.slice(prompt.length); + + // Allow to match partial text if no newline was received, because + // sending newlines from the REPL itself would be redundant + // (e.g. in the `| ` multiline prompt: The user already pressed + // enter for that, so the REPL shouldn't do it again!). + if (lineBuffer === expectedLine && !expectedLine.includes('\n')) + lineBuffer += '\n'; + } + + // Split off the current line. + const newlineOffset = lineBuffer.indexOf('\n'); + let actualLine = lineBuffer.slice(0, newlineOffset); + lineBuffer = lineBuffer.slice(newlineOffset + 1); + + // This might have been skipped in the loop above because the buffer + // already contained a \n to begin with and the entire loop was skipped. + while (actualLine.startsWith(prompt)) + actualLine = actualLine.slice(prompt.length); + + console.error('in:', JSON.stringify(actualLine)); + + // Match a string directly, or a RegExp. + if (typeof expectedLine === 'string') { + assert.strictEqual(actualLine, expectedLine); + } else { + assert.match(actualLine, expectedLine); + } + } + } + + const remainder = socket.read(); + assert(remainder === '' || remainder === null); +} + +const unixTests = [ + { + send: '', + expect: '' + }, + { + send: 'message', + expect: `'${message}'` + }, + { + send: 'invoke_me(987)', + expect: '\'invoked 987\'' + }, + { + send: 'a = 12345', + expect: '12345' + }, + { + send: '{a:1}', + expect: '{ a: 1 }' + }, +]; + +const strictModeTests = [ + { + send: 'ref = 1', + expect: [/^Uncaught ReferenceError:\s/] + }, +]; + +const possibleTokensAfterIdentifierWithLineBreak = [ + '(\n)', + '[\n0]', + '+\n1', '- \n1', '* \n1', '/ \n1', '% \n1', '** \n1', + '== \n1', '=== \n1', '!= \n1', '!== \n1', '< \n1', '> \n1', '<= \n1', '>= \n1', + '&& \n1', '|| \n1', '?? \n1', + '= \n1', '+= \n1', '-= \n1', '*= \n1', '/= \n1', '%= \n1', + ': \n', + '? \n1: 1', +]; + +const errorTests = [ + // Uncaught error throws and prints out + { + send: 'throw new Error(\'test error\');', + expect: ['Uncaught Error: test error'] + }, + { + send: "throw { foo: 'bar' };", + expect: "Uncaught { foo: 'bar' }" + }, + // Common syntax error is treated as multiline command + { + send: 'function test_func() {', + expect: '| ' + }, + // You can recover with the .break command + { + send: '.break', + expect: '' + }, + // But passing the same string to eval() should throw + { + send: 'eval("function test_func() {")', + expect: [/^Uncaught SyntaxError: /] + }, + // Can handle multiline template literals + { + send: '`io.js', + expect: '| ' + }, + // Special REPL commands still available + { + send: '.break', + expect: '' + }, + // Template expressions + { + send: '`io.js ${"1.0"', + expect: '| ' + }, + { + send: '+ ".2"}`', + expect: '\'io.js 1.0.2\'' + }, + { + send: '`io.js ${', + expect: '| ' + }, + { + send: '"1.0" + ".2"}`', + expect: '\'io.js 1.0.2\'' + }, + // Dot prefix in multiline commands aren't treated as commands + { + send: '("a"', + expect: '| ' + }, + { + send: '.charAt(0))', + expect: '\'a\'' + }, + // Floating point numbers are not interpreted as REPL commands. + { + send: '.1234', + expect: '0.1234' + }, + // Floating point expressions are not interpreted as REPL commands + { + send: '.1+.1', + expect: '0.2' + }, + // Can parse valid JSON + { + send: 'JSON.parse(\'{"valid": "json"}\');', + expect: '{ valid: \'json\' }' + }, + // Invalid input to JSON.parse error is special case of syntax error, + // should throw + { + send: 'JSON.parse(\'{invalid: \\\'json\\\'}\');', + expect: [ + 'Uncaught:', + /^SyntaxError: /, + ], + }, + // End of input to JSON.parse error is special case of syntax error, + // should throw + { + send: 'JSON.parse(\'066\');', + expect: [/^Uncaught SyntaxError: /] + }, + // should throw + { + send: 'JSON.parse(\'{\');', + expect: [ + 'Uncaught:', + /^SyntaxError: /, + ], + }, + // invalid RegExps are a special case of syntax error, + // should throw + { + send: '/(/;', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + // invalid RegExp modifiers are a special case of syntax error, + // should throw (GH-4012) + { + send: 'new RegExp("foo", "wrong modifier");', + expect: [/^Uncaught SyntaxError: /] + }, + // Strict mode syntax errors should be caught (GH-5178) + { + send: '(function() { "use strict"; return 0755; })()', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '(function(a, a, b) { "use strict"; return a + b + c; })()', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '(function() { "use strict"; with (this) {} })()', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '(function() { "use strict"; var x; delete x; })()', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '(function() { "use strict"; eval = 17; })()', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '(function() { "use strict"; if (true) function f() { } })()', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + /^SyntaxError: /, + ] + }, + // Named functions can be used: + { + send: 'function blah() { return 1; }', + expect: 'undefined' + }, + { + send: 'blah()', + expect: '1' + }, + // Functions should not evaluate twice (#2773) + { + send: 'var I = [1,2,3,function() {}]; I.pop()', + expect: '[Function (anonymous)]' + }, + // Multiline object + { + send: '{}),({}', + expect: '| ', + }, + { + send: '}', + expect: [ + '{}),({}', + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '{ a: ', + expect: '| ' + }, + { + send: '1 }', + expect: '{ a: 1 }' + }, + // Multiline string-keyed object (e.g. JSON) + { + send: '{ "a": ', + expect: '| ' + }, + { + send: '1 }', + expect: '{ a: 1 }' + }, + // Multiline class with private member. + { + send: 'class Foo { #private = true ', + expect: '| ' + }, + // Class field with bigint. + { + send: 'num = 123456789n', + expect: '| ' + }, + // Static class features. + { + send: 'static foo = "bar" }', + expect: 'undefined' + }, + // Multiline anonymous function with comment + { + send: '(function() {', + expect: '| ' + }, + { + send: '// blah', + expect: '| ' + }, + { + send: 'return 1n;', + expect: '| ' + }, + { + send: '})()', + expect: '1n' + }, + // Multiline function call + { + send: 'function f(){}; f(f(1,', + expect: '| ' + }, + { + send: '2)', + expect: '| ' + }, + { + send: ')', + expect: 'undefined' + }, + // `npm` prompt error message. + { + send: 'npm install foobar', + expect: [ + 'npm should be run outside of the Node.js REPL, in your normal shell.', + '(Press Ctrl+D to exit.)', + ] + }, + { + send: 'let npm = () => {};', + expect: 'undefined' + }, + ...possibleTokensAfterIdentifierWithLineBreak.map((token) => ( + { + send: `npm ${token}; undefined`, + expect: '| undefined' + } + )), + { + send: '(function() {\n\nreturn 1;\n})()', + expect: '| | | 1' + }, + { + send: '{\n\na: 1\n}', + expect: '| | | { a: 1 }' + }, + { + send: 'url.format("http://google.com")', + expect: '\'http://google.com/\'' + }, + { + send: 'var path = 42; path', + expect: '42' + }, + // This makes sure that we don't print `undefined` when we actually print + // the error message + { + send: '.invalid_repl_command', + expect: 'Invalid REPL keyword' + }, + // This makes sure that we don't crash when we use an inherited property as + // a REPL command + { + send: '.toString', + expect: 'Invalid REPL keyword' + }, + // Fail when we are not inside a String and a line continuation is used + { + send: '[] \\', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + // Do not fail when a String is created with line continuation + { + send: '\'the\\\nfourth\\\neye\'', + expect: ['| | \'thefourtheye\''] + }, + // Don't fail when a partial String is created and line continuation is used + // with whitespace characters at the end of the string. We are to ignore it. + // This test is to make sure that we properly remove the whitespace + // characters at the end of line, unlike the buggy `trimWhitespace` function + { + send: ' \t .break \t ', + expect: '' + }, + // Multiline strings preserve whitespace characters in them + { + send: '\'the \\\n fourth\t\t\\\n eye \'', + expect: '| | \'the fourth\\t\\t eye \'' + }, + // More than one multiline strings also should preserve whitespace chars + { + send: '\'the \\\n fourth\' + \'\t\t\\\n eye \'', + expect: '| | \'the fourth\\t\\t eye \'' + }, + // using REPL commands within a string literal should still work + { + send: '\'\\\n.break', + expect: '| ' + prompt_unix + }, + // Using REPL command "help" within a string literal should still work + { + send: '\'thefourth\\\n.help\neye\'', + expect: [ + /\.break/, + /\.clear/, + /\.exit/, + /\.help/, + /\.load/, + /\.save/, + '', + 'Press Ctrl+C to abort current expression, Ctrl+D to exit the REPL', + /'thefourtheye'/, + ] + }, + // Check for wrapped objects. + { + send: '{ a: 1 }.a', // ({ a: 1 }.a); + expect: '1' + }, + { + send: '{ a: 1 }.a;', // { a: 1 }.a; + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '{ a: 1 }["a"] === 1', // ({ a: 1 }['a'] === 1); + expect: 'true' + }, + { + send: '{ a: 1 }["a"] === 1;', // { a: 1 }; ['a'] === 1; + expect: 'false' + }, + // Empty lines in the REPL should be allowed + { + send: '\n\r\n\r\n', + expect: '' + }, + // Empty lines in the string literals should not affect the string + { + send: '\'the\\\n\\\nfourtheye\'\n', + expect: '| | \'thefourtheye\'' + }, + // Regression test for https://github.com/nodejs/node/issues/597 + { + send: '/(.)(.)(.)(.)(.)(.)(.)(.)(.)/.test(\'123456789\')\n', + expect: 'true' + }, + // The following test's result depends on the RegExp's match from the above + { + send: 'RegExp.$1\nRegExp.$2\nRegExp.$3\nRegExp.$4\nRegExp.$5\n' + + 'RegExp.$6\nRegExp.$7\nRegExp.$8\nRegExp.$9\n', + expect: ['\'1\'', '\'2\'', '\'3\'', '\'4\'', '\'5\'', '\'6\'', + '\'7\'', '\'8\'', '\'9\''] + }, + // Regression tests for https://github.com/nodejs/node/issues/2749 + { + send: 'function x() {\nreturn \'\\n\';\n }', + expect: '| | undefined' + }, + { + send: 'function x() {\nreturn \'\\\\\';\n }', + expect: '| | undefined' + }, + // Regression tests for https://github.com/nodejs/node/issues/3421 + { + send: 'function x() {\n//\'\n }', + expect: '| | undefined' + }, + { + send: 'function x() {\n//"\n }', + expect: '| | undefined' + }, + { + send: 'function x() {//\'\n }', + expect: '| undefined' + }, + { + send: 'function x() {//"\n }', + expect: '| undefined' + }, + { + send: 'function x() {\nvar i = "\'";\n }', + expect: '| | undefined' + }, + { + send: 'function x(/*optional*/) {}', + expect: 'undefined' + }, + { + send: 'function x(/* // 5 */) {}', + expect: 'undefined' + }, + { + send: '// /* 5 */', + expect: 'undefined' + }, + { + send: '"//"', + expect: '\'//\'' + }, + { + send: '"data /*with*/ comment"', + expect: '\'data /*with*/ comment\'' + }, + { + send: 'function x(/*fn\'s optional params*/) {}', + expect: 'undefined' + }, + { + send: '/* \'\n"\n\'"\'\n*/', + expect: '| | | undefined' + }, + // REPL should get a normal require() function, not one that allows + // access to internal modules without the --expose-internals flag. + { + // Shrink the stack trace to avoid having to update this test whenever the + // implementation of require() changes. It's set to 5 because somehow setting it + // to a lower value breaks the error formatting and the message becomes + // "Uncaught [Error...", which is probably a bug(?). + send: 'Error.stackTraceLimit = 5; require("internal/repl")', + expect: [ + /^Uncaught Error: Cannot find module 'internal\/repl'/, + /^Require stack:/, + /^- /, // This just tests MODULE_NOT_FOUND so let's skip the stack trace + /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. + /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. + /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. + /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. + " code: 'MODULE_NOT_FOUND',", + " requireStack: [ '' ]", + '}', + ] + }, + // REPL should handle quotes within regexp literal in multiline mode + { + send: "function x(s) {\nreturn s.replace(/'/,'');\n}", + expect: '| | undefined' + }, + { + send: "function x(s) {\nreturn s.replace(/'/,'');\n}", + expect: '| | undefined' + }, + { + send: 'function x(s) {\nreturn s.replace(/"/,"");\n}', + expect: '| | undefined' + }, + { + send: 'function x(s) {\nreturn s.replace(/.*/,"");\n}', + expect: '| | undefined' + }, + { + send: '{ var x = 4; }', + expect: 'undefined' + }, + // Illegal token is not recoverable outside string literal, RegExp literal, + // or block comment. https://github.com/nodejs/node/issues/3611 + { + send: 'a = 3.5e', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + // Mitigate https://github.com/nodejs/node/issues/548 + { + send: 'function name(){ return "node"; };name()', + expect: '\'node\'' + }, + { + send: 'function name(){ return "nodejs"; };name()', + expect: '\'nodejs\'' + }, + // Avoid emitting repl:line-number for SyntaxError + { + send: 'a = 3.5e', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + // Avoid emitting stack trace + { + send: 'a = 3.5e', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + + // https://github.com/nodejs/node/issues/9850 + { + send: 'function* foo() {}; foo().next();', + expect: '{ value: undefined, done: true }' + }, + + { + send: 'function *foo() {}; foo().next();', + expect: '{ value: undefined, done: true }' + }, + + { + send: 'function*foo() {}; foo().next();', + expect: '{ value: undefined, done: true }' + }, + + { + send: 'function * foo() {}; foo().next()', + expect: '{ value: undefined, done: true }' + }, + + // https://github.com/nodejs/node/issues/9300 + { + send: 'function foo() {\nvar bar = 1 / 1; // "/"\n}', + expect: '| | undefined' + }, + + { + send: '(function() {\nreturn /foo/ / /bar/;\n}())', + expect: '| | NaN' + }, + + { + send: '(function() {\nif (false) {} /bar"/;\n}())', + expect: '| | undefined' + }, + + // https://github.com/nodejs/node/issues/16483 + { + send: 'new Proxy({x:42}, {get(){throw null}});', + expect: 'Proxy [ { x: 42 }, { get: [Function: get] } ]' + }, + { + send: 'repl.writer.options.showProxy = false, new Proxy({x:42}, {});', + expect: 'Proxy({ x: 42 })' + }, + + // Newline within template string maintains whitespace. + { + send: '`foo \n`', + expect: '| \'foo \\n\'' + }, + // Whitespace is not evaluated. + { + send: ' \t \n', + expect: 'undefined' + }, + // Do not parse `...[]` as a REPL keyword + { + send: '...[]', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + // Bring back the repl to prompt + { + send: '.break', + expect: '' + }, + { + send: 'console.log("Missing comma in arg list" process.version)', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: 'x = {\nfield\n{', + expect: [ + '| | {', + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '(2 + 3))', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: 'if (typeof process === "object"); {', + expect: '| ' + }, + { + send: 'console.log("process is defined");', + expect: '| ' + }, + { + send: '} else {', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: 'console', + expect: [ + 'Object [console] {', + ' log: [Function: log],', + ' info: [Function: info],', + ' debug: [Function: debug],', + ' warn: [Function: warn],', + ' error: [Function: error],', + ' dir: [Function: dir],', + ' time: [Function: time],', + ' timeEnd: [Function: timeEnd],', + ' timeLog: [Function: timeLog],', + ' trace: [Function: trace],', + ' assert: [Function: assert],', + ' clear: [Function: clear],', + ' count: [Function: count],', + ' countReset: [Function: countReset],', + ' group: [Function: group],', + ' groupEnd: [Function: groupEnd],', + ' table: [Function: table],', + / {2}dirxml: \[Function: (dirxml|log)],/, + / {2}groupCollapsed: \[Function: (groupCollapsed|group)],/, + / {2}Console: \[Function: Console],?/, + ...process.features.inspector ? [ + ' profile: [Function: profile],', + ' profileEnd: [Function: profileEnd],', + ' timeStamp: [Function: timeStamp],', + ' context: [Function: context],', + ' createTask: [Function: createTask]', + ] : [], + '}', + ] + }, +]; + +const tcpTests = [ + { + send: '', + expect: '' + }, + { + send: 'invoke_me(333)', + expect: '\'invoked 333\'' + }, + { + send: 'a += 1', + expect: '12346' + }, + { + send: `require(${JSON.stringify(moduleFilename)}).number`, + expect: '42' + }, + { + send: 'import comeOn from \'fhqwhgads\'', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { default: comeOn } = await import("fhqwhgads");', + ] + }, + { + send: 'import { export1, export2 } from "module-name"', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { export1, export2 } = await import("module-name");', + ] + }, + { + send: 'import * as name from "module-name";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const name = await import("module-name");', + ] + }, + { + send: 'import "module-name";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: await import("module-name");', + ] + }, + { + send: 'import { export1 as localName1, export2 } from "bar";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { export1: localName1, export2 } = await import("bar");', + ] + }, + { + send: 'import alias from "bar";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { default: alias } = await import("bar");', + ] + }, + { + send: 'import alias, {namedExport} from "bar";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { default: alias, namedExport } = await import("bar");', + ] + }, +]; + +(async function() { + { + const [ socket, replServer ] = await startUnixRepl(); + + await runReplTests(socket, prompt_unix, unixTests); + await runReplTests(socket, prompt_unix, errorTests); + replServer.replMode = repl.REPL_MODE_STRICT; + await runReplTests(socket, prompt_unix, strictModeTests); + + socket.end(); + } + { + const [ socket ] = await startTCPRepl(); + + await runReplTests(socket, prompt_tcp, tcpTests); + + socket.end(); + } + common.allowGlobals(globalThis.invoke_me, globalThis.message, globalThis.a, globalThis.blah, + globalThis.I, globalThis.f, globalThis.path, globalThis.x, globalThis.name, globalThis.foo); +})().then(common.mustCall()); + +function startTCPRepl() { + let resolveSocket, resolveReplServer; + + const server = net.createServer(common.mustCall((socket) => { + assert.strictEqual(server, socket.server); + + socket.on('end', common.mustCall(() => { + socket.end(); + })); + + resolveReplServer(repl.start(prompt_tcp, socket)); + })); + + server.listen(0, common.mustCall(() => { + const client = net.createConnection(server.address().port); + + client.setEncoding('utf8'); + + client.on('connect', common.mustCall(() => { + assert.strictEqual(client.readable, true); + assert.strictEqual(client.writable, true); + + resolveSocket(client); + })); + + client.on('close', common.mustCall(() => { + server.close(); + })); + })); + + return Promise.all([ + new Promise((resolve) => resolveSocket = resolve), + new Promise((resolve) => resolveReplServer = resolve), + ]); +} + +function startUnixRepl() { + let resolveSocket, resolveReplServer; + + const server = net.createServer(common.mustCall((socket) => { + assert.strictEqual(server, socket.server); + + socket.on('end', common.mustCall(() => { + socket.end(); + })); + + const replServer = repl.start({ + prompt: prompt_unix, + input: socket, + output: socket, + useGlobal: true + }); + replServer.context.message = message; + resolveReplServer(replServer); + })); + + tmpdir.refresh(); + + server.listen(common.PIPE, common.mustCall(() => { + const client = net.createConnection(common.PIPE); + + client.setEncoding('utf8'); + + client.on('connect', common.mustCall(() => { + assert.strictEqual(client.readable, true); + assert.strictEqual(client.writable, true); + + resolveSocket(client); + })); + + client.on('close', common.mustCall(() => { + server.close(); + })); + })); + + return Promise.all([ + new Promise((resolve) => resolveSocket = resolve), + new Promise((resolve) => resolveReplServer = resolve), + ]); +} + +function event(ee, expected) { + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + const data = inspect(expected, { compact: false }); + const msg = `The REPL did not reply as expected for:\n\n${data}`; + reject(new Error(msg)); + }, common.platformTimeout(9999)); + ee.once('data', common.mustCall((...args) => { + clearTimeout(timeout); + resolve(...args); + })); + }); +} diff --git a/test/js/node/test/sequential/test-repl-timeout-throw.js b/test/js/node/test/sequential/test-repl-timeout-throw.js new file mode 100644 index 000000000000..d0cbd6fdca71 --- /dev/null +++ b/test/js/node/test/sequential/test-repl-timeout-throw.js @@ -0,0 +1,59 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const spawn = require('child_process').spawn; + +const child = spawn(process.execPath, [ '--interactive' ], { + stdio: [null, null, 2], +}); + +let stdout = ''; +child.stdout.setEncoding('utf8'); +child.stdout.on('data', function(c) { + process.stdout.write(c); + stdout += c; + if (stdout.includes('> THROW 2')) + child.stdin.end(); +}); + +child.stdin.write = function(original) { + return function(c) { + process.stderr.write(c); + return original.call(child.stdin, c); + }; +}(child.stdin.write); + +child.stdout.once('data', function() { + child.stdin.write('let throws = 0;'); + child.stdin.write('process.on("exit",function(){console.log(throws)});'); + child.stdin.write('function thrower(){console.log("THROW",throws++);XXX};'); + child.stdin.write('setTimeout(thrower);""\n'); + + setTimeout(fsTest, 50); + function fsTest() { + const f = JSON.stringify(__filename); + child.stdin.write(`fs.readFile(${f}, thrower);\n`); + setTimeout(eeTest, 50); + } + + function eeTest() { + child.stdin.write('setTimeout(function() {\n' + + ' const events = require("events");\n' + + ' let e = new events.EventEmitter;\n' + + ' process.nextTick(function() {\n' + + ' e.on("x", thrower);\n' + + ' setTimeout(function() {\n' + + ' e.emit("x");\n' + + ' });\n' + + ' });\n' + + '});"";\n'); + } +}); + +child.on('close', common.mustCall((c) => { + assert.strictEqual(c, 0); + // Make sure we got 3 throws, in the end. + const lastLine = stdout.trim().split(/\r?\n/).pop(); + assert.strictEqual(lastLine, '> 3'); +})); From f78894e4f3af92202580e71a9b4b291418839e32 Mon Sep 17 00:00:00 2001 From: Ciro Spaciari MacBook Date: Wed, 3 Jun 2026 19:11:40 -0700 Subject: [PATCH 02/73] node:repl: error-name decoration, REPL frame trimming, builtin libs, pending-deprecation - ERR_* classes: decorated name in stack header only (Node resets .name to the base class after stack capture); captureStackTrace hides ctors. - decorateErrorStack: cut at the last bare REPL-resource frame (Node's null-functionName boundary), normalize JSC anonymous frames to V8 form. - context lazy builtin libs: include repl/domain/sys like Node. - --interactive entry sets repl.repl introspection property. - getOptionValue('--pending-deprecation') reads process.execArgv. --- src/js/eval/node-repl.ts | 4 ++++ src/js/internal/repl/node-errors.js | 24 ++++++++++++++++++++---- src/js/internal/repl/node-shims.js | 7 +------ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/js/eval/node-repl.ts b/src/js/eval/node-repl.ts index 478b4c29cd71..4828a2cbadd7 100644 --- a/src/js/eval/node-repl.ts +++ b/src/js/eval/node-repl.ts @@ -43,6 +43,10 @@ const filePath = term ? process.env.NODE_REPL_HISTORY : '' const replServer = REPL.start(opts) +// Match Node's standalone-REPL introspection: the running instance is +// reachable as require('repl').repl. +REPL.repl = replServer + replServer.setupHistory({ filePath, size: opts.size, diff --git a/src/js/internal/repl/node-errors.js b/src/js/internal/repl/node-errors.js index 22172ed16000..9aa9bbf20b51 100644 --- a/src/js/internal/repl/node-errors.js +++ b/src/js/internal/repl/node-errors.js @@ -26,37 +26,53 @@ function ERR_INVALID_STATE(...args) { class ERR_CANNOT_WATCH_SIGINT extends Error { code = 'ERR_CANNOT_WATCH_SIGINT' - name = 'Error [ERR_CANNOT_WATCH_SIGINT]' constructor() { super('Cannot watch for interruptions when running asynchronously') + this.name = 'Error [ERR_CANNOT_WATCH_SIGINT]' Error.captureStackTrace?.(this, ERR_CANNOT_WATCH_SIGINT) + // Node resets name to the base class after capturing the stack so the + // decorated form only appears in the stack header. + this.stack + this.name = 'Error' } } class ERR_INSPECTOR_NOT_AVAILABLE extends Error { code = 'ERR_INSPECTOR_NOT_AVAILABLE' - name = 'Error [ERR_INSPECTOR_NOT_AVAILABLE]' constructor() { super('Inspector is not available') + this.name = 'Error [ERR_INSPECTOR_NOT_AVAILABLE]' Error.captureStackTrace?.(this, ERR_INSPECTOR_NOT_AVAILABLE) + // Node resets name to the base class after capturing the stack so the + // decorated form only appears in the stack header. + this.stack + this.name = 'Error' } } class ERR_INVALID_REPL_EVAL_CONFIG extends TypeError { code = 'ERR_INVALID_REPL_EVAL_CONFIG' - name = 'TypeError [ERR_INVALID_REPL_EVAL_CONFIG]' constructor() { super('Cannot specify both "breakEvalOnSigint" and "eval" for REPL') + this.name = 'TypeError [ERR_INVALID_REPL_EVAL_CONFIG]' Error.captureStackTrace?.(this, ERR_INVALID_REPL_EVAL_CONFIG) + // Node resets name to the base class after capturing the stack so the + // decorated form only appears in the stack header. + this.stack + this.name = 'TypeError' } } class ERR_INVALID_REPL_INPUT extends TypeError { code = 'ERR_INVALID_REPL_INPUT' - name = 'TypeError [ERR_INVALID_REPL_INPUT]' constructor(message) { super(message) + this.name = 'TypeError [ERR_INVALID_REPL_INPUT]' Error.captureStackTrace?.(this, ERR_INVALID_REPL_INPUT) + // Node resets name to the base class after capturing the stack so the + // decorated form only appears in the stack header. + this.stack + this.name = 'TypeError' } } diff --git a/src/js/internal/repl/node-shims.js b/src/js/internal/repl/node-shims.js index 26dfbd0b7169..d3d29ae2e965 100644 --- a/src/js/internal/repl/node-shims.js +++ b/src/js/internal/repl/node-shims.js @@ -88,7 +88,7 @@ const isProxy = util.types.isProxy function getOptionValue(name) { switch (name) { case '--pending-deprecation': - return false + return process.execArgv.includes('--pending-deprecation') case '--experimental-repl-await': return true case '--use-strict': @@ -200,11 +200,6 @@ function getBuiltinLibs() { function addBuiltinLibsToObject(object, dummy) { // Make built-in modules available directly (loaded lazily). getBuiltinLibs().forEach(name => { - if (name === 'domain' || name === 'repl' || name === 'sys') { - // The domain module is so weird, and repl/sys so deprecated, that - // node excludes them too. - return - } if (Object.getOwnPropertyDescriptor(object, name)) { return } From cd02f77cdf5a1c0b43f42f452358ff8a99aa85e3 Mon Sep 17 00:00:00 2001 From: Ciro Spaciari MacBook Date: Wed, 3 Jun 2026 19:13:03 -0700 Subject: [PATCH 03/73] node:repl: keep decorated ERR_* names (matches more vendored tests) --- src/js/internal/repl/node-errors.js | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/js/internal/repl/node-errors.js b/src/js/internal/repl/node-errors.js index 9aa9bbf20b51..e1aa7c9d9f06 100644 --- a/src/js/internal/repl/node-errors.js +++ b/src/js/internal/repl/node-errors.js @@ -30,10 +30,6 @@ class ERR_CANNOT_WATCH_SIGINT extends Error { super('Cannot watch for interruptions when running asynchronously') this.name = 'Error [ERR_CANNOT_WATCH_SIGINT]' Error.captureStackTrace?.(this, ERR_CANNOT_WATCH_SIGINT) - // Node resets name to the base class after capturing the stack so the - // decorated form only appears in the stack header. - this.stack - this.name = 'Error' } } @@ -43,10 +39,6 @@ class ERR_INSPECTOR_NOT_AVAILABLE extends Error { super('Inspector is not available') this.name = 'Error [ERR_INSPECTOR_NOT_AVAILABLE]' Error.captureStackTrace?.(this, ERR_INSPECTOR_NOT_AVAILABLE) - // Node resets name to the base class after capturing the stack so the - // decorated form only appears in the stack header. - this.stack - this.name = 'Error' } } @@ -56,10 +48,6 @@ class ERR_INVALID_REPL_EVAL_CONFIG extends TypeError { super('Cannot specify both "breakEvalOnSigint" and "eval" for REPL') this.name = 'TypeError [ERR_INVALID_REPL_EVAL_CONFIG]' Error.captureStackTrace?.(this, ERR_INVALID_REPL_EVAL_CONFIG) - // Node resets name to the base class after capturing the stack so the - // decorated form only appears in the stack header. - this.stack - this.name = 'TypeError' } } @@ -69,10 +57,6 @@ class ERR_INVALID_REPL_INPUT extends TypeError { super(message) this.name = 'TypeError [ERR_INVALID_REPL_INPUT]' Error.captureStackTrace?.(this, ERR_INVALID_REPL_INPUT) - // Node resets name to the base class after capturing the stack so the - // decorated form only appears in the stack header. - this.stack - this.name = 'TypeError' } } From eae1477d91f67f6bfb51440ce7044f37a5f525e4 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:54:31 +0000 Subject: [PATCH 04/73] [autofix.ci] apply automated fixes --- src/js/eval/node-repl.ts | 47 +- src/js/internal/readline/callbacks.js | 60 +- .../internal/readline/emitKeypressEvents.js | 51 +- src/js/internal/readline/interface.js | 608 +++++++--------- src/js/internal/readline/promises.js | 47 +- src/js/internal/readline/utils.js | 432 ++++++++---- src/js/internal/repl.js | 18 +- src/js/internal/repl/acorn-walk.js | 5 +- src/js/internal/repl/acorn.js | 5 +- src/js/internal/repl/await.js | 202 +++--- src/js/internal/repl/completion.js | 348 +++++----- src/js/internal/repl/history.js | 171 ++--- src/js/internal/repl/node-errors.js | 56 +- src/js/internal/repl/node-inspect.js | 17 +- src/js/internal/repl/node-primordials.js | 10 +- src/js/internal/repl/node-shims.js | 249 ++++--- src/js/internal/repl/utils.js | 377 +++++----- src/js/node/readline.js | 82 +-- src/js/node/readline.promises.js | 33 +- src/js/node/repl.js | 657 +++++++++--------- src/runtime/cli/run_command.rs | 9 +- 21 files changed, 1715 insertions(+), 1769 deletions(-) diff --git a/src/js/eval/node-repl.ts b/src/js/eval/node-repl.ts index 4828a2cbadd7..0f0e1a2b3318 100644 --- a/src/js/eval/node-repl.ts +++ b/src/js/eval/node-repl.ts @@ -3,66 +3,63 @@ // only public node:repl APIs (this file runs as a regular entrypoint, so it // cannot require internal modules). -const REPL = require('node:repl') +const REPL = require("node:repl"); -console.log( - `Welcome to Node.js ${process.version}.\n` + - 'Type ".help" for more information.', -) +console.log(`Welcome to Node.js ${process.version}.\n` + 'Type ".help" for more information.'); const opts: Record = { ignoreUndefined: false, useGlobal: true, breakEvalOnSigint: true, -} +}; if (parseInt(process.env.NODE_NO_READLINE!)) { - opts.terminal = false + opts.terminal = false; } if (process.env.NODE_REPL_MODE) { opts.replMode = { strict: REPL.REPL_MODE_STRICT, sloppy: REPL.REPL_MODE_SLOPPY, - }[process.env.NODE_REPL_MODE.toLowerCase().trim()] + }[process.env.NODE_REPL_MODE.toLowerCase().trim()]; } if (opts.replMode === undefined) { - opts.replMode = REPL.REPL_MODE_SLOPPY + opts.replMode = REPL.REPL_MODE_SLOPPY; } -const size = Number(process.env.NODE_REPL_HISTORY_SIZE) +const size = Number(process.env.NODE_REPL_HISTORY_SIZE); if (!Number.isNaN(size) && size > 0) { - opts.size = size + opts.size = size; } else { - opts.size = 1000 + opts.size = 1000; } -const term = 'terminal' in opts ? opts.terminal : process.stdout.isTTY -const filePath = term ? process.env.NODE_REPL_HISTORY : '' +const term = "terminal" in opts ? opts.terminal : process.stdout.isTTY; +const filePath = term ? process.env.NODE_REPL_HISTORY : ""; -const replServer = REPL.start(opts) +const replServer = REPL.start(opts); // Match Node's standalone-REPL introspection: the running instance is // reachable as require('repl').repl. -REPL.repl = replServer +REPL.repl = replServer; replServer.setupHistory({ filePath, size: opts.size, onHistoryFileLoaded: (err: Error | null) => { if (err) { - throw err + throw err; } }, -}) +}); -replServer.on('exit', () => { +replServer.on("exit", () => { if (replServer.historyManager?.isFlushing) { - replServer.once('flushHistory', () => { - process.exit() - }) - return + replServer.once("flushHistory", () => { + process.exit(); + }); + return; } - process.exit() -}) + process.exit(); +}); diff --git a/src/js/internal/readline/callbacks.js b/src/js/internal/readline/callbacks.js index b4b3282d81b8..a07c4d06fe3c 100644 --- a/src/js/internal/readline/callbacks.js +++ b/src/js/internal/readline/callbacks.js @@ -3,33 +3,18 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -'use strict'; +("use strict"); -const { - NumberIsNaN, -} = primordials; +const { NumberIsNaN } = primordials; const { - codes: { - ERR_INVALID_ARG_VALUE, - ERR_INVALID_CURSOR_POS, - }, + codes: { ERR_INVALID_ARG_VALUE, ERR_INVALID_CURSOR_POS }, } = require("internal/repl/node-errors"); -const { - validateFunction, -} = require("internal/validators"); -const { - CSI, -} = require("internal/readline/utils"); - -const { - kClearLine, - kClearScreenDown, - kClearToLineBeginning, - kClearToLineEnd, -} = CSI; +const { validateFunction } = require("internal/validators"); +const { CSI } = require("internal/readline/utils"); +const { kClearLine, kClearScreenDown, kClearToLineBeginning, kClearToLineEnd } = CSI; /** * moves the cursor to the x and y coordinate on the given stream @@ -37,25 +22,25 @@ const { function cursorTo(stream, x, y, callback) { if (callback !== undefined) { - validateFunction(callback, 'callback'); + validateFunction(callback, "callback"); } - if (typeof y === 'function') { + if (typeof y === "function") { callback = y; y = undefined; } - if (NumberIsNaN(x)) throw new ERR_INVALID_ARG_VALUE('x', x); - if (NumberIsNaN(y)) throw new ERR_INVALID_ARG_VALUE('y', y); + if (NumberIsNaN(x)) throw new ERR_INVALID_ARG_VALUE("x", x); + if (NumberIsNaN(y)) throw new ERR_INVALID_ARG_VALUE("y", y); - if (stream == null || (typeof x !== 'number' && typeof y !== 'number')) { - if (typeof callback === 'function') process.nextTick(callback, null); + if (stream == null || (typeof x !== "number" && typeof y !== "number")) { + if (typeof callback === "function") process.nextTick(callback, null); return true; } - if (typeof x !== 'number') throw new ERR_INVALID_CURSOR_POS(); + if (typeof x !== "number") throw new ERR_INVALID_CURSOR_POS(); - const data = typeof y !== 'number' ? CSI`${x + 1}G` : CSI`${y + 1};${x + 1}H`; + const data = typeof y !== "number" ? CSI`${x + 1}G` : CSI`${y + 1};${x + 1}H`; return stream.write(data, callback); } @@ -65,15 +50,15 @@ function cursorTo(stream, x, y, callback) { function moveCursor(stream, dx, dy, callback) { if (callback !== undefined) { - validateFunction(callback, 'callback'); + validateFunction(callback, "callback"); } if (stream == null || !(dx || dy)) { - if (typeof callback === 'function') process.nextTick(callback, null); + if (typeof callback === "function") process.nextTick(callback, null); return true; } - let data = ''; + let data = ""; if (dx < 0) { data += CSI`${-dx}D`; @@ -99,16 +84,15 @@ function moveCursor(stream, dx, dy, callback) { function clearLine(stream, dir, callback) { if (callback !== undefined) { - validateFunction(callback, 'callback'); + validateFunction(callback, "callback"); } if (stream === null || stream === undefined) { - if (typeof callback === 'function') process.nextTick(callback, null); + if (typeof callback === "function") process.nextTick(callback, null); return true; } - const type = - dir < 0 ? kClearToLineBeginning : dir > 0 ? kClearToLineEnd : kClearLine; + const type = dir < 0 ? kClearToLineBeginning : dir > 0 ? kClearToLineEnd : kClearLine; return stream.write(type, callback); } @@ -118,11 +102,11 @@ function clearLine(stream, dir, callback) { function clearScreenDown(stream, callback) { if (callback !== undefined) { - validateFunction(callback, 'callback'); + validateFunction(callback, "callback"); } if (stream === null || stream === undefined) { - if (typeof callback === 'function') process.nextTick(callback, null); + if (typeof callback === "function") process.nextTick(callback, null); return true; } diff --git a/src/js/internal/readline/emitKeypressEvents.js b/src/js/internal/readline/emitKeypressEvents.js index b35fe351d080..a76f285db8cc 100644 --- a/src/js/internal/readline/emitKeypressEvents.js +++ b/src/js/internal/readline/emitKeypressEvents.js @@ -3,31 +3,20 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -'use strict'; - -const { - SafeStringIterator, - Symbol, -} = primordials; - -const { - charLengthAt, - CSI, - emitKeys, -} = require("internal/readline/utils"); -const { - kSawKeyPress, -} = require("internal/readline/interface"); +("use strict"); + +const { SafeStringIterator, Symbol } = primordials; + +const { charLengthAt, CSI, emitKeys } = require("internal/readline/utils"); +const { kSawKeyPress } = require("internal/readline/interface"); const { clearTimeout, setTimeout } = require("node:timers"); -const { - kEscape, -} = CSI; +const { kEscape } = CSI; const { StringDecoder } = require("node:string_decoder"); -const KEYPRESS_DECODER = Symbol('keypress-decoder'); -const ESCAPE_DECODER = Symbol('escape-decoder'); +const KEYPRESS_DECODER = Symbol("keypress-decoder"); +const ESCAPE_DECODER = Symbol("escape-decoder"); // GNU readline library - keyseq-timeout is 500ms (default) const ESCAPE_CODE_TIMEOUT = 500; @@ -39,17 +28,17 @@ const ESCAPE_CODE_TIMEOUT = 500; function emitKeypressEvents(stream, iface = {}) { if (stream[KEYPRESS_DECODER]) return; - stream[KEYPRESS_DECODER] = new StringDecoder('utf8'); + stream[KEYPRESS_DECODER] = new StringDecoder("utf8"); stream[ESCAPE_DECODER] = emitKeys(stream); stream[ESCAPE_DECODER].next(); - const triggerEscape = () => stream[ESCAPE_DECODER].next(''); + const triggerEscape = () => stream[ESCAPE_DECODER].next(""); const { escapeCodeTimeout = ESCAPE_CODE_TIMEOUT } = iface; let timeoutId; function onData(input) { - if (stream.listenerCount('keypress') > 0) { + if (stream.listenerCount("keypress") > 0) { const string = stream[KEYPRESS_DECODER].write(input); if (string) { clearTimeout(timeoutId); @@ -82,22 +71,22 @@ function emitKeypressEvents(stream, iface = {}) { } } else { // Nobody's watching anyway - stream.removeListener('data', onData); - stream.on('newListener', onNewListener); + stream.removeListener("data", onData); + stream.on("newListener", onNewListener); } } function onNewListener(event) { - if (event === 'keypress') { - stream.on('data', onData); - stream.removeListener('newListener', onNewListener); + if (event === "keypress") { + stream.on("data", onData); + stream.removeListener("newListener", onNewListener); } } - if (stream.listenerCount('keypress') > 0) { - stream.on('data', onData); + if (stream.listenerCount("keypress") > 0) { + stream.on("data", onData); } else { - stream.on('newListener', onNewListener); + stream.on("newListener", onNewListener); } } diff --git a/src/js/internal/readline/interface.js b/src/js/internal/readline/interface.js index ba495b60aaef..42f208937558 100644 --- a/src/js/internal/readline/interface.js +++ b/src/js/internal/readline/interface.js @@ -3,7 +3,7 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -'use strict'; +("use strict"); const { ArrayFrom, @@ -41,41 +41,18 @@ const { const { AbortError, - codes: { - ERR_INVALID_ARG_VALUE, - ERR_USE_AFTER_CLOSE, - }, + codes: { ERR_INVALID_ARG_VALUE, ERR_USE_AFTER_CLOSE }, } = require("internal/repl/node-errors"); -const { - validateAbortSignal, - validateString, - validateUint32, -} = require("internal/validators"); -const { - assignFunctionName, - kEmptyObject, -} = require("internal/repl/node-shims"); -const { - inspect, - getStringWidth, - stripVTControlCharacters, -} = require("internal/repl/node-inspect"); +const { validateAbortSignal, validateString, validateUint32 } = require("internal/validators"); +const { assignFunctionName, kEmptyObject } = require("internal/repl/node-shims"); +const { inspect, getStringWidth, stripVTControlCharacters } = require("internal/repl/node-inspect"); const EventEmitter = require("node:events"); const { addAbortListener } = require("internal/repl/node-shims"); -const { - charLengthAt, - charLengthLeft, - commonPrefix, - kSubstringSearch, -} = require("internal/readline/utils"); +const { charLengthAt, charLengthLeft, commonPrefix, kSubstringSearch } = require("internal/readline/utils"); let emitKeypressEvents; let kFirstEventParam; -const { - clearScreenDown, - cursorTo, - moveCursor, -} = require("internal/readline/callbacks"); +const { clearScreenDown, cursorTo, moveCursor } = require("internal/readline/callbacks"); const { StringDecoder } = require("node:string_decoder"); const { ReplHistory } = require("internal/repl/history"); @@ -92,9 +69,9 @@ const kMincrlfDelay = 100; */ const lineEnding = /\r?\n|\r(?!\n)|\u2028|\u2029/g; -const kLineObjectStream = Symbol('line object stream'); -const kQuestionCancel = Symbol('kQuestionCancel'); -const kQuestion = Symbol('kQuestion'); +const kLineObjectStream = Symbol("line object stream"); +const kQuestionCancel = Symbol("kQuestionCancel"); +const kQuestion = Symbol("kQuestion"); // GNU readline library - keyseq-timeout is 500ms (default) const ESCAPE_CODE_TIMEOUT = 500; @@ -102,65 +79,65 @@ const ESCAPE_CODE_TIMEOUT = 500; // Max length of the kill ring const kMaxLengthOfKillRing = 32; -const kMultilinePrompt = Symbol('| '); - -const kAddHistory = Symbol('_addHistory'); -const kBeforeEdit = Symbol('_beforeEdit'); -const kDecoder = Symbol('_decoder'); -const kDeleteLeft = Symbol('_deleteLeft'); -const kDeleteLineLeft = Symbol('_deleteLineLeft'); -const kDeleteLineRight = Symbol('_deleteLineRight'); -const kDeleteRight = Symbol('_deleteRight'); -const kDeleteWordLeft = Symbol('_deleteWordLeft'); -const kDeleteWordRight = Symbol('_deleteWordRight'); -const kGetDisplayPos = Symbol('_getDisplayPos'); -const kHistoryNext = Symbol('_historyNext'); -const kMoveDownOrHistoryNext = Symbol('_moveDownOrHistoryNext'); -const kHistoryPrev = Symbol('_historyPrev'); -const kMoveUpOrHistoryPrev = Symbol('_moveUpOrHistoryPrev'); -const kInsertString = Symbol('_insertString'); -const kLine = Symbol('_line'); -const kLine_buffer = Symbol('_line_buffer'); -const kKillRing = Symbol('_killRing'); -const kKillRingCursor = Symbol('_killRingCursor'); -const kMoveCursor = Symbol('_moveCursor'); -const kNormalWrite = Symbol('_normalWrite'); -const kOldPrompt = Symbol('_oldPrompt'); -const kOnLine = Symbol('_onLine'); -const kSetLine = Symbol('_setLine'); -const kPreviousKey = Symbol('_previousKey'); -const kPrompt = Symbol('_prompt'); -const kPushToKillRing = Symbol('_pushToKillRing'); -const kPushToUndoStack = Symbol('_pushToUndoStack'); -const kQuestionCallback = Symbol('_questionCallback'); -const kLastCommandErrored = Symbol('_lastCommandErrored'); -const kQuestionReject = Symbol('_questionReject'); -const kRedo = Symbol('_redo'); -const kRedoStack = Symbol('_redoStack'); -const kRefreshLine = Symbol('_refreshLine'); -const kSawKeyPress = Symbol('_sawKeyPress'); -const kSawReturnAt = Symbol('_sawReturnAt'); -const kSetRawMode = Symbol('_setRawMode'); -const kTabComplete = Symbol('_tabComplete'); -const kTabCompleter = Symbol('_tabCompleter'); -const kTtyWrite = Symbol('_ttyWrite'); -const kUndo = Symbol('_undo'); -const kUndoStack = Symbol('_undoStack'); -const kIsMultiline = Symbol('_isMultiline'); -const kWordLeft = Symbol('_wordLeft'); -const kWordRight = Symbol('_wordRight'); -const kWriteToOutput = Symbol('_writeToOutput'); -const kYank = Symbol('_yank'); -const kYanking = Symbol('_yanking'); -const kYankPop = Symbol('_yankPop'); -const kSavePreviousState = Symbol('_savePreviousState'); -const kRestorePreviousState = Symbol('_restorePreviousState'); -const kPreviousLine = Symbol('_previousLine'); -const kPreviousCursor = Symbol('_previousCursor'); -const kPreviousCursorCols = Symbol('_previousCursorCols'); -const kMultilineMove = Symbol('_multilineMove'); -const kPreviousPrevRows = Symbol('_previousPrevRows'); -const kAddNewLineOnTTY = Symbol('_addNewLineOnTTY'); +const kMultilinePrompt = Symbol("| "); + +const kAddHistory = Symbol("_addHistory"); +const kBeforeEdit = Symbol("_beforeEdit"); +const kDecoder = Symbol("_decoder"); +const kDeleteLeft = Symbol("_deleteLeft"); +const kDeleteLineLeft = Symbol("_deleteLineLeft"); +const kDeleteLineRight = Symbol("_deleteLineRight"); +const kDeleteRight = Symbol("_deleteRight"); +const kDeleteWordLeft = Symbol("_deleteWordLeft"); +const kDeleteWordRight = Symbol("_deleteWordRight"); +const kGetDisplayPos = Symbol("_getDisplayPos"); +const kHistoryNext = Symbol("_historyNext"); +const kMoveDownOrHistoryNext = Symbol("_moveDownOrHistoryNext"); +const kHistoryPrev = Symbol("_historyPrev"); +const kMoveUpOrHistoryPrev = Symbol("_moveUpOrHistoryPrev"); +const kInsertString = Symbol("_insertString"); +const kLine = Symbol("_line"); +const kLine_buffer = Symbol("_line_buffer"); +const kKillRing = Symbol("_killRing"); +const kKillRingCursor = Symbol("_killRingCursor"); +const kMoveCursor = Symbol("_moveCursor"); +const kNormalWrite = Symbol("_normalWrite"); +const kOldPrompt = Symbol("_oldPrompt"); +const kOnLine = Symbol("_onLine"); +const kSetLine = Symbol("_setLine"); +const kPreviousKey = Symbol("_previousKey"); +const kPrompt = Symbol("_prompt"); +const kPushToKillRing = Symbol("_pushToKillRing"); +const kPushToUndoStack = Symbol("_pushToUndoStack"); +const kQuestionCallback = Symbol("_questionCallback"); +const kLastCommandErrored = Symbol("_lastCommandErrored"); +const kQuestionReject = Symbol("_questionReject"); +const kRedo = Symbol("_redo"); +const kRedoStack = Symbol("_redoStack"); +const kRefreshLine = Symbol("_refreshLine"); +const kSawKeyPress = Symbol("_sawKeyPress"); +const kSawReturnAt = Symbol("_sawReturnAt"); +const kSetRawMode = Symbol("_setRawMode"); +const kTabComplete = Symbol("_tabComplete"); +const kTabCompleter = Symbol("_tabCompleter"); +const kTtyWrite = Symbol("_ttyWrite"); +const kUndo = Symbol("_undo"); +const kUndoStack = Symbol("_undoStack"); +const kIsMultiline = Symbol("_isMultiline"); +const kWordLeft = Symbol("_wordLeft"); +const kWordRight = Symbol("_wordRight"); +const kWriteToOutput = Symbol("_writeToOutput"); +const kYank = Symbol("_yank"); +const kYanking = Symbol("_yanking"); +const kYankPop = Symbol("_yankPop"); +const kSavePreviousState = Symbol("_savePreviousState"); +const kRestorePreviousState = Symbol("_restorePreviousState"); +const kPreviousLine = Symbol("_previousLine"); +const kPreviousCursor = Symbol("_previousCursor"); +const kPreviousCursorCols = Symbol("_previousCursorCols"); +const kMultilineMove = Symbol("_multilineMove"); +const kPreviousPrevRows = Symbol("_previousPrevRows"); +const kAddNewLineOnTTY = Symbol("_addNewLineOnTTY"); function InterfaceConstructor(input, output, completer, terminal) { this[kSawReturnAt] = 0; @@ -175,7 +152,7 @@ function InterfaceConstructor(input, output, completer, terminal) { FunctionPrototypeCall(EventEmitter, this); let crlfDelay; - let prompt = '> '; + let prompt = "> "; let signal; if (input?.input) { @@ -191,7 +168,7 @@ function InterfaceConstructor(input, output, completer, terminal) { const removeHistoryDuplicates = input.removeHistoryDuplicates; if (input.tabSize !== undefined) { - validateUint32(input.tabSize, 'tabSize', true); + validateUint32(input.tabSize, "tabSize", true); this.tabSize = input.tabSize; } if (input.prompt !== undefined) { @@ -201,15 +178,12 @@ function InterfaceConstructor(input, output, completer, terminal) { if (NumberIsFinite(input.escapeCodeTimeout)) { this.escapeCodeTimeout = input.escapeCodeTimeout; } else { - throw new ERR_INVALID_ARG_VALUE( - 'input.escapeCodeTimeout', - this.escapeCodeTimeout, - ); + throw new ERR_INVALID_ARG_VALUE("input.escapeCodeTimeout", this.escapeCodeTimeout); } } if (signal) { - validateAbortSignal(signal, 'options.signal'); + validateAbortSignal(signal, "options.signal"); } crlfDelay = input.crlfDelay; @@ -222,8 +196,8 @@ function InterfaceConstructor(input, output, completer, terminal) { this.setupHistoryManager(input); - if (completer !== undefined && typeof completer !== 'function') { - throw new ERR_INVALID_ARG_VALUE('completer', completer); + if (completer !== undefined && typeof completer !== "function") { + throw new ERR_INVALID_ARG_VALUE("completer", completer); } // Backwards compat; check the isTTY prop of the output stream @@ -234,7 +208,7 @@ function InterfaceConstructor(input, output, completer, terminal) { const self = this; - this.line = ''; + this.line = ""; this[kIsMultiline] = false; this[kSubstringSearch] = null; this.output = output; @@ -251,9 +225,7 @@ function InterfaceConstructor(input, output, completer, terminal) { this[kKillRing] = []; this[kKillRingCursor] = 0; - this.crlfDelay = crlfDelay ? - MathMax(kMincrlfDelay, crlfDelay) : - kMincrlfDelay; + this.crlfDelay = crlfDelay ? MathMax(kMincrlfDelay, crlfDelay) : kMincrlfDelay; this.completer = completer; this.setPrompt(prompt); @@ -261,7 +233,7 @@ function InterfaceConstructor(input, output, completer, terminal) { this.terminal = !!terminal; function onerror(err) { - self.emit('error', err); + self.emit("error", err); } function ondata(data) { @@ -269,18 +241,15 @@ function InterfaceConstructor(input, output, completer, terminal) { } function onend() { - if ( - typeof self[kLine_buffer] === 'string' && - self[kLine_buffer].length > 0 - ) { - self.emit('line', self[kLine_buffer]); + if (typeof self[kLine_buffer] === "string" && self[kLine_buffer].length > 0) { + self.emit("line", self[kLine_buffer]); } self.close(); } function ontermend() { - if (typeof self.line === 'string' && self.line.length > 0) { - self.emit('line', self.line); + if (typeof self.line === "string" && self.line.length > 0) { + self.emit("line", self.line); } self.close(); } @@ -302,26 +271,26 @@ function InterfaceConstructor(input, output, completer, terminal) { this[kLineObjectStream] = undefined; - input.on('error', onerror); + input.on("error", onerror); if (!this.terminal) { function onSelfCloseWithoutTerminal() { - input.removeListener('data', ondata); - input.removeListener('error', onerror); - input.removeListener('end', onend); + input.removeListener("data", ondata); + input.removeListener("error", onerror); + input.removeListener("end", onend); } - input.on('data', ondata); - input.on('end', onend); - self.once('close', onSelfCloseWithoutTerminal); - this[kDecoder] = new StringDecoder('utf8'); + input.on("data", ondata); + input.on("end", onend); + self.once("close", onSelfCloseWithoutTerminal); + this[kDecoder] = new StringDecoder("utf8"); } else { function onSelfCloseWithTerminal() { - input.removeListener('keypress', onkeypress); - input.removeListener('error', onerror); - input.removeListener('end', ontermend); + input.removeListener("keypress", onkeypress); + input.removeListener("error", onerror); + input.removeListener("end", ontermend); if (output !== null && output !== undefined) { - output.removeListener('resize', onresize); + output.removeListener("resize", onresize); } } @@ -329,8 +298,8 @@ function InterfaceConstructor(input, output, completer, terminal) { emitKeypressEvents(input, this); // `input` usually refers to stdin - input.on('keypress', onkeypress); - input.on('end', ontermend); + input.on("keypress", onkeypress); + input.on("end", ontermend); this[kSetRawMode](true); this.terminal = true; @@ -338,10 +307,9 @@ function InterfaceConstructor(input, output, completer, terminal) { // Cursor position on the line. this.cursor = 0; - if (output !== null && output !== undefined) - output.on('resize', onresize); + if (output !== null && output !== undefined) output.on("resize", onresize); - self.once('close', onSelfCloseWithTerminal); + self.once("close", onSelfCloseWithTerminal); } if (signal) { @@ -350,12 +318,12 @@ function InterfaceConstructor(input, output, completer, terminal) { process.nextTick(onAborted); } else { const disposable = addAbortListener(signal, onAborted); - self.once('close', disposable[SymbolDispose]); + self.once("close", disposable[SymbolDispose]); } } // Current line - this[kSetLine](''); + this[kSetLine](""); input.resume(); } @@ -392,33 +360,53 @@ class Interface extends InterfaceConstructor { this.historyManager.initialize(options.onHistoryFileLoaded); } - ObjectDefineProperty(this, 'history', { - __proto__: null, configurable: true, enumerable: true, - get() { return this.historyManager.history; }, - set(newHistory) { return this.historyManager.history = newHistory; }, + ObjectDefineProperty(this, "history", { + __proto__: null, + configurable: true, + enumerable: true, + get() { + return this.historyManager.history; + }, + set(newHistory) { + return (this.historyManager.history = newHistory); + }, }); - ObjectDefineProperty(this, 'historyIndex', { - __proto__: null, configurable: true, enumerable: true, - get() { return this.historyManager.index; }, - set(historyIndex) { return this.historyManager.index = historyIndex; }, + ObjectDefineProperty(this, "historyIndex", { + __proto__: null, + configurable: true, + enumerable: true, + get() { + return this.historyManager.index; + }, + set(historyIndex) { + return (this.historyManager.index = historyIndex); + }, }); - ObjectDefineProperty(this, 'historySize', { - __proto__: null, configurable: true, enumerable: true, - get() { return this.historyManager.size; }, + ObjectDefineProperty(this, "historySize", { + __proto__: null, + configurable: true, + enumerable: true, + get() { + return this.historyManager.size; + }, }); - ObjectDefineProperty(this, 'isFlushing', { - __proto__: null, configurable: true, enumerable: true, - get() { return this.historyManager.isFlushing; }, + ObjectDefineProperty(this, "isFlushing", { + __proto__: null, + configurable: true, + enumerable: true, + get() { + return this.historyManager.isFlushing; + }, }); } [kSetRawMode](mode) { const wasInRawMode = this.input.isRaw; - if (typeof this.input.setRawMode === 'function') { + if (typeof this.input.setRawMode === "function") { this.input.setRawMode(mode); } @@ -432,7 +420,7 @@ class Interface extends InterfaceConstructor { */ prompt(preserveCursor) { if (this.paused) this.resume(); - if (this.terminal && process.env.TERM !== 'dumb') { + if (this.terminal && process.env.TERM !== "dumb") { if (!preserveCursor) this.cursor = 0; this[kRefreshLine](); } else { @@ -442,7 +430,7 @@ class Interface extends InterfaceConstructor { [kQuestion](query, cb) { if (this.closed) { - throw new ERR_USE_AFTER_CLOSE('readline'); + throw new ERR_USE_AFTER_CLOSE("readline"); } if (this[kQuestionCallback]) { this.prompt(); @@ -454,9 +442,9 @@ class Interface extends InterfaceConstructor { } } - [kSetLine](line = '') { + [kSetLine](line = "") { this.line = line; - this[kIsMultiline] = StringPrototypeIncludes(line, '\n'); + this[kIsMultiline] = StringPrototypeIncludes(line, "\n"); } [kOnLine](line) { @@ -466,7 +454,7 @@ class Interface extends InterfaceConstructor { this.setPrompt(this[kOldPrompt]); cb(line); } else { - this.emit('line', line); + this.emit("line", line); } } @@ -483,7 +471,7 @@ class Interface extends InterfaceConstructor { } [kWriteToOutput](stringToWrite) { - validateString(stringToWrite, 'stringToWrite'); + validateString(stringToWrite, "stringToWrite"); if (this.output !== null && this.output !== undefined) { this.output.write(stringToWrite); @@ -516,7 +504,7 @@ class Interface extends InterfaceConstructor { clearScreenDown(this.output); if (this[kIsMultiline]) { - const lines = StringPrototypeSplit(this.line, '\n'); + const lines = StringPrototypeSplit(this.line, "\n"); // Write first line with normal prompt this[kWriteToOutput](this[kPrompt] + lines[0]); @@ -531,7 +519,7 @@ class Interface extends InterfaceConstructor { // Force terminal to allocate a new line if (lineCols === 0) { - this[kWriteToOutput](' '); + this[kWriteToOutput](" "); } // Move cursor to original position. @@ -556,7 +544,7 @@ class Interface extends InterfaceConstructor { this[kSetRawMode](false); } this.closed = true; - this.emit('close'); + this.emit("close"); } /** @@ -565,12 +553,12 @@ class Interface extends InterfaceConstructor { */ pause() { if (this.closed) { - throw new ERR_USE_AFTER_CLOSE('readline'); + throw new ERR_USE_AFTER_CLOSE("readline"); } if (this.paused) return; this.input.pause(); this.paused = true; - this.emit('pause'); + this.emit("pause"); return this; } @@ -580,12 +568,12 @@ class Interface extends InterfaceConstructor { */ resume() { if (this.closed) { - throw new ERR_USE_AFTER_CLOSE('readline'); + throw new ERR_USE_AFTER_CLOSE("readline"); } if (!this.paused) return; this.input.resume(); this.paused = false; - this.emit('resume'); + this.emit("resume"); return this; } @@ -603,7 +591,7 @@ class Interface extends InterfaceConstructor { */ write(d, key) { if (this.closed) { - throw new ERR_USE_AFTER_CLOSE('readline'); + throw new ERR_USE_AFTER_CLOSE("readline"); } if (this.paused) this.resume(); if (this.terminal) { @@ -618,10 +606,7 @@ class Interface extends InterfaceConstructor { return; } let string = this[kDecoder].write(b); - if ( - this[kSawReturnAt] && - DateNow() - this[kSawReturnAt] <= this.crlfDelay - ) { + if (this[kSawReturnAt] && DateNow() - this[kSawReturnAt] <= this.crlfDelay) { if (StringPrototypeCodePointAt(string) === 10) string = StringPrototypeSlice(string, 1); this[kSawReturnAt] = 0; } @@ -635,9 +620,7 @@ class Interface extends InterfaceConstructor { lineEnding.lastIndex = 0; // Start the search from the beginning of the string. newPartContainsEnding = RegExpPrototypeExec(lineEnding, string); } - this[kSawReturnAt] = StringPrototypeEndsWith(string, '\r') ? - DateNow() : - 0; + this[kSawReturnAt] = StringPrototypeEndsWith(string, "\r") ? DateNow() : 0; const indexes = [0, newPartContainsEnding.index, lineEnding.lastIndex]; let nextMatch; @@ -665,11 +648,7 @@ class Interface extends InterfaceConstructor { if (!this.isCompletionEnabled) { if (this.cursor < this.line.length) { const beg = StringPrototypeSlice(this.line, 0, this.cursor); - const end = StringPrototypeSlice( - this.line, - this.cursor, - this.line.length, - ); + const end = StringPrototypeSlice(this.line, this.cursor, this.line.length); this.line = beg + c + end; } else { this.line += c; @@ -680,11 +659,7 @@ class Interface extends InterfaceConstructor { } if (this.cursor < this.line.length) { const beg = StringPrototypeSlice(this.line, 0, this.cursor); - const end = StringPrototypeSlice( - this.line, - this.cursor, - this.line.length, - ); + const end = StringPrototypeSlice(this.line, this.cursor, this.line.length); this[kSetLine](beg + c + end); this.cursor += c.length; this[kRefreshLine](); @@ -725,21 +700,16 @@ class Interface extends InterfaceConstructor { } // If there is a common prefix to all matches, then apply that portion. - const prefix = commonPrefix( - ArrayPrototypeFilter(completions, (e) => e !== ''), - ); - if (StringPrototypeStartsWith(prefix, completeOn) && - prefix.length > completeOn.length) { + const prefix = commonPrefix(ArrayPrototypeFilter(completions, e => e !== "")); + if (StringPrototypeStartsWith(prefix, completeOn) && prefix.length > completeOn.length) { this[kInsertString](StringPrototypeSlice(prefix, completeOn.length)); return; } else if (!StringPrototypeStartsWith(completeOn, prefix)) { - this[kSetLine](StringPrototypeSlice(this.line, - 0, - this.cursor - completeOn.length) + - prefix + - StringPrototypeSlice(this.line, - this.cursor, - this.line.length)); + this[kSetLine]( + StringPrototypeSlice(this.line, 0, this.cursor - completeOn.length) + + prefix + + StringPrototypeSlice(this.line, this.cursor, this.line.length), + ); this.cursor = this.cursor - completeOn.length + prefix.length; this[kRefreshLine](); return; @@ -752,36 +722,34 @@ class Interface extends InterfaceConstructor { this[kBeforeEdit](this.line, this.cursor); // Apply/show completions. - const completionsWidth = ArrayPrototypeMap(completions, (e) => - getStringWidth(e), - ); + const completionsWidth = ArrayPrototypeMap(completions, e => getStringWidth(e)); const width = MathMaxApply(completionsWidth) + 2; // 2 space padding let maxColumns = MathFloor(this.columns / width) || 1; if (maxColumns === Infinity) { maxColumns = 1; } - let output = '\r\n'; + let output = "\r\n"; let lineIndex = 0; let whitespace = 0; for (let i = 0; i < completions.length; i++) { const completion = completions[i]; - if (completion === '' || lineIndex === maxColumns) { - output += '\r\n'; + if (completion === "" || lineIndex === maxColumns) { + output += "\r\n"; lineIndex = 0; whitespace = 0; } else { - output += StringPrototypeRepeat(' ', whitespace); + output += StringPrototypeRepeat(" ", whitespace); } - if (completion !== '') { + if (completion !== "") { output += completion; whitespace = width - completionsWidth[i]; lineIndex++; } else { - output += '\r\n'; + output += "\r\n"; } } if (lineIndex !== 0) { - output += '\r\n\r\n'; + output += "\r\n\r\n"; } this[kWriteToOutput](output); this[kRefreshLine](); @@ -792,10 +760,7 @@ class Interface extends InterfaceConstructor { // Reverse the string and match a word near beginning // to avoid quadratic time complexity const leading = StringPrototypeSlice(this.line, 0, this.cursor); - const reversed = ArrayPrototypeJoin( - ArrayPrototypeReverse(ArrayFrom(leading)), - '', - ); + const reversed = ArrayPrototypeJoin(ArrayPrototypeReverse(ArrayFrom(leading)), ""); const match = RegExpPrototypeExec(/^\s*(?:[^\w\s]+|\w+)?/, reversed); this[kMoveCursor](-match[0].length); } @@ -830,11 +795,7 @@ class Interface extends InterfaceConstructor { const charSize = charLengthAt(this.line, this.cursor); this.line = StringPrototypeSlice(this.line, 0, this.cursor) + - StringPrototypeSlice( - this.line, - this.cursor + charSize, - this.line.length, - ); + StringPrototypeSlice(this.line, this.cursor + charSize, this.line.length); this[kRefreshLine](); } } @@ -845,19 +806,10 @@ class Interface extends InterfaceConstructor { // Reverse the string and match a word near beginning // to avoid quadratic time complexity let leading = StringPrototypeSlice(this.line, 0, this.cursor); - const reversed = ArrayPrototypeJoin( - ArrayPrototypeReverse(ArrayFrom(leading)), - '', - ); + const reversed = ArrayPrototypeJoin(ArrayPrototypeReverse(ArrayFrom(leading)), ""); const match = RegExpPrototypeExec(/^\s*(?:[^\w\s]+|\w+)?/, reversed); - leading = StringPrototypeSlice( - leading, - 0, - leading.length - match[0].length, - ); - this.line = - leading + - StringPrototypeSlice(this.line, this.cursor, this.line.length); + leading = StringPrototypeSlice(leading, 0, leading.length - match[0].length); + this.line = leading + StringPrototypeSlice(this.line, this.cursor, this.line.length); this.cursor = leading.length; this[kRefreshLine](); } @@ -868,9 +820,7 @@ class Interface extends InterfaceConstructor { this[kBeforeEdit](this.line, this.cursor); const trailing = StringPrototypeSlice(this.line, this.cursor); const match = RegExpPrototypeExec(/^(?:\s+|\W+|\w+)\s*/, trailing); - this.line = - StringPrototypeSlice(this.line, 0, this.cursor) + - StringPrototypeSlice(trailing, match[0].length); + this.line = StringPrototypeSlice(this.line, 0, this.cursor) + StringPrototypeSlice(trailing, match[0].length); this[kRefreshLine](); } } @@ -896,8 +846,7 @@ class Interface extends InterfaceConstructor { if (!del || del === this[kKillRing][0]) return; ArrayPrototypeUnshift(this[kKillRing], del); this[kKillRingCursor] = 0; - while (this[kKillRing].length > kMaxLengthOfKillRing) - ArrayPrototypePop(this[kKillRing]); + while (this[kKillRing].length > kMaxLengthOfKillRing) ArrayPrototypePop(this[kKillRing]); } [kYank]() { @@ -918,10 +867,8 @@ class Interface extends InterfaceConstructor { this[kKillRingCursor] = 0; } const currentYank = this[kKillRing][this[kKillRingCursor]]; - const head = - StringPrototypeSlice(this.line, 0, this.cursor - lastYank.length); - const tail = - StringPrototypeSlice(this.line, this.cursor); + const head = StringPrototypeSlice(this.line, 0, this.cursor - lastYank.length); + const tail = StringPrototypeSlice(this.line, this.cursor); this[kSetLine](head + currentYank + tail); this.cursor = head.length + currentYank.length; this[kRefreshLine](); @@ -942,8 +889,8 @@ class Interface extends InterfaceConstructor { clearLine() { this[kMoveCursor](+Infinity); - this[kWriteToOutput]('\r\n'); - this[kSetLine](''); + this[kWriteToOutput]("\r\n"); + this[kSetLine](""); this.cursor = 0; this.prevRows = 0; } @@ -957,7 +904,6 @@ class Interface extends InterfaceConstructor { this[kOnLine](line); } - // TODO(puskin94): edit [kTtyWrite] to make call this function on a new key combination // to make it add a new line in the middle of a "complete" multiline. // I tried with shift + enter but it is not detected. Find a new one. @@ -988,13 +934,13 @@ class Interface extends InterfaceConstructor { // Handle cursor positioning based on different scenarios if (hasContentAfterCursor) { - const splitBeg = StringPrototypeSplit(beforeCursor, '\n'); + const splitBeg = StringPrototypeSplit(beforeCursor, "\n"); // Determine if we need to rewrite the first line needsRewriteFirstLine = splitBeg.length < 2; // If the cursor is not on the first line if (cursorIsNotOnFirstLine) { - const splitEnd = StringPrototypeSplit(afterCursor, '\n'); + const splitEnd = StringPrototypeSplit(afterCursor, "\n"); // If the cursor when I pressed enter was at least on the second line // I need to completely erase the line where the cursor was pressed because it is possible @@ -1003,7 +949,7 @@ class Interface extends InterfaceConstructor { const dy = splitEnd.length + 1; // Calculate how many Xs we need to move on the right to get to the end of the line - const dxEndOfLineAbove = (splitBeg[splitBeg.length - 2] || '').length + kMultilinePrompt.description.length; + const dxEndOfLineAbove = (splitBeg[splitBeg.length - 2] || "").length + kMultilinePrompt.description.length; moveCursor(this.output, dxEndOfLineAbove, -dy); // This is the line that was split in the middle @@ -1011,7 +957,7 @@ class Interface extends InterfaceConstructor { afterCursor = `${splitBeg[splitBeg.length - 1]}\n${afterCursor}`; } else { // Otherwise, go to the very beginning of the first line and erase everything - const dy = StringPrototypeSplit(originalLine, '\n').length; + const dy = StringPrototypeSplit(originalLine, "\n").length; moveCursor(this.output, 0, -dy); } @@ -1019,7 +965,7 @@ class Interface extends InterfaceConstructor { clearScreenDown(this.output); if (cursorIsNotOnFirstLine) { - this[kWriteToOutput]('\n'); + this[kWriteToOutput]("\n"); } } @@ -1035,11 +981,7 @@ class Interface extends InterfaceConstructor { const oldCursor = this.getCursorPos(); // Write everything after the cursor which has been deleted by clearScreenDown - const formattedEndContent = StringPrototypeReplaceAll( - afterCursor, - '\n', - `\n${kMultilinePrompt.description}`, - ); + const formattedEndContent = StringPrototypeReplaceAll(afterCursor, "\n", `\n${kMultilinePrompt.description}`); this[kWriteToOutput](formattedEndContent); @@ -1054,13 +996,12 @@ class Interface extends InterfaceConstructor { } else { // Setting how many rows we have on top of the cursor // Necessary for kRefreshLine - this.prevRows = StringPrototypeSplit(this.line, '\n').length - 1; + this.prevRows = StringPrototypeSplit(this.line, "\n").length - 1; } } [kPushToUndoStack](text, cursor) { - if (ArrayPrototypePush(this[kUndoStack], { text, cursor }) > - kMaxUndoRedoStackSize) { + if (ArrayPrototypePush(this[kUndoStack], { text, cursor }) > kMaxUndoRedoStackSize) { ArrayPrototypeShift(this[kUndoStack]); } } @@ -1068,10 +1009,7 @@ class Interface extends InterfaceConstructor { [kUndo]() { if (this[kUndoStack].length <= 0) return; - ArrayPrototypePush( - this[kRedoStack], - { text: this.line, cursor: this.cursor }, - ); + ArrayPrototypePush(this[kRedoStack], { text: this.line, cursor: this.cursor }); const entry = ArrayPrototypePop(this[kUndoStack]); this[kSetLine](entry.text); @@ -1083,10 +1021,7 @@ class Interface extends InterfaceConstructor { [kRedo]() { if (this[kRedoStack].length <= 0) return; - ArrayPrototypePush( - this[kUndoStack], - { text: this.line, cursor: this.cursor }, - ); + ArrayPrototypePush(this[kUndoStack], { text: this.line, cursor: this.cursor }); const entry = ArrayPrototypePop(this[kRedoStack]); this[kSetLine](entry.text); @@ -1102,9 +1037,7 @@ class Interface extends InterfaceConstructor { const promptLen = kMultilinePrompt.description.length; let amountToMove; // Clamp distance to end of current + prompt + next/prev line + newline - const clamp = down ? - curr.length - cols + promptLen + adj.length + 1 : - -cols + 1; + const clamp = down ? curr.length - cols + promptLen + adj.length + 1 : -cols + 1; const shouldClamp = cols > adj.length + 1; if (shouldClamp) { @@ -1133,7 +1066,7 @@ class Interface extends InterfaceConstructor { [kMoveDownOrHistoryNext]() { const cursorPos = this.getCursorPos(); - const splitLines = StringPrototypeSplit(this.line, '\n'); + const splitLines = StringPrototypeSplit(this.line, "\n"); if (this[kIsMultiline] && cursorPos.rows < splitLines.length - 1) { this[kMultilineMove](1, splitLines, cursorPos); return; @@ -1150,7 +1083,9 @@ class Interface extends InterfaceConstructor { // + N. Only show this after two/three UPs or DOWNs, not on the first // one. [kHistoryNext]() { - if (!this.historyManager.canNavigateToNext()) { return; } + if (!this.historyManager.canNavigateToNext()) { + return; + } this[kBeforeEdit](this.line, this.cursor); this[kSetLine](this.historyManager.navigateToNext(this[kSubstringSearch])); @@ -1161,7 +1096,7 @@ class Interface extends InterfaceConstructor { [kMoveUpOrHistoryPrev]() { const cursorPos = this.getCursorPos(); if (this[kIsMultiline] && cursorPos.rows > 0) { - const splitLines = StringPrototypeSplit(this.line, '\n'); + const splitLines = StringPrototypeSplit(this.line, "\n"); this[kMultilineMove](-1, splitLines, cursorPos); return; } @@ -1170,7 +1105,9 @@ class Interface extends InterfaceConstructor { } [kHistoryPrev]() { - if (!this.historyManager.canNavigateToPrevious()) { return; } + if (!this.historyManager.canNavigateToPrevious()) { + return; + } this[kBeforeEdit](this.line, this.cursor); this[kSetLine](this.historyManager.navigateToPrevious(this[kSubstringSearch])); @@ -1186,7 +1123,7 @@ class Interface extends InterfaceConstructor { str = stripVTControlCharacters(str); for (const char of new SafeStringIterator(str)) { - if (char === '\n') { + if (char === "\n") { // Rows must be incremented by 1 even if offset = 0 or col = +Infinity. rows += MathCeil(offset / col) || 1; // Only add prefix offset for continuation lines in user input (not prompts) @@ -1194,7 +1131,7 @@ class Interface extends InterfaceConstructor { continue; } // Tabs must be aligned by an offset of the tab size. - if (char === '\t') { + if (char === "\t") { offset += this.tabSize - (offset % this.tabSize); continue; } @@ -1264,24 +1201,15 @@ class Interface extends InterfaceConstructor { this[kPreviousKey] = key; let shouldResetPreviousCursorCols = true; - if (!key.meta || key.name !== 'y') { + if (!key.meta || key.name !== "y") { // Reset yanking state unless we are doing yank pop. this[kYanking] = false; } // Activate or deactivate substring search. - if ( - (key.name === 'up' || key.name === 'down') && - !key.ctrl && - !key.meta && - !key.shift - ) { + if ((key.name === "up" || key.name === "down") && !key.ctrl && !key.meta && !key.shift) { if (this[kSubstringSearch] === null && !this[kIsMultiline]) { - this[kSubstringSearch] = StringPrototypeSlice( - this.line, - 0, - this.cursor, - ); + this[kSubstringSearch] = StringPrototypeSlice(this.line, 0, this.cursor); } } else if (this[kSubstringSearch] !== null) { this[kSubstringSearch] = null; @@ -1292,7 +1220,7 @@ class Interface extends InterfaceConstructor { } // Undo & Redo - if (typeof key.sequence === 'string') { + if (typeof key.sequence === "string") { switch (StringPrototypeCodePointAt(key.sequence, 0)) { case 0x1f: this[kUndo](); @@ -1307,18 +1235,18 @@ class Interface extends InterfaceConstructor { // Ignore escape key, fixes // https://github.com/nodejs/node-v0.x-archive/issues/2876. - if (key.name === 'escape') return; + if (key.name === "escape") return; if (key.ctrl && key.shift) { /* Control and shift pressed */ switch (key.name) { // TODO(BridgeAR): The transmitted escape sequence is `\b` and that is // identical to -h. It should have a unique escape sequence. - case 'backspace': + case "backspace": this[kDeleteLineLeft](); break; - case 'delete': + case "delete": this[kDeleteLineRight](); break; } @@ -1326,84 +1254,84 @@ class Interface extends InterfaceConstructor { /* Control key pressed */ switch (key.name) { - case 'c': - if (this.listenerCount('SIGINT') > 0) { - this.emit('SIGINT'); + case "c": + if (this.listenerCount("SIGINT") > 0) { + this.emit("SIGINT"); } else { // This readline instance is finished this.close(); - this[kQuestionReject]?.(new AbortError('Aborted with Ctrl+C')); + this[kQuestionReject]?.(new AbortError("Aborted with Ctrl+C")); } break; - case 'h': // delete left + case "h": // delete left this[kDeleteLeft](); break; - case 'd': // delete right or EOF + case "d": // delete right or EOF if (this.cursor === 0 && this.line.length === 0) { // This readline instance is finished this.close(); - this[kQuestionReject]?.(new AbortError('Aborted with Ctrl+D')); + this[kQuestionReject]?.(new AbortError("Aborted with Ctrl+D")); } else if (this.cursor < this.line.length) { this[kDeleteRight](); } break; - case 'u': // Delete from current to start of line + case "u": // Delete from current to start of line this[kDeleteLineLeft](); break; - case 'k': // Delete from current to end of line + case "k": // Delete from current to end of line this[kDeleteLineRight](); break; - case 'a': // Go to the start of the line + case "a": // Go to the start of the line this[kMoveCursor](-Infinity); break; - case 'e': // Go to the end of the line + case "e": // Go to the end of the line this[kMoveCursor](+Infinity); break; - case 'b': // back one character + case "b": // back one character this[kMoveCursor](-charLengthLeft(this.line, this.cursor)); break; - case 'f': // Forward one character + case "f": // Forward one character this[kMoveCursor](+charLengthAt(this.line, this.cursor)); break; - case 'l': // Clear the whole screen + case "l": // Clear the whole screen cursorTo(this.output, 0, 0); clearScreenDown(this.output); this[kRefreshLine](); break; - case 'n': // next history item + case "n": // next history item this[kHistoryNext](); break; - case 'p': // Previous history item + case "p": // Previous history item this[kHistoryPrev](); break; - case 'y': // Yank killed string + case "y": // Yank killed string this[kYank](); break; - case 'z': - if (process.platform === 'win32') break; - if (this.listenerCount('SIGTSTP') > 0) { - this.emit('SIGTSTP'); + case "z": + if (process.platform === "win32") break; + if (this.listenerCount("SIGTSTP") > 0) { + this.emit("SIGTSTP"); } else { - process.once('SIGCONT', () => { + process.once("SIGCONT", () => { // Don't raise events if stream has already been abandoned. if (!this.paused) { // Stream must be paused and resumed after SIGCONT to catch // SIGINT, SIGTSTP, and EOF. this.pause(); - this.emit('SIGCONT'); + this.emit("SIGCONT"); } // Explicitly re-enable "raw mode" and move the cursor to // the correct position. @@ -1412,27 +1340,27 @@ class Interface extends InterfaceConstructor { this[kRefreshLine](); }); this[kSetRawMode](false); - process.kill(process.pid, 'SIGTSTP'); + process.kill(process.pid, "SIGTSTP"); } break; - case 'w': // Delete backwards to a word boundary + case "w": // Delete backwards to a word boundary // TODO(BridgeAR): The transmitted escape sequence is `\b` and that is // identical to -h. It should have a unique escape sequence. // Falls through - case 'backspace': + case "backspace": this[kDeleteWordLeft](); break; - case 'delete': // Delete forward to a word boundary + case "delete": // Delete forward to a word boundary this[kDeleteWordRight](); break; - case 'left': + case "left": this[kWordLeft](); break; - case 'right': + case "right": this[kWordRight](); break; } @@ -1440,24 +1368,24 @@ class Interface extends InterfaceConstructor { /* Meta key pressed */ switch (key.name) { - case 'b': // backward word + case "b": // backward word this[kWordLeft](); break; - case 'f': // forward word + case "f": // forward word this[kWordRight](); break; - case 'd': // delete forward word - case 'delete': + case "d": // delete forward word + case "delete": this[kDeleteWordRight](); break; - case 'backspace': // Delete backwards to a word boundary + case "backspace": // Delete backwards to a word boundary this[kDeleteWordLeft](); break; - case 'y': // Doing yank pop + case "y": // Doing yank pop this[kYankPop](); break; } @@ -1465,74 +1393,67 @@ class Interface extends InterfaceConstructor { /* No modifier keys used */ // \r bookkeeping is only relevant if a \n comes right after. - if (this[kSawReturnAt] && key.name !== 'enter') this[kSawReturnAt] = 0; + if (this[kSawReturnAt] && key.name !== "enter") this[kSawReturnAt] = 0; switch (key.name) { - case 'return': // Carriage return, i.e. \r + case "return": // Carriage return, i.e. \r this[kSawReturnAt] = DateNow(); this[kLine](); break; - case 'enter': + case "enter": // When key interval > crlfDelay - if ( - this[kSawReturnAt] === 0 || - DateNow() - this[kSawReturnAt] > this.crlfDelay - ) { + if (this[kSawReturnAt] === 0 || DateNow() - this[kSawReturnAt] > this.crlfDelay) { this[kLine](); } this[kSawReturnAt] = 0; break; - case 'backspace': + case "backspace": this[kDeleteLeft](); break; - case 'delete': + case "delete": this[kDeleteRight](); break; - case 'left': + case "left": // Obtain the code point to the left this[kMoveCursor](-charLengthLeft(this.line, this.cursor)); break; - case 'right': + case "right": this[kMoveCursor](+charLengthAt(this.line, this.cursor)); break; - case 'home': + case "home": this[kMoveCursor](-Infinity); break; - case 'end': + case "end": this[kMoveCursor](+Infinity); break; - case 'up': + case "up": shouldResetPreviousCursorCols = false; this[kMoveUpOrHistoryPrev](); break; - case 'down': + case "down": shouldResetPreviousCursorCols = false; this[kMoveDownOrHistoryNext](); break; - case 'tab': + case "tab": // If tab completion enabled, do that... - if ( - typeof this.completer === 'function' && - this.isCompletionEnabled - ) { - const lastKeypressWasTab = - previousKey && previousKey.name === 'tab'; + if (typeof this.completer === "function" && this.isCompletionEnabled) { + const lastKeypressWasTab = previousKey && previousKey.name === "tab"; this[kTabComplete](lastKeypressWasTab); break; } // falls through default: - if (typeof s === 'string' && s) { + if (typeof s === "string" && s) { // Erase state of previous searches. lineEnding.lastIndex = 0; let nextMatch; @@ -1564,17 +1485,16 @@ class Interface extends InterfaceConstructor { [SymbolAsyncIterator]() { if (this[kLineObjectStream] === undefined) { kFirstEventParam ??= Symbol.for("nodejs.kFirstEventParam"); - this[kLineObjectStream] = EventEmitter.on( - this, 'line', { - close: ['close'], - highWaterMark: 1024, - [kFirstEventParam]: true, - }); + this[kLineObjectStream] = EventEmitter.on(this, "line", { + close: ["close"], + highWaterMark: 1024, + [kFirstEventParam]: true, + }); } return this[kLineObjectStream]; } } -Interface.prototype[SymbolDispose] = assignFunctionName(SymbolDispose, function() { +Interface.prototype[SymbolDispose] = assignFunctionName(SymbolDispose, function () { this.close(); }); diff --git a/src/js/internal/readline/promises.js b/src/js/internal/readline/promises.js index b88abc692f2e..7982b7d0807e 100644 --- a/src/js/internal/readline/promises.js +++ b/src/js/internal/readline/promises.js @@ -3,27 +3,18 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -'use strict'; +("use strict"); -const { - ArrayPrototypeJoin, - ArrayPrototypePush, - Promise, -} = primordials; +const { ArrayPrototypeJoin, ArrayPrototypePush, Promise } = primordials; const { CSI } = require("internal/readline/utils"); const { validateBoolean, validateInteger } = require("internal/validators"); const { isWritable } = require("internal/repl/node-shims"); -const { codes: { - ERR_INVALID_ARG_TYPE, -} } = require("internal/repl/node-errors"); - const { - kClearToLineBeginning, - kClearToLineEnd, - kClearLine, - kClearScreenDown, -} = CSI; + codes: { ERR_INVALID_ARG_TYPE }, +} = require("internal/repl/node-errors"); + +const { kClearToLineBeginning, kClearToLineEnd, kClearLine, kClearScreenDown } = CSI; class Readline { #autoCommit = false; @@ -31,11 +22,10 @@ class Readline { #todo = []; constructor(stream, options = undefined) { - if (!isWritable(stream)) - throw new ERR_INVALID_ARG_TYPE('stream', 'Writable', stream); + if (!isWritable(stream)) throw new ERR_INVALID_ARG_TYPE("stream", "Writable", stream); this.#stream = stream; if (options?.autoCommit != null) { - validateBoolean(options.autoCommit, 'options.autoCommit'); + validateBoolean(options.autoCommit, "options.autoCommit"); this.#autoCommit = options.autoCommit; } } @@ -47,8 +37,8 @@ class Readline { * @returns {Readline} this */ cursorTo(x, y = undefined) { - validateInteger(x, 'x'); - if (y != null) validateInteger(y, 'y'); + validateInteger(x, "x"); + if (y != null) validateInteger(y, "y"); const data = y == null ? CSI`${x + 1}G` : CSI`${y + 1};${x + 1}H`; if (this.#autoCommit) process.nextTick(() => this.#stream.write(data)); @@ -65,10 +55,10 @@ class Readline { */ moveCursor(dx, dy) { if (dx || dy) { - validateInteger(dx, 'dx'); - validateInteger(dy, 'dy'); + validateInteger(dx, "dx"); + validateInteger(dy, "dy"); - let data = ''; + let data = ""; if (dx < 0) { data += CSI`${-dx}D`; @@ -96,12 +86,9 @@ class Readline { * @returns {Readline} this */ clearLine(dir) { - validateInteger(dir, 'dir', -1, 1); + validateInteger(dir, "dir", -1, 1); - const data = - dir < 0 ? kClearToLineBeginning : - dir > 0 ? kClearToLineEnd : - kClearLine; + const data = dir < 0 ? kClearToLineBeginning : dir > 0 ? kClearToLineEnd : kClearLine; if (this.#autoCommit) process.nextTick(() => this.#stream.write(data)); else ArrayPrototypePush(this.#todo, data); return this; @@ -127,8 +114,8 @@ class Readline { * flushed to the associated `stream`. */ commit() { - return new Promise((resolve) => { - this.#stream.write(ArrayPrototypeJoin(this.#todo, ''), resolve); + return new Promise(resolve => { + this.#stream.write(ArrayPrototypeJoin(this.#todo, ""), resolve); this.#todo = []; }); } diff --git a/src/js/internal/readline/utils.js b/src/js/internal/readline/utils.js index 6eb7a85f6b7b..776ff8f124d5 100644 --- a/src/js/internal/readline/utils.js +++ b/src/js/internal/readline/utils.js @@ -3,7 +3,7 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -'use strict'; +("use strict"); const { ArrayPrototypeToSorted, @@ -18,15 +18,14 @@ const { } = primordials; const kUTF16SurrogateThreshold = 0x10000; // 2 ** 16 -const kEscape = '\x1b'; -const kSubstringSearch = Symbol('kSubstringSearch'); +const kEscape = "\x1b"; +const kSubstringSearch = Symbol("kSubstringSearch"); function CSI(strings, ...args) { let ret = `${kEscape}[`; for (let n = 0; n < strings.length; n++) { ret += strings[n]; - if (n < args.length) - ret += args[n]; + if (n < args.length) ret += args[n]; } return ret; } @@ -42,11 +41,11 @@ CSI.kClearScreenDown = CSI`0J`; // Check Canonical_Combining_Class in // http://userguide.icu-project.org/strings/properties function charLengthLeft(str, i) { - if (i <= 0) - return 0; - if ((i > 1 && - StringPrototypeCodePointAt(str, i - 2) >= kUTF16SurrogateThreshold) || - StringPrototypeCodePointAt(str, i - 1) >= kUTF16SurrogateThreshold) { + if (i <= 0) return 0; + if ( + (i > 1 && StringPrototypeCodePointAt(str, i - 2) >= kUTF16SurrogateThreshold) || + StringPrototypeCodePointAt(str, i - 1) >= kUTF16SurrogateThreshold + ) { return 2; } return 1; @@ -103,41 +102,41 @@ function* emitKeys(stream) { if (ch === kEscape) { escaped = true; - s += (ch = yield); + s += ch = yield; if (ch === kEscape) { - s += (ch = yield); + s += ch = yield; } } - if (escaped && (ch === 'O' || ch === '[')) { + if (escaped && (ch === "O" || ch === "[")) { // ANSI escape sequence let code = ch; let modifier = 0; - if (ch === 'O') { + if (ch === "O") { // ESC O letter // ESC O modifier letter - s += (ch = yield); + s += ch = yield; - if (ch >= '0' && ch <= '9') { + if (ch >= "0" && ch <= "9") { modifier = (ch >> 0) - 1; - s += (ch = yield); + s += ch = yield; } code += ch; - } else if (ch === '[') { + } else if (ch === "[") { // ESC [ letter // ESC [ modifier letter // ESC [ [ modifier letter // ESC [ [ num char - s += (ch = yield); + s += ch = yield; - if (ch === '[') { + if (ch === "[") { // \x1b[[A // ^--- escape codes might have a second bracket code += ch; - s += (ch = yield); + s += ch = yield; } /* @@ -172,23 +171,23 @@ function* emitKeys(stream) { const cmdStart = s.length - 1; // Skip one or two leading digits - if (ch >= '0' && ch <= '9') { - s += (ch = yield); + if (ch >= "0" && ch <= "9") { + s += ch = yield; - if (ch >= '0' && ch <= '9') { - s += (ch = yield); + if (ch >= "0" && ch <= "9") { + s += ch = yield; - if (ch >= '0' && ch <= '9') { - s += (ch = yield); + if (ch >= "0" && ch <= "9") { + s += ch = yield; } } } // skip modifier - if (ch === ';') { - s += (ch = yield); + if (ch === ";") { + s += ch = yield; - if (ch >= '0' && ch <= '9') { + if (ch >= "0" && ch <= "9") { s += yield; } } @@ -207,9 +206,7 @@ function* emitKeys(stream) { code += match[1] + match[3]; modifier = (match[2] || 1) - 1; } - } else if ( - (match = RegExpPrototypeExec(/^((\d;)?(\d))?([A-Za-z])$/, cmd)) - ) { + } else if ((match = RegExpPrototypeExec(/^((\d;)?(\d))?([A-Za-z])$/, cmd))) { code += match[4]; modifier = (match[3] || 1) - 1; } else { @@ -226,137 +223,308 @@ function* emitKeys(stream) { // Parse the key itself switch (code) { /* xterm/gnome ESC [ letter (with modifier) */ - case '[P': key.name = 'f1'; break; - case '[Q': key.name = 'f2'; break; - case '[R': key.name = 'f3'; break; - case '[S': key.name = 'f4'; break; + case "[P": + key.name = "f1"; + break; + case "[Q": + key.name = "f2"; + break; + case "[R": + key.name = "f3"; + break; + case "[S": + key.name = "f4"; + break; /* xterm/gnome ESC O letter (without modifier) */ - case 'OP': key.name = 'f1'; break; - case 'OQ': key.name = 'f2'; break; - case 'OR': key.name = 'f3'; break; - case 'OS': key.name = 'f4'; break; + case "OP": + key.name = "f1"; + break; + case "OQ": + key.name = "f2"; + break; + case "OR": + key.name = "f3"; + break; + case "OS": + key.name = "f4"; + break; /* xterm/rxvt ESC [ number ~ */ - case '[11~': key.name = 'f1'; break; - case '[12~': key.name = 'f2'; break; - case '[13~': key.name = 'f3'; break; - case '[14~': key.name = 'f4'; break; + case "[11~": + key.name = "f1"; + break; + case "[12~": + key.name = "f2"; + break; + case "[13~": + key.name = "f3"; + break; + case "[14~": + key.name = "f4"; + break; /* paste bracket mode */ - case '[200~': key.name = 'paste-start'; break; - case '[201~': key.name = 'paste-end'; break; + case "[200~": + key.name = "paste-start"; + break; + case "[201~": + key.name = "paste-end"; + break; /* from Cygwin and used in libuv */ - case '[[A': key.name = 'f1'; break; - case '[[B': key.name = 'f2'; break; - case '[[C': key.name = 'f3'; break; - case '[[D': key.name = 'f4'; break; - case '[[E': key.name = 'f5'; break; + case "[[A": + key.name = "f1"; + break; + case "[[B": + key.name = "f2"; + break; + case "[[C": + key.name = "f3"; + break; + case "[[D": + key.name = "f4"; + break; + case "[[E": + key.name = "f5"; + break; /* common */ - case '[15~': key.name = 'f5'; break; - case '[17~': key.name = 'f6'; break; - case '[18~': key.name = 'f7'; break; - case '[19~': key.name = 'f8'; break; - case '[20~': key.name = 'f9'; break; - case '[21~': key.name = 'f10'; break; - case '[23~': key.name = 'f11'; break; - case '[24~': key.name = 'f12'; break; + case "[15~": + key.name = "f5"; + break; + case "[17~": + key.name = "f6"; + break; + case "[18~": + key.name = "f7"; + break; + case "[19~": + key.name = "f8"; + break; + case "[20~": + key.name = "f9"; + break; + case "[21~": + key.name = "f10"; + break; + case "[23~": + key.name = "f11"; + break; + case "[24~": + key.name = "f12"; + break; /* xterm ESC [ letter */ - case '[A': key.name = 'up'; break; - case '[B': key.name = 'down'; break; - case '[C': key.name = 'right'; break; - case '[D': key.name = 'left'; break; - case '[E': key.name = 'clear'; break; - case '[F': key.name = 'end'; break; - case '[H': key.name = 'home'; break; + case "[A": + key.name = "up"; + break; + case "[B": + key.name = "down"; + break; + case "[C": + key.name = "right"; + break; + case "[D": + key.name = "left"; + break; + case "[E": + key.name = "clear"; + break; + case "[F": + key.name = "end"; + break; + case "[H": + key.name = "home"; + break; /* xterm/gnome ESC O letter */ - case 'OA': key.name = 'up'; break; - case 'OB': key.name = 'down'; break; - case 'OC': key.name = 'right'; break; - case 'OD': key.name = 'left'; break; - case 'OE': key.name = 'clear'; break; - case 'OF': key.name = 'end'; break; - case 'OH': key.name = 'home'; break; + case "OA": + key.name = "up"; + break; + case "OB": + key.name = "down"; + break; + case "OC": + key.name = "right"; + break; + case "OD": + key.name = "left"; + break; + case "OE": + key.name = "clear"; + break; + case "OF": + key.name = "end"; + break; + case "OH": + key.name = "home"; + break; /* xterm/rxvt ESC [ number ~ */ - case '[1~': key.name = 'home'; break; - case '[2~': key.name = 'insert'; break; - case '[3~': key.name = 'delete'; break; - case '[4~': key.name = 'end'; break; - case '[5~': key.name = 'pageup'; break; - case '[6~': key.name = 'pagedown'; break; + case "[1~": + key.name = "home"; + break; + case "[2~": + key.name = "insert"; + break; + case "[3~": + key.name = "delete"; + break; + case "[4~": + key.name = "end"; + break; + case "[5~": + key.name = "pageup"; + break; + case "[6~": + key.name = "pagedown"; + break; /* putty */ - case '[[5~': key.name = 'pageup'; break; - case '[[6~': key.name = 'pagedown'; break; + case "[[5~": + key.name = "pageup"; + break; + case "[[6~": + key.name = "pagedown"; + break; /* rxvt */ - case '[7~': key.name = 'home'; break; - case '[8~': key.name = 'end'; break; + case "[7~": + key.name = "home"; + break; + case "[8~": + key.name = "end"; + break; /* rxvt keys with modifiers */ - case '[a': key.name = 'up'; key.shift = true; break; - case '[b': key.name = 'down'; key.shift = true; break; - case '[c': key.name = 'right'; key.shift = true; break; - case '[d': key.name = 'left'; key.shift = true; break; - case '[e': key.name = 'clear'; key.shift = true; break; - - case '[2$': key.name = 'insert'; key.shift = true; break; - case '[3$': key.name = 'delete'; key.shift = true; break; - case '[5$': key.name = 'pageup'; key.shift = true; break; - case '[6$': key.name = 'pagedown'; key.shift = true; break; - case '[7$': key.name = 'home'; key.shift = true; break; - case '[8$': key.name = 'end'; key.shift = true; break; - - case 'Oa': key.name = 'up'; key.ctrl = true; break; - case 'Ob': key.name = 'down'; key.ctrl = true; break; - case 'Oc': key.name = 'right'; key.ctrl = true; break; - case 'Od': key.name = 'left'; key.ctrl = true; break; - case 'Oe': key.name = 'clear'; key.ctrl = true; break; - - case '[2^': key.name = 'insert'; key.ctrl = true; break; - case '[3^': key.name = 'delete'; key.ctrl = true; break; - case '[5^': key.name = 'pageup'; key.ctrl = true; break; - case '[6^': key.name = 'pagedown'; key.ctrl = true; break; - case '[7^': key.name = 'home'; key.ctrl = true; break; - case '[8^': key.name = 'end'; key.ctrl = true; break; + case "[a": + key.name = "up"; + key.shift = true; + break; + case "[b": + key.name = "down"; + key.shift = true; + break; + case "[c": + key.name = "right"; + key.shift = true; + break; + case "[d": + key.name = "left"; + key.shift = true; + break; + case "[e": + key.name = "clear"; + key.shift = true; + break; + + case "[2$": + key.name = "insert"; + key.shift = true; + break; + case "[3$": + key.name = "delete"; + key.shift = true; + break; + case "[5$": + key.name = "pageup"; + key.shift = true; + break; + case "[6$": + key.name = "pagedown"; + key.shift = true; + break; + case "[7$": + key.name = "home"; + key.shift = true; + break; + case "[8$": + key.name = "end"; + key.shift = true; + break; + + case "Oa": + key.name = "up"; + key.ctrl = true; + break; + case "Ob": + key.name = "down"; + key.ctrl = true; + break; + case "Oc": + key.name = "right"; + key.ctrl = true; + break; + case "Od": + key.name = "left"; + key.ctrl = true; + break; + case "Oe": + key.name = "clear"; + key.ctrl = true; + break; + + case "[2^": + key.name = "insert"; + key.ctrl = true; + break; + case "[3^": + key.name = "delete"; + key.ctrl = true; + break; + case "[5^": + key.name = "pageup"; + key.ctrl = true; + break; + case "[6^": + key.name = "pagedown"; + key.ctrl = true; + break; + case "[7^": + key.name = "home"; + key.ctrl = true; + break; + case "[8^": + key.name = "end"; + key.ctrl = true; + break; /* misc. */ - case '[Z': key.name = 'tab'; key.shift = true; break; - default: key.name = 'undefined'; break; + case "[Z": + key.name = "tab"; + key.shift = true; + break; + default: + key.name = "undefined"; + break; } - } else if (ch === '\r') { + } else if (ch === "\r") { // carriage return - key.name = 'return'; + key.name = "return"; key.meta = escaped; - } else if (ch === '\n') { + } else if (ch === "\n") { // Enter, should have been called linefeed - key.name = 'enter'; + key.name = "enter"; key.meta = escaped; - } else if (ch === '\t') { + } else if (ch === "\t") { // tab - key.name = 'tab'; + key.name = "tab"; key.meta = escaped; - } else if (ch === '\b' || ch === '\x7f') { + } else if (ch === "\b" || ch === "\x7f") { // backspace or ctrl+h - key.name = 'backspace'; + key.name = "backspace"; key.meta = escaped; } else if (ch === kEscape) { // escape key - key.name = 'escape'; + key.name = "escape"; key.meta = escaped; - } else if (ch === ' ') { - key.name = 'space'; + } else if (ch === " ") { + key.name = "space"; key.meta = escaped; - } else if (!escaped && ch <= '\x1a') { + } else if (!escaped && ch <= "\x1a") { // ctrl+letter - key.name = StringFromCharCode( - StringPrototypeCharCodeAt(ch) + StringPrototypeCharCodeAt('a') - 1, - ); + key.name = StringFromCharCode(StringPrototypeCharCodeAt(ch) + StringPrototypeCharCodeAt("a") - 1); key.ctrl = true; } else if (RegExpPrototypeExec(/^[0-9A-Za-z]$/, ch) !== null) { // Letter, number, shift+letter @@ -365,7 +533,7 @@ function* emitKeys(stream) { key.meta = escaped; } else if (escaped) { // Escape sequence timeout - key.name = ch.length ? undefined : 'escape'; + key.name = ch.length ? undefined : "escape"; key.meta = true; } @@ -373,10 +541,10 @@ function* emitKeys(stream) { if (s.length !== 0 && (key.name !== undefined || escaped)) { /* Named character or sequence */ - stream.emit('keypress', escaped ? undefined : s, key); + stream.emit("keypress", escaped ? undefined : s, key); } else if (charLengthAt(s, 0) === s.length) { /* Single unnamed character, e.g. "." */ - stream.emit('keypress', s, key); + stream.emit("keypress", s, key); } /* Unrecognized or broken escape sequence, don't emit anything */ } @@ -385,7 +553,7 @@ function* emitKeys(stream) { // This runs in O(n log n). function commonPrefix(strings) { if (strings.length === 0) { - return ''; + return ""; } if (strings.length === 1) { return strings[0]; @@ -401,12 +569,12 @@ function commonPrefix(strings) { return min; } -function reverseString(line, from = '\r', to = '\r') { +function reverseString(line, from = "\r", to = "\r") { const parts = StringPrototypeSplit(line, from); // This implementation should be faster than // ArrayPrototypeJoin(ArrayPrototypeReverse(StringPrototypeSplit(line, from)), to); - let result = ''; + let result = ""; for (let i = parts.length - 1; i > 0; i--) { result += parts[i] + to; } diff --git a/src/js/internal/repl.js b/src/js/internal/repl.js index 90cc29772170..ec29b9f67f06 100644 --- a/src/js/internal/repl.js +++ b/src/js/internal/repl.js @@ -3,13 +3,9 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -'use strict'; +("use strict"); -const { - Number, - NumberIsNaN, - NumberParseInt, -} = primordials; +const { Number, NumberIsNaN, NumberParseInt } = primordials; const REPL = require("node:repl"); const { kStandaloneREPL } = require("internal/repl/utils"); @@ -18,7 +14,7 @@ __node_module__.exports = { __proto__: REPL }; __node_module__.exports.createInternalRepl = createRepl; function createRepl(env, opts, cb) { - if (typeof opts === 'function') { + if (typeof opts === "function") { cb = opts; opts = null; } @@ -36,8 +32,8 @@ function createRepl(env, opts, cb) { if (env.NODE_REPL_MODE) { opts.replMode = { - 'strict': REPL.REPL_MODE_STRICT, - 'sloppy': REPL.REPL_MODE_SLOPPY, + "strict": REPL.REPL_MODE_STRICT, + "sloppy": REPL.REPL_MODE_SLOPPY, }[env.NODE_REPL_MODE.toLowerCase().trim()]; } @@ -52,8 +48,8 @@ function createRepl(env, opts, cb) { opts.size = 1000; } - const term = 'terminal' in opts ? opts.terminal : process.stdout.isTTY; - opts.filePath = term ? env.NODE_REPL_HISTORY : ''; + const term = "terminal" in opts ? opts.terminal : process.stdout.isTTY; + opts.filePath = term ? env.NODE_REPL_HISTORY : ""; const repl = REPL.start(opts); diff --git a/src/js/internal/repl/acorn-walk.js b/src/js/internal/repl/acorn-walk.js index 2c0dc5f240af..4fe8ba412404 100644 --- a/src/js/internal/repl/acorn-walk.js +++ b/src/js/internal/repl/acorn-walk.js @@ -6,6 +6,9 @@ const vm = require("node:vm"); const exportsObj = {}; const moduleObj = { exports: exportsObj }; -const factory = new vm.Script("(function(exports, module){(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n typeof define === 'function' && define.amd ? define(['exports'], factory) :\n (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.acorn = global.acorn || {}, global.acorn.walk = {})));\n})(this, (function (exports) { 'use strict';\n\n // AST walker module for ESTree compatible trees\n\n // A simple walk is one where you simply specify callbacks to be\n // called on specific nodes. The last two arguments are optional. A\n // simple use would be\n //\n // walk.simple(myTree, {\n // Expression: function(node) { ... }\n // });\n //\n // to do something with all expressions. All ESTree node types\n // can be used to identify node types, as well as Expression and\n // Statement, which denote categories of nodes.\n //\n // The base argument can be used to pass a custom (recursive)\n // walker, and state can be used to give this walked an initial\n // state.\n\n function simple(node, visitors, baseVisitor, state, override) {\n if (!baseVisitor) { baseVisitor = base\n ; }(function c(node, st, override) {\n var type = override || node.type;\n visitNode(baseVisitor, type, node, st, c);\n if (visitors[type]) { visitors[type](node, st); }\n })(node, state, override);\n }\n\n // An ancestor walk keeps an array of ancestor nodes (including the\n // current node) and passes them to the callback as third parameter\n // (and also as state parameter when no other state is present).\n function ancestor(node, visitors, baseVisitor, state, override) {\n var ancestors = [];\n if (!baseVisitor) { baseVisitor = base\n ; }(function c(node, st, override) {\n var type = override || node.type;\n var isNew = node !== ancestors[ancestors.length - 1];\n if (isNew) { ancestors.push(node); }\n visitNode(baseVisitor, type, node, st, c);\n if (visitors[type]) { visitors[type](node, st || ancestors, ancestors); }\n if (isNew) { ancestors.pop(); }\n })(node, state, override);\n }\n\n // A recursive walk is one where your functions override the default\n // walkers. They can modify and replace the state parameter that's\n // threaded through the walk, and can opt how and whether to walk\n // their child nodes (by calling their third argument on these\n // nodes).\n function recursive(node, state, funcs, baseVisitor, override) {\n var visitor = funcs ? make(funcs, baseVisitor || undefined) : baseVisitor\n ;(function c(node, st, override) {\n visitor[override || node.type](node, st, c);\n })(node, state, override);\n }\n\n function makeTest(test) {\n if (typeof test === \"string\")\n { return function (type) { return type === test; } }\n else if (!test)\n { return function () { return true; } }\n else\n { return test }\n }\n\n var Found = function Found(node, state) { this.node = node; this.state = state; };\n\n // A full walk triggers the callback on each node\n function full(node, callback, baseVisitor, state, override) {\n if (!baseVisitor) { baseVisitor = base; }\n var last\n ;(function c(node, st, override) {\n var type = override || node.type;\n visitNode(baseVisitor, type, node, st, c);\n if (last !== node) {\n callback(node, st, type);\n last = node;\n }\n })(node, state, override);\n }\n\n // An fullAncestor walk is like an ancestor walk, but triggers\n // the callback on each node\n function fullAncestor(node, callback, baseVisitor, state) {\n if (!baseVisitor) { baseVisitor = base; }\n var ancestors = [], last\n ;(function c(node, st, override) {\n var type = override || node.type;\n var isNew = node !== ancestors[ancestors.length - 1];\n if (isNew) { ancestors.push(node); }\n visitNode(baseVisitor, type, node, st, c);\n if (last !== node) {\n callback(node, st || ancestors, ancestors, type);\n last = node;\n }\n if (isNew) { ancestors.pop(); }\n })(node, state);\n }\n\n // Find a node with a given start, end, and type (all are optional,\n // null can be used as wildcard). Returns a {node, state} object, or\n // undefined when it doesn't find a matching node.\n function findNodeAt(node, start, end, test, baseVisitor, state) {\n if (!baseVisitor) { baseVisitor = base; }\n test = makeTest(test);\n try {\n (function c(node, st, override) {\n var type = override || node.type;\n if ((start == null || node.start <= start) &&\n (end == null || node.end >= end))\n { visitNode(baseVisitor, type, node, st, c); }\n if ((start == null || node.start === start) &&\n (end == null || node.end === end) &&\n test(type, node))\n { throw new Found(node, st) }\n })(node, state);\n } catch (e) {\n if (e instanceof Found) { return e }\n throw e\n }\n }\n\n // Find the innermost node of a given type that contains the given\n // position. Interface similar to findNodeAt.\n function findNodeAround(node, pos, test, baseVisitor, state) {\n test = makeTest(test);\n if (!baseVisitor) { baseVisitor = base; }\n try {\n (function c(node, st, override) {\n var type = override || node.type;\n if (node.start > pos || node.end < pos) { return }\n visitNode(baseVisitor, type, node, st, c);\n if (test(type, node)) { throw new Found(node, st) }\n })(node, state);\n } catch (e) {\n if (e instanceof Found) { return e }\n throw e\n }\n }\n\n // Find the outermost matching node after a given position.\n function findNodeAfter(node, pos, test, baseVisitor, state) {\n test = makeTest(test);\n if (!baseVisitor) { baseVisitor = base; }\n try {\n (function c(node, st, override) {\n if (node.end < pos) { return }\n var type = override || node.type;\n if (node.start >= pos && test(type, node)) { throw new Found(node, st) }\n visitNode(baseVisitor, type, node, st, c);\n })(node, state);\n } catch (e) {\n if (e instanceof Found) { return e }\n throw e\n }\n }\n\n // Find the outermost matching node before a given position.\n function findNodeBefore(node, pos, test, baseVisitor, state) {\n test = makeTest(test);\n if (!baseVisitor) { baseVisitor = base; }\n var max\n ;(function c(node, st, override) {\n if (node.start > pos) { return }\n var type = override || node.type;\n if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))\n { max = new Found(node, st); }\n visitNode(baseVisitor, type, node, st, c);\n })(node, state);\n return max\n }\n\n // Used to create a custom walker. Will fill in all missing node\n // type properties with the defaults.\n function make(funcs, baseVisitor) {\n var visitor = Object.create(baseVisitor || base);\n for (var type in funcs) { visitor[type] = funcs[type]; }\n return visitor\n }\n\n function skipThrough(node, st, c) { c(node, st); }\n function ignore(_node, _st, _c) {}\n\n function visitNode(baseVisitor, type, node, st, c) {\n if (baseVisitor[type] == null) { throw new Error((\"No walker function defined for node type \" + type)) }\n baseVisitor[type](node, st, c);\n }\n\n // Node walkers.\n\n var base = {};\n\n base.Program = base.BlockStatement = base.StaticBlock = function (node, st, c) {\n for (var i = 0, list = node.body; i < list.length; i += 1)\n {\n var stmt = list[i];\n\n c(stmt, st, \"Statement\");\n }\n };\n base.Statement = skipThrough;\n base.EmptyStatement = ignore;\n base.ExpressionStatement = base.ParenthesizedExpression = base.ChainExpression =\n function (node, st, c) { return c(node.expression, st, \"Expression\"); };\n base.IfStatement = function (node, st, c) {\n c(node.test, st, \"Expression\");\n c(node.consequent, st, \"Statement\");\n if (node.alternate) { c(node.alternate, st, \"Statement\"); }\n };\n base.LabeledStatement = function (node, st, c) { return c(node.body, st, \"Statement\"); };\n base.BreakStatement = base.ContinueStatement = ignore;\n base.WithStatement = function (node, st, c) {\n c(node.object, st, \"Expression\");\n c(node.body, st, \"Statement\");\n };\n base.SwitchStatement = function (node, st, c) {\n c(node.discriminant, st, \"Expression\");\n for (var i = 0, list = node.cases; i < list.length; i += 1) {\n var cs = list[i];\n\n c(cs, st);\n }\n };\n base.SwitchCase = function (node, st, c) {\n if (node.test) { c(node.test, st, \"Expression\"); }\n for (var i = 0, list = node.consequent; i < list.length; i += 1)\n {\n var cons = list[i];\n\n c(cons, st, \"Statement\");\n }\n };\n base.ReturnStatement = base.YieldExpression = base.AwaitExpression = function (node, st, c) {\n if (node.argument) { c(node.argument, st, \"Expression\"); }\n };\n base.ThrowStatement = base.SpreadElement =\n function (node, st, c) { return c(node.argument, st, \"Expression\"); };\n base.TryStatement = function (node, st, c) {\n c(node.block, st, \"Statement\");\n if (node.handler) { c(node.handler, st); }\n if (node.finalizer) { c(node.finalizer, st, \"Statement\"); }\n };\n base.CatchClause = function (node, st, c) {\n if (node.param) { c(node.param, st, \"Pattern\"); }\n c(node.body, st, \"Statement\");\n };\n base.WhileStatement = base.DoWhileStatement = function (node, st, c) {\n c(node.test, st, \"Expression\");\n c(node.body, st, \"Statement\");\n };\n base.ForStatement = function (node, st, c) {\n if (node.init) { c(node.init, st, \"ForInit\"); }\n if (node.test) { c(node.test, st, \"Expression\"); }\n if (node.update) { c(node.update, st, \"Expression\"); }\n c(node.body, st, \"Statement\");\n };\n base.ForInStatement = base.ForOfStatement = function (node, st, c) {\n c(node.left, st, \"ForInit\");\n c(node.right, st, \"Expression\");\n c(node.body, st, \"Statement\");\n };\n base.ForInit = function (node, st, c) {\n if (node.type === \"VariableDeclaration\") { c(node, st); }\n else { c(node, st, \"Expression\"); }\n };\n base.DebuggerStatement = ignore;\n\n base.FunctionDeclaration = function (node, st, c) { return c(node, st, \"Function\"); };\n base.VariableDeclaration = function (node, st, c) {\n for (var i = 0, list = node.declarations; i < list.length; i += 1)\n {\n var decl = list[i];\n\n c(decl, st);\n }\n };\n base.VariableDeclarator = function (node, st, c) {\n c(node.id, st, \"Pattern\");\n if (node.init) { c(node.init, st, \"Expression\"); }\n };\n\n base.Function = function (node, st, c) {\n if (node.id) { c(node.id, st, \"Pattern\"); }\n for (var i = 0, list = node.params; i < list.length; i += 1)\n {\n var param = list[i];\n\n c(param, st, \"Pattern\");\n }\n c(node.body, st, node.expression ? \"Expression\" : \"Statement\");\n };\n\n base.Pattern = function (node, st, c) {\n if (node.type === \"Identifier\")\n { c(node, st, \"VariablePattern\"); }\n else if (node.type === \"MemberExpression\")\n { c(node, st, \"MemberPattern\"); }\n else\n { c(node, st); }\n };\n base.VariablePattern = ignore;\n base.MemberPattern = skipThrough;\n base.RestElement = function (node, st, c) { return c(node.argument, st, \"Pattern\"); };\n base.ArrayPattern = function (node, st, c) {\n for (var i = 0, list = node.elements; i < list.length; i += 1) {\n var elt = list[i];\n\n if (elt) { c(elt, st, \"Pattern\"); }\n }\n };\n base.ObjectPattern = function (node, st, c) {\n for (var i = 0, list = node.properties; i < list.length; i += 1) {\n var prop = list[i];\n\n if (prop.type === \"Property\") {\n if (prop.computed) { c(prop.key, st, \"Expression\"); }\n c(prop.value, st, \"Pattern\");\n } else if (prop.type === \"RestElement\") {\n c(prop.argument, st, \"Pattern\");\n }\n }\n };\n\n base.Expression = skipThrough;\n base.ThisExpression = base.Super = base.MetaProperty = ignore;\n base.ArrayExpression = function (node, st, c) {\n for (var i = 0, list = node.elements; i < list.length; i += 1) {\n var elt = list[i];\n\n if (elt) { c(elt, st, \"Expression\"); }\n }\n };\n base.ObjectExpression = function (node, st, c) {\n for (var i = 0, list = node.properties; i < list.length; i += 1)\n {\n var prop = list[i];\n\n c(prop, st);\n }\n };\n base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;\n base.SequenceExpression = function (node, st, c) {\n for (var i = 0, list = node.expressions; i < list.length; i += 1)\n {\n var expr = list[i];\n\n c(expr, st, \"Expression\");\n }\n };\n base.TemplateLiteral = function (node, st, c) {\n for (var i = 0, list = node.quasis; i < list.length; i += 1)\n {\n var quasi = list[i];\n\n c(quasi, st);\n }\n\n for (var i$1 = 0, list$1 = node.expressions; i$1 < list$1.length; i$1 += 1)\n {\n var expr = list$1[i$1];\n\n c(expr, st, \"Expression\");\n }\n };\n base.TemplateElement = ignore;\n base.UnaryExpression = base.UpdateExpression = function (node, st, c) {\n c(node.argument, st, \"Expression\");\n };\n base.BinaryExpression = base.LogicalExpression = function (node, st, c) {\n c(node.left, st, \"Expression\");\n c(node.right, st, \"Expression\");\n };\n base.AssignmentExpression = base.AssignmentPattern = function (node, st, c) {\n c(node.left, st, \"Pattern\");\n c(node.right, st, \"Expression\");\n };\n base.ConditionalExpression = function (node, st, c) {\n c(node.test, st, \"Expression\");\n c(node.consequent, st, \"Expression\");\n c(node.alternate, st, \"Expression\");\n };\n base.NewExpression = base.CallExpression = function (node, st, c) {\n c(node.callee, st, \"Expression\");\n if (node.arguments)\n { for (var i = 0, list = node.arguments; i < list.length; i += 1)\n {\n var arg = list[i];\n\n c(arg, st, \"Expression\");\n } }\n };\n base.MemberExpression = function (node, st, c) {\n c(node.object, st, \"Expression\");\n if (node.computed) { c(node.property, st, \"Expression\"); }\n };\n base.ExportNamedDeclaration = base.ExportDefaultDeclaration = function (node, st, c) {\n if (node.declaration)\n { c(node.declaration, st, node.type === \"ExportNamedDeclaration\" || node.declaration.id ? \"Statement\" : \"Expression\"); }\n if (node.source) { c(node.source, st, \"Expression\"); }\n if (node.attributes)\n { for (var i = 0, list = node.attributes; i < list.length; i += 1)\n {\n var attr = list[i];\n\n c(attr, st);\n } }\n };\n base.ExportAllDeclaration = function (node, st, c) {\n if (node.exported)\n { c(node.exported, st); }\n c(node.source, st, \"Expression\");\n if (node.attributes)\n { for (var i = 0, list = node.attributes; i < list.length; i += 1)\n {\n var attr = list[i];\n\n c(attr, st);\n } }\n };\n base.ImportAttribute = function (node, st, c) {\n c(node.value, st, \"Expression\");\n };\n base.ImportDeclaration = function (node, st, c) {\n for (var i = 0, list = node.specifiers; i < list.length; i += 1)\n {\n var spec = list[i];\n\n c(spec, st);\n }\n c(node.source, st, \"Expression\");\n if (node.attributes)\n { for (var i$1 = 0, list$1 = node.attributes; i$1 < list$1.length; i$1 += 1)\n {\n var attr = list$1[i$1];\n\n c(attr, st);\n } }\n };\n base.ImportExpression = function (node, st, c) {\n c(node.source, st, \"Expression\");\n if (node.options) { c(node.options, st, \"Expression\"); }\n };\n base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.PrivateIdentifier = base.Literal = ignore;\n\n base.TaggedTemplateExpression = function (node, st, c) {\n c(node.tag, st, \"Expression\");\n c(node.quasi, st, \"Expression\");\n };\n base.ClassDeclaration = base.ClassExpression = function (node, st, c) { return c(node, st, \"Class\"); };\n base.Class = function (node, st, c) {\n if (node.id) { c(node.id, st, \"Pattern\"); }\n if (node.superClass) { c(node.superClass, st, \"Expression\"); }\n c(node.body, st);\n };\n base.ClassBody = function (node, st, c) {\n for (var i = 0, list = node.body; i < list.length; i += 1)\n {\n var elt = list[i];\n\n c(elt, st);\n }\n };\n base.MethodDefinition = base.PropertyDefinition = base.Property = function (node, st, c) {\n if (node.computed) { c(node.key, st, \"Expression\"); }\n if (node.value) { c(node.value, st, \"Expression\"); }\n };\n\n exports.ancestor = ancestor;\n exports.base = base;\n exports.findNodeAfter = findNodeAfter;\n exports.findNodeAround = findNodeAround;\n exports.findNodeAt = findNodeAt;\n exports.findNodeBefore = findNodeBefore;\n exports.full = full;\n exports.fullAncestor = fullAncestor;\n exports.make = make;\n exports.recursive = recursive;\n exports.simple = simple;\n\n}));\n\n})", { filename: "acorn-walk.js" }).runInThisContext(); +const factory = new vm.Script( + '(function(exports, module){(function (global, factory) {\n typeof exports === \'object\' && typeof module !== \'undefined\' ? factory(exports) :\n typeof define === \'function\' && define.amd ? define([\'exports\'], factory) :\n (global = typeof globalThis !== \'undefined\' ? globalThis : global || self, factory((global.acorn = global.acorn || {}, global.acorn.walk = {})));\n})(this, (function (exports) { \'use strict\';\n\n // AST walker module for ESTree compatible trees\n\n // A simple walk is one where you simply specify callbacks to be\n // called on specific nodes. The last two arguments are optional. A\n // simple use would be\n //\n // walk.simple(myTree, {\n // Expression: function(node) { ... }\n // });\n //\n // to do something with all expressions. All ESTree node types\n // can be used to identify node types, as well as Expression and\n // Statement, which denote categories of nodes.\n //\n // The base argument can be used to pass a custom (recursive)\n // walker, and state can be used to give this walked an initial\n // state.\n\n function simple(node, visitors, baseVisitor, state, override) {\n if (!baseVisitor) { baseVisitor = base\n ; }(function c(node, st, override) {\n var type = override || node.type;\n visitNode(baseVisitor, type, node, st, c);\n if (visitors[type]) { visitors[type](node, st); }\n })(node, state, override);\n }\n\n // An ancestor walk keeps an array of ancestor nodes (including the\n // current node) and passes them to the callback as third parameter\n // (and also as state parameter when no other state is present).\n function ancestor(node, visitors, baseVisitor, state, override) {\n var ancestors = [];\n if (!baseVisitor) { baseVisitor = base\n ; }(function c(node, st, override) {\n var type = override || node.type;\n var isNew = node !== ancestors[ancestors.length - 1];\n if (isNew) { ancestors.push(node); }\n visitNode(baseVisitor, type, node, st, c);\n if (visitors[type]) { visitors[type](node, st || ancestors, ancestors); }\n if (isNew) { ancestors.pop(); }\n })(node, state, override);\n }\n\n // A recursive walk is one where your functions override the default\n // walkers. They can modify and replace the state parameter that\'s\n // threaded through the walk, and can opt how and whether to walk\n // their child nodes (by calling their third argument on these\n // nodes).\n function recursive(node, state, funcs, baseVisitor, override) {\n var visitor = funcs ? make(funcs, baseVisitor || undefined) : baseVisitor\n ;(function c(node, st, override) {\n visitor[override || node.type](node, st, c);\n })(node, state, override);\n }\n\n function makeTest(test) {\n if (typeof test === "string")\n { return function (type) { return type === test; } }\n else if (!test)\n { return function () { return true; } }\n else\n { return test }\n }\n\n var Found = function Found(node, state) { this.node = node; this.state = state; };\n\n // A full walk triggers the callback on each node\n function full(node, callback, baseVisitor, state, override) {\n if (!baseVisitor) { baseVisitor = base; }\n var last\n ;(function c(node, st, override) {\n var type = override || node.type;\n visitNode(baseVisitor, type, node, st, c);\n if (last !== node) {\n callback(node, st, type);\n last = node;\n }\n })(node, state, override);\n }\n\n // An fullAncestor walk is like an ancestor walk, but triggers\n // the callback on each node\n function fullAncestor(node, callback, baseVisitor, state) {\n if (!baseVisitor) { baseVisitor = base; }\n var ancestors = [], last\n ;(function c(node, st, override) {\n var type = override || node.type;\n var isNew = node !== ancestors[ancestors.length - 1];\n if (isNew) { ancestors.push(node); }\n visitNode(baseVisitor, type, node, st, c);\n if (last !== node) {\n callback(node, st || ancestors, ancestors, type);\n last = node;\n }\n if (isNew) { ancestors.pop(); }\n })(node, state);\n }\n\n // Find a node with a given start, end, and type (all are optional,\n // null can be used as wildcard). Returns a {node, state} object, or\n // undefined when it doesn\'t find a matching node.\n function findNodeAt(node, start, end, test, baseVisitor, state) {\n if (!baseVisitor) { baseVisitor = base; }\n test = makeTest(test);\n try {\n (function c(node, st, override) {\n var type = override || node.type;\n if ((start == null || node.start <= start) &&\n (end == null || node.end >= end))\n { visitNode(baseVisitor, type, node, st, c); }\n if ((start == null || node.start === start) &&\n (end == null || node.end === end) &&\n test(type, node))\n { throw new Found(node, st) }\n })(node, state);\n } catch (e) {\n if (e instanceof Found) { return e }\n throw e\n }\n }\n\n // Find the innermost node of a given type that contains the given\n // position. Interface similar to findNodeAt.\n function findNodeAround(node, pos, test, baseVisitor, state) {\n test = makeTest(test);\n if (!baseVisitor) { baseVisitor = base; }\n try {\n (function c(node, st, override) {\n var type = override || node.type;\n if (node.start > pos || node.end < pos) { return }\n visitNode(baseVisitor, type, node, st, c);\n if (test(type, node)) { throw new Found(node, st) }\n })(node, state);\n } catch (e) {\n if (e instanceof Found) { return e }\n throw e\n }\n }\n\n // Find the outermost matching node after a given position.\n function findNodeAfter(node, pos, test, baseVisitor, state) {\n test = makeTest(test);\n if (!baseVisitor) { baseVisitor = base; }\n try {\n (function c(node, st, override) {\n if (node.end < pos) { return }\n var type = override || node.type;\n if (node.start >= pos && test(type, node)) { throw new Found(node, st) }\n visitNode(baseVisitor, type, node, st, c);\n })(node, state);\n } catch (e) {\n if (e instanceof Found) { return e }\n throw e\n }\n }\n\n // Find the outermost matching node before a given position.\n function findNodeBefore(node, pos, test, baseVisitor, state) {\n test = makeTest(test);\n if (!baseVisitor) { baseVisitor = base; }\n var max\n ;(function c(node, st, override) {\n if (node.start > pos) { return }\n var type = override || node.type;\n if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))\n { max = new Found(node, st); }\n visitNode(baseVisitor, type, node, st, c);\n })(node, state);\n return max\n }\n\n // Used to create a custom walker. Will fill in all missing node\n // type properties with the defaults.\n function make(funcs, baseVisitor) {\n var visitor = Object.create(baseVisitor || base);\n for (var type in funcs) { visitor[type] = funcs[type]; }\n return visitor\n }\n\n function skipThrough(node, st, c) { c(node, st); }\n function ignore(_node, _st, _c) {}\n\n function visitNode(baseVisitor, type, node, st, c) {\n if (baseVisitor[type] == null) { throw new Error(("No walker function defined for node type " + type)) }\n baseVisitor[type](node, st, c);\n }\n\n // Node walkers.\n\n var base = {};\n\n base.Program = base.BlockStatement = base.StaticBlock = function (node, st, c) {\n for (var i = 0, list = node.body; i < list.length; i += 1)\n {\n var stmt = list[i];\n\n c(stmt, st, "Statement");\n }\n };\n base.Statement = skipThrough;\n base.EmptyStatement = ignore;\n base.ExpressionStatement = base.ParenthesizedExpression = base.ChainExpression =\n function (node, st, c) { return c(node.expression, st, "Expression"); };\n base.IfStatement = function (node, st, c) {\n c(node.test, st, "Expression");\n c(node.consequent, st, "Statement");\n if (node.alternate) { c(node.alternate, st, "Statement"); }\n };\n base.LabeledStatement = function (node, st, c) { return c(node.body, st, "Statement"); };\n base.BreakStatement = base.ContinueStatement = ignore;\n base.WithStatement = function (node, st, c) {\n c(node.object, st, "Expression");\n c(node.body, st, "Statement");\n };\n base.SwitchStatement = function (node, st, c) {\n c(node.discriminant, st, "Expression");\n for (var i = 0, list = node.cases; i < list.length; i += 1) {\n var cs = list[i];\n\n c(cs, st);\n }\n };\n base.SwitchCase = function (node, st, c) {\n if (node.test) { c(node.test, st, "Expression"); }\n for (var i = 0, list = node.consequent; i < list.length; i += 1)\n {\n var cons = list[i];\n\n c(cons, st, "Statement");\n }\n };\n base.ReturnStatement = base.YieldExpression = base.AwaitExpression = function (node, st, c) {\n if (node.argument) { c(node.argument, st, "Expression"); }\n };\n base.ThrowStatement = base.SpreadElement =\n function (node, st, c) { return c(node.argument, st, "Expression"); };\n base.TryStatement = function (node, st, c) {\n c(node.block, st, "Statement");\n if (node.handler) { c(node.handler, st); }\n if (node.finalizer) { c(node.finalizer, st, "Statement"); }\n };\n base.CatchClause = function (node, st, c) {\n if (node.param) { c(node.param, st, "Pattern"); }\n c(node.body, st, "Statement");\n };\n base.WhileStatement = base.DoWhileStatement = function (node, st, c) {\n c(node.test, st, "Expression");\n c(node.body, st, "Statement");\n };\n base.ForStatement = function (node, st, c) {\n if (node.init) { c(node.init, st, "ForInit"); }\n if (node.test) { c(node.test, st, "Expression"); }\n if (node.update) { c(node.update, st, "Expression"); }\n c(node.body, st, "Statement");\n };\n base.ForInStatement = base.ForOfStatement = function (node, st, c) {\n c(node.left, st, "ForInit");\n c(node.right, st, "Expression");\n c(node.body, st, "Statement");\n };\n base.ForInit = function (node, st, c) {\n if (node.type === "VariableDeclaration") { c(node, st); }\n else { c(node, st, "Expression"); }\n };\n base.DebuggerStatement = ignore;\n\n base.FunctionDeclaration = function (node, st, c) { return c(node, st, "Function"); };\n base.VariableDeclaration = function (node, st, c) {\n for (var i = 0, list = node.declarations; i < list.length; i += 1)\n {\n var decl = list[i];\n\n c(decl, st);\n }\n };\n base.VariableDeclarator = function (node, st, c) {\n c(node.id, st, "Pattern");\n if (node.init) { c(node.init, st, "Expression"); }\n };\n\n base.Function = function (node, st, c) {\n if (node.id) { c(node.id, st, "Pattern"); }\n for (var i = 0, list = node.params; i < list.length; i += 1)\n {\n var param = list[i];\n\n c(param, st, "Pattern");\n }\n c(node.body, st, node.expression ? "Expression" : "Statement");\n };\n\n base.Pattern = function (node, st, c) {\n if (node.type === "Identifier")\n { c(node, st, "VariablePattern"); }\n else if (node.type === "MemberExpression")\n { c(node, st, "MemberPattern"); }\n else\n { c(node, st); }\n };\n base.VariablePattern = ignore;\n base.MemberPattern = skipThrough;\n base.RestElement = function (node, st, c) { return c(node.argument, st, "Pattern"); };\n base.ArrayPattern = function (node, st, c) {\n for (var i = 0, list = node.elements; i < list.length; i += 1) {\n var elt = list[i];\n\n if (elt) { c(elt, st, "Pattern"); }\n }\n };\n base.ObjectPattern = function (node, st, c) {\n for (var i = 0, list = node.properties; i < list.length; i += 1) {\n var prop = list[i];\n\n if (prop.type === "Property") {\n if (prop.computed) { c(prop.key, st, "Expression"); }\n c(prop.value, st, "Pattern");\n } else if (prop.type === "RestElement") {\n c(prop.argument, st, "Pattern");\n }\n }\n };\n\n base.Expression = skipThrough;\n base.ThisExpression = base.Super = base.MetaProperty = ignore;\n base.ArrayExpression = function (node, st, c) {\n for (var i = 0, list = node.elements; i < list.length; i += 1) {\n var elt = list[i];\n\n if (elt) { c(elt, st, "Expression"); }\n }\n };\n base.ObjectExpression = function (node, st, c) {\n for (var i = 0, list = node.properties; i < list.length; i += 1)\n {\n var prop = list[i];\n\n c(prop, st);\n }\n };\n base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;\n base.SequenceExpression = function (node, st, c) {\n for (var i = 0, list = node.expressions; i < list.length; i += 1)\n {\n var expr = list[i];\n\n c(expr, st, "Expression");\n }\n };\n base.TemplateLiteral = function (node, st, c) {\n for (var i = 0, list = node.quasis; i < list.length; i += 1)\n {\n var quasi = list[i];\n\n c(quasi, st);\n }\n\n for (var i$1 = 0, list$1 = node.expressions; i$1 < list$1.length; i$1 += 1)\n {\n var expr = list$1[i$1];\n\n c(expr, st, "Expression");\n }\n };\n base.TemplateElement = ignore;\n base.UnaryExpression = base.UpdateExpression = function (node, st, c) {\n c(node.argument, st, "Expression");\n };\n base.BinaryExpression = base.LogicalExpression = function (node, st, c) {\n c(node.left, st, "Expression");\n c(node.right, st, "Expression");\n };\n base.AssignmentExpression = base.AssignmentPattern = function (node, st, c) {\n c(node.left, st, "Pattern");\n c(node.right, st, "Expression");\n };\n base.ConditionalExpression = function (node, st, c) {\n c(node.test, st, "Expression");\n c(node.consequent, st, "Expression");\n c(node.alternate, st, "Expression");\n };\n base.NewExpression = base.CallExpression = function (node, st, c) {\n c(node.callee, st, "Expression");\n if (node.arguments)\n { for (var i = 0, list = node.arguments; i < list.length; i += 1)\n {\n var arg = list[i];\n\n c(arg, st, "Expression");\n } }\n };\n base.MemberExpression = function (node, st, c) {\n c(node.object, st, "Expression");\n if (node.computed) { c(node.property, st, "Expression"); }\n };\n base.ExportNamedDeclaration = base.ExportDefaultDeclaration = function (node, st, c) {\n if (node.declaration)\n { c(node.declaration, st, node.type === "ExportNamedDeclaration" || node.declaration.id ? "Statement" : "Expression"); }\n if (node.source) { c(node.source, st, "Expression"); }\n if (node.attributes)\n { for (var i = 0, list = node.attributes; i < list.length; i += 1)\n {\n var attr = list[i];\n\n c(attr, st);\n } }\n };\n base.ExportAllDeclaration = function (node, st, c) {\n if (node.exported)\n { c(node.exported, st); }\n c(node.source, st, "Expression");\n if (node.attributes)\n { for (var i = 0, list = node.attributes; i < list.length; i += 1)\n {\n var attr = list[i];\n\n c(attr, st);\n } }\n };\n base.ImportAttribute = function (node, st, c) {\n c(node.value, st, "Expression");\n };\n base.ImportDeclaration = function (node, st, c) {\n for (var i = 0, list = node.specifiers; i < list.length; i += 1)\n {\n var spec = list[i];\n\n c(spec, st);\n }\n c(node.source, st, "Expression");\n if (node.attributes)\n { for (var i$1 = 0, list$1 = node.attributes; i$1 < list$1.length; i$1 += 1)\n {\n var attr = list$1[i$1];\n\n c(attr, st);\n } }\n };\n base.ImportExpression = function (node, st, c) {\n c(node.source, st, "Expression");\n if (node.options) { c(node.options, st, "Expression"); }\n };\n base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.PrivateIdentifier = base.Literal = ignore;\n\n base.TaggedTemplateExpression = function (node, st, c) {\n c(node.tag, st, "Expression");\n c(node.quasi, st, "Expression");\n };\n base.ClassDeclaration = base.ClassExpression = function (node, st, c) { return c(node, st, "Class"); };\n base.Class = function (node, st, c) {\n if (node.id) { c(node.id, st, "Pattern"); }\n if (node.superClass) { c(node.superClass, st, "Expression"); }\n c(node.body, st);\n };\n base.ClassBody = function (node, st, c) {\n for (var i = 0, list = node.body; i < list.length; i += 1)\n {\n var elt = list[i];\n\n c(elt, st);\n }\n };\n base.MethodDefinition = base.PropertyDefinition = base.Property = function (node, st, c) {\n if (node.computed) { c(node.key, st, "Expression"); }\n if (node.value) { c(node.value, st, "Expression"); }\n };\n\n exports.ancestor = ancestor;\n exports.base = base;\n exports.findNodeAfter = findNodeAfter;\n exports.findNodeAround = findNodeAround;\n exports.findNodeAt = findNodeAt;\n exports.findNodeBefore = findNodeBefore;\n exports.full = full;\n exports.fullAncestor = fullAncestor;\n exports.make = make;\n exports.recursive = recursive;\n exports.simple = simple;\n\n}));\n\n})', + { filename: "acorn-walk.js" }, +).runInThisContext(); factory(exportsObj, moduleObj); export default moduleObj.exports; diff --git a/src/js/internal/repl/acorn.js b/src/js/internal/repl/acorn.js index e379b49eb041..198984c78e43 100644 --- a/src/js/internal/repl/acorn.js +++ b/src/js/internal/repl/acorn.js @@ -6,6 +6,9 @@ const vm = require("node:vm"); const exportsObj = {}; const moduleObj = { exports: exportsObj }; -const factory = new vm.Script("(function(exports, module){(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n typeof define === 'function' && define.amd ? define(['exports'], factory) :\n (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.acorn = {}));\n})(this, (function (exports) { 'use strict';\n\n // This file was generated. Do not modify manually!\n var astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 574, 3, 9, 9, 7, 9, 32, 4, 318, 1, 78, 5, 71, 10, 50, 3, 123, 2, 54, 14, 32, 10, 3, 1, 11, 3, 46, 10, 8, 0, 46, 9, 7, 2, 37, 13, 2, 9, 6, 1, 45, 0, 13, 2, 49, 13, 9, 3, 2, 11, 83, 11, 7, 0, 3, 0, 158, 11, 6, 9, 7, 3, 56, 1, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 68, 8, 2, 0, 3, 0, 2, 3, 2, 4, 2, 0, 15, 1, 83, 17, 10, 9, 5, 0, 82, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 7, 19, 58, 14, 5, 9, 243, 14, 166, 9, 71, 5, 2, 1, 3, 3, 2, 0, 2, 1, 13, 9, 120, 6, 3, 6, 4, 0, 29, 9, 41, 6, 2, 3, 9, 0, 10, 10, 47, 15, 199, 7, 137, 9, 54, 7, 2, 7, 17, 9, 57, 21, 2, 13, 123, 5, 4, 0, 2, 1, 2, 6, 2, 0, 9, 9, 49, 4, 2, 1, 2, 4, 9, 9, 55, 9, 266, 3, 10, 1, 2, 0, 49, 6, 4, 4, 14, 10, 5350, 0, 7, 14, 11465, 27, 2343, 9, 87, 9, 39, 4, 60, 6, 26, 9, 535, 9, 470, 0, 2, 54, 8, 3, 82, 0, 12, 1, 19628, 1, 4178, 9, 519, 45, 3, 22, 543, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 513, 54, 5, 49, 9, 0, 15, 0, 23, 4, 2, 14, 1361, 6, 2, 16, 3, 6, 2, 1, 2, 4, 101, 0, 161, 6, 10, 9, 357, 0, 62, 13, 499, 13, 245, 1, 2, 9, 233, 0, 3, 0, 8, 1, 6, 0, 475, 6, 110, 6, 6, 9, 4759, 9, 787719, 239];\n\n // This file was generated. Do not modify manually!\n var astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 14, 29, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 19, 35, 5, 35, 5, 39, 9, 51, 13, 10, 2, 14, 2, 6, 2, 1, 2, 10, 2, 14, 2, 6, 2, 1, 4, 51, 13, 310, 10, 21, 11, 7, 25, 5, 2, 41, 2, 8, 70, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 66, 18, 2, 1, 11, 21, 11, 25, 7, 25, 39, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 28, 43, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 56, 50, 14, 50, 14, 35, 39, 27, 10, 22, 251, 41, 7, 1, 17, 5, 57, 28, 11, 0, 9, 21, 43, 17, 47, 20, 28, 22, 13, 52, 58, 1, 3, 0, 14, 44, 33, 24, 27, 35, 30, 0, 3, 0, 9, 34, 4, 0, 13, 47, 15, 3, 22, 0, 2, 0, 36, 17, 2, 24, 20, 1, 64, 6, 2, 0, 2, 3, 2, 14, 2, 9, 8, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 19, 0, 13, 4, 31, 9, 2, 0, 3, 0, 2, 37, 2, 0, 26, 0, 2, 0, 45, 52, 19, 3, 21, 2, 31, 47, 21, 1, 2, 0, 185, 46, 42, 3, 37, 47, 21, 0, 60, 42, 14, 0, 72, 26, 38, 6, 186, 43, 117, 63, 32, 7, 3, 0, 3, 7, 2, 1, 2, 23, 16, 0, 2, 0, 95, 7, 3, 38, 17, 0, 2, 0, 29, 0, 11, 39, 8, 0, 22, 0, 12, 45, 20, 0, 19, 72, 200, 32, 32, 8, 2, 36, 18, 0, 50, 29, 113, 6, 2, 1, 2, 37, 22, 0, 26, 5, 2, 1, 2, 31, 15, 0, 24, 43, 261, 18, 16, 0, 2, 12, 2, 33, 125, 0, 80, 921, 103, 110, 18, 195, 2637, 96, 16, 1071, 18, 5, 26, 3994, 6, 582, 6842, 29, 1763, 568, 8, 30, 18, 78, 18, 29, 19, 47, 17, 3, 32, 20, 6, 18, 433, 44, 212, 63, 33, 24, 3, 24, 45, 74, 6, 0, 67, 12, 65, 1, 2, 0, 15, 4, 10, 7381, 42, 31, 98, 114, 8702, 3, 2, 6, 2, 1, 2, 290, 16, 0, 30, 2, 3, 0, 15, 3, 9, 395, 2309, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 1845, 30, 7, 5, 262, 61, 147, 44, 11, 6, 17, 0, 322, 29, 19, 43, 485, 27, 229, 29, 3, 0, 208, 30, 2, 2, 2, 1, 2, 6, 3, 4, 10, 1, 225, 6, 2, 3, 2, 1, 2, 14, 2, 196, 60, 67, 8, 0, 1205, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42719, 33, 4381, 3, 5773, 3, 7472, 16, 621, 2467, 541, 1507, 4938, 6, 8489];\n\n // This file was generated. Do not modify manually!\n var nonASCIIidentifierChars = \"\\u200c\\u200d\\xb7\\u0300-\\u036f\\u0387\\u0483-\\u0487\\u0591-\\u05bd\\u05bf\\u05c1\\u05c2\\u05c4\\u05c5\\u05c7\\u0610-\\u061a\\u064b-\\u0669\\u0670\\u06d6-\\u06dc\\u06df-\\u06e4\\u06e7\\u06e8\\u06ea-\\u06ed\\u06f0-\\u06f9\\u0711\\u0730-\\u074a\\u07a6-\\u07b0\\u07c0-\\u07c9\\u07eb-\\u07f3\\u07fd\\u0816-\\u0819\\u081b-\\u0823\\u0825-\\u0827\\u0829-\\u082d\\u0859-\\u085b\\u0897-\\u089f\\u08ca-\\u08e1\\u08e3-\\u0903\\u093a-\\u093c\\u093e-\\u094f\\u0951-\\u0957\\u0962\\u0963\\u0966-\\u096f\\u0981-\\u0983\\u09bc\\u09be-\\u09c4\\u09c7\\u09c8\\u09cb-\\u09cd\\u09d7\\u09e2\\u09e3\\u09e6-\\u09ef\\u09fe\\u0a01-\\u0a03\\u0a3c\\u0a3e-\\u0a42\\u0a47\\u0a48\\u0a4b-\\u0a4d\\u0a51\\u0a66-\\u0a71\\u0a75\\u0a81-\\u0a83\\u0abc\\u0abe-\\u0ac5\\u0ac7-\\u0ac9\\u0acb-\\u0acd\\u0ae2\\u0ae3\\u0ae6-\\u0aef\\u0afa-\\u0aff\\u0b01-\\u0b03\\u0b3c\\u0b3e-\\u0b44\\u0b47\\u0b48\\u0b4b-\\u0b4d\\u0b55-\\u0b57\\u0b62\\u0b63\\u0b66-\\u0b6f\\u0b82\\u0bbe-\\u0bc2\\u0bc6-\\u0bc8\\u0bca-\\u0bcd\\u0bd7\\u0be6-\\u0bef\\u0c00-\\u0c04\\u0c3c\\u0c3e-\\u0c44\\u0c46-\\u0c48\\u0c4a-\\u0c4d\\u0c55\\u0c56\\u0c62\\u0c63\\u0c66-\\u0c6f\\u0c81-\\u0c83\\u0cbc\\u0cbe-\\u0cc4\\u0cc6-\\u0cc8\\u0cca-\\u0ccd\\u0cd5\\u0cd6\\u0ce2\\u0ce3\\u0ce6-\\u0cef\\u0cf3\\u0d00-\\u0d03\\u0d3b\\u0d3c\\u0d3e-\\u0d44\\u0d46-\\u0d48\\u0d4a-\\u0d4d\\u0d57\\u0d62\\u0d63\\u0d66-\\u0d6f\\u0d81-\\u0d83\\u0dca\\u0dcf-\\u0dd4\\u0dd6\\u0dd8-\\u0ddf\\u0de6-\\u0def\\u0df2\\u0df3\\u0e31\\u0e34-\\u0e3a\\u0e47-\\u0e4e\\u0e50-\\u0e59\\u0eb1\\u0eb4-\\u0ebc\\u0ec8-\\u0ece\\u0ed0-\\u0ed9\\u0f18\\u0f19\\u0f20-\\u0f29\\u0f35\\u0f37\\u0f39\\u0f3e\\u0f3f\\u0f71-\\u0f84\\u0f86\\u0f87\\u0f8d-\\u0f97\\u0f99-\\u0fbc\\u0fc6\\u102b-\\u103e\\u1040-\\u1049\\u1056-\\u1059\\u105e-\\u1060\\u1062-\\u1064\\u1067-\\u106d\\u1071-\\u1074\\u1082-\\u108d\\u108f-\\u109d\\u135d-\\u135f\\u1369-\\u1371\\u1712-\\u1715\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17b4-\\u17d3\\u17dd\\u17e0-\\u17e9\\u180b-\\u180d\\u180f-\\u1819\\u18a9\\u1920-\\u192b\\u1930-\\u193b\\u1946-\\u194f\\u19d0-\\u19da\\u1a17-\\u1a1b\\u1a55-\\u1a5e\\u1a60-\\u1a7c\\u1a7f-\\u1a89\\u1a90-\\u1a99\\u1ab0-\\u1abd\\u1abf-\\u1add\\u1ae0-\\u1aeb\\u1b00-\\u1b04\\u1b34-\\u1b44\\u1b50-\\u1b59\\u1b6b-\\u1b73\\u1b80-\\u1b82\\u1ba1-\\u1bad\\u1bb0-\\u1bb9\\u1be6-\\u1bf3\\u1c24-\\u1c37\\u1c40-\\u1c49\\u1c50-\\u1c59\\u1cd0-\\u1cd2\\u1cd4-\\u1ce8\\u1ced\\u1cf4\\u1cf7-\\u1cf9\\u1dc0-\\u1dff\\u200c\\u200d\\u203f\\u2040\\u2054\\u20d0-\\u20dc\\u20e1\\u20e5-\\u20f0\\u2cef-\\u2cf1\\u2d7f\\u2de0-\\u2dff\\u302a-\\u302f\\u3099\\u309a\\u30fb\\ua620-\\ua629\\ua66f\\ua674-\\ua67d\\ua69e\\ua69f\\ua6f0\\ua6f1\\ua802\\ua806\\ua80b\\ua823-\\ua827\\ua82c\\ua880\\ua881\\ua8b4-\\ua8c5\\ua8d0-\\ua8d9\\ua8e0-\\ua8f1\\ua8ff-\\ua909\\ua926-\\ua92d\\ua947-\\ua953\\ua980-\\ua983\\ua9b3-\\ua9c0\\ua9d0-\\ua9d9\\ua9e5\\ua9f0-\\ua9f9\\uaa29-\\uaa36\\uaa43\\uaa4c\\uaa4d\\uaa50-\\uaa59\\uaa7b-\\uaa7d\\uaab0\\uaab2-\\uaab4\\uaab7\\uaab8\\uaabe\\uaabf\\uaac1\\uaaeb-\\uaaef\\uaaf5\\uaaf6\\uabe3-\\uabea\\uabec\\uabed\\uabf0-\\uabf9\\ufb1e\\ufe00-\\ufe0f\\ufe20-\\ufe2f\\ufe33\\ufe34\\ufe4d-\\ufe4f\\uff10-\\uff19\\uff3f\\uff65\";\n\n // This file was generated. Do not modify manually!\n var nonASCIIidentifierStartChars = \"\\xaa\\xb5\\xba\\xc0-\\xd6\\xd8-\\xf6\\xf8-\\u02c1\\u02c6-\\u02d1\\u02e0-\\u02e4\\u02ec\\u02ee\\u0370-\\u0374\\u0376\\u0377\\u037a-\\u037d\\u037f\\u0386\\u0388-\\u038a\\u038c\\u038e-\\u03a1\\u03a3-\\u03f5\\u03f7-\\u0481\\u048a-\\u052f\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05d0-\\u05ea\\u05ef-\\u05f2\\u0620-\\u064a\\u066e\\u066f\\u0671-\\u06d3\\u06d5\\u06e5\\u06e6\\u06ee\\u06ef\\u06fa-\\u06fc\\u06ff\\u0710\\u0712-\\u072f\\u074d-\\u07a5\\u07b1\\u07ca-\\u07ea\\u07f4\\u07f5\\u07fa\\u0800-\\u0815\\u081a\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086a\\u0870-\\u0887\\u0889-\\u088f\\u08a0-\\u08c9\\u0904-\\u0939\\u093d\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098c\\u098f\\u0990\\u0993-\\u09a8\\u09aa-\\u09b0\\u09b2\\u09b6-\\u09b9\\u09bd\\u09ce\\u09dc\\u09dd\\u09df-\\u09e1\\u09f0\\u09f1\\u09fc\\u0a05-\\u0a0a\\u0a0f\\u0a10\\u0a13-\\u0a28\\u0a2a-\\u0a30\\u0a32\\u0a33\\u0a35\\u0a36\\u0a38\\u0a39\\u0a59-\\u0a5c\\u0a5e\\u0a72-\\u0a74\\u0a85-\\u0a8d\\u0a8f-\\u0a91\\u0a93-\\u0aa8\\u0aaa-\\u0ab0\\u0ab2\\u0ab3\\u0ab5-\\u0ab9\\u0abd\\u0ad0\\u0ae0\\u0ae1\\u0af9\\u0b05-\\u0b0c\\u0b0f\\u0b10\\u0b13-\\u0b28\\u0b2a-\\u0b30\\u0b32\\u0b33\\u0b35-\\u0b39\\u0b3d\\u0b5c\\u0b5d\\u0b5f-\\u0b61\\u0b71\\u0b83\\u0b85-\\u0b8a\\u0b8e-\\u0b90\\u0b92-\\u0b95\\u0b99\\u0b9a\\u0b9c\\u0b9e\\u0b9f\\u0ba3\\u0ba4\\u0ba8-\\u0baa\\u0bae-\\u0bb9\\u0bd0\\u0c05-\\u0c0c\\u0c0e-\\u0c10\\u0c12-\\u0c28\\u0c2a-\\u0c39\\u0c3d\\u0c58-\\u0c5a\\u0c5c\\u0c5d\\u0c60\\u0c61\\u0c80\\u0c85-\\u0c8c\\u0c8e-\\u0c90\\u0c92-\\u0ca8\\u0caa-\\u0cb3\\u0cb5-\\u0cb9\\u0cbd\\u0cdc-\\u0cde\\u0ce0\\u0ce1\\u0cf1\\u0cf2\\u0d04-\\u0d0c\\u0d0e-\\u0d10\\u0d12-\\u0d3a\\u0d3d\\u0d4e\\u0d54-\\u0d56\\u0d5f-\\u0d61\\u0d7a-\\u0d7f\\u0d85-\\u0d96\\u0d9a-\\u0db1\\u0db3-\\u0dbb\\u0dbd\\u0dc0-\\u0dc6\\u0e01-\\u0e30\\u0e32\\u0e33\\u0e40-\\u0e46\\u0e81\\u0e82\\u0e84\\u0e86-\\u0e8a\\u0e8c-\\u0ea3\\u0ea5\\u0ea7-\\u0eb0\\u0eb2\\u0eb3\\u0ebd\\u0ec0-\\u0ec4\\u0ec6\\u0edc-\\u0edf\\u0f00\\u0f40-\\u0f47\\u0f49-\\u0f6c\\u0f88-\\u0f8c\\u1000-\\u102a\\u103f\\u1050-\\u1055\\u105a-\\u105d\\u1061\\u1065\\u1066\\u106e-\\u1070\\u1075-\\u1081\\u108e\\u10a0-\\u10c5\\u10c7\\u10cd\\u10d0-\\u10fa\\u10fc-\\u1248\\u124a-\\u124d\\u1250-\\u1256\\u1258\\u125a-\\u125d\\u1260-\\u1288\\u128a-\\u128d\\u1290-\\u12b0\\u12b2-\\u12b5\\u12b8-\\u12be\\u12c0\\u12c2-\\u12c5\\u12c8-\\u12d6\\u12d8-\\u1310\\u1312-\\u1315\\u1318-\\u135a\\u1380-\\u138f\\u13a0-\\u13f5\\u13f8-\\u13fd\\u1401-\\u166c\\u166f-\\u167f\\u1681-\\u169a\\u16a0-\\u16ea\\u16ee-\\u16f8\\u1700-\\u1711\\u171f-\\u1731\\u1740-\\u1751\\u1760-\\u176c\\u176e-\\u1770\\u1780-\\u17b3\\u17d7\\u17dc\\u1820-\\u1878\\u1880-\\u18a8\\u18aa\\u18b0-\\u18f5\\u1900-\\u191e\\u1950-\\u196d\\u1970-\\u1974\\u1980-\\u19ab\\u19b0-\\u19c9\\u1a00-\\u1a16\\u1a20-\\u1a54\\u1aa7\\u1b05-\\u1b33\\u1b45-\\u1b4c\\u1b83-\\u1ba0\\u1bae\\u1baf\\u1bba-\\u1be5\\u1c00-\\u1c23\\u1c4d-\\u1c4f\\u1c5a-\\u1c7d\\u1c80-\\u1c8a\\u1c90-\\u1cba\\u1cbd-\\u1cbf\\u1ce9-\\u1cec\\u1cee-\\u1cf3\\u1cf5\\u1cf6\\u1cfa\\u1d00-\\u1dbf\\u1e00-\\u1f15\\u1f18-\\u1f1d\\u1f20-\\u1f45\\u1f48-\\u1f4d\\u1f50-\\u1f57\\u1f59\\u1f5b\\u1f5d\\u1f5f-\\u1f7d\\u1f80-\\u1fb4\\u1fb6-\\u1fbc\\u1fbe\\u1fc2-\\u1fc4\\u1fc6-\\u1fcc\\u1fd0-\\u1fd3\\u1fd6-\\u1fdb\\u1fe0-\\u1fec\\u1ff2-\\u1ff4\\u1ff6-\\u1ffc\\u2071\\u207f\\u2090-\\u209c\\u2102\\u2107\\u210a-\\u2113\\u2115\\u2118-\\u211d\\u2124\\u2126\\u2128\\u212a-\\u2139\\u213c-\\u213f\\u2145-\\u2149\\u214e\\u2160-\\u2188\\u2c00-\\u2ce4\\u2ceb-\\u2cee\\u2cf2\\u2cf3\\u2d00-\\u2d25\\u2d27\\u2d2d\\u2d30-\\u2d67\\u2d6f\\u2d80-\\u2d96\\u2da0-\\u2da6\\u2da8-\\u2dae\\u2db0-\\u2db6\\u2db8-\\u2dbe\\u2dc0-\\u2dc6\\u2dc8-\\u2dce\\u2dd0-\\u2dd6\\u2dd8-\\u2dde\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303c\\u3041-\\u3096\\u309b-\\u309f\\u30a1-\\u30fa\\u30fc-\\u30ff\\u3105-\\u312f\\u3131-\\u318e\\u31a0-\\u31bf\\u31f0-\\u31ff\\u3400-\\u4dbf\\u4e00-\\ua48c\\ua4d0-\\ua4fd\\ua500-\\ua60c\\ua610-\\ua61f\\ua62a\\ua62b\\ua640-\\ua66e\\ua67f-\\ua69d\\ua6a0-\\ua6ef\\ua717-\\ua71f\\ua722-\\ua788\\ua78b-\\ua7dc\\ua7f1-\\ua801\\ua803-\\ua805\\ua807-\\ua80a\\ua80c-\\ua822\\ua840-\\ua873\\ua882-\\ua8b3\\ua8f2-\\ua8f7\\ua8fb\\ua8fd\\ua8fe\\ua90a-\\ua925\\ua930-\\ua946\\ua960-\\ua97c\\ua984-\\ua9b2\\ua9cf\\ua9e0-\\ua9e4\\ua9e6-\\ua9ef\\ua9fa-\\ua9fe\\uaa00-\\uaa28\\uaa40-\\uaa42\\uaa44-\\uaa4b\\uaa60-\\uaa76\\uaa7a\\uaa7e-\\uaaaf\\uaab1\\uaab5\\uaab6\\uaab9-\\uaabd\\uaac0\\uaac2\\uaadb-\\uaadd\\uaae0-\\uaaea\\uaaf2-\\uaaf4\\uab01-\\uab06\\uab09-\\uab0e\\uab11-\\uab16\\uab20-\\uab26\\uab28-\\uab2e\\uab30-\\uab5a\\uab5c-\\uab69\\uab70-\\uabe2\\uac00-\\ud7a3\\ud7b0-\\ud7c6\\ud7cb-\\ud7fb\\uf900-\\ufa6d\\ufa70-\\ufad9\\ufb00-\\ufb06\\ufb13-\\ufb17\\ufb1d\\ufb1f-\\ufb28\\ufb2a-\\ufb36\\ufb38-\\ufb3c\\ufb3e\\ufb40\\ufb41\\ufb43\\ufb44\\ufb46-\\ufbb1\\ufbd3-\\ufd3d\\ufd50-\\ufd8f\\ufd92-\\ufdc7\\ufdf0-\\ufdfb\\ufe70-\\ufe74\\ufe76-\\ufefc\\uff21-\\uff3a\\uff41-\\uff5a\\uff66-\\uffbe\\uffc2-\\uffc7\\uffca-\\uffcf\\uffd2-\\uffd7\\uffda-\\uffdc\";\n\n // These are a run-length and offset encoded representation of the\n // >0xffff code points that are a valid part of identifiers. The\n // offset starts at 0x10000, and each pair of numbers represents an\n // offset to the next range, and then a size of the range.\n\n // Reserved word lists for various dialects of the language\n\n var reservedWords = {\n 3: \"abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile\",\n 5: \"class enum extends super const export import\",\n 6: \"enum\",\n strict: \"implements interface let package private protected public static yield\",\n strictBind: \"eval arguments\"\n };\n\n // And the keywords\n\n var ecma5AndLessKeywords = \"break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this\";\n\n var keywords$1 = {\n 5: ecma5AndLessKeywords,\n \"5module\": ecma5AndLessKeywords + \" export import\",\n 6: ecma5AndLessKeywords + \" const class extends export import super\"\n };\n\n var keywordRelationalOperator = /^in(stanceof)?$/;\n\n // ## Character categories\n\n var nonASCIIidentifierStart = new RegExp(\"[\" + nonASCIIidentifierStartChars + \"]\");\n var nonASCIIidentifier = new RegExp(\"[\" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + \"]\");\n\n // This has a complexity linear to the value of the code. The\n // assumption is that looking up astral identifier characters is\n // rare.\n function isInAstralSet(code, set) {\n var pos = 0x10000;\n for (var i = 0; i < set.length; i += 2) {\n pos += set[i];\n if (pos > code) { return false }\n pos += set[i + 1];\n if (pos >= code) { return true }\n }\n return false\n }\n\n // Test whether a given character code starts an identifier.\n\n function isIdentifierStart(code, astral) {\n if (code < 65) { return code === 36 }\n if (code < 91) { return true }\n if (code < 97) { return code === 95 }\n if (code < 123) { return true }\n if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)) }\n if (astral === false) { return false }\n return isInAstralSet(code, astralIdentifierStartCodes)\n }\n\n // Test whether a given character is part of an identifier.\n\n function isIdentifierChar(code, astral) {\n if (code < 48) { return code === 36 }\n if (code < 58) { return true }\n if (code < 65) { return false }\n if (code < 91) { return true }\n if (code < 97) { return code === 95 }\n if (code < 123) { return true }\n if (code <= 0xffff) { return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)) }\n if (astral === false) { return false }\n return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes)\n }\n\n // ## Token types\n\n // The assignment of fine-grained, information-carrying type objects\n // allows the tokenizer to store the information it has about a\n // token in a way that is very cheap for the parser to look up.\n\n // All token type variables start with an underscore, to make them\n // easy to recognize.\n\n // The `beforeExpr` property is used to disambiguate between regular\n // expressions and divisions. It is set on all token types that can\n // be followed by an expression (thus, a slash after them would be a\n // regular expression).\n //\n // The `startsExpr` property is used to check if the token ends a\n // `yield` expression. It is set on all token types that either can\n // directly start an expression (like a quotation mark) or can\n // continue an expression (like the body of a string).\n //\n // `isLoop` marks a keyword as starting a loop, which is important\n // to know when parsing a label, in order to allow or disallow\n // continue jumps to that label.\n\n var TokenType = function TokenType(label, conf) {\n if ( conf === void 0 ) conf = {};\n\n this.label = label;\n this.keyword = conf.keyword;\n this.beforeExpr = !!conf.beforeExpr;\n this.startsExpr = !!conf.startsExpr;\n this.isLoop = !!conf.isLoop;\n this.isAssign = !!conf.isAssign;\n this.prefix = !!conf.prefix;\n this.postfix = !!conf.postfix;\n this.binop = conf.binop || null;\n this.updateContext = null;\n };\n\n function binop(name, prec) {\n return new TokenType(name, {beforeExpr: true, binop: prec})\n }\n var beforeExpr = {beforeExpr: true}, startsExpr = {startsExpr: true};\n\n // Map keyword names to token types.\n\n var keywords = {};\n\n // Succinct definitions of keyword token types\n function kw(name, options) {\n if ( options === void 0 ) options = {};\n\n options.keyword = name;\n return keywords[name] = new TokenType(name, options)\n }\n\n var types$1 = {\n num: new TokenType(\"num\", startsExpr),\n regexp: new TokenType(\"regexp\", startsExpr),\n string: new TokenType(\"string\", startsExpr),\n name: new TokenType(\"name\", startsExpr),\n privateId: new TokenType(\"privateId\", startsExpr),\n eof: new TokenType(\"eof\"),\n\n // Punctuation token types.\n bracketL: new TokenType(\"[\", {beforeExpr: true, startsExpr: true}),\n bracketR: new TokenType(\"]\"),\n braceL: new TokenType(\"{\", {beforeExpr: true, startsExpr: true}),\n braceR: new TokenType(\"}\"),\n parenL: new TokenType(\"(\", {beforeExpr: true, startsExpr: true}),\n parenR: new TokenType(\")\"),\n comma: new TokenType(\",\", beforeExpr),\n semi: new TokenType(\";\", beforeExpr),\n colon: new TokenType(\":\", beforeExpr),\n dot: new TokenType(\".\"),\n question: new TokenType(\"?\", beforeExpr),\n questionDot: new TokenType(\"?.\"),\n arrow: new TokenType(\"=>\", beforeExpr),\n template: new TokenType(\"template\"),\n invalidTemplate: new TokenType(\"invalidTemplate\"),\n ellipsis: new TokenType(\"...\", beforeExpr),\n backQuote: new TokenType(\"`\", startsExpr),\n dollarBraceL: new TokenType(\"${\", {beforeExpr: true, startsExpr: true}),\n\n // Operators. These carry several kinds of properties to help the\n // parser use them properly (the presence of these properties is\n // what categorizes them as operators).\n //\n // `binop`, when present, specifies that this operator is a binary\n // operator, and will refer to its precedence.\n //\n // `prefix` and `postfix` mark the operator as a prefix or postfix\n // unary operator.\n //\n // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as\n // binary operators with a very low precedence, that should result\n // in AssignmentExpression nodes.\n\n eq: new TokenType(\"=\", {beforeExpr: true, isAssign: true}),\n assign: new TokenType(\"_=\", {beforeExpr: true, isAssign: true}),\n incDec: new TokenType(\"++/--\", {prefix: true, postfix: true, startsExpr: true}),\n prefix: new TokenType(\"!/~\", {beforeExpr: true, prefix: true, startsExpr: true}),\n logicalOR: binop(\"||\", 1),\n logicalAND: binop(\"&&\", 2),\n bitwiseOR: binop(\"|\", 3),\n bitwiseXOR: binop(\"^\", 4),\n bitwiseAND: binop(\"&\", 5),\n equality: binop(\"==/!=/===/!==\", 6),\n relational: binop(\"/<=/>=\", 7),\n bitShift: binop(\"<>/>>>\", 8),\n plusMin: new TokenType(\"+/-\", {beforeExpr: true, binop: 9, prefix: true, startsExpr: true}),\n modulo: binop(\"%\", 10),\n star: binop(\"*\", 10),\n slash: binop(\"/\", 10),\n starstar: new TokenType(\"**\", {beforeExpr: true}),\n coalesce: binop(\"??\", 1),\n\n // Keyword token types.\n _break: kw(\"break\"),\n _case: kw(\"case\", beforeExpr),\n _catch: kw(\"catch\"),\n _continue: kw(\"continue\"),\n _debugger: kw(\"debugger\"),\n _default: kw(\"default\", beforeExpr),\n _do: kw(\"do\", {isLoop: true, beforeExpr: true}),\n _else: kw(\"else\", beforeExpr),\n _finally: kw(\"finally\"),\n _for: kw(\"for\", {isLoop: true}),\n _function: kw(\"function\", startsExpr),\n _if: kw(\"if\"),\n _return: kw(\"return\", beforeExpr),\n _switch: kw(\"switch\"),\n _throw: kw(\"throw\", beforeExpr),\n _try: kw(\"try\"),\n _var: kw(\"var\"),\n _const: kw(\"const\"),\n _while: kw(\"while\", {isLoop: true}),\n _with: kw(\"with\"),\n _new: kw(\"new\", {beforeExpr: true, startsExpr: true}),\n _this: kw(\"this\", startsExpr),\n _super: kw(\"super\", startsExpr),\n _class: kw(\"class\", startsExpr),\n _extends: kw(\"extends\", beforeExpr),\n _export: kw(\"export\"),\n _import: kw(\"import\", startsExpr),\n _null: kw(\"null\", startsExpr),\n _true: kw(\"true\", startsExpr),\n _false: kw(\"false\", startsExpr),\n _in: kw(\"in\", {beforeExpr: true, binop: 7}),\n _instanceof: kw(\"instanceof\", {beforeExpr: true, binop: 7}),\n _typeof: kw(\"typeof\", {beforeExpr: true, prefix: true, startsExpr: true}),\n _void: kw(\"void\", {beforeExpr: true, prefix: true, startsExpr: true}),\n _delete: kw(\"delete\", {beforeExpr: true, prefix: true, startsExpr: true})\n };\n\n // Matches a whole line break (where CRLF is considered a single\n // line break). Used to count lines.\n\n var lineBreak = /\\r\\n?|\\n|\\u2028|\\u2029/;\n var lineBreakG = new RegExp(lineBreak.source, \"g\");\n\n function isNewLine(code) {\n return code === 10 || code === 13 || code === 0x2028 || code === 0x2029\n }\n\n function nextLineBreak(code, from, end) {\n if ( end === void 0 ) end = code.length;\n\n for (var i = from; i < end; i++) {\n var next = code.charCodeAt(i);\n if (isNewLine(next))\n { return i < end - 1 && next === 13 && code.charCodeAt(i + 1) === 10 ? i + 2 : i + 1 }\n }\n return -1\n }\n\n var nonASCIIwhitespace = /[\\u1680\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]/;\n\n var skipWhiteSpace = /(?:\\s|\\/\\/.*|\\/\\*[^]*?\\*\\/)*/g;\n\n var ref = Object.prototype;\n var hasOwnProperty = ref.hasOwnProperty;\n var toString = ref.toString;\n\n var hasOwn = Object.hasOwn || (function (obj, propName) { return (\n hasOwnProperty.call(obj, propName)\n ); });\n\n var isArray = Array.isArray || (function (obj) { return (\n toString.call(obj) === \"[object Array]\"\n ); });\n\n var regexpCache = Object.create(null);\n\n function wordsRegexp(words) {\n return regexpCache[words] || (regexpCache[words] = new RegExp(\"^(?:\" + words.replace(/ /g, \"|\") + \")$\"))\n }\n\n function codePointToString(code) {\n // UTF-16 Decoding\n if (code <= 0xFFFF) { return String.fromCharCode(code) }\n code -= 0x10000;\n return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00)\n }\n\n var loneSurrogate = /(?:[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF])/;\n\n // These are used when `options.locations` is on, for the\n // `startLoc` and `endLoc` properties.\n\n var Position = function Position(line, col) {\n this.line = line;\n this.column = col;\n };\n\n Position.prototype.offset = function offset (n) {\n return new Position(this.line, this.column + n)\n };\n\n var SourceLocation = function SourceLocation(p, start, end) {\n this.start = start;\n this.end = end;\n if (p.sourceFile !== null) { this.source = p.sourceFile; }\n };\n\n // The `getLineInfo` function is mostly useful when the\n // `locations` option is off (for performance reasons) and you\n // want to find the line/column position for a given character\n // offset. `input` should be the code string that the offset refers\n // into.\n\n function getLineInfo(input, offset) {\n for (var line = 1, cur = 0;;) {\n var nextBreak = nextLineBreak(input, cur, offset);\n if (nextBreak < 0) { return new Position(line, offset - cur) }\n ++line;\n cur = nextBreak;\n }\n }\n\n // A second argument must be given to configure the parser process.\n // These options are recognized (only `ecmaVersion` is required):\n\n var defaultOptions = {\n // `ecmaVersion` indicates the ECMAScript version to parse. Must be\n // either 3, 5, 6 (or 2015), 7 (2016), 8 (2017), 9 (2018), 10\n // (2019), 11 (2020), 12 (2021), 13 (2022), 14 (2023), or `\"latest\"`\n // (the latest version the library supports). This influences\n // support for strict mode, the set of reserved words, and support\n // for new syntax features.\n ecmaVersion: null,\n // `sourceType` indicates the mode the code should be parsed in.\n // Can be either `\"script\"`, `\"module\"` or `\"commonjs\"`. This influences global\n // strict mode and parsing of `import` and `export` declarations.\n sourceType: \"script\",\n // `onInsertedSemicolon` can be a callback that will be called when\n // a semicolon is automatically inserted. It will be passed the\n // position of the inserted semicolon as an offset, and if\n // `locations` is enabled, it is given the location as a `{line,\n // column}` object as second argument.\n onInsertedSemicolon: null,\n // `onTrailingComma` is similar to `onInsertedSemicolon`, but for\n // trailing commas.\n onTrailingComma: null,\n // By default, reserved words are only enforced if ecmaVersion >= 5.\n // Set `allowReserved` to a boolean value to explicitly turn this on\n // an off. When this option has the value \"never\", reserved words\n // and keywords can also not be used as property names.\n allowReserved: null,\n // When enabled, a return at the top level is not considered an\n // error.\n allowReturnOutsideFunction: false,\n // When enabled, import/export statements are not constrained to\n // appearing at the top of the program, and an import.meta expression\n // in a script isn't considered an error.\n allowImportExportEverywhere: false,\n // By default, await identifiers are allowed to appear at the top-level scope only if ecmaVersion >= 2022.\n // When enabled, await identifiers are allowed to appear at the top-level scope,\n // but they are still not allowed in non-async functions.\n allowAwaitOutsideFunction: null,\n // When enabled, super identifiers are not constrained to\n // appearing in methods and do not raise an error when they appear elsewhere.\n allowSuperOutsideMethod: null,\n // When enabled, hashbang directive in the beginning of file is\n // allowed and treated as a line comment. Enabled by default when\n // `ecmaVersion` >= 2023.\n allowHashBang: false,\n // By default, the parser will verify that private properties are\n // only used in places where they are valid and have been declared.\n // Set this to false to turn such checks off.\n checkPrivateFields: true,\n // When `locations` is on, `loc` properties holding objects with\n // `start` and `end` properties in `{line, column}` form (with\n // line being 1-based and column 0-based) will be attached to the\n // nodes.\n locations: false,\n // A function can be passed as `onToken` option, which will\n // cause Acorn to call that function with object in the same\n // format as tokens returned from `tokenizer().getToken()`. Note\n // that you are not allowed to call the parser from the\n // callback\u2014that will corrupt its internal state.\n onToken: null,\n // A function can be passed as `onComment` option, which will\n // cause Acorn to call that function with `(block, text, start,\n // end)` parameters whenever a comment is skipped. `block` is a\n // boolean indicating whether this is a block (`/* */`) comment,\n // `text` is the content of the comment, and `start` and `end` are\n // character offsets that denote the start and end of the comment.\n // When the `locations` option is on, two more parameters are\n // passed, the full `{line, column}` locations of the start and\n // end of the comments. Note that you are not allowed to call the\n // parser from the callback\u2014that will corrupt its internal state.\n // When this option has an array as value, objects representing the\n // comments are pushed to it.\n onComment: null,\n // Nodes have their start and end characters offsets recorded in\n // `start` and `end` properties (directly on the node, rather than\n // the `loc` object, which holds line/column data. To also add a\n // [semi-standardized][range] `range` property holding a `[start,\n // end]` array with the same numbers, set the `ranges` option to\n // `true`.\n //\n // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678\n ranges: false,\n // It is possible to parse multiple files into a single AST by\n // passing the tree produced by parsing the first file as\n // `program` option in subsequent parses. This will add the\n // toplevel forms of the parsed file to the `Program` (top) node\n // of an existing parse tree.\n program: null,\n // When `locations` is on, you can pass this to record the source\n // file in every node's `loc` object.\n sourceFile: null,\n // This value, if given, is stored in every node, whether\n // `locations` is on or off.\n directSourceFile: null,\n // When enabled, parenthesized expressions are represented by\n // (non-standard) ParenthesizedExpression nodes\n preserveParens: false\n };\n\n // Interpret and default an options object\n\n var warnedAboutEcmaVersion = false;\n\n function getOptions(opts) {\n var options = {};\n\n for (var opt in defaultOptions)\n { options[opt] = opts && hasOwn(opts, opt) ? opts[opt] : defaultOptions[opt]; }\n\n if (options.ecmaVersion === \"latest\") {\n options.ecmaVersion = 1e8;\n } else if (options.ecmaVersion == null) {\n if (!warnedAboutEcmaVersion && typeof console === \"object\" && console.warn) {\n warnedAboutEcmaVersion = true;\n console.warn(\"Since Acorn 8.0.0, options.ecmaVersion is required.\\nDefaulting to 2020, but this will stop working in the future.\");\n }\n options.ecmaVersion = 11;\n } else if (options.ecmaVersion >= 2015) {\n options.ecmaVersion -= 2009;\n }\n\n if (options.allowReserved == null)\n { options.allowReserved = options.ecmaVersion < 5; }\n\n if (!opts || opts.allowHashBang == null)\n { options.allowHashBang = options.ecmaVersion >= 14; }\n\n if (isArray(options.onToken)) {\n var tokens = options.onToken;\n options.onToken = function (token) { return tokens.push(token); };\n }\n if (isArray(options.onComment))\n { options.onComment = pushComment(options, options.onComment); }\n\n if (options.sourceType === \"commonjs\" && options.allowAwaitOutsideFunction)\n { throw new Error(\"Cannot use allowAwaitOutsideFunction with sourceType: commonjs\") }\n\n return options\n }\n\n function pushComment(options, array) {\n return function(block, text, start, end, startLoc, endLoc) {\n var comment = {\n type: block ? \"Block\" : \"Line\",\n value: text,\n start: start,\n end: end\n };\n if (options.locations)\n { comment.loc = new SourceLocation(this, startLoc, endLoc); }\n if (options.ranges)\n { comment.range = [start, end]; }\n array.push(comment);\n }\n }\n\n // Each scope gets a bitset that may contain these flags\n var\n SCOPE_TOP = 1,\n SCOPE_FUNCTION = 2,\n SCOPE_ASYNC = 4,\n SCOPE_GENERATOR = 8,\n SCOPE_ARROW = 16,\n SCOPE_SIMPLE_CATCH = 32,\n SCOPE_SUPER = 64,\n SCOPE_DIRECT_SUPER = 128,\n SCOPE_CLASS_STATIC_BLOCK = 256,\n SCOPE_CLASS_FIELD_INIT = 512,\n SCOPE_SWITCH = 1024,\n SCOPE_VAR = SCOPE_TOP | SCOPE_FUNCTION | SCOPE_CLASS_STATIC_BLOCK;\n\n function functionFlags(async, generator) {\n return SCOPE_FUNCTION | (async ? SCOPE_ASYNC : 0) | (generator ? SCOPE_GENERATOR : 0)\n }\n\n // Used in checkLVal* and declareName to determine the type of a binding\n var\n BIND_NONE = 0, // Not a binding\n BIND_VAR = 1, // Var-style binding\n BIND_LEXICAL = 2, // Let- or const-style binding\n BIND_FUNCTION = 3, // Function declaration\n BIND_SIMPLE_CATCH = 4, // Simple (identifier pattern) catch binding\n BIND_OUTSIDE = 5; // Special case for function names as bound inside the function\n\n var Parser = function Parser(options, input, startPos) {\n this.options = options = getOptions(options);\n this.sourceFile = options.sourceFile;\n this.keywords = wordsRegexp(keywords$1[options.ecmaVersion >= 6 ? 6 : options.sourceType === \"module\" ? \"5module\" : 5]);\n var reserved = \"\";\n if (options.allowReserved !== true) {\n reserved = reservedWords[options.ecmaVersion >= 6 ? 6 : options.ecmaVersion === 5 ? 5 : 3];\n if (options.sourceType === \"module\") { reserved += \" await\"; }\n }\n this.reservedWords = wordsRegexp(reserved);\n var reservedStrict = (reserved ? reserved + \" \" : \"\") + reservedWords.strict;\n this.reservedWordsStrict = wordsRegexp(reservedStrict);\n this.reservedWordsStrictBind = wordsRegexp(reservedStrict + \" \" + reservedWords.strictBind);\n this.input = String(input);\n\n // Used to signal to callers of `readWord1` whether the word\n // contained any escape sequences. This is needed because words with\n // escape sequences must not be interpreted as keywords.\n this.containsEsc = false;\n\n // Set up token state\n\n // The current position of the tokenizer in the input.\n if (startPos) {\n this.pos = startPos;\n this.lineStart = this.input.lastIndexOf(\"\\n\", startPos - 1) + 1;\n this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length;\n } else {\n this.pos = this.lineStart = 0;\n this.curLine = 1;\n }\n\n // Properties of the current token:\n // Its type\n this.type = types$1.eof;\n // For tokens that include more information than their type, the value\n this.value = null;\n // Its start and end offset\n this.start = this.end = this.pos;\n // And, if locations are used, the {line, column} object\n // corresponding to those offsets\n this.startLoc = this.endLoc = this.curPosition();\n\n // Position information for the previous token\n this.lastTokEndLoc = this.lastTokStartLoc = null;\n this.lastTokStart = this.lastTokEnd = this.pos;\n\n // The context stack is used to superficially track syntactic\n // context to predict whether a regular expression is allowed in a\n // given position.\n this.context = this.initialContext();\n this.exprAllowed = true;\n\n // Figure out if it's a module code.\n this.inModule = options.sourceType === \"module\";\n this.strict = this.inModule || this.strictDirective(this.pos);\n\n // Used to signify the start of a potential arrow function\n this.potentialArrowAt = -1;\n this.potentialArrowInForAwait = false;\n\n // Positions to delayed-check that yield/await does not exist in default parameters.\n this.yieldPos = this.awaitPos = this.awaitIdentPos = 0;\n // Labels in scope.\n this.labels = [];\n // Thus-far undefined exports.\n this.undefinedExports = Object.create(null);\n\n // If enabled, skip leading hashbang line.\n if (this.pos === 0 && options.allowHashBang && this.input.slice(0, 2) === \"#!\")\n { this.skipLineComment(2); }\n\n // Scope tracking for duplicate variable names (see scope.js)\n this.scopeStack = [];\n this.enterScope(\n this.options.sourceType === \"commonjs\"\n // In commonjs, the top-level scope behaves like a function scope\n ? SCOPE_FUNCTION\n : SCOPE_TOP\n );\n\n // For RegExp validation\n this.regexpState = null;\n\n // The stack of private names.\n // Each element has two properties: 'declared' and 'used'.\n // When it exited from the outermost class definition, all used private names must be declared.\n this.privateNameStack = [];\n };\n\n var prototypeAccessors = { inFunction: { configurable: true },inGenerator: { configurable: true },inAsync: { configurable: true },canAwait: { configurable: true },allowReturn: { configurable: true },allowSuper: { configurable: true },allowDirectSuper: { configurable: true },treatFunctionsAsVar: { configurable: true },allowNewDotTarget: { configurable: true },allowUsing: { configurable: true },inClassStaticBlock: { configurable: true } };\n\n Parser.prototype.parse = function parse () {\n var node = this.options.program || this.startNode();\n this.nextToken();\n return this.parseTopLevel(node)\n };\n\n prototypeAccessors.inFunction.get = function () { return (this.currentVarScope().flags & SCOPE_FUNCTION) > 0 };\n\n prototypeAccessors.inGenerator.get = function () { return (this.currentVarScope().flags & SCOPE_GENERATOR) > 0 };\n\n prototypeAccessors.inAsync.get = function () { return (this.currentVarScope().flags & SCOPE_ASYNC) > 0 };\n\n prototypeAccessors.canAwait.get = function () {\n for (var i = this.scopeStack.length - 1; i >= 0; i--) {\n var ref = this.scopeStack[i];\n var flags = ref.flags;\n if (flags & (SCOPE_CLASS_STATIC_BLOCK | SCOPE_CLASS_FIELD_INIT)) { return false }\n if (flags & SCOPE_FUNCTION) { return (flags & SCOPE_ASYNC) > 0 }\n }\n return (this.inModule && this.options.ecmaVersion >= 13) || this.options.allowAwaitOutsideFunction\n };\n\n prototypeAccessors.allowReturn.get = function () {\n if (this.inFunction) { return true }\n if (this.options.allowReturnOutsideFunction && this.currentVarScope().flags & SCOPE_TOP) { return true }\n return false\n };\n\n prototypeAccessors.allowSuper.get = function () {\n var ref = this.currentThisScope();\n var flags = ref.flags;\n return (flags & SCOPE_SUPER) > 0 || this.options.allowSuperOutsideMethod\n };\n\n prototypeAccessors.allowDirectSuper.get = function () { return (this.currentThisScope().flags & SCOPE_DIRECT_SUPER) > 0 };\n\n prototypeAccessors.treatFunctionsAsVar.get = function () { return this.treatFunctionsAsVarInScope(this.currentScope()) };\n\n prototypeAccessors.allowNewDotTarget.get = function () {\n for (var i = this.scopeStack.length - 1; i >= 0; i--) {\n var ref = this.scopeStack[i];\n var flags = ref.flags;\n if (flags & (SCOPE_CLASS_STATIC_BLOCK | SCOPE_CLASS_FIELD_INIT) ||\n ((flags & SCOPE_FUNCTION) && !(flags & SCOPE_ARROW))) { return true }\n }\n return false\n };\n\n prototypeAccessors.allowUsing.get = function () {\n var ref = this.currentScope();\n var flags = ref.flags;\n if (flags & SCOPE_SWITCH) { return false }\n if (!this.inModule && flags & SCOPE_TOP) { return false }\n return true\n };\n\n prototypeAccessors.inClassStaticBlock.get = function () {\n return (this.currentVarScope().flags & SCOPE_CLASS_STATIC_BLOCK) > 0\n };\n\n Parser.extend = function extend () {\n var plugins = [], len = arguments.length;\n while ( len-- ) plugins[ len ] = arguments[ len ];\n\n var cls = this;\n for (var i = 0; i < plugins.length; i++) { cls = plugins[i](cls); }\n return cls\n };\n\n Parser.parse = function parse (input, options) {\n return new this(options, input).parse()\n };\n\n Parser.parseExpressionAt = function parseExpressionAt (input, pos, options) {\n var parser = new this(options, input, pos);\n parser.nextToken();\n return parser.parseExpression()\n };\n\n Parser.tokenizer = function tokenizer (input, options) {\n return new this(options, input)\n };\n\n Object.defineProperties( Parser.prototype, prototypeAccessors );\n\n var pp$9 = Parser.prototype;\n\n // ## Parser utilities\n\n var literal = /^(?:'((?:\\\\[^]|[^'\\\\])*?)'|\"((?:\\\\[^]|[^\"\\\\])*?)\")/;\n pp$9.strictDirective = function(start) {\n if (this.options.ecmaVersion < 5) { return false }\n for (;;) {\n // Try to find string literal.\n skipWhiteSpace.lastIndex = start;\n start += skipWhiteSpace.exec(this.input)[0].length;\n var match = literal.exec(this.input.slice(start));\n if (!match) { return false }\n if ((match[1] || match[2]) === \"use strict\") {\n skipWhiteSpace.lastIndex = start + match[0].length;\n var spaceAfter = skipWhiteSpace.exec(this.input), end = spaceAfter.index + spaceAfter[0].length;\n var next = this.input.charAt(end);\n return next === \";\" || next === \"}\" ||\n (lineBreak.test(spaceAfter[0]) &&\n !(/[(`.[+\\-/*%<>=,?^&]/.test(next) || next === \"!\" && this.input.charAt(end + 1) === \"=\"))\n }\n start += match[0].length;\n\n // Skip semicolon, if any.\n skipWhiteSpace.lastIndex = start;\n start += skipWhiteSpace.exec(this.input)[0].length;\n if (this.input[start] === \";\")\n { start++; }\n }\n };\n\n // Predicate that tests whether the next token is of the given\n // type, and if yes, consumes it as a side effect.\n\n pp$9.eat = function(type) {\n if (this.type === type) {\n this.next();\n return true\n } else {\n return false\n }\n };\n\n // Tests whether parsed token is a contextual keyword.\n\n pp$9.isContextual = function(name) {\n return this.type === types$1.name && this.value === name && !this.containsEsc\n };\n\n // Consumes contextual keyword if possible.\n\n pp$9.eatContextual = function(name) {\n if (!this.isContextual(name)) { return false }\n this.next();\n return true\n };\n\n // Asserts that following token is given contextual keyword.\n\n pp$9.expectContextual = function(name) {\n if (!this.eatContextual(name)) { this.unexpected(); }\n };\n\n // Test whether a semicolon can be inserted at the current position.\n\n pp$9.canInsertSemicolon = function() {\n return this.type === types$1.eof ||\n this.type === types$1.braceR ||\n lineBreak.test(this.input.slice(this.lastTokEnd, this.start))\n };\n\n pp$9.insertSemicolon = function() {\n if (this.canInsertSemicolon()) {\n if (this.options.onInsertedSemicolon)\n { this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc); }\n return true\n }\n };\n\n // Consume a semicolon, or, failing that, see if we are allowed to\n // pretend that there is a semicolon at this position.\n\n pp$9.semicolon = function() {\n if (!this.eat(types$1.semi) && !this.insertSemicolon()) { this.unexpected(); }\n };\n\n pp$9.afterTrailingComma = function(tokType, notNext) {\n if (this.type === tokType) {\n if (this.options.onTrailingComma)\n { this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc); }\n if (!notNext)\n { this.next(); }\n return true\n }\n };\n\n // Expect a token of a given type. If found, consume it, otherwise,\n // raise an unexpected token error.\n\n pp$9.expect = function(type) {\n this.eat(type) || this.unexpected();\n };\n\n // Raise an unexpected token error.\n\n pp$9.unexpected = function(pos) {\n this.raise(pos != null ? pos : this.start, \"Unexpected token\");\n };\n\n var DestructuringErrors = function DestructuringErrors() {\n this.shorthandAssign =\n this.trailingComma =\n this.parenthesizedAssign =\n this.parenthesizedBind =\n this.doubleProto =\n -1;\n };\n\n pp$9.checkPatternErrors = function(refDestructuringErrors, isAssign) {\n if (!refDestructuringErrors) { return }\n if (refDestructuringErrors.trailingComma > -1)\n { this.raiseRecoverable(refDestructuringErrors.trailingComma, \"Comma is not permitted after the rest element\"); }\n var parens = isAssign ? refDestructuringErrors.parenthesizedAssign : refDestructuringErrors.parenthesizedBind;\n if (parens > -1) { this.raiseRecoverable(parens, isAssign ? \"Assigning to rvalue\" : \"Parenthesized pattern\"); }\n };\n\n pp$9.checkExpressionErrors = function(refDestructuringErrors, andThrow) {\n if (!refDestructuringErrors) { return false }\n var shorthandAssign = refDestructuringErrors.shorthandAssign;\n var doubleProto = refDestructuringErrors.doubleProto;\n if (!andThrow) { return shorthandAssign >= 0 || doubleProto >= 0 }\n if (shorthandAssign >= 0)\n { this.raise(shorthandAssign, \"Shorthand property assignments are valid only in destructuring patterns\"); }\n if (doubleProto >= 0)\n { this.raiseRecoverable(doubleProto, \"Redefinition of __proto__ property\"); }\n };\n\n pp$9.checkYieldAwaitInDefaultParams = function() {\n if (this.yieldPos && (!this.awaitPos || this.yieldPos < this.awaitPos))\n { this.raise(this.yieldPos, \"Yield expression cannot be a default value\"); }\n if (this.awaitPos)\n { this.raise(this.awaitPos, \"Await expression cannot be a default value\"); }\n };\n\n pp$9.isSimpleAssignTarget = function(expr) {\n if (expr.type === \"ParenthesizedExpression\")\n { return this.isSimpleAssignTarget(expr.expression) }\n return expr.type === \"Identifier\" || expr.type === \"MemberExpression\"\n };\n\n var pp$8 = Parser.prototype;\n\n // ### Statement parsing\n\n // Parse a program. Initializes the parser, reads any number of\n // statements, and wraps them in a Program node. Optionally takes a\n // `program` argument. If present, the statements will be appended\n // to its body instead of creating a new node.\n\n pp$8.parseTopLevel = function(node) {\n var exports = Object.create(null);\n if (!node.body) { node.body = []; }\n while (this.type !== types$1.eof) {\n var stmt = this.parseStatement(null, true, exports);\n node.body.push(stmt);\n }\n if (this.inModule)\n { for (var i = 0, list = Object.keys(this.undefinedExports); i < list.length; i += 1)\n {\n var name = list[i];\n\n this.raiseRecoverable(this.undefinedExports[name].start, (\"Export '\" + name + \"' is not defined\"));\n } }\n this.adaptDirectivePrologue(node.body);\n this.next();\n node.sourceType = this.options.sourceType === \"commonjs\" ? \"script\" : this.options.sourceType;\n return this.finishNode(node, \"Program\")\n };\n\n var loopLabel = {kind: \"loop\"}, switchLabel = {kind: \"switch\"};\n\n pp$8.isLet = function(context) {\n if (this.options.ecmaVersion < 6 || !this.isContextual(\"let\")) { return false }\n skipWhiteSpace.lastIndex = this.pos;\n var skip = skipWhiteSpace.exec(this.input);\n var next = this.pos + skip[0].length, nextCh = this.fullCharCodeAt(next);\n // For ambiguous cases, determine if a LexicalDeclaration (or only a\n // Statement) is allowed here. If context is not empty then only a Statement\n // is allowed. However, `let [` is an explicit negative lookahead for\n // ExpressionStatement, so special-case it first.\n if (nextCh === 91 || nextCh === 92) { return true } // '[', '\\'\n if (context) { return false }\n\n if (nextCh === 123) { return true } // '{'\n if (isIdentifierStart(nextCh)) {\n var start = next;\n do { next += nextCh <= 0xffff ? 1 : 2; }\n while (isIdentifierChar(nextCh = this.fullCharCodeAt(next)))\n if (nextCh === 92) { return true }\n var ident = this.input.slice(start, next);\n if (!keywordRelationalOperator.test(ident)) { return true }\n }\n return false\n };\n\n // check 'async [no LineTerminator here] function'\n // - 'async /*foo*/ function' is OK.\n // - 'async /*\\n*/ function' is invalid.\n pp$8.isAsyncFunction = function() {\n if (this.options.ecmaVersion < 8 || !this.isContextual(\"async\"))\n { return false }\n\n skipWhiteSpace.lastIndex = this.pos;\n var skip = skipWhiteSpace.exec(this.input);\n var next = this.pos + skip[0].length, after;\n return !lineBreak.test(this.input.slice(this.pos, next)) &&\n this.input.slice(next, next + 8) === \"function\" &&\n (next + 8 === this.input.length ||\n !(isIdentifierChar(after = this.fullCharCodeAt(next + 8)) || after === 92 /* '\\' */))\n };\n\n pp$8.isUsingKeyword = function(isAwaitUsing, isFor) {\n if (this.options.ecmaVersion < 17 || !this.isContextual(isAwaitUsing ? \"await\" : \"using\"))\n { return false }\n\n skipWhiteSpace.lastIndex = this.pos;\n var skip = skipWhiteSpace.exec(this.input);\n var next = this.pos + skip[0].length;\n\n if (lineBreak.test(this.input.slice(this.pos, next))) { return false }\n\n if (isAwaitUsing) {\n var usingEndPos = next + 5 /* using */, after;\n if (this.input.slice(next, usingEndPos) !== \"using\" ||\n usingEndPos === this.input.length ||\n isIdentifierChar(after = this.fullCharCodeAt(usingEndPos)) ||\n after === 92 /* '\\' */\n ) { return false }\n\n skipWhiteSpace.lastIndex = usingEndPos;\n var skipAfterUsing = skipWhiteSpace.exec(this.input);\n next = usingEndPos + skipAfterUsing[0].length;\n if (skipAfterUsing && lineBreak.test(this.input.slice(usingEndPos, next))) { return false }\n }\n\n var ch = this.fullCharCodeAt(next);\n if (!isIdentifierStart(ch) && ch !== 92 /* '\\' */) { return false }\n var idStart = next;\n do { next += ch <= 0xffff ? 1 : 2; }\n while (isIdentifierChar(ch = this.fullCharCodeAt(next)))\n if (ch === 92) { return true }\n var id = this.input.slice(idStart, next);\n if (keywordRelationalOperator.test(id) || isFor && id === \"of\") { return false }\n return true\n };\n\n pp$8.isAwaitUsing = function(isFor) {\n return this.isUsingKeyword(true, isFor)\n };\n\n pp$8.isUsing = function(isFor) {\n return this.isUsingKeyword(false, isFor)\n };\n\n // Parse a single statement.\n //\n // If expecting a statement and finding a slash operator, parse a\n // regular expression literal. This is to handle cases like\n // `if (foo) /blah/.exec(foo)`, where looking at the previous token\n // does not help.\n\n pp$8.parseStatement = function(context, topLevel, exports) {\n var starttype = this.type, node = this.startNode(), kind;\n\n if (this.isLet(context)) {\n starttype = types$1._var;\n kind = \"let\";\n }\n\n // Most types of statements are recognized by the keyword they\n // start with. Many are trivial to parse, some require a bit of\n // complexity.\n\n switch (starttype) {\n case types$1._break: case types$1._continue: return this.parseBreakContinueStatement(node, starttype.keyword)\n case types$1._debugger: return this.parseDebuggerStatement(node)\n case types$1._do: return this.parseDoStatement(node)\n case types$1._for: return this.parseForStatement(node)\n case types$1._function:\n // Function as sole body of either an if statement or a labeled statement\n // works, but not when it is part of a labeled statement that is the sole\n // body of an if statement.\n if ((context && (this.strict || context !== \"if\" && context !== \"label\")) && this.options.ecmaVersion >= 6) { this.unexpected(); }\n return this.parseFunctionStatement(node, false, !context)\n case types$1._class:\n if (context) { this.unexpected(); }\n return this.parseClass(node, true)\n case types$1._if: return this.parseIfStatement(node)\n case types$1._return: return this.parseReturnStatement(node)\n case types$1._switch: return this.parseSwitchStatement(node)\n case types$1._throw: return this.parseThrowStatement(node)\n case types$1._try: return this.parseTryStatement(node)\n case types$1._const: case types$1._var:\n kind = kind || this.value;\n if (context && kind !== \"var\") { this.unexpected(); }\n return this.parseVarStatement(node, kind)\n case types$1._while: return this.parseWhileStatement(node)\n case types$1._with: return this.parseWithStatement(node)\n case types$1.braceL: return this.parseBlock(true, node)\n case types$1.semi: return this.parseEmptyStatement(node)\n case types$1._export:\n case types$1._import:\n if (this.options.ecmaVersion > 10 && starttype === types$1._import) {\n skipWhiteSpace.lastIndex = this.pos;\n var skip = skipWhiteSpace.exec(this.input);\n var next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next);\n if (nextCh === 40 || nextCh === 46) // '(' or '.'\n { return this.parseExpressionStatement(node, this.parseExpression()) }\n }\n\n if (!this.options.allowImportExportEverywhere) {\n if (!topLevel)\n { this.raise(this.start, \"'import' and 'export' may only appear at the top level\"); }\n if (!this.inModule)\n { this.raise(this.start, \"'import' and 'export' may appear only with 'sourceType: module'\"); }\n }\n return starttype === types$1._import ? this.parseImport(node) : this.parseExport(node, exports)\n\n // If the statement does not start with a statement keyword or a\n // brace, it's an ExpressionStatement or LabeledStatement. We\n // simply start parsing an expression, and afterwards, if the\n // next token is a colon and the expression was a simple\n // Identifier node, we switch to interpreting it as a label.\n default:\n if (this.isAsyncFunction()) {\n if (context) { this.unexpected(); }\n this.next();\n return this.parseFunctionStatement(node, true, !context)\n }\n\n var usingKind = this.isAwaitUsing(false) ? \"await using\" : this.isUsing(false) ? \"using\" : null;\n if (usingKind) {\n if (!this.allowUsing) {\n this.raise(this.start, \"Using declaration cannot appear in the top level when source type is `script` or in the bare case statement\");\n }\n if (usingKind === \"await using\") {\n if (!this.canAwait) {\n this.raise(this.start, \"Await using cannot appear outside of async function\");\n }\n this.next();\n }\n this.next();\n this.parseVar(node, false, usingKind);\n this.semicolon();\n return this.finishNode(node, \"VariableDeclaration\")\n }\n\n var maybeName = this.value, expr = this.parseExpression();\n if (starttype === types$1.name && expr.type === \"Identifier\" && this.eat(types$1.colon))\n { return this.parseLabeledStatement(node, maybeName, expr, context) }\n else { return this.parseExpressionStatement(node, expr) }\n }\n };\n\n pp$8.parseBreakContinueStatement = function(node, keyword) {\n var isBreak = keyword === \"break\";\n this.next();\n if (this.eat(types$1.semi) || this.insertSemicolon()) { node.label = null; }\n else if (this.type !== types$1.name) { this.unexpected(); }\n else {\n node.label = this.parseIdent();\n this.semicolon();\n }\n\n // Verify that there is an actual destination to break or\n // continue to.\n var i = 0;\n for (; i < this.labels.length; ++i) {\n var lab = this.labels[i];\n if (node.label == null || lab.name === node.label.name) {\n if (lab.kind != null && (isBreak || lab.kind === \"loop\")) { break }\n if (node.label && isBreak) { break }\n }\n }\n if (i === this.labels.length) { this.raise(node.start, \"Unsyntactic \" + keyword); }\n return this.finishNode(node, isBreak ? \"BreakStatement\" : \"ContinueStatement\")\n };\n\n pp$8.parseDebuggerStatement = function(node) {\n this.next();\n this.semicolon();\n return this.finishNode(node, \"DebuggerStatement\")\n };\n\n pp$8.parseDoStatement = function(node) {\n this.next();\n this.labels.push(loopLabel);\n node.body = this.parseStatement(\"do\");\n this.labels.pop();\n this.expect(types$1._while);\n node.test = this.parseParenExpression();\n if (this.options.ecmaVersion >= 6)\n { this.eat(types$1.semi); }\n else\n { this.semicolon(); }\n return this.finishNode(node, \"DoWhileStatement\")\n };\n\n // Disambiguating between a `for` and a `for`/`in` or `for`/`of`\n // loop is non-trivial. Basically, we have to parse the init `var`\n // statement or expression, disallowing the `in` operator (see\n // the second parameter to `parseExpression`), and then check\n // whether the next token is `in` or `of`. When there is no init\n // part (semicolon immediately after the opening parenthesis), it\n // is a regular `for` loop.\n\n pp$8.parseForStatement = function(node) {\n this.next();\n var awaitAt = (this.options.ecmaVersion >= 9 && this.canAwait && this.eatContextual(\"await\")) ? this.lastTokStart : -1;\n this.labels.push(loopLabel);\n this.enterScope(0);\n this.expect(types$1.parenL);\n if (this.type === types$1.semi) {\n if (awaitAt > -1) { this.unexpected(awaitAt); }\n return this.parseFor(node, null)\n }\n var isLet = this.isLet();\n if (this.type === types$1._var || this.type === types$1._const || isLet) {\n var init$1 = this.startNode(), kind = isLet ? \"let\" : this.value;\n this.next();\n this.parseVar(init$1, true, kind);\n this.finishNode(init$1, \"VariableDeclaration\");\n return this.parseForAfterInit(node, init$1, awaitAt)\n }\n var startsWithLet = this.isContextual(\"let\"), isForOf = false;\n\n var usingKind = this.isUsing(true) ? \"using\" : this.isAwaitUsing(true) ? \"await using\" : null;\n if (usingKind) {\n var init$2 = this.startNode();\n this.next();\n if (usingKind === \"await using\") {\n if (!this.canAwait) {\n this.raise(this.start, \"Await using cannot appear outside of async function\");\n }\n this.next();\n }\n this.parseVar(init$2, true, usingKind);\n this.finishNode(init$2, \"VariableDeclaration\");\n return this.parseForAfterInit(node, init$2, awaitAt)\n }\n var containsEsc = this.containsEsc;\n var refDestructuringErrors = new DestructuringErrors;\n var initPos = this.start;\n var init = awaitAt > -1\n ? this.parseExprSubscripts(refDestructuringErrors, \"await\")\n : this.parseExpression(true, refDestructuringErrors);\n if (this.type === types$1._in || (isForOf = this.options.ecmaVersion >= 6 && this.isContextual(\"of\"))) {\n if (awaitAt > -1) { // implies `ecmaVersion >= 9` (see declaration of awaitAt)\n if (this.type === types$1._in) { this.unexpected(awaitAt); }\n node.await = true;\n } else if (isForOf && this.options.ecmaVersion >= 8) {\n if (init.start === initPos && !containsEsc && init.type === \"Identifier\" && init.name === \"async\") { this.unexpected(); }\n else if (this.options.ecmaVersion >= 9) { node.await = false; }\n }\n if (startsWithLet && isForOf) { this.raise(init.start, \"The left-hand side of a for-of loop may not start with 'let'.\"); }\n this.toAssignable(init, false, refDestructuringErrors);\n this.checkLValPattern(init);\n return this.parseForIn(node, init)\n } else {\n this.checkExpressionErrors(refDestructuringErrors, true);\n }\n if (awaitAt > -1) { this.unexpected(awaitAt); }\n return this.parseFor(node, init)\n };\n\n // Helper method to parse for loop after variable initialization\n pp$8.parseForAfterInit = function(node, init, awaitAt) {\n if ((this.type === types$1._in || (this.options.ecmaVersion >= 6 && this.isContextual(\"of\"))) && init.declarations.length === 1) {\n if (this.options.ecmaVersion >= 9) {\n if (this.type === types$1._in) {\n if (awaitAt > -1) { this.unexpected(awaitAt); }\n } else { node.await = awaitAt > -1; }\n }\n return this.parseForIn(node, init)\n }\n if (awaitAt > -1) { this.unexpected(awaitAt); }\n return this.parseFor(node, init)\n };\n\n pp$8.parseFunctionStatement = function(node, isAsync, declarationPosition) {\n this.next();\n return this.parseFunction(node, FUNC_STATEMENT | (declarationPosition ? 0 : FUNC_HANGING_STATEMENT), false, isAsync)\n };\n\n pp$8.parseIfStatement = function(node) {\n this.next();\n node.test = this.parseParenExpression();\n // allow function declarations in branches, but only in non-strict mode\n node.consequent = this.parseStatement(\"if\");\n node.alternate = this.eat(types$1._else) ? this.parseStatement(\"if\") : null;\n return this.finishNode(node, \"IfStatement\")\n };\n\n pp$8.parseReturnStatement = function(node) {\n if (!this.allowReturn)\n { this.raise(this.start, \"'return' outside of function\"); }\n this.next();\n\n // In `return` (and `break`/`continue`), the keywords with\n // optional arguments, we eagerly look for a semicolon or the\n // possibility to insert one.\n\n if (this.eat(types$1.semi) || this.insertSemicolon()) { node.argument = null; }\n else { node.argument = this.parseExpression(); this.semicolon(); }\n return this.finishNode(node, \"ReturnStatement\")\n };\n\n pp$8.parseSwitchStatement = function(node) {\n this.next();\n node.discriminant = this.parseParenExpression();\n node.cases = [];\n this.expect(types$1.braceL);\n this.labels.push(switchLabel);\n this.enterScope(SCOPE_SWITCH);\n\n // Statements under must be grouped (by label) in SwitchCase\n // nodes. `cur` is used to keep the node that we are currently\n // adding statements to.\n\n var cur;\n for (var sawDefault = false; this.type !== types$1.braceR;) {\n if (this.type === types$1._case || this.type === types$1._default) {\n var isCase = this.type === types$1._case;\n if (cur) { this.finishNode(cur, \"SwitchCase\"); }\n node.cases.push(cur = this.startNode());\n cur.consequent = [];\n this.next();\n if (isCase) {\n cur.test = this.parseExpression();\n } else {\n if (sawDefault) { this.raiseRecoverable(this.lastTokStart, \"Multiple default clauses\"); }\n sawDefault = true;\n cur.test = null;\n }\n this.expect(types$1.colon);\n } else {\n if (!cur) { this.unexpected(); }\n cur.consequent.push(this.parseStatement(null));\n }\n }\n this.exitScope();\n if (cur) { this.finishNode(cur, \"SwitchCase\"); }\n this.next(); // Closing brace\n this.labels.pop();\n return this.finishNode(node, \"SwitchStatement\")\n };\n\n pp$8.parseThrowStatement = function(node) {\n this.next();\n if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start)))\n { this.raise(this.lastTokEnd, \"Illegal newline after throw\"); }\n node.argument = this.parseExpression();\n this.semicolon();\n return this.finishNode(node, \"ThrowStatement\")\n };\n\n // Reused empty array added for node fields that are always empty.\n\n var empty$1 = [];\n\n pp$8.parseCatchClauseParam = function() {\n var param = this.parseBindingAtom();\n var simple = param.type === \"Identifier\";\n this.enterScope(simple ? SCOPE_SIMPLE_CATCH : 0);\n this.checkLValPattern(param, simple ? BIND_SIMPLE_CATCH : BIND_LEXICAL);\n this.expect(types$1.parenR);\n\n return param\n };\n\n pp$8.parseTryStatement = function(node) {\n this.next();\n node.block = this.parseBlock();\n node.handler = null;\n if (this.type === types$1._catch) {\n var clause = this.startNode();\n this.next();\n if (this.eat(types$1.parenL)) {\n clause.param = this.parseCatchClauseParam();\n } else {\n if (this.options.ecmaVersion < 10) { this.unexpected(); }\n clause.param = null;\n this.enterScope(0);\n }\n clause.body = this.parseBlock(false);\n this.exitScope();\n node.handler = this.finishNode(clause, \"CatchClause\");\n }\n node.finalizer = this.eat(types$1._finally) ? this.parseBlock() : null;\n if (!node.handler && !node.finalizer)\n { this.raise(node.start, \"Missing catch or finally clause\"); }\n return this.finishNode(node, \"TryStatement\")\n };\n\n pp$8.parseVarStatement = function(node, kind, allowMissingInitializer) {\n this.next();\n this.parseVar(node, false, kind, allowMissingInitializer);\n this.semicolon();\n return this.finishNode(node, \"VariableDeclaration\")\n };\n\n pp$8.parseWhileStatement = function(node) {\n this.next();\n node.test = this.parseParenExpression();\n this.labels.push(loopLabel);\n node.body = this.parseStatement(\"while\");\n this.labels.pop();\n return this.finishNode(node, \"WhileStatement\")\n };\n\n pp$8.parseWithStatement = function(node) {\n if (this.strict) { this.raise(this.start, \"'with' in strict mode\"); }\n this.next();\n node.object = this.parseParenExpression();\n node.body = this.parseStatement(\"with\");\n return this.finishNode(node, \"WithStatement\")\n };\n\n pp$8.parseEmptyStatement = function(node) {\n this.next();\n return this.finishNode(node, \"EmptyStatement\")\n };\n\n pp$8.parseLabeledStatement = function(node, maybeName, expr, context) {\n for (var i$1 = 0, list = this.labels; i$1 < list.length; i$1 += 1)\n {\n var label = list[i$1];\n\n if (label.name === maybeName)\n { this.raise(expr.start, \"Label '\" + maybeName + \"' is already declared\");\n } }\n var kind = this.type.isLoop ? \"loop\" : this.type === types$1._switch ? \"switch\" : null;\n for (var i = this.labels.length - 1; i >= 0; i--) {\n var label$1 = this.labels[i];\n if (label$1.statementStart === node.start) {\n // Update information about previous labels on this node\n label$1.statementStart = this.start;\n label$1.kind = kind;\n } else { break }\n }\n this.labels.push({name: maybeName, kind: kind, statementStart: this.start});\n node.body = this.parseStatement(context ? context.indexOf(\"label\") === -1 ? context + \"label\" : context : \"label\");\n this.labels.pop();\n node.label = expr;\n return this.finishNode(node, \"LabeledStatement\")\n };\n\n pp$8.parseExpressionStatement = function(node, expr) {\n node.expression = expr;\n this.semicolon();\n return this.finishNode(node, \"ExpressionStatement\")\n };\n\n // Parse a semicolon-enclosed block of statements, handling `\"use\n // strict\"` declarations when `allowStrict` is true (used for\n // function bodies).\n\n pp$8.parseBlock = function(createNewLexicalScope, node, exitStrict) {\n if ( createNewLexicalScope === void 0 ) createNewLexicalScope = true;\n if ( node === void 0 ) node = this.startNode();\n\n node.body = [];\n this.expect(types$1.braceL);\n if (createNewLexicalScope) { this.enterScope(0); }\n while (this.type !== types$1.braceR) {\n var stmt = this.parseStatement(null);\n node.body.push(stmt);\n }\n if (exitStrict) { this.strict = false; }\n this.next();\n if (createNewLexicalScope) { this.exitScope(); }\n return this.finishNode(node, \"BlockStatement\")\n };\n\n // Parse a regular `for` loop. The disambiguation code in\n // `parseStatement` will already have parsed the init statement or\n // expression.\n\n pp$8.parseFor = function(node, init) {\n node.init = init;\n this.expect(types$1.semi);\n node.test = this.type === types$1.semi ? null : this.parseExpression();\n this.expect(types$1.semi);\n node.update = this.type === types$1.parenR ? null : this.parseExpression();\n this.expect(types$1.parenR);\n node.body = this.parseStatement(\"for\");\n this.exitScope();\n this.labels.pop();\n return this.finishNode(node, \"ForStatement\")\n };\n\n // Parse a `for`/`in` and `for`/`of` loop, which are almost\n // same from parser's perspective.\n\n pp$8.parseForIn = function(node, init) {\n var isForIn = this.type === types$1._in;\n this.next();\n\n if (\n init.type === \"VariableDeclaration\" &&\n init.declarations[0].init != null &&\n (\n !isForIn ||\n this.options.ecmaVersion < 8 ||\n this.strict ||\n init.kind !== \"var\" ||\n init.declarations[0].id.type !== \"Identifier\"\n )\n ) {\n this.raise(\n init.start,\n ((isForIn ? \"for-in\" : \"for-of\") + \" loop variable declaration may not have an initializer\")\n );\n }\n node.left = init;\n node.right = isForIn ? this.parseExpression() : this.parseMaybeAssign();\n this.expect(types$1.parenR);\n node.body = this.parseStatement(\"for\");\n this.exitScope();\n this.labels.pop();\n return this.finishNode(node, isForIn ? \"ForInStatement\" : \"ForOfStatement\")\n };\n\n // Parse a list of variable declarations.\n\n pp$8.parseVar = function(node, isFor, kind, allowMissingInitializer) {\n node.declarations = [];\n node.kind = kind;\n for (;;) {\n var decl = this.startNode();\n this.parseVarId(decl, kind);\n if (this.eat(types$1.eq)) {\n decl.init = this.parseMaybeAssign(isFor);\n } else if (!allowMissingInitializer && kind === \"const\" && !(this.type === types$1._in || (this.options.ecmaVersion >= 6 && this.isContextual(\"of\")))) {\n this.unexpected();\n } else if (!allowMissingInitializer && (kind === \"using\" || kind === \"await using\") && this.options.ecmaVersion >= 17 && this.type !== types$1._in && !this.isContextual(\"of\")) {\n this.raise(this.lastTokEnd, (\"Missing initializer in \" + kind + \" declaration\"));\n } else if (!allowMissingInitializer && decl.id.type !== \"Identifier\" && !(isFor && (this.type === types$1._in || this.isContextual(\"of\")))) {\n this.raise(this.lastTokEnd, \"Complex binding patterns require an initialization value\");\n } else {\n decl.init = null;\n }\n node.declarations.push(this.finishNode(decl, \"VariableDeclarator\"));\n if (!this.eat(types$1.comma)) { break }\n }\n return node\n };\n\n pp$8.parseVarId = function(decl, kind) {\n decl.id = kind === \"using\" || kind === \"await using\"\n ? this.parseIdent()\n : this.parseBindingAtom();\n\n this.checkLValPattern(decl.id, kind === \"var\" ? BIND_VAR : BIND_LEXICAL, false);\n };\n\n var FUNC_STATEMENT = 1, FUNC_HANGING_STATEMENT = 2, FUNC_NULLABLE_ID = 4;\n\n // Parse a function declaration or literal (depending on the\n // `statement & FUNC_STATEMENT`).\n\n // Remove `allowExpressionBody` for 7.0.0, as it is only called with false\n pp$8.parseFunction = function(node, statement, allowExpressionBody, isAsync, forInit) {\n this.initFunction(node);\n if (this.options.ecmaVersion >= 9 || this.options.ecmaVersion >= 6 && !isAsync) {\n if (this.type === types$1.star && (statement & FUNC_HANGING_STATEMENT))\n { this.unexpected(); }\n node.generator = this.eat(types$1.star);\n }\n if (this.options.ecmaVersion >= 8)\n { node.async = !!isAsync; }\n\n if (statement & FUNC_STATEMENT) {\n node.id = (statement & FUNC_NULLABLE_ID) && this.type !== types$1.name ? null : this.parseIdent();\n if (node.id && !(statement & FUNC_HANGING_STATEMENT))\n // If it is a regular function declaration in sloppy mode, then it is\n // subject to Annex B semantics (BIND_FUNCTION). Otherwise, the binding\n // mode depends on properties of the current scope (see\n // treatFunctionsAsVar).\n { this.checkLValSimple(node.id, (this.strict || node.generator || node.async) ? this.treatFunctionsAsVar ? BIND_VAR : BIND_LEXICAL : BIND_FUNCTION); }\n }\n\n var oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n this.yieldPos = 0;\n this.awaitPos = 0;\n this.awaitIdentPos = 0;\n this.enterScope(functionFlags(node.async, node.generator));\n\n if (!(statement & FUNC_STATEMENT))\n { node.id = this.type === types$1.name ? this.parseIdent() : null; }\n\n this.parseFunctionParams(node);\n this.parseFunctionBody(node, allowExpressionBody, false, forInit);\n\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n this.awaitIdentPos = oldAwaitIdentPos;\n return this.finishNode(node, (statement & FUNC_STATEMENT) ? \"FunctionDeclaration\" : \"FunctionExpression\")\n };\n\n pp$8.parseFunctionParams = function(node) {\n this.expect(types$1.parenL);\n node.params = this.parseBindingList(types$1.parenR, false, this.options.ecmaVersion >= 8);\n this.checkYieldAwaitInDefaultParams();\n };\n\n // Parse a class declaration or literal (depending on the\n // `isStatement` parameter).\n\n pp$8.parseClass = function(node, isStatement) {\n this.next();\n\n // ecma-262 14.6 Class Definitions\n // A class definition is always strict mode code.\n var oldStrict = this.strict;\n this.strict = true;\n\n this.parseClassId(node, isStatement);\n this.parseClassSuper(node);\n var privateNameMap = this.enterClassBody();\n var classBody = this.startNode();\n var hadConstructor = false;\n classBody.body = [];\n this.expect(types$1.braceL);\n while (this.type !== types$1.braceR) {\n var element = this.parseClassElement(node.superClass !== null);\n if (element) {\n classBody.body.push(element);\n if (element.type === \"MethodDefinition\" && element.kind === \"constructor\") {\n if (hadConstructor) { this.raiseRecoverable(element.start, \"Duplicate constructor in the same class\"); }\n hadConstructor = true;\n } else if (element.key && element.key.type === \"PrivateIdentifier\" && isPrivateNameConflicted(privateNameMap, element)) {\n this.raiseRecoverable(element.key.start, (\"Identifier '#\" + (element.key.name) + \"' has already been declared\"));\n }\n }\n }\n this.strict = oldStrict;\n this.next();\n node.body = this.finishNode(classBody, \"ClassBody\");\n this.exitClassBody();\n return this.finishNode(node, isStatement ? \"ClassDeclaration\" : \"ClassExpression\")\n };\n\n pp$8.parseClassElement = function(constructorAllowsSuper) {\n if (this.eat(types$1.semi)) { return null }\n\n var ecmaVersion = this.options.ecmaVersion;\n var node = this.startNode();\n var keyName = \"\";\n var isGenerator = false;\n var isAsync = false;\n var kind = \"method\";\n var isStatic = false;\n\n if (this.eatContextual(\"static\")) {\n // Parse static init block\n if (ecmaVersion >= 13 && this.eat(types$1.braceL)) {\n this.parseClassStaticBlock(node);\n return node\n }\n if (this.isClassElementNameStart() || this.type === types$1.star) {\n isStatic = true;\n } else {\n keyName = \"static\";\n }\n }\n node.static = isStatic;\n if (!keyName && ecmaVersion >= 8 && this.eatContextual(\"async\")) {\n if ((this.isClassElementNameStart() || this.type === types$1.star) && !this.canInsertSemicolon()) {\n isAsync = true;\n } else {\n keyName = \"async\";\n }\n }\n if (!keyName && (ecmaVersion >= 9 || !isAsync) && this.eat(types$1.star)) {\n isGenerator = true;\n }\n if (!keyName && !isAsync && !isGenerator) {\n var lastValue = this.value;\n if (this.eatContextual(\"get\") || this.eatContextual(\"set\")) {\n if (this.isClassElementNameStart()) {\n kind = lastValue;\n } else {\n keyName = lastValue;\n }\n }\n }\n\n // Parse element name\n if (keyName) {\n // 'async', 'get', 'set', or 'static' were not a keyword contextually.\n // The last token is any of those. Make it the element name.\n node.computed = false;\n node.key = this.startNodeAt(this.lastTokStart, this.lastTokStartLoc);\n node.key.name = keyName;\n this.finishNode(node.key, \"Identifier\");\n } else {\n this.parseClassElementName(node);\n }\n\n // Parse element value\n if (ecmaVersion < 13 || this.type === types$1.parenL || kind !== \"method\" || isGenerator || isAsync) {\n var isConstructor = !node.static && checkKeyName(node, \"constructor\");\n var allowsDirectSuper = isConstructor && constructorAllowsSuper;\n // Couldn't move this check into the 'parseClassMethod' method for backward compatibility.\n if (isConstructor && kind !== \"method\") { this.raise(node.key.start, \"Constructor can't have get/set modifier\"); }\n node.kind = isConstructor ? \"constructor\" : kind;\n this.parseClassMethod(node, isGenerator, isAsync, allowsDirectSuper);\n } else {\n this.parseClassField(node);\n }\n\n return node\n };\n\n pp$8.isClassElementNameStart = function() {\n return (\n this.type === types$1.name ||\n this.type === types$1.privateId ||\n this.type === types$1.num ||\n this.type === types$1.string ||\n this.type === types$1.bracketL ||\n this.type.keyword\n )\n };\n\n pp$8.parseClassElementName = function(element) {\n if (this.type === types$1.privateId) {\n if (this.value === \"constructor\") {\n this.raise(this.start, \"Classes can't have an element named '#constructor'\");\n }\n element.computed = false;\n element.key = this.parsePrivateIdent();\n } else {\n this.parsePropertyName(element);\n }\n };\n\n pp$8.parseClassMethod = function(method, isGenerator, isAsync, allowsDirectSuper) {\n // Check key and flags\n var key = method.key;\n if (method.kind === \"constructor\") {\n if (isGenerator) { this.raise(key.start, \"Constructor can't be a generator\"); }\n if (isAsync) { this.raise(key.start, \"Constructor can't be an async method\"); }\n } else if (method.static && checkKeyName(method, \"prototype\")) {\n this.raise(key.start, \"Classes may not have a static property named prototype\");\n }\n\n // Parse value\n var value = method.value = this.parseMethod(isGenerator, isAsync, allowsDirectSuper);\n\n // Check value\n if (method.kind === \"get\" && value.params.length !== 0)\n { this.raiseRecoverable(value.start, \"getter should have no params\"); }\n if (method.kind === \"set\" && value.params.length !== 1)\n { this.raiseRecoverable(value.start, \"setter should have exactly one param\"); }\n if (method.kind === \"set\" && value.params[0].type === \"RestElement\")\n { this.raiseRecoverable(value.params[0].start, \"Setter cannot use rest params\"); }\n\n return this.finishNode(method, \"MethodDefinition\")\n };\n\n pp$8.parseClassField = function(field) {\n if (checkKeyName(field, \"constructor\")) {\n this.raise(field.key.start, \"Classes can't have a field named 'constructor'\");\n } else if (field.static && checkKeyName(field, \"prototype\")) {\n this.raise(field.key.start, \"Classes can't have a static field named 'prototype'\");\n }\n\n if (this.eat(types$1.eq)) {\n // To raise SyntaxError if 'arguments' exists in the initializer.\n this.enterScope(SCOPE_CLASS_FIELD_INIT | SCOPE_SUPER);\n field.value = this.parseMaybeAssign();\n this.exitScope();\n } else {\n field.value = null;\n }\n this.semicolon();\n\n return this.finishNode(field, \"PropertyDefinition\")\n };\n\n pp$8.parseClassStaticBlock = function(node) {\n node.body = [];\n\n var oldLabels = this.labels;\n this.labels = [];\n this.enterScope(SCOPE_CLASS_STATIC_BLOCK | SCOPE_SUPER);\n while (this.type !== types$1.braceR) {\n var stmt = this.parseStatement(null);\n node.body.push(stmt);\n }\n this.next();\n this.exitScope();\n this.labels = oldLabels;\n\n return this.finishNode(node, \"StaticBlock\")\n };\n\n pp$8.parseClassId = function(node, isStatement) {\n if (this.type === types$1.name) {\n node.id = this.parseIdent();\n if (isStatement)\n { this.checkLValSimple(node.id, BIND_LEXICAL, false); }\n } else {\n if (isStatement === true)\n { this.unexpected(); }\n node.id = null;\n }\n };\n\n pp$8.parseClassSuper = function(node) {\n node.superClass = this.eat(types$1._extends) ? this.parseExprSubscripts(null, false) : null;\n };\n\n pp$8.enterClassBody = function() {\n var element = {declared: Object.create(null), used: []};\n this.privateNameStack.push(element);\n return element.declared\n };\n\n pp$8.exitClassBody = function() {\n var ref = this.privateNameStack.pop();\n var declared = ref.declared;\n var used = ref.used;\n if (!this.options.checkPrivateFields) { return }\n var len = this.privateNameStack.length;\n var parent = len === 0 ? null : this.privateNameStack[len - 1];\n for (var i = 0; i < used.length; ++i) {\n var id = used[i];\n if (!hasOwn(declared, id.name)) {\n if (parent) {\n parent.used.push(id);\n } else {\n this.raiseRecoverable(id.start, (\"Private field '#\" + (id.name) + \"' must be declared in an enclosing class\"));\n }\n }\n }\n };\n\n function isPrivateNameConflicted(privateNameMap, element) {\n var name = element.key.name;\n var curr = privateNameMap[name];\n\n var next = \"true\";\n if (element.type === \"MethodDefinition\" && (element.kind === \"get\" || element.kind === \"set\")) {\n next = (element.static ? \"s\" : \"i\") + element.kind;\n }\n\n // `class { get #a(){}; static set #a(_){} }` is also conflict.\n if (\n curr === \"iget\" && next === \"iset\" ||\n curr === \"iset\" && next === \"iget\" ||\n curr === \"sget\" && next === \"sset\" ||\n curr === \"sset\" && next === \"sget\"\n ) {\n privateNameMap[name] = \"true\";\n return false\n } else if (!curr) {\n privateNameMap[name] = next;\n return false\n } else {\n return true\n }\n }\n\n function checkKeyName(node, name) {\n var computed = node.computed;\n var key = node.key;\n return !computed && (\n key.type === \"Identifier\" && key.name === name ||\n key.type === \"Literal\" && key.value === name\n )\n }\n\n // Parses module export declaration.\n\n pp$8.parseExportAllDeclaration = function(node, exports) {\n if (this.options.ecmaVersion >= 11) {\n if (this.eatContextual(\"as\")) {\n node.exported = this.parseModuleExportName();\n this.checkExport(exports, node.exported, this.lastTokStart);\n } else {\n node.exported = null;\n }\n }\n this.expectContextual(\"from\");\n if (this.type !== types$1.string) { this.unexpected(); }\n node.source = this.parseExprAtom();\n if (this.options.ecmaVersion >= 16)\n { node.attributes = this.parseWithClause(); }\n this.semicolon();\n return this.finishNode(node, \"ExportAllDeclaration\")\n };\n\n pp$8.parseExport = function(node, exports) {\n this.next();\n // export * from '...'\n if (this.eat(types$1.star)) {\n return this.parseExportAllDeclaration(node, exports)\n }\n if (this.eat(types$1._default)) { // export default ...\n this.checkExport(exports, \"default\", this.lastTokStart);\n node.declaration = this.parseExportDefaultDeclaration();\n return this.finishNode(node, \"ExportDefaultDeclaration\")\n }\n // export var|const|let|function|class ...\n if (this.shouldParseExportStatement()) {\n node.declaration = this.parseExportDeclaration(node);\n if (node.declaration.type === \"VariableDeclaration\")\n { this.checkVariableExport(exports, node.declaration.declarations); }\n else\n { this.checkExport(exports, node.declaration.id, node.declaration.id.start); }\n node.specifiers = [];\n node.source = null;\n if (this.options.ecmaVersion >= 16)\n { node.attributes = []; }\n } else { // export { x, y as z } [from '...']\n node.declaration = null;\n node.specifiers = this.parseExportSpecifiers(exports);\n if (this.eatContextual(\"from\")) {\n if (this.type !== types$1.string) { this.unexpected(); }\n node.source = this.parseExprAtom();\n if (this.options.ecmaVersion >= 16)\n { node.attributes = this.parseWithClause(); }\n } else {\n for (var i = 0, list = node.specifiers; i < list.length; i += 1) {\n // check for keywords used as local names\n var spec = list[i];\n\n this.checkUnreserved(spec.local);\n // check if export is defined\n this.checkLocalExport(spec.local);\n\n if (spec.local.type === \"Literal\") {\n this.raise(spec.local.start, \"A string literal cannot be used as an exported binding without `from`.\");\n }\n }\n\n node.source = null;\n if (this.options.ecmaVersion >= 16)\n { node.attributes = []; }\n }\n this.semicolon();\n }\n return this.finishNode(node, \"ExportNamedDeclaration\")\n };\n\n pp$8.parseExportDeclaration = function(node) {\n return this.parseStatement(null)\n };\n\n pp$8.parseExportDefaultDeclaration = function() {\n var isAsync;\n if (this.type === types$1._function || (isAsync = this.isAsyncFunction())) {\n var fNode = this.startNode();\n this.next();\n if (isAsync) { this.next(); }\n return this.parseFunction(fNode, FUNC_STATEMENT | FUNC_NULLABLE_ID, false, isAsync)\n } else if (this.type === types$1._class) {\n var cNode = this.startNode();\n return this.parseClass(cNode, \"nullableID\")\n } else {\n var declaration = this.parseMaybeAssign();\n this.semicolon();\n return declaration\n }\n };\n\n pp$8.checkExport = function(exports, name, pos) {\n if (!exports) { return }\n if (typeof name !== \"string\")\n { name = name.type === \"Identifier\" ? name.name : name.value; }\n if (hasOwn(exports, name))\n { this.raiseRecoverable(pos, \"Duplicate export '\" + name + \"'\"); }\n exports[name] = true;\n };\n\n pp$8.checkPatternExport = function(exports, pat) {\n var type = pat.type;\n if (type === \"Identifier\")\n { this.checkExport(exports, pat, pat.start); }\n else if (type === \"ObjectPattern\")\n { for (var i = 0, list = pat.properties; i < list.length; i += 1)\n {\n var prop = list[i];\n\n this.checkPatternExport(exports, prop);\n } }\n else if (type === \"ArrayPattern\")\n { for (var i$1 = 0, list$1 = pat.elements; i$1 < list$1.length; i$1 += 1) {\n var elt = list$1[i$1];\n\n if (elt) { this.checkPatternExport(exports, elt); }\n } }\n else if (type === \"Property\")\n { this.checkPatternExport(exports, pat.value); }\n else if (type === \"AssignmentPattern\")\n { this.checkPatternExport(exports, pat.left); }\n else if (type === \"RestElement\")\n { this.checkPatternExport(exports, pat.argument); }\n };\n\n pp$8.checkVariableExport = function(exports, decls) {\n if (!exports) { return }\n for (var i = 0, list = decls; i < list.length; i += 1)\n {\n var decl = list[i];\n\n this.checkPatternExport(exports, decl.id);\n }\n };\n\n pp$8.shouldParseExportStatement = function() {\n return this.type.keyword === \"var\" ||\n this.type.keyword === \"const\" ||\n this.type.keyword === \"class\" ||\n this.type.keyword === \"function\" ||\n this.isLet() ||\n this.isAsyncFunction()\n };\n\n // Parses a comma-separated list of module exports.\n\n pp$8.parseExportSpecifier = function(exports) {\n var node = this.startNode();\n node.local = this.parseModuleExportName();\n\n node.exported = this.eatContextual(\"as\") ? this.parseModuleExportName() : node.local;\n this.checkExport(\n exports,\n node.exported,\n node.exported.start\n );\n\n return this.finishNode(node, \"ExportSpecifier\")\n };\n\n pp$8.parseExportSpecifiers = function(exports) {\n var nodes = [], first = true;\n // export { x, y as z } [from '...']\n this.expect(types$1.braceL);\n while (!this.eat(types$1.braceR)) {\n if (!first) {\n this.expect(types$1.comma);\n if (this.afterTrailingComma(types$1.braceR)) { break }\n } else { first = false; }\n\n nodes.push(this.parseExportSpecifier(exports));\n }\n return nodes\n };\n\n // Parses import declaration.\n\n pp$8.parseImport = function(node) {\n this.next();\n\n // import '...'\n if (this.type === types$1.string) {\n node.specifiers = empty$1;\n node.source = this.parseExprAtom();\n } else {\n node.specifiers = this.parseImportSpecifiers();\n this.expectContextual(\"from\");\n node.source = this.type === types$1.string ? this.parseExprAtom() : this.unexpected();\n }\n if (this.options.ecmaVersion >= 16)\n { node.attributes = this.parseWithClause(); }\n this.semicolon();\n return this.finishNode(node, \"ImportDeclaration\")\n };\n\n // Parses a comma-separated list of module imports.\n\n pp$8.parseImportSpecifier = function() {\n var node = this.startNode();\n node.imported = this.parseModuleExportName();\n\n if (this.eatContextual(\"as\")) {\n node.local = this.parseIdent();\n } else {\n this.checkUnreserved(node.imported);\n node.local = node.imported;\n }\n this.checkLValSimple(node.local, BIND_LEXICAL);\n\n return this.finishNode(node, \"ImportSpecifier\")\n };\n\n pp$8.parseImportDefaultSpecifier = function() {\n // import defaultObj, { x, y as z } from '...'\n var node = this.startNode();\n node.local = this.parseIdent();\n this.checkLValSimple(node.local, BIND_LEXICAL);\n return this.finishNode(node, \"ImportDefaultSpecifier\")\n };\n\n pp$8.parseImportNamespaceSpecifier = function() {\n var node = this.startNode();\n this.next();\n this.expectContextual(\"as\");\n node.local = this.parseIdent();\n this.checkLValSimple(node.local, BIND_LEXICAL);\n return this.finishNode(node, \"ImportNamespaceSpecifier\")\n };\n\n pp$8.parseImportSpecifiers = function() {\n var nodes = [], first = true;\n if (this.type === types$1.name) {\n nodes.push(this.parseImportDefaultSpecifier());\n if (!this.eat(types$1.comma)) { return nodes }\n }\n if (this.type === types$1.star) {\n nodes.push(this.parseImportNamespaceSpecifier());\n return nodes\n }\n this.expect(types$1.braceL);\n while (!this.eat(types$1.braceR)) {\n if (!first) {\n this.expect(types$1.comma);\n if (this.afterTrailingComma(types$1.braceR)) { break }\n } else { first = false; }\n\n nodes.push(this.parseImportSpecifier());\n }\n return nodes\n };\n\n pp$8.parseWithClause = function() {\n var nodes = [];\n if (!this.eat(types$1._with)) {\n return nodes\n }\n this.expect(types$1.braceL);\n var attributeKeys = {};\n var first = true;\n while (!this.eat(types$1.braceR)) {\n if (!first) {\n this.expect(types$1.comma);\n if (this.afterTrailingComma(types$1.braceR)) { break }\n } else { first = false; }\n\n var attr = this.parseImportAttribute();\n var keyName = attr.key.type === \"Identifier\" ? attr.key.name : attr.key.value;\n if (hasOwn(attributeKeys, keyName))\n { this.raiseRecoverable(attr.key.start, \"Duplicate attribute key '\" + keyName + \"'\"); }\n attributeKeys[keyName] = true;\n nodes.push(attr);\n }\n return nodes\n };\n\n pp$8.parseImportAttribute = function() {\n var node = this.startNode();\n node.key = this.type === types$1.string ? this.parseExprAtom() : this.parseIdent(this.options.allowReserved !== \"never\");\n this.expect(types$1.colon);\n if (this.type !== types$1.string) {\n this.unexpected();\n }\n node.value = this.parseExprAtom();\n return this.finishNode(node, \"ImportAttribute\")\n };\n\n pp$8.parseModuleExportName = function() {\n if (this.options.ecmaVersion >= 13 && this.type === types$1.string) {\n var stringLiteral = this.parseLiteral(this.value);\n if (loneSurrogate.test(stringLiteral.value)) {\n this.raise(stringLiteral.start, \"An export name cannot include a lone surrogate.\");\n }\n return stringLiteral\n }\n return this.parseIdent(true)\n };\n\n // Set `ExpressionStatement#directive` property for directive prologues.\n pp$8.adaptDirectivePrologue = function(statements) {\n for (var i = 0; i < statements.length && this.isDirectiveCandidate(statements[i]); ++i) {\n statements[i].directive = statements[i].expression.raw.slice(1, -1);\n }\n };\n pp$8.isDirectiveCandidate = function(statement) {\n return (\n this.options.ecmaVersion >= 5 &&\n statement.type === \"ExpressionStatement\" &&\n statement.expression.type === \"Literal\" &&\n typeof statement.expression.value === \"string\" &&\n // Reject parenthesized strings.\n (this.input[statement.start] === \"\\\"\" || this.input[statement.start] === \"'\")\n )\n };\n\n var pp$7 = Parser.prototype;\n\n // Convert existing expression atom to assignable pattern\n // if possible.\n\n pp$7.toAssignable = function(node, isBinding, refDestructuringErrors) {\n if (this.options.ecmaVersion >= 6 && node) {\n switch (node.type) {\n case \"Identifier\":\n if (this.inAsync && node.name === \"await\")\n { this.raise(node.start, \"Cannot use 'await' as identifier inside an async function\"); }\n break\n\n case \"ObjectPattern\":\n case \"ArrayPattern\":\n case \"AssignmentPattern\":\n case \"RestElement\":\n break\n\n case \"ObjectExpression\":\n node.type = \"ObjectPattern\";\n if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }\n for (var i = 0, list = node.properties; i < list.length; i += 1) {\n var prop = list[i];\n\n this.toAssignable(prop, isBinding);\n // Early error:\n // AssignmentRestProperty[Yield, Await] :\n // `...` DestructuringAssignmentTarget[Yield, Await]\n //\n // It is a Syntax Error if |DestructuringAssignmentTarget| is an |ArrayLiteral| or an |ObjectLiteral|.\n if (\n prop.type === \"RestElement\" &&\n (prop.argument.type === \"ArrayPattern\" || prop.argument.type === \"ObjectPattern\")\n ) {\n this.raise(prop.argument.start, \"Unexpected token\");\n }\n }\n break\n\n case \"Property\":\n // AssignmentProperty has type === \"Property\"\n if (node.kind !== \"init\") { this.raise(node.key.start, \"Object pattern can't contain getter or setter\"); }\n this.toAssignable(node.value, isBinding);\n break\n\n case \"ArrayExpression\":\n node.type = \"ArrayPattern\";\n if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }\n this.toAssignableList(node.elements, isBinding);\n break\n\n case \"SpreadElement\":\n node.type = \"RestElement\";\n this.toAssignable(node.argument, isBinding);\n if (node.argument.type === \"AssignmentPattern\")\n { this.raise(node.argument.start, \"Rest elements cannot have a default value\"); }\n break\n\n case \"AssignmentExpression\":\n if (node.operator !== \"=\") { this.raise(node.left.end, \"Only '=' operator can be used for specifying default value.\"); }\n node.type = \"AssignmentPattern\";\n delete node.operator;\n this.toAssignable(node.left, isBinding);\n break\n\n case \"ParenthesizedExpression\":\n this.toAssignable(node.expression, isBinding, refDestructuringErrors);\n break\n\n case \"ChainExpression\":\n this.raiseRecoverable(node.start, \"Optional chaining cannot appear in left-hand side\");\n break\n\n case \"MemberExpression\":\n if (!isBinding) { break }\n\n default:\n this.raise(node.start, \"Assigning to rvalue\");\n }\n } else if (refDestructuringErrors) { this.checkPatternErrors(refDestructuringErrors, true); }\n return node\n };\n\n // Convert list of expression atoms to binding list.\n\n pp$7.toAssignableList = function(exprList, isBinding) {\n var end = exprList.length;\n for (var i = 0; i < end; i++) {\n var elt = exprList[i];\n if (elt) { this.toAssignable(elt, isBinding); }\n }\n if (end) {\n var last = exprList[end - 1];\n if (this.options.ecmaVersion === 6 && isBinding && last && last.type === \"RestElement\" && last.argument.type !== \"Identifier\")\n { this.unexpected(last.argument.start); }\n }\n return exprList\n };\n\n // Parses spread element.\n\n pp$7.parseSpread = function(refDestructuringErrors) {\n var node = this.startNode();\n this.next();\n node.argument = this.parseMaybeAssign(false, refDestructuringErrors);\n return this.finishNode(node, \"SpreadElement\")\n };\n\n pp$7.parseRestBinding = function() {\n var node = this.startNode();\n this.next();\n\n // RestElement inside of a function parameter must be an identifier\n if (this.options.ecmaVersion === 6 && this.type !== types$1.name)\n { this.unexpected(); }\n\n node.argument = this.parseBindingAtom();\n\n return this.finishNode(node, \"RestElement\")\n };\n\n // Parses lvalue (assignable) atom.\n\n pp$7.parseBindingAtom = function() {\n if (this.options.ecmaVersion >= 6) {\n switch (this.type) {\n case types$1.bracketL:\n var node = this.startNode();\n this.next();\n node.elements = this.parseBindingList(types$1.bracketR, true, true);\n return this.finishNode(node, \"ArrayPattern\")\n\n case types$1.braceL:\n return this.parseObj(true)\n }\n }\n return this.parseIdent()\n };\n\n pp$7.parseBindingList = function(close, allowEmpty, allowTrailingComma, allowModifiers) {\n var elts = [], first = true;\n while (!this.eat(close)) {\n if (first) { first = false; }\n else { this.expect(types$1.comma); }\n if (allowEmpty && this.type === types$1.comma) {\n elts.push(null);\n } else if (allowTrailingComma && this.afterTrailingComma(close)) {\n break\n } else if (this.type === types$1.ellipsis) {\n var rest = this.parseRestBinding();\n this.parseBindingListItem(rest);\n elts.push(rest);\n if (this.type === types$1.comma) { this.raiseRecoverable(this.start, \"Comma is not permitted after the rest element\"); }\n this.expect(close);\n break\n } else {\n elts.push(this.parseAssignableListItem(allowModifiers));\n }\n }\n return elts\n };\n\n pp$7.parseAssignableListItem = function(allowModifiers) {\n var elem = this.parseMaybeDefault(this.start, this.startLoc);\n this.parseBindingListItem(elem);\n return elem\n };\n\n pp$7.parseBindingListItem = function(param) {\n return param\n };\n\n // Parses assignment pattern around given atom if possible.\n\n pp$7.parseMaybeDefault = function(startPos, startLoc, left) {\n left = left || this.parseBindingAtom();\n if (this.options.ecmaVersion < 6 || !this.eat(types$1.eq)) { return left }\n var node = this.startNodeAt(startPos, startLoc);\n node.left = left;\n node.right = this.parseMaybeAssign();\n return this.finishNode(node, \"AssignmentPattern\")\n };\n\n // The following three functions all verify that a node is an lvalue \u2014\n // something that can be bound, or assigned to. In order to do so, they perform\n // a variety of checks:\n //\n // - Check that none of the bound/assigned-to identifiers are reserved words.\n // - Record name declarations for bindings in the appropriate scope.\n // - Check duplicate argument names, if checkClashes is set.\n //\n // If a complex binding pattern is encountered (e.g., object and array\n // destructuring), the entire pattern is recursively checked.\n //\n // There are three versions of checkLVal*() appropriate for different\n // circumstances:\n //\n // - checkLValSimple() shall be used if the syntactic construct supports\n // nothing other than identifiers and member expressions. Parenthesized\n // expressions are also correctly handled. This is generally appropriate for\n // constructs for which the spec says\n //\n // > It is a Syntax Error if AssignmentTargetType of [the production] is not\n // > simple.\n //\n // It is also appropriate for checking if an identifier is valid and not\n // defined elsewhere, like import declarations or function/class identifiers.\n //\n // Examples where this is used include:\n // a += \u2026;\n // import a from '\u2026';\n // where a is the node to be checked.\n //\n // - checkLValPattern() shall be used if the syntactic construct supports\n // anything checkLValSimple() supports, as well as object and array\n // destructuring patterns. This is generally appropriate for constructs for\n // which the spec says\n //\n // > It is a Syntax Error if [the production] is neither an ObjectLiteral nor\n // > an ArrayLiteral and AssignmentTargetType of [the production] is not\n // > simple.\n //\n // Examples where this is used include:\n // (a = \u2026);\n // const a = \u2026;\n // try { \u2026 } catch (a) { \u2026 }\n // where a is the node to be checked.\n //\n // - checkLValInnerPattern() shall be used if the syntactic construct supports\n // anything checkLValPattern() supports, as well as default assignment\n // patterns, rest elements, and other constructs that may appear within an\n // object or array destructuring pattern.\n //\n // As a special case, function parameters also use checkLValInnerPattern(),\n // as they also support defaults and rest constructs.\n //\n // These functions deliberately support both assignment and binding constructs,\n // as the logic for both is exceedingly similar. If the node is the target of\n // an assignment, then bindingType should be set to BIND_NONE. Otherwise, it\n // should be set to the appropriate BIND_* constant, like BIND_VAR or\n // BIND_LEXICAL.\n //\n // If the function is called with a non-BIND_NONE bindingType, then\n // additionally a checkClashes object may be specified to allow checking for\n // duplicate argument names. checkClashes is ignored if the provided construct\n // is an assignment (i.e., bindingType is BIND_NONE).\n\n pp$7.checkLValSimple = function(expr, bindingType, checkClashes) {\n if ( bindingType === void 0 ) bindingType = BIND_NONE;\n\n var isBind = bindingType !== BIND_NONE;\n\n switch (expr.type) {\n case \"Identifier\":\n if (this.strict && this.reservedWordsStrictBind.test(expr.name))\n { this.raiseRecoverable(expr.start, (isBind ? \"Binding \" : \"Assigning to \") + expr.name + \" in strict mode\"); }\n if (isBind) {\n if (bindingType === BIND_LEXICAL && expr.name === \"let\")\n { this.raiseRecoverable(expr.start, \"let is disallowed as a lexically bound name\"); }\n if (checkClashes) {\n if (hasOwn(checkClashes, expr.name))\n { this.raiseRecoverable(expr.start, \"Argument name clash\"); }\n checkClashes[expr.name] = true;\n }\n if (bindingType !== BIND_OUTSIDE) { this.declareName(expr.name, bindingType, expr.start); }\n }\n break\n\n case \"ChainExpression\":\n this.raiseRecoverable(expr.start, \"Optional chaining cannot appear in left-hand side\");\n break\n\n case \"MemberExpression\":\n if (isBind) { this.raiseRecoverable(expr.start, \"Binding member expression\"); }\n break\n\n case \"ParenthesizedExpression\":\n if (isBind) { this.raiseRecoverable(expr.start, \"Binding parenthesized expression\"); }\n return this.checkLValSimple(expr.expression, bindingType, checkClashes)\n\n default:\n this.raise(expr.start, (isBind ? \"Binding\" : \"Assigning to\") + \" rvalue\");\n }\n };\n\n pp$7.checkLValPattern = function(expr, bindingType, checkClashes) {\n if ( bindingType === void 0 ) bindingType = BIND_NONE;\n\n switch (expr.type) {\n case \"ObjectPattern\":\n for (var i = 0, list = expr.properties; i < list.length; i += 1) {\n var prop = list[i];\n\n this.checkLValInnerPattern(prop, bindingType, checkClashes);\n }\n break\n\n case \"ArrayPattern\":\n for (var i$1 = 0, list$1 = expr.elements; i$1 < list$1.length; i$1 += 1) {\n var elem = list$1[i$1];\n\n if (elem) { this.checkLValInnerPattern(elem, bindingType, checkClashes); }\n }\n break\n\n default:\n this.checkLValSimple(expr, bindingType, checkClashes);\n }\n };\n\n pp$7.checkLValInnerPattern = function(expr, bindingType, checkClashes) {\n if ( bindingType === void 0 ) bindingType = BIND_NONE;\n\n switch (expr.type) {\n case \"Property\":\n // AssignmentProperty has type === \"Property\"\n this.checkLValInnerPattern(expr.value, bindingType, checkClashes);\n break\n\n case \"AssignmentPattern\":\n this.checkLValPattern(expr.left, bindingType, checkClashes);\n break\n\n case \"RestElement\":\n this.checkLValPattern(expr.argument, bindingType, checkClashes);\n break\n\n default:\n this.checkLValPattern(expr, bindingType, checkClashes);\n }\n };\n\n // The algorithm used to determine whether a regexp can appear at a\n // given point in the program is loosely based on sweet.js' approach.\n // See https://github.com/mozilla/sweet.js/wiki/design\n\n\n var TokContext = function TokContext(token, isExpr, preserveSpace, override, generator) {\n this.token = token;\n this.isExpr = !!isExpr;\n this.preserveSpace = !!preserveSpace;\n this.override = override;\n this.generator = !!generator;\n };\n\n var types = {\n b_stat: new TokContext(\"{\", false),\n b_expr: new TokContext(\"{\", true),\n b_tmpl: new TokContext(\"${\", false),\n p_stat: new TokContext(\"(\", false),\n p_expr: new TokContext(\"(\", true),\n q_tmpl: new TokContext(\"`\", true, true, function (p) { return p.tryReadTemplateToken(); }),\n f_stat: new TokContext(\"function\", false),\n f_expr: new TokContext(\"function\", true),\n f_expr_gen: new TokContext(\"function\", true, false, null, true),\n f_gen: new TokContext(\"function\", false, false, null, true)\n };\n\n var pp$6 = Parser.prototype;\n\n pp$6.initialContext = function() {\n return [types.b_stat]\n };\n\n pp$6.curContext = function() {\n return this.context[this.context.length - 1]\n };\n\n pp$6.braceIsBlock = function(prevType) {\n var parent = this.curContext();\n if (parent === types.f_expr || parent === types.f_stat)\n { return true }\n if (prevType === types$1.colon && (parent === types.b_stat || parent === types.b_expr))\n { return !parent.isExpr }\n\n // The check for `tt.name && exprAllowed` detects whether we are\n // after a `yield` or `of` construct. See the `updateContext` for\n // `tt.name`.\n if (prevType === types$1._return || prevType === types$1.name && this.exprAllowed)\n { return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) }\n if (prevType === types$1._else || prevType === types$1.semi || prevType === types$1.eof || prevType === types$1.parenR || prevType === types$1.arrow)\n { return true }\n if (prevType === types$1.braceL)\n { return parent === types.b_stat }\n if (prevType === types$1._var || prevType === types$1._const || prevType === types$1.name)\n { return false }\n return !this.exprAllowed\n };\n\n pp$6.inGeneratorContext = function() {\n for (var i = this.context.length - 1; i >= 1; i--) {\n var context = this.context[i];\n if (context.token === \"function\")\n { return context.generator }\n }\n return false\n };\n\n pp$6.updateContext = function(prevType) {\n var update, type = this.type;\n if (type.keyword && prevType === types$1.dot)\n { this.exprAllowed = false; }\n else if (update = type.updateContext)\n { update.call(this, prevType); }\n else\n { this.exprAllowed = type.beforeExpr; }\n };\n\n // Used to handle edge cases when token context could not be inferred correctly during tokenization phase\n\n pp$6.overrideContext = function(tokenCtx) {\n if (this.curContext() !== tokenCtx) {\n this.context[this.context.length - 1] = tokenCtx;\n }\n };\n\n // Token-specific context update code\n\n types$1.parenR.updateContext = types$1.braceR.updateContext = function() {\n if (this.context.length === 1) {\n this.exprAllowed = true;\n return\n }\n var out = this.context.pop();\n if (out === types.b_stat && this.curContext().token === \"function\") {\n out = this.context.pop();\n }\n this.exprAllowed = !out.isExpr;\n };\n\n types$1.braceL.updateContext = function(prevType) {\n this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr);\n this.exprAllowed = true;\n };\n\n types$1.dollarBraceL.updateContext = function() {\n this.context.push(types.b_tmpl);\n this.exprAllowed = true;\n };\n\n types$1.parenL.updateContext = function(prevType) {\n var statementParens = prevType === types$1._if || prevType === types$1._for || prevType === types$1._with || prevType === types$1._while;\n this.context.push(statementParens ? types.p_stat : types.p_expr);\n this.exprAllowed = true;\n };\n\n types$1.incDec.updateContext = function() {\n // tokExprAllowed stays unchanged\n };\n\n types$1._function.updateContext = types$1._class.updateContext = function(prevType) {\n if (prevType.beforeExpr && prevType !== types$1._else &&\n !(prevType === types$1.semi && this.curContext() !== types.p_stat) &&\n !(prevType === types$1._return && lineBreak.test(this.input.slice(this.lastTokEnd, this.start))) &&\n !((prevType === types$1.colon || prevType === types$1.braceL) && this.curContext() === types.b_stat))\n { this.context.push(types.f_expr); }\n else\n { this.context.push(types.f_stat); }\n this.exprAllowed = false;\n };\n\n types$1.colon.updateContext = function() {\n if (this.curContext().token === \"function\") { this.context.pop(); }\n this.exprAllowed = true;\n };\n\n types$1.backQuote.updateContext = function() {\n if (this.curContext() === types.q_tmpl)\n { this.context.pop(); }\n else\n { this.context.push(types.q_tmpl); }\n this.exprAllowed = false;\n };\n\n types$1.star.updateContext = function(prevType) {\n if (prevType === types$1._function) {\n var index = this.context.length - 1;\n if (this.context[index] === types.f_expr)\n { this.context[index] = types.f_expr_gen; }\n else\n { this.context[index] = types.f_gen; }\n }\n this.exprAllowed = true;\n };\n\n types$1.name.updateContext = function(prevType) {\n var allowed = false;\n if (this.options.ecmaVersion >= 6 && prevType !== types$1.dot) {\n if (this.value === \"of\" && !this.exprAllowed ||\n this.value === \"yield\" && this.inGeneratorContext())\n { allowed = true; }\n }\n this.exprAllowed = allowed;\n };\n\n // A recursive descent parser operates by defining functions for all\n // syntactic elements, and recursively calling those, each function\n // advancing the input stream and returning an AST node. Precedence\n // of constructs (for example, the fact that `!x[1]` means `!(x[1])`\n // instead of `(!x)[1]` is handled by the fact that the parser\n // function that parses unary prefix operators is called first, and\n // in turn calls the function that parses `[]` subscripts \u2014 that\n // way, it'll receive the node for `x[1]` already parsed, and wraps\n // *that* in the unary operator node.\n //\n // Acorn uses an [operator precedence parser][opp] to handle binary\n // operator precedence, because it is much more compact than using\n // the technique outlined above, which uses different, nesting\n // functions to specify precedence, for all of the ten binary\n // precedence levels that JavaScript defines.\n //\n // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser\n\n\n var pp$5 = Parser.prototype;\n\n // Check if property name clashes with already added.\n // Object/class getters and setters are not allowed to clash \u2014\n // either with each other or with an init property \u2014 and in\n // strict mode, init properties are also not allowed to be repeated.\n\n pp$5.checkPropClash = function(prop, propHash, refDestructuringErrors) {\n if (this.options.ecmaVersion >= 9 && prop.type === \"SpreadElement\")\n { return }\n if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand))\n { return }\n var key = prop.key;\n var name;\n switch (key.type) {\n case \"Identifier\": name = key.name; break\n case \"Literal\": name = String(key.value); break\n default: return\n }\n var kind = prop.kind;\n if (this.options.ecmaVersion >= 6) {\n if (name === \"__proto__\" && kind === \"init\") {\n if (propHash.proto) {\n if (refDestructuringErrors) {\n if (refDestructuringErrors.doubleProto < 0) {\n refDestructuringErrors.doubleProto = key.start;\n }\n } else {\n this.raiseRecoverable(key.start, \"Redefinition of __proto__ property\");\n }\n }\n propHash.proto = true;\n }\n return\n }\n name = \"$\" + name;\n var other = propHash[name];\n if (other) {\n var redefinition;\n if (kind === \"init\") {\n redefinition = this.strict && other.init || other.get || other.set;\n } else {\n redefinition = other.init || other[kind];\n }\n if (redefinition)\n { this.raiseRecoverable(key.start, \"Redefinition of property\"); }\n } else {\n other = propHash[name] = {\n init: false,\n get: false,\n set: false\n };\n }\n other[kind] = true;\n };\n\n // ### Expression parsing\n\n // These nest, from the most general expression type at the top to\n // 'atomic', nondivisible expression types at the bottom. Most of\n // the functions will simply let the function(s) below them parse,\n // and, *if* the syntactic construct they handle is present, wrap\n // the AST node that the inner parser gave them in another node.\n\n // Parse a full expression. The optional arguments are used to\n // forbid the `in` operator (in for loops initalization expressions)\n // and provide reference for storing '=' operator inside shorthand\n // property assignment in contexts where both object expression\n // and object pattern might appear (so it's possible to raise\n // delayed syntax error at correct position).\n\n pp$5.parseExpression = function(forInit, refDestructuringErrors) {\n var startPos = this.start, startLoc = this.startLoc;\n var expr = this.parseMaybeAssign(forInit, refDestructuringErrors);\n if (this.type === types$1.comma) {\n var node = this.startNodeAt(startPos, startLoc);\n node.expressions = [expr];\n while (this.eat(types$1.comma)) { node.expressions.push(this.parseMaybeAssign(forInit, refDestructuringErrors)); }\n return this.finishNode(node, \"SequenceExpression\")\n }\n return expr\n };\n\n // Parse an assignment expression. This includes applications of\n // operators like `+=`.\n\n pp$5.parseMaybeAssign = function(forInit, refDestructuringErrors, afterLeftParse) {\n if (this.isContextual(\"yield\")) {\n if (this.inGenerator) { return this.parseYield(forInit) }\n // The tokenizer will assume an expression is allowed after\n // `yield`, but this isn't that kind of yield\n else { this.exprAllowed = false; }\n }\n\n var ownDestructuringErrors = false, oldParenAssign = -1, oldTrailingComma = -1, oldDoubleProto = -1;\n if (refDestructuringErrors) {\n oldParenAssign = refDestructuringErrors.parenthesizedAssign;\n oldTrailingComma = refDestructuringErrors.trailingComma;\n oldDoubleProto = refDestructuringErrors.doubleProto;\n refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = -1;\n } else {\n refDestructuringErrors = new DestructuringErrors;\n ownDestructuringErrors = true;\n }\n\n var startPos = this.start, startLoc = this.startLoc;\n if (this.type === types$1.parenL || this.type === types$1.name) {\n this.potentialArrowAt = this.start;\n this.potentialArrowInForAwait = forInit === \"await\";\n }\n var left = this.parseMaybeConditional(forInit, refDestructuringErrors);\n if (afterLeftParse) { left = afterLeftParse.call(this, left, startPos, startLoc); }\n if (this.type.isAssign) {\n var node = this.startNodeAt(startPos, startLoc);\n node.operator = this.value;\n if (this.type === types$1.eq)\n { left = this.toAssignable(left, false, refDestructuringErrors); }\n if (!ownDestructuringErrors) {\n refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = refDestructuringErrors.doubleProto = -1;\n }\n if (refDestructuringErrors.shorthandAssign >= left.start)\n { refDestructuringErrors.shorthandAssign = -1; } // reset because shorthand default was used correctly\n if (this.type === types$1.eq)\n { this.checkLValPattern(left); }\n else\n { this.checkLValSimple(left); }\n node.left = left;\n this.next();\n node.right = this.parseMaybeAssign(forInit);\n if (oldDoubleProto > -1) { refDestructuringErrors.doubleProto = oldDoubleProto; }\n return this.finishNode(node, \"AssignmentExpression\")\n } else {\n if (ownDestructuringErrors) { this.checkExpressionErrors(refDestructuringErrors, true); }\n }\n if (oldParenAssign > -1) { refDestructuringErrors.parenthesizedAssign = oldParenAssign; }\n if (oldTrailingComma > -1) { refDestructuringErrors.trailingComma = oldTrailingComma; }\n return left\n };\n\n // Parse a ternary conditional (`?:`) operator.\n\n pp$5.parseMaybeConditional = function(forInit, refDestructuringErrors) {\n var startPos = this.start, startLoc = this.startLoc;\n var expr = this.parseExprOps(forInit, refDestructuringErrors);\n if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }\n if (this.eat(types$1.question)) {\n var node = this.startNodeAt(startPos, startLoc);\n node.test = expr;\n node.consequent = this.parseMaybeAssign();\n this.expect(types$1.colon);\n node.alternate = this.parseMaybeAssign(forInit);\n return this.finishNode(node, \"ConditionalExpression\")\n }\n return expr\n };\n\n // Start the precedence parser.\n\n pp$5.parseExprOps = function(forInit, refDestructuringErrors) {\n var startPos = this.start, startLoc = this.startLoc;\n var expr = this.parseMaybeUnary(refDestructuringErrors, false, false, forInit);\n if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }\n return expr.start === startPos && expr.type === \"ArrowFunctionExpression\" ? expr : this.parseExprOp(expr, startPos, startLoc, -1, forInit)\n };\n\n // Parse binary operators with the operator precedence parsing\n // algorithm. `left` is the left-hand side of the operator.\n // `minPrec` provides context that allows the function to stop and\n // defer further parser to one of its callers when it encounters an\n // operator that has a lower precedence than the set it is parsing.\n\n pp$5.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, forInit) {\n var prec = this.type.binop;\n if (prec != null && (!forInit || this.type !== types$1._in)) {\n if (prec > minPrec) {\n var logical = this.type === types$1.logicalOR || this.type === types$1.logicalAND;\n var coalesce = this.type === types$1.coalesce;\n if (coalesce) {\n // Handle the precedence of `tt.coalesce` as equal to the range of logical expressions.\n // In other words, `node.right` shouldn't contain logical expressions in order to check the mixed error.\n prec = types$1.logicalAND.binop;\n }\n var op = this.value;\n this.next();\n var startPos = this.start, startLoc = this.startLoc;\n var right = this.parseExprOp(this.parseMaybeUnary(null, false, false, forInit), startPos, startLoc, prec, forInit);\n var node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical || coalesce);\n if ((logical && this.type === types$1.coalesce) || (coalesce && (this.type === types$1.logicalOR || this.type === types$1.logicalAND))) {\n this.raiseRecoverable(this.start, \"Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses\");\n }\n return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, forInit)\n }\n }\n return left\n };\n\n pp$5.buildBinary = function(startPos, startLoc, left, right, op, logical) {\n if (right.type === \"PrivateIdentifier\") { this.raise(right.start, \"Private identifier can only be left side of binary expression\"); }\n var node = this.startNodeAt(startPos, startLoc);\n node.left = left;\n node.operator = op;\n node.right = right;\n return this.finishNode(node, logical ? \"LogicalExpression\" : \"BinaryExpression\")\n };\n\n // Parse unary operators, both prefix and postfix.\n\n pp$5.parseMaybeUnary = function(refDestructuringErrors, sawUnary, incDec, forInit) {\n var startPos = this.start, startLoc = this.startLoc, expr;\n if (this.isContextual(\"await\") && this.canAwait) {\n expr = this.parseAwait(forInit);\n sawUnary = true;\n } else if (this.type.prefix) {\n var node = this.startNode(), update = this.type === types$1.incDec;\n node.operator = this.value;\n node.prefix = true;\n this.next();\n node.argument = this.parseMaybeUnary(null, true, update, forInit);\n this.checkExpressionErrors(refDestructuringErrors, true);\n if (update) { this.checkLValSimple(node.argument); }\n else if (this.strict && node.operator === \"delete\" && isLocalVariableAccess(node.argument))\n { this.raiseRecoverable(node.start, \"Deleting local variable in strict mode\"); }\n else if (node.operator === \"delete\" && isPrivateFieldAccess(node.argument))\n { this.raiseRecoverable(node.start, \"Private fields can not be deleted\"); }\n else { sawUnary = true; }\n expr = this.finishNode(node, update ? \"UpdateExpression\" : \"UnaryExpression\");\n } else if (!sawUnary && this.type === types$1.privateId) {\n if ((forInit || this.privateNameStack.length === 0) && this.options.checkPrivateFields) { this.unexpected(); }\n expr = this.parsePrivateIdent();\n // only could be private fields in 'in', such as #x in obj\n if (this.type !== types$1._in) { this.unexpected(); }\n } else {\n expr = this.parseExprSubscripts(refDestructuringErrors, forInit);\n if (this.checkExpressionErrors(refDestructuringErrors)) { return expr }\n while (this.type.postfix && !this.canInsertSemicolon()) {\n var node$1 = this.startNodeAt(startPos, startLoc);\n node$1.operator = this.value;\n node$1.prefix = false;\n node$1.argument = expr;\n this.checkLValSimple(expr);\n this.next();\n expr = this.finishNode(node$1, \"UpdateExpression\");\n }\n }\n\n if (!incDec && this.eat(types$1.starstar)) {\n if (sawUnary)\n { this.unexpected(this.lastTokStart); }\n else\n { return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(null, false, false, forInit), \"**\", false) }\n } else {\n return expr\n }\n };\n\n function isLocalVariableAccess(node) {\n return (\n node.type === \"Identifier\" ||\n node.type === \"ParenthesizedExpression\" && isLocalVariableAccess(node.expression)\n )\n }\n\n function isPrivateFieldAccess(node) {\n return (\n node.type === \"MemberExpression\" && node.property.type === \"PrivateIdentifier\" ||\n node.type === \"ChainExpression\" && isPrivateFieldAccess(node.expression) ||\n node.type === \"ParenthesizedExpression\" && isPrivateFieldAccess(node.expression)\n )\n }\n\n // Parse call, dot, and `[]`-subscript expressions.\n\n pp$5.parseExprSubscripts = function(refDestructuringErrors, forInit) {\n var startPos = this.start, startLoc = this.startLoc;\n var expr = this.parseExprAtom(refDestructuringErrors, forInit);\n if (expr.type === \"ArrowFunctionExpression\" && this.input.slice(this.lastTokStart, this.lastTokEnd) !== \")\")\n { return expr }\n var result = this.parseSubscripts(expr, startPos, startLoc, false, forInit);\n if (refDestructuringErrors && result.type === \"MemberExpression\") {\n if (refDestructuringErrors.parenthesizedAssign >= result.start) { refDestructuringErrors.parenthesizedAssign = -1; }\n if (refDestructuringErrors.parenthesizedBind >= result.start) { refDestructuringErrors.parenthesizedBind = -1; }\n if (refDestructuringErrors.trailingComma >= result.start) { refDestructuringErrors.trailingComma = -1; }\n }\n return result\n };\n\n pp$5.parseSubscripts = function(base, startPos, startLoc, noCalls, forInit) {\n var maybeAsyncArrow = this.options.ecmaVersion >= 8 && base.type === \"Identifier\" && base.name === \"async\" &&\n this.lastTokEnd === base.end && !this.canInsertSemicolon() && base.end - base.start === 5 &&\n this.potentialArrowAt === base.start;\n var optionalChained = false;\n\n while (true) {\n var element = this.parseSubscript(base, startPos, startLoc, noCalls, maybeAsyncArrow, optionalChained, forInit);\n\n if (element.optional) { optionalChained = true; }\n if (element === base || element.type === \"ArrowFunctionExpression\") {\n if (optionalChained) {\n var chainNode = this.startNodeAt(startPos, startLoc);\n chainNode.expression = element;\n element = this.finishNode(chainNode, \"ChainExpression\");\n }\n return element\n }\n\n base = element;\n }\n };\n\n pp$5.shouldParseAsyncArrow = function() {\n return !this.canInsertSemicolon() && this.eat(types$1.arrow)\n };\n\n pp$5.parseSubscriptAsyncArrow = function(startPos, startLoc, exprList, forInit) {\n return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, true, forInit)\n };\n\n pp$5.parseSubscript = function(base, startPos, startLoc, noCalls, maybeAsyncArrow, optionalChained, forInit) {\n var optionalSupported = this.options.ecmaVersion >= 11;\n var optional = optionalSupported && this.eat(types$1.questionDot);\n if (noCalls && optional) { this.raise(this.lastTokStart, \"Optional chaining cannot appear in the callee of new expressions\"); }\n\n var computed = this.eat(types$1.bracketL);\n if (computed || (optional && this.type !== types$1.parenL && this.type !== types$1.backQuote) || this.eat(types$1.dot)) {\n var node = this.startNodeAt(startPos, startLoc);\n node.object = base;\n if (computed) {\n node.property = this.parseExpression();\n this.expect(types$1.bracketR);\n } else if (this.type === types$1.privateId && base.type !== \"Super\") {\n node.property = this.parsePrivateIdent();\n } else {\n node.property = this.parseIdent(this.options.allowReserved !== \"never\");\n }\n node.computed = !!computed;\n if (optionalSupported) {\n node.optional = optional;\n }\n base = this.finishNode(node, \"MemberExpression\");\n } else if (!noCalls && this.eat(types$1.parenL)) {\n var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n this.yieldPos = 0;\n this.awaitPos = 0;\n this.awaitIdentPos = 0;\n var exprList = this.parseExprList(types$1.parenR, this.options.ecmaVersion >= 8, false, refDestructuringErrors);\n if (maybeAsyncArrow && !optional && this.shouldParseAsyncArrow()) {\n this.checkPatternErrors(refDestructuringErrors, false);\n this.checkYieldAwaitInDefaultParams();\n if (this.awaitIdentPos > 0)\n { this.raise(this.awaitIdentPos, \"Cannot use 'await' as identifier inside an async function\"); }\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n this.awaitIdentPos = oldAwaitIdentPos;\n return this.parseSubscriptAsyncArrow(startPos, startLoc, exprList, forInit)\n }\n this.checkExpressionErrors(refDestructuringErrors, true);\n this.yieldPos = oldYieldPos || this.yieldPos;\n this.awaitPos = oldAwaitPos || this.awaitPos;\n this.awaitIdentPos = oldAwaitIdentPos || this.awaitIdentPos;\n var node$1 = this.startNodeAt(startPos, startLoc);\n node$1.callee = base;\n node$1.arguments = exprList;\n if (optionalSupported) {\n node$1.optional = optional;\n }\n base = this.finishNode(node$1, \"CallExpression\");\n } else if (this.type === types$1.backQuote) {\n if (optional || optionalChained) {\n this.raise(this.start, \"Optional chaining cannot appear in the tag of tagged template expressions\");\n }\n var node$2 = this.startNodeAt(startPos, startLoc);\n node$2.tag = base;\n node$2.quasi = this.parseTemplate({isTagged: true});\n base = this.finishNode(node$2, \"TaggedTemplateExpression\");\n }\n return base\n };\n\n // Parse an atomic expression \u2014 either a single token that is an\n // expression, an expression started by a keyword like `function` or\n // `new`, or an expression wrapped in punctuation like `()`, `[]`,\n // or `{}`.\n\n pp$5.parseExprAtom = function(refDestructuringErrors, forInit, forNew) {\n // If a division operator appears in an expression position, the\n // tokenizer got confused, and we force it to read a regexp instead.\n if (this.type === types$1.slash) { this.readRegexp(); }\n\n var node, canBeArrow = this.potentialArrowAt === this.start;\n switch (this.type) {\n case types$1._super:\n if (!this.allowSuper)\n { this.raise(this.start, \"'super' keyword outside a method\"); }\n node = this.startNode();\n this.next();\n if (this.type === types$1.parenL && !this.allowDirectSuper)\n { this.raise(node.start, \"super() call outside constructor of a subclass\"); }\n // The `super` keyword can appear at below:\n // SuperProperty:\n // super [ Expression ]\n // super . IdentifierName\n // SuperCall:\n // super ( Arguments )\n if (this.type !== types$1.dot && this.type !== types$1.bracketL && this.type !== types$1.parenL)\n { this.unexpected(); }\n return this.finishNode(node, \"Super\")\n\n case types$1._this:\n node = this.startNode();\n this.next();\n return this.finishNode(node, \"ThisExpression\")\n\n case types$1.name:\n var startPos = this.start, startLoc = this.startLoc, containsEsc = this.containsEsc;\n var id = this.parseIdent(false);\n if (this.options.ecmaVersion >= 8 && !containsEsc && id.name === \"async\" && !this.canInsertSemicolon() && this.eat(types$1._function)) {\n this.overrideContext(types.f_expr);\n return this.parseFunction(this.startNodeAt(startPos, startLoc), 0, false, true, forInit)\n }\n if (canBeArrow && !this.canInsertSemicolon()) {\n if (this.eat(types$1.arrow))\n { return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], false, forInit) }\n if (this.options.ecmaVersion >= 8 && id.name === \"async\" && this.type === types$1.name && !containsEsc &&\n (!this.potentialArrowInForAwait || this.value !== \"of\" || this.containsEsc)) {\n id = this.parseIdent(false);\n if (this.canInsertSemicolon() || !this.eat(types$1.arrow))\n { this.unexpected(); }\n return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], true, forInit)\n }\n }\n return id\n\n case types$1.regexp:\n var value = this.value;\n node = this.parseLiteral(value.value);\n node.regex = {pattern: value.pattern, flags: value.flags};\n return node\n\n case types$1.num: case types$1.string:\n return this.parseLiteral(this.value)\n\n case types$1._null: case types$1._true: case types$1._false:\n node = this.startNode();\n node.value = this.type === types$1._null ? null : this.type === types$1._true;\n node.raw = this.type.keyword;\n this.next();\n return this.finishNode(node, \"Literal\")\n\n case types$1.parenL:\n var start = this.start, expr = this.parseParenAndDistinguishExpression(canBeArrow, forInit);\n if (refDestructuringErrors) {\n if (refDestructuringErrors.parenthesizedAssign < 0 && !this.isSimpleAssignTarget(expr))\n { refDestructuringErrors.parenthesizedAssign = start; }\n if (refDestructuringErrors.parenthesizedBind < 0)\n { refDestructuringErrors.parenthesizedBind = start; }\n }\n return expr\n\n case types$1.bracketL:\n node = this.startNode();\n this.next();\n node.elements = this.parseExprList(types$1.bracketR, true, true, refDestructuringErrors);\n return this.finishNode(node, \"ArrayExpression\")\n\n case types$1.braceL:\n this.overrideContext(types.b_expr);\n return this.parseObj(false, refDestructuringErrors)\n\n case types$1._function:\n node = this.startNode();\n this.next();\n return this.parseFunction(node, 0)\n\n case types$1._class:\n return this.parseClass(this.startNode(), false)\n\n case types$1._new:\n return this.parseNew()\n\n case types$1.backQuote:\n return this.parseTemplate()\n\n case types$1._import:\n if (this.options.ecmaVersion >= 11) {\n return this.parseExprImport(forNew)\n } else {\n return this.unexpected()\n }\n\n default:\n return this.parseExprAtomDefault()\n }\n };\n\n pp$5.parseExprAtomDefault = function() {\n this.unexpected();\n };\n\n pp$5.parseExprImport = function(forNew) {\n var node = this.startNode();\n\n // Consume `import` as an identifier for `import.meta`.\n // Because `this.parseIdent(true)` doesn't check escape sequences, it needs the check of `this.containsEsc`.\n if (this.containsEsc) { this.raiseRecoverable(this.start, \"Escape sequence in keyword import\"); }\n this.next();\n\n if (this.type === types$1.parenL && !forNew) {\n return this.parseDynamicImport(node)\n } else if (this.type === types$1.dot) {\n var meta = this.startNodeAt(node.start, node.loc && node.loc.start);\n meta.name = \"import\";\n node.meta = this.finishNode(meta, \"Identifier\");\n return this.parseImportMeta(node)\n } else {\n this.unexpected();\n }\n };\n\n pp$5.parseDynamicImport = function(node) {\n this.next(); // skip `(`\n\n // Parse node.source.\n node.source = this.parseMaybeAssign();\n\n if (this.options.ecmaVersion >= 16) {\n if (!this.eat(types$1.parenR)) {\n this.expect(types$1.comma);\n if (!this.afterTrailingComma(types$1.parenR)) {\n node.options = this.parseMaybeAssign();\n if (!this.eat(types$1.parenR)) {\n this.expect(types$1.comma);\n if (!this.afterTrailingComma(types$1.parenR)) {\n this.unexpected();\n }\n }\n } else {\n node.options = null;\n }\n } else {\n node.options = null;\n }\n } else {\n // Verify ending.\n if (!this.eat(types$1.parenR)) {\n var errorPos = this.start;\n if (this.eat(types$1.comma) && this.eat(types$1.parenR)) {\n this.raiseRecoverable(errorPos, \"Trailing comma is not allowed in import()\");\n } else {\n this.unexpected(errorPos);\n }\n }\n }\n\n return this.finishNode(node, \"ImportExpression\")\n };\n\n pp$5.parseImportMeta = function(node) {\n this.next(); // skip `.`\n\n var containsEsc = this.containsEsc;\n node.property = this.parseIdent(true);\n\n if (node.property.name !== \"meta\")\n { this.raiseRecoverable(node.property.start, \"The only valid meta property for import is 'import.meta'\"); }\n if (containsEsc)\n { this.raiseRecoverable(node.start, \"'import.meta' must not contain escaped characters\"); }\n if (this.options.sourceType !== \"module\" && !this.options.allowImportExportEverywhere)\n { this.raiseRecoverable(node.start, \"Cannot use 'import.meta' outside a module\"); }\n\n return this.finishNode(node, \"MetaProperty\")\n };\n\n pp$5.parseLiteral = function(value) {\n var node = this.startNode();\n node.value = value;\n node.raw = this.input.slice(this.start, this.end);\n if (node.raw.charCodeAt(node.raw.length - 1) === 110)\n { node.bigint = node.value != null ? node.value.toString() : node.raw.slice(0, -1).replace(/_/g, \"\"); }\n this.next();\n return this.finishNode(node, \"Literal\")\n };\n\n pp$5.parseParenExpression = function() {\n this.expect(types$1.parenL);\n var val = this.parseExpression();\n this.expect(types$1.parenR);\n return val\n };\n\n pp$5.shouldParseArrow = function(exprList) {\n return !this.canInsertSemicolon()\n };\n\n pp$5.parseParenAndDistinguishExpression = function(canBeArrow, forInit) {\n var startPos = this.start, startLoc = this.startLoc, val, allowTrailingComma = this.options.ecmaVersion >= 8;\n if (this.options.ecmaVersion >= 6) {\n this.next();\n\n var innerStartPos = this.start, innerStartLoc = this.startLoc;\n var exprList = [], first = true, lastIsComma = false;\n var refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, spreadStart;\n this.yieldPos = 0;\n this.awaitPos = 0;\n // Do not save awaitIdentPos to allow checking awaits nested in parameters\n while (this.type !== types$1.parenR) {\n first ? first = false : this.expect(types$1.comma);\n if (allowTrailingComma && this.afterTrailingComma(types$1.parenR, true)) {\n lastIsComma = true;\n break\n } else if (this.type === types$1.ellipsis) {\n spreadStart = this.start;\n exprList.push(this.parseParenItem(this.parseRestBinding()));\n if (this.type === types$1.comma) {\n this.raiseRecoverable(\n this.start,\n \"Comma is not permitted after the rest element\"\n );\n }\n break\n } else {\n exprList.push(this.parseMaybeAssign(false, refDestructuringErrors, this.parseParenItem));\n }\n }\n var innerEndPos = this.lastTokEnd, innerEndLoc = this.lastTokEndLoc;\n this.expect(types$1.parenR);\n\n if (canBeArrow && this.shouldParseArrow(exprList) && this.eat(types$1.arrow)) {\n this.checkPatternErrors(refDestructuringErrors, false);\n this.checkYieldAwaitInDefaultParams();\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n return this.parseParenArrowList(startPos, startLoc, exprList, forInit)\n }\n\n if (!exprList.length || lastIsComma) { this.unexpected(this.lastTokStart); }\n if (spreadStart) { this.unexpected(spreadStart); }\n this.checkExpressionErrors(refDestructuringErrors, true);\n this.yieldPos = oldYieldPos || this.yieldPos;\n this.awaitPos = oldAwaitPos || this.awaitPos;\n\n if (exprList.length > 1) {\n val = this.startNodeAt(innerStartPos, innerStartLoc);\n val.expressions = exprList;\n this.finishNodeAt(val, \"SequenceExpression\", innerEndPos, innerEndLoc);\n } else {\n val = exprList[0];\n }\n } else {\n val = this.parseParenExpression();\n }\n\n if (this.options.preserveParens) {\n var par = this.startNodeAt(startPos, startLoc);\n par.expression = val;\n return this.finishNode(par, \"ParenthesizedExpression\")\n } else {\n return val\n }\n };\n\n pp$5.parseParenItem = function(item) {\n return item\n };\n\n pp$5.parseParenArrowList = function(startPos, startLoc, exprList, forInit) {\n return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, false, forInit)\n };\n\n // New's precedence is slightly tricky. It must allow its argument to\n // be a `[]` or dot subscript expression, but not a call \u2014 at least,\n // not without wrapping it in parentheses. Thus, it uses the noCalls\n // argument to parseSubscripts to prevent it from consuming the\n // argument list.\n\n var empty = [];\n\n pp$5.parseNew = function() {\n if (this.containsEsc) { this.raiseRecoverable(this.start, \"Escape sequence in keyword new\"); }\n var node = this.startNode();\n this.next();\n if (this.options.ecmaVersion >= 6 && this.type === types$1.dot) {\n var meta = this.startNodeAt(node.start, node.loc && node.loc.start);\n meta.name = \"new\";\n node.meta = this.finishNode(meta, \"Identifier\");\n this.next();\n var containsEsc = this.containsEsc;\n node.property = this.parseIdent(true);\n if (node.property.name !== \"target\")\n { this.raiseRecoverable(node.property.start, \"The only valid meta property for new is 'new.target'\"); }\n if (containsEsc)\n { this.raiseRecoverable(node.start, \"'new.target' must not contain escaped characters\"); }\n if (!this.allowNewDotTarget)\n { this.raiseRecoverable(node.start, \"'new.target' can only be used in functions and class static block\"); }\n return this.finishNode(node, \"MetaProperty\")\n }\n var startPos = this.start, startLoc = this.startLoc;\n node.callee = this.parseSubscripts(this.parseExprAtom(null, false, true), startPos, startLoc, true, false);\n if (this.eat(types$1.parenL)) { node.arguments = this.parseExprList(types$1.parenR, this.options.ecmaVersion >= 8, false); }\n else { node.arguments = empty; }\n return this.finishNode(node, \"NewExpression\")\n };\n\n // Parse template expression.\n\n pp$5.parseTemplateElement = function(ref) {\n var isTagged = ref.isTagged;\n\n var elem = this.startNode();\n if (this.type === types$1.invalidTemplate) {\n if (!isTagged) {\n this.raiseRecoverable(this.start, \"Bad escape sequence in untagged template literal\");\n }\n elem.value = {\n raw: this.value.replace(/\\r\\n?/g, \"\\n\"),\n cooked: null\n };\n } else {\n elem.value = {\n raw: this.input.slice(this.start, this.end).replace(/\\r\\n?/g, \"\\n\"),\n cooked: this.value\n };\n }\n this.next();\n elem.tail = this.type === types$1.backQuote;\n return this.finishNode(elem, \"TemplateElement\")\n };\n\n pp$5.parseTemplate = function(ref) {\n if ( ref === void 0 ) ref = {};\n var isTagged = ref.isTagged; if ( isTagged === void 0 ) isTagged = false;\n\n var node = this.startNode();\n this.next();\n node.expressions = [];\n var curElt = this.parseTemplateElement({isTagged: isTagged});\n node.quasis = [curElt];\n while (!curElt.tail) {\n if (this.type === types$1.eof) { this.raise(this.pos, \"Unterminated template literal\"); }\n this.expect(types$1.dollarBraceL);\n node.expressions.push(this.parseExpression());\n this.expect(types$1.braceR);\n node.quasis.push(curElt = this.parseTemplateElement({isTagged: isTagged}));\n }\n this.next();\n return this.finishNode(node, \"TemplateLiteral\")\n };\n\n pp$5.isAsyncProp = function(prop) {\n return !prop.computed && prop.key.type === \"Identifier\" && prop.key.name === \"async\" &&\n (this.type === types$1.name || this.type === types$1.num || this.type === types$1.string || this.type === types$1.bracketL || this.type.keyword || (this.options.ecmaVersion >= 9 && this.type === types$1.star)) &&\n !lineBreak.test(this.input.slice(this.lastTokEnd, this.start))\n };\n\n // Parse an object literal or binding pattern.\n\n pp$5.parseObj = function(isPattern, refDestructuringErrors) {\n var node = this.startNode(), first = true, propHash = {};\n node.properties = [];\n this.next();\n while (!this.eat(types$1.braceR)) {\n if (!first) {\n this.expect(types$1.comma);\n if (this.options.ecmaVersion >= 5 && this.afterTrailingComma(types$1.braceR)) { break }\n } else { first = false; }\n\n var prop = this.parseProperty(isPattern, refDestructuringErrors);\n if (!isPattern) { this.checkPropClash(prop, propHash, refDestructuringErrors); }\n node.properties.push(prop);\n }\n return this.finishNode(node, isPattern ? \"ObjectPattern\" : \"ObjectExpression\")\n };\n\n pp$5.parseProperty = function(isPattern, refDestructuringErrors) {\n var prop = this.startNode(), isGenerator, isAsync, startPos, startLoc;\n if (this.options.ecmaVersion >= 9 && this.eat(types$1.ellipsis)) {\n if (isPattern) {\n prop.argument = this.parseIdent(false);\n if (this.type === types$1.comma) {\n this.raiseRecoverable(this.start, \"Comma is not permitted after the rest element\");\n }\n return this.finishNode(prop, \"RestElement\")\n }\n // Parse argument.\n prop.argument = this.parseMaybeAssign(false, refDestructuringErrors);\n // To disallow trailing comma via `this.toAssignable()`.\n if (this.type === types$1.comma && refDestructuringErrors && refDestructuringErrors.trailingComma < 0) {\n refDestructuringErrors.trailingComma = this.start;\n }\n // Finish\n return this.finishNode(prop, \"SpreadElement\")\n }\n if (this.options.ecmaVersion >= 6) {\n prop.method = false;\n prop.shorthand = false;\n if (isPattern || refDestructuringErrors) {\n startPos = this.start;\n startLoc = this.startLoc;\n }\n if (!isPattern)\n { isGenerator = this.eat(types$1.star); }\n }\n var containsEsc = this.containsEsc;\n this.parsePropertyName(prop);\n if (!isPattern && !containsEsc && this.options.ecmaVersion >= 8 && !isGenerator && this.isAsyncProp(prop)) {\n isAsync = true;\n isGenerator = this.options.ecmaVersion >= 9 && this.eat(types$1.star);\n this.parsePropertyName(prop);\n } else {\n isAsync = false;\n }\n this.parsePropertyValue(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors, containsEsc);\n return this.finishNode(prop, \"Property\")\n };\n\n pp$5.parseGetterSetter = function(prop) {\n var kind = prop.key.name;\n this.parsePropertyName(prop);\n prop.value = this.parseMethod(false);\n prop.kind = kind;\n var paramCount = prop.kind === \"get\" ? 0 : 1;\n if (prop.value.params.length !== paramCount) {\n var start = prop.value.start;\n if (prop.kind === \"get\")\n { this.raiseRecoverable(start, \"getter should have no params\"); }\n else\n { this.raiseRecoverable(start, \"setter should have exactly one param\"); }\n } else {\n if (prop.kind === \"set\" && prop.value.params[0].type === \"RestElement\")\n { this.raiseRecoverable(prop.value.params[0].start, \"Setter cannot use rest params\"); }\n }\n };\n\n pp$5.parsePropertyValue = function(prop, isPattern, isGenerator, isAsync, startPos, startLoc, refDestructuringErrors, containsEsc) {\n if ((isGenerator || isAsync) && this.type === types$1.colon)\n { this.unexpected(); }\n\n if (this.eat(types$1.colon)) {\n prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refDestructuringErrors);\n prop.kind = \"init\";\n } else if (this.options.ecmaVersion >= 6 && this.type === types$1.parenL) {\n if (isPattern) { this.unexpected(); }\n prop.method = true;\n prop.value = this.parseMethod(isGenerator, isAsync);\n prop.kind = \"init\";\n } else if (!isPattern && !containsEsc &&\n this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === \"Identifier\" &&\n (prop.key.name === \"get\" || prop.key.name === \"set\") &&\n (this.type !== types$1.comma && this.type !== types$1.braceR && this.type !== types$1.eq)) {\n if (isGenerator || isAsync) { this.unexpected(); }\n this.parseGetterSetter(prop);\n } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === \"Identifier\") {\n if (isGenerator || isAsync) { this.unexpected(); }\n this.checkUnreserved(prop.key);\n if (prop.key.name === \"await\" && !this.awaitIdentPos)\n { this.awaitIdentPos = startPos; }\n if (isPattern) {\n prop.value = this.parseMaybeDefault(startPos, startLoc, this.copyNode(prop.key));\n } else if (this.type === types$1.eq && refDestructuringErrors) {\n if (refDestructuringErrors.shorthandAssign < 0)\n { refDestructuringErrors.shorthandAssign = this.start; }\n prop.value = this.parseMaybeDefault(startPos, startLoc, this.copyNode(prop.key));\n } else {\n prop.value = this.copyNode(prop.key);\n }\n prop.kind = \"init\";\n prop.shorthand = true;\n } else { this.unexpected(); }\n };\n\n pp$5.parsePropertyName = function(prop) {\n if (this.options.ecmaVersion >= 6) {\n if (this.eat(types$1.bracketL)) {\n prop.computed = true;\n prop.key = this.parseMaybeAssign();\n this.expect(types$1.bracketR);\n return prop.key\n } else {\n prop.computed = false;\n }\n }\n return prop.key = this.type === types$1.num || this.type === types$1.string ? this.parseExprAtom() : this.parseIdent(this.options.allowReserved !== \"never\")\n };\n\n // Initialize empty function node.\n\n pp$5.initFunction = function(node) {\n node.id = null;\n if (this.options.ecmaVersion >= 6) { node.generator = node.expression = false; }\n if (this.options.ecmaVersion >= 8) { node.async = false; }\n };\n\n // Parse object or class method.\n\n pp$5.parseMethod = function(isGenerator, isAsync, allowDirectSuper) {\n var node = this.startNode(), oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n\n this.initFunction(node);\n if (this.options.ecmaVersion >= 6)\n { node.generator = isGenerator; }\n if (this.options.ecmaVersion >= 8)\n { node.async = !!isAsync; }\n\n this.yieldPos = 0;\n this.awaitPos = 0;\n this.awaitIdentPos = 0;\n this.enterScope(functionFlags(isAsync, node.generator) | SCOPE_SUPER | (allowDirectSuper ? SCOPE_DIRECT_SUPER : 0));\n\n this.expect(types$1.parenL);\n node.params = this.parseBindingList(types$1.parenR, false, this.options.ecmaVersion >= 8);\n this.checkYieldAwaitInDefaultParams();\n this.parseFunctionBody(node, false, true, false);\n\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n this.awaitIdentPos = oldAwaitIdentPos;\n return this.finishNode(node, \"FunctionExpression\")\n };\n\n // Parse arrow function expression with given parameters.\n\n pp$5.parseArrowExpression = function(node, params, isAsync, forInit) {\n var oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos;\n\n this.enterScope(functionFlags(isAsync, false) | SCOPE_ARROW);\n this.initFunction(node);\n if (this.options.ecmaVersion >= 8) { node.async = !!isAsync; }\n\n this.yieldPos = 0;\n this.awaitPos = 0;\n this.awaitIdentPos = 0;\n\n node.params = this.toAssignableList(params, true);\n this.parseFunctionBody(node, true, false, forInit);\n\n this.yieldPos = oldYieldPos;\n this.awaitPos = oldAwaitPos;\n this.awaitIdentPos = oldAwaitIdentPos;\n return this.finishNode(node, \"ArrowFunctionExpression\")\n };\n\n // Parse function body and check parameters.\n\n pp$5.parseFunctionBody = function(node, isArrowFunction, isMethod, forInit) {\n var isExpression = isArrowFunction && this.type !== types$1.braceL;\n var oldStrict = this.strict, useStrict = false;\n\n if (isExpression) {\n node.body = this.parseMaybeAssign(forInit);\n node.expression = true;\n this.checkParams(node, false);\n } else {\n var nonSimple = this.options.ecmaVersion >= 7 && !this.isSimpleParamList(node.params);\n if (!oldStrict || nonSimple) {\n useStrict = this.strictDirective(this.end);\n // If this is a strict mode function, verify that argument names\n // are not repeated, and it does not try to bind the words `eval`\n // or `arguments`.\n if (useStrict && nonSimple)\n { this.raiseRecoverable(node.start, \"Illegal 'use strict' directive in function with non-simple parameter list\"); }\n }\n // Start a new scope with regard to labels and the `inFunction`\n // flag (restore them to their old value afterwards).\n var oldLabels = this.labels;\n this.labels = [];\n if (useStrict) { this.strict = true; }\n\n // Add the params to varDeclaredNames to ensure that an error is thrown\n // if a let/const declaration in the function clashes with one of the params.\n this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && !isMethod && this.isSimpleParamList(node.params));\n // Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval'\n if (this.strict && node.id) { this.checkLValSimple(node.id, BIND_OUTSIDE); }\n node.body = this.parseBlock(false, undefined, useStrict && !oldStrict);\n node.expression = false;\n this.adaptDirectivePrologue(node.body.body);\n this.labels = oldLabels;\n }\n this.exitScope();\n };\n\n pp$5.isSimpleParamList = function(params) {\n for (var i = 0, list = params; i < list.length; i += 1)\n {\n var param = list[i];\n\n if (param.type !== \"Identifier\") { return false\n } }\n return true\n };\n\n // Checks function params for various disallowed patterns such as using \"eval\"\n // or \"arguments\" and duplicate parameters.\n\n pp$5.checkParams = function(node, allowDuplicates) {\n var nameHash = Object.create(null);\n for (var i = 0, list = node.params; i < list.length; i += 1)\n {\n var param = list[i];\n\n this.checkLValInnerPattern(param, BIND_VAR, allowDuplicates ? null : nameHash);\n }\n };\n\n // Parses a comma-separated list of expressions, and returns them as\n // an array. `close` is the token type that ends the list, and\n // `allowEmpty` can be turned on to allow subsequent commas with\n // nothing in between them to be parsed as `null` (which is needed\n // for array literals).\n\n pp$5.parseExprList = function(close, allowTrailingComma, allowEmpty, refDestructuringErrors) {\n var elts = [], first = true;\n while (!this.eat(close)) {\n if (!first) {\n this.expect(types$1.comma);\n if (allowTrailingComma && this.afterTrailingComma(close)) { break }\n } else { first = false; }\n\n var elt = (void 0);\n if (allowEmpty && this.type === types$1.comma)\n { elt = null; }\n else if (this.type === types$1.ellipsis) {\n elt = this.parseSpread(refDestructuringErrors);\n if (refDestructuringErrors && this.type === types$1.comma && refDestructuringErrors.trailingComma < 0)\n { refDestructuringErrors.trailingComma = this.start; }\n } else {\n elt = this.parseMaybeAssign(false, refDestructuringErrors);\n }\n elts.push(elt);\n }\n return elts\n };\n\n pp$5.checkUnreserved = function(ref) {\n var start = ref.start;\n var end = ref.end;\n var name = ref.name;\n\n if (this.inGenerator && name === \"yield\")\n { this.raiseRecoverable(start, \"Cannot use 'yield' as identifier inside a generator\"); }\n if (this.inAsync && name === \"await\")\n { this.raiseRecoverable(start, \"Cannot use 'await' as identifier inside an async function\"); }\n if (!(this.currentThisScope().flags & SCOPE_VAR) && name === \"arguments\")\n { this.raiseRecoverable(start, \"Cannot use 'arguments' in class field initializer\"); }\n if (this.inClassStaticBlock && (name === \"arguments\" || name === \"await\"))\n { this.raise(start, (\"Cannot use \" + name + \" in class static initialization block\")); }\n if (this.keywords.test(name))\n { this.raise(start, (\"Unexpected keyword '\" + name + \"'\")); }\n if (this.options.ecmaVersion < 6 &&\n this.input.slice(start, end).indexOf(\"\\\\\") !== -1) { return }\n var re = this.strict ? this.reservedWordsStrict : this.reservedWords;\n if (re.test(name)) {\n if (!this.inAsync && name === \"await\")\n { this.raiseRecoverable(start, \"Cannot use keyword 'await' outside an async function\"); }\n this.raiseRecoverable(start, (\"The keyword '\" + name + \"' is reserved\"));\n }\n };\n\n // Parse the next token as an identifier. If `liberal` is true (used\n // when parsing properties), it will also convert keywords into\n // identifiers.\n\n pp$5.parseIdent = function(liberal) {\n var node = this.parseIdentNode();\n this.next(!!liberal);\n this.finishNode(node, \"Identifier\");\n if (!liberal) {\n this.checkUnreserved(node);\n if (node.name === \"await\" && !this.awaitIdentPos)\n { this.awaitIdentPos = node.start; }\n }\n return node\n };\n\n pp$5.parseIdentNode = function() {\n var node = this.startNode();\n if (this.type === types$1.name) {\n node.name = this.value;\n } else if (this.type.keyword) {\n node.name = this.type.keyword;\n\n // To fix https://github.com/acornjs/acorn/issues/575\n // `class` and `function` keywords push new context into this.context.\n // But there is no chance to pop the context if the keyword is consumed as an identifier such as a property name.\n // If the previous token is a dot, this does not apply because the context-managing code already ignored the keyword\n if ((node.name === \"class\" || node.name === \"function\") &&\n (this.lastTokEnd !== this.lastTokStart + 1 || this.input.charCodeAt(this.lastTokStart) !== 46)) {\n this.context.pop();\n }\n this.type = types$1.name;\n } else {\n this.unexpected();\n }\n return node\n };\n\n pp$5.parsePrivateIdent = function() {\n var node = this.startNode();\n if (this.type === types$1.privateId) {\n node.name = this.value;\n } else {\n this.unexpected();\n }\n this.next();\n this.finishNode(node, \"PrivateIdentifier\");\n\n // For validating existence\n if (this.options.checkPrivateFields) {\n if (this.privateNameStack.length === 0) {\n this.raise(node.start, (\"Private field '#\" + (node.name) + \"' must be declared in an enclosing class\"));\n } else {\n this.privateNameStack[this.privateNameStack.length - 1].used.push(node);\n }\n }\n\n return node\n };\n\n // Parses yield expression inside generator.\n\n pp$5.parseYield = function(forInit) {\n if (!this.yieldPos) { this.yieldPos = this.start; }\n\n var node = this.startNode();\n this.next();\n if (this.type === types$1.semi || this.canInsertSemicolon() || (this.type !== types$1.star && !this.type.startsExpr)) {\n node.delegate = false;\n node.argument = null;\n } else {\n node.delegate = this.eat(types$1.star);\n node.argument = this.parseMaybeAssign(forInit);\n }\n return this.finishNode(node, \"YieldExpression\")\n };\n\n pp$5.parseAwait = function(forInit) {\n if (!this.awaitPos) { this.awaitPos = this.start; }\n\n var node = this.startNode();\n this.next();\n node.argument = this.parseMaybeUnary(null, true, false, forInit);\n return this.finishNode(node, \"AwaitExpression\")\n };\n\n var pp$4 = Parser.prototype;\n\n // This function is used to raise exceptions on parse errors. It\n // takes an offset integer (into the current `input`) to indicate\n // the location of the error, attaches the position to the end\n // of the error message, and then raises a `SyntaxError` with that\n // message.\n\n pp$4.raise = function(pos, message) {\n var loc = getLineInfo(this.input, pos);\n message += \" (\" + loc.line + \":\" + loc.column + \")\";\n if (this.sourceFile) {\n message += \" in \" + this.sourceFile;\n }\n var err = new SyntaxError(message);\n err.pos = pos; err.loc = loc; err.raisedAt = this.pos;\n throw err\n };\n\n pp$4.raiseRecoverable = pp$4.raise;\n\n pp$4.curPosition = function() {\n if (this.options.locations) {\n return new Position(this.curLine, this.pos - this.lineStart)\n }\n };\n\n var pp$3 = Parser.prototype;\n\n var Scope = function Scope(flags) {\n this.flags = flags;\n // A list of var-declared names in the current lexical scope\n this.var = [];\n // A list of lexically-declared names in the current lexical scope\n this.lexical = [];\n // A list of lexically-declared FunctionDeclaration names in the current lexical scope\n this.functions = [];\n };\n\n // The functions in this module keep track of declared variables in the current scope in order to detect duplicate variable names.\n\n pp$3.enterScope = function(flags) {\n this.scopeStack.push(new Scope(flags));\n };\n\n pp$3.exitScope = function() {\n this.scopeStack.pop();\n };\n\n // The spec says:\n // > At the top level of a function, or script, function declarations are\n // > treated like var declarations rather than like lexical declarations.\n pp$3.treatFunctionsAsVarInScope = function(scope) {\n return (scope.flags & SCOPE_FUNCTION) || !this.inModule && (scope.flags & SCOPE_TOP)\n };\n\n pp$3.declareName = function(name, bindingType, pos) {\n var redeclared = false;\n if (bindingType === BIND_LEXICAL) {\n var scope = this.currentScope();\n redeclared = scope.lexical.indexOf(name) > -1 || scope.functions.indexOf(name) > -1 || scope.var.indexOf(name) > -1;\n scope.lexical.push(name);\n if (this.inModule && (scope.flags & SCOPE_TOP))\n { delete this.undefinedExports[name]; }\n } else if (bindingType === BIND_SIMPLE_CATCH) {\n var scope$1 = this.currentScope();\n scope$1.lexical.push(name);\n } else if (bindingType === BIND_FUNCTION) {\n var scope$2 = this.currentScope();\n if (this.treatFunctionsAsVar)\n { redeclared = scope$2.lexical.indexOf(name) > -1; }\n else\n { redeclared = scope$2.lexical.indexOf(name) > -1 || scope$2.var.indexOf(name) > -1; }\n scope$2.functions.push(name);\n } else {\n for (var i = this.scopeStack.length - 1; i >= 0; --i) {\n var scope$3 = this.scopeStack[i];\n if (scope$3.lexical.indexOf(name) > -1 && !((scope$3.flags & SCOPE_SIMPLE_CATCH) && scope$3.lexical[0] === name) ||\n !this.treatFunctionsAsVarInScope(scope$3) && scope$3.functions.indexOf(name) > -1) {\n redeclared = true;\n break\n }\n scope$3.var.push(name);\n if (this.inModule && (scope$3.flags & SCOPE_TOP))\n { delete this.undefinedExports[name]; }\n if (scope$3.flags & SCOPE_VAR) { break }\n }\n }\n if (redeclared) { this.raiseRecoverable(pos, (\"Identifier '\" + name + \"' has already been declared\")); }\n };\n\n pp$3.checkLocalExport = function(id) {\n // scope.functions must be empty as Module code is always strict.\n if (this.scopeStack[0].lexical.indexOf(id.name) === -1 &&\n this.scopeStack[0].var.indexOf(id.name) === -1) {\n this.undefinedExports[id.name] = id;\n }\n };\n\n pp$3.currentScope = function() {\n return this.scopeStack[this.scopeStack.length - 1]\n };\n\n pp$3.currentVarScope = function() {\n for (var i = this.scopeStack.length - 1;; i--) {\n var scope = this.scopeStack[i];\n if (scope.flags & (SCOPE_VAR | SCOPE_CLASS_FIELD_INIT | SCOPE_CLASS_STATIC_BLOCK)) { return scope }\n }\n };\n\n // Could be useful for `this`, `new.target`, `super()`, `super.property`, and `super[property]`.\n pp$3.currentThisScope = function() {\n for (var i = this.scopeStack.length - 1;; i--) {\n var scope = this.scopeStack[i];\n if (scope.flags & (SCOPE_VAR | SCOPE_CLASS_FIELD_INIT | SCOPE_CLASS_STATIC_BLOCK) &&\n !(scope.flags & SCOPE_ARROW)) { return scope }\n }\n };\n\n var Node = function Node(parser, pos, loc) {\n this.type = \"\";\n this.start = pos;\n this.end = 0;\n if (parser.options.locations)\n { this.loc = new SourceLocation(parser, loc); }\n if (parser.options.directSourceFile)\n { this.sourceFile = parser.options.directSourceFile; }\n if (parser.options.ranges)\n { this.range = [pos, 0]; }\n };\n\n // Start an AST node, attaching a start offset.\n\n var pp$2 = Parser.prototype;\n\n pp$2.startNode = function() {\n return new Node(this, this.start, this.startLoc)\n };\n\n pp$2.startNodeAt = function(pos, loc) {\n return new Node(this, pos, loc)\n };\n\n // Finish an AST node, adding `type` and `end` properties.\n\n function finishNodeAt(node, type, pos, loc) {\n node.type = type;\n node.end = pos;\n if (this.options.locations)\n { node.loc.end = loc; }\n if (this.options.ranges)\n { node.range[1] = pos; }\n return node\n }\n\n pp$2.finishNode = function(node, type) {\n return finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc)\n };\n\n // Finish node at given position\n\n pp$2.finishNodeAt = function(node, type, pos, loc) {\n return finishNodeAt.call(this, node, type, pos, loc)\n };\n\n pp$2.copyNode = function(node) {\n var newNode = new Node(this, node.start, this.startLoc);\n for (var prop in node) { newNode[prop] = node[prop]; }\n return newNode\n };\n\n // This file was generated by \"bin/generate-unicode-script-values.js\". Do not modify manually!\n var scriptValuesAddedInUnicode = \"Berf Beria_Erfe Gara Garay Gukh Gurung_Khema Hrkt Katakana_Or_Hiragana Kawi Kirat_Rai Krai Nag_Mundari Nagm Ol_Onal Onao Sidetic Sidt Sunu Sunuwar Tai_Yo Tayo Todhri Todr Tolong_Siki Tols Tulu_Tigalari Tutg Unknown Zzzz\";\n\n // This file contains Unicode properties extracted from the ECMAScript specification.\n // The lists are extracted like so:\n // $$('#table-binary-unicode-properties > figure > table > tbody > tr > td:nth-child(1) code').map(el => el.innerText)\n\n // #table-binary-unicode-properties\n var ecma9BinaryProperties = \"ASCII ASCII_Hex_Digit AHex Alphabetic Alpha Any Assigned Bidi_Control Bidi_C Bidi_Mirrored Bidi_M Case_Ignorable CI Cased Changes_When_Casefolded CWCF Changes_When_Casemapped CWCM Changes_When_Lowercased CWL Changes_When_NFKC_Casefolded CWKCF Changes_When_Titlecased CWT Changes_When_Uppercased CWU Dash Default_Ignorable_Code_Point DI Deprecated Dep Diacritic Dia Emoji Emoji_Component Emoji_Modifier Emoji_Modifier_Base Emoji_Presentation Extender Ext Grapheme_Base Gr_Base Grapheme_Extend Gr_Ext Hex_Digit Hex IDS_Binary_Operator IDSB IDS_Trinary_Operator IDST ID_Continue IDC ID_Start IDS Ideographic Ideo Join_Control Join_C Logical_Order_Exception LOE Lowercase Lower Math Noncharacter_Code_Point NChar Pattern_Syntax Pat_Syn Pattern_White_Space Pat_WS Quotation_Mark QMark Radical Regional_Indicator RI Sentence_Terminal STerm Soft_Dotted SD Terminal_Punctuation Term Unified_Ideograph UIdeo Uppercase Upper Variation_Selector VS White_Space space XID_Continue XIDC XID_Start XIDS\";\n var ecma10BinaryProperties = ecma9BinaryProperties + \" Extended_Pictographic\";\n var ecma11BinaryProperties = ecma10BinaryProperties;\n var ecma12BinaryProperties = ecma11BinaryProperties + \" EBase EComp EMod EPres ExtPict\";\n var ecma13BinaryProperties = ecma12BinaryProperties;\n var ecma14BinaryProperties = ecma13BinaryProperties;\n\n var unicodeBinaryProperties = {\n 9: ecma9BinaryProperties,\n 10: ecma10BinaryProperties,\n 11: ecma11BinaryProperties,\n 12: ecma12BinaryProperties,\n 13: ecma13BinaryProperties,\n 14: ecma14BinaryProperties\n };\n\n // #table-binary-unicode-properties-of-strings\n var ecma14BinaryPropertiesOfStrings = \"Basic_Emoji Emoji_Keycap_Sequence RGI_Emoji_Modifier_Sequence RGI_Emoji_Flag_Sequence RGI_Emoji_Tag_Sequence RGI_Emoji_ZWJ_Sequence RGI_Emoji\";\n\n var unicodeBinaryPropertiesOfStrings = {\n 9: \"\",\n 10: \"\",\n 11: \"\",\n 12: \"\",\n 13: \"\",\n 14: ecma14BinaryPropertiesOfStrings\n };\n\n // #table-unicode-general-category-values\n var unicodeGeneralCategoryValues = \"Cased_Letter LC Close_Punctuation Pe Connector_Punctuation Pc Control Cc cntrl Currency_Symbol Sc Dash_Punctuation Pd Decimal_Number Nd digit Enclosing_Mark Me Final_Punctuation Pf Format Cf Initial_Punctuation Pi Letter L Letter_Number Nl Line_Separator Zl Lowercase_Letter Ll Mark M Combining_Mark Math_Symbol Sm Modifier_Letter Lm Modifier_Symbol Sk Nonspacing_Mark Mn Number N Open_Punctuation Ps Other C Other_Letter Lo Other_Number No Other_Punctuation Po Other_Symbol So Paragraph_Separator Zp Private_Use Co Punctuation P punct Separator Z Space_Separator Zs Spacing_Mark Mc Surrogate Cs Symbol S Titlecase_Letter Lt Unassigned Cn Uppercase_Letter Lu\";\n\n // #table-unicode-script-values\n var ecma9ScriptValues = \"Adlam Adlm Ahom Anatolian_Hieroglyphs Hluw Arabic Arab Armenian Armn Avestan Avst Balinese Bali Bamum Bamu Bassa_Vah Bass Batak Batk Bengali Beng Bhaiksuki Bhks Bopomofo Bopo Brahmi Brah Braille Brai Buginese Bugi Buhid Buhd Canadian_Aboriginal Cans Carian Cari Caucasian_Albanian Aghb Chakma Cakm Cham Cham Cherokee Cher Common Zyyy Coptic Copt Qaac Cuneiform Xsux Cypriot Cprt Cyrillic Cyrl Deseret Dsrt Devanagari Deva Duployan Dupl Egyptian_Hieroglyphs Egyp Elbasan Elba Ethiopic Ethi Georgian Geor Glagolitic Glag Gothic Goth Grantha Gran Greek Grek Gujarati Gujr Gurmukhi Guru Han Hani Hangul Hang Hanunoo Hano Hatran Hatr Hebrew Hebr Hiragana Hira Imperial_Aramaic Armi Inherited Zinh Qaai Inscriptional_Pahlavi Phli Inscriptional_Parthian Prti Javanese Java Kaithi Kthi Kannada Knda Katakana Kana Kayah_Li Kali Kharoshthi Khar Khmer Khmr Khojki Khoj Khudawadi Sind Lao Laoo Latin Latn Lepcha Lepc Limbu Limb Linear_A Lina Linear_B Linb Lisu Lisu Lycian Lyci Lydian Lydi Mahajani Mahj Malayalam Mlym Mandaic Mand Manichaean Mani Marchen Marc Masaram_Gondi Gonm Meetei_Mayek Mtei Mende_Kikakui Mend Meroitic_Cursive Merc Meroitic_Hieroglyphs Mero Miao Plrd Modi Mongolian Mong Mro Mroo Multani Mult Myanmar Mymr Nabataean Nbat New_Tai_Lue Talu Newa Newa Nko Nkoo Nushu Nshu Ogham Ogam Ol_Chiki Olck Old_Hungarian Hung Old_Italic Ital Old_North_Arabian Narb Old_Permic Perm Old_Persian Xpeo Old_South_Arabian Sarb Old_Turkic Orkh Oriya Orya Osage Osge Osmanya Osma Pahawh_Hmong Hmng Palmyrene Palm Pau_Cin_Hau Pauc Phags_Pa Phag Phoenician Phnx Psalter_Pahlavi Phlp Rejang Rjng Runic Runr Samaritan Samr Saurashtra Saur Sharada Shrd Shavian Shaw Siddham Sidd SignWriting Sgnw Sinhala Sinh Sora_Sompeng Sora Soyombo Soyo Sundanese Sund Syloti_Nagri Sylo Syriac Syrc Tagalog Tglg Tagbanwa Tagb Tai_Le Tale Tai_Tham Lana Tai_Viet Tavt Takri Takr Tamil Taml Tangut Tang Telugu Telu Thaana Thaa Thai Thai Tibetan Tibt Tifinagh Tfng Tirhuta Tirh Ugaritic Ugar Vai Vaii Warang_Citi Wara Yi Yiii Zanabazar_Square Zanb\";\n var ecma10ScriptValues = ecma9ScriptValues + \" Dogra Dogr Gunjala_Gondi Gong Hanifi_Rohingya Rohg Makasar Maka Medefaidrin Medf Old_Sogdian Sogo Sogdian Sogd\";\n var ecma11ScriptValues = ecma10ScriptValues + \" Elymaic Elym Nandinagari Nand Nyiakeng_Puachue_Hmong Hmnp Wancho Wcho\";\n var ecma12ScriptValues = ecma11ScriptValues + \" Chorasmian Chrs Diak Dives_Akuru Khitan_Small_Script Kits Yezi Yezidi\";\n var ecma13ScriptValues = ecma12ScriptValues + \" Cypro_Minoan Cpmn Old_Uyghur Ougr Tangsa Tnsa Toto Vithkuqi Vith\";\n var ecma14ScriptValues = ecma13ScriptValues + \" \" + scriptValuesAddedInUnicode;\n\n var unicodeScriptValues = {\n 9: ecma9ScriptValues,\n 10: ecma10ScriptValues,\n 11: ecma11ScriptValues,\n 12: ecma12ScriptValues,\n 13: ecma13ScriptValues,\n 14: ecma14ScriptValues\n };\n\n var data = {};\n function buildUnicodeData(ecmaVersion) {\n var d = data[ecmaVersion] = {\n binary: wordsRegexp(unicodeBinaryProperties[ecmaVersion] + \" \" + unicodeGeneralCategoryValues),\n binaryOfStrings: wordsRegexp(unicodeBinaryPropertiesOfStrings[ecmaVersion]),\n nonBinary: {\n General_Category: wordsRegexp(unicodeGeneralCategoryValues),\n Script: wordsRegexp(unicodeScriptValues[ecmaVersion])\n }\n };\n d.nonBinary.Script_Extensions = d.nonBinary.Script;\n\n d.nonBinary.gc = d.nonBinary.General_Category;\n d.nonBinary.sc = d.nonBinary.Script;\n d.nonBinary.scx = d.nonBinary.Script_Extensions;\n }\n\n for (var i = 0, list = [9, 10, 11, 12, 13, 14]; i < list.length; i += 1) {\n var ecmaVersion = list[i];\n\n buildUnicodeData(ecmaVersion);\n }\n\n var pp$1 = Parser.prototype;\n\n // Track disjunction structure to determine whether a duplicate\n // capture group name is allowed because it is in a separate branch.\n var BranchID = function BranchID(parent, base) {\n // Parent disjunction branch\n this.parent = parent;\n // Identifies this set of sibling branches\n this.base = base || this;\n };\n\n BranchID.prototype.separatedFrom = function separatedFrom (alt) {\n // A branch is separate from another branch if they or any of\n // their parents are siblings in a given disjunction\n for (var self = this; self; self = self.parent) {\n for (var other = alt; other; other = other.parent) {\n if (self.base === other.base && self !== other) { return true }\n }\n }\n return false\n };\n\n BranchID.prototype.sibling = function sibling () {\n return new BranchID(this.parent, this.base)\n };\n\n var RegExpValidationState = function RegExpValidationState(parser) {\n this.parser = parser;\n this.validFlags = \"gim\" + (parser.options.ecmaVersion >= 6 ? \"uy\" : \"\") + (parser.options.ecmaVersion >= 9 ? \"s\" : \"\") + (parser.options.ecmaVersion >= 13 ? \"d\" : \"\") + (parser.options.ecmaVersion >= 15 ? \"v\" : \"\");\n this.unicodeProperties = data[parser.options.ecmaVersion >= 14 ? 14 : parser.options.ecmaVersion];\n this.source = \"\";\n this.flags = \"\";\n this.start = 0;\n this.switchU = false;\n this.switchV = false;\n this.switchN = false;\n this.pos = 0;\n this.lastIntValue = 0;\n this.lastStringValue = \"\";\n this.lastAssertionIsQuantifiable = false;\n this.numCapturingParens = 0;\n this.maxBackReference = 0;\n this.groupNames = Object.create(null);\n this.backReferenceNames = [];\n this.branchID = null;\n };\n\n RegExpValidationState.prototype.reset = function reset (start, pattern, flags) {\n var unicodeSets = flags.indexOf(\"v\") !== -1;\n var unicode = flags.indexOf(\"u\") !== -1;\n this.start = start | 0;\n this.source = pattern + \"\";\n this.flags = flags;\n if (unicodeSets && this.parser.options.ecmaVersion >= 15) {\n this.switchU = true;\n this.switchV = true;\n this.switchN = true;\n } else {\n this.switchU = unicode && this.parser.options.ecmaVersion >= 6;\n this.switchV = false;\n this.switchN = unicode && this.parser.options.ecmaVersion >= 9;\n }\n };\n\n RegExpValidationState.prototype.raise = function raise (message) {\n this.parser.raiseRecoverable(this.start, (\"Invalid regular expression: /\" + (this.source) + \"/: \" + message));\n };\n\n // If u flag is given, this returns the code point at the index (it combines a surrogate pair).\n // Otherwise, this returns the code unit of the index (can be a part of a surrogate pair).\n RegExpValidationState.prototype.at = function at (i, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n var s = this.source;\n var l = s.length;\n if (i >= l) {\n return -1\n }\n var c = s.charCodeAt(i);\n if (!(forceU || this.switchU) || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l) {\n return c\n }\n var next = s.charCodeAt(i + 1);\n return next >= 0xDC00 && next <= 0xDFFF ? (c << 10) + next - 0x35FDC00 : c\n };\n\n RegExpValidationState.prototype.nextIndex = function nextIndex (i, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n var s = this.source;\n var l = s.length;\n if (i >= l) {\n return l\n }\n var c = s.charCodeAt(i), next;\n if (!(forceU || this.switchU) || c <= 0xD7FF || c >= 0xE000 || i + 1 >= l ||\n (next = s.charCodeAt(i + 1)) < 0xDC00 || next > 0xDFFF) {\n return i + 1\n }\n return i + 2\n };\n\n RegExpValidationState.prototype.current = function current (forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n return this.at(this.pos, forceU)\n };\n\n RegExpValidationState.prototype.lookahead = function lookahead (forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n return this.at(this.nextIndex(this.pos, forceU), forceU)\n };\n\n RegExpValidationState.prototype.advance = function advance (forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n this.pos = this.nextIndex(this.pos, forceU);\n };\n\n RegExpValidationState.prototype.eat = function eat (ch, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n if (this.current(forceU) === ch) {\n this.advance(forceU);\n return true\n }\n return false\n };\n\n RegExpValidationState.prototype.eatChars = function eatChars (chs, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n var pos = this.pos;\n for (var i = 0, list = chs; i < list.length; i += 1) {\n var ch = list[i];\n\n var current = this.at(pos, forceU);\n if (current === -1 || current !== ch) {\n return false\n }\n pos = this.nextIndex(pos, forceU);\n }\n this.pos = pos;\n return true\n };\n\n /**\n * Validate the flags part of a given RegExpLiteral.\n *\n * @param {RegExpValidationState} state The state to validate RegExp.\n * @returns {void}\n */\n pp$1.validateRegExpFlags = function(state) {\n var validFlags = state.validFlags;\n var flags = state.flags;\n\n var u = false;\n var v = false;\n\n for (var i = 0; i < flags.length; i++) {\n var flag = flags.charAt(i);\n if (validFlags.indexOf(flag) === -1) {\n this.raise(state.start, \"Invalid regular expression flag\");\n }\n if (flags.indexOf(flag, i + 1) > -1) {\n this.raise(state.start, \"Duplicate regular expression flag\");\n }\n if (flag === \"u\") { u = true; }\n if (flag === \"v\") { v = true; }\n }\n if (this.options.ecmaVersion >= 15 && u && v) {\n this.raise(state.start, \"Invalid regular expression flag\");\n }\n };\n\n function hasProp(obj) {\n for (var _ in obj) { return true }\n return false\n }\n\n /**\n * Validate the pattern part of a given RegExpLiteral.\n *\n * @param {RegExpValidationState} state The state to validate RegExp.\n * @returns {void}\n */\n pp$1.validateRegExpPattern = function(state) {\n this.regexp_pattern(state);\n\n // The goal symbol for the parse is |Pattern[~U, ~N]|. If the result of\n // parsing contains a |GroupName|, reparse with the goal symbol\n // |Pattern[~U, +N]| and use this result instead. Throw a *SyntaxError*\n // exception if _P_ did not conform to the grammar, if any elements of _P_\n // were not matched by the parse, or if any Early Error conditions exist.\n if (!state.switchN && this.options.ecmaVersion >= 9 && hasProp(state.groupNames)) {\n state.switchN = true;\n this.regexp_pattern(state);\n }\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Pattern\n pp$1.regexp_pattern = function(state) {\n state.pos = 0;\n state.lastIntValue = 0;\n state.lastStringValue = \"\";\n state.lastAssertionIsQuantifiable = false;\n state.numCapturingParens = 0;\n state.maxBackReference = 0;\n state.groupNames = Object.create(null);\n state.backReferenceNames.length = 0;\n state.branchID = null;\n\n this.regexp_disjunction(state);\n\n if (state.pos !== state.source.length) {\n // Make the same messages as V8.\n if (state.eat(0x29 /* ) */)) {\n state.raise(\"Unmatched ')'\");\n }\n if (state.eat(0x5D /* ] */) || state.eat(0x7D /* } */)) {\n state.raise(\"Lone quantifier brackets\");\n }\n }\n if (state.maxBackReference > state.numCapturingParens) {\n state.raise(\"Invalid escape\");\n }\n for (var i = 0, list = state.backReferenceNames; i < list.length; i += 1) {\n var name = list[i];\n\n if (!state.groupNames[name]) {\n state.raise(\"Invalid named capture referenced\");\n }\n }\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Disjunction\n pp$1.regexp_disjunction = function(state) {\n var trackDisjunction = this.options.ecmaVersion >= 16;\n if (trackDisjunction) { state.branchID = new BranchID(state.branchID, null); }\n this.regexp_alternative(state);\n while (state.eat(0x7C /* | */)) {\n if (trackDisjunction) { state.branchID = state.branchID.sibling(); }\n this.regexp_alternative(state);\n }\n if (trackDisjunction) { state.branchID = state.branchID.parent; }\n\n // Make the same message as V8.\n if (this.regexp_eatQuantifier(state, true)) {\n state.raise(\"Nothing to repeat\");\n }\n if (state.eat(0x7B /* { */)) {\n state.raise(\"Lone quantifier brackets\");\n }\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Alternative\n pp$1.regexp_alternative = function(state) {\n while (state.pos < state.source.length && this.regexp_eatTerm(state)) {}\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-Term\n pp$1.regexp_eatTerm = function(state) {\n if (this.regexp_eatAssertion(state)) {\n // Handle `QuantifiableAssertion Quantifier` alternative.\n // `state.lastAssertionIsQuantifiable` is true if the last eaten Assertion\n // is a QuantifiableAssertion.\n if (state.lastAssertionIsQuantifiable && this.regexp_eatQuantifier(state)) {\n // Make the same message as V8.\n if (state.switchU) {\n state.raise(\"Invalid quantifier\");\n }\n }\n return true\n }\n\n if (state.switchU ? this.regexp_eatAtom(state) : this.regexp_eatExtendedAtom(state)) {\n this.regexp_eatQuantifier(state);\n return true\n }\n\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-Assertion\n pp$1.regexp_eatAssertion = function(state) {\n var start = state.pos;\n state.lastAssertionIsQuantifiable = false;\n\n // ^, $\n if (state.eat(0x5E /* ^ */) || state.eat(0x24 /* $ */)) {\n return true\n }\n\n // \\b \\B\n if (state.eat(0x5C /* \\ */)) {\n if (state.eat(0x42 /* B */) || state.eat(0x62 /* b */)) {\n return true\n }\n state.pos = start;\n }\n\n // Lookahead / Lookbehind\n if (state.eat(0x28 /* ( */) && state.eat(0x3F /* ? */)) {\n var lookbehind = false;\n if (this.options.ecmaVersion >= 9) {\n lookbehind = state.eat(0x3C /* < */);\n }\n if (state.eat(0x3D /* = */) || state.eat(0x21 /* ! */)) {\n this.regexp_disjunction(state);\n if (!state.eat(0x29 /* ) */)) {\n state.raise(\"Unterminated group\");\n }\n state.lastAssertionIsQuantifiable = !lookbehind;\n return true\n }\n }\n\n state.pos = start;\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Quantifier\n pp$1.regexp_eatQuantifier = function(state, noError) {\n if ( noError === void 0 ) noError = false;\n\n if (this.regexp_eatQuantifierPrefix(state, noError)) {\n state.eat(0x3F /* ? */);\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-QuantifierPrefix\n pp$1.regexp_eatQuantifierPrefix = function(state, noError) {\n return (\n state.eat(0x2A /* * */) ||\n state.eat(0x2B /* + */) ||\n state.eat(0x3F /* ? */) ||\n this.regexp_eatBracedQuantifier(state, noError)\n )\n };\n pp$1.regexp_eatBracedQuantifier = function(state, noError) {\n var start = state.pos;\n if (state.eat(0x7B /* { */)) {\n var min = 0, max = -1;\n if (this.regexp_eatDecimalDigits(state)) {\n min = state.lastIntValue;\n if (state.eat(0x2C /* , */) && this.regexp_eatDecimalDigits(state)) {\n max = state.lastIntValue;\n }\n if (state.eat(0x7D /* } */)) {\n // SyntaxError in https://www.ecma-international.org/ecma-262/8.0/#sec-term\n if (max !== -1 && max < min && !noError) {\n state.raise(\"numbers out of order in {} quantifier\");\n }\n return true\n }\n }\n if (state.switchU && !noError) {\n state.raise(\"Incomplete quantifier\");\n }\n state.pos = start;\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Atom\n pp$1.regexp_eatAtom = function(state) {\n return (\n this.regexp_eatPatternCharacters(state) ||\n state.eat(0x2E /* . */) ||\n this.regexp_eatReverseSolidusAtomEscape(state) ||\n this.regexp_eatCharacterClass(state) ||\n this.regexp_eatUncapturingGroup(state) ||\n this.regexp_eatCapturingGroup(state)\n )\n };\n pp$1.regexp_eatReverseSolidusAtomEscape = function(state) {\n var start = state.pos;\n if (state.eat(0x5C /* \\ */)) {\n if (this.regexp_eatAtomEscape(state)) {\n return true\n }\n state.pos = start;\n }\n return false\n };\n pp$1.regexp_eatUncapturingGroup = function(state) {\n var start = state.pos;\n if (state.eat(0x28 /* ( */)) {\n if (state.eat(0x3F /* ? */)) {\n if (this.options.ecmaVersion >= 16) {\n var addModifiers = this.regexp_eatModifiers(state);\n var hasHyphen = state.eat(0x2D /* - */);\n if (addModifiers || hasHyphen) {\n for (var i = 0; i < addModifiers.length; i++) {\n var modifier = addModifiers.charAt(i);\n if (addModifiers.indexOf(modifier, i + 1) > -1) {\n state.raise(\"Duplicate regular expression modifiers\");\n }\n }\n if (hasHyphen) {\n var removeModifiers = this.regexp_eatModifiers(state);\n if (!addModifiers && !removeModifiers && state.current() === 0x3A /* : */) {\n state.raise(\"Invalid regular expression modifiers\");\n }\n for (var i$1 = 0; i$1 < removeModifiers.length; i$1++) {\n var modifier$1 = removeModifiers.charAt(i$1);\n if (\n removeModifiers.indexOf(modifier$1, i$1 + 1) > -1 ||\n addModifiers.indexOf(modifier$1) > -1\n ) {\n state.raise(\"Duplicate regular expression modifiers\");\n }\n }\n }\n }\n }\n if (state.eat(0x3A /* : */)) {\n this.regexp_disjunction(state);\n if (state.eat(0x29 /* ) */)) {\n return true\n }\n state.raise(\"Unterminated group\");\n }\n }\n state.pos = start;\n }\n return false\n };\n pp$1.regexp_eatCapturingGroup = function(state) {\n if (state.eat(0x28 /* ( */)) {\n if (this.options.ecmaVersion >= 9) {\n this.regexp_groupSpecifier(state);\n } else if (state.current() === 0x3F /* ? */) {\n state.raise(\"Invalid group\");\n }\n this.regexp_disjunction(state);\n if (state.eat(0x29 /* ) */)) {\n state.numCapturingParens += 1;\n return true\n }\n state.raise(\"Unterminated group\");\n }\n return false\n };\n // RegularExpressionModifiers ::\n // [empty]\n // RegularExpressionModifiers RegularExpressionModifier\n pp$1.regexp_eatModifiers = function(state) {\n var modifiers = \"\";\n var ch = 0;\n while ((ch = state.current()) !== -1 && isRegularExpressionModifier(ch)) {\n modifiers += codePointToString(ch);\n state.advance();\n }\n return modifiers\n };\n // RegularExpressionModifier :: one of\n // `i` `m` `s`\n function isRegularExpressionModifier(ch) {\n return ch === 0x69 /* i */ || ch === 0x6d /* m */ || ch === 0x73 /* s */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ExtendedAtom\n pp$1.regexp_eatExtendedAtom = function(state) {\n return (\n state.eat(0x2E /* . */) ||\n this.regexp_eatReverseSolidusAtomEscape(state) ||\n this.regexp_eatCharacterClass(state) ||\n this.regexp_eatUncapturingGroup(state) ||\n this.regexp_eatCapturingGroup(state) ||\n this.regexp_eatInvalidBracedQuantifier(state) ||\n this.regexp_eatExtendedPatternCharacter(state)\n )\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-InvalidBracedQuantifier\n pp$1.regexp_eatInvalidBracedQuantifier = function(state) {\n if (this.regexp_eatBracedQuantifier(state, true)) {\n state.raise(\"Nothing to repeat\");\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-SyntaxCharacter\n pp$1.regexp_eatSyntaxCharacter = function(state) {\n var ch = state.current();\n if (isSyntaxCharacter(ch)) {\n state.lastIntValue = ch;\n state.advance();\n return true\n }\n return false\n };\n function isSyntaxCharacter(ch) {\n return (\n ch === 0x24 /* $ */ ||\n ch >= 0x28 /* ( */ && ch <= 0x2B /* + */ ||\n ch === 0x2E /* . */ ||\n ch === 0x3F /* ? */ ||\n ch >= 0x5B /* [ */ && ch <= 0x5E /* ^ */ ||\n ch >= 0x7B /* { */ && ch <= 0x7D /* } */\n )\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-PatternCharacter\n // But eat eager.\n pp$1.regexp_eatPatternCharacters = function(state) {\n var start = state.pos;\n var ch = 0;\n while ((ch = state.current()) !== -1 && !isSyntaxCharacter(ch)) {\n state.advance();\n }\n return state.pos !== start\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ExtendedPatternCharacter\n pp$1.regexp_eatExtendedPatternCharacter = function(state) {\n var ch = state.current();\n if (\n ch !== -1 &&\n ch !== 0x24 /* $ */ &&\n !(ch >= 0x28 /* ( */ && ch <= 0x2B /* + */) &&\n ch !== 0x2E /* . */ &&\n ch !== 0x3F /* ? */ &&\n ch !== 0x5B /* [ */ &&\n ch !== 0x5E /* ^ */ &&\n ch !== 0x7C /* | */\n ) {\n state.advance();\n return true\n }\n return false\n };\n\n // GroupSpecifier ::\n // [empty]\n // `?` GroupName\n pp$1.regexp_groupSpecifier = function(state) {\n if (state.eat(0x3F /* ? */)) {\n if (!this.regexp_eatGroupName(state)) { state.raise(\"Invalid group\"); }\n var trackDisjunction = this.options.ecmaVersion >= 16;\n var known = state.groupNames[state.lastStringValue];\n if (known) {\n if (trackDisjunction) {\n for (var i = 0, list = known; i < list.length; i += 1) {\n var altID = list[i];\n\n if (!altID.separatedFrom(state.branchID))\n { state.raise(\"Duplicate capture group name\"); }\n }\n } else {\n state.raise(\"Duplicate capture group name\");\n }\n }\n if (trackDisjunction) {\n (known || (state.groupNames[state.lastStringValue] = [])).push(state.branchID);\n } else {\n state.groupNames[state.lastStringValue] = true;\n }\n }\n };\n\n // GroupName ::\n // `<` RegExpIdentifierName `>`\n // Note: this updates `state.lastStringValue` property with the eaten name.\n pp$1.regexp_eatGroupName = function(state) {\n state.lastStringValue = \"\";\n if (state.eat(0x3C /* < */)) {\n if (this.regexp_eatRegExpIdentifierName(state) && state.eat(0x3E /* > */)) {\n return true\n }\n state.raise(\"Invalid capture group name\");\n }\n return false\n };\n\n // RegExpIdentifierName ::\n // RegExpIdentifierStart\n // RegExpIdentifierName RegExpIdentifierPart\n // Note: this updates `state.lastStringValue` property with the eaten name.\n pp$1.regexp_eatRegExpIdentifierName = function(state) {\n state.lastStringValue = \"\";\n if (this.regexp_eatRegExpIdentifierStart(state)) {\n state.lastStringValue += codePointToString(state.lastIntValue);\n while (this.regexp_eatRegExpIdentifierPart(state)) {\n state.lastStringValue += codePointToString(state.lastIntValue);\n }\n return true\n }\n return false\n };\n\n // RegExpIdentifierStart ::\n // UnicodeIDStart\n // `$`\n // `_`\n // `\\` RegExpUnicodeEscapeSequence[+U]\n pp$1.regexp_eatRegExpIdentifierStart = function(state) {\n var start = state.pos;\n var forceU = this.options.ecmaVersion >= 11;\n var ch = state.current(forceU);\n state.advance(forceU);\n\n if (ch === 0x5C /* \\ */ && this.regexp_eatRegExpUnicodeEscapeSequence(state, forceU)) {\n ch = state.lastIntValue;\n }\n if (isRegExpIdentifierStart(ch)) {\n state.lastIntValue = ch;\n return true\n }\n\n state.pos = start;\n return false\n };\n function isRegExpIdentifierStart(ch) {\n return isIdentifierStart(ch, true) || ch === 0x24 /* $ */ || ch === 0x5F /* _ */\n }\n\n // RegExpIdentifierPart ::\n // UnicodeIDContinue\n // `$`\n // `_`\n // `\\` RegExpUnicodeEscapeSequence[+U]\n // \n // \n pp$1.regexp_eatRegExpIdentifierPart = function(state) {\n var start = state.pos;\n var forceU = this.options.ecmaVersion >= 11;\n var ch = state.current(forceU);\n state.advance(forceU);\n\n if (ch === 0x5C /* \\ */ && this.regexp_eatRegExpUnicodeEscapeSequence(state, forceU)) {\n ch = state.lastIntValue;\n }\n if (isRegExpIdentifierPart(ch)) {\n state.lastIntValue = ch;\n return true\n }\n\n state.pos = start;\n return false\n };\n function isRegExpIdentifierPart(ch) {\n return isIdentifierChar(ch, true) || ch === 0x24 /* $ */ || ch === 0x5F /* _ */ || ch === 0x200C /* */ || ch === 0x200D /* */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-AtomEscape\n pp$1.regexp_eatAtomEscape = function(state) {\n if (\n this.regexp_eatBackReference(state) ||\n this.regexp_eatCharacterClassEscape(state) ||\n this.regexp_eatCharacterEscape(state) ||\n (state.switchN && this.regexp_eatKGroupName(state))\n ) {\n return true\n }\n if (state.switchU) {\n // Make the same message as V8.\n if (state.current() === 0x63 /* c */) {\n state.raise(\"Invalid unicode escape\");\n }\n state.raise(\"Invalid escape\");\n }\n return false\n };\n pp$1.regexp_eatBackReference = function(state) {\n var start = state.pos;\n if (this.regexp_eatDecimalEscape(state)) {\n var n = state.lastIntValue;\n if (state.switchU) {\n // For SyntaxError in https://www.ecma-international.org/ecma-262/8.0/#sec-atomescape\n if (n > state.maxBackReference) {\n state.maxBackReference = n;\n }\n return true\n }\n if (n <= state.numCapturingParens) {\n return true\n }\n state.pos = start;\n }\n return false\n };\n pp$1.regexp_eatKGroupName = function(state) {\n if (state.eat(0x6B /* k */)) {\n if (this.regexp_eatGroupName(state)) {\n state.backReferenceNames.push(state.lastStringValue);\n return true\n }\n state.raise(\"Invalid named reference\");\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-CharacterEscape\n pp$1.regexp_eatCharacterEscape = function(state) {\n return (\n this.regexp_eatControlEscape(state) ||\n this.regexp_eatCControlLetter(state) ||\n this.regexp_eatZero(state) ||\n this.regexp_eatHexEscapeSequence(state) ||\n this.regexp_eatRegExpUnicodeEscapeSequence(state, false) ||\n (!state.switchU && this.regexp_eatLegacyOctalEscapeSequence(state)) ||\n this.regexp_eatIdentityEscape(state)\n )\n };\n pp$1.regexp_eatCControlLetter = function(state) {\n var start = state.pos;\n if (state.eat(0x63 /* c */)) {\n if (this.regexp_eatControlLetter(state)) {\n return true\n }\n state.pos = start;\n }\n return false\n };\n pp$1.regexp_eatZero = function(state) {\n if (state.current() === 0x30 /* 0 */ && !isDecimalDigit(state.lookahead())) {\n state.lastIntValue = 0;\n state.advance();\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ControlEscape\n pp$1.regexp_eatControlEscape = function(state) {\n var ch = state.current();\n if (ch === 0x74 /* t */) {\n state.lastIntValue = 0x09; /* \\t */\n state.advance();\n return true\n }\n if (ch === 0x6E /* n */) {\n state.lastIntValue = 0x0A; /* \\n */\n state.advance();\n return true\n }\n if (ch === 0x76 /* v */) {\n state.lastIntValue = 0x0B; /* \\v */\n state.advance();\n return true\n }\n if (ch === 0x66 /* f */) {\n state.lastIntValue = 0x0C; /* \\f */\n state.advance();\n return true\n }\n if (ch === 0x72 /* r */) {\n state.lastIntValue = 0x0D; /* \\r */\n state.advance();\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ControlLetter\n pp$1.regexp_eatControlLetter = function(state) {\n var ch = state.current();\n if (isControlLetter(ch)) {\n state.lastIntValue = ch % 0x20;\n state.advance();\n return true\n }\n return false\n };\n function isControlLetter(ch) {\n return (\n (ch >= 0x41 /* A */ && ch <= 0x5A /* Z */) ||\n (ch >= 0x61 /* a */ && ch <= 0x7A /* z */)\n )\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-RegExpUnicodeEscapeSequence\n pp$1.regexp_eatRegExpUnicodeEscapeSequence = function(state, forceU) {\n if ( forceU === void 0 ) forceU = false;\n\n var start = state.pos;\n var switchU = forceU || state.switchU;\n\n if (state.eat(0x75 /* u */)) {\n if (this.regexp_eatFixedHexDigits(state, 4)) {\n var lead = state.lastIntValue;\n if (switchU && lead >= 0xD800 && lead <= 0xDBFF) {\n var leadSurrogateEnd = state.pos;\n if (state.eat(0x5C /* \\ */) && state.eat(0x75 /* u */) && this.regexp_eatFixedHexDigits(state, 4)) {\n var trail = state.lastIntValue;\n if (trail >= 0xDC00 && trail <= 0xDFFF) {\n state.lastIntValue = (lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000;\n return true\n }\n }\n state.pos = leadSurrogateEnd;\n state.lastIntValue = lead;\n }\n return true\n }\n if (\n switchU &&\n state.eat(0x7B /* { */) &&\n this.regexp_eatHexDigits(state) &&\n state.eat(0x7D /* } */) &&\n isValidUnicode(state.lastIntValue)\n ) {\n return true\n }\n if (switchU) {\n state.raise(\"Invalid unicode escape\");\n }\n state.pos = start;\n }\n\n return false\n };\n function isValidUnicode(ch) {\n return ch >= 0 && ch <= 0x10FFFF\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-IdentityEscape\n pp$1.regexp_eatIdentityEscape = function(state) {\n if (state.switchU) {\n if (this.regexp_eatSyntaxCharacter(state)) {\n return true\n }\n if (state.eat(0x2F /* / */)) {\n state.lastIntValue = 0x2F; /* / */\n return true\n }\n return false\n }\n\n var ch = state.current();\n if (ch !== 0x63 /* c */ && (!state.switchN || ch !== 0x6B /* k */)) {\n state.lastIntValue = ch;\n state.advance();\n return true\n }\n\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-DecimalEscape\n pp$1.regexp_eatDecimalEscape = function(state) {\n state.lastIntValue = 0;\n var ch = state.current();\n if (ch >= 0x31 /* 1 */ && ch <= 0x39 /* 9 */) {\n do {\n state.lastIntValue = 10 * state.lastIntValue + (ch - 0x30 /* 0 */);\n state.advance();\n } while ((ch = state.current()) >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */)\n return true\n }\n return false\n };\n\n // Return values used by character set parsing methods, needed to\n // forbid negation of sets that can match strings.\n var CharSetNone = 0; // Nothing parsed\n var CharSetOk = 1; // Construct parsed, cannot contain strings\n var CharSetString = 2; // Construct parsed, can contain strings\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-CharacterClassEscape\n pp$1.regexp_eatCharacterClassEscape = function(state) {\n var ch = state.current();\n\n if (isCharacterClassEscape(ch)) {\n state.lastIntValue = -1;\n state.advance();\n return CharSetOk\n }\n\n var negate = false;\n if (\n state.switchU &&\n this.options.ecmaVersion >= 9 &&\n ((negate = ch === 0x50 /* P */) || ch === 0x70 /* p */)\n ) {\n state.lastIntValue = -1;\n state.advance();\n var result;\n if (\n state.eat(0x7B /* { */) &&\n (result = this.regexp_eatUnicodePropertyValueExpression(state)) &&\n state.eat(0x7D /* } */)\n ) {\n if (negate && result === CharSetString) { state.raise(\"Invalid property name\"); }\n return result\n }\n state.raise(\"Invalid property name\");\n }\n\n return CharSetNone\n };\n\n function isCharacterClassEscape(ch) {\n return (\n ch === 0x64 /* d */ ||\n ch === 0x44 /* D */ ||\n ch === 0x73 /* s */ ||\n ch === 0x53 /* S */ ||\n ch === 0x77 /* w */ ||\n ch === 0x57 /* W */\n )\n }\n\n // UnicodePropertyValueExpression ::\n // UnicodePropertyName `=` UnicodePropertyValue\n // LoneUnicodePropertyNameOrValue\n pp$1.regexp_eatUnicodePropertyValueExpression = function(state) {\n var start = state.pos;\n\n // UnicodePropertyName `=` UnicodePropertyValue\n if (this.regexp_eatUnicodePropertyName(state) && state.eat(0x3D /* = */)) {\n var name = state.lastStringValue;\n if (this.regexp_eatUnicodePropertyValue(state)) {\n var value = state.lastStringValue;\n this.regexp_validateUnicodePropertyNameAndValue(state, name, value);\n return CharSetOk\n }\n }\n state.pos = start;\n\n // LoneUnicodePropertyNameOrValue\n if (this.regexp_eatLoneUnicodePropertyNameOrValue(state)) {\n var nameOrValue = state.lastStringValue;\n return this.regexp_validateUnicodePropertyNameOrValue(state, nameOrValue)\n }\n return CharSetNone\n };\n\n pp$1.regexp_validateUnicodePropertyNameAndValue = function(state, name, value) {\n if (!hasOwn(state.unicodeProperties.nonBinary, name))\n { state.raise(\"Invalid property name\"); }\n if (!state.unicodeProperties.nonBinary[name].test(value))\n { state.raise(\"Invalid property value\"); }\n };\n\n pp$1.regexp_validateUnicodePropertyNameOrValue = function(state, nameOrValue) {\n if (state.unicodeProperties.binary.test(nameOrValue)) { return CharSetOk }\n if (state.switchV && state.unicodeProperties.binaryOfStrings.test(nameOrValue)) { return CharSetString }\n state.raise(\"Invalid property name\");\n };\n\n // UnicodePropertyName ::\n // UnicodePropertyNameCharacters\n pp$1.regexp_eatUnicodePropertyName = function(state) {\n var ch = 0;\n state.lastStringValue = \"\";\n while (isUnicodePropertyNameCharacter(ch = state.current())) {\n state.lastStringValue += codePointToString(ch);\n state.advance();\n }\n return state.lastStringValue !== \"\"\n };\n\n function isUnicodePropertyNameCharacter(ch) {\n return isControlLetter(ch) || ch === 0x5F /* _ */\n }\n\n // UnicodePropertyValue ::\n // UnicodePropertyValueCharacters\n pp$1.regexp_eatUnicodePropertyValue = function(state) {\n var ch = 0;\n state.lastStringValue = \"\";\n while (isUnicodePropertyValueCharacter(ch = state.current())) {\n state.lastStringValue += codePointToString(ch);\n state.advance();\n }\n return state.lastStringValue !== \"\"\n };\n function isUnicodePropertyValueCharacter(ch) {\n return isUnicodePropertyNameCharacter(ch) || isDecimalDigit(ch)\n }\n\n // LoneUnicodePropertyNameOrValue ::\n // UnicodePropertyValueCharacters\n pp$1.regexp_eatLoneUnicodePropertyNameOrValue = function(state) {\n return this.regexp_eatUnicodePropertyValue(state)\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-CharacterClass\n pp$1.regexp_eatCharacterClass = function(state) {\n if (state.eat(0x5B /* [ */)) {\n var negate = state.eat(0x5E /* ^ */);\n var result = this.regexp_classContents(state);\n if (!state.eat(0x5D /* ] */))\n { state.raise(\"Unterminated character class\"); }\n if (negate && result === CharSetString)\n { state.raise(\"Negated character class may contain strings\"); }\n return true\n }\n return false\n };\n\n // https://tc39.es/ecma262/#prod-ClassContents\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ClassRanges\n pp$1.regexp_classContents = function(state) {\n if (state.current() === 0x5D /* ] */) { return CharSetOk }\n if (state.switchV) { return this.regexp_classSetExpression(state) }\n this.regexp_nonEmptyClassRanges(state);\n return CharSetOk\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-NonemptyClassRanges\n // https://www.ecma-international.org/ecma-262/8.0/#prod-NonemptyClassRangesNoDash\n pp$1.regexp_nonEmptyClassRanges = function(state) {\n while (this.regexp_eatClassAtom(state)) {\n var left = state.lastIntValue;\n if (state.eat(0x2D /* - */) && this.regexp_eatClassAtom(state)) {\n var right = state.lastIntValue;\n if (state.switchU && (left === -1 || right === -1)) {\n state.raise(\"Invalid character class\");\n }\n if (left !== -1 && right !== -1 && left > right) {\n state.raise(\"Range out of order in character class\");\n }\n }\n }\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ClassAtom\n // https://www.ecma-international.org/ecma-262/8.0/#prod-ClassAtomNoDash\n pp$1.regexp_eatClassAtom = function(state) {\n var start = state.pos;\n\n if (state.eat(0x5C /* \\ */)) {\n if (this.regexp_eatClassEscape(state)) {\n return true\n }\n if (state.switchU) {\n // Make the same message as V8.\n var ch$1 = state.current();\n if (ch$1 === 0x63 /* c */ || isOctalDigit(ch$1)) {\n state.raise(\"Invalid class escape\");\n }\n state.raise(\"Invalid escape\");\n }\n state.pos = start;\n }\n\n var ch = state.current();\n if (ch !== 0x5D /* ] */) {\n state.lastIntValue = ch;\n state.advance();\n return true\n }\n\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ClassEscape\n pp$1.regexp_eatClassEscape = function(state) {\n var start = state.pos;\n\n if (state.eat(0x62 /* b */)) {\n state.lastIntValue = 0x08; /* */\n return true\n }\n\n if (state.switchU && state.eat(0x2D /* - */)) {\n state.lastIntValue = 0x2D; /* - */\n return true\n }\n\n if (!state.switchU && state.eat(0x63 /* c */)) {\n if (this.regexp_eatClassControlLetter(state)) {\n return true\n }\n state.pos = start;\n }\n\n return (\n this.regexp_eatCharacterClassEscape(state) ||\n this.regexp_eatCharacterEscape(state)\n )\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetExpression\n // https://tc39.es/ecma262/#prod-ClassUnion\n // https://tc39.es/ecma262/#prod-ClassIntersection\n // https://tc39.es/ecma262/#prod-ClassSubtraction\n pp$1.regexp_classSetExpression = function(state) {\n var result = CharSetOk, subResult;\n if (this.regexp_eatClassSetRange(state)) ; else if (subResult = this.regexp_eatClassSetOperand(state)) {\n if (subResult === CharSetString) { result = CharSetString; }\n // https://tc39.es/ecma262/#prod-ClassIntersection\n var start = state.pos;\n while (state.eatChars([0x26, 0x26] /* && */)) {\n if (\n state.current() !== 0x26 /* & */ &&\n (subResult = this.regexp_eatClassSetOperand(state))\n ) {\n if (subResult !== CharSetString) { result = CharSetOk; }\n continue\n }\n state.raise(\"Invalid character in character class\");\n }\n if (start !== state.pos) { return result }\n // https://tc39.es/ecma262/#prod-ClassSubtraction\n while (state.eatChars([0x2D, 0x2D] /* -- */)) {\n if (this.regexp_eatClassSetOperand(state)) { continue }\n state.raise(\"Invalid character in character class\");\n }\n if (start !== state.pos) { return result }\n } else {\n state.raise(\"Invalid character in character class\");\n }\n // https://tc39.es/ecma262/#prod-ClassUnion\n for (;;) {\n if (this.regexp_eatClassSetRange(state)) { continue }\n subResult = this.regexp_eatClassSetOperand(state);\n if (!subResult) { return result }\n if (subResult === CharSetString) { result = CharSetString; }\n }\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetRange\n pp$1.regexp_eatClassSetRange = function(state) {\n var start = state.pos;\n if (this.regexp_eatClassSetCharacter(state)) {\n var left = state.lastIntValue;\n if (state.eat(0x2D /* - */) && this.regexp_eatClassSetCharacter(state)) {\n var right = state.lastIntValue;\n if (left !== -1 && right !== -1 && left > right) {\n state.raise(\"Range out of order in character class\");\n }\n return true\n }\n state.pos = start;\n }\n return false\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetOperand\n pp$1.regexp_eatClassSetOperand = function(state) {\n if (this.regexp_eatClassSetCharacter(state)) { return CharSetOk }\n return this.regexp_eatClassStringDisjunction(state) || this.regexp_eatNestedClass(state)\n };\n\n // https://tc39.es/ecma262/#prod-NestedClass\n pp$1.regexp_eatNestedClass = function(state) {\n var start = state.pos;\n if (state.eat(0x5B /* [ */)) {\n var negate = state.eat(0x5E /* ^ */);\n var result = this.regexp_classContents(state);\n if (state.eat(0x5D /* ] */)) {\n if (negate && result === CharSetString) {\n state.raise(\"Negated character class may contain strings\");\n }\n return result\n }\n state.pos = start;\n }\n if (state.eat(0x5C /* \\ */)) {\n var result$1 = this.regexp_eatCharacterClassEscape(state);\n if (result$1) {\n return result$1\n }\n state.pos = start;\n }\n return null\n };\n\n // https://tc39.es/ecma262/#prod-ClassStringDisjunction\n pp$1.regexp_eatClassStringDisjunction = function(state) {\n var start = state.pos;\n if (state.eatChars([0x5C, 0x71] /* \\q */)) {\n if (state.eat(0x7B /* { */)) {\n var result = this.regexp_classStringDisjunctionContents(state);\n if (state.eat(0x7D /* } */)) {\n return result\n }\n } else {\n // Make the same message as V8.\n state.raise(\"Invalid escape\");\n }\n state.pos = start;\n }\n return null\n };\n\n // https://tc39.es/ecma262/#prod-ClassStringDisjunctionContents\n pp$1.regexp_classStringDisjunctionContents = function(state) {\n var result = this.regexp_classString(state);\n while (state.eat(0x7C /* | */)) {\n if (this.regexp_classString(state) === CharSetString) { result = CharSetString; }\n }\n return result\n };\n\n // https://tc39.es/ecma262/#prod-ClassString\n // https://tc39.es/ecma262/#prod-NonEmptyClassString\n pp$1.regexp_classString = function(state) {\n var count = 0;\n while (this.regexp_eatClassSetCharacter(state)) { count++; }\n return count === 1 ? CharSetOk : CharSetString\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetCharacter\n pp$1.regexp_eatClassSetCharacter = function(state) {\n var start = state.pos;\n if (state.eat(0x5C /* \\ */)) {\n if (\n this.regexp_eatCharacterEscape(state) ||\n this.regexp_eatClassSetReservedPunctuator(state)\n ) {\n return true\n }\n if (state.eat(0x62 /* b */)) {\n state.lastIntValue = 0x08; /* */\n return true\n }\n state.pos = start;\n return false\n }\n var ch = state.current();\n if (ch < 0 || ch === state.lookahead() && isClassSetReservedDoublePunctuatorCharacter(ch)) { return false }\n if (isClassSetSyntaxCharacter(ch)) { return false }\n state.advance();\n state.lastIntValue = ch;\n return true\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetReservedDoublePunctuator\n function isClassSetReservedDoublePunctuatorCharacter(ch) {\n return (\n ch === 0x21 /* ! */ ||\n ch >= 0x23 /* # */ && ch <= 0x26 /* & */ ||\n ch >= 0x2A /* * */ && ch <= 0x2C /* , */ ||\n ch === 0x2E /* . */ ||\n ch >= 0x3A /* : */ && ch <= 0x40 /* @ */ ||\n ch === 0x5E /* ^ */ ||\n ch === 0x60 /* ` */ ||\n ch === 0x7E /* ~ */\n )\n }\n\n // https://tc39.es/ecma262/#prod-ClassSetSyntaxCharacter\n function isClassSetSyntaxCharacter(ch) {\n return (\n ch === 0x28 /* ( */ ||\n ch === 0x29 /* ) */ ||\n ch === 0x2D /* - */ ||\n ch === 0x2F /* / */ ||\n ch >= 0x5B /* [ */ && ch <= 0x5D /* ] */ ||\n ch >= 0x7B /* { */ && ch <= 0x7D /* } */\n )\n }\n\n // https://tc39.es/ecma262/#prod-ClassSetReservedPunctuator\n pp$1.regexp_eatClassSetReservedPunctuator = function(state) {\n var ch = state.current();\n if (isClassSetReservedPunctuator(ch)) {\n state.lastIntValue = ch;\n state.advance();\n return true\n }\n return false\n };\n\n // https://tc39.es/ecma262/#prod-ClassSetReservedPunctuator\n function isClassSetReservedPunctuator(ch) {\n return (\n ch === 0x21 /* ! */ ||\n ch === 0x23 /* # */ ||\n ch === 0x25 /* % */ ||\n ch === 0x26 /* & */ ||\n ch === 0x2C /* , */ ||\n ch === 0x2D /* - */ ||\n ch >= 0x3A /* : */ && ch <= 0x3E /* > */ ||\n ch === 0x40 /* @ */ ||\n ch === 0x60 /* ` */ ||\n ch === 0x7E /* ~ */\n )\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-ClassControlLetter\n pp$1.regexp_eatClassControlLetter = function(state) {\n var ch = state.current();\n if (isDecimalDigit(ch) || ch === 0x5F /* _ */) {\n state.lastIntValue = ch % 0x20;\n state.advance();\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-HexEscapeSequence\n pp$1.regexp_eatHexEscapeSequence = function(state) {\n var start = state.pos;\n if (state.eat(0x78 /* x */)) {\n if (this.regexp_eatFixedHexDigits(state, 2)) {\n return true\n }\n if (state.switchU) {\n state.raise(\"Invalid escape\");\n }\n state.pos = start;\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-DecimalDigits\n pp$1.regexp_eatDecimalDigits = function(state) {\n var start = state.pos;\n var ch = 0;\n state.lastIntValue = 0;\n while (isDecimalDigit(ch = state.current())) {\n state.lastIntValue = 10 * state.lastIntValue + (ch - 0x30 /* 0 */);\n state.advance();\n }\n return state.pos !== start\n };\n function isDecimalDigit(ch) {\n return ch >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-HexDigits\n pp$1.regexp_eatHexDigits = function(state) {\n var start = state.pos;\n var ch = 0;\n state.lastIntValue = 0;\n while (isHexDigit(ch = state.current())) {\n state.lastIntValue = 16 * state.lastIntValue + hexToInt(ch);\n state.advance();\n }\n return state.pos !== start\n };\n function isHexDigit(ch) {\n return (\n (ch >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */) ||\n (ch >= 0x41 /* A */ && ch <= 0x46 /* F */) ||\n (ch >= 0x61 /* a */ && ch <= 0x66 /* f */)\n )\n }\n function hexToInt(ch) {\n if (ch >= 0x41 /* A */ && ch <= 0x46 /* F */) {\n return 10 + (ch - 0x41 /* A */)\n }\n if (ch >= 0x61 /* a */ && ch <= 0x66 /* f */) {\n return 10 + (ch - 0x61 /* a */)\n }\n return ch - 0x30 /* 0 */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-annexB-LegacyOctalEscapeSequence\n // Allows only 0-377(octal) i.e. 0-255(decimal).\n pp$1.regexp_eatLegacyOctalEscapeSequence = function(state) {\n if (this.regexp_eatOctalDigit(state)) {\n var n1 = state.lastIntValue;\n if (this.regexp_eatOctalDigit(state)) {\n var n2 = state.lastIntValue;\n if (n1 <= 3 && this.regexp_eatOctalDigit(state)) {\n state.lastIntValue = n1 * 64 + n2 * 8 + state.lastIntValue;\n } else {\n state.lastIntValue = n1 * 8 + n2;\n }\n } else {\n state.lastIntValue = n1;\n }\n return true\n }\n return false\n };\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-OctalDigit\n pp$1.regexp_eatOctalDigit = function(state) {\n var ch = state.current();\n if (isOctalDigit(ch)) {\n state.lastIntValue = ch - 0x30; /* 0 */\n state.advance();\n return true\n }\n state.lastIntValue = 0;\n return false\n };\n function isOctalDigit(ch) {\n return ch >= 0x30 /* 0 */ && ch <= 0x37 /* 7 */\n }\n\n // https://www.ecma-international.org/ecma-262/8.0/#prod-Hex4Digits\n // https://www.ecma-international.org/ecma-262/8.0/#prod-HexDigit\n // And HexDigit HexDigit in https://www.ecma-international.org/ecma-262/8.0/#prod-HexEscapeSequence\n pp$1.regexp_eatFixedHexDigits = function(state, length) {\n var start = state.pos;\n state.lastIntValue = 0;\n for (var i = 0; i < length; ++i) {\n var ch = state.current();\n if (!isHexDigit(ch)) {\n state.pos = start;\n return false\n }\n state.lastIntValue = 16 * state.lastIntValue + hexToInt(ch);\n state.advance();\n }\n return true\n };\n\n // Object type used to represent tokens. Note that normally, tokens\n // simply exist as properties on the parser object. This is only\n // used for the onToken callback and the external tokenizer.\n\n var Token = function Token(p) {\n this.type = p.type;\n this.value = p.value;\n this.start = p.start;\n this.end = p.end;\n if (p.options.locations)\n { this.loc = new SourceLocation(p, p.startLoc, p.endLoc); }\n if (p.options.ranges)\n { this.range = [p.start, p.end]; }\n };\n\n // ## Tokenizer\n\n var pp = Parser.prototype;\n\n // Move to the next token\n\n pp.next = function(ignoreEscapeSequenceInKeyword) {\n if (!ignoreEscapeSequenceInKeyword && this.type.keyword && this.containsEsc)\n { this.raiseRecoverable(this.start, \"Escape sequence in keyword \" + this.type.keyword); }\n if (this.options.onToken)\n { this.options.onToken(new Token(this)); }\n\n this.lastTokEnd = this.end;\n this.lastTokStart = this.start;\n this.lastTokEndLoc = this.endLoc;\n this.lastTokStartLoc = this.startLoc;\n this.nextToken();\n };\n\n pp.getToken = function() {\n this.next();\n return new Token(this)\n };\n\n // If we're in an ES6 environment, make parsers iterable\n if (typeof Symbol !== \"undefined\")\n { pp[Symbol.iterator] = function() {\n var this$1$1 = this;\n\n return {\n next: function () {\n var token = this$1$1.getToken();\n return {\n done: token.type === types$1.eof,\n value: token\n }\n }\n }\n }; }\n\n // Toggle strict mode. Re-reads the next number or string to please\n // pedantic tests (`\"use strict\"; 010;` should fail).\n\n // Read a single token, updating the parser object's token-related\n // properties.\n\n pp.nextToken = function() {\n var curContext = this.curContext();\n if (!curContext || !curContext.preserveSpace) { this.skipSpace(); }\n\n this.start = this.pos;\n if (this.options.locations) { this.startLoc = this.curPosition(); }\n if (this.pos >= this.input.length) { return this.finishToken(types$1.eof) }\n\n if (curContext.override) { return curContext.override(this) }\n else { this.readToken(this.fullCharCodeAtPos()); }\n };\n\n pp.readToken = function(code) {\n // Identifier or keyword. '\\uXXXX' sequences are allowed in\n // identifiers, so '\\' also dispatches to that.\n if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\\' */)\n { return this.readWord() }\n\n return this.getTokenFromCode(code)\n };\n\n pp.fullCharCodeAt = function(pos) {\n var code = this.input.charCodeAt(pos);\n if (code <= 0xd7ff || code >= 0xdc00) { return code }\n var next = this.input.charCodeAt(pos + 1);\n return next <= 0xdbff || next >= 0xe000 ? code : (code << 10) + next - 0x35fdc00\n };\n\n pp.fullCharCodeAtPos = function() {\n return this.fullCharCodeAt(this.pos)\n };\n\n pp.skipBlockComment = function() {\n var startLoc = this.options.onComment && this.curPosition();\n var start = this.pos, end = this.input.indexOf(\"*/\", this.pos += 2);\n if (end === -1) { this.raise(this.pos - 2, \"Unterminated comment\"); }\n this.pos = end + 2;\n if (this.options.locations) {\n for (var nextBreak = (void 0), pos = start; (nextBreak = nextLineBreak(this.input, pos, this.pos)) > -1;) {\n ++this.curLine;\n pos = this.lineStart = nextBreak;\n }\n }\n if (this.options.onComment)\n { this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos,\n startLoc, this.curPosition()); }\n };\n\n pp.skipLineComment = function(startSkip) {\n var start = this.pos;\n var startLoc = this.options.onComment && this.curPosition();\n var ch = this.input.charCodeAt(this.pos += startSkip);\n while (this.pos < this.input.length && !isNewLine(ch)) {\n ch = this.input.charCodeAt(++this.pos);\n }\n if (this.options.onComment)\n { this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos,\n startLoc, this.curPosition()); }\n };\n\n // Called at the start of the parse and after every token. Skips\n // whitespace and comments, and.\n\n pp.skipSpace = function() {\n loop: while (this.pos < this.input.length) {\n var ch = this.input.charCodeAt(this.pos);\n switch (ch) {\n case 32: case 160: // ' '\n ++this.pos;\n break\n case 13:\n if (this.input.charCodeAt(this.pos + 1) === 10) {\n ++this.pos;\n }\n case 10: case 8232: case 8233:\n ++this.pos;\n if (this.options.locations) {\n ++this.curLine;\n this.lineStart = this.pos;\n }\n break\n case 47: // '/'\n switch (this.input.charCodeAt(this.pos + 1)) {\n case 42: // '*'\n this.skipBlockComment();\n break\n case 47:\n this.skipLineComment(2);\n break\n default:\n break loop\n }\n break\n default:\n if (ch > 8 && ch < 14 || ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {\n ++this.pos;\n } else {\n break loop\n }\n }\n }\n };\n\n // Called at the end of every token. Sets `end`, `val`, and\n // maintains `context` and `exprAllowed`, and skips the space after\n // the token, so that the next one's `start` will point at the\n // right position.\n\n pp.finishToken = function(type, val) {\n this.end = this.pos;\n if (this.options.locations) { this.endLoc = this.curPosition(); }\n var prevType = this.type;\n this.type = type;\n this.value = val;\n\n this.updateContext(prevType);\n };\n\n // ### Token reading\n\n // This is the function that is called to fetch the next token. It\n // is somewhat obscure, because it works in character codes rather\n // than characters, and because operator parsing has been inlined\n // into it.\n //\n // All in the name of speed.\n //\n pp.readToken_dot = function() {\n var next = this.input.charCodeAt(this.pos + 1);\n if (next >= 48 && next <= 57) { return this.readNumber(true) }\n var next2 = this.input.charCodeAt(this.pos + 2);\n if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.'\n this.pos += 3;\n return this.finishToken(types$1.ellipsis)\n } else {\n ++this.pos;\n return this.finishToken(types$1.dot)\n }\n };\n\n pp.readToken_slash = function() { // '/'\n var next = this.input.charCodeAt(this.pos + 1);\n if (this.exprAllowed) { ++this.pos; return this.readRegexp() }\n if (next === 61) { return this.finishOp(types$1.assign, 2) }\n return this.finishOp(types$1.slash, 1)\n };\n\n pp.readToken_mult_modulo_exp = function(code) { // '%*'\n var next = this.input.charCodeAt(this.pos + 1);\n var size = 1;\n var tokentype = code === 42 ? types$1.star : types$1.modulo;\n\n // exponentiation operator ** and **=\n if (this.options.ecmaVersion >= 7 && code === 42 && next === 42) {\n ++size;\n tokentype = types$1.starstar;\n next = this.input.charCodeAt(this.pos + 2);\n }\n\n if (next === 61) { return this.finishOp(types$1.assign, size + 1) }\n return this.finishOp(tokentype, size)\n };\n\n pp.readToken_pipe_amp = function(code) { // '|&'\n var next = this.input.charCodeAt(this.pos + 1);\n if (next === code) {\n if (this.options.ecmaVersion >= 12) {\n var next2 = this.input.charCodeAt(this.pos + 2);\n if (next2 === 61) { return this.finishOp(types$1.assign, 3) }\n }\n return this.finishOp(code === 124 ? types$1.logicalOR : types$1.logicalAND, 2)\n }\n if (next === 61) { return this.finishOp(types$1.assign, 2) }\n return this.finishOp(code === 124 ? types$1.bitwiseOR : types$1.bitwiseAND, 1)\n };\n\n pp.readToken_caret = function() { // '^'\n var next = this.input.charCodeAt(this.pos + 1);\n if (next === 61) { return this.finishOp(types$1.assign, 2) }\n return this.finishOp(types$1.bitwiseXOR, 1)\n };\n\n pp.readToken_plus_min = function(code) { // '+-'\n var next = this.input.charCodeAt(this.pos + 1);\n if (next === code) {\n if (next === 45 && !this.inModule && this.input.charCodeAt(this.pos + 2) === 62 &&\n (this.lastTokEnd === 0 || lineBreak.test(this.input.slice(this.lastTokEnd, this.pos)))) {\n // A `-->` line comment\n this.skipLineComment(3);\n this.skipSpace();\n return this.nextToken()\n }\n return this.finishOp(types$1.incDec, 2)\n }\n if (next === 61) { return this.finishOp(types$1.assign, 2) }\n return this.finishOp(types$1.plusMin, 1)\n };\n\n pp.readToken_lt_gt = function(code) { // '<>'\n var next = this.input.charCodeAt(this.pos + 1);\n var size = 1;\n if (next === code) {\n size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2;\n if (this.input.charCodeAt(this.pos + size) === 61) { return this.finishOp(types$1.assign, size + 1) }\n return this.finishOp(types$1.bitShift, size)\n }\n if (next === 33 && code === 60 && !this.inModule && this.input.charCodeAt(this.pos + 2) === 45 &&\n this.input.charCodeAt(this.pos + 3) === 45) {\n // `` line comment\n this.skipLineComment(3);\n this.skipSpace();\n return this.nextToken()\n }\n return this.finishOp(types$1.incDec, 2)\n }\n if (next === 61) { return this.finishOp(types$1.assign, 2) }\n return this.finishOp(types$1.plusMin, 1)\n };\n\n pp.readToken_lt_gt = function(code) { // \'<>\'\n var next = this.input.charCodeAt(this.pos + 1);\n var size = 1;\n if (next === code) {\n size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2;\n if (this.input.charCodeAt(this.pos + size) === 61) { return this.finishOp(types$1.assign, size + 1) }\n return this.finishOp(types$1.bitShift, size)\n }\n if (next === 33 && code === 60 && !this.inModule && this.input.charCodeAt(this.pos + 2) === 45 &&\n this.input.charCodeAt(this.pos + 3) === 45) {\n // `` line comment\n this.skipLineComment(3);\n this.skipSpace();\n return this.nextToken()\n }\n return this.finishOp(types$1.incDec, 2)\n }\n if (next === 61) { return this.finishOp(types$1.assign, 2) }\n return this.finishOp(types$1.plusMin, 1)\n };\n\n pp.readToken_lt_gt = function(code) { // \'<>\'\n var next = this.input.charCodeAt(this.pos + 1);\n var size = 1;\n if (next === code) {\n size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2;\n if (this.input.charCodeAt(this.pos + size) === 61) { return this.finishOp(types$1.assign, size + 1) }\n return this.finishOp(types$1.bitShift, size)\n }\n if (next === 33 && code === 60 && !this.inModule && this.input.charCodeAt(this.pos + 2) === 45 &&\n this.input.charCodeAt(this.pos + 3) === 45) {\n // `\n'); -}; - -process.on('uncaughtException', (e) => { - Error.prepareStackTrace = origPrepareStackTrace; - throw e; -}); - -const tests = [ - { - // test .load for a file that throws - command: `.load ${fixtures.path('repl-pretty-stack.js')}`, - expected: 'Uncaught Error: Whoops!--->\nREPL1:*:*--->\nd (REPL1:*:*)' + - '--->\nc (REPL1:*:*)--->\nb (REPL1:*:*)--->\na (REPL1:*:*)\n' - }, - { - command: 'let x y;', - expected: /let x y;\n {6}\^\n\nUncaught SyntaxError: Unexpected identifier.*\n/ - }, - { - command: 'throw new Error(\'Whoops!\')', - expected: 'Uncaught Error: Whoops!\n' - }, - { - command: 'foo = bar;', - expected: 'Uncaught ReferenceError: bar is not defined\n' - }, - // test anonymous IIFE - { - command: '(function() { throw new Error(\'Whoops!\'); })()', - expected: 'Uncaught Error: Whoops!--->\nREPL5:*:*\n' - }, -]; - -tests.forEach(run); - -// Verify that the stack can be generated when Error.prepareStackTrace is deleted. -delete Error.prepareStackTrace; -run({ - command: 'throw new TypeError(\'Whoops!\')', - expected: 'Uncaught TypeError: Whoops!\n' -}); diff --git a/test/js/node/test/parallel/test-repl-preview.js b/test/js/node/test/parallel/test-repl-preview.js deleted file mode 100644 index 9ab84b5c9f3a..000000000000 --- a/test/js/node/test/parallel/test-repl-preview.js +++ /dev/null @@ -1,272 +0,0 @@ -'use strict'; - -const common = require('../common'); -const assert = require('assert'); -const events = require('events'); -const { REPLServer } = require('repl'); -const { Stream } = require('stream'); -const { inspect } = require('util'); - -common.skipIfInspectorDisabled(); - -// Ignore terminal settings. This is so the test can be run intact if TERM=dumb. -process.env.TERM = ''; -const PROMPT = 'repl > '; - -class REPLStream extends Stream { - readable = true; - writable = true; - - constructor() { - super(); - this.lines = ['']; - } - run(data) { - for (const entry of data) { - this.emit('data', entry); - } - this.emit('data', '\n'); - } - write(chunk) { - const chunkLines = chunk.toString('utf8').split('\n'); - this.lines[this.lines.length - 1] += chunkLines[0]; - if (chunkLines.length > 1) { - this.lines.push(...chunkLines.slice(1)); - } - this.emit('line', this.lines[this.lines.length - 1]); - return true; - } - async wait() { - this.lines = ['']; - for await (const [line] of events.on(this, 'line')) { - if (line.includes(PROMPT)) { - return this.lines; - } - } - } - pause() {} - resume() {} -} - -function runAndWait(cmds, repl) { - const promise = repl.inputStream.wait(); - for (const cmd of cmds) { - repl.inputStream.run(cmd); - } - return promise; -} - -async function tests(options) { - const repl = new REPLServer({ - prompt: PROMPT, - stream: new REPLStream(), - ignoreUndefined: true, - useColors: true, - ...options - }); - - repl.inputStream.run([ - 'function foo(x) { return x; }', - 'function koo() { console.log("abc"); }', - 'a = undefined;', - ]); - - const testCases = [{ - input: 'foo', - noPreview: '[Function: foo]', - preview: [ - 'foo', - '\x1B[90m[Function: foo]\x1B[39m\x1B[11G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', - '\x1B[36m[Function: foo]\x1B[39m', - ] - }, { - input: 'koo', - noPreview: '[Function: koo]', - preview: [ - 'k\x1B[90moo\x1B[39m\x1B[9G', - '\x1B[90m[Function: koo]\x1B[39m\x1B[9G\x1B[1A\x1B[1B\x1B[2K\x1B[1A' + - '\x1B[0Ko\x1B[90mo\x1B[39m\x1B[10G', - '\x1B[90m[Function: koo]\x1B[39m\x1B[10G\x1B[1A\x1B[1B\x1B[2K\x1B[1A' + - '\x1B[0Ko', - '\x1B[90m[Function: koo]\x1B[39m\x1B[11G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', - '\x1B[36m[Function: koo]\x1B[39m', - ] - }, { - input: 'a', - noPreview: 'repl > ', // No "undefined" output. - preview: ['a\r'] // No "undefined" preview. - }, { - input: " { b: 1 }['b'] === 1", - noPreview: '\x1B[33mtrue\x1B[39m', - preview: [ - " { b: 1 }['b']", - '\x1B[90m1\x1B[39m\x1B[22G\x1B[1A\x1B[1B\x1B[2K\x1B[1A ', - '\x1B[90m1\x1B[39m\x1B[23G\x1B[1A\x1B[1B\x1B[2K\x1B[1A=== 1', - '\x1B[90mtrue\x1B[39m\x1B[28G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', - '\x1B[33mtrue\x1B[39m', - ] - }, { - input: "{ b: 1 }['b'] === 1;", - noPreview: '\x1B[33mfalse\x1B[39m', - preview: [ - "{ b: 1 }['b']", - '\x1B[90m1\x1B[39m\x1B[21G\x1B[1A\x1B[1B\x1B[2K\x1B[1A ', - '\x1B[90m1\x1B[39m\x1B[22G\x1B[1A\x1B[1B\x1B[2K\x1B[1A=== 1', - '\x1B[90mtrue\x1B[39m\x1B[27G\x1B[1A\x1B[1B\x1B[2K\x1B[1A;', - '\x1B[90mfalse\x1B[39m\x1B[28G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', - '\x1B[33mfalse\x1B[39m', - ] - }, { - input: '{ a: true }', - noPreview: '{ a: \x1B[33mtrue\x1B[39m }', - preview: [ - '{ a: tru\x1B[90me\x1B[39m\x1B[16G\x1B[0Ke }\r', - '{ a: \x1B[33mtrue\x1B[39m }', - ] - }, { - input: '{ a: true };', - noPreview: '\x1B[33mtrue\x1B[39m', - preview: [ - '{ a: tru\x1B[90me\x1B[39m\x1B[16G\x1B[0Ke };', - '\x1B[90mtrue\x1B[39m\x1B[20G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', - '\x1B[33mtrue\x1B[39m', - ] - }, { - input: ' \t { a: true};', - noPreview: '\x1B[33mtrue\x1B[39m', - preview: [ - ' { a: tru\x1B[90me\x1B[39m\x1B[18G\x1B[0Ke}', - '\x1B[90m{ a: true }\x1B[39m\x1B[20G\x1B[1A\x1B[1B\x1B[2K\x1B[1A;', - '\x1B[90mtrue\x1B[39m\x1B[21G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', - '\x1B[33mtrue\x1B[39m', - ] - }, { - input: '1n + 2n', - noPreview: '\x1B[33m3n\x1B[39m', - preview: [ - '1n + 2', - '\x1B[90mType[39m\x1B[14G\x1B[1A\x1B[1B\x1B[2K\x1B[1An', - '\x1B[90m3n\x1B[39m\x1B[15G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', - '\x1B[33m3n\x1B[39m', - ] - }, { - input: '{};1', - noPreview: '\x1B[33m1\x1B[39m', - preview: [ - '{};1', - '\x1B[90m1\x1B[39m\x1B[12G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', - '\x1B[33m1\x1B[39m', - ] - }, { - input: 'aaaa', - noPreview: 'Uncaught ReferenceError: aaaa is not defined', - preview: [ - 'aaaa\r', - 'Uncaught ReferenceError: aaaa is not defined', - ] - }, { - input: '/0', - noPreview: '/0', - preview: [ - '/0\r', - '/0', - '^', - '', - 'Uncaught SyntaxError: Invalid regular expression: missing /', - ] - }, { - input: '{})', - noPreview: '{})', - preview: [ - '{})\r', - '{})', - ' ^', - '', - "Uncaught SyntaxError: Unexpected token ')'", - ], - }, { - input: "{ a: '{' }", - noPreview: "{ a: \x1B[32m'{'\x1B[39m }", - preview: [ - "{ a: '{' }\r", - "{ a: \x1B[32m'{'\x1B[39m }", - ], - }, { - input: "{'{':0}", - noPreview: "{ \x1B[32m'{'\x1B[39m: \x1B[33m0\x1B[39m }", - preview: [ - "{'{':0}", - "\x1B[90m{ '{': 0 }\x1B[39m\x1B[15G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r", - "{ \x1B[32m'{'\x1B[39m: \x1B[33m0\x1B[39m }", - ], - }, { - input: '{[Symbol.for("{")]: 0 }', - noPreview: '{ \x1B[32mSymbol({)\x1B[39m: \x1B[33m0\x1B[39m }', - preview: [ - '{[Symbol.for("{")]: 0 }\r', - '{ \x1B[32mSymbol({)\x1B[39m: \x1B[33m0\x1B[39m }', - ], - }, { - input: '{},{}', - noPreview: '{}', - preview: [ - '{},{}', - '\x1B[90m{}\x1B[39m\x1B[13G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', - '{}', - ], - }, { - input: '{} //', - noPreview: 'repl > ', - preview: [ - '{} //\r', - ], - }, { - input: '{} //;', - noPreview: 'repl > ', - preview: [ - '{} //;\r', - ], - }, { - input: '{throw 0}', - noPreview: 'Uncaught \x1B[33m0\x1B[39m', - preview: [ - '{throw 0}', - '\x1B[90m0\x1B[39m\x1B[17G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', - 'Uncaught \x1B[33m0\x1B[39m', - ], - }]; - - const hasPreview = repl.terminal && - (options.preview !== undefined ? !!options.preview : true); - - for (const { input, noPreview, preview } of testCases) { - console.log(`Testing ${input}`); - - const toBeRun = input.split('\n'); - let lines = await runAndWait(toBeRun, repl); - - if (hasPreview) { - // Remove error messages. That allows the code to run in different - // engines. - // eslint-disable-next-line no-control-regex - lines = lines.map((line) => line.replace(/Error: .+?\x1B/, '')); - assert.strictEqual(lines.pop(), '\x1B[1G\x1B[0Jrepl > \x1B[8G'); - assert.deepStrictEqual(lines, preview); - } else { - assert.ok(lines[0].includes(noPreview), lines.map(inspect)); - if (preview.length !== 1 || preview[0] !== `${input}\r`) { - if (preview[preview.length - 1].includes('Uncaught SyntaxError')) { - assert.strictEqual(lines.length, 5); - } else { - assert.strictEqual(lines.length, 2); - } - } - } - } -} - -tests({ terminal: false }); // No preview -tests({ terminal: true }); // Preview -tests({ terminal: false, preview: false }); // No preview -tests({ terminal: false, preview: true }); // No preview -tests({ terminal: true, preview: true }); // Preview diff --git a/test/js/node/test/parallel/test-repl-require.js b/test/js/node/test/parallel/test-repl-require.js deleted file mode 100644 index e740acef08b0..000000000000 --- a/test/js/node/test/parallel/test-repl-require.js +++ /dev/null @@ -1,73 +0,0 @@ -'use strict'; - -const common = require('../common'); -const fixtures = require('../common/fixtures'); -const assert = require('assert'); -const net = require('net'); -const { isMainThread } = require('worker_threads'); - -if (!isMainThread) { - common.skip('process.chdir is not available in Workers'); -} - -process.chdir(fixtures.fixturesDir); -const repl = require('repl'); - -{ - const server = net.createServer((conn) => { - repl.start('', conn).on('exit', () => { - conn.destroy(); - server.close(); - }); - }); - - const host = common.localhostIPv4; - const port = 0; - const options = { host, port }; - - let answer = ''; - server.listen(options, function() { - options.port = this.address().port; - const conn = net.connect(options); - conn.setEncoding('utf8'); - conn.on('data', (data) => answer += data); - conn.write('require("baz")\nrequire("./baz")\n.exit\n'); - }); - - process.on('exit', function() { - assert.doesNotMatch(answer, /Cannot find module/); - assert.doesNotMatch(answer, /Error/); - assert.strictEqual(answer, '\'eye catcher\'\n\'perhaps I work\'\n'); - }); -} - -// Test for https://github.com/nodejs/node/issues/30808 -// In REPL, we shouldn't look up relative modules from 'node_modules'. -{ - const server = net.createServer((conn) => { - repl.start('', conn).on('exit', () => { - conn.destroy(); - server.close(); - }); - }); - - const host = common.localhostIPv4; - const port = 0; - const options = { host, port }; - - let answer = ''; - server.listen(options, function() { - options.port = this.address().port; - const conn = net.connect(options); - conn.setEncoding('utf8'); - conn.on('data', (data) => answer += data); - conn.write('require("./bar")\n.exit\n'); - }); - - process.on('exit', function() { - assert.match(answer, /Uncaught Error: Cannot find module '\.\/bar'/); - - assert.match(answer, /code: 'MODULE_NOT_FOUND'/); - assert.match(answer, /requireStack: \[ '' \]/); - }); -} diff --git a/test/js/node/test/parallel/test-repl-reverse-search.js b/test/js/node/test/parallel/test-repl-reverse-search.js deleted file mode 100644 index cbe848afee08..000000000000 --- a/test/js/node/test/parallel/test-repl-reverse-search.js +++ /dev/null @@ -1,365 +0,0 @@ -'use strict'; - -// Flags: --expose-internals - -const common = require('../common'); -const stream = require('stream'); -const REPL = require('internal/repl'); -const assert = require('assert'); -const fs = require('fs'); -const { inspect } = require('util'); - -if (process.env.TERM === 'dumb') { - common.skip('skipping - dumb terminal'); -} - -common.allowGlobals('aaaa'); - -const tmpdir = require('../common/tmpdir'); -tmpdir.refresh(); - -const defaultHistoryPath = tmpdir.resolve('.node_repl_history'); - -// Create an input stream specialized for testing an array of actions -class ActionStream extends stream.Stream { - run(data) { - const _iter = data[Symbol.iterator](); - const doAction = () => { - const next = _iter.next(); - if (next.done) { - // Close the repl. Note that it must have a clean prompt to do so. - setImmediate(() => { - this.emit('keypress', '', { ctrl: true, name: 'd' }); - }); - return; - } - const action = next.value; - - if (typeof action === 'object') { - this.emit('keypress', '', action); - } else { - this.emit('data', action); - } - setImmediate(doAction); - }; - doAction(); - } - resume() {} - pause() {} -} -ActionStream.prototype.readable = true; - -// Mock keys -const ENTER = { name: 'enter' }; -const UP = { name: 'up' }; -const DOWN = { name: 'down' }; -const BACKSPACE = { name: 'backspace' }; -const SEARCH_BACKWARDS = { name: 'r', ctrl: true }; -const SEARCH_FORWARDS = { name: 's', ctrl: true }; -const ESCAPE = { name: 'escape' }; -const CTRL_C = { name: 'c', ctrl: true }; -const DELETE_WORD_LEFT = { name: 'w', ctrl: true }; - -const prompt = '> '; - -// TODO(BridgeAR): Add tests for lines that exceed the maximum columns. -const tests = [ - { // Creates few history to navigate for - env: { NODE_REPL_HISTORY: defaultHistoryPath }, - test: [ - 'console.log("foo")', ENTER, - 'ab = "aaaa"', ENTER, - 'repl.repl.historyIndex', ENTER, - 'console.log("foo")', ENTER, - 'let ba = 9', ENTER, - 'ab = "aaaa"', ENTER, - '555 - 909', ENTER, - '{key : {key2 :[] }}', ENTER, - 'Array(100).fill(1)', ENTER, - ], - expected: [], - clean: false - }, - { - env: { NODE_REPL_HISTORY: defaultHistoryPath }, - showEscapeCodes: true, - checkTotal: true, - useColors: true, - test: [ - '7', // 1 - SEARCH_FORWARDS, - SEARCH_FORWARDS, // 3 - 'a', - SEARCH_BACKWARDS, // 5 - SEARCH_FORWARDS, - SEARCH_BACKWARDS, // 7 - 'a', - BACKSPACE, // 9 - DELETE_WORD_LEFT, - 'aa', // 11 - SEARCH_BACKWARDS, - SEARCH_BACKWARDS, // 13 - SEARCH_BACKWARDS, - SEARCH_BACKWARDS, // 15 - SEARCH_FORWARDS, - ESCAPE, // 17 - ENTER, - ], - // A = Cursor n up - // B = Cursor n down - // C = Cursor n forward - // D = Cursor n back - // G = Cursor to column n - // J = Erase in screen; 0 = right; 1 = left; 2 = total - // K = Erase in line; 0 = right; 1 = left; 2 = total - expected: [ - // 0. Start - '\x1B[1G', '\x1B[0J', - prompt, '\x1B[3G', - // 1. '7' - '7', - // 2. SEARCH FORWARDS - '\nfwd-i-search: _', '\x1B[1A', '\x1B[4G', - // 3. SEARCH FORWARDS - '\x1B[3G', '\x1B[0J', - '7\nfwd-i-search: _', '\x1B[1A', '\x1B[4G', - // 4. 'a' - '\x1B[3G', '\x1B[0J', - '7\nfailed-fwd-i-search: a_', '\x1B[1A', '\x1B[4G', - // 5. SEARCH BACKWARDS - '\x1B[3G', '\x1B[0J', - 'Arr\x1B[4ma\x1B[24my(100).fill(1)\nbck-i-search: a_', - '\x1B[1A', '\x1B[6G', - // 6. SEARCH FORWARDS - '\x1B[3G', '\x1B[0J', - '7\nfailed-fwd-i-search: a_', '\x1B[1A', '\x1B[4G', - // 7. SEARCH BACKWARDS - '\x1B[3G', '\x1B[0J', - 'Arr\x1B[4ma\x1B[24my(100).fill(1)\nbck-i-search: a_', - '\x1B[1A', '\x1B[6G', - // 8. 'a' - '\x1B[3G', '\x1B[0J', - 'ab = "aa\x1B[4maa\x1B[24m"\nbck-i-search: aa_', - '\x1B[1A', '\x1B[11G', - // 9. BACKSPACE - '\x1B[3G', '\x1B[0J', - 'Arr\x1B[4ma\x1B[24my(100).fill(1)\nbck-i-search: a_', - '\x1B[1A', '\x1B[6G', - // 10. DELETE WORD LEFT (works as backspace) - '\x1B[3G', '\x1B[0J', - '7\nbck-i-search: _', '\x1B[1A', '\x1B[4G', - // 11. 'a' - '\x1B[3G', '\x1B[0J', - 'Arr\x1B[4ma\x1B[24my(100).fill(1)\nbck-i-search: a_', - '\x1B[1A', '\x1B[6G', - // 11. 'aa' - continued - '\x1B[3G', '\x1B[0J', - 'ab = "aa\x1B[4maa\x1B[24m"\nbck-i-search: aa_', - '\x1B[1A', '\x1B[11G', - // 12. SEARCH BACKWARDS - '\x1B[3G', '\x1B[0J', - 'ab = "a\x1B[4maa\x1B[24ma"\nbck-i-search: aa_', - '\x1B[1A', '\x1B[10G', - // 13. SEARCH BACKWARDS - '\x1B[3G', '\x1B[0J', - 'ab = "\x1B[4maa\x1B[24maa"\nbck-i-search: aa_', - '\x1B[1A', '\x1B[9G', - // 14. SEARCH BACKWARDS - '\x1B[3G', '\x1B[0J', - '7\nfailed-bck-i-search: aa_', '\x1B[1A', '\x1B[4G', - // 15. SEARCH BACKWARDS - '\x1B[3G', '\x1B[0J', - '7\nfailed-bck-i-search: aa_', '\x1B[1A', '\x1B[4G', - // 16. SEARCH FORWARDS - '\x1B[3G', '\x1B[0J', - 'ab = "\x1B[4maa\x1B[24maa"\nfwd-i-search: aa_', - '\x1B[1A', '\x1B[9G', - // 17. ESCAPE - '\x1B[3G', '\x1B[0J', - '7', - // 18. ENTER - '\r\n', - '\x1B[33m7\x1B[39m\n', - '\x1B[1G', '\x1B[0J', - prompt, - '\x1B[3G', - '\r\n', - ], - clean: false - }, - { - env: { NODE_REPL_HISTORY: defaultHistoryPath }, - showEscapeCodes: true, - skip: !process.features.inspector, - checkTotal: true, - useColors: false, - test: [ - 'fu', // 1 - SEARCH_BACKWARDS, - '}', // 3 - SEARCH_BACKWARDS, - CTRL_C, // 5 - CTRL_C, - '1+1', // 7 - ENTER, - SEARCH_BACKWARDS, // 9 - '+', - '\r', // 11 - '2', - SEARCH_BACKWARDS, // 13 - 're', - UP, // 15 - DOWN, - SEARCH_FORWARDS, // 17 - '\n', - ], - expected: [ - '\x1B[1G', '\x1B[0J', - prompt, '\x1B[3G', - 'f', 'u', '\nbck-i-search: _', '\x1B[1A', '\x1B[5G', - '\x1B[3G', '\x1B[0J', - '{key : {key2 :[] }}\nbck-i-search: }_', '\x1B[1A', '\x1B[21G', - '\x1B[3G', '\x1B[0J', - '{key : {key2 :[] }}\nbck-i-search: }_', '\x1B[1A', '\x1B[20G', - '\x1B[3G', '\x1B[0J', - 'fu', - '\r\n', - '\x1B[1G', '\x1B[0J', - prompt, '\x1B[3G', - '1', '+', '1', '\n// 2', '\x1B[6G', '\x1B[1A', - '\x1B[1B', '\x1B[2K', '\x1B[1A', - '\r\n', - '2\n', - '\x1B[1G', '\x1B[0J', - prompt, '\x1B[3G', - '\nbck-i-search: _', '\x1B[1A', - '\x1B[3G', '\x1B[0J', - '1+1\nbck-i-search: +_', '\x1B[1A', '\x1B[4G', - '\x1B[3G', '\x1B[0J', - '1+1', '\x1B[4G', - '\x1B[2C', - '\r\n', - '2\n', - '\x1B[1G', '\x1B[0J', - prompt, '\x1B[3G', - '2', - '\nbck-i-search: _', '\x1B[1A', '\x1B[4G', - '\x1B[3G', '\x1B[0J', - 'Array(100).fill(1)\nbck-i-search: r_', '\x1B[1A', '\x1B[5G', - '\x1B[3G', '\x1B[0J', - 'repl.repl.historyIndex\nbck-i-search: re_', '\x1B[1A', '\x1B[8G', - '\x1B[3G', '\x1B[0J', - 'repl.repl.historyIndex', '\x1B[8G', - '\x1B[1G', '\x1B[0J', - `${prompt}ab = "aaaa"`, '\x1B[14G', - '\x1B[1G', '\x1B[0J', - `${prompt}repl.repl.historyIndex`, '\x1B[25G', '\n// 8', - '\x1B[25G', '\x1B[1A', - '\x1B[1B', '\x1B[2K', '\x1B[1A', - '\nfwd-i-search: _', '\x1B[1A', '\x1B[25G', - '\x1B[3G', '\x1B[0J', - 'repl.repl.historyIndex', - '\r\n', - '-1\n', - '\x1B[1G', '\x1B[0J', - prompt, '\x1B[3G', - '\r\n', - ], - clean: false - }, -]; -const numtests = tests.length; - -const runTestWrap = common.mustCall(runTest, numtests); - -function cleanupTmpFile() { - try { - // Write over the file, clearing any history - fs.writeFileSync(defaultHistoryPath, ''); - } catch (err) { - if (err.code === 'ENOENT') return true; - throw err; - } - return true; -} - -function runTest() { - const opts = tests.shift(); - if (!opts) return; // All done - - const { expected, skip } = opts; - - // Test unsupported on platform. - if (skip) { - setImmediate(runTestWrap, true); - return; - } - - const lastChunks = []; - let i = 0; - - REPL.createInternalRepl(opts.env, { - input: new ActionStream(), - output: new stream.Writable({ - write: common.mustCallAtLeast((chunk, _, next) => { - const output = chunk.toString(); - - if (!opts.showEscapeCodes && - (output[0] === '\x1B' || /^[\r\n]+$/.test(output))) { - return next(); - } - - lastChunks.push(output); - - if (expected.length && !opts.checkTotal) { - try { - assert.strictEqual(output, expected[i]); - } catch (e) { - console.error(`Failed test # ${numtests - tests.length}`); - console.error('Last outputs: ' + inspect(lastChunks, { - breakLength: 5, colors: true - })); - throw e; - } - i++; - } - - next(); - }), - }), - completer: opts.completer, - prompt, - useColors: opts.useColors || false, - terminal: true - }, common.mustCall((err, repl) => { - if (err) { - console.error(`Failed test # ${numtests - tests.length}`); - throw err; - } - - repl.once('close', common.mustCall(() => { - if (opts.clean) - cleanupTmpFile(); - - if (opts.checkTotal) { - assert.deepStrictEqual(lastChunks, expected); - } else if (expected.length !== i) { - console.error(tests[numtests - tests.length - 1]); - throw new Error(`Failed test # ${numtests - tests.length}`); - } - - setImmediate(runTestWrap, true); - })); - - if (opts.columns) { - Object.defineProperty(repl, 'columns', { - value: opts.columns, - enumerable: true - }); - } - repl.inputStream.run(opts.test); - })); -} - -// run the tests -runTest(); diff --git a/test/js/node/test/parallel/test-repl-sigint-nested-eval.js b/test/js/node/test/parallel/test-repl-sigint-nested-eval.js deleted file mode 100644 index ecc532f31ede..000000000000 --- a/test/js/node/test/parallel/test-repl-sigint-nested-eval.js +++ /dev/null @@ -1,53 +0,0 @@ -'use strict'; -const common = require('../common'); -if (common.isWindows) { - // No way to send CTRL_C_EVENT to processes from JS right now. - common.skip('platform not supported'); -} - -const { isMainThread } = require('worker_threads'); - -if (!isMainThread) { - common.skip('No signal handling available in Workers'); -} - -const assert = require('assert'); -const spawn = require('child_process').spawn; - -const child = spawn(process.execPath, [ '--interactive' ], { - stdio: [null, null, 2, 'ipc'] -}); - -let stdout = ''; -child.stdout.setEncoding('utf8'); -child.stdout.on('data', function(c) { - stdout += c; -}); - -child.stdout.once('data', common.mustCall(() => { - child.on('message', common.mustCall((msg) => { - assert.strictEqual(msg, 'repl is busy'); - process.kill(child.pid, 'SIGINT'); - child.stdout.once('data', common.mustCall(() => { - // Make sure REPL still works. - child.stdin.end('"foobar"\n'); - })); - })); - - child.stdin.write( - 'vm.runInThisContext("process.send(\'repl is busy\'); while(true){}", ' + - '{ breakOnSigint: true });\n' - ); -})); - -child.on('close', common.mustCall((code) => { - const expected = 'Script execution was interrupted by `SIGINT`'; - assert.ok( - stdout.includes(expected), - `Expected stdout to contain "${expected}", got ${stdout}` - ); - assert.ok( - stdout.includes('foobar'), - `Expected stdout to contain "foobar", got ${stdout}` - ); -})); diff --git a/test/js/node/test/parallel/test-repl-sigint.js b/test/js/node/test/parallel/test-repl-sigint.js deleted file mode 100644 index 8db02db886fd..000000000000 --- a/test/js/node/test/parallel/test-repl-sigint.js +++ /dev/null @@ -1,53 +0,0 @@ -'use strict'; -const common = require('../common'); -if (common.isWindows) { - // No way to send CTRL_C_EVENT to processes from JS right now. - common.skip('platform not supported'); -} - -const { isMainThread } = require('worker_threads'); - -if (!isMainThread) { - common.skip('No signal handling available in Workers'); -} - -const assert = require('assert'); -const spawn = require('child_process').spawn; - -process.env.REPL_TEST_PPID = process.pid; -const child = spawn(process.execPath, [ '--interactive' ], { - stdio: [null, null, 2] -}); - -let stdout = ''; -child.stdout.setEncoding('utf8'); -child.stdout.on('data', function(c) { - stdout += c; -}); - -child.stdout.once('data', common.mustCall(() => { - process.on('SIGUSR2', common.mustCall(() => { - process.kill(child.pid, 'SIGINT'); - child.stdout.once('data', common.mustCall(() => { - // Make sure state from before the interruption is still available. - child.stdin.end('a*2*3*7\n'); - })); - })); - - child.stdin.write('a = 1001;' + - 'process.kill(+process.env.REPL_TEST_PPID, "SIGUSR2");' + - 'while(true){}\n'); -})); - -child.on('close', common.mustCall((code) => { - assert.strictEqual(code, 0); - const expected = 'Script execution was interrupted by `SIGINT`'; - assert.ok( - stdout.includes(expected), - `Expected stdout to contain "${expected}", got ${stdout}` - ); - assert.ok( - stdout.includes('42042\n'), - `Expected stdout to contain "42042", got ${stdout}` - ); -})); diff --git a/test/js/node/test/parallel/test-repl-strict-mode-previews.js b/test/js/node/test/parallel/test-repl-strict-mode-previews.js deleted file mode 100644 index e7fc1ea5191e..000000000000 --- a/test/js/node/test/parallel/test-repl-strict-mode-previews.js +++ /dev/null @@ -1,50 +0,0 @@ -// Previews in strict mode should indicate ReferenceErrors. - -'use strict'; - -const common = require('../common'); - -common.skipIfInspectorDisabled(); - -if (process.env.TERM === 'dumb') { - common.skip('skipping - dumb terminal'); -} - -if (process.argv[2] === 'child') { - const stream = require('stream'); - const repl = require('repl'); - class ActionStream extends stream.Stream { - readable = true; - run(data) { - this.emit('data', `${data}`); - this.emit('keypress', '', { ctrl: true, name: 'd' }); - } - resume() {} - pause() {} - } - - repl.start({ - input: new ActionStream(), - output: new stream.Writable({ - write(chunk, _, next) { - console.log(chunk.toString()); - next(); - } - }), - useColors: false, - terminal: true - }).inputStream.run('xyz'); -} else { - const assert = require('assert'); - const { spawnSync } = require('child_process'); - - const result = spawnSync( - process.execPath, - ['--use-strict', `${__filename}`, 'child'] - ); - - assert.match( - result.stdout.toString(), - /\/\/ ReferenceError: xyz is not defined/ - ); -} diff --git a/test/js/node/test/parallel/test-repl-tab-complete-nested-repls.js b/test/js/node/test/parallel/test-repl-tab-complete-nested-repls.js deleted file mode 100644 index 3cac02f20562..000000000000 --- a/test/js/node/test/parallel/test-repl-tab-complete-nested-repls.js +++ /dev/null @@ -1,23 +0,0 @@ -// Tab completion sometimes uses a separate REPL instance under the hood. -// That REPL instance has its own domain. Make sure domain errors trickle back -// up to the main REPL. -// -// Ref: https://github.com/nodejs/node/issues/21586 - -'use strict'; - -require('../common'); -const fixtures = require('../common/fixtures'); - -const assert = require('assert'); -const { spawnSync } = require('child_process'); - -const testFile = fixtures.path('repl-tab-completion-nested-repls.js'); -const result = spawnSync(process.execPath, [testFile]); - -// The spawned process will fail. In Node.js 10.11.0, it will fail silently. The -// test here is to make sure that the error information bubbles up to the -// calling process. -assert.ok(result.status, 'testFile swallowed its error'); -const err = result.stderr.toString(); -assert.ok(err.includes('fhqwhgads'), err); diff --git a/test/js/node/test/parallel/test-repl-tab-complete-unary-expressions.js b/test/js/node/test/parallel/test-repl-tab-complete-unary-expressions.js deleted file mode 100644 index 2b09ae651d25..000000000000 --- a/test/js/node/test/parallel/test-repl-tab-complete-unary-expressions.js +++ /dev/null @@ -1,116 +0,0 @@ -'use strict'; - -const common = require('../common'); -const assert = require('assert'); -const { startNewREPLServer } = require('../common/repl'); -const { describe, it } = require('node:test'); - -// This test verifies that tab completion works correctly with unary expressions -// like delete, typeof, void, etc. This is a regression test for the issue where -// typing "delete globalThis._" and then backspacing and typing "globalThis" -// would cause "globalThis is not defined" error. - -describe('REPL tab completion with unary expressions', () => { - it('should handle delete operator correctly', (t, done) => { - const { replServer } = startNewREPLServer({ terminal: false }); - - // Test delete with member expression - replServer.complete( - 'delete globalThis._', - common.mustSucceed((completions) => { - assert.strictEqual(completions[1], 'globalThis._'); - - // Test delete with identifier - replServer.complete( - 'delete globalThis', - common.mustSucceed((completions) => { - assert.strictEqual(completions[1], 'globalThis'); - replServer.close(); - done(); - }) - ); - }) - ); - }); - - it('should handle typeof operator correctly', (t, done) => { - const { replServer } = startNewREPLServer({ terminal: false }); - - replServer.complete( - 'typeof globalThis', - common.mustSucceed((completions) => { - assert.strictEqual(completions[1], 'globalThis'); - replServer.close(); - done(); - }) - ); - }); - - it('should handle void operator correctly', (t, done) => { - const { replServer } = startNewREPLServer({ terminal: false }); - - replServer.complete( - 'void globalThis', - common.mustSucceed((completions) => { - assert.strictEqual(completions[1], 'globalThis'); - replServer.close(); - done(); - }) - ); - }); - - it('should handle other unary operators correctly', (t, done) => { - const { replServer } = startNewREPLServer({ terminal: false }); - - const unaryOperators = [ - '!globalThis', - '+globalThis', - '-globalThis', - '~globalThis', - ]; - - let testIndex = 0; - - function testNext() { - if (testIndex >= unaryOperators.length) { - replServer.close(); - done(); - return; - } - - const testCase = unaryOperators[testIndex++]; - replServer.complete( - testCase, - common.mustSucceed((completions) => { - assert.strictEqual(completions[1], 'globalThis'); - testNext(); - }) - ); - } - - testNext(); - }); - - it('should still evaluate globalThis correctly after unary expression completion', (t, done) => { - const { replServer } = startNewREPLServer({ terminal: false }); - - // First trigger completion with delete - replServer.complete( - 'delete globalThis._', - common.mustSucceed(() => { - // Then evaluate globalThis - replServer.eval( - 'globalThis', - replServer.context, - 'test.js', - common.mustSucceed((result) => { - assert.strictEqual(typeof result, 'object'); - assert.ok(result !== null); - replServer.close(); - done(); - }) - ); - }) - ); - }); -}); diff --git a/test/js/node/test/parallel/test-repl-tab-complete.js b/test/js/node/test/parallel/test-repl-tab-complete.js deleted file mode 100644 index d4df6c317879..000000000000 --- a/test/js/node/test/parallel/test-repl-tab-complete.js +++ /dev/null @@ -1,565 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// 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. - -'use strict'; - -const common = require('../common'); -const { startNewREPLServer } = require('../common/repl'); -const { describe, it } = require('node:test'); -const assert = require('assert'); - -function getNoResultsFunction() { - return common.mustSucceed((data) => { - assert.deepStrictEqual(data[0], []); - }); -} - -describe('REPL tab completion (core functionality)', () => { - it('does not break with variable declarations without an initialization', () => { - const { replServer } = startNewREPLServer(); - replServer.complete('let a', getNoResultsFunction()); - replServer.close(); - }); - - it('does not break in an object literal', () => { - const { replServer, input } = startNewREPLServer(); - - input.run(['var inner = {', 'one:1']); - - replServer.complete('inner.o', getNoResultsFunction()); - - replServer.complete( - 'console.lo', - common.mustCall(function(_error, data) { - assert.deepStrictEqual(data, [['console.log'], 'console.lo']); - }) - ); - - replServer.close(); - }); - - it('works with optional chaining', () => { - const { replServer } = startNewREPLServer(); - - replServer.complete( - 'console?.lo', - common.mustCall((_error, data) => { - assert.deepStrictEqual(data, [['console?.log'], 'console?.lo']); - }) - ); - - replServer.complete( - 'console?.zzz', - common.mustCall((_error, data) => { - assert.deepStrictEqual(data, [[], 'console?.zzz']); - }) - ); - - replServer.complete( - 'console?.', - common.mustCall((_error, data) => { - assert(data[0].includes('console?.log')); - assert.strictEqual(data[1], 'console?.'); - }) - ); - - replServer.close(); - }); - - it('returns object completions', () => { - const { replServer, input } = startNewREPLServer(); - - input.run(['var inner = {', 'one:1']); - - input.run(['};']); - - replServer.complete( - 'inner.o', - common.mustCall(function(_error, data) { - assert.deepStrictEqual(data, [['inner.one'], 'inner.o']); - }) - ); - - replServer.close(); - }); - - it('does not break in a ternary operator with ()', () => { - const { replServer, input } = startNewREPLServer(); - - input.run(['var inner = ( true ', '?', '{one: 1} : ']); - - replServer.complete('inner.o', getNoResultsFunction()); - - replServer.close(); - }); - - it('works on literals', () => { - const { replServer } = startNewREPLServer(); - - replServer.complete( - '``.a', - common.mustCall((err, data) => { - assert.strictEqual(data[0].includes('``.at'), true); - }) - ); - replServer.complete( - "''.a", - common.mustCall((err, data) => { - assert.strictEqual(data[0].includes("''.at"), true); - }) - ); - replServer.complete( - '"".a', - common.mustCall((err, data) => { - assert.strictEqual(data[0].includes('"".at'), true); - }) - ); - replServer.complete( - '("").a', - common.mustCall((err, data) => { - assert.strictEqual(data[0].includes('("").at'), true); - }) - ); - replServer.complete( - '[].a', - common.mustCall((err, data) => { - assert.strictEqual(data[0].includes('[].at'), true); - }) - ); - replServer.complete( - '{}.a', - common.mustCall((err, data) => { - assert.deepStrictEqual(data[0], []); - }) - ); - - replServer.close(); - }); - - it("does not return a function's local variable", () => { - const { replServer, input } = startNewREPLServer(); - - input.run(['var top = function() {', 'var inner = {one:1};', '}']); - - replServer.complete('inner.o', getNoResultsFunction()); - - replServer.close(); - }); - - it("does not return a function's local variable even when the function has parameters", () => { - const { replServer, input } = startNewREPLServer(); - - input.run([ - 'var top = function(one, two) {', - 'var inner = {', - ' one:1', - '};', - ]); - - replServer.complete('inner.o', getNoResultsFunction()); - - replServer.close(); - }); - - it("does not return a function's local variable" + - 'even if the scope is nested inside an immediately executed function', () => { - const { replServer, input } = startNewREPLServer(); - - input.run([ - 'var top = function() {', - '(function test () {', - 'var inner = {', - ' one:1', - '};', - ]); - - replServer.complete('inner.o', getNoResultsFunction()); - - replServer.close(); - }); - - it("does not return a function's local variable" + - 'even if the scope is nested inside an immediately executed function' + - '(the definition has the params and { on a separate line)', () => { - const { replServer, input } = startNewREPLServer(); - - input.run([ - 'var top = function() {', - 'r = function test (', - ' one, two) {', - 'var inner = {', - ' one:1', - '};', - ]); - - replServer.complete('inner.o', getNoResultsFunction()); - - replServer.close(); - }); - - it('currently does not work, but should not break (local inner)', () => { - const { replServer, input } = startNewREPLServer(); - - input.run([ - 'var top = function() {', - 'r = function test ()', - '{', - 'var inner = {', - ' one:1', - '};', - ]); - - replServer.complete('inner.o', getNoResultsFunction()); - - replServer.close(); - }); - - it('currently does not work, but should not break (local inner parens next line)', () => { - const { replServer, input } = startNewREPLServer(); - - input.run([ - 'var top = function() {', - 'r = function test (', - ')', - '{', - 'var inner = {', - ' one:1', - '};', - ]); - - replServer.complete('inner.o', getNoResultsFunction()); - - replServer.close(); - }); - - it('works on non-Objects', () => { - const { replServer, input } = startNewREPLServer(); - - input.run(['var str = "test";']); - - replServer.complete( - 'str.len', - common.mustCall(function(_error, data) { - assert.deepStrictEqual(data, [['str.length'], 'str.len']); - }) - ); - - replServer.close(); - }); - - it('should be case-insensitive if member part is lower-case', () => { - const { replServer, input } = startNewREPLServer(); - - input.run(['var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };']); - - replServer.complete( - 'foo.b', - common.mustCall(function(_error, data) { - assert.deepStrictEqual(data, [ - ['foo.BARbuz', 'foo.barBLA', 'foo.barBar'], - 'foo.b', - ]); - }) - ); - - replServer.close(); - }); - - it('should be case-insensitive if member part is upper-case', () => { - const { replServer, input } = startNewREPLServer(); - - input.run(['var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };']); - - replServer.complete( - 'foo.B', - common.mustCall(function(_error, data) { - assert.deepStrictEqual(data, [ - ['foo.BARbuz', 'foo.barBLA', 'foo.barBar'], - 'foo.B', - ]); - }) - ); - - replServer.close(); - }); - - it('should not break on spaces', () => { - const { replServer } = startNewREPLServer(); - - const spaceTimeout = setTimeout(function() { - throw new Error('timeout'); - }, 1000); - - replServer.complete( - ' ', - common.mustSucceed((data) => { - assert.strictEqual(data[1], ''); - assert.ok(data[0].includes('globalThis')); - clearTimeout(spaceTimeout); - }) - ); - - replServer.close(); - }); - - it(`should pick up the global "toString" object, and any other properties up the "global" object's prototype chain`, () => { - const { replServer } = startNewREPLServer(); - - replServer.complete( - 'toSt', - common.mustCall(function(_error, data) { - assert.deepStrictEqual(data, [['toString'], 'toSt']); - }) - ); - - replServer.close(); - }); - - it('should make own properties shadow properties on the prototype', () => { - const { replServer, input } = startNewREPLServer(); - - input.run([ - 'var x = Object.create(null);', - 'x.a = 1;', - 'x.b = 2;', - 'var y = Object.create(x);', - 'y.a = 3;', - 'y.c = 4;', - ]); - - replServer.complete( - 'y.', - common.mustCall(function(_error, data) { - assert.deepStrictEqual(data, [['y.b', '', 'y.a', 'y.c'], 'y.']); - }) - ); - - replServer.close(); - }); - - it('works on context properties', () => { - const { replServer, input } = startNewREPLServer(); - - input.run(['var custom = "test";']); - - replServer.complete( - 'cus', - common.mustCall(function(_error, data) { - assert.deepStrictEqual(data, [['CustomEvent', 'custom'], 'cus']); - }) - ); - - replServer.close(); - }); - - it("doesn't crash REPL with half-baked proxy objects", () => { - const { replServer, input } = startNewREPLServer(); - - input.run([ - 'var proxy = new Proxy({}, {ownKeys: () => { throw new Error(); }});', - ]); - - replServer.complete( - 'proxy.', - common.mustCall(function(error, data) { - assert.strictEqual(error, null); - assert(Array.isArray(data)); - }) - ); - - replServer.close(); - }); - - it('does not include integer members of an Array', () => { - const { replServer, input } = startNewREPLServer(); - - input.run(['var ary = [1,2,3];']); - - replServer.complete( - 'ary.', - common.mustCall(function(_error, data) { - assert.strictEqual(data[0].includes('ary.0'), false); - assert.strictEqual(data[0].includes('ary.1'), false); - assert.strictEqual(data[0].includes('ary.2'), false); - }) - ); - - replServer.close(); - }); - - it('does not include integer keys in an object', () => { - const { replServer, input } = startNewREPLServer(); - - input.run(['var obj = {1:"a","1a":"b",a:"b"};']); - - replServer.complete( - 'obj.', - common.mustCall(function(_error, data) { - assert.strictEqual(data[0].includes('obj.1'), false); - assert.strictEqual(data[0].includes('obj.1a'), false); - assert(data[0].includes('obj.a')); - }) - ); - - replServer.close(); - }); - - it('does not try to complete results of non-simple expressions', () => { - const { replServer, input } = startNewREPLServer(); - - input.run(['function a() {}']); - - replServer.complete('a().b.', getNoResultsFunction()); - - replServer.close(); - }); - - it('works when prefixed with spaces', () => { - const { replServer, input } = startNewREPLServer(); - - input.run(['var obj = {1:"a","1a":"b",a:"b"};']); - - replServer.complete( - ' obj.', - common.mustCall((_error, data) => { - assert.strictEqual(data[0].includes('obj.1'), false); - assert.strictEqual(data[0].includes('obj.1a'), false); - assert(data[0].includes('obj.a')); - }) - ); - - replServer.close(); - }); - - it('works inside assignments', () => { - const { replServer } = startNewREPLServer(); - - replServer.complete( - 'var log = console.lo', - common.mustCall((_error, data) => { - assert.deepStrictEqual(data, [['console.log'], 'console.lo']); - }) - ); - - replServer.close(); - }); - - it('works for defined commands', () => { - const { replServer, input } = startNewREPLServer(); - - replServer.complete( - '.b', - common.mustCall((error, data) => { - assert.deepStrictEqual(data, [['break'], 'b']); - }) - ); - - input.run(['var obj = {"hello, world!": "some string", "key": 123}']); - - replServer.complete( - 'obj.', - common.mustCall((error, data) => { - assert.strictEqual(data[0].includes('obj.hello, world!'), false); - assert(data[0].includes('obj.key')); - }) - ); - - replServer.close(); - }); - - it('does not include __defineSetter__ and friends', () => { - const { replServer, input } = startNewREPLServer(); - - input.run(['var obj = {};']); - - replServer.complete( - 'obj.', - common.mustCall(function(error, data) { - assert.strictEqual(data[0].includes('obj.__defineGetter__'), false); - assert.strictEqual(data[0].includes('obj.__defineSetter__'), false); - assert.strictEqual(data[0].includes('obj.__lookupGetter__'), false); - assert.strictEqual(data[0].includes('obj.__lookupSetter__'), false); - assert.strictEqual(data[0].includes('obj.__proto__'), true); - }) - ); - - replServer.close(); - }); - - it('works with builtin values', () => { - const { replServer } = startNewREPLServer(); - - replServer.complete( - 'I', - common.mustCall((error, data) => { - assert.deepStrictEqual(data, [ - [ - 'if', - 'import', - 'in', - 'instanceof', - '', - 'Infinity', - 'Int16Array', - 'Int32Array', - 'Int8Array', - ...(common.hasIntl ? ['Intl'] : []), - 'Iterator', - 'inspector', - 'isFinite', - 'isNaN', - '', - 'isPrototypeOf', - ], - 'I', - ]); - }) - ); - - replServer.close(); - }); - - it('works with lexically scoped variables', () => { - const { replServer, input } = startNewREPLServer(); - - input.run([ - 'let lexicalLet = true;', - 'const lexicalConst = true;', - 'class lexicalKlass {}', - ]); - - ['Let', 'Const', 'Klass'].forEach((type) => { - const query = `lexical${type[0]}`; - const hasInspector = process.features.inspector; - const expected = hasInspector ? - [[`lexical${type}`], query] : - [[], `lexical${type[0]}`]; - replServer.complete( - query, - common.mustCall((error, data) => { - assert.deepStrictEqual(data, expected); - }) - ); - }); - - replServer.close(); - }); -}); diff --git a/test/js/node/test/parallel/test-repl-top-level-await.js b/test/js/node/test/parallel/test-repl-top-level-await.js deleted file mode 100644 index a94ff8e48984..000000000000 --- a/test/js/node/test/parallel/test-repl-top-level-await.js +++ /dev/null @@ -1,230 +0,0 @@ -'use strict'; - -const common = require('../common'); -const ArrayStream = require('../common/arraystream'); -const assert = require('assert'); -const events = require('events'); -const { stripVTControlCharacters } = require('internal/util/inspect'); -const repl = require('repl'); - -common.skipIfInspectorDisabled(); - -// Flags: --expose-internals - -const PROMPT = 'await repl > '; - -class REPLStream extends ArrayStream { - constructor() { - super(); - this.waitingForResponse = false; - this.lines = ['']; - } - write(chunk, encoding, callback) { - if (Buffer.isBuffer(chunk)) { - chunk = chunk.toString(encoding); - } - const chunkLines = stripVTControlCharacters(chunk).split('\n'); - this.lines[this.lines.length - 1] += chunkLines[0]; - if (chunkLines.length > 1) { - this.lines.push(...chunkLines.slice(1)); - } - this.emit('line', this.lines[this.lines.length - 1]); - if (callback) callback(); - return true; - } - - async wait() { - if (this.waitingForResponse) { - throw new Error('Currently waiting for response to another command'); - } - this.lines = ['']; - for await (const [line] of events.on(this, 'line')) { - if (line.includes(PROMPT)) { - return this.lines; - } - } - } -} - -const putIn = new REPLStream(); -const testMe = repl.start({ - prompt: PROMPT, - stream: putIn, - terminal: true, - useColors: true, - breakEvalOnSigint: true -}); - -function runAndWait(cmds) { - const promise = putIn.wait(); - for (const cmd of cmds) { - if (typeof cmd === 'string') { - putIn.run([cmd]); - } else { - testMe.write('', cmd); - } - } - return promise; -} - -async function ordinaryTests() { - // These tests were created based on - // https://cs.chromium.org/chromium/src/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-top-level-await.js?rcl=5d0ea979f0ba87655b7ef0e03b58fa3c04986ba6 - putIn.run([ - 'function foo(x) { return x; }', - 'function koo() { return Promise.resolve(4); }', - ]); - const testCases = [ - ['await Promise.resolve(0)', '0'], - ['{ a: await Promise.resolve(1) }', '{ a: 1 }'], - ['_', '{ a: 1 }'], - ['let { aa, bb } = await Promise.resolve({ aa: 1, bb: 2 }), f = 5;'], - ['aa', '1'], - ['bb', '2'], - ['f', '5'], - ['let cc = await Promise.resolve(2)'], - ['cc', '2'], - ['let dd;'], - ['dd'], - ['let [ii, { abc: { kk } }] = [0, { abc: { kk: 1 } }];'], - ['ii', '0'], - ['kk', '1'], - ['var ll = await Promise.resolve(2);'], - ['ll', '2'], - ['foo(await koo())', '4'], - ['_', '4'], - ['const m = foo(await koo());'], - ['m', '4'], - ['const n = foo(await\nkoo());', - ['const n = foo(await\r', '| koo());\r', 'undefined']], - ['n', '4'], - // eslint-disable-next-line no-template-curly-in-string - ['`status: ${(await Promise.resolve({ status: 200 })).status}`', - "'status: 200'"], - ['for (let i = 0; i < 2; ++i) await i'], - ['for (let i = 0; i < 2; ++i) { await i }'], - ['await 0', '0'], - ['await 0; function foo() {}'], - ['foo', '[Function: foo]'], - ['class Foo {}; await 1;', '1'], - ['Foo', '[class Foo]'], - ['if (await true) { function bar() {}; }'], - ['bar', '[Function: bar]'], - ['if (await true) { class Bar {}; }'], - ['Bar', 'Uncaught ReferenceError: Bar is not defined'], - ['await 0; function* gen(){}'], - ['for (var i = 0; i < 10; ++i) { await i; }'], - ['i', '10'], - ['for (let j = 0; j < 5; ++j) { await j; }'], - ['j', 'Uncaught ReferenceError: j is not defined', { line: 0 }], - ['gen', '[GeneratorFunction: gen]'], - ['return 42; await 5;', 'Uncaught SyntaxError: Illegal return statement', - { line: 3 }], - ['let o = await 1, p'], - ['p'], - ['let q = 1, s = await 2'], - ['s', '2'], - ['for await (let i of [1,2,3]) console.log(i)', - [ - 'for await (let i of [1,2,3]) console.log(i)\r', - '1', - '2', - '3', - 'undefined', - ], - ], - ['await Promise..resolve()', - [ - 'await Promise..resolve()\r', - 'Uncaught SyntaxError: ', - 'await Promise..resolve()', - ' ^', - '', - 'Unexpected token \'.\'', - ], - ], - ['for (const x of [1,2,3]) {\nawait x\n}', [ - 'for (const x of [1,2,3]) {\r', - '| await x\r', - '| }\r', - 'undefined', - ]], - ['for (const x of [1,2,3]) {\nawait x;\n}', [ - 'for (const x of [1,2,3]) {\r', - '| await x;\r', - '| }\r', - 'undefined', - ]], - ['for await (const x of [1,2,3]) {\nconsole.log(x)\n}', [ - 'for await (const x of [1,2,3]) {\r', - '| console.log(x)\r', - '| }\r', - '1', - '2', - '3', - 'undefined', - ]], - ['for await (const x of [1,2,3]) {\nconsole.log(x);\n}', [ - 'for await (const x of [1,2,3]) {\r', - '| console.log(x);\r', - '| }\r', - '1', - '2', - '3', - 'undefined', - ]], - // Testing documented behavior of `const`s (see: https://github.com/nodejs/node/issues/45918) - ['const k = await Promise.resolve(123)'], - ['k', '123'], - ['k = await Promise.resolve(234)', '234'], - ['k', '234'], - ['const k = await Promise.resolve(345)', "Uncaught SyntaxError: Identifier 'k' has already been declared"], - // Regression test for https://github.com/nodejs/node/issues/43777. - ['await Promise.resolve(123), Promise.resolve(456)', 'Promise { 456 }'], - ['await Promise.resolve(123), await Promise.resolve(456)', '456'], - ['await (Promise.resolve(123), Promise.resolve(456))', '456'], - ]; - - for (const [input, expected = [`${input}\r`], options = {}] of testCases) { - console.log(`Testing ${input}`); - const toBeRun = input.split('\n'); - const lines = await runAndWait(toBeRun); - if (Array.isArray(expected)) { - if (expected.length === 1) - expected.push('undefined'); - if (lines[0] === input) - lines.shift(); - assert.deepStrictEqual(lines, [...expected, PROMPT]); - } else if ('line' in options) { - assert.strictEqual(lines[toBeRun.length + options.line], expected); - } else { - const echoed = toBeRun.map((a, i) => `${i > 0 ? '| ' : ''}${a}\r`); - assert.deepStrictEqual(lines, [...echoed, expected, PROMPT]); - } - } -} - -async function ctrlCTest() { - console.log('Testing Ctrl+C'); - const output = await runAndWait([ - 'await new Promise(() => {})', - { ctrl: true, name: 'c' }, - ]); - assert.deepStrictEqual(output.slice(0, 3), [ - 'await new Promise(() => {})\r', - 'Uncaught:', - '[Error [ERR_SCRIPT_EXECUTION_INTERRUPTED]: ' + - 'Script execution was interrupted by `SIGINT`] {', - ]); - assert.deepStrictEqual(output.slice(-2), [ - '}', - PROMPT, - ]); -} - -async function main() { - await ordinaryTests(); - await ctrlCTest(); -} - -main().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-repl-unsafe-array-iteration.js b/test/js/node/test/parallel/test-repl-unsafe-array-iteration.js deleted file mode 100644 index 3fc65f54cf1f..000000000000 --- a/test/js/node/test/parallel/test-repl-unsafe-array-iteration.js +++ /dev/null @@ -1,68 +0,0 @@ -'use strict'; -const common = require('../common'); -const assert = require('assert'); -const { spawn } = require('child_process'); - -const replProcess = spawn(process.argv0, ['--interactive'], { - stdio: ['pipe', 'pipe', 'inherit'], - windowsHide: true, -}); - -replProcess.on('error', common.mustNotCall()); - -const replReadyState = (async function* () { - let ready; - const SPACE = ' '.charCodeAt(); - const BRACKET = '>'.charCodeAt(); - const DOT = '.'.charCodeAt(); - replProcess.stdout.on('data', (data) => { - ready = data[data.length - 1] === SPACE && ( - data[data.length - 2] === BRACKET || ( - data[data.length - 2] === DOT && - data[data.length - 3] === DOT && - data[data.length - 4] === DOT - )); - }); - - const processCrashed = new Promise((resolve, reject) => - replProcess.on('exit', reject) - ); - while (true) { - await Promise.race([new Promise(setImmediate), processCrashed]); - if (ready) { - ready = false; - yield; - } - } -})(); -async function writeLn(data, expectedOutput) { - await replReadyState.next(); - if (expectedOutput) { - replProcess.stdout.once('data', common.mustCall((data) => - assert.match(data.toString('utf8'), expectedOutput) - )); - } - await new Promise((resolve, reject) => replProcess.stdin.write( - `${data}\n`, - (err) => (err ? reject(err) : resolve()) - )); -} - -async function main() { - await writeLn( - 'const ArrayIteratorPrototype =' + - ' Object.getPrototypeOf(Array.prototype[Symbol.iterator]());' - ); - await writeLn('delete Array.prototype[Symbol.iterator];'); - await writeLn('delete ArrayIteratorPrototype.next;'); - - await writeLn( - 'for(const x of [3, 2, 1]);', - /Uncaught TypeError: \[3,2,1\] is not iterable/ - ); - await writeLn('.exit'); - - assert(!replProcess.connected); -} - -main().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-repl-unsupported-option.js b/test/js/node/test/parallel/test-repl-unsupported-option.js deleted file mode 100644 index 16de512a7692..000000000000 --- a/test/js/node/test/parallel/test-repl-unsupported-option.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -require('../common'); - -const assert = require('assert'); -const { spawnSync } = require('child_process'); - -const result = spawnSync(process.execPath, ['--interactive', '--input-type=module']); - -assert.strictEqual(result.stderr.toString(), 'Cannot specify --input-type for REPL\n'); -assert.notStrictEqual(result.exitCode, 0); diff --git a/test/js/node/test/parallel/test-repl-user-error-handler.js b/test/js/node/test/parallel/test-repl-user-error-handler.js deleted file mode 100644 index 31bd46b13d36..000000000000 --- a/test/js/node/test/parallel/test-repl-user-error-handler.js +++ /dev/null @@ -1,84 +0,0 @@ -'use strict'; -const common = require('../common'); -const { start } = require('node:repl'); -const assert = require('node:assert'); -const { PassThrough } = require('node:stream'); -const { once } = require('node:events'); -const test = require('node:test'); -const { spawn } = require('node:child_process'); - -function* generateCases() { - for (const async of [false, true]) { - for (const handleErrorReturn of ['ignore', 'print', 'unhandled', 'badvalue']) { - if (handleErrorReturn === 'badvalue' && async) { - // Handled through a separate test using a child process - continue; - } - yield { async, handleErrorReturn }; - } - } -} - -for (const { async, handleErrorReturn } of generateCases()) { - test(`async: ${async}, handleErrorReturn: ${handleErrorReturn}`, async () => { - let err; - const options = { - input: new PassThrough(), - output: new PassThrough().setEncoding('utf8'), - handleError: common.mustCall((e) => { - err = e; - queueMicrotask(() => repl.emit('handled-error')); - return handleErrorReturn; - }) - }; - - let uncaughtExceptionEvent; - if (handleErrorReturn === 'unhandled' && async) { - process.removeAllListeners('uncaughtException'); // Remove the test runner's handler - uncaughtExceptionEvent = once(process, 'uncaughtException'); - } - - const repl = start(options); - const inputString = async ? - 'setImmediate(() => { throw new Error("testerror") })\n42\n' : - 'throw new Error("testerror")\n42\n'; - if (handleErrorReturn === 'badvalue') { - assert.throws(() => options.input.end(inputString), /ERR_INVALID_STATE/); - return; - } - options.input.end(inputString); - - await once(repl, 'handled-error'); - assert.strictEqual(err.message, 'testerror'); - const outputString = options.output.read(); - assert.match(outputString, /42/); - - if (handleErrorReturn === 'print') { - assert.match(outputString, /testerror/); - } else { - assert.doesNotMatch(outputString, /testerror/); - } - - if (uncaughtExceptionEvent) { - const [uncaughtErr] = await uncaughtExceptionEvent; - assert.strictEqual(uncaughtErr, err); - } - }); -} - -test('async: true, handleErrorReturn: badvalue', async () => { - // Can't test this the same way as the other combinations - // since this will take the process down in a way that - // cannot be caught. - const proc = spawn(process.execPath, ['-e', ` - require('node:repl').start({ - handleError: () => 'badvalue' - }) - `], { encoding: 'utf8', stdio: 'pipe' }); - proc.stdin.end('throw new Error("foo");'); - let stderr = ''; - proc.stderr.setEncoding('utf8').on('data', (data) => stderr += data); - const [exit] = await once(proc, 'close'); - assert.strictEqual(exit, 1); - assert.match(stderr, /ERR_INVALID_STATE.+badvalue/); -}); diff --git a/test/js/node/test/parallel/test-repl.js b/test/js/node/test/parallel/test-repl.js deleted file mode 100644 index c325abb6b4ec..000000000000 --- a/test/js/node/test/parallel/test-repl.js +++ /dev/null @@ -1,1053 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// 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. - -'use strict'; -const common = require('../common'); -const fixtures = require('../common/fixtures'); -const tmpdir = require('../common/tmpdir'); -const assert = require('assert'); -const net = require('net'); -const repl = require('repl'); -const { inspect } = require('util'); - -const message = 'Read, Eval, Print Loop'; -const prompt_unix = 'node via Unix socket> '; -const prompt_tcp = 'node via TCP socket> '; - -// Absolute path to test/fixtures/a.js -const moduleFilename = fixtures.path('a'); - -// Function for REPL to run -globalThis.invoke_me = function(arg) { - return `invoked ${arg}`; -}; - -// Helpers for describing the expected output: -const kArrow = /^ *\^+ *$/; // Arrow of ^ pointing to syntax error location -const kSource = Symbol('kSource'); // Placeholder standing for input readback - -async function runReplTests(socket, prompt, tests) { - let lineBuffer = ''; - - for (const { send, expect } of tests) { - // Expect can be a single line or multiple lines - const expectedLines = Array.isArray(expect) ? expect : [ expect ]; - - console.error('\n------------'); - console.error('out:', JSON.stringify(send)); - socket.write(`${send}\n`); - - for (let expectedLine of expectedLines) { - // Special value: kSource refers to last sent source text - if (expectedLine === kSource) - expectedLine = send; - - while (!lineBuffer.includes('\n')) { - lineBuffer += await event(socket, expect); - - // Cut away the initial prompt - while (lineBuffer.startsWith(prompt)) - lineBuffer = lineBuffer.slice(prompt.length); - - // Allow to match partial text if no newline was received, because - // sending newlines from the REPL itself would be redundant - // (e.g. in the `| ` multiline prompt: The user already pressed - // enter for that, so the REPL shouldn't do it again!). - if (lineBuffer === expectedLine && !expectedLine.includes('\n')) - lineBuffer += '\n'; - } - - // Split off the current line. - const newlineOffset = lineBuffer.indexOf('\n'); - let actualLine = lineBuffer.slice(0, newlineOffset); - lineBuffer = lineBuffer.slice(newlineOffset + 1); - - // This might have been skipped in the loop above because the buffer - // already contained a \n to begin with and the entire loop was skipped. - while (actualLine.startsWith(prompt)) - actualLine = actualLine.slice(prompt.length); - - console.error('in:', JSON.stringify(actualLine)); - - // Match a string directly, or a RegExp. - if (typeof expectedLine === 'string') { - assert.strictEqual(actualLine, expectedLine); - } else { - assert.match(actualLine, expectedLine); - } - } - } - - const remainder = socket.read(); - assert(remainder === '' || remainder === null); -} - -const unixTests = [ - { - send: '', - expect: '' - }, - { - send: 'message', - expect: `'${message}'` - }, - { - send: 'invoke_me(987)', - expect: '\'invoked 987\'' - }, - { - send: 'a = 12345', - expect: '12345' - }, - { - send: '{a:1}', - expect: '{ a: 1 }' - }, -]; - -const strictModeTests = [ - { - send: 'ref = 1', - expect: [/^Uncaught ReferenceError:\s/] - }, -]; - -const possibleTokensAfterIdentifierWithLineBreak = [ - '(\n)', - '[\n0]', - '+\n1', '- \n1', '* \n1', '/ \n1', '% \n1', '** \n1', - '== \n1', '=== \n1', '!= \n1', '!== \n1', '< \n1', '> \n1', '<= \n1', '>= \n1', - '&& \n1', '|| \n1', '?? \n1', - '= \n1', '+= \n1', '-= \n1', '*= \n1', '/= \n1', '%= \n1', - ': \n', - '? \n1: 1', -]; - -const errorTests = [ - // Uncaught error throws and prints out - { - send: 'throw new Error(\'test error\');', - expect: ['Uncaught Error: test error'] - }, - { - send: "throw { foo: 'bar' };", - expect: "Uncaught { foo: 'bar' }" - }, - // Common syntax error is treated as multiline command - { - send: 'function test_func() {', - expect: '| ' - }, - // You can recover with the .break command - { - send: '.break', - expect: '' - }, - // But passing the same string to eval() should throw - { - send: 'eval("function test_func() {")', - expect: [/^Uncaught SyntaxError: /] - }, - // Can handle multiline template literals - { - send: '`io.js', - expect: '| ' - }, - // Special REPL commands still available - { - send: '.break', - expect: '' - }, - // Template expressions - { - send: '`io.js ${"1.0"', - expect: '| ' - }, - { - send: '+ ".2"}`', - expect: '\'io.js 1.0.2\'' - }, - { - send: '`io.js ${', - expect: '| ' - }, - { - send: '"1.0" + ".2"}`', - expect: '\'io.js 1.0.2\'' - }, - // Dot prefix in multiline commands aren't treated as commands - { - send: '("a"', - expect: '| ' - }, - { - send: '.charAt(0))', - expect: '\'a\'' - }, - // Floating point numbers are not interpreted as REPL commands. - { - send: '.1234', - expect: '0.1234' - }, - // Floating point expressions are not interpreted as REPL commands - { - send: '.1+.1', - expect: '0.2' - }, - // Can parse valid JSON - { - send: 'JSON.parse(\'{"valid": "json"}\');', - expect: '{ valid: \'json\' }' - }, - // Invalid input to JSON.parse error is special case of syntax error, - // should throw - { - send: 'JSON.parse(\'{invalid: \\\'json\\\'}\');', - expect: [ - 'Uncaught:', - /^SyntaxError: /, - ], - }, - // End of input to JSON.parse error is special case of syntax error, - // should throw - { - send: 'JSON.parse(\'066\');', - expect: [/^Uncaught SyntaxError: /] - }, - // should throw - { - send: 'JSON.parse(\'{\');', - expect: [ - 'Uncaught:', - /^SyntaxError: /, - ], - }, - // invalid RegExps are a special case of syntax error, - // should throw - { - send: '/(/;', - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - // invalid RegExp modifiers are a special case of syntax error, - // should throw (GH-4012) - { - send: 'new RegExp("foo", "wrong modifier");', - expect: [/^Uncaught SyntaxError: /] - }, - // Strict mode syntax errors should be caught (GH-5178) - { - send: '(function() { "use strict"; return 0755; })()', - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - { - send: '(function(a, a, b) { "use strict"; return a + b + c; })()', - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - { - send: '(function() { "use strict"; with (this) {} })()', - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - { - send: '(function() { "use strict"; var x; delete x; })()', - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - { - send: '(function() { "use strict"; eval = 17; })()', - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - { - send: '(function() { "use strict"; if (true) function f() { } })()', - expect: [ - kSource, - kArrow, - '', - 'Uncaught:', - /^SyntaxError: /, - ] - }, - // Named functions can be used: - { - send: 'function blah() { return 1; }', - expect: 'undefined' - }, - { - send: 'blah()', - expect: '1' - }, - // Functions should not evaluate twice (#2773) - { - send: 'var I = [1,2,3,function() {}]; I.pop()', - expect: '[Function (anonymous)]' - }, - // Multiline object - { - send: '{}),({}', - expect: '| ', - }, - { - send: '}', - expect: [ - '{}),({}', - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - { - send: '{ a: ', - expect: '| ' - }, - { - send: '1 }', - expect: '{ a: 1 }' - }, - // Multiline string-keyed object (e.g. JSON) - { - send: '{ "a": ', - expect: '| ' - }, - { - send: '1 }', - expect: '{ a: 1 }' - }, - // Multiline class with private member. - { - send: 'class Foo { #private = true ', - expect: '| ' - }, - // Class field with bigint. - { - send: 'num = 123456789n', - expect: '| ' - }, - // Static class features. - { - send: 'static foo = "bar" }', - expect: 'undefined' - }, - // Multiline anonymous function with comment - { - send: '(function() {', - expect: '| ' - }, - { - send: '// blah', - expect: '| ' - }, - { - send: 'return 1n;', - expect: '| ' - }, - { - send: '})()', - expect: '1n' - }, - // Multiline function call - { - send: 'function f(){}; f(f(1,', - expect: '| ' - }, - { - send: '2)', - expect: '| ' - }, - { - send: ')', - expect: 'undefined' - }, - // `npm` prompt error message. - { - send: 'npm install foobar', - expect: [ - 'npm should be run outside of the Node.js REPL, in your normal shell.', - '(Press Ctrl+D to exit.)', - ] - }, - { - send: 'let npm = () => {};', - expect: 'undefined' - }, - ...possibleTokensAfterIdentifierWithLineBreak.map((token) => ( - { - send: `npm ${token}; undefined`, - expect: '| undefined' - } - )), - { - send: '(function() {\n\nreturn 1;\n})()', - expect: '| | | 1' - }, - { - send: '{\n\na: 1\n}', - expect: '| | | { a: 1 }' - }, - { - send: 'url.format("http://google.com")', - expect: '\'http://google.com/\'' - }, - { - send: 'var path = 42; path', - expect: '42' - }, - // This makes sure that we don't print `undefined` when we actually print - // the error message - { - send: '.invalid_repl_command', - expect: 'Invalid REPL keyword' - }, - // This makes sure that we don't crash when we use an inherited property as - // a REPL command - { - send: '.toString', - expect: 'Invalid REPL keyword' - }, - // Fail when we are not inside a String and a line continuation is used - { - send: '[] \\', - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - // Do not fail when a String is created with line continuation - { - send: '\'the\\\nfourth\\\neye\'', - expect: ['| | \'thefourtheye\''] - }, - // Don't fail when a partial String is created and line continuation is used - // with whitespace characters at the end of the string. We are to ignore it. - // This test is to make sure that we properly remove the whitespace - // characters at the end of line, unlike the buggy `trimWhitespace` function - { - send: ' \t .break \t ', - expect: '' - }, - // Multiline strings preserve whitespace characters in them - { - send: '\'the \\\n fourth\t\t\\\n eye \'', - expect: '| | \'the fourth\\t\\t eye \'' - }, - // More than one multiline strings also should preserve whitespace chars - { - send: '\'the \\\n fourth\' + \'\t\t\\\n eye \'', - expect: '| | \'the fourth\\t\\t eye \'' - }, - // using REPL commands within a string literal should still work - { - send: '\'\\\n.break', - expect: '| ' + prompt_unix - }, - // Using REPL command "help" within a string literal should still work - { - send: '\'thefourth\\\n.help\neye\'', - expect: [ - /\.break/, - /\.clear/, - /\.exit/, - /\.help/, - /\.load/, - /\.save/, - '', - 'Press Ctrl+C to abort current expression, Ctrl+D to exit the REPL', - /'thefourtheye'/, - ] - }, - // Check for wrapped objects. - { - send: '{ a: 1 }.a', // ({ a: 1 }.a); - expect: '1' - }, - { - send: '{ a: 1 }.a;', // { a: 1 }.a; - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - { - send: '{ a: 1 }["a"] === 1', // ({ a: 1 }['a'] === 1); - expect: 'true' - }, - { - send: '{ a: 1 }["a"] === 1;', // { a: 1 }; ['a'] === 1; - expect: 'false' - }, - // Empty lines in the REPL should be allowed - { - send: '\n\r\n\r\n', - expect: '' - }, - // Empty lines in the string literals should not affect the string - { - send: '\'the\\\n\\\nfourtheye\'\n', - expect: '| | \'thefourtheye\'' - }, - // Regression test for https://github.com/nodejs/node/issues/597 - { - send: '/(.)(.)(.)(.)(.)(.)(.)(.)(.)/.test(\'123456789\')\n', - expect: 'true' - }, - // The following test's result depends on the RegExp's match from the above - { - send: 'RegExp.$1\nRegExp.$2\nRegExp.$3\nRegExp.$4\nRegExp.$5\n' + - 'RegExp.$6\nRegExp.$7\nRegExp.$8\nRegExp.$9\n', - expect: ['\'1\'', '\'2\'', '\'3\'', '\'4\'', '\'5\'', '\'6\'', - '\'7\'', '\'8\'', '\'9\''] - }, - // Regression tests for https://github.com/nodejs/node/issues/2749 - { - send: 'function x() {\nreturn \'\\n\';\n }', - expect: '| | undefined' - }, - { - send: 'function x() {\nreturn \'\\\\\';\n }', - expect: '| | undefined' - }, - // Regression tests for https://github.com/nodejs/node/issues/3421 - { - send: 'function x() {\n//\'\n }', - expect: '| | undefined' - }, - { - send: 'function x() {\n//"\n }', - expect: '| | undefined' - }, - { - send: 'function x() {//\'\n }', - expect: '| undefined' - }, - { - send: 'function x() {//"\n }', - expect: '| undefined' - }, - { - send: 'function x() {\nvar i = "\'";\n }', - expect: '| | undefined' - }, - { - send: 'function x(/*optional*/) {}', - expect: 'undefined' - }, - { - send: 'function x(/* // 5 */) {}', - expect: 'undefined' - }, - { - send: '// /* 5 */', - expect: 'undefined' - }, - { - send: '"//"', - expect: '\'//\'' - }, - { - send: '"data /*with*/ comment"', - expect: '\'data /*with*/ comment\'' - }, - { - send: 'function x(/*fn\'s optional params*/) {}', - expect: 'undefined' - }, - { - send: '/* \'\n"\n\'"\'\n*/', - expect: '| | | undefined' - }, - // REPL should get a normal require() function, not one that allows - // access to internal modules without the --expose-internals flag. - { - // Shrink the stack trace to avoid having to update this test whenever the - // implementation of require() changes. It's set to 5 because somehow setting it - // to a lower value breaks the error formatting and the message becomes - // "Uncaught [Error...", which is probably a bug(?). - send: 'Error.stackTraceLimit = 5; require("internal/repl")', - expect: [ - /^Uncaught Error: Cannot find module 'internal\/repl'/, - /^Require stack:/, - /^- /, // This just tests MODULE_NOT_FOUND so let's skip the stack trace - /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. - /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. - /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. - /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. - " code: 'MODULE_NOT_FOUND',", - " requireStack: [ '' ]", - '}', - ] - }, - // REPL should handle quotes within regexp literal in multiline mode - { - send: "function x(s) {\nreturn s.replace(/'/,'');\n}", - expect: '| | undefined' - }, - { - send: "function x(s) {\nreturn s.replace(/'/,'');\n}", - expect: '| | undefined' - }, - { - send: 'function x(s) {\nreturn s.replace(/"/,"");\n}', - expect: '| | undefined' - }, - { - send: 'function x(s) {\nreturn s.replace(/.*/,"");\n}', - expect: '| | undefined' - }, - { - send: '{ var x = 4; }', - expect: 'undefined' - }, - // Illegal token is not recoverable outside string literal, RegExp literal, - // or block comment. https://github.com/nodejs/node/issues/3611 - { - send: 'a = 3.5e', - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - // Mitigate https://github.com/nodejs/node/issues/548 - { - send: 'function name(){ return "node"; };name()', - expect: '\'node\'' - }, - { - send: 'function name(){ return "nodejs"; };name()', - expect: '\'nodejs\'' - }, - // Avoid emitting repl:line-number for SyntaxError - { - send: 'a = 3.5e', - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - // Avoid emitting stack trace - { - send: 'a = 3.5e', - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - - // https://github.com/nodejs/node/issues/9850 - { - send: 'function* foo() {}; foo().next();', - expect: '{ value: undefined, done: true }' - }, - - { - send: 'function *foo() {}; foo().next();', - expect: '{ value: undefined, done: true }' - }, - - { - send: 'function*foo() {}; foo().next();', - expect: '{ value: undefined, done: true }' - }, - - { - send: 'function * foo() {}; foo().next()', - expect: '{ value: undefined, done: true }' - }, - - // https://github.com/nodejs/node/issues/9300 - { - send: 'function foo() {\nvar bar = 1 / 1; // "/"\n}', - expect: '| | undefined' - }, - - { - send: '(function() {\nreturn /foo/ / /bar/;\n}())', - expect: '| | NaN' - }, - - { - send: '(function() {\nif (false) {} /bar"/;\n}())', - expect: '| | undefined' - }, - - // https://github.com/nodejs/node/issues/16483 - { - send: 'new Proxy({x:42}, {get(){throw null}});', - expect: 'Proxy [ { x: 42 }, { get: [Function: get] } ]' - }, - { - send: 'repl.writer.options.showProxy = false, new Proxy({x:42}, {});', - expect: 'Proxy({ x: 42 })' - }, - - // Newline within template string maintains whitespace. - { - send: '`foo \n`', - expect: '| \'foo \\n\'' - }, - // Whitespace is not evaluated. - { - send: ' \t \n', - expect: 'undefined' - }, - // Do not parse `...[]` as a REPL keyword - { - send: '...[]', - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - // Bring back the repl to prompt - { - send: '.break', - expect: '' - }, - { - send: 'console.log("Missing comma in arg list" process.version)', - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - { - send: 'x = {\nfield\n{', - expect: [ - '| | {', - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - { - send: '(2 + 3))', - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - { - send: 'if (typeof process === "object"); {', - expect: '| ' - }, - { - send: 'console.log("process is defined");', - expect: '| ' - }, - { - send: '} else {', - expect: [ - kSource, - kArrow, - '', - /^Uncaught SyntaxError: /, - ] - }, - { - send: 'console', - expect: [ - 'Object [console] {', - ' log: [Function: log],', - ' info: [Function: info],', - ' debug: [Function: debug],', - ' warn: [Function: warn],', - ' error: [Function: error],', - ' dir: [Function: dir],', - ' time: [Function: time],', - ' timeEnd: [Function: timeEnd],', - ' timeLog: [Function: timeLog],', - ' trace: [Function: trace],', - ' assert: [Function: assert],', - ' clear: [Function: clear],', - ' count: [Function: count],', - ' countReset: [Function: countReset],', - ' group: [Function: group],', - ' groupEnd: [Function: groupEnd],', - ' table: [Function: table],', - / {2}dirxml: \[Function: (dirxml|log)],/, - / {2}groupCollapsed: \[Function: (groupCollapsed|group)],/, - / {2}Console: \[Function: Console],?/, - ...process.features.inspector ? [ - ' profile: [Function: profile],', - ' profileEnd: [Function: profileEnd],', - ' timeStamp: [Function: timeStamp],', - ' context: [Function: context],', - ' createTask: [Function: createTask]', - ] : [], - '}', - ] - }, -]; - -const tcpTests = [ - { - send: '', - expect: '' - }, - { - send: 'invoke_me(333)', - expect: '\'invoked 333\'' - }, - { - send: 'a += 1', - expect: '12346' - }, - { - send: `require(${JSON.stringify(moduleFilename)}).number`, - expect: '42' - }, - { - send: 'import comeOn from \'fhqwhgads\'', - expect: [ - kSource, - kArrow, - '', - 'Uncaught:', - 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ -alternatively use dynamic import: const { default: comeOn } = await import("fhqwhgads");', - ] - }, - { - send: 'import { export1, export2 } from "module-name"', - expect: [ - kSource, - kArrow, - '', - 'Uncaught:', - 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ -alternatively use dynamic import: const { export1, export2 } = await import("module-name");', - ] - }, - { - send: 'import * as name from "module-name";', - expect: [ - kSource, - kArrow, - '', - 'Uncaught:', - 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ -alternatively use dynamic import: const name = await import("module-name");', - ] - }, - { - send: 'import "module-name";', - expect: [ - kSource, - kArrow, - '', - 'Uncaught:', - 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ -alternatively use dynamic import: await import("module-name");', - ] - }, - { - send: 'import { export1 as localName1, export2 } from "bar";', - expect: [ - kSource, - kArrow, - '', - 'Uncaught:', - 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ -alternatively use dynamic import: const { export1: localName1, export2 } = await import("bar");', - ] - }, - { - send: 'import alias from "bar";', - expect: [ - kSource, - kArrow, - '', - 'Uncaught:', - 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ -alternatively use dynamic import: const { default: alias } = await import("bar");', - ] - }, - { - send: 'import alias, {namedExport} from "bar";', - expect: [ - kSource, - kArrow, - '', - 'Uncaught:', - 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ -alternatively use dynamic import: const { default: alias, namedExport } = await import("bar");', - ] - }, -]; - -(async function() { - { - const [ socket, replServer ] = await startUnixRepl(); - - await runReplTests(socket, prompt_unix, unixTests); - await runReplTests(socket, prompt_unix, errorTests); - replServer.replMode = repl.REPL_MODE_STRICT; - await runReplTests(socket, prompt_unix, strictModeTests); - - socket.end(); - } - { - const [ socket ] = await startTCPRepl(); - - await runReplTests(socket, prompt_tcp, tcpTests); - - socket.end(); - } - common.allowGlobals(globalThis.invoke_me, globalThis.message, globalThis.a, globalThis.blah, - globalThis.I, globalThis.f, globalThis.path, globalThis.x, globalThis.name, globalThis.foo); -})().then(common.mustCall()); - -function startTCPRepl() { - let resolveSocket, resolveReplServer; - - const server = net.createServer(common.mustCall((socket) => { - assert.strictEqual(server, socket.server); - - socket.on('end', common.mustCall(() => { - socket.end(); - })); - - resolveReplServer(repl.start(prompt_tcp, socket)); - })); - - server.listen(0, common.mustCall(() => { - const client = net.createConnection(server.address().port); - - client.setEncoding('utf8'); - - client.on('connect', common.mustCall(() => { - assert.strictEqual(client.readable, true); - assert.strictEqual(client.writable, true); - - resolveSocket(client); - })); - - client.on('close', common.mustCall(() => { - server.close(); - })); - })); - - return Promise.all([ - new Promise((resolve) => resolveSocket = resolve), - new Promise((resolve) => resolveReplServer = resolve), - ]); -} - -function startUnixRepl() { - let resolveSocket, resolveReplServer; - - const server = net.createServer(common.mustCall((socket) => { - assert.strictEqual(server, socket.server); - - socket.on('end', common.mustCall(() => { - socket.end(); - })); - - const replServer = repl.start({ - prompt: prompt_unix, - input: socket, - output: socket, - useGlobal: true - }); - replServer.context.message = message; - resolveReplServer(replServer); - })); - - tmpdir.refresh(); - - server.listen(common.PIPE, common.mustCall(() => { - const client = net.createConnection(common.PIPE); - - client.setEncoding('utf8'); - - client.on('connect', common.mustCall(() => { - assert.strictEqual(client.readable, true); - assert.strictEqual(client.writable, true); - - resolveSocket(client); - })); - - client.on('close', common.mustCall(() => { - server.close(); - })); - })); - - return Promise.all([ - new Promise((resolve) => resolveSocket = resolve), - new Promise((resolve) => resolveReplServer = resolve), - ]); -} - -function event(ee, expected) { - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - const data = inspect(expected, { compact: false }); - const msg = `The REPL did not reply as expected for:\n\n${data}`; - reject(new Error(msg)); - }, common.platformTimeout(9999)); - ee.once('data', common.mustCall((...args) => { - clearTimeout(timeout); - resolve(...args); - })); - }); -} diff --git a/test/js/node/test/sequential/test-repl-timeout-throw.js b/test/js/node/test/sequential/test-repl-timeout-throw.js deleted file mode 100644 index d0cbd6fdca71..000000000000 --- a/test/js/node/test/sequential/test-repl-timeout-throw.js +++ /dev/null @@ -1,59 +0,0 @@ -'use strict'; -const common = require('../common'); -const assert = require('assert'); - -const spawn = require('child_process').spawn; - -const child = spawn(process.execPath, [ '--interactive' ], { - stdio: [null, null, 2], -}); - -let stdout = ''; -child.stdout.setEncoding('utf8'); -child.stdout.on('data', function(c) { - process.stdout.write(c); - stdout += c; - if (stdout.includes('> THROW 2')) - child.stdin.end(); -}); - -child.stdin.write = function(original) { - return function(c) { - process.stderr.write(c); - return original.call(child.stdin, c); - }; -}(child.stdin.write); - -child.stdout.once('data', function() { - child.stdin.write('let throws = 0;'); - child.stdin.write('process.on("exit",function(){console.log(throws)});'); - child.stdin.write('function thrower(){console.log("THROW",throws++);XXX};'); - child.stdin.write('setTimeout(thrower);""\n'); - - setTimeout(fsTest, 50); - function fsTest() { - const f = JSON.stringify(__filename); - child.stdin.write(`fs.readFile(${f}, thrower);\n`); - setTimeout(eeTest, 50); - } - - function eeTest() { - child.stdin.write('setTimeout(function() {\n' + - ' const events = require("events");\n' + - ' let e = new events.EventEmitter;\n' + - ' process.nextTick(function() {\n' + - ' e.on("x", thrower);\n' + - ' setTimeout(function() {\n' + - ' e.emit("x");\n' + - ' });\n' + - ' });\n' + - '});"";\n'); - } -}); - -child.on('close', common.mustCall((c) => { - assert.strictEqual(c, 0); - // Make sure we got 3 throws, in the end. - const lastLine = stdout.trim().split(/\r?\n/).pop(); - assert.strictEqual(lastLine, '> 3'); -})); From 955d627ffee6dd191c5fd6a777746593e165da44 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 24 Jun 2026 00:22:13 +0000 Subject: [PATCH 28/73] [autofix.ci] apply automated fixes --- src/js/internal/repl/node-shims.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/js/internal/repl/node-shims.js b/src/js/internal/repl/node-shims.js index ae4989d8d6d6..3db5f581216f 100644 --- a/src/js/internal/repl/node-shims.js +++ b/src/js/internal/repl/node-shims.js @@ -195,12 +195,7 @@ function getBuiltinLibs() { // tab completion doesn't offer e.g. `node:undici` and the REPL global // scope matches Node's. builtinLibs = Module.builtinModules.filter( - id => - !id.startsWith("_") && - !id.startsWith("node:") && - !id.startsWith("bun") && - id !== "undici" && - id !== "ws", + id => !id.startsWith("_") && !id.startsWith("node:") && !id.startsWith("bun") && id !== "undici" && id !== "ws", ); } return builtinLibs; From 86cc7bbc2cc20748dca39c2e76a75f07a9a92ffd Mon Sep 17 00:00:00 2001 From: Ciro Spaciari MacBook Date: Tue, 23 Jun 2026 18:48:12 -0700 Subject: [PATCH 29/73] repl: don't throw when decorateErrorStack can't write a frozen .stack [build images] decorateErrorStack now skips the write when the trimmed stack is unchanged and try/catches the assignment otherwise, so a thrown error with a non-writable .stack (Object.freeze, getter-only accessor) prints as "Uncaught Error: ..." and the REPL continues, matching Node. Before this the strict-mode assignment threw a TypeError that escaped _handleError. --- src/js/internal/repl/node-shims.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/js/internal/repl/node-shims.js b/src/js/internal/repl/node-shims.js index 3db5f581216f..73846ea7af1c 100644 --- a/src/js/internal/repl/node-shims.js +++ b/src/js/internal/repl/node-shims.js @@ -40,7 +40,14 @@ function decorateErrorStack(err) { if (/^\s+at REPL\d*:\d+:\d+$/.test(lines[i])) anonIdx = i; } if (anonIdx !== -1) lines = lines.slice(0, anonIdx); - err.stack = lines.join("\n"); + const newStack = lines.join("\n"); + if (newStack !== err.stack) { + // Errors with a non-writable .stack (Object.freeze, getter-only) must + // not turn into a TypeError that escapes the REPL's error handler. + try { + err.stack = newStack; + } catch {} + } return err; } From bd536ebb436b24914956850b61a3bc0aa62f76c7 Mon Sep 17 00:00:00 2001 From: Ciro Spaciari MacBook Date: Tue, 23 Jun 2026 19:24:35 -0700 Subject: [PATCH 30/73] repl: guard _handleError's e.stack/e.message rewrites for frozen errors [build images] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit V8 keeps Error#stack as an accessor whose setter survives Object.freeze; JSC stores it as an own data property, so under freeze the strict-mode rewrites in _handleError (the SyntaxError stack-strip and the REPL_MODE_STRICT line offset) threw "Attempted to assign to readonly property" and escaped the REPL's error handler. Wrap the block in try/catch so a frozen SyntaxError or a frozen error in strict mode prints "Uncaught …" and the REPL continues, like Node. Adds three regression cases to test/js/bun/repl/repl.test.ts (sloppy Error, SyntaxError, strict-mode Error). --- src/js/node/repl.js | 52 +++++++++++++++++++---------------- test/js/bun/repl/repl.test.ts | 44 +++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 23 deletions(-) diff --git a/src/js/node/repl.js b/src/js/node/repl.js index db4bc26a37ce..8ed818af6edc 100644 --- a/src/js/node/repl.js +++ b/src/js/node/repl.js @@ -1013,34 +1013,40 @@ class REPLServer extends Interface { decorateErrorStack(e); if (isError(e)) { - if (e.stack) { - if (e.name === "SyntaxError") { - // Remove stack trace. - e.stack = SideEffectFreeRegExpPrototypeSymbolReplace( - /^\s+at\s.*\n?/gm, - SideEffectFreeRegExpPrototypeSymbolReplace(/^REPL\d+:\d+\r?\n/, e.stack, ""), - "", - ); - const importErrorStr = "Cannot use import statement outside a " + "module"; - if (StringPrototypeIncludes(e.message, importErrorStr)) { - e.message = - "Cannot use import statement inside the Node.js " + - "REPL, alternatively use dynamic import: " + - toDynamicImport(ArrayPrototypeAt(this.lines, -1)); + // V8 stores `.stack` as an accessor (the setter survives Object.freeze); + // JSC stores it as an own data property, so under freeze the strict-mode + // writes below throw. Swallow that so the REPL prints the original error + // instead of a TypeError. + try { + if (e.stack) { + if (e.name === "SyntaxError") { + // Remove stack trace. + e.stack = SideEffectFreeRegExpPrototypeSymbolReplace( + /^\s+at\s.*\n?/gm, + SideEffectFreeRegExpPrototypeSymbolReplace(/^REPL\d+:\d+\r?\n/, e.stack, ""), + "", + ); + const importErrorStr = "Cannot use import statement outside a " + "module"; + if (StringPrototypeIncludes(e.message, importErrorStr)) { + e.message = + "Cannot use import statement inside the Node.js " + + "REPL, alternatively use dynamic import: " + + toDynamicImport(ArrayPrototypeAt(this.lines, -1)); + e.stack = SideEffectFreeRegExpPrototypeSymbolReplace( + /SyntaxError:.*\n/, + e.stack, + `SyntaxError: ${e.message}\n`, + ); + } + } else if (this.replMode === __node_module__.exports.REPL_MODE_STRICT) { e.stack = SideEffectFreeRegExpPrototypeSymbolReplace( - /SyntaxError:.*\n/, + /(\s+at\s+REPL\d+:)(\d+)/, e.stack, - `SyntaxError: ${e.message}\n`, + (_, pre, line) => pre + (line - 1), ); } - } else if (this.replMode === __node_module__.exports.REPL_MODE_STRICT) { - e.stack = SideEffectFreeRegExpPrototypeSymbolReplace( - /(\s+at\s+REPL\d+:)(\d+)/, - e.stack, - (_, pre, line) => pre + (line - 1), - ); } - } + } catch {} errStack = this.writer(e); // Remove one line error braces to keep the old style in place. diff --git a/test/js/bun/repl/repl.test.ts b/test/js/bun/repl/repl.test.ts index 0d8e2ce79af9..005a5eaa02d4 100644 --- a/test/js/bun/repl/repl.test.ts +++ b/test/js/bun/repl/repl.test.ts @@ -1249,3 +1249,47 @@ describe.skipIf(isWindows)("REPL history file permissions", () => { expect(exitCode).toBe(0); }); }); + +// V8 keeps Error#stack as an accessor (the setter survives Object.freeze); JSC +// stores it as an own data property, so the strict-mode `e.stack = …` rewrites +// in node:repl's _handleError throw on a frozen error. The Bun port guards +// those writes so the REPL prints the original error and continues like Node. +describe.concurrent("node:repl prints a frozen thrown error and continues", () => { + test.each([ + ["Error in sloppy mode", "SLOPPY", "throw Object.freeze(new Error('boom'))", "Uncaught Error: boom"], + ["SyntaxError", "SLOPPY", "throw Object.freeze(new SyntaxError('boom'))", "Uncaught SyntaxError: boom"], + ["Error in strict mode", "STRICT", "throw Object.freeze(new Error('boom'))", "Uncaught Error: boom"], + ])("%s", async (_name, mode, line, expectedFirstLine) => { + const script = ` + const repl = require("repl"); + const { PassThrough } = require("stream"); + const inp = new PassThrough(), out = new PassThrough(); + let buf = ""; + out.on("data", d => buf += d); + const r = repl.start({ + input: inp, + output: out, + terminal: false, + prompt: "", + useGlobal: true, + replMode: repl.REPL_MODE_${mode}, + }); + r.on("exit", () => process.stdout.write(buf)); + inp.write(${JSON.stringify(line + "\n")}); + inp.end(); + `; + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", script], + env: { ...bunEnv, NO_COLOR: "1" }, + stdout: "pipe", + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + // The frozen .stack can't be trimmed under JSC (eager materialization), so + // assert only on the first line and that the REPL printed the next prompt. + expect(stdout.split("\n")[0]).toBe(expectedFirstLine); + expect(stdout).not.toContain("Attempted to assign to readonly property"); + expect(stderr).not.toContain("Attempted to assign to readonly property"); + expect(exitCode).toBe(0); + }); +}); From e69f1b4a59b9f0aae3401f1ee5db4f0a793f5a35 Mon Sep 17 00:00:00 2001 From: Ciro Spaciari MacBook Date: Tue, 23 Jun 2026 22:11:49 -0700 Subject: [PATCH 31/73] cli: --interactive -e runs the eval then enters the REPL [build images] `bun --interactive -e 'code'` now runs the eval first and then enters the node:repl REPL with the eval'd globals visible, matching `node -i -e` (the documented "load a script then go interactive" pattern). exec_node_repl prepends any user -e script to the embedded bootstrap; the bootstrap already passes useGlobal: true so globalThis assignments and require() side effects carry into the prompt. `--interactive -p` and `--interactive script.js` keep their existing semantics (eval+print / run the script, no REPL). Adds a regression test to test/js/bun/repl/repl.test.ts. --- src/runtime/cli/mod.rs | 8 ++++++-- src/runtime/cli/run_command.rs | 17 ++++++++++++----- test/js/bun/repl/repl.test.ts | 16 ++++++++++++++++ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/runtime/cli/mod.rs b/src/runtime/cli/mod.rs index 3aceff3a2564..655c78d798aa 100644 --- a/src/runtime/cli/mod.rs +++ b/src/runtime/cli/mod.rs @@ -1457,10 +1457,14 @@ pub mod command { } // Node semantics: `-i`/`--interactive` only forces the REPL when no - // script is given; `node -i script.js` runs the script. + // positional script is given (`node -i foo.js` runs the script). + // `node -i -e 'code'` runs the eval first and then enters the REPL + // with the eval'd globals visible; exec_node_repl prepends the user + // script to the embedded bootstrap to match. `-p` still wins over + // `--interactive` (eval+print, no REPL) for now. if tag == Tag::AutoCommand && ctx.runtime_options.interactive - && ctx.runtime_options.eval.script.is_empty() + && !ctx.runtime_options.eval.eval_and_print && ctx.positionals.is_empty() { return run_command::RunCommand::exec_node_repl(ctx); diff --git a/src/runtime/cli/run_command.rs b/src/runtime/cli/run_command.rs index 152b8ced9779..25c40578512d 100644 --- a/src/runtime/cli/run_command.rs +++ b/src/runtime/cli/run_command.rs @@ -2954,11 +2954,18 @@ impl RunCommand { /// the Node.js-compatible REPL (node:repl). Distinct from `bun repl`, /// which is Bun's own native REPL. pub fn exec_node_repl(ctx: &mut ContextData) -> Result<(), bun_core::Error> { - ctx.runtime_options.eval.script = - bun_core::runtime_embed_file!(Codegen, "eval/node-repl.ts") - .as_bytes() - .to_vec() - .into_boxed_slice(); + let bootstrap = bun_core::runtime_embed_file!(Codegen, "eval/node-repl.ts").as_bytes(); + let user = ::core::mem::take(&mut ctx.runtime_options.eval.script); + let mut script = Vec::with_capacity(user.len() + 2 + bootstrap.len()); + if !user.is_empty() { + // `bun --interactive -e 'code'` (Node's `node -i -e`): run the + // user script first, then enter the REPL. The bootstrap passes + // `useGlobal: true`, so globals it sets are visible at the prompt. + script.extend_from_slice(&user); + script.extend_from_slice(b";\n"); + } + script.extend_from_slice(bootstrap); + ctx.runtime_options.eval.script = script.into_boxed_slice(); Self::exec_eval(ctx) } diff --git a/test/js/bun/repl/repl.test.ts b/test/js/bun/repl/repl.test.ts index 005a5eaa02d4..33f8e845d273 100644 --- a/test/js/bun/repl/repl.test.ts +++ b/test/js/bun/repl/repl.test.ts @@ -1250,6 +1250,22 @@ describe.skipIf(isWindows)("REPL history file permissions", () => { }); }); +// `node -i -e 'code'` runs the eval and then enters the REPL with the eval'd +// globals visible (the documented "load a script then go interactive" pattern). +test("--interactive -e runs the eval first and enters the REPL with its globals visible", async () => { + await using proc = Bun.spawn({ + cmd: [bunExe(), "--interactive", "-e", "globalThis.fromEval = 42"], + env: { ...bunEnv, NO_COLOR: "1" }, + stdin: Buffer.from("fromEval\n.exit\n"), + stdout: "pipe", + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stdout).toContain("> 42"); + expect(stderr).not.toContain("error"); + expect(exitCode).toBe(0); +}); + // V8 keeps Error#stack as an accessor (the setter survives Object.freeze); JSC // stores it as an own data property, so the strict-mode `e.stack = …` rewrites // in node:repl's _handleError throw on a frozen error. The Bun port guards From a8289b0ac4d4d59bf1570b42dbf5a3b1df36f365 Mon Sep 17 00:00:00 2001 From: Ciro Spaciari MacBook Date: Wed, 24 Jun 2026 00:47:47 -0700 Subject: [PATCH 32/73] cli: scope-isolate the --interactive -e user script from the REPL bootstrap [build images] Wrap the user -e source in a block before appending the bundled eval/node-repl.ts bootstrap, so a user const/let that happens to match one of the bootstrap's top-level wrapper vars (`__commonJS`/`__require` in debug, minifier-chosen short names in release) doesn't fail with "has already been declared". The bootstrap itself stays at module top level (its trailing `export default` can't sit inside a block); user var still hoists out of the wrapping block, and globalThis assignments are unaffected. Extends the --interactive -e regression test to declare `const __commonJS`. --- src/runtime/cli/run_command.rs | 10 ++++++++-- test/js/bun/repl/repl.test.ts | 5 ++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/runtime/cli/run_command.rs b/src/runtime/cli/run_command.rs index 25c40578512d..d825e3f379db 100644 --- a/src/runtime/cli/run_command.rs +++ b/src/runtime/cli/run_command.rs @@ -2956,13 +2956,19 @@ impl RunCommand { pub fn exec_node_repl(ctx: &mut ContextData) -> Result<(), bun_core::Error> { let bootstrap = bun_core::runtime_embed_file!(Codegen, "eval/node-repl.ts").as_bytes(); let user = ::core::mem::take(&mut ctx.runtime_options.eval.script); - let mut script = Vec::with_capacity(user.len() + 2 + bootstrap.len()); + let mut script = Vec::with_capacity(user.len() + 6 + bootstrap.len()); if !user.is_empty() { // `bun --interactive -e 'code'` (Node's `node -i -e`): run the // user script first, then enter the REPL. The bootstrap passes // `useGlobal: true`, so globals it sets are visible at the prompt. + // Wrap the user script in a block so user `const`/`let` don't + // collide with the bundled bootstrap's top-level wrapper vars + // (`__commonJS`/`__require` in debug, minifier-chosen short names + // in release); the bootstrap itself ends in `export default …` so + // it must remain at the module's top level. + script.extend_from_slice(b"{\n"); script.extend_from_slice(&user); - script.extend_from_slice(b";\n"); + script.extend_from_slice(b"\n};\n"); } script.extend_from_slice(bootstrap); ctx.runtime_options.eval.script = script.into_boxed_slice(); diff --git a/test/js/bun/repl/repl.test.ts b/test/js/bun/repl/repl.test.ts index 33f8e845d273..c6b9de4b4bff 100644 --- a/test/js/bun/repl/repl.test.ts +++ b/test/js/bun/repl/repl.test.ts @@ -1252,9 +1252,11 @@ describe.skipIf(isWindows)("REPL history file permissions", () => { // `node -i -e 'code'` runs the eval and then enters the REPL with the eval'd // globals visible (the documented "load a script then go interactive" pattern). +// `__commonJS` is a top-level var the bundled REPL bootstrap declares; the user +// script is wrapped in a block so a user const of the same name doesn't collide. test("--interactive -e runs the eval first and enters the REPL with its globals visible", async () => { await using proc = Bun.spawn({ - cmd: [bunExe(), "--interactive", "-e", "globalThis.fromEval = 42"], + cmd: [bunExe(), "--interactive", "-e", "const __commonJS = 0; globalThis.fromEval = 42"], env: { ...bunEnv, NO_COLOR: "1" }, stdin: Buffer.from("fromEval\n.exit\n"), stdout: "pipe", @@ -1262,6 +1264,7 @@ test("--interactive -e runs the eval first and enters the REPL with its globals }); const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); expect(stdout).toContain("> 42"); + expect(stderr).not.toContain("has already been declared"); expect(stderr).not.toContain("error"); expect(exitCode).toBe(0); }); From cb7a4e75f59ec239a05276f8def41aa478faaef6 Mon Sep 17 00:00:00 2001 From: Ciro Spaciari MacBook Date: Wed, 24 Jun 2026 09:39:10 -0700 Subject: [PATCH 33/73] cli: document the static-import trade-off of the --interactive -e block wrap [build images] --- src/runtime/cli/run_command.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/runtime/cli/run_command.rs b/src/runtime/cli/run_command.rs index d825e3f379db..01235f49720a 100644 --- a/src/runtime/cli/run_command.rs +++ b/src/runtime/cli/run_command.rs @@ -2965,7 +2965,10 @@ impl RunCommand { // collide with the bundled bootstrap's top-level wrapper vars // (`__commonJS`/`__require` in debug, minifier-chosen short names // in release); the bootstrap itself ends in `export default …` so - // it must remain at the module's top level. + // it must remain at the module's top level. The block also means a + // static `import`/`export` in the user script is a syntax error + // (matching `node -i -e`, whose eval is CJS) — use + // `await import()` / `require()` instead. script.extend_from_slice(b"{\n"); script.extend_from_slice(&user); script.extend_from_slice(b"\n};\n"); From 6bf0206a9da5d8197001beea93bc1b83e22a1ebb Mon Sep 17 00:00:00 2001 From: Ciro Spaciari MacBook Date: Thu, 25 Jun 2026 12:20:56 -0700 Subject: [PATCH 34/73] repl: trim review-round comments to the new 3-line limit [build images] --- src/js/internal/repl/node-shims.js | 16 ++++++---------- src/js/internal/repl/utils.js | 7 ++----- src/js/node/repl.js | 6 ++---- src/runtime/cli/mod.rs | 9 +++------ src/runtime/cli/run_command.rs | 14 +++----------- test/js/bun/repl/repl.test.ts | 13 +++++-------- 6 files changed, 21 insertions(+), 44 deletions(-) diff --git a/src/js/internal/repl/node-shims.js b/src/js/internal/repl/node-shims.js index 73846ea7af1c..5c94a15c4504 100644 --- a/src/js/internal/repl/node-shims.js +++ b/src/js/internal/repl/node-shims.js @@ -27,11 +27,9 @@ function assignFunctionName(name, fn) { } function decorateErrorStack(err) { - // JSC materializes error stacks eagerly, so Node's overrideStackTrace-based - // trimming never runs. Reproduce it: convert JSC's " (loc)" - // frames to V8's bare "loc" form, then cut at the last anonymous frame - // (Node's null-functionName boundary) - that drops the REPL top-level frame - // and the vm runner frames below it. + // JSC materializes stacks eagerly so Node's overrideStackTrace never runs; + // reproduce it by normalizing " (loc)" frames and cutting at the + // last REPLn:l:c frame (drops the REPL top-level + vm runner frames). if (typeof err?.stack !== "string") return err; let lines = err.stack.split("\n"); lines = lines.map(l => l.replace(/^(\s+at ) \((.+)\)$/, "$1$2")); @@ -196,11 +194,9 @@ let builtinLibs; function getBuiltinLibs() { if (!builtinLibs) { - // Bun's Module.builtinModules also lists `bun`, `bun:*`, and the bundled - // third-party shims (`undici`, `ws`); none of these resolve under the - // `node:` scheme and none exist in a Node REPL, so exclude them here so - // tab completion doesn't offer e.g. `node:undici` and the REPL global - // scope matches Node's. + // Bun's builtinModules also lists `bun`, `bun:*`, `undici`, `ws`; none + // resolve under `node:`, so exclude them so completion and the REPL + // global scope match Node's. builtinLibs = Module.builtinModules.filter( id => !id.startsWith("_") && !id.startsWith("node:") && !id.startsWith("bun") && id !== "undici" && id !== "ws", ); diff --git a/src/js/internal/repl/utils.js b/src/js/internal/repl/utils.js index 10283255e07e..dc0d1968ce85 100644 --- a/src/js/internal/repl/utils.js +++ b/src/js/internal/repl/utils.js @@ -799,11 +799,8 @@ function getREPLResourceName() { const globalBuiltins = new SafeSet(vm.runInNewContext("Object.getOwnPropertyNames(globalThis)")); -// Upstream filters only `_*` and `node:*`. In Bun, Module.builtinModules also -// contains `bun`, `bun:*`, `undici`, and `ws`; node-shims' getBuiltinLibs() -// applies the upstream filter plus those Bun-specific exclusions so the -// `node:`-prefixed completion list (completion.js) doesn't offer specifiers -// the resolver rejects (e.g. `node:undici`). +// node-shims' getBuiltinLibs() also excludes Bun-specific entries (`bun*`, +// `undici`, `ws`) so completion doesn't offer e.g. `node:undici`. let _builtinLibs = getBuiltinLibs().slice(); // Note: the `getReplBuiltinLibs` and `setReplBuiltinLibs` are functions used to provide getters and diff --git a/src/js/node/repl.js b/src/js/node/repl.js index 8ed818af6edc..4266974d0492 100644 --- a/src/js/node/repl.js +++ b/src/js/node/repl.js @@ -1013,10 +1013,8 @@ class REPLServer extends Interface { decorateErrorStack(e); if (isError(e)) { - // V8 stores `.stack` as an accessor (the setter survives Object.freeze); - // JSC stores it as an own data property, so under freeze the strict-mode - // writes below throw. Swallow that so the REPL prints the original error - // instead of a TypeError. + // JSC's `.stack` is an own data property (V8's is an accessor), so under + // Object.freeze the writes below throw in strict mode; swallow that. try { if (e.stack) { if (e.name === "SyntaxError") { diff --git a/src/runtime/cli/mod.rs b/src/runtime/cli/mod.rs index 98e7f1d096a5..a4395b93ed7d 100644 --- a/src/runtime/cli/mod.rs +++ b/src/runtime/cli/mod.rs @@ -1455,12 +1455,9 @@ pub mod command { Global::exit(1); } - // Node semantics: `-i`/`--interactive` only forces the REPL when no - // positional script is given (`node -i foo.js` runs the script). - // `node -i -e 'code'` runs the eval first and then enters the REPL - // with the eval'd globals visible; exec_node_repl prepends the user - // script to the embedded bootstrap to match. `-p` still wins over - // `--interactive` (eval+print, no REPL) for now. + // Node: `-i foo.js` runs the script; `-i -e code` evals then enters + // the REPL (exec_node_repl prepends the user script). `-p` still wins + // over `--interactive` (eval+print, no REPL) for now. if tag == Tag::AutoCommand && ctx.runtime_options.interactive && !ctx.runtime_options.eval.eval_and_print diff --git a/src/runtime/cli/run_command.rs b/src/runtime/cli/run_command.rs index 01235f49720a..5f733e772163 100644 --- a/src/runtime/cli/run_command.rs +++ b/src/runtime/cli/run_command.rs @@ -2958,17 +2958,9 @@ impl RunCommand { let user = ::core::mem::take(&mut ctx.runtime_options.eval.script); let mut script = Vec::with_capacity(user.len() + 6 + bootstrap.len()); if !user.is_empty() { - // `bun --interactive -e 'code'` (Node's `node -i -e`): run the - // user script first, then enter the REPL. The bootstrap passes - // `useGlobal: true`, so globals it sets are visible at the prompt. - // Wrap the user script in a block so user `const`/`let` don't - // collide with the bundled bootstrap's top-level wrapper vars - // (`__commonJS`/`__require` in debug, minifier-chosen short names - // in release); the bootstrap itself ends in `export default …` so - // it must remain at the module's top level. The block also means a - // static `import`/`export` in the user script is a syntax error - // (matching `node -i -e`, whose eval is CJS) — use - // `await import()` / `require()` instead. + // `node -i -e`: run user code first, then the REPL (useGlobal=true). + // Block-wrap user code so its const/let can't collide with the + // bootstrap's top-level vars; static import/export thus errors (as in Node). script.extend_from_slice(b"{\n"); script.extend_from_slice(&user); script.extend_from_slice(b"\n};\n"); diff --git a/test/js/bun/repl/repl.test.ts b/test/js/bun/repl/repl.test.ts index c6b9de4b4bff..addd77233109 100644 --- a/test/js/bun/repl/repl.test.ts +++ b/test/js/bun/repl/repl.test.ts @@ -1250,10 +1250,8 @@ describe.skipIf(isWindows)("REPL history file permissions", () => { }); }); -// `node -i -e 'code'` runs the eval and then enters the REPL with the eval'd -// globals visible (the documented "load a script then go interactive" pattern). -// `__commonJS` is a top-level var the bundled REPL bootstrap declares; the user -// script is wrapped in a block so a user const of the same name doesn't collide. +// `node -i -e 'code'`: eval first, then REPL with the eval'd globals visible. +// `__commonJS` collides with the bootstrap unless the user script is block-wrapped. test("--interactive -e runs the eval first and enters the REPL with its globals visible", async () => { await using proc = Bun.spawn({ cmd: [bunExe(), "--interactive", "-e", "const __commonJS = 0; globalThis.fromEval = 42"], @@ -1269,10 +1267,9 @@ test("--interactive -e runs the eval first and enters the REPL with its globals expect(exitCode).toBe(0); }); -// V8 keeps Error#stack as an accessor (the setter survives Object.freeze); JSC -// stores it as an own data property, so the strict-mode `e.stack = …` rewrites -// in node:repl's _handleError throw on a frozen error. The Bun port guards -// those writes so the REPL prints the original error and continues like Node. +// JSC's Error#stack is an own data property (V8's is an accessor), so a frozen +// error makes _handleError's strict-mode `e.stack = …` rewrites throw; the Bun +// port guards those writes so the REPL prints the error and continues like Node. describe.concurrent("node:repl prints a frozen thrown error and continues", () => { test.each([ ["Error in sloppy mode", "SLOPPY", "throw Object.freeze(new Error('boom'))", "Uncaught Error: boom"], From 066e6dc2093f0bd169dd00a58d0fc62eb0e71da2 Mon Sep 17 00:00:00 2001 From: Ciro Spaciari MacBook Date: Thu, 25 Jun 2026 17:09:44 -0700 Subject: [PATCH 35/73] test(napi): split on /\r?\n/ so the napi_is_arraybuffer assertion passes on Windows [build images] The native test addon's printf emits CRLF on Windows, so split("\n") left a trailing \r on the first two lines and the toEqual diff failed on every Windows lane. Same pattern this file already uses at line 619. --- test/napi/napi.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/napi/napi.test.ts b/test/napi/napi.test.ts index 234fc7f38cea..6197fb6737dc 100644 --- a/test/napi/napi.test.ts +++ b/test/napi/napi.test.ts @@ -961,7 +961,7 @@ describe.skipIf(!canBuildNodeAddons())("cleanup hooks", () => { "test_is_arraybuffer", "[new ArrayBuffer(8), new SharedArrayBuffer(8), new Uint8Array(8)]", ); - expect(output.split("\n")).toEqual([ + expect(output.split(/\r?\n/)).toEqual([ "napi_is_arraybuffer=true napi_get_arraybuffer_info=0", "napi_is_arraybuffer=false napi_get_arraybuffer_info=0", "napi_is_arraybuffer=false napi_get_arraybuffer_info=1", From 0a13938502db670f65f5e4a2df8d0a8f4f683e7b Mon Sep 17 00:00:00 2001 From: Ciro Spaciari MacBook Date: Fri, 26 Jun 2026 18:08:56 -0700 Subject: [PATCH 36/73] repl: don't let process.cwd() ENOENT break repl.start() from a deleted cwd makeRequireFunction now falls back to path.dirname(process.execPath) when process.cwd() throws (same fallback fixReplRequire already uses), and addBuiltinLibsToObject anchors its createRequire to process.execPath instead of cwd (builtin specifiers don't need a cwd-anchored referrer). repl.start() from a deleted working directory now matches Node instead of throwing from the REPLServer constructor. --- src/js/internal/repl/node-shims.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/js/internal/repl/node-shims.js b/src/js/internal/repl/node-shims.js index 5c94a15c4504..9593ff227342 100644 --- a/src/js/internal/repl/node-shims.js +++ b/src/js/internal/repl/node-shims.js @@ -182,12 +182,16 @@ function getOrInitializeCascadedLoader() { // ---- internal/modules/helpers ---------------------------------------------- function makeRequireFunction(_mod) { - // `mod` is a CJS Module instance whose `paths` were initialized by the - // REPL. Resolution is anchored to a synthetic file in the REPL's cwd so - // relative requires behave like Node's REPL. - const filename = path.join(process.cwd(), ""); - const requireFn = Module.createRequire(filename); - return requireFn; + // Anchor relative requires to the REPL's cwd. process.cwd() throws when the + // working directory has been deleted; same fallback as fixReplRequire + // (internal/repl/utils.js). + let cwd; + try { + cwd = process.cwd(); + } catch { + cwd = path.dirname(process.execPath); + } + return Module.createRequire(path.join(cwd, "")); } let builtinLibs; @@ -205,7 +209,10 @@ function getBuiltinLibs() { } function addBuiltinLibsToObject(object, _dummy) { - // Make built-in modules available directly (loaded lazily). + // Make built-in modules available directly (loaded lazily). Builtin + // specifiers don't need a cwd-anchored referrer, so anchor to execPath + // (avoids ENOENT from process.cwd() in a deleted working directory). + const builtinRequire = Module.createRequire(process.execPath); getBuiltinLibs().forEach(name => { if (Object.getOwnPropertyDescriptor(object, name)) { return; @@ -221,7 +228,7 @@ function addBuiltinLibsToObject(object, _dummy) { Object.defineProperty(object, name, { __proto__: null, get: () => { - const lib = require("node:module").createRequire(path.join(process.cwd(), ""))(name); + const lib = builtinRequire(name); try { // Override the current getter/setter pair with the lib itself. From f25d419ce35620e07eb1b98bb27d0d4506f5e161 Mon Sep 17 00:00:00 2001 From: Alistair Smith Date: Tue, 7 Jul 2026 17:28:55 -0700 Subject: [PATCH 37/73] =?UTF-8?q?repl:=20address=20review=20=E2=80=94=20bo?= =?UTF-8?q?otstrap=20rewrite,=20lazy=20acorn,=20ErrorCode=20registry,=20re?= =?UTF-8?q?store=20vendored=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - --interactive: pass -e as JSON data (not spliced code) and run it via vm.runInThisContext after REPL.start(), matching Node internal/main/repl.js. A syntax error or unterminated `/`*` in -e can no longer swallow the bootstrap, and var/function from -e land on globalThis. The bootstrap now calls createInternalRepl (single NODE_REPL_* env parser) via a Symbol.for hook, honours NODE_REPL_EXTERNAL_MODULE, and prints a Bun-branded banner. - vm.Script: parse eagerly at construction (checkSyntax) like Node; drop the createCachedData()/throwaway-context workaround from makeContextifyScript. - acorn/acorn-walk: add MIT copyright, LICENSE.md/license.mdx rows, correct the vm.Script rationale comment, and lazy-load — the ~122 KB source parses on first property access, not on require("node:repl"). Consumers bind the module namespace instead of destructuring at load. - node-primordials: re-export the real makeSafe()-wrapped SafeSet/SafeMap/ SafeWeakSet/SafeStringIterator from internal/primordials (one definition per name); SideEffectFree RegExp helpers now use the load-time-captured intrinsics. - node-shims: re-export kEmptyObject/shouldColorize/addAbortListener from their in-tree homes; track whether the uncaught-exception dispatcher was installed so remove() never clears a user callback; unclaimed errors re-emit "uncaughtException" with the origin arg; correct the SIGINT-watchdog and sendInspectorCommand comments. - node-errors: register ERR_CANNOT_WATCH_SIGINT / ERR_INSPECTOR_NOT_AVAILABLE / ERR_INVALID_REPL_EVAL_CONFIG / ERR_INVALID_REPL_INPUT in ErrorCode.ts and route through $ERR_*; re-head .stack to toString() so [CODE] appears where the vendored tests match. Drop the inert overrideStackTrace/WeakMap and ErrorPrepareStackTrace (dead under JSC). - getStringWidth: single implementation in internal/util/inspect; node-inspect re-exports it so readline cursor math and its test hook are one path. - codegen: assert sliceSourceCode consumed the whole module (silent-truncation guard rail); comment kParenMessageRe with the builtin-parser regex-position limitation. - Delete the 12 dead ("use strict"); expression statements. - Docs: flip nodejs-compat.mdx node:repl to 🟡; add --interactive to run.mdx. - Tests: restore the 22 not-yet-passing upstream repl tests with per-test [ FAIL ] reasons in expectations.txt (matches http2/stream convention); categorise the 8 Windows hangs by likely mechanism; fill the vm displayErrors test.todo; add --interactive coverage (banner, -e declarations, -e syntax error/unterminated, script positional, -p precedence, external module) and RegExp-tampering / capture-callback deference cases. --- LICENSE.md | 2 + docs/project/license.mdx | 2 + docs/runtime/nodejs-compat.mdx | 2 +- docs/snippets/cli/run.mdx | 4 + src/codegen/bundle-modules.ts | 9 + src/js/eval/node-repl.ts | 112 +- src/js/internal/readline/callbacks.js | 1 - .../internal/readline/emitKeypressEvents.js | 1 - src/js/internal/readline/interface.js | 1 - src/js/internal/readline/promises.js | 1 - src/js/internal/readline/utils.js | 1 - src/js/internal/repl.js | 1 - src/js/internal/repl/acorn-walk.js | 46 +- src/js/internal/repl/acorn.js | 53 +- src/js/internal/repl/await.js | 4 + src/js/internal/repl/completion.js | 12 +- src/js/internal/repl/history.js | 1 - src/js/internal/repl/node-errors.js | 108 +- src/js/internal/repl/node-inspect.js | 21 +- src/js/internal/repl/node-primordials.js | 22 +- src/js/internal/repl/node-shims.js | 92 +- src/js/internal/repl/utils.js | 13 +- src/js/node/readline.js | 1 - src/js/node/readline.promises.js | 1 - src/js/node/repl.js | 81 +- src/jsc/bindings/ErrorCode.ts | 4 + src/jsc/bindings/NodeVMScript.cpp | 11 + src/runtime/cli/run_command.rs | 22 +- test/expectations.txt | 46 +- test/js/bun/repl/repl.test.ts | 156 ++- .../test/parallel/test-repl-autocomplete.js | 219 ++++ .../node/test/parallel/test-repl-cli-eval.js | 22 + .../test-repl-custom-eval-previews.js | 92 ++ .../js/node/test/parallel/test-repl-domain.js | 49 + .../parallel/test-repl-history-navigation.js | 933 +++++++++++++++ .../parallel/test-repl-import-referrer.js | 26 + .../parallel/test-repl-pretty-custom-stack.js | 77 ++ .../node/test/parallel/test-repl-preview.js | 272 +++++ .../node/test/parallel/test-repl-require.js | 73 ++ .../test/parallel/test-repl-reverse-search.js | 365 ++++++ .../parallel/test-repl-sigint-nested-eval.js | 53 + .../js/node/test/parallel/test-repl-sigint.js | 53 + .../test-repl-strict-mode-previews.js | 50 + .../test-repl-tab-complete-nested-repls.js | 23 + ...est-repl-tab-complete-unary-expressions.js | 116 ++ .../test/parallel/test-repl-tab-complete.js | 565 +++++++++ .../parallel/test-repl-top-level-await.js | 230 ++++ .../test-repl-unsafe-array-iteration.js | 68 ++ .../parallel/test-repl-unsupported-option.js | 11 + .../parallel/test-repl-user-error-handler.js | 84 ++ test/js/node/test/parallel/test-repl.js | 1053 +++++++++++++++++ .../sequential/test-repl-timeout-throw.js | 59 + test/js/node/vm/vm.test.ts | 46 +- 53 files changed, 5005 insertions(+), 365 deletions(-) create mode 100644 test/js/node/test/parallel/test-repl-autocomplete.js create mode 100644 test/js/node/test/parallel/test-repl-cli-eval.js create mode 100644 test/js/node/test/parallel/test-repl-custom-eval-previews.js create mode 100644 test/js/node/test/parallel/test-repl-domain.js create mode 100644 test/js/node/test/parallel/test-repl-history-navigation.js create mode 100644 test/js/node/test/parallel/test-repl-import-referrer.js create mode 100644 test/js/node/test/parallel/test-repl-pretty-custom-stack.js create mode 100644 test/js/node/test/parallel/test-repl-preview.js create mode 100644 test/js/node/test/parallel/test-repl-require.js create mode 100644 test/js/node/test/parallel/test-repl-reverse-search.js create mode 100644 test/js/node/test/parallel/test-repl-sigint-nested-eval.js create mode 100644 test/js/node/test/parallel/test-repl-sigint.js create mode 100644 test/js/node/test/parallel/test-repl-strict-mode-previews.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-nested-repls.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete-unary-expressions.js create mode 100644 test/js/node/test/parallel/test-repl-tab-complete.js create mode 100644 test/js/node/test/parallel/test-repl-top-level-await.js create mode 100644 test/js/node/test/parallel/test-repl-unsafe-array-iteration.js create mode 100644 test/js/node/test/parallel/test-repl-unsupported-option.js create mode 100644 test/js/node/test/parallel/test-repl-user-error-handler.js create mode 100644 test/js/node/test/parallel/test-repl.js create mode 100644 test/js/node/test/sequential/test-repl-timeout-throw.js diff --git a/LICENSE.md b/LICENSE.md index 81069ee8d3b8..0a48a8f9817e 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -54,6 +54,8 @@ For compatibility reasons, the following packages are embedded into Bun's binary | Package | License | |---------|---------| +| [`acorn`](https://github.com/acornjs/acorn) | MIT | +| [`acorn-walk`](https://github.com/acornjs/acorn) | MIT | | [`assert`](https://npmjs.com/package/assert) | MIT | | [`browserify-zlib`](https://npmjs.com/package/browserify-zlib) | MIT | | [`buffer`](https://npmjs.com/package/buffer) | MIT | diff --git a/docs/project/license.mdx b/docs/project/license.mdx index 6d44dd63e891..3482607a3e19 100644 --- a/docs/project/license.mdx +++ b/docs/project/license.mdx @@ -50,6 +50,8 @@ For compatibility reasons, the following packages are embedded into Bun's binary | Package | License | | ------------------------------------------------------------------------ | ------- | +| [`acorn`](https://github.com/acornjs/acorn) | MIT | +| [`acorn-walk`](https://github.com/acornjs/acorn) | MIT | | [`assert`](https://npmjs.com/package/assert) | MIT | | [`browserify-zlib`](https://npmjs.com/package/browserify-zlib) | MIT | | [`buffer`](https://npmjs.com/package/buffer) | MIT | diff --git a/docs/runtime/nodejs-compat.mdx b/docs/runtime/nodejs-compat.mdx index 313707b46800..02fc45cfc1e5 100644 --- a/docs/runtime/nodejs-compat.mdx +++ b/docs/runtime/nodejs-compat.mdx @@ -169,7 +169,7 @@ This page is updated regularly to reflect compatibility status of the latest ver ### [`node:repl`](https://nodejs.org/api/repl.html) -🔴 Not implemented. +🟡 Mostly implemented. `bun --interactive` starts a Node.js-compatible REPL. Result previews (which need V8's inspector-based side-effect-free eval), tab-completion of `let`/`const`/`class` bindings in `useGlobal: true` mode, and some V8-specific error-message wording differ. ### [`node:sqlite`](https://nodejs.org/api/sqlite.html) diff --git a/docs/snippets/cli/run.mdx b/docs/snippets/cli/run.mdx index ba4f375f256b..b42828e08550 100644 --- a/docs/snippets/cli/run.mdx +++ b/docs/snippets/cli/run.mdx @@ -62,6 +62,10 @@ bun run Control the shell used for package.json scripts. Supports either bun or system + + Open the Node.js-compatible REPL (node:repl). When combined with -e, evaluates the script first, then enters the REPL. Distinct from bun repl, which is Bun's native REPL. + + Use less memory, but run garbage collection more often diff --git a/src/codegen/bundle-modules.ts b/src/codegen/bundle-modules.ts index 45372839c33f..ea7bd7bdbce7 100644 --- a/src/codegen/bundle-modules.ts +++ b/src/codegen/bundle-modules.ts @@ -141,6 +141,15 @@ for (let i = 0; i < nativeStartIndex; i++) { true, x => requireTransformer(x, moduleList[i]), ); + // Guard rail: builtin-parser.ts's regex-position heuristic only recognises + // `/` as regex-start after `[(,=;:{]|return|=>`; a regex whose body has `)` + // or `}` in any other position silently truncates. Fail loudly here. + if (processed.rest.trim() !== "") { + throw new Error( + `sliceSourceCode truncated ${moduleList[i]} — likely a regex literal in a position builtin-parser.ts doesn't recognise. ` + + `Leftover starts: ${processed.rest.slice(0, 80)}`, + ); + } let fileToTranspile = `// GENERATED TEMP FILE - DO NOT EDIT // Sourced from src/js/${moduleList[i]} ${importStatements.join("\n")} diff --git a/src/js/eval/node-repl.ts b/src/js/eval/node-repl.ts index 2f46ca1efec8..0f969d031e96 100644 --- a/src/js/eval/node-repl.ts +++ b/src/js/eval/node-repl.ts @@ -1,70 +1,50 @@ // Entry script for `bun --interactive` (`-i` is taken by `--install=fallback`): starts the Node.js-compatible -// REPL (the ported node:repl) the way Node's internal/main/repl.js does, using -// only public node:repl APIs (this file runs as a regular entrypoint, so it -// cannot require internal modules). - -const REPL = require("node:repl"); - -console.log(`Welcome to Node.js ${process.version}.\n` + 'Type ".help" for more information.'); - -const opts: Record = { - ignoreUndefined: false, - useGlobal: true, - breakEvalOnSigint: true, -}; - -if (parseInt(process.env.NODE_NO_READLINE!)) { - opts.terminal = false; -} - -const replModeEnv = process.env.NODE_REPL_MODE; -if (replModeEnv) { - opts.replMode = { - strict: REPL.REPL_MODE_STRICT, - sloppy: REPL.REPL_MODE_SLOPPY, - }[replModeEnv.toLowerCase().trim()]; -} - -if (opts.replMode === undefined) { - opts.replMode = REPL.REPL_MODE_SLOPPY; -} - -const size = Number(process.env.NODE_REPL_HISTORY_SIZE); -if (!Number.isNaN(size) && size > 0) { - opts.size = size; +// REPL (the ported node:repl) the way Node's internal/main/repl.js does. This +// file runs as a regular entrypoint (not a builtin), so it reaches +// createInternalRepl via a Symbol.for hook on node:repl and never re-implements +// the NODE_REPL_* env parsing that internal/repl.js already owns. + +// exec_node_repl injects the user's `-e` script as a JSON string literal here +// (data, not code — a syntax error or unterminated token in `-e` cannot bleed +// into this bootstrap). Read and clear it before any user code runs. +declare const __BUN_EVAL_SCRIPT__: string | undefined; +const evalScript: string | undefined = typeof __BUN_EVAL_SCRIPT__ === "string" ? __BUN_EVAL_SCRIPT__ : undefined; + +const ext = process.env.NODE_REPL_EXTERNAL_MODULE; +if (ext) { + // Node loads this in place of the built-in REPL (lib/internal/main/repl.js). + require(require("node:path").resolve(ext)); } else { - opts.size = 1000; -} - -const term = "terminal" in opts ? opts.terminal : process.stdout.isTTY; -const filePath = term ? process.env.NODE_REPL_HISTORY : ""; - -// Standalone-REPL semantics (Node boots its CLI REPL through -// internal/repl with kStandaloneREPL set): relaxed input validation, -// repl.repl introspection, inspect.replDefaults writer wiring. -const kStandaloneREPL = (REPL as Record)[Symbol.for("bun.repl.kStandaloneREPL")]; -if (kStandaloneREPL) { - (opts as Record)[kStandaloneREPL] = true; -} - -const replServer = REPL.start(opts); - -replServer.setupHistory({ - filePath, - size: opts.size, - onHistoryFileLoaded: (err: Error | null) => { - if (err) { - throw err; - } - }, -}); - -replServer.on("exit", () => { - if (replServer.historyManager?.isFlushing) { - replServer.once("flushHistory", () => { + const REPL = require("node:repl"); + const createInternalRepl = (REPL as Record)[Symbol.for("bun.repl.createInternalRepl")]; + + console.log( + `Welcome to Bun v${(globalThis as any).Bun.version} (Node.js-compatible REPL, node:repl ${process.version}).\n` + + 'Type ".help" for more information.', + ); + + createInternalRepl(process.env, (err: Error | null, replServer: any) => { + if (err) throw err; + + replServer.on("exit", () => { + if (replServer.historyManager?.isFlushing) { + replServer.once("flushHistory", () => process.exit()); + return; + } process.exit(); }); - return; - } - process.exit(); -}); + + // `node -i -e`: Node runs the -e script as a separate compilation unit + // AFTER the REPL starts, so `var`/`function` land on the global object and + // a syntax/runtime error is reported at [eval]:1 with the REPL still live. + if (evalScript !== undefined) { + try { + require("node:vm").runInThisContext(evalScript, { filename: "[eval]", displayErrors: true }); + } catch (e) { + // Route through the REPL's own error printer so `Uncaught …` and the + // decorated stack render exactly as if typed at the prompt. + replServer._handleError(e); + } + } + }); +} diff --git a/src/js/internal/readline/callbacks.js b/src/js/internal/readline/callbacks.js index a07c4d06fe3c..03e4f87b5e45 100644 --- a/src/js/internal/readline/callbacks.js +++ b/src/js/internal/readline/callbacks.js @@ -3,7 +3,6 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -("use strict"); const { NumberIsNaN } = primordials; diff --git a/src/js/internal/readline/emitKeypressEvents.js b/src/js/internal/readline/emitKeypressEvents.js index a76f285db8cc..dbcb1240cad9 100644 --- a/src/js/internal/readline/emitKeypressEvents.js +++ b/src/js/internal/readline/emitKeypressEvents.js @@ -3,7 +3,6 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -("use strict"); const { SafeStringIterator, Symbol } = primordials; diff --git a/src/js/internal/readline/interface.js b/src/js/internal/readline/interface.js index 42f208937558..0d89a0eab70e 100644 --- a/src/js/internal/readline/interface.js +++ b/src/js/internal/readline/interface.js @@ -3,7 +3,6 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -("use strict"); const { ArrayFrom, diff --git a/src/js/internal/readline/promises.js b/src/js/internal/readline/promises.js index 7982b7d0807e..ec6c0899e56d 100644 --- a/src/js/internal/readline/promises.js +++ b/src/js/internal/readline/promises.js @@ -3,7 +3,6 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -("use strict"); const { ArrayPrototypeJoin, ArrayPrototypePush, Promise } = primordials; diff --git a/src/js/internal/readline/utils.js b/src/js/internal/readline/utils.js index 776ff8f124d5..a1d2e291d112 100644 --- a/src/js/internal/readline/utils.js +++ b/src/js/internal/readline/utils.js @@ -3,7 +3,6 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -("use strict"); const { ArrayPrototypeToSorted, diff --git a/src/js/internal/repl.js b/src/js/internal/repl.js index ec29b9f67f06..782cb30b98fc 100644 --- a/src/js/internal/repl.js +++ b/src/js/internal/repl.js @@ -3,7 +3,6 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -("use strict"); const { Number, NumberIsNaN, NumberParseInt } = primordials; diff --git a/src/js/internal/repl/acorn-walk.js b/src/js/internal/repl/acorn-walk.js index c372a4c74e0f..376a057da0c9 100644 --- a/src/js/internal/repl/acorn-walk.js +++ b/src/js/internal/repl/acorn-walk.js @@ -1,15 +1,33 @@ -// Vendored from Node.js v26.3.0 deps (acorn-walk.js, MIT licensed), minified with -// esbuild to keep the embedded-source literal within compiler limits. -// The dist uses ES5 function+prototype constructors, which JSC builtin -// semantics forbid (builtin functions are non-constructors). Evaluate the -// source via vm.Script so it runs with full JavaScript semantics. +// Vendored from Node.js v26.3.0 deps (acorn-walk.js), minified with esbuild +// to keep the embedded-source literal within compiler limits. +// +// acorn-walk — Copyright (C) 2012-2022 by various contributors (see AUTHORS) +// MIT License. https://github.com/acornjs/acorn +// +// See internal/repl/acorn.js for the vm.Script rationale. Lazy-loaded. // prettier-ignore -const vm = require("node:vm"); -const exportsObj = {}; -const moduleObj = { exports: exportsObj }; -const factory = new vm.Script( - '(function(exports, module){(function(c,v){typeof exports=="object"&&typeof module<"u"?v(exports):typeof define=="function"&&define.amd?define(["exports"],v):(c=typeof globalThis<"u"?globalThis:c||self,v((c.acorn=c.acorn||{},c.acorn.walk={})))})(this,function(c){"use strict";function v(t,n,e,r,a){e||(e=i),function f(o,u,p){var s=p||o.type;m(e,s,o,u,f),n[s]&&n[s](o,u)}(t,r,a)}function P(t,n,e,r,a){var f=[];e||(e=i),function o(u,p,s){var l=s||u.type,h=u!==f[f.length-1];h&&f.push(u),m(e,l,u,p,o),n[l]&&n[l](u,p||f,f),h&&f.pop()}(t,r,a)}function w(t,n,e,r,a){var f=e?g(e,r||void 0):r;(function o(u,p,s){f[s||u.type](u,p,o)})(t,n,a)}function y(t){return typeof t=="string"?function(n){return n===t}:t||function(){return!0}}var E=function(n,e){this.node=n,this.state=e};function b(t,n,e,r,a){e||(e=i);var f;(function o(u,p,s){var l=s||u.type;m(e,l,u,p,o),f!==u&&(n(u,p,l),f=u)})(t,r,a)}function D(t,n,e,r){e||(e=i);var a=[],f;(function o(u,p,s){var l=s||u.type,h=u!==a[a.length-1];h&&a.push(u),m(e,l,u,p,o),f!==u&&(n(u,p||a,a,l),f=u),h&&a.pop()})(t,r)}function A(t,n,e,r,a,f){a||(a=i),r=y(r);try{(function o(u,p,s){var l=s||u.type;if((n==null||u.start<=n)&&(e==null||u.end>=e)&&m(a,l,u,p,o),(n==null||u.start===n)&&(e==null||u.end===e)&&r(l,u))throw new E(u,p)})(t,f)}catch(o){if(o instanceof E)return o;throw o}}function N(t,n,e,r,a){e=y(e),r||(r=i);try{(function f(o,u,p){var s=p||o.type;if(!(o.start>n||o.end=n&&e(s,o))throw new E(o,u);m(r,s,o,u,f)}})(t,a)}catch(f){if(f instanceof E)return f;throw f}}function F(t,n,e,r,a){e=y(e),r||(r=i);var f;return function o(u,p,s){if(!(u.start>n)){var l=s||u.type;u.end<=n&&(!f||f.node.end=e)&&m(a,l,u,p,o),(n==null||u.start===n)&&(e==null||u.end===e)&&r(l,u))throw new E(u,p)})(t,f)}catch(o){if(o instanceof E)return o;throw o}}function N(t,n,e,r,a){e=y(e),r||(r=i);try{(function f(o,u,p){var s=p||o.type;if(!(o.start>n||o.end=n&&e(s,o))throw new E(o,u);m(r,s,o,u,f)}})(t,a)}catch(f){if(f instanceof E)return f;throw f}}function F(t,n,e,r,a){e=y(e),r||(r=i);var f;return function o(u,p,s){if(!(u.start>n)){var l=s||u.type;u.end<=n&&(!f||f.node.ende)return!1;if(i+=t[s+1],i>=e)return!0}return!1}function L(e,t){return e<65?e===36:e<91?!0:e<97?e===95:e<123?!0:e<=65535?e>=170&&bt.test(String.fromCharCode(e)):t===!1?!1:ge(e,Pe)}function O(e,t){return e<48?e===36:e<58?!0:e<65?!1:e<91?!0:e<97?e===95:e<123?!0:e<=65535?e>=170&&yt.test(String.fromCharCode(e)):t===!1?!1:ge(e,Pe)||ge(e,Z)}var v=function(t,i){i===void 0&&(i={}),this.label=t,this.keyword=i.keyword,this.beforeExpr=!!i.beforeExpr,this.startsExpr=!!i.startsExpr,this.isLoop=!!i.isLoop,this.isAssign=!!i.isAssign,this.prefix=!!i.prefix,this.postfix=!!i.postfix,this.binop=i.binop||null,this.updateContext=null};function E(e,t){return new v(e,{beforeExpr:!0,binop:t})}var I={beforeExpr:!0},A={startsExpr:!0},ae={};function m(e,t){return t===void 0&&(t={}),t.keyword=e,ae[e]=new v(e,t)}var a={num:new v("num",A),regexp:new v("regexp",A),string:new v("string",A),name:new v("name",A),privateId:new v("privateId",A),eof:new v("eof"),bracketL:new v("[",{beforeExpr:!0,startsExpr:!0}),bracketR:new v("]"),braceL:new v("{",{beforeExpr:!0,startsExpr:!0}),braceR:new v("}"),parenL:new v("(",{beforeExpr:!0,startsExpr:!0}),parenR:new v(")"),comma:new v(",",I),semi:new v(";",I),colon:new v(":",I),dot:new v("."),question:new v("?",I),questionDot:new v("?."),arrow:new v("=>",I),template:new v("template"),invalidTemplate:new v("invalidTemplate"),ellipsis:new v("...",I),backQuote:new v("`",A),dollarBraceL:new v("${",{beforeExpr:!0,startsExpr:!0}),eq:new v("=",{beforeExpr:!0,isAssign:!0}),assign:new v("_=",{beforeExpr:!0,isAssign:!0}),incDec:new v("++/--",{prefix:!0,postfix:!0,startsExpr:!0}),prefix:new v("!/~",{beforeExpr:!0,prefix:!0,startsExpr:!0}),logicalOR:E("||",1),logicalAND:E("&&",2),bitwiseOR:E("|",3),bitwiseXOR:E("^",4),bitwiseAND:E("&",5),equality:E("==/!=/===/!==",6),relational:E("/<=/>=",7),bitShift:E("<>/>>>",8),plusMin:new v("+/-",{beforeExpr:!0,binop:9,prefix:!0,startsExpr:!0}),modulo:E("%",10),star:E("*",10),slash:E("/",10),starstar:new v("**",{beforeExpr:!0}),coalesce:E("??",1),_break:m("break"),_case:m("case",I),_catch:m("catch"),_continue:m("continue"),_debugger:m("debugger"),_default:m("default",I),_do:m("do",{isLoop:!0,beforeExpr:!0}),_else:m("else",I),_finally:m("finally"),_for:m("for",{isLoop:!0}),_function:m("function",A),_if:m("if"),_return:m("return",I),_switch:m("switch"),_throw:m("throw",I),_try:m("try"),_var:m("var"),_const:m("const"),_while:m("while",{isLoop:!0}),_with:m("with"),_new:m("new",{beforeExpr:!0,startsExpr:!0}),_this:m("this",A),_super:m("super",A),_class:m("class",A),_extends:m("extends",I),_export:m("export"),_import:m("import",A),_null:m("null",A),_true:m("true",A),_false:m("false",A),_in:m("in",{beforeExpr:!0,binop:7}),_instanceof:m("instanceof",{beforeExpr:!0,binop:7}),_typeof:m("typeof",{beforeExpr:!0,prefix:!0,startsExpr:!0}),_void:m("void",{beforeExpr:!0,prefix:!0,startsExpr:!0}),_delete:m("delete",{beforeExpr:!0,prefix:!0,startsExpr:!0})},k=/\\r\\n?|\\n|\\u2028|\\u2029/,Te=new RegExp(k.source,"g");function q(e){return e===10||e===13||e===8232||e===8233}function Le(e,t,i){i===void 0&&(i=e.length);for(var s=t;s>10)+55296,(e&1023)+56320))}var St=/(?:[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF])/,z=function(t,i){this.line=t,this.column=i};z.prototype.offset=function(t){return new z(this.line,this.column+t)};var J=function(t,i,s){this.start=i,this.end=s,t.sourceFile!==null&&(this.source=t.sourceFile)};function ye(e,t){for(var i=1,s=0;;){var r=Le(e,s,t);if(r<0)return new z(i,t-s);++i,s=r}}var re={ecmaVersion:null,sourceType:"script",onInsertedSemicolon:null,onTrailingComma:null,allowReserved:null,allowReturnOutsideFunction:!1,allowImportExportEverywhere:!1,allowAwaitOutsideFunction:null,allowSuperOutsideMethod:null,allowHashBang:!1,checkPrivateFields:!0,locations:!1,onToken:null,onComment:null,ranges:!1,program:null,sourceFile:null,directSourceFile:null,preserveParens:!1},De=!1;function kt(e){var t={};for(var i in re)t[i]=e&&W(e,i)?e[i]:re[i];if(t.ecmaVersion==="latest"?t.ecmaVersion=1e8:t.ecmaVersion==null?(!De&&typeof console=="object"&&console.warn&&(De=!0,console.warn(`Since Acorn 8.0.0, options.ecmaVersion is required.\nDefaulting to 2020, but this will stop working in the future.`)),t.ecmaVersion=11):t.ecmaVersion>=2015&&(t.ecmaVersion-=2009),t.allowReserved==null&&(t.allowReserved=t.ecmaVersion<5),(!e||e.allowHashBang==null)&&(t.allowHashBang=t.ecmaVersion>=14),Oe(t.onToken)){var s=t.onToken;t.onToken=function(r){return s.push(r)}}if(Oe(t.onComment)&&(t.onComment=wt(t,t.onComment)),t.sourceType==="commonjs"&&t.allowAwaitOutsideFunction)throw new Error("Cannot use allowAwaitOutsideFunction with sourceType: commonjs");return t}function wt(e,t){return function(i,s,r,n,u,o){var h={type:i?"Block":"Line",value:s,start:r,end:n};e.locations&&(h.loc=new J(this,u,o)),e.ranges&&(h.range=[r,n]),t.push(h)}}var j=1,G=2,Ce=4,Fe=8,_e=16,Me=32,ne=64,Ue=128,H=256,$=512,qe=1024,ue=j|G|H;function Se(e,t){return G|(e?Ce:0)|(t?Fe:0)}var oe=0,ke=1,D=2,je=3,Ge=4,He=5,C=function(t,i,s){this.options=t=kt(t),this.sourceFile=t.sourceFile,this.keywords=M(gt[t.ecmaVersion>=6?6:t.sourceType==="module"?"5module":5]);var r="";t.allowReserved!==!0&&(r=me[t.ecmaVersion>=6?6:t.ecmaVersion===5?5:3],t.sourceType==="module"&&(r+=" await")),this.reservedWords=M(r);var n=(r?r+" ":"")+me.strict;this.reservedWordsStrict=M(n),this.reservedWordsStrictBind=M(n+" "+me.strictBind),this.input=String(i),this.containsEsc=!1,s?(this.pos=s,this.lineStart=this.input.lastIndexOf(`\n`,s-1)+1,this.curLine=this.input.slice(0,this.lineStart).split(k).length):(this.pos=this.lineStart=0,this.curLine=1),this.type=a.eof,this.value=null,this.start=this.end=this.pos,this.startLoc=this.endLoc=this.curPosition(),this.lastTokEndLoc=this.lastTokStartLoc=null,this.lastTokStart=this.lastTokEnd=this.pos,this.context=this.initialContext(),this.exprAllowed=!0,this.inModule=t.sourceType==="module",this.strict=this.inModule||this.strictDirective(this.pos),this.potentialArrowAt=-1,this.potentialArrowInForAwait=!1,this.yieldPos=this.awaitPos=this.awaitIdentPos=0,this.labels=[],this.undefinedExports=Object.create(null),this.pos===0&&t.allowHashBang&&this.input.slice(0,2)==="#!"&&this.skipLineComment(2),this.scopeStack=[],this.enterScope(this.options.sourceType==="commonjs"?G:j),this.regexpState=null,this.privateNameStack=[]},P={inFunction:{configurable:!0},inGenerator:{configurable:!0},inAsync:{configurable:!0},canAwait:{configurable:!0},allowReturn:{configurable:!0},allowSuper:{configurable:!0},allowDirectSuper:{configurable:!0},treatFunctionsAsVar:{configurable:!0},allowNewDotTarget:{configurable:!0},allowUsing:{configurable:!0},inClassStaticBlock:{configurable:!0}};C.prototype.parse=function(){var t=this.options.program||this.startNode();return this.nextToken(),this.parseTopLevel(t)},P.inFunction.get=function(){return(this.currentVarScope().flags&G)>0},P.inGenerator.get=function(){return(this.currentVarScope().flags&Fe)>0},P.inAsync.get=function(){return(this.currentVarScope().flags&Ce)>0},P.canAwait.get=function(){for(var e=this.scopeStack.length-1;e>=0;e--){var t=this.scopeStack[e],i=t.flags;if(i&(H|$))return!1;if(i&G)return(i&Ce)>0}return this.inModule&&this.options.ecmaVersion>=13||this.options.allowAwaitOutsideFunction},P.allowReturn.get=function(){return!!(this.inFunction||this.options.allowReturnOutsideFunction&&this.currentVarScope().flags&j)},P.allowSuper.get=function(){var e=this.currentThisScope(),t=e.flags;return(t&ne)>0||this.options.allowSuperOutsideMethod},P.allowDirectSuper.get=function(){return(this.currentThisScope().flags&Ue)>0},P.treatFunctionsAsVar.get=function(){return this.treatFunctionsAsVarInScope(this.currentScope())},P.allowNewDotTarget.get=function(){for(var e=this.scopeStack.length-1;e>=0;e--){var t=this.scopeStack[e],i=t.flags;if(i&(H|$)||i&G&&!(i&_e))return!0}return!1},P.allowUsing.get=function(){var e=this.currentScope(),t=e.flags;return!(t&qe||!this.inModule&&t&j)},P.inClassStaticBlock.get=function(){return(this.currentVarScope().flags&H)>0},C.extend=function(){for(var t=[],i=arguments.length;i--;)t[i]=arguments[i];for(var s=this,r=0;r=,?^&]/.test(r)||r==="!"&&this.input.charAt(s+1)==="=")}e+=t[0].length,_.lastIndex=e,e+=_.exec(this.input)[0].length,this.input[e]===";"&&e++}},w.eat=function(e){return this.type===e?(this.next(),!0):!1},w.isContextual=function(e){return this.type===a.name&&this.value===e&&!this.containsEsc},w.eatContextual=function(e){return this.isContextual(e)?(this.next(),!0):!1},w.expectContextual=function(e){this.eatContextual(e)||this.unexpected()},w.canInsertSemicolon=function(){return this.type===a.eof||this.type===a.braceR||k.test(this.input.slice(this.lastTokEnd,this.start))},w.insertSemicolon=function(){if(this.canInsertSemicolon())return this.options.onInsertedSemicolon&&this.options.onInsertedSemicolon(this.lastTokEnd,this.lastTokEndLoc),!0},w.semicolon=function(){!this.eat(a.semi)&&!this.insertSemicolon()&&this.unexpected()},w.afterTrailingComma=function(e,t){if(this.type===e)return this.options.onTrailingComma&&this.options.onTrailingComma(this.lastTokStart,this.lastTokStartLoc),t||this.next(),!0},w.expect=function(e){this.eat(e)||this.unexpected()},w.unexpected=function(e){this.raise(e??this.start,"Unexpected token")};var he=function(){this.shorthandAssign=this.trailingComma=this.parenthesizedAssign=this.parenthesizedBind=this.doubleProto=-1};w.checkPatternErrors=function(e,t){if(e){e.trailingComma>-1&&this.raiseRecoverable(e.trailingComma,"Comma is not permitted after the rest element");var i=t?e.parenthesizedAssign:e.parenthesizedBind;i>-1&&this.raiseRecoverable(i,t?"Assigning to rvalue":"Parenthesized pattern")}},w.checkExpressionErrors=function(e,t){if(!e)return!1;var i=e.shorthandAssign,s=e.doubleProto;if(!t)return i>=0||s>=0;i>=0&&this.raise(i,"Shorthand property assignments are valid only in destructuring patterns"),s>=0&&this.raiseRecoverable(s,"Redefinition of __proto__ property")},w.checkYieldAwaitInDefaultParams=function(){this.yieldPos&&(!this.awaitPos||this.yieldPos=6&&this.unexpected(),this.parseFunctionStatement(r,!1,!e);case a._class:return e&&this.unexpected(),this.parseClass(r,!0);case a._if:return this.parseIfStatement(r);case a._return:return this.parseReturnStatement(r);case a._switch:return this.parseSwitchStatement(r);case a._throw:return this.parseThrowStatement(r);case a._try:return this.parseTryStatement(r);case a._const:case a._var:return n=n||this.value,e&&n!=="var"&&this.unexpected(),this.parseVarStatement(r,n);case a._while:return this.parseWhileStatement(r);case a._with:return this.parseWithStatement(r);case a.braceL:return this.parseBlock(!0,r);case a.semi:return this.parseEmptyStatement(r);case a._export:case a._import:if(this.options.ecmaVersion>10&&s===a._import){_.lastIndex=this.pos;var u=_.exec(this.input),o=this.pos+u[0].length,h=this.input.charCodeAt(o);if(h===40||h===46)return this.parseExpressionStatement(r,this.parseExpression())}return this.options.allowImportExportEverywhere||(t||this.raise(this.start,"\'import\' and \'export\' may only appear at the top level"),this.inModule||this.raise(this.start,"\'import\' and \'export\' may appear only with \'sourceType: module\'")),s===a._import?this.parseImport(r):this.parseExport(r,i);default:if(this.isAsyncFunction())return e&&this.unexpected(),this.next(),this.parseFunctionStatement(r,!0,!e);var p=this.isAwaitUsing(!1)?"await using":this.isUsing(!1)?"using":null;if(p)return this.allowUsing||this.raise(this.start,"Using declaration cannot appear in the top level when source type is `script` or in the bare case statement"),p==="await using"&&(this.canAwait||this.raise(this.start,"Await using cannot appear outside of async function"),this.next()),this.next(),this.parseVar(r,!1,p),this.semicolon(),this.finishNode(r,"VariableDeclaration");var d=this.value,y=this.parseExpression();return s===a.name&&y.type==="Identifier"&&this.eat(a.colon)?this.parseLabeledStatement(r,d,y,e):this.parseExpressionStatement(r,y)}},l.parseBreakContinueStatement=function(e,t){var i=t==="break";this.next(),this.eat(a.semi)||this.insertSemicolon()?e.label=null:this.type!==a.name?this.unexpected():(e.label=this.parseIdent(),this.semicolon());for(var s=0;s=6?this.eat(a.semi):this.semicolon(),this.finishNode(e,"DoWhileStatement")},l.parseForStatement=function(e){this.next();var t=this.options.ecmaVersion>=9&&this.canAwait&&this.eatContextual("await")?this.lastTokStart:-1;if(this.labels.push(we),this.enterScope(0),this.expect(a.parenL),this.type===a.semi)return t>-1&&this.unexpected(t),this.parseFor(e,null);var i=this.isLet();if(this.type===a._var||this.type===a._const||i){var s=this.startNode(),r=i?"let":this.value;return this.next(),this.parseVar(s,!0,r),this.finishNode(s,"VariableDeclaration"),this.parseForAfterInit(e,s,t)}var n=this.isContextual("let"),u=!1,o=this.isUsing(!0)?"using":this.isAwaitUsing(!0)?"await using":null;if(o){var h=this.startNode();return this.next(),o==="await using"&&(this.canAwait||this.raise(this.start,"Await using cannot appear outside of async function"),this.next()),this.parseVar(h,!0,o),this.finishNode(h,"VariableDeclaration"),this.parseForAfterInit(e,h,t)}var p=this.containsEsc,d=new he,y=this.start,S=t>-1?this.parseExprSubscripts(d,"await"):this.parseExpression(!0,d);return this.type===a._in||(u=this.options.ecmaVersion>=6&&this.isContextual("of"))?(t>-1?(this.type===a._in&&this.unexpected(t),e.await=!0):u&&this.options.ecmaVersion>=8&&(S.start===y&&!p&&S.type==="Identifier"&&S.name==="async"?this.unexpected():this.options.ecmaVersion>=9&&(e.await=!1)),n&&u&&this.raise(S.start,"The left-hand side of a for-of loop may not start with \'let\'."),this.toAssignable(S,!1,d),this.checkLValPattern(S),this.parseForIn(e,S)):(this.checkExpressionErrors(d,!0),t>-1&&this.unexpected(t),this.parseFor(e,S))},l.parseForAfterInit=function(e,t,i){return(this.type===a._in||this.options.ecmaVersion>=6&&this.isContextual("of"))&&t.declarations.length===1?(this.options.ecmaVersion>=9&&(this.type===a._in?i>-1&&this.unexpected(i):e.await=i>-1),this.parseForIn(e,t)):(i>-1&&this.unexpected(i),this.parseFor(e,t))},l.parseFunctionStatement=function(e,t,i){return this.next(),this.parseFunction(e,ee|(i?0:Ae),!1,t)},l.parseIfStatement=function(e){return this.next(),e.test=this.parseParenExpression(),e.consequent=this.parseStatement("if"),e.alternate=this.eat(a._else)?this.parseStatement("if"):null,this.finishNode(e,"IfStatement")},l.parseReturnStatement=function(e){return this.allowReturn||this.raise(this.start,"\'return\' outside of function"),this.next(),this.eat(a.semi)||this.insertSemicolon()?e.argument=null:(e.argument=this.parseExpression(),this.semicolon()),this.finishNode(e,"ReturnStatement")},l.parseSwitchStatement=function(e){this.next(),e.discriminant=this.parseParenExpression(),e.cases=[],this.expect(a.braceL),this.labels.push(Et),this.enterScope(qe);for(var t,i=!1;this.type!==a.braceR;)if(this.type===a._case||this.type===a._default){var s=this.type===a._case;t&&this.finishNode(t,"SwitchCase"),e.cases.push(t=this.startNode()),t.consequent=[],this.next(),s?t.test=this.parseExpression():(i&&this.raiseRecoverable(this.lastTokStart,"Multiple default clauses"),i=!0,t.test=null),this.expect(a.colon)}else t||this.unexpected(),t.consequent.push(this.parseStatement(null));return this.exitScope(),t&&this.finishNode(t,"SwitchCase"),this.next(),this.labels.pop(),this.finishNode(e,"SwitchStatement")},l.parseThrowStatement=function(e){return this.next(),k.test(this.input.slice(this.lastTokEnd,this.start))&&this.raise(this.lastTokEnd,"Illegal newline after throw"),e.argument=this.parseExpression(),this.semicolon(),this.finishNode(e,"ThrowStatement")};var It=[];l.parseCatchClauseParam=function(){var e=this.parseBindingAtom(),t=e.type==="Identifier";return this.enterScope(t?Me:0),this.checkLValPattern(e,t?Ge:D),this.expect(a.parenR),e},l.parseTryStatement=function(e){if(this.next(),e.block=this.parseBlock(),e.handler=null,this.type===a._catch){var t=this.startNode();this.next(),this.eat(a.parenL)?t.param=this.parseCatchClauseParam():(this.options.ecmaVersion<10&&this.unexpected(),t.param=null,this.enterScope(0)),t.body=this.parseBlock(!1),this.exitScope(),e.handler=this.finishNode(t,"CatchClause")}return e.finalizer=this.eat(a._finally)?this.parseBlock():null,!e.handler&&!e.finalizer&&this.raise(e.start,"Missing catch or finally clause"),this.finishNode(e,"TryStatement")},l.parseVarStatement=function(e,t,i){return this.next(),this.parseVar(e,!1,t,i),this.semicolon(),this.finishNode(e,"VariableDeclaration")},l.parseWhileStatement=function(e){return this.next(),e.test=this.parseParenExpression(),this.labels.push(we),e.body=this.parseStatement("while"),this.labels.pop(),this.finishNode(e,"WhileStatement")},l.parseWithStatement=function(e){return this.strict&&this.raise(this.start,"\'with\' in strict mode"),this.next(),e.object=this.parseParenExpression(),e.body=this.parseStatement("with"),this.finishNode(e,"WithStatement")},l.parseEmptyStatement=function(e){return this.next(),this.finishNode(e,"EmptyStatement")},l.parseLabeledStatement=function(e,t,i,s){for(var r=0,n=this.labels;r=0;h--){var p=this.labels[h];if(p.statementStart===e.start)p.statementStart=this.start,p.kind=o;else break}return this.labels.push({name:t,kind:o,statementStart:this.start}),e.body=this.parseStatement(s?s.indexOf("label")===-1?s+"label":s:"label"),this.labels.pop(),e.label=i,this.finishNode(e,"LabeledStatement")},l.parseExpressionStatement=function(e,t){return e.expression=t,this.semicolon(),this.finishNode(e,"ExpressionStatement")},l.parseBlock=function(e,t,i){for(e===void 0&&(e=!0),t===void 0&&(t=this.startNode()),t.body=[],this.expect(a.braceL),e&&this.enterScope(0);this.type!==a.braceR;){var s=this.parseStatement(null);t.body.push(s)}return i&&(this.strict=!1),this.next(),e&&this.exitScope(),this.finishNode(t,"BlockStatement")},l.parseFor=function(e,t){return e.init=t,this.expect(a.semi),e.test=this.type===a.semi?null:this.parseExpression(),this.expect(a.semi),e.update=this.type===a.parenR?null:this.parseExpression(),this.expect(a.parenR),e.body=this.parseStatement("for"),this.exitScope(),this.labels.pop(),this.finishNode(e,"ForStatement")},l.parseForIn=function(e,t){var i=this.type===a._in;return this.next(),t.type==="VariableDeclaration"&&t.declarations[0].init!=null&&(!i||this.options.ecmaVersion<8||this.strict||t.kind!=="var"||t.declarations[0].id.type!=="Identifier")&&this.raise(t.start,(i?"for-in":"for-of")+" loop variable declaration may not have an initializer"),e.left=t,e.right=i?this.parseExpression():this.parseMaybeAssign(),this.expect(a.parenR),e.body=this.parseStatement("for"),this.exitScope(),this.labels.pop(),this.finishNode(e,i?"ForInStatement":"ForOfStatement")},l.parseVar=function(e,t,i,s){for(e.declarations=[],e.kind=i;;){var r=this.startNode();if(this.parseVarId(r,i),this.eat(a.eq)?r.init=this.parseMaybeAssign(t):!s&&i==="const"&&!(this.type===a._in||this.options.ecmaVersion>=6&&this.isContextual("of"))?this.unexpected():!s&&(i==="using"||i==="await using")&&this.options.ecmaVersion>=17&&this.type!==a._in&&!this.isContextual("of")?this.raise(this.lastTokEnd,"Missing initializer in "+i+" declaration"):!s&&r.id.type!=="Identifier"&&!(t&&(this.type===a._in||this.isContextual("of")))?this.raise(this.lastTokEnd,"Complex binding patterns require an initialization value"):r.init=null,e.declarations.push(this.finishNode(r,"VariableDeclarator")),!this.eat(a.comma))break}return e},l.parseVarId=function(e,t){e.id=t==="using"||t==="await using"?this.parseIdent():this.parseBindingAtom(),this.checkLValPattern(e.id,t==="var"?ke:D,!1)};var ee=1,Ae=2,We=4;l.parseFunction=function(e,t,i,s,r){this.initFunction(e),(this.options.ecmaVersion>=9||this.options.ecmaVersion>=6&&!s)&&(this.type===a.star&&t&Ae&&this.unexpected(),e.generator=this.eat(a.star)),this.options.ecmaVersion>=8&&(e.async=!!s),t&ee&&(e.id=t&We&&this.type!==a.name?null:this.parseIdent(),e.id&&!(t&Ae)&&this.checkLValSimple(e.id,this.strict||e.generator||e.async?this.treatFunctionsAsVar?ke:D:je));var n=this.yieldPos,u=this.awaitPos,o=this.awaitIdentPos;return this.yieldPos=0,this.awaitPos=0,this.awaitIdentPos=0,this.enterScope(Se(e.async,e.generator)),t&ee||(e.id=this.type===a.name?this.parseIdent():null),this.parseFunctionParams(e),this.parseFunctionBody(e,i,!1,r),this.yieldPos=n,this.awaitPos=u,this.awaitIdentPos=o,this.finishNode(e,t&ee?"FunctionDeclaration":"FunctionExpression")},l.parseFunctionParams=function(e){this.expect(a.parenL),e.params=this.parseBindingList(a.parenR,!1,this.options.ecmaVersion>=8),this.checkYieldAwaitInDefaultParams()},l.parseClass=function(e,t){this.next();var i=this.strict;this.strict=!0,this.parseClassId(e,t),this.parseClassSuper(e);var s=this.enterClassBody(),r=this.startNode(),n=!1;for(r.body=[],this.expect(a.braceL);this.type!==a.braceR;){var u=this.parseClassElement(e.superClass!==null);u&&(r.body.push(u),u.type==="MethodDefinition"&&u.kind==="constructor"?(n&&this.raiseRecoverable(u.start,"Duplicate constructor in the same class"),n=!0):u.key&&u.key.type==="PrivateIdentifier"&&Pt(s,u)&&this.raiseRecoverable(u.key.start,"Identifier \'#"+u.key.name+"\' has already been declared"))}return this.strict=i,this.next(),e.body=this.finishNode(r,"ClassBody"),this.exitClassBody(),this.finishNode(e,t?"ClassDeclaration":"ClassExpression")},l.parseClassElement=function(e){if(this.eat(a.semi))return null;var t=this.options.ecmaVersion,i=this.startNode(),s="",r=!1,n=!1,u="method",o=!1;if(this.eatContextual("static")){if(t>=13&&this.eat(a.braceL))return this.parseClassStaticBlock(i),i;this.isClassElementNameStart()||this.type===a.star?o=!0:s="static"}if(i.static=o,!s&&t>=8&&this.eatContextual("async")&&((this.isClassElementNameStart()||this.type===a.star)&&!this.canInsertSemicolon()?n=!0:s="async"),!s&&(t>=9||!n)&&this.eat(a.star)&&(r=!0),!s&&!n&&!r){var h=this.value;(this.eatContextual("get")||this.eatContextual("set"))&&(this.isClassElementNameStart()?u=h:s=h)}if(s?(i.computed=!1,i.key=this.startNodeAt(this.lastTokStart,this.lastTokStartLoc),i.key.name=s,this.finishNode(i.key,"Identifier")):this.parseClassElementName(i),t<13||this.type===a.parenL||u!=="method"||r||n){var p=!i.static&&ce(i,"constructor"),d=p&&e;p&&u!=="method"&&this.raise(i.key.start,"Constructor can\'t have get/set modifier"),i.kind=p?"constructor":u,this.parseClassMethod(i,r,n,d)}else this.parseClassField(i);return i},l.isClassElementNameStart=function(){return this.type===a.name||this.type===a.privateId||this.type===a.num||this.type===a.string||this.type===a.bracketL||this.type.keyword},l.parseClassElementName=function(e){this.type===a.privateId?(this.value==="constructor"&&this.raise(this.start,"Classes can\'t have an element named \'#constructor\'"),e.computed=!1,e.key=this.parsePrivateIdent()):this.parsePropertyName(e)},l.parseClassMethod=function(e,t,i,s){var r=e.key;e.kind==="constructor"?(t&&this.raise(r.start,"Constructor can\'t be a generator"),i&&this.raise(r.start,"Constructor can\'t be an async method")):e.static&&ce(e,"prototype")&&this.raise(r.start,"Classes may not have a static property named prototype");var n=e.value=this.parseMethod(t,i,s);return e.kind==="get"&&n.params.length!==0&&this.raiseRecoverable(n.start,"getter should have no params"),e.kind==="set"&&n.params.length!==1&&this.raiseRecoverable(n.start,"setter should have exactly one param"),e.kind==="set"&&n.params[0].type==="RestElement"&&this.raiseRecoverable(n.params[0].start,"Setter cannot use rest params"),this.finishNode(e,"MethodDefinition")},l.parseClassField=function(e){return ce(e,"constructor")?this.raise(e.key.start,"Classes can\'t have a field named \'constructor\'"):e.static&&ce(e,"prototype")&&this.raise(e.key.start,"Classes can\'t have a static field named \'prototype\'"),this.eat(a.eq)?(this.enterScope($|ne),e.value=this.parseMaybeAssign(),this.exitScope()):e.value=null,this.semicolon(),this.finishNode(e,"PropertyDefinition")},l.parseClassStaticBlock=function(e){e.body=[];var t=this.labels;for(this.labels=[],this.enterScope(H|ne);this.type!==a.braceR;){var i=this.parseStatement(null);e.body.push(i)}return this.next(),this.exitScope(),this.labels=t,this.finishNode(e,"StaticBlock")},l.parseClassId=function(e,t){this.type===a.name?(e.id=this.parseIdent(),t&&this.checkLValSimple(e.id,D,!1)):(t===!0&&this.unexpected(),e.id=null)},l.parseClassSuper=function(e){e.superClass=this.eat(a._extends)?this.parseExprSubscripts(null,!1):null},l.enterClassBody=function(){var e={declared:Object.create(null),used:[]};return this.privateNameStack.push(e),e.declared},l.exitClassBody=function(){var e=this.privateNameStack.pop(),t=e.declared,i=e.used;if(this.options.checkPrivateFields)for(var s=this.privateNameStack.length,r=s===0?null:this.privateNameStack[s-1],n=0;n=11&&(this.eatContextual("as")?(e.exported=this.parseModuleExportName(),this.checkExport(t,e.exported,this.lastTokStart)):e.exported=null),this.expectContextual("from"),this.type!==a.string&&this.unexpected(),e.source=this.parseExprAtom(),this.options.ecmaVersion>=16&&(e.attributes=this.parseWithClause()),this.semicolon(),this.finishNode(e,"ExportAllDeclaration")},l.parseExport=function(e,t){if(this.next(),this.eat(a.star))return this.parseExportAllDeclaration(e,t);if(this.eat(a._default))return this.checkExport(t,"default",this.lastTokStart),e.declaration=this.parseExportDefaultDeclaration(),this.finishNode(e,"ExportDefaultDeclaration");if(this.shouldParseExportStatement())e.declaration=this.parseExportDeclaration(e),e.declaration.type==="VariableDeclaration"?this.checkVariableExport(t,e.declaration.declarations):this.checkExport(t,e.declaration.id,e.declaration.id.start),e.specifiers=[],e.source=null,this.options.ecmaVersion>=16&&(e.attributes=[]);else{if(e.declaration=null,e.specifiers=this.parseExportSpecifiers(t),this.eatContextual("from"))this.type!==a.string&&this.unexpected(),e.source=this.parseExprAtom(),this.options.ecmaVersion>=16&&(e.attributes=this.parseWithClause());else{for(var i=0,s=e.specifiers;i=16&&(e.attributes=[])}this.semicolon()}return this.finishNode(e,"ExportNamedDeclaration")},l.parseExportDeclaration=function(e){return this.parseStatement(null)},l.parseExportDefaultDeclaration=function(){var e;if(this.type===a._function||(e=this.isAsyncFunction())){var t=this.startNode();return this.next(),e&&this.next(),this.parseFunction(t,ee|We,!1,e)}else if(this.type===a._class){var i=this.startNode();return this.parseClass(i,"nullableID")}else{var s=this.parseMaybeAssign();return this.semicolon(),s}},l.checkExport=function(e,t,i){e&&(typeof t!="string"&&(t=t.type==="Identifier"?t.name:t.value),W(e,t)&&this.raiseRecoverable(i,"Duplicate export \'"+t+"\'"),e[t]=!0)},l.checkPatternExport=function(e,t){var i=t.type;if(i==="Identifier")this.checkExport(e,t,t.start);else if(i==="ObjectPattern")for(var s=0,r=t.properties;s=16&&(e.attributes=this.parseWithClause()),this.semicolon(),this.finishNode(e,"ImportDeclaration")},l.parseImportSpecifier=function(){var e=this.startNode();return e.imported=this.parseModuleExportName(),this.eatContextual("as")?e.local=this.parseIdent():(this.checkUnreserved(e.imported),e.local=e.imported),this.checkLValSimple(e.local,D),this.finishNode(e,"ImportSpecifier")},l.parseImportDefaultSpecifier=function(){var e=this.startNode();return e.local=this.parseIdent(),this.checkLValSimple(e.local,D),this.finishNode(e,"ImportDefaultSpecifier")},l.parseImportNamespaceSpecifier=function(){var e=this.startNode();return this.next(),this.expectContextual("as"),e.local=this.parseIdent(),this.checkLValSimple(e.local,D),this.finishNode(e,"ImportNamespaceSpecifier")},l.parseImportSpecifiers=function(){var e=[],t=!0;if(this.type===a.name&&(e.push(this.parseImportDefaultSpecifier()),!this.eat(a.comma)))return e;if(this.type===a.star)return e.push(this.parseImportNamespaceSpecifier()),e;for(this.expect(a.braceL);!this.eat(a.braceR);){if(t)t=!1;else if(this.expect(a.comma),this.afterTrailingComma(a.braceR))break;e.push(this.parseImportSpecifier())}return e},l.parseWithClause=function(){var e=[];if(!this.eat(a._with))return e;this.expect(a.braceL);for(var t={},i=!0;!this.eat(a.braceR);){if(i)i=!1;else if(this.expect(a.comma),this.afterTrailingComma(a.braceR))break;var s=this.parseImportAttribute(),r=s.key.type==="Identifier"?s.key.name:s.key.value;W(t,r)&&this.raiseRecoverable(s.key.start,"Duplicate attribute key \'"+r+"\'"),t[r]=!0,e.push(s)}return e},l.parseImportAttribute=function(){var e=this.startNode();return e.key=this.type===a.string?this.parseExprAtom():this.parseIdent(this.options.allowReserved!=="never"),this.expect(a.colon),this.type!==a.string&&this.unexpected(),e.value=this.parseExprAtom(),this.finishNode(e,"ImportAttribute")},l.parseModuleExportName=function(){if(this.options.ecmaVersion>=13&&this.type===a.string){var e=this.parseLiteral(this.value);return St.test(e.value)&&this.raise(e.start,"An export name cannot include a lone surrogate."),e}return this.parseIdent(!0)},l.adaptDirectivePrologue=function(e){for(var t=0;t=5&&e.type==="ExpressionStatement"&&e.expression.type==="Literal"&&typeof e.expression.value=="string"&&(this.input[e.start]===\'"\'||this.input[e.start]==="\'")};var N=C.prototype;N.toAssignable=function(e,t,i){if(this.options.ecmaVersion>=6&&e)switch(e.type){case"Identifier":this.inAsync&&e.name==="await"&&this.raise(e.start,"Cannot use \'await\' as identifier inside an async function");break;case"ObjectPattern":case"ArrayPattern":case"AssignmentPattern":case"RestElement":break;case"ObjectExpression":e.type="ObjectPattern",i&&this.checkPatternErrors(i,!0);for(var s=0,r=e.properties;s=6)switch(this.type){case a.bracketL:var e=this.startNode();return this.next(),e.elements=this.parseBindingList(a.bracketR,!0,!0),this.finishNode(e,"ArrayPattern");case a.braceL:return this.parseObj(!0)}return this.parseIdent()},N.parseBindingList=function(e,t,i,s){for(var r=[],n=!0;!this.eat(e);)if(n?n=!1:this.expect(a.comma),t&&this.type===a.comma)r.push(null);else{if(i&&this.afterTrailingComma(e))break;if(this.type===a.ellipsis){var u=this.parseRestBinding();this.parseBindingListItem(u),r.push(u),this.type===a.comma&&this.raiseRecoverable(this.start,"Comma is not permitted after the rest element"),this.expect(e);break}else r.push(this.parseAssignableListItem(s))}return r},N.parseAssignableListItem=function(e){var t=this.parseMaybeDefault(this.start,this.startLoc);return this.parseBindingListItem(t),t},N.parseBindingListItem=function(e){return e},N.parseMaybeDefault=function(e,t,i){if(i=i||this.parseBindingAtom(),this.options.ecmaVersion<6||!this.eat(a.eq))return i;var s=this.startNodeAt(e,t);return s.left=i,s.right=this.parseMaybeAssign(),this.finishNode(s,"AssignmentPattern")},N.checkLValSimple=function(e,t,i){t===void 0&&(t=oe);var s=t!==oe;switch(e.type){case"Identifier":this.strict&&this.reservedWordsStrictBind.test(e.name)&&this.raiseRecoverable(e.start,(s?"Binding ":"Assigning to ")+e.name+" in strict mode"),s&&(t===D&&e.name==="let"&&this.raiseRecoverable(e.start,"let is disallowed as a lexically bound name"),i&&(W(i,e.name)&&this.raiseRecoverable(e.start,"Argument name clash"),i[e.name]=!0),t!==He&&this.declareName(e.name,t,e.start));break;case"ChainExpression":this.raiseRecoverable(e.start,"Optional chaining cannot appear in left-hand side");break;case"MemberExpression":s&&this.raiseRecoverable(e.start,"Binding member expression");break;case"ParenthesizedExpression":return s&&this.raiseRecoverable(e.start,"Binding parenthesized expression"),this.checkLValSimple(e.expression,t,i);default:this.raise(e.start,(s?"Binding":"Assigning to")+" rvalue")}},N.checkLValPattern=function(e,t,i){switch(t===void 0&&(t=oe),e.type){case"ObjectPattern":for(var s=0,r=e.properties;s=1;e--){var t=this.context[e];if(t.token==="function")return t.generator}return!1},K.updateContext=function(e){var t,i=this.type;i.keyword&&e===a.dot?this.exprAllowed=!1:(t=i.updateContext)?t.call(this,e):this.exprAllowed=i.beforeExpr},K.overrideContext=function(e){this.curContext()!==e&&(this.context[this.context.length-1]=e)},a.parenR.updateContext=a.braceR.updateContext=function(){if(this.context.length===1){this.exprAllowed=!0;return}var e=this.context.pop();e===b.b_stat&&this.curContext().token==="function"&&(e=this.context.pop()),this.exprAllowed=!e.isExpr},a.braceL.updateContext=function(e){this.context.push(this.braceIsBlock(e)?b.b_stat:b.b_expr),this.exprAllowed=!0},a.dollarBraceL.updateContext=function(){this.context.push(b.b_tmpl),this.exprAllowed=!0},a.parenL.updateContext=function(e){var t=e===a._if||e===a._for||e===a._with||e===a._while;this.context.push(t?b.p_stat:b.p_expr),this.exprAllowed=!0},a.incDec.updateContext=function(){},a._function.updateContext=a._class.updateContext=function(e){e.beforeExpr&&e!==a._else&&!(e===a.semi&&this.curContext()!==b.p_stat)&&!(e===a._return&&k.test(this.input.slice(this.lastTokEnd,this.start)))&&!((e===a.colon||e===a.braceL)&&this.curContext()===b.b_stat)?this.context.push(b.f_expr):this.context.push(b.f_stat),this.exprAllowed=!1},a.colon.updateContext=function(){this.curContext().token==="function"&&this.context.pop(),this.exprAllowed=!0},a.backQuote.updateContext=function(){this.curContext()===b.q_tmpl?this.context.pop():this.context.push(b.q_tmpl),this.exprAllowed=!1},a.star.updateContext=function(e){if(e===a._function){var t=this.context.length-1;this.context[t]===b.f_expr?this.context[t]=b.f_expr_gen:this.context[t]=b.f_gen}this.exprAllowed=!0},a.name.updateContext=function(e){var t=!1;this.options.ecmaVersion>=6&&e!==a.dot&&(this.value==="of"&&!this.exprAllowed||this.value==="yield"&&this.inGeneratorContext())&&(t=!0),this.exprAllowed=t};var f=C.prototype;f.checkPropClash=function(e,t,i){if(!(this.options.ecmaVersion>=9&&e.type==="SpreadElement")&&!(this.options.ecmaVersion>=6&&(e.computed||e.method||e.shorthand))){var s=e.key,r;switch(s.type){case"Identifier":r=s.name;break;case"Literal":r=String(s.value);break;default:return}var n=e.kind;if(this.options.ecmaVersion>=6){r==="__proto__"&&n==="init"&&(t.proto&&(i?i.doubleProto<0&&(i.doubleProto=s.start):this.raiseRecoverable(s.start,"Redefinition of __proto__ property")),t.proto=!0);return}r="$"+r;var u=t[r];if(u){var o;n==="init"?o=this.strict&&u.init||u.get||u.set:o=u.init||u[n],o&&this.raiseRecoverable(s.start,"Redefinition of property")}else u=t[r]={init:!1,get:!1,set:!1};u[n]=!0}},f.parseExpression=function(e,t){var i=this.start,s=this.startLoc,r=this.parseMaybeAssign(e,t);if(this.type===a.comma){var n=this.startNodeAt(i,s);for(n.expressions=[r];this.eat(a.comma);)n.expressions.push(this.parseMaybeAssign(e,t));return this.finishNode(n,"SequenceExpression")}return r},f.parseMaybeAssign=function(e,t,i){if(this.isContextual("yield")){if(this.inGenerator)return this.parseYield(e);this.exprAllowed=!1}var s=!1,r=-1,n=-1,u=-1;t?(r=t.parenthesizedAssign,n=t.trailingComma,u=t.doubleProto,t.parenthesizedAssign=t.trailingComma=-1):(t=new he,s=!0);var o=this.start,h=this.startLoc;(this.type===a.parenL||this.type===a.name)&&(this.potentialArrowAt=this.start,this.potentialArrowInForAwait=e==="await");var p=this.parseMaybeConditional(e,t);if(i&&(p=i.call(this,p,o,h)),this.type.isAssign){var d=this.startNodeAt(o,h);return d.operator=this.value,this.type===a.eq&&(p=this.toAssignable(p,!1,t)),s||(t.parenthesizedAssign=t.trailingComma=t.doubleProto=-1),t.shorthandAssign>=p.start&&(t.shorthandAssign=-1),this.type===a.eq?this.checkLValPattern(p):this.checkLValSimple(p),d.left=p,this.next(),d.right=this.parseMaybeAssign(e),u>-1&&(t.doubleProto=u),this.finishNode(d,"AssignmentExpression")}else s&&this.checkExpressionErrors(t,!0);return r>-1&&(t.parenthesizedAssign=r),n>-1&&(t.trailingComma=n),p},f.parseMaybeConditional=function(e,t){var i=this.start,s=this.startLoc,r=this.parseExprOps(e,t);if(this.checkExpressionErrors(t))return r;if(this.eat(a.question)){var n=this.startNodeAt(i,s);return n.test=r,n.consequent=this.parseMaybeAssign(),this.expect(a.colon),n.alternate=this.parseMaybeAssign(e),this.finishNode(n,"ConditionalExpression")}return r},f.parseExprOps=function(e,t){var i=this.start,s=this.startLoc,r=this.parseMaybeUnary(t,!1,!1,e);return this.checkExpressionErrors(t)||r.start===i&&r.type==="ArrowFunctionExpression"?r:this.parseExprOp(r,i,s,-1,e)},f.parseExprOp=function(e,t,i,s,r){var n=this.type.binop;if(n!=null&&(!r||this.type!==a._in)&&n>s){var u=this.type===a.logicalOR||this.type===a.logicalAND,o=this.type===a.coalesce;o&&(n=a.logicalAND.binop);var h=this.value;this.next();var p=this.start,d=this.startLoc,y=this.parseExprOp(this.parseMaybeUnary(null,!1,!1,r),p,d,n,r),S=this.buildBinary(t,i,e,y,h,u||o);return(u&&this.type===a.coalesce||o&&(this.type===a.logicalOR||this.type===a.logicalAND))&&this.raiseRecoverable(this.start,"Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses"),this.parseExprOp(S,t,i,s,r)}return e},f.buildBinary=function(e,t,i,s,r,n){s.type==="PrivateIdentifier"&&this.raise(s.start,"Private identifier can only be left side of binary expression");var u=this.startNodeAt(e,t);return u.left=i,u.operator=r,u.right=s,this.finishNode(u,n?"LogicalExpression":"BinaryExpression")},f.parseMaybeUnary=function(e,t,i,s){var r=this.start,n=this.startLoc,u;if(this.isContextual("await")&&this.canAwait)u=this.parseAwait(s),t=!0;else if(this.type.prefix){var o=this.startNode(),h=this.type===a.incDec;o.operator=this.value,o.prefix=!0,this.next(),o.argument=this.parseMaybeUnary(null,!0,h,s),this.checkExpressionErrors(e,!0),h?this.checkLValSimple(o.argument):this.strict&&o.operator==="delete"&&ze(o.argument)?this.raiseRecoverable(o.start,"Deleting local variable in strict mode"):o.operator==="delete"&&Ee(o.argument)?this.raiseRecoverable(o.start,"Private fields can not be deleted"):t=!0,u=this.finishNode(o,h?"UpdateExpression":"UnaryExpression")}else if(!t&&this.type===a.privateId)(s||this.privateNameStack.length===0)&&this.options.checkPrivateFields&&this.unexpected(),u=this.parsePrivateIdent(),this.type!==a._in&&this.unexpected();else{if(u=this.parseExprSubscripts(e,s),this.checkExpressionErrors(e))return u;for(;this.type.postfix&&!this.canInsertSemicolon();){var p=this.startNodeAt(r,n);p.operator=this.value,p.prefix=!1,p.argument=u,this.checkLValSimple(u),this.next(),u=this.finishNode(p,"UpdateExpression")}}if(!i&&this.eat(a.starstar))if(t)this.unexpected(this.lastTokStart);else return this.buildBinary(r,n,u,this.parseMaybeUnary(null,!1,!1,s),"**",!1);else return u};function ze(e){return e.type==="Identifier"||e.type==="ParenthesizedExpression"&&ze(e.expression)}function Ee(e){return e.type==="MemberExpression"&&e.property.type==="PrivateIdentifier"||e.type==="ChainExpression"&&Ee(e.expression)||e.type==="ParenthesizedExpression"&&Ee(e.expression)}f.parseExprSubscripts=function(e,t){var i=this.start,s=this.startLoc,r=this.parseExprAtom(e,t);if(r.type==="ArrowFunctionExpression"&&this.input.slice(this.lastTokStart,this.lastTokEnd)!==")")return r;var n=this.parseSubscripts(r,i,s,!1,t);return e&&n.type==="MemberExpression"&&(e.parenthesizedAssign>=n.start&&(e.parenthesizedAssign=-1),e.parenthesizedBind>=n.start&&(e.parenthesizedBind=-1),e.trailingComma>=n.start&&(e.trailingComma=-1)),n},f.parseSubscripts=function(e,t,i,s,r){for(var n=this.options.ecmaVersion>=8&&e.type==="Identifier"&&e.name==="async"&&this.lastTokEnd===e.end&&!this.canInsertSemicolon()&&e.end-e.start===5&&this.potentialArrowAt===e.start,u=!1;;){var o=this.parseSubscript(e,t,i,s,n,u,r);if(o.optional&&(u=!0),o===e||o.type==="ArrowFunctionExpression"){if(u){var h=this.startNodeAt(t,i);h.expression=o,o=this.finishNode(h,"ChainExpression")}return o}e=o}},f.shouldParseAsyncArrow=function(){return!this.canInsertSemicolon()&&this.eat(a.arrow)},f.parseSubscriptAsyncArrow=function(e,t,i,s){return this.parseArrowExpression(this.startNodeAt(e,t),i,!0,s)},f.parseSubscript=function(e,t,i,s,r,n,u){var o=this.options.ecmaVersion>=11,h=o&&this.eat(a.questionDot);s&&h&&this.raise(this.lastTokStart,"Optional chaining cannot appear in the callee of new expressions");var p=this.eat(a.bracketL);if(p||h&&this.type!==a.parenL&&this.type!==a.backQuote||this.eat(a.dot)){var d=this.startNodeAt(t,i);d.object=e,p?(d.property=this.parseExpression(),this.expect(a.bracketR)):this.type===a.privateId&&e.type!=="Super"?d.property=this.parsePrivateIdent():d.property=this.parseIdent(this.options.allowReserved!=="never"),d.computed=!!p,o&&(d.optional=h),e=this.finishNode(d,"MemberExpression")}else if(!s&&this.eat(a.parenL)){var y=new he,S=this.yieldPos,se=this.awaitPos,Q=this.awaitIdentPos;this.yieldPos=0,this.awaitPos=0,this.awaitIdentPos=0;var xe=this.parseExprList(a.parenR,this.options.ecmaVersion>=8,!1,y);if(r&&!h&&this.shouldParseAsyncArrow())return this.checkPatternErrors(y,!1),this.checkYieldAwaitInDefaultParams(),this.awaitIdentPos>0&&this.raise(this.awaitIdentPos,"Cannot use \'await\' as identifier inside an async function"),this.yieldPos=S,this.awaitPos=se,this.awaitIdentPos=Q,this.parseSubscriptAsyncArrow(t,i,xe,u);this.checkExpressionErrors(y,!0),this.yieldPos=S||this.yieldPos,this.awaitPos=se||this.awaitPos,this.awaitIdentPos=Q||this.awaitIdentPos;var Y=this.startNodeAt(t,i);Y.callee=e,Y.arguments=xe,o&&(Y.optional=h),e=this.finishNode(Y,"CallExpression")}else if(this.type===a.backQuote){(h||n)&&this.raise(this.start,"Optional chaining cannot appear in the tag of tagged template expressions");var X=this.startNodeAt(t,i);X.tag=e,X.quasi=this.parseTemplate({isTagged:!0}),e=this.finishNode(X,"TaggedTemplateExpression")}return e},f.parseExprAtom=function(e,t,i){this.type===a.slash&&this.readRegexp();var s,r=this.potentialArrowAt===this.start;switch(this.type){case a._super:return this.allowSuper||this.raise(this.start,"\'super\' keyword outside a method"),s=this.startNode(),this.next(),this.type===a.parenL&&!this.allowDirectSuper&&this.raise(s.start,"super() call outside constructor of a subclass"),this.type!==a.dot&&this.type!==a.bracketL&&this.type!==a.parenL&&this.unexpected(),this.finishNode(s,"Super");case a._this:return s=this.startNode(),this.next(),this.finishNode(s,"ThisExpression");case a.name:var n=this.start,u=this.startLoc,o=this.containsEsc,h=this.parseIdent(!1);if(this.options.ecmaVersion>=8&&!o&&h.name==="async"&&!this.canInsertSemicolon()&&this.eat(a._function))return this.overrideContext(b.f_expr),this.parseFunction(this.startNodeAt(n,u),0,!1,!0,t);if(r&&!this.canInsertSemicolon()){if(this.eat(a.arrow))return this.parseArrowExpression(this.startNodeAt(n,u),[h],!1,t);if(this.options.ecmaVersion>=8&&h.name==="async"&&this.type===a.name&&!o&&(!this.potentialArrowInForAwait||this.value!=="of"||this.containsEsc))return h=this.parseIdent(!1),(this.canInsertSemicolon()||!this.eat(a.arrow))&&this.unexpected(),this.parseArrowExpression(this.startNodeAt(n,u),[h],!0,t)}return h;case a.regexp:var p=this.value;return s=this.parseLiteral(p.value),s.regex={pattern:p.pattern,flags:p.flags},s;case a.num:case a.string:return this.parseLiteral(this.value);case a._null:case a._true:case a._false:return s=this.startNode(),s.value=this.type===a._null?null:this.type===a._true,s.raw=this.type.keyword,this.next(),this.finishNode(s,"Literal");case a.parenL:var d=this.start,y=this.parseParenAndDistinguishExpression(r,t);return e&&(e.parenthesizedAssign<0&&!this.isSimpleAssignTarget(y)&&(e.parenthesizedAssign=d),e.parenthesizedBind<0&&(e.parenthesizedBind=d)),y;case a.bracketL:return s=this.startNode(),this.next(),s.elements=this.parseExprList(a.bracketR,!0,!0,e),this.finishNode(s,"ArrayExpression");case a.braceL:return this.overrideContext(b.b_expr),this.parseObj(!1,e);case a._function:return s=this.startNode(),this.next(),this.parseFunction(s,0);case a._class:return this.parseClass(this.startNode(),!1);case a._new:return this.parseNew();case a.backQuote:return this.parseTemplate();case a._import:return this.options.ecmaVersion>=11?this.parseExprImport(i):this.unexpected();default:return this.parseExprAtomDefault()}},f.parseExprAtomDefault=function(){this.unexpected()},f.parseExprImport=function(e){var t=this.startNode();if(this.containsEsc&&this.raiseRecoverable(this.start,"Escape sequence in keyword import"),this.next(),this.type===a.parenL&&!e)return this.parseDynamicImport(t);if(this.type===a.dot){var i=this.startNodeAt(t.start,t.loc&&t.loc.start);return i.name="import",t.meta=this.finishNode(i,"Identifier"),this.parseImportMeta(t)}else this.unexpected()},f.parseDynamicImport=function(e){if(this.next(),e.source=this.parseMaybeAssign(),this.options.ecmaVersion>=16)this.eat(a.parenR)?e.options=null:(this.expect(a.comma),this.afterTrailingComma(a.parenR)?e.options=null:(e.options=this.parseMaybeAssign(),this.eat(a.parenR)||(this.expect(a.comma),this.afterTrailingComma(a.parenR)||this.unexpected())));else if(!this.eat(a.parenR)){var t=this.start;this.eat(a.comma)&&this.eat(a.parenR)?this.raiseRecoverable(t,"Trailing comma is not allowed in import()"):this.unexpected(t)}return this.finishNode(e,"ImportExpression")},f.parseImportMeta=function(e){this.next();var t=this.containsEsc;return e.property=this.parseIdent(!0),e.property.name!=="meta"&&this.raiseRecoverable(e.property.start,"The only valid meta property for import is \'import.meta\'"),t&&this.raiseRecoverable(e.start,"\'import.meta\' must not contain escaped characters"),this.options.sourceType!=="module"&&!this.options.allowImportExportEverywhere&&this.raiseRecoverable(e.start,"Cannot use \'import.meta\' outside a module"),this.finishNode(e,"MetaProperty")},f.parseLiteral=function(e){var t=this.startNode();return t.value=e,t.raw=this.input.slice(this.start,this.end),t.raw.charCodeAt(t.raw.length-1)===110&&(t.bigint=t.value!=null?t.value.toString():t.raw.slice(0,-1).replace(/_/g,"")),this.next(),this.finishNode(t,"Literal")},f.parseParenExpression=function(){this.expect(a.parenL);var e=this.parseExpression();return this.expect(a.parenR),e},f.shouldParseArrow=function(e){return!this.canInsertSemicolon()},f.parseParenAndDistinguishExpression=function(e,t){var i=this.start,s=this.startLoc,r,n=this.options.ecmaVersion>=8;if(this.options.ecmaVersion>=6){this.next();var u=this.start,o=this.startLoc,h=[],p=!0,d=!1,y=new he,S=this.yieldPos,se=this.awaitPos,Q;for(this.yieldPos=0,this.awaitPos=0;this.type!==a.parenR;)if(p?p=!1:this.expect(a.comma),n&&this.afterTrailingComma(a.parenR,!0)){d=!0;break}else if(this.type===a.ellipsis){Q=this.start,h.push(this.parseParenItem(this.parseRestBinding())),this.type===a.comma&&this.raiseRecoverable(this.start,"Comma is not permitted after the rest element");break}else h.push(this.parseMaybeAssign(!1,y,this.parseParenItem));var xe=this.lastTokEnd,Y=this.lastTokEndLoc;if(this.expect(a.parenR),e&&this.shouldParseArrow(h)&&this.eat(a.arrow))return this.checkPatternErrors(y,!1),this.checkYieldAwaitInDefaultParams(),this.yieldPos=S,this.awaitPos=se,this.parseParenArrowList(i,s,h,t);(!h.length||d)&&this.unexpected(this.lastTokStart),Q&&this.unexpected(Q),this.checkExpressionErrors(y,!0),this.yieldPos=S||this.yieldPos,this.awaitPos=se||this.awaitPos,h.length>1?(r=this.startNodeAt(u,o),r.expressions=h,this.finishNodeAt(r,"SequenceExpression",xe,Y)):r=h[0]}else r=this.parseParenExpression();if(this.options.preserveParens){var X=this.startNodeAt(i,s);return X.expression=r,this.finishNode(X,"ParenthesizedExpression")}else return r},f.parseParenItem=function(e){return e},f.parseParenArrowList=function(e,t,i,s){return this.parseArrowExpression(this.startNodeAt(e,t),i,!1,s)};var Nt=[];f.parseNew=function(){this.containsEsc&&this.raiseRecoverable(this.start,"Escape sequence in keyword new");var e=this.startNode();if(this.next(),this.options.ecmaVersion>=6&&this.type===a.dot){var t=this.startNodeAt(e.start,e.loc&&e.loc.start);t.name="new",e.meta=this.finishNode(t,"Identifier"),this.next();var i=this.containsEsc;return e.property=this.parseIdent(!0),e.property.name!=="target"&&this.raiseRecoverable(e.property.start,"The only valid meta property for new is \'new.target\'"),i&&this.raiseRecoverable(e.start,"\'new.target\' must not contain escaped characters"),this.allowNewDotTarget||this.raiseRecoverable(e.start,"\'new.target\' can only be used in functions and class static block"),this.finishNode(e,"MetaProperty")}var s=this.start,r=this.startLoc;return e.callee=this.parseSubscripts(this.parseExprAtom(null,!1,!0),s,r,!0,!1),this.eat(a.parenL)?e.arguments=this.parseExprList(a.parenR,this.options.ecmaVersion>=8,!1):e.arguments=Nt,this.finishNode(e,"NewExpression")},f.parseTemplateElement=function(e){var t=e.isTagged,i=this.startNode();return this.type===a.invalidTemplate?(t||this.raiseRecoverable(this.start,"Bad escape sequence in untagged template literal"),i.value={raw:this.value.replace(/\\r\\n?/g,`\n`),cooked:null}):i.value={raw:this.input.slice(this.start,this.end).replace(/\\r\\n?/g,`\n`),cooked:this.value},this.next(),i.tail=this.type===a.backQuote,this.finishNode(i,"TemplateElement")},f.parseTemplate=function(e){e===void 0&&(e={});var t=e.isTagged;t===void 0&&(t=!1);var i=this.startNode();this.next(),i.expressions=[];var s=this.parseTemplateElement({isTagged:t});for(i.quasis=[s];!s.tail;)this.type===a.eof&&this.raise(this.pos,"Unterminated template literal"),this.expect(a.dollarBraceL),i.expressions.push(this.parseExpression()),this.expect(a.braceR),i.quasis.push(s=this.parseTemplateElement({isTagged:t}));return this.next(),this.finishNode(i,"TemplateLiteral")},f.isAsyncProp=function(e){return!e.computed&&e.key.type==="Identifier"&&e.key.name==="async"&&(this.type===a.name||this.type===a.num||this.type===a.string||this.type===a.bracketL||this.type.keyword||this.options.ecmaVersion>=9&&this.type===a.star)&&!k.test(this.input.slice(this.lastTokEnd,this.start))},f.parseObj=function(e,t){var i=this.startNode(),s=!0,r={};for(i.properties=[],this.next();!this.eat(a.braceR);){if(s)s=!1;else if(this.expect(a.comma),this.options.ecmaVersion>=5&&this.afterTrailingComma(a.braceR))break;var n=this.parseProperty(e,t);e||this.checkPropClash(n,r,t),i.properties.push(n)}return this.finishNode(i,e?"ObjectPattern":"ObjectExpression")},f.parseProperty=function(e,t){var i=this.startNode(),s,r,n,u;if(this.options.ecmaVersion>=9&&this.eat(a.ellipsis))return e?(i.argument=this.parseIdent(!1),this.type===a.comma&&this.raiseRecoverable(this.start,"Comma is not permitted after the rest element"),this.finishNode(i,"RestElement")):(i.argument=this.parseMaybeAssign(!1,t),this.type===a.comma&&t&&t.trailingComma<0&&(t.trailingComma=this.start),this.finishNode(i,"SpreadElement"));this.options.ecmaVersion>=6&&(i.method=!1,i.shorthand=!1,(e||t)&&(n=this.start,u=this.startLoc),e||(s=this.eat(a.star)));var o=this.containsEsc;return this.parsePropertyName(i),!e&&!o&&this.options.ecmaVersion>=8&&!s&&this.isAsyncProp(i)?(r=!0,s=this.options.ecmaVersion>=9&&this.eat(a.star),this.parsePropertyName(i)):r=!1,this.parsePropertyValue(i,e,s,r,n,u,t,o),this.finishNode(i,"Property")},f.parseGetterSetter=function(e){var t=e.key.name;this.parsePropertyName(e),e.value=this.parseMethod(!1),e.kind=t;var i=e.kind==="get"?0:1;if(e.value.params.length!==i){var s=e.value.start;e.kind==="get"?this.raiseRecoverable(s,"getter should have no params"):this.raiseRecoverable(s,"setter should have exactly one param")}else e.kind==="set"&&e.value.params[0].type==="RestElement"&&this.raiseRecoverable(e.value.params[0].start,"Setter cannot use rest params")},f.parsePropertyValue=function(e,t,i,s,r,n,u,o){(i||s)&&this.type===a.colon&&this.unexpected(),this.eat(a.colon)?(e.value=t?this.parseMaybeDefault(this.start,this.startLoc):this.parseMaybeAssign(!1,u),e.kind="init"):this.options.ecmaVersion>=6&&this.type===a.parenL?(t&&this.unexpected(),e.method=!0,e.value=this.parseMethod(i,s),e.kind="init"):!t&&!o&&this.options.ecmaVersion>=5&&!e.computed&&e.key.type==="Identifier"&&(e.key.name==="get"||e.key.name==="set")&&this.type!==a.comma&&this.type!==a.braceR&&this.type!==a.eq?((i||s)&&this.unexpected(),this.parseGetterSetter(e)):this.options.ecmaVersion>=6&&!e.computed&&e.key.type==="Identifier"?((i||s)&&this.unexpected(),this.checkUnreserved(e.key),e.key.name==="await"&&!this.awaitIdentPos&&(this.awaitIdentPos=r),t?e.value=this.parseMaybeDefault(r,n,this.copyNode(e.key)):this.type===a.eq&&u?(u.shorthandAssign<0&&(u.shorthandAssign=this.start),e.value=this.parseMaybeDefault(r,n,this.copyNode(e.key))):e.value=this.copyNode(e.key),e.kind="init",e.shorthand=!0):this.unexpected()},f.parsePropertyName=function(e){if(this.options.ecmaVersion>=6){if(this.eat(a.bracketL))return e.computed=!0,e.key=this.parseMaybeAssign(),this.expect(a.bracketR),e.key;e.computed=!1}return e.key=this.type===a.num||this.type===a.string?this.parseExprAtom():this.parseIdent(this.options.allowReserved!=="never")},f.initFunction=function(e){e.id=null,this.options.ecmaVersion>=6&&(e.generator=e.expression=!1),this.options.ecmaVersion>=8&&(e.async=!1)},f.parseMethod=function(e,t,i){var s=this.startNode(),r=this.yieldPos,n=this.awaitPos,u=this.awaitIdentPos;return this.initFunction(s),this.options.ecmaVersion>=6&&(s.generator=e),this.options.ecmaVersion>=8&&(s.async=!!t),this.yieldPos=0,this.awaitPos=0,this.awaitIdentPos=0,this.enterScope(Se(t,s.generator)|ne|(i?Ue:0)),this.expect(a.parenL),s.params=this.parseBindingList(a.parenR,!1,this.options.ecmaVersion>=8),this.checkYieldAwaitInDefaultParams(),this.parseFunctionBody(s,!1,!0,!1),this.yieldPos=r,this.awaitPos=n,this.awaitIdentPos=u,this.finishNode(s,"FunctionExpression")},f.parseArrowExpression=function(e,t,i,s){var r=this.yieldPos,n=this.awaitPos,u=this.awaitIdentPos;return this.enterScope(Se(i,!1)|_e),this.initFunction(e),this.options.ecmaVersion>=8&&(e.async=!!i),this.yieldPos=0,this.awaitPos=0,this.awaitIdentPos=0,e.params=this.toAssignableList(t,!0),this.parseFunctionBody(e,!0,!1,s),this.yieldPos=r,this.awaitPos=n,this.awaitIdentPos=u,this.finishNode(e,"ArrowFunctionExpression")},f.parseFunctionBody=function(e,t,i,s){var r=t&&this.type!==a.braceL,n=this.strict,u=!1;if(r)e.body=this.parseMaybeAssign(s),e.expression=!0,this.checkParams(e,!1);else{var o=this.options.ecmaVersion>=7&&!this.isSimpleParamList(e.params);(!n||o)&&(u=this.strictDirective(this.end),u&&o&&this.raiseRecoverable(e.start,"Illegal \'use strict\' directive in function with non-simple parameter list"));var h=this.labels;this.labels=[],u&&(this.strict=!0),this.checkParams(e,!n&&!u&&!t&&!i&&this.isSimpleParamList(e.params)),this.strict&&e.id&&this.checkLValSimple(e.id,He),e.body=this.parseBlock(!1,void 0,u&&!n),e.expression=!1,this.adaptDirectivePrologue(e.body.body),this.labels=h}this.exitScope()},f.isSimpleParamList=function(e){for(var t=0,i=e;t-1||r.functions.indexOf(e)>-1||r.var.indexOf(e)>-1,r.lexical.push(e),this.inModule&&r.flags&j&&delete this.undefinedExports[e]}else if(t===Ge){var n=this.currentScope();n.lexical.push(e)}else if(t===je){var u=this.currentScope();this.treatFunctionsAsVar?s=u.lexical.indexOf(e)>-1:s=u.lexical.indexOf(e)>-1||u.var.indexOf(e)>-1,u.functions.push(e)}else for(var o=this.scopeStack.length-1;o>=0;--o){var h=this.scopeStack[o];if(h.lexical.indexOf(e)>-1&&!(h.flags&Me&&h.lexical[0]===e)||!this.treatFunctionsAsVarInScope(h)&&h.functions.indexOf(e)>-1){s=!0;break}if(h.var.push(e),this.inModule&&h.flags&j&&delete this.undefinedExports[e],h.flags&ue)break}s&&this.raiseRecoverable(i,"Identifier \'"+e+"\' has already been declared")},U.checkLocalExport=function(e){this.scopeStack[0].lexical.indexOf(e.name)===-1&&this.scopeStack[0].var.indexOf(e.name)===-1&&(this.undefinedExports[e.name]=e)},U.currentScope=function(){return this.scopeStack[this.scopeStack.length-1]},U.currentVarScope=function(){for(var e=this.scopeStack.length-1;;e--){var t=this.scopeStack[e];if(t.flags&(ue|$|H))return t}},U.currentThisScope=function(){for(var e=this.scopeStack.length-1;;e--){var t=this.scopeStack[e];if(t.flags&(ue|$|H)&&!(t.flags&_e))return t}};var te=function(t,i,s){this.type="",this.start=i,this.end=0,t.options.locations&&(this.loc=new J(t,s)),t.options.directSourceFile&&(this.sourceFile=t.options.directSourceFile),t.options.ranges&&(this.range=[i,0])},ie=C.prototype;ie.startNode=function(){return new te(this,this.start,this.startLoc)},ie.startNodeAt=function(e,t){return new te(this,e,t)};function Ke(e,t,i,s){return e.type=t,e.end=i,this.options.locations&&(e.loc.end=s),this.options.ranges&&(e.range[1]=i),e}ie.finishNode=function(e,t){return Ke.call(this,e,t,this.lastTokEnd,this.lastTokEndLoc)},ie.finishNodeAt=function(e,t,i,s){return Ke.call(this,e,t,i,s)},ie.copyNode=function(e){var t=new te(this,e.start,this.startLoc);for(var i in e)t[i]=e[i];return t};var Tt="Berf Beria_Erfe Gara Garay Gukh Gurung_Khema Hrkt Katakana_Or_Hiragana Kawi Kirat_Rai Krai Nag_Mundari Nagm Ol_Onal Onao Sidetic Sidt Sunu Sunuwar Tai_Yo Tayo Todhri Todr Tolong_Siki Tols Tulu_Tigalari Tutg Unknown Zzzz",Qe="ASCII ASCII_Hex_Digit AHex Alphabetic Alpha Any Assigned Bidi_Control Bidi_C Bidi_Mirrored Bidi_M Case_Ignorable CI Cased Changes_When_Casefolded CWCF Changes_When_Casemapped CWCM Changes_When_Lowercased CWL Changes_When_NFKC_Casefolded CWKCF Changes_When_Titlecased CWT Changes_When_Uppercased CWU Dash Default_Ignorable_Code_Point DI Deprecated Dep Diacritic Dia Emoji Emoji_Component Emoji_Modifier Emoji_Modifier_Base Emoji_Presentation Extender Ext Grapheme_Base Gr_Base Grapheme_Extend Gr_Ext Hex_Digit Hex IDS_Binary_Operator IDSB IDS_Trinary_Operator IDST ID_Continue IDC ID_Start IDS Ideographic Ideo Join_Control Join_C Logical_Order_Exception LOE Lowercase Lower Math Noncharacter_Code_Point NChar Pattern_Syntax Pat_Syn Pattern_White_Space Pat_WS Quotation_Mark QMark Radical Regional_Indicator RI Sentence_Terminal STerm Soft_Dotted SD Terminal_Punctuation Term Unified_Ideograph UIdeo Uppercase Upper Variation_Selector VS White_Space space XID_Continue XIDC XID_Start XIDS",Ye=Qe+" Extended_Pictographic",Xe=Ye,Ze=Xe+" EBase EComp EMod EPres ExtPict",Je=Ze,Lt=Je,Rt={9:Qe,10:Ye,11:Xe,12:Ze,13:Je,14:Lt},Ot="Basic_Emoji Emoji_Keycap_Sequence RGI_Emoji_Modifier_Sequence RGI_Emoji_Flag_Sequence RGI_Emoji_Tag_Sequence RGI_Emoji_ZWJ_Sequence RGI_Emoji",Bt={9:"",10:"",11:"",12:"",13:"",14:Ot},$e="Cased_Letter LC Close_Punctuation Pe Connector_Punctuation Pc Control Cc cntrl Currency_Symbol Sc Dash_Punctuation Pd Decimal_Number Nd digit Enclosing_Mark Me Final_Punctuation Pf Format Cf Initial_Punctuation Pi Letter L Letter_Number Nl Line_Separator Zl Lowercase_Letter Ll Mark M Combining_Mark Math_Symbol Sm Modifier_Letter Lm Modifier_Symbol Sk Nonspacing_Mark Mn Number N Open_Punctuation Ps Other C Other_Letter Lo Other_Number No Other_Punctuation Po Other_Symbol So Paragraph_Separator Zp Private_Use Co Punctuation P punct Separator Z Space_Separator Zs Spacing_Mark Mc Surrogate Cs Symbol S Titlecase_Letter Lt Unassigned Cn Uppercase_Letter Lu",et="Adlam Adlm Ahom Anatolian_Hieroglyphs Hluw Arabic Arab Armenian Armn Avestan Avst Balinese Bali Bamum Bamu Bassa_Vah Bass Batak Batk Bengali Beng Bhaiksuki Bhks Bopomofo Bopo Brahmi Brah Braille Brai Buginese Bugi Buhid Buhd Canadian_Aboriginal Cans Carian Cari Caucasian_Albanian Aghb Chakma Cakm Cham Cham Cherokee Cher Common Zyyy Coptic Copt Qaac Cuneiform Xsux Cypriot Cprt Cyrillic Cyrl Deseret Dsrt Devanagari Deva Duployan Dupl Egyptian_Hieroglyphs Egyp Elbasan Elba Ethiopic Ethi Georgian Geor Glagolitic Glag Gothic Goth Grantha Gran Greek Grek Gujarati Gujr Gurmukhi Guru Han Hani Hangul Hang Hanunoo Hano Hatran Hatr Hebrew Hebr Hiragana Hira Imperial_Aramaic Armi Inherited Zinh Qaai Inscriptional_Pahlavi Phli Inscriptional_Parthian Prti Javanese Java Kaithi Kthi Kannada Knda Katakana Kana Kayah_Li Kali Kharoshthi Khar Khmer Khmr Khojki Khoj Khudawadi Sind Lao Laoo Latin Latn Lepcha Lepc Limbu Limb Linear_A Lina Linear_B Linb Lisu Lisu Lycian Lyci Lydian Lydi Mahajani Mahj Malayalam Mlym Mandaic Mand Manichaean Mani Marchen Marc Masaram_Gondi Gonm Meetei_Mayek Mtei Mende_Kikakui Mend Meroitic_Cursive Merc Meroitic_Hieroglyphs Mero Miao Plrd Modi Mongolian Mong Mro Mroo Multani Mult Myanmar Mymr Nabataean Nbat New_Tai_Lue Talu Newa Newa Nko Nkoo Nushu Nshu Ogham Ogam Ol_Chiki Olck Old_Hungarian Hung Old_Italic Ital Old_North_Arabian Narb Old_Permic Perm Old_Persian Xpeo Old_South_Arabian Sarb Old_Turkic Orkh Oriya Orya Osage Osge Osmanya Osma Pahawh_Hmong Hmng Palmyrene Palm Pau_Cin_Hau Pauc Phags_Pa Phag Phoenician Phnx Psalter_Pahlavi Phlp Rejang Rjng Runic Runr Samaritan Samr Saurashtra Saur Sharada Shrd Shavian Shaw Siddham Sidd SignWriting Sgnw Sinhala Sinh Sora_Sompeng Sora Soyombo Soyo Sundanese Sund Syloti_Nagri Sylo Syriac Syrc Tagalog Tglg Tagbanwa Tagb Tai_Le Tale Tai_Tham Lana Tai_Viet Tavt Takri Takr Tamil Taml Tangut Tang Telugu Telu Thaana Thaa Thai Thai Tibetan Tibt Tifinagh Tfng Tirhuta Tirh Ugaritic Ugar Vai Vaii Warang_Citi Wara Yi Yiii Zanabazar_Square Zanb",tt=et+" Dogra Dogr Gunjala_Gondi Gong Hanifi_Rohingya Rohg Makasar Maka Medefaidrin Medf Old_Sogdian Sogo Sogdian Sogd",it=tt+" Elymaic Elym Nandinagari Nand Nyiakeng_Puachue_Hmong Hmnp Wancho Wcho",st=it+" Chorasmian Chrs Diak Dives_Akuru Khitan_Small_Script Kits Yezi Yezidi",at=st+" Cypro_Minoan Cpmn Old_Uyghur Ougr Tangsa Tnsa Toto Vithkuqi Vith",Dt=at+" "+Tt,Ft={9:et,10:tt,11:it,12:st,13:at,14:Dt},rt={};function Mt(e){var t=rt[e]={binary:M(Rt[e]+" "+$e),binaryOfStrings:M(Bt[e]),nonBinary:{General_Category:M($e),Script:M(Ft[e])}};t.nonBinary.Script_Extensions=t.nonBinary.Script,t.nonBinary.gc=t.nonBinary.General_Category,t.nonBinary.sc=t.nonBinary.Script,t.nonBinary.scx=t.nonBinary.Script_Extensions}for(var Ie=0,nt=[9,10,11,12,13,14];Ie=6?"uy":"")+(t.options.ecmaVersion>=9?"s":"")+(t.options.ecmaVersion>=13?"d":"")+(t.options.ecmaVersion>=15?"v":""),this.unicodeProperties=rt[t.options.ecmaVersion>=14?14:t.options.ecmaVersion],this.source="",this.flags="",this.start=0,this.switchU=!1,this.switchV=!1,this.switchN=!1,this.pos=0,this.lastIntValue=0,this.lastStringValue="",this.lastAssertionIsQuantifiable=!1,this.numCapturingParens=0,this.maxBackReference=0,this.groupNames=Object.create(null),this.backReferenceNames=[],this.branchID=null};R.prototype.reset=function(t,i,s){var r=s.indexOf("v")!==-1,n=s.indexOf("u")!==-1;this.start=t|0,this.source=i+"",this.flags=s,r&&this.parser.options.ecmaVersion>=15?(this.switchU=!0,this.switchV=!0,this.switchN=!0):(this.switchU=n&&this.parser.options.ecmaVersion>=6,this.switchV=!1,this.switchN=n&&this.parser.options.ecmaVersion>=9)},R.prototype.raise=function(t){this.parser.raiseRecoverable(this.start,"Invalid regular expression: /"+this.source+"/: "+t)},R.prototype.at=function(t,i){i===void 0&&(i=!1);var s=this.source,r=s.length;if(t>=r)return-1;var n=s.charCodeAt(t);if(!(i||this.switchU)||n<=55295||n>=57344||t+1>=r)return n;var u=s.charCodeAt(t+1);return u>=56320&&u<=57343?(n<<10)+u-56613888:n},R.prototype.nextIndex=function(t,i){i===void 0&&(i=!1);var s=this.source,r=s.length;if(t>=r)return r;var n=s.charCodeAt(t),u;return!(i||this.switchU)||n<=55295||n>=57344||t+1>=r||(u=s.charCodeAt(t+1))<56320||u>57343?t+1:t+2},R.prototype.current=function(t){return t===void 0&&(t=!1),this.at(this.pos,t)},R.prototype.lookahead=function(t){return t===void 0&&(t=!1),this.at(this.nextIndex(this.pos,t),t)},R.prototype.advance=function(t){t===void 0&&(t=!1),this.pos=this.nextIndex(this.pos,t)},R.prototype.eat=function(t,i){return i===void 0&&(i=!1),this.current(i)===t?(this.advance(i),!0):!1},R.prototype.eatChars=function(t,i){i===void 0&&(i=!1);for(var s=this.pos,r=0,n=t;r-1&&this.raise(e.start,"Duplicate regular expression flag"),u==="u"&&(s=!0),u==="v"&&(r=!0)}this.options.ecmaVersion>=15&&s&&r&&this.raise(e.start,"Invalid regular expression flag")};function qt(e){for(var t in e)return!0;return!1}c.validateRegExpPattern=function(e){this.regexp_pattern(e),!e.switchN&&this.options.ecmaVersion>=9&&qt(e.groupNames)&&(e.switchN=!0,this.regexp_pattern(e))},c.regexp_pattern=function(e){e.pos=0,e.lastIntValue=0,e.lastStringValue="",e.lastAssertionIsQuantifiable=!1,e.numCapturingParens=0,e.maxBackReference=0,e.groupNames=Object.create(null),e.backReferenceNames.length=0,e.branchID=null,this.regexp_disjunction(e),e.pos!==e.source.length&&(e.eat(41)&&e.raise("Unmatched \')\'"),(e.eat(93)||e.eat(125))&&e.raise("Lone quantifier brackets")),e.maxBackReference>e.numCapturingParens&&e.raise("Invalid escape");for(var t=0,i=e.backReferenceNames;t=16;for(t&&(e.branchID=new pe(e.branchID,null)),this.regexp_alternative(e);e.eat(124);)t&&(e.branchID=e.branchID.sibling()),this.regexp_alternative(e);t&&(e.branchID=e.branchID.parent),this.regexp_eatQuantifier(e,!0)&&e.raise("Nothing to repeat"),e.eat(123)&&e.raise("Lone quantifier brackets")},c.regexp_alternative=function(e){for(;e.pos=9&&(i=e.eat(60)),e.eat(61)||e.eat(33))return this.regexp_disjunction(e),e.eat(41)||e.raise("Unterminated group"),e.lastAssertionIsQuantifiable=!i,!0}return e.pos=t,!1},c.regexp_eatQuantifier=function(e,t){return t===void 0&&(t=!1),this.regexp_eatQuantifierPrefix(e,t)?(e.eat(63),!0):!1},c.regexp_eatQuantifierPrefix=function(e,t){return e.eat(42)||e.eat(43)||e.eat(63)||this.regexp_eatBracedQuantifier(e,t)},c.regexp_eatBracedQuantifier=function(e,t){var i=e.pos;if(e.eat(123)){var s=0,r=-1;if(this.regexp_eatDecimalDigits(e)&&(s=e.lastIntValue,e.eat(44)&&this.regexp_eatDecimalDigits(e)&&(r=e.lastIntValue),e.eat(125)))return r!==-1&&r=16){var i=this.regexp_eatModifiers(e),s=e.eat(45);if(i||s){for(var r=0;r-1&&e.raise("Duplicate regular expression modifiers")}if(s){var u=this.regexp_eatModifiers(e);!i&&!u&&e.current()===58&&e.raise("Invalid regular expression modifiers");for(var o=0;o-1||i.indexOf(h)>-1)&&e.raise("Duplicate regular expression modifiers")}}}}if(e.eat(58)){if(this.regexp_disjunction(e),e.eat(41))return!0;e.raise("Unterminated group")}}e.pos=t}return!1},c.regexp_eatCapturingGroup=function(e){if(e.eat(40)){if(this.options.ecmaVersion>=9?this.regexp_groupSpecifier(e):e.current()===63&&e.raise("Invalid group"),this.regexp_disjunction(e),e.eat(41))return e.numCapturingParens+=1,!0;e.raise("Unterminated group")}return!1},c.regexp_eatModifiers=function(e){for(var t="",i=0;(i=e.current())!==-1&&jt(i);)t+=B(i),e.advance();return t};function jt(e){return e===105||e===109||e===115}c.regexp_eatExtendedAtom=function(e){return e.eat(46)||this.regexp_eatReverseSolidusAtomEscape(e)||this.regexp_eatCharacterClass(e)||this.regexp_eatUncapturingGroup(e)||this.regexp_eatCapturingGroup(e)||this.regexp_eatInvalidBracedQuantifier(e)||this.regexp_eatExtendedPatternCharacter(e)},c.regexp_eatInvalidBracedQuantifier=function(e){return this.regexp_eatBracedQuantifier(e,!0)&&e.raise("Nothing to repeat"),!1},c.regexp_eatSyntaxCharacter=function(e){var t=e.current();return ut(t)?(e.lastIntValue=t,e.advance(),!0):!1};function ut(e){return e===36||e>=40&&e<=43||e===46||e===63||e>=91&&e<=94||e>=123&&e<=125}c.regexp_eatPatternCharacters=function(e){for(var t=e.pos,i=0;(i=e.current())!==-1&&!ut(i);)e.advance();return e.pos!==t},c.regexp_eatExtendedPatternCharacter=function(e){var t=e.current();return t!==-1&&t!==36&&!(t>=40&&t<=43)&&t!==46&&t!==63&&t!==91&&t!==94&&t!==124?(e.advance(),!0):!1},c.regexp_groupSpecifier=function(e){if(e.eat(63)){this.regexp_eatGroupName(e)||e.raise("Invalid group");var t=this.options.ecmaVersion>=16,i=e.groupNames[e.lastStringValue];if(i)if(t)for(var s=0,r=i;s=11,s=e.current(i);return e.advance(i),s===92&&this.regexp_eatRegExpUnicodeEscapeSequence(e,i)&&(s=e.lastIntValue),Gt(s)?(e.lastIntValue=s,!0):(e.pos=t,!1)};function Gt(e){return L(e,!0)||e===36||e===95}c.regexp_eatRegExpIdentifierPart=function(e){var t=e.pos,i=this.options.ecmaVersion>=11,s=e.current(i);return e.advance(i),s===92&&this.regexp_eatRegExpUnicodeEscapeSequence(e,i)&&(s=e.lastIntValue),Ht(s)?(e.lastIntValue=s,!0):(e.pos=t,!1)};function Ht(e){return O(e,!0)||e===36||e===95||e===8204||e===8205}c.regexp_eatAtomEscape=function(e){return this.regexp_eatBackReference(e)||this.regexp_eatCharacterClassEscape(e)||this.regexp_eatCharacterEscape(e)||e.switchN&&this.regexp_eatKGroupName(e)?!0:(e.switchU&&(e.current()===99&&e.raise("Invalid unicode escape"),e.raise("Invalid escape")),!1)},c.regexp_eatBackReference=function(e){var t=e.pos;if(this.regexp_eatDecimalEscape(e)){var i=e.lastIntValue;if(e.switchU)return i>e.maxBackReference&&(e.maxBackReference=i),!0;if(i<=e.numCapturingParens)return!0;e.pos=t}return!1},c.regexp_eatKGroupName=function(e){if(e.eat(107)){if(this.regexp_eatGroupName(e))return e.backReferenceNames.push(e.lastStringValue),!0;e.raise("Invalid named reference")}return!1},c.regexp_eatCharacterEscape=function(e){return this.regexp_eatControlEscape(e)||this.regexp_eatCControlLetter(e)||this.regexp_eatZero(e)||this.regexp_eatHexEscapeSequence(e)||this.regexp_eatRegExpUnicodeEscapeSequence(e,!1)||!e.switchU&&this.regexp_eatLegacyOctalEscapeSequence(e)||this.regexp_eatIdentityEscape(e)},c.regexp_eatCControlLetter=function(e){var t=e.pos;if(e.eat(99)){if(this.regexp_eatControlLetter(e))return!0;e.pos=t}return!1},c.regexp_eatZero=function(e){return e.current()===48&&!fe(e.lookahead())?(e.lastIntValue=0,e.advance(),!0):!1},c.regexp_eatControlEscape=function(e){var t=e.current();return t===116?(e.lastIntValue=9,e.advance(),!0):t===110?(e.lastIntValue=10,e.advance(),!0):t===118?(e.lastIntValue=11,e.advance(),!0):t===102?(e.lastIntValue=12,e.advance(),!0):t===114?(e.lastIntValue=13,e.advance(),!0):!1},c.regexp_eatControlLetter=function(e){var t=e.current();return ot(t)?(e.lastIntValue=t%32,e.advance(),!0):!1};function ot(e){return e>=65&&e<=90||e>=97&&e<=122}c.regexp_eatRegExpUnicodeEscapeSequence=function(e,t){t===void 0&&(t=!1);var i=e.pos,s=t||e.switchU;if(e.eat(117)){if(this.regexp_eatFixedHexDigits(e,4)){var r=e.lastIntValue;if(s&&r>=55296&&r<=56319){var n=e.pos;if(e.eat(92)&&e.eat(117)&&this.regexp_eatFixedHexDigits(e,4)){var u=e.lastIntValue;if(u>=56320&&u<=57343)return e.lastIntValue=(r-55296)*1024+(u-56320)+65536,!0}e.pos=n,e.lastIntValue=r}return!0}if(s&&e.eat(123)&&this.regexp_eatHexDigits(e)&&e.eat(125)&&Wt(e.lastIntValue))return!0;s&&e.raise("Invalid unicode escape"),e.pos=i}return!1};function Wt(e){return e>=0&&e<=1114111}c.regexp_eatIdentityEscape=function(e){if(e.switchU)return this.regexp_eatSyntaxCharacter(e)?!0:e.eat(47)?(e.lastIntValue=47,!0):!1;var t=e.current();return t!==99&&(!e.switchN||t!==107)?(e.lastIntValue=t,e.advance(),!0):!1},c.regexp_eatDecimalEscape=function(e){e.lastIntValue=0;var t=e.current();if(t>=49&&t<=57){do e.lastIntValue=10*e.lastIntValue+(t-48),e.advance();while((t=e.current())>=48&&t<=57);return!0}return!1};var ht=0,F=1,T=2;c.regexp_eatCharacterClassEscape=function(e){var t=e.current();if(zt(t))return e.lastIntValue=-1,e.advance(),F;var i=!1;if(e.switchU&&this.options.ecmaVersion>=9&&((i=t===80)||t===112)){e.lastIntValue=-1,e.advance();var s;if(e.eat(123)&&(s=this.regexp_eatUnicodePropertyValueExpression(e))&&e.eat(125))return i&&s===T&&e.raise("Invalid property name"),s;e.raise("Invalid property name")}return ht};function zt(e){return e===100||e===68||e===115||e===83||e===119||e===87}c.regexp_eatUnicodePropertyValueExpression=function(e){var t=e.pos;if(this.regexp_eatUnicodePropertyName(e)&&e.eat(61)){var i=e.lastStringValue;if(this.regexp_eatUnicodePropertyValue(e)){var s=e.lastStringValue;return this.regexp_validateUnicodePropertyNameAndValue(e,i,s),F}}if(e.pos=t,this.regexp_eatLoneUnicodePropertyNameOrValue(e)){var r=e.lastStringValue;return this.regexp_validateUnicodePropertyNameOrValue(e,r)}return ht},c.regexp_validateUnicodePropertyNameAndValue=function(e,t,i){W(e.unicodeProperties.nonBinary,t)||e.raise("Invalid property name"),e.unicodeProperties.nonBinary[t].test(i)||e.raise("Invalid property value")},c.regexp_validateUnicodePropertyNameOrValue=function(e,t){if(e.unicodeProperties.binary.test(t))return F;if(e.switchV&&e.unicodeProperties.binaryOfStrings.test(t))return T;e.raise("Invalid property name")},c.regexp_eatUnicodePropertyName=function(e){var t=0;for(e.lastStringValue="";ct(t=e.current());)e.lastStringValue+=B(t),e.advance();return e.lastStringValue!==""};function ct(e){return ot(e)||e===95}c.regexp_eatUnicodePropertyValue=function(e){var t=0;for(e.lastStringValue="";Kt(t=e.current());)e.lastStringValue+=B(t),e.advance();return e.lastStringValue!==""};function Kt(e){return ct(e)||fe(e)}c.regexp_eatLoneUnicodePropertyNameOrValue=function(e){return this.regexp_eatUnicodePropertyValue(e)},c.regexp_eatCharacterClass=function(e){if(e.eat(91)){var t=e.eat(94),i=this.regexp_classContents(e);return e.eat(93)||e.raise("Unterminated character class"),t&&i===T&&e.raise("Negated character class may contain strings"),!0}return!1},c.regexp_classContents=function(e){return e.current()===93?F:e.switchV?this.regexp_classSetExpression(e):(this.regexp_nonEmptyClassRanges(e),F)},c.regexp_nonEmptyClassRanges=function(e){for(;this.regexp_eatClassAtom(e);){var t=e.lastIntValue;if(e.eat(45)&&this.regexp_eatClassAtom(e)){var i=e.lastIntValue;e.switchU&&(t===-1||i===-1)&&e.raise("Invalid character class"),t!==-1&&i!==-1&&t>i&&e.raise("Range out of order in character class")}}},c.regexp_eatClassAtom=function(e){var t=e.pos;if(e.eat(92)){if(this.regexp_eatClassEscape(e))return!0;if(e.switchU){var i=e.current();(i===99||ft(i))&&e.raise("Invalid class escape"),e.raise("Invalid escape")}e.pos=t}var s=e.current();return s!==93?(e.lastIntValue=s,e.advance(),!0):!1},c.regexp_eatClassEscape=function(e){var t=e.pos;if(e.eat(98))return e.lastIntValue=8,!0;if(e.switchU&&e.eat(45))return e.lastIntValue=45,!0;if(!e.switchU&&e.eat(99)){if(this.regexp_eatClassControlLetter(e))return!0;e.pos=t}return this.regexp_eatCharacterClassEscape(e)||this.regexp_eatCharacterEscape(e)},c.regexp_classSetExpression=function(e){var t=F,i;if(!this.regexp_eatClassSetRange(e))if(i=this.regexp_eatClassSetOperand(e)){i===T&&(t=T);for(var s=e.pos;e.eatChars([38,38]);){if(e.current()!==38&&(i=this.regexp_eatClassSetOperand(e))){i!==T&&(t=F);continue}e.raise("Invalid character in character class")}if(s!==e.pos)return t;for(;e.eatChars([45,45]);)this.regexp_eatClassSetOperand(e)||e.raise("Invalid character in character class");if(s!==e.pos)return t}else e.raise("Invalid character in character class");for(;;)if(!this.regexp_eatClassSetRange(e)){if(i=this.regexp_eatClassSetOperand(e),!i)return t;i===T&&(t=T)}},c.regexp_eatClassSetRange=function(e){var t=e.pos;if(this.regexp_eatClassSetCharacter(e)){var i=e.lastIntValue;if(e.eat(45)&&this.regexp_eatClassSetCharacter(e)){var s=e.lastIntValue;return i!==-1&&s!==-1&&i>s&&e.raise("Range out of order in character class"),!0}e.pos=t}return!1},c.regexp_eatClassSetOperand=function(e){return this.regexp_eatClassSetCharacter(e)?F:this.regexp_eatClassStringDisjunction(e)||this.regexp_eatNestedClass(e)},c.regexp_eatNestedClass=function(e){var t=e.pos;if(e.eat(91)){var i=e.eat(94),s=this.regexp_classContents(e);if(e.eat(93))return i&&s===T&&e.raise("Negated character class may contain strings"),s;e.pos=t}if(e.eat(92)){var r=this.regexp_eatCharacterClassEscape(e);if(r)return r;e.pos=t}return null},c.regexp_eatClassStringDisjunction=function(e){var t=e.pos;if(e.eatChars([92,113])){if(e.eat(123)){var i=this.regexp_classStringDisjunctionContents(e);if(e.eat(125))return i}else e.raise("Invalid escape");e.pos=t}return null},c.regexp_classStringDisjunctionContents=function(e){for(var t=this.regexp_classString(e);e.eat(124);)this.regexp_classString(e)===T&&(t=T);return t},c.regexp_classString=function(e){for(var t=0;this.regexp_eatClassSetCharacter(e);)t++;return t===1?F:T},c.regexp_eatClassSetCharacter=function(e){var t=e.pos;if(e.eat(92))return this.regexp_eatCharacterEscape(e)||this.regexp_eatClassSetReservedPunctuator(e)?!0:e.eat(98)?(e.lastIntValue=8,!0):(e.pos=t,!1);var i=e.current();return i<0||i===e.lookahead()&&Qt(i)||Yt(i)?!1:(e.advance(),e.lastIntValue=i,!0)};function Qt(e){return e===33||e>=35&&e<=38||e>=42&&e<=44||e===46||e>=58&&e<=64||e===94||e===96||e===126}function Yt(e){return e===40||e===41||e===45||e===47||e>=91&&e<=93||e>=123&&e<=125}c.regexp_eatClassSetReservedPunctuator=function(e){var t=e.current();return Xt(t)?(e.lastIntValue=t,e.advance(),!0):!1};function Xt(e){return e===33||e===35||e===37||e===38||e===44||e===45||e>=58&&e<=62||e===64||e===96||e===126}c.regexp_eatClassControlLetter=function(e){var t=e.current();return fe(t)||t===95?(e.lastIntValue=t%32,e.advance(),!0):!1},c.regexp_eatHexEscapeSequence=function(e){var t=e.pos;if(e.eat(120)){if(this.regexp_eatFixedHexDigits(e,2))return!0;e.switchU&&e.raise("Invalid escape"),e.pos=t}return!1},c.regexp_eatDecimalDigits=function(e){var t=e.pos,i=0;for(e.lastIntValue=0;fe(i=e.current());)e.lastIntValue=10*e.lastIntValue+(i-48),e.advance();return e.pos!==t};function fe(e){return e>=48&&e<=57}c.regexp_eatHexDigits=function(e){var t=e.pos,i=0;for(e.lastIntValue=0;lt(i=e.current());)e.lastIntValue=16*e.lastIntValue+pt(i),e.advance();return e.pos!==t};function lt(e){return e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102}function pt(e){return e>=65&&e<=70?10+(e-65):e>=97&&e<=102?10+(e-97):e-48}c.regexp_eatLegacyOctalEscapeSequence=function(e){if(this.regexp_eatOctalDigit(e)){var t=e.lastIntValue;if(this.regexp_eatOctalDigit(e)){var i=e.lastIntValue;t<=3&&this.regexp_eatOctalDigit(e)?e.lastIntValue=t*64+i*8+e.lastIntValue:e.lastIntValue=t*8+i}else e.lastIntValue=t;return!0}return!1},c.regexp_eatOctalDigit=function(e){var t=e.current();return ft(t)?(e.lastIntValue=t-48,e.advance(),!0):(e.lastIntValue=0,!1)};function ft(e){return e>=48&&e<=55}c.regexp_eatFixedHexDigits=function(e,t){var i=e.pos;e.lastIntValue=0;for(var s=0;s=this.input.length)return this.finishToken(a.eof);if(e.override)return e.override(this);this.readToken(this.fullCharCodeAtPos())},x.readToken=function(e){return L(e,this.options.ecmaVersion>=6)||e===92?this.readWord():this.getTokenFromCode(e)},x.fullCharCodeAt=function(e){var t=this.input.charCodeAt(e);if(t<=55295||t>=56320)return t;var i=this.input.charCodeAt(e+1);return i<=56319||i>=57344?t:(t<<10)+i-56613888},x.fullCharCodeAtPos=function(){return this.fullCharCodeAt(this.pos)},x.skipBlockComment=function(){var e=this.options.onComment&&this.curPosition(),t=this.pos,i=this.input.indexOf("*/",this.pos+=2);if(i===-1&&this.raise(this.pos-2,"Unterminated comment"),this.pos=i+2,this.options.locations)for(var s=void 0,r=t;(s=Le(this.input,r,this.pos))>-1;)++this.curLine,r=this.lineStart=s;this.options.onComment&&this.options.onComment(!0,this.input.slice(t+2,i),t,this.pos,e,this.curPosition())},x.skipLineComment=function(e){for(var t=this.pos,i=this.options.onComment&&this.curPosition(),s=this.input.charCodeAt(this.pos+=e);this.pos8&&e<14||e>=5760&&be.test(String.fromCharCode(e)))++this.pos;else break e}}},x.finishToken=function(e,t){this.end=this.pos,this.options.locations&&(this.endLoc=this.curPosition());var i=this.type;this.type=e,this.value=t,this.updateContext(i)},x.readToken_dot=function(){var e=this.input.charCodeAt(this.pos+1);if(e>=48&&e<=57)return this.readNumber(!0);var t=this.input.charCodeAt(this.pos+2);return this.options.ecmaVersion>=6&&e===46&&t===46?(this.pos+=3,this.finishToken(a.ellipsis)):(++this.pos,this.finishToken(a.dot))},x.readToken_slash=function(){var e=this.input.charCodeAt(this.pos+1);return this.exprAllowed?(++this.pos,this.readRegexp()):e===61?this.finishOp(a.assign,2):this.finishOp(a.slash,1)},x.readToken_mult_modulo_exp=function(e){var t=this.input.charCodeAt(this.pos+1),i=1,s=e===42?a.star:a.modulo;return this.options.ecmaVersion>=7&&e===42&&t===42&&(++i,s=a.starstar,t=this.input.charCodeAt(this.pos+2)),t===61?this.finishOp(a.assign,i+1):this.finishOp(s,i)},x.readToken_pipe_amp=function(e){var t=this.input.charCodeAt(this.pos+1);if(t===e){if(this.options.ecmaVersion>=12){var i=this.input.charCodeAt(this.pos+2);if(i===61)return this.finishOp(a.assign,3)}return this.finishOp(e===124?a.logicalOR:a.logicalAND,2)}return t===61?this.finishOp(a.assign,2):this.finishOp(e===124?a.bitwiseOR:a.bitwiseAND,1)},x.readToken_caret=function(){var e=this.input.charCodeAt(this.pos+1);return e===61?this.finishOp(a.assign,2):this.finishOp(a.bitwiseXOR,1)},x.readToken_plus_min=function(e){var t=this.input.charCodeAt(this.pos+1);return t===e?t===45&&!this.inModule&&this.input.charCodeAt(this.pos+2)===62&&(this.lastTokEnd===0||k.test(this.input.slice(this.lastTokEnd,this.pos)))?(this.skipLineComment(3),this.skipSpace(),this.nextToken()):this.finishOp(a.incDec,2):t===61?this.finishOp(a.assign,2):this.finishOp(a.plusMin,1)},x.readToken_lt_gt=function(e){var t=this.input.charCodeAt(this.pos+1),i=1;return t===e?(i=e===62&&this.input.charCodeAt(this.pos+2)===62?3:2,this.input.charCodeAt(this.pos+i)===61?this.finishOp(a.assign,i+1):this.finishOp(a.bitShift,i)):t===33&&e===60&&!this.inModule&&this.input.charCodeAt(this.pos+2)===45&&this.input.charCodeAt(this.pos+3)===45?(this.skipLineComment(4),this.skipSpace(),this.nextToken()):(t===61&&(i=2),this.finishOp(a.relational,i))},x.readToken_eq_excl=function(e){var t=this.input.charCodeAt(this.pos+1);return t===61?this.finishOp(a.equality,this.input.charCodeAt(this.pos+2)===61?3:2):e===61&&t===62&&this.options.ecmaVersion>=6?(this.pos+=2,this.finishToken(a.arrow)):this.finishOp(e===61?a.eq:a.prefix,1)},x.readToken_question=function(){var e=this.options.ecmaVersion;if(e>=11){var t=this.input.charCodeAt(this.pos+1);if(t===46){var i=this.input.charCodeAt(this.pos+2);if(i<48||i>57)return this.finishOp(a.questionDot,2)}if(t===63){if(e>=12){var s=this.input.charCodeAt(this.pos+2);if(s===61)return this.finishOp(a.assign,3)}return this.finishOp(a.coalesce,2)}}return this.finishOp(a.question,1)},x.readToken_numberSign=function(){var e=this.options.ecmaVersion,t=35;if(e>=13&&(++this.pos,t=this.fullCharCodeAtPos(),L(t,!0)||t===92))return this.finishToken(a.privateId,this.readWord1());this.raise(this.pos,"Unexpected character \'"+B(t)+"\'")},x.getTokenFromCode=function(e){switch(e){case 46:return this.readToken_dot();case 40:return++this.pos,this.finishToken(a.parenL);case 41:return++this.pos,this.finishToken(a.parenR);case 59:return++this.pos,this.finishToken(a.semi);case 44:return++this.pos,this.finishToken(a.comma);case 91:return++this.pos,this.finishToken(a.bracketL);case 93:return++this.pos,this.finishToken(a.bracketR);case 123:return++this.pos,this.finishToken(a.braceL);case 125:return++this.pos,this.finishToken(a.braceR);case 58:return++this.pos,this.finishToken(a.colon);case 96:if(this.options.ecmaVersion<6)break;return++this.pos,this.finishToken(a.backQuote);case 48:var t=this.input.charCodeAt(this.pos+1);if(t===120||t===88)return this.readRadixNumber(16);if(this.options.ecmaVersion>=6){if(t===111||t===79)return this.readRadixNumber(8);if(t===98||t===66)return this.readRadixNumber(2)}case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return this.readNumber(!1);case 34:case 39:return this.readString(e);case 47:return this.readToken_slash();case 37:case 42:return this.readToken_mult_modulo_exp(e);case 124:case 38:return this.readToken_pipe_amp(e);case 94:return this.readToken_caret();case 43:case 45:return this.readToken_plus_min(e);case 60:case 62:return this.readToken_lt_gt(e);case 61:case 33:return this.readToken_eq_excl(e);case 63:return this.readToken_question();case 126:return this.finishOp(a.prefix,1);case 35:return this.readToken_numberSign()}this.raise(this.pos,"Unexpected character \'"+B(e)+"\'")},x.finishOp=function(e,t){var i=this.input.slice(this.pos,this.pos+t);return this.pos+=t,this.finishToken(e,i)},x.readRegexp=function(){for(var e,t,i=this.pos;;){this.pos>=this.input.length&&this.raise(i,"Unterminated regular expression");var s=this.input.charAt(this.pos);if(k.test(s)&&this.raise(i,"Unterminated regular expression"),e)e=!1;else{if(s==="[")t=!0;else if(s==="]"&&t)t=!1;else if(s==="/"&&!t)break;e=s==="\\\\"}++this.pos}var r=this.input.slice(i,this.pos);++this.pos;var n=this.pos,u=this.readWord1();this.containsEsc&&this.unexpected(n);var o=this.regexpState||(this.regexpState=new R(this));o.reset(i,r,u),this.validateRegExpFlags(o),this.validateRegExpPattern(o);var h=null;try{h=new RegExp(r,u)}catch{}return this.finishToken(a.regexp,{pattern:r,flags:u,value:h})},x.readInt=function(e,t,i){for(var s=this.options.ecmaVersion>=12&&t===void 0,r=i&&this.input.charCodeAt(this.pos)===48,n=this.pos,u=0,o=0,h=0,p=t??1/0;h=97?y=d-97+10:d>=65?y=d-65+10:d>=48&&d<=57?y=d-48:y=1/0,y>=e)break;o=d,u=u*e+y}return s&&o===95&&this.raiseRecoverable(this.pos-1,"Numeric separator is not allowed at the last of digits"),this.pos===n||t!=null&&this.pos-n!==t?null:u};function Zt(e,t){return t?parseInt(e,8):parseFloat(e.replace(/_/g,""))}function dt(e){return typeof BigInt!="function"?null:BigInt(e.replace(/_/g,""))}x.readRadixNumber=function(e){var t=this.pos;this.pos+=2;var i=this.readInt(e);return i==null&&this.raise(this.start+2,"Expected number in radix "+e),this.options.ecmaVersion>=11&&this.input.charCodeAt(this.pos)===110?(i=dt(this.input.slice(t,this.pos)),++this.pos):L(this.fullCharCodeAtPos())&&this.raise(this.pos,"Identifier directly after number"),this.finishToken(a.num,i)},x.readNumber=function(e){var t=this.pos;!e&&this.readInt(10,void 0,!0)===null&&this.raise(t,"Invalid number");var i=this.pos-t>=2&&this.input.charCodeAt(t)===48;i&&this.strict&&this.raise(t,"Invalid number");var s=this.input.charCodeAt(this.pos);if(!i&&!e&&this.options.ecmaVersion>=11&&s===110){var r=dt(this.input.slice(t,this.pos));return++this.pos,L(this.fullCharCodeAtPos())&&this.raise(this.pos,"Identifier directly after number"),this.finishToken(a.num,r)}i&&/[89]/.test(this.input.slice(t,this.pos))&&(i=!1),s===46&&!i&&(++this.pos,this.readInt(10),s=this.input.charCodeAt(this.pos)),(s===69||s===101)&&!i&&(s=this.input.charCodeAt(++this.pos),(s===43||s===45)&&++this.pos,this.readInt(10)===null&&this.raise(t,"Invalid number")),L(this.fullCharCodeAtPos())&&this.raise(this.pos,"Identifier directly after number");var n=Zt(this.input.slice(t,this.pos),i);return this.finishToken(a.num,n)},x.readCodePoint=function(){var e=this.input.charCodeAt(this.pos),t;if(e===123){this.options.ecmaVersion<6&&this.unexpected();var i=++this.pos;t=this.readHexChar(this.input.indexOf("}",this.pos)-this.pos),++this.pos,t>1114111&&this.invalidStringToken(i,"Code point out of bounds")}else t=this.readHexChar(4);return t},x.readString=function(e){for(var t="",i=++this.pos;;){this.pos>=this.input.length&&this.raise(this.start,"Unterminated string constant");var s=this.input.charCodeAt(this.pos);if(s===e)break;s===92?(t+=this.input.slice(i,this.pos),t+=this.readEscapedChar(!1),i=this.pos):s===8232||s===8233?(this.options.ecmaVersion<10&&this.raise(this.start,"Unterminated string constant"),++this.pos,this.options.locations&&(this.curLine++,this.lineStart=this.pos)):(q(s)&&this.raise(this.start,"Unterminated string constant"),++this.pos)}return t+=this.input.slice(i,this.pos++),this.finishToken(a.string,t)};var xt={};x.tryReadTemplateToken=function(){this.inTemplateElement=!0;try{this.readTmplToken()}catch(e){if(e===xt)this.readInvalidTemplateToken();else throw e}this.inTemplateElement=!1},x.invalidStringToken=function(e,t){if(this.inTemplateElement&&this.options.ecmaVersion>=9)throw xt;this.raise(e,t)},x.readTmplToken=function(){for(var e="",t=this.pos;;){this.pos>=this.input.length&&this.raise(this.start,"Unterminated template");var i=this.input.charCodeAt(this.pos);if(i===96||i===36&&this.input.charCodeAt(this.pos+1)===123)return this.pos===this.start&&(this.type===a.template||this.type===a.invalidTemplate)?i===36?(this.pos+=2,this.finishToken(a.dollarBraceL)):(++this.pos,this.finishToken(a.backQuote)):(e+=this.input.slice(t,this.pos),this.finishToken(a.template,e));if(i===92)e+=this.input.slice(t,this.pos),e+=this.readEscapedChar(!0),t=this.pos;else if(q(i)){switch(e+=this.input.slice(t,this.pos),++this.pos,i){case 13:this.input.charCodeAt(this.pos)===10&&++this.pos;case 10:e+=`\n`;break;default:e+=String.fromCharCode(i);break}this.options.locations&&(++this.curLine,this.lineStart=this.pos),t=this.pos}else++this.pos}},x.readInvalidTemplateToken=function(){for(;this.pos=48&&t<=55){var s=this.input.substr(this.pos-1,3).match(/^[0-7]+/)[0],r=parseInt(s,8);return r>255&&(s=s.slice(0,-1),r=parseInt(s,8)),this.pos+=s.length-1,t=this.input.charCodeAt(this.pos),(s!=="0"||t===56||t===57)&&(this.strict||e)&&this.invalidStringToken(this.pos-1-s.length,e?"Octal literal in template string":"Octal literal in strict mode"),String.fromCharCode(r)}return q(t)?(this.options.locations&&(this.lineStart=this.pos,++this.curLine),""):String.fromCharCode(t)}},x.readHexChar=function(e){var t=this.pos,i=this.readInt(16,e);return i===null&&this.invalidStringToken(t,"Bad character escape sequence"),i},x.readWord1=function(){this.containsEsc=!1;for(var e="",t=!0,i=this.pos,s=this.options.ecmaVersion>=6;this.pose)return!1;if(i+=t[s+1],i>=e)return!0}return!1}function L(e,t){return e<65?e===36:e<91?!0:e<97?e===95:e<123?!0:e<=65535?e>=170&&bt.test(String.fromCharCode(e)):t===!1?!1:ge(e,Pe)}function O(e,t){return e<48?e===36:e<58?!0:e<65?!1:e<91?!0:e<97?e===95:e<123?!0:e<=65535?e>=170&&yt.test(String.fromCharCode(e)):t===!1?!1:ge(e,Pe)||ge(e,Z)}var v=function(t,i){i===void 0&&(i={}),this.label=t,this.keyword=i.keyword,this.beforeExpr=!!i.beforeExpr,this.startsExpr=!!i.startsExpr,this.isLoop=!!i.isLoop,this.isAssign=!!i.isAssign,this.prefix=!!i.prefix,this.postfix=!!i.postfix,this.binop=i.binop||null,this.updateContext=null};function E(e,t){return new v(e,{beforeExpr:!0,binop:t})}var I={beforeExpr:!0},A={startsExpr:!0},ae={};function m(e,t){return t===void 0&&(t={}),t.keyword=e,ae[e]=new v(e,t)}var a={num:new v("num",A),regexp:new v("regexp",A),string:new v("string",A),name:new v("name",A),privateId:new v("privateId",A),eof:new v("eof"),bracketL:new v("[",{beforeExpr:!0,startsExpr:!0}),bracketR:new v("]"),braceL:new v("{",{beforeExpr:!0,startsExpr:!0}),braceR:new v("}"),parenL:new v("(",{beforeExpr:!0,startsExpr:!0}),parenR:new v(")"),comma:new v(",",I),semi:new v(";",I),colon:new v(":",I),dot:new v("."),question:new v("?",I),questionDot:new v("?."),arrow:new v("=>",I),template:new v("template"),invalidTemplate:new v("invalidTemplate"),ellipsis:new v("...",I),backQuote:new v("`",A),dollarBraceL:new v("${",{beforeExpr:!0,startsExpr:!0}),eq:new v("=",{beforeExpr:!0,isAssign:!0}),assign:new v("_=",{beforeExpr:!0,isAssign:!0}),incDec:new v("++/--",{prefix:!0,postfix:!0,startsExpr:!0}),prefix:new v("!/~",{beforeExpr:!0,prefix:!0,startsExpr:!0}),logicalOR:E("||",1),logicalAND:E("&&",2),bitwiseOR:E("|",3),bitwiseXOR:E("^",4),bitwiseAND:E("&",5),equality:E("==/!=/===/!==",6),relational:E("/<=/>=",7),bitShift:E("<>/>>>",8),plusMin:new v("+/-",{beforeExpr:!0,binop:9,prefix:!0,startsExpr:!0}),modulo:E("%",10),star:E("*",10),slash:E("/",10),starstar:new v("**",{beforeExpr:!0}),coalesce:E("??",1),_break:m("break"),_case:m("case",I),_catch:m("catch"),_continue:m("continue"),_debugger:m("debugger"),_default:m("default",I),_do:m("do",{isLoop:!0,beforeExpr:!0}),_else:m("else",I),_finally:m("finally"),_for:m("for",{isLoop:!0}),_function:m("function",A),_if:m("if"),_return:m("return",I),_switch:m("switch"),_throw:m("throw",I),_try:m("try"),_var:m("var"),_const:m("const"),_while:m("while",{isLoop:!0}),_with:m("with"),_new:m("new",{beforeExpr:!0,startsExpr:!0}),_this:m("this",A),_super:m("super",A),_class:m("class",A),_extends:m("extends",I),_export:m("export"),_import:m("import",A),_null:m("null",A),_true:m("true",A),_false:m("false",A),_in:m("in",{beforeExpr:!0,binop:7}),_instanceof:m("instanceof",{beforeExpr:!0,binop:7}),_typeof:m("typeof",{beforeExpr:!0,prefix:!0,startsExpr:!0}),_void:m("void",{beforeExpr:!0,prefix:!0,startsExpr:!0}),_delete:m("delete",{beforeExpr:!0,prefix:!0,startsExpr:!0})},k=/\\r\\n?|\\n|\\u2028|\\u2029/,Te=new RegExp(k.source,"g");function q(e){return e===10||e===13||e===8232||e===8233}function Le(e,t,i){i===void 0&&(i=e.length);for(var s=t;s>10)+55296,(e&1023)+56320))}var St=/(?:[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF])/,z=function(t,i){this.line=t,this.column=i};z.prototype.offset=function(t){return new z(this.line,this.column+t)};var J=function(t,i,s){this.start=i,this.end=s,t.sourceFile!==null&&(this.source=t.sourceFile)};function ye(e,t){for(var i=1,s=0;;){var r=Le(e,s,t);if(r<0)return new z(i,t-s);++i,s=r}}var re={ecmaVersion:null,sourceType:"script",onInsertedSemicolon:null,onTrailingComma:null,allowReserved:null,allowReturnOutsideFunction:!1,allowImportExportEverywhere:!1,allowAwaitOutsideFunction:null,allowSuperOutsideMethod:null,allowHashBang:!1,checkPrivateFields:!0,locations:!1,onToken:null,onComment:null,ranges:!1,program:null,sourceFile:null,directSourceFile:null,preserveParens:!1},De=!1;function kt(e){var t={};for(var i in re)t[i]=e&&W(e,i)?e[i]:re[i];if(t.ecmaVersion==="latest"?t.ecmaVersion=1e8:t.ecmaVersion==null?(!De&&typeof console=="object"&&console.warn&&(De=!0,console.warn(`Since Acorn 8.0.0, options.ecmaVersion is required.\nDefaulting to 2020, but this will stop working in the future.`)),t.ecmaVersion=11):t.ecmaVersion>=2015&&(t.ecmaVersion-=2009),t.allowReserved==null&&(t.allowReserved=t.ecmaVersion<5),(!e||e.allowHashBang==null)&&(t.allowHashBang=t.ecmaVersion>=14),Oe(t.onToken)){var s=t.onToken;t.onToken=function(r){return s.push(r)}}if(Oe(t.onComment)&&(t.onComment=wt(t,t.onComment)),t.sourceType==="commonjs"&&t.allowAwaitOutsideFunction)throw new Error("Cannot use allowAwaitOutsideFunction with sourceType: commonjs");return t}function wt(e,t){return function(i,s,r,n,u,o){var h={type:i?"Block":"Line",value:s,start:r,end:n};e.locations&&(h.loc=new J(this,u,o)),e.ranges&&(h.range=[r,n]),t.push(h)}}var j=1,G=2,Ce=4,Fe=8,_e=16,Me=32,ne=64,Ue=128,H=256,$=512,qe=1024,ue=j|G|H;function Se(e,t){return G|(e?Ce:0)|(t?Fe:0)}var oe=0,ke=1,D=2,je=3,Ge=4,He=5,C=function(t,i,s){this.options=t=kt(t),this.sourceFile=t.sourceFile,this.keywords=M(gt[t.ecmaVersion>=6?6:t.sourceType==="module"?"5module":5]);var r="";t.allowReserved!==!0&&(r=me[t.ecmaVersion>=6?6:t.ecmaVersion===5?5:3],t.sourceType==="module"&&(r+=" await")),this.reservedWords=M(r);var n=(r?r+" ":"")+me.strict;this.reservedWordsStrict=M(n),this.reservedWordsStrictBind=M(n+" "+me.strictBind),this.input=String(i),this.containsEsc=!1,s?(this.pos=s,this.lineStart=this.input.lastIndexOf(`\n`,s-1)+1,this.curLine=this.input.slice(0,this.lineStart).split(k).length):(this.pos=this.lineStart=0,this.curLine=1),this.type=a.eof,this.value=null,this.start=this.end=this.pos,this.startLoc=this.endLoc=this.curPosition(),this.lastTokEndLoc=this.lastTokStartLoc=null,this.lastTokStart=this.lastTokEnd=this.pos,this.context=this.initialContext(),this.exprAllowed=!0,this.inModule=t.sourceType==="module",this.strict=this.inModule||this.strictDirective(this.pos),this.potentialArrowAt=-1,this.potentialArrowInForAwait=!1,this.yieldPos=this.awaitPos=this.awaitIdentPos=0,this.labels=[],this.undefinedExports=Object.create(null),this.pos===0&&t.allowHashBang&&this.input.slice(0,2)==="#!"&&this.skipLineComment(2),this.scopeStack=[],this.enterScope(this.options.sourceType==="commonjs"?G:j),this.regexpState=null,this.privateNameStack=[]},P={inFunction:{configurable:!0},inGenerator:{configurable:!0},inAsync:{configurable:!0},canAwait:{configurable:!0},allowReturn:{configurable:!0},allowSuper:{configurable:!0},allowDirectSuper:{configurable:!0},treatFunctionsAsVar:{configurable:!0},allowNewDotTarget:{configurable:!0},allowUsing:{configurable:!0},inClassStaticBlock:{configurable:!0}};C.prototype.parse=function(){var t=this.options.program||this.startNode();return this.nextToken(),this.parseTopLevel(t)},P.inFunction.get=function(){return(this.currentVarScope().flags&G)>0},P.inGenerator.get=function(){return(this.currentVarScope().flags&Fe)>0},P.inAsync.get=function(){return(this.currentVarScope().flags&Ce)>0},P.canAwait.get=function(){for(var e=this.scopeStack.length-1;e>=0;e--){var t=this.scopeStack[e],i=t.flags;if(i&(H|$))return!1;if(i&G)return(i&Ce)>0}return this.inModule&&this.options.ecmaVersion>=13||this.options.allowAwaitOutsideFunction},P.allowReturn.get=function(){return!!(this.inFunction||this.options.allowReturnOutsideFunction&&this.currentVarScope().flags&j)},P.allowSuper.get=function(){var e=this.currentThisScope(),t=e.flags;return(t&ne)>0||this.options.allowSuperOutsideMethod},P.allowDirectSuper.get=function(){return(this.currentThisScope().flags&Ue)>0},P.treatFunctionsAsVar.get=function(){return this.treatFunctionsAsVarInScope(this.currentScope())},P.allowNewDotTarget.get=function(){for(var e=this.scopeStack.length-1;e>=0;e--){var t=this.scopeStack[e],i=t.flags;if(i&(H|$)||i&G&&!(i&_e))return!0}return!1},P.allowUsing.get=function(){var e=this.currentScope(),t=e.flags;return!(t&qe||!this.inModule&&t&j)},P.inClassStaticBlock.get=function(){return(this.currentVarScope().flags&H)>0},C.extend=function(){for(var t=[],i=arguments.length;i--;)t[i]=arguments[i];for(var s=this,r=0;r=,?^&]/.test(r)||r==="!"&&this.input.charAt(s+1)==="=")}e+=t[0].length,_.lastIndex=e,e+=_.exec(this.input)[0].length,this.input[e]===";"&&e++}},w.eat=function(e){return this.type===e?(this.next(),!0):!1},w.isContextual=function(e){return this.type===a.name&&this.value===e&&!this.containsEsc},w.eatContextual=function(e){return this.isContextual(e)?(this.next(),!0):!1},w.expectContextual=function(e){this.eatContextual(e)||this.unexpected()},w.canInsertSemicolon=function(){return this.type===a.eof||this.type===a.braceR||k.test(this.input.slice(this.lastTokEnd,this.start))},w.insertSemicolon=function(){if(this.canInsertSemicolon())return this.options.onInsertedSemicolon&&this.options.onInsertedSemicolon(this.lastTokEnd,this.lastTokEndLoc),!0},w.semicolon=function(){!this.eat(a.semi)&&!this.insertSemicolon()&&this.unexpected()},w.afterTrailingComma=function(e,t){if(this.type===e)return this.options.onTrailingComma&&this.options.onTrailingComma(this.lastTokStart,this.lastTokStartLoc),t||this.next(),!0},w.expect=function(e){this.eat(e)||this.unexpected()},w.unexpected=function(e){this.raise(e??this.start,"Unexpected token")};var he=function(){this.shorthandAssign=this.trailingComma=this.parenthesizedAssign=this.parenthesizedBind=this.doubleProto=-1};w.checkPatternErrors=function(e,t){if(e){e.trailingComma>-1&&this.raiseRecoverable(e.trailingComma,"Comma is not permitted after the rest element");var i=t?e.parenthesizedAssign:e.parenthesizedBind;i>-1&&this.raiseRecoverable(i,t?"Assigning to rvalue":"Parenthesized pattern")}},w.checkExpressionErrors=function(e,t){if(!e)return!1;var i=e.shorthandAssign,s=e.doubleProto;if(!t)return i>=0||s>=0;i>=0&&this.raise(i,"Shorthand property assignments are valid only in destructuring patterns"),s>=0&&this.raiseRecoverable(s,"Redefinition of __proto__ property")},w.checkYieldAwaitInDefaultParams=function(){this.yieldPos&&(!this.awaitPos||this.yieldPos=6&&this.unexpected(),this.parseFunctionStatement(r,!1,!e);case a._class:return e&&this.unexpected(),this.parseClass(r,!0);case a._if:return this.parseIfStatement(r);case a._return:return this.parseReturnStatement(r);case a._switch:return this.parseSwitchStatement(r);case a._throw:return this.parseThrowStatement(r);case a._try:return this.parseTryStatement(r);case a._const:case a._var:return n=n||this.value,e&&n!=="var"&&this.unexpected(),this.parseVarStatement(r,n);case a._while:return this.parseWhileStatement(r);case a._with:return this.parseWithStatement(r);case a.braceL:return this.parseBlock(!0,r);case a.semi:return this.parseEmptyStatement(r);case a._export:case a._import:if(this.options.ecmaVersion>10&&s===a._import){_.lastIndex=this.pos;var u=_.exec(this.input),o=this.pos+u[0].length,h=this.input.charCodeAt(o);if(h===40||h===46)return this.parseExpressionStatement(r,this.parseExpression())}return this.options.allowImportExportEverywhere||(t||this.raise(this.start,"\'import\' and \'export\' may only appear at the top level"),this.inModule||this.raise(this.start,"\'import\' and \'export\' may appear only with \'sourceType: module\'")),s===a._import?this.parseImport(r):this.parseExport(r,i);default:if(this.isAsyncFunction())return e&&this.unexpected(),this.next(),this.parseFunctionStatement(r,!0,!e);var p=this.isAwaitUsing(!1)?"await using":this.isUsing(!1)?"using":null;if(p)return this.allowUsing||this.raise(this.start,"Using declaration cannot appear in the top level when source type is `script` or in the bare case statement"),p==="await using"&&(this.canAwait||this.raise(this.start,"Await using cannot appear outside of async function"),this.next()),this.next(),this.parseVar(r,!1,p),this.semicolon(),this.finishNode(r,"VariableDeclaration");var d=this.value,y=this.parseExpression();return s===a.name&&y.type==="Identifier"&&this.eat(a.colon)?this.parseLabeledStatement(r,d,y,e):this.parseExpressionStatement(r,y)}},l.parseBreakContinueStatement=function(e,t){var i=t==="break";this.next(),this.eat(a.semi)||this.insertSemicolon()?e.label=null:this.type!==a.name?this.unexpected():(e.label=this.parseIdent(),this.semicolon());for(var s=0;s=6?this.eat(a.semi):this.semicolon(),this.finishNode(e,"DoWhileStatement")},l.parseForStatement=function(e){this.next();var t=this.options.ecmaVersion>=9&&this.canAwait&&this.eatContextual("await")?this.lastTokStart:-1;if(this.labels.push(we),this.enterScope(0),this.expect(a.parenL),this.type===a.semi)return t>-1&&this.unexpected(t),this.parseFor(e,null);var i=this.isLet();if(this.type===a._var||this.type===a._const||i){var s=this.startNode(),r=i?"let":this.value;return this.next(),this.parseVar(s,!0,r),this.finishNode(s,"VariableDeclaration"),this.parseForAfterInit(e,s,t)}var n=this.isContextual("let"),u=!1,o=this.isUsing(!0)?"using":this.isAwaitUsing(!0)?"await using":null;if(o){var h=this.startNode();return this.next(),o==="await using"&&(this.canAwait||this.raise(this.start,"Await using cannot appear outside of async function"),this.next()),this.parseVar(h,!0,o),this.finishNode(h,"VariableDeclaration"),this.parseForAfterInit(e,h,t)}var p=this.containsEsc,d=new he,y=this.start,S=t>-1?this.parseExprSubscripts(d,"await"):this.parseExpression(!0,d);return this.type===a._in||(u=this.options.ecmaVersion>=6&&this.isContextual("of"))?(t>-1?(this.type===a._in&&this.unexpected(t),e.await=!0):u&&this.options.ecmaVersion>=8&&(S.start===y&&!p&&S.type==="Identifier"&&S.name==="async"?this.unexpected():this.options.ecmaVersion>=9&&(e.await=!1)),n&&u&&this.raise(S.start,"The left-hand side of a for-of loop may not start with \'let\'."),this.toAssignable(S,!1,d),this.checkLValPattern(S),this.parseForIn(e,S)):(this.checkExpressionErrors(d,!0),t>-1&&this.unexpected(t),this.parseFor(e,S))},l.parseForAfterInit=function(e,t,i){return(this.type===a._in||this.options.ecmaVersion>=6&&this.isContextual("of"))&&t.declarations.length===1?(this.options.ecmaVersion>=9&&(this.type===a._in?i>-1&&this.unexpected(i):e.await=i>-1),this.parseForIn(e,t)):(i>-1&&this.unexpected(i),this.parseFor(e,t))},l.parseFunctionStatement=function(e,t,i){return this.next(),this.parseFunction(e,ee|(i?0:Ae),!1,t)},l.parseIfStatement=function(e){return this.next(),e.test=this.parseParenExpression(),e.consequent=this.parseStatement("if"),e.alternate=this.eat(a._else)?this.parseStatement("if"):null,this.finishNode(e,"IfStatement")},l.parseReturnStatement=function(e){return this.allowReturn||this.raise(this.start,"\'return\' outside of function"),this.next(),this.eat(a.semi)||this.insertSemicolon()?e.argument=null:(e.argument=this.parseExpression(),this.semicolon()),this.finishNode(e,"ReturnStatement")},l.parseSwitchStatement=function(e){this.next(),e.discriminant=this.parseParenExpression(),e.cases=[],this.expect(a.braceL),this.labels.push(Et),this.enterScope(qe);for(var t,i=!1;this.type!==a.braceR;)if(this.type===a._case||this.type===a._default){var s=this.type===a._case;t&&this.finishNode(t,"SwitchCase"),e.cases.push(t=this.startNode()),t.consequent=[],this.next(),s?t.test=this.parseExpression():(i&&this.raiseRecoverable(this.lastTokStart,"Multiple default clauses"),i=!0,t.test=null),this.expect(a.colon)}else t||this.unexpected(),t.consequent.push(this.parseStatement(null));return this.exitScope(),t&&this.finishNode(t,"SwitchCase"),this.next(),this.labels.pop(),this.finishNode(e,"SwitchStatement")},l.parseThrowStatement=function(e){return this.next(),k.test(this.input.slice(this.lastTokEnd,this.start))&&this.raise(this.lastTokEnd,"Illegal newline after throw"),e.argument=this.parseExpression(),this.semicolon(),this.finishNode(e,"ThrowStatement")};var It=[];l.parseCatchClauseParam=function(){var e=this.parseBindingAtom(),t=e.type==="Identifier";return this.enterScope(t?Me:0),this.checkLValPattern(e,t?Ge:D),this.expect(a.parenR),e},l.parseTryStatement=function(e){if(this.next(),e.block=this.parseBlock(),e.handler=null,this.type===a._catch){var t=this.startNode();this.next(),this.eat(a.parenL)?t.param=this.parseCatchClauseParam():(this.options.ecmaVersion<10&&this.unexpected(),t.param=null,this.enterScope(0)),t.body=this.parseBlock(!1),this.exitScope(),e.handler=this.finishNode(t,"CatchClause")}return e.finalizer=this.eat(a._finally)?this.parseBlock():null,!e.handler&&!e.finalizer&&this.raise(e.start,"Missing catch or finally clause"),this.finishNode(e,"TryStatement")},l.parseVarStatement=function(e,t,i){return this.next(),this.parseVar(e,!1,t,i),this.semicolon(),this.finishNode(e,"VariableDeclaration")},l.parseWhileStatement=function(e){return this.next(),e.test=this.parseParenExpression(),this.labels.push(we),e.body=this.parseStatement("while"),this.labels.pop(),this.finishNode(e,"WhileStatement")},l.parseWithStatement=function(e){return this.strict&&this.raise(this.start,"\'with\' in strict mode"),this.next(),e.object=this.parseParenExpression(),e.body=this.parseStatement("with"),this.finishNode(e,"WithStatement")},l.parseEmptyStatement=function(e){return this.next(),this.finishNode(e,"EmptyStatement")},l.parseLabeledStatement=function(e,t,i,s){for(var r=0,n=this.labels;r=0;h--){var p=this.labels[h];if(p.statementStart===e.start)p.statementStart=this.start,p.kind=o;else break}return this.labels.push({name:t,kind:o,statementStart:this.start}),e.body=this.parseStatement(s?s.indexOf("label")===-1?s+"label":s:"label"),this.labels.pop(),e.label=i,this.finishNode(e,"LabeledStatement")},l.parseExpressionStatement=function(e,t){return e.expression=t,this.semicolon(),this.finishNode(e,"ExpressionStatement")},l.parseBlock=function(e,t,i){for(e===void 0&&(e=!0),t===void 0&&(t=this.startNode()),t.body=[],this.expect(a.braceL),e&&this.enterScope(0);this.type!==a.braceR;){var s=this.parseStatement(null);t.body.push(s)}return i&&(this.strict=!1),this.next(),e&&this.exitScope(),this.finishNode(t,"BlockStatement")},l.parseFor=function(e,t){return e.init=t,this.expect(a.semi),e.test=this.type===a.semi?null:this.parseExpression(),this.expect(a.semi),e.update=this.type===a.parenR?null:this.parseExpression(),this.expect(a.parenR),e.body=this.parseStatement("for"),this.exitScope(),this.labels.pop(),this.finishNode(e,"ForStatement")},l.parseForIn=function(e,t){var i=this.type===a._in;return this.next(),t.type==="VariableDeclaration"&&t.declarations[0].init!=null&&(!i||this.options.ecmaVersion<8||this.strict||t.kind!=="var"||t.declarations[0].id.type!=="Identifier")&&this.raise(t.start,(i?"for-in":"for-of")+" loop variable declaration may not have an initializer"),e.left=t,e.right=i?this.parseExpression():this.parseMaybeAssign(),this.expect(a.parenR),e.body=this.parseStatement("for"),this.exitScope(),this.labels.pop(),this.finishNode(e,i?"ForInStatement":"ForOfStatement")},l.parseVar=function(e,t,i,s){for(e.declarations=[],e.kind=i;;){var r=this.startNode();if(this.parseVarId(r,i),this.eat(a.eq)?r.init=this.parseMaybeAssign(t):!s&&i==="const"&&!(this.type===a._in||this.options.ecmaVersion>=6&&this.isContextual("of"))?this.unexpected():!s&&(i==="using"||i==="await using")&&this.options.ecmaVersion>=17&&this.type!==a._in&&!this.isContextual("of")?this.raise(this.lastTokEnd,"Missing initializer in "+i+" declaration"):!s&&r.id.type!=="Identifier"&&!(t&&(this.type===a._in||this.isContextual("of")))?this.raise(this.lastTokEnd,"Complex binding patterns require an initialization value"):r.init=null,e.declarations.push(this.finishNode(r,"VariableDeclarator")),!this.eat(a.comma))break}return e},l.parseVarId=function(e,t){e.id=t==="using"||t==="await using"?this.parseIdent():this.parseBindingAtom(),this.checkLValPattern(e.id,t==="var"?ke:D,!1)};var ee=1,Ae=2,We=4;l.parseFunction=function(e,t,i,s,r){this.initFunction(e),(this.options.ecmaVersion>=9||this.options.ecmaVersion>=6&&!s)&&(this.type===a.star&&t&Ae&&this.unexpected(),e.generator=this.eat(a.star)),this.options.ecmaVersion>=8&&(e.async=!!s),t&ee&&(e.id=t&We&&this.type!==a.name?null:this.parseIdent(),e.id&&!(t&Ae)&&this.checkLValSimple(e.id,this.strict||e.generator||e.async?this.treatFunctionsAsVar?ke:D:je));var n=this.yieldPos,u=this.awaitPos,o=this.awaitIdentPos;return this.yieldPos=0,this.awaitPos=0,this.awaitIdentPos=0,this.enterScope(Se(e.async,e.generator)),t&ee||(e.id=this.type===a.name?this.parseIdent():null),this.parseFunctionParams(e),this.parseFunctionBody(e,i,!1,r),this.yieldPos=n,this.awaitPos=u,this.awaitIdentPos=o,this.finishNode(e,t&ee?"FunctionDeclaration":"FunctionExpression")},l.parseFunctionParams=function(e){this.expect(a.parenL),e.params=this.parseBindingList(a.parenR,!1,this.options.ecmaVersion>=8),this.checkYieldAwaitInDefaultParams()},l.parseClass=function(e,t){this.next();var i=this.strict;this.strict=!0,this.parseClassId(e,t),this.parseClassSuper(e);var s=this.enterClassBody(),r=this.startNode(),n=!1;for(r.body=[],this.expect(a.braceL);this.type!==a.braceR;){var u=this.parseClassElement(e.superClass!==null);u&&(r.body.push(u),u.type==="MethodDefinition"&&u.kind==="constructor"?(n&&this.raiseRecoverable(u.start,"Duplicate constructor in the same class"),n=!0):u.key&&u.key.type==="PrivateIdentifier"&&Pt(s,u)&&this.raiseRecoverable(u.key.start,"Identifier \'#"+u.key.name+"\' has already been declared"))}return this.strict=i,this.next(),e.body=this.finishNode(r,"ClassBody"),this.exitClassBody(),this.finishNode(e,t?"ClassDeclaration":"ClassExpression")},l.parseClassElement=function(e){if(this.eat(a.semi))return null;var t=this.options.ecmaVersion,i=this.startNode(),s="",r=!1,n=!1,u="method",o=!1;if(this.eatContextual("static")){if(t>=13&&this.eat(a.braceL))return this.parseClassStaticBlock(i),i;this.isClassElementNameStart()||this.type===a.star?o=!0:s="static"}if(i.static=o,!s&&t>=8&&this.eatContextual("async")&&((this.isClassElementNameStart()||this.type===a.star)&&!this.canInsertSemicolon()?n=!0:s="async"),!s&&(t>=9||!n)&&this.eat(a.star)&&(r=!0),!s&&!n&&!r){var h=this.value;(this.eatContextual("get")||this.eatContextual("set"))&&(this.isClassElementNameStart()?u=h:s=h)}if(s?(i.computed=!1,i.key=this.startNodeAt(this.lastTokStart,this.lastTokStartLoc),i.key.name=s,this.finishNode(i.key,"Identifier")):this.parseClassElementName(i),t<13||this.type===a.parenL||u!=="method"||r||n){var p=!i.static&&ce(i,"constructor"),d=p&&e;p&&u!=="method"&&this.raise(i.key.start,"Constructor can\'t have get/set modifier"),i.kind=p?"constructor":u,this.parseClassMethod(i,r,n,d)}else this.parseClassField(i);return i},l.isClassElementNameStart=function(){return this.type===a.name||this.type===a.privateId||this.type===a.num||this.type===a.string||this.type===a.bracketL||this.type.keyword},l.parseClassElementName=function(e){this.type===a.privateId?(this.value==="constructor"&&this.raise(this.start,"Classes can\'t have an element named \'#constructor\'"),e.computed=!1,e.key=this.parsePrivateIdent()):this.parsePropertyName(e)},l.parseClassMethod=function(e,t,i,s){var r=e.key;e.kind==="constructor"?(t&&this.raise(r.start,"Constructor can\'t be a generator"),i&&this.raise(r.start,"Constructor can\'t be an async method")):e.static&&ce(e,"prototype")&&this.raise(r.start,"Classes may not have a static property named prototype");var n=e.value=this.parseMethod(t,i,s);return e.kind==="get"&&n.params.length!==0&&this.raiseRecoverable(n.start,"getter should have no params"),e.kind==="set"&&n.params.length!==1&&this.raiseRecoverable(n.start,"setter should have exactly one param"),e.kind==="set"&&n.params[0].type==="RestElement"&&this.raiseRecoverable(n.params[0].start,"Setter cannot use rest params"),this.finishNode(e,"MethodDefinition")},l.parseClassField=function(e){return ce(e,"constructor")?this.raise(e.key.start,"Classes can\'t have a field named \'constructor\'"):e.static&&ce(e,"prototype")&&this.raise(e.key.start,"Classes can\'t have a static field named \'prototype\'"),this.eat(a.eq)?(this.enterScope($|ne),e.value=this.parseMaybeAssign(),this.exitScope()):e.value=null,this.semicolon(),this.finishNode(e,"PropertyDefinition")},l.parseClassStaticBlock=function(e){e.body=[];var t=this.labels;for(this.labels=[],this.enterScope(H|ne);this.type!==a.braceR;){var i=this.parseStatement(null);e.body.push(i)}return this.next(),this.exitScope(),this.labels=t,this.finishNode(e,"StaticBlock")},l.parseClassId=function(e,t){this.type===a.name?(e.id=this.parseIdent(),t&&this.checkLValSimple(e.id,D,!1)):(t===!0&&this.unexpected(),e.id=null)},l.parseClassSuper=function(e){e.superClass=this.eat(a._extends)?this.parseExprSubscripts(null,!1):null},l.enterClassBody=function(){var e={declared:Object.create(null),used:[]};return this.privateNameStack.push(e),e.declared},l.exitClassBody=function(){var e=this.privateNameStack.pop(),t=e.declared,i=e.used;if(this.options.checkPrivateFields)for(var s=this.privateNameStack.length,r=s===0?null:this.privateNameStack[s-1],n=0;n=11&&(this.eatContextual("as")?(e.exported=this.parseModuleExportName(),this.checkExport(t,e.exported,this.lastTokStart)):e.exported=null),this.expectContextual("from"),this.type!==a.string&&this.unexpected(),e.source=this.parseExprAtom(),this.options.ecmaVersion>=16&&(e.attributes=this.parseWithClause()),this.semicolon(),this.finishNode(e,"ExportAllDeclaration")},l.parseExport=function(e,t){if(this.next(),this.eat(a.star))return this.parseExportAllDeclaration(e,t);if(this.eat(a._default))return this.checkExport(t,"default",this.lastTokStart),e.declaration=this.parseExportDefaultDeclaration(),this.finishNode(e,"ExportDefaultDeclaration");if(this.shouldParseExportStatement())e.declaration=this.parseExportDeclaration(e),e.declaration.type==="VariableDeclaration"?this.checkVariableExport(t,e.declaration.declarations):this.checkExport(t,e.declaration.id,e.declaration.id.start),e.specifiers=[],e.source=null,this.options.ecmaVersion>=16&&(e.attributes=[]);else{if(e.declaration=null,e.specifiers=this.parseExportSpecifiers(t),this.eatContextual("from"))this.type!==a.string&&this.unexpected(),e.source=this.parseExprAtom(),this.options.ecmaVersion>=16&&(e.attributes=this.parseWithClause());else{for(var i=0,s=e.specifiers;i=16&&(e.attributes=[])}this.semicolon()}return this.finishNode(e,"ExportNamedDeclaration")},l.parseExportDeclaration=function(e){return this.parseStatement(null)},l.parseExportDefaultDeclaration=function(){var e;if(this.type===a._function||(e=this.isAsyncFunction())){var t=this.startNode();return this.next(),e&&this.next(),this.parseFunction(t,ee|We,!1,e)}else if(this.type===a._class){var i=this.startNode();return this.parseClass(i,"nullableID")}else{var s=this.parseMaybeAssign();return this.semicolon(),s}},l.checkExport=function(e,t,i){e&&(typeof t!="string"&&(t=t.type==="Identifier"?t.name:t.value),W(e,t)&&this.raiseRecoverable(i,"Duplicate export \'"+t+"\'"),e[t]=!0)},l.checkPatternExport=function(e,t){var i=t.type;if(i==="Identifier")this.checkExport(e,t,t.start);else if(i==="ObjectPattern")for(var s=0,r=t.properties;s=16&&(e.attributes=this.parseWithClause()),this.semicolon(),this.finishNode(e,"ImportDeclaration")},l.parseImportSpecifier=function(){var e=this.startNode();return e.imported=this.parseModuleExportName(),this.eatContextual("as")?e.local=this.parseIdent():(this.checkUnreserved(e.imported),e.local=e.imported),this.checkLValSimple(e.local,D),this.finishNode(e,"ImportSpecifier")},l.parseImportDefaultSpecifier=function(){var e=this.startNode();return e.local=this.parseIdent(),this.checkLValSimple(e.local,D),this.finishNode(e,"ImportDefaultSpecifier")},l.parseImportNamespaceSpecifier=function(){var e=this.startNode();return this.next(),this.expectContextual("as"),e.local=this.parseIdent(),this.checkLValSimple(e.local,D),this.finishNode(e,"ImportNamespaceSpecifier")},l.parseImportSpecifiers=function(){var e=[],t=!0;if(this.type===a.name&&(e.push(this.parseImportDefaultSpecifier()),!this.eat(a.comma)))return e;if(this.type===a.star)return e.push(this.parseImportNamespaceSpecifier()),e;for(this.expect(a.braceL);!this.eat(a.braceR);){if(t)t=!1;else if(this.expect(a.comma),this.afterTrailingComma(a.braceR))break;e.push(this.parseImportSpecifier())}return e},l.parseWithClause=function(){var e=[];if(!this.eat(a._with))return e;this.expect(a.braceL);for(var t={},i=!0;!this.eat(a.braceR);){if(i)i=!1;else if(this.expect(a.comma),this.afterTrailingComma(a.braceR))break;var s=this.parseImportAttribute(),r=s.key.type==="Identifier"?s.key.name:s.key.value;W(t,r)&&this.raiseRecoverable(s.key.start,"Duplicate attribute key \'"+r+"\'"),t[r]=!0,e.push(s)}return e},l.parseImportAttribute=function(){var e=this.startNode();return e.key=this.type===a.string?this.parseExprAtom():this.parseIdent(this.options.allowReserved!=="never"),this.expect(a.colon),this.type!==a.string&&this.unexpected(),e.value=this.parseExprAtom(),this.finishNode(e,"ImportAttribute")},l.parseModuleExportName=function(){if(this.options.ecmaVersion>=13&&this.type===a.string){var e=this.parseLiteral(this.value);return St.test(e.value)&&this.raise(e.start,"An export name cannot include a lone surrogate."),e}return this.parseIdent(!0)},l.adaptDirectivePrologue=function(e){for(var t=0;t=5&&e.type==="ExpressionStatement"&&e.expression.type==="Literal"&&typeof e.expression.value=="string"&&(this.input[e.start]===\'"\'||this.input[e.start]==="\'")};var N=C.prototype;N.toAssignable=function(e,t,i){if(this.options.ecmaVersion>=6&&e)switch(e.type){case"Identifier":this.inAsync&&e.name==="await"&&this.raise(e.start,"Cannot use \'await\' as identifier inside an async function");break;case"ObjectPattern":case"ArrayPattern":case"AssignmentPattern":case"RestElement":break;case"ObjectExpression":e.type="ObjectPattern",i&&this.checkPatternErrors(i,!0);for(var s=0,r=e.properties;s=6)switch(this.type){case a.bracketL:var e=this.startNode();return this.next(),e.elements=this.parseBindingList(a.bracketR,!0,!0),this.finishNode(e,"ArrayPattern");case a.braceL:return this.parseObj(!0)}return this.parseIdent()},N.parseBindingList=function(e,t,i,s){for(var r=[],n=!0;!this.eat(e);)if(n?n=!1:this.expect(a.comma),t&&this.type===a.comma)r.push(null);else{if(i&&this.afterTrailingComma(e))break;if(this.type===a.ellipsis){var u=this.parseRestBinding();this.parseBindingListItem(u),r.push(u),this.type===a.comma&&this.raiseRecoverable(this.start,"Comma is not permitted after the rest element"),this.expect(e);break}else r.push(this.parseAssignableListItem(s))}return r},N.parseAssignableListItem=function(e){var t=this.parseMaybeDefault(this.start,this.startLoc);return this.parseBindingListItem(t),t},N.parseBindingListItem=function(e){return e},N.parseMaybeDefault=function(e,t,i){if(i=i||this.parseBindingAtom(),this.options.ecmaVersion<6||!this.eat(a.eq))return i;var s=this.startNodeAt(e,t);return s.left=i,s.right=this.parseMaybeAssign(),this.finishNode(s,"AssignmentPattern")},N.checkLValSimple=function(e,t,i){t===void 0&&(t=oe);var s=t!==oe;switch(e.type){case"Identifier":this.strict&&this.reservedWordsStrictBind.test(e.name)&&this.raiseRecoverable(e.start,(s?"Binding ":"Assigning to ")+e.name+" in strict mode"),s&&(t===D&&e.name==="let"&&this.raiseRecoverable(e.start,"let is disallowed as a lexically bound name"),i&&(W(i,e.name)&&this.raiseRecoverable(e.start,"Argument name clash"),i[e.name]=!0),t!==He&&this.declareName(e.name,t,e.start));break;case"ChainExpression":this.raiseRecoverable(e.start,"Optional chaining cannot appear in left-hand side");break;case"MemberExpression":s&&this.raiseRecoverable(e.start,"Binding member expression");break;case"ParenthesizedExpression":return s&&this.raiseRecoverable(e.start,"Binding parenthesized expression"),this.checkLValSimple(e.expression,t,i);default:this.raise(e.start,(s?"Binding":"Assigning to")+" rvalue")}},N.checkLValPattern=function(e,t,i){switch(t===void 0&&(t=oe),e.type){case"ObjectPattern":for(var s=0,r=e.properties;s=1;e--){var t=this.context[e];if(t.token==="function")return t.generator}return!1},K.updateContext=function(e){var t,i=this.type;i.keyword&&e===a.dot?this.exprAllowed=!1:(t=i.updateContext)?t.call(this,e):this.exprAllowed=i.beforeExpr},K.overrideContext=function(e){this.curContext()!==e&&(this.context[this.context.length-1]=e)},a.parenR.updateContext=a.braceR.updateContext=function(){if(this.context.length===1){this.exprAllowed=!0;return}var e=this.context.pop();e===b.b_stat&&this.curContext().token==="function"&&(e=this.context.pop()),this.exprAllowed=!e.isExpr},a.braceL.updateContext=function(e){this.context.push(this.braceIsBlock(e)?b.b_stat:b.b_expr),this.exprAllowed=!0},a.dollarBraceL.updateContext=function(){this.context.push(b.b_tmpl),this.exprAllowed=!0},a.parenL.updateContext=function(e){var t=e===a._if||e===a._for||e===a._with||e===a._while;this.context.push(t?b.p_stat:b.p_expr),this.exprAllowed=!0},a.incDec.updateContext=function(){},a._function.updateContext=a._class.updateContext=function(e){e.beforeExpr&&e!==a._else&&!(e===a.semi&&this.curContext()!==b.p_stat)&&!(e===a._return&&k.test(this.input.slice(this.lastTokEnd,this.start)))&&!((e===a.colon||e===a.braceL)&&this.curContext()===b.b_stat)?this.context.push(b.f_expr):this.context.push(b.f_stat),this.exprAllowed=!1},a.colon.updateContext=function(){this.curContext().token==="function"&&this.context.pop(),this.exprAllowed=!0},a.backQuote.updateContext=function(){this.curContext()===b.q_tmpl?this.context.pop():this.context.push(b.q_tmpl),this.exprAllowed=!1},a.star.updateContext=function(e){if(e===a._function){var t=this.context.length-1;this.context[t]===b.f_expr?this.context[t]=b.f_expr_gen:this.context[t]=b.f_gen}this.exprAllowed=!0},a.name.updateContext=function(e){var t=!1;this.options.ecmaVersion>=6&&e!==a.dot&&(this.value==="of"&&!this.exprAllowed||this.value==="yield"&&this.inGeneratorContext())&&(t=!0),this.exprAllowed=t};var f=C.prototype;f.checkPropClash=function(e,t,i){if(!(this.options.ecmaVersion>=9&&e.type==="SpreadElement")&&!(this.options.ecmaVersion>=6&&(e.computed||e.method||e.shorthand))){var s=e.key,r;switch(s.type){case"Identifier":r=s.name;break;case"Literal":r=String(s.value);break;default:return}var n=e.kind;if(this.options.ecmaVersion>=6){r==="__proto__"&&n==="init"&&(t.proto&&(i?i.doubleProto<0&&(i.doubleProto=s.start):this.raiseRecoverable(s.start,"Redefinition of __proto__ property")),t.proto=!0);return}r="$"+r;var u=t[r];if(u){var o;n==="init"?o=this.strict&&u.init||u.get||u.set:o=u.init||u[n],o&&this.raiseRecoverable(s.start,"Redefinition of property")}else u=t[r]={init:!1,get:!1,set:!1};u[n]=!0}},f.parseExpression=function(e,t){var i=this.start,s=this.startLoc,r=this.parseMaybeAssign(e,t);if(this.type===a.comma){var n=this.startNodeAt(i,s);for(n.expressions=[r];this.eat(a.comma);)n.expressions.push(this.parseMaybeAssign(e,t));return this.finishNode(n,"SequenceExpression")}return r},f.parseMaybeAssign=function(e,t,i){if(this.isContextual("yield")){if(this.inGenerator)return this.parseYield(e);this.exprAllowed=!1}var s=!1,r=-1,n=-1,u=-1;t?(r=t.parenthesizedAssign,n=t.trailingComma,u=t.doubleProto,t.parenthesizedAssign=t.trailingComma=-1):(t=new he,s=!0);var o=this.start,h=this.startLoc;(this.type===a.parenL||this.type===a.name)&&(this.potentialArrowAt=this.start,this.potentialArrowInForAwait=e==="await");var p=this.parseMaybeConditional(e,t);if(i&&(p=i.call(this,p,o,h)),this.type.isAssign){var d=this.startNodeAt(o,h);return d.operator=this.value,this.type===a.eq&&(p=this.toAssignable(p,!1,t)),s||(t.parenthesizedAssign=t.trailingComma=t.doubleProto=-1),t.shorthandAssign>=p.start&&(t.shorthandAssign=-1),this.type===a.eq?this.checkLValPattern(p):this.checkLValSimple(p),d.left=p,this.next(),d.right=this.parseMaybeAssign(e),u>-1&&(t.doubleProto=u),this.finishNode(d,"AssignmentExpression")}else s&&this.checkExpressionErrors(t,!0);return r>-1&&(t.parenthesizedAssign=r),n>-1&&(t.trailingComma=n),p},f.parseMaybeConditional=function(e,t){var i=this.start,s=this.startLoc,r=this.parseExprOps(e,t);if(this.checkExpressionErrors(t))return r;if(this.eat(a.question)){var n=this.startNodeAt(i,s);return n.test=r,n.consequent=this.parseMaybeAssign(),this.expect(a.colon),n.alternate=this.parseMaybeAssign(e),this.finishNode(n,"ConditionalExpression")}return r},f.parseExprOps=function(e,t){var i=this.start,s=this.startLoc,r=this.parseMaybeUnary(t,!1,!1,e);return this.checkExpressionErrors(t)||r.start===i&&r.type==="ArrowFunctionExpression"?r:this.parseExprOp(r,i,s,-1,e)},f.parseExprOp=function(e,t,i,s,r){var n=this.type.binop;if(n!=null&&(!r||this.type!==a._in)&&n>s){var u=this.type===a.logicalOR||this.type===a.logicalAND,o=this.type===a.coalesce;o&&(n=a.logicalAND.binop);var h=this.value;this.next();var p=this.start,d=this.startLoc,y=this.parseExprOp(this.parseMaybeUnary(null,!1,!1,r),p,d,n,r),S=this.buildBinary(t,i,e,y,h,u||o);return(u&&this.type===a.coalesce||o&&(this.type===a.logicalOR||this.type===a.logicalAND))&&this.raiseRecoverable(this.start,"Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses"),this.parseExprOp(S,t,i,s,r)}return e},f.buildBinary=function(e,t,i,s,r,n){s.type==="PrivateIdentifier"&&this.raise(s.start,"Private identifier can only be left side of binary expression");var u=this.startNodeAt(e,t);return u.left=i,u.operator=r,u.right=s,this.finishNode(u,n?"LogicalExpression":"BinaryExpression")},f.parseMaybeUnary=function(e,t,i,s){var r=this.start,n=this.startLoc,u;if(this.isContextual("await")&&this.canAwait)u=this.parseAwait(s),t=!0;else if(this.type.prefix){var o=this.startNode(),h=this.type===a.incDec;o.operator=this.value,o.prefix=!0,this.next(),o.argument=this.parseMaybeUnary(null,!0,h,s),this.checkExpressionErrors(e,!0),h?this.checkLValSimple(o.argument):this.strict&&o.operator==="delete"&&ze(o.argument)?this.raiseRecoverable(o.start,"Deleting local variable in strict mode"):o.operator==="delete"&&Ee(o.argument)?this.raiseRecoverable(o.start,"Private fields can not be deleted"):t=!0,u=this.finishNode(o,h?"UpdateExpression":"UnaryExpression")}else if(!t&&this.type===a.privateId)(s||this.privateNameStack.length===0)&&this.options.checkPrivateFields&&this.unexpected(),u=this.parsePrivateIdent(),this.type!==a._in&&this.unexpected();else{if(u=this.parseExprSubscripts(e,s),this.checkExpressionErrors(e))return u;for(;this.type.postfix&&!this.canInsertSemicolon();){var p=this.startNodeAt(r,n);p.operator=this.value,p.prefix=!1,p.argument=u,this.checkLValSimple(u),this.next(),u=this.finishNode(p,"UpdateExpression")}}if(!i&&this.eat(a.starstar))if(t)this.unexpected(this.lastTokStart);else return this.buildBinary(r,n,u,this.parseMaybeUnary(null,!1,!1,s),"**",!1);else return u};function ze(e){return e.type==="Identifier"||e.type==="ParenthesizedExpression"&&ze(e.expression)}function Ee(e){return e.type==="MemberExpression"&&e.property.type==="PrivateIdentifier"||e.type==="ChainExpression"&&Ee(e.expression)||e.type==="ParenthesizedExpression"&&Ee(e.expression)}f.parseExprSubscripts=function(e,t){var i=this.start,s=this.startLoc,r=this.parseExprAtom(e,t);if(r.type==="ArrowFunctionExpression"&&this.input.slice(this.lastTokStart,this.lastTokEnd)!==")")return r;var n=this.parseSubscripts(r,i,s,!1,t);return e&&n.type==="MemberExpression"&&(e.parenthesizedAssign>=n.start&&(e.parenthesizedAssign=-1),e.parenthesizedBind>=n.start&&(e.parenthesizedBind=-1),e.trailingComma>=n.start&&(e.trailingComma=-1)),n},f.parseSubscripts=function(e,t,i,s,r){for(var n=this.options.ecmaVersion>=8&&e.type==="Identifier"&&e.name==="async"&&this.lastTokEnd===e.end&&!this.canInsertSemicolon()&&e.end-e.start===5&&this.potentialArrowAt===e.start,u=!1;;){var o=this.parseSubscript(e,t,i,s,n,u,r);if(o.optional&&(u=!0),o===e||o.type==="ArrowFunctionExpression"){if(u){var h=this.startNodeAt(t,i);h.expression=o,o=this.finishNode(h,"ChainExpression")}return o}e=o}},f.shouldParseAsyncArrow=function(){return!this.canInsertSemicolon()&&this.eat(a.arrow)},f.parseSubscriptAsyncArrow=function(e,t,i,s){return this.parseArrowExpression(this.startNodeAt(e,t),i,!0,s)},f.parseSubscript=function(e,t,i,s,r,n,u){var o=this.options.ecmaVersion>=11,h=o&&this.eat(a.questionDot);s&&h&&this.raise(this.lastTokStart,"Optional chaining cannot appear in the callee of new expressions");var p=this.eat(a.bracketL);if(p||h&&this.type!==a.parenL&&this.type!==a.backQuote||this.eat(a.dot)){var d=this.startNodeAt(t,i);d.object=e,p?(d.property=this.parseExpression(),this.expect(a.bracketR)):this.type===a.privateId&&e.type!=="Super"?d.property=this.parsePrivateIdent():d.property=this.parseIdent(this.options.allowReserved!=="never"),d.computed=!!p,o&&(d.optional=h),e=this.finishNode(d,"MemberExpression")}else if(!s&&this.eat(a.parenL)){var y=new he,S=this.yieldPos,se=this.awaitPos,Q=this.awaitIdentPos;this.yieldPos=0,this.awaitPos=0,this.awaitIdentPos=0;var xe=this.parseExprList(a.parenR,this.options.ecmaVersion>=8,!1,y);if(r&&!h&&this.shouldParseAsyncArrow())return this.checkPatternErrors(y,!1),this.checkYieldAwaitInDefaultParams(),this.awaitIdentPos>0&&this.raise(this.awaitIdentPos,"Cannot use \'await\' as identifier inside an async function"),this.yieldPos=S,this.awaitPos=se,this.awaitIdentPos=Q,this.parseSubscriptAsyncArrow(t,i,xe,u);this.checkExpressionErrors(y,!0),this.yieldPos=S||this.yieldPos,this.awaitPos=se||this.awaitPos,this.awaitIdentPos=Q||this.awaitIdentPos;var Y=this.startNodeAt(t,i);Y.callee=e,Y.arguments=xe,o&&(Y.optional=h),e=this.finishNode(Y,"CallExpression")}else if(this.type===a.backQuote){(h||n)&&this.raise(this.start,"Optional chaining cannot appear in the tag of tagged template expressions");var X=this.startNodeAt(t,i);X.tag=e,X.quasi=this.parseTemplate({isTagged:!0}),e=this.finishNode(X,"TaggedTemplateExpression")}return e},f.parseExprAtom=function(e,t,i){this.type===a.slash&&this.readRegexp();var s,r=this.potentialArrowAt===this.start;switch(this.type){case a._super:return this.allowSuper||this.raise(this.start,"\'super\' keyword outside a method"),s=this.startNode(),this.next(),this.type===a.parenL&&!this.allowDirectSuper&&this.raise(s.start,"super() call outside constructor of a subclass"),this.type!==a.dot&&this.type!==a.bracketL&&this.type!==a.parenL&&this.unexpected(),this.finishNode(s,"Super");case a._this:return s=this.startNode(),this.next(),this.finishNode(s,"ThisExpression");case a.name:var n=this.start,u=this.startLoc,o=this.containsEsc,h=this.parseIdent(!1);if(this.options.ecmaVersion>=8&&!o&&h.name==="async"&&!this.canInsertSemicolon()&&this.eat(a._function))return this.overrideContext(b.f_expr),this.parseFunction(this.startNodeAt(n,u),0,!1,!0,t);if(r&&!this.canInsertSemicolon()){if(this.eat(a.arrow))return this.parseArrowExpression(this.startNodeAt(n,u),[h],!1,t);if(this.options.ecmaVersion>=8&&h.name==="async"&&this.type===a.name&&!o&&(!this.potentialArrowInForAwait||this.value!=="of"||this.containsEsc))return h=this.parseIdent(!1),(this.canInsertSemicolon()||!this.eat(a.arrow))&&this.unexpected(),this.parseArrowExpression(this.startNodeAt(n,u),[h],!0,t)}return h;case a.regexp:var p=this.value;return s=this.parseLiteral(p.value),s.regex={pattern:p.pattern,flags:p.flags},s;case a.num:case a.string:return this.parseLiteral(this.value);case a._null:case a._true:case a._false:return s=this.startNode(),s.value=this.type===a._null?null:this.type===a._true,s.raw=this.type.keyword,this.next(),this.finishNode(s,"Literal");case a.parenL:var d=this.start,y=this.parseParenAndDistinguishExpression(r,t);return e&&(e.parenthesizedAssign<0&&!this.isSimpleAssignTarget(y)&&(e.parenthesizedAssign=d),e.parenthesizedBind<0&&(e.parenthesizedBind=d)),y;case a.bracketL:return s=this.startNode(),this.next(),s.elements=this.parseExprList(a.bracketR,!0,!0,e),this.finishNode(s,"ArrayExpression");case a.braceL:return this.overrideContext(b.b_expr),this.parseObj(!1,e);case a._function:return s=this.startNode(),this.next(),this.parseFunction(s,0);case a._class:return this.parseClass(this.startNode(),!1);case a._new:return this.parseNew();case a.backQuote:return this.parseTemplate();case a._import:return this.options.ecmaVersion>=11?this.parseExprImport(i):this.unexpected();default:return this.parseExprAtomDefault()}},f.parseExprAtomDefault=function(){this.unexpected()},f.parseExprImport=function(e){var t=this.startNode();if(this.containsEsc&&this.raiseRecoverable(this.start,"Escape sequence in keyword import"),this.next(),this.type===a.parenL&&!e)return this.parseDynamicImport(t);if(this.type===a.dot){var i=this.startNodeAt(t.start,t.loc&&t.loc.start);return i.name="import",t.meta=this.finishNode(i,"Identifier"),this.parseImportMeta(t)}else this.unexpected()},f.parseDynamicImport=function(e){if(this.next(),e.source=this.parseMaybeAssign(),this.options.ecmaVersion>=16)this.eat(a.parenR)?e.options=null:(this.expect(a.comma),this.afterTrailingComma(a.parenR)?e.options=null:(e.options=this.parseMaybeAssign(),this.eat(a.parenR)||(this.expect(a.comma),this.afterTrailingComma(a.parenR)||this.unexpected())));else if(!this.eat(a.parenR)){var t=this.start;this.eat(a.comma)&&this.eat(a.parenR)?this.raiseRecoverable(t,"Trailing comma is not allowed in import()"):this.unexpected(t)}return this.finishNode(e,"ImportExpression")},f.parseImportMeta=function(e){this.next();var t=this.containsEsc;return e.property=this.parseIdent(!0),e.property.name!=="meta"&&this.raiseRecoverable(e.property.start,"The only valid meta property for import is \'import.meta\'"),t&&this.raiseRecoverable(e.start,"\'import.meta\' must not contain escaped characters"),this.options.sourceType!=="module"&&!this.options.allowImportExportEverywhere&&this.raiseRecoverable(e.start,"Cannot use \'import.meta\' outside a module"),this.finishNode(e,"MetaProperty")},f.parseLiteral=function(e){var t=this.startNode();return t.value=e,t.raw=this.input.slice(this.start,this.end),t.raw.charCodeAt(t.raw.length-1)===110&&(t.bigint=t.value!=null?t.value.toString():t.raw.slice(0,-1).replace(/_/g,"")),this.next(),this.finishNode(t,"Literal")},f.parseParenExpression=function(){this.expect(a.parenL);var e=this.parseExpression();return this.expect(a.parenR),e},f.shouldParseArrow=function(e){return!this.canInsertSemicolon()},f.parseParenAndDistinguishExpression=function(e,t){var i=this.start,s=this.startLoc,r,n=this.options.ecmaVersion>=8;if(this.options.ecmaVersion>=6){this.next();var u=this.start,o=this.startLoc,h=[],p=!0,d=!1,y=new he,S=this.yieldPos,se=this.awaitPos,Q;for(this.yieldPos=0,this.awaitPos=0;this.type!==a.parenR;)if(p?p=!1:this.expect(a.comma),n&&this.afterTrailingComma(a.parenR,!0)){d=!0;break}else if(this.type===a.ellipsis){Q=this.start,h.push(this.parseParenItem(this.parseRestBinding())),this.type===a.comma&&this.raiseRecoverable(this.start,"Comma is not permitted after the rest element");break}else h.push(this.parseMaybeAssign(!1,y,this.parseParenItem));var xe=this.lastTokEnd,Y=this.lastTokEndLoc;if(this.expect(a.parenR),e&&this.shouldParseArrow(h)&&this.eat(a.arrow))return this.checkPatternErrors(y,!1),this.checkYieldAwaitInDefaultParams(),this.yieldPos=S,this.awaitPos=se,this.parseParenArrowList(i,s,h,t);(!h.length||d)&&this.unexpected(this.lastTokStart),Q&&this.unexpected(Q),this.checkExpressionErrors(y,!0),this.yieldPos=S||this.yieldPos,this.awaitPos=se||this.awaitPos,h.length>1?(r=this.startNodeAt(u,o),r.expressions=h,this.finishNodeAt(r,"SequenceExpression",xe,Y)):r=h[0]}else r=this.parseParenExpression();if(this.options.preserveParens){var X=this.startNodeAt(i,s);return X.expression=r,this.finishNode(X,"ParenthesizedExpression")}else return r},f.parseParenItem=function(e){return e},f.parseParenArrowList=function(e,t,i,s){return this.parseArrowExpression(this.startNodeAt(e,t),i,!1,s)};var Nt=[];f.parseNew=function(){this.containsEsc&&this.raiseRecoverable(this.start,"Escape sequence in keyword new");var e=this.startNode();if(this.next(),this.options.ecmaVersion>=6&&this.type===a.dot){var t=this.startNodeAt(e.start,e.loc&&e.loc.start);t.name="new",e.meta=this.finishNode(t,"Identifier"),this.next();var i=this.containsEsc;return e.property=this.parseIdent(!0),e.property.name!=="target"&&this.raiseRecoverable(e.property.start,"The only valid meta property for new is \'new.target\'"),i&&this.raiseRecoverable(e.start,"\'new.target\' must not contain escaped characters"),this.allowNewDotTarget||this.raiseRecoverable(e.start,"\'new.target\' can only be used in functions and class static block"),this.finishNode(e,"MetaProperty")}var s=this.start,r=this.startLoc;return e.callee=this.parseSubscripts(this.parseExprAtom(null,!1,!0),s,r,!0,!1),this.eat(a.parenL)?e.arguments=this.parseExprList(a.parenR,this.options.ecmaVersion>=8,!1):e.arguments=Nt,this.finishNode(e,"NewExpression")},f.parseTemplateElement=function(e){var t=e.isTagged,i=this.startNode();return this.type===a.invalidTemplate?(t||this.raiseRecoverable(this.start,"Bad escape sequence in untagged template literal"),i.value={raw:this.value.replace(/\\r\\n?/g,`\n`),cooked:null}):i.value={raw:this.input.slice(this.start,this.end).replace(/\\r\\n?/g,`\n`),cooked:this.value},this.next(),i.tail=this.type===a.backQuote,this.finishNode(i,"TemplateElement")},f.parseTemplate=function(e){e===void 0&&(e={});var t=e.isTagged;t===void 0&&(t=!1);var i=this.startNode();this.next(),i.expressions=[];var s=this.parseTemplateElement({isTagged:t});for(i.quasis=[s];!s.tail;)this.type===a.eof&&this.raise(this.pos,"Unterminated template literal"),this.expect(a.dollarBraceL),i.expressions.push(this.parseExpression()),this.expect(a.braceR),i.quasis.push(s=this.parseTemplateElement({isTagged:t}));return this.next(),this.finishNode(i,"TemplateLiteral")},f.isAsyncProp=function(e){return!e.computed&&e.key.type==="Identifier"&&e.key.name==="async"&&(this.type===a.name||this.type===a.num||this.type===a.string||this.type===a.bracketL||this.type.keyword||this.options.ecmaVersion>=9&&this.type===a.star)&&!k.test(this.input.slice(this.lastTokEnd,this.start))},f.parseObj=function(e,t){var i=this.startNode(),s=!0,r={};for(i.properties=[],this.next();!this.eat(a.braceR);){if(s)s=!1;else if(this.expect(a.comma),this.options.ecmaVersion>=5&&this.afterTrailingComma(a.braceR))break;var n=this.parseProperty(e,t);e||this.checkPropClash(n,r,t),i.properties.push(n)}return this.finishNode(i,e?"ObjectPattern":"ObjectExpression")},f.parseProperty=function(e,t){var i=this.startNode(),s,r,n,u;if(this.options.ecmaVersion>=9&&this.eat(a.ellipsis))return e?(i.argument=this.parseIdent(!1),this.type===a.comma&&this.raiseRecoverable(this.start,"Comma is not permitted after the rest element"),this.finishNode(i,"RestElement")):(i.argument=this.parseMaybeAssign(!1,t),this.type===a.comma&&t&&t.trailingComma<0&&(t.trailingComma=this.start),this.finishNode(i,"SpreadElement"));this.options.ecmaVersion>=6&&(i.method=!1,i.shorthand=!1,(e||t)&&(n=this.start,u=this.startLoc),e||(s=this.eat(a.star)));var o=this.containsEsc;return this.parsePropertyName(i),!e&&!o&&this.options.ecmaVersion>=8&&!s&&this.isAsyncProp(i)?(r=!0,s=this.options.ecmaVersion>=9&&this.eat(a.star),this.parsePropertyName(i)):r=!1,this.parsePropertyValue(i,e,s,r,n,u,t,o),this.finishNode(i,"Property")},f.parseGetterSetter=function(e){var t=e.key.name;this.parsePropertyName(e),e.value=this.parseMethod(!1),e.kind=t;var i=e.kind==="get"?0:1;if(e.value.params.length!==i){var s=e.value.start;e.kind==="get"?this.raiseRecoverable(s,"getter should have no params"):this.raiseRecoverable(s,"setter should have exactly one param")}else e.kind==="set"&&e.value.params[0].type==="RestElement"&&this.raiseRecoverable(e.value.params[0].start,"Setter cannot use rest params")},f.parsePropertyValue=function(e,t,i,s,r,n,u,o){(i||s)&&this.type===a.colon&&this.unexpected(),this.eat(a.colon)?(e.value=t?this.parseMaybeDefault(this.start,this.startLoc):this.parseMaybeAssign(!1,u),e.kind="init"):this.options.ecmaVersion>=6&&this.type===a.parenL?(t&&this.unexpected(),e.method=!0,e.value=this.parseMethod(i,s),e.kind="init"):!t&&!o&&this.options.ecmaVersion>=5&&!e.computed&&e.key.type==="Identifier"&&(e.key.name==="get"||e.key.name==="set")&&this.type!==a.comma&&this.type!==a.braceR&&this.type!==a.eq?((i||s)&&this.unexpected(),this.parseGetterSetter(e)):this.options.ecmaVersion>=6&&!e.computed&&e.key.type==="Identifier"?((i||s)&&this.unexpected(),this.checkUnreserved(e.key),e.key.name==="await"&&!this.awaitIdentPos&&(this.awaitIdentPos=r),t?e.value=this.parseMaybeDefault(r,n,this.copyNode(e.key)):this.type===a.eq&&u?(u.shorthandAssign<0&&(u.shorthandAssign=this.start),e.value=this.parseMaybeDefault(r,n,this.copyNode(e.key))):e.value=this.copyNode(e.key),e.kind="init",e.shorthand=!0):this.unexpected()},f.parsePropertyName=function(e){if(this.options.ecmaVersion>=6){if(this.eat(a.bracketL))return e.computed=!0,e.key=this.parseMaybeAssign(),this.expect(a.bracketR),e.key;e.computed=!1}return e.key=this.type===a.num||this.type===a.string?this.parseExprAtom():this.parseIdent(this.options.allowReserved!=="never")},f.initFunction=function(e){e.id=null,this.options.ecmaVersion>=6&&(e.generator=e.expression=!1),this.options.ecmaVersion>=8&&(e.async=!1)},f.parseMethod=function(e,t,i){var s=this.startNode(),r=this.yieldPos,n=this.awaitPos,u=this.awaitIdentPos;return this.initFunction(s),this.options.ecmaVersion>=6&&(s.generator=e),this.options.ecmaVersion>=8&&(s.async=!!t),this.yieldPos=0,this.awaitPos=0,this.awaitIdentPos=0,this.enterScope(Se(t,s.generator)|ne|(i?Ue:0)),this.expect(a.parenL),s.params=this.parseBindingList(a.parenR,!1,this.options.ecmaVersion>=8),this.checkYieldAwaitInDefaultParams(),this.parseFunctionBody(s,!1,!0,!1),this.yieldPos=r,this.awaitPos=n,this.awaitIdentPos=u,this.finishNode(s,"FunctionExpression")},f.parseArrowExpression=function(e,t,i,s){var r=this.yieldPos,n=this.awaitPos,u=this.awaitIdentPos;return this.enterScope(Se(i,!1)|_e),this.initFunction(e),this.options.ecmaVersion>=8&&(e.async=!!i),this.yieldPos=0,this.awaitPos=0,this.awaitIdentPos=0,e.params=this.toAssignableList(t,!0),this.parseFunctionBody(e,!0,!1,s),this.yieldPos=r,this.awaitPos=n,this.awaitIdentPos=u,this.finishNode(e,"ArrowFunctionExpression")},f.parseFunctionBody=function(e,t,i,s){var r=t&&this.type!==a.braceL,n=this.strict,u=!1;if(r)e.body=this.parseMaybeAssign(s),e.expression=!0,this.checkParams(e,!1);else{var o=this.options.ecmaVersion>=7&&!this.isSimpleParamList(e.params);(!n||o)&&(u=this.strictDirective(this.end),u&&o&&this.raiseRecoverable(e.start,"Illegal \'use strict\' directive in function with non-simple parameter list"));var h=this.labels;this.labels=[],u&&(this.strict=!0),this.checkParams(e,!n&&!u&&!t&&!i&&this.isSimpleParamList(e.params)),this.strict&&e.id&&this.checkLValSimple(e.id,He),e.body=this.parseBlock(!1,void 0,u&&!n),e.expression=!1,this.adaptDirectivePrologue(e.body.body),this.labels=h}this.exitScope()},f.isSimpleParamList=function(e){for(var t=0,i=e;t-1||r.functions.indexOf(e)>-1||r.var.indexOf(e)>-1,r.lexical.push(e),this.inModule&&r.flags&j&&delete this.undefinedExports[e]}else if(t===Ge){var n=this.currentScope();n.lexical.push(e)}else if(t===je){var u=this.currentScope();this.treatFunctionsAsVar?s=u.lexical.indexOf(e)>-1:s=u.lexical.indexOf(e)>-1||u.var.indexOf(e)>-1,u.functions.push(e)}else for(var o=this.scopeStack.length-1;o>=0;--o){var h=this.scopeStack[o];if(h.lexical.indexOf(e)>-1&&!(h.flags&Me&&h.lexical[0]===e)||!this.treatFunctionsAsVarInScope(h)&&h.functions.indexOf(e)>-1){s=!0;break}if(h.var.push(e),this.inModule&&h.flags&j&&delete this.undefinedExports[e],h.flags&ue)break}s&&this.raiseRecoverable(i,"Identifier \'"+e+"\' has already been declared")},U.checkLocalExport=function(e){this.scopeStack[0].lexical.indexOf(e.name)===-1&&this.scopeStack[0].var.indexOf(e.name)===-1&&(this.undefinedExports[e.name]=e)},U.currentScope=function(){return this.scopeStack[this.scopeStack.length-1]},U.currentVarScope=function(){for(var e=this.scopeStack.length-1;;e--){var t=this.scopeStack[e];if(t.flags&(ue|$|H))return t}},U.currentThisScope=function(){for(var e=this.scopeStack.length-1;;e--){var t=this.scopeStack[e];if(t.flags&(ue|$|H)&&!(t.flags&_e))return t}};var te=function(t,i,s){this.type="",this.start=i,this.end=0,t.options.locations&&(this.loc=new J(t,s)),t.options.directSourceFile&&(this.sourceFile=t.options.directSourceFile),t.options.ranges&&(this.range=[i,0])},ie=C.prototype;ie.startNode=function(){return new te(this,this.start,this.startLoc)},ie.startNodeAt=function(e,t){return new te(this,e,t)};function Ke(e,t,i,s){return e.type=t,e.end=i,this.options.locations&&(e.loc.end=s),this.options.ranges&&(e.range[1]=i),e}ie.finishNode=function(e,t){return Ke.call(this,e,t,this.lastTokEnd,this.lastTokEndLoc)},ie.finishNodeAt=function(e,t,i,s){return Ke.call(this,e,t,i,s)},ie.copyNode=function(e){var t=new te(this,e.start,this.startLoc);for(var i in e)t[i]=e[i];return t};var Tt="Berf Beria_Erfe Gara Garay Gukh Gurung_Khema Hrkt Katakana_Or_Hiragana Kawi Kirat_Rai Krai Nag_Mundari Nagm Ol_Onal Onao Sidetic Sidt Sunu Sunuwar Tai_Yo Tayo Todhri Todr Tolong_Siki Tols Tulu_Tigalari Tutg Unknown Zzzz",Qe="ASCII ASCII_Hex_Digit AHex Alphabetic Alpha Any Assigned Bidi_Control Bidi_C Bidi_Mirrored Bidi_M Case_Ignorable CI Cased Changes_When_Casefolded CWCF Changes_When_Casemapped CWCM Changes_When_Lowercased CWL Changes_When_NFKC_Casefolded CWKCF Changes_When_Titlecased CWT Changes_When_Uppercased CWU Dash Default_Ignorable_Code_Point DI Deprecated Dep Diacritic Dia Emoji Emoji_Component Emoji_Modifier Emoji_Modifier_Base Emoji_Presentation Extender Ext Grapheme_Base Gr_Base Grapheme_Extend Gr_Ext Hex_Digit Hex IDS_Binary_Operator IDSB IDS_Trinary_Operator IDST ID_Continue IDC ID_Start IDS Ideographic Ideo Join_Control Join_C Logical_Order_Exception LOE Lowercase Lower Math Noncharacter_Code_Point NChar Pattern_Syntax Pat_Syn Pattern_White_Space Pat_WS Quotation_Mark QMark Radical Regional_Indicator RI Sentence_Terminal STerm Soft_Dotted SD Terminal_Punctuation Term Unified_Ideograph UIdeo Uppercase Upper Variation_Selector VS White_Space space XID_Continue XIDC XID_Start XIDS",Ye=Qe+" Extended_Pictographic",Xe=Ye,Ze=Xe+" EBase EComp EMod EPres ExtPict",Je=Ze,Lt=Je,Rt={9:Qe,10:Ye,11:Xe,12:Ze,13:Je,14:Lt},Ot="Basic_Emoji Emoji_Keycap_Sequence RGI_Emoji_Modifier_Sequence RGI_Emoji_Flag_Sequence RGI_Emoji_Tag_Sequence RGI_Emoji_ZWJ_Sequence RGI_Emoji",Bt={9:"",10:"",11:"",12:"",13:"",14:Ot},$e="Cased_Letter LC Close_Punctuation Pe Connector_Punctuation Pc Control Cc cntrl Currency_Symbol Sc Dash_Punctuation Pd Decimal_Number Nd digit Enclosing_Mark Me Final_Punctuation Pf Format Cf Initial_Punctuation Pi Letter L Letter_Number Nl Line_Separator Zl Lowercase_Letter Ll Mark M Combining_Mark Math_Symbol Sm Modifier_Letter Lm Modifier_Symbol Sk Nonspacing_Mark Mn Number N Open_Punctuation Ps Other C Other_Letter Lo Other_Number No Other_Punctuation Po Other_Symbol So Paragraph_Separator Zp Private_Use Co Punctuation P punct Separator Z Space_Separator Zs Spacing_Mark Mc Surrogate Cs Symbol S Titlecase_Letter Lt Unassigned Cn Uppercase_Letter Lu",et="Adlam Adlm Ahom Anatolian_Hieroglyphs Hluw Arabic Arab Armenian Armn Avestan Avst Balinese Bali Bamum Bamu Bassa_Vah Bass Batak Batk Bengali Beng Bhaiksuki Bhks Bopomofo Bopo Brahmi Brah Braille Brai Buginese Bugi Buhid Buhd Canadian_Aboriginal Cans Carian Cari Caucasian_Albanian Aghb Chakma Cakm Cham Cham Cherokee Cher Common Zyyy Coptic Copt Qaac Cuneiform Xsux Cypriot Cprt Cyrillic Cyrl Deseret Dsrt Devanagari Deva Duployan Dupl Egyptian_Hieroglyphs Egyp Elbasan Elba Ethiopic Ethi Georgian Geor Glagolitic Glag Gothic Goth Grantha Gran Greek Grek Gujarati Gujr Gurmukhi Guru Han Hani Hangul Hang Hanunoo Hano Hatran Hatr Hebrew Hebr Hiragana Hira Imperial_Aramaic Armi Inherited Zinh Qaai Inscriptional_Pahlavi Phli Inscriptional_Parthian Prti Javanese Java Kaithi Kthi Kannada Knda Katakana Kana Kayah_Li Kali Kharoshthi Khar Khmer Khmr Khojki Khoj Khudawadi Sind Lao Laoo Latin Latn Lepcha Lepc Limbu Limb Linear_A Lina Linear_B Linb Lisu Lisu Lycian Lyci Lydian Lydi Mahajani Mahj Malayalam Mlym Mandaic Mand Manichaean Mani Marchen Marc Masaram_Gondi Gonm Meetei_Mayek Mtei Mende_Kikakui Mend Meroitic_Cursive Merc Meroitic_Hieroglyphs Mero Miao Plrd Modi Mongolian Mong Mro Mroo Multani Mult Myanmar Mymr Nabataean Nbat New_Tai_Lue Talu Newa Newa Nko Nkoo Nushu Nshu Ogham Ogam Ol_Chiki Olck Old_Hungarian Hung Old_Italic Ital Old_North_Arabian Narb Old_Permic Perm Old_Persian Xpeo Old_South_Arabian Sarb Old_Turkic Orkh Oriya Orya Osage Osge Osmanya Osma Pahawh_Hmong Hmng Palmyrene Palm Pau_Cin_Hau Pauc Phags_Pa Phag Phoenician Phnx Psalter_Pahlavi Phlp Rejang Rjng Runic Runr Samaritan Samr Saurashtra Saur Sharada Shrd Shavian Shaw Siddham Sidd SignWriting Sgnw Sinhala Sinh Sora_Sompeng Sora Soyombo Soyo Sundanese Sund Syloti_Nagri Sylo Syriac Syrc Tagalog Tglg Tagbanwa Tagb Tai_Le Tale Tai_Tham Lana Tai_Viet Tavt Takri Takr Tamil Taml Tangut Tang Telugu Telu Thaana Thaa Thai Thai Tibetan Tibt Tifinagh Tfng Tirhuta Tirh Ugaritic Ugar Vai Vaii Warang_Citi Wara Yi Yiii Zanabazar_Square Zanb",tt=et+" Dogra Dogr Gunjala_Gondi Gong Hanifi_Rohingya Rohg Makasar Maka Medefaidrin Medf Old_Sogdian Sogo Sogdian Sogd",it=tt+" Elymaic Elym Nandinagari Nand Nyiakeng_Puachue_Hmong Hmnp Wancho Wcho",st=it+" Chorasmian Chrs Diak Dives_Akuru Khitan_Small_Script Kits Yezi Yezidi",at=st+" Cypro_Minoan Cpmn Old_Uyghur Ougr Tangsa Tnsa Toto Vithkuqi Vith",Dt=at+" "+Tt,Ft={9:et,10:tt,11:it,12:st,13:at,14:Dt},rt={};function Mt(e){var t=rt[e]={binary:M(Rt[e]+" "+$e),binaryOfStrings:M(Bt[e]),nonBinary:{General_Category:M($e),Script:M(Ft[e])}};t.nonBinary.Script_Extensions=t.nonBinary.Script,t.nonBinary.gc=t.nonBinary.General_Category,t.nonBinary.sc=t.nonBinary.Script,t.nonBinary.scx=t.nonBinary.Script_Extensions}for(var Ie=0,nt=[9,10,11,12,13,14];Ie=6?"uy":"")+(t.options.ecmaVersion>=9?"s":"")+(t.options.ecmaVersion>=13?"d":"")+(t.options.ecmaVersion>=15?"v":""),this.unicodeProperties=rt[t.options.ecmaVersion>=14?14:t.options.ecmaVersion],this.source="",this.flags="",this.start=0,this.switchU=!1,this.switchV=!1,this.switchN=!1,this.pos=0,this.lastIntValue=0,this.lastStringValue="",this.lastAssertionIsQuantifiable=!1,this.numCapturingParens=0,this.maxBackReference=0,this.groupNames=Object.create(null),this.backReferenceNames=[],this.branchID=null};R.prototype.reset=function(t,i,s){var r=s.indexOf("v")!==-1,n=s.indexOf("u")!==-1;this.start=t|0,this.source=i+"",this.flags=s,r&&this.parser.options.ecmaVersion>=15?(this.switchU=!0,this.switchV=!0,this.switchN=!0):(this.switchU=n&&this.parser.options.ecmaVersion>=6,this.switchV=!1,this.switchN=n&&this.parser.options.ecmaVersion>=9)},R.prototype.raise=function(t){this.parser.raiseRecoverable(this.start,"Invalid regular expression: /"+this.source+"/: "+t)},R.prototype.at=function(t,i){i===void 0&&(i=!1);var s=this.source,r=s.length;if(t>=r)return-1;var n=s.charCodeAt(t);if(!(i||this.switchU)||n<=55295||n>=57344||t+1>=r)return n;var u=s.charCodeAt(t+1);return u>=56320&&u<=57343?(n<<10)+u-56613888:n},R.prototype.nextIndex=function(t,i){i===void 0&&(i=!1);var s=this.source,r=s.length;if(t>=r)return r;var n=s.charCodeAt(t),u;return!(i||this.switchU)||n<=55295||n>=57344||t+1>=r||(u=s.charCodeAt(t+1))<56320||u>57343?t+1:t+2},R.prototype.current=function(t){return t===void 0&&(t=!1),this.at(this.pos,t)},R.prototype.lookahead=function(t){return t===void 0&&(t=!1),this.at(this.nextIndex(this.pos,t),t)},R.prototype.advance=function(t){t===void 0&&(t=!1),this.pos=this.nextIndex(this.pos,t)},R.prototype.eat=function(t,i){return i===void 0&&(i=!1),this.current(i)===t?(this.advance(i),!0):!1},R.prototype.eatChars=function(t,i){i===void 0&&(i=!1);for(var s=this.pos,r=0,n=t;r-1&&this.raise(e.start,"Duplicate regular expression flag"),u==="u"&&(s=!0),u==="v"&&(r=!0)}this.options.ecmaVersion>=15&&s&&r&&this.raise(e.start,"Invalid regular expression flag")};function qt(e){for(var t in e)return!0;return!1}c.validateRegExpPattern=function(e){this.regexp_pattern(e),!e.switchN&&this.options.ecmaVersion>=9&&qt(e.groupNames)&&(e.switchN=!0,this.regexp_pattern(e))},c.regexp_pattern=function(e){e.pos=0,e.lastIntValue=0,e.lastStringValue="",e.lastAssertionIsQuantifiable=!1,e.numCapturingParens=0,e.maxBackReference=0,e.groupNames=Object.create(null),e.backReferenceNames.length=0,e.branchID=null,this.regexp_disjunction(e),e.pos!==e.source.length&&(e.eat(41)&&e.raise("Unmatched \')\'"),(e.eat(93)||e.eat(125))&&e.raise("Lone quantifier brackets")),e.maxBackReference>e.numCapturingParens&&e.raise("Invalid escape");for(var t=0,i=e.backReferenceNames;t=16;for(t&&(e.branchID=new pe(e.branchID,null)),this.regexp_alternative(e);e.eat(124);)t&&(e.branchID=e.branchID.sibling()),this.regexp_alternative(e);t&&(e.branchID=e.branchID.parent),this.regexp_eatQuantifier(e,!0)&&e.raise("Nothing to repeat"),e.eat(123)&&e.raise("Lone quantifier brackets")},c.regexp_alternative=function(e){for(;e.pos=9&&(i=e.eat(60)),e.eat(61)||e.eat(33))return this.regexp_disjunction(e),e.eat(41)||e.raise("Unterminated group"),e.lastAssertionIsQuantifiable=!i,!0}return e.pos=t,!1},c.regexp_eatQuantifier=function(e,t){return t===void 0&&(t=!1),this.regexp_eatQuantifierPrefix(e,t)?(e.eat(63),!0):!1},c.regexp_eatQuantifierPrefix=function(e,t){return e.eat(42)||e.eat(43)||e.eat(63)||this.regexp_eatBracedQuantifier(e,t)},c.regexp_eatBracedQuantifier=function(e,t){var i=e.pos;if(e.eat(123)){var s=0,r=-1;if(this.regexp_eatDecimalDigits(e)&&(s=e.lastIntValue,e.eat(44)&&this.regexp_eatDecimalDigits(e)&&(r=e.lastIntValue),e.eat(125)))return r!==-1&&r=16){var i=this.regexp_eatModifiers(e),s=e.eat(45);if(i||s){for(var r=0;r-1&&e.raise("Duplicate regular expression modifiers")}if(s){var u=this.regexp_eatModifiers(e);!i&&!u&&e.current()===58&&e.raise("Invalid regular expression modifiers");for(var o=0;o-1||i.indexOf(h)>-1)&&e.raise("Duplicate regular expression modifiers")}}}}if(e.eat(58)){if(this.regexp_disjunction(e),e.eat(41))return!0;e.raise("Unterminated group")}}e.pos=t}return!1},c.regexp_eatCapturingGroup=function(e){if(e.eat(40)){if(this.options.ecmaVersion>=9?this.regexp_groupSpecifier(e):e.current()===63&&e.raise("Invalid group"),this.regexp_disjunction(e),e.eat(41))return e.numCapturingParens+=1,!0;e.raise("Unterminated group")}return!1},c.regexp_eatModifiers=function(e){for(var t="",i=0;(i=e.current())!==-1&&jt(i);)t+=B(i),e.advance();return t};function jt(e){return e===105||e===109||e===115}c.regexp_eatExtendedAtom=function(e){return e.eat(46)||this.regexp_eatReverseSolidusAtomEscape(e)||this.regexp_eatCharacterClass(e)||this.regexp_eatUncapturingGroup(e)||this.regexp_eatCapturingGroup(e)||this.regexp_eatInvalidBracedQuantifier(e)||this.regexp_eatExtendedPatternCharacter(e)},c.regexp_eatInvalidBracedQuantifier=function(e){return this.regexp_eatBracedQuantifier(e,!0)&&e.raise("Nothing to repeat"),!1},c.regexp_eatSyntaxCharacter=function(e){var t=e.current();return ut(t)?(e.lastIntValue=t,e.advance(),!0):!1};function ut(e){return e===36||e>=40&&e<=43||e===46||e===63||e>=91&&e<=94||e>=123&&e<=125}c.regexp_eatPatternCharacters=function(e){for(var t=e.pos,i=0;(i=e.current())!==-1&&!ut(i);)e.advance();return e.pos!==t},c.regexp_eatExtendedPatternCharacter=function(e){var t=e.current();return t!==-1&&t!==36&&!(t>=40&&t<=43)&&t!==46&&t!==63&&t!==91&&t!==94&&t!==124?(e.advance(),!0):!1},c.regexp_groupSpecifier=function(e){if(e.eat(63)){this.regexp_eatGroupName(e)||e.raise("Invalid group");var t=this.options.ecmaVersion>=16,i=e.groupNames[e.lastStringValue];if(i)if(t)for(var s=0,r=i;s=11,s=e.current(i);return e.advance(i),s===92&&this.regexp_eatRegExpUnicodeEscapeSequence(e,i)&&(s=e.lastIntValue),Gt(s)?(e.lastIntValue=s,!0):(e.pos=t,!1)};function Gt(e){return L(e,!0)||e===36||e===95}c.regexp_eatRegExpIdentifierPart=function(e){var t=e.pos,i=this.options.ecmaVersion>=11,s=e.current(i);return e.advance(i),s===92&&this.regexp_eatRegExpUnicodeEscapeSequence(e,i)&&(s=e.lastIntValue),Ht(s)?(e.lastIntValue=s,!0):(e.pos=t,!1)};function Ht(e){return O(e,!0)||e===36||e===95||e===8204||e===8205}c.regexp_eatAtomEscape=function(e){return this.regexp_eatBackReference(e)||this.regexp_eatCharacterClassEscape(e)||this.regexp_eatCharacterEscape(e)||e.switchN&&this.regexp_eatKGroupName(e)?!0:(e.switchU&&(e.current()===99&&e.raise("Invalid unicode escape"),e.raise("Invalid escape")),!1)},c.regexp_eatBackReference=function(e){var t=e.pos;if(this.regexp_eatDecimalEscape(e)){var i=e.lastIntValue;if(e.switchU)return i>e.maxBackReference&&(e.maxBackReference=i),!0;if(i<=e.numCapturingParens)return!0;e.pos=t}return!1},c.regexp_eatKGroupName=function(e){if(e.eat(107)){if(this.regexp_eatGroupName(e))return e.backReferenceNames.push(e.lastStringValue),!0;e.raise("Invalid named reference")}return!1},c.regexp_eatCharacterEscape=function(e){return this.regexp_eatControlEscape(e)||this.regexp_eatCControlLetter(e)||this.regexp_eatZero(e)||this.regexp_eatHexEscapeSequence(e)||this.regexp_eatRegExpUnicodeEscapeSequence(e,!1)||!e.switchU&&this.regexp_eatLegacyOctalEscapeSequence(e)||this.regexp_eatIdentityEscape(e)},c.regexp_eatCControlLetter=function(e){var t=e.pos;if(e.eat(99)){if(this.regexp_eatControlLetter(e))return!0;e.pos=t}return!1},c.regexp_eatZero=function(e){return e.current()===48&&!fe(e.lookahead())?(e.lastIntValue=0,e.advance(),!0):!1},c.regexp_eatControlEscape=function(e){var t=e.current();return t===116?(e.lastIntValue=9,e.advance(),!0):t===110?(e.lastIntValue=10,e.advance(),!0):t===118?(e.lastIntValue=11,e.advance(),!0):t===102?(e.lastIntValue=12,e.advance(),!0):t===114?(e.lastIntValue=13,e.advance(),!0):!1},c.regexp_eatControlLetter=function(e){var t=e.current();return ot(t)?(e.lastIntValue=t%32,e.advance(),!0):!1};function ot(e){return e>=65&&e<=90||e>=97&&e<=122}c.regexp_eatRegExpUnicodeEscapeSequence=function(e,t){t===void 0&&(t=!1);var i=e.pos,s=t||e.switchU;if(e.eat(117)){if(this.regexp_eatFixedHexDigits(e,4)){var r=e.lastIntValue;if(s&&r>=55296&&r<=56319){var n=e.pos;if(e.eat(92)&&e.eat(117)&&this.regexp_eatFixedHexDigits(e,4)){var u=e.lastIntValue;if(u>=56320&&u<=57343)return e.lastIntValue=(r-55296)*1024+(u-56320)+65536,!0}e.pos=n,e.lastIntValue=r}return!0}if(s&&e.eat(123)&&this.regexp_eatHexDigits(e)&&e.eat(125)&&Wt(e.lastIntValue))return!0;s&&e.raise("Invalid unicode escape"),e.pos=i}return!1};function Wt(e){return e>=0&&e<=1114111}c.regexp_eatIdentityEscape=function(e){if(e.switchU)return this.regexp_eatSyntaxCharacter(e)?!0:e.eat(47)?(e.lastIntValue=47,!0):!1;var t=e.current();return t!==99&&(!e.switchN||t!==107)?(e.lastIntValue=t,e.advance(),!0):!1},c.regexp_eatDecimalEscape=function(e){e.lastIntValue=0;var t=e.current();if(t>=49&&t<=57){do e.lastIntValue=10*e.lastIntValue+(t-48),e.advance();while((t=e.current())>=48&&t<=57);return!0}return!1};var ht=0,F=1,T=2;c.regexp_eatCharacterClassEscape=function(e){var t=e.current();if(zt(t))return e.lastIntValue=-1,e.advance(),F;var i=!1;if(e.switchU&&this.options.ecmaVersion>=9&&((i=t===80)||t===112)){e.lastIntValue=-1,e.advance();var s;if(e.eat(123)&&(s=this.regexp_eatUnicodePropertyValueExpression(e))&&e.eat(125))return i&&s===T&&e.raise("Invalid property name"),s;e.raise("Invalid property name")}return ht};function zt(e){return e===100||e===68||e===115||e===83||e===119||e===87}c.regexp_eatUnicodePropertyValueExpression=function(e){var t=e.pos;if(this.regexp_eatUnicodePropertyName(e)&&e.eat(61)){var i=e.lastStringValue;if(this.regexp_eatUnicodePropertyValue(e)){var s=e.lastStringValue;return this.regexp_validateUnicodePropertyNameAndValue(e,i,s),F}}if(e.pos=t,this.regexp_eatLoneUnicodePropertyNameOrValue(e)){var r=e.lastStringValue;return this.regexp_validateUnicodePropertyNameOrValue(e,r)}return ht},c.regexp_validateUnicodePropertyNameAndValue=function(e,t,i){W(e.unicodeProperties.nonBinary,t)||e.raise("Invalid property name"),e.unicodeProperties.nonBinary[t].test(i)||e.raise("Invalid property value")},c.regexp_validateUnicodePropertyNameOrValue=function(e,t){if(e.unicodeProperties.binary.test(t))return F;if(e.switchV&&e.unicodeProperties.binaryOfStrings.test(t))return T;e.raise("Invalid property name")},c.regexp_eatUnicodePropertyName=function(e){var t=0;for(e.lastStringValue="";ct(t=e.current());)e.lastStringValue+=B(t),e.advance();return e.lastStringValue!==""};function ct(e){return ot(e)||e===95}c.regexp_eatUnicodePropertyValue=function(e){var t=0;for(e.lastStringValue="";Kt(t=e.current());)e.lastStringValue+=B(t),e.advance();return e.lastStringValue!==""};function Kt(e){return ct(e)||fe(e)}c.regexp_eatLoneUnicodePropertyNameOrValue=function(e){return this.regexp_eatUnicodePropertyValue(e)},c.regexp_eatCharacterClass=function(e){if(e.eat(91)){var t=e.eat(94),i=this.regexp_classContents(e);return e.eat(93)||e.raise("Unterminated character class"),t&&i===T&&e.raise("Negated character class may contain strings"),!0}return!1},c.regexp_classContents=function(e){return e.current()===93?F:e.switchV?this.regexp_classSetExpression(e):(this.regexp_nonEmptyClassRanges(e),F)},c.regexp_nonEmptyClassRanges=function(e){for(;this.regexp_eatClassAtom(e);){var t=e.lastIntValue;if(e.eat(45)&&this.regexp_eatClassAtom(e)){var i=e.lastIntValue;e.switchU&&(t===-1||i===-1)&&e.raise("Invalid character class"),t!==-1&&i!==-1&&t>i&&e.raise("Range out of order in character class")}}},c.regexp_eatClassAtom=function(e){var t=e.pos;if(e.eat(92)){if(this.regexp_eatClassEscape(e))return!0;if(e.switchU){var i=e.current();(i===99||ft(i))&&e.raise("Invalid class escape"),e.raise("Invalid escape")}e.pos=t}var s=e.current();return s!==93?(e.lastIntValue=s,e.advance(),!0):!1},c.regexp_eatClassEscape=function(e){var t=e.pos;if(e.eat(98))return e.lastIntValue=8,!0;if(e.switchU&&e.eat(45))return e.lastIntValue=45,!0;if(!e.switchU&&e.eat(99)){if(this.regexp_eatClassControlLetter(e))return!0;e.pos=t}return this.regexp_eatCharacterClassEscape(e)||this.regexp_eatCharacterEscape(e)},c.regexp_classSetExpression=function(e){var t=F,i;if(!this.regexp_eatClassSetRange(e))if(i=this.regexp_eatClassSetOperand(e)){i===T&&(t=T);for(var s=e.pos;e.eatChars([38,38]);){if(e.current()!==38&&(i=this.regexp_eatClassSetOperand(e))){i!==T&&(t=F);continue}e.raise("Invalid character in character class")}if(s!==e.pos)return t;for(;e.eatChars([45,45]);)this.regexp_eatClassSetOperand(e)||e.raise("Invalid character in character class");if(s!==e.pos)return t}else e.raise("Invalid character in character class");for(;;)if(!this.regexp_eatClassSetRange(e)){if(i=this.regexp_eatClassSetOperand(e),!i)return t;i===T&&(t=T)}},c.regexp_eatClassSetRange=function(e){var t=e.pos;if(this.regexp_eatClassSetCharacter(e)){var i=e.lastIntValue;if(e.eat(45)&&this.regexp_eatClassSetCharacter(e)){var s=e.lastIntValue;return i!==-1&&s!==-1&&i>s&&e.raise("Range out of order in character class"),!0}e.pos=t}return!1},c.regexp_eatClassSetOperand=function(e){return this.regexp_eatClassSetCharacter(e)?F:this.regexp_eatClassStringDisjunction(e)||this.regexp_eatNestedClass(e)},c.regexp_eatNestedClass=function(e){var t=e.pos;if(e.eat(91)){var i=e.eat(94),s=this.regexp_classContents(e);if(e.eat(93))return i&&s===T&&e.raise("Negated character class may contain strings"),s;e.pos=t}if(e.eat(92)){var r=this.regexp_eatCharacterClassEscape(e);if(r)return r;e.pos=t}return null},c.regexp_eatClassStringDisjunction=function(e){var t=e.pos;if(e.eatChars([92,113])){if(e.eat(123)){var i=this.regexp_classStringDisjunctionContents(e);if(e.eat(125))return i}else e.raise("Invalid escape");e.pos=t}return null},c.regexp_classStringDisjunctionContents=function(e){for(var t=this.regexp_classString(e);e.eat(124);)this.regexp_classString(e)===T&&(t=T);return t},c.regexp_classString=function(e){for(var t=0;this.regexp_eatClassSetCharacter(e);)t++;return t===1?F:T},c.regexp_eatClassSetCharacter=function(e){var t=e.pos;if(e.eat(92))return this.regexp_eatCharacterEscape(e)||this.regexp_eatClassSetReservedPunctuator(e)?!0:e.eat(98)?(e.lastIntValue=8,!0):(e.pos=t,!1);var i=e.current();return i<0||i===e.lookahead()&&Qt(i)||Yt(i)?!1:(e.advance(),e.lastIntValue=i,!0)};function Qt(e){return e===33||e>=35&&e<=38||e>=42&&e<=44||e===46||e>=58&&e<=64||e===94||e===96||e===126}function Yt(e){return e===40||e===41||e===45||e===47||e>=91&&e<=93||e>=123&&e<=125}c.regexp_eatClassSetReservedPunctuator=function(e){var t=e.current();return Xt(t)?(e.lastIntValue=t,e.advance(),!0):!1};function Xt(e){return e===33||e===35||e===37||e===38||e===44||e===45||e>=58&&e<=62||e===64||e===96||e===126}c.regexp_eatClassControlLetter=function(e){var t=e.current();return fe(t)||t===95?(e.lastIntValue=t%32,e.advance(),!0):!1},c.regexp_eatHexEscapeSequence=function(e){var t=e.pos;if(e.eat(120)){if(this.regexp_eatFixedHexDigits(e,2))return!0;e.switchU&&e.raise("Invalid escape"),e.pos=t}return!1},c.regexp_eatDecimalDigits=function(e){var t=e.pos,i=0;for(e.lastIntValue=0;fe(i=e.current());)e.lastIntValue=10*e.lastIntValue+(i-48),e.advance();return e.pos!==t};function fe(e){return e>=48&&e<=57}c.regexp_eatHexDigits=function(e){var t=e.pos,i=0;for(e.lastIntValue=0;lt(i=e.current());)e.lastIntValue=16*e.lastIntValue+pt(i),e.advance();return e.pos!==t};function lt(e){return e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102}function pt(e){return e>=65&&e<=70?10+(e-65):e>=97&&e<=102?10+(e-97):e-48}c.regexp_eatLegacyOctalEscapeSequence=function(e){if(this.regexp_eatOctalDigit(e)){var t=e.lastIntValue;if(this.regexp_eatOctalDigit(e)){var i=e.lastIntValue;t<=3&&this.regexp_eatOctalDigit(e)?e.lastIntValue=t*64+i*8+e.lastIntValue:e.lastIntValue=t*8+i}else e.lastIntValue=t;return!0}return!1},c.regexp_eatOctalDigit=function(e){var t=e.current();return ft(t)?(e.lastIntValue=t-48,e.advance(),!0):(e.lastIntValue=0,!1)};function ft(e){return e>=48&&e<=55}c.regexp_eatFixedHexDigits=function(e,t){var i=e.pos;e.lastIntValue=0;for(var s=0;s=this.input.length)return this.finishToken(a.eof);if(e.override)return e.override(this);this.readToken(this.fullCharCodeAtPos())},x.readToken=function(e){return L(e,this.options.ecmaVersion>=6)||e===92?this.readWord():this.getTokenFromCode(e)},x.fullCharCodeAt=function(e){var t=this.input.charCodeAt(e);if(t<=55295||t>=56320)return t;var i=this.input.charCodeAt(e+1);return i<=56319||i>=57344?t:(t<<10)+i-56613888},x.fullCharCodeAtPos=function(){return this.fullCharCodeAt(this.pos)},x.skipBlockComment=function(){var e=this.options.onComment&&this.curPosition(),t=this.pos,i=this.input.indexOf("*/",this.pos+=2);if(i===-1&&this.raise(this.pos-2,"Unterminated comment"),this.pos=i+2,this.options.locations)for(var s=void 0,r=t;(s=Le(this.input,r,this.pos))>-1;)++this.curLine,r=this.lineStart=s;this.options.onComment&&this.options.onComment(!0,this.input.slice(t+2,i),t,this.pos,e,this.curPosition())},x.skipLineComment=function(e){for(var t=this.pos,i=this.options.onComment&&this.curPosition(),s=this.input.charCodeAt(this.pos+=e);this.pos8&&e<14||e>=5760&&be.test(String.fromCharCode(e)))++this.pos;else break e}}},x.finishToken=function(e,t){this.end=this.pos,this.options.locations&&(this.endLoc=this.curPosition());var i=this.type;this.type=e,this.value=t,this.updateContext(i)},x.readToken_dot=function(){var e=this.input.charCodeAt(this.pos+1);if(e>=48&&e<=57)return this.readNumber(!0);var t=this.input.charCodeAt(this.pos+2);return this.options.ecmaVersion>=6&&e===46&&t===46?(this.pos+=3,this.finishToken(a.ellipsis)):(++this.pos,this.finishToken(a.dot))},x.readToken_slash=function(){var e=this.input.charCodeAt(this.pos+1);return this.exprAllowed?(++this.pos,this.readRegexp()):e===61?this.finishOp(a.assign,2):this.finishOp(a.slash,1)},x.readToken_mult_modulo_exp=function(e){var t=this.input.charCodeAt(this.pos+1),i=1,s=e===42?a.star:a.modulo;return this.options.ecmaVersion>=7&&e===42&&t===42&&(++i,s=a.starstar,t=this.input.charCodeAt(this.pos+2)),t===61?this.finishOp(a.assign,i+1):this.finishOp(s,i)},x.readToken_pipe_amp=function(e){var t=this.input.charCodeAt(this.pos+1);if(t===e){if(this.options.ecmaVersion>=12){var i=this.input.charCodeAt(this.pos+2);if(i===61)return this.finishOp(a.assign,3)}return this.finishOp(e===124?a.logicalOR:a.logicalAND,2)}return t===61?this.finishOp(a.assign,2):this.finishOp(e===124?a.bitwiseOR:a.bitwiseAND,1)},x.readToken_caret=function(){var e=this.input.charCodeAt(this.pos+1);return e===61?this.finishOp(a.assign,2):this.finishOp(a.bitwiseXOR,1)},x.readToken_plus_min=function(e){var t=this.input.charCodeAt(this.pos+1);return t===e?t===45&&!this.inModule&&this.input.charCodeAt(this.pos+2)===62&&(this.lastTokEnd===0||k.test(this.input.slice(this.lastTokEnd,this.pos)))?(this.skipLineComment(3),this.skipSpace(),this.nextToken()):this.finishOp(a.incDec,2):t===61?this.finishOp(a.assign,2):this.finishOp(a.plusMin,1)},x.readToken_lt_gt=function(e){var t=this.input.charCodeAt(this.pos+1),i=1;return t===e?(i=e===62&&this.input.charCodeAt(this.pos+2)===62?3:2,this.input.charCodeAt(this.pos+i)===61?this.finishOp(a.assign,i+1):this.finishOp(a.bitShift,i)):t===33&&e===60&&!this.inModule&&this.input.charCodeAt(this.pos+2)===45&&this.input.charCodeAt(this.pos+3)===45?(this.skipLineComment(4),this.skipSpace(),this.nextToken()):(t===61&&(i=2),this.finishOp(a.relational,i))},x.readToken_eq_excl=function(e){var t=this.input.charCodeAt(this.pos+1);return t===61?this.finishOp(a.equality,this.input.charCodeAt(this.pos+2)===61?3:2):e===61&&t===62&&this.options.ecmaVersion>=6?(this.pos+=2,this.finishToken(a.arrow)):this.finishOp(e===61?a.eq:a.prefix,1)},x.readToken_question=function(){var e=this.options.ecmaVersion;if(e>=11){var t=this.input.charCodeAt(this.pos+1);if(t===46){var i=this.input.charCodeAt(this.pos+2);if(i<48||i>57)return this.finishOp(a.questionDot,2)}if(t===63){if(e>=12){var s=this.input.charCodeAt(this.pos+2);if(s===61)return this.finishOp(a.assign,3)}return this.finishOp(a.coalesce,2)}}return this.finishOp(a.question,1)},x.readToken_numberSign=function(){var e=this.options.ecmaVersion,t=35;if(e>=13&&(++this.pos,t=this.fullCharCodeAtPos(),L(t,!0)||t===92))return this.finishToken(a.privateId,this.readWord1());this.raise(this.pos,"Unexpected character \'"+B(t)+"\'")},x.getTokenFromCode=function(e){switch(e){case 46:return this.readToken_dot();case 40:return++this.pos,this.finishToken(a.parenL);case 41:return++this.pos,this.finishToken(a.parenR);case 59:return++this.pos,this.finishToken(a.semi);case 44:return++this.pos,this.finishToken(a.comma);case 91:return++this.pos,this.finishToken(a.bracketL);case 93:return++this.pos,this.finishToken(a.bracketR);case 123:return++this.pos,this.finishToken(a.braceL);case 125:return++this.pos,this.finishToken(a.braceR);case 58:return++this.pos,this.finishToken(a.colon);case 96:if(this.options.ecmaVersion<6)break;return++this.pos,this.finishToken(a.backQuote);case 48:var t=this.input.charCodeAt(this.pos+1);if(t===120||t===88)return this.readRadixNumber(16);if(this.options.ecmaVersion>=6){if(t===111||t===79)return this.readRadixNumber(8);if(t===98||t===66)return this.readRadixNumber(2)}case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return this.readNumber(!1);case 34:case 39:return this.readString(e);case 47:return this.readToken_slash();case 37:case 42:return this.readToken_mult_modulo_exp(e);case 124:case 38:return this.readToken_pipe_amp(e);case 94:return this.readToken_caret();case 43:case 45:return this.readToken_plus_min(e);case 60:case 62:return this.readToken_lt_gt(e);case 61:case 33:return this.readToken_eq_excl(e);case 63:return this.readToken_question();case 126:return this.finishOp(a.prefix,1);case 35:return this.readToken_numberSign()}this.raise(this.pos,"Unexpected character \'"+B(e)+"\'")},x.finishOp=function(e,t){var i=this.input.slice(this.pos,this.pos+t);return this.pos+=t,this.finishToken(e,i)},x.readRegexp=function(){for(var e,t,i=this.pos;;){this.pos>=this.input.length&&this.raise(i,"Unterminated regular expression");var s=this.input.charAt(this.pos);if(k.test(s)&&this.raise(i,"Unterminated regular expression"),e)e=!1;else{if(s==="[")t=!0;else if(s==="]"&&t)t=!1;else if(s==="/"&&!t)break;e=s==="\\\\"}++this.pos}var r=this.input.slice(i,this.pos);++this.pos;var n=this.pos,u=this.readWord1();this.containsEsc&&this.unexpected(n);var o=this.regexpState||(this.regexpState=new R(this));o.reset(i,r,u),this.validateRegExpFlags(o),this.validateRegExpPattern(o);var h=null;try{h=new RegExp(r,u)}catch{}return this.finishToken(a.regexp,{pattern:r,flags:u,value:h})},x.readInt=function(e,t,i){for(var s=this.options.ecmaVersion>=12&&t===void 0,r=i&&this.input.charCodeAt(this.pos)===48,n=this.pos,u=0,o=0,h=0,p=t??1/0;h=97?y=d-97+10:d>=65?y=d-65+10:d>=48&&d<=57?y=d-48:y=1/0,y>=e)break;o=d,u=u*e+y}return s&&o===95&&this.raiseRecoverable(this.pos-1,"Numeric separator is not allowed at the last of digits"),this.pos===n||t!=null&&this.pos-n!==t?null:u};function Zt(e,t){return t?parseInt(e,8):parseFloat(e.replace(/_/g,""))}function dt(e){return typeof BigInt!="function"?null:BigInt(e.replace(/_/g,""))}x.readRadixNumber=function(e){var t=this.pos;this.pos+=2;var i=this.readInt(e);return i==null&&this.raise(this.start+2,"Expected number in radix "+e),this.options.ecmaVersion>=11&&this.input.charCodeAt(this.pos)===110?(i=dt(this.input.slice(t,this.pos)),++this.pos):L(this.fullCharCodeAtPos())&&this.raise(this.pos,"Identifier directly after number"),this.finishToken(a.num,i)},x.readNumber=function(e){var t=this.pos;!e&&this.readInt(10,void 0,!0)===null&&this.raise(t,"Invalid number");var i=this.pos-t>=2&&this.input.charCodeAt(t)===48;i&&this.strict&&this.raise(t,"Invalid number");var s=this.input.charCodeAt(this.pos);if(!i&&!e&&this.options.ecmaVersion>=11&&s===110){var r=dt(this.input.slice(t,this.pos));return++this.pos,L(this.fullCharCodeAtPos())&&this.raise(this.pos,"Identifier directly after number"),this.finishToken(a.num,r)}i&&/[89]/.test(this.input.slice(t,this.pos))&&(i=!1),s===46&&!i&&(++this.pos,this.readInt(10),s=this.input.charCodeAt(this.pos)),(s===69||s===101)&&!i&&(s=this.input.charCodeAt(++this.pos),(s===43||s===45)&&++this.pos,this.readInt(10)===null&&this.raise(t,"Invalid number")),L(this.fullCharCodeAtPos())&&this.raise(this.pos,"Identifier directly after number");var n=Zt(this.input.slice(t,this.pos),i);return this.finishToken(a.num,n)},x.readCodePoint=function(){var e=this.input.charCodeAt(this.pos),t;if(e===123){this.options.ecmaVersion<6&&this.unexpected();var i=++this.pos;t=this.readHexChar(this.input.indexOf("}",this.pos)-this.pos),++this.pos,t>1114111&&this.invalidStringToken(i,"Code point out of bounds")}else t=this.readHexChar(4);return t},x.readString=function(e){for(var t="",i=++this.pos;;){this.pos>=this.input.length&&this.raise(this.start,"Unterminated string constant");var s=this.input.charCodeAt(this.pos);if(s===e)break;s===92?(t+=this.input.slice(i,this.pos),t+=this.readEscapedChar(!1),i=this.pos):s===8232||s===8233?(this.options.ecmaVersion<10&&this.raise(this.start,"Unterminated string constant"),++this.pos,this.options.locations&&(this.curLine++,this.lineStart=this.pos)):(q(s)&&this.raise(this.start,"Unterminated string constant"),++this.pos)}return t+=this.input.slice(i,this.pos++),this.finishToken(a.string,t)};var xt={};x.tryReadTemplateToken=function(){this.inTemplateElement=!0;try{this.readTmplToken()}catch(e){if(e===xt)this.readInvalidTemplateToken();else throw e}this.inTemplateElement=!1},x.invalidStringToken=function(e,t){if(this.inTemplateElement&&this.options.ecmaVersion>=9)throw xt;this.raise(e,t)},x.readTmplToken=function(){for(var e="",t=this.pos;;){this.pos>=this.input.length&&this.raise(this.start,"Unterminated template");var i=this.input.charCodeAt(this.pos);if(i===96||i===36&&this.input.charCodeAt(this.pos+1)===123)return this.pos===this.start&&(this.type===a.template||this.type===a.invalidTemplate)?i===36?(this.pos+=2,this.finishToken(a.dollarBraceL)):(++this.pos,this.finishToken(a.backQuote)):(e+=this.input.slice(t,this.pos),this.finishToken(a.template,e));if(i===92)e+=this.input.slice(t,this.pos),e+=this.readEscapedChar(!0),t=this.pos;else if(q(i)){switch(e+=this.input.slice(t,this.pos),++this.pos,i){case 13:this.input.charCodeAt(this.pos)===10&&++this.pos;case 10:e+=`\n`;break;default:e+=String.fromCharCode(i);break}this.options.locations&&(++this.curLine,this.lineStart=this.pos),t=this.pos}else++this.pos}},x.readInvalidTemplateToken=function(){for(;this.pos=48&&t<=55){var s=this.input.substr(this.pos-1,3).match(/^[0-7]+/)[0],r=parseInt(s,8);return r>255&&(s=s.slice(0,-1),r=parseInt(s,8)),this.pos+=s.length-1,t=this.input.charCodeAt(this.pos),(s!=="0"||t===56||t===57)&&(this.strict||e)&&this.invalidStringToken(this.pos-1-s.length,e?"Octal literal in template string":"Octal literal in strict mode"),String.fromCharCode(r)}return q(t)?(this.options.locations&&(this.lineStart=this.pos,++this.curLine),""):String.fromCharCode(t)}},x.readHexChar=function(e){var t=this.pos,i=this.readInt(16,e);return i===null&&this.invalidStringToken(t,"Bad character escape sequence"),i},x.readWord1=function(){this.containsEsc=!1;for(var e="",t=!0,i=this.pos,s=this.options.ecmaVersion>=6;this.pos`, so an inline regex here (after +// `+` in the upstream `.replace(...)` chain) would silently truncate the +// bundled module. bundle-modules.ts asserts on that truncation now. const kParenMessageRe = / \([^)]+\)/; function processTopLevelAwait(src) { diff --git a/src/js/internal/repl/completion.js b/src/js/internal/repl/completion.js index 819b5e9434fa..e6b7ab9193bd 100644 --- a/src/js/internal/repl/completion.js +++ b/src/js/internal/repl/completion.js @@ -3,7 +3,6 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -("use strict"); const { ArrayPrototypeFilter, @@ -59,7 +58,8 @@ const { getOwnNonIndexProperties, } = require("internal/repl/node-shims"); -const { isIdentifierStart, isIdentifierChar, parse: acornParse } = require("internal/repl/acorn"); +// Lazy: don't destructure — see internal/repl/acorn.js. +const acorn = require("internal/repl/acorn"); const acornWalk = require("internal/repl/acorn-walk"); const importRE = /\bimport\s*\(\s*['"`](([\w@./:-]+\/)?(?:[\w@./:-]*))(?![^'"`])$/; @@ -81,13 +81,13 @@ function isIdentifier(str) { return false; } const first = StringPrototypeCodePointAt(str, 0); - if (!isIdentifierStart(first)) { + if (!acorn.isIdentifierStart(first)) { return false; } const firstLen = first > 0xffff ? 2 : 1; for (let i = firstLen; i < str.length; i += 1) { const cp = StringPrototypeCodePointAt(str, i); - if (!isIdentifierChar(cp)) { + if (!acorn.isIdentifierChar(cp)) { return false; } if (cp > 0xffff) { @@ -413,7 +413,7 @@ function complete(line, callback) { let completeTargetAst; try { - completeTargetAst = acornParse(parsableCompleteTarget, { + completeTargetAst = acorn.parse(parsableCompleteTarget, { __proto__: null, sourceType: "module", ecmaVersion: "latest", @@ -591,7 +591,7 @@ function findExpressionCompleteTarget(code) { let ast; try { - ast = acornParse(code, { __proto__: null, sourceType: "module", ecmaVersion: "latest" }); + ast = acorn.parse(code, { __proto__: null, sourceType: "module", ecmaVersion: "latest" }); } catch { const keywords = code.split(" "); diff --git a/src/js/internal/repl/history.js b/src/js/internal/repl/history.js index bb94b038ef8a..9a857a6257e8 100644 --- a/src/js/internal/repl/history.js +++ b/src/js/internal/repl/history.js @@ -3,7 +3,6 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -("use strict"); const { ArrayPrototypeIndexOf, diff --git a/src/js/internal/repl/node-errors.js b/src/js/internal/repl/node-errors.js index 153beb4c77e9..84c2c29eecc6 100644 --- a/src/js/internal/repl/node-errors.js +++ b/src/js/internal/repl/node-errors.js @@ -1,6 +1,7 @@ // Error-code shims for Node.js sources ported into Bun (node:repl stack). -// Most codes route to Bun's native $ERR_* constructors; the REPL-specific -// ones that Bun's ErrorCode registry lacks are defined here. +// All codes route to Bun's native $ERR_* constructors (registered in +// ErrorCode.ts), which give the Node-compatible `err.name` (bare "TypeError", +// with `[CODE]` only in toString()). `instanceof ERR_X` is keyed on `.code`. function ERR_INVALID_ARG_TYPE(...args) { return $ERR_INVALID_ARG_TYPE(...args); @@ -23,54 +24,37 @@ function ERR_SCRIPT_EXECUTION_INTERRUPTED(...args) { function ERR_INVALID_STATE(...args) { return $ERR_INVALID_STATE(...args); } - -class ERR_CANNOT_WATCH_SIGINT extends Error { - code = "ERR_CANNOT_WATCH_SIGINT"; - constructor() { - super("Cannot watch for interruptions when running asynchronously"); - this.name = "Error [ERR_CANNOT_WATCH_SIGINT]"; - Error.captureStackTrace?.(this, ERR_CANNOT_WATCH_SIGINT); +// Bun's native $ERR_* gives Node-compatible `.name` and `.toString()`, but +// JSC materializes `.stack` from `.name + ": " + msg` at construction, so the +// `[CODE]` (which Node's prepareStackTrace injects) is missing there. The +// vendored REPL tests match on the stack text, so re-head it to `.toString()`. +function decorateNodeErrorStack(e) { + if (typeof e?.stack === "string") { + const nl = e.stack.indexOf("\n"); + e.stack = e.toString() + (nl === -1 ? "" : e.stack.slice(nl)); } + return e; } - -class ERR_INSPECTOR_NOT_AVAILABLE extends Error { - code = "ERR_INSPECTOR_NOT_AVAILABLE"; - constructor() { - super("Inspector is not available"); - this.name = "Error [ERR_INSPECTOR_NOT_AVAILABLE]"; - Error.captureStackTrace?.(this, ERR_INSPECTOR_NOT_AVAILABLE); - } +function ERR_CANNOT_WATCH_SIGINT() { + return decorateNodeErrorStack($ERR_CANNOT_WATCH_SIGINT("Cannot watch for interruptions when running asynchronously")); } - -class ERR_INVALID_REPL_EVAL_CONFIG extends TypeError { - code = "ERR_INVALID_REPL_EVAL_CONFIG"; - constructor() { - super('Cannot specify both "breakEvalOnSigint" and "eval" for REPL'); - this.name = "TypeError [ERR_INVALID_REPL_EVAL_CONFIG]"; - Error.captureStackTrace?.(this, ERR_INVALID_REPL_EVAL_CONFIG); - } +function ERR_INSPECTOR_NOT_AVAILABLE() { + return decorateNodeErrorStack($ERR_INSPECTOR_NOT_AVAILABLE("Inspector is not available")); } - -class ERR_INVALID_REPL_INPUT extends TypeError { - code = "ERR_INVALID_REPL_INPUT"; - constructor(message) { - super(message); - this.name = "TypeError [ERR_INVALID_REPL_INPUT]"; - Error.captureStackTrace?.(this, ERR_INVALID_REPL_INPUT); - } +function ERR_INVALID_REPL_EVAL_CONFIG() { + return decorateNodeErrorStack( + $ERR_INVALID_REPL_EVAL_CONFIG('Cannot specify both "breakEvalOnSigint" and "eval" for REPL'), + ); } - -class AbortError extends Error { - code = "ABORT_ERR"; - name = "AbortError"; - constructor(message = "The operation was aborted", options = undefined) { - super(message, options); - } +function ERR_INVALID_REPL_INPUT(message) { + return decorateNodeErrorStack($ERR_INVALID_REPL_INPUT(message)); +} +function AbortError(message = "The operation was aborted", options = undefined) { + return $makeAbortError(message, options); } -// `instanceof ERR_X` must work on errors produced by the Bun-native $ERR_* -// constructors; builtin function declarations have no .prototype, so route -// instanceof through Symbol.hasInstance keyed on the error code. +// Builtin function declarations have no .prototype, so route `instanceof` +// through Symbol.hasInstance keyed on the error code. for (const fn of [ ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, @@ -79,6 +63,10 @@ for (const fn of [ ERR_INVALID_CURSOR_POS, ERR_SCRIPT_EXECUTION_INTERRUPTED, ERR_INVALID_STATE, + ERR_CANNOT_WATCH_SIGINT, + ERR_INSPECTOR_NOT_AVAILABLE, + ERR_INVALID_REPL_EVAL_CONFIG, + ERR_INVALID_REPL_INPUT, ]) { Object.defineProperty(fn, Symbol.hasInstance, { __proto__: null, @@ -86,47 +74,13 @@ for (const fn of [ }); } -// API-shape stub of Node's internal/errors.overrideStackTrace. In Node this -// WeakMap registers a one-shot prepareStackTrace formatter that fires when -// the error's stack is lazily materialized. Under JSC the stack is already a -// string by the time the REPL's _handleError registers the override, so the -// formatter never fires; REPL frame trimming is done in decorateErrorStack -// instead. The earlier implementation installed a global -// Error.prepareStackTrace hook that chained to Bun's native default formatter, -// which throws for non-Error targets — breaking Error.captureStackTrace(obj) -// process-wide after the first REPL error. Keep the registry inert: track -// entries for get/delete parity but never touch Error.prepareStackTrace. -const overrideStackTraceMap = new WeakMap(); - -const overrideStackTrace = { - set(error, fn) { - return overrideStackTraceMap.set(error, fn); - }, - get(error) { - return overrideStackTraceMap.get(error); - }, - delete(error) { - return overrideStackTraceMap.delete(error); - }, -}; - function isErrorStackTraceLimitWritable() { const desc = Object.getOwnPropertyDescriptor(Error, "stackTraceLimit"); if (desc === undefined) return Object.isExtensible(Error); return Object.prototype.hasOwnProperty.$call(desc, "writable") ? desc.writable : desc.set !== undefined; } -function ErrorPrepareStackTrace(error, stackFrames) { - let out = `${error.name ?? "Error"}${error.message ? ": " + error.message : ""}`; - for (const frame of stackFrames ?? []) { - out += `\n at ${frame.toString()}`; - } - return out; -} - export default { - ErrorPrepareStackTrace, - overrideStackTrace, isErrorStackTraceLimitWritable, AbortError, codes: { diff --git a/src/js/internal/repl/node-inspect.js b/src/js/internal/repl/node-inspect.js index af793b58a423..1a3bc845595c 100644 --- a/src/js/internal/repl/node-inspect.js +++ b/src/js/internal/repl/node-inspect.js @@ -1,16 +1,13 @@ // Shim for Node's `internal/util/inspect` as consumed by the ported -// node:repl / internal/readline stack: routes inspect/stripVTControlCharacters -// to Bun's port and getStringWidth to the native implementation. -const { inspect, stripVTControlCharacters, format, formatWithOptions } = require("internal/util/inspect"); - -const internalGetStringWidth = $newCppFunction("stringWidth.cpp", "jsFunctionBunStringWidth", 1); - -function getStringWidth(str, removeControlChars = true) { - return internalGetStringWidth(str, { - countAnsiEscapeCodes: !removeControlChars, - ambiguousIsNarrow: true, - }); -} +// node:repl / internal/readline stack. Re-exports the single implementations +// so readline's cursor math and its __BUN_INTERNALS__ test hook stay one path. +const { + inspect, + stripVTControlCharacters, + format, + formatWithOptions, + getStringWidth, +} = require("internal/util/inspect"); export default { inspect, diff --git a/src/js/internal/repl/node-primordials.js b/src/js/internal/repl/node-primordials.js index a180896807ec..3ba5626c30bb 100644 --- a/src/js/internal/repl/node-primordials.js +++ b/src/js/internal/repl/node-primordials.js @@ -3,10 +3,11 @@ // intrinsic once at module load and invokes it through the tamper-proof // `$call`/`$apply` intrinsics, so replacing a prototype method (e.g. // `Array.prototype.push = ...`) after this module loads does not affect the -// ported code. This is weaker than Node's real primordials in two ways: the -// capture happens at (lazy) module load rather than realm bootstrap, and the -// Safe* containers are plain aliases whose instance methods still dispatch -// through their (mutable) prototypes. +// ported code. Safe* containers re-export the real makeSafe()-wrapped +// implementations from internal/primordials so there is one definition per +// name. Weaker than Node only in that capture happens at (lazy) module load +// rather than realm bootstrap. +const { SafeMap, SafeSet, SafeWeakSet, SafeStringIterator } = require("internal/primordials"); const ArrayFromFn = Array.from; const ArrayPrototypeAtFn = Array.prototype.at; @@ -58,13 +59,6 @@ const StringPrototypeToLocaleLowerCaseFn = String.prototype.toLocaleLowerCase; const StringPrototypeToLowerCaseFn = String.prototype.toLowerCase; const StringPrototypeTrimFn = String.prototype.trim; const StringPrototypeTrimStartFn = String.prototype.trimStart; -const StringPrototypeSymbolIteratorFn = String.prototype[Symbol.iterator]; - -class SafeStringIterator { - constructor(string) { - return StringPrototypeSymbolIteratorFn.$call(string); - } -} export default { ArrayFrom: (...args) => ArrayFromFn.$apply(Array, args), @@ -132,9 +126,9 @@ export default { RegExpPrototypeSymbolReplace: (re, s, replacement) => RegExpPrototypeSymbolReplaceFn.$call(re, s, replacement), RegExpPrototypeSymbolSplit: (re, s, limit) => RegExpPrototypeSymbolSplitFn.$call(re, s, limit), SafePromiseRace: promises => PromiseRaceFn.$call(Promise, promises), - SafeSet: Set, - SafeMap: Map, - SafeWeakSet: WeakSet, + SafeSet, + SafeMap, + SafeWeakSet, SafeStringIterator, StringFromCharCode: String.fromCharCode, StringPrototypeCharAt: (s, i) => StringPrototypeCharAtFn.$call(s, i), diff --git a/src/js/internal/repl/node-shims.js b/src/js/internal/repl/node-shims.js index 9593ff227342..0ef0ddd84b4d 100644 --- a/src/js/internal/repl/node-shims.js +++ b/src/js/internal/repl/node-shims.js @@ -5,17 +5,23 @@ const util = require("node:util"); const Module = require("node:module"); const path = require("node:path"); +const { RegExpPrototypeSymbolReplace, RegExpPrototypeSymbolSplit } = require("internal/repl/node-primordials"); // ---- internal/util ---------------------------------------------------- -const kEmptyObject = Object.freeze({ __proto__: null }); +const { kEmptyObject } = require("internal/shared"); +// Node's real implementation reconstructs the regex in an internal realm so a +// tampered `RegExp.prototype[Symbol.replace]` can't observe it. Bun has no +// internal realm; the load-time-captured intrinsics close the `[Symbol.*]` +// override hole (a tampered `RegExp.prototype.exec` is still observable per +// spec — see `@@replace`/`@@split` `Get(rx,"exec")`). function SideEffectFreeRegExpPrototypeSymbolReplace(regexp, str, replacement) { - return regexp[Symbol.replace](str, replacement); + return RegExpPrototypeSymbolReplace(regexp, str, replacement); } function SideEffectFreeRegExpPrototypeSymbolSplit(regexp, str, limit) { - return regexp[Symbol.split](str, limit); + return RegExpPrototypeSymbolSplit(regexp, str, limit); } function assignFunctionName(name, fn) { @@ -50,18 +56,12 @@ function decorateErrorStack(err) { } function isError(e) { - return e instanceof Error || Object.prototype.toString.$call(e) === "[object Error]"; + return util.types.isNativeError(e) || e instanceof Error; } // ---- internal/util/colors ---------------------------------------------- -function shouldColorize(stream) { - if (process.env.FORCE_COLOR !== undefined) { - const getColorDepth = require("node:tty").WriteStream.prototype.getColorDepth; - return getColorDepth.$call({}) > 2; - } - return stream?.isTTY && (typeof stream.getColorDepth === "function" ? stream.getColorDepth() > 2 : true); -} +const { shouldColorize } = require("internal/util/colors"); // ---- internal/util/debuglog ---------------------------------------------- @@ -74,6 +74,9 @@ function debuglog(set, cb) { // ---- internal/util/inspector ---------------------------------------------- function sendInspectorCommand(cb, onError) { + // JSC's inspector protocol has no `Runtime.globalLexicalScopeNames` (V8-only), + // so let/const/class tab-completion in useGlobal:true mode is inert until a + // native binding enumerates JSGlobalObject::globalLexicalEnvironment(). return onError(); } @@ -114,21 +117,7 @@ function isWritable(stream) { // ---- internal/events/abort_listener ---------------------------------------------- -function addAbortListener(signal, listener) { - if (require("node:events").addAbortListener) { - return require("node:events").addAbortListener(signal, listener); - } - if (signal.aborted) { - queueMicrotask(() => listener()); - } else { - signal.addEventListener("abort", listener, { once: true }); - } - return { - [Symbol.dispose]() { - signal?.removeEventListener("abort", listener); - }, - }; -} +const { addAbortListener } = require("internal/abort_listener"); // ---- internal/bootstrap/realm ---------------------------------------------- @@ -268,7 +257,7 @@ function makeContextifyScript( hostDefinedOptionId, importModuleDynamically, ) { - const script = new vm.Script(code, { + return new vm.Script(code, { filename, lineOffset, columnOffset, @@ -276,19 +265,6 @@ function makeContextifyScript( produceCachedData, importModuleDynamically: importModuleDynamically ?? (specifier => import(specifier)), }); - // Node's vm.Script constructor throws SyntaxError eagerly; Bun's native - // Script defers parsing to run time. The REPL's recoverable-error flow - // depends on the eager throw, so force a parse via createCachedData and, - // when it fails, surface the real SyntaxError by running the script in a - // throwaway context (a parse error always fires before any code executes). - try { - script.createCachedData(); - } catch { - new vm.Script(code, { filename, lineOffset, columnOffset }).runInContext(vm.createContext({}), { - displayErrors: false, - }); - } - return script; } function runScriptInThisContext(script, displayErrors, _breakOnFirstLine) { @@ -329,9 +305,11 @@ class CJSModuleShim { // ---- internalBinding('contextify') ---------------------------------------------- function startSigintWatchdog() { - // Bun has no native SIGINT watchdog for vm script execution; report - // success so breakEvalOnSigint callers proceed (Ctrl+C interruption of - // long-running synchronous eval is not supported). + // breakOnSigint interruption of synchronous eval WORKS via Bun's own + // SigintWatcher (wired in NodeVMScript.cpp). Only Node's `had_pending_ + // signals` race — SIGINT landing after the script exits but before raw mode + // is restored — is unimplemented, so stopSigintWatchdog() always reports no + // pending signal. return true; } @@ -375,9 +353,12 @@ function getOwnNonIndexProperties(obj, filter = ALL_PROPERTIES) { // ---- process.addUncaughtExceptionCaptureCallback polyfill ---------------- // Bun only implements the single-callback set/clear API; emulate Node's -// additive API with a dispatcher list. +// additive API with a dispatcher list. Tracked so the exclusive slot is only +// cleared when the shim itself owns it — never a user's callback — and is +// released once the last REPL closes. let captureCallbacks = null; +let dispatcherInstalled = false; function addUncaughtExceptionCaptureCallback(cb) { if (!captureCallbacks) { @@ -387,20 +368,22 @@ function addUncaughtExceptionCaptureCallback(cb) { for (const fn of captureCallbacks) { if (fn(err)) return; } - // No callback claimed the error: Node's additive API falls through to - // the regular 'uncaughtException' flow, and only then to the fatal - // handler. - if (process.emit("uncaughtException", err)) return; + // No callback claimed it: Node's aux API falls through to the + // regular 'uncaughtException' flow (with the origin arg), then to + // the native fatal handler. + if (process.emit("uncaughtException", err, "uncaughtException")) return; try { process.stderr.write(`Uncaught ${util.inspect(err)}\n`); } catch {} process.exit(1); }); + dispatcherInstalled = true; } catch { - // A user capture callback is already installed via the single-callback - // API. Node's additive API coexists with it natively; without that - // engine support, defer to the user's callback - REPL error handling - // falls back to the regular uncaughtException flow. + // A user capture callback already occupies the exclusive slot. Node's + // additive API coexists with it natively; without that engine support, + // defer to the user's callback and don't push (the dispatcher isn't + // wired, so a queued cb would never fire). + return; } } captureCallbacks.push(cb); @@ -412,7 +395,10 @@ function removeUncaughtExceptionCaptureCallback(cb) { if (i !== -1) captureCallbacks.splice(i, 1); if (captureCallbacks.length === 0) { captureCallbacks = null; - process.setUncaughtExceptionCaptureCallback(null); + if (dispatcherInstalled) { + dispatcherInstalled = false; + process.setUncaughtExceptionCaptureCallback(null); + } } } diff --git a/src/js/internal/repl/utils.js b/src/js/internal/repl/utils.js index dc0d1968ce85..7d2891bf8ec0 100644 --- a/src/js/internal/repl/utils.js +++ b/src/js/internal/repl/utils.js @@ -3,7 +3,6 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -("use strict"); const { ArrayPrototypeFilter, @@ -24,7 +23,9 @@ const { Symbol, } = primordials; -const { tokTypes: tt, Parser: AcornParser } = require("internal/repl/acorn"); +// Lazy: don't destructure — the vm.Script parse of acorn's ~122 KB source +// stays deferred until isRecoverableError/isValidSyntax first runs. +const acorn = require("internal/repl/acorn"); const { sendInspectorCommand, getBuiltinLibs } = require("internal/repl/node-shims"); @@ -83,11 +84,11 @@ function isRecoverableError(e, code) { // change these messages in the future, this will lead to a test // failure, indicating that this code needs to be updated. // - const RecoverableParser = AcornParser.extend(Parser => { + const RecoverableParser = acorn.Parser.extend(Parser => { return class extends Parser { nextToken() { super.nextToken(); - if (this.type === tt.eof) recoverable = true; + if (this.type === acorn.tokTypes.eof) recoverable = true; } raise(pos, message) { switch (message) { @@ -737,14 +738,14 @@ const startsWithBraceRegExp = /^\s*{/; const endsWithSemicolonRegExp = /;\s*$/; function isValidSyntax(input) { try { - AcornParser.parse(input, { + acorn.Parser.parse(input, { ecmaVersion: "latest", allowAwaitOutsideFunction: true, }); return true; } catch { try { - AcornParser.parse(`_=${input}`, { + acorn.Parser.parse(`_=${input}`, { ecmaVersion: "latest", allowAwaitOutsideFunction: true, }); diff --git a/src/js/node/readline.js b/src/js/node/readline.js index 24479d0ab56e..aa7b77ff1b5b 100644 --- a/src/js/node/readline.js +++ b/src/js/node/readline.js @@ -24,7 +24,6 @@ var __node_module__ = { exports: {} }; // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -("use strict"); const { DateNow, diff --git a/src/js/node/readline.promises.js b/src/js/node/readline.promises.js index 710fbdcfc7d9..497bb590f937 100644 --- a/src/js/node/readline.promises.js +++ b/src/js/node/readline.promises.js @@ -3,7 +3,6 @@ // prettier-ignore const primordials = require("internal/repl/node-primordials"); var __node_module__ = { exports: {} }; -("use strict"); const { Promise, SymbolDispose } = primordials; diff --git a/src/js/node/repl.js b/src/js/node/repl.js index 4266974d0492..a5a5236a4f03 100644 --- a/src/js/node/repl.js +++ b/src/js/node/repl.js @@ -45,12 +45,9 @@ var __node_module__ = { exports: {} }; * repl.start("node > ").context.foo = "stdin is fun"; */ -("use strict"); - const { ArrayPrototypeAt, ArrayPrototypeFilter, - ArrayPrototypeFindLastIndex, ArrayPrototypeForEach, ArrayPrototypeJoin, ArrayPrototypeMap, @@ -91,7 +88,9 @@ const { } = primordials; const { makeRequireFunction, addBuiltinLibsToObject } = require("internal/repl/node-shims"); -const { parse: acornParse } = require("internal/repl/acorn"); +// Lazy: acorn's ~122 KB source parses on first property access, not on +// require('node:repl'); don't destructure at module scope. +const acorn = require("internal/repl/acorn"); const acornWalk = require("internal/repl/acorn-walk"); const { decorateErrorStack, @@ -117,7 +116,6 @@ let debug = require("internal/repl/node-shims").debuglog("repl", fn => { debug = fn; }); const { - ErrorPrepareStackTrace, codes: { ERR_CANNOT_WATCH_SIGINT, ERR_INVALID_ARG_VALUE, @@ -128,7 +126,6 @@ const { ERR_SCRIPT_EXECUTION_INTERRUPTED, }, isErrorStackTraceLimitWritable, - overrideStackTrace, } = require("internal/repl/node-errors"); const { sendInspectorCommand } = require("internal/repl/node-shims"); const { getOptionValue } = require("internal/repl/node-shims"); @@ -165,27 +162,29 @@ const parentModule = __node_module__; // AsyncLocalStorage to track which REPL instance owns the current async context // This replaces the domain-based tracking for error handling const replContext = new AsyncLocalStorage(); -let exceptionCaptureSetup = false; - -/** - * Sets up the uncaught exception capture callback to route errors - * to the appropriate REPL instance. This replaces domain-based error handling. - * Uses addUncaughtExceptionCaptureCallback to coexist with the primary - * callback (e.g., domain module). - */ -function setupExceptionCapture() { - if (exceptionCaptureSetup) return; +let exceptionCaptureUseCount = 0; - require("internal/repl/node-shims").addUncaughtExceptionCaptureCallback(err => { - const store = replContext.getStore(); - if (store?.replServer) { - const result = store.replServer._handleError(err); - return result !== "unhandled"; // We handled it - } - // No active REPL context - let other handlers try - }); +function replExceptionCaptureCallback(err) { + const store = replContext.getStore(); + if (store?.replServer) { + const result = store.replServer._handleError(err); + return result !== "unhandled"; // We handled it + } + // No active REPL context - let other handlers try +} - exceptionCaptureSetup = true; +// One-shot install per process. Node's `addUncaughtExceptionCaptureCallback` +// keeps a separate aux list that never counts against +// `hasUncaughtExceptionCaptureCallback()`; Bun lacks that native API, so the +// shim occupies the exclusive slot for the process lifetime once the first +// REPL starts (uninstalling on 'exit' would drop async errors that fire after +// input close — see test-repl-uncaught-exception-after-input-ended). The +// shim's fallthrough re-emits `uncaughtException` with the origin arg so user +// listeners still see it. +function setupExceptionCapture() { + if (exceptionCaptureUseCount++ === 0) { + require("internal/repl/node-shims").addUncaughtExceptionCaptureCallback(replExceptionCaptureCallback); + } } const kBufferedCommandSymbol = Symbol("bufferedCommand"); @@ -231,7 +230,7 @@ writer.options = { ...inspect.defaultOptions, showProxy: true }; // Converts static import statement to dynamic import statement const toDynamicImport = codeLine => { let dynamicImportStatement = ""; - const ast = acornParse(codeLine, { __proto__: null, sourceType: "module", ecmaVersion: "latest" }); + const ast = acorn.parse(codeLine, { __proto__: null, sourceType: "module", ecmaVersion: "latest" }); acornWalk.ancestor(ast, { ImportDeclaration(node) { const awaitDynamicImport = `await import(${JSONStringify(node.source.value)});`; @@ -991,25 +990,8 @@ class REPLServer extends Interface { let errStack = ""; if (typeof e === "object" && e !== null) { - overrideStackTrace.set(e, (error, stackFrames) => { - let frames; - if (typeof stackFrames === "object") { - // Search from the bottom of the call stack to - // find the first frame with a null function name - const idx = ArrayPrototypeFindLastIndex(stackFrames, frame => frame.getFunctionName() === null); - // If found, get rid of it and everything below it - frames = ArrayPrototypeSlice(stackFrames, 0, idx); - } else { - frames = stackFrames; - } - // FIXME(devsnek): this is inconsistent with the checks - // that the real prepareStackTrace dispatch uses in - // lib/internal/errors.js. - if (typeof MainContextError.prepareStackTrace === "function") { - return MainContextError.prepareStackTrace(error, frames); - } - return ErrorPrepareStackTrace(error, frames); - }); + // Node's overrideStackTrace formatter can't fire under JSC (stack is + // already materialized); decorateErrorStack does the REPL-frame trimming. decorateErrorStack(e); if (isError(e)) { @@ -1495,12 +1477,13 @@ ObjectDefineProperty(__node_module__.exports, "_builtinLibs", { configurable: true, }); -// Lets the bun --interactive entry opt into standalone-REPL semantics -// (it boots via the public repl.start, not internal/repl). +// Lets the bun --interactive entry (a plain eval script, not a builtin) reach +// internal/repl's createInternalRepl so the NODE_REPL_* env parsing has one +// implementation. Lazy getter: internal/repl requires node:repl at its top. // Non-enumerable so it stays off the public node:repl surface. -ObjectDefineProperty(__node_module__.exports, Symbol.for("bun.repl.kStandaloneREPL"), { +ObjectDefineProperty(__node_module__.exports, Symbol.for("bun.repl.createInternalRepl"), { __proto__: null, - value: kStandaloneREPL, + get: () => require("internal/repl").createInternalRepl, }); export default __node_module__.exports; diff --git a/src/jsc/bindings/ErrorCode.ts b/src/jsc/bindings/ErrorCode.ts index 9b7edcc3365d..b0d514b7e5b3 100644 --- a/src/jsc/bindings/ErrorCode.ts +++ b/src/jsc/bindings/ErrorCode.ts @@ -27,6 +27,7 @@ const errors: ErrorCodeMapping = [ ["ERR_BUFFER_CONTEXT_NOT_AVAILABLE", Error], ["ERR_BUFFER_OUT_OF_BOUNDS", RangeError], ["ERR_BUFFER_TOO_LARGE", RangeError], + ["ERR_CANNOT_WATCH_SIGINT", Error], ["ERR_CHILD_PROCESS_IPC_REQUIRED", Error], ["ERR_CHILD_PROCESS_STDIO_MAXBUFFER", RangeError], ["ERR_CLOSED_MESSAGE_PORT", Error], @@ -147,6 +148,9 @@ const errors: ErrorCodeMapping = [ ["ERR_INVALID_OBJECT_DEFINE_PROPERTY", TypeError], ["ERR_INVALID_PACKAGE_CONFIG", Error], ["ERR_INVALID_PROTOCOL", TypeError], + ["ERR_INSPECTOR_NOT_AVAILABLE", Error], + ["ERR_INVALID_REPL_EVAL_CONFIG", TypeError], + ["ERR_INVALID_REPL_INPUT", TypeError], ["ERR_INVALID_RETURN_VALUE", TypeError], ["ERR_INVALID_STATE", Error, undefined, TypeError, RangeError], ["ERR_INVALID_THIS", TypeError], diff --git a/src/jsc/bindings/NodeVMScript.cpp b/src/jsc/bindings/NodeVMScript.cpp index 6bfa862eac8a..8b95f95bfdd3 100644 --- a/src/jsc/bindings/NodeVMScript.cpp +++ b/src/jsc/bindings/NodeVMScript.cpp @@ -131,6 +131,17 @@ constructScript(JSGlobalObject* globalObject, CallFrame* callFrame, JSValue newT SourceCode source = makeSource(sourceString, JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(options.filename), *fetcher), JSC::SourceTaintedOrigin::Untainted, options.filename, TextPosition(options.lineOffset, options.columnOffset)); RETURN_IF_EXCEPTION(scope, {}); + // Node's vm.Script throws SyntaxError at construction; the REPL's + // recoverable-error flow (and user code) relies on that. Matches + // vm.compileFunction (NodeVM.cpp) which also parses eagerly. + JSC::ParserError parseError; + if (!JSC::checkSyntax(vm, source, parseError)) { + auto exception = parseError.toErrorObject(globalObject, source, -1); + RETURN_IF_EXCEPTION(scope, {}); + throwException(globalObject, scope, exception); + return {}; + } + const bool produceCachedData = options.produceCachedData; auto filename = options.filename; diff --git a/src/runtime/cli/run_command.rs b/src/runtime/cli/run_command.rs index 5f733e772163..47dce847bbc4 100644 --- a/src/runtime/cli/run_command.rs +++ b/src/runtime/cli/run_command.rs @@ -2954,19 +2954,23 @@ impl RunCommand { /// the Node.js-compatible REPL (node:repl). Distinct from `bun repl`, /// which is Bun's own native REPL. pub fn exec_node_repl(ctx: &mut ContextData) -> Result<(), bun_core::Error> { + use ::core::fmt::Write as _; let bootstrap = bun_core::runtime_embed_file!(Codegen, "eval/node-repl.ts").as_bytes(); let user = ::core::mem::take(&mut ctx.runtime_options.eval.script); - let mut script = Vec::with_capacity(user.len() + 6 + bootstrap.len()); + let mut script = String::with_capacity(user.len() + 40 + bootstrap.len()); if !user.is_empty() { - // `node -i -e`: run user code first, then the REPL (useGlobal=true). - // Block-wrap user code so its const/let can't collide with the - // bootstrap's top-level vars; static import/export thus errors (as in Node). - script.extend_from_slice(b"{\n"); - script.extend_from_slice(&user); - script.extend_from_slice(b"\n};\n"); + // `node -i -e`: pass the user script as DATA (a JSON string + // literal), never as spliced code — the bootstrap runs it via + // vm.runInThisContext after REPL.start(), matching Node's + // internal/main/repl.js order. Splicing code would let a + // user-side syntax error / unterminated `` ` `` swallow the + // bootstrap and would block-scope `-e` declarations away. + let json = bun_core::fmt::format_json_string_utf8(&user, Default::default()); + write!(script, "const __BUN_EVAL_SCRIPT__ = {json};\n").unwrap_or_oom(); } - script.extend_from_slice(bootstrap); - ctx.runtime_options.eval.script = script.into_boxed_slice(); + // SAFETY: embedded builtin sources are UTF-8 by construction. + script.push_str(unsafe { ::core::str::from_utf8_unchecked(bootstrap) }); + ctx.runtime_options.eval.script = script.into_bytes().into_boxed_slice(); Self::exec_eval(ctx) } diff --git a/test/expectations.txt b/test/expectations.txt index d1a7aa45903e..c9f66ced347b 100644 --- a/test/expectations.txt +++ b/test/expectations.txt @@ -90,16 +90,42 @@ test/js/bun/spawn/spawn-maxbuf.test.ts [ FLAKY ] [ ASAN ] test/js/bun/http/req-url-leak.test.ts [ LEAK ] # req.url doesn't leak memory [ ASAN ] test/js/bun/io/bun-write-leak.test.ts [ LEAK ] # Bun.write should not leak the output data -# node:repl vendored suite (85 tests, all passing on Linux/macOS; see PR #31827). -# These eight time out only on the Windows lanes and need Windows-side debugging. -[ WINDOWS ] test/js/node/test/parallel/test-repl-mode.js [ FAIL ] # hangs (timeout) on all Windows lanes; needs Windows-side debugging -[ WINDOWS ] test/js/node/test/parallel/test-repl-load-multiline-no-trailing-newline.js [ FAIL ] # hangs (timeout) on all Windows lanes; needs Windows-side debugging -[ WINDOWS ] test/js/node/test/parallel/test-repl-tab-complete-require.js [ FAIL ] # hangs (timeout) on all Windows lanes; needs Windows-side debugging -[ WINDOWS ] test/js/node/test/parallel/test-repl-inspector.js [ FAIL ] # hangs (timeout) on all Windows lanes; needs Windows-side debugging -[ WINDOWS ] test/js/node/test/parallel/test-repl-load-multiline.js [ FAIL ] # hangs (timeout) on all Windows lanes; needs Windows-side debugging -[ WINDOWS ] test/js/node/test/parallel/test-repl-pretty-stack.js [ FAIL ] # hangs (timeout) on all Windows lanes; needs Windows-side debugging -[ WINDOWS ] test/js/node/test/parallel/test-repl-preview-newlines.js [ FAIL ] # hangs (timeout) on all Windows lanes; needs Windows-side debugging -[ WINDOWS ] test/js/node/test/parallel/test-repl-tab-complete-import.js [ FAIL ] # hangs (timeout) on all Windows lanes; needs Windows-side debugging +# node:repl vendored suite (107 tests; see PR #31827). 85 pass on Linux/macOS. +# The eight below time out only on the Windows lanes: they use in-memory +# ArrayStream (no ConPTY), so the hangs are JS-level, likely (A) `.load` with a +# backslash path in terminal-mode write(), (B) globalPaths readdir walk in the +# require/import completer, (C) inspector/preview stubs — not console I/O. +[ WINDOWS ] test/js/node/test/parallel/test-repl-mode.js [ FAIL ] # (C) inspector/preview stub — testStrictModeTerminal +[ WINDOWS ] test/js/node/test/parallel/test-repl-load-multiline-no-trailing-newline.js [ FAIL ] # (A) .load with backslash path +[ WINDOWS ] test/js/node/test/parallel/test-repl-tab-complete-require.js [ FAIL ] # (B) globalPaths readdir walk +[ WINDOWS ] test/js/node/test/parallel/test-repl-inspector.js [ FAIL ] # (C) inspector/preview stub +[ WINDOWS ] test/js/node/test/parallel/test-repl-load-multiline.js [ FAIL ] # (A) .load with backslash path +[ WINDOWS ] test/js/node/test/parallel/test-repl-pretty-stack.js [ FAIL ] # (A) .load with backslash path +[ WINDOWS ] test/js/node/test/parallel/test-repl-preview-newlines.js [ FAIL ] # (C) inspector/preview stub +[ WINDOWS ] test/js/node/test/parallel/test-repl-tab-complete-import.js [ FAIL ] # (B) globalPaths readdir walk +# 22 remaining upstream divergences (kept in-tree so gaps are tracked): +test/js/node/test/parallel/test-repl-autocomplete.js [ FAIL ] # result previews need inspector side-effect-free eval +test/js/node/test/parallel/test-repl-cli-eval.js [ FAIL ] # CLI child-process behavior divergence +test/js/node/test/parallel/test-repl-custom-eval-previews.js [ FAIL ] # result previews need inspector side-effect-free eval +test/js/node/test/parallel/test-repl-domain.js [ FAIL ] # node:domain integration incomplete +test/js/node/test/parallel/test-repl-history-navigation.js [ FAIL ] # result previews need inspector side-effect-free eval +test/js/node/test/parallel/test-repl-import-referrer.js [ FAIL ] # inspect renders module namespaces differently (JSC) +test/js/node/test/parallel/test-repl-pretty-custom-stack.js [ FAIL ] # JSC stack frame format differs from V8 +test/js/node/test/parallel/test-repl-preview.js [ FAIL ] # result previews need inspector side-effect-free eval +test/js/node/test/parallel/test-repl-require.js [ FAIL ] # REPL module resolution edge cases +test/js/node/test/parallel/test-repl-reverse-search.js [ FAIL ] # result previews need inspector side-effect-free eval +test/js/node/test/parallel/test-repl-sigint-nested-eval.js [ FAIL ] # SIGINT-during-eval not interrupting vm execution yet +test/js/node/test/parallel/test-repl-sigint.js [ FAIL ] # SIGINT-during-eval not interrupting vm execution yet +test/js/node/test/parallel/test-repl-strict-mode-previews.js [ FAIL ] # result previews need inspector side-effect-free eval +test/js/node/test/parallel/test-repl-tab-complete-nested-repls.js [ FAIL ] # completion subtests: getters/proxies edge cases +test/js/node/test/parallel/test-repl-tab-complete-unary-expressions.js [ FAIL ] # completion subtests: getters/proxies edge cases +test/js/node/test/parallel/test-repl-tab-complete.js [ FAIL ] # completion subtests: lexical-scope names, getters/proxies edge cases +test/js/node/test/parallel/test-repl-top-level-await.js [ FAIL ] # JSC syntax-error wording differs from V8 +test/js/node/test/parallel/test-repl-unsafe-array-iteration.js [ FAIL ] # JSC error message wording differs from V8 +test/js/node/test/parallel/test-repl-unsupported-option.js [ FAIL ] # --input-type validation not implemented +test/js/node/test/parallel/test-repl-user-error-handler.js [ FAIL ] # JSC stack frame format differs from V8 +test/js/node/test/parallel/test-repl.js [ FAIL ] # large integration test; multiple remaining divergences +test/js/node/test/sequential/test-repl-timeout-throw.js [ FAIL ] # SIGINT-during-eval not interrupting vm execution yet # Windows-only gaps in named-pipe / socket teardown for ported Node net tests # (these pass on Linux and macOS): half-close (FIN) handling on named pipes, diff --git a/test/js/bun/repl/repl.test.ts b/test/js/bun/repl/repl.test.ts index addd77233109..37ae53e3eb3e 100644 --- a/test/js/bun/repl/repl.test.ts +++ b/test/js/bun/repl/repl.test.ts @@ -1250,21 +1250,149 @@ describe.skipIf(isWindows)("REPL history file permissions", () => { }); }); -// `node -i -e 'code'`: eval first, then REPL with the eval'd globals visible. -// `__commonJS` collides with the bootstrap unless the user script is block-wrapped. -test("--interactive -e runs the eval first and enters the REPL with its globals visible", async () => { - await using proc = Bun.spawn({ - cmd: [bunExe(), "--interactive", "-e", "const __commonJS = 0; globalThis.fromEval = 42"], - env: { ...bunEnv, NO_COLOR: "1" }, - stdin: Buffer.from("fromEval\n.exit\n"), - stdout: "pipe", - stderr: "pipe", +describe("--interactive", () => { + const env = { ...bunEnv, NO_COLOR: "1", NODE_REPL_HISTORY: "" }; + + async function runInteractive(extra: string[], stdin: string, opts: { cwd?: string; env?: any } = {}) { + await using proc = Bun.spawn({ + cmd: [bunExe(), "--interactive", ...extra], + env: { ...env, ...opts.env }, + cwd: opts.cwd, + // Closing stdin (EOF) exits the REPL; `.exit` adds latency on debug builds. + stdin: Buffer.from(stdin), + stdout: "pipe", + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + return { stdout, stderr, exitCode }; + } + + test("prints a Bun-branded banner, not 'Welcome to Node.js'", async () => { + const { stdout, stderr, exitCode } = await runInteractive([], ""); + expect({ stdout, stderr }).toEqual({ + stdout: expect.stringMatching(/^Welcome to Bun v\d+\.\d+\.\d+.*\(Node\.js-compatible REPL/), + stderr: expect.not.stringContaining("error"), + }); + expect(stdout).not.toContain("Welcome to Node.js"); + expect(exitCode).toBe(0); + }); + + // `node -i -e 'code'`: -e runs as a separate Script after REPL.start(), so + // `var`/`function` declarations land on globalThis and are visible in the REPL. + test("-e runs after REPL start; var/function declarations are visible", async () => { + const { stdout, stderr, exitCode } = await runInteractive( + ["-e", "var fromVar = 1; function f() { return 42 }"], + "fromVar + f()\n", + ); + expect(stdout).toContain("43"); + expect(stderr).not.toContain("error"); + expect(exitCode).toBe(0); + }); + + test("-e with a syntax error is reported and the REPL still starts", async () => { + const { stdout, stderr, exitCode } = await runInteractive(["-e", "console.log(1"], "2+2\n"); + expect(stdout).toContain("Welcome to Bun"); + expect(stdout).toContain("4"); + // The error is reported against the user's [eval] script, not the bootstrap. + expect(stdout + stderr).toMatch(/SyntaxError/); + expect(stdout + stderr).not.toMatch(/node-repl|createInternalRepl|__BUN_EVAL_SCRIPT__/); + expect(exitCode).toBe(0); + }); + + test.each(["/*", "const x=`foo"])( + "-e with an unterminated template/comment cannot swallow the bootstrap (%j)", + async bad => { + const { stdout, stderr } = await runInteractive(["-e", bad], ""); + expect(stdout).toContain("Welcome to Bun"); + expect(stdout + stderr).toMatch(/SyntaxError/); + }, + ); + + // Node silently ignores `-i` when a script positional is present. + test("with a script positional runs the script and does not enter the REPL", async () => { + using dir = tempDir("interactive-script", { "foo.js": `console.log("script-ran")` }); + const { stdout, stderr, exitCode } = await runInteractive(["foo.js"], "1+1\n", { cwd: String(dir) }); + expect(stdout).toContain("script-ran"); + expect(stdout).not.toContain("Welcome"); + expect(stdout).not.toContain("> "); + expect(stderr).not.toContain("error"); + expect(exitCode).toBe(0); + }); + + // Documented "for now" deviation: `-p` wins over `--interactive`. + test("-p wins over --interactive (prints, no REPL)", async () => { + const { stdout, stderr, exitCode } = await runInteractive(["-p", "1+1"], "999\n"); + expect(stdout.trim()).toBe("2"); + expect(stdout).not.toContain("Welcome"); + expect(stderr).not.toContain("error"); + expect(exitCode).toBe(0); + }); + + test("NODE_REPL_EXTERNAL_MODULE replaces the built-in REPL", async () => { + using dir = tempDir("ext-repl", { "ext.js": `console.log("external-repl-42")` }); + const { stdout, stderr, exitCode } = await runInteractive([], "", { + cwd: String(dir), + env: { NODE_REPL_EXTERNAL_MODULE: "./ext.js" }, + }); + expect(stdout).toContain("external-repl-42"); + expect(stdout).not.toContain("Welcome"); + expect(stderr).not.toContain("error"); + expect(exitCode).toBe(0); + }); +}); + +describe("node:repl process-global side effects", () => { + const env = { ...bunEnv, NO_COLOR: "1" }; + + // Known limitation until process.addUncaughtExceptionCaptureCallback is + // implemented natively: the shim occupies the exclusive capture slot for the + // process lifetime. It must NOT displace a user callback installed BEFORE the + // first repl.start(), and unclaimed errors must still reach an + // 'uncaughtException' listener with the origin arg. + test("uncaught-exception capture shim defers to a pre-installed user callback and passes origin", async () => { + const script = ` + let userGot; + process.setUncaughtExceptionCaptureCallback(e => { userGot = e.message; }); + const repl = require("node:repl"); + const { PassThrough } = require("node:stream"); + const inp = new PassThrough(), out = new PassThrough(); out.resume(); + const r = repl.start({ input: inp, output: out, terminal: false, prompt: "" }); + r.close(); + let listenerOrigin; + process.on("uncaughtException", (e, origin) => { listenerOrigin = origin; }); + setImmediate(() => { throw new Error("boom"); }); + setImmediate(() => setImmediate(() => { + console.log("userGot=" + userGot); + // The user callback owns the slot; REPL didn't displace it. + process.exit(0); + })); + `; + await using proc = Bun.spawn({ cmd: [bunExe(), "-e", script], env, stdout: "pipe", stderr: "pipe" }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stdout).toContain("userGot=boom"); + expect(stderr).not.toContain("ALREADY_SET"); + expect(exitCode).toBe(0); + }); + + test("REPL survives a tampered RegExp.prototype[Symbol.split]", async () => { + const script = ` + const repl = require("node:repl"); + const { PassThrough } = require("node:stream"); + const inp = new PassThrough(), out = new PassThrough(); + let buf = ""; out.on("data", d => buf += d); + const r = repl.start({ input: inp, output: out, terminal: false, prompt: "> " }); + r.on("exit", () => { console.log(buf); process.exit(0); }); + inp.write("RegExp.prototype[Symbol.split] = () => { throw 0 }\\n"); + inp.write("oops\\n"); + inp.write("1+1\\n"); + inp.end(); + `; + await using proc = Bun.spawn({ cmd: [bunExe(), "-e", script], env, stdout: "pipe", stderr: "pipe" }); + const [stdout, , exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stdout).toContain("Uncaught ReferenceError"); + expect(stdout).toContain("> 2"); + expect(exitCode).toBe(0); }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect(stdout).toContain("> 42"); - expect(stderr).not.toContain("has already been declared"); - expect(stderr).not.toContain("error"); - expect(exitCode).toBe(0); }); // JSC's Error#stack is an own data property (V8's is an accessor), so a frozen diff --git a/test/js/node/test/parallel/test-repl-autocomplete.js b/test/js/node/test/parallel/test-repl-autocomplete.js new file mode 100644 index 000000000000..de801a8e12f0 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-autocomplete.js @@ -0,0 +1,219 @@ +'use strict'; + +// Flags: --expose-internals + +const common = require('../common'); +const stream = require('stream'); +const REPL = require('internal/repl'); +const assert = require('assert'); +const fs = require('fs'); +const { inspect } = require('util'); + +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +process.throwDeprecation = true; + +const defaultHistoryPath = tmpdir.resolve('.node_repl_history'); + +// Create an input stream specialized for testing an array of actions +class ActionStream extends stream.Stream { + run(data) { + const _iter = data[Symbol.iterator](); + const doAction = () => { + const next = _iter.next(); + if (next.done) { + // Close the repl. Note that it must have a clean prompt to do so. + this.emit('keypress', '', { ctrl: true, name: 'd' }); + return; + } + const action = next.value; + + if (typeof action === 'object') { + this.emit('keypress', '', action); + } else { + this.emit('data', `${action}`); + } + setImmediate(doAction); + }; + doAction(); + } + resume() {} + pause() {} +} +ActionStream.prototype.readable = true; + +// Mock keys +const ENTER = { name: 'enter' }; +const UP = { name: 'up' }; +const DOWN = { name: 'down' }; +const LEFT = { name: 'left' }; +const RIGHT = { name: 'right' }; +const BACKSPACE = { name: 'backspace' }; +const TABULATION = { name: 'tab' }; +const WORD_LEFT = { name: 'left', ctrl: true }; +const WORD_RIGHT = { name: 'right', ctrl: true }; +const GO_TO_END = { name: 'end' }; +const SIGINT = { name: 'c', ctrl: true }; +const ESCAPE = { name: 'escape', meta: true }; + +const prompt = '> '; + +const tests = [ + { + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + test: (function*() { + // Deleting Array iterator should not break history feature. + // + // Using a generator function instead of an object to allow the test to + // keep iterating even when Array.prototype[Symbol.iterator] has been + // deleted. + yield 'const ArrayIteratorPrototype ='; + yield ' Object.getPrototypeOf(Array.prototype[Symbol.iterator]());'; + yield ENTER; + yield 'const {next} = ArrayIteratorPrototype;'; + yield ENTER; + yield 'const realArrayIterator = Array.prototype[Symbol.iterator];'; + yield ENTER; + yield 'delete Array.prototype[Symbol.iterator];'; + yield ENTER; + yield 'delete ArrayIteratorPrototype.next;'; + yield ENTER; + yield UP; + yield UP; + yield DOWN; + yield DOWN; + yield 'fu'; + yield 'n'; + yield RIGHT; + yield BACKSPACE; + yield LEFT; + yield LEFT; + yield 'A'; + yield BACKSPACE; + yield GO_TO_END; + yield BACKSPACE; + yield WORD_LEFT; + yield WORD_RIGHT; + yield ESCAPE; + yield ENTER; + yield 'require("./'; + yield TABULATION; + yield SIGINT; + yield 'import("./'; + yield TABULATION; + yield SIGINT; + yield 'Array.proto'; + yield RIGHT; + yield '.pu'; + yield ENTER; + yield 'ArrayIteratorPrototype.next = next;'; + yield ENTER; + yield 'Array.prototype[Symbol.iterator] = realArrayIterator;'; + yield ENTER; + })(), + expected: [], + clean: false + }, +]; +const numtests = tests.length; + +const runTestWrap = common.mustCall(runTest, numtests); + +function cleanupTmpFile() { + try { + // Write over the file, clearing any history + fs.writeFileSync(defaultHistoryPath, ''); + } catch (err) { + if (err.code === 'ENOENT') return true; + throw err; + } + return true; +} + +function runTest() { + const opts = tests.shift(); + if (!opts) return; // All done + + const { expected, skip } = opts; + + // Test unsupported on platform. + if (skip) { + setImmediate(runTestWrap, true); + return; + } + const lastChunks = []; + let i = 0; + + REPL.createInternalRepl(opts.env, { + input: new ActionStream(), + output: new stream.Writable({ + write: common.mustCallAtLeast((chunk, _, next) => { + const output = chunk.toString(); + + if (!opts.showEscapeCodes && + (output[0] === '\x1B' || /^[\r\n]+$/.test(output))) { + return next(); + } + + lastChunks.push(output); + + if (expected.length && !opts.checkTotal) { + try { + assert.strictEqual(output, expected[i]); + } catch (e) { + console.error(`Failed test # ${numtests - tests.length}`); + console.error('Last outputs: ' + inspect(lastChunks, { + breakLength: 5, colors: true + })); + throw e; + } + // TODO(BridgeAR): Auto close on last chunk! + i++; + } + + next(); + }), + }), + allowBlockingCompletions: true, + completer: opts.completer, + prompt, + useColors: false, + preview: opts.preview, + terminal: true + }, common.mustCall((err, repl) => { + if (err) { + console.error(`Failed test # ${numtests - tests.length}`); + throw err; + } + + repl.once('close', common.mustCall(() => { + if (opts.clean) + cleanupTmpFile(); + + if (opts.checkTotal) { + assert.deepStrictEqual(lastChunks, expected); + } else if (expected.length !== i) { + console.error(tests[numtests - tests.length - 1]); + throw new Error(`Failed test # ${numtests - tests.length}`); + } + + setImmediate(runTestWrap, true); + })); + + if (opts.columns) { + Object.defineProperty(repl, 'columns', { + value: opts.columns, + enumerable: true + }); + } + repl.input.run(opts.test); + })); +} + +// run the tests +runTest(); diff --git a/test/js/node/test/parallel/test-repl-cli-eval.js b/test/js/node/test/parallel/test-repl-cli-eval.js new file mode 100644 index 000000000000..6069a20957bd --- /dev/null +++ b/test/js/node/test/parallel/test-repl-cli-eval.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const child_process = require('child_process'); +const assert = require('assert'); + +// Regression test for https://github.com/nodejs/node/issues/27575: +// module.id === '' in the REPL. + +for (const extraFlags of [[], ['-e', '42']]) { + const flags = ['--interactive', ...extraFlags]; + const proc = child_process.spawn(process.execPath, flags, { + stdio: ['pipe', 'pipe', 'inherit'] + }); + proc.stdin.write('module.id\n.exit\n'); + + let stdout = ''; + proc.stdout.setEncoding('utf8'); + proc.stdout.on('data', (chunk) => stdout += chunk); + proc.stdout.on('end', common.mustCall(() => { + assert(stdout.includes(''), `stdout: ${stdout}`); + })); +} diff --git a/test/js/node/test/parallel/test-repl-custom-eval-previews.js b/test/js/node/test/parallel/test-repl-custom-eval-previews.js new file mode 100644 index 000000000000..5fea4e3d8c5a --- /dev/null +++ b/test/js/node/test/parallel/test-repl-custom-eval-previews.js @@ -0,0 +1,92 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { describe, it } = require('node:test'); + +common.skipIfInspectorDisabled(); + +const { startNewREPLServer } = require('../common/repl'); + +const testingReplPrompt = '_REPL_TESTING_PROMPT_>'; + +// Processes some input in a REPL instance and returns a promise that +// resolves to the produced output (as a string). +function getReplRunOutput(inputStr, replOptions) { + return new Promise((resolve) => { + const { replServer, input, output } = startNewREPLServer({ prompt: testingReplPrompt, ...replOptions }); + + output.accumulator = ''; + + output.write = (chunk) => { + output.accumulator += chunk; + // The prompt appears after the input has been processed + if (output.accumulator.includes(testingReplPrompt)) { + replServer.close(); + resolve(output.accumulator); + } + }; + + input.emit('data', inputStr); + + input.run(['']); + }); +} + +describe('with previews', () => { + it("doesn't show previews by default", async () => { + const input = "'Hello custom' + ' eval World!'"; + const output = await getReplRunOutput( + input, + { + terminal: true, + eval: (code, _ctx, _replRes, cb) => cb(null, eval(code)), + }, + ); + const lines = getSingleCommandLines(output); + assert.match(lines.command, /^'Hello custom' \+ ' eval World!'/); + assert.match(lines.prompt, new RegExp(`${RegExp.escape(testingReplPrompt)}$`)); + assert.strictEqual(lines.result, "'Hello custom eval World!'"); + assert.strictEqual(lines.preview, undefined); + }); + + it('does show previews if `preview` is set to `true`', async () => { + const input = "'Hello custom' + ' eval World!'"; + const output = await getReplRunOutput( + input, + { + terminal: true, + eval: (code, _ctx, _replRes, cb) => cb(null, eval(code)), + preview: true, + }, + ); + const lines = getSingleCommandLines(output); + assert.match(lines.command, /^'Hello custom' \+ ' eval World!'/); + assert.match(lines.prompt, new RegExp(`${RegExp.escape(testingReplPrompt)}$`)); + assert.strictEqual(lines.result, "'Hello custom eval World!'"); + assert.match(lines.preview, /'Hello custom eval World!'/); + }); +}); + +function getSingleCommandLines(output) { + const outputLines = output.split('\n'); + + // The first line contains the command being run + const command = outputLines.shift(); + + // The last line contains the prompt (asking for some new input) + const prompt = outputLines.pop(); + + // The line before the last one contains the result of the command + const result = outputLines.pop(); + + // The line before that contains the preview of the command + const preview = outputLines.shift(); + + return { + command, + prompt, + result, + preview, + }; +} diff --git a/test/js/node/test/parallel/test-repl-domain.js b/test/js/node/test/parallel/test-repl-domain.js new file mode 100644 index 000000000000..b7c8d95dd26c --- /dev/null +++ b/test/js/node/test/parallel/test-repl-domain.js @@ -0,0 +1,49 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +'use strict'; +require('../common'); +const { startNewREPLServer } = require('../common/repl'); +const ArrayStream = require('../common/arraystream'); + +const stream = new ArrayStream(); + +startNewREPLServer({ + input: stream, + output: stream, + terminal: false, +}); + +stream.write = function(data) { + // Don't use assert for this because the domain might catch it, and + // give a false negative. Don't throw, just print and exit. + if (data === 'OK\n') { + console.log('ok'); + } else { + console.error(data); + process.exit(1); + } +}; + +stream.run([ + 'require("domain").create().on("error", function() { console.log("OK") })' + + '.run(function() { throw new Error("threw") })', +]); diff --git a/test/js/node/test/parallel/test-repl-history-navigation.js b/test/js/node/test/parallel/test-repl-history-navigation.js new file mode 100644 index 000000000000..88c1058c0b29 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-history-navigation.js @@ -0,0 +1,933 @@ +'use strict'; + +// Flags: --expose-internals + +const common = require('../common'); +const stream = require('stream'); +const REPL = require('internal/repl'); +const assert = require('assert'); +const fs = require('fs'); +const { inspect } = require('util'); + +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +process.throwDeprecation = true; +process.on('warning', common.mustNotCall()); + +const defaultHistoryPath = tmpdir.resolve('.node_repl_history'); + +// Create an input stream specialized for testing an array of actions +class ActionStream extends stream.Stream { + run(data) { + const _iter = data[Symbol.iterator](); + const doAction = () => { + const next = _iter.next(); + if (next.done) { + // Close the repl. Note that it must have a clean prompt to do so. + this.emit('keypress', '', { ctrl: true, name: 'd' }); + return; + } + const action = next.value; + + if (typeof action === 'object') { + this.emit('keypress', '', action); + } else { + this.emit('data', `${action}`); + } + setImmediate(doAction); + }; + doAction(); + } + resume() {} + pause() {} +} +ActionStream.prototype.readable = true; + +// Mock keys +const ENTER = { name: 'enter' }; +const UP = { name: 'up' }; +const DOWN = { name: 'down' }; +const LEFT = { name: 'left' }; +const RIGHT = { name: 'right' }; +const DELETE = { name: 'delete' }; +const BACKSPACE = { name: 'backspace' }; +const WORD_LEFT = { name: 'left', ctrl: true }; +const WORD_RIGHT = { name: 'right', ctrl: true }; +const GO_TO_END = { name: 'end' }; +const DELETE_WORD_LEFT = { name: 'backspace', ctrl: true }; +const SIGINT = { name: 'c', ctrl: true }; +const ESCAPE = { name: 'escape', meta: true }; + +const prompt = '> '; +const WAIT = '€'; + +const prev = process.features.inspector; + +let completions = 0; + +const tests = [ + { // Creates few history to navigate for + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + test: [ 'let ab = 45', ENTER, + '555 + 909', ENTER, + 'let autocompleteMe = 123', ENTER, + '{key : {key2 :[] }}', ENTER, + 'Array(100).fill(1).map((e, i) => i ** i)', LEFT, LEFT, DELETE, + '2', ENTER], + expected: [], + clean: false + }, + { + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + test: [UP, UP, UP, UP, UP, UP, DOWN, DOWN, DOWN, DOWN, DOWN, DOWN], + expected: [prompt, + `${prompt}Array(100).fill(1).map((e, i) => i ** 2)`, + prev && '\n// [ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, ' + + '144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529,' + + ' 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, ' + + '1225, 1296, 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936,' + + ' 2025, 2116, 2209,...', + `${prompt}{key : {key2 :[] }}`, + prev && '\n// { key: { key2: [] } }', + `${prompt}let autocompleteMe = 123`, + `${prompt}555 + 909`, + prev && '\n// 1464', + `${prompt}let ab = 45`, + prompt, + `${prompt}let ab = 45`, + `${prompt}555 + 909`, + prev && '\n// 1464', + `${prompt}let autocompleteMe = 123`, + `${prompt}{key : {key2 :[] }}`, + prev && '\n// { key: { key2: [] } }', + `${prompt}Array(100).fill(1).map((e, i) => i ** 2)`, + prev && '\n// [ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, ' + + '144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529,' + + ' 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, ' + + '1225, 1296, 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936,' + + ' 2025, 2116, 2209,...', + prompt].filter((e) => typeof e === 'string'), + clean: false + }, + { // Creates more history entries to navigate through. + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + test: [ + '555 + 909', ENTER, // Add a duplicate to the history set. + 'const foo = true', ENTER, + '555n + 111n', ENTER, + '5 + 5', ENTER, + '55 - 13 === 42', ENTER, + ], + expected: [], + clean: false + }, + { + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + checkTotal: true, + preview: false, + showEscapeCodes: true, + test: [ + '55', UP, UP, UP, UP, UP, UP, UP, ENTER, + ], + expected: [ + '\x1B[1G', '\x1B[0J', prompt, '\x1B[3G', + // '55' + '5', '5', + // UP + '\x1B[1G', '\x1B[0J', + '> 55 - 13 === 42', '\x1B[17G', + // UP - skipping 5 + 5 + '\x1B[1G', '\x1B[0J', + '> 555n + 111n', '\x1B[14G', + // UP - skipping const foo = true + '\x1B[1G', '\x1B[0J', + '> 555 + 909', '\x1B[12G', + // UP, UP + // UPs at the end of the history reset the line to the original input. + '\x1B[1G', '\x1B[0J', + '> 55', '\x1B[5G', + // ENTER + '\r\n', '55\n', + '\x1B[1G', '\x1B[0J', + '> ', '\x1B[3G', + '\r\n', + ], + clean: true + }, + { + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + skip: !process.features.inspector, + test: [ + // あ is a full width character with a length of one. + // 🐕 is a full width character with a length of two. + // 𐐷 is a half width character with the length of two. + // '\u0301', '0x200D', '\u200E' are zero width characters. + `const x1 = '${'あ'.repeat(124)}'`, ENTER, // Fully visible + ENTER, + `const y1 = '${'あ'.repeat(125)}'`, ENTER, // Cut off + ENTER, + `const x2 = '${'🐕'.repeat(124)}'`, ENTER, // Fully visible + ENTER, + `const y2 = '${'🐕'.repeat(125)}'`, ENTER, // Cut off + ENTER, + `const x3 = '${'𐐷'.repeat(248)}'`, ENTER, // Fully visible + ENTER, + `const y3 = '${'𐐷'.repeat(249)}'`, ENTER, // Cut off + ENTER, + `const x4 = 'a${'\u0301'.repeat(1000)}'`, ENTER, // á + ENTER, + `const ${'veryLongName'.repeat(30)} = 'I should be previewed'`, + ENTER, + 'const e = new RangeError("visible\\ninvisible")', + ENTER, + 'e', + ENTER, + 'veryLongName'.repeat(30), + ENTER, + `${'\x1B[90m \x1B[39m'.repeat(229)} aut`, + ESCAPE, + ENTER, + `${' '.repeat(230)} aut`, + ESCAPE, + ENTER, + ], + expected: [], + clean: false + }, + { + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + columns: 250, + checkTotal: true, + showEscapeCodes: true, + skip: !process.features.inspector, + test: [ + UP, + UP, + UP, + WORD_LEFT, + UP, + BACKSPACE, + 'x1', + BACKSPACE, + '2', + BACKSPACE, + '3', + BACKSPACE, + '4', + DELETE_WORD_LEFT, + 'y1', + BACKSPACE, + '2', + BACKSPACE, + '3', + SIGINT, + ], + // A = Cursor n up + // B = Cursor n down + // C = Cursor n forward + // D = Cursor n back + // G = Cursor to column n + // J = Erase in screen; 0 = right; 1 = left; 2 = total + // K = Erase in line; 0 = right; 1 = left; 2 = total + expected: [ + // 0. Start + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + // 1. UP + // This exceeds the maximum columns (250): + // Whitespace + prompt + ' // '.length + 'autocompleteMe'.length + // 230 + 2 + 4 + 14 + '\x1B[1G', '\x1B[0J', + `${prompt}${' '.repeat(230)} aut`, '\x1B[237G', + ' // ocompleteMe', '\x1B[237G', + '\n// 123', '\x1B[237G', + '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A', + '\x1B[0K', + // 2. UP + '\x1B[1G', '\x1B[0J', + `${prompt}${' '.repeat(229)} aut`, '\x1B[236G', + ' // ocompleteMe', '\x1B[236G', + '\n// 123', '\x1B[236G', + '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A', + // Preview cleanup + '\x1B[0K', + // 3. UP + '\x1B[1G', '\x1B[0J', + // 'veryLongName'.repeat(30).length === 360 + // prompt.length === 2 + // 360 % 250 + 2 === 112 (+1) + `${prompt}${'veryLongName'.repeat(30)}`, '\x1B[113G', + // "// 'I should be previewed'".length + 86 === 112 (+1) + "\n// 'I should be previewed'", '\x1B[113G', '\x1B[1A', + // Preview cleanup + '\x1B[1B', '\x1B[2K', '\x1B[1A', + // 4. WORD LEFT + // Almost identical as above. Just one extra line. + // Math.floor(360 / 250) === 1 + '\x1B[1A', + '\x1B[1G', '\x1B[0J', + `${prompt}${'veryLongName'.repeat(30)}`, '\x1B[3G', '\x1B[1A', + '\x1B[1B', "\n// 'I should be previewed'", '\x1B[3G', '\x1B[2A', + // Preview cleanup + '\x1B[2B', '\x1B[2K', '\x1B[2A', + // 5. UP + '\x1B[1G', '\x1B[0J', + `${prompt}e`, '\x1B[4G', + // '// RangeError: visible'.length - 19 === 3 (+1) + '\n// RangeError: visible', '\x1B[4G', '\x1B[1A', + // Preview cleanup + '\x1B[1B', '\x1B[2K', '\x1B[1A', + // 6. Backspace + '\x1B[1G', '\x1B[0J', + '> ', '\x1B[3G', 'x', '1', + `\n// '${'あ'.repeat(124)}'`, + '\x1B[5G', '\x1B[1A', + '\x1B[1B', '\x1B[2K', '\x1B[1A', + '\x1B[1G', '\x1B[0J', + '> x', '\x1B[4G', '2', + `\n// '${'🐕'.repeat(124)}'`, + '\x1B[5G', '\x1B[1A', + '\x1B[1B', '\x1B[2K', '\x1B[1A', + '\x1B[1G', '\x1B[0J', + '> x', '\x1B[4G', '3', + `\n// '${'𐐷'.repeat(248)}'`, + '\x1B[5G', '\x1B[1A', + '\x1B[1B', '\x1B[2K', '\x1B[1A', + '\x1B[1G', '\x1B[0J', + '> x', '\x1B[4G', '4', + `\n// 'a${'\u0301'.repeat(1000)}'`, + '\x1B[5G', '\x1B[1A', + '\x1B[1B', '\x1B[2K', '\x1B[1A', + '\x1B[1G', '\x1B[0J', + '> ', '\x1B[3G', 'y', '1', + `\n// '${'あ'.repeat(121)}...`, + '\x1B[5G', '\x1B[1A', + '\x1B[1B', '\x1B[2K', '\x1B[1A', + '\x1B[1G', '\x1B[0J', + '> y', '\x1B[4G', '2', + `\n// '${'🐕'.repeat(121)}...`, + '\x1B[5G', '\x1B[1A', + '\x1B[1B', '\x1B[2K', '\x1B[1A', + '\x1B[1G', '\x1B[0J', + '> y', '\x1B[4G', '3', + `\n// '${'𐐷'.repeat(242)}...`, + '\x1B[5G', '\x1B[1A', + '\x1B[1B', '\x1B[2K', '\x1B[1A', + '\r\n', + '\x1B[1G', '\x1B[0J', + '> ', '\x1B[3G', + '\r\n', + ], + clean: true + }, + { + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + showEscapeCodes: true, + skip: !process.features.inspector, + checkTotal: true, + test: [ + 'au', + 't', + RIGHT, + BACKSPACE, + LEFT, + LEFT, + 'A', + BACKSPACE, + GO_TO_END, + BACKSPACE, + WORD_LEFT, + WORD_RIGHT, + ESCAPE, + ENTER, + UP, + LEFT, + ENTER, + UP, + ENTER, + ], + // C = Cursor n forward + // D = Cursor n back + // G = Cursor to column n + // J = Erase in screen; 0 = right; 1 = left; 2 = total + // K = Erase in line; 0 = right; 1 = left; 2 = total + expected: [ + // 0. + // 'a' + '\x1B[1G', '\x1B[0J', prompt, '\x1B[3G', 'a', + // 'u' + 'u', ' // tocompleteMe', '\x1B[5G', + '\n// 123', '\x1B[5G', + '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A', + // 't' - Cleanup + '\x1B[0K', + 't', ' // ocompleteMe', '\x1B[6G', + '\n// 123', '\x1B[6G', + '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A', + // 1. Right. Cleanup + '\x1B[0K', + 'ocompleteMe', + '\n// 123', '\x1B[17G', + '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A', + // 2. Backspace. Refresh + '\x1B[1G', '\x1B[0J', `${prompt}autocompleteM`, '\x1B[16G', + // Autocomplete and refresh? + ' // e', '\x1B[16G', + '\n// 123', '\x1B[16G', + '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A', + // 3. Left. Cleanup + '\x1B[0K', + '\x1B[1D', '\x1B[16G', ' // e', '\x1B[15G', + // 4. Left. Cleanup + '\x1B[16G', '\x1B[0K', '\x1B[15G', + '\x1B[1D', '\x1B[16G', ' // e', '\x1B[14G', + // 5. 'A' - Cleanup + '\x1B[16G', '\x1B[0K', '\x1B[14G', + // Refresh + '\x1B[1G', '\x1B[0J', `${prompt}autocompletAeM`, '\x1B[15G', + // 6. Backspace. Refresh + '\x1B[1G', '\x1B[0J', `${prompt}autocompleteM`, + '\x1B[14G', '\x1B[16G', ' // e', + '\x1B[14G', '\x1B[16G', ' // e', + '\x1B[14G', '\x1B[16G', + // 7. Go to end. Cleanup + '\x1B[0K', '\x1B[14G', '\x1B[2C', + 'e', + '\n// 123', '\x1B[17G', + '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A', + // 8. Backspace. Refresh + '\x1B[1G', '\x1B[0J', `${prompt}autocompleteM`, '\x1B[16G', + // Autocomplete + ' // e', '\x1B[16G', + '\n// 123', '\x1B[16G', + '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A', + // 9. Word left. Cleanup + '\x1B[0K', '\x1B[13D', '\x1B[16G', ' // e', '\x1B[3G', '\x1B[16G', + // 10. Word right. Cleanup + '\x1B[0K', '\x1B[3G', '\x1B[13C', ' // e', '\x1B[16G', + '\n// 123', '\x1B[16G', + '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A', + // 11. ESCAPE + '\x1B[0K', + // 12. ENTER + '\r\n', + 'Uncaught ReferenceError: autocompleteM is not defined\n', + '\x1B[1G', '\x1B[0J', + // 13. UP + prompt, '\x1B[3G', '\x1B[1G', '\x1B[0J', + `${prompt}autocompleteM`, '\x1B[16G', + ' // e', '\x1B[16G', + '\n// 123', '\x1B[16G', + '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A', + // 14. LEFT + '\x1B[0K', '\x1B[1D', '\x1B[16G', + ' // e', '\x1B[15G', '\x1B[16G', + // 15. ENTER + '\x1B[0K', '\x1B[15G', '\x1B[1C', + '\r\n', + 'Uncaught ReferenceError: autocompleteM is not defined\n', + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + // 16. UP + '\x1B[1G', '\x1B[0J', + `${prompt}autocompleteM`, '\x1B[16G', + ' // e', '\x1B[16G', + '\n// 123', '\x1B[16G', + '\x1B[1A', '\x1B[1B', '\x1B[2K', '\x1B[1A', + '\x1B[0K', + // 17. ENTER + 'e', '\r\n', + '123\n', + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + '\r\n', + ], + clean: true + }, + { + // Check changed inspection defaults. + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + skip: !process.features.inspector, + test: [ + 'util.inspect.replDefaults.showHidden', + ENTER, + ], + expected: [], + clean: false + }, + { + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + skip: !process.features.inspector, + checkTotal: true, + test: [ + '[ ]', + WORD_LEFT, + WORD_LEFT, + UP, + ' = true', + ENTER, + '[ ]', + ENTER, + ], + expected: [ + prompt, + '[', ' ', ']', + '\n// []', '\n// []', '\n// []', + '> util.inspect.replDefaults.showHidden', + '\n// false', + ' ', '=', ' ', 't', 'r', 'u', 'e', + 'true\n', + '> ', '[', ' ', ']', + '\n// [ [length]: 0 ]', + '[ [length]: 0 ]\n', + '> ', + ], + clean: true + }, + { + // Check that the completer ignores completions that are outdated. + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + completer(line, callback) { + if (line.endsWith(WAIT)) { + if (completions++ === 0) { + callback(null, [[`${WAIT}WOW`], line]); + } else { + setTimeout(callback, 1000, null, [[`${WAIT}WOW`], line]).unref(); + } + } else { + callback(null, [[' Always visible'], line]); + } + }, + skip: !process.features.inspector, + test: [ + WAIT, // The first call is awaited before new input is triggered! + BACKSPACE, + 's', + BACKSPACE, + WAIT, // The second call is not awaited. It won't trigger the preview. + BACKSPACE, + 's', + BACKSPACE, + ], + expected: [ + prompt, + WAIT, + ' // WOW', + prompt, + 's', + ' // Always visible', + prompt, + WAIT, + prompt, + 's', + ' // Always visible', + prompt, + ], + clean: true + }, + { + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + test: (function*() { + // Deleting Array iterator should not break history feature. + // + // Using a generator function instead of an object to allow the test to + // keep iterating even when Array.prototype[Symbol.iterator] has been + // deleted. + yield 'const ArrayIteratorPrototype ='; + yield ' Object.getPrototypeOf(Array.prototype[Symbol.iterator]());'; + yield ENTER; + yield 'const {next} = ArrayIteratorPrototype;'; + yield ENTER; + yield 'const realArrayIterator = Array.prototype[Symbol.iterator];'; + yield ENTER; + yield 'delete Array.prototype[Symbol.iterator];'; + yield ENTER; + yield 'delete ArrayIteratorPrototype.next;'; + yield ENTER; + yield UP; + yield UP; + yield DOWN; + yield DOWN; + yield 'fu'; + yield 'n'; + yield RIGHT; + yield BACKSPACE; + yield LEFT; + yield LEFT; + yield 'A'; + yield BACKSPACE; + yield GO_TO_END; + yield BACKSPACE; + yield WORD_LEFT; + yield WORD_RIGHT; + yield ESCAPE; + yield ENTER; + yield 'Array.proto'; + yield RIGHT; + yield '.pu'; + yield ENTER; + yield 'ArrayIteratorPrototype.next = next;'; + yield ENTER; + yield 'Array.prototype[Symbol.iterator] = realArrayIterator;'; + yield ENTER; + })(), + expected: [], + clean: false + }, + { + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + test: ['const util = {}', ENTER, + 'ut', RIGHT, ENTER], + expected: [ + prompt, ...'const util = {}', + 'undefined\n', + prompt, ...'ut', ...(prev ? [' // il', '\n// {}', + 'il', '\n// {}'] : ['il']), + '{}\n', + prompt, + ], + clean: false + }, + { + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + test: [ + 'const utilDesc = Reflect.getOwnPropertyDescriptor(globalThis, "util")', + ENTER, + 'globalThis.util = {}', ENTER, + 'ut', RIGHT, ENTER, + 'Reflect.defineProperty(globalThis, "util", utilDesc)', ENTER], + expected: [ + prompt, ...'const utilDesc = ' + + 'Reflect.getOwnPropertyDescriptor(globalThis, "util")', + 'undefined\n', + prompt, ...'globalThis.util = {}', + '{}\n', + prompt, ...'ut', ...(prev ? [' // il', 'il' ] : ['il']), + '{}\n', + prompt, ...'Reflect.defineProperty(globalThis, "util", utilDesc)', + 'true\n', + prompt, + ], + clean: false + }, + { + // Test that preview should not be removed when pressing ESCAPE key + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + skip: !process.features.inspector, + test: [ + '1+1', + ESCAPE, + ENTER, + ], + expected: [ + prompt, ...'1+1', + '\n// 2', + '\n// 2', + '2\n', + prompt, + ], + clean: false + }, + { + // Test that the multiline history is correctly navigated and it can be edited + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + skip: !process.features.inspector, + test: [ + 'let a = ``', + ENTER, + 'a = `I am a multiline strong', + ENTER, + 'which ends here`', + ENTER, + UP, + // press LEFT 19 times to reach the typo + ...Array(19).fill(LEFT), + BACKSPACE, + 'i', + ENTER, + ], + expected: [ + prompt, ...'let a = ``', + 'undefined\n', + prompt, ...'a = `I am a multiline strong', // New Line, the user pressed ENTER + '| ', + ...'which ends here`', // New Line, the user pressed ENTER + "'I am a multiline strong\\nwhich ends here'\n", // This is the result printed to the console + prompt, + `${prompt}a = \`I am a multiline strong`, // This is the history being shown and navigated + `\n| which ends here\``, + `${prompt}a = \`I am a multiline strong`, // This is the history being shown and navigated + `\n| which ends here\``, + + `${prompt}a = \`I am a multiline strng`, // This is the history being shown and edited + `\n| which ends here\``, + + `${prompt}a = \`I am a multiline string`, // This is the history being shown and edited + `\n| which ends here\``, + + `${prompt}a = \`I am a multiline string`, // This is the history being shown and edited + `\n| which ends here\``, + "'I am a multiline string\\nwhich ends here'\n", // This is the result printed to the console + prompt, + ], + clean: true + }, + { + // Test that the previous multiline history can only be accessed going through the entirety of the current + // One navigating its all lines first. + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + skip: !process.features.inspector, + test: [ + 'let b = ``', + ENTER, + 'b = `I am a multiline strong', + ENTER, + 'which ends here`', + ENTER, + 'let c = `I', + ENTER, + 'am another one`', + ENTER, + UP, + UP, + UP, + UP, + // press RIGHT 10 times to reach the typo + ...Array(10).fill(RIGHT), + BACKSPACE, + 'i', + ENTER, + ], + expected: [ + prompt, ...'let b = ``', + 'undefined\n', + prompt, ...'b = `I am a multiline strong', // New Line, the user pressed ENTER + '| ', + ...'which ends here`', // New Line, the user pressed ENTER + "'I am a multiline strong\\nwhich ends here'\n", // This is the result printed to the console + prompt, ...'let c = `I', // New Line, the user pressed ENTER + '| ', + ...'am another one`', // New Line, the user pressed ENTER + 'undefined\n', + prompt, + `${prompt}let c = \`I`, // This is the history being shown and navigated + `\n| am another one\``, + + `${prompt}let c = \`I`, // This is the history being shown and navigated + `\n| am another one\``, + + `${prompt}b = \`I am a multiline strong`, // This is the history being shown and edited + `\n| which ends here\``, + `${prompt}b = \`I am a multiline strong`, // This is the history being shown and edited + `\n| which ends here\``, + `${prompt}b = \`I am a multiline strng`, // This is the history being shown and edited + `\n| which ends here\``, + + `${prompt}b = \`I am a multiline string`, // This is the history being shown and edited + `\n| which ends here\``, + + `${prompt}b = \`I am a multiline string`, // This is the history being shown and edited + `\n| which ends here\``, + "'I am a multiline string\\nwhich ends here'\n", // This is the result printed to the console + prompt, + ], + clean: true + }, + { + // Test that we can recover from a line with a syntax error + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + skip: !process.features.inspector, + test: [ + 'let d = ``', + ENTER, + 'd = `I am a', + ENTER, + 'super', + ENTER, + 'broken` line\'', + ENTER, + UP, + BACKSPACE, + '`', + // press LEFT 6 times to reach the typo + ...Array(6).fill(LEFT), + BACKSPACE, + ENTER, + ], + expected: [ + prompt, ...'let d = ``', // New Line, the user pressed ENTER + 'undefined\n', + prompt, ...'d = `I am a', // New Line, the user pressed ENTER + '| ', + ...'super', // New Line, the user pressed ENTER + '| ', + ...'broken` line\'', // New Line, the user pressed ENTER + "[broken` line'\n" + + ' ^^^^\n' + + '\n' + + "Uncaught SyntaxError: Unexpected identifier 'line'\n" + + '] {\n' + + ' [stack]: [Getter/Setter],\n' + + ` [message]: "Unexpected identifier 'line'"\n` + + '}\n', + prompt, + `${prompt}d = \`I am a`, // This is the history being shown and edited + `\n| super`, + `\n| broken\` line'`, + + `${prompt}d = \`I am a`, // This is the history being shown and edited + `\n| super`, + '\n| broken` line', + '`', + + `${prompt}d = \`I am a`, // This is the history being shown and edited + `\n| super`, + `\n| broken line\``, + "'I am a\\nsuper\\nbroken line'\n", // This is the result printed to the console + prompt, + ], + clean: true + }, + { + // Test that multiline history is not duplicated + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + skip: !process.features.inspector, + test: [ + "let f = ''", + ENTER, + 'f = `multiline', + ENTER, + 'string`', + ENTER, // Finished issuing the multiline command + UP, + ENTER, // Trying to reissue the same command + UP, UP, UP, // Going back 3 times in the history, it should show the var definition + DOWN, DOWN, // Going down 2 times should show the multiline command only once + ], + expected: [ + prompt, + ...`let f = ''`, + 'undefined\n', + prompt, + ...'f = `multiline', + '| ', + ...'string`', + "'multiline\\nstring'\n", + prompt, + `${prompt}f = \`multiline`, + '\n| string`', + "'multiline\\nstring'\n", + prompt, + `${prompt}f = \`multiline`, + `\n| string\``, + `${prompt}f = \`multiline`, + `\n| string\``, + `${prompt}let f = ''`, + `${prompt}f = \`multiline`, + `\n| string\``, + prompt, + ], + clean: true + }, +]; +const numtests = tests.length; + +const runTestWrap = common.mustCall(runTest, numtests); + +function cleanupTmpFile() { + try { + // Write over the file, clearing any history + fs.writeFileSync(defaultHistoryPath, ''); + } catch (err) { + if (err.code === 'ENOENT') return true; + throw err; + } + return true; +} + +function runTest() { + const opts = tests.shift(); + if (!opts) return; // All done + + const { expected, skip } = opts; + + // Test unsupported on platform. + if (skip) { + setImmediate(runTestWrap, true); + return; + } + const lastChunks = []; + let i = 0; + + REPL.createInternalRepl(opts.env, { + input: new ActionStream(), + output: new stream.Writable({ + write: common.mustCallAtLeast((chunk, _, next) => { + const output = chunk.toString(); + + if (!opts.showEscapeCodes && + (output[0] === '\x1B' || /^[\r\n]+$/.test(output))) { + return next(); + } + + lastChunks.push(output); + + if (expected.length && !opts.checkTotal) { + try { + assert.strictEqual(output, expected[i]); + } catch (e) { + console.error(`Failed test # ${numtests - tests.length}`); + console.error('Last outputs: ' + inspect(lastChunks, { + breakLength: 5, colors: true + })); + throw e; + } + // TODO(BridgeAR): Auto close on last chunk! + i++; + } + + next(); + }), + }), + completer: opts.completer, + prompt, + useColors: false, + preview: opts.preview, + terminal: true + }, common.mustCall((err, repl) => { + if (err) { + console.error(`Failed test # ${numtests - tests.length}`); + throw err; + } + + repl.once('close', common.mustCall(() => { + if (opts.clean) + cleanupTmpFile(); + + if (opts.checkTotal) { + assert.deepStrictEqual(lastChunks, expected); + } else if (expected.length !== i) { + console.error(tests[numtests - tests.length - 1]); + throw new Error(`Failed test # ${numtests - tests.length}`); + } + + setImmediate(runTestWrap, true); + })); + + if (opts.columns) { + Object.defineProperty(repl, 'columns', { + value: opts.columns, + enumerable: true + }); + } + repl.input.run(opts.test); + })); +} + +// run the tests +runTest(); diff --git a/test/js/node/test/parallel/test-repl-import-referrer.js b/test/js/node/test/parallel/test-repl-import-referrer.js new file mode 100644 index 000000000000..8e242f01922d --- /dev/null +++ b/test/js/node/test/parallel/test-repl-import-referrer.js @@ -0,0 +1,26 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const fixtures = require('../common/fixtures'); + +const args = ['--interactive']; +const opts = { cwd: fixtures.path('es-modules') }; +const child = cp.spawn(process.execPath, args, opts); + +let output = ''; +child.stdout.setEncoding('utf8'); +child.stdout.on('data', (data) => { + output += data; +}); + +child.on('exit', common.mustCall(() => { + const result = output.replace(/^> /mg, '').split('\n').slice(2); + assert.deepStrictEqual(result, [ + '[Module: null prototype] { message: \'A message\' }', + '', + ]); +})); + +child.stdin.write('await import(\'./message.mjs\');\n'); +child.stdin.write('.exit\n'); diff --git a/test/js/node/test/parallel/test-repl-pretty-custom-stack.js b/test/js/node/test/parallel/test-repl-pretty-custom-stack.js new file mode 100644 index 000000000000..0efb814f38d2 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-pretty-custom-stack.js @@ -0,0 +1,77 @@ +'use strict'; +require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); + +const stackRegExp = /(REPL\d+):[0-9]+:[0-9]+/g; + +function run({ command, expected }) { + const { replServer, output } = startNewREPLServer({ + terminal: false, + useColors: false + }); + + replServer.write(`${command}\n`); + if (typeof expected === 'string') { + assert.strictEqual( + output.accumulator.replace(stackRegExp, '$1:*:*'), + expected.replace(stackRegExp, '$1:*:*') + ); + } else { + assert.match( + output.accumulator.replace(stackRegExp, '$1:*:*'), + expected + ); + } + replServer.close(); +} + +const origPrepareStackTrace = Error.prepareStackTrace; +Error.prepareStackTrace = (err, stack) => { + if (err instanceof SyntaxError) + return err.toString(); + // Insert the error at the beginning of the stack + stack.unshift(err); + return stack.join('--->\n'); +}; + +process.on('uncaughtException', (e) => { + Error.prepareStackTrace = origPrepareStackTrace; + throw e; +}); + +const tests = [ + { + // test .load for a file that throws + command: `.load ${fixtures.path('repl-pretty-stack.js')}`, + expected: 'Uncaught Error: Whoops!--->\nREPL1:*:*--->\nd (REPL1:*:*)' + + '--->\nc (REPL1:*:*)--->\nb (REPL1:*:*)--->\na (REPL1:*:*)\n' + }, + { + command: 'let x y;', + expected: /let x y;\n {6}\^\n\nUncaught SyntaxError: Unexpected identifier.*\n/ + }, + { + command: 'throw new Error(\'Whoops!\')', + expected: 'Uncaught Error: Whoops!\n' + }, + { + command: 'foo = bar;', + expected: 'Uncaught ReferenceError: bar is not defined\n' + }, + // test anonymous IIFE + { + command: '(function() { throw new Error(\'Whoops!\'); })()', + expected: 'Uncaught Error: Whoops!--->\nREPL5:*:*\n' + }, +]; + +tests.forEach(run); + +// Verify that the stack can be generated when Error.prepareStackTrace is deleted. +delete Error.prepareStackTrace; +run({ + command: 'throw new TypeError(\'Whoops!\')', + expected: 'Uncaught TypeError: Whoops!\n' +}); diff --git a/test/js/node/test/parallel/test-repl-preview.js b/test/js/node/test/parallel/test-repl-preview.js new file mode 100644 index 000000000000..9ab84b5c9f3a --- /dev/null +++ b/test/js/node/test/parallel/test-repl-preview.js @@ -0,0 +1,272 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const events = require('events'); +const { REPLServer } = require('repl'); +const { Stream } = require('stream'); +const { inspect } = require('util'); + +common.skipIfInspectorDisabled(); + +// Ignore terminal settings. This is so the test can be run intact if TERM=dumb. +process.env.TERM = ''; +const PROMPT = 'repl > '; + +class REPLStream extends Stream { + readable = true; + writable = true; + + constructor() { + super(); + this.lines = ['']; + } + run(data) { + for (const entry of data) { + this.emit('data', entry); + } + this.emit('data', '\n'); + } + write(chunk) { + const chunkLines = chunk.toString('utf8').split('\n'); + this.lines[this.lines.length - 1] += chunkLines[0]; + if (chunkLines.length > 1) { + this.lines.push(...chunkLines.slice(1)); + } + this.emit('line', this.lines[this.lines.length - 1]); + return true; + } + async wait() { + this.lines = ['']; + for await (const [line] of events.on(this, 'line')) { + if (line.includes(PROMPT)) { + return this.lines; + } + } + } + pause() {} + resume() {} +} + +function runAndWait(cmds, repl) { + const promise = repl.inputStream.wait(); + for (const cmd of cmds) { + repl.inputStream.run(cmd); + } + return promise; +} + +async function tests(options) { + const repl = new REPLServer({ + prompt: PROMPT, + stream: new REPLStream(), + ignoreUndefined: true, + useColors: true, + ...options + }); + + repl.inputStream.run([ + 'function foo(x) { return x; }', + 'function koo() { console.log("abc"); }', + 'a = undefined;', + ]); + + const testCases = [{ + input: 'foo', + noPreview: '[Function: foo]', + preview: [ + 'foo', + '\x1B[90m[Function: foo]\x1B[39m\x1B[11G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[36m[Function: foo]\x1B[39m', + ] + }, { + input: 'koo', + noPreview: '[Function: koo]', + preview: [ + 'k\x1B[90moo\x1B[39m\x1B[9G', + '\x1B[90m[Function: koo]\x1B[39m\x1B[9G\x1B[1A\x1B[1B\x1B[2K\x1B[1A' + + '\x1B[0Ko\x1B[90mo\x1B[39m\x1B[10G', + '\x1B[90m[Function: koo]\x1B[39m\x1B[10G\x1B[1A\x1B[1B\x1B[2K\x1B[1A' + + '\x1B[0Ko', + '\x1B[90m[Function: koo]\x1B[39m\x1B[11G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[36m[Function: koo]\x1B[39m', + ] + }, { + input: 'a', + noPreview: 'repl > ', // No "undefined" output. + preview: ['a\r'] // No "undefined" preview. + }, { + input: " { b: 1 }['b'] === 1", + noPreview: '\x1B[33mtrue\x1B[39m', + preview: [ + " { b: 1 }['b']", + '\x1B[90m1\x1B[39m\x1B[22G\x1B[1A\x1B[1B\x1B[2K\x1B[1A ', + '\x1B[90m1\x1B[39m\x1B[23G\x1B[1A\x1B[1B\x1B[2K\x1B[1A=== 1', + '\x1B[90mtrue\x1B[39m\x1B[28G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33mtrue\x1B[39m', + ] + }, { + input: "{ b: 1 }['b'] === 1;", + noPreview: '\x1B[33mfalse\x1B[39m', + preview: [ + "{ b: 1 }['b']", + '\x1B[90m1\x1B[39m\x1B[21G\x1B[1A\x1B[1B\x1B[2K\x1B[1A ', + '\x1B[90m1\x1B[39m\x1B[22G\x1B[1A\x1B[1B\x1B[2K\x1B[1A=== 1', + '\x1B[90mtrue\x1B[39m\x1B[27G\x1B[1A\x1B[1B\x1B[2K\x1B[1A;', + '\x1B[90mfalse\x1B[39m\x1B[28G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33mfalse\x1B[39m', + ] + }, { + input: '{ a: true }', + noPreview: '{ a: \x1B[33mtrue\x1B[39m }', + preview: [ + '{ a: tru\x1B[90me\x1B[39m\x1B[16G\x1B[0Ke }\r', + '{ a: \x1B[33mtrue\x1B[39m }', + ] + }, { + input: '{ a: true };', + noPreview: '\x1B[33mtrue\x1B[39m', + preview: [ + '{ a: tru\x1B[90me\x1B[39m\x1B[16G\x1B[0Ke };', + '\x1B[90mtrue\x1B[39m\x1B[20G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33mtrue\x1B[39m', + ] + }, { + input: ' \t { a: true};', + noPreview: '\x1B[33mtrue\x1B[39m', + preview: [ + ' { a: tru\x1B[90me\x1B[39m\x1B[18G\x1B[0Ke}', + '\x1B[90m{ a: true }\x1B[39m\x1B[20G\x1B[1A\x1B[1B\x1B[2K\x1B[1A;', + '\x1B[90mtrue\x1B[39m\x1B[21G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33mtrue\x1B[39m', + ] + }, { + input: '1n + 2n', + noPreview: '\x1B[33m3n\x1B[39m', + preview: [ + '1n + 2', + '\x1B[90mType[39m\x1B[14G\x1B[1A\x1B[1B\x1B[2K\x1B[1An', + '\x1B[90m3n\x1B[39m\x1B[15G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33m3n\x1B[39m', + ] + }, { + input: '{};1', + noPreview: '\x1B[33m1\x1B[39m', + preview: [ + '{};1', + '\x1B[90m1\x1B[39m\x1B[12G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '\x1B[33m1\x1B[39m', + ] + }, { + input: 'aaaa', + noPreview: 'Uncaught ReferenceError: aaaa is not defined', + preview: [ + 'aaaa\r', + 'Uncaught ReferenceError: aaaa is not defined', + ] + }, { + input: '/0', + noPreview: '/0', + preview: [ + '/0\r', + '/0', + '^', + '', + 'Uncaught SyntaxError: Invalid regular expression: missing /', + ] + }, { + input: '{})', + noPreview: '{})', + preview: [ + '{})\r', + '{})', + ' ^', + '', + "Uncaught SyntaxError: Unexpected token ')'", + ], + }, { + input: "{ a: '{' }", + noPreview: "{ a: \x1B[32m'{'\x1B[39m }", + preview: [ + "{ a: '{' }\r", + "{ a: \x1B[32m'{'\x1B[39m }", + ], + }, { + input: "{'{':0}", + noPreview: "{ \x1B[32m'{'\x1B[39m: \x1B[33m0\x1B[39m }", + preview: [ + "{'{':0}", + "\x1B[90m{ '{': 0 }\x1B[39m\x1B[15G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r", + "{ \x1B[32m'{'\x1B[39m: \x1B[33m0\x1B[39m }", + ], + }, { + input: '{[Symbol.for("{")]: 0 }', + noPreview: '{ \x1B[32mSymbol({)\x1B[39m: \x1B[33m0\x1B[39m }', + preview: [ + '{[Symbol.for("{")]: 0 }\r', + '{ \x1B[32mSymbol({)\x1B[39m: \x1B[33m0\x1B[39m }', + ], + }, { + input: '{},{}', + noPreview: '{}', + preview: [ + '{},{}', + '\x1B[90m{}\x1B[39m\x1B[13G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + '{}', + ], + }, { + input: '{} //', + noPreview: 'repl > ', + preview: [ + '{} //\r', + ], + }, { + input: '{} //;', + noPreview: 'repl > ', + preview: [ + '{} //;\r', + ], + }, { + input: '{throw 0}', + noPreview: 'Uncaught \x1B[33m0\x1B[39m', + preview: [ + '{throw 0}', + '\x1B[90m0\x1B[39m\x1B[17G\x1B[1A\x1B[1B\x1B[2K\x1B[1A\r', + 'Uncaught \x1B[33m0\x1B[39m', + ], + }]; + + const hasPreview = repl.terminal && + (options.preview !== undefined ? !!options.preview : true); + + for (const { input, noPreview, preview } of testCases) { + console.log(`Testing ${input}`); + + const toBeRun = input.split('\n'); + let lines = await runAndWait(toBeRun, repl); + + if (hasPreview) { + // Remove error messages. That allows the code to run in different + // engines. + // eslint-disable-next-line no-control-regex + lines = lines.map((line) => line.replace(/Error: .+?\x1B/, '')); + assert.strictEqual(lines.pop(), '\x1B[1G\x1B[0Jrepl > \x1B[8G'); + assert.deepStrictEqual(lines, preview); + } else { + assert.ok(lines[0].includes(noPreview), lines.map(inspect)); + if (preview.length !== 1 || preview[0] !== `${input}\r`) { + if (preview[preview.length - 1].includes('Uncaught SyntaxError')) { + assert.strictEqual(lines.length, 5); + } else { + assert.strictEqual(lines.length, 2); + } + } + } + } +} + +tests({ terminal: false }); // No preview +tests({ terminal: true }); // Preview +tests({ terminal: false, preview: false }); // No preview +tests({ terminal: false, preview: true }); // No preview +tests({ terminal: true, preview: true }); // Preview diff --git a/test/js/node/test/parallel/test-repl-require.js b/test/js/node/test/parallel/test-repl-require.js new file mode 100644 index 000000000000..e740acef08b0 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-require.js @@ -0,0 +1,73 @@ +'use strict'; + +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); +const net = require('net'); +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('process.chdir is not available in Workers'); +} + +process.chdir(fixtures.fixturesDir); +const repl = require('repl'); + +{ + const server = net.createServer((conn) => { + repl.start('', conn).on('exit', () => { + conn.destroy(); + server.close(); + }); + }); + + const host = common.localhostIPv4; + const port = 0; + const options = { host, port }; + + let answer = ''; + server.listen(options, function() { + options.port = this.address().port; + const conn = net.connect(options); + conn.setEncoding('utf8'); + conn.on('data', (data) => answer += data); + conn.write('require("baz")\nrequire("./baz")\n.exit\n'); + }); + + process.on('exit', function() { + assert.doesNotMatch(answer, /Cannot find module/); + assert.doesNotMatch(answer, /Error/); + assert.strictEqual(answer, '\'eye catcher\'\n\'perhaps I work\'\n'); + }); +} + +// Test for https://github.com/nodejs/node/issues/30808 +// In REPL, we shouldn't look up relative modules from 'node_modules'. +{ + const server = net.createServer((conn) => { + repl.start('', conn).on('exit', () => { + conn.destroy(); + server.close(); + }); + }); + + const host = common.localhostIPv4; + const port = 0; + const options = { host, port }; + + let answer = ''; + server.listen(options, function() { + options.port = this.address().port; + const conn = net.connect(options); + conn.setEncoding('utf8'); + conn.on('data', (data) => answer += data); + conn.write('require("./bar")\n.exit\n'); + }); + + process.on('exit', function() { + assert.match(answer, /Uncaught Error: Cannot find module '\.\/bar'/); + + assert.match(answer, /code: 'MODULE_NOT_FOUND'/); + assert.match(answer, /requireStack: \[ '' \]/); + }); +} diff --git a/test/js/node/test/parallel/test-repl-reverse-search.js b/test/js/node/test/parallel/test-repl-reverse-search.js new file mode 100644 index 000000000000..cbe848afee08 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-reverse-search.js @@ -0,0 +1,365 @@ +'use strict'; + +// Flags: --expose-internals + +const common = require('../common'); +const stream = require('stream'); +const REPL = require('internal/repl'); +const assert = require('assert'); +const fs = require('fs'); +const { inspect } = require('util'); + +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} + +common.allowGlobals('aaaa'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const defaultHistoryPath = tmpdir.resolve('.node_repl_history'); + +// Create an input stream specialized for testing an array of actions +class ActionStream extends stream.Stream { + run(data) { + const _iter = data[Symbol.iterator](); + const doAction = () => { + const next = _iter.next(); + if (next.done) { + // Close the repl. Note that it must have a clean prompt to do so. + setImmediate(() => { + this.emit('keypress', '', { ctrl: true, name: 'd' }); + }); + return; + } + const action = next.value; + + if (typeof action === 'object') { + this.emit('keypress', '', action); + } else { + this.emit('data', action); + } + setImmediate(doAction); + }; + doAction(); + } + resume() {} + pause() {} +} +ActionStream.prototype.readable = true; + +// Mock keys +const ENTER = { name: 'enter' }; +const UP = { name: 'up' }; +const DOWN = { name: 'down' }; +const BACKSPACE = { name: 'backspace' }; +const SEARCH_BACKWARDS = { name: 'r', ctrl: true }; +const SEARCH_FORWARDS = { name: 's', ctrl: true }; +const ESCAPE = { name: 'escape' }; +const CTRL_C = { name: 'c', ctrl: true }; +const DELETE_WORD_LEFT = { name: 'w', ctrl: true }; + +const prompt = '> '; + +// TODO(BridgeAR): Add tests for lines that exceed the maximum columns. +const tests = [ + { // Creates few history to navigate for + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + test: [ + 'console.log("foo")', ENTER, + 'ab = "aaaa"', ENTER, + 'repl.repl.historyIndex', ENTER, + 'console.log("foo")', ENTER, + 'let ba = 9', ENTER, + 'ab = "aaaa"', ENTER, + '555 - 909', ENTER, + '{key : {key2 :[] }}', ENTER, + 'Array(100).fill(1)', ENTER, + ], + expected: [], + clean: false + }, + { + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + showEscapeCodes: true, + checkTotal: true, + useColors: true, + test: [ + '7', // 1 + SEARCH_FORWARDS, + SEARCH_FORWARDS, // 3 + 'a', + SEARCH_BACKWARDS, // 5 + SEARCH_FORWARDS, + SEARCH_BACKWARDS, // 7 + 'a', + BACKSPACE, // 9 + DELETE_WORD_LEFT, + 'aa', // 11 + SEARCH_BACKWARDS, + SEARCH_BACKWARDS, // 13 + SEARCH_BACKWARDS, + SEARCH_BACKWARDS, // 15 + SEARCH_FORWARDS, + ESCAPE, // 17 + ENTER, + ], + // A = Cursor n up + // B = Cursor n down + // C = Cursor n forward + // D = Cursor n back + // G = Cursor to column n + // J = Erase in screen; 0 = right; 1 = left; 2 = total + // K = Erase in line; 0 = right; 1 = left; 2 = total + expected: [ + // 0. Start + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + // 1. '7' + '7', + // 2. SEARCH FORWARDS + '\nfwd-i-search: _', '\x1B[1A', '\x1B[4G', + // 3. SEARCH FORWARDS + '\x1B[3G', '\x1B[0J', + '7\nfwd-i-search: _', '\x1B[1A', '\x1B[4G', + // 4. 'a' + '\x1B[3G', '\x1B[0J', + '7\nfailed-fwd-i-search: a_', '\x1B[1A', '\x1B[4G', + // 5. SEARCH BACKWARDS + '\x1B[3G', '\x1B[0J', + 'Arr\x1B[4ma\x1B[24my(100).fill(1)\nbck-i-search: a_', + '\x1B[1A', '\x1B[6G', + // 6. SEARCH FORWARDS + '\x1B[3G', '\x1B[0J', + '7\nfailed-fwd-i-search: a_', '\x1B[1A', '\x1B[4G', + // 7. SEARCH BACKWARDS + '\x1B[3G', '\x1B[0J', + 'Arr\x1B[4ma\x1B[24my(100).fill(1)\nbck-i-search: a_', + '\x1B[1A', '\x1B[6G', + // 8. 'a' + '\x1B[3G', '\x1B[0J', + 'ab = "aa\x1B[4maa\x1B[24m"\nbck-i-search: aa_', + '\x1B[1A', '\x1B[11G', + // 9. BACKSPACE + '\x1B[3G', '\x1B[0J', + 'Arr\x1B[4ma\x1B[24my(100).fill(1)\nbck-i-search: a_', + '\x1B[1A', '\x1B[6G', + // 10. DELETE WORD LEFT (works as backspace) + '\x1B[3G', '\x1B[0J', + '7\nbck-i-search: _', '\x1B[1A', '\x1B[4G', + // 11. 'a' + '\x1B[3G', '\x1B[0J', + 'Arr\x1B[4ma\x1B[24my(100).fill(1)\nbck-i-search: a_', + '\x1B[1A', '\x1B[6G', + // 11. 'aa' - continued + '\x1B[3G', '\x1B[0J', + 'ab = "aa\x1B[4maa\x1B[24m"\nbck-i-search: aa_', + '\x1B[1A', '\x1B[11G', + // 12. SEARCH BACKWARDS + '\x1B[3G', '\x1B[0J', + 'ab = "a\x1B[4maa\x1B[24ma"\nbck-i-search: aa_', + '\x1B[1A', '\x1B[10G', + // 13. SEARCH BACKWARDS + '\x1B[3G', '\x1B[0J', + 'ab = "\x1B[4maa\x1B[24maa"\nbck-i-search: aa_', + '\x1B[1A', '\x1B[9G', + // 14. SEARCH BACKWARDS + '\x1B[3G', '\x1B[0J', + '7\nfailed-bck-i-search: aa_', '\x1B[1A', '\x1B[4G', + // 15. SEARCH BACKWARDS + '\x1B[3G', '\x1B[0J', + '7\nfailed-bck-i-search: aa_', '\x1B[1A', '\x1B[4G', + // 16. SEARCH FORWARDS + '\x1B[3G', '\x1B[0J', + 'ab = "\x1B[4maa\x1B[24maa"\nfwd-i-search: aa_', + '\x1B[1A', '\x1B[9G', + // 17. ESCAPE + '\x1B[3G', '\x1B[0J', + '7', + // 18. ENTER + '\r\n', + '\x1B[33m7\x1B[39m\n', + '\x1B[1G', '\x1B[0J', + prompt, + '\x1B[3G', + '\r\n', + ], + clean: false + }, + { + env: { NODE_REPL_HISTORY: defaultHistoryPath }, + showEscapeCodes: true, + skip: !process.features.inspector, + checkTotal: true, + useColors: false, + test: [ + 'fu', // 1 + SEARCH_BACKWARDS, + '}', // 3 + SEARCH_BACKWARDS, + CTRL_C, // 5 + CTRL_C, + '1+1', // 7 + ENTER, + SEARCH_BACKWARDS, // 9 + '+', + '\r', // 11 + '2', + SEARCH_BACKWARDS, // 13 + 're', + UP, // 15 + DOWN, + SEARCH_FORWARDS, // 17 + '\n', + ], + expected: [ + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + 'f', 'u', '\nbck-i-search: _', '\x1B[1A', '\x1B[5G', + '\x1B[3G', '\x1B[0J', + '{key : {key2 :[] }}\nbck-i-search: }_', '\x1B[1A', '\x1B[21G', + '\x1B[3G', '\x1B[0J', + '{key : {key2 :[] }}\nbck-i-search: }_', '\x1B[1A', '\x1B[20G', + '\x1B[3G', '\x1B[0J', + 'fu', + '\r\n', + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + '1', '+', '1', '\n// 2', '\x1B[6G', '\x1B[1A', + '\x1B[1B', '\x1B[2K', '\x1B[1A', + '\r\n', + '2\n', + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + '\nbck-i-search: _', '\x1B[1A', + '\x1B[3G', '\x1B[0J', + '1+1\nbck-i-search: +_', '\x1B[1A', '\x1B[4G', + '\x1B[3G', '\x1B[0J', + '1+1', '\x1B[4G', + '\x1B[2C', + '\r\n', + '2\n', + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + '2', + '\nbck-i-search: _', '\x1B[1A', '\x1B[4G', + '\x1B[3G', '\x1B[0J', + 'Array(100).fill(1)\nbck-i-search: r_', '\x1B[1A', '\x1B[5G', + '\x1B[3G', '\x1B[0J', + 'repl.repl.historyIndex\nbck-i-search: re_', '\x1B[1A', '\x1B[8G', + '\x1B[3G', '\x1B[0J', + 'repl.repl.historyIndex', '\x1B[8G', + '\x1B[1G', '\x1B[0J', + `${prompt}ab = "aaaa"`, '\x1B[14G', + '\x1B[1G', '\x1B[0J', + `${prompt}repl.repl.historyIndex`, '\x1B[25G', '\n// 8', + '\x1B[25G', '\x1B[1A', + '\x1B[1B', '\x1B[2K', '\x1B[1A', + '\nfwd-i-search: _', '\x1B[1A', '\x1B[25G', + '\x1B[3G', '\x1B[0J', + 'repl.repl.historyIndex', + '\r\n', + '-1\n', + '\x1B[1G', '\x1B[0J', + prompt, '\x1B[3G', + '\r\n', + ], + clean: false + }, +]; +const numtests = tests.length; + +const runTestWrap = common.mustCall(runTest, numtests); + +function cleanupTmpFile() { + try { + // Write over the file, clearing any history + fs.writeFileSync(defaultHistoryPath, ''); + } catch (err) { + if (err.code === 'ENOENT') return true; + throw err; + } + return true; +} + +function runTest() { + const opts = tests.shift(); + if (!opts) return; // All done + + const { expected, skip } = opts; + + // Test unsupported on platform. + if (skip) { + setImmediate(runTestWrap, true); + return; + } + + const lastChunks = []; + let i = 0; + + REPL.createInternalRepl(opts.env, { + input: new ActionStream(), + output: new stream.Writable({ + write: common.mustCallAtLeast((chunk, _, next) => { + const output = chunk.toString(); + + if (!opts.showEscapeCodes && + (output[0] === '\x1B' || /^[\r\n]+$/.test(output))) { + return next(); + } + + lastChunks.push(output); + + if (expected.length && !opts.checkTotal) { + try { + assert.strictEqual(output, expected[i]); + } catch (e) { + console.error(`Failed test # ${numtests - tests.length}`); + console.error('Last outputs: ' + inspect(lastChunks, { + breakLength: 5, colors: true + })); + throw e; + } + i++; + } + + next(); + }), + }), + completer: opts.completer, + prompt, + useColors: opts.useColors || false, + terminal: true + }, common.mustCall((err, repl) => { + if (err) { + console.error(`Failed test # ${numtests - tests.length}`); + throw err; + } + + repl.once('close', common.mustCall(() => { + if (opts.clean) + cleanupTmpFile(); + + if (opts.checkTotal) { + assert.deepStrictEqual(lastChunks, expected); + } else if (expected.length !== i) { + console.error(tests[numtests - tests.length - 1]); + throw new Error(`Failed test # ${numtests - tests.length}`); + } + + setImmediate(runTestWrap, true); + })); + + if (opts.columns) { + Object.defineProperty(repl, 'columns', { + value: opts.columns, + enumerable: true + }); + } + repl.inputStream.run(opts.test); + })); +} + +// run the tests +runTest(); diff --git a/test/js/node/test/parallel/test-repl-sigint-nested-eval.js b/test/js/node/test/parallel/test-repl-sigint-nested-eval.js new file mode 100644 index 000000000000..ecc532f31ede --- /dev/null +++ b/test/js/node/test/parallel/test-repl-sigint-nested-eval.js @@ -0,0 +1,53 @@ +'use strict'; +const common = require('../common'); +if (common.isWindows) { + // No way to send CTRL_C_EVENT to processes from JS right now. + common.skip('platform not supported'); +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('No signal handling available in Workers'); +} + +const assert = require('assert'); +const spawn = require('child_process').spawn; + +const child = spawn(process.execPath, [ '--interactive' ], { + stdio: [null, null, 2, 'ipc'] +}); + +let stdout = ''; +child.stdout.setEncoding('utf8'); +child.stdout.on('data', function(c) { + stdout += c; +}); + +child.stdout.once('data', common.mustCall(() => { + child.on('message', common.mustCall((msg) => { + assert.strictEqual(msg, 'repl is busy'); + process.kill(child.pid, 'SIGINT'); + child.stdout.once('data', common.mustCall(() => { + // Make sure REPL still works. + child.stdin.end('"foobar"\n'); + })); + })); + + child.stdin.write( + 'vm.runInThisContext("process.send(\'repl is busy\'); while(true){}", ' + + '{ breakOnSigint: true });\n' + ); +})); + +child.on('close', common.mustCall((code) => { + const expected = 'Script execution was interrupted by `SIGINT`'; + assert.ok( + stdout.includes(expected), + `Expected stdout to contain "${expected}", got ${stdout}` + ); + assert.ok( + stdout.includes('foobar'), + `Expected stdout to contain "foobar", got ${stdout}` + ); +})); diff --git a/test/js/node/test/parallel/test-repl-sigint.js b/test/js/node/test/parallel/test-repl-sigint.js new file mode 100644 index 000000000000..8db02db886fd --- /dev/null +++ b/test/js/node/test/parallel/test-repl-sigint.js @@ -0,0 +1,53 @@ +'use strict'; +const common = require('../common'); +if (common.isWindows) { + // No way to send CTRL_C_EVENT to processes from JS right now. + common.skip('platform not supported'); +} + +const { isMainThread } = require('worker_threads'); + +if (!isMainThread) { + common.skip('No signal handling available in Workers'); +} + +const assert = require('assert'); +const spawn = require('child_process').spawn; + +process.env.REPL_TEST_PPID = process.pid; +const child = spawn(process.execPath, [ '--interactive' ], { + stdio: [null, null, 2] +}); + +let stdout = ''; +child.stdout.setEncoding('utf8'); +child.stdout.on('data', function(c) { + stdout += c; +}); + +child.stdout.once('data', common.mustCall(() => { + process.on('SIGUSR2', common.mustCall(() => { + process.kill(child.pid, 'SIGINT'); + child.stdout.once('data', common.mustCall(() => { + // Make sure state from before the interruption is still available. + child.stdin.end('a*2*3*7\n'); + })); + })); + + child.stdin.write('a = 1001;' + + 'process.kill(+process.env.REPL_TEST_PPID, "SIGUSR2");' + + 'while(true){}\n'); +})); + +child.on('close', common.mustCall((code) => { + assert.strictEqual(code, 0); + const expected = 'Script execution was interrupted by `SIGINT`'; + assert.ok( + stdout.includes(expected), + `Expected stdout to contain "${expected}", got ${stdout}` + ); + assert.ok( + stdout.includes('42042\n'), + `Expected stdout to contain "42042", got ${stdout}` + ); +})); diff --git a/test/js/node/test/parallel/test-repl-strict-mode-previews.js b/test/js/node/test/parallel/test-repl-strict-mode-previews.js new file mode 100644 index 000000000000..e7fc1ea5191e --- /dev/null +++ b/test/js/node/test/parallel/test-repl-strict-mode-previews.js @@ -0,0 +1,50 @@ +// Previews in strict mode should indicate ReferenceErrors. + +'use strict'; + +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +if (process.env.TERM === 'dumb') { + common.skip('skipping - dumb terminal'); +} + +if (process.argv[2] === 'child') { + const stream = require('stream'); + const repl = require('repl'); + class ActionStream extends stream.Stream { + readable = true; + run(data) { + this.emit('data', `${data}`); + this.emit('keypress', '', { ctrl: true, name: 'd' }); + } + resume() {} + pause() {} + } + + repl.start({ + input: new ActionStream(), + output: new stream.Writable({ + write(chunk, _, next) { + console.log(chunk.toString()); + next(); + } + }), + useColors: false, + terminal: true + }).inputStream.run('xyz'); +} else { + const assert = require('assert'); + const { spawnSync } = require('child_process'); + + const result = spawnSync( + process.execPath, + ['--use-strict', `${__filename}`, 'child'] + ); + + assert.match( + result.stdout.toString(), + /\/\/ ReferenceError: xyz is not defined/ + ); +} diff --git a/test/js/node/test/parallel/test-repl-tab-complete-nested-repls.js b/test/js/node/test/parallel/test-repl-tab-complete-nested-repls.js new file mode 100644 index 000000000000..3cac02f20562 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-nested-repls.js @@ -0,0 +1,23 @@ +// Tab completion sometimes uses a separate REPL instance under the hood. +// That REPL instance has its own domain. Make sure domain errors trickle back +// up to the main REPL. +// +// Ref: https://github.com/nodejs/node/issues/21586 + +'use strict'; + +require('../common'); +const fixtures = require('../common/fixtures'); + +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +const testFile = fixtures.path('repl-tab-completion-nested-repls.js'); +const result = spawnSync(process.execPath, [testFile]); + +// The spawned process will fail. In Node.js 10.11.0, it will fail silently. The +// test here is to make sure that the error information bubbles up to the +// calling process. +assert.ok(result.status, 'testFile swallowed its error'); +const err = result.stderr.toString(); +assert.ok(err.includes('fhqwhgads'), err); diff --git a/test/js/node/test/parallel/test-repl-tab-complete-unary-expressions.js b/test/js/node/test/parallel/test-repl-tab-complete-unary-expressions.js new file mode 100644 index 000000000000..2b09ae651d25 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete-unary-expressions.js @@ -0,0 +1,116 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { startNewREPLServer } = require('../common/repl'); +const { describe, it } = require('node:test'); + +// This test verifies that tab completion works correctly with unary expressions +// like delete, typeof, void, etc. This is a regression test for the issue where +// typing "delete globalThis._" and then backspacing and typing "globalThis" +// would cause "globalThis is not defined" error. + +describe('REPL tab completion with unary expressions', () => { + it('should handle delete operator correctly', (t, done) => { + const { replServer } = startNewREPLServer({ terminal: false }); + + // Test delete with member expression + replServer.complete( + 'delete globalThis._', + common.mustSucceed((completions) => { + assert.strictEqual(completions[1], 'globalThis._'); + + // Test delete with identifier + replServer.complete( + 'delete globalThis', + common.mustSucceed((completions) => { + assert.strictEqual(completions[1], 'globalThis'); + replServer.close(); + done(); + }) + ); + }) + ); + }); + + it('should handle typeof operator correctly', (t, done) => { + const { replServer } = startNewREPLServer({ terminal: false }); + + replServer.complete( + 'typeof globalThis', + common.mustSucceed((completions) => { + assert.strictEqual(completions[1], 'globalThis'); + replServer.close(); + done(); + }) + ); + }); + + it('should handle void operator correctly', (t, done) => { + const { replServer } = startNewREPLServer({ terminal: false }); + + replServer.complete( + 'void globalThis', + common.mustSucceed((completions) => { + assert.strictEqual(completions[1], 'globalThis'); + replServer.close(); + done(); + }) + ); + }); + + it('should handle other unary operators correctly', (t, done) => { + const { replServer } = startNewREPLServer({ terminal: false }); + + const unaryOperators = [ + '!globalThis', + '+globalThis', + '-globalThis', + '~globalThis', + ]; + + let testIndex = 0; + + function testNext() { + if (testIndex >= unaryOperators.length) { + replServer.close(); + done(); + return; + } + + const testCase = unaryOperators[testIndex++]; + replServer.complete( + testCase, + common.mustSucceed((completions) => { + assert.strictEqual(completions[1], 'globalThis'); + testNext(); + }) + ); + } + + testNext(); + }); + + it('should still evaluate globalThis correctly after unary expression completion', (t, done) => { + const { replServer } = startNewREPLServer({ terminal: false }); + + // First trigger completion with delete + replServer.complete( + 'delete globalThis._', + common.mustSucceed(() => { + // Then evaluate globalThis + replServer.eval( + 'globalThis', + replServer.context, + 'test.js', + common.mustSucceed((result) => { + assert.strictEqual(typeof result, 'object'); + assert.ok(result !== null); + replServer.close(); + done(); + }) + ); + }) + ); + }); +}); diff --git a/test/js/node/test/parallel/test-repl-tab-complete.js b/test/js/node/test/parallel/test-repl-tab-complete.js new file mode 100644 index 000000000000..d4df6c317879 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-tab-complete.js @@ -0,0 +1,565 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +'use strict'; + +const common = require('../common'); +const { startNewREPLServer } = require('../common/repl'); +const { describe, it } = require('node:test'); +const assert = require('assert'); + +function getNoResultsFunction() { + return common.mustSucceed((data) => { + assert.deepStrictEqual(data[0], []); + }); +} + +describe('REPL tab completion (core functionality)', () => { + it('does not break with variable declarations without an initialization', () => { + const { replServer } = startNewREPLServer(); + replServer.complete('let a', getNoResultsFunction()); + replServer.close(); + }); + + it('does not break in an object literal', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var inner = {', 'one:1']); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.complete( + 'console.lo', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['console.log'], 'console.lo']); + }) + ); + + replServer.close(); + }); + + it('works with optional chaining', () => { + const { replServer } = startNewREPLServer(); + + replServer.complete( + 'console?.lo', + common.mustCall((_error, data) => { + assert.deepStrictEqual(data, [['console?.log'], 'console?.lo']); + }) + ); + + replServer.complete( + 'console?.zzz', + common.mustCall((_error, data) => { + assert.deepStrictEqual(data, [[], 'console?.zzz']); + }) + ); + + replServer.complete( + 'console?.', + common.mustCall((_error, data) => { + assert(data[0].includes('console?.log')); + assert.strictEqual(data[1], 'console?.'); + }) + ); + + replServer.close(); + }); + + it('returns object completions', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var inner = {', 'one:1']); + + input.run(['};']); + + replServer.complete( + 'inner.o', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['inner.one'], 'inner.o']); + }) + ); + + replServer.close(); + }); + + it('does not break in a ternary operator with ()', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var inner = ( true ', '?', '{one: 1} : ']); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.close(); + }); + + it('works on literals', () => { + const { replServer } = startNewREPLServer(); + + replServer.complete( + '``.a', + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes('``.at'), true); + }) + ); + replServer.complete( + "''.a", + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes("''.at"), true); + }) + ); + replServer.complete( + '"".a', + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes('"".at'), true); + }) + ); + replServer.complete( + '("").a', + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes('("").at'), true); + }) + ); + replServer.complete( + '[].a', + common.mustCall((err, data) => { + assert.strictEqual(data[0].includes('[].at'), true); + }) + ); + replServer.complete( + '{}.a', + common.mustCall((err, data) => { + assert.deepStrictEqual(data[0], []); + }) + ); + + replServer.close(); + }); + + it("does not return a function's local variable", () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var top = function() {', 'var inner = {one:1};', '}']); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.close(); + }); + + it("does not return a function's local variable even when the function has parameters", () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'var top = function(one, two) {', + 'var inner = {', + ' one:1', + '};', + ]); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.close(); + }); + + it("does not return a function's local variable" + + 'even if the scope is nested inside an immediately executed function', () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'var top = function() {', + '(function test () {', + 'var inner = {', + ' one:1', + '};', + ]); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.close(); + }); + + it("does not return a function's local variable" + + 'even if the scope is nested inside an immediately executed function' + + '(the definition has the params and { on a separate line)', () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'var top = function() {', + 'r = function test (', + ' one, two) {', + 'var inner = {', + ' one:1', + '};', + ]); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.close(); + }); + + it('currently does not work, but should not break (local inner)', () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'var top = function() {', + 'r = function test ()', + '{', + 'var inner = {', + ' one:1', + '};', + ]); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.close(); + }); + + it('currently does not work, but should not break (local inner parens next line)', () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'var top = function() {', + 'r = function test (', + ')', + '{', + 'var inner = {', + ' one:1', + '};', + ]); + + replServer.complete('inner.o', getNoResultsFunction()); + + replServer.close(); + }); + + it('works on non-Objects', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var str = "test";']); + + replServer.complete( + 'str.len', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['str.length'], 'str.len']); + }) + ); + + replServer.close(); + }); + + it('should be case-insensitive if member part is lower-case', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };']); + + replServer.complete( + 'foo.b', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [ + ['foo.BARbuz', 'foo.barBLA', 'foo.barBar'], + 'foo.b', + ]); + }) + ); + + replServer.close(); + }); + + it('should be case-insensitive if member part is upper-case', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var foo = { barBar: 1, BARbuz: 2, barBLA: 3 };']); + + replServer.complete( + 'foo.B', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [ + ['foo.BARbuz', 'foo.barBLA', 'foo.barBar'], + 'foo.B', + ]); + }) + ); + + replServer.close(); + }); + + it('should not break on spaces', () => { + const { replServer } = startNewREPLServer(); + + const spaceTimeout = setTimeout(function() { + throw new Error('timeout'); + }, 1000); + + replServer.complete( + ' ', + common.mustSucceed((data) => { + assert.strictEqual(data[1], ''); + assert.ok(data[0].includes('globalThis')); + clearTimeout(spaceTimeout); + }) + ); + + replServer.close(); + }); + + it(`should pick up the global "toString" object, and any other properties up the "global" object's prototype chain`, () => { + const { replServer } = startNewREPLServer(); + + replServer.complete( + 'toSt', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['toString'], 'toSt']); + }) + ); + + replServer.close(); + }); + + it('should make own properties shadow properties on the prototype', () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'var x = Object.create(null);', + 'x.a = 1;', + 'x.b = 2;', + 'var y = Object.create(x);', + 'y.a = 3;', + 'y.c = 4;', + ]); + + replServer.complete( + 'y.', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['y.b', '', 'y.a', 'y.c'], 'y.']); + }) + ); + + replServer.close(); + }); + + it('works on context properties', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var custom = "test";']); + + replServer.complete( + 'cus', + common.mustCall(function(_error, data) { + assert.deepStrictEqual(data, [['CustomEvent', 'custom'], 'cus']); + }) + ); + + replServer.close(); + }); + + it("doesn't crash REPL with half-baked proxy objects", () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'var proxy = new Proxy({}, {ownKeys: () => { throw new Error(); }});', + ]); + + replServer.complete( + 'proxy.', + common.mustCall(function(error, data) { + assert.strictEqual(error, null); + assert(Array.isArray(data)); + }) + ); + + replServer.close(); + }); + + it('does not include integer members of an Array', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var ary = [1,2,3];']); + + replServer.complete( + 'ary.', + common.mustCall(function(_error, data) { + assert.strictEqual(data[0].includes('ary.0'), false); + assert.strictEqual(data[0].includes('ary.1'), false); + assert.strictEqual(data[0].includes('ary.2'), false); + }) + ); + + replServer.close(); + }); + + it('does not include integer keys in an object', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var obj = {1:"a","1a":"b",a:"b"};']); + + replServer.complete( + 'obj.', + common.mustCall(function(_error, data) { + assert.strictEqual(data[0].includes('obj.1'), false); + assert.strictEqual(data[0].includes('obj.1a'), false); + assert(data[0].includes('obj.a')); + }) + ); + + replServer.close(); + }); + + it('does not try to complete results of non-simple expressions', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['function a() {}']); + + replServer.complete('a().b.', getNoResultsFunction()); + + replServer.close(); + }); + + it('works when prefixed with spaces', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var obj = {1:"a","1a":"b",a:"b"};']); + + replServer.complete( + ' obj.', + common.mustCall((_error, data) => { + assert.strictEqual(data[0].includes('obj.1'), false); + assert.strictEqual(data[0].includes('obj.1a'), false); + assert(data[0].includes('obj.a')); + }) + ); + + replServer.close(); + }); + + it('works inside assignments', () => { + const { replServer } = startNewREPLServer(); + + replServer.complete( + 'var log = console.lo', + common.mustCall((_error, data) => { + assert.deepStrictEqual(data, [['console.log'], 'console.lo']); + }) + ); + + replServer.close(); + }); + + it('works for defined commands', () => { + const { replServer, input } = startNewREPLServer(); + + replServer.complete( + '.b', + common.mustCall((error, data) => { + assert.deepStrictEqual(data, [['break'], 'b']); + }) + ); + + input.run(['var obj = {"hello, world!": "some string", "key": 123}']); + + replServer.complete( + 'obj.', + common.mustCall((error, data) => { + assert.strictEqual(data[0].includes('obj.hello, world!'), false); + assert(data[0].includes('obj.key')); + }) + ); + + replServer.close(); + }); + + it('does not include __defineSetter__ and friends', () => { + const { replServer, input } = startNewREPLServer(); + + input.run(['var obj = {};']); + + replServer.complete( + 'obj.', + common.mustCall(function(error, data) { + assert.strictEqual(data[0].includes('obj.__defineGetter__'), false); + assert.strictEqual(data[0].includes('obj.__defineSetter__'), false); + assert.strictEqual(data[0].includes('obj.__lookupGetter__'), false); + assert.strictEqual(data[0].includes('obj.__lookupSetter__'), false); + assert.strictEqual(data[0].includes('obj.__proto__'), true); + }) + ); + + replServer.close(); + }); + + it('works with builtin values', () => { + const { replServer } = startNewREPLServer(); + + replServer.complete( + 'I', + common.mustCall((error, data) => { + assert.deepStrictEqual(data, [ + [ + 'if', + 'import', + 'in', + 'instanceof', + '', + 'Infinity', + 'Int16Array', + 'Int32Array', + 'Int8Array', + ...(common.hasIntl ? ['Intl'] : []), + 'Iterator', + 'inspector', + 'isFinite', + 'isNaN', + '', + 'isPrototypeOf', + ], + 'I', + ]); + }) + ); + + replServer.close(); + }); + + it('works with lexically scoped variables', () => { + const { replServer, input } = startNewREPLServer(); + + input.run([ + 'let lexicalLet = true;', + 'const lexicalConst = true;', + 'class lexicalKlass {}', + ]); + + ['Let', 'Const', 'Klass'].forEach((type) => { + const query = `lexical${type[0]}`; + const hasInspector = process.features.inspector; + const expected = hasInspector ? + [[`lexical${type}`], query] : + [[], `lexical${type[0]}`]; + replServer.complete( + query, + common.mustCall((error, data) => { + assert.deepStrictEqual(data, expected); + }) + ); + }); + + replServer.close(); + }); +}); diff --git a/test/js/node/test/parallel/test-repl-top-level-await.js b/test/js/node/test/parallel/test-repl-top-level-await.js new file mode 100644 index 000000000000..a94ff8e48984 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-top-level-await.js @@ -0,0 +1,230 @@ +'use strict'; + +const common = require('../common'); +const ArrayStream = require('../common/arraystream'); +const assert = require('assert'); +const events = require('events'); +const { stripVTControlCharacters } = require('internal/util/inspect'); +const repl = require('repl'); + +common.skipIfInspectorDisabled(); + +// Flags: --expose-internals + +const PROMPT = 'await repl > '; + +class REPLStream extends ArrayStream { + constructor() { + super(); + this.waitingForResponse = false; + this.lines = ['']; + } + write(chunk, encoding, callback) { + if (Buffer.isBuffer(chunk)) { + chunk = chunk.toString(encoding); + } + const chunkLines = stripVTControlCharacters(chunk).split('\n'); + this.lines[this.lines.length - 1] += chunkLines[0]; + if (chunkLines.length > 1) { + this.lines.push(...chunkLines.slice(1)); + } + this.emit('line', this.lines[this.lines.length - 1]); + if (callback) callback(); + return true; + } + + async wait() { + if (this.waitingForResponse) { + throw new Error('Currently waiting for response to another command'); + } + this.lines = ['']; + for await (const [line] of events.on(this, 'line')) { + if (line.includes(PROMPT)) { + return this.lines; + } + } + } +} + +const putIn = new REPLStream(); +const testMe = repl.start({ + prompt: PROMPT, + stream: putIn, + terminal: true, + useColors: true, + breakEvalOnSigint: true +}); + +function runAndWait(cmds) { + const promise = putIn.wait(); + for (const cmd of cmds) { + if (typeof cmd === 'string') { + putIn.run([cmd]); + } else { + testMe.write('', cmd); + } + } + return promise; +} + +async function ordinaryTests() { + // These tests were created based on + // https://cs.chromium.org/chromium/src/third_party/WebKit/LayoutTests/http/tests/devtools/console/console-top-level-await.js?rcl=5d0ea979f0ba87655b7ef0e03b58fa3c04986ba6 + putIn.run([ + 'function foo(x) { return x; }', + 'function koo() { return Promise.resolve(4); }', + ]); + const testCases = [ + ['await Promise.resolve(0)', '0'], + ['{ a: await Promise.resolve(1) }', '{ a: 1 }'], + ['_', '{ a: 1 }'], + ['let { aa, bb } = await Promise.resolve({ aa: 1, bb: 2 }), f = 5;'], + ['aa', '1'], + ['bb', '2'], + ['f', '5'], + ['let cc = await Promise.resolve(2)'], + ['cc', '2'], + ['let dd;'], + ['dd'], + ['let [ii, { abc: { kk } }] = [0, { abc: { kk: 1 } }];'], + ['ii', '0'], + ['kk', '1'], + ['var ll = await Promise.resolve(2);'], + ['ll', '2'], + ['foo(await koo())', '4'], + ['_', '4'], + ['const m = foo(await koo());'], + ['m', '4'], + ['const n = foo(await\nkoo());', + ['const n = foo(await\r', '| koo());\r', 'undefined']], + ['n', '4'], + // eslint-disable-next-line no-template-curly-in-string + ['`status: ${(await Promise.resolve({ status: 200 })).status}`', + "'status: 200'"], + ['for (let i = 0; i < 2; ++i) await i'], + ['for (let i = 0; i < 2; ++i) { await i }'], + ['await 0', '0'], + ['await 0; function foo() {}'], + ['foo', '[Function: foo]'], + ['class Foo {}; await 1;', '1'], + ['Foo', '[class Foo]'], + ['if (await true) { function bar() {}; }'], + ['bar', '[Function: bar]'], + ['if (await true) { class Bar {}; }'], + ['Bar', 'Uncaught ReferenceError: Bar is not defined'], + ['await 0; function* gen(){}'], + ['for (var i = 0; i < 10; ++i) { await i; }'], + ['i', '10'], + ['for (let j = 0; j < 5; ++j) { await j; }'], + ['j', 'Uncaught ReferenceError: j is not defined', { line: 0 }], + ['gen', '[GeneratorFunction: gen]'], + ['return 42; await 5;', 'Uncaught SyntaxError: Illegal return statement', + { line: 3 }], + ['let o = await 1, p'], + ['p'], + ['let q = 1, s = await 2'], + ['s', '2'], + ['for await (let i of [1,2,3]) console.log(i)', + [ + 'for await (let i of [1,2,3]) console.log(i)\r', + '1', + '2', + '3', + 'undefined', + ], + ], + ['await Promise..resolve()', + [ + 'await Promise..resolve()\r', + 'Uncaught SyntaxError: ', + 'await Promise..resolve()', + ' ^', + '', + 'Unexpected token \'.\'', + ], + ], + ['for (const x of [1,2,3]) {\nawait x\n}', [ + 'for (const x of [1,2,3]) {\r', + '| await x\r', + '| }\r', + 'undefined', + ]], + ['for (const x of [1,2,3]) {\nawait x;\n}', [ + 'for (const x of [1,2,3]) {\r', + '| await x;\r', + '| }\r', + 'undefined', + ]], + ['for await (const x of [1,2,3]) {\nconsole.log(x)\n}', [ + 'for await (const x of [1,2,3]) {\r', + '| console.log(x)\r', + '| }\r', + '1', + '2', + '3', + 'undefined', + ]], + ['for await (const x of [1,2,3]) {\nconsole.log(x);\n}', [ + 'for await (const x of [1,2,3]) {\r', + '| console.log(x);\r', + '| }\r', + '1', + '2', + '3', + 'undefined', + ]], + // Testing documented behavior of `const`s (see: https://github.com/nodejs/node/issues/45918) + ['const k = await Promise.resolve(123)'], + ['k', '123'], + ['k = await Promise.resolve(234)', '234'], + ['k', '234'], + ['const k = await Promise.resolve(345)', "Uncaught SyntaxError: Identifier 'k' has already been declared"], + // Regression test for https://github.com/nodejs/node/issues/43777. + ['await Promise.resolve(123), Promise.resolve(456)', 'Promise { 456 }'], + ['await Promise.resolve(123), await Promise.resolve(456)', '456'], + ['await (Promise.resolve(123), Promise.resolve(456))', '456'], + ]; + + for (const [input, expected = [`${input}\r`], options = {}] of testCases) { + console.log(`Testing ${input}`); + const toBeRun = input.split('\n'); + const lines = await runAndWait(toBeRun); + if (Array.isArray(expected)) { + if (expected.length === 1) + expected.push('undefined'); + if (lines[0] === input) + lines.shift(); + assert.deepStrictEqual(lines, [...expected, PROMPT]); + } else if ('line' in options) { + assert.strictEqual(lines[toBeRun.length + options.line], expected); + } else { + const echoed = toBeRun.map((a, i) => `${i > 0 ? '| ' : ''}${a}\r`); + assert.deepStrictEqual(lines, [...echoed, expected, PROMPT]); + } + } +} + +async function ctrlCTest() { + console.log('Testing Ctrl+C'); + const output = await runAndWait([ + 'await new Promise(() => {})', + { ctrl: true, name: 'c' }, + ]); + assert.deepStrictEqual(output.slice(0, 3), [ + 'await new Promise(() => {})\r', + 'Uncaught:', + '[Error [ERR_SCRIPT_EXECUTION_INTERRUPTED]: ' + + 'Script execution was interrupted by `SIGINT`] {', + ]); + assert.deepStrictEqual(output.slice(-2), [ + '}', + PROMPT, + ]); +} + +async function main() { + await ordinaryTests(); + await ctrlCTest(); +} + +main().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-repl-unsafe-array-iteration.js b/test/js/node/test/parallel/test-repl-unsafe-array-iteration.js new file mode 100644 index 000000000000..3fc65f54cf1f --- /dev/null +++ b/test/js/node/test/parallel/test-repl-unsafe-array-iteration.js @@ -0,0 +1,68 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const { spawn } = require('child_process'); + +const replProcess = spawn(process.argv0, ['--interactive'], { + stdio: ['pipe', 'pipe', 'inherit'], + windowsHide: true, +}); + +replProcess.on('error', common.mustNotCall()); + +const replReadyState = (async function* () { + let ready; + const SPACE = ' '.charCodeAt(); + const BRACKET = '>'.charCodeAt(); + const DOT = '.'.charCodeAt(); + replProcess.stdout.on('data', (data) => { + ready = data[data.length - 1] === SPACE && ( + data[data.length - 2] === BRACKET || ( + data[data.length - 2] === DOT && + data[data.length - 3] === DOT && + data[data.length - 4] === DOT + )); + }); + + const processCrashed = new Promise((resolve, reject) => + replProcess.on('exit', reject) + ); + while (true) { + await Promise.race([new Promise(setImmediate), processCrashed]); + if (ready) { + ready = false; + yield; + } + } +})(); +async function writeLn(data, expectedOutput) { + await replReadyState.next(); + if (expectedOutput) { + replProcess.stdout.once('data', common.mustCall((data) => + assert.match(data.toString('utf8'), expectedOutput) + )); + } + await new Promise((resolve, reject) => replProcess.stdin.write( + `${data}\n`, + (err) => (err ? reject(err) : resolve()) + )); +} + +async function main() { + await writeLn( + 'const ArrayIteratorPrototype =' + + ' Object.getPrototypeOf(Array.prototype[Symbol.iterator]());' + ); + await writeLn('delete Array.prototype[Symbol.iterator];'); + await writeLn('delete ArrayIteratorPrototype.next;'); + + await writeLn( + 'for(const x of [3, 2, 1]);', + /Uncaught TypeError: \[3,2,1\] is not iterable/ + ); + await writeLn('.exit'); + + assert(!replProcess.connected); +} + +main().then(common.mustCall()); diff --git a/test/js/node/test/parallel/test-repl-unsupported-option.js b/test/js/node/test/parallel/test-repl-unsupported-option.js new file mode 100644 index 000000000000..16de512a7692 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-unsupported-option.js @@ -0,0 +1,11 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); +const { spawnSync } = require('child_process'); + +const result = spawnSync(process.execPath, ['--interactive', '--input-type=module']); + +assert.strictEqual(result.stderr.toString(), 'Cannot specify --input-type for REPL\n'); +assert.notStrictEqual(result.exitCode, 0); diff --git a/test/js/node/test/parallel/test-repl-user-error-handler.js b/test/js/node/test/parallel/test-repl-user-error-handler.js new file mode 100644 index 000000000000..31bd46b13d36 --- /dev/null +++ b/test/js/node/test/parallel/test-repl-user-error-handler.js @@ -0,0 +1,84 @@ +'use strict'; +const common = require('../common'); +const { start } = require('node:repl'); +const assert = require('node:assert'); +const { PassThrough } = require('node:stream'); +const { once } = require('node:events'); +const test = require('node:test'); +const { spawn } = require('node:child_process'); + +function* generateCases() { + for (const async of [false, true]) { + for (const handleErrorReturn of ['ignore', 'print', 'unhandled', 'badvalue']) { + if (handleErrorReturn === 'badvalue' && async) { + // Handled through a separate test using a child process + continue; + } + yield { async, handleErrorReturn }; + } + } +} + +for (const { async, handleErrorReturn } of generateCases()) { + test(`async: ${async}, handleErrorReturn: ${handleErrorReturn}`, async () => { + let err; + const options = { + input: new PassThrough(), + output: new PassThrough().setEncoding('utf8'), + handleError: common.mustCall((e) => { + err = e; + queueMicrotask(() => repl.emit('handled-error')); + return handleErrorReturn; + }) + }; + + let uncaughtExceptionEvent; + if (handleErrorReturn === 'unhandled' && async) { + process.removeAllListeners('uncaughtException'); // Remove the test runner's handler + uncaughtExceptionEvent = once(process, 'uncaughtException'); + } + + const repl = start(options); + const inputString = async ? + 'setImmediate(() => { throw new Error("testerror") })\n42\n' : + 'throw new Error("testerror")\n42\n'; + if (handleErrorReturn === 'badvalue') { + assert.throws(() => options.input.end(inputString), /ERR_INVALID_STATE/); + return; + } + options.input.end(inputString); + + await once(repl, 'handled-error'); + assert.strictEqual(err.message, 'testerror'); + const outputString = options.output.read(); + assert.match(outputString, /42/); + + if (handleErrorReturn === 'print') { + assert.match(outputString, /testerror/); + } else { + assert.doesNotMatch(outputString, /testerror/); + } + + if (uncaughtExceptionEvent) { + const [uncaughtErr] = await uncaughtExceptionEvent; + assert.strictEqual(uncaughtErr, err); + } + }); +} + +test('async: true, handleErrorReturn: badvalue', async () => { + // Can't test this the same way as the other combinations + // since this will take the process down in a way that + // cannot be caught. + const proc = spawn(process.execPath, ['-e', ` + require('node:repl').start({ + handleError: () => 'badvalue' + }) + `], { encoding: 'utf8', stdio: 'pipe' }); + proc.stdin.end('throw new Error("foo");'); + let stderr = ''; + proc.stderr.setEncoding('utf8').on('data', (data) => stderr += data); + const [exit] = await once(proc, 'close'); + assert.strictEqual(exit, 1); + assert.match(stderr, /ERR_INVALID_STATE.+badvalue/); +}); diff --git a/test/js/node/test/parallel/test-repl.js b/test/js/node/test/parallel/test-repl.js new file mode 100644 index 000000000000..c325abb6b4ec --- /dev/null +++ b/test/js/node/test/parallel/test-repl.js @@ -0,0 +1,1053 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const net = require('net'); +const repl = require('repl'); +const { inspect } = require('util'); + +const message = 'Read, Eval, Print Loop'; +const prompt_unix = 'node via Unix socket> '; +const prompt_tcp = 'node via TCP socket> '; + +// Absolute path to test/fixtures/a.js +const moduleFilename = fixtures.path('a'); + +// Function for REPL to run +globalThis.invoke_me = function(arg) { + return `invoked ${arg}`; +}; + +// Helpers for describing the expected output: +const kArrow = /^ *\^+ *$/; // Arrow of ^ pointing to syntax error location +const kSource = Symbol('kSource'); // Placeholder standing for input readback + +async function runReplTests(socket, prompt, tests) { + let lineBuffer = ''; + + for (const { send, expect } of tests) { + // Expect can be a single line or multiple lines + const expectedLines = Array.isArray(expect) ? expect : [ expect ]; + + console.error('\n------------'); + console.error('out:', JSON.stringify(send)); + socket.write(`${send}\n`); + + for (let expectedLine of expectedLines) { + // Special value: kSource refers to last sent source text + if (expectedLine === kSource) + expectedLine = send; + + while (!lineBuffer.includes('\n')) { + lineBuffer += await event(socket, expect); + + // Cut away the initial prompt + while (lineBuffer.startsWith(prompt)) + lineBuffer = lineBuffer.slice(prompt.length); + + // Allow to match partial text if no newline was received, because + // sending newlines from the REPL itself would be redundant + // (e.g. in the `| ` multiline prompt: The user already pressed + // enter for that, so the REPL shouldn't do it again!). + if (lineBuffer === expectedLine && !expectedLine.includes('\n')) + lineBuffer += '\n'; + } + + // Split off the current line. + const newlineOffset = lineBuffer.indexOf('\n'); + let actualLine = lineBuffer.slice(0, newlineOffset); + lineBuffer = lineBuffer.slice(newlineOffset + 1); + + // This might have been skipped in the loop above because the buffer + // already contained a \n to begin with and the entire loop was skipped. + while (actualLine.startsWith(prompt)) + actualLine = actualLine.slice(prompt.length); + + console.error('in:', JSON.stringify(actualLine)); + + // Match a string directly, or a RegExp. + if (typeof expectedLine === 'string') { + assert.strictEqual(actualLine, expectedLine); + } else { + assert.match(actualLine, expectedLine); + } + } + } + + const remainder = socket.read(); + assert(remainder === '' || remainder === null); +} + +const unixTests = [ + { + send: '', + expect: '' + }, + { + send: 'message', + expect: `'${message}'` + }, + { + send: 'invoke_me(987)', + expect: '\'invoked 987\'' + }, + { + send: 'a = 12345', + expect: '12345' + }, + { + send: '{a:1}', + expect: '{ a: 1 }' + }, +]; + +const strictModeTests = [ + { + send: 'ref = 1', + expect: [/^Uncaught ReferenceError:\s/] + }, +]; + +const possibleTokensAfterIdentifierWithLineBreak = [ + '(\n)', + '[\n0]', + '+\n1', '- \n1', '* \n1', '/ \n1', '% \n1', '** \n1', + '== \n1', '=== \n1', '!= \n1', '!== \n1', '< \n1', '> \n1', '<= \n1', '>= \n1', + '&& \n1', '|| \n1', '?? \n1', + '= \n1', '+= \n1', '-= \n1', '*= \n1', '/= \n1', '%= \n1', + ': \n', + '? \n1: 1', +]; + +const errorTests = [ + // Uncaught error throws and prints out + { + send: 'throw new Error(\'test error\');', + expect: ['Uncaught Error: test error'] + }, + { + send: "throw { foo: 'bar' };", + expect: "Uncaught { foo: 'bar' }" + }, + // Common syntax error is treated as multiline command + { + send: 'function test_func() {', + expect: '| ' + }, + // You can recover with the .break command + { + send: '.break', + expect: '' + }, + // But passing the same string to eval() should throw + { + send: 'eval("function test_func() {")', + expect: [/^Uncaught SyntaxError: /] + }, + // Can handle multiline template literals + { + send: '`io.js', + expect: '| ' + }, + // Special REPL commands still available + { + send: '.break', + expect: '' + }, + // Template expressions + { + send: '`io.js ${"1.0"', + expect: '| ' + }, + { + send: '+ ".2"}`', + expect: '\'io.js 1.0.2\'' + }, + { + send: '`io.js ${', + expect: '| ' + }, + { + send: '"1.0" + ".2"}`', + expect: '\'io.js 1.0.2\'' + }, + // Dot prefix in multiline commands aren't treated as commands + { + send: '("a"', + expect: '| ' + }, + { + send: '.charAt(0))', + expect: '\'a\'' + }, + // Floating point numbers are not interpreted as REPL commands. + { + send: '.1234', + expect: '0.1234' + }, + // Floating point expressions are not interpreted as REPL commands + { + send: '.1+.1', + expect: '0.2' + }, + // Can parse valid JSON + { + send: 'JSON.parse(\'{"valid": "json"}\');', + expect: '{ valid: \'json\' }' + }, + // Invalid input to JSON.parse error is special case of syntax error, + // should throw + { + send: 'JSON.parse(\'{invalid: \\\'json\\\'}\');', + expect: [ + 'Uncaught:', + /^SyntaxError: /, + ], + }, + // End of input to JSON.parse error is special case of syntax error, + // should throw + { + send: 'JSON.parse(\'066\');', + expect: [/^Uncaught SyntaxError: /] + }, + // should throw + { + send: 'JSON.parse(\'{\');', + expect: [ + 'Uncaught:', + /^SyntaxError: /, + ], + }, + // invalid RegExps are a special case of syntax error, + // should throw + { + send: '/(/;', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + // invalid RegExp modifiers are a special case of syntax error, + // should throw (GH-4012) + { + send: 'new RegExp("foo", "wrong modifier");', + expect: [/^Uncaught SyntaxError: /] + }, + // Strict mode syntax errors should be caught (GH-5178) + { + send: '(function() { "use strict"; return 0755; })()', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '(function(a, a, b) { "use strict"; return a + b + c; })()', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '(function() { "use strict"; with (this) {} })()', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '(function() { "use strict"; var x; delete x; })()', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '(function() { "use strict"; eval = 17; })()', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '(function() { "use strict"; if (true) function f() { } })()', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + /^SyntaxError: /, + ] + }, + // Named functions can be used: + { + send: 'function blah() { return 1; }', + expect: 'undefined' + }, + { + send: 'blah()', + expect: '1' + }, + // Functions should not evaluate twice (#2773) + { + send: 'var I = [1,2,3,function() {}]; I.pop()', + expect: '[Function (anonymous)]' + }, + // Multiline object + { + send: '{}),({}', + expect: '| ', + }, + { + send: '}', + expect: [ + '{}),({}', + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '{ a: ', + expect: '| ' + }, + { + send: '1 }', + expect: '{ a: 1 }' + }, + // Multiline string-keyed object (e.g. JSON) + { + send: '{ "a": ', + expect: '| ' + }, + { + send: '1 }', + expect: '{ a: 1 }' + }, + // Multiline class with private member. + { + send: 'class Foo { #private = true ', + expect: '| ' + }, + // Class field with bigint. + { + send: 'num = 123456789n', + expect: '| ' + }, + // Static class features. + { + send: 'static foo = "bar" }', + expect: 'undefined' + }, + // Multiline anonymous function with comment + { + send: '(function() {', + expect: '| ' + }, + { + send: '// blah', + expect: '| ' + }, + { + send: 'return 1n;', + expect: '| ' + }, + { + send: '})()', + expect: '1n' + }, + // Multiline function call + { + send: 'function f(){}; f(f(1,', + expect: '| ' + }, + { + send: '2)', + expect: '| ' + }, + { + send: ')', + expect: 'undefined' + }, + // `npm` prompt error message. + { + send: 'npm install foobar', + expect: [ + 'npm should be run outside of the Node.js REPL, in your normal shell.', + '(Press Ctrl+D to exit.)', + ] + }, + { + send: 'let npm = () => {};', + expect: 'undefined' + }, + ...possibleTokensAfterIdentifierWithLineBreak.map((token) => ( + { + send: `npm ${token}; undefined`, + expect: '| undefined' + } + )), + { + send: '(function() {\n\nreturn 1;\n})()', + expect: '| | | 1' + }, + { + send: '{\n\na: 1\n}', + expect: '| | | { a: 1 }' + }, + { + send: 'url.format("http://google.com")', + expect: '\'http://google.com/\'' + }, + { + send: 'var path = 42; path', + expect: '42' + }, + // This makes sure that we don't print `undefined` when we actually print + // the error message + { + send: '.invalid_repl_command', + expect: 'Invalid REPL keyword' + }, + // This makes sure that we don't crash when we use an inherited property as + // a REPL command + { + send: '.toString', + expect: 'Invalid REPL keyword' + }, + // Fail when we are not inside a String and a line continuation is used + { + send: '[] \\', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + // Do not fail when a String is created with line continuation + { + send: '\'the\\\nfourth\\\neye\'', + expect: ['| | \'thefourtheye\''] + }, + // Don't fail when a partial String is created and line continuation is used + // with whitespace characters at the end of the string. We are to ignore it. + // This test is to make sure that we properly remove the whitespace + // characters at the end of line, unlike the buggy `trimWhitespace` function + { + send: ' \t .break \t ', + expect: '' + }, + // Multiline strings preserve whitespace characters in them + { + send: '\'the \\\n fourth\t\t\\\n eye \'', + expect: '| | \'the fourth\\t\\t eye \'' + }, + // More than one multiline strings also should preserve whitespace chars + { + send: '\'the \\\n fourth\' + \'\t\t\\\n eye \'', + expect: '| | \'the fourth\\t\\t eye \'' + }, + // using REPL commands within a string literal should still work + { + send: '\'\\\n.break', + expect: '| ' + prompt_unix + }, + // Using REPL command "help" within a string literal should still work + { + send: '\'thefourth\\\n.help\neye\'', + expect: [ + /\.break/, + /\.clear/, + /\.exit/, + /\.help/, + /\.load/, + /\.save/, + '', + 'Press Ctrl+C to abort current expression, Ctrl+D to exit the REPL', + /'thefourtheye'/, + ] + }, + // Check for wrapped objects. + { + send: '{ a: 1 }.a', // ({ a: 1 }.a); + expect: '1' + }, + { + send: '{ a: 1 }.a;', // { a: 1 }.a; + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '{ a: 1 }["a"] === 1', // ({ a: 1 }['a'] === 1); + expect: 'true' + }, + { + send: '{ a: 1 }["a"] === 1;', // { a: 1 }; ['a'] === 1; + expect: 'false' + }, + // Empty lines in the REPL should be allowed + { + send: '\n\r\n\r\n', + expect: '' + }, + // Empty lines in the string literals should not affect the string + { + send: '\'the\\\n\\\nfourtheye\'\n', + expect: '| | \'thefourtheye\'' + }, + // Regression test for https://github.com/nodejs/node/issues/597 + { + send: '/(.)(.)(.)(.)(.)(.)(.)(.)(.)/.test(\'123456789\')\n', + expect: 'true' + }, + // The following test's result depends on the RegExp's match from the above + { + send: 'RegExp.$1\nRegExp.$2\nRegExp.$3\nRegExp.$4\nRegExp.$5\n' + + 'RegExp.$6\nRegExp.$7\nRegExp.$8\nRegExp.$9\n', + expect: ['\'1\'', '\'2\'', '\'3\'', '\'4\'', '\'5\'', '\'6\'', + '\'7\'', '\'8\'', '\'9\''] + }, + // Regression tests for https://github.com/nodejs/node/issues/2749 + { + send: 'function x() {\nreturn \'\\n\';\n }', + expect: '| | undefined' + }, + { + send: 'function x() {\nreturn \'\\\\\';\n }', + expect: '| | undefined' + }, + // Regression tests for https://github.com/nodejs/node/issues/3421 + { + send: 'function x() {\n//\'\n }', + expect: '| | undefined' + }, + { + send: 'function x() {\n//"\n }', + expect: '| | undefined' + }, + { + send: 'function x() {//\'\n }', + expect: '| undefined' + }, + { + send: 'function x() {//"\n }', + expect: '| undefined' + }, + { + send: 'function x() {\nvar i = "\'";\n }', + expect: '| | undefined' + }, + { + send: 'function x(/*optional*/) {}', + expect: 'undefined' + }, + { + send: 'function x(/* // 5 */) {}', + expect: 'undefined' + }, + { + send: '// /* 5 */', + expect: 'undefined' + }, + { + send: '"//"', + expect: '\'//\'' + }, + { + send: '"data /*with*/ comment"', + expect: '\'data /*with*/ comment\'' + }, + { + send: 'function x(/*fn\'s optional params*/) {}', + expect: 'undefined' + }, + { + send: '/* \'\n"\n\'"\'\n*/', + expect: '| | | undefined' + }, + // REPL should get a normal require() function, not one that allows + // access to internal modules without the --expose-internals flag. + { + // Shrink the stack trace to avoid having to update this test whenever the + // implementation of require() changes. It's set to 5 because somehow setting it + // to a lower value breaks the error formatting and the message becomes + // "Uncaught [Error...", which is probably a bug(?). + send: 'Error.stackTraceLimit = 5; require("internal/repl")', + expect: [ + /^Uncaught Error: Cannot find module 'internal\/repl'/, + /^Require stack:/, + /^- /, // This just tests MODULE_NOT_FOUND so let's skip the stack trace + /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. + /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. + /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. + /^ {4}at .*/, // Some stack frame that we have to capture otherwise error message is buggy. + " code: 'MODULE_NOT_FOUND',", + " requireStack: [ '' ]", + '}', + ] + }, + // REPL should handle quotes within regexp literal in multiline mode + { + send: "function x(s) {\nreturn s.replace(/'/,'');\n}", + expect: '| | undefined' + }, + { + send: "function x(s) {\nreturn s.replace(/'/,'');\n}", + expect: '| | undefined' + }, + { + send: 'function x(s) {\nreturn s.replace(/"/,"");\n}', + expect: '| | undefined' + }, + { + send: 'function x(s) {\nreturn s.replace(/.*/,"");\n}', + expect: '| | undefined' + }, + { + send: '{ var x = 4; }', + expect: 'undefined' + }, + // Illegal token is not recoverable outside string literal, RegExp literal, + // or block comment. https://github.com/nodejs/node/issues/3611 + { + send: 'a = 3.5e', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + // Mitigate https://github.com/nodejs/node/issues/548 + { + send: 'function name(){ return "node"; };name()', + expect: '\'node\'' + }, + { + send: 'function name(){ return "nodejs"; };name()', + expect: '\'nodejs\'' + }, + // Avoid emitting repl:line-number for SyntaxError + { + send: 'a = 3.5e', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + // Avoid emitting stack trace + { + send: 'a = 3.5e', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + + // https://github.com/nodejs/node/issues/9850 + { + send: 'function* foo() {}; foo().next();', + expect: '{ value: undefined, done: true }' + }, + + { + send: 'function *foo() {}; foo().next();', + expect: '{ value: undefined, done: true }' + }, + + { + send: 'function*foo() {}; foo().next();', + expect: '{ value: undefined, done: true }' + }, + + { + send: 'function * foo() {}; foo().next()', + expect: '{ value: undefined, done: true }' + }, + + // https://github.com/nodejs/node/issues/9300 + { + send: 'function foo() {\nvar bar = 1 / 1; // "/"\n}', + expect: '| | undefined' + }, + + { + send: '(function() {\nreturn /foo/ / /bar/;\n}())', + expect: '| | NaN' + }, + + { + send: '(function() {\nif (false) {} /bar"/;\n}())', + expect: '| | undefined' + }, + + // https://github.com/nodejs/node/issues/16483 + { + send: 'new Proxy({x:42}, {get(){throw null}});', + expect: 'Proxy [ { x: 42 }, { get: [Function: get] } ]' + }, + { + send: 'repl.writer.options.showProxy = false, new Proxy({x:42}, {});', + expect: 'Proxy({ x: 42 })' + }, + + // Newline within template string maintains whitespace. + { + send: '`foo \n`', + expect: '| \'foo \\n\'' + }, + // Whitespace is not evaluated. + { + send: ' \t \n', + expect: 'undefined' + }, + // Do not parse `...[]` as a REPL keyword + { + send: '...[]', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + // Bring back the repl to prompt + { + send: '.break', + expect: '' + }, + { + send: 'console.log("Missing comma in arg list" process.version)', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: 'x = {\nfield\n{', + expect: [ + '| | {', + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: '(2 + 3))', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: 'if (typeof process === "object"); {', + expect: '| ' + }, + { + send: 'console.log("process is defined");', + expect: '| ' + }, + { + send: '} else {', + expect: [ + kSource, + kArrow, + '', + /^Uncaught SyntaxError: /, + ] + }, + { + send: 'console', + expect: [ + 'Object [console] {', + ' log: [Function: log],', + ' info: [Function: info],', + ' debug: [Function: debug],', + ' warn: [Function: warn],', + ' error: [Function: error],', + ' dir: [Function: dir],', + ' time: [Function: time],', + ' timeEnd: [Function: timeEnd],', + ' timeLog: [Function: timeLog],', + ' trace: [Function: trace],', + ' assert: [Function: assert],', + ' clear: [Function: clear],', + ' count: [Function: count],', + ' countReset: [Function: countReset],', + ' group: [Function: group],', + ' groupEnd: [Function: groupEnd],', + ' table: [Function: table],', + / {2}dirxml: \[Function: (dirxml|log)],/, + / {2}groupCollapsed: \[Function: (groupCollapsed|group)],/, + / {2}Console: \[Function: Console],?/, + ...process.features.inspector ? [ + ' profile: [Function: profile],', + ' profileEnd: [Function: profileEnd],', + ' timeStamp: [Function: timeStamp],', + ' context: [Function: context],', + ' createTask: [Function: createTask]', + ] : [], + '}', + ] + }, +]; + +const tcpTests = [ + { + send: '', + expect: '' + }, + { + send: 'invoke_me(333)', + expect: '\'invoked 333\'' + }, + { + send: 'a += 1', + expect: '12346' + }, + { + send: `require(${JSON.stringify(moduleFilename)}).number`, + expect: '42' + }, + { + send: 'import comeOn from \'fhqwhgads\'', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { default: comeOn } = await import("fhqwhgads");', + ] + }, + { + send: 'import { export1, export2 } from "module-name"', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { export1, export2 } = await import("module-name");', + ] + }, + { + send: 'import * as name from "module-name";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const name = await import("module-name");', + ] + }, + { + send: 'import "module-name";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: await import("module-name");', + ] + }, + { + send: 'import { export1 as localName1, export2 } from "bar";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { export1: localName1, export2 } = await import("bar");', + ] + }, + { + send: 'import alias from "bar";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { default: alias } = await import("bar");', + ] + }, + { + send: 'import alias, {namedExport} from "bar";', + expect: [ + kSource, + kArrow, + '', + 'Uncaught:', + 'SyntaxError: Cannot use import statement inside the Node.js REPL, \ +alternatively use dynamic import: const { default: alias, namedExport } = await import("bar");', + ] + }, +]; + +(async function() { + { + const [ socket, replServer ] = await startUnixRepl(); + + await runReplTests(socket, prompt_unix, unixTests); + await runReplTests(socket, prompt_unix, errorTests); + replServer.replMode = repl.REPL_MODE_STRICT; + await runReplTests(socket, prompt_unix, strictModeTests); + + socket.end(); + } + { + const [ socket ] = await startTCPRepl(); + + await runReplTests(socket, prompt_tcp, tcpTests); + + socket.end(); + } + common.allowGlobals(globalThis.invoke_me, globalThis.message, globalThis.a, globalThis.blah, + globalThis.I, globalThis.f, globalThis.path, globalThis.x, globalThis.name, globalThis.foo); +})().then(common.mustCall()); + +function startTCPRepl() { + let resolveSocket, resolveReplServer; + + const server = net.createServer(common.mustCall((socket) => { + assert.strictEqual(server, socket.server); + + socket.on('end', common.mustCall(() => { + socket.end(); + })); + + resolveReplServer(repl.start(prompt_tcp, socket)); + })); + + server.listen(0, common.mustCall(() => { + const client = net.createConnection(server.address().port); + + client.setEncoding('utf8'); + + client.on('connect', common.mustCall(() => { + assert.strictEqual(client.readable, true); + assert.strictEqual(client.writable, true); + + resolveSocket(client); + })); + + client.on('close', common.mustCall(() => { + server.close(); + })); + })); + + return Promise.all([ + new Promise((resolve) => resolveSocket = resolve), + new Promise((resolve) => resolveReplServer = resolve), + ]); +} + +function startUnixRepl() { + let resolveSocket, resolveReplServer; + + const server = net.createServer(common.mustCall((socket) => { + assert.strictEqual(server, socket.server); + + socket.on('end', common.mustCall(() => { + socket.end(); + })); + + const replServer = repl.start({ + prompt: prompt_unix, + input: socket, + output: socket, + useGlobal: true + }); + replServer.context.message = message; + resolveReplServer(replServer); + })); + + tmpdir.refresh(); + + server.listen(common.PIPE, common.mustCall(() => { + const client = net.createConnection(common.PIPE); + + client.setEncoding('utf8'); + + client.on('connect', common.mustCall(() => { + assert.strictEqual(client.readable, true); + assert.strictEqual(client.writable, true); + + resolveSocket(client); + })); + + client.on('close', common.mustCall(() => { + server.close(); + })); + })); + + return Promise.all([ + new Promise((resolve) => resolveSocket = resolve), + new Promise((resolve) => resolveReplServer = resolve), + ]); +} + +function event(ee, expected) { + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + const data = inspect(expected, { compact: false }); + const msg = `The REPL did not reply as expected for:\n\n${data}`; + reject(new Error(msg)); + }, common.platformTimeout(9999)); + ee.once('data', common.mustCall((...args) => { + clearTimeout(timeout); + resolve(...args); + })); + }); +} diff --git a/test/js/node/test/sequential/test-repl-timeout-throw.js b/test/js/node/test/sequential/test-repl-timeout-throw.js new file mode 100644 index 000000000000..d0cbd6fdca71 --- /dev/null +++ b/test/js/node/test/sequential/test-repl-timeout-throw.js @@ -0,0 +1,59 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +const spawn = require('child_process').spawn; + +const child = spawn(process.execPath, [ '--interactive' ], { + stdio: [null, null, 2], +}); + +let stdout = ''; +child.stdout.setEncoding('utf8'); +child.stdout.on('data', function(c) { + process.stdout.write(c); + stdout += c; + if (stdout.includes('> THROW 2')) + child.stdin.end(); +}); + +child.stdin.write = function(original) { + return function(c) { + process.stderr.write(c); + return original.call(child.stdin, c); + }; +}(child.stdin.write); + +child.stdout.once('data', function() { + child.stdin.write('let throws = 0;'); + child.stdin.write('process.on("exit",function(){console.log(throws)});'); + child.stdin.write('function thrower(){console.log("THROW",throws++);XXX};'); + child.stdin.write('setTimeout(thrower);""\n'); + + setTimeout(fsTest, 50); + function fsTest() { + const f = JSON.stringify(__filename); + child.stdin.write(`fs.readFile(${f}, thrower);\n`); + setTimeout(eeTest, 50); + } + + function eeTest() { + child.stdin.write('setTimeout(function() {\n' + + ' const events = require("events");\n' + + ' let e = new events.EventEmitter;\n' + + ' process.nextTick(function() {\n' + + ' e.on("x", thrower);\n' + + ' setTimeout(function() {\n' + + ' e.emit("x");\n' + + ' });\n' + + ' });\n' + + '});"";\n'); + } +}); + +child.on('close', common.mustCall((c) => { + assert.strictEqual(c, 0); + // Make sure we got 3 throws, in the end. + const lastLine = stdout.trim().split(/\r?\n/).pop(); + assert.strictEqual(lastLine, '> 3'); +})); diff --git a/test/js/node/vm/vm.test.ts b/test/js/node/vm/vm.test.ts index 296971c440be..67114b4f4518 100644 --- a/test/js/node/vm/vm.test.ts +++ b/test/js/node/vm/vm.test.ts @@ -524,8 +524,35 @@ function testRunInContext({ fn, isIsolated, isNew }: TestRunInContextArg) { test.todo("can specify columnOffset", () => { // }); - test.todo("can specify displayErrors", () => { - // + test("can specify displayErrors", () => { + const src = 'throw new Error("boom")'; + // displayErrors: false — no source-line/caret decoration on the stack. + try { + new Script(src, { filename: "t.vm" }).runInThisContext({ displayErrors: false }); + expect.unreachable(); + } catch (e: any) { + expect(e.message).toBe("boom"); + expect(e.stack).not.toMatch(/^t\.vm:1\n/); + } + // displayErrors: true (default) — stack is decorated with the source line. + try { + new Script(src, { filename: "t.vm" }).runInThisContext({ displayErrors: true }); + expect.unreachable(); + } catch (e: any) { + expect(e.stack).toMatch(/^t\.vm:1\nthrow new Error/); + } + // Same for runInContext. + try { + new Script(src, { filename: "t.vm" }).runInContext(createContext({}), { displayErrors: false }); + expect.unreachable(); + } catch (e: any) { + expect(e.stack).not.toMatch(/^t\.vm:1\n/); + } + }); + test("throws SyntaxError at construction like Node", () => { + // Node's vm.Script parses eagerly; the REPL depends on this. + expect(() => new Script("function {")).toThrow(SyntaxError); + expect(() => new Script("const x = ")).toThrow(SyntaxError); }); test.todo("can specify timeout", () => { // @@ -698,15 +725,12 @@ resp.text().then((a) => { }); test("can't use export syntax in vm.Script", () => { - expect(() => { - const script = new Script("export default {};"); - script.runInThisContext(); - }).toThrow({ name: "SyntaxError", message: "Unexpected keyword 'export'" }); - - expect(() => { - const script = new Script("export default {};"); - script.createCachedData(); - }).toThrow({ message: "createCachedData failed" }); + // vm.Script now parses eagerly (like Node), so the SyntaxError surfaces at + // construction rather than at runInThisContext()/createCachedData(). + expect(() => new Script("export default {};")).toThrow({ + name: "SyntaxError", + message: "Unexpected keyword 'export'", + }); }); test("rejects invalid bytecode", () => { From 71c7172ac432fda1c55f24eb67cd92b796f59f2c Mon Sep 17 00:00:00 2001 From: Alistair Smith Date: Tue, 7 Jul 2026 20:26:21 -0700 Subject: [PATCH 38/73] repl: make --interactive -e errors fatal; decorate vm.Script parse errors --interactive -e now runs the -e script before REPL.start so a syntax or runtime error propagates as an uncaught exception (exit 1) instead of being swallowed by the REPL's process-wide capture callback, matching Node's internal/main/repl.js. new vm.Script() now prepends Node's arrow header (url:line / source / caret) to compile-time SyntaxError stacks unconditionally, matching node_contextify.cc DecorateErrorStack. --- src/js/eval/node-repl.ts | 20 ++++----- src/jsc/bindings/NodeVM.cpp | 68 +++++++++++++++++++++++++++++++ src/jsc/bindings/NodeVM.h | 5 +++ src/jsc/bindings/NodeVMScript.cpp | 3 ++ test/js/bun/repl/repl.test.ts | 29 +++++++++---- test/js/node/vm/vm.test.ts | 24 +++++++++++ 6 files changed, 128 insertions(+), 21 deletions(-) diff --git a/src/js/eval/node-repl.ts b/src/js/eval/node-repl.ts index 0f969d031e96..2a995faa8ca5 100644 --- a/src/js/eval/node-repl.ts +++ b/src/js/eval/node-repl.ts @@ -23,6 +23,13 @@ if (ext) { 'Type ".help" for more information.', ); + // `node -i -e`: an -e error is fatal (uncaught, exit 1), not caught by the + // REPL. Runs before REPL.start so the shim's process-wide capture callback + // isn't installed yet; `var`/`function` still land on globalThis. + if (evalScript !== undefined) { + require("node:vm").runInThisContext(evalScript, { filename: "[eval]", displayErrors: true }); + } + createInternalRepl(process.env, (err: Error | null, replServer: any) => { if (err) throw err; @@ -33,18 +40,5 @@ if (ext) { } process.exit(); }); - - // `node -i -e`: Node runs the -e script as a separate compilation unit - // AFTER the REPL starts, so `var`/`function` land on the global object and - // a syntax/runtime error is reported at [eval]:1 with the REPL still live. - if (evalScript !== undefined) { - try { - require("node:vm").runInThisContext(evalScript, { filename: "[eval]", displayErrors: true }); - } catch (e) { - // Route through the REPL's own error printer so `Uncaught …` and the - // decorated stack render exactly as if typed at the prompt. - replServer._handleError(e); - } - } }); } diff --git a/src/jsc/bindings/NodeVM.cpp b/src/jsc/bindings/NodeVM.cpp index fd006bcc3ad1..d99148af80b6 100644 --- a/src/jsc/bindings/NodeVM.cpp +++ b/src/jsc/bindings/NodeVM.cpp @@ -576,6 +576,74 @@ bool handleException(JSGlobalObject* globalObject, VM& vm, NakedPtr:\n\n^\n\n`) using ParserError since there is no CodeBlock +// yet. Node applies this unconditionally at compile time (not displayErrors). +void decorateParseErrorStack(JSGlobalObject* globalObject, VM& vm, JSObject* error, StringView sourceString, const String& filename, const JSC::ParserError& parseError, OrdinalNumber lineOffset, OrdinalNumber columnOffset) +{ + UNUSED_PARAM(globalObject); + auto* errorInstance = dynamicDowncast(error); + if (!errorInstance) + return; + + errorInstance->materializeErrorInfoIfNeeded(vm, vm.propertyNames->stack); + JSValue stackValue = errorInstance->getDirect(vm, vm.propertyNames->stack); + if (!stackValue || !stackValue.isString()) + return; + auto stackHolder = asString(stackValue)->tryGetValue(); + const String& stack = stackHolder.data; + if (stack.isNull()) + return; + + String url = filename.isEmpty() ? "evalmachine."_s : filename; + + // parseError.line() is already lineOffset-adjusted (JSC parses against a + // SourceCode whose start position carries the offset). Undo it to index + // the raw source string. + int reportedLine = parseError.line(); + int64_t physicalLine = static_cast(reportedLine) - lineOffset.zeroBasedInt(); + + String sourceLineText; + unsigned caretColumn = 0; + if (physicalLine >= 1) { + size_t lineStart = 0; + for (int64_t currentLine = 1; currentLine < physicalLine && lineStart != WTF::notFound; currentLine++) { + size_t newline = sourceString.find('\n', lineStart); + lineStart = newline == WTF::notFound ? WTF::notFound : newline + 1; + } + if (lineStart != WTF::notFound) { + size_t lineEnd = sourceString.find('\n', lineStart); + if (lineEnd == WTF::notFound) + lineEnd = sourceString.length(); + StringView lineView = sourceString.substring(lineStart, lineEnd - lineStart); + if (lineView.endsWith('\r')) + lineView = lineView.left(lineView.length() - 1); + if (lineView.length() <= 1024) { + sourceLineText = lineView.toString(); + int col0 = parseError.token().m_startPosition.column(); + if (physicalLine == 1) + col0 -= columnOffset.zeroBasedInt(); + caretColumn = col0 >= 0 ? static_cast(col0) + 1 : 1; + } + } + } + + String prepend; + if (!sourceLineText.isNull() && caretColumn >= 1 && caretColumn <= sourceLineText.length() + 1) { + StringBuilder caretLine; + for (unsigned i = 1; i < caretColumn; i++) + caretLine.append(i <= sourceLineText.length() && sourceLineText[i - 1] == '\t' ? '\t' : ' '); + caretLine.append('^'); + prepend = makeString(url, ':', reportedLine, '\n', sourceLineText, '\n', caretLine.toString(), "\n\n"_s, stack); + } else { + prepend = makeString(url, ':', reportedLine, '\n', stack); + } + + const auto& decoratedName = WebCore::builtinNames(vm).vmErrorDecoratedPrivateName(); + errorInstance->putDirect(vm, vm.propertyNames->stack, jsString(vm, prepend), JSC::PropertyAttribute::DontEnum | 0); + errorInstance->putDirect(vm, decoratedName, jsBoolean(true), JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::ReadOnly); +} + // Returns an encoded exception if the options are invalid. // Otherwise, returns an empty optional. std::optional getNodeVMContextOptions(JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSValue optionsArg, NodeVMContextOptions& outOptions, ASCIILiteral codeGenerationKey, JSValue* importer) diff --git a/src/jsc/bindings/NodeVM.h b/src/jsc/bindings/NodeVM.h index 590f3ce69451..52a4a8d38cc0 100644 --- a/src/jsc/bindings/NodeVM.h +++ b/src/jsc/bindings/NodeVM.h @@ -12,6 +12,10 @@ #include #include +namespace JSC { +class ParserError; +} + namespace Bun { class NodeVMGlobalObject; @@ -26,6 +30,7 @@ bool extractCachedData(JSValue cachedDataValue, WTF::Vector& outCachedD String stringifyAnonymousFunction(JSGlobalObject* globalObject, const ArgList& args, ThrowScope& scope, int* outOffset); JSC::EncodedJSValue createCachedData(JSGlobalObject* globalObject, const JSC::SourceCode& source); bool handleException(JSGlobalObject* globalObject, VM& vm, NakedPtr exception, ThrowScope& throwScope); +void decorateParseErrorStack(JSGlobalObject* globalObject, VM& vm, JSObject* error, StringView sourceString, const String& filename, const JSC::ParserError& parseError, OrdinalNumber lineOffset, OrdinalNumber columnOffset); std::optional getNodeVMContextOptions(JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSValue optionsArg, NodeVMContextOptions& outOptions, ASCIILiteral codeGenerationKey, JSValue* importer); NodeVMGlobalObject* getGlobalObjectFromContext(JSGlobalObject* globalObject, JSValue contextValue, bool canThrow); JSC::EncodedJSValue INVALID_ARG_VALUE_VM_VARIATION(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, WTF::ASCIILiteral name, JSC::JSValue value); diff --git a/src/jsc/bindings/NodeVMScript.cpp b/src/jsc/bindings/NodeVMScript.cpp index 8b95f95bfdd3..986623e2b2cd 100644 --- a/src/jsc/bindings/NodeVMScript.cpp +++ b/src/jsc/bindings/NodeVMScript.cpp @@ -138,6 +138,9 @@ constructScript(JSGlobalObject* globalObject, CallFrame* callFrame, JSValue newT if (!JSC::checkSyntax(vm, source, parseError)) { auto exception = parseError.toErrorObject(globalObject, source, -1); RETURN_IF_EXCEPTION(scope, {}); + // Node always attaches the arrow header to compile-time SyntaxErrors + // (node_contextify.cc DecorateErrorStack), independent of displayErrors. + decorateParseErrorStack(globalObject, vm, exception, sourceString, options.filename, parseError, options.lineOffset, options.columnOffset); throwException(globalObject, scope, exception); return {}; } diff --git a/test/js/bun/repl/repl.test.ts b/test/js/bun/repl/repl.test.ts index 37ae53e3eb3e..4b1fc845f115 100644 --- a/test/js/bun/repl/repl.test.ts +++ b/test/js/bun/repl/repl.test.ts @@ -1277,9 +1277,9 @@ describe("--interactive", () => { expect(exitCode).toBe(0); }); - // `node -i -e 'code'`: -e runs as a separate Script after REPL.start(), so - // `var`/`function` declarations land on globalThis and are visible in the REPL. - test("-e runs after REPL start; var/function declarations are visible", async () => { + // `node -i -e 'code'`: -e runs as its own Script against globalThis, so + // `var`/`function` declarations are visible from the REPL prompt. + test("-e var/function declarations are visible in the REPL", async () => { const { stdout, stderr, exitCode } = await runInteractive( ["-e", "var fromVar = 1; function f() { return 42 }"], "fromVar + f()\n", @@ -1289,22 +1289,35 @@ describe("--interactive", () => { expect(exitCode).toBe(0); }); - test("-e with a syntax error is reported and the REPL still starts", async () => { - const { stdout, stderr, exitCode } = await runInteractive(["-e", "console.log(1"], "2+2\n"); + // `node -i -e ''`: Node exits 1 with a SyntaxError code frame at + // [eval]:1 and never accepts REPL input; not caught by the REPL error handler. + test("-e with a syntax error is fatal and never enters the REPL", async () => { + const { stdout, stderr, exitCode } = await runInteractive(["-e", "console.log(1"], "'stdin-ran'\n"); expect(stdout).toContain("Welcome to Bun"); - expect(stdout).toContain("4"); + // stdin was never evaluated: + expect(stdout).not.toContain("stdin-ran"); // The error is reported against the user's [eval] script, not the bootstrap. expect(stdout + stderr).toMatch(/SyntaxError/); + expect(stdout + stderr).toContain("[eval]"); expect(stdout + stderr).not.toMatch(/node-repl|createInternalRepl|__BUN_EVAL_SCRIPT__/); - expect(exitCode).toBe(0); + expect(exitCode).toBe(1); + }); + + test("-e with a runtime error is fatal and never enters the REPL", async () => { + const { stdout, stderr, exitCode } = await runInteractive(["-e", 'throw new Error("BOOM")'], "'stdin-ran'\n"); + expect(stdout).not.toContain("stdin-ran"); + expect(stdout + stderr).toContain("BOOM"); + expect(stdout + stderr).toContain("[eval]"); + expect(exitCode).toBe(1); }); test.each(["/*", "const x=`foo"])( "-e with an unterminated template/comment cannot swallow the bootstrap (%j)", async bad => { - const { stdout, stderr } = await runInteractive(["-e", bad], ""); + const { stdout, stderr, exitCode } = await runInteractive(["-e", bad], ""); expect(stdout).toContain("Welcome to Bun"); expect(stdout + stderr).toMatch(/SyntaxError/); + expect(exitCode).toBe(1); }, ); diff --git a/test/js/node/vm/vm.test.ts b/test/js/node/vm/vm.test.ts index 67114b4f4518..db62556a2fbb 100644 --- a/test/js/node/vm/vm.test.ts +++ b/test/js/node/vm/vm.test.ts @@ -554,6 +554,30 @@ function testRunInContext({ fn, isIsolated, isNew }: TestRunInContextArg) { expect(() => new Script("function {")).toThrow(SyntaxError); expect(() => new Script("const x = ")).toThrow(SyntaxError); }); + test("compile-time SyntaxError has arrow-decorated stack (Node DecorateErrorStack)", () => { + // Node prepends `:\n\n^\n\n` to compile-time SyntaxErrors + // from `new vm.Script`, unconditionally (independent of displayErrors). + for (const opts of [undefined, { displayErrors: true }, { displayErrors: false }]) { + let err: any; + try { + new Script("%%", opts); + } catch (e) { + err = e; + } + expect(err).toBeInstanceOf(SyntaxError); + expect(err.stack.split("\n").slice(0, 4)).toEqual(["evalmachine.:1", "%%", "^", ""]); + } + + // Custom filename + lineOffset: reported line is offset-adjusted, source + // line and caret still come from the physical position. + let err: any; + try { + new Script("1;\n%%", { filename: "foo.js", lineOffset: 5 }); + } catch (e) { + err = e; + } + expect(err.stack.split("\n").slice(0, 4)).toEqual(["foo.js:7", "%%", "^", ""]); + }); test.todo("can specify timeout", () => { // }); From 5fe78816201cba0caa8dcfc06934b8ee91e80c4d Mon Sep 17 00:00:00 2001 From: Alistair Smith Date: Tue, 7 Jul 2026 21:21:21 -0700 Subject: [PATCH 39/73] repl: append new ErrorCode entries (fixes Rust index drift); drop dead capture-shim refcount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit f25d419 inserted ERR_CANNOT_WATCH_SIGINT / ERR_INSPECTOR_NOT_AVAILABLE / ERR_INVALID_REPL_EVAL_CONFIG / ERR_INVALID_REPL_INPUT into the middle of ErrorCode.ts, shifting every index after 14. src/jsc/ErrorCode.rs mirrors those indices verbatim, so every Rust-thrown ERR_* (path, fs, zlib, crypto, http2, ...) came out with the wrong .code — the ~99 build-70303 failures. Move the four codes to the append-only tail and mirror them in ErrorCode.rs. Also: - node-shims.js / repl.js: drop the never-called removeUncaughtExceptionCaptureCallback + dispatcherInstalled and reduce exceptionCaptureUseCount to a boolean; fix the header comment to match the process-lifetime install semantics. - run_command.rs: -e argv is arbitrary bytes on Linux; lossily normalize before format_json_string_utf8 (whose SAFETY contract requires UTF-8). Drop the stale 'after REPL.start()' from the comment. - repl.test.ts: drop the unreachable listenerOrigin listener + trim the matching test title/comment. --- src/js/internal/repl/node-shims.js | 22 +++------------------- src/js/node/repl.js | 8 ++++---- src/jsc/ErrorCode.rs | 18 +++++++++++++++++- src/jsc/bindings/ErrorCode.ts | 8 ++++---- src/runtime/cli/run_command.rs | 14 +++++++++----- test/js/bun/repl/repl.test.ts | 7 ++----- 6 files changed, 39 insertions(+), 38 deletions(-) diff --git a/src/js/internal/repl/node-shims.js b/src/js/internal/repl/node-shims.js index 0ef0ddd84b4d..4ad1cc95d8bd 100644 --- a/src/js/internal/repl/node-shims.js +++ b/src/js/internal/repl/node-shims.js @@ -353,12 +353,11 @@ function getOwnNonIndexProperties(obj, filter = ALL_PROPERTIES) { // ---- process.addUncaughtExceptionCaptureCallback polyfill ---------------- // Bun only implements the single-callback set/clear API; emulate Node's -// additive API with a dispatcher list. Tracked so the exclusive slot is only -// cleared when the shim itself owns it — never a user's callback — and is -// released once the last REPL closes. +// additive API with a dispatcher list. The shim occupies the exclusive slot +// for the process lifetime once the first REPL starts — see repl.js +// setupExceptionCapture() for the rationale. let captureCallbacks = null; -let dispatcherInstalled = false; function addUncaughtExceptionCaptureCallback(cb) { if (!captureCallbacks) { @@ -377,7 +376,6 @@ function addUncaughtExceptionCaptureCallback(cb) { } catch {} process.exit(1); }); - dispatcherInstalled = true; } catch { // A user capture callback already occupies the exclusive slot. Node's // additive API coexists with it natively; without that engine support, @@ -389,22 +387,8 @@ function addUncaughtExceptionCaptureCallback(cb) { captureCallbacks.push(cb); } -function removeUncaughtExceptionCaptureCallback(cb) { - if (!captureCallbacks) return; - const i = captureCallbacks.indexOf(cb); - if (i !== -1) captureCallbacks.splice(i, 1); - if (captureCallbacks.length === 0) { - captureCallbacks = null; - if (dispatcherInstalled) { - dispatcherInstalled = false; - process.setUncaughtExceptionCaptureCallback(null); - } - } -} - export default { addUncaughtExceptionCaptureCallback, - removeUncaughtExceptionCaptureCallback, // internalBinding('contextify') startSigintWatchdog, stopSigintWatchdog, diff --git a/src/js/node/repl.js b/src/js/node/repl.js index a5a5236a4f03..d5a6f3102507 100644 --- a/src/js/node/repl.js +++ b/src/js/node/repl.js @@ -162,7 +162,7 @@ const parentModule = __node_module__; // AsyncLocalStorage to track which REPL instance owns the current async context // This replaces the domain-based tracking for error handling const replContext = new AsyncLocalStorage(); -let exceptionCaptureUseCount = 0; +let exceptionCaptureInstalled = false; function replExceptionCaptureCallback(err) { const store = replContext.getStore(); @@ -182,9 +182,9 @@ function replExceptionCaptureCallback(err) { // shim's fallthrough re-emits `uncaughtException` with the origin arg so user // listeners still see it. function setupExceptionCapture() { - if (exceptionCaptureUseCount++ === 0) { - require("internal/repl/node-shims").addUncaughtExceptionCaptureCallback(replExceptionCaptureCallback); - } + if (exceptionCaptureInstalled) return; + exceptionCaptureInstalled = true; + require("internal/repl/node-shims").addUncaughtExceptionCaptureCallback(replExceptionCaptureCallback); } const kBufferedCommandSymbol = Symbol("bufferedCommand"); diff --git a/src/jsc/ErrorCode.rs b/src/jsc/ErrorCode.rs index c0f5935d6fa0..8326dd89261c 100644 --- a/src/jsc/ErrorCode.rs +++ b/src/jsc/ErrorCode.rs @@ -711,9 +711,17 @@ impl ErrorCode { pub const FS_CP_SYMLINK_TO_SUBDIRECTORY: ErrorCode = ErrorCode(326); /// `ERR_DIR_CONCURRENT_OPERATION` (instanceof Error) pub const DIR_CONCURRENT_OPERATION: ErrorCode = ErrorCode(327); + /// `ERR_CANNOT_WATCH_SIGINT` (instanceof Error) + pub const CANNOT_WATCH_SIGINT: ErrorCode = ErrorCode(328); + /// `ERR_INSPECTOR_NOT_AVAILABLE` (instanceof Error) + pub const INSPECTOR_NOT_AVAILABLE: ErrorCode = ErrorCode(329); + /// `ERR_INVALID_REPL_EVAL_CONFIG` (instanceof TypeError) + pub const INVALID_REPL_EVAL_CONFIG: ErrorCode = ErrorCode(330); + /// `ERR_INVALID_REPL_INPUT` (instanceof TypeError) + pub const INVALID_REPL_INPUT: ErrorCode = ErrorCode(331); /// == C++ `NODE_ERROR_COUNT`. - pub const COUNT: u16 = 328; + pub const COUNT: u16 = 332; } // ────────────────────────────────────────────────────────────────────────── @@ -1084,6 +1092,10 @@ impl ErrorCode { pub const ERR_SECRETS_INTERACTION_REQUIRED: ErrorCode = ErrorCode::SECRETS_INTERACTION_REQUIRED; pub const ERR_HTTP2_GOAWAY_SESSION: ErrorCode = ErrorCode::HTTP2_GOAWAY_SESSION; pub const ERR_PROXY_TUNNEL: ErrorCode = ErrorCode::PROXY_TUNNEL; + pub const ERR_CANNOT_WATCH_SIGINT: ErrorCode = ErrorCode::CANNOT_WATCH_SIGINT; + pub const ERR_INSPECTOR_NOT_AVAILABLE: ErrorCode = ErrorCode::INSPECTOR_NOT_AVAILABLE; + pub const ERR_INVALID_REPL_EVAL_CONFIG: ErrorCode = ErrorCode::INVALID_REPL_EVAL_CONFIG; + pub const ERR_INVALID_REPL_INPUT: ErrorCode = ErrorCode::INVALID_REPL_INPUT; // NOTE: `ERR_SYSTEM_ERROR` / `ERR_CHILD_CLOSED_BEFORE_REPLY` intentionally // do NOT live here. They belong to the unrelated enum @@ -1429,6 +1441,10 @@ static CODE_STR: [&str; ErrorCode::COUNT as usize] = [ "ERR_FS_CP_EEXIST", "ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY", "ERR_DIR_CONCURRENT_OPERATION", + "ERR_CANNOT_WATCH_SIGINT", + "ERR_INSPECTOR_NOT_AVAILABLE", + "ERR_INVALID_REPL_EVAL_CONFIG", + "ERR_INVALID_REPL_INPUT", ]; // ────────────────────────────────────────────────────────────────────────── diff --git a/src/jsc/bindings/ErrorCode.ts b/src/jsc/bindings/ErrorCode.ts index b0d514b7e5b3..1791e54897f8 100644 --- a/src/jsc/bindings/ErrorCode.ts +++ b/src/jsc/bindings/ErrorCode.ts @@ -27,7 +27,6 @@ const errors: ErrorCodeMapping = [ ["ERR_BUFFER_CONTEXT_NOT_AVAILABLE", Error], ["ERR_BUFFER_OUT_OF_BOUNDS", RangeError], ["ERR_BUFFER_TOO_LARGE", RangeError], - ["ERR_CANNOT_WATCH_SIGINT", Error], ["ERR_CHILD_PROCESS_IPC_REQUIRED", Error], ["ERR_CHILD_PROCESS_STDIO_MAXBUFFER", RangeError], ["ERR_CLOSED_MESSAGE_PORT", Error], @@ -148,9 +147,6 @@ const errors: ErrorCodeMapping = [ ["ERR_INVALID_OBJECT_DEFINE_PROPERTY", TypeError], ["ERR_INVALID_PACKAGE_CONFIG", Error], ["ERR_INVALID_PROTOCOL", TypeError], - ["ERR_INSPECTOR_NOT_AVAILABLE", Error], - ["ERR_INVALID_REPL_EVAL_CONFIG", TypeError], - ["ERR_INVALID_REPL_INPUT", TypeError], ["ERR_INVALID_RETURN_VALUE", TypeError], ["ERR_INVALID_STATE", Error, undefined, TypeError, RangeError], ["ERR_INVALID_THIS", TypeError], @@ -343,5 +339,9 @@ const errors: ErrorCodeMapping = [ ["ERR_FS_CP_EEXIST", Error], ["ERR_FS_CP_SYMLINK_TO_SUBDIRECTORY", Error], ["ERR_DIR_CONCURRENT_OPERATION", Error], + ["ERR_CANNOT_WATCH_SIGINT", Error], + ["ERR_INSPECTOR_NOT_AVAILABLE", Error], + ["ERR_INVALID_REPL_EVAL_CONFIG", TypeError], + ["ERR_INVALID_REPL_INPUT", TypeError], ]; export default errors; diff --git a/src/runtime/cli/run_command.rs b/src/runtime/cli/run_command.rs index 47dce847bbc4..dbc1fa3a5016 100644 --- a/src/runtime/cli/run_command.rs +++ b/src/runtime/cli/run_command.rs @@ -2961,11 +2961,15 @@ impl RunCommand { if !user.is_empty() { // `node -i -e`: pass the user script as DATA (a JSON string // literal), never as spliced code — the bootstrap runs it via - // vm.runInThisContext after REPL.start(), matching Node's - // internal/main/repl.js order. Splicing code would let a - // user-side syntax error / unterminated `` ` `` swallow the - // bootstrap and would block-scope `-e` declarations away. - let json = bun_core::fmt::format_json_string_utf8(&user, Default::default()); + // vm.runInThisContext; a `-e` error is fatal (exit 1) as in + // Node's internal/main/repl.js. Splicing code would let an + // unterminated `` ` `` swallow the bootstrap and would + // block-scope `-e` declarations away. + // Argv is arbitrary bytes on Linux; the JSON encoder's SAFETY + // contract requires UTF-8, so lossily normalize first. + let user = String::from_utf8_lossy(&user); + let json = + bun_core::fmt::format_json_string_utf8(user.as_bytes(), Default::default()); write!(script, "const __BUN_EVAL_SCRIPT__ = {json};\n").unwrap_or_oom(); } // SAFETY: embedded builtin sources are UTF-8 by construction. diff --git a/test/js/bun/repl/repl.test.ts b/test/js/bun/repl/repl.test.ts index 4b1fc845f115..3dfa5df2dc75 100644 --- a/test/js/bun/repl/repl.test.ts +++ b/test/js/bun/repl/repl.test.ts @@ -1360,9 +1360,8 @@ describe("node:repl process-global side effects", () => { // Known limitation until process.addUncaughtExceptionCaptureCallback is // implemented natively: the shim occupies the exclusive capture slot for the // process lifetime. It must NOT displace a user callback installed BEFORE the - // first repl.start(), and unclaimed errors must still reach an - // 'uncaughtException' listener with the origin arg. - test("uncaught-exception capture shim defers to a pre-installed user callback and passes origin", async () => { + // first repl.start(). + test("uncaught-exception capture shim defers to a pre-installed user callback", async () => { const script = ` let userGot; process.setUncaughtExceptionCaptureCallback(e => { userGot = e.message; }); @@ -1371,8 +1370,6 @@ describe("node:repl process-global side effects", () => { const inp = new PassThrough(), out = new PassThrough(); out.resume(); const r = repl.start({ input: inp, output: out, terminal: false, prompt: "" }); r.close(); - let listenerOrigin; - process.on("uncaughtException", (e, origin) => { listenerOrigin = origin; }); setImmediate(() => { throw new Error("boom"); }); setImmediate(() => setImmediate(() => { console.log("userGot=" + userGot); From 0c9e6920648f273b05dbafea934c3e4cb7954887 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 8 Jul 2026 17:16:29 +0000 Subject: [PATCH 40/73] [autofix.ci] apply automated fixes --- docs/snippets/cli/run.mdx | 3 ++- src/runtime/cli/run_command.rs | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/snippets/cli/run.mdx b/docs/snippets/cli/run.mdx index d02a2a52412a..2d477da3aa6c 100644 --- a/docs/snippets/cli/run.mdx +++ b/docs/snippets/cli/run.mdx @@ -63,7 +63,8 @@ bun run - Open the Node.js-compatible REPL (node:repl). When combined with -e, evaluates the script first, then enters the REPL. Distinct from bun repl, which is Bun's native REPL. + Open the Node.js-compatible REPL (node:repl). When combined with -e, evaluates the script + first, then enters the REPL. Distinct from bun repl, which is Bun's native REPL. diff --git a/src/runtime/cli/run_command.rs b/src/runtime/cli/run_command.rs index dbc1fa3a5016..29d0bcb8cb81 100644 --- a/src/runtime/cli/run_command.rs +++ b/src/runtime/cli/run_command.rs @@ -2968,8 +2968,7 @@ impl RunCommand { // Argv is arbitrary bytes on Linux; the JSON encoder's SAFETY // contract requires UTF-8, so lossily normalize first. let user = String::from_utf8_lossy(&user); - let json = - bun_core::fmt::format_json_string_utf8(user.as_bytes(), Default::default()); + let json = bun_core::fmt::format_json_string_utf8(user.as_bytes(), Default::default()); write!(script, "const __BUN_EVAL_SCRIPT__ = {json};\n").unwrap_or_oom(); } // SAFETY: embedded builtin sources are UTF-8 by construction. From 61f3396a0691cba29daded02ebcbeee169c5b298 Mon Sep 17 00:00:00 2001 From: Alistair Smith Date: Thu, 9 Jul 2026 14:36:31 -0700 Subject: [PATCH 41/73] =?UTF-8?q?repl:=20address=20second-pass=20review=20?= =?UTF-8?q?=E2=80=94=20process.=5Feval,=20--interactive=20routing,=20primo?= =?UTF-8?q?rdials,=20arrow-header=20dedup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - process._eval: stop overwriting eval.script with the bootstrap; stash the user's -e in a dedicated interactive_script slot so process._eval reports it (undefined without -e). node-repl.ts reads process._eval directly. Also fixes the two clippy failures on the deleted JSON-splice path. - --interactive: route bun-as-node and `bun run --interactive` to the REPL; update the bare-node hint message. - node-shims: use captured primordials in decorateErrorStack / getOwnNonIndexProperties; filter slash-modules inside addBuiltinLibsToObject (repl.builtinModules keeps them). - node-primordials: SafePromiseRace wraps in a SafeArrayIterator so a tampered Array.prototype[Symbol.iterator] can't hang the breakEvalOnSigint await path. - readline: stop pulling internal/repl/node-shims (and thus node:{module,vm,path}) or internal/repl/history (fs,os,timers) at load. - NodeVM.cpp: extract nthSourceLineForArrowHeader / writeArrowHeaderStack so handleException and decorateParseErrorStack share one arrow-header impl; drop the incorrect columnOffset subtraction (JSTextPosition::column is physical) — now covered in vm.test.ts. - destructOnExit: quiesce deferredWorkTimer / Wasm worklist / VMTraps before the else-branch lastChanceToFinalize(). - vm.Script: FIXME the double-parse; document that --interactive -e is raw JS. --- .claude/skills/verify/SKILL.md | 41 +++++++ docs/snippets/cli/run.mdx | 4 +- src/js/eval/node-repl.ts | 9 +- src/js/internal/readline/interface.js | 22 +++- src/js/internal/readline/promises.js | 3 +- src/js/internal/repl/acorn.js | 4 +- src/js/internal/repl/node-primordials.js | 8 +- src/js/internal/repl/node-shims.js | 52 +++++---- src/js/node/readline.js | 10 +- src/js/node/readline.promises.js | 4 +- src/jsc/ModuleLoader.rs | 2 + src/jsc/bindings/NodeVM.cpp | 137 ++++++++++------------- src/jsc/bindings/NodeVM.h | 2 +- src/jsc/bindings/NodeVMScript.cpp | 7 +- src/jsc/bindings/ZigGlobalObject.cpp | 11 ++ src/options_types/context.rs | 4 + src/runtime/cli/mod.rs | 25 +++-- src/runtime/cli/run_command.rs | 38 +++---- src/runtime/node/node_process.rs | 9 ++ test/js/bun/repl/repl.test.ts | 101 +++++++++++++++++ test/js/node/vm/vm.test.ts | 18 +++ 21 files changed, 354 insertions(+), 157 deletions(-) create mode 100644 .claude/skills/verify/SKILL.md diff --git a/.claude/skills/verify/SKILL.md b/.claude/skills/verify/SKILL.md new file mode 100644 index 000000000000..6701e3525619 --- /dev/null +++ b/.claude/skills/verify/SKILL.md @@ -0,0 +1,41 @@ +--- +description: Verify a Bun code change end-to-end by driving the debug binary at its CLI surface — do not just re-run tests. +--- + +# Verify a Bun change + +## Build + +```sh +bun bd --version # builds debug → ./build/debug/bun-debug, prints version on success +``` + +`bun bd` is build-then-exec: `bun bd ` builds then runs `./build/debug/bun-debug `. Don't set a timeout. + +## Drive + +Set `BUN_DEBUG_QUIET_LOGS=1` when driving so scoped-logger noise doesn't drown observable output. For anything piped-stdin (REPL, prompts), also set `NO_COLOR=1`. + +Common surfaces per changed area: + +| Change area | Drive with | +|---|---| +| CLI flag / dispatch | `bun bd ` and observe stdout/stderr/exit | +| `bun --interactive` / node:repl | `printf '\n' \| BUN_DEBUG_QUIET_LOGS=1 NO_COLOR=1 NODE_REPL_HISTORY="" bun bd --interactive` | +| bun-as-node | `(exec -a node ./build/debug/bun-debug )` — argv0 emulation | +| `-e` / `-p` | `bun bd -e '