diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2020-03-11 10:53:06 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-11 10:53:06 +0100 |
commit | 99a0c6df79b903e4fe72ce066787039bdede3868 (patch) | |
tree | 1c0abdb964a2e052b593dc8fa3e515f76dbc0642 /cli/js/compiler/bundler.ts | |
parent | 94f4c6807a34a564f567b984e71e36e9cb9c5005 (diff) |
reorg: cli/js/compiler/, move more API to cli/js/web/ (#4310)
- moves compiler implementation to "cli/js/compiler/" directory
- moves more APIs to "cli/js/web":
* "console.ts"
* "console_table.ts"
* "performance.ts"
* "timers.ts"
* "workers.ts"
- removes some dead code from "cli/js/"
Diffstat (limited to 'cli/js/compiler/bundler.ts')
-rw-r--r-- | cli/js/compiler/bundler.ts | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/cli/js/compiler/bundler.ts b/cli/js/compiler/bundler.ts new file mode 100644 index 000000000..ab987a7fc --- /dev/null +++ b/cli/js/compiler/bundler.ts @@ -0,0 +1,103 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +import { SYSTEM_LOADER } from "./bootstrap.ts"; +import { commonPath, normalizeString, CHAR_FORWARD_SLASH } from "./util.ts"; +import { assert } from "../util.ts"; + +/** Local state of what the root exports are of a root module. */ +let rootExports: string[] | undefined; + +/** Take a URL and normalize it, resolving relative path parts. */ +function normalizeUrl(rootName: string): string { + const match = /^(\S+:\/{2,3})(.+)$/.exec(rootName); + if (match) { + const [, protocol, path] = match; + return `${protocol}${normalizeString( + path, + false, + "/", + code => code === CHAR_FORWARD_SLASH + )}`; + } else { + return rootName; + } +} + +/** Given a root name, contents, and source files, enrich the data of the + * bundle with a loader and re-export the exports of the root name. */ +export function buildBundle( + rootName: string, + data: string, + sourceFiles: readonly ts.SourceFile[] +): 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 + // publicly, so we have to try to replicate + const sources = sourceFiles.map(sf => sf.fileName); + const sharedPath = commonPath(sources); + rootName = normalizeUrl(rootName) + .replace(sharedPath, "") + .replace(/\.\w+$/i, ""); + // If one of the modules requires support for top-level-await, TypeScript will + // emit the execute function as an async function. When this is the case we + // need to bubble up the TLA to the instantiation, otherwise we instantiate + // synchronously. + const hasTla = data.match(/execute:\sasync\sfunction\s/); + let instantiate: string; + if (rootExports && rootExports.length) { + instantiate = hasTla + ? `const __exp = await __instantiateAsync("${rootName}");\n` + : `const __exp = __instantiate("${rootName}");\n`; + for (const rootExport of rootExports) { + if (rootExport === "default") { + instantiate += `export default __exp["${rootExport}"];\n`; + } else { + instantiate += `export const ${rootExport} = __exp["${rootExport}"];\n`; + } + } + } else { + instantiate = hasTla + ? `await __instantiateAsync("${rootName}");\n` + : `__instantiate("${rootName}");\n`; + } + return `${SYSTEM_LOADER}\n${data}\n${instantiate}`; +} + +/** Set the rootExports which will by the `emitBundle()` */ +export function setRootExports(program: ts.Program, rootModule: string): void { + // get a reference to the type checker, this will let us find symbols from + // the AST. + const checker = program.getTypeChecker(); + // get a reference to the main source file for the bundle + const mainSourceFile = program.getSourceFile(rootModule); + assert(mainSourceFile); + // retrieve the internal TypeScript symbol for this AST node + const mainSymbol = checker.getSymbolAtLocation(mainSourceFile); + if (!mainSymbol) { + return; + } + rootExports = checker + .getExportsOfModule(mainSymbol) + // .getExportsOfModule includes type only symbols which are exported from + // the module, so we need to try to filter those out. While not critical + // someone looking at the bundle would think there is runtime code behind + // that when there isn't. There appears to be no clean way of figuring that + // out, so inspecting SymbolFlags that might be present that are type only + .filter( + sym => + sym.flags & ts.SymbolFlags.Class || + !( + sym.flags & ts.SymbolFlags.Interface || + sym.flags & ts.SymbolFlags.TypeLiteral || + sym.flags & ts.SymbolFlags.Signature || + sym.flags & ts.SymbolFlags.TypeParameter || + sym.flags & ts.SymbolFlags.TypeAlias || + sym.flags & ts.SymbolFlags.Type || + sym.flags & ts.SymbolFlags.Namespace || + sym.flags & ts.SymbolFlags.InterfaceExcludes || + sym.flags & ts.SymbolFlags.TypeParameterExcludes || + sym.flags & ts.SymbolFlags.TypeAliasExcludes + ) + ) + .map(sym => sym.getName()); +} |