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
@@ -103,7 +103,7 @@ func runTransform(args []string) int {
103
103
  fmt.Fprintln(stderr, "ttsc-typia transform: --out requires --file")
104
104
  return 2
105
105
  }
106
- return runTransformProject(prog, cwd, typiaTransform, &transformDiags)
106
+ return runTransformProject(prog, cwd, typiaTransform, &transformDiags, &extras)
107
107
  }
108
108
 
109
109
  absFile := *file
@@ -190,11 +190,25 @@ func runTransformSingle(
190
190
  // dependencies[F]), so graph is added alongside dependencies, not a replacement.
191
191
  // Every section keys through driver.TransformOutputKey, so the consumer joins
192
192
  // them by key.
193
+ //
194
+ // A third section, `dependenciesComplete`, names the files whose `dependencies`
195
+ // entry is the whole story, and for those the consumer stops unioning: it
196
+ // derives dependencies[F] ∪ configs ∪ candidates and drops reach(edges, F) and
197
+ // globals. That is the difference between re-validating the reference closure
198
+ // of a file and re-validating the handful of declarations typia read, paid once
199
+ // per delivered module, and for the majority of a project's files -- the ones
200
+ // with no typia call at all -- between the closure and nothing.
201
+ //
202
+ // The declaration transfers responsibility, so it is made per file and only
203
+ // where the collector could bound the file's inputs; the dependency listener
204
+ // and the diagnostic report this function wires are what decide that
205
+ // (samchon/typia#2357). See transformDependencyCollector.Unbound.
193
206
  func runTransformProject(
194
207
  prog *driver.Program,
195
208
  cwd string,
196
209
  typiaTransform driver.PluginTransform,
197
210
  transformDiags *[]typiaTransformDiagnostic,
211
+ extras *nativecontext.ITypiaContext_Extras,
198
212
  ) int {
199
213
  out := transformProjectOutput{
200
214
  Diagnostics: []transformCompilerDiagnostic{},
@@ -209,8 +223,27 @@ func runTransformProject(
209
223
  sf := prog.SourceFile(fileName)
210
224
  return sf != nil && prog.TSProgram.IsLibFile(sf)
211
225
  })
212
- schemametadata.MetadataDependency_listen(prog.Checker, collector.Touch)
226
+ // Only this host declares completeness, which is why only this host asks the
227
+ // analysis to admit what it could not bound: every such admission costs one
228
+ // file its declaration and nothing else.
229
+ schemametadata.MetadataDependency_listen(prog.Checker, schemametadata.MetadataDependency_IListener{
230
+ File: collector.Touch,
231
+ Callee: collector.TouchCallee,
232
+ Unbounded: collector.Unbound,
233
+ })
213
234
  defer schemametadata.MetadataDependency_release(prog.Checker)
235
+ // A diagnostic means typia could not finish lowering a call in the file being
236
+ // transformed, so what the analysis consulted before giving up is not that
237
+ // file's whole input set: an edit to a declaration it never reached could make
238
+ // the same call succeed and publish different text. The build fails either
239
+ // way, but a false claim is still false, so withhold the declaration rather
240
+ // than reason about how far the analysis got. The transformer reads `extras`
241
+ // on every call, so wrapping it through the pointer reaches the loop below.
242
+ reportDiagnostic := extras.AddDiagnostic
243
+ extras.AddDiagnostic = func(diag *nativecontext.ITypiaDiagnostic) int {
244
+ collector.Unbound()
245
+ return reportDiagnostic(diag)
246
+ }
214
247
  for _, sf := range prog.SourceFiles() {
215
248
  if sf.IsDeclarationFile {
216
249
  continue
@@ -221,8 +254,16 @@ func runTransformProject(
221
254
  }
222
255
  collector.Begin(key)
223
256
  out.TypeScript[key] = transformFileToTypeScript(prog, typiaTransform, sf)
257
+ collector.End()
224
258
  }
225
259
  out.Dependencies = collector.ToJSON()
260
+ // The config chain is the one input that stays universal for a declared file,
261
+ // and it reaches the consumer through this same envelope's graph. Declaring
262
+ // completeness without a graph would leave nothing universal behind, so a
263
+ // tsconfig edit would stop invalidating the files the declaration narrowed.
264
+ if out.Graph != nil {
265
+ out.DependenciesComplete = collector.ToCompleteJSON(out.TypeScript)
266
+ }
226
267
  for _, diag := range *transformDiags {
227
268
  out.Diagnostics = append(out.Diagnostics, transformDiagnosticToCompilerDiagnostic(diag))
228
269
  }
@@ -359,24 +400,62 @@ type transformProjectOutput struct {
359
400
  // files are excluded (toolchain-versioned, not project inputs);
360
401
  // `node_modules` declaration files are included.
361
402
  Dependencies map[string][]string `json:"dependencies,omitempty"`
403
+ // DependenciesComplete lists the TypeScript keys whose Dependencies entry is
404
+ // exhaustive: nothing outside that entry, the file itself, and the universal
405
+ // config chain can change the file's transformed text. The consumer stops
406
+ // widening those files with the reference closure and the global-scope set.
407
+ //
408
+ // Unlisted is always the safe answer, so the list carries only what the
409
+ // collector could bound. An older ttsc ignores the field; 0.19.3 and newer
410
+ // narrow on it.
411
+ DependenciesComplete []string `json:"dependenciesComplete,omitempty"`
362
412
  }
363
413
 
364
414
  // transformDependencyCollector accumulates the consulted-declaration files the
365
415
  // metadata dependency listener reports while one project file is transformed,
366
- // attributing them to that file's envelope key.
416
+ // attributing them to that file's envelope key, and decides which of those keys
417
+ // may be declared complete.
367
418
  type transformDependencyCollector struct {
368
419
  cwd string
369
420
  current string
370
421
  files map[string]map[string]bool
422
+ // unbounded holds the keys withheld from the completeness declaration. It is
423
+ // a set of exclusions rather than a set of admissions because every path that
424
+ // learns a file cannot be bounded runs while that file is being transformed,
425
+ // whereas nothing ever proves the opposite: a file is declarable exactly when
426
+ // no such path fired for it.
427
+ unbounded map[string]bool
371
428
  // isLibraryFile reports the compiler's own classification of a file as a
372
429
  // default library. Classification must come from the program, never from a
373
430
  // filename pattern: a project's own ambient declaration file may legitimately
374
431
  // be named `lib.custom.d.ts`, and dropping it by basename silently loses
375
432
  // cache invalidation for the types it declares (samchon/typia#2108).
376
433
  isLibraryFile func(fileName string) bool
377
- // values memoizes fileName -> envelope value ("" for a dropped file): the
378
- // listener reports the same declaration files repeatedly across call sites.
379
- values map[string]string
434
+ // values memoizes fileName -> rendered envelope value: the listener reports
435
+ // the same declaration files repeatedly across call sites.
436
+ values map[string]transformDependencyValue
437
+ }
438
+
439
+ // transformDependencyBundledScheme is the prefix tsgo gives its embedded default
440
+ // libraries, and the one `driver.NewTransformGraph` drops from the host-owned
441
+ // reference graph.
442
+ const transformDependencyBundledScheme = "bundled:///"
443
+
444
+ // transformDependencyValue is one reported file rendered for the envelope: its
445
+ // key, or the reason it was dropped. The reasons differ in what they cost a
446
+ // completeness declaration, so they cannot both collapse to an empty value.
447
+ type transformDependencyValue struct {
448
+ // key is the envelope value, empty when the file was dropped.
449
+ key string
450
+ // withhold marks a file this envelope cannot report but the host-owned bound
451
+ // still carries, so narrowing to the reported list would lose it.
452
+ withhold bool
453
+ // libraryOnly marks a withhold whose only cause is the compiler classifying
454
+ // the file as a default library. Such a file can change a consulted type and
455
+ // so a generated validator, but never which declaration a call resolves to,
456
+ // which is why the identity channel drops it without withholding
457
+ // (samchon/typia#2361).
458
+ libraryOnly bool
380
459
  }
381
460
 
382
461
  func newTransformDependencyCollector(
@@ -387,7 +466,8 @@ func newTransformDependencyCollector(
387
466
  cwd: cwd,
388
467
  isLibraryFile: isLibraryFile,
389
468
  files: map[string]map[string]bool{},
390
- values: map[string]string{},
469
+ unbounded: map[string]bool{},
470
+ values: map[string]transformDependencyValue{},
391
471
  }
392
472
  }
393
473
 
@@ -396,11 +476,61 @@ func (collector *transformDependencyCollector) Begin(key string) {
396
476
  collector.current = key
397
477
  }
398
478
 
479
+ // End closes the attribution window Begin opened. Nothing consults the checker
480
+ // between two files today, so this changes no output; it is what keeps that
481
+ // true, because a touch that arrives outside a window is dropped rather than
482
+ // charged -- or worse, withheld -- against whichever file happened to run last.
483
+ func (collector *transformDependencyCollector) End() {
484
+ collector.current = ""
485
+ }
486
+
487
+ // Unbound withholds the completeness declaration from the file being
488
+ // transformed. The file keeps its reported dependencies and falls back to the
489
+ // host-owned reference-closure bound, which is what an unlisted file gets and
490
+ // is always sound. Its callers name the reasons: an input the analysis could
491
+ // only read from an inferred position, a consulted on-disk default library, and
492
+ // a call typia could not lower.
493
+ func (collector *transformDependencyCollector) Unbound() {
494
+ if collector.current == "" {
495
+ return
496
+ }
497
+ collector.unbounded[collector.current] = true
498
+ }
499
+
399
500
  // Touch records one consulted declaration file for the current key. Default
400
501
  // library files, virtual URI sources (e.g. tsgo's `bundled:///` libraries),
401
502
  // and the transformed file itself are dropped so the envelope carries only
402
503
  // actionable project inputs.
504
+ //
505
+ // A drop the host-owned bound does not share also withholds the current file's
506
+ // completeness declaration: the graph still carries that file, so a narrowed
507
+ // file would stop watching an input the default bound watches.
403
508
  func (collector *transformDependencyCollector) Touch(fileName string) {
509
+ collector.touch(fileName, true)
510
+ }
511
+
512
+ // TouchCallee records one file reached while deciding WHICH declaration a call
513
+ // resolves to, and does not let a default library withhold the current file.
514
+ //
515
+ // A default library is dropped from the reported list either way -- that
516
+ // contract is samchon/typia#2108's -- but the withholding beside it belongs to
517
+ // the type channel alone. Under `libReplacement` a replaced library can change
518
+ // a generated validator, so a file that consulted one has to give up its
519
+ // declaration. It can never change which declaration a call resolves to:
520
+ // callExpressionTransformer_targetModule recognizes typia's own calls by a path
521
+ // ending in `/typia/lib/<file>.d.ts` or `/typia/src/<file>.ts`, and a file the
522
+ // compiler classifies as a default library satisfies neither wherever it sits
523
+ // on disk. Withholding there cost a file its declaration for calling
524
+ // `table.get(...)`, and under `noembed` -- where every library is a real file --
525
+ // for calling anything at all (samchon/typia#2361).
526
+ //
527
+ // A virtual non-`bundled:///` scheme still withholds on both channels: that drop
528
+ // is about being unwatchable, not about being a library.
529
+ func (collector *transformDependencyCollector) TouchCallee(fileName string) {
530
+ collector.touch(fileName, false)
531
+ }
532
+
533
+ func (collector *transformDependencyCollector) touch(fileName string, library bool) {
404
534
  if collector.current == "" {
405
535
  return
406
536
  }
@@ -409,7 +539,10 @@ func (collector *transformDependencyCollector) Touch(fileName string) {
409
539
  value = collector.value(fileName)
410
540
  collector.values[fileName] = value
411
541
  }
412
- if value == "" || value == collector.current {
542
+ if value.withhold && (library || value.libraryOnly == false) {
543
+ collector.Unbound()
544
+ }
545
+ if value.key == "" || value.key == collector.current {
413
546
  return
414
547
  }
415
548
  set := collector.files[collector.current]
@@ -417,19 +550,36 @@ func (collector *transformDependencyCollector) Touch(fileName string) {
417
550
  set = map[string]bool{}
418
551
  collector.files[collector.current] = set
419
552
  }
420
- set[value] = true
553
+ set[value.key] = true
421
554
  }
422
555
 
423
- // value renders one reported file as its envelope value, or "" when the file
424
- // must be dropped: virtual URI sources (e.g. tsgo's `bundled:///` embedded
425
- // libraries) cannot be watched, and files the compiler classifies as default
426
- // libraries (on-disk in `noembed` or `libReplacement` configurations) are
427
- // toolchain inputs, not project inputs.
428
- func (collector *transformDependencyCollector) value(fileName string) string {
429
- if strings.Contains(fileName, "://") || collector.isLibraryFile(fileName) {
430
- return ""
431
- }
432
- return driver.TransformOutputKey(collector.cwd, fileName)
556
+ // value renders one reported file as its envelope value, or records why it must
557
+ // be dropped.
558
+ //
559
+ // tsgo's embedded libraries under `bundled:///` are nobody's input:
560
+ // `driver.NewTransformGraph` skips that exact prefix, so dropping them leaves
561
+ // the reported list and the host-owned bound in agreement and costs a
562
+ // completeness declaration nothing. Any other URI scheme is unwatchable for us
563
+ // but still reaches the graph, so it is dropped AND withheld -- the asymmetry
564
+ // only runs in the safe direction.
565
+ //
566
+ // A file the compiler classifies as a default library is a toolchain input
567
+ // rather than a project input, which is why the envelope has never reported one.
568
+ // Under `noembed` or `libReplacement` it is nevertheless a real path that
569
+ // `graph.globals` carries, so the default bound watches it while a narrowed file
570
+ // would not. Keep dropping it from the reported list -- that contract is
571
+ // #2108's -- and withhold the declaration from the file that consulted it.
572
+ func (collector *transformDependencyCollector) value(fileName string) transformDependencyValue {
573
+ if strings.HasPrefix(fileName, transformDependencyBundledScheme) {
574
+ return transformDependencyValue{}
575
+ }
576
+ if strings.Contains(fileName, "://") {
577
+ return transformDependencyValue{withhold: true}
578
+ }
579
+ if collector.isLibraryFile(fileName) {
580
+ return transformDependencyValue{withhold: true, libraryOnly: true}
581
+ }
582
+ return transformDependencyValue{key: driver.TransformOutputKey(collector.cwd, fileName)}
433
583
  }
434
584
 
435
585
  // ToJSON renders the collected sets as the envelope's `dependencies` map with
@@ -456,6 +606,30 @@ func (collector *transformDependencyCollector) ToJSON() map[string][]string {
456
606
  return output
457
607
  }
458
608
 
609
+ // ToCompleteJSON renders the envelope's `dependenciesComplete` list: every key
610
+ // the transform published except the ones the collector could not bound.
611
+ //
612
+ // The published map is the input rather than the entries of ToJSON. A file typia
613
+ // never touched reports no dependency at all, and that empty entry is the
614
+ // strongest claim in the envelope -- nothing outside the file can change a
615
+ // faithful re-print -- so reading the dependency map instead would drop the
616
+ // majority of the narrowing on the floor. Taking the same map the consumer keys
617
+ // on also makes the list a subset of it by construction.
618
+ func (collector *transformDependencyCollector) ToCompleteJSON(published map[string]string) []string {
619
+ output := make([]string, 0, len(published))
620
+ for key := range published {
621
+ if collector.unbounded[key] {
622
+ continue
623
+ }
624
+ output = append(output, key)
625
+ }
626
+ if len(output) == 0 {
627
+ return nil
628
+ }
629
+ sort.Strings(output)
630
+ return output
631
+ }
632
+
459
633
  type transformCompilerDiagnostic struct {
460
634
  File *string `json:"file"`
461
635
  Category string `json:"category"`