@ttsc/lint 0.10.2 → 0.11.0-dev.20260517
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 +172 -33
- package/lib/defineConfig.d.ts +55 -0
- package/lib/defineConfig.js +39 -0
- package/lib/defineConfig.js.map +1 -0
- package/lib/index.d.ts +25 -1
- package/lib/index.js +622 -2
- package/lib/index.js.map +1 -1
- package/lib/structures/ITtscLintPlugin.d.ts +53 -0
- package/lib/structures/ITtscLintPlugin.js +3 -0
- package/lib/structures/ITtscLintPlugin.js.map +1 -0
- package/lib/structures/ITtscLintPluginConfig.d.ts +82 -5
- package/lib/structures/ITtscLintPluginMeta.d.ts +12 -0
- package/lib/structures/ITtscLintPluginMeta.js +3 -0
- package/lib/structures/ITtscLintPluginMeta.js.map +1 -0
- package/lib/structures/PluginRuleNames.d.ts +14 -0
- package/lib/structures/PluginRuleNames.js +3 -0
- package/lib/structures/PluginRuleNames.js.map +1 -0
- package/lib/structures/TtscLintConfig.d.ts +24 -8
- package/lib/structures/TtscLintConfigEntry.d.ts +39 -0
- package/lib/structures/TtscLintConfigEntry.js +3 -0
- package/lib/structures/TtscLintConfigEntry.js.map +1 -0
- package/lib/structures/TtscLintFormatConfig.d.ts +154 -0
- package/lib/structures/TtscLintFormatConfig.js +3 -0
- package/lib/structures/TtscLintFormatConfig.js.map +1 -0
- package/lib/structures/TtscLintPlugins.d.ts +9 -0
- package/lib/structures/TtscLintPlugins.js +3 -0
- package/lib/structures/TtscLintPlugins.js.map +1 -0
- package/lib/structures/TtscLintRule.d.ts +1 -1
- package/lib/structures/TtscLintRuleEntry.d.ts +22 -0
- package/lib/structures/TtscLintRuleEntry.js +3 -0
- package/lib/structures/TtscLintRuleEntry.js.map +1 -0
- package/lib/structures/TtscLintRuleMap.d.ts +44 -0
- package/lib/structures/TtscLintRuleMap.js +3 -0
- package/lib/structures/TtscLintRuleMap.js.map +1 -0
- package/lib/structures/TtscLintRuleOptions.d.ts +149 -0
- package/lib/structures/TtscLintRuleOptions.js +3 -0
- package/lib/structures/TtscLintRuleOptions.js.map +1 -0
- package/lib/structures/defaultFormat.d.ts +31 -0
- package/lib/structures/defaultFormat.js +35 -0
- package/lib/structures/defaultFormat.js.map +1 -0
- package/lib/structures/index.d.ts +10 -0
- package/lib/structures/index.js +10 -0
- package/lib/structures/index.js.map +1 -1
- package/{plugin → linthost}/ast_helpers.go +92 -1
- package/{plugin → linthost}/compile.go +1 -1
- package/{plugin → linthost}/config.go +583 -66
- package/linthost/config_format.go +360 -0
- package/linthost/contrib_adapter.go +134 -0
- package/{plugin → linthost}/directives.go +1 -1
- package/linthost/dispatch.go +62 -0
- package/{plugin → linthost}/engine.go +156 -11
- package/{plugin → linthost}/eslint_runtime.go +76 -2
- package/linthost/fix.go +249 -0
- package/linthost/format.go +107 -0
- package/{plugin → linthost}/host.go +1 -1
- package/linthost/print_dispatch.go +118 -0
- package/linthost/print_doc.go +168 -0
- package/linthost/print_engine.go +343 -0
- package/linthost/print_nodes_array.go +46 -0
- package/linthost/print_nodes_call.go +180 -0
- package/linthost/print_nodes_imports.go +171 -0
- package/linthost/print_nodes_list.go +64 -0
- package/linthost/print_nodes_object.go +51 -0
- package/{plugin → linthost}/rules_arrays.go +1 -1
- package/{plugin → linthost}/rules_console.go +1 -1
- package/{plugin → linthost}/rules_debugger.go +1 -1
- package/{plugin → linthost}/rules_dupes.go +1 -1
- package/{plugin → linthost}/rules_empty.go +1 -1
- package/linthost/rules_escape.go +238 -0
- package/{plugin → linthost}/rules_eval.go +1 -1
- package/{plugin → linthost}/rules_finally.go +1 -1
- package/linthost/rules_format_jsdoc.go +230 -0
- package/linthost/rules_format_print_width.go +332 -0
- package/linthost/rules_format_quotes.go +219 -0
- package/linthost/rules_format_semi.go +190 -0
- package/linthost/rules_format_sort_imports.go +462 -0
- package/linthost/rules_format_trailing_comma.go +460 -0
- package/{plugin → linthost}/rules_gap.go +101 -6
- package/linthost/rules_imports.go +101 -0
- package/{plugin → linthost}/rules_logic.go +55 -3
- package/{plugin → linthost}/rules_loops.go +1 -1
- package/{plugin → linthost}/rules_misc.go +1 -1
- package/linthost/rules_params.go +64 -0
- package/{plugin → linthost}/rules_problems.go +10 -2
- package/linthost/rules_promise.go +111 -0
- package/{plugin → linthost}/rules_protos.go +1 -1
- package/{plugin → linthost}/rules_self.go +1 -1
- package/{plugin → linthost}/rules_strings.go +1 -1
- package/{plugin → linthost}/rules_suggestions.go +51 -9
- package/{plugin → linthost}/rules_throw.go +1 -1
- package/{plugin → linthost}/rules_ts.go +12 -2
- package/{plugin → linthost}/rules_ts_extra.go +90 -4
- package/{plugin → linthost}/rules_var.go +36 -5
- package/package.json +8 -6
- package/plugin/main.go +14 -27
- package/rule/astutil/astutil.go +148 -0
- package/rule/rule.go +291 -0
- package/src/defineConfig.ts +69 -0
- package/src/index.ts +807 -3
- package/src/structures/ITtscLintPlugin.ts +56 -0
- package/src/structures/ITtscLintPluginConfig.ts +86 -5
- package/src/structures/ITtscLintPluginMeta.ts +14 -0
- package/src/structures/PluginRuleNames.ts +19 -0
- package/src/structures/TtscLintConfig.ts +27 -8
- package/src/structures/TtscLintConfigEntry.ts +50 -0
- package/src/structures/TtscLintFormatConfig.ts +169 -0
- package/src/structures/TtscLintPlugins.ts +10 -0
- package/src/structures/TtscLintRule.ts +14 -1
- package/src/structures/TtscLintRuleEntry.ts +28 -0
- package/src/structures/TtscLintRuleMap.ts +52 -0
- package/src/structures/TtscLintRuleOptions.ts +162 -0
- package/src/structures/defaultFormat.ts +33 -0
- package/src/structures/index.ts +10 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
package linthost
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
|
|
6
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// format/quotes normalizes string-literal quote style. Mirrors
|
|
10
|
+
// prettier's `singleQuote` option:
|
|
11
|
+
//
|
|
12
|
+
// - `prefer: "double"` (default) converts single-quoted literals to
|
|
13
|
+
// double-quoted when the escape cost is equal or better.
|
|
14
|
+
// - `prefer: "single"` does the reverse.
|
|
15
|
+
//
|
|
16
|
+
// In both directions the escape-cost tie-breaker holds: if conversion
|
|
17
|
+
// would strictly increase the escape count, the literal is left alone.
|
|
18
|
+
//
|
|
19
|
+
// JSX attribute initializers (`<div className="foo" />`) are skipped on
|
|
20
|
+
// purpose. Prettier exposes a separate `jsxSingleQuote` option for that
|
|
21
|
+
// surface and never rewrites JSX attributes via `singleQuote`; the
|
|
22
|
+
// JSX-grammar-canonical form is double quotes and rewriting to single
|
|
23
|
+
// quotes corrupts working code. Template literals, no-substitution
|
|
24
|
+
// template literals, and JSX text nodes use distinct AST kinds and are
|
|
25
|
+
// also intentionally out of scope.
|
|
26
|
+
type formatQuotes struct{}
|
|
27
|
+
|
|
28
|
+
type formatQuotesOptions struct {
|
|
29
|
+
Prefer string `json:"prefer"`
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func (formatQuotes) Name() string { return "format/quotes" }
|
|
33
|
+
func (formatQuotes) IsFormat() bool { return true }
|
|
34
|
+
|
|
35
|
+
func (formatQuotes) Visits() []shimast.Kind {
|
|
36
|
+
return []shimast.Kind{shimast.KindStringLiteral}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
func (formatQuotes) Check(ctx *Context, node *shimast.Node) {
|
|
40
|
+
if ctx == nil || ctx.File == nil || node == nil {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
// JSX attribute initializers parse as plain StringLiteral but are
|
|
44
|
+
// grammatically required to use double quotes in the standard JSX
|
|
45
|
+
// form. Prettier mirrors that and never touches them via singleQuote.
|
|
46
|
+
if parent := node.Parent; parent != nil && parent.Kind == shimast.KindJsxAttribute {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
var opts formatQuotesOptions
|
|
50
|
+
_ = ctx.DecodeOptions(&opts)
|
|
51
|
+
preferSingle := opts.Prefer == "single"
|
|
52
|
+
|
|
53
|
+
pos, end := tokenRange(ctx.File, node)
|
|
54
|
+
if pos < 0 || end-pos < 2 {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
src := ctx.File.Text()
|
|
58
|
+
raw := src[pos:end]
|
|
59
|
+
if preferSingle {
|
|
60
|
+
if raw[0] != '"' || raw[len(raw)-1] != '"' {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
converted, ok := convertDoubleQuotedToSingle(raw[1 : len(raw)-1])
|
|
64
|
+
if !ok || converted == raw {
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
ctx.ReportRangeFix(
|
|
68
|
+
pos,
|
|
69
|
+
end,
|
|
70
|
+
"Strings must use single quotes.",
|
|
71
|
+
TextEdit{Pos: pos, End: end, Text: converted},
|
|
72
|
+
)
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
if raw[0] != '\'' || raw[len(raw)-1] != '\'' {
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
converted, ok := convertSingleQuotedToDouble(raw[1 : len(raw)-1])
|
|
79
|
+
if !ok || converted == raw {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
ctx.ReportRangeFix(
|
|
83
|
+
pos,
|
|
84
|
+
end,
|
|
85
|
+
"Strings must use double quotes.",
|
|
86
|
+
TextEdit{Pos: pos, End: end, Text: converted},
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// convertDoubleQuotedToSingle walks the inner text of a double-quoted
|
|
91
|
+
// literal and returns the single-quoted equivalent, plus an `ok`
|
|
92
|
+
// boolean. `ok=false` means converting would require strictly more
|
|
93
|
+
// escapes than the source (prettier's tie-breaker rule), so the
|
|
94
|
+
// caller should leave the literal alone.
|
|
95
|
+
//
|
|
96
|
+
// Conversion is escape-aware:
|
|
97
|
+
// - `\"` becomes a bare `"` (no longer needs escaping).
|
|
98
|
+
// - Bare `'` becomes `\'` (now must be escaped).
|
|
99
|
+
// - Every other escape sequence (`\n`, `\\`, `\u{…}`) survives intact.
|
|
100
|
+
func convertDoubleQuotedToSingle(inner string) (string, bool) {
|
|
101
|
+
escapedDouble, unescapedSingle := countDoubleEscapes(inner)
|
|
102
|
+
if unescapedSingle > escapedDouble {
|
|
103
|
+
return "", false
|
|
104
|
+
}
|
|
105
|
+
var b strings.Builder
|
|
106
|
+
b.Grow(len(inner) + 2)
|
|
107
|
+
b.WriteByte('\'')
|
|
108
|
+
for i := 0; i < len(inner); {
|
|
109
|
+
if inner[i] == '\\' && i+1 < len(inner) {
|
|
110
|
+
if inner[i+1] == '"' {
|
|
111
|
+
b.WriteByte('"')
|
|
112
|
+
i += 2
|
|
113
|
+
continue
|
|
114
|
+
}
|
|
115
|
+
b.WriteByte(inner[i])
|
|
116
|
+
b.WriteByte(inner[i+1])
|
|
117
|
+
i += 2
|
|
118
|
+
continue
|
|
119
|
+
}
|
|
120
|
+
if inner[i] == '\'' {
|
|
121
|
+
b.WriteByte('\\')
|
|
122
|
+
b.WriteByte('\'')
|
|
123
|
+
i++
|
|
124
|
+
continue
|
|
125
|
+
}
|
|
126
|
+
b.WriteByte(inner[i])
|
|
127
|
+
i++
|
|
128
|
+
}
|
|
129
|
+
b.WriteByte('\'')
|
|
130
|
+
return b.String(), true
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// countDoubleEscapes returns the number of `\"` sequences and bare `'`
|
|
134
|
+
// bytes inside a double-quoted literal's text. Pairs with
|
|
135
|
+
// countSingleEscapes.
|
|
136
|
+
func countDoubleEscapes(inner string) (escapedDouble, unescapedSingle int) {
|
|
137
|
+
for i := 0; i < len(inner); {
|
|
138
|
+
if inner[i] == '\\' && i+1 < len(inner) {
|
|
139
|
+
if inner[i+1] == '"' {
|
|
140
|
+
escapedDouble++
|
|
141
|
+
}
|
|
142
|
+
i += 2
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
if inner[i] == '\'' {
|
|
146
|
+
unescapedSingle++
|
|
147
|
+
}
|
|
148
|
+
i++
|
|
149
|
+
}
|
|
150
|
+
return escapedDouble, unescapedSingle
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// convertSingleQuotedToDouble walks the inner text of a single-quoted
|
|
154
|
+
// literal and returns the double-quoted equivalent, plus an `ok`
|
|
155
|
+
// boolean. `ok=false` means converting would require strictly more
|
|
156
|
+
// escapes than the source (prettier's tie-breaker rule), so the
|
|
157
|
+
// caller should leave the literal alone.
|
|
158
|
+
//
|
|
159
|
+
// Conversion is escape-aware:
|
|
160
|
+
// - `\'` becomes a bare `'` (no longer needs escaping).
|
|
161
|
+
// - Bare `"` becomes `\"` (now must be escaped).
|
|
162
|
+
// - Every other escape sequence (`\n`, `\\`, `\u{…}`) survives intact.
|
|
163
|
+
func convertSingleQuotedToDouble(inner string) (string, bool) {
|
|
164
|
+
escapedSingle, unescapedDouble := countSingleEscapes(inner)
|
|
165
|
+
if unescapedDouble > escapedSingle {
|
|
166
|
+
return "", false
|
|
167
|
+
}
|
|
168
|
+
var b strings.Builder
|
|
169
|
+
b.Grow(len(inner) + 2)
|
|
170
|
+
b.WriteByte('"')
|
|
171
|
+
for i := 0; i < len(inner); {
|
|
172
|
+
if inner[i] == '\\' && i+1 < len(inner) {
|
|
173
|
+
if inner[i+1] == '\'' {
|
|
174
|
+
b.WriteByte('\'')
|
|
175
|
+
i += 2
|
|
176
|
+
continue
|
|
177
|
+
}
|
|
178
|
+
b.WriteByte(inner[i])
|
|
179
|
+
b.WriteByte(inner[i+1])
|
|
180
|
+
i += 2
|
|
181
|
+
continue
|
|
182
|
+
}
|
|
183
|
+
if inner[i] == '"' {
|
|
184
|
+
b.WriteByte('\\')
|
|
185
|
+
b.WriteByte('"')
|
|
186
|
+
i++
|
|
187
|
+
continue
|
|
188
|
+
}
|
|
189
|
+
b.WriteByte(inner[i])
|
|
190
|
+
i++
|
|
191
|
+
}
|
|
192
|
+
b.WriteByte('"')
|
|
193
|
+
return b.String(), true
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// countSingleEscapes returns the number of `\'` sequences and bare `"`
|
|
197
|
+
// bytes inside a single-quoted literal's text. Pairs with
|
|
198
|
+
// countDoubleEscapes; the names describe what they count (the quote kind
|
|
199
|
+
// that's been escape-prefixed).
|
|
200
|
+
func countSingleEscapes(inner string) (escapedSingle, unescapedDouble int) {
|
|
201
|
+
for i := 0; i < len(inner); {
|
|
202
|
+
if inner[i] == '\\' && i+1 < len(inner) {
|
|
203
|
+
if inner[i+1] == '\'' {
|
|
204
|
+
escapedSingle++
|
|
205
|
+
}
|
|
206
|
+
i += 2
|
|
207
|
+
continue
|
|
208
|
+
}
|
|
209
|
+
if inner[i] == '"' {
|
|
210
|
+
unescapedDouble++
|
|
211
|
+
}
|
|
212
|
+
i++
|
|
213
|
+
}
|
|
214
|
+
return escapedSingle, unescapedDouble
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
func init() {
|
|
218
|
+
Register(formatQuotes{})
|
|
219
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
package linthost
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
// format/semi controls trailing-semicolon style on ASI statements.
|
|
8
|
+
// Mirrors prettier's `semi` option:
|
|
9
|
+
//
|
|
10
|
+
// - `prefer: "always"` (default) inserts a missing terminator.
|
|
11
|
+
// - `prefer: "never"` strips a trailing terminator from the same
|
|
12
|
+
// statement kinds.
|
|
13
|
+
//
|
|
14
|
+
// The rule scans only statement kinds where TypeScript inserts an
|
|
15
|
+
// optional semicolon. Body-shaped declarations (functions, classes,
|
|
16
|
+
// namespaces, enums) and control-flow statements (if/for/while/try)
|
|
17
|
+
// are out of scope because they parse correctly without a terminator.
|
|
18
|
+
type formatSemi struct{}
|
|
19
|
+
|
|
20
|
+
// formatSemiOptions is the Go mirror of `TtscLintRuleOptions.Semi`. The
|
|
21
|
+
// JSON tag matches the TypeScript field name so users get the same key
|
|
22
|
+
// in both layers.
|
|
23
|
+
type formatSemiOptions struct {
|
|
24
|
+
Prefer string `json:"prefer"`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
func (formatSemi) Name() string { return "format/semi" }
|
|
28
|
+
func (formatSemi) IsFormat() bool { return true }
|
|
29
|
+
|
|
30
|
+
func (formatSemi) Visits() []shimast.Kind {
|
|
31
|
+
return []shimast.Kind{
|
|
32
|
+
shimast.KindVariableStatement,
|
|
33
|
+
shimast.KindExpressionStatement,
|
|
34
|
+
shimast.KindReturnStatement,
|
|
35
|
+
shimast.KindThrowStatement,
|
|
36
|
+
shimast.KindBreakStatement,
|
|
37
|
+
shimast.KindContinueStatement,
|
|
38
|
+
shimast.KindDoStatement,
|
|
39
|
+
shimast.KindDebuggerStatement,
|
|
40
|
+
shimast.KindImportDeclaration,
|
|
41
|
+
shimast.KindImportEqualsDeclaration,
|
|
42
|
+
shimast.KindExportDeclaration,
|
|
43
|
+
shimast.KindExportAssignment,
|
|
44
|
+
shimast.KindPropertyDeclaration,
|
|
45
|
+
shimast.KindTypeAliasDeclaration,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
func (formatSemi) Check(ctx *Context, node *shimast.Node) {
|
|
50
|
+
if ctx == nil || ctx.File == nil || node == nil {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
var opts formatSemiOptions
|
|
54
|
+
_ = ctx.DecodeOptions(&opts)
|
|
55
|
+
preferNever := opts.Prefer == "never"
|
|
56
|
+
|
|
57
|
+
src := ctx.File.Text()
|
|
58
|
+
end := node.End()
|
|
59
|
+
if end <= 0 || end > len(src) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
hasSemi := src[end-1] == ';'
|
|
63
|
+
if preferNever {
|
|
64
|
+
if !hasSemi {
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
if !preferNeverSafeKind(node.Kind) {
|
|
68
|
+
// Dropping the `;` after a class field or a type alias can
|
|
69
|
+
// change parse — e.g. `class A { x: number; [k](): void {} }`
|
|
70
|
+
// would reparse `[k]` as a computed index access on `number`.
|
|
71
|
+
// Keep the terminator on those kinds even in prefer:"never"
|
|
72
|
+
// mode.
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
if nextStatementHasASIHazard(src, end) {
|
|
76
|
+
// Stripping `;` here would let ASI fail. Prettier defends with a
|
|
77
|
+
// leading-`;` on the next statement; this rule conservatively
|
|
78
|
+
// refuses to strip rather than synthesizing an edit on a node
|
|
79
|
+
// it didn't visit.
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
pos := end - 1
|
|
83
|
+
if pos < 0 {
|
|
84
|
+
pos = 0
|
|
85
|
+
}
|
|
86
|
+
ctx.ReportRangeFix(
|
|
87
|
+
pos,
|
|
88
|
+
end,
|
|
89
|
+
"Unexpected trailing semicolon.",
|
|
90
|
+
TextEdit{Pos: end - 1, End: end, Text: ""},
|
|
91
|
+
)
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
if hasSemi {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
// Diagnostic anchors on the last character of the statement so the
|
|
98
|
+
// banner underlines "the place a semicolon should follow". The fix
|
|
99
|
+
// itself is a zero-width insertion at node.End() — keeping the edit
|
|
100
|
+
// disjoint from any other rule's edits on the same statement.
|
|
101
|
+
pos := end - 1
|
|
102
|
+
if pos < 0 {
|
|
103
|
+
pos = 0
|
|
104
|
+
}
|
|
105
|
+
ctx.ReportRangeFix(
|
|
106
|
+
pos,
|
|
107
|
+
end,
|
|
108
|
+
"Missing semicolon.",
|
|
109
|
+
TextEdit{Pos: end, End: end, Text: ";"},
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// nextStatementHasASIHazard reports whether the next non-trivia byte
|
|
114
|
+
// after `end` starts a token that would re-associate with the prior
|
|
115
|
+
// expression if the trailing `;` is removed. Prettier handles this by
|
|
116
|
+
// inserting a defensive leading `;` on the next line; this rule's
|
|
117
|
+
// fixer is single-node, so the safer move is to keep the explicit
|
|
118
|
+
// terminator.
|
|
119
|
+
//
|
|
120
|
+
// Hazard tokens per the ASI spec:
|
|
121
|
+
//
|
|
122
|
+
// - `[` — bracket access continues an expression
|
|
123
|
+
// - `(` — call expression continues
|
|
124
|
+
// - “ ` “ — tagged template literal continues
|
|
125
|
+
// - `+`, `-`, `*`, `/` — binary operator continues
|
|
126
|
+
// - `,` — comma operator continues
|
|
127
|
+
func nextStatementHasASIHazard(src string, end int) bool {
|
|
128
|
+
for i := end; i < len(src); i++ {
|
|
129
|
+
c := src[i]
|
|
130
|
+
if c == ' ' || c == '\t' || c == '\r' || c == '\n' {
|
|
131
|
+
continue
|
|
132
|
+
}
|
|
133
|
+
if c == '/' && i+1 < len(src) {
|
|
134
|
+
if src[i+1] == '/' {
|
|
135
|
+
for i < len(src) && src[i] != '\n' {
|
|
136
|
+
i++
|
|
137
|
+
}
|
|
138
|
+
continue
|
|
139
|
+
}
|
|
140
|
+
if src[i+1] == '*' {
|
|
141
|
+
i += 2
|
|
142
|
+
for i+1 < len(src) && !(src[i] == '*' && src[i+1] == '/') {
|
|
143
|
+
i++
|
|
144
|
+
}
|
|
145
|
+
if i+1 < len(src) {
|
|
146
|
+
i++ // step past '*/'
|
|
147
|
+
}
|
|
148
|
+
continue
|
|
149
|
+
}
|
|
150
|
+
// bare `/` starts a regex literal or division — hazard.
|
|
151
|
+
return true
|
|
152
|
+
}
|
|
153
|
+
switch c {
|
|
154
|
+
case '[', '(', '`', '+', '-', '*', ',':
|
|
155
|
+
return true
|
|
156
|
+
}
|
|
157
|
+
return false
|
|
158
|
+
}
|
|
159
|
+
return false
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// preferNeverSafeKind reports whether stripping the trailing semicolon
|
|
163
|
+
// is safe for `kind`. Statement kinds end at a line break or `}` in
|
|
164
|
+
// practice; declaration-style kinds (PropertyDeclaration,
|
|
165
|
+
// TypeAliasDeclaration) live next to other class/module members where
|
|
166
|
+
// the explicit terminator disambiguates the next token. The
|
|
167
|
+
// prefer:"never" branch refuses to touch those.
|
|
168
|
+
func preferNeverSafeKind(kind shimast.Kind) bool {
|
|
169
|
+
switch kind {
|
|
170
|
+
case
|
|
171
|
+
shimast.KindVariableStatement,
|
|
172
|
+
shimast.KindExpressionStatement,
|
|
173
|
+
shimast.KindReturnStatement,
|
|
174
|
+
shimast.KindThrowStatement,
|
|
175
|
+
shimast.KindBreakStatement,
|
|
176
|
+
shimast.KindContinueStatement,
|
|
177
|
+
shimast.KindDoStatement,
|
|
178
|
+
shimast.KindDebuggerStatement,
|
|
179
|
+
shimast.KindImportDeclaration,
|
|
180
|
+
shimast.KindImportEqualsDeclaration,
|
|
181
|
+
shimast.KindExportDeclaration,
|
|
182
|
+
shimast.KindExportAssignment:
|
|
183
|
+
return true
|
|
184
|
+
}
|
|
185
|
+
return false
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
func init() {
|
|
189
|
+
Register(formatSemi{})
|
|
190
|
+
}
|