Skip to content

Commit f956faa

Browse files
committed
Use Babylon for parsing the React component files
We need to use Babel in order to be able to parse ES7 class properties. However, there is no need to load all of Babel. Babylon, Babel's parser, suffices. In turns out that almost all handlers and helper methods work well with Babylon's AST.
1 parent 65f0339 commit f956faa

File tree

10 files changed

+82
-51
lines changed

10 files changed

+82
-51
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"dependencies": {
2727
"async": "^0.9.0",
2828
"babel-runtime": "^5.7.0",
29+
"babylon": "~5.8.3",
2930
"node-dir": "^0.1.6",
3031
"nomnom": "^1.8.1",
3132
"recast": "^0.10.x"
@@ -49,7 +50,8 @@
4950
"unmockedModulePathPatterns": [
5051
"tests/utils",
5152
"recast",
52-
"babel"
53+
"babel",
54+
"babylon"
5355
]
5456
}
5557
}

src/__tests__/parse-test.js

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,18 @@
88
*
99
*/
1010

11-
"use strict";
11+
/*global jest, describe, beforeEach, it, expect*/
1212

1313
jest.autoMockOff();
1414

1515
describe('parse', () => {
1616
var utils;
17-
var parse;
17+
var parse, ERROR_MISSING_DEFINITION;
1818

1919
beforeEach(() => {
2020
utils = require('../../tests/utils');
21-
parse = require('../parse');
21+
// ugly but necessary because ../parse has default and named exports
22+
({default: parse, ERROR_MISSING_DEFINITION} = require('../parse'));
2223
});
2324

2425
function pathFromSource(source) {
@@ -37,15 +38,10 @@ describe('parse', () => {
3738

3839
it('errors if component definition is not found', () => {
3940
var resolver = jest.genMockFunction();
40-
expect(function() {
41-
parse('//empty', resolver);
42-
}).toThrow(parse.ERROR_MISSING_DEFINITION);
41+
expect(() => parse('//empty', resolver)).toThrow(ERROR_MISSING_DEFINITION);
4342
expect(resolver).toBeCalled();
4443

45-
handler = jest.genMockFunction().mockReturnValue([]);
46-
expect(function() {
47-
parse('//empty', resolver);
48-
}).toThrow(parse.ERROR_MISSING_DEFINITION);
44+
expect(() => parse('//empty', resolver)).toThrow(ERROR_MISSING_DEFINITION);
4945
expect(resolver).toBeCalled();
5046
});
5147

src/babylon.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2015, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
*/
10+
11+
var babylon = require('babylon');
12+
13+
var options = {
14+
sourceType: 'module',
15+
strictMode: false,
16+
locations: true,
17+
ranges: true,
18+
ecmaVersion: 7,
19+
features: {
20+
'es7.classProperties': true,
21+
'es7.decorators': true,
22+
'es7.comprehensions': true,
23+
'es7.asyncFunctions': true,
24+
'es7.exportExtensions': true,
25+
'es7.trailingFunctionCommas': true,
26+
'es7.objectRestSpread': true,
27+
},
28+
plugins: { jsx: true, flow: true },
29+
};
30+
31+
export default {
32+
parse(src) {
33+
var file = babylon.parse(src, options);
34+
file.program.comments = file.comments;
35+
return file.program;
36+
},
37+
};

src/main.js

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
11
/*
2-
* Copyright (c) 2015, Facebook, Inc.
3-
* All rights reserved.
2+
* Copyright (c) 2015, Facebook, Inc.
3+
* All rights reserved.
44
*
55
* This source code is licensed under the BSD-style license found in the
6-
* LICENSE file in the root directory of this source tree. An additional grant
7-
* of patent rights can be found in the PATENTS file in the same directory.
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
88
*
9-
*/
10-
11-
/**
129
* @flow
10+
*
1311
*/
14-
"use strict";
1512

16-
var handlers = require('./handlers');
17-
var parse = require('./parse');
18-
var resolver = require('./resolver');
19-
var utils = require('./utils');
13+
import handlers from './handlers';
14+
import parse from './parse';
15+
import resolver from './resolver';
16+
import utils from './utils';
2017

21-
var defaultResolver = resolver.findExportedReactCreateClassCall;
22-
var defaultHandlers = [
18+
const defaultResolver = resolver.findExportedReactCreateClassCall;
19+
const defaultHandlers = [
2320
handlers.propTypeHandler,
2421
handlers.propDocBlockHandler,
2522
handlers.defaultPropsHandler,
@@ -38,11 +35,11 @@ var defaultHandlers = [
3835
* documentation (from docblocks), default prop values and component
3936
* documentation (from a docblock).
4037
*/
41-
function defaultParse(
38+
function defaultParse( // eslint-disable-line no-unused-vars
4239
src: string,
4340
resolver?: ?Resolver,
4441
handlers?: ?Array<Handler>
45-
): (Array<Object>|Object) {
42+
): Array<Object>|Object {
4643
if (!resolver) {
4744
resolver = defaultResolver;
4845
}
@@ -53,8 +50,8 @@ function defaultParse(
5350
return parse(src, resolver, handlers);
5451
}
5552

56-
module.exports = {
57-
parse: defaultParse,
53+
export {
54+
defaultParse as parse,
5855
defaultHandlers,
5956
handlers,
6057
resolver,

src/parse.js

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
11
/*
2-
* Copyright (c) 2015, Facebook, Inc.
3-
* All rights reserved.
2+
* Copyright (c) 2015, Facebook, Inc.
3+
* All rights reserved.
44
*
5-
* This source code is licensed under the BSD-style license found in the
6-
* LICENSE file in the root directory of this source tree. An additional grant
7-
* of patent rights can be found in the PATENTS file in the same directory.
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
88
*
9-
*/
10-
11-
/**
129
* @flow
10+
*
1311
*/
14-
"use strict";
1512

16-
var Documentation = require('./Documentation');
13+
import Documentation from './Documentation';
1714

18-
var recast = require('recast');
15+
import babylon from './babylon';
16+
import recast from 'recast';
1917

20-
var ERROR_MISSING_DEFINITION = 'No suitable component definition found.';
18+
const ERROR_MISSING_DEFINITION = 'No suitable component definition found.';
2119

2220
function executeHandlers(handlers, componentDefinitions) {
2321
return componentDefinitions.map(componentDefinition => {
@@ -47,12 +45,12 @@ function executeHandlers(handlers, componentDefinitions) {
4745
* an array of documentation objects. If `resolver` returns a single node
4846
* instead, `parse` will return a documentation object.
4947
*/
50-
function parse(
48+
export default function parse(
5149
src: string,
5250
resolver: Resolver,
5351
handlers: Array<Handler>
54-
): (Array<Object>|Object) {
55-
var ast = recast.parse(src);
52+
): Array<Object>|Object {
53+
var ast = recast.parse(src, {esprima: babylon});
5654
var componentDefinitions = resolver(ast.program, recast);
5755
var isArray = Array.isArray(componentDefinitions);
5856

@@ -65,5 +63,4 @@ function parse(
6563
executeHandlers(handlers, [componentDefinitions])[0];
6664
}
6765

68-
module.exports = parse;
69-
exports.ERROR_MISSING_DEFINITION = ERROR_MISSING_DEFINITION;
66+
export {ERROR_MISSING_DEFINITION};

src/utils/__tests__/getClassMemberValuePath-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('getClassMemberValuePath', () => {
5555
});
5656
});
5757

58-
xdescribe('ClassProperty', () => {
58+
describe('ClassProperty', () => {
5959
it('finds "normal" class properties', () => {
6060
var def = statement(`
6161
class Foo {

src/utils/docblock.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ function getDocblock(path: NodePath): ?string {
3333
if (path.node.comments) {
3434
var comments = path.node.comments.filter(function(comment) {
3535
return comment.leading &&
36-
comment.type === 'Block' &&
36+
comment.type === 'CommentBlock' &&
3737
comment.value.indexOf('*\n') === 0;
3838
});
3939
if (comments.length > 0) {

src/utils/isReactComponentClass.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function isRenderMethod(node) {
2121
return types.MethodDefinition.check(node) &&
2222
!node.computed &&
2323
!node.static &&
24-
node.kind === '' &&
24+
(node.kind === '' || node.kind === 'method') &&
2525
node.key.name === 'render';
2626
}
2727

src/utils/normalizeClassDefinition.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ var ignore = () => false;
3030
*
3131
* is converted to
3232
*
33-
* class
3433
* class MyComponent extends React.Component {
3534
* // ...
3635
* static propTypes = { ... };

tests/utils.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44

55
import _recast from 'recast';
6+
import babylon from '../src/babylon';
67

78
function stringify(value) {
89
if (Array.isArray(value)) {
@@ -15,7 +16,9 @@ function stringify(value) {
1516
* Returns a NodePath to the program node of the passed node
1617
*/
1718
export function parse(src, recast=_recast) {
18-
return new recast.types.NodePath(recast.parse(stringify(src)).program);
19+
return new recast.types.NodePath(
20+
recast.parse(stringify(src), {esprima: babylon}).program
21+
);
1922
}
2023

2124
export function statement(src, recast=_recast) {

0 commit comments

Comments
 (0)