Original: fe7f6e117f
This commit is contained in:
Kitson Kelly 2019-06-11 00:34:43 +10:00 committed by Ryan Dahl
parent 87d3b9b5cc
commit 0ed3046a9a
6 changed files with 314 additions and 0 deletions

16
bundle/README.md Normal file
View file

@ -0,0 +1,16 @@
# bundle
These are modules that help support bundling with Deno.
## Usage
The main usage is to load and run bundles. For example, to run a bundle named
`bundle.js` in your current working directory:
```sh
deno run https://deno.land/std/bundle/run.ts bundle.js
```
---
Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.

11
bundle/run.ts Normal file
View file

@ -0,0 +1,11 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { evaluate, instantiate, load } from "./utils.ts";
async function main(args: string[]): Promise<void> {
const text = await load(args);
const result = evaluate(text);
instantiate(...result);
}
main(Deno.args);

111
bundle/test.ts Normal file
View file

@ -0,0 +1,111 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { test } from "../testing/mod.ts";
import {
assert,
AssertionError,
assertStrictEq,
assertThrowsAsync
} from "../testing/asserts.ts";
import { assertEquals } from "../testing/pretty.ts";
import { evaluate, instantiate, load, ModuleMetaData } from "./utils.ts";
/* eslint-disable @typescript-eslint/no-namespace */
declare global {
namespace globalThis {
var __results: [string, string] | undefined;
}
}
/* eslint-enable @typescript-eslint/no-namespace */
const fixture = `
define("data", [], { "baz": "qat" });
define("modB", ["require", "exports", "data"], function(require, exports, data) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.foo = "bar";
exports.baz = data.baz;
});
define("modA", ["require", "exports", "modB"], function(require, exports, modB) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
globalThis.__results = [modB.foo, modB.baz];
});
`;
const fixtureQueue = ["data", "modB", "modA"];
const fixtureModules = new Map<string, ModuleMetaData>();
fixtureModules.set("data", {
dependencies: [],
factory: {
baz: "qat"
},
exports: {}
});
fixtureModules.set("modB", {
dependencies: ["require", "exports", "data"],
factory(_require, exports, data): void {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.foo = "bar";
exports.baz = data.baz;
},
exports: {}
});
fixtureModules.set("modA", {
dependencies: ["require", "exports", "modB"],
factory(_require, exports, modB): void {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
globalThis.__results = [modB.foo, modB.baz];
},
exports: {}
});
test(async function loadBundle(): Promise<void> {
const result = await load(["", "./bundle/testdata/bundle.js"]);
assert(result != null);
assert(
result.includes(
`define("subdir/print_hello", ["require", "exports"], function(`
)
);
});
test(async function loadBadArgs(): Promise<void> {
await assertThrowsAsync(
async (): Promise<void> => {
await load(["bundle/test.ts"]);
},
AssertionError,
"Expected exactly two arguments."
);
});
test(async function loadMissingBundle(): Promise<void> {
await assertThrowsAsync(
async (): Promise<void> => {
await load([".", "bad_bundle.js"]);
},
AssertionError,
`Expected "bad_bundle.js" to exist.`
);
});
test(async function evaluateBundle(): Promise<void> {
assert(globalThis.define == null, "Expected 'define' to be undefined");
const [queue, modules] = evaluate(fixture);
assert(globalThis.define == null, "Expected 'define' to be undefined");
assertEquals(queue, ["data", "modB", "modA"]);
assert(modules.has("modA"));
assert(modules.has("modB"));
assert(modules.has("data"));
assertStrictEq(modules.size, 3);
});
test(async function instantiateBundle(): Promise<void> {
assert(globalThis.__results == null);
instantiate(fixtureQueue, fixtureModules);
assertEquals(globalThis.__results, ["bar", "qat"]);
delete globalThis.__results;
});

67
bundle/testdata/bundle.js vendored Normal file
View file

@ -0,0 +1,67 @@
define("subdir/print_hello", ["require", "exports"], function(
require,
exports
) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function printHello() {
console.log("Hello");
}
exports.printHello = printHello;
});
define("subdir/subdir2/mod2", [
"require",
"exports",
"subdir/print_hello"
], function(require, exports, print_hello_ts_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function returnsFoo() {
return "Foo";
}
exports.returnsFoo = returnsFoo;
function printHello2() {
print_hello_ts_1.printHello();
}
exports.printHello2 = printHello2;
});
define("subdir/mod1", ["require", "exports", "subdir/subdir2/mod2"], function(
require,
exports,
mod2_ts_1
) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function returnsHi() {
return "Hi";
}
exports.returnsHi = returnsHi;
function returnsFoo2() {
return mod2_ts_1.returnsFoo();
}
exports.returnsFoo2 = returnsFoo2;
function printHello3() {
mod2_ts_1.printHello2();
}
exports.printHello3 = printHello3;
function throwsError() {
throw Error("exception from mod1");
}
exports.throwsError = throwsError;
});
define("005_more_imports", ["require", "exports", "subdir/mod1"], function(
require,
exports,
mod1_ts_1
) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
mod1_ts_1.printHello3();
if (mod1_ts_1.returnsHi() !== "Hi") {
throw Error("Unexpected");
}
if (mod1_ts_1.returnsFoo2() !== "Foo") {
throw Error("Unexpected");
}
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVuZGxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZmlsZTovLy9Vc2Vycy9ra2VsbHkvZ2l0aHViL2Rlbm8vdGVzdHMvc3ViZGlyL3ByaW50X2hlbGxvLnRzIiwiZmlsZTovLy9Vc2Vycy9ra2VsbHkvZ2l0aHViL2Rlbm8vdGVzdHMvc3ViZGlyL3N1YmRpcjIvbW9kMi50cyIsImZpbGU6Ly8vVXNlcnMva2tlbGx5L2dpdGh1Yi9kZW5vL3Rlc3RzL3N1YmRpci9tb2QxLnRzIiwiZmlsZTovLy9Vc2Vycy9ra2VsbHkvZ2l0aHViL2Rlbm8vdGVzdHMvMDA1X21vcmVfaW1wb3J0cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7SUFBQSxTQUFnQixVQUFVO1FBQ3hCLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdkIsQ0FBQztJQUZELGdDQUVDOzs7OztJQ0FELFNBQWdCLFVBQVU7UUFDeEIsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRkQsZ0NBRUM7SUFFRCxTQUFnQixXQUFXO1FBQ3pCLDJCQUFVLEVBQUUsQ0FBQztJQUNmLENBQUM7SUFGRCxrQ0FFQzs7Ozs7SUNORCxTQUFnQixTQUFTO1FBQ3ZCLE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztJQUZELDhCQUVDO0lBRUQsU0FBZ0IsV0FBVztRQUN6QixPQUFPLG9CQUFVLEVBQUUsQ0FBQztJQUN0QixDQUFDO0lBRkQsa0NBRUM7SUFFRCxTQUFnQixXQUFXO1FBQ3pCLHFCQUFXLEVBQUUsQ0FBQztJQUNoQixDQUFDO0lBRkQsa0NBRUM7SUFFRCxTQUFnQixXQUFXO1FBQ3pCLE1BQU0sS0FBSyxDQUFDLHFCQUFxQixDQUFDLENBQUM7SUFDckMsQ0FBQztJQUZELGtDQUVDOzs7OztJQ2RELHFCQUFXLEVBQUUsQ0FBQztJQUVkLElBQUksbUJBQVMsRUFBRSxLQUFLLElBQUksRUFBRTtRQUN4QixNQUFNLEtBQUssQ0FBQyxZQUFZLENBQUMsQ0FBQztLQUMzQjtJQUVELElBQUkscUJBQVcsRUFBRSxLQUFLLEtBQUssRUFBRTtRQUMzQixNQUFNLEtBQUssQ0FBQyxZQUFZLENBQUMsQ0FBQztLQUMzQiIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBmdW5jdGlvbiBwcmludEhlbGxvKCk6IHZvaWQge1xuICBjb25zb2xlLmxvZyhcIkhlbGxvXCIpO1xufVxuIiwiaW1wb3J0IHsgcHJpbnRIZWxsbyB9IGZyb20gXCIuLi9wcmludF9oZWxsby50c1wiO1xuXG5leHBvcnQgZnVuY3Rpb24gcmV0dXJuc0ZvbygpOiBzdHJpbmcge1xuICByZXR1cm4gXCJGb29cIjtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHByaW50SGVsbG8yKCk6IHZvaWQge1xuICBwcmludEhlbGxvKCk7XG59XG4iLCJpbXBvcnQgeyByZXR1cm5zRm9vLCBwcmludEhlbGxvMiB9IGZyb20gXCIuL3N1YmRpcjIvbW9kMi50c1wiO1xuXG5leHBvcnQgZnVuY3Rpb24gcmV0dXJuc0hpKCk6IHN0cmluZyB7XG4gIHJldHVybiBcIkhpXCI7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZXR1cm5zRm9vMigpOiBzdHJpbmcge1xuICByZXR1cm4gcmV0dXJuc0ZvbygpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcHJpbnRIZWxsbzMoKTogdm9pZCB7XG4gIHByaW50SGVsbG8yKCk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB0aHJvd3NFcnJvcigpOiB2b2lkIHtcbiAgdGhyb3cgRXJyb3IoXCJleGNlcHRpb24gZnJvbSBtb2QxXCIpO1xufVxuIiwiaW1wb3J0IHsgcmV0dXJuc0hpLCByZXR1cm5zRm9vMiwgcHJpbnRIZWxsbzMgfSBmcm9tIFwiLi9zdWJkaXIvbW9kMS50c1wiO1xuXG5wcmludEhlbGxvMygpO1xuXG5pZiAocmV0dXJuc0hpKCkgIT09IFwiSGlcIikge1xuICB0aHJvdyBFcnJvcihcIlVuZXhwZWN0ZWRcIik7XG59XG5cbmlmIChyZXR1cm5zRm9vMigpICE9PSBcIkZvb1wiKSB7XG4gIHRocm93IEVycm9yKFwiVW5leHBlY3RlZFwiKTtcbn1cbiJdfQ==

108
bundle/utils.ts Normal file
View file

@ -0,0 +1,108 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { assertStrictEq, assert } from "../testing/asserts.ts";
import { exists } from "../fs/exists.ts";
export interface DefineFactory {
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
(...args: any): object | void;
}
export interface ModuleMetaData {
dependencies: string[];
factory?: DefineFactory | object;
exports: object;
}
type Define = (
id: string,
dependencies: string[],
factory: DefineFactory
) => void;
/* eslint-disable @typescript-eslint/no-namespace */
declare global {
namespace globalThis {
var define: Define | undefined;
}
}
/* eslint-enable @typescript-eslint/no-namespace */
/** Evaluate the bundle, returning a queue of module IDs and their data to
* instantiate.
*/
export function evaluate(
text: string
): [string[], Map<string, ModuleMetaData>] {
const queue: string[] = [];
const modules = new Map<string, ModuleMetaData>();
globalThis.define = function define(
id: string,
dependencies: string[],
factory: DefineFactory
): void {
modules.set(id, {
dependencies,
factory,
exports: {}
});
queue.push(id);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(Deno as any).core.evalContext(text);
// Deleting `define()` so it isn't accidentally there when the modules
// instantiate.
delete globalThis.define;
return [queue, modules];
}
/** Drain the queue of module IDs while instantiating the modules. */
export function instantiate(
queue: string[],
modules: Map<string, ModuleMetaData>
): void {
let id: string | undefined;
while ((id = queue.shift())) {
const module = modules.get(id)!;
assert(module != null);
assert(module.factory != null);
const dependencies = module.dependencies.map(
(id): object => {
if (id === "require") {
// TODO(kitsonk) support dynamic import by passing a `require()` that
// can return a local module or dynamically import one.
return (): void => {};
} else if (id === "exports") {
return module.exports;
}
const dep = modules.get(id)!;
assert(dep != null);
return dep.exports;
}
);
if (typeof module.factory === "function") {
module.factory!(...dependencies);
} else if (module.factory) {
// when bundling JSON, TypeScript just emits it as an object/array as the
// third argument of the `define()`.
module.exports = module.factory;
}
delete module.factory;
}
}
/** Load the bundle and return the contents asynchronously. */
export async function load(args: string[]): Promise<string> {
// TODO(kitsonk) allow loading of remote bundles via fetch.
assertStrictEq(args.length, 2, "Expected exactly two arguments.");
const [, bundleFileName] = args;
assert(
await exists(bundleFileName),
`Expected "${bundleFileName}" to exist.`
);
return new TextDecoder().decode(await Deno.readFile(bundleFileName));
}

View file

@ -2,6 +2,7 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import "./archive/tar_test.ts";
import "./bytes/test.ts";
import "./bundle/test.ts";
import "./colors/test.ts";
import "./datetime/test.ts";
import "./encoding/test.ts";