mirror of
https://github.com/denoland/deno.git
synced 2024-11-23 15:16:54 -05:00
parent
87d3b9b5cc
commit
0ed3046a9a
6 changed files with 314 additions and 0 deletions
16
bundle/README.md
Normal file
16
bundle/README.md
Normal 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
11
bundle/run.ts
Normal 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
111
bundle/test.ts
Normal 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
67
bundle/testdata/bundle.js
vendored
Normal 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
108
bundle/utils.ts
Normal 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));
|
||||||
|
}
|
1
test.ts
1
test.ts
|
@ -2,6 +2,7 @@
|
||||||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||||
import "./archive/tar_test.ts";
|
import "./archive/tar_test.ts";
|
||||||
import "./bytes/test.ts";
|
import "./bytes/test.ts";
|
||||||
|
import "./bundle/test.ts";
|
||||||
import "./colors/test.ts";
|
import "./colors/test.ts";
|
||||||
import "./datetime/test.ts";
|
import "./datetime/test.ts";
|
||||||
import "./encoding/test.ts";
|
import "./encoding/test.ts";
|
||||||
|
|
Loading…
Reference in a new issue