Skip to content

Support custom parser options in programmatic API #327

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ src/
[libs]

[options]

suppress_comment= \\(.\\|\n\\)*\\$FlowIssue
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ Options:
-i, --ignore Folders to ignore. Default: [node_modules,__tests__,__mocks__]
--resolver RESOLVER Resolver name (findAllComponentDefinitions, findExportedComponentDefinition) or
path to a module that exports a resolver. [findExportedComponentDefinition]
--legacy-decorators Switch parsing to support only the legacy decorators syntax

Extract meta information from React components.
If a directory is passed, it is recursively traversed.
Expand All @@ -60,6 +59,9 @@ resolvers](#resolver).
Have a look at `example/` for an example of how to use the result to generate a
markdown version of the documentation.

`react-docgen` will look for a babel configuration and use it if available. If no config file is found
it will fallback to a default configuration, enabling all [syntax extension](https://babeljs.io/docs/en/babel-parser#plugins) of the babel-parser.

## API

The tool can be used programmatically to extract component information and customize the extraction process:
Expand All @@ -83,7 +85,31 @@ As with the CLI, this will look for the exported component created through `Reac
| source | string | The source text |
| resolver | function | A function of the form `(ast: ASTNode, recast: Object) => (NodePath|Array<NodePath>)`. Given an AST and a reference to recast, it returns an (array of) NodePath which represents the component definition. |
| handlers | Array\<function\> | An array of functions of the form `(documentation: Documentation, definition: NodePath) => void`. Each function is called with a `Documentation` object and a reference to the component definition as returned by `resolver`. Handlers extract relevant information from the definition and augment `documentation`. |
| options | Object | Pass options to react-docgen. Supported option is `legacyDecorators` which is a boolean |
| options | Object | Pass options to react-docgen, see below. |

#### options

##### ∙ filename

Type: `string`

The absolute path to the file associated with the code currently being parsed, if there is one. This is used to search for the correct babel config.

This option is optional, but it is highly recommended to set it when integrating `react-docgen`.

##### ∙ cwd

Type: `string`
Default: `process.cwd()`

The working directory that babel configurations will be searched in.

##### ∙ parserOptions

Type: `BabelParserOptions`

This options will be directly supplied to `@babel/parser`. To see a list of
supported options head over to the [babel website](https://babeljs.io/docs/en/babel-parser#options) and have a look.

#### resolver

Expand Down
10 changes: 0 additions & 10 deletions bin/react-docgen.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,6 @@ argv
collect,
[]
)
.option(
'--legacy-decorators',
'Enable parsing of legacy decorators proposal. By default only the new decorators syntax will be parsable.'
)
.option(
'--decorators-before-export',
'Switches the decorators proposal to allow decorators before the export statement. By default this is false.'
)
.option(
'-i, --ignore <path>',
'Folders to ignore. Default: ' + JSON.stringify(defaultIgnore),
Expand Down Expand Up @@ -115,8 +107,6 @@ if (argv.resolver) {
function parse(source, filename) {
return parser.parse(source, resolver, null, {
filename,
legacyDecorators: argv.legacyDecorators,
decoratorsBeforeExport: argv.decoratorsBeforeExport,
});
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"eslint": "^5.7.0",
"eslint-config-prettier": "^3.1.0",
"eslint-plugin-prettier": "^3.0.0",
"flow-bin": "^0.87.0",
"flow-bin": "^0.93.0",
"jest": "^23.6.0",
"jest-diff": "^23.6.0",
"jest-matcher-utils": "^23.6.0",
Expand Down
15 changes: 14 additions & 1 deletion src/__tests__/parse-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('parse', () => {
expect(resolver).toBeCalled();
});

it.only('uses local babelrc', () => {
it('uses local babelrc', () => {
const dir = temp.mkdirSync();

try {
Expand All @@ -72,4 +72,17 @@ describe('parse', () => {
fs.rmdirSync(dir);
}
});

it('supports custom parserOptions', () => {
expect(() =>
parse('const chained: Type = 1;', () => {}, null, {
parserOptions: {
plugins: [
// no flow
'jsx',
],
},
}),
).toThrowError(/.*Unexpected token \(1:13\).*/);
});
});
101 changes: 53 additions & 48 deletions src/babelParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,71 +12,76 @@

const babel = require('@babel/core');

const babelParserOptions = {
sourceType: 'module',
strictMode: false,
tokens: true,
plugins: [
'jsx',
'flow',
'estree',
'doExpressions',
'objectRestSpread',
'classProperties',
'classPrivateProperties',
'classPrivateMethods',
'exportDefaultFrom',
'exportNamespaceFrom',
'asyncGenerators',
'functionBind',
'functionSent',
'dynamicImport',
'numericSeparator',
'optionalChaining',
'importMeta',
'bigInt',
'optionalCatchBinding',
'throwExpressions',
['pipelineOperator', { proposal: 'minimal' }],
'nullishCoalescingOperator',
],
const defaultPlugins = [
'jsx',
'flow',
'asyncGenerators',
'bigInt',
'classProperties',
'classPrivateProperties',
'classPrivateMethods',
['decorators', { decoratorsBeforeExport: false }],
'doExpressions',
'dynamicImport',
'exportDefaultFrom',
'exportNamespaceFrom',
'functionBind',
'functionSent',
'importMeta',
'logicalAssignment',
'nullishCoalescingOperator',
'numericSeparator',
'objectRestSpread',
'optionalCatchBinding',
'optionalChaining',
['pipelineOperator', { proposal: 'minimal' }],
'throwExpressions',
];

type ParserOptions = {
plugins?: Array<string | [string, {}]>,
tokens?: boolean,
};

export type Options = {
cwd?: string,
filename?: string,
legacyDecorators?: boolean,
decoratorsBeforeExport?: boolean,
parserOptions?: ParserOptions,
};

function buildOptions(options: Options) {
const parserOptions = {
strictMode: false,
tokens: true,
function buildOptions({
cwd,
filename,
parserOptions,
}: Options): ParserOptions {
let options = {
plugins: [],
};

if (options.legacyDecorators) {
parserOptions.plugins.push('decorators-legacy');
if (parserOptions) {
options = {
...parserOptions,
plugins: parserOptions.plugins ? [...parserOptions.plugins] : [],
};
}

const partialConfig = babel.loadPartialConfig({
cwd: options.cwd,
filename: options.filename,
cwd,
filename,
});

if (!partialConfig.hasFilesystemConfig()) {
parserOptions.plugins = [...babelParserOptions.plugins];

if (!options.legacyDecorators) {
parserOptions.plugins.push([
'decorators',
{ decoratorsBeforeExport: options.decoratorsBeforeExport || false },
]);
}
if (!partialConfig.hasFilesystemConfig() && options.plugins.length === 0) {
options.plugins = [...defaultPlugins];
}

return parserOptions;
// Recast needs tokens to be in the tree
// $FlowIssue tokens is clearly in the Options
options.tokens = true;
// Ensure we always have estree plugin enabled, if we add it a second time
// here it does not matter
options.plugins.push('estree');

return options;
}

export default function buildParse(options?: Options = {}) {
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1984,10 +1984,10 @@ flat-cache@^1.2.1:
graceful-fs "^4.1.2"
write "^0.2.1"

flow-bin@^0.87.0:
version "0.87.0"
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.87.0.tgz#fab7f984d8cc767e93fa9eb01cf7d57ed744f19d"
integrity sha512-mnvBXXZkUp4y6A96bR5BHa3q1ioIIN2L10w5osxJqagAakTXFYZwjl0t9cT3y2aCEf1wnK6n91xgYypQS/Dqbw==
flow-bin@^0.93.0:
version "0.93.0"
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.93.0.tgz#9192a08d88db2a8da0ff55e42420f44539791430"
integrity sha512-p8yq4ocOlpyJgOEBEj0v0GzCP25c9WP0ilFQ8hXSbrTR7RPKuR+Whr+OitlVyp8ocdX0j1MrIwQ8x28dacy1pg==

for-in@^1.0.1, for-in@^1.0.2:
version "1.0.2"
Expand Down