Files
SnowLuma/.pnpm-store/v11/files/97/87a813a7fc825772c128cef94f9c8a993cf795c9af0d90f8afd9a3bb53a85f0eec3b702889f79299a4e82e0413745c0524da63a3451e43383b686d27cc3db6
T
TanTan 2a69b23b47
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
docs: 仓库链接指向本镜像仓库
2026-11-13 09:27:07 +08:00

158 lines
7.1 KiB
Plaintext

import { X as isWhiteSpaces, f as createRule } from "../utils.js";
var jsx_one_expression_per_line_default = createRule({
name: "jsx-one-expression-per-line",
meta: {
type: "layout",
docs: { description: "Require one JSX element per line" },
fixable: "whitespace",
schema: [{
type: "object",
properties: { allow: {
type: "string",
enum: [
"none",
"literal",
"single-child",
"single-line",
"non-jsx"
]
} },
additionalProperties: false
}],
defaultOptions: [{ allow: "none" }],
messages: { moveToNewLine: "`{{descriptor}}` must be placed on a new line" }
},
create(context, [options]) {
const { allow } = options;
function nodeKey(node) {
return `${node.loc.start.line},${node.loc.start.column}`;
}
function nodeDescriptor(n) {
return "openingElement" in n && n.openingElement && "name" in n.openingElement.name ? String(n.openingElement.name.name) : context.sourceCode.getText(n).replace(/\n/g, "");
}
function report(node, fix) {
context.report({
messageId: "moveToNewLine",
node,
data: { descriptor: nodeDescriptor(node) },
fix
});
}
function handleJSX(node) {
const children = node.children;
if (!children || !children.length) return;
if (allow === "non-jsx" && !children.some((child) => child.type === "JSXFragment" || child.type === "JSXElement")) return;
const isFragment = node.type === "JSXFragment";
const openingElement = isFragment ? node.openingFragment : node.openingElement;
const closingElement = isFragment ? node.closingFragment : node.closingElement;
const openingElementStartLine = openingElement.loc.start.line;
const openingElementEndLine = openingElement.loc.end.line;
const closingElementStartLine = closingElement.loc.start.line;
const closingElementEndLine = closingElement.loc.end.line;
if (children.length === 1) {
const child = children[0];
if (openingElementStartLine === openingElementEndLine && openingElementEndLine === closingElementStartLine && closingElementStartLine === closingElementEndLine && closingElementEndLine === child.loc.start.line && child.loc.start.line === child.loc.end.line) {
if (allow === "single-child" || allow === "literal" && (child.type === "Literal" || child.type === "JSXText") || allow === "single-line") return;
}
}
if (allow === "single-line") {
const firstChild = children[0];
const lastChild = children[children.length - 1];
const lineDifference = lastChild.loc.end.line - firstChild.loc.start.line;
let lineBreaks = 0;
if (firstChild.type === "Literal" || firstChild.type === "JSXText") {
if (/^\s*?\n/.test(firstChild.raw)) lineBreaks += 1;
}
if (lastChild.type === "Literal" || lastChild.type === "JSXText") {
if (/\n\s*$/.test(lastChild.raw)) lineBreaks += 1;
}
if (lineDifference === 0 && lineBreaks === 0 || lineDifference === 2 && lineBreaks === 2) return;
}
const childrenGroupedByLine = {};
const fixDetailsByNode = {};
children.forEach((child) => {
let countNewLinesBeforeContent = 0;
let countNewLinesAfterContent = 0;
if (child.type === "Literal" || child.type === "JSXText") {
if (isWhiteSpaces(child.raw)) return;
countNewLinesBeforeContent = (child.raw.match(/^\s*\n/g) || []).length;
countNewLinesAfterContent = (child.raw.match(/\n\s*$/g) || []).length;
}
const startLine = child.loc.start.line + countNewLinesBeforeContent;
const endLine = child.loc.end.line - countNewLinesAfterContent;
if (startLine === endLine) {
if (!childrenGroupedByLine[startLine]) childrenGroupedByLine[startLine] = [];
childrenGroupedByLine[startLine].push(child);
} else {
if (!childrenGroupedByLine[startLine]) childrenGroupedByLine[startLine] = [];
childrenGroupedByLine[startLine].push(child);
if (!childrenGroupedByLine[endLine]) childrenGroupedByLine[endLine] = [];
childrenGroupedByLine[endLine].push(child);
}
});
const lines = Object.keys(childrenGroupedByLine);
if (lines.length === 1 && allow === "single-line") {
const line = parseInt(lines[0]);
const children = childrenGroupedByLine[line];
const firstChild = children[0];
if (line === openingElementEndLine) report(firstChild, (fixer) => fixer.insertTextBefore(firstChild, "\n"));
const lastChild = children.at(-1);
if (line === closingElementStartLine) report(lastChild, (fixer) => fixer.insertTextAfter(lastChild, "\n"));
} else {
lines.forEach((_line) => {
const line = parseInt(_line, 10);
const firstIndex = 0;
const lastIndex = childrenGroupedByLine[line].length - 1;
childrenGroupedByLine[line].forEach((child, i) => {
let prevChild;
let nextChild;
if (i === firstIndex) {
if (line === openingElementEndLine) prevChild = openingElement;
} else prevChild = childrenGroupedByLine[line][i - 1];
if (i === lastIndex) {
if (line === closingElementStartLine) nextChild = closingElement;
}
if (!prevChild && !nextChild) return;
const spaceBetweenPrev = () => {
const tokenBetweenNodes = context.sourceCode.getTokensBetween(prevChild, child)[0];
return (prevChild.type === "Literal" || prevChild.type === "JSXText") && prevChild.raw.endsWith(" ") || (child.type === "Literal" || child.type === "JSXText") && child.raw.startsWith(" ") || isWhiteSpaces(tokenBetweenNodes?.value);
};
const spaceBetweenNext = () => {
const tokenBetweenNodes = context.sourceCode.getTokensBetween(child, nextChild)[0];
return (nextChild.type === "Literal" || nextChild.type === "JSXText") && nextChild.raw.startsWith(" ") || (child.type === "Literal" || child.type === "JSXText") && child.raw.endsWith(" ") || isWhiteSpaces(tokenBetweenNodes?.value);
};
const source = context.sourceCode.getText(child);
const leadingSpace = !!(prevChild && spaceBetweenPrev());
const trailingSpace = !!(nextChild && spaceBetweenNext());
const leadingNewLine = !!prevChild;
const trailingNewLine = !!nextChild;
const key = nodeKey(child);
if (!fixDetailsByNode[key]) fixDetailsByNode[key] = {
node: child,
source
};
if (leadingSpace) fixDetailsByNode[key].leadingSpace = true;
if (leadingNewLine) fixDetailsByNode[key].leadingNewLine = true;
if (trailingNewLine) fixDetailsByNode[key].trailingNewLine = true;
if (trailingSpace) fixDetailsByNode[key].trailingSpace = true;
});
});
Object.keys(fixDetailsByNode).forEach((key) => {
const details = fixDetailsByNode[key];
const nodeToReport = details.node;
const source = details.source.replace(/(^ +| +$)/g, "");
const leadingSpaceString = details.leadingSpace ? "\n{' '}" : "";
const trailingSpaceString = details.trailingSpace ? "{' '}\n" : "";
const replaceText = `${leadingSpaceString}${details.leadingNewLine ? "\n" : ""}${source}${details.trailingNewLine ? "\n" : ""}${trailingSpaceString}`;
report(nodeToReport, (fixer) => fixer.replaceText(nodeToReport, replaceText));
});
}
}
return {
JSXElement: handleJSX,
JSXFragment: handleJSX
};
}
});
export { jsx_one_expression_per_line_default as t };