Skip to content

Commit 0bda30d

Browse files
committed
Fix some issues with the logic for TSDocConfigFile.hasErrors and TSDocConfigFile.fileNotFound
1 parent 1eb6706 commit 0bda30d

File tree

7 files changed

+208
-40
lines changed

7 files changed

+208
-40
lines changed

tsdoc-config/src/TSDocConfigFile.ts

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export class TSDocConfigFile {
7575

7676
this._extendsFiles = [];
7777
this._filePath = '';
78-
this._fileNotFound = true;
78+
this._fileNotFound = false;
7979
this._hasErrors = false;
8080
this._fileMTime = 0;
8181
this._tsdocSchema = '';
@@ -102,8 +102,11 @@ export class TSDocConfigFile {
102102
}
103103

104104
/**
105-
* If true, then the TSDocConfigFile object contains an empty state, because the `tsdoc.json` file could
106-
* not be found by the loader.
105+
* If true, then the TSDocConfigFile object contains an empty state, because the `tsdoc.json` file
106+
* was not found by the loader.
107+
*
108+
* @remarks
109+
* A missing "tsdoc.json" file is not considered an error. It simply means that the defaults will be used.
107110
*/
108111
public get fileNotFound(): boolean {
109112
return this._fileNotFound;
@@ -278,8 +281,6 @@ export class TSDocConfigFile {
278281
}
279282

280283
private _loadJsonObject(configJson: IConfigJson): void {
281-
this._fileNotFound = false;
282-
283284
if (configJson.$schema !== TSDocConfigFile.CURRENT_SCHEMA_URL) {
284285
this._reportError({
285286
messageId: TSDocMessageId.ConfigFileUnsupportedSchema,
@@ -347,6 +348,10 @@ export class TSDocConfigFile {
347348
referencingConfigFile: TSDocConfigFile | undefined,
348349
alreadyVisitedPaths: Set<string>
349350
): void {
351+
// In case an exception is thrown, start by assuming that the file was not found; we'll revise
352+
// this later upon success
353+
this._fileNotFound = true;
354+
350355
if (!configFilePath) {
351356
this._reportError({
352357
messageId: TSDocMessageId.ConfigFileNotFound,
@@ -367,6 +372,11 @@ export class TSDocConfigFile {
367372
return;
368373
}
369374

375+
const configJsonContent: string = fs.readFileSync(this._filePath).toString();
376+
this._fileMTime = fs.statSync(this._filePath).mtimeMs;
377+
378+
this._fileNotFound = false;
379+
370380
const hashKey: string = fs.realpathSync(this._filePath);
371381
if (referencingConfigFile && alreadyVisitedPaths.has(hashKey)) {
372382
this._reportError({
@@ -378,20 +388,15 @@ export class TSDocConfigFile {
378388
}
379389
alreadyVisitedPaths.add(hashKey);
380390

381-
const configJsonContent: string = fs.readFileSync(this._filePath).toString();
382-
this._fileMTime = fs.statSync(this._filePath).mtimeMs;
383-
384391
let configJson: IConfigJson;
385392
try {
386393
configJson = jju.parse(configJsonContent, { mode: 'cjson' });
387394
} catch (e) {
388-
this.log.addMessage(
389-
new ParserMessage({
390-
messageId: TSDocMessageId.ConfigInvalidJson,
391-
messageText: 'Error parsing JSON input: ' + e.message,
392-
textRange: TextRange.empty,
393-
})
394-
);
395+
this._reportError({
396+
messageId: TSDocMessageId.ConfigInvalidJson,
397+
messageText: 'Error parsing JSON input: ' + e.message,
398+
textRange: TextRange.empty,
399+
});
395400
return;
396401
}
397402

@@ -616,20 +621,26 @@ export class TSDocConfigFile {
616621
return 'No errors.';
617622
}
618623

619-
let result: string;
624+
let result: string = '';
620625

621-
if (this.filePath) {
622-
result = `Errors encountered for ${this.filePath}:\n`;
623-
} else {
624-
result = `Errors encountered when loading TSDoc configuration:\n`;
625-
}
626+
if (this.log.messages.length > 0) {
627+
const errorNoun: string = this.log.messages.length > 1 ? 'Errors' : 'Error';
628+
if (this.filePath) {
629+
result += `${errorNoun} encountered for ${this.filePath}:\n`;
630+
} else {
631+
result += `${errorNoun} encountered when loading TSDoc configuration:\n`;
632+
}
626633

627-
for (const message of this.log.messages) {
628-
result += ` ${message.text}\n`;
634+
for (const message of this.log.messages) {
635+
result += ` ${message.text}\n`;
636+
}
629637
}
630638

631639
for (const extendsFile of this.extendsFiles) {
632640
if (extendsFile.hasErrors) {
641+
if (result !== '') {
642+
result += '\n';
643+
}
633644
result += extendsFile.getErrorSummary();
634645
}
635646
}
@@ -681,6 +692,8 @@ export class TSDocConfigFile {
681692
// Note that setSupportForTag() automatically enables configuration.validation.reportUnsupportedTags
682693
configuration.setSupportForTag(tagDefinition, supported);
683694
} else {
695+
// Note that this validation may depend partially on the preexisting state of the TSDocConfiguration
696+
// object, so it cannot be performed during the TSConfigFile.loadFile() stage.
684697
this._reportError({
685698
messageId: TSDocMessageId.ConfigFileUndefinedTag,
686699
messageText: `The "supportForTags" field refers to an undefined tag ${JSON.stringify(tagName)}.`,

tsdoc-config/src/__tests__/ErrorHandling.test.ts

Lines changed: 154 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,62 +2,199 @@ import * as path from 'path';
22

33
import { TSDocConfigFile } from '../TSDocConfigFile';
44

5-
function testLoadingFolder(assetPath: string): unknown {
6-
const configFile: TSDocConfigFile = TSDocConfigFile.loadForFolder(path.join(__dirname, assetPath));
5+
interface ISnapshot {
6+
_0_filePath: string;
7+
_1_fileNotFound: boolean;
8+
_2_hasErrors: boolean;
9+
_3_errorSummary: string;
10+
_4_log: string[];
11+
_5_extends: ISnapshot[];
12+
}
13+
14+
// To make the unit tests deterministic, we need to replace all OS-dependent absolute paths
15+
// with OS-independent paths that are relative to the unit test folder.
16+
function makeStablePath(testPath: string): string {
17+
if (testPath.length === 0) {
18+
return '';
19+
}
20+
return '.../' + path.relative(__dirname, testPath).split('\\').join('/');
21+
}
22+
23+
// Build a map from absolute path --> stable path, for each TSDocConfigFile.filePath value
24+
function buildStablePathMap(stablePathMap: Map<string, string>, configFile: TSDocConfigFile): void {
25+
if (!stablePathMap.has(configFile.filePath)) {
26+
stablePathMap.set(configFile.filePath, makeStablePath(configFile.filePath));
27+
}
28+
for (const extendsFile of configFile.extendsFiles) {
29+
buildStablePathMap(stablePathMap, extendsFile);
30+
}
31+
}
732

33+
// Search and replace all absolute paths with the corresponding stable path.
34+
// For example, "Found C:\A\B\C.txt in C:\A\D\E.txt" becomes "Found .../B/C.txt in .../D/E.txt".
35+
function convertToStablePaths(text: string, stablePathMap: Map<string, string>): string {
36+
for (const pair of Array.from(stablePathMap.entries())) {
37+
text = text.split(pair[0]).join(pair[1]);
38+
}
39+
return text;
40+
}
41+
42+
function createSnapshot(configFile: TSDocConfigFile, pathFixupMap: Map<string, string>): ISnapshot {
843
return {
9-
fileNotFound: configFile.fileNotFound,
10-
hasErrors: configFile.hasErrors,
11-
log: configFile.log.messages.map((x) => `[${x.messageId}] ${x.text}`),
44+
_0_filePath: convertToStablePaths(configFile.filePath, pathFixupMap),
45+
_1_fileNotFound: configFile.fileNotFound,
46+
_2_hasErrors: configFile.hasErrors,
47+
_4_log: configFile.log.messages.map((x) => `[${x.messageId}] ${convertToStablePaths(x.text, pathFixupMap)}`),
48+
_5_extends: configFile.extendsFiles.map((x) => createSnapshot(x, pathFixupMap)),
49+
_3_errorSummary: convertToStablePaths(configFile.getErrorSummary(), pathFixupMap),
1250
};
1351
}
1452

53+
function testLoadingFolder(assetPath: string): ISnapshot {
54+
const configFile: TSDocConfigFile = TSDocConfigFile.loadForFolder(path.join(__dirname, assetPath));
55+
56+
const pathFixupMap: Map<string, string> = new Map();
57+
buildStablePathMap(pathFixupMap, configFile);
58+
59+
return createSnapshot(configFile, pathFixupMap);
60+
}
61+
1562
test('Load e1', () => {
1663
expect(testLoadingFolder('assets/e1')).toMatchInlineSnapshot(`
1764
Object {
18-
"fileNotFound": false,
19-
"hasErrors": true,
20-
"log": Array [
65+
"_0_filePath": ".../assets/e1/tsdoc.json",
66+
"_1_fileNotFound": false,
67+
"_2_hasErrors": true,
68+
"_3_errorSummary": "Error encountered for .../assets/e1/tsdoc.json:
69+
Error loading config file: data should NOT have additional properties
70+
",
71+
"_4_log": Array [
2172
"[tsdoc-config-schema-error] Error loading config file: data should NOT have additional properties",
2273
],
74+
"_5_extends": Array [],
2375
}
2476
`);
2577
});
2678

2779
test('Load e2', () => {
2880
expect(testLoadingFolder('assets/e2')).toMatchInlineSnapshot(`
2981
Object {
30-
"fileNotFound": true,
31-
"hasErrors": false,
32-
"log": Array [
82+
"_0_filePath": ".../assets/e2/tsdoc.json",
83+
"_1_fileNotFound": false,
84+
"_2_hasErrors": true,
85+
"_3_errorSummary": "Error encountered for .../assets/e2/tsdoc.json:
86+
Error parsing JSON input: Unexpected token '\\\\n' at 3:12
87+
\\"invalid
88+
^
89+
",
90+
"_4_log": Array [
3391
"[tsdoc-config-invalid-json] Error parsing JSON input: Unexpected token '\\\\n' at 3:12
3492
\\"invalid
3593
^",
3694
],
95+
"_5_extends": Array [],
3796
}
3897
`);
3998
});
4099

41100
test('Load e3', () => {
42101
expect(testLoadingFolder('assets/e3')).toMatchInlineSnapshot(`
43102
Object {
44-
"fileNotFound": false,
45-
"hasErrors": true,
46-
"log": Array [
103+
"_0_filePath": ".../assets/e3/tsdoc.json",
104+
"_1_fileNotFound": false,
105+
"_2_hasErrors": true,
106+
"_3_errorSummary": "Error encountered for .../assets/e3/tsdoc.json:
107+
Unsupported JSON \\"$schema\\" value; expecting \\"https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json\\"
108+
",
109+
"_4_log": Array [
47110
"[tsdoc-config-unsupported-schema] Unsupported JSON \\"$schema\\" value; expecting \\"https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json\\"",
48111
],
112+
"_5_extends": Array [],
49113
}
50114
`);
51115
});
52116

53117
test('Load e4', () => {
54118
expect(testLoadingFolder('assets/e4')).toMatchInlineSnapshot(`
55119
Object {
56-
"fileNotFound": false,
57-
"hasErrors": true,
58-
"log": Array [
120+
"_0_filePath": ".../assets/e4/tsdoc.json",
121+
"_1_fileNotFound": false,
122+
"_2_hasErrors": true,
123+
"_3_errorSummary": "Error encountered for .../assets/e4/tsdoc.json:
124+
The \\"tagDefinitions\\" field specifies more than one tag with the name \\"@dupe\\"
125+
",
126+
"_4_log": Array [
59127
"[tsdoc-config-duplicate-tag-name] The \\"tagDefinitions\\" field specifies more than one tag with the name \\"@dupe\\"",
60128
],
129+
"_5_extends": Array [],
130+
}
131+
`);
132+
});
133+
134+
test('Load e5', () => {
135+
expect(testLoadingFolder('assets/e5')).toMatchInlineSnapshot(`
136+
Object {
137+
"_0_filePath": ".../assets/e5/tsdoc.json",
138+
"_1_fileNotFound": false,
139+
"_2_hasErrors": true,
140+
"_3_errorSummary": "Error encountered for .../assets/e5/tsdoc-a.json:
141+
Circular reference encountered for \\"extends\\" field of \\".../assets/e5/tsdoc-b.json\\"
142+
143+
Error encountered for .../assets/e5/tsdoc-c.json:
144+
Error loading config file: data should NOT have additional properties
145+
",
146+
"_4_log": Array [],
147+
"_5_extends": Array [
148+
Object {
149+
"_0_filePath": ".../assets/e5/tsdoc-a.json",
150+
"_1_fileNotFound": false,
151+
"_2_hasErrors": true,
152+
"_3_errorSummary": "Error encountered for .../assets/e5/tsdoc-a.json:
153+
Circular reference encountered for \\"extends\\" field of \\".../assets/e5/tsdoc-b.json\\"
154+
155+
Error encountered for .../assets/e5/tsdoc-c.json:
156+
Error loading config file: data should NOT have additional properties
157+
",
158+
"_4_log": Array [],
159+
"_5_extends": Array [
160+
Object {
161+
"_0_filePath": ".../assets/e5/tsdoc-b.json",
162+
"_1_fileNotFound": false,
163+
"_2_hasErrors": true,
164+
"_3_errorSummary": "Error encountered for .../assets/e5/tsdoc-a.json:
165+
Circular reference encountered for \\"extends\\" field of \\".../assets/e5/tsdoc-b.json\\"
166+
",
167+
"_4_log": Array [],
168+
"_5_extends": Array [
169+
Object {
170+
"_0_filePath": ".../assets/e5/tsdoc-a.json",
171+
"_1_fileNotFound": false,
172+
"_2_hasErrors": true,
173+
"_3_errorSummary": "Error encountered for .../assets/e5/tsdoc-a.json:
174+
Circular reference encountered for \\"extends\\" field of \\".../assets/e5/tsdoc-b.json\\"
175+
",
176+
"_4_log": Array [
177+
"[tsdoc-config-cyclic-extends] Circular reference encountered for \\"extends\\" field of \\".../assets/e5/tsdoc-b.json\\"",
178+
],
179+
"_5_extends": Array [],
180+
},
181+
],
182+
},
183+
Object {
184+
"_0_filePath": ".../assets/e5/tsdoc-c.json",
185+
"_1_fileNotFound": false,
186+
"_2_hasErrors": true,
187+
"_3_errorSummary": "Error encountered for .../assets/e5/tsdoc-c.json:
188+
Error loading config file: data should NOT have additional properties
189+
",
190+
"_4_log": Array [
191+
"[tsdoc-config-schema-error] Error loading config file: data should NOT have additional properties",
192+
],
193+
"_5_extends": Array [],
194+
},
195+
],
196+
},
197+
],
61198
}
62199
`);
63200
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
3+
"extends": [ "./tsdoc-b.json", "./tsdoc-c.json" ]
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
3+
"extends": [ "./tsdoc-a.json" ]
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
3+
"unknownSetting": 123
4+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
3+
"extends": [ "./tsdoc-a.json" ]
4+
5+
}

0 commit comments

Comments
 (0)