@stylistic/eslint-plugin-ts
Advanced tools
Comparing version 2.10.1 to 2.11.0
@@ -31,2 +31,4 @@ | ||
functions?: ValueWithIgnore | ||
importAttributes?: ValueWithIgnore | ||
dynamicImports?: ValueWithIgnore | ||
enums?: ValueWithIgnore | ||
@@ -67,2 +69,6 @@ generics?: ValueWithIgnore | ||
allowNewlines?: boolean | ||
optionalChain?: { | ||
before?: boolean | ||
after?: boolean | ||
} | ||
}, | ||
@@ -69,0 +75,0 @@ ] |
@@ -14,3 +14,5 @@ 'use strict'; | ||
exports: "never", | ||
functions: "never" | ||
functions: "never", | ||
importAttributes: "never", | ||
dynamicImports: "never" | ||
}); | ||
@@ -28,3 +30,5 @@ const closeBraces = ["}", "]", ")", ">"]; | ||
exports: optionValue, | ||
functions: !ecmaVersion || ecmaVersion === "latest" ? optionValue : ecmaVersion < 2017 ? "ignore" : optionValue | ||
functions: !ecmaVersion || ecmaVersion === "latest" ? optionValue : ecmaVersion < 2017 ? "ignore" : optionValue, | ||
importAttributes: optionValue, | ||
dynamicImports: !ecmaVersion || ecmaVersion === "latest" ? optionValue : ecmaVersion < 2025 ? "ignore" : optionValue | ||
}; | ||
@@ -38,3 +42,5 @@ } | ||
exports: optionValue.exports || DEFAULT_OPTIONS.exports, | ||
functions: optionValue.functions || DEFAULT_OPTIONS.functions | ||
functions: optionValue.functions || DEFAULT_OPTIONS.functions, | ||
importAttributes: optionValue.importAttributes || DEFAULT_OPTIONS.importAttributes, | ||
dynamicImports: optionValue.dynamicImports || DEFAULT_OPTIONS.dynamicImports | ||
}; | ||
@@ -89,3 +95,5 @@ } | ||
exports: { $ref: "#/definitions/valueWithIgnore" }, | ||
functions: { $ref: "#/definitions/valueWithIgnore" } | ||
functions: { $ref: "#/definitions/valueWithIgnore" }, | ||
importAttributes: { $ref: "#/definitions/valueWithIgnore" }, | ||
dynamicImports: { $ref: "#/definitions/valueWithIgnore" } | ||
}, | ||
@@ -108,29 +116,9 @@ additionalProperties: false | ||
const sourceCode = context.sourceCode; | ||
function getLastItem(node) { | ||
function last(array) { | ||
return array[array.length - 1]; | ||
} | ||
switch (node.type) { | ||
case "ObjectExpression": | ||
case "ObjectPattern": | ||
return last(node.properties); | ||
case "ArrayExpression": | ||
case "ArrayPattern": | ||
return last(node.elements); | ||
case "ImportDeclaration": | ||
case "ExportNamedDeclaration": | ||
return last(node.specifiers); | ||
case "FunctionDeclaration": | ||
case "FunctionExpression": | ||
case "ArrowFunctionExpression": | ||
return last(node.params); | ||
case "CallExpression": | ||
case "NewExpression": | ||
return last(node.arguments); | ||
default: | ||
return null; | ||
} | ||
function last(array) { | ||
if (!array) | ||
return null; | ||
return array[array.length - 1] ?? null; | ||
} | ||
function getTrailingToken(node, lastItem) { | ||
switch (node.type) { | ||
function getTrailingToken(info) { | ||
switch (info.node.type) { | ||
case "ObjectExpression": | ||
@@ -140,4 +128,8 @@ case "ArrayExpression": | ||
case "NewExpression": | ||
return sourceCode.getLastToken(node, 1); | ||
case "ImportExpression": | ||
return sourceCode.getLastToken(info.node, 1); | ||
default: { | ||
const lastItem = info.lastItem; | ||
if (!lastItem) | ||
return null; | ||
const nextToken = sourceCode.getTokenAfter(lastItem); | ||
@@ -150,7 +142,7 @@ if (utils.isCommaToken(nextToken)) | ||
} | ||
function isMultiline(node) { | ||
const lastItem = getLastItem(node); | ||
function isMultiline(info) { | ||
const lastItem = info.lastItem; | ||
if (!lastItem) | ||
return false; | ||
const penultimateToken = getTrailingToken(node, lastItem); | ||
const penultimateToken = getTrailingToken(info); | ||
if (!penultimateToken) | ||
@@ -163,7 +155,7 @@ return false; | ||
} | ||
function forbidTrailingComma(node) { | ||
const lastItem = getLastItem(node); | ||
if (!lastItem || node.type === "ImportDeclaration" && lastItem.type !== "ImportSpecifier") | ||
function forbidTrailingComma(info) { | ||
const lastItem = info.lastItem; | ||
if (!lastItem) | ||
return; | ||
const trailingToken = getTrailingToken(node, lastItem); | ||
const trailingToken = getTrailingToken(info); | ||
if (trailingToken && utils.isCommaToken(trailingToken)) { | ||
@@ -182,11 +174,11 @@ context.report({ | ||
} | ||
function forceTrailingComma(node) { | ||
const lastItem = getLastItem(node); | ||
if (!lastItem || node.type === "ImportDeclaration" && lastItem.type !== "ImportSpecifier") | ||
function forceTrailingComma(info) { | ||
const lastItem = info.lastItem; | ||
if (!lastItem) | ||
return; | ||
if (!isTrailingCommaAllowed(lastItem)) { | ||
forbidTrailingComma(node); | ||
forbidTrailingComma(info); | ||
return; | ||
} | ||
const trailingToken = getTrailingToken(node, lastItem); | ||
const trailingToken = getTrailingToken(info); | ||
if (!trailingToken || trailingToken.value === ",") | ||
@@ -211,11 +203,11 @@ return; | ||
} | ||
function forceTrailingCommaIfMultiline(node) { | ||
if (isMultiline(node)) | ||
forceTrailingComma(node); | ||
function forceTrailingCommaIfMultiline(info) { | ||
if (isMultiline(info)) | ||
forceTrailingComma(info); | ||
else | ||
forbidTrailingComma(node); | ||
forbidTrailingComma(info); | ||
} | ||
function allowTrailingCommaIfMultiline(node) { | ||
if (!isMultiline(node)) | ||
forbidTrailingComma(node); | ||
function allowTrailingCommaIfMultiline(info) { | ||
if (!isMultiline(info)) | ||
forbidTrailingComma(info); | ||
} | ||
@@ -231,13 +223,91 @@ const predicate = { | ||
return { | ||
ObjectExpression: predicate[options.objects], | ||
ObjectPattern: predicate[options.objects], | ||
ArrayExpression: predicate[options.arrays], | ||
ArrayPattern: predicate[options.arrays], | ||
ImportDeclaration: predicate[options.imports], | ||
ExportNamedDeclaration: predicate[options.exports], | ||
FunctionDeclaration: predicate[options.functions], | ||
FunctionExpression: predicate[options.functions], | ||
ArrowFunctionExpression: predicate[options.functions], | ||
CallExpression: predicate[options.functions], | ||
NewExpression: predicate[options.functions] | ||
ObjectExpression: (node) => { | ||
predicate[options.objects]({ | ||
node, | ||
lastItem: last(node.properties) | ||
}); | ||
}, | ||
ObjectPattern: (node) => { | ||
predicate[options.objects]({ | ||
node, | ||
lastItem: last(node.properties) | ||
}); | ||
}, | ||
ArrayExpression: (node) => { | ||
predicate[options.arrays]({ | ||
node, | ||
lastItem: last(node.elements) | ||
}); | ||
}, | ||
ArrayPattern: (node) => { | ||
predicate[options.arrays]({ | ||
node, | ||
lastItem: last(node.elements) | ||
}); | ||
}, | ||
ImportDeclaration: (node) => { | ||
const lastSpecifier = last(node.specifiers); | ||
if (lastSpecifier?.type === "ImportSpecifier") { | ||
predicate[options.imports]({ | ||
node, | ||
lastItem: lastSpecifier | ||
}); | ||
} | ||
predicate[options.importAttributes]({ | ||
node, | ||
lastItem: last(node.attributes) | ||
}); | ||
}, | ||
ExportNamedDeclaration: (node) => { | ||
predicate[options.exports]({ | ||
node, | ||
lastItem: last(node.specifiers) | ||
}); | ||
predicate[options.importAttributes]({ | ||
node, | ||
lastItem: last(node.attributes) | ||
}); | ||
}, | ||
ExportAllDeclaration: (node) => { | ||
predicate[options.importAttributes]({ | ||
node, | ||
lastItem: last(node.attributes) | ||
}); | ||
}, | ||
FunctionDeclaration: (node) => { | ||
predicate[options.functions]({ | ||
node, | ||
lastItem: last(node.params) | ||
}); | ||
}, | ||
FunctionExpression: (node) => { | ||
predicate[options.functions]({ | ||
node, | ||
lastItem: last(node.params) | ||
}); | ||
}, | ||
ArrowFunctionExpression: (node) => { | ||
predicate[options.functions]({ | ||
node, | ||
lastItem: last(node.params) | ||
}); | ||
}, | ||
CallExpression: (node) => { | ||
predicate[options.functions]({ | ||
node, | ||
lastItem: last(node.arguments) | ||
}); | ||
}, | ||
NewExpression: (node) => { | ||
predicate[options.functions]({ | ||
node, | ||
lastItem: last(node.arguments) | ||
}); | ||
}, | ||
ImportExpression: (node) => { | ||
predicate[options.dynamicImports]({ | ||
node, | ||
lastItem: node.options ?? node.source | ||
}); | ||
} | ||
}; | ||
@@ -303,2 +373,4 @@ } | ||
functions: { $ref: "#/$defs/valueWithIgnore" }, | ||
importAttributes: { $ref: "#/$defs/valueWithIgnore" }, | ||
dynamicImports: { $ref: "#/$defs/valueWithIgnore" }, | ||
enums: { $ref: "#/$defs/valueWithIgnore" }, | ||
@@ -305,0 +377,0 @@ generics: { $ref: "#/$defs/valueWithIgnore" }, |
@@ -41,3 +41,18 @@ 'use strict'; | ||
allowNewlines: { | ||
type: "boolean" | ||
type: "boolean", | ||
default: false | ||
}, | ||
optionalChain: { | ||
type: "object", | ||
properties: { | ||
before: { | ||
type: "boolean", | ||
default: true | ||
}, | ||
after: { | ||
type: "boolean", | ||
default: true | ||
} | ||
}, | ||
additionalProperties: false | ||
} | ||
@@ -63,2 +78,3 @@ }, | ||
const text = sourceCode.getText(); | ||
const { allowNewlines = false, optionalChain = { before: true, after: true } } = config; | ||
function checkSpacing(node, leftToken, rightToken) { | ||
@@ -95,7 +111,28 @@ const isOptionalCall = astUtils.isOptionalCallExpression(node); | ||
} else if (isOptionalCall) { | ||
if (hasWhitespace || hasNewline) { | ||
const { before: beforeOptionChain = true, after: afterOptionChain = true } = optionalChain; | ||
const hasPrefixSpace = /^\s/u.test(textBetweenTokens); | ||
const hasSuffixSpace = /\s$/u.test(textBetweenTokens); | ||
const hasCorrectPrefixSpace = beforeOptionChain ? hasPrefixSpace : !hasPrefixSpace; | ||
const hasCorrectSuffixSpace = afterOptionChain ? hasSuffixSpace : !hasSuffixSpace; | ||
const hasCorrectNewline = allowNewlines || !hasNewline; | ||
if (!hasCorrectPrefixSpace || !hasCorrectSuffixSpace || !hasCorrectNewline) { | ||
const messageId = !hasCorrectNewline ? "unexpectedNewline" : !beforeOptionChain && hasPrefixSpace || !afterOptionChain && hasSuffixSpace ? "unexpectedWhitespace" : "missing"; | ||
context.report({ | ||
node, | ||
loc: leftToken.loc.start, | ||
messageId: "unexpectedWhitespace" | ||
messageId, | ||
fix(fixer) { | ||
if (sourceCode.commentsExistBetween(leftToken, rightToken)) | ||
return null; | ||
let text2 = textBetweenTokens; | ||
if (!allowNewlines) { | ||
const GLOBAL_LINEBREAK_MATCHER = new RegExp(astUtils.LINEBREAK_MATCHER.source, "g"); | ||
text2 = text2.replaceAll(GLOBAL_LINEBREAK_MATCHER, " "); | ||
} | ||
if (!hasCorrectPrefixSpace) | ||
text2 = beforeOptionChain ? ` ${text2}` : text2.trimStart(); | ||
if (!hasCorrectSuffixSpace) | ||
text2 = afterOptionChain ? `${text2} ` : text2.trimEnd(); | ||
return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], text2); | ||
} | ||
}); | ||
@@ -113,3 +150,3 @@ } | ||
}); | ||
} else if (!config.allowNewlines && hasNewline) { | ||
} else if (!allowNewlines && hasNewline) { | ||
context.report({ | ||
@@ -116,0 +153,0 @@ node, |
{ | ||
"name": "@stylistic/eslint-plugin-ts", | ||
"type": "commonjs", | ||
"version": "2.10.1", | ||
"version": "2.11.0", | ||
"author": "Anthony Fu <anthonyfu117@hotmail.com>", | ||
@@ -66,3 +66,3 @@ "license": "MIT", | ||
"dependencies": { | ||
"@typescript-eslint/utils": "^8.12.2", | ||
"@typescript-eslint/utils": "^8.13.0", | ||
"eslint-visitor-keys": "^4.2.0", | ||
@@ -69,0 +69,0 @@ "espree": "^10.3.0" |
390031
10765