feat: add --no-check option (#6456)

This commit adds a "--no-check" option to following subcommands:
- "deno cache"
- "deno info"
- "deno run"
- "deno test"

The "--no-check" options allows to skip type checking step and instead 
directly transpiles TS sources to JS sources. 

This solution uses `ts.transpileModule()` API and is just an interim
solution before implementing it fully in Rust.
This commit is contained in:
Kitson Kelly 2020-07-08 19:26:39 +10:00 committed by GitHub
parent 862bc2ecae
commit 82aabb657a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
78 changed files with 567 additions and 200 deletions

View file

@ -14,10 +14,10 @@
import "./ts_global.d.ts";
import { bold, cyan, yellow } from "./colors.ts";
import { CompilerOptions } from "./compiler_options.ts";
import { Diagnostic, DiagnosticItem } from "./diagnostics.ts";
import type { CompilerOptions } from "./compiler_options.ts";
import type { Diagnostic, DiagnosticItem } from "./diagnostics.ts";
import { fromTypeScriptDiagnostic } from "./diagnostics_util.ts";
import { TranspileOnlyResult } from "./ops/runtime_compiler.ts";
import type { TranspileOnlyResult } from "./ops/runtime_compiler.ts";
import { bootstrapWorkerRuntime } from "./runtime_worker.ts";
import { assert, log, notImplemented } from "./util.ts";
import { core } from "./core.ts";
@ -47,6 +47,8 @@ const TS_BUILD_INFO = "cache:///tsbuildinfo.json";
// TODO(Bartlomieju): this check should be done in Rust
const IGNORED_COMPILER_OPTIONS: readonly string[] = [
"allowSyntheticDefaultImports",
"allowUmdGlobalAccess",
"assumeChangesOnlyAffectDirectDependencies",
"baseUrl",
"build",
"composite",
@ -60,13 +62,13 @@ const IGNORED_COMPILER_OPTIONS: readonly string[] = [
"esModuleInterop",
"extendedDiagnostics",
"forceConsistentCasingInFileNames",
"generateCpuProfile",
"help",
"importHelpers",
"incremental",
"inlineSourceMap",
"inlineSources",
"init",
"isolatedModules",
"listEmittedFiles",
"listFiles",
"mapRoot",
@ -139,10 +141,18 @@ const DEFAULT_COMPILE_OPTIONS: ts.CompilerOptions = {
jsx: ts.JsxEmit.React,
module: ts.ModuleKind.ESNext,
outDir: OUT_DIR,
resolveJsonModule: true,
sourceMap: true,
strict: true,
stripComments: true,
removeComments: true,
target: ts.ScriptTarget.ESNext,
};
const DEFAULT_TRANSPILE_OPTIONS: ts.CompilerOptions = {
esModuleInterop: true,
inlineSourceMap: true,
jsx: ts.JsxEmit.React,
module: ts.ModuleKind.ESNext,
removeComments: true,
target: ts.ScriptTarget.ESNext,
};
@ -172,16 +182,23 @@ interface CompilerHostOptions {
incremental?: boolean;
}
interface IncrementalCompilerHostOptions extends CompilerHostOptions {
type IncrementalCompilerHostOptions = Omit<
CompilerHostOptions,
"incremental"
> & {
rootNames?: string[];
buildInfo?: string;
}
};
interface ConfigureResponse {
interface HostConfigureResponse {
ignoredOptions?: string[];
diagnostics?: ts.Diagnostic[];
}
interface ConfigureResponse extends HostConfigureResponse {
options: ts.CompilerOptions;
}
// Warning! The values in this enum are duplicated in `cli/msg.rs`
// Update carefully!
enum MediaType {
@ -238,6 +255,37 @@ const SOURCE_FILE_CACHE: Map<string, SourceFile> = new Map();
*/
const RESOLVED_SPECIFIER_CACHE: Map<string, Map<string, string>> = new Map();
function configure(
defaultOptions: ts.CompilerOptions,
source: string,
path: string,
cwd: string
): ConfigureResponse {
const { config, error } = ts.parseConfigFileTextToJson(path, source);
if (error) {
return { diagnostics: [error], options: defaultOptions };
}
const { options, errors } = ts.convertCompilerOptionsFromJson(
config.compilerOptions,
cwd
);
const ignoredOptions: string[] = [];
for (const key of Object.keys(options)) {
if (
IGNORED_COMPILER_OPTIONS.includes(key) &&
(!(key in defaultOptions) || options[key] !== defaultOptions[key])
) {
ignoredOptions.push(key);
delete options[key];
}
}
return {
options: Object.assign({}, defaultOptions, options),
ignoredOptions: ignoredOptions.length ? ignoredOptions : undefined,
diagnostics: errors.length ? errors : undefined,
};
}
class SourceFile {
extension!: ts.Extension;
filename!: string;
@ -314,7 +362,7 @@ function getAssetInternal(filename: string): SourceFile {
}
class Host implements ts.CompilerHost {
protected _options = DEFAULT_COMPILE_OPTIONS;
#options = DEFAULT_COMPILE_OPTIONS;
readonly #target: CompilerHostTarget;
readonly #writeFile: WriteFileCallback;
/* Deno specific APIs */
@ -330,12 +378,12 @@ class Host implements ts.CompilerHost {
this.#writeFile = writeFile;
if (bundle) {
// options we need to change when we are generating a bundle
Object.assign(this._options, DEFAULT_BUNDLER_OPTIONS);
Object.assign(this.#options, DEFAULT_BUNDLER_OPTIONS);
} else if (incremental) {
Object.assign(this._options, DEFAULT_INCREMENTAL_COMPILE_OPTIONS);
Object.assign(this.#options, DEFAULT_INCREMENTAL_COMPILE_OPTIONS);
}
if (unstable) {
this._options.lib = [
this.#options.lib = [
target === CompilerHostTarget.Worker
? "lib.deno.worker.d.ts"
: "lib.deno.window.d.ts",
@ -345,47 +393,28 @@ class Host implements ts.CompilerHost {
}
get options(): ts.CompilerOptions {
return this._options;
return this.#options;
}
configure(
cwd: string,
path: string,
configurationText: string
): ConfigureResponse {
): HostConfigureResponse {
log("compiler::host.configure", path);
assert(configurationText);
const { config, error } = ts.parseConfigFileTextToJson(
const { options, ...result } = configure(
this.#options,
configurationText,
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 (
IGNORED_COMPILER_OPTIONS.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,
};
this.#options = options;
return result;
}
mergeOptions(...options: ts.CompilerOptions[]): ts.CompilerOptions {
Object.assign(this._options, ...options);
return Object.assign({}, this._options);
Object.assign(this.#options, ...options);
return Object.assign({}, this.#options);
}
/* TypeScript CompilerHost APIs */
@ -400,7 +429,7 @@ class Host implements ts.CompilerHost {
getCompilationSettings(): ts.CompilerOptions {
log("compiler::host.getCompilationSettings()");
return this._options;
return this.#options;
}
getCurrentDirectory(): string {
@ -522,7 +551,7 @@ class IncrementalCompileHost extends Host {
readonly #buildInfo?: string;
constructor(options: IncrementalCompilerHostOptions) {
super(options);
super({ ...options, incremental: true });
const { buildInfo } = options;
if (buildInfo) {
this.#buildInfo = buildInfo;
@ -735,10 +764,11 @@ interface BundleWriteFileState {
// Update carefully!
enum CompilerRequestType {
Compile = 0,
Bundle = 1,
RuntimeCompile = 2,
RuntimeBundle = 3,
RuntimeTranspile = 4,
Transpile = 1,
Bundle = 2,
RuntimeCompile = 3,
RuntimeBundle = 4,
RuntimeTranspile = 5,
}
function createBundleWriteFile(state: BundleWriteFileState): WriteFileCallback {
@ -943,16 +973,29 @@ function performanceStart(): void {
ts.performance.enable();
}
function performanceProgram(program: ts.Program | ts.BuilderProgram): void {
if ("getProgram" in program) {
program = program.getProgram();
function performanceProgram({
program,
fileCount,
}: {
program?: ts.Program | ts.BuilderProgram;
fileCount?: number;
}): void {
if (program) {
if ("getProgram" in program) {
program = program.getProgram();
}
stats.push({ key: "Files", value: program.getSourceFiles().length });
stats.push({ key: "Nodes", value: program.getNodeCount() });
stats.push({ key: "Identifiers", value: program.getIdentifierCount() });
stats.push({ key: "Symbols", value: program.getSymbolCount() });
stats.push({ key: "Types", value: program.getTypeCount() });
stats.push({
key: "Instantiations",
value: program.getInstantiationCount(),
});
} else if (fileCount != null) {
stats.push({ key: "Files", value: fileCount });
}
stats.push({ key: "Files", value: program.getSourceFiles().length });
stats.push({ key: "Nodes", value: program.getNodeCount() });
stats.push({ key: "Identifiers", value: program.getIdentifierCount() });
stats.push({ key: "Symbols", value: program.getSymbolCount() });
stats.push({ key: "Types", value: program.getTypeCount() });
stats.push({ key: "Instantiations", value: program.getInstantiationCount() });
const programTime = ts.performance.getDuration("Program");
const bindTime = ts.performance.getDuration("Bind");
const checkTime = ts.performance.getDuration("Check");
@ -976,7 +1019,7 @@ function performanceEnd(): Stats {
// TODO(Bartlomieju): this check should be done in Rust; there should be no
function processConfigureResponse(
configResult: ConfigureResponse,
configResult: HostConfigureResponse,
configPath: string
): ts.Diagnostic[] | undefined {
const { ignoredOptions, diagnostics } = configResult;
@ -1209,6 +1252,20 @@ interface CompileRequest {
buildInfo?: string;
}
interface TranspileRequest {
type: CompilerRequestType.Transpile;
config?: string;
configPath?: string;
cwd?: string;
performance: boolean;
sourceFiles: TranspileSourceFile[];
}
interface TranspileSourceFile {
sourceCode: string;
fileName: string;
}
/** Used when "deno bundle" is invoked */
interface BundleRequest {
type: CompilerRequestType.Bundle;
@ -1252,6 +1309,7 @@ interface RuntimeTranspileRequest {
type CompilerRequest =
| CompileRequest
| TranspileRequest
| BundleRequest
| RuntimeCompileRequest
| RuntimeBundleRequest
@ -1264,6 +1322,12 @@ interface CompileResponse {
stats?: Stats;
}
interface TranspileResponse {
emitMap: Record<string, EmittedSource>;
diagnostics: Diagnostic;
stats?: Stats;
}
interface BundleResponse {
bundleOutput?: string;
diagnostics: Diagnostic;
@ -1310,7 +1374,6 @@ function compile({
bundle: false,
target,
unstable,
incremental: true,
writeFile: createCompileWriteFile(state),
rootNames,
buildInfo,
@ -1364,7 +1427,7 @@ function compile({
// without casting.
diagnostics = emitResult.diagnostics;
}
performanceProgram(program);
performanceProgram({ program });
}
log("<<< compile end", { rootNames, type: CompilerRequestType[type] });
@ -1378,21 +1441,81 @@ function compile({
};
}
function transpile({
config: configText,
configPath,
cwd,
performance,
sourceFiles,
}: TranspileRequest): TranspileResponse {
if (performance) {
performanceStart();
}
log(">>> transpile start");
let compilerOptions: ts.CompilerOptions;
if (configText && configPath && cwd) {
const { options, ...response } = configure(
DEFAULT_TRANSPILE_OPTIONS,
configText,
configPath,
cwd
);
const diagnostics = processConfigureResponse(response, configPath);
if (diagnostics && diagnostics.length) {
return {
diagnostics: fromTypeScriptDiagnostic(diagnostics),
emitMap: {},
};
}
compilerOptions = options;
} else {
compilerOptions = Object.assign({}, DEFAULT_TRANSPILE_OPTIONS);
}
const emitMap: Record<string, EmittedSource> = {};
let diagnostics: ts.Diagnostic[] = [];
for (const { sourceCode, fileName } of sourceFiles) {
const {
outputText,
sourceMapText,
diagnostics: diags,
} = ts.transpileModule(sourceCode, {
fileName,
compilerOptions,
reportDiagnostics: true,
});
if (diags) {
diagnostics = diagnostics.concat(...diags);
}
emitMap[`${fileName}.js`] = { filename: fileName, contents: outputText };
// currently we inline source maps, but this is good logic to have if this
// ever changes
if (sourceMapText) {
emitMap[`${fileName}.map`] = {
filename: fileName,
contents: sourceMapText,
};
}
}
performanceProgram({ fileCount: sourceFiles.length });
const stats = performance ? performanceEnd() : undefined;
log("<<< transpile end");
return { diagnostics: fromTypeScriptDiagnostic(diagnostics), emitMap, stats };
}
function bundle({
type,
config,
configPath,
rootNames,
target,
unstable,
performance,
cwd,
sourceFileMap,
type,
}: BundleRequest): BundleResponse {
if (performance) {
performanceStart();
}
log(">>> start start", {
log(">>> bundle start", {
rootNames,
type: CompilerRequestType[type],
});
@ -1447,7 +1570,7 @@ function bundle({
diagnostics = emitResult.diagnostics;
}
if (performance) {
performanceProgram(program);
performanceProgram({ program });
}
}
@ -1655,6 +1778,11 @@ async function tsCompilerOnMessage({
globalThis.postMessage(result);
break;
}
case CompilerRequestType.Transpile: {
const result = transpile(request);
globalThis.postMessage(result);
break;
}
case CompilerRequestType.Bundle: {
const result = bundle(request);
globalThis.postMessage(result);