UNPKG

eslint-plugin-react-native-unistyles

Version:
178 lines (177 loc) 9.82 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.sortStyles = void 0; const utils_1 = require("@typescript-eslint/utils"); const stylesheet_1 = require("../util/stylesheet"); const createRule = utils_1.ESLintUtils.RuleCreator((name) => `https://github.com/RodSarhan/eslint-plugin-react-native-unistyles/blob/main/docs/rules/${name}.md`); exports.sortStyles = createRule({ name: 'sort-styles', meta: { docs: { description: 'Disallow unused styles' }, type: 'suggestion', messages: { expectedDifferentOrder: "Expected {{ type }} to be in {{ order }}ending order. '{{ currentName }}' should be before '{{ prevName }}'.", }, fixable: 'code', schema: [ { type: 'string', enum: ['asc', 'desc'] }, { type: 'object', properties: { ignoreStyleNames: { type: 'boolean' }, ignoreStyleProperties: { type: 'boolean' } }, additionalProperties: false, }, ], }, defaultOptions: ['asc', { ignoreStyleNames: false, ignoreStyleProperties: false }], create: (context) => { const order = context.options[0] || 'asc'; const options = context.options[1] || {}; const { ignoreStyleNames } = options; const { ignoreStyleProperties } = options; const isValidOrder = order === 'asc' ? (a, b) => a <= b : (a, b) => a >= b; const sourceCode = context.sourceCode; function sort(array) { const result = []; return result.concat(array).sort((a, b) => { const identifierA = stylesheet_1.stylesASTHelpers.getStylePropertyIdentifier(a); const identifierB = stylesheet_1.stylesASTHelpers.getStylePropertyIdentifier(b); let sortOrder = 0; if (identifierA && identifierB && stylesheet_1.stylesASTHelpers.isEitherShortHand(identifierA, identifierB)) { return a.range[0] - b.range[0]; } if (identifierA && identifierB) { if (identifierA < identifierB) { sortOrder = -1; } else if (identifierA > identifierB) { sortOrder = 1; } } return sortOrder * (order === 'asc' ? 1 : -1); }); } function report(args) { const { array, type, node, prev, current } = args; const currentName = stylesheet_1.stylesASTHelpers.getStylePropertyIdentifier(current); const prevName = stylesheet_1.stylesASTHelpers.getStylePropertyIdentifier(prev); const fixFunction = (fixer) => { // Create sorted array with properties and their associated comments const sortedArray = sort(array); // Build property-comment pairs for original and sorted arrays const originalPairs = array.map((prop) => ({ property: prop, comments: sourceCode.getCommentsBefore(prop), })); const sortedPairs = sortedArray.map((prop) => { const originalPair = originalPairs.find((pair) => pair.property === prop); return originalPair || { property: prop, comments: [] }; }); // Generate fixes by replacing each property with its sorted counterpart const fixes = []; for (let i = 0; i < array.length; i++) { const originalProp = array[i]; const sortedPair = sortedPairs[i]; if (!originalProp || !sortedPair) { continue; } if (originalProp !== sortedPair.property) { // Build replacement text with comments above the property let replacementText = ''; // Add comments above the property sortedPair.comments.forEach((comment) => { // preserve indentation by getting the original property's indentation const originalPropStartLine = sourceCode.getLocFromIndex(originalProp.range[0]).line; const lines = sourceCode.getLines(); const lineText = lines[originalPropStartLine - 1]; const indentation = lineText?.match(/^(\s*)/)?.[1] || ''; replacementText += sourceCode.getText(comment) + '\n' + indentation; }); // Add the property itself replacementText += sourceCode.getText(sortedPair.property); // Calculate the range to include comments above the original property const commentsBeforeOriginal = sourceCode.getCommentsBefore(originalProp); const startPos = commentsBeforeOriginal.length > 0 && commentsBeforeOriginal[0] ? commentsBeforeOriginal[0].range[0] : originalProp.range[0]; fixes.push(fixer.replaceTextRange([startPos, originalProp.range[1]], replacementText)); } } return fixes; }; context.report({ node, messageId: 'expectedDifferentOrder', data: { type, order, currentName, prevName }, loc: current.key.loc, fix: fixFunction, }); } function checkIsSorted(args) { const { array, type, node } = args; for (let i = 1; i < array.length; i += 1) { const prev = array[i - 1]; const current = array[i]; if (prev?.type !== utils_1.AST_NODE_TYPES.Property || current?.type !== utils_1.AST_NODE_TYPES.Property) { return; } const prevName = stylesheet_1.stylesASTHelpers.getStylePropertyIdentifier(prev); const currentName = stylesheet_1.stylesASTHelpers.getStylePropertyIdentifier(current); const oneIsShorthandForTheOther = type === 'style-properties' && prevName && currentName && stylesheet_1.stylesASTHelpers.isEitherShortHand(prevName, currentName); if (!oneIsShorthandForTheOther && prevName && currentName && !isValidOrder(prevName, currentName)) { return report({ array, type, node, prev, current }); } } } return { CallExpression: function (node) { if (!stylesheet_1.stylesASTHelpers.isStyleSheetDeclaration(node)) { return; } const styleDeclarationsChunks = stylesheet_1.stylesASTHelpers.getStyleDeclarationsChunks(node); if (!ignoreStyleNames) { styleDeclarationsChunks.forEach((styleDeclarationChunk) => { checkIsSorted({ array: styleDeclarationChunk, type: 'style-names', node }); }); } if (ignoreStyleProperties) return; styleDeclarationsChunks.forEach((styleDeclarationChunk) => { styleDeclarationChunk.forEach((styleChunck) => { let innerStyleProperties = []; if (styleChunck.value.type === utils_1.AST_NODE_TYPES.ObjectExpression) { innerStyleProperties = styleChunck.value.properties; } if (styleChunck.value.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) { if (styleChunck.value.body.type === utils_1.AST_NODE_TYPES.ObjectExpression) { innerStyleProperties = styleChunck.value.body.properties; } if (styleChunck.value.body.type === utils_1.AST_NODE_TYPES.BlockStatement) { const returnStatement = styleChunck.value.body.body.find((statement) => statement.type === utils_1.AST_NODE_TYPES.ReturnStatement); if (returnStatement?.argument?.type === utils_1.AST_NODE_TYPES.ObjectExpression) { innerStyleProperties = returnStatement.argument.properties; } } } if (styleChunck.value.type === utils_1.AST_NODE_TYPES.FunctionExpression) { const returnStatement = styleChunck.value.body.body.find((statement) => statement.type === utils_1.AST_NODE_TYPES.ReturnStatement); if (returnStatement?.argument?.type === utils_1.AST_NODE_TYPES.ObjectExpression) { innerStyleProperties = returnStatement.argument.properties; } } if (innerStyleProperties.length < 2) { return; } const stylePropertyChunks = stylesheet_1.stylesASTHelpers.getPropertiesChunks(innerStyleProperties); stylePropertyChunks.forEach((stylePropertyChunk) => { checkIsSorted({ array: stylePropertyChunk, type: 'style-properties', node }); }); return; }); }); }, }; }, });