Icard/angular-clarity-master(work.../node_modules/ts-node/dist/repl.js

561 lines
21 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupContext = exports.createEvalAwarePartialHost = exports.EvalState = exports.createRepl = exports.REPL_NAME = exports.REPL_FILENAME = exports.STDIN_NAME = exports.STDIN_FILENAME = exports.EVAL_NAME = exports.EVAL_FILENAME = void 0;
const os_1 = require("os");
const path_1 = require("path");
const repl_1 = require("repl");
const vm_1 = require("vm");
const index_1 = require("./index");
const fs_1 = require("fs");
const console_1 = require("console");
const assert = require("assert");
const module_1 = require("module");
// Lazy-loaded.
let _processTopLevelAwait;
function getProcessTopLevelAwait() {
if (_processTopLevelAwait === undefined) {
({
processTopLevelAwait: _processTopLevelAwait,
} = require('../dist-raw/node-internal-repl-await'));
}
return _processTopLevelAwait;
}
let diff;
function getDiffLines() {
if (diff === undefined) {
diff = require('diff');
}
return diff.diffLines;
}
/** @internal */
exports.EVAL_FILENAME = `[eval].ts`;
/** @internal */
exports.EVAL_NAME = `[eval]`;
/** @internal */
exports.STDIN_FILENAME = `[stdin].ts`;
/** @internal */
exports.STDIN_NAME = `[stdin]`;
/** @internal */
exports.REPL_FILENAME = '<repl>.ts';
/** @internal */
exports.REPL_NAME = '<repl>';
/**
* Create a ts-node REPL instance.
*
* Pay close attention to the example below. Today, the API requires a few lines
* of boilerplate to correctly bind the `ReplService` to the ts-node `Service` and
* vice-versa.
*
* Usage example:
*
* const repl = tsNode.createRepl();
* const service = tsNode.create({...repl.evalAwarePartialHost});
* repl.setService(service);
* repl.start();
*
* @category REPL
*/
function createRepl(options = {}) {
var _a, _b, _c, _d, _e;
const { ignoreDiagnosticsThatAreAnnoyingInInteractiveRepl = true } = options;
let service = options.service;
let nodeReplServer;
// If `useGlobal` is not true, then REPL creates a context when started.
// This stores a reference to it or to `global`, whichever is used, after REPL has started.
let context;
const state = (_a = options.state) !== null && _a !== void 0 ? _a : new EvalState((0, path_1.join)(process.cwd(), exports.REPL_FILENAME));
const evalAwarePartialHost = createEvalAwarePartialHost(state, options.composeWithEvalAwarePartialHost);
const stdin = (_b = options.stdin) !== null && _b !== void 0 ? _b : process.stdin;
const stdout = (_c = options.stdout) !== null && _c !== void 0 ? _c : process.stdout;
const stderr = (_d = options.stderr) !== null && _d !== void 0 ? _d : process.stderr;
const _console = stdout === process.stdout && stderr === process.stderr
? console
: new console_1.Console(stdout, stderr);
const replService = {
state: (_e = options.state) !== null && _e !== void 0 ? _e : new EvalState((0, path_1.join)(process.cwd(), exports.EVAL_FILENAME)),
setService,
evalCode,
evalCodeInternal,
nodeEval,
evalAwarePartialHost,
start,
startInternal,
stdin,
stdout,
stderr,
console: _console,
};
return replService;
function setService(_service) {
service = _service;
if (ignoreDiagnosticsThatAreAnnoyingInInteractiveRepl) {
service.addDiagnosticFilter({
appliesToAllFiles: false,
filenamesAbsolute: [state.path],
diagnosticsIgnored: [
2393,
6133,
7027,
...(service.shouldReplAwait ? topLevelAwaitDiagnosticCodes : []),
],
});
}
}
function evalCode(code) {
const result = appendCompileAndEvalInput({
service: service,
state,
input: code,
context,
overrideIsCompletion: false,
});
assert(result.containsTopLevelAwait === false);
return result.value;
}
function evalCodeInternal(options) {
const { code, enableTopLevelAwait, context } = options;
return appendCompileAndEvalInput({
service: service,
state,
input: code,
enableTopLevelAwait,
context,
});
}
function nodeEval(code, context, _filename, callback) {
// TODO: Figure out how to handle completion here.
if (code === '.scope') {
callback(null);
return;
}
try {
const evalResult = evalCodeInternal({
code,
enableTopLevelAwait: true,
context,
});
if (evalResult.containsTopLevelAwait) {
(async () => {
try {
callback(null, await evalResult.valuePromise);
}
catch (promiseError) {
handleError(promiseError);
}
})();
}
else {
callback(null, evalResult.value);
}
}
catch (error) {
handleError(error);
}
// Log TSErrors, check if they're recoverable, log helpful hints for certain
// well-known errors, and invoke `callback()`
// TODO should evalCode API get the same error-handling benefits?
function handleError(error) {
var _a, _b;
// Don't show TLA hint if the user explicitly disabled repl top level await
const canLogTopLevelAwaitHint = service.options.experimentalReplAwait !== false &&
!service.shouldReplAwait;
if (error instanceof index_1.TSError) {
// Support recoverable compilations using >= node 6.
if (repl_1.Recoverable && isRecoverable(error)) {
callback(new repl_1.Recoverable(error));
return;
}
else {
_console.error(error);
if (canLogTopLevelAwaitHint &&
error.diagnosticCodes.some((dC) => topLevelAwaitDiagnosticCodes.includes(dC))) {
_console.error(getTopLevelAwaitHint());
}
callback(null);
}
}
else {
let _error = error;
if (canLogTopLevelAwaitHint &&
_error instanceof SyntaxError &&
((_a = _error.message) === null || _a === void 0 ? void 0 : _a.includes('await is only valid'))) {
try {
// Only way I know to make our hint appear after the error
_error.message += `\n\n${getTopLevelAwaitHint()}`;
_error.stack = (_b = _error.stack) === null || _b === void 0 ? void 0 : _b.replace(/(SyntaxError:.*)/, (_, $1) => `${$1}\n\n${getTopLevelAwaitHint()}`);
}
catch { }
}
callback(_error);
}
}
function getTopLevelAwaitHint() {
return `Hint: REPL top-level await requires TypeScript version 3.8 or higher and target ES2018 or higher. You are using TypeScript ${service.ts.version} and target ${service.ts.ScriptTarget[service.config.options.target]}.`;
}
}
// Note: `code` argument is deprecated
function start(code) {
startInternal({ code });
}
// Note: `code` argument is deprecated
function startInternal(options) {
const { code, forceToBeModule = true, ...optionsOverride } = options !== null && options !== void 0 ? options : {};
// TODO assert that `service` is set; remove all `service!` non-null assertions
// Eval incoming code before the REPL starts.
// Note: deprecated
if (code) {
try {
evalCode(`${code}\n`);
}
catch (err) {
_console.error(err);
// Note: should not be killing the process here, but this codepath is deprecated anyway
process.exit(1);
}
}
// In case the typescript compiler hasn't compiled anything yet,
// make it run though compilation at least one time before
// the REPL starts for a snappier user experience on startup.
service === null || service === void 0 ? void 0 : service.compile('', state.path);
const repl = (0, repl_1.start)({
prompt: '> ',
input: replService.stdin,
output: replService.stdout,
// Mimicking node's REPL implementation: https://github.com/nodejs/node/blob/168b22ba073ee1cbf8d0bcb4ded7ff3099335d04/lib/internal/repl.js#L28-L30
terminal: stdout.isTTY &&
!parseInt(index_1.env.NODE_NO_READLINE, 10),
eval: nodeEval,
useGlobal: true,
...optionsOverride,
});
nodeReplServer = repl;
context = repl.context;
// Bookmark the point where we should reset the REPL state.
const resetEval = appendToEvalState(state, '');
function reset() {
resetEval();
// Hard fix for TypeScript forcing `Object.defineProperty(exports, ...)`.
runInContext('exports = module.exports', state.path, context);
if (forceToBeModule) {
state.input += 'export {};void 0;\n';
}
// Declare node builtins.
// Skip the same builtins as `addBuiltinLibsToObject`:
// those starting with _
// those containing /
// those that already exist as globals
// Intentionally suppress type errors in case @types/node does not declare any of them, and because
// `declare import` is technically invalid syntax.
// Avoid this when in transpileOnly, because third-party transpilers may not handle `declare import`.
if (!(service === null || service === void 0 ? void 0 : service.transpileOnly)) {
state.input += `// @ts-ignore\n${module_1.builtinModules
.filter((name) => !name.startsWith('_') &&
!name.includes('/') &&
!['console', 'module', 'process'].includes(name))
.map((name) => `declare import ${name} = require('${name}')`)
.join(';')}\n`;
}
}
reset();
repl.on('reset', reset);
repl.defineCommand('type', {
help: 'Check the type of a TypeScript identifier',
action: function (identifier) {
if (!identifier) {
repl.displayPrompt();
return;
}
const undo = appendToEvalState(state, identifier);
const { name, comment } = service.getTypeInfo(state.input, state.path, state.input.length);
undo();
if (name)
repl.outputStream.write(`${name}\n`);
if (comment)
repl.outputStream.write(`${comment}\n`);
repl.displayPrompt();
},
});
// Set up REPL history when available natively via node.js >= 11.
if (repl.setupHistory) {
const historyPath = index_1.env.TS_NODE_HISTORY || (0, path_1.join)((0, os_1.homedir)(), '.ts_node_repl_history');
repl.setupHistory(historyPath, (err) => {
if (!err)
return;
_console.error(err);
process.exit(1);
});
}
return repl;
}
}
exports.createRepl = createRepl;
/**
* Eval state management. Stores virtual `[eval].ts` file
*/
class EvalState {
constructor(path) {
this.path = path;
/** @internal */
this.input = '';
/** @internal */
this.output = '';
/** @internal */
this.version = 0;
/** @internal */
this.lines = 0;
}
}
exports.EvalState = EvalState;
function createEvalAwarePartialHost(state, composeWith) {
function readFile(path) {
if (path === state.path)
return state.input;
if (composeWith === null || composeWith === void 0 ? void 0 : composeWith.readFile)
return composeWith.readFile(path);
try {
return (0, fs_1.readFileSync)(path, 'utf8');
}
catch (err) {
/* Ignore. */
}
}
function fileExists(path) {
if (path === state.path)
return true;
if (composeWith === null || composeWith === void 0 ? void 0 : composeWith.fileExists)
return composeWith.fileExists(path);
try {
const stats = (0, fs_1.statSync)(path);
return stats.isFile() || stats.isFIFO();
}
catch (err) {
return false;
}
}
return { readFile, fileExists };
}
exports.createEvalAwarePartialHost = createEvalAwarePartialHost;
const sourcemapCommentRe = /\/\/# ?sourceMappingURL=\S+[\s\r\n]*$/;
/**
* Evaluate the code snippet.
*
* Append it to virtual .ts file, compile, handle compiler errors, compute a diff of the JS, and eval any code that
* appears as "added" in the diff.
*/
function appendCompileAndEvalInput(options) {
const { service, state, wrappedErr, enableTopLevelAwait = false, context, overrideIsCompletion, } = options;
let { input } = options;
// It's confusing for `{ a: 1 }` to be interpreted as a block statement
// rather than an object literal. So, we first try to wrap it in
// parentheses, so that it will be interpreted as an expression.
// Based on https://github.com/nodejs/node/blob/c2e6822153bad023ab7ebd30a6117dcc049e475c/lib/repl.js#L413-L422
let wrappedCmd = false;
if (!wrappedErr && /^\s*{/.test(input) && !/;\s*$/.test(input)) {
input = `(${input.trim()})\n`;
wrappedCmd = true;
}
const lines = state.lines;
const isCompletion = overrideIsCompletion !== null && overrideIsCompletion !== void 0 ? overrideIsCompletion : !/\n$/.test(input);
const undo = appendToEvalState(state, input);
let output;
// Based on https://github.com/nodejs/node/blob/92573721c7cff104ccb82b6ed3e8aa69c4b27510/lib/repl.js#L457-L461
function adjustUseStrict(code) {
// "void 0" keeps the repl from returning "use strict" as the result
// value for statements and declarations that don't return a value.
return code.replace(/^"use strict";/, '"use strict"; void 0;');
}
try {
output = service.compile(state.input, state.path, -lines);
}
catch (err) {
undo();
if (wrappedCmd) {
if (err instanceof index_1.TSError && err.diagnosticCodes[0] === 2339) {
// Ensure consistent and more sane behavior between { a: 1 }['b'] and ({ a: 1 }['b'])
throw err;
}
// Unwrap and try again
return appendCompileAndEvalInput({
...options,
wrappedErr: err,
});
}
if (wrappedErr)
throw wrappedErr;
throw err;
}
output = adjustUseStrict(output);
// Note: REPL does not respect sourcemaps!
// To properly do that, we'd need to prefix the code we eval -- which comes
// from `diffLines` -- with newlines so that it's at the proper line numbers.
// Then we'd need to ensure each bit of eval-ed code, if there are multiples,
// has the sourcemap appended to it.
// We might also need to integrate with our sourcemap hooks' cache; I'm not sure.
const outputWithoutSourcemapComment = output.replace(sourcemapCommentRe, '');
const oldOutputWithoutSourcemapComment = state.output.replace(sourcemapCommentRe, '');
// Use `diff` to check for new JavaScript to execute.
const changes = getDiffLines()(oldOutputWithoutSourcemapComment, outputWithoutSourcemapComment);
if (isCompletion) {
undo();
}
else {
state.output = output;
// Insert a semicolon to make sure that the code doesn't interact with the next line,
// for example to prevent `2\n+ 2` from producing 4.
// This is safe since the output will not change since we can only get here with successful inputs,
// and adding a semicolon to the end of a successful input won't ever change the output.
state.input = state.input.replace(/([^\n\s])([\n\s]*)$/, (all, lastChar, whitespace) => {
if (lastChar !== ';')
return `${lastChar};${whitespace}`;
return all;
});
}
let commands = [];
let containsTopLevelAwait = false;
// Build a list of "commands": bits of JS code in the diff that must be executed.
for (const change of changes) {
if (change.added) {
if (enableTopLevelAwait &&
service.shouldReplAwait &&
change.value.indexOf('await') > -1) {
const processTopLevelAwait = getProcessTopLevelAwait();
// Newline prevents comments to mess with wrapper
const wrappedResult = processTopLevelAwait(change.value + '\n');
if (wrappedResult !== null) {
containsTopLevelAwait = true;
commands.push({
mustAwait: true,
execCommand: () => runInContext(wrappedResult, state.path, context),
});
continue;
}
}
commands.push({
execCommand: () => runInContext(change.value, state.path, context),
});
}
}
// Execute all commands asynchronously if necessary, returning the result or a
// promise of the result.
if (containsTopLevelAwait) {
return {
containsTopLevelAwait,
valuePromise: (async () => {
let value;
for (const command of commands) {
const r = command.execCommand();
value = command.mustAwait ? await r : r;
}
return value;
})(),
};
}
else {
return {
containsTopLevelAwait: false,
value: commands.reduce((_, c) => c.execCommand(), undefined),
};
}
}
/**
* Low-level execution of JS code in context
*/
function runInContext(code, filename, context) {
const script = new vm_1.Script(code, { filename });
if (context === undefined || context === global) {
return script.runInThisContext();
}
else {
return script.runInContext(context);
}
}
/**
* Append to the eval instance and return an undo function.
*/
function appendToEvalState(state, input) {
const undoInput = state.input;
const undoVersion = state.version;
const undoOutput = state.output;
const undoLines = state.lines;
state.input += input;
state.lines += lineCount(input);
state.version++;
return function () {
state.input = undoInput;
state.output = undoOutput;
state.version = undoVersion;
state.lines = undoLines;
};
}
/**
* Count the number of lines.
*/
function lineCount(value) {
let count = 0;
for (const char of value) {
if (char === '\n') {
count++;
}
}
return count;
}
/**
* TS diagnostic codes which are recoverable, meaning that the user likely entered an incomplete line of code
* and should be prompted for the next. For example, starting a multi-line for() loop and not finishing it.
* null value means code is always recoverable. `Set` means code is only recoverable when occurring alongside at least one
* of the other codes.
*/
const RECOVERY_CODES = new Map([
[1003, null],
[1005, null],
[1109, null],
[1126, null],
[
1136,
new Set([1005]), // happens when typing out an object literal or block scope across multiple lines: '{ foo: 123,'
],
[1160, null],
[1161, null],
[2355, null],
[2391, null],
[
7010,
new Set([1005]), // happens when fn signature spread across multiple lines: 'function a(\nb: any\n) {'
],
]);
/**
* Diagnostic codes raised when using top-level await.
* These are suppressed when top-level await is enabled.
* When it is *not* enabled, these trigger a helpful hint about enabling top-level await.
*/
const topLevelAwaitDiagnosticCodes = [
1375,
1378,
1431,
1432, // Top-level 'for await' loops are only allowed when the 'module' option is set to 'esnext' or 'system', and the 'target' option is set to 'es2017' or higher.
];
/**
* Check if a function can recover gracefully.
*/
function isRecoverable(error) {
return error.diagnosticCodes.every((code) => {
const deps = RECOVERY_CODES.get(code);
return (deps === null ||
(deps && error.diagnosticCodes.some((code) => deps.has(code))));
});
}
/**
* @internal
* Set properties on `context` before eval-ing [stdin] or [eval] input.
*/
function setupContext(context, module, filenameAndDirname) {
if (filenameAndDirname) {
context.__dirname = '.';
context.__filename = `[${filenameAndDirname}]`;
}
context.module = module;
context.exports = module.exports;
context.require = module.require.bind(module);
}
exports.setupContext = setupContext;
//# sourceMappingURL=repl.js.map