@ttsc/lint 0.18.4 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -43
- package/go.mod +5 -4
- package/internal/rulecode/rulecode.go +88 -0
- package/lib/index.d.ts +10 -1
- package/lib/index.js +45 -17
- package/lib/index.js.map +1 -1
- package/lib/structures/ITtscLintConfig.d.ts +3 -0
- package/lib/structures/TtscLintRuleSetting.d.ts +3 -2
- package/lib/structures/format/ITtscLintFormat.d.ts +3 -2
- package/lib/structures/format/ITtscLintFormatSortImports.d.ts +20 -4
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +80 -8
- package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +6 -8
- package/lib/structures/rules/ITtscLintContributorRules.d.ts +18 -2
- package/lib/structures/rules/ITtscLintCoreRuleOptions.d.ts +347 -0
- package/lib/structures/rules/ITtscLintCoreRuleOptions.js +3 -0
- package/lib/structures/rules/ITtscLintCoreRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintCoreRules.d.ts +101 -67
- package/lib/structures/rules/ITtscLintRegexpRules.d.ts +7 -2
- package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +53 -7
- package/lib/structures/rules/ITtscLintRules.d.ts +6 -2
- package/lib/structures/rules/ITtscLintSolidRules.d.ts +0 -8
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.d.ts +206 -0
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.js +8 -0
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +55 -42
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.d.ts +276 -0
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.js +3 -0
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintUnicornRules.d.ts +44 -36
- package/lib/structures/rules/index.d.ts +3 -0
- package/lib/structures/rules/index.js +3 -0
- package/lib/structures/rules/index.js.map +1 -1
- package/linthost/ast_helpers.go +521 -23
- package/linthost/ast_selector_matcher.go +1628 -0
- package/linthost/ast_selector_parser.go +628 -0
- package/linthost/comment_scan.go +154 -0
- package/linthost/compile.go +129 -79
- package/linthost/config.go +352 -88
- package/linthost/config_format.go +15 -1
- package/linthost/contrib_adapter.go +47 -8
- package/linthost/declaration_rules.go +12 -11
- package/linthost/directives.go +9 -67
- package/linthost/dispatch.go +37 -3
- package/linthost/engine.go +368 -53
- package/linthost/escape_string.go +77 -0
- package/linthost/fix.go +133 -23
- package/linthost/flags_gen.go +18 -17
- package/linthost/format.go +73 -167
- package/linthost/format_editor_settings.go +134 -26
- package/linthost/grapheme_tables_gen.go +1920 -0
- package/linthost/host.go +160 -61
- package/linthost/literal_escape_scan.go +117 -0
- package/linthost/lsp.go +545 -98
- package/linthost/numeric_literal_precision.go +228 -0
- package/linthost/polyfill_browserslist.go +1439 -0
- package/linthost/polyfill_browserslist_config.go +603 -0
- package/linthost/polyfill_corejs.go +750 -0
- package/linthost/polyfill_data.go +113 -0
- package/linthost/polyfill_data_gen.json +39868 -0
- package/linthost/print_nodes_array.go +7 -1
- package/linthost/print_nodes_object.go +7 -1
- package/linthost/project_engine.go +151 -0
- package/linthost/project_rules.go +78 -0
- package/linthost/regex_clean.go +191 -0
- package/linthost/regex_tree.go +1544 -0
- package/linthost/regex_tree_optimizer.go +1685 -0
- package/linthost/regexp_cache.go +44 -0
- package/linthost/rule_codes.go +105 -0
- package/linthost/rule_codes.json +745 -0
- package/linthost/rules_ban_ts_comment.go +312 -0
- package/linthost/rules_boundaries.go +128 -43
- package/linthost/rules_boundaries_dependencies.go +1013 -0
- package/linthost/rules_core_extra.go +62 -92
- package/linthost/rules_cypress.go +1 -1
- package/linthost/rules_default_case.go +87 -11
- package/linthost/rules_dupes.go +38 -13
- package/linthost/rules_empty.go +188 -20
- package/linthost/rules_escape.go +62 -48
- package/linthost/rules_format_arrow_parens.go +19 -5
- package/linthost/rules_format_bracket_spacing.go +1 -1
- package/linthost/rules_format_clause_join.go +1 -1
- package/linthost/rules_format_declaration_header.go +31 -19
- package/linthost/rules_format_indent.go +1 -1
- package/linthost/rules_format_jsdoc.go +1 -1
- package/linthost/rules_format_orphan_semi.go +1 -1
- package/linthost/rules_format_parameter_properties.go +17 -7
- package/linthost/rules_format_print_width.go +1 -1
- package/linthost/rules_format_quote_props.go +1 -1
- package/linthost/rules_format_quotes.go +1 -1
- package/linthost/rules_format_semi.go +106 -79
- package/linthost/rules_format_sort_imports.go +79 -45
- package/linthost/rules_format_statement_split.go +1 -1
- package/linthost/rules_format_ternary_nullish_parens.go +1 -1
- package/linthost/rules_format_trailing_comma.go +64 -1
- package/linthost/rules_format_whitespace.go +1 -1
- package/linthost/rules_functional.go +21 -22
- package/linthost/rules_gap.go +80 -3
- package/linthost/rules_grouped_accessor_pairs.go +84 -25
- package/linthost/rules_guard_for_in.go +64 -147
- package/linthost/rules_jsx_a11y.go +232 -22
- package/linthost/rules_logic.go +84 -38
- package/linthost/rules_nextjs.go +105 -16
- package/linthost/rules_no_class_assign.go +70 -0
- package/linthost/rules_no_duplicate_imports.go +266 -0
- package/linthost/rules_no_else_return.go +156 -38
- package/linthost/rules_no_extend_native.go +113 -17
- package/linthost/rules_no_fallthrough.go +1013 -0
- package/linthost/rules_no_func_assign.go +70 -0
- package/linthost/rules_no_import_assign.go +278 -86
- package/linthost/rules_no_inner_declarations.go +186 -0
- package/linthost/rules_no_loop_func.go +438 -53
- package/linthost/rules_no_mixed_operators.go +208 -52
- package/linthost/rules_no_param_reassign.go +376 -61
- package/linthost/rules_no_promise_executor_return.go +156 -0
- package/linthost/rules_no_restricted_imports.go +1013 -57
- package/linthost/rules_no_restricted_syntax.go +190 -26
- package/linthost/rules_no_useless_constructor.go +6 -36
- package/linthost/rules_problems.go +154 -311
- package/linthost/rules_promise.go +2578 -109
- package/linthost/rules_react_extras.go +71 -45
- package/linthost/rules_react_hooks.go +18 -2
- package/linthost/rules_react_perf.go +1 -0
- package/linthost/rules_react_refresh.go +1 -1
- package/linthost/rules_regexp.go +230 -14
- package/linthost/rules_security.go +45 -19
- package/linthost/rules_self.go +21 -1
- package/linthost/rules_solid.go +13 -7
- package/linthost/rules_storybook.go +1 -1
- package/linthost/rules_suggestions.go +490 -84
- package/linthost/rules_testing_library.go +12 -1
- package/linthost/rules_ts.go +227 -54
- package/linthost/rules_ts_async.go +0 -131
- package/linthost/rules_ts_no_base_to_string.go +3 -4
- package/linthost/rules_ts_no_misused_promises.go +817 -0
- package/linthost/rules_ts_no_restricted_types.go +320 -34
- package/linthost/rules_ts_no_unnecessary_qualifier.go +132 -55
- package/linthost/rules_ts_no_unsafe_assignment.go +608 -60
- package/linthost/rules_ts_require_array_sort_compare.go +5 -5
- package/linthost/rules_ts_switch_exhaustiveness_check.go +556 -86
- package/linthost/rules_unicorn_better_regex.go +224 -16
- package/linthost/rules_unicorn_consistent_destructuring.go +1002 -7
- package/linthost/rules_unicorn_consistent_existence_index_check.go +208 -29
- package/linthost/rules_unicorn_consistent_function_scoping.go +1041 -8
- package/linthost/rules_unicorn_consistent_template_literal_escape.go +178 -7
- package/linthost/rules_unicorn_escape_case.go +30 -19
- package/linthost/rules_unicorn_filename_case.go +763 -16
- package/linthost/rules_unicorn_import_style.go +783 -17
- package/linthost/rules_unicorn_isolated_functions.go +934 -14
- package/linthost/rules_unicorn_no_hex_escape.go +26 -11
- package/linthost/rules_unicorn_no_typeof_undefined.go +234 -25
- package/linthost/rules_unicorn_no_unnecessary_polyfills.go +376 -21
- package/linthost/rules_unicorn_no_unused_properties.go +756 -19
- package/linthost/rules_unicorn_number_literal_case.go +58 -39
- package/linthost/rules_unicorn_prefer_at.go +7 -3
- package/linthost/rules_unicorn_prefer_number_properties.go +381 -48
- package/linthost/rules_unicorn_prefer_optional_catch_binding.go +24 -24
- package/linthost/rules_unicorn_prefer_simple_condition_first.go +597 -19
- package/linthost/rules_unicorn_prefer_string_raw.go +147 -16
- package/linthost/rules_unicorn_prevent_abbreviations.go +1963 -95
- package/linthost/rules_unicorn_string_content.go +611 -15
- package/linthost/rules_unicorn_switch_case_break_position.go +172 -15
- package/linthost/rules_unicorn_template_indent.go +923 -18
- package/linthost/rules_unicorn_text_encoding_identifier_case.go +202 -42
- package/linthost/rules_unicorn_throw_new_error.go +218 -26
- package/linthost/rules_var.go +829 -142
- package/linthost/string_length.go +222 -0
- package/package.json +4 -3
- package/rule/project.go +145 -0
- package/rule/rule.go +58 -1
- package/src/index.ts +58 -17
- package/src/structures/ITtscLintConfig.ts +3 -0
- package/src/structures/TtscLintRuleSetting.ts +3 -2
- package/src/structures/format/ITtscLintFormat.ts +3 -2
- package/src/structures/format/ITtscLintFormatSortImports.ts +21 -4
- package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +122 -8
- package/src/structures/rules/ITtscLintBoundariesRules.ts +6 -8
- package/src/structures/rules/ITtscLintContributorRules.ts +18 -2
- package/src/structures/rules/ITtscLintCoreRuleOptions.ts +465 -0
- package/src/structures/rules/ITtscLintCoreRules.ts +122 -67
- package/src/structures/rules/ITtscLintRegexpRules.ts +7 -2
- package/src/structures/rules/ITtscLintRuleOptionsMap.ts +88 -7
- package/src/structures/rules/ITtscLintRules.ts +7 -2
- package/src/structures/rules/ITtscLintSolidRules.ts +0 -9
- package/src/structures/rules/ITtscLintTypeScriptRuleOptions.ts +248 -0
- package/src/structures/rules/ITtscLintTypeScriptRules.ts +64 -42
- package/src/structures/rules/ITtscLintUnicornRuleOptions.ts +350 -0
- package/src/structures/rules/ITtscLintUnicornRules.ts +60 -36
- package/src/structures/rules/index.ts +3 -0
|
@@ -1,13 +1,31 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// unicorn/consistent-template-literal-escape: a literal `${` inside a
|
|
2
|
+
// template can be spelled `\${` (escaped dollar), `$\{` (escaped brace),
|
|
3
|
+
// or `\$\{` (both). Every spelling cooks to the same text, so the parser
|
|
4
|
+
// erases the difference and a codebase silently mixes them. The upstream
|
|
5
|
+
// rule canonicalizes on `\${` and autofixes the other two spellings.
|
|
6
|
+
//
|
|
7
|
+
// Raw-source port: the two spellings are indistinguishable in the decoded
|
|
8
|
+
// `.Text` value, so the rule slices each template element's raw payload
|
|
9
|
+
// out of the file text between its delimiters (backtick or `}` on the
|
|
10
|
+
// left, `${` or backtick on the right) and rewrites every `$\{` whose
|
|
11
|
+
// optional leading backslash is preceded by an even-length backslash run,
|
|
12
|
+
// the exact lookbehind of the upstream regex. Tagged templates are
|
|
13
|
+
// skipped because the tag function observes `strings.raw`, where the
|
|
14
|
+
// spellings differ; template literal types are checked, matching what the
|
|
15
|
+
// upstream rule reports for `TemplateElement` nodes under the
|
|
16
|
+
// typescript-eslint parser.
|
|
5
17
|
//
|
|
6
|
-
// unicorn/consistent-template-literal-escape:
|
|
7
18
|
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/consistent-template-literal-escape.md
|
|
8
19
|
package linthost
|
|
9
20
|
|
|
10
|
-
import
|
|
21
|
+
import (
|
|
22
|
+
"strings"
|
|
23
|
+
|
|
24
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
25
|
+
shimscanner "github.com/microsoft/typescript-go/shim/scanner"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
const unicornConsistentTemplateLiteralEscapeMessage = "Use `\\${` instead of `$\\{` to escape in template literals."
|
|
11
29
|
|
|
12
30
|
type unicornConsistentTemplateLiteralEscape struct{}
|
|
13
31
|
|
|
@@ -15,9 +33,162 @@ func (unicornConsistentTemplateLiteralEscape) Name() string {
|
|
|
15
33
|
return "unicorn/consistent-template-literal-escape"
|
|
16
34
|
}
|
|
17
35
|
func (unicornConsistentTemplateLiteralEscape) Visits() []shimast.Kind {
|
|
18
|
-
return []shimast.Kind{
|
|
36
|
+
return []shimast.Kind{
|
|
37
|
+
shimast.KindNoSubstitutionTemplateLiteral,
|
|
38
|
+
shimast.KindTemplateExpression,
|
|
39
|
+
shimast.KindTemplateLiteralType,
|
|
40
|
+
}
|
|
19
41
|
}
|
|
20
42
|
func (unicornConsistentTemplateLiteralEscape) Check(ctx *Context, node *shimast.Node) {
|
|
43
|
+
if ctx == nil || ctx.File == nil || node == nil {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
source := ctx.File.Text()
|
|
47
|
+
switch node.Kind {
|
|
48
|
+
case shimast.KindNoSubstitutionTemplateLiteral:
|
|
49
|
+
if isTaggedTemplateQuasi(node) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
unicornConsistentTemplateLiteralEscapeCheckElement(ctx, source, node)
|
|
53
|
+
case shimast.KindTemplateExpression:
|
|
54
|
+
if isTaggedTemplateQuasi(node) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
expression := node.AsTemplateExpression()
|
|
58
|
+
if expression == nil || expression.Head == nil || expression.TemplateSpans == nil {
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
unicornConsistentTemplateLiteralEscapeCheckElement(ctx, source, expression.Head)
|
|
62
|
+
for _, spanNode := range expression.TemplateSpans.Nodes {
|
|
63
|
+
span := spanNode.AsTemplateSpan()
|
|
64
|
+
if span == nil || span.Literal == nil {
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
unicornConsistentTemplateLiteralEscapeCheckElement(ctx, source, span.Literal)
|
|
68
|
+
}
|
|
69
|
+
case shimast.KindTemplateLiteralType:
|
|
70
|
+
literalType := node.AsTemplateLiteralTypeNode()
|
|
71
|
+
if literalType == nil || literalType.Head == nil || literalType.TemplateSpans == nil {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
unicornConsistentTemplateLiteralEscapeCheckElement(ctx, source, literalType.Head)
|
|
75
|
+
for _, spanNode := range literalType.TemplateSpans.Nodes {
|
|
76
|
+
span := spanNode.AsTemplateLiteralTypeSpan()
|
|
77
|
+
if span == nil || span.Literal == nil {
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
unicornConsistentTemplateLiteralEscapeCheckElement(ctx, source, span.Literal)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// unicornConsistentTemplateLiteralEscapeCheckElement slices one template
|
|
86
|
+
// element's raw payload from the file text and reports the element with a
|
|
87
|
+
// payload-only autofix when canonicalization changes it. The reported
|
|
88
|
+
// range is the element token itself (opening delimiter through closing
|
|
89
|
+
// delimiter), which is exactly the ESTree TemplateElement range upstream
|
|
90
|
+
// highlights.
|
|
91
|
+
func unicornConsistentTemplateLiteralEscapeCheckElement(ctx *Context, source string, element *shimast.Node) {
|
|
92
|
+
payloadStart, payloadEnd, ok := unicornConsistentTemplateLiteralEscapePayload(source, element)
|
|
93
|
+
if !ok {
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
fixed, changed := unicornConsistentTemplateLiteralEscapeRewrite(source[payloadStart:payloadEnd])
|
|
97
|
+
if !changed {
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
ctx.ReportFix(element, unicornConsistentTemplateLiteralEscapeMessage, TextEdit{
|
|
101
|
+
Pos: payloadStart,
|
|
102
|
+
End: payloadEnd,
|
|
103
|
+
Text: fixed,
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// unicornConsistentTemplateLiteralEscapePayload returns the raw-text range
|
|
108
|
+
// strictly between an element token's delimiters. Head and
|
|
109
|
+
// no-substitution tokens open with a backtick, middle and tail tokens
|
|
110
|
+
// open with `}`; head and middle tokens close with `${`, tail and
|
|
111
|
+
// no-substitution tokens close with a backtick. Malformed shapes (e.g. an
|
|
112
|
+
// unterminated template at end of file) fail the delimiter guards and are
|
|
113
|
+
// skipped.
|
|
114
|
+
func unicornConsistentTemplateLiteralEscapePayload(source string, element *shimast.Node) (int, int, bool) {
|
|
115
|
+
var opener byte
|
|
116
|
+
closerWidth := 0
|
|
117
|
+
switch element.Kind {
|
|
118
|
+
case shimast.KindNoSubstitutionTemplateLiteral:
|
|
119
|
+
opener, closerWidth = '`', 1
|
|
120
|
+
case shimast.KindTemplateHead:
|
|
121
|
+
opener, closerWidth = '`', 2
|
|
122
|
+
case shimast.KindTemplateMiddle:
|
|
123
|
+
opener, closerWidth = '}', 2
|
|
124
|
+
case shimast.KindTemplateTail:
|
|
125
|
+
opener, closerWidth = '}', 1
|
|
126
|
+
default:
|
|
127
|
+
return 0, 0, false
|
|
128
|
+
}
|
|
129
|
+
start := shimscanner.SkipTrivia(source, element.Pos())
|
|
130
|
+
end := element.End()
|
|
131
|
+
if start < 0 || end > len(source) || start+1+closerWidth > end || source[start] != opener {
|
|
132
|
+
return 0, 0, false
|
|
133
|
+
}
|
|
134
|
+
if closerWidth == 2 && source[end-2:end] != "${" {
|
|
135
|
+
return 0, 0, false
|
|
136
|
+
}
|
|
137
|
+
if closerWidth == 1 && source[end-1] != '`' {
|
|
138
|
+
return 0, 0, false
|
|
139
|
+
}
|
|
140
|
+
return start + 1, end - closerWidth, true
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// unicornConsistentTemplateLiteralEscapeRewrite canonicalizes every
|
|
144
|
+
// escaped-brace `${` spelling inside one raw template payload. It is the
|
|
145
|
+
// byte-scan equivalent of the upstream replacement
|
|
146
|
+
//
|
|
147
|
+
// raw.replaceAll(/(?<=(?:^|[^\\])(?:\\\\)*)\\?\$\\{/g, "\\${")
|
|
148
|
+
//
|
|
149
|
+
// : a match consumes `$\{` or `\$\{` whose leading position follows an
|
|
150
|
+
// even-length backslash run, so escaped backslashes (`\\`) never donate
|
|
151
|
+
// their second byte to a match. Byte scanning is UTF-8 safe because every
|
|
152
|
+
// matched byte is ASCII and multibyte sequences contain no ASCII bytes.
|
|
153
|
+
func unicornConsistentTemplateLiteralEscapeRewrite(raw string) (string, bool) {
|
|
154
|
+
var out strings.Builder
|
|
155
|
+
changed := false
|
|
156
|
+
backslashRun := 0
|
|
157
|
+
index := 0
|
|
158
|
+
for index < len(raw) {
|
|
159
|
+
switch character := raw[index]; character {
|
|
160
|
+
case '\\':
|
|
161
|
+
if backslashRun%2 == 0 && strings.HasPrefix(raw[index:], `\$\{`) {
|
|
162
|
+
out.WriteString(`\${`)
|
|
163
|
+
changed = true
|
|
164
|
+
index += len(`\$\{`)
|
|
165
|
+
backslashRun = 0
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
168
|
+
backslashRun++
|
|
169
|
+
out.WriteByte(character)
|
|
170
|
+
index++
|
|
171
|
+
case '$':
|
|
172
|
+
if backslashRun%2 == 0 && strings.HasPrefix(raw[index:], `$\{`) {
|
|
173
|
+
out.WriteString(`\${`)
|
|
174
|
+
changed = true
|
|
175
|
+
index += len(`$\{`)
|
|
176
|
+
backslashRun = 0
|
|
177
|
+
continue
|
|
178
|
+
}
|
|
179
|
+
backslashRun = 0
|
|
180
|
+
out.WriteByte(character)
|
|
181
|
+
index++
|
|
182
|
+
default:
|
|
183
|
+
backslashRun = 0
|
|
184
|
+
out.WriteByte(character)
|
|
185
|
+
index++
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if !changed {
|
|
189
|
+
return raw, false
|
|
190
|
+
}
|
|
191
|
+
return out.String(), true
|
|
21
192
|
}
|
|
22
193
|
|
|
23
194
|
func init() {
|
|
@@ -4,48 +4,59 @@
|
|
|
4
4
|
// mix `\xa9` and `\xA9`. Canonical form uses uppercase hex digits; the
|
|
5
5
|
// rule fires when any hex escape's A-F digits are lowercase.
|
|
6
6
|
//
|
|
7
|
-
// AST-only: visit `KindStringLiteral
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
7
|
+
// AST-only: visit `KindStringLiteral`, `KindNoSubstitutionTemplateLiteral`,
|
|
8
|
+
// and the `KindTemplateHead`/`Middle`/`Tail` elements an interpolated
|
|
9
|
+
// template (or a template literal type) is built from, read each token's raw
|
|
10
|
+
// source text via `nodeText` (the parser already decodes escapes into the
|
|
11
|
+
// `.Text` value), and fire when an active escape's hex digits contain an a-f
|
|
12
|
+
// letter. The scan is hex-only — `\n` and other identifier escapes are
|
|
13
|
+
// untouched — and tagged templates are skipped because their tag observes
|
|
14
|
+
// the raw text, where the two spellings differ. See
|
|
15
|
+
// `literal_escape_scan.go` for the parity and digit-width rules the scan
|
|
16
|
+
// enforces.
|
|
13
17
|
//
|
|
14
18
|
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/escape-case.md
|
|
15
19
|
package linthost
|
|
16
20
|
|
|
17
21
|
import (
|
|
18
|
-
"
|
|
22
|
+
"strings"
|
|
19
23
|
|
|
20
24
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
21
25
|
)
|
|
22
26
|
|
|
23
|
-
// unicornEscapeCasePattern matches a hex escape whose hex digits contain
|
|
24
|
-
// at least one lowercase a-f letter. The regex anchors on `\x`, `\u`, or
|
|
25
|
-
// `\u{...}` and rejects matches where every hex digit is already
|
|
26
|
-
// uppercase / 0-9.
|
|
27
|
-
var unicornEscapeCasePattern = regexp.MustCompile(
|
|
28
|
-
`\\x[0-9A-Fa-f]*[a-f][0-9A-Fa-f]*|` +
|
|
29
|
-
`\\u\{[0-9A-Fa-f]*[a-f][0-9A-Fa-f]*\}|` +
|
|
30
|
-
`\\u[0-9A-Fa-f]*[a-f][0-9A-Fa-f]*`,
|
|
31
|
-
)
|
|
32
|
-
|
|
33
27
|
type unicornEscapeCase struct{}
|
|
34
28
|
|
|
35
29
|
func (unicornEscapeCase) Name() string { return "unicorn/escape-case" }
|
|
36
30
|
func (unicornEscapeCase) Visits() []shimast.Kind {
|
|
37
|
-
return []shimast.Kind{
|
|
31
|
+
return []shimast.Kind{
|
|
32
|
+
shimast.KindStringLiteral,
|
|
33
|
+
shimast.KindNoSubstitutionTemplateLiteral,
|
|
34
|
+
shimast.KindTemplateHead,
|
|
35
|
+
shimast.KindTemplateMiddle,
|
|
36
|
+
shimast.KindTemplateTail,
|
|
37
|
+
}
|
|
38
38
|
}
|
|
39
39
|
func (unicornEscapeCase) Check(ctx *Context, node *shimast.Node) {
|
|
40
|
+
if isTaggedTemplateElement(node) {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
40
43
|
source := nodeText(ctx.File, node)
|
|
41
44
|
if source == "" {
|
|
42
45
|
return
|
|
43
46
|
}
|
|
44
|
-
if
|
|
47
|
+
if hasActiveLiteralEscape(source, unicornEscapeCaseIsLowercase) {
|
|
45
48
|
ctx.Report(node, "Use uppercase letters for escape sequence hex digits (`\\xA9` over `\\xa9`).")
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
51
|
|
|
52
|
+
// unicornEscapeCaseIsLowercase selects an escape whose hex digits carry at
|
|
53
|
+
// least one lowercase a-f letter — exactly the escapes upstream's uppercasing
|
|
54
|
+
// replacement would rewrite. Digits `0`-`9` and `A`-`F` are already canonical,
|
|
55
|
+
// and the `\u{...}` brackets are not part of `Digits`.
|
|
56
|
+
func unicornEscapeCaseIsLowercase(escape literalEscape) bool {
|
|
57
|
+
return strings.ContainsAny(escape.Digits, "abcdef")
|
|
58
|
+
}
|
|
59
|
+
|
|
49
60
|
func init() {
|
|
50
61
|
Register(unicornEscapeCase{})
|
|
51
62
|
}
|