summaryrefslogtreecommitdiff
path: root/deno_typescript/bundle_loader.js
diff options
context:
space:
mode:
Diffstat (limited to 'deno_typescript/bundle_loader.js')
-rw-r--r--deno_typescript/bundle_loader.js124
1 files changed, 124 insertions, 0 deletions
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;
+ };
+})();