summaryrefslogtreecommitdiff
path: root/deno_typescript
diff options
context:
space:
mode:
Diffstat (limited to 'deno_typescript')
-rw-r--r--deno_typescript/README.md59
-rw-r--r--deno_typescript/bundle_loader.js121
-rw-r--r--deno_typescript/lib.rs24
-rw-r--r--deno_typescript/system_loader.js110
4 files changed, 120 insertions, 194 deletions
diff --git a/deno_typescript/README.md b/deno_typescript/README.md
index c2fb7f5a5..220b04ae4 100644
--- a/deno_typescript/README.md
+++ b/deno_typescript/README.md
@@ -5,3 +5,62 @@
This crate provides utilities to compile typescript, bundle it up, and create a
V8 snapshot, all during build. Snapshots allow the executable to startup fast.
+
+## `system_loader.js`
+
+This is a minimalistic implementation of a
+[System](https://github.com/systemjs/systemjs) module loader. It is specifically
+designed to load modules that are emitted from TypeScript the module format is
+`"system"` and a single `"outfile"` is supplied, which is commonly refereed to
+as a bundle.
+
+Because this loader becomes part of an emitted bundle under `Deno.bundle()` and
+`deno bundle`, it has minimal comments and very terse and cryptic syntax, which
+isn't very self documenting. Because of this, a guide to this file is provided
+here.
+
+A bundle of System modules expects a `System.register()` function to be in scope
+for registering the modules. Modules that are emitted from TypeScript in a
+single out file always pass 3 arguments, the module specifier, an array of
+strings of modules specifiers that this module depends upon, and finally a
+module factory.
+
+The module factory requires two arguments to be passed, a function for exporting
+values and a context object. We have to bind to some information in the
+environment to provide these, so `gC` gets the context and `gE` gets the export
+function to be passed to a factory. The context contains information like the
+module specifier, a reference to the dynamic `import()` and the equivalent of
+`import.meta`. The export function takes either two arguments of an named export
+and its value, or an object record of keys of the named exports and the values
+of the exports.
+
+The running of the factories is handled by `rF()`. When the factory is run, it
+returns an object with two keys, `execute` and `setters`. `execute` is a
+function which finalises that instantiation of the module, and `setters` which
+is an array of functions that sets the value of the exports of the dependent
+module.
+
+The `gExp()` and `gExpA()` are the recursive functions which returns the exports
+of a given module. It will determine if the module has been fully initialized,
+and if not, it will gather the exports of the dependencies, set those exports in
+the module via the `setters` and run the modules `execute()`. It will then
+always return or resolve with the exports of the module.
+
+As of TypeScript 3.8, top level await is supported when emitting ES or System
+modules. When Deno creates a module bundle, it creates a valid, self-contained
+ES module which exports the exports of the "main" module that was used when the
+bundle was created. If a module in the bundle requires top-level-await, then the
+`execute()` function is emitted as an async function, returning a promise. This
+means that in order to export the values of the main module, the instantiation
+needs to utilise top-level-await as well.
+
+At the time of this writing, while V8 and other JavaScript engines have
+implemented top-level-await, no browsers have it implemented, meaning that most
+browsers could not consume modules that require top-level-await.
+
+In order to facilitate this, there are two functions that are in the scope of
+the module in addition to the `System.register()` method. `__instantiate(main)`
+will bootstrap everything synchronously and `__instantiate(main)` will do so
+asynchronously. When emitting a bundle that contains a module that requires
+top-level-await, Deno will detect this and utilise
+`await __instantiateAsync(main)` instead.
diff --git a/deno_typescript/bundle_loader.js b/deno_typescript/bundle_loader.js
deleted file mode 100644
index 5b4b5ec16..000000000
--- a/deno_typescript/bundle_loader.js
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2018-2020 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.
-
-// @ts-nocheck
-
-/**
- * @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) => any=}
- */
-// 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;
- const result = getExports(dep);
- // clean up, or otherwise these end up in the runtime environment
- instantiate = undefined;
- return result;
- };
-})();
diff --git a/deno_typescript/lib.rs b/deno_typescript/lib.rs
index f1b0dd4da..d88932eeb 100644
--- a/deno_typescript/lib.rs
+++ b/deno_typescript/lib.rs
@@ -25,7 +25,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 BUNDLE_LOADER: &str = include_str!("bundle_loader.js");
+static SYSTEM_LOADER: &str = include_str!("system_loader.js");
pub fn ts_version() -> String {
let data = include_str!("typescript/package.json");
@@ -143,20 +143,20 @@ pub fn compile_bundle(
let config_json = serde_json::json!({
"compilerOptions": {
- "strict": true,
"declaration": true,
- "lib": ["esnext"],
- "module": "amd",
- "target": "esnext",
- "listFiles": true,
- "listEmittedFiles": true,
- // "types" : ["typescript.d.ts"],
- "typeRoots" : ["$typeRoots$"],
// Emit the source alongside the sourcemaps within a single file;
// requires --inlineSourceMap or --sourceMap to be set.
// "inlineSources": true,
- "sourceMap": true,
+ "lib": ["esnext"],
+ "listEmittedFiles": true,
+ "listFiles": true,
+ "module": "system",
"outFile": bundle_filename,
+ "removeComments": true,
+ "sourceMap": true,
+ "strict": true,
+ "target": "esnext",
+ "typeRoots" : ["$typeRoots$"],
},
});
@@ -198,13 +198,13 @@ pub fn mksnapshot_bundle(
bundle_filename: &Path,
main_module_name: &str,
) -> Result<(), ErrBox> {
- js_check(isolate.execute("bundle_loader.js", BUNDLE_LOADER));
+ js_check(isolate.execute("system_loader.js", SYSTEM_LOADER));
let source_code_vec = std::fs::read(bundle_filename).unwrap();
let bundle_source_code = std::str::from_utf8(&source_code_vec).unwrap();
js_check(
isolate.execute(&bundle_filename.to_string_lossy(), bundle_source_code),
);
- let script = &format!("instantiate('{}')", main_module_name);
+ let script = &format!("__instantiate(\"{}\");", main_module_name);
js_check(isolate.execute("anon", script));
write_snapshot(isolate, snapshot_filename)?;
Ok(())
diff --git a/deno_typescript/system_loader.js b/deno_typescript/system_loader.js
index 18a08107e..41748c46e 100644
--- a/deno_typescript/system_loader.js
+++ b/deno_typescript/system_loader.js
@@ -4,30 +4,29 @@
// @ts-nocheck
/* eslint-disable */
-
-let System, __inst, __inst_s;
+let System, __instantiateAsync, __instantiate;
(() => {
- const mMap = new Map();
+ const r = new Map();
+
System = {
register(id, d, f) {
- mMap.set(id, { id, d, f, exp: {} });
+ r.set(id, { d, f, exp: {} });
}
};
- const gC = (data, main) => {
- const { id } = data;
+ function gC(id, main) {
return {
id,
- import: async id => mMap.get(id)?.exp,
+ import: async id => r.get(id)?.exp,
meta: { url: id, main }
};
- };
+ }
- const gE = ({ exp }) => {
+ function gE(exp) {
return (id, v) => {
- const vs = typeof id === "string" ? { [id]: v } : id;
- for (const [id, value] of Object.entries(vs)) {
+ v = typeof id === "string" ? { [id]: v } : id;
+ for (const [id, value] of Object.entries(v)) {
Object.defineProperty(exp, id, {
value,
writable: true,
@@ -35,65 +34,54 @@ let System, __inst, __inst_s;
});
}
};
- };
-
- const iQ = [];
-
- const enq = ids => {
- for (const id of ids) {
- if (!iQ.includes(id)) {
- const { d } = mMap.get(id);
- iQ.push(id);
- enq(d);
- }
- }
- };
+ }
- const gRQ = main => {
- const rQ = [];
- let id;
- while ((id = iQ.pop())) {
- const m = mMap.get(id),
- { f } = m;
- if (!f) return;
- rQ.push([m.d, f(gE(m), gC(m, id === main))]);
+ function rF(main) {
+ for (const [id, m] of r.entries()) {
+ const { f, exp } = m;
+ const { execute: e, setters: s } = f(gE(exp), gC(id, id === main));
delete m.f;
+ m.e = e;
+ m.s = s;
}
- return rQ;
- };
+ }
- const dr = async main => {
- const rQ = gRQ(main);
- let r;
- while ((r = rQ.shift())) {
- const [d, { execute, setters }] = r;
- for (let i = 0; i < d.length; i++) setters[i](mMap.get(d[i])?.exp);
- const e = execute();
- if (e) await e;
+ async function gExpA(id) {
+ if (!r.has(id)) return;
+ const m = r.get(id);
+ if (m.s) {
+ const { d, e, s } = m;
+ delete m.s;
+ delete m.e;
+ for (let i = 0; i < s.length; i++) s[i](await gExpA(d[i]));
+ const r = e();
+ if (r) await r;
}
- };
+ return m.exp;
+ }
- const dr_s = main => {
- const rQ = gRQ(main);
- let r;
- while ((r = rQ.shift())) {
- const [d, { execute, setters }] = r;
- for (let i = 0; i < d.length; i++) setters[i](mMap.get(d[i])?.exp);
- execute();
+ function gExp(id) {
+ if (!r.has(id)) return;
+ const m = r.get(id);
+ if (m.s) {
+ const { d, e, s } = m;
+ delete m.s;
+ delete m.e;
+ for (let i = 0; i < s.length; i++) s[i](gExp(d[i]));
+ e();
}
- };
+ return m.exp;
+ }
- __inst = async id => {
- System = __inst = __inst_s = undefined;
- enq([id]);
- await dr(id);
- return mMap.get(id)?.exp;
+ __instantiateAsync = async m => {
+ System = __instantiateAsync = __instantiate = undefined;
+ rF(m);
+ return gExpA(m);
};
- __inst_s = id => {
- System = __inst = __inst_s = undefined;
- enq([id]);
- dr_s(id);
- return mMap.get(id)?.exp;
+ __instantiate = m => {
+ System = __instantiateAsync = __instantiate = undefined;
+ rF(m);
+ return gExp(m);
};
})();