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/host.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/host.ts')
-rw-r--r-- | cli/js/compiler/host.ts | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/cli/js/compiler/host.ts b/cli/js/compiler/host.ts new file mode 100644 index 000000000..8032d83b3 --- /dev/null +++ b/cli/js/compiler/host.ts @@ -0,0 +1,329 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +import { ASSETS, MediaType, SourceFile } from "./sourcefile.ts"; +import { OUT_DIR, WriteFileCallback, getAsset } from "./util.ts"; +import { cwd } from "../ops/fs/dir.ts"; +import { assert, notImplemented } from "../util.ts"; +import * as util from "../util.ts"; + +/** Specifies the target that the host should use to inform the TypeScript + * compiler of what types should be used to validate the program against. */ +export enum CompilerHostTarget { + /** The main isolate library, where the main program runs. */ + Main = "main", + /** The runtime API library. */ + Runtime = "runtime", + /** The worker isolate library, where worker programs run. */ + Worker = "worker" +} + +export interface CompilerHostOptions { + /** Flag determines if the host should assume a single bundle output. */ + bundle?: boolean; + + /** Determines what the default library that should be used when type checking + * TS code. */ + target: CompilerHostTarget; + + /** A function to be used when the program emit occurs to write out files. */ + writeFile: WriteFileCallback; +} + +export interface ConfigureResponse { + ignoredOptions?: string[]; + diagnostics?: ts.Diagnostic[]; +} + +/** Options that need to be used when generating a bundle (either trusted or + * runtime). */ +export const defaultBundlerOptions: ts.CompilerOptions = { + allowJs: true, + inlineSourceMap: false, + module: ts.ModuleKind.System, + 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: false, + allowNonTsExtensions: true, + checkJs: false, + esModuleInterop: true, + jsx: ts.JsxEmit.React, + module: ts.ModuleKind.ESNext, + outDir: OUT_DIR, + resolveJsonModule: true, + sourceMap: true, + strict: true, + stripComments: true, + target: ts.ScriptTarget.ESNext +}; + +/** 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", + "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 _target: CompilerHostTarget; + + private _writeFile: WriteFileCallback; + + private _getAsset(filename: string): SourceFile { + const lastSegment = filename.split("/").pop()!; + const url = ts.libMap.has(lastSegment) + ? ts.libMap.get(lastSegment)! + : lastSegment; + const sourceFile = SourceFile.get(url); + if (sourceFile) { + return sourceFile; + } + const name = url.includes(".") ? url : `${url}.d.ts`; + const sourceCode = getAsset(name); + return new SourceFile({ + url, + filename: `${ASSETS}/${name}`, + mediaType: MediaType.TypeScript, + sourceCode + }); + } + + /* Deno specific APIs */ + + /** Provides the `ts.HostCompiler` interface for Deno. */ + constructor({ bundle = false, target, writeFile }: CompilerHostOptions) { + this._target = target; + 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 { + util.log("compiler::host.getDefaultLibFileName()"); + switch (this._target) { + case CompilerHostTarget.Main: + case CompilerHostTarget.Runtime: + return `${ASSETS}/lib.deno.window.d.ts`; + case CompilerHostTarget.Worker: + return `${ASSETS}/lib.deno.worker.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) { + assert(sourceFile.sourceCode != null); + sourceFile.tsSourceFile = ts.createSourceFile( + fileName.startsWith(ASSETS) ? sourceFile.filename : fileName, + sourceFile.sourceCode, + languageVersion + ); + delete sourceFile.sourceCode; + } + 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); + } +} |