@ttsc/lint 0.12.3 → 0.13.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/lib/defaultFormat.d.ts +10 -11
- package/lib/defaultFormat.js +10 -11
- package/lib/defaultFormat.js.map +1 -1
- package/lib/index.js +233 -135
- package/lib/index.js.map +1 -1
- package/lib/structures/ITtscLintConfig.d.ts +2 -2
- package/lib/structures/ITtscLintFormatConfig.d.ts +53 -55
- package/lib/structures/ITtscLintPluginConfig.d.ts +10 -77
- package/lib/structures/ITtscLintPluginMeta.d.ts +9 -1
- package/lib/structures/TtscLintRule.d.ts +10 -1
- package/lib/structures/TtscLintRuleMap.d.ts +2 -2
- package/lib/structures/TtscLintRuleOptions.d.ts +14 -0
- package/linthost/ast_helpers.go +80 -11
- package/linthost/compile.go +116 -112
- package/linthost/config.go +647 -687
- package/linthost/config_format.go +272 -247
- package/linthost/contrib_adapter.go +7 -0
- package/linthost/directives.go +116 -0
- package/linthost/dispatch.go +33 -33
- package/linthost/engine.go +156 -43
- package/linthost/fix.go +47 -27
- package/linthost/flags_gen.go +31 -0
- package/linthost/format.go +119 -3
- package/linthost/host.go +115 -5
- package/linthost/print_dispatch.go +128 -28
- package/linthost/print_doc.go +23 -0
- package/linthost/print_engine.go +168 -4
- package/linthost/print_nodes_array.go +17 -7
- package/linthost/print_nodes_call.go +138 -20
- package/linthost/print_nodes_function.go +353 -0
- package/linthost/print_nodes_imports.go +46 -29
- package/linthost/print_nodes_list.go +86 -5
- package/linthost/print_nodes_object.go +56 -11
- package/linthost/rules_arrays.go +5 -2
- package/linthost/rules_debugger.go +3 -2
- package/linthost/rules_dupes.go +7 -4
- package/linthost/rules_empty.go +3 -2
- package/linthost/rules_escape.go +35 -3
- package/linthost/rules_eval.go +3 -0
- package/linthost/rules_finally.go +11 -0
- package/linthost/rules_format_jsdoc.go +7 -0
- package/linthost/rules_format_print_width.go +270 -15
- package/linthost/rules_format_quotes.go +3 -0
- package/linthost/rules_format_sort_imports.go +4 -0
- package/linthost/rules_gap.go +75 -3
- package/linthost/rules_imports.go +5 -7
- package/linthost/rules_logic.go +75 -5
- package/linthost/rules_loops.go +4 -0
- package/linthost/rules_misc.go +4 -2
- package/linthost/rules_params.go +7 -11
- package/linthost/rules_problems.go +89 -22
- package/linthost/rules_promise.go +15 -0
- package/linthost/rules_protos.go +3 -2
- package/linthost/rules_self.go +6 -0
- package/linthost/rules_strings.go +10 -0
- package/linthost/rules_suggestions.go +174 -9
- package/linthost/rules_throw.go +2 -0
- package/linthost/rules_ts.go +13 -0
- package/linthost/rules_ts_extra.go +25 -8
- package/linthost/rules_var.go +15 -2
- package/package.json +3 -3
- package/plugin/main.go +4 -4
- package/rule/astutil/astutil.go +12 -0
- package/rule/rule.go +123 -123
- package/src/defaultFormat.ts +10 -11
- package/src/index.ts +257 -168
- package/src/structures/ITtscLintConfig.ts +2 -2
- package/src/structures/ITtscLintFormatConfig.ts +53 -55
- package/src/structures/ITtscLintPluginConfig.ts +10 -83
- package/src/structures/ITtscLintPluginMeta.ts +9 -1
- package/src/structures/TtscLintRule.ts +10 -1
- package/src/structures/TtscLintRuleMap.ts +17 -18
- package/src/structures/TtscLintRuleOptions.ts +15 -0
- package/linthost/eslint_runtime.go +0 -320
package/linthost/compile.go
CHANGED
|
@@ -13,6 +13,7 @@ package linthost
|
|
|
13
13
|
|
|
14
14
|
import (
|
|
15
15
|
"context"
|
|
16
|
+
"encoding/json"
|
|
16
17
|
"errors"
|
|
17
18
|
"flag"
|
|
18
19
|
"fmt"
|
|
@@ -59,26 +60,40 @@ func RunTransform(args []string) int {
|
|
|
59
60
|
tsconfig := fs.String("tsconfig", "tsconfig.json", "tsconfig owning --file")
|
|
60
61
|
cwd := fs.String("cwd", "", "override the working directory")
|
|
61
62
|
pluginsJSON := fs.String("plugins-json", "", "ttsc plugin manifest JSON")
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"plugins-json": true,
|
|
67
|
-
"tsconfig": true,
|
|
68
|
-
})); err != nil {
|
|
63
|
+
singleThreaded := fs.Bool("singleThreaded", false, "run TypeScript-Go single-threaded")
|
|
64
|
+
checkers := fs.Int("checkers", 0, "type-checker pool size (0 = TypeScript-Go default)")
|
|
65
|
+
tsgoArgsRaw := fs.String("tsgo-args", "", "JSON array of forwarded tsgo CLI flags")
|
|
66
|
+
if err := fs.Parse(filterKnownFlags(args, LintFlagAllowList)); err != nil {
|
|
69
67
|
return 2
|
|
70
68
|
}
|
|
71
69
|
if *file == "" {
|
|
72
70
|
fmt.Fprintln(os.Stderr, "@ttsc/lint transform: --file is required")
|
|
73
71
|
return 2
|
|
74
72
|
}
|
|
73
|
+
tsgoArgs, err := decodeTsgoArgs(*tsgoArgsRaw)
|
|
74
|
+
if err != nil {
|
|
75
|
+
fmt.Fprintln(os.Stderr, err)
|
|
76
|
+
return 2
|
|
77
|
+
}
|
|
75
78
|
resolvedCwd, err := resolveCwd(*cwd)
|
|
76
79
|
if err != nil {
|
|
77
80
|
fmt.Fprintln(os.Stderr, err)
|
|
78
81
|
return 2
|
|
79
82
|
}
|
|
83
|
+
rules, err := loadRules(*pluginsJSON, resolvedCwd, *tsconfig)
|
|
84
|
+
if err != nil {
|
|
85
|
+
fmt.Fprintln(os.Stderr, err)
|
|
86
|
+
return 2
|
|
87
|
+
}
|
|
88
|
+
engine := NewEngineWithResolver(rules)
|
|
89
|
+
engine.SetSerial(*singleThreaded)
|
|
90
|
+
|
|
80
91
|
prog, parseDiags, err := loadProgram(resolvedCwd, *tsconfig, loadProgramOptions{
|
|
81
|
-
forceEmit:
|
|
92
|
+
forceEmit: true,
|
|
93
|
+
needsRuleChecker: engine.NeedsTypeChecker(),
|
|
94
|
+
singleThreaded: *singleThreaded,
|
|
95
|
+
checkers: *checkers,
|
|
96
|
+
tsgoArgs: tsgoArgs,
|
|
82
97
|
})
|
|
83
98
|
if err != nil {
|
|
84
99
|
fmt.Fprintf(os.Stderr, "@ttsc/lint: %v\n", err)
|
|
@@ -90,21 +105,12 @@ func RunTransform(args []string) int {
|
|
|
90
105
|
}
|
|
91
106
|
defer prog.close()
|
|
92
107
|
|
|
93
|
-
|
|
94
|
-
if err != nil {
|
|
95
|
-
fmt.Fprintln(os.Stderr, err)
|
|
96
|
-
return 2
|
|
97
|
-
}
|
|
98
|
-
engine := NewEngineWithResolver(rules)
|
|
99
|
-
|
|
100
|
-
astDiags, lintDiags, externalRan, err := collectDiagnostics(prog, engine)
|
|
108
|
+
astDiags, lintDiags, err := collectDiagnostics(prog, engine)
|
|
101
109
|
if err != nil {
|
|
102
110
|
fmt.Fprintln(os.Stderr, err)
|
|
103
111
|
return 2
|
|
104
112
|
}
|
|
105
|
-
|
|
106
|
-
warnUnknownRules(os.Stderr, engine.UnknownRules())
|
|
107
|
-
}
|
|
113
|
+
warnUnknownRules(os.Stderr, engine.UnknownRules())
|
|
108
114
|
if errors := shimdw.FormatMixedDiagnostics(os.Stderr, astDiags, lintDiags, resolvedCwd); errors > 0 {
|
|
109
115
|
return 2
|
|
110
116
|
}
|
|
@@ -158,16 +164,22 @@ func RunTransform(args []string) int {
|
|
|
158
164
|
}
|
|
159
165
|
|
|
160
166
|
type subcommandOpts struct {
|
|
161
|
-
cwd
|
|
162
|
-
tsconfig
|
|
163
|
-
pluginsJSON
|
|
164
|
-
emit
|
|
165
|
-
noEmit
|
|
166
|
-
quiet
|
|
167
|
-
verbose
|
|
168
|
-
outDir
|
|
167
|
+
cwd string
|
|
168
|
+
tsconfig string
|
|
169
|
+
pluginsJSON string
|
|
170
|
+
emit bool
|
|
171
|
+
noEmit bool
|
|
172
|
+
quiet bool
|
|
173
|
+
verbose bool
|
|
174
|
+
outDir string
|
|
175
|
+
singleThreaded bool
|
|
176
|
+
checkers int
|
|
177
|
+
tsgoArgs []string
|
|
169
178
|
}
|
|
170
179
|
|
|
180
|
+
// parseSubcommandFlags parses the shared flag set used by the `check`,
|
|
181
|
+
// `build`, and `fix`/`format` subcommands. Unknown flags are silently
|
|
182
|
+
// stripped by `filterKnownFlags` before the standard FlagSet sees them.
|
|
171
183
|
func parseSubcommandFlags(name string, args []string) (*subcommandOpts, error) {
|
|
172
184
|
fs := flag.NewFlagSet(name, flag.ContinueOnError)
|
|
173
185
|
fs.SetOutput(os.Stderr)
|
|
@@ -179,42 +191,72 @@ func parseSubcommandFlags(name string, args []string) (*subcommandOpts, error) {
|
|
|
179
191
|
quiet := fs.Bool("quiet", false, "")
|
|
180
192
|
verbose := fs.Bool("verbose", false, "")
|
|
181
193
|
outDir := fs.String("outDir", "", "")
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
"outDir": true,
|
|
187
|
-
"plugins-json": true,
|
|
188
|
-
"quiet": false,
|
|
189
|
-
"tsconfig": true,
|
|
190
|
-
"verbose": false,
|
|
191
|
-
})); err != nil {
|
|
194
|
+
singleThreaded := fs.Bool("singleThreaded", false, "")
|
|
195
|
+
checkers := fs.Int("checkers", 0, "")
|
|
196
|
+
tsgoArgsRaw := fs.String("tsgo-args", "", "")
|
|
197
|
+
if err := fs.Parse(filterKnownFlags(args, LintFlagAllowList)); err != nil {
|
|
192
198
|
return nil, err
|
|
193
199
|
}
|
|
194
200
|
if *emit && *noEmit {
|
|
195
201
|
return nil, errors.New("@ttsc/lint: --emit and --noEmit are mutually exclusive")
|
|
196
202
|
}
|
|
203
|
+
tsgoArgs, err := decodeTsgoArgs(*tsgoArgsRaw)
|
|
204
|
+
if err != nil {
|
|
205
|
+
return nil, err
|
|
206
|
+
}
|
|
197
207
|
resolvedCwd, err := resolveCwd(*cwd)
|
|
198
208
|
if err != nil {
|
|
199
209
|
return nil, err
|
|
200
210
|
}
|
|
201
211
|
return &subcommandOpts{
|
|
202
|
-
cwd:
|
|
203
|
-
tsconfig:
|
|
204
|
-
pluginsJSON:
|
|
205
|
-
emit:
|
|
206
|
-
noEmit:
|
|
207
|
-
quiet:
|
|
208
|
-
verbose:
|
|
209
|
-
outDir:
|
|
212
|
+
cwd: resolvedCwd,
|
|
213
|
+
tsconfig: *tsconfig,
|
|
214
|
+
pluginsJSON: *pluginsJSON,
|
|
215
|
+
emit: *emit,
|
|
216
|
+
noEmit: *noEmit,
|
|
217
|
+
quiet: *quiet,
|
|
218
|
+
verbose: *verbose,
|
|
219
|
+
outDir: *outDir,
|
|
220
|
+
singleThreaded: *singleThreaded,
|
|
221
|
+
checkers: *checkers,
|
|
222
|
+
tsgoArgs: tsgoArgs,
|
|
210
223
|
}, nil
|
|
211
224
|
}
|
|
212
225
|
|
|
226
|
+
// decodeTsgoArgs decodes the JSON-array value of the `--tsgo-args` flag — the
|
|
227
|
+
// tsgo CLI flags the `ttsc` launcher forwarded — into a string slice. An empty
|
|
228
|
+
// flag yields a nil slice.
|
|
229
|
+
func decodeTsgoArgs(raw string) ([]string, error) {
|
|
230
|
+
if raw == "" {
|
|
231
|
+
return nil, nil
|
|
232
|
+
}
|
|
233
|
+
var args []string
|
|
234
|
+
if err := json.Unmarshal([]byte(raw), &args); err != nil {
|
|
235
|
+
return nil, fmt.Errorf("@ttsc/lint: invalid --tsgo-args: %w", err)
|
|
236
|
+
}
|
|
237
|
+
return args, nil
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// runProject is the shared body of RunCheck and RunBuild. It loads the
|
|
241
|
+
// program, collects diagnostics, renders them, and optionally emits
|
|
242
|
+
// JavaScript output when the config allows it.
|
|
213
243
|
func runProject(opts *subcommandOpts) int {
|
|
244
|
+
rules, err := loadRules(opts.pluginsJSON, opts.cwd, opts.tsconfig)
|
|
245
|
+
if err != nil {
|
|
246
|
+
fmt.Fprintln(os.Stderr, err)
|
|
247
|
+
return 2
|
|
248
|
+
}
|
|
249
|
+
engine := NewEngineWithResolver(rules)
|
|
250
|
+
engine.SetSerial(opts.singleThreaded)
|
|
251
|
+
|
|
214
252
|
prog, parseDiags, err := loadProgram(opts.cwd, opts.tsconfig, loadProgramOptions{
|
|
215
|
-
forceEmit:
|
|
216
|
-
forceNoEmit:
|
|
217
|
-
outDir:
|
|
253
|
+
forceEmit: opts.emit,
|
|
254
|
+
forceNoEmit: opts.noEmit,
|
|
255
|
+
outDir: opts.outDir,
|
|
256
|
+
needsRuleChecker: engine.NeedsTypeChecker(),
|
|
257
|
+
singleThreaded: opts.singleThreaded,
|
|
258
|
+
checkers: opts.checkers,
|
|
259
|
+
tsgoArgs: opts.tsgoArgs,
|
|
218
260
|
})
|
|
219
261
|
if err != nil {
|
|
220
262
|
fmt.Fprintf(os.Stderr, "@ttsc/lint: %v\n", err)
|
|
@@ -226,21 +268,12 @@ func runProject(opts *subcommandOpts) int {
|
|
|
226
268
|
}
|
|
227
269
|
defer prog.close()
|
|
228
270
|
|
|
229
|
-
|
|
230
|
-
if err != nil {
|
|
231
|
-
fmt.Fprintln(os.Stderr, err)
|
|
232
|
-
return 2
|
|
233
|
-
}
|
|
234
|
-
engine := NewEngineWithResolver(rules)
|
|
235
|
-
|
|
236
|
-
astDiags, lintDiags, externalRan, err := collectDiagnostics(prog, engine)
|
|
271
|
+
astDiags, lintDiags, err := collectDiagnostics(prog, engine)
|
|
237
272
|
if err != nil {
|
|
238
273
|
fmt.Fprintln(os.Stderr, err)
|
|
239
274
|
return 2
|
|
240
275
|
}
|
|
241
|
-
|
|
242
|
-
warnUnknownRules(os.Stderr, engine.UnknownRules())
|
|
243
|
-
}
|
|
276
|
+
warnUnknownRules(os.Stderr, engine.UnknownRules())
|
|
244
277
|
if errCount := shimdw.FormatMixedDiagnostics(os.Stderr, astDiags, lintDiags, opts.cwd); errCount > 0 {
|
|
245
278
|
return 2
|
|
246
279
|
}
|
|
@@ -273,6 +306,9 @@ func runProject(opts *subcommandOpts) int {
|
|
|
273
306
|
return 0
|
|
274
307
|
}
|
|
275
308
|
|
|
309
|
+
// loadRules decodes `--plugins-json`, locates the `@ttsc/lint` entry, and
|
|
310
|
+
// returns its resolved RuleResolver. Returns an empty RuleConfig (no rules
|
|
311
|
+
// enabled) when the lint entry is absent from the plugin manifest.
|
|
276
312
|
func loadRules(pluginsJSON, cwd, tsconfigPath string) (RuleResolver, error) {
|
|
277
313
|
entries, err := ParsePlugins(pluginsJSON)
|
|
278
314
|
if err != nil {
|
|
@@ -288,12 +324,22 @@ func loadRules(pluginsJSON, cwd, tsconfigPath string) (RuleResolver, error) {
|
|
|
288
324
|
return LoadConfigResolver(entry, cwd, tsconfigPath)
|
|
289
325
|
}
|
|
290
326
|
|
|
327
|
+
// warnUnknownRules writes one warning line per name in `unknown` to `w`.
|
|
328
|
+
// Called after engine construction so a config that names a rule the native
|
|
329
|
+
// engine does not implement surfaces a loud warning instead of silently
|
|
330
|
+
// linting nothing for that rule.
|
|
291
331
|
func warnUnknownRules(w io.Writer, unknown []string) {
|
|
292
332
|
for _, name := range unknown {
|
|
293
333
|
fmt.Fprintf(w, "@ttsc/lint: ignoring unknown rule %q\n", name)
|
|
294
334
|
}
|
|
295
335
|
}
|
|
296
336
|
|
|
337
|
+
// filterKnownFlags strips flags from `args` that are not present in `known`.
|
|
338
|
+
// The `known` map value is true when the flag takes a separate value token
|
|
339
|
+
// (e.g. `--tsconfig tsconfig.json`) and false for boolean flags. Unknown
|
|
340
|
+
// flags are silently dropped along with their value token when present.
|
|
341
|
+
// This lets the host forward a superset of flags without confusing the
|
|
342
|
+
// standard library's FlagSet.
|
|
297
343
|
func filterKnownFlags(args []string, known map[string]bool) []string {
|
|
298
344
|
out := make([]string, 0, len(args))
|
|
299
345
|
for i := 0; i < len(args); i++ {
|
|
@@ -326,7 +372,7 @@ func filterKnownFlags(args []string, known map[string]bool) []string {
|
|
|
326
372
|
// collectDiagnostics merges tsgo typecheck diagnostics with the lint
|
|
327
373
|
// engine's findings. The renderer takes the two slices and walks them in
|
|
328
374
|
// source order, so we don't need to interleave here.
|
|
329
|
-
func collectDiagnostics(prog *program, engine *Engine) ([]*shimast.Diagnostic, []*shimdw.LintDiagnostic,
|
|
375
|
+
func collectDiagnostics(prog *program, engine *Engine) ([]*shimast.Diagnostic, []*shimdw.LintDiagnostic, error) {
|
|
330
376
|
astDiags := prog.programDiagnostics()
|
|
331
377
|
files := prog.userSourceFiles()
|
|
332
378
|
findings := engine.Run(files, prog.checker)
|
|
@@ -345,58 +391,7 @@ func collectDiagnostics(prog *program, engine *Engine) ([]*shimast.Diagnostic, [
|
|
|
345
391
|
fmt.Sprintf("[%s] %s", finding.Rule, finding.Message),
|
|
346
392
|
))
|
|
347
393
|
}
|
|
348
|
-
|
|
349
|
-
return nil, nil, false, err
|
|
350
|
-
} else if ran {
|
|
351
|
-
return astDiags, mergeNativeAndExternalDiagnostics(nativeDiags, externalDiags), true, nil
|
|
352
|
-
}
|
|
353
|
-
return astDiags, nativeDiags, false, nil
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
func mergeNativeAndExternalDiagnostics(nativeDiags, externalDiags []*shimdw.LintDiagnostic) []*shimdw.LintDiagnostic {
|
|
357
|
-
if len(nativeDiags) == 0 {
|
|
358
|
-
return externalDiags
|
|
359
|
-
}
|
|
360
|
-
if len(externalDiags) == 0 {
|
|
361
|
-
return nativeDiags
|
|
362
|
-
}
|
|
363
|
-
externalRules := make(map[string]struct{})
|
|
364
|
-
for _, diag := range externalDiags {
|
|
365
|
-
if rule := canonicalLintRule(lintDiagnosticRule(diag)); rule != "" {
|
|
366
|
-
externalRules[rule] = struct{}{}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
out := make([]*shimdw.LintDiagnostic, 0, len(nativeDiags)+len(externalDiags))
|
|
370
|
-
for _, diag := range nativeDiags {
|
|
371
|
-
rule := canonicalLintRule(lintDiagnosticRule(diag))
|
|
372
|
-
if _, exists := externalRules[rule]; rule != "" && exists {
|
|
373
|
-
continue
|
|
374
|
-
}
|
|
375
|
-
out = append(out, diag)
|
|
376
|
-
}
|
|
377
|
-
return append(out, externalDiags...)
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
func lintDiagnosticRule(diag *shimdw.LintDiagnostic) string {
|
|
381
|
-
if diag == nil {
|
|
382
|
-
return ""
|
|
383
|
-
}
|
|
384
|
-
message := diag.Message()
|
|
385
|
-
if !strings.HasPrefix(message, "[") {
|
|
386
|
-
return ""
|
|
387
|
-
}
|
|
388
|
-
end := strings.Index(message, "]")
|
|
389
|
-
if end <= 1 {
|
|
390
|
-
return ""
|
|
391
|
-
}
|
|
392
|
-
return message[1:end]
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
func canonicalLintRule(rule string) string {
|
|
396
|
-
rule = strings.TrimSpace(rule)
|
|
397
|
-
rule = strings.TrimPrefix(rule, "@typescript-eslint/")
|
|
398
|
-
rule = strings.TrimPrefix(rule, "typescript-eslint/")
|
|
399
|
-
return rule
|
|
394
|
+
return astDiags, nativeDiags, nil
|
|
400
395
|
}
|
|
401
396
|
|
|
402
397
|
// RuleCode hashes a rule name into a stable, positive int32 so the
|
|
@@ -417,6 +412,9 @@ func RuleCode(name string) int32 {
|
|
|
417
412
|
// that prefer the lowercase form.
|
|
418
413
|
func ruleCode(name string) int32 { return RuleCode(name) }
|
|
419
414
|
|
|
415
|
+
// resolveCwd returns an absolute working directory. When `override` is
|
|
416
|
+
// non-empty it is made absolute; otherwise the process working directory
|
|
417
|
+
// is returned.
|
|
420
418
|
func resolveCwd(override string) (string, error) {
|
|
421
419
|
if override != "" {
|
|
422
420
|
abs, err := filepath.Abs(override)
|
|
@@ -432,6 +430,9 @@ func resolveCwd(override string) (string, error) {
|
|
|
432
430
|
return wd, nil
|
|
433
431
|
}
|
|
434
432
|
|
|
433
|
+
// isJavaScriptOutput reports whether `name` has a JavaScript output
|
|
434
|
+
// extension (.js, .mjs, or .cjs). Used to filter the emit callback so
|
|
435
|
+
// that `RunTransform` captures only the JS output for the target file.
|
|
435
436
|
func isJavaScriptOutput(name string) bool {
|
|
436
437
|
switch strings.ToLower(filepath.Ext(name)) {
|
|
437
438
|
case ".js", ".mjs", ".cjs":
|
|
@@ -441,6 +442,9 @@ func isJavaScriptOutput(name string) bool {
|
|
|
441
442
|
}
|
|
442
443
|
}
|
|
443
444
|
|
|
445
|
+
// defaultWriteFile creates all parent directories and writes `text` to
|
|
446
|
+
// `name` with mode 0644. Used as the WriteFile callback in `runProject`
|
|
447
|
+
// when the user requested emit.
|
|
444
448
|
func defaultWriteFile(name string, text string) error {
|
|
445
449
|
if err := os.MkdirAll(filepath.Dir(name), 0o755); err != nil {
|
|
446
450
|
return err
|