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
@@ -0,0 +1,117 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "testing"
8
+ )
9
+
10
+ // TestProjectDependenciesCalleeShapeTransform verifies where the callee walk
11
+ // stops, in both directions.
12
+ //
13
+ // A function literal written directly in callee position is the most bounded
14
+ // declaration there is: the call resolves to the literal itself, in this very
15
+ // file, so its body is nobody's dependency and the file stays declarable.
16
+ // Reached through a nested call the same literal is the opposite -- the identity
17
+ // is whatever its body returns, and no set of files describes that -- so the
18
+ // file has to be withheld from the completeness declaration instead
19
+ // (samchon/typia#2357).
20
+ //
21
+ // 1. Build a project where `direct.ts` calls an IIFE whose body names a type
22
+ // from `deep.ts`, while `indirect.ts` calls what an IIFE returns and
23
+ // `tagged.ts` calls what a tagged template returns.
24
+ // 2. Run project transform mode and decode the JSON envelope.
25
+ // 3. Assert `direct.ts` is declared complete and reports nothing from its own
26
+ // IIFE body, so the walk neither charged nor withheld the body's names.
27
+ // 4. Assert `indirect.ts` and `tagged.ts` are withheld, so a call-like form
28
+ // other than `()` does not quietly escape the same rule.
29
+ func TestProjectDependenciesCalleeShapeTransform(t *testing.T) {
30
+ project := projectDependenciesCalleeShapeProject(t)
31
+ out, errText, code := ttscTypiaTestCapture(func() int {
32
+ return runTransform([]string{
33
+ "--cwd", project,
34
+ "--tsconfig", "tsconfig.json",
35
+ "--output", "ts",
36
+ })
37
+ })
38
+ if code != 0 {
39
+ t.Fatalf("project transform failed: code=%d stderr=\n%s", code, errText)
40
+ }
41
+ var envelope struct {
42
+ Dependencies map[string][]string `json:"dependencies"`
43
+ DependenciesComplete []string `json:"dependenciesComplete"`
44
+ }
45
+ if err := json.Unmarshal([]byte(out), &envelope); err != nil {
46
+ t.Fatalf("decode envelope: %v\n%s", err, out)
47
+ }
48
+ declared := map[string]bool{}
49
+ for _, key := range envelope.DependenciesComplete {
50
+ declared[key] = true
51
+ }
52
+ if !declared["src/direct.ts"] {
53
+ t.Fatalf("a call to a function literal written in callee position resolves to that literal and must stay declarable: %v", envelope.DependenciesComplete)
54
+ }
55
+ for _, entry := range envelope.Dependencies["src/direct.ts"] {
56
+ if entry == "src/deep.ts" {
57
+ t.Fatalf("the walk must not charge src/direct.ts with names its own IIFE body uses: %v", envelope.Dependencies["src/direct.ts"])
58
+ }
59
+ }
60
+ if declared["src/indirect.ts"] {
61
+ t.Fatalf("a call to what a function literal returns has no bounded identity and must be withheld: %v", envelope.DependenciesComplete)
62
+ }
63
+ if declared["src/tagged.ts"] {
64
+ t.Fatalf("a tagged template is a call, so what it returns has no bounded identity either: %v", envelope.DependenciesComplete)
65
+ }
66
+ }
67
+
68
+ func projectDependenciesCalleeShapeProject(t *testing.T) string {
69
+ t.Helper()
70
+ root := ttscTypiaTestRepoRoot(t)
71
+ base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
72
+ if err := os.MkdirAll(base, 0o755); err != nil {
73
+ t.Fatalf("mkdir temp base: %v", err)
74
+ }
75
+ dir, err := os.MkdirTemp(base, "project-dependencies-callee-shape-")
76
+ if err != nil {
77
+ t.Fatalf("create temp fixture: %v", err)
78
+ }
79
+ t.Cleanup(func() { _ = os.RemoveAll(dir) })
80
+ src := filepath.Join(dir, "src")
81
+ if err := os.MkdirAll(src, 0o755); err != nil {
82
+ t.Fatalf("mkdir fixture src: %v", err)
83
+ }
84
+ if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(projectDependenciesEnvelopeTSConfig), 0o644); err != nil {
85
+ t.Fatalf("write tsconfig: %v", err)
86
+ }
87
+ for name, body := range map[string]string{
88
+ "direct.ts": projectDependenciesCalleeShapeSourceDirect,
89
+ "indirect.ts": projectDependenciesCalleeShapeSourceIndirect,
90
+ "tagged.ts": projectDependenciesCalleeShapeSourceTagged,
91
+ "deep.ts": projectDependenciesCalleeShapeSourceDeep,
92
+ } {
93
+ if err := os.WriteFile(filepath.Join(src, name), []byte(body), 0o644); err != nil {
94
+ t.Fatalf("write %s: %v", name, err)
95
+ }
96
+ }
97
+ return dir
98
+ }
99
+
100
+ const projectDependenciesCalleeShapeSourceDirect = `import { Deep } from "./deep";
101
+
102
+ export const value = (() => {
103
+ const held: Deep = { id: "x" };
104
+ return held.id;
105
+ })();
106
+ `
107
+
108
+ const projectDependenciesCalleeShapeSourceIndirect = `export const value = (() => (input: string): string => input)()("x");
109
+ `
110
+
111
+ const projectDependenciesCalleeShapeSourceTagged = `export const value = ((_: TemplateStringsArray) => (input: string): string => input)` + "`x`" + `("y");
112
+ `
113
+
114
+ const projectDependenciesCalleeShapeSourceDeep = `export interface Deep {
115
+ id: string;
116
+ }
117
+ `
@@ -0,0 +1,121 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "strings"
8
+ "testing"
9
+ )
10
+
11
+ // TestProjectDependenciesCalleeUntransformedBarrelTransform verifies a file
12
+ // typia did not transform still reports the modules its callees resolve
13
+ // through, which is what makes declaring that file complete honest.
14
+ //
15
+ // A file with no typia call is declared complete with an empty dependency list,
16
+ // and the claim behind it is that nothing outside the file can change a
17
+ // faithful re-print. Exactly one thing can: an edit that makes one of its calls
18
+ // a typia call. Re-pointing `export { is } from "./local"` at `"typia"` does
19
+ // that without touching the caller, so the barrel has to be in the caller's
20
+ // reported list even though the caller consulted no type at all -- otherwise
21
+ // the narrowed bound has nothing left to invalidate it (samchon/typia#2357).
22
+ //
23
+ // 1. Build a project where `plain.ts` calls `is<Alpha>` imported from
24
+ // `barrel.ts`, which re-exports a local helper rather than typia.
25
+ // 2. Run project transform mode and decode the JSON envelope.
26
+ // 3. Assert `plain.ts` was left untransformed, so it is a class-2 file.
27
+ // 4. Assert it is declared complete, and that its reported list nevertheless
28
+ // carries `src/barrel.ts` and the `src/local.ts` the barrel resolves to.
29
+ func TestProjectDependenciesCalleeUntransformedBarrelTransform(t *testing.T) {
30
+ project := projectDependenciesCalleeUntransformedBarrelProject(t)
31
+ out, errText, code := ttscTypiaTestCapture(func() int {
32
+ return runTransform([]string{
33
+ "--cwd", project,
34
+ "--tsconfig", "tsconfig.json",
35
+ "--output", "ts",
36
+ })
37
+ })
38
+ if code != 0 {
39
+ t.Fatalf("project transform failed: code=%d stderr=\n%s", code, errText)
40
+ }
41
+ var envelope struct {
42
+ TypeScript map[string]string `json:"typescript"`
43
+ Dependencies map[string][]string `json:"dependencies"`
44
+ DependenciesComplete []string `json:"dependenciesComplete"`
45
+ }
46
+ if err := json.Unmarshal([]byte(out), &envelope); err != nil {
47
+ t.Fatalf("decode envelope: %v\n%s", err, out)
48
+ }
49
+ text := envelope.TypeScript["src/plain.ts"]
50
+ if !strings.Contains(text, "is<Alpha>(input)") {
51
+ t.Fatalf("src/plain.ts calls a non-typia helper and must be left untransformed, got:\n%s", text)
52
+ }
53
+ declared := map[string]bool{}
54
+ for _, key := range envelope.DependenciesComplete {
55
+ declared[key] = true
56
+ }
57
+ if !declared["src/plain.ts"] {
58
+ t.Fatalf("an untransformed file must be declared complete: %v", envelope.DependenciesComplete)
59
+ }
60
+ entries := envelope.Dependencies["src/plain.ts"]
61
+ found := map[string]bool{}
62
+ for _, entry := range entries {
63
+ found[entry] = true
64
+ }
65
+ if !found["src/barrel.ts"] {
66
+ t.Fatalf("dependencies of src/plain.ts must contain src/barrel.ts, whose retarget would make the call typia's: %v", entries)
67
+ }
68
+ if !found["src/local.ts"] {
69
+ t.Fatalf("dependencies of src/plain.ts must contain src/local.ts, which currently declares the callee: %v", entries)
70
+ }
71
+ }
72
+
73
+ func projectDependenciesCalleeUntransformedBarrelProject(t *testing.T) string {
74
+ t.Helper()
75
+ root := ttscTypiaTestRepoRoot(t)
76
+ base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
77
+ if err := os.MkdirAll(base, 0o755); err != nil {
78
+ t.Fatalf("mkdir temp base: %v", err)
79
+ }
80
+ dir, err := os.MkdirTemp(base, "project-dependencies-callee-untransformed-barrel-")
81
+ if err != nil {
82
+ t.Fatalf("create temp fixture: %v", err)
83
+ }
84
+ t.Cleanup(func() { _ = os.RemoveAll(dir) })
85
+ src := filepath.Join(dir, "src")
86
+ if err := os.MkdirAll(src, 0o755); err != nil {
87
+ t.Fatalf("mkdir fixture src: %v", err)
88
+ }
89
+ if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(projectDependenciesEnvelopeTSConfig), 0o644); err != nil {
90
+ t.Fatalf("write tsconfig: %v", err)
91
+ }
92
+ for name, body := range map[string]string{
93
+ "plain.ts": projectDependenciesCalleeUntransformedBarrelSourcePlain,
94
+ "barrel.ts": projectDependenciesCalleeUntransformedBarrelSourceBarrel,
95
+ "local.ts": projectDependenciesCalleeUntransformedBarrelSourceLocal,
96
+ "alpha.ts": projectDependenciesCalleeUntransformedBarrelSourceAlpha,
97
+ } {
98
+ if err := os.WriteFile(filepath.Join(src, name), []byte(body), 0o644); err != nil {
99
+ t.Fatalf("write %s: %v", name, err)
100
+ }
101
+ }
102
+ return dir
103
+ }
104
+
105
+ const projectDependenciesCalleeUntransformedBarrelSourcePlain = `import { is } from "./barrel";
106
+
107
+ import { Alpha } from "./alpha";
108
+
109
+ export const checkAlpha = (input: unknown) => is<Alpha>(input);
110
+ `
111
+
112
+ const projectDependenciesCalleeUntransformedBarrelSourceBarrel = `export { is } from "./local";
113
+ `
114
+
115
+ const projectDependenciesCalleeUntransformedBarrelSourceLocal = `export const is = <T>(input: unknown): input is T => input !== null;
116
+ `
117
+
118
+ const projectDependenciesCalleeUntransformedBarrelSourceAlpha = `export interface Alpha {
119
+ id: string;
120
+ }
121
+ `
@@ -0,0 +1,116 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "testing"
8
+ )
9
+
10
+ // TestProjectDependenciesCompleteDiagnosticTransform verifies a file whose
11
+ // typia call raised a diagnostic is withheld from the completeness declaration.
12
+ //
13
+ // A diagnostic means the analysis stopped partway, so what it consulted before
14
+ // giving up is not that file's whole input set: an edit to a declaration it
15
+ // never reached could make the same call succeed and publish different text.
16
+ // The envelope is a failure result either way and the protocol allows a partial
17
+ // declaration, but a claim that is false is worth withholding rather than
18
+ // explaining (samchon/typia#2357).
19
+ //
20
+ // 1. Build a project where `rejected.ts` asks for a protobuf message over
21
+ // `bigint`, which no protobuf entry point supports, beside a `control.ts`
22
+ // that transforms cleanly.
23
+ // 2. Run project transform mode; the host reports the diagnostic and exits 3
24
+ // after printing the envelope.
25
+ // 3. Assert the diagnostic names `rejected.ts`, so the fixture failed for the
26
+ // reason the test intends.
27
+ // 4. Assert `control.ts` is declared complete and `rejected.ts` is not.
28
+ func TestProjectDependenciesCompleteDiagnosticTransform(t *testing.T) {
29
+ project := projectDependenciesCompleteDiagnosticProject(t)
30
+ out, _, code := ttscTypiaTestCapture(func() int {
31
+ return runTransform([]string{
32
+ "--cwd", project,
33
+ "--tsconfig", "tsconfig.json",
34
+ "--output", "ts",
35
+ })
36
+ })
37
+ if code != 3 {
38
+ t.Fatalf("a rejected call must fail the project transform with code 3, got %d:\n%s", code, out)
39
+ }
40
+ var envelope struct {
41
+ Diagnostics []struct {
42
+ File *string `json:"file"`
43
+ } `json:"diagnostics"`
44
+ DependenciesComplete []string `json:"dependenciesComplete"`
45
+ }
46
+ if err := json.Unmarshal([]byte(out), &envelope); err != nil {
47
+ t.Fatalf("decode envelope: %v\n%s", err, out)
48
+ }
49
+ named := false
50
+ for _, diagnostic := range envelope.Diagnostics {
51
+ if diagnostic.File != nil && filepath.Base(filepath.FromSlash(*diagnostic.File)) == "rejected.ts" {
52
+ named = true
53
+ }
54
+ }
55
+ if !named {
56
+ t.Fatalf("the diagnostic must name src/rejected.ts, so the fixture failed for the intended reason: %s", out)
57
+ }
58
+ declared := map[string]bool{}
59
+ for _, key := range envelope.DependenciesComplete {
60
+ declared[key] = true
61
+ }
62
+ if !declared["src/control.ts"] {
63
+ t.Fatalf("a file that transformed cleanly must still be declared complete: %v", envelope.DependenciesComplete)
64
+ }
65
+ if declared["src/rejected.ts"] {
66
+ t.Fatalf("a file whose call typia could not lower must be withheld from the completeness declaration: %v", envelope.DependenciesComplete)
67
+ }
68
+ }
69
+
70
+ func projectDependenciesCompleteDiagnosticProject(t *testing.T) string {
71
+ t.Helper()
72
+ root := ttscTypiaTestRepoRoot(t)
73
+ base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
74
+ if err := os.MkdirAll(base, 0o755); err != nil {
75
+ t.Fatalf("mkdir temp base: %v", err)
76
+ }
77
+ dir, err := os.MkdirTemp(base, "project-dependencies-complete-diagnostic-")
78
+ if err != nil {
79
+ t.Fatalf("create temp fixture: %v", err)
80
+ }
81
+ t.Cleanup(func() { _ = os.RemoveAll(dir) })
82
+ src := filepath.Join(dir, "src")
83
+ if err := os.MkdirAll(src, 0o755); err != nil {
84
+ t.Fatalf("mkdir fixture src: %v", err)
85
+ }
86
+ if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(projectDependenciesEnvelopeTSConfig), 0o644); err != nil {
87
+ t.Fatalf("write tsconfig: %v", err)
88
+ }
89
+ for name, body := range map[string]string{
90
+ "rejected.ts": projectDependenciesCompleteDiagnosticSourceRejected,
91
+ "control.ts": projectDependenciesCompleteDiagnosticSourceControl,
92
+ "shape.ts": projectDependenciesCompleteDiagnosticSourceShape,
93
+ } {
94
+ if err := os.WriteFile(filepath.Join(src, name), []byte(body), 0o644); err != nil {
95
+ t.Fatalf("write %s: %v", name, err)
96
+ }
97
+ }
98
+ return dir
99
+ }
100
+
101
+ const projectDependenciesCompleteDiagnosticSourceRejected = `import typia from "typia";
102
+
103
+ export const message = typia.protobuf.message<bigint>();
104
+ `
105
+
106
+ const projectDependenciesCompleteDiagnosticSourceControl = `import typia from "typia";
107
+
108
+ import { Shape } from "./shape";
109
+
110
+ export const validateShape = (input: unknown) => typia.is<Shape>(input);
111
+ `
112
+
113
+ const projectDependenciesCompleteDiagnosticSourceShape = `export interface Shape {
114
+ id: string;
115
+ }
116
+ `
@@ -0,0 +1,115 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "sort"
8
+ "testing"
9
+ )
10
+
11
+ // TestProjectDependenciesCompleteEnvelopeTransform verifies the project
12
+ // transform envelope declares every file it published as having a complete
13
+ // dependency list.
14
+ //
15
+ // `dependencies` alone only widens the consumer's bound; `dependenciesComplete`
16
+ // is what lets the consumer stop widening and validate the ~10 declarations
17
+ // typia consulted instead of the file's whole reference closure plus every
18
+ // global-scope declaration, once per delivered module. The claim covers both
19
+ // classes of envelope file, and the second one is the larger half: a file with
20
+ // no typia call receives no contribution at all, so its empty entry is the
21
+ // strongest claim the envelope carries (samchon/typia#2357).
22
+ //
23
+ // 1. Build a project where `a.ts` validates `Bee` from `b.ts` (which reaches
24
+ // `Cee` in `c.ts`), plus `d.ts` with no typia call at all.
25
+ // 2. Run project transform mode and decode the JSON envelope.
26
+ // 3. Assert `dependenciesComplete` names exactly the published `typescript`
27
+ // keys, so neither the transformed file nor the untouched ones are left on
28
+ // the conservative bound.
29
+ // 4. Assert the declared list for `src/a.ts` still carries `src/b.ts` and
30
+ // `src/c.ts`, because the declaration narrows to that entry and an entry
31
+ // that lost a consulted file would now serve a stale validator.
32
+ func TestProjectDependenciesCompleteEnvelopeTransform(t *testing.T) {
33
+ project := projectDependenciesCompleteEnvelopeProject(t)
34
+ out, errText, code := ttscTypiaTestCapture(func() int {
35
+ return runTransform([]string{
36
+ "--cwd", project,
37
+ "--tsconfig", "tsconfig.json",
38
+ "--output", "ts",
39
+ })
40
+ })
41
+ if code != 0 {
42
+ t.Fatalf("project transform failed: code=%d stderr=\n%s", code, errText)
43
+ }
44
+ var envelope struct {
45
+ TypeScript map[string]string `json:"typescript"`
46
+ Dependencies map[string][]string `json:"dependencies"`
47
+ DependenciesComplete []string `json:"dependenciesComplete"`
48
+ }
49
+ if err := json.Unmarshal([]byte(out), &envelope); err != nil {
50
+ t.Fatalf("decode envelope: %v\n%s", err, out)
51
+ }
52
+
53
+ published := make([]string, 0, len(envelope.TypeScript))
54
+ for key := range envelope.TypeScript {
55
+ published = append(published, key)
56
+ }
57
+ sort.Strings(published)
58
+ declared := append([]string{}, envelope.DependenciesComplete...)
59
+ sort.Strings(declared)
60
+ if len(published) != len(declared) {
61
+ t.Fatalf("dependenciesComplete must name every published file; published=%v declared=%v", published, declared)
62
+ }
63
+ for index, key := range published {
64
+ if declared[index] != key {
65
+ t.Fatalf("dependenciesComplete must name every published file; published=%v declared=%v", published, declared)
66
+ }
67
+ }
68
+
69
+ entries := envelope.Dependencies["src/a.ts"]
70
+ found := map[string]bool{}
71
+ for _, entry := range entries {
72
+ found[entry] = true
73
+ }
74
+ if !found["src/b.ts"] {
75
+ t.Fatalf("the declared entry for src/a.ts must keep the direct declaration file src/b.ts: %v", entries)
76
+ }
77
+ if !found["src/c.ts"] {
78
+ t.Fatalf("the declared entry for src/a.ts must keep the transitive declaration file src/c.ts: %v", entries)
79
+ }
80
+ if _, ok := envelope.Dependencies["src/d.ts"]; ok {
81
+ t.Fatalf("src/d.ts has no typia call and must be declared with no dependency at all: %v", envelope.Dependencies["src/d.ts"])
82
+ }
83
+ }
84
+
85
+ func projectDependenciesCompleteEnvelopeProject(t *testing.T) string {
86
+ t.Helper()
87
+ root := ttscTypiaTestRepoRoot(t)
88
+ base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
89
+ if err := os.MkdirAll(base, 0o755); err != nil {
90
+ t.Fatalf("mkdir temp base: %v", err)
91
+ }
92
+ dir, err := os.MkdirTemp(base, "project-dependencies-complete-envelope-")
93
+ if err != nil {
94
+ t.Fatalf("create temp fixture: %v", err)
95
+ }
96
+ t.Cleanup(func() { _ = os.RemoveAll(dir) })
97
+ src := filepath.Join(dir, "src")
98
+ if err := os.MkdirAll(src, 0o755); err != nil {
99
+ t.Fatalf("mkdir fixture src: %v", err)
100
+ }
101
+ if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(projectDependenciesEnvelopeTSConfig), 0o644); err != nil {
102
+ t.Fatalf("write tsconfig: %v", err)
103
+ }
104
+ for name, body := range map[string]string{
105
+ "a.ts": projectDependenciesEnvelopeSourceA,
106
+ "b.ts": projectDependenciesEnvelopeSourceB,
107
+ "c.ts": projectDependenciesEnvelopeSourceC,
108
+ "d.ts": projectDependenciesEnvelopeSourceD,
109
+ } {
110
+ if err := os.WriteFile(filepath.Join(src, name), []byte(body), 0o644); err != nil {
111
+ t.Fatalf("write %s: %v", name, err)
112
+ }
113
+ }
114
+ return dir
115
+ }
@@ -0,0 +1,127 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "strings"
8
+ "testing"
9
+ )
10
+
11
+ // TestProjectDependenciesCompleteInferredTypeTransform verifies a file whose
12
+ // typia call takes its validated type from the value argument is withheld from
13
+ // the completeness declaration, while its written-type-argument twin is not.
14
+ //
15
+ // A written type argument bounds the analysis: every declaration reachable from
16
+ // it is touched and reported, so the reported list is the whole input set. An
17
+ // inferred one does not, because contextual typing can put the deciding
18
+ // annotation in a file the resolved type never names -- `const handler:
19
+ // Handler = (input) => typia.assert(input)` validates whatever `Handler`
20
+ // declares, and `Handler`'s file is nowhere in the consulted-declaration set.
21
+ // Declaring such a file complete would drop the reference closure that is the
22
+ // only thing still watching it (samchon/typia#2357).
23
+ //
24
+ // 1. Build a project where `written.ts` calls `typia.assert<Shape>(input)` and
25
+ // `inferred.ts` calls `typia.assert(input)` on a parameter contextually
26
+ // typed through `handler.ts`.
27
+ // 2. Run project transform mode and decode the JSON envelope.
28
+ // 3. Assert both files transformed, so the two differ only in where the type
29
+ // came from.
30
+ // 4. Assert `written.ts` is declared complete and `inferred.ts` is not, and
31
+ // that `inferred.ts` still keeps its reported dependency entry -- it falls
32
+ // back to the host-owned bound, it is not stripped of what it did report.
33
+ func TestProjectDependenciesCompleteInferredTypeTransform(t *testing.T) {
34
+ project := projectDependenciesCompleteInferredTypeProject(t)
35
+ out, errText, code := ttscTypiaTestCapture(func() int {
36
+ return runTransform([]string{
37
+ "--cwd", project,
38
+ "--tsconfig", "tsconfig.json",
39
+ "--output", "ts",
40
+ })
41
+ })
42
+ if code != 0 {
43
+ t.Fatalf("project transform failed: code=%d stderr=\n%s", code, errText)
44
+ }
45
+ var envelope struct {
46
+ TypeScript map[string]string `json:"typescript"`
47
+ Dependencies map[string][]string `json:"dependencies"`
48
+ DependenciesComplete []string `json:"dependenciesComplete"`
49
+ }
50
+ if err := json.Unmarshal([]byte(out), &envelope); err != nil {
51
+ t.Fatalf("decode envelope: %v\n%s", err, out)
52
+ }
53
+ for _, key := range []string{"src/written.ts", "src/inferred.ts"} {
54
+ if text := envelope.TypeScript[key]; !strings.Contains(text, "typeof input") {
55
+ t.Fatalf("%s must have been transformed into a generated validator, got:\n%s", key, text)
56
+ }
57
+ }
58
+ declared := map[string]bool{}
59
+ for _, key := range envelope.DependenciesComplete {
60
+ declared[key] = true
61
+ }
62
+ if !declared["src/written.ts"] {
63
+ t.Fatalf("a call with a written type argument bounds its inputs and must be declared complete: %v", envelope.DependenciesComplete)
64
+ }
65
+ if declared["src/inferred.ts"] {
66
+ t.Fatalf("a call that infers its type from the value argument must be withheld from the completeness declaration: %v", envelope.DependenciesComplete)
67
+ }
68
+ if len(envelope.Dependencies["src/inferred.ts"]) == 0 {
69
+ t.Fatalf("withholding the declaration must not strip what src/inferred.ts did report: %v", envelope.Dependencies)
70
+ }
71
+ }
72
+
73
+ func projectDependenciesCompleteInferredTypeProject(t *testing.T) string {
74
+ t.Helper()
75
+ root := ttscTypiaTestRepoRoot(t)
76
+ base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
77
+ if err := os.MkdirAll(base, 0o755); err != nil {
78
+ t.Fatalf("mkdir temp base: %v", err)
79
+ }
80
+ dir, err := os.MkdirTemp(base, "project-dependencies-complete-inferred-")
81
+ if err != nil {
82
+ t.Fatalf("create temp fixture: %v", err)
83
+ }
84
+ t.Cleanup(func() { _ = os.RemoveAll(dir) })
85
+ src := filepath.Join(dir, "src")
86
+ if err := os.MkdirAll(src, 0o755); err != nil {
87
+ t.Fatalf("mkdir fixture src: %v", err)
88
+ }
89
+ if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(projectDependenciesEnvelopeTSConfig), 0o644); err != nil {
90
+ t.Fatalf("write tsconfig: %v", err)
91
+ }
92
+ for name, body := range map[string]string{
93
+ "shape.ts": projectDependenciesCompleteInferredSourceShape,
94
+ "handler.ts": projectDependenciesCompleteInferredSourceHandler,
95
+ "written.ts": projectDependenciesCompleteInferredSourceWritten,
96
+ "inferred.ts": projectDependenciesCompleteInferredSourceInferred,
97
+ } {
98
+ if err := os.WriteFile(filepath.Join(src, name), []byte(body), 0o644); err != nil {
99
+ t.Fatalf("write %s: %v", name, err)
100
+ }
101
+ }
102
+ return dir
103
+ }
104
+
105
+ const projectDependenciesCompleteInferredSourceShape = `export interface Shape {
106
+ id: string;
107
+ }
108
+ `
109
+
110
+ const projectDependenciesCompleteInferredSourceHandler = `import { Shape } from "./shape";
111
+
112
+ export type Handler = (input: Shape) => unknown;
113
+ `
114
+
115
+ const projectDependenciesCompleteInferredSourceWritten = `import typia from "typia";
116
+
117
+ import { Shape } from "./shape";
118
+
119
+ export const validateShape = (input: Shape) => typia.assert<Shape>(input);
120
+ `
121
+
122
+ const projectDependenciesCompleteInferredSourceInferred = `import typia from "typia";
123
+
124
+ import { Handler } from "./handler";
125
+
126
+ export const handle: Handler = (input) => typia.assert(input);
127
+ `