diff --git a/Makefile b/Makefile index 25416012b2..93791200ea 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ msg.pb.js: msg.proto node_modules msg.pb.d.ts: msg.pb.js node_modules ./node_modules/.bin/pbts -o msg.pb.d.ts msg.pb.js -dist/main.js: main.ts compiler.ts fs.ts util.ts msg.pb.js msg.pb.d.ts node_modules +dist/main.js: main.ts compiler.ts os.ts util.ts msg.pb.js msg.pb.d.ts node_modules ./node_modules/.bin/tsc --noEmit # Only for type checking. ./node_modules/.bin/parcel build --out-dir=dist/ --no-minify main.ts diff --git a/compiler.ts b/compiler.ts index fdd9dc997a..7f889b30c7 100644 --- a/compiler.ts +++ b/compiler.ts @@ -1,23 +1,32 @@ import * as ts from "typescript"; import { assert, globalEval } from "./util"; -import { readFileSync } from "./fs"; +import { exit, readFileSync } from "./os"; +import * as path from "path"; export function compile(cwd: string, inputFn: string): void { const options: ts.CompilerOptions = { - "allowJs": true, - "outFile": "out.js", + allowJs: true, + outFile: "out.js" }; const host = new CompilerHost(cwd); - let program = ts.createProgram([inputFn], options, host); + const inputExt = path.extname(inputFn); + if (!EXTENSIONS.includes(inputExt)) { + console.error(`Bad file name extension for input "${inputFn}"`); + exit(1); + } + + const program = ts.createProgram([inputFn], options, host); //let sourceFiles = program.getSourceFiles(); //console.log("rootFileNames", program.getRootFileNames()); - let emitResult = program.emit(); + const emitResult = program.emit(); assert(!emitResult.emitSkipped); //console.log("emitResult", emitResult); } +const EXTENSIONS = [".ts", ".js"]; + export class CompilerHost { constructor(public cwd: string) {} @@ -101,9 +110,24 @@ export class CompilerHost { moduleNames: string[], containingFile: string, reusedNames?: string[] - ): (ts.ResolvedModule | undefined)[] { - console.log("resolveModuleNames", moduleNames); - return []; + ): Array { + console.log("resolveModuleNames", { moduleNames, reusedNames }); + return moduleNames.map((name: string) => { + if ( + name.startsWith("/") || + name.startsWith("http://") || + name.startsWith("https://") + ) { + throw Error("Non-relative imports not yet supported."); + } else { + // Relative import. + console.log("relative import", { containingFile, name }); + const containingDir = path.dirname(containingFile); + const resolvedFileName = path.join(containingDir, name); + const isExternalLibraryImport = false; + return { resolvedFileName, isExternalLibraryImport }; + } + }); } fileExists(fileName: string): boolean { diff --git a/fs.ts b/fs.ts deleted file mode 100644 index a3d4c95195..0000000000 --- a/fs.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { main as pb } from "./msg.pb"; -import { TextDecoder } from "text-encoding"; - -export function readFileSync(filename: string): string { - const msg = pb.Msg.fromObject({ - kind: pb.Msg.MsgKind.READ_FILE_SYNC, - path: filename - }); - const ui8 = pb.Msg.encode(msg).finish(); - const ab = ui8.buffer.slice(ui8.byteOffset, ui8.byteOffset + ui8.byteLength); - const resBuf = V8Worker2.send(ab as ArrayBuffer); - const res = pb.Msg.decode(new Uint8Array(resBuf)); - if (res.error != null && res.error.length > 0) { - throw Error(res.error); - } - const decoder = new TextDecoder("utf8"); - return decoder.decode(res.data); -} diff --git a/main.go b/main.go index e634a6abe2..6e38d5f95b 100644 --- a/main.go +++ b/main.go @@ -31,9 +31,13 @@ func recv(buf []byte) []byte { switch msg.Kind { case Msg_READ_FILE_SYNC: return ReadFileSync(msg.Path) + case Msg_EXIT: + os.Exit(int(msg.Code)) default: panic("Unexpected message") } + + return nil } func loadAsset(w *v8worker2.Worker, path string) { diff --git a/main.ts b/main.ts index bd1b13ed5e..6e01557595 100644 --- a/main.ts +++ b/main.ts @@ -2,7 +2,6 @@ import { main as pb } from "./msg.pb"; import "./util"; import { compile } from "./compiler"; - function start(cwd: string, argv: string[]): void { // TODO parse arguments. const inputFn = argv[1]; diff --git a/msg.proto b/msg.proto index df0b7c102e..fcbd9bcbce 100644 --- a/msg.proto +++ b/msg.proto @@ -7,6 +7,7 @@ message Msg { START = 0; READ_FILE_SYNC = 1; DATA_RESPONSE = 2; + EXIT = 3; } MsgKind kind = 10; @@ -20,4 +21,7 @@ message Msg { // DATA_RESPONSE bytes data = 30; string error = 31; + + // EXIT + int32 code = 40; } diff --git a/os.ts b/os.ts new file mode 100644 index 0000000000..ca97e98e94 --- /dev/null +++ b/os.ts @@ -0,0 +1,41 @@ +import { main as pb } from "./msg.pb"; +import { TextDecoder } from "text-encoding"; + +// TODO move this to types.ts +type TypedArray = Uint8Array | Float32Array | Int32Array; + +export function exit(code = 0): void { + sendMsgFromObject({ + kind: pb.Msg.MsgKind.EXIT, + code + }); +} + +export function readFileSync(filename: string): string { + const res = sendMsgFromObject({ + kind: pb.Msg.MsgKind.READ_FILE_SYNC, + path: filename + }); + if (res.error != null && res.error.length > 0) { + throw Error(res.error); + } + const decoder = new TextDecoder("utf8"); + return decoder.decode(res.data); +} + +function typedArrayToArrayBuffer(ta: TypedArray): ArrayBuffer { + const ab = ta.buffer.slice(ta.byteOffset, ta.byteOffset + ta.byteLength); + return ab as ArrayBuffer; +} + +function sendMsgFromObject(obj: pb.IMsg): null | pb.Msg { + const msg = pb.Msg.fromObject(obj); + const ui8 = pb.Msg.encode(msg).finish(); + const ab = typedArrayToArrayBuffer(ui8); + const resBuf = V8Worker2.send(ab); + if (resBuf != null && resBuf.byteLength > 0) { + return pb.Msg.decode(new Uint8Array(resBuf)); + } else { + return null; + } +} diff --git a/testdata/hello_world.js b/testdata/001_hello.js similarity index 100% rename from testdata/hello_world.js rename to testdata/001_hello.js diff --git a/testdata/hello_world.js.out b/testdata/001_hello.js.out similarity index 100% rename from testdata/hello_world.js.out rename to testdata/001_hello.js.out diff --git a/testdata/hello_world.ts b/testdata/002_hello.ts similarity index 100% rename from testdata/hello_world.ts rename to testdata/002_hello.ts diff --git a/testdata/hello_world.ts.out b/testdata/002_hello.ts.out similarity index 100% rename from testdata/hello_world.ts.out rename to testdata/002_hello.ts.out diff --git a/testdata/003_relative_import.ts b/testdata/003_relative_import.ts new file mode 100644 index 0000000000..01d5d7faac --- /dev/null +++ b/testdata/003_relative_import.ts @@ -0,0 +1,3 @@ +import { printHello } from "./subdir/print_hello.ts"; + +printHello(); diff --git a/testdata/003_relative_import.ts.out b/testdata/003_relative_import.ts.out new file mode 100644 index 0000000000..e965047ad7 --- /dev/null +++ b/testdata/003_relative_import.ts.out @@ -0,0 +1 @@ +Hello diff --git a/testdata/subdir/print_hello.ts b/testdata/subdir/print_hello.ts new file mode 100644 index 0000000000..7ecce50405 --- /dev/null +++ b/testdata/subdir/print_hello.ts @@ -0,0 +1,3 @@ +export function printHello(): void { + console.log("Hello"); +} diff --git a/util.ts b/util.ts index f5e5e847e6..7a66669426 100644 --- a/util.ts +++ b/util.ts @@ -13,18 +13,28 @@ const print = V8Worker2.print; _global["console"] = { // tslint:disable-next-line:no-any log(...args: any[]): void { - const out: string[] = []; - for (const a of args) { - if (typeof a === "string") { - out.push(a); - } else { - out.push(JSON.stringify(a)); - } - } - print(out.join(" ")); + print(stringifyArgs(args)); + }, + + // tslint:disable-next-line:no-any + error(...args: any[]): void { + print("ERROR: " + stringifyArgs(args)); } }; +// tslint:disable-next-line:no-any +function stringifyArgs(args: any[]): string { + const out: string[] = []; + for (const a of args) { + if (typeof a === "string") { + out.push(a); + } else { + out.push(JSON.stringify(a)); + } + } + return out.join(" "); +} + export function assert(cond: boolean, msg = "") { if (!cond) { throw Error("Assertion failed. " + msg);