mirror of
https://github.com/denoland/deno.git
synced 2025-08-03 02:22:40 +00:00
Runtime Compiler API (#3442)
Also restructures the compiler TypeScript files to make them easier to manage and eventually integrate deno_typescript fully.
This commit is contained in:
parent
cbdf9c5009
commit
d325566a7e
26 changed files with 2704 additions and 877 deletions
168
cli/js/compiler_sourcefile.ts
Normal file
168
cli/js/compiler_sourcefile.ts
Normal file
|
@ -0,0 +1,168 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import {
|
||||
getMappedModuleName,
|
||||
parseTypeDirectives
|
||||
} from "./compiler_type_directives.ts";
|
||||
import { assert, log } from "./util.ts";
|
||||
|
||||
// Warning! The values in this enum are duplicated in `cli/msg.rs`
|
||||
// Update carefully!
|
||||
export enum MediaType {
|
||||
JavaScript = 0,
|
||||
JSX = 1,
|
||||
TypeScript = 2,
|
||||
TSX = 3,
|
||||
Json = 4,
|
||||
Wasm = 5,
|
||||
Unknown = 6
|
||||
}
|
||||
|
||||
/** The shape of the SourceFile that comes from the privileged side */
|
||||
export interface SourceFileJson {
|
||||
url: string;
|
||||
filename: string;
|
||||
mediaType: MediaType;
|
||||
sourceCode: string;
|
||||
}
|
||||
|
||||
/** Returns the TypeScript Extension enum for a given media type. */
|
||||
function getExtension(fileName: string, mediaType: MediaType): ts.Extension {
|
||||
switch (mediaType) {
|
||||
case MediaType.JavaScript:
|
||||
return ts.Extension.Js;
|
||||
case MediaType.JSX:
|
||||
return ts.Extension.Jsx;
|
||||
case MediaType.TypeScript:
|
||||
return fileName.endsWith(".d.ts") ? ts.Extension.Dts : ts.Extension.Ts;
|
||||
case MediaType.TSX:
|
||||
return ts.Extension.Tsx;
|
||||
case MediaType.Json:
|
||||
return ts.Extension.Json;
|
||||
case MediaType.Wasm:
|
||||
// Custom marker for Wasm type.
|
||||
return ts.Extension.Js;
|
||||
case MediaType.Unknown:
|
||||
default:
|
||||
throw TypeError("Cannot resolve extension.");
|
||||
}
|
||||
}
|
||||
|
||||
/** A self registering abstraction of source files. */
|
||||
export class SourceFile {
|
||||
extension!: ts.Extension;
|
||||
filename!: string;
|
||||
|
||||
/** An array of tuples which represent the imports for the source file. The
|
||||
* first element is the one that will be requested at compile time, the
|
||||
* second is the one that should be actually resolved. This provides the
|
||||
* feature of type directives for Deno. */
|
||||
importedFiles?: Array<[string, string]>;
|
||||
|
||||
mediaType!: MediaType;
|
||||
processed = false;
|
||||
sourceCode!: string;
|
||||
tsSourceFile?: ts.SourceFile;
|
||||
url!: string;
|
||||
|
||||
constructor(json: SourceFileJson) {
|
||||
if (SourceFile._moduleCache.has(json.url)) {
|
||||
throw new TypeError("SourceFile already exists");
|
||||
}
|
||||
Object.assign(this, json);
|
||||
this.extension = getExtension(this.url, this.mediaType);
|
||||
SourceFile._moduleCache.set(this.url, this);
|
||||
}
|
||||
|
||||
/** Cache the source file to be able to be retrieved by `moduleSpecifier` and
|
||||
* `containingFile`. */
|
||||
cache(moduleSpecifier: string, containingFile?: string): void {
|
||||
containingFile = containingFile || "";
|
||||
let innerCache = SourceFile._specifierCache.get(containingFile);
|
||||
if (!innerCache) {
|
||||
innerCache = new Map();
|
||||
SourceFile._specifierCache.set(containingFile, innerCache);
|
||||
}
|
||||
innerCache.set(moduleSpecifier, this);
|
||||
}
|
||||
|
||||
/** Process the imports for the file and return them. */
|
||||
imports(): Array<[string, string]> {
|
||||
if (this.processed) {
|
||||
throw new Error("SourceFile has already been processed.");
|
||||
}
|
||||
assert(this.sourceCode != null);
|
||||
// we shouldn't process imports for files which contain the nocheck pragma
|
||||
// (like bundles)
|
||||
if (this.sourceCode.match(/\/{2}\s+@ts-nocheck/)) {
|
||||
log(`Skipping imports for "${this.filename}"`);
|
||||
return [];
|
||||
}
|
||||
const preProcessedFileInfo = ts.preProcessFile(this.sourceCode, true, true);
|
||||
this.processed = true;
|
||||
const files = (this.importedFiles = [] as Array<[string, string]>);
|
||||
|
||||
function process(references: ts.FileReference[]): void {
|
||||
for (const { fileName } of references) {
|
||||
files.push([fileName, fileName]);
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
importedFiles,
|
||||
referencedFiles,
|
||||
libReferenceDirectives,
|
||||
typeReferenceDirectives
|
||||
} = preProcessedFileInfo;
|
||||
const typeDirectives = parseTypeDirectives(this.sourceCode);
|
||||
if (typeDirectives) {
|
||||
for (const importedFile of importedFiles) {
|
||||
files.push([
|
||||
importedFile.fileName,
|
||||
getMappedModuleName(importedFile, typeDirectives)
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
process(importedFiles);
|
||||
}
|
||||
process(referencedFiles);
|
||||
process(libReferenceDirectives);
|
||||
process(typeReferenceDirectives);
|
||||
return files;
|
||||
}
|
||||
|
||||
/** A cache of all the source files which have been loaded indexed by the
|
||||
* url. */
|
||||
private static _moduleCache: Map<string, SourceFile> = new Map();
|
||||
|
||||
/** A cache of source files based on module specifiers and containing files
|
||||
* which is used by the TypeScript compiler to resolve the url */
|
||||
private static _specifierCache: Map<
|
||||
string,
|
||||
Map<string, SourceFile>
|
||||
> = new Map();
|
||||
|
||||
/** Retrieve a `SourceFile` based on a `moduleSpecifier` and `containingFile`
|
||||
* or return `undefined` if not preset. */
|
||||
static getUrl(
|
||||
moduleSpecifier: string,
|
||||
containingFile: string
|
||||
): string | undefined {
|
||||
const containingCache = this._specifierCache.get(containingFile);
|
||||
if (containingCache) {
|
||||
const sourceFile = containingCache.get(moduleSpecifier);
|
||||
return sourceFile && sourceFile.url;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Retrieve a `SourceFile` based on a `url` */
|
||||
static get(url: string): SourceFile | undefined {
|
||||
return this._moduleCache.get(url);
|
||||
}
|
||||
|
||||
/** Determine if a source file exists or not */
|
||||
static has(url: string): boolean {
|
||||
return this._moduleCache.has(url);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue