mirror of
https://github.com/denoland/deno.git
synced 2024-11-01 09:24:20 -04:00
83d902a780
Fixes #4602 We turned off `allowJs` by default, to keep the compiler from grabbing a bunch of files that it wouldn't actually do anything useful with. On the other hand, this caused problems with bundles, where the compiler needs to gather all the dependencies, including JavaScript ones. This fixes this so that when we are bundling, we analyse JavaScript imports in the compiler.
330 lines
8.8 KiB
TypeScript
330 lines
8.8 KiB
TypeScript
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
|
|
import { ASSETS, MediaType, SourceFile } from "./compiler_sourcefile.ts";
|
|
import { OUT_DIR, WriteFileCallback, getAsset } from "./compiler_util.ts";
|
|
import { cwd } from "./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(options: CompilerHostOptions) {
|
|
const { bundle = false, target, writeFile } = options;
|
|
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);
|
|
}
|
|
}
|