@ttsc/lint 0.5.0-dev.20260429
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/LICENSE +21 -0
- package/README.md +204 -0
- package/go-plugin/go.mod +38 -0
- package/go-plugin/lint/ast_helpers.go +170 -0
- package/go-plugin/lint/compile.go +393 -0
- package/go-plugin/lint/config.go +134 -0
- package/go-plugin/lint/engine.go +259 -0
- package/go-plugin/lint/host.go +190 -0
- package/go-plugin/lint/rules_arrays.go +78 -0
- package/go-plugin/lint/rules_console.go +37 -0
- package/go-plugin/lint/rules_debugger.go +29 -0
- package/go-plugin/lint/rules_dupes.go +179 -0
- package/go-plugin/lint/rules_empty.go +110 -0
- package/go-plugin/lint/rules_eval.go +57 -0
- package/go-plugin/lint/rules_finally.go +123 -0
- package/go-plugin/lint/rules_logic.go +368 -0
- package/go-plugin/lint/rules_loops.go +107 -0
- package/go-plugin/lint/rules_misc.go +69 -0
- package/go-plugin/lint/rules_problems.go +571 -0
- package/go-plugin/lint/rules_protos.go +42 -0
- package/go-plugin/lint/rules_self.go +89 -0
- package/go-plugin/lint/rules_strings.go +123 -0
- package/go-plugin/lint/rules_suggestions.go +1236 -0
- package/go-plugin/lint/rules_throw.go +31 -0
- package/go-plugin/lint/rules_ts.go +250 -0
- package/go-plugin/lint/rules_ts_extra.go +654 -0
- package/go-plugin/lint/rules_var.go +40 -0
- package/go-plugin/main.go +50 -0
- package/index.cjs +28 -0
- package/package.json +41 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
package lint
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
|
|
6
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// no-template-curly-in-string: a regular string contains `${...}`. The
|
|
10
|
+
// developer probably meant a template literal.
|
|
11
|
+
// https://eslint.org/docs/latest/rules/no-template-curly-in-string
|
|
12
|
+
type noTemplateCurlyInString struct{}
|
|
13
|
+
|
|
14
|
+
func (noTemplateCurlyInString) Name() string { return "no-template-curly-in-string" }
|
|
15
|
+
func (noTemplateCurlyInString) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindStringLiteral} }
|
|
16
|
+
func (noTemplateCurlyInString) Check(ctx *Context, node *shimast.Node) {
|
|
17
|
+
text := stringLiteralText(node)
|
|
18
|
+
if hasTemplatePlaceholder(text) {
|
|
19
|
+
ctx.Report(node, "Unexpected template string expression in regular string.")
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
func hasTemplatePlaceholder(text string) bool {
|
|
24
|
+
if !strings.Contains(text, "${") {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
// Match `${ ... }` style, not just literal "${" with nothing after.
|
|
28
|
+
idx := strings.Index(text, "${")
|
|
29
|
+
for idx >= 0 {
|
|
30
|
+
rest := text[idx+2:]
|
|
31
|
+
if strings.Contains(rest, "}") {
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
next := strings.Index(text[idx+1:], "${")
|
|
35
|
+
if next < 0 {
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
38
|
+
idx += next + 1
|
|
39
|
+
}
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// no-multi-str: `"line one \\n line two"` style backslash continuations.
|
|
44
|
+
// TS strips them silently and the result is rarely what the author meant.
|
|
45
|
+
// https://eslint.org/docs/latest/rules/no-multi-str
|
|
46
|
+
type noMultiStr struct{}
|
|
47
|
+
|
|
48
|
+
func (noMultiStr) Name() string { return "no-multi-str" }
|
|
49
|
+
func (noMultiStr) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindStringLiteral} }
|
|
50
|
+
func (noMultiStr) Check(ctx *Context, node *shimast.Node) {
|
|
51
|
+
src := nodeText(ctx.File, node)
|
|
52
|
+
// Look for `\` immediately before a newline in the *raw* source text.
|
|
53
|
+
if hasBackslashLineContinuation(src) {
|
|
54
|
+
ctx.Report(node, "Multiline support is limited to comments.")
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
func hasBackslashLineContinuation(src string) bool {
|
|
59
|
+
for i := 0; i < len(src)-1; i++ {
|
|
60
|
+
if src[i] == '\\' {
|
|
61
|
+
next := src[i+1]
|
|
62
|
+
if next == '\n' || next == '\r' {
|
|
63
|
+
return true
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return false
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// no-useless-concat: `"a" + "b"` of two literals. The compiler can't
|
|
71
|
+
// constant-fold every case, but this catches the obvious shapes.
|
|
72
|
+
// https://eslint.org/docs/latest/rules/no-useless-concat
|
|
73
|
+
type noUselessConcat struct{}
|
|
74
|
+
|
|
75
|
+
func (noUselessConcat) Name() string { return "no-useless-concat" }
|
|
76
|
+
func (noUselessConcat) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindBinaryExpression} }
|
|
77
|
+
func (noUselessConcat) Check(ctx *Context, node *shimast.Node) {
|
|
78
|
+
expr := node.AsBinaryExpression()
|
|
79
|
+
if expr == nil || expr.OperatorToken == nil {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
if expr.OperatorToken.Kind != shimast.KindPlusToken {
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
if isStringLikeLiteral(stripParens(expr.Left)) && isStringLikeLiteral(stripParens(expr.Right)) {
|
|
86
|
+
ctx.Report(node, "Unexpected string concatenation of literals.")
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
func isStringLikeLiteral(node *shimast.Node) bool {
|
|
91
|
+
if node == nil {
|
|
92
|
+
return false
|
|
93
|
+
}
|
|
94
|
+
switch node.Kind {
|
|
95
|
+
case shimast.KindStringLiteral, shimast.KindNoSubstitutionTemplateLiteral:
|
|
96
|
+
return true
|
|
97
|
+
}
|
|
98
|
+
return false
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// no-octal: `010` octal literal. Has to be opt-in via `0o10` in
|
|
102
|
+
// modern code; the legacy form is silently confusing.
|
|
103
|
+
// https://eslint.org/docs/latest/rules/no-octal
|
|
104
|
+
type noOctal struct{}
|
|
105
|
+
|
|
106
|
+
func (noOctal) Name() string { return "no-octal" }
|
|
107
|
+
func (noOctal) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindNumericLiteral} }
|
|
108
|
+
func (noOctal) Check(ctx *Context, node *shimast.Node) {
|
|
109
|
+
src := nodeText(ctx.File, node)
|
|
110
|
+
src = strings.TrimLeft(src, " \t")
|
|
111
|
+
if len(src) >= 2 && src[0] == '0' && isAsciiDigit(src[1]) {
|
|
112
|
+
ctx.Report(node, "Octal literals should not be used.")
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
func isAsciiDigit(b byte) bool { return b >= '0' && b <= '9' }
|
|
117
|
+
|
|
118
|
+
func init() {
|
|
119
|
+
Register(noTemplateCurlyInString{})
|
|
120
|
+
Register(noMultiStr{})
|
|
121
|
+
Register(noUselessConcat{})
|
|
122
|
+
Register(noOctal{})
|
|
123
|
+
}
|