prettier-plugin-imports
Version:
A prettier plugins to sort imports in provided RegEx order
112 lines (98 loc) • 3.39 kB
text/typescript
import { newLineNode, THIRD_PARTY_MODULES_SPECIAL_WORD } from '../constants';
import { getImportNodesMatchedGroup } from './get-import-nodes-matched-group';
import { getSortedImportSpecifiers } from './get-sorted-import-specifiers';
import { getSortedNodesGroup } from './get-sorted-nodes-group';
import {
isCustomGroupSeparator,
testingOnly,
} from './normalize-plugin-options';
import type {
GetSortedNodesByImportOrder,
ImportGroups,
ImportOrLine,
} from '../types';
/**
* This function returns the given nodes, sorted in the order as indicated by
* the importOrder array from the given options. The plugin considers these
* import nodes as local import declarations.
*
* @param originalNodes A subset (of all import nodes) that should be sorted.
* @param options Options to influence the behavior of the sorting algorithm.
*/
export const getSortedNodesByImportOrder: GetSortedNodesByImportOrder = (
originalNodes,
{ importOrder, importOrderCaseSensitive },
) => {
if (
process.env.NODE_ENV === 'test' &&
JSON.stringify(importOrder) !==
JSON.stringify(testingOnly.normalizeImportOrderOption(importOrder))
) {
throw new Error(
'API Misuse: getSortedNodesByImportOrder::importOrder option already should be normalized.',
);
}
const finalNodes: ImportOrLine[] = [];
const importOrderGroups = importOrder.reduce<ImportGroups>(
(groups, regexp) =>
// Don't create a new group for explicit import separators
isCustomGroupSeparator(regexp)
? groups
: {
...groups,
[regexp]: [],
},
{},
);
// Select just the SPECIAL WORDS and the matchers
const sanitizedImportOrder = importOrder.filter(
(group) =>
!isCustomGroupSeparator(group) &&
group !== THIRD_PARTY_MODULES_SPECIAL_WORD,
);
// Assign import nodes into import order groups
for (const node of originalNodes) {
const matchedGroupName = getImportNodesMatchedGroup(
node,
sanitizedImportOrder,
);
const matchedGroup = importOrderGroups[matchedGroupName];
if (matchedGroup) {
matchedGroup.push(node);
} else {
throw new Error(
`Could not find a matching group in importOrder for: "${node.source.value}" on line ${node.source.loc?.start.line}.${node.importKind === 'type' ? ' Did you forget to include "<TYPES>"?' : ''}`,
);
}
}
for (const group of importOrder) {
// If it's a custom separator, all we need to do is add a newline
if (isCustomGroupSeparator(group)) {
const lastNode = finalNodes[finalNodes.length - 1];
// Avoid empty new line if first group is empty
if (!lastNode) {
continue;
}
// Don't add multiple newlines
if (isNodeANewline(lastNode)) {
continue;
}
finalNodes.push(newLineNode);
continue;
}
const groupNodes = importOrderGroups[group];
if (groupNodes.length === 0) continue;
const sortedInsideGroup = getSortedNodesGroup(groupNodes, {
importOrderCaseSensitive,
});
// Sort the import specifiers
sortedInsideGroup.forEach((node) =>
getSortedImportSpecifiers(node, { importOrderCaseSensitive }),
);
finalNodes.push(...sortedInsideGroup);
}
return finalNodes;
};
function isNodeANewline(node: ImportOrLine) {
return node.type === 'ExpressionStatement';
}