diff options
author | Kitson Kelly <me@kitsonkelly.com> | 2020-01-09 01:17:44 +1100 |
---|---|---|
committer | Ry Dahl <ry@tinyclouds.org> | 2020-01-08 09:17:44 -0500 |
commit | d325566a7e2d736870990835dfe076a30a1b26ab (patch) | |
tree | fed5de4826b04140922c2573bdad2e8c6fd2445e /cli/js/compiler_bundler.ts | |
parent | cbdf9c50095b86e72a8e0e715a02f6eb327f7c53 (diff) |
Runtime Compiler API (#3442)
Also restructures the compiler TypeScript files to make them easier to
manage and eventually integrate deno_typescript fully.
Diffstat (limited to 'cli/js/compiler_bundler.ts')
-rw-r--r-- | cli/js/compiler_bundler.ts | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/cli/js/compiler_bundler.ts b/cli/js/compiler_bundler.ts new file mode 100644 index 000000000..a4e4557ca --- /dev/null +++ b/cli/js/compiler_bundler.ts @@ -0,0 +1,109 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +import * as dispatch from "./dispatch.ts"; +import { sendSync } from "./dispatch_json.ts"; +import { + assert, + commonPath, + normalizeString, + CHAR_FORWARD_SLASH +} from "./util.ts"; + +const BUNDLE_LOADER = "bundle_loader.js"; + +/** A loader of bundled modules that we will inline into any bundle outputs. */ +let bundleLoader: string; + +/** 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 { + // we can only do this once we are bootstrapped and easiest way to do it is + // inline here + if (!bundleLoader) { + bundleLoader = sendSync(dispatch.OP_FETCH_ASSET, { name: BUNDLE_LOADER }); + } + + // 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, ""); + let instantiate: string; + if (rootExports && rootExports.length) { + instantiate = `const __rootExports = instantiate("${rootName}");\n`; + for (const rootExport of rootExports) { + if (rootExport === "default") { + instantiate += `export default __rootExports["${rootExport}"];\n`; + } else { + instantiate += `export const ${rootExport} = __rootExports["${rootExport}"];\n`; + } + } + } else { + instantiate = `instantiate("${rootName}");\n`; + } + return `${bundleLoader}\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.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()); +} |