@ttsc/paths 0.19.3 → 0.20.1
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/driver/paths.go +63 -25
- package/package.json +1 -1
package/driver/paths.go
CHANGED
|
@@ -6,6 +6,7 @@ import (
|
|
|
6
6
|
"strings"
|
|
7
7
|
|
|
8
8
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
9
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
9
10
|
shimcore "github.com/microsoft/typescript-go/shim/core"
|
|
10
11
|
shimtspath "github.com/microsoft/typescript-go/shim/tspath"
|
|
11
12
|
|
|
@@ -32,12 +33,14 @@ func (plugin) ApplyProgram(prog *driver.Program, _ driver.PluginContext) error {
|
|
|
32
33
|
// rewriter holds the resolved tsconfig paths configuration used to rewrite
|
|
33
34
|
// module specifiers across an entire program.
|
|
34
35
|
type rewriter struct {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
checker *shimchecker.Checker
|
|
37
|
+
basePath string
|
|
38
|
+
canonicalFileName func(string) string
|
|
39
|
+
jsxPreserve bool
|
|
40
|
+
outDir string
|
|
41
|
+
patterns []pathPattern
|
|
42
|
+
rootDir string
|
|
43
|
+
sourceFiles map[string]string // canonical source path → original normalized path
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
// pathPattern is a single tsconfig paths entry with its wildcard pattern and
|
|
@@ -56,10 +59,17 @@ var sourceLookupExtensions = []string{
|
|
|
56
59
|
// Patterns are sorted by decreasing specificity (longer literal prefix first)
|
|
57
60
|
// so the most-specific match wins on overlapping patterns.
|
|
58
61
|
func newRewriter(prog *driver.Program) *rewriter {
|
|
59
|
-
|
|
62
|
+
caseSensitive := useCaseSensitiveFileNames(prog)
|
|
63
|
+
out := &rewriter{
|
|
64
|
+
canonicalFileName: func(name string) string {
|
|
65
|
+
return shimtspath.GetCanonicalFileName(name, caseSensitive)
|
|
66
|
+
},
|
|
67
|
+
sourceFiles: map[string]string{},
|
|
68
|
+
}
|
|
60
69
|
if prog == nil || prog.ParsedConfig == nil || prog.ParsedConfig.ParsedConfig == nil || prog.ParsedConfig.ParsedConfig.CompilerOptions == nil {
|
|
61
70
|
return out
|
|
62
71
|
}
|
|
72
|
+
out.checker = prog.Checker
|
|
63
73
|
options := prog.ParsedConfig.ParsedConfig.CompilerOptions
|
|
64
74
|
cwd := prog.Host.GetCurrentDirectory()
|
|
65
75
|
out.basePath = filepath.Clean(options.GetPathsBasePath(cwd))
|
|
@@ -75,7 +85,7 @@ func newRewriter(prog *driver.Program) *rewriter {
|
|
|
75
85
|
out.rootDir = inferredRootDir(options.ConfigFilePath, fileNames, cwd, useCaseSensitiveFileNames(prog))
|
|
76
86
|
}
|
|
77
87
|
for _, name := range fileNames {
|
|
78
|
-
out.sourceFiles[name] = name
|
|
88
|
+
out.sourceFiles[out.sourceKey(name)] = name
|
|
79
89
|
}
|
|
80
90
|
if options.Paths != nil {
|
|
81
91
|
for key, targets := range options.Paths.Entries() {
|
|
@@ -104,7 +114,7 @@ func (r *rewriter) apply(file *shimast.SourceFile) {
|
|
|
104
114
|
if r == nil || file == nil || len(r.patterns) == 0 {
|
|
105
115
|
return
|
|
106
116
|
}
|
|
107
|
-
visitModuleSpecifiers(file.
|
|
117
|
+
visitModuleSpecifiers(file, r.checker, func(lit *shimast.Node) {
|
|
108
118
|
if lit == nil || lit.Kind != shimast.KindStringLiteral {
|
|
109
119
|
return
|
|
110
120
|
}
|
|
@@ -118,16 +128,16 @@ func (r *rewriter) apply(file *shimast.SourceFile) {
|
|
|
118
128
|
})
|
|
119
129
|
}
|
|
120
130
|
|
|
121
|
-
// visitModuleSpecifiers recursively walks
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
131
|
+
// visitModuleSpecifiers recursively walks file, calling visit for every
|
|
132
|
+
// string-literal module reference it finds. Syntax identifies candidates, then
|
|
133
|
+
// SourceFile and Checker facts exclude declarations and calls whose literal is
|
|
134
|
+
// not actually a module reference in the containing file.
|
|
125
135
|
//
|
|
126
136
|
// The recursion runs through one closure created per walk, not one per node:
|
|
127
137
|
// handing ForEachChild a fresh closure at every node would allocate once per
|
|
128
138
|
// AST node across the whole program on every apply pass.
|
|
129
|
-
func visitModuleSpecifiers(
|
|
130
|
-
if
|
|
139
|
+
func visitModuleSpecifiers(file *shimast.SourceFile, checker *shimchecker.Checker, visit func(*shimast.Node)) {
|
|
140
|
+
if file == nil {
|
|
131
141
|
return
|
|
132
142
|
}
|
|
133
143
|
var walk func(node *shimast.Node) bool
|
|
@@ -152,24 +162,24 @@ func visitModuleSpecifiers(node *shimast.Node, visit func(*shimast.Node)) {
|
|
|
152
162
|
}
|
|
153
163
|
case shimast.KindModuleDeclaration:
|
|
154
164
|
decl := node.AsModuleDeclaration()
|
|
155
|
-
if decl != nil {
|
|
165
|
+
if file.ExternalModuleIndicator != nil && decl != nil {
|
|
156
166
|
visit(decl.Name())
|
|
157
167
|
}
|
|
158
168
|
case shimast.KindCallExpression:
|
|
159
169
|
call := node.AsCallExpression()
|
|
160
|
-
if isModuleSpecifierCall(call) && call.Arguments != nil && len(call.Arguments.Nodes) > 0 {
|
|
170
|
+
if isModuleSpecifierCall(checker, call) && call.Arguments != nil && len(call.Arguments.Nodes) > 0 {
|
|
161
171
|
visit(call.Arguments.Nodes[0])
|
|
162
172
|
}
|
|
163
173
|
}
|
|
164
174
|
node.ForEachChild(walk)
|
|
165
175
|
return false
|
|
166
176
|
}
|
|
167
|
-
walk(
|
|
177
|
+
walk(file.AsNode())
|
|
168
178
|
}
|
|
169
179
|
|
|
170
180
|
// isModuleSpecifierCall reports whether call is a dynamic import() or a
|
|
171
|
-
// CommonJS require() expression.
|
|
172
|
-
func isModuleSpecifierCall(call *shimast.CallExpression) bool {
|
|
181
|
+
// CommonJS require() expression that resolves to the module loader.
|
|
182
|
+
func isModuleSpecifierCall(checker *shimchecker.Checker, call *shimast.CallExpression) bool {
|
|
173
183
|
if call == nil || call.Expression == nil {
|
|
174
184
|
return false
|
|
175
185
|
}
|
|
@@ -177,12 +187,28 @@ func isModuleSpecifierCall(call *shimast.CallExpression) bool {
|
|
|
177
187
|
case shimast.KindImportKeyword:
|
|
178
188
|
return true
|
|
179
189
|
case shimast.KindIdentifier:
|
|
180
|
-
return call.Expression.Text() == "require"
|
|
190
|
+
return call.Expression.Text() == "require" && isModuleLoader(checker, call.Expression)
|
|
181
191
|
default:
|
|
182
192
|
return false
|
|
183
193
|
}
|
|
184
194
|
}
|
|
185
195
|
|
|
196
|
+
// isModuleLoader reports whether expression names the CommonJS loader rather
|
|
197
|
+
// than a parameter, local, or imported binding named require. An unresolved
|
|
198
|
+
// bare require is the runtime CommonJS global; declared loaders are ambient.
|
|
199
|
+
func isModuleLoader(checker *shimchecker.Checker, expression *shimast.Node) bool {
|
|
200
|
+
if checker == nil || expression == nil {
|
|
201
|
+
return false
|
|
202
|
+
}
|
|
203
|
+
symbol := checker.GetSymbolAtLocation(expression)
|
|
204
|
+
if symbol == nil {
|
|
205
|
+
return true
|
|
206
|
+
}
|
|
207
|
+
return symbol.Flags&shimast.SymbolFlagsAlias == 0 &&
|
|
208
|
+
symbol.ValueDeclaration != nil &&
|
|
209
|
+
symbol.ValueDeclaration.Flags&shimast.NodeFlagsAmbient != 0
|
|
210
|
+
}
|
|
211
|
+
|
|
186
212
|
// rewrite resolves specifier from fromSource using the tsconfig paths table and
|
|
187
213
|
// returns the relative output path. Returns (specifier, false) when the specifier
|
|
188
214
|
// is already relative, absolute, or does not match any paths pattern.
|
|
@@ -236,23 +262,35 @@ func (r *rewriter) resolveSource(specifier string) (string, bool) {
|
|
|
236
262
|
// extension) corresponds to a known source file. It tries the exact path, stem
|
|
237
263
|
// with each known TypeScript/JavaScript source extension, and index files.
|
|
238
264
|
func (r *rewriter) lookupSource(candidate string) (string, bool) {
|
|
239
|
-
|
|
265
|
+
normalized := normalizePath(candidate)
|
|
266
|
+
if source, ok := r.sourceFiles[r.sourceKey(normalized)]; ok {
|
|
240
267
|
return source, true
|
|
241
268
|
}
|
|
242
|
-
stem := stripKnownSourceExtension(
|
|
269
|
+
stem := stripKnownSourceExtension(normalized)
|
|
243
270
|
for _, ext := range sourceLookupExtensions {
|
|
244
|
-
if source, ok := r.sourceFiles[stem+ext]; ok {
|
|
271
|
+
if source, ok := r.sourceFiles[r.sourceKey(stem+ext)]; ok {
|
|
245
272
|
return source, true
|
|
246
273
|
}
|
|
247
274
|
}
|
|
248
275
|
for _, ext := range sourceLookupExtensions {
|
|
249
|
-
if source, ok := r.sourceFiles[
|
|
276
|
+
if source, ok := r.sourceFiles[r.sourceKey(filepath.Join(stem, "index"+ext))]; ok {
|
|
250
277
|
return source, true
|
|
251
278
|
}
|
|
252
279
|
}
|
|
253
280
|
return "", false
|
|
254
281
|
}
|
|
255
282
|
|
|
283
|
+
// sourceKey applies the compiler host's filesystem identity rule to one path.
|
|
284
|
+
// Synthetic test rewriters leave canonicalFileName nil and retain the previous
|
|
285
|
+
// exact-key behavior.
|
|
286
|
+
func (r *rewriter) sourceKey(value string) string {
|
|
287
|
+
normalized := normalizePath(value)
|
|
288
|
+
if r.canonicalFileName == nil {
|
|
289
|
+
return normalized
|
|
290
|
+
}
|
|
291
|
+
return r.canonicalFileName(normalized)
|
|
292
|
+
}
|
|
293
|
+
|
|
256
294
|
// outputPathForSource maps a source file path to its emitted output path under
|
|
257
295
|
// outDir, swapping the source extension for the appropriate JS extension. Returns
|
|
258
296
|
// "" when outDir or rootDir is unset, or when source is outside rootDir.
|