ttsc 0.26.0 → 0.26.2
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 +1 -1
- package/driver/diagnostics_preamble.go +284 -0
- package/driver/emit_plugin.go +51 -16
- package/driver/program.go +27 -14
- package/driver/rewrite.go +2 -2
- package/internal/graph/diagnostics.go +4 -3
- package/internal/graph/provenance.go +10 -1
- package/lib/compiler/internal/compileProjectInMemory.js +7 -0
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +28 -0
- package/lib/compiler/internal/runBuild.js +41 -2
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.js +7 -0
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/flags/schema.js +21 -0
- package/lib/flags/schema.js.map +1 -1
- package/lib/launcher/internal/assertNoSolutionBuild.d.ts +31 -0
- package/lib/launcher/internal/assertNoSolutionBuild.js +38 -0
- package/lib/launcher/internal/assertNoSolutionBuild.js.map +1 -0
- package/lib/launcher/internal/prepareExecution.js +31 -12
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +2 -0
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +2 -0
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/launcher/internal/runtimeHooks.js +7 -0
- package/lib/launcher/internal/runtimeHooks.js.map +1 -1
- package/package.json +8 -8
- package/shim/compiler/emit_sourcemap.go +56 -15
- package/src/compiler/internal/compileProjectInMemory.ts +7 -0
- package/src/compiler/internal/runBuild.ts +77 -4
- package/src/compiler/internal/runSingleFileEmit.ts +7 -0
- package/src/flags/schema.ts +23 -0
- package/src/launcher/internal/assertNoSolutionBuild.ts +40 -0
- package/src/launcher/internal/prepareExecution.ts +28 -12
- package/src/launcher/internal/runTtsc.ts +2 -0
- package/src/launcher/internal/runTtsx.ts +2 -0
- package/src/launcher/internal/runtimeHooks.ts +7 -0
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ That covers the CLI. The integrations each have a short guide:
|
|
|
38
38
|
|
|
39
39
|
## Lint
|
|
40
40
|
|
|
41
|
-
Lint and format inside the type-check you already run. 720+ rules across 21 families, plus a formatter
|
|
41
|
+
Lint and format inside the type-check you already run. 720+ rules across 21 families, plus a formatter whose rules are ported from Prettier 3.8.3 — see the [format guide](https://ttsc.dev/docs/lint/format) for the shapes it covers and the ones it leaves alone.
|
|
42
42
|
|
|
43
43
|
```ts
|
|
44
44
|
// src/index.ts
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
package driver
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
|
|
6
|
+
"github.com/microsoft/typescript-go/shim/ast"
|
|
7
|
+
"github.com/microsoft/typescript-go/shim/core"
|
|
8
|
+
shimparser "github.com/microsoft/typescript-go/shim/parser"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// Source-preamble correction for compiler diagnostics.
|
|
12
|
+
//
|
|
13
|
+
// ttsc injects a plugin's source preamble (e.g. @ttsc/banner's copyright block)
|
|
14
|
+
// at the SOURCE level: sourcePreambleFS prepends it before TypeScript-Go parses,
|
|
15
|
+
// so the preamble participates in comment emission, removeComments, JSDoc
|
|
16
|
+
// association, and `.d.ts` emit naturally. The side effect is that every
|
|
17
|
+
// position tsgo records — including every diagnostic's — is shifted down by the
|
|
18
|
+
// preamble's line count, while the file the user reads has no preamble. The
|
|
19
|
+
// emitted source map already undoes that shift (AdjustEmittedSourceMap); this is
|
|
20
|
+
// the same invariant applied to the other coordinate the user is shown.
|
|
21
|
+
//
|
|
22
|
+
// The correction lives here, at the Program boundary where the preamble is
|
|
23
|
+
// known, rather than in each renderer. `WritePrettyDiagnostics`, the structured
|
|
24
|
+
// diagnostic list the JS launcher serializes, the graph snapshot, and any future
|
|
25
|
+
// consumer all read a Diagnostic that is already authored-relative, so none of
|
|
26
|
+
// them has to remember.
|
|
27
|
+
|
|
28
|
+
// preambleView is the authored (preamble-free) view of one preamble-injected
|
|
29
|
+
// source file, plus the byte region the preamble occupies in the buffer tsgo
|
|
30
|
+
// parsed.
|
|
31
|
+
//
|
|
32
|
+
// file is a re-parse of the authored text. The diagnostic renderer reads a
|
|
33
|
+
// source file's FileName, Text, and ECMA line map to place a location and quote
|
|
34
|
+
// a code frame, and all three have to describe the text the user wrote — moving
|
|
35
|
+
// the position alone would still quote the preamble-bearing buffer. Re-parsing
|
|
36
|
+
// is what produces a line map tsgo itself computed rather than one this package
|
|
37
|
+
// re-derived, and it costs one parse per file that actually carries a
|
|
38
|
+
// diagnostic.
|
|
39
|
+
type preambleView struct {
|
|
40
|
+
file *ast.SourceFile
|
|
41
|
+
// start is the byte offset where the preamble was inserted, and length its
|
|
42
|
+
// byte length. ApplySourcePreamble inserts after a BOM and after a hashbang
|
|
43
|
+
// line, so start is not always zero.
|
|
44
|
+
start int
|
|
45
|
+
length int
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// authoredPos maps a position in the preamble-bearing buffer to the authored
|
|
49
|
+
// file. ok is false for a position inside the injected region, which has no
|
|
50
|
+
// authored counterpart at all.
|
|
51
|
+
func (v *preambleView) authoredPos(pos int) (int, bool) {
|
|
52
|
+
if pos < v.start {
|
|
53
|
+
return pos, true
|
|
54
|
+
}
|
|
55
|
+
if pos >= v.start+v.length {
|
|
56
|
+
return pos - v.length, true
|
|
57
|
+
}
|
|
58
|
+
return 0, false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// authoredEnd maps an exclusive end offset. An end inside the injected region is
|
|
62
|
+
// truncated to the region's authored boundary, so a range that starts before the
|
|
63
|
+
// preamble never stretches over text the user did not write.
|
|
64
|
+
func (v *preambleView) authoredEnd(end int) int {
|
|
65
|
+
if end <= v.start {
|
|
66
|
+
return end
|
|
67
|
+
}
|
|
68
|
+
if end >= v.start+v.length {
|
|
69
|
+
return end - v.length
|
|
70
|
+
}
|
|
71
|
+
return v.start
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// sourcePreambleBOM is the UTF-8 byte order mark ApplySourcePreamble keeps
|
|
75
|
+
// ahead of an injected preamble.
|
|
76
|
+
const sourcePreambleBOM = "\ufeff"
|
|
77
|
+
|
|
78
|
+
// sourcePreambleRegion locates the injected preamble inside the text tsgo
|
|
79
|
+
// parsed, mirroring ApplySourcePreamble's insertion point: after a BOM, and
|
|
80
|
+
// after a hashbang line when one is present.
|
|
81
|
+
//
|
|
82
|
+
// The region is read back out of the parsed text rather than recomputed from the
|
|
83
|
+
// on-disk file, so it stays correct whichever of the BOM or hashbang the host
|
|
84
|
+
// stripped before parsing, and the leading-prefix check makes the result
|
|
85
|
+
// self-verifying: a file the preamble was never injected into (a declaration
|
|
86
|
+
// file, a `.json` input, a library file) simply does not match and is reported
|
|
87
|
+
// as having no region.
|
|
88
|
+
//
|
|
89
|
+
// One degenerate shape is nominally off by one, matching the same allowance
|
|
90
|
+
// AdjustSourceMapForPreamble states: a file whose entire content is a hashbang
|
|
91
|
+
// with no trailing newline gets the preamble appended after an inserted newline,
|
|
92
|
+
// which is indistinguishable from an authored trailing newline. Only a position
|
|
93
|
+
// at that file's very end could differ, and such a file has no code to diagnose.
|
|
94
|
+
func sourcePreambleRegion(filePath, text, preamble string) (start int, length int, ok bool) {
|
|
95
|
+
if preamble == "" || !isSourcePreambleTarget(filePath) {
|
|
96
|
+
return 0, 0, false
|
|
97
|
+
}
|
|
98
|
+
start = 0
|
|
99
|
+
if strings.HasPrefix(text, sourcePreambleBOM) {
|
|
100
|
+
start = len(sourcePreambleBOM)
|
|
101
|
+
}
|
|
102
|
+
if strings.HasPrefix(text[start:], "#!") {
|
|
103
|
+
newline := strings.IndexByte(text[start:], '\n')
|
|
104
|
+
if newline < 0 {
|
|
105
|
+
return 0, 0, false
|
|
106
|
+
}
|
|
107
|
+
start += newline + 1
|
|
108
|
+
}
|
|
109
|
+
if !strings.HasPrefix(text[start:], preamble) {
|
|
110
|
+
return 0, 0, false
|
|
111
|
+
}
|
|
112
|
+
return start, len(preamble), true
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// preambleMapper rebuilds diagnostics against the authored source files of one
|
|
116
|
+
// preamble-bearing Program. It caches one authored view per source file so every
|
|
117
|
+
// diagnostic in a file shares a single re-parsed file object — the renderer's
|
|
118
|
+
// "Found N errors in M files" summary groups by that object's identity.
|
|
119
|
+
//
|
|
120
|
+
// A mapper is built per diagnostics call and thrown away with it: it holds
|
|
121
|
+
// re-parsed source files, which have no reason to outlive the report they were
|
|
122
|
+
// built for.
|
|
123
|
+
type preambleMapper struct {
|
|
124
|
+
preamble string
|
|
125
|
+
views map[*ast.SourceFile]*preambleView
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// newPreambleMapper returns nil when this Program has no source preamble, which
|
|
129
|
+
// is the signal to leave every diagnostic exactly as tsgo produced it.
|
|
130
|
+
func (p *Program) newPreambleMapper() *preambleMapper {
|
|
131
|
+
if p == nil || p.SourcePreamble == "" {
|
|
132
|
+
return nil
|
|
133
|
+
}
|
|
134
|
+
return &preambleMapper{
|
|
135
|
+
preamble: p.SourcePreamble,
|
|
136
|
+
views: map[*ast.SourceFile]*preambleView{},
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// viewOf returns the authored view of file, or nil when the preamble was never
|
|
141
|
+
// injected into it.
|
|
142
|
+
func (m *preambleMapper) viewOf(file *ast.SourceFile) *preambleView {
|
|
143
|
+
if m == nil || file == nil {
|
|
144
|
+
return nil
|
|
145
|
+
}
|
|
146
|
+
if view, cached := m.views[file]; cached {
|
|
147
|
+
return view
|
|
148
|
+
}
|
|
149
|
+
view := newPreambleView(file, m.preamble)
|
|
150
|
+
m.views[file] = view
|
|
151
|
+
return view
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// newPreambleView re-parses file without its injected preamble. It returns nil
|
|
155
|
+
// when the file carries no preamble, or when the re-parse yields nothing to
|
|
156
|
+
// anchor against — an uncorrected position is still better than a nil file.
|
|
157
|
+
func newPreambleView(file *ast.SourceFile, preamble string) *preambleView {
|
|
158
|
+
text := file.Text()
|
|
159
|
+
start, length, ok := sourcePreambleRegion(file.FileName(), text, preamble)
|
|
160
|
+
if !ok {
|
|
161
|
+
return nil
|
|
162
|
+
}
|
|
163
|
+
authored := shimparser.ParseSourceFile(file.ParseOptions(), text[:start]+text[start+length:], file.ScriptKind)
|
|
164
|
+
if authored == nil {
|
|
165
|
+
return nil
|
|
166
|
+
}
|
|
167
|
+
return &preambleView{file: authored, start: start, length: length}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// remapDiagnostic returns d anchored to its authored source file. ok is false
|
|
171
|
+
// when d's own position falls inside the injected preamble, which is the one
|
|
172
|
+
// case with no authored answer; see unanchoredPreambleDiagnostic for what the
|
|
173
|
+
// caller does with it.
|
|
174
|
+
//
|
|
175
|
+
// The original diagnostic is never mutated: it belongs to the Program's own
|
|
176
|
+
// diagnostic collection, which other callers read.
|
|
177
|
+
func (m *preambleMapper) remapDiagnostic(d *ast.Diagnostic) (*ast.Diagnostic, bool) {
|
|
178
|
+
if m == nil || d == nil {
|
|
179
|
+
return d, d != nil
|
|
180
|
+
}
|
|
181
|
+
related, relatedChanged := m.remapRelated(d.RelatedInformation())
|
|
182
|
+
view := m.viewOf(d.File())
|
|
183
|
+
if view == nil {
|
|
184
|
+
if !relatedChanged {
|
|
185
|
+
return d, true
|
|
186
|
+
}
|
|
187
|
+
clone := d.Clone()
|
|
188
|
+
clone.SetRelatedInfo(related)
|
|
189
|
+
return clone, true
|
|
190
|
+
}
|
|
191
|
+
pos, ok := view.authoredPos(d.Pos())
|
|
192
|
+
if !ok {
|
|
193
|
+
return nil, false
|
|
194
|
+
}
|
|
195
|
+
clone := d.Clone()
|
|
196
|
+
clone.SetFile(view.file)
|
|
197
|
+
clone.SetLocation(core.NewTextRange(pos, view.authoredEnd(d.End())))
|
|
198
|
+
if relatedChanged {
|
|
199
|
+
clone.SetRelatedInfo(related)
|
|
200
|
+
}
|
|
201
|
+
return clone, true
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// remapRelated remaps a diagnostic's related information, each entry against its
|
|
205
|
+
// own file. An entry with no authored counterpart is dropped rather than
|
|
206
|
+
// reported without a position: related information exists only to point at a
|
|
207
|
+
// second location, and the primary diagnostic still carries the message.
|
|
208
|
+
func (m *preambleMapper) remapRelated(in []*ast.Diagnostic) ([]*ast.Diagnostic, bool) {
|
|
209
|
+
if len(in) == 0 {
|
|
210
|
+
return nil, false
|
|
211
|
+
}
|
|
212
|
+
out := make([]*ast.Diagnostic, 0, len(in))
|
|
213
|
+
changed := false
|
|
214
|
+
for _, related := range in {
|
|
215
|
+
mapped, ok := m.remapDiagnostic(related)
|
|
216
|
+
if !ok {
|
|
217
|
+
changed = true
|
|
218
|
+
continue
|
|
219
|
+
}
|
|
220
|
+
if mapped != related {
|
|
221
|
+
changed = true
|
|
222
|
+
}
|
|
223
|
+
out = append(out, mapped)
|
|
224
|
+
}
|
|
225
|
+
return out, changed
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// unanchoredPreambleDiagnostic reports a diagnostic whose position lies inside
|
|
229
|
+
// an injected source preamble.
|
|
230
|
+
//
|
|
231
|
+
// The preamble is text the user never wrote, so there is no authored line to
|
|
232
|
+
// name. The source-map lane drops such segments, but a diagnostic is not a
|
|
233
|
+
// mapping: dropping it would leave a failing build with nothing on stderr to
|
|
234
|
+
// explain the failure, and clamping it to the top of the file would print a
|
|
235
|
+
// coordinate that is simply false — the exact defect this correction exists to
|
|
236
|
+
// remove. So the report is kept and only the coordinate is dropped: the message,
|
|
237
|
+
// the code, and the file the preamble was injected into survive, and the
|
|
238
|
+
// diagnostic renders through the anchor-less `path: message` form ttsc already
|
|
239
|
+
// uses for diagnostics that have no source range. It counts as an error exactly
|
|
240
|
+
// as it did before, so the exit code is unchanged.
|
|
241
|
+
//
|
|
242
|
+
// One consequence to know: the JS launcher recovers structured diagnostics by
|
|
243
|
+
// parsing rendered text, and the anchor-less form carries no `line:col` for that
|
|
244
|
+
// parser to match — so a preamble-region diagnostic reaches stderr and the exit
|
|
245
|
+
// code but not `IFailure.diagnostics`. That is the same deal every anchor-less
|
|
246
|
+
// driver diagnostic has always had (`driver: linked plugins failed to apply`),
|
|
247
|
+
// and it is preferable to publishing a structured position that is not real.
|
|
248
|
+
func unanchoredPreambleDiagnostic(d *ast.Diagnostic) Diagnostic {
|
|
249
|
+
out := Diagnostic{
|
|
250
|
+
Code: d.Code(),
|
|
251
|
+
Message: d.String(),
|
|
252
|
+
Severity: SeverityError,
|
|
253
|
+
}
|
|
254
|
+
if file := d.File(); file != nil {
|
|
255
|
+
out.File = file.FileName()
|
|
256
|
+
}
|
|
257
|
+
return out
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// convertProgramDiagnostics converts tsgo diagnostics produced by this Program,
|
|
261
|
+
// undoing any source-preamble shift first. Every path that turns this Program's
|
|
262
|
+
// raw diagnostics into driver Diagnostics goes through here, so the correction
|
|
263
|
+
// cannot be forgotten by a new caller. Diagnostics that never pass through a
|
|
264
|
+
// Program — a tsconfig parse failure, a bad CLI flag — keep using
|
|
265
|
+
// convertDiagnostics directly, because no preamble is ever injected into them.
|
|
266
|
+
func (p *Program) convertProgramDiagnostics(in []*ast.Diagnostic) []Diagnostic {
|
|
267
|
+
mapper := p.newPreambleMapper()
|
|
268
|
+
if mapper == nil {
|
|
269
|
+
return convertDiagnostics(in)
|
|
270
|
+
}
|
|
271
|
+
out := make([]Diagnostic, 0, len(in))
|
|
272
|
+
for _, d := range in {
|
|
273
|
+
if d == nil {
|
|
274
|
+
continue
|
|
275
|
+
}
|
|
276
|
+
mapped, ok := mapper.remapDiagnostic(d)
|
|
277
|
+
if !ok {
|
|
278
|
+
out = append(out, unanchoredPreambleDiagnostic(d))
|
|
279
|
+
continue
|
|
280
|
+
}
|
|
281
|
+
out = append(out, convertDiagnostic(mapped))
|
|
282
|
+
}
|
|
283
|
+
return out
|
|
284
|
+
}
|
package/driver/emit_plugin.go
CHANGED
|
@@ -133,13 +133,16 @@ func restoreOriginalDeclarationSymbols(ec *shimprinter.EmitContext, node *shimas
|
|
|
133
133
|
// EmitTransformPlugins are chained after the caller's transforms in
|
|
134
134
|
// registration order.
|
|
135
135
|
//
|
|
136
|
-
// Because the JavaScript side bypasses tsgo's own emitter, it reproduces
|
|
137
|
-
// emitter's
|
|
138
|
-
// `inlineSourceMap` build emits a `.js.map` (and
|
|
139
|
-
// trailer) just like a plain build, even when a
|
|
140
|
-
// line into many
|
|
141
|
-
//
|
|
142
|
-
//
|
|
136
|
+
// Because the JavaScript side bypasses tsgo's own emitter, it reproduces that
|
|
137
|
+
// emitter's whole printSourceFile step via PrintFileWithSourceMap: a
|
|
138
|
+
// `sourceMap` / `inlineSourceMap` build emits a `.js.map` (and
|
|
139
|
+
// `//# sourceMappingURL=` trailer) just like a plain build, even when a
|
|
140
|
+
// transform expanded one source line into many; an `emitBOM` build still starts
|
|
141
|
+
// with the byte order mark; and the caller's WriteFile still receives the
|
|
142
|
+
// WriteFileData the emitter would hand it. All non-JavaScript outputs stay
|
|
143
|
+
// delegated to tsgo's normal dts-only emitter so declaration files, declaration
|
|
144
|
+
// maps, and any future declaration-lane outputs are not silently lost by the
|
|
145
|
+
// hand-assembled JS path.
|
|
143
146
|
func (p *Program) EmitWithPluginTransformers(transforms []PluginTransform, writeFile shimcompiler.WriteFile) ([]Diagnostic, error) {
|
|
144
147
|
if p == nil || p.TSProgram == nil {
|
|
145
148
|
return nil, errors.New("driver: nil program")
|
|
@@ -183,10 +186,10 @@ func (p *Program) EmitWithPluginTransformers(transforms []PluginTransform, write
|
|
|
183
186
|
}
|
|
184
187
|
// Print through the source-map-aware helper so a `sourceMap` /
|
|
185
188
|
// `inlineSourceMap` build still gets its `.js.map` and sourceMappingURL
|
|
186
|
-
// trailer
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
// before.
|
|
189
|
+
// trailer, and an `emitBOM` build its leading mark: the hand-assembled
|
|
190
|
+
// emit pipeline does not run tsgo's emitter, so everything printSourceFile
|
|
191
|
+
// would otherwise do around the printer has to happen here. With maps and
|
|
192
|
+
// emitBOM off this is the same bare-printer output as before.
|
|
190
193
|
printed := shimcompiler.PrintFileWithSourceMap(ec, out.AsNode(), out, options, host, paths.JsFilePath(), paths.SourceMapFilePath())
|
|
191
194
|
// A source-level preamble (e.g. @ttsc/banner linked into a typia host)
|
|
192
195
|
// shifts the map's source coordinates; correct them here too, so the
|
|
@@ -204,10 +207,17 @@ func (p *Program) EmitWithPluginTransformers(transforms []PluginTransform, write
|
|
|
204
207
|
}
|
|
205
208
|
}
|
|
206
209
|
}
|
|
207
|
-
|
|
210
|
+
// The emitter hands its writeFile callback a WriteFileData for the
|
|
211
|
+
// JavaScript and a nil one for the map (printSourceFile:
|
|
212
|
+
// `writeText(sourceMapFilePath, sourceMap, nil)`), so this lane does the
|
|
213
|
+
// same. See writePluginEmitOutput for what the struct carries here and
|
|
214
|
+
// why the remaining fields stay zero.
|
|
215
|
+
if err := p.writePluginEmitOutput(paths.JsFilePath(), printed.JS, &shimcompiler.WriteFileData{
|
|
216
|
+
SourceMapUrlPos: printed.SourceMapUrlPos,
|
|
217
|
+
}, writeFile); err != nil {
|
|
208
218
|
return nil, err
|
|
209
219
|
}
|
|
210
|
-
if err := p.writePluginEmitOutput(printed.MapPath, printed.MapText, writeFile); err != nil {
|
|
220
|
+
if err := p.writePluginEmitOutput(printed.MapPath, printed.MapText, nil, writeFile); err != nil {
|
|
211
221
|
return nil, err
|
|
212
222
|
}
|
|
213
223
|
}
|
|
@@ -235,17 +245,42 @@ func (p *Program) EmitWithPluginTransformers(transforms []PluginTransform, write
|
|
|
235
245
|
},
|
|
236
246
|
})
|
|
237
247
|
if result != nil && len(result.Diagnostics) != 0 {
|
|
238
|
-
return
|
|
248
|
+
return p.convertProgramDiagnostics(result.Diagnostics), nil
|
|
239
249
|
}
|
|
240
250
|
return nil, nil
|
|
241
251
|
}
|
|
242
252
|
|
|
243
|
-
|
|
253
|
+
// writePluginEmitOutput writes one artifact of the hand-assembled emit, passing
|
|
254
|
+
// the caller's WriteFile the same WriteFileData tsgo's emitter would.
|
|
255
|
+
//
|
|
256
|
+
// What this lane can populate, and what it deliberately cannot:
|
|
257
|
+
//
|
|
258
|
+
// - SourceMapUrlPos: the offset of the `//# sourceMappingURL=` trailer, or -1
|
|
259
|
+
// when none was written. PrintFileWithSourceMap records it exactly where
|
|
260
|
+
// printSourceFile does, so a consumer that relocates or rewrites the trailer
|
|
261
|
+
// works the same on both lanes. This is why a nil data was a real loss and
|
|
262
|
+
// not merely a cosmetic one.
|
|
263
|
+
// - Diagnostics: always empty. The emitter's field carries its accumulated
|
|
264
|
+
// emitterDiagnostics, which on the JavaScript lane are its own write
|
|
265
|
+
// failures; here a write failure is returned as an `error` from
|
|
266
|
+
// EmitWithPluginTransformers instead, and the declaration lane's diagnostics
|
|
267
|
+
// reach the caller through tsgo's own EmitOnlyDts pass and its own
|
|
268
|
+
// WriteFileData. Empty therefore means "nothing to report", not "never
|
|
269
|
+
// populated".
|
|
270
|
+
// - BuildInfo: always nil. It is the `.tsbuildinfo` payload, and this lane
|
|
271
|
+
// emits only JavaScript and its map.
|
|
272
|
+
// - SkippedDtsWrite: an out-parameter for the callee, left zero. Each write
|
|
273
|
+
// gets its own struct so one file's callback cannot observe another's.
|
|
274
|
+
//
|
|
275
|
+
// Nothing on this lane reads the struct back afterwards: unlike tsgo's emitter,
|
|
276
|
+
// EmitWithPluginTransformers builds no EmitResult, so there is no EmittedFiles
|
|
277
|
+
// list for a callee-set SkippedDtsWrite to keep a file out of.
|
|
278
|
+
func (p *Program) writePluginEmitOutput(fileName, text string, data *shimcompiler.WriteFileData, writeFile shimcompiler.WriteFile) error {
|
|
244
279
|
if fileName == "" || p.outputEscapesOutDir(fileName) {
|
|
245
280
|
return nil
|
|
246
281
|
}
|
|
247
282
|
if writeFile != nil {
|
|
248
|
-
return writeFile(fileName, text,
|
|
283
|
+
return writeFile(fileName, text, data)
|
|
249
284
|
}
|
|
250
285
|
return DefaultWriteFile(fileName, text)
|
|
251
286
|
}
|
package/driver/program.go
CHANGED
|
@@ -638,7 +638,11 @@ func (p *Program) diagnostics(files []*ast.SourceFile) []Diagnostic {
|
|
|
638
638
|
}
|
|
639
639
|
}
|
|
640
640
|
raw = filterDiagnostics(raw)
|
|
641
|
-
|
|
641
|
+
// filterDiagnostics runs first because it resolves a node from the position
|
|
642
|
+
// tsgo recorded, which only makes sense against the tree tsgo parsed. The
|
|
643
|
+
// preamble correction happens inside convertProgramDiagnostics, once the
|
|
644
|
+
// positions are no longer used to look anything up.
|
|
645
|
+
return append(out, p.convertProgramDiagnostics(shimcompiler.SortAndDeduplicateDiagnostics(raw))...)
|
|
642
646
|
}
|
|
643
647
|
|
|
644
648
|
// filterDiagnostics removes diagnostics that are false positives in ttsc's
|
|
@@ -684,25 +688,34 @@ func isUnusedOverloadSignatureTypeParameterDiagnostic(d *ast.Diagnostic) bool {
|
|
|
684
688
|
// convertDiagnostics translates shim-specific diagnostics into the plain
|
|
685
689
|
// Diagnostic struct with line/column populated via tsgo's ECMALineMap (the
|
|
686
690
|
// same helper tsc uses for its "file:line:col: message" banner).
|
|
691
|
+
//
|
|
692
|
+
// Diagnostics produced by a Program go through convertProgramDiagnostics
|
|
693
|
+
// instead, which undoes a source preamble's position shift first.
|
|
687
694
|
func convertDiagnostics(in []*ast.Diagnostic) []Diagnostic {
|
|
688
695
|
out := make([]Diagnostic, 0, len(in))
|
|
689
696
|
for _, d := range in {
|
|
690
697
|
if d == nil {
|
|
691
698
|
continue
|
|
692
699
|
}
|
|
693
|
-
|
|
694
|
-
if file := d.File(); file != nil {
|
|
695
|
-
diag.File = file.FileName()
|
|
696
|
-
if pos := d.Pos(); pos >= 0 {
|
|
697
|
-
length := d.Len()
|
|
698
|
-
diag.Start = &pos
|
|
699
|
-
diag.Length = &length
|
|
700
|
-
line, col := shimscanner.GetECMALineAndByteOffsetOfPosition(file, pos)
|
|
701
|
-
diag.Line = line + 1
|
|
702
|
-
diag.Column = col + 1
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
out = append(out, diag)
|
|
700
|
+
out = append(out, convertDiagnostic(d))
|
|
706
701
|
}
|
|
707
702
|
return out
|
|
708
703
|
}
|
|
704
|
+
|
|
705
|
+
// convertDiagnostic translates one shim diagnostic, taking its file, line, and
|
|
706
|
+
// column from whatever source file the diagnostic is anchored to.
|
|
707
|
+
func convertDiagnostic(d *ast.Diagnostic) Diagnostic {
|
|
708
|
+
diag := Diagnostic{Code: d.Code(), Message: d.String(), raw: d}
|
|
709
|
+
if file := d.File(); file != nil {
|
|
710
|
+
diag.File = file.FileName()
|
|
711
|
+
if pos := d.Pos(); pos >= 0 {
|
|
712
|
+
length := d.Len()
|
|
713
|
+
diag.Start = &pos
|
|
714
|
+
diag.Length = &length
|
|
715
|
+
line, col := shimscanner.GetECMALineAndByteOffsetOfPosition(file, pos)
|
|
716
|
+
diag.Line = line + 1
|
|
717
|
+
diag.Column = col + 1
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return diag
|
|
721
|
+
}
|
package/driver/rewrite.go
CHANGED
|
@@ -132,7 +132,7 @@ func (p *Program) EmitAllRaw(writeFile shimcompiler.WriteFile) (*shimcompiler.Em
|
|
|
132
132
|
result := p.TSProgram.Emit(context.Background(), shimcompiler.EmitOptions{
|
|
133
133
|
WriteFile: wf,
|
|
134
134
|
})
|
|
135
|
-
return result,
|
|
135
|
+
return result, p.convertProgramDiagnostics(result.Diagnostics), nil
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
// EmitFile runs tsgo's emitter for one source file, applying the same rewrite
|
|
@@ -201,7 +201,7 @@ func (p *Program) emit(rs *RewriteSet, target *ast.SourceFile, writeFile shimcom
|
|
|
201
201
|
TargetSourceFile: target,
|
|
202
202
|
WriteFile: wf,
|
|
203
203
|
})
|
|
204
|
-
return result,
|
|
204
|
+
return result, p.convertProgramDiagnostics(result.Diagnostics), nil
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
// DefaultWriteFile is the default disk writer used when EmitAll's caller does not
|
|
@@ -3,9 +3,10 @@ package graph
|
|
|
3
3
|
import "github.com/samchon/ttsc/packages/ttsc/driver"
|
|
4
4
|
|
|
5
5
|
// FileDiagnostics returns the resident program's compiler diagnostics for the
|
|
6
|
-
// source file at path,
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// source file at path, with the code tsgo reports and the location the driver
|
|
7
|
+
// resolves (see Diagnostic: authored-relative under a source preamble). Because
|
|
8
|
+
// the graph rides the already-open Program, this is one Program.Diagnostics()
|
|
9
|
+
// call over the warm checker, not a second compile.
|
|
9
10
|
//
|
|
10
11
|
// This is the compiler-only slice: it carries the TypeScript semantic
|
|
11
12
|
// diagnostics, not @ttsc/lint or transform-plugin findings.
|
|
@@ -179,7 +179,16 @@ type SourceDigest struct {
|
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
// Diagnostic is one compiler diagnostic riding the snapshot that produced the
|
|
182
|
-
// facts. Positions are 1-based
|
|
182
|
+
// facts. Positions are 1-based and authored-relative: where a SourcePreamble
|
|
183
|
+
// plugin injected text ahead of a file, the driver maps the position back onto
|
|
184
|
+
// the bytes a consumer can read for itself (the file this dump publishes a Disk
|
|
185
|
+
// digest of), rather than onto the augmented text the checker saw. Node spans in
|
|
186
|
+
// this dump stay checker-relative by declaration — see SourceDigest — because
|
|
187
|
+
// they describe the facts, while a diagnostic is an instruction to go look at a
|
|
188
|
+
// line.
|
|
189
|
+
//
|
|
190
|
+
// A diagnostic that points inside the injected preamble itself has no authored
|
|
191
|
+
// counterpart, so it arrives with Line and Column zero and its message intact.
|
|
183
192
|
type Diagnostic struct {
|
|
184
193
|
// File uses the dump's path vocabulary.
|
|
185
194
|
File string `json:"file"`
|
|
@@ -91,6 +91,13 @@ function compileProjectWithPlugins(options, cwd, project) {
|
|
|
91
91
|
emit: true,
|
|
92
92
|
forceListEmittedFiles: true,
|
|
93
93
|
outDir: tempOutDir,
|
|
94
|
+
// The temp directory is an `outDir` this lane injected so the emit can be
|
|
95
|
+
// read back as strings; the project need not declare one at all. tsgo
|
|
96
|
+
// answers an inferred common source directory with TS5011 as soon as any
|
|
97
|
+
// `outDir` is in play, so pin the root it would infer. The keys
|
|
98
|
+
// `outputKeyMapper` builds are unchanged by it — that root is exactly the
|
|
99
|
+
// layout tsgo already lays the emit out against (issue #1172).
|
|
100
|
+
pinInferredRootDir: true,
|
|
94
101
|
quiet: true,
|
|
95
102
|
resolvedProject: project,
|
|
96
103
|
structuredDiagnostics: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compileProjectInMemory.js","sourceRoot":"","sources":["../../../src/compiler/internal/compileProjectInMemory.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"compileProjectInMemory.js","sourceRoot":"","sources":["../../../src/compiler/internal/compileProjectInMemory.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,34 @@ export type RunBuildOptions = TtscBuildOptions & {
|
|
|
11
11
|
forceListEmittedFiles?: boolean;
|
|
12
12
|
/** Keep every compiler-owned side product inside this private directory. */
|
|
13
13
|
isolateOutputsTo?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Hand tsgo the `rootDir` it would otherwise infer, for a build whose
|
|
16
|
+
* `outDir` this process injected rather than the project declaring it.
|
|
17
|
+
*
|
|
18
|
+
* Tsgo answers an inferred common source directory with TS5011 the moment an
|
|
19
|
+
* `outDir` is in play, so injecting one turns a project that declares no
|
|
20
|
+
* output at all — the `noEmit` check-only shape `tsgo`, `ttsc`, and `ttsc
|
|
21
|
+
* --emit` all accept — into one that must configure the layout of output the
|
|
22
|
+
* user never asked for and never sees (issue #1172).
|
|
23
|
+
*
|
|
24
|
+
* The pinned value is the one tsgo itself infers: with a config file in play
|
|
25
|
+
* its common source directory is that file's directory, never the computed
|
|
26
|
+
* common directory of the input files (`outputpaths.GetCommonSourceDirectory`
|
|
27
|
+
* consults the file list only for a config-less program). Pinning it
|
|
28
|
+
* therefore silences the demand without moving a single output, and it is the
|
|
29
|
+
* same source root `prepareExecution.ts::resolveRuntimeSourceRoot`,
|
|
30
|
+
* `runtimeHooks.ts::resolveDependencySourceRoot`, and
|
|
31
|
+
* `watchTopology.ts::inferPerSourceCompilerOutputs` already model on the
|
|
32
|
+
* JavaScript side.
|
|
33
|
+
*
|
|
34
|
+
* Ignored when the project declares its own `rootDir`: that project already
|
|
35
|
+
* satisfies tsgo, and overriding it would relocate the emit out from under
|
|
36
|
+
* every consumer that mirrors the declared root.
|
|
37
|
+
*
|
|
38
|
+
* Never set for a user-supplied `--outDir`. That outDir is the user's own
|
|
39
|
+
* request, and TS5011 is then tsgo's genuine answer to it.
|
|
40
|
+
*/
|
|
41
|
+
pinInferredRootDir?: boolean;
|
|
14
42
|
/**
|
|
15
43
|
* Receives selected native-plugin source roots after the project resolves.
|
|
16
44
|
* The watch launcher uses these roots to invalidate a sidecar when its Go
|
|
@@ -861,6 +861,9 @@ function createTsgoBuildArgs(execution, options, flags) {
|
|
|
861
861
|
if (options.forceRuntimeSourceMap === true) {
|
|
862
862
|
args.push("--sourceMap", "true");
|
|
863
863
|
}
|
|
864
|
+
// Pushed before passthrough for the same reason: a user `--rootDir` still
|
|
865
|
+
// wins over the value ttsc pins for its own injected `outDir`.
|
|
866
|
+
args.push(...pinnedRootDirArgs(execution, options));
|
|
864
867
|
}
|
|
865
868
|
else if (options.emit === false) {
|
|
866
869
|
args.push("--noEmit");
|
|
@@ -880,6 +883,36 @@ function createTsgoBuildArgs(execution, options, flags) {
|
|
|
880
883
|
}
|
|
881
884
|
return args;
|
|
882
885
|
}
|
|
886
|
+
/**
|
|
887
|
+
* The explicit `--rootDir` a caller-injected `outDir` needs, or nothing.
|
|
888
|
+
*
|
|
889
|
+
* See {@link RunBuildOptions.pinInferredRootDir} for why an injected `outDir`
|
|
890
|
+
* needs one at all. Three conditions gate it, and each one is load-bearing:
|
|
891
|
+
*
|
|
892
|
+
* - The caller injected the `outDir` itself, so a user `--outDir` keeps tsgo's
|
|
893
|
+
* own TS5011 answer;
|
|
894
|
+
* - This pass emits, because a no-emit pass has no layout to pin (tsgo skips the
|
|
895
|
+
* check for `noEmit` too);
|
|
896
|
+
* - The project declares no `rootDir` of its own, so a declared layout is never
|
|
897
|
+
* overridden.
|
|
898
|
+
*
|
|
899
|
+
* `execution.projectRoot` is the directory of the tsconfig ttsc resolved and
|
|
900
|
+
* hands tsgo as `-p`, which is exactly the directory tsgo infers, and it is
|
|
901
|
+
* spelled the way tsgo will spell the input file names it compares against it —
|
|
902
|
+
* both come from the same `fs.realpathSync` pass. Resolving it any further (a
|
|
903
|
+
* Windows 8.3 expansion, say) would leave the comparison lexically mixed, and
|
|
904
|
+
* `ContainsPath` is lexical: every input would count as outside `rootDir` and
|
|
905
|
+
* tsgo would emit it beside the user's source instead of under `outDir`.
|
|
906
|
+
*/
|
|
907
|
+
function pinnedRootDirArgs(execution, options) {
|
|
908
|
+
if (options.pinInferredRootDir !== true)
|
|
909
|
+
return [];
|
|
910
|
+
if (options.emit !== true)
|
|
911
|
+
return [];
|
|
912
|
+
if (typeof execution.project.compilerOptions.rootDir === "string")
|
|
913
|
+
return [];
|
|
914
|
+
return ["--rootDir", execution.projectRoot];
|
|
915
|
+
}
|
|
883
916
|
/**
|
|
884
917
|
* Return `["--pretty", "false"]` when structured diagnostics are requested so
|
|
885
918
|
* that the output can be parsed line-by-line, or an empty array otherwise.
|
|
@@ -945,7 +978,10 @@ function createNativeBuildArgs(execution, options, plugins) {
|
|
|
945
978
|
args.push("--quiet");
|
|
946
979
|
}
|
|
947
980
|
}
|
|
948
|
-
|
|
981
|
+
// The sidecar builds its Program in-process, so the pinned `rootDir` has to
|
|
982
|
+
// travel the same channel every other tsgo option takes to it — the host flag
|
|
983
|
+
// set does not declare `--rootDir`, and `filterHostArgs` would strip it.
|
|
984
|
+
args.push(...createNativeTsgoArgs(options, pinnedRootDirArgs(execution, options)));
|
|
949
985
|
return args;
|
|
950
986
|
}
|
|
951
987
|
/** Build the argument list for a native plugin check/fix/format invocation. */
|
|
@@ -1059,8 +1095,11 @@ function transformHostTimingLabel(plugins) {
|
|
|
1059
1095
|
* a plugin build the same way it reaches the plain tsgo lane. Encoded as a
|
|
1060
1096
|
* single token so the sidecars' unknown-flag filters keep it intact.
|
|
1061
1097
|
*/
|
|
1062
|
-
function createNativeTsgoArgs(options) {
|
|
1098
|
+
function createNativeTsgoArgs(options, leading = []) {
|
|
1063
1099
|
const passthrough = [
|
|
1100
|
+
// Ahead of the user's own flags, so the same precedence holds here as on
|
|
1101
|
+
// the direct tsgo lane: whatever the user forwarded wins.
|
|
1102
|
+
...leading,
|
|
1064
1103
|
...(nativeTsgoPassthroughArgs(options) ?? []),
|
|
1065
1104
|
...isolatedTsgoOutputArgs(options),
|
|
1066
1105
|
];
|