@ttsc/lint 0.7.2 → 0.7.3

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/plugin/compile.go CHANGED
@@ -12,328 +12,328 @@
12
12
  package main
13
13
 
14
14
  import (
15
- "context"
16
- "errors"
17
- "flag"
18
- "fmt"
19
- "io"
20
- "os"
21
- "path/filepath"
22
- "strings"
15
+ "context"
16
+ "errors"
17
+ "flag"
18
+ "fmt"
19
+ "io"
20
+ "os"
21
+ "path/filepath"
22
+ "strings"
23
23
 
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"
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
- 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)
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
- opts, err := parseSubcommandFlags("build", args)
44
- if err != nil {
45
- fmt.Fprintln(os.Stderr, err)
46
- return 2
47
- }
48
- return runProject(opts)
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
- 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()
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
- 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())
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
- astDiags, lintDiags := collectDiagnostics(prog, engine)
102
- if errors := shimdw.FormatMixedDiagnostics(os.Stderr, astDiags, lintDiags, resolvedCwd); errors > 0 {
103
- return 2
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
- 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
- }
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
- 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
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
- cwd string
156
- tsconfig string
157
- pluginsJSON string
158
- emit bool
159
- noEmit bool
160
- quiet bool
161
- verbose bool
162
- outDir string
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
- 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
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
- 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()
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
- 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())
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
- astDiags, lintDiags := collectDiagnostics(prog, engine)
232
- if errCount := shimdw.FormatMixedDiagnostics(os.Stderr, astDiags, lintDiags, opts.cwd); errCount > 0 {
233
- return 2
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
- if opts.noEmit || prog.parsed.ParsedConfig.CompilerOptions.NoEmit.IsTrue() {
237
- return 0
238
- }
236
+ if opts.noEmit || prog.parsed.ParsedConfig.CompilerOptions.NoEmit.IsTrue() {
237
+ return 0
238
+ }
239
239
 
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
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
- 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)
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
- for _, name := range unknown {
281
- fmt.Fprintf(w, "@ttsc/lint: ignoring unknown rule %q\n", name)
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
- 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
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
- 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
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
- 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))
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
- 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
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
- switch strings.ToLower(filepath.Ext(name)) {
374
- case ".js", ".mjs", ".cjs":
375
- return true
376
- default:
377
- return false
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
- if err := os.MkdirAll(filepath.Dir(name), 0o755); err != nil {
383
- return err
384
- }
385
- return os.WriteFile(name, []byte(text), 0o644)
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
  }