Skip to content

Commit 1542515

Browse files
committed
Refactor resolvers and add new ChainResolver
1 parent 81cb0ed commit 1542515

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+3600
-3103
lines changed

.changeset/friendly-timers-tap.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@react-docgen/cli': minor
3+
---
4+
5+
`--resolver` option can now be used multiple times.
6+
7+
If used multiple times the resolvers will be chained in the defined order and
8+
all components from all resolvers will be used.

.changeset/hungry-crabs-scream.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'react-docgen': minor
3+
---
4+
5+
Add new ChainResolver to allow multiple resolvers to be chained.
6+
7+
```ts
8+
import { builtinResolvers } from 'react-docgen';
9+
10+
const { ChainResolver } = builtinResolvers;
11+
const resolver = new ChainResolver([resolver1, resolver2], {
12+
chainingLogic: ChainResolver.Logic.ALL, // or ChainResolver.Logic.FIRST_FOUND,
13+
});
14+
```

.changeset/quiet-spiders-grab.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
'react-docgen': minor
3+
---
4+
5+
Allow resolvers to be classes in addition to functions.
6+
7+
```ts
8+
import type { ResolverClass, ResolverFunction } from 'react-dcogen';
9+
10+
// This was the only option until now
11+
const functionResolver: ResolverFunction = (file: FileState) => {
12+
//needs to return array of found components
13+
};
14+
15+
// This is the new class resolver
16+
class MyResolver implements ResolverClass {
17+
resolve(file: FileState) {
18+
//needs to return array of found components
19+
}
20+
}
21+
22+
const classResolver = new MyResolver();
23+
```

.changeset/shy-papayas-attack.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@react-docgen/cli': major
3+
---
4+
5+
Renamed `--handlers` option to`--handler`. This unifies all options to be
6+
singular.

.changeset/thin-tables-sneeze.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
'react-docgen': major
3+
---
4+
5+
Renamed and migrated built-in resolvers to classes.
6+
7+
- `findAllComponentDefinitions` was renamed to `FindAllDefinitionsResolver` and
8+
is now a class.
9+
10+
```diff
11+
-const resolver = builtinResolvers.findAllComponentDefinitions
12+
+const resolver = new builtinResolvers.FindAllDefinitionsResolver()
13+
```
14+
15+
- `findAllExportedComponentDefinitions` was renamed to
16+
`FindExportedDefinitionsResolver` and is now a class.
17+
18+
```diff
19+
-const resolver = builtinResolvers.findAllExportedComponentDefinitions
20+
+const resolver = new builtinResolvers.FindExportedDefinitionsResolver()
21+
```
22+
23+
- `findExportedComponentDefinition` was removed.
24+
Use`FindExportedDefinitionsResolver` with the `limit` option instead.
25+
26+
> This is still the default resolver.
27+
28+
```diff
29+
-const resolver = builtinResolvers.findExportedComponentDefinition
30+
+const resolver = new builtinResolvers.FindExportedDefinitionsResolver({ limit: 1 })
31+
```

packages/react-docgen-cli/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
},
1616
"scripts": {
1717
"build": "rimraf dist/ && tsc",
18+
"test": "vitest run",
1819
"watch": "rimraf dist/ && tsc --watch"
1920
},
2021
"keywords": [

packages/react-docgen-cli/src/commands/parse/command.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import outputError from './output/outputError.js';
1010
import { resolve } from 'path';
1111
import slash from 'slash';
1212
import type { Documentation } from 'react-docgen';
13+
import { ResolverConfigs } from './options/loadResolvers.js';
1314

1415
const debug = debugFactory('react-docgen:cli');
1516

@@ -20,12 +21,14 @@ const defaultIgnoreGlobs = [
2021
];
2122

2223
const defaultHandlers = Object.keys(builtinHandlers);
24+
const defaultResolvers = ['find-exported-component'];
2325

2426
function collect(value: string, previous: string[]) {
2527
if (
2628
!previous ||
2729
previous === defaultIgnoreGlobs ||
28-
previous === defaultHandlers
30+
previous === defaultHandlers ||
31+
previous === defaultResolvers
2932
) {
3033
previous = [];
3134
}
@@ -38,12 +41,12 @@ function collect(value: string, previous: string[]) {
3841
interface CLIOptions {
3942
defaultIgnores: boolean;
4043
failOnWarning: boolean;
41-
handlers?: string[];
44+
handler?: string[];
4245
ignore: string[];
4346
importer?: string;
4447
out?: string;
4548
pretty: boolean;
46-
resolver?: string;
49+
resolver?: string[];
4750
}
4851

4952
program
@@ -73,18 +76,21 @@ program
7376
false,
7477
)
7578
.option(
76-
'--resolver <resolver>',
77-
'Built-in resolver name (findAllComponentDefinitions, findAllExportedComponentDefinitions, findExportedComponentDefinition) or path to a module that exports a resolver.',
78-
'findExportedComponentDefinition',
79+
'--resolver <resolvers>',
80+
`Built-in resolver config (${Object.values(ResolverConfigs).join(
81+
', ',
82+
)}), package name or path to a module that exports a resolver. Can also be used multiple times. When used, no default handlers will be added.`,
83+
collect,
84+
defaultResolvers,
7985
)
8086
.option(
8187
'--importer <importer>',
82-
'Built-in importer name (fsImport, ignoreImporter) or path to a module that exports an importer.',
88+
'Built-in importer name (fsImport, ignoreImporter), package name or path to a module that exports an importer.',
8389
'fsImporter',
8490
)
8591
.option(
86-
'--handlers <handlers>',
87-
'Comma separated list of handlers to use. Can also be used multiple times. When used no default handlers will be added.',
92+
'--handler <handlers>',
93+
'Comma separated list of handlers to use. Can also be used multiple times. When used, no default handlers will be added.',
8894
collect,
8995
defaultHandlers,
9096
)
@@ -93,7 +99,7 @@ program
9399
const {
94100
defaultIgnores,
95101
failOnWarning,
96-
handlers,
102+
handler,
97103
ignore,
98104
importer,
99105
out: output,
@@ -111,7 +117,7 @@ program
111117
}
112118

113119
const options = await loadOptions({
114-
handlers,
120+
handler,
115121
importer,
116122
resolver,
117123
});
Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,17 @@
11
import type { Handler, Importer, Resolver } from 'react-docgen';
2-
import {
3-
builtinHandlers,
4-
builtinImporters,
5-
builtinResolvers,
6-
} from 'react-docgen';
2+
import { builtinHandlers, builtinImporters } from 'react-docgen';
73
import loadReactDocgenPlugin from './loadReactDocgenPlugin.js';
4+
import loadResolvers from './loadResolvers.js';
85

96
export default async function loadOptions(input: {
10-
handlers: string[] | undefined;
7+
handler: string[] | undefined;
118
importer: string | undefined;
12-
resolver: string | undefined;
9+
resolver: string[] | undefined;
1310
}): Promise<{
1411
handlers: Handler[] | undefined;
1512
importer: Importer | undefined;
1613
resolver: Resolver | undefined;
1714
}> {
18-
const resolver =
19-
input.resolver && input.resolver.length !== 0
20-
? await loadReactDocgenPlugin<Resolver>(
21-
input.resolver,
22-
'resolver',
23-
builtinResolvers,
24-
)
25-
: undefined;
26-
2715
const importer =
2816
input.importer && input.importer.length !== 0
2917
? await loadReactDocgenPlugin<Importer>(
@@ -33,9 +21,9 @@ export default async function loadOptions(input: {
3321
)
3422
: undefined;
3523

36-
const handlers = input.handlers
24+
const handlers = input.handler
3725
? await Promise.all(
38-
input.handlers.map(async handler => {
26+
input.handler.map(async handler => {
3927
return await loadReactDocgenPlugin<Handler>(
4028
handler,
4129
'handler',
@@ -45,5 +33,9 @@ export default async function loadOptions(input: {
4533
)
4634
: undefined;
4735

48-
return { handlers, importer, resolver };
36+
return {
37+
handlers,
38+
importer,
39+
resolver: await loadResolvers(input.resolver),
40+
};
4941
}

packages/react-docgen-cli/src/commands/parse/options/loadReactDocgenPlugin.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@ import importFile from '../../../utils/importFile.js';
44
export default async function loadReactDocgenPlugin<T>(
55
input: string,
66
name: string,
7-
builtins: Record<string, T>,
7+
builtins?: Record<string, T>,
88
): Promise<T> {
9-
if (builtins[input]) {
9+
if (builtins?.[input]) {
1010
return builtins[input];
1111
}
1212

1313
const path = resolve(process.cwd(), input);
1414
// Maybe it is local path or a package
15-
const importer: T | undefined =
15+
const plugin: T | undefined =
1616
(await importFile(path)) ?? (await importFile(input));
1717

18-
if (importer) {
19-
return importer;
18+
if (plugin) {
19+
return plugin;
2020
}
2121

2222
throw new Error(
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import type { Resolver } from 'react-docgen';
2+
import { builtinResolvers } from 'react-docgen';
3+
import loadReactDocgenPlugin from './loadReactDocgenPlugin.js';
4+
5+
const { ChainResolver } = builtinResolvers;
6+
7+
export enum ResolverConfigs {
8+
FindAll = 'find-all-components',
9+
FindAllExported = 'find-all-exported-components',
10+
FindExported = 'find-exported-component',
11+
}
12+
13+
async function loadResolver(input: string): Promise<Resolver> {
14+
if (input === ResolverConfigs.FindAll) {
15+
return new builtinResolvers.FindAllDefinitionsResolver();
16+
} else if (input === ResolverConfigs.FindAllExported) {
17+
return new builtinResolvers.FindExportedDefinitionsResolver();
18+
} else if (input === ResolverConfigs.FindExported) {
19+
return new builtinResolvers.FindExportedDefinitionsResolver({
20+
limit: 1,
21+
});
22+
}
23+
24+
const loadedResolver = await loadReactDocgenPlugin<Resolver>(
25+
input,
26+
'resolver',
27+
);
28+
29+
// Check if it is a class constructor
30+
// If it is we do not know how to construct the resolver so error instead
31+
if (
32+
typeof loadedResolver === 'function' &&
33+
loadedResolver.toString().startsWith('class ')
34+
) {
35+
throw new Error(
36+
`The provided resolver '${input}' is not a function or a class instance but instead a class.` +
37+
' To solve this please make sure to provide a path to a file that returns a class instance.',
38+
);
39+
}
40+
41+
return loadedResolver;
42+
}
43+
44+
export default async function loadResolvers(
45+
input: string[] | undefined,
46+
): Promise<Resolver | undefined> {
47+
if (!input || input.length === 0) {
48+
return;
49+
}
50+
51+
if (input.length > 1) {
52+
return new ChainResolver(await Promise.all(input.map(loadResolver)), {
53+
chainingLogic: ChainResolver.Logic.ALL,
54+
});
55+
}
56+
57+
return loadResolver(input[0]);
58+
}

packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-class/node_modules/test-react-docgen-resolver-class/index.js

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-class/node_modules/test-react-docgen-resolver-class/package.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/react-docgen-cli/tests/integration/__fixtures__/custom-resolver-class/node_modules/test-react-docgen-resolver/index.js

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const code = `
2+
({
3+
displayName: 'Custom',
4+
})
5+
`;
6+
7+
const customResolver = class {
8+
resolve(file) {
9+
const newFile = file.parse(code, 'x.js');
10+
let path;
11+
12+
newFile.traverse({
13+
Program(p) {
14+
path = p;
15+
p.stop();
16+
}
17+
});
18+
19+
return [path.get('body')[0].get('expression')];
20+
}
21+
}
22+
23+
module.exports = new customResolver();

0 commit comments

Comments
 (0)