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,159 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "strings"
8
+ "testing"
9
+ )
10
+
11
+ // TestProjectDependenciesInferredDeclarationTransform verifies a validated type
12
+ // that reaches a declaration with no written annotation withholds the
13
+ // completeness declaration, while its annotated twin keeps it.
14
+ //
15
+ // The written surface exists because the checker erases an alias of an
16
+ // intrinsic: `type Id = string` interns as the bare intrinsic and leaves no
17
+ // symbol for the type graph to follow, so only the syntax naming the alias can
18
+ // register its file (samchon/typia#2126). A `const` with an annotation has that
19
+ // syntax. A `const` typed by its initializer alone does not, and recovering it
20
+ // means following type inference over arbitrary expressions -- a call's return
21
+ // type, an overload's selection, a contextually typed parameter -- which is
22
+ // exactly the unbounded problem a typia call with no written type argument has.
23
+ // Both get the same answer rather than a partial walk that would leave the file
24
+ // declared on an incomplete list (samchon/typia#2357).
25
+ //
26
+ // 1. Build a project where `cfg.ts` exports one annotated constant, one
27
+ // inferred constant of the same shape, and one destructured binding, and
28
+ // three callers validate `typeof` each.
29
+ // 2. Run project transform mode and decode the JSON envelope.
30
+ // 3. Assert every caller transformed, so the set differs only in where the
31
+ // type was written.
32
+ // 4. Assert the annotated caller is declared complete and names the
33
+ // annotation's own file, and that the inferred caller is withheld while
34
+ // keeping what it did report.
35
+ // 5. Assert the destructured caller is withheld too. A binding element is a
36
+ // declaration kind the predicate names nowhere, so it answers through the
37
+ // default -- which is what pins that the default is "withhold".
38
+ func TestProjectDependenciesInferredDeclarationTransform(t *testing.T) {
39
+ project := projectDependenciesInferredDeclarationProject(t)
40
+ out, errText, code := ttscTypiaTestCapture(func() int {
41
+ return runTransform([]string{
42
+ "--cwd", project,
43
+ "--tsconfig", "tsconfig.json",
44
+ "--output", "ts",
45
+ })
46
+ })
47
+ if code != 0 {
48
+ t.Fatalf("project transform failed: code=%d stderr=\n%s", code, errText)
49
+ }
50
+ var envelope struct {
51
+ TypeScript map[string]string `json:"typescript"`
52
+ Dependencies map[string][]string `json:"dependencies"`
53
+ DependenciesComplete []string `json:"dependenciesComplete"`
54
+ }
55
+ if err := json.Unmarshal([]byte(out), &envelope); err != nil {
56
+ t.Fatalf("decode envelope: %v\n%s", err, out)
57
+ }
58
+ for _, key := range []string{"src/annotated.ts", "src/inferred.ts", "src/destructured.ts"} {
59
+ if text := envelope.TypeScript[key]; !strings.Contains(text, "typeof input") {
60
+ t.Fatalf("%s must have been transformed into a generated validator, got:\n%s", key, text)
61
+ }
62
+ }
63
+ declared := map[string]bool{}
64
+ for _, key := range envelope.DependenciesComplete {
65
+ declared[key] = true
66
+ }
67
+ if !declared["src/annotated.ts"] {
68
+ t.Fatalf("a validated type written as an annotation bounds its inputs and must be declared complete: %v", envelope.DependenciesComplete)
69
+ }
70
+ found := map[string]bool{}
71
+ for _, entry := range envelope.Dependencies["src/annotated.ts"] {
72
+ found[entry] = true
73
+ }
74
+ if !found["src/shape.ts"] {
75
+ t.Fatalf("the annotated constant's own annotation file must be reported: %v", envelope.Dependencies["src/annotated.ts"])
76
+ }
77
+ if declared["src/inferred.ts"] {
78
+ t.Fatalf("a validated type that reaches a declaration typed by its initializer alone must be withheld: %v", envelope.DependenciesComplete)
79
+ }
80
+ if len(envelope.Dependencies["src/inferred.ts"]) == 0 {
81
+ t.Fatalf("withholding the declaration must not strip what src/inferred.ts did report: %v", envelope.Dependencies)
82
+ }
83
+ if declared["src/destructured.ts"] {
84
+ t.Fatalf("a declaration kind the predicate names nowhere must answer through the default and withhold: %v", envelope.DependenciesComplete)
85
+ }
86
+ }
87
+
88
+ func projectDependenciesInferredDeclarationProject(t *testing.T) string {
89
+ t.Helper()
90
+ root := ttscTypiaTestRepoRoot(t)
91
+ base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
92
+ if err := os.MkdirAll(base, 0o755); err != nil {
93
+ t.Fatalf("mkdir temp base: %v", err)
94
+ }
95
+ dir, err := os.MkdirTemp(base, "project-dependencies-inferred-declaration-")
96
+ if err != nil {
97
+ t.Fatalf("create temp fixture: %v", err)
98
+ }
99
+ t.Cleanup(func() { _ = os.RemoveAll(dir) })
100
+ src := filepath.Join(dir, "src")
101
+ if err := os.MkdirAll(src, 0o755); err != nil {
102
+ t.Fatalf("mkdir fixture src: %v", err)
103
+ }
104
+ if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(projectDependenciesEnvelopeTSConfig), 0o644); err != nil {
105
+ t.Fatalf("write tsconfig: %v", err)
106
+ }
107
+ for name, body := range map[string]string{
108
+ "annotated.ts": projectDependenciesInferredDeclarationSourceAnnotated,
109
+ "inferred.ts": projectDependenciesInferredDeclarationSourceInferred,
110
+ "destructured.ts": projectDependenciesInferredDeclarationSourceDestructured,
111
+ "cfg.ts": projectDependenciesInferredDeclarationSourceCfg,
112
+ "shape.ts": projectDependenciesInferredDeclarationSourceShape,
113
+ "id.ts": projectDependenciesInferredDeclarationSourceId,
114
+ } {
115
+ if err := os.WriteFile(filepath.Join(src, name), []byte(body), 0o644); err != nil {
116
+ t.Fatalf("write %s: %v", name, err)
117
+ }
118
+ }
119
+ return dir
120
+ }
121
+
122
+ const projectDependenciesInferredDeclarationSourceAnnotated = `import typia from "typia";
123
+
124
+ import { written } from "./cfg";
125
+
126
+ export const check = (input: unknown) => typia.is<typeof written>(input);
127
+ `
128
+
129
+ const projectDependenciesInferredDeclarationSourceInferred = `import typia from "typia";
130
+
131
+ import { guessed } from "./cfg";
132
+
133
+ export const check = (input: unknown) => typia.is<typeof guessed>(input);
134
+ `
135
+
136
+ const projectDependenciesInferredDeclarationSourceDestructured = `import typia from "typia";
137
+
138
+ import { picked } from "./cfg";
139
+
140
+ export const check = (input: unknown) => typia.is<typeof picked>(input);
141
+ `
142
+
143
+ const projectDependenciesInferredDeclarationSourceCfg = `import { Id } from "./id";
144
+ import { Shape } from "./shape";
145
+
146
+ export const written: Shape = { id: "w" };
147
+
148
+ export const guessed = { id: "g" as Id };
149
+
150
+ export const { id: picked } = written;
151
+ `
152
+
153
+ const projectDependenciesInferredDeclarationSourceShape = `export interface Shape {
154
+ id: string;
155
+ }
156
+ `
157
+
158
+ const projectDependenciesInferredDeclarationSourceId = `export type Id = string;
159
+ `
@@ -0,0 +1,134 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "testing"
8
+ )
9
+
10
+ // TestProjectDependenciesJsDocTypedefTransform verifies an `allowJs` project
11
+ // whose type is written as a JSDoc typedef reports the file behind a
12
+ // `@property` tag and still declares the consumer complete.
13
+ //
14
+ // A JSDoc typedef reaches the walk as a `JSTypeAliasDeclaration`, so its whole
15
+ // node is surfaced and every reference written inside it -- including the
16
+ // `import("./id").Id` a `@property` tag names -- resolves from there. The tags
17
+ // themselves never arrive at the boundedness predicate, so this is what pins
18
+ // that they do not need to: were a property enumeration to start handing tags
19
+ // over, the default would withhold and this file would stop being declared
20
+ // (samchon/typia#2357).
21
+ //
22
+ // 1. Build an `allowJs` project where `main.ts` validates a typedef declared
23
+ // in `doc.js`, whose `@property` tag names a typedef in `id.js`.
24
+ // 2. Run project transform mode and decode the JSON envelope.
25
+ // 3. Assert `dependencies["src/main.ts"]` carries both `src/doc.js` and
26
+ // `src/id.js`.
27
+ // 4. Assert `src/main.ts` is declared complete, so the narrowed bound is the
28
+ // one carrying them.
29
+ func TestProjectDependenciesJsDocTypedefTransform(t *testing.T) {
30
+ project := projectDependenciesJsDocTypedefProject(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
+ entries := envelope.Dependencies["src/main.ts"]
49
+ found := map[string]bool{}
50
+ for _, entry := range entries {
51
+ found[entry] = true
52
+ }
53
+ if !found["src/doc.js"] {
54
+ t.Fatalf("dependencies of src/main.ts must contain the typedef's own file src/doc.js: %v", entries)
55
+ }
56
+ if !found["src/id.js"] {
57
+ t.Fatalf("dependencies of src/main.ts must contain src/id.js, which a @property tag names: %v", entries)
58
+ }
59
+ declared := false
60
+ for _, key := range envelope.DependenciesComplete {
61
+ if key == "src/main.ts" {
62
+ declared = true
63
+ }
64
+ }
65
+ if !declared {
66
+ t.Fatalf("src/main.ts must be declared complete, so the reported entry is the whole bound: %v", envelope.DependenciesComplete)
67
+ }
68
+ }
69
+
70
+ func projectDependenciesJsDocTypedefProject(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-jsdoc-typedef-")
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(projectDependenciesJsDocTypedefTSConfig), 0o644); err != nil {
87
+ t.Fatalf("write tsconfig: %v", err)
88
+ }
89
+ for name, body := range map[string]string{
90
+ "main.ts": projectDependenciesJsDocTypedefSourceMain,
91
+ "doc.js": projectDependenciesJsDocTypedefSourceDoc,
92
+ "id.js": projectDependenciesJsDocTypedefSourceId,
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 projectDependenciesJsDocTypedefTSConfig = `{
102
+ "compilerOptions": {
103
+ "target": "ES2022",
104
+ "module": "commonjs",
105
+ "moduleResolution": "bundler",
106
+ "ignoreDeprecations": "6.0",
107
+ "esModuleInterop": true,
108
+ "strict": true,
109
+ "skipLibCheck": true,
110
+ "allowJs": true,
111
+ "rootDir": "src",
112
+ "outDir": "dist"
113
+ },
114
+ "include": ["src"]
115
+ }
116
+ `
117
+
118
+ const projectDependenciesJsDocTypedefSourceMain = `import typia from "typia";
119
+
120
+ export const check = (input: unknown) => typia.is<import("./doc").Doc>(input);
121
+ `
122
+
123
+ const projectDependenciesJsDocTypedefSourceDoc = `/**
124
+ * @typedef {Object} Doc
125
+ * @property {import("./id").Id} id
126
+ */
127
+
128
+ export const marker = 1;
129
+ `
130
+
131
+ const projectDependenciesJsDocTypedefSourceId = `/** @typedef {string} Id */
132
+
133
+ export const marker = 1;
134
+ `
@@ -0,0 +1,123 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "testing"
8
+ )
9
+
10
+ // TestProjectDependenciesNamedTupleMemberTransform verifies a named tuple
11
+ // member's written type reaches the reported dependencies, and that the file
12
+ // consulting it is still declared complete.
13
+ //
14
+ // `[id: Id]` writes its type as plainly as a property annotation does, and the
15
+ // checker interns `type Id = string` as the bare intrinsic, so the written
16
+ // surface is the only thing that can register `id.ts` (samchon/typia#2126). The
17
+ // member is a declaration kind of its own, so it needs its own place in both the
18
+ // surface that walks a written type and the predicate that decides whether a
19
+ // file may be declared on the strength of one — a kind in the second without the
20
+ // first is exactly an over-declaration (samchon/typia#2357).
21
+ //
22
+ // 1. Build a project where `a.ts` validates a tuple alias whose first member is
23
+ // named and typed `Id`, beside an alias the tuple never uses.
24
+ // 2. Run project transform mode and decode the JSON envelope.
25
+ // 3. Assert `dependencies["src/a.ts"]` contains `src/id.ts` and that `a.ts` is
26
+ // declared complete, so the narrowed bound is the one carrying it.
27
+ // 4. Assert it omits `src/unused.ts`, which no consulted member names.
28
+ func TestProjectDependenciesNamedTupleMemberTransform(t *testing.T) {
29
+ project := projectDependenciesNamedTupleMemberProject(t)
30
+ out, errText, code := ttscTypiaTestCapture(func() int {
31
+ return runTransform([]string{
32
+ "--cwd", project,
33
+ "--tsconfig", "tsconfig.json",
34
+ "--output", "ts",
35
+ })
36
+ })
37
+ if code != 0 {
38
+ t.Fatalf("project transform failed: code=%d stderr=\n%s", code, errText)
39
+ }
40
+ var envelope struct {
41
+ Dependencies map[string][]string `json:"dependencies"`
42
+ DependenciesComplete []string `json:"dependenciesComplete"`
43
+ }
44
+ if err := json.Unmarshal([]byte(out), &envelope); err != nil {
45
+ t.Fatalf("decode envelope: %v\n%s", err, out)
46
+ }
47
+ entries := envelope.Dependencies["src/a.ts"]
48
+ found := map[string]bool{}
49
+ for _, entry := range entries {
50
+ found[entry] = true
51
+ }
52
+ if !found["src/pair.ts"] {
53
+ t.Fatalf("dependencies of src/a.ts must contain the declaring file src/pair.ts: %v", entries)
54
+ }
55
+ if !found["src/id.ts"] {
56
+ t.Fatalf("dependencies of src/a.ts must contain src/id.ts, which the named tuple member's type names: %v", entries)
57
+ }
58
+ if found["src/unused.ts"] {
59
+ t.Fatalf("dependencies of src/a.ts must not contain src/unused.ts, which no consulted member names: %v", entries)
60
+ }
61
+ declared := false
62
+ for _, key := range envelope.DependenciesComplete {
63
+ if key == "src/a.ts" {
64
+ declared = true
65
+ }
66
+ }
67
+ if !declared {
68
+ t.Fatalf("src/a.ts must be declared complete, so the reported entry is the whole bound: %v", envelope.DependenciesComplete)
69
+ }
70
+ }
71
+
72
+ func projectDependenciesNamedTupleMemberProject(t *testing.T) string {
73
+ t.Helper()
74
+ root := ttscTypiaTestRepoRoot(t)
75
+ base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
76
+ if err := os.MkdirAll(base, 0o755); err != nil {
77
+ t.Fatalf("mkdir temp base: %v", err)
78
+ }
79
+ dir, err := os.MkdirTemp(base, "project-dependencies-named-tuple-member-")
80
+ if err != nil {
81
+ t.Fatalf("create temp fixture: %v", err)
82
+ }
83
+ t.Cleanup(func() { _ = os.RemoveAll(dir) })
84
+ src := filepath.Join(dir, "src")
85
+ if err := os.MkdirAll(src, 0o755); err != nil {
86
+ t.Fatalf("mkdir fixture src: %v", err)
87
+ }
88
+ if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(projectDependenciesEnvelopeTSConfig), 0o644); err != nil {
89
+ t.Fatalf("write tsconfig: %v", err)
90
+ }
91
+ for name, body := range map[string]string{
92
+ "a.ts": projectDependenciesNamedTupleMemberSourceA,
93
+ "pair.ts": projectDependenciesNamedTupleMemberSourcePair,
94
+ "id.ts": projectDependenciesNamedTupleMemberSourceId,
95
+ "unused.ts": projectDependenciesNamedTupleMemberSourceUnused,
96
+ } {
97
+ if err := os.WriteFile(filepath.Join(src, name), []byte(body), 0o644); err != nil {
98
+ t.Fatalf("write %s: %v", name, err)
99
+ }
100
+ }
101
+ return dir
102
+ }
103
+
104
+ const projectDependenciesNamedTupleMemberSourceA = `import typia from "typia";
105
+
106
+ import { Pair } from "./pair";
107
+
108
+ export const check = (input: unknown) => typia.is<Pair>(input);
109
+ `
110
+
111
+ const projectDependenciesNamedTupleMemberSourcePair = `import { Id } from "./id";
112
+ import { Unused } from "./unused";
113
+
114
+ export type Pair = [id: Id, count: number];
115
+
116
+ export type Spare = [label: Unused];
117
+ `
118
+
119
+ const projectDependenciesNamedTupleMemberSourceId = `export type Id = string;
120
+ `
121
+
122
+ const projectDependenciesNamedTupleMemberSourceUnused = `export type Unused = string;
123
+ `
@@ -0,0 +1,141 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "strings"
8
+ "testing"
9
+ )
10
+
11
+ // TestProjectDependenciesQualifiedBarrelTransform verifies a QUALIFIED type
12
+ // reference reports the barrel that selected its namespace half, not only the
13
+ // module declaring its terminus.
14
+ //
15
+ // `TestProjectDependenciesBarrelReexportTransform` pins the bare spelling, and
16
+ // it works because the checker answers `Alpha` with the local import alias, whose
17
+ // path metadataDependency_touchPath walks. `Kind.A` is answered with the enum
18
+ // member instead -- not an alias, no path to walk -- so the barrel between the
19
+ // caller and the enum went unreported while its value went straight into the
20
+ // validator as `"alpha" === input.kind`. Re-pointing that barrel at another enum
21
+ // changed the emitted constant with nothing left to invalidate the file
22
+ // (samchon/typia#2126, samchon/typia#2357).
23
+ //
24
+ // 1. Build a project where `main.ts` validates `Doc`, whose member type is
25
+ // `Kind.A` for a `Kind` imported from `barrel.ts`, which re-exports it from
26
+ // `kind.ts` and also re-exports an unconsumed `Unused` from `unused.ts`.
27
+ // 2. Run project transform mode and decode the JSON envelope.
28
+ // 3. Assert the emitted validator compares against the enum member's value, so
29
+ // the fixture really does read `kind.ts` into its output.
30
+ // 4. Assert `dependencies["src/main.ts"]` contains `src/barrel.ts` and
31
+ // `src/kind.ts`, and that `src/main.ts` is declared complete.
32
+ // 5. Assert it omits `src/unused.ts`, the sibling the reference never traverses.
33
+ func TestProjectDependenciesQualifiedBarrelTransform(t *testing.T) {
34
+ project := projectDependenciesQualifiedBarrelProject(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
+ if text := envelope.TypeScript["src/main.ts"]; !strings.Contains(text, `"alpha"`) {
54
+ t.Fatalf("the validator must compare against the enum member's value, so the fixture reads kind.ts into its output, got:\n%s", text)
55
+ }
56
+ entries := envelope.Dependencies["src/main.ts"]
57
+ found := map[string]bool{}
58
+ for _, entry := range entries {
59
+ found[entry] = true
60
+ }
61
+ if !found["src/kind.ts"] {
62
+ t.Fatalf("dependencies of src/main.ts must contain src/kind.ts, which declares the member the type names: %v", entries)
63
+ }
64
+ if !found["src/barrel.ts"] {
65
+ t.Fatalf("dependencies of src/main.ts must contain src/barrel.ts, which selects the enum the qualified name reads: %v", entries)
66
+ }
67
+ if found["src/unused.ts"] {
68
+ t.Fatalf("dependencies of src/main.ts must not contain src/unused.ts, a sibling the reference never traverses: %v", entries)
69
+ }
70
+ declared := false
71
+ for _, key := range envelope.DependenciesComplete {
72
+ if key == "src/main.ts" {
73
+ declared = true
74
+ }
75
+ }
76
+ if !declared {
77
+ t.Fatalf("src/main.ts must be declared complete, so the reported entry is the whole bound: %v", envelope.DependenciesComplete)
78
+ }
79
+ }
80
+
81
+ func projectDependenciesQualifiedBarrelProject(t *testing.T) string {
82
+ t.Helper()
83
+ root := ttscTypiaTestRepoRoot(t)
84
+ base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
85
+ if err := os.MkdirAll(base, 0o755); err != nil {
86
+ t.Fatalf("mkdir temp base: %v", err)
87
+ }
88
+ dir, err := os.MkdirTemp(base, "project-dependencies-qualified-barrel-")
89
+ if err != nil {
90
+ t.Fatalf("create temp fixture: %v", err)
91
+ }
92
+ t.Cleanup(func() { _ = os.RemoveAll(dir) })
93
+ src := filepath.Join(dir, "src")
94
+ if err := os.MkdirAll(src, 0o755); err != nil {
95
+ t.Fatalf("mkdir fixture src: %v", err)
96
+ }
97
+ if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(projectDependenciesEnvelopeTSConfig), 0o644); err != nil {
98
+ t.Fatalf("write tsconfig: %v", err)
99
+ }
100
+ for name, body := range map[string]string{
101
+ "main.ts": projectDependenciesQualifiedBarrelSourceMain,
102
+ "doc.ts": projectDependenciesQualifiedBarrelSourceDoc,
103
+ "barrel.ts": projectDependenciesQualifiedBarrelSourceBarrel,
104
+ "kind.ts": projectDependenciesQualifiedBarrelSourceKind,
105
+ "unused.ts": projectDependenciesQualifiedBarrelSourceUnused,
106
+ } {
107
+ if err := os.WriteFile(filepath.Join(src, name), []byte(body), 0o644); err != nil {
108
+ t.Fatalf("write %s: %v", name, err)
109
+ }
110
+ }
111
+ return dir
112
+ }
113
+
114
+ const projectDependenciesQualifiedBarrelSourceMain = `import typia from "typia";
115
+
116
+ import { Doc } from "./doc";
117
+
118
+ export const check = (input: unknown) => typia.is<Doc>(input);
119
+ `
120
+
121
+ const projectDependenciesQualifiedBarrelSourceDoc = `import { Kind } from "./barrel";
122
+
123
+ export interface Doc {
124
+ kind: Kind.A;
125
+ }
126
+ `
127
+
128
+ const projectDependenciesQualifiedBarrelSourceBarrel = `export { Kind } from "./kind";
129
+ export { Unused } from "./unused";
130
+ `
131
+
132
+ const projectDependenciesQualifiedBarrelSourceKind = `export enum Kind {
133
+ A = "alpha",
134
+ B = "beta",
135
+ }
136
+ `
137
+
138
+ const projectDependenciesQualifiedBarrelSourceUnused = `export enum Unused {
139
+ C = "gamma",
140
+ }
141
+ `
@@ -0,0 +1,127 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "testing"
8
+ )
9
+
10
+ // TestProjectDependenciesTypeParameterDefaultTransform verifies an interface's
11
+ // type-parameter default reports the file its alias comes from.
12
+ //
13
+ // `typia.is<Wrapper>()` over `interface Wrapper<T = Id>` validates whatever `Id`
14
+ // is, and the checker interns `type Id = string` as the bare intrinsic, so the
15
+ // resolved type carries no symbol the type graph can follow to `id.ts`
16
+ // (samchon/typia#2126). The equivalent `type AliasWrapper<T = Id> = { v: T }`
17
+ // always reported it, because a `type` alias surfaces its whole node; an
18
+ // interface surfaced only its index signatures, one declaration kind away from
19
+ // the same defect (samchon/typia#2357).
20
+ //
21
+ // 1. Build a project where `a.ts` validates `Wrapper` from `wrapper.ts`, whose
22
+ // type parameter defaults to `Id` from `id.ts`, and where the same file
23
+ // declares a second interface defaulting to `Unused` from `unused.ts`.
24
+ // 2. Run project transform mode and decode the JSON envelope.
25
+ // 3. Assert `dependencies["src/a.ts"]` contains `src/id.ts`, and that `a.ts`
26
+ // is declared complete so the narrowed bound is the one that carries it.
27
+ // 4. Assert it omits `src/unused.ts`, whose interface the call never reaches.
28
+ func TestProjectDependenciesTypeParameterDefaultTransform(t *testing.T) {
29
+ project := projectDependenciesTypeParameterDefaultProject(t)
30
+ out, errText, code := ttscTypiaTestCapture(func() int {
31
+ return runTransform([]string{
32
+ "--cwd", project,
33
+ "--tsconfig", "tsconfig.json",
34
+ "--output", "ts",
35
+ })
36
+ })
37
+ if code != 0 {
38
+ t.Fatalf("project transform failed: code=%d stderr=\n%s", code, errText)
39
+ }
40
+ var envelope struct {
41
+ Dependencies map[string][]string `json:"dependencies"`
42
+ DependenciesComplete []string `json:"dependenciesComplete"`
43
+ }
44
+ if err := json.Unmarshal([]byte(out), &envelope); err != nil {
45
+ t.Fatalf("decode envelope: %v\n%s", err, out)
46
+ }
47
+ entries := envelope.Dependencies["src/a.ts"]
48
+ found := map[string]bool{}
49
+ for _, entry := range entries {
50
+ found[entry] = true
51
+ }
52
+ if !found["src/wrapper.ts"] {
53
+ t.Fatalf("dependencies of src/a.ts must contain the declaring file src/wrapper.ts: %v", entries)
54
+ }
55
+ if !found["src/id.ts"] {
56
+ t.Fatalf("dependencies of src/a.ts must contain src/id.ts, which the type-parameter default names: %v", entries)
57
+ }
58
+ if found["src/unused.ts"] {
59
+ t.Fatalf("dependencies of src/a.ts must not contain src/unused.ts, which no consulted type reaches: %v", entries)
60
+ }
61
+ declared := false
62
+ for _, key := range envelope.DependenciesComplete {
63
+ if key == "src/a.ts" {
64
+ declared = true
65
+ }
66
+ }
67
+ if !declared {
68
+ t.Fatalf("src/a.ts must be declared complete, so the reported entry is the whole bound: %v", envelope.DependenciesComplete)
69
+ }
70
+ }
71
+
72
+ func projectDependenciesTypeParameterDefaultProject(t *testing.T) string {
73
+ t.Helper()
74
+ root := ttscTypiaTestRepoRoot(t)
75
+ base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
76
+ if err := os.MkdirAll(base, 0o755); err != nil {
77
+ t.Fatalf("mkdir temp base: %v", err)
78
+ }
79
+ dir, err := os.MkdirTemp(base, "project-dependencies-type-parameter-default-")
80
+ if err != nil {
81
+ t.Fatalf("create temp fixture: %v", err)
82
+ }
83
+ t.Cleanup(func() { _ = os.RemoveAll(dir) })
84
+ src := filepath.Join(dir, "src")
85
+ if err := os.MkdirAll(src, 0o755); err != nil {
86
+ t.Fatalf("mkdir fixture src: %v", err)
87
+ }
88
+ if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(projectDependenciesEnvelopeTSConfig), 0o644); err != nil {
89
+ t.Fatalf("write tsconfig: %v", err)
90
+ }
91
+ for name, body := range map[string]string{
92
+ "a.ts": projectDependenciesTypeParameterDefaultSourceA,
93
+ "wrapper.ts": projectDependenciesTypeParameterDefaultSourceWrapper,
94
+ "id.ts": projectDependenciesTypeParameterDefaultSourceId,
95
+ "unused.ts": projectDependenciesTypeParameterDefaultSourceUnused,
96
+ } {
97
+ if err := os.WriteFile(filepath.Join(src, name), []byte(body), 0o644); err != nil {
98
+ t.Fatalf("write %s: %v", name, err)
99
+ }
100
+ }
101
+ return dir
102
+ }
103
+
104
+ const projectDependenciesTypeParameterDefaultSourceA = `import typia from "typia";
105
+
106
+ import { Wrapper } from "./wrapper";
107
+
108
+ export const check = (input: unknown) => typia.is<Wrapper>(input);
109
+ `
110
+
111
+ const projectDependenciesTypeParameterDefaultSourceWrapper = `import { Id } from "./id";
112
+ import { Unused } from "./unused";
113
+
114
+ export interface Wrapper<T = Id> {
115
+ value: T;
116
+ }
117
+
118
+ export interface Spare<T = Unused> {
119
+ value: T;
120
+ }
121
+ `
122
+
123
+ const projectDependenciesTypeParameterDefaultSourceId = `export type Id = string;
124
+ `
125
+
126
+ const projectDependenciesTypeParameterDefaultSourceUnused = `export type Unused = number;
127
+ `