routie dev init since i didn't adhere to any proper guidance up until now
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright OpenJS Foundation and other contributors, <www.openjsf.org>
|
||||
Copyright (c) 2023-PRESENT ESLint Stylistic contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+579
@@ -0,0 +1,579 @@
|
||||
import { $ as warnDeprecation, et as createAllConfigs } from "./utils.js";
|
||||
import { t as array_bracket_newline_default } from "./rules/array-bracket-newline.js";
|
||||
import { t as array_bracket_spacing_default } from "./rules/array-bracket-spacing.js";
|
||||
import { t as array_element_newline_default } from "./rules/array-element-newline.js";
|
||||
import { t as arrow_parens_default } from "./rules/arrow-parens.js";
|
||||
import { t as arrow_spacing_default } from "./rules/arrow-spacing.js";
|
||||
import { t as block_spacing_default } from "./rules/block-spacing.js";
|
||||
import { t as brace_style_default } from "./rules/brace-style.js";
|
||||
import { t as comma_dangle_default } from "./rules/comma-dangle.js";
|
||||
import { t as comma_spacing_default } from "./rules/comma-spacing.js";
|
||||
import { t as comma_style_default } from "./rules/comma-style.js";
|
||||
import { t as computed_property_spacing_default } from "./rules/computed-property-spacing.js";
|
||||
import { t as curly_newline_default } from "./rules/curly-newline.js";
|
||||
import { t as dot_location_default } from "./rules/dot-location.js";
|
||||
import { t as eol_last_default } from "./rules/eol-last.js";
|
||||
import { t as function_call_argument_newline_default } from "./rules/function-call-argument-newline.js";
|
||||
import { t as function_call_spacing_default } from "./rules/function-call-spacing.js";
|
||||
import { t as function_paren_newline_default } from "./rules/function-paren-newline.js";
|
||||
import { t as generator_star_spacing_default } from "./rules/generator-star-spacing.js";
|
||||
import { t as implicit_arrow_linebreak_default } from "./rules/implicit-arrow-linebreak.js";
|
||||
import { t as indent_binary_ops_default } from "./rules/indent-binary-ops.js";
|
||||
import { t as indent_default } from "./rules/indent.js";
|
||||
import { t as jsx_child_element_spacing_default } from "./rules/jsx-child-element-spacing.js";
|
||||
import { t as jsx_closing_bracket_location_default } from "./rules/jsx-closing-bracket-location.js";
|
||||
import { t as jsx_closing_tag_location_default } from "./rules/jsx-closing-tag-location.js";
|
||||
import { t as jsx_curly_brace_presence_default } from "./rules/jsx-curly-brace-presence.js";
|
||||
import { t as jsx_curly_newline_default } from "./rules/jsx-curly-newline.js";
|
||||
import { t as jsx_curly_spacing_default } from "./rules/jsx-curly-spacing.js";
|
||||
import { t as jsx_equals_spacing_default } from "./rules/jsx-equals-spacing.js";
|
||||
import { t as jsx_first_prop_new_line_default } from "./rules/jsx-first-prop-new-line.js";
|
||||
import { t as jsx_function_call_newline_default } from "./rules/jsx-function-call-newline.js";
|
||||
import { t as jsx_indent_props_default } from "./rules/jsx-indent-props.js";
|
||||
import { t as jsx_indent_default } from "./rules/jsx-indent.js";
|
||||
import { t as jsx_max_props_per_line_default } from "./rules/jsx-max-props-per-line.js";
|
||||
import { t as jsx_newline_default } from "./rules/jsx-newline.js";
|
||||
import { t as jsx_one_expression_per_line_default } from "./rules/jsx-one-expression-per-line.js";
|
||||
import { t as jsx_pascal_case_default } from "./rules/jsx-pascal-case.js";
|
||||
import { t as jsx_props_no_multi_spaces_default } from "./rules/jsx-props-no-multi-spaces.js";
|
||||
import { t as jsx_props_style_default } from "./rules/jsx-props-style.js";
|
||||
import { t as jsx_quotes_default } from "./rules/jsx-quotes.js";
|
||||
import { t as jsx_self_closing_comp_default } from "./rules/jsx-self-closing-comp.js";
|
||||
import { t as jsx_sort_props_default } from "./rules/jsx-sort-props.js";
|
||||
import { t as jsx_tag_spacing_default } from "./rules/jsx-tag-spacing.js";
|
||||
import { t as jsx_wrap_multilines_default } from "./rules/jsx-wrap-multilines.js";
|
||||
import { t as key_spacing_default } from "./rules/key-spacing.js";
|
||||
import { t as keyword_spacing_default } from "./rules/keyword-spacing.js";
|
||||
import { t as line_comment_position_default } from "./rules/line-comment-position.js";
|
||||
import { t as linebreak_style_default } from "./rules/linebreak-style.js";
|
||||
import { t as lines_around_comment_default } from "./rules/lines-around-comment.js";
|
||||
import { t as lines_between_class_members_default } from "./rules/lines-between-class-members.js";
|
||||
import { t as list_style_default } from "./rules/list-style.js";
|
||||
import { t as max_len_default } from "./rules/max-len.js";
|
||||
import { t as max_statements_per_line_default } from "./rules/max-statements-per-line.js";
|
||||
import { t as member_delimiter_style_default } from "./rules/member-delimiter-style.js";
|
||||
import { t as multiline_comment_style_default } from "./rules/multiline-comment-style.js";
|
||||
import { t as multiline_ternary_default } from "./rules/multiline-ternary.js";
|
||||
import { t as new_parens_default } from "./rules/new-parens.js";
|
||||
import { t as newline_per_chained_call_default } from "./rules/newline-per-chained-call.js";
|
||||
import { t as no_confusing_arrow_default } from "./rules/no-confusing-arrow.js";
|
||||
import { t as no_extra_parens_default } from "./rules/no-extra-parens.js";
|
||||
import { t as no_extra_semi_default } from "./rules/no-extra-semi.js";
|
||||
import { t as no_floating_decimal_default } from "./rules/no-floating-decimal.js";
|
||||
import { t as no_mixed_operators_default } from "./rules/no-mixed-operators.js";
|
||||
import { t as no_mixed_spaces_and_tabs_default } from "./rules/no-mixed-spaces-and-tabs.js";
|
||||
import { t as no_multi_spaces_default } from "./rules/no-multi-spaces.js";
|
||||
import { t as no_multiple_empty_lines_default } from "./rules/no-multiple-empty-lines.js";
|
||||
import { t as no_tabs_default } from "./rules/no-tabs.js";
|
||||
import { t as no_trailing_spaces_default } from "./rules/no-trailing-spaces.js";
|
||||
import { t as no_whitespace_before_property_default } from "./rules/no-whitespace-before-property.js";
|
||||
import { t as nonblock_statement_body_position_default } from "./rules/nonblock-statement-body-position.js";
|
||||
import { t as object_curly_newline_default } from "./rules/object-curly-newline.js";
|
||||
import { t as object_curly_spacing_default } from "./rules/object-curly-spacing.js";
|
||||
import { t as object_property_newline_default } from "./rules/object-property-newline.js";
|
||||
import { t as one_var_declaration_per_line_default } from "./rules/one-var-declaration-per-line.js";
|
||||
import { t as operator_linebreak_default } from "./rules/operator-linebreak.js";
|
||||
import { t as padded_blocks_default } from "./rules/padded-blocks.js";
|
||||
import { t as padding_line_between_statements_default } from "./rules/padding-line-between-statements.js";
|
||||
import { t as quote_props_default } from "./rules/quote-props.js";
|
||||
import { t as quotes_default } from "./rules/quotes.js";
|
||||
import { t as rest_spread_spacing_default } from "./rules/rest-spread-spacing.js";
|
||||
import { t as semi_spacing_default } from "./rules/semi-spacing.js";
|
||||
import { t as semi_style_default } from "./rules/semi-style.js";
|
||||
import { t as semi_default } from "./rules/semi.js";
|
||||
import { t as space_before_blocks_default } from "./rules/space-before-blocks.js";
|
||||
import { t as space_before_function_paren_default } from "./rules/space-before-function-paren.js";
|
||||
import { t as space_in_parens_default } from "./rules/space-in-parens.js";
|
||||
import { t as space_infix_ops_default } from "./rules/space-infix-ops.js";
|
||||
import { t as space_unary_ops_default } from "./rules/space-unary-ops.js";
|
||||
import { t as spaced_comment_default } from "./rules/spaced-comment.js";
|
||||
import { t as switch_colon_spacing_default } from "./rules/switch-colon-spacing.js";
|
||||
import { t as template_curly_spacing_default } from "./rules/template-curly-spacing.js";
|
||||
import { t as template_tag_spacing_default } from "./rules/template-tag-spacing.js";
|
||||
import { t as type_annotation_spacing_default } from "./rules/type-annotation-spacing.js";
|
||||
import { t as type_generic_spacing_default } from "./rules/type-generic-spacing.js";
|
||||
import { t as type_named_tuple_spacing_default } from "./rules/type-named-tuple-spacing.js";
|
||||
import { t as wrap_iife_default } from "./rules/wrap-iife.js";
|
||||
import { t as wrap_regex_default } from "./rules/wrap-regex.js";
|
||||
import { t as yield_star_spacing_default } from "./rules/yield-star-spacing.js";
|
||||
const plugin = {
|
||||
meta: {
|
||||
name: "@stylistic/eslint-plugin",
|
||||
version: "5.10.0",
|
||||
namespace: "@stylistic"
|
||||
},
|
||||
rules: {
|
||||
"array-bracket-newline": array_bracket_newline_default,
|
||||
"array-bracket-spacing": array_bracket_spacing_default,
|
||||
"array-element-newline": array_element_newline_default,
|
||||
"arrow-parens": arrow_parens_default,
|
||||
"arrow-spacing": arrow_spacing_default,
|
||||
"block-spacing": block_spacing_default,
|
||||
"brace-style": brace_style_default,
|
||||
"comma-dangle": comma_dangle_default,
|
||||
"comma-spacing": comma_spacing_default,
|
||||
"comma-style": comma_style_default,
|
||||
"computed-property-spacing": computed_property_spacing_default,
|
||||
"curly-newline": curly_newline_default,
|
||||
"dot-location": dot_location_default,
|
||||
"eol-last": eol_last_default,
|
||||
"function-call-argument-newline": function_call_argument_newline_default,
|
||||
"function-call-spacing": function_call_spacing_default,
|
||||
"function-paren-newline": function_paren_newline_default,
|
||||
"generator-star-spacing": generator_star_spacing_default,
|
||||
"implicit-arrow-linebreak": implicit_arrow_linebreak_default,
|
||||
"indent": indent_default,
|
||||
"indent-binary-ops": indent_binary_ops_default,
|
||||
"jsx-child-element-spacing": jsx_child_element_spacing_default,
|
||||
"jsx-closing-bracket-location": jsx_closing_bracket_location_default,
|
||||
"jsx-closing-tag-location": jsx_closing_tag_location_default,
|
||||
"jsx-curly-brace-presence": jsx_curly_brace_presence_default,
|
||||
"jsx-curly-newline": jsx_curly_newline_default,
|
||||
"jsx-curly-spacing": jsx_curly_spacing_default,
|
||||
"jsx-equals-spacing": jsx_equals_spacing_default,
|
||||
"jsx-first-prop-new-line": jsx_first_prop_new_line_default,
|
||||
"jsx-function-call-newline": jsx_function_call_newline_default,
|
||||
"jsx-indent": jsx_indent_default,
|
||||
"jsx-indent-props": jsx_indent_props_default,
|
||||
"jsx-max-props-per-line": jsx_max_props_per_line_default,
|
||||
"jsx-newline": jsx_newline_default,
|
||||
"jsx-one-expression-per-line": jsx_one_expression_per_line_default,
|
||||
"jsx-pascal-case": jsx_pascal_case_default,
|
||||
"jsx-props-no-multi-spaces": jsx_props_no_multi_spaces_default,
|
||||
"exp-jsx-props-style": jsx_props_style_default,
|
||||
"jsx-quotes": jsx_quotes_default,
|
||||
"jsx-self-closing-comp": jsx_self_closing_comp_default,
|
||||
"jsx-sort-props": jsx_sort_props_default,
|
||||
"jsx-tag-spacing": jsx_tag_spacing_default,
|
||||
"jsx-wrap-multilines": jsx_wrap_multilines_default,
|
||||
"key-spacing": key_spacing_default,
|
||||
"keyword-spacing": keyword_spacing_default,
|
||||
"line-comment-position": line_comment_position_default,
|
||||
"linebreak-style": linebreak_style_default,
|
||||
"lines-around-comment": lines_around_comment_default,
|
||||
"lines-between-class-members": lines_between_class_members_default,
|
||||
"exp-list-style": list_style_default,
|
||||
"max-len": max_len_default,
|
||||
"max-statements-per-line": max_statements_per_line_default,
|
||||
"member-delimiter-style": member_delimiter_style_default,
|
||||
"multiline-comment-style": multiline_comment_style_default,
|
||||
"multiline-ternary": multiline_ternary_default,
|
||||
"new-parens": new_parens_default,
|
||||
"newline-per-chained-call": newline_per_chained_call_default,
|
||||
"no-confusing-arrow": no_confusing_arrow_default,
|
||||
"no-extra-parens": no_extra_parens_default,
|
||||
"no-extra-semi": no_extra_semi_default,
|
||||
"no-floating-decimal": no_floating_decimal_default,
|
||||
"no-mixed-operators": no_mixed_operators_default,
|
||||
"no-mixed-spaces-and-tabs": no_mixed_spaces_and_tabs_default,
|
||||
"no-multi-spaces": no_multi_spaces_default,
|
||||
"no-multiple-empty-lines": no_multiple_empty_lines_default,
|
||||
"no-tabs": no_tabs_default,
|
||||
"no-trailing-spaces": no_trailing_spaces_default,
|
||||
"no-whitespace-before-property": no_whitespace_before_property_default,
|
||||
"nonblock-statement-body-position": nonblock_statement_body_position_default,
|
||||
"object-curly-newline": object_curly_newline_default,
|
||||
"object-curly-spacing": object_curly_spacing_default,
|
||||
"object-property-newline": object_property_newline_default,
|
||||
"one-var-declaration-per-line": one_var_declaration_per_line_default,
|
||||
"operator-linebreak": operator_linebreak_default,
|
||||
"padded-blocks": padded_blocks_default,
|
||||
"padding-line-between-statements": padding_line_between_statements_default,
|
||||
"quote-props": quote_props_default,
|
||||
"quotes": quotes_default,
|
||||
"rest-spread-spacing": rest_spread_spacing_default,
|
||||
"semi": semi_default,
|
||||
"semi-spacing": semi_spacing_default,
|
||||
"semi-style": semi_style_default,
|
||||
"space-before-blocks": space_before_blocks_default,
|
||||
"space-before-function-paren": space_before_function_paren_default,
|
||||
"space-in-parens": space_in_parens_default,
|
||||
"space-infix-ops": space_infix_ops_default,
|
||||
"space-unary-ops": space_unary_ops_default,
|
||||
"spaced-comment": spaced_comment_default,
|
||||
"switch-colon-spacing": switch_colon_spacing_default,
|
||||
"template-curly-spacing": template_curly_spacing_default,
|
||||
"template-tag-spacing": template_tag_spacing_default,
|
||||
"type-annotation-spacing": type_annotation_spacing_default,
|
||||
"type-generic-spacing": type_generic_spacing_default,
|
||||
"type-named-tuple-spacing": type_named_tuple_spacing_default,
|
||||
"wrap-iife": wrap_iife_default,
|
||||
"wrap-regex": wrap_regex_default,
|
||||
"yield-star-spacing": yield_star_spacing_default
|
||||
}
|
||||
};
|
||||
function customize(options = {}) {
|
||||
const defaultPluginName = plugin.meta.namespace;
|
||||
const { arrowParens = false, blockSpacing = true, braceStyle = "stroustrup", commaDangle = "always-multiline", experimental: enableExperimentalRules = false, indent = 2, jsx = true, pluginName = defaultPluginName, quoteProps = "consistent-as-needed", quotes = "single", semi = false, severity = "error" } = options;
|
||||
const [indentLevel = 2, indentOptions = {
|
||||
ArrayExpression: 1,
|
||||
CallExpression: { arguments: 1 },
|
||||
flatTernaryExpressions: false,
|
||||
FunctionDeclaration: {
|
||||
body: 1,
|
||||
parameters: 1,
|
||||
returnType: 1
|
||||
},
|
||||
FunctionExpression: {
|
||||
body: 1,
|
||||
parameters: 1,
|
||||
returnType: 1
|
||||
},
|
||||
ignoreComments: false,
|
||||
ignoredNodes: ["TSUnionType", "TSIntersectionType"],
|
||||
ImportDeclaration: 1,
|
||||
MemberExpression: 1,
|
||||
ObjectExpression: 1,
|
||||
offsetTernaryExpressions: true,
|
||||
outerIIFEBody: 1,
|
||||
SwitchCase: 1,
|
||||
tabLength: indentLevel === "tab" ? 4 : indentLevel,
|
||||
VariableDeclarator: 1
|
||||
}] = Array.isArray(indent) ? indent : [indent];
|
||||
let rules = {
|
||||
"@stylistic/array-bracket-spacing": [severity, "never"],
|
||||
"@stylistic/arrow-parens": [
|
||||
severity,
|
||||
arrowParens ? "always" : "as-needed",
|
||||
{ requireForBlockBody: true }
|
||||
],
|
||||
"@stylistic/arrow-spacing": [severity, {
|
||||
after: true,
|
||||
before: true
|
||||
}],
|
||||
"@stylistic/block-spacing": [severity, blockSpacing ? "always" : "never"],
|
||||
"@stylistic/brace-style": [
|
||||
severity,
|
||||
braceStyle,
|
||||
{ allowSingleLine: true }
|
||||
],
|
||||
"@stylistic/comma-dangle": [severity, commaDangle],
|
||||
"@stylistic/comma-spacing": [severity, {
|
||||
after: true,
|
||||
before: false
|
||||
}],
|
||||
"@stylistic/comma-style": [severity, "last"],
|
||||
"@stylistic/computed-property-spacing": [
|
||||
severity,
|
||||
"never",
|
||||
{ enforceForClassMembers: true }
|
||||
],
|
||||
"@stylistic/dot-location": [severity, "property"],
|
||||
"@stylistic/eol-last": severity,
|
||||
"@stylistic/generator-star-spacing": [severity, {
|
||||
after: true,
|
||||
before: false
|
||||
}],
|
||||
"@stylistic/indent": [
|
||||
severity,
|
||||
indentLevel,
|
||||
indentOptions
|
||||
],
|
||||
"@stylistic/indent-binary-ops": [severity, indentLevel],
|
||||
"@stylistic/key-spacing": [severity, {
|
||||
afterColon: true,
|
||||
beforeColon: false
|
||||
}],
|
||||
"@stylistic/keyword-spacing": [severity, {
|
||||
after: true,
|
||||
before: true
|
||||
}],
|
||||
"@stylistic/lines-between-class-members": [
|
||||
severity,
|
||||
"always",
|
||||
{ exceptAfterSingleLine: true }
|
||||
],
|
||||
"@stylistic/max-statements-per-line": [severity, { max: 1 }],
|
||||
"@stylistic/member-delimiter-style": [severity, {
|
||||
multiline: {
|
||||
delimiter: semi ? "semi" : "none",
|
||||
requireLast: semi
|
||||
},
|
||||
multilineDetection: "brackets",
|
||||
overrides: { interface: { multiline: {
|
||||
delimiter: semi ? "semi" : "none",
|
||||
requireLast: semi
|
||||
} } },
|
||||
singleline: { delimiter: semi ? "semi" : "comma" }
|
||||
}],
|
||||
"@stylistic/multiline-ternary": [severity, "always-multiline"],
|
||||
"@stylistic/new-parens": severity,
|
||||
"@stylistic/no-extra-parens": [severity, "functions"],
|
||||
"@stylistic/no-floating-decimal": severity,
|
||||
"@stylistic/no-mixed-operators": [severity, {
|
||||
allowSamePrecedence: true,
|
||||
groups: [
|
||||
[
|
||||
"==",
|
||||
"!=",
|
||||
"===",
|
||||
"!==",
|
||||
">",
|
||||
">=",
|
||||
"<",
|
||||
"<="
|
||||
],
|
||||
["&&", "||"],
|
||||
["in", "instanceof"]
|
||||
]
|
||||
}],
|
||||
"@stylistic/no-mixed-spaces-and-tabs": severity,
|
||||
"@stylistic/no-multi-spaces": severity,
|
||||
"@stylistic/no-multiple-empty-lines": [severity, {
|
||||
max: 1,
|
||||
maxBOF: 0,
|
||||
maxEOF: 0
|
||||
}],
|
||||
"@stylistic/no-tabs": indentLevel === "tab" ? "off" : severity,
|
||||
"@stylistic/no-trailing-spaces": severity,
|
||||
"@stylistic/no-whitespace-before-property": severity,
|
||||
"@stylistic/object-curly-spacing": [severity, "always"],
|
||||
"@stylistic/operator-linebreak": [severity, "before"],
|
||||
"@stylistic/padded-blocks": [severity, {
|
||||
blocks: "never",
|
||||
classes: "never",
|
||||
switches: "never"
|
||||
}],
|
||||
"@stylistic/quote-props": [severity, quoteProps],
|
||||
"@stylistic/quotes": [
|
||||
severity,
|
||||
quotes,
|
||||
{
|
||||
allowTemplateLiterals: "always",
|
||||
avoidEscape: false
|
||||
}
|
||||
],
|
||||
"@stylistic/rest-spread-spacing": [severity, "never"],
|
||||
"@stylistic/semi": [severity, semi ? "always" : "never"],
|
||||
"@stylistic/semi-spacing": [severity, {
|
||||
after: true,
|
||||
before: false
|
||||
}],
|
||||
"@stylistic/space-before-blocks": [severity, "always"],
|
||||
"@stylistic/space-before-function-paren": [severity, {
|
||||
anonymous: "always",
|
||||
asyncArrow: "always",
|
||||
named: "never"
|
||||
}],
|
||||
"@stylistic/space-in-parens": [severity, "never"],
|
||||
"@stylistic/space-infix-ops": severity,
|
||||
"@stylistic/space-unary-ops": [severity, {
|
||||
nonwords: false,
|
||||
words: true
|
||||
}],
|
||||
"@stylistic/spaced-comment": [
|
||||
severity,
|
||||
"always",
|
||||
{
|
||||
block: {
|
||||
balanced: true,
|
||||
exceptions: ["*"],
|
||||
markers: ["!"]
|
||||
},
|
||||
line: {
|
||||
exceptions: ["/", "#"],
|
||||
markers: ["/"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"@stylistic/template-curly-spacing": severity,
|
||||
"@stylistic/template-tag-spacing": [severity, "never"],
|
||||
"@stylistic/type-annotation-spacing": [severity, {}],
|
||||
"@stylistic/type-generic-spacing": severity,
|
||||
"@stylistic/type-named-tuple-spacing": severity,
|
||||
"@stylistic/wrap-iife": [
|
||||
severity,
|
||||
"any",
|
||||
{ functionPrototypeMethods: true }
|
||||
],
|
||||
"@stylistic/yield-star-spacing": [severity, {
|
||||
after: true,
|
||||
before: false
|
||||
}],
|
||||
...jsx ? {
|
||||
"@stylistic/jsx-closing-bracket-location": severity,
|
||||
"@stylistic/jsx-closing-tag-location": severity,
|
||||
"@stylistic/jsx-curly-brace-presence": [severity, { propElementValues: "always" }],
|
||||
"@stylistic/jsx-curly-newline": severity,
|
||||
"@stylistic/jsx-curly-spacing": [severity, "never"],
|
||||
"@stylistic/jsx-equals-spacing": severity,
|
||||
"@stylistic/jsx-first-prop-new-line": severity,
|
||||
"@stylistic/jsx-function-call-newline": [severity, "multiline"],
|
||||
"@stylistic/jsx-indent-props": [severity, indentLevel],
|
||||
"@stylistic/jsx-max-props-per-line": [severity, {
|
||||
maximum: 1,
|
||||
when: "multiline"
|
||||
}],
|
||||
"@stylistic/jsx-one-expression-per-line": [severity, { allow: "single-child" }],
|
||||
"@stylistic/jsx-quotes": severity,
|
||||
"@stylistic/jsx-tag-spacing": [severity, {
|
||||
afterOpening: "never",
|
||||
beforeClosing: "never",
|
||||
beforeSelfClosing: "always",
|
||||
closingSlash: "never"
|
||||
}],
|
||||
"@stylistic/jsx-wrap-multilines": [severity, {
|
||||
arrow: "parens-new-line",
|
||||
assignment: "parens-new-line",
|
||||
condition: "parens-new-line",
|
||||
declaration: "parens-new-line",
|
||||
logical: "parens-new-line",
|
||||
prop: "parens-new-line",
|
||||
propertyValue: "parens-new-line",
|
||||
return: "parens-new-line"
|
||||
}]
|
||||
} : {}
|
||||
};
|
||||
if (enableExperimentalRules) rules = {
|
||||
...rules,
|
||||
"@stylistic/array-bracket-newline": "off",
|
||||
"@stylistic/array-bracket-spacing": "off",
|
||||
"@stylistic/array-element-newline": "off",
|
||||
"@stylistic/exp-list-style": severity,
|
||||
"@stylistic/function-call-argument-newline": "off",
|
||||
"@stylistic/function-paren-newline": "off",
|
||||
"@stylistic/object-curly-newline": "off",
|
||||
"@stylistic/object-curly-spacing": "off",
|
||||
"@stylistic/object-property-newline": "off",
|
||||
...jsx ? {
|
||||
"@stylistic/exp-jsx-props-style": severity,
|
||||
"@stylistic/jsx-first-prop-new-line": "off",
|
||||
"@stylistic/jsx-function-call-newline": "off",
|
||||
"@stylistic/jsx-max-props-per-line": "off"
|
||||
} : {}
|
||||
};
|
||||
if (pluginName !== defaultPluginName) {
|
||||
const regex = new RegExp(`^${defaultPluginName}/`);
|
||||
rules = Object.fromEntries(Object.entries(rules).map(([ruleName, ruleConfig]) => [ruleName.replace(regex, `${pluginName}/`), ruleConfig]));
|
||||
}
|
||||
return {
|
||||
plugins: { [pluginName]: plugin },
|
||||
rules
|
||||
};
|
||||
}
|
||||
const config = { rules: {
|
||||
"array-bracket-newline": 0,
|
||||
"array-bracket-spacing": 0,
|
||||
"array-element-newline": 0,
|
||||
"arrow-parens": 0,
|
||||
"arrow-spacing": 0,
|
||||
"block-spacing": 0,
|
||||
"brace-style": 0,
|
||||
"comma-dangle": 0,
|
||||
"comma-spacing": 0,
|
||||
"comma-style": 0,
|
||||
"computed-property-spacing": 0,
|
||||
"dot-location": 0,
|
||||
"eol-last": 0,
|
||||
"func-call-spacing": 0,
|
||||
"function-call-argument-newline": 0,
|
||||
"function-paren-newline": 0,
|
||||
"generator-star-spacing": 0,
|
||||
"implicit-arrow-linebreak": 0,
|
||||
"indent": 0,
|
||||
"jsx-quotes": 0,
|
||||
"key-spacing": 0,
|
||||
"keyword-spacing": 0,
|
||||
"linebreak-style": 0,
|
||||
"lines-around-comment": 0,
|
||||
"lines-between-class-members": 0,
|
||||
"max-len": 0,
|
||||
"max-statements-per-line": 0,
|
||||
"multiline-ternary": 0,
|
||||
"multiline-comment-style": 0,
|
||||
"new-parens": 0,
|
||||
"newline-per-chained-call": 0,
|
||||
"no-confusing-arrow": 0,
|
||||
"no-extra-parens": 0,
|
||||
"no-extra-semi": 0,
|
||||
"no-floating-decimal": 0,
|
||||
"no-mixed-operators": 0,
|
||||
"no-mixed-spaces-and-tabs": 0,
|
||||
"no-multi-spaces": 0,
|
||||
"no-multiple-empty-lines": 0,
|
||||
"no-tabs": 0,
|
||||
"no-trailing-spaces": 0,
|
||||
"no-whitespace-before-property": 0,
|
||||
"nonblock-statement-body-position": 0,
|
||||
"object-curly-newline": 0,
|
||||
"object-curly-spacing": 0,
|
||||
"object-property-newline": 0,
|
||||
"one-var-declaration-per-line": 0,
|
||||
"operator-linebreak": 0,
|
||||
"padded-blocks": 0,
|
||||
"padding-line-between-statements": 0,
|
||||
"quote-props": 0,
|
||||
"quotes": 0,
|
||||
"rest-spread-spacing": 0,
|
||||
"semi": 0,
|
||||
"semi-spacing": 0,
|
||||
"semi-style": 0,
|
||||
"space-before-blocks": 0,
|
||||
"space-before-function-paren": 0,
|
||||
"space-in-parens": 0,
|
||||
"space-infix-ops": 0,
|
||||
"space-unary-ops": 0,
|
||||
"spaced-comment": 0,
|
||||
"switch-colon-spacing": 0,
|
||||
"template-curly-spacing": 0,
|
||||
"template-tag-spacing": 0,
|
||||
"wrap-iife": 0,
|
||||
"wrap-regex": 0,
|
||||
"yield-star-spacing": 0,
|
||||
"@typescript-eslint/block-spacing": 0,
|
||||
"@typescript-eslint/brace-style": 0,
|
||||
"@typescript-eslint/comma-dangle": 0,
|
||||
"@typescript-eslint/comma-spacing": 0,
|
||||
"@typescript-eslint/func-call-spacing": 0,
|
||||
"@typescript-eslint/indent": 0,
|
||||
"@typescript-eslint/key-spacing": 0,
|
||||
"@typescript-eslint/keyword-spacing": 0,
|
||||
"@typescript-eslint/lines-around-comment": 0,
|
||||
"@typescript-eslint/lines-between-class-members": 0,
|
||||
"@typescript-eslint/member-delimiter-style": 0,
|
||||
"@typescript-eslint/no-extra-parens": 0,
|
||||
"@typescript-eslint/no-extra-semi": 0,
|
||||
"@typescript-eslint/object-curly-spacing": 0,
|
||||
"@typescript-eslint/padding-line-between-statements": 0,
|
||||
"@typescript-eslint/quotes": 0,
|
||||
"@typescript-eslint/semi": 0,
|
||||
"@typescript-eslint/space-before-blocks": 0,
|
||||
"@typescript-eslint/space-before-function-paren": 0,
|
||||
"@typescript-eslint/space-infix-ops": 0,
|
||||
"@typescript-eslint/type-annotation-spacing": 0,
|
||||
"react/jsx-child-element-spacing": 0,
|
||||
"react/jsx-closing-bracket-location": 0,
|
||||
"react/jsx-closing-tag-location": 0,
|
||||
"react/jsx-curly-brace-presence": 0,
|
||||
"react/jsx-curly-newline": 0,
|
||||
"react/jsx-curly-spacing": 0,
|
||||
"react/jsx-equals-spacing": 0,
|
||||
"react/jsx-first-prop-new-line": 0,
|
||||
"react/jsx-indent": 0,
|
||||
"react/jsx-indent-props": 0,
|
||||
"react/jsx-max-props-per-line": 0,
|
||||
"react/jsx-newline": 0,
|
||||
"react/jsx-one-expression-per-line": 0,
|
||||
"react/jsx-pascal-case": 0,
|
||||
"react/jsx-props-no-multi-spaces": 0,
|
||||
"react/self-closing-comp": 0,
|
||||
"react/jsx-sort-props": 0,
|
||||
"react/jsx-tag-spacing": 0,
|
||||
"react/jsx-wrap-multilines": 0
|
||||
} };
|
||||
const allConfigsIgnore = [/^jsx-/, /^curly-newline$/];
|
||||
const all = /* @__PURE__ */ createAllConfigs(plugin, "@stylistic", (name) => !allConfigsIgnore.some((re) => re.test(name)));
|
||||
const recommended = /* @__PURE__ */ customize();
|
||||
const configs = new Proxy({
|
||||
"disable-legacy": config,
|
||||
"customize": customize,
|
||||
"recommended": recommended,
|
||||
"recommended-flat": recommended,
|
||||
"all": all,
|
||||
"all-flat": all
|
||||
}, { get(target, p, receiver) {
|
||||
const prop = p.toString();
|
||||
if (prop.endsWith("-flat")) warnDeprecation(`config("${prop}")`, `"${prop.replace("-flat", "")}"`);
|
||||
return Reflect.get(target, p, receiver);
|
||||
} });
|
||||
export { plugin as n, configs as t };
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
import { RuleOptions } from "./rule-options.js";
|
||||
|
||||
//#region dts/define-config-support.d.ts
|
||||
declare module 'eslint-define-config' {
|
||||
export interface CustomRuleOptions extends RuleOptions {}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
import { RuleOptions, UnprefixedRuleOptions } from "./rule-options.js";
|
||||
import { ESLint, Linter, Rule } from "eslint";
|
||||
|
||||
//#region dts/options.d.ts
|
||||
type IndentRuleOptions = UnprefixedRuleOptions['indent'];
|
||||
interface StylisticCustomizeOptions {
|
||||
/**
|
||||
* The name of the registered plugin, used to prefix rule IDs
|
||||
* @default '@stylistic'
|
||||
*/
|
||||
pluginName?: string;
|
||||
/**
|
||||
* Indentation level
|
||||
* Similar to the `tabWidth` and `useTabs` options in Prettier
|
||||
*
|
||||
* @default 2
|
||||
*/
|
||||
indent?: IndentRuleOptions[0] | IndentRuleOptions;
|
||||
/**
|
||||
* Quote style
|
||||
* Similar to `singleQuote` option in Prettier
|
||||
*
|
||||
* @default 'single'
|
||||
*/
|
||||
quotes?: 'single' | 'double' | 'backtick';
|
||||
/**
|
||||
* Whether to enable semicolons
|
||||
* Similar to `semi` option in Prettier
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
semi?: boolean;
|
||||
/**
|
||||
* Enable JSX support
|
||||
* @default true
|
||||
*/
|
||||
jsx?: boolean;
|
||||
/**
|
||||
* When to enable arrow parenthesis
|
||||
* Similar to `arrowParens` option in Prettier
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
arrowParens?: boolean;
|
||||
/**
|
||||
* Which brace style to use
|
||||
* @default 'stroustrup'
|
||||
*/
|
||||
braceStyle?: '1tbs' | 'stroustrup' | 'allman';
|
||||
/**
|
||||
* Whether to require spaces around braces
|
||||
* Similar to `bracketSpacing` option in Prettier
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
blockSpacing?: boolean;
|
||||
/**
|
||||
* When to enable prop quoting
|
||||
* Similar to `quoteProps` option in Prettier
|
||||
*
|
||||
* @default 'consistent-as-needed'
|
||||
*/
|
||||
quoteProps?: 'always' | 'as-needed' | 'consistent' | 'consistent-as-needed';
|
||||
/**
|
||||
* When to enable comma dangles
|
||||
* Similar to `trailingComma` option in Prettier
|
||||
*
|
||||
* @default 'always-multiline'
|
||||
*/
|
||||
commaDangle?: 'never' | 'always' | 'always-multiline' | 'only-multiline';
|
||||
/**
|
||||
* Severity level of the rules
|
||||
* Determines how violations are reported.
|
||||
*
|
||||
* @default 'error'
|
||||
*/
|
||||
severity?: 'error' | 'warn';
|
||||
/**
|
||||
* Enable the experimental rules
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
experimental?: boolean;
|
||||
}
|
||||
//#endregion
|
||||
//#region dts/configs.d.ts
|
||||
declare function customize(options?: StylisticCustomizeOptions): Linter.Config;
|
||||
declare const configs: {
|
||||
/**
|
||||
* Disable all legacy rules from `eslint`, `@typescript-eslint` and `eslint-plugin-react`
|
||||
*
|
||||
* This config works for both flat and legacy config format
|
||||
*/
|
||||
'disable-legacy': Linter.Config;
|
||||
/**
|
||||
* A factory function to customize the recommended config
|
||||
*/
|
||||
'customize': typeof customize;
|
||||
/**
|
||||
* The default recommended config in Flat Config Format
|
||||
*/
|
||||
'recommended': Linter.Config;
|
||||
/**
|
||||
* The default recommended config in Flat Config Format
|
||||
*
|
||||
* @deprecated use `recommended` instead.
|
||||
*/
|
||||
'recommended-flat': Linter.Config;
|
||||
/**
|
||||
* Enable all rules, in Flat Config Format
|
||||
*/
|
||||
'all': Linter.Config;
|
||||
/**
|
||||
* Enable all rules, in Flat Config Format
|
||||
*
|
||||
* @deprecated use `all` instead.
|
||||
*/
|
||||
'all-flat': Linter.Config;
|
||||
};
|
||||
type Configs = typeof configs;
|
||||
//#endregion
|
||||
//#region dts/rules.d.ts
|
||||
type Rules = { [K in keyof UnprefixedRuleOptions]: Rule.RuleModule };
|
||||
//#endregion
|
||||
//#region dts/index.d.ts
|
||||
declare const plugin: {
|
||||
rules: Rules;
|
||||
configs: ESLint.Plugin['configs'] & Configs;
|
||||
};
|
||||
//#endregion
|
||||
export { type Configs, type RuleOptions, type Rules, type StylisticCustomizeOptions, type UnprefixedRuleOptions, plugin as default };
|
||||
+979
@@ -0,0 +1,979 @@
|
||||
import { $ as KeywordSpacingRuleOptions, A as NoWhitespaceBeforePropertyRuleOptions, At as EolLastRuleOptions, B as NoConfusingArrowRuleOptions, Bt as ArrowParensRuleOptions, C as PaddedBlocksRuleOptions, Ct as IndentRuleOptions, D as ObjectCurlySpacingRuleOptions, Dt as FunctionParenNewlineRuleOptions, E as ObjectPropertyNewlineRuleOptions, Et as GeneratorStarSpacingRuleOptions, F as NoMixedSpacesAndTabsRuleOptions, Ft as CommaSpacingRuleOptions, G as MemberDelimiterStyleRuleOptions, H as NewParensRuleOptions, Ht as ArrayBracketSpacingRuleOptions, I as NoMixedOperatorsRuleOptions, It as CommaDangleRuleOptions, J as ListStyleRuleOptions, K as MaxStatementsPerLineRuleOptions, L as NoFloatingDecimalRuleOptions, Lt as BraceStyleRuleOptions, M as NoTabsRuleOptions, Mt as CurlyNewlineRuleOptions, N as NoMultipleEmptyLinesRuleOptions, Nt as ComputedPropertySpacingRuleOptions, O as ObjectCurlyNewlineRuleOptions, Ot as FunctionCallSpacingRuleOptions, P as NoMultiSpacesRuleOptions, Pt as CommaStyleRuleOptions, Q as LineCommentPositionRuleOptions, R as NoExtraSemiRuleOptions, Rt as BlockSpacingRuleOptions, S as PaddingLineBetweenStatementsRuleOptions, St as JsxChildElementSpacingRuleOptions, T as OneVarDeclarationPerLineRuleOptions, Tt as ImplicitArrowLinebreakRuleOptions, U as MultilineTernaryRuleOptions, Ut as ArrayBracketNewlineRuleOptions, V as NewlinePerChainedCallRuleOptions, Vt as ArrayElementNewlineRuleOptions, W as MultilineCommentStyleRuleOptions, X as LinesAroundCommentRuleOptions, Y as LinesBetweenClassMembersRuleOptions, Z as LinebreakStyleRuleOptions, _ as SemiStyleRuleOptions, _t as JsxCurlySpacingRuleOptions, a as TypeGenericSpacingRuleOptions, at as JsxQuotesRuleOptions, b as QuotesRuleOptions, bt as JsxClosingTagLocationRuleOptions, c as TemplateCurlySpacingRuleOptions, ct as JsxPascalCaseRuleOptions, d as SpaceUnaryOpsRuleOptions, dt as JsxMaxPropsPerLineRuleOptions, et as KeySpacingRuleOptions, f as SpaceInfixOpsRuleOptions, ft as JsxIndentRuleOptions, g as SemiRuleOptions, gt as JsxEqualsSpacingRuleOptions, h as SpaceBeforeBlocksRuleOptions, ht as JsxFirstPropNewLineRuleOptions, i as TypeNamedTupleSpacingRuleOptions, it as JsxSelfClosingCompRuleOptions, j as NoTrailingSpacesRuleOptions, jt as DotLocationRuleOptions, k as NonblockStatementBodyPositionRuleOptions, kt as FunctionCallArgumentNewlineRuleOptions, l as SwitchColonSpacingRuleOptions, lt as JsxOneExpressionPerLineRuleOptions, m as SpaceBeforeFunctionParenRuleOptions, mt as JsxFunctionCallNewlineRuleOptions, n as WrapRegexRuleOptions, nt as JsxTagSpacingRuleOptions, o as TypeAnnotationSpacingRuleOptions, ot as JsxPropsStyleRuleOptions, p as SpaceInParensRuleOptions, pt as JsxIndentPropsRuleOptions, q as MaxLenRuleOptions, r as WrapIifeRuleOptions, rt as JsxSortPropsRuleOptions, s as TemplateTagSpacingRuleOptions, st as JsxPropsNoMultiSpacesRuleOptions, t as YieldStarSpacingRuleOptions, tt as JsxWrapMultilinesRuleOptions, u as SpacedCommentRuleOptions, ut as JsxNewlineRuleOptions, v as SemiSpacingRuleOptions, vt as JsxCurlyNewlineRuleOptions, w as OperatorLinebreakRuleOptions, wt as IndentBinaryOpsRuleOptions, x as QuotePropsRuleOptions, xt as JsxClosingBracketLocationRuleOptions, y as RestSpreadSpacingRuleOptions, yt as JsxCurlyBracePresenceRuleOptions, z as NoExtraParensRuleOptions, zt as ArrowSpacingRuleOptions } from "./types.mjs";
|
||||
|
||||
//#region dts/rule-options.d.ts
|
||||
interface RuleOptions {
|
||||
/**
|
||||
* Enforce linebreaks after opening and before closing array brackets
|
||||
* @see https://eslint.style/rules/array-bracket-newline
|
||||
*/
|
||||
'@stylistic/array-bracket-newline': ArrayBracketNewlineRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing inside array brackets
|
||||
* @see https://eslint.style/rules/array-bracket-spacing
|
||||
*/
|
||||
'@stylistic/array-bracket-spacing': ArrayBracketSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce line breaks after each array element
|
||||
* @see https://eslint.style/rules/array-element-newline
|
||||
*/
|
||||
'@stylistic/array-element-newline': ArrayElementNewlineRuleOptions;
|
||||
/**
|
||||
* Require parentheses around arrow function arguments
|
||||
* @see https://eslint.style/rules/arrow-parens
|
||||
*/
|
||||
'@stylistic/arrow-parens': ArrowParensRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing before and after the arrow in arrow functions
|
||||
* @see https://eslint.style/rules/arrow-spacing
|
||||
*/
|
||||
'@stylistic/arrow-spacing': ArrowSpacingRuleOptions;
|
||||
/**
|
||||
* Disallow or enforce spaces inside of blocks after opening block and before closing block
|
||||
* @see https://eslint.style/rules/block-spacing
|
||||
*/
|
||||
'@stylistic/block-spacing': BlockSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce consistent brace style for blocks
|
||||
* @see https://eslint.style/rules/brace-style
|
||||
*/
|
||||
'@stylistic/brace-style': BraceStyleRuleOptions;
|
||||
/**
|
||||
* Require or disallow trailing commas
|
||||
* @see https://eslint.style/rules/comma-dangle
|
||||
*/
|
||||
'@stylistic/comma-dangle': CommaDangleRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing before and after commas
|
||||
* @see https://eslint.style/rules/comma-spacing
|
||||
*/
|
||||
'@stylistic/comma-spacing': CommaSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce consistent comma style
|
||||
* @see https://eslint.style/rules/comma-style
|
||||
*/
|
||||
'@stylistic/comma-style': CommaStyleRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing inside computed property brackets
|
||||
* @see https://eslint.style/rules/computed-property-spacing
|
||||
*/
|
||||
'@stylistic/computed-property-spacing': ComputedPropertySpacingRuleOptions;
|
||||
/**
|
||||
* Enforce consistent line breaks after opening and before closing braces
|
||||
* @see https://eslint.style/rules/curly-newline
|
||||
*/
|
||||
'@stylistic/curly-newline': CurlyNewlineRuleOptions;
|
||||
/**
|
||||
* Enforce consistent newlines before and after dots
|
||||
* @see https://eslint.style/rules/dot-location
|
||||
*/
|
||||
'@stylistic/dot-location': DotLocationRuleOptions;
|
||||
/**
|
||||
* Require or disallow newline at the end of files
|
||||
* @see https://eslint.style/rules/eol-last
|
||||
*/
|
||||
'@stylistic/eol-last': EolLastRuleOptions;
|
||||
/**
|
||||
* Enforce line breaks between arguments of a function call
|
||||
* @see https://eslint.style/rules/function-call-argument-newline
|
||||
*/
|
||||
'@stylistic/function-call-argument-newline': FunctionCallArgumentNewlineRuleOptions;
|
||||
/**
|
||||
* Require or disallow spacing between function identifiers and their invocations
|
||||
* @see https://eslint.style/rules/function-call-spacing
|
||||
*/
|
||||
'@stylistic/function-call-spacing': FunctionCallSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce consistent line breaks inside function parentheses
|
||||
* @see https://eslint.style/rules/function-paren-newline
|
||||
*/
|
||||
'@stylistic/function-paren-newline': FunctionParenNewlineRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing around `*` operators in generator functions
|
||||
* @see https://eslint.style/rules/generator-star-spacing
|
||||
*/
|
||||
'@stylistic/generator-star-spacing': GeneratorStarSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce the location of arrow function bodies
|
||||
* @see https://eslint.style/rules/implicit-arrow-linebreak
|
||||
*/
|
||||
'@stylistic/implicit-arrow-linebreak': ImplicitArrowLinebreakRuleOptions;
|
||||
/**
|
||||
* Enforce consistent indentation
|
||||
* @see https://eslint.style/rules/indent
|
||||
*/
|
||||
'@stylistic/indent': IndentRuleOptions;
|
||||
/**
|
||||
* Indentation for binary operators
|
||||
* @see https://eslint.style/rules/indent-binary-ops
|
||||
*/
|
||||
'@stylistic/indent-binary-ops': IndentBinaryOpsRuleOptions;
|
||||
/**
|
||||
* Enforce or disallow spaces inside of curly braces in JSX attributes and expressions
|
||||
* @see https://eslint.style/rules/jsx-child-element-spacing
|
||||
*/
|
||||
'@stylistic/jsx-child-element-spacing': JsxChildElementSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce closing bracket location in JSX
|
||||
* @see https://eslint.style/rules/jsx-closing-bracket-location
|
||||
*/
|
||||
'@stylistic/jsx-closing-bracket-location': JsxClosingBracketLocationRuleOptions;
|
||||
/**
|
||||
* Enforce closing tag location for multiline JSX
|
||||
* @see https://eslint.style/rules/jsx-closing-tag-location
|
||||
*/
|
||||
'@stylistic/jsx-closing-tag-location': JsxClosingTagLocationRuleOptions;
|
||||
/**
|
||||
* Disallow unnecessary JSX expressions when literals alone are sufficient or enforce JSX expressions on literals in JSX children or attributes
|
||||
* @see https://eslint.style/rules/jsx-curly-brace-presence
|
||||
*/
|
||||
'@stylistic/jsx-curly-brace-presence': JsxCurlyBracePresenceRuleOptions;
|
||||
/**
|
||||
* Enforce consistent linebreaks in curly braces in JSX attributes and expressions
|
||||
* @see https://eslint.style/rules/jsx-curly-newline
|
||||
*/
|
||||
'@stylistic/jsx-curly-newline': JsxCurlyNewlineRuleOptions;
|
||||
/**
|
||||
* Enforce or disallow spaces inside of curly braces in JSX attributes and expressions
|
||||
* @see https://eslint.style/rules/jsx-curly-spacing
|
||||
*/
|
||||
'@stylistic/jsx-curly-spacing': JsxCurlySpacingRuleOptions;
|
||||
/**
|
||||
* Enforce or disallow spaces around equal signs in JSX attributes
|
||||
* @see https://eslint.style/rules/jsx-equals-spacing
|
||||
*/
|
||||
'@stylistic/jsx-equals-spacing': JsxEqualsSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce proper position of the first property in JSX
|
||||
* @see https://eslint.style/rules/jsx-first-prop-new-line
|
||||
*/
|
||||
'@stylistic/jsx-first-prop-new-line': JsxFirstPropNewLineRuleOptions;
|
||||
/**
|
||||
* Enforce line breaks before and after JSX elements when they are used as arguments to a function.
|
||||
* @see https://eslint.style/rules/jsx-function-call-newline
|
||||
*/
|
||||
'@stylistic/jsx-function-call-newline': JsxFunctionCallNewlineRuleOptions;
|
||||
/**
|
||||
* Enforce JSX indentation. Deprecated, use `indent` rule instead.
|
||||
* @see https://eslint.style/rules/jsx-indent
|
||||
*/
|
||||
'@stylistic/jsx-indent': JsxIndentRuleOptions;
|
||||
/**
|
||||
* Enforce props indentation in JSX
|
||||
* @see https://eslint.style/rules/jsx-indent-props
|
||||
*/
|
||||
'@stylistic/jsx-indent-props': JsxIndentPropsRuleOptions;
|
||||
/**
|
||||
* Enforce maximum of props on a single line in JSX
|
||||
* @see https://eslint.style/rules/jsx-max-props-per-line
|
||||
*/
|
||||
'@stylistic/jsx-max-props-per-line': JsxMaxPropsPerLineRuleOptions;
|
||||
/**
|
||||
* Require or prevent a new line after jsx elements and expressions.
|
||||
* @see https://eslint.style/rules/jsx-newline
|
||||
*/
|
||||
'@stylistic/jsx-newline': JsxNewlineRuleOptions;
|
||||
/**
|
||||
* Require one JSX element per line
|
||||
* @see https://eslint.style/rules/jsx-one-expression-per-line
|
||||
*/
|
||||
'@stylistic/jsx-one-expression-per-line': JsxOneExpressionPerLineRuleOptions;
|
||||
/**
|
||||
* Enforce PascalCase for user-defined JSX components
|
||||
* @see https://eslint.style/rules/jsx-pascal-case
|
||||
*/
|
||||
'@stylistic/jsx-pascal-case': JsxPascalCaseRuleOptions;
|
||||
/**
|
||||
* Disallow multiple spaces between inline JSX props. Deprecated, use `no-multi-spaces` rule instead.
|
||||
* @see https://eslint.style/rules/jsx-props-no-multi-spaces
|
||||
*/
|
||||
'@stylistic/jsx-props-no-multi-spaces': JsxPropsNoMultiSpacesRuleOptions;
|
||||
/**
|
||||
* Enforce consistent line break styles for JSX props
|
||||
* @see https://eslint.style/rules/jsx-props-style
|
||||
*/
|
||||
'@stylistic/exp-jsx-props-style': JsxPropsStyleRuleOptions;
|
||||
/**
|
||||
* Enforce the consistent use of either double or single quotes in JSX attributes
|
||||
* @see https://eslint.style/rules/jsx-quotes
|
||||
*/
|
||||
'@stylistic/jsx-quotes': JsxQuotesRuleOptions;
|
||||
/**
|
||||
* Disallow extra closing tags for components without children
|
||||
* @see https://eslint.style/rules/jsx-self-closing-comp
|
||||
*/
|
||||
'@stylistic/jsx-self-closing-comp': JsxSelfClosingCompRuleOptions;
|
||||
/**
|
||||
* Enforce props alphabetical sorting
|
||||
* @see https://eslint.style/rules/jsx-sort-props
|
||||
*/
|
||||
'@stylistic/jsx-sort-props': JsxSortPropsRuleOptions;
|
||||
/**
|
||||
* Enforce whitespace in and around the JSX opening and closing brackets
|
||||
* @see https://eslint.style/rules/jsx-tag-spacing
|
||||
*/
|
||||
'@stylistic/jsx-tag-spacing': JsxTagSpacingRuleOptions;
|
||||
/**
|
||||
* Disallow missing parentheses around multiline JSX
|
||||
* @see https://eslint.style/rules/jsx-wrap-multilines
|
||||
*/
|
||||
'@stylistic/jsx-wrap-multilines': JsxWrapMultilinesRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing between property names and type annotations in types and interfaces
|
||||
* @see https://eslint.style/rules/key-spacing
|
||||
*/
|
||||
'@stylistic/key-spacing': KeySpacingRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing before and after keywords
|
||||
* @see https://eslint.style/rules/keyword-spacing
|
||||
*/
|
||||
'@stylistic/keyword-spacing': KeywordSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce position of line comments
|
||||
* @see https://eslint.style/rules/line-comment-position
|
||||
*/
|
||||
'@stylistic/line-comment-position': LineCommentPositionRuleOptions;
|
||||
/**
|
||||
* Enforce consistent linebreak style
|
||||
* @see https://eslint.style/rules/linebreak-style
|
||||
*/
|
||||
'@stylistic/linebreak-style': LinebreakStyleRuleOptions;
|
||||
/**
|
||||
* Require empty lines around comments
|
||||
* @see https://eslint.style/rules/lines-around-comment
|
||||
*/
|
||||
'@stylistic/lines-around-comment': LinesAroundCommentRuleOptions;
|
||||
/**
|
||||
* Require or disallow an empty line between class members
|
||||
* @see https://eslint.style/rules/lines-between-class-members
|
||||
*/
|
||||
'@stylistic/lines-between-class-members': LinesBetweenClassMembersRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing and line break styles inside brackets.
|
||||
* @see https://eslint.style/rules/list-style
|
||||
*/
|
||||
'@stylistic/exp-list-style': ListStyleRuleOptions;
|
||||
/**
|
||||
* Enforce a maximum line length
|
||||
* @see https://eslint.style/rules/max-len
|
||||
*/
|
||||
'@stylistic/max-len': MaxLenRuleOptions;
|
||||
/**
|
||||
* Enforce a maximum number of statements allowed per line
|
||||
* @see https://eslint.style/rules/max-statements-per-line
|
||||
*/
|
||||
'@stylistic/max-statements-per-line': MaxStatementsPerLineRuleOptions;
|
||||
/**
|
||||
* Require a specific member delimiter style for interfaces and type literals
|
||||
* @see https://eslint.style/rules/member-delimiter-style
|
||||
*/
|
||||
'@stylistic/member-delimiter-style': MemberDelimiterStyleRuleOptions;
|
||||
/**
|
||||
* Enforce a particular style for multiline comments
|
||||
* @see https://eslint.style/rules/multiline-comment-style
|
||||
*/
|
||||
'@stylistic/multiline-comment-style': MultilineCommentStyleRuleOptions;
|
||||
/**
|
||||
* Enforce newlines between operands of ternary expressions
|
||||
* @see https://eslint.style/rules/multiline-ternary
|
||||
*/
|
||||
'@stylistic/multiline-ternary': MultilineTernaryRuleOptions;
|
||||
/**
|
||||
* Enforce or disallow parentheses when invoking a constructor with no arguments
|
||||
* @see https://eslint.style/rules/new-parens
|
||||
*/
|
||||
'@stylistic/new-parens': NewParensRuleOptions;
|
||||
/**
|
||||
* Require a newline after each call in a method chain
|
||||
* @see https://eslint.style/rules/newline-per-chained-call
|
||||
*/
|
||||
'@stylistic/newline-per-chained-call': NewlinePerChainedCallRuleOptions;
|
||||
/**
|
||||
* Disallow arrow functions where they could be confused with comparisons
|
||||
* @see https://eslint.style/rules/no-confusing-arrow
|
||||
*/
|
||||
'@stylistic/no-confusing-arrow': NoConfusingArrowRuleOptions;
|
||||
/**
|
||||
* Disallow unnecessary parentheses
|
||||
* @see https://eslint.style/rules/no-extra-parens
|
||||
*/
|
||||
'@stylistic/no-extra-parens': NoExtraParensRuleOptions;
|
||||
/**
|
||||
* Disallow unnecessary semicolons
|
||||
* @see https://eslint.style/rules/no-extra-semi
|
||||
*/
|
||||
'@stylistic/no-extra-semi': NoExtraSemiRuleOptions;
|
||||
/**
|
||||
* Disallow leading or trailing decimal points in numeric literals
|
||||
* @see https://eslint.style/rules/no-floating-decimal
|
||||
*/
|
||||
'@stylistic/no-floating-decimal': NoFloatingDecimalRuleOptions;
|
||||
/**
|
||||
* Disallow mixed binary operators
|
||||
* @see https://eslint.style/rules/no-mixed-operators
|
||||
*/
|
||||
'@stylistic/no-mixed-operators': NoMixedOperatorsRuleOptions;
|
||||
/**
|
||||
* Disallow mixed spaces and tabs for indentation
|
||||
* @see https://eslint.style/rules/no-mixed-spaces-and-tabs
|
||||
*/
|
||||
'@stylistic/no-mixed-spaces-and-tabs': NoMixedSpacesAndTabsRuleOptions;
|
||||
/**
|
||||
* Disallow multiple spaces
|
||||
* @see https://eslint.style/rules/no-multi-spaces
|
||||
*/
|
||||
'@stylistic/no-multi-spaces': NoMultiSpacesRuleOptions;
|
||||
/**
|
||||
* Disallow multiple empty lines
|
||||
* @see https://eslint.style/rules/no-multiple-empty-lines
|
||||
*/
|
||||
'@stylistic/no-multiple-empty-lines': NoMultipleEmptyLinesRuleOptions;
|
||||
/**
|
||||
* Disallow all tabs
|
||||
* @see https://eslint.style/rules/no-tabs
|
||||
*/
|
||||
'@stylistic/no-tabs': NoTabsRuleOptions;
|
||||
/**
|
||||
* Disallow trailing whitespace at the end of lines
|
||||
* @see https://eslint.style/rules/no-trailing-spaces
|
||||
*/
|
||||
'@stylistic/no-trailing-spaces': NoTrailingSpacesRuleOptions;
|
||||
/**
|
||||
* Disallow whitespace before properties
|
||||
* @see https://eslint.style/rules/no-whitespace-before-property
|
||||
*/
|
||||
'@stylistic/no-whitespace-before-property': NoWhitespaceBeforePropertyRuleOptions;
|
||||
/**
|
||||
* Enforce the location of single-line statements
|
||||
* @see https://eslint.style/rules/nonblock-statement-body-position
|
||||
*/
|
||||
'@stylistic/nonblock-statement-body-position': NonblockStatementBodyPositionRuleOptions;
|
||||
/**
|
||||
* Enforce consistent line breaks after opening and before closing braces
|
||||
* @see https://eslint.style/rules/object-curly-newline
|
||||
*/
|
||||
'@stylistic/object-curly-newline': ObjectCurlyNewlineRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing inside braces
|
||||
* @see https://eslint.style/rules/object-curly-spacing
|
||||
*/
|
||||
'@stylistic/object-curly-spacing': ObjectCurlySpacingRuleOptions;
|
||||
/**
|
||||
* Enforce placing object properties on separate lines
|
||||
* @see https://eslint.style/rules/object-property-newline
|
||||
*/
|
||||
'@stylistic/object-property-newline': ObjectPropertyNewlineRuleOptions;
|
||||
/**
|
||||
* Require or disallow newlines around variable declarations
|
||||
* @see https://eslint.style/rules/one-var-declaration-per-line
|
||||
*/
|
||||
'@stylistic/one-var-declaration-per-line': OneVarDeclarationPerLineRuleOptions;
|
||||
/**
|
||||
* Enforce consistent linebreak style for operators
|
||||
* @see https://eslint.style/rules/operator-linebreak
|
||||
*/
|
||||
'@stylistic/operator-linebreak': OperatorLinebreakRuleOptions;
|
||||
/**
|
||||
* Require or disallow padding within blocks
|
||||
* @see https://eslint.style/rules/padded-blocks
|
||||
*/
|
||||
'@stylistic/padded-blocks': PaddedBlocksRuleOptions;
|
||||
/**
|
||||
* Require or disallow padding lines between statements
|
||||
* @see https://eslint.style/rules/padding-line-between-statements
|
||||
*/
|
||||
'@stylistic/padding-line-between-statements': PaddingLineBetweenStatementsRuleOptions;
|
||||
/**
|
||||
* Require quotes around object literal, type literal, interfaces and enums property names
|
||||
* @see https://eslint.style/rules/quote-props
|
||||
*/
|
||||
'@stylistic/quote-props': QuotePropsRuleOptions;
|
||||
/**
|
||||
* Enforce the consistent use of either backticks, double, or single quotes
|
||||
* @see https://eslint.style/rules/quotes
|
||||
*/
|
||||
'@stylistic/quotes': QuotesRuleOptions;
|
||||
/**
|
||||
* Enforce spacing between rest and spread operators and their expressions
|
||||
* @see https://eslint.style/rules/rest-spread-spacing
|
||||
*/
|
||||
'@stylistic/rest-spread-spacing': RestSpreadSpacingRuleOptions;
|
||||
/**
|
||||
* Require or disallow semicolons instead of ASI
|
||||
* @see https://eslint.style/rules/semi
|
||||
*/
|
||||
'@stylistic/semi': SemiRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing before and after semicolons
|
||||
* @see https://eslint.style/rules/semi-spacing
|
||||
*/
|
||||
'@stylistic/semi-spacing': SemiSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce location of semicolons
|
||||
* @see https://eslint.style/rules/semi-style
|
||||
*/
|
||||
'@stylistic/semi-style': SemiStyleRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing before blocks
|
||||
* @see https://eslint.style/rules/space-before-blocks
|
||||
*/
|
||||
'@stylistic/space-before-blocks': SpaceBeforeBlocksRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing before function parenthesis
|
||||
* @see https://eslint.style/rules/space-before-function-paren
|
||||
*/
|
||||
'@stylistic/space-before-function-paren': SpaceBeforeFunctionParenRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing inside parentheses
|
||||
* @see https://eslint.style/rules/space-in-parens
|
||||
*/
|
||||
'@stylistic/space-in-parens': SpaceInParensRuleOptions;
|
||||
/**
|
||||
* Require spacing around infix operators
|
||||
* @see https://eslint.style/rules/space-infix-ops
|
||||
*/
|
||||
'@stylistic/space-infix-ops': SpaceInfixOpsRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing before or after unary operators
|
||||
* @see https://eslint.style/rules/space-unary-ops
|
||||
*/
|
||||
'@stylistic/space-unary-ops': SpaceUnaryOpsRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing after the `//` or `/*` in a comment
|
||||
* @see https://eslint.style/rules/spaced-comment
|
||||
*/
|
||||
'@stylistic/spaced-comment': SpacedCommentRuleOptions;
|
||||
/**
|
||||
* Enforce spacing around colons of switch statements
|
||||
* @see https://eslint.style/rules/switch-colon-spacing
|
||||
*/
|
||||
'@stylistic/switch-colon-spacing': SwitchColonSpacingRuleOptions;
|
||||
/**
|
||||
* Require or disallow spacing around embedded expressions of template strings
|
||||
* @see https://eslint.style/rules/template-curly-spacing
|
||||
*/
|
||||
'@stylistic/template-curly-spacing': TemplateCurlySpacingRuleOptions;
|
||||
/**
|
||||
* Require or disallow spacing between template tags and their literals
|
||||
* @see https://eslint.style/rules/template-tag-spacing
|
||||
*/
|
||||
'@stylistic/template-tag-spacing': TemplateTagSpacingRuleOptions;
|
||||
/**
|
||||
* Require consistent spacing around type annotations
|
||||
* @see https://eslint.style/rules/type-annotation-spacing
|
||||
*/
|
||||
'@stylistic/type-annotation-spacing': TypeAnnotationSpacingRuleOptions;
|
||||
/**
|
||||
* Enforces consistent spacing inside TypeScript type generics
|
||||
* @see https://eslint.style/rules/type-generic-spacing
|
||||
*/
|
||||
'@stylistic/type-generic-spacing': TypeGenericSpacingRuleOptions;
|
||||
/**
|
||||
* Expect space before the type declaration in the named tuple
|
||||
* @see https://eslint.style/rules/type-named-tuple-spacing
|
||||
*/
|
||||
'@stylistic/type-named-tuple-spacing': TypeNamedTupleSpacingRuleOptions;
|
||||
/**
|
||||
* Require parentheses around immediate `function` invocations
|
||||
* @see https://eslint.style/rules/wrap-iife
|
||||
*/
|
||||
'@stylistic/wrap-iife': WrapIifeRuleOptions;
|
||||
/**
|
||||
* Require parenthesis around regex literals
|
||||
* @see https://eslint.style/rules/wrap-regex
|
||||
*/
|
||||
'@stylistic/wrap-regex': WrapRegexRuleOptions;
|
||||
/**
|
||||
* Require or disallow spacing around the `*` in `yield*` expressions
|
||||
* @see https://eslint.style/rules/yield-star-spacing
|
||||
*/
|
||||
'@stylistic/yield-star-spacing': YieldStarSpacingRuleOptions;
|
||||
}
|
||||
interface UnprefixedRuleOptions {
|
||||
/**
|
||||
* Enforce linebreaks after opening and before closing array brackets
|
||||
* @see https://eslint.style/rules/array-bracket-newline
|
||||
*/
|
||||
'array-bracket-newline': ArrayBracketNewlineRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing inside array brackets
|
||||
* @see https://eslint.style/rules/array-bracket-spacing
|
||||
*/
|
||||
'array-bracket-spacing': ArrayBracketSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce line breaks after each array element
|
||||
* @see https://eslint.style/rules/array-element-newline
|
||||
*/
|
||||
'array-element-newline': ArrayElementNewlineRuleOptions;
|
||||
/**
|
||||
* Require parentheses around arrow function arguments
|
||||
* @see https://eslint.style/rules/arrow-parens
|
||||
*/
|
||||
'arrow-parens': ArrowParensRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing before and after the arrow in arrow functions
|
||||
* @see https://eslint.style/rules/arrow-spacing
|
||||
*/
|
||||
'arrow-spacing': ArrowSpacingRuleOptions;
|
||||
/**
|
||||
* Disallow or enforce spaces inside of blocks after opening block and before closing block
|
||||
* @see https://eslint.style/rules/block-spacing
|
||||
*/
|
||||
'block-spacing': BlockSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce consistent brace style for blocks
|
||||
* @see https://eslint.style/rules/brace-style
|
||||
*/
|
||||
'brace-style': BraceStyleRuleOptions;
|
||||
/**
|
||||
* Require or disallow trailing commas
|
||||
* @see https://eslint.style/rules/comma-dangle
|
||||
*/
|
||||
'comma-dangle': CommaDangleRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing before and after commas
|
||||
* @see https://eslint.style/rules/comma-spacing
|
||||
*/
|
||||
'comma-spacing': CommaSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce consistent comma style
|
||||
* @see https://eslint.style/rules/comma-style
|
||||
*/
|
||||
'comma-style': CommaStyleRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing inside computed property brackets
|
||||
* @see https://eslint.style/rules/computed-property-spacing
|
||||
*/
|
||||
'computed-property-spacing': ComputedPropertySpacingRuleOptions;
|
||||
/**
|
||||
* Enforce consistent line breaks after opening and before closing braces
|
||||
* @see https://eslint.style/rules/curly-newline
|
||||
*/
|
||||
'curly-newline': CurlyNewlineRuleOptions;
|
||||
/**
|
||||
* Enforce consistent newlines before and after dots
|
||||
* @see https://eslint.style/rules/dot-location
|
||||
*/
|
||||
'dot-location': DotLocationRuleOptions;
|
||||
/**
|
||||
* Require or disallow newline at the end of files
|
||||
* @see https://eslint.style/rules/eol-last
|
||||
*/
|
||||
'eol-last': EolLastRuleOptions;
|
||||
/**
|
||||
* Enforce line breaks between arguments of a function call
|
||||
* @see https://eslint.style/rules/function-call-argument-newline
|
||||
*/
|
||||
'function-call-argument-newline': FunctionCallArgumentNewlineRuleOptions;
|
||||
/**
|
||||
* Require or disallow spacing between function identifiers and their invocations
|
||||
* @see https://eslint.style/rules/function-call-spacing
|
||||
*/
|
||||
'function-call-spacing': FunctionCallSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce consistent line breaks inside function parentheses
|
||||
* @see https://eslint.style/rules/function-paren-newline
|
||||
*/
|
||||
'function-paren-newline': FunctionParenNewlineRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing around `*` operators in generator functions
|
||||
* @see https://eslint.style/rules/generator-star-spacing
|
||||
*/
|
||||
'generator-star-spacing': GeneratorStarSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce the location of arrow function bodies
|
||||
* @see https://eslint.style/rules/implicit-arrow-linebreak
|
||||
*/
|
||||
'implicit-arrow-linebreak': ImplicitArrowLinebreakRuleOptions;
|
||||
/**
|
||||
* Enforce consistent indentation
|
||||
* @see https://eslint.style/rules/indent
|
||||
*/
|
||||
'indent': IndentRuleOptions;
|
||||
/**
|
||||
* Indentation for binary operators
|
||||
* @see https://eslint.style/rules/indent-binary-ops
|
||||
*/
|
||||
'indent-binary-ops': IndentBinaryOpsRuleOptions;
|
||||
/**
|
||||
* Enforce or disallow spaces inside of curly braces in JSX attributes and expressions
|
||||
* @see https://eslint.style/rules/jsx-child-element-spacing
|
||||
*/
|
||||
'jsx-child-element-spacing': JsxChildElementSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce closing bracket location in JSX
|
||||
* @see https://eslint.style/rules/jsx-closing-bracket-location
|
||||
*/
|
||||
'jsx-closing-bracket-location': JsxClosingBracketLocationRuleOptions;
|
||||
/**
|
||||
* Enforce closing tag location for multiline JSX
|
||||
* @see https://eslint.style/rules/jsx-closing-tag-location
|
||||
*/
|
||||
'jsx-closing-tag-location': JsxClosingTagLocationRuleOptions;
|
||||
/**
|
||||
* Disallow unnecessary JSX expressions when literals alone are sufficient or enforce JSX expressions on literals in JSX children or attributes
|
||||
* @see https://eslint.style/rules/jsx-curly-brace-presence
|
||||
*/
|
||||
'jsx-curly-brace-presence': JsxCurlyBracePresenceRuleOptions;
|
||||
/**
|
||||
* Enforce consistent linebreaks in curly braces in JSX attributes and expressions
|
||||
* @see https://eslint.style/rules/jsx-curly-newline
|
||||
*/
|
||||
'jsx-curly-newline': JsxCurlyNewlineRuleOptions;
|
||||
/**
|
||||
* Enforce or disallow spaces inside of curly braces in JSX attributes and expressions
|
||||
* @see https://eslint.style/rules/jsx-curly-spacing
|
||||
*/
|
||||
'jsx-curly-spacing': JsxCurlySpacingRuleOptions;
|
||||
/**
|
||||
* Enforce or disallow spaces around equal signs in JSX attributes
|
||||
* @see https://eslint.style/rules/jsx-equals-spacing
|
||||
*/
|
||||
'jsx-equals-spacing': JsxEqualsSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce proper position of the first property in JSX
|
||||
* @see https://eslint.style/rules/jsx-first-prop-new-line
|
||||
*/
|
||||
'jsx-first-prop-new-line': JsxFirstPropNewLineRuleOptions;
|
||||
/**
|
||||
* Enforce line breaks before and after JSX elements when they are used as arguments to a function.
|
||||
* @see https://eslint.style/rules/jsx-function-call-newline
|
||||
*/
|
||||
'jsx-function-call-newline': JsxFunctionCallNewlineRuleOptions;
|
||||
/**
|
||||
* Enforce JSX indentation. Deprecated, use `indent` rule instead.
|
||||
* @see https://eslint.style/rules/jsx-indent
|
||||
*/
|
||||
'jsx-indent': JsxIndentRuleOptions;
|
||||
/**
|
||||
* Enforce props indentation in JSX
|
||||
* @see https://eslint.style/rules/jsx-indent-props
|
||||
*/
|
||||
'jsx-indent-props': JsxIndentPropsRuleOptions;
|
||||
/**
|
||||
* Enforce maximum of props on a single line in JSX
|
||||
* @see https://eslint.style/rules/jsx-max-props-per-line
|
||||
*/
|
||||
'jsx-max-props-per-line': JsxMaxPropsPerLineRuleOptions;
|
||||
/**
|
||||
* Require or prevent a new line after jsx elements and expressions.
|
||||
* @see https://eslint.style/rules/jsx-newline
|
||||
*/
|
||||
'jsx-newline': JsxNewlineRuleOptions;
|
||||
/**
|
||||
* Require one JSX element per line
|
||||
* @see https://eslint.style/rules/jsx-one-expression-per-line
|
||||
*/
|
||||
'jsx-one-expression-per-line': JsxOneExpressionPerLineRuleOptions;
|
||||
/**
|
||||
* Enforce PascalCase for user-defined JSX components
|
||||
* @see https://eslint.style/rules/jsx-pascal-case
|
||||
*/
|
||||
'jsx-pascal-case': JsxPascalCaseRuleOptions;
|
||||
/**
|
||||
* Disallow multiple spaces between inline JSX props. Deprecated, use `no-multi-spaces` rule instead.
|
||||
* @see https://eslint.style/rules/jsx-props-no-multi-spaces
|
||||
*/
|
||||
'jsx-props-no-multi-spaces': JsxPropsNoMultiSpacesRuleOptions;
|
||||
/**
|
||||
* Enforce consistent line break styles for JSX props
|
||||
* @see https://eslint.style/rules/jsx-props-style
|
||||
*/
|
||||
'exp-jsx-props-style': JsxPropsStyleRuleOptions;
|
||||
/**
|
||||
* Enforce the consistent use of either double or single quotes in JSX attributes
|
||||
* @see https://eslint.style/rules/jsx-quotes
|
||||
*/
|
||||
'jsx-quotes': JsxQuotesRuleOptions;
|
||||
/**
|
||||
* Disallow extra closing tags for components without children
|
||||
* @see https://eslint.style/rules/jsx-self-closing-comp
|
||||
*/
|
||||
'jsx-self-closing-comp': JsxSelfClosingCompRuleOptions;
|
||||
/**
|
||||
* Enforce props alphabetical sorting
|
||||
* @see https://eslint.style/rules/jsx-sort-props
|
||||
*/
|
||||
'jsx-sort-props': JsxSortPropsRuleOptions;
|
||||
/**
|
||||
* Enforce whitespace in and around the JSX opening and closing brackets
|
||||
* @see https://eslint.style/rules/jsx-tag-spacing
|
||||
*/
|
||||
'jsx-tag-spacing': JsxTagSpacingRuleOptions;
|
||||
/**
|
||||
* Disallow missing parentheses around multiline JSX
|
||||
* @see https://eslint.style/rules/jsx-wrap-multilines
|
||||
*/
|
||||
'jsx-wrap-multilines': JsxWrapMultilinesRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing between property names and type annotations in types and interfaces
|
||||
* @see https://eslint.style/rules/key-spacing
|
||||
*/
|
||||
'key-spacing': KeySpacingRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing before and after keywords
|
||||
* @see https://eslint.style/rules/keyword-spacing
|
||||
*/
|
||||
'keyword-spacing': KeywordSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce position of line comments
|
||||
* @see https://eslint.style/rules/line-comment-position
|
||||
*/
|
||||
'line-comment-position': LineCommentPositionRuleOptions;
|
||||
/**
|
||||
* Enforce consistent linebreak style
|
||||
* @see https://eslint.style/rules/linebreak-style
|
||||
*/
|
||||
'linebreak-style': LinebreakStyleRuleOptions;
|
||||
/**
|
||||
* Require empty lines around comments
|
||||
* @see https://eslint.style/rules/lines-around-comment
|
||||
*/
|
||||
'lines-around-comment': LinesAroundCommentRuleOptions;
|
||||
/**
|
||||
* Require or disallow an empty line between class members
|
||||
* @see https://eslint.style/rules/lines-between-class-members
|
||||
*/
|
||||
'lines-between-class-members': LinesBetweenClassMembersRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing and line break styles inside brackets.
|
||||
* @see https://eslint.style/rules/list-style
|
||||
*/
|
||||
'exp-list-style': ListStyleRuleOptions;
|
||||
/**
|
||||
* Enforce a maximum line length
|
||||
* @see https://eslint.style/rules/max-len
|
||||
*/
|
||||
'max-len': MaxLenRuleOptions;
|
||||
/**
|
||||
* Enforce a maximum number of statements allowed per line
|
||||
* @see https://eslint.style/rules/max-statements-per-line
|
||||
*/
|
||||
'max-statements-per-line': MaxStatementsPerLineRuleOptions;
|
||||
/**
|
||||
* Require a specific member delimiter style for interfaces and type literals
|
||||
* @see https://eslint.style/rules/member-delimiter-style
|
||||
*/
|
||||
'member-delimiter-style': MemberDelimiterStyleRuleOptions;
|
||||
/**
|
||||
* Enforce a particular style for multiline comments
|
||||
* @see https://eslint.style/rules/multiline-comment-style
|
||||
*/
|
||||
'multiline-comment-style': MultilineCommentStyleRuleOptions;
|
||||
/**
|
||||
* Enforce newlines between operands of ternary expressions
|
||||
* @see https://eslint.style/rules/multiline-ternary
|
||||
*/
|
||||
'multiline-ternary': MultilineTernaryRuleOptions;
|
||||
/**
|
||||
* Enforce or disallow parentheses when invoking a constructor with no arguments
|
||||
* @see https://eslint.style/rules/new-parens
|
||||
*/
|
||||
'new-parens': NewParensRuleOptions;
|
||||
/**
|
||||
* Require a newline after each call in a method chain
|
||||
* @see https://eslint.style/rules/newline-per-chained-call
|
||||
*/
|
||||
'newline-per-chained-call': NewlinePerChainedCallRuleOptions;
|
||||
/**
|
||||
* Disallow arrow functions where they could be confused with comparisons
|
||||
* @see https://eslint.style/rules/no-confusing-arrow
|
||||
*/
|
||||
'no-confusing-arrow': NoConfusingArrowRuleOptions;
|
||||
/**
|
||||
* Disallow unnecessary parentheses
|
||||
* @see https://eslint.style/rules/no-extra-parens
|
||||
*/
|
||||
'no-extra-parens': NoExtraParensRuleOptions;
|
||||
/**
|
||||
* Disallow unnecessary semicolons
|
||||
* @see https://eslint.style/rules/no-extra-semi
|
||||
*/
|
||||
'no-extra-semi': NoExtraSemiRuleOptions;
|
||||
/**
|
||||
* Disallow leading or trailing decimal points in numeric literals
|
||||
* @see https://eslint.style/rules/no-floating-decimal
|
||||
*/
|
||||
'no-floating-decimal': NoFloatingDecimalRuleOptions;
|
||||
/**
|
||||
* Disallow mixed binary operators
|
||||
* @see https://eslint.style/rules/no-mixed-operators
|
||||
*/
|
||||
'no-mixed-operators': NoMixedOperatorsRuleOptions;
|
||||
/**
|
||||
* Disallow mixed spaces and tabs for indentation
|
||||
* @see https://eslint.style/rules/no-mixed-spaces-and-tabs
|
||||
*/
|
||||
'no-mixed-spaces-and-tabs': NoMixedSpacesAndTabsRuleOptions;
|
||||
/**
|
||||
* Disallow multiple spaces
|
||||
* @see https://eslint.style/rules/no-multi-spaces
|
||||
*/
|
||||
'no-multi-spaces': NoMultiSpacesRuleOptions;
|
||||
/**
|
||||
* Disallow multiple empty lines
|
||||
* @see https://eslint.style/rules/no-multiple-empty-lines
|
||||
*/
|
||||
'no-multiple-empty-lines': NoMultipleEmptyLinesRuleOptions;
|
||||
/**
|
||||
* Disallow all tabs
|
||||
* @see https://eslint.style/rules/no-tabs
|
||||
*/
|
||||
'no-tabs': NoTabsRuleOptions;
|
||||
/**
|
||||
* Disallow trailing whitespace at the end of lines
|
||||
* @see https://eslint.style/rules/no-trailing-spaces
|
||||
*/
|
||||
'no-trailing-spaces': NoTrailingSpacesRuleOptions;
|
||||
/**
|
||||
* Disallow whitespace before properties
|
||||
* @see https://eslint.style/rules/no-whitespace-before-property
|
||||
*/
|
||||
'no-whitespace-before-property': NoWhitespaceBeforePropertyRuleOptions;
|
||||
/**
|
||||
* Enforce the location of single-line statements
|
||||
* @see https://eslint.style/rules/nonblock-statement-body-position
|
||||
*/
|
||||
'nonblock-statement-body-position': NonblockStatementBodyPositionRuleOptions;
|
||||
/**
|
||||
* Enforce consistent line breaks after opening and before closing braces
|
||||
* @see https://eslint.style/rules/object-curly-newline
|
||||
*/
|
||||
'object-curly-newline': ObjectCurlyNewlineRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing inside braces
|
||||
* @see https://eslint.style/rules/object-curly-spacing
|
||||
*/
|
||||
'object-curly-spacing': ObjectCurlySpacingRuleOptions;
|
||||
/**
|
||||
* Enforce placing object properties on separate lines
|
||||
* @see https://eslint.style/rules/object-property-newline
|
||||
*/
|
||||
'object-property-newline': ObjectPropertyNewlineRuleOptions;
|
||||
/**
|
||||
* Require or disallow newlines around variable declarations
|
||||
* @see https://eslint.style/rules/one-var-declaration-per-line
|
||||
*/
|
||||
'one-var-declaration-per-line': OneVarDeclarationPerLineRuleOptions;
|
||||
/**
|
||||
* Enforce consistent linebreak style for operators
|
||||
* @see https://eslint.style/rules/operator-linebreak
|
||||
*/
|
||||
'operator-linebreak': OperatorLinebreakRuleOptions;
|
||||
/**
|
||||
* Require or disallow padding within blocks
|
||||
* @see https://eslint.style/rules/padded-blocks
|
||||
*/
|
||||
'padded-blocks': PaddedBlocksRuleOptions;
|
||||
/**
|
||||
* Require or disallow padding lines between statements
|
||||
* @see https://eslint.style/rules/padding-line-between-statements
|
||||
*/
|
||||
'padding-line-between-statements': PaddingLineBetweenStatementsRuleOptions;
|
||||
/**
|
||||
* Require quotes around object literal, type literal, interfaces and enums property names
|
||||
* @see https://eslint.style/rules/quote-props
|
||||
*/
|
||||
'quote-props': QuotePropsRuleOptions;
|
||||
/**
|
||||
* Enforce the consistent use of either backticks, double, or single quotes
|
||||
* @see https://eslint.style/rules/quotes
|
||||
*/
|
||||
'quotes': QuotesRuleOptions;
|
||||
/**
|
||||
* Enforce spacing between rest and spread operators and their expressions
|
||||
* @see https://eslint.style/rules/rest-spread-spacing
|
||||
*/
|
||||
'rest-spread-spacing': RestSpreadSpacingRuleOptions;
|
||||
/**
|
||||
* Require or disallow semicolons instead of ASI
|
||||
* @see https://eslint.style/rules/semi
|
||||
*/
|
||||
'semi': SemiRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing before and after semicolons
|
||||
* @see https://eslint.style/rules/semi-spacing
|
||||
*/
|
||||
'semi-spacing': SemiSpacingRuleOptions;
|
||||
/**
|
||||
* Enforce location of semicolons
|
||||
* @see https://eslint.style/rules/semi-style
|
||||
*/
|
||||
'semi-style': SemiStyleRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing before blocks
|
||||
* @see https://eslint.style/rules/space-before-blocks
|
||||
*/
|
||||
'space-before-blocks': SpaceBeforeBlocksRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing before function parenthesis
|
||||
* @see https://eslint.style/rules/space-before-function-paren
|
||||
*/
|
||||
'space-before-function-paren': SpaceBeforeFunctionParenRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing inside parentheses
|
||||
* @see https://eslint.style/rules/space-in-parens
|
||||
*/
|
||||
'space-in-parens': SpaceInParensRuleOptions;
|
||||
/**
|
||||
* Require spacing around infix operators
|
||||
* @see https://eslint.style/rules/space-infix-ops
|
||||
*/
|
||||
'space-infix-ops': SpaceInfixOpsRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing before or after unary operators
|
||||
* @see https://eslint.style/rules/space-unary-ops
|
||||
*/
|
||||
'space-unary-ops': SpaceUnaryOpsRuleOptions;
|
||||
/**
|
||||
* Enforce consistent spacing after the `//` or `/*` in a comment
|
||||
* @see https://eslint.style/rules/spaced-comment
|
||||
*/
|
||||
'spaced-comment': SpacedCommentRuleOptions;
|
||||
/**
|
||||
* Enforce spacing around colons of switch statements
|
||||
* @see https://eslint.style/rules/switch-colon-spacing
|
||||
*/
|
||||
'switch-colon-spacing': SwitchColonSpacingRuleOptions;
|
||||
/**
|
||||
* Require or disallow spacing around embedded expressions of template strings
|
||||
* @see https://eslint.style/rules/template-curly-spacing
|
||||
*/
|
||||
'template-curly-spacing': TemplateCurlySpacingRuleOptions;
|
||||
/**
|
||||
* Require or disallow spacing between template tags and their literals
|
||||
* @see https://eslint.style/rules/template-tag-spacing
|
||||
*/
|
||||
'template-tag-spacing': TemplateTagSpacingRuleOptions;
|
||||
/**
|
||||
* Require consistent spacing around type annotations
|
||||
* @see https://eslint.style/rules/type-annotation-spacing
|
||||
*/
|
||||
'type-annotation-spacing': TypeAnnotationSpacingRuleOptions;
|
||||
/**
|
||||
* Enforces consistent spacing inside TypeScript type generics
|
||||
* @see https://eslint.style/rules/type-generic-spacing
|
||||
*/
|
||||
'type-generic-spacing': TypeGenericSpacingRuleOptions;
|
||||
/**
|
||||
* Expect space before the type declaration in the named tuple
|
||||
* @see https://eslint.style/rules/type-named-tuple-spacing
|
||||
*/
|
||||
'type-named-tuple-spacing': TypeNamedTupleSpacingRuleOptions;
|
||||
/**
|
||||
* Require parentheses around immediate `function` invocations
|
||||
* @see https://eslint.style/rules/wrap-iife
|
||||
*/
|
||||
'wrap-iife': WrapIifeRuleOptions;
|
||||
/**
|
||||
* Require parenthesis around regex literals
|
||||
* @see https://eslint.style/rules/wrap-regex
|
||||
*/
|
||||
'wrap-regex': WrapRegexRuleOptions;
|
||||
/**
|
||||
* Require or disallow spacing around the `*` in `yield*` expressions
|
||||
* @see https://eslint.style/rules/yield-star-spacing
|
||||
*/
|
||||
'yield-star-spacing': YieldStarSpacingRuleOptions;
|
||||
}
|
||||
//#endregion
|
||||
export { RuleOptions, UnprefixedRuleOptions };
|
||||
+1536
File diff suppressed because it is too large
Load Diff
+102
@@ -0,0 +1,102 @@
|
||||
import "./utils.js";
|
||||
import { n as plugin, t as configs } from "./configs.js";
|
||||
import "./vendor.js";
|
||||
import "./rules/array-bracket-newline.js";
|
||||
import "./rules/array-bracket-spacing.js";
|
||||
import "./rules/array-element-newline.js";
|
||||
import "./rules/arrow-parens.js";
|
||||
import "./rules/arrow-spacing.js";
|
||||
import "./rules/block-spacing.js";
|
||||
import "./rules/brace-style.js";
|
||||
import "./rules/comma-dangle.js";
|
||||
import "./rules/comma-spacing.js";
|
||||
import "./rules/comma-style.js";
|
||||
import "./rules/computed-property-spacing.js";
|
||||
import "./rules/curly-newline.js";
|
||||
import "./rules/dot-location.js";
|
||||
import "./rules/eol-last.js";
|
||||
import "./rules/function-call-argument-newline.js";
|
||||
import "./rules/function-call-spacing.js";
|
||||
import "./rules/function-paren-newline.js";
|
||||
import "./rules/generator-star-spacing.js";
|
||||
import "./rules/implicit-arrow-linebreak.js";
|
||||
import "./rules/indent-binary-ops.js";
|
||||
import "./rules/indent.js";
|
||||
import "./rules/jsx-child-element-spacing.js";
|
||||
import "./rules/jsx-closing-bracket-location.js";
|
||||
import "./rules/jsx-closing-tag-location.js";
|
||||
import "./rules/jsx-curly-brace-presence.js";
|
||||
import "./rules/jsx-curly-newline.js";
|
||||
import "./rules/jsx-curly-spacing.js";
|
||||
import "./rules/jsx-equals-spacing.js";
|
||||
import "./rules/jsx-first-prop-new-line.js";
|
||||
import "./rules/jsx-function-call-newline.js";
|
||||
import "./rules/jsx-indent-props.js";
|
||||
import "./rules/jsx-indent.js";
|
||||
import "./rules/jsx-max-props-per-line.js";
|
||||
import "./rules/jsx-newline.js";
|
||||
import "./rules/jsx-one-expression-per-line.js";
|
||||
import "./rules/jsx-pascal-case.js";
|
||||
import "./rules/jsx-props-no-multi-spaces.js";
|
||||
import "./rules/jsx-props-style.js";
|
||||
import "./rules/jsx-quotes.js";
|
||||
import "./rules/jsx-self-closing-comp.js";
|
||||
import "./rules/jsx-sort-props.js";
|
||||
import "./rules/jsx-tag-spacing.js";
|
||||
import "./rules/jsx-wrap-multilines.js";
|
||||
import "./rules/key-spacing.js";
|
||||
import "./rules/keyword-spacing.js";
|
||||
import "./rules/line-comment-position.js";
|
||||
import "./rules/linebreak-style.js";
|
||||
import "./rules/lines-around-comment.js";
|
||||
import "./rules/lines-between-class-members.js";
|
||||
import "./rules/list-style.js";
|
||||
import "./rules/max-len.js";
|
||||
import "./rules/max-statements-per-line.js";
|
||||
import "./rules/member-delimiter-style.js";
|
||||
import "./rules/multiline-comment-style.js";
|
||||
import "./rules/multiline-ternary.js";
|
||||
import "./rules/new-parens.js";
|
||||
import "./rules/newline-per-chained-call.js";
|
||||
import "./rules/no-confusing-arrow.js";
|
||||
import "./rules/no-extra-parens.js";
|
||||
import "./rules/no-extra-semi.js";
|
||||
import "./rules/no-floating-decimal.js";
|
||||
import "./rules/no-mixed-operators.js";
|
||||
import "./rules/no-mixed-spaces-and-tabs.js";
|
||||
import "./rules/no-multi-spaces.js";
|
||||
import "./rules/no-multiple-empty-lines.js";
|
||||
import "./rules/no-tabs.js";
|
||||
import "./rules/no-trailing-spaces.js";
|
||||
import "./rules/no-whitespace-before-property.js";
|
||||
import "./rules/nonblock-statement-body-position.js";
|
||||
import "./rules/object-curly-newline.js";
|
||||
import "./rules/object-curly-spacing.js";
|
||||
import "./rules/object-property-newline.js";
|
||||
import "./rules/one-var-declaration-per-line.js";
|
||||
import "./rules/operator-linebreak.js";
|
||||
import "./rules/padded-blocks.js";
|
||||
import "./rules/padding-line-between-statements.js";
|
||||
import "./rules/quote-props.js";
|
||||
import "./rules/quotes.js";
|
||||
import "./rules/rest-spread-spacing.js";
|
||||
import "./rules/semi-spacing.js";
|
||||
import "./rules/semi-style.js";
|
||||
import "./rules/semi.js";
|
||||
import "./rules/space-before-blocks.js";
|
||||
import "./rules/space-before-function-paren.js";
|
||||
import "./rules/space-in-parens.js";
|
||||
import "./rules/space-infix-ops.js";
|
||||
import "./rules/space-unary-ops.js";
|
||||
import "./rules/spaced-comment.js";
|
||||
import "./rules/switch-colon-spacing.js";
|
||||
import "./rules/template-curly-spacing.js";
|
||||
import "./rules/template-tag-spacing.js";
|
||||
import "./rules/type-annotation-spacing.js";
|
||||
import "./rules/type-generic-spacing.js";
|
||||
import "./rules/type-named-tuple-spacing.js";
|
||||
import "./rules/wrap-iife.js";
|
||||
import "./rules/wrap-regex.js";
|
||||
import "./rules/yield-star-spacing.js";
|
||||
const index = Object.assign(plugin, { configs });
|
||||
export { index as default, index as "module.exports" };
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { createRequire } from "node:module";
|
||||
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
||||
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
||||
export { __require as n, __commonJSMin as t };
|
||||
Generated
Vendored
+134
@@ -0,0 +1,134 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var array_bracket_newline_default = createRule({
|
||||
name: "array-bracket-newline",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce linebreaks after opening and before closing array brackets" },
|
||||
fixable: "whitespace",
|
||||
schema: [{ oneOf: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"consistent"
|
||||
]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
multiline: { type: "boolean" },
|
||||
minItems: {
|
||||
type: ["integer", "null"],
|
||||
minimum: 0
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}] }],
|
||||
defaultOptions: [],
|
||||
messages: {
|
||||
unexpectedOpeningLinebreak: "There should be no linebreak after '['.",
|
||||
unexpectedClosingLinebreak: "There should be no linebreak before ']'.",
|
||||
missingOpeningLinebreak: "A linebreak is required after '['.",
|
||||
missingClosingLinebreak: "A linebreak is required before ']'."
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
function normalizeOptionValue(option) {
|
||||
let consistent = false;
|
||||
let multiline = false;
|
||||
let minItems = 0;
|
||||
if (option) if (option === "consistent") {
|
||||
consistent = true;
|
||||
minItems = Number.POSITIVE_INFINITY;
|
||||
} else if (option === "always" || typeof option !== "string" && option.minItems === 0) minItems = 0;
|
||||
else if (option === "never") minItems = Number.POSITIVE_INFINITY;
|
||||
else {
|
||||
multiline = Boolean(option.multiline);
|
||||
minItems = option.minItems || Number.POSITIVE_INFINITY;
|
||||
}
|
||||
else {
|
||||
consistent = false;
|
||||
multiline = true;
|
||||
minItems = Number.POSITIVE_INFINITY;
|
||||
}
|
||||
return {
|
||||
consistent,
|
||||
multiline,
|
||||
minItems
|
||||
};
|
||||
}
|
||||
function normalizeOptions(options) {
|
||||
const value = normalizeOptionValue(options);
|
||||
return {
|
||||
ArrayExpression: value,
|
||||
ArrayPattern: value
|
||||
};
|
||||
}
|
||||
function reportNoBeginningLinebreak(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "unexpectedOpeningLinebreak",
|
||||
fix(fixer) {
|
||||
const nextToken = sourceCode.getTokenAfter(token, { includeComments: true });
|
||||
if (!nextToken || (0, import_ast_utils.isCommentToken)(nextToken)) return null;
|
||||
return fixer.removeRange([token.range[1], nextToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportNoEndingLinebreak(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "unexpectedClosingLinebreak",
|
||||
fix(fixer) {
|
||||
const previousToken = sourceCode.getTokenBefore(token, { includeComments: true });
|
||||
if (!previousToken || (0, import_ast_utils.isCommentToken)(previousToken)) return null;
|
||||
return fixer.removeRange([previousToken.range[1], token.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportRequiredBeginningLinebreak(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "missingOpeningLinebreak",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(token, "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportRequiredEndingLinebreak(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "missingClosingLinebreak",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(token, "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
function check(node) {
|
||||
const elements = node.elements;
|
||||
const options = normalizeOptions(context.options[0])[node.type];
|
||||
const openBracket = sourceCode.getFirstToken(node);
|
||||
const closeBracket = sourceCode.getLastToken(node);
|
||||
const firstIncComment = sourceCode.getTokenAfter(openBracket, { includeComments: true });
|
||||
const lastIncComment = sourceCode.getTokenBefore(closeBracket, { includeComments: true });
|
||||
const first = sourceCode.getTokenAfter(openBracket);
|
||||
const last = sourceCode.getTokenBefore(closeBracket);
|
||||
if (elements.length >= options.minItems || options.multiline && elements.length > 0 && !(0, import_ast_utils.isTokenOnSameLine)(lastIncComment, firstIncComment) || elements.length === 0 && firstIncComment.type === "Block" && !(0, import_ast_utils.isTokenOnSameLine)(lastIncComment, firstIncComment) && firstIncComment === lastIncComment || options.consistent && !(0, import_ast_utils.isTokenOnSameLine)(openBracket, first)) {
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(openBracket, first)) reportRequiredBeginningLinebreak(node, openBracket);
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(last, closeBracket)) reportRequiredEndingLinebreak(node, closeBracket);
|
||||
} else {
|
||||
if (!(0, import_ast_utils.isTokenOnSameLine)(openBracket, first)) reportNoBeginningLinebreak(node, openBracket);
|
||||
if (!(0, import_ast_utils.isTokenOnSameLine)(last, closeBracket)) reportNoEndingLinebreak(node, closeBracket);
|
||||
}
|
||||
}
|
||||
return {
|
||||
ArrayPattern: check,
|
||||
ArrayExpression: check
|
||||
};
|
||||
}
|
||||
});
|
||||
export { array_bracket_newline_default as t };
|
||||
Generated
Vendored
+123
@@ -0,0 +1,123 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var array_bracket_spacing_default = createRule({
|
||||
name: "array-bracket-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent spacing inside array brackets" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
singleValue: { type: "boolean" },
|
||||
objectsInArrays: { type: "boolean" },
|
||||
arraysInArrays: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: ["never"],
|
||||
messages: {
|
||||
unexpectedSpaceAfter: "There should be no space after '{{tokenValue}}'.",
|
||||
unexpectedSpaceBefore: "There should be no space before '{{tokenValue}}'.",
|
||||
missingSpaceAfter: "A space is required after '{{tokenValue}}'.",
|
||||
missingSpaceBefore: "A space is required before '{{tokenValue}}'."
|
||||
}
|
||||
},
|
||||
create(context, [style]) {
|
||||
const spaced = style === "always";
|
||||
const sourceCode = context.sourceCode;
|
||||
function isOptionSet(option) {
|
||||
return context.options[1] ? context.options[1][option] === !spaced : false;
|
||||
}
|
||||
const options = {
|
||||
spaced,
|
||||
singleElementException: isOptionSet("singleValue"),
|
||||
objectsInArraysException: isOptionSet("objectsInArrays"),
|
||||
arraysInArraysException: isOptionSet("arraysInArrays")
|
||||
};
|
||||
function reportNoBeginningSpace(node, token) {
|
||||
const nextToken = sourceCode.getTokenAfter(token);
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: token.loc.end,
|
||||
end: nextToken.loc.start
|
||||
},
|
||||
messageId: "unexpectedSpaceAfter",
|
||||
data: { tokenValue: token.value },
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([token.range[1], nextToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportNoEndingSpace(node, token) {
|
||||
const previousToken = sourceCode.getTokenBefore(token);
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: previousToken.loc.end,
|
||||
end: token.loc.start
|
||||
},
|
||||
messageId: "unexpectedSpaceBefore",
|
||||
data: { tokenValue: token.value },
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([previousToken.range[1], token.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportRequiredBeginningSpace(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "missingSpaceAfter",
|
||||
data: { tokenValue: token.value },
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportRequiredEndingSpace(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "missingSpaceBefore",
|
||||
data: { tokenValue: token.value },
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
function isObjectType(node) {
|
||||
return node && (node.type === "ObjectExpression" || node.type === "ObjectPattern");
|
||||
}
|
||||
function isArrayType(node) {
|
||||
return node && (node.type === "ArrayExpression" || node.type === "ArrayPattern");
|
||||
}
|
||||
function validateArraySpacing(node) {
|
||||
if (options.spaced && node.elements.length === 0) return;
|
||||
const first = sourceCode.getFirstToken(node);
|
||||
const second = sourceCode.getFirstToken(node, 1);
|
||||
const last = node.type === "ArrayPattern" && node.typeAnnotation ? sourceCode.getTokenBefore(node.typeAnnotation) : sourceCode.getLastToken(node);
|
||||
const penultimate = sourceCode.getTokenBefore(last);
|
||||
const firstElement = node.elements[0];
|
||||
const lastElement = node.elements[node.elements.length - 1];
|
||||
const openingBracketMustBeSpaced = firstElement && options.objectsInArraysException && isObjectType(firstElement) || firstElement && options.arraysInArraysException && isArrayType(firstElement) || options.singleElementException && node.elements.length === 1 ? !options.spaced : options.spaced;
|
||||
const closingBracketMustBeSpaced = lastElement && options.objectsInArraysException && isObjectType(lastElement) || lastElement && options.arraysInArraysException && isArrayType(lastElement) || options.singleElementException && node.elements.length === 1 ? !options.spaced : options.spaced;
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(first, second)) {
|
||||
if (openingBracketMustBeSpaced && !sourceCode.isSpaceBetween(first, second)) reportRequiredBeginningSpace(node, first);
|
||||
if (!openingBracketMustBeSpaced && sourceCode.isSpaceBetween(first, second)) reportNoBeginningSpace(node, first);
|
||||
}
|
||||
if (first !== penultimate && (0, import_ast_utils.isTokenOnSameLine)(penultimate, last)) {
|
||||
if (closingBracketMustBeSpaced && !sourceCode.isSpaceBetween(penultimate, last)) reportRequiredEndingSpace(node, last);
|
||||
if (!closingBracketMustBeSpaced && sourceCode.isSpaceBetween(penultimate, last)) reportNoEndingSpace(node, last);
|
||||
}
|
||||
}
|
||||
return {
|
||||
ArrayPattern: validateArraySpacing,
|
||||
ArrayExpression: validateArraySpacing
|
||||
};
|
||||
}
|
||||
});
|
||||
export { array_bracket_spacing_default as t };
|
||||
Generated
Vendored
+146
@@ -0,0 +1,146 @@
|
||||
import { K as isSingleLine, f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var array_element_newline_default = createRule({
|
||||
name: "array-element-newline",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce line breaks after each array element" },
|
||||
fixable: "whitespace",
|
||||
schema: {
|
||||
definitions: { basicConfig: { oneOf: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"consistent"
|
||||
]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
consistent: { type: "boolean" },
|
||||
multiline: { type: "boolean" },
|
||||
minItems: {
|
||||
type: ["integer", "null"],
|
||||
minimum: 0
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}] } },
|
||||
type: "array",
|
||||
items: [{ oneOf: [{ $ref: "#/definitions/basicConfig" }, {
|
||||
type: "object",
|
||||
properties: {
|
||||
ArrayExpression: { $ref: "#/definitions/basicConfig" },
|
||||
ArrayPattern: { $ref: "#/definitions/basicConfig" }
|
||||
},
|
||||
additionalProperties: false,
|
||||
minProperties: 1
|
||||
}] }]
|
||||
},
|
||||
defaultOptions: [],
|
||||
messages: {
|
||||
unexpectedLineBreak: "There should be no linebreak here.",
|
||||
missingLineBreak: "There should be a linebreak after this element."
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
function normalizeOptionValue(providedOption) {
|
||||
let consistent = false;
|
||||
let multiline = false;
|
||||
let minItems;
|
||||
const option = providedOption || "always";
|
||||
if (!option || option === "always" || typeof option === "object" && option.minItems === 0) minItems = 0;
|
||||
else if (option === "never") minItems = Number.POSITIVE_INFINITY;
|
||||
else if (option === "consistent") {
|
||||
consistent = true;
|
||||
minItems = Number.POSITIVE_INFINITY;
|
||||
} else {
|
||||
consistent = Boolean(option.consistent);
|
||||
multiline = Boolean(option.multiline);
|
||||
minItems = option.minItems || Number.POSITIVE_INFINITY;
|
||||
}
|
||||
return {
|
||||
consistent,
|
||||
multiline,
|
||||
minItems
|
||||
};
|
||||
}
|
||||
function normalizeOptions(options) {
|
||||
if (options && (options.ArrayExpression || options.ArrayPattern)) {
|
||||
let expressionOptions, patternOptions;
|
||||
if (options.ArrayExpression) expressionOptions = normalizeOptionValue(options.ArrayExpression);
|
||||
if (options.ArrayPattern) patternOptions = normalizeOptionValue(options.ArrayPattern);
|
||||
return {
|
||||
ArrayExpression: expressionOptions,
|
||||
ArrayPattern: patternOptions
|
||||
};
|
||||
}
|
||||
const value = normalizeOptionValue(options);
|
||||
return {
|
||||
ArrayExpression: value,
|
||||
ArrayPattern: value
|
||||
};
|
||||
}
|
||||
function reportNoLineBreak(token) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(token, { includeComments: true });
|
||||
context.report({
|
||||
loc: {
|
||||
start: tokenBefore.loc.end,
|
||||
end: token.loc.start
|
||||
},
|
||||
messageId: "unexpectedLineBreak",
|
||||
fix(fixer) {
|
||||
if ((0, import_ast_utils.isCommentToken)(tokenBefore)) return null;
|
||||
if (!(0, import_ast_utils.isTokenOnSameLine)(tokenBefore, token)) return fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], " ");
|
||||
const twoTokensBefore = sourceCode.getTokenBefore(tokenBefore, { includeComments: true });
|
||||
if ((0, import_ast_utils.isCommentToken)(twoTokensBefore)) return null;
|
||||
return fixer.replaceTextRange([twoTokensBefore.range[1], tokenBefore.range[0]], "");
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportRequiredLineBreak(token) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(token, { includeComments: true });
|
||||
context.report({
|
||||
loc: {
|
||||
start: tokenBefore.loc.end,
|
||||
end: token.loc.start
|
||||
},
|
||||
messageId: "missingLineBreak",
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
function check(node) {
|
||||
const elements = node.elements;
|
||||
const options = normalizeOptions(context.options[0])[node.type];
|
||||
if (!options) return;
|
||||
let elementBreak = false;
|
||||
if (options.multiline) elementBreak = elements.some((element) => element !== null && !isSingleLine(element));
|
||||
let linebreaksCount = 0;
|
||||
for (let i = 0; i < node.elements.length; i++) {
|
||||
const element = node.elements[i];
|
||||
const previousElement = elements[i - 1];
|
||||
if (i === 0 || element === null || previousElement === null) continue;
|
||||
const commaToken = sourceCode.getFirstTokenBetween(previousElement, element, import_ast_utils.isCommaToken);
|
||||
if (!(0, import_ast_utils.isTokenOnSameLine)(sourceCode.getTokenBefore(commaToken), sourceCode.getTokenAfter(commaToken))) linebreaksCount++;
|
||||
}
|
||||
const needsLinebreaks = elements.length >= options.minItems || options.multiline && elementBreak || options.consistent && linebreaksCount > 0 && linebreaksCount < node.elements.length;
|
||||
elements.forEach((element, i) => {
|
||||
const previousElement = elements[i - 1];
|
||||
if (i === 0 || element === null || previousElement === null) return;
|
||||
const commaToken = sourceCode.getFirstTokenBetween(previousElement, element, import_ast_utils.isCommaToken);
|
||||
const lastTokenOfPreviousElement = sourceCode.getTokenBefore(commaToken);
|
||||
const firstTokenOfCurrentElement = sourceCode.getTokenAfter(commaToken);
|
||||
if (needsLinebreaks) {
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) reportRequiredLineBreak(firstTokenOfCurrentElement);
|
||||
} else if (!(0, import_ast_utils.isTokenOnSameLine)(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) reportNoLineBreak(firstTokenOfCurrentElement);
|
||||
});
|
||||
}
|
||||
return {
|
||||
ArrayPattern: check,
|
||||
ArrayExpression: check
|
||||
};
|
||||
}
|
||||
});
|
||||
export { array_element_newline_default as t };
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
import { f as createRule, g as import_ast_utils, w as canTokensBeAdjacent } from "../utils.js";
|
||||
function hasBlockBody(node) {
|
||||
return node.body.type === "BlockStatement";
|
||||
}
|
||||
var arrow_parens_default = createRule({
|
||||
name: "arrow-parens",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require parentheses around arrow function arguments" },
|
||||
fixable: "code",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["always", "as-needed"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: { requireForBlockBody: { type: "boolean" } },
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: ["always"],
|
||||
messages: {
|
||||
unexpectedParens: "Unexpected parentheses around single function argument.",
|
||||
expectedParens: "Expected parentheses around arrow function argument.",
|
||||
unexpectedParensInline: "Unexpected parentheses around single function argument having a body with no curly braces.",
|
||||
expectedParensBlock: "Expected parentheses around arrow function argument having a body with curly braces."
|
||||
}
|
||||
},
|
||||
create(context, [style, options]) {
|
||||
const asNeeded = style === "as-needed";
|
||||
const requireForBlockBody = asNeeded && options?.requireForBlockBody === true;
|
||||
const sourceCode = context.sourceCode;
|
||||
function findOpeningParenOfParams(node) {
|
||||
const tokenBeforeParams = sourceCode.getTokenBefore(node.params[0]);
|
||||
if (tokenBeforeParams && (0, import_ast_utils.isOpeningParenToken)(tokenBeforeParams) && node.range[0] <= tokenBeforeParams.range[0]) return tokenBeforeParams;
|
||||
return null;
|
||||
}
|
||||
function getClosingParenOfParams(node) {
|
||||
return sourceCode.getTokenAfter(node.params[0], import_ast_utils.isClosingParenToken);
|
||||
}
|
||||
function hasCommentsInParensOfParams(node, openingParen) {
|
||||
return sourceCode.commentsExistBetween(openingParen, getClosingParenOfParams(node));
|
||||
}
|
||||
function hasUnexpectedTokensBeforeOpeningParen(node, openingParen) {
|
||||
const expectedCount = node.async ? 1 : 0;
|
||||
return sourceCode.getFirstToken(node, { skip: expectedCount }) !== openingParen;
|
||||
}
|
||||
return { "ArrowFunctionExpression[params.length=1]": function(node) {
|
||||
const shouldHaveParens = !asNeeded || requireForBlockBody && hasBlockBody(node);
|
||||
const openingParen = findOpeningParenOfParams(node);
|
||||
const hasParens = openingParen !== null;
|
||||
const [param] = node.params;
|
||||
if (shouldHaveParens && !hasParens) context.report({
|
||||
node,
|
||||
messageId: requireForBlockBody ? "expectedParensBlock" : "expectedParens",
|
||||
loc: param.loc,
|
||||
*fix(fixer) {
|
||||
yield fixer.insertTextBefore(param, "(");
|
||||
yield fixer.insertTextAfter(param, ")");
|
||||
}
|
||||
});
|
||||
if (!shouldHaveParens && hasParens && param.type === "Identifier" && !param.optional && !param.typeAnnotation && !node.returnType && !hasCommentsInParensOfParams(node, openingParen) && !hasUnexpectedTokensBeforeOpeningParen(node, openingParen)) context.report({
|
||||
node,
|
||||
messageId: requireForBlockBody ? "unexpectedParensInline" : "unexpectedParens",
|
||||
loc: param.loc,
|
||||
*fix(fixer) {
|
||||
const tokenBeforeOpeningParen = sourceCode.getTokenBefore(openingParen);
|
||||
const closingParen = getClosingParenOfParams(node);
|
||||
if (tokenBeforeOpeningParen && tokenBeforeOpeningParen.range[1] === openingParen.range[0] && !canTokensBeAdjacent(tokenBeforeOpeningParen, sourceCode.getFirstToken(param))) yield fixer.insertTextBefore(openingParen, " ");
|
||||
yield fixer.removeRange([openingParen.range[0], param.range[0]]);
|
||||
yield fixer.removeRange([param.range[1], closingParen.range[1]]);
|
||||
}
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { arrow_parens_default as t };
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var arrow_spacing_default = createRule({
|
||||
name: "arrow-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent spacing before and after the arrow in arrow functions" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
before: { type: "boolean" },
|
||||
after: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
before: true,
|
||||
after: true
|
||||
}],
|
||||
messages: {
|
||||
expectedBefore: "Missing space before =>.",
|
||||
unexpectedBefore: "Unexpected space before =>.",
|
||||
expectedAfter: "Missing space after =>.",
|
||||
unexpectedAfter: "Unexpected space after =>."
|
||||
}
|
||||
},
|
||||
create(context, [option]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
function getArrow(node) {
|
||||
if (node.type === "ArrowFunctionExpression") return sourceCode.getTokenBefore(node.body, import_ast_utils.isArrowToken);
|
||||
else return sourceCode.getFirstToken(node.returnType, import_ast_utils.isArrowToken);
|
||||
}
|
||||
function spaces(node) {
|
||||
const arrowToken = getArrow(node);
|
||||
const beforeToken = sourceCode.getTokenBefore(arrowToken, { includeComments: true });
|
||||
const isSpacedBefore = sourceCode.isSpaceBetween(beforeToken, arrowToken);
|
||||
if (option.before) {
|
||||
if (!isSpacedBefore) context.report({
|
||||
node: beforeToken,
|
||||
messageId: "expectedBefore",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(arrowToken, " ");
|
||||
}
|
||||
});
|
||||
} else if (isSpacedBefore) context.report({
|
||||
node: beforeToken,
|
||||
messageId: "unexpectedBefore",
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([beforeToken.range[1], arrowToken.range[0]]);
|
||||
}
|
||||
});
|
||||
const afterToken = sourceCode.getTokenAfter(arrowToken, { includeComments: true });
|
||||
const isSpacedAfter = sourceCode.isSpaceBetween(arrowToken, afterToken);
|
||||
if (option.after) {
|
||||
if (!isSpacedAfter) context.report({
|
||||
node: afterToken,
|
||||
messageId: "expectedAfter",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(arrowToken, " ");
|
||||
}
|
||||
});
|
||||
} else if (isSpacedAfter) context.report({
|
||||
node: afterToken,
|
||||
messageId: "unexpectedAfter",
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([arrowToken.range[1], afterToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
ArrowFunctionExpression: spaces,
|
||||
TSFunctionType: spaces,
|
||||
TSConstructorType: spaces
|
||||
};
|
||||
}
|
||||
});
|
||||
export { arrow_spacing_default as t };
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
import { f as createRule, g as import_ast_utils, h as AST_TOKEN_TYPES } from "../utils.js";
|
||||
var block_spacing_default = createRule({
|
||||
name: "block-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow or enforce spaces inside of blocks after opening block and before closing block" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}],
|
||||
defaultOptions: ["always"],
|
||||
messages: {
|
||||
missing: "Requires a space {{location}} '{{token}}'.",
|
||||
extra: "Unexpected space(s) {{location}} '{{token}}'."
|
||||
}
|
||||
},
|
||||
create(context, [whenToApplyOption]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const always = whenToApplyOption !== "never";
|
||||
const messageId = always ? "missing" : "extra";
|
||||
function getOpenBrace(node) {
|
||||
return sourceCode.getFirstToken(node, { filter: (token) => (0, import_ast_utils.isOpeningBraceToken)(token) });
|
||||
}
|
||||
function isValid(left, right) {
|
||||
return !(0, import_ast_utils.isTokenOnSameLine)(left, right) || sourceCode.isSpaceBetween(left, right) === always;
|
||||
}
|
||||
function checkSpacingInsideBraces(node) {
|
||||
const openBrace = getOpenBrace(node);
|
||||
const closeBrace = sourceCode.getLastToken(node);
|
||||
const firstToken = sourceCode.getTokenAfter(openBrace, { includeComments: true });
|
||||
const lastToken = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
|
||||
if (!(0, import_ast_utils.isOpeningBraceToken)(openBrace) || !(0, import_ast_utils.isClosingBraceToken)(closeBrace) || firstToken === closeBrace) return;
|
||||
if (!always && firstToken.type === AST_TOKEN_TYPES.Line) return;
|
||||
if (!isValid(openBrace, firstToken)) {
|
||||
let loc = openBrace.loc;
|
||||
if (messageId === "extra") loc = {
|
||||
start: openBrace.loc.end,
|
||||
end: firstToken.loc.start
|
||||
};
|
||||
context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId,
|
||||
data: {
|
||||
location: "after",
|
||||
token: openBrace.value
|
||||
},
|
||||
fix(fixer) {
|
||||
if (always) return fixer.insertTextBefore(firstToken, " ");
|
||||
return fixer.removeRange([openBrace.range[1], firstToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!isValid(lastToken, closeBrace)) {
|
||||
let loc = closeBrace.loc;
|
||||
if (messageId === "extra") loc = {
|
||||
start: lastToken.loc.end,
|
||||
end: closeBrace.loc.start
|
||||
};
|
||||
context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId,
|
||||
data: {
|
||||
location: "before",
|
||||
token: closeBrace.value
|
||||
},
|
||||
fix(fixer) {
|
||||
if (always) return fixer.insertTextAfter(lastToken, " ");
|
||||
return fixer.removeRange([lastToken.range[1], closeBrace.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return {
|
||||
BlockStatement: checkSpacingInsideBraces,
|
||||
StaticBlock: checkSpacingInsideBraces,
|
||||
SwitchStatement: checkSpacingInsideBraces
|
||||
};
|
||||
}
|
||||
});
|
||||
export { block_spacing_default as t };
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
import { S as STATEMENT_LIST_PARENTS, d as safeReplaceTextBetween, f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var brace_style_default = createRule({
|
||||
name: "brace-style",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent brace style for blocks" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"1tbs",
|
||||
"stroustrup",
|
||||
"allman"
|
||||
]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: { allowSingleLine: { type: "boolean" } },
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: ["1tbs", { allowSingleLine: false }],
|
||||
messages: {
|
||||
nextLineOpen: "Opening curly brace does not appear on the same line as controlling statement.",
|
||||
sameLineOpen: "Opening curly brace appears on the same line as controlling statement.",
|
||||
blockSameLine: "Statement inside of curly braces should be on next line.",
|
||||
nextLineClose: "Closing curly brace does not appear on the same line as the subsequent block.",
|
||||
singleLineClose: "Closing curly brace should be on the same line as opening curly brace or on the line after the previous block.",
|
||||
sameLineClose: "Closing curly brace appears on the same line as the subsequent block."
|
||||
}
|
||||
},
|
||||
create(context, [style, options]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const { allowSingleLine } = options;
|
||||
const isAllmanStyle = style === "allman";
|
||||
function validateCurlyPair(openingCurlyToken, closingCurlyToken) {
|
||||
const tokenBeforeOpeningCurly = sourceCode.getTokenBefore(openingCurlyToken);
|
||||
const tokenBeforeClosingCurly = sourceCode.getTokenBefore(closingCurlyToken);
|
||||
const tokenAfterOpeningCurly = sourceCode.getTokenAfter(openingCurlyToken);
|
||||
const singleLineException = allowSingleLine && (0, import_ast_utils.isTokenOnSameLine)(openingCurlyToken, closingCurlyToken);
|
||||
if (!isAllmanStyle && !(0, import_ast_utils.isTokenOnSameLine)(tokenBeforeOpeningCurly, openingCurlyToken)) context.report({
|
||||
node: openingCurlyToken,
|
||||
messageId: "nextLineOpen",
|
||||
fix: safeReplaceTextBetween(sourceCode, tokenBeforeOpeningCurly, openingCurlyToken, " ")
|
||||
});
|
||||
if (isAllmanStyle && (0, import_ast_utils.isTokenOnSameLine)(tokenBeforeOpeningCurly, openingCurlyToken) && !singleLineException) context.report({
|
||||
node: openingCurlyToken,
|
||||
messageId: "sameLineOpen",
|
||||
fix: (fixer) => fixer.insertTextBefore(openingCurlyToken, "\n")
|
||||
});
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(openingCurlyToken, tokenAfterOpeningCurly) && tokenAfterOpeningCurly !== closingCurlyToken && !singleLineException) context.report({
|
||||
node: openingCurlyToken,
|
||||
messageId: "blockSameLine",
|
||||
fix: (fixer) => fixer.insertTextAfter(openingCurlyToken, "\n")
|
||||
});
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(tokenBeforeClosingCurly, closingCurlyToken) && tokenBeforeClosingCurly !== openingCurlyToken && !singleLineException) context.report({
|
||||
node: closingCurlyToken,
|
||||
messageId: "singleLineClose",
|
||||
fix: (fixer) => fixer.insertTextBefore(closingCurlyToken, "\n")
|
||||
});
|
||||
}
|
||||
function validateCurlyBeforeKeyword(curlyToken) {
|
||||
const keywordToken = sourceCode.getTokenAfter(curlyToken);
|
||||
if (style === "1tbs" && !(0, import_ast_utils.isTokenOnSameLine)(curlyToken, keywordToken)) context.report({
|
||||
node: curlyToken,
|
||||
messageId: "nextLineClose",
|
||||
fix: safeReplaceTextBetween(sourceCode, curlyToken, keywordToken, " ")
|
||||
});
|
||||
if (style !== "1tbs" && (0, import_ast_utils.isTokenOnSameLine)(curlyToken, keywordToken)) context.report({
|
||||
node: curlyToken,
|
||||
messageId: "sameLineClose",
|
||||
fix: (fixer) => fixer.insertTextAfter(curlyToken, "\n")
|
||||
});
|
||||
}
|
||||
return {
|
||||
BlockStatement(node) {
|
||||
if (!STATEMENT_LIST_PARENTS.has(node.parent.type)) validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
|
||||
},
|
||||
StaticBlock(node) {
|
||||
validateCurlyPair(sourceCode.getFirstToken(node, { skip: 1 }), sourceCode.getLastToken(node));
|
||||
},
|
||||
ClassBody(node) {
|
||||
validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
|
||||
},
|
||||
SwitchStatement(node) {
|
||||
const closingCurly = sourceCode.getLastToken(node);
|
||||
validateCurlyPair(sourceCode.getTokenBefore(node.cases.length ? node.cases[0] : closingCurly), closingCurly);
|
||||
},
|
||||
IfStatement(node) {
|
||||
if (node.consequent.type === "BlockStatement" && node.alternate) validateCurlyBeforeKeyword(sourceCode.getLastToken(node.consequent));
|
||||
},
|
||||
TryStatement(node) {
|
||||
validateCurlyBeforeKeyword(sourceCode.getLastToken(node.block));
|
||||
if (node.handler && node.finalizer) validateCurlyBeforeKeyword(sourceCode.getLastToken(node.handler.body));
|
||||
},
|
||||
TSModuleBlock(node) {
|
||||
validateCurlyPair(sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { brace_style_default as t };
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
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 };
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
import { f as createRule, g as import_ast_utils, h as AST_TOKEN_TYPES } from "../utils.js";
|
||||
var comma_spacing_default = createRule({
|
||||
name: "comma-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent spacing before and after commas" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
before: { type: "boolean" },
|
||||
after: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
before: false,
|
||||
after: true
|
||||
}],
|
||||
messages: {
|
||||
unexpected: `There should be no space {{loc}} ','.`,
|
||||
missing: `A space is required {{loc}} ','.`
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const { before: spaceBefore, after: spaceAfter } = options;
|
||||
const sourceCode = context.sourceCode;
|
||||
const tokensAndComments = sourceCode.tokensAndComments;
|
||||
const ignoredTokens = /* @__PURE__ */ new Set();
|
||||
function addNullElementsToIgnoreList(node) {
|
||||
let previousToken = sourceCode.getFirstToken(node);
|
||||
for (const element of node.elements) {
|
||||
let token;
|
||||
if (element == null) {
|
||||
token = sourceCode.getTokenAfter(previousToken);
|
||||
if (token && (0, import_ast_utils.isCommaToken)(token)) ignoredTokens.add(token);
|
||||
} else token = sourceCode.getTokenAfter(element);
|
||||
previousToken = token;
|
||||
}
|
||||
}
|
||||
function addTypeParametersTrailingCommaToIgnoreList(node) {
|
||||
const paramLength = node.params.length;
|
||||
if (paramLength) {
|
||||
const param = node.params[paramLength - 1];
|
||||
const afterToken = sourceCode.getTokenAfter(param);
|
||||
if (afterToken && (0, import_ast_utils.isCommaToken)(afterToken)) ignoredTokens.add(afterToken);
|
||||
}
|
||||
}
|
||||
function validateCommaSpacing(commaToken, prevToken, nextToken) {
|
||||
if (prevToken && (0, import_ast_utils.isTokenOnSameLine)(prevToken, commaToken) && spaceBefore !== sourceCode.isSpaceBetween(prevToken, commaToken)) context.report({
|
||||
node: commaToken,
|
||||
data: { loc: "before" },
|
||||
messageId: spaceBefore ? "missing" : "unexpected",
|
||||
fix: (fixer) => spaceBefore ? fixer.insertTextBefore(commaToken, " ") : fixer.replaceTextRange([prevToken.range[1], commaToken.range[0]], "")
|
||||
});
|
||||
if (nextToken && (0, import_ast_utils.isTokenOnSameLine)(commaToken, nextToken) && !(0, import_ast_utils.isClosingParenToken)(nextToken) && !(0, import_ast_utils.isClosingBracketToken)(nextToken) && !(0, import_ast_utils.isClosingBraceToken)(nextToken) && !(!spaceAfter && nextToken.type === AST_TOKEN_TYPES.Line) && spaceAfter !== sourceCode.isSpaceBetween(commaToken, nextToken)) context.report({
|
||||
node: commaToken,
|
||||
data: { loc: "after" },
|
||||
messageId: spaceAfter ? "missing" : "unexpected",
|
||||
fix: (fixer) => spaceAfter ? fixer.insertTextAfter(commaToken, " ") : fixer.replaceTextRange([commaToken.range[1], nextToken.range[0]], "")
|
||||
});
|
||||
}
|
||||
return {
|
||||
"ArrayExpression": addNullElementsToIgnoreList,
|
||||
"ArrayPattern": addNullElementsToIgnoreList,
|
||||
"TSTypeParameterDeclaration": addTypeParametersTrailingCommaToIgnoreList,
|
||||
"Program:exit": function() {
|
||||
tokensAndComments.forEach((token, i) => {
|
||||
if (!(0, import_ast_utils.isCommaToken)(token)) return;
|
||||
const prevToken = tokensAndComments[i - 1];
|
||||
const nextToken = tokensAndComments[i + 1];
|
||||
validateCommaSpacing(token, (0, import_ast_utils.isCommaToken)(prevToken) || ignoredTokens.has(token) ? null : prevToken, nextToken && (0, import_ast_utils.isCommaToken)(nextToken) || ignoredTokens.has(token) ? null : nextToken);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { comma_spacing_default as t };
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var comma_style_default = createRule({
|
||||
name: "comma-style",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent comma style" },
|
||||
fixable: "code",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["first", "last"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: { exceptions: {
|
||||
type: "object",
|
||||
additionalProperties: { type: "boolean" }
|
||||
} },
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: ["last"],
|
||||
messages: {
|
||||
unexpectedLineBeforeAndAfterComma: "Bad line breaking before and after ','.",
|
||||
expectedCommaFirst: "',' should be placed first.",
|
||||
expectedCommaLast: "',' should be placed last."
|
||||
}
|
||||
},
|
||||
create(context, [style, options]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const exceptions = options?.exceptions ?? {};
|
||||
function getReplacedText(styleType, text) {
|
||||
switch (styleType) {
|
||||
case "between": return `,${text.replace(import_ast_utils.LINEBREAK_MATCHER, "")}`;
|
||||
case "first": return `${text},`;
|
||||
case "last": return `,${text}`;
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
function getFixerFunction(styleType, tokenBeforeComma, commaToken, tokenAfterComma) {
|
||||
const text = sourceCode.text.slice(tokenBeforeComma.range[1], commaToken.range[0]) + sourceCode.text.slice(commaToken.range[1], tokenAfterComma.range[0]);
|
||||
const range = [tokenBeforeComma.range[1], tokenAfterComma.range[0]];
|
||||
return function(fixer) {
|
||||
return fixer.replaceTextRange(range, getReplacedText(styleType, text));
|
||||
};
|
||||
}
|
||||
function validateCommaItemSpacing(tokenBeforeComma, commaToken, tokenAfterComma) {
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(commaToken, tokenAfterComma) && (0, import_ast_utils.isTokenOnSameLine)(tokenBeforeComma, commaToken)) {} else if (!(0, import_ast_utils.isTokenOnSameLine)(commaToken, tokenAfterComma) && !(0, import_ast_utils.isTokenOnSameLine)(tokenBeforeComma, commaToken)) {
|
||||
const comment = sourceCode.getCommentsAfter(commaToken)[0];
|
||||
const styleType = comment && comment.type === "Block" && (0, import_ast_utils.isTokenOnSameLine)(commaToken, comment) ? style : "between";
|
||||
context.report({
|
||||
node: commaToken,
|
||||
messageId: "unexpectedLineBeforeAndAfterComma",
|
||||
fix: getFixerFunction(styleType, tokenBeforeComma, commaToken, tokenAfterComma)
|
||||
});
|
||||
} else if (style === "first" && !(0, import_ast_utils.isTokenOnSameLine)(commaToken, tokenAfterComma)) context.report({
|
||||
node: commaToken,
|
||||
messageId: "expectedCommaFirst",
|
||||
fix: getFixerFunction(style, tokenBeforeComma, commaToken, tokenAfterComma)
|
||||
});
|
||||
else if (style === "last" && (0, import_ast_utils.isTokenOnSameLine)(commaToken, tokenAfterComma)) context.report({
|
||||
node: commaToken,
|
||||
messageId: "expectedCommaLast",
|
||||
fix: getFixerFunction(style, tokenBeforeComma, commaToken, tokenAfterComma)
|
||||
});
|
||||
}
|
||||
function extractCommaTokens(node, items) {
|
||||
if (items.length === 0) return [];
|
||||
const definedItems = items.filter((item) => Boolean(item));
|
||||
if (definedItems.length === 0) return sourceCode.getTokens(node).filter(import_ast_utils.isCommaToken);
|
||||
const commaTokens = [];
|
||||
const firstItem = definedItems[0];
|
||||
let prevToken = sourceCode.getTokenBefore(firstItem);
|
||||
while (prevToken && node.range[0] <= prevToken.range[0]) {
|
||||
if ((0, import_ast_utils.isCommaToken)(prevToken)) commaTokens.unshift(prevToken);
|
||||
else if ((0, import_ast_utils.isNotOpeningParenToken)(prevToken)) break;
|
||||
prevToken = sourceCode.getTokenBefore(prevToken);
|
||||
}
|
||||
let prevItem = null;
|
||||
for (const item of definedItems) {
|
||||
if (prevItem) commaTokens.push(...sourceCode.getTokensBetween(prevItem, item).filter(import_ast_utils.isCommaToken));
|
||||
const tokenLastItem = sourceCode.getLastToken(item);
|
||||
if (tokenLastItem && (0, import_ast_utils.isCommaToken)(tokenLastItem)) commaTokens.push(tokenLastItem);
|
||||
prevItem = item;
|
||||
}
|
||||
let nextToken = sourceCode.getTokenAfter(prevItem);
|
||||
while (nextToken && nextToken.range[1] <= node.range[1]) {
|
||||
if ((0, import_ast_utils.isCommaToken)(nextToken)) commaTokens.push(nextToken);
|
||||
else if ((0, import_ast_utils.isNotClosingParenToken)(nextToken)) break;
|
||||
nextToken = sourceCode.getTokenAfter(nextToken);
|
||||
}
|
||||
return commaTokens;
|
||||
}
|
||||
function validateComma(node, items) {
|
||||
extractCommaTokens(node, items).forEach((commaToken) => {
|
||||
const tokenBeforeComma = sourceCode.getTokenBefore(commaToken);
|
||||
const tokenAfterComma = sourceCode.getTokenAfter(commaToken);
|
||||
if ((0, import_ast_utils.isOpeningBracketToken)(tokenBeforeComma)) return;
|
||||
if ((0, import_ast_utils.isCommaToken)(tokenBeforeComma) && (0, import_ast_utils.isOpeningBracketToken)(sourceCode.getTokenBefore(tokenBeforeComma, import_ast_utils.isNotCommaToken))) return;
|
||||
if ((0, import_ast_utils.isCommaToken)(tokenAfterComma) && !(0, import_ast_utils.isTokenOnSameLine)(commaToken, tokenAfterComma)) return;
|
||||
validateCommaItemSpacing(tokenBeforeComma, commaToken, tokenAfterComma);
|
||||
});
|
||||
}
|
||||
const nodes = {};
|
||||
if (!exceptions.VariableDeclaration) nodes.VariableDeclaration = (node) => validateComma(node, node.declarations);
|
||||
if (!exceptions.ObjectExpression) nodes.ObjectExpression = validateObjectProperties;
|
||||
if (!exceptions.ObjectPattern) nodes.ObjectPattern = validateObjectProperties;
|
||||
if (!exceptions.ArrayExpression) nodes.ArrayExpression = validateArrayElements;
|
||||
if (!exceptions.ArrayPattern) nodes.ArrayPattern = validateArrayElements;
|
||||
if (!exceptions.FunctionDeclaration) nodes.FunctionDeclaration = validateFunctionParams;
|
||||
if (!exceptions.FunctionExpression) nodes.FunctionExpression = validateFunctionParams;
|
||||
if (!exceptions.ArrowFunctionExpression) nodes.ArrowFunctionExpression = validateFunctionParams;
|
||||
if (!exceptions.CallExpression) nodes.CallExpression = validateCallArguments;
|
||||
if (!exceptions.ImportDeclaration) nodes.ImportDeclaration = (node) => {
|
||||
validateComma(node, node.specifiers);
|
||||
visitImportAttributes(node);
|
||||
};
|
||||
if (!exceptions.NewExpression) nodes.NewExpression = validateCallArguments;
|
||||
if (!exceptions.ExportAllDeclaration) nodes.ExportAllDeclaration = visitImportAttributes;
|
||||
if (!exceptions.ExportNamedDeclaration) nodes.ExportNamedDeclaration = (node) => {
|
||||
validateComma(node, node.specifiers);
|
||||
visitImportAttributes(node);
|
||||
};
|
||||
if (!exceptions.ImportExpression) nodes.ImportExpression = (node) => {
|
||||
validateComma(node, [node.source, node.options ?? null]);
|
||||
};
|
||||
if (!exceptions.SequenceExpression) nodes.SequenceExpression = (node) => validateComma(node, node.expressions);
|
||||
if (!exceptions.ClassDeclaration) nodes.ClassDeclaration = visitClassImplements;
|
||||
if (!exceptions.ClassExpression) nodes.ClassExpression = visitClassImplements;
|
||||
if (!exceptions.TSDeclareFunction) nodes.TSDeclareFunction = validateFunctionParams;
|
||||
if (!exceptions.TSFunctionType) nodes.TSFunctionType = validateFunctionParams;
|
||||
if (!exceptions.TSConstructorType) nodes.TSConstructorType = validateFunctionParams;
|
||||
if (!exceptions.TSEmptyBodyFunctionExpression) nodes.TSEmptyBodyFunctionExpression = validateFunctionParams;
|
||||
if (!exceptions.TSMethodSignature) nodes.TSMethodSignature = validateFunctionParams;
|
||||
if (!exceptions.TSCallSignatureDeclaration) nodes.TSCallSignatureDeclaration = validateFunctionParams;
|
||||
if (!exceptions.TSConstructSignatureDeclaration) nodes.TSConstructSignatureDeclaration = validateFunctionParams;
|
||||
if (!exceptions.TSTypeParameterDeclaration) nodes.TSTypeParameterDeclaration = validateTypeParams;
|
||||
if (!exceptions.TSTypeParameterInstantiation) nodes.TSTypeParameterInstantiation = validateTypeParams;
|
||||
if (!exceptions.TSEnumBody) nodes.TSEnumBody = visitMembers;
|
||||
if (!exceptions.TSTypeLiteral) nodes.TSTypeLiteral = visitMembers;
|
||||
if (!exceptions.TSIndexSignature) nodes.TSIndexSignature = (node) => validateComma(node, node.parameters);
|
||||
if (!exceptions.TSInterfaceDeclaration) nodes.TSInterfaceDeclaration = (node) => validateComma(node, node.extends);
|
||||
if (!exceptions.TSInterfaceBody) nodes.TSInterfaceBody = (node) => validateComma(node, node.body);
|
||||
if (!exceptions.TSTupleType) nodes.TSTupleType = (node) => validateComma(node, node.elementTypes);
|
||||
return nodes;
|
||||
function validateObjectProperties(node) {
|
||||
validateComma(node, node.properties);
|
||||
}
|
||||
function validateArrayElements(node) {
|
||||
validateComma(node, node.elements);
|
||||
}
|
||||
function validateFunctionParams(node) {
|
||||
validateComma(node, node.params);
|
||||
}
|
||||
function validateCallArguments(node) {
|
||||
validateComma(node, node.arguments);
|
||||
}
|
||||
function visitImportAttributes(node) {
|
||||
if (!node.attributes) return;
|
||||
validateComma(node, node.attributes);
|
||||
}
|
||||
function visitClassImplements(node) {
|
||||
if (!node.implements) return;
|
||||
validateComma(node, node.implements);
|
||||
}
|
||||
function visitMembers(node) {
|
||||
validateComma(node, node.members);
|
||||
}
|
||||
function validateTypeParams(node) {
|
||||
validateComma(node, node.params);
|
||||
}
|
||||
}
|
||||
});
|
||||
export { comma_style_default as t };
|
||||
Generated
Vendored
+111
@@ -0,0 +1,111 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var computed_property_spacing_default = createRule({
|
||||
name: "computed-property-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent spacing inside computed property brackets" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: { enforceForClassMembers: { type: "boolean" } },
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: ["never", { enforceForClassMembers: true }],
|
||||
messages: {
|
||||
unexpectedSpaceBefore: "There should be no space before '{{tokenValue}}'.",
|
||||
unexpectedSpaceAfter: "There should be no space after '{{tokenValue}}'.",
|
||||
missingSpaceBefore: "A space is required before '{{tokenValue}}'.",
|
||||
missingSpaceAfter: "A space is required after '{{tokenValue}}'."
|
||||
}
|
||||
},
|
||||
create(context, [style, options]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const propertyNameMustBeSpaced = style === "always";
|
||||
const { enforceForClassMembers } = options;
|
||||
function reportNoBeginningSpace(node, token, tokenAfter) {
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: token.loc.end,
|
||||
end: tokenAfter.loc.start
|
||||
},
|
||||
messageId: "unexpectedSpaceAfter",
|
||||
data: { tokenValue: token.value },
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([token.range[1], tokenAfter.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportNoEndingSpace(node, token, tokenBefore) {
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: tokenBefore.loc.end,
|
||||
end: token.loc.start
|
||||
},
|
||||
messageId: "unexpectedSpaceBefore",
|
||||
data: { tokenValue: token.value },
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([tokenBefore.range[1], token.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportRequiredBeginningSpace(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "missingSpaceAfter",
|
||||
data: { tokenValue: token.value },
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportRequiredEndingSpace(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "missingSpaceBefore",
|
||||
data: { tokenValue: token.value },
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
function checkSpacing(propertyName) {
|
||||
return function(node) {
|
||||
if (node.type !== "TSIndexedAccessType" && !node.computed) return;
|
||||
const property = node[propertyName];
|
||||
const before = sourceCode.getTokenBefore(property, import_ast_utils.isOpeningBracketToken);
|
||||
const first = sourceCode.getTokenAfter(before, { includeComments: true });
|
||||
const after = sourceCode.getTokenAfter(property, import_ast_utils.isClosingBracketToken);
|
||||
const last = sourceCode.getTokenBefore(after, { includeComments: true });
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(before, first)) {
|
||||
if (propertyNameMustBeSpaced) {
|
||||
if (!sourceCode.isSpaceBetween(before, first) && (0, import_ast_utils.isTokenOnSameLine)(before, first)) reportRequiredBeginningSpace(node, before);
|
||||
} else if (sourceCode.isSpaceBetween(before, first)) reportNoBeginningSpace(node, before, first);
|
||||
}
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(last, after)) {
|
||||
if (propertyNameMustBeSpaced) {
|
||||
if (!sourceCode.isSpaceBetween(last, after) && (0, import_ast_utils.isTokenOnSameLine)(last, after)) reportRequiredEndingSpace(node, after);
|
||||
} else if (sourceCode.isSpaceBetween(last, after)) reportNoEndingSpace(node, after, last);
|
||||
}
|
||||
};
|
||||
}
|
||||
const listeners = {
|
||||
Property: checkSpacing("key"),
|
||||
MemberExpression: checkSpacing("property"),
|
||||
TSIndexedAccessType: checkSpacing("indexType")
|
||||
};
|
||||
if (enforceForClassMembers) {
|
||||
listeners.MethodDefinition = checkSpacing("key");
|
||||
listeners.PropertyDefinition = checkSpacing("key");
|
||||
listeners.AccessorProperty = checkSpacing("key");
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
});
|
||||
export { computed_property_spacing_default as t };
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
const commonProperties = {
|
||||
multiline: { type: "boolean" },
|
||||
minElements: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
},
|
||||
consistent: { type: "boolean" }
|
||||
};
|
||||
const optionValueSchema = { oneOf: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: commonProperties,
|
||||
additionalProperties: false
|
||||
}] };
|
||||
var Specialization = /* @__PURE__ */ function(Specialization) {
|
||||
Specialization["IfStatementConsequent"] = "IfStatementConsequent";
|
||||
Specialization["IfStatementAlternative"] = "IfStatementAlternative";
|
||||
Specialization["DoWhileStatement"] = "DoWhileStatement";
|
||||
Specialization["ForInStatement"] = "ForInStatement";
|
||||
Specialization["ForOfStatement"] = "ForOfStatement";
|
||||
Specialization["ForStatement"] = "ForStatement";
|
||||
Specialization["WhileStatement"] = "WhileStatement";
|
||||
Specialization["SwitchStatement"] = "SwitchStatement";
|
||||
Specialization["SwitchCase"] = "SwitchCase";
|
||||
Specialization["TryStatementBlock"] = "TryStatementBlock";
|
||||
Specialization["TryStatementHandler"] = "TryStatementHandler";
|
||||
Specialization["TryStatementFinalizer"] = "TryStatementFinalizer";
|
||||
Specialization["BlockStatement"] = "BlockStatement";
|
||||
Specialization["ArrowFunctionExpression"] = "ArrowFunctionExpression";
|
||||
Specialization["FunctionDeclaration"] = "FunctionDeclaration";
|
||||
Specialization["FunctionExpression"] = "FunctionExpression";
|
||||
Specialization["Property"] = "Property";
|
||||
Specialization["ClassBody"] = "ClassBody";
|
||||
Specialization["StaticBlock"] = "StaticBlock";
|
||||
Specialization["WithStatement"] = "WithStatement";
|
||||
Specialization["TSModuleBlock"] = "TSModuleBlock";
|
||||
return Specialization;
|
||||
}(Specialization || {});
|
||||
const presets = {
|
||||
default: {
|
||||
multiline: false,
|
||||
minElements: Number.POSITIVE_INFINITY,
|
||||
consistent: true
|
||||
},
|
||||
always: {
|
||||
multiline: false,
|
||||
minElements: 0,
|
||||
consistent: false
|
||||
},
|
||||
never: {
|
||||
multiline: false,
|
||||
minElements: Number.POSITIVE_INFINITY,
|
||||
consistent: false
|
||||
}
|
||||
};
|
||||
function normalizeOptionValue(value) {
|
||||
if (value === "always") return presets.always;
|
||||
if (value === "never") return presets.never;
|
||||
if (value) return {
|
||||
consistent: !!value.consistent,
|
||||
minElements: value.minElements ?? Number.POSITIVE_INFINITY,
|
||||
multiline: !!value.multiline
|
||||
};
|
||||
return presets.default;
|
||||
}
|
||||
function normalizeOptions(options) {
|
||||
const value = normalizeOptionValue(options);
|
||||
return Object.fromEntries(Object.entries(Specialization).map(([k]) => [k, typeof options === "object" && options != null && k in options ? normalizeOptionValue(options[k]) : value]));
|
||||
}
|
||||
var curly_newline_default = createRule({
|
||||
name: "curly-newline",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent line breaks after opening and before closing braces" },
|
||||
fixable: "whitespace",
|
||||
schema: [{ oneOf: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
...Object.fromEntries(Object.entries(Specialization).map(([k]) => [k, optionValueSchema])),
|
||||
...commonProperties
|
||||
},
|
||||
additionalProperties: false
|
||||
}] }],
|
||||
defaultOptions: [],
|
||||
messages: {
|
||||
unexpectedLinebreakBeforeClosingBrace: "Unexpected line break before this closing brace.",
|
||||
unexpectedLinebreakAfterOpeningBrace: "Unexpected line break after this opening brace.",
|
||||
expectedLinebreakBeforeClosingBrace: "Expected a line break before this closing brace.",
|
||||
expectedLinebreakAfterOpeningBrace: "Expected a line break after this opening brace."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const normalizedOptions = normalizeOptions(options);
|
||||
function check(node, specialization) {
|
||||
const options = normalizedOptions[specialization];
|
||||
let openBrace;
|
||||
let closeBrace;
|
||||
let elementCount;
|
||||
switch (node.type) {
|
||||
case "SwitchStatement":
|
||||
closeBrace = sourceCode.getLastToken(node);
|
||||
openBrace = sourceCode.getTokenBefore(node.cases.length ? node.cases[0] : closeBrace);
|
||||
elementCount = node.cases.length;
|
||||
break;
|
||||
case "StaticBlock":
|
||||
openBrace = sourceCode.getFirstToken(node, (token) => token.value === "{");
|
||||
closeBrace = sourceCode.getLastToken(node);
|
||||
elementCount = node.body.length;
|
||||
break;
|
||||
default:
|
||||
openBrace = sourceCode.getFirstToken(node);
|
||||
closeBrace = sourceCode.getLastToken(node);
|
||||
elementCount = node.body.length;
|
||||
}
|
||||
let first = sourceCode.getTokenAfter(openBrace, { includeComments: true });
|
||||
let last = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
|
||||
const needsLineBreaks = elementCount >= options.minElements || options.multiline && elementCount > 0 && !(0, import_ast_utils.isTokenOnSameLine)(last, first);
|
||||
const hasCommentsFirstToken = (0, import_ast_utils.isCommentToken)(first);
|
||||
const hasCommentsLastToken = (0, import_ast_utils.isCommentToken)(last);
|
||||
first = sourceCode.getTokenAfter(openBrace);
|
||||
last = sourceCode.getTokenBefore(closeBrace);
|
||||
if (needsLineBreaks) {
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(openBrace, first)) context.report({
|
||||
messageId: "expectedLinebreakAfterOpeningBrace",
|
||||
node,
|
||||
loc: openBrace.loc,
|
||||
fix(fixer) {
|
||||
if (hasCommentsFirstToken) return null;
|
||||
return fixer.insertTextAfter(openBrace, "\n");
|
||||
}
|
||||
});
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(last, closeBrace)) context.report({
|
||||
messageId: "expectedLinebreakBeforeClosingBrace",
|
||||
node,
|
||||
loc: closeBrace.loc,
|
||||
fix(fixer) {
|
||||
if (hasCommentsLastToken) return null;
|
||||
return fixer.insertTextBefore(closeBrace, "\n");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const consistent = options.consistent;
|
||||
const hasLineBreakBetweenOpenBraceAndFirst = !(0, import_ast_utils.isTokenOnSameLine)(openBrace, first);
|
||||
const hasLineBreakBetweenCloseBraceAndLast = !(0, import_ast_utils.isTokenOnSameLine)(last, closeBrace);
|
||||
if (!consistent && hasLineBreakBetweenOpenBraceAndFirst || consistent && hasLineBreakBetweenOpenBraceAndFirst && !hasLineBreakBetweenCloseBraceAndLast) context.report({
|
||||
messageId: "unexpectedLinebreakAfterOpeningBrace",
|
||||
node,
|
||||
loc: openBrace.loc,
|
||||
fix(fixer) {
|
||||
if (hasCommentsFirstToken) return null;
|
||||
return fixer.removeRange([openBrace.range[1], first.range[0]]);
|
||||
}
|
||||
});
|
||||
if (!consistent && hasLineBreakBetweenCloseBraceAndLast || consistent && !hasLineBreakBetweenOpenBraceAndFirst && hasLineBreakBetweenCloseBraceAndLast) context.report({
|
||||
messageId: "unexpectedLinebreakBeforeClosingBrace",
|
||||
node,
|
||||
loc: closeBrace.loc,
|
||||
fix(fixer) {
|
||||
if (hasCommentsLastToken) return null;
|
||||
return fixer.removeRange([last.range[1], closeBrace.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function checkBlockLike(node) {
|
||||
check(node, node.type);
|
||||
}
|
||||
return {
|
||||
BlockStatement(node) {
|
||||
const { parent } = node;
|
||||
switch (parent.type) {
|
||||
case "DoWhileStatement":
|
||||
case "ForInStatement":
|
||||
case "ForOfStatement":
|
||||
case "ForStatement":
|
||||
case "WhileStatement":
|
||||
case "ArrowFunctionExpression":
|
||||
case "FunctionDeclaration":
|
||||
case "WithStatement":
|
||||
check(node, parent.type);
|
||||
break;
|
||||
case "FunctionExpression":
|
||||
if (parent.parent.type === "Property" && parent.parent.method) check(node, "Property");
|
||||
else check(node, parent.type);
|
||||
break;
|
||||
case "IfStatement":
|
||||
if (node === parent.consequent) check(node, "IfStatementConsequent");
|
||||
if (node === parent.alternate) check(node, "IfStatementAlternative");
|
||||
break;
|
||||
case "TryStatement":
|
||||
if (node === parent.block) check(node, "TryStatementBlock");
|
||||
if (node === parent.finalizer) check(node, "TryStatementFinalizer");
|
||||
break;
|
||||
case "CatchClause":
|
||||
check(node, "TryStatementHandler");
|
||||
break;
|
||||
default: if (parent.type === "SwitchCase" && parent.consequent.length === 1) check(node, "SwitchCase");
|
||||
else check(node, "BlockStatement");
|
||||
}
|
||||
},
|
||||
SwitchStatement: checkBlockLike,
|
||||
ClassBody: checkBlockLike,
|
||||
StaticBlock: checkBlockLike,
|
||||
TSModuleBlock: checkBlockLike
|
||||
};
|
||||
}
|
||||
});
|
||||
export { curly_newline_default as t };
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
import { F as isDecimalIntegerNumericToken, f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var dot_location_default = createRule({
|
||||
name: "dot-location",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent newlines before and after dots" },
|
||||
fixable: "code",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["object", "property"]
|
||||
}],
|
||||
defaultOptions: ["object"],
|
||||
messages: {
|
||||
expectedDotAfterObject: "Expected dot to be on same line as object.",
|
||||
expectedDotBeforeProperty: "Expected dot to be on same line as property."
|
||||
}
|
||||
},
|
||||
create(context, [config]) {
|
||||
const onObject = config === "object";
|
||||
const sourceCode = context.sourceCode;
|
||||
function getProperty(node) {
|
||||
if (node.type === "TSImportType") return node.qualifier;
|
||||
if (node.type === "TSQualifiedName") return node.right;
|
||||
return node.property;
|
||||
}
|
||||
function checkDotLocation(node) {
|
||||
const property = getProperty(node);
|
||||
if (!property) return;
|
||||
const dotToken = sourceCode.getTokenBefore(property);
|
||||
if (!dotToken) return;
|
||||
if (onObject) {
|
||||
const tokenBeforeDot = sourceCode.getTokenBefore(dotToken);
|
||||
if (tokenBeforeDot && !(0, import_ast_utils.isTokenOnSameLine)(tokenBeforeDot, dotToken)) context.report({
|
||||
node,
|
||||
loc: dotToken.loc,
|
||||
messageId: "expectedDotAfterObject",
|
||||
*fix(fixer) {
|
||||
if (dotToken.value.startsWith(".") && isDecimalIntegerNumericToken(tokenBeforeDot)) yield fixer.insertTextAfter(tokenBeforeDot, ` ${dotToken.value}`);
|
||||
else yield fixer.insertTextAfter(tokenBeforeDot, dotToken.value);
|
||||
yield fixer.remove(dotToken);
|
||||
}
|
||||
});
|
||||
} else if (!(0, import_ast_utils.isTokenOnSameLine)(dotToken, property)) context.report({
|
||||
node,
|
||||
loc: dotToken.loc,
|
||||
messageId: "expectedDotBeforeProperty",
|
||||
*fix(fixer) {
|
||||
yield fixer.remove(dotToken);
|
||||
yield fixer.insertTextBefore(property, dotToken.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
MemberExpression(node) {
|
||||
if (node.computed) return;
|
||||
checkDotLocation(node);
|
||||
},
|
||||
MetaProperty: checkDotLocation,
|
||||
JSXMemberExpression: checkDotLocation,
|
||||
TSQualifiedName: checkDotLocation,
|
||||
TSImportType: checkDotLocation
|
||||
};
|
||||
}
|
||||
});
|
||||
export { dot_location_default as t };
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import { $ as warnDeprecation, c as hasLinesAndGetLocFromIndex, f as createRule, u as isTextSourceCode } from "../utils.js";
|
||||
var eol_last_default = createRule({
|
||||
name: "eol-last",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require or disallow newline at the end of files" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"unix",
|
||||
"windows"
|
||||
]
|
||||
}],
|
||||
defaultOptions: ["always"],
|
||||
messages: {
|
||||
missing: "Newline required at end of file but not found.",
|
||||
unexpected: "Newline not allowed at end of file."
|
||||
}
|
||||
},
|
||||
create(context, [mode]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
if (!isTextSourceCode(sourceCode) || !hasLinesAndGetLocFromIndex(sourceCode)) return {};
|
||||
const src = sourceCode.text;
|
||||
const LF = "\n";
|
||||
const CRLF = `\r${LF}`;
|
||||
const endsWithNewline = src.endsWith(LF);
|
||||
if (!src.length) return {};
|
||||
let appendCRLF = false;
|
||||
if (mode === "unix") {
|
||||
warnDeprecation("option(\"unix\")", "\"always\" and \"@stylistic/eslint-plugin/rules/linebreak-style\"", "eol-last");
|
||||
mode = "always";
|
||||
}
|
||||
if (mode === "windows") {
|
||||
warnDeprecation("option(\"windows\")", "\"always\" and \"@stylistic/eslint-plugin/rules/linebreak-style\"", "eol-last");
|
||||
mode = "always";
|
||||
appendCRLF = true;
|
||||
}
|
||||
if (mode === "always" && !endsWithNewline) context.report({
|
||||
loc: sourceCode.getLocFromIndex(src.length),
|
||||
messageId: "missing",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfterRange([0, src.length], appendCRLF ? CRLF : LF);
|
||||
}
|
||||
});
|
||||
else if (mode === "never" && endsWithNewline) {
|
||||
const startLoc = sourceCode.getLocFromIndex(src.length - (src.endsWith(CRLF) ? 2 : 1));
|
||||
const endLoc = sourceCode.getLocFromIndex(src.length);
|
||||
context.report({
|
||||
loc: {
|
||||
start: startLoc,
|
||||
end: endLoc
|
||||
},
|
||||
messageId: "unexpected",
|
||||
fix(fixer) {
|
||||
const start = /(?:\r?\n)+$/u.exec(src).index;
|
||||
const end = src.length;
|
||||
return fixer.replaceTextRange([start, end], "");
|
||||
}
|
||||
});
|
||||
}
|
||||
return {};
|
||||
}
|
||||
});
|
||||
export { eol_last_default as t };
|
||||
Generated
Vendored
+72
@@ -0,0 +1,72 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var function_call_argument_newline_default = createRule({
|
||||
name: "function-call-argument-newline",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce line breaks between arguments of a function call" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"consistent"
|
||||
]
|
||||
}],
|
||||
defaultOptions: ["always"],
|
||||
messages: {
|
||||
unexpectedLineBreak: "There should be no line break here.",
|
||||
missingLineBreak: "There should be a line break after this argument."
|
||||
}
|
||||
},
|
||||
create(context, [option]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const checkers = {
|
||||
unexpected: {
|
||||
messageId: "unexpectedLineBreak",
|
||||
check: (prevToken, currentToken) => !(0, import_ast_utils.isTokenOnSameLine)(prevToken, currentToken),
|
||||
createFix: (token, tokenBefore) => (fixer) => fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], " ")
|
||||
},
|
||||
missing: {
|
||||
messageId: "missingLineBreak",
|
||||
check: (prevToken, currentToken) => (0, import_ast_utils.isTokenOnSameLine)(prevToken, currentToken),
|
||||
createFix: (token, tokenBefore) => (fixer) => fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], "\n")
|
||||
}
|
||||
};
|
||||
function checkArguments(argumentNodes, checker) {
|
||||
for (let i = 1; i < argumentNodes.length; i++) {
|
||||
const argumentNode = argumentNodes[i - 1];
|
||||
const prevArgToken = sourceCode.getLastToken(argumentNode);
|
||||
const currentArgToken = sourceCode.getFirstToken(argumentNodes[i]);
|
||||
if (checker.check(prevArgToken, currentArgToken)) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(currentArgToken, { includeComments: true });
|
||||
const hasLineCommentBefore = tokenBefore.type === "Line";
|
||||
context.report({
|
||||
node: argumentNodes[i - 1],
|
||||
loc: {
|
||||
start: tokenBefore.loc.end,
|
||||
end: currentArgToken.loc.start
|
||||
},
|
||||
messageId: checker.messageId,
|
||||
fix: hasLineCommentBefore ? null : checker.createFix(currentArgToken, tokenBefore)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
function check(argumentNodes) {
|
||||
if (argumentNodes.length < 2) return;
|
||||
if (option === "never") checkArguments(argumentNodes, checkers.unexpected);
|
||||
else if (option === "always") checkArguments(argumentNodes, checkers.missing);
|
||||
else if (option === "consistent") if ((0, import_ast_utils.isTokenOnSameLine)(sourceCode.getLastToken(argumentNodes[0]), sourceCode.getFirstToken(argumentNodes[1]))) checkArguments(argumentNodes, checkers.unexpected);
|
||||
else checkArguments(argumentNodes, checkers.missing);
|
||||
}
|
||||
return {
|
||||
CallExpression: (node) => check(node.arguments),
|
||||
NewExpression: (node) => check(node.arguments),
|
||||
ImportExpression: (node) => {
|
||||
if (node.options) check([node.source, node.options]);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { function_call_argument_newline_default as t };
|
||||
Generated
Vendored
+132
@@ -0,0 +1,132 @@
|
||||
import { d as safeReplaceTextBetween, f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var function_call_spacing_default = createRule({
|
||||
name: "function-call-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require or disallow spacing between function identifiers and their invocations" },
|
||||
fixable: "whitespace",
|
||||
schema: { anyOf: [{
|
||||
type: "array",
|
||||
items: [{
|
||||
type: "string",
|
||||
enum: ["never"]
|
||||
}],
|
||||
minItems: 0,
|
||||
maxItems: 1
|
||||
}, {
|
||||
type: "array",
|
||||
items: [{
|
||||
type: "string",
|
||||
enum: ["always"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
allowNewlines: { type: "boolean" },
|
||||
optionalChain: {
|
||||
type: "object",
|
||||
properties: {
|
||||
before: { type: "boolean" },
|
||||
after: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
minItems: 0,
|
||||
maxItems: 2
|
||||
}] },
|
||||
defaultOptions: ["never"],
|
||||
messages: {
|
||||
unexpectedWhitespace: "Unexpected whitespace between function name and paren.",
|
||||
unexpectedNewline: "Unexpected newline between function name and paren.",
|
||||
missing: "Missing space between function name and paren."
|
||||
}
|
||||
},
|
||||
create(context, [option, config]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const text = sourceCode.getText();
|
||||
const { allowNewlines = false, optionalChain = {
|
||||
before: true,
|
||||
after: true
|
||||
} } = config ?? {};
|
||||
function checkSpacing(node, leftToken, rightToken) {
|
||||
const isOptionalCall = (0, import_ast_utils.isOptionalCallExpression)(node);
|
||||
const textBetweenTokens = text.slice(leftToken.range[1], rightToken.range[0]).replace(/\/\*.*?\*\//gu, "");
|
||||
const hasWhitespace = /\s/u.test(textBetweenTokens);
|
||||
const hasNewline = hasWhitespace && import_ast_utils.LINEBREAK_MATCHER.test(textBetweenTokens);
|
||||
if (option === "never") {
|
||||
if (hasWhitespace) return context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: leftToken.loc.end,
|
||||
end: rightToken.loc.start
|
||||
},
|
||||
messageId: "unexpectedWhitespace",
|
||||
fix: safeReplaceTextBetween(sourceCode, leftToken, rightToken, isOptionalCall ? "?." : "")
|
||||
});
|
||||
} else if (isOptionalCall) {
|
||||
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: {
|
||||
start: leftToken.loc.end,
|
||||
end: rightToken.loc.start
|
||||
},
|
||||
messageId,
|
||||
fix: safeReplaceTextBetween(sourceCode, leftToken, rightToken, () => {
|
||||
let text = textBetweenTokens;
|
||||
if (!allowNewlines) {
|
||||
const GLOBAL_LINEBREAK_MATCHER = new RegExp(import_ast_utils.LINEBREAK_MATCHER.source, "g");
|
||||
text = text.replaceAll(GLOBAL_LINEBREAK_MATCHER, " ");
|
||||
}
|
||||
if (!hasCorrectPrefixSpace) text = beforeOptionChain ? ` ${text}` : text.trimStart();
|
||||
if (!hasCorrectSuffixSpace) text = afterOptionChain ? `${text} ` : text.trimEnd();
|
||||
return text;
|
||||
})
|
||||
});
|
||||
}
|
||||
} else if (!hasWhitespace) context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: leftToken.loc.end,
|
||||
end: rightToken.loc.start
|
||||
},
|
||||
messageId: "missing",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(rightToken, " ");
|
||||
}
|
||||
});
|
||||
else if (!allowNewlines && hasNewline) context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: leftToken.loc.end,
|
||||
end: rightToken.loc.start
|
||||
},
|
||||
messageId: "unexpectedNewline",
|
||||
fix: safeReplaceTextBetween(sourceCode, leftToken, rightToken, " ")
|
||||
});
|
||||
}
|
||||
return {
|
||||
"CallExpression, NewExpression": function(node) {
|
||||
const closingParenToken = sourceCode.getLastToken(node);
|
||||
const lastCalleeTokenWithoutPossibleParens = sourceCode.getLastToken(node.typeArguments ?? node.callee);
|
||||
const openingParenToken = sourceCode.getFirstTokenBetween(lastCalleeTokenWithoutPossibleParens, closingParenToken, import_ast_utils.isOpeningParenToken);
|
||||
if (!openingParenToken || openingParenToken.range[1] >= node.range[1]) return;
|
||||
checkSpacing(node, sourceCode.getTokenBefore(openingParenToken, import_ast_utils.isNotOptionalChainPunctuator), openingParenToken);
|
||||
},
|
||||
ImportExpression(node) {
|
||||
const leftToken = sourceCode.getFirstToken(node);
|
||||
checkSpacing(node, leftToken, sourceCode.getTokenAfter(leftToken));
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { function_call_spacing_default as t };
|
||||
Generated
Vendored
+149
@@ -0,0 +1,149 @@
|
||||
import { d as safeReplaceTextBetween, f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var function_paren_newline_default = createRule({
|
||||
name: "function-paren-newline",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent line breaks inside function parentheses" },
|
||||
fixable: "whitespace",
|
||||
schema: [{ oneOf: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"consistent",
|
||||
"multiline",
|
||||
"multiline-arguments"
|
||||
]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: { minItems: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
} },
|
||||
additionalProperties: false
|
||||
}] }],
|
||||
defaultOptions: ["multiline"],
|
||||
messages: {
|
||||
expectedBefore: "Expected newline before ')'.",
|
||||
expectedAfter: "Expected newline after '('.",
|
||||
expectedBetween: "Expected newline between arguments/params.",
|
||||
unexpectedBefore: "Unexpected newline before ')'.",
|
||||
unexpectedAfter: "Unexpected newline after '('."
|
||||
}
|
||||
},
|
||||
create(context, [rawOption]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const multilineOption = rawOption === "multiline";
|
||||
const multilineArgumentsOption = rawOption === "multiline-arguments";
|
||||
const consistentOption = rawOption === "consistent";
|
||||
let minItems;
|
||||
if (typeof rawOption === "object") minItems = rawOption.minItems;
|
||||
else if (rawOption === "always") minItems = 0;
|
||||
else if (rawOption === "never") minItems = Infinity;
|
||||
function shouldHaveNewlines(elements, hasLeftNewline) {
|
||||
if (multilineArgumentsOption && elements.length === 1) return hasLeftNewline;
|
||||
if (multilineOption || multilineArgumentsOption) return elements.some((element, index) => index !== elements.length - 1 && !(0, import_ast_utils.isTokenOnSameLine)(element, elements[index + 1]));
|
||||
if (consistentOption) return hasLeftNewline;
|
||||
return minItems == null || elements.length >= minItems;
|
||||
}
|
||||
function validateParens(parens, elements) {
|
||||
const leftParen = parens.leftParen;
|
||||
const rightParen = parens.rightParen;
|
||||
const tokenAfterLeftParen = sourceCode.getTokenAfter(leftParen);
|
||||
const tokenBeforeRightParen = sourceCode.getTokenBefore(rightParen);
|
||||
const hasLeftNewline = !(0, import_ast_utils.isTokenOnSameLine)(leftParen, tokenAfterLeftParen);
|
||||
const hasRightNewline = !(0, import_ast_utils.isTokenOnSameLine)(tokenBeforeRightParen, rightParen);
|
||||
const needsNewlines = shouldHaveNewlines(elements, hasLeftNewline);
|
||||
if (hasLeftNewline && !needsNewlines) context.report({
|
||||
node: leftParen,
|
||||
messageId: "unexpectedAfter",
|
||||
fix: safeReplaceTextBetween(sourceCode, leftParen, tokenAfterLeftParen, "")
|
||||
});
|
||||
else if (!hasLeftNewline && needsNewlines) context.report({
|
||||
node: leftParen,
|
||||
messageId: "expectedAfter",
|
||||
fix: (fixer) => fixer.insertTextAfter(leftParen, "\n")
|
||||
});
|
||||
if (hasRightNewline && !needsNewlines) context.report({
|
||||
node: rightParen,
|
||||
messageId: "unexpectedBefore",
|
||||
fix: safeReplaceTextBetween(sourceCode, tokenBeforeRightParen, rightParen, "")
|
||||
});
|
||||
else if (!hasRightNewline && needsNewlines) context.report({
|
||||
node: rightParen,
|
||||
messageId: "expectedBefore",
|
||||
fix: (fixer) => fixer.insertTextBefore(rightParen, "\n")
|
||||
});
|
||||
}
|
||||
function validateArguments(parens, elements) {
|
||||
const leftParen = parens.leftParen;
|
||||
const needsNewlines = shouldHaveNewlines(elements, !(0, import_ast_utils.isTokenOnSameLine)(leftParen, sourceCode.getTokenAfter(leftParen)));
|
||||
for (let i = 0; i <= elements.length - 2; i++) {
|
||||
const currentElement = elements[i];
|
||||
const nextElement = elements[i + 1];
|
||||
if (!!(0, import_ast_utils.isTokenOnSameLine)(currentElement, nextElement) && needsNewlines) context.report({
|
||||
node: currentElement,
|
||||
messageId: "expectedBetween",
|
||||
fix: (fixer) => fixer.insertTextBefore(nextElement, "\n")
|
||||
});
|
||||
}
|
||||
}
|
||||
function getParenTokens(node) {
|
||||
const isOpeningParenTokenOutsideTypeParameter = () => {
|
||||
let typeParameterOpeningLevel = 0;
|
||||
return (token) => {
|
||||
if (token.type === "Punctuator" && token.value === "<") typeParameterOpeningLevel += 1;
|
||||
if (token.type === "Punctuator" && token.value === ">") typeParameterOpeningLevel -= 1;
|
||||
return typeParameterOpeningLevel !== 0 ? false : (0, import_ast_utils.isOpeningParenToken)(token);
|
||||
};
|
||||
};
|
||||
switch (node.type) {
|
||||
case "NewExpression": if (!node.arguments.length && !((0, import_ast_utils.isOpeningParenToken)(sourceCode.getLastToken(node, { skip: 1 })) && (0, import_ast_utils.isClosingParenToken)(sourceCode.getLastToken(node)) && node.callee.range[1] < node.range[1])) return null;
|
||||
case "CallExpression": return {
|
||||
leftParen: sourceCode.getTokenAfter(node.callee, isOpeningParenTokenOutsideTypeParameter()),
|
||||
rightParen: sourceCode.getLastToken(node)
|
||||
};
|
||||
case "FunctionDeclaration":
|
||||
case "FunctionExpression": {
|
||||
const leftParen = sourceCode.getFirstToken(node, isOpeningParenTokenOutsideTypeParameter());
|
||||
return {
|
||||
leftParen,
|
||||
rightParen: node.params.length ? sourceCode.getTokenAfter(node.params[node.params.length - 1], import_ast_utils.isClosingParenToken) : sourceCode.getTokenAfter(leftParen)
|
||||
};
|
||||
}
|
||||
case "ArrowFunctionExpression": {
|
||||
const firstToken = sourceCode.getFirstToken(node, { skip: node.async ? 1 : 0 });
|
||||
if (!(0, import_ast_utils.isOpeningParenToken)(firstToken)) return null;
|
||||
return {
|
||||
leftParen: firstToken,
|
||||
rightParen: node.params.length ? sourceCode.getTokenAfter(node.params[node.params.length - 1], import_ast_utils.isClosingParenToken) : sourceCode.getTokenAfter(firstToken)
|
||||
};
|
||||
}
|
||||
case "ImportExpression": return {
|
||||
leftParen: sourceCode.getFirstToken(node, 1),
|
||||
rightParen: sourceCode.getLastToken(node)
|
||||
};
|
||||
default: throw new TypeError(`unexpected node with type ${node.type}`);
|
||||
}
|
||||
}
|
||||
return { [[
|
||||
"ArrowFunctionExpression",
|
||||
"CallExpression",
|
||||
"FunctionDeclaration",
|
||||
"FunctionExpression",
|
||||
"ImportExpression",
|
||||
"NewExpression"
|
||||
].join(", ")](node) {
|
||||
const parens = getParenTokens(node);
|
||||
let params;
|
||||
if (node.type === "ImportExpression") params = [node.source, ...node.options ? [node.options] : []];
|
||||
else if ((0, import_ast_utils.isFunction)(node)) params = node.params;
|
||||
else params = node.arguments;
|
||||
if (parens) {
|
||||
validateParens(parens, params);
|
||||
if (multilineArgumentsOption) validateArguments(parens, params);
|
||||
}
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { function_paren_newline_default as t };
|
||||
Generated
Vendored
+135
@@ -0,0 +1,135 @@
|
||||
import { f as createRule } from "../utils.js";
|
||||
const OVERRIDE_SCHEMA = { oneOf: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"before",
|
||||
"after",
|
||||
"both",
|
||||
"neither"
|
||||
]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
before: { type: "boolean" },
|
||||
after: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}] };
|
||||
var generator_star_spacing_default = createRule({
|
||||
name: "generator-star-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent spacing around `*` operators in generator functions" },
|
||||
fixable: "whitespace",
|
||||
schema: [{ oneOf: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"before",
|
||||
"after",
|
||||
"both",
|
||||
"neither"
|
||||
]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
before: { type: "boolean" },
|
||||
after: { type: "boolean" },
|
||||
named: OVERRIDE_SCHEMA,
|
||||
anonymous: OVERRIDE_SCHEMA,
|
||||
method: OVERRIDE_SCHEMA,
|
||||
shorthand: OVERRIDE_SCHEMA
|
||||
},
|
||||
additionalProperties: false
|
||||
}] }],
|
||||
defaultOptions: [{
|
||||
before: true,
|
||||
after: false
|
||||
}],
|
||||
messages: {
|
||||
missingBefore: "Missing space before *.",
|
||||
missingAfter: "Missing space after *.",
|
||||
unexpectedBefore: "Unexpected space before *.",
|
||||
unexpectedAfter: "Unexpected space after *."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const optionDefinitions = {
|
||||
before: {
|
||||
before: true,
|
||||
after: false
|
||||
},
|
||||
after: {
|
||||
before: false,
|
||||
after: true
|
||||
},
|
||||
both: {
|
||||
before: true,
|
||||
after: true
|
||||
},
|
||||
neither: {
|
||||
before: false,
|
||||
after: false
|
||||
}
|
||||
};
|
||||
function optionToDefinition(option, defaults) {
|
||||
if (!option) return defaults;
|
||||
return typeof option === "string" ? optionDefinitions[option] : Object.assign({}, defaults, option);
|
||||
}
|
||||
const modes = function() {
|
||||
const defaults = optionToDefinition(options, optionDefinitions.before);
|
||||
const { named, anonymous, method, shorthand } = options;
|
||||
return {
|
||||
named: optionToDefinition(named, defaults),
|
||||
anonymous: optionToDefinition(anonymous, defaults),
|
||||
method: optionToDefinition(method, defaults),
|
||||
shorthand: optionToDefinition(shorthand ?? method, defaults)
|
||||
};
|
||||
}();
|
||||
const sourceCode = context.sourceCode;
|
||||
function isStarToken(token) {
|
||||
return token.value === "*" && token.type === "Punctuator";
|
||||
}
|
||||
function getStarToken(node) {
|
||||
return sourceCode.getFirstToken("method" in node.parent && node.parent.method || node.parent.type === "MethodDefinition" ? node.parent : node, isStarToken);
|
||||
}
|
||||
function capitalize(str) {
|
||||
return str[0].toUpperCase() + str.slice(1);
|
||||
}
|
||||
function checkSpacing(kind, side, leftToken, rightToken) {
|
||||
if (!!(rightToken.range[0] - leftToken.range[1]) !== modes[kind][side]) {
|
||||
const after = leftToken.value === "*";
|
||||
const spaceRequired = modes[kind][side];
|
||||
const node = after ? leftToken : rightToken;
|
||||
const messageId = `${spaceRequired ? "missing" : "unexpected"}${capitalize(side)}`;
|
||||
context.report({
|
||||
node,
|
||||
messageId,
|
||||
fix(fixer) {
|
||||
if (spaceRequired) {
|
||||
if (after) return fixer.insertTextAfter(node, " ");
|
||||
return fixer.insertTextBefore(node, " ");
|
||||
}
|
||||
return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function checkFunction(node) {
|
||||
if (!node.generator) return;
|
||||
const starToken = getStarToken(node);
|
||||
const prevToken = sourceCode.getTokenBefore(starToken);
|
||||
const nextToken = sourceCode.getTokenAfter(starToken);
|
||||
let kind = "named";
|
||||
if (node.parent.type === "Property" && node.parent.method) kind = "shorthand";
|
||||
else if (node.parent.type === "MethodDefinition") kind = "method";
|
||||
else if (!node.id) kind = "anonymous";
|
||||
if (!((kind === "method" || kind === "shorthand") && starToken === sourceCode.getFirstToken(node.parent))) checkSpacing(kind, "before", prevToken, starToken);
|
||||
checkSpacing(kind, "after", starToken, nextToken);
|
||||
}
|
||||
return {
|
||||
FunctionDeclaration: checkFunction,
|
||||
FunctionExpression: checkFunction
|
||||
};
|
||||
}
|
||||
});
|
||||
export { generator_star_spacing_default as t };
|
||||
Generated
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
import { d as safeReplaceTextBetween, f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var implicit_arrow_linebreak_default = createRule({
|
||||
name: "implicit-arrow-linebreak",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce the location of arrow function bodies" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["beside", "below"]
|
||||
}],
|
||||
defaultOptions: ["beside"],
|
||||
messages: {
|
||||
expected: "Expected a linebreak before this expression.",
|
||||
unexpected: "Expected no linebreak before this expression."
|
||||
}
|
||||
},
|
||||
create(context, [option]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
function validateExpression(node) {
|
||||
if (node.body.type === "BlockStatement") return;
|
||||
const arrowToken = sourceCode.getTokenBefore(node.body, import_ast_utils.isNotOpeningParenToken);
|
||||
const firstTokenOfBody = sourceCode.getTokenAfter(arrowToken);
|
||||
const onSameLine = (0, import_ast_utils.isTokenOnSameLine)(arrowToken, firstTokenOfBody);
|
||||
if (onSameLine && option === "below") context.report({
|
||||
node: firstTokenOfBody,
|
||||
messageId: "expected",
|
||||
fix: (fixer) => fixer.insertTextBefore(firstTokenOfBody, "\n")
|
||||
});
|
||||
else if (!onSameLine && option === "beside") context.report({
|
||||
node: firstTokenOfBody,
|
||||
messageId: "unexpected",
|
||||
fix: safeReplaceTextBetween(sourceCode, arrowToken, firstTokenOfBody, " ")
|
||||
});
|
||||
}
|
||||
return { ArrowFunctionExpression: (node) => validateExpression(node) };
|
||||
}
|
||||
});
|
||||
export { implicit_arrow_linebreak_default as t };
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
import { _ as ASSIGNMENT_OPERATOR, f as createRule, z as isKeywordToken } from "../utils.js";
|
||||
var indent_binary_ops_default = createRule({
|
||||
name: "indent-binary-ops",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Indentation for binary operators" },
|
||||
fixable: "whitespace",
|
||||
schema: [{ oneOf: [{
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
}, {
|
||||
type: "string",
|
||||
enum: ["tab"]
|
||||
}] }],
|
||||
defaultOptions: [2],
|
||||
messages: { wrongIndentation: "Expected indentation of {{expected}}" }
|
||||
},
|
||||
create: (context, options) => {
|
||||
const { sourceCode } = context;
|
||||
const indentStr = options[0] === "tab" ? " " : " ".repeat(options[0] ?? 2);
|
||||
const indentCache = /* @__PURE__ */ new Map();
|
||||
function getIndentOfLine(line) {
|
||||
if (indentCache.has(line)) return indentCache.get(line);
|
||||
return sourceCode.lines[line - 1].match(/^\s*/)?.[0] ?? "";
|
||||
}
|
||||
function subtractionIndent(indent) {
|
||||
if (options[0] === "tab") return indent.slice(1);
|
||||
return indent.slice(options[0] ?? 2);
|
||||
}
|
||||
function getTargetIndent(indent, needAdditionIndent, needSubtractionIndent) {
|
||||
if (needAdditionIndent && !needSubtractionIndent) return indent + indentStr;
|
||||
if (!needAdditionIndent && needSubtractionIndent) return subtractionIndent(indent);
|
||||
return indent;
|
||||
}
|
||||
function firstTokenOfLine(line) {
|
||||
return sourceCode.tokensAndComments.find((token) => token.loc.start.line === line);
|
||||
}
|
||||
function lastTokenOfLine(line) {
|
||||
return [...sourceCode.tokensAndComments].reverse().find((token) => token.loc.end.line === line);
|
||||
}
|
||||
function isGreaterThanCloseBracketOfLine(line) {
|
||||
const tokensAndCommentsOfLine = sourceCode.tokensAndComments.filter((token) => token.loc.start.line === line);
|
||||
let openBracketCount = 0;
|
||||
let closeBracketCount = 0;
|
||||
for (const token of tokensAndCommentsOfLine) {
|
||||
if (token.value === "(") openBracketCount++;
|
||||
if (token.value === ")") closeBracketCount++;
|
||||
}
|
||||
return openBracketCount < closeBracketCount;
|
||||
}
|
||||
function isTypeKeywordOfNode(typeToken, node) {
|
||||
while (node.parent) {
|
||||
node = node.parent;
|
||||
if (node.type === "TSTypeAliasDeclaration" && context.sourceCode.getTokenBefore(node.id) === typeToken) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function handler(node, right) {
|
||||
if (node.loc.start.line === node.loc.end.line) return;
|
||||
let tokenRight = sourceCode.getFirstToken(right);
|
||||
let tokenOperator = sourceCode.getTokenBefore(tokenRight);
|
||||
while (tokenOperator.value === "(") {
|
||||
tokenRight = tokenOperator;
|
||||
tokenOperator = sourceCode.getTokenBefore(tokenRight);
|
||||
if (tokenOperator.range[0] <= right.parent.range[0]) return;
|
||||
}
|
||||
const tokenBeforeAll = sourceCode.getTokenBefore(node);
|
||||
const tokenLeft = sourceCode.getTokenBefore(tokenOperator);
|
||||
if (!(tokenRight.loc.start.line !== tokenLeft.loc.start.line)) return;
|
||||
const firstTokenOfLineLeft = firstTokenOfLine(tokenLeft.loc.start.line);
|
||||
const lastTokenOfLineLeft = lastTokenOfLine(tokenLeft.loc.start.line);
|
||||
const needAdditionIndent = isKeywordToken(firstTokenOfLineLeft) && ![
|
||||
"typeof",
|
||||
"instanceof",
|
||||
"this"
|
||||
].includes(firstTokenOfLineLeft.value) || firstTokenOfLineLeft?.type === "Identifier" && firstTokenOfLineLeft.value === "type" && isTypeKeywordOfNode(firstTokenOfLineLeft, node) || [
|
||||
":",
|
||||
"[",
|
||||
"(",
|
||||
"<"
|
||||
].concat(ASSIGNMENT_OPERATOR).includes(lastTokenOfLineLeft?.value || "") || [
|
||||
"[",
|
||||
"(",
|
||||
"{",
|
||||
"=>",
|
||||
":"
|
||||
].concat(ASSIGNMENT_OPERATOR).includes(tokenBeforeAll?.value || "") && firstTokenOfLineLeft?.loc.start.line === tokenBeforeAll?.loc.start.line;
|
||||
const needSubtractionIndent = lastTokenOfLineLeft?.value === ")" && isGreaterThanCloseBracketOfLine(tokenLeft.loc.start.line) && ![
|
||||
"]",
|
||||
")",
|
||||
"}"
|
||||
].includes(firstTokenOfLineLeft?.value || "");
|
||||
const indentLeft = getIndentOfLine(tokenLeft.loc.start.line);
|
||||
const indentRight = getIndentOfLine(tokenRight.loc.start.line);
|
||||
const indentTarget = getTargetIndent(indentLeft, needAdditionIndent, needSubtractionIndent);
|
||||
if (indentTarget !== indentRight) {
|
||||
const start = {
|
||||
line: tokenRight.loc.start.line,
|
||||
column: 0
|
||||
};
|
||||
const end = {
|
||||
line: tokenRight.loc.start.line,
|
||||
column: indentRight.length
|
||||
};
|
||||
context.report({
|
||||
loc: {
|
||||
start,
|
||||
end
|
||||
},
|
||||
messageId: "wrongIndentation",
|
||||
data: { expected: `${indentTarget.length} ${options[0] === "tab" ? "tab" : "space"}${indentTarget.length === 1 ? "" : "s"}` },
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange([sourceCode.getIndexFromLoc(start), sourceCode.getIndexFromLoc(end)], indentTarget);
|
||||
}
|
||||
});
|
||||
indentCache.set(tokenRight.loc.start.line, indentTarget);
|
||||
}
|
||||
}
|
||||
return {
|
||||
BinaryExpression(node) {
|
||||
handler(node, node.right);
|
||||
},
|
||||
LogicalExpression(node) {
|
||||
handler(node, node.right);
|
||||
},
|
||||
TSUnionType(node) {
|
||||
if (node.types.length > 1) node.types.forEach((type) => {
|
||||
handler(node, type);
|
||||
});
|
||||
},
|
||||
TSIntersectionType(node) {
|
||||
if (node.types.length > 1) node.types.forEach((type) => {
|
||||
handler(node, type);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { indent_binary_ops_default as t };
|
||||
+1141
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+78
@@ -0,0 +1,78 @@
|
||||
import { f as createRule } from "../utils.js";
|
||||
const INLINE_ELEMENTS = new Set([
|
||||
"a",
|
||||
"abbr",
|
||||
"acronym",
|
||||
"b",
|
||||
"bdo",
|
||||
"big",
|
||||
"button",
|
||||
"cite",
|
||||
"code",
|
||||
"dfn",
|
||||
"em",
|
||||
"i",
|
||||
"img",
|
||||
"input",
|
||||
"kbd",
|
||||
"label",
|
||||
"map",
|
||||
"object",
|
||||
"q",
|
||||
"samp",
|
||||
"script",
|
||||
"select",
|
||||
"small",
|
||||
"span",
|
||||
"strong",
|
||||
"sub",
|
||||
"sup",
|
||||
"textarea",
|
||||
"tt",
|
||||
"var"
|
||||
]);
|
||||
var jsx_child_element_spacing_default = createRule({
|
||||
name: "jsx-child-element-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce or disallow spaces inside of curly braces in JSX attributes and expressions" },
|
||||
schema: [],
|
||||
messages: {
|
||||
spacingAfterPrev: "Ambiguous spacing after previous element {{element}}",
|
||||
spacingBeforeNext: "Ambiguous spacing before next element {{element}}"
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
const TEXT_FOLLOWING_ELEMENT_PATTERN = /^[\t\v\f\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*\n\s*\S/;
|
||||
const TEXT_PRECEDING_ELEMENT_PATTERN = /\S[\t\v\f\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*\n\s*$/;
|
||||
const elementName = (node) => node.openingElement && node.openingElement.name && node.openingElement.name.type === "JSXIdentifier" && node.openingElement.name.name || "";
|
||||
const isInlineElement = (node) => node.type === "JSXElement" && INLINE_ELEMENTS.has(elementName(node));
|
||||
const handleJSX = (node) => {
|
||||
let lastChild = null;
|
||||
let child = null;
|
||||
[...node.children, null].forEach((nextChild) => {
|
||||
if ((lastChild || nextChild) && (!lastChild || isInlineElement(lastChild)) && child && (child.type === "Literal" || child.type === "JSXText") && (!nextChild || isInlineElement(nextChild)) && true) {
|
||||
if (lastChild && String(child.value).match(TEXT_FOLLOWING_ELEMENT_PATTERN)) context.report({
|
||||
messageId: "spacingAfterPrev",
|
||||
node: lastChild,
|
||||
loc: lastChild.loc.end,
|
||||
data: { element: elementName(lastChild) }
|
||||
});
|
||||
else if (nextChild && String(child.value).match(TEXT_PRECEDING_ELEMENT_PATTERN)) context.report({
|
||||
messageId: "spacingBeforeNext",
|
||||
node: nextChild,
|
||||
loc: nextChild.loc.start,
|
||||
data: { element: elementName(nextChild) }
|
||||
});
|
||||
}
|
||||
lastChild = child;
|
||||
child = nextChild;
|
||||
});
|
||||
};
|
||||
return {
|
||||
JSXElement: handleJSX,
|
||||
JSXFragment: handleJSX
|
||||
};
|
||||
}
|
||||
});
|
||||
export { jsx_child_element_spacing_default as t };
|
||||
Generated
Vendored
+202
@@ -0,0 +1,202 @@
|
||||
import { f as createRule } from "../utils.js";
|
||||
var jsx_closing_bracket_location_default = createRule({
|
||||
name: "jsx-closing-bracket-location",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce closing bracket location in JSX" },
|
||||
fixable: "code",
|
||||
schema: [{ anyOf: [
|
||||
{
|
||||
type: "string",
|
||||
enum: [
|
||||
"after-props",
|
||||
"props-aligned",
|
||||
"tag-aligned",
|
||||
"line-aligned"
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: { location: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"after-props",
|
||||
"props-aligned",
|
||||
"tag-aligned",
|
||||
"line-aligned"
|
||||
]
|
||||
} },
|
||||
additionalProperties: false
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
nonEmpty: { oneOf: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"after-props",
|
||||
"props-aligned",
|
||||
"tag-aligned",
|
||||
"line-aligned"
|
||||
]
|
||||
}, {
|
||||
type: "boolean",
|
||||
enum: [false]
|
||||
}] },
|
||||
selfClosing: { oneOf: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"after-props",
|
||||
"props-aligned",
|
||||
"tag-aligned",
|
||||
"line-aligned"
|
||||
]
|
||||
}, {
|
||||
type: "boolean",
|
||||
enum: [false]
|
||||
}] }
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
] }],
|
||||
defaultOptions: ["tag-aligned"],
|
||||
messages: { bracketLocation: "The closing bracket must be {{location}}{{details}}" }
|
||||
},
|
||||
create(context, [config]) {
|
||||
const MESSAGE_LOCATION = {
|
||||
"after-props": "placed after the last prop",
|
||||
"after-tag": "placed after the opening tag",
|
||||
"props-aligned": "aligned with the last prop",
|
||||
"tag-aligned": "aligned with the opening tag",
|
||||
"line-aligned": "aligned with the line containing the opening tag"
|
||||
};
|
||||
const { nonEmpty = "tag-aligned", selfClosing = "tag-aligned" } = typeof config === "string" ? {
|
||||
nonEmpty: config,
|
||||
selfClosing: config
|
||||
} : "location" in config ? {
|
||||
nonEmpty: config.location,
|
||||
selfClosing: config.location
|
||||
} : config;
|
||||
function getExpectedLocation(tokens) {
|
||||
let location;
|
||||
if (typeof tokens.lastProp === "undefined") location = "after-tag";
|
||||
else if (tokens.opening.line === tokens.lastProp.lastLine) location = "after-props";
|
||||
else location = tokens.selfClosing ? selfClosing : nonEmpty;
|
||||
return location;
|
||||
}
|
||||
function getCorrectColumn(tokens, expectedLocation) {
|
||||
switch (expectedLocation) {
|
||||
case "props-aligned": return tokens.lastProp.column;
|
||||
case "tag-aligned": return tokens.opening.column;
|
||||
case "line-aligned": return tokens.openingStartOfLine.column;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
function hasCorrectLocation(tokens, expectedLocation) {
|
||||
switch (expectedLocation) {
|
||||
case "after-tag": return tokens.tag.line === tokens.closing.line;
|
||||
case "after-props": return tokens.lastProp.lastLine === tokens.closing.line;
|
||||
case "props-aligned":
|
||||
case "tag-aligned":
|
||||
case "line-aligned": return getCorrectColumn(tokens, expectedLocation) === tokens.closing.column;
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
function getIndentation(tokens, expectedLocation, correctColumn) {
|
||||
const newColumn = correctColumn || 0;
|
||||
let indentation;
|
||||
let spaces = [];
|
||||
switch (expectedLocation) {
|
||||
case "props-aligned":
|
||||
indentation = /^\s*/.exec(context.sourceCode.lines[tokens.lastProp.firstLine - 1])[0];
|
||||
break;
|
||||
case "tag-aligned":
|
||||
case "line-aligned":
|
||||
indentation = /^\s*/.exec(context.sourceCode.lines[tokens.opening.line - 1])[0];
|
||||
break;
|
||||
default: indentation = "";
|
||||
}
|
||||
if (indentation.length + 1 < newColumn) spaces = Array.from({ length: +correctColumn + 1 - indentation.length });
|
||||
return indentation + spaces.join(" ");
|
||||
}
|
||||
function getTokensLocations(node) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const opening = sourceCode.getFirstToken(node).loc.start;
|
||||
const closing = sourceCode.getLastTokens(node, node.selfClosing ? 2 : 1)[0].loc.start;
|
||||
const tag = sourceCode.getFirstToken(node.name).loc.start;
|
||||
let lastProp;
|
||||
if (node.attributes.length) {
|
||||
lastProp = node.attributes[node.attributes.length - 1];
|
||||
lastProp = {
|
||||
column: sourceCode.getFirstToken(lastProp).loc.start.column,
|
||||
firstLine: sourceCode.getFirstToken(lastProp).loc.start.line,
|
||||
lastLine: sourceCode.getLastToken(lastProp).loc.end.line
|
||||
};
|
||||
}
|
||||
const openingLine = sourceCode.lines[opening.line - 1];
|
||||
const closingLine = sourceCode.lines[closing.line - 1];
|
||||
const isTab = {
|
||||
openTab: /^\t/.test(openingLine),
|
||||
closeTab: /^\t/.test(closingLine)
|
||||
};
|
||||
const openingStartOfLine = {
|
||||
column: /^\s*/.exec(openingLine)?.[0].length,
|
||||
line: opening.line
|
||||
};
|
||||
return {
|
||||
isTab,
|
||||
tag,
|
||||
opening,
|
||||
closing,
|
||||
lastProp,
|
||||
selfClosing: node.selfClosing,
|
||||
openingStartOfLine
|
||||
};
|
||||
}
|
||||
return { "JSXOpeningElement:exit": function(node) {
|
||||
const lastAttributeNode = node.attributes.at(-1);
|
||||
const cachedLastAttributeEndPos = lastAttributeNode ? lastAttributeNode.range[1] : null;
|
||||
let expectedNextLine;
|
||||
const tokens = getTokensLocations(node);
|
||||
let expectedLocation = getExpectedLocation(tokens);
|
||||
let usingSameIndentation = true;
|
||||
if (expectedLocation === "tag-aligned") usingSameIndentation = tokens.isTab.openTab === tokens.isTab.closeTab;
|
||||
const lastComment = context.sourceCode.getCommentsInside(node).at(-1);
|
||||
const hasTrailingComment = lastComment && lastComment.range[0] > (lastAttributeNode ?? node.name).range[1];
|
||||
if ((expectedLocation === "after-props" || expectedLocation === "after-tag") && !(hasCorrectLocation(tokens, expectedLocation) && usingSameIndentation) && hasTrailingComment) expectedLocation = "line-aligned";
|
||||
if (hasCorrectLocation(tokens, expectedLocation) && usingSameIndentation) return;
|
||||
const data = {
|
||||
location: MESSAGE_LOCATION[expectedLocation],
|
||||
details: ""
|
||||
};
|
||||
const correctColumn = getCorrectColumn(tokens, expectedLocation);
|
||||
if (correctColumn !== null) {
|
||||
expectedNextLine = tokens.lastProp && tokens.lastProp.lastLine === tokens.closing.line;
|
||||
data.details = ` (expected column ${correctColumn + 1}${expectedNextLine ? " on the next line)" : ")"}`;
|
||||
}
|
||||
context.report({
|
||||
node,
|
||||
messageId: "bracketLocation",
|
||||
loc: tokens.closing,
|
||||
data,
|
||||
fix(fixer) {
|
||||
const closingTag = tokens.selfClosing ? "/>" : ">";
|
||||
switch (expectedLocation) {
|
||||
case "after-tag":
|
||||
if (cachedLastAttributeEndPos) return fixer.replaceTextRange([cachedLastAttributeEndPos, node.range[1]], (expectedNextLine ? "\n" : "") + closingTag);
|
||||
return fixer.replaceTextRange([node.name.range[1], node.range[1]], (expectedNextLine ? "\n" : " ") + closingTag);
|
||||
case "after-props": return fixer.replaceTextRange([cachedLastAttributeEndPos, node.range[1]], (expectedNextLine ? "\n" : "") + closingTag);
|
||||
case "props-aligned":
|
||||
case "tag-aligned":
|
||||
case "line-aligned": {
|
||||
const rangeStart = hasTrailingComment ? lastComment.range[1] : cachedLastAttributeEndPos;
|
||||
return fixer.replaceTextRange([rangeStart, node.range[1]], `\n${getIndentation(tokens, expectedLocation, correctColumn)}${closingTag}`);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { jsx_closing_bracket_location_default as t };
|
||||
Generated
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
import { V as isNodeFirstInLine, f as createRule } from "../utils.js";
|
||||
const MESSAGE_LOCATION = {
|
||||
"tag-aligned": "matchIndent",
|
||||
"line-aligned": "alignWithOpening"
|
||||
};
|
||||
var jsx_closing_tag_location_default = createRule({
|
||||
name: "jsx-closing-tag-location",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce closing tag location for multiline JSX" },
|
||||
fixable: "whitespace",
|
||||
schema: [{ anyOf: [{
|
||||
type: "string",
|
||||
enum: ["tag-aligned", "line-aligned"]
|
||||
}] }],
|
||||
defaultOptions: ["tag-aligned"],
|
||||
messages: {
|
||||
onOwnLine: "Closing tag of a multiline JSX expression must be on its own line.",
|
||||
matchIndent: "Expected closing tag to match indentation of opening.",
|
||||
alignWithOpening: "Expected closing tag to be aligned with the line containing the opening tag"
|
||||
}
|
||||
},
|
||||
create(context, [option]) {
|
||||
function getIndentation(openingStartOfLine, opening) {
|
||||
if (option === "line-aligned") return openingStartOfLine.column;
|
||||
else return opening.loc.start.column;
|
||||
}
|
||||
function handleClosingElement(node) {
|
||||
if (!node.parent) return;
|
||||
const sourceCode = context.sourceCode;
|
||||
const opening = "openingFragment" in node.parent ? node.parent.openingFragment : node.parent.openingElement;
|
||||
const openingLoc = sourceCode.getFirstToken(opening).loc.start;
|
||||
const openingLine = sourceCode.lines[openingLoc.line - 1];
|
||||
const openingStartOfLine = {
|
||||
column: /^\s*/.exec(openingLine)?.[0].length,
|
||||
line: openingLoc.line
|
||||
};
|
||||
if (opening.loc.start.line === node.loc.start.line) return;
|
||||
if (opening.loc.start.column === node.loc.start.column && option === "tag-aligned") return;
|
||||
if (openingStartOfLine.column === node.loc.start.column && option === "line-aligned") return;
|
||||
const messageId = isNodeFirstInLine(context, node) ? MESSAGE_LOCATION[option] : "onOwnLine";
|
||||
context.report({
|
||||
node,
|
||||
messageId,
|
||||
loc: node.loc,
|
||||
fix(fixer) {
|
||||
const indent = new Array((getIndentation(openingStartOfLine, opening) || 0) + 1).join(" ");
|
||||
if (isNodeFirstInLine(context, node)) return fixer.replaceTextRange([node.range[0] - node.loc.start.column, node.range[0]], indent);
|
||||
return fixer.insertTextBefore(node, `\n${indent}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
JSXClosingElement: handleClosingElement,
|
||||
JSXClosingFragment: handleClosingElement
|
||||
};
|
||||
}
|
||||
});
|
||||
export { jsx_closing_tag_location_default as t };
|
||||
Generated
Vendored
+197
@@ -0,0 +1,197 @@
|
||||
import { X as isWhiteSpaces, f as createRule, g as import_ast_utils, o as isJSX } from "../utils.js";
|
||||
const OPTION_VALUES = [
|
||||
"always",
|
||||
"never",
|
||||
"ignore"
|
||||
];
|
||||
var jsx_curly_brace_presence_default = createRule({
|
||||
name: "jsx-curly-brace-presence",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow unnecessary JSX expressions when literals alone are sufficient or enforce JSX expressions on literals in JSX children or attributes" },
|
||||
fixable: "code",
|
||||
schema: [{ anyOf: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
props: {
|
||||
type: "string",
|
||||
enum: OPTION_VALUES
|
||||
},
|
||||
children: {
|
||||
type: "string",
|
||||
enum: OPTION_VALUES
|
||||
},
|
||||
propElementValues: {
|
||||
type: "string",
|
||||
enum: OPTION_VALUES
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}, {
|
||||
type: "string",
|
||||
enum: OPTION_VALUES
|
||||
}] }],
|
||||
defaultOptions: [{
|
||||
props: "never",
|
||||
children: "never",
|
||||
propElementValues: "ignore"
|
||||
}],
|
||||
messages: {
|
||||
unnecessaryCurly: "Curly braces are unnecessary here.",
|
||||
missingCurly: "Need to wrap this literal in a JSX expression."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const HTML_ENTITY_REGEX = () => /&[A-Z\d#]+;/gi;
|
||||
const { props, children, propElementValues } = typeof options === "string" ? {
|
||||
props: options,
|
||||
children: options,
|
||||
propElementValues: "ignore"
|
||||
} : options;
|
||||
function containsLineTerminators(rawStringValue) {
|
||||
return import_ast_utils.LINEBREAK_MATCHER.test(rawStringValue);
|
||||
}
|
||||
function containsBackslash(rawStringValue) {
|
||||
return rawStringValue.includes("\\");
|
||||
}
|
||||
function containsHTMLEntity(rawStringValue) {
|
||||
return HTML_ENTITY_REGEX().test(rawStringValue);
|
||||
}
|
||||
function containsOnlyHtmlEntities(rawStringValue) {
|
||||
return rawStringValue.replace(HTML_ENTITY_REGEX(), "").trim() === "";
|
||||
}
|
||||
function containsDisallowedJSXTextChars(rawStringValue) {
|
||||
return /[{<>}]/.test(rawStringValue);
|
||||
}
|
||||
function containsQuoteCharacters(value) {
|
||||
return /['"]/.test(value);
|
||||
}
|
||||
function containsMultilineComment(value) {
|
||||
return /\/\*/.test(value);
|
||||
}
|
||||
function escapeDoubleQuotes(rawStringValue) {
|
||||
return rawStringValue.replace(/\\"/g, "\"").replace(/"/g, "\\\"");
|
||||
}
|
||||
function escapeBackslashes(rawStringValue) {
|
||||
return rawStringValue.replace(/\\/g, "\\\\");
|
||||
}
|
||||
function needToEscapeCharacterForJSX(raw, node) {
|
||||
return containsBackslash(raw) || containsHTMLEntity(raw) || node.parent.type !== "JSXAttribute" && containsDisallowedJSXTextChars(raw);
|
||||
}
|
||||
function containsWhitespaceExpression(child) {
|
||||
if (child.type === "JSXExpressionContainer") {
|
||||
const value = child.expression.value;
|
||||
return value ? isWhiteSpaces(value) : false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isLineBreak(text) {
|
||||
return containsLineTerminators(text) && text.trim() === "";
|
||||
}
|
||||
function wrapNonHTMLEntities(text) {
|
||||
const HTML_ENTITY = "<HTML_ENTITY>";
|
||||
const withCurlyBraces = text.split(HTML_ENTITY_REGEX()).map((word) => word === "" ? "" : `{${JSON.stringify(word)}}`).join(HTML_ENTITY);
|
||||
return text.match(HTML_ENTITY_REGEX()).reduce((acc, htmlEntity) => acc.replace(HTML_ENTITY, htmlEntity), withCurlyBraces);
|
||||
}
|
||||
function wrapWithCurlyBraces(rawText) {
|
||||
if (!containsLineTerminators(rawText)) return `{${JSON.stringify(rawText)}}`;
|
||||
return rawText.split("\n").map((line) => {
|
||||
if (line.trim() === "") return line;
|
||||
const firstCharIndex = line.search(/\S/);
|
||||
const leftWhitespace = line.slice(0, firstCharIndex);
|
||||
const text = line.slice(firstCharIndex);
|
||||
if (containsHTMLEntity(line)) return `${leftWhitespace}${wrapNonHTMLEntities(text)}`;
|
||||
return `${leftWhitespace}{${JSON.stringify(text)}}`;
|
||||
}).join("\n");
|
||||
}
|
||||
function reportUnnecessaryCurly(JSXExpressionNode) {
|
||||
context.report({
|
||||
messageId: "unnecessaryCurly",
|
||||
node: JSXExpressionNode,
|
||||
fix(fixer) {
|
||||
const expression = JSXExpressionNode.expression;
|
||||
let textToReplace;
|
||||
if (isJSX(expression)) textToReplace = context.sourceCode.getText(expression);
|
||||
else if (JSXExpressionNode.parent.type === "JSXAttribute") textToReplace = `"${expression.type === "TemplateLiteral" ? expression.quasis[0].value.raw : expression.raw.slice(1, -1)}"`;
|
||||
else if (isJSX(expression)) textToReplace = context.sourceCode.getText(expression);
|
||||
else textToReplace = expression.type === "TemplateLiteral" ? expression.quasis[0].value.cooked : expression.value;
|
||||
return fixer.replaceText(JSXExpressionNode, textToReplace);
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportMissingCurly(literalNode) {
|
||||
context.report({
|
||||
messageId: "missingCurly",
|
||||
node: literalNode,
|
||||
fix(fixer) {
|
||||
if (isJSX(literalNode)) return fixer.replaceText(literalNode, `{${context.sourceCode.getText(literalNode)}}`);
|
||||
if (containsOnlyHtmlEntities(literalNode.raw) || literalNode.parent.type === "JSXAttribute" && containsLineTerminators(literalNode.raw) || isLineBreak(literalNode.raw)) return null;
|
||||
const expression = literalNode.parent.type === "JSXAttribute" ? `{"${escapeDoubleQuotes(escapeBackslashes(literalNode.raw.slice(1, -1)))}"}` : wrapWithCurlyBraces(literalNode.raw);
|
||||
return fixer.replaceText(literalNode, expression);
|
||||
}
|
||||
});
|
||||
}
|
||||
function isWhiteSpaceLiteral(node) {
|
||||
return node.type && node.type === "Literal" && node.value && isWhiteSpaces(node.value);
|
||||
}
|
||||
function isStringWithTrailingWhiteSpaces(value) {
|
||||
return /^\s|\s$/.test(value);
|
||||
}
|
||||
function isLiteralWithTrailingWhiteSpaces(node) {
|
||||
return node.type && node.type === "Literal" && node.value && isStringWithTrailingWhiteSpaces(node.value);
|
||||
}
|
||||
function lintUnnecessaryCurly(JSXExpressionNode) {
|
||||
const expression = JSXExpressionNode.expression;
|
||||
const expressionType = expression.type;
|
||||
const sourceCode = context.sourceCode;
|
||||
if (sourceCode.getCommentsInside && sourceCode.getCommentsInside(JSXExpressionNode).length > 0) return;
|
||||
if ((expressionType === "Literal" || expressionType === "JSXText") && typeof expression.value === "string" && (JSXExpressionNode.parent.type === "JSXAttribute" && !isWhiteSpaceLiteral(expression) || !isLiteralWithTrailingWhiteSpaces(expression)) && !containsMultilineComment(expression.value) && !needToEscapeCharacterForJSX(expression.raw, JSXExpressionNode) && (isJSX(JSXExpressionNode.parent) || !containsQuoteCharacters(expression.value))) reportUnnecessaryCurly(JSXExpressionNode);
|
||||
else if (expressionType === "TemplateLiteral" && expression.expressions.length === 0 && !expression.quasis[0].value.raw.includes("\n") && !isStringWithTrailingWhiteSpaces(expression.quasis[0].value.raw) && !needToEscapeCharacterForJSX(expression.quasis[0].value.raw, JSXExpressionNode) && !containsQuoteCharacters(expression.quasis[0].value.cooked)) reportUnnecessaryCurly(JSXExpressionNode);
|
||||
else if (isJSX(expression)) reportUnnecessaryCurly(JSXExpressionNode);
|
||||
}
|
||||
function areRuleConditionsSatisfied(parent, ruleCondition) {
|
||||
return parent.type === "JSXAttribute" && props === ruleCondition || isJSX(parent) && children === ruleCondition;
|
||||
}
|
||||
function getAdjacentSiblings(node, children) {
|
||||
for (let i = 1; i < children.length - 1; i++) if (node === children[i]) return [children[i - 1], children[i + 1]];
|
||||
if (node === children[0] && children[1]) return [children[1]];
|
||||
if (node === children[children.length - 1] && children[children.length - 2]) return [children[children.length - 2]];
|
||||
return [];
|
||||
}
|
||||
function hasAdjacentJsxExpressionContainers(node, children) {
|
||||
if (!children) return false;
|
||||
return getAdjacentSiblings(node, children.filter((child) => !isWhiteSpaceLiteral(child))).some((x) => x.type && x.type === "JSXExpressionContainer");
|
||||
}
|
||||
function hasAdjacentJsx(node, children) {
|
||||
if (!children) return false;
|
||||
return getAdjacentSiblings(node, children.filter((child) => !isWhiteSpaceLiteral(child))).some((x) => x.type && ["JSXExpressionContainer", "JSXElement"].includes(x.type));
|
||||
}
|
||||
function shouldCheckForUnnecessaryCurly(node) {
|
||||
const parent = node.parent;
|
||||
if (parent.type && parent.type === "JSXAttribute" && node.expression && node.expression.type && node.expression.type !== "Literal" && node.expression.type !== "StringLiteral" && node.expression.type !== "TemplateLiteral") return false;
|
||||
if (isJSX(parent) && hasAdjacentJsxExpressionContainers(node, parent.children)) return false;
|
||||
if (containsWhitespaceExpression(node) && hasAdjacentJsx(node, parent.children)) return false;
|
||||
if (parent.children && parent.children.length === 1 && containsWhitespaceExpression(node)) return false;
|
||||
return areRuleConditionsSatisfied(parent, "never");
|
||||
}
|
||||
function shouldCheckForMissingCurly(node) {
|
||||
if (isJSX(node)) return propElementValues !== "ignore";
|
||||
if (isLineBreak(node.raw) || containsOnlyHtmlEntities(node.raw)) return false;
|
||||
const parent = node.parent;
|
||||
if (parent.children && parent.children.length === 1 && containsWhitespaceExpression(parent.children[0])) return false;
|
||||
return areRuleConditionsSatisfied(parent, "always");
|
||||
}
|
||||
return {
|
||||
"JSXAttribute > JSXExpressionContainer > JSXElement": function(node) {
|
||||
if (propElementValues === "never") reportUnnecessaryCurly(node.parent);
|
||||
},
|
||||
JSXExpressionContainer(node) {
|
||||
if (shouldCheckForUnnecessaryCurly(node)) lintUnnecessaryCurly(node);
|
||||
},
|
||||
"JSXAttribute > JSXElement, Literal, JSXText": function(node) {
|
||||
if (shouldCheckForMissingCurly(node)) reportMissingCurly(node);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { jsx_curly_brace_presence_default as t };
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
import { K as isSingleLine, d as safeReplaceTextBetween, f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var jsx_curly_newline_default = createRule({
|
||||
name: "jsx-curly-newline",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent linebreaks in curly braces in JSX attributes and expressions" },
|
||||
fixable: "whitespace",
|
||||
schema: [{ anyOf: [{
|
||||
type: "string",
|
||||
enum: ["consistent", "never"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
singleline: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"consistent",
|
||||
"require",
|
||||
"forbid"
|
||||
]
|
||||
},
|
||||
multiline: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"consistent",
|
||||
"require",
|
||||
"forbid"
|
||||
]
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}] }],
|
||||
defaultOptions: ["consistent"],
|
||||
messages: {
|
||||
expectedBefore: "Expected newline before '}'.",
|
||||
expectedAfter: "Expected newline after '{'.",
|
||||
unexpectedBefore: "Unexpected newline before '}'.",
|
||||
unexpectedAfter: "Unexpected newline after '{'."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const { multiline = "consistent", singleline = "consistent" } = options === "never" ? {
|
||||
multiline: "forbid",
|
||||
singleline: "forbid"
|
||||
} : typeof options === "string" ? {
|
||||
multiline: options,
|
||||
singleline: options
|
||||
} : options;
|
||||
function shouldHaveNewlines(expression, hasLeftNewline) {
|
||||
switch (!isSingleLine(expression) ? multiline : singleline) {
|
||||
case "forbid": return false;
|
||||
case "require": return true;
|
||||
default: return hasLeftNewline;
|
||||
}
|
||||
}
|
||||
function validateCurlys(curlys, expression) {
|
||||
const leftCurly = curlys.leftCurly;
|
||||
const rightCurly = curlys.rightCurly;
|
||||
const tokenAfterLeftCurly = sourceCode.getTokenAfter(leftCurly);
|
||||
const tokenBeforeRightCurly = sourceCode.getTokenBefore(rightCurly);
|
||||
const hasLeftNewline = !(0, import_ast_utils.isTokenOnSameLine)(leftCurly, tokenAfterLeftCurly);
|
||||
const hasRightNewline = !(0, import_ast_utils.isTokenOnSameLine)(tokenBeforeRightCurly, rightCurly);
|
||||
const needsNewlines = shouldHaveNewlines(expression, hasLeftNewline);
|
||||
if (hasLeftNewline && !needsNewlines) context.report({
|
||||
node: leftCurly,
|
||||
messageId: "unexpectedAfter",
|
||||
fix: safeReplaceTextBetween(sourceCode, leftCurly, tokenAfterLeftCurly, "")
|
||||
});
|
||||
else if (!hasLeftNewline && needsNewlines) context.report({
|
||||
node: leftCurly,
|
||||
messageId: "expectedAfter",
|
||||
fix: (fixer) => fixer.insertTextAfter(leftCurly, "\n")
|
||||
});
|
||||
if (hasRightNewline && !needsNewlines) context.report({
|
||||
node: rightCurly,
|
||||
messageId: "unexpectedBefore",
|
||||
fix: safeReplaceTextBetween(sourceCode, tokenBeforeRightCurly, rightCurly, "")
|
||||
});
|
||||
else if (!hasRightNewline && needsNewlines) context.report({
|
||||
node: rightCurly,
|
||||
messageId: "expectedBefore",
|
||||
fix: (fixer) => fixer.insertTextBefore(rightCurly, "\n")
|
||||
});
|
||||
}
|
||||
return { JSXExpressionContainer(node) {
|
||||
validateCurlys({
|
||||
leftCurly: sourceCode.getFirstToken(node),
|
||||
rightCurly: sourceCode.getLastToken(node)
|
||||
}, node.expression);
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { jsx_curly_newline_default as t };
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
const SPACING = {
|
||||
always: "always",
|
||||
never: "never"
|
||||
};
|
||||
const SPACING_VALUES = ["always", "never"];
|
||||
const BASIC_CONFIG_SCHEMA = {
|
||||
type: "object",
|
||||
properties: {
|
||||
when: {
|
||||
type: "string",
|
||||
enum: SPACING_VALUES
|
||||
},
|
||||
allowMultiline: { type: "boolean" },
|
||||
spacing: {
|
||||
type: "object",
|
||||
properties: { objectLiterals: {
|
||||
type: "string",
|
||||
enum: SPACING_VALUES
|
||||
} },
|
||||
additionalProperties: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
};
|
||||
const BASIC_CONFIG_OR_BOOLEAN_SCHEMA = { anyOf: [BASIC_CONFIG_SCHEMA, { type: "boolean" }] };
|
||||
var jsx_curly_spacing_default = createRule({
|
||||
name: "jsx-curly-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce or disallow spaces inside of curly braces in JSX attributes and expressions" },
|
||||
fixable: "code",
|
||||
schema: {
|
||||
type: "array",
|
||||
items: [{ anyOf: [{
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
...BASIC_CONFIG_SCHEMA.properties,
|
||||
attributes: BASIC_CONFIG_OR_BOOLEAN_SCHEMA,
|
||||
children: BASIC_CONFIG_OR_BOOLEAN_SCHEMA
|
||||
}
|
||||
}, {
|
||||
type: "string",
|
||||
enum: SPACING_VALUES
|
||||
}] }, {
|
||||
type: "object",
|
||||
properties: {
|
||||
allowMultiline: { type: "boolean" },
|
||||
spacing: {
|
||||
type: "object",
|
||||
properties: { objectLiterals: {
|
||||
type: "string",
|
||||
enum: SPACING_VALUES
|
||||
} },
|
||||
additionalProperties: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}]
|
||||
},
|
||||
defaultOptions: [{
|
||||
when: "never",
|
||||
allowMultiline: true
|
||||
}],
|
||||
messages: {
|
||||
noNewlineAfter: "There should be no newline after '{{token}}'",
|
||||
noNewlineBefore: "There should be no newline before '{{token}}'",
|
||||
noSpaceAfter: "There should be no space after '{{token}}'",
|
||||
noSpaceBefore: "There should be no space before '{{token}}'",
|
||||
spaceNeededAfter: "A space is required after '{{token}}'",
|
||||
spaceNeededBefore: "A space is required before '{{token}}'"
|
||||
}
|
||||
},
|
||||
create(context, [firstOption, secondOption = {}]) {
|
||||
const { when: defaultWhen = "never", allowMultiline: defaultAllowMultiline = true, spacing: defaultSpacing = {}, attributes = true, children = false } = typeof firstOption === "string" ? {
|
||||
when: firstOption,
|
||||
...secondOption
|
||||
} : firstOption;
|
||||
function normalizeConfig(configOrTrue) {
|
||||
const { when = defaultWhen, allowMultiline = defaultAllowMultiline, spacing = defaultSpacing } = configOrTrue === true ? {} : configOrTrue;
|
||||
return {
|
||||
when,
|
||||
allowMultiline,
|
||||
objectLiteralSpaces: spacing.objectLiterals ?? when
|
||||
};
|
||||
}
|
||||
const attributesConfig = attributes ? normalizeConfig(attributes) : null;
|
||||
const childrenConfig = children ? normalizeConfig(children) : null;
|
||||
const sourceCode = context.sourceCode;
|
||||
function fixByTrimmingWhitespace(fixer, fromLoc, toLoc, mode, spacing = "") {
|
||||
let replacementText = sourceCode.text.slice(fromLoc, toLoc);
|
||||
if (mode === "start") replacementText = replacementText.replace(/^\s+/gm, "");
|
||||
else replacementText = replacementText.replace(/\s+$/gm, "");
|
||||
if (spacing === SPACING.always) if (mode === "start") replacementText += " ";
|
||||
else replacementText = ` ${replacementText}`;
|
||||
return fixer.replaceTextRange([fromLoc, toLoc], replacementText);
|
||||
}
|
||||
function reportNoBeginningNewline(node, token, spacing) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc.start,
|
||||
messageId: "noNewlineAfter",
|
||||
data: { token: token.value },
|
||||
fix(fixer) {
|
||||
const nextToken = sourceCode.getTokenAfter(token);
|
||||
return fixByTrimmingWhitespace(fixer, token.range[1], nextToken.range[0], "start", spacing);
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportNoEndingNewline(node, token, spacing) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc.start,
|
||||
messageId: "noNewlineBefore",
|
||||
data: { token: token.value },
|
||||
fix(fixer) {
|
||||
return fixByTrimmingWhitespace(fixer, sourceCode.getTokenBefore(token).range[1], token.range[0], "end", spacing);
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportNoBeginningSpace(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc.start,
|
||||
messageId: "noSpaceAfter",
|
||||
data: { token: token.value },
|
||||
fix(fixer) {
|
||||
const nextToken = sourceCode.getTokenAfter(token);
|
||||
const nextComment = sourceCode.getCommentsAfter(token);
|
||||
if (nextComment.length > 0) return fixByTrimmingWhitespace(fixer, token.range[1], Math.min(nextToken.range[0], nextComment[0].range[0]), "start");
|
||||
return fixByTrimmingWhitespace(fixer, token.range[1], nextToken.range[0], "start");
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportNoEndingSpace(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc.start,
|
||||
messageId: "noSpaceBefore",
|
||||
data: { token: token.value },
|
||||
fix(fixer) {
|
||||
const previousToken = sourceCode.getTokenBefore(token);
|
||||
const previousComment = sourceCode.getCommentsBefore(token);
|
||||
if (previousComment.length > 0) return fixByTrimmingWhitespace(fixer, Math.max(previousToken.range[1], previousComment[0].range[1]), token.range[0], "end");
|
||||
return fixByTrimmingWhitespace(fixer, previousToken.range[1], token.range[0], "end");
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportRequiredBeginningSpace(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc.start,
|
||||
messageId: "spaceNeededAfter",
|
||||
data: { token: token.value },
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportRequiredEndingSpace(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc.start,
|
||||
messageId: "spaceNeededBefore",
|
||||
data: { token: token.value },
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
function validateBraceSpacing(node) {
|
||||
let config;
|
||||
switch (node.parent?.type) {
|
||||
case "JSXAttribute":
|
||||
case "JSXOpeningElement":
|
||||
config = attributesConfig;
|
||||
break;
|
||||
case "JSXElement":
|
||||
case "JSXFragment":
|
||||
config = childrenConfig;
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
if (config === null) return;
|
||||
const first = sourceCode.getFirstToken(node);
|
||||
const last = sourceCode.getLastToken(node);
|
||||
let second = sourceCode.getTokenAfter(first, { includeComments: true });
|
||||
let penultimate = sourceCode.getTokenBefore(last, { includeComments: true });
|
||||
if (!second) {
|
||||
second = sourceCode.getTokenAfter(first);
|
||||
const leadingComments = sourceCode.getCommentsBefore(second);
|
||||
second = leadingComments ? leadingComments[0] : second;
|
||||
}
|
||||
if (!penultimate) {
|
||||
penultimate = sourceCode.getTokenBefore(last);
|
||||
const trailingComments = sourceCode.getCommentsAfter(penultimate);
|
||||
penultimate = trailingComments ? trailingComments[trailingComments.length - 1] : penultimate;
|
||||
}
|
||||
const spacing = first.value === second.value ? config.objectLiteralSpaces : config.when;
|
||||
if (spacing === SPACING.always) {
|
||||
if (!sourceCode.isSpaceBetween(first, second)) reportRequiredBeginningSpace(node, first);
|
||||
else if (!config.allowMultiline && !(0, import_ast_utils.isTokenOnSameLine)(first, second)) reportNoBeginningNewline(node, first, spacing);
|
||||
if (!sourceCode.isSpaceBetween(penultimate, last)) reportRequiredEndingSpace(node, last);
|
||||
else if (!config.allowMultiline && !(0, import_ast_utils.isTokenOnSameLine)(penultimate, last)) reportNoEndingNewline(node, last, spacing);
|
||||
} else if (spacing === SPACING.never) {
|
||||
if (!(0, import_ast_utils.isTokenOnSameLine)(first, second)) {
|
||||
if (!config.allowMultiline) reportNoBeginningNewline(node, first, spacing);
|
||||
} else if (sourceCode.isSpaceBetween(first, second)) reportNoBeginningSpace(node, first);
|
||||
if (!(0, import_ast_utils.isTokenOnSameLine)(penultimate, last)) {
|
||||
if (!config.allowMultiline) reportNoEndingNewline(node, last, spacing);
|
||||
} else if (sourceCode.isSpaceBetween(penultimate, last)) reportNoEndingSpace(node, last);
|
||||
}
|
||||
}
|
||||
return {
|
||||
JSXExpressionContainer: validateBraceSpacing,
|
||||
JSXSpreadAttribute: validateBraceSpacing
|
||||
};
|
||||
}
|
||||
});
|
||||
export { jsx_curly_spacing_default as t };
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import { f as createRule } from "../utils.js";
|
||||
var jsx_equals_spacing_default = createRule({
|
||||
name: "jsx-equals-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce or disallow spaces around equal signs in JSX attributes" },
|
||||
fixable: "code",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}],
|
||||
defaultOptions: ["never"],
|
||||
messages: {
|
||||
noSpaceBefore: "There should be no space before '='",
|
||||
noSpaceAfter: "There should be no space after '='",
|
||||
needSpaceBefore: "A space is required before '='",
|
||||
needSpaceAfter: "A space is required after '='"
|
||||
}
|
||||
},
|
||||
create(context, [config]) {
|
||||
return { JSXOpeningElement(node) {
|
||||
node.attributes.forEach((attrNode) => {
|
||||
if (!(attrNode.type !== "JSXSpreadAttribute" && attrNode.value !== null)) return;
|
||||
const sourceCode = context.sourceCode;
|
||||
const equalToken = sourceCode.getTokenAfter(attrNode.name);
|
||||
const spacedBefore = sourceCode.isSpaceBetween(attrNode.name, equalToken);
|
||||
const spacedAfter = sourceCode.isSpaceBetween(equalToken, attrNode.value);
|
||||
if (config === "never") {
|
||||
if (spacedBefore) context.report({
|
||||
node: attrNode,
|
||||
messageId: "noSpaceBefore",
|
||||
loc: equalToken.loc.start,
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([attrNode.name.range[1], equalToken.range[0]]);
|
||||
}
|
||||
});
|
||||
if (spacedAfter) context.report({
|
||||
node: attrNode,
|
||||
messageId: "noSpaceAfter",
|
||||
loc: equalToken.loc.start,
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([equalToken.range[1], attrNode.value.range[0]]);
|
||||
}
|
||||
});
|
||||
} else if (config === "always") {
|
||||
if (!spacedBefore) context.report({
|
||||
messageId: "needSpaceBefore",
|
||||
node: attrNode,
|
||||
loc: equalToken.loc.start,
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(equalToken, " ");
|
||||
}
|
||||
});
|
||||
if (!spacedAfter) context.report({
|
||||
node: attrNode,
|
||||
messageId: "needSpaceAfter",
|
||||
loc: equalToken.loc.start,
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(equalToken, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { jsx_equals_spacing_default as t };
|
||||
Generated
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
import { K as isSingleLine, f as createRule } from "../utils.js";
|
||||
var jsx_first_prop_new_line_default = createRule({
|
||||
name: "jsx-first-prop-new-line",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce proper position of the first property in JSX" },
|
||||
fixable: "code",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"multiline",
|
||||
"multiline-multiprop",
|
||||
"multiprop"
|
||||
]
|
||||
}],
|
||||
defaultOptions: ["multiline-multiprop"],
|
||||
messages: {
|
||||
propOnNewLine: "Property should be placed on a new line",
|
||||
propOnSameLine: "Property should be placed on the same line as the component declaration"
|
||||
}
|
||||
},
|
||||
create(context, [configuration]) {
|
||||
return { JSXOpeningElement(node) {
|
||||
if (configuration === "multiline" && !isSingleLine(node) || configuration === "multiline-multiprop" && !isSingleLine(node) && node.attributes.length > 1 || configuration === "multiprop" && node.attributes.length > 1 || configuration === "always") node.attributes.some((decl) => {
|
||||
if (decl.loc.start.line === node.loc.start.line) context.report({
|
||||
node: decl,
|
||||
messageId: "propOnNewLine",
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange([(node.typeArguments || node.name).range[1], decl.range[0]], "\n");
|
||||
}
|
||||
});
|
||||
return true;
|
||||
});
|
||||
else if (configuration === "never" && node.attributes.length > 0 || configuration === "multiprop" && !isSingleLine(node) && node.attributes.length <= 1) {
|
||||
const firstNode = node.attributes[0];
|
||||
if (node.loc.start.line < firstNode.loc.start.line) context.report({
|
||||
node: firstNode,
|
||||
messageId: "propOnSameLine",
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange([node.name.range[1], firstNode.range[0]], " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { jsx_first_prop_new_line_default as t };
|
||||
Generated
Vendored
+62
@@ -0,0 +1,62 @@
|
||||
import { K as isSingleLine, f as createRule, g as import_ast_utils, o as isJSX } from "../utils.js";
|
||||
function endWithComma(context, node) {
|
||||
const nextToken = context.sourceCode.getTokenAfter(node);
|
||||
return !!nextToken && nextToken.value === "," && nextToken.range[0] >= node.range[1];
|
||||
}
|
||||
var jsx_function_call_newline_default = createRule({
|
||||
name: "jsx-function-call-newline",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce line breaks before and after JSX elements when they are used as arguments to a function." },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["always", "multiline"]
|
||||
}],
|
||||
defaultOptions: ["multiline"],
|
||||
messages: { missingLineBreak: "Missing line break around JSX" }
|
||||
},
|
||||
create(context, [option]) {
|
||||
function needsOpeningNewLine(node) {
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(context.sourceCode.getTokenBefore(node), node)) return true;
|
||||
return false;
|
||||
}
|
||||
function needsClosingNewLine(node) {
|
||||
const nextToken = context.sourceCode.getTokenAfter(node);
|
||||
if (endWithComma(context, node)) return false;
|
||||
if (node.loc.end.line === nextToken.loc.end.line) return true;
|
||||
return false;
|
||||
}
|
||||
function check(node) {
|
||||
if (!node || !isJSX(node)) return;
|
||||
const sourceCode = context.sourceCode;
|
||||
if (option === "always" || !isSingleLine(node)) {
|
||||
const needsOpening = needsOpeningNewLine(node);
|
||||
const needsClosing = needsClosingNewLine(node);
|
||||
if (needsOpening || needsClosing) context.report({
|
||||
node,
|
||||
messageId: "missingLineBreak",
|
||||
fix: (fixer) => {
|
||||
let fixed = sourceCode.getText(node);
|
||||
if (needsOpening) fixed = `\n${fixed}`;
|
||||
if (needsClosing) fixed = `${fixed}\n`;
|
||||
return fixer.replaceText(node, fixed);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function handleCallExpression(node) {
|
||||
if (node.arguments.length === 0) return;
|
||||
node.arguments.forEach(check);
|
||||
}
|
||||
return {
|
||||
CallExpression(node) {
|
||||
handleCallExpression(node);
|
||||
},
|
||||
NewExpression(node) {
|
||||
handleCallExpression(node);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { jsx_function_call_newline_default as t };
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
import { V as isNodeFirstInLine, f as createRule } from "../utils.js";
|
||||
var jsx_indent_props_default = createRule({
|
||||
name: "jsx-indent-props",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce props indentation in JSX" },
|
||||
fixable: "code",
|
||||
schema: [{ anyOf: [
|
||||
{
|
||||
type: "string",
|
||||
enum: ["tab", "first"]
|
||||
},
|
||||
{ type: "integer" },
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
indentMode: { anyOf: [{
|
||||
type: "string",
|
||||
enum: ["tab", "first"]
|
||||
}, { type: "integer" }] },
|
||||
ignoreTernaryOperator: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
] }],
|
||||
defaultOptions: [4],
|
||||
messages: { wrongIndent: "Expected indentation of {{needed}} {{type}} {{characters}} but found {{gotten}}." }
|
||||
},
|
||||
create(context, [options]) {
|
||||
const extraColumnStart = 0;
|
||||
const line = {
|
||||
isUsingOperator: false,
|
||||
currentOperator: false
|
||||
};
|
||||
const { indentMode = 4, ignoreTernaryOperator = false } = typeof options === "object" ? options : { indentMode: options };
|
||||
const indentType = indentMode === "tab" ? "tab" : "space";
|
||||
const indentSize = indentMode === "first" ? "first" : indentMode === "tab" ? 1 : indentMode;
|
||||
function getNodeIndent(node) {
|
||||
let src = context.sourceCode.getText(node, node.loc.start.column + extraColumnStart);
|
||||
src = src.split("\n")[0];
|
||||
let regExp;
|
||||
if (indentType === "space") regExp = /^ +/;
|
||||
else regExp = /^\t+/;
|
||||
const indent = regExp.exec(src);
|
||||
const useOperator = /^[ \t]*:/.test(src) || /^[ \t]*\?/.test(src);
|
||||
const useBracket = /</.test(src);
|
||||
line.currentOperator = false;
|
||||
if (useOperator) {
|
||||
line.isUsingOperator = true;
|
||||
line.currentOperator = true;
|
||||
} else if (useBracket) line.isUsingOperator = false;
|
||||
return indent ? indent[0].length : 0;
|
||||
}
|
||||
function checkNodesIndent(nodes, indent) {
|
||||
let nestedIndent = indent;
|
||||
nodes.forEach((node) => {
|
||||
const nodeIndent = getNodeIndent(node);
|
||||
if (line.isUsingOperator && !line.currentOperator && indentSize !== "first" && !ignoreTernaryOperator) {
|
||||
nestedIndent += indentSize;
|
||||
line.isUsingOperator = false;
|
||||
}
|
||||
if (node.type !== "ArrayExpression" && node.type !== "ObjectExpression" && nodeIndent !== nestedIndent && isNodeFirstInLine(context, node)) context.report({
|
||||
node,
|
||||
messageId: "wrongIndent",
|
||||
data: {
|
||||
needed: nestedIndent,
|
||||
type: indentType,
|
||||
characters: nestedIndent === 1 ? "character" : "characters",
|
||||
gotten: nodeIndent
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange([node.range[0] - node.loc.start.column, node.range[0]], new Array(nestedIndent + 1).join(indentType === "space" ? " " : " "));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
return { JSXOpeningElement(node) {
|
||||
if (!node.attributes.length) return;
|
||||
let propIndent;
|
||||
if (indentSize === "first") propIndent = node.attributes[0].loc.start.column;
|
||||
else propIndent = getNodeIndent(node) + indentSize;
|
||||
checkNodesIndent(node.attributes, propIndent);
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { jsx_indent_props_default as t };
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
import { D as getFirstNodeInLine, V as isNodeFirstInLine, f as createRule, g as import_ast_utils, o as isJSX, s as isReturningJSX } from "../utils.js";
|
||||
var jsx_indent_default = createRule({
|
||||
name: "jsx-indent",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce JSX indentation. Deprecated, use `indent` rule instead." },
|
||||
fixable: "whitespace",
|
||||
deprecated: {
|
||||
message: "The rule was replaced with a more general rule.",
|
||||
deprecatedSince: "5.0.0",
|
||||
replacedBy: [{ rule: {
|
||||
name: "indent",
|
||||
url: "https://eslint.style/rules/indent"
|
||||
} }]
|
||||
},
|
||||
schema: [{ anyOf: [{
|
||||
type: "string",
|
||||
enum: ["tab"]
|
||||
}, { type: "integer" }] }, {
|
||||
type: "object",
|
||||
properties: {
|
||||
checkAttributes: { type: "boolean" },
|
||||
indentLogicalExpressions: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [4],
|
||||
messages: { wrongIndent: "Expected indentation of {{needed}} {{type}} {{characters}} but found {{gotten}}." }
|
||||
},
|
||||
create(context) {
|
||||
const extraColumnStart = 0;
|
||||
let indentType = "space";
|
||||
let indentSize = 4;
|
||||
if (context.options.length) {
|
||||
if (context.options[0] === "tab") {
|
||||
indentSize = 1;
|
||||
indentType = "tab";
|
||||
} else if (typeof context.options[0] === "number") {
|
||||
indentSize = context.options[0];
|
||||
indentType = "space";
|
||||
}
|
||||
}
|
||||
const indentChar = indentType === "space" ? " " : " ";
|
||||
const options = context.options[1] || {};
|
||||
const checkAttributes = options.checkAttributes || false;
|
||||
const indentLogicalExpressions = options.indentLogicalExpressions || false;
|
||||
function getFixerFunction(node, needed) {
|
||||
const indent = new Array(needed + 1).join(indentChar);
|
||||
if (node.type === "JSXText" || node.type === "Literal") return function fix(fixer) {
|
||||
const fixedText = node.raw.replace(/\n[\t ]*(\S)/g, (match, p1) => `\n${indent}${p1}`);
|
||||
return fixer.replaceText(node, fixedText);
|
||||
};
|
||||
if (node.type === "ReturnStatement") {
|
||||
const raw = context.sourceCode.getText(node);
|
||||
if (raw.split("\n").length > 1) return function fix(fixer) {
|
||||
const lastLineStart = raw.lastIndexOf("\n");
|
||||
const lastLine = raw.slice(lastLineStart).replace(/^\n[\t ]*(\S)/, (match, p1) => `\n${indent}${p1}`);
|
||||
return fixer.replaceTextRange([node.range[0] + lastLineStart, node.range[1]], lastLine);
|
||||
};
|
||||
}
|
||||
return function fix(fixer) {
|
||||
return fixer.replaceTextRange([node.range[0] - node.loc.start.column, node.range[0]], indent);
|
||||
};
|
||||
}
|
||||
function report(node, needed, gotten, loc) {
|
||||
const msgContext = {
|
||||
needed,
|
||||
type: indentType,
|
||||
characters: needed === 1 ? "character" : "characters",
|
||||
gotten
|
||||
};
|
||||
context.report({
|
||||
node,
|
||||
messageId: "wrongIndent",
|
||||
data: msgContext,
|
||||
fix: getFixerFunction(node, needed),
|
||||
...loc ? { loc } : {}
|
||||
});
|
||||
}
|
||||
function getNodeIndent(node, byLastLine = false, excludeCommas = false) {
|
||||
let src = context.sourceCode.getText(node, node.loc.start.column + extraColumnStart);
|
||||
const lines = src.split("\n");
|
||||
if (byLastLine) src = lines[lines.length - 1];
|
||||
else src = lines[0];
|
||||
const skip = excludeCommas ? "," : "";
|
||||
let regExp;
|
||||
if (indentType === "space") regExp = new RegExp(`^[ ${skip}]+`);
|
||||
else regExp = new RegExp(`^[\t${skip}]+`);
|
||||
const indent = regExp.exec(src);
|
||||
return indent ? indent[0].length : 0;
|
||||
}
|
||||
function isRightInLogicalExp(node) {
|
||||
return node.parent && node.parent.parent && node.parent.parent.type === "LogicalExpression" && node.parent.parent.right === node.parent && !indentLogicalExpressions;
|
||||
}
|
||||
function isAlternateInConditionalExp(node) {
|
||||
return node.parent && node.parent.parent && node.parent.parent.type === "ConditionalExpression" && node.parent.parent.alternate === node.parent && context.sourceCode.getTokenBefore(node).value !== "(";
|
||||
}
|
||||
function isSecondOrSubsequentExpWithinDoExp(node) {
|
||||
if (!node.parent || !node.parent.parent || node.parent.parent.type !== "ExpressionStatement") return false;
|
||||
const expStmt = node.parent.parent;
|
||||
if (!(expStmt.parent && expStmt.parent.type === "BlockStatement" && expStmt.parent.parent && expStmt.parent.parent.type === "DoExpression")) return false;
|
||||
return !(expStmt.parent.body[0] === expStmt);
|
||||
}
|
||||
function checkNodesIndent(node, indent, excludeCommas = false) {
|
||||
const nodeIndent = getNodeIndent(node, false, excludeCommas);
|
||||
const isCorrectRightInLogicalExp = isRightInLogicalExp(node) && nodeIndent - indent === indentSize;
|
||||
const isCorrectAlternateInCondExp = isAlternateInConditionalExp(node) && nodeIndent - indent === 0;
|
||||
if (nodeIndent !== indent && isNodeFirstInLine(context, node) && !isCorrectRightInLogicalExp && !isCorrectAlternateInCondExp) report(node, indent, nodeIndent);
|
||||
}
|
||||
function checkLiteralNodeIndent(node, indent) {
|
||||
const value = node.value;
|
||||
const regExp = indentType === "space" ? /\n( *)[\t ]*\S/g : /\n(\t*)[\t ]*\S/g;
|
||||
const nodeIndentsPerLine = Array.from(String(value).matchAll(regExp), (match) => match[1] ? match[1].length : 0);
|
||||
if (nodeIndentsPerLine.length > 0 && !nodeIndentsPerLine.every((actualIndent) => actualIndent === indent)) nodeIndentsPerLine.forEach((nodeIndent) => {
|
||||
report(node, indent, nodeIndent);
|
||||
});
|
||||
}
|
||||
function handleOpeningElement(node) {
|
||||
const sourceCode = context.sourceCode;
|
||||
let prevToken = sourceCode.getTokenBefore(node);
|
||||
if (!prevToken) return;
|
||||
if (prevToken.type === "JSXText" || (0, import_ast_utils.isCommaToken)(prevToken)) {
|
||||
prevToken = sourceCode.getNodeByRangeIndex(prevToken.range[0]);
|
||||
prevToken = prevToken.type === "Literal" || prevToken.type === "JSXText" ? prevToken.parent : prevToken;
|
||||
} else if ((0, import_ast_utils.isColonToken)(prevToken)) {
|
||||
do
|
||||
prevToken = sourceCode.getTokenBefore(prevToken);
|
||||
while (prevToken.type === "Punctuator" && prevToken.value !== "/");
|
||||
prevToken = sourceCode.getNodeByRangeIndex(prevToken.range[0]);
|
||||
while (prevToken.parent && prevToken.parent.type !== "ConditionalExpression") prevToken = prevToken.parent;
|
||||
}
|
||||
prevToken = prevToken.type === "JSXExpressionContainer" ? prevToken.expression : prevToken;
|
||||
checkNodesIndent(node, getNodeIndent(prevToken) + (prevToken.loc.start.line === node.loc.start.line || isRightInLogicalExp(node) || isAlternateInConditionalExp(node) || isSecondOrSubsequentExpWithinDoExp(node) ? 0 : indentSize));
|
||||
}
|
||||
function handleClosingElement(node) {
|
||||
if (!node.parent) return;
|
||||
checkNodesIndent(node, getNodeIndent(node.parent.openingElement || node.parent.openingFragment));
|
||||
}
|
||||
function handleAttribute(node) {
|
||||
if (!checkAttributes || !node.value || node.value.type !== "JSXExpressionContainer") return;
|
||||
const nameIndent = getNodeIndent(node.name);
|
||||
const lastToken = context.sourceCode.getLastToken(node.value);
|
||||
const firstInLine = getFirstNodeInLine(context, lastToken);
|
||||
if (firstInLine.loc.start.line !== lastToken.loc.start.line) return;
|
||||
checkNodesIndent(firstInLine, node.name.loc.start.line === firstInLine.loc.start.line ? 0 : nameIndent);
|
||||
}
|
||||
function handleLiteral(node) {
|
||||
if (!node.parent) return;
|
||||
if (node.parent.type !== "JSXElement" && node.parent.type !== "JSXFragment") return;
|
||||
checkLiteralNodeIndent(node, getNodeIndent(node.parent) + indentSize);
|
||||
}
|
||||
return {
|
||||
JSXOpeningElement: handleOpeningElement,
|
||||
JSXOpeningFragment: handleOpeningElement,
|
||||
JSXClosingElement: handleClosingElement,
|
||||
JSXClosingFragment: handleClosingElement,
|
||||
JSXAttribute: handleAttribute,
|
||||
JSXExpressionContainer(node) {
|
||||
if (!node.parent) return;
|
||||
checkNodesIndent(node, getNodeIndent(node.parent) + indentSize);
|
||||
},
|
||||
Literal: handleLiteral,
|
||||
JSXText: handleLiteral,
|
||||
ReturnStatement(node) {
|
||||
if (!node.parent || !node.argument || !isJSX(node.argument)) return;
|
||||
let fn = node.parent;
|
||||
while (fn && fn.type !== "FunctionDeclaration" && fn.type !== "FunctionExpression") fn = fn.parent;
|
||||
if (!fn || !isReturningJSX(node, context, true)) return;
|
||||
const openingIndent = getNodeIndent(node);
|
||||
const closingIndent = getNodeIndent(node, true);
|
||||
if (openingIndent !== closingIndent) report(node, openingIndent, closingIndent);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { jsx_indent_default as t };
|
||||
Generated
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
import { K as isSingleLine, f as createRule, g as import_ast_utils, i as getPropName } from "../utils.js";
|
||||
var jsx_max_props_per_line_default = createRule({
|
||||
name: "jsx-max-props-per-line",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce maximum of props on a single line in JSX" },
|
||||
fixable: "code",
|
||||
schema: [{ anyOf: [{
|
||||
type: "object",
|
||||
properties: { maximum: {
|
||||
type: "object",
|
||||
properties: {
|
||||
single: {
|
||||
type: "integer",
|
||||
minimum: 1
|
||||
},
|
||||
multi: {
|
||||
type: "integer",
|
||||
minimum: 1
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
} },
|
||||
additionalProperties: false
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
maximum: {
|
||||
type: "number",
|
||||
minimum: 1
|
||||
},
|
||||
when: {
|
||||
type: "string",
|
||||
enum: ["always", "multiline"]
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}] }],
|
||||
defaultOptions: [{ maximum: 1 }],
|
||||
messages: { newLine: "Prop `{{prop}}` must be placed on a new line" }
|
||||
},
|
||||
create(context, [configuration]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const { maximum } = configuration;
|
||||
const { single = Infinity, multi = Infinity } = typeof maximum === "number" ? {
|
||||
single: configuration.when === "multiline" ? Infinity : maximum,
|
||||
multi: maximum
|
||||
} : maximum;
|
||||
function generateFixFunction(line, max) {
|
||||
const output = [];
|
||||
const front = line[0].range[0];
|
||||
const back = line[line.length - 1].range[1];
|
||||
for (let i = 0; i < line.length; i += max) {
|
||||
const nodes = line.slice(i, i + max);
|
||||
output.push(nodes.reduce((prev, curr) => {
|
||||
if (prev === "") return sourceCode.getText(curr);
|
||||
return `${prev} ${sourceCode.getText(curr)}`;
|
||||
}, ""));
|
||||
}
|
||||
const code = output.join("\n");
|
||||
return function fix(fixer) {
|
||||
return fixer.replaceTextRange([front, back], code);
|
||||
};
|
||||
}
|
||||
return { JSXOpeningElement(node) {
|
||||
if (!node.attributes.length) return;
|
||||
const isSingleLineTag = isSingleLine(node);
|
||||
if ((isSingleLineTag ? single : multi) === Infinity) return;
|
||||
const linePartitionedProps = [[node.attributes[0]]];
|
||||
node.attributes.reduce((last, decl) => {
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(last, decl)) linePartitionedProps[linePartitionedProps.length - 1].push(decl);
|
||||
else linePartitionedProps.push([decl]);
|
||||
return decl;
|
||||
});
|
||||
linePartitionedProps.forEach((propsInLine) => {
|
||||
const maxPropsCountPerLine = isSingleLineTag && propsInLine[0].loc.start.line === node.loc.start.line ? single : multi;
|
||||
if (propsInLine.length > maxPropsCountPerLine) {
|
||||
const name = getPropName(sourceCode, propsInLine[maxPropsCountPerLine]);
|
||||
context.report({
|
||||
messageId: "newLine",
|
||||
node: propsInLine[maxPropsCountPerLine],
|
||||
data: { prop: name },
|
||||
fix: generateFixFunction(propsInLine, maxPropsCountPerLine)
|
||||
});
|
||||
}
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { jsx_max_props_per_line_default as t };
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
import { K as isSingleLine, f as createRule } from "../utils.js";
|
||||
var jsx_newline_default = createRule({
|
||||
name: "jsx-newline",
|
||||
package: "jsx",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require or prevent a new line after jsx elements and expressions." },
|
||||
fixable: "code",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
prevent: { type: "boolean" },
|
||||
allowMultilines: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false,
|
||||
if: { properties: { allowMultilines: { const: true } } },
|
||||
then: {
|
||||
properties: { prevent: { const: true } },
|
||||
required: ["prevent"]
|
||||
}
|
||||
}],
|
||||
defaultOptions: [{
|
||||
prevent: false,
|
||||
allowMultilines: false
|
||||
}],
|
||||
messages: {
|
||||
require: "JSX element should start in a new line",
|
||||
prevent: "JSX element should not start in a new line",
|
||||
allowMultilines: "Multiline JSX elements should start in a new line"
|
||||
}
|
||||
},
|
||||
create(context, [configuration]) {
|
||||
const { prevent, allowMultilines } = configuration;
|
||||
const jsxElementParents = /* @__PURE__ */ new Set();
|
||||
const sourceCode = context.sourceCode;
|
||||
function isBlockCommentInCurlyBraces(element) {
|
||||
const elementRawValue = sourceCode.getText(element);
|
||||
return /^\s*\{\/\*/.test(elementRawValue);
|
||||
}
|
||||
function isNonBlockComment(element) {
|
||||
return !isBlockCommentInCurlyBraces(element) && (element.type === "JSXElement" || element.type === "JSXExpressionContainer");
|
||||
}
|
||||
return {
|
||||
"Program:exit": function() {
|
||||
jsxElementParents.forEach((parent) => {
|
||||
parent.children.forEach((element, index, elements) => {
|
||||
if (element.type === "JSXElement" || element.type === "JSXExpressionContainer") {
|
||||
const firstAdjacentSibling = elements[index + 1];
|
||||
const secondAdjacentSibling = elements[index + 2];
|
||||
if (!(firstAdjacentSibling && secondAdjacentSibling && (firstAdjacentSibling.type === "Literal" || firstAdjacentSibling.type === "JSXText"))) return;
|
||||
const isWithoutNewLine = !/\n\s*\n/.test(firstAdjacentSibling.value);
|
||||
if (isBlockCommentInCurlyBraces(element)) return;
|
||||
const nextNonBlockComment = elements.slice(index + 2).find(isNonBlockComment);
|
||||
if (allowMultilines && (!isSingleLine(element) || nextNonBlockComment && !isSingleLine(nextNonBlockComment))) {
|
||||
if (!isWithoutNewLine) return;
|
||||
const regex = /(\n)(?!.*\1)/g;
|
||||
const replacement = "\n\n";
|
||||
context.report({
|
||||
messageId: "allowMultilines",
|
||||
node: secondAdjacentSibling,
|
||||
fix(fixer) {
|
||||
return fixer.replaceText(firstAdjacentSibling, sourceCode.getText(firstAdjacentSibling).replace(regex, replacement));
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (isWithoutNewLine === prevent) return;
|
||||
const messageId = prevent ? "prevent" : "require";
|
||||
const regex = prevent ? /(\n\n)(?!.*\1)/g : /(\n)(?!.*\1)/g;
|
||||
const replacement = prevent ? "\n" : "\n\n";
|
||||
context.report({
|
||||
messageId,
|
||||
node: secondAdjacentSibling,
|
||||
fix(fixer) {
|
||||
return fixer.replaceText(firstAdjacentSibling, sourceCode.getText(firstAdjacentSibling).replace(regex, replacement));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
":matches(JSXElement, JSXFragment) > :matches(JSXElement, JSXExpressionContainer)": (node) => {
|
||||
jsxElementParents.add(node.parent);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { jsx_newline_default as t };
|
||||
Generated
Vendored
+157
@@ -0,0 +1,157 @@
|
||||
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 };
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
import { a as isDOMComponent, f as createRule, r as getElementType } from "../utils.js";
|
||||
import picomatch from "picomatch";
|
||||
function testDigit(char) {
|
||||
const charCode = char.charCodeAt(0);
|
||||
return charCode >= 48 && charCode <= 57;
|
||||
}
|
||||
function testUpperCase(char) {
|
||||
const upperCase = char.toUpperCase();
|
||||
return char === upperCase && upperCase !== char.toLowerCase();
|
||||
}
|
||||
function testLowerCase(char) {
|
||||
const lowerCase = char.toLowerCase();
|
||||
return char === lowerCase && lowerCase !== char.toUpperCase();
|
||||
}
|
||||
function testPascalCase(name) {
|
||||
if (!testUpperCase(name.charAt(0))) return false;
|
||||
if (Array.prototype.some.call(name.slice(1), (char) => char.toLowerCase() === char.toUpperCase() && !testDigit(char))) return false;
|
||||
return Array.prototype.some.call(name.slice(1), (char) => testLowerCase(char) || testDigit(char));
|
||||
}
|
||||
function testAllCaps(name) {
|
||||
const firstChar = name.charAt(0);
|
||||
if (!(testUpperCase(firstChar) || testDigit(firstChar))) return false;
|
||||
for (let i = 1; i < name.length - 1; i += 1) {
|
||||
const char = name.charAt(i);
|
||||
if (!(testUpperCase(char) || testDigit(char) || char === "_")) return false;
|
||||
}
|
||||
const lastChar = name.charAt(name.length - 1);
|
||||
if (!(testUpperCase(lastChar) || testDigit(lastChar))) return false;
|
||||
return true;
|
||||
}
|
||||
var jsx_pascal_case_default = createRule({
|
||||
name: "jsx-pascal-case",
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
docs: { description: "Enforce PascalCase for user-defined JSX components" },
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
allowAllCaps: { type: "boolean" },
|
||||
allowLeadingUnderscore: { type: "boolean" },
|
||||
allowNamespace: { type: "boolean" },
|
||||
ignore: {
|
||||
items: { type: "string" },
|
||||
type: "array",
|
||||
uniqueItems: true
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
allowAllCaps: false,
|
||||
allowLeadingUnderscore: false,
|
||||
allowNamespace: false
|
||||
}],
|
||||
messages: {
|
||||
usePascalCase: "Imported JSX component {{name}} must be in PascalCase",
|
||||
usePascalOrSnakeCase: "Imported JSX component {{name}} must be in PascalCase or SCREAMING_SNAKE_CASE"
|
||||
}
|
||||
},
|
||||
create(context, [configuration]) {
|
||||
const { allowAllCaps, allowLeadingUnderscore, allowNamespace, ignore = [] } = configuration;
|
||||
const isMatchIgnore = picomatch(ignore, { noglobstar: true });
|
||||
function ignoreCheck(name) {
|
||||
return isMatchIgnore(name) || ignore.includes(name);
|
||||
}
|
||||
return { JSXOpeningElement(node) {
|
||||
if (isDOMComponent(node)) return;
|
||||
const name = getElementType(node);
|
||||
let checkNames = [name];
|
||||
let index = 0;
|
||||
if (name.includes(":")) checkNames = name.split(":");
|
||||
else if (name.includes(".")) checkNames = name.split(".");
|
||||
do {
|
||||
const splitName = checkNames[index];
|
||||
if (splitName.length === 1) return;
|
||||
const isIgnored = ignoreCheck(splitName);
|
||||
const checkName = allowLeadingUnderscore && splitName.startsWith("_") ? splitName.slice(1) : splitName;
|
||||
const isPascalCase = testPascalCase(checkName);
|
||||
const isAllowedAllCaps = allowAllCaps && testAllCaps(checkName);
|
||||
if (!isPascalCase && !isAllowedAllCaps && !isIgnored) {
|
||||
const messageId = allowAllCaps ? "usePascalOrSnakeCase" : "usePascalCase";
|
||||
context.report({
|
||||
messageId,
|
||||
node,
|
||||
data: { name: splitName }
|
||||
});
|
||||
break;
|
||||
}
|
||||
index += 1;
|
||||
} while (index < checkNames.length && !allowNamespace);
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { jsx_pascal_case_default as t };
|
||||
Generated
Vendored
+82
@@ -0,0 +1,82 @@
|
||||
import { f as createRule } from "../utils.js";
|
||||
var jsx_props_no_multi_spaces_default = createRule({
|
||||
name: "jsx-props-no-multi-spaces",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow multiple spaces between inline JSX props. Deprecated, use `no-multi-spaces` rule instead." },
|
||||
fixable: "code",
|
||||
deprecated: {
|
||||
message: "The rule was replaced with a more general rule.",
|
||||
deprecatedSince: "5.0.0",
|
||||
replacedBy: [{ rule: {
|
||||
name: "no-multi-spaces",
|
||||
url: "https://eslint.style/rules/no-multi-spaces"
|
||||
} }]
|
||||
},
|
||||
schema: [],
|
||||
messages: {
|
||||
noLineGap: "Expected no line gap between “{{prop1}}” and “{{prop2}}”",
|
||||
onlyOneSpace: "Expected only one space between “{{prop1}}” and “{{prop2}}”"
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
function getPropName(propNode) {
|
||||
switch (propNode.type) {
|
||||
case "JSXSpreadAttribute": return sourceCode.getText(propNode.argument);
|
||||
case "JSXIdentifier": return propNode.name;
|
||||
case "JSXMemberExpression": return `${getPropName(propNode.object)}.${propNode.property.name}`;
|
||||
default: return propNode.name ? propNode.name.name : `${sourceCode.getText(propNode.object)}.${propNode.property.name}`;
|
||||
}
|
||||
}
|
||||
function hasEmptyLines(first, second) {
|
||||
const comments = sourceCode.getCommentsBefore ? sourceCode.getCommentsBefore(second) : [];
|
||||
const nodes = [].concat(first, comments, second);
|
||||
for (let i = 1; i < nodes.length; i += 1) {
|
||||
const prev = nodes[i - 1];
|
||||
if (nodes[i].loc.start.line - prev.loc.end.line >= 2) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function checkSpacing(prev, node) {
|
||||
if (hasEmptyLines(prev, node)) context.report({
|
||||
messageId: "noLineGap",
|
||||
node,
|
||||
data: {
|
||||
prop1: getPropName(prev),
|
||||
prop2: getPropName(node)
|
||||
}
|
||||
});
|
||||
if (prev.loc.end.line !== node.loc.end.line) return;
|
||||
if (sourceCode.text.slice(prev.range[1], node.range[0]) !== " ") context.report({
|
||||
node,
|
||||
messageId: "onlyOneSpace",
|
||||
data: {
|
||||
prop1: getPropName(prev),
|
||||
prop2: getPropName(node)
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange([prev.range[1], node.range[0]], " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
function containsGenericType(node) {
|
||||
return typeof node.typeArguments !== "undefined" && node.typeArguments?.type === "TSTypeParameterInstantiation";
|
||||
}
|
||||
function getGenericNode(node) {
|
||||
const name = node.name;
|
||||
if (containsGenericType(node)) {
|
||||
const type = node.typeArguments;
|
||||
return Object.assign({}, node, { range: [name.range[0], type?.range[1]] });
|
||||
}
|
||||
return name;
|
||||
}
|
||||
return { JSXOpeningElement(node) {
|
||||
node.attributes.reduce((prev, prop) => {
|
||||
checkSpacing(prev, prop);
|
||||
return prop;
|
||||
}, getGenericNode(node));
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { jsx_props_no_multi_spaces_default as t };
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
import { K as isSingleLine, d as safeReplaceTextBetween, f as createRule, g as import_ast_utils, i as getPropName } from "../utils.js";
|
||||
var jsx_props_style_default = createRule({
|
||||
name: "jsx-props-style",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: {
|
||||
experimental: true,
|
||||
description: "Enforce consistent line break styles for JSX props"
|
||||
},
|
||||
fixable: "code",
|
||||
schema: [{
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
singleLine: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: { maxItems: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
} }
|
||||
},
|
||||
multiLine: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
minItems: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
},
|
||||
maxItemsPerLine: {
|
||||
type: "integer",
|
||||
minimum: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}],
|
||||
defaultOptions: [{
|
||||
singleLine: { maxItems: Number.POSITIVE_INFINITY },
|
||||
multiLine: {
|
||||
minItems: 0,
|
||||
maxItemsPerLine: 1
|
||||
}
|
||||
}],
|
||||
messages: {
|
||||
shouldWrap: "Prop `{{prop}}` must be placed on a new line",
|
||||
shouldNotWrap: "Prop `{{prop}}` should not be placed on a new line"
|
||||
}
|
||||
},
|
||||
create(context, [option]) {
|
||||
const { sourceCode } = context;
|
||||
const { singleLine, multiLine } = option;
|
||||
function getPrevToken(node, prev, i) {
|
||||
return i === 0 ? sourceCode.getTokenBefore(node) : sourceCode.getLastToken(prev);
|
||||
}
|
||||
function reportShouldWrap(node, prev, i) {
|
||||
const prevToken = getPrevToken(node, prev, i);
|
||||
context.report({
|
||||
node,
|
||||
messageId: "shouldWrap",
|
||||
data: { prop: getPropName(sourceCode, node) },
|
||||
fix: safeReplaceTextBetween(sourceCode, prevToken, node, "\n")
|
||||
});
|
||||
}
|
||||
function reportShouldNotWrap(node, prev, i) {
|
||||
const prevToken = getPrevToken(node, prev, i);
|
||||
context.report({
|
||||
node,
|
||||
messageId: "shouldNotWrap",
|
||||
data: { prop: getPropName(sourceCode, node) },
|
||||
fix: safeReplaceTextBetween(sourceCode, prevToken, node, " ")
|
||||
});
|
||||
}
|
||||
return { JSXOpeningElement(node) {
|
||||
const attrs = node.attributes;
|
||||
if (!attrs.length) return;
|
||||
const needWrap = isSingleLine(node) ? attrs.length > singleLine.maxItems : attrs.length >= multiLine.minItems && node.loc.start.line !== attrs[0].loc.start.line;
|
||||
const maxPerLine = needWrap ? multiLine.maxItemsPerLine : Number.POSITIVE_INFINITY;
|
||||
let itemsOnCurrentLine = 0;
|
||||
for (let i = 0; i < attrs.length; i++) {
|
||||
const current = attrs[i];
|
||||
const prev = i === 0 ? node.typeArguments ?? node.name : attrs[i - 1];
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(prev, current)) {
|
||||
itemsOnCurrentLine++;
|
||||
if (!needWrap) continue;
|
||||
if (i === 0) reportShouldWrap(current, prev, i);
|
||||
else if (itemsOnCurrentLine > maxPerLine) {
|
||||
reportShouldWrap(current, prev, i);
|
||||
itemsOnCurrentLine = 1;
|
||||
}
|
||||
} else {
|
||||
itemsOnCurrentLine = 1;
|
||||
if (needWrap) continue;
|
||||
reportShouldNotWrap(current, prev, i);
|
||||
}
|
||||
}
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { jsx_props_style_default as t };
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
import { J as isSurroundedBy, f as createRule, q as isStringLiteral } from "../utils.js";
|
||||
const QUOTE_SETTINGS = {
|
||||
"prefer-double": {
|
||||
quote: "\"",
|
||||
description: "singlequote",
|
||||
convert(str) {
|
||||
return str.replace(/'/gu, "\"");
|
||||
}
|
||||
},
|
||||
"prefer-single": {
|
||||
quote: "'",
|
||||
description: "doublequote",
|
||||
convert(str) {
|
||||
return str.replace(/"/gu, "'");
|
||||
}
|
||||
}
|
||||
};
|
||||
var jsx_quotes_default = createRule({
|
||||
name: "jsx-quotes",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce the consistent use of either double or single quotes in JSX attributes" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["prefer-single", "prefer-double"]
|
||||
}],
|
||||
defaultOptions: ["prefer-double"],
|
||||
messages: { unexpected: "Unexpected usage of {{description}}." }
|
||||
},
|
||||
create(context, [quoteOption]) {
|
||||
const setting = QUOTE_SETTINGS[quoteOption];
|
||||
function usesExpectedQuotes(node) {
|
||||
return node.value.includes(setting.quote) || isSurroundedBy(node.raw, setting.quote);
|
||||
}
|
||||
return { JSXAttribute(node) {
|
||||
const attributeValue = node.value;
|
||||
if (attributeValue && isStringLiteral(attributeValue) && !usesExpectedQuotes(attributeValue)) context.report({
|
||||
node: attributeValue,
|
||||
messageId: "unexpected",
|
||||
data: { description: setting.description },
|
||||
fix(fixer) {
|
||||
return fixer.replaceText(attributeValue, setting.convert(attributeValue.raw));
|
||||
}
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { jsx_quotes_default as t };
|
||||
Generated
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
import { a as isDOMComponent, f as createRule } from "../utils.js";
|
||||
var jsx_self_closing_comp_default = createRule({
|
||||
name: "jsx-self-closing-comp",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow extra closing tags for components without children" },
|
||||
fixable: "code",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
component: { type: "boolean" },
|
||||
html: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
component: true,
|
||||
html: true
|
||||
}],
|
||||
messages: { notSelfClosing: "Empty components are self-closing" }
|
||||
},
|
||||
create(context, [configuration]) {
|
||||
const { component, html } = configuration;
|
||||
function isComponent(node) {
|
||||
return node.name && (node.name.type === "JSXIdentifier" || node.name.type === "JSXMemberExpression") && !isDOMComponent(node);
|
||||
}
|
||||
function childrenIsEmpty(node) {
|
||||
return node.parent.children.length === 0;
|
||||
}
|
||||
function childrenIsMultilineSpaces(node) {
|
||||
const childrens = node.parent.children;
|
||||
return childrens.length === 1 && childrens[0].type === "JSXText" && childrens[0].value.includes("\n") && childrens[0].value.replace(/(?!\xA0)\s/g, "") === "";
|
||||
}
|
||||
function isShouldBeSelfClosed(node) {
|
||||
return (!!component && isComponent(node) || !!html && isDOMComponent(node)) && !node.selfClosing && (childrenIsEmpty(node) || childrenIsMultilineSpaces(node));
|
||||
}
|
||||
return { JSXOpeningElement(node) {
|
||||
if (!isShouldBeSelfClosed(node)) return;
|
||||
context.report({
|
||||
messageId: "notSelfClosing",
|
||||
node,
|
||||
fix(fixer) {
|
||||
const range = [node.range[1] - 1, node.parent.closingElement?.range[1] ?? NaN];
|
||||
return fixer.replaceTextRange(range, " />");
|
||||
}
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { jsx_self_closing_comp_default as t };
|
||||
+375
@@ -0,0 +1,375 @@
|
||||
import { K as isSingleLine, a as isDOMComponent, f as createRule, i as getPropName } from "../utils.js";
|
||||
function isCallbackPropName(name) {
|
||||
return /^on[A-Z]/.test(name);
|
||||
}
|
||||
const RESERVED_PROPS_LIST = [
|
||||
"children",
|
||||
"dangerouslySetInnerHTML",
|
||||
"key",
|
||||
"ref"
|
||||
];
|
||||
function getReservedPropIndex(name, list) {
|
||||
return list.indexOf(name.split(":")[0]);
|
||||
}
|
||||
let attributeMap;
|
||||
function shouldSortToEnd(node) {
|
||||
const attr = attributeMap.get(node);
|
||||
return !!attr && !!attr.hasComment;
|
||||
}
|
||||
function contextCompare(sourceCode, a, b, options) {
|
||||
let aProp = getPropName(sourceCode, a);
|
||||
let bProp = getPropName(sourceCode, b);
|
||||
const aPropNamespace = aProp.split(":")[0];
|
||||
const bPropNamespace = bProp.split(":")[0];
|
||||
const aSortToEnd = shouldSortToEnd(a);
|
||||
const bSortToEnd = shouldSortToEnd(b);
|
||||
if (aSortToEnd && !bSortToEnd) return 1;
|
||||
if (!aSortToEnd && bSortToEnd) return -1;
|
||||
if (options.reservedFirst) {
|
||||
const aIndex = getReservedPropIndex(aProp, options.reservedList);
|
||||
const bIndex = getReservedPropIndex(bProp, options.reservedList);
|
||||
if (aIndex > -1 && bIndex === -1) return -1;
|
||||
if (aIndex === -1 && bIndex > -1) return 1;
|
||||
if (aIndex > -1 && bIndex > -1 && aPropNamespace !== bPropNamespace) return aIndex > bIndex ? 1 : -1;
|
||||
}
|
||||
if (options.reservedLast.length > 0) {
|
||||
const aLastIndex = getReservedPropIndex(aProp, options.reservedLast);
|
||||
const bLastIndex = getReservedPropIndex(bProp, options.reservedLast);
|
||||
if (aLastIndex > -1 && bLastIndex === -1) return 1;
|
||||
if (aLastIndex === -1 && bLastIndex > -1) return -1;
|
||||
if (aLastIndex > -1 && bLastIndex > -1 && aPropNamespace !== bPropNamespace) return aLastIndex > bLastIndex ? -1 : 1;
|
||||
}
|
||||
if (options.callbacksLast) {
|
||||
const aIsCallback = isCallbackPropName(aProp);
|
||||
const bIsCallback = isCallbackPropName(bProp);
|
||||
if (aIsCallback && !bIsCallback) return 1;
|
||||
if (!aIsCallback && bIsCallback) return -1;
|
||||
}
|
||||
if (options.shorthandFirst || options.shorthandLast) {
|
||||
const shorthandSign = options.shorthandFirst ? -1 : 1;
|
||||
if (!a.value && b.value) return shorthandSign;
|
||||
if (a.value && !b.value) return -shorthandSign;
|
||||
}
|
||||
if (options.multiline !== "ignore") {
|
||||
const multilineSign = options.multiline === "first" ? -1 : 1;
|
||||
const aIsMultiline = !isSingleLine(a);
|
||||
const bIsMultiline = !isSingleLine(b);
|
||||
if (aIsMultiline && !bIsMultiline) return multilineSign;
|
||||
if (!aIsMultiline && bIsMultiline) return -multilineSign;
|
||||
}
|
||||
if (options.noSortAlphabetically) return 0;
|
||||
const actualLocale = options.locale === "auto" ? void 0 : options.locale;
|
||||
if (options.ignoreCase) {
|
||||
aProp = aProp.toLowerCase();
|
||||
bProp = bProp.toLowerCase();
|
||||
return aProp.localeCompare(bProp, actualLocale);
|
||||
}
|
||||
if (aProp === bProp) return 0;
|
||||
if (options.locale === "auto") return aProp < bProp ? -1 : 1;
|
||||
return aProp.localeCompare(bProp, actualLocale);
|
||||
}
|
||||
function getGroupsOfSortableAttributes(attributes, context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const sortableAttributeGroups = [];
|
||||
let groupCount = 0;
|
||||
function addtoSortableAttributeGroups(attribute) {
|
||||
sortableAttributeGroups[groupCount - 1].push(attribute);
|
||||
}
|
||||
for (let i = 0; i < attributes.length; i++) {
|
||||
const attribute = attributes[i];
|
||||
const nextAttribute = attributes[i + 1];
|
||||
const attributeline = attribute.loc.start.line;
|
||||
let comment = [];
|
||||
try {
|
||||
comment = sourceCode.getCommentsAfter(attribute);
|
||||
} catch {}
|
||||
const lastAttr = attributes[i - 1];
|
||||
const attrIsSpread = attribute.type === "JSXSpreadAttribute";
|
||||
if (!lastAttr || lastAttr.type === "JSXSpreadAttribute" && !attrIsSpread) {
|
||||
groupCount += 1;
|
||||
sortableAttributeGroups[groupCount - 1] = [];
|
||||
}
|
||||
if (!attrIsSpread) if (comment.length === 0) {
|
||||
attributeMap.set(attribute, {
|
||||
end: attribute.range[1],
|
||||
hasComment: false
|
||||
});
|
||||
addtoSortableAttributeGroups(attribute);
|
||||
} else {
|
||||
const firstComment = comment[0];
|
||||
const commentline = firstComment.loc.start.line;
|
||||
if (comment.length === 1) {
|
||||
if (attributeline + 1 === commentline && nextAttribute) {
|
||||
attributeMap.set(attribute, {
|
||||
end: nextAttribute.range[1],
|
||||
hasComment: true
|
||||
});
|
||||
addtoSortableAttributeGroups(attribute);
|
||||
i += 1;
|
||||
} else if (attributeline === commentline) {
|
||||
if (firstComment.type === "Block" && nextAttribute) {
|
||||
attributeMap.set(attribute, {
|
||||
end: nextAttribute.range[1],
|
||||
hasComment: true
|
||||
});
|
||||
i += 1;
|
||||
} else if (firstComment.type === "Block") attributeMap.set(attribute, {
|
||||
end: firstComment.range[1],
|
||||
hasComment: true
|
||||
});
|
||||
else attributeMap.set(attribute, {
|
||||
end: firstComment.range[1],
|
||||
hasComment: false
|
||||
});
|
||||
addtoSortableAttributeGroups(attribute);
|
||||
}
|
||||
} else if (comment.length > 1 && attributeline + 1 === comment[1].loc.start.line && nextAttribute) {
|
||||
const commentNextAttribute = sourceCode.getCommentsAfter(nextAttribute);
|
||||
attributeMap.set(attribute, {
|
||||
end: nextAttribute.range[1],
|
||||
hasComment: true
|
||||
});
|
||||
if (commentNextAttribute.length === 1 && nextAttribute.loc.start.line === commentNextAttribute[0].loc.start.line) attributeMap.set(attribute, {
|
||||
end: commentNextAttribute[0].range[1],
|
||||
hasComment: true
|
||||
});
|
||||
addtoSortableAttributeGroups(attribute);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sortableAttributeGroups;
|
||||
}
|
||||
function generateFixerFunction(node, context, reservedList) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const attributes = node.attributes.slice(0);
|
||||
const configuration = context.options[0] || {};
|
||||
const options = {
|
||||
ignoreCase: configuration.ignoreCase || false,
|
||||
callbacksLast: configuration.callbacksLast || false,
|
||||
shorthandFirst: configuration.shorthandFirst || false,
|
||||
shorthandLast: configuration.shorthandLast || false,
|
||||
multiline: configuration.multiline || "ignore",
|
||||
noSortAlphabetically: configuration.noSortAlphabetically || false,
|
||||
reservedFirst: configuration.reservedFirst || false,
|
||||
reservedList,
|
||||
reservedLast: configuration.reservedLast || [],
|
||||
locale: configuration.locale || "auto"
|
||||
};
|
||||
const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes, context);
|
||||
const sortedAttributeGroups = sortableAttributeGroups.slice(0).map((group) => [...group].sort((a, b) => contextCompare(context.sourceCode, a, b, options)));
|
||||
return function fixFunction(fixer) {
|
||||
const fixers = [];
|
||||
let source = sourceCode.getText();
|
||||
sortableAttributeGroups.forEach((sortableGroup, ii) => {
|
||||
sortableGroup.forEach((attr, jj) => {
|
||||
const sortedAttr = sortedAttributeGroups[ii][jj];
|
||||
const sortedAttrText = source.slice(sortedAttr.range[0], attributeMap.get(sortedAttr).end);
|
||||
fixers.push({
|
||||
range: [attr.range[0], attributeMap.get(attr).end],
|
||||
text: sortedAttrText
|
||||
});
|
||||
});
|
||||
});
|
||||
fixers.sort((a, b) => b.range[0] - a.range[0]);
|
||||
const firstFixer = fixers[0];
|
||||
const lastFixer = fixers[fixers.length - 1];
|
||||
const rangeStart = lastFixer ? lastFixer.range[0] : 0;
|
||||
const rangeEnd = firstFixer ? firstFixer.range[1] : -0;
|
||||
fixers.forEach((fix) => {
|
||||
source = `${source.slice(0, fix.range[0])}${fix.text}${source.slice(fix.range[1])}`;
|
||||
});
|
||||
return fixer.replaceTextRange([rangeStart, rangeEnd], source.slice(rangeStart, rangeEnd));
|
||||
};
|
||||
}
|
||||
function validateReservedFirstConfig(context, reservedFirst) {
|
||||
if (reservedFirst) {
|
||||
if (Array.isArray(reservedFirst)) {
|
||||
if (reservedFirst.length === 0) return function Report(decl) {
|
||||
context.report({
|
||||
node: decl,
|
||||
messageId: "listIsEmpty"
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
const reportedNodeAttributes = /* @__PURE__ */ new WeakMap();
|
||||
function reportNodeAttribute(nodeAttribute, errorType, node, context, reservedList) {
|
||||
const errors = reportedNodeAttributes.get(nodeAttribute) || [];
|
||||
if (errors.includes(errorType)) return;
|
||||
errors.push(errorType);
|
||||
reportedNodeAttributes.set(nodeAttribute, errors);
|
||||
context.report({
|
||||
node: nodeAttribute.name ?? "",
|
||||
messageId: errorType,
|
||||
fix: generateFixerFunction(node, context, reservedList)
|
||||
});
|
||||
}
|
||||
var jsx_sort_props_default = createRule({
|
||||
name: "jsx-sort-props",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce props alphabetical sorting" },
|
||||
fixable: "code",
|
||||
deprecated: {
|
||||
message: "We recommend using the `eslint-plugin-perfectionist` plugin instead.",
|
||||
deprecatedSince: "5.7.0",
|
||||
replacedBy: [{
|
||||
plugin: {
|
||||
name: "eslint-plugin-perfectionist",
|
||||
url: "https://perfectionist.dev"
|
||||
},
|
||||
rule: {
|
||||
name: "sort-jsx-props",
|
||||
url: "https://perfectionist.dev/rules/sort-jsx-props"
|
||||
}
|
||||
}]
|
||||
},
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
callbacksLast: { type: "boolean" },
|
||||
shorthandFirst: { type: "boolean" },
|
||||
shorthandLast: { type: "boolean" },
|
||||
multiline: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"ignore",
|
||||
"first",
|
||||
"last"
|
||||
]
|
||||
},
|
||||
ignoreCase: { type: "boolean" },
|
||||
noSortAlphabetically: { type: "boolean" },
|
||||
reservedFirst: { oneOf: [{
|
||||
type: "array",
|
||||
items: { type: "string" }
|
||||
}, { type: "boolean" }] },
|
||||
reservedLast: {
|
||||
type: "array",
|
||||
items: { type: "string" }
|
||||
},
|
||||
locale: { type: "string" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
ignoreCase: false,
|
||||
callbacksLast: false,
|
||||
shorthandFirst: false,
|
||||
shorthandLast: false,
|
||||
multiline: "ignore",
|
||||
noSortAlphabetically: false,
|
||||
reservedFirst: false,
|
||||
reservedLast: [],
|
||||
locale: "auto"
|
||||
}],
|
||||
messages: {
|
||||
listIsEmpty: "A customized reserved first list must not be empty",
|
||||
listReservedPropsFirst: "Reserved props must be listed before all other props",
|
||||
listReservedPropsLast: "Reserved props must be listed after all other props",
|
||||
listCallbacksLast: "Callbacks must be listed after all other props",
|
||||
listShorthandFirst: "Shorthand props must be listed before all other props",
|
||||
listShorthandLast: "Shorthand props must be listed after all other props",
|
||||
listMultilineFirst: "Multiline props must be listed before all other props",
|
||||
listMultilineLast: "Multiline props must be listed after all other props",
|
||||
sortPropsByAlpha: "Props should be sorted alphabetically"
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const { sourceCode } = context;
|
||||
const { ignoreCase, callbacksLast, shorthandFirst, shorthandLast, multiline, noSortAlphabetically, reservedFirst, reservedLast, locale } = options;
|
||||
const reservedFirstError = validateReservedFirstConfig(context, reservedFirst);
|
||||
const reservedList = Array.isArray(reservedFirst) ? reservedFirst : RESERVED_PROPS_LIST;
|
||||
return {
|
||||
Program() {
|
||||
attributeMap = /* @__PURE__ */ new WeakMap();
|
||||
},
|
||||
JSXOpeningElement(node) {
|
||||
const nodeReservedList = reservedFirst && !isDOMComponent(node) ? reservedList.filter((prop) => prop !== "dangerouslySetInnerHTML") : reservedList;
|
||||
node.attributes.reduce((memo, decl, idx, attrs) => {
|
||||
if (decl.type === "JSXSpreadAttribute") return attrs[idx + 1];
|
||||
let previousPropName = getPropName(sourceCode, memo);
|
||||
let currentPropName = getPropName(sourceCode, decl);
|
||||
const previousReservedNamespace = previousPropName.split(":")[0];
|
||||
const currentReservedNamespace = currentPropName.split(":")[0];
|
||||
const previousValue = memo.value;
|
||||
const currentValue = decl.value;
|
||||
const previousIsCallback = isCallbackPropName(previousPropName);
|
||||
const currentIsCallback = isCallbackPropName(currentPropName);
|
||||
if (ignoreCase) {
|
||||
previousPropName = previousPropName.toLowerCase();
|
||||
currentPropName = currentPropName.toLowerCase();
|
||||
}
|
||||
if (reservedFirst) {
|
||||
if (reservedFirstError) {
|
||||
reservedFirstError(decl);
|
||||
return memo;
|
||||
}
|
||||
const previousReservedIndex = getReservedPropIndex(previousPropName, nodeReservedList);
|
||||
const currentReservedIndex = getReservedPropIndex(currentPropName, nodeReservedList);
|
||||
if (previousReservedIndex > -1 && currentReservedIndex === -1) return decl;
|
||||
if (reservedFirst !== true && previousReservedIndex > currentReservedIndex || previousReservedIndex === -1 && currentReservedIndex > -1) {
|
||||
reportNodeAttribute(decl, "listReservedPropsFirst", node, context, nodeReservedList);
|
||||
return memo;
|
||||
}
|
||||
if (previousReservedIndex > -1 && currentReservedIndex > -1 && currentReservedIndex > previousReservedIndex && previousReservedNamespace !== currentReservedNamespace) return decl;
|
||||
}
|
||||
if (reservedLast.length > 0) {
|
||||
const previousReservedIndex = getReservedPropIndex(previousPropName, reservedLast);
|
||||
const currentReservedIndex = getReservedPropIndex(currentPropName, reservedLast);
|
||||
if (previousReservedIndex === -1 && currentReservedIndex > -1) return decl;
|
||||
if (previousReservedIndex < currentReservedIndex || previousReservedIndex > -1 && currentReservedIndex === -1) {
|
||||
reportNodeAttribute(decl, "listReservedPropsLast", node, context, nodeReservedList);
|
||||
return memo;
|
||||
}
|
||||
if (previousReservedIndex > -1 && currentReservedIndex > -1 && currentReservedIndex > previousReservedIndex && previousReservedNamespace !== currentReservedNamespace) return decl;
|
||||
}
|
||||
if (callbacksLast) {
|
||||
if (!previousIsCallback && currentIsCallback) return decl;
|
||||
if (previousIsCallback && !currentIsCallback) {
|
||||
reportNodeAttribute(memo, "listCallbacksLast", node, context, nodeReservedList);
|
||||
return memo;
|
||||
}
|
||||
}
|
||||
if (shorthandFirst) {
|
||||
if (currentValue && !previousValue) return decl;
|
||||
if (!currentValue && previousValue) {
|
||||
reportNodeAttribute(decl, "listShorthandFirst", node, context, nodeReservedList);
|
||||
return memo;
|
||||
}
|
||||
}
|
||||
if (shorthandLast) {
|
||||
if (!currentValue && previousValue) return decl;
|
||||
if (currentValue && !previousValue) {
|
||||
reportNodeAttribute(memo, "listShorthandLast", node, context, nodeReservedList);
|
||||
return memo;
|
||||
}
|
||||
}
|
||||
const previousIsMultiline = !isSingleLine(memo);
|
||||
const currentIsMultiline = !isSingleLine(decl);
|
||||
if (multiline === "first") {
|
||||
if (previousIsMultiline && !currentIsMultiline) return decl;
|
||||
if (!previousIsMultiline && currentIsMultiline) {
|
||||
reportNodeAttribute(decl, "listMultilineFirst", node, context, nodeReservedList);
|
||||
return memo;
|
||||
}
|
||||
} else if (multiline === "last") {
|
||||
if (!previousIsMultiline && currentIsMultiline) return decl;
|
||||
if (previousIsMultiline && !currentIsMultiline) {
|
||||
reportNodeAttribute(memo, "listMultilineLast", node, context, nodeReservedList);
|
||||
return memo;
|
||||
}
|
||||
}
|
||||
if (!noSortAlphabetically && (ignoreCase || locale !== "auto" ? previousPropName.localeCompare(currentPropName, locale === "auto" ? void 0 : locale) > 0 : previousPropName > currentPropName)) {
|
||||
reportNodeAttribute(decl, "sortPropsByAlpha", node, context, nodeReservedList);
|
||||
return memo;
|
||||
}
|
||||
return decl;
|
||||
}, node.attributes[0]);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { jsx_sort_props_default as t };
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
import { K as isSingleLine, M as getTokenBeforeClosingBracket, f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
function validateClosingSlash(context, node, option) {
|
||||
const sourceCode = context.sourceCode;
|
||||
let adjacent;
|
||||
if ("selfClosing" in node && node.selfClosing) {
|
||||
const lastTokens = sourceCode.getLastTokens(node, 2);
|
||||
adjacent = !sourceCode.isSpaceBetween(lastTokens[0], lastTokens[1]);
|
||||
if (option === "never") {
|
||||
if (!adjacent) context.report({
|
||||
node,
|
||||
messageId: "selfCloseSlashNoSpace",
|
||||
loc: {
|
||||
start: lastTokens[0].loc.start,
|
||||
end: lastTokens[1].loc.end
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([lastTokens[0].range[1], lastTokens[1].range[0]]);
|
||||
}
|
||||
});
|
||||
} else if (option === "always" && adjacent) context.report({
|
||||
node,
|
||||
messageId: "selfCloseSlashNeedSpace",
|
||||
loc: {
|
||||
start: lastTokens[0].loc.start,
|
||||
end: lastTokens[1].loc.end
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(lastTokens[1], " ");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const firstTokens = sourceCode.getFirstTokens(node, 2);
|
||||
adjacent = !sourceCode.isSpaceBetween(firstTokens[0], firstTokens[1]);
|
||||
if (option === "never") {
|
||||
if (!adjacent) context.report({
|
||||
node,
|
||||
messageId: "closeSlashNoSpace",
|
||||
loc: {
|
||||
start: firstTokens[0].loc.start,
|
||||
end: firstTokens[1].loc.end
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([firstTokens[0].range[1], firstTokens[1].range[0]]);
|
||||
}
|
||||
});
|
||||
} else if (option === "always" && adjacent) context.report({
|
||||
node,
|
||||
messageId: "closeSlashNeedSpace",
|
||||
loc: {
|
||||
start: firstTokens[0].loc.start,
|
||||
end: firstTokens[1].loc.end
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(firstTokens[1], " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function validateBeforeSelfClosing(context, node, option) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const leftToken = getTokenBeforeClosingBracket(node);
|
||||
const closingSlash = sourceCode.getTokenAfter(leftToken);
|
||||
if (!isSingleLine(node) && option === "proportional-always") {
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(leftToken, closingSlash)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "beforeSelfCloseNeedNewline",
|
||||
loc: leftToken.loc.end,
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(closingSlash, "\n");
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!(0, import_ast_utils.isTokenOnSameLine)(leftToken, closingSlash)) return;
|
||||
const adjacent = !sourceCode.isSpaceBetween(leftToken, closingSlash);
|
||||
if ((option === "always" || option === "proportional-always") && adjacent) context.report({
|
||||
node,
|
||||
messageId: "beforeSelfCloseNeedSpace",
|
||||
loc: closingSlash.loc.start,
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(closingSlash, " ");
|
||||
}
|
||||
});
|
||||
else if (option === "never" && !adjacent) context.report({
|
||||
node,
|
||||
messageId: "beforeSelfCloseNoSpace",
|
||||
loc: closingSlash.loc.start,
|
||||
fix(fixer) {
|
||||
const previousToken = sourceCode.getTokenBefore(closingSlash);
|
||||
return fixer.removeRange([previousToken.range[1], closingSlash.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
function validateAfterOpening(context, node, option) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const openingToken = sourceCode.getTokenBefore(node.name);
|
||||
if (option === "allow-multiline") {
|
||||
if (openingToken.loc.start.line !== node.name.loc.start.line) return;
|
||||
}
|
||||
const adjacent = !sourceCode.isSpaceBetween(openingToken, node.name);
|
||||
if (option === "never" || option === "allow-multiline") {
|
||||
if (!adjacent) context.report({
|
||||
node,
|
||||
messageId: "afterOpenNoSpace",
|
||||
loc: {
|
||||
start: openingToken.loc.start,
|
||||
end: node.name.loc.start
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([openingToken.range[1], node.name.range[0]]);
|
||||
}
|
||||
});
|
||||
} else if (option === "always" && adjacent) context.report({
|
||||
node,
|
||||
messageId: "afterOpenNeedSpace",
|
||||
loc: {
|
||||
start: openingToken.loc.start,
|
||||
end: node.name.loc.start
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(node.name, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
function validateBeforeClosing(context, node, option) {
|
||||
if (!("selfClosing" in node && node.selfClosing)) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const leftToken = option === "proportional-always" ? getTokenBeforeClosingBracket(node) : sourceCode.getLastTokens(node, 2)[0];
|
||||
const closingToken = sourceCode.getTokenAfter(leftToken);
|
||||
if (!isSingleLine(node) && option === "proportional-always") {
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(leftToken, closingToken)) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "beforeCloseNeedNewline",
|
||||
loc: leftToken.loc.end,
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(closingToken, "\n");
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (leftToken.loc.start.line !== closingToken.loc.start.line) return;
|
||||
const adjacent = !sourceCode.isSpaceBetween(leftToken, closingToken);
|
||||
if (option === "never" && !adjacent) context.report({
|
||||
node,
|
||||
messageId: "beforeCloseNoSpace",
|
||||
loc: {
|
||||
start: leftToken.loc.end,
|
||||
end: closingToken.loc.start
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([leftToken.range[1], closingToken.range[0]]);
|
||||
}
|
||||
});
|
||||
else if (option === "always" && adjacent) context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: leftToken.loc.end,
|
||||
end: closingToken.loc.start
|
||||
},
|
||||
messageId: "beforeCloseNeedSpace",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(closingToken, " ");
|
||||
}
|
||||
});
|
||||
else if (option === "proportional-always" && node.type === "JSXOpeningElement" && adjacent !== isSingleLine(node)) context.report({
|
||||
node,
|
||||
messageId: "beforeCloseNeedSpace",
|
||||
loc: {
|
||||
start: leftToken.loc.end,
|
||||
end: closingToken.loc.start
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(closingToken, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
var jsx_tag_spacing_default = createRule({
|
||||
name: "jsx-tag-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce whitespace in and around the JSX opening and closing brackets" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
closingSlash: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"allow"
|
||||
]
|
||||
},
|
||||
beforeSelfClosing: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"proportional-always",
|
||||
"never",
|
||||
"allow"
|
||||
]
|
||||
},
|
||||
afterOpening: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"allow-multiline",
|
||||
"never",
|
||||
"allow"
|
||||
]
|
||||
},
|
||||
beforeClosing: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"proportional-always",
|
||||
"never",
|
||||
"allow"
|
||||
]
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
closingSlash: "never",
|
||||
beforeSelfClosing: "always",
|
||||
afterOpening: "never",
|
||||
beforeClosing: "allow"
|
||||
}],
|
||||
messages: {
|
||||
selfCloseSlashNoSpace: "Whitespace is forbidden between `/` and `>`; write `/>`",
|
||||
selfCloseSlashNeedSpace: "Whitespace is required between `/` and `>`; write `/ >`",
|
||||
closeSlashNoSpace: "Whitespace is forbidden between `<` and `/`; write `</`",
|
||||
closeSlashNeedSpace: "Whitespace is required between `<` and `/`; write `< /`",
|
||||
beforeSelfCloseNoSpace: "A space is forbidden before closing bracket",
|
||||
beforeSelfCloseNeedSpace: "A space is required before closing bracket",
|
||||
beforeSelfCloseNeedNewline: "A newline is required before closing bracket",
|
||||
afterOpenNoSpace: "A space is forbidden after opening bracket",
|
||||
afterOpenNeedSpace: "A space is required after opening bracket",
|
||||
beforeCloseNoSpace: "A space is forbidden before closing bracket",
|
||||
beforeCloseNeedSpace: "Whitespace is required before closing bracket",
|
||||
beforeCloseNeedNewline: "A newline is required before closing bracket"
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const { closingSlash, beforeSelfClosing, afterOpening, beforeClosing } = options;
|
||||
return {
|
||||
JSXOpeningElement(node) {
|
||||
if (closingSlash !== "allow" && node.selfClosing) validateClosingSlash(context, node, closingSlash);
|
||||
if (afterOpening !== "allow") validateAfterOpening(context, node, afterOpening);
|
||||
if (beforeSelfClosing !== "allow" && node.selfClosing) validateBeforeSelfClosing(context, node, beforeSelfClosing);
|
||||
if (beforeClosing !== "allow") validateBeforeClosing(context, node, beforeClosing);
|
||||
},
|
||||
JSXClosingElement(node) {
|
||||
if (afterOpening !== "allow") validateAfterOpening(context, node, afterOpening);
|
||||
if (closingSlash !== "allow") validateClosingSlash(context, node, closingSlash);
|
||||
if (beforeClosing !== "allow") validateBeforeClosing(context, node, beforeClosing);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { jsx_tag_spacing_default as t };
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
import { K as isSingleLine, f as createRule, g as import_ast_utils, o as isJSX } from "../utils.js";
|
||||
var jsx_wrap_multilines_default = createRule({
|
||||
name: "jsx-wrap-multilines",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow missing parentheses around multiline JSX" },
|
||||
fixable: "code",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
declaration: {
|
||||
type: ["string", "boolean"],
|
||||
enum: [
|
||||
true,
|
||||
false,
|
||||
"ignore",
|
||||
"parens",
|
||||
"parens-new-line"
|
||||
]
|
||||
},
|
||||
assignment: {
|
||||
type: ["string", "boolean"],
|
||||
enum: [
|
||||
true,
|
||||
false,
|
||||
"ignore",
|
||||
"parens",
|
||||
"parens-new-line"
|
||||
]
|
||||
},
|
||||
return: {
|
||||
type: ["string", "boolean"],
|
||||
enum: [
|
||||
true,
|
||||
false,
|
||||
"ignore",
|
||||
"parens",
|
||||
"parens-new-line"
|
||||
]
|
||||
},
|
||||
arrow: {
|
||||
type: ["string", "boolean"],
|
||||
enum: [
|
||||
true,
|
||||
false,
|
||||
"ignore",
|
||||
"parens",
|
||||
"parens-new-line"
|
||||
]
|
||||
},
|
||||
condition: {
|
||||
type: ["string", "boolean"],
|
||||
enum: [
|
||||
true,
|
||||
false,
|
||||
"ignore",
|
||||
"parens",
|
||||
"parens-new-line"
|
||||
]
|
||||
},
|
||||
logical: {
|
||||
type: ["string", "boolean"],
|
||||
enum: [
|
||||
true,
|
||||
false,
|
||||
"ignore",
|
||||
"parens",
|
||||
"parens-new-line"
|
||||
]
|
||||
},
|
||||
prop: {
|
||||
type: ["string", "boolean"],
|
||||
enum: [
|
||||
true,
|
||||
false,
|
||||
"ignore",
|
||||
"parens",
|
||||
"parens-new-line"
|
||||
]
|
||||
},
|
||||
propertyValue: {
|
||||
type: ["string", "boolean"],
|
||||
enum: [
|
||||
true,
|
||||
false,
|
||||
"ignore",
|
||||
"parens",
|
||||
"parens-new-line"
|
||||
]
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
declaration: "parens",
|
||||
assignment: "parens",
|
||||
return: "parens",
|
||||
arrow: "parens",
|
||||
condition: "ignore",
|
||||
logical: "ignore",
|
||||
prop: "ignore",
|
||||
propertyValue: "ignore"
|
||||
}],
|
||||
messages: {
|
||||
missingParens: "Missing parentheses around multilines JSX",
|
||||
parensOnNewLines: "Parentheses around JSX should be on separate lines"
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
function isEnabled(type) {
|
||||
const option = options[type];
|
||||
return option && option !== "ignore";
|
||||
}
|
||||
const { sourceCode } = context;
|
||||
function needsOpeningNewLine(node) {
|
||||
const previousToken = sourceCode.getTokenBefore(node);
|
||||
if (!(0, import_ast_utils.isParenthesized)(node, sourceCode)) return false;
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(previousToken, node)) return true;
|
||||
return false;
|
||||
}
|
||||
function needsClosingNewLine(node) {
|
||||
const nextToken = sourceCode.getTokenAfter(node);
|
||||
if (!(0, import_ast_utils.isParenthesized)(node, sourceCode)) return false;
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(node, nextToken)) return true;
|
||||
return false;
|
||||
}
|
||||
function trimTokenBeforeNewline(tokenBefore) {
|
||||
const isBracket = tokenBefore.value === "{" || tokenBefore.value === "[";
|
||||
return `${tokenBefore.value.trim()}${isBracket ? "" : " "}`;
|
||||
}
|
||||
function check(node, type) {
|
||||
if (!node || !isJSX(node)) return;
|
||||
const option = options[type];
|
||||
if ((option === true || option === "parens") && !(0, import_ast_utils.isParenthesized)(node, sourceCode) && !isSingleLine(node)) context.report({
|
||||
node,
|
||||
messageId: "missingParens",
|
||||
fix: (fixer) => fixer.replaceText(node, `(${sourceCode.getText(node)})`)
|
||||
});
|
||||
if (option === "parens-new-line" && !isSingleLine(node)) if (!(0, import_ast_utils.isParenthesized)(node, sourceCode)) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(node);
|
||||
const tokenAfter = sourceCode.getTokenAfter(node);
|
||||
const start = node.loc.start;
|
||||
if (tokenBefore.loc.end.line < start.line) {
|
||||
const textBefore = sourceCode.getText().slice(tokenBefore.range[1], node.range[0]).trim();
|
||||
const isTab = /^\t/.test(sourceCode.lines[start.line - 1]);
|
||||
const INDENT = isTab ? " " : " ";
|
||||
const indentBefore = INDENT.repeat(start.column);
|
||||
const indentAfter = INDENT.repeat(Math.max(0, start.column - (isTab ? 1 : 2)));
|
||||
context.report({
|
||||
node,
|
||||
messageId: "missingParens",
|
||||
fix: (fixer) => fixer.replaceTextRange([tokenBefore.range[0], tokenAfter && (tokenAfter.value === ";" || tokenAfter.value === "}") ? tokenAfter.range[0] : node.range[1]], `${trimTokenBeforeNewline(tokenBefore)}(\n${indentBefore}${textBefore}${textBefore.length > 0 ? `\n${indentBefore}` : ""}${sourceCode.getText(node)}\n${indentAfter})`)
|
||||
});
|
||||
} else context.report({
|
||||
node,
|
||||
messageId: "missingParens",
|
||||
fix: (fixer) => fixer.replaceText(node, `(\n${sourceCode.getText(node)}\n)`)
|
||||
});
|
||||
} else {
|
||||
const needsOpening = needsOpeningNewLine(node);
|
||||
const needsClosing = needsClosingNewLine(node);
|
||||
if (needsOpening || needsClosing) context.report({
|
||||
node,
|
||||
messageId: "parensOnNewLines",
|
||||
fix: (fixer) => {
|
||||
let fixed = sourceCode.getText(node);
|
||||
if (needsOpening) fixed = `\n${fixed}`;
|
||||
if (needsClosing) fixed = `${fixed}\n`;
|
||||
return fixer.replaceText(node, fixed);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return {
|
||||
VariableDeclarator(node) {
|
||||
const type = "declaration";
|
||||
if (!isEnabled(type)) return;
|
||||
if (!isEnabled("condition") && node.init?.type === "ConditionalExpression") {
|
||||
check(node.init.consequent, type);
|
||||
check(node.init.alternate, type);
|
||||
return;
|
||||
}
|
||||
check(node.init, type);
|
||||
},
|
||||
AssignmentExpression(node) {
|
||||
const type = "assignment";
|
||||
if (!isEnabled(type)) return;
|
||||
if (!isEnabled("condition") && node.right.type === "ConditionalExpression") {
|
||||
check(node.right.consequent, type);
|
||||
check(node.right.alternate, type);
|
||||
return;
|
||||
}
|
||||
check(node.right, type);
|
||||
},
|
||||
ReturnStatement(node) {
|
||||
const type = "return";
|
||||
if (isEnabled(type)) check(node.argument, type);
|
||||
},
|
||||
"ArrowFunctionExpression:exit": (node) => {
|
||||
const arrowBody = node.body;
|
||||
const type = "arrow";
|
||||
if (isEnabled(type) && arrowBody.type !== "BlockStatement") check(arrowBody, type);
|
||||
},
|
||||
ConditionalExpression(node) {
|
||||
const type = "condition";
|
||||
if (isEnabled(type)) {
|
||||
check(node.consequent, type);
|
||||
check(node.alternate, type);
|
||||
}
|
||||
},
|
||||
LogicalExpression(node) {
|
||||
const type = "logical";
|
||||
if (isEnabled(type)) check(node.right, type);
|
||||
},
|
||||
JSXAttribute(node) {
|
||||
const type = "prop";
|
||||
if (isEnabled(type) && node.value?.type === "JSXExpressionContainer") check(node.value.expression, type);
|
||||
},
|
||||
ObjectExpression(node) {
|
||||
const type = "propertyValue";
|
||||
if (isEnabled(type)) node.properties.forEach((property) => {
|
||||
if (property.type === "Property" && property.value.type === "JSXElement") check(property.value, type);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { jsx_wrap_multilines_default as t };
|
||||
+543
@@ -0,0 +1,543 @@
|
||||
import { A as getStaticPropertyName, K as isSingleLine, f as createRule, g as import_ast_utils, m as AST_NODE_TYPES, n as getStringLength } from "../utils.js";
|
||||
const listeningNodes = [
|
||||
"ObjectExpression",
|
||||
"ObjectPattern",
|
||||
"ImportDeclaration",
|
||||
"ExportNamedDeclaration",
|
||||
"ExportAllDeclaration",
|
||||
"TSTypeLiteral",
|
||||
"TSInterfaceBody",
|
||||
"ClassBody"
|
||||
];
|
||||
function initOptionProperty(toOptions, fromOptions) {
|
||||
toOptions.mode = fromOptions.mode || "strict";
|
||||
if (typeof fromOptions.beforeColon !== "undefined") toOptions.beforeColon = +fromOptions.beforeColon;
|
||||
else toOptions.beforeColon = 0;
|
||||
if (typeof fromOptions.afterColon !== "undefined") toOptions.afterColon = +fromOptions.afterColon;
|
||||
else toOptions.afterColon = 1;
|
||||
if (typeof fromOptions.align !== "undefined") if (typeof fromOptions.align === "object") toOptions.align = fromOptions.align;
|
||||
else toOptions.align = {
|
||||
on: fromOptions.align,
|
||||
mode: toOptions.mode,
|
||||
beforeColon: toOptions.beforeColon,
|
||||
afterColon: toOptions.afterColon
|
||||
};
|
||||
return toOptions;
|
||||
}
|
||||
function initOptions(toOptions, fromOptions) {
|
||||
if (typeof fromOptions.align === "object") {
|
||||
toOptions.align = initOptionProperty({}, fromOptions.align);
|
||||
toOptions.align.on = fromOptions.align.on || "colon";
|
||||
toOptions.align.mode = fromOptions.align.mode || "strict";
|
||||
toOptions.multiLine = initOptionProperty({}, fromOptions.multiLine || fromOptions);
|
||||
toOptions.singleLine = initOptionProperty({}, fromOptions.singleLine || fromOptions);
|
||||
} else {
|
||||
toOptions.multiLine = initOptionProperty({}, fromOptions.multiLine || fromOptions);
|
||||
toOptions.singleLine = initOptionProperty({}, fromOptions.singleLine || fromOptions);
|
||||
if (toOptions.multiLine.align) toOptions.align = {
|
||||
on: toOptions.multiLine.align.on,
|
||||
mode: toOptions.multiLine.align.mode || toOptions.multiLine.mode,
|
||||
beforeColon: toOptions.multiLine.align.beforeColon,
|
||||
afterColon: toOptions.multiLine.align.afterColon
|
||||
};
|
||||
}
|
||||
toOptions.ignoredNodes = fromOptions.ignoredNodes || [];
|
||||
return toOptions;
|
||||
}
|
||||
var key_spacing_default = createRule({
|
||||
name: "key-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent spacing between property names and type annotations in types and interfaces" },
|
||||
fixable: "whitespace",
|
||||
schema: [{ anyOf: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
align: { anyOf: [{
|
||||
type: "string",
|
||||
enum: ["colon", "value"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
mode: {
|
||||
type: "string",
|
||||
enum: ["strict", "minimum"]
|
||||
},
|
||||
on: {
|
||||
type: "string",
|
||||
enum: ["colon", "value"]
|
||||
},
|
||||
beforeColon: { type: "boolean" },
|
||||
afterColon: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}] },
|
||||
mode: {
|
||||
type: "string",
|
||||
enum: ["strict", "minimum"]
|
||||
},
|
||||
beforeColon: { type: "boolean" },
|
||||
afterColon: { type: "boolean" },
|
||||
ignoredNodes: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
enum: listeningNodes
|
||||
}
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
singleLine: {
|
||||
type: "object",
|
||||
properties: {
|
||||
mode: {
|
||||
type: "string",
|
||||
enum: ["strict", "minimum"]
|
||||
},
|
||||
beforeColon: { type: "boolean" },
|
||||
afterColon: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
},
|
||||
multiLine: {
|
||||
type: "object",
|
||||
properties: {
|
||||
align: { anyOf: [{
|
||||
type: "string",
|
||||
enum: ["colon", "value"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
mode: {
|
||||
type: "string",
|
||||
enum: ["strict", "minimum"]
|
||||
},
|
||||
on: {
|
||||
type: "string",
|
||||
enum: ["colon", "value"]
|
||||
},
|
||||
beforeColon: { type: "boolean" },
|
||||
afterColon: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}] },
|
||||
mode: {
|
||||
type: "string",
|
||||
enum: ["strict", "minimum"]
|
||||
},
|
||||
beforeColon: { type: "boolean" },
|
||||
afterColon: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
singleLine: {
|
||||
type: "object",
|
||||
properties: {
|
||||
mode: {
|
||||
type: "string",
|
||||
enum: ["strict", "minimum"]
|
||||
},
|
||||
beforeColon: { type: "boolean" },
|
||||
afterColon: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
},
|
||||
multiLine: {
|
||||
type: "object",
|
||||
properties: {
|
||||
mode: {
|
||||
type: "string",
|
||||
enum: ["strict", "minimum"]
|
||||
},
|
||||
beforeColon: { type: "boolean" },
|
||||
afterColon: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
},
|
||||
align: {
|
||||
type: "object",
|
||||
properties: {
|
||||
mode: {
|
||||
type: "string",
|
||||
enum: ["strict", "minimum"]
|
||||
},
|
||||
on: {
|
||||
type: "string",
|
||||
enum: ["colon", "value"]
|
||||
},
|
||||
beforeColon: { type: "boolean" },
|
||||
afterColon: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
] }],
|
||||
defaultOptions: [{}],
|
||||
messages: {
|
||||
extraKey: "Extra space after {{computed}}key '{{key}}'.",
|
||||
extraValue: "Extra space before value for {{computed}}key '{{key}}'.",
|
||||
missingKey: "Missing space after {{computed}}key '{{key}}'.",
|
||||
missingValue: "Missing space before value for {{computed}}key '{{key}}'."
|
||||
}
|
||||
},
|
||||
create(context, [_options]) {
|
||||
const options = _options || {};
|
||||
const ruleOptions = initOptions({}, options);
|
||||
const multiLineOptions = ruleOptions.multiLine;
|
||||
const singleLineOptions = ruleOptions.singleLine;
|
||||
const alignmentOptions = ruleOptions.align || null;
|
||||
const ignoredNodes = ruleOptions.ignoredNodes;
|
||||
const sourceCode = context.sourceCode;
|
||||
function containsLineTerminator(str) {
|
||||
return import_ast_utils.LINEBREAK_MATCHER.test(str);
|
||||
}
|
||||
function isSingleLineImportAttributes(node, sourceCode) {
|
||||
if (node.type === "TSImportType") {
|
||||
if ("options" in node && node.options) return isSingleLine(node.options);
|
||||
return false;
|
||||
}
|
||||
const openingBrace = sourceCode.getTokenBefore(node.attributes[0], import_ast_utils.isOpeningBraceToken);
|
||||
return (0, import_ast_utils.isTokenOnSameLine)(sourceCode.getTokenAfter(node.attributes[node.attributes.length - 1], import_ast_utils.isClosingBraceToken), openingBrace);
|
||||
}
|
||||
function isSingleLineProperties(properties) {
|
||||
const [firstProp] = properties;
|
||||
return (0, import_ast_utils.isTokenOnSameLine)(properties.at(-1), firstProp);
|
||||
}
|
||||
function isKeyValueProperty(property) {
|
||||
if (property.type === "ImportAttribute") return true;
|
||||
return !("method" in property && property.method || "shorthand" in property && property.shorthand || "kind" in property && property.kind !== "init" || property.type !== "Property");
|
||||
}
|
||||
function getNextColon(node) {
|
||||
return sourceCode.getTokenAfter(node, import_ast_utils.isColonToken);
|
||||
}
|
||||
function getLastTokenBeforeColon(node) {
|
||||
const colonToken = getNextColon(node);
|
||||
return sourceCode.getTokenBefore(colonToken);
|
||||
}
|
||||
function getFirstTokenAfterColon(node) {
|
||||
const colonToken = getNextColon(node);
|
||||
return sourceCode.getTokenAfter(colonToken);
|
||||
}
|
||||
function continuesPropertyGroup(lastMember, candidate) {
|
||||
const groupEndLine = lastMember.loc.start.line;
|
||||
const candidateValueStartLine = (isKeyValueProperty(candidate) ? getFirstTokenAfterColon(candidate.key) : candidate).loc.start.line;
|
||||
if (candidateValueStartLine - groupEndLine <= 1) return true;
|
||||
const leadingComments = sourceCode.getCommentsBefore(candidate);
|
||||
if (leadingComments.length && leadingComments[0].loc.start.line - groupEndLine <= 1 && candidateValueStartLine - leadingComments.at(-1).loc.end.line <= 1) {
|
||||
for (let i = 1; i < leadingComments.length; i++) if (leadingComments[i].loc.start.line - leadingComments[i - 1].loc.end.line > 1) return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function getKey(property) {
|
||||
const key = property.key;
|
||||
if (property.type !== "ImportAttribute" && property.computed) return sourceCode.getText().slice(key.range[0], key.range[1]);
|
||||
return getStaticPropertyName(property);
|
||||
}
|
||||
function report(property, side, whitespace, expected, mode) {
|
||||
const diff = whitespace.length - expected;
|
||||
if ((diff && mode === "strict" || diff < 0 && mode === "minimum" || diff > 0 && !expected && mode === "minimum") && !(expected && containsLineTerminator(whitespace))) {
|
||||
const nextColon = getNextColon(property.key);
|
||||
const tokenBeforeColon = sourceCode.getTokenBefore(nextColon, { includeComments: true });
|
||||
const tokenAfterColon = sourceCode.getTokenAfter(nextColon, { includeComments: true });
|
||||
const isKeySide = side === "key";
|
||||
const isExtra = diff > 0;
|
||||
const diffAbs = Math.abs(diff);
|
||||
const spaces = new Array(diffAbs + 1).join(" ");
|
||||
const locStart = isKeySide ? tokenBeforeColon.loc.end : nextColon.loc.start;
|
||||
const locEnd = isKeySide ? nextColon.loc.start : tokenAfterColon.loc.start;
|
||||
const missingLoc = isKeySide ? tokenBeforeColon.loc : tokenAfterColon.loc;
|
||||
const loc = isExtra ? {
|
||||
start: locStart,
|
||||
end: locEnd
|
||||
} : missingLoc;
|
||||
let fix;
|
||||
if (isExtra) {
|
||||
let range;
|
||||
if (isKeySide) range = [tokenBeforeColon.range[1], tokenBeforeColon.range[1] + diffAbs];
|
||||
else range = [tokenAfterColon.range[0] - diffAbs, tokenAfterColon.range[0]];
|
||||
fix = function(fixer) {
|
||||
return fixer.removeRange(range);
|
||||
};
|
||||
} else if (isKeySide) fix = function(fixer) {
|
||||
return fixer.insertTextAfter(tokenBeforeColon, spaces);
|
||||
};
|
||||
else fix = function(fixer) {
|
||||
return fixer.insertTextBefore(tokenAfterColon, spaces);
|
||||
};
|
||||
let messageId;
|
||||
if (isExtra) messageId = side === "key" ? "extraKey" : "extraValue";
|
||||
else messageId = side === "key" ? "missingKey" : "missingValue";
|
||||
context.report({
|
||||
node: property[side],
|
||||
loc,
|
||||
messageId,
|
||||
data: {
|
||||
computed: property.type !== "ImportAttribute" && property.computed ? "computed " : "",
|
||||
key: getKey(property)
|
||||
},
|
||||
fix
|
||||
});
|
||||
}
|
||||
}
|
||||
function getKeyWidth(property) {
|
||||
const startToken = sourceCode.getFirstToken(property);
|
||||
const endToken = getLastTokenBeforeColon(property.key);
|
||||
return getStringLength(sourceCode.getText().slice(startToken.range[0], endToken.range[1]));
|
||||
}
|
||||
function getPropertyWhitespace(property) {
|
||||
const whitespace = /(\s*):(\s*)/u.exec(sourceCode.getText().slice(property.key.range[1], property.value.range[0]));
|
||||
if (whitespace) return {
|
||||
beforeColon: whitespace[1],
|
||||
afterColon: whitespace[2]
|
||||
};
|
||||
return null;
|
||||
}
|
||||
function createGroups(properties) {
|
||||
if (properties.length === 1) return [properties];
|
||||
return properties.reduce((groups, property) => {
|
||||
const currentGroup = groups.at(-1);
|
||||
const prev = currentGroup.at(-1);
|
||||
if (!prev || continuesPropertyGroup(prev, property)) currentGroup.push(property);
|
||||
else groups.push([property]);
|
||||
return groups;
|
||||
}, [[]]);
|
||||
}
|
||||
function verifyGroupAlignment(properties) {
|
||||
const length = properties.length;
|
||||
const widths = properties.map(getKeyWidth);
|
||||
const align = alignmentOptions.on;
|
||||
let targetWidth = Math.max(...widths);
|
||||
let beforeColon;
|
||||
let afterColon;
|
||||
let mode;
|
||||
if (alignmentOptions && length > 1) {
|
||||
beforeColon = alignmentOptions.beforeColon;
|
||||
afterColon = alignmentOptions.afterColon;
|
||||
mode = alignmentOptions.mode;
|
||||
} else {
|
||||
beforeColon = multiLineOptions.beforeColon;
|
||||
afterColon = multiLineOptions.afterColon;
|
||||
mode = alignmentOptions.mode;
|
||||
}
|
||||
targetWidth += align === "colon" ? beforeColon : afterColon;
|
||||
for (let i = 0; i < length; i++) {
|
||||
const property = properties[i];
|
||||
const whitespace = getPropertyWhitespace(property);
|
||||
if (whitespace) {
|
||||
const width = widths[i];
|
||||
if (align === "value") {
|
||||
report(property, "key", whitespace.beforeColon, beforeColon, mode);
|
||||
report(property, "value", whitespace.afterColon, targetWidth - width, mode);
|
||||
} else {
|
||||
report(property, "key", whitespace.beforeColon, targetWidth - width, mode);
|
||||
report(property, "value", whitespace.afterColon, afterColon, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function verifySpacing(node, lineOptions) {
|
||||
if (ignoredNodes.includes(node.parent.type)) return;
|
||||
const actual = getPropertyWhitespace(node);
|
||||
if (actual) {
|
||||
report(node, "key", actual.beforeColon, lineOptions.beforeColon, lineOptions.mode);
|
||||
report(node, "value", actual.afterColon, lineOptions.afterColon, lineOptions.mode);
|
||||
}
|
||||
}
|
||||
function verifyListSpacing(properties, lineOptions) {
|
||||
const length = properties.length;
|
||||
for (let i = 0; i < length; i++) verifySpacing(properties[i], lineOptions);
|
||||
}
|
||||
function verifyAlignment(properties) {
|
||||
createGroups(properties).forEach((group) => {
|
||||
const properties = group.filter(isKeyValueProperty);
|
||||
if (properties.length > 0 && isSingleLineProperties(properties)) verifyListSpacing(properties, multiLineOptions);
|
||||
else verifyGroupAlignment(properties);
|
||||
});
|
||||
}
|
||||
function verifyImportAttributes(node) {
|
||||
if (ignoredNodes.includes(node.type)) return;
|
||||
if (!node.attributes) return;
|
||||
if (!node.attributes.length) return;
|
||||
if (isSingleLineImportAttributes(node, sourceCode)) verifyListSpacing(node.attributes, singleLineOptions);
|
||||
else verifyAlignment(node.attributes);
|
||||
}
|
||||
const baseRules = alignmentOptions ? {
|
||||
ObjectExpression(node) {
|
||||
if (ignoredNodes.includes(node.type)) return;
|
||||
if (isSingleLine(node)) verifyListSpacing(node.properties.filter(isKeyValueProperty), singleLineOptions);
|
||||
else verifyAlignment(node.properties);
|
||||
},
|
||||
ImportDeclaration(node) {
|
||||
verifyImportAttributes(node);
|
||||
},
|
||||
ExportNamedDeclaration(node) {
|
||||
verifyImportAttributes(node);
|
||||
},
|
||||
ExportAllDeclaration(node) {
|
||||
verifyImportAttributes(node);
|
||||
}
|
||||
} : {
|
||||
Property(node) {
|
||||
verifySpacing(node, isSingleLine(node.parent) ? singleLineOptions : multiLineOptions);
|
||||
},
|
||||
ImportAttribute(node) {
|
||||
const parent = node.parent;
|
||||
verifySpacing(node, isSingleLineImportAttributes(parent, sourceCode) ? singleLineOptions : multiLineOptions);
|
||||
}
|
||||
};
|
||||
function adjustedColumn(position) {
|
||||
const line = position.line - 1;
|
||||
return getStringLength(sourceCode.lines.at(line).slice(0, position.column));
|
||||
}
|
||||
function isKeyTypeNode(node) {
|
||||
return (node.type === AST_NODE_TYPES.TSPropertySignature || node.type === AST_NODE_TYPES.TSIndexSignature || node.type === AST_NODE_TYPES.PropertyDefinition) && !!node.typeAnnotation;
|
||||
}
|
||||
function isApplicable(node) {
|
||||
return isKeyTypeNode(node) && (0, import_ast_utils.isTokenOnSameLine)(node, node.typeAnnotation);
|
||||
}
|
||||
function getKeyText(node) {
|
||||
if (node.type !== AST_NODE_TYPES.TSIndexSignature) return sourceCode.getText(node.key);
|
||||
return sourceCode.getText(node).slice(0, sourceCode.getTokenAfter(node.parameters.at(-1), import_ast_utils.isClosingBracketToken).range[1] - node.range[0]);
|
||||
}
|
||||
function getKeyLocEnd(node) {
|
||||
return getLastTokenBeforeColon(node.type !== AST_NODE_TYPES.TSIndexSignature ? node.key : node.parameters.at(-1)).loc.end;
|
||||
}
|
||||
function checkBeforeColon(node, expectedWhitespaceBeforeColon, mode) {
|
||||
const { typeAnnotation } = node;
|
||||
const difference = typeAnnotation.loc.start.column - getKeyLocEnd(node).column - expectedWhitespaceBeforeColon;
|
||||
if (mode === "strict" ? difference : difference < 0) context.report({
|
||||
node,
|
||||
messageId: difference > 0 ? "extraKey" : "missingKey",
|
||||
fix: (fixer) => {
|
||||
if (difference > 0) return fixer.removeRange([typeAnnotation.range[0] - difference, typeAnnotation.range[0]]);
|
||||
return fixer.insertTextBefore(typeAnnotation, " ".repeat(-difference));
|
||||
},
|
||||
data: {
|
||||
computed: "",
|
||||
key: getKeyText(node)
|
||||
}
|
||||
});
|
||||
}
|
||||
function checkAfterColon(node, expectedWhitespaceAfterColon, mode) {
|
||||
const { typeAnnotation } = node;
|
||||
const colonToken = sourceCode.getFirstToken(typeAnnotation);
|
||||
const difference = sourceCode.getTokenAfter(colonToken, { includeComments: true }).loc.start.column - colonToken.loc.start.column - 1 - expectedWhitespaceAfterColon;
|
||||
if (mode === "strict" ? difference : difference < 0) context.report({
|
||||
node,
|
||||
messageId: difference > 0 ? "extraValue" : "missingValue",
|
||||
fix: (fixer) => {
|
||||
if (difference > 0) return fixer.removeRange([colonToken.range[1], colonToken.range[1] + difference]);
|
||||
return fixer.insertTextAfter(colonToken, " ".repeat(-difference));
|
||||
},
|
||||
data: {
|
||||
computed: "",
|
||||
key: getKeyText(node)
|
||||
}
|
||||
});
|
||||
}
|
||||
function continuesAlignGroup(lastMember, candidate) {
|
||||
const groupEndLine = lastMember.loc.start.line;
|
||||
const candidateValueStartLine = (isKeyTypeNode(candidate) ? candidate.typeAnnotation : candidate).loc.start.line;
|
||||
if (candidateValueStartLine === groupEndLine) return false;
|
||||
if (candidateValueStartLine - groupEndLine === 1) return true;
|
||||
const leadingComments = sourceCode.getCommentsBefore(candidate);
|
||||
if (leadingComments.length && leadingComments[0].loc.start.line - groupEndLine <= 1 && candidateValueStartLine - leadingComments.at(-1).loc.end.line <= 1) {
|
||||
for (let i = 1; i < leadingComments.length; i++) if (leadingComments[i].loc.start.line - leadingComments[i - 1].loc.end.line > 1) return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function checkAlignGroup(group) {
|
||||
let alignColumn = 0;
|
||||
const align = (typeof options.align === "object" ? options.align.on : typeof options.multiLine?.align === "object" ? options.multiLine.align.on : options.multiLine?.align ?? options.align) ?? "colon";
|
||||
const expectedWhitespaceBeforeColon = (typeof options.align === "object" ? options.align.beforeColon : options.multiLine ? typeof options.multiLine.align === "object" ? options.multiLine.align.beforeColon : options.multiLine.beforeColon : options.beforeColon) ?? false ? 1 : 0;
|
||||
const expectedWhitespaceAfterColon = (typeof options.align === "object" ? options.align.afterColon : options.multiLine ? typeof options.multiLine.align === "object" ? options.multiLine.align.afterColon : options.multiLine.afterColon : options.afterColon) ?? true ? 1 : 0;
|
||||
const mode = (typeof options.align === "object" ? options.align.mode : options.multiLine ? typeof options.multiLine.align === "object" ? options.multiLine.align.mode ?? options.multiLine.mode : options.multiLine.mode : options.mode) ?? "strict";
|
||||
for (const node of group) if (isKeyTypeNode(node)) {
|
||||
const keyEnd = adjustedColumn(getKeyLocEnd(node));
|
||||
alignColumn = Math.max(alignColumn, align === "colon" ? keyEnd + expectedWhitespaceBeforeColon : keyEnd + 1 + expectedWhitespaceAfterColon + expectedWhitespaceBeforeColon);
|
||||
}
|
||||
for (const node of group) {
|
||||
if (!isApplicable(node)) continue;
|
||||
const { typeAnnotation } = node;
|
||||
const toCheck = align === "colon" ? typeAnnotation : typeAnnotation.typeAnnotation;
|
||||
const difference = adjustedColumn(toCheck.loc.start) - alignColumn;
|
||||
if (difference) context.report({
|
||||
node,
|
||||
messageId: difference > 0 ? align === "colon" ? "extraKey" : "extraValue" : align === "colon" ? "missingKey" : "missingValue",
|
||||
fix: (fixer) => {
|
||||
if (difference > 0) return fixer.removeRange([toCheck.range[0] - difference, toCheck.range[0]]);
|
||||
return fixer.insertTextBefore(toCheck, " ".repeat(-difference));
|
||||
},
|
||||
data: {
|
||||
computed: "",
|
||||
key: getKeyText(node)
|
||||
}
|
||||
});
|
||||
if (align === "colon") checkAfterColon(node, expectedWhitespaceAfterColon, mode);
|
||||
else checkBeforeColon(node, expectedWhitespaceBeforeColon, mode);
|
||||
}
|
||||
}
|
||||
function checkIndividualNode(node, { singleLine }) {
|
||||
const expectedWhitespaceBeforeColon = (singleLine ? options.singleLine ? options.singleLine.beforeColon : options.beforeColon : options.multiLine ? options.multiLine.beforeColon : options.beforeColon) ?? false ? 1 : 0;
|
||||
const expectedWhitespaceAfterColon = (singleLine ? options.singleLine ? options.singleLine.afterColon : options.afterColon : options.multiLine ? options.multiLine.afterColon : options.afterColon) ?? true ? 1 : 0;
|
||||
const mode = (singleLine ? options.singleLine ? options.singleLine.mode : options.mode : options.multiLine ? options.multiLine.mode : options.mode) ?? "strict";
|
||||
if (isApplicable(node)) {
|
||||
checkBeforeColon(node, expectedWhitespaceBeforeColon, mode);
|
||||
checkAfterColon(node, expectedWhitespaceAfterColon, mode);
|
||||
}
|
||||
}
|
||||
function validateBody(body) {
|
||||
if (ignoredNodes.includes(body.type)) return;
|
||||
const members = body.type === AST_NODE_TYPES.TSTypeLiteral ? body.members : body.body;
|
||||
let alignGroups = [];
|
||||
let unalignedElements = [];
|
||||
if (options.align || options.multiLine?.align) {
|
||||
let currentAlignGroup = [];
|
||||
alignGroups.push(currentAlignGroup);
|
||||
let prevNode;
|
||||
for (const node of members) {
|
||||
let prevAlignedNode = currentAlignGroup.at(-1);
|
||||
if (prevAlignedNode !== prevNode) prevAlignedNode = void 0;
|
||||
if (prevAlignedNode && continuesAlignGroup(prevAlignedNode, node)) currentAlignGroup.push(node);
|
||||
else if (prevNode?.loc.start.line === node.loc.start.line) {
|
||||
if (prevAlignedNode) {
|
||||
unalignedElements.push(prevAlignedNode);
|
||||
currentAlignGroup.pop();
|
||||
}
|
||||
unalignedElements.push(node);
|
||||
} else {
|
||||
currentAlignGroup = [node];
|
||||
alignGroups.push(currentAlignGroup);
|
||||
}
|
||||
prevNode = node;
|
||||
}
|
||||
unalignedElements = unalignedElements.concat(...alignGroups.filter((group) => group.length === 1));
|
||||
alignGroups = alignGroups.filter((group) => group.length >= 2);
|
||||
} else unalignedElements = members;
|
||||
for (const group of alignGroups) checkAlignGroup(group);
|
||||
for (const node of unalignedElements) checkIndividualNode(node, { singleLine: isSingleLine(body) });
|
||||
}
|
||||
return {
|
||||
...baseRules,
|
||||
TSTypeLiteral: validateBody,
|
||||
TSInterfaceBody: validateBody,
|
||||
ClassBody: validateBody
|
||||
};
|
||||
}
|
||||
});
|
||||
export { key_spacing_default as t };
|
||||
+311
@@ -0,0 +1,311 @@
|
||||
import { b as KEYWORDS, f as createRule, g as import_ast_utils, m as AST_NODE_TYPES, z as isKeywordToken } from "../utils.js";
|
||||
const PREV_TOKEN = /^[)\]}>]$/u;
|
||||
const NEXT_TOKEN = /^(?:[([{<~!]|\+\+?|--?)$/u;
|
||||
const PREV_TOKEN_M = /^[)\]}>*]$/u;
|
||||
const NEXT_TOKEN_M = /^[{*]$/u;
|
||||
const TEMPLATE_OPEN_PAREN = /\$\{$/u;
|
||||
const TEMPLATE_CLOSE_PAREN = /^\}/u;
|
||||
const CHECK_TYPE = /^(?:JSXElement|RegularExpression|String|Template|PrivateIdentifier)$/u;
|
||||
var keyword_spacing_default = createRule({
|
||||
name: "keyword-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent spacing before and after keywords" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
before: { type: "boolean" },
|
||||
after: { type: "boolean" },
|
||||
overrides: {
|
||||
type: "object",
|
||||
properties: KEYWORDS.reduce((retv, key) => {
|
||||
retv[key] = {
|
||||
type: "object",
|
||||
properties: {
|
||||
before: { type: "boolean" },
|
||||
after: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
};
|
||||
return retv;
|
||||
}, {}),
|
||||
additionalProperties: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
before: true,
|
||||
after: true,
|
||||
overrides: {}
|
||||
}],
|
||||
messages: {
|
||||
expectedBefore: "Expected space(s) before \"{{value}}\".",
|
||||
expectedAfter: "Expected space(s) after \"{{value}}\".",
|
||||
unexpectedBefore: "Unexpected space(s) before \"{{value}}\".",
|
||||
unexpectedAfter: "Unexpected space(s) after \"{{value}}\"."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const tokensToIgnore = /* @__PURE__ */ new WeakSet();
|
||||
function isOpenParenOfTemplate(token) {
|
||||
return token.type === "Template" && TEMPLATE_OPEN_PAREN.test(token.value);
|
||||
}
|
||||
function isCloseParenOfTemplate(token) {
|
||||
return token.type === "Template" && TEMPLATE_CLOSE_PAREN.test(token.value);
|
||||
}
|
||||
function expectSpaceBefore(token, pattern) {
|
||||
const prevToken = sourceCode.getTokenBefore(token);
|
||||
if (prevToken && (CHECK_TYPE.test(prevToken.type) || pattern.test(prevToken.value)) && !isOpenParenOfTemplate(prevToken) && !tokensToIgnore.has(prevToken) && (0, import_ast_utils.isTokenOnSameLine)(prevToken, token) && !sourceCode.isSpaceBetween(prevToken, token)) context.report({
|
||||
loc: token.loc,
|
||||
messageId: "expectedBefore",
|
||||
data: token,
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
function unexpectSpaceBefore(token, pattern) {
|
||||
const prevToken = sourceCode.getTokenBefore(token);
|
||||
if (prevToken && (CHECK_TYPE.test(prevToken.type) || pattern.test(prevToken.value)) && !isOpenParenOfTemplate(prevToken) && !tokensToIgnore.has(prevToken) && (0, import_ast_utils.isTokenOnSameLine)(prevToken, token) && sourceCode.isSpaceBetween(prevToken, token)) context.report({
|
||||
loc: {
|
||||
start: prevToken.loc.end,
|
||||
end: token.loc.start
|
||||
},
|
||||
messageId: "unexpectedBefore",
|
||||
data: token,
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([prevToken.range[1], token.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
function expectSpaceAfter(token, pattern) {
|
||||
const nextToken = sourceCode.getTokenAfter(token);
|
||||
if (nextToken && (CHECK_TYPE.test(nextToken.type) || pattern.test(nextToken.value)) && !isCloseParenOfTemplate(nextToken) && !tokensToIgnore.has(nextToken) && (0, import_ast_utils.isTokenOnSameLine)(token, nextToken) && !sourceCode.isSpaceBetween(token, nextToken)) context.report({
|
||||
loc: token.loc,
|
||||
messageId: "expectedAfter",
|
||||
data: token,
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
function unexpectSpaceAfter(token, pattern) {
|
||||
const nextToken = sourceCode.getTokenAfter(token);
|
||||
if (nextToken && (CHECK_TYPE.test(nextToken.type) || pattern.test(nextToken.value)) && !isCloseParenOfTemplate(nextToken) && !tokensToIgnore.has(nextToken) && (0, import_ast_utils.isTokenOnSameLine)(token, nextToken) && sourceCode.isSpaceBetween(token, nextToken)) context.report({
|
||||
loc: {
|
||||
start: token.loc.end,
|
||||
end: nextToken.loc.start
|
||||
},
|
||||
messageId: "unexpectedAfter",
|
||||
data: token,
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([token.range[1], nextToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
function parseOptions(options) {
|
||||
const { before, after, overrides } = options;
|
||||
const defaultValue = {
|
||||
before: before ? expectSpaceBefore : unexpectSpaceBefore,
|
||||
after: after ? expectSpaceAfter : unexpectSpaceAfter
|
||||
};
|
||||
const retv = Object.create(null);
|
||||
for (let i = 0; i < KEYWORDS.length; ++i) {
|
||||
const key = KEYWORDS[i];
|
||||
const override = overrides[key];
|
||||
if (override) {
|
||||
const thisBefore = "before" in override ? override.before : before;
|
||||
const thisAfter = "after" in override ? override.after : after;
|
||||
retv[key] = {
|
||||
before: thisBefore ? expectSpaceBefore : unexpectSpaceBefore,
|
||||
after: thisAfter ? expectSpaceAfter : unexpectSpaceAfter
|
||||
};
|
||||
} else retv[key] = defaultValue;
|
||||
}
|
||||
return retv;
|
||||
}
|
||||
const checkMethodMap = parseOptions(options);
|
||||
function checkSpacingBefore(token, pattern) {
|
||||
checkMethodMap[token.value].before(token, pattern || PREV_TOKEN);
|
||||
}
|
||||
function checkSpacingAfter(token, pattern) {
|
||||
checkMethodMap[token.value].after(token, pattern || NEXT_TOKEN);
|
||||
}
|
||||
function checkSpacingAround(token) {
|
||||
checkSpacingBefore(token);
|
||||
checkSpacingAfter(token);
|
||||
}
|
||||
function checkSpacingAroundFirstToken(node) {
|
||||
const firstToken = node && sourceCode.getFirstToken(node);
|
||||
if (!firstToken) return;
|
||||
if (!isKeywordToken(firstToken)) if (node.type === "VariableDeclaration") {
|
||||
if (node.kind !== "using" && node.kind !== "await using" || firstToken.type !== "Identifier") return;
|
||||
} else return;
|
||||
checkSpacingAround(firstToken);
|
||||
}
|
||||
function checkSpacingBeforeFirstToken(node) {
|
||||
const firstToken = node && sourceCode.getFirstToken(node);
|
||||
if (isKeywordToken(firstToken)) checkSpacingBefore(firstToken);
|
||||
}
|
||||
function checkSpacingAroundTokenBefore(node) {
|
||||
if (node) {
|
||||
const token = sourceCode.getTokenBefore(node, isKeywordToken);
|
||||
if (token) checkSpacingAround(token);
|
||||
}
|
||||
}
|
||||
function checkSpacingForFunction(node) {
|
||||
const firstToken = node && sourceCode.getFirstToken(node);
|
||||
if (firstToken && (isKeywordToken(firstToken) && firstToken.value === "function" || firstToken.value === "async")) checkSpacingBefore(firstToken);
|
||||
}
|
||||
function checkSpacingForClass(node) {
|
||||
checkSpacingAroundFirstToken(node);
|
||||
checkSpacingAroundTokenBefore(node.superClass);
|
||||
}
|
||||
function checkSpacingForModuleDeclaration(node) {
|
||||
const firstToken = sourceCode.getFirstToken(node);
|
||||
checkSpacingBefore(firstToken, PREV_TOKEN_M);
|
||||
checkSpacingAfter(firstToken, NEXT_TOKEN_M);
|
||||
if (node.type === "ExportDefaultDeclaration") checkSpacingAround(sourceCode.getTokenAfter(firstToken));
|
||||
if (node.type === "ExportAllDeclaration" && node.exported) {
|
||||
const asToken = sourceCode.getTokenBefore(node.exported);
|
||||
checkSpacingBefore(asToken, PREV_TOKEN_M);
|
||||
checkSpacingAfter(asToken, NEXT_TOKEN_M);
|
||||
}
|
||||
if ("source" in node && node.source) {
|
||||
const fromToken = sourceCode.getTokenBefore(node.source);
|
||||
checkSpacingBefore(fromToken, PREV_TOKEN_M);
|
||||
checkSpacingAfter(fromToken, NEXT_TOKEN_M);
|
||||
if (node.attributes) {
|
||||
const withToken = sourceCode.getTokenAfter(node.source);
|
||||
if (isKeywordToken(withToken)) checkSpacingAround(withToken);
|
||||
}
|
||||
}
|
||||
if (node.type !== "ExportDefaultDeclaration") checkSpacingForTypeKeywordInImportExport(node);
|
||||
}
|
||||
function checkSpacingForTypeKeywordInImportExport(node) {
|
||||
let kind;
|
||||
switch (node.type) {
|
||||
case AST_NODE_TYPES.ImportDeclaration:
|
||||
kind = node.importKind;
|
||||
break;
|
||||
case AST_NODE_TYPES.ExportAllDeclaration:
|
||||
case AST_NODE_TYPES.ExportNamedDeclaration:
|
||||
kind = node.exportKind;
|
||||
break;
|
||||
}
|
||||
if (kind !== "type") return;
|
||||
const typeToken = sourceCode.getFirstToken(node, { skip: 1 });
|
||||
if (!(0, import_ast_utils.isTypeKeyword)(typeToken)) return;
|
||||
checkSpacingBefore(typeToken, PREV_TOKEN_M);
|
||||
checkSpacingAfter(typeToken, NEXT_TOKEN_M);
|
||||
}
|
||||
function checkSpacingForProperty(node) {
|
||||
if ("static" in node && node.static) checkSpacingAroundFirstToken(node);
|
||||
if (node.kind === "get" || node.kind === "set" || ("method" in node && node.method || node.type === "MethodDefinition") && "async" in node.value && node.value.async || node.type === AST_NODE_TYPES.AccessorProperty) {
|
||||
const token = sourceCode.getTokenBefore(node.key, (tok) => {
|
||||
switch (tok.value) {
|
||||
case "get":
|
||||
case "set":
|
||||
case "async":
|
||||
case "accessor": return true;
|
||||
default: return false;
|
||||
}
|
||||
});
|
||||
if (!token) throw new Error("Failed to find token get, set, or async beside method name");
|
||||
checkSpacingAround(token);
|
||||
}
|
||||
}
|
||||
return {
|
||||
DebuggerStatement: checkSpacingAroundFirstToken,
|
||||
WithStatement: checkSpacingAroundFirstToken,
|
||||
BreakStatement: checkSpacingAroundFirstToken,
|
||||
ContinueStatement: checkSpacingAroundFirstToken,
|
||||
ReturnStatement: checkSpacingAroundFirstToken,
|
||||
ThrowStatement: checkSpacingAroundFirstToken,
|
||||
TryStatement(node) {
|
||||
checkSpacingAroundFirstToken(node);
|
||||
if (node.handler) if (node.handler.param) checkSpacingBeforeFirstToken(node.handler);
|
||||
else checkSpacingAroundFirstToken(node.handler);
|
||||
checkSpacingAroundTokenBefore(node.finalizer);
|
||||
},
|
||||
IfStatement(node) {
|
||||
checkSpacingAroundFirstToken(node);
|
||||
checkSpacingAroundTokenBefore(node.alternate);
|
||||
},
|
||||
SwitchStatement: checkSpacingAroundFirstToken,
|
||||
SwitchCase: checkSpacingAroundFirstToken,
|
||||
DoWhileStatement(node) {
|
||||
checkSpacingAroundFirstToken(node);
|
||||
checkSpacingAroundTokenBefore(node.test);
|
||||
},
|
||||
ForInStatement(node) {
|
||||
checkSpacingAroundFirstToken(node);
|
||||
const inToken = sourceCode.getTokenBefore(node.right, import_ast_utils.isNotOpeningParenToken);
|
||||
if (sourceCode.getTokenBefore(inToken).type !== "PrivateIdentifier") checkSpacingBefore(inToken);
|
||||
checkSpacingAfter(inToken);
|
||||
},
|
||||
ForOfStatement(node) {
|
||||
if (node.await) {
|
||||
checkSpacingBefore(sourceCode.getFirstToken(node, 0));
|
||||
checkSpacingAfter(sourceCode.getFirstToken(node, 1));
|
||||
} else checkSpacingAroundFirstToken(node);
|
||||
const ofToken = sourceCode.getTokenBefore(node.right, import_ast_utils.isNotOpeningParenToken);
|
||||
if (sourceCode.getTokenBefore(ofToken).type !== "PrivateIdentifier") checkSpacingBefore(ofToken);
|
||||
checkSpacingAfter(ofToken);
|
||||
},
|
||||
ForStatement: checkSpacingAroundFirstToken,
|
||||
WhileStatement: checkSpacingAroundFirstToken,
|
||||
ClassDeclaration: checkSpacingForClass,
|
||||
ExportNamedDeclaration: checkSpacingForModuleDeclaration,
|
||||
ExportDefaultDeclaration: checkSpacingForModuleDeclaration,
|
||||
ExportAllDeclaration: checkSpacingForModuleDeclaration,
|
||||
FunctionDeclaration: checkSpacingForFunction,
|
||||
ImportDeclaration: checkSpacingForModuleDeclaration,
|
||||
VariableDeclaration: checkSpacingAroundFirstToken,
|
||||
ArrowFunctionExpression: checkSpacingForFunction,
|
||||
AwaitExpression(node) {
|
||||
checkSpacingBefore(sourceCode.getFirstToken(node));
|
||||
},
|
||||
ClassExpression: checkSpacingForClass,
|
||||
FunctionExpression: checkSpacingForFunction,
|
||||
NewExpression: checkSpacingBeforeFirstToken,
|
||||
Super: checkSpacingBeforeFirstToken,
|
||||
ThisExpression: checkSpacingBeforeFirstToken,
|
||||
UnaryExpression: checkSpacingBeforeFirstToken,
|
||||
YieldExpression: checkSpacingBeforeFirstToken,
|
||||
ImportSpecifier(node) {
|
||||
if (node.imported.range[0] !== node.local.range[0]) checkSpacingBefore(sourceCode.getTokenBefore(node.local), PREV_TOKEN_M);
|
||||
},
|
||||
ExportSpecifier(node) {
|
||||
if (node.local.range[0] !== node.exported.range[0]) {
|
||||
const asToken = sourceCode.getTokenBefore(node.exported);
|
||||
checkSpacingBefore(asToken, PREV_TOKEN_M);
|
||||
checkSpacingAfter(asToken, NEXT_TOKEN_M);
|
||||
}
|
||||
},
|
||||
ImportNamespaceSpecifier(node) {
|
||||
checkSpacingBefore(sourceCode.getFirstToken(node, 1), PREV_TOKEN_M);
|
||||
},
|
||||
MethodDefinition: checkSpacingForProperty,
|
||||
PropertyDefinition: checkSpacingForProperty,
|
||||
AccessorProperty: checkSpacingForProperty,
|
||||
StaticBlock: checkSpacingAroundFirstToken,
|
||||
Property: checkSpacingForProperty,
|
||||
BinaryExpression(node) {
|
||||
if (node.operator !== ">") return;
|
||||
const operatorToken = sourceCode.getTokenBefore(node.right, import_ast_utils.isNotOpeningParenToken);
|
||||
tokensToIgnore.add(operatorToken);
|
||||
},
|
||||
TSAsExpression(node) {
|
||||
checkSpacingAround(sourceCode.getTokenAfter(node.expression, (token) => token.value === "as"));
|
||||
},
|
||||
TSSatisfiesExpression(node) {
|
||||
checkSpacingAround(sourceCode.getTokenAfter(node.expression, (token) => token.value === "satisfies"));
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { keyword_spacing_default as t };
|
||||
Generated
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
import { Q as warnDeprecatedOptions, f as createRule, g as import_ast_utils, v as COMMENTS_IGNORE_PATTERN } from "../utils.js";
|
||||
var line_comment_position_default = createRule({
|
||||
name: "line-comment-position",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce position of line comments" },
|
||||
schema: [{ oneOf: [{
|
||||
type: "string",
|
||||
enum: ["above", "beside"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
position: {
|
||||
type: "string",
|
||||
enum: ["above", "beside"]
|
||||
},
|
||||
ignorePattern: { type: "string" },
|
||||
applyDefaultPatterns: { type: "boolean" },
|
||||
applyDefaultIgnorePatterns: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}] }],
|
||||
defaultOptions: ["above"],
|
||||
messages: {
|
||||
above: "Expected comment to be above code.",
|
||||
beside: "Expected comment to be beside code."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
if (typeof options !== "string") warnDeprecatedOptions(options, "applyDefaultPatterns", "applyDefaultIgnorePatterns", "line-comment-position");
|
||||
const { position = "above", ignorePattern, applyDefaultPatterns = true, applyDefaultIgnorePatterns = applyDefaultPatterns } = typeof options === "string" ? { position: options } : options;
|
||||
const above = position === "above";
|
||||
const customIgnoreRegExp = ignorePattern ? new RegExp(ignorePattern, "u") : null;
|
||||
const defaultIgnoreRegExp = COMMENTS_IGNORE_PATTERN;
|
||||
const fallThroughRegExp = /^\s*falls?\s?through/u;
|
||||
const sourceCode = context.sourceCode;
|
||||
return { Program() {
|
||||
sourceCode.getAllComments().forEach((node) => {
|
||||
if (node.type !== "Line") return;
|
||||
if (applyDefaultIgnorePatterns && (defaultIgnoreRegExp.test(node.value) || fallThroughRegExp.test(node.value))) return;
|
||||
if (customIgnoreRegExp?.test(node.value)) return;
|
||||
const previous = sourceCode.getTokenBefore(node, { includeComments: true });
|
||||
const isOnSameLine = previous && (0, import_ast_utils.isTokenOnSameLine)(previous, node);
|
||||
if (above) {
|
||||
if (isOnSameLine) context.report({
|
||||
node,
|
||||
messageId: "above"
|
||||
});
|
||||
} else if (!isOnSameLine) context.report({
|
||||
node,
|
||||
messageId: "beside"
|
||||
});
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { line_comment_position_default as t };
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { c as hasLinesAndGetLocFromIndex, f as createRule, u as isTextSourceCode } from "../utils.js";
|
||||
var linebreak_style_default = createRule({
|
||||
name: "linebreak-style",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent linebreak style" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["unix", "windows"]
|
||||
}],
|
||||
defaultOptions: ["unix"],
|
||||
messages: {
|
||||
expectedLF: "Expected linebreaks to be 'LF' but found 'CRLF'.",
|
||||
expectedCRLF: "Expected linebreaks to be 'CRLF' but found 'LF'."
|
||||
}
|
||||
},
|
||||
create(context, [linebreakStyle]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
if (!isTextSourceCode(sourceCode) || !hasLinesAndGetLocFromIndex(sourceCode)) return {};
|
||||
const source = sourceCode.text;
|
||||
const lines = sourceCode.lines;
|
||||
const expectedLF = linebreakStyle === "unix";
|
||||
const expectedLFChars = expectedLF ? "\n" : "\r\n";
|
||||
let currentIndex = 0;
|
||||
for (const line of lines.slice(0, -1)) {
|
||||
const startIndex = currentIndex + line.length;
|
||||
const startLoc = sourceCode.getLocFromIndex(startIndex);
|
||||
let endIndex = startIndex + 1;
|
||||
let endLoc = sourceCode.getLocFromIndex(endIndex);
|
||||
while (endLoc.line === startLoc.line && endIndex < source.length) {
|
||||
endIndex++;
|
||||
endLoc = sourceCode.getLocFromIndex(endIndex);
|
||||
}
|
||||
if (source.slice(startIndex, endIndex) !== expectedLFChars) context.report({
|
||||
loc: {
|
||||
start: startLoc,
|
||||
end: endLoc
|
||||
},
|
||||
messageId: expectedLF ? "expectedLF" : "expectedCRLF",
|
||||
fix: (fixer) => fixer.replaceTextRange([startIndex, endIndex], expectedLFChars)
|
||||
});
|
||||
currentIndex = endIndex;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
});
|
||||
export { linebreak_style_default as t };
|
||||
Generated
Vendored
+186
@@ -0,0 +1,186 @@
|
||||
import { L as isHashbangComment, f as createRule, g as import_ast_utils, h as AST_TOKEN_TYPES, m as AST_NODE_TYPES, v as COMMENTS_IGNORE_PATTERN } from "../utils.js";
|
||||
function getEmptyLineNums(lines) {
|
||||
const emptyLines = [];
|
||||
lines.forEach((line, i) => {
|
||||
if (!line.trim()) emptyLines.push(i + 1);
|
||||
});
|
||||
return emptyLines;
|
||||
}
|
||||
function getCommentLineNums(comments) {
|
||||
const lines = [];
|
||||
comments.forEach((token) => {
|
||||
const start = token.loc.start.line;
|
||||
const end = token.loc.end.line;
|
||||
lines.push(start, end);
|
||||
});
|
||||
return lines;
|
||||
}
|
||||
const ALLOW_TARGETS = [
|
||||
"Block",
|
||||
"Class",
|
||||
"Object",
|
||||
"Array",
|
||||
"Interface",
|
||||
"Type",
|
||||
"Enum",
|
||||
"Module"
|
||||
];
|
||||
const ALLOW_NODE_TYPES = {
|
||||
Block: [
|
||||
AST_NODE_TYPES.ClassBody,
|
||||
AST_NODE_TYPES.BlockStatement,
|
||||
AST_NODE_TYPES.StaticBlock,
|
||||
AST_NODE_TYPES.SwitchCase,
|
||||
AST_NODE_TYPES.SwitchStatement
|
||||
],
|
||||
Class: [AST_NODE_TYPES.ClassBody],
|
||||
Object: [AST_NODE_TYPES.ObjectExpression, AST_NODE_TYPES.ObjectPattern],
|
||||
Array: [AST_NODE_TYPES.ArrayExpression, AST_NODE_TYPES.ArrayPattern],
|
||||
Interface: [AST_NODE_TYPES.TSInterfaceBody],
|
||||
Type: [AST_NODE_TYPES.TSTypeLiteral],
|
||||
Enum: [AST_NODE_TYPES.TSEnumBody, AST_NODE_TYPES.TSEnumDeclaration],
|
||||
Module: [AST_NODE_TYPES.TSModuleBlock]
|
||||
};
|
||||
function getAllowOptionName(target, boundary) {
|
||||
return `allow${target}${boundary}`;
|
||||
}
|
||||
var lines_around_comment_default = createRule({
|
||||
name: "lines-around-comment",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require empty lines around comments" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
beforeBlockComment: { type: "boolean" },
|
||||
afterBlockComment: { type: "boolean" },
|
||||
beforeLineComment: { type: "boolean" },
|
||||
afterLineComment: { type: "boolean" },
|
||||
...Object.fromEntries(ALLOW_TARGETS.flatMap((target) => [[getAllowOptionName(target, "Start"), { type: "boolean" }], [getAllowOptionName(target, "End"), { type: "boolean" }]])),
|
||||
ignorePattern: { type: "string" },
|
||||
applyDefaultIgnorePatterns: { type: "boolean" },
|
||||
afterHashbangComment: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{ beforeBlockComment: true }],
|
||||
messages: {
|
||||
after: "Expected line after comment.",
|
||||
before: "Expected line before comment."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const normalizedOptions = options;
|
||||
const defaultIgnoreRegExp = COMMENTS_IGNORE_PATTERN;
|
||||
const { beforeBlockComment, afterBlockComment, beforeLineComment, afterLineComment, afterHashbangComment, applyDefaultIgnorePatterns, ignorePattern = "" } = normalizedOptions;
|
||||
const customIgnoreRegExp = ignorePattern ? new RegExp(ignorePattern, "u") : null;
|
||||
const sourceCode = context.sourceCode;
|
||||
const comments = sourceCode.getAllComments();
|
||||
const lines = sourceCode.lines;
|
||||
const numLines = lines.length + 1;
|
||||
const commentLines = getCommentLineNums(comments);
|
||||
const emptyLines = getEmptyLineNums(lines);
|
||||
const commentAndEmptyLines = new Set(commentLines.concat(emptyLines));
|
||||
function codeAroundComment(token) {
|
||||
let currentToken = token;
|
||||
do
|
||||
currentToken = sourceCode.getTokenBefore(currentToken, { includeComments: true });
|
||||
while (currentToken && (0, import_ast_utils.isCommentToken)(currentToken));
|
||||
if (currentToken && (0, import_ast_utils.isTokenOnSameLine)(currentToken, token)) return true;
|
||||
currentToken = token;
|
||||
do
|
||||
currentToken = sourceCode.getTokenAfter(currentToken, { includeComments: true });
|
||||
while (currentToken && (0, import_ast_utils.isCommentToken)(currentToken));
|
||||
if (currentToken && (0, import_ast_utils.isTokenOnSameLine)(token, currentToken)) return true;
|
||||
return false;
|
||||
}
|
||||
function getParentNodeOfToken(token) {
|
||||
const node = sourceCode.getNodeByRangeIndex(token.range[0]);
|
||||
if (node && node.type === "StaticBlock") {
|
||||
const openingBrace = sourceCode.getFirstToken(node, { skip: 1 });
|
||||
return openingBrace && token.range[0] >= openingBrace.range[0] ? node : null;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
function isCommentAtParentStart(token, nodeTypes) {
|
||||
const parent = getParentNodeOfToken(token);
|
||||
if (parent && (0, import_ast_utils.isNodeOfTypes)(nodeTypes)(parent)) {
|
||||
let parentStartNodeOrToken = parent;
|
||||
if (parent.type === "StaticBlock") parentStartNodeOrToken = sourceCode.getFirstToken(parent, { skip: 1 });
|
||||
else if (parent.type === "SwitchStatement") parentStartNodeOrToken = sourceCode.getTokenAfter(parent.discriminant, { filter: import_ast_utils.isOpeningBraceToken });
|
||||
return !!parentStartNodeOrToken && token.loc.start.line - parentStartNodeOrToken.loc.start.line === 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isCommentAtParentEnd(token, nodeTypes) {
|
||||
const parent = getParentNodeOfToken(token);
|
||||
return !!parent && (0, import_ast_utils.isNodeOfTypes)(nodeTypes)(parent) && parent.loc.end.line - token.loc.end.line === 1;
|
||||
}
|
||||
function isCommentAtBoundary(token, target, boundary) {
|
||||
return (boundary === "Start" ? isCommentAtParentStart : isCommentAtParentEnd)(token, ALLOW_NODE_TYPES[target]);
|
||||
}
|
||||
function isExceptionAllowed(token, boundary) {
|
||||
const blockOptionName = getAllowOptionName("Block", boundary);
|
||||
const classOptionName = getAllowOptionName("Class", boundary);
|
||||
if (normalizedOptions[blockOptionName] && isCommentAtBoundary(token, "Block", boundary) && !(normalizedOptions[classOptionName] === false && isCommentAtBoundary(token, "Class", boundary))) return true;
|
||||
return ALLOW_TARGETS.some((target) => {
|
||||
if (target === "Block") return false;
|
||||
const optionName = getAllowOptionName(target, boundary);
|
||||
return Boolean(normalizedOptions[optionName]) && isCommentAtBoundary(token, target, boundary);
|
||||
});
|
||||
}
|
||||
function checkForEmptyLine(token, { before, after }) {
|
||||
if (applyDefaultIgnorePatterns !== false && defaultIgnoreRegExp.test(token.value)) return;
|
||||
if (customIgnoreRegExp?.test(token.value)) return;
|
||||
const prevLineNum = token.loc.start.line - 1;
|
||||
const nextLineNum = token.loc.end.line + 1;
|
||||
if (prevLineNum < 1) before = false;
|
||||
if (nextLineNum >= numLines) after = false;
|
||||
if (codeAroundComment(token)) return;
|
||||
const exceptionStartAllowed = isExceptionAllowed(token, "Start");
|
||||
const exceptionEndAllowed = isExceptionAllowed(token, "End");
|
||||
const previousTokenOrComment = sourceCode.getTokenBefore(token, { includeComments: true });
|
||||
const nextTokenOrComment = sourceCode.getTokenAfter(token, { includeComments: true });
|
||||
if (!exceptionStartAllowed && before && !commentAndEmptyLines.has(prevLineNum) && !((0, import_ast_utils.isCommentToken)(previousTokenOrComment) && (0, import_ast_utils.isTokenOnSameLine)(previousTokenOrComment, token))) {
|
||||
const lineStart = token.range[0] - token.loc.start.column;
|
||||
const range = [lineStart, lineStart];
|
||||
context.report({
|
||||
node: token,
|
||||
messageId: "before",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBeforeRange(range, "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!exceptionEndAllowed && after && !commentAndEmptyLines.has(nextLineNum) && !((0, import_ast_utils.isCommentToken)(nextTokenOrComment) && (0, import_ast_utils.isTokenOnSameLine)(token, nextTokenOrComment))) context.report({
|
||||
node: token,
|
||||
messageId: "after",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(token, "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
return { Program() {
|
||||
comments.forEach((token) => {
|
||||
if (token.type === AST_TOKEN_TYPES.Line) {
|
||||
if (beforeLineComment || afterLineComment) checkForEmptyLine(token, {
|
||||
after: afterLineComment,
|
||||
before: beforeLineComment
|
||||
});
|
||||
} else if (token.type === AST_TOKEN_TYPES.Block) {
|
||||
if (beforeBlockComment || afterBlockComment) checkForEmptyLine(token, {
|
||||
after: afterBlockComment,
|
||||
before: beforeBlockComment
|
||||
});
|
||||
} else if (isHashbangComment(token)) {
|
||||
if (afterHashbangComment) checkForEmptyLine(token, {
|
||||
after: afterHashbangComment,
|
||||
before: false
|
||||
});
|
||||
}
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { lines_around_comment_default as t };
|
||||
Generated
Vendored
+151
@@ -0,0 +1,151 @@
|
||||
import { f as createRule, g as import_ast_utils, m as AST_NODE_TYPES } from "../utils.js";
|
||||
const ClassMemberTypes = {
|
||||
"*": { test: () => true },
|
||||
"field": { test: (node) => node.type === "PropertyDefinition" },
|
||||
"method": { test: (node) => node.type === "MethodDefinition" }
|
||||
};
|
||||
var lines_between_class_members_default = createRule({
|
||||
name: "lines-between-class-members",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require or disallow an empty line between class members" },
|
||||
fixable: "whitespace",
|
||||
schema: [{ anyOf: [{
|
||||
type: "object",
|
||||
properties: { enforce: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
blankLine: {
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
},
|
||||
prev: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"method",
|
||||
"field",
|
||||
"*"
|
||||
]
|
||||
},
|
||||
next: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"method",
|
||||
"field",
|
||||
"*"
|
||||
]
|
||||
}
|
||||
},
|
||||
additionalProperties: false,
|
||||
required: [
|
||||
"blankLine",
|
||||
"prev",
|
||||
"next"
|
||||
]
|
||||
},
|
||||
minItems: 1
|
||||
} },
|
||||
additionalProperties: false,
|
||||
required: ["enforce"]
|
||||
}, {
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}] }, {
|
||||
type: "object",
|
||||
properties: {
|
||||
exceptAfterSingleLine: { type: "boolean" },
|
||||
exceptAfterOverload: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: ["always", {
|
||||
exceptAfterOverload: true,
|
||||
exceptAfterSingleLine: false
|
||||
}],
|
||||
messages: {
|
||||
never: "Unexpected blank line between class members.",
|
||||
always: "Expected blank line between class members."
|
||||
}
|
||||
},
|
||||
create(context, [firstOption, secondOption]) {
|
||||
const configureList = typeof firstOption === "object" ? firstOption.enforce : [{
|
||||
blankLine: firstOption,
|
||||
prev: "*",
|
||||
next: "*"
|
||||
}];
|
||||
const { exceptAfterSingleLine } = secondOption;
|
||||
const exceptAfterOverload = secondOption?.exceptAfterOverload && (firstOption === "always" || typeof firstOption !== "string" && firstOption?.enforce.some(({ blankLine, prev, next }) => blankLine === "always" && prev !== "field" && next !== "field"));
|
||||
const sourceCode = context.sourceCode;
|
||||
function getBoundaryTokens(curNode, nextNode) {
|
||||
const lastToken = sourceCode.getLastToken(curNode);
|
||||
const prevToken = sourceCode.getTokenBefore(lastToken);
|
||||
const nextToken = sourceCode.getFirstToken(nextNode);
|
||||
return (0, import_ast_utils.isSemicolonToken)(lastToken) && !(0, import_ast_utils.isTokenOnSameLine)(prevToken, lastToken) && (0, import_ast_utils.isTokenOnSameLine)(lastToken, nextToken) ? {
|
||||
curLast: prevToken,
|
||||
nextFirst: lastToken
|
||||
} : {
|
||||
curLast: lastToken,
|
||||
nextFirst: nextToken
|
||||
};
|
||||
}
|
||||
function findLastConsecutiveTokenAfter(prevLastToken, nextFirstToken, maxLine) {
|
||||
const after = sourceCode.getTokenAfter(prevLastToken, { includeComments: true });
|
||||
if (after !== nextFirstToken && after.loc.start.line - prevLastToken.loc.end.line <= maxLine) return findLastConsecutiveTokenAfter(after, nextFirstToken, maxLine);
|
||||
return prevLastToken;
|
||||
}
|
||||
function findFirstConsecutiveTokenBefore(nextFirstToken, prevLastToken, maxLine) {
|
||||
const before = sourceCode.getTokenBefore(nextFirstToken, { includeComments: true });
|
||||
if (before !== prevLastToken && nextFirstToken.loc.start.line - before.loc.end.line <= maxLine) return findFirstConsecutiveTokenBefore(before, prevLastToken, maxLine);
|
||||
return nextFirstToken;
|
||||
}
|
||||
function hasTokenOrCommentBetween(before, after) {
|
||||
return sourceCode.getTokensBetween(before, after, { includeComments: true }).length !== 0;
|
||||
}
|
||||
function match(node, type) {
|
||||
return ClassMemberTypes[type].test(node);
|
||||
}
|
||||
function getPaddingType(prevNode, nextNode) {
|
||||
for (let i = configureList.length - 1; i >= 0; --i) {
|
||||
const configure = configureList[i];
|
||||
if (match(prevNode, configure.prev) && match(nextNode, configure.next)) return configure.blankLine;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function isOverload(node) {
|
||||
return (node.type === AST_NODE_TYPES.TSAbstractMethodDefinition || node.type === AST_NODE_TYPES.MethodDefinition) && node.value.type === AST_NODE_TYPES.TSEmptyBodyFunctionExpression;
|
||||
}
|
||||
return { ClassBody(node) {
|
||||
const body = exceptAfterOverload ? node.body.filter((node) => !isOverload(node)) : node.body;
|
||||
for (let i = 0; i < body.length - 1; i++) {
|
||||
const curFirst = sourceCode.getFirstToken(body[i]);
|
||||
const { curLast, nextFirst } = getBoundaryTokens(body[i], body[i + 1]);
|
||||
const skip = !!(0, import_ast_utils.isTokenOnSameLine)(curFirst, curLast) && exceptAfterSingleLine;
|
||||
const beforePadding = findLastConsecutiveTokenAfter(curLast, nextFirst, 1);
|
||||
const afterPadding = findFirstConsecutiveTokenBefore(nextFirst, curLast, 1);
|
||||
const isPadded = afterPadding.loc.start.line - beforePadding.loc.end.line > 1;
|
||||
const hasTokenInPadding = hasTokenOrCommentBetween(beforePadding, afterPadding);
|
||||
const curLineLastToken = findLastConsecutiveTokenAfter(curLast, nextFirst, 0);
|
||||
const paddingType = getPaddingType(body[i], body[i + 1]);
|
||||
if (paddingType === "never" && isPadded) context.report({
|
||||
node: body[i + 1],
|
||||
messageId: "never",
|
||||
fix(fixer) {
|
||||
if (hasTokenInPadding) return null;
|
||||
return fixer.replaceTextRange([beforePadding.range[1], afterPadding.range[0]], "\n");
|
||||
}
|
||||
});
|
||||
else if (paddingType === "always" && !skip && !isPadded) context.report({
|
||||
node: body[i + 1],
|
||||
messageId: "always",
|
||||
fix(fixer) {
|
||||
if (hasTokenInPadding) return null;
|
||||
return fixer.insertTextAfter(curLineLastToken, "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { lines_between_class_members_default as t };
|
||||
+356
@@ -0,0 +1,356 @@
|
||||
import { K as isSingleLine, d as safeReplaceTextBetween, f as createRule, g as import_ast_utils, m as AST_NODE_TYPES } from "../utils.js";
|
||||
var list_style_default = createRule({
|
||||
name: "list-style",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: {
|
||||
description: "Enforce consistent spacing and line break styles inside brackets.",
|
||||
experimental: true
|
||||
},
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
$defs: {
|
||||
singleLineConfig: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
spacing: {
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
},
|
||||
maxItems: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
multiLineConfig: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: { minItems: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
} }
|
||||
},
|
||||
baseConfig: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
singleLine: { $ref: "#/items/0/$defs/singleLineConfig" },
|
||||
multiline: { $ref: "#/items/0/$defs/multiLineConfig" }
|
||||
}
|
||||
},
|
||||
overrideConfig: { oneOf: [{ $ref: "#/items/0/$defs/baseConfig" }, {
|
||||
type: "string",
|
||||
enum: ["off"]
|
||||
}] }
|
||||
},
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
singleLine: { $ref: "#/items/0/$defs/singleLineConfig" },
|
||||
multiLine: { $ref: "#/items/0/$defs/multiLineConfig" },
|
||||
overrides: {
|
||||
type: "object",
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
"()": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"[]": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"{}": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"<>": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"ArrayExpression": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"ArrayPattern": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"ArrowFunctionExpression": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"CallExpression": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"ExportNamedDeclaration": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"FunctionDeclaration": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"FunctionExpression": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"IfStatement": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"ImportAttributes": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"ImportDeclaration": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"JSONArrayExpression": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"JSONObjectExpression": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"NewExpression": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"ObjectExpression": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"ObjectPattern": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"TSDeclareFunction": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"TSEnumBody": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"TSFunctionType": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"TSInterfaceBody": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"TSTupleType": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"TSTypeLiteral": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"TSTypeParameterDeclaration": { $ref: "#/items/0/$defs/overrideConfig" },
|
||||
"TSTypeParameterInstantiation": { $ref: "#/items/0/$defs/overrideConfig" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}],
|
||||
defaultOptions: [{
|
||||
singleLine: {
|
||||
spacing: "never",
|
||||
maxItems: Number.POSITIVE_INFINITY
|
||||
},
|
||||
multiLine: { minItems: 0 },
|
||||
overrides: { "{}": { singleLine: { spacing: "always" } } }
|
||||
}],
|
||||
messages: {
|
||||
shouldSpacing: `Should have space between '{{prev}}' and '{{next}}'`,
|
||||
shouldNotSpacing: `Should not have space(s) between '{{prev}}' and '{{next}}'`,
|
||||
shouldWrap: `Should have line break between '{{prev}}' and '{{next}}'`,
|
||||
shouldNotWrap: `Should not have line break(s) between '{{prev}}' and '{{next}}'`
|
||||
}
|
||||
},
|
||||
create: (context, [options]) => {
|
||||
const { sourceCode } = context;
|
||||
const { singleLine, multiLine, overrides } = options;
|
||||
const _resolvedOptions = /* @__PURE__ */ new Map();
|
||||
function resolveOption(parenType, nodeType) {
|
||||
if (_resolvedOptions.has(nodeType)) return _resolvedOptions.get(nodeType);
|
||||
let overridesByParen = overrides?.[parenType];
|
||||
let overridesByNode = overrides?.[nodeType];
|
||||
let resolved;
|
||||
if (overridesByNode === "off" || overridesByNode === void 0 && overridesByParen === "off") resolved = "off";
|
||||
else {
|
||||
overridesByParen = typeof overridesByParen === "object" ? overridesByParen : {};
|
||||
overridesByNode ??= {};
|
||||
resolved = {
|
||||
singleLine: {
|
||||
...singleLine,
|
||||
...overridesByParen.singleLine,
|
||||
...overridesByNode.singleLine
|
||||
},
|
||||
multiline: {
|
||||
...multiLine,
|
||||
...overridesByParen.multiline,
|
||||
...overridesByNode.multiline
|
||||
}
|
||||
};
|
||||
}
|
||||
_resolvedOptions.set(nodeType, resolved);
|
||||
return resolved;
|
||||
}
|
||||
function getDelimiter(root, current) {
|
||||
if (root.type !== "TSInterfaceBody" && root.type !== "TSTypeLiteral") return;
|
||||
return current.value.match(/(?:,|;)$/) ? void 0 : ",";
|
||||
}
|
||||
function checkSpacing(node, left, right, config) {
|
||||
const shouldSpace = config.singleLine.spacing === "always";
|
||||
const firstToken = sourceCode.getTokenAfter(left, { includeComments: true });
|
||||
const lastToken = sourceCode.getTokenBefore(right, { includeComments: true });
|
||||
function doCheck(prev, next) {
|
||||
const spaced = sourceCode.isSpaceBetween(prev, next);
|
||||
if (!spaced && shouldSpace) context.report({
|
||||
node,
|
||||
messageId: "shouldSpacing",
|
||||
loc: {
|
||||
start: prev.loc.end,
|
||||
end: next.loc.start
|
||||
},
|
||||
data: {
|
||||
prev: prev.value,
|
||||
next: next.value
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(prev, " ");
|
||||
}
|
||||
});
|
||||
else if (spaced && !shouldSpace) context.report({
|
||||
node,
|
||||
messageId: "shouldNotSpacing",
|
||||
loc: {
|
||||
start: prev.loc.end,
|
||||
end: next.loc.start
|
||||
},
|
||||
data: {
|
||||
prev: prev.value,
|
||||
next: next.value
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([prev.range[1], next.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
doCheck(left, firstToken);
|
||||
doCheck(lastToken, right);
|
||||
}
|
||||
function checkWrap(node, items, left, right, config) {
|
||||
const len = items.length;
|
||||
const needWrap = isSingleLine(node) ? len > config.singleLine.maxItems : len >= config.multiline.minItems && !(0, import_ast_utils.isTokenOnSameLine)(left, items[0] ?? sourceCode.getTokenAfter(left));
|
||||
function doCheck(prev, next) {
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(prev, next)) {
|
||||
if (!needWrap) return;
|
||||
context.report({
|
||||
node,
|
||||
messageId: "shouldWrap",
|
||||
loc: {
|
||||
start: prev.loc.end,
|
||||
end: next.loc.start
|
||||
},
|
||||
data: {
|
||||
prev: prev.value,
|
||||
next: next.value
|
||||
},
|
||||
fix(fixer) {
|
||||
if (sourceCode.commentsExistBetween(prev, next)) return null;
|
||||
return fixer.insertTextBefore(next, "\n");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (needWrap) return;
|
||||
context.report({
|
||||
node,
|
||||
messageId: "shouldNotWrap",
|
||||
loc: {
|
||||
start: prev.loc.end,
|
||||
end: next.loc.start
|
||||
},
|
||||
data: {
|
||||
prev: prev.value,
|
||||
next: next.value
|
||||
},
|
||||
fix: safeReplaceTextBetween(sourceCode, prev, next, () => items.length === 1 ? "" : getDelimiter(node, prev) ?? "")
|
||||
});
|
||||
}
|
||||
}
|
||||
const tokenAfterLeft = sourceCode.getTokenAfter(left, { includeComments: false });
|
||||
doCheck(left, tokenAfterLeft);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const currentItem = items[i];
|
||||
if (!currentItem) continue;
|
||||
const currentFirstToken = sourceCode.getFirstToken(currentItem);
|
||||
if (i === 0 && tokenAfterLeft === currentFirstToken) continue;
|
||||
doCheck(sourceCode.getTokenBefore(currentItem, {
|
||||
filter: (token) => (0, import_ast_utils.isNotOpeningParenToken)(token) || token === left,
|
||||
includeComments: false
|
||||
}), currentFirstToken);
|
||||
}
|
||||
doCheck(sourceCode.getTokenBefore(right, { includeComments: false }), right);
|
||||
}
|
||||
const parenMatchers = {
|
||||
"[]": {
|
||||
left: import_ast_utils.isOpeningBracketToken,
|
||||
right: import_ast_utils.isClosingBracketToken
|
||||
},
|
||||
"{}": {
|
||||
left: import_ast_utils.isOpeningBraceToken,
|
||||
right: import_ast_utils.isClosingBraceToken
|
||||
},
|
||||
"()": {
|
||||
left: import_ast_utils.isOpeningParenToken,
|
||||
right: import_ast_utils.isClosingParenToken
|
||||
},
|
||||
"<>": {
|
||||
left: (token) => token.value === "<",
|
||||
right: (token) => token.value === ">"
|
||||
}
|
||||
};
|
||||
function getLeftParen(node, items, type) {
|
||||
switch (node.type) {
|
||||
case AST_NODE_TYPES.CallExpression:
|
||||
case AST_NODE_TYPES.NewExpression: return sourceCode.getTokenAfter(node.typeArguments ?? node.callee, import_ast_utils.isOpeningParenToken);
|
||||
case AST_NODE_TYPES.ArrayExpression:
|
||||
case AST_NODE_TYPES.ArrayPattern: return sourceCode.getFirstToken(node);
|
||||
default: {
|
||||
const maybeLeft = sourceCode.getTokenBefore(items[0]);
|
||||
const { left: matcher } = parenMatchers[type];
|
||||
return maybeLeft && matcher(maybeLeft) ? maybeLeft : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
function getRightParen(node, items, type) {
|
||||
switch (node.type) {
|
||||
case AST_NODE_TYPES.ArrayExpression:
|
||||
case AST_NODE_TYPES.ArrayPattern: return sourceCode.getLastToken(node);
|
||||
default: {
|
||||
const maybeRight = sourceCode.getTokenAfter(items.at(-1), import_ast_utils.isNotCommaToken);
|
||||
const { right: matcher } = parenMatchers[type];
|
||||
return maybeRight && matcher(maybeRight) ? maybeRight : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
function check(parenType, node, items) {
|
||||
if (items.length === 0) return;
|
||||
const left = getLeftParen(node, items, parenType);
|
||||
const right = getRightParen(node, items, parenType);
|
||||
if (!left || !right) return;
|
||||
const config = resolveOption(parenType, items[0]?.type === "ImportAttribute" ? "ImportAttributes" : node.type);
|
||||
if (config === "off") return;
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(left, right) && items.length <= config.singleLine.maxItems) checkSpacing(node, left, right, config);
|
||||
else checkWrap(node, items, left, right, config);
|
||||
}
|
||||
return {
|
||||
ArrayExpression(node) {
|
||||
check("[]", node, node.elements);
|
||||
},
|
||||
ArrayPattern(node) {
|
||||
check("[]", node, node.elements);
|
||||
},
|
||||
ArrowFunctionExpression(node) {
|
||||
check("()", node, node.params);
|
||||
},
|
||||
CallExpression(node) {
|
||||
check("()", node, node.arguments);
|
||||
},
|
||||
ExportAllDeclaration(node) {
|
||||
if (node.attributes) check("{}", node, node.attributes);
|
||||
},
|
||||
ExportNamedDeclaration(node) {
|
||||
check("{}", node, node.specifiers);
|
||||
if (node.attributes) check("{}", node, node.attributes);
|
||||
},
|
||||
FunctionDeclaration(node) {
|
||||
check("()", node, node.params);
|
||||
},
|
||||
FunctionExpression(node) {
|
||||
check("()", node, node.params);
|
||||
},
|
||||
IfStatement: (node) => {
|
||||
check("()", node, [node.test]);
|
||||
},
|
||||
ImportDeclaration(node) {
|
||||
check("{}", node, node.specifiers.filter((specifier) => specifier.type === "ImportSpecifier"));
|
||||
if (node.attributes) check("{}", node, node.attributes);
|
||||
},
|
||||
JSONArrayExpression(node) {
|
||||
check("[]", node, node.elements);
|
||||
},
|
||||
JSONObjectExpression(node) {
|
||||
check("{}", node, node.properties);
|
||||
},
|
||||
NewExpression(node) {
|
||||
check("()", node, node.arguments);
|
||||
},
|
||||
ObjectExpression(node) {
|
||||
check("{}", node, node.properties);
|
||||
},
|
||||
ObjectPattern(node) {
|
||||
check("{}", node, node.properties);
|
||||
},
|
||||
TSDeclareFunction(node) {
|
||||
check("()", node, node.params);
|
||||
},
|
||||
TSEnumBody(node) {
|
||||
check("{}", node, node.members);
|
||||
},
|
||||
TSFunctionType(node) {
|
||||
check("()", node, node.params);
|
||||
},
|
||||
TSInterfaceBody(node) {
|
||||
check("{}", node, node.body);
|
||||
},
|
||||
TSTupleType(node) {
|
||||
check("[]", node, node.elementTypes);
|
||||
},
|
||||
TSTypeLiteral(node) {
|
||||
check("{}", node, node.members);
|
||||
},
|
||||
TSTypeParameterDeclaration(node) {
|
||||
check("<>", node, node.params);
|
||||
},
|
||||
TSTypeParameterInstantiation(node) {
|
||||
check("<>", node, node.params);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { list_style_default as t };
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
import { K as isSingleLine, f as createRule } from "../utils.js";
|
||||
const OPTIONS_SCHEMA = {
|
||||
type: "object",
|
||||
properties: {
|
||||
code: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
},
|
||||
comments: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
},
|
||||
tabWidth: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
},
|
||||
ignorePattern: { type: "string" },
|
||||
ignoreComments: { type: "boolean" },
|
||||
ignoreStrings: { type: "boolean" },
|
||||
ignoreUrls: { type: "boolean" },
|
||||
ignoreTemplateLiterals: { type: "boolean" },
|
||||
ignoreRegExpLiterals: { type: "boolean" },
|
||||
ignoreTrailingComments: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
};
|
||||
const OPTIONS_OR_INTEGER_SCHEMA = { anyOf: [OPTIONS_SCHEMA, {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
}] };
|
||||
var max_len_default = createRule({
|
||||
name: "max-len",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce a maximum line length" },
|
||||
schema: [
|
||||
OPTIONS_OR_INTEGER_SCHEMA,
|
||||
OPTIONS_OR_INTEGER_SCHEMA,
|
||||
OPTIONS_SCHEMA
|
||||
],
|
||||
defaultOptions: [{
|
||||
code: 80,
|
||||
tabWidth: 4
|
||||
}],
|
||||
messages: {
|
||||
max: "This line has a length of {{lineLength}}. Maximum allowed is {{maxLength}}.",
|
||||
maxComment: "This line has a comment length of {{lineLength}}. Maximum allowed is {{maxCommentLength}}."
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
const URL_REGEXP = /[^:/?#]:\/\/[^?#]/u;
|
||||
const sourceCode = context.sourceCode;
|
||||
function computeLineLength(line, tabWidth) {
|
||||
let extraCharacterCount = 0;
|
||||
line.replace(/\t/gu, (_, offset) => {
|
||||
const totalOffset = offset + extraCharacterCount;
|
||||
const spaceCount = tabWidth - (tabWidth ? totalOffset % tabWidth : 0);
|
||||
extraCharacterCount += spaceCount - 1;
|
||||
return "";
|
||||
});
|
||||
return Array.from(line).length + extraCharacterCount;
|
||||
}
|
||||
const options = Object.assign({}, context.options[context.options.length - 1]);
|
||||
if (typeof context.options[0] === "number") options.code = context.options[0];
|
||||
if (typeof context.options[1] === "number") options.tabWidth = context.options[1];
|
||||
const maxLength = typeof options.code === "number" ? options.code : 80;
|
||||
const tabWidth = typeof options.tabWidth === "number" ? options.tabWidth : 4;
|
||||
const ignoreComments = !!options.ignoreComments;
|
||||
const ignoreStrings = !!options.ignoreStrings;
|
||||
const ignoreTemplateLiterals = !!options.ignoreTemplateLiterals;
|
||||
const ignoreRegExpLiterals = !!options.ignoreRegExpLiterals;
|
||||
const ignoreTrailingComments = !!options.ignoreTrailingComments || !!options.ignoreComments;
|
||||
const ignoreUrls = !!options.ignoreUrls;
|
||||
const maxCommentLength = options.comments;
|
||||
let ignorePattern = null;
|
||||
if (options.ignorePattern) ignorePattern = new RegExp(options.ignorePattern, "u");
|
||||
function isTrailingComment(line, lineNumber, comment) {
|
||||
return comment && comment.loc.start.line === lineNumber && lineNumber <= comment.loc.end.line && (comment.loc.end.line > lineNumber || comment.loc.end.column === line.length);
|
||||
}
|
||||
function isFullLineComment(line, lineNumber, comment) {
|
||||
const start = comment.loc.start;
|
||||
const end = comment.loc.end;
|
||||
const isFirstTokenOnLine = !line.slice(0, comment.loc.start.column).trim();
|
||||
return comment && (start.line < lineNumber || start.line === lineNumber && isFirstTokenOnLine) && (end.line > lineNumber || end.line === lineNumber && end.column === line.length);
|
||||
}
|
||||
function isJSXEmptyExpressionInSingleLineContainer(node) {
|
||||
if (!node || !node.parent || node.type !== "JSXEmptyExpression" || node.parent.type !== "JSXExpressionContainer") return false;
|
||||
const parent = node.parent;
|
||||
return isSingleLine(parent);
|
||||
}
|
||||
function stripTrailingComment(line, comment) {
|
||||
return line.slice(0, comment.loc.start.column).replace(/\s+$/u, "");
|
||||
}
|
||||
function ensureArrayAndPush(object, key, value) {
|
||||
if (!Array.isArray(object[key])) object[key] = [];
|
||||
object[key].push(value);
|
||||
}
|
||||
function getAllStrings() {
|
||||
return sourceCode.ast.tokens.filter((token) => token.type === "String" || token.type === "JSXText" && sourceCode.getNodeByRangeIndex(token.range[0] - 1).type === "JSXAttribute");
|
||||
}
|
||||
function getAllTemplateLiterals() {
|
||||
return sourceCode.ast.tokens.filter((token) => token.type === "Template");
|
||||
}
|
||||
function getAllRegExpLiterals() {
|
||||
return sourceCode.ast.tokens.filter((token) => token.type === "RegularExpression");
|
||||
}
|
||||
function groupArrayByLineNumber(arr) {
|
||||
const obj = {};
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const node = arr[i];
|
||||
for (let j = node.loc.start.line; j <= node.loc.end.line; ++j) ensureArrayAndPush(obj, j, node);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
function getAllComments() {
|
||||
const comments = [];
|
||||
sourceCode.getAllComments().forEach((commentNode) => {
|
||||
const containingNode = sourceCode.getNodeByRangeIndex(commentNode.range[0]);
|
||||
if (isJSXEmptyExpressionInSingleLineContainer(containingNode)) {
|
||||
if (comments[comments.length - 1] !== containingNode.parent) comments.push(containingNode.parent);
|
||||
} else comments.push(commentNode);
|
||||
});
|
||||
return comments;
|
||||
}
|
||||
function checkProgramForMaxLength(node) {
|
||||
const lines = sourceCode.lines;
|
||||
const comments = ignoreComments || maxCommentLength || ignoreTrailingComments ? getAllComments() : [];
|
||||
let commentsIndex = 0;
|
||||
const stringsByLine = groupArrayByLineNumber(getAllStrings());
|
||||
const templateLiteralsByLine = groupArrayByLineNumber(getAllTemplateLiterals());
|
||||
const regExpLiteralsByLine = groupArrayByLineNumber(getAllRegExpLiterals());
|
||||
lines.forEach((line, i) => {
|
||||
const lineNumber = i + 1;
|
||||
let lineIsComment = false;
|
||||
let textToMeasure;
|
||||
if (commentsIndex < comments.length) {
|
||||
let comment = null;
|
||||
do
|
||||
comment = comments[++commentsIndex];
|
||||
while (comment && comment.loc.start.line <= lineNumber);
|
||||
comment = comments[--commentsIndex];
|
||||
if (isFullLineComment(line, lineNumber, comment)) {
|
||||
lineIsComment = true;
|
||||
textToMeasure = line;
|
||||
} else if (ignoreTrailingComments && isTrailingComment(line, lineNumber, comment)) {
|
||||
textToMeasure = stripTrailingComment(line, comment);
|
||||
let lastIndex = commentsIndex;
|
||||
while (isTrailingComment(textToMeasure, lineNumber, comments[--lastIndex])) textToMeasure = stripTrailingComment(textToMeasure, comments[lastIndex]);
|
||||
} else textToMeasure = line;
|
||||
} else textToMeasure = line;
|
||||
if (ignorePattern && ignorePattern.test(textToMeasure) || ignoreUrls && URL_REGEXP.test(textToMeasure) || ignoreStrings && stringsByLine[lineNumber] || ignoreTemplateLiterals && templateLiteralsByLine[lineNumber] || ignoreRegExpLiterals && regExpLiteralsByLine[lineNumber]) return;
|
||||
const lineLength = computeLineLength(textToMeasure, tabWidth);
|
||||
const commentLengthApplies = lineIsComment && maxCommentLength;
|
||||
if (lineIsComment && ignoreComments) return;
|
||||
const loc = {
|
||||
start: {
|
||||
line: lineNumber,
|
||||
column: 0
|
||||
},
|
||||
end: {
|
||||
line: lineNumber,
|
||||
column: textToMeasure.length
|
||||
}
|
||||
};
|
||||
if (commentLengthApplies) {
|
||||
if (lineLength > maxCommentLength) context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId: "maxComment",
|
||||
data: {
|
||||
lineLength,
|
||||
maxCommentLength
|
||||
}
|
||||
});
|
||||
} else if (lineLength > maxLength) context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId: "max",
|
||||
data: {
|
||||
lineLength,
|
||||
maxLength
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
return { Program: checkProgramForMaxLength };
|
||||
}
|
||||
});
|
||||
export { max_len_default as t };
|
||||
Generated
Vendored
+102
@@ -0,0 +1,102 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
const listeningNodes = [
|
||||
"BreakStatement",
|
||||
"ClassDeclaration",
|
||||
"ContinueStatement",
|
||||
"DebuggerStatement",
|
||||
"DoWhileStatement",
|
||||
"ExpressionStatement",
|
||||
"ForInStatement",
|
||||
"ForOfStatement",
|
||||
"ForStatement",
|
||||
"FunctionDeclaration",
|
||||
"IfStatement",
|
||||
"ImportDeclaration",
|
||||
"LabeledStatement",
|
||||
"ReturnStatement",
|
||||
"SwitchStatement",
|
||||
"ThrowStatement",
|
||||
"TryStatement",
|
||||
"VariableDeclaration",
|
||||
"WhileStatement",
|
||||
"WithStatement",
|
||||
"ExportNamedDeclaration",
|
||||
"ExportDefaultDeclaration",
|
||||
"ExportAllDeclaration"
|
||||
];
|
||||
var max_statements_per_line_default = createRule({
|
||||
name: "max-statements-per-line",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce a maximum number of statements allowed per line" },
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
max: {
|
||||
type: "integer",
|
||||
minimum: 1
|
||||
},
|
||||
ignoredNodes: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
enum: listeningNodes
|
||||
}
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{ max: 1 }],
|
||||
messages: { exceed: "This line has {{numberOfStatementsOnThisLine}} {{statements}}. Maximum allowed is {{maxStatementsPerLine}}." }
|
||||
},
|
||||
create(context, [options]) {
|
||||
const { max: maxStatementsPerLine = 1, ignoredNodes = [] } = options;
|
||||
const sourceCode = context.sourceCode;
|
||||
let lastStatementLine = 0;
|
||||
let numberOfStatementsOnThisLine = 0;
|
||||
let firstExtraStatement = null;
|
||||
const SINGLE_CHILD_ALLOWED = /^(?:(?:DoWhile|For|ForIn|ForOf|If|Labeled|While)Statement|Export(?:Default|Named)Declaration)$/u;
|
||||
function reportFirstExtraStatementAndClear() {
|
||||
if (firstExtraStatement) context.report({
|
||||
node: firstExtraStatement,
|
||||
messageId: "exceed",
|
||||
data: {
|
||||
numberOfStatementsOnThisLine,
|
||||
maxStatementsPerLine,
|
||||
statements: numberOfStatementsOnThisLine === 1 ? "statement" : "statements"
|
||||
}
|
||||
});
|
||||
firstExtraStatement = null;
|
||||
}
|
||||
function getActualLastToken(node) {
|
||||
return sourceCode.getLastToken(node, import_ast_utils.isNotSemicolonToken);
|
||||
}
|
||||
function enterStatement(node) {
|
||||
const line = node.loc.start.line;
|
||||
if (node.parent && SINGLE_CHILD_ALLOWED.test(node.parent.type) && (!("alternate" in node.parent) || node.parent.alternate !== node)) return;
|
||||
if (line === lastStatementLine) numberOfStatementsOnThisLine += 1;
|
||||
else {
|
||||
reportFirstExtraStatementAndClear();
|
||||
numberOfStatementsOnThisLine = 1;
|
||||
lastStatementLine = line;
|
||||
}
|
||||
if (numberOfStatementsOnThisLine === maxStatementsPerLine + 1) firstExtraStatement = firstExtraStatement || node;
|
||||
}
|
||||
function leaveStatement(node) {
|
||||
const line = getActualLastToken(node).loc.end.line;
|
||||
if (line !== lastStatementLine) {
|
||||
reportFirstExtraStatementAndClear();
|
||||
numberOfStatementsOnThisLine = 1;
|
||||
lastStatementLine = line;
|
||||
}
|
||||
}
|
||||
const listeners = { "Program:exit": reportFirstExtraStatementAndClear };
|
||||
for (const node of listeningNodes) {
|
||||
if (ignoredNodes.includes(node)) continue;
|
||||
listeners[node] = enterStatement;
|
||||
listeners[`${node}:exit`] = leaveStatement;
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
});
|
||||
export { max_statements_per_line_default as t };
|
||||
Generated
Vendored
+186
@@ -0,0 +1,186 @@
|
||||
import { K as isSingleLine, f as createRule, m as AST_NODE_TYPES, p as deepMerge } from "../utils.js";
|
||||
function isLastTokenEndOfLine(token, line) {
|
||||
return token.loc.start.column === line.length - 1;
|
||||
}
|
||||
function isCommentsEndOfLine(token, comments, line) {
|
||||
if (!comments) return false;
|
||||
if (comments.loc.end.line > token.loc.end.line) return true;
|
||||
return comments.loc.end.column === line.length;
|
||||
}
|
||||
function makeFixFunction({ optsNone, optsSemi, lastToken, commentsAfterLastToken, missingDelimiter, lastTokenLine, isSingleLine }) {
|
||||
if (optsNone && !isLastTokenEndOfLine(lastToken, lastTokenLine) && !isCommentsEndOfLine(lastToken, commentsAfterLastToken, lastTokenLine) && !isSingleLine) return;
|
||||
return (fixer) => {
|
||||
if (optsNone) return fixer.remove(lastToken);
|
||||
const token = optsSemi ? ";" : ",";
|
||||
if (missingDelimiter) return fixer.insertTextAfter(lastToken, token);
|
||||
return fixer.replaceText(lastToken, token);
|
||||
};
|
||||
}
|
||||
const BASE_SCHEMA = {
|
||||
type: "object",
|
||||
properties: {
|
||||
multiline: {
|
||||
type: "object",
|
||||
properties: {
|
||||
delimiter: { $ref: "#/items/0/$defs/multiLineOption" },
|
||||
requireLast: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
},
|
||||
singleline: {
|
||||
type: "object",
|
||||
properties: {
|
||||
delimiter: { $ref: "#/items/0/$defs/singleLineOption" },
|
||||
requireLast: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
};
|
||||
var member_delimiter_style_default = createRule({
|
||||
name: "member-delimiter-style",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require a specific member delimiter style for interfaces and type literals" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
$defs: {
|
||||
multiLineOption: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"none",
|
||||
"semi",
|
||||
"comma"
|
||||
]
|
||||
},
|
||||
singleLineOption: {
|
||||
type: "string",
|
||||
enum: ["semi", "comma"]
|
||||
},
|
||||
delimiterConfig: BASE_SCHEMA
|
||||
},
|
||||
type: "object",
|
||||
properties: {
|
||||
...BASE_SCHEMA.properties,
|
||||
overrides: {
|
||||
type: "object",
|
||||
properties: {
|
||||
interface: { $ref: "#/items/0/$defs/delimiterConfig" },
|
||||
typeLiteral: { $ref: "#/items/0/$defs/delimiterConfig" }
|
||||
},
|
||||
additionalProperties: false
|
||||
},
|
||||
multilineDetection: {
|
||||
type: "string",
|
||||
enum: ["brackets", "last-member"]
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
multiline: {
|
||||
delimiter: "semi",
|
||||
requireLast: true
|
||||
},
|
||||
singleline: {
|
||||
delimiter: "semi",
|
||||
requireLast: false
|
||||
},
|
||||
multilineDetection: "brackets"
|
||||
}],
|
||||
messages: {
|
||||
unexpectedComma: "Unexpected separator (,).",
|
||||
unexpectedSemi: "Unexpected separator (;).",
|
||||
expectedComma: "Expected a comma.",
|
||||
expectedSemi: "Expected a semicolon."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const baseOptions = options;
|
||||
const overrides = baseOptions.overrides ?? {};
|
||||
const interfaceOptions = deepMerge(baseOptions, overrides.interface);
|
||||
const typeLiteralOptions = deepMerge(baseOptions, overrides.typeLiteral);
|
||||
function checkLastToken(member, opts, isLast) {
|
||||
function getOption(type) {
|
||||
if (isLast && !opts.requireLast) return type === "none";
|
||||
return opts.delimiter === type;
|
||||
}
|
||||
let messageId = null;
|
||||
let missingDelimiter = false;
|
||||
const lastToken = sourceCode.getLastToken(member, { includeComments: false });
|
||||
if (!lastToken) return;
|
||||
const commentsAfterLastToken = sourceCode.getCommentsAfter(lastToken).pop();
|
||||
const lastTokenLine = sourceCode.getLines()[lastToken?.loc.start.line - 1];
|
||||
const optsSemi = getOption("semi");
|
||||
const optsComma = getOption("comma");
|
||||
const optsNone = getOption("none");
|
||||
if (lastToken.value === ";") {
|
||||
if (optsComma) messageId = "expectedComma";
|
||||
else if (optsNone) {
|
||||
missingDelimiter = true;
|
||||
messageId = "unexpectedSemi";
|
||||
}
|
||||
} else if (lastToken.value === ",") {
|
||||
if (optsSemi) messageId = "expectedSemi";
|
||||
else if (optsNone) {
|
||||
missingDelimiter = true;
|
||||
messageId = "unexpectedComma";
|
||||
}
|
||||
} else if (optsSemi) {
|
||||
missingDelimiter = true;
|
||||
messageId = "expectedSemi";
|
||||
} else if (optsComma) {
|
||||
missingDelimiter = true;
|
||||
messageId = "expectedComma";
|
||||
}
|
||||
if (messageId) context.report({
|
||||
node: lastToken,
|
||||
loc: {
|
||||
start: {
|
||||
line: lastToken.loc.end.line,
|
||||
column: lastToken.loc.end.column
|
||||
},
|
||||
end: {
|
||||
line: lastToken.loc.end.line,
|
||||
column: lastToken.loc.end.column
|
||||
}
|
||||
},
|
||||
messageId,
|
||||
fix: makeFixFunction({
|
||||
optsNone,
|
||||
optsSemi,
|
||||
lastToken,
|
||||
commentsAfterLastToken,
|
||||
missingDelimiter,
|
||||
lastTokenLine,
|
||||
isSingleLine: opts.type === "single-line"
|
||||
})
|
||||
});
|
||||
}
|
||||
function checkMemberSeparatorStyle(node) {
|
||||
const members = node.type === AST_NODE_TYPES.TSInterfaceBody ? node.body : node.members;
|
||||
let _isSingleLine = isSingleLine(node);
|
||||
if (options.multilineDetection === "last-member" && !_isSingleLine && members.length > 0) {
|
||||
if (members[members.length - 1].loc.end.line === node.loc.end.line) _isSingleLine = true;
|
||||
}
|
||||
const typeOpts = node.type === AST_NODE_TYPES.TSInterfaceBody ? interfaceOptions : typeLiteralOptions;
|
||||
const opts = _isSingleLine ? {
|
||||
...typeOpts.singleline,
|
||||
type: "single-line"
|
||||
} : {
|
||||
...typeOpts.multiline,
|
||||
type: "multi-line"
|
||||
};
|
||||
members.forEach((member, index) => {
|
||||
checkLastToken(member, opts ?? {}, index === members.length - 1);
|
||||
});
|
||||
}
|
||||
return {
|
||||
TSInterfaceBody: checkMemberSeparatorStyle,
|
||||
TSTypeLiteral: checkMemberSeparatorStyle
|
||||
};
|
||||
}
|
||||
});
|
||||
export { member_delimiter_style_default as t };
|
||||
Generated
Vendored
+267
@@ -0,0 +1,267 @@
|
||||
import { C as WHITE_SPACES_PATTERN, K as isSingleLine, L as isHashbangComment, X as isWhiteSpaces, f as createRule, g as import_ast_utils, v as COMMENTS_IGNORE_PATTERN } from "../utils.js";
|
||||
var multiline_comment_style_default = createRule({
|
||||
name: "multiline-comment-style",
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
docs: { description: "Enforce a particular style for multiline comments" },
|
||||
fixable: "whitespace",
|
||||
schema: { anyOf: [{
|
||||
type: "array",
|
||||
items: [{
|
||||
enum: ["starred-block", "bare-block"],
|
||||
type: "string"
|
||||
}],
|
||||
additionalItems: false
|
||||
}, {
|
||||
type: "array",
|
||||
items: [{
|
||||
enum: ["separate-lines"],
|
||||
type: "string"
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
checkJSDoc: { type: "boolean" },
|
||||
checkExclamation: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
additionalItems: false
|
||||
}] },
|
||||
defaultOptions: ["starred-block"],
|
||||
messages: {
|
||||
expectedBlock: "Expected a block comment instead of consecutive line comments.",
|
||||
expectedBareBlock: "Expected a block comment without padding stars.",
|
||||
startNewline: "Expected a linebreak after '/*'.",
|
||||
endNewline: "Expected a linebreak before '*/'.",
|
||||
missingStar: "Expected a '*' at the start of this line.",
|
||||
alignment: "Expected this line to be aligned with the start of the comment.",
|
||||
expectedLines: "Expected multiple line comments instead of a block comment."
|
||||
}
|
||||
},
|
||||
create(context, [style, options = {}]) {
|
||||
const { checkJSDoc, checkExclamation } = options;
|
||||
const sourceCode = context.sourceCode;
|
||||
function isStarredCommentLine(line) {
|
||||
return /^\s*\*/u.test(line);
|
||||
}
|
||||
function isStarredBlockComment([firstComment]) {
|
||||
if (firstComment.type !== "Block") return false;
|
||||
const lines = firstComment.value.split(import_ast_utils.LINEBREAK_MATCHER);
|
||||
return lines.length > 0 && lines.every((line, i) => i === 0 || i === lines.length - 1 ? isWhiteSpaces(line) : isStarredCommentLine(line));
|
||||
}
|
||||
function isJSDocComment([firstComment]) {
|
||||
if (firstComment.type !== "Block") return false;
|
||||
const lines = firstComment.value.split(import_ast_utils.LINEBREAK_MATCHER);
|
||||
return /^\*\s*$/u.test(lines[0]) && lines.slice(1, -1).every((line) => /^\s* /u.test(line)) && isWhiteSpaces(lines.at(-1));
|
||||
}
|
||||
function isExclamationComment([firstComment]) {
|
||||
if (firstComment.type !== "Block") return false;
|
||||
const lines = firstComment.value.split(import_ast_utils.LINEBREAK_MATCHER);
|
||||
return /^!\s*$/u.test(lines[0]) && lines.slice(1, -1).every((line) => /^\s* /u.test(line)) && isWhiteSpaces(lines.at(-1));
|
||||
}
|
||||
function processSeparateLineComments(commentGroup) {
|
||||
const allLinesHaveLeadingSpace = commentGroup.every(({ value: line }) => line.trim().length === 0 || line.startsWith(" "));
|
||||
return commentGroup.map(({ value }) => allLinesHaveLeadingSpace ? value.replace(/^ /u, "") : value);
|
||||
}
|
||||
function processStarredBlockComment(comment) {
|
||||
const lines = comment.value.split(import_ast_utils.LINEBREAK_MATCHER).slice(1, -1).map((line) => line.replace(WHITE_SPACES_PATTERN, ""));
|
||||
const allLinesHaveLeadingSpace = lines.every((line) => {
|
||||
const lineWithoutPrefix = line.replace(/\s*\*/u, "");
|
||||
return lineWithoutPrefix.trim().length === 0 || lineWithoutPrefix.startsWith(" ");
|
||||
});
|
||||
return lines.map((line) => line.replace(allLinesHaveLeadingSpace ? /\s*\* ?/u : /\s*\*/u, ""));
|
||||
}
|
||||
function processBareBlockComment(comment) {
|
||||
const lines = comment.value.split(import_ast_utils.LINEBREAK_MATCHER).map((line) => line.replace(WHITE_SPACES_PATTERN, ""));
|
||||
const leadingWhitespace = `${sourceCode.text.slice(comment.range[0] - comment.loc.start.column, comment.range[0])} `;
|
||||
let offset = "";
|
||||
for (const [i, line] of lines.entries()) {
|
||||
if (!line.trim().length || i === 0) continue;
|
||||
const [, lineOffset] = line.match(/^(\s*\*?\s*)/u);
|
||||
if (lineOffset.length < leadingWhitespace.length) {
|
||||
const newOffset = leadingWhitespace.slice(lineOffset.length - leadingWhitespace.length);
|
||||
if (newOffset.length > offset.length) offset = newOffset;
|
||||
}
|
||||
}
|
||||
return lines.map((line) => {
|
||||
const [, lineOffset, lineContents] = line.match(/^(\s*\*?\s*)(.*)/u);
|
||||
if (lineOffset.length > leadingWhitespace.length) return `${lineOffset.slice(leadingWhitespace.length - (offset.length + lineOffset.length))}${lineContents}`;
|
||||
if (lineOffset.length < leadingWhitespace.length) return `${lineOffset.slice(leadingWhitespace.length)}${lineContents}`;
|
||||
return lineContents;
|
||||
});
|
||||
}
|
||||
function getCommentLines(commentGroup) {
|
||||
const [firstComment] = commentGroup;
|
||||
if (firstComment.type === "Line") return processSeparateLineComments(commentGroup);
|
||||
if (isStarredBlockComment(commentGroup)) return processStarredBlockComment(firstComment);
|
||||
return processBareBlockComment(firstComment);
|
||||
}
|
||||
function getInitialOffset(comment) {
|
||||
return sourceCode.text.slice(comment.range[0] - comment.loc.start.column, comment.range[0]);
|
||||
}
|
||||
function convertToStarredBlock(firstComment, commentLinesList) {
|
||||
const initialOffset = getInitialOffset(firstComment);
|
||||
return `/*\n${commentLinesList.map((line) => `${initialOffset} * ${line}`).join("\n")}\n${initialOffset} */`;
|
||||
}
|
||||
function convertToSeparateLines(firstComment, commentLinesList) {
|
||||
return commentLinesList.map((line) => `// ${line}`).join(`\n${getInitialOffset(firstComment)}`);
|
||||
}
|
||||
function convertToBlock(firstComment, commentLinesList) {
|
||||
return `/* ${commentLinesList.join(`\n${getInitialOffset(firstComment)} `)} */`;
|
||||
}
|
||||
const commentGroupCheckers = {
|
||||
"starred-block": function(commentGroup) {
|
||||
const [firstComment] = commentGroup;
|
||||
const commentLines = getCommentLines(commentGroup);
|
||||
if (commentLines.some((value) => value.includes("*/"))) return;
|
||||
if (commentGroup.length > 1) context.report({
|
||||
loc: {
|
||||
start: firstComment.loc.start,
|
||||
end: commentGroup.at(-1).loc.end
|
||||
},
|
||||
messageId: "expectedBlock",
|
||||
fix(fixer) {
|
||||
const range = [firstComment.range[0], commentGroup.at(-1).range[1]];
|
||||
return commentLines.some((value) => value.startsWith("/")) ? null : fixer.replaceTextRange(range, convertToStarredBlock(firstComment, commentLines));
|
||||
}
|
||||
});
|
||||
else {
|
||||
const lines = firstComment.value.split(import_ast_utils.LINEBREAK_MATCHER);
|
||||
const expectedLinePrefix = `${getInitialOffset(firstComment)} *`;
|
||||
if (!/^[*!]?\s*$/u.test(lines[0])) {
|
||||
const start = /^[*!]/.test(firstComment.value) ? firstComment.range[0] + 1 : firstComment.range[0];
|
||||
context.report({
|
||||
loc: {
|
||||
start: firstComment.loc.start,
|
||||
end: {
|
||||
line: firstComment.loc.start.line,
|
||||
column: firstComment.loc.start.column + 2
|
||||
}
|
||||
},
|
||||
messageId: "startNewline",
|
||||
fix: (fixer) => fixer.insertTextAfterRange([start, start + 2], `\n${expectedLinePrefix}`)
|
||||
});
|
||||
}
|
||||
if (!isWhiteSpaces(lines.at(-1))) context.report({
|
||||
loc: {
|
||||
start: {
|
||||
line: firstComment.loc.end.line,
|
||||
column: firstComment.loc.end.column - 2
|
||||
},
|
||||
end: firstComment.loc.end
|
||||
},
|
||||
messageId: "endNewline",
|
||||
fix: (fixer) => fixer.replaceTextRange([firstComment.range[1] - 2, firstComment.range[1]], `\n${expectedLinePrefix}/`)
|
||||
});
|
||||
for (let lineNumber = firstComment.loc.start.line + 1; lineNumber <= firstComment.loc.end.line; lineNumber++) {
|
||||
const lineText = sourceCode.lines[lineNumber - 1];
|
||||
const errorType = isStarredCommentLine(lineText) ? "alignment" : "missingStar";
|
||||
if (!lineText.startsWith(expectedLinePrefix)) context.report({
|
||||
loc: {
|
||||
start: {
|
||||
line: lineNumber,
|
||||
column: 0
|
||||
},
|
||||
end: {
|
||||
line: lineNumber,
|
||||
column: lineText.length
|
||||
}
|
||||
},
|
||||
messageId: errorType,
|
||||
fix(fixer) {
|
||||
const lineStartIndex = sourceCode.getIndexFromLoc({
|
||||
line: lineNumber,
|
||||
column: 0
|
||||
});
|
||||
if (errorType === "alignment") {
|
||||
const [, commentTextPrefix = ""] = lineText.match(/^(\s*\*)/u) || [];
|
||||
const commentTextStartIndex = lineStartIndex + commentTextPrefix.length;
|
||||
return fixer.replaceTextRange([lineStartIndex, commentTextStartIndex], expectedLinePrefix);
|
||||
}
|
||||
const [, commentTextPrefix = ""] = lineText.match(/^(\s*)/u) || [];
|
||||
const commentTextStartIndex = lineStartIndex + commentTextPrefix.length;
|
||||
let offset;
|
||||
for (const [idx, line] of lines.entries()) {
|
||||
if (!/\S+/u.test(line)) continue;
|
||||
const [, prefix = "", initialOffset = ""] = sourceCode.lines[firstComment.loc.start.line - 1 + idx].match(/^(\s*(?:\/?\*)?(\s*))/u) || [];
|
||||
offset = `${commentTextPrefix.slice(prefix.length)}${initialOffset}`;
|
||||
if (/^\s*\//u.test(lineText) && offset.length === 0) offset += " ";
|
||||
break;
|
||||
}
|
||||
return fixer.replaceTextRange([lineStartIndex, commentTextStartIndex], `${expectedLinePrefix}${offset}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
"separate-lines": function(commentGroup) {
|
||||
const [firstComment] = commentGroup;
|
||||
const isJSDoc = isJSDocComment(commentGroup);
|
||||
const isExclamation = isExclamationComment(commentGroup);
|
||||
if (firstComment.type !== "Block" || !checkJSDoc && isJSDoc || !checkExclamation && isExclamation) return;
|
||||
let commentLines = getCommentLines(commentGroup);
|
||||
if (isJSDoc || isExclamation) commentLines = commentLines.slice(1, commentLines.length - 1);
|
||||
const tokenAfter = sourceCode.getTokenAfter(firstComment, { includeComments: true });
|
||||
if (tokenAfter && (0, import_ast_utils.isTokenOnSameLine)(firstComment, tokenAfter)) return;
|
||||
context.report({
|
||||
loc: {
|
||||
start: firstComment.loc.start,
|
||||
end: {
|
||||
line: firstComment.loc.start.line,
|
||||
column: firstComment.loc.start.column + 2
|
||||
}
|
||||
},
|
||||
messageId: "expectedLines",
|
||||
fix(fixer) {
|
||||
return fixer.replaceText(firstComment, convertToSeparateLines(firstComment, commentLines));
|
||||
}
|
||||
});
|
||||
},
|
||||
"bare-block": function(commentGroup) {
|
||||
if (isJSDocComment(commentGroup) || isExclamationComment(commentGroup)) return;
|
||||
const [firstComment] = commentGroup;
|
||||
const commentLines = getCommentLines(commentGroup);
|
||||
if (firstComment.type === "Line" && commentLines.length > 1 && !commentLines.some((value) => value.includes("*/"))) context.report({
|
||||
loc: {
|
||||
start: firstComment.loc.start,
|
||||
end: commentGroup.at(-1).loc.end
|
||||
},
|
||||
messageId: "expectedBlock",
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange([firstComment.range[0], commentGroup.at(-1).range[1]], convertToBlock(firstComment, commentLines));
|
||||
}
|
||||
});
|
||||
if (isStarredBlockComment(commentGroup)) context.report({
|
||||
loc: {
|
||||
start: firstComment.loc.start,
|
||||
end: {
|
||||
line: firstComment.loc.start.line,
|
||||
column: firstComment.loc.start.column + 2
|
||||
}
|
||||
},
|
||||
messageId: "expectedBareBlock",
|
||||
fix(fixer) {
|
||||
return fixer.replaceText(firstComment, convertToBlock(firstComment, commentLines));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
return { Program() {
|
||||
return sourceCode.getAllComments().filter((comment) => {
|
||||
if (isHashbangComment(comment) || COMMENTS_IGNORE_PATTERN.test(comment.value)) return false;
|
||||
const tokenBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
|
||||
return !tokenBefore || tokenBefore.loc.end.line < comment.loc.start.line;
|
||||
}).reduce((commentGroups, comment, index, commentList) => {
|
||||
const tokenBefore = sourceCode.getTokenBefore(comment, { includeComments: true });
|
||||
if (comment.type === "Line" && index && commentList[index - 1].type === "Line" && tokenBefore && tokenBefore.loc.end.line === comment.loc.start.line - 1 && tokenBefore === commentList[index - 1]) commentGroups.at(-1).push(comment);
|
||||
else commentGroups.push([comment]);
|
||||
return commentGroups;
|
||||
}, []).forEach((commentGroup) => {
|
||||
if (commentGroup.length === 1 && isSingleLine(commentGroup[0])) return;
|
||||
const check = commentGroupCheckers[style];
|
||||
check(commentGroup);
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { multiline_comment_style_default as t };
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
import { K as isSingleLine, f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var multiline_ternary_default = createRule({
|
||||
name: "multiline-ternary",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce newlines between operands of ternary expressions" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"always-multiline",
|
||||
"never"
|
||||
]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: { ignoreJSX: { type: "boolean" } },
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: ["always"],
|
||||
messages: {
|
||||
expectedTestCons: "Expected newline between test and consequent of ternary expression.",
|
||||
expectedConsAlt: "Expected newline between consequent and alternate of ternary expression.",
|
||||
unexpectedTestCons: "Unexpected newline between test and consequent of ternary expression.",
|
||||
unexpectedConsAlt: "Unexpected newline between consequent and alternate of ternary expression."
|
||||
}
|
||||
},
|
||||
create(context, [style, options = {}]) {
|
||||
const multiline = style !== "never";
|
||||
const allowSingleLine = style === "always-multiline";
|
||||
const { ignoreJSX } = options;
|
||||
const sourceCode = context.sourceCode;
|
||||
return { ConditionalExpression(node) {
|
||||
const questionToken = sourceCode.getTokenAfter(node.test, import_ast_utils.isNotClosingParenToken);
|
||||
const colonToken = sourceCode.getTokenAfter(node.consequent, import_ast_utils.isNotClosingParenToken);
|
||||
const firstTokenOfTest = sourceCode.getFirstToken(node);
|
||||
const lastTokenOfTest = sourceCode.getTokenBefore(questionToken);
|
||||
const firstTokenOfConsequent = sourceCode.getTokenAfter(questionToken);
|
||||
const lastTokenOfConsequent = sourceCode.getTokenBefore(colonToken);
|
||||
const firstTokenOfAlternate = sourceCode.getTokenAfter(colonToken);
|
||||
const areTestAndConsequentOnSameLine = (0, import_ast_utils.isTokenOnSameLine)(lastTokenOfTest, firstTokenOfConsequent);
|
||||
const areConsequentAndAlternateOnSameLine = (0, import_ast_utils.isTokenOnSameLine)(lastTokenOfConsequent, firstTokenOfAlternate);
|
||||
const hasComments = !!sourceCode.getCommentsInside(node).length;
|
||||
if (ignoreJSX) {
|
||||
if (node.parent.type === "JSXElement" || node.parent.type === "JSXFragment" || node.parent.type === "JSXExpressionContainer") return null;
|
||||
}
|
||||
if (!multiline) {
|
||||
if (!areTestAndConsequentOnSameLine) context.report({
|
||||
node: node.test,
|
||||
loc: {
|
||||
start: firstTokenOfTest.loc.start,
|
||||
end: lastTokenOfTest.loc.end
|
||||
},
|
||||
messageId: "unexpectedTestCons",
|
||||
fix(fixer) {
|
||||
if (hasComments) return null;
|
||||
const fixers = [];
|
||||
const areTestAndQuestionOnSameLine = (0, import_ast_utils.isTokenOnSameLine)(lastTokenOfTest, questionToken);
|
||||
const areQuestionAndConsOnSameLine = (0, import_ast_utils.isTokenOnSameLine)(questionToken, firstTokenOfConsequent);
|
||||
if (!areTestAndQuestionOnSameLine) fixers.push(fixer.removeRange([lastTokenOfTest.range[1], questionToken.range[0]]));
|
||||
if (!areQuestionAndConsOnSameLine) fixers.push(fixer.removeRange([questionToken.range[1], firstTokenOfConsequent.range[0]]));
|
||||
return fixers;
|
||||
}
|
||||
});
|
||||
if (!areConsequentAndAlternateOnSameLine) context.report({
|
||||
node: node.consequent,
|
||||
loc: {
|
||||
start: firstTokenOfConsequent.loc.start,
|
||||
end: lastTokenOfConsequent.loc.end
|
||||
},
|
||||
messageId: "unexpectedConsAlt",
|
||||
fix(fixer) {
|
||||
if (hasComments) return null;
|
||||
const fixers = [];
|
||||
const areConsAndColonOnSameLine = (0, import_ast_utils.isTokenOnSameLine)(lastTokenOfConsequent, colonToken);
|
||||
const areColonAndAltOnSameLine = (0, import_ast_utils.isTokenOnSameLine)(colonToken, firstTokenOfAlternate);
|
||||
if (!areConsAndColonOnSameLine) fixers.push(fixer.removeRange([lastTokenOfConsequent.range[1], colonToken.range[0]]));
|
||||
if (!areColonAndAltOnSameLine) fixers.push(fixer.removeRange([colonToken.range[1], firstTokenOfAlternate.range[0]]));
|
||||
return fixers;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (allowSingleLine && isSingleLine(node)) return;
|
||||
if (areTestAndConsequentOnSameLine) context.report({
|
||||
node: node.test,
|
||||
loc: {
|
||||
start: firstTokenOfTest.loc.start,
|
||||
end: lastTokenOfTest.loc.end
|
||||
},
|
||||
messageId: "expectedTestCons",
|
||||
fix: (fixer) => hasComments ? null : fixer.replaceTextRange([lastTokenOfTest.range[1], questionToken.range[0]], "\n")
|
||||
});
|
||||
if (areConsequentAndAlternateOnSameLine) context.report({
|
||||
node: node.consequent,
|
||||
loc: {
|
||||
start: firstTokenOfConsequent.loc.start,
|
||||
end: lastTokenOfConsequent.loc.end
|
||||
},
|
||||
messageId: "expectedConsAlt",
|
||||
fix: (fixer) => hasComments ? null : fixer.replaceTextRange([lastTokenOfConsequent.range[1], colonToken.range[0]], "\n")
|
||||
});
|
||||
}
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { multiline_ternary_default as t };
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var new_parens_default = createRule({
|
||||
name: "new-parens",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce or disallow parentheses when invoking a constructor with no arguments" },
|
||||
fixable: "code",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}],
|
||||
defaultOptions: ["always"],
|
||||
messages: {
|
||||
missing: "Missing '()' invoking a constructor.",
|
||||
unnecessary: "Unnecessary '()' invoking a constructor with no arguments."
|
||||
}
|
||||
},
|
||||
create(context, [style]) {
|
||||
const always = style !== "never";
|
||||
const sourceCode = context.sourceCode;
|
||||
return { NewExpression(node) {
|
||||
if (node.arguments.length !== 0) return;
|
||||
const lastToken = sourceCode.getLastToken(node);
|
||||
const hasLastParen = lastToken && (0, import_ast_utils.isClosingParenToken)(lastToken);
|
||||
const tokenBeforeLastToken = sourceCode.getTokenBefore(lastToken);
|
||||
const hasParens = hasLastParen && (0, import_ast_utils.isOpeningParenToken)(tokenBeforeLastToken) && node.callee.range[1] < node.range[1];
|
||||
if (always) {
|
||||
if (!hasParens) context.report({
|
||||
node,
|
||||
messageId: "missing",
|
||||
fix: (fixer) => fixer.insertTextAfter(node, "()")
|
||||
});
|
||||
} else if (hasParens) context.report({
|
||||
node,
|
||||
messageId: "unnecessary",
|
||||
fix: (fixer) => [
|
||||
fixer.remove(tokenBeforeLastToken),
|
||||
fixer.remove(lastToken),
|
||||
fixer.insertTextBefore(node, "("),
|
||||
fixer.insertTextAfter(node, ")")
|
||||
]
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { new_parens_default as t };
|
||||
Generated
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
import { Z as skipChainExpression, f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var newline_per_chained_call_default = createRule({
|
||||
name: "newline-per-chained-call",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require a newline after each call in a method chain" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: { ignoreChainWithDepth: {
|
||||
type: "integer",
|
||||
minimum: 1,
|
||||
maximum: 10
|
||||
} },
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{ ignoreChainWithDepth: 2 }],
|
||||
messages: { expected: "Expected line break before `{{callee}}`." }
|
||||
},
|
||||
create(context, [options]) {
|
||||
const { ignoreChainWithDepth } = options;
|
||||
const sourceCode = context.sourceCode;
|
||||
function getPrefix(node) {
|
||||
if (node.computed) {
|
||||
if (node.optional) return "?.[";
|
||||
return "[";
|
||||
}
|
||||
if (node.optional) return "?.";
|
||||
return ".";
|
||||
}
|
||||
function getPropertyText(node) {
|
||||
const prefix = getPrefix(node);
|
||||
const lines = sourceCode.getText(node.property).split(import_ast_utils.LINEBREAK_MATCHER);
|
||||
const suffix = node.computed && lines.length === 1 ? "]" : "";
|
||||
return prefix + lines[0] + suffix;
|
||||
}
|
||||
return { "CallExpression:exit": function(node) {
|
||||
const callee = skipChainExpression(node.callee);
|
||||
if (callee.type !== "MemberExpression") return;
|
||||
let parent = skipChainExpression(callee.object);
|
||||
let depth = 1;
|
||||
while (parent && "callee" in parent && parent.callee) {
|
||||
depth += 1;
|
||||
const parentCallee = skipChainExpression(parent.callee);
|
||||
if (!("object" in parentCallee)) break;
|
||||
parent = skipChainExpression(parentCallee.object);
|
||||
}
|
||||
if (depth > ignoreChainWithDepth && (0, import_ast_utils.isTokenOnSameLine)(callee.object, callee.property)) {
|
||||
const firstTokenAfterObject = sourceCode.getTokenAfter(callee.object, import_ast_utils.isNotClosingParenToken);
|
||||
context.report({
|
||||
node: callee.property,
|
||||
loc: {
|
||||
start: firstTokenAfterObject.loc.start,
|
||||
end: callee.loc.end
|
||||
},
|
||||
messageId: "expected",
|
||||
data: { callee: getPropertyText(callee) },
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(firstTokenAfterObject, "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { newline_per_chained_call_default as t };
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import { U as isParenthesised, f as createRule } from "../utils.js";
|
||||
function isConditional(node) {
|
||||
return node.type === "ConditionalExpression";
|
||||
}
|
||||
var no_confusing_arrow_default = createRule({
|
||||
name: "no-confusing-arrow",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow arrow functions where they could be confused with comparisons" },
|
||||
fixable: "code",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
allowParens: { type: "boolean" },
|
||||
onlyOneSimpleParam: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
allowParens: true,
|
||||
onlyOneSimpleParam: false
|
||||
}],
|
||||
messages: { confusing: "Arrow function used ambiguously with a conditional expression." }
|
||||
},
|
||||
create(context, [options]) {
|
||||
const { allowParens, onlyOneSimpleParam } = options;
|
||||
const sourceCode = context.sourceCode;
|
||||
function checkArrowFunc(node) {
|
||||
const body = node.body;
|
||||
if (isConditional(body) && !(allowParens && isParenthesised(sourceCode, body)) && !(onlyOneSimpleParam && !(node.params.length === 1 && node.params[0].type === "Identifier"))) context.report({
|
||||
node,
|
||||
messageId: "confusing",
|
||||
fix(fixer) {
|
||||
return allowParens ? fixer.replaceText(node.body, `(${sourceCode.getText(node.body)})`) : null;
|
||||
}
|
||||
});
|
||||
}
|
||||
return { ArrowFunctionExpression: checkArrowFunc };
|
||||
}
|
||||
});
|
||||
export { no_confusing_arrow_default as t };
|
||||
+723
@@ -0,0 +1,723 @@
|
||||
import { A as getStaticPropertyName, B as isMixedLogicalAndCoalesceExpressions, G as isRegExpLiteral, K as isSingleLine, P as isDecimalInteger, Q as warnDeprecatedOptions, R as isJSDocComment, Y as isTopLevelExpressionStatement, Z as skipChainExpression, f as createRule, g as import_ast_utils, k as getPrecedence, m as AST_NODE_TYPES, w as canTokensBeAdjacent, z as isKeywordToken } from "../utils.js";
|
||||
const isTypeAssertion = (0, import_ast_utils.isNodeOfTypes)([
|
||||
AST_NODE_TYPES.TSAsExpression,
|
||||
AST_NODE_TYPES.TSNonNullExpression,
|
||||
AST_NODE_TYPES.TSSatisfiesExpression,
|
||||
AST_NODE_TYPES.TSTypeAssertion
|
||||
]);
|
||||
var no_extra_parens_default = createRule({
|
||||
name: "no-extra-parens",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow unnecessary parentheses" },
|
||||
fixable: "code",
|
||||
schema: { anyOf: [{
|
||||
type: "array",
|
||||
items: [{
|
||||
type: "string",
|
||||
enum: ["functions"]
|
||||
}],
|
||||
minItems: 0,
|
||||
maxItems: 1
|
||||
}, {
|
||||
type: "array",
|
||||
items: [{
|
||||
type: "string",
|
||||
enum: ["all"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
conditionalAssign: { type: "boolean" },
|
||||
ternaryOperandBinaryExpressions: { type: "boolean" },
|
||||
nestedBinaryExpressions: { type: "boolean" },
|
||||
returnAssign: { type: "boolean" },
|
||||
ignoreJSX: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"none",
|
||||
"all",
|
||||
"single-line",
|
||||
"multi-line"
|
||||
]
|
||||
},
|
||||
enforceForArrowConditionals: { type: "boolean" },
|
||||
enforceForSequenceExpressions: { type: "boolean" },
|
||||
enforceForNewInMemberExpressions: { type: "boolean" },
|
||||
enforceForFunctionPrototypeMethods: { type: "boolean" },
|
||||
allowParensAfterCommentPattern: { type: "string" },
|
||||
nestedConditionalExpressions: { type: "boolean" },
|
||||
allowNodesInSpreadElement: {
|
||||
type: "object",
|
||||
properties: {
|
||||
ConditionalExpression: { type: "boolean" },
|
||||
LogicalExpression: { type: "boolean" },
|
||||
AwaitExpression: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
},
|
||||
ignoredNodes: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
not: {
|
||||
type: "string",
|
||||
pattern: ":exit$"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
minItems: 0,
|
||||
maxItems: 2
|
||||
}] },
|
||||
defaultOptions: ["all"],
|
||||
messages: { unexpected: "Unnecessary parentheses around expression." }
|
||||
},
|
||||
create(context, [nodes, options]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const tokensToIgnore = /* @__PURE__ */ new WeakSet();
|
||||
const precedence = getPrecedence;
|
||||
const ALL_NODES = nodes !== "functions";
|
||||
const EXCEPT_COND_ASSIGN = ALL_NODES && options?.conditionalAssign === false;
|
||||
const EXCEPT_COND_TERNARY = ALL_NODES && options?.ternaryOperandBinaryExpressions === false;
|
||||
const IGNORE_NESTED_BINARY = ALL_NODES && options?.nestedBinaryExpressions === false;
|
||||
const EXCEPT_RETURN_ASSIGN = ALL_NODES && options?.returnAssign === false;
|
||||
const IGNORE_JSX = ALL_NODES && options?.ignoreJSX;
|
||||
const IGNORE_ARROW_CONDITIONALS = ALL_NODES && options?.enforceForArrowConditionals === false;
|
||||
const IGNORE_SEQUENCE_EXPRESSIONS = ALL_NODES && options?.enforceForSequenceExpressions === false;
|
||||
const IGNORE_NEW_IN_MEMBER_EXPR = ALL_NODES && options?.enforceForNewInMemberExpressions === false;
|
||||
const IGNORE_FUNCTION_PROTOTYPE_METHODS = ALL_NODES && options?.enforceForFunctionPrototypeMethods === false;
|
||||
const ALLOW_PARENS_AFTER_COMMENT_PATTERN = ALL_NODES && options?.allowParensAfterCommentPattern;
|
||||
const ALLOW_NESTED_TERNARY = ALL_NODES && options?.nestedConditionalExpressions === false;
|
||||
const ALLOW_NODES_IN_SPREAD = ALL_NODES && options && new Set(Object.entries(options.allowNodesInSpreadElement || {}).filter(([_, value]) => value).map(([key]) => key));
|
||||
warnDeprecatedOptions(options, [
|
||||
"enforceForArrowConditionals",
|
||||
"enforceForNewInMemberExpressions",
|
||||
"allowNodesInSpreadElement"
|
||||
], "ignoredNodes", "no-extra-parens");
|
||||
const PRECEDENCE_OF_ASSIGNMENT_EXPR = precedence({ type: "AssignmentExpression" });
|
||||
const PRECEDENCE_OF_UPDATE_EXPR = precedence({ type: "UpdateExpression" });
|
||||
let reportsBuffer;
|
||||
function pathToAncestor(node, ancestor) {
|
||||
const path = [node];
|
||||
let currentNode = node;
|
||||
while (currentNode !== ancestor) {
|
||||
currentNode = currentNode.parent;
|
||||
if (currentNode === null || currentNode === void 0) throw new Error("Nodes are not in the ancestor-descendant relationship.");
|
||||
path.push(currentNode);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
function pathToDescendant(node, descendant) {
|
||||
return pathToAncestor(descendant, node).reverse();
|
||||
}
|
||||
function isSafelyEnclosingInExpression(node, child) {
|
||||
switch (node.type) {
|
||||
case "ArrayExpression":
|
||||
case "ArrayPattern":
|
||||
case "BlockStatement":
|
||||
case "ObjectExpression":
|
||||
case "ObjectPattern":
|
||||
case "TemplateLiteral": return true;
|
||||
case "ArrowFunctionExpression":
|
||||
case "FunctionExpression": return node.params.includes(child);
|
||||
case "CallExpression":
|
||||
case "NewExpression": return node.arguments.includes(child);
|
||||
case "MemberExpression": return node.computed && node.property === child;
|
||||
case "ConditionalExpression": return node.consequent === child;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
function startNewReportsBuffering() {
|
||||
reportsBuffer = {
|
||||
upper: reportsBuffer,
|
||||
inExpressionNodes: [],
|
||||
reports: []
|
||||
};
|
||||
}
|
||||
function endCurrentReportsBuffering() {
|
||||
const { upper, inExpressionNodes, reports } = reportsBuffer ?? {};
|
||||
if (upper) {
|
||||
upper.inExpressionNodes.push(...inExpressionNodes ?? []);
|
||||
upper.reports.push(...reports ?? []);
|
||||
} else reports?.forEach(({ finishReport }) => finishReport());
|
||||
reportsBuffer = upper;
|
||||
}
|
||||
function isInCurrentReportsBuffer(node) {
|
||||
return reportsBuffer?.reports.some((r) => r.node === node);
|
||||
}
|
||||
function removeFromCurrentReportsBuffer(node) {
|
||||
if (reportsBuffer) reportsBuffer.reports = reportsBuffer.reports.filter((r) => r.node !== node);
|
||||
}
|
||||
function isImmediateFunctionPrototypeMethodCall(node) {
|
||||
const callNode = skipChainExpression(node);
|
||||
if (callNode.type !== "CallExpression") return false;
|
||||
const callee = skipChainExpression(callNode.callee);
|
||||
return callee.type === "MemberExpression" && callee.object.type === "FunctionExpression" && ["call", "apply"].includes(getStaticPropertyName(callee));
|
||||
}
|
||||
function ruleApplies(node) {
|
||||
if (node.type === "JSXElement" || node.type === "JSXFragment") switch (IGNORE_JSX) {
|
||||
case "all": return false;
|
||||
case "multi-line": return isSingleLine(node);
|
||||
case "single-line": return !isSingleLine(node);
|
||||
case "none": break;
|
||||
}
|
||||
if (node.type === "SequenceExpression" && IGNORE_SEQUENCE_EXPRESSIONS) return false;
|
||||
if (isImmediateFunctionPrototypeMethodCall(node) && IGNORE_FUNCTION_PROTOTYPE_METHODS) return false;
|
||||
return ALL_NODES || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
||||
}
|
||||
function isParenthesised(node) {
|
||||
return (0, import_ast_utils.isParenthesized)(1, node, sourceCode);
|
||||
}
|
||||
function isParenthesisedTwice(node) {
|
||||
return (0, import_ast_utils.isParenthesized)(2, node, sourceCode);
|
||||
}
|
||||
function hasExcessParens(node) {
|
||||
return ruleApplies(node) && isParenthesised(node);
|
||||
}
|
||||
function hasDoubleExcessParens(node) {
|
||||
return ruleApplies(node) && isParenthesisedTwice(node);
|
||||
}
|
||||
function hasExcessParensWithPrecedence(node, precedenceLowerLimit) {
|
||||
if (ruleApplies(node) && isParenthesised(node)) {
|
||||
if (precedence(node) >= precedenceLowerLimit || isParenthesisedTwice(node)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isCondAssignException(node) {
|
||||
return EXCEPT_COND_ASSIGN && node.test && node.test.type === "AssignmentExpression";
|
||||
}
|
||||
function isInReturnStatement(node) {
|
||||
for (let currentNode = node; currentNode; currentNode = currentNode.parent) if (currentNode.type === "ReturnStatement" || currentNode.type === "ArrowFunctionExpression" && currentNode.body.type !== "BlockStatement") return true;
|
||||
return false;
|
||||
}
|
||||
function isNewExpressionWithParens(newExpression) {
|
||||
const lastToken = sourceCode.getLastToken(newExpression);
|
||||
const penultimateToken = sourceCode.getTokenBefore(lastToken);
|
||||
return newExpression.arguments.length > 0 || (0, import_ast_utils.isOpeningParenToken)(penultimateToken) && (0, import_ast_utils.isClosingParenToken)(lastToken) && newExpression.callee.range[1] < newExpression.range[1];
|
||||
}
|
||||
function isMemberExpInNewCallee(node) {
|
||||
if (node.type === "MemberExpression") return node.parent.type === "NewExpression" && node.parent.callee === node ? true : "object" in node.parent && node.parent.object === node && isMemberExpInNewCallee(node.parent);
|
||||
return false;
|
||||
}
|
||||
function doesMemberExpressionContainCallExpression(node) {
|
||||
let currentNode = node.object;
|
||||
let currentNodeType = node.object.type;
|
||||
while (currentNodeType === "MemberExpression") {
|
||||
if (!("object" in currentNode)) break;
|
||||
currentNode = currentNode.object;
|
||||
currentNodeType = currentNode.type;
|
||||
}
|
||||
return currentNodeType === "CallExpression";
|
||||
}
|
||||
function containsAssignment(node) {
|
||||
if (node.type === "AssignmentExpression") return true;
|
||||
if (node.type === "ConditionalExpression" && (node.consequent.type === "AssignmentExpression" || node.alternate.type === "AssignmentExpression")) return true;
|
||||
if ("left" in node && (node.left && node.left.type === "AssignmentExpression" || node.right && node.right.type === "AssignmentExpression")) return true;
|
||||
return false;
|
||||
}
|
||||
function isReturnAssignException(node) {
|
||||
if (!EXCEPT_RETURN_ASSIGN || !isInReturnStatement(node)) return false;
|
||||
if (node.type === "ReturnStatement") return node.argument && containsAssignment(node.argument);
|
||||
if (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") return containsAssignment(node.body);
|
||||
return containsAssignment(node);
|
||||
}
|
||||
function hasExcessParensNoLineTerminator(token, node) {
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(token, node)) return hasExcessParens(node);
|
||||
return hasDoubleExcessParens(node);
|
||||
}
|
||||
function requiresLeadingSpace(node) {
|
||||
const leftParenToken = sourceCode.getTokenBefore(node);
|
||||
const tokenBeforeLeftParen = sourceCode.getTokenBefore(leftParenToken, { includeComments: true });
|
||||
const tokenAfterLeftParen = sourceCode.getTokenAfter(leftParenToken, { includeComments: true });
|
||||
return tokenBeforeLeftParen && tokenBeforeLeftParen.range[1] === leftParenToken.range[0] && leftParenToken.range[1] === tokenAfterLeftParen.range[0] && !canTokensBeAdjacent(tokenBeforeLeftParen, tokenAfterLeftParen);
|
||||
}
|
||||
function requiresTrailingSpace(node) {
|
||||
const nextTwoTokens = sourceCode.getTokensAfter(node, { count: 2 });
|
||||
const rightParenToken = nextTwoTokens[0];
|
||||
const tokenAfterRightParen = nextTwoTokens[1];
|
||||
const tokenBeforeRightParen = sourceCode.getLastToken(node);
|
||||
return rightParenToken && tokenAfterRightParen && !sourceCode.isSpaceBetween(rightParenToken, tokenAfterRightParen) && !canTokensBeAdjacent(tokenBeforeRightParen, tokenAfterRightParen);
|
||||
}
|
||||
function isIIFE(node) {
|
||||
const maybeCallNode = skipChainExpression(node);
|
||||
return maybeCallNode.type === "CallExpression" && maybeCallNode.callee.type === "FunctionExpression";
|
||||
}
|
||||
function canBeAssignmentTarget(node) {
|
||||
return !!(node && (node.type === "Identifier" || node.type === "MemberExpression"));
|
||||
}
|
||||
function isAnonymousFunctionAssignmentException({ left, operator, right }) {
|
||||
if (left.type === "Identifier" && [
|
||||
"=",
|
||||
"&&=",
|
||||
"||=",
|
||||
"??="
|
||||
].includes(operator)) {
|
||||
const rhsType = right.type;
|
||||
if (rhsType === "ArrowFunctionExpression") return true;
|
||||
if ((rhsType === "FunctionExpression" || rhsType === "ClassExpression") && !right.id) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isFixable(node) {
|
||||
if (node.type !== "Literal" || typeof node.value !== "string") return true;
|
||||
if (isParenthesisedTwice(node)) return true;
|
||||
return !isTopLevelExpressionStatement(node.parent);
|
||||
}
|
||||
function isAllowedByCommentDirective(leftParenToken) {
|
||||
const comments = sourceCode.getCommentsBefore(leftParenToken);
|
||||
if (comments.length === 0) return false;
|
||||
const lastComment = comments.at(-1);
|
||||
if (isJSDocComment(lastComment) && /@type\s*\{[^}]+\}/.test(lastComment.value)) return true;
|
||||
if (ALLOW_PARENS_AFTER_COMMENT_PATTERN) {
|
||||
if (new RegExp(ALLOW_PARENS_AFTER_COMMENT_PATTERN, "u").test(lastComment.value)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function report(node) {
|
||||
let leftParenToken = sourceCode.getTokenBefore(node);
|
||||
let rightParenToken = sourceCode.getTokenAfter(node);
|
||||
if (!isParenthesisedTwice(node)) {
|
||||
if (tokensToIgnore.has(sourceCode.getFirstToken(node))) return;
|
||||
if (isIIFE(node) && !("callee" in node && isParenthesised(node.callee))) return;
|
||||
}
|
||||
let parenLayers = 2;
|
||||
while (isAllowedByCommentDirective(leftParenToken)) {
|
||||
if (!(0, import_ast_utils.isParenthesized)(parenLayers, node, sourceCode)) return;
|
||||
leftParenToken = sourceCode.getTokenBefore(leftParenToken, import_ast_utils.isOpeningParenToken);
|
||||
rightParenToken = sourceCode.getTokenAfter(rightParenToken, import_ast_utils.isClosingParenToken);
|
||||
parenLayers++;
|
||||
}
|
||||
function finishReport() {
|
||||
context.report({
|
||||
node,
|
||||
loc: leftParenToken.loc,
|
||||
messageId: "unexpected",
|
||||
fix: isFixable(node) ? (fixer) => {
|
||||
return [fixer.replaceText(leftParenToken, requiresLeadingSpace(node) ? " " : ""), fixer.replaceText(rightParenToken, requiresTrailingSpace(node) ? " " : "")];
|
||||
} : null
|
||||
});
|
||||
}
|
||||
if (reportsBuffer) {
|
||||
reportsBuffer.reports.push({
|
||||
node,
|
||||
finishReport
|
||||
});
|
||||
return;
|
||||
}
|
||||
finishReport();
|
||||
}
|
||||
function checkArgumentWithPrecedence(node) {
|
||||
if ("argument" in node && node.argument && hasExcessParensWithPrecedence(node.argument, precedence(node))) report(node.argument);
|
||||
}
|
||||
function checkBinaryLogical(node) {
|
||||
const isLeftTypeAssertion = isTypeAssertion(node.left);
|
||||
const isRightTypeAssertion = isTypeAssertion(node.right);
|
||||
if (isLeftTypeAssertion && isRightTypeAssertion) return;
|
||||
const rule = (n) => {
|
||||
const prec = precedence(n);
|
||||
const leftPrecedence = precedence(n.left);
|
||||
const rightPrecedence = precedence(n.right);
|
||||
const isExponentiation = n.operator === "**";
|
||||
const shouldSkipLeft = IGNORE_NESTED_BINARY && (n.left.type === "BinaryExpression" || n.left.type === "LogicalExpression");
|
||||
const shouldSkipRight = IGNORE_NESTED_BINARY && (n.right.type === "BinaryExpression" || n.right.type === "LogicalExpression");
|
||||
if (!shouldSkipLeft && hasExcessParens(n.left)) {
|
||||
if (!(["AwaitExpression", "UnaryExpression"].includes(n.left.type) && isExponentiation) && !isMixedLogicalAndCoalesceExpressions(n.left, n) && !(n.parent.type === "ReturnStatement" && n.parent.loc.start.line !== n.left.loc.start.line && !isParenthesised(n)) && (leftPrecedence > prec || leftPrecedence === prec && !isExponentiation) || isParenthesisedTwice(n.left)) report(n.left);
|
||||
}
|
||||
if (!shouldSkipRight && hasExcessParens(n.right)) {
|
||||
if (!isMixedLogicalAndCoalesceExpressions(n.right, n) && (rightPrecedence > prec || rightPrecedence === prec && isExponentiation) || isParenthesisedTwice(n.right)) report(n.right);
|
||||
}
|
||||
};
|
||||
if (isLeftTypeAssertion) return rule({
|
||||
...node,
|
||||
left: {
|
||||
...node.left,
|
||||
type: AST_NODE_TYPES.SequenceExpression
|
||||
}
|
||||
});
|
||||
if (isRightTypeAssertion) return rule({
|
||||
...node,
|
||||
right: {
|
||||
...node.right,
|
||||
type: AST_NODE_TYPES.SequenceExpression
|
||||
}
|
||||
});
|
||||
return rule(node);
|
||||
}
|
||||
function checkCallNew(node) {
|
||||
const rule = (node) => {
|
||||
const callee = node.callee;
|
||||
if (hasExcessParensWithPrecedence(callee, precedence(node))) {
|
||||
if (hasDoubleExcessParens(callee) || !(isIIFE(node) || callee.type === "NewExpression" && !isNewExpressionWithParens(callee) && !(node.type === "NewExpression" && !isNewExpressionWithParens(node)) || node.type === "NewExpression" && callee.type === "MemberExpression" && doesMemberExpressionContainCallExpression(callee) || (!("optional" in node) || !node.optional) && callee.type === "ChainExpression")) report(node.callee);
|
||||
}
|
||||
node.arguments.forEach((arg) => {
|
||||
if (hasExcessParensWithPrecedence(arg, PRECEDENCE_OF_ASSIGNMENT_EXPR)) report(arg);
|
||||
});
|
||||
};
|
||||
if (isTypeAssertion(node.callee)) return rule({
|
||||
...node,
|
||||
callee: {
|
||||
...node.callee,
|
||||
type: AST_NODE_TYPES.SequenceExpression
|
||||
}
|
||||
});
|
||||
return rule(node);
|
||||
}
|
||||
function checkClass(node) {
|
||||
if (!node.superClass) return;
|
||||
if (precedence(node.superClass) > PRECEDENCE_OF_UPDATE_EXPR ? hasExcessParens(node.superClass) : hasDoubleExcessParens(node.superClass)) report(node.superClass);
|
||||
}
|
||||
function checkExpressionOrExportStatement(node) {
|
||||
const firstToken = isParenthesised(node) ? sourceCode.getTokenBefore(node) : sourceCode.getFirstToken(node);
|
||||
const secondToken = sourceCode.getTokenAfter(firstToken, import_ast_utils.isNotOpeningParenToken);
|
||||
const thirdToken = secondToken ? sourceCode.getTokenAfter(secondToken) : null;
|
||||
const tokenAfterClosingParens = secondToken ? sourceCode.getTokenAfter(secondToken, import_ast_utils.isNotClosingParenToken) : null;
|
||||
if ((0, import_ast_utils.isOpeningParenToken)(firstToken) && ((0, import_ast_utils.isOpeningBraceToken)(secondToken) || isKeywordToken(secondToken) && (secondToken.value === "function" || secondToken.value === "class" || secondToken.value === "let" && tokenAfterClosingParens && ((0, import_ast_utils.isOpeningBracketToken)(tokenAfterClosingParens) || tokenAfterClosingParens.type === "Identifier")) || secondToken && secondToken.type === "Identifier" && secondToken.value === "async" && isKeywordToken(thirdToken) && thirdToken.value === "function")) tokensToIgnore.add(secondToken);
|
||||
if (node.parent.type === "ExportDefaultDeclaration" ? hasExcessParensWithPrecedence(node, PRECEDENCE_OF_ASSIGNMENT_EXPR) : hasExcessParens(node)) report(node);
|
||||
}
|
||||
function checkUnaryUpdate(node) {
|
||||
if (isTypeAssertion(node.argument)) return checkArgumentWithPrecedence({
|
||||
...node,
|
||||
argument: {
|
||||
...node.argument,
|
||||
type: AST_NODE_TYPES.SequenceExpression
|
||||
}
|
||||
});
|
||||
return checkArgumentWithPrecedence(node);
|
||||
}
|
||||
function checkClassProperty(node) {
|
||||
if (node.computed && hasExcessParensWithPrecedence(node.key, PRECEDENCE_OF_ASSIGNMENT_EXPR)) report(node.key);
|
||||
if (node.value && hasExcessParensWithPrecedence(node.value, PRECEDENCE_OF_ASSIGNMENT_EXPR)) report(node.value);
|
||||
}
|
||||
function checkTSBinaryType(node) {
|
||||
node.types.forEach((type) => {
|
||||
if (IGNORE_NESTED_BINARY && (0, import_ast_utils.isNodeOfTypes)([AST_NODE_TYPES.TSUnionType, AST_NODE_TYPES.TSIntersectionType])(type) ? isParenthesisedTwice(type) : hasExcessParensWithPrecedence(type, precedence(node))) report(type);
|
||||
});
|
||||
}
|
||||
const baseListeners = {
|
||||
ArrayExpression(node) {
|
||||
node.elements.map((element) => isTypeAssertion(element) ? {
|
||||
...element,
|
||||
type: AST_NODE_TYPES.FunctionExpression
|
||||
} : element).forEach((ele) => {
|
||||
if (!!ele && hasExcessParensWithPrecedence(ele, PRECEDENCE_OF_ASSIGNMENT_EXPR)) report(ele);
|
||||
});
|
||||
},
|
||||
ArrayPattern(node) {
|
||||
node.elements.forEach((ele) => {
|
||||
if (!!ele && canBeAssignmentTarget(ele) && hasExcessParens(ele)) report(ele);
|
||||
});
|
||||
},
|
||||
ArrowFunctionExpression(node) {
|
||||
if (isTypeAssertion(node.body)) return;
|
||||
if (isReturnAssignException(node)) return;
|
||||
if (node.body.type === "ConditionalExpression" && IGNORE_ARROW_CONDITIONALS) return;
|
||||
if (node.body.type === "BlockStatement") return;
|
||||
const firstBodyToken = sourceCode.getFirstToken(node.body, import_ast_utils.isNotOpeningParenToken);
|
||||
if ((0, import_ast_utils.isOpeningParenToken)(sourceCode.getTokenBefore(firstBodyToken)) && (0, import_ast_utils.isOpeningBraceToken)(firstBodyToken)) tokensToIgnore.add(firstBodyToken);
|
||||
if (hasExcessParensWithPrecedence(node.body, PRECEDENCE_OF_ASSIGNMENT_EXPR)) report(node.body);
|
||||
},
|
||||
AssignmentExpression(node) {
|
||||
if (canBeAssignmentTarget(node.left) && hasExcessParens(node.left) && (!isAnonymousFunctionAssignmentException(node) || isParenthesisedTwice(node.left))) report(node.left);
|
||||
if (!isReturnAssignException(node) && hasExcessParensWithPrecedence(node.right, precedence(node))) report(node.right);
|
||||
},
|
||||
AssignmentPattern(node) {
|
||||
const { left, right } = node;
|
||||
if (canBeAssignmentTarget(left) && hasExcessParens(left)) report(left);
|
||||
if (right && hasExcessParensWithPrecedence(right, PRECEDENCE_OF_ASSIGNMENT_EXPR)) report(right);
|
||||
},
|
||||
AwaitExpression(node) {
|
||||
if (isTypeAssertion(node.argument)) return checkArgumentWithPrecedence({
|
||||
...node,
|
||||
argument: {
|
||||
...node.argument,
|
||||
type: AST_NODE_TYPES.SequenceExpression
|
||||
}
|
||||
});
|
||||
return checkArgumentWithPrecedence(node);
|
||||
},
|
||||
BinaryExpression(node) {
|
||||
if (reportsBuffer && node.operator === "in") reportsBuffer.inExpressionNodes.push(node);
|
||||
checkBinaryLogical(node);
|
||||
},
|
||||
"CallExpression": checkCallNew,
|
||||
ClassDeclaration(node) {
|
||||
if (node.superClass?.type === AST_NODE_TYPES.TSAsExpression) return checkClass({
|
||||
...node,
|
||||
superClass: {
|
||||
...node.superClass,
|
||||
type: AST_NODE_TYPES.SequenceExpression
|
||||
}
|
||||
});
|
||||
return checkClass(node);
|
||||
},
|
||||
ClassExpression(node) {
|
||||
if (node.superClass?.type === AST_NODE_TYPES.TSAsExpression) return checkClass({
|
||||
...node,
|
||||
superClass: {
|
||||
...node.superClass,
|
||||
type: AST_NODE_TYPES.SequenceExpression
|
||||
}
|
||||
});
|
||||
return checkClass(node);
|
||||
},
|
||||
ConditionalExpression(node) {
|
||||
const rule = (node) => {
|
||||
if (isReturnAssignException(node)) return;
|
||||
const availableTypes = new Set(["BinaryExpression", "LogicalExpression"]);
|
||||
if (!(EXCEPT_COND_TERNARY && availableTypes.has(node.test.type)) && !(ALLOW_NESTED_TERNARY && ["ConditionalExpression"].includes(node.test.type)) && !isCondAssignException(node) && hasExcessParensWithPrecedence(node.test, precedence({
|
||||
type: "LogicalExpression",
|
||||
operator: "||"
|
||||
}))) report(node.test);
|
||||
if (!(EXCEPT_COND_TERNARY && availableTypes.has(node.consequent.type)) && !(ALLOW_NESTED_TERNARY && ["ConditionalExpression"].includes(node.consequent.type)) && hasExcessParensWithPrecedence(node.consequent, PRECEDENCE_OF_ASSIGNMENT_EXPR)) report(node.consequent);
|
||||
if (!(EXCEPT_COND_TERNARY && availableTypes.has(node.alternate.type)) && !(ALLOW_NESTED_TERNARY && ["ConditionalExpression"].includes(node.alternate.type)) && hasExcessParensWithPrecedence(node.alternate, PRECEDENCE_OF_ASSIGNMENT_EXPR)) report(node.alternate);
|
||||
};
|
||||
if (isTypeAssertion(node.test)) return rule({
|
||||
...node,
|
||||
test: {
|
||||
...node.test,
|
||||
type: AST_NODE_TYPES.SequenceExpression
|
||||
}
|
||||
});
|
||||
if (isTypeAssertion(node.consequent)) return rule({
|
||||
...node,
|
||||
consequent: {
|
||||
...node.consequent,
|
||||
type: AST_NODE_TYPES.SequenceExpression
|
||||
}
|
||||
});
|
||||
if (isTypeAssertion(node.alternate)) return rule({
|
||||
...node,
|
||||
alternate: {
|
||||
...node.alternate,
|
||||
type: AST_NODE_TYPES.SequenceExpression
|
||||
}
|
||||
});
|
||||
return rule(node);
|
||||
},
|
||||
DoWhileStatement(node) {
|
||||
if (hasExcessParens(node.test) && !isCondAssignException(node)) report(node.test);
|
||||
},
|
||||
ExportDefaultDeclaration(node) {
|
||||
checkExpressionOrExportStatement(node.declaration);
|
||||
},
|
||||
ExpressionStatement(node) {
|
||||
checkExpressionOrExportStatement(node.expression);
|
||||
},
|
||||
ForInStatement(node) {
|
||||
if (isTypeAssertion(node.right)) return;
|
||||
if (node.left.type !== "VariableDeclaration") {
|
||||
const firstLeftToken = sourceCode.getFirstToken(node.left, import_ast_utils.isNotOpeningParenToken);
|
||||
if (firstLeftToken.value === "let" && (0, import_ast_utils.isOpeningBracketToken)(sourceCode.getTokenAfter(firstLeftToken, import_ast_utils.isNotClosingParenToken))) tokensToIgnore.add(firstLeftToken);
|
||||
}
|
||||
if (hasExcessParens(node.left)) report(node.left);
|
||||
if (hasExcessParens(node.right)) report(node.right);
|
||||
},
|
||||
ForOfStatement(node) {
|
||||
if (node.left.type !== "VariableDeclaration") {
|
||||
const firstLeftToken = sourceCode.getFirstToken(node.left, import_ast_utils.isNotOpeningParenToken);
|
||||
if (firstLeftToken.value === "let") tokensToIgnore.add(firstLeftToken);
|
||||
}
|
||||
if (hasExcessParens(node.left)) report(node.left);
|
||||
if (!isTypeAssertion(node.right) && hasExcessParensWithPrecedence(node.right, PRECEDENCE_OF_ASSIGNMENT_EXPR)) report(node.right);
|
||||
},
|
||||
ForStatement(node) {
|
||||
if (node.test && hasExcessParens(node.test) && !isCondAssignException(node) && !isTypeAssertion(node.test)) report(node.test);
|
||||
if (node.update && hasExcessParens(node.update) && !isTypeAssertion(node.update)) report(node.update);
|
||||
if (node.init && !isTypeAssertion(node.init)) {
|
||||
if (node.init.type !== "VariableDeclaration") {
|
||||
const firstToken = sourceCode.getFirstToken(node.init, import_ast_utils.isNotOpeningParenToken);
|
||||
if (firstToken.value === "let" && (0, import_ast_utils.isOpeningBracketToken)(sourceCode.getTokenAfter(firstToken, import_ast_utils.isNotClosingParenToken))) tokensToIgnore.add(firstToken);
|
||||
}
|
||||
startNewReportsBuffering();
|
||||
if (hasExcessParens(node.init)) report(node.init);
|
||||
}
|
||||
},
|
||||
"ForStatement > *.init:exit": function(node) {
|
||||
if (isTypeAssertion(node)) return;
|
||||
if (reportsBuffer?.reports.length) reportsBuffer.inExpressionNodes.forEach((inExpressionNode) => {
|
||||
const path = pathToDescendant(node, inExpressionNode);
|
||||
let nodeToExclude = null;
|
||||
for (let i = 0; i < path.length; i++) {
|
||||
const pathNode = path[i];
|
||||
if (i < path.length - 1) {
|
||||
const nextPathNode = path[i + 1];
|
||||
if (isSafelyEnclosingInExpression(pathNode, nextPathNode)) return;
|
||||
}
|
||||
if (isParenthesised(pathNode)) if (isInCurrentReportsBuffer(pathNode)) {
|
||||
if (isParenthesisedTwice(pathNode)) return;
|
||||
if (!nodeToExclude) nodeToExclude = pathNode;
|
||||
} else return;
|
||||
}
|
||||
if (nodeToExclude) removeFromCurrentReportsBuffer(nodeToExclude);
|
||||
});
|
||||
endCurrentReportsBuffering();
|
||||
},
|
||||
IfStatement(node) {
|
||||
if (hasExcessParens(node.test) && !isCondAssignException(node)) report(node.test);
|
||||
},
|
||||
ImportExpression(node) {
|
||||
const { source } = node;
|
||||
if (source.type === "SequenceExpression") {
|
||||
if (hasDoubleExcessParens(source)) report(source);
|
||||
} else if (hasExcessParens(source)) report(source);
|
||||
},
|
||||
"LogicalExpression": checkBinaryLogical,
|
||||
MemberExpression(node) {
|
||||
const rule = (node) => {
|
||||
const nodeObjHasExcessParens = isMemberExpInNewCallee(node) && doesMemberExpressionContainCallExpression(node) ? hasDoubleExcessParens(node.object) : hasExcessParens(node.object) && !(isImmediateFunctionPrototypeMethodCall(node.parent) && "callee" in node.parent && node.parent.callee === node && IGNORE_FUNCTION_PROTOTYPE_METHODS);
|
||||
if (nodeObjHasExcessParens && precedence(node.object) >= precedence(node) && (node.computed || !(isDecimalInteger(node.object) || isRegExpLiteral(node.object)))) report(node.object);
|
||||
if (nodeObjHasExcessParens && node.object.type === "CallExpression") report(node.object);
|
||||
if (nodeObjHasExcessParens && !IGNORE_NEW_IN_MEMBER_EXPR && node.object.type === "NewExpression" && isNewExpressionWithParens(node.object)) report(node.object);
|
||||
if (nodeObjHasExcessParens && node.optional && node.object.type === "ChainExpression") report(node.object);
|
||||
if (node.computed && hasExcessParens(node.property)) report(node.property);
|
||||
};
|
||||
if (isTypeAssertion(node.object)) return rule({
|
||||
...node,
|
||||
object: {
|
||||
...node.object,
|
||||
type: AST_NODE_TYPES.SequenceExpression
|
||||
}
|
||||
});
|
||||
if (isTypeAssertion(node.property)) return rule({
|
||||
...node,
|
||||
property: {
|
||||
...node.property,
|
||||
type: AST_NODE_TYPES.FunctionExpression
|
||||
}
|
||||
});
|
||||
return rule(node);
|
||||
},
|
||||
MethodDefinition(node) {
|
||||
if (!node.computed) return;
|
||||
if (hasExcessParensWithPrecedence(node.key, PRECEDENCE_OF_ASSIGNMENT_EXPR)) report(node.key);
|
||||
},
|
||||
"NewExpression": checkCallNew,
|
||||
ObjectExpression(node) {
|
||||
node.properties.forEach((property) => {
|
||||
if (property.type === "Property" && property.value && hasExcessParensWithPrecedence(property.value, PRECEDENCE_OF_ASSIGNMENT_EXPR)) report(property.value);
|
||||
});
|
||||
},
|
||||
ObjectPattern(node) {
|
||||
node.properties.forEach(({ value }) => {
|
||||
if (value && canBeAssignmentTarget(value) && hasExcessParens(value)) report(value);
|
||||
});
|
||||
},
|
||||
Property(node) {
|
||||
if (node.computed) {
|
||||
const { key } = node;
|
||||
if (key && hasExcessParensWithPrecedence(key, PRECEDENCE_OF_ASSIGNMENT_EXPR)) report(key);
|
||||
}
|
||||
},
|
||||
"PropertyDefinition": checkClassProperty,
|
||||
"AccessorProperty": checkClassProperty,
|
||||
RestElement(node) {
|
||||
const argument = node.argument;
|
||||
if (canBeAssignmentTarget(argument) && hasExcessParens(argument)) report(argument);
|
||||
},
|
||||
ReturnStatement(node) {
|
||||
const returnToken = sourceCode.getFirstToken(node);
|
||||
if (isReturnAssignException(node)) return;
|
||||
if (node.argument && returnToken && hasExcessParensNoLineTerminator(returnToken, node.argument) && !isRegExpLiteral(node.argument)) report(node.argument);
|
||||
},
|
||||
SequenceExpression(node) {
|
||||
const precedenceOfNode = precedence(node);
|
||||
node.expressions.forEach((expression) => {
|
||||
if (hasExcessParensWithPrecedence(expression, precedenceOfNode)) report(expression);
|
||||
});
|
||||
},
|
||||
SpreadElement(node) {
|
||||
if (isTypeAssertion(node.argument)) return;
|
||||
if (ALLOW_NODES_IN_SPREAD && ALLOW_NODES_IN_SPREAD.has(node.argument.type)) return;
|
||||
if (!hasExcessParensWithPrecedence(node.argument, PRECEDENCE_OF_ASSIGNMENT_EXPR)) return;
|
||||
report(node.argument);
|
||||
},
|
||||
SwitchCase(node) {
|
||||
if (node.test && !isTypeAssertion(node.test) && hasExcessParens(node.test)) report(node.test);
|
||||
},
|
||||
SwitchStatement(node) {
|
||||
if (hasExcessParens(node.discriminant)) report(node.discriminant);
|
||||
},
|
||||
TemplateLiteral(node) {
|
||||
node.expressions.forEach((expression) => {
|
||||
if (hasExcessParens(expression)) report(expression);
|
||||
});
|
||||
},
|
||||
ThrowStatement(node) {
|
||||
if (!node.argument || isTypeAssertion(node.argument)) return;
|
||||
const throwToken = sourceCode.getFirstToken(node);
|
||||
if (!throwToken) return;
|
||||
if (hasExcessParensNoLineTerminator(throwToken, node.argument)) report(node.argument);
|
||||
},
|
||||
"UnaryExpression": checkUnaryUpdate,
|
||||
UpdateExpression(node) {
|
||||
if (isTypeAssertion(node.argument)) return checkUnaryUpdate(node);
|
||||
if (node.prefix) checkArgumentWithPrecedence(node);
|
||||
else {
|
||||
const { argument } = node;
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(argument, sourceCode.getLastToken(node))) checkArgumentWithPrecedence(node);
|
||||
else if (hasDoubleExcessParens(argument)) report(argument);
|
||||
}
|
||||
},
|
||||
VariableDeclarator(node) {
|
||||
const rule = (node) => {
|
||||
if (node.init && hasExcessParensWithPrecedence(node.init, PRECEDENCE_OF_ASSIGNMENT_EXPR) && !isRegExpLiteral(node.init)) report(node.init);
|
||||
};
|
||||
if (isTypeAssertion(node.init)) return rule({
|
||||
...node,
|
||||
type: AST_NODE_TYPES.VariableDeclarator,
|
||||
init: {
|
||||
...node.init,
|
||||
type: AST_NODE_TYPES.FunctionExpression
|
||||
}
|
||||
});
|
||||
return rule(node);
|
||||
},
|
||||
WhileStatement(node) {
|
||||
if (hasExcessParens(node.test) && !isCondAssignException(node)) report(node.test);
|
||||
},
|
||||
WithStatement(node) {
|
||||
if (hasExcessParens(node.object)) report(node.object);
|
||||
},
|
||||
YieldExpression(node) {
|
||||
if (!node.argument || isTypeAssertion(node.argument)) return;
|
||||
const yieldToken = sourceCode.getFirstToken(node);
|
||||
if (precedence(node.argument) >= precedence(node) && yieldToken && hasExcessParensNoLineTerminator(yieldToken, node.argument) || hasDoubleExcessParens(node.argument)) report(node.argument);
|
||||
},
|
||||
TSArrayType(node) {
|
||||
if (hasExcessParensWithPrecedence(node.elementType, precedence(node))) report(node.elementType);
|
||||
},
|
||||
"TSIntersectionType": checkTSBinaryType,
|
||||
"TSUnionType": checkTSBinaryType,
|
||||
TSTypeAnnotation(node) {
|
||||
if (hasExcessParens(node.typeAnnotation)) report(node.typeAnnotation);
|
||||
},
|
||||
TSTypeAliasDeclaration(node) {
|
||||
if (hasExcessParens(node.typeAnnotation)) report(node.typeAnnotation);
|
||||
},
|
||||
TSEnumMember(node) {
|
||||
if (!node.initializer) return;
|
||||
if (hasExcessParens(node.initializer)) report(node.initializer);
|
||||
}
|
||||
};
|
||||
const listeners = {};
|
||||
const ignoreNodes = /* @__PURE__ */ new Set();
|
||||
const listenerCallQueue = [];
|
||||
for (const key in baseListeners) listeners[key] = (node) => listenerCallQueue.push({
|
||||
node,
|
||||
listener: baseListeners[key]
|
||||
});
|
||||
return {
|
||||
...listeners,
|
||||
...options?.ignoredNodes?.reduce((listener, selector) => Object.assign(listener, { [selector]: (node) => ignoreNodes.add(node) }), {}),
|
||||
"Program:exit": function() {
|
||||
for (let i = 0; i < listenerCallQueue.length; i++) {
|
||||
const { node, listener } = listenerCallQueue[i];
|
||||
if (!ignoreNodes.has(node)) listener(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { no_extra_parens_default as t };
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import { Y as isTopLevelExpressionStatement, f as createRule, g as import_ast_utils, t as FixTracker } from "../utils.js";
|
||||
var no_extra_semi_default = createRule({
|
||||
name: "no-extra-semi",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow unnecessary semicolons" },
|
||||
fixable: "code",
|
||||
schema: [],
|
||||
messages: { unexpected: "Unnecessary semicolon." }
|
||||
},
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
function isFixable(nodeOrToken) {
|
||||
const nextToken = sourceCode.getTokenAfter(nodeOrToken);
|
||||
if (!nextToken || nextToken.type !== "String") return true;
|
||||
return !isTopLevelExpressionStatement(sourceCode.getNodeByRangeIndex(nextToken.range[0]).parent);
|
||||
}
|
||||
function report(nodeOrToken) {
|
||||
context.report({
|
||||
node: nodeOrToken,
|
||||
messageId: "unexpected",
|
||||
fix: isFixable(nodeOrToken) ? (fixer) => new FixTracker(fixer, context.sourceCode).retainSurroundingTokens(nodeOrToken).remove(nodeOrToken) : null
|
||||
});
|
||||
}
|
||||
function checkForPartOfClassBody(firstToken) {
|
||||
for (let token = firstToken; token.type === "Punctuator" && !(0, import_ast_utils.isClosingBraceToken)(token); token = sourceCode.getTokenAfter(token)) if ((0, import_ast_utils.isSemicolonToken)(token)) report(token);
|
||||
}
|
||||
return {
|
||||
EmptyStatement(node) {
|
||||
const parent = node.parent;
|
||||
if (![
|
||||
"ForStatement",
|
||||
"ForInStatement",
|
||||
"ForOfStatement",
|
||||
"WhileStatement",
|
||||
"DoWhileStatement",
|
||||
"IfStatement",
|
||||
"LabeledStatement",
|
||||
"WithStatement"
|
||||
].includes(parent.type)) report(node);
|
||||
},
|
||||
ClassBody(node) {
|
||||
checkForPartOfClassBody(sourceCode.getFirstToken(node, 1));
|
||||
},
|
||||
MethodDefinition(node) {
|
||||
checkForPartOfClassBody(sourceCode.getTokenAfter(node));
|
||||
},
|
||||
PropertyDefinition(node) {
|
||||
checkForPartOfClassBody(sourceCode.getTokenAfter(node));
|
||||
},
|
||||
AccessorProperty(node) {
|
||||
checkForPartOfClassBody(sourceCode.getTokenAfter(node));
|
||||
},
|
||||
StaticBlock(node) {
|
||||
checkForPartOfClassBody(sourceCode.getTokenAfter(node));
|
||||
},
|
||||
TSAbstractMethodDefinition(node) {
|
||||
checkForPartOfClassBody(sourceCode.getTokenAfter(node));
|
||||
},
|
||||
TSAbstractPropertyDefinition(node) {
|
||||
checkForPartOfClassBody(sourceCode.getTokenAfter(node));
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { no_extra_semi_default as t };
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
import { f as createRule, w as canTokensBeAdjacent } from "../utils.js";
|
||||
var no_floating_decimal_default = createRule({
|
||||
name: "no-floating-decimal",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow leading or trailing decimal points in numeric literals" },
|
||||
fixable: "code",
|
||||
schema: [],
|
||||
messages: {
|
||||
leading: "A leading decimal point can be confused with a dot.",
|
||||
trailing: "A trailing decimal point can be confused with a dot."
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
return { Literal(node) {
|
||||
if (typeof node.value === "number") {
|
||||
if (node.raw.startsWith(".")) context.report({
|
||||
node,
|
||||
messageId: "leading",
|
||||
fix(fixer) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(node);
|
||||
const needsSpaceBefore = tokenBefore && tokenBefore.range[1] === node.range[0] && !canTokensBeAdjacent(tokenBefore, `0${node.raw}`);
|
||||
return fixer.insertTextBefore(node, needsSpaceBefore ? " 0" : "0");
|
||||
}
|
||||
});
|
||||
if (node.raw.indexOf(".") === node.raw.length - 1) context.report({
|
||||
node,
|
||||
messageId: "trailing",
|
||||
fix: (fixer) => fixer.insertTextAfter(node, "0")
|
||||
});
|
||||
}
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { no_floating_decimal_default as t };
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
import { U as isParenthesised, f as createRule, g as import_ast_utils, k as getPrecedence } from "../utils.js";
|
||||
const ARITHMETIC_OPERATORS = [
|
||||
"+",
|
||||
"-",
|
||||
"*",
|
||||
"/",
|
||||
"%",
|
||||
"**"
|
||||
];
|
||||
const BITWISE_OPERATORS = [
|
||||
"&",
|
||||
"|",
|
||||
"^",
|
||||
"~",
|
||||
"<<",
|
||||
">>",
|
||||
">>>"
|
||||
];
|
||||
const COMPARISON_OPERATORS = [
|
||||
"==",
|
||||
"!=",
|
||||
"===",
|
||||
"!==",
|
||||
">",
|
||||
">=",
|
||||
"<",
|
||||
"<="
|
||||
];
|
||||
const LOGICAL_OPERATORS = ["&&", "||"];
|
||||
const RELATIONAL_OPERATORS = ["in", "instanceof"];
|
||||
const ALL_OPERATORS = [].concat(ARITHMETIC_OPERATORS, BITWISE_OPERATORS, COMPARISON_OPERATORS, LOGICAL_OPERATORS, RELATIONAL_OPERATORS, ["?:"], ["??"]);
|
||||
const DEFAULT_GROUPS = [
|
||||
ARITHMETIC_OPERATORS,
|
||||
BITWISE_OPERATORS,
|
||||
COMPARISON_OPERATORS,
|
||||
LOGICAL_OPERATORS,
|
||||
RELATIONAL_OPERATORS
|
||||
];
|
||||
const TARGET_NODE_TYPE = /^(?:Binary|Logical|Conditional)Expression$/u;
|
||||
function includesBothInAGroup(groups, left, right) {
|
||||
return groups.some((group) => group.includes(left) && group.includes(right));
|
||||
}
|
||||
function getChildNode(node) {
|
||||
return node.type === "ConditionalExpression" ? node.test : node.left;
|
||||
}
|
||||
var no_mixed_operators_default = createRule({
|
||||
name: "no-mixed-operators",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow mixed binary operators" },
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
groups: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
enum: ALL_OPERATORS
|
||||
},
|
||||
minItems: 2,
|
||||
uniqueItems: true
|
||||
},
|
||||
uniqueItems: true
|
||||
},
|
||||
allowSamePrecedence: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
groups: DEFAULT_GROUPS,
|
||||
allowSamePrecedence: true
|
||||
}],
|
||||
messages: { unexpectedMixedOperator: "Unexpected mix of '{{leftOperator}}' and '{{rightOperator}}'. Use parentheses to clarify the intended order of operations." }
|
||||
},
|
||||
create(context, [options]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const { groups, allowSamePrecedence } = options;
|
||||
function shouldIgnore(node) {
|
||||
const a = node;
|
||||
const b = node.parent;
|
||||
return !includesBothInAGroup(groups, a.operator, b.type === "ConditionalExpression" ? "?:" : b.operator) || allowSamePrecedence && getPrecedence(a) === getPrecedence(b);
|
||||
}
|
||||
function isMixedWithParent(node) {
|
||||
return node.operator !== node.parent.operator && !isParenthesised(sourceCode, node);
|
||||
}
|
||||
function getOperatorToken(node) {
|
||||
return sourceCode.getTokenAfter(getChildNode(node), import_ast_utils.isNotClosingParenToken);
|
||||
}
|
||||
function reportBothOperators(node) {
|
||||
const parent = node.parent;
|
||||
const left = getChildNode(parent) === node ? node : parent;
|
||||
const right = getChildNode(parent) !== node ? node : parent;
|
||||
const data = {
|
||||
leftOperator: left.operator || "?:",
|
||||
rightOperator: right.operator || "?:"
|
||||
};
|
||||
context.report({
|
||||
node: left,
|
||||
loc: getOperatorToken(left).loc,
|
||||
messageId: "unexpectedMixedOperator",
|
||||
data
|
||||
});
|
||||
context.report({
|
||||
node: right,
|
||||
loc: getOperatorToken(right).loc,
|
||||
messageId: "unexpectedMixedOperator",
|
||||
data
|
||||
});
|
||||
}
|
||||
function check(node) {
|
||||
if (TARGET_NODE_TYPE.test(node.parent.type) && isMixedWithParent(node) && !shouldIgnore(node)) reportBothOperators(node);
|
||||
}
|
||||
return {
|
||||
BinaryExpression: check,
|
||||
LogicalExpression: check
|
||||
};
|
||||
}
|
||||
});
|
||||
export { no_mixed_operators_default as t };
|
||||
Generated
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
import { f as createRule } from "../utils.js";
|
||||
var no_mixed_spaces_and_tabs_default = createRule({
|
||||
name: "no-mixed-spaces-and-tabs",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow mixed spaces and tabs for indentation" },
|
||||
schema: [{ oneOf: [{
|
||||
type: "string",
|
||||
enum: ["smart-tabs"]
|
||||
}, { type: "boolean" }] }],
|
||||
defaultOptions: [false],
|
||||
messages: { mixedSpacesAndTabs: "Mixed spaces and tabs." }
|
||||
},
|
||||
create(context, [style]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const smartTabs = typeof style === "boolean" ? style : style === "smart-tabs";
|
||||
return { "Program:exit": function(node) {
|
||||
const lines = sourceCode.lines;
|
||||
const comments = sourceCode.getAllComments();
|
||||
const ignoredCommentLines = /* @__PURE__ */ new Set();
|
||||
comments.forEach((comment) => {
|
||||
for (let i = comment.loc.start.line + 1; i <= comment.loc.end.line; i++) ignoredCommentLines.add(i);
|
||||
});
|
||||
let regex = /^(?=( +|\t+))\1(?:\t| )/u;
|
||||
if (smartTabs) regex = /^(?=(\t*))\1(?=( +))\2\t/u;
|
||||
lines.forEach((line, i) => {
|
||||
const match = regex.exec(line);
|
||||
if (match) {
|
||||
const lineNumber = i + 1;
|
||||
const loc = {
|
||||
start: {
|
||||
line: lineNumber,
|
||||
column: match[0].length - 2
|
||||
},
|
||||
end: {
|
||||
line: lineNumber,
|
||||
column: match[0].length
|
||||
}
|
||||
};
|
||||
if (!ignoredCommentLines.has(lineNumber)) {
|
||||
const containingNode = sourceCode.getNodeByRangeIndex(sourceCode.getIndexFromLoc(loc.start));
|
||||
if (!(containingNode && ["Literal", "TemplateElement"].includes(containingNode.type))) context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId: "mixedSpacesAndTabs"
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { no_mixed_spaces_and_tabs_default as t };
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var no_multi_spaces_default = createRule({
|
||||
name: "no-multi-spaces",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow multiple spaces" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
exceptions: {
|
||||
type: "object",
|
||||
patternProperties: { "^([A-Z][a-z]*)+$": { type: "boolean" } },
|
||||
additionalProperties: false
|
||||
},
|
||||
ignoreEOLComments: { type: "boolean" },
|
||||
includeTabs: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
exceptions: {
|
||||
Property: true,
|
||||
ImportAttribute: true
|
||||
},
|
||||
ignoreEOLComments: false,
|
||||
includeTabs: true
|
||||
}],
|
||||
messages: { multipleSpaces: "Multiple spaces found before '{{displayValue}}'." }
|
||||
},
|
||||
create(context, [options]) {
|
||||
const { ignoreEOLComments, exceptions, includeTabs } = options;
|
||||
const sourceCode = context.sourceCode;
|
||||
const hasExceptions = Object.keys(exceptions).some((key) => exceptions[key]);
|
||||
const spacesRe = includeTabs ? /[ \t]{2}/ : / {2}/;
|
||||
function formatReportedCommentValue(token) {
|
||||
const valueLines = token.value.split("\n");
|
||||
const value = valueLines[0];
|
||||
const formattedValue = `${value.slice(0, 12)}...`;
|
||||
return valueLines.length === 1 && value.length <= 12 ? value : formattedValue;
|
||||
}
|
||||
return { Program() {
|
||||
sourceCode.tokensAndComments.forEach((leftToken, leftIndex, tokensAndComments) => {
|
||||
if (leftIndex === tokensAndComments.length - 1) return;
|
||||
const rightToken = tokensAndComments[leftIndex + 1];
|
||||
if (!spacesRe.test(sourceCode.text.slice(leftToken.range[1], rightToken.range[0])) || leftToken.loc.end.line < rightToken.loc.start.line) return;
|
||||
if (ignoreEOLComments && (0, import_ast_utils.isCommentToken)(rightToken) && (leftIndex === tokensAndComments.length - 2 || rightToken.loc.end.line < tokensAndComments[leftIndex + 2].loc.start.line)) return;
|
||||
if (hasExceptions) {
|
||||
const parentNode = sourceCode.getNodeByRangeIndex(rightToken.range[0] - 1);
|
||||
if (parentNode && exceptions[parentNode.type]) return;
|
||||
}
|
||||
let displayValue;
|
||||
if (rightToken.type === "Block") displayValue = `/*${formatReportedCommentValue(rightToken)}*/`;
|
||||
else if (rightToken.type === "Line") displayValue = `//${formatReportedCommentValue(rightToken)}`;
|
||||
else displayValue = rightToken.value;
|
||||
context.report({
|
||||
node: rightToken,
|
||||
loc: {
|
||||
start: leftToken.loc.end,
|
||||
end: rightToken.loc.start
|
||||
},
|
||||
messageId: "multipleSpaces",
|
||||
data: { displayValue },
|
||||
fix: (fixer) => fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ")
|
||||
});
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { no_multi_spaces_default as t };
|
||||
Generated
Vendored
+97
@@ -0,0 +1,97 @@
|
||||
import { f as createRule } from "../utils.js";
|
||||
var no_multiple_empty_lines_default = createRule({
|
||||
name: "no-multiple-empty-lines",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow multiple empty lines" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
max: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
},
|
||||
maxEOF: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
},
|
||||
maxBOF: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
}
|
||||
},
|
||||
required: ["max"],
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{ max: 2 }],
|
||||
messages: {
|
||||
blankBeginningOfFile: "Too many blank lines at the beginning of file. Max of {{max}} allowed.",
|
||||
blankEndOfFile: "Too many blank lines at the end of file. Max of {{max}} allowed.",
|
||||
consecutiveBlank: "More than {{max}} blank {{pluralizedLines}} not allowed."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const { max, maxEOF = max, maxBOF = max } = options;
|
||||
const sourceCode = context.sourceCode;
|
||||
const allLines = sourceCode.lines[sourceCode.lines.length - 1] === "" ? sourceCode.lines.slice(0, -1) : sourceCode.lines;
|
||||
const templateLiteralLines = /* @__PURE__ */ new Set();
|
||||
return {
|
||||
TemplateLiteral(node) {
|
||||
node.quasis.forEach((literalPart) => {
|
||||
for (let ignoredLine = literalPart.loc.start.line; ignoredLine < literalPart.loc.end.line; ignoredLine++) templateLiteralLines.add(ignoredLine);
|
||||
});
|
||||
},
|
||||
"Program:exit": function(node) {
|
||||
return allLines.reduce((nonEmptyLineNumbers, line, index) => {
|
||||
if (line.trim() || templateLiteralLines.has(index + 1)) nonEmptyLineNumbers.push(index + 1);
|
||||
return nonEmptyLineNumbers;
|
||||
}, []).concat(allLines.length + 1).reduce((lastLineNumber, lineNumber) => {
|
||||
let messageId, maxAllowed;
|
||||
if (lastLineNumber === 0) {
|
||||
messageId = "blankBeginningOfFile";
|
||||
maxAllowed = maxBOF;
|
||||
} else if (lineNumber === allLines.length + 1) {
|
||||
messageId = "blankEndOfFile";
|
||||
maxAllowed = maxEOF;
|
||||
} else {
|
||||
messageId = "consecutiveBlank";
|
||||
maxAllowed = max;
|
||||
}
|
||||
if (lineNumber - lastLineNumber - 1 > maxAllowed) context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: {
|
||||
line: lastLineNumber + maxAllowed + 1,
|
||||
column: 0
|
||||
},
|
||||
end: {
|
||||
line: lineNumber,
|
||||
column: 0
|
||||
}
|
||||
},
|
||||
messageId,
|
||||
data: {
|
||||
max: maxAllowed,
|
||||
pluralizedLines: maxAllowed === 1 ? "line" : "lines"
|
||||
},
|
||||
fix(fixer) {
|
||||
const rangeStart = sourceCode.getIndexFromLoc({
|
||||
line: lastLineNumber + 1,
|
||||
column: 0
|
||||
});
|
||||
const lineNumberAfterRemovedLines = lineNumber - maxAllowed;
|
||||
const rangeEnd = lineNumberAfterRemovedLines <= allLines.length ? sourceCode.getIndexFromLoc({
|
||||
line: lineNumberAfterRemovedLines,
|
||||
column: 0
|
||||
}) : sourceCode.text.length;
|
||||
return fixer.removeRange([rangeStart, rangeEnd]);
|
||||
}
|
||||
});
|
||||
return lineNumber;
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { no_multiple_empty_lines_default as t };
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { f as createRule } from "../utils.js";
|
||||
const tabRegex = /\t+/gu;
|
||||
const anyNonWhitespaceRegex = /\S/u;
|
||||
var no_tabs_default = createRule({
|
||||
name: "no-tabs",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow all tabs" },
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: { allowIndentationTabs: { type: "boolean" } },
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{ allowIndentationTabs: false }],
|
||||
messages: { unexpectedTab: "Unexpected tab character." }
|
||||
},
|
||||
create(context, [options]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const { allowIndentationTabs } = options;
|
||||
return { Program(node) {
|
||||
sourceCode.getLines().forEach((line, index) => {
|
||||
let match;
|
||||
while ((match = tabRegex.exec(line)) !== null) {
|
||||
if (allowIndentationTabs && !anyNonWhitespaceRegex.test(line.slice(0, match.index))) continue;
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: {
|
||||
line: index + 1,
|
||||
column: match.index
|
||||
},
|
||||
end: {
|
||||
line: index + 1,
|
||||
column: match.index + match[0].length
|
||||
}
|
||||
},
|
||||
messageId: "unexpectedTab"
|
||||
});
|
||||
}
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { no_tabs_default as t };
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
import { T as createGlobalLinebreakMatcher, f as createRule } from "../utils.js";
|
||||
var no_trailing_spaces_default = createRule({
|
||||
name: "no-trailing-spaces",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow trailing whitespace at the end of lines" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
skipBlankLines: { type: "boolean" },
|
||||
ignoreComments: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
skipBlankLines: false,
|
||||
ignoreComments: false
|
||||
}],
|
||||
messages: { trailingSpace: "Trailing spaces not allowed." }
|
||||
},
|
||||
create(context, [options]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const BLANK_CLASS = "[ \xA0 - ]";
|
||||
const SKIP_BLANK = `^${BLANK_CLASS}*$`;
|
||||
const NONBLANK = `${BLANK_CLASS}+$`;
|
||||
const { skipBlankLines, ignoreComments } = options;
|
||||
function report(node, location, fixRange) {
|
||||
context.report({
|
||||
node,
|
||||
loc: location,
|
||||
messageId: "trailingSpace",
|
||||
fix(fixer) {
|
||||
return fixer.removeRange(fixRange);
|
||||
}
|
||||
});
|
||||
}
|
||||
function getCommentLineNumbers(comments) {
|
||||
const lines = /* @__PURE__ */ new Set();
|
||||
comments.forEach((comment) => {
|
||||
const endLine = comment.type === "Block" ? comment.loc.end.line - 1 : comment.loc.end.line;
|
||||
for (let i = comment.loc.start.line; i <= endLine; i++) lines.add(i);
|
||||
});
|
||||
return lines;
|
||||
}
|
||||
return { [context.sourceCode.ast.type || "Program"]: function checkTrailingSpaces(node) {
|
||||
const re = new RegExp(NONBLANK, "u");
|
||||
const skipMatch = new RegExp(SKIP_BLANK, "u");
|
||||
const lines = sourceCode.lines;
|
||||
const linebreaks = sourceCode.getText().match(createGlobalLinebreakMatcher());
|
||||
const commentLineNumbers = getCommentLineNumbers("getAllComments" in sourceCode ? sourceCode.getAllComments() : []);
|
||||
let totalLength = 0;
|
||||
for (let i = 0, ii = lines.length; i < ii; i++) {
|
||||
const lineNumber = i + 1;
|
||||
const linebreakLength = linebreaks && linebreaks[i] ? linebreaks[i].length : 1;
|
||||
const lineLength = lines[i].length + linebreakLength;
|
||||
const matches = re.exec(lines[i]);
|
||||
if (matches) {
|
||||
const location = {
|
||||
start: {
|
||||
line: lineNumber,
|
||||
column: matches.index
|
||||
},
|
||||
end: {
|
||||
line: lineNumber,
|
||||
column: lineLength - linebreakLength
|
||||
}
|
||||
};
|
||||
const rangeStart = totalLength + location.start.column;
|
||||
const rangeEnd = totalLength + location.end.column;
|
||||
const containingNode = "getNodeByRangeIndex" in sourceCode ? sourceCode.getNodeByRangeIndex(rangeStart) : null;
|
||||
if (containingNode && containingNode.type === "TemplateElement" && rangeStart > containingNode.parent.range[0] && rangeEnd < containingNode.parent.range[1]) {
|
||||
totalLength += lineLength;
|
||||
continue;
|
||||
}
|
||||
if (skipBlankLines && skipMatch.test(lines[i])) {
|
||||
totalLength += lineLength;
|
||||
continue;
|
||||
}
|
||||
const fixRange = [rangeStart, rangeEnd];
|
||||
if (!ignoreComments || !commentLineNumbers.has(lineNumber)) report(node, location, fixRange);
|
||||
}
|
||||
totalLength += lineLength;
|
||||
}
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { no_trailing_spaces_default as t };
|
||||
Generated
Vendored
+86
@@ -0,0 +1,86 @@
|
||||
import { P as isDecimalInteger, d as safeReplaceTextBetween, f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var no_whitespace_before_property_default = createRule({
|
||||
name: "no-whitespace-before-property",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Disallow whitespace before properties" },
|
||||
fixable: "whitespace",
|
||||
schema: [],
|
||||
messages: { unexpectedWhitespace: "Unexpected whitespace before property {{propName}}." }
|
||||
},
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
function reportError(descriptor) {
|
||||
const { node, leftToken, rightToken, propName, replacementText = "", preventAutoFix } = descriptor;
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unexpectedWhitespace",
|
||||
data: { propName },
|
||||
fix: preventAutoFix?.() ? null : safeReplaceTextBetween(sourceCode, leftToken, rightToken, replacementText)
|
||||
});
|
||||
}
|
||||
return {
|
||||
MemberExpression(node) {
|
||||
if (!(0, import_ast_utils.isTokenOnSameLine)(node.object, node.property)) return;
|
||||
let rightToken;
|
||||
let leftToken;
|
||||
if (node.computed) {
|
||||
rightToken = sourceCode.getTokenBefore(node.property, import_ast_utils.isOpeningBracketToken);
|
||||
leftToken = sourceCode.getTokenBefore(rightToken, node.optional ? 1 : 0);
|
||||
} else {
|
||||
rightToken = sourceCode.getFirstToken(node.property);
|
||||
leftToken = sourceCode.getTokenBefore(rightToken, 1);
|
||||
}
|
||||
if (!sourceCode.isSpaceBetween(leftToken, rightToken)) return;
|
||||
let replacementText = "";
|
||||
if (node.optional) replacementText = "?.";
|
||||
else if (!node.computed) replacementText = ".";
|
||||
reportError({
|
||||
node,
|
||||
leftToken,
|
||||
rightToken,
|
||||
propName: sourceCode.getText(node.property),
|
||||
replacementText,
|
||||
preventAutoFix: () => !node.computed && !node.optional && isDecimalInteger(node.object)
|
||||
});
|
||||
},
|
||||
TSIndexedAccessType(node) {
|
||||
const rightToken = sourceCode.getTokenBefore(node.indexType, import_ast_utils.isOpeningBracketToken);
|
||||
const leftToken = sourceCode.getTokenBefore(rightToken);
|
||||
if (!sourceCode.isSpaceBetween(leftToken, rightToken)) return;
|
||||
reportError({
|
||||
node,
|
||||
leftToken,
|
||||
rightToken,
|
||||
propName: sourceCode.getText(node.indexType)
|
||||
});
|
||||
},
|
||||
TSQualifiedName(node) {
|
||||
const leftToken = node.left;
|
||||
const rightToken = node.right;
|
||||
if (!sourceCode.isSpaceBetween(leftToken, rightToken)) return;
|
||||
reportError({
|
||||
node,
|
||||
leftToken,
|
||||
rightToken,
|
||||
replacementText: ".",
|
||||
propName: sourceCode.getText(node.right)
|
||||
});
|
||||
},
|
||||
TSImportType(node) {
|
||||
if (!node.qualifier) return;
|
||||
const rightToken = node.qualifier;
|
||||
const leftToken = sourceCode.getTokenBefore(rightToken, 1);
|
||||
if (!sourceCode.isSpaceBetween(leftToken, rightToken)) return;
|
||||
reportError({
|
||||
node,
|
||||
leftToken,
|
||||
rightToken,
|
||||
replacementText: ".",
|
||||
propName: sourceCode.getText(node.qualifier)
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { no_whitespace_before_property_default as t };
|
||||
Generated
Vendored
+68
@@ -0,0 +1,68 @@
|
||||
import { d as safeReplaceTextBetween, f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
const POSITION_SCHEMA = {
|
||||
type: "string",
|
||||
enum: [
|
||||
"beside",
|
||||
"below",
|
||||
"any"
|
||||
]
|
||||
};
|
||||
var nonblock_statement_body_position_default = createRule({
|
||||
name: "nonblock-statement-body-position",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce the location of single-line statements" },
|
||||
fixable: "whitespace",
|
||||
schema: [POSITION_SCHEMA, {
|
||||
type: "object",
|
||||
properties: { overrides: {
|
||||
type: "object",
|
||||
properties: {
|
||||
if: POSITION_SCHEMA,
|
||||
else: POSITION_SCHEMA,
|
||||
while: POSITION_SCHEMA,
|
||||
do: POSITION_SCHEMA,
|
||||
for: POSITION_SCHEMA
|
||||
},
|
||||
additionalProperties: false
|
||||
} },
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: ["beside"],
|
||||
messages: {
|
||||
expectNoLinebreak: "Expected no linebreak before this statement.",
|
||||
expectLinebreak: "Expected a linebreak before this statement."
|
||||
}
|
||||
},
|
||||
create(context, [style, { overrides = {} } = {}]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
function validateStatement(node, keywordName) {
|
||||
const option = overrides[keywordName] ?? style;
|
||||
if (node.type === "BlockStatement" || option === "any") return;
|
||||
const tokenBefore = sourceCode.getTokenBefore(node);
|
||||
const onSameLine = (0, import_ast_utils.isTokenOnSameLine)(tokenBefore, node);
|
||||
if (onSameLine && option === "below") context.report({
|
||||
node,
|
||||
messageId: "expectLinebreak",
|
||||
fix: (fixer) => fixer.insertTextBefore(node, "\n")
|
||||
});
|
||||
else if (!onSameLine && option === "beside") context.report({
|
||||
node,
|
||||
messageId: "expectNoLinebreak",
|
||||
fix: safeReplaceTextBetween(sourceCode, tokenBefore, node, " ")
|
||||
});
|
||||
}
|
||||
return {
|
||||
IfStatement(node) {
|
||||
validateStatement(node.consequent, "if");
|
||||
if (node.alternate && node.alternate.type !== "IfStatement") validateStatement(node.alternate, "else");
|
||||
},
|
||||
WhileStatement: (node) => validateStatement(node.body, "while"),
|
||||
DoWhileStatement: (node) => validateStatement(node.body, "do"),
|
||||
ForStatement: (node) => validateStatement(node.body, "for"),
|
||||
ForInStatement: (node) => validateStatement(node.body, "for"),
|
||||
ForOfStatement: (node) => validateStatement(node.body, "for")
|
||||
};
|
||||
}
|
||||
});
|
||||
export { nonblock_statement_body_position_default as t };
|
||||
Generated
Vendored
+171
@@ -0,0 +1,171 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
const OPTION_VALUE = { oneOf: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
multiline: { type: "boolean" },
|
||||
minProperties: {
|
||||
type: "integer",
|
||||
minimum: 0
|
||||
},
|
||||
consistent: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false,
|
||||
minProperties: 1
|
||||
}] };
|
||||
var object_curly_newline_default = createRule({
|
||||
name: "object-curly-newline",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent line breaks after opening and before closing braces" },
|
||||
fixable: "whitespace",
|
||||
schema: [{ oneOf: [OPTION_VALUE, {
|
||||
type: "object",
|
||||
properties: {
|
||||
ObjectExpression: OPTION_VALUE,
|
||||
ObjectPattern: OPTION_VALUE,
|
||||
ImportDeclaration: OPTION_VALUE,
|
||||
ExportDeclaration: OPTION_VALUE,
|
||||
TSTypeLiteral: OPTION_VALUE,
|
||||
TSInterfaceBody: OPTION_VALUE,
|
||||
TSEnumBody: OPTION_VALUE
|
||||
},
|
||||
additionalProperties: false,
|
||||
minProperties: 1
|
||||
}] }],
|
||||
defaultOptions: [],
|
||||
messages: {
|
||||
unexpectedLinebreakBeforeClosingBrace: "Unexpected line break before this closing brace.",
|
||||
unexpectedLinebreakAfterOpeningBrace: "Unexpected line break after this opening brace.",
|
||||
expectedLinebreakBeforeClosingBrace: "Expected a line break before this closing brace.",
|
||||
expectedLinebreakAfterOpeningBrace: "Expected a line break after this opening brace."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
function normalizeOptionValue(value) {
|
||||
let multiline = false;
|
||||
let minProperties = Number.POSITIVE_INFINITY;
|
||||
let consistent = false;
|
||||
if (value) if (value === "always") minProperties = 0;
|
||||
else if (value === "never") minProperties = Number.POSITIVE_INFINITY;
|
||||
else {
|
||||
multiline = Boolean(value.multiline);
|
||||
minProperties = value.minProperties || Number.POSITIVE_INFINITY;
|
||||
consistent = Boolean(value.consistent);
|
||||
}
|
||||
else consistent = true;
|
||||
return {
|
||||
multiline,
|
||||
minProperties,
|
||||
consistent
|
||||
};
|
||||
}
|
||||
function isObject(value) {
|
||||
return typeof value === "object" && value !== null;
|
||||
}
|
||||
function isNodeSpecificOption(option) {
|
||||
return isObject(option) || typeof option === "string";
|
||||
}
|
||||
function normalizeOptions(options) {
|
||||
if (isObject(options) && Object.values(options).some(isNodeSpecificOption)) return {
|
||||
ObjectExpression: normalizeOptionValue(options.ObjectExpression),
|
||||
ObjectPattern: normalizeOptionValue(options.ObjectPattern),
|
||||
ImportDeclaration: normalizeOptionValue(options.ImportDeclaration),
|
||||
ExportNamedDeclaration: normalizeOptionValue(options.ExportDeclaration),
|
||||
TSTypeLiteral: normalizeOptionValue(options.TSTypeLiteral),
|
||||
TSInterfaceBody: normalizeOptionValue(options.TSInterfaceBody),
|
||||
TSEnumBody: normalizeOptionValue(options.TSEnumBody)
|
||||
};
|
||||
const value = normalizeOptionValue(options);
|
||||
return {
|
||||
ObjectExpression: value,
|
||||
ObjectPattern: value,
|
||||
ImportDeclaration: value,
|
||||
ExportNamedDeclaration: value,
|
||||
TSTypeLiteral: value,
|
||||
TSInterfaceBody: value,
|
||||
TSEnumBody: value
|
||||
};
|
||||
}
|
||||
const normalizedOptions = normalizeOptions(options);
|
||||
function areLineBreaksRequired(node, options, first, last) {
|
||||
let objectProperties;
|
||||
if (node.type === "ObjectExpression" || node.type === "ObjectPattern") objectProperties = node.properties;
|
||||
else if (node.type === "TSTypeLiteral") objectProperties = node.members;
|
||||
else if (node.type === "TSInterfaceBody") objectProperties = node.body;
|
||||
else if (node.type === "TSEnumBody") objectProperties = node.members;
|
||||
else objectProperties = node.specifiers.filter((s) => s.type === "ImportSpecifier" || s.type === "ExportSpecifier");
|
||||
return objectProperties.length >= options.minProperties || options.multiline && objectProperties.length > 0 && !(0, import_ast_utils.isTokenOnSameLine)(last, first);
|
||||
}
|
||||
function check(node) {
|
||||
const options = normalizedOptions[node.type];
|
||||
if (node.type === "ImportDeclaration" && !node.specifiers.some((specifier) => specifier.type === "ImportSpecifier") || node.type === "ExportNamedDeclaration" && !node.specifiers.some((specifier) => specifier.type === "ExportSpecifier")) return;
|
||||
const openBrace = sourceCode.getFirstToken(node, (token) => token.value === "{");
|
||||
let closeBrace;
|
||||
if (node.type === "ObjectPattern" && node.typeAnnotation) closeBrace = sourceCode.getTokenBefore(node.typeAnnotation);
|
||||
else closeBrace = sourceCode.getLastToken(node, (token) => token.value === "}");
|
||||
let first = sourceCode.getTokenAfter(openBrace, { includeComments: true });
|
||||
let last = sourceCode.getTokenBefore(closeBrace, { includeComments: true });
|
||||
const needsLineBreaks = areLineBreaksRequired(node, options, first, last);
|
||||
const hasCommentsFirstToken = (0, import_ast_utils.isCommentToken)(first);
|
||||
const hasCommentsLastToken = (0, import_ast_utils.isCommentToken)(last);
|
||||
first = sourceCode.getTokenAfter(openBrace);
|
||||
last = sourceCode.getTokenBefore(closeBrace);
|
||||
if (needsLineBreaks) {
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(openBrace, first)) context.report({
|
||||
messageId: "expectedLinebreakAfterOpeningBrace",
|
||||
node,
|
||||
loc: openBrace.loc,
|
||||
fix(fixer) {
|
||||
if (hasCommentsFirstToken) return null;
|
||||
return fixer.insertTextAfter(openBrace, "\n");
|
||||
}
|
||||
});
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(last, closeBrace)) context.report({
|
||||
messageId: "expectedLinebreakBeforeClosingBrace",
|
||||
node,
|
||||
loc: closeBrace.loc,
|
||||
fix(fixer) {
|
||||
if (hasCommentsLastToken) return null;
|
||||
return fixer.insertTextBefore(closeBrace, "\n");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const consistent = options.consistent;
|
||||
const hasLineBreakBetweenOpenBraceAndFirst = !(0, import_ast_utils.isTokenOnSameLine)(openBrace, first);
|
||||
const hasLineBreakBetweenCloseBraceAndLast = !(0, import_ast_utils.isTokenOnSameLine)(last, closeBrace);
|
||||
if (!consistent && hasLineBreakBetweenOpenBraceAndFirst || consistent && hasLineBreakBetweenOpenBraceAndFirst && !hasLineBreakBetweenCloseBraceAndLast) context.report({
|
||||
messageId: "unexpectedLinebreakAfterOpeningBrace",
|
||||
node,
|
||||
loc: openBrace.loc,
|
||||
fix(fixer) {
|
||||
if (hasCommentsFirstToken) return null;
|
||||
return fixer.removeRange([openBrace.range[1], first.range[0]]);
|
||||
}
|
||||
});
|
||||
if (!consistent && hasLineBreakBetweenCloseBraceAndLast || consistent && !hasLineBreakBetweenOpenBraceAndFirst && hasLineBreakBetweenCloseBraceAndLast) context.report({
|
||||
messageId: "unexpectedLinebreakBeforeClosingBrace",
|
||||
node,
|
||||
loc: closeBrace.loc,
|
||||
fix(fixer) {
|
||||
if (hasCommentsLastToken) return null;
|
||||
return fixer.removeRange([last.range[1], closeBrace.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return {
|
||||
ObjectExpression: check,
|
||||
ObjectPattern: check,
|
||||
ImportDeclaration: check,
|
||||
ExportNamedDeclaration: check,
|
||||
TSTypeLiteral: check,
|
||||
TSInterfaceBody: check,
|
||||
TSEnumBody: check
|
||||
};
|
||||
}
|
||||
});
|
||||
export { object_curly_newline_default as t };
|
||||
Generated
Vendored
+253
@@ -0,0 +1,253 @@
|
||||
import { f as createRule, g as import_ast_utils, m as AST_NODE_TYPES } from "../utils.js";
|
||||
var object_curly_spacing_default = createRule({
|
||||
name: "object-curly-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent spacing inside braces" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
arraysInObjects: { type: "boolean" },
|
||||
objectsInObjects: { type: "boolean" },
|
||||
overrides: {
|
||||
type: "object",
|
||||
properties: Object.fromEntries([
|
||||
"ObjectPattern",
|
||||
"ObjectExpression",
|
||||
"ImportDeclaration",
|
||||
"ImportAttributes",
|
||||
"ExportNamedDeclaration",
|
||||
"ExportAllDeclaration",
|
||||
"TSMappedType",
|
||||
"TSTypeLiteral",
|
||||
"TSInterfaceBody",
|
||||
"TSEnumBody"
|
||||
].map((node) => [node, {
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}])),
|
||||
additionalProperties: false
|
||||
},
|
||||
emptyObjects: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"ignore",
|
||||
"always",
|
||||
"never"
|
||||
]
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: ["never"],
|
||||
messages: {
|
||||
requireSpaceBefore: "A space is required before '{{token}}'.",
|
||||
requireSpaceAfter: "A space is required after '{{token}}'.",
|
||||
unexpectedSpaceBefore: "There should be no space before '{{token}}'.",
|
||||
unexpectedSpaceAfter: "There should be no space after '{{token}}'.",
|
||||
requiredSpaceInEmptyObject: "A space is required in empty '{{node}}'.",
|
||||
unexpectedSpaceInEmptyObject: "There should be no space in empty '{{node}}'."
|
||||
}
|
||||
},
|
||||
create(context, [firstOption, secondOption]) {
|
||||
const spaced = firstOption === "always";
|
||||
const sourceCode = context.sourceCode;
|
||||
function isOptionSet(option) {
|
||||
return secondOption ? secondOption[option] === !spaced : false;
|
||||
}
|
||||
const options = {
|
||||
spaced,
|
||||
arraysInObjectsException: isOptionSet("arraysInObjects"),
|
||||
objectsInObjectsException: isOptionSet("objectsInObjects"),
|
||||
overrides: secondOption?.overrides ?? {},
|
||||
emptyObjects: secondOption?.emptyObjects ?? "ignore"
|
||||
};
|
||||
function reportNoBeginningSpace(node, token) {
|
||||
const nextToken = sourceCode.getTokenAfter(token, { includeComments: true });
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: token.loc.end,
|
||||
end: nextToken.loc.start
|
||||
},
|
||||
messageId: "unexpectedSpaceAfter",
|
||||
data: { token: token.value },
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([token.range[1], nextToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportNoEndingSpace(node, token) {
|
||||
const previousToken = sourceCode.getTokenBefore(token, { includeComments: true });
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: previousToken.loc.end,
|
||||
end: token.loc.start
|
||||
},
|
||||
messageId: "unexpectedSpaceBefore",
|
||||
data: { token: token.value },
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([previousToken.range[1], token.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportRequiredBeginningSpace(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "requireSpaceAfter",
|
||||
data: { token: token.value },
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
function reportRequiredEndingSpace(node, token) {
|
||||
context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "requireSpaceBefore",
|
||||
data: { token: token.value },
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
function validateBraceSpacing(node, openingToken, closingToken, nodeType = node.type) {
|
||||
const tokenAfterOpening = sourceCode.getTokenAfter(openingToken, { includeComments: true });
|
||||
const spaced = options.overrides[nodeType] ? options.overrides[nodeType] === "always" : options.spaced;
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(openingToken, tokenAfterOpening)) {
|
||||
const firstSpaced = sourceCode.isSpaceBetween(openingToken, tokenAfterOpening);
|
||||
const secondType = sourceCode.getNodeByRangeIndex(tokenAfterOpening.range[0]).type;
|
||||
const openingCurlyBraceMustBeSpaced = options.arraysInObjectsException && [AST_NODE_TYPES.TSMappedType, AST_NODE_TYPES.TSIndexSignature].includes(secondType) ? !spaced : spaced;
|
||||
if (openingCurlyBraceMustBeSpaced && !firstSpaced) reportRequiredBeginningSpace(node, openingToken);
|
||||
if (!openingCurlyBraceMustBeSpaced && firstSpaced && !((0, import_ast_utils.isCommentToken)(tokenAfterOpening) && !(0, import_ast_utils.isTokenOnSameLine)(openingToken, closingToken))) reportNoBeginningSpace(node, openingToken);
|
||||
}
|
||||
const tokenBeforeClosing = sourceCode.getTokenBefore(closingToken, { includeComments: true });
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(tokenBeforeClosing, closingToken)) {
|
||||
const penultimateType = options.arraysInObjectsException && (0, import_ast_utils.isClosingBracketToken)(tokenBeforeClosing) || options.objectsInObjectsException && (0, import_ast_utils.isClosingBraceToken)(tokenBeforeClosing) ? sourceCode.getNodeByRangeIndex(tokenBeforeClosing.range[0]).type : void 0;
|
||||
const closingCurlyBraceMustBeSpaced = options.arraysInObjectsException && [AST_NODE_TYPES.ArrayExpression, AST_NODE_TYPES.TSTupleType].includes(penultimateType) || options.objectsInObjectsException && penultimateType !== void 0 && [
|
||||
AST_NODE_TYPES.ObjectExpression,
|
||||
AST_NODE_TYPES.ObjectPattern,
|
||||
AST_NODE_TYPES.TSMappedType,
|
||||
AST_NODE_TYPES.TSTypeLiteral
|
||||
].includes(penultimateType) ? !spaced : spaced;
|
||||
const lastSpaced = sourceCode.isSpaceBetween(tokenBeforeClosing, closingToken);
|
||||
if (closingCurlyBraceMustBeSpaced && !lastSpaced) reportRequiredEndingSpace(node, closingToken);
|
||||
if (!closingCurlyBraceMustBeSpaced && lastSpaced) reportNoEndingSpace(node, closingToken);
|
||||
}
|
||||
}
|
||||
function checkSpaceInEmptyObjectLike(node, openingToken, closingToken, nodeType = node.type) {
|
||||
if (options.emptyObjects === "ignore" || !(0, import_ast_utils.isTokenOnSameLine)(openingToken, closingToken) || sourceCode.commentsExistBetween(openingToken, closingToken)) return;
|
||||
const sourceBetween = sourceCode.getText().slice(openingToken.range[0] + 1, closingToken.range[1] - 1);
|
||||
if (sourceBetween.trim() !== "") return;
|
||||
if (options.emptyObjects === "always") {
|
||||
if (sourceBetween === " ") return;
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: openingToken.loc.end,
|
||||
end: closingToken.loc.start
|
||||
},
|
||||
messageId: "requiredSpaceInEmptyObject",
|
||||
data: { node: nodeType },
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange([openingToken.range[1], closingToken.range[0]], " ");
|
||||
}
|
||||
});
|
||||
} else if (options.emptyObjects === "never") {
|
||||
if (sourceBetween === "") return;
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: openingToken.loc.end,
|
||||
end: closingToken.loc.start
|
||||
},
|
||||
messageId: "unexpectedSpaceInEmptyObject",
|
||||
data: { node: nodeType },
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([openingToken.range[1], closingToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function getBraceToken(node, nodeType = node.type) {
|
||||
switch (nodeType) {
|
||||
case "ImportDeclaration":
|
||||
case "ExportNamedDeclaration":
|
||||
case "ExportAllDeclaration": {
|
||||
const attrTokens = sourceCode.getTokens(node);
|
||||
return [attrTokens.find((token) => (0, import_ast_utils.isOpeningBraceToken)(token)), attrTokens.find((token) => (0, import_ast_utils.isClosingBraceToken)(token))];
|
||||
}
|
||||
case "ImportAttributes": {
|
||||
const attrTokens = sourceCode.getTokens(node);
|
||||
const openingAttrToken = attrTokens.findLast((token) => (0, import_ast_utils.isOpeningBraceToken)(token));
|
||||
const closingAttrToken = attrTokens.findLast((token) => (0, import_ast_utils.isClosingBraceToken)(token));
|
||||
if (!openingAttrToken || !closingAttrToken || !node.source || openingAttrToken.range[0] < node.source.range[0]) return [null, null];
|
||||
return [openingAttrToken, closingAttrToken];
|
||||
}
|
||||
case "ObjectPattern":
|
||||
case "ObjectExpression":
|
||||
case "TSMappedType":
|
||||
case "TSTypeLiteral":
|
||||
case "TSInterfaceBody":
|
||||
case "TSEnumBody": {
|
||||
const allTokens = sourceCode.getTokens(node);
|
||||
return [allTokens.find((token) => (0, import_ast_utils.isOpeningBraceToken)(token)), node.type === "ObjectPattern" && node.typeAnnotation ? sourceCode.getTokenBefore(node.typeAnnotation) : allTokens.findLast((token) => (0, import_ast_utils.isClosingBraceToken)(token))];
|
||||
}
|
||||
default: throw new Error(`Unsupported node type: ${nodeType}`);
|
||||
}
|
||||
}
|
||||
function checkForObjectLike(node, properties, nodeType = node.type) {
|
||||
const [openingToken, closingToken] = getBraceToken(node, nodeType);
|
||||
if (!openingToken || !closingToken) return;
|
||||
if (properties.length === 0) {
|
||||
checkSpaceInEmptyObjectLike(node, openingToken, closingToken, nodeType);
|
||||
return;
|
||||
}
|
||||
validateBraceSpacing(node, openingToken, closingToken, nodeType);
|
||||
}
|
||||
return {
|
||||
ObjectPattern(node) {
|
||||
checkForObjectLike(node, node.properties);
|
||||
},
|
||||
ObjectExpression(node) {
|
||||
checkForObjectLike(node, node.properties);
|
||||
},
|
||||
ImportDeclaration(node) {
|
||||
if (node.attributes) checkForObjectLike(node, node.attributes, "ImportAttributes");
|
||||
const firstSpecifierIndex = node.specifiers.findIndex((specifier) => specifier.type === "ImportSpecifier");
|
||||
if (firstSpecifierIndex === -1) {
|
||||
checkForObjectLike(node, []);
|
||||
return;
|
||||
}
|
||||
checkForObjectLike(node, node.specifiers.slice(firstSpecifierIndex));
|
||||
},
|
||||
ExportNamedDeclaration(node) {
|
||||
checkForObjectLike(node, node.specifiers);
|
||||
if (node.attributes) checkForObjectLike(node, node.attributes, "ImportAttributes");
|
||||
},
|
||||
ExportAllDeclaration(node) {
|
||||
if (node.attributes) checkForObjectLike(node, node.attributes, "ImportAttributes");
|
||||
},
|
||||
TSMappedType(node) {
|
||||
validateBraceSpacing(node, sourceCode.getFirstToken(node), sourceCode.getLastToken(node));
|
||||
},
|
||||
TSTypeLiteral(node) {
|
||||
checkForObjectLike(node, node.members);
|
||||
},
|
||||
TSInterfaceBody(node) {
|
||||
checkForObjectLike(node, node.body);
|
||||
},
|
||||
TSEnumBody(node) {
|
||||
checkForObjectLike(node, node.members);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { object_curly_spacing_default as t };
|
||||
Generated
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var object_property_newline_default = createRule({
|
||||
name: "object-property-newline",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce placing object properties on separate lines" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: { allowAllPropertiesOnSameLine: { type: "boolean" } },
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{ allowAllPropertiesOnSameLine: false }],
|
||||
messages: {
|
||||
propertiesOnNewlineAll: "Object properties must go on a new line if they aren't all on the same line.",
|
||||
propertiesOnNewline: "Object properties must go on a new line."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const allowSameLine = options.allowAllPropertiesOnSameLine;
|
||||
const messageId = allowSameLine ? "propertiesOnNewlineAll" : "propertiesOnNewline";
|
||||
const sourceCode = context.sourceCode;
|
||||
function check(node, children) {
|
||||
if (allowSameLine) {
|
||||
if (children.length > 1) {
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(sourceCode.getFirstToken(children[0]), sourceCode.getLastToken(children[children.length - 1]))) return;
|
||||
}
|
||||
}
|
||||
for (let i = 1; i < children.length; i++) {
|
||||
const lastTokenOfPreviousProperty = sourceCode.getLastToken(children[i - 1]);
|
||||
const firstTokenOfCurrentProperty = sourceCode.getFirstToken(children[i]);
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(lastTokenOfPreviousProperty, firstTokenOfCurrentProperty)) context.report({
|
||||
node,
|
||||
loc: firstTokenOfCurrentProperty.loc,
|
||||
messageId,
|
||||
fix(fixer) {
|
||||
const rangeAfterComma = [sourceCode.getTokenBefore(firstTokenOfCurrentProperty).range[1], firstTokenOfCurrentProperty.range[0]];
|
||||
if (sourceCode.text.slice(rangeAfterComma[0], rangeAfterComma[1]).trim()) return null;
|
||||
return fixer.replaceTextRange(rangeAfterComma, "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return {
|
||||
ObjectExpression(node) {
|
||||
check(node, node.properties);
|
||||
},
|
||||
TSTypeLiteral(node) {
|
||||
check(node, node.members);
|
||||
},
|
||||
TSInterfaceBody(node) {
|
||||
check(node, node.body);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { object_property_newline_default as t };
|
||||
Generated
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var one_var_declaration_per_line_default = createRule({
|
||||
name: "one-var-declaration-per-line",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require or disallow newlines around variable declarations" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["always", "initializations"]
|
||||
}],
|
||||
defaultOptions: ["initializations"],
|
||||
messages: { expectVarOnNewline: "Expected variable declaration to be on a new line." }
|
||||
},
|
||||
create(context, [style]) {
|
||||
const { sourceCode } = context;
|
||||
const always = style === "always";
|
||||
function isForTypeSpecifier(keyword) {
|
||||
return keyword === "ForStatement" || keyword === "ForInStatement" || keyword === "ForOfStatement";
|
||||
}
|
||||
function checkForNewLine(node) {
|
||||
if (isForTypeSpecifier(node.parent.type)) return;
|
||||
const declarations = node.declarations;
|
||||
for (let i = 1; i < declarations.length; i++) {
|
||||
const prev = declarations[i - 1];
|
||||
const current = declarations[i];
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(prev, current)) {
|
||||
if (always || prev.init || current.init) context.report({
|
||||
node,
|
||||
messageId: "expectVarOnNewline",
|
||||
loc: current.loc,
|
||||
fix: (fixer) => {
|
||||
const tokenBefore = sourceCode.getTokenBefore(current);
|
||||
return fixer.insertTextAfterRange([tokenBefore.range[1], current.range[0]], "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return { VariableDeclaration: checkForNewLine };
|
||||
}
|
||||
});
|
||||
export { one_var_declaration_per_line_default as t };
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
import { T as createGlobalLinebreakMatcher, f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var operator_linebreak_default = createRule({
|
||||
name: "operator-linebreak",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent linebreak style for operators" },
|
||||
fixable: "code",
|
||||
schema: [{ oneOf: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"after",
|
||||
"before",
|
||||
"none"
|
||||
]
|
||||
}, { type: "null" }] }, {
|
||||
type: "object",
|
||||
properties: { overrides: {
|
||||
type: "object",
|
||||
additionalProperties: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"after",
|
||||
"before",
|
||||
"none",
|
||||
"ignore"
|
||||
]
|
||||
}
|
||||
} },
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [],
|
||||
messages: {
|
||||
operatorAtBeginning: "'{{operator}}' should be placed at the beginning of the line.",
|
||||
operatorAtEnd: "'{{operator}}' should be placed at the end of the line.",
|
||||
badLinebreak: "Bad line breaking before and after '{{operator}}'.",
|
||||
noLinebreak: "There should be no line break before or after '{{operator}}'."
|
||||
}
|
||||
},
|
||||
create(context) {
|
||||
const usedDefaultGlobal = !context.options[0];
|
||||
const globalStyle = context.options[0] || "after";
|
||||
const options = context.options[1] || {};
|
||||
const styleOverrides = options.overrides ? Object.assign({}, options.overrides) : {};
|
||||
if (usedDefaultGlobal && !styleOverrides["?"]) styleOverrides["?"] = "before";
|
||||
if (usedDefaultGlobal && !styleOverrides[":"]) styleOverrides[":"] = "before";
|
||||
const sourceCode = context.sourceCode;
|
||||
function getFixer(operatorToken, desiredStyle) {
|
||||
return (fixer) => {
|
||||
const tokenBefore = sourceCode.getTokenBefore(operatorToken);
|
||||
const tokenAfter = sourceCode.getTokenAfter(operatorToken);
|
||||
const textBefore = sourceCode.text.slice(tokenBefore.range[1], operatorToken.range[0]);
|
||||
const textAfter = sourceCode.text.slice(operatorToken.range[1], tokenAfter.range[0]);
|
||||
const hasLinebreakBefore = !(0, import_ast_utils.isTokenOnSameLine)(tokenBefore, operatorToken);
|
||||
const hasLinebreakAfter = !(0, import_ast_utils.isTokenOnSameLine)(operatorToken, tokenAfter);
|
||||
let newTextBefore, newTextAfter;
|
||||
if (hasLinebreakBefore !== hasLinebreakAfter && desiredStyle !== "none") {
|
||||
if (sourceCode.getTokenBefore(operatorToken, { includeComments: true }) !== tokenBefore && sourceCode.getTokenAfter(operatorToken, { includeComments: true }) !== tokenAfter) return null;
|
||||
newTextBefore = textAfter;
|
||||
newTextAfter = textBefore;
|
||||
} else {
|
||||
const LINEBREAK_REGEX = createGlobalLinebreakMatcher();
|
||||
newTextBefore = desiredStyle === "before" || textBefore.trim() ? textBefore : textBefore.replace(LINEBREAK_REGEX, "");
|
||||
newTextAfter = desiredStyle === "after" || textAfter.trim() ? textAfter : textAfter.replace(LINEBREAK_REGEX, "");
|
||||
if (newTextBefore === textBefore && newTextAfter === textAfter) return null;
|
||||
}
|
||||
if (newTextAfter === "" && tokenAfter.type === "Punctuator" && "+-".includes(operatorToken.value) && tokenAfter.value === operatorToken.value) newTextAfter += " ";
|
||||
return fixer.replaceTextRange([tokenBefore.range[1], tokenAfter.range[0]], newTextBefore + operatorToken.value + newTextAfter);
|
||||
};
|
||||
}
|
||||
function validateNode(node, rightSide, operator) {
|
||||
const operatorToken = sourceCode.getTokenBefore(rightSide, (token) => token.value === operator);
|
||||
const leftToken = sourceCode.getTokenBefore(operatorToken);
|
||||
const rightToken = sourceCode.getTokenAfter(operatorToken);
|
||||
const operatorStyleOverride = styleOverrides[operator];
|
||||
const style = operatorStyleOverride || globalStyle;
|
||||
const fix = getFixer(operatorToken, style);
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(leftToken, operatorToken) && (0, import_ast_utils.isTokenOnSameLine)(operatorToken, rightToken)) {} else if (operatorStyleOverride !== "ignore" && !(0, import_ast_utils.isTokenOnSameLine)(leftToken, operatorToken) && !(0, import_ast_utils.isTokenOnSameLine)(operatorToken, rightToken)) context.report({
|
||||
node,
|
||||
loc: operatorToken.loc,
|
||||
messageId: "badLinebreak",
|
||||
data: { operator },
|
||||
fix
|
||||
});
|
||||
else if (style === "before" && (0, import_ast_utils.isTokenOnSameLine)(leftToken, operatorToken)) context.report({
|
||||
node,
|
||||
loc: operatorToken.loc,
|
||||
messageId: "operatorAtBeginning",
|
||||
data: { operator },
|
||||
fix
|
||||
});
|
||||
else if (style === "after" && (0, import_ast_utils.isTokenOnSameLine)(operatorToken, rightToken)) context.report({
|
||||
node,
|
||||
loc: operatorToken.loc,
|
||||
messageId: "operatorAtEnd",
|
||||
data: { operator },
|
||||
fix
|
||||
});
|
||||
else if (style === "none") context.report({
|
||||
node,
|
||||
loc: operatorToken.loc,
|
||||
messageId: "noLinebreak",
|
||||
data: { operator },
|
||||
fix
|
||||
});
|
||||
}
|
||||
function validateBinaryExpression(node) {
|
||||
validateNode(node, node.right, node.operator);
|
||||
}
|
||||
return {
|
||||
BinaryExpression: validateBinaryExpression,
|
||||
LogicalExpression: validateBinaryExpression,
|
||||
AssignmentExpression: validateBinaryExpression,
|
||||
VariableDeclarator(node) {
|
||||
if (node.init) validateNode(node, node.init, "=");
|
||||
},
|
||||
PropertyDefinition(node) {
|
||||
if (node.value) validateNode(node, node.value, "=");
|
||||
},
|
||||
AccessorProperty(node) {
|
||||
if (node.value) validateNode(node, node.value, "=");
|
||||
},
|
||||
ConditionalExpression(node) {
|
||||
validateNode(node, node.consequent, "?");
|
||||
validateNode(node, node.alternate, ":");
|
||||
},
|
||||
TSImportEqualsDeclaration(node) {
|
||||
validateNode(node, node.moduleReference, "=");
|
||||
},
|
||||
TSTypeAliasDeclaration(node) {
|
||||
validateNode(node, node.typeAnnotation, "=");
|
||||
},
|
||||
TSConditionalType(node) {
|
||||
validateNode(node, node.trueType, "?");
|
||||
validateNode(node, node.falseType, ":");
|
||||
},
|
||||
TSIntersectionType(node) {
|
||||
const { types } = node;
|
||||
for (let idx = 0; idx < types.length - 1; idx++) validateNode(types[idx], types[idx + 1], "&");
|
||||
},
|
||||
TSUnionType(node) {
|
||||
const { types } = node;
|
||||
for (let idx = 0; idx < types.length - 1; idx++) validateNode(types[idx], types[idx + 1], "|");
|
||||
},
|
||||
TSTypeParameter(node) {
|
||||
if (!node.default) return;
|
||||
validateNode(node, node.default, "=");
|
||||
},
|
||||
TSEnumMember(node) {
|
||||
if (!node.initializer) return;
|
||||
validateNode(node, node.initializer, "=");
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { operator_linebreak_default as t };
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
const OPTION_ENUMS = [
|
||||
"always",
|
||||
"never",
|
||||
"start",
|
||||
"end"
|
||||
];
|
||||
var padded_blocks_default = createRule({
|
||||
name: "padded-blocks",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require or disallow padding within blocks" },
|
||||
fixable: "whitespace",
|
||||
schema: [{ oneOf: [{
|
||||
type: "string",
|
||||
enum: OPTION_ENUMS
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
blocks: {
|
||||
type: "string",
|
||||
enum: OPTION_ENUMS
|
||||
},
|
||||
switches: {
|
||||
type: "string",
|
||||
enum: OPTION_ENUMS
|
||||
},
|
||||
classes: {
|
||||
type: "string",
|
||||
enum: OPTION_ENUMS
|
||||
}
|
||||
},
|
||||
additionalProperties: false,
|
||||
minProperties: 1
|
||||
}] }, {
|
||||
type: "object",
|
||||
properties: { allowSingleLineBlocks: { type: "boolean" } },
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: ["always", { allowSingleLineBlocks: false }],
|
||||
messages: {
|
||||
missingPadBlock: "Block must be padded by blank lines.",
|
||||
extraPadBlock: "Block must not be padded by blank lines."
|
||||
}
|
||||
},
|
||||
create(context, [typeOptions, { allowSingleLineBlocks } = {}]) {
|
||||
const options = typeof typeOptions === "string" ? {
|
||||
blocks: typeOptions,
|
||||
switches: typeOptions,
|
||||
classes: typeOptions
|
||||
} : typeOptions;
|
||||
const sourceCode = context.sourceCode;
|
||||
function getOpenBrace(node) {
|
||||
if (node.type === "SwitchStatement") return sourceCode.getTokenBefore(node.cases[0]);
|
||||
if (node.type === "StaticBlock") return sourceCode.getFirstToken(node, { skip: 1 });
|
||||
return sourceCode.getFirstToken(node);
|
||||
}
|
||||
function isComment(node) {
|
||||
return node.type === "Line" || node.type === "Block";
|
||||
}
|
||||
function isPaddingBetweenTokens(first, second) {
|
||||
return second.loc.start.line - first.loc.end.line >= 2;
|
||||
}
|
||||
function getFirstBlockToken(token) {
|
||||
let prev;
|
||||
let first = token;
|
||||
do {
|
||||
prev = first;
|
||||
first = sourceCode.getTokenAfter(first, { includeComments: true });
|
||||
} while (isComment(first) && (0, import_ast_utils.isTokenOnSameLine)(prev, first));
|
||||
return first;
|
||||
}
|
||||
function getLastBlockToken(token) {
|
||||
let last = token;
|
||||
let next;
|
||||
do {
|
||||
next = last;
|
||||
last = sourceCode.getTokenBefore(last, { includeComments: true });
|
||||
} while (isComment(last) && (0, import_ast_utils.isTokenOnSameLine)(last, next));
|
||||
return last;
|
||||
}
|
||||
function requirePaddingFor(node) {
|
||||
switch (node.type) {
|
||||
case "BlockStatement":
|
||||
case "StaticBlock": return options.blocks;
|
||||
case "SwitchStatement": return options.switches;
|
||||
case "ClassBody": return options.classes;
|
||||
default: throw new Error("unreachable");
|
||||
}
|
||||
}
|
||||
function checkPadding(node) {
|
||||
const firstBlockToken = getFirstBlockToken(getOpenBrace(node));
|
||||
const tokenBeforeFirst = sourceCode.getTokenBefore(firstBlockToken, { includeComments: true });
|
||||
const lastBlockToken = getLastBlockToken(sourceCode.getLastToken(node));
|
||||
const tokenAfterLast = sourceCode.getTokenAfter(lastBlockToken, { includeComments: true });
|
||||
const blockHasTopPadding = isPaddingBetweenTokens(tokenBeforeFirst, firstBlockToken);
|
||||
const blockHasBottomPadding = isPaddingBetweenTokens(lastBlockToken, tokenAfterLast);
|
||||
if (allowSingleLineBlocks && (0, import_ast_utils.isTokenOnSameLine)(tokenBeforeFirst, tokenAfterLast)) return;
|
||||
const requiredPadding = requirePaddingFor(node);
|
||||
if (blockHasTopPadding) {
|
||||
if (requiredPadding === "never" || requiredPadding === "end") context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: tokenBeforeFirst.loc.start,
|
||||
end: firstBlockToken.loc.start
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange([tokenBeforeFirst.range[1], firstBlockToken.range[0] - firstBlockToken.loc.start.column], "\n");
|
||||
},
|
||||
messageId: "extraPadBlock"
|
||||
});
|
||||
} else if (requiredPadding === "always" || requiredPadding === "start") context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: tokenBeforeFirst.loc.start,
|
||||
end: firstBlockToken.loc.start
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(tokenBeforeFirst, "\n");
|
||||
},
|
||||
messageId: "missingPadBlock"
|
||||
});
|
||||
if (blockHasBottomPadding) {
|
||||
if (requiredPadding === "never" || requiredPadding === "start") context.report({
|
||||
node,
|
||||
loc: {
|
||||
end: tokenAfterLast.loc.start,
|
||||
start: lastBlockToken.loc.end
|
||||
},
|
||||
messageId: "extraPadBlock",
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange([lastBlockToken.range[1], tokenAfterLast.range[0] - tokenAfterLast.loc.start.column], "\n");
|
||||
}
|
||||
});
|
||||
} else if (requiredPadding === "always" || requiredPadding === "end") context.report({
|
||||
node,
|
||||
loc: {
|
||||
end: tokenAfterLast.loc.start,
|
||||
start: lastBlockToken.loc.end
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(tokenAfterLast, "\n");
|
||||
},
|
||||
messageId: "missingPadBlock"
|
||||
});
|
||||
}
|
||||
const rule = {};
|
||||
if (Object.hasOwn(options, "switches")) rule.SwitchStatement = function(node) {
|
||||
if (node.cases.length === 0) return;
|
||||
checkPadding(node);
|
||||
};
|
||||
if (Object.hasOwn(options, "blocks")) {
|
||||
rule.BlockStatement = function(node) {
|
||||
if (node.body.length === 0) return;
|
||||
checkPadding(node);
|
||||
};
|
||||
rule.StaticBlock = rule.BlockStatement;
|
||||
}
|
||||
if (Object.hasOwn(options, "classes")) rule.ClassBody = function(node) {
|
||||
if (node.body.length === 0) return;
|
||||
checkPadding(node);
|
||||
};
|
||||
return rule;
|
||||
}
|
||||
});
|
||||
export { padded_blocks_default as t };
|
||||
Generated
Vendored
+387
@@ -0,0 +1,387 @@
|
||||
import { K as isSingleLine, Y as isTopLevelExpressionStatement, Z as skipChainExpression, f as createRule, g as import_ast_utils, m as AST_NODE_TYPES, x as LINEBREAKS } from "../utils.js";
|
||||
const CJS_EXPORT = /^(?:module\s*\.\s*)?exports(?:\s*\.|\s*\[|$)/u;
|
||||
const CJS_IMPORT = /^require\(/u;
|
||||
const LT = `[${Array.from(LINEBREAKS).join("")}]`;
|
||||
const PADDING_LINE_SEQUENCE = new RegExp(String.raw`^(\s*?${LT})\s*${LT}(\s*;?)$`, "u");
|
||||
function isSelectorOption(option) {
|
||||
return typeof option === "object" && !Array.isArray(option);
|
||||
}
|
||||
function newKeywordTester(type, keyword) {
|
||||
return { test(node, sourceCode) {
|
||||
const isSameKeyword = sourceCode.getFirstToken(node)?.value === keyword;
|
||||
const isSameType = Array.isArray(type) ? type.includes(node.type) : type === node.type;
|
||||
return isSameKeyword && isSameType;
|
||||
} };
|
||||
}
|
||||
function newNodeTypeTester(type) {
|
||||
return { test: (node) => node.type === type };
|
||||
}
|
||||
function isIIFEStatement(node) {
|
||||
if (node.type === AST_NODE_TYPES.ExpressionStatement) {
|
||||
let expression = skipChainExpression(node.expression);
|
||||
if (expression.type === AST_NODE_TYPES.UnaryExpression) expression = skipChainExpression(expression.argument);
|
||||
if (expression.type === AST_NODE_TYPES.CallExpression) {
|
||||
let node = expression.callee;
|
||||
while (node.type === AST_NODE_TYPES.SequenceExpression) node = node.expressions[node.expressions.length - 1];
|
||||
return (0, import_ast_utils.isFunction)(node);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isCJSRequire(node) {
|
||||
if (node.type === AST_NODE_TYPES.VariableDeclaration) {
|
||||
const declaration = node.declarations[0];
|
||||
if (declaration?.init) {
|
||||
let call = declaration?.init;
|
||||
while (call.type === AST_NODE_TYPES.MemberExpression) call = call.object;
|
||||
if (call.type === AST_NODE_TYPES.CallExpression && call.callee.type === AST_NODE_TYPES.Identifier) return call.callee.name === "require";
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isBlockLikeStatement(node, sourceCode) {
|
||||
if (node.type === AST_NODE_TYPES.DoWhileStatement && node.body.type === AST_NODE_TYPES.BlockStatement) return true;
|
||||
if (isIIFEStatement(node)) return true;
|
||||
const lastToken = sourceCode.getLastToken(node, import_ast_utils.isNotSemicolonToken);
|
||||
const belongingNode = lastToken && (0, import_ast_utils.isClosingBraceToken)(lastToken) ? sourceCode.getNodeByRangeIndex(lastToken.range[0]) : null;
|
||||
return !!belongingNode && (belongingNode.type === AST_NODE_TYPES.BlockStatement || belongingNode.type === AST_NODE_TYPES.SwitchStatement);
|
||||
}
|
||||
function isDirective(node, sourceCode) {
|
||||
return isTopLevelExpressionStatement(node) && node.expression.type === AST_NODE_TYPES.Literal && typeof node.expression.value === "string" && !(0, import_ast_utils.isParenthesized)(node.expression, sourceCode);
|
||||
}
|
||||
function isDirectivePrologue(node, sourceCode) {
|
||||
if (isDirective(node, sourceCode) && node.parent && "body" in node.parent && Array.isArray(node.parent.body)) {
|
||||
for (const sibling of node.parent.body) {
|
||||
if (sibling === node) break;
|
||||
if (!isDirective(sibling, sourceCode)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isCJSExport(node) {
|
||||
if (node.type === AST_NODE_TYPES.ExpressionStatement) {
|
||||
const expression = node.expression;
|
||||
if (expression.type === AST_NODE_TYPES.AssignmentExpression) {
|
||||
let left = expression.left;
|
||||
if (left.type === AST_NODE_TYPES.MemberExpression) {
|
||||
while (left.object.type === AST_NODE_TYPES.MemberExpression) left = left.object;
|
||||
return left.object.type === AST_NODE_TYPES.Identifier && (left.object.name === "exports" || left.object.name === "module" && left.property.type === AST_NODE_TYPES.Identifier && left.property.name === "exports");
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isExpression(node, sourceCode) {
|
||||
return node.type === AST_NODE_TYPES.ExpressionStatement && !isDirectivePrologue(node, sourceCode);
|
||||
}
|
||||
function getActualLastToken(node, sourceCode) {
|
||||
const semiToken = sourceCode.getLastToken(node);
|
||||
const prevToken = sourceCode.getTokenBefore(semiToken);
|
||||
const nextToken = sourceCode.getTokenAfter(semiToken);
|
||||
return prevToken && nextToken && prevToken.range[0] >= node.range[0] && (0, import_ast_utils.isSemicolonToken)(semiToken) && !(0, import_ast_utils.isTokenOnSameLine)(prevToken, semiToken) && (0, import_ast_utils.isTokenOnSameLine)(semiToken, nextToken) ? prevToken : semiToken;
|
||||
}
|
||||
function replacerToRemovePaddingLines(_, trailingSpaces, indentSpaces) {
|
||||
return trailingSpaces + indentSpaces;
|
||||
}
|
||||
function getReportLoc(node, sourceCode) {
|
||||
if (isSingleLine(node)) return node.loc;
|
||||
const line = node.loc.start.line;
|
||||
return {
|
||||
start: node.loc.start,
|
||||
end: {
|
||||
line,
|
||||
column: sourceCode.lines[line - 1].length
|
||||
}
|
||||
};
|
||||
}
|
||||
function verifyForAny() {}
|
||||
function verifyForNever(context, _, nextNode, paddingLines) {
|
||||
if (paddingLines.length === 0) return;
|
||||
context.report({
|
||||
node: nextNode,
|
||||
messageId: "unexpectedBlankLine",
|
||||
loc: getReportLoc(nextNode, context.sourceCode),
|
||||
fix(fixer) {
|
||||
if (paddingLines.length >= 2) return null;
|
||||
const prevToken = paddingLines[0][0];
|
||||
const nextToken = paddingLines[0][1];
|
||||
const start = prevToken.range[1];
|
||||
const end = nextToken.range[0];
|
||||
const text = context.sourceCode.text.slice(start, end).replace(PADDING_LINE_SEQUENCE, replacerToRemovePaddingLines);
|
||||
return fixer.replaceTextRange([start, end], text);
|
||||
}
|
||||
});
|
||||
}
|
||||
function verifyForAlways(context, prevNode, nextNode, paddingLines) {
|
||||
if (paddingLines.length > 0) return;
|
||||
context.report({
|
||||
node: nextNode,
|
||||
messageId: "expectedBlankLine",
|
||||
loc: getReportLoc(nextNode, context.sourceCode),
|
||||
fix(fixer) {
|
||||
const sourceCode = context.sourceCode;
|
||||
let prevToken = getActualLastToken(prevNode, sourceCode);
|
||||
const nextToken = sourceCode.getFirstTokenBetween(prevToken, nextNode, {
|
||||
includeComments: true,
|
||||
filter(token) {
|
||||
if ((0, import_ast_utils.isTokenOnSameLine)(prevToken, token)) {
|
||||
prevToken = token;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}) || nextNode;
|
||||
const insertText = (0, import_ast_utils.isTokenOnSameLine)(prevToken, nextToken) ? "\n\n" : "\n";
|
||||
return fixer.insertTextAfter(prevToken, insertText);
|
||||
}
|
||||
});
|
||||
}
|
||||
const PaddingTypes = {
|
||||
any: { verify: verifyForAny },
|
||||
never: { verify: verifyForNever },
|
||||
always: { verify: verifyForAlways }
|
||||
};
|
||||
const MaybeMultilineStatementType = {
|
||||
"block-like": { test: isBlockLikeStatement },
|
||||
"expression": { test: isExpression },
|
||||
"return": newKeywordTester(AST_NODE_TYPES.ReturnStatement, "return"),
|
||||
"export": newKeywordTester([
|
||||
AST_NODE_TYPES.ExportAllDeclaration,
|
||||
AST_NODE_TYPES.ExportDefaultDeclaration,
|
||||
AST_NODE_TYPES.ExportNamedDeclaration
|
||||
], "export"),
|
||||
"var": newKeywordTester(AST_NODE_TYPES.VariableDeclaration, "var"),
|
||||
"let": newKeywordTester(AST_NODE_TYPES.VariableDeclaration, "let"),
|
||||
"const": newKeywordTester(AST_NODE_TYPES.VariableDeclaration, "const"),
|
||||
"using": { test: (node) => node.type === "VariableDeclaration" && (node.kind === "using" || node.kind === "await using") },
|
||||
"type": newKeywordTester(AST_NODE_TYPES.TSTypeAliasDeclaration, "type")
|
||||
};
|
||||
const StatementTypes = {
|
||||
"*": { test: () => true },
|
||||
"exports": { test: isCJSExport },
|
||||
"require": { test: isCJSRequire },
|
||||
"directive": { test: isDirectivePrologue },
|
||||
"iife": { test: isIIFEStatement },
|
||||
"block": newNodeTypeTester(AST_NODE_TYPES.BlockStatement),
|
||||
"empty": newNodeTypeTester(AST_NODE_TYPES.EmptyStatement),
|
||||
"function": newNodeTypeTester(AST_NODE_TYPES.FunctionDeclaration),
|
||||
"ts-method": newNodeTypeTester(AST_NODE_TYPES.TSMethodSignature),
|
||||
"break": newKeywordTester(AST_NODE_TYPES.BreakStatement, "break"),
|
||||
"case": newKeywordTester(AST_NODE_TYPES.SwitchCase, "case"),
|
||||
"class": newKeywordTester(AST_NODE_TYPES.ClassDeclaration, "class"),
|
||||
"continue": newKeywordTester(AST_NODE_TYPES.ContinueStatement, "continue"),
|
||||
"debugger": newKeywordTester(AST_NODE_TYPES.DebuggerStatement, "debugger"),
|
||||
"default": newKeywordTester([AST_NODE_TYPES.SwitchCase, AST_NODE_TYPES.ExportDefaultDeclaration], "default"),
|
||||
"do": newKeywordTester(AST_NODE_TYPES.DoWhileStatement, "do"),
|
||||
"for": newKeywordTester([
|
||||
AST_NODE_TYPES.ForStatement,
|
||||
AST_NODE_TYPES.ForInStatement,
|
||||
AST_NODE_TYPES.ForOfStatement
|
||||
], "for"),
|
||||
"if": newKeywordTester(AST_NODE_TYPES.IfStatement, "if"),
|
||||
"import": newKeywordTester(AST_NODE_TYPES.ImportDeclaration, "import"),
|
||||
"switch": newKeywordTester(AST_NODE_TYPES.SwitchStatement, "switch"),
|
||||
"throw": newKeywordTester(AST_NODE_TYPES.ThrowStatement, "throw"),
|
||||
"try": newKeywordTester(AST_NODE_TYPES.TryStatement, "try"),
|
||||
"while": newKeywordTester([AST_NODE_TYPES.WhileStatement, AST_NODE_TYPES.DoWhileStatement], "while"),
|
||||
"with": newKeywordTester(AST_NODE_TYPES.WithStatement, "with"),
|
||||
"cjs-export": { test: (node, sourceCode) => node.type === "ExpressionStatement" && node.expression.type === "AssignmentExpression" && CJS_EXPORT.test(sourceCode.getText(node.expression.left)) },
|
||||
"cjs-import": { test: (node, sourceCode) => node.type === "VariableDeclaration" && node.declarations.length > 0 && Boolean(node.declarations[0].init) && CJS_IMPORT.test(sourceCode.getText(node.declarations[0].init)) },
|
||||
"enum": newKeywordTester(AST_NODE_TYPES.TSEnumDeclaration, "enum"),
|
||||
"interface": newKeywordTester(AST_NODE_TYPES.TSInterfaceDeclaration, "interface"),
|
||||
"function-overload": newNodeTypeTester(AST_NODE_TYPES.TSDeclareFunction),
|
||||
...Object.fromEntries(Object.entries(MaybeMultilineStatementType).flatMap(([key, value]) => [
|
||||
[key, value],
|
||||
[`singleline-${key}`, {
|
||||
...value,
|
||||
test: (node, sourceCode) => value.test(node, sourceCode) && isSingleLine(node)
|
||||
}],
|
||||
[`multiline-${key}`, {
|
||||
...value,
|
||||
test: (node, sourceCode) => value.test(node, sourceCode) && !isSingleLine(node)
|
||||
}]
|
||||
]))
|
||||
};
|
||||
var padding_line_between_statements_default = createRule({
|
||||
name: "padding-line-between-statements",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require or disallow padding lines between statements" },
|
||||
fixable: "whitespace",
|
||||
hasSuggestions: false,
|
||||
schema: {
|
||||
$defs: {
|
||||
paddingType: {
|
||||
type: "string",
|
||||
enum: Object.keys(PaddingTypes)
|
||||
},
|
||||
statementType: {
|
||||
type: "string",
|
||||
enum: Object.keys(StatementTypes)
|
||||
},
|
||||
selectorOption: {
|
||||
type: "object",
|
||||
properties: {
|
||||
selector: { type: "string" },
|
||||
lineMode: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"any",
|
||||
"singleline",
|
||||
"multiline"
|
||||
]
|
||||
}
|
||||
},
|
||||
required: ["selector"],
|
||||
additionalProperties: false
|
||||
},
|
||||
statementMatcher: { anyOf: [{ $ref: "#/$defs/statementType" }, { $ref: "#/$defs/selectorOption" }] },
|
||||
statementOption: { anyOf: [{ $ref: "#/$defs/statementMatcher" }, {
|
||||
type: "array",
|
||||
items: { $ref: "#/$defs/statementMatcher" },
|
||||
minItems: 1,
|
||||
uniqueItems: true,
|
||||
additionalItems: false
|
||||
}] }
|
||||
},
|
||||
type: "array",
|
||||
additionalItems: false,
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
blankLine: { $ref: "#/$defs/paddingType" },
|
||||
prev: { $ref: "#/$defs/statementOption" },
|
||||
next: { $ref: "#/$defs/statementOption" }
|
||||
},
|
||||
additionalProperties: false,
|
||||
required: [
|
||||
"blankLine",
|
||||
"prev",
|
||||
"next"
|
||||
]
|
||||
}
|
||||
},
|
||||
defaultOptions: [],
|
||||
messages: {
|
||||
unexpectedBlankLine: "Unexpected blank line before this statement.",
|
||||
expectedBlankLine: "Expected blank line before this statement."
|
||||
}
|
||||
},
|
||||
create(context, options) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const selectorMatchedNodes = /* @__PURE__ */ new Map();
|
||||
const pendingPairs = [];
|
||||
function collectSelectorOption(option) {
|
||||
if (Array.isArray(option)) {
|
||||
for (const item of option) collectSelectorOption(item);
|
||||
return;
|
||||
}
|
||||
if (!isSelectorOption(option)) return;
|
||||
selectorMatchedNodes.set(option.selector, /* @__PURE__ */ new Set());
|
||||
}
|
||||
for (const configure of options) {
|
||||
collectSelectorOption(configure.prev);
|
||||
collectSelectorOption(configure.next);
|
||||
}
|
||||
let scopeInfo = null;
|
||||
function enterScope() {
|
||||
scopeInfo = {
|
||||
upper: scopeInfo,
|
||||
prevNode: null
|
||||
};
|
||||
}
|
||||
function exitScope() {
|
||||
if (scopeInfo) scopeInfo = scopeInfo.upper;
|
||||
}
|
||||
function match(node, type) {
|
||||
let innerStatementNode = node;
|
||||
while (innerStatementNode.type === AST_NODE_TYPES.LabeledStatement) innerStatementNode = innerStatementNode.body;
|
||||
if (Array.isArray(type)) return type.some(match.bind(null, innerStatementNode));
|
||||
if (isSelectorOption(type)) {
|
||||
if (!selectorMatchedNodes.get(type.selector)?.has(innerStatementNode)) return false;
|
||||
const lineMode = type.lineMode;
|
||||
if (lineMode === "singleline") return isSingleLine(innerStatementNode);
|
||||
else if (lineMode === "multiline") return !isSingleLine(innerStatementNode);
|
||||
return true;
|
||||
} else return StatementTypes[type].test(innerStatementNode, sourceCode);
|
||||
}
|
||||
function getPaddingType(prevNode, nextNode) {
|
||||
for (let i = options.length - 1; i >= 0; --i) {
|
||||
const configure = options[i];
|
||||
if (match(prevNode, configure.prev) && match(nextNode, configure.next)) return PaddingTypes[configure.blankLine];
|
||||
}
|
||||
return PaddingTypes.any;
|
||||
}
|
||||
function getPaddingLineSequences(prevNode, nextNode) {
|
||||
const pairs = [];
|
||||
let prevToken = getActualLastToken(prevNode, sourceCode);
|
||||
if (nextNode.loc.start.line - prevToken.loc.end.line >= 2) do {
|
||||
const token = sourceCode.getTokenAfter(prevToken, { includeComments: true });
|
||||
if (token.loc.start.line - prevToken.loc.end.line >= 2) pairs.push([prevToken, token]);
|
||||
prevToken = token;
|
||||
} while (prevToken.range[0] < nextNode.range[0]);
|
||||
return pairs;
|
||||
}
|
||||
function verify(node) {
|
||||
if (!node.parent || ![
|
||||
AST_NODE_TYPES.BlockStatement,
|
||||
AST_NODE_TYPES.Program,
|
||||
AST_NODE_TYPES.StaticBlock,
|
||||
AST_NODE_TYPES.SwitchCase,
|
||||
AST_NODE_TYPES.SwitchStatement,
|
||||
AST_NODE_TYPES.TSInterfaceBody,
|
||||
AST_NODE_TYPES.TSModuleBlock,
|
||||
AST_NODE_TYPES.TSTypeLiteral
|
||||
].includes(node.parent.type)) return;
|
||||
const prevNode = scopeInfo.prevNode;
|
||||
if (prevNode) pendingPairs.push({
|
||||
prevNode,
|
||||
nextNode: node
|
||||
});
|
||||
scopeInfo.prevNode = node;
|
||||
}
|
||||
function verifyPendingPairs() {
|
||||
for (const { prevNode, nextNode } of pendingPairs) {
|
||||
const type = getPaddingType(prevNode, nextNode);
|
||||
const paddingLines = getPaddingLineSequences(prevNode, nextNode);
|
||||
type.verify(context, prevNode, nextNode, paddingLines);
|
||||
}
|
||||
}
|
||||
function verifyThenEnterScope(node) {
|
||||
verify(node);
|
||||
enterScope();
|
||||
}
|
||||
const selectorMatchListeners = Object.fromEntries(Array.from(selectorMatchedNodes.keys(), (selector) => [selector, (node) => {
|
||||
selectorMatchedNodes.get(selector)?.add(node);
|
||||
}]));
|
||||
return {
|
||||
"Program": enterScope,
|
||||
"Program:exit": () => {
|
||||
verifyPendingPairs();
|
||||
exitScope();
|
||||
},
|
||||
"BlockStatement": enterScope,
|
||||
"BlockStatement:exit": exitScope,
|
||||
"SwitchStatement": enterScope,
|
||||
"SwitchStatement:exit": exitScope,
|
||||
"SwitchCase": verifyThenEnterScope,
|
||||
"SwitchCase:exit": exitScope,
|
||||
"StaticBlock": enterScope,
|
||||
"StaticBlock:exit": exitScope,
|
||||
"TSInterfaceBody": enterScope,
|
||||
"TSInterfaceBody:exit": exitScope,
|
||||
"TSModuleBlock": enterScope,
|
||||
"TSModuleBlock:exit": exitScope,
|
||||
"TSTypeLiteral": enterScope,
|
||||
"TSTypeLiteral:exit": exitScope,
|
||||
"TSDeclareFunction": verifyThenEnterScope,
|
||||
"TSDeclareFunction:exit": exitScope,
|
||||
"TSMethodSignature": verifyThenEnterScope,
|
||||
"TSMethodSignature:exit": exitScope,
|
||||
":statement": verify,
|
||||
...selectorMatchListeners
|
||||
};
|
||||
}
|
||||
});
|
||||
export { padding_line_between_statements_default as t };
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
import { H as isNumericLiteral, f as createRule, q as isStringLiteral, y as ES3_KEYWORDS } from "../utils.js";
|
||||
import { tokenize } from "espree";
|
||||
var quote_props_default = createRule({
|
||||
name: "quote-props",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require quotes around object literal, type literal, interfaces and enums property names" },
|
||||
fixable: "code",
|
||||
schema: { anyOf: [{
|
||||
type: "array",
|
||||
items: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"as-needed",
|
||||
"consistent",
|
||||
"consistent-as-needed"
|
||||
]
|
||||
}],
|
||||
minItems: 0,
|
||||
maxItems: 1
|
||||
}, {
|
||||
type: "array",
|
||||
items: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"as-needed",
|
||||
"consistent",
|
||||
"consistent-as-needed"
|
||||
]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
keywords: { type: "boolean" },
|
||||
unnecessary: { type: "boolean" },
|
||||
numbers: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
minItems: 0,
|
||||
maxItems: 2
|
||||
}] },
|
||||
defaultOptions: ["always"],
|
||||
messages: {
|
||||
requireQuotesDueToReservedWord: "Properties should be quoted as '{{property}}' is a reserved word.",
|
||||
inconsistentlyQuotedProperty: "Inconsistently quoted property '{{key}}' found.",
|
||||
unnecessarilyQuotedProperty: "Unnecessarily quoted property '{{property}}' found.",
|
||||
unquotedReservedProperty: "Unquoted reserved word '{{property}}' used as key.",
|
||||
unquotedNumericProperty: "Unquoted number literal '{{property}}' used as key.",
|
||||
unquotedPropertyFound: "Unquoted property '{{property}}' found.",
|
||||
redundantQuoting: "Properties shouldn't be quoted as all quotes are redundant."
|
||||
}
|
||||
},
|
||||
create(context, [MODE, options = {}]) {
|
||||
const { keywords: KEYWORDS = false, unnecessary: CHECK_UNNECESSARY = true, numbers: NUMBERS = false } = options;
|
||||
const sourceCode = context.sourceCode;
|
||||
function isKeyword(tokenStr) {
|
||||
return ES3_KEYWORDS.includes(tokenStr);
|
||||
}
|
||||
function areQuotesRedundant(rawKey, tokens, skipNumberLiterals = false) {
|
||||
return tokens.length === 1 && tokens[0].start === 0 && tokens[0].end === rawKey.length && ([
|
||||
"Identifier",
|
||||
"Keyword",
|
||||
"Null",
|
||||
"Boolean"
|
||||
].includes(tokens[0].type) || tokens[0].type === "Numeric" && !skipNumberLiterals && String(+tokens[0].value) === tokens[0].value);
|
||||
}
|
||||
function getUnquotedKey(key) {
|
||||
return key.type === "Identifier" ? key.name : key.value;
|
||||
}
|
||||
function getQuotedKey(key) {
|
||||
if (isStringLiteral(key)) return sourceCode.getText(key);
|
||||
return `"${key.type === "Identifier" ? key.name : key.value}"`;
|
||||
}
|
||||
function checkUnnecessaryQuotes(node) {
|
||||
if (node.type === "Property" && (node.method || node.computed || node.shorthand)) return;
|
||||
if (node.type !== "ImportAttribute" && node.computed) return;
|
||||
const key = node.type === "TSEnumMember" ? node.id : node.key;
|
||||
if (key.type === "Literal" && typeof key.value === "string") {
|
||||
let tokens;
|
||||
try {
|
||||
tokens = tokenize(key.value);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
if (tokens.length !== 1) return;
|
||||
if (isKeyword(tokens[0].value) && KEYWORDS) return;
|
||||
if (CHECK_UNNECESSARY && areQuotesRedundant(key.value, tokens, NUMBERS)) context.report({
|
||||
node,
|
||||
messageId: "unnecessarilyQuotedProperty",
|
||||
data: { property: key.value },
|
||||
fix: (fixer) => fixer.replaceText(key, getUnquotedKey(key))
|
||||
});
|
||||
} else if (KEYWORDS && key.type === "Identifier" && isKeyword(key.name)) context.report({
|
||||
node,
|
||||
messageId: "unquotedReservedProperty",
|
||||
data: { property: key.name },
|
||||
fix: (fixer) => fixer.replaceText(key, getQuotedKey(key))
|
||||
});
|
||||
else if (NUMBERS && isNumericLiteral(key)) context.report({
|
||||
node,
|
||||
messageId: "unquotedNumericProperty",
|
||||
data: { property: key.value },
|
||||
fix: (fixer) => fixer.replaceText(key, getQuotedKey(key))
|
||||
});
|
||||
}
|
||||
function checkOmittedQuotes(node) {
|
||||
if (node.type === "Property" && (node.method || node.computed || node.shorthand)) return;
|
||||
if (node.type !== "ImportAttribute" && node.computed) return;
|
||||
const key = node.type === "TSEnumMember" ? node.id : node.key;
|
||||
if (key.type === "Literal" && typeof key.value === "string") return;
|
||||
context.report({
|
||||
node,
|
||||
messageId: "unquotedPropertyFound",
|
||||
data: { property: key.name || key.value },
|
||||
fix: (fixer) => fixer.replaceText(key, getQuotedKey(key))
|
||||
});
|
||||
}
|
||||
function checkConsistencyForObject(properties, checkQuotesRedundancy) {
|
||||
checkConsistency(properties.filter((property) => property.type !== "SpreadElement" && property.key && !property.method && !property.computed && !property.shorthand), checkQuotesRedundancy);
|
||||
}
|
||||
function checkImportAttributes(attributes) {
|
||||
if (!attributes) return;
|
||||
if (MODE === "consistent") checkConsistency(attributes, false);
|
||||
if (MODE === "consistent-as-needed") checkConsistency(attributes, true);
|
||||
}
|
||||
function checkConsistency(properties, checkQuotesRedundancy) {
|
||||
const quotedProps = [];
|
||||
const unquotedProps = [];
|
||||
let keywordKeyName = null;
|
||||
let necessaryQuotes = false;
|
||||
properties.forEach((property) => {
|
||||
const key = property.key;
|
||||
if (key.type === "Literal" && typeof key.value === "string") {
|
||||
quotedProps.push(property);
|
||||
if (checkQuotesRedundancy) {
|
||||
let tokens;
|
||||
try {
|
||||
tokens = tokenize(key.value);
|
||||
} catch {
|
||||
necessaryQuotes = true;
|
||||
return;
|
||||
}
|
||||
necessaryQuotes = necessaryQuotes || !areQuotesRedundant(key.value, tokens) || KEYWORDS && isKeyword(tokens[0].value);
|
||||
}
|
||||
} else if (KEYWORDS && checkQuotesRedundancy && key.type === "Identifier" && isKeyword(key.name)) {
|
||||
unquotedProps.push(property);
|
||||
necessaryQuotes = true;
|
||||
keywordKeyName = key.name;
|
||||
} else unquotedProps.push(property);
|
||||
});
|
||||
if (checkQuotesRedundancy && quotedProps.length && !necessaryQuotes) quotedProps.forEach((property) => {
|
||||
const key = property.key;
|
||||
context.report({
|
||||
node: property,
|
||||
messageId: "redundantQuoting",
|
||||
fix: (fixer) => fixer.replaceText(key, getUnquotedKey(key))
|
||||
});
|
||||
});
|
||||
else if (unquotedProps.length && keywordKeyName) unquotedProps.forEach((property) => {
|
||||
context.report({
|
||||
node: property,
|
||||
messageId: "requireQuotesDueToReservedWord",
|
||||
data: { property: keywordKeyName },
|
||||
fix: (fixer) => fixer.replaceText(property.key, getQuotedKey(property.key))
|
||||
});
|
||||
});
|
||||
else if (quotedProps.length && unquotedProps.length) unquotedProps.forEach((property) => {
|
||||
context.report({
|
||||
node: property,
|
||||
messageId: "inconsistentlyQuotedProperty",
|
||||
data: { key: property.key.name || property.key.value },
|
||||
fix: (fixer) => fixer.replaceText(property.key, getQuotedKey(property.key))
|
||||
});
|
||||
});
|
||||
}
|
||||
return {
|
||||
Property(node) {
|
||||
if (MODE === "always" || !MODE) checkOmittedQuotes(node);
|
||||
if (MODE === "as-needed") checkUnnecessaryQuotes(node);
|
||||
},
|
||||
ObjectExpression(node) {
|
||||
if (MODE === "consistent") checkConsistencyForObject(node.properties, false);
|
||||
if (MODE === "consistent-as-needed") checkConsistencyForObject(node.properties, true);
|
||||
},
|
||||
ImportAttribute(node) {
|
||||
if (MODE === "always" || !MODE) checkOmittedQuotes(node);
|
||||
if (MODE === "as-needed") checkUnnecessaryQuotes(node);
|
||||
},
|
||||
ImportDeclaration(node) {
|
||||
checkImportAttributes(node.attributes);
|
||||
},
|
||||
ExportAllDeclaration(node) {
|
||||
checkImportAttributes(node.attributes);
|
||||
},
|
||||
ExportNamedDeclaration(node) {
|
||||
checkImportAttributes(node.attributes);
|
||||
},
|
||||
TSPropertySignature(node) {
|
||||
if (MODE === "always" || !MODE) checkOmittedQuotes(node);
|
||||
if (MODE === "as-needed") checkUnnecessaryQuotes(node);
|
||||
},
|
||||
TSEnumMember(node) {
|
||||
if (MODE === "always" || !MODE) checkOmittedQuotes(node);
|
||||
if (MODE === "as-needed") checkUnnecessaryQuotes(node);
|
||||
},
|
||||
TSTypeLiteral(node) {
|
||||
if (MODE === "consistent") checkConsistencyForObject(node.members, false);
|
||||
if (MODE === "consistent-as-needed") checkConsistencyForObject(node.members, true);
|
||||
},
|
||||
TSInterfaceBody(node) {
|
||||
if (MODE === "consistent") checkConsistencyForObject(node.body, false);
|
||||
if (MODE === "consistent-as-needed") checkConsistencyForObject(node.body, true);
|
||||
},
|
||||
TSEnumDeclaration(node) {
|
||||
const members = (node.body?.members || node.members).map((member) => ({
|
||||
...member,
|
||||
key: member.id
|
||||
}));
|
||||
if (MODE === "consistent") checkConsistencyForObject(members, false);
|
||||
if (MODE === "consistent-as-needed") checkConsistencyForObject(members, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { quote_props_default as t };
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
import { $ as warnDeprecation, J as isSurroundedBy, N as hasOctalOrNonOctalDecimalEscapeSequence, U as isParenthesised, Y as isTopLevelExpressionStatement, f as createRule, m as AST_NODE_TYPES, x as LINEBREAKS } from "../utils.js";
|
||||
function switchQuote(str) {
|
||||
const newQuote = this.quote;
|
||||
const oldQuote = str[0];
|
||||
if (newQuote === oldQuote) return str;
|
||||
return newQuote + str.slice(1, -1).replace(/\\(\$\{|\r\n?|\n|.)|["'`]|\$\{|(\r\n?|\n)/gu, (match, escaped, newline) => {
|
||||
if (escaped === oldQuote || oldQuote === "`" && escaped === "${") return escaped;
|
||||
if (match === newQuote || newQuote === "`" && match === "${") return `\\${match}`;
|
||||
if (newline && oldQuote === "`") return "\\n";
|
||||
return match;
|
||||
}) + newQuote;
|
||||
}
|
||||
const QUOTE_SETTINGS = {
|
||||
double: {
|
||||
quote: "\"",
|
||||
alternateQuote: "'",
|
||||
description: "doublequote",
|
||||
convert: switchQuote
|
||||
},
|
||||
single: {
|
||||
quote: "'",
|
||||
alternateQuote: "\"",
|
||||
description: "singlequote",
|
||||
convert: switchQuote
|
||||
},
|
||||
backtick: {
|
||||
quote: "`",
|
||||
alternateQuote: "\"",
|
||||
description: "backtick",
|
||||
convert: switchQuote
|
||||
}
|
||||
};
|
||||
const UNESCAPED_LINEBREAK_PATTERN = new RegExp(String.raw`(^|[^\\])(\\\\)*[${Array.from(LINEBREAKS).join("")}]`, "u");
|
||||
const AVOID_ESCAPE = "avoid-escape";
|
||||
var quotes_default = createRule({
|
||||
name: "quotes",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce the consistent use of either backticks, double, or single quotes" },
|
||||
fixable: "code",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: [
|
||||
"single",
|
||||
"double",
|
||||
"backtick"
|
||||
]
|
||||
}, { anyOf: [{
|
||||
type: "string",
|
||||
enum: ["avoid-escape"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
avoidEscape: { type: "boolean" },
|
||||
allowTemplateLiterals: { anyOf: [{ type: "boolean" }, {
|
||||
type: "string",
|
||||
enum: [
|
||||
"never",
|
||||
"avoidEscape",
|
||||
"always"
|
||||
]
|
||||
}] },
|
||||
ignoreStringLiterals: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}] }],
|
||||
defaultOptions: ["double", {
|
||||
allowTemplateLiterals: "never",
|
||||
avoidEscape: false,
|
||||
ignoreStringLiterals: false
|
||||
}],
|
||||
messages: { wrongQuotes: "Strings must use {{description}}." }
|
||||
},
|
||||
create(context, [quoteOption, options]) {
|
||||
const settings = QUOTE_SETTINGS[quoteOption || "double"];
|
||||
const sourceCode = context.sourceCode;
|
||||
let avoidEscape = false;
|
||||
let ignoreStringLiterals = false;
|
||||
let allowTemplateLiteralsAlways = false;
|
||||
let allowTemplateLiteralsToAvoidEscape = false;
|
||||
if (typeof options === "object") {
|
||||
avoidEscape = options.avoidEscape === true;
|
||||
ignoreStringLiterals = options.ignoreStringLiterals === true;
|
||||
if (typeof options.allowTemplateLiterals === "string") {
|
||||
allowTemplateLiteralsAlways = options.allowTemplateLiterals === "always";
|
||||
allowTemplateLiteralsToAvoidEscape = allowTemplateLiteralsAlways || options.allowTemplateLiterals === "avoidEscape";
|
||||
} else if (typeof options.allowTemplateLiterals === "boolean") {
|
||||
warnDeprecation("value(boolean) for \"allowTemplateLiterals\"", "\"always\"/\"never\"", "quotes");
|
||||
allowTemplateLiteralsAlways = options.allowTemplateLiterals === true;
|
||||
allowTemplateLiteralsToAvoidEscape = options.allowTemplateLiterals === true;
|
||||
}
|
||||
} else if (options === AVOID_ESCAPE) {
|
||||
warnDeprecation(`option("${AVOID_ESCAPE}")`, "\"avoidEscape\"", "quotes");
|
||||
avoidEscape = true;
|
||||
}
|
||||
function isJSXLiteral(node) {
|
||||
if (!node.parent) return false;
|
||||
return node.parent.type === "JSXAttribute" || node.parent.type === "JSXElement" || node.parent.type === "JSXFragment";
|
||||
}
|
||||
function isDirective(node) {
|
||||
return node.type === "ExpressionStatement" && node.expression.type === "Literal" && typeof node.expression.value === "string" && !isParenthesised(sourceCode, node.expression);
|
||||
}
|
||||
function isExpressionInOrJustAfterDirectivePrologue(node) {
|
||||
if (!node.parent) return false;
|
||||
if (!isTopLevelExpressionStatement(node.parent)) return false;
|
||||
const block = node.parent.parent;
|
||||
if (!block || !("body" in block) || !Array.isArray(block.body)) return false;
|
||||
for (let i = 0; i < block.body.length; ++i) {
|
||||
const statement = block.body[i];
|
||||
if (statement === node.parent) return true;
|
||||
if (!isDirective(statement)) break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isAllowedAsNonBacktick(node) {
|
||||
const parent = node.parent;
|
||||
if (!parent) return false;
|
||||
switch (parent.type) {
|
||||
case AST_NODE_TYPES.ExpressionStatement: return !isParenthesised(sourceCode, node) && isExpressionInOrJustAfterDirectivePrologue(node);
|
||||
case AST_NODE_TYPES.Property:
|
||||
case AST_NODE_TYPES.MethodDefinition: return parent.key === node && !parent.computed;
|
||||
case AST_NODE_TYPES.ImportDeclaration:
|
||||
case AST_NODE_TYPES.ExportNamedDeclaration: return parent.source === node;
|
||||
case AST_NODE_TYPES.ExportAllDeclaration: return parent.exported === node || parent.source === node;
|
||||
case AST_NODE_TYPES.ImportSpecifier: return parent.imported === node;
|
||||
case AST_NODE_TYPES.ExportSpecifier: return parent.local === node || parent.exported === node;
|
||||
case AST_NODE_TYPES.ImportAttribute: return parent.value === node;
|
||||
case AST_NODE_TYPES.TSImportType:
|
||||
case AST_NODE_TYPES.TSAbstractMethodDefinition:
|
||||
case AST_NODE_TYPES.TSMethodSignature:
|
||||
case AST_NODE_TYPES.TSPropertySignature:
|
||||
case AST_NODE_TYPES.TSModuleDeclaration:
|
||||
case AST_NODE_TYPES.TSExternalModuleReference: return true;
|
||||
case AST_NODE_TYPES.TSEnumMember: return node === parent.id;
|
||||
case AST_NODE_TYPES.TSAbstractPropertyDefinition:
|
||||
case AST_NODE_TYPES.PropertyDefinition:
|
||||
case AST_NODE_TYPES.AccessorProperty: return parent.key === node && !parent.computed;
|
||||
case AST_NODE_TYPES.TSLiteralType: return parent.parent?.type === AST_NODE_TYPES.TSImportType;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
function isUsingFeatureOfTemplateLiteral(node) {
|
||||
if (node.parent.type === "TaggedTemplateExpression" && node === node.parent.quasi) return true;
|
||||
if (node.expressions.length > 0) return true;
|
||||
if (node.quasis.length >= 1 && UNESCAPED_LINEBREAK_PATTERN.test(node.quasis[0].value.raw)) return true;
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
Literal(node) {
|
||||
if (ignoreStringLiterals) return;
|
||||
const val = node.value;
|
||||
const rawVal = node.raw;
|
||||
if (settings && typeof val === "string") {
|
||||
let isValid = quoteOption === "backtick" && isAllowedAsNonBacktick(node) || isJSXLiteral(node) || isSurroundedBy(rawVal, settings.quote);
|
||||
if (!isValid && avoidEscape) isValid = isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.includes(settings.quote);
|
||||
if (!isValid) context.report({
|
||||
node,
|
||||
messageId: "wrongQuotes",
|
||||
data: { description: settings.description },
|
||||
fix(fixer) {
|
||||
if (quoteOption === "backtick" && hasOctalOrNonOctalDecimalEscapeSequence(rawVal)) return null;
|
||||
return fixer.replaceText(node, settings.convert(node.raw));
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
TemplateLiteral(node) {
|
||||
if (allowTemplateLiteralsAlways || quoteOption === "backtick" || isUsingFeatureOfTemplateLiteral(node)) return;
|
||||
if (allowTemplateLiteralsToAvoidEscape && avoidEscape && sourceCode.getText(node).includes(settings.quote)) return;
|
||||
context.report({
|
||||
node,
|
||||
messageId: "wrongQuotes",
|
||||
data: { description: settings.description },
|
||||
fix(fixer) {
|
||||
if (isTopLevelExpressionStatement(node.parent) && !isParenthesised(sourceCode, node)) return null;
|
||||
return fixer.replaceText(node, settings.convert(sourceCode.getText(node)));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { quotes_default as t };
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
import { f as createRule } from "../utils.js";
|
||||
var rest_spread_spacing_default = createRule({
|
||||
name: "rest-spread-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce spacing between rest and spread operators and their expressions" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}],
|
||||
defaultOptions: ["never"],
|
||||
messages: {
|
||||
unexpectedWhitespace: "Unexpected whitespace after {{type}} operator.",
|
||||
expectedWhitespace: "Expected whitespace after {{type}} operator."
|
||||
}
|
||||
},
|
||||
create(context, [style]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const alwaysSpace = style === "always";
|
||||
function checkWhiteSpace(node) {
|
||||
const operator = sourceCode.getFirstToken(node);
|
||||
const nextToken = sourceCode.getTokenAfter(operator);
|
||||
const hasWhitespace = sourceCode.isSpaceBetween(operator, nextToken);
|
||||
let type;
|
||||
switch (node.type) {
|
||||
case "SpreadElement":
|
||||
type = "spread";
|
||||
if (node.parent.type === "ObjectExpression") type += " property";
|
||||
break;
|
||||
case "RestElement":
|
||||
type = "rest";
|
||||
if (node.parent.type === "ObjectPattern") type += " property";
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
if (alwaysSpace && !hasWhitespace) context.report({
|
||||
node,
|
||||
loc: operator.loc,
|
||||
messageId: "expectedWhitespace",
|
||||
data: { type },
|
||||
fix(fixer) {
|
||||
return fixer.replaceTextRange([operator.range[1], nextToken.range[0]], " ");
|
||||
}
|
||||
});
|
||||
else if (!alwaysSpace && hasWhitespace) context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: operator.loc.end,
|
||||
end: nextToken.loc.start
|
||||
},
|
||||
messageId: "unexpectedWhitespace",
|
||||
data: { type },
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([operator.range[1], nextToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
SpreadElement: checkWhiteSpace,
|
||||
RestElement: checkWhiteSpace
|
||||
};
|
||||
}
|
||||
});
|
||||
export { rest_spread_spacing_default as t };
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
import { f as createRule, g as import_ast_utils, m as AST_NODE_TYPES } from "../utils.js";
|
||||
var semi_spacing_default = createRule({
|
||||
name: "semi-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent spacing before and after semicolons" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
before: { type: "boolean" },
|
||||
after: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
before: false,
|
||||
after: true
|
||||
}],
|
||||
messages: {
|
||||
unexpectedWhitespaceBefore: "Unexpected whitespace before semicolon.",
|
||||
unexpectedWhitespaceAfter: "Unexpected whitespace after semicolon.",
|
||||
missingWhitespaceBefore: "Missing whitespace before semicolon.",
|
||||
missingWhitespaceAfter: "Missing whitespace after semicolon."
|
||||
}
|
||||
},
|
||||
create(context, [config]) {
|
||||
const { before: requireSpaceBefore, after: requireSpaceAfter } = config;
|
||||
const sourceCode = context.sourceCode;
|
||||
function hasLeadingSpace(token) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(token);
|
||||
return tokenBefore && (0, import_ast_utils.isTokenOnSameLine)(tokenBefore, token) && sourceCode.isSpaceBetween(tokenBefore, token);
|
||||
}
|
||||
function hasTrailingSpace(token) {
|
||||
const tokenAfter = sourceCode.getTokenAfter(token);
|
||||
return tokenAfter && (0, import_ast_utils.isTokenOnSameLine)(token, tokenAfter) && sourceCode.isSpaceBetween(token, tokenAfter);
|
||||
}
|
||||
function isLastTokenInCurrentLine(token) {
|
||||
const tokenAfter = sourceCode.getTokenAfter(token);
|
||||
return !(tokenAfter && (0, import_ast_utils.isTokenOnSameLine)(token, tokenAfter));
|
||||
}
|
||||
function isFirstTokenInCurrentLine(token) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(token);
|
||||
return !(tokenBefore && (0, import_ast_utils.isTokenOnSameLine)(token, tokenBefore));
|
||||
}
|
||||
function isBeforeClosingParen(token) {
|
||||
const nextToken = sourceCode.getTokenAfter(token);
|
||||
return nextToken && (0, import_ast_utils.isClosingBraceToken)(nextToken) || (0, import_ast_utils.isClosingParenToken)(nextToken);
|
||||
}
|
||||
function checkSemicolonSpacing(token, node) {
|
||||
if ((0, import_ast_utils.isSemicolonToken)(token)) {
|
||||
if (hasLeadingSpace(token)) {
|
||||
if (!requireSpaceBefore) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(token);
|
||||
const loc = {
|
||||
start: tokenBefore.loc.end,
|
||||
end: token.loc.start
|
||||
};
|
||||
context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId: "unexpectedWhitespaceBefore",
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([tokenBefore.range[1], token.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (requireSpaceBefore) {
|
||||
const loc = token.loc;
|
||||
context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId: "missingWhitespaceBefore",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!isFirstTokenInCurrentLine(token) && !isLastTokenInCurrentLine(token) && !isBeforeClosingParen(token)) {
|
||||
if (hasTrailingSpace(token)) {
|
||||
if (!requireSpaceAfter) {
|
||||
const tokenAfter = sourceCode.getTokenAfter(token);
|
||||
const loc = {
|
||||
start: token.loc.end,
|
||||
end: tokenAfter.loc.start
|
||||
};
|
||||
context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId: "unexpectedWhitespaceAfter",
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([token.range[1], tokenAfter.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (requireSpaceAfter) {
|
||||
const loc = token.loc;
|
||||
context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId: "missingWhitespaceAfter",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(token, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function checkNode(node) {
|
||||
checkSemicolonSpacing(sourceCode.getLastToken(node), node);
|
||||
}
|
||||
return {
|
||||
VariableDeclaration: checkNode,
|
||||
ExpressionStatement: checkNode,
|
||||
BreakStatement: checkNode,
|
||||
ContinueStatement: checkNode,
|
||||
DebuggerStatement: checkNode,
|
||||
DoWhileStatement: checkNode,
|
||||
ReturnStatement: checkNode,
|
||||
ThrowStatement: checkNode,
|
||||
ImportDeclaration: checkNode,
|
||||
ExportNamedDeclaration: checkNode,
|
||||
ExportAllDeclaration: checkNode,
|
||||
ExportDefaultDeclaration: checkNode,
|
||||
ForStatement(node) {
|
||||
if (node.init) checkSemicolonSpacing(sourceCode.getTokenAfter(node.init), node);
|
||||
if (node.test) checkSemicolonSpacing(sourceCode.getTokenAfter(node.test), node);
|
||||
},
|
||||
PropertyDefinition: checkNode,
|
||||
AccessorProperty: checkNode,
|
||||
TSDeclareFunction: checkNode,
|
||||
TSTypeAliasDeclaration: checkNode,
|
||||
TSTypeAnnotation(node) {
|
||||
const excludeNodeTypes = new Set([AST_NODE_TYPES.TSDeclareFunction]);
|
||||
if (node.parent && !excludeNodeTypes.has(node.parent.type)) checkNode(node.parent);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { semi_spacing_default as t };
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
const SELECTOR = [
|
||||
"BreakStatement",
|
||||
"ContinueStatement",
|
||||
"DebuggerStatement",
|
||||
"DoWhileStatement",
|
||||
"ExportAllDeclaration",
|
||||
"ExportDefaultDeclaration",
|
||||
"ExportNamedDeclaration",
|
||||
"ExpressionStatement",
|
||||
"ImportDeclaration",
|
||||
"ReturnStatement",
|
||||
"ThrowStatement",
|
||||
"VariableDeclaration",
|
||||
"PropertyDefinition",
|
||||
"AccessorProperty"
|
||||
].join(",");
|
||||
function getChildren(node) {
|
||||
const t = node.type;
|
||||
if (t === "BlockStatement" || t === "StaticBlock" || t === "Program" || t === "ClassBody") return node.body;
|
||||
if (t === "SwitchCase") return node.consequent;
|
||||
return null;
|
||||
}
|
||||
function isLastChild(node) {
|
||||
if (!node.parent) return true;
|
||||
const t = node.parent.type;
|
||||
if (t === "IfStatement" && node.parent.consequent === node && node.parent.alternate) return true;
|
||||
if (t === "DoWhileStatement") return true;
|
||||
const nodeList = getChildren(node.parent);
|
||||
return nodeList !== null && nodeList[nodeList.length - 1] === node;
|
||||
}
|
||||
var semi_style_default = createRule({
|
||||
name: "semi-style",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce location of semicolons" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["last", "first"]
|
||||
}],
|
||||
defaultOptions: ["last"],
|
||||
messages: { expectedSemiColon: "Expected this semicolon to be at {{pos}}." }
|
||||
},
|
||||
create(context, [option]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
function check(semiToken, expected) {
|
||||
const prevToken = sourceCode.getTokenBefore(semiToken);
|
||||
const nextToken = sourceCode.getTokenAfter(semiToken);
|
||||
const prevIsSameLine = !prevToken || (0, import_ast_utils.isTokenOnSameLine)(prevToken, semiToken);
|
||||
const nextIsSameLine = !nextToken || (0, import_ast_utils.isTokenOnSameLine)(semiToken, nextToken);
|
||||
if (expected === "last" && !prevIsSameLine || expected === "first" && !nextIsSameLine) context.report({
|
||||
loc: semiToken.loc,
|
||||
messageId: "expectedSemiColon",
|
||||
data: { pos: expected === "last" ? "the end of the previous line" : "the beginning of the next line" },
|
||||
fix(fixer) {
|
||||
if (prevToken && nextToken && sourceCode.commentsExistBetween(prevToken, nextToken)) return null;
|
||||
const start = prevToken ? prevToken.range[1] : semiToken.range[0];
|
||||
const end = nextToken ? nextToken.range[0] : semiToken.range[1];
|
||||
const text = expected === "last" ? ";\n" : "\n;";
|
||||
return fixer.replaceTextRange([start, end], text);
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
[SELECTOR](node) {
|
||||
if (option === "first" && isLastChild(node)) return;
|
||||
const lastToken = sourceCode.getLastToken(node);
|
||||
if ((0, import_ast_utils.isSemicolonToken)(lastToken)) check(lastToken, option);
|
||||
},
|
||||
ForStatement(node) {
|
||||
const firstSemi = node.init && sourceCode.getTokenAfter(node.init, import_ast_utils.isSemicolonToken);
|
||||
const secondSemi = node.test && sourceCode.getTokenAfter(node.test, import_ast_utils.isSemicolonToken);
|
||||
if (firstSemi) check(firstSemi, "last");
|
||||
if (secondSemi) check(secondSemi, "last");
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { semi_style_default as t };
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
import { K as isSingleLine, O as getNextLocation, f as createRule, g as import_ast_utils, m as AST_NODE_TYPES, t as FixTracker } from "../utils.js";
|
||||
var semi_default = createRule({
|
||||
name: "semi",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require or disallow semicolons instead of ASI" },
|
||||
fixable: "code",
|
||||
schema: { anyOf: [{
|
||||
type: "array",
|
||||
items: [{
|
||||
type: "string",
|
||||
enum: ["never"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: { beforeStatementContinuationChars: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"any",
|
||||
"never"
|
||||
]
|
||||
} },
|
||||
additionalProperties: false
|
||||
}],
|
||||
minItems: 0,
|
||||
maxItems: 2
|
||||
}, {
|
||||
type: "array",
|
||||
items: [{
|
||||
type: "string",
|
||||
enum: ["always"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
omitLastInOneLineBlock: { type: "boolean" },
|
||||
omitLastInOneLineClassBody: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
minItems: 0,
|
||||
maxItems: 2
|
||||
}] },
|
||||
defaultOptions: ["always"],
|
||||
messages: {
|
||||
missingSemi: "Missing semicolon.",
|
||||
extraSemi: "Extra semicolon."
|
||||
}
|
||||
},
|
||||
create(context, [mode, options]) {
|
||||
const OPT_OUT_PATTERN = /^[-[(/+`]/u;
|
||||
const unsafeClassFieldNames = new Set([
|
||||
"get",
|
||||
"set",
|
||||
"static"
|
||||
]);
|
||||
const unsafeClassFieldFollowers = new Set([
|
||||
"*",
|
||||
"in",
|
||||
"instanceof"
|
||||
]);
|
||||
const never = mode === "never";
|
||||
const exceptOneLine = Boolean(options && "omitLastInOneLineBlock" in options && options.omitLastInOneLineBlock);
|
||||
const exceptOneLineClassBody = Boolean(options && "omitLastInOneLineClassBody" in options && options.omitLastInOneLineClassBody);
|
||||
const beforeStatementContinuationChars = options && "beforeStatementContinuationChars" in options && options.beforeStatementContinuationChars || "any";
|
||||
const sourceCode = context.sourceCode;
|
||||
function report(node, missing = false) {
|
||||
const lastToken = sourceCode.getLastToken(node);
|
||||
let messageId = "missingSemi";
|
||||
let fix, loc;
|
||||
if (!missing) {
|
||||
loc = {
|
||||
start: lastToken.loc.end,
|
||||
end: getNextLocation(sourceCode, lastToken.loc.end)
|
||||
};
|
||||
fix = function(fixer) {
|
||||
return fixer.insertTextAfter(lastToken, ";");
|
||||
};
|
||||
} else {
|
||||
messageId = "extraSemi";
|
||||
loc = lastToken.loc;
|
||||
fix = function(fixer) {
|
||||
return new FixTracker(fixer, sourceCode).retainSurroundingTokens(lastToken).remove(lastToken);
|
||||
};
|
||||
}
|
||||
context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId,
|
||||
fix
|
||||
});
|
||||
}
|
||||
function isRedundantSemi(semiToken) {
|
||||
const nextToken = sourceCode.getTokenAfter(semiToken);
|
||||
return !nextToken || (0, import_ast_utils.isClosingBraceToken)(nextToken) || (0, import_ast_utils.isSemicolonToken)(nextToken);
|
||||
}
|
||||
function isEndOfArrowBlock(lastToken) {
|
||||
if (!(0, import_ast_utils.isClosingBraceToken)(lastToken)) return false;
|
||||
const node = sourceCode.getNodeByRangeIndex(lastToken.range[0]);
|
||||
return node.type === "BlockStatement" && node.parent.type === "ArrowFunctionExpression";
|
||||
}
|
||||
function maybeClassFieldAsiHazard(node) {
|
||||
if (node.type !== "PropertyDefinition") return false;
|
||||
if (!node.computed && node.key.type === "Identifier" && "name" in node.key && unsafeClassFieldNames.has(node.key.name)) {
|
||||
if (!(node.static && node.key.name === "static") && !node.value) return true;
|
||||
}
|
||||
const followingToken = sourceCode.getTokenAfter(node);
|
||||
return unsafeClassFieldFollowers.has(followingToken.value);
|
||||
}
|
||||
function isOnSameLineWithNextToken(node) {
|
||||
const prevToken = sourceCode.getLastToken(node, 1);
|
||||
const nextToken = sourceCode.getTokenAfter(node);
|
||||
return !!nextToken && (0, import_ast_utils.isTokenOnSameLine)(prevToken, nextToken);
|
||||
}
|
||||
function maybeAsiHazardAfter(node) {
|
||||
const t = node.type;
|
||||
if (t === "DoWhileStatement" || t === "BreakStatement" || t === "ContinueStatement" || t === "DebuggerStatement" || t === "ImportDeclaration" || t === "ExportAllDeclaration") return false;
|
||||
if (t === "ReturnStatement") return Boolean(node.argument);
|
||||
if (t === "ExportNamedDeclaration") return Boolean(node.declaration);
|
||||
if (isEndOfArrowBlock(sourceCode.getLastToken(node, 1))) return false;
|
||||
return true;
|
||||
}
|
||||
function maybeAsiHazardBefore(token) {
|
||||
return Boolean(token) && OPT_OUT_PATTERN.test(token.value) && token.value !== "++" && token.value !== "--";
|
||||
}
|
||||
function canRemoveSemicolon(node) {
|
||||
if (isRedundantSemi(sourceCode.getLastToken(node))) return true;
|
||||
if (maybeClassFieldAsiHazard(node)) return false;
|
||||
if (isOnSameLineWithNextToken(node)) return false;
|
||||
if (node.type !== "PropertyDefinition" && beforeStatementContinuationChars === "never" && !maybeAsiHazardAfter(node)) return true;
|
||||
if (!maybeAsiHazardBefore(sourceCode.getTokenAfter(node))) return true;
|
||||
return false;
|
||||
}
|
||||
function isLastInOneLinerBlock(node) {
|
||||
const parent = node.parent;
|
||||
const nextToken = sourceCode.getTokenAfter(node);
|
||||
if (!nextToken || nextToken.value !== "}") return false;
|
||||
if (parent.type === "BlockStatement") return isSingleLine(parent);
|
||||
if (parent.type === "StaticBlock") return (0, import_ast_utils.isTokenOnSameLine)(parent, sourceCode.getFirstToken(parent, { skip: 1 }));
|
||||
return false;
|
||||
}
|
||||
function isLastInOneLinerClassBody(node) {
|
||||
const parent = node.parent;
|
||||
const nextToken = sourceCode.getTokenAfter(node);
|
||||
if (!nextToken || nextToken.value !== "}") return false;
|
||||
if (parent.type === "ClassBody") return isSingleLine(parent);
|
||||
return false;
|
||||
}
|
||||
function checkForSemicolon(node) {
|
||||
const isSemi = (0, import_ast_utils.isSemicolonToken)(sourceCode.getLastToken(node));
|
||||
if (never) {
|
||||
const nextToken = sourceCode.getTokenAfter(node);
|
||||
if (isSemi && canRemoveSemicolon(node)) report(node, true);
|
||||
else if (!isSemi && beforeStatementContinuationChars === "always" && node.type !== "PropertyDefinition" && maybeAsiHazardBefore(nextToken)) report(node);
|
||||
} else {
|
||||
const oneLinerBlock = exceptOneLine && isLastInOneLinerBlock(node);
|
||||
const oneLinerClassBody = exceptOneLineClassBody && isLastInOneLinerClassBody(node);
|
||||
const oneLinerBlockOrClassBody = oneLinerBlock || oneLinerClassBody;
|
||||
if (isSemi && oneLinerBlockOrClassBody) report(node, true);
|
||||
else if (!isSemi && !oneLinerBlockOrClassBody) report(node);
|
||||
}
|
||||
}
|
||||
return {
|
||||
VariableDeclaration(node) {
|
||||
const parent = node.parent;
|
||||
if ((parent.type !== "ForStatement" || parent.init !== node) && (!/^For(?:In|Of)Statement/u.test(parent.type) || parent.left !== node)) checkForSemicolon(node);
|
||||
},
|
||||
ExpressionStatement: checkForSemicolon,
|
||||
ReturnStatement: checkForSemicolon,
|
||||
ThrowStatement: checkForSemicolon,
|
||||
DoWhileStatement: checkForSemicolon,
|
||||
DebuggerStatement: checkForSemicolon,
|
||||
BreakStatement: checkForSemicolon,
|
||||
ContinueStatement: checkForSemicolon,
|
||||
ImportDeclaration: checkForSemicolon,
|
||||
ExportAllDeclaration: checkForSemicolon,
|
||||
ExportNamedDeclaration(node) {
|
||||
if (!node.declaration) checkForSemicolon(node);
|
||||
},
|
||||
ExportDefaultDeclaration(node) {
|
||||
if (node.declaration.type === AST_NODE_TYPES.TSInterfaceDeclaration) return;
|
||||
if (!/(?:Class|Function)Declaration/u.test(node.declaration.type)) checkForSemicolon(node);
|
||||
},
|
||||
PropertyDefinition: checkForSemicolon,
|
||||
AccessorProperty: checkForSemicolon,
|
||||
TSAbstractPropertyDefinition: checkForSemicolon,
|
||||
TSDeclareFunction: checkForSemicolon,
|
||||
TSExportAssignment: checkForSemicolon,
|
||||
TSImportEqualsDeclaration: checkForSemicolon,
|
||||
TSTypeAliasDeclaration: checkForSemicolon,
|
||||
TSEmptyBodyFunctionExpression: checkForSemicolon
|
||||
};
|
||||
}
|
||||
});
|
||||
export { semi_default as t };
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
import { f as createRule, g as import_ast_utils, j as getSwitchCaseColonToken, z as isKeywordToken } from "../utils.js";
|
||||
var space_before_blocks_default = createRule({
|
||||
name: "space-before-blocks",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent spacing before blocks" },
|
||||
fixable: "whitespace",
|
||||
schema: [{ oneOf: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
keywords: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"off"
|
||||
]
|
||||
},
|
||||
functions: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"off"
|
||||
]
|
||||
},
|
||||
classes: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"off"
|
||||
]
|
||||
},
|
||||
modules: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"off"
|
||||
]
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}] }],
|
||||
defaultOptions: ["always"],
|
||||
messages: {
|
||||
unexpectedSpace: "Unexpected space before opening brace.",
|
||||
missingSpace: "Missing space before opening brace."
|
||||
}
|
||||
},
|
||||
create(context, [config]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
let alwaysFunctions = true;
|
||||
let alwaysKeywords = true;
|
||||
let alwaysClasses = true;
|
||||
let alwaysModules = true;
|
||||
let neverFunctions = false;
|
||||
let neverKeywords = false;
|
||||
let neverClasses = false;
|
||||
let neverModules = false;
|
||||
if (typeof config === "object") {
|
||||
alwaysFunctions = config.functions === "always";
|
||||
alwaysKeywords = config.keywords === "always";
|
||||
alwaysClasses = config.classes === "always";
|
||||
alwaysModules = config.modules === "always";
|
||||
neverFunctions = config.functions === "never";
|
||||
neverKeywords = config.keywords === "never";
|
||||
neverClasses = config.classes === "never";
|
||||
neverModules = config.modules === "never";
|
||||
} else if (config === "never") {
|
||||
alwaysFunctions = false;
|
||||
alwaysKeywords = false;
|
||||
alwaysClasses = false;
|
||||
alwaysModules = false;
|
||||
neverFunctions = true;
|
||||
neverKeywords = true;
|
||||
neverClasses = true;
|
||||
neverModules = true;
|
||||
}
|
||||
function isFunctionBody(node) {
|
||||
if (!("parent" in node)) return false;
|
||||
const parent = node.parent;
|
||||
return node.type === "BlockStatement" && (0, import_ast_utils.isFunction)(parent) && parent.body === node;
|
||||
}
|
||||
function isConflicted(precedingToken, node) {
|
||||
return (0, import_ast_utils.isArrowToken)(precedingToken) || isKeywordToken(precedingToken) && !isFunctionBody(node) || (0, import_ast_utils.isColonToken)(precedingToken) && "parent" in node && node.parent && node.parent.type === "SwitchCase" && precedingToken === getSwitchCaseColonToken(node.parent, sourceCode);
|
||||
}
|
||||
function checkPrecedingSpace(node) {
|
||||
const precedingToken = sourceCode.getTokenBefore(node);
|
||||
if (precedingToken && !isConflicted(precedingToken, node) && (0, import_ast_utils.isTokenOnSameLine)(precedingToken, node)) {
|
||||
const hasSpace = sourceCode.isSpaceBetween(precedingToken, node);
|
||||
let requireSpace;
|
||||
let requireNoSpace;
|
||||
if (isFunctionBody(node)) {
|
||||
requireSpace = alwaysFunctions;
|
||||
requireNoSpace = neverFunctions;
|
||||
} else if (node.type === "ClassBody" || node.type === "TSEnumBody" || node.type === "TSInterfaceBody") {
|
||||
requireSpace = alwaysClasses;
|
||||
requireNoSpace = neverClasses;
|
||||
} else if (node.type === "TSModuleBlock") {
|
||||
requireSpace = alwaysModules;
|
||||
requireNoSpace = neverModules;
|
||||
} else {
|
||||
requireSpace = alwaysKeywords;
|
||||
requireNoSpace = neverKeywords;
|
||||
}
|
||||
if (requireSpace && !hasSpace) context.report({
|
||||
node,
|
||||
messageId: "missingSpace",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(node, " ");
|
||||
}
|
||||
});
|
||||
else if (requireNoSpace && hasSpace) context.report({
|
||||
node,
|
||||
messageId: "unexpectedSpace",
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([precedingToken.range[1], node.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return {
|
||||
BlockStatement: checkPrecedingSpace,
|
||||
ClassBody: checkPrecedingSpace,
|
||||
SwitchStatement(node) {
|
||||
const cases = node.cases;
|
||||
let openingBrace;
|
||||
if (cases.length > 0) openingBrace = sourceCode.getTokenBefore(cases[0]);
|
||||
else openingBrace = sourceCode.getLastToken(node, 1);
|
||||
checkPrecedingSpace(openingBrace);
|
||||
},
|
||||
TSEnumBody: checkPrecedingSpace,
|
||||
TSInterfaceBody: checkPrecedingSpace,
|
||||
TSModuleBlock: checkPrecedingSpace
|
||||
};
|
||||
}
|
||||
});
|
||||
export { space_before_blocks_default as t };
|
||||
Generated
Vendored
+128
@@ -0,0 +1,128 @@
|
||||
import { f as createRule, g as import_ast_utils, m as AST_NODE_TYPES } from "../utils.js";
|
||||
var space_before_function_paren_default = createRule({
|
||||
name: "space-before-function-paren",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent spacing before function parenthesis" },
|
||||
fixable: "whitespace",
|
||||
schema: [{ oneOf: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
anonymous: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"ignore"
|
||||
]
|
||||
},
|
||||
named: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"ignore"
|
||||
]
|
||||
},
|
||||
asyncArrow: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"ignore"
|
||||
]
|
||||
},
|
||||
catch: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"always",
|
||||
"never",
|
||||
"ignore"
|
||||
]
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}] }],
|
||||
defaultOptions: ["always"],
|
||||
messages: {
|
||||
unexpectedSpace: "Unexpected space before function parentheses.",
|
||||
missingSpace: "Missing space before function parentheses."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const { asyncArrow = "always", anonymous = "always", named = "always", catch: catchOption = "always" } = typeof options === "string" ? {
|
||||
asyncArrow: options,
|
||||
anonymous: options,
|
||||
named: options,
|
||||
catch: options
|
||||
} : options;
|
||||
function isNamedFunction(node) {
|
||||
if (node.id != null) return true;
|
||||
const parent = node.parent;
|
||||
return parent.type === AST_NODE_TYPES.MethodDefinition || parent.type === AST_NODE_TYPES.TSAbstractMethodDefinition || parent.type === AST_NODE_TYPES.Property && (parent.kind === "get" || parent.kind === "set" || parent.method);
|
||||
}
|
||||
function getConfigForFunction(node) {
|
||||
if (node.type === AST_NODE_TYPES.ArrowFunctionExpression) {
|
||||
if (node.async && (0, import_ast_utils.isOpeningParenToken)(sourceCode.getFirstToken(node, { skip: 1 }))) return asyncArrow;
|
||||
} else if (isNamedFunction(node)) return named;
|
||||
else if (!node.generator) return anonymous;
|
||||
return "ignore";
|
||||
}
|
||||
function checkFunction(node) {
|
||||
const functionConfig = getConfigForFunction(node);
|
||||
if (functionConfig === "ignore") return;
|
||||
if (functionConfig === "always" && node.typeParameters && !node.id) return;
|
||||
let leftToken;
|
||||
let rightToken;
|
||||
if (node.typeParameters) {
|
||||
leftToken = sourceCode.getLastToken(node.typeParameters);
|
||||
rightToken = sourceCode.getTokenAfter(leftToken);
|
||||
} else {
|
||||
rightToken = sourceCode.getFirstToken(node, import_ast_utils.isOpeningParenToken);
|
||||
leftToken = sourceCode.getTokenBefore(rightToken);
|
||||
}
|
||||
checkSpace(node, leftToken, rightToken, functionConfig);
|
||||
}
|
||||
function checkSpace(node, leftToken, rightToken, option) {
|
||||
const hasSpacing = sourceCode.isSpaceBetween(leftToken, rightToken);
|
||||
if (hasSpacing && option === "never") context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: leftToken.loc.end,
|
||||
end: rightToken.loc.start
|
||||
},
|
||||
messageId: "unexpectedSpace",
|
||||
fix: (fixer) => {
|
||||
const comments = sourceCode.getCommentsBefore(rightToken);
|
||||
if (comments.some((comment) => comment.type === "Line")) return null;
|
||||
return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], comments.reduce((text, comment) => text + sourceCode.getText(comment), ""));
|
||||
}
|
||||
});
|
||||
else if (!hasSpacing && option === "always") context.report({
|
||||
node,
|
||||
loc: rightToken.loc,
|
||||
messageId: "missingSpace",
|
||||
fix: (fixer) => fixer.insertTextAfter(leftToken, " ")
|
||||
});
|
||||
}
|
||||
return {
|
||||
ArrowFunctionExpression: checkFunction,
|
||||
FunctionDeclaration: checkFunction,
|
||||
FunctionExpression: checkFunction,
|
||||
TSEmptyBodyFunctionExpression: checkFunction,
|
||||
TSDeclareFunction: checkFunction,
|
||||
CatchClause(node) {
|
||||
if (!node.param) return;
|
||||
const option = catchOption;
|
||||
if (catchOption === "ignore") return;
|
||||
const rightToken = sourceCode.getFirstToken(node, import_ast_utils.isOpeningParenToken);
|
||||
checkSpace(node, sourceCode.getTokenBefore(rightToken), rightToken, option);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { space_before_function_paren_default as t };
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var space_in_parens_default = createRule({
|
||||
name: "space-in-parens",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent spacing inside parentheses" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: { exceptions: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"{}",
|
||||
"[]",
|
||||
"()",
|
||||
"empty"
|
||||
]
|
||||
},
|
||||
uniqueItems: true
|
||||
} },
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: ["never"],
|
||||
messages: {
|
||||
missingOpeningSpace: "There must be a space after this paren.",
|
||||
missingClosingSpace: "There must be a space before this paren.",
|
||||
rejectedOpeningSpace: "There should be no space after this paren.",
|
||||
rejectedClosingSpace: "There should be no space before this paren."
|
||||
}
|
||||
},
|
||||
create(context, [style, { exceptions: exceptionsArrayOptions = [] } = {}]) {
|
||||
const ALWAYS = style === "always";
|
||||
const options = {
|
||||
braceException: false,
|
||||
bracketException: false,
|
||||
parenException: false,
|
||||
empty: false
|
||||
};
|
||||
let exceptions = {
|
||||
openers: [],
|
||||
closers: []
|
||||
};
|
||||
if (exceptionsArrayOptions.length) {
|
||||
options.braceException = exceptionsArrayOptions.includes("{}");
|
||||
options.bracketException = exceptionsArrayOptions.includes("[]");
|
||||
options.parenException = exceptionsArrayOptions.includes("()");
|
||||
options.empty = exceptionsArrayOptions.includes("empty");
|
||||
}
|
||||
function getExceptions() {
|
||||
const openers = [];
|
||||
const closers = [];
|
||||
if (options.braceException) {
|
||||
openers.push("{");
|
||||
closers.push("}");
|
||||
}
|
||||
if (options.bracketException) {
|
||||
openers.push("[");
|
||||
closers.push("]");
|
||||
}
|
||||
if (options.parenException) {
|
||||
openers.push("(");
|
||||
closers.push(")");
|
||||
}
|
||||
if (options.empty) {
|
||||
openers.push(")");
|
||||
closers.push("(");
|
||||
}
|
||||
return {
|
||||
openers,
|
||||
closers
|
||||
};
|
||||
}
|
||||
const sourceCode = context.sourceCode;
|
||||
function isOpenerException(token) {
|
||||
return exceptions.openers.includes(token.value);
|
||||
}
|
||||
function isCloserException(token) {
|
||||
return exceptions.closers.includes(token.value);
|
||||
}
|
||||
function openerMissingSpace(openingParenToken, tokenAfterOpeningParen) {
|
||||
if (sourceCode.isSpaceBetween(openingParenToken, tokenAfterOpeningParen)) return false;
|
||||
if (!options.empty && (0, import_ast_utils.isClosingParenToken)(tokenAfterOpeningParen)) return false;
|
||||
if (ALWAYS) return !isOpenerException(tokenAfterOpeningParen);
|
||||
return isOpenerException(tokenAfterOpeningParen);
|
||||
}
|
||||
function openerRejectsSpace(openingParenToken, tokenAfterOpeningParen) {
|
||||
if (!(0, import_ast_utils.isTokenOnSameLine)(openingParenToken, tokenAfterOpeningParen)) return false;
|
||||
if (tokenAfterOpeningParen.type === "Line") return false;
|
||||
if (!sourceCode.isSpaceBetween(openingParenToken, tokenAfterOpeningParen)) return false;
|
||||
if (ALWAYS) return isOpenerException(tokenAfterOpeningParen);
|
||||
return !isOpenerException(tokenAfterOpeningParen);
|
||||
}
|
||||
function closerMissingSpace(tokenBeforeClosingParen, closingParenToken) {
|
||||
if (sourceCode.isSpaceBetween(tokenBeforeClosingParen, closingParenToken)) return false;
|
||||
if (!options.empty && (0, import_ast_utils.isOpeningParenToken)(tokenBeforeClosingParen)) return false;
|
||||
if (ALWAYS) return !isCloserException(tokenBeforeClosingParen);
|
||||
return isCloserException(tokenBeforeClosingParen);
|
||||
}
|
||||
function closerRejectsSpace(tokenBeforeClosingParen, closingParenToken) {
|
||||
if (!(0, import_ast_utils.isTokenOnSameLine)(tokenBeforeClosingParen, closingParenToken)) return false;
|
||||
if (!sourceCode.isSpaceBetween(tokenBeforeClosingParen, closingParenToken)) return false;
|
||||
if (ALWAYS) return isCloserException(tokenBeforeClosingParen);
|
||||
return !isCloserException(tokenBeforeClosingParen);
|
||||
}
|
||||
return { Program: function checkParenSpaces(node) {
|
||||
exceptions = getExceptions();
|
||||
const tokens = sourceCode.tokensAndComments;
|
||||
tokens.forEach((token, i) => {
|
||||
const prevToken = tokens[i - 1];
|
||||
const nextToken = tokens[i + 1];
|
||||
if (!(0, import_ast_utils.isOpeningParenToken)(token) && !(0, import_ast_utils.isClosingParenToken)(token)) return;
|
||||
if (token.value === "(" && openerMissingSpace(token, nextToken)) context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "missingOpeningSpace",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(token, " ");
|
||||
}
|
||||
});
|
||||
if (token.value === "(" && openerRejectsSpace(token, nextToken)) context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: token.loc.end,
|
||||
end: nextToken.loc.start
|
||||
},
|
||||
messageId: "rejectedOpeningSpace",
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([token.range[1], nextToken.range[0]]);
|
||||
}
|
||||
});
|
||||
if (token.value === ")" && closerMissingSpace(prevToken, token)) context.report({
|
||||
node,
|
||||
loc: token.loc,
|
||||
messageId: "missingClosingSpace",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(token, " ");
|
||||
}
|
||||
});
|
||||
if (token.value === ")" && closerRejectsSpace(prevToken, token)) context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: prevToken.loc.end,
|
||||
end: token.loc.start
|
||||
},
|
||||
messageId: "rejectedClosingSpace",
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([prevToken.range[1], token.range[0]]);
|
||||
}
|
||||
});
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { space_in_parens_default as t };
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
import { f as createRule, g as import_ast_utils, h as AST_TOKEN_TYPES, m as AST_NODE_TYPES } from "../utils.js";
|
||||
const UNIONS = ["|", "&"];
|
||||
var space_infix_ops_default = createRule({
|
||||
name: "space-infix-ops",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require spacing around infix operators" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
int32Hint: { type: "boolean" },
|
||||
ignoreTypes: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
int32Hint: false,
|
||||
ignoreTypes: false
|
||||
}],
|
||||
messages: { missingSpace: "Operator '{{operator}}' must be spaced." }
|
||||
},
|
||||
create(context, [options]) {
|
||||
const { int32Hint, ignoreTypes } = options;
|
||||
const sourceCode = context.sourceCode;
|
||||
function report(node, operator) {
|
||||
context.report({
|
||||
node,
|
||||
loc: operator.loc,
|
||||
messageId: "missingSpace",
|
||||
data: { operator: operator.value },
|
||||
fix(fixer) {
|
||||
const previousToken = sourceCode.getTokenBefore(operator);
|
||||
const afterToken = sourceCode.getTokenAfter(operator);
|
||||
let fixString = "";
|
||||
if (operator.range[0] - previousToken.range[1] === 0) fixString = " ";
|
||||
fixString += operator.value;
|
||||
if (afterToken.range[0] - operator.range[1] === 0) fixString += " ";
|
||||
return fixer.replaceText(operator, fixString);
|
||||
}
|
||||
});
|
||||
}
|
||||
function getFirstNonSpacedToken(left, right, op) {
|
||||
const operator = sourceCode.getFirstTokenBetween(left, right, (token) => token.value === op);
|
||||
const prev = sourceCode.getTokenBefore(operator);
|
||||
const next = sourceCode.getTokenAfter(operator);
|
||||
if (!sourceCode.isSpaceBetween(prev, operator) || !sourceCode.isSpaceBetween(operator, next)) return operator;
|
||||
return null;
|
||||
}
|
||||
function checkBinary(node) {
|
||||
const leftNode = "typeAnnotation" in node.left && node.left.typeAnnotation ? node.left.typeAnnotation : node.left;
|
||||
const rightNode = node.right;
|
||||
const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode, "operator" in node && node.operator ? node.operator : "=");
|
||||
if (nonSpacedNode) {
|
||||
if (!(int32Hint && sourceCode.getText(node).endsWith("|0"))) report(node, nonSpacedNode);
|
||||
}
|
||||
}
|
||||
function isSpaceChar(token) {
|
||||
return token.type === AST_TOKEN_TYPES.Punctuator && /^[=?:]$/.test(token.value);
|
||||
}
|
||||
function checkAndReportAssignmentSpace(node, leftNode, rightNode) {
|
||||
if (!rightNode || !leftNode) return;
|
||||
const operator = sourceCode.getFirstTokenBetween(leftNode, rightNode, isSpaceChar);
|
||||
const prev = sourceCode.getTokenBefore(operator);
|
||||
const next = sourceCode.getTokenAfter(operator);
|
||||
if (!sourceCode.isSpaceBetween(prev, operator) || !sourceCode.isSpaceBetween(operator, next)) report(node, operator);
|
||||
}
|
||||
function checkPropertyAssignment(node) {
|
||||
checkAndReportAssignmentSpace(node, node.optional && !node.typeAnnotation ? sourceCode.getTokenAfter(node.key) : node.typeAnnotation ?? node.key, node.value);
|
||||
}
|
||||
function checkTSBinary(typeAnnotation) {
|
||||
typeAnnotation.types.forEach((type) => {
|
||||
const skipFunctionParenthesis = type.type === AST_NODE_TYPES.TSFunctionType ? import_ast_utils.isNotOpeningParenToken : 0;
|
||||
const operator = sourceCode.getTokenBefore(type, skipFunctionParenthesis);
|
||||
if (!ignoreTypes && operator != null && UNIONS.includes(operator.value)) {
|
||||
const prev = sourceCode.getTokenBefore(operator);
|
||||
const next = sourceCode.getTokenAfter(operator);
|
||||
if (!sourceCode.isSpaceBetween(prev, operator) || !sourceCode.isSpaceBetween(operator, next)) report(typeAnnotation, operator);
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
AssignmentExpression: checkBinary,
|
||||
AssignmentPattern: checkBinary,
|
||||
BinaryExpression: checkBinary,
|
||||
LogicalExpression: checkBinary,
|
||||
ConditionalExpression(node) {
|
||||
checkAndReportAssignmentSpace(node, node.test, node.consequent);
|
||||
checkAndReportAssignmentSpace(node, node.consequent, node.alternate);
|
||||
},
|
||||
VariableDeclarator(node) {
|
||||
checkAndReportAssignmentSpace(node, node.id.typeAnnotation ?? node.id, node.init);
|
||||
},
|
||||
PropertyDefinition: checkPropertyAssignment,
|
||||
AccessorProperty: checkPropertyAssignment,
|
||||
TSEnumMember(node) {
|
||||
checkAndReportAssignmentSpace(node, node.id, node.initializer);
|
||||
},
|
||||
TSTypeAliasDeclaration(node) {
|
||||
checkAndReportAssignmentSpace(node, node.typeParameters ?? node.id, node.typeAnnotation);
|
||||
},
|
||||
TSUnionType: checkTSBinary,
|
||||
TSIntersectionType: checkTSBinary,
|
||||
TSConditionalType(node) {
|
||||
checkAndReportAssignmentSpace(node, node.extendsType, node.trueType);
|
||||
checkAndReportAssignmentSpace(node, node.trueType, node.falseType);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { space_infix_ops_default as t };
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
import { f as createRule, w as canTokensBeAdjacent, z as isKeywordToken } from "../utils.js";
|
||||
var space_unary_ops_default = createRule({
|
||||
name: "space-unary-ops",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent spacing before or after unary operators" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
words: { type: "boolean" },
|
||||
nonwords: { type: "boolean" },
|
||||
overrides: {
|
||||
type: "object",
|
||||
additionalProperties: { type: "boolean" }
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
words: true,
|
||||
nonwords: false
|
||||
}],
|
||||
messages: {
|
||||
unexpectedBefore: "Unexpected space before unary operator '{{operator}}'.",
|
||||
unexpectedAfter: "Unexpected space after unary operator '{{operator}}'.",
|
||||
unexpectedAfterWord: "Unexpected space after unary word operator '{{word}}'.",
|
||||
requireAfterWord: "Unary word operator '{{word}}' must be followed by whitespace.",
|
||||
requireAfter: "Unary operator '{{operator}}' must be followed by whitespace.",
|
||||
requireBefore: "Space is required before unary operator '{{operator}}'."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const { words, nonwords, overrides = {} } = options;
|
||||
const sourceCode = context.sourceCode;
|
||||
function isFirstBangInBangBangExpression(node) {
|
||||
return node && node.type === "UnaryExpression" && node.argument && node.argument.type === "UnaryExpression" && node.argument.operator === "!";
|
||||
}
|
||||
function verifyWordHasSpaces(node, firstToken, secondToken, word) {
|
||||
if (secondToken.range[0] === firstToken.range[1]) context.report({
|
||||
node,
|
||||
messageId: "requireAfterWord",
|
||||
data: { word },
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(firstToken, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
function verifyWordDoesntHaveSpaces(node, firstToken, secondToken, word) {
|
||||
if (canTokensBeAdjacent(firstToken, secondToken)) {
|
||||
if (secondToken.range[0] > firstToken.range[1]) context.report({
|
||||
node,
|
||||
messageId: "unexpectedAfterWord",
|
||||
data: { word },
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function checkUnaryWordOperatorForSpaces(node, firstToken, secondToken, word) {
|
||||
if (overrides[word] ?? words) verifyWordHasSpaces(node, firstToken, secondToken, word);
|
||||
else verifyWordDoesntHaveSpaces(node, firstToken, secondToken, word);
|
||||
}
|
||||
function checkForSpacesAroundNonNull(node) {
|
||||
const operator = "!";
|
||||
const operatorToken = sourceCode.getLastToken(node, (token) => token.value === operator);
|
||||
const prefixToken = sourceCode.getTokenBefore(operatorToken);
|
||||
if (overrides["ts-non-null"] ?? overrides[operator] ?? nonwords) verifyNonWordsHaveSpaces(node, prefixToken, operatorToken);
|
||||
else verifyNonWordsDontHaveSpaces(node, prefixToken, operatorToken);
|
||||
}
|
||||
function checkForSpacesAfterYield(node) {
|
||||
const tokens = sourceCode.getFirstTokens(node, 3);
|
||||
const word = "yield";
|
||||
if (!node.argument || node.delegate) return;
|
||||
checkUnaryWordOperatorForSpaces(node, tokens[0], tokens[1], word);
|
||||
}
|
||||
function checkForSpacesAfterAwait(node) {
|
||||
const tokens = sourceCode.getFirstTokens(node, 3);
|
||||
checkUnaryWordOperatorForSpaces(node, tokens[0], tokens[1], "await");
|
||||
}
|
||||
function verifyNonWordsHaveSpaces(node, firstToken, secondToken) {
|
||||
if ("prefix" in node && node.prefix) {
|
||||
if (isFirstBangInBangBangExpression(node)) return;
|
||||
if (firstToken.range[1] === secondToken.range[0]) context.report({
|
||||
node,
|
||||
messageId: "requireAfter",
|
||||
data: { operator: firstToken.value },
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(firstToken, " ");
|
||||
}
|
||||
});
|
||||
} else if (firstToken.range[1] === secondToken.range[0]) context.report({
|
||||
node,
|
||||
messageId: "requireBefore",
|
||||
data: { operator: secondToken.value },
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(secondToken, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
function verifyNonWordsDontHaveSpaces(node, firstToken, secondToken) {
|
||||
if ("prefix" in node && node.prefix) {
|
||||
if (secondToken.range[0] > firstToken.range[1]) context.report({
|
||||
node,
|
||||
messageId: "unexpectedAfter",
|
||||
data: { operator: firstToken.value },
|
||||
fix(fixer) {
|
||||
if (canTokensBeAdjacent(firstToken, secondToken)) return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} else if (secondToken.range[0] > firstToken.range[1]) context.report({
|
||||
node,
|
||||
messageId: "unexpectedBefore",
|
||||
data: { operator: secondToken.value },
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([firstToken.range[1], secondToken.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
function checkForSpaces(node) {
|
||||
const tokens = node.type === "UpdateExpression" && !node.prefix ? sourceCode.getLastTokens(node, 2) : sourceCode.getFirstTokens(node, 2);
|
||||
const firstToken = tokens[0];
|
||||
const secondToken = tokens[1];
|
||||
if ((node.type === "NewExpression" || node.prefix) && isKeywordToken(firstToken)) {
|
||||
checkUnaryWordOperatorForSpaces(node, firstToken, secondToken, firstToken.value);
|
||||
return;
|
||||
}
|
||||
if (overrides["prefix" in node && node.prefix ? tokens[0].value : tokens[1].value] ?? nonwords) verifyNonWordsHaveSpaces(node, firstToken, secondToken);
|
||||
else verifyNonWordsDontHaveSpaces(node, firstToken, secondToken);
|
||||
}
|
||||
return {
|
||||
UnaryExpression: checkForSpaces,
|
||||
UpdateExpression: checkForSpaces,
|
||||
NewExpression: checkForSpaces,
|
||||
YieldExpression: checkForSpacesAfterYield,
|
||||
AwaitExpression: checkForSpacesAfterAwait,
|
||||
TSNonNullExpression: checkForSpacesAroundNonNull
|
||||
};
|
||||
}
|
||||
});
|
||||
export { space_unary_ops_default as t };
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
import { L as isHashbangComment, f as createRule, x as LINEBREAKS } from "../utils.js";
|
||||
import { t as escapeStringRegexp } from "../vendor.js";
|
||||
function escape(s) {
|
||||
return `(?:${escapeStringRegexp(s)})`;
|
||||
}
|
||||
function escapeAndRepeat(s) {
|
||||
return `${escape(s)}+`;
|
||||
}
|
||||
function parseMarkersOption(markers) {
|
||||
if (!markers.includes("*")) return markers.concat("*");
|
||||
return markers;
|
||||
}
|
||||
function createExceptionsPattern(exceptions) {
|
||||
let pattern = "";
|
||||
if (exceptions.length === 0) pattern += "\\s";
|
||||
else {
|
||||
pattern += "(?:\\s|";
|
||||
if (exceptions.length === 1) pattern += escapeAndRepeat(exceptions[0]);
|
||||
else {
|
||||
pattern += "(?:";
|
||||
pattern += exceptions.map(escapeAndRepeat).join("|");
|
||||
pattern += ")";
|
||||
}
|
||||
pattern += `(?:$|[${Array.from(LINEBREAKS).join("")}]))`;
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
function createAlwaysStylePattern(markers, exceptions) {
|
||||
let pattern = "^";
|
||||
if (markers.length === 1) pattern += escape(markers[0]);
|
||||
else {
|
||||
pattern += "(?:";
|
||||
pattern += markers.map(escape).join("|");
|
||||
pattern += ")";
|
||||
}
|
||||
pattern += "?";
|
||||
pattern += createExceptionsPattern(exceptions);
|
||||
return new RegExp(pattern, "u");
|
||||
}
|
||||
function createNeverStylePattern(markers) {
|
||||
const pattern = `^(${markers.map(escape).join("|")})?[ \t]+`;
|
||||
return new RegExp(pattern, "u");
|
||||
}
|
||||
var spaced_comment_default = createRule({
|
||||
name: "spaced-comment",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce consistent spacing after the `//` or `/*` in a comment" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}, {
|
||||
type: "object",
|
||||
properties: {
|
||||
exceptions: {
|
||||
type: "array",
|
||||
items: { type: "string" }
|
||||
},
|
||||
markers: {
|
||||
type: "array",
|
||||
items: { type: "string" }
|
||||
},
|
||||
line: {
|
||||
type: "object",
|
||||
properties: {
|
||||
exceptions: {
|
||||
type: "array",
|
||||
items: { type: "string" }
|
||||
},
|
||||
markers: {
|
||||
type: "array",
|
||||
items: { type: "string" }
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
},
|
||||
block: {
|
||||
type: "object",
|
||||
properties: {
|
||||
exceptions: {
|
||||
type: "array",
|
||||
items: { type: "string" }
|
||||
},
|
||||
markers: {
|
||||
type: "array",
|
||||
items: { type: "string" }
|
||||
},
|
||||
balanced: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: ["always"],
|
||||
messages: {
|
||||
unexpectedSpaceAfterMarker: "Unexpected space or tab after marker ({{refChar}}) in comment.",
|
||||
expectedExceptionAfter: "Expected exception block, space or tab after '{{refChar}}' in comment.",
|
||||
unexpectedSpaceBefore: "Unexpected space or tab before '*/' in comment.",
|
||||
unexpectedSpaceAfter: "Unexpected space or tab after '{{refChar}}' in comment.",
|
||||
expectedSpaceBefore: "Expected space or tab before '*/' in comment.",
|
||||
expectedSpaceAfter: "Expected space or tab after '{{refChar}}' in comment."
|
||||
}
|
||||
},
|
||||
create(context, [style, config = {}]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const requireSpace = style !== "never";
|
||||
const balanced = config.block && config.block.balanced;
|
||||
const styleRules = ["block", "line"].reduce((rule, type) => {
|
||||
const nodeType = type;
|
||||
const markers = parseMarkersOption(config[nodeType] && config[nodeType]?.markers || config.markers || []);
|
||||
const exceptions = config[nodeType] && config[nodeType]?.exceptions || config.exceptions || [];
|
||||
rule[nodeType] = {
|
||||
beginRegex: requireSpace ? createAlwaysStylePattern(markers, exceptions) : createNeverStylePattern(markers),
|
||||
endRegex: balanced && requireSpace ? new RegExp(`${createExceptionsPattern(exceptions)}$`, "u") : /* @__PURE__ */ new RegExp("[ ]+$", "u"),
|
||||
hasExceptions: exceptions.length > 0,
|
||||
captureMarker: new RegExp(`^(${markers.map(escape).join("|")})`, "u"),
|
||||
markers: new Set(markers)
|
||||
};
|
||||
return rule;
|
||||
}, {});
|
||||
function reportBegin(node, messageId, match, refChar) {
|
||||
const commentIdentifier = node.type.toLowerCase() === "block" ? "/*" : "//";
|
||||
context.report({
|
||||
node,
|
||||
fix(fixer) {
|
||||
const start = node.range[0];
|
||||
let end = start + 2;
|
||||
if (requireSpace) {
|
||||
if (match) end += match[0].length;
|
||||
return fixer.insertTextAfterRange([start, end], " ");
|
||||
}
|
||||
if (match) end += match[0].length;
|
||||
return fixer.replaceTextRange([start, end], commentIdentifier + (match && match[1] ? match[1] : ""));
|
||||
},
|
||||
messageId,
|
||||
data: { refChar }
|
||||
});
|
||||
}
|
||||
function reportEnd(node, messageId, match) {
|
||||
context.report({
|
||||
node,
|
||||
fix(fixer) {
|
||||
if (requireSpace) return fixer.insertTextAfterRange([node.range[0], node.range[1] - 2], " ");
|
||||
const end = node.range[1] - 2;
|
||||
let start = end;
|
||||
if (match) start -= match[0].length;
|
||||
return fixer.replaceTextRange([start, end], "");
|
||||
},
|
||||
messageId
|
||||
});
|
||||
}
|
||||
function checkCommentForSpace(node) {
|
||||
const type = node.type.toLowerCase();
|
||||
const rule = styleRules[type];
|
||||
const commentIdentifier = type === "block" ? "/*" : "//";
|
||||
if (node.value.length === 0 || rule.markers.has(node.value)) return;
|
||||
if (type === "line" && (node.value.startsWith("/ <reference") || node.value.startsWith("/ <amd"))) return;
|
||||
const beginMatch = rule.beginRegex.exec(node.value);
|
||||
const endMatch = rule.endRegex.exec(node.value);
|
||||
if (requireSpace) {
|
||||
if (!beginMatch) {
|
||||
const hasMarker = rule.captureMarker.exec(node.value);
|
||||
const marker = hasMarker ? commentIdentifier + hasMarker[0] : commentIdentifier;
|
||||
if (rule.hasExceptions) reportBegin(node, "expectedExceptionAfter", hasMarker, marker);
|
||||
else reportBegin(node, "expectedSpaceAfter", hasMarker, marker);
|
||||
}
|
||||
if (balanced && type === "block" && !endMatch) reportEnd(node, "expectedSpaceBefore", null);
|
||||
} else {
|
||||
if (beginMatch) if (!beginMatch[1]) reportBegin(node, "unexpectedSpaceAfter", beginMatch, commentIdentifier);
|
||||
else reportBegin(node, "unexpectedSpaceAfterMarker", beginMatch, beginMatch[1]);
|
||||
if (balanced && type === "block" && endMatch) reportEnd(node, "unexpectedSpaceBefore", endMatch);
|
||||
}
|
||||
}
|
||||
return { Program() {
|
||||
sourceCode.getAllComments().forEach((comment) => {
|
||||
if (!isHashbangComment(comment)) checkCommentForSpace(comment);
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { spaced_comment_default as t };
|
||||
Generated
Vendored
+58
@@ -0,0 +1,58 @@
|
||||
import { f as createRule, g as import_ast_utils, j as getSwitchCaseColonToken } from "../utils.js";
|
||||
var switch_colon_spacing_default = createRule({
|
||||
name: "switch-colon-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Enforce spacing around colons of switch statements" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "object",
|
||||
properties: {
|
||||
before: { type: "boolean" },
|
||||
after: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{
|
||||
before: false,
|
||||
after: true
|
||||
}],
|
||||
messages: {
|
||||
expectedBefore: "Expected space(s) before this colon.",
|
||||
expectedAfter: "Expected space(s) after this colon.",
|
||||
unexpectedBefore: "Unexpected space(s) before this colon.",
|
||||
unexpectedAfter: "Unexpected space(s) after this colon."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const beforeSpacing = options.before === true;
|
||||
const afterSpacing = options.after !== false;
|
||||
function isValidSpacing(left, right, expected) {
|
||||
return (0, import_ast_utils.isClosingBraceToken)(right) || !(0, import_ast_utils.isTokenOnSameLine)(left, right) || sourceCode.isSpaceBetween(left, right) === expected;
|
||||
}
|
||||
function fix(fixer, left, right, spacing) {
|
||||
if (sourceCode.commentsExistBetween(left, right)) return null;
|
||||
if (spacing) return fixer.insertTextAfter(left, " ");
|
||||
return fixer.removeRange([left.range[1], right.range[0]]);
|
||||
}
|
||||
return { SwitchCase(node) {
|
||||
const colonToken = getSwitchCaseColonToken(node, sourceCode);
|
||||
const beforeToken = sourceCode.getTokenBefore(colonToken);
|
||||
const afterToken = sourceCode.getTokenAfter(colonToken);
|
||||
if (!isValidSpacing(beforeToken, colonToken, beforeSpacing)) context.report({
|
||||
node,
|
||||
loc: colonToken.loc,
|
||||
messageId: beforeSpacing ? "expectedBefore" : "unexpectedBefore",
|
||||
fix: (fixer) => fix(fixer, beforeToken, colonToken, beforeSpacing)
|
||||
});
|
||||
if (!isValidSpacing(colonToken, afterToken, afterSpacing)) context.report({
|
||||
node,
|
||||
loc: colonToken.loc,
|
||||
messageId: afterSpacing ? "expectedAfter" : "unexpectedAfter",
|
||||
fix: (fixer) => fix(fixer, colonToken, afterToken, afterSpacing)
|
||||
});
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { switch_colon_spacing_default as t };
|
||||
Generated
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
import { f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
var template_curly_spacing_default = createRule({
|
||||
name: "template-curly-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require or disallow spacing around embedded expressions of template strings" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}],
|
||||
defaultOptions: ["never"],
|
||||
messages: {
|
||||
expectedBefore: "Expected space(s) before '}'.",
|
||||
expectedAfter: "Expected space(s) after '${'.",
|
||||
unexpectedBefore: "Unexpected space(s) before '}'.",
|
||||
unexpectedAfter: "Unexpected space(s) after '${'."
|
||||
}
|
||||
},
|
||||
create(context, [style]) {
|
||||
const sourceCode = context.sourceCode;
|
||||
const always = style === "always";
|
||||
function checkSpacingBefore(token) {
|
||||
if (!token.value.startsWith("}")) return;
|
||||
const prevToken = sourceCode.getTokenBefore(token, { includeComments: true });
|
||||
const hasSpace = sourceCode.isSpaceBetween(prevToken, token);
|
||||
if (!(0, import_ast_utils.isTokenOnSameLine)(prevToken, token)) return;
|
||||
if (always && !hasSpace) context.report({
|
||||
loc: {
|
||||
start: token.loc.start,
|
||||
end: {
|
||||
line: token.loc.start.line,
|
||||
column: token.loc.start.column + 1
|
||||
}
|
||||
},
|
||||
messageId: "expectedBefore",
|
||||
fix: (fixer) => fixer.insertTextBefore(token, " ")
|
||||
});
|
||||
if (!always && hasSpace) context.report({
|
||||
loc: {
|
||||
start: prevToken.loc.end,
|
||||
end: token.loc.start
|
||||
},
|
||||
messageId: "unexpectedBefore",
|
||||
fix: (fixer) => fixer.removeRange([prevToken.range[1], token.range[0]])
|
||||
});
|
||||
}
|
||||
function checkSpacingAfter(token) {
|
||||
if (!token.value.endsWith("${")) return;
|
||||
const nextToken = sourceCode.getTokenAfter(token, { includeComments: true });
|
||||
const hasSpace = sourceCode.isSpaceBetween(token, nextToken);
|
||||
if (!(0, import_ast_utils.isTokenOnSameLine)(token, nextToken)) return;
|
||||
if (always && !hasSpace) context.report({
|
||||
loc: {
|
||||
start: {
|
||||
line: token.loc.end.line,
|
||||
column: token.loc.end.column - 2
|
||||
},
|
||||
end: token.loc.end
|
||||
},
|
||||
messageId: "expectedAfter",
|
||||
fix: (fixer) => fixer.insertTextAfter(token, " ")
|
||||
});
|
||||
if (!always && hasSpace) context.report({
|
||||
loc: {
|
||||
start: token.loc.end,
|
||||
end: nextToken.loc.start
|
||||
},
|
||||
messageId: "unexpectedAfter",
|
||||
fix: (fixer) => fixer.removeRange([token.range[1], nextToken.range[0]])
|
||||
});
|
||||
}
|
||||
return { TemplateElement(node) {
|
||||
const token = sourceCode.getFirstToken(node);
|
||||
checkSpacingBefore(token);
|
||||
checkSpacingAfter(token);
|
||||
} };
|
||||
}
|
||||
});
|
||||
export { template_curly_spacing_default as t };
|
||||
Generated
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
import { f as createRule } from "../utils.js";
|
||||
var template_tag_spacing_default = createRule({
|
||||
name: "template-tag-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require or disallow spacing between template tags and their literals" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
type: "string",
|
||||
enum: ["always", "never"]
|
||||
}],
|
||||
defaultOptions: ["never"],
|
||||
messages: {
|
||||
unexpected: "Unexpected space between template tag and template literal.",
|
||||
missing: "Missing space between template tag and template literal."
|
||||
}
|
||||
},
|
||||
create(context, [style]) {
|
||||
const never = style !== "always";
|
||||
const sourceCode = context.sourceCode;
|
||||
function checkSpacing(node) {
|
||||
const tagToken = sourceCode.getTokenBefore(node.quasi);
|
||||
const literalToken = sourceCode.getFirstToken(node.quasi);
|
||||
const hasWhitespace = sourceCode.isSpaceBetween(tagToken, literalToken);
|
||||
if (never && hasWhitespace) context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: tagToken.loc.end,
|
||||
end: literalToken.loc.start
|
||||
},
|
||||
messageId: "unexpected",
|
||||
fix(fixer) {
|
||||
const comments = sourceCode.getCommentsBefore(node.quasi);
|
||||
if (comments.some((comment) => comment.type === "Line")) return null;
|
||||
return fixer.replaceTextRange([tagToken.range[1], literalToken.range[0]], comments.reduce((text, comment) => text + sourceCode.getText(comment), ""));
|
||||
}
|
||||
});
|
||||
else if (!never && !hasWhitespace) context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: node.loc.start,
|
||||
end: literalToken.loc.start
|
||||
},
|
||||
messageId: "missing",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(tagToken, " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
return { TaggedTemplateExpression: checkSpacing };
|
||||
}
|
||||
});
|
||||
export { template_tag_spacing_default as t };
|
||||
Generated
Vendored
+181
@@ -0,0 +1,181 @@
|
||||
import { $ as warnDeprecation, f as createRule, g as import_ast_utils } from "../utils.js";
|
||||
function createRules(options) {
|
||||
const globals = {
|
||||
...options?.before !== void 0 ? { before: options.before } : {},
|
||||
...options?.after !== void 0 ? { after: options.after } : {}
|
||||
};
|
||||
const override = options?.overrides ?? {};
|
||||
const colon = {
|
||||
before: false,
|
||||
after: true,
|
||||
...globals,
|
||||
...override?.colon
|
||||
};
|
||||
const arrow = typeof override.arrow === "string" ? override.arrow : {
|
||||
before: true,
|
||||
after: true,
|
||||
...globals,
|
||||
...override?.arrow
|
||||
};
|
||||
if (Object.hasOwn(override, "arrow") && override.arrow !== "ignore") warnDeprecation("options(\"overrides.arrow\")", "\"arrow-spacing\"", "type-annotation-spacing");
|
||||
return {
|
||||
colon,
|
||||
arrow,
|
||||
variable: {
|
||||
...colon,
|
||||
...override?.variable
|
||||
},
|
||||
property: {
|
||||
...colon,
|
||||
...override?.property
|
||||
},
|
||||
parameter: {
|
||||
...colon,
|
||||
...override?.parameter
|
||||
},
|
||||
returnType: {
|
||||
...colon,
|
||||
...override?.returnType
|
||||
}
|
||||
};
|
||||
}
|
||||
function getIdentifierRules(rules, node) {
|
||||
const scope = node?.parent;
|
||||
if ((0, import_ast_utils.isVariableDeclarator)(scope)) return rules.variable;
|
||||
else if ((0, import_ast_utils.isFunctionOrFunctionType)(scope)) return rules.parameter;
|
||||
return rules.colon;
|
||||
}
|
||||
function getRules(rules, node) {
|
||||
const scope = node?.parent?.parent;
|
||||
if ((0, import_ast_utils.isTSFunctionType)(scope) || (0, import_ast_utils.isTSConstructorType)(scope)) return rules.arrow;
|
||||
else if ((0, import_ast_utils.isIdentifier)(scope)) return getIdentifierRules(rules, scope);
|
||||
else if ((0, import_ast_utils.isClassOrTypeElement)(scope)) return rules.property;
|
||||
else if ((0, import_ast_utils.isFunction)(scope)) return rules.returnType;
|
||||
return rules.colon;
|
||||
}
|
||||
var type_annotation_spacing_default = createRule({
|
||||
name: "type-annotation-spacing",
|
||||
meta: {
|
||||
type: "layout",
|
||||
docs: { description: "Require consistent spacing around type annotations" },
|
||||
fixable: "whitespace",
|
||||
schema: [{
|
||||
$defs: { spacingConfig: {
|
||||
type: "object",
|
||||
properties: {
|
||||
before: { type: "boolean" },
|
||||
after: { type: "boolean" }
|
||||
},
|
||||
additionalProperties: false
|
||||
} },
|
||||
type: "object",
|
||||
properties: {
|
||||
before: { type: "boolean" },
|
||||
after: { type: "boolean" },
|
||||
overrides: {
|
||||
type: "object",
|
||||
properties: {
|
||||
colon: { $ref: "#/items/0/$defs/spacingConfig" },
|
||||
arrow: { oneOf: [{
|
||||
type: "string",
|
||||
enum: ["ignore"]
|
||||
}, { $ref: "#/items/0/$defs/spacingConfig" }] },
|
||||
variable: { $ref: "#/items/0/$defs/spacingConfig" },
|
||||
parameter: { $ref: "#/items/0/$defs/spacingConfig" },
|
||||
property: { $ref: "#/items/0/$defs/spacingConfig" },
|
||||
returnType: { $ref: "#/items/0/$defs/spacingConfig" }
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}],
|
||||
defaultOptions: [{}],
|
||||
messages: {
|
||||
expectedSpaceAfter: "Expected a space after the '{{type}}'.",
|
||||
expectedSpaceBefore: "Expected a space before the '{{type}}'.",
|
||||
unexpectedSpaceAfter: "Unexpected space after the '{{type}}'.",
|
||||
unexpectedSpaceBefore: "Unexpected space before the '{{type}}'.",
|
||||
unexpectedSpaceBetween: "Unexpected space between the '{{previousToken}}' and the '{{type}}'."
|
||||
}
|
||||
},
|
||||
create(context, [options]) {
|
||||
const punctuators = [":", "=>"];
|
||||
const sourceCode = context.sourceCode;
|
||||
const ruleSet = createRules(options);
|
||||
function checkTypeAnnotationSpacing(typeAnnotation) {
|
||||
const punctuatorTokenEnd = sourceCode.getTokenBefore(typeAnnotation, import_ast_utils.isNotOpeningParenToken);
|
||||
let punctuatorTokenStart = punctuatorTokenEnd;
|
||||
let previousToken = sourceCode.getTokenBefore(punctuatorTokenEnd);
|
||||
let type = punctuatorTokenEnd.value;
|
||||
if (!punctuators.includes(type)) return;
|
||||
const config = getRules(ruleSet, typeAnnotation);
|
||||
if (config === "ignore") return;
|
||||
const { before, after } = config;
|
||||
if (type === ":" && previousToken.value === "?") {
|
||||
if (sourceCode.isSpaceBetween(previousToken, punctuatorTokenStart)) context.report({
|
||||
node: punctuatorTokenStart,
|
||||
messageId: "unexpectedSpaceBetween",
|
||||
data: {
|
||||
type,
|
||||
previousToken: previousToken.value
|
||||
},
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([previousToken.range[1], punctuatorTokenStart.range[0]]);
|
||||
}
|
||||
});
|
||||
type = "?:";
|
||||
punctuatorTokenStart = previousToken;
|
||||
previousToken = sourceCode.getTokenBefore(previousToken);
|
||||
if (previousToken.value === "+" || previousToken.value === "-") {
|
||||
type = `${previousToken.value}?:`;
|
||||
punctuatorTokenStart = previousToken;
|
||||
previousToken = sourceCode.getTokenBefore(previousToken);
|
||||
}
|
||||
}
|
||||
const hasNextSpace = sourceCode.isSpaceBetween(punctuatorTokenEnd, typeAnnotation);
|
||||
if (after && !hasNextSpace) context.report({
|
||||
node: punctuatorTokenEnd,
|
||||
messageId: "expectedSpaceAfter",
|
||||
data: { type },
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(punctuatorTokenEnd, " ");
|
||||
}
|
||||
});
|
||||
else if (!after && hasNextSpace) context.report({
|
||||
node: punctuatorTokenEnd,
|
||||
messageId: "unexpectedSpaceAfter",
|
||||
data: { type },
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([punctuatorTokenEnd.range[1], typeAnnotation.range[0]]);
|
||||
}
|
||||
});
|
||||
const hasPrevSpace = sourceCode.isSpaceBetween(previousToken, punctuatorTokenStart);
|
||||
if (before && !hasPrevSpace) context.report({
|
||||
node: punctuatorTokenStart,
|
||||
messageId: "expectedSpaceBefore",
|
||||
data: { type },
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(previousToken, " ");
|
||||
}
|
||||
});
|
||||
else if (!before && hasPrevSpace) context.report({
|
||||
node: punctuatorTokenStart,
|
||||
messageId: "unexpectedSpaceBefore",
|
||||
data: { type },
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([previousToken.range[1], punctuatorTokenStart.range[0]]);
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
TSMappedType(node) {
|
||||
if (node.typeAnnotation) checkTypeAnnotationSpacing(node.typeAnnotation);
|
||||
},
|
||||
TSTypeAnnotation(node) {
|
||||
checkTypeAnnotationSpacing(node.typeAnnotation);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
export { type_annotation_spacing_default as t };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user