Dev Build / cleanup (push) Canceled after 0s
Dev Build / typecheck (push) Canceled after 0s
Dev Build / build (linux-arm64, ubuntu-24.04-arm, linux-arm64) (push) Canceled after 0s
Dev Build / build (linux-x64, ubuntu-latest, linux-x64) (push) Canceled after 0s
Dev Build / build (win-x64, windows-latest, win32-x64) (push) Canceled after 0s
Dev Build / dispatch-dev-docker (push) Skipped
301 lines
8.8 KiB
Plaintext
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 };
|