Convert config file parsing tests to baseline for easier future update verification and adds missing location verification (#53324)

This commit is contained in:
Sheetal Nandi
2023-03-17 12:58:55 -07:00
committed by GitHub
parent f473058361
commit b1ef5b79f7
285 changed files with 20659 additions and 3413 deletions

View File

@@ -25,7 +25,6 @@ import "./unittests/config/convertCompilerOptionsFromJson";
import "./unittests/config/convertTypeAcquisitionFromJson";
import "./unittests/config/initializeTSConfig";
import "./unittests/config/matchFiles";
import "./unittests/config/projectReferences";
import "./unittests/config/showConfig";
import "./unittests/config/tsconfigParsing";
import "./unittests/config/tsconfigParsingWatchOptions";
@@ -106,6 +105,7 @@ import "./unittests/tsc/forceConsistentCasingInFileNames";
import "./unittests/tsc/incremental";
import "./unittests/tsc/listFilesOnly";
import "./unittests/tsc/projectReferences";
import "./unittests/tsc/projectReferencesConfig";
import "./unittests/tsc/redirect";
import "./unittests/tsc/runWithoutArgs";
import "./unittests/tscWatch/consoleClearing";

View File

@@ -1,6 +1,8 @@
import * as fakes from "../../_namespaces/fakes";
import * as Harness from "../../_namespaces/Harness";
import * as ts from "../../_namespaces/ts";
import * as vfs from "../../_namespaces/vfs";
import { baselineParseConfig, baselineParseConfigHost } from "./helpers";
function createFileSystem(ignoreCase: boolean, cwd: string, root: string) {
return new vfs.FileSystem(ignoreCase, {
@@ -207,46 +209,46 @@ function createFileSystem(ignoreCase: boolean, cwd: string, root: string) {
"dev/tests/utils.ts": "",
"dev/tests/scenarios/first.json": "",
"dev/tests/baselines/first/output.ts": "",
"dev/configs/extendsArrayFirst.json": JSON.stringify({
compilerOptions: {
allowJs: true,
noImplicitAny: true,
strictNullChecks: true
}
}),
"dev/configs/extendsArraySecond.json": JSON.stringify({
compilerOptions: {
module: "amd"
},
include: ["../supplemental.*"]
}),
"dev/configs/extendsArrayThird.json": JSON.stringify({
compilerOptions: {
module: null, // eslint-disable-line no-null/no-null
noImplicitAny: false
},
extends: "./extendsArrayFirst",
include: ["../supplemental.*"]
}),
"dev/configs/extendsArrayFourth.json": JSON.stringify({
compilerOptions: {
module: "system",
strictNullChecks: false
},
include: null, // eslint-disable-line no-null/no-null
files: ["../main.ts"]
}),
"dev/configs/extendsArrayFifth.json": JSON.stringify({
extends: ["./extendsArrayFirst", "./extendsArraySecond", "./extendsArrayThird", "./extendsArrayFourth"],
files: [],
}),
"dev/extendsArrayFails.json": JSON.stringify({
extends: ["./missingFile"],
compilerOptions: {
types: []
}
}),
"dev/extendsArrayFails2.json": JSON.stringify({ extends: [42] }),
"dev/configs/extendsArrayFirst.json": JSON.stringify({
compilerOptions: {
allowJs: true,
noImplicitAny: true,
strictNullChecks: true
}
}),
"dev/configs/extendsArraySecond.json": JSON.stringify({
compilerOptions: {
module: "amd"
},
include: ["../supplemental.*"]
}),
"dev/configs/extendsArrayThird.json": JSON.stringify({
compilerOptions: {
module: null, // eslint-disable-line no-null/no-null
noImplicitAny: false
},
extends: "./extendsArrayFirst",
include: ["../supplemental.*"]
}),
"dev/configs/extendsArrayFourth.json": JSON.stringify({
compilerOptions: {
module: "system",
strictNullChecks: false
},
include: null, // eslint-disable-line no-null/no-null
files: ["../main.ts"]
}),
"dev/configs/extendsArrayFifth.json": JSON.stringify({
extends: ["./extendsArrayFirst", "./extendsArraySecond", "./extendsArrayThird", "./extendsArrayFourth"],
files: [],
}),
"dev/extendsArrayFails.json": JSON.stringify({
extends: ["./missingFile"],
compilerOptions: {
types: []
}
}),
"dev/extendsArrayFails2.json": JSON.stringify({ extends: [42] }),
}
}
});
@@ -258,205 +260,78 @@ const caseInsensitiveHost = new fakes.ParseConfigHost(createFileSystem(/*ignoreC
const caseSensitiveBasePath = "/dev/";
const caseSensitiveHost = new fakes.ParseConfigHost(createFileSystem(/*ignoreCase*/ false, caseSensitiveBasePath, "/"));
function verifyDiagnostics(actual: ts.Diagnostic[], expected: { code: number; messageText: string; }[]) {
assert.isTrue(expected.length === actual.length, `Expected error: ${JSON.stringify(expected)}. Actual error: ${JSON.stringify(actual)}.`);
for (let i = 0; i < actual.length; i++) {
const actualError = actual[i];
const expectedError = expected[i];
assert.equal(actualError.code, expectedError.code, "Error code mismatch");
assert.equal(actualError.category, ts.DiagnosticCategory.Error, "Category mismatch"); // Should always be error
assert.equal(ts.flattenDiagnosticMessageText(actualError.messageText, "\n"), expectedError.messageText);
}
}
describe("unittests:: config:: configurationExtension", () => {
ts.forEach<[string, string, fakes.ParseConfigHost], void>([
["under a case insensitive host", caseInsensitiveBasePath, caseInsensitiveHost],
["under a case sensitive host", caseSensitiveBasePath, caseSensitiveHost]
], ([testName, basePath, host]) => {
function getParseCommandLine(entry: string) {
const {config, error} = ts.readConfigFile(entry, name => host.readFile(name));
assert(config && !error, ts.flattenDiagnosticMessageText(error && error.messageText, "\n"));
return ts.parseJsonConfigFileContent(config, host, basePath, {}, entry);
const nameAndEntry: [name: string, entry: string][] = [];
function baselineParsedCommandLine(name: string, entry: string) {
nameAndEntry.push([name, entry]);
}
function getParseCommandLineJsonSourceFile(entry: string) {
const jsonSourceFile = ts.readJsonConfigFile(entry, name => host.readFile(name));
assert(jsonSourceFile.endOfFileToken && !jsonSourceFile.parseDiagnostics.length, ts.flattenDiagnosticMessageText(jsonSourceFile.parseDiagnostics[0] && jsonSourceFile.parseDiagnostics[0].messageText, "\n"));
return {
jsonSourceFile,
parsed: ts.parseJsonSourceFileConfigFileContent(jsonSourceFile, host, basePath, {}, entry)
};
}
baselineParsedCommandLine("can resolve an extension with a base extension", "tsconfig.json");
baselineParsedCommandLine("can resolve an extension with a base extension that overrides options", "tsconfig.nostrictnull.json");
baselineParsedCommandLine("can report errors on circular imports", "circular.json");
baselineParsedCommandLine("can report missing configurations", "missing.json");
baselineParsedCommandLine("can report errors in extended configs", "failure.json");
baselineParsedCommandLine("can error when 'extends' is not a string or Array", "extends.json");
baselineParsedCommandLine("can error when 'extends' is given an empty string", "extends3.json");
baselineParsedCommandLine("can error when 'extends' is given an empty string in an array", "extends4.json");
baselineParsedCommandLine("can overwrite compiler options using extended 'null'", "configs/third.json");
baselineParsedCommandLine("can overwrite top-level options using extended 'null'", "configs/fourth.json");
baselineParsedCommandLine("can overwrite top-level files using extended []", "configs/fifth.json");
baselineParsedCommandLine("can lookup via tsconfig field", "tsconfig.extendsBox.json");
baselineParsedCommandLine("can lookup via package-relative path", "tsconfig.extendsStrict.json");
baselineParsedCommandLine("can lookup via non-redirected-to package-relative path", "tsconfig.extendsUnStrict.json");
baselineParsedCommandLine("can lookup via package-relative path with extension", "tsconfig.extendsStrictExtension.json");
baselineParsedCommandLine("can lookup via an implicit tsconfig", "tsconfig.extendsBoxImplied.json");
baselineParsedCommandLine("can lookup via an implicit tsconfig in a package-relative directory", "tsconfig.extendsBoxImpliedUnstrict.json");
baselineParsedCommandLine("can lookup via an implicit tsconfig in a package-relative directory with name", "tsconfig.extendsBoxImpliedUnstrictExtension.json");
baselineParsedCommandLine("can lookup via an implicit tsconfig in a package-relative directory with extension", "tsconfig.extendsBoxImpliedPath.json");
baselineParsedCommandLine("can lookup via an package.json exports", "tsconfig.extendsFoo.json");
function testSuccess(name: string, entry: string, expected: ts.CompilerOptions, expectedFiles: string[]) {
expected.configFilePath = entry;
it(name, () => {
const parsed = getParseCommandLine(entry);
assert(!parsed.errors.length, ts.flattenDiagnosticMessageText(parsed.errors[0] && parsed.errors[0].messageText, "\n"));
assert.deepEqual(parsed.options, expected);
assert.deepEqual(parsed.fileNames, expectedFiles);
});
baselineExtendsSourceFile("adds extendedSourceFiles only once", "configs/fourth.json");
baselineExtendsSourceFile("adds extendedSourceFiles from an array only once", "configs/extendsArrayFifth.json");
it(name + " with jsonSourceFile", () => {
const { parsed, jsonSourceFile } = getParseCommandLineJsonSourceFile(entry);
assert(!parsed.errors.length, ts.flattenDiagnosticMessageText(parsed.errors[0] && parsed.errors[0].messageText, "\n"));
assert.deepEqual(parsed.options, expected);
assert.equal(parsed.options.configFile, jsonSourceFile);
assert.deepEqual(parsed.fileNames, expectedFiles);
});
}
baselineParsedCommandLine("can overwrite top-level compilerOptions", "configs/extendsArrayFifth.json");
baselineParsedCommandLine("can report missing configurations", "extendsArrayFails.json");
baselineParsedCommandLine("can error when 'extends' is not a string or Array2", "extendsArrayFails2.json");
function testFailure(name: string, entry: string, expectedDiagnostics: { code: number; messageText: string; }[]) {
it(name, () => {
const parsed = getParseCommandLine(entry);
verifyDiagnostics(parsed.errors, expectedDiagnostics);
});
it(name + " with jsonSourceFile", () => {
const { parsed } = getParseCommandLineJsonSourceFile(entry);
verifyDiagnostics(parsed.errors, expectedDiagnostics);
});
}
describe(testName, () => {
testSuccess("can resolve an extension with a base extension", "tsconfig.json", {
allowJs: true,
noImplicitAny: true,
strictNullChecks: true,
}, [
ts.combinePaths(basePath, "main.ts"),
ts.combinePaths(basePath, "supplemental.ts"),
]);
testSuccess("can resolve an extension with a base extension that overrides options", "tsconfig.nostrictnull.json", {
allowJs: true,
noImplicitAny: true,
strictNullChecks: false,
}, [
ts.combinePaths(basePath, "main.ts"),
ts.combinePaths(basePath, "supplemental.ts"),
]);
testFailure("can report errors on circular imports", "circular.json", [
{
code: 18000,
messageText: `Circularity detected while resolving configuration: ${[ts.combinePaths(basePath, "circular.json"), ts.combinePaths(basePath, "circular2.json"), ts.combinePaths(basePath, "circular.json")].join(" -> ")}`
}
]);
testFailure("can report missing configurations", "missing.json", [{
code: 6053,
messageText: `File './missing2' not found.`
}]);
testFailure("can report errors in extended configs", "failure.json", [{
code: 6114,
messageText: `Unknown option 'excludes'. Did you mean 'exclude'?`
}]);
testFailure("can error when 'extends' is not a string or Array", "extends.json", [{
code: 5024,
messageText: `Compiler option 'extends' requires a value of type string or Array.`
}]);
testFailure("can error when 'extends' is given an empty string", "extends3.json", [{
code: 18051,
messageText: `Compiler option 'extends' cannot be given an empty string.`
}]);
testFailure("can error when 'extends' is given an empty string in an array", "extends4.json", [{
code: 18051,
messageText: `Compiler option 'extends' cannot be given an empty string.`
}]);
testSuccess("can overwrite compiler options using extended 'null'", "configs/third.json", {
allowJs: true,
noImplicitAny: true,
strictNullChecks: true,
module: undefined // Technically, this is distinct from the key never being set; but within the compiler we don't make the distinction
}, [
ts.combinePaths(basePath, "supplemental.ts")
]);
testSuccess("can overwrite top-level options using extended 'null'", "configs/fourth.json", {
allowJs: true,
noImplicitAny: true,
strictNullChecks: true,
module: ts.ModuleKind.System
}, [
ts.combinePaths(basePath, "main.ts")
]);
testSuccess("can overwrite top-level files using extended []", "configs/fifth.json", {
allowJs: true,
noImplicitAny: true,
strictNullChecks: true,
module: ts.ModuleKind.System
}, [
ts.combinePaths(basePath, "tests/utils.ts")
]);
describe("finding extended configs from node_modules", () => {
testSuccess("can lookup via tsconfig field", "tsconfig.extendsBox.json", { strict: true }, [ts.combinePaths(basePath, "main.ts")]);
testSuccess("can lookup via package-relative path", "tsconfig.extendsStrict.json", { strict: true }, [ts.combinePaths(basePath, "main.ts")]);
testSuccess("can lookup via non-redirected-to package-relative path", "tsconfig.extendsUnStrict.json", { strict: false }, [ts.combinePaths(basePath, "main.ts")]);
testSuccess("can lookup via package-relative path with extension", "tsconfig.extendsStrictExtension.json", { strict: true }, [ts.combinePaths(basePath, "main.ts")]);
testSuccess("can lookup via an implicit tsconfig", "tsconfig.extendsBoxImplied.json", { strict: true }, [ts.combinePaths(basePath, "main.ts")]);
testSuccess("can lookup via an implicit tsconfig in a package-relative directory", "tsconfig.extendsBoxImpliedUnstrict.json", { strict: false }, [ts.combinePaths(basePath, "main.ts")]);
testSuccess("can lookup via an implicit tsconfig in a package-relative directory with name", "tsconfig.extendsBoxImpliedUnstrictExtension.json", { strict: false }, [ts.combinePaths(basePath, "main.ts")]);
testSuccess("can lookup via an implicit tsconfig in a package-relative directory with extension", "tsconfig.extendsBoxImpliedPath.json", { strict: true }, [ts.combinePaths(basePath, "main.ts")]);
testSuccess("can lookup via an package.json exports", "tsconfig.extendsFoo.json", { strict: true }, [ts.combinePaths(basePath, "main.ts")]);
});
it("adds extendedSourceFiles only once", () => {
const sourceFile = ts.readJsonConfigFile("configs/fourth.json", (path) => host.readFile(path));
const dir = ts.combinePaths(basePath, "configs");
const expected = [
ts.combinePaths(dir, "third.json"),
ts.combinePaths(dir, "second.json"),
ts.combinePaths(dir, "base.json"),
];
ts.parseJsonSourceFileConfigFileContent(sourceFile, host, dir, {}, "fourth.json");
assert.deepEqual(sourceFile.extendedSourceFiles, expected);
ts.parseJsonSourceFileConfigFileContent(sourceFile, host, dir, {}, "fourth.json");
assert.deepEqual(sourceFile.extendedSourceFiles, expected);
});
baselineParseConfig({
scenario: "configurationExtension",
subScenario: testName,
input: () => nameAndEntry.map(([name, entry]) => ({
createHost: baseline => {
baseline.push(name);
return host;
},
jsonText: host.readFile(entry)!,
configFileName: entry,
baselineParsed: (baseline, parsed) => {
baseline.push("CompilerOptions::");
baseline.push(JSON.stringify(parsed.options, undefined, " "));
baseline.push("FileNames::");
baseline.push(parsed.fileNames.join());
},
})),
skipFs: true,
header: baseline => baselineParseConfigHost(baseline, host),
});
describe(testName, () => {
it("adds extendedSourceFiles from an array only once", () => {
const sourceFile = ts.readJsonConfigFile("configs/extendsArrayFifth.json", (path) => host.readFile(path));
function baselineExtendsSourceFile(name: string, entry: string) {
it(name, () => {
const baseline: string[] = [];
baselineParseConfigHost(baseline, host);
baseline.push(`configFileName:: ${name}`);
const sourceFile = ts.readJsonConfigFile(entry, (path) => host.readFile(path));
const dir = ts.combinePaths(basePath, "configs");
const expected = [
ts.combinePaths(dir, "extendsArrayFirst.json"),
ts.combinePaths(dir, "extendsArraySecond.json"),
ts.combinePaths(dir, "extendsArrayThird.json"),
ts.combinePaths(dir, "extendsArrayFourth.json"),
];
ts.parseJsonSourceFileConfigFileContent(sourceFile, host, dir, {}, "extendsArrayFifth.json");
assert.deepEqual(sourceFile.extendedSourceFiles, expected);
ts.parseJsonSourceFileConfigFileContent(sourceFile, host, dir, {}, "extendsArrayFifth.json");
assert.deepEqual(sourceFile.extendedSourceFiles, expected);
ts.parseJsonSourceFileConfigFileContent(sourceFile, host, dir, {}, ts.getBaseFileName(entry));
baseline.push("ExtendedSourceFiles::", ...sourceFile.extendedSourceFiles!);
ts.parseJsonSourceFileConfigFileContent(sourceFile, host, dir, {}, ts.getBaseFileName(entry));
baseline.push("After reusing sourceFile ExtendedSourceFiles::", ...sourceFile.extendedSourceFiles!);
Harness.Baseline.runBaseline(`config/configurationExtension/${name} ${testName}.js`, baseline.join("\n"));
});
testSuccess("can overwrite top-level compilerOptions", "configs/extendsArrayFifth.json", {
allowJs: true,
noImplicitAny: false,
strictNullChecks: false,
module: ts.ModuleKind.System
}, []);
testFailure("can report missing configurations", "extendsArrayFails.json", [{
code: 6053,
messageText: `File './missingFile' not found.`
}]);
testFailure("can error when 'extends' is not a string or Array2", "extendsArrayFails2.json", [{
code: 5024,
messageText: `Compiler option 'extends' requires a value of type string.`
}]);
});
}
});
});

View File

@@ -1,646 +1,228 @@
import * as fakes from "../../_namespaces/fakes";
import * as ts from "../../_namespaces/ts";
import * as vfs from "../../_namespaces/vfs";
import { baselineParseConfig } from "./helpers";
describe("unittests:: config:: convertCompilerOptionsFromJson", () => {
const formatDiagnosticHost: ts.FormatDiagnosticsHost = {
getCurrentDirectory: () => "/apath/",
getCanonicalFileName: ts.createGetCanonicalFileName(/*useCaseSensitiveFileNames*/ true),
getNewLine: () => "\n"
};
interface ExpectedResultWithParsingSuccess {
compilerOptions: ts.CompilerOptions;
errors: readonly ts.Diagnostic[];
function baselineCompilerOptions(subScenario: string, json: any, configFileName: string) {
baselineCompilerOptionsJsonText(
subScenario,
JSON.stringify(json, undefined, " "),
configFileName,
/*skipJson*/ false,
);
}
interface ExpectedResultWithParsingFailure {
compilerOptions: ts.CompilerOptions;
hasParseErrors: true;
}
type ExpectedResult = ExpectedResultWithParsingSuccess | ExpectedResultWithParsingFailure;
function isExpectedResultWithParsingFailure(expectedResult: ExpectedResult): expectedResult is ExpectedResultWithParsingFailure {
return !!(expectedResult as ExpectedResultWithParsingFailure).hasParseErrors;
}
function assertCompilerOptions(json: any, configFileName: string, expectedResult: ExpectedResultWithParsingSuccess) {
assertCompilerOptionsWithJson(json, configFileName, expectedResult);
assertCompilerOptionsWithJsonNode(json, configFileName, expectedResult);
}
function assertCompilerOptionsWithJson(json: any, configFileName: string, expectedResult: ExpectedResultWithParsingSuccess) {
const { options: actualCompilerOptions, errors: actualErrors } = ts.convertCompilerOptionsFromJson(json.compilerOptions, "/apath/", configFileName);
const parsedCompilerOptions = JSON.stringify(actualCompilerOptions);
const expectedCompilerOptions = JSON.stringify({ ...expectedResult.compilerOptions, configFilePath: configFileName });
assert.equal(parsedCompilerOptions, expectedCompilerOptions);
verifyErrors(actualErrors, expectedResult.errors, /*ignoreLocation*/ true);
}
function assertCompilerOptionsWithJsonNode(json: any, configFileName: string, expectedResult: ExpectedResultWithParsingSuccess) {
assertCompilerOptionsWithJsonText(JSON.stringify(json), configFileName, expectedResult);
}
function assertCompilerOptionsWithJsonText(fileText: string, configFileName: string, expectedResult: ExpectedResult) {
const result = ts.parseJsonText(configFileName, fileText);
assert(!!result.endOfFileToken);
assert.equal(!!result.parseDiagnostics.length, isExpectedResultWithParsingFailure(expectedResult));
const host: ts.ParseConfigHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: "/apath/" }));
const { options: actualCompilerOptions, errors: actualParseErrors } = ts.parseJsonSourceFileConfigFileContent(result, host, "/apath/", /*existingOptions*/ undefined, configFileName);
expectedResult.compilerOptions.configFilePath = configFileName;
const parsedCompilerOptions = JSON.stringify(actualCompilerOptions);
const expectedCompilerOptions = JSON.stringify(expectedResult.compilerOptions);
assert.equal(parsedCompilerOptions, expectedCompilerOptions);
assert.equal(actualCompilerOptions.configFile, result);
if (!isExpectedResultWithParsingFailure(expectedResult)) {
verifyErrors(actualParseErrors.filter(error => error.code !== ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code), expectedResult.errors);
}
}
function verifyErrors(actualErrors: ts.Diagnostic[], expectedErrors: readonly ts.Diagnostic[], ignoreLocation?: boolean) {
assert.isTrue(expectedErrors.length === actualErrors.length, `Expected error: ${JSON.stringify(expectedErrors.map(getDiagnosticString), undefined, " ")}. Actual error: ${JSON.stringify(actualErrors.map(getDiagnosticString), undefined, " ")}.`);
for (let i = 0; i < actualErrors.length; i++) {
const actualError = actualErrors[i];
const expectedError = expectedErrors[i];
assert.equal(actualError.code, expectedError.code, `Expected error-code: ${JSON.stringify(expectedError.code)}. Actual error-code: ${JSON.stringify(actualError.code)}.`);
assert.equal(actualError.category, expectedError.category, `Expected error-category: ${JSON.stringify(expectedError.category)}. Actual error-category: ${JSON.stringify(actualError.category)}.`);
if (!ignoreLocation) {
assert(actualError.file);
assert.isDefined(actualError.start);
assert(actualError.length);
}
}
function getDiagnosticString(diagnostic: ts.Diagnostic) {
if (ignoreLocation) {
const { file, ...rest } = diagnostic;
diagnostic = { file: undefined, ...rest };
}
return ts.formatDiagnostic(diagnostic, formatDiagnosticHost);
}
function baselineCompilerOptionsJsonText(subScenario: string, jsonText: string, configFileName: string, skipJson = true) {
baselineParseConfig({
scenario: "convertCompilerOptionsFromJson",
subScenario,
input: () => [{
createHost: () => new fakes.ParseConfigHost(new vfs.FileSystem(
/*ignoreCase*/ false,
{
cwd: "/apath/",
files: {
[`/apath/${configFileName}`]: jsonText,
"/apath/a.ts": "",
"/apath/b.js": "",
}
},
)),
jsonText,
configFileName,
basePath: "/apath",
baselineParsed: (baseline, parsed) => baseline.push("CompilerOptions::", JSON.stringify(parsed.options, undefined, " ")),
}],
skipJson,
});
}
// tsconfig.json tests
it("Convert correctly format tsconfig.json to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
lib: ["es5", "es2015.core", "es2015.symbol"]
}
}, "tsconfig.json",
{
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"]
},
errors: []
}
);
});
baselineCompilerOptions("Convert correctly format tsconfig.json to compiler-options", {
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
lib: ["es5", "es2015.core", "es2015.symbol"]
}
}, "tsconfig.json");
it("Convert correctly format tsconfig.json with allowJs is false to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
allowJs: false,
lib: ["es5", "es2015.core", "es2015.symbol"]
}
}, "tsconfig.json",
{
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
allowJs: false,
lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"]
},
errors: []
}
);
});
baselineCompilerOptions("Convert correctly format tsconfig.json with allowJs is false to compiler-options", {
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
allowJs: false,
lib: ["es5", "es2015.core", "es2015.symbol"]
}
}, "tsconfig.json");
it("Convert incorrect option of jsx to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
jsx: ""
}
}, "tsconfig.json",
{
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
},
errors: [{
file: undefined,
start: 0,
length: 0,
messageText: "Argument for '--jsx' option must be: 'preserve', 'react-native', 'react'.",
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
}
);
});
baselineCompilerOptions("Convert incorrect option of jsx to compiler-options", {
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
jsx: ""
}
}, "tsconfig.json");
it("Convert incorrect option of module to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
module: "",
target: "es5",
noImplicitAny: false,
sourceMap: false,
}
}, "tsconfig.json",
{
compilerOptions: {
target: ts.ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
},
errors: [{
file: undefined,
start: 0,
length: 0,
messageText: "Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015', 'es2020', 'es2022', 'esnext'.",
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
}
);
});
baselineCompilerOptions("Convert incorrect option of module to compiler-options", {
compilerOptions: {
module: "",
target: "es5",
noImplicitAny: false,
sourceMap: false,
}
}, "tsconfig.json");
it("Convert incorrect option of newLine to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
newLine: "",
target: "es5",
noImplicitAny: false,
sourceMap: false,
}
}, "tsconfig.json",
{
compilerOptions: {
target: ts.ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
},
errors: [{
file: undefined,
start: 0,
length: 0,
messageText: "Argument for '--newLine' option must be: 'crlf', 'lf'.",
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
}
);
});
baselineCompilerOptions("Convert incorrect option of newLine to compiler-options", {
compilerOptions: {
newLine: "",
target: "es5",
noImplicitAny: false,
sourceMap: false,
}
}, "tsconfig.json");
it("Convert incorrect option of target to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
target: "",
noImplicitAny: false,
sourceMap: false,
}
}, "tsconfig.json",
{
compilerOptions: {
noImplicitAny: false,
sourceMap: false,
},
errors: [{
file: undefined,
start: 0,
length: 0,
messageText: "Argument for '--target' option must be: 'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'esnext'.",
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
}
);
});
baselineCompilerOptions("Convert incorrect option of target to compiler-options", {
compilerOptions: {
target: "",
noImplicitAny: false,
sourceMap: false,
}
}, "tsconfig.json");
it("Convert incorrect option of module-resolution to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
moduleResolution: "",
noImplicitAny: false,
sourceMap: false,
}
}, "tsconfig.json",
{
compilerOptions: {
noImplicitAny: false,
sourceMap: false,
},
errors: [{
file: undefined,
start: 0,
length: 0,
messageText: "Argument for '--moduleResolution' option must be: 'node', 'classic'.",
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
}
);
});
baselineCompilerOptions("Convert incorrect option of module-resolution to compiler-options", {
compilerOptions: {
moduleResolution: "",
noImplicitAny: false,
sourceMap: false,
}
}, "tsconfig.json");
it("Convert incorrect option of libs to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
lib: ["es5", "es2015.core", "incorrectLib"]
}
}, "tsconfig.json",
{
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts"]
},
errors: [{
file: undefined,
start: 0,
length: 0,
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'esnext.array', 'esnext.symbol', 'esnext.intl', 'esnext.bigint', 'esnext.bigint', 'esnext.string', 'esnext.promise'.",
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
}
);
});
baselineCompilerOptions("Convert incorrect option of libs to compiler-options", {
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
lib: ["es5", "es2015.core", "incorrectLib"]
}
}, "tsconfig.json");
it("Convert empty string option of libs to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
lib: ["es5", ""]
}
}, "tsconfig.json",
{
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
lib: ["lib.es5.d.ts"]
},
errors: [{
file: undefined,
start: 0,
length: 0,
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'esnext.array', 'esnext.symbol', 'esnext.intl', 'esnext.bigint', 'esnext.string', 'esnext.promise'.",
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
}
);
});
baselineCompilerOptions("Convert empty string option of libs to compiler-options", {
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
lib: ["es5", ""]
}
}, "tsconfig.json");
it("Convert empty string option of libs to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
lib: [""]
}
}, "tsconfig.json",
{
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
lib: []
},
errors: [{
file: undefined,
start: 0,
length: 0,
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'esnext.array', 'esnext.symbol', 'esnext.intl', 'esnext.bigint', 'esnext.string', 'esnext.promise'.",
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
}
);
});
baselineCompilerOptions("Convert empty string option of libs array to compiler-options", {
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
lib: [""]
}
}, "tsconfig.json");
it("Convert trailing-whitespace string option of libs to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
lib: [" "]
}
}, "tsconfig.json",
{
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
lib: []
},
errors: [{
file: undefined,
start: 0,
length: 0,
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'es2018', 'esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.importscripts', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string', 'es2017.intl', 'es2017.typedarrays', 'es2018.asynciterable', 'es2018.intl', 'es2018.promise', 'es2018.regexp', 'esnext.array', 'esnext.symbol', 'esnext.intl', 'esnext.bigint', 'esnext.string', 'esnext.promise'.",
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category
}]
}
);
});
baselineCompilerOptions("Convert trailing-whitespace string option of libs to compiler-options", {
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
lib: [" "]
}
}, "tsconfig.json");
it("Convert empty option of libs to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
lib: []
}
}, "tsconfig.json",
{
compilerOptions: {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
lib: []
},
errors: []
}
);
});
baselineCompilerOptions("Convert empty option of libs to compiler-options", {
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
lib: []
}
}, "tsconfig.json");
it("Convert empty string option of moduleSuffixes to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
moduleSuffixes: [".ios", ""]
}
}, "tsconfig.json",
{
compilerOptions: {
moduleSuffixes: [".ios", ""]
},
errors: []
}
);
});
baselineCompilerOptions("Convert empty string option of moduleSuffixes to compiler-options", {
compilerOptions: {
moduleSuffixes: [".ios", ""]
}
}, "tsconfig.json");
it("Convert empty string option of moduleSuffixes to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
moduleSuffixes: [""]
}
}, "tsconfig.json",
{
compilerOptions: {
moduleSuffixes: [""]
},
errors: []
}
);
});
baselineCompilerOptions("Convert empty string option of moduleSuffixes single to compiler-options", {
compilerOptions: {
moduleSuffixes: [""]
}
}, "tsconfig.json");
it("Convert trailing-whitespace string option of moduleSuffixes to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
moduleSuffixes: [" "]
}
}, "tsconfig.json",
{
compilerOptions: {
moduleSuffixes: [" "]
},
errors: []
}
);
});
baselineCompilerOptions("Convert trailing-whitespace string option of moduleSuffixes to compiler-options", {
compilerOptions: {
moduleSuffixes: [" "]
}
}, "tsconfig.json");
it("Convert empty option of moduleSuffixes to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
moduleSuffixes: []
}
}, "tsconfig.json",
{
compilerOptions: {
moduleSuffixes: []
},
errors: []
}
);
});
baselineCompilerOptions("Convert empty option of moduleSuffixes to compiler-options", {
compilerOptions: {
moduleSuffixes: []
}
}, "tsconfig.json");
it("Convert incorrectly format tsconfig.json to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
modu: "commonjs",
}
}, "tsconfig.json",
{
compilerOptions: {},
errors: [{
file: undefined,
start: 0,
length: 0,
messageText: "Unknown compiler option 'modu'.",
code: ts.Diagnostics.Unknown_compiler_option_0.code,
category: ts.Diagnostics.Unknown_compiler_option_0.category
}]
}
);
});
baselineCompilerOptions("Convert incorrectly format tsconfig.json to compiler-options", {
compilerOptions: {
modu: "commonjs",
}
}, "tsconfig.json");
it("Convert default tsconfig.json to compiler-options ", () => {
assertCompilerOptions({}, "tsconfig.json",
{
compilerOptions: {},
errors: []
}
);
});
baselineCompilerOptions("Convert default tsconfig.json to compiler-options", {}, "tsconfig.json");
it("Convert negative numbers in tsconfig.json ", () => {
assertCompilerOptions(
{
compilerOptions: {
allowJs: true,
maxNodeModuleJsDepth: -1
}
}, "tsconfig.json",
{
compilerOptions: {
allowJs: true,
maxNodeModuleJsDepth: -1
},
errors: []
}
);
});
baselineCompilerOptions("Convert negative numbers in tsconfig.json", {
compilerOptions: {
allowJs: true,
maxNodeModuleJsDepth: -1
}
}, "tsconfig.json");
// jsconfig.json
it("Convert correctly format jsconfig.json to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
lib: ["es5", "es2015.core", "es2015.symbol"]
}
}, "jsconfig.json",
{
compilerOptions: {
allowJs: true,
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true,
skipLibCheck: true,
noEmit: true,
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"]
},
errors: []
}
);
});
baselineCompilerOptions("Convert correctly format jsconfig.json to compiler-options", {
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
lib: ["es5", "es2015.core", "es2015.symbol"]
}
}, "jsconfig.json");
it("Convert correctly format jsconfig.json with allowJs is false to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
allowJs: false,
lib: ["es5", "es2015.core", "es2015.symbol"]
}
}, "jsconfig.json",
{
compilerOptions: {
allowJs: false,
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true,
skipLibCheck: true,
noEmit: true,
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"]
},
errors: []
}
);
});
baselineCompilerOptions("Convert correctly format jsconfig.json with allowJs is false to compiler-options", {
compilerOptions: {
module: "commonjs",
target: "es5",
noImplicitAny: false,
sourceMap: false,
allowJs: false,
lib: ["es5", "es2015.core", "es2015.symbol"]
}
}, "jsconfig.json");
it("Convert incorrectly format jsconfig.json to compiler-options ", () => {
assertCompilerOptions(
{
compilerOptions: {
modu: "commonjs",
}
}, "jsconfig.json",
{
compilerOptions:
{
allowJs: true,
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true,
skipLibCheck: true,
noEmit: true
},
errors: [{
file: undefined,
start: 0,
length: 0,
messageText: "Unknown compiler option 'modu'.",
code: ts.Diagnostics.Unknown_compiler_option_0.code,
category: ts.Diagnostics.Unknown_compiler_option_0.category
}]
}
);
});
baselineCompilerOptions("Convert incorrectly format jsconfig.json to compiler-options", {
compilerOptions: {
modu: "commonjs",
}
}, "jsconfig.json");
it("Convert default jsconfig.json to compiler-options ", () => {
assertCompilerOptions({}, "jsconfig.json",
{
compilerOptions:
{
allowJs: true,
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true,
skipLibCheck: true,
noEmit: true
},
errors: []
}
);
});
baselineCompilerOptions("Convert default jsconfig.json to compiler-options", {}, "jsconfig.json");
it("Convert tsconfig options when there are multiple invalid strings", () => {
assertCompilerOptionsWithJsonText(`{
baselineCompilerOptionsJsonText("Convert tsconfig options when there are multiple invalid strings", `{
"compilerOptions": {
"target": "<%- options.useTsWithBabel ? 'esnext' : 'es5' %>",
"module": "esnext",
@@ -660,122 +242,43 @@ describe("unittests:: config:: convertCompilerOptionsFromJson", () => {
}
}
`,
"tsconfig.json",
{
compilerOptions: {
target: undefined,
module: ts.ModuleKind.ESNext,
experimentalDecorators: true,
},
hasParseErrors: true
});
});
"tsconfig.json");
it("Convert a tsconfig file with stray trailing characters", () => {
assertCompilerOptionsWithJsonText(`{
"compilerOptions": {
"target": "esnext"
}
} blah`, "tsconfig.json", {
compilerOptions: {
target: ts.ScriptTarget.ESNext
},
hasParseErrors: true,
errors: [{
...ts.Diagnostics.The_root_value_of_a_0_file_must_be_an_object,
messageText: "The root value of a 'tsconfig.json' file must be an object.",
file: undefined,
start: 0,
length: 0
}]
});
});
baselineCompilerOptionsJsonText("Convert a tsconfig file with stray trailing characters",
`{
"compilerOptions": {
"target": "esnext"
}
} blah`, "tsconfig.json");
it("Convert a tsconfig file with stray leading characters", () => {
assertCompilerOptionsWithJsonText(`blah {
"compilerOptions": {
"target": "esnext"
}
}`, "tsconfig.json", {
compilerOptions: {
target: ts.ScriptTarget.ESNext
},
hasParseErrors: true,
errors: [{
...ts.Diagnostics.The_root_value_of_a_0_file_must_be_an_object,
messageText: "The root value of a 'tsconfig.json' file must be an object.",
file: undefined,
start: 0,
length: 0
}]
});
});
baselineCompilerOptionsJsonText("Convert a tsconfig file with stray leading characters",
`blah {
"compilerOptions": {
"target": "esnext"
}
}`, "tsconfig.json");
it("Convert a tsconfig file as an array", () => {
assertCompilerOptionsWithJsonText(`[{
"compilerOptions": {
"target": "esnext"
}
}]`, "tsconfig.json", {
compilerOptions: {
target: ts.ScriptTarget.ESNext
},
errors: [{
...ts.Diagnostics.The_root_value_of_a_0_file_must_be_an_object,
messageText: "The root value of a 'tsconfig.json' file must be an object.",
file: undefined,
start: 0,
length: 0
}]
});
});
baselineCompilerOptionsJsonText("Convert a tsconfig file as an array",
`[{
"compilerOptions": {
"target": "esnext"
}
}]`, "tsconfig.json");
it("raises an error if you've set a compiler flag in the root without including 'compilerOptions'", () => {
assertCompilerOptionsWithJsonText(`{
"module": "esnext",
}`, "tsconfig.json", {
compilerOptions: {},
errors: [{
...ts.Diagnostics._0_should_be_set_inside_the_compilerOptions_object_of_the_config_json_file,
messageText: "'module' should be set inside the 'compilerOptions' object of the config json file.",
file: undefined,
start: 0,
length: 0
}]
});
});
baselineCompilerOptionsJsonText("raises an error if you've set a compiler flag in the root without including compilerOptions",
`{
"module": "esnext",
}`, "tsconfig.json");
it("does not raise an error if you've set a compiler flag in the root when you have included 'compilerOptions'", () => {
assertCompilerOptionsWithJsonText(`{
"target": "esnext",
"compilerOptions": {
"module": "esnext"
}
}`, "tsconfig.json", {
compilerOptions: {
module: ts.ModuleKind.ESNext
},
errors: []
});
});
baselineCompilerOptionsJsonText("does not raise an error if you've set a compiler flag in the root when you have included 'compilerOptions'",
`{
"target": "esnext",
"compilerOptions": {
"module": "esnext"
}
}`, "tsconfig.json");
it("Don't crash when root expression is not object at all", () => {
assertCompilerOptionsWithJsonText(`42`, "tsconfig.json", {
compilerOptions: {},
errors: [{
...ts.Diagnostics.The_root_value_of_a_0_file_must_be_an_object,
messageText: "The root value of a 'tsconfig.json' file must be an object.",
file: undefined,
start: 0,
length: 0
}]
});
});
baselineCompilerOptionsJsonText("Don't crash when root expression is not object at all", `42`, "tsconfig.json");
it("Allow trailing comments", () => {
assertCompilerOptionsWithJsonText(`{} // no options`, "tsconfig.json", {
compilerOptions: {},
errors: []
});
});
baselineCompilerOptionsJsonText("Allow trailing comments", `{} // no options`, "tsconfig.json");
});

View File

@@ -1,217 +1,96 @@
import * as fakes from "../../_namespaces/fakes";
import * as ts from "../../_namespaces/ts";
import * as vfs from "../../_namespaces/vfs";
import { baselineParseConfig } from "./helpers";
interface ExpectedResult { typeAcquisition: ts.TypeAcquisition; errors: ts.Diagnostic[]; }
describe("unittests:: config:: convertTypeAcquisitionFromJson", () => {
function assertTypeAcquisition(json: any, configFileName: string, expectedResult: ExpectedResult) {
assertTypeAcquisitionWithJson(json, configFileName, expectedResult);
assertTypeAcquisitionWithJsonNode(json, configFileName, expectedResult);
}
function verifyAcquisition(actualTypeAcquisition: ts.TypeAcquisition | undefined, expectedResult: ExpectedResult) {
const parsedTypeAcquisition = JSON.stringify(actualTypeAcquisition);
const expectedTypeAcquisition = JSON.stringify(expectedResult.typeAcquisition);
assert.equal(parsedTypeAcquisition, expectedTypeAcquisition);
}
function verifyErrors(actualErrors: ts.Diagnostic[], expectedResult: ExpectedResult, hasLocation?: boolean) {
const expectedErrors = expectedResult.errors;
assert.isTrue(expectedResult.errors.length === actualErrors.length, `Expected error: ${JSON.stringify(expectedResult.errors)}. Actual error: ${JSON.stringify(actualErrors)}.`);
for (let i = 0; i < actualErrors.length; i++) {
const actualError = actualErrors[i];
const expectedError = expectedErrors[i];
assert.equal(actualError.code, expectedError.code, `Expected error-code: ${JSON.stringify(expectedError.code)}. Actual error-code: ${JSON.stringify(actualError.code)}.`);
assert.equal(actualError.category, expectedError.category, `Expected error-category: ${JSON.stringify(expectedError.category)}. Actual error-category: ${JSON.stringify(actualError.category)}.`);
if (hasLocation) {
assert(actualError.file);
assert(actualError.start);
assert(actualError.length);
}
}
}
function assertTypeAcquisitionWithJson(json: any, configFileName: string, expectedResult: ExpectedResult) {
const jsonOptions = json.typeAcquisition;
const { options: actualTypeAcquisition, errors: actualErrors } = ts.convertTypeAcquisitionFromJson(jsonOptions, "/apath/", configFileName);
verifyAcquisition(actualTypeAcquisition, expectedResult);
verifyErrors(actualErrors, expectedResult);
}
function assertTypeAcquisitionWithJsonNode(json: any, configFileName: string, expectedResult: ExpectedResult) {
const fileText = JSON.stringify(json);
const result = ts.parseJsonText(configFileName, fileText);
assert(!result.parseDiagnostics.length);
assert(!!result.endOfFileToken);
const host: ts.ParseConfigHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: "/apath/" }));
const { typeAcquisition: actualTypeAcquisition, errors: actualParseErrors } = ts.parseJsonSourceFileConfigFileContent(result, host, "/apath/", /*existingOptions*/ undefined, configFileName);
verifyAcquisition(actualTypeAcquisition, expectedResult);
const actualErrors = ts.filter(actualParseErrors, error => error.code !== ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code);
verifyErrors(actualErrors, expectedResult, /*hasLocation*/ true);
}
it("Convert correctly format tsconfig.json to typeAcquisition ", () => {
assertTypeAcquisition(
{
typeAcquisition:
{
enable: true,
include: ["0.d.ts", "1.d.ts"],
exclude: ["0.js", "1.js"]
}
function baselineTypeAcquisition(subScenario: string, json: any, configFileName: string) {
baselineParseConfig({
scenario: "convertTypeAcquisitionFromJson",
subScenario,
input: () => {
const jsonText = JSON.stringify(json, undefined, " ");
return [{
createHost: () => new fakes.ParseConfigHost(new vfs.FileSystem(
/*ignoreCase*/ false,
{
cwd: "/apath/",
files: {
[`/apath/${configFileName}`]: jsonText,
"/apath/a.ts": "",
"/apath/b.js": "",
}
},
)),
jsonText,
configFileName,
basePath: "/apath",
baselineParsed: (baseline, parsed) => baseline.push("TypeAcquisition::", JSON.stringify(parsed.typeAcquisition, undefined, " ")),
}];
},
"tsconfig.json",
{
typeAcquisition:
{
enable: true,
include: ["0.d.ts", "1.d.ts"],
exclude: ["0.js", "1.js"]
},
errors: [] as ts.Diagnostic[]
});
});
});
}
it("Convert incorrect format tsconfig.json to typeAcquisition ", () => {
assertTypeAcquisition(
baselineTypeAcquisition("Convert correctly format tsconfig.json to typeAcquisition ",
{
typeAcquisition:
{
typeAcquisition:
{
enableAutoDiscovy: true,
}
}, "tsconfig.json",
{
typeAcquisition:
{
enable: false,
include: [],
exclude: []
},
errors: [
{
category: ts.Diagnostics.Unknown_type_acquisition_option_0.category,
code: ts.Diagnostics.Unknown_type_acquisition_option_0.code,
file: undefined,
start: 0,
length: 0,
messageText: undefined!, // TODO: GH#18217
}
]
});
});
enable: true,
include: ["0.d.ts", "1.d.ts"],
exclude: ["0.js", "1.js"]
}
},
"tsconfig.json"
);
it("Convert default tsconfig.json to typeAcquisition ", () => {
assertTypeAcquisition({}, "tsconfig.json",
baselineTypeAcquisition("Convert incorrect format tsconfig.json to typeAcquisition ",
{
typeAcquisition:
{
typeAcquisition:
{
enable: false,
include: [],
exclude: []
},
errors: [] as ts.Diagnostic[]
});
});
enableAutoDiscovy: true,
}
}, "tsconfig.json",
);
it("Convert tsconfig.json with only enable property to typeAcquisition ", () => {
assertTypeAcquisition(
baselineTypeAcquisition("Convert default tsconfig.json to typeAcquisition ", {}, "tsconfig.json");
baselineTypeAcquisition("Convert tsconfig.json with only enable property to typeAcquisition ",
{
typeAcquisition:
{
typeAcquisition:
{
enable: true
}
}, "tsconfig.json",
{
typeAcquisition:
{
enable: true,
include: [],
exclude: []
},
errors: [] as ts.Diagnostic[]
});
});
enable: true
}
}, "tsconfig.json",
);
// jsconfig.json
it("Convert jsconfig.json to typeAcquisition ", () => {
assertTypeAcquisition(
baselineTypeAcquisition("Convert jsconfig.json to typeAcquisition ",
{
typeAcquisition:
{
typeAcquisition:
{
enable: false,
include: ["0.d.ts"],
exclude: ["0.js"]
}
}, "jsconfig.json",
{
typeAcquisition:
{
enable: false,
include: ["0.d.ts"],
exclude: ["0.js"]
},
errors: [] as ts.Diagnostic[]
});
});
enable: false,
include: ["0.d.ts"],
exclude: ["0.js"]
}
}, "jsconfig.json",
);
it("Convert default jsconfig.json to typeAcquisition ", () => {
assertTypeAcquisition({ }, "jsconfig.json",
{
typeAcquisition:
{
enable: true,
include: [],
exclude: []
},
errors: [] as ts.Diagnostic[]
});
});
baselineTypeAcquisition("Convert default jsconfig.json to typeAcquisition ", {}, "jsconfig.json");
it("Convert incorrect format jsconfig.json to typeAcquisition ", () => {
assertTypeAcquisition(
baselineTypeAcquisition("Convert incorrect format jsconfig.json to typeAcquisition ",
{
typeAcquisition:
{
typeAcquisition:
{
enableAutoDiscovy: true,
}
}, "jsconfig.json",
{
typeAcquisition:
{
enable: true,
include: [],
exclude: []
},
errors: [
{
category: ts.Diagnostics.Unknown_type_acquisition_option_0.category,
code: ts.Diagnostics.Unknown_type_acquisition_option_0.code,
file: undefined,
start: 0,
length: 0,
messageText: undefined!, // TODO: GH#18217
}
]
});
});
enableAutoDiscovy: true,
}
}, "jsconfig.json",
);
it("Convert jsconfig.json with only enable property to typeAcquisition ", () => {
assertTypeAcquisition(
baselineTypeAcquisition("Convert jsconfig.json with only enable property to typeAcquisition ",
{
typeAcquisition:
{
typeAcquisition:
{
enable: false
}
}, "jsconfig.json",
{
typeAcquisition:
{
enable: false,
include: [],
exclude: []
},
errors: [] as ts.Diagnostic[]
});
});
enable: false
}
}, "jsconfig.json",
);
});

View File

@@ -0,0 +1,105 @@
import * as fakes from "../../_namespaces/fakes";
import * as Harness from "../../_namespaces/Harness";
import * as ts from "../../_namespaces/ts";
import * as vfs from "../../_namespaces/vfs";
function getParsedCommandJson(
jsonText: string,
configFileName: string,
host: fakes.ParseConfigHost,
basePath: string | undefined,
existingOptions: ts.CompilerOptions | undefined,
existingWatchOptions: ts.WatchOptions | undefined,
) {
const parsed = ts.parseConfigFileTextToJson(configFileName, jsonText);
return ts.parseJsonConfigFileContent(
parsed.config,
host,
basePath ?? host.sys.getCurrentDirectory(),
existingOptions,
configFileName,
/*resolutionStack*/ undefined,
/*extraFileExtensions*/ undefined,
/*extendedConfigCache*/ undefined,
existingWatchOptions,
);
}
function getParsedCommandJsonSourceFile(
jsonText: string,
configFileName: string,
host: fakes.ParseConfigHost,
basePath: string | undefined,
existingOptions: ts.CompilerOptions | undefined,
existingWatchOptions: ts.WatchOptions | undefined,
) {
const parsed = ts.parseJsonText(configFileName, jsonText);
return ts.parseJsonSourceFileConfigFileContent(
parsed,
host,
basePath ?? host.sys.getCurrentDirectory(),
existingOptions,
configFileName,
/*resolutionStack*/ undefined,
/*extraFileExtensions*/ undefined,
/*extendedConfigCache*/ undefined,
existingWatchOptions,
);
}
export interface ParseConfigInput {
createHost: (baseline: string[]) => fakes.ParseConfigHost;
jsonText: string;
configFileName: string;
basePath?: string,
existingOptions?: ts.CompilerOptions;
existingWatchOptions?: ts.WatchOptions;
baselineParsed(baseline: string[], parsed: ts.ParsedCommandLine): void;
}
export interface BaselineParseConfigInput {
scenario: string;
subScenario: string;
input(): ParseConfigInput[];
skipJson?: boolean;
skipErrors?: boolean;
skipFs?: boolean;
header?(baseline: string[]): void;
}
export function baselineParseConfig(input: BaselineParseConfigInput) {
if (!input.skipJson) baselineParseConfigWith("with json api", getParsedCommandJson, input);
baselineParseConfigWith("with jsonSourceFile api", getParsedCommandJsonSourceFile, input);
}
export function baselineParseConfigHost(baseline: string[], host: fakes.ParseConfigHost) {
baseline.push("Fs::", vfs.formatPatch(host.sys.vfs.diff(/*base*/ undefined, { baseIsNotShadowRoot: true })!));
}
function baselineParseConfigWith(
jsonTest: string,
getParsed: typeof getParsedCommandJson,
{ scenario, subScenario, input, skipErrors, skipFs, header }: BaselineParseConfigInput,
) {
describe(subScenario, () => {
it(jsonTest, () => {
const baseline: string[] = [];
header?.(baseline);
for (const { createHost, jsonText, configFileName, existingOptions, basePath, existingWatchOptions, baselineParsed } of input()) {
const host = createHost(baseline);
if (!skipFs) baselineParseConfigHost(baseline, host);
baseline.push(`configFileName:: ${configFileName}`);
const parsed = getParsed(jsonText, configFileName, host, basePath, existingOptions, existingWatchOptions);
baselineParsed(baseline, parsed);
if (!skipErrors) {
baseline.push("Errors::");
baseline.push(ts.formatDiagnosticsWithColorAndContext(parsed.errors, {
getCurrentDirectory: () => basePath || host.sys.getCurrentDirectory(),
getCanonicalFileName: ts.createGetCanonicalFileName(host.useCaseSensitiveFileNames),
getNewLine: () => host.sys.newLine,
}));
}
baseline.push("");
}
Harness.Baseline.runBaseline(`config/${scenario}/${subScenario} ${jsonTest}.js`, baseline.join("\n"));
});
});
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,355 +0,0 @@
import * as fakes from "../../_namespaces/fakes";
import * as ts from "../../_namespaces/ts";
import * as vfs from "../../_namespaces/vfs";
import { libFile } from "../virtualFileSystemWithWatch";
interface TestProjectSpecification {
configFileName?: string;
references?: readonly (string | ts.ProjectReference)[];
files: { [fileName: string]: string };
outputFiles?: { [fileName: string]: string };
config?: object;
options?: Partial<ts.CompilerOptions>;
}
interface TestSpecification {
[path: string]: TestProjectSpecification;
}
function assertHasError(message: string, errors: readonly ts.Diagnostic[], diag: ts.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: readonly ts.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 = ts.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: ts.Program, host: fakes.CompilerHost) => void) {
const files = new Map<string, string>();
for (const key in spec) {
const sp = spec[key];
const configFileName = combineAllPaths("/", key, sp.configFileName || "tsconfig.json");
const options = {
compilerOptions: {
composite: true,
outDir: "bin",
...sp.options
},
references: (sp.references || []).map(r => {
if (typeof r === "string") {
return { path: r };
}
return r;
}),
...sp.config
};
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 vfsys = new vfs.FileSystem(false, { files: { "/lib.d.ts": libFile.content } });
files.forEach((v, k) => {
vfsys.mkdirpSync(ts.getDirectoryPath(k));
vfsys.writeFileSync(k, v);
});
const host = new fakes.CompilerHost(new fakes.System(vfsys));
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, ts.flattenDiagnosticMessageText(error && error.messageText, "\n"));
const file = ts.parseJsonConfigFileContent(config, ts.parseConfigHostFromCompilerHostLike(host), ts.getDirectoryPath(entryPointConfigFileName), {}, entryPointConfigFileName);
file.options.configFilePath = entryPointConfigFileName;
const prog = ts.createProgram({
rootNames: file.fileNames,
options: file.options,
host,
projectReferences: file.projectReferences
});
checkResult(prog, host);
}
describe("unittests:: config:: 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());
});
});
});
/**
* Validate that we enforce the basic settings constraints for referenced projects
*/
describe("unittests:: config:: project-references constraint checking for settings", () => {
it("errors when declaration = false", () => {
const spec: TestSpecification = {
"/primary": {
files: { "/primary/a.ts": emptyModule },
references: [],
options: {
declaration: false
}
}
};
testProjectReferences(spec, "/primary/tsconfig.json", program => {
const errs = program.getOptionsDiagnostics();
assertHasError("Reports an error about the wrong decl setting", errs, ts.Diagnostics.Composite_projects_may_not_disable_declaration_emit);
});
});
it("errors when the referenced project doesn't have composite:true", () => {
const spec: TestSpecification = {
"/primary": {
files: { "/primary/a.ts": emptyModule },
references: [],
options: {
composite: false
}
},
"/reference": {
files: { "/secondary/b.ts": moduleImporting("../primary/a") },
references: ["../primary"],
config: {
files: ["b.ts"]
}
}
};
testProjectReferences(spec, "/reference/tsconfig.json", program => {
const errs = program.getOptionsDiagnostics();
assertHasError("Reports an error about 'composite' not being set", errs, ts.Diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true);
});
});
it("does not error when the referenced project doesn't have composite:true if its a container project", () => {
const spec: TestSpecification = {
"/primary": {
files: { "/primary/a.ts": emptyModule },
references: [],
options: {
composite: false
}
},
"/reference": {
files: { "/secondary/b.ts": moduleImporting("../primary/a") },
references: ["../primary"],
}
};
testProjectReferences(spec, "/reference/tsconfig.json", program => {
const errs = program.getOptionsDiagnostics();
assertNoErrors("Reports an error about 'composite' not being set", errs);
});
});
it("errors when the file list is not exhaustive", () => {
const spec: TestSpecification = {
"/primary": {
files: {
"/primary/a.ts": "import * as b from './b'",
"/primary/b.ts": "export {}"
},
config: {
files: ["a.ts"]
}
}
};
testProjectReferences(spec, "/primary/tsconfig.json", program => {
const errs = program.getSemanticDiagnostics(program.getSourceFile("/primary/a.ts"));
assertHasError("Reports an error about b.ts not being in the list", errs, ts.Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern);
});
});
it("errors when the referenced project doesn't exist", () => {
const spec: TestSpecification = {
"/primary": {
files: { "/primary/a.ts": emptyModule },
references: ["../foo"]
}
};
testProjectReferences(spec, "/primary/tsconfig.json", program => {
const errs = program.getOptionsDiagnostics();
assertHasError("Reports an error about a missing file", errs, ts.Diagnostics.File_0_not_found);
});
});
it("errors when a prepended project reference doesn't set outFile", () => {
const spec: TestSpecification = {
"/primary": {
files: { "/primary/a.ts": emptyModule },
references: [{ path: "../someProj", prepend: true }]
},
"/someProj": {
files: { "/someProj/b.ts": "const x = 100;" }
}
};
testProjectReferences(spec, "/primary/tsconfig.json", program => {
const errs = program.getOptionsDiagnostics();
assertHasError("Reports an error about outFile not being set", errs, ts.Diagnostics.Cannot_prepend_project_0_because_it_does_not_have_outFile_set);
});
});
it("errors when a prepended project reference output doesn't exist", () => {
const spec: TestSpecification = {
"/primary": {
files: { "/primary/a.ts": "const y = x;" },
references: [{ path: "../someProj", prepend: true }]
},
"/someProj": {
files: { "/someProj/b.ts": "const x = 100;" },
options: { outFile: "foo.js" }
}
};
testProjectReferences(spec, "/primary/tsconfig.json", program => {
const errs = program.getOptionsDiagnostics();
assertHasError("Reports an error about outFile being missing", errs, ts.Diagnostics.Output_file_0_from_project_1_does_not_exist);
});
});
});
/**
* Path mapping behavior
*/
describe("unittests:: config:: project-references path mapping", () => {
it("redirects to the output .d.ts file", () => {
const spec: TestSpecification = {
"/alpha": {
files: { "/alpha/a.ts": "export const m: number = 3;" },
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(), ts.Diagnostics.Module_0_has_no_exported_member_1);
});
});
});
describe("unittests:: config:: project-references nice-behavior", () => {
it("issues a nice error when the input file is missing", () => {
const spec: TestSpecification = {
"/alpha": {
files: { "/alpha/a.ts": "export const m: number = 3;" },
references: []
},
"/beta": {
files: { "/beta/b.ts": "import { m } from '../alpha/a'" },
references: ["../alpha"]
}
};
testProjectReferences(spec, "/beta/tsconfig.json", program => {
assertHasError("Issues a useful error", program.getSemanticDiagnostics(), ts.Diagnostics.Output_file_0_has_not_been_built_from_source_file_1);
});
});
it("issues a nice error when the input file is missing when module reference is not relative", () => {
const spec: TestSpecification = {
"/alpha": {
files: { "/alpha/a.ts": "export const m: number = 3;" },
references: []
},
"/beta": {
files: { "/beta/b.ts": "import { m } from '@alpha/a'" },
references: ["../alpha"],
options: {
baseUrl: "./",
paths: {
"@alpha/*": ["/alpha/*"]
}
}
}
};
testProjectReferences(spec, "/beta/tsconfig.json", program => {
assertHasError("Issues a useful error", program.getSemanticDiagnostics(), ts.Diagnostics.Output_file_0_has_not_been_built_from_source_file_1);
});
});
});
/**
* 'composite' behavior
*/
describe("unittests:: config:: project-references behavior changes under composite: true", () => {
it("doesn't infer the rootDir from source paths", () => {
const spec: TestSpecification = {
"/alpha": {
files: { "/alpha/src/a.ts": "export const m: number = 3;" },
options: {
declaration: true,
outDir: "bin"
},
references: []
}
};
testProjectReferences(spec, "/alpha/tsconfig.json", (program, host) => {
program.emit();
assert.deepEqual(host.outputs.map(e => e.file).sort(), ["/alpha/bin/src/a.d.ts", "/alpha/bin/src/a.js", "/alpha/bin/tsconfig.tsbuildinfo"]);
});
});
});
describe("unittests:: config:: project-references errors when a file in a composite project occurs outside the root", () => {
it("Errors when a file is outside the rootdir", () => {
const spec: TestSpecification = {
"/alpha": {
files: { "/alpha/src/a.ts": "import * from '../../beta/b'", "/beta/b.ts": "export { }" },
options: {
declaration: true,
outDir: "bin"
},
references: []
}
};
testProjectReferences(spec, "/alpha/tsconfig.json", (program) => {
const semanticDiagnostics = program.getSemanticDiagnostics(program.getSourceFile("/alpha/src/a.ts"));
assertHasError("Issues an error about the rootDir", semanticDiagnostics, ts.Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files);
assertHasError("Issues an error about the fileList", semanticDiagnostics, ts.Diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern);
});
});
});

View File

@@ -1,268 +1,224 @@
import * as fakes from "../../_namespaces/fakes";
import * as Harness from "../../_namespaces/Harness";
import * as ts from "../../_namespaces/ts";
import * as vfs from "../../_namespaces/vfs";
import { baselineParseConfig } from "./helpers";
describe("unittests:: config:: tsconfigParsing:: parseConfigFileTextToJson", () => {
function assertParseResult(jsonText: string, expectedConfigObject: { config?: any; error?: ts.Diagnostic[] }) {
const parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText);
assert.equal(JSON.stringify(parsed), JSON.stringify(expectedConfigObject));
function formatErrors(errors: readonly ts.Diagnostic[]) {
return ts.formatDiagnosticsWithColorAndContext(errors, {
getCurrentDirectory: () => "/",
getCanonicalFileName: ts.identity,
getNewLine: () => "\n",
});
}
function assertParseErrorWithExcludesKeyword(jsonText: string) {
{
const parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText);
const parsedCommand = ts.parseJsonConfigFileContent(parsed.config, ts.sys, "tests/cases/unittests");
assert.isTrue(parsedCommand.errors && parsedCommand.errors.length === 1 &&
parsedCommand.errors[0].code === ts.Diagnostics.Unknown_option_excludes_Did_you_mean_exclude.code);
}
{
const parsed = ts.parseJsonText("/apath/tsconfig.json", jsonText);
const parsedCommand = ts.parseJsonSourceFileConfigFileContent(parsed, ts.sys, "tests/cases/unittests");
assert.isTrue(parsedCommand.errors && parsedCommand.errors.length === 1 &&
parsedCommand.errors[0].code === ts.Diagnostics.Unknown_option_excludes_Did_you_mean_exclude.code);
}
}
function getParsedCommandJson(jsonText: string, configFileName: string, basePath: string, allFileList: string[]) {
const parsed = ts.parseConfigFileTextToJson(configFileName, jsonText);
const files = allFileList.reduce((files, value) => (files[value] = "", files), {} as vfs.FileSet);
const host: ts.ParseConfigHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: basePath, files: { "/": {}, ...files } }));
return ts.parseJsonConfigFileContent(parsed.config, host, basePath, /*existingOptions*/ undefined, configFileName);
}
function getParsedCommandJsonNode(jsonText: string, configFileName: string, basePath: string, allFileList: string[]) {
const parsed = ts.parseJsonText(configFileName, jsonText);
const files = allFileList.reduce((files, value) => (files[value] = "", files), {} as vfs.FileSet);
const host: ts.ParseConfigHost = new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, { cwd: basePath, files: { "/": {}, ...files } }));
return ts.parseJsonSourceFileConfigFileContent(parsed, host, basePath, /*existingOptions*/ undefined, configFileName);
}
function assertParseFileList(jsonText: string, configFileName: string, basePath: string, allFileList: string[], expectedFileList: string[]) {
{
const parsed = getParsedCommandJson(jsonText, configFileName, basePath, allFileList);
assert.isTrue(ts.arrayIsEqualTo(parsed.fileNames.sort(), expectedFileList.sort()));
}
{
const parsed = getParsedCommandJsonNode(jsonText, configFileName, basePath, allFileList);
assert.isTrue(ts.arrayIsEqualTo(parsed.fileNames.sort(), expectedFileList.sort()));
}
}
function assertParseFileDiagnostics(jsonText: string, configFileName: string, basePath: string, allFileList: string[], expectedDiagnosticCode: number, noLocation?: boolean) {
{
const parsed = getParsedCommandJson(jsonText, configFileName, basePath, allFileList);
assert.isTrue(parsed.errors.length >= 0);
assert.isTrue(parsed.errors.filter(e => e.code === expectedDiagnosticCode).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)}`);
}
{
const parsed = getParsedCommandJsonNode(jsonText, configFileName, basePath, allFileList);
assert.isTrue(parsed.errors.length >= 0);
assert.isTrue(parsed.errors.filter(e => e.code === expectedDiagnosticCode).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)}`);
if (!noLocation) {
assert.isTrue(parsed.errors.filter(e => e.code === expectedDiagnosticCode && e.file && e.start && e.length).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)} with location information`);
function baselineParseResult(name: string, jsonTexts: () => string[]) {
it(name, () => {
const baseline: string[] = [];
for (const jsonText of jsonTexts()) {
baseline.push("Input::", jsonText);
const parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText);
baseline.push("Config::", JSON.stringify(parsed.config, /*replacer*/ undefined, " "));
baseline.push("Errors::");
baseline.push(formatErrors(parsed.error ? [parsed.error] : ts.emptyArray));
baseline.push("");
}
}
Harness.Baseline.runBaseline(`config/tsconfigParsing/${name} jsonParse.js`, baseline.join("\n"));
});
}
function assertParseFileDiagnosticsExclusion(jsonText: string, configFileName: string, basePath: string, allFileList: string[], expectedExcludedDiagnosticCode: number) {
{
const parsed = getParsedCommandJson(jsonText, configFileName, basePath, allFileList);
assert.isTrue(parsed.errors.length >= 0);
assert.isTrue(parsed.errors.findIndex(e => e.code === expectedExcludedDiagnosticCode) === -1, `Expected error code ${expectedExcludedDiagnosticCode} to not be in ${JSON.stringify(parsed.errors)}`);
}
{
const parsed = getParsedCommandJsonNode(jsonText, configFileName, basePath, allFileList);
assert.isTrue(parsed.errors.length >= 0);
assert.isTrue(parsed.errors.findIndex(e => e.code === expectedExcludedDiagnosticCode) === -1, `Expected error code ${expectedExcludedDiagnosticCode} to not be in ${JSON.stringify(parsed.errors)}`);
}
interface VerifyConfig {
jsonText: string;
configFileName: string;
basePath: string;
allFileList: string[];
}
it("returns empty config for file with only whitespaces", () => {
assertParseResult("", { config : {} });
assertParseResult(" ", { config : {} });
});
function baselinedParsed(subScenario: string, scenario: () => VerifyConfig[], skipJson?: true) {
baselineParseConfig({
scenario: "tsconfigParsing",
subScenario,
input: () => scenario().map(({ jsonText, configFileName, basePath, allFileList }) => ({
createHost: () => {
const files = allFileList.reduce((files, value) => (files[value] = "", files), {} as vfs.FileSet);
files[ts.combinePaths(basePath, configFileName)] = jsonText;
return new fakes.ParseConfigHost(new vfs.FileSystem(
/*ignoreCase*/ false,
{
cwd: basePath,
files: { "/": {}, ...files }
}));
},
jsonText,
configFileName,
basePath,
baselineParsed: (baseline, parsed) => {
baseline.push("FileNames::");
baseline.push(parsed.fileNames.join());
},
})),
skipJson
});
}
it("returns empty config for file with comments only", () => {
assertParseResult("// Comment", { config: {} });
assertParseResult("/* Comment*/", { config: {} });
});
baselineParseResult("returns empty config for file with only whitespaces", () => [
"",
" ",
]);
it("returns empty config when config is empty object", () => {
assertParseResult("{}", { config: {} });
});
baselineParseResult("returns empty config for file with comments only", () => [
"// Comment",
"/* Comment*/",
]);
it("returns config object without comments", () => {
assertParseResult(
`{ // Excluded files
"exclude": [
// Exclude d.ts
"file.d.ts"
]
}`, { config: { exclude: ["file.d.ts"] } });
baselineParseResult("returns empty config when config is empty object", () => [
"{}"
]);
assertParseResult(
`{
/* Excluded
Files
*/
"exclude": [
/* multiline comments can be in the middle of a line */"file.d.ts"
]
}`, { config: { exclude: ["file.d.ts"] } });
});
baselineParseResult("returns config object without comments", () => [
`{ // Excluded files
"exclude": [
// Exclude d.ts
"file.d.ts"
]
}`,
`{
/* Excluded
Files
*/
"exclude": [
/* multiline comments can be in the middle of a line */"file.d.ts"
]
}`,
]);
it("keeps string content untouched", () => {
assertParseResult(
`{
"exclude": [
"xx//file.d.ts"
]
}`, { config: { exclude: ["xx//file.d.ts"] } });
assertParseResult(
`{
"exclude": [
"xx/*file.d.ts*/"
]
}`, { config: { exclude: ["xx/*file.d.ts*/"] } });
});
baselineParseResult("keeps string content untouched", () => [
`{
"exclude": [
"xx//file.d.ts"
]
}`,
`{
"exclude": [
"xx/*file.d.ts*/"
]
}`,
]);
it("handles escaped characters in strings correctly", () => {
assertParseResult(
`{
"exclude": [
"xx\\"//files"
]
}`, { config: { exclude: ["xx\"//files"] } });
baselineParseResult("handles escaped characters in strings correctly", () => [
`{
"exclude": [
"xx\\"//files"
]
}`,
`{
"exclude": [
"xx\\\\" // end of line comment
]
}`,
]);
assertParseResult(
`{
"exclude": [
"xx\\\\" // end of line comment
]
}`, { config: { exclude: ["xx\\"] } });
});
baselineParseResult("returns object with error when json is invalid", () => [
"invalid",
]);
it("returns object with error when json is invalid", () => {
const parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", "invalid");
assert.deepEqual(parsed.config, {});
const expected = ts.createCompilerDiagnostic(ts.Diagnostics._0_expected, "{");
const error = parsed.error!;
assert.equal(error.messageText, expected.messageText);
assert.equal(error.category, expected.category);
assert.equal(error.code, expected.code);
assert.equal(error.start, 0);
assert.equal(error.length, "invalid".length);
});
baselineParseResult("returns object when users correctly specify library", () => [
`{
"compilerOptions": {
"lib": ["es5"]
}
}`,
`{
"compilerOptions": {
"lib": ["es5", "es6"]
}
}`,
]);
it("returns object when users correctly specify library", () => {
assertParseResult(
`{
"compilerOptions": {
"lib": ["es5"]
}
}`, {
config: { compilerOptions: { lib: ["es5"] } }
});
assertParseResult(
`{
"compilerOptions": {
"lib": ["es5", "es6"]
}
}`, {
config: { compilerOptions: { lib: ["es5", "es6"] } }
});
});
it("returns error when tsconfig have excludes", () => {
assertParseErrorWithExcludesKeyword(
`{
baselinedParsed("returns error when tsconfig have excludes", () => [{
jsonText: `{
"compilerOptions": {
"lib": ["es5"]
},
"excludes": [
"foge.ts"
]
}`);
});
}`,
configFileName: "tsconfig.json",
basePath: "/apath",
allFileList: ["/apath/test.ts", "/apath/foge.ts"],
}]);
it("ignore dotted files and folders", () => {
assertParseFileList(
`{}`,
"tsconfig.json",
"/apath",
["/apath/test.ts", "/apath/.git/a.ts", "/apath/.b.ts", "/apath/..c.ts"],
["/apath/test.ts"]
);
});
baselinedParsed("ignore dotted files and folders", () => [{
jsonText: `{}`,
configFileName: "tsconfig.json",
basePath: "/apath",
allFileList: ["/apath/test.ts", "/apath/.git/a.ts", "/apath/.b.ts", "/apath/..c.ts"],
}]);
it("allow dotted files and folders when explicitly requested", () => {
assertParseFileList(
`{
baselinedParsed("allow dotted files and folders when explicitly requested", () => [{
jsonText: `{
"files": ["/apath/.git/a.ts", "/apath/.b.ts", "/apath/..c.ts"]
}`,
"tsconfig.json",
"/apath",
["/apath/test.ts", "/apath/.git/a.ts", "/apath/.b.ts", "/apath/..c.ts"],
["/apath/.git/a.ts", "/apath/.b.ts", "/apath/..c.ts"]
);
});
configFileName: "tsconfig.json",
basePath: "/apath",
allFileList: ["/apath/test.ts", "/apath/.git/a.ts", "/apath/.b.ts", "/apath/..c.ts"],
}]);
it("exclude outDir unless overridden", () => {
baselinedParsed("exclude outDir unless overridden", () => {
const tsconfigWithoutExclude =
`{
`{
"compilerOptions": {
"outDir": "bin"
}
}`;
const tsconfigWithExclude =
`{
`{
"compilerOptions": {
"outDir": "bin"
},
"exclude": [ "obj" ]
}`;
const rootDir = "/";
const allFiles = ["/bin/a.ts", "/b.ts"];
const expectedFiles = ["/b.ts"];
assertParseFileList(tsconfigWithoutExclude, "tsconfig.json", rootDir, allFiles, expectedFiles);
assertParseFileList(tsconfigWithExclude, "tsconfig.json", rootDir, allFiles, allFiles);
const basePath = "/";
const allFileList = ["/bin/a.ts", "/b.ts"];
return [
{ jsonText: tsconfigWithoutExclude, configFileName: "tsconfig.json", basePath, allFileList },
{ jsonText: tsconfigWithExclude, configFileName: "tsconfig.json", basePath, allFileList },
];
});
it("exclude declarationDir unless overridden", () => {
baselinedParsed("exclude declarationDir unless overridden", () => {
const tsconfigWithoutExclude =
`{
`{
"compilerOptions": {
"declarationDir": "declarations"
}
}`;
const tsconfigWithExclude =
`{
`{
"compilerOptions": {
"declarationDir": "declarations"
},
"exclude": [ "types" ]
}`;
const rootDir = "/";
const allFiles = ["/declarations/a.d.ts", "/a.ts"];
const expectedFiles = ["/a.ts"];
assertParseFileList(tsconfigWithoutExclude, "tsconfig.json", rootDir, allFiles, expectedFiles);
assertParseFileList(tsconfigWithExclude, "tsconfig.json", rootDir, allFiles, allFiles);
const basePath = "/";
const allFileList = ["/declarations/a.d.ts", "/a.ts"];
return [
{ jsonText: tsconfigWithoutExclude, configFileName: "tsconfig.json", basePath, allFileList },
{ jsonText: tsconfigWithExclude, configFileName: "tsconfig.json", basePath, allFileList },
];
});
it("implicitly exclude common package folders", () => {
assertParseFileList(
`{}`,
"tsconfig.json",
"/",
["/node_modules/a.ts", "/bower_components/b.ts", "/jspm_packages/c.ts", "/d.ts", "/folder/e.ts"],
["/d.ts", "/folder/e.ts"]
);
});
baselinedParsed("implicitly exclude common package folders", () => [{
jsonText: `{}`,
configFileName: "tsconfig.json",
basePath: "/",
allFileList: ["/node_modules/a.ts", "/bower_components/b.ts", "/jspm_packages/c.ts", "/d.ts", "/folder/e.ts"],
}]);
it("parse and re-emit tsconfig.json file with diagnostics", () => {
const baseline: string[] = [];
const content = `{
"compilerOptions": {
"allowJs": true
@@ -271,166 +227,157 @@ describe("unittests:: config:: tsconfigParsing:: parseConfigFileTextToJson", ()
}
"files": ["file1.ts"]
}`;
baseline.push("Initial::", content);
const result = ts.parseJsonText("config.json", content);
const diagnostics = result.parseDiagnostics;
const configJsonObject = ts.convertToObject(result, diagnostics);
const expectedResult = {
compilerOptions: {
allowJs: true,
outDir: "bin"
},
files: ["file1.ts"]
};
assert.isTrue(diagnostics.length === 2);
assert.equal(JSON.stringify(configJsonObject), JSON.stringify(expectedResult));
const configJsonObject = ts.convertToObject(result, result.parseDiagnostics);
baseline.push("Result::", JSON.stringify(configJsonObject, undefined, " "));
baseline.push("Errors::", formatErrors(result.parseDiagnostics));
Harness.Baseline.runBaseline(`config/tsconfigParsing/parse and re-emit tsconfig.json file with diagnostics.js`, baseline.join("\n"));
});
it("generates errors for empty files list", () => {
const content = `{
baselinedParsed("generates errors for empty files list", () => [{
jsonText: `{
"files": []
}`;
assertParseFileDiagnostics(content,
"/apath/tsconfig.json",
"tests/cases/unittests",
["/apath/a.ts"],
ts.Diagnostics.The_files_list_in_config_file_0_is_empty.code);
});
}`,
configFileName: "/apath/tsconfig.json",
basePath: "tests/cases/unittests",
allFileList: ["/apath/a.ts"],
}]);
it("generates errors for empty files list when no references are provided", () => {
const content = `{
baselinedParsed("generates errors for empty files list when no references are provided", () => [{
jsonText: `{
"files": [],
"references": []
}`;
assertParseFileDiagnostics(content,
"/apath/tsconfig.json",
"tests/cases/unittests",
["/apath/a.ts"],
ts.Diagnostics.The_files_list_in_config_file_0_is_empty.code);
});
}`,
configFileName: "/apath/tsconfig.json",
basePath: "tests/cases/unittests",
allFileList: ["/apath/a.ts"],
}]);
it("does not generate errors for empty files list when one or more references are provided", () => {
const content = `{
baselinedParsed("does not generate errors for empty files list when one or more references are provided", () => [{
jsonText: `{
"files": [],
"references": [{ "path": "/apath" }]
}`;
assertParseFileDiagnosticsExclusion(content,
"/apath/tsconfig.json",
"tests/cases/unittests",
["/apath/a.ts"],
ts.Diagnostics.The_files_list_in_config_file_0_is_empty.code);
});
}`,
configFileName: "/apath/tsconfig.json",
basePath: "tests/cases/unittests",
allFileList: ["/apath/a.ts"],
}]);
it("generates errors for directory with no .ts files", () => {
const content = `{
}`;
assertParseFileDiagnostics(content,
"/apath/tsconfig.json",
"tests/cases/unittests",
["/apath/a.js"],
ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code,
/*noLocation*/ true);
});
baselinedParsed("generates errors for directory with no .ts files", () => [{
jsonText: `{
}`,
configFileName: "/apath/tsconfig.json",
basePath: "tests/cases/unittests",
allFileList: ["/apath/a.js"],
}]);
it("generates errors for empty directory", () => {
const content = `{
baselinedParsed("generates errors for empty directory", () => [{
jsonText: `{
"compilerOptions": {
"allowJs": true
}
}`;
assertParseFileDiagnostics(content,
"/apath/tsconfig.json",
"tests/cases/unittests",
[],
ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code,
/*noLocation*/ true);
});
}`,
configFileName: "/apath/tsconfig.json",
basePath: "tests/cases/unittests",
allFileList: [],
}]);
it("generates errors for empty include", () => {
const content = `{
baselinedParsed("generates errors for empty include", () => [{
jsonText: `{
"include": []
}`;
assertParseFileDiagnostics(content,
"/apath/tsconfig.json",
"tests/cases/unittests",
["/apath/a.ts"],
ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code,
/*noLocation*/ true);
});
}`,
configFileName: "/apath/tsconfig.json",
basePath: "tests/cases/unittests",
allFileList: ["/apath/a.ts"],
}]);
it("generates errors for includes with outDir", () => {
const content = `{
baselinedParsed("generates errors for includes with outDir", () => [{
jsonText: `{
"compilerOptions": {
"outDir": "./"
},
"include": ["**/*"]
}`;
assertParseFileDiagnostics(content,
"/apath/tsconfig.json",
"tests/cases/unittests",
["/apath/a.ts"],
ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code,
/*noLocation*/ true);
});
}`,
configFileName: "/apath/tsconfig.json",
basePath: "tests/cases/unittests",
allFileList: ["/apath/a.ts"],
}]);
it("generates errors for when invalid comment type present in tsconfig", () => {
const jsonText = `{
baselinedParsed("generates errors for when invalid comment type present in tsconfig", () => [{
jsonText: `{
"compilerOptions": {
## this comment does cause issues
"types" : [
]
}
}`;
const parsed = getParsedCommandJsonNode(jsonText, "/apath/tsconfig.json", "tests/cases/unittests", ["/apath/a.ts"]);
assert.isTrue(parsed.errors.length >= 0);
});
}`,
configFileName: "/apath/tsconfig.json",
basePath: "tests/cases/unittests",
allFileList: ["/apath/a.ts"],
}], /*skipJson*/ true);
it("generates errors when files is not string", () => {
assertParseFileDiagnostics(
JSON.stringify({
files: [{
compilerOptions: {
experimentalDecorators: true,
allowJs: true
}
}]
}),
"/apath/tsconfig.json",
"tests/cases/unittests",
["/apath/a.ts"],
ts.Diagnostics.Compiler_option_0_requires_a_value_of_type_1.code,
/*noLocation*/ true);
});
baselinedParsed("generates errors when files is not string", () => [{
jsonText: JSON.stringify({
files: [{
compilerOptions: {
experimentalDecorators: true,
allowJs: true
}
}]
}),
configFileName: "/apath/tsconfig.json",
basePath: "tests/cases/unittests",
allFileList: ["/apath/a.ts"],
}]);
it("generates errors when include is not string", () => {
assertParseFileDiagnostics(
JSON.stringify({
include: [
["./**/*.ts"]
]
}),
"/apath/tsconfig.json",
"tests/cases/unittests",
["/apath/a.ts"],
ts.Diagnostics.Compiler_option_0_requires_a_value_of_type_1.code,
/*noLocation*/ true);
});
baselinedParsed("generates errors when include is not string", () => [{
jsonText: JSON.stringify({
include: [
["./**/*.ts"]
]
}),
configFileName: "/apath/tsconfig.json",
basePath: "tests/cases/unittests",
allFileList: ["/apath/a.ts"],
}]);
it("parses wildcard directories even when parent directories have dots", () => {
const parsed = ts.parseConfigFileTextToJson("/foo.bar/tsconfig.json", JSON.stringify({
function baselineWildcards(subScenario: string, scenario: () => { configFileName: string, jsonText: string, basePath: string }[]) {
baselineParseConfig({
scenario: "tsconfigParsing",
subScenario,
input: () => scenario().map(({ jsonText, configFileName, basePath }) => ({
createHost: () => new fakes.ParseConfigHost(new vfs.FileSystem(/*ignoreCase*/ false, {
cwd: basePath,
files: { [configFileName]: jsonText }
})),
jsonText,
configFileName,
basePath,
baselineParsed: (baseline, parsed) => {
baseline.push("Wildcards::");
ts.getOwnKeys(parsed.wildcardDirectories!).forEach(dir =>
baseline.push(`${dir}: WatchDirectoryFlags.${(ts as any).WatchDirectoryFlags[parsed.wildcardDirectories![dir]]}`)
);
},
})),
skipErrors: true,
});
}
baselineWildcards("parses wildcard directories even when parent directories have dots", () => [{
configFileName: "/foo.bar/tsconfig.json",
jsonText: JSON.stringify({
include: ["src"]
}));
}),
basePath: "/foo.bar",
}]);
const parsedCommand = ts.parseJsonConfigFileContent(parsed.config, ts.sys, "/foo.bar");
assert.deepEqual(parsedCommand.wildcardDirectories, { "/foo.bar/src": ts.WatchDirectoryFlags.Recursive });
});
it("correctly parses wild card directories from implicit glob when two keys differ only in directory seperator", () => {
const parsed = ts.parseConfigFileTextToJson("/foo.bar/tsconfig.json", JSON.stringify({
baselineWildcards("correctly parses wild card directories from implicit glob when two keys differ only in directory seperator", () => [{
configFileName: "/foo.bar/tsconfig.json",
jsonText: JSON.stringify({
include: ["./", "./**/*.json"]
}));
const parsedCommand = ts.parseJsonConfigFileContent(parsed.config, ts.sys, "/foo");
assert.deepEqual(parsedCommand.wildcardDirectories, { "/foo": ts.WatchDirectoryFlags.Recursive });
});
}),
basePath: "/foo",
}]);
});

View File

@@ -1,49 +1,9 @@
import * as fakes from "../../_namespaces/fakes";
import * as Harness from "../../_namespaces/Harness";
import * as ts from "../../_namespaces/ts";
import * as vfs from "../../_namespaces/vfs";
import { baselineParseConfig } from "./helpers";
describe("unittests:: config:: tsconfigParsingWatchOptions:: parseConfigFileTextToJson", () => {
function createParseConfigHost(additionalFiles?: vfs.FileSet) {
return new fakes.ParseConfigHost(
new vfs.FileSystem(
/*ignoreCase*/ false,
{
cwd: "/",
files: { "/": {}, "/a.ts": "", ...additionalFiles }
}
)
);
}
function getParsedCommandJson(json: object, additionalFiles?: vfs.FileSet, existingWatchOptions?: ts.WatchOptions) {
return ts.parseJsonConfigFileContent(
json,
createParseConfigHost(additionalFiles),
"/",
/*existingOptions*/ undefined,
"tsconfig.json",
/*resolutionStack*/ undefined,
/*extraFileExtensions*/ undefined,
/*extendedConfigCache*/ undefined,
existingWatchOptions,
);
}
function getParsedCommandJsonNode(json: object, additionalFiles?: vfs.FileSet, existingWatchOptions?: ts.WatchOptions) {
const parsed = ts.parseJsonText("tsconfig.json", JSON.stringify(json));
return ts.parseJsonSourceFileConfigFileContent(
parsed,
createParseConfigHost(additionalFiles),
"/",
/*existingOptions*/ undefined,
"tsconfig.json",
/*resolutionStack*/ undefined,
/*extraFileExtensions*/ undefined,
/*extendedConfigCache*/ undefined,
existingWatchOptions,
);
}
interface VerifyWatchOptions {
json: object;
additionalFiles?: vfs.FileSet;
@@ -51,37 +11,35 @@ describe("unittests:: config:: tsconfigParsingWatchOptions:: parseConfigFileText
}
function verifyWatchOptions(subScenario: string, scenario: () => VerifyWatchOptions[]) {
describe(subScenario, () => {
it("with json api", () => {
const baseline: string[] = [];
for (const { json, additionalFiles, existingWatchOptions } of scenario()) {
addToBaseLine(baseline, json, getParsedCommandJson(json, additionalFiles, existingWatchOptions));
}
runBaseline(`${subScenario} with json api`, baseline);
});
it("with json source file api", () => {
const baseline: string[] = [];
for (const { json, additionalFiles, existingWatchOptions, } of scenario()) {
addToBaseLine(baseline, json, getParsedCommandJsonNode(json, additionalFiles, existingWatchOptions));
}
runBaseline(`${subScenario} with jsonSourceFile api`, baseline);
});
baselineParseConfig({
scenario: "tsconfigParsingWatchOptions",
subScenario,
input: () => scenario().map(({ json, additionalFiles, existingWatchOptions }) => {
const jsonText = JSON.stringify(json, undefined, " ");
return {
createHost: () => new fakes.ParseConfigHost(
new vfs.FileSystem(
/*ignoreCase*/ false,
{
cwd: "/",
files: {
"/a.ts": "",
...additionalFiles,
"/tsconfig.json": jsonText,
}
}
)
),
jsonText,
configFileName: "tsconfig.json",
existingWatchOptions,
baselineParsed: (baseline, parsed) => {
baseline.push(`Result: WatchOptions::`);
baseline.push(JSON.stringify(parsed.watchOptions, undefined, " "));
},
};
}),
});
function addToBaseLine(baseline: string[], json: object, parsed: ts.ParsedCommandLine) {
baseline.push(`Input:: ${JSON.stringify(json, /*replacer*/ undefined, " ")}`);
baseline.push(`Result: WatchOptions::`);
baseline.push(JSON.stringify(parsed.watchOptions, /*replacer*/ undefined, " "));
baseline.push(`Result: Errors::`);
baseline.push(ts.formatDiagnosticsWithColorAndContext(parsed.errors, {
getCurrentDirectory: () => "/",
getCanonicalFileName: ts.identity,
getNewLine: () => "\n"
}));
}
function runBaseline(subScenario: string, baseline: readonly string[]) {
Harness.Baseline.runBaseline(`config/tsconfigParsingWatchOptions/${subScenario}.js`, baseline.join("\n"));
}
}
verifyWatchOptions("no watchOptions specified option", () => [{

View File

@@ -0,0 +1,253 @@
import * as ts from "../../_namespaces/ts";
import { loadProjectFromFiles, verifyTsc } from "./helpers";
function emptyModule() {
return "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 getConfig({ references, options, config }: {
references?: (string | ts.ProjectReference)[];
options?: ts.CompilerOptions;
config?: object
} = {}) {
return JSON.stringify({
compilerOptions: {
composite: true,
outDir: "bin",
...options
},
references: references?.map(r => {
if (typeof r === "string") {
return { path: r };
}
return r;
}) || [],
...config,
}, undefined, " ");
}
describe("unittests:: config:: project-references meta check", () => {
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "default setup was created correctly",
fs: () => loadProjectFromFiles({
"/primary/tsconfig.json": getConfig(),
"/primary/a.ts": emptyModule(),
"/secondary/tsconfig.json": getConfig({
references: ["../primary"]
}),
"/secondary/b.ts": moduleImporting("../primary/a"),
}),
commandLineArgs: ["--p", "/primary/tsconfig.json"]
});
});
/**
* Validate that we enforce the basic settings constraints for referenced projects
*/
describe("unittests:: config:: project-references constraint checking for settings", () => {
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "errors when declaration = false",
fs: () => loadProjectFromFiles({
"/primary/tsconfig.json": getConfig({
options: {
declaration: false
}
}),
"/primary/a.ts": emptyModule(),
}),
commandLineArgs: ["--p", "/primary/tsconfig.json"]
});
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "errors when the referenced project doesnt have composite",
fs: () => loadProjectFromFiles({
"/primary/tsconfig.json": getConfig({
options: {
composite: false
}
}),
"/primary/a.ts": emptyModule(),
"/reference/tsconfig.json": getConfig({
references: ["../primary"],
config: {
files: ["b.ts"]
}
}),
"/reference/b.ts": moduleImporting("../primary/a"),
}),
commandLineArgs: ["--p", "/reference/tsconfig.json"]
});
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "does not error when the referenced project doesnt have composite if its a container project",
fs: () => loadProjectFromFiles({
"/primary/tsconfig.json": getConfig({
options: {
composite: false
}
}),
"/primary/a.ts": emptyModule(),
"/reference/tsconfig.json": getConfig({
references: ["../primary"],
config: {
files: []
}
}),
"/reference/b.ts": moduleImporting("../primary/a"),
}),
commandLineArgs: ["--p", "/reference/tsconfig.json"]
});
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "errors when the file list is not exhaustive",
fs: () => loadProjectFromFiles({
"/primary/tsconfig.json": getConfig({
config: {
files: ["a.ts"]
}
}),
"/primary/a.ts": "import * as b from './b'",
"/primary/b.ts": "export {}",
}),
commandLineArgs: ["--p", "/primary/tsconfig.json"]
});
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "errors when the referenced project doesnt exist",
fs: () => loadProjectFromFiles({
"/primary/tsconfig.json": getConfig({
references: ["../foo"]
}),
"/primary/a.ts": emptyModule(),
}),
commandLineArgs: ["--p", "/primary/tsconfig.json"]
});
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "errors when a prepended project reference doesnt set outFile",
fs: () => loadProjectFromFiles({
"/primary/tsconfig.json": getConfig({
references: [{ path: "../someProj", prepend: true }],
}),
"/primary/a.ts": emptyModule(),
"/someProj/tsconfig.json": getConfig(),
"/someProj/b.ts": "const x = 100;",
}),
commandLineArgs: ["--p", "/primary/tsconfig.json", "--ignoreDeprecations", "5.0"]
});
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "errors when a prepended project reference output doesnt exist",
fs: () => loadProjectFromFiles({
"/primary/tsconfig.json": getConfig({
references: [{ path: "../someProj", prepend: true }],
}),
"/primary/a.ts": "const y = x;",
"/someProj/tsconfig.json": getConfig({
options: { outFile: "foo.js" }
}),
"/someProj/b.ts": "const x = 100;",
}),
commandLineArgs: ["--p", "/primary/tsconfig.json", "--ignoreDeprecations", "5.0"]
});
});
/**
* Path mapping behavior
*/
describe("unittests:: config:: project-references path mapping", () => {
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "redirects to the output dts file",
fs: () => loadProjectFromFiles({
"/alpha/tsconfig.json": getConfig(),
"/alpha/a.ts": "export const m: number = 3;",
"/alpha/bin/a.d.ts": emptyModule(),
"/beta/tsconfig.json": getConfig({
references: ["../alpha"]
}),
"/beta/b.ts": "import { m } from '../alpha/a'",
}),
commandLineArgs: ["--p", "/beta/tsconfig.json", "--explainFiles"]
});
});
describe("unittests:: config:: project-references nice-behavior", () => {
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "issues a nice error when the input file is missing",
fs: () => loadProjectFromFiles({
"/alpha/tsconfig.json": getConfig(),
"/alpha/a.ts": "export const m: number = 3;",
"/beta/tsconfig.json": getConfig({
references: ["../alpha"]
}),
"/beta/b.ts": "import { m } from '../alpha/a'",
}),
commandLineArgs: ["--p", "/beta/tsconfig.json"]
});
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "issues a nice error when the input file is missing when module reference is not relative",
fs: () => loadProjectFromFiles({
"/alpha/tsconfig.json": getConfig(),
"/alpha/a.ts": "export const m: number = 3;",
"/beta/tsconfig.json": getConfig({
references: ["../alpha"],
options: {
baseUrl: "./",
paths: {
"@alpha/*": ["/alpha/*"]
}
}
}),
"/beta/b.ts": "import { m } from '@alpha/a'",
}),
commandLineArgs: ["--p", "/beta/tsconfig.json"]
});
});
/**
* 'composite' behavior
*/
describe("unittests:: config:: project-references behavior changes under composite: true", () => {
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "doesnt infer the rootDir from source paths",
fs: () => loadProjectFromFiles({
"/alpha/tsconfig.json": getConfig(),
"/alpha/src/a.ts": "export const m: number = 3;",
}),
commandLineArgs: ["--p", "/alpha/tsconfig.json"]
});
});
describe("unittests:: config:: project-references errors when a file in a composite project occurs outside the root", () => {
verifyTsc({
scenario: "projectReferencesConfig",
subScenario: "errors when a file is outside the rootdir",
fs: () => loadProjectFromFiles({
"/alpha/tsconfig.json": getConfig(),
"/alpha/src/a.ts": "import * as b from '../../beta/b'",
"/beta/b.ts": "export { }",
}),
commandLineArgs: ["--p", "/alpha/tsconfig.json"]
});
});