diff options
author | Kitson Kelly <me@kitsonkelly.com> | 2019-11-14 02:35:56 +1100 |
---|---|---|
committer | Ry Dahl <ry@tinyclouds.org> | 2019-11-13 10:35:56 -0500 |
commit | 8d03397293b388317299dfb0648b541a7005807d (patch) | |
tree | 38aeb4deb371e81f47b6750110d96de46b9f5b05 /deno_typescript | |
parent | ee1b8dc883db1531d913f7b10a8d1160f3209d93 (diff) |
Make bundles fully standalone (#3325)
- Bundles are fully standalone. They now include the shared loader with
`deno_typescript`.
- Refactor of the loader in `deno_typescript` to perform module
instantiation in a more
- Change of behaviour when an output file is not specified on the CLI.
Previously a default name was determined and the bundle written to that
file, now the bundle will be sent to `stdout`.
- Refactors in the TypeScript compiler to be able to support the concept
of a request type. This provides a cleaner abstraction and makes it
easier to support things like single module transpiles to the userland.
- Remove a "dangerous" circular dependency between `os.ts` and `deno.ts`,
and define `pid` and `noColor` in a better way.
- Don't bind early to `console` in `repl.ts`.
- Add an integration test for generating a bundle.
Diffstat (limited to 'deno_typescript')
-rw-r--r-- | deno_typescript/amd_runtime.js | 54 | ||||
-rw-r--r-- | deno_typescript/bundle_loader.js | 124 | ||||
-rw-r--r-- | deno_typescript/lib.rs | 15 |
3 files changed, 134 insertions, 59 deletions
diff --git a/deno_typescript/amd_runtime.js b/deno_typescript/amd_runtime.js deleted file mode 100644 index 1c4f0007a..000000000 --- a/deno_typescript/amd_runtime.js +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. - -// A very very basic AMD preamble to support the output of TypeScript outFile -// bundles. - -/** - * @type {(name: string) => any} - */ -let require; - -/** - * @type {(name: string, deps: ReadonlyArray<string>, factory: (...deps: any[]) => void) => void} - */ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -let define; - -(function() { - /** - * @type {Map<string, { name: string, exports: any }>} - */ - const modules = new Map(); - - /** - * @param {string} name - */ - function createOrLoadModule(name) { - let m = modules.get(name); - if (!m) { - m = { name, exports: {} }; - modules.set(name, m); - } - return m; - } - - require = name => { - return createOrLoadModule(name).exports; - }; - - define = (name, deps, factory) => { - const currentModule = createOrLoadModule(name); - const localExports = currentModule.exports; - const args = deps.map(dep => { - if (dep === "require") { - return require; - } else if (dep === "exports") { - return localExports; - } else { - const depModule = createOrLoadModule(dep); - return depModule.exports; - } - }); - factory(...args); - }; -})(); diff --git a/deno_typescript/bundle_loader.js b/deno_typescript/bundle_loader.js new file mode 100644 index 000000000..682754628 --- /dev/null +++ b/deno_typescript/bundle_loader.js @@ -0,0 +1,124 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. + +// A script preamble that provides the ability to load a single outfile +// TypeScript "bundle" where a main module is loaded which recursively +// instantiates all the other modules in the bundle. This code is used to load +// bundles when creating snapshots, but is also used when emitting bundles from +// Deno cli. + +/** + * @type {(name: string, deps: ReadonlyArray<string>, factory: (...deps: any[]) => void) => void} + */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +let define; + +/** + * @type {(mod: string | string[]) => void} + */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars +let instantiate; + +/** + * @callback Factory + * @argument {...any[]} args + * @returns {object | void} + */ + +/** + * @typedef ModuleMetaData + * @property {ReadonlyArray<string>} dependencies + * @property {(Factory | object)=} factory + * @property {object} exports + */ + +(function() { + /** + * @type {Map<string, ModuleMetaData>} + */ + const modules = new Map(); + + /** + * Bundles in theory can support "dynamic" imports, but for internal bundles + * we can't go outside to fetch any modules that haven't been statically + * defined. + * @param {string[]} deps + * @param {(...deps: any[]) => void} resolve + * @param {(err: any) => void} reject + */ + const require = (deps, resolve, reject) => { + try { + if (deps.length !== 1) { + throw new TypeError("Expected only a single module specifier."); + } + if (!modules.has(deps[0])) { + throw new RangeError(`Module "${deps[0]}" not defined.`); + } + resolve(getExports(deps[0])); + } catch (e) { + if (reject) { + reject(e); + } else { + throw e; + } + } + }; + + define = (id, dependencies, factory) => { + if (modules.has(id)) { + throw new RangeError(`Module "${id}" has already been defined.`); + } + modules.set(id, { + dependencies, + factory, + exports: {} + }); + }; + + /** + * @param {string} id + * @returns {any} + */ + function getExports(id) { + const module = modules.get(id); + if (!module) { + // because `$deno$/ts_global.d.ts` looks like a real script, it doesn't + // get erased from output as an import, but it doesn't get defined, so + // we don't have a cache for it, so because this is an internal bundle + // we can just safely return an empty object literal. + return {}; + } + if (!module.factory) { + return module.exports; + } else if (module.factory) { + const { factory, exports } = module; + delete module.factory; + if (typeof factory === "function") { + const dependencies = module.dependencies.map(id => { + if (id === "require") { + return require; + } else if (id === "exports") { + return exports; + } + return getExports(id); + }); + factory(...dependencies); + } else { + Object.assign(exports, factory); + } + return exports; + } + } + + instantiate = dep => { + define = undefined; + if (Array.isArray(dep)) { + for (const d of dep) { + getExports(d); + } + } else { + getExports(dep); + } + // clean up, or otherwise these end up in the runtime environment + instantiate = undefined; + }; +})(); diff --git a/deno_typescript/lib.rs b/deno_typescript/lib.rs index d130ee4c6..509bce56f 100644 --- a/deno_typescript/lib.rs +++ b/deno_typescript/lib.rs @@ -22,7 +22,7 @@ use std::sync::Mutex; static TYPESCRIPT_CODE: &str = include_str!("typescript/lib/typescript.js"); static COMPILER_CODE: &str = include_str!("compiler_main.js"); -static AMD_RUNTIME_CODE: &str = include_str!("amd_runtime.js"); +static BUNDLE_LOADER: &str = include_str!("bundle_loader.js"); pub fn ts_version() -> String { let data = include_str!("typescript/package.json"); @@ -181,11 +181,13 @@ pub fn mksnapshot_bundle( let source_code_vec = std::fs::read(bundle)?; let source_code = std::str::from_utf8(&source_code_vec)?; - js_check(runtime_isolate.execute("amd_runtime.js", AMD_RUNTIME_CODE)); + js_check(runtime_isolate.execute("bundle_loader.js", BUNDLE_LOADER)); js_check(runtime_isolate.execute(&bundle.to_string_lossy(), &source_code)); let main = state.lock().unwrap().main_module_name(); - js_check(runtime_isolate.execute("anon", &format!("require('{}')", main))); + js_check( + runtime_isolate.execute("anon", &format!("instantiate('{}')", main)), + ); write_snapshot(runtime_isolate, bundle)?; @@ -202,12 +204,14 @@ pub fn mksnapshot_bundle_ts( let source_code_vec = std::fs::read(bundle)?; let source_code = std::str::from_utf8(&source_code_vec)?; - js_check(runtime_isolate.execute("amd_runtime.js", AMD_RUNTIME_CODE)); + js_check(runtime_isolate.execute("bundle_loader.js", BUNDLE_LOADER)); js_check(runtime_isolate.execute("typescript.js", TYPESCRIPT_CODE)); js_check(runtime_isolate.execute(&bundle.to_string_lossy(), &source_code)); let main = state.lock().unwrap().main_module_name(); - js_check(runtime_isolate.execute("anon", &format!("require('{}')", main))); + js_check( + runtime_isolate.execute("anon", &format!("instantiate('{}')", main)), + ); write_snapshot(runtime_isolate, bundle)?; @@ -249,6 +253,7 @@ pub fn get_asset(name: &str) -> Option<&'static str> { }; } match name { + "bundle_loader.js" => Some(include_str!("bundle_loader.js")), "lib.deno_core.d.ts" => Some(include_str!("lib.deno_core.d.ts")), "typescript.d.ts" => inc!("typescript.d.ts"), "lib.esnext.d.ts" => inc!("lib.esnext.d.ts"), |