@ttsc/lint 0.7.2 → 0.8.0-dev.20260505
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 +3 -3
- package/lib/index.js.map +1 -1
- package/package.json +3 -3
- package/plugin/ast_helpers.go +158 -158
- package/plugin/compile.go +303 -303
- package/plugin/config.go +322 -322
- package/plugin/engine.go +152 -153
- package/plugin/host.go +128 -128
- package/plugin/main.go +21 -21
- package/plugin/rules_arrays.go +51 -51
- package/plugin/rules_console.go +22 -23
- package/plugin/rules_debugger.go +4 -4
- package/plugin/rules_dupes.go +134 -134
- package/plugin/rules_empty.go +68 -68
- package/plugin/rules_eval.go +28 -28
- package/plugin/rules_finally.go +91 -91
- package/plugin/rules_gap.go +188 -188
- package/plugin/rules_logic.go +268 -268
- package/plugin/rules_loops.go +89 -89
- package/plugin/rules_misc.go +43 -43
- package/plugin/rules_problems.go +396 -396
- package/plugin/rules_protos.go +17 -17
- package/plugin/rules_self.go +56 -56
- package/plugin/rules_strings.go +65 -65
- package/plugin/rules_suggestions.go +863 -863
- package/plugin/rules_throw.go +16 -16
- package/plugin/rules_ts.go +161 -161
- package/plugin/rules_ts_extra.go +467 -467
- package/plugin/rules_var.go +86 -86
- package/src/index.ts +1 -4
package/plugin/compile.go
CHANGED
|
@@ -12,328 +12,328 @@
|
|
|
12
12
|
package main
|
|
13
13
|
|
|
14
14
|
import (
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
15
|
+
"context"
|
|
16
|
+
"errors"
|
|
17
|
+
"flag"
|
|
18
|
+
"fmt"
|
|
19
|
+
"io"
|
|
20
|
+
"os"
|
|
21
|
+
"path/filepath"
|
|
22
|
+
"strings"
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
25
|
+
shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
|
|
26
|
+
shimdw "github.com/microsoft/typescript-go/shim/diagnosticwriter"
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
// RunCheck implements `@ttsc/lint check` — typecheck + lint, no emit.
|
|
30
30
|
func RunCheck(args []string) int {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
opts, err := parseSubcommandFlags("check", args)
|
|
32
|
+
if err != nil {
|
|
33
|
+
fmt.Fprintln(os.Stderr, err)
|
|
34
|
+
return 2
|
|
35
|
+
}
|
|
36
|
+
opts.noEmit = true
|
|
37
|
+
return runProject(opts)
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
// RunBuild implements `@ttsc/lint build` — same diagnostic flow as
|
|
41
41
|
// `check`, plus the tsgo emit pipeline when emit is requested.
|
|
42
42
|
func RunBuild(args []string) int {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
opts, err := parseSubcommandFlags("build", args)
|
|
44
|
+
if err != nil {
|
|
45
|
+
fmt.Fprintln(os.Stderr, err)
|
|
46
|
+
return 2
|
|
47
|
+
}
|
|
48
|
+
return runProject(opts)
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// RunTransform implements `@ttsc/lint transform --file=PATH`. Lint rules
|
|
52
52
|
// still run for the whole program (lint quality depends on context), but
|
|
53
53
|
// emit is restricted to the requested file's JS output.
|
|
54
54
|
func RunTransform(args []string) int {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
55
|
+
fs := flag.NewFlagSet("transform", flag.ContinueOnError)
|
|
56
|
+
fs.SetOutput(os.Stderr)
|
|
57
|
+
file := fs.String("file", "", "absolute or cwd-relative path of the .ts file to transform")
|
|
58
|
+
out := fs.String("out", "", "write output JS to PATH (default: stdout)")
|
|
59
|
+
tsconfig := fs.String("tsconfig", "tsconfig.json", "tsconfig owning --file")
|
|
60
|
+
cwd := fs.String("cwd", "", "override the working directory")
|
|
61
|
+
pluginsJSON := fs.String("plugins-json", "", "ttsc plugin manifest JSON")
|
|
62
|
+
if err := fs.Parse(filterKnownFlags(args, map[string]bool{
|
|
63
|
+
"cwd": true,
|
|
64
|
+
"file": true,
|
|
65
|
+
"out": true,
|
|
66
|
+
"plugins-json": true,
|
|
67
|
+
"tsconfig": true,
|
|
68
|
+
})); err != nil {
|
|
69
|
+
return 2
|
|
70
|
+
}
|
|
71
|
+
if *file == "" {
|
|
72
|
+
fmt.Fprintln(os.Stderr, "@ttsc/lint transform: --file is required")
|
|
73
|
+
return 2
|
|
74
|
+
}
|
|
75
|
+
resolvedCwd, err := resolveCwd(*cwd)
|
|
76
|
+
if err != nil {
|
|
77
|
+
fmt.Fprintln(os.Stderr, err)
|
|
78
|
+
return 2
|
|
79
|
+
}
|
|
80
|
+
prog, parseDiags, err := loadProgram(resolvedCwd, *tsconfig, loadProgramOptions{
|
|
81
|
+
forceEmit: true,
|
|
82
|
+
})
|
|
83
|
+
if err != nil {
|
|
84
|
+
fmt.Fprintf(os.Stderr, "@ttsc/lint: %v\n", err)
|
|
85
|
+
return 2
|
|
86
|
+
}
|
|
87
|
+
if len(parseDiags) > 0 {
|
|
88
|
+
shimdw.FormatASTDiagnosticsWithColorAndContext(os.Stderr, parseDiags, resolvedCwd)
|
|
89
|
+
return 2
|
|
90
|
+
}
|
|
91
|
+
defer prog.close()
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
rules, err := loadRules(*pluginsJSON, resolvedCwd, *tsconfig)
|
|
94
|
+
if err != nil {
|
|
95
|
+
fmt.Fprintln(os.Stderr, err)
|
|
96
|
+
return 2
|
|
97
|
+
}
|
|
98
|
+
engine := NewEngine(rules)
|
|
99
|
+
warnUnknownRules(os.Stderr, engine.UnknownRules())
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
101
|
+
astDiags, lintDiags := collectDiagnostics(prog, engine)
|
|
102
|
+
if errors := shimdw.FormatMixedDiagnostics(os.Stderr, astDiags, lintDiags, resolvedCwd); errors > 0 {
|
|
103
|
+
return 2
|
|
104
|
+
}
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
106
|
+
absFile := *file
|
|
107
|
+
if !filepath.IsAbs(absFile) {
|
|
108
|
+
absFile = filepath.Join(resolvedCwd, absFile)
|
|
109
|
+
}
|
|
110
|
+
target := prog.findSourceFile(absFile)
|
|
111
|
+
if target == nil {
|
|
112
|
+
fmt.Fprintf(os.Stderr, "@ttsc/lint transform: source file not in program: %s\n", absFile)
|
|
113
|
+
return 2
|
|
114
|
+
}
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
116
|
+
var captured string
|
|
117
|
+
capture := func(name, text string, _ *shimcompiler.WriteFileData) error {
|
|
118
|
+
if !isJavaScriptOutput(name) {
|
|
119
|
+
return nil
|
|
120
|
+
}
|
|
121
|
+
captured = text
|
|
122
|
+
return nil
|
|
123
|
+
}
|
|
124
|
+
result := prog.tsProgram.Emit(context.Background(), shimcompiler.EmitOptions{
|
|
125
|
+
TargetSourceFile: target,
|
|
126
|
+
WriteFile: shimcompiler.WriteFile(capture),
|
|
127
|
+
})
|
|
128
|
+
if result == nil {
|
|
129
|
+
fmt.Fprintln(os.Stderr, "@ttsc/lint transform: Emit returned nil")
|
|
130
|
+
return 3
|
|
131
|
+
}
|
|
132
|
+
if len(result.Diagnostics) > 0 {
|
|
133
|
+
shimdw.FormatASTDiagnosticsWithColorAndContext(os.Stderr, result.Diagnostics, resolvedCwd)
|
|
134
|
+
}
|
|
135
|
+
if captured == "" {
|
|
136
|
+
fmt.Fprintf(os.Stderr, "@ttsc/lint transform: no output produced for %s\n", absFile)
|
|
137
|
+
return 3
|
|
138
|
+
}
|
|
139
|
+
if *out == "" {
|
|
140
|
+
fmt.Fprint(os.Stdout, captured)
|
|
141
|
+
return 0
|
|
142
|
+
}
|
|
143
|
+
if err := os.MkdirAll(filepath.Dir(*out), 0o755); err != nil {
|
|
144
|
+
fmt.Fprintf(os.Stderr, "@ttsc/lint transform: mkdir: %v\n", err)
|
|
145
|
+
return 3
|
|
146
|
+
}
|
|
147
|
+
if err := os.WriteFile(*out, []byte(captured), 0o644); err != nil {
|
|
148
|
+
fmt.Fprintf(os.Stderr, "@ttsc/lint transform: write: %v\n", err)
|
|
149
|
+
return 3
|
|
150
|
+
}
|
|
151
|
+
return 0
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
type subcommandOpts struct {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
155
|
+
cwd string
|
|
156
|
+
tsconfig string
|
|
157
|
+
pluginsJSON string
|
|
158
|
+
emit bool
|
|
159
|
+
noEmit bool
|
|
160
|
+
quiet bool
|
|
161
|
+
verbose bool
|
|
162
|
+
outDir string
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
func parseSubcommandFlags(name string, args []string) (*subcommandOpts, error) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
166
|
+
fs := flag.NewFlagSet(name, flag.ContinueOnError)
|
|
167
|
+
fs.SetOutput(os.Stderr)
|
|
168
|
+
cwd := fs.String("cwd", "", "")
|
|
169
|
+
tsconfig := fs.String("tsconfig", "tsconfig.json", "")
|
|
170
|
+
pluginsJSON := fs.String("plugins-json", "", "")
|
|
171
|
+
emit := fs.Bool("emit", false, "")
|
|
172
|
+
noEmit := fs.Bool("noEmit", false, "")
|
|
173
|
+
quiet := fs.Bool("quiet", false, "")
|
|
174
|
+
verbose := fs.Bool("verbose", false, "")
|
|
175
|
+
outDir := fs.String("outDir", "", "")
|
|
176
|
+
if err := fs.Parse(filterKnownFlags(args, map[string]bool{
|
|
177
|
+
"cwd": true,
|
|
178
|
+
"emit": false,
|
|
179
|
+
"noEmit": false,
|
|
180
|
+
"outDir": true,
|
|
181
|
+
"plugins-json": true,
|
|
182
|
+
"quiet": false,
|
|
183
|
+
"tsconfig": true,
|
|
184
|
+
"verbose": false,
|
|
185
|
+
})); err != nil {
|
|
186
|
+
return nil, err
|
|
187
|
+
}
|
|
188
|
+
if *emit && *noEmit {
|
|
189
|
+
return nil, errors.New("@ttsc/lint: --emit and --noEmit are mutually exclusive")
|
|
190
|
+
}
|
|
191
|
+
resolvedCwd, err := resolveCwd(*cwd)
|
|
192
|
+
if err != nil {
|
|
193
|
+
return nil, err
|
|
194
|
+
}
|
|
195
|
+
return &subcommandOpts{
|
|
196
|
+
cwd: resolvedCwd,
|
|
197
|
+
tsconfig: *tsconfig,
|
|
198
|
+
pluginsJSON: *pluginsJSON,
|
|
199
|
+
emit: *emit,
|
|
200
|
+
noEmit: *noEmit,
|
|
201
|
+
quiet: *quiet,
|
|
202
|
+
verbose: *verbose,
|
|
203
|
+
outDir: *outDir,
|
|
204
|
+
}, nil
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
func runProject(opts *subcommandOpts) int {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
208
|
+
prog, parseDiags, err := loadProgram(opts.cwd, opts.tsconfig, loadProgramOptions{
|
|
209
|
+
forceEmit: opts.emit,
|
|
210
|
+
forceNoEmit: opts.noEmit,
|
|
211
|
+
outDir: opts.outDir,
|
|
212
|
+
})
|
|
213
|
+
if err != nil {
|
|
214
|
+
fmt.Fprintf(os.Stderr, "@ttsc/lint: %v\n", err)
|
|
215
|
+
return 2
|
|
216
|
+
}
|
|
217
|
+
if len(parseDiags) > 0 {
|
|
218
|
+
shimdw.FormatASTDiagnosticsWithColorAndContext(os.Stderr, parseDiags, opts.cwd)
|
|
219
|
+
return 2
|
|
220
|
+
}
|
|
221
|
+
defer prog.close()
|
|
222
222
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
223
|
+
rules, err := loadRules(opts.pluginsJSON, opts.cwd, opts.tsconfig)
|
|
224
|
+
if err != nil {
|
|
225
|
+
fmt.Fprintln(os.Stderr, err)
|
|
226
|
+
return 2
|
|
227
|
+
}
|
|
228
|
+
engine := NewEngine(rules)
|
|
229
|
+
warnUnknownRules(os.Stderr, engine.UnknownRules())
|
|
230
230
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
231
|
+
astDiags, lintDiags := collectDiagnostics(prog, engine)
|
|
232
|
+
if errCount := shimdw.FormatMixedDiagnostics(os.Stderr, astDiags, lintDiags, opts.cwd); errCount > 0 {
|
|
233
|
+
return 2
|
|
234
|
+
}
|
|
235
235
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
236
|
+
if opts.noEmit || prog.parsed.ParsedConfig.CompilerOptions.NoEmit.IsTrue() {
|
|
237
|
+
return 0
|
|
238
|
+
}
|
|
239
239
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
240
|
+
result := prog.tsProgram.Emit(context.Background(), shimcompiler.EmitOptions{
|
|
241
|
+
WriteFile: shimcompiler.WriteFile(func(fileName, text string, data *shimcompiler.WriteFileData) error {
|
|
242
|
+
return defaultWriteFile(fileName, text)
|
|
243
|
+
}),
|
|
244
|
+
})
|
|
245
|
+
if result == nil {
|
|
246
|
+
fmt.Fprintln(os.Stderr, "@ttsc/lint: Emit returned nil")
|
|
247
|
+
return 3
|
|
248
|
+
}
|
|
249
|
+
if len(result.Diagnostics) > 0 {
|
|
250
|
+
errCount := shimdw.FormatMixedDiagnostics(os.Stderr, result.Diagnostics, nil, opts.cwd)
|
|
251
|
+
if errCount > 0 {
|
|
252
|
+
return 2
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if opts.verbose && result.EmittedFiles != nil {
|
|
256
|
+
fmt.Fprintf(os.Stdout, "@ttsc/lint: emitted=%d files\n", len(result.EmittedFiles))
|
|
257
|
+
for _, f := range result.EmittedFiles {
|
|
258
|
+
fmt.Fprintln(os.Stdout, " +", f)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return 0
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
func loadRules(pluginsJSON, cwd, tsconfigPath string) (RuleConfig, error) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
265
|
+
entries, err := ParsePlugins(pluginsJSON)
|
|
266
|
+
if err != nil {
|
|
267
|
+
return nil, err
|
|
268
|
+
}
|
|
269
|
+
entry, err := FindLintEntry(entries)
|
|
270
|
+
if err != nil {
|
|
271
|
+
return nil, err
|
|
272
|
+
}
|
|
273
|
+
if entry == nil {
|
|
274
|
+
return RuleConfig{}, nil
|
|
275
|
+
}
|
|
276
|
+
return LoadRuleConfig(entry, cwd, tsconfigPath)
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
func warnUnknownRules(w io.Writer, unknown []string) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
280
|
+
for _, name := range unknown {
|
|
281
|
+
fmt.Fprintf(w, "@ttsc/lint: ignoring unknown rule %q\n", name)
|
|
282
|
+
}
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
func filterKnownFlags(args []string, known map[string]bool) []string {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
286
|
+
out := make([]string, 0, len(args))
|
|
287
|
+
for i := 0; i < len(args); i++ {
|
|
288
|
+
arg := args[i]
|
|
289
|
+
if !strings.HasPrefix(arg, "-") || arg == "-" {
|
|
290
|
+
out = append(out, arg)
|
|
291
|
+
continue
|
|
292
|
+
}
|
|
293
|
+
name := strings.TrimLeft(arg, "-")
|
|
294
|
+
hasValue := strings.Contains(name, "=")
|
|
295
|
+
if index := strings.Index(name, "="); index >= 0 {
|
|
296
|
+
name = name[:index]
|
|
297
|
+
}
|
|
298
|
+
needsValue, ok := known[name]
|
|
299
|
+
if !ok {
|
|
300
|
+
if !hasValue && i+1 < len(args) && !strings.HasPrefix(args[i+1], "-") {
|
|
301
|
+
i++
|
|
302
|
+
}
|
|
303
|
+
continue
|
|
304
|
+
}
|
|
305
|
+
out = append(out, arg)
|
|
306
|
+
if needsValue && !hasValue && i+1 < len(args) {
|
|
307
|
+
i++
|
|
308
|
+
out = append(out, args[i])
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return out
|
|
312
312
|
}
|
|
313
313
|
|
|
314
314
|
// collectDiagnostics merges tsgo typecheck diagnostics with the lint
|
|
315
315
|
// engine's findings. The renderer takes the two slices and walks them in
|
|
316
316
|
// source order, so we don't need to interleave here.
|
|
317
317
|
func collectDiagnostics(prog *program, engine *Engine) ([]*shimast.Diagnostic, []*shimdw.LintDiagnostic) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
318
|
+
astDiags := prog.programDiagnostics()
|
|
319
|
+
files := prog.userSourceFiles()
|
|
320
|
+
findings := engine.Run(files, prog.checker)
|
|
321
|
+
lintDiags := make([]*shimdw.LintDiagnostic, 0, len(findings))
|
|
322
|
+
for _, finding := range findings {
|
|
323
|
+
category := shimdw.LintCategoryError
|
|
324
|
+
if finding.Severity == SeverityWarn {
|
|
325
|
+
category = shimdw.LintCategoryWarning
|
|
326
|
+
}
|
|
327
|
+
lintDiags = append(lintDiags, shimdw.NewLintDiagnostic(
|
|
328
|
+
finding.File,
|
|
329
|
+
finding.Pos,
|
|
330
|
+
finding.End,
|
|
331
|
+
ruleCode(finding.Rule),
|
|
332
|
+
category,
|
|
333
|
+
fmt.Sprintf("[%s] %s", finding.Rule, finding.Message),
|
|
334
|
+
))
|
|
335
|
+
}
|
|
336
|
+
return astDiags, lintDiags
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
// RuleCode hashes a rule name into a stable, positive int32 so the
|
|
@@ -341,13 +341,13 @@ func collectDiagnostics(prog *program, engine *Engine) ([]*shimast.Diagnostic, [
|
|
|
341
341
|
// 9000 to avoid colliding with tsgo's diagnostic codes (which top out
|
|
342
342
|
// well below that range). Public because tests pin the hash band.
|
|
343
343
|
func RuleCode(name string) int32 {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
344
|
+
const prime = 16777619
|
|
345
|
+
var h uint32 = 2166136261
|
|
346
|
+
for i := 0; i < len(name); i++ {
|
|
347
|
+
h ^= uint32(name[i])
|
|
348
|
+
h *= prime
|
|
349
|
+
}
|
|
350
|
+
return int32(9000 + (h % 9000))
|
|
351
351
|
}
|
|
352
352
|
|
|
353
353
|
// ruleCode is the internal alias kept for callers in the same package
|
|
@@ -355,32 +355,32 @@ func RuleCode(name string) int32 {
|
|
|
355
355
|
func ruleCode(name string) int32 { return RuleCode(name) }
|
|
356
356
|
|
|
357
357
|
func resolveCwd(override string) (string, error) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
358
|
+
if override != "" {
|
|
359
|
+
abs, err := filepath.Abs(override)
|
|
360
|
+
if err != nil {
|
|
361
|
+
return "", fmt.Errorf("@ttsc/lint: --cwd: %w", err)
|
|
362
|
+
}
|
|
363
|
+
return abs, nil
|
|
364
|
+
}
|
|
365
|
+
wd, err := os.Getwd()
|
|
366
|
+
if err != nil {
|
|
367
|
+
return "", fmt.Errorf("@ttsc/lint: cwd: %w", err)
|
|
368
|
+
}
|
|
369
|
+
return wd, nil
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
func isJavaScriptOutput(name string) bool {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
373
|
+
switch strings.ToLower(filepath.Ext(name)) {
|
|
374
|
+
case ".js", ".mjs", ".cjs":
|
|
375
|
+
return true
|
|
376
|
+
default:
|
|
377
|
+
return false
|
|
378
|
+
}
|
|
379
379
|
}
|
|
380
380
|
|
|
381
381
|
func defaultWriteFile(name string, text string) error {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
382
|
+
if err := os.MkdirAll(filepath.Dir(name), 0o755); err != nil {
|
|
383
|
+
return err
|
|
384
|
+
}
|
|
385
|
+
return os.WriteFile(name, []byte(text), 0o644)
|
|
386
386
|
}
|