mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge remote-tracking branch 'upstream/master' into async-es2018
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "../tsconfig-base",
|
||||
"extends": "../tsconfig-noncomposite-base",
|
||||
"compilerOptions": {
|
||||
"outFile": "../../built/local/run.js",
|
||||
"composite": false,
|
||||
@@ -43,6 +43,7 @@
|
||||
"unittests/asserts.ts",
|
||||
"unittests/base64.ts",
|
||||
"unittests/builder.ts",
|
||||
"unittests/comments.ts",
|
||||
"unittests/compilerCore.ts",
|
||||
"unittests/convertToBase64.ts",
|
||||
"unittests/customTransforms.ts",
|
||||
|
||||
@@ -5,5 +5,8 @@ namespace ts {
|
||||
assert.throws(() => assert.deepEqual(createNodeArray([], /*hasTrailingComma*/ true), createNodeArray([], /*hasTrailingComma*/ false)));
|
||||
assert.deepEqual(createNodeArray([createIdentifier("A")], /*hasTrailingComma*/ true), createNodeArray([createIdentifier("A")], /*hasTrailingComma*/ true));
|
||||
});
|
||||
it("assertNever on string has correct error", () => {
|
||||
assert.throws(() => Debug.assertNever("hi" as never), "Debug Failure. Illegal value: \"hi\"");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace ts {
|
||||
describe("comment parsing", () => {
|
||||
const withShebang = `#! node
|
||||
/** comment */
|
||||
// another one
|
||||
;`;
|
||||
const noShebang = `/** comment */
|
||||
// another one
|
||||
;`;
|
||||
const withTrailing = `;/* comment */
|
||||
// another one
|
||||
`;
|
||||
it("skips shebang", () => {
|
||||
const result = getLeadingCommentRanges(withShebang, 0);
|
||||
assert.isDefined(result);
|
||||
assert.strictEqual(result!.length, 2);
|
||||
});
|
||||
|
||||
it("treats all comments at start of file as leading comments", () => {
|
||||
const result = getLeadingCommentRanges(noShebang, 0);
|
||||
assert.isDefined(result);
|
||||
assert.strictEqual(result!.length, 2);
|
||||
});
|
||||
|
||||
it("returns leading comments if position is not 0", () => {
|
||||
const result = getLeadingCommentRanges(withTrailing, 1);
|
||||
assert.isDefined(result);
|
||||
assert.strictEqual(result!.length, 1);
|
||||
assert.strictEqual(result![0].kind, SyntaxKind.SingleLineCommentTrivia);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -85,7 +85,7 @@ namespace ts {
|
||||
|
||||
// We shouldn't have any errors about invalid tsconfig files in these tests
|
||||
assert(config && !error, flattenDiagnosticMessageText(error && error.messageText, "\n"));
|
||||
const file = parseJsonConfigFileContent(config, parseConfigHostFromCompilerHost(host), getDirectoryPath(entryPointConfigFileName), {}, entryPointConfigFileName);
|
||||
const file = parseJsonConfigFileContent(config, parseConfigHostFromCompilerHostLike(host), getDirectoryPath(entryPointConfigFileName), {}, entryPointConfigFileName);
|
||||
file.options.configFilePath = entryPointConfigFileName;
|
||||
const prog = createProgram({
|
||||
rootNames: file.fileNames,
|
||||
|
||||
@@ -129,5 +129,34 @@ namespace ts {
|
||||
},
|
||||
{ sourceMap: true }
|
||||
);
|
||||
|
||||
emitsCorrectly("skipTriviaExternalSourceFiles",
|
||||
[
|
||||
{
|
||||
file: "source.ts",
|
||||
// The source file contains preceding trivia (e.g. whitespace) to try to confuse the `skipSourceTrivia` function.
|
||||
text: " original;"
|
||||
},
|
||||
],
|
||||
{
|
||||
before: [
|
||||
context => node => visitNode(node, function visitor(node: Node): Node {
|
||||
if (isIdentifier(node) && node.text === "original") {
|
||||
const newNode = createIdentifier("changed");
|
||||
setSourceMapRange(newNode, {
|
||||
pos: 0,
|
||||
end: 7,
|
||||
// Do not provide a custom skipTrivia function for `source`.
|
||||
source: createSourceMapSource("another.html", "changed;")
|
||||
});
|
||||
return newNode;
|
||||
}
|
||||
return visitEachChild(node, visitor, context);
|
||||
})
|
||||
]
|
||||
},
|
||||
{ sourceMap: true }
|
||||
);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -400,6 +400,46 @@ namespace Foo {
|
||||
}
|
||||
}).outputText;
|
||||
});
|
||||
|
||||
// https://github.com/Microsoft/TypeScript/issues/24709
|
||||
testBaseline("issue24709", () => {
|
||||
const fs = vfs.createFromFileSystem(Harness.IO, /*caseSensitive*/ true);
|
||||
const transformed = transform(createSourceFile("source.ts", "class X { echo(x: string) { return x; } }", ScriptTarget.ES3), [transformSourceFile]);
|
||||
const transformedSourceFile = transformed.transformed[0];
|
||||
transformed.dispose();
|
||||
const host = new fakes.CompilerHost(fs);
|
||||
host.getSourceFile = () => transformedSourceFile;
|
||||
const program = createProgram(["source.ts"], {
|
||||
target: ScriptTarget.ES3,
|
||||
module: ModuleKind.None,
|
||||
noLib: true
|
||||
}, host);
|
||||
program.emit(transformedSourceFile, (_p, s, b) => host.writeFile("source.js", s, b));
|
||||
return host.readFile("source.js")!.toString();
|
||||
|
||||
function transformSourceFile(context: TransformationContext) {
|
||||
const visitor: Visitor = (node) => {
|
||||
if (isMethodDeclaration(node)) {
|
||||
return updateMethod(
|
||||
node,
|
||||
node.decorators,
|
||||
node.modifiers,
|
||||
node.asteriskToken,
|
||||
createIdentifier("foobar"),
|
||||
node.questionToken,
|
||||
node.typeParameters,
|
||||
node.parameters,
|
||||
node.type,
|
||||
node.body,
|
||||
);
|
||||
}
|
||||
return visitEachChild(node, visitor, context);
|
||||
};
|
||||
return (node: SourceFile) => visitNode(node, visitor);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
namespace ts {
|
||||
let currentTime = 100;
|
||||
|
||||
function getExpectedDiagnosticForProjectsInBuild(...projects: string[]): fakes.ExpectedDiagnostic {
|
||||
return [Diagnostics.Projects_in_this_build_Colon_0, projects.map(p => "\r\n * " + p).join("")];
|
||||
}
|
||||
|
||||
export namespace Sample1 {
|
||||
tick();
|
||||
const projFs = loadProjectFromDisk("tests/projects/sample1");
|
||||
@@ -67,7 +72,11 @@ namespace ts {
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
host.assertDiagnosticMessages(Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0);
|
||||
host.assertDiagnosticMessages(
|
||||
[Diagnostics.A_non_dry_build_would_build_project_0, "/src/core/tsconfig.json"],
|
||||
[Diagnostics.A_non_dry_build_would_build_project_0, "/src/logic/tsconfig.json"],
|
||||
[Diagnostics.A_non_dry_build_would_build_project_0, "/src/tests/tsconfig.json"]
|
||||
);
|
||||
|
||||
// Check for outputs to not be written. Not an exhaustive list
|
||||
for (const output of allExpectedOutputs) {
|
||||
@@ -86,7 +95,11 @@ namespace ts {
|
||||
host.clearDiagnostics();
|
||||
builder = createSolutionBuilder(host, ["/src/tests"], { dry: true, force: false, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
host.assertDiagnosticMessages(Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date, Diagnostics.Project_0_is_up_to_date);
|
||||
host.assertDiagnosticMessages(
|
||||
[Diagnostics.Project_0_is_up_to_date, "/src/core/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_up_to_date, "/src/logic/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_up_to_date, "/src/tests/tsconfig.json"]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -146,13 +159,15 @@ namespace ts {
|
||||
host.clearDiagnostics();
|
||||
builder.resetBuildContext();
|
||||
builder.buildAllProjects();
|
||||
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
|
||||
Diagnostics.Building_project_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
|
||||
Diagnostics.Building_project_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
|
||||
Diagnostics.Building_project_0);
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/logic/tsconfig.json", "src/logic/index.js"],
|
||||
[Diagnostics.Building_project_0, "/src/logic/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/tests/tsconfig.json", "src/tests/index.js"],
|
||||
[Diagnostics.Building_project_0, "/src/tests/tsconfig.json"]
|
||||
);
|
||||
tick();
|
||||
});
|
||||
|
||||
@@ -161,10 +176,12 @@ namespace ts {
|
||||
host.clearDiagnostics();
|
||||
builder.resetBuildContext();
|
||||
builder.buildAllProjects();
|
||||
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2);
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/core/tsconfig.json", "src/core/anotherModule.ts", "src/core/anotherModule.js"],
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/logic/tsconfig.json", "src/logic/index.ts", "src/logic/index.js"],
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/tests/tsconfig.json", "src/tests/index.ts", "src/tests/index.js"]
|
||||
);
|
||||
tick();
|
||||
});
|
||||
|
||||
@@ -175,11 +192,13 @@ namespace ts {
|
||||
builder.resetBuildContext();
|
||||
builder.buildAllProjects();
|
||||
|
||||
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
|
||||
Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2,
|
||||
Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2,
|
||||
Diagnostics.Building_project_0);
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/core/tsconfig.json", "src/core/anotherModule.ts", "src/core/anotherModule.js"],
|
||||
[Diagnostics.Project_0_is_up_to_date_because_newest_input_1_is_older_than_oldest_output_2, "src/logic/tsconfig.json", "src/logic/index.ts", "src/logic/index.js"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/tests/tsconfig.json", "src/tests/index.js", "src/tests/index.ts"],
|
||||
[Diagnostics.Building_project_0, "/src/tests/tsconfig.json"]
|
||||
);
|
||||
tick();
|
||||
});
|
||||
|
||||
@@ -190,13 +209,15 @@ namespace ts {
|
||||
builder.resetBuildContext();
|
||||
builder.buildAllProjects();
|
||||
|
||||
host.assertDiagnosticMessages(Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2,
|
||||
Diagnostics.Building_project_0,
|
||||
Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies,
|
||||
Diagnostics.Updating_output_timestamps_of_project_0,
|
||||
Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies,
|
||||
Diagnostics.Updating_output_timestamps_of_project_0);
|
||||
host.assertDiagnosticMessages(
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_oldest_output_1_is_older_than_newest_input_2, "src/core/tsconfig.json", "src/core/anotherModule.js", "src/core/index.ts"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies, "src/logic/tsconfig.json"],
|
||||
[Diagnostics.Updating_output_timestamps_of_project_0, "/src/logic/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_up_to_date_with_d_ts_files_from_its_dependencies, "src/tests/tsconfig.json"],
|
||||
[Diagnostics.Updating_output_timestamps_of_project_0, "/src/tests/tsconfig.json"]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -210,14 +231,14 @@ namespace ts {
|
||||
replaceText(fs, "/src/logic/index.ts", "c.multiply(10, 15)", `c.muitply()`);
|
||||
builder.buildAllProjects();
|
||||
host.assertDiagnosticMessages(
|
||||
Diagnostics.Projects_in_this_build_Colon_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
|
||||
Diagnostics.Building_project_0,
|
||||
Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist,
|
||||
Diagnostics.Building_project_0,
|
||||
Diagnostics.Property_0_does_not_exist_on_type_1,
|
||||
Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_has_errors,
|
||||
Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors
|
||||
getExpectedDiagnosticForProjectsInBuild("src/core/tsconfig.json", "src/logic/tsconfig.json", "src/tests/tsconfig.json"),
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/core/tsconfig.json", "src/core/anotherModule.js"],
|
||||
[Diagnostics.Building_project_0, "/src/core/tsconfig.json"],
|
||||
[Diagnostics.Project_0_is_out_of_date_because_output_file_1_does_not_exist, "src/logic/tsconfig.json", "src/logic/index.js"],
|
||||
[Diagnostics.Building_project_0, "/src/logic/tsconfig.json"],
|
||||
[Diagnostics.Property_0_does_not_exist_on_type_1, "muitply", `typeof import("/src/core/index")`],
|
||||
[Diagnostics.Project_0_can_t_be_built_because_its_dependency_1_has_errors, "src/tests/tsconfig.json", "src/logic"],
|
||||
[Diagnostics.Skipping_build_of_project_0_because_its_dependency_1_has_errors, "/src/tests/tsconfig.json", "/src/logic"]
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -234,39 +255,46 @@ namespace ts {
|
||||
// Update a timestamp in the middle project
|
||||
tick();
|
||||
touch(fs, "/src/logic/index.ts");
|
||||
const originalWriteFile = fs.writeFileSync;
|
||||
const writtenFiles = createMap<true>();
|
||||
fs.writeFileSync = (path, data, encoding) => {
|
||||
writtenFiles.set(path, true);
|
||||
originalWriteFile.call(fs, path, data, encoding);
|
||||
};
|
||||
// Because we haven't reset the build context, the builder should assume there's nothing to do right now
|
||||
const status = builder.getUpToDateStatusOfFile(builder.resolveProjectName("/src/logic"));
|
||||
assert.equal(status.type, UpToDateStatusType.UpToDate, "Project should be assumed to be up-to-date");
|
||||
verifyInvalidation(/*expectedToWriteTests*/ false);
|
||||
|
||||
// Rebuild this project
|
||||
tick();
|
||||
builder.invalidateProject("/src/logic");
|
||||
builder.buildInvalidatedProject();
|
||||
// The file should be updated
|
||||
assert.equal(fs.statSync("/src/logic/index.js").mtimeMs, time(), "JS file should have been rebuilt");
|
||||
assert.isBelow(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should *not* have been rebuilt");
|
||||
|
||||
// Does not build tests or core because there is no change in declaration file
|
||||
tick();
|
||||
builder.buildInvalidatedProject();
|
||||
assert.isBelow(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should have been rebuilt");
|
||||
assert.isBelow(fs.statSync("/src/core/index.js").mtimeMs, time(), "Upstream JS file should not have been rebuilt");
|
||||
|
||||
// Rebuild this project
|
||||
tick();
|
||||
fs.writeFileSync("/src/logic/index.ts", `${fs.readFileSync("/src/logic/index.ts")}
|
||||
export class cNew {}`);
|
||||
builder.invalidateProject("/src/logic");
|
||||
builder.buildInvalidatedProject();
|
||||
// The file should be updated
|
||||
assert.equal(fs.statSync("/src/logic/index.js").mtimeMs, time(), "JS file should have been rebuilt");
|
||||
assert.isBelow(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should *not* have been rebuilt");
|
||||
verifyInvalidation(/*expectedToWriteTests*/ true);
|
||||
|
||||
// Build downstream projects should update 'tests', but not 'core'
|
||||
tick();
|
||||
builder.buildInvalidatedProject();
|
||||
assert.isBelow(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should have been rebuilt");
|
||||
assert.isBelow(fs.statSync("/src/core/index.js").mtimeMs, time(), "Upstream JS file should not have been rebuilt");
|
||||
function verifyInvalidation(expectedToWriteTests: boolean) {
|
||||
// Rebuild this project
|
||||
tick();
|
||||
builder.invalidateProject("/src/logic");
|
||||
builder.buildInvalidatedProject();
|
||||
// The file should be updated
|
||||
assert.isTrue(writtenFiles.has("/src/logic/index.js"), "JS file should have been rebuilt");
|
||||
assert.equal(fs.statSync("/src/logic/index.js").mtimeMs, time(), "JS file should have been rebuilt");
|
||||
assert.isFalse(writtenFiles.has("/src/tests/index.js"), "Downstream JS file should *not* have been rebuilt");
|
||||
assert.isBelow(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should *not* have been rebuilt");
|
||||
writtenFiles.clear();
|
||||
|
||||
// Build downstream projects should update 'tests', but not 'core'
|
||||
tick();
|
||||
builder.buildInvalidatedProject();
|
||||
if (expectedToWriteTests) {
|
||||
assert.isTrue(writtenFiles.has("/src/tests/index.js"), "Downstream JS file should have been rebuilt");
|
||||
}
|
||||
else {
|
||||
assert.equal(writtenFiles.size, 0, "Should not write any new files");
|
||||
}
|
||||
assert.equal(fs.statSync("/src/tests/index.js").mtimeMs, time(), "Downstream JS file should have new timestamp");
|
||||
assert.isBelow(fs.statSync("/src/core/index.js").mtimeMs, time(), "Upstream JS file should not have been rebuilt");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -274,12 +302,12 @@ export class cNew {}`);
|
||||
const projFs = loadProjectFromDisk("tests/projects/resolveJsonModuleAndComposite");
|
||||
const allExpectedOutputs = ["/src/tests/dist/src/index.js", "/src/tests/dist/src/index.d.ts", "/src/tests/dist/src/hello.json"];
|
||||
|
||||
function verifyProjectWithResolveJsonModule(configFile: string, ...expectedDiagnosticMessages: DiagnosticMessage[]) {
|
||||
function verifyProjectWithResolveJsonModule(configFile: string, ...expectedDiagnosticMessages: fakes.ExpectedDiagnostic[]) {
|
||||
const fs = projFs.shadow();
|
||||
verifyProjectWithResolveJsonModuleWithFs(fs, configFile, allExpectedOutputs, ...expectedDiagnosticMessages);
|
||||
}
|
||||
|
||||
function verifyProjectWithResolveJsonModuleWithFs(fs: vfs.FileSystem, configFile: string, allExpectedOutputs: ReadonlyArray<string>, ...expectedDiagnosticMessages: DiagnosticMessage[]) {
|
||||
function verifyProjectWithResolveJsonModuleWithFs(fs: vfs.FileSystem, configFile: string, allExpectedOutputs: ReadonlyArray<string>, ...expectedDiagnosticMessages: fakes.ExpectedDiagnostic[]) {
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const builder = createSolutionBuilder(host, [configFile], { dry: false, force: false, verbose: false });
|
||||
builder.buildAllProjects();
|
||||
@@ -293,7 +321,10 @@ export class cNew {}`);
|
||||
}
|
||||
|
||||
it("with resolveJsonModule and include only", () => {
|
||||
verifyProjectWithResolveJsonModule("/src/tests/tsconfig_withInclude.json", Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern);
|
||||
verifyProjectWithResolveJsonModule("/src/tests/tsconfig_withInclude.json", [
|
||||
Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern,
|
||||
"/src/tests/src/hello.json"
|
||||
]);
|
||||
});
|
||||
|
||||
it("with resolveJsonModule and include of *.json along with other include", () => {
|
||||
@@ -408,7 +439,7 @@ export default hello.hello`);
|
||||
"/src/c.ts"
|
||||
];
|
||||
|
||||
function verifyBuild(modifyDiskLayout: (fs: vfs.FileSystem) => void, allExpectedOutputs: ReadonlyArray<string>, expectedDiagnostics: DiagnosticMessage[], expectedFileTraces: ReadonlyArray<string>) {
|
||||
function verifyBuild(modifyDiskLayout: (fs: vfs.FileSystem) => void, allExpectedOutputs: ReadonlyArray<string>, expectedFileTraces: ReadonlyArray<string>, ...expectedDiagnostics: fakes.ExpectedDiagnostic[]) {
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
modifyDiskLayout(fs);
|
||||
@@ -435,11 +466,11 @@ export const b = new A();`);
|
||||
}
|
||||
|
||||
it("verify that it builds correctly", () => {
|
||||
verifyBuild(noop, allExpectedOutputs, emptyArray, expectedFileTraces);
|
||||
verifyBuild(noop, allExpectedOutputs, expectedFileTraces);
|
||||
});
|
||||
|
||||
it("verify that it builds correctly when the referenced project uses different module resolution", () => {
|
||||
verifyBuild(fs => modifyFsBTsToNonRelativeImport(fs, "classic"), allExpectedOutputs, emptyArray, expectedFileTraces);
|
||||
verifyBuild(fs => modifyFsBTsToNonRelativeImport(fs, "classic"), allExpectedOutputs, expectedFileTraces);
|
||||
});
|
||||
|
||||
it("verify that it build reports error about module not found with node resolution with external module name", () => {
|
||||
@@ -451,10 +482,25 @@ export const b = new A();`);
|
||||
];
|
||||
verifyBuild(fs => modifyFsBTsToNonRelativeImport(fs, "node"),
|
||||
allExpectedOutputs,
|
||||
[Diagnostics.Cannot_find_module_0],
|
||||
expectedFileTraces);
|
||||
expectedFileTraces,
|
||||
[Diagnostics.Cannot_find_module_0, "a"],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("unittests:: tsbuild - when tsconfig extends the missing file", () => {
|
||||
const projFs = loadProjectFromDisk("tests/projects/missingExtendedConfig");
|
||||
const fs = projFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/tsconfig.json"], {});
|
||||
builder.buildAllProjects();
|
||||
host.assertDiagnosticMessages(
|
||||
[Diagnostics.The_specified_path_does_not_exist_Colon_0, "/src/foobar.json"],
|
||||
[Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, "/src/tsconfig.first.json", "[\"**/*\"]", "[]"],
|
||||
[Diagnostics.The_specified_path_does_not_exist_Colon_0, "/src/foobar.json"],
|
||||
[Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, "/src/tsconfig.second.json", "[\"**/*\"]", "[]"]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export namespace OutFile {
|
||||
@@ -462,11 +508,20 @@ export const b = new A();`);
|
||||
|
||||
describe("unittests:: tsbuild - baseline sectioned sourcemaps", () => {
|
||||
let fs: vfs.FileSystem | undefined;
|
||||
const actualReadFileMap = createMap<number>();
|
||||
before(() => {
|
||||
fs = outFileFs.shadow();
|
||||
const host = new fakes.SolutionBuilderHost(fs);
|
||||
const builder = createSolutionBuilder(host, ["/src/third"], { dry: false, force: false, verbose: false });
|
||||
host.clearDiagnostics();
|
||||
const originalReadFile = host.readFile;
|
||||
host.readFile = path => {
|
||||
// Dont record libs
|
||||
if (path.startsWith("/src/")) {
|
||||
actualReadFileMap.set(path, (actualReadFileMap.get(path) || 0) + 1);
|
||||
}
|
||||
return originalReadFile.call(host, path);
|
||||
};
|
||||
builder.buildAllProjects();
|
||||
host.assertDiagnosticMessages(/*none*/);
|
||||
});
|
||||
@@ -478,6 +533,38 @@ export const b = new A();`);
|
||||
// tslint:disable-next-line:no-null-keyword
|
||||
Harness.Baseline.runBaseline("outfile-concat.js", patch ? vfs.formatPatch(patch) : null);
|
||||
});
|
||||
it("verify readFile calls", () => {
|
||||
const expected = [
|
||||
// Configs
|
||||
"/src/third/tsconfig.json",
|
||||
"/src/second/tsconfig.json",
|
||||
"/src/first/tsconfig.json",
|
||||
|
||||
// Source files
|
||||
"/src/third/third_part1.ts",
|
||||
"/src/second/second_part1.ts",
|
||||
"/src/second/second_part2.ts",
|
||||
"/src/first/first_PART1.ts",
|
||||
"/src/first/first_part2.ts",
|
||||
"/src/first/first_part3.ts",
|
||||
|
||||
// outputs
|
||||
"/src/first/bin/first-output.js",
|
||||
"/src/first/bin/first-output.js.map",
|
||||
"/src/first/bin/first-output.d.ts",
|
||||
"/src/first/bin/first-output.d.ts.map",
|
||||
"/src/2/second-output.js",
|
||||
"/src/2/second-output.js.map",
|
||||
"/src/2/second-output.d.ts",
|
||||
"/src/2/second-output.d.ts.map"
|
||||
];
|
||||
|
||||
assert.equal(actualReadFileMap.size, expected.length, `Expected: ${JSON.stringify(expected)} \nActual: ${JSON.stringify(arrayFrom(actualReadFileMap.entries()))}`);
|
||||
expected.forEach(expectedValue => {
|
||||
const actual = actualReadFileMap.get(expectedValue);
|
||||
assert.equal(actual, 1, `Mismatch in read file call number for: ${expectedValue}\nExpected: ${JSON.stringify(expected)} \nActual: ${JSON.stringify(arrayFrom(actualReadFileMap.entries()))}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("unittests:: tsbuild - downstream prepend projects always get rebuilt", () => {
|
||||
@@ -516,7 +603,7 @@ export const b = new A();`);
|
||||
|
||||
host.clearDiagnostics();
|
||||
builder.buildAllProjects();
|
||||
host.assertDiagnosticMessages(Diagnostics.The_files_list_in_config_file_0_is_empty);
|
||||
host.assertDiagnosticMessages([Diagnostics.The_files_list_in_config_file_0_is_empty, "/src/no-references/tsconfig.json"]);
|
||||
|
||||
// Check for outputs to not be written.
|
||||
for (const output of allExpectedOutputs) {
|
||||
|
||||
@@ -2,18 +2,37 @@ namespace ts.tscWatch {
|
||||
import projectsLocation = TestFSWithWatch.tsbuildProjectsLocation;
|
||||
import getFilePathInProject = TestFSWithWatch.getTsBuildProjectFilePath;
|
||||
import getFileFromProject = TestFSWithWatch.getTsBuildProjectFile;
|
||||
type TsBuildWatchSystem = WatchedSystem & { writtenFiles: Map<true>; };
|
||||
|
||||
function createTsBuildWatchSystem(fileOrFolderList: ReadonlyArray<TestFSWithWatch.FileOrFolderOrSymLink>, params?: TestFSWithWatch.TestServerHostCreationParameters) {
|
||||
const host = createWatchedSystem(fileOrFolderList, params) as TsBuildWatchSystem;
|
||||
const originalWriteFile = host.writeFile;
|
||||
host.writtenFiles = createMap<true>();
|
||||
host.writeFile = (fileName, content) => {
|
||||
originalWriteFile.call(host, fileName, content);
|
||||
const path = host.toFullPath(fileName);
|
||||
host.writtenFiles.set(path, true);
|
||||
};
|
||||
return host;
|
||||
}
|
||||
|
||||
export function createSolutionBuilder(system: WatchedSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
|
||||
const host = createSolutionBuilderWithWatchHost(system);
|
||||
return ts.createSolutionBuilder(host, rootNames, defaultOptions || { watch: true });
|
||||
}
|
||||
|
||||
function createSolutionBuilderWithWatch(host: WatchedSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
|
||||
function createSolutionBuilderWithWatch(host: TsBuildWatchSystem, rootNames: ReadonlyArray<string>, defaultOptions?: BuildOptions) {
|
||||
const solutionBuilder = createSolutionBuilder(host, rootNames, defaultOptions);
|
||||
solutionBuilder.buildAllProjects();
|
||||
solutionBuilder.startWatching();
|
||||
return solutionBuilder;
|
||||
}
|
||||
|
||||
type OutputFileStamp = [string, Date | undefined, boolean];
|
||||
function transformOutputToOutputFileStamp(f: string, host: TsBuildWatchSystem): OutputFileStamp {
|
||||
return [f, host.getModifiedTime(f), host.writtenFiles.has(host.toFullPath(f))] as OutputFileStamp;
|
||||
}
|
||||
|
||||
describe("unittests:: tsbuild-watch program updates", () => {
|
||||
const project = "sample1";
|
||||
const enum SubProject {
|
||||
@@ -61,12 +80,11 @@ namespace ts.tscWatch {
|
||||
return [`${file}.js`, `${file}.d.ts`];
|
||||
}
|
||||
|
||||
type OutputFileStamp = [string, Date | undefined];
|
||||
function getOutputStamps(host: WatchedSystem, subProject: SubProject, baseFileNameWithoutExtension: string): OutputFileStamp[] {
|
||||
return getOutputFileNames(subProject, baseFileNameWithoutExtension).map(f => [f, host.getModifiedTime(f)] as OutputFileStamp);
|
||||
function getOutputStamps(host: TsBuildWatchSystem, subProject: SubProject, baseFileNameWithoutExtension: string): OutputFileStamp[] {
|
||||
return getOutputFileNames(subProject, baseFileNameWithoutExtension).map(f => transformOutputToOutputFileStamp(f, host));
|
||||
}
|
||||
|
||||
function getOutputFileStamps(host: WatchedSystem, additionalFiles?: ReadonlyArray<[SubProject, string]>): OutputFileStamp[] {
|
||||
function getOutputFileStamps(host: TsBuildWatchSystem, additionalFiles?: ReadonlyArray<[SubProject, string]>): OutputFileStamp[] {
|
||||
const result = [
|
||||
...getOutputStamps(host, SubProject.core, "anotherModule"),
|
||||
...getOutputStamps(host, SubProject.core, "index"),
|
||||
@@ -76,18 +94,21 @@ namespace ts.tscWatch {
|
||||
if (additionalFiles) {
|
||||
additionalFiles.forEach(([subProject, baseFileNameWithoutExtension]) => result.push(...getOutputStamps(host, subProject, baseFileNameWithoutExtension)));
|
||||
}
|
||||
host.writtenFiles.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
function verifyChangedFiles(actualStamps: OutputFileStamp[], oldTimeStamps: OutputFileStamp[], changedFiles: string[]) {
|
||||
function verifyChangedFiles(actualStamps: OutputFileStamp[], oldTimeStamps: OutputFileStamp[], changedFiles: ReadonlyArray<string>, modifiedTimeStampFiles: ReadonlyArray<string>) {
|
||||
for (let i = 0; i < oldTimeStamps.length; i++) {
|
||||
const actual = actualStamps[i];
|
||||
const old = oldTimeStamps[i];
|
||||
if (contains(changedFiles, actual[0])) {
|
||||
assert.isTrue((actual[1] || 0) > (old[1] || 0), `${actual[0]} expected to written`);
|
||||
const expectedIsChanged = contains(changedFiles, actual[0]);
|
||||
assert.equal(actual[2], contains(changedFiles, actual[0]), `Expected ${actual[0]} to be written.`);
|
||||
if (expectedIsChanged || contains(modifiedTimeStampFiles, actual[0])) {
|
||||
assert.isTrue((actual[1] || 0) > (old[1] || 0), `${actual[0]} file expected to have newer modified time because it is expected to ${expectedIsChanged ? "be changed" : "have modified time stamp"}`);
|
||||
}
|
||||
else {
|
||||
assert.equal(actual[1], old[1], `${actual[0]} expected to not change`);
|
||||
assert.equal(actual[1], old[1], `${actual[0]} expected to not change or have timestamp modified.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,7 +122,7 @@ namespace ts.tscWatch {
|
||||
const testProjectExpectedWatchedDirectoriesRecursive = [projectPath(SubProject.core), projectPath(SubProject.logic)];
|
||||
|
||||
function createSolutionInWatchMode(allFiles: ReadonlyArray<File>, defaultOptions?: BuildOptions, disableConsoleClears?: boolean) {
|
||||
const host = createWatchedSystem(allFiles, { currentDirectory: projectsLocation });
|
||||
const host = createTsBuildWatchSystem(allFiles, { currentDirectory: projectsLocation });
|
||||
createSolutionBuilderWithWatch(host, [`${project}/${SubProject.tests}`], defaultOptions);
|
||||
verifyWatches(host);
|
||||
checkOutputErrorsInitial(host, emptyArray, disableConsoleClears);
|
||||
@@ -112,7 +133,7 @@ namespace ts.tscWatch {
|
||||
return host;
|
||||
}
|
||||
|
||||
function verifyWatches(host: WatchedSystem) {
|
||||
function verifyWatches(host: TsBuildWatchSystem) {
|
||||
checkWatchedFiles(host, testProjectExpectedWatchedFiles);
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
|
||||
checkWatchedDirectories(host, testProjectExpectedWatchedDirectoriesRecursive, /*recursive*/ true);
|
||||
@@ -134,30 +155,50 @@ namespace ts.tscWatch {
|
||||
const host = createSolutionInWatchMode(allFiles);
|
||||
return { host, verifyChangeWithFile, verifyChangeAfterTimeout, verifyWatches };
|
||||
|
||||
function verifyChangeWithFile(fileName: string, content: string) {
|
||||
function verifyChangeWithFile(fileName: string, content: string, local?: boolean) {
|
||||
const outputFileStamps = getOutputFileStamps(host, additionalFiles);
|
||||
host.writeFile(fileName, content);
|
||||
verifyChangeAfterTimeout(outputFileStamps);
|
||||
verifyChangeAfterTimeout(outputFileStamps, local);
|
||||
}
|
||||
|
||||
function verifyChangeAfterTimeout(outputFileStamps: OutputFileStamp[]) {
|
||||
function verifyChangeAfterTimeout(outputFileStamps: OutputFileStamp[], local?: boolean) {
|
||||
host.checkTimeoutQueueLengthAndRun(1); // Builds core
|
||||
const changedCore = getOutputFileStamps(host, additionalFiles);
|
||||
verifyChangedFiles(changedCore, outputFileStamps, [
|
||||
...getOutputFileNames(SubProject.core, "anotherModule"), // This should not be written really
|
||||
...getOutputFileNames(SubProject.core, "index"),
|
||||
...(additionalFiles ? getOutputFileNames(SubProject.core, newFileWithoutExtension) : emptyArray)
|
||||
]);
|
||||
host.checkTimeoutQueueLengthAndRun(1); // Builds logic
|
||||
verifyChangedFiles(
|
||||
changedCore,
|
||||
outputFileStamps,
|
||||
additionalFiles ?
|
||||
getOutputFileNames(SubProject.core, newFileWithoutExtension) :
|
||||
getOutputFileNames(SubProject.core, "index"), // Written files are new file or core index file thats changed
|
||||
[
|
||||
...getOutputFileNames(SubProject.core, "anotherModule"),
|
||||
...(additionalFiles ? getOutputFileNames(SubProject.core, "index") : emptyArray)
|
||||
]
|
||||
);
|
||||
host.checkTimeoutQueueLengthAndRun(1); // Builds logic or updates timestamps
|
||||
const changedLogic = getOutputFileStamps(host, additionalFiles);
|
||||
verifyChangedFiles(changedLogic, changedCore, [
|
||||
...getOutputFileNames(SubProject.logic, "index") // Again these need not be written
|
||||
]);
|
||||
verifyChangedFiles(
|
||||
changedLogic,
|
||||
changedCore,
|
||||
additionalFiles || local ?
|
||||
emptyArray :
|
||||
getOutputFileNames(SubProject.logic, "index"),
|
||||
additionalFiles || local ?
|
||||
getOutputFileNames(SubProject.logic, "index") :
|
||||
emptyArray
|
||||
);
|
||||
host.checkTimeoutQueueLengthAndRun(1); // Builds tests
|
||||
const changedTests = getOutputFileStamps(host, additionalFiles);
|
||||
verifyChangedFiles(changedTests, changedLogic, [
|
||||
...getOutputFileNames(SubProject.tests, "index") // Again these need not be written
|
||||
]);
|
||||
verifyChangedFiles(
|
||||
changedTests,
|
||||
changedLogic,
|
||||
additionalFiles || local ?
|
||||
emptyArray :
|
||||
getOutputFileNames(SubProject.tests, "index"),
|
||||
additionalFiles || local ?
|
||||
getOutputFileNames(SubProject.tests, "index") :
|
||||
emptyArray
|
||||
);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
verifyWatches();
|
||||
@@ -193,19 +234,9 @@ export class someClass2 { }`);
|
||||
});
|
||||
|
||||
it("non local change does not start build of referencing projects", () => {
|
||||
const host = createSolutionInWatchMode(allFiles);
|
||||
const outputFileStamps = getOutputFileStamps(host);
|
||||
host.writeFile(core[1].path, `${core[1].content}
|
||||
function foo() { }`);
|
||||
host.checkTimeoutQueueLengthAndRun(1); // Builds core
|
||||
const changedCore = getOutputFileStamps(host);
|
||||
verifyChangedFiles(changedCore, outputFileStamps, [
|
||||
...getOutputFileNames(SubProject.core, "anotherModule"), // This should not be written really
|
||||
...getOutputFileNames(SubProject.core, "index"),
|
||||
]);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
verifyWatches(host);
|
||||
const { verifyChangeWithFile } = createSolutionInWatchModeToVerifyChanges();
|
||||
verifyChangeWithFile(core[1].path, `${core[1].content}
|
||||
function foo() { }`, /*local*/ true);
|
||||
});
|
||||
|
||||
it("builds when new file is added, and its subsequent updates", () => {
|
||||
@@ -242,7 +273,7 @@ export class someClass2 { }`);
|
||||
|
||||
it("watches config files that are not present", () => {
|
||||
const allFiles = [libFile, ...core, logic[1], ...tests];
|
||||
const host = createWatchedSystem(allFiles, { currentDirectory: projectsLocation });
|
||||
const host = createTsBuildWatchSystem(allFiles, { currentDirectory: projectsLocation });
|
||||
createSolutionBuilderWithWatch(host, [`${project}/${SubProject.tests}`]);
|
||||
checkWatchedFiles(host, [core[0], core[1], core[2]!, logic[0], ...tests].map(f => f.path.toLowerCase())); // tslint:disable-line no-unnecessary-type-assertion (TODO: type assertion should be necessary)
|
||||
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
|
||||
@@ -268,14 +299,10 @@ export class someClass2 { }`);
|
||||
host.writeFile(logic[0].path, logic[0].content);
|
||||
host.checkTimeoutQueueLengthAndRun(1); // Builds logic
|
||||
const changedLogic = getOutputFileStamps(host);
|
||||
verifyChangedFiles(changedLogic, initial, [
|
||||
...getOutputFileNames(SubProject.logic, "index")
|
||||
]);
|
||||
verifyChangedFiles(changedLogic, initial, getOutputFileNames(SubProject.logic, "index"), emptyArray);
|
||||
host.checkTimeoutQueueLengthAndRun(1); // Builds tests
|
||||
const changedTests = getOutputFileStamps(host);
|
||||
verifyChangedFiles(changedTests, changedLogic, [
|
||||
...getOutputFileNames(SubProject.tests, "index")
|
||||
]);
|
||||
verifyChangedFiles(changedTests, changedLogic, getOutputFileNames(SubProject.tests, "index"), emptyArray);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
verifyWatches(host);
|
||||
@@ -305,7 +332,7 @@ export class someClass2 { }`);
|
||||
};
|
||||
|
||||
const projectFiles = [coreTsConfig, coreIndex, logicTsConfig, logicIndex];
|
||||
const host = createWatchedSystem([libFile, ...projectFiles], { currentDirectory: projectsLocation });
|
||||
const host = createTsBuildWatchSystem([libFile, ...projectFiles], { currentDirectory: projectsLocation });
|
||||
createSolutionBuilderWithWatch(host, [`${project}/${SubProject.logic}`]);
|
||||
verifyWatches();
|
||||
checkOutputErrorsInitial(host, emptyArray);
|
||||
@@ -318,6 +345,7 @@ export class someClass2 { }`);
|
||||
verifyChangeInCore(`${coreIndex.content}
|
||||
function myFunc() { return 10; }`);
|
||||
|
||||
// TODO:: local change does not build logic.js because builder doesnt find any changes in input files to generate output
|
||||
// Make local change to function bar
|
||||
verifyChangeInCore(`${coreIndex.content}
|
||||
function myFunc() { return 100; }`);
|
||||
@@ -328,14 +356,20 @@ function myFunc() { return 100; }`);
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(1); // Builds core
|
||||
const changedCore = getOutputFileStamps();
|
||||
verifyChangedFiles(changedCore, outputFileStamps, [
|
||||
...getOutputFileNames(SubProject.core, "index")
|
||||
]);
|
||||
verifyChangedFiles(
|
||||
changedCore,
|
||||
outputFileStamps,
|
||||
getOutputFileNames(SubProject.core, "index"),
|
||||
emptyArray
|
||||
);
|
||||
host.checkTimeoutQueueLengthAndRun(1); // Builds logic
|
||||
const changedLogic = getOutputFileStamps();
|
||||
verifyChangedFiles(changedLogic, changedCore, [
|
||||
...getOutputFileNames(SubProject.logic, "index")
|
||||
]);
|
||||
verifyChangedFiles(
|
||||
changedLogic,
|
||||
changedCore,
|
||||
getOutputFileNames(SubProject.logic, "index"),
|
||||
emptyArray
|
||||
);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
checkOutputErrorsIncremental(host, emptyArray);
|
||||
verifyWatches();
|
||||
@@ -346,6 +380,7 @@ function myFunc() { return 100; }`);
|
||||
...getOutputStamps(host, SubProject.core, "index"),
|
||||
...getOutputStamps(host, SubProject.logic, "index"),
|
||||
];
|
||||
host.writtenFiles.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -389,7 +424,7 @@ createSomeObject().message;`
|
||||
};
|
||||
|
||||
const files = [libFile, libraryTs, libraryTsconfig, appTs, appTsconfig];
|
||||
const host = createWatchedSystem(files, { currentDirectory: `${projectsLocation}/${project}` });
|
||||
const host = createTsBuildWatchSystem(files, { currentDirectory: `${projectsLocation}/${project}` });
|
||||
createSolutionBuilderWithWatch(host, ["App"]);
|
||||
checkOutputErrorsInitial(host, emptyArray);
|
||||
|
||||
@@ -418,7 +453,7 @@ let y: string = 10;`);
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(1); // Builds logic
|
||||
const changedLogic = getOutputFileStamps(host);
|
||||
verifyChangedFiles(changedLogic, outputFileStamps, emptyArray);
|
||||
verifyChangedFiles(changedLogic, outputFileStamps, emptyArray, emptyArray);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
checkOutputErrorsIncremental(host, [
|
||||
`sample1/logic/index.ts(8,5): error TS2322: Type '10' is not assignable to type 'string'.\n`
|
||||
@@ -429,7 +464,7 @@ let x: string = 10;`);
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(1); // Builds core
|
||||
const changedCore = getOutputFileStamps(host);
|
||||
verifyChangedFiles(changedCore, changedLogic, emptyArray);
|
||||
verifyChangedFiles(changedCore, changedLogic, emptyArray, emptyArray);
|
||||
host.checkTimeoutQueueLength(0);
|
||||
checkOutputErrorsIncremental(host, [
|
||||
`sample1/core/index.ts(5,5): error TS2322: Type '10' is not assignable to type 'string'.\n`,
|
||||
@@ -444,11 +479,118 @@ let x: string = 10;`);
|
||||
it("when preserveWatchOutput is passed on command line", () => {
|
||||
verifyIncrementalErrors({ preserveWatchOutput: true, watch: true }, /*disabledConsoleClear*/ true);
|
||||
});
|
||||
|
||||
describe("when declaration emit errors are present", () => {
|
||||
const solution = "solution";
|
||||
const subProject = "app";
|
||||
const subProjectLocation = `${projectsLocation}/${solution}/${subProject}`;
|
||||
const fileWithError: File = {
|
||||
path: `${subProjectLocation}/fileWithError.ts`,
|
||||
content: `export var myClassWithError = class {
|
||||
tags() { }
|
||||
private p = 12
|
||||
};`
|
||||
};
|
||||
const fileWithFixedError: File = {
|
||||
path: fileWithError.path,
|
||||
content: fileWithError.content.replace("private p = 12", "")
|
||||
};
|
||||
const fileWithoutError: File = {
|
||||
path: `${subProjectLocation}/fileWithoutError.ts`,
|
||||
content: `export class myClass { }`
|
||||
};
|
||||
const tsconfig: File = {
|
||||
path: `${subProjectLocation}/tsconfig.json`,
|
||||
content: JSON.stringify({ compilerOptions: { composite: true } })
|
||||
};
|
||||
const expectedDtsEmitErrors = [
|
||||
`${subProject}/fileWithError.ts(1,12): error TS4094: Property 'p' of exported class expression may not be private or protected.\n`
|
||||
];
|
||||
const outputs = [
|
||||
changeExtension(fileWithError.path, Extension.Js),
|
||||
changeExtension(fileWithError.path, Extension.Dts),
|
||||
changeExtension(fileWithoutError.path, Extension.Js),
|
||||
changeExtension(fileWithoutError.path, Extension.Dts)
|
||||
];
|
||||
|
||||
function verifyDtsErrors(host: TsBuildWatchSystem, isIncremental: boolean, expectedErrors: ReadonlyArray<string>) {
|
||||
(isIncremental ? checkOutputErrorsIncremental : checkOutputErrorsInitial)(host, expectedErrors);
|
||||
outputs.forEach(f => assert.equal(host.fileExists(f), !expectedErrors.length, `Expected file ${f} to ${!expectedErrors.length ? "exist" : "not exist"}`));
|
||||
}
|
||||
|
||||
function createSolutionWithWatch(withFixedError?: true) {
|
||||
const files = [libFile, withFixedError ? fileWithFixedError : fileWithError, fileWithoutError, tsconfig];
|
||||
const host = createTsBuildWatchSystem(files, { currentDirectory: `${projectsLocation}/${solution}` });
|
||||
createSolutionBuilderWithWatch(host, [subProject]);
|
||||
verifyDtsErrors(host, /*isIncremental*/ false, withFixedError ? emptyArray : expectedDtsEmitErrors);
|
||||
return host;
|
||||
}
|
||||
|
||||
function incrementalBuild(host: TsBuildWatchSystem) {
|
||||
host.checkTimeoutQueueLengthAndRun(1); // Build the app
|
||||
host.checkTimeoutQueueLength(0);
|
||||
}
|
||||
|
||||
function fixError(host: TsBuildWatchSystem) {
|
||||
// Fix error
|
||||
host.writeFile(fileWithError.path, fileWithFixedError.content);
|
||||
host.writtenFiles.clear();
|
||||
incrementalBuild(host);
|
||||
verifyDtsErrors(host, /*isIncremental*/ true, emptyArray);
|
||||
}
|
||||
|
||||
it("when fixing error files all files are emitted", () => {
|
||||
const host = createSolutionWithWatch();
|
||||
fixError(host);
|
||||
});
|
||||
|
||||
it("when file with no error changes, declaration errors are reported", () => {
|
||||
const host = createSolutionWithWatch();
|
||||
host.writeFile(fileWithoutError.path, fileWithoutError.content.replace(/myClass/g, "myClass2"));
|
||||
incrementalBuild(host);
|
||||
verifyDtsErrors(host, /*isIncremental*/ true, expectedDtsEmitErrors);
|
||||
});
|
||||
|
||||
describe("when reporting errors on introducing error", () => {
|
||||
function createSolutionWithIncrementalError() {
|
||||
const host = createSolutionWithWatch(/*withFixedError*/ true);
|
||||
host.writeFile(fileWithError.path, fileWithError.content);
|
||||
host.writtenFiles.clear();
|
||||
|
||||
incrementalBuild(host);
|
||||
checkOutputErrorsIncremental(host, expectedDtsEmitErrors);
|
||||
assert.equal(host.writtenFiles.size, 0, `Expected not to write any files: ${arrayFrom(host.writtenFiles.keys())}`);
|
||||
return host;
|
||||
}
|
||||
|
||||
function verifyWrittenFile(host: TsBuildWatchSystem, f: string) {
|
||||
assert.isTrue(host.writtenFiles.has(host.toFullPath(f)), `Expected to write ${f}: ${arrayFrom(host.writtenFiles.keys())}`);
|
||||
}
|
||||
|
||||
it("when fixing errors only changed file is emitted", () => {
|
||||
const host = createSolutionWithIncrementalError();
|
||||
fixError(host);
|
||||
assert.equal(host.writtenFiles.size, 2, `Expected to write only changed files: ${arrayFrom(host.writtenFiles.keys())}`);
|
||||
verifyWrittenFile(host, outputs[0]);
|
||||
verifyWrittenFile(host, outputs[1]);
|
||||
});
|
||||
|
||||
it("when file with no error changes, declaration errors are reported", () => {
|
||||
const host = createSolutionWithIncrementalError();
|
||||
host.writeFile(fileWithoutError.path, fileWithoutError.content.replace(/myClass/g, "myClass2"));
|
||||
host.writtenFiles.clear();
|
||||
|
||||
incrementalBuild(host);
|
||||
checkOutputErrorsIncremental(host, expectedDtsEmitErrors);
|
||||
assert.equal(host.writtenFiles.size, 0, `Expected not to write any files: ${arrayFrom(host.writtenFiles.keys())}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsc-watch and tsserver works with project references", () => {
|
||||
describe("invoking when references are already built", () => {
|
||||
function verifyWatchesOfProject(host: WatchedSystem, expectedWatchedFiles: ReadonlyArray<string>, expectedWatchedDirectoriesRecursive: ReadonlyArray<string>, expectedWatchedDirectories?: ReadonlyArray<string>) {
|
||||
function verifyWatchesOfProject(host: TsBuildWatchSystem, expectedWatchedFiles: ReadonlyArray<string>, expectedWatchedDirectoriesRecursive: ReadonlyArray<string>, expectedWatchedDirectories?: ReadonlyArray<string>) {
|
||||
checkWatchedFilesDetailed(host, expectedWatchedFiles, 1);
|
||||
checkWatchedDirectoriesDetailed(host, expectedWatchedDirectories || emptyArray, 1, /*recursive*/ false);
|
||||
checkWatchedDirectoriesDetailed(host, expectedWatchedDirectoriesRecursive, 1, /*recursive*/ true);
|
||||
@@ -457,9 +599,9 @@ let x: string = 10;`);
|
||||
function createSolutionOfProject(allFiles: ReadonlyArray<File>,
|
||||
currentDirectory: string,
|
||||
solutionBuilderconfig: string,
|
||||
getOutputFileStamps: (host: WatchedSystem) => ReadonlyArray<OutputFileStamp>) {
|
||||
getOutputFileStamps: (host: TsBuildWatchSystem) => ReadonlyArray<OutputFileStamp>) {
|
||||
// Build the composite project
|
||||
const host = createWatchedSystem(allFiles, { currentDirectory });
|
||||
const host = createTsBuildWatchSystem(allFiles, { currentDirectory });
|
||||
const solutionBuilder = createSolutionBuilder(host, [solutionBuilderconfig], {});
|
||||
solutionBuilder.buildAllProjects();
|
||||
const outputFileStamps = getOutputFileStamps(host);
|
||||
@@ -474,7 +616,7 @@ let x: string = 10;`);
|
||||
currentDirectory: string,
|
||||
solutionBuilderconfig: string,
|
||||
watchConfig: string,
|
||||
getOutputFileStamps: (host: WatchedSystem) => ReadonlyArray<OutputFileStamp>) {
|
||||
getOutputFileStamps: (host: TsBuildWatchSystem) => ReadonlyArray<OutputFileStamp>) {
|
||||
// Build the composite project
|
||||
const { host, solutionBuilder } = createSolutionOfProject(allFiles, currentDirectory, solutionBuilderconfig, getOutputFileStamps);
|
||||
|
||||
@@ -489,7 +631,7 @@ let x: string = 10;`);
|
||||
currentDirectory: string,
|
||||
solutionBuilderconfig: string,
|
||||
openFileName: string,
|
||||
getOutputFileStamps: (host: WatchedSystem) => ReadonlyArray<OutputFileStamp>) {
|
||||
getOutputFileStamps: (host: TsBuildWatchSystem) => ReadonlyArray<OutputFileStamp>) {
|
||||
// Build the composite project
|
||||
const { host, solutionBuilder } = createSolutionOfProject(allFiles, currentDirectory, solutionBuilderconfig, getOutputFileStamps);
|
||||
|
||||
@@ -525,12 +667,12 @@ let x: string = 10;`);
|
||||
return createSolutionAndServiceOfProject(allFiles, projectsLocation, `${project}/${SubProject.tests}`, tests[1].path, getOutputFileStamps);
|
||||
}
|
||||
|
||||
function verifyWatches(host: WatchedSystem, withTsserver?: boolean) {
|
||||
function verifyWatches(host: TsBuildWatchSystem, withTsserver?: boolean) {
|
||||
verifyWatchesOfProject(host, withTsserver ? expectedWatchedFiles.filter(f => f !== tests[1].path.toLowerCase()) : expectedWatchedFiles, expectedWatchedDirectoriesRecursive);
|
||||
}
|
||||
|
||||
function verifyScenario(
|
||||
edit: (host: WatchedSystem, solutionBuilder: SolutionBuilder) => void,
|
||||
edit: (host: TsBuildWatchSystem, solutionBuilder: SolutionBuilder) => void,
|
||||
expectedFilesAfterEdit: ReadonlyArray<string>
|
||||
) {
|
||||
it("with tsc-watch", () => {
|
||||
@@ -633,7 +775,7 @@ export function gfoo() {
|
||||
}
|
||||
|
||||
function verifyWatchState(
|
||||
host: WatchedSystem,
|
||||
host: TsBuildWatchSystem,
|
||||
watch: Watch,
|
||||
expectedProgramFiles: ReadonlyArray<string>,
|
||||
expectedWatchedFiles: ReadonlyArray<string>,
|
||||
@@ -720,20 +862,20 @@ export function gfoo() {
|
||||
return createSolutionAndServiceOfProject(allFiles, getProjectPath(project), configToBuild, cTs.path, getOutputFileStamps);
|
||||
}
|
||||
|
||||
function getOutputFileStamps(host: WatchedSystem) {
|
||||
return expectedFiles.map(file => [file, host.getModifiedTime(file)] as OutputFileStamp);
|
||||
function getOutputFileStamps(host: TsBuildWatchSystem) {
|
||||
return expectedFiles.map(file => transformOutputToOutputFileStamp(file, host));
|
||||
}
|
||||
|
||||
function verifyProgram(host: WatchedSystem, watch: Watch) {
|
||||
function verifyProgram(host: TsBuildWatchSystem, watch: Watch) {
|
||||
verifyWatchState(host, watch, expectedProgramFiles, expectedWatchedFiles, expectedWatchedDirectoriesRecursive, defaultDependencies, expectedWatchedDirectories);
|
||||
}
|
||||
|
||||
function verifyProject(host: WatchedSystem, service: projectSystem.TestProjectService, orphanInfos?: ReadonlyArray<string>) {
|
||||
function verifyProject(host: TsBuildWatchSystem, service: projectSystem.TestProjectService, orphanInfos?: ReadonlyArray<string>) {
|
||||
verifyServerState(host, service, expectedProgramFiles, expectedWatchedFiles, expectedWatchedDirectoriesRecursive, orphanInfos);
|
||||
}
|
||||
|
||||
function verifyServerState(
|
||||
host: WatchedSystem,
|
||||
host: TsBuildWatchSystem,
|
||||
service: projectSystem.TestProjectService,
|
||||
expectedProgramFiles: ReadonlyArray<string>,
|
||||
expectedWatchedFiles: ReadonlyArray<string>,
|
||||
@@ -753,13 +895,13 @@ export function gfoo() {
|
||||
}
|
||||
|
||||
function verifyScenario(
|
||||
edit: (host: WatchedSystem, solutionBuilder: SolutionBuilder) => void,
|
||||
edit: (host: TsBuildWatchSystem, solutionBuilder: SolutionBuilder) => void,
|
||||
expectedEditErrors: ReadonlyArray<string>,
|
||||
expectedProgramFiles: ReadonlyArray<string>,
|
||||
expectedWatchedFiles: ReadonlyArray<string>,
|
||||
expectedWatchedDirectoriesRecursive: ReadonlyArray<string>,
|
||||
dependencies: ReadonlyArray<[string, ReadonlyArray<string>]>,
|
||||
revert?: (host: WatchedSystem) => void,
|
||||
revert?: (host: TsBuildWatchSystem) => void,
|
||||
orphanInfosAfterEdit?: ReadonlyArray<string>,
|
||||
orphanInfosAfterRevert?: ReadonlyArray<string>) {
|
||||
it("with tsc-watch", () => {
|
||||
@@ -978,8 +1120,8 @@ export function gfoo() {
|
||||
[refs.path, [refs.path]],
|
||||
[cTsFile.path, [cTsFile.path, refs.path, bDts]]
|
||||
];
|
||||
function getOutputFileStamps(host: WatchedSystem) {
|
||||
return expectedFiles.map(file => [file, host.getModifiedTime(file)] as OutputFileStamp);
|
||||
function getOutputFileStamps(host: TsBuildWatchSystem) {
|
||||
return expectedFiles.map(file => transformOutputToOutputFileStamp(file, host));
|
||||
}
|
||||
const { host, watch } = createSolutionAndWatchModeOfProject(allFiles, getProjectPath(project), "tsconfig.c.json", "tsconfig.c.json", getOutputFileStamps);
|
||||
verifyWatchState(host, watch, expectedProgramFiles, expectedWatchedFiles, expectedWatchedDirectoriesRecursive, defaultDependencies);
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace ts.projectSystem {
|
||||
const session = createSession(createServerHost([aTs, bTs]));
|
||||
openFilesForSession([bTs], session);
|
||||
|
||||
// rename fails with allowRenameOfImportPath disabled
|
||||
const response1 = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(bTs, 'a";'));
|
||||
assert.deepEqual<protocol.RenameResponseBody | undefined>(response1, {
|
||||
info: {
|
||||
@@ -16,6 +17,7 @@ namespace ts.projectSystem {
|
||||
locs: [{ file: bTs.path, locs: [protocolRenameSpanFromSubstring(bTs.content, "./a")] }],
|
||||
});
|
||||
|
||||
// rename succeeds with allowRenameOfImportPath enabled in host
|
||||
session.getProjectService().setHostConfiguration({ preferences: { allowRenameOfImportPath: true } });
|
||||
const response2 = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(bTs, 'a";'));
|
||||
assert.deepEqual<protocol.RenameResponseBody | undefined>(response2, {
|
||||
@@ -30,15 +32,83 @@ namespace ts.projectSystem {
|
||||
},
|
||||
locs: [{ file: bTs.path, locs: [protocolRenameSpanFromSubstring(bTs.content, "./a")] }],
|
||||
});
|
||||
|
||||
// rename succeeds with allowRenameOfImportPath enabled in file
|
||||
session.getProjectService().setHostConfiguration({ preferences: { allowRenameOfImportPath: false } });
|
||||
session.getProjectService().setHostConfiguration({ file: "/b.ts", formatOptions: {}, preferences: { allowRenameOfImportPath: true } });
|
||||
const response3 = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(bTs, 'a";'));
|
||||
assert.deepEqual<protocol.RenameResponseBody | undefined>(response3, {
|
||||
info: {
|
||||
canRename: true,
|
||||
fileToRename: aTs.path,
|
||||
displayName: aTs.path,
|
||||
fullDisplayName: aTs.path,
|
||||
kind: ScriptElementKind.moduleElement,
|
||||
kindModifiers: "",
|
||||
triggerSpan: protocolTextSpanFromSubstring(bTs.content, "a", { index: 1 }),
|
||||
},
|
||||
locs: [{ file: bTs.path, locs: [protocolRenameSpanFromSubstring(bTs.content, "./a")] }],
|
||||
});
|
||||
});
|
||||
|
||||
it("works with prefixText and suffixText", () => {
|
||||
it("works with prefixText and suffixText when enabled", () => {
|
||||
const aTs: File = { path: "/a.ts", content: "const x = 0; const o = { x };" };
|
||||
const session = createSession(createServerHost([aTs]));
|
||||
const host = createServerHost([aTs]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([aTs], session);
|
||||
|
||||
const response = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(aTs, "x"));
|
||||
assert.deepEqual<protocol.RenameResponseBody | undefined>(response, {
|
||||
// rename with prefixText and suffixText disabled
|
||||
const response1 = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(aTs, "x"));
|
||||
assert.deepEqual<protocol.RenameResponseBody | undefined>(response1, {
|
||||
info: {
|
||||
canRename: true,
|
||||
fileToRename: undefined,
|
||||
displayName: "x",
|
||||
fullDisplayName: "x",
|
||||
kind: ScriptElementKind.constElement,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
triggerSpan: protocolTextSpanFromSubstring(aTs.content, "x"),
|
||||
},
|
||||
locs: [
|
||||
{
|
||||
file: aTs.path,
|
||||
locs: [
|
||||
protocolRenameSpanFromSubstring(aTs.content, "x"),
|
||||
protocolRenameSpanFromSubstring(aTs.content, "x", { index: 1 }),
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// rename with prefixText and suffixText enabled in host
|
||||
session.getProjectService().setHostConfiguration({ preferences: { providePrefixAndSuffixTextForRename: true } });
|
||||
const response2 = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(aTs, "x"));
|
||||
assert.deepEqual<protocol.RenameResponseBody | undefined>(response2, {
|
||||
info: {
|
||||
canRename: true,
|
||||
fileToRename: undefined,
|
||||
displayName: "x",
|
||||
fullDisplayName: "x",
|
||||
kind: ScriptElementKind.constElement,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
triggerSpan: protocolTextSpanFromSubstring(aTs.content, "x"),
|
||||
},
|
||||
locs: [
|
||||
{
|
||||
file: aTs.path,
|
||||
locs: [
|
||||
protocolRenameSpanFromSubstring(aTs.content, "x"),
|
||||
protocolRenameSpanFromSubstring(aTs.content, "x", { index: 1 }, { prefixText: "x: " }),
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// rename with prefixText and suffixText enabled for file
|
||||
session.getProjectService().setHostConfiguration({ preferences: { providePrefixAndSuffixTextForRename: false } });
|
||||
session.getProjectService().setHostConfiguration({ file: "/a.ts", formatOptions: {}, preferences: { providePrefixAndSuffixTextForRename: true } });
|
||||
const response3 = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(aTs, "x"));
|
||||
assert.deepEqual<protocol.RenameResponseBody | undefined>(response3, {
|
||||
info: {
|
||||
canRename: true,
|
||||
fileToRename: undefined,
|
||||
@@ -59,5 +129,67 @@ namespace ts.projectSystem {
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("rename behavior is based on file of rename initiation", () => {
|
||||
const aTs: File = { path: "/a.ts", content: "const x = 1; export { x };" };
|
||||
const bTs: File = { path: "/b.ts", content: `import { x } from "./a"; const y = x + 1;` };
|
||||
const host = createServerHost([aTs, bTs]);
|
||||
const session = createSession(host);
|
||||
openFilesForSession([aTs, bTs], session);
|
||||
|
||||
// rename from file with prefixText and suffixText enabled
|
||||
session.getProjectService().setHostConfiguration({ file: "/a.ts", formatOptions: {}, preferences: { providePrefixAndSuffixTextForRename: true } });
|
||||
const response1 = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(aTs, "x"));
|
||||
assert.deepEqual<protocol.RenameResponseBody | undefined>(response1, {
|
||||
info: {
|
||||
canRename: true,
|
||||
fileToRename: undefined,
|
||||
displayName: "x",
|
||||
fullDisplayName: "x",
|
||||
kind: ScriptElementKind.constElement,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
triggerSpan: protocolTextSpanFromSubstring(aTs.content, "x"),
|
||||
},
|
||||
locs: [
|
||||
{
|
||||
file: aTs.path,
|
||||
locs: [
|
||||
protocolRenameSpanFromSubstring(aTs.content, "x"),
|
||||
protocolRenameSpanFromSubstring(aTs.content, "x", { index: 2 }, { suffixText: " as x" }),
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// rename from file with prefixText and suffixText disabled
|
||||
const response2 = executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, protocolFileLocationFromSubstring(bTs, "x"));
|
||||
assert.deepEqual<protocol.RenameResponseBody | undefined>(response2, {
|
||||
info: {
|
||||
canRename: true,
|
||||
fileToRename: undefined,
|
||||
displayName: "x",
|
||||
fullDisplayName: "x",
|
||||
kind: ScriptElementKind.alias,
|
||||
kindModifiers: ScriptElementKindModifier.none,
|
||||
triggerSpan: protocolTextSpanFromSubstring(bTs.content, "x"),
|
||||
},
|
||||
locs: [
|
||||
{
|
||||
file: bTs.path,
|
||||
locs: [
|
||||
protocolRenameSpanFromSubstring(bTs.content, "x"),
|
||||
protocolRenameSpanFromSubstring(bTs.content, "x", { index: 1 })
|
||||
]
|
||||
},
|
||||
{
|
||||
file: aTs.path,
|
||||
locs: [
|
||||
protocolRenameSpanFromSubstring(aTs.content, "x"),
|
||||
protocolRenameSpanFromSubstring(aTs.content, "x", { index: 2 }),
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user