summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2020-06-18 09:06:48 -0400
committerGitHub <noreply@github.com>2020-06-18 09:06:48 -0400
commita2969ecd27645bafc7195baa7cfecbebfd8d2bf4 (patch)
treed4bfd2fa7473b9bbfb5fa25eee4dc7238f040ddf /cli
parent940f8e8433ae5ec74b2642438849089a0433e512 (diff)
Deno.bundle supports targets < ES2017 (#6346)
This commit provides a "system_loader_es5.js" bundle loader which will be added to the bundle when the target is < ES2017, which is the minimum target syntax required for "system_loader.js". Supports #5913 (via Deno.bundle()) with a couple caveats: * Allowing "deno bundle" to take a different target is not supported, as we specifically ignore "target" when passed in a TypeScript config file. This is because deno bundle is really intended to generate bundles that work in Deno. It is an unintentional side effect that some bundles are loadable in browsers. * While a target of "es3" will be accepted, the module loader will still only be compatible with ES5 or later. Realistically no one should be expecting bundles generated by Deno to be used on IE8 and prior, and there is just too much "baggage" to support that at this point. This is a minor variation of 75bb9d, which exposed some sort of internal V8 bug. Ref #6358 This is 100% authored by Kitson Kelly. Github might change the author when landing so I'm leaving this in: Co-authored-by: Kitson Kelly <me@kitsonkelly.com>
Diffstat (limited to 'cli')
-rw-r--r--cli/js/compiler.ts34
-rw-r--r--cli/tests/bundle.test.out5
-rw-r--r--cli/tests/compiler_api_test.ts297
3 files changed, 203 insertions, 133 deletions
diff --git a/cli/js/compiler.ts b/cli/js/compiler.ts
index abe145da2..46dbfcaf9 100644
--- a/cli/js/compiler.ts
+++ b/cli/js/compiler.ts
@@ -307,6 +307,10 @@ class Host implements ts.CompilerHost {
}
}
+ get options(): ts.CompilerOptions {
+ return this.#options;
+ }
+
configure(
cwd: string,
path: string,
@@ -528,6 +532,7 @@ const _TS_SNAPSHOT_PROGRAM = ts.createProgram({
// This function is called only during snapshotting process
const SYSTEM_LOADER = getAsset("system_loader.js");
+const SYSTEM_LOADER_ES5 = getAsset("system_loader_es5.js");
function buildLocalSourceFileCache(
sourceFileMap: Record<string, SourceFileMapEntry>
@@ -683,7 +688,12 @@ function createBundleWriteFile(state: WriteFileState): WriteFileCallback {
assert(state.bundle);
// we only support single root names for bundles
assert(state.rootNames.length === 1);
- state.bundleOutput = buildBundle(state.rootNames[0], data, sourceFiles);
+ state.bundleOutput = buildBundle(
+ state.rootNames[0],
+ data,
+ sourceFiles,
+ state.host.options.target ?? ts.ScriptTarget.ESNext
+ );
};
}
@@ -949,7 +959,8 @@ function normalizeUrl(rootName: string): string {
function buildBundle(
rootName: string,
data: string,
- sourceFiles: readonly ts.SourceFile[]
+ sourceFiles: readonly ts.SourceFile[],
+ target: ts.ScriptTarget
): string {
// when outputting to AMD and a single outfile, TypeScript makes up the module
// specifiers which are used to define the modules, and doesn't expose them
@@ -967,8 +978,8 @@ function buildBundle(
let instantiate: string;
if (rootExports && rootExports.length) {
instantiate = hasTla
- ? `const __exp = await __instantiateAsync("${rootName}");\n`
- : `const __exp = __instantiate("${rootName}");\n`;
+ ? `const __exp = await __instantiate("${rootName}", true);\n`
+ : `const __exp = __instantiate("${rootName}", false);\n`;
for (const rootExport of rootExports) {
if (rootExport === "default") {
instantiate += `export default __exp["${rootExport}"];\n`;
@@ -978,10 +989,19 @@ function buildBundle(
}
} else {
instantiate = hasTla
- ? `await __instantiateAsync("${rootName}");\n`
- : `__instantiate("${rootName}");\n`;
+ ? `await __instantiate("${rootName}", true);\n`
+ : `__instantiate("${rootName}", false);\n`;
}
- return `${SYSTEM_LOADER}\n${data}\n${instantiate}`;
+ const es5Bundle =
+ target === ts.ScriptTarget.ES3 ||
+ target === ts.ScriptTarget.ES5 ||
+ target === ts.ScriptTarget.ES2015 ||
+ target === ts.ScriptTarget.ES2016
+ ? true
+ : false;
+ return `${
+ es5Bundle ? SYSTEM_LOADER_ES5 : SYSTEM_LOADER
+ }\n${data}\n${instantiate}`;
}
function setRootExports(program: ts.Program, rootModule: string): void {
diff --git a/cli/tests/bundle.test.out b/cli/tests/bundle.test.out
index b286dad48..eba439424 100644
--- a/cli/tests/bundle.test.out
+++ b/cli/tests/bundle.test.out
@@ -1,6 +1,5 @@
[WILDCARD]
-let System, __instantiateAsync, __instantiate;
-[WILDCARD]
+let System, __instantiate;
(() => {
[WILDCARD]
})();
@@ -15,7 +14,7 @@ System.register("mod1", ["subdir2/mod2"], function (exports_3, context_3) {
[WILDCARD]
});
-const __exp = __instantiate("mod1");
+const __exp = __instantiate("mod1", false);
export const returnsHi = __exp["returnsHi"];
export const returnsFoo2 = __exp["returnsFoo2"];
export const printHello3 = __exp["printHello3"];
diff --git a/cli/tests/compiler_api_test.ts b/cli/tests/compiler_api_test.ts
index 967220cb4..274f1b98d 100644
--- a/cli/tests/compiler_api_test.ts
+++ b/cli/tests/compiler_api_test.ts
@@ -1,150 +1,201 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals } from "../../std/testing/asserts.ts";
-Deno.test("compilerApiCompileSources", async function () {
- const [diagnostics, actual] = await Deno.compile("/foo.ts", {
- "/foo.ts": `import * as bar from "./bar.ts";\n\nconsole.log(bar);\n`,
- "/bar.ts": `export const bar = "bar";\n`,
- });
- assert(diagnostics == null);
- assert(actual);
- assertEquals(Object.keys(actual), [
- "/bar.js.map",
- "/bar.js",
- "/foo.js.map",
- "/foo.js",
- ]);
+Deno.test({
+ name: "Deno.compile() - sources provided",
+ async fn() {
+ const [diagnostics, actual] = await Deno.compile("/foo.ts", {
+ "/foo.ts": `import * as bar from "./bar.ts";\n\nconsole.log(bar);\n`,
+ "/bar.ts": `export const bar = "bar";\n`,
+ });
+ assert(diagnostics == null);
+ assert(actual);
+ assertEquals(Object.keys(actual), [
+ "/bar.js.map",
+ "/bar.js",
+ "/foo.js.map",
+ "/foo.js",
+ ]);
+ },
});
-Deno.test("compilerApiCompileNoSources", async function () {
- const [diagnostics, actual] = await Deno.compile("./subdir/mod1.ts");
- assert(diagnostics == null);
- assert(actual);
- const keys = Object.keys(actual);
- assertEquals(keys.length, 6);
- assert(keys[0].endsWith("print_hello.js.map"));
- assert(keys[1].endsWith("print_hello.js"));
+Deno.test({
+ name: "Deno.compile() - no sources provided",
+ async fn() {
+ const [diagnostics, actual] = await Deno.compile("./subdir/mod1.ts");
+ assert(diagnostics == null);
+ assert(actual);
+ const keys = Object.keys(actual);
+ assertEquals(keys.length, 6);
+ assert(keys[0].endsWith("print_hello.js.map"));
+ assert(keys[1].endsWith("print_hello.js"));
+ },
});
-Deno.test("compilerApiCompileOptions", async function () {
- const [diagnostics, actual] = await Deno.compile(
- "/foo.ts",
- {
- "/foo.ts": `export const foo = "foo";`,
- },
- {
- module: "amd",
- sourceMap: false,
- }
- );
- assert(diagnostics == null);
- assert(actual);
- assertEquals(Object.keys(actual), ["/foo.js"]);
- assert(actual["/foo.js"].startsWith("define("));
+Deno.test({
+ name: "Deno.compile() - compiler options effects imit",
+ async fn() {
+ const [diagnostics, actual] = await Deno.compile(
+ "/foo.ts",
+ {
+ "/foo.ts": `export const foo = "foo";`,
+ },
+ {
+ module: "amd",
+ sourceMap: false,
+ }
+ );
+ assert(diagnostics == null);
+ assert(actual);
+ assertEquals(Object.keys(actual), ["/foo.js"]);
+ assert(actual["/foo.js"].startsWith("define("));
+ },
});
-Deno.test("compilerApiCompileLib", async function () {
- const [diagnostics, actual] = await Deno.compile(
- "/foo.ts",
- {
- "/foo.ts": `console.log(document.getElementById("foo"));
+Deno.test({
+ name: "Deno.compile() - pass lib in compiler options",
+ async fn() {
+ const [diagnostics, actual] = await Deno.compile(
+ "/foo.ts",
+ {
+ "/foo.ts": `console.log(document.getElementById("foo"));
console.log(Deno.args);`,
- },
- {
- lib: ["dom", "es2018", "deno.ns"],
- }
- );
- assert(diagnostics == null);
- assert(actual);
- assertEquals(Object.keys(actual), ["/foo.js.map", "/foo.js"]);
+ },
+ {
+ lib: ["dom", "es2018", "deno.ns"],
+ }
+ );
+ assert(diagnostics == null);
+ assert(actual);
+ assertEquals(Object.keys(actual), ["/foo.js.map", "/foo.js"]);
+ },
});
-Deno.test("compilerApiCompileTypes", async function () {
- const [diagnostics, actual] = await Deno.compile(
- "/foo.ts",
- {
- "/foo.ts": `console.log(Foo.bar);`,
- },
- {
- types: ["./subdir/foo_types.d.ts"],
- }
- );
- assert(diagnostics == null);
- assert(actual);
- assertEquals(Object.keys(actual), ["/foo.js.map", "/foo.js"]);
+Deno.test({
+ name: "Deno.compile() - properly handles .d.ts files",
+ async fn() {
+ const [diagnostics, actual] = await Deno.compile(
+ "/foo.ts",
+ {
+ "/foo.ts": `console.log(Foo.bar);`,
+ },
+ {
+ types: ["./subdir/foo_types.d.ts"],
+ }
+ );
+ assert(diagnostics == null);
+ assert(actual);
+ assertEquals(Object.keys(actual), ["/foo.js.map", "/foo.js"]);
+ },
});
-Deno.test("transpileOnlyApi", async function () {
- const actual = await Deno.transpileOnly({
- "foo.ts": `export enum Foo { Foo, Bar, Baz };\n`,
- });
- assert(actual);
- assertEquals(Object.keys(actual), ["foo.ts"]);
- assert(actual["foo.ts"].source.startsWith("export var Foo;"));
- assert(actual["foo.ts"].map);
+Deno.test({
+ name: "Deno.transpileOnly()",
+ async fn() {
+ const actual = await Deno.transpileOnly({
+ "foo.ts": `export enum Foo { Foo, Bar, Baz };\n`,
+ });
+ assert(actual);
+ assertEquals(Object.keys(actual), ["foo.ts"]);
+ assert(actual["foo.ts"].source.startsWith("export var Foo;"));
+ assert(actual["foo.ts"].map);
+ },
});
-Deno.test("transpileOnlyApiConfig", async function () {
- const actual = await Deno.transpileOnly(
- {
- "foo.ts": `export enum Foo { Foo, Bar, Baz };\n`,
- },
- {
- sourceMap: false,
- module: "amd",
- }
- );
- assert(actual);
- assertEquals(Object.keys(actual), ["foo.ts"]);
- assert(actual["foo.ts"].source.startsWith("define("));
- assert(actual["foo.ts"].map == null);
+Deno.test({
+ name: "Deno.transpileOnly() - config effects commit",
+ async fn() {
+ const actual = await Deno.transpileOnly(
+ {
+ "foo.ts": `export enum Foo { Foo, Bar, Baz };\n`,
+ },
+ {
+ sourceMap: false,
+ module: "amd",
+ }
+ );
+ assert(actual);
+ assertEquals(Object.keys(actual), ["foo.ts"]);
+ assert(actual["foo.ts"].source.startsWith("define("));
+ assert(actual["foo.ts"].map == null);
+ },
});
-Deno.test("bundleApiSources", async function () {
- const [diagnostics, actual] = await Deno.bundle("/foo.ts", {
- "/foo.ts": `export * from "./bar.ts";\n`,
- "/bar.ts": `export const bar = "bar";\n`,
- });
- assert(diagnostics == null);
- assert(actual.includes(`__instantiate("foo")`));
- assert(actual.includes(`__exp["bar"]`));
+Deno.test({
+ name: "Deno.bundle() - sources passed",
+ async fn() {
+ const [diagnostics, actual] = await Deno.bundle("/foo.ts", {
+ "/foo.ts": `export * from "./bar.ts";\n`,
+ "/bar.ts": `export const bar = "bar";\n`,
+ });
+ assert(diagnostics == null);
+ assert(actual.includes(`__instantiate("foo", false)`));
+ assert(actual.includes(`__exp["bar"]`));
+ },
});
-Deno.test("bundleApiNoSources", async function () {
- const [diagnostics, actual] = await Deno.bundle("./subdir/mod1.ts");
- assert(diagnostics == null);
- assert(actual.includes(`__instantiate("mod1")`));
- assert(actual.includes(`__exp["printHello3"]`));
+Deno.test({
+ name: "Deno.bundle() - no sources passed",
+ async fn() {
+ const [diagnostics, actual] = await Deno.bundle("./subdir/mod1.ts");
+ assert(diagnostics == null);
+ assert(actual.includes(`__instantiate("mod1", false)`));
+ assert(actual.includes(`__exp["printHello3"]`));
+ },
});
-Deno.test("bundleApiConfig", async function () {
- const [diagnostics, actual] = await Deno.bundle(
- "/foo.ts",
- {
- "/foo.ts": `// random comment\nexport * from "./bar.ts";\n`,
- "/bar.ts": `export const bar = "bar";\n`,
- },
- {
- removeComments: true,
- }
- );
- assert(diagnostics == null);
- assert(!actual.includes(`random`));
+Deno.test({
+ name: "Deno.bundle() - compiler config effects emit",
+ async fn() {
+ const [diagnostics, actual] = await Deno.bundle(
+ "/foo.ts",
+ {
+ "/foo.ts": `// random comment\nexport * from "./bar.ts";\n`,
+ "/bar.ts": `export const bar = "bar";\n`,
+ },
+ {
+ removeComments: true,
+ }
+ );
+ assert(diagnostics == null);
+ assert(!actual.includes(`random`));
+ },
+});
+
+Deno.test({
+ name: "Deno.bundle() - JS Modules included",
+ async fn() {
+ const [diagnostics, actual] = await Deno.bundle("/foo.js", {
+ "/foo.js": `export * from "./bar.js";\n`,
+ "/bar.js": `export const bar = "bar";\n`,
+ });
+ assert(diagnostics == null);
+ assert(actual.includes(`System.register("bar",`));
+ },
});
-Deno.test("bundleApiJsModules", async function () {
- const [diagnostics, actual] = await Deno.bundle("/foo.js", {
- "/foo.js": `export * from "./bar.js";\n`,
- "/bar.js": `export const bar = "bar";\n`,
- });
- assert(diagnostics == null);
- assert(actual.includes(`System.register("bar",`));
+Deno.test({
+ name: "Deno.bundle - pre ES2017 uses ES5 loader",
+ async fn() {
+ const [diagnostics, actual] = await Deno.bundle(
+ "/foo.ts",
+ {
+ "/foo.ts": `console.log("hello world!")\n`,
+ },
+ { target: "es2015" }
+ );
+ assert(diagnostics == null);
+ assert(actual.includes(`var __awaiter = `));
+ },
});
-Deno.test("diagnosticsTest", async function () {
- const [diagnostics] = await Deno.compile("/foo.ts", {
- "/foo.ts": `document.getElementById("foo");`,
- });
- assert(Array.isArray(diagnostics));
- assert(diagnostics.length === 1);
+Deno.test({
+ name: "runtime compiler APIs diagnostics",
+ async fn() {
+ const [diagnostics] = await Deno.compile("/foo.ts", {
+ "/foo.ts": `document.getElementById("foo");`,
+ });
+ assert(Array.isArray(diagnostics));
+ assert(diagnostics.length === 1);
+ },
});