Allow comments in tsconfig.json issue #4987
This commit is contained in:
parent
d28acec24e
commit
b60d88fa80
|
@ -145,7 +145,8 @@ var harnessSources = harnessCoreSources.concat([
|
|||
"transpile.ts",
|
||||
"reuseProgramStructure.ts",
|
||||
"cachingInServerLSHost.ts",
|
||||
"moduleResolution.ts"
|
||||
"moduleResolution.ts",
|
||||
"tsconfigParsing.ts"
|
||||
].map(function (f) {
|
||||
return path.join(unittestsDirectory, f);
|
||||
})).concat([
|
||||
|
|
|
@ -405,13 +405,105 @@ namespace ts {
|
|||
*/
|
||||
export function parseConfigFileTextToJson(fileName: string, jsonText: string): { config?: any; error?: Diagnostic } {
|
||||
try {
|
||||
return { config: /\S/.test(jsonText) ? JSON.parse(jsonText) : {} };
|
||||
let jsonTextWithoutComments = removeComments(jsonText);
|
||||
return { config: /\S/.test(jsonTextWithoutComments) ? JSON.parse(jsonTextWithoutComments) : {} };
|
||||
}
|
||||
catch (e) {
|
||||
return { error: createCompilerDiagnostic(Diagnostics.Failed_to_parse_file_0_Colon_1, fileName, e.message) };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove the comments from a json like text.
|
||||
* Comments can be single line comments (starting with # or //) or multiline comments using / * * /
|
||||
*
|
||||
* This method replace comment content by whitespace rather than completely remove them to keep positions in json parsing error reporting accurate.
|
||||
*/
|
||||
function removeComments(jsonText: string): string {
|
||||
let result = "";
|
||||
let processingString = false;
|
||||
let processingSingleLineComment = false;
|
||||
let processingMultiLineComment = false;
|
||||
for (let i = 0; i < jsonText.length; i++) {
|
||||
let currentChar = jsonText.charAt(i);
|
||||
let nextChar = (i + 1 < jsonText.length) ? jsonText.charAt(i + 1) : undefined;
|
||||
if (processingString) {
|
||||
if (currentChar === "\\"
|
||||
&& nextChar === "\"") {
|
||||
// Escaped quote consume the 2 characters
|
||||
result += currentChar;
|
||||
result += nextChar;
|
||||
i += 1;
|
||||
}
|
||||
else if (currentChar === "\"") {
|
||||
// End of string
|
||||
result += currentChar;
|
||||
processingString = false;
|
||||
}
|
||||
else {
|
||||
// String content
|
||||
result += currentChar;
|
||||
}
|
||||
}
|
||||
else if (processingSingleLineComment) {
|
||||
if (currentChar === "\n") {
|
||||
// End of single line comment
|
||||
processingSingleLineComment = false;
|
||||
// Keep the line breaks to keep line numbers aligned
|
||||
result += currentChar;
|
||||
}
|
||||
else {
|
||||
// replace comment content by whitespaces
|
||||
result += " ";
|
||||
}
|
||||
}
|
||||
else if (processingMultiLineComment) {
|
||||
if (currentChar === "*" && nextChar === "/") {
|
||||
// End of comment
|
||||
result += " ";
|
||||
i += 1;
|
||||
processingMultiLineComment = false;
|
||||
}
|
||||
else if (currentChar === "\n") {
|
||||
// Keep the line breaks to Keep line aligned
|
||||
result += currentChar;
|
||||
}
|
||||
else {
|
||||
// replace comment content by whitespaces
|
||||
result += " ";
|
||||
}
|
||||
}
|
||||
else if (currentChar === "\"") {
|
||||
// String start
|
||||
result += currentChar;
|
||||
processingString = true;
|
||||
}
|
||||
else if (currentChar === "#") {
|
||||
// Start of # comment
|
||||
result += " ";
|
||||
processingSingleLineComment = true;
|
||||
}
|
||||
else if (currentChar === "/" && nextChar === "/") {
|
||||
// Start of // comment
|
||||
result += " ";
|
||||
i += 1;
|
||||
processingSingleLineComment = true;
|
||||
}
|
||||
else if (currentChar === "/" && nextChar === "*") {
|
||||
// Start of /**/ comment
|
||||
result += " ";
|
||||
i += 1;
|
||||
processingMultiLineComment = true;
|
||||
}
|
||||
else {
|
||||
// Keep other characters
|
||||
result += currentChar;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the contents of a config file (tsconfig.json).
|
||||
* @param json The contents of the config file to parse
|
||||
|
|
84
tests/cases/unittests/tsconfigParsing.ts
Normal file
84
tests/cases/unittests/tsconfigParsing.ts
Normal file
|
@ -0,0 +1,84 @@
|
|||
/// <reference path="..\..\..\src\harness\harness.ts" />
|
||||
/// <reference path="..\..\..\src\compiler\commandLineParser.ts" />
|
||||
|
||||
module ts {
|
||||
describe('parseConfigFileTextToJson', () => {
|
||||
function assertParseResult(jsonText: string, expectedConfigObject: { config?: any; error?: Diagnostic }) {
|
||||
let parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText);
|
||||
assert.equal(JSON.stringify(parsed), JSON.stringify(expectedConfigObject));
|
||||
}
|
||||
|
||||
function assertParseError(jsonText: string) {
|
||||
let parsed = ts.parseConfigFileTextToJson("/apath/tsconfig.json", jsonText);
|
||||
assert.isTrue(undefined === parsed.config);
|
||||
assert.isTrue(undefined !== parsed.error);
|
||||
}
|
||||
|
||||
it("returns empty config for file with only whitespaces", () => {
|
||||
assertParseResult("", { config : {} });
|
||||
assertParseResult(" ", { config : {} });
|
||||
});
|
||||
|
||||
it("returns empty config for file with comments only", () => {
|
||||
assertParseResult("// Comment", { config: {} });
|
||||
assertParseResult("# Comment", { config: {} });
|
||||
assertParseResult("/* Comment*/", { config: {} });
|
||||
});
|
||||
|
||||
it("returns empty config when config is empty object", () => {
|
||||
assertParseResult("{}", { config: {} });
|
||||
});
|
||||
|
||||
it("returns config object without comments", () => {
|
||||
assertParseResult(
|
||||
`{ // Excluded files
|
||||
"exclude": [
|
||||
// Exclude d.ts
|
||||
"file.d.ts"
|
||||
]
|
||||
}`, { config: { exclude: ["file.d.ts"] } });
|
||||
assertParseResult(
|
||||
`{
|
||||
# Excluded files
|
||||
"exclude": [
|
||||
# Exclude d.ts
|
||||
"file.d.ts"
|
||||
]
|
||||
}`, { config: { exclude: ["file.d.ts"] } });
|
||||
assertParseResult(
|
||||
`{
|
||||
/* Excluded
|
||||
Files
|
||||
*/
|
||||
"exclude": [
|
||||
/* multiline comments can be in the middle of a line */"file.d.ts"
|
||||
]
|
||||
}`, { config: { exclude: ["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"] } });
|
||||
assertParseResult(
|
||||
`{
|
||||
"exclude": [
|
||||
"xx/*file.d.ts*/"
|
||||
]
|
||||
}`, { config: { exclude: ["xx/*file.d.ts*/"] } });
|
||||
});
|
||||
|
||||
it("returns object with error when json is invalid", () => {
|
||||
assertParseError("invalid");
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue