Skip to content

Commit

Permalink
[New] named: allow validating named imports
Browse files Browse the repository at this point in the history
  • Loading branch information
manuth committed Aug 30, 2024
1 parent ee1ea02 commit 60b0493
Showing 1 changed file with 121 additions and 30 deletions.
151 changes: 121 additions & 30 deletions src/rules/order.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,58 +198,105 @@ function makeImportDescription(node) {
return 'import';
}

function fixOutOfOrder(context, firstNode, secondNode, order) {
function fixOutOfOrder(context, firstNode, secondNode, order, named) {
const sourceCode = context.getSourceCode();

const firstRoot = findRootNode(firstNode.node);
const {
firstRoot,
secondRoot,
} = named ? {
firstRoot: firstNode.node,
secondRoot: secondNode.node,
} : {
firstRoot: findRootNode(firstNode.node),
secondRoot: findRootNode(secondNode.node),
};

const firstRootStart = findStartOfLineWithComments(sourceCode, firstRoot);
const firstRootEnd = findEndOfLineWithComments(sourceCode, firstRoot);

const secondRoot = findRootNode(secondNode.node);
const secondRootStart = findStartOfLineWithComments(sourceCode, secondRoot);
const secondRootEnd = findEndOfLineWithComments(sourceCode, secondRoot);
const canFix = canReorderItems(firstRoot, secondRoot);

let newCode = sourceCode.text.substring(secondRootStart, secondRootEnd);
if (newCode[newCode.length - 1] !== '\n') {
newCode = `${newCode}\n`;
if (firstNode.displayName === secondNode.displayName) {
for (const node of [firstNode, secondNode]) {
if (node.alias) {
node.displayName = `${node.displayName} as ${node.alias}`;
}
}
}

const firstImport = `${makeImportDescription(firstNode)} of \`${firstNode.displayName}\``;
const secondImport = `\`${secondNode.displayName}\` ${makeImportDescription(secondNode)}`;
const message = `${secondImport} should occur ${order} ${firstImport}`;

if (order === 'before') {
context.report({
node: secondNode.node,
message,
fix: canFix && ((fixer) => fixer.replaceTextRange(
[firstRootStart, secondRootEnd],
newCode + sourceCode.text.substring(firstRootStart, secondRootStart),
)),
});
} else if (order === 'after') {
context.report({
node: secondNode.node,
message,
fix: canFix && ((fixer) => fixer.replaceTextRange(
[secondRootStart, firstRootEnd],
sourceCode.text.substring(secondRootEnd, firstRootEnd) + newCode,
)),
});
if (named) {
const firstCode = sourceCode.text.substring(firstRootStart, firstRootEnd);
const secondCode = sourceCode.text.substring(secondRootStart, secondRootEnd);

if (order === 'before') {
const trimmed = secondCode.trimEnd();
const gapCode = sourceCode.text.substring(firstRootEnd, secondRootStart).replace(/,$/, '');
const whiespaces = secondCode.substring(trimmed.length);
context.report({
node: secondNode.node,
message,
fix: (fixer) => fixer.replaceTextRange(
[firstRootStart, secondRootEnd],
`${trimmed},${firstCode}${gapCode}${whiespaces}`),
});
} else if (order === 'after') {
const trimmed = firstCode.trimEnd();
const gapCode = sourceCode.text.substring(secondRootEnd, firstRootStart).replace(/^,/, '');
const whiespaces = firstCode.substring(trimmed.length);
context.report({
node: secondNode.node,
message,
fix: (fixes) => fixes.replaceTextRange(
[secondRootStart, firstRootEnd],
`${gapCode}${trimmed},${secondCode}${whiespaces}`),
});
}
} else {
const canFix = canReorderItems(firstRoot, secondRoot);
let newCode = sourceCode.text.substring(secondRootStart, secondRootEnd);

if (newCode[newCode.length - 1] !== '\n') {
newCode = `${newCode}\n`;
}

if (order === 'before') {
context.report({
node: secondNode.node,
message,
fix: canFix && ((fixer) => fixer.replaceTextRange(
[firstRootStart, secondRootEnd],
newCode + sourceCode.text.substring(firstRootStart, secondRootStart),
)),
});
} else if (order === 'after') {
context.report({
node: secondNode.node,
message,
fix: canFix && ((fixer) => fixer.replaceTextRange(
[secondRootStart, firstRootEnd],
sourceCode.text.substring(secondRootEnd, firstRootEnd) + newCode,
)),
});
}
}
}

function reportOutOfOrder(context, imported, outOfOrder, order) {
function reportOutOfOrder(context, imported, outOfOrder, order, named) {
outOfOrder.forEach(function (imp) {
const found = imported.find(function hasHigherRank(importedItem) {
return importedItem.rank > imp.rank;
});
fixOutOfOrder(context, found, imp, order);
fixOutOfOrder(context, found, imp, order, named);
});
}

function makeOutOfOrderReport(context, imported) {
function makeOutOfOrderReport(context, imported, named) {
const outOfOrder = findOutOfOrder(imported);
if (!outOfOrder.length) {
return;
Expand All @@ -259,10 +306,10 @@ function makeOutOfOrderReport(context, imported) {
const reversedImported = reverse(imported);
const reversedOrder = findOutOfOrder(reversedImported);
if (reversedOrder.length < outOfOrder.length) {
reportOutOfOrder(context, reversedImported, reversedOrder, 'after');
reportOutOfOrder(context, reversedImported, reversedOrder, 'after', named);
return;
}
reportOutOfOrder(context, imported, outOfOrder, 'before');
reportOutOfOrder(context, imported, outOfOrder, 'before', named);
}

const compareString = (a, b) => {
Expand Down Expand Up @@ -632,6 +679,10 @@ module.exports = {
'never',
],
},
named: {
type: 'boolean',
default: false,
},
alphabetize: {
type: 'object',
properties: {
Expand Down Expand Up @@ -664,6 +715,7 @@ module.exports = {
const options = context.options[0] || {};
const newlinesBetweenImports = options['newlines-between'] || 'ignore';
const pathGroupsExcludedImportTypes = new Set(options.pathGroupsExcludedImportTypes || ['builtin', 'external', 'object']);
const named = options.named || false;
const alphabetize = getAlphabetizeConfig(options);
const distinctGroup = options.distinctGroup == null ? defaultDistinctGroup : !!options.distinctGroup;
let ranks;
Expand Down Expand Up @@ -694,6 +746,24 @@ module.exports = {
return importMap.get(node);
}

function makeNamedOrderReport(context, namedImports) {
if (namedImports.length > 1) {
const imports = namedImports.map(
(namedImport) => ({
displayName: namedImport.value,
type: 'named',
rank: 0,
...namedImport,
}));

if (alphabetize.order !== 'ignore') {
mutateRanksToAlphabetize(imports, alphabetize);
}

makeOutOfOrderReport(context, imports, true);
}
}

return {
ImportDeclaration: function handleImports(node) {
// Ignoring unassigned imports unless warnOnUnassignedImports is set
Expand All @@ -711,6 +781,17 @@ module.exports = {
getBlockImports(node.parent),
pathGroupsExcludedImportTypes,
);

if (named) {
makeNamedOrderReport(
context,
node.specifiers.filter(
(specifier) => specifier.type === 'ImportSpecifier').map(
(specifier) => ({
node: specifier,
value: specifier.imported.name,
})));
}
}
},
TSImportEqualsDeclaration: function handleImports(node) {
Expand Down Expand Up @@ -765,6 +846,16 @@ module.exports = {
pathGroupsExcludedImportTypes,
);
},
...named ? {
VariableDeclarator: function orderRequireNames(node) {
if (node.id.type === 'ObjectPattern' && isRequireExpression(node.init)) {
makeNamedOrderReport(
context,
node.id.properties.map((prop) => ({ node: prop, value: prop.key.name })),
);
}
},
} : {},
'Program:exit': function reportAndReset() {
importMap.forEach((imported) => {
if (newlinesBetweenImports !== 'ignore') {
Expand Down

0 comments on commit 60b0493

Please sign in to comment.