From d7c031cfdffafa3ca836d57cf514b132550873ba Mon Sep 17 00:00:00 2001 From: Daniel Tschinder <231804+danez@users.noreply.github.com> Date: Sat, 4 Feb 2023 00:07:45 +0100 Subject: [PATCH] Support annotated resolver in the cli --- .changeset/six-cows-trade.md | 11 + .../commands/parse/options/loadResolvers.ts | 3 + .../__fixtures__/basic/Component.js | 12 +- .../tests/integration/cli-test.ts | 231 ------------------ .../tests/integration/handler-test.ts | 136 +++++++++++ .../tests/integration/importer-test.ts | 102 ++++++++ packages/website/pages/docs/reference/cli.mdx | 24 +- 7 files changed, 281 insertions(+), 238 deletions(-) create mode 100644 .changeset/six-cows-trade.md create mode 100644 packages/react-docgen-cli/tests/integration/handler-test.ts create mode 100644 packages/react-docgen-cli/tests/integration/importer-test.ts diff --git a/.changeset/six-cows-trade.md b/.changeset/six-cows-trade.md new file mode 100644 index 00000000000..b5ad5032e32 --- /dev/null +++ b/.changeset/six-cows-trade.md @@ -0,0 +1,11 @@ +--- +'@react-docgen/cli': minor +--- + +Add support for the `FindAnnotatedDefinitionsResolver`. + +Can be used with + +``` +react-docgen --resolver find-all-annotated-components +``` diff --git a/packages/react-docgen-cli/src/commands/parse/options/loadResolvers.ts b/packages/react-docgen-cli/src/commands/parse/options/loadResolvers.ts index 2f3c4141a51..b084c08602f 100644 --- a/packages/react-docgen-cli/src/commands/parse/options/loadResolvers.ts +++ b/packages/react-docgen-cli/src/commands/parse/options/loadResolvers.ts @@ -7,6 +7,7 @@ const { ChainResolver } = builtinResolvers; export enum ResolverConfigs { FindAll = 'find-all-components', FindAllExported = 'find-all-exported-components', + FindAnnotatedComponents = 'find-all-annotated-components', FindExported = 'find-exported-component', } @@ -15,6 +16,8 @@ async function loadResolver(input: string): Promise { return new builtinResolvers.FindAllDefinitionsResolver(); } else if (input === ResolverConfigs.FindAllExported) { return new builtinResolvers.FindExportedDefinitionsResolver(); + } else if (input === ResolverConfigs.FindAnnotatedComponents) { + return new builtinResolvers.FindAnnotatedDefinitionsResolver(); } else if (input === ResolverConfigs.FindExported) { return new builtinResolvers.FindExportedDefinitionsResolver({ limit: 1, diff --git a/packages/react-docgen-cli/tests/integration/__fixtures__/basic/Component.js b/packages/react-docgen-cli/tests/integration/__fixtures__/basic/Component.js index bde42400052..d35bc49b9b0 100644 --- a/packages/react-docgen-cli/tests/integration/__fixtures__/basic/Component.js +++ b/packages/react-docgen-cli/tests/integration/__fixtures__/basic/Component.js @@ -1,7 +1,9 @@ const React = require('react'); -module.exports = React.createClass({ - displayName: 'Component', - otherMethod: function () {}, - render: function () {}, -}); +// @component +module.exports = class Component extends React.Component { + displayName = "Component" + + otherMethod() {} + render() {} +}; diff --git a/packages/react-docgen-cli/tests/integration/cli-test.ts b/packages/react-docgen-cli/tests/integration/cli-test.ts index 1948b8d731a..17bafe1e8c9 100644 --- a/packages/react-docgen-cli/tests/integration/cli-test.ts +++ b/packages/react-docgen-cli/tests/integration/cli-test.ts @@ -2,7 +2,6 @@ import { readFile } from 'fs/promises'; import { join } from 'path'; import { temporaryFile } from 'tempy'; import { describe, expect, test } from 'vitest'; -import { builtinHandlers, builtinImporters } from 'react-docgen'; import withFixture from './utils/withFixture'; describe('cli', () => { @@ -172,236 +171,6 @@ describe('cli', () => { }); }); - describe('importer', () => { - describe('accepts the names of builtin importers', () => { - test.each(Object.keys(builtinImporters))('%s', async (importer) => { - await withFixture('basic', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - `--importer=${importer}`, - `${dir}/Component.js`, - ]); - - expect(stderr).toBe(''); - expect(stdout).toContain('Component'); - expect(() => JSON.parse(stdout)).not.toThrowError(); - }); - }); - }); - - describe('custom importer', () => { - test('accepts an absolute local CommonJS path', async () => { - await withFixture('custom-importer-cjs', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - `--importer=${join(dir, 'importer.cjs')}`, - `${dir}/Component.js`, - ]); - - expect(stderr).toBe(''); - expect(stdout).toContain('"displayName":"importer"'); - expect(() => JSON.parse(stdout)).not.toThrowError(); - }); - }); - - test('accepts a relative local CommonJS path', async () => { - await withFixture('custom-importer-cjs', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - '--importer', - './importer.cjs', - `${dir}/Component.js`, - ]); - - expect(stderr).toBe(''); - expect(stdout).toContain('"displayName":"importer"'); - expect(() => JSON.parse(stdout)).not.toThrowError(); - }); - }); - - test('accepts an absolute local ESM path', async () => { - await withFixture('custom-importer-esm', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - `--importer=${join(dir, 'importer.mjs')}`, - `${dir}/Component.js`, - ]); - - expect(stderr).toBe(''); - expect(stdout).toContain('"displayName":"importer"'); - expect(() => JSON.parse(stdout)).not.toThrowError(); - }); - }); - - test('accepts a relative local ESM path', async () => { - await withFixture('custom-importer-esm', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - '--importer', - './importer.mjs', - `${dir}/Component.js`, - ]); - - expect(stderr).toBe(''); - expect(stdout).toContain('"displayName":"importer"'); - expect(() => JSON.parse(stdout)).not.toThrowError(); - }); - }); - - test('accepts a npm package', async () => { - await withFixture('custom-importer-npm', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - '--importer=test-react-docgen-importer', - `${dir}/Component.js`, - ]); - - expect(stderr).toBe(''); - expect(stdout).toContain('"displayName":"importer"'); - expect(() => JSON.parse(stdout)).not.toThrowError(); - }); - }); - - test('throws error when not found', async () => { - await withFixture('basic', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - '--importer=does-not-exist', - `${dir}/Component.js`, - ]); - - expect(stderr).toContain('Unknown importer: "does-not-exist"'); - expect(stdout).toBe(''); - }); - }); - }); - }); - - describe('handlers', () => { - describe('accepts the names of builtin handlers', () => { - test.each(Object.keys(builtinHandlers))('%s', async (importer) => { - await withFixture('basic', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - `--handler=${importer}`, - `${dir}/Component.js`, - ]); - - expect(stderr).toBe(''); - expect(stdout).toContain('Component'); - expect(() => JSON.parse(stdout)).not.toThrowError(); - }); - }); - }); - - describe('multiple handlers', () => { - test('multiple handlers arguments', async () => { - await withFixture('basic', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - `--handler=displayNameHandler`, - `--handler=componentDocblockHandler`, - `--handler=componentMethodsHandler`, - `${dir}/Component.js`, - ]); - - expect(stderr).toBe(''); - expect(stdout).toContain('"displayName":"Component"'); - expect(stdout).toContain('"description":""'); - expect(stdout).toContain('"name":"otherMethod"'); - expect(() => JSON.parse(stdout)).not.toThrowError(); - }); - }); - - test('multiple handlers comma separated', async () => { - await withFixture('basic', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - `--handler=displayNameHandler,componentDocblockHandler,componentMethodsHandler`, - `${dir}/Component.js`, - ]); - - expect(stderr).toBe(''); - expect(stdout).toContain('"displayName":"Component"'); - expect(stdout).toContain('"description":""'); - expect(stdout).toContain('"name":"otherMethod"'); - expect(() => JSON.parse(stdout)).not.toThrowError(); - }); - }); - }); - - describe('custom handlers', () => { - test('accepts an absolute local CommonJS path', async () => { - await withFixture('custom-handler-cjs', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - `--handler=${join(dir, 'handler.cjs')}`, - `${dir}/Component.js`, - ]); - - expect(stderr).toBe(''); - expect(stdout).toContain('"displayName":"testhandler"'); - expect(() => JSON.parse(stdout)).not.toThrowError(); - }); - }); - - test('accepts a relative local CommonJS path', async () => { - await withFixture('custom-handler-cjs', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - '--handler', - './handler.cjs', - `${dir}/Component.js`, - ]); - - expect(stderr).toBe(''); - expect(stdout).toContain('"displayName":"testhandler"'); - expect(() => JSON.parse(stdout)).not.toThrowError(); - }); - }); - - test('accepts an absolute local ESM path', async () => { - await withFixture('custom-handler-esm', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - `--handler=${join(dir, 'handler.mjs')}`, - `${dir}/Component.js`, - ]); - - expect(stderr).toBe(''); - expect(stdout).toContain('"displayName":"testhandler"'); - expect(() => JSON.parse(stdout)).not.toThrowError(); - }); - }); - - test('accepts a relative local ESM path', async () => { - await withFixture('custom-handler-esm', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - '--handler', - './handler.mjs', - `${dir}/Component.js`, - ]); - - expect(stderr).toBe(''); - expect(stdout).toContain('"displayName":"testhandler"'); - expect(() => JSON.parse(stdout)).not.toThrowError(); - }); - }); - - test('accepts a npm package', async () => { - await withFixture('custom-handler-npm', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - '--handler=test-react-docgen-handler', - `${dir}/Component.js`, - ]); - - expect(stderr).toBe(''); - expect(stdout).toContain('"displayName":"testhandler"'); - expect(() => JSON.parse(stdout)).not.toThrowError(); - }); - }); - - test('throws error when not found', async () => { - await withFixture('basic', async ({ dir, run }) => { - const { stdout, stderr } = await run([ - '--handler=does-not-exist', - `${dir}/Component.js`, - ]); - - expect(stderr).toContain('Unknown handler: "does-not-exist"'); - expect(stdout).toBe(''); - }); - }); - }); - }); - describe('pretty', () => { test('by default does not prettify output', async () => { await withFixture('basic', async ({ dir, run }) => { diff --git a/packages/react-docgen-cli/tests/integration/handler-test.ts b/packages/react-docgen-cli/tests/integration/handler-test.ts new file mode 100644 index 00000000000..f37e3b5934d --- /dev/null +++ b/packages/react-docgen-cli/tests/integration/handler-test.ts @@ -0,0 +1,136 @@ +import { join } from 'path'; +import { describe, expect, test } from 'vitest'; +import { builtinHandlers } from 'react-docgen'; +import withFixture from './utils/withFixture'; + +describe('handler', () => { + describe('accepts the names of builtin handlers', () => { + test.each(Object.keys(builtinHandlers))('%s', async (importer) => { + await withFixture('basic', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + `--handler=${importer}`, + `${dir}/Component.js`, + ]); + + expect(stderr).toBe(''); + expect(stdout).toContain('Component'); + expect(() => JSON.parse(stdout)).not.toThrowError(); + }); + }); + }); + + describe('multiple handlers', () => { + test('multiple handlers arguments', async () => { + await withFixture('basic', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + `--handler=displayNameHandler`, + `--handler=componentDocblockHandler`, + `--handler=componentMethodsHandler`, + `${dir}/Component.js`, + ]); + + expect(stderr).toBe(''); + expect(stdout).toContain('"displayName":"Component"'); + expect(stdout).toContain('"description":""'); + expect(stdout).toContain('"name":"otherMethod"'); + expect(() => JSON.parse(stdout)).not.toThrowError(); + }); + }); + + test('multiple handlers comma separated', async () => { + await withFixture('basic', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + `--handler=displayNameHandler,componentDocblockHandler,componentMethodsHandler`, + `${dir}/Component.js`, + ]); + + expect(stderr).toBe(''); + expect(stdout).toContain('"displayName":"Component"'); + expect(stdout).toContain('"description":""'); + expect(stdout).toContain('"name":"otherMethod"'); + expect(() => JSON.parse(stdout)).not.toThrowError(); + }); + }); + }); + + describe('custom handlers', () => { + test('accepts an absolute local CommonJS path', async () => { + await withFixture('custom-handler-cjs', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + `--handler=${join(dir, 'handler.cjs')}`, + `${dir}/Component.js`, + ]); + + expect(stderr).toBe(''); + expect(stdout).toContain('"displayName":"testhandler"'); + expect(() => JSON.parse(stdout)).not.toThrowError(); + }); + }); + + test('accepts a relative local CommonJS path', async () => { + await withFixture('custom-handler-cjs', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + '--handler', + './handler.cjs', + `${dir}/Component.js`, + ]); + + expect(stderr).toBe(''); + expect(stdout).toContain('"displayName":"testhandler"'); + expect(() => JSON.parse(stdout)).not.toThrowError(); + }); + }); + + test('accepts an absolute local ESM path', async () => { + await withFixture('custom-handler-esm', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + `--handler=${join(dir, 'handler.mjs')}`, + `${dir}/Component.js`, + ]); + + expect(stderr).toBe(''); + expect(stdout).toContain('"displayName":"testhandler"'); + expect(() => JSON.parse(stdout)).not.toThrowError(); + }); + }); + + test('accepts a relative local ESM path', async () => { + await withFixture('custom-handler-esm', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + '--handler', + './handler.mjs', + `${dir}/Component.js`, + ]); + + expect(stderr).toBe(''); + expect(stdout).toContain('"displayName":"testhandler"'); + expect(() => JSON.parse(stdout)).not.toThrowError(); + }); + }); + + test('accepts a npm package', async () => { + await withFixture('custom-handler-npm', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + '--handler=test-react-docgen-handler', + `${dir}/Component.js`, + ]); + + expect(stderr).toBe(''); + expect(stdout).toContain('"displayName":"testhandler"'); + expect(() => JSON.parse(stdout)).not.toThrowError(); + }); + }); + + test('throws error when not found', async () => { + await withFixture('basic', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + '--handler=does-not-exist', + `${dir}/Component.js`, + ]); + + expect(stderr).toContain('Unknown handler: "does-not-exist"'); + expect(stdout).toBe(''); + }); + }); + }); +}); diff --git a/packages/react-docgen-cli/tests/integration/importer-test.ts b/packages/react-docgen-cli/tests/integration/importer-test.ts new file mode 100644 index 00000000000..19a59852dc3 --- /dev/null +++ b/packages/react-docgen-cli/tests/integration/importer-test.ts @@ -0,0 +1,102 @@ +import { join } from 'path'; +import { describe, expect, test } from 'vitest'; +import { builtinImporters } from 'react-docgen'; +import withFixture from './utils/withFixture'; + +describe('importer', () => { + describe('accepts the names of builtin importers', () => { + test.each(Object.keys(builtinImporters))('%s', async (importer) => { + await withFixture('basic', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + `--importer=${importer}`, + `${dir}/Component.js`, + ]); + + expect(stderr).toBe(''); + expect(stdout).toContain('Component'); + expect(() => JSON.parse(stdout)).not.toThrowError(); + }); + }); + }); + + describe('custom importer', () => { + test('accepts an absolute local CommonJS path', async () => { + await withFixture('custom-importer-cjs', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + `--importer=${join(dir, 'importer.cjs')}`, + `${dir}/Component.js`, + ]); + + expect(stderr).toBe(''); + expect(stdout).toContain('"displayName":"importer"'); + expect(() => JSON.parse(stdout)).not.toThrowError(); + }); + }); + + test('accepts a relative local CommonJS path', async () => { + await withFixture('custom-importer-cjs', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + '--importer', + './importer.cjs', + `${dir}/Component.js`, + ]); + + expect(stderr).toBe(''); + expect(stdout).toContain('"displayName":"importer"'); + expect(() => JSON.parse(stdout)).not.toThrowError(); + }); + }); + + test('accepts an absolute local ESM path', async () => { + await withFixture('custom-importer-esm', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + `--importer=${join(dir, 'importer.mjs')}`, + `${dir}/Component.js`, + ]); + + expect(stderr).toBe(''); + expect(stdout).toContain('"displayName":"importer"'); + expect(() => JSON.parse(stdout)).not.toThrowError(); + }); + }); + + test('accepts a relative local ESM path', async () => { + await withFixture('custom-importer-esm', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + '--importer', + './importer.mjs', + `${dir}/Component.js`, + ]); + + expect(stderr).toBe(''); + expect(stdout).toContain('"displayName":"importer"'); + expect(() => JSON.parse(stdout)).not.toThrowError(); + }); + }); + + test('accepts a npm package', async () => { + await withFixture('custom-importer-npm', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + '--importer=test-react-docgen-importer', + `${dir}/Component.js`, + ]); + + expect(stderr).toBe(''); + expect(stdout).toContain('"displayName":"importer"'); + expect(() => JSON.parse(stdout)).not.toThrowError(); + }); + }); + + test('throws error when not found', async () => { + await withFixture('basic', async ({ dir, run }) => { + const { stdout, stderr } = await run([ + '--importer=does-not-exist', + `${dir}/Component.js`, + ]); + + expect(stderr).toContain('Unknown importer: "does-not-exist"'); + expect(stdout).toBe(''); + }); + }); + }); +}); diff --git a/packages/website/pages/docs/reference/cli.mdx b/packages/website/pages/docs/reference/cli.mdx index a2a68240482..9fac257a423 100644 --- a/packages/website/pages/docs/reference/cli.mdx +++ b/packages/website/pages/docs/reference/cli.mdx @@ -86,6 +86,26 @@ newlines. ### `--resolver ` -**Default**: `["find-exported-component"]` +**Default**: `["find-exported-component", "find-annotated-components"]` -**TODO** +Allows to specify the resolvers that find react components in the code. + +Possible values are: + +#### Built-in resolvers + +- `find-all-components` +- `find-all-exported-components` +- `find-all-annotated-components` +- `find-exported-component` + +#### Path to resolver + +A path can be specified to your resolver. The path can be absolute (recommended) +or relative and in the later case the path will be resolved from the current +working directory. + +#### Module name of resolver + +An npm module name can be specified. The default export of this module needs to +be an instance of the resolver.