mirror of
https://github.com/denoland/deno
synced 2024-08-24 18:47:06 +00:00
parent
c5bb412933
commit
e7cab71574
4
BUILD.gn
4
BUILD.gn
|
@ -162,6 +162,7 @@ executable("snapshot_creator") {
|
|||
run_node("gen_declarations") {
|
||||
out_dir = target_gen_dir
|
||||
sources = [
|
||||
"js/compiler.ts",
|
||||
"js/console.ts",
|
||||
"js/deno.ts",
|
||||
"js/globals.ts",
|
||||
|
@ -171,6 +172,7 @@ run_node("gen_declarations") {
|
|||
"js/util.ts",
|
||||
]
|
||||
outputs = [
|
||||
out_dir + "/js/compiler.d.ts",
|
||||
out_dir + "/js/console.d.ts",
|
||||
out_dir + "/js/deno.d.ts",
|
||||
out_dir + "/js/globals.d.ts",
|
||||
|
@ -196,6 +198,7 @@ run_node("bundle") {
|
|||
out_dir = "$target_gen_dir/bundle/"
|
||||
sources = [
|
||||
"js/assets.ts",
|
||||
"js/compiler.ts",
|
||||
"js/console.ts",
|
||||
"js/fetch.ts",
|
||||
"js/fetch_types.d.ts",
|
||||
|
@ -204,7 +207,6 @@ run_node("bundle") {
|
|||
"js/main.ts",
|
||||
"js/os.ts",
|
||||
"js/plugins.d.ts",
|
||||
"js/runtime.ts",
|
||||
"js/text_encoding.ts",
|
||||
"js/timers.ts",
|
||||
"js/types.d.ts",
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
// tslint:disable:max-line-length
|
||||
|
||||
// Generated definitions
|
||||
import compilerDts from "gen/js/compiler.d.ts!string";
|
||||
import consoleDts from "gen/js/console.d.ts!string";
|
||||
import denoDts from "gen/js/deno.d.ts!string";
|
||||
import globalsDts from "gen/js/globals.d.ts!string";
|
||||
|
@ -55,6 +56,7 @@ import fetchTypesDts from "/js/fetch_types.d.ts!string";
|
|||
// prettier-ignore
|
||||
export const assetSourceCode: { [key: string]: string } = {
|
||||
// Generated definitions
|
||||
"compiler.d.ts": compilerDts,
|
||||
"console.d.ts": consoleDts,
|
||||
"deno.d.ts": denoDts,
|
||||
"globals.d.ts": globalsDts,
|
||||
|
|
595
js/compiler.ts
Normal file
595
js/compiler.ts
Normal file
|
@ -0,0 +1,595 @@
|
|||
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||
import * as ts from "typescript";
|
||||
import { assetSourceCode } from "./assets";
|
||||
import * as deno from "./deno";
|
||||
import { libdeno, window, globalEval } from "./globals";
|
||||
import * as os from "./os";
|
||||
import { RawSourceMap } from "./types";
|
||||
import { assert, log, notImplemented } from "./util";
|
||||
import * as sourceMaps from "./v8_source_maps";
|
||||
|
||||
const EOL = "\n";
|
||||
const ASSETS = "$asset$";
|
||||
|
||||
// tslint:disable:no-any
|
||||
type AmdCallback = (...args: any[]) => void;
|
||||
type AmdErrback = (err: any) => void;
|
||||
export type AmdFactory = (...args: any[]) => object | void;
|
||||
// tslint:enable:no-any
|
||||
export type AmdDefine = (deps: string[], factory: AmdFactory) => void;
|
||||
type AmdRequire = (
|
||||
deps: string[],
|
||||
callback: AmdCallback,
|
||||
errback?: AmdErrback
|
||||
) => void;
|
||||
|
||||
// The location that a module is being loaded from. This could be a directory,
|
||||
// like ".", or it could be a module specifier like
|
||||
// "http://gist.github.com/somefile.ts"
|
||||
type ContainingFile = string;
|
||||
// The internal local filename of a compiled module. It will often be something
|
||||
// like "/home/ry/.deno/gen/f7b4605dfbc4d3bb356e98fda6ceb1481e4a8df5.js"
|
||||
type ModuleFileName = string;
|
||||
// The external name of a module - could be a URL or could be a relative path.
|
||||
// Examples "http://gist.github.com/somefile.ts" or "./somefile.ts"
|
||||
type ModuleSpecifier = string;
|
||||
// The compiled source code which is cached in .deno/gen/
|
||||
type OutputCode = string;
|
||||
|
||||
/**
|
||||
* Abstraction of the APIs required from the `os` module so they can be
|
||||
* easily mocked.
|
||||
*/
|
||||
export interface Os {
|
||||
codeCache: typeof os.codeCache;
|
||||
codeFetch: typeof os.codeFetch;
|
||||
exit: typeof os.exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction of the APIs required from the `typescript` module so they can
|
||||
* be easily mocked.
|
||||
*/
|
||||
export interface Ts {
|
||||
createLanguageService: typeof ts.createLanguageService;
|
||||
/* tslint:disable-next-line:max-line-length */
|
||||
formatDiagnosticsWithColorAndContext: typeof ts.formatDiagnosticsWithColorAndContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple object structure for caching resolved modules and their contents.
|
||||
*
|
||||
* Named `ModuleMetaData` to clarify it is just a representation of meta data of
|
||||
* the module, not the actual module instance.
|
||||
*/
|
||||
export class ModuleMetaData {
|
||||
public readonly exports = {};
|
||||
public scriptSnapshot?: ts.IScriptSnapshot;
|
||||
public scriptVersion = "";
|
||||
|
||||
constructor(
|
||||
public readonly fileName: string,
|
||||
public readonly sourceCode = "",
|
||||
public outputCode = ""
|
||||
) {
|
||||
if (outputCode !== "" || fileName.endsWith(".d.ts")) {
|
||||
this.scriptVersion = "1";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The required minimal API to allow formatting of TypeScript compiler
|
||||
* diagnostics.
|
||||
*/
|
||||
const formatDiagnosticsHost: ts.FormatDiagnosticsHost = {
|
||||
getCurrentDirectory: () => ".",
|
||||
getCanonicalFileName: (fileName: string) => fileName,
|
||||
getNewLine: () => EOL
|
||||
};
|
||||
|
||||
/**
|
||||
* Throw a module resolution error, when a module is unsuccessfully resolved.
|
||||
*/
|
||||
function throwResolutionError(
|
||||
message: string,
|
||||
moduleSpecifier: ModuleSpecifier,
|
||||
containingFile: ContainingFile
|
||||
): never {
|
||||
throw new Error(
|
||||
// tslint:disable-next-line:max-line-length
|
||||
`Cannot resolve module "${moduleSpecifier}" from "${containingFile}".\n ${message}`
|
||||
);
|
||||
}
|
||||
|
||||
// ts.ScriptKind is not available at runtime, so local enum definition
|
||||
enum ScriptKind {
|
||||
JS = 1,
|
||||
TS = 3,
|
||||
JSON = 6
|
||||
}
|
||||
|
||||
/**
|
||||
* A singleton class that combines the TypeScript Language Service host API
|
||||
* with Deno specific APIs to provide an interface for compiling and running
|
||||
* TypeScript and JavaScript modules.
|
||||
*/
|
||||
export class DenoCompiler implements ts.LanguageServiceHost {
|
||||
// Modules are usually referenced by their ModuleSpecifier and ContainingFile,
|
||||
// and keeping a map of the resolved module file name allows more efficient
|
||||
// future resolution
|
||||
private readonly _fileNamesMap = new Map<
|
||||
ContainingFile,
|
||||
Map<ModuleSpecifier, ModuleFileName>
|
||||
>();
|
||||
// A reference to global eval, so it can be monkey patched during testing
|
||||
private _globalEval = globalEval;
|
||||
// A reference to the log utility, so it can be monkey patched during testing
|
||||
private _log = log;
|
||||
// A map of module file names to module meta data
|
||||
private readonly _moduleMetaDataMap = new Map<
|
||||
ModuleFileName,
|
||||
ModuleMetaData
|
||||
>();
|
||||
// TODO ideally this are not static and can be influenced by command line
|
||||
// arguments
|
||||
private readonly _options: Readonly<ts.CompilerOptions> = {
|
||||
allowJs: true,
|
||||
module: ts.ModuleKind.AMD,
|
||||
outDir: "$deno$",
|
||||
// TODO https://github.com/denoland/deno/issues/23
|
||||
inlineSourceMap: true,
|
||||
inlineSources: true,
|
||||
stripComments: true,
|
||||
target: ts.ScriptTarget.ESNext
|
||||
};
|
||||
// A reference to the `./os.ts` module, so it can be monkey patched during
|
||||
// testing
|
||||
private _os: Os = os;
|
||||
// Used to contain the script file we are currently running
|
||||
private _scriptFileNames: string[] = [];
|
||||
// A reference to the TypeScript LanguageService instance so it can be
|
||||
// monkey patched during testing
|
||||
private _service: ts.LanguageService;
|
||||
// A reference to `typescript` module so it can be monkey patched during
|
||||
// testing
|
||||
private _ts: Ts = ts;
|
||||
// A reference to the global scope so it can be monkey patched during
|
||||
// testing
|
||||
private _window = window;
|
||||
|
||||
/**
|
||||
* The TypeScript language service often refers to the resolved fileName of
|
||||
* a module, this is a shortcut to avoid unnecessary module resolution logic
|
||||
* for modules that may have been initially resolved by a `moduleSpecifier`
|
||||
* and `containingFile`. Also, `resolveModule()` throws when the module
|
||||
* cannot be resolved, which isn't always valid when dealing with the
|
||||
* TypeScript compiler, but the TypeScript compiler shouldn't be asking about
|
||||
* external modules that we haven't told it about yet.
|
||||
*/
|
||||
private _getModuleMetaData(
|
||||
fileName: ModuleFileName
|
||||
): ModuleMetaData | undefined {
|
||||
return this._moduleMetaDataMap.has(fileName)
|
||||
? this._moduleMetaDataMap.get(fileName)
|
||||
: fileName.startsWith(ASSETS)
|
||||
? this.resolveModule(fileName, "")
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup being able to map back source references back to their source
|
||||
*
|
||||
* TODO is this the best place for this? It is tightly coupled to how the
|
||||
* compiler works, but it is also tightly coupled to how the whole runtime
|
||||
* environment is bootstrapped. It also needs efficient access to the
|
||||
* `outputCode` of the module information, which exists inside of the
|
||||
* compiler instance.
|
||||
*/
|
||||
private _setupSourceMaps(): void {
|
||||
sourceMaps.install({
|
||||
installPrepareStackTrace: true,
|
||||
getGeneratedContents: (fileName: string): string | RawSourceMap => {
|
||||
this._log("getGeneratedContents", fileName);
|
||||
if (fileName === "gen/bundle/main.js") {
|
||||
assert(libdeno.mainSource.length > 0);
|
||||
return libdeno.mainSource;
|
||||
} else if (fileName === "main.js.map") {
|
||||
return libdeno.mainSourceMap;
|
||||
} else if (fileName === "deno_main.js") {
|
||||
return "";
|
||||
} else {
|
||||
const moduleMetaData = this._moduleMetaDataMap.get(fileName);
|
||||
if (!moduleMetaData) {
|
||||
this._log("getGeneratedContents cannot find", fileName);
|
||||
return "";
|
||||
}
|
||||
return moduleMetaData.outputCode;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
if (DenoCompiler._instance) {
|
||||
throw new TypeError("Attempt to create an additional compiler.");
|
||||
}
|
||||
this._service = this._ts.createLanguageService(this);
|
||||
this._setupSourceMaps();
|
||||
}
|
||||
|
||||
// Deno specific compiler API
|
||||
|
||||
/**
|
||||
* Retrieve the output of the TypeScript compiler for a given `fileName`.
|
||||
*/
|
||||
compile(fileName: ModuleFileName): OutputCode {
|
||||
const service = this._service;
|
||||
const output = service.getEmitOutput(fileName);
|
||||
|
||||
// Get the relevant diagnostics - this is 3x faster than
|
||||
// `getPreEmitDiagnostics`.
|
||||
const diagnostics = [
|
||||
...service.getCompilerOptionsDiagnostics(),
|
||||
...service.getSyntacticDiagnostics(fileName),
|
||||
...service.getSemanticDiagnostics(fileName)
|
||||
];
|
||||
if (diagnostics.length > 0) {
|
||||
const errMsg = this._ts.formatDiagnosticsWithColorAndContext(
|
||||
diagnostics,
|
||||
formatDiagnosticsHost
|
||||
);
|
||||
console.log(errMsg);
|
||||
// All TypeScript errors are terminal for deno
|
||||
this._os.exit(1);
|
||||
}
|
||||
|
||||
assert(!output.emitSkipped, "The emit was skipped for an unknown reason.");
|
||||
|
||||
// Currently we are inlining source maps, there should be only 1 output file
|
||||
// See: https://github.com/denoland/deno/issues/23
|
||||
assert(
|
||||
output.outputFiles.length === 1,
|
||||
"Only single file should be output."
|
||||
);
|
||||
|
||||
const [outputFile] = output.outputFiles;
|
||||
return outputFile.text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a localized AMD `define` function and return it.
|
||||
*/
|
||||
makeDefine(moduleMetaData: ModuleMetaData): AmdDefine {
|
||||
const localDefine = (deps: string[], factory: AmdFactory): void => {
|
||||
// TypeScript will emit a local require dependency when doing dynamic
|
||||
// `import()`
|
||||
const localRequire: AmdRequire = (
|
||||
deps: string[],
|
||||
callback: AmdCallback,
|
||||
errback?: AmdErrback
|
||||
): void => {
|
||||
this._log("localRequire", deps);
|
||||
try {
|
||||
const args = deps.map(dep => {
|
||||
if (dep in DenoCompiler._builtins) {
|
||||
return DenoCompiler._builtins[dep];
|
||||
} else {
|
||||
const depModuleMetaData = this.run(dep, moduleMetaData.fileName);
|
||||
return depModuleMetaData.exports;
|
||||
}
|
||||
});
|
||||
callback(...args);
|
||||
} catch (e) {
|
||||
if (errback) {
|
||||
errback(e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
const localExports = moduleMetaData.exports;
|
||||
this._log("localDefine", moduleMetaData.fileName, deps, localExports);
|
||||
const args = deps.map(dep => {
|
||||
if (dep === "require") {
|
||||
return localRequire;
|
||||
} else if (dep === "exports") {
|
||||
return localExports;
|
||||
} else if (dep in DenoCompiler._builtins) {
|
||||
return DenoCompiler._builtins[dep];
|
||||
} else {
|
||||
const depModuleMetaData = this.run(dep, moduleMetaData.fileName);
|
||||
return depModuleMetaData.exports;
|
||||
}
|
||||
});
|
||||
factory(...args);
|
||||
};
|
||||
return localDefine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a `moduleSpecifier` and `containingFile` retrieve the cached
|
||||
* `fileName` for a given module. If the module has yet to be resolved
|
||||
* this will return `undefined`.
|
||||
*/
|
||||
resolveFileName(
|
||||
moduleSpecifier: ModuleSpecifier,
|
||||
containingFile: ContainingFile
|
||||
): ModuleFileName | undefined {
|
||||
this._log("resolveFileName", { moduleSpecifier, containingFile });
|
||||
const innerMap = this._fileNamesMap.get(containingFile);
|
||||
if (innerMap) {
|
||||
return innerMap.get(moduleSpecifier);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a `moduleSpecifier` and `containingFile`, resolve the module and
|
||||
* return the `ModuleMetaData`.
|
||||
*/
|
||||
resolveModule(
|
||||
moduleSpecifier: ModuleSpecifier,
|
||||
containingFile: ContainingFile
|
||||
): ModuleMetaData {
|
||||
this._log("resolveModule", { moduleSpecifier, containingFile });
|
||||
assert(moduleSpecifier != null && moduleSpecifier.length > 0);
|
||||
let fileName = this.resolveFileName(moduleSpecifier, containingFile);
|
||||
if (fileName && this._moduleMetaDataMap.has(fileName)) {
|
||||
return this._moduleMetaDataMap.get(fileName)!;
|
||||
}
|
||||
let sourceCode: string | undefined;
|
||||
let outputCode: string | undefined;
|
||||
if (
|
||||
moduleSpecifier.startsWith(ASSETS) ||
|
||||
containingFile.startsWith(ASSETS)
|
||||
) {
|
||||
// Assets are compiled into the runtime javascript bundle.
|
||||
// we _know_ `.pop()` will return a string, but TypeScript doesn't so
|
||||
// not null assertion
|
||||
const moduleId = moduleSpecifier.split("/").pop()!;
|
||||
const assetName = moduleId.includes(".") ? moduleId : `${moduleId}.d.ts`;
|
||||
assert(assetName in assetSourceCode, `No such asset "${assetName}"`);
|
||||
sourceCode = assetSourceCode[assetName];
|
||||
fileName = `${ASSETS}/${assetName}`;
|
||||
} else {
|
||||
// We query Rust with a CodeFetch message. It will load the sourceCode,
|
||||
// and if there is any outputCode cached, will return that as well.
|
||||
let fetchResponse;
|
||||
try {
|
||||
fetchResponse = this._os.codeFetch(moduleSpecifier, containingFile);
|
||||
} catch (e) {
|
||||
return throwResolutionError(
|
||||
`os.codeFetch message: ${e.message}`,
|
||||
moduleSpecifier,
|
||||
containingFile
|
||||
);
|
||||
}
|
||||
fileName = fetchResponse.filename || undefined;
|
||||
sourceCode = fetchResponse.sourceCode || undefined;
|
||||
outputCode = fetchResponse.outputCode || undefined;
|
||||
}
|
||||
if (!sourceCode || sourceCode.length === 0 || !fileName) {
|
||||
return throwResolutionError(
|
||||
"Invalid source code or file name.",
|
||||
moduleSpecifier,
|
||||
containingFile
|
||||
);
|
||||
}
|
||||
this._log("resolveModule sourceCode length ", sourceCode.length);
|
||||
this.setFileName(moduleSpecifier, containingFile, fileName);
|
||||
if (fileName && this._moduleMetaDataMap.has(fileName)) {
|
||||
return this._moduleMetaDataMap.get(fileName)!;
|
||||
}
|
||||
const moduleMetaData = new ModuleMetaData(fileName, sourceCode, outputCode);
|
||||
this._moduleMetaDataMap.set(fileName, moduleMetaData);
|
||||
return moduleMetaData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the `fileName` for a given `moduleSpecifier` and `containingFile`
|
||||
*/
|
||||
resolveModuleName(
|
||||
moduleSpecifier: ModuleSpecifier,
|
||||
containingFile: ContainingFile
|
||||
): ModuleFileName | undefined {
|
||||
const moduleMetaData = this.resolveModule(moduleSpecifier, containingFile);
|
||||
return moduleMetaData ? moduleMetaData.fileName : undefined;
|
||||
}
|
||||
|
||||
/* tslint:disable-next-line:no-any */
|
||||
/**
|
||||
* Execute a module based on the `moduleSpecifier` and the `containingFile`
|
||||
* and return the resulting `FileModule`.
|
||||
*/
|
||||
run(
|
||||
moduleSpecifier: ModuleSpecifier,
|
||||
containingFile: ContainingFile
|
||||
): ModuleMetaData {
|
||||
this._log("run", { moduleSpecifier, containingFile });
|
||||
const moduleMetaData = this.resolveModule(moduleSpecifier, containingFile);
|
||||
const fileName = moduleMetaData.fileName;
|
||||
this._scriptFileNames = [fileName];
|
||||
const sourceCode = moduleMetaData.sourceCode;
|
||||
let outputCode = moduleMetaData.outputCode;
|
||||
if (!outputCode) {
|
||||
outputCode = moduleMetaData.outputCode = `${this.compile(
|
||||
fileName
|
||||
)}\n//# sourceURL=${fileName}`;
|
||||
moduleMetaData!.scriptVersion = "1";
|
||||
this._os.codeCache(fileName, sourceCode, outputCode);
|
||||
}
|
||||
this._window.define = this.makeDefine(moduleMetaData);
|
||||
this._globalEval(moduleMetaData.outputCode);
|
||||
this._window.define = undefined;
|
||||
return moduleMetaData!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the resolved `fileName` in relationship to the `moduleSpecifier`
|
||||
* and `containingFile` in order to reduce calls to the privileged side
|
||||
* to retrieve the contents of a module.
|
||||
*/
|
||||
setFileName(
|
||||
moduleSpecifier: ModuleSpecifier,
|
||||
containingFile: ContainingFile,
|
||||
fileName: ModuleFileName
|
||||
): void {
|
||||
this._log("setFileName", { moduleSpecifier, containingFile });
|
||||
let innerMap = this._fileNamesMap.get(containingFile);
|
||||
if (!innerMap) {
|
||||
innerMap = new Map();
|
||||
this._fileNamesMap.set(containingFile, innerMap);
|
||||
}
|
||||
innerMap.set(moduleSpecifier, fileName);
|
||||
}
|
||||
|
||||
// TypeScript Language Service API
|
||||
|
||||
getCompilationSettings(): ts.CompilerOptions {
|
||||
this._log("getCompilationSettings()");
|
||||
return this._options;
|
||||
}
|
||||
|
||||
getNewLine(): string {
|
||||
return EOL;
|
||||
}
|
||||
|
||||
getScriptFileNames(): string[] {
|
||||
// This is equal to `"files"` in the `tsconfig.json`, therefore we only need
|
||||
// to include the actual base source files we are evaluating at the moment,
|
||||
// which would be what is set during the `.run()`
|
||||
return this._scriptFileNames;
|
||||
}
|
||||
|
||||
getScriptKind(fileName: ModuleFileName): ts.ScriptKind {
|
||||
this._log("getScriptKind()", fileName);
|
||||
const suffix = fileName.substr(fileName.lastIndexOf(".") + 1);
|
||||
switch (suffix) {
|
||||
case "ts":
|
||||
return ScriptKind.TS;
|
||||
case "js":
|
||||
return ScriptKind.JS;
|
||||
case "json":
|
||||
return ScriptKind.JSON;
|
||||
default:
|
||||
return this._options.allowJs ? ScriptKind.JS : ScriptKind.TS;
|
||||
}
|
||||
}
|
||||
|
||||
getScriptVersion(fileName: ModuleFileName): string {
|
||||
this._log("getScriptVersion()", fileName);
|
||||
const moduleMetaData = this._getModuleMetaData(fileName);
|
||||
return (moduleMetaData && moduleMetaData.scriptVersion) || "";
|
||||
}
|
||||
|
||||
getScriptSnapshot(fileName: ModuleFileName): ts.IScriptSnapshot | undefined {
|
||||
this._log("getScriptSnapshot()", fileName);
|
||||
const moduleMetaData = this._getModuleMetaData(fileName);
|
||||
if (moduleMetaData) {
|
||||
return (
|
||||
moduleMetaData.scriptSnapshot ||
|
||||
(moduleMetaData.scriptSnapshot = {
|
||||
getText(start, end) {
|
||||
return moduleMetaData.sourceCode.substring(start, end);
|
||||
},
|
||||
getLength() {
|
||||
return moduleMetaData.sourceCode.length;
|
||||
},
|
||||
getChangeRange() {
|
||||
return undefined;
|
||||
}
|
||||
})
|
||||
);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
getCurrentDirectory(): string {
|
||||
this._log("getCurrentDirectory()");
|
||||
return "";
|
||||
}
|
||||
|
||||
getDefaultLibFileName(): string {
|
||||
this._log("getDefaultLibFileName()");
|
||||
const moduleSpecifier = "lib.globals.d.ts";
|
||||
const moduleMetaData = this.resolveModule(moduleSpecifier, ASSETS);
|
||||
return moduleMetaData.fileName;
|
||||
}
|
||||
|
||||
useCaseSensitiveFileNames(): boolean {
|
||||
this._log("useCaseSensitiveFileNames");
|
||||
return true;
|
||||
}
|
||||
|
||||
readFile(path: string): string | undefined {
|
||||
this._log("readFile", path);
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
fileExists(fileName: string): boolean {
|
||||
const moduleMetaData = this._getModuleMetaData(fileName);
|
||||
const exists = moduleMetaData != null;
|
||||
this._log("fileExists", fileName, exists);
|
||||
return exists;
|
||||
}
|
||||
|
||||
resolveModuleNames(
|
||||
moduleNames: ModuleSpecifier[],
|
||||
containingFile: ContainingFile
|
||||
): ts.ResolvedModule[] {
|
||||
this._log("resolveModuleNames", { moduleNames, containingFile });
|
||||
return moduleNames.map(name => {
|
||||
let resolvedFileName;
|
||||
if (name === "deno") {
|
||||
resolvedFileName = this.resolveModuleName("deno.d.ts", ASSETS);
|
||||
} else if (name === "compiler") {
|
||||
resolvedFileName = this.resolveModuleName("compiler.d.ts", ASSETS);
|
||||
} else if (name === "typescript") {
|
||||
resolvedFileName = this.resolveModuleName("typescript.d.ts", ASSETS);
|
||||
} else {
|
||||
resolvedFileName = this.resolveModuleName(name, containingFile);
|
||||
}
|
||||
// According to the interface we shouldn't return `undefined` but if we
|
||||
// fail to return the same length of modules to those we cannot resolve
|
||||
// then TypeScript fails on an assertion that the lengths can't be
|
||||
// different, so we have to return an "empty" resolved module
|
||||
// TODO: all this does is push the problem downstream, and TypeScript
|
||||
// will complain it can't identify the type of the file and throw
|
||||
// a runtime exception, so we need to handle missing modules better
|
||||
resolvedFileName = resolvedFileName || "";
|
||||
// This flags to the compiler to not go looking to transpile functional
|
||||
// code, anything that is in `/$asset$/` is just library code
|
||||
const isExternalLibraryImport = resolvedFileName.startsWith(ASSETS);
|
||||
// TODO: we should be returning a ts.ResolveModuleFull
|
||||
return { resolvedFileName, isExternalLibraryImport };
|
||||
});
|
||||
}
|
||||
|
||||
// Deno specific static properties and methods
|
||||
|
||||
/**
|
||||
* Built in modules which can be returned to external modules
|
||||
*
|
||||
* Placed as a private static otherwise we get use before
|
||||
* declared with the `DenoCompiler`
|
||||
*/
|
||||
// tslint:disable-next-line:no-any
|
||||
private static _builtins: { [mid: string]: any } = {
|
||||
typescript: ts,
|
||||
deno,
|
||||
compiler: { DenoCompiler, ModuleMetaData }
|
||||
};
|
||||
|
||||
private static _instance: DenoCompiler | undefined;
|
||||
|
||||
/**
|
||||
* Returns the instance of `DenoCompiler` or creates a new instance.
|
||||
*/
|
||||
static instance(): DenoCompiler {
|
||||
return (
|
||||
DenoCompiler._instance || (DenoCompiler._instance = new DenoCompiler())
|
||||
);
|
||||
}
|
||||
}
|
471
js/compiler_test.ts
Normal file
471
js/compiler_test.ts
Normal file
|
@ -0,0 +1,471 @@
|
|||
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||
import { test, assert, assertEqual } from "./testing/testing.ts";
|
||||
import * as compiler from "compiler";
|
||||
import * as ts from "typescript";
|
||||
|
||||
// We use a silly amount of `any` in these tests...
|
||||
// tslint:disable:no-any
|
||||
|
||||
const { DenoCompiler, ModuleMetaData } = compiler;
|
||||
|
||||
// Enums like this don't exist at runtime, so local copy
|
||||
enum ScriptKind {
|
||||
JS = 1,
|
||||
TS = 3,
|
||||
JSON = 6
|
||||
}
|
||||
|
||||
interface ModuleInfo {
|
||||
moduleName: string | null;
|
||||
filename: string | null;
|
||||
sourceCode: string | null;
|
||||
outputCode: string | null;
|
||||
}
|
||||
|
||||
const compilerInstance = DenoCompiler.instance();
|
||||
|
||||
// References to orignal items we are going to mock
|
||||
const originals = {
|
||||
_globalEval: (compilerInstance as any)._globalEval,
|
||||
_log: (compilerInstance as any)._log,
|
||||
_os: (compilerInstance as any)._os,
|
||||
_ts: (compilerInstance as any)._ts,
|
||||
_service: (compilerInstance as any)._service,
|
||||
_window: (compilerInstance as any)._window
|
||||
};
|
||||
|
||||
function mockModuleInfo(
|
||||
moduleName: string | null,
|
||||
filename: string | null,
|
||||
sourceCode: string | null,
|
||||
outputCode: string | null
|
||||
): ModuleInfo {
|
||||
return {
|
||||
moduleName,
|
||||
filename,
|
||||
sourceCode,
|
||||
outputCode
|
||||
};
|
||||
}
|
||||
|
||||
// Some fixtures we will us in testing
|
||||
const fooBarTsSource = `import * as compiler from "compiler";
|
||||
console.log(compiler);
|
||||
export const foo = "bar";
|
||||
`;
|
||||
|
||||
const fooBazTsSource = `import { foo } from "./bar.ts";
|
||||
console.log(foo);
|
||||
`;
|
||||
|
||||
// TODO(#23) Remove source map strings from fooBarTsOutput.
|
||||
// tslint:disable:max-line-length
|
||||
const fooBarTsOutput = `define(["require", "exports", "compiler"], function (require, exports, compiler) {
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
console.log(compiler);
|
||||
exports.foo = "bar";
|
||||
});
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZmlsZTovLy9yb290L3Byb2plY3QvZm9vL2Jhci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7SUFDQSxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ1QsUUFBQSxHQUFHLEdBQUcsS0FBSyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgY29tcGlsZXIgZnJvbSBcImNvbXBpbGVyXCI7XG5jb25zb2xlLmxvZyhjb21waWxlcik7XG5leHBvcnQgY29uc3QgZm9vID0gXCJiYXJcIjtcbiJdfQ==
|
||||
//# sourceURL=/root/project/foo/bar.ts`;
|
||||
|
||||
// TODO(#23) Remove source map strings from fooBazTsOutput.
|
||||
const fooBazTsOutput = `define(["require", "exports", "./bar.ts"], function (require, exports, bar_ts_1) {
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
console.log(bar_ts_1.foo);
|
||||
});
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmF6LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZmlsZTovLy9yb290L3Byb2plY3QvZm9vL2Jhei50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7SUFDQSxPQUFPLENBQUMsR0FBRyxDQUFDLFlBQUcsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZm9vIH0gZnJvbSBcIi4vYmFyLnRzXCI7XG5jb25zb2xlLmxvZyhmb28pO1xuIl19
|
||||
//# sourceURL=/root/project/foo/baz.ts`;
|
||||
// tslint:enable:max-line-length
|
||||
|
||||
const moduleMap: {
|
||||
[containFile: string]: { [moduleSpecifier: string]: ModuleInfo };
|
||||
} = {
|
||||
"/root/project": {
|
||||
"foo/bar.ts": mockModuleInfo(
|
||||
"foo/bar",
|
||||
"/root/project/foo/bar.ts",
|
||||
fooBarTsSource,
|
||||
null
|
||||
),
|
||||
"foo/baz.ts": mockModuleInfo(
|
||||
"foo/baz",
|
||||
"/root/project/foo/baz.ts",
|
||||
fooBazTsSource,
|
||||
null
|
||||
),
|
||||
"foo/qat.ts": mockModuleInfo(
|
||||
"foo/qat",
|
||||
"/root/project/foo/qat.ts",
|
||||
null,
|
||||
null
|
||||
)
|
||||
},
|
||||
"/root/project/foo/baz.ts": {
|
||||
"./bar.ts": mockModuleInfo(
|
||||
"foo/bar",
|
||||
"/root/project/foo/bar.ts",
|
||||
fooBarTsSource,
|
||||
fooBarTsOutput
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
const emittedFiles = {
|
||||
"/root/project/foo/qat.ts": "console.log('foo');"
|
||||
};
|
||||
|
||||
let globalEvalStack: string[] = [];
|
||||
let getEmitOutputStack: string[] = [];
|
||||
let logStack: any[][] = [];
|
||||
let codeCacheStack: Array<{
|
||||
fileName: string;
|
||||
sourceCode: string;
|
||||
outputCode: string;
|
||||
}> = [];
|
||||
let codeFetchStack: Array<{
|
||||
moduleSpecifier: string;
|
||||
containingFile: string;
|
||||
}> = [];
|
||||
|
||||
function reset() {
|
||||
codeFetchStack = [];
|
||||
codeCacheStack = [];
|
||||
logStack = [];
|
||||
getEmitOutputStack = [];
|
||||
globalEvalStack = [];
|
||||
}
|
||||
|
||||
let mockDeps: string[] | undefined;
|
||||
let mockFactory: compiler.AmdFactory;
|
||||
|
||||
function globalEvalMock(x: string): void {
|
||||
globalEvalStack.push(x);
|
||||
if (windowMock.define && mockDeps && mockFactory) {
|
||||
windowMock.define(mockDeps, mockFactory);
|
||||
}
|
||||
}
|
||||
function logMock(...args: any[]): void {
|
||||
logStack.push(args);
|
||||
}
|
||||
const osMock: compiler.Os = {
|
||||
codeCache(fileName: string, sourceCode: string, outputCode: string): void {
|
||||
codeCacheStack.push({ fileName, sourceCode, outputCode });
|
||||
},
|
||||
codeFetch(moduleSpecifier: string, containingFile: string): ModuleInfo {
|
||||
codeFetchStack.push({ moduleSpecifier, containingFile });
|
||||
if (containingFile in moduleMap) {
|
||||
if (moduleSpecifier in moduleMap[containingFile]) {
|
||||
return moduleMap[containingFile][moduleSpecifier];
|
||||
}
|
||||
}
|
||||
return mockModuleInfo(null, null, null, null);
|
||||
},
|
||||
exit(code: number): never {
|
||||
throw new Error(`os.exit(${code})`);
|
||||
}
|
||||
};
|
||||
const tsMock: compiler.Ts = {
|
||||
createLanguageService(host: ts.LanguageServiceHost): ts.LanguageService {
|
||||
return {} as ts.LanguageService;
|
||||
},
|
||||
formatDiagnosticsWithColorAndContext(
|
||||
diagnostics: ReadonlyArray<ts.Diagnostic>,
|
||||
host: ts.FormatDiagnosticsHost
|
||||
): string {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
const getEmitOutputPassThrough = true;
|
||||
|
||||
const serviceMock = {
|
||||
getCompilerOptionsDiagnostics(): ts.Diagnostic[] {
|
||||
return originals._service.getCompilerOptionsDiagnostics.call(
|
||||
originals._service
|
||||
);
|
||||
},
|
||||
getEmitOutput(fileName: string): ts.EmitOutput {
|
||||
getEmitOutputStack.push(fileName);
|
||||
if (getEmitOutputPassThrough) {
|
||||
return originals._service.getEmitOutput.call(
|
||||
originals._service,
|
||||
fileName
|
||||
);
|
||||
}
|
||||
if (fileName in emittedFiles) {
|
||||
return {
|
||||
outputFiles: [{ text: emittedFiles[fileName] }] as any,
|
||||
emitSkipped: false
|
||||
};
|
||||
}
|
||||
return { outputFiles: [], emitSkipped: false };
|
||||
},
|
||||
getSemanticDiagnostics(fileName: string): ts.Diagnostic[] {
|
||||
return originals._service.getSemanticDiagnostics.call(
|
||||
originals._service,
|
||||
fileName
|
||||
);
|
||||
},
|
||||
getSyntacticDiagnostics(fileName: string): ts.Diagnostic[] {
|
||||
return originals._service.getSyntacticDiagnostics.call(
|
||||
originals._service,
|
||||
fileName
|
||||
);
|
||||
}
|
||||
};
|
||||
const windowMock: { define?: compiler.AmdDefine } = {};
|
||||
const mocks = {
|
||||
_globalEval: globalEvalMock,
|
||||
_log: logMock,
|
||||
_os: osMock,
|
||||
_ts: tsMock,
|
||||
_service: serviceMock,
|
||||
_window: windowMock
|
||||
};
|
||||
|
||||
// Setup the mocks
|
||||
test(function compilerTestsSetup() {
|
||||
assert("_globalEval" in compilerInstance);
|
||||
assert("_log" in compilerInstance);
|
||||
assert("_os" in compilerInstance);
|
||||
assert("_ts" in compilerInstance);
|
||||
assert("_service" in compilerInstance);
|
||||
assert("_window" in compilerInstance);
|
||||
Object.assign(compilerInstance, mocks);
|
||||
});
|
||||
|
||||
test(function compilerInstance() {
|
||||
assert(DenoCompiler != null);
|
||||
assert(DenoCompiler.instance() != null);
|
||||
});
|
||||
|
||||
// Testing the internal APIs
|
||||
|
||||
test(function compilerMakeDefine() {
|
||||
const moduleMetaData = new ModuleMetaData(
|
||||
"/root/project/foo/bar.ts",
|
||||
fooBarTsSource,
|
||||
fooBarTsOutput
|
||||
);
|
||||
const localDefine = compilerInstance.makeDefine(moduleMetaData);
|
||||
let factoryCalled = false;
|
||||
localDefine(
|
||||
["require", "exports", "compiler"],
|
||||
(_require, _exports, _compiler): void => {
|
||||
factoryCalled = true;
|
||||
assertEqual(
|
||||
typeof _require,
|
||||
"function",
|
||||
"localRequire should be a function"
|
||||
);
|
||||
assert(_exports != null);
|
||||
assert(
|
||||
Object.keys(_exports).length === 0,
|
||||
"exports should have no properties"
|
||||
);
|
||||
assert(compiler === _compiler, "compiler should be passed to factory");
|
||||
}
|
||||
);
|
||||
assert(factoryCalled, "Factory expected to be called");
|
||||
});
|
||||
|
||||
// TODO testMakeDefineExternalModule - testing that make define properly runs
|
||||
// external modules, this is implicitly tested though in
|
||||
// `compilerRunMultiModule`
|
||||
|
||||
test(function compilerRun() {
|
||||
// equal to `deno foo/bar.ts`
|
||||
reset();
|
||||
const result = compilerInstance.run("foo/bar.ts", "/root/project");
|
||||
assert(result instanceof ModuleMetaData);
|
||||
assertEqual(codeFetchStack.length, 1);
|
||||
assertEqual(codeCacheStack.length, 1);
|
||||
assertEqual(globalEvalStack.length, 1);
|
||||
|
||||
const lastGlobalEval = globalEvalStack.pop();
|
||||
assertEqual(lastGlobalEval, fooBarTsOutput);
|
||||
const lastCodeFetch = codeFetchStack.pop();
|
||||
assertEqual(lastCodeFetch, {
|
||||
moduleSpecifier: "foo/bar.ts",
|
||||
containingFile: "/root/project"
|
||||
});
|
||||
const lastCodeCache = codeCacheStack.pop();
|
||||
assertEqual(lastCodeCache, {
|
||||
fileName: "/root/project/foo/bar.ts",
|
||||
sourceCode: fooBarTsSource,
|
||||
outputCode: fooBarTsOutput
|
||||
});
|
||||
});
|
||||
|
||||
test(function compilerRunMultiModule() {
|
||||
// equal to `deno foo/baz.ts`
|
||||
reset();
|
||||
let factoryRun = false;
|
||||
mockDeps = ["require", "exports", "compiler"];
|
||||
mockFactory = (...deps: any[]) => {
|
||||
const [_require, _exports, _compiler] = deps;
|
||||
assertEqual(typeof _require, "function");
|
||||
assertEqual(typeof _exports, "object");
|
||||
assertEqual(_compiler, compiler);
|
||||
factoryRun = true;
|
||||
Object.defineProperty(_exports, "__esModule", { value: true });
|
||||
_exports.foo = "bar";
|
||||
// it is too complicated to test the outer factory, because the localised
|
||||
// make define already has a reference to this factory and it can't really
|
||||
// be easily unwound. So we will do what we can with the inner one and
|
||||
// then just clear it...
|
||||
mockDeps = undefined;
|
||||
mockFactory = undefined;
|
||||
};
|
||||
|
||||
const result = compilerInstance.run("foo/baz.ts", "/root/project");
|
||||
assert(result instanceof ModuleMetaData);
|
||||
// we have mocked that foo/bar.ts is already cached, so two fetches,
|
||||
// but only a single cache
|
||||
assertEqual(codeFetchStack.length, 2);
|
||||
assertEqual(codeCacheStack.length, 1);
|
||||
// because of the challenges with the way the module factories are generated
|
||||
// we only get one invocation of the `globalEval` mock.
|
||||
assertEqual(globalEvalStack.length, 1);
|
||||
assert(factoryRun);
|
||||
});
|
||||
|
||||
// TypeScript LanguageServiceHost APIs
|
||||
|
||||
test(function compilerGetCompilationSettings() {
|
||||
const result = compilerInstance.getCompilationSettings();
|
||||
for (const key of [
|
||||
"allowJs",
|
||||
"module",
|
||||
"outDir",
|
||||
"inlineSourceMap",
|
||||
"inlineSources",
|
||||
"stripComments",
|
||||
"target"
|
||||
]) {
|
||||
assert(key in result, `Expected "${key}" in compiler options.`);
|
||||
}
|
||||
});
|
||||
|
||||
test(function compilerGetNewLine() {
|
||||
const result = compilerInstance.getNewLine();
|
||||
assertEqual(result, "\n", "Expected newline value of '\\n'.");
|
||||
});
|
||||
|
||||
test(function compilerGetScriptFileNames() {
|
||||
compilerInstance.run("foo/bar.ts", "/root/project");
|
||||
const result = compilerInstance.getScriptFileNames();
|
||||
assertEqual(result.length, 1, "Expected only a single filename.");
|
||||
assertEqual(result[0], "/root/project/foo/bar.ts");
|
||||
});
|
||||
|
||||
test(function compilerGetScriptKind() {
|
||||
assertEqual(compilerInstance.getScriptKind("foo.ts"), ScriptKind.TS);
|
||||
assertEqual(compilerInstance.getScriptKind("foo.d.ts"), ScriptKind.TS);
|
||||
assertEqual(compilerInstance.getScriptKind("foo.js"), ScriptKind.JS);
|
||||
assertEqual(compilerInstance.getScriptKind("foo.json"), ScriptKind.JSON);
|
||||
assertEqual(compilerInstance.getScriptKind("foo.txt"), ScriptKind.JS);
|
||||
});
|
||||
|
||||
test(function compilerGetScriptVersion() {
|
||||
const moduleMetaData = compilerInstance.resolveModule(
|
||||
"foo/bar.ts",
|
||||
"/root/project"
|
||||
);
|
||||
assertEqual(
|
||||
compilerInstance.getScriptVersion(moduleMetaData.fileName),
|
||||
"1",
|
||||
"Expected known module to have script version of 1"
|
||||
);
|
||||
});
|
||||
|
||||
test(function compilerGetScriptVersionUnknown() {
|
||||
assertEqual(
|
||||
compilerInstance.getScriptVersion("/root/project/unknown_module.ts"),
|
||||
"",
|
||||
"Expected unknown module to have an empty script version"
|
||||
);
|
||||
});
|
||||
|
||||
test(function compilerGetScriptSnapshot() {
|
||||
const moduleMetaData = compilerInstance.resolveModule(
|
||||
"foo/bar.ts",
|
||||
"/root/project"
|
||||
);
|
||||
const result = compilerInstance.getScriptSnapshot(moduleMetaData.fileName);
|
||||
assert(result != null, "Expected snapshot to be defined.");
|
||||
assertEqual(result.getLength(), fooBarTsSource.length);
|
||||
assertEqual(
|
||||
result.getText(0, 6),
|
||||
"import",
|
||||
"Expected .getText() to equal 'import'"
|
||||
);
|
||||
assertEqual(result.getChangeRange(result), undefined);
|
||||
assert(!("dispose" in result));
|
||||
});
|
||||
|
||||
test(function compilerGetCurrentDirectory() {
|
||||
assertEqual(compilerInstance.getCurrentDirectory(), "");
|
||||
});
|
||||
|
||||
test(function compilerGetDefaultLibFileName() {
|
||||
assertEqual(
|
||||
compilerInstance.getDefaultLibFileName(),
|
||||
"$asset$/lib.globals.d.ts"
|
||||
);
|
||||
});
|
||||
|
||||
test(function compilerUseCaseSensitiveFileNames() {
|
||||
assertEqual(compilerInstance.useCaseSensitiveFileNames(), true);
|
||||
});
|
||||
|
||||
test(function compilerReadFile() {
|
||||
let doesThrow = false;
|
||||
try {
|
||||
compilerInstance.readFile("foobar.ts");
|
||||
} catch (e) {
|
||||
doesThrow = true;
|
||||
assert(e.message.includes("Not implemented") === true);
|
||||
}
|
||||
assert(doesThrow);
|
||||
});
|
||||
|
||||
test(function compilerFileExists() {
|
||||
const moduleMetaData = compilerInstance.resolveModule(
|
||||
"foo/bar.ts",
|
||||
"/root/project"
|
||||
);
|
||||
assert(compilerInstance.fileExists(moduleMetaData.fileName));
|
||||
assert(compilerInstance.fileExists("$asset$/compiler.d.ts"));
|
||||
assertEqual(
|
||||
compilerInstance.fileExists("/root/project/unknown-module.ts"),
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
test(function compilerResolveModuleNames() {
|
||||
const results = compilerInstance.resolveModuleNames(
|
||||
["foo/bar.ts", "foo/baz.ts", "$asset$/lib.globals.d.ts", "deno"],
|
||||
"/root/project"
|
||||
);
|
||||
assertEqual(results.length, 4);
|
||||
const fixtures: Array<[string, boolean]> = [
|
||||
["/root/project/foo/bar.ts", false],
|
||||
["/root/project/foo/baz.ts", false],
|
||||
["$asset$/lib.globals.d.ts", true],
|
||||
["$asset$/deno.d.ts", true]
|
||||
];
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
const result = results[i];
|
||||
const [resolvedFileName, isExternalLibraryImport] = fixtures[i];
|
||||
assertEqual(result.resolvedFileName, resolvedFileName);
|
||||
assertEqual(result.isExternalLibraryImport, isExternalLibraryImport);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove the mocks
|
||||
test(function compilerTestsTeardown() {
|
||||
Object.assign(compilerInstance, originals);
|
||||
});
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { Console } from "./console";
|
||||
import { exit } from "./os";
|
||||
import { RawSourceMap } from "./types";
|
||||
import * as timers from "./timers";
|
||||
import { TextEncoder, TextDecoder } from "./text_encoding";
|
||||
|
@ -9,6 +10,14 @@ import * as fetch_ from "./fetch";
|
|||
declare global {
|
||||
interface Window {
|
||||
console: Console;
|
||||
define: Readonly<unknown>;
|
||||
onerror?: (
|
||||
message: string,
|
||||
source: string,
|
||||
lineno: number,
|
||||
colno: number,
|
||||
error: Error
|
||||
) => void;
|
||||
}
|
||||
|
||||
const clearTimeout: typeof timers.clearTimer;
|
||||
|
@ -58,6 +67,22 @@ window.clearTimeout = timers.clearTimer;
|
|||
window.clearInterval = timers.clearTimer;
|
||||
|
||||
window.console = new Console(libdeno.print);
|
||||
// Uncaught exceptions are sent to window.onerror by the privileged binding.
|
||||
window.onerror = (
|
||||
message: string,
|
||||
source: string,
|
||||
lineno: number,
|
||||
colno: number,
|
||||
error: Error
|
||||
) => {
|
||||
// TODO Currently there is a bug in v8_source_maps.ts that causes a
|
||||
// segfault if it is used within window.onerror. To workaround we
|
||||
// uninstall the Error.prepareStackTrace handler. Users will get unmapped
|
||||
// stack traces on uncaught exceptions until this issue is fixed.
|
||||
//Error.prepareStackTrace = null;
|
||||
console.log(error.stack);
|
||||
exit(1);
|
||||
};
|
||||
window.TextEncoder = TextEncoder;
|
||||
window.TextDecoder = TextDecoder;
|
||||
|
||||
|
|
14
js/main.ts
14
js/main.ts
|
@ -1,10 +1,9 @@
|
|||
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||
import { flatbuffers } from "flatbuffers";
|
||||
import { deno as fbs } from "gen/msg_generated";
|
||||
import { assert, log, assignCmdId } from "./util";
|
||||
import * as util from "./util";
|
||||
import { assert, assignCmdId, log, setLogDebug } from "./util";
|
||||
import * as os from "./os";
|
||||
import * as runtime from "./runtime";
|
||||
import { DenoCompiler } from "./compiler";
|
||||
import { libdeno } from "./globals";
|
||||
import * as timers from "./timers";
|
||||
import { onFetchRes } from "./fetch";
|
||||
|
@ -47,7 +46,7 @@ function onMessage(ui8: Uint8Array) {
|
|||
/* tslint:disable-next-line:no-default-export */
|
||||
export default function denoMain() {
|
||||
libdeno.recv(onMessage);
|
||||
runtime.setup();
|
||||
const compiler = DenoCompiler.instance();
|
||||
|
||||
// First we send an empty "Start" message to let the privlaged side know we
|
||||
// are ready. The response should be a "StartRes" message containing the CLI
|
||||
|
@ -69,7 +68,7 @@ export default function denoMain() {
|
|||
const startResMsg = new fbs.StartRes();
|
||||
assert(base.msg(startResMsg) != null);
|
||||
|
||||
util.setLogDebug(startResMsg.debugFlag());
|
||||
setLogDebug(startResMsg.debugFlag());
|
||||
|
||||
const cwd = startResMsg.cwd();
|
||||
log("cwd", cwd);
|
||||
|
@ -86,8 +85,5 @@ export default function denoMain() {
|
|||
os.exit(1);
|
||||
}
|
||||
|
||||
const mod = runtime.resolveModule(inputFn, `${cwd}/`);
|
||||
assert(mod != null);
|
||||
// TypeScript does not track assert, therefore not null assertion
|
||||
mod!.compileAndRun();
|
||||
compiler.run(inputFn, `${cwd}/`);
|
||||
}
|
||||
|
|
376
js/runtime.ts
376
js/runtime.ts
|
@ -1,376 +0,0 @@
|
|||
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||
// Glossary
|
||||
// outputCode = generated javascript code
|
||||
// sourceCode = typescript code (or input javascript code)
|
||||
// moduleName = a resolved module name
|
||||
// fileName = an unresolved raw fileName.
|
||||
// for http modules , its the path to the locally downloaded
|
||||
// version.
|
||||
|
||||
import * as ts from "typescript";
|
||||
import * as util from "./util";
|
||||
import { log } from "./util";
|
||||
import { assetSourceCode } from "./assets";
|
||||
import * as os from "./os";
|
||||
import * as sourceMaps from "./v8_source_maps";
|
||||
import { libdeno, window, globalEval } from "./globals";
|
||||
import * as deno from "./deno";
|
||||
import { RawSourceMap } from "./types";
|
||||
|
||||
const EOL = "\n";
|
||||
const ASSETS = "/$asset$/";
|
||||
|
||||
// tslint:disable-next-line:no-any
|
||||
export type AmdFactory = (...args: any[]) => undefined | object;
|
||||
export type AmdDefine = (deps: string[], factory: AmdFactory) => void;
|
||||
|
||||
// Uncaught exceptions are sent to window.onerror by the privlaged binding.
|
||||
window.onerror = (
|
||||
message: string,
|
||||
source: string,
|
||||
lineno: number,
|
||||
colno: number,
|
||||
error: Error
|
||||
) => {
|
||||
// TODO Currently there is a bug in v8_source_maps.ts that causes a segfault
|
||||
// if it is used within window.onerror. To workaround we uninstall the
|
||||
// Error.prepareStackTrace handler. Users will get unmapped stack traces on
|
||||
// uncaught exceptions until this issue is fixed.
|
||||
//Error.prepareStackTrace = null;
|
||||
console.log(error.stack);
|
||||
os.exit(1);
|
||||
};
|
||||
|
||||
export function setup(): void {
|
||||
sourceMaps.install({
|
||||
installPrepareStackTrace: true,
|
||||
getGeneratedContents: (filename: string): string | RawSourceMap => {
|
||||
util.log("getGeneratedContents", filename);
|
||||
if (filename === "gen/bundle/main.js") {
|
||||
util.assert(libdeno.mainSource.length > 0);
|
||||
return libdeno.mainSource;
|
||||
} else if (filename === "main.js.map") {
|
||||
return libdeno.mainSourceMap;
|
||||
} else if (filename === "deno_main.js") {
|
||||
return "";
|
||||
} else {
|
||||
const mod = FileModule.load(filename);
|
||||
if (!mod) {
|
||||
util.log("getGeneratedContents cannot find", filename);
|
||||
return "";
|
||||
}
|
||||
return mod.outputCode;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// This class represents a module. We call it FileModule to make it explicit
|
||||
// that each module represents a single file.
|
||||
// Access to FileModule instances should only be done thru the static method
|
||||
// FileModule.load(). FileModules are NOT executed upon first load, only when
|
||||
// compileAndRun is called.
|
||||
export class FileModule {
|
||||
scriptVersion = "";
|
||||
readonly exports = {};
|
||||
|
||||
private static readonly map = new Map<string, FileModule>();
|
||||
constructor(
|
||||
readonly fileName: string,
|
||||
readonly sourceCode = "",
|
||||
public outputCode = ""
|
||||
) {
|
||||
util.assert(
|
||||
!FileModule.map.has(fileName),
|
||||
`FileModule.map already has ${fileName}`
|
||||
);
|
||||
FileModule.map.set(fileName, this);
|
||||
if (outputCode !== "") {
|
||||
this.scriptVersion = "1";
|
||||
}
|
||||
}
|
||||
|
||||
compileAndRun(): void {
|
||||
util.log("compileAndRun", this.sourceCode);
|
||||
if (!this.outputCode) {
|
||||
// If there is no cached outputCode, then compile the code.
|
||||
util.assert(
|
||||
this.sourceCode != null && this.sourceCode.length > 0,
|
||||
`Have no source code from ${this.fileName}`
|
||||
);
|
||||
const compiler = Compiler.instance();
|
||||
this.outputCode = compiler.compile(this.fileName);
|
||||
os.codeCache(this.fileName, this.sourceCode, this.outputCode);
|
||||
}
|
||||
execute(this.fileName, this.outputCode);
|
||||
}
|
||||
|
||||
static load(fileName: string): FileModule | undefined {
|
||||
return this.map.get(fileName);
|
||||
}
|
||||
|
||||
static getScriptsWithSourceCode(): string[] {
|
||||
const out = [];
|
||||
for (const fn of this.map.keys()) {
|
||||
const m = this.map.get(fn);
|
||||
if (m && m.sourceCode) {
|
||||
out.push(fn);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
export function makeDefine(fileName: string): AmdDefine {
|
||||
const localDefine = (deps: string[], factory: AmdFactory): void => {
|
||||
const localRequire = (x: string) => {
|
||||
log("localRequire", x);
|
||||
};
|
||||
const currentModule = FileModule.load(fileName);
|
||||
util.assert(currentModule != null);
|
||||
const localExports = currentModule!.exports;
|
||||
log("localDefine", fileName, deps, localExports);
|
||||
const args = deps.map(dep => {
|
||||
if (dep === "require") {
|
||||
return localRequire;
|
||||
} else if (dep === "exports") {
|
||||
return localExports;
|
||||
} else if (dep === "typescript") {
|
||||
return ts;
|
||||
} else if (dep === "deno") {
|
||||
return deno;
|
||||
} else {
|
||||
const resolved = resolveModuleName(dep, fileName);
|
||||
util.assert(resolved != null);
|
||||
const depModule = FileModule.load(resolved!);
|
||||
if (depModule) {
|
||||
depModule.compileAndRun();
|
||||
return depModule.exports;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
factory(...args);
|
||||
};
|
||||
return localDefine;
|
||||
}
|
||||
|
||||
export function resolveModule(
|
||||
moduleSpecifier: string,
|
||||
containingFile: string
|
||||
): null | FileModule {
|
||||
util.log("resolveModule", { moduleSpecifier, containingFile });
|
||||
util.assert(moduleSpecifier != null && moduleSpecifier.length > 0);
|
||||
let filename: string | null;
|
||||
let sourceCode: string | null;
|
||||
let outputCode: string | null;
|
||||
if (moduleSpecifier.startsWith(ASSETS) || containingFile.startsWith(ASSETS)) {
|
||||
// Assets are compiled into the runtime javascript bundle.
|
||||
// we _know_ `.pop()` will return a string, but TypeScript doesn't so
|
||||
// not null assertion
|
||||
const moduleId = moduleSpecifier.split("/").pop()!;
|
||||
const assetName = moduleId.includes(".") ? moduleId : `${moduleId}.d.ts`;
|
||||
util.assert(assetName in assetSourceCode, `No such asset "${assetName}"`);
|
||||
sourceCode = assetSourceCode[assetName];
|
||||
filename = ASSETS + assetName;
|
||||
} else {
|
||||
// We query Rust with a CodeFetch message. It will load the sourceCode, and
|
||||
// if there is any outputCode cached, will return that as well.
|
||||
const fetchResponse = os.codeFetch(moduleSpecifier, containingFile);
|
||||
filename = fetchResponse.filename;
|
||||
sourceCode = fetchResponse.sourceCode;
|
||||
outputCode = fetchResponse.outputCode;
|
||||
}
|
||||
if (sourceCode == null || sourceCode.length === 0 || filename == null) {
|
||||
return null;
|
||||
}
|
||||
util.log("resolveModule sourceCode length ", sourceCode.length);
|
||||
const m = FileModule.load(filename);
|
||||
if (m != null) {
|
||||
return m;
|
||||
} else {
|
||||
// null and undefined are incompatible in strict mode, but outputCode being
|
||||
// null here has no runtime behavior impact, therefore not null assertion
|
||||
return new FileModule(filename, sourceCode, outputCode!);
|
||||
}
|
||||
}
|
||||
|
||||
function resolveModuleName(
|
||||
moduleSpecifier: string,
|
||||
containingFile: string
|
||||
): string | undefined {
|
||||
const mod = resolveModule(moduleSpecifier, containingFile);
|
||||
if (mod) {
|
||||
return mod.fileName;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function execute(fileName: string, outputCode: string): void {
|
||||
util.assert(outputCode != null && outputCode.length > 0);
|
||||
window["define"] = makeDefine(fileName);
|
||||
outputCode += `\n//# sourceURL=${fileName}`;
|
||||
globalEval(outputCode);
|
||||
window["define"] = null;
|
||||
}
|
||||
|
||||
// This is a singleton class. Use Compiler.instance() to access.
|
||||
class Compiler {
|
||||
options: ts.CompilerOptions = {
|
||||
allowJs: true,
|
||||
module: ts.ModuleKind.AMD,
|
||||
outDir: "$deno$",
|
||||
inlineSourceMap: true,
|
||||
inlineSources: true,
|
||||
target: ts.ScriptTarget.ESNext
|
||||
};
|
||||
/*
|
||||
allowJs: true,
|
||||
module: ts.ModuleKind.AMD,
|
||||
noEmit: false,
|
||||
outDir: '$deno$',
|
||||
*/
|
||||
private service: ts.LanguageService;
|
||||
|
||||
private constructor() {
|
||||
const host = new TypeScriptHost(this.options);
|
||||
this.service = ts.createLanguageService(host);
|
||||
}
|
||||
|
||||
private static _instance: Compiler;
|
||||
static instance(): Compiler {
|
||||
return this._instance || (this._instance = new this());
|
||||
}
|
||||
|
||||
compile(fileName: string): string {
|
||||
const output = this.service.getEmitOutput(fileName);
|
||||
|
||||
// Get the relevant diagnostics - this is 3x faster than
|
||||
// `getPreEmitDiagnostics`.
|
||||
const diagnostics = this.service
|
||||
.getCompilerOptionsDiagnostics()
|
||||
.concat(this.service.getSyntacticDiagnostics(fileName))
|
||||
.concat(this.service.getSemanticDiagnostics(fileName));
|
||||
if (diagnostics.length > 0) {
|
||||
const errMsg = ts.formatDiagnosticsWithColorAndContext(
|
||||
diagnostics,
|
||||
formatDiagnosticsHost
|
||||
);
|
||||
console.log(errMsg);
|
||||
os.exit(1);
|
||||
}
|
||||
|
||||
util.assert(!output.emitSkipped);
|
||||
|
||||
const outputCode = output.outputFiles[0].text;
|
||||
// let sourceMapCode = output.outputFiles[0].text;
|
||||
return outputCode;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the compiler host for type checking.
|
||||
class TypeScriptHost implements ts.LanguageServiceHost {
|
||||
constructor(readonly options: ts.CompilerOptions) {}
|
||||
|
||||
getScriptFileNames(): string[] {
|
||||
const keys = FileModule.getScriptsWithSourceCode();
|
||||
util.log("getScriptFileNames", keys);
|
||||
return keys;
|
||||
}
|
||||
|
||||
getScriptVersion(fileName: string): string {
|
||||
util.log("getScriptVersion", fileName);
|
||||
const m = FileModule.load(fileName);
|
||||
return (m && m.scriptVersion) || "";
|
||||
}
|
||||
|
||||
getScriptSnapshot(fileName: string): ts.IScriptSnapshot | undefined {
|
||||
util.log("getScriptSnapshot", fileName);
|
||||
const m = resolveModule(fileName, ".");
|
||||
if (m == null) {
|
||||
util.log("getScriptSnapshot", fileName, "NOT FOUND");
|
||||
return undefined;
|
||||
}
|
||||
//const m = resolveModule(fileName, ".");
|
||||
util.assert(m.sourceCode.length > 0);
|
||||
return ts.ScriptSnapshot.fromString(m.sourceCode);
|
||||
}
|
||||
|
||||
fileExists(fileName: string): boolean {
|
||||
const m = resolveModule(fileName, ".");
|
||||
const exists = m != null;
|
||||
util.log("fileExist", fileName, exists);
|
||||
return exists;
|
||||
}
|
||||
|
||||
readFile(path: string, encoding?: string): string | undefined {
|
||||
util.log("readFile", path);
|
||||
return util.notImplemented();
|
||||
}
|
||||
|
||||
getNewLine() {
|
||||
return EOL;
|
||||
}
|
||||
|
||||
getCurrentDirectory() {
|
||||
util.log("getCurrentDirectory");
|
||||
return ".";
|
||||
}
|
||||
|
||||
getCompilationSettings() {
|
||||
util.log("getCompilationSettings");
|
||||
return this.options;
|
||||
}
|
||||
|
||||
getDefaultLibFileName(options: ts.CompilerOptions): string {
|
||||
const fn = "lib.globals.d.ts"; // ts.getDefaultLibFileName(options);
|
||||
util.log("getDefaultLibFileName", fn);
|
||||
const m = resolveModule(fn, ASSETS);
|
||||
util.assert(m != null);
|
||||
// TypeScript cannot track assertions, therefore not null assertion
|
||||
return m!.fileName;
|
||||
}
|
||||
|
||||
resolveModuleNames(
|
||||
moduleNames: string[],
|
||||
containingFile: string
|
||||
): ts.ResolvedModule[] {
|
||||
//util.log("resolveModuleNames", { moduleNames, reusedNames });
|
||||
return moduleNames.map(name => {
|
||||
let resolvedFileName;
|
||||
if (name === "deno") {
|
||||
resolvedFileName = resolveModuleName("deno.d.ts", ASSETS);
|
||||
} else if (name === "typescript") {
|
||||
resolvedFileName = resolveModuleName("typescript.d.ts", ASSETS);
|
||||
} else {
|
||||
resolvedFileName = resolveModuleName(name, containingFile);
|
||||
}
|
||||
// According to the interface we shouldn't return `undefined` but if we
|
||||
// fail to return the same length of modules to those we cannot resolve
|
||||
// then TypeScript fails on an assertion that the lengths can't be
|
||||
// different, so we have to return an "empty" resolved module
|
||||
// TODO: all this does is push the problem downstream, and TypeScript
|
||||
// will complain it can't identify the type of the file and throw
|
||||
// a runtime exception, so we need to handle missing modules better
|
||||
resolvedFileName = resolvedFileName || "";
|
||||
// This flags to the compiler to not go looking to transpile functional
|
||||
// code, anything that is in `/$asset$/` is just library code
|
||||
const isExternalLibraryImport = resolvedFileName.startsWith(ASSETS);
|
||||
// TODO: we should be returning a ts.ResolveModuleFull
|
||||
return { resolvedFileName, isExternalLibraryImport };
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const formatDiagnosticsHost: ts.FormatDiagnosticsHost = {
|
||||
getCurrentDirectory(): string {
|
||||
return ".";
|
||||
},
|
||||
getCanonicalFileName(fileName: string): string {
|
||||
return fileName;
|
||||
},
|
||||
getNewLine(): string {
|
||||
return EOL;
|
||||
}
|
||||
};
|
|
@ -11,6 +11,7 @@
|
|||
},
|
||||
"files": [
|
||||
"../node_modules/typescript/lib/lib.esnext.d.ts",
|
||||
"./compiler.ts",
|
||||
"./deno.ts",
|
||||
"./globals.ts"
|
||||
]
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
import { test, assert, assertEqual } from "./testing/testing.ts";
|
||||
import { readFileSync } from "deno";
|
||||
|
||||
import "./compiler_test.ts";
|
||||
|
||||
test(async function tests_test() {
|
||||
assert(true);
|
||||
});
|
||||
|
|
17
tests/013_dynamic_import.ts
Normal file
17
tests/013_dynamic_import.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
(async () => {
|
||||
const {
|
||||
returnsHi,
|
||||
returnsFoo2,
|
||||
printHello3
|
||||
} = await import("./subdir/mod1.ts");
|
||||
|
||||
printHello3();
|
||||
|
||||
if (returnsHi() !== "Hi") {
|
||||
throw Error("Unexpected");
|
||||
}
|
||||
|
||||
if (returnsFoo2() !== "Foo") {
|
||||
throw Error("Unexpected");
|
||||
}
|
||||
})();
|
1
tests/013_dynamic_import.ts.out
Normal file
1
tests/013_dynamic_import.ts.out
Normal file
|
@ -0,0 +1 @@
|
|||
Hello
|
|
@ -3,8 +3,7 @@ before error
|
|||
Error: error
|
||||
at foo ([WILDCARD]tests/async_error.ts:4:9)
|
||||
at eval ([WILDCARD]tests/async_error.ts:7:1)
|
||||
at eval (<anonymous>)
|
||||
at execute (deno/js/runtime.ts:[WILDCARD])
|
||||
at FileModule.compileAndRun (deno/js/runtime.ts:[WILDCARD])
|
||||
at DenoCompiler.eval [as _globalEval] (<anonymous>)
|
||||
at DenoCompiler.run (deno/js/compiler.ts:[WILDCARD])
|
||||
at denoMain (deno/js/main.ts:[WILDCARD])
|
||||
at deno_main.js:1:1
|
||||
|
|
|
@ -2,8 +2,7 @@ Error: bad
|
|||
at foo (file://[WILDCARD]tests/error_001.ts:2:9)
|
||||
at bar (file://[WILDCARD]tests/error_001.ts:6:3)
|
||||
at eval (file://[WILDCARD]tests/error_001.ts:9:1)
|
||||
at eval (<anonymous>)
|
||||
at execute (deno/js/runtime.ts:[WILDCARD])
|
||||
at FileModule.compileAndRun (deno/js/runtime.ts:[WILDCARD])
|
||||
at DenoCompiler.eval [as _globalEval] (<anonymous>)
|
||||
at DenoCompiler.run (deno/js/compiler.ts:[WILDCARD])
|
||||
at denoMain (deno/js/main.ts:[WILDCARD])
|
||||
at deno_main.js:1:1
|
||||
|
|
|
@ -2,10 +2,9 @@ Error: exception from mod1
|
|||
at Object.throwsError (file://[WILDCARD]tests/subdir/mod1.ts:16:9)
|
||||
at foo (file://[WILDCARD]tests/error_002.ts:4:3)
|
||||
at eval (file://[WILDCARD]tests/error_002.ts:7:1)
|
||||
at localDefine (deno/js/runtime.ts:[WILDCARD])
|
||||
at localDefine (deno/js/compiler.ts:[WILDCARD])
|
||||
at eval ([WILDCARD]tests/error_002.ts, <anonymous>)
|
||||
at eval (<anonymous>)
|
||||
at execute (deno/js/runtime.ts:[WILDCARD])
|
||||
at FileModule.compileAndRun (deno/js/runtime.ts:[WILDCARD])
|
||||
at DenoCompiler.eval [as _globalEval] (<anonymous>)
|
||||
at DenoCompiler.run (deno/js/compiler.ts:[WILDCARD])
|
||||
at denoMain (deno/js/main.ts:[WILDCARD])
|
||||
at deno_main.js:1:1
|
||||
|
|
2
tests/error_003_typescript.ts
Normal file
2
tests/error_003_typescript.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
// console.log intentionally misspelled to trigger TypeScript error
|
||||
consol.log("hello world!");
|
10
tests/error_003_typescript.ts.out
Normal file
10
tests/error_003_typescript.ts.out
Normal file
|
@ -0,0 +1,10 @@
|
|||
[96m[WILDCARD]tests/error_003_typescript.ts[WILDCARD] - [91merror[0m[90m TS2552: [0mCannot find name 'consol'. Did you mean 'console'?
|
||||
|
||||
[30;47m[WILDCARD][0m consol.log("hello world!");
|
||||
[30;47m [0m [91m~~~~~~[0m
|
||||
|
||||
[96m$asset$/globals.d.ts[WILDCARD]
|
||||
[30;47m[WILDCARD][0m const console: Console;
|
||||
[30;47m [0m [96m ~~~~~~~[0m
|
||||
'console' is declared here.
|
||||
|
1
tests/error_004_missing_module.ts
Normal file
1
tests/error_004_missing_module.ts
Normal file
|
@ -0,0 +1 @@
|
|||
import * as badModule from "bad-module.ts";
|
12
tests/error_004_missing_module.ts.out
Normal file
12
tests/error_004_missing_module.ts.out
Normal file
|
@ -0,0 +1,12 @@
|
|||
Error: Cannot resolve module "bad-module.ts" from "[WILDCARD]error_004_missing_module.ts".
|
||||
os.codeFetch message: [WILDCARD] (os error 2)
|
||||
at throwResolutionError (deno/js/compiler.ts:[WILDCARD])
|
||||
at DenoCompiler.resolveModule (deno/js/compiler.ts:[WILDCARD])
|
||||
at DenoCompiler.resolveModuleName (deno/js/compiler.ts:[WILDCARD])
|
||||
at moduleNames.map.name (deno/js/compiler.ts:[WILDCARD])
|
||||
at Array.map (<anonymous>)
|
||||
at DenoCompiler.resolveModuleNames (deno/js/compiler.ts:[WILDCARD])
|
||||
at Object.compilerHost.resolveModuleNames (deno/third_party/node_modules/typescript/lib/typescript.js:[WILDCARD])
|
||||
at resolveModuleNamesWorker (deno/third_party/node_modules/typescript/lib/typescript.js:[WILDCARD])
|
||||
at resolveModuleNamesReusingOldState (deno/third_party/node_modules/typescript/lib/typescript.js:[WILDCARD])
|
||||
at processImportedModules (deno/third_party/node_modules/typescript/lib/typescript.js:[WILDCARD])
|
Loading…
Reference in a new issue