Skip to content

Commit 07fb1ce

Browse files
Gerrit0Richienb
andcommitted
feat: Support for extended config in typedoc.json
Closes #493 Closes #1115 Co-Authored-By: Richie Bendall <richiebendall@gmail.com>
1 parent 20db9a5 commit 07fb1ce

File tree

5 files changed

+53
-7
lines changed

5 files changed

+53
-7
lines changed

src/lib/utils/options/readers/typedoc.ts

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export class TypeDocReader implements OptionsReader {
2525
*/
2626
read(container: Options, logger: Logger): void {
2727
const path = container.getValue('options');
28-
const file = this.findTypedocFile(path, logger);
28+
const file = this.findTypedocFile(path);
2929

3030
if (!file) {
3131
if (!container.isDefault('options')) {
@@ -34,15 +34,43 @@ export class TypeDocReader implements OptionsReader {
3434
return;
3535
}
3636

37-
let data: any = require(file);
38-
if (typeof data !== 'object') {
37+
const seen = new Set<string>();
38+
this.readFile(file, container, logger, seen);
39+
}
40+
41+
/**
42+
* Read the given options file + any extended files.
43+
* @param file
44+
* @param container
45+
* @param logger
46+
*/
47+
private readFile(file: string, container: Options, logger: Logger, seen: Set<string>) {
48+
if (seen.has(file)) {
49+
logger.error(`Tried to load the options file ${file} multiple times.`);
50+
return;
51+
}
52+
seen.add(file);
53+
54+
const data: unknown = require(file);
55+
56+
if (typeof data !== 'object' || !data) {
3957
logger.error(`The file ${file} is not an object.`);
4058
return;
4159
}
60+
61+
if ('extends' in data) {
62+
const extended: string[] = getStringArray(data['extends']);
63+
for (const extendedFile of extended) {
64+
// Extends is relative to the file it appears in.
65+
this.readFile(Path.resolve(Path.dirname(file), extendedFile), container, logger, seen);
66+
}
67+
delete data['extends'];
68+
}
69+
4270
// deprecate: data.src is alias to inputFiles as of 0.16, warn in 0.17, remove in 0.19
4371
if ('src' in data && !('inputFiles' in data)) {
44-
data.inputFiles = Array.isArray(data.src) ? data.src : [data.src];
45-
delete data.src;
72+
data['inputFiles'] = getStringArray(data['src']);
73+
delete data['src'];
4674
}
4775

4876
container.setValues(data).match({
@@ -63,7 +91,7 @@ export class TypeDocReader implements OptionsReader {
6391
* @param logger
6492
* @return the typedoc.(js|json) file path or undefined
6593
*/
66-
private findTypedocFile(path: string, logger: Logger): string | undefined {
94+
private findTypedocFile(path: string): string | undefined {
6795
path = Path.resolve(path);
6896

6997
return [
@@ -73,3 +101,7 @@ export class TypeDocReader implements OptionsReader {
73101
].find(path => FS.existsSync(path) && FS.statSync(path).isFile());
74102
}
75103
}
104+
105+
function getStringArray(arg: unknown): string[] {
106+
return Array.isArray(arg) ? arg.map(String) : [String(arg)];
107+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": ["./circular-extends.json"]
3+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "./src.json",
3+
"name": "extends"
4+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2-
"src": ["a"]
2+
"src": ["a"],
3+
"name": "src"
34
}

src/test/utils/options/readers/typedoc.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ describe('Options - TypeDocReader', () => {
2626
equal(options.getValue('inputFiles'), ['a']);
2727
});
2828

29+
test('Supports extends', join(__dirname, 'data/extends.json'), () => {
30+
equal(options.getValue('name'), 'extends');
31+
equal(options.getValue('inputFiles'), ['a']);
32+
});
33+
2934
function testError(name: string, file: string) {
3035
it(name, () => {
3136
options.reset();
@@ -39,6 +44,7 @@ describe('Options - TypeDocReader', () => {
3944
testError('Errors if the file cannot be found', join(__dirname, 'data/non-existent-file.json'));
4045
testError('Errors if the data is invalid', join(__dirname, 'data/invalid.json'));
4146
testError('Errors if any set option errors', join(__dirname, 'data/unknown.json'));
47+
testError('Errors if extends results in a loop', join(__dirname, 'data/circular-extends.json'));
4248

4349
it('Does not error if the option file cannot be found but was not set.', () => {
4450
const options = new class LyingOptions extends Options {

0 commit comments

Comments
 (0)