Files
SnowLuma/.pnpm-store/v11/files/43/5b7a39500dd6b8dc2f9d480632b020621b414a75f00ead0ef19c607be337b6a9cdf843f91986a6f3311b79a839a4e8b331c1737ca76097af11ea1077390bde
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

301 lines
8.8 KiB
Plaintext

import { O as getNextLocation, f as createRule, g as import_ast_utils, m as AST_NODE_TYPES } from "../utils.js";
const OPTION_VALUE_SCHEME = [
"always-multiline",
"always",
"never",
"only-multiline"
];
var comma_dangle_default = createRule({
name: "comma-dangle",
meta: {
type: "layout",
docs: { description: "Require or disallow trailing commas" },
fixable: "code",
schema: {
$defs: {
value: {
type: "string",
enum: OPTION_VALUE_SCHEME
},
valueWithIgnore: {
type: "string",
enum: [...OPTION_VALUE_SCHEME, "ignore"]
}
},
type: "array",
items: [{ oneOf: [{ $ref: "#/$defs/value" }, {
type: "object",
properties: {
arrays: { $ref: "#/$defs/valueWithIgnore" },
objects: { $ref: "#/$defs/valueWithIgnore" },
imports: { $ref: "#/$defs/valueWithIgnore" },
exports: { $ref: "#/$defs/valueWithIgnore" },
functions: { $ref: "#/$defs/valueWithIgnore" },
importAttributes: { $ref: "#/$defs/valueWithIgnore" },
dynamicImports: { $ref: "#/$defs/valueWithIgnore" },
enums: { $ref: "#/$defs/valueWithIgnore" },
generics: { $ref: "#/$defs/valueWithIgnore" },
tuples: { $ref: "#/$defs/valueWithIgnore" }
},
additionalProperties: false
}] }],
additionalItems: false
},
defaultOptions: ["never"],
messages: {
unexpected: "Unexpected trailing comma.",
missing: "Missing trailing comma."
}
},
create(context, [options]) {
function normalizeOptions(options = {}, ecmaVersion) {
const DEFAULT_OPTION_VALUE = "never";
if (typeof options === "string") return {
arrays: options,
objects: options,
imports: options,
exports: options,
functions: !ecmaVersion || ecmaVersion === "latest" ? options : ecmaVersion < 2017 ? "ignore" : options,
importAttributes: options,
dynamicImports: !ecmaVersion || ecmaVersion === "latest" ? options : ecmaVersion < 2025 ? "ignore" : options,
enums: options,
generics: options,
tuples: options
};
return {
arrays: options.arrays ?? DEFAULT_OPTION_VALUE,
objects: options.objects ?? DEFAULT_OPTION_VALUE,
imports: options.imports ?? DEFAULT_OPTION_VALUE,
exports: options.exports ?? DEFAULT_OPTION_VALUE,
functions: options.functions ?? DEFAULT_OPTION_VALUE,
importAttributes: options.importAttributes ?? DEFAULT_OPTION_VALUE,
dynamicImports: options.dynamicImports ?? DEFAULT_OPTION_VALUE,
enums: options.enums ?? DEFAULT_OPTION_VALUE,
generics: options.generics ?? DEFAULT_OPTION_VALUE,
tuples: options.tuples ?? DEFAULT_OPTION_VALUE
};
}
const normalizedOptions = normalizeOptions(options, context.languageOptions?.ecmaVersion ?? context.parserOptions?.ecmaVersion);
const isTSX = (context.languageOptions?.parserOptions?.ecmaFeatures?.jsx ?? context.parserOptions?.ecmaFeatures?.jsx) && context.filename?.endsWith(".tsx");
const sourceCode = context.sourceCode;
const closeBraces = [
"}",
"]",
")",
">"
];
const predicate = {
"always": forceTrailingComma,
"always-multiline": forceTrailingCommaIfMultiline,
"only-multiline": allowTrailingCommaIfMultiline,
"never": forbidTrailingComma,
"ignore": () => {}
};
function last(nodes) {
if (!nodes) return null;
return nodes[nodes.length - 1] ?? null;
}
function getTrailingToken(info) {
switch (info.node.type) {
case "ObjectExpression":
case "ArrayExpression":
case "CallExpression":
case "NewExpression":
case "ImportExpression": return sourceCode.getLastToken(info.node, 1);
default: {
const lastItem = info.lastItem;
if (!lastItem) return null;
const nextToken = sourceCode.getTokenAfter(lastItem);
if ((0, import_ast_utils.isCommaToken)(nextToken)) return nextToken;
return sourceCode.getLastToken(lastItem);
}
}
}
function isMultiline(info) {
if (!info.lastItem) return false;
const penultimateToken = getTrailingToken(info);
if (!penultimateToken) return false;
const lastToken = sourceCode.getTokenAfter(penultimateToken);
if (!lastToken) return false;
return lastToken.loc.end.line !== penultimateToken.loc.end.line;
}
function isTrailingCommaAllowed(lastItem) {
return lastItem.type !== "RestElement";
}
function forbidTrailingComma(info) {
if (isTSX && info.node.type === AST_NODE_TYPES.TSTypeParameterDeclaration && info.node.params.length === 1) return;
const lastItem = info.lastItem;
if (!lastItem) return;
const trailingToken = getTrailingToken(info);
if (trailingToken && (0, import_ast_utils.isCommaToken)(trailingToken)) context.report({
node: lastItem,
loc: trailingToken.loc,
messageId: "unexpected",
*fix(fixer) {
yield fixer.remove(trailingToken);
yield fixer.insertTextBefore(sourceCode.getTokenBefore(trailingToken), "");
yield fixer.insertTextAfter(sourceCode.getTokenAfter(trailingToken), "");
}
});
}
function forceTrailingComma(info) {
const lastItem = info.lastItem;
if (!lastItem) return;
if (!isTrailingCommaAllowed(lastItem)) {
forbidTrailingComma(info);
return;
}
const trailingToken = getTrailingToken(info);
if (!trailingToken || trailingToken.value === ",") return;
const nextToken = sourceCode.getTokenAfter(trailingToken);
if (!nextToken || !closeBraces.includes(nextToken.value)) return;
context.report({
node: lastItem,
loc: {
start: trailingToken.loc.end,
end: getNextLocation(sourceCode, trailingToken.loc.end)
},
messageId: "missing",
*fix(fixer) {
yield fixer.insertTextAfter(trailingToken, ",");
yield fixer.insertTextBefore(trailingToken, "");
yield fixer.insertTextAfter(sourceCode.getTokenAfter(trailingToken), "");
}
});
}
function allowTrailingCommaIfMultiline(info) {
if (!isMultiline(info)) forbidTrailingComma(info);
}
function forceTrailingCommaIfMultiline(info) {
if (isMultiline(info)) forceTrailingComma(info);
else forbidTrailingComma(info);
}
return {
ObjectExpression: (node) => {
predicate[normalizedOptions.objects]({
node,
lastItem: last(node.properties)
});
},
ObjectPattern: (node) => {
predicate[normalizedOptions.objects]({
node,
lastItem: last(node.properties)
});
},
ArrayExpression: (node) => {
predicate[normalizedOptions.arrays]({
node,
lastItem: last(node.elements)
});
},
ArrayPattern: (node) => {
predicate[normalizedOptions.arrays]({
node,
lastItem: last(node.elements)
});
},
ImportDeclaration: (node) => {
const lastSpecifier = last(node.specifiers);
if (lastSpecifier?.type === "ImportSpecifier") predicate[normalizedOptions.imports]({
node,
lastItem: lastSpecifier
});
predicate[normalizedOptions.importAttributes]({
node,
lastItem: last(node.attributes)
});
},
ExportNamedDeclaration: (node) => {
predicate[normalizedOptions.exports]({
node,
lastItem: last(node.specifiers)
});
predicate[normalizedOptions.importAttributes]({
node,
lastItem: last(node.attributes)
});
},
ExportAllDeclaration: (node) => {
predicate[normalizedOptions.importAttributes]({
node,
lastItem: last(node.attributes)
});
},
FunctionDeclaration: (node) => {
predicate[normalizedOptions.functions]({
node,
lastItem: last(node.params)
});
},
FunctionExpression: (node) => {
predicate[normalizedOptions.functions]({
node,
lastItem: last(node.params)
});
},
ArrowFunctionExpression: (node) => {
predicate[normalizedOptions.functions]({
node,
lastItem: last(node.params)
});
},
CallExpression: (node) => {
predicate[normalizedOptions.functions]({
node,
lastItem: last(node.arguments)
});
},
NewExpression: (node) => {
predicate[normalizedOptions.functions]({
node,
lastItem: last(node.arguments)
});
},
ImportExpression: (node) => {
predicate[normalizedOptions.dynamicImports]({
node,
lastItem: node.options ?? node.source
});
},
TSEnumDeclaration(node) {
predicate[normalizedOptions.enums]({
node,
lastItem: last(node.body?.members ?? node.members)
});
},
TSTypeParameterDeclaration(node) {
predicate[normalizedOptions.generics]({
node,
lastItem: last(node.params)
});
},
TSTypeParameterInstantiation(node) {
predicate.never({
node,
lastItem: last(node.params)
});
},
TSTupleType(node) {
predicate[normalizedOptions.tuples]({
node,
lastItem: last(node.elementTypes)
});
},
TSDeclareFunction(node) {
predicate[normalizedOptions.functions]({
node,
lastItem: last(node.params)
});
},
TSFunctionType(node) {
predicate[normalizedOptions.functions]({
node,
lastItem: last(node.params)
});
}
};
}
});
export { comma_dangle_default as t };