mirror of
https://github.com/denoland/deno.git
synced 2024-10-31 09:14:20 -04:00
a21a5ad2fa
Resolves #1705 This PR adds the Deno APIs as a global namespace named `Deno`. For backwards compatibility, the ability to `import * from "deno"` is preserved. I have tried to convert every test and internal code the references the module to use the namespace instead, but because I didn't break compatibility I am not sure. On the REPL, `deno` no longer exists, replaced only with `Deno` to align with the regular runtime. The runtime type library includes both the namespace and module. This means it duplicates the whole type information. When we remove the functionality from the runtime, it will be a one line change to the library generator to remove the module definition from the type library. I marked a `TODO` in a couple places where to remove the `"deno"` module, but there are additional places I know I didn't mark.
174 lines
5.1 KiB
TypeScript
174 lines
5.1 KiB
TypeScript
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
|
import * as msg from "gen/msg_generated";
|
|
import * as flatbuffers from "./flatbuffers";
|
|
import { assert } from "./util";
|
|
import { close } from "./files";
|
|
import * as dispatch from "./dispatch";
|
|
import { exit } from "./os";
|
|
import { globalEval } from "./global_eval";
|
|
import { libdeno } from "./libdeno";
|
|
import { formatError } from "./format_error";
|
|
|
|
const window = globalEval("this");
|
|
|
|
const helpMsg = [
|
|
"exit Exit the REPL",
|
|
"help Print this help message"
|
|
].join("\n");
|
|
|
|
const replCommands = {
|
|
exit: {
|
|
get() {
|
|
exit(0);
|
|
}
|
|
},
|
|
help: {
|
|
get() {
|
|
return helpMsg;
|
|
}
|
|
}
|
|
};
|
|
|
|
function startRepl(historyFile: string): number {
|
|
const builder = flatbuffers.createBuilder();
|
|
const historyFile_ = builder.createString(historyFile);
|
|
|
|
msg.ReplStart.startReplStart(builder);
|
|
msg.ReplStart.addHistoryFile(builder, historyFile_);
|
|
const inner = msg.ReplStart.endReplStart(builder);
|
|
|
|
const baseRes = dispatch.sendSync(builder, msg.Any.ReplStart, inner);
|
|
assert(baseRes != null);
|
|
assert(msg.Any.ReplStartRes === baseRes!.innerType());
|
|
const innerRes = new msg.ReplStartRes();
|
|
assert(baseRes!.inner(innerRes) != null);
|
|
const rid = innerRes.rid();
|
|
return rid;
|
|
}
|
|
|
|
// @internal
|
|
export async function readline(rid: number, prompt: string): Promise<string> {
|
|
const builder = flatbuffers.createBuilder();
|
|
const prompt_ = builder.createString(prompt);
|
|
msg.ReplReadline.startReplReadline(builder);
|
|
msg.ReplReadline.addRid(builder, rid);
|
|
msg.ReplReadline.addPrompt(builder, prompt_);
|
|
const inner = msg.ReplReadline.endReplReadline(builder);
|
|
|
|
const baseRes = await dispatch.sendAsync(
|
|
builder,
|
|
msg.Any.ReplReadline,
|
|
inner
|
|
);
|
|
|
|
assert(baseRes != null);
|
|
assert(msg.Any.ReplReadlineRes === baseRes!.innerType());
|
|
const innerRes = new msg.ReplReadlineRes();
|
|
assert(baseRes!.inner(innerRes) != null);
|
|
const line = innerRes.line();
|
|
assert(line !== null);
|
|
return line || "";
|
|
}
|
|
|
|
// @internal
|
|
export async function replLoop(): Promise<void> {
|
|
Object.defineProperties(window, replCommands);
|
|
|
|
const historyFile = "deno_history.txt";
|
|
const rid = startRepl(historyFile);
|
|
|
|
const quitRepl = (exitCode: number) => {
|
|
// Special handling in case user calls deno.close(3).
|
|
try {
|
|
close(rid); // close signals Drop on REPL and saves history.
|
|
} catch {}
|
|
exit(exitCode);
|
|
};
|
|
|
|
while (true) {
|
|
let code = "";
|
|
// Top level read
|
|
try {
|
|
code = await readline(rid, "> ");
|
|
if (code.trim() === "") {
|
|
continue;
|
|
}
|
|
} catch (err) {
|
|
if (err.message === "EOF") {
|
|
quitRepl(0);
|
|
} else {
|
|
// If interrupted, don't print error.
|
|
if (err.message !== "Interrupted") {
|
|
// e.g. this happens when we have deno.close(3).
|
|
// We want to display the problem.
|
|
const formattedError = formatError(libdeno.errorToJSON(err));
|
|
console.error(formattedError);
|
|
}
|
|
// Quit REPL anyways.
|
|
quitRepl(1);
|
|
}
|
|
}
|
|
// Start continued read
|
|
while (!evaluate(code)) {
|
|
code += "\n";
|
|
try {
|
|
code += await readline(rid, " ");
|
|
} catch (err) {
|
|
// If interrupted on continued read,
|
|
// abort this read instead of quitting.
|
|
if (err.message === "Interrupted") {
|
|
break;
|
|
} else if (err.message === "EOF") {
|
|
quitRepl(0);
|
|
} else {
|
|
// e.g. this happens when we have deno.close(3).
|
|
// We want to display the problem.
|
|
const formattedError = formatError(libdeno.errorToJSON(err));
|
|
console.error(formattedError);
|
|
quitRepl(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Evaluate code.
|
|
// Returns true if code is consumed (no error/irrecoverable error).
|
|
// Returns false if error is recoverable
|
|
function evaluate(code: string): boolean {
|
|
const [result, errInfo] = libdeno.evalContext(code);
|
|
if (!errInfo) {
|
|
console.log(result);
|
|
} else if (errInfo.isCompileError && isRecoverableError(errInfo.thrown)) {
|
|
// Recoverable compiler error
|
|
return false; // don't consume code.
|
|
} else {
|
|
if (errInfo.isNativeError) {
|
|
const formattedError = formatError(
|
|
libdeno.errorToJSON(errInfo.thrown as Error)
|
|
);
|
|
console.error(formattedError);
|
|
} else {
|
|
console.error("Thrown:", errInfo.thrown);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Error messages that allow users to continue input
|
|
// instead of throwing an error to REPL
|
|
// ref: https://github.com/v8/v8/blob/master/src/message-template.h
|
|
// TODO(kevinkassimo): this list might not be comprehensive
|
|
const recoverableErrorMessages = [
|
|
"Unexpected end of input", // { or [ or (
|
|
"Missing initializer in const declaration", // const a
|
|
"Missing catch or finally after try", // try {}
|
|
"missing ) after argument list", // console.log(1
|
|
"Unterminated template literal" // `template
|
|
// TODO(kevinkassimo): need a parser to handling errors such as:
|
|
// "Missing } in template expression" // `${ or `${ a 123 }`
|
|
];
|
|
|
|
function isRecoverableError(e: Error): boolean {
|
|
return recoverableErrorMessages.includes(e.message);
|
|
}
|