mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
Two-pass module evaluation.
Plus changes to tests to accomodate.
This commit is contained in:
parent
9960b1d22b
commit
54aefa2886
5 changed files with 330 additions and 210 deletions
244
js/compiler.ts
244
js/compiler.ts
|
@ -18,7 +18,12 @@ type AmdCallback = (...args: any[]) => void;
|
||||||
type AmdErrback = (err: any) => void;
|
type AmdErrback = (err: any) => void;
|
||||||
export type AmdFactory = (...args: any[]) => object | void;
|
export type AmdFactory = (...args: any[]) => object | void;
|
||||||
// tslint:enable:no-any
|
// tslint:enable:no-any
|
||||||
export type AmdDefine = (deps: string[], factory: AmdFactory) => void;
|
export type AmdDefine = (deps: ModuleSpecifier[], factory: AmdFactory) => void;
|
||||||
|
type AMDRequire = (
|
||||||
|
deps: ModuleSpecifier[],
|
||||||
|
callback: AmdCallback,
|
||||||
|
errback: AmdErrback
|
||||||
|
) => void;
|
||||||
|
|
||||||
// The location that a module is being loaded from. This could be a directory,
|
// The location that a module is being loaded from. This could be a directory,
|
||||||
// like ".", or it could be a module specifier like
|
// like ".", or it could be a module specifier like
|
||||||
|
@ -60,7 +65,10 @@ export interface Ts {
|
||||||
* the module, not the actual module instance.
|
* the module, not the actual module instance.
|
||||||
*/
|
*/
|
||||||
export class ModuleMetaData implements ts.IScriptSnapshot {
|
export class ModuleMetaData implements ts.IScriptSnapshot {
|
||||||
|
public deps?: ModuleFileName[];
|
||||||
public readonly exports = {};
|
public readonly exports = {};
|
||||||
|
public factory?: AmdFactory;
|
||||||
|
public hasRun = false;
|
||||||
public scriptVersion = "";
|
public scriptVersion = "";
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -155,6 +163,9 @@ export class DenoCompiler implements ts.LanguageServiceHost {
|
||||||
// A reference to the `./os.ts` module, so it can be monkey patched during
|
// A reference to the `./os.ts` module, so it can be monkey patched during
|
||||||
// testing
|
// testing
|
||||||
private _os: Os = os;
|
private _os: Os = os;
|
||||||
|
// Contains a queue of modules that have been resolved, but not yet
|
||||||
|
// run
|
||||||
|
private _runQueue: ModuleMetaData[] = [];
|
||||||
// Used to contain the script file we are currently running
|
// Used to contain the script file we are currently running
|
||||||
private _scriptFileNames: string[] = [];
|
private _scriptFileNames: string[] = [];
|
||||||
// A reference to the TypeScript LanguageService instance so it can be
|
// A reference to the TypeScript LanguageService instance so it can be
|
||||||
|
@ -167,6 +178,78 @@ export class DenoCompiler implements ts.LanguageServiceHost {
|
||||||
// testing
|
// testing
|
||||||
private _window = window;
|
private _window = window;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drain the run queue, retrieving the arguments for the module
|
||||||
|
* factory and calling the module's factory.
|
||||||
|
*/
|
||||||
|
private _drainRunQueue(): void {
|
||||||
|
this._log(
|
||||||
|
"compiler._drainRunQueue",
|
||||||
|
this._runQueue.map(metaData => metaData.fileName)
|
||||||
|
);
|
||||||
|
let moduleMetaData: ModuleMetaData | undefined;
|
||||||
|
while ((moduleMetaData = this._runQueue.shift())) {
|
||||||
|
assert(
|
||||||
|
moduleMetaData.factory != null,
|
||||||
|
"Cannot run module without factory."
|
||||||
|
);
|
||||||
|
assert(moduleMetaData.hasRun === false, "Module has already been run.");
|
||||||
|
// asserts not tracked by TypeScripts, so using not null operator
|
||||||
|
moduleMetaData.factory!(...this._getFactoryArguments(moduleMetaData));
|
||||||
|
moduleMetaData.hasRun = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the dependencies for a given module, but don't run the module,
|
||||||
|
* just add the module factory to the run queue.
|
||||||
|
*/
|
||||||
|
private _gatherDependencies(moduleMetaData: ModuleMetaData): void {
|
||||||
|
this._log("compiler._resolveDependencies", moduleMetaData.fileName);
|
||||||
|
|
||||||
|
// if the module has already run, we can short circuit.
|
||||||
|
// it is intentional though that if we have already resolved dependencies,
|
||||||
|
// we won't short circuit, as something may have changed, or we might have
|
||||||
|
// only collected the dependencies to be able to able to obtain the graph of
|
||||||
|
// dependencies
|
||||||
|
if (moduleMetaData.hasRun) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._window.define = this.makeDefine(moduleMetaData);
|
||||||
|
this._globalEval(this.compile(moduleMetaData));
|
||||||
|
this._window.define = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the arguments to pass a module's factory function.
|
||||||
|
*/
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
private _getFactoryArguments(moduleMetaData: ModuleMetaData): any[] {
|
||||||
|
if (!moduleMetaData.deps) {
|
||||||
|
throw new Error("Cannot get arguments until dependencies resolved.");
|
||||||
|
}
|
||||||
|
return moduleMetaData.deps.map(dep => {
|
||||||
|
if (dep === "require") {
|
||||||
|
return this._makeLocalRequire(moduleMetaData);
|
||||||
|
}
|
||||||
|
if (dep === "exports") {
|
||||||
|
return moduleMetaData.exports;
|
||||||
|
}
|
||||||
|
if (dep in DenoCompiler._builtins) {
|
||||||
|
return DenoCompiler._builtins[dep];
|
||||||
|
}
|
||||||
|
const dependencyMetaData = this._getModuleMetaData(dep);
|
||||||
|
assert(dependencyMetaData != null, `Missing dependency "${dep}".`);
|
||||||
|
assert(
|
||||||
|
dependencyMetaData!.hasRun === true,
|
||||||
|
`Module "${dep}" was not run.`
|
||||||
|
);
|
||||||
|
// TypeScript does not track assert, therefore using not null operator
|
||||||
|
return dependencyMetaData!.exports;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The TypeScript language service often refers to the resolved fileName of
|
* The TypeScript language service often refers to the resolved fileName of
|
||||||
* a module, this is a shortcut to avoid unnecessary module resolution logic
|
* a module, this is a shortcut to avoid unnecessary module resolution logic
|
||||||
|
@ -186,6 +269,35 @@ export class DenoCompiler implements ts.LanguageServiceHost {
|
||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a require that specifically handles the resolution of a transpiled
|
||||||
|
* emit of a dynamic ES `import()` from TypeScript.
|
||||||
|
*/
|
||||||
|
private _makeLocalRequire(moduleMetaData: ModuleMetaData): AMDRequire {
|
||||||
|
const localRequire = (
|
||||||
|
deps: ModuleSpecifier[],
|
||||||
|
callback: AmdCallback,
|
||||||
|
errback: AmdErrback
|
||||||
|
): void => {
|
||||||
|
log("localRequire", deps);
|
||||||
|
assert(
|
||||||
|
deps.length === 1,
|
||||||
|
"Local require requires exactly one dependency."
|
||||||
|
);
|
||||||
|
const [moduleSpecifier] = deps;
|
||||||
|
try {
|
||||||
|
const requiredMetaData = this.run(
|
||||||
|
moduleSpecifier,
|
||||||
|
moduleMetaData.fileName
|
||||||
|
);
|
||||||
|
callback(requiredMetaData.exports);
|
||||||
|
} catch (e) {
|
||||||
|
errback(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return localRequire;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup being able to map back source references back to their source
|
* Setup being able to map back source references back to their source
|
||||||
*
|
*
|
||||||
|
@ -232,7 +344,12 @@ export class DenoCompiler implements ts.LanguageServiceHost {
|
||||||
/**
|
/**
|
||||||
* Retrieve the output of the TypeScript compiler for a given `fileName`.
|
* Retrieve the output of the TypeScript compiler for a given `fileName`.
|
||||||
*/
|
*/
|
||||||
compile(fileName: ModuleFileName): OutputCode {
|
compile(moduleMetaData: ModuleMetaData): OutputCode {
|
||||||
|
this._log("compiler.compile", moduleMetaData.fileName);
|
||||||
|
if (moduleMetaData.outputCode) {
|
||||||
|
return moduleMetaData.outputCode;
|
||||||
|
}
|
||||||
|
const { fileName, sourceCode } = moduleMetaData;
|
||||||
const service = this._service;
|
const service = this._service;
|
||||||
const output = service.getEmitOutput(fileName);
|
const output = service.getEmitOutput(fileName);
|
||||||
|
|
||||||
|
@ -263,53 +380,72 @@ export class DenoCompiler implements ts.LanguageServiceHost {
|
||||||
);
|
);
|
||||||
|
|
||||||
const [outputFile] = output.outputFiles;
|
const [outputFile] = output.outputFiles;
|
||||||
return outputFile.text;
|
const outputCode = (moduleMetaData.outputCode = `${
|
||||||
|
outputFile.text
|
||||||
|
}\n//# sourceURL=${fileName}`);
|
||||||
|
moduleMetaData.scriptVersion = "1";
|
||||||
|
this._os.codeCache(fileName, sourceCode, outputCode);
|
||||||
|
return moduleMetaData.outputCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For a given module specifier and containing file, return a list of absolute
|
||||||
|
* identifiers for dependent modules that are required by this module.
|
||||||
|
*/
|
||||||
|
getModuleDependencies(
|
||||||
|
moduleSpecifier: ModuleSpecifier,
|
||||||
|
containingFile: ContainingFile
|
||||||
|
): ModuleFileName[] {
|
||||||
|
assert(
|
||||||
|
this._runQueue.length === 0,
|
||||||
|
"Cannot get dependencies with modules queued to be run."
|
||||||
|
);
|
||||||
|
const moduleMetaData = this.resolveModule(moduleSpecifier, containingFile);
|
||||||
|
assert(
|
||||||
|
!moduleMetaData.hasRun,
|
||||||
|
"Cannot get dependencies for a module that has already been run."
|
||||||
|
);
|
||||||
|
this._gatherDependencies(moduleMetaData);
|
||||||
|
const dependencies = this._runQueue.map(
|
||||||
|
moduleMetaData => moduleMetaData.fileName
|
||||||
|
);
|
||||||
|
// empty the run queue, to free up references to factories we have collected
|
||||||
|
// and to ensure that if there is a further invocation of `.run()` the
|
||||||
|
// factories don't get called
|
||||||
|
this._runQueue = [];
|
||||||
|
return dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a localized AMD `define` function and return it.
|
* Create a localized AMD `define` function and return it.
|
||||||
*/
|
*/
|
||||||
makeDefine(moduleMetaData: ModuleMetaData): AmdDefine {
|
makeDefine(moduleMetaData: ModuleMetaData): AmdDefine {
|
||||||
const localDefine = (deps: string[], factory: AmdFactory): void => {
|
// TODO should this really be part of the public API of the compiler?
|
||||||
// TypeScript will emit a local require dependency when doing dynamic
|
const localDefine: AmdDefine = (
|
||||||
// `import()`
|
deps: ModuleSpecifier[],
|
||||||
const { _log: log } = this;
|
factory: AmdFactory
|
||||||
const localExports = moduleMetaData.exports;
|
): void => {
|
||||||
|
this._log("compiler.localDefine", moduleMetaData.fileName);
|
||||||
// tslint:disable-next-line:no-any
|
moduleMetaData.factory = factory;
|
||||||
const resolveDependencies = (deps: string[]): any[] => {
|
// we will recursively resolve the dependencies for any modules
|
||||||
return deps.map(dep => {
|
moduleMetaData.deps = deps.map(dep => {
|
||||||
if (dep === "require") {
|
if (
|
||||||
return localRequire;
|
dep === "require" ||
|
||||||
} else if (dep === "exports") {
|
dep === "exports" ||
|
||||||
return localExports;
|
dep in DenoCompiler._builtins
|
||||||
} else if (dep in DenoCompiler._builtins) {
|
) {
|
||||||
return DenoCompiler._builtins[dep];
|
return dep;
|
||||||
} else {
|
|
||||||
const depModuleMetaData = this.run(dep, moduleMetaData.fileName);
|
|
||||||
return depModuleMetaData.exports;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// this is a function because we need hoisting
|
|
||||||
function localRequire(
|
|
||||||
deps: string[],
|
|
||||||
callback: AmdCallback,
|
|
||||||
errback: AmdErrback
|
|
||||||
): void {
|
|
||||||
log("localRequire", deps);
|
|
||||||
try {
|
|
||||||
const args = resolveDependencies(deps);
|
|
||||||
callback(...args);
|
|
||||||
} catch (e) {
|
|
||||||
errback(e);
|
|
||||||
}
|
}
|
||||||
|
const dependencyMetaData = this.resolveModule(
|
||||||
|
dep,
|
||||||
|
moduleMetaData.fileName
|
||||||
|
);
|
||||||
|
this._gatherDependencies(dependencyMetaData);
|
||||||
|
return dependencyMetaData.fileName;
|
||||||
|
});
|
||||||
|
if (!this._runQueue.includes(moduleMetaData)) {
|
||||||
|
this._runQueue.push(moduleMetaData);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._log("localDefine", moduleMetaData.fileName, deps, localExports);
|
|
||||||
const args = resolveDependencies(deps);
|
|
||||||
factory(...args);
|
|
||||||
};
|
};
|
||||||
return localDefine;
|
return localDefine;
|
||||||
}
|
}
|
||||||
|
@ -404,32 +540,22 @@ export class DenoCompiler implements ts.LanguageServiceHost {
|
||||||
return moduleMetaData ? moduleMetaData.fileName : undefined;
|
return moduleMetaData ? moduleMetaData.fileName : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tslint:disable-next-line:no-any */
|
|
||||||
/**
|
/**
|
||||||
* Execute a module based on the `moduleSpecifier` and the `containingFile`
|
* Load and run a module and all of its dependencies based on a module
|
||||||
* and return the resulting `FileModule`.
|
* specifier and a containing file
|
||||||
*/
|
*/
|
||||||
run(
|
run(
|
||||||
moduleSpecifier: ModuleSpecifier,
|
moduleSpecifier: ModuleSpecifier,
|
||||||
containingFile: ContainingFile
|
containingFile: ContainingFile
|
||||||
): ModuleMetaData {
|
): ModuleMetaData {
|
||||||
this._log("run", { moduleSpecifier, containingFile });
|
this._log("compiler.run", { moduleSpecifier, containingFile });
|
||||||
const moduleMetaData = this.resolveModule(moduleSpecifier, containingFile);
|
const moduleMetaData = this.resolveModule(moduleSpecifier, containingFile);
|
||||||
const fileName = moduleMetaData.fileName;
|
this._scriptFileNames = [moduleMetaData.fileName];
|
||||||
this._scriptFileNames = [fileName];
|
if (!moduleMetaData.deps) {
|
||||||
const sourceCode = moduleMetaData.sourceCode;
|
this._gatherDependencies(moduleMetaData);
|
||||||
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._drainRunQueue();
|
||||||
this._globalEval(moduleMetaData.outputCode);
|
return moduleMetaData;
|
||||||
this._window.define = undefined;
|
|
||||||
return moduleMetaData!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -6,7 +6,7 @@ import * as ts from "typescript";
|
||||||
// We use a silly amount of `any` in these tests...
|
// We use a silly amount of `any` in these tests...
|
||||||
// tslint:disable:no-any
|
// tslint:disable:no-any
|
||||||
|
|
||||||
const { DenoCompiler, ModuleMetaData } = compiler;
|
const { DenoCompiler } = compiler;
|
||||||
|
|
||||||
// Enums like this don't exist at runtime, so local copy
|
// Enums like this don't exist at runtime, so local copy
|
||||||
enum ScriptKind {
|
enum ScriptKind {
|
||||||
|
@ -93,13 +93,7 @@ const moduleMap: {
|
||||||
"foo/baz",
|
"foo/baz",
|
||||||
"/root/project/foo/baz.ts",
|
"/root/project/foo/baz.ts",
|
||||||
fooBazTsSource,
|
fooBazTsSource,
|
||||||
null
|
fooBazTsOutput
|
||||||
),
|
|
||||||
"foo/qat.ts": mockModuleInfo(
|
|
||||||
"foo/qat",
|
|
||||||
"/root/project/foo/qat.ts",
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
"/root/project/foo/baz.ts": {
|
"/root/project/foo/baz.ts": {
|
||||||
|
@ -108,12 +102,6 @@ const moduleMap: {
|
||||||
"/root/project/foo/bar.ts",
|
"/root/project/foo/bar.ts",
|
||||||
fooBarTsSource,
|
fooBarTsSource,
|
||||||
fooBarTsOutput
|
fooBarTsOutput
|
||||||
),
|
|
||||||
"./qat.ts": mockModuleInfo(
|
|
||||||
"foo/qat",
|
|
||||||
"/root/project/foo/qat.ts",
|
|
||||||
"export const foo = 'bar'",
|
|
||||||
null
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -135,21 +123,13 @@ let codeFetchStack: Array<{
|
||||||
containingFile: string;
|
containingFile: string;
|
||||||
}> = [];
|
}> = [];
|
||||||
|
|
||||||
function reset() {
|
let mockDepsStack: string[][] = [];
|
||||||
codeFetchStack = [];
|
let mockFactoryStack: compiler.AmdFactory[] = [];
|
||||||
codeCacheStack = [];
|
|
||||||
logStack = [];
|
|
||||||
getEmitOutputStack = [];
|
|
||||||
globalEvalStack = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
let mockDeps: string[] | undefined;
|
|
||||||
let mockFactory: compiler.AmdFactory;
|
|
||||||
|
|
||||||
function globalEvalMock(x: string): void {
|
function globalEvalMock(x: string): void {
|
||||||
globalEvalStack.push(x);
|
globalEvalStack.push(x);
|
||||||
if (windowMock.define && mockDeps && mockFactory) {
|
if (windowMock.define && mockDepsStack.length && mockFactoryStack.length) {
|
||||||
windowMock.define(mockDeps, mockFactory);
|
windowMock.define(mockDepsStack.pop(), mockFactoryStack.pop());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function logMock(...args: any[]): void {
|
function logMock(...args: any[]): void {
|
||||||
|
@ -231,16 +211,37 @@ const mocks = {
|
||||||
_window: windowMock
|
_window: windowMock
|
||||||
};
|
};
|
||||||
|
|
||||||
// Setup the mocks
|
/**
|
||||||
test(function compilerTestsSetup() {
|
* Setup the mocks for a test
|
||||||
assert("_globalEval" in compilerInstance);
|
*/
|
||||||
assert("_log" in compilerInstance);
|
function setup() {
|
||||||
assert("_os" in compilerInstance);
|
// monkey patch mocks on instance
|
||||||
assert("_ts" in compilerInstance);
|
|
||||||
assert("_service" in compilerInstance);
|
|
||||||
assert("_window" in compilerInstance);
|
|
||||||
Object.assign(compilerInstance, mocks);
|
Object.assign(compilerInstance, mocks);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teardown the mocks for a test
|
||||||
|
*/
|
||||||
|
function teardown() {
|
||||||
|
// reset compiler internal state
|
||||||
|
(compilerInstance as any)._moduleMetaDataMap.clear();
|
||||||
|
(compilerInstance as any)._fileNamesMap.clear();
|
||||||
|
|
||||||
|
// reset mock states
|
||||||
|
codeFetchStack = [];
|
||||||
|
codeCacheStack = [];
|
||||||
|
logStack = [];
|
||||||
|
getEmitOutputStack = [];
|
||||||
|
globalEvalStack = [];
|
||||||
|
|
||||||
|
assertEqual(mockDepsStack.length, 0);
|
||||||
|
assertEqual(mockFactoryStack.length, 0);
|
||||||
|
mockDepsStack = [];
|
||||||
|
mockFactoryStack = [];
|
||||||
|
|
||||||
|
// restore original properties and methods
|
||||||
|
Object.assign(compilerInstance, originals);
|
||||||
|
}
|
||||||
|
|
||||||
test(function compilerInstance() {
|
test(function compilerInstance() {
|
||||||
assert(DenoCompiler != null);
|
assert(DenoCompiler != null);
|
||||||
|
@ -249,120 +250,105 @@ test(function compilerInstance() {
|
||||||
|
|
||||||
// Testing the internal APIs
|
// 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 compilerLocalRequire() {
|
|
||||||
const moduleMetaData = new ModuleMetaData(
|
|
||||||
"/root/project/foo/baz.ts",
|
|
||||||
fooBazTsSource,
|
|
||||||
fooBazTsOutput
|
|
||||||
);
|
|
||||||
const localDefine = compilerInstance.makeDefine(moduleMetaData);
|
|
||||||
let requireCallbackCalled = false;
|
|
||||||
localDefine(
|
|
||||||
["require", "exports"],
|
|
||||||
(_require, _exports, _compiler): void => {
|
|
||||||
assertEqual(typeof _require, "function");
|
|
||||||
_require(
|
|
||||||
["./qat.ts"],
|
|
||||||
_qat => {
|
|
||||||
requireCallbackCalled = true;
|
|
||||||
assert(_qat);
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
throw new Error("Should not error");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
assert(requireCallbackCalled, "Factory expected to be called");
|
|
||||||
});
|
|
||||||
|
|
||||||
test(function compilerRun() {
|
test(function compilerRun() {
|
||||||
// equal to `deno foo/bar.ts`
|
// equal to `deno foo/bar.ts`
|
||||||
reset();
|
setup();
|
||||||
const result = compilerInstance.run("foo/bar.ts", "/root/project");
|
let factoryRun = false;
|
||||||
assert(result instanceof ModuleMetaData);
|
mockDepsStack.push(["require", "exports", "compiler"]);
|
||||||
assertEqual(codeFetchStack.length, 1);
|
mockFactoryStack.push((_require, _exports, _compiler) => {
|
||||||
assertEqual(codeCacheStack.length, 1);
|
factoryRun = true;
|
||||||
assertEqual(globalEvalStack.length, 1);
|
assertEqual(typeof _require, "function");
|
||||||
|
assertEqual(typeof _exports, "object");
|
||||||
|
assert(_compiler === compiler);
|
||||||
|
_exports.foo = "bar";
|
||||||
|
});
|
||||||
|
const moduleMetaData = compilerInstance.run("foo/bar.ts", "/root/project");
|
||||||
|
assert(factoryRun);
|
||||||
|
assert(moduleMetaData.hasRun);
|
||||||
|
assertEqual(moduleMetaData.sourceCode, fooBarTsSource);
|
||||||
|
assertEqual(moduleMetaData.outputCode, fooBarTsOutput);
|
||||||
|
assertEqual(moduleMetaData.exports, { foo: "bar" });
|
||||||
|
|
||||||
const lastGlobalEval = globalEvalStack.pop();
|
assertEqual(
|
||||||
assertEqual(lastGlobalEval, fooBarTsOutput);
|
codeFetchStack.length,
|
||||||
const lastCodeFetch = codeFetchStack.pop();
|
1,
|
||||||
assertEqual(lastCodeFetch, {
|
"Module should have only been fetched once."
|
||||||
moduleSpecifier: "foo/bar.ts",
|
);
|
||||||
containingFile: "/root/project"
|
assertEqual(
|
||||||
});
|
codeCacheStack.length,
|
||||||
const lastCodeCache = codeCacheStack.pop();
|
1,
|
||||||
assertEqual(lastCodeCache, {
|
"Compiled code should have only been cached once."
|
||||||
fileName: "/root/project/foo/bar.ts",
|
);
|
||||||
sourceCode: fooBarTsSource,
|
teardown();
|
||||||
outputCode: fooBarTsOutput
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function compilerRunMultiModule() {
|
test(function compilerRunMultiModule() {
|
||||||
// equal to `deno foo/baz.ts`
|
// equal to `deno foo/baz.ts`
|
||||||
reset();
|
setup();
|
||||||
let factoryRun = false;
|
const factoryStack: string[] = [];
|
||||||
mockDeps = ["require", "exports", "compiler"];
|
const bazDeps = ["require", "exports", "./bar.ts"];
|
||||||
mockFactory = (...deps: any[]) => {
|
const bazFactory = (_require, _exports, _bar) => {
|
||||||
const [_require, _exports, _compiler] = deps;
|
factoryStack.push("baz");
|
||||||
assertEqual(typeof _require, "function");
|
assertEqual(_bar.foo, "bar");
|
||||||
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 barDeps = ["require", "exports", "compiler"];
|
||||||
|
const barFactory = (_require, _exports, _compiler) => {
|
||||||
|
factoryStack.push("bar");
|
||||||
|
_exports.foo = "bar";
|
||||||
|
};
|
||||||
|
mockDepsStack.push(barDeps);
|
||||||
|
mockFactoryStack.push(barFactory);
|
||||||
|
mockDepsStack.push(bazDeps);
|
||||||
|
mockFactoryStack.push(bazFactory);
|
||||||
|
compilerInstance.run("foo/baz.ts", "/root/project");
|
||||||
|
assertEqual(factoryStack, ["bar", "baz"]);
|
||||||
|
|
||||||
const result = compilerInstance.run("foo/baz.ts", "/root/project");
|
assertEqual(
|
||||||
assert(result instanceof ModuleMetaData);
|
codeFetchStack.length,
|
||||||
// we have mocked that foo/bar.ts is already cached, so two fetches,
|
2,
|
||||||
// but only a single cache
|
"Modules should have only been fetched once."
|
||||||
assertEqual(codeFetchStack.length, 2);
|
);
|
||||||
assertEqual(codeCacheStack.length, 1);
|
assertEqual(codeCacheStack.length, 0, "No code should have been cached.");
|
||||||
// because of the challenges with the way the module factories are generated
|
teardown();
|
||||||
// we only get one invocation of the `globalEval` mock.
|
});
|
||||||
assertEqual(globalEvalStack.length, 1);
|
|
||||||
assert(factoryRun);
|
test(function compilerResolveModule() {
|
||||||
|
setup();
|
||||||
|
const moduleMetaData = compilerInstance.resolveModule(
|
||||||
|
"foo/baz.ts",
|
||||||
|
"/root/project"
|
||||||
|
);
|
||||||
|
assertEqual(moduleMetaData.sourceCode, fooBazTsSource);
|
||||||
|
assertEqual(moduleMetaData.outputCode, fooBazTsOutput);
|
||||||
|
assert(!moduleMetaData.hasRun);
|
||||||
|
assert(!moduleMetaData.deps);
|
||||||
|
assertEqual(moduleMetaData.exports, {});
|
||||||
|
assertEqual(moduleMetaData.scriptVersion, "1");
|
||||||
|
|
||||||
|
assertEqual(codeFetchStack.length, 1, "Only initial module is resolved.");
|
||||||
|
teardown();
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function compilerGetModuleDependencies() {
|
||||||
|
setup();
|
||||||
|
const bazDeps = ["require", "exports", "./bar.ts"];
|
||||||
|
const bazFactory = () => {
|
||||||
|
throw new Error("Unexpected factory call");
|
||||||
|
};
|
||||||
|
const barDeps = ["require", "exports", "compiler"];
|
||||||
|
const barFactory = () => {
|
||||||
|
throw new Error("Unexpected factory call");
|
||||||
|
};
|
||||||
|
mockDepsStack.push(barDeps);
|
||||||
|
mockFactoryStack.push(barFactory);
|
||||||
|
mockDepsStack.push(bazDeps);
|
||||||
|
mockFactoryStack.push(bazFactory);
|
||||||
|
const deps = compilerInstance.getModuleDependencies(
|
||||||
|
"foo/baz.ts",
|
||||||
|
"/root/project"
|
||||||
|
);
|
||||||
|
assertEqual(deps, ["/root/project/foo/bar.ts", "/root/project/foo/baz.ts"]);
|
||||||
|
teardown();
|
||||||
});
|
});
|
||||||
|
|
||||||
// TypeScript LanguageServiceHost APIs
|
// TypeScript LanguageServiceHost APIs
|
||||||
|
@ -388,10 +374,12 @@ test(function compilerGetNewLine() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function compilerGetScriptFileNames() {
|
test(function compilerGetScriptFileNames() {
|
||||||
|
setup();
|
||||||
compilerInstance.run("foo/bar.ts", "/root/project");
|
compilerInstance.run("foo/bar.ts", "/root/project");
|
||||||
const result = compilerInstance.getScriptFileNames();
|
const result = compilerInstance.getScriptFileNames();
|
||||||
assertEqual(result.length, 1, "Expected only a single filename.");
|
assertEqual(result.length, 1, "Expected only a single filename.");
|
||||||
assertEqual(result[0], "/root/project/foo/bar.ts");
|
assertEqual(result[0], "/root/project/foo/bar.ts");
|
||||||
|
teardown();
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function compilerGetScriptKind() {
|
test(function compilerGetScriptKind() {
|
||||||
|
@ -403,15 +391,18 @@ test(function compilerGetScriptKind() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function compilerGetScriptVersion() {
|
test(function compilerGetScriptVersion() {
|
||||||
|
setup();
|
||||||
const moduleMetaData = compilerInstance.resolveModule(
|
const moduleMetaData = compilerInstance.resolveModule(
|
||||||
"foo/bar.ts",
|
"foo/bar.ts",
|
||||||
"/root/project"
|
"/root/project"
|
||||||
);
|
);
|
||||||
|
compilerInstance.compile(moduleMetaData);
|
||||||
assertEqual(
|
assertEqual(
|
||||||
compilerInstance.getScriptVersion(moduleMetaData.fileName),
|
compilerInstance.getScriptVersion(moduleMetaData.fileName),
|
||||||
"1",
|
"1",
|
||||||
"Expected known module to have script version of 1"
|
"Expected known module to have script version of 1"
|
||||||
);
|
);
|
||||||
|
teardown();
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function compilerGetScriptVersionUnknown() {
|
test(function compilerGetScriptVersionUnknown() {
|
||||||
|
@ -423,6 +414,7 @@ test(function compilerGetScriptVersionUnknown() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function compilerGetScriptSnapshot() {
|
test(function compilerGetScriptSnapshot() {
|
||||||
|
setup();
|
||||||
const moduleMetaData = compilerInstance.resolveModule(
|
const moduleMetaData = compilerInstance.resolveModule(
|
||||||
"foo/bar.ts",
|
"foo/bar.ts",
|
||||||
"/root/project"
|
"/root/project"
|
||||||
|
@ -444,6 +436,7 @@ test(function compilerGetScriptSnapshot() {
|
||||||
result === moduleMetaData,
|
result === moduleMetaData,
|
||||||
"result should strictly equal moduleMetaData"
|
"result should strictly equal moduleMetaData"
|
||||||
);
|
);
|
||||||
|
teardown();
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function compilerGetCurrentDirectory() {
|
test(function compilerGetCurrentDirectory() {
|
||||||
|
@ -451,10 +444,12 @@ test(function compilerGetCurrentDirectory() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function compilerGetDefaultLibFileName() {
|
test(function compilerGetDefaultLibFileName() {
|
||||||
|
setup();
|
||||||
assertEqual(
|
assertEqual(
|
||||||
compilerInstance.getDefaultLibFileName(),
|
compilerInstance.getDefaultLibFileName(),
|
||||||
"$asset$/lib.globals.d.ts"
|
"$asset$/lib.globals.d.ts"
|
||||||
);
|
);
|
||||||
|
teardown();
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function compilerUseCaseSensitiveFileNames() {
|
test(function compilerUseCaseSensitiveFileNames() {
|
||||||
|
@ -473,6 +468,7 @@ test(function compilerReadFile() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function compilerFileExists() {
|
test(function compilerFileExists() {
|
||||||
|
setup();
|
||||||
const moduleMetaData = compilerInstance.resolveModule(
|
const moduleMetaData = compilerInstance.resolveModule(
|
||||||
"foo/bar.ts",
|
"foo/bar.ts",
|
||||||
"/root/project"
|
"/root/project"
|
||||||
|
@ -483,9 +479,11 @@ test(function compilerFileExists() {
|
||||||
compilerInstance.fileExists("/root/project/unknown-module.ts"),
|
compilerInstance.fileExists("/root/project/unknown-module.ts"),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
teardown();
|
||||||
});
|
});
|
||||||
|
|
||||||
test(function compilerResolveModuleNames() {
|
test(function compilerResolveModuleNames() {
|
||||||
|
setup();
|
||||||
const results = compilerInstance.resolveModuleNames(
|
const results = compilerInstance.resolveModuleNames(
|
||||||
["foo/bar.ts", "foo/baz.ts", "$asset$/lib.globals.d.ts", "deno"],
|
["foo/bar.ts", "foo/baz.ts", "$asset$/lib.globals.d.ts", "deno"],
|
||||||
"/root/project"
|
"/root/project"
|
||||||
|
@ -503,9 +501,5 @@ test(function compilerResolveModuleNames() {
|
||||||
assertEqual(result.resolvedFileName, resolvedFileName);
|
assertEqual(result.resolvedFileName, resolvedFileName);
|
||||||
assertEqual(result.isExternalLibraryImport, isExternalLibraryImport);
|
assertEqual(result.isExternalLibraryImport, isExternalLibraryImport);
|
||||||
}
|
}
|
||||||
});
|
teardown();
|
||||||
|
|
||||||
// Remove the mocks
|
|
||||||
test(function compilerTestsTeardown() {
|
|
||||||
Object.assign(compilerInstance, originals);
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,6 +4,7 @@ Error: error
|
||||||
at foo ([WILDCARD]tests/async_error.ts:4:9)
|
at foo ([WILDCARD]tests/async_error.ts:4:9)
|
||||||
at eval ([WILDCARD]tests/async_error.ts:7:1)
|
at eval ([WILDCARD]tests/async_error.ts:7:1)
|
||||||
at DenoCompiler.eval [as _globalEval] (<anonymous>)
|
at DenoCompiler.eval [as _globalEval] (<anonymous>)
|
||||||
|
at DenoCompiler._gatherDependencies (deno/js/compiler.ts:[WILDCARD])
|
||||||
at DenoCompiler.run (deno/js/compiler.ts:[WILDCARD])
|
at DenoCompiler.run (deno/js/compiler.ts:[WILDCARD])
|
||||||
at denoMain (deno/js/main.ts:[WILDCARD])
|
at denoMain (deno/js/main.ts:[WILDCARD])
|
||||||
at deno_main.js:1:1
|
at deno_main.js:1:1
|
||||||
|
|
|
@ -3,6 +3,7 @@ Error: bad
|
||||||
at bar (file://[WILDCARD]tests/error_001.ts:6:3)
|
at bar (file://[WILDCARD]tests/error_001.ts:6:3)
|
||||||
at eval (file://[WILDCARD]tests/error_001.ts:9:1)
|
at eval (file://[WILDCARD]tests/error_001.ts:9:1)
|
||||||
at DenoCompiler.eval [as _globalEval] (<anonymous>)
|
at DenoCompiler.eval [as _globalEval] (<anonymous>)
|
||||||
|
at DenoCompiler._gatherDependencies (deno/js/compiler.ts:[WILDCARD])
|
||||||
at DenoCompiler.run (deno/js/compiler.ts:[WILDCARD])
|
at DenoCompiler.run (deno/js/compiler.ts:[WILDCARD])
|
||||||
at denoMain (deno/js/main.ts:[WILDCARD])
|
at denoMain (deno/js/main.ts:[WILDCARD])
|
||||||
at deno_main.js:1:1
|
at deno_main.js:1:1
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
Error: exception from mod1
|
Error: exception from mod1
|
||||||
at Object.throwsError (file://[WILDCARD]tests/subdir/mod1.ts:16:9)
|
at Object.throwsError (file://[WILDCARD]deno/tests/subdir/mod1.ts:16:9)
|
||||||
at foo (file://[WILDCARD]tests/error_002.ts:4:3)
|
at foo (file://[WILDCARD]deno/tests/error_002.ts:4:3)
|
||||||
at eval (file://[WILDCARD]tests/error_002.ts:7:1)
|
at ModuleMetaData.eval [as factory ] (file://[WILDCARD]deno/tests/error_002.ts:7:1)
|
||||||
at localDefine (deno/js/compiler.ts:[WILDCARD])
|
at DenoCompiler._drainRunQueue (deno/js/compiler.ts:[WILDCARD])
|
||||||
at eval ([WILDCARD]tests/error_002.ts, <anonymous>)
|
|
||||||
at DenoCompiler.eval [as _globalEval] (<anonymous>)
|
|
||||||
at DenoCompiler.run (deno/js/compiler.ts:[WILDCARD])
|
at DenoCompiler.run (deno/js/compiler.ts:[WILDCARD])
|
||||||
at denoMain (deno/js/main.ts:[WILDCARD])
|
at denoMain (deno/js/main.ts:[WILDCARD])
|
||||||
at deno_main.js:1:1
|
at deno_main.js:1:1
|
||||||
|
|
Loading…
Reference in a new issue