Skip to content

Commit

Permalink
[New]: add componentWrapperFunctions setting
Browse files Browse the repository at this point in the history
  • Loading branch information
jzabala committed Jul 14, 2020
1 parent e3e767b commit ecdbeda
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 8 deletions.
6 changes: 6 additions & 0 deletions README.md
Expand Up @@ -51,6 +51,12 @@ You should also specify settings that will be shared across all the plugin rules
{"property": "freeze", "object": "Object"},
{"property": "myFavoriteWrapper"}
],
"componentWrapperFunctions": [
// The names of any function used to wrap components, e.g. Mobx `observer` function. If this isn't set, components wrapped by these functions will be skipped.
"observer", // `property`
{"property": "styled"} // `object` is optional
{"property": "observer", "object": "Mobx"},
],
"linkComponents": [
// Components used as alternatives to <a> for linking, eg. <Link to={ url } />
"Hyperlink",
Expand Down
38 changes: 30 additions & 8 deletions lib/util/Components.js
Expand Up @@ -213,11 +213,27 @@ class Components {
}
}

function getWrapperFunctions(context, pragma) {
const componentWrapperFunctions = context.settings.componentWrapperFunctions || [];

return componentWrapperFunctions.map((wrapperFunction) => {
wrapperFunction = typeof wrapperFunction === 'string' ? {property: wrapperFunction} : Object.assign({}, wrapperFunction);
if (wrapperFunction.object === '<pragma>') {
wrapperFunction.object = pragma;
}
return wrapperFunction;
}).concat([
{property: 'forwardRef', object: pragma},
{property: 'memo', object: pragma}
]);
}

function componentRule(rule, context) {
const createClass = pragmaUtil.getCreateClassFromContext(context);
const pragma = pragmaUtil.getFromContext(context);
const sourceCode = context.getSourceCode();
const components = new Components();
const wrapperFunctions = getWrapperFunctions(context, pragma);

// Utilities for component detection
const utils = {
Expand Down Expand Up @@ -594,14 +610,20 @@ function componentRule(rule, context) {
if (!node || node.type !== 'CallExpression') {
return false;
}
const propertyNames = ['forwardRef', 'memo'];
const calleeObject = node.callee.object;
if (calleeObject && node.callee.property) {
return arrayIncludes(propertyNames, node.callee.property.name)
&& calleeObject.name === pragma
&& !this.nodeWrapsComponent(node);
}
return arrayIncludes(propertyNames, node.callee.name) && this.isDestructuredFromPragmaImport(node.callee.name);

return wrapperFunctions.some((wrapperFunction) => {
if (node.callee.type === 'MemberExpression') {
return wrapperFunction.object
&& wrapperFunction.object === node.callee.object.name
&& wrapperFunction.property === node.callee.property.name
&& !this.nodeWrapsComponent(node);
}
return wrapperFunction.property === node.callee.name
&& (!wrapperFunction.object
// Functions coming from the current pragma need special handling
|| (wrapperFunction.object === pragma && this.isDestructuredFromPragmaImport(node.callee.name))
);
});
},

/**
Expand Down
59 changes: 59 additions & 0 deletions tests/lib/rules/prop-types.js
Expand Up @@ -2569,6 +2569,26 @@ ruleTester.run('prop-types', rule, {
return null;
}`,
parser: parsers.TYPESCRIPT_ESLINT
},
{
code: `
const SideMenu = styled(
({ componentId }) => (
<S.Container>
<S.Head />
<UserInfo />
<Separator />
<MenuList componentId={componentId} />
</S.Container>
),
);
SideMenu.propTypes = {
componentId: PropTypes.string.isRequired
}
`,
settings: {
componentWrapperFunctions: [{property: 'styled'}]
}
}
],

Expand Down Expand Up @@ -5161,6 +5181,45 @@ ruleTester.run('prop-types', rule, {
errors: [{
message: '\'value\' is missing in props validation'
}]
},
{
code: `
const SideMenu = observer(
({ componentId }) => (
<S.Container>
<S.Head />
<UserInfo />
<Separator />
<MenuList componentId={componentId} />
</S.Container>
),
);`,
settings: {
componentWrapperFunctions: ['observer']
},
errors: [{
message: '\'componentId\' is missing in props validation'
}]
},
{
code: `
const SideMenu = Mobx.observer(
({ id }) => (
<S.Container>
<S.Head />
<UserInfo />
<Separator />
<MenuList componentId={id} />
</S.Container>
),
);
`,
settings: {
componentWrapperFunctions: [{property: 'observer', object: 'Mobx'}]
},
errors: [{
message: '\'id\' is missing in props validation'
}]
}
]
});

0 comments on commit ecdbeda

Please sign in to comment.