mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-30 05:55:48 +08:00
Update react-docgen
This commit is contained in:
124
website/react-docgen/lib/ReactDocumentationParser.js
vendored
124
website/react-docgen/lib/ReactDocumentationParser.js
vendored
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
type Handler = (documentation: Documentation, path: NodePath) => void;
|
||||
|
||||
var Documentation = require('./Documentation');
|
||||
|
||||
var findExportedReactCreateClass =
|
||||
require('./strategies/findExportedReactCreateClassCall');
|
||||
var getPropertyName = require('./utils/getPropertyName');
|
||||
var recast = require('recast');
|
||||
var resolveToValue = require('./utils/resolveToValue');
|
||||
var n = recast.types.namedTypes;
|
||||
|
||||
class ReactDocumentationParser {
|
||||
_componentHandlers: Array<Handler>;
|
||||
_apiHandlers: Object<string, Handler>;
|
||||
|
||||
constructor() {
|
||||
this._componentHandlers = [];
|
||||
this._apiHandlers = Object.create(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handlers to extract information from the component definition.
|
||||
*
|
||||
* If "property" is not provided, the handler is passed the whole component
|
||||
* definition.
|
||||
*
|
||||
* NOTE: The component definition is currently expected to be represented as
|
||||
* an ObjectExpression (an object literal). This will likely change in the
|
||||
* future.
|
||||
*/
|
||||
addHandler(handler: Handler, property?: string): void {
|
||||
if (!property) {
|
||||
this._componentHandlers.push(handler);
|
||||
} else {
|
||||
if (!this._apiHandlers[property]) {
|
||||
this._apiHandlers[property] = [];
|
||||
}
|
||||
this._apiHandlers[property].push(handler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes JavaScript source code and returns an object with the information
|
||||
* extract from it.
|
||||
*
|
||||
* The second argument is strategy to find the AST node(s) of the component
|
||||
* definition(s) inside `source`.
|
||||
* It is a function that gets passed the program AST node of
|
||||
* the source as first argument, and a reference to recast as second argument.
|
||||
*
|
||||
* This allows you define your own strategy for finding component definitions.
|
||||
* By default it will look for the exported component created by
|
||||
* React.createClass. An error is thrown if multiple components are exported.
|
||||
*
|
||||
* NOTE: The component definition is currently expected to be represented as
|
||||
* an ObjectExpression (an object literal), no matter which strategy is
|
||||
* chosen. This will likely change in the future.
|
||||
*/
|
||||
parseSource(
|
||||
source: string,
|
||||
componentDefinitionStrategy?:
|
||||
(program: ASTNode, recast: Object) => (Array<NodePath>|NodePath)
|
||||
): (Array<Object>|Object) {
|
||||
if (!componentDefinitionStrategy) {
|
||||
componentDefinitionStrategy = findExportedReactCreateClass;
|
||||
}
|
||||
var ast = recast.parse(source);
|
||||
// Find the component definitions first. The return value should be
|
||||
// an ObjectExpression.
|
||||
var componentDefinition = componentDefinitionStrategy(ast.program, recast);
|
||||
var isArray = Array.isArray(componentDefinition);
|
||||
if (!componentDefinition || (isArray && componentDefinition.length === 0)) {
|
||||
throw new Error(ReactDocumentationParser.ERROR_MISSING_DEFINITION);
|
||||
}
|
||||
|
||||
return isArray ?
|
||||
this._executeHandlers(componentDefinition).map(
|
||||
documentation => documentation.toObject()
|
||||
) :
|
||||
this._executeHandlers([componentDefinition])[0].toObject();
|
||||
}
|
||||
|
||||
_executeHandlers(componentDefinitions: Array<NodePath>): Array<Documenation> {
|
||||
return componentDefinitions.map(componentDefinition => {
|
||||
var documentation = new Documentation();
|
||||
componentDefinition.get('properties').each(propertyPath => {
|
||||
var name = getPropertyName(propertyPath);
|
||||
if (!this._apiHandlers[name]) {
|
||||
return;
|
||||
}
|
||||
var propertyValuePath = propertyPath.get('value');
|
||||
this._apiHandlers[name].forEach(
|
||||
handler => handler(documentation, propertyValuePath)
|
||||
);
|
||||
});
|
||||
|
||||
this._componentHandlers.forEach(
|
||||
handler => handler(documentation, componentDefinition)
|
||||
);
|
||||
return documentation;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ReactDocumentationParser.ERROR_MISSING_DEFINITION =
|
||||
'No suitable component definition found.';
|
||||
|
||||
module.exports = ReactDocumentationParser;
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
jest.autoMockOff();
|
||||
|
||||
describe('React documentation parser', function() {
|
||||
var ReactDocumentationParser;
|
||||
var parser;
|
||||
var recast;
|
||||
|
||||
beforeEach(function() {
|
||||
recast = require('recast');
|
||||
ReactDocumentationParser = require('../ReactDocumentationParser');
|
||||
parser = new ReactDocumentationParser();
|
||||
});
|
||||
|
||||
function pathFromSource(source) {
|
||||
return new recast.types.NodePath(
|
||||
recast.parse(source).program.body[0].expression
|
||||
);
|
||||
}
|
||||
|
||||
describe('parseSource', function() {
|
||||
|
||||
it('allows custom component definition resolvers', function() {
|
||||
var path = pathFromSource('({foo: "bar"})');
|
||||
var resolver = jest.genMockFunction().mockReturnValue(path);
|
||||
var handler = jest.genMockFunction();
|
||||
parser.addHandler(handler);
|
||||
parser.parseSource('', resolver);
|
||||
|
||||
expect(resolver).toBeCalled();
|
||||
expect(handler.mock.calls[0][1]).toBe(path);
|
||||
});
|
||||
|
||||
it('errors if component definition is not found', function() {
|
||||
var handler = jest.genMockFunction();
|
||||
expect(function() {
|
||||
parser.parseSource('', handler);
|
||||
}).toThrow(ReactDocumentationParser.ERROR_MISSING_DEFINITION);
|
||||
expect(handler).toBeCalled();
|
||||
|
||||
handler = jest.genMockFunction().mockReturnValue([]);
|
||||
expect(function() {
|
||||
parser.parseSource('', handler);
|
||||
}).toThrow(ReactDocumentationParser.ERROR_MISSING_DEFINITION);
|
||||
expect(handler).toBeCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
jest.autoMockOff();
|
||||
|
||||
var module_template = [
|
||||
'var React = require("React");',
|
||||
'var PropTypes = React.PropTypes;',
|
||||
'var Component = React.createClass(%s);',
|
||||
'module.exports = Component;'
|
||||
].join('\n');
|
||||
|
||||
function getSource(definition) {
|
||||
return module_template.replace('%s', definition);
|
||||
}
|
||||
|
||||
describe('React documentation parser', function() {
|
||||
var parser;
|
||||
|
||||
beforeEach(function() {
|
||||
parser = new (require('../../ReactDocumentationParser'));
|
||||
parser.addHandler(require('../defaultValueHandler'), 'getDefaultProps');
|
||||
});
|
||||
|
||||
it ('should find prop default values that are literals', function() {
|
||||
var source = getSource([
|
||||
'{',
|
||||
' getDefaultProps: function() {',
|
||||
' return {',
|
||||
' foo: "bar",',
|
||||
' bar: 42,',
|
||||
' baz: ["foo", "bar"],',
|
||||
' abc: {xyz: abc.def, 123: 42}',
|
||||
' };',
|
||||
' }',
|
||||
'}'
|
||||
].join('\n'));
|
||||
|
||||
var expectedResult = {
|
||||
description: '',
|
||||
props: {
|
||||
foo: {
|
||||
defaultValue: {
|
||||
value: '"bar"',
|
||||
computed: false
|
||||
}
|
||||
},
|
||||
bar: {
|
||||
defaultValue: {
|
||||
value: '42',
|
||||
computed: false
|
||||
}
|
||||
},
|
||||
baz: {
|
||||
defaultValue: {
|
||||
value: '["foo", "bar"]',
|
||||
computed: false
|
||||
}
|
||||
},
|
||||
abc: {
|
||||
defaultValue: {
|
||||
value: '{xyz: abc.def, 123: 42}',
|
||||
computed: false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var result = parser.parseSource(source);
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
});
|
||||
@@ -25,12 +25,17 @@ describe('propDocblockHandler', function() {
|
||||
});
|
||||
|
||||
function parse(definition) {
|
||||
return utils.parse('(' + definition + ')').get('body', 0, 'expression');
|
||||
var programPath = utils.parse(definition);
|
||||
return programPath.get(
|
||||
'body',
|
||||
programPath.node.body.length - 1,
|
||||
'expression'
|
||||
);
|
||||
}
|
||||
|
||||
it('finds docblocks for prop types', function() {
|
||||
var definition = parse([
|
||||
'{',
|
||||
'({',
|
||||
' propTypes: {',
|
||||
' /**',
|
||||
' * Foo comment',
|
||||
@@ -42,7 +47,7 @@ describe('propDocblockHandler', function() {
|
||||
' */',
|
||||
' bar: Prop.bool,',
|
||||
' }',
|
||||
'}'
|
||||
'})'
|
||||
].join('\n'));
|
||||
|
||||
propDocblockHandler(documentation, definition);
|
||||
@@ -58,7 +63,7 @@ describe('propDocblockHandler', function() {
|
||||
|
||||
it('can handle multline comments', function() {
|
||||
var definition = parse([
|
||||
'{',
|
||||
'({',
|
||||
' propTypes: {',
|
||||
' /**',
|
||||
' * Foo comment with',
|
||||
@@ -68,7 +73,7 @@ describe('propDocblockHandler', function() {
|
||||
' */',
|
||||
' foo: Prop.bool',
|
||||
' }',
|
||||
'}'
|
||||
'})'
|
||||
].join('\n'));
|
||||
|
||||
propDocblockHandler(documentation, definition);
|
||||
@@ -82,7 +87,7 @@ describe('propDocblockHandler', function() {
|
||||
|
||||
it('ignores non-docblock comments', function() {
|
||||
var definition = parse([
|
||||
'{',
|
||||
'({',
|
||||
' propTypes: {',
|
||||
' /**',
|
||||
' * Foo comment',
|
||||
@@ -96,7 +101,7 @@ describe('propDocblockHandler', function() {
|
||||
' /* This is not a doc comment */',
|
||||
' bar: Prop.bool,',
|
||||
' }',
|
||||
'}'
|
||||
'})'
|
||||
].join('\n'));
|
||||
|
||||
propDocblockHandler(documentation, definition);
|
||||
@@ -112,7 +117,7 @@ describe('propDocblockHandler', function() {
|
||||
|
||||
it('only considers the comment with the property below it', function() {
|
||||
var definition = parse([
|
||||
'{',
|
||||
'({',
|
||||
' propTypes: {',
|
||||
' /**',
|
||||
' * Foo comment',
|
||||
@@ -120,7 +125,7 @@ describe('propDocblockHandler', function() {
|
||||
' foo: Prop.bool,',
|
||||
' bar: Prop.bool,',
|
||||
' }',
|
||||
'}'
|
||||
'})'
|
||||
].join('\n'));
|
||||
|
||||
propDocblockHandler(documentation, definition);
|
||||
@@ -136,7 +141,7 @@ describe('propDocblockHandler', function() {
|
||||
|
||||
it('understands and ignores the spread operator', function() {
|
||||
var definition = parse([
|
||||
'{',
|
||||
'({',
|
||||
' propTypes: {',
|
||||
' ...Foo.propTypes,',
|
||||
' /**',
|
||||
@@ -144,7 +149,28 @@ describe('propDocblockHandler', function() {
|
||||
' */',
|
||||
' foo: Prop.bool,',
|
||||
' }',
|
||||
'}'
|
||||
'})'
|
||||
].join('\n'));
|
||||
|
||||
propDocblockHandler(documentation, definition);
|
||||
expect(documentation.descriptors).toEqual({
|
||||
foo: {
|
||||
description: 'Foo comment'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves variables', function() {
|
||||
var definition = parse([
|
||||
'var Props = {',
|
||||
' /**',
|
||||
' * Foo comment',
|
||||
' */',
|
||||
' foo: Prop.bool,',
|
||||
'};',
|
||||
'({',
|
||||
' propTypes: Props',
|
||||
'})'
|
||||
].join('\n'));
|
||||
|
||||
propDocblockHandler(documentation, definition);
|
||||
|
||||
@@ -171,4 +171,21 @@ describe('propTypeHandler', function() {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves variables', function() {
|
||||
var definition = parse([
|
||||
'var props = {bar: PropTypes.bool};',
|
||||
'({',
|
||||
' propTypes: props',
|
||||
'})',
|
||||
].join('\n'));
|
||||
|
||||
propTypeHandler(documentation, definition);
|
||||
expect(documentation.descriptors).toEqual({
|
||||
bar: {
|
||||
type: {},
|
||||
required: false
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
var Documentation = require('../Documentation');
|
||||
|
||||
var expressionTo = require('../utils/expressionTo');
|
||||
var getPropertyName = require('../utils/getPropertyName');
|
||||
var recast = require('recast');
|
||||
var resolveToValue = require('../utils/resolveToValue');
|
||||
var types = recast.types.namedTypes;
|
||||
var visit = recast.types.visit;
|
||||
|
||||
function getDefaultValue(path) {
|
||||
var node = path.node;
|
||||
var defaultValue;
|
||||
if (types.Literal.check(node)) {
|
||||
defaultValue = node.raw;
|
||||
} else {
|
||||
path = resolveToValue(path);
|
||||
node = path.node;
|
||||
defaultValue = recast.print(path).code;
|
||||
}
|
||||
if (typeof defaultValue !== 'undefined') {
|
||||
return {
|
||||
value: defaultValue,
|
||||
computed: types.CallExpression.check(node) ||
|
||||
types.MemberExpression.check(node) ||
|
||||
types.Identifier.check(node)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function defaultValueHandler(documentation: Documentation, path: NodePath) {
|
||||
if (!types.FunctionExpression.check(path.node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the value that is returned from the function and process it if it is
|
||||
// an object literal.
|
||||
var objectExpressionPath;
|
||||
visit(path.get('body'), {
|
||||
visitFunction: () => false,
|
||||
visitReturnStatement: function(path) {
|
||||
var resolvedPath = resolveToValue(path.get('argument'));
|
||||
if (types.ObjectExpression.check(resolvedPath.node)) {
|
||||
objectExpressionPath = resolvedPath;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (objectExpressionPath) {
|
||||
objectExpressionPath.get('properties').each(function(propertyPath) {
|
||||
var propDescriptor = documentation.getPropDescriptor(
|
||||
getPropertyName(propertyPath)
|
||||
);
|
||||
var defaultValue = getDefaultValue(propertyPath.get('value'));
|
||||
if (defaultValue) {
|
||||
propDescriptor.defaultValue = defaultValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = defaultValueHandler;
|
||||
@@ -15,13 +15,14 @@
|
||||
|
||||
var Documentation = require('../Documentation');
|
||||
|
||||
var types = require('recast').types.namedTypes;
|
||||
var getDocblock = require('../utils/docblock').getDocblock;
|
||||
var getPropertyName = require('../utils/getPropertyName');
|
||||
var getPropertyValuePath = require('../utils/getPropertyValuePath');
|
||||
var types = require('recast').types.namedTypes;
|
||||
var resolveToValue = require('../utils/resolveToValue');
|
||||
|
||||
function propDocBlockHandler(documentation: Documentation, path: NodePath) {
|
||||
var propTypesPath = getPropertyValuePath(path, 'propTypes');
|
||||
var propTypesPath = resolveToValue(getPropertyValuePath(path, 'propTypes'));
|
||||
if (!propTypesPath || !types.ObjectExpression.check(propTypesPath.node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ function amendPropTypes(documentation, path) {
|
||||
}
|
||||
|
||||
function propTypeHandler(documentation: Documentation, path: NodePath) {
|
||||
var propTypesPath = getPropertyValuePath(resolveToValue(path), 'propTypes');
|
||||
var propTypesPath = resolveToValue(getPropertyValuePath(path, 'propTypes'));
|
||||
if (!propTypesPath || !types.ObjectExpression.check(propTypesPath.node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
jest.autoMockOff();
|
||||
|
||||
describe('React documentation parser', function() {
|
||||
var findAllReactCreateClassCalls;
|
||||
var recast;
|
||||
|
||||
function parse(source) {
|
||||
return findAllReactCreateClassCalls(
|
||||
recast.parse(source).program,
|
||||
recast
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
findAllReactCreateClassCalls = require('../findAllReactCreateClassCalls');
|
||||
recast = require('recast');
|
||||
});
|
||||
|
||||
|
||||
it('finds React.createClass', function() {
|
||||
var source = [
|
||||
'var React = require("React");',
|
||||
'var Component = React.createClass({});',
|
||||
'module.exports = Component;'
|
||||
].join('\n');
|
||||
|
||||
var result = parse(source);
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0] instanceof recast.types.NodePath).toBe(true);
|
||||
expect(result[0].node.type).toBe('ObjectExpression');
|
||||
});
|
||||
|
||||
it('finds React.createClass, independent of the var name', function() {
|
||||
var source = [
|
||||
'var R = require("React");',
|
||||
'var Component = R.createClass({});',
|
||||
'module.exports = Component;'
|
||||
].join('\n');
|
||||
|
||||
var result = parse(source);
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
expect(result.length).toBe(1);
|
||||
});
|
||||
|
||||
it('does not process X.createClass of other modules', function() {
|
||||
var source = [
|
||||
'var R = require("NoReact");',
|
||||
'var Component = R.createClass({});',
|
||||
'module.exports = Component;'
|
||||
].join('\n');
|
||||
|
||||
var result = parse(source);
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
expect(result.length).toBe(0);
|
||||
});
|
||||
|
||||
it('finds assignments to exports', function() {
|
||||
var source = [
|
||||
'var R = require("React");',
|
||||
'var Component = R.createClass({});',
|
||||
'exports.foo = 42;',
|
||||
'exports.Component = Component;'
|
||||
].join('\n');
|
||||
|
||||
var result = parse(source);
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
expect(result.length).toBe(1);
|
||||
});
|
||||
|
||||
it('accepts multiple definitions', function() {
|
||||
var source = [
|
||||
'var R = require("React");',
|
||||
'var ComponentA = R.createClass({});',
|
||||
'var ComponentB = R.createClass({});',
|
||||
'exports.ComponentB = ComponentB;'
|
||||
].join('\n');
|
||||
|
||||
var result = parse(source);
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
expect(result.length).toBe(2);
|
||||
|
||||
source = [
|
||||
'var R = require("React");',
|
||||
'var ComponentA = R.createClass({});',
|
||||
'var ComponentB = R.createClass({});',
|
||||
'module.exports = ComponentB;'
|
||||
].join('\n');
|
||||
|
||||
result = parse(source);
|
||||
expect(Array.isArray(result)).toBe(true);
|
||||
expect(result.length).toBe(2);
|
||||
});
|
||||
});
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
jest.autoMockOff();
|
||||
|
||||
describe('React documentation parser', function() {
|
||||
var findExportedReactCreateClass;
|
||||
var recast;
|
||||
|
||||
function parse(source) {
|
||||
return findExportedReactCreateClass(
|
||||
recast.parse(source).program,
|
||||
recast
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
findExportedReactCreateClass =
|
||||
require('../findExportedReactCreateClassCall');
|
||||
recast = require('recast');
|
||||
});
|
||||
|
||||
it('finds React.createClass', function() {
|
||||
var source = [
|
||||
'var React = require("React");',
|
||||
'var Component = React.createClass({});',
|
||||
'module.exports = Component;'
|
||||
].join('\n');
|
||||
|
||||
expect(parse(source)).toBeDefined();
|
||||
});
|
||||
|
||||
it('finds React.createClass, independent of the var name', function() {
|
||||
var source = [
|
||||
'var R = require("React");',
|
||||
'var Component = R.createClass({});',
|
||||
'module.exports = Component;'
|
||||
].join('\n');
|
||||
|
||||
expect(parse(source)).toBeDefined();
|
||||
});
|
||||
|
||||
it('does not process X.createClass of other modules', function() {
|
||||
var source = [
|
||||
'var R = require("NoReact");',
|
||||
'var Component = R.createClass({});',
|
||||
'module.exports = Component;'
|
||||
].join('\n');
|
||||
|
||||
expect(parse(source)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('finds assignments to exports', function() {
|
||||
var source = [
|
||||
'var R = require("React");',
|
||||
'var Component = R.createClass({});',
|
||||
'exports.foo = 42;',
|
||||
'exports.Component = Component;'
|
||||
].join('\n');
|
||||
|
||||
expect(parse(source)).toBeDefined();
|
||||
});
|
||||
|
||||
it('errors if multiple components are exported', function() {
|
||||
var source = [
|
||||
'var R = require("React");',
|
||||
'var ComponentA = R.createClass({});',
|
||||
'var ComponentB = R.createClass({});',
|
||||
'exports.ComponentA = ComponentA;',
|
||||
'exports.ComponentB = ComponentB;'
|
||||
].join('\n');
|
||||
|
||||
expect(function() {
|
||||
parse(source)
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('accepts multiple definitions if only one is exported', function() {
|
||||
var source = [
|
||||
'var R = require("React");',
|
||||
'var ComponentA = R.createClass({});',
|
||||
'var ComponentB = R.createClass({});',
|
||||
'exports.ComponentB = ComponentB;'
|
||||
].join('\n');
|
||||
|
||||
expect(parse(source)).toBeDefined();
|
||||
|
||||
source = [
|
||||
'var R = require("React");',
|
||||
'var ComponentA = R.createClass({});',
|
||||
'var ComponentB = R.createClass({});',
|
||||
'module.exports = ComponentB;'
|
||||
].join('\n');
|
||||
|
||||
expect(parse(source)).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
var isReactCreateClassCall = require('../utils/isReactCreateClassCall');
|
||||
var resolveToValue = require('../utils/resolveToValue');
|
||||
|
||||
/**
|
||||
* Given an AST, this function tries to find all object expressions that are
|
||||
* passed to `React.createClass` calls, by resolving all references properly.
|
||||
*/
|
||||
function findAllReactCreateClassCalls(
|
||||
ast: ASTNode,
|
||||
recast: Object
|
||||
): Array<NodePath> {
|
||||
var types = recast.types.namedTypes;
|
||||
var definitions = [];
|
||||
|
||||
recast.visit(ast, {
|
||||
visitCallExpression: function(path) {
|
||||
if (!isReactCreateClassCall(path)) {
|
||||
return false;
|
||||
}
|
||||
// We found React.createClass. Lets get cracking!
|
||||
var resolvedPath = resolveToValue(path.get('arguments', 0));
|
||||
if (types.ObjectExpression.check(resolvedPath.node)) {
|
||||
definitions.push(resolvedPath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
module.exports = findAllReactCreateClassCalls;
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
var isExportsOrModuleAssignment =
|
||||
require('../utils/isExportsOrModuleAssignment');
|
||||
var isReactCreateClassCall = require('../utils/isReactCreateClassCall');
|
||||
var resolveToValue = require('../utils/resolveToValue');
|
||||
|
||||
var ERROR_MULTIPLE_DEFINITIONS =
|
||||
'Multiple exported component definitions found.';
|
||||
|
||||
function ignore() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an AST, this function tries to find the object expression that is
|
||||
* passed to `React.createClass`, by resolving all references properly.
|
||||
*/
|
||||
function findExportedReactCreateClass(
|
||||
ast: ASTNode,
|
||||
recast: Object
|
||||
): ?NodePath {
|
||||
var types = recast.types.namedTypes;
|
||||
var definition;
|
||||
|
||||
recast.visit(ast, {
|
||||
visitFunctionDeclaration: ignore,
|
||||
visitFunctionExpression: ignore,
|
||||
visitIfStatement: ignore,
|
||||
visitWithStatement: ignore,
|
||||
visitSwitchStatement: ignore,
|
||||
visitCatchCause: ignore,
|
||||
visitWhileStatement: ignore,
|
||||
visitDoWhileStatement: ignore,
|
||||
visitForStatement: ignore,
|
||||
visitForInStatement: ignore,
|
||||
visitAssignmentExpression: function(path) {
|
||||
// Ignore anything that is not `exports.X = ...;` or
|
||||
// `module.exports = ...;`
|
||||
if (!isExportsOrModuleAssignment(path)) {
|
||||
return false;
|
||||
}
|
||||
// Resolve the value of the right hand side. It should resolve to a call
|
||||
// expression, something like React.createClass
|
||||
path = resolveToValue(path.get('right'));
|
||||
if (!isReactCreateClassCall(path)) {
|
||||
return false;
|
||||
}
|
||||
if (definition) {
|
||||
// If a file exports multiple components, ... complain!
|
||||
throw new Error(ERROR_MULTIPLE_DEFINITIONS);
|
||||
}
|
||||
// We found React.createClass. Lets get cracking!
|
||||
var resolvedPath = resolveToValue(path.get('arguments', 0));
|
||||
if (types.ObjectExpression.check(resolvedPath.node)) {
|
||||
definition = resolvedPath;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
module.exports = findExportedReactCreateClass;
|
||||
Reference in New Issue
Block a user