mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Project references WIP
This commit is contained in:
@@ -131,6 +131,7 @@ var harnessSources = harnessCoreSources.concat([
|
||||
"tsconfigParsing.ts",
|
||||
"builder.ts",
|
||||
"commandLineParsing.ts",
|
||||
"projectReferences.ts",
|
||||
"configurationExtension.ts",
|
||||
"convertCompilerOptionsFromJson.ts",
|
||||
"convertTypeAcquisitionFromJson.ts",
|
||||
|
||||
@@ -384,6 +384,17 @@ namespace ts {
|
||||
category: Diagnostics.Module_Resolution_Options,
|
||||
description: Diagnostics.Type_declaration_files_to_be_included_in_compilation
|
||||
},
|
||||
{
|
||||
name: "references",
|
||||
type: "list",
|
||||
element: {
|
||||
name: "references",
|
||||
type: "object"
|
||||
},
|
||||
showInSimplifiedHelpView: true,
|
||||
category: Diagnostics.Module_Resolution_Options,
|
||||
description: Diagnostics.Projects_to_reference
|
||||
},
|
||||
{
|
||||
name: "allowSyntheticDefaultImports",
|
||||
type: "boolean",
|
||||
@@ -902,8 +913,9 @@ namespace ts {
|
||||
*/
|
||||
export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } {
|
||||
const jsonSourceFile = parseJsonText(fileName, jsonText);
|
||||
const config = convertToObject(jsonSourceFile, jsonSourceFile.parseDiagnostics);
|
||||
return {
|
||||
config: convertToObject(jsonSourceFile, jsonSourceFile.parseDiagnostics),
|
||||
config,
|
||||
error: jsonSourceFile.parseDiagnostics.length ? jsonSourceFile.parseDiagnostics[0] : undefined
|
||||
};
|
||||
}
|
||||
@@ -966,6 +978,22 @@ namespace ts {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "references",
|
||||
type: "list",
|
||||
element: {
|
||||
name: "references",
|
||||
type: "object"
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "projects",
|
||||
type: "list",
|
||||
element: {
|
||||
name: "projects",
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "include",
|
||||
type: "list",
|
||||
|
||||
@@ -3326,6 +3326,26 @@
|
||||
"category": "Message",
|
||||
"code": 6186
|
||||
},
|
||||
"Project references may not form a circular graph. Cycle detected: {0}": {
|
||||
"category": "Error",
|
||||
"code": 6187
|
||||
},
|
||||
"Projects to reference": {
|
||||
"category": "Message",
|
||||
"code": 6188
|
||||
},
|
||||
"Referenced project '{0}' must have 'declaration': true": {
|
||||
"category": "Error",
|
||||
"code": 6201
|
||||
},
|
||||
"Referenced project '{0}' must have an explicit 'rootDir' setting": {
|
||||
"category": "Error",
|
||||
"code": 6202
|
||||
},
|
||||
"Output file '{0}' has not been built from source file '{1}'": {
|
||||
"category": "Error",
|
||||
"code": 6203
|
||||
},
|
||||
"Variable '{0}' implicitly has an '{1}' type.": {
|
||||
"category": "Error",
|
||||
"code": 7005
|
||||
|
||||
+138
-7
@@ -333,12 +333,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
output += host.getNewLine();
|
||||
output += `${ relativeFileName }(${ firstLine + 1 },${ firstLineChar + 1 }): `;
|
||||
output += `${relativeFileName}(${firstLine + 1},${firstLineChar + 1}): `;
|
||||
}
|
||||
|
||||
const categoryColor = getCategoryFormat(diagnostic.category);
|
||||
const category = DiagnosticCategory[diagnostic.category].toLowerCase();
|
||||
output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine()) }`;
|
||||
output += `${formatAndReset(category, categoryColor)} TS${diagnostic.code}: ${flattenDiagnosticMessageText(diagnostic.messageText, host.getNewLine())}`;
|
||||
|
||||
if (diagnostic.file) {
|
||||
output += host.getNewLine();
|
||||
@@ -562,6 +562,10 @@ namespace ts {
|
||||
resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(checkAllDefined(typeReferenceDirectiveNames), containingFile, loader);
|
||||
}
|
||||
|
||||
const projectReferenceRedirects = createProjectReferenceRedirects(options);
|
||||
checkProjectReferenceGraph();
|
||||
void getReferencesSyntax;
|
||||
|
||||
// Map from a stringified PackageId to the source file with that id.
|
||||
// Only one source file may have a given packageId. Others become redirects (see createRedirectSourceFile).
|
||||
// `packageIdToSourceFile` is only used while building the program, while `sourceFileToPackageName` and `isSourceFileTargetOfRedirect` are kept around.
|
||||
@@ -1098,7 +1102,7 @@ namespace ts {
|
||||
// If '--lib' is not specified, include default library file according to '--target'
|
||||
// otherwise, using options specified in '--lib' instead of '--target' default library file
|
||||
if (!options.lib) {
|
||||
return compareStrings(file.fileName, getDefaultLibraryFileName(), /*ignoreCase*/ !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
|
||||
return compareStrings(file.fileName, getDefaultLibraryFileName(), /*ignoreCase*/ !host.useCaseSensitiveFileNames()) === Comparison.EqualTo;
|
||||
}
|
||||
else {
|
||||
return forEach(options.lib, libFileName => compareStrings(file.fileName, combinePaths(defaultLibraryPath, libFileName), /*ignoreCase*/ !host.useCaseSensitiveFileNames()) === Comparison.EqualTo);
|
||||
@@ -1695,7 +1699,13 @@ namespace ts {
|
||||
const sourceFile = getSourceFile(fileName);
|
||||
if (fail) {
|
||||
if (!sourceFile) {
|
||||
fail(Diagnostics.File_0_not_found, fileName);
|
||||
const redirect = getProjectReferenceRedirect(fileName);
|
||||
if (redirect) {
|
||||
fail(Diagnostics.Output_file_0_has_not_been_built_from_source_file_1, redirect, fileName);
|
||||
}
|
||||
else {
|
||||
fail(Diagnostics.File_0_not_found, fileName);
|
||||
}
|
||||
}
|
||||
else if (refFile && host.getCanonicalFileName(fileName) === host.getCanonicalFileName(refFile.fileName)) {
|
||||
fail(Diagnostics.A_file_cannot_have_a_reference_to_itself);
|
||||
@@ -1791,6 +1801,9 @@ namespace ts {
|
||||
return file;
|
||||
}
|
||||
|
||||
const redirect = getProjectReferenceRedirect(fileName);
|
||||
fileName = redirect || fileName;
|
||||
|
||||
// We haven't looked for this file, do so now and cache result
|
||||
const file = host.getSourceFile(fileName, options.target, hostErrorMessage => {
|
||||
if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) {
|
||||
@@ -1860,6 +1873,23 @@ namespace ts {
|
||||
return file;
|
||||
}
|
||||
|
||||
function getProjectReferenceRedirect(fileName: string): string | undefined {
|
||||
const path = toPath(fileName);
|
||||
// If this file is produced by a referenced project, we need to rewrite it to
|
||||
// look in the output folder of the referenced project rather than the input
|
||||
const normalized = getNormalizedAbsolutePath(fileName, path);
|
||||
let result: string | undefined = undefined;
|
||||
projectReferenceRedirects.forEach((v, k) => {
|
||||
if (result !== undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (normalized.indexOf(k) === 0) {
|
||||
result = changeExtension(fileName.replace(k, v), ".d.ts");
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function processReferencedFiles(file: SourceFile, isDefaultLib: boolean) {
|
||||
forEach(file.referencedFiles, ref => {
|
||||
const referencedFileName = resolveTripleslashReference(ref.fileName, file.fileName);
|
||||
@@ -2032,6 +2062,59 @@ namespace ts {
|
||||
return allFilesBelongToPath;
|
||||
}
|
||||
|
||||
function createProjectReferenceRedirects(rootOptions: CompilerOptions): Map<string> {
|
||||
const result = createMap<string>();
|
||||
walkProjectReferenceGraph(host, rootOptions, createMapping);
|
||||
|
||||
function createMapping(_resolvedFile: string, referencedProject: CompilerOptions) {
|
||||
// No rootDir in target set; this will be an error later on in the process
|
||||
if (referencedProject.rootDir === undefined) return;
|
||||
result.set(referencedProject.rootDir, referencedProject.outDir);
|
||||
// If this project uses outFile, add the outFile to our compilation
|
||||
if (referencedProject.outFile) {
|
||||
const outFile = combinePaths(referencedProject.outDir, referencedProject.outFile);
|
||||
processSourceFile(outFile, /*isDefaultLib*/ false, /*packageId*/ undefined);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function checkProjectReferenceGraph() {
|
||||
// Checks the following conditions:
|
||||
// * Any referenced project has declaration: true
|
||||
// * Any referenced project has an explicit rootDir
|
||||
// * No circularities exist
|
||||
// * TODO No project root is a subfolder of any other project root
|
||||
|
||||
const illegalRefs = createMap<true>();
|
||||
const cycleName: string[] = [options.configFilePath || host.getCurrentDirectory()];
|
||||
|
||||
walkProjectReferenceGraph(host, options, checkReference, createDiagnosticForOptionName);
|
||||
|
||||
function checkReference(fileName: string, opts: CompilerOptions) {
|
||||
const normalizedPath = ts.normalizePath(fileName);
|
||||
if (illegalRefs.has(normalizedPath)) {
|
||||
createDiagnosticForOptionName(Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0, cycleName.map(normalizePath).map(s => host.getNewLine() + " " + s).join(" -> "));
|
||||
return;
|
||||
}
|
||||
if (opts === undefined) {
|
||||
Debug.fail("Options cannot be undefined");
|
||||
return;
|
||||
}
|
||||
if (!opts.declaration) {
|
||||
createDiagnosticForOptionName(Diagnostics.Referenced_project_0_must_have_declaration_Colon_true, fileName);
|
||||
}
|
||||
if (!opts.rootDir) {
|
||||
createDiagnosticForOptionName(Diagnostics.Referenced_project_0_must_have_an_explicit_rootDir_setting, fileName);
|
||||
}
|
||||
illegalRefs.set(normalizedPath, true);
|
||||
cycleName.push(normalizedPath);
|
||||
walkProjectReferenceGraph(host, opts, checkReference, createDiagnosticForOptionName);
|
||||
cycleName.pop();
|
||||
illegalRefs.delete(normalizedPath);
|
||||
}
|
||||
}
|
||||
|
||||
function verifyCompilerOptions() {
|
||||
if (options.isolatedModules) {
|
||||
if (options.declaration) {
|
||||
@@ -2277,12 +2360,20 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getOptionPathsSyntax() {
|
||||
function getOptionsSyntaxByName(name: string): object | undefined {
|
||||
const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
|
||||
if (compilerOptionsObjectLiteralSyntax) {
|
||||
return getPropertyAssignment(compilerOptionsObjectLiteralSyntax, "paths");
|
||||
return getPropertyAssignment(compilerOptionsObjectLiteralSyntax, name);
|
||||
}
|
||||
return emptyArray;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getReferencesSyntax(): ObjectLiteralExpression[] | undefined {
|
||||
return getOptionsSyntaxByName("references") as ObjectLiteralExpression[] | undefined;
|
||||
}
|
||||
|
||||
function getOptionPathsSyntax(): PropertyAssignment[] {
|
||||
return getOptionsSyntaxByName("paths") as PropertyAssignment[] || emptyArray;
|
||||
}
|
||||
|
||||
function createDiagnosticForOptionName(message: DiagnosticMessage, option1: string, option2?: string) {
|
||||
@@ -2332,6 +2423,46 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function parseConfigHostFromCompilerHost(host: CompilerHost): ParseConfigHost {
|
||||
return {
|
||||
fileExists: host.fileExists,
|
||||
readDirectory: () => [],
|
||||
readFile: host.readFile,
|
||||
useCaseSensitiveFileNames: host.useCaseSensitiveFileNames()
|
||||
};
|
||||
}
|
||||
|
||||
export function walkProjectReferenceGraph(host: CompilerHost, rootOptions: CompilerOptions,
|
||||
callback: (resolvedFile: string, referencedProject: CompilerOptions) => void,
|
||||
error?: (message: DiagnosticMessage | DiagnosticMessageChain | string, option1?: string) => void) {
|
||||
if (rootOptions.references === undefined) return;
|
||||
|
||||
const configHost = parseConfigHostFromCompilerHost(host);
|
||||
const rootPath = rootOptions.configFilePath ? getDirectoryPath(rootOptions.configFilePath) : host.getCurrentDirectory();
|
||||
for (const ref of rootOptions.references) {
|
||||
let refPath = combinePaths(rootPath, ref.path);
|
||||
if (!host.fileExists(refPath)) {
|
||||
refPath = combinePaths(refPath, "tsconfig.json");
|
||||
}
|
||||
if (!host.fileExists(refPath)) {
|
||||
if (error) {
|
||||
error(Diagnostics.File_0_not_found, refPath);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const referenceJsonSource = parseJsonText(refPath, host.readFile(refPath));
|
||||
const cmdLine = parseJsonSourceFileConfigFileContent(referenceJsonSource, configHost, getDirectoryPath(refPath), /*existingOptions*/ undefined, refPath);
|
||||
cmdLine.options.configFilePath = refPath;
|
||||
if (cmdLine.errors && cmdLine.errors.length) {
|
||||
// TODO: Pass along errors
|
||||
}
|
||||
if (cmdLine.options) {
|
||||
callback(refPath, cmdLine.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
/**
|
||||
* Returns a DiagnosticMessage if we won't include a resolved module due to its extension.
|
||||
|
||||
@@ -3675,7 +3675,11 @@ namespace ts {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[] | null | undefined;
|
||||
export interface ProjectReference {
|
||||
path: string;
|
||||
}
|
||||
|
||||
export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[] | ProjectReference[] | null | undefined;
|
||||
|
||||
export interface CompilerOptions {
|
||||
/*@internal*/ all?: boolean;
|
||||
@@ -3743,6 +3747,7 @@ namespace ts {
|
||||
/* @internal */ pretty?: DiagnosticStyle;
|
||||
reactNamespace?: string;
|
||||
jsxFactory?: string;
|
||||
references?: ProjectReference[];
|
||||
removeComments?: boolean;
|
||||
rootDir?: string;
|
||||
rootDirs?: string[];
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
/// <reference path="../harness.ts" />
|
||||
/// <reference path="../../compiler/commandLineParser.ts" />
|
||||
|
||||
namespace ts {
|
||||
interface TestProjectSpecification {
|
||||
configFileName?: string;
|
||||
references: string[];
|
||||
files: { [fileName: string]: string };
|
||||
outputFiles?: { [fileName: string]: string };
|
||||
options?: Partial<CompilerOptions>;
|
||||
}
|
||||
interface TestSpecification {
|
||||
[path: string]: TestProjectSpecification;
|
||||
}
|
||||
|
||||
function assertHasError(message: string, errors: ReadonlyArray<Diagnostic>, diag: DiagnosticMessage) {
|
||||
if (!errors.some(e => e.code === diag.code)) {
|
||||
const errorString = errors.map(e => ` ${e.file ? e.file.fileName : "[global]"}: ${e.messageText}`).join("\r\n");
|
||||
assert(false, `${message}: Did not find any diagnostic for ${diag.message} in:\r\n${errorString}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertNoErrors(message: string, errors: ReadonlyArray<Diagnostic>) {
|
||||
if (errors && errors.length > 0) {
|
||||
assert(false, `${message}: Expected no errors, but found:\r\n${errors.map(e => ` ${e.messageText}`).join("\r\n")}`);
|
||||
}
|
||||
}
|
||||
|
||||
function combineAllPaths(...paths: string[]) {
|
||||
let result = paths[0];
|
||||
for (let i = 1; i < paths.length; i++) {
|
||||
result = combinePaths(result, paths[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const emptyModule = "export { };";
|
||||
|
||||
/**
|
||||
* Produces the text of a source file which imports all of the
|
||||
* specified module names
|
||||
*/
|
||||
function moduleImporting(...names: string[]) {
|
||||
return names.map((n, i) => `import * as mod_${i} from ${n}`).join("\r\n");
|
||||
}
|
||||
|
||||
function testProjectReferences(spec: TestSpecification, entryPointConfigFileName: string, checkResult: (prog: Program) => void) {
|
||||
const files = createMap<string>();
|
||||
for (const key in spec) {
|
||||
const sp = spec[key];
|
||||
const configFileName = combineAllPaths("/", key, sp.configFileName || "tsconfig.json");
|
||||
const options = {
|
||||
compilerOptions: {
|
||||
references: sp.references.map(r => ({ path: r })),
|
||||
declaration: true,
|
||||
rootDir: ".",
|
||||
outDir: "bin",
|
||||
...sp.options
|
||||
}
|
||||
};
|
||||
const configContent = JSON.stringify(options);
|
||||
const outDir = options.compilerOptions.outDir;
|
||||
files.set(configFileName, configContent);
|
||||
for (const sourceFile of Object.keys(sp.files)) {
|
||||
files.set(sourceFile, sp.files[sourceFile]);
|
||||
}
|
||||
if (sp.outputFiles) {
|
||||
for (const outFile of Object.keys(sp.outputFiles)) {
|
||||
files.set(combineAllPaths("/", key, outDir, outFile), sp.outputFiles[outFile]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const host = new Utils.MockProjectReferenceCompilerHost("/", /*useCaseSensitiveFileNames*/ true, files);
|
||||
|
||||
const { config, error } = ts.readConfigFile(entryPointConfigFileName, name => host.readFile(name));
|
||||
|
||||
// We shouldn't have any errors about invalid tsconfig files in these tests
|
||||
assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n"));
|
||||
const file = ts.parseJsonConfigFileContent(config, host.configHost, getDirectoryPath(entryPointConfigFileName), {}, entryPointConfigFileName);
|
||||
file.options.configFilePath = entryPointConfigFileName;
|
||||
const prog = ts.createProgram(file.fileNames, file.options, host);
|
||||
checkResult(prog);
|
||||
}
|
||||
|
||||
describe("project-references meta check", () => {
|
||||
it("default setup was created correctly", () => {
|
||||
const spec: TestSpecification = {
|
||||
"/primary": {
|
||||
files: { "/primary/a.ts": emptyModule },
|
||||
references: []
|
||||
},
|
||||
"/reference": {
|
||||
files: { "/secondary/b.ts": moduleImporting("../primary/a") },
|
||||
references: ["../primary"]
|
||||
}
|
||||
};
|
||||
testProjectReferences(spec, "/primary/tsconfig.json", prog => {
|
||||
assert.isTrue(!!prog, "Program should exist");
|
||||
assertNoErrors("Sanity check should not produce errors", prog.getOptionsDiagnostics());
|
||||
});
|
||||
});
|
||||
|
||||
it("can detect a circularity error", () => {
|
||||
const spec: TestSpecification = {
|
||||
"/primary": {
|
||||
files: { "/primary/a.ts": emptyModule },
|
||||
references: ["../secondary"]
|
||||
},
|
||||
"/secondary": {
|
||||
files: { "/secondary/b.ts": moduleImporting("../primary/a") },
|
||||
references: ["../primary"]
|
||||
}
|
||||
};
|
||||
testProjectReferences(spec, "/primary/tsconfig.json", prog => {
|
||||
assert.isTrue(!!prog, "Program should exist");
|
||||
assertHasError("Should detect a circular error", prog.getOptionsDiagnostics(), Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Validate that we enforce the basic settings constraints for referenced projects
|
||||
*/
|
||||
describe("project-references constraint checking for settings", () => {
|
||||
const spec: TestSpecification = {
|
||||
"/primary": {
|
||||
files: { "/primary/a.ts": emptyModule },
|
||||
references: ["../secondary"]
|
||||
},
|
||||
"/secondary": {
|
||||
files: { "/secondary/b.ts": moduleImporting("../primary/a") },
|
||||
references: [],
|
||||
options: {
|
||||
declaration: false
|
||||
}
|
||||
}
|
||||
};
|
||||
it("errors when declaration = false", () => {
|
||||
testProjectReferences(spec, "/primary/tsconfig.json", program => {
|
||||
const errs = program.getOptionsDiagnostics();
|
||||
assertHasError("Reports an error about the wrong decl setting", errs, Diagnostics.Referenced_project_0_must_have_declaration_Colon_true);
|
||||
});
|
||||
});
|
||||
|
||||
it("errors when rootDir is not set", () => {
|
||||
spec["/secondary"].options.declaration = true;
|
||||
spec["/secondary"].options.rootDir = undefined;
|
||||
testProjectReferences(spec, "/primary/tsconfig.json", program => {
|
||||
const errs = program.getOptionsDiagnostics();
|
||||
assertHasError("Reports an error about the wrong decl setting", errs, Diagnostics.Referenced_project_0_must_have_an_explicit_rootDir_setting);
|
||||
});
|
||||
});
|
||||
// * TODO No project root is a subfolder of any other project root
|
||||
});
|
||||
|
||||
/**
|
||||
* Circularity checking
|
||||
*/
|
||||
describe("project-references circularity checking", () => {
|
||||
// Bare cycle with relative paths tested in sanity check block
|
||||
it("detects an indirected cycle", () => {
|
||||
const spec: TestSpecification = {
|
||||
"/alpha": {
|
||||
files: { "/alpha/a.ts": emptyModule },
|
||||
references: ["../beta"]
|
||||
},
|
||||
"/beta": {
|
||||
files: { "/beta/b.ts": moduleImporting("../alpha/a") },
|
||||
references: ["../gamma"]
|
||||
},
|
||||
"/gamma": {
|
||||
files: { "/gamma/a.ts": emptyModule },
|
||||
references: ["../alpha"],
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
testProjectReferences(spec, "/alpha/tsconfig.json", program => {
|
||||
const errs = program.getOptionsDiagnostics();
|
||||
assertHasError("Reports an error about the circular diagnsotic", errs, Diagnostics.Project_references_may_not_form_a_circular_graph_Cycle_detected_Colon_0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Path mapping behavior
|
||||
*/
|
||||
describe("project-references path mapping", () => {
|
||||
it("redirects to the output .d.ts file", () => {
|
||||
const spec: TestSpecification = {
|
||||
"/alpha": {
|
||||
files: { "/alpha/a.ts": "export const m: number;" },
|
||||
references: [],
|
||||
outputFiles: { "a.d.ts": emptyModule }
|
||||
},
|
||||
"/beta": {
|
||||
files: { "/beta/b.ts": "import { m } from '../alpha/a'" },
|
||||
references: ["../alpha"]
|
||||
}
|
||||
};
|
||||
testProjectReferences(spec, "/beta/tsconfig.json", program => {
|
||||
assertNoErrors("File setup should be correct", program.getOptionsDiagnostics());
|
||||
assertHasError("Found a type error", program.getSemanticDiagnostics(), Diagnostics.Module_0_has_no_exported_member_1);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -220,4 +220,67 @@ namespace Utils {
|
||||
return ts.matchFiles(path, extensions, excludes, includes, this.useCaseSensitiveFileNames, this.currentDirectory, depth, (path: string) => this.getAccessibleFileSystemEntries(path));
|
||||
}
|
||||
}
|
||||
|
||||
export class MockProjectReferenceCompilerHost implements ts.CompilerHost {
|
||||
public configHost: ts.ParseConfigHost = new MockParseConfigHost(this.currentDirectory, this.ignoreCase, this.files);
|
||||
private readonly getCanonicalFileNameImpl = ts.createGetCanonicalFileName(!this.ignoreCase);
|
||||
constructor(private currentDirectory: string, private ignoreCase: boolean, private files: ts.Map<string> | string[]) {
|
||||
}
|
||||
|
||||
getCanonicalFileName = (fileName: string): string => {
|
||||
return this.getCanonicalFileNameImpl(fileName);
|
||||
}
|
||||
fileExists = (fileName: string): boolean => {
|
||||
return this.configHost.fileExists(fileName);
|
||||
}
|
||||
|
||||
// TODO try deleting this
|
||||
directoryExists = (dirName: string): boolean => {
|
||||
const fullName = this.getCanonicalFileName(dirName);
|
||||
let exists = false;
|
||||
if (Array.isArray(this.files)) {
|
||||
for (const k of this.files) {
|
||||
if (this.getCanonicalFileName(k).indexOf(fullName) === 0) {
|
||||
exists = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.files.forEach((_v, k) => {
|
||||
if (this.getCanonicalFileName(k).indexOf(fullName) === 0) {
|
||||
exists = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return exists;
|
||||
}
|
||||
readFile = (fileName: string): string => {
|
||||
if (fileName === "lib.d.ts") return "declare var window: any;";
|
||||
|
||||
return this.configHost.readFile(fileName);
|
||||
}
|
||||
getSourceFile = (fileName: string, languageVersion: ts.ScriptTarget): ts.SourceFile => {
|
||||
const content = this.readFile(fileName);
|
||||
if (content === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return ts.createSourceFile(fileName, content, languageVersion);
|
||||
}
|
||||
getDefaultLibFileName(options: ts.CompilerOptions): string {
|
||||
return ts.getDefaultLibFileName(options);
|
||||
}
|
||||
writeFile: ts.WriteFileCallback;
|
||||
getCurrentDirectory = (): string => {
|
||||
return this.currentDirectory;
|
||||
}
|
||||
getNewLine(): string {
|
||||
return "\r\n";
|
||||
}
|
||||
getDirectories(): string[] {
|
||||
return [];
|
||||
}
|
||||
useCaseSensitiveFileNames = () => {
|
||||
return this.ignoreCase;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2501,6 +2501,7 @@ namespace ts.server.protocol {
|
||||
project?: string;
|
||||
reactNamespace?: string;
|
||||
removeComments?: boolean;
|
||||
references?: ProjectReference[];
|
||||
rootDir?: string;
|
||||
rootDirs?: string[];
|
||||
skipLibCheck?: boolean;
|
||||
|
||||
+7
-1
@@ -2199,7 +2199,10 @@ declare namespace ts {
|
||||
interface PluginImport {
|
||||
name: string;
|
||||
}
|
||||
type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[] | null | undefined;
|
||||
interface ProjectReference {
|
||||
path: string;
|
||||
}
|
||||
type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[] | ProjectReference[] | null | undefined;
|
||||
interface CompilerOptions {
|
||||
allowJs?: boolean;
|
||||
allowSyntheticDefaultImports?: boolean;
|
||||
@@ -2252,6 +2255,7 @@ declare namespace ts {
|
||||
project?: string;
|
||||
reactNamespace?: string;
|
||||
jsxFactory?: string;
|
||||
references?: ProjectReference[];
|
||||
removeComments?: boolean;
|
||||
rootDir?: string;
|
||||
rootDirs?: string[];
|
||||
@@ -3766,6 +3770,7 @@ declare namespace ts {
|
||||
* @returns A 'Program' object.
|
||||
*/
|
||||
function createProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program;
|
||||
function walkProjectReferenceGraph(host: CompilerHost, rootOptions: CompilerOptions, callback: (resolvedFile: string, referencedProject: CompilerOptions) => void, error?: (message: DiagnosticMessage | DiagnosticMessageChain | string, option1?: string) => void): void;
|
||||
}
|
||||
declare namespace ts {
|
||||
interface Node {
|
||||
@@ -6788,6 +6793,7 @@ declare namespace ts.server.protocol {
|
||||
project?: string;
|
||||
reactNamespace?: string;
|
||||
removeComments?: boolean;
|
||||
references?: ProjectReference[];
|
||||
rootDir?: string;
|
||||
rootDirs?: string[];
|
||||
skipLibCheck?: boolean;
|
||||
|
||||
+6
-1
@@ -2199,7 +2199,10 @@ declare namespace ts {
|
||||
interface PluginImport {
|
||||
name: string;
|
||||
}
|
||||
type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[] | null | undefined;
|
||||
interface ProjectReference {
|
||||
path: string;
|
||||
}
|
||||
type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[] | ProjectReference[] | null | undefined;
|
||||
interface CompilerOptions {
|
||||
allowJs?: boolean;
|
||||
allowSyntheticDefaultImports?: boolean;
|
||||
@@ -2252,6 +2255,7 @@ declare namespace ts {
|
||||
project?: string;
|
||||
reactNamespace?: string;
|
||||
jsxFactory?: string;
|
||||
references?: ProjectReference[];
|
||||
removeComments?: boolean;
|
||||
rootDir?: string;
|
||||
rootDirs?: string[];
|
||||
@@ -3713,6 +3717,7 @@ declare namespace ts {
|
||||
* @returns A 'Program' object.
|
||||
*/
|
||||
function createProgram(rootNames: ReadonlyArray<string>, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program;
|
||||
function walkProjectReferenceGraph(host: CompilerHost, rootOptions: CompilerOptions, callback: (resolvedFile: string, referencedProject: CompilerOptions) => void, error?: (message: DiagnosticMessage | DiagnosticMessageChain | string, option1?: string) => void): void;
|
||||
}
|
||||
declare namespace ts {
|
||||
function parseCommandLine(commandLine: ReadonlyArray<string>, readFile?: (path: string) => string | undefined): ParsedCommandLine;
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "references": [], /* Projects to reference */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
|
||||
+1
@@ -39,6 +39,7 @@
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "references": [], /* Projects to reference */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
|
||||
+1
@@ -39,6 +39,7 @@
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "references": [], /* Projects to reference */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
|
||||
+1
@@ -39,6 +39,7 @@
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "references": [], /* Projects to reference */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
|
||||
+1
@@ -39,6 +39,7 @@
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "references": [], /* Projects to reference */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
|
||||
+1
@@ -39,6 +39,7 @@
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "references": [], /* Projects to reference */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
|
||||
+1
@@ -39,6 +39,7 @@
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "references": [], /* Projects to reference */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
|
||||
+1
@@ -39,6 +39,7 @@
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
"types": ["jquery","mocha"] /* Type declaration files to be included in compilation. */
|
||||
// "references": [], /* Projects to reference */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user