mirror of
https://github.com/denoland/deno.git
synced 2024-11-29 16:30:56 -05:00
da8cb408c8
Fixes #4101 Previously, we would just provide the raw JSON to the TypeScript compiler worker, but TypeScript does not transform JSON. This caused a problem when emitting a bundle, that the JSON would just be "inlined" into the output, instead of being transformed into a module. This fixes this problem by providing the compiled JSON to the TypeScript compiler, so TypeScript just sees JSON as a "normal" TypeScript module.
315 lines
7.9 KiB
TypeScript
315 lines
7.9 KiB
TypeScript
// 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";
|
|
|
|
export enum CompilerHostTarget {
|
|
Main = "main",
|
|
Runtime = "runtime",
|
|
Worker = "worker"
|
|
}
|
|
|
|
export interface CompilerHostOptions {
|
|
bundle?: boolean;
|
|
|
|
target: CompilerHostTarget;
|
|
|
|
writeFile: WriteFileCallback;
|
|
}
|
|
|
|
export interface ConfigureResponse {
|
|
ignoredOptions?: string[];
|
|
diagnostics?: ts.Diagnostic[];
|
|
}
|
|
|
|
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
|
|
};
|
|
|
|
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
|
|
};
|
|
|
|
export const defaultRuntimeCompileOptions: ts.CompilerOptions = {
|
|
outDir: undefined
|
|
};
|
|
|
|
export const defaultTranspileOptions: ts.CompilerOptions = {
|
|
esModuleInterop: true,
|
|
module: ts.ModuleKind.ESNext,
|
|
sourceMap: true,
|
|
scriptComments: true,
|
|
target: ts.ScriptTarget.ESNext
|
|
};
|
|
|
|
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 */
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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
|
|
};
|
|
}
|
|
|
|
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);
|
|
// even though we assert the extension for JSON modules to the compiler
|
|
// is TypeScript, TypeScript internally analyses the filename for its
|
|
// extension and tries to parse it as JSON instead of TS. We have to
|
|
// change the filename to the TypeScript file.
|
|
sourceFile.tsSourceFile = ts.createSourceFile(
|
|
fileName.startsWith(ASSETS)
|
|
? sourceFile.filename
|
|
: fileName.toLowerCase().endsWith(".json")
|
|
? `${fileName}.ts`
|
|
: 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);
|
|
}
|
|
}
|