Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] TypeScript support in REPL #1169

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions js/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ export class DenoCompiler
assert(
mediaType === MediaType.TypeScript || mediaType === MediaType.JavaScript
);
console.log("pre emit output");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before shipping, these should be util's log (hidden behind debug flag).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course, just needed some guidance during debugging.

const output = service.getEmitOutput(fileName);

// Get the relevant diagnostics - this is 3x faster than
Expand Down Expand Up @@ -469,6 +470,87 @@ export class DenoCompiler
return moduleMetaData.outputCode;
}

/**
* TODO:
* Incremental compilation of code used in REPL.
*/
incrementalCompile(
sourceCode: SourceCode,
previousOutput?: OutputCode
): {
define: AmdDefine,
diagnostics: ts.Diagnostic[];
outputCode: OutputCode;
additionalCode: OutputCode;
} {
const moduleId = "[repl]";
const fileName = `${moduleId}.ts`;

// TODO: this should probably be done only once per REPL
// create Module for REPL
const moduleMetaData = new ModuleMetaData(
moduleId,
fileName,
MediaType.TypeScript,
sourceCode
);
// TODO: this is poor-boy's cache-buster against TS compiler
moduleMetaData.scriptVersion = `${Math.random()}`;

this._moduleMetaDataMap.set(fileName, moduleMetaData);
this._scriptFileNames = [fileName];

// TODO: temporary solution to be able to resolve modules
const define = this._makeDefine(moduleMetaData);

// TODO: this is mostly copy-pasted from compile()
const service = this._service;
assert(
moduleMetaData.mediaType === MediaType.TypeScript ||
moduleMetaData.mediaType === MediaType.JavaScript
);

const output = service.getEmitOutput(fileName);

// Get the relevant diagnostics - this is 3x faster than
// `getPreEmitDiagnostics`.
const diagnostics = [
// TypeScript is overly opinionated that only CommonJS modules kinds can
// support JSON imports. Allegedly this was fixed in
// Microsoft/TypeScript#26825 but that doesn't seem to be working here,
// so we will ignore complaints about this compiler setting.
...service
.getCompilerOptionsDiagnostics()
.filter(diagnostic => diagnostic.code !== 5070),
...service.getSyntacticDiagnostics(fileName),
...service.getSemanticDiagnostics(fileName)
];

assert(!output.emitSkipped, "The emit was skipped for an unknown reason.");

assert(
output.outputFiles.length === 2,
`Expected 2 files to be emitted, got ${output.outputFiles.length}.`
);

const [sourceMapFile, outputFile] = output.outputFiles;
moduleMetaData.outputCode = `${outputFile.text}\n//# sourceURL=${fileName}`;
moduleMetaData.sourceMap = JSON.parse(sourceMapFile.text);

moduleMetaData.scriptVersion = "1";

// TODO:
// - diff code with 'previousOutput'
// - return only new lines as outputCode

return {
define,
diagnostics,
outputCode: moduleMetaData.outputCode,
additionalCode: ""
};
}

/** Given a fileName, return what was generated by the compiler. */
getGeneratedContents = (fileName: string): string | RawSourceMap => {
this._log("compiler.getGeneratedContents", fileName);
Expand Down
46 changes: 45 additions & 1 deletion js/repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { close } from "./files";
import * as dispatch from "./dispatch";
import { exit } from "./os";
import { window } from "./globals";
import { DenoCompiler } from "./compiler";

function startRepl(historyFile: string): number {
const builder = flatbuffers.createBuilder();
Expand Down Expand Up @@ -46,6 +47,20 @@ export function readline(rid: number, prompt: string): string {
return line || "";
}

interface ReplContext {
lines: string[];
previousOutput: string;
}

/**
* Eval helpers.
*/
// const EVAL_FILENAME = `[eval].ts`
const REPL_CONTEXT: ReplContext = {
lines: [],
previousOutput: ""
};

// @internal
export function replLoop(): void {
window.deno = deno; // FIXME use a new scope (rather than window).
Expand Down Expand Up @@ -78,8 +93,28 @@ export function replLoop(): void {

function evaluate(code: string): void {
try {
const result = eval.call(window, code); // FIXME use a new scope.
// TODO:
// 1. preserve context between calls
// 2. run only incremental lines, not full output
// 3. output diagnostics
REPL_CONTEXT.lines.push(code);
const compiledCode = compileReplCode(REPL_CONTEXT);

console.log("compiledCode", compiledCode);

if (compiledCode.diagnostics.length > 0) {
// TODO: use ts.formatDiagnostic
for (const diagnostic of compiledCode.diagnostics) {
console.log("[TS diagnostic]", diagnostic.messageText);
}
}

window.define = compiledCode.define;
// FIXME use a new scope.
const result = eval.call(window, compiledCode.outputCode);
console.log(result);
window.define = undefined;
REPL_CONTEXT.previousOutput = compiledCode.outputCode;
} catch (err) {
if (err instanceof Error) {
console.error(`${err.constructor.name}: ${err.message}`);
Expand All @@ -89,6 +124,15 @@ function evaluate(code: string): void {
}
}

const compiler = DenoCompiler.instance();

function compileReplCode(context: ReplContext) {
// TODO: right now only last line is passed, we should pass
// all lines, and let `incrementalCompile` return only new lines
const sourceCode = context.lines[context.lines.length - 1];
return compiler.incrementalCompile(sourceCode, context.previousOutput);
}

function readBlock(
rid: number,
prompt: string,
Expand Down