typia 14.0.0 → 14.0.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.
Files changed (41) hide show
  1. package/lib/internal/_createStandardSchema.d.ts +1 -2
  2. package/lib/internal/_createStandardSchema.js.map +1 -1
  3. package/lib/internal/_createStandardSchema.mjs.map +1 -1
  4. package/lib/module.d.ts +1 -2
  5. package/lib/module.js.map +1 -1
  6. package/lib/module.mjs.map +1 -1
  7. package/lib/re-exports.d.ts +1 -1
  8. package/lib/re-exports.js.map +1 -1
  9. package/lib/transformers/NoTransformConfigurationError.js +19 -4
  10. package/lib/transformers/NoTransformConfigurationError.js.map +1 -1
  11. package/lib/transformers/NoTransformConfigurationError.mjs +12 -6
  12. package/lib/transformers/NoTransformConfigurationError.mjs.map +1 -1
  13. package/native/cmd/ttsc-typia/create_assert_error_factory_arity_test.go +5 -2
  14. package/native/cmd/ttsc-typia/dependency_collector_virtual_scheme_test.go +52 -0
  15. package/native/cmd/ttsc-typia/json_schema_nonsensible_intersection_diagnostic_test.go +80 -0
  16. package/native/cmd/ttsc-typia/project_dependencies_callee_argument_transform_test.go +177 -0
  17. package/native/cmd/ttsc-typia/project_dependencies_callee_barrel_transform_test.go +115 -0
  18. package/native/cmd/ttsc-typia/project_dependencies_callee_inference_transform_test.go +285 -0
  19. package/native/cmd/ttsc-typia/project_dependencies_callee_shape_transform_test.go +117 -0
  20. package/native/cmd/ttsc-typia/project_dependencies_callee_untransformed_barrel_transform_test.go +121 -0
  21. package/native/cmd/ttsc-typia/project_dependencies_complete_diagnostic_transform_test.go +116 -0
  22. package/native/cmd/ttsc-typia/project_dependencies_complete_envelope_transform_test.go +115 -0
  23. package/native/cmd/ttsc-typia/project_dependencies_complete_inferred_type_transform_test.go +127 -0
  24. package/native/cmd/ttsc-typia/project_dependencies_complete_replaced_library_transform_test.go +153 -0
  25. package/native/cmd/ttsc-typia/project_dependencies_complete_untouched_reprint_transform_test.go +155 -0
  26. package/native/cmd/ttsc-typia/project_dependencies_computed_key_transform_test.go +218 -0
  27. package/native/cmd/ttsc-typia/project_dependencies_custom_lib_name_transform_test.go +6 -5
  28. package/native/cmd/ttsc-typia/project_dependencies_enum_member_value_transform_test.go +131 -0
  29. package/native/cmd/ttsc-typia/project_dependencies_inferred_declaration_transform_test.go +159 -0
  30. package/native/cmd/ttsc-typia/project_dependencies_jsdoc_typedef_transform_test.go +134 -0
  31. package/native/cmd/ttsc-typia/project_dependencies_named_tuple_member_transform_test.go +123 -0
  32. package/native/cmd/ttsc-typia/project_dependencies_qualified_barrel_transform_test.go +141 -0
  33. package/native/cmd/ttsc-typia/project_dependencies_type_parameter_default_transform_test.go +127 -0
  34. package/native/cmd/ttsc-typia/transform.go +193 -19
  35. package/native/core/schemas/metadata/MetadataDependency.go +613 -30
  36. package/native/transform/CallExpressionTransformer.go +16 -1
  37. package/package.json +4 -5
  38. package/src/internal/_createStandardSchema.ts +1 -2
  39. package/src/module.ts +6 -2
  40. package/src/re-exports.ts +13 -0
  41. package/src/transformers/NoTransformConfigurationError.ts +19 -4
@@ -22,16 +22,61 @@ import (
22
22
  // modes register none).
23
23
  var metadataDependency_listeners sync.Map
24
24
 
25
+ // MetadataDependency_IListener is what a registered host receives while one
26
+ // file is transformed. `File` is the report; `Unbounded` is the admission that
27
+ // the report cannot be the whole story.
28
+ type MetadataDependency_IListener struct {
29
+ // File receives each consulted declaration file name.
30
+ File func(fileName string)
31
+ // Callee receives a declaration file name reached while deciding WHICH
32
+ // declaration a call resolves to, rather than what a consulted type is built
33
+ // from.
34
+ //
35
+ // The two differ on default libraries alone, and only once the libraries are
36
+ // real files (`libReplacement`, `noembed`). A replacement really can change a
37
+ // generated validator, so the type channel withholds the file that consulted
38
+ // one. It can never change which declaration a call resolves to: typia
39
+ // recognizes its own calls by a path ending in `/typia/lib/<file>.d.ts` or
40
+ // `/typia/src/<file>.ts`, and a file the compiler classifies as a default
41
+ // library satisfies neither, whatever its path on disk. Withholding there
42
+ // costs a file its declaration for calling `table.get(...)` and, under
43
+ // `noembed`, for calling anything at all (samchon/typia#2361).
44
+ //
45
+ // A host that does not distinguish them may leave this nil; the registry
46
+ // falls back to File.
47
+ Callee func(fileName string)
48
+ // Unbounded reports that the analysis consulted something no file list can
49
+ // bound: a declaration whose type this package can only read from an
50
+ // inferred position, or a callee whose identity is decided by an expression
51
+ // that names nothing.
52
+ //
53
+ // A host that only widens invalidation may ignore it, because an unreported
54
+ // file is still watched through the reference closure. A host that narrows to
55
+ // the reported list cannot: it has to leave the file out of that narrowing,
56
+ // since the list it would vouch for is missing an input nobody can enumerate.
57
+ Unbounded func()
58
+ }
59
+
25
60
  // MetadataDependency_listen registers the listener that receives every
26
61
  // consulted declaration file name resolved through `checker`. The caller owns
27
62
  // attribution (which transformed file the touches belong to) and filtering.
28
- func MetadataDependency_listen(checker *nativechecker.Checker, listener func(fileName string)) {
29
- if checker == nil || listener == nil {
63
+ func MetadataDependency_listen(checker *nativechecker.Checker, listener MetadataDependency_IListener) {
64
+ if checker == nil || listener.File == nil {
30
65
  return
31
66
  }
67
+ if listener.Callee == nil {
68
+ listener.Callee = listener.File
69
+ }
32
70
  metadataDependency_listeners.Store(checker, listener)
33
71
  }
34
72
 
73
+ // MetadataDependency_unbounded raises the admission from outside this package.
74
+ // The transform raises it for a typia call that takes its validated type from
75
+ // the value argument, which no written type node bounds.
76
+ func MetadataDependency_unbounded(checker *nativechecker.Checker) {
77
+ metadataDependency_listener(checker).unbounded()
78
+ }
79
+
35
80
  // MetadataDependency_release removes the listener registered for `checker`.
36
81
  func MetadataDependency_release(checker *nativechecker.Checker) {
37
82
  if checker == nil {
@@ -43,7 +88,7 @@ func MetadataDependency_release(checker *nativechecker.Checker) {
43
88
  // MetadataDependency_active reports whether a listener is registered for
44
89
  // `checker`, so analysis code can skip walks performed only for collection.
45
90
  func MetadataDependency_active(checker *nativechecker.Checker) bool {
46
- return metadataDependency_listener(checker) != nil
91
+ return metadataDependency_listener(checker).active()
47
92
  }
48
93
 
49
94
  // MetadataDependency_touchType reports the declaration files of a consulted
@@ -54,7 +99,7 @@ func MetadataDependency_touchType(checker *nativechecker.Checker, typ *nativeche
54
99
  return
55
100
  }
56
101
  listener := metadataDependency_listener(checker)
57
- if listener == nil {
102
+ if listener.active() == false {
58
103
  return
59
104
  }
60
105
  visited := map[*nativeast.Symbol]bool{}
@@ -66,7 +111,7 @@ func MetadataDependency_touchType(checker *nativechecker.Checker, typ *nativeche
66
111
  // symbol (e.g. an object property or a heritage target).
67
112
  func MetadataDependency_touchSymbol(checker *nativechecker.Checker, symbol *nativeast.Symbol) {
68
113
  listener := metadataDependency_listener(checker)
69
- if listener == nil {
114
+ if listener.active() == false {
70
115
  return
71
116
  }
72
117
  metadataDependency_touchVisited(checker, listener, symbol, map[*nativeast.Symbol]bool{})
@@ -80,36 +125,235 @@ func MetadataDependency_touchSymbol(checker *nativechecker.Checker, symbol *nati
80
125
  // symbol — so only the written reference can register the alias' own file.
81
126
  func MetadataDependency_touchTypeNode(checker *nativechecker.Checker, node *nativeast.Node) {
82
127
  listener := metadataDependency_listener(checker)
83
- if listener == nil || node == nil {
128
+ if listener.active() == false || node == nil {
84
129
  return
85
130
  }
86
131
  metadataDependency_walkNode(checker, listener, node, map[*nativeast.Symbol]bool{})
87
132
  }
88
133
 
89
- func metadataDependency_listener(checker *nativechecker.Checker) func(fileName string) {
134
+ // MetadataDependency_touchCallee reports the files that decide WHICH
135
+ // declaration a call expression's callee names.
136
+ //
137
+ // typia recognizes its own call sites by the source file that declares the
138
+ // resolved signature, so every module an import or re-export chain passes
139
+ // through chooses whether a call is rewritten at all. Re-pointing a barrel from
140
+ // `export { is } from "./local"` to `export { is } from "typia"` turns an
141
+ // untouched file into a generated validator, and the reverse turns a validator
142
+ // back into a plain call. The type-graph touches cannot see either: they report
143
+ // what the validated type is, never what selected the callee.
144
+ //
145
+ // This runs for every call expression the transformer examines rather than only
146
+ // for recognized typia calls, because a call that is not typia's today is
147
+ // exactly the one an edit to one of those files makes typia's tomorrow. A
148
+ // callee whose identity no name in it decides raises the unbounded admission.
149
+ func MetadataDependency_touchCallee(checker *nativechecker.Checker, callee *nativeast.Node) {
150
+ listener := metadataDependency_listener(checker)
151
+ if listener.active() == false || callee == nil {
152
+ return
153
+ }
154
+ if metadataDependency_walkCallee(checker, listener, callee, false) == false {
155
+ listener.unbounded()
156
+ }
157
+ }
158
+
159
+ // metadataDependency_walkCallee reports every name written in a callee and
160
+ // answers whether those names decide which declaration the call resolves to.
161
+ //
162
+ // Two shapes are treated specially, and both are about where a call's identity
163
+ // actually comes from:
164
+ //
165
+ // - A nested call contributes its own callee AND the names it is applied to,
166
+ // but only the callee decides boundedness. Every indirection a caller writes
167
+ // -- a factory, a barrel, a namespace alias, a local binding -- sits on the
168
+ // callee chain, while a generic pass-through carries identity through the
169
+ // argument instead (`pick(ns).is<T>(x)`), so both are reported;
170
+ // metadataDependency_touchApplied says why the applied walk's answer is
171
+ // dropped. Reporting the names is not the whole answer there:
172
+ // metadataDependency_calleeBounded says why a callable reached inside a
173
+ // nested call must also write its return type. What stays given up is an
174
+ // overload selected by an argument's TYPE, or by a borrowed VALUE, since no
175
+ // written name on the chain carries either.
176
+ // - A function literal is where the walk stops. Written directly in callee
177
+ // position it is the most bounded declaration there is: the call resolves to
178
+ // the literal itself, in this very file, and its body is nobody's input.
179
+ // Reached through a nested call (`(() => is)()<T>(x)`) it is the opposite:
180
+ // the identity is whatever its body returns, and no set of files describes
181
+ // that, so the walk reports unbounded instead of guessing.
182
+ //
183
+ // Every other expression composes sub-expressions, so its children are walked
184
+ // and each name in them reported.
185
+ func metadataDependency_walkCallee(
186
+ checker *nativechecker.Checker,
187
+ listener MetadataDependency_IListener,
188
+ node *nativeast.Node,
189
+ nested bool,
190
+ ) bool {
191
+ if node == nil {
192
+ return false
193
+ }
194
+ switch node.Kind {
195
+ case nativeast.KindArrowFunction,
196
+ nativeast.KindFunctionExpression,
197
+ nativeast.KindFunctionDeclaration,
198
+ nativeast.KindClassExpression,
199
+ nativeast.KindClassDeclaration,
200
+ nativeast.KindMethodDeclaration,
201
+ nativeast.KindGetAccessor,
202
+ nativeast.KindSetAccessor,
203
+ nativeast.KindConstructor:
204
+ return nested == false
205
+ case nativeast.KindCallExpression,
206
+ nativeast.KindNewExpression:
207
+ metadataDependency_touchApplied(checker, listener, node.Arguments())
208
+ return metadataDependency_walkCallee(checker, listener, node.Expression(), true)
209
+ case nativeast.KindTaggedTemplateExpression:
210
+ tagged := node.AsTaggedTemplateExpression()
211
+ metadataDependency_touchApplied(checker, listener, []*nativeast.Node{tagged.Template})
212
+ return metadataDependency_walkCallee(checker, listener, tagged.Tag, true)
213
+ case nativeast.KindIdentifier,
214
+ nativeast.KindPrivateIdentifier,
215
+ nativeast.KindThisKeyword,
216
+ nativeast.KindSuperKeyword,
217
+ nativeast.KindPropertyAccessExpression,
218
+ nativeast.KindElementAccessExpression,
219
+ nativeast.KindQualifiedName:
220
+ // The whole access resolves to the member the call names; walking on to its
221
+ // qualifier reports whatever selected the object that publishes it, and a
222
+ // module other than the caller may declare that (`helpers.is`).
223
+ metadataDependency_touchDeclarations(checker, listener, node, nested)
224
+ }
225
+ bounded := true
226
+ node.ForEachChild(func(child *nativeast.Node) bool {
227
+ if metadataDependency_walkCallee(checker, listener, child, nested) == false {
228
+ bounded = false
229
+ }
230
+ return false
231
+ })
232
+ return bounded
233
+ }
234
+
235
+ // metadataDependency_touchApplied reports the names a nested call is applied to,
236
+ // without letting their shape decide the caller's boundedness.
237
+ //
238
+ // A generic pass-through carries a callee's identity through what it is applied
239
+ // to, not through its own name: `pick(ns).is<T>(x)` resolves to typia's `is`
240
+ // because `ns` holds the typia namespace, so the file declaring `ns` chooses
241
+ // whether this call is rewritten at all -- and the callee chain alone never
242
+ // names it. Reproduced: `main.ts` was declared complete reporting only `foo.ts`
243
+ // and `pick.ts` (samchon/typia#2357).
244
+ //
245
+ // The walk's answer is deliberately dropped rather than combined. An argument's
246
+ // shape is not the caller's identity: a function literal handed to
247
+ // `arr.map(...).filter(...)` would otherwise withhold every file that chains an
248
+ // array method, for a body that decides nothing about which `filter` this is.
249
+ // Dropping it also keeps the walk's own boundary -- it stops at a function
250
+ // literal instead of descending -- so no file is charged with the identifiers
251
+ // inside a callback.
252
+ //
253
+ // What stays unreported is the same boundary the callee chain already states: an
254
+ // argument that is itself computed (`pick(getNs())`) names its own callee and no
255
+ // further. That one no longer over-declares, because the callee it names is a
256
+ // callable whose inferred return type withholds the caller
257
+ // (metadataDependency_calleeBounded); an overload selected by an argument's
258
+ // TYPE, or by a borrowed VALUE, is still decided by something no written name
259
+ // carries.
260
+ func metadataDependency_touchApplied(
261
+ checker *nativechecker.Checker,
262
+ listener MetadataDependency_IListener,
263
+ applied []*nativeast.Node,
264
+ ) {
265
+ for _, node := range applied {
266
+ metadataDependency_walkCallee(checker, listener, node, true)
267
+ }
268
+ }
269
+
270
+ // metadataDependency_touchDeclarations reports one written reference's own
271
+ // declaring files, following alias hops but without walking the type surface of
272
+ // what it names. The caller asks which declaration a name selects, not what
273
+ // that declaration's type is built from; the type graph reports the latter
274
+ // where it is actually consulted.
275
+ //
276
+ // `nested` says the reference sits inside a nested call, and there the question
277
+ // changes. The outermost callee's identity IS the declaration this resolves to,
278
+ // whose file is reported on the line above, so how that declaration was written
279
+ // cannot matter. One call in, the identity is the inner call's RETURN TYPE, and
280
+ // an unbounded declaration -- `const getNs = () => ns`, `const FLAG = RAW` --
281
+ // carries it from a file no written name on this chain reaches. Reporting the
282
+ // names without admitting that declared three shapes complete while an edit to
283
+ // an unreported file deleted the generated validator outright
284
+ // (samchon/typia#2360).
285
+ func metadataDependency_touchDeclarations(
286
+ checker *nativechecker.Checker,
287
+ listener MetadataDependency_IListener,
288
+ reference *nativeast.Node,
289
+ nested bool,
290
+ ) {
90
291
  if checker == nil {
91
- return nil
292
+ return
293
+ }
294
+ symbol := checker.GetSymbolAtLocation(reference)
295
+ if symbol == nil {
296
+ return
297
+ }
298
+ // Everything this function reaches was reached to decide which declaration a
299
+ // name selects, so all of it belongs to the identity channel -- the alias hops
300
+ // as much as the terminus.
301
+ report := listener.Callee
302
+ if symbol.Flags&nativeast.SymbolFlagsAlias != 0 {
303
+ metadataDependency_touchPath(checker, listener, symbol, report)
304
+ if resolved := nativechecker.Checker_getAliasedSymbol(checker, symbol); resolved != nil {
305
+ symbol = resolved
306
+ }
307
+ }
308
+ for _, declaration := range symbol.Declarations {
309
+ if src := nativeast.GetSourceFileOfNode(declaration); src != nil {
310
+ report(src.FileName())
311
+ }
312
+ if nested && metadataDependency_calleeBounded(declaration) == false {
313
+ listener.unbounded()
314
+ }
315
+ }
316
+ }
317
+
318
+ func metadataDependency_listener(checker *nativechecker.Checker) MetadataDependency_IListener {
319
+ if checker == nil {
320
+ return MetadataDependency_IListener{}
92
321
  }
93
322
  value, ok := metadataDependency_listeners.Load(checker)
94
323
  if ok == false {
95
- return nil
324
+ return MetadataDependency_IListener{}
96
325
  }
97
- listener, ok := value.(func(fileName string))
326
+ listener, ok := value.(MetadataDependency_IListener)
98
327
  if ok == false {
99
- return nil
328
+ return MetadataDependency_IListener{}
100
329
  }
101
330
  return listener
102
331
  }
103
332
 
333
+ // active reports whether a host is collecting through this listener.
334
+ func (listener MetadataDependency_IListener) active() bool {
335
+ return listener.File != nil
336
+ }
337
+
338
+ // unbounded raises the admission when the registered host asked for it, so
339
+ // every call site can report unconditionally instead of guarding twice.
340
+ func (listener MetadataDependency_IListener) unbounded() {
341
+ if listener.Unbounded != nil {
342
+ listener.Unbounded()
343
+ }
344
+ }
345
+
104
346
  // metadataDependency_touchVisited reports the symbol's declaration files and
105
347
  // walks the type references written inside declarations that carry type
106
348
  // annotations (aliases, properties, methods, parameters, index signatures), so
107
349
  // checker-collapsed references — an alias of an intrinsic consumed through a
108
- // property or another alias — still register their declaring files. `visited`
109
- // guards alias cycles and bounds repeated work within one touch.
350
+ // property or another alias — still register their declaring files. A
351
+ // declaration whose type is not written on it raises the unbounded admission
352
+ // instead. `visited` guards alias cycles and bounds repeated work within one
353
+ // touch.
110
354
  func metadataDependency_touchVisited(
111
355
  checker *nativechecker.Checker,
112
- listener func(fileName string),
356
+ listener MetadataDependency_IListener,
113
357
  symbol *nativeast.Symbol,
114
358
  visited map[*nativeast.Symbol]bool,
115
359
  ) {
@@ -122,25 +366,309 @@ func metadataDependency_touchVisited(
122
366
  continue
123
367
  }
124
368
  if src := nativeast.GetSourceFileOfNode(declaration); src != nil {
125
- listener(src.FileName())
369
+ listener.File(src.FileName())
126
370
  }
371
+ if metadataDependency_bounded(declaration) == false {
372
+ listener.unbounded()
373
+ }
374
+ metadataDependency_touchName(checker, listener, declaration, visited)
127
375
  for _, surface := range metadataDependency_typeSurface(declaration) {
128
376
  metadataDependency_walkNode(checker, listener, surface, visited)
129
377
  }
130
378
  }
131
379
  }
132
380
 
381
+ // metadataDependency_touchName reports the files that decide a member's KEY
382
+ // when the key is computed.
383
+ //
384
+ // A validator indexes the property by name — `interface Doc { [Kind.A]: number
385
+ // }` emits `input.alpha` — so the enum member's value is read into the output
386
+ // exactly the way an `enum` member's own value is, and it is written in
387
+ // whatever file declares it. The declaration's written type says nothing about
388
+ // it, and neither does the type graph: the resolved key is an interned string
389
+ // literal with no symbol to follow. Renaming `Kind.A` therefore changed the
390
+ // generated validator with no reported edge for a bundler cache to invalidate
391
+ // (samchon/typia#2126, samchon/typia#2357).
392
+ //
393
+ // Only a computed name needs this. An identifier or string-literal member name
394
+ // is written on the declaration itself, and its file is already reported.
395
+ func metadataDependency_touchName(
396
+ checker *nativechecker.Checker,
397
+ listener MetadataDependency_IListener,
398
+ declaration *nativeast.Node,
399
+ visited map[*nativeast.Symbol]bool,
400
+ ) {
401
+ name := declaration.Name()
402
+ if name == nil || name.Kind != nativeast.KindComputedPropertyName {
403
+ return
404
+ }
405
+ metadataDependency_walkName(checker, listener, name.Expression(), visited)
406
+ }
407
+
408
+ // metadataDependency_walkName reports the declarations a computed key's
409
+ // expression names, and admits when nothing written bounds it.
410
+ //
411
+ // A literal key needs no report: `["alpha"]` is written here, so no other file
412
+ // decides it. Every other shape is a reference, and resolving it through
413
+ // metadataDependency_touchVisited is what makes the report transitive: the
414
+ // symbol's own declaration is reported, its alias path registers the barrel it
415
+ // traveled, and metadataDependency_bounded answers whether that declaration
416
+ // fixes the value or borrows it — `const KEY = "gamma"` is bounded while `const
417
+ // KEY = other.VALUE` is not, exactly as for an enum member.
418
+ //
419
+ // The qualifier is walked as well as the whole reference. `Kind.A` resolves
420
+ // straight to the enum member, which reports the file declaring it but not the
421
+ // modules the reference traveled to select `Kind`; those are what a re-pointed
422
+ // barrel changes.
423
+ //
424
+ // Every other expression raises the unbounded admission rather than a guess: a
425
+ // call or a substituted template takes its value from evaluation, which no file
426
+ // list bounds. Only the kinds a property enumeration actually delivers are
427
+ // named: a string, no-substitution template, or numeric literal key, a bare
428
+ // name, and a property access each declare a property, while a parenthesized
429
+ // reference, a bigint literal, and an element access declare none at all -- so
430
+ // the walk never sees those, and refusing to vouch for a shape nobody has
431
+ // exhibited costs nothing.
432
+ func metadataDependency_walkName(
433
+ checker *nativechecker.Checker,
434
+ listener MetadataDependency_IListener,
435
+ expression *nativeast.Node,
436
+ visited map[*nativeast.Symbol]bool,
437
+ ) {
438
+ if expression == nil {
439
+ listener.unbounded()
440
+ return
441
+ }
442
+ switch expression.Kind {
443
+ case nativeast.KindStringLiteral,
444
+ nativeast.KindNoSubstitutionTemplateLiteral,
445
+ nativeast.KindNumericLiteral:
446
+ return
447
+ case nativeast.KindIdentifier,
448
+ nativeast.KindPropertyAccessExpression:
449
+ // A reference the checker cannot resolve takes the fall-through below
450
+ // rather than an exit of its own. Nothing reaches it: a name resolving to
451
+ // no symbol declares no property symbol either -- the member becomes a
452
+ // dynamic key instead -- and this walk only ever runs on a declaration the
453
+ // property enumeration already handed over.
454
+ if symbol := metadataDependency_resolve(checker, listener, expression); symbol != nil {
455
+ metadataDependency_touchVisited(checker, listener, symbol, visited)
456
+ metadataDependency_touchQualifier(checker, listener, expression, visited)
457
+ return
458
+ }
459
+ }
460
+ listener.unbounded()
461
+ }
462
+
463
+ // metadataDependency_calleeBounded reports whether a declaration reached inside
464
+ // a nested call pins the identity of what that call RETURNS.
465
+ //
466
+ // A nested call's identity is its return type, so the question here is only
467
+ // ever asked of callables -- and of a variable whose initializer is one, which
468
+ // is the same declaration spelled differently. A callable pins the identity
469
+ // when its return type is written; when it is inferred, the identity comes from
470
+ // whatever the body happens to return, which lives in a file no name on the
471
+ // callee chain reaches. `const getNs = () => ns` and `function getNs() { return
472
+ // ns; }` are that shape, and both declared their caller complete while an edit
473
+ // to the unreported `ns.ts` deleted the generated validator outright
474
+ // (samchon/typia#2360).
475
+ //
476
+ // Everything else stays bounded on purpose, and metadataDependency_bounded is
477
+ // deliberately NOT consulted here. It answers a different question -- whether a
478
+ // declaration's TYPE is written where the type walk can read it -- and it calls
479
+ // `const ns = typia` unbounded for want of an annotation. On this channel that
480
+ // declaration is fine: the walk writes the name `ns`, so `ns.ts` is reported
481
+ // and an edit to it invalidates the caller. Withholding on it would take back
482
+ // samchon/typia#2357, whose whole point is that such a caller stays declared.
483
+ //
484
+ // What that leaves open is an overload selected by a borrowed VALUE (`const
485
+ // FLAG = RAW; sel(FLAG).is<T>(x)`): `flag.ts` is reported, `rawflag.ts` is not,
486
+ // and the two are structurally indistinguishable from the pair above.
487
+ // Separating them needs the initializer chain followed and reported, not a
488
+ // boundedness answer.
489
+ func metadataDependency_calleeBounded(declaration *nativeast.Node) bool {
490
+ if metadataDependency_calleeCallable(declaration) {
491
+ return declaration.Type() != nil
492
+ }
493
+ if declaration.Kind == nativeast.KindVariableDeclaration {
494
+ if initializer := declaration.Initializer(); initializer != nil &&
495
+ metadataDependency_calleeCallable(initializer) {
496
+ return declaration.Type() != nil || initializer.Type() != nil
497
+ }
498
+ }
499
+ return true
500
+ }
501
+
502
+ // metadataDependency_calleeCallable reports whether a node is a callable whose
503
+ // written return type would pin what a call on it produces.
504
+ //
505
+ // A construct signature is absent on purpose: `new X()` returns `X`, and the
506
+ // class is the declaration the walk already reports.
507
+ func metadataDependency_calleeCallable(node *nativeast.Node) bool {
508
+ switch node.Kind {
509
+ case nativeast.KindFunctionDeclaration,
510
+ nativeast.KindFunctionExpression,
511
+ nativeast.KindArrowFunction,
512
+ nativeast.KindMethodDeclaration,
513
+ nativeast.KindMethodSignature,
514
+ nativeast.KindCallSignature,
515
+ nativeast.KindFunctionType,
516
+ nativeast.KindGetAccessor:
517
+ return true
518
+ }
519
+ return false
520
+ }
521
+
522
+ // metadataDependency_bounded reports whether a declaration's type is written
523
+ // where this walk can read it. A declaration that fails it raises the unbounded
524
+ // admission, which costs the file being transformed its completeness
525
+ // declaration and nothing else.
526
+ //
527
+ // The answer is NO for every kind this switch does not name, and that direction
528
+ // is the whole point. Under-reporting a file is safe -- it keeps the host-owned
529
+ // reference closure -- while declaring a file whose inputs were not enumerated
530
+ // serves a stale validator, so a declaration kind nobody thought about has to
531
+ // cost narrowing rather than correctness.
532
+ //
533
+ // What makes a kind bounded is that its type is either written on it, surfaced
534
+ // by metadataDependency_typeSurface, or absent entirely (a module, an alias
535
+ // hop, an enum's own declaration). What makes one unbounded is inference: an
536
+ // object-literal member and a destructured binding take their type from an
537
+ // expression, and so does a property, parameter, or variable with no
538
+ // annotation. Following that means following type inference over arbitrary
539
+ // expressions -- a call's return type, an overload's selection, a conditional's
540
+ // branches, a contextually typed parameter -- which is the same problem a typia
541
+ // call with no written type argument has, and it gets the same answer
542
+ // (samchon/typia#2126, samchon/typia#2357).
543
+ //
544
+ // Only kinds an input actually reaches are named. A tuple element and a JSDoc
545
+ // `@property` / `@param` tag are not: each is reached through the written node
546
+ // that spells it -- the alias or literal spelling the tuple, the typedef owning
547
+ // the tag -- which metadataDependency_walkNode descends whole, so no property
548
+ // enumeration ever hands one of them here. Naming them changed no envelope in
549
+ // either direction, which is the only evidence that settles it;
550
+ // TestProjectDependenciesNamedTupleMemberTransform and
551
+ // TestProjectDependenciesJsDocTypedefTransform are the fixtures that measured
552
+ // it and would fail if either kind started arriving.
553
+ func metadataDependency_bounded(declaration *nativeast.Node) bool {
554
+ switch declaration.Kind {
555
+ case nativeast.KindTypeAliasDeclaration,
556
+ nativeast.KindJSTypeAliasDeclaration,
557
+ nativeast.KindInterfaceDeclaration,
558
+ nativeast.KindClassDeclaration,
559
+ nativeast.KindClassExpression,
560
+ nativeast.KindTypeLiteral,
561
+ nativeast.KindMappedType,
562
+ nativeast.KindTypeParameter,
563
+ nativeast.KindEnumDeclaration:
564
+ // Structural declarations: metadataDependency_typeSurface hands their
565
+ // written types to the walk, and their members surface individually.
566
+ return true
567
+ case nativeast.KindMethodSignature,
568
+ nativeast.KindMethodDeclaration,
569
+ nativeast.KindGetAccessor,
570
+ nativeast.KindSetAccessor,
571
+ nativeast.KindIndexSignature,
572
+ nativeast.KindCallSignature,
573
+ nativeast.KindConstructSignature,
574
+ nativeast.KindFunctionType,
575
+ nativeast.KindConstructorType,
576
+ nativeast.KindFunctionDeclaration,
577
+ nativeast.KindFunctionExpression,
578
+ nativeast.KindArrowFunction,
579
+ nativeast.KindConstructor,
580
+ nativeast.KindClassStaticBlockDeclaration:
581
+ // Callables. An analysis emits one as `typeof x === "function"` and reads
582
+ // no part of its signature, which is the contract
583
+ // TestProjectDependenciesSignatureAliasTransform states, so an inferred
584
+ // return type is not an input either.
585
+ return true
586
+ case nativeast.KindSourceFile,
587
+ nativeast.KindModuleDeclaration,
588
+ nativeast.KindNamespaceExportDeclaration,
589
+ nativeast.KindImportDeclaration,
590
+ nativeast.KindImportClause,
591
+ nativeast.KindImportSpecifier,
592
+ nativeast.KindImportEqualsDeclaration,
593
+ nativeast.KindNamespaceImport,
594
+ nativeast.KindNamespaceExport,
595
+ nativeast.KindExportDeclaration,
596
+ nativeast.KindExportSpecifier,
597
+ nativeast.KindExportAssignment:
598
+ // Modules and alias hops carry no type of their own; the hop itself is
599
+ // reported by metadataDependency_touchPath and the terminus is resolved
600
+ // separately.
601
+ return true
602
+ case nativeast.KindPropertySignature,
603
+ nativeast.KindPropertyDeclaration,
604
+ nativeast.KindParameter,
605
+ nativeast.KindVariableDeclaration:
606
+ return declaration.Type() != nil || metadataDependency_literal(declaration.Initializer())
607
+ case nativeast.KindEnumMember,
608
+ nativeast.KindPropertyAssignment:
609
+ // An enum member's value is read straight into the generated validator, and
610
+ // an object-literal member's type is its initializer. Both are written here
611
+ // when the initializer is a literal or, for an auto-numbered enum member,
612
+ // absent; anything else names a value some other file may declare.
613
+ initializer := declaration.Initializer()
614
+ return initializer == nil || metadataDependency_literal(initializer)
615
+ }
616
+ return false
617
+ }
618
+
619
+ // metadataDependency_literal reports an initializer that fixes a declaration's
620
+ // type inside the declaration itself, so no other file can decide it.
621
+ func metadataDependency_literal(initializer *nativeast.Node) bool {
622
+ if initializer == nil {
623
+ return false
624
+ }
625
+ switch initializer.Kind {
626
+ case nativeast.KindNumericLiteral,
627
+ nativeast.KindBigIntLiteral,
628
+ nativeast.KindStringLiteral,
629
+ nativeast.KindNoSubstitutionTemplateLiteral,
630
+ nativeast.KindRegularExpressionLiteral,
631
+ nativeast.KindTrueKeyword,
632
+ nativeast.KindFalseKeyword,
633
+ nativeast.KindNullKeyword:
634
+ return true
635
+ case nativeast.KindParenthesizedExpression:
636
+ return metadataDependency_literal(initializer.Expression())
637
+ case nativeast.KindPrefixUnaryExpression:
638
+ // `-1` is how a negative number is written, and TypeScript reads the sign
639
+ // and the literal as one constant. Only the numeric operators qualify: `!x`
640
+ // and `~x` say nothing about where x came from.
641
+ unary := initializer.AsPrefixUnaryExpression()
642
+ switch unary.Operator {
643
+ case nativeast.KindMinusToken, nativeast.KindPlusToken:
644
+ switch unary.Operand.Kind {
645
+ case nativeast.KindNumericLiteral, nativeast.KindBigIntLiteral:
646
+ return true
647
+ }
648
+ }
649
+ }
650
+ return false
651
+ }
652
+
133
653
  // metadataDependency_typeSurface selects the WRITTEN type nodes of a
134
- // declaration: the whole node for a `type` alias (type parameters + aliased
135
- // type), the annotation for properties and parameters, and parameter types +
136
- // return type for methods, accessors, and index signatures. Bodies and
137
- // initializers are excluded a reference appearing only there is not part of
138
- // the type the analysis consulted. Interface, class, and type-literal
139
- // declarations contribute only their index signatures: every other member
140
- // surfaces individually through property enumeration.
654
+ // declaration: the whole node for a `type` alias, a mapped type, or a type
655
+ // parameter (constraint and default included), the type parameters and index
656
+ // signatures of an interface or class, the annotation for properties,
657
+ // parameters, and variables, and parameter types + return type for methods,
658
+ // accessors, and index signatures. Bodies and initializers are excluded — a
659
+ // reference appearing only there is not part of the type the analysis consulted,
660
+ // and a declaration that has only an initializer to go on is reported unbounded
661
+ // by metadataDependency_bounded rather than guessed at.
662
+ //
663
+ // Interface, class, and type-literal declarations contribute only their index
664
+ // signatures: every other member surfaces individually through property
665
+ // enumeration.
141
666
  func metadataDependency_typeSurface(declaration *nativeast.Node) []*nativeast.Node {
142
667
  switch declaration.Kind {
143
- case nativeast.KindTypeAliasDeclaration:
668
+ case nativeast.KindTypeAliasDeclaration,
669
+ nativeast.KindJSTypeAliasDeclaration,
670
+ nativeast.KindMappedType,
671
+ nativeast.KindTypeParameter:
144
672
  return []*nativeast.Node{declaration}
145
673
  case nativeast.KindInterfaceDeclaration,
146
674
  nativeast.KindClassDeclaration,
@@ -155,6 +683,18 @@ func metadataDependency_typeSurface(declaration *nativeast.Node) []*nativeast.No
155
683
  // bundler cache to invalidate (samchon/typia#2126). Members are surfaced
156
684
  // through this same selector, which keeps an index signature's own body-
157
685
  // free key/value contract as the boundary.
686
+ //
687
+ // A call or construct signature is symbol-less in the same way and is
688
+ // deliberately NOT surfaced: the analysis emits a callable as `typeof x ===
689
+ // "function"` and never reads its parameter or return types, so registering
690
+ // them would invalidate consumers on edits that cannot change the output.
691
+ // TestProjectDependenciesSignatureAliasTransform is that boundary.
692
+ // A type parameter is not surfaced from here even though the declaration
693
+ // owns the list: a parameter the members use is reached through the member
694
+ // that uses it, and one no member uses is not consulted at all, so appending
695
+ // the list would report an alias behind an unused default. The parameter's
696
+ // own declaration carries its constraint and default, and this same selector
697
+ // surfaces those when the walk arrives there.
158
698
  output := []*nativeast.Node{}
159
699
  for _, member := range declaration.Members() {
160
700
  if member == nil || member.Kind != nativeast.KindIndexSignature {
@@ -165,7 +705,8 @@ func metadataDependency_typeSurface(declaration *nativeast.Node) []*nativeast.No
165
705
  return output
166
706
  case nativeast.KindPropertySignature,
167
707
  nativeast.KindPropertyDeclaration,
168
- nativeast.KindParameter:
708
+ nativeast.KindParameter,
709
+ nativeast.KindVariableDeclaration:
169
710
  if annotation := declaration.Type(); annotation != nil {
170
711
  return []*nativeast.Node{annotation}
171
712
  }
@@ -199,7 +740,7 @@ func metadataDependency_typeSurface(declaration *nativeast.Node) []*nativeast.No
199
740
  // which in turn walks type-alias declarations transitively.
200
741
  func metadataDependency_walkNode(
201
742
  checker *nativechecker.Checker,
202
- listener func(fileName string),
743
+ listener MetadataDependency_IListener,
203
744
  node *nativeast.Node,
204
745
  visited map[*nativeast.Symbol]bool,
205
746
  ) {
@@ -227,6 +768,7 @@ func metadataDependency_walkNode(
227
768
  }
228
769
  if name != nil {
229
770
  metadataDependency_touchVisited(checker, listener, metadataDependency_resolve(checker, listener, name), visited)
771
+ metadataDependency_touchQualifier(checker, listener, name, visited)
230
772
  }
231
773
  node.ForEachChild(func(child *nativeast.Node) bool {
232
774
  metadataDependency_walkNode(checker, listener, child, visited)
@@ -234,6 +776,44 @@ func metadataDependency_walkNode(
234
776
  })
235
777
  }
236
778
 
779
+ // metadataDependency_touchQualifier resolves every prefix of a qualified
780
+ // reference, so the modules that SELECTED the namespace half are reported too.
781
+ //
782
+ // The checker answers a qualified name with its terminus: `Kind.A` hands back
783
+ // the enum member, which is not an alias, so metadataDependency_resolve finds no
784
+ // path to walk and the barrel between the caller and the enum goes unreported --
785
+ // while `Kind` alone would have been an alias and reported it. Re-pointing
786
+ // `export { Kind } from "./kind"` at another enum changes the constant the
787
+ // validator compares against, so that barrel is an input like any other
788
+ // (samchon/typia#2126, samchon/typia#2357).
789
+ //
790
+ // Each prefix is resolved the same way the terminus is, which keeps a nested
791
+ // qualification (`outer.inner.Type`) reported hop by hop and costs nothing for a
792
+ // bare name, whose prefix chain is empty.
793
+ func metadataDependency_touchQualifier(
794
+ checker *nativechecker.Checker,
795
+ listener MetadataDependency_IListener,
796
+ name *nativeast.Node,
797
+ visited map[*nativeast.Symbol]bool,
798
+ ) {
799
+ for qualifier := metadataDependency_left(name); qualifier != nil; qualifier = metadataDependency_left(qualifier) {
800
+ metadataDependency_touchVisited(checker, listener, metadataDependency_resolve(checker, listener, qualifier), visited)
801
+ }
802
+ }
803
+
804
+ // metadataDependency_left returns the namespace half of a qualified reference,
805
+ // in either the type spelling (`A.B`) or the value spelling (`a.b`), and nil for
806
+ // a bare name that qualifies nothing.
807
+ func metadataDependency_left(node *nativeast.Node) *nativeast.Node {
808
+ switch node.Kind {
809
+ case nativeast.KindQualifiedName:
810
+ return node.AsQualifiedName().Left
811
+ case nativeast.KindPropertyAccessExpression:
812
+ return node.Expression()
813
+ }
814
+ return nil
815
+ }
816
+
237
817
  // metadataDependency_resolve resolves a written reference name to its final
238
818
  // symbol, following import aliases so the touched declarations are the real
239
819
  // declaring files rather than the local import specifier. Every module the
@@ -241,7 +821,7 @@ func metadataDependency_walkNode(
241
821
  // SELECT which declaration the terminus is (see metadataDependency_touchPath).
242
822
  func metadataDependency_resolve(
243
823
  checker *nativechecker.Checker,
244
- listener func(fileName string),
824
+ listener MetadataDependency_IListener,
245
825
  name *nativeast.Node,
246
826
  ) *nativeast.Symbol {
247
827
  if checker == nil || name == nil {
@@ -252,7 +832,9 @@ func metadataDependency_resolve(
252
832
  return nil
253
833
  }
254
834
  if symbol.Flags&nativeast.SymbolFlagsAlias != 0 {
255
- metadataDependency_touchPath(checker, listener, symbol)
835
+ // The type walk: a hop reached here was reached because a consulted type is
836
+ // built from what it publishes, so it reports on the consultation channel.
837
+ metadataDependency_touchPath(checker, listener, symbol, listener.File)
256
838
  if resolved := nativechecker.Checker_getAliasedSymbol(checker, symbol); resolved != nil {
257
839
  symbol = resolved
258
840
  }
@@ -275,8 +857,9 @@ func metadataDependency_resolve(
275
857
  // barrel's exports, keeping unrelated siblings out of the envelope.
276
858
  func metadataDependency_touchPath(
277
859
  checker *nativechecker.Checker,
278
- listener func(fileName string),
860
+ listener MetadataDependency_IListener,
279
861
  symbol *nativeast.Symbol,
862
+ report func(fileName string),
280
863
  ) {
281
864
  visited := map[*nativeast.Symbol]bool{}
282
865
  for symbol != nil && symbol.Flags&nativeast.SymbolFlagsAlias != 0 && visited[symbol] == false {
@@ -287,7 +870,7 @@ func metadataDependency_touchPath(
287
870
  }
288
871
  // The file that WRITES this hop (the barrel's own `export ... from` line).
289
872
  if src := nativeast.GetSourceFileOfNode(declaration); src != nil {
290
- listener(src.FileName())
873
+ report(src.FileName())
291
874
  }
292
875
  specifier := metadataDependency_moduleSpecifier(declaration)
293
876
  if specifier == nil {
@@ -308,7 +891,7 @@ func metadataDependency_touchPath(
308
891
  }
309
892
  for _, moduleDeclaration := range module.Declarations {
310
893
  if src := nativeast.GetSourceFileOfNode(moduleDeclaration); src != nil {
311
- listener(src.FileName())
894
+ report(src.FileName())
312
895
  }
313
896
  }
314
897
  symbol = metadataDependency_exported(module, declaration)