Skip to content

Commit

Permalink
Various speed ups by avoiding work and memory allocations
Browse files Browse the repository at this point in the history
- Combine filter -> forEach chains
- Extract deeply nested functions to higher scopes
- Promote shared or mostly-constant values and conditionals to higher scopes
- Swap compound boolean expressions to exploit short circuiting
- Push expensive operations below guards / early returns
- Push expensive operations into conditionals to exploit short circuiting
- Swap filters for finds when possible
- Swap some array function orders, e.g. Filter before reversing arrays
  • Loading branch information
willheslam committed May 4, 2021
1 parent d16fcb3 commit 232d6a1
Show file tree
Hide file tree
Showing 24 changed files with 293 additions and 234 deletions.
4 changes: 3 additions & 1 deletion lib/rules/boolean-prop-naming.js
Expand Up @@ -227,7 +227,9 @@ module.exports = {
if (!node || !Array.isArray(args)) {
return;
}
args.filter((arg) => arg.type === 'ObjectExpression').forEach((object) => validatePropNaming(node, object.properties));
args.forEach((object) => object.type === 'ObjectExpression'
&& validatePropNaming(node, object.properties)
);
}

// --------------------------------------------------------------------------
Expand Down
12 changes: 7 additions & 5 deletions lib/rules/default-props-match-prop-types.js
Expand Up @@ -94,11 +94,13 @@ module.exports = {
const list = components.list();

// If no defaultProps could be found, we don't report anything.
values(list).filter((component) => component.defaultProps).forEach((component) => {
reportInvalidDefaultProps(
component.declaredPropTypes,
component.defaultProps || {}
);
values(list).forEach((component) => {
if (component.defaultProps) {
reportInvalidDefaultProps(
component.declaredPropTypes,
component.defaultProps || {}
);
}
});
}
};
Expand Down
139 changes: 71 additions & 68 deletions lib/rules/destructuring-assignment.js
Expand Up @@ -13,6 +13,16 @@ const DEFAULT_OPTION = 'always';
function createSFCParams() {
const queue = [];

const identifyProps = (params) => {
const props = params[0];
return props && !props.destructuring && props.name;
};

const identifyContext = (params) => {
const context = params[1];
return context && !context.destructuring && context.name;
};

return {
push(params) {
queue.unshift(params);
Expand All @@ -21,17 +31,11 @@ function createSFCParams() {
queue.shift();
},
propsName() {
const found = queue.find((params) => {
const props = params[0];
return props && !props.destructuring && props.name;
});
const found = queue.find(identifyProps);
return found && found[0] && found[0].name;
},
contextName() {
const found = queue.find((params) => {
const context = params[1];
return context && !context.destructuring && context.name;
});
const found = queue.find(identifyContext);
return found && found[1] && found.name;
}
};
Expand Down Expand Up @@ -87,24 +91,26 @@ module.exports = {
* FunctionDeclaration, or FunctionExpression
*/
function handleStatelessComponent(node) {
const params = evalParams(node.params);

const SFCComponent = components.get(context.getScope(node).block);
if (!SFCComponent) {
return;
}
const params = evalParams(node.params);

sfcParams.push(params);

if (params[0] && params[0].destructuring && components.get(node) && configuration === 'never') {
context.report({
node,
messageId: 'noDestructPropsInSFCArg'
});
} else if (params[1] && params[1].destructuring && components.get(node) && configuration === 'never') {
context.report({
node,
messageId: 'noDestructContextInSFCArg'
});
if (configuration === 'never') {
if (params[0] && params[0].destructuring && components.get(node)) {
context.report({
node,
messageId: 'noDestructPropsInSFCArg'
});
} else if (params[1] && params[1].destructuring && components.get(node)) {
context.report({
node,
messageId: 'noDestructContextInSFCArg'
});
}
}
}

Expand All @@ -116,15 +122,11 @@ module.exports = {
}

function handleSFCUsage(node) {
const propsName = sfcParams.propsName();
const contextName = sfcParams.contextName();
// props.aProp || context.aProp
const isPropUsed = (
(propsName && node.object.name === propsName)
|| (contextName && node.object.name === contextName)
)
&& !isAssignmentLHS(node);
if (isPropUsed && configuration === 'always') {
const isPropUsed = node.object.name && (
node.object.name === sfcParams.propsName() || node.object.name === sfcParams.contextName()
) && !isAssignmentLHS(node);
if (isPropUsed) {
context.report({
node,
messageId: 'useDestructAssignment',
Expand Down Expand Up @@ -155,8 +157,7 @@ module.exports = {
);

if (
isPropUsed && configuration === 'always'
&& !(ignoreClassFields && isInClassProperty(node))
isPropUsed && !(ignoreClassFields && isInClassProperty(node))
) {
context.report({
node,
Expand All @@ -183,49 +184,51 @@ module.exports = {
'FunctionExpression:exit': handleStatelessComponentExit,

MemberExpression(node) {
const SFCComponent = components.get(context.getScope(node).block);
const classComponent = utils.getParentComponent(node);
if (SFCComponent) {
handleSFCUsage(node);
}
if (classComponent) {
handleClassUsage(node);
if (configuration === 'always') {
const SFCComponent = components.get(context.getScope(node).block);
const classComponent = utils.getParentComponent(node);
if (SFCComponent) {
handleSFCUsage(node);
}
if (classComponent) {
handleClassUsage(node);
}
}
},

VariableDeclarator(node) {
const classComponent = utils.getParentComponent(node);
const SFCComponent = components.get(context.getScope(node).block);

const destructuring = (node.init && node.id && node.id.type === 'ObjectPattern');
// let {foo} = props;
const destructuringSFC = destructuring && (node.init.name === 'props' || node.init.name === 'context');
// let {foo} = this.props;
const destructuringClass = destructuring && node.init.object && node.init.object.type === 'ThisExpression' && (
node.init.property.name === 'props' || node.init.property.name === 'context' || node.init.property.name === 'state'
);

if (SFCComponent && destructuringSFC && configuration === 'never') {
context.report({
node,
messageId: 'noDestructAssignment',
data: {
type: node.init.name
}
});
}
if (configuration === 'never') {
const destructuring = (node.init && node.id && node.id.type === 'ObjectPattern');
// let {foo} = props;
const destructuringSFC = destructuring && (node.init.name === 'props' || node.init.name === 'context');
// let {foo} = this.props;
const destructuringClass = destructuring && node.init.object && node.init.object.type === 'ThisExpression' && (
node.init.property.name === 'props' || node.init.property.name === 'context' || node.init.property.name === 'state'
);

if (destructuringSFC && components.get(context.getScope(node).block)) {
context.report({
node,
messageId: 'noDestructAssignment',
data: {
type: node.init.name
}
});
}

if (
classComponent && destructuringClass && configuration === 'never'
&& !(ignoreClassFields && node.parent.type === 'ClassProperty')
) {
context.report({
node,
messageId: 'noDestructAssignment',
data: {
type: node.init.property.name
}
});
if (
destructuringClass
&& !(ignoreClassFields && node.parent.type === 'ClassProperty')
&& utils.getParentComponent(node)
) {
context.report({
node,
messageId: 'noDestructAssignment',
data: {
type: node.init.property.name
}
});
}
}
}
};
Expand Down
14 changes: 9 additions & 5 deletions lib/rules/display-name.js
Expand Up @@ -43,14 +43,16 @@ module.exports = {
const config = context.options[0] || {};
const ignoreTranspilerName = config.ignoreTranspilerName || false;

const markFlag = {
hasDisplayName: true
};

/**
* Mark a prop type as declared
* @param {ASTNode} node The AST node being checked.
*/
function markDisplayNameAsDeclared(node) {
components.set(node, {
hasDisplayName: true
});
components.set(node, markFlag);
}

/**
Expand Down Expand Up @@ -230,9 +232,11 @@ module.exports = {
'Program:exit'() {
const list = components.list();
// Report missing display name for all components
values(list).filter((component) => !component.hasDisplayName)
values(list)
.forEach((component) => {
reportMissingDisplayName(component);
if (!component.hasDisplayName) {
reportMissingDisplayName(component);
}
});
}
};
Expand Down
5 changes: 3 additions & 2 deletions lib/rules/forbid-component-props.js
Expand Up @@ -83,8 +83,6 @@ module.exports = {
return {
JSXAttribute(node) {
const parentName = node.parent.name;
// Extract a component name when using a "namespace", e.g. `<AntdLayout.Content />`.
const tag = parentName.name || `${parentName.object.name}.${parentName.property.name}`;
const componentName = parentName.name || parentName.property.name;
if (componentName && typeof componentName[0] === 'string' && componentName[0] !== componentName[0].toUpperCase()) {
// This is a DOM node, not a Component, so exit.
Expand All @@ -93,6 +91,9 @@ module.exports = {

const prop = node.name.name;

// Extract a component name when using a "namespace", e.g. `<AntdLayout.Content />`.
const tag = parentName.name || `${parentName.object.name}.${parentName.property.name}`;

if (!isForbidden(prop, tag)) {
return;
}
Expand Down
6 changes: 2 additions & 4 deletions lib/rules/jsx-indent.js
Expand Up @@ -277,13 +277,11 @@ module.exports = {
*/
function checkNodesIndent(node, indent, excludeCommas) {
const nodeIndent = getNodeIndent(node, false, excludeCommas);
const isCorrectRightInLogicalExp = isRightInLogicalExp(node) && (nodeIndent - indent) === indentSize;
const isCorrectAlternateInCondExp = isAlternateInConditionalExp(node) && (nodeIndent - indent) === 0;
if (
nodeIndent !== indent
&& astUtil.isNodeFirstInLine(context, node)
&& !isCorrectRightInLogicalExp
&& !isCorrectAlternateInCondExp
&& !(isRightInLogicalExp(node) && (nodeIndent - indent) === indentSize)
&& !(isAlternateInConditionalExp(node) && (nodeIndent - indent) === 0)
) {
report(node, indent, nodeIndent);
}
Expand Down
4 changes: 3 additions & 1 deletion lib/rules/jsx-key.js
Expand Up @@ -19,6 +19,8 @@ const defaultOptions = {
checkKeyMustBeforeSpread: false
};

const hasReturnStatementType = (item) => item.type === 'ReturnStatement';

module.exports = {
meta: {
docs: {
Expand Down Expand Up @@ -78,7 +80,7 @@ module.exports = {
}

function getReturnStatement(body) {
return body.filter((item) => item.type === 'ReturnStatement')[0];
return body.find(hasReturnStatementType);
}

function isKeyAfterSpread(attributes) {
Expand Down

0 comments on commit 232d6a1

Please sign in to comment.