summaryrefslogtreecommitdiff
path: root/cli/js/compiler.ts
diff options
context:
space:
mode:
Diffstat (limited to 'cli/js/compiler.ts')
-rw-r--r--cli/js/compiler.ts109
1 files changed, 84 insertions, 25 deletions
diff --git a/cli/js/compiler.ts b/cli/js/compiler.ts
index e4953cee2..179f2af6b 100644
--- a/cli/js/compiler.ts
+++ b/cli/js/compiler.ts
@@ -31,6 +31,13 @@ enum MediaType {
Unknown = 5
}
+// Warning! The values in this enum are duplicated in cli/msg.rs
+// Update carefully!
+enum CompilerRequestType {
+ Compile = 0,
+ Bundle = 1
+}
+
// Startup boilerplate. This is necessary because the compiler has its own
// snapshot. (It would be great if we could remove these things or centralize
// them somewhere else.)
@@ -44,16 +51,23 @@ window["denoMain"] = denoMain;
const ASSETS = "$asset$";
const OUT_DIR = "$deno$";
+const BUNDLE_LOADER = "bundle_loader.js";
/** The format of the work message payload coming from the privileged side */
-interface CompilerReq {
+type CompilerRequest = {
rootNames: string[];
- bundle?: string;
// TODO(ry) add compiler config to this interface.
// options: ts.CompilerOptions;
configPath?: string;
config?: string;
-}
+} & (
+ | {
+ type: CompilerRequestType.Compile;
+ }
+ | {
+ type: CompilerRequestType.Bundle;
+ outFile?: string;
+ });
interface ConfigureResponse {
ignoredOptions?: string[];
@@ -271,7 +285,7 @@ function fetchSourceFiles(
async function processImports(
specifiers: Array<[string, string]>,
referrer = ""
-): Promise<void> {
+): Promise<SourceFileJson[]> {
if (!specifiers.length) {
return;
}
@@ -287,6 +301,7 @@ async function processImports(
await processImports(sourceFile.imports(), sourceFile.url);
}
}
+ return sourceFiles;
}
/** Utility function to turn the number of bytes into a human readable
@@ -314,16 +329,36 @@ function cache(extension: string, moduleId: string, contents: string): void {
const encoder = new TextEncoder();
/** Given a fileName and the data, emit the file to the file system. */
-function emitBundle(fileName: string, data: string): void {
+function emitBundle(
+ rootNames: string[],
+ fileName: string | undefined,
+ data: string,
+ sourceFiles: readonly ts.SourceFile[]
+): void {
// For internal purposes, when trying to emit to `$deno$` just no-op
- if (fileName.startsWith("$deno$")) {
+ if (fileName && fileName.startsWith("$deno$")) {
console.warn("skipping emitBundle", fileName);
return;
}
- const encodedData = encoder.encode(data);
- console.log(`Emitting bundle to "${fileName}"`);
- writeFileSync(fileName, encodedData);
- console.log(`${humanFileSize(encodedData.length)} emitted.`);
+ const loader = fetchAsset(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 = util.commonPath(sources);
+ rootNames = rootNames.map(id =>
+ id.replace(sharedPath, "").replace(/\.\w+$/i, "")
+ );
+ const instantiate = `instantiate(${JSON.stringify(rootNames)});\n`;
+ const bundle = `${loader}\n${data}\n${instantiate}`;
+ if (fileName) {
+ const encodedData = encoder.encode(bundle);
+ console.warn(`Emitting bundle to "${fileName}"`);
+ writeFileSync(fileName, encodedData);
+ console.warn(`${humanFileSize(encodedData.length)} emitted.`);
+ } else {
+ console.log(bundle);
+ }
}
/** Returns the TypeScript Extension enum for a given media type. */
@@ -380,17 +415,23 @@ class Host implements ts.CompilerHost {
/** Provides the `ts.HostCompiler` interface for Deno.
*
+ * @param _rootNames A set of modules that are the ones that should be
+ * instantiated first. Used when generating a bundle.
* @param _bundle Set to a string value to configure the host to write out a
* bundle instead of caching individual files.
*/
- constructor(private _bundle?: string) {
- if (this._bundle) {
+ constructor(
+ private _requestType: CompilerRequestType,
+ private _rootNames: string[],
+ private _outFile?: string
+ ) {
+ if (this._requestType === CompilerRequestType.Bundle) {
// options we need to change when we are generating a bundle
const bundlerOptions: ts.CompilerOptions = {
module: ts.ModuleKind.AMD,
- inlineSourceMap: true,
outDir: undefined,
outFile: `${OUT_DIR}/bundle.js`,
+ // disabled until we have effective way to modify source maps
sourceMap: false
};
Object.assign(this._options, bundlerOptions);
@@ -531,10 +572,11 @@ class Host implements ts.CompilerHost {
): void {
util.log("compiler::host.writeFile", fileName);
try {
- if (this._bundle) {
- emitBundle(this._bundle, data);
+ assert(sourceFiles != null);
+ if (this._requestType === CompilerRequestType.Bundle) {
+ emitBundle(this._rootNames, this._outFile, data, sourceFiles!);
} else {
- assert(sourceFiles != null && sourceFiles.length == 1);
+ assert(sourceFiles.length == 1);
const url = sourceFiles![0].fileName;
const sourceFile = SourceFile.get(url);
@@ -579,16 +621,29 @@ class Host implements ts.CompilerHost {
// lazy instantiating the compiler web worker
window.compilerMain = function compilerMain(): void {
// workerMain should have already been called since a compiler is a worker.
- window.onmessage = async ({ data }: { data: CompilerReq }): Promise<void> => {
- const { rootNames, configPath, config, bundle } = data;
- util.log(">>> compile start", { rootNames, bundle });
+ window.onmessage = async ({
+ data: request
+ }: {
+ data: CompilerRequest;
+ }): Promise<void> => {
+ const { rootNames, configPath, config } = request;
+ util.log(">>> compile start", {
+ rootNames,
+ type: CompilerRequestType[request.type]
+ });
// This will recursively analyse all the code for other imports, requesting
// those from the privileged side, populating the in memory cache which
// will be used by the host, before resolving.
- await processImports(rootNames.map(rootName => [rootName, rootName]));
-
- const host = new Host(bundle);
+ const resolvedRootModules = (await processImports(
+ rootNames.map(rootName => [rootName, rootName])
+ )).map(info => info.url);
+
+ const host = new Host(
+ request.type,
+ resolvedRootModules,
+ request.type === CompilerRequestType.Bundle ? request.outFile : undefined
+ );
let emitSkipped = true;
let diagnostics: ts.Diagnostic[] | undefined;
@@ -642,8 +697,9 @@ window.compilerMain = function compilerMain(): void {
// We will only proceed with the emit if there are no diagnostics.
if (diagnostics && diagnostics.length === 0) {
- if (bundle) {
- console.log(`Bundling "${bundle}"`);
+ if (request.type === CompilerRequestType.Bundle) {
+ // warning so it goes to stderr instead of stdout
+ console.warn(`Bundling "${resolvedRootModules.join(`", "`)}"`);
}
const emitResult = program.emit();
emitSkipped = emitResult.emitSkipped;
@@ -662,7 +718,10 @@ window.compilerMain = function compilerMain(): void {
postMessage(result);
- util.log("<<< compile end", { rootNames, bundle });
+ util.log("<<< compile end", {
+ rootNames,
+ type: CompilerRequestType[request.type]
+ });
// The compiler isolate exits after a single message.
workerClose();