Skip to content

Commit 3e63144

Browse files
committed
Merge pull request #5450 from sarod/allow-comments-tsconfig
Allow comments in tsconfig.json
2 parents f503169 + 638e4b7 commit 3e63144

File tree

3 files changed

+117
-2
lines changed

3 files changed

+117
-2
lines changed

Jakefile.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,8 @@ var harnessSources = harnessCoreSources.concat([
147147
"transpile.ts",
148148
"reuseProgramStructure.ts",
149149
"cachingInServerLSHost.ts",
150-
"moduleResolution.ts"
150+
"moduleResolution.ts",
151+
"tsconfigParsing.ts"
151152
].map(function (f) {
152153
return path.join(unittestsDirectory, f);
153154
})).concat([

src/compiler/commandLineParser.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,13 +405,41 @@ namespace ts {
405405
*/
406406
export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } {
407407
try {
408-
return { config: /\S/.test(jsonText) ? JSON.parse(jsonText) : {} };
408+
let jsonTextWithoutComments = removeComments(jsonText);
409+
return { config: /\S/.test(jsonTextWithoutComments) ? JSON.parse(jsonTextWithoutComments) : {} };
409410
}
410411
catch (e) {
411412
return { error: createCompilerDiagnostic(Diagnostics.Failed_to_parse_file_0_Colon_1, fileName, e.message) };
412413
}
413414
}
414415

416+
417+
/**
418+
* Remove the comments from a json like text.
419+
* Comments can be single line comments (starting with # or //) or multiline comments using / * * /
420+
*
421+
* This method replace comment content by whitespace rather than completely remove them to keep positions in json parsing error reporting accurate.
422+
*/
423+
function removeComments(jsonText: string): string {
424+
let output = "";
425+
let scanner = createScanner(ScriptTarget.ES5, /* skipTrivia */ false, LanguageVariant.Standard, jsonText);
426+
let token: SyntaxKind;
427+
while ((token = scanner.scan()) !== SyntaxKind.EndOfFileToken) {
428+
switch (token) {
429+
case SyntaxKind.SingleLineCommentTrivia:
430+
case SyntaxKind.MultiLineCommentTrivia:
431+
// replace comments with whitespace to preserve original character positions
432+
output += scanner.getTokenText().replace(/\S/g, " ");
433+
break;
434+
default:
435+
output += scanner.getTokenText();
436+
break;
437+
}
438+
}
439+
return output;
440+
}
441+
442+
415443
/**
416444
* Parse the contents of a config file (tsconfig.json).
417445
* @param json The contents of the config file to parse
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/// <reference path="..\..\..\src\harness\harness.ts" />
2+
/// <reference path="..\..\..\src\compiler\commandLineParser.ts" />
3+
4+
namespace ts {
5+
describe('parseConfigFileTextToJson', () => {
6+
function assertParseResult(jsonText: string, expectedConfigObject: { config?: any; error?: Diagnostic }) {
7+
let parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText);
8+
assert.equal(JSON.stringify(parsed), JSON.stringify(expectedConfigObject));
9+
}
10+
11+
function assertParseError(jsonText: string) {
12+
let parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText);
13+
assert.isTrue(undefined === parsed.config);
14+
assert.isTrue(undefined !== parsed.error);
15+
}
16+
17+
it("returns empty config for file with only whitespaces", () => {
18+
assertParseResult("", { config : {} });
19+
assertParseResult(" ", { config : {} });
20+
});
21+
22+
it("returns empty config for file with comments only", () => {
23+
assertParseResult("// Comment", { config: {} });
24+
assertParseResult("/* Comment*/", { config: {} });
25+
});
26+
27+
it("returns empty config when config is empty object", () => {
28+
assertParseResult("{}", { config: {} });
29+
});
30+
31+
it("returns config object without comments", () => {
32+
assertParseResult(
33+
`{ // Excluded files
34+
"exclude": [
35+
// Exclude d.ts
36+
"file.d.ts"
37+
]
38+
}`, { config: { exclude: ["file.d.ts"] } });
39+
40+
assertParseResult(
41+
`{
42+
/* Excluded
43+
Files
44+
*/
45+
"exclude": [
46+
/* multiline comments can be in the middle of a line */"file.d.ts"
47+
]
48+
}`, { config: { exclude: ["file.d.ts"] } });
49+
});
50+
51+
it("keeps string content untouched", () => {
52+
assertParseResult(
53+
`{
54+
"exclude": [
55+
"xx//file.d.ts"
56+
]
57+
}`, { config: { exclude: ["xx//file.d.ts"] } });
58+
assertParseResult(
59+
`{
60+
"exclude": [
61+
"xx/*file.d.ts*/"
62+
]
63+
}`, { config: { exclude: ["xx/*file.d.ts*/"] } });
64+
});
65+
66+
it("handles escaped characters in strings correctly", () => {
67+
assertParseResult(
68+
`{
69+
"exclude": [
70+
"xx\\"//files"
71+
]
72+
}`, { config: { exclude: ["xx\"//files"] } });
73+
74+
assertParseResult(
75+
`{
76+
"exclude": [
77+
"xx\\\\" // end of line comment
78+
]
79+
}`, { config: { exclude: ["xx\\"] } });
80+
});
81+
82+
it("returns object with error when json is invalid", () => {
83+
assertParseError("invalid");
84+
});
85+
});
86+
}

0 commit comments

Comments
 (0)