Skip to content

Commit

Permalink
[WIP] Restructuring of compiler modules
Browse files Browse the repository at this point in the history
  • Loading branch information
kitsonk committed Dec 11, 2019
1 parent 8553ee8 commit 0047cc9
Show file tree
Hide file tree
Showing 13 changed files with 1,113 additions and 984 deletions.
1,039 changes: 70 additions & 969 deletions cli/js/compiler.ts

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions cli/js/compiler_api_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ const {

test(async function compilerApiCompileSources() {
const [diagnostics, actual] = await compile("/foo.ts", {
"/foo.ts": `console.log("foo");\nexport {}\n`
"/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).length, 2);
assertEquals(Object.keys(actual).length, 4);
});
7 changes: 6 additions & 1 deletion cli/js/bundler.ts → cli/js/compiler_bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ import { assert, commonPath } 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;

/** Given a fileName and the data, emit the file to the file system. */
/** 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 });
}
Expand Down
302 changes: 302 additions & 0 deletions cli/js/compiler_host.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.

import { MediaType, SourceFile } from "./compiler_sourcefile.ts";
import { OUT_DIR, WriteFileCallback } from "./compiler_util.ts";
import { cwd } from "./dir.ts";
import { sendSync } from "./dispatch_json.ts";
import * as dispatch from "./dispatch.ts";
import { assert, notImplemented } from "./util.ts";
import * as util from "./util.ts";

export interface CompilerHostOptions {
bundle?: boolean;
writeFile: WriteFileCallback;
}

export interface ConfigureResponse {
ignoredOptions?: string[];
diagnostics?: ts.Diagnostic[];
}

const ASSETS = "$asset$";

/** Options that need to be used when generating a bundle (either trusted or
* runtime). */
export const defaultBundlerOptions: ts.CompilerOptions = {
inlineSourceMap: false,
module: ts.ModuleKind.AMD,
outDir: undefined,
outFile: `${OUT_DIR}/bundle.js`,
// disabled until we have effective way to modify source maps
sourceMap: false
};

/** Default options used by the compiler Host when compiling. */
export const defaultCompileOptions: ts.CompilerOptions = {
allowJs: true,
allowNonTsExtensions: true,
// TODO(#3324) Enable strict mode for user code.
// strict: true,
checkJs: false,
esModuleInterop: true,
module: ts.ModuleKind.ESNext,
outDir: OUT_DIR,
resolveJsonModule: true,
sourceMap: true,
stripComments: true,
target: ts.ScriptTarget.ESNext,
jsx: ts.JsxEmit.React
};

/** Options that need to be used when doing a runtime (non bundled) compilation */
export const defaultRuntimeCompileOptions: ts.CompilerOptions = {
outDir: undefined
};

/** Default options used when doing a transpile only. */
export const defaultTranspileOptions: ts.CompilerOptions = {
esModuleInterop: true,
module: ts.ModuleKind.ESNext,
sourceMap: true,
scriptComments: true,
target: ts.ScriptTarget.ESNext
};

/** Options that either do nothing in Deno, or would cause undesired behavior
* if modified. */
const ignoredCompilerOptions: readonly string[] = [
"allowSyntheticDefaultImports",
"baseUrl",
"build",
"composite",
"declaration",
"declarationDir",
"declarationMap",
"diagnostics",
"downlevelIteration",
"emitBOM",
"emitDeclarationOnly",
"esModuleInterop",
"extendedDiagnostics",
"forceConsistentCasingInFileNames",
"help",
"importHelpers",
"incremental",
"inlineSourceMap",
"inlineSources",
"init",
"isolatedModules",
"lib",
"listEmittedFiles",
"listFiles",
"mapRoot",
"maxNodeModuleJsDepth",
"module",
"moduleResolution",
"newLine",
"noEmit",
"noEmitHelpers",
"noEmitOnError",
"noLib",
"noResolve",
"out",
"outDir",
"outFile",
"paths",
"preserveSymlinks",
"preserveWatchOutput",
"pretty",
"rootDir",
"rootDirs",
"showConfig",
"skipDefaultLibCheck",
"skipLibCheck",
"sourceMap",
"sourceRoot",
"stripInternal",
"target",
"traceResolution",
"tsBuildInfoFile",
"types",
"typeRoots",
"version",
"watch"
];

export class Host implements ts.CompilerHost {
private readonly _options = defaultCompileOptions;

private _writeFile: WriteFileCallback;

private _getAsset(filename: string): SourceFile {
const sourceFile = SourceFile.get(filename);
if (sourceFile) {
return sourceFile;
}
const url = filename.split("/").pop()!;
const name = url.includes(".") ? url : `${url}.d.ts`;
const sourceCode = sendSync(dispatch.OP_FETCH_ASSET, { name });
return new SourceFile({
url,
filename,
mediaType: MediaType.TypeScript,
sourceCode
});
}

/* Deno specific APIs */

/** Provides the `ts.HostCompiler` interface for Deno. */
constructor(options: CompilerHostOptions) {
const { bundle = false, writeFile } = options;
this._writeFile = writeFile;
if (bundle) {
// options we need to change when we are generating a bundle
Object.assign(this._options, defaultBundlerOptions);
}
}

/** Take a configuration string, parse it, and use it to merge with the
* compiler's configuration options. The method returns an array of compiler
* options which were ignored, or `undefined`. */
configure(path: string, configurationText: string): ConfigureResponse {
util.log("compiler::host.configure", path);
assert(configurationText);
const { config, error } = ts.parseConfigFileTextToJson(
path,
configurationText
);
if (error) {
return { diagnostics: [error] };
}
const { options, errors } = ts.convertCompilerOptionsFromJson(
config.compilerOptions,
cwd()
);
const ignoredOptions: string[] = [];
for (const key of Object.keys(options)) {
if (
ignoredCompilerOptions.includes(key) &&
(!(key in this._options) || options[key] !== this._options[key])
) {
ignoredOptions.push(key);
delete options[key];
}
}
Object.assign(this._options, options);
return {
ignoredOptions: ignoredOptions.length ? ignoredOptions : undefined,
diagnostics: errors.length ? errors : undefined
};
}

/** Merge options into the host's current set of compiler options and return
* the merged set. */
mergeOptions(...options: ts.CompilerOptions[]): ts.CompilerOptions {
Object.assign(this._options, ...options);
return Object.assign({}, this._options);
}

/* TypeScript CompilerHost APIs */

fileExists(_fileName: string): boolean {
return notImplemented();
}

getCanonicalFileName(fileName: string): string {
return fileName;
}

getCompilationSettings(): ts.CompilerOptions {
util.log("compiler::host.getCompilationSettings()");
return this._options;
}

getCurrentDirectory(): string {
return "";
}

getDefaultLibFileName(_options: ts.CompilerOptions): string {
return ASSETS + "/lib.deno_runtime.d.ts";
}

getNewLine(): string {
return "\n";
}

getSourceFile(
fileName: string,
languageVersion: ts.ScriptTarget,
onError?: (message: string) => void,
shouldCreateNewSourceFile?: boolean
): ts.SourceFile | undefined {
util.log("compiler::host.getSourceFile", fileName);
try {
assert(!shouldCreateNewSourceFile);
const sourceFile = fileName.startsWith(ASSETS)
? this._getAsset(fileName)
: SourceFile.get(fileName);
assert(sourceFile != null);
if (!sourceFile.tsSourceFile) {
sourceFile.tsSourceFile = ts.createSourceFile(
fileName,
sourceFile.sourceCode,
languageVersion
);
}
return sourceFile!.tsSourceFile;
} catch (e) {
if (onError) {
onError(String(e));
} else {
throw e;
}
return undefined;
}
}

readFile(_fileName: string): string | undefined {
return notImplemented();
}

resolveModuleNames(
moduleNames: string[],
containingFile: string
): Array<ts.ResolvedModuleFull | undefined> {
util.log("compiler::host.resolveModuleNames", {
moduleNames,
containingFile
});
return moduleNames.map(specifier => {
const url = SourceFile.getUrl(specifier, containingFile);
const sourceFile = specifier.startsWith(ASSETS)
? this._getAsset(specifier)
: url
? SourceFile.get(url)
: undefined;
if (!sourceFile) {
return undefined;
}
return {
resolvedFileName: sourceFile.url,
isExternalLibraryImport: specifier.startsWith(ASSETS),
extension: sourceFile.extension
};
});
}

useCaseSensitiveFileNames(): boolean {
return true;
}

writeFile(
fileName: string,
data: string,
_writeByteOrderMark: boolean,
_onError?: (message: string) => void,
sourceFiles?: readonly ts.SourceFile[]
): void {
util.log("compiler::host.writeFile", fileName);
this._writeFile(fileName, data, sourceFiles);
}
}
Loading

0 comments on commit 0047cc9

Please sign in to comment.