typia 13.3.0 → 14.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/internal/_createStandardSchema.d.ts +1 -2
- package/lib/internal/_createStandardSchema.js.map +1 -1
- package/lib/internal/_createStandardSchema.mjs.map +1 -1
- package/lib/internal/_randomPattern.js +20 -3
- package/lib/internal/_randomPattern.js.map +1 -1
- package/lib/internal/_randomPattern.mjs +5 -1
- package/lib/internal/_randomPattern.mjs.map +1 -1
- package/lib/internal/_randomStringLength.js +9 -2
- package/lib/internal/_randomStringLength.js.map +1 -1
- package/lib/internal/_randomStringLength.mjs +3 -1
- package/lib/internal/_randomStringLength.mjs.map +1 -1
- package/lib/module.d.ts +1 -2
- package/lib/module.js.map +1 -1
- package/lib/module.mjs.map +1 -1
- package/lib/re-exports.d.ts +1 -1
- package/lib/re-exports.js.map +1 -1
- package/lib/transform.mjs +1 -1
- package/native/cmd/ttsc-typia/create_assert_error_factory_arity_test.go +5 -2
- package/native/cmd/ttsc-typia/dependency_collector_virtual_scheme_test.go +52 -0
- package/native/cmd/ttsc-typia/dynamic_key_tags_transform_test.go +206 -0
- package/native/cmd/ttsc-typia/notation_kebab_case_transform_test.go +2 -2
- package/native/cmd/ttsc-typia/project_catalog_transform_coverage_test.go +84 -85
- package/native/cmd/ttsc-typia/project_dependencies_callee_argument_transform_test.go +177 -0
- package/native/cmd/ttsc-typia/project_dependencies_callee_barrel_transform_test.go +115 -0
- package/native/cmd/ttsc-typia/project_dependencies_callee_inference_transform_test.go +285 -0
- package/native/cmd/ttsc-typia/project_dependencies_callee_shape_transform_test.go +117 -0
- package/native/cmd/ttsc-typia/project_dependencies_callee_untransformed_barrel_transform_test.go +121 -0
- package/native/cmd/ttsc-typia/project_dependencies_complete_diagnostic_transform_test.go +116 -0
- package/native/cmd/ttsc-typia/project_dependencies_complete_envelope_transform_test.go +115 -0
- package/native/cmd/ttsc-typia/project_dependencies_complete_inferred_type_transform_test.go +127 -0
- package/native/cmd/ttsc-typia/project_dependencies_complete_replaced_library_transform_test.go +153 -0
- package/native/cmd/ttsc-typia/project_dependencies_complete_untouched_reprint_transform_test.go +155 -0
- package/native/cmd/ttsc-typia/project_dependencies_computed_key_transform_test.go +218 -0
- package/native/cmd/ttsc-typia/project_dependencies_custom_lib_name_transform_test.go +6 -5
- package/native/cmd/ttsc-typia/project_dependencies_enum_member_value_transform_test.go +131 -0
- package/native/cmd/ttsc-typia/project_dependencies_inferred_declaration_transform_test.go +159 -0
- package/native/cmd/ttsc-typia/project_dependencies_jsdoc_typedef_transform_test.go +134 -0
- package/native/cmd/ttsc-typia/project_dependencies_named_tuple_member_transform_test.go +123 -0
- package/native/cmd/ttsc-typia/project_dependencies_qualified_barrel_transform_test.go +141 -0
- package/native/cmd/ttsc-typia/project_dependencies_type_parameter_default_transform_test.go +127 -0
- package/native/cmd/ttsc-typia/template_literal_type_tags_transform_test.go +3 -1
- package/native/cmd/ttsc-typia/transform.go +195 -21
- package/native/cmd/ttsc-typia/transform_helpers_test.go +12 -0
- package/native/core/factories/MetadataCommentTagFactory.go +96 -27
- package/native/core/factories/metadata_comment_tag_factory_coverage_test.go +2 -2
- package/native/core/factories/metadata_comment_tag_factory_rejects_out_of_range_integer_test.go +124 -0
- package/native/core/programmers/AssertProgrammer.go +20 -6
- package/native/core/programmers/RandomProgrammer.go +78 -6
- package/native/core/programmers/ValidateProgrammer.go +21 -6
- package/native/core/programmers/helpers/RandomJoiner.go +8 -1
- package/native/core/programmers/iterate/check_dynamic_properties.go +177 -3
- package/native/core/programmers/iterate/check_object.go +12 -1
- package/native/core/schemas/metadata/MetadataDependency.go +613 -30
- package/native/transform/CallExpressionTransformer.go +16 -1
- package/package.json +4 -14
- package/src/internal/_createStandardSchema.ts +1 -2
- package/src/internal/_randomPattern.ts +18 -3
- package/src/internal/_randomStringLength.ts +9 -2
- package/src/module.ts +6 -2
- package/src/re-exports.ts +13 -0
- package/lib/executable/FileSystemIdentity.d.ts +0 -44
- package/lib/executable/FileSystemIdentity.js +0 -213
- package/lib/executable/FileSystemIdentity.js.map +0 -1
- package/lib/executable/FileSystemIdentity.mjs +0 -133
- package/lib/executable/FileSystemIdentity.mjs.map +0 -1
- package/lib/executable/TypiaGenerateWizard.d.ts +0 -9
- package/lib/executable/TypiaGenerateWizard.js +0 -852
- package/lib/executable/TypiaGenerateWizard.js.map +0 -1
- package/lib/executable/TypiaGenerateWizard.mjs +0 -618
- package/lib/executable/TypiaGenerateWizard.mjs.map +0 -1
- package/lib/executable/generate/ttsc.d.ts +0 -1
- package/lib/executable/generate/ttsc.js +0 -53
- package/lib/executable/generate/ttsc.js.map +0 -1
- package/lib/executable/generate/ttsc.mjs +0 -37
- package/lib/executable/generate/ttsc.mjs.map +0 -1
- package/lib/executable/typia.d.ts +0 -2
- package/lib/executable/typia.js +0 -84
- package/lib/executable/typia.js.map +0 -1
- package/lib/executable/typia.mjs +0 -60
- package/lib/executable/typia.mjs.map +0 -1
- package/src/executable/FileSystemIdentity.ts +0 -222
- package/src/executable/TypiaGenerateWizard.ts +0 -1133
- package/src/executable/generate/ttsc.ts +0 -70
- package/src/executable/typia.ts +0 -80
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"testing"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestProjectDependenciesComputedKeyTransform verifies a computed member key
|
|
11
|
+
// reports the file that decides the name, and withholds the declaration when
|
|
12
|
+
// that name is borrowed from a file the written surface cannot enumerate.
|
|
13
|
+
//
|
|
14
|
+
// `interface Doc { [Kind.A]: number }` emits `input.alpha`, so the enum
|
|
15
|
+
// member's value is read into the validator exactly the way an `enum` member's
|
|
16
|
+
// own value is — and the checker interns the resolved key as a bare string
|
|
17
|
+
// literal with no symbol to follow, so neither the declaration's written type
|
|
18
|
+
// nor the type graph names `kind.ts`. Renaming `Kind.A` changed the generated
|
|
19
|
+
// validator with no reported edge, which is invisible while a file keeps the
|
|
20
|
+
// reference closure and serves a stale validator once it is declared complete
|
|
21
|
+
// (samchon/typia#2126, samchon/typia#2357).
|
|
22
|
+
//
|
|
23
|
+
// 1. Build a project where `a.ts` validates `Doc`, whose only key is
|
|
24
|
+
// `[Kind.A]` reached through a barrel, and where `b.ts` validates `Spare`,
|
|
25
|
+
// whose key is a constant that borrows its value from a third file.
|
|
26
|
+
// 2. Run project transform mode and decode the JSON envelope.
|
|
27
|
+
// 3. Assert `dependencies["src/a.ts"]` contains `src/kind.ts` and the
|
|
28
|
+
// `src/barrel.ts` the reference traveled, and that `a.ts` is declared
|
|
29
|
+
// complete so the narrowed bound is the one that carries them.
|
|
30
|
+
// 4. Assert it omits `src/unused.ts`, a sibling export the reference never
|
|
31
|
+
// traverses.
|
|
32
|
+
// 5. Assert `b.ts` is withheld: `const BORROWED = VALUE` takes its value from
|
|
33
|
+
// a file no written key names, which is the same answer an enum member
|
|
34
|
+
// borrowing a value gets.
|
|
35
|
+
// 6. Assert `c.ts` stays declared: `["gamma"]` is written where it is used, so
|
|
36
|
+
// treating every computed key as unbounded would cost narrowing for a name
|
|
37
|
+
// no other file can decide.
|
|
38
|
+
// 7. Assert `d.ts` stays declared AND reports `src/keyconst.ts`: a bare name
|
|
39
|
+
// whose constant fixes its own value is the bounded twin of `b.ts`, and it
|
|
40
|
+
// is the only case that pins both halves of the bare-name branch.
|
|
41
|
+
func TestProjectDependenciesComputedKeyTransform(t *testing.T) {
|
|
42
|
+
project := projectDependenciesComputedKeyProject(t)
|
|
43
|
+
out, errText, code := ttscTypiaTestCapture(func() int {
|
|
44
|
+
return runTransform([]string{
|
|
45
|
+
"--cwd", project,
|
|
46
|
+
"--tsconfig", "tsconfig.json",
|
|
47
|
+
"--output", "ts",
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
if code != 0 {
|
|
51
|
+
t.Fatalf("project transform failed: code=%d stderr=\n%s", code, errText)
|
|
52
|
+
}
|
|
53
|
+
var envelope struct {
|
|
54
|
+
Dependencies map[string][]string `json:"dependencies"`
|
|
55
|
+
DependenciesComplete []string `json:"dependenciesComplete"`
|
|
56
|
+
}
|
|
57
|
+
if err := json.Unmarshal([]byte(out), &envelope); err != nil {
|
|
58
|
+
t.Fatalf("decode envelope: %v\n%s", err, out)
|
|
59
|
+
}
|
|
60
|
+
entries := envelope.Dependencies["src/a.ts"]
|
|
61
|
+
found := map[string]bool{}
|
|
62
|
+
for _, entry := range entries {
|
|
63
|
+
found[entry] = true
|
|
64
|
+
}
|
|
65
|
+
if !found["src/kind.ts"] {
|
|
66
|
+
t.Fatalf("dependencies of src/a.ts must contain src/kind.ts, whose enum member decides the emitted key: %v", entries)
|
|
67
|
+
}
|
|
68
|
+
if !found["src/barrel.ts"] {
|
|
69
|
+
t.Fatalf("dependencies of src/a.ts must contain src/barrel.ts, which selects the enum the key reads: %v", entries)
|
|
70
|
+
}
|
|
71
|
+
if found["src/unused.ts"] {
|
|
72
|
+
t.Fatalf("dependencies of src/a.ts must not contain src/unused.ts, a sibling the reference never traverses: %v", entries)
|
|
73
|
+
}
|
|
74
|
+
declared := map[string]bool{}
|
|
75
|
+
for _, key := range envelope.DependenciesComplete {
|
|
76
|
+
declared[key] = true
|
|
77
|
+
}
|
|
78
|
+
if !declared["src/a.ts"] {
|
|
79
|
+
t.Fatalf("src/a.ts must be declared complete, so the reported entry is the whole bound: %v", envelope.DependenciesComplete)
|
|
80
|
+
}
|
|
81
|
+
if declared["src/b.ts"] {
|
|
82
|
+
t.Fatalf("a key whose constant borrows its value from another file is not bounded by any written name, so src/b.ts must be withheld: %v", envelope.DependenciesComplete)
|
|
83
|
+
}
|
|
84
|
+
if !declared["src/c.ts"] {
|
|
85
|
+
t.Fatalf("a computed key written as a literal names no other file and must stay declared: %v", envelope.DependenciesComplete)
|
|
86
|
+
}
|
|
87
|
+
if !declared["src/d.ts"] {
|
|
88
|
+
t.Fatalf("a key whose constant fixes its own value is bounded and must stay declared: %v", envelope.DependenciesComplete)
|
|
89
|
+
}
|
|
90
|
+
bare := map[string]bool{}
|
|
91
|
+
for _, entry := range envelope.Dependencies["src/d.ts"] {
|
|
92
|
+
bare[entry] = true
|
|
93
|
+
}
|
|
94
|
+
if !bare["src/keyconst.ts"] {
|
|
95
|
+
t.Fatalf("dependencies of src/d.ts must contain src/keyconst.ts, whose constant decides the emitted key: %v", envelope.Dependencies["src/d.ts"])
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
func projectDependenciesComputedKeyProject(t *testing.T) string {
|
|
100
|
+
t.Helper()
|
|
101
|
+
root := ttscTypiaTestRepoRoot(t)
|
|
102
|
+
base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
|
|
103
|
+
if err := os.MkdirAll(base, 0o755); err != nil {
|
|
104
|
+
t.Fatalf("mkdir temp base: %v", err)
|
|
105
|
+
}
|
|
106
|
+
dir, err := os.MkdirTemp(base, "project-dependencies-computed-key-")
|
|
107
|
+
if err != nil {
|
|
108
|
+
t.Fatalf("create temp fixture: %v", err)
|
|
109
|
+
}
|
|
110
|
+
t.Cleanup(func() { _ = os.RemoveAll(dir) })
|
|
111
|
+
src := filepath.Join(dir, "src")
|
|
112
|
+
if err := os.MkdirAll(src, 0o755); err != nil {
|
|
113
|
+
t.Fatalf("mkdir fixture src: %v", err)
|
|
114
|
+
}
|
|
115
|
+
if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(projectDependenciesEnvelopeTSConfig), 0o644); err != nil {
|
|
116
|
+
t.Fatalf("write tsconfig: %v", err)
|
|
117
|
+
}
|
|
118
|
+
for name, body := range map[string]string{
|
|
119
|
+
"a.ts": projectDependenciesComputedKeySourceA,
|
|
120
|
+
"b.ts": projectDependenciesComputedKeySourceB,
|
|
121
|
+
"c.ts": projectDependenciesComputedKeySourceC,
|
|
122
|
+
"d.ts": projectDependenciesComputedKeySourceD,
|
|
123
|
+
"doc.ts": projectDependenciesComputedKeySourceDoc,
|
|
124
|
+
"spare.ts": projectDependenciesComputedKeySourceSpare,
|
|
125
|
+
"plain.ts": projectDependenciesComputedKeySourcePlain,
|
|
126
|
+
"bare.ts": projectDependenciesComputedKeySourceBare,
|
|
127
|
+
"keyconst.ts": projectDependenciesComputedKeySourceKeyConst,
|
|
128
|
+
"barrel.ts": projectDependenciesComputedKeySourceBarrel,
|
|
129
|
+
"kind.ts": projectDependenciesComputedKeySourceKind,
|
|
130
|
+
"unused.ts": projectDependenciesComputedKeySourceUnused,
|
|
131
|
+
"borrow.ts": projectDependenciesComputedKeySourceBorrow,
|
|
132
|
+
"value.ts": projectDependenciesComputedKeySourceValue,
|
|
133
|
+
} {
|
|
134
|
+
if err := os.WriteFile(filepath.Join(src, name), []byte(body), 0o644); err != nil {
|
|
135
|
+
t.Fatalf("write %s: %v", name, err)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return dir
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const projectDependenciesComputedKeySourceA = `import typia from "typia";
|
|
142
|
+
|
|
143
|
+
import { Doc } from "./doc";
|
|
144
|
+
|
|
145
|
+
export const check = (input: unknown) => typia.is<Doc>(input);
|
|
146
|
+
`
|
|
147
|
+
|
|
148
|
+
const projectDependenciesComputedKeySourceB = `import typia from "typia";
|
|
149
|
+
|
|
150
|
+
import { Spare } from "./spare";
|
|
151
|
+
|
|
152
|
+
export const check = (input: unknown) => typia.is<Spare>(input);
|
|
153
|
+
`
|
|
154
|
+
|
|
155
|
+
const projectDependenciesComputedKeySourceC = `import typia from "typia";
|
|
156
|
+
|
|
157
|
+
import { Plain } from "./plain";
|
|
158
|
+
|
|
159
|
+
export const check = (input: unknown) => typia.is<Plain>(input);
|
|
160
|
+
`
|
|
161
|
+
|
|
162
|
+
const projectDependenciesComputedKeySourcePlain = `export interface Plain {
|
|
163
|
+
["gamma"]: number;
|
|
164
|
+
}
|
|
165
|
+
`
|
|
166
|
+
|
|
167
|
+
const projectDependenciesComputedKeySourceD = `import typia from "typia";
|
|
168
|
+
|
|
169
|
+
import { Bare } from "./bare";
|
|
170
|
+
|
|
171
|
+
export const check = (input: unknown) => typia.is<Bare>(input);
|
|
172
|
+
`
|
|
173
|
+
|
|
174
|
+
const projectDependenciesComputedKeySourceBare = `import { KEY } from "./keyconst";
|
|
175
|
+
|
|
176
|
+
export interface Bare {
|
|
177
|
+
[KEY]: number;
|
|
178
|
+
}
|
|
179
|
+
`
|
|
180
|
+
|
|
181
|
+
const projectDependenciesComputedKeySourceKeyConst = `export const KEY = "epsilon";
|
|
182
|
+
`
|
|
183
|
+
|
|
184
|
+
const projectDependenciesComputedKeySourceDoc = `import { Kind } from "./barrel";
|
|
185
|
+
|
|
186
|
+
export interface Doc {
|
|
187
|
+
[Kind.A]: number;
|
|
188
|
+
}
|
|
189
|
+
`
|
|
190
|
+
|
|
191
|
+
const projectDependenciesComputedKeySourceSpare = `import { BORROWED } from "./borrow";
|
|
192
|
+
|
|
193
|
+
export interface Spare {
|
|
194
|
+
[BORROWED]: number;
|
|
195
|
+
}
|
|
196
|
+
`
|
|
197
|
+
|
|
198
|
+
const projectDependenciesComputedKeySourceBarrel = `export { Kind } from "./kind";
|
|
199
|
+
export { Unused } from "./unused";
|
|
200
|
+
`
|
|
201
|
+
|
|
202
|
+
const projectDependenciesComputedKeySourceKind = `export enum Kind {
|
|
203
|
+
A = "alpha",
|
|
204
|
+
}
|
|
205
|
+
`
|
|
206
|
+
|
|
207
|
+
const projectDependenciesComputedKeySourceUnused = `export enum Unused {
|
|
208
|
+
B = "beta",
|
|
209
|
+
}
|
|
210
|
+
`
|
|
211
|
+
|
|
212
|
+
const projectDependenciesComputedKeySourceBorrow = `import { VALUE } from "./value";
|
|
213
|
+
|
|
214
|
+
export const BORROWED = VALUE;
|
|
215
|
+
`
|
|
216
|
+
|
|
217
|
+
const projectDependenciesComputedKeySourceValue = `export const VALUE = "delta";
|
|
218
|
+
`
|
|
@@ -25,8 +25,12 @@ import (
|
|
|
25
25
|
// analysis touches real default libraries.
|
|
26
26
|
// 2. Run project transform mode and decode the JSON envelope.
|
|
27
27
|
// 3. Assert `dependencies["src/a.ts"]` contains `src/lib.custom.d.ts`.
|
|
28
|
-
// 4. Assert no compiler-owned default library
|
|
29
|
-
//
|
|
28
|
+
// 4. Assert no compiler-owned default library leaks into the entry. Under this
|
|
29
|
+
// fixture they are the program's virtual `bundled:///` sources, so the URI
|
|
30
|
+
// shape is what proves the classification still excludes them. Being outside
|
|
31
|
+
// the project is not evidence of one: the envelope reports a `node_modules`
|
|
32
|
+
// declaration file like any other, and the files that decide a callee's
|
|
33
|
+
// typia identity are exactly such files.
|
|
30
34
|
func TestProjectDependenciesCustomLibNameTransform(t *testing.T) {
|
|
31
35
|
project := projectDependenciesCustomLibNameProject(t)
|
|
32
36
|
out, errText, code := ttscTypiaTestCapture(func() int {
|
|
@@ -56,9 +60,6 @@ func TestProjectDependenciesCustomLibNameTransform(t *testing.T) {
|
|
|
56
60
|
if strings.Contains(entry, "://") {
|
|
57
61
|
t.Fatalf("dependencies must exclude virtual URI sources, got %q in %v", entry, entries)
|
|
58
62
|
}
|
|
59
|
-
if filepath.IsAbs(filepath.FromSlash(entry)) || strings.HasPrefix(entry, "../") {
|
|
60
|
-
t.Fatalf("dependencies must not report files outside the project, got %q in %v", entry, entries)
|
|
61
|
-
}
|
|
62
63
|
}
|
|
63
64
|
if !found["src/lib.custom.d.ts"] {
|
|
64
65
|
t.Fatalf("dependencies of src/a.ts must contain the project-owned src/lib.custom.d.ts: %v", entries)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"strings"
|
|
8
|
+
"testing"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// TestProjectDependenciesEnumMemberValueTransform verifies an enum whose member
|
|
12
|
+
// borrows its value from another file withholds the completeness declaration,
|
|
13
|
+
// while an enum that writes its own values keeps it.
|
|
14
|
+
//
|
|
15
|
+
// An enum member's value is read straight into the generated validator, so
|
|
16
|
+
// `Kind.A === input` becomes `"x" === input` and changes the moment the value
|
|
17
|
+
// changes. When the member writes a literal — including the negative form, which
|
|
18
|
+
// TypeScript reads as one constant — that value is written here and no other
|
|
19
|
+
// file can decide it. When it borrows one (`A = Other.X`), the value lives
|
|
20
|
+
// somewhere the type graph never names: the checker hands the analysis the
|
|
21
|
+
// resolved literal, not the reference that produced it (samchon/typia#2357).
|
|
22
|
+
//
|
|
23
|
+
// 1. Build a project with `plain.ts`, whose enum writes a string and a negative
|
|
24
|
+
// number, and `kind.ts`, whose enum takes a member value from `other.ts`.
|
|
25
|
+
// 2. Run project transform mode and decode the JSON envelope.
|
|
26
|
+
// 3. Assert both callers transformed into a literal comparison, so both enums
|
|
27
|
+
// really did reach the validator.
|
|
28
|
+
// 4. Assert the caller of the self-contained enum is declared complete and the
|
|
29
|
+
// caller of the borrowing enum is not.
|
|
30
|
+
func TestProjectDependenciesEnumMemberValueTransform(t *testing.T) {
|
|
31
|
+
project := projectDependenciesEnumMemberValueProject(t)
|
|
32
|
+
out, errText, code := ttscTypiaTestCapture(func() int {
|
|
33
|
+
return runTransform([]string{
|
|
34
|
+
"--cwd", project,
|
|
35
|
+
"--tsconfig", "tsconfig.json",
|
|
36
|
+
"--output", "ts",
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
if code != 0 {
|
|
40
|
+
t.Fatalf("project transform failed: code=%d stderr=\n%s", code, errText)
|
|
41
|
+
}
|
|
42
|
+
var envelope struct {
|
|
43
|
+
TypeScript map[string]string `json:"typescript"`
|
|
44
|
+
Dependencies map[string][]string `json:"dependencies"`
|
|
45
|
+
DependenciesComplete []string `json:"dependenciesComplete"`
|
|
46
|
+
}
|
|
47
|
+
if err := json.Unmarshal([]byte(out), &envelope); err != nil {
|
|
48
|
+
t.Fatalf("decode envelope: %v\n%s", err, out)
|
|
49
|
+
}
|
|
50
|
+
if text := envelope.TypeScript["src/own.ts"]; !strings.Contains(text, `"a" === input`) || !strings.Contains(text, "-1 === input") {
|
|
51
|
+
t.Fatalf("src/own.ts must compare against both written member values, got:\n%s", text)
|
|
52
|
+
}
|
|
53
|
+
if text := envelope.TypeScript["src/borrowed.ts"]; !strings.Contains(text, `"x" === input`) {
|
|
54
|
+
t.Fatalf("src/borrowed.ts must compare against the borrowed member value, got:\n%s", text)
|
|
55
|
+
}
|
|
56
|
+
declared := map[string]bool{}
|
|
57
|
+
for _, key := range envelope.DependenciesComplete {
|
|
58
|
+
declared[key] = true
|
|
59
|
+
}
|
|
60
|
+
if !declared["src/own.ts"] {
|
|
61
|
+
t.Fatalf("an enum that writes its own member values bounds them and must be declared complete: %v", envelope.DependenciesComplete)
|
|
62
|
+
}
|
|
63
|
+
if declared["src/borrowed.ts"] {
|
|
64
|
+
t.Fatalf("an enum member that borrows its value must withhold the declaration: %v", envelope.DependenciesComplete)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
func projectDependenciesEnumMemberValueProject(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-enum-member-value-")
|
|
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
|
+
"own.ts": projectDependenciesEnumMemberValueSourceOwn,
|
|
89
|
+
"borrowed.ts": projectDependenciesEnumMemberValueSourceBorrowed,
|
|
90
|
+
"plain.ts": projectDependenciesEnumMemberValueSourcePlain,
|
|
91
|
+
"kind.ts": projectDependenciesEnumMemberValueSourceKind,
|
|
92
|
+
"other.ts": projectDependenciesEnumMemberValueSourceOther,
|
|
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 projectDependenciesEnumMemberValueSourceOwn = `import typia from "typia";
|
|
102
|
+
|
|
103
|
+
import { Plain } from "./plain";
|
|
104
|
+
|
|
105
|
+
export const check = (input: unknown) => typia.is<Plain>(input);
|
|
106
|
+
`
|
|
107
|
+
|
|
108
|
+
const projectDependenciesEnumMemberValueSourceBorrowed = `import typia from "typia";
|
|
109
|
+
|
|
110
|
+
import { Kind } from "./kind";
|
|
111
|
+
|
|
112
|
+
export const check = (input: unknown) => typia.is<Kind>(input);
|
|
113
|
+
`
|
|
114
|
+
|
|
115
|
+
const projectDependenciesEnumMemberValueSourcePlain = `export enum Plain {
|
|
116
|
+
Text = "a",
|
|
117
|
+
Negative = -1,
|
|
118
|
+
}
|
|
119
|
+
`
|
|
120
|
+
|
|
121
|
+
const projectDependenciesEnumMemberValueSourceKind = `import { Other } from "./other";
|
|
122
|
+
|
|
123
|
+
export enum Kind {
|
|
124
|
+
Borrowed = Other.Value,
|
|
125
|
+
}
|
|
126
|
+
`
|
|
127
|
+
|
|
128
|
+
const projectDependenciesEnumMemberValueSourceOther = `export enum Other {
|
|
129
|
+
Value = "x",
|
|
130
|
+
}
|
|
131
|
+
`
|
|
@@ -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
|
+
`
|