diff --git a/BUILD.gn b/BUILD.gn index 3e5ed6083d1f05e755884b6c9a28d93748ebd28d..fda094475506a0a6cd9e3a97e25a46b657d269f3 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -197,19 +197,53 @@ flatbuffer("msg_cpp") { ] } +# Generates type declarations for files that need to be included +# in the runtime bundle +run_node("gen_declarations") { + out_dir = target_gen_dir + sources = [ + "js/console.ts", + "js/deno.ts", + "js/globals.ts", + "js/os.ts", + "js/tsconfig.generated.json", + "js/util.ts", + ] + outputs = [ + out_dir + "/js/console.d.ts", + out_dir + "/js/deno.d.ts", + out_dir + "/js/globals.d.ts", + out_dir + "/js/os.d.ts", + out_dir + "/js/util.d.ts", + ] + deps = [ + ":msg_ts", + ] + args = [ + "./node_modules/typescript/bin/tsc", + "-p", + rebase_path("js/tsconfig.generated.json", root_build_dir), + "--baseUrl", + rebase_path(root_build_dir), + "--outDir", + rebase_path(out_dir), + ] +} + run_node("bundle") { out_dir = "$target_gen_dir/bundle/" sources = [ "js/assets.ts", "js/console.ts", - "js/deno.d.ts", "js/globals.ts", "js/lib.globals.d.ts", "js/main.ts", "js/os.ts", + "js/plugins.d.ts", "js/runtime.ts", - "js/types.ts", + "js/types.d.ts", "js/util.ts", + "js/v8_source_maps.ts", "rollup.config.js", "src/msg.fbs", "tsconfig.json", @@ -219,6 +253,7 @@ run_node("bundle") { out_dir + "main.js.map", ] deps = [ + ":gen_declarations", ":msg_ts", ] args = [ diff --git a/js/assets.ts b/js/assets.ts index 52c33b26b9fa15534b4dd98aa149def931501c4c..0c1b85e5dd5ee60cfa88eed26dfdfa45fa192f66 100644 --- a/js/assets.ts +++ b/js/assets.ts @@ -5,11 +5,15 @@ // There is a rollup plugin that will inline any module ending with `!string` // tslint:disable:max-line-length -import denoDts from "/js/deno.d.ts!string"; -// import libDts from "/third_party/node_modules/typescript/lib/lib.d.ts!string"; -import globalsDts from "/js/lib.globals.d.ts!string"; -// import libDomD qts from "/third_party/node_modules/typescript/lib/lib.dom.d.ts!string"; -// import libDomIterableDts from "/third_party/node_modules/typescript/lib/lib.dom.iterable.d.ts!string"; + +// Generated definitions +import consoleDts from "gen/js/console.d.ts!string"; +import denoDts from "gen/js/deno.d.ts!string"; +import globalsDts from "gen/js/globals.d.ts!string"; +import osDts from "gen/js/os.d.ts!string"; +import utilDts from "gen/js/util.d.ts!string"; + +// Static libraries import libEs2015Dts from "/third_party/node_modules/typescript/lib/lib.es2015.d.ts!string"; import libEs2015CollectionDts from "/third_party/node_modules/typescript/lib/lib.es2015.collection.d.ts!string"; import libEs2015CoreDts from "/third_party/node_modules/typescript/lib/lib.es2015.core.d.ts!string"; @@ -38,17 +42,23 @@ import libEsnextAsynciterablesDts from "/third_party/node_modules/typescript/lib import libEsnextDts from "/third_party/node_modules/typescript/lib/lib.esnext.d.ts!string"; import libEsnextIntlDts from "/third_party/node_modules/typescript/lib/lib.esnext.intl.d.ts!string"; import libEsnextSymbolDts from "/third_party/node_modules/typescript/lib/lib.esnext.symbol.d.ts!string"; -// import libScripthost from "/third_party/node_modules/typescript/lib/lib.scripthost.d.ts!string"; -// import libWebworkerImportscripts from "/third_party/node_modules/typescript/lib/lib.webworker.importscripts.d.ts!string"; +import libGlobalsDts from "/js/lib.globals.d.ts!string"; + +// Static definitions import typescriptDts from "/third_party/node_modules/typescript/lib/typescript.d.ts!string"; +import typesDts from "/js/types.d.ts!string"; +// tslint:enable:max-line-length // prettier-ignore export const assetSourceCode: { [key: string]: string } = { + // Generated definitions + "console.d.ts": consoleDts, "deno.d.ts": denoDts, - // "lib.d.ts": libDts, - "lib.globals.d.ts": globalsDts, - // "lib.dom.d.ts": libDomDts, - // "lib.dom.iterable.d.ts": libDomIterableDts, + "globals.d.ts": globalsDts, + "os.d.ts": osDts, + "util.d.ts": utilDts, + + // Static libraries "lib.es2015.collection.d.ts": libEs2015CollectionDts, "lib.es2015.core.d.ts": libEs2015CoreDts, "lib.es2015.d.ts": libEs2015Dts, @@ -61,32 +71,25 @@ export const assetSourceCode: { [key: string]: string } = { "lib.es2015.symbol.wellknown.d.ts": libEs2015SymbolWellknownDts, "lib.es2016.array.include.d.ts": libEs2016ArrayIncludeDts, "lib.es2016.d.ts": libEs2016Dts, - //"lib.es2016.full.d.ts": readFileSync(__dirname + "/../third_party/node_modules/typescript/lib/lib.es2016.full.d.ts", "utf8"), "lib.es2017.d.ts": libEs2017Dts, - //"lib.es2017.full.d.ts": readFileSync(__dirname + "/../third_party/node_modules/typescript/lib/lib.es2017.full.d.ts", "utf8"), "lib.es2017.intl.d.ts": libEs2017IntlDts, "lib.es2017.object.d.ts": libEs2017ObjectDts, "lib.es2017.sharedmemory.d.ts": libEs2017SharedmemoryDts, "lib.es2017.string.d.ts": libEs2017StringDts, "lib.es2017.typedarrays.d.ts": libEs2017TypedarraysDts, "lib.es2018.d.ts": libEs2018Dts, - //"lib.es2018.full.d.ts": readFileSync(__dirname + "/../third_party/node_modules/typescript/lib/lib.es2018.full.d.ts", "utf8"), "lib.es2018.intl.d.ts": libEs2018IntlDts, "lib.es2018.promise.d.ts": libEs2018PromiseDts, "lib.es2018.regexp.d.ts": libEs2018RegexpDts, "lib.es5.d.ts": libEs5Dts, - //"lib.es6.d.ts": readFileSync(__dirname + "/../third_party/node_modules/typescript/lib/lib.es6.d.ts", "utf8"), "lib.esnext.d.ts": libEsnextDts, "lib.esnext.array.d.ts": libEsnextArrayDts, "lib.esnext.asynciterable.d.ts": libEsnextAsynciterablesDts, "lib.esnext.intl.d.ts": libEsnextIntlDts, "lib.esnext.symbol.d.ts": libEsnextSymbolDts, - //"lib.esnext.full.d.ts": readFileSync(__dirname + "/../third_party/node_modules/typescript/lib/lib.esnext.full.d.ts", "utf8"), - // "lib.scripthost.d.ts": libScripthost, - // "lib.webworker.d.ts": libWebworker, - // "lib.webworker.importscripts.d.ts": libWebworkerImportscripts, - //"protocol.d.ts": readFileSync(__dirname + "/../third_party/node_modules/typescript/lib/protocol.d.ts", "utf8"), - //"tsserverlibrary.d.ts": readFileSync(__dirname + "/../third_party/node_modules/typescript/lib/tsserverlibrary.d.ts", "utf8"), + "lib.globals.d.ts": libGlobalsDts, + + // Static definitions "typescript.d.ts": typescriptDts, - //"typescriptServices.d.ts": readFileSync(__dirname + "/../third_party/node_modules/typescript/lib/typescriptServices.d.ts", "utf8"), + "types.d.ts": typesDts, }; diff --git a/js/deno.d.ts b/js/deno.d.ts deleted file mode 100644 index f2b726d8ecbfb44d0b8af9a8bab379abb78c742b..0000000000000000000000000000000000000000 --- a/js/deno.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2018 the Deno authors. All rights reserved. MIT license. -type MessageCallback = (msg: Uint8Array) => void; - -interface Deno { - recv(cb: MessageCallback): void; - send(msg: ArrayBufferView): null | Uint8Array; - print(x: string): void; -} - -declare let deno: Deno; diff --git a/js/deno.ts b/js/deno.ts index df1148453822edbca7cdf36fe19f631184cd410b..9ef533a9eaa5ba5459a279fa3679329cc184c47a 100644 --- a/js/deno.ts +++ b/js/deno.ts @@ -1,5 +1,5 @@ // Copyright 2018 the Deno authors. All rights reserved. MIT license. // Public deno module. // TODO get rid of deno.d.ts -export { pub, sub } from "./dispatch"; +// export { pub, sub } from "./dispatch"; export { readFileSync, writeFileSync } from "./os"; diff --git a/js/globals.ts b/js/globals.ts index c36eaff6033bc9a7030fa8a03dab945d6bd0b239..82c9a24c0f633518dc348c5a3a1b7665b9fa0ff1 100644 --- a/js/globals.ts +++ b/js/globals.ts @@ -1,5 +1,25 @@ // Copyright 2018 the Deno authors. All rights reserved. MIT license. +import { Console } from "./console"; + +declare global { + type MessageCallback = (msg: Uint8Array) => void; + + interface Deno { + print(text: string): void; + recv(cb: MessageCallback): void; + send(msg: ArrayBufferView): Uint8Array | null; + } + + interface Window { + console: Console; + } + + const console: Console; + const deno: Readonly; + const window: Window; +} + // If you use the eval function indirectly, by invoking it via a reference // other than eval, as of ECMAScript 5 it works in the global scope rather than // the local scope. This means, for instance, that function declarations create @@ -20,7 +40,6 @@ window["window"] = window; // Create a window object. // window["clearTimeout"] = timer.clearTimer; // window["clearInterval"] = timer.clearTimer; -import { Console } from "./console"; window["console"] = new Console(); // import { fetch } from "./fetch"; diff --git a/js/lib.globals.d.ts b/js/lib.globals.d.ts index b11a08edfb773d5b43318e6b2d10173fe0a69d85..d11bbe97daeb048e74429ab79c3c1988254bdd16 100644 --- a/js/lib.globals.d.ts +++ b/js/lib.globals.d.ts @@ -6,36 +6,14 @@ /// -// TODO generate `console.d.ts` and inline it in `assets.ts` and remove -// declaration of `Console` -// import { Console } from 'gen/console'; - -declare class Console { - // tslint:disable-next-line:no-any - log(...args: any[]): void; - // tslint:disable-next-line:no-any - debug(...args: any[]): void; - // tslint:disable-next-line:no-any - info(...args: any[]): void; - // tslint:disable-next-line:no-any - warn(...args: any[]): void; - // tslint:disable-next-line:no-any - error(...args: any[]): void; - // tslint:disable-next-line:no-any - assert(condition: boolean, ...args: any[]): void; -} +import "gen/js/globals"; interface Window { - console: Console; // TODO(ry) These shouldn't be global. mainSource: string; setMainSourceMap(sm: string): void; } -// Globals in the runtime environment -declare let console: Console; -declare const window: Window; - // TODO(ry) These shouldn't be global. declare let mainSource: string; declare function setMainSourceMap(sm: string): void; diff --git a/js/main.ts b/js/main.ts index 81dabab331c855efed46c23d6ae8837fdeb985ec..29c4ab4124856770d9cb95324ecefe832ee9cf99 100644 --- a/js/main.ts +++ b/js/main.ts @@ -1,6 +1,5 @@ // Copyright 2018 the Deno authors. All rights reserved. MIT license. // tslint:disable-next-line:no-reference -/// import { flatbuffers } from "flatbuffers"; import { deno as fbs } from "gen/msg_generated"; import { assert, log, assignCmdId } from "./util"; diff --git a/js/runtime.ts b/js/runtime.ts index 8b7863b9fbd2adf139dc44551a98dd2ec73fc417..9a3302d64a082bf85c71644fc35003ad3aeeecfd 100644 --- a/js/runtime.ts +++ b/js/runtime.ts @@ -166,12 +166,11 @@ export function resolveModule( util.log("resolveModule", { moduleSpecifier, containingFile }); util.assert(moduleSpecifier != null && moduleSpecifier.length > 0); let filename: string, sourceCode: string, outputCode: string; - if ( - moduleSpecifier.startsWith(ASSETS) || - containingFile.startsWith(ASSETS) - ) { + if (moduleSpecifier.startsWith(ASSETS) || containingFile.startsWith(ASSETS)) { // Assets are compiled into the runtime javascript bundle. - const assetName = moduleSpecifier.split("/").pop(); + const moduleId = moduleSpecifier.split("/").pop(); + const assetName = moduleId.includes(".") ? moduleId : `${moduleId}.d.ts`; + util.assert(assetName in assetSourceCode); sourceCode = assetSourceCode[assetName]; filename = ASSETS + assetName; } else { @@ -330,7 +329,7 @@ class TypeScriptHost implements ts.LanguageServiceHost { } getDefaultLibFileName(options: ts.CompilerOptions): string { - const fn = "lib.deno.d.ts"; // ts.getDefaultLibFileName(options); + const fn = "lib.globals.d.ts"; // ts.getDefaultLibFileName(options); util.log("getDefaultLibFileName", fn); const m = resolveModule(fn, ASSETS); return m.fileName; @@ -354,7 +353,9 @@ class TypeScriptHost implements ts.LanguageServiceHost { return undefined; } } - const isExternalLibraryImport = false; + // This flags to the compiler to not go looking to transpile functional + // code, anything that is in `/$asset$/` is just library code + const isExternalLibraryImport = resolvedFileName.startsWith(ASSETS); return { resolvedFileName, isExternalLibraryImport }; }); } diff --git a/js/tsconfig.generated.json b/js/tsconfig.generated.json new file mode 100644 index 0000000000000000000000000000000000000000..4fa3be1a19a6d74c3df0b802148e25aa4e46feb3 --- /dev/null +++ b/js/tsconfig.generated.json @@ -0,0 +1,16 @@ +{ + // This configuration file provides the tsc configuration for generating + // definitions for the runtime, which are then inlined via the `js/assets.ts` + // module into the bundle to be available for type checking at runtime + // See also gen_declarations in //BUILD.gn + "extends": "../tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + }, + "files": [ + "../node_modules/typescript/lib/lib.esnext.d.ts", + "./deno.ts", + "./globals.ts" + ] +} diff --git a/js/types.d.ts b/js/types.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..a9e013193cd60c03b29d0965c56f9e1e322e94fc --- /dev/null +++ b/js/types.d.ts @@ -0,0 +1,134 @@ +// Copyright 2018 the Deno authors. All rights reserved. MIT license. +export type TypedArray = Uint8Array | Float32Array | Int32Array; + +export interface ModuleInfo { + moduleName?: string; + filename?: string; + sourceCode?: string; + outputCode?: string; +} + +// Following definitions adapted from: +// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/node/index.d.ts +// Type definitions for Node.js 10.3.x +// Definitions by: Microsoft TypeScript +// DefinitelyTyped +// Parambir Singh +// Christian Vaagland Tellnes +// Wilco Bakker +// Nicolas Voigt +// Chigozirim C. +// Flarna +// Mariusz Wiktorczyk +// wwwy3y3 +// Deividas Bakanas +// Kelvin Jin +// Alvis HT Tang +// Sebastian Silbermann +// Hannes Magnusson +// Alberto Schiabel +// Klaus Meinhardt +// Huw +// Nicolas Even +// Bruno Scheufler +// Mohsen Azimi +// Hoàng Văn Khải +// Alexander T. +// Lishude +// Andrew Makarov + +export interface CallSite { + /** + * Value of "this" + */ + getThis(): any; + + /** + * Type of "this" as a string. + * This is the name of the function stored in the constructor field of + * "this", if available. Otherwise the object's [[Class]] internal + * property. + */ + getTypeName(): string | null; + + /** + * Current function + */ + getFunction(): Function | undefined; + + /** + * Name of the current function, typically its name property. + * If a name property is not available an attempt will be made to try + * to infer a name from the function's context. + */ + getFunctionName(): string | null; + + /** + * Name of the property [of "this" or one of its prototypes] that holds + * the current function + */ + getMethodName(): string | null; + + /** + * Name of the script [if this function was defined in a script] + */ + getFileName(): string | null; + + /** + * Get the script name or source URL for the source map + */ + getScriptNameOrSourceURL(): string; + + /** + * Current line number [if this function was defined in a script] + */ + getLineNumber(): number | null; + + /** + * Current column number [if this function was defined in a script] + */ + getColumnNumber(): number | null; + + /** + * A call site object representing the location where eval was called + * [if this function was created using a call to eval] + */ + getEvalOrigin(): string | undefined; + + /** + * Is this a toplevel invocation, that is, is "this" the global object? + */ + isToplevel(): boolean; + + /** + * Does this call take place in code defined by a call to eval? + */ + isEval(): boolean; + + /** + * Is this call in native V8 code? + */ + isNative(): boolean; + + /** + * Is this a constructor call? + */ + isConstructor(): boolean; +} + +declare global { + // Declare "static" methods in Error + interface ErrorConstructor { + /** Create .stack property on a target object */ + captureStackTrace(targetObject: Object, constructorOpt?: Function): void; + + /** + * Optional override for formatting stack traces + * + * @see https://github.com/v8/v8/wiki/Stack%20Trace%20API#customizing-stack-traces + */ + prepareStackTrace?: (err: Error, stackTraces: CallSite[]) => any; + + stackTraceLimit: number; + } +} diff --git a/js/types.ts b/js/types.ts deleted file mode 100644 index ae8ee1e81dcb3b4bea74d20229e41e852f0f0e81..0000000000000000000000000000000000000000 --- a/js/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2018 the Deno authors. All rights reserved. MIT license. -export type TypedArray = Uint8Array | Float32Array | Int32Array; - -export interface ModuleInfo { - moduleName?: string; - filename?: string; - sourceCode?: string; - outputCode?: string; -} diff --git a/js/v8_source_maps.ts b/js/v8_source_maps.ts index d5feeb1c053559b426514731aeb9f456d06f1b40..29091486c11435239b7057a28c35b7bc380c0094 100644 --- a/js/v8_source_maps.ts +++ b/js/v8_source_maps.ts @@ -2,16 +2,11 @@ // Copyright 2018 the Deno authors. All rights reserved. MIT license. // Originated from source-map-support but has been heavily modified for deno. -// Because NodeJS.CallSite and Error.prepareStackTrace are used we add a -// dependency on the Node types. -// TODO(ry) Ideally this triple slash directive should be removed as we only -// need CallSite and Error.prepareStackTrace but nothing else. -/// - import { SourceMapConsumer, MappedPosition } from "source-map"; import { RawSourceMap } from "source-map"; import * as base64 from "base64-js"; import { arrayToStr } from "./util"; +import { CallSite } from "./types"; const consumers = new Map(); @@ -22,10 +17,6 @@ interface Options { installPrepareStackTrace: boolean; } -interface CallSite extends NodeJS.CallSite { - getScriptNameOrSourceURL(): string; -} - interface Position { source: string; // Filename column: number; diff --git a/rollup.config.js b/rollup.config.js index a9fdacf85e3322a34d665b81614a7a72a50f0242..0f0e5a3e1dda9f7d0d5e85a14f2b8caef026e884 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -24,6 +24,15 @@ const tsconfigOverride = { } }; +// when the build path is not a child of the root of the project path +// TypeScript will output resources following the same path structure, +// `BASEPATH` will be a relative path to the root of the source project which +// we can use to determine what TypeScript would have output +const basePathParts = process.env.BASEPATH.split("/"); +while (basePathParts[0] === "..") { + basePathParts.shift(); +} + // this is a rollup plugin which will look for imports ending with `!string` and resolve // them with a module that will inline the contents of the file as a string. Needed to // support `js/assets.ts`. @@ -37,17 +46,38 @@ function strings({ include, exclude } = {}) { return { name: "strings", + /** + * @param {string} importee + */ resolveId(importee) { if (importee.endsWith("!string")) { + // strip the `!string` from `importee` + importee = importee.slice(0, importee.lastIndexOf("!string")); + if (!importee.startsWith("gen/")) { + // this is a static asset which is located relative to the root of the source project + return path.resolve(path.join(process.env.BASEPATH, importee)); + } + // ignoring the first part, which is "gen" + const [, ...importeeParts] = importee.split("/"); + // generated assets will be output by TypeScript relative between the build path and the + // root of the project. For example on Travis, the project path is: + // /home/travis/build/denoland/deno/ + // and the build path is: + // /home/travis/out/Default/ + // TypeScript will then output `globals.d.ts` from `js/globals.ts` to: + // /home/travis/out/Default/gen/build/denoland/deno/js/globals.d.ts + // therefore we need to insert any non relative BASEPATH parts into + // the final module ID return path.resolve( - path.join( - process.env.BASEPATH, - importee.slice(0, importee.lastIndexOf("!string")) - ) + path.join(process.cwd(), "gen", ...basePathParts, ...importeeParts) ); } }, + /** + * @param {any} code + * @param {string} id + */ transform(code, id) { if (filter(id)) { return { @@ -98,6 +128,15 @@ export default function makeConfig(commandOptions) { module: mockPath }), + // Provides inlining of file contents for `js/assets.ts` + strings({ + include: [ + "*.d.ts", + `${__dirname}/**/*.d.ts`, + `${process.cwd()}/**/*.d.ts` + ] + }), + // Resolves any resources that have been generated at build time resolveGenerated(), @@ -146,11 +185,6 @@ export default function makeConfig(commandOptions) { ] }), - // Provides inlining of file contents for `js/assets.ts` - strings({ - include: ["*.d.ts", `${__dirname}/**/*.d.ts`] - }), - // Provide some concise information about the bundle analyze({ skipFormatted: true, diff --git a/tsconfig.json b/tsconfig.json index 516868d3ee4859466acab061e4d0ba811454c6ee..e48f0fdf799f610a659260cdeee3fda09bc3d7d7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,23 +1,22 @@ { "compilerOptions": { - "allowJs": true, + "allowUnreachableCode": false, "baseUrl": ".", "module": "esnext", - "noImplicitAny": true, - "sourceMap": true, - "removeComments": true, - "preserveConstEnums": true, - "target": "esnext", "moduleResolution": "node", + "noImplicitAny": true, "noImplicitReturns": true, - "pretty": true, "noFallthroughCasesInSwitch": true, - "allowUnreachableCode": false, - "experimentalDecorators": true, + "noLib": true, "paths": { "*": ["*", "out/debug/*", "out/default/*", "out/release/*"] - } + }, + "preserveConstEnums": true, + "pretty": true, + "removeComments": true, + "sourceMap": true, + "target": "esnext", + "types": [] }, - "include": ["js/main.ts"], - "exclude": ["node_modules"] + "files": ["node_modules/typescript/lib/lib.esnext.d.ts", "js/main.ts"] }