1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | 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;
|
4 | const os_1 = require("os");
|
5 | const path_1 = require("path");
|
6 | const repl_1 = require("repl");
|
7 | const vm_1 = require("vm");
|
8 | const index_1 = require("./index");
|
9 | const fs_1 = require("fs");
|
10 | const console_1 = require("console");
|
11 | const assert = require("assert");
|
12 | const module_1 = require("module");
|
13 |
|
14 | let _processTopLevelAwait;
|
15 | function getProcessTopLevelAwait() {
|
16 | if (_processTopLevelAwait === undefined) {
|
17 | ({
|
18 | processTopLevelAwait: _processTopLevelAwait,
|
19 | } = require('../dist-raw/node-internal-repl-await'));
|
20 | }
|
21 | return _processTopLevelAwait;
|
22 | }
|
23 | let diff;
|
24 | function getDiffLines() {
|
25 | if (diff === undefined) {
|
26 | diff = require('diff');
|
27 | }
|
28 | return diff.diffLines;
|
29 | }
|
30 |
|
31 | exports.EVAL_FILENAME = `[eval].ts`;
|
32 |
|
33 | exports.EVAL_NAME = `[eval]`;
|
34 |
|
35 | exports.STDIN_FILENAME = `[stdin].ts`;
|
36 |
|
37 | exports.STDIN_NAME = `[stdin]`;
|
38 |
|
39 | exports.REPL_FILENAME = '<repl>.ts';
|
40 |
|
41 | exports.REPL_NAME = '<repl>';
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 | function createRepl(options = {}) {
|
59 | var _a, _b, _c, _d, _e;
|
60 | const { ignoreDiagnosticsThatAreAnnoyingInInteractiveRepl = true } = options;
|
61 | let service = options.service;
|
62 | let nodeReplServer;
|
63 |
|
64 |
|
65 | let context;
|
66 | const state = (_a = options.state) !== null && _a !== void 0 ? _a : new EvalState((0, path_1.join)(process.cwd(), exports.REPL_FILENAME));
|
67 | const evalAwarePartialHost = createEvalAwarePartialHost(state, options.composeWithEvalAwarePartialHost);
|
68 | const stdin = (_b = options.stdin) !== null && _b !== void 0 ? _b : process.stdin;
|
69 | const stdout = (_c = options.stdout) !== null && _c !== void 0 ? _c : process.stdout;
|
70 | const stderr = (_d = options.stderr) !== null && _d !== void 0 ? _d : process.stderr;
|
71 | const _console = stdout === process.stdout && stderr === process.stderr
|
72 | ? console
|
73 | : new console_1.Console(stdout, stderr);
|
74 | const replService = {
|
75 | state: (_e = options.state) !== null && _e !== void 0 ? _e : new EvalState((0, path_1.join)(process.cwd(), exports.EVAL_FILENAME)),
|
76 | setService,
|
77 | evalCode,
|
78 | evalCodeInternal,
|
79 | nodeEval,
|
80 | evalAwarePartialHost,
|
81 | start,
|
82 | startInternal,
|
83 | stdin,
|
84 | stdout,
|
85 | stderr,
|
86 | console: _console,
|
87 | };
|
88 | return replService;
|
89 | function setService(_service) {
|
90 | service = _service;
|
91 | if (ignoreDiagnosticsThatAreAnnoyingInInteractiveRepl) {
|
92 | service.addDiagnosticFilter({
|
93 | appliesToAllFiles: false,
|
94 | filenamesAbsolute: [state.path],
|
95 | diagnosticsIgnored: [
|
96 | 2393,
|
97 | 6133,
|
98 | 7027,
|
99 | ...(service.shouldReplAwait ? topLevelAwaitDiagnosticCodes : []),
|
100 | ],
|
101 | });
|
102 | }
|
103 | }
|
104 | function evalCode(code) {
|
105 | const result = appendCompileAndEvalInput({
|
106 | service: service,
|
107 | state,
|
108 | input: code,
|
109 | context,
|
110 | overrideIsCompletion: false,
|
111 | });
|
112 | assert(result.containsTopLevelAwait === false);
|
113 | return result.value;
|
114 | }
|
115 | function evalCodeInternal(options) {
|
116 | const { code, enableTopLevelAwait, context } = options;
|
117 | return appendCompileAndEvalInput({
|
118 | service: service,
|
119 | state,
|
120 | input: code,
|
121 | enableTopLevelAwait,
|
122 | context,
|
123 | });
|
124 | }
|
125 | function nodeEval(code, context, _filename, callback) {
|
126 |
|
127 | if (code === '.scope') {
|
128 | callback(null);
|
129 | return;
|
130 | }
|
131 | try {
|
132 | const evalResult = evalCodeInternal({
|
133 | code,
|
134 | enableTopLevelAwait: true,
|
135 | context,
|
136 | });
|
137 | if (evalResult.containsTopLevelAwait) {
|
138 | (async () => {
|
139 | try {
|
140 | callback(null, await evalResult.valuePromise);
|
141 | }
|
142 | catch (promiseError) {
|
143 | handleError(promiseError);
|
144 | }
|
145 | })();
|
146 | }
|
147 | else {
|
148 | callback(null, evalResult.value);
|
149 | }
|
150 | }
|
151 | catch (error) {
|
152 | handleError(error);
|
153 | }
|
154 |
|
155 |
|
156 |
|
157 | function handleError(error) {
|
158 | var _a, _b;
|
159 |
|
160 | const canLogTopLevelAwaitHint = service.options.experimentalReplAwait !== false &&
|
161 | !service.shouldReplAwait;
|
162 | if (error instanceof index_1.TSError) {
|
163 |
|
164 | if (repl_1.Recoverable && isRecoverable(error)) {
|
165 | callback(new repl_1.Recoverable(error));
|
166 | return;
|
167 | }
|
168 | else {
|
169 | _console.error(error);
|
170 | if (canLogTopLevelAwaitHint &&
|
171 | error.diagnosticCodes.some((dC) => topLevelAwaitDiagnosticCodes.includes(dC))) {
|
172 | _console.error(getTopLevelAwaitHint());
|
173 | }
|
174 | callback(null);
|
175 | }
|
176 | }
|
177 | else {
|
178 | let _error = error;
|
179 | if (canLogTopLevelAwaitHint &&
|
180 | _error instanceof SyntaxError &&
|
181 | ((_a = _error.message) === null || _a === void 0 ? void 0 : _a.includes('await is only valid'))) {
|
182 | try {
|
183 |
|
184 | _error.message += `\n\n${getTopLevelAwaitHint()}`;
|
185 | _error.stack = (_b = _error.stack) === null || _b === void 0 ? void 0 : _b.replace(/(SyntaxError:.*)/, (_, $1) => `${$1}\n\n${getTopLevelAwaitHint()}`);
|
186 | }
|
187 | catch { }
|
188 | }
|
189 | callback(_error);
|
190 | }
|
191 | }
|
192 | function getTopLevelAwaitHint() {
|
193 | 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]}.`;
|
194 | }
|
195 | }
|
196 |
|
197 | function start(code) {
|
198 | startInternal({ code });
|
199 | }
|
200 |
|
201 | function startInternal(options) {
|
202 | const { code, forceToBeModule = true, ...optionsOverride } = options !== null && options !== void 0 ? options : {};
|
203 |
|
204 |
|
205 |
|
206 | if (code) {
|
207 | try {
|
208 | evalCode(`${code}\n`);
|
209 | }
|
210 | catch (err) {
|
211 | _console.error(err);
|
212 |
|
213 | process.exit(1);
|
214 | }
|
215 | }
|
216 |
|
217 |
|
218 |
|
219 | service === null || service === void 0 ? void 0 : service.compile('', state.path);
|
220 | const repl = (0, repl_1.start)({
|
221 | prompt: '> ',
|
222 | input: replService.stdin,
|
223 | output: replService.stdout,
|
224 |
|
225 | terminal: stdout.isTTY &&
|
226 | !parseInt(index_1.env.NODE_NO_READLINE, 10),
|
227 | eval: nodeEval,
|
228 | useGlobal: true,
|
229 | ...optionsOverride,
|
230 | });
|
231 | nodeReplServer = repl;
|
232 | context = repl.context;
|
233 |
|
234 | const resetEval = appendToEvalState(state, '');
|
235 | function reset() {
|
236 | resetEval();
|
237 |
|
238 | runInContext('exports = module.exports', state.path, context);
|
239 | if (forceToBeModule) {
|
240 | state.input += 'export {};void 0;\n';
|
241 | }
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 | if (!(service === null || service === void 0 ? void 0 : service.transpileOnly)) {
|
251 | state.input += `// @ts-ignore\n${module_1.builtinModules
|
252 | .filter((name) => !name.startsWith('_') &&
|
253 | !name.includes('/') &&
|
254 | !['console', 'module', 'process'].includes(name))
|
255 | .map((name) => `declare import ${name} = require('${name}')`)
|
256 | .join(';')}\n`;
|
257 | }
|
258 | }
|
259 | reset();
|
260 | repl.on('reset', reset);
|
261 | repl.defineCommand('type', {
|
262 | help: 'Check the type of a TypeScript identifier',
|
263 | action: function (identifier) {
|
264 | if (!identifier) {
|
265 | repl.displayPrompt();
|
266 | return;
|
267 | }
|
268 | const undo = appendToEvalState(state, identifier);
|
269 | const { name, comment } = service.getTypeInfo(state.input, state.path, state.input.length);
|
270 | undo();
|
271 | if (name)
|
272 | repl.outputStream.write(`${name}\n`);
|
273 | if (comment)
|
274 | repl.outputStream.write(`${comment}\n`);
|
275 | repl.displayPrompt();
|
276 | },
|
277 | });
|
278 |
|
279 | if (repl.setupHistory) {
|
280 | const historyPath = index_1.env.TS_NODE_HISTORY || (0, path_1.join)((0, os_1.homedir)(), '.ts_node_repl_history');
|
281 | repl.setupHistory(historyPath, (err) => {
|
282 | if (!err)
|
283 | return;
|
284 | _console.error(err);
|
285 | process.exit(1);
|
286 | });
|
287 | }
|
288 | return repl;
|
289 | }
|
290 | }
|
291 | exports.createRepl = createRepl;
|
292 |
|
293 |
|
294 |
|
295 | class EvalState {
|
296 | constructor(path) {
|
297 | this.path = path;
|
298 |
|
299 | this.input = '';
|
300 |
|
301 | this.output = '';
|
302 |
|
303 | this.version = 0;
|
304 |
|
305 | this.lines = 0;
|
306 | }
|
307 | }
|
308 | exports.EvalState = EvalState;
|
309 | function createEvalAwarePartialHost(state, composeWith) {
|
310 | function readFile(path) {
|
311 | if (path === state.path)
|
312 | return state.input;
|
313 | if (composeWith === null || composeWith === void 0 ? void 0 : composeWith.readFile)
|
314 | return composeWith.readFile(path);
|
315 | try {
|
316 | return (0, fs_1.readFileSync)(path, 'utf8');
|
317 | }
|
318 | catch (err) {
|
319 |
|
320 | }
|
321 | }
|
322 | function fileExists(path) {
|
323 | if (path === state.path)
|
324 | return true;
|
325 | if (composeWith === null || composeWith === void 0 ? void 0 : composeWith.fileExists)
|
326 | return composeWith.fileExists(path);
|
327 | try {
|
328 | const stats = (0, fs_1.statSync)(path);
|
329 | return stats.isFile() || stats.isFIFO();
|
330 | }
|
331 | catch (err) {
|
332 | return false;
|
333 | }
|
334 | }
|
335 | return { readFile, fileExists };
|
336 | }
|
337 | exports.createEvalAwarePartialHost = createEvalAwarePartialHost;
|
338 | const sourcemapCommentRe = /\/\/# ?sourceMappingURL=\S+[\s\r\n]*$/;
|
339 |
|
340 |
|
341 |
|
342 |
|
343 |
|
344 |
|
345 | function appendCompileAndEvalInput(options) {
|
346 | const { service, state, wrappedErr, enableTopLevelAwait = false, context, overrideIsCompletion, } = options;
|
347 | let { input } = options;
|
348 |
|
349 |
|
350 |
|
351 |
|
352 | let wrappedCmd = false;
|
353 | if (!wrappedErr && /^\s*{/.test(input) && !/;\s*$/.test(input)) {
|
354 | input = `(${input.trim()})\n`;
|
355 | wrappedCmd = true;
|
356 | }
|
357 | const lines = state.lines;
|
358 | const isCompletion = overrideIsCompletion !== null && overrideIsCompletion !== void 0 ? overrideIsCompletion : !/\n$/.test(input);
|
359 | const undo = appendToEvalState(state, input);
|
360 | let output;
|
361 |
|
362 | function adjustUseStrict(code) {
|
363 |
|
364 |
|
365 | return code.replace(/^"use strict";/, '"use strict"; void 0;');
|
366 | }
|
367 | try {
|
368 | output = service.compile(state.input, state.path, -lines);
|
369 | }
|
370 | catch (err) {
|
371 | undo();
|
372 | if (wrappedCmd) {
|
373 | if (err instanceof index_1.TSError && err.diagnosticCodes[0] === 2339) {
|
374 |
|
375 | throw err;
|
376 | }
|
377 |
|
378 | return appendCompileAndEvalInput({
|
379 | ...options,
|
380 | wrappedErr: err,
|
381 | });
|
382 | }
|
383 | if (wrappedErr)
|
384 | throw wrappedErr;
|
385 | throw err;
|
386 | }
|
387 | output = adjustUseStrict(output);
|
388 |
|
389 |
|
390 |
|
391 |
|
392 |
|
393 |
|
394 | const outputWithoutSourcemapComment = output.replace(sourcemapCommentRe, '');
|
395 | const oldOutputWithoutSourcemapComment = state.output.replace(sourcemapCommentRe, '');
|
396 |
|
397 | const changes = getDiffLines()(oldOutputWithoutSourcemapComment, outputWithoutSourcemapComment);
|
398 | if (isCompletion) {
|
399 | undo();
|
400 | }
|
401 | else {
|
402 | state.output = output;
|
403 |
|
404 |
|
405 |
|
406 |
|
407 | state.input = state.input.replace(/([^\n\s])([\n\s]*)$/, (all, lastChar, whitespace) => {
|
408 | if (lastChar !== ';')
|
409 | return `${lastChar};${whitespace}`;
|
410 | return all;
|
411 | });
|
412 | }
|
413 | let commands = [];
|
414 | let containsTopLevelAwait = false;
|
415 |
|
416 | for (const change of changes) {
|
417 | if (change.added) {
|
418 | if (enableTopLevelAwait &&
|
419 | service.shouldReplAwait &&
|
420 | change.value.indexOf('await') > -1) {
|
421 | const processTopLevelAwait = getProcessTopLevelAwait();
|
422 |
|
423 | const wrappedResult = processTopLevelAwait(change.value + '\n');
|
424 | if (wrappedResult !== null) {
|
425 | containsTopLevelAwait = true;
|
426 | commands.push({
|
427 | mustAwait: true,
|
428 | execCommand: () => runInContext(wrappedResult, state.path, context),
|
429 | });
|
430 | continue;
|
431 | }
|
432 | }
|
433 | commands.push({
|
434 | execCommand: () => runInContext(change.value, state.path, context),
|
435 | });
|
436 | }
|
437 | }
|
438 |
|
439 |
|
440 | if (containsTopLevelAwait) {
|
441 | return {
|
442 | containsTopLevelAwait,
|
443 | valuePromise: (async () => {
|
444 | let value;
|
445 | for (const command of commands) {
|
446 | const r = command.execCommand();
|
447 | value = command.mustAwait ? await r : r;
|
448 | }
|
449 | return value;
|
450 | })(),
|
451 | };
|
452 | }
|
453 | else {
|
454 | return {
|
455 | containsTopLevelAwait: false,
|
456 | value: commands.reduce((_, c) => c.execCommand(), undefined),
|
457 | };
|
458 | }
|
459 | }
|
460 |
|
461 |
|
462 |
|
463 | function runInContext(code, filename, context) {
|
464 | const script = new vm_1.Script(code, { filename });
|
465 | if (context === undefined || context === global) {
|
466 | return script.runInThisContext();
|
467 | }
|
468 | else {
|
469 | return script.runInContext(context);
|
470 | }
|
471 | }
|
472 |
|
473 |
|
474 |
|
475 | function appendToEvalState(state, input) {
|
476 | const undoInput = state.input;
|
477 | const undoVersion = state.version;
|
478 | const undoOutput = state.output;
|
479 | const undoLines = state.lines;
|
480 | state.input += input;
|
481 | state.lines += lineCount(input);
|
482 | state.version++;
|
483 | return function () {
|
484 | state.input = undoInput;
|
485 | state.output = undoOutput;
|
486 | state.version = undoVersion;
|
487 | state.lines = undoLines;
|
488 | };
|
489 | }
|
490 |
|
491 |
|
492 |
|
493 | function lineCount(value) {
|
494 | let count = 0;
|
495 | for (const char of value) {
|
496 | if (char === '\n') {
|
497 | count++;
|
498 | }
|
499 | }
|
500 | return count;
|
501 | }
|
502 |
|
503 |
|
504 |
|
505 |
|
506 |
|
507 |
|
508 | const RECOVERY_CODES = new Map([
|
509 | [1003, null],
|
510 | [1005, null],
|
511 | [1109, null],
|
512 | [1126, null],
|
513 | [
|
514 | 1136,
|
515 | new Set([1005]),
|
516 | ],
|
517 | [1160, null],
|
518 | [1161, null],
|
519 | [2355, null],
|
520 | [2391, null],
|
521 | [
|
522 | 7010,
|
523 | new Set([1005]),
|
524 | ],
|
525 | ]);
|
526 |
|
527 |
|
528 |
|
529 |
|
530 |
|
531 | const topLevelAwaitDiagnosticCodes = [
|
532 | 1375,
|
533 | 1378,
|
534 | 1431,
|
535 | 1432,
|
536 | ];
|
537 |
|
538 |
|
539 |
|
540 | function isRecoverable(error) {
|
541 | return error.diagnosticCodes.every((code) => {
|
542 | const deps = RECOVERY_CODES.get(code);
|
543 | return (deps === null ||
|
544 | (deps && error.diagnosticCodes.some((code) => deps.has(code))));
|
545 | });
|
546 | }
|
547 |
|
548 |
|
549 |
|
550 |
|
551 | function setupContext(context, module, filenameAndDirname) {
|
552 | if (filenameAndDirname) {
|
553 | context.__dirname = '.';
|
554 | context.__filename = `[${filenameAndDirname}]`;
|
555 | }
|
556 | context.module = module;
|
557 | context.exports = module.exports;
|
558 | context.require = module.require.bind(module);
|
559 | }
|
560 | exports.setupContext = setupContext;
|
561 |
|
\ | No newline at end of file |