summaryrefslogtreecommitdiff
path: root/cli/js/compiler_bundler.ts
diff options
context:
space:
mode:
authorKitson Kelly <me@kitsonkelly.com>2020-01-09 01:17:44 +1100
committerRy Dahl <ry@tinyclouds.org>2020-01-08 09:17:44 -0500
commitd325566a7e2d736870990835dfe076a30a1b26ab (patch)
treefed5de4826b04140922c2573bdad2e8c6fd2445e /cli/js/compiler_bundler.ts
parentcbdf9c50095b86e72a8e0e715a02f6eb327f7c53 (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.ts109
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());
+}