Skip to content

Commit

Permalink
[Fix]: use variable value in prop type fields defined by variables
Browse files Browse the repository at this point in the history
  • Loading branch information
jzabala committed Jul 9, 2020
1 parent c8915b1 commit 0e0de41
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 0 deletions.
50 changes: 50 additions & 0 deletions lib/util/propTypes.js
Expand Up @@ -390,6 +390,27 @@ module.exports = function propTypesInstructions(context, components, utils) {
});
}

/**
* Resolve node of type Identifier when building declaration types.
* @param {ASTNode} node
* @param {Function} callback called with the resolved value only if resolved.
*/
function resolveValueForIdentifierNode(node, callback) {
if (
node
&& node.type === 'Identifier'
) {
const scope = context.getScope();
const identVariable = scope.variableScope.variables.find(
(variable) => variable.name === node.name
);
if (identVariable) {
const definition = identVariable.defs[identVariable.defs.length - 1];
callback(definition.node.init);
}
}
}

/**
* Creates the representation of the React propTypes for the component.
* The representation is used to verify nested used properties.
Expand All @@ -408,6 +429,23 @@ module.exports = function propTypesInstructions(context, components, utils) {
return {};
}

let identNodeResolved = false;
// Resolve identifier node for cases where isRequired is set in
// the variable declaration or not at all.
// const variableType = PropTypes.shape({ foo: ... }).isRequired
// propTypes = {
// example: variableType
// }
// --------
// const variableType = PropTypes.shape({ foo: ... })
// propTypes = {
// example: variableType
// }
resolveValueForIdentifierNode(value, (newValue) => {
identNodeResolved = true;
value = newValue;
});

if (
value
&& value.type === 'MemberExpression'
Expand All @@ -418,6 +456,18 @@ module.exports = function propTypesInstructions(context, components, utils) {
value = value.object;
}

// Resolve identifier node for cases where isRequired is set in
// the prop types.
// const variableType = PropTypes.shape({ foo: ... })
// propTypes = {
// example: variableType.isRequired
// }
if (!identNodeResolved) {
resolveValueForIdentifierNode(value, (newValue) => {
value = newValue;
});
}

// Verify PropTypes that are functions
if (
value
Expand Down
132 changes: 132 additions & 0 deletions tests/lib/rules/prop-types.js
Expand Up @@ -4949,6 +4949,138 @@ ruleTester.run('prop-types', rule, {
{
message: '\'ordering\' is missing in props validation'
}]
},
{
code: `
const firstType = PropTypes.shape({
id: PropTypes.number,
});
class ComponentX extends React.Component {
static propTypes = {
first: firstType.isRequired,
};
render() {
return (
<div>
<div>Counter = {this.props.first.name}</div>
</div>
);
}
}
`,
parser: parsers.BABEL_ESLINT,
errors: [{
message: "'first.name' is missing in props validation"
}]
},
{
code: `
const firstType = PropTypes.shape({
id: PropTypes.number,
}).isRequired;
class ComponentX extends React.Component {
static propTypes = {
first: firstType,
};
render() {
return (
<div>
<div>Counter = {this.props.first.name}</div>
</div>
);
}
}
`,
parser: parsers.BABEL_ESLINT,
errors: [{
message: "'first.name' is missing in props validation"
}]
},
{
code: `
const firstType = PropTypes.shape({
id: PropTypes.number,
});
class ComponentX extends React.Component {
static propTypes = {
first: firstType,
};
render() {
return (
<div>
<div>Counter = {this.props.first.name}</div>
</div>
);
}
}
`,
parser: parsers.BABEL_ESLINT,
errors: [{
message: "'first.name' is missing in props validation"
}]
},
{
code: `
function Foo({
foo: {
bar: foo,
baz
},
}) {
return <p>{foo.reduce(() => 5)}</p>;
}
const fooType = PropTypes.shape({
bar: PropTypes.arrayOf(PropTypes.string).isRequired,
}).isRequired
Foo.propTypes = {
foo: fooType,
};
`,
errors: [{
message: '\'foo.baz\' is missing in props validation'
}]
},
{
code: `
function Foo({
foo: {
bar: foo,
baz
},
}) {
return <p>{foo.reduce(() => 5)}</p>;
}
const fooType = PropTypes.shape({
bar: PropTypes.arrayOf(PropTypes.string).isRequired,
})
Foo.propTypes = {
foo: fooType.isRequired,
};
`,
errors: [{
message: '\'foo.baz\' is missing in props validation'
}]
},
{
code: `
function Foo({
foo: {
bar: foo,
baz
},
}) {
return <p>{foo.reduce(() => 5)}</p>;
}
const fooType = PropTypes.shape({
bar: PropTypes.arrayOf(PropTypes.string).isRequired,
})
Foo.propTypes = {
foo: fooType,
};
`,
errors: [{
message: '\'foo.baz\' is missing in props validation'
}]
}
]
});

0 comments on commit 0e0de41

Please sign in to comment.