typia 9.7.2 → 10.0.0-dev.20251107

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 (116) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +153 -153
  3. package/lib/factories/ProtobufFactory.js +1 -1
  4. package/lib/factories/ProtobufFactory.mjs +1 -1
  5. package/lib/programmers/internal/json_schema_station.d.mts +2 -2
  6. package/lib/programmers/internal/json_schema_station.d.ts +2 -2
  7. package/lib/programmers/llm/LlmApplicationProgrammer.js +5 -1
  8. package/lib/programmers/llm/LlmApplicationProgrammer.js.map +1 -1
  9. package/lib/programmers/llm/LlmApplicationProgrammer.mjs +5 -1
  10. package/lib/programmers/llm/LlmSchemaProgrammer.js +1 -4
  11. package/lib/programmers/llm/LlmSchemaProgrammer.js.map +1 -1
  12. package/lib/programmers/llm/LlmSchemaProgrammer.mjs +1 -35
  13. package/package.json +121 -121
  14. package/src/AssertionGuard.ts +41 -41
  15. package/src/CamelCase.ts +75 -75
  16. package/src/IRandomGenerator.ts +337 -337
  17. package/src/IReadableURLSearchParams.ts +9 -9
  18. package/src/PascalCase.ts +71 -71
  19. package/src/Primitive.ts +90 -90
  20. package/src/Resolved.ts +72 -72
  21. package/src/SnakeCase.ts +127 -127
  22. package/src/TypeGuardError.ts +216 -216
  23. package/src/factories/MetadataCollection.ts +270 -270
  24. package/src/factories/MetadataCommentTagFactory.ts +632 -632
  25. package/src/factories/MetadataFactory.ts +402 -402
  26. package/src/factories/ProtobufFactory.ts +873 -873
  27. package/src/functional.ts +705 -705
  28. package/src/http.ts +972 -972
  29. package/src/internal/_ProtobufReader.ts +188 -188
  30. package/src/internal/_ProtobufSizer.ts +137 -137
  31. package/src/internal/_ProtobufWriter.ts +135 -135
  32. package/src/internal/_jsonStringifyString.ts +42 -42
  33. package/src/json.ts +643 -643
  34. package/src/llm.ts +615 -615
  35. package/src/misc.ts +594 -594
  36. package/src/module.ts +889 -889
  37. package/src/notations.ts +751 -751
  38. package/src/programmers/FeatureProgrammer.ts +605 -605
  39. package/src/programmers/ImportProgrammer.ts +179 -179
  40. package/src/programmers/RandomProgrammer.ts +1195 -1195
  41. package/src/programmers/helpers/ProtobufWire.ts +34 -34
  42. package/src/programmers/internal/check_array_length.ts +43 -43
  43. package/src/programmers/internal/check_bigint.ts +46 -46
  44. package/src/programmers/internal/check_dynamic_key.ts +197 -197
  45. package/src/programmers/internal/check_dynamic_properties.ts +231 -231
  46. package/src/programmers/internal/check_everything.ts +21 -21
  47. package/src/programmers/internal/check_native.ts +23 -23
  48. package/src/programmers/internal/check_number.ts +108 -108
  49. package/src/programmers/internal/check_object.ts +72 -72
  50. package/src/programmers/internal/check_string.ts +46 -46
  51. package/src/programmers/internal/check_template.ts +46 -46
  52. package/src/programmers/internal/check_union_array_like.ts +331 -331
  53. package/src/programmers/internal/decode_union_object.ts +110 -110
  54. package/src/programmers/internal/feature_object_entries.ts +59 -59
  55. package/src/programmers/internal/json_schema_escaped.ts +78 -78
  56. package/src/programmers/internal/json_schema_object.ts +150 -150
  57. package/src/programmers/internal/json_schema_station.ts +2 -2
  58. package/src/programmers/internal/metadata_to_pattern.ts +40 -40
  59. package/src/programmers/internal/postfix_of_tuple.ts +3 -3
  60. package/src/programmers/internal/prune_object_properties.ts +69 -69
  61. package/src/programmers/internal/stringify_dynamic_properties.ts +158 -158
  62. package/src/programmers/internal/stringify_native.ts +5 -5
  63. package/src/programmers/internal/stringify_regular_properties.ts +77 -77
  64. package/src/programmers/internal/template_to_pattern.ts +21 -21
  65. package/src/programmers/internal/wrap_metadata_rest_tuple.ts +21 -21
  66. package/src/programmers/json/JsonStringifyProgrammer.ts +1124 -1124
  67. package/src/programmers/llm/LlmApplicationProgrammer.ts +10 -1
  68. package/src/programmers/llm/LlmSchemaProgrammer.ts +2 -7
  69. package/src/protobuf.ts +820 -820
  70. package/src/reflect.ts +46 -46
  71. package/src/schemas/json/IJsonApplication.ts +77 -77
  72. package/src/schemas/json/IJsonSchemaCollection.ts +212 -212
  73. package/src/schemas/json/IJsonSchemaUnit.ts +263 -263
  74. package/src/schemas/metadata/IMetadataTypeTag.ts +14 -14
  75. package/src/schemas/metadata/Metadata.ts +669 -669
  76. package/src/schemas/metadata/MetadataAliasType.ts +57 -57
  77. package/src/schemas/metadata/MetadataApplication.ts +40 -40
  78. package/src/schemas/metadata/MetadataArray.ts +47 -47
  79. package/src/schemas/metadata/MetadataArrayType.ts +51 -51
  80. package/src/schemas/metadata/MetadataAtomic.ts +85 -85
  81. package/src/schemas/metadata/MetadataEscaped.ts +45 -45
  82. package/src/schemas/metadata/MetadataFunction.ts +45 -45
  83. package/src/schemas/metadata/MetadataObject.ts +46 -46
  84. package/src/schemas/metadata/MetadataObjectType.ts +137 -137
  85. package/src/schemas/metadata/MetadataParameter.ts +52 -52
  86. package/src/schemas/metadata/MetadataProperty.ts +53 -53
  87. package/src/schemas/metadata/MetadataTemplate.ts +78 -78
  88. package/src/schemas/metadata/MetadataTuple.ts +28 -28
  89. package/src/schemas/metadata/MetadataTupleType.ts +61 -61
  90. package/src/tags/Constant.ts +47 -47
  91. package/src/tags/ContentMediaType.ts +27 -27
  92. package/src/tags/Default.ts +52 -52
  93. package/src/tags/Example.ts +56 -56
  94. package/src/tags/Examples.ts +56 -56
  95. package/src/tags/ExclusiveMaximum.ts +44 -44
  96. package/src/tags/ExclusiveMinimum.ts +44 -44
  97. package/src/tags/Format.ts +78 -78
  98. package/src/tags/JsonSchemaPlugin.ts +36 -36
  99. package/src/tags/MaxItems.ts +31 -31
  100. package/src/tags/MaxLength.ts +25 -25
  101. package/src/tags/Maximum.ts +39 -39
  102. package/src/tags/MinItems.ts +31 -31
  103. package/src/tags/MinLength.ts +25 -25
  104. package/src/tags/Minimum.ts +39 -39
  105. package/src/tags/MultipleOf.ts +42 -42
  106. package/src/tags/Pattern.ts +49 -49
  107. package/src/tags/Sequence.ts +37 -37
  108. package/src/tags/TagBase.ts +102 -102
  109. package/src/tags/Type.ts +64 -64
  110. package/src/tags/UniqueItems.ts +34 -34
  111. package/src/tags/internal/FormatCheatSheet.ts +71 -71
  112. package/src/transformers/ITransformOptions.ts +70 -70
  113. package/src/transformers/ImportTransformer.ts +253 -253
  114. package/src/transformers/NoTransformConfigurationError.ts +16 -16
  115. package/src/transformers/features/llm/LlmApplicationTransformer.ts +224 -224
  116. package/src/typings/Equal.ts +18 -18
@@ -1,70 +1,70 @@
1
- export interface ITransformOptions {
2
- /**
3
- * Whether to validate finite number or not.
4
- *
5
- * If configured true, number typed values would be validated by
6
- * Number.isNaN().
7
- *
8
- * However, whatever you configure, it would be ignored when marshaling or
9
- * parsing.
10
- *
11
- * - When marshaling, always be true
12
- *
13
- * - AssertStringify()
14
- * - ValidateEncode()
15
- * - When parsing, always be false
16
- *
17
- * - AssertParse()
18
- * - IsDecode()
19
- *
20
- * @default false
21
- */
22
- finite?: undefined | boolean;
23
-
24
- /**
25
- * Whether to validate finite number or not.
26
- *
27
- * If configured true, number typed values would be validated by
28
- * Number.isFinite().
29
- *
30
- * However, whatever you configure, it can be ignored in below case.
31
- *
32
- * - When `finite` option is true, this option would be ignored
33
- * - When marshaling, always be true
34
- *
35
- * - AssertStringify()
36
- * - ValidateEncode()
37
- * - When parsing, always be false
38
- *
39
- * - AssertParse()
40
- * - IsDecode()
41
- *
42
- * @default false
43
- */
44
- numeric?: undefined | boolean;
45
-
46
- /**
47
- * Whether to validate functional type or not.
48
- *
49
- * However, whatever you configure, it becomes false when marshaling or
50
- * parsing.
51
- *
52
- * @default false
53
- */
54
- functional?: undefined | boolean;
55
-
56
- /**
57
- * Whether to check undefined value or not.
58
- *
59
- * JavaScript can assign `undefined` value to a specific property and it is an
60
- * issue when validating without allowing superfluous properties. Should
61
- * undefined value assigned superfluous property be allowed or not?
62
- *
63
- * Note that, this option only works on {@link equals} function. Other function
64
- * like {@link assertEquals} or {@link validateEquals} would ignore this option
65
- * value and always allow the `undefined` value.
66
- *
67
- * @default true
68
- */
69
- undefined?: undefined | boolean;
70
- }
1
+ export interface ITransformOptions {
2
+ /**
3
+ * Whether to validate finite number or not.
4
+ *
5
+ * If configured true, number typed values would be validated by
6
+ * Number.isNaN().
7
+ *
8
+ * However, whatever you configure, it would be ignored when marshaling or
9
+ * parsing.
10
+ *
11
+ * - When marshaling, always be true
12
+ *
13
+ * - AssertStringify()
14
+ * - ValidateEncode()
15
+ * - When parsing, always be false
16
+ *
17
+ * - AssertParse()
18
+ * - IsDecode()
19
+ *
20
+ * @default false
21
+ */
22
+ finite?: undefined | boolean;
23
+
24
+ /**
25
+ * Whether to validate finite number or not.
26
+ *
27
+ * If configured true, number typed values would be validated by
28
+ * Number.isFinite().
29
+ *
30
+ * However, whatever you configure, it can be ignored in below case.
31
+ *
32
+ * - When `finite` option is true, this option would be ignored
33
+ * - When marshaling, always be true
34
+ *
35
+ * - AssertStringify()
36
+ * - ValidateEncode()
37
+ * - When parsing, always be false
38
+ *
39
+ * - AssertParse()
40
+ * - IsDecode()
41
+ *
42
+ * @default false
43
+ */
44
+ numeric?: undefined | boolean;
45
+
46
+ /**
47
+ * Whether to validate functional type or not.
48
+ *
49
+ * However, whatever you configure, it becomes false when marshaling or
50
+ * parsing.
51
+ *
52
+ * @default false
53
+ */
54
+ functional?: undefined | boolean;
55
+
56
+ /**
57
+ * Whether to check undefined value or not.
58
+ *
59
+ * JavaScript can assign `undefined` value to a specific property and it is an
60
+ * issue when validating without allowing superfluous properties. Should
61
+ * undefined value assigned superfluous property be allowed or not?
62
+ *
63
+ * Note that, this option only works on {@link equals} function. Other function
64
+ * like {@link assertEquals} or {@link validateEquals} would ignore this option
65
+ * value and always allow the `undefined` value.
66
+ *
67
+ * @default true
68
+ */
69
+ undefined?: undefined | boolean;
70
+ }
@@ -1,253 +1,253 @@
1
- import path from "path";
2
- import ts from "typescript";
3
-
4
- export namespace ImportTransformer {
5
- export const transform =
6
- (props: { from: string; to: string }) =>
7
- (context: ts.TransformationContext) =>
8
- (file: ts.SourceFile) =>
9
- transform_file({
10
- top: props.from,
11
- to: props.to,
12
- context,
13
- file,
14
- });
15
-
16
- const transform_file = (props: {
17
- top: string;
18
- to: string;
19
- context: ts.TransformationContext;
20
- file: ts.SourceFile;
21
- }): ts.SourceFile => {
22
- if (props.file.isDeclarationFile) return props.file;
23
-
24
- const from: string = get_directory_path(
25
- path.resolve(props.file.getSourceFile().fileName),
26
- );
27
- const to: string = from.replace(props.top, props.to);
28
-
29
- // First pass: transform relative imports
30
- let transformedFile = ts.visitEachChild(
31
- props.file,
32
- (node) =>
33
- transform_node({
34
- top: props.top,
35
- from,
36
- to,
37
- node,
38
- }),
39
- props.context,
40
- );
41
-
42
- // Second pass: remove unused typia imports
43
- transformedFile = removeUnusedTypiaImports(transformedFile);
44
-
45
- return transformedFile;
46
- };
47
-
48
- const transform_node = (props: {
49
- top: string;
50
- from: string;
51
- to: string;
52
- node: ts.Node;
53
- }) => {
54
- if (
55
- !ts.isImportDeclaration(props.node) ||
56
- !ts.isStringLiteral(props.node.moduleSpecifier)
57
- )
58
- return props.node;
59
-
60
- const text: string = props.node.moduleSpecifier.text;
61
- if (text[0] !== ".") return props.node;
62
-
63
- const location: string = path.resolve(props.from, text);
64
- if (location.indexOf(props.top) === 0) return props.node;
65
-
66
- const replaced: string = (() => {
67
- const simple: string = path
68
- .relative(props.to, location)
69
- .split(path.sep)
70
- .join("/");
71
- return simple[0] === "." ? simple : `./${simple}`;
72
- })();
73
-
74
- return ts.factory.createImportDeclaration(
75
- undefined,
76
- props.node.importClause,
77
- ts.factory.createStringLiteral(replaced),
78
- props.node.assertClause,
79
- );
80
- };
81
- }
82
-
83
- const get_directory_path = (file: string): string => {
84
- const split: string[] = path.resolve(file).split(path.sep);
85
- split.pop();
86
- return path.resolve(split.join(path.sep));
87
- };
88
-
89
- /** Remove unused typia imports that are only used in transformable calls */
90
- const removeUnusedTypiaImports = (file: ts.SourceFile): ts.SourceFile => {
91
- // Find typia imports and collect all identifiers
92
- interface ImportMetadata {
93
- declaration: ts.ImportDeclaration;
94
- default: boolean;
95
- }
96
- const imports: Map<string, ImportMetadata> = new Map();
97
- for (const stmt of file.statements) {
98
- if (
99
- ts.isImportDeclaration(stmt) === false ||
100
- ts.isStringLiteral(stmt.moduleSpecifier) === false ||
101
- stmt.moduleSpecifier.text !== "typia" ||
102
- stmt.importClause === undefined
103
- )
104
- continue;
105
-
106
- // Track default import (import typia from 'typia')
107
- if (stmt.importClause.name)
108
- imports.set(stmt.importClause.name.text, {
109
- declaration: stmt,
110
- default: true,
111
- });
112
-
113
- // Track named imports (import { tags } from 'typia') - keep these
114
- if (
115
- stmt.importClause.namedBindings &&
116
- ts.isNamedImports(stmt.importClause.namedBindings)
117
- )
118
- for (const element of stmt.importClause.namedBindings.elements)
119
- imports.set(element.name.text, {
120
- declaration: stmt,
121
- default: false,
122
- });
123
- }
124
- if (imports.size === 0) return file; // No typia imports to check
125
-
126
- // Find usage of typia identifiers that are NOT transformable calls
127
- const nonTransformableUsage = new Set<string>();
128
- const checkUsage = (node: ts.Node) => {
129
- if (ts.isIdentifier(node) && imports.has(node.text)) {
130
- const identifier: string = node.text;
131
- // Check if this identifier is being used as part of a property access
132
- if (
133
- node.parent &&
134
- ts.isPropertyAccessExpression(node.parent) &&
135
- node.parent.expression === node
136
- ) {
137
- // This is typia.something - check if it's a transformable call pattern
138
- if (!isLikelyTransformableCall(node.parent))
139
- nonTransformableUsage.add(identifier);
140
- } else
141
- // Direct usage of the typia identifier (not as property access)
142
- // This is definitely non-transformable usage
143
- nonTransformableUsage.add(identifier);
144
- }
145
- ts.forEachChild(node, checkUsage);
146
- };
147
-
148
- // Check all statements except import declarations
149
- for (const stmt of file.statements)
150
- if (
151
- !ts.isImportDeclaration(stmt) ||
152
- !ts.isStringLiteral(stmt.moduleSpecifier) ||
153
- stmt.moduleSpecifier.text !== "typia"
154
- )
155
- checkUsage(stmt);
156
-
157
- // Update import statements
158
- const newStatements: ts.Statement[] = file.statements
159
- .map((stmt) => {
160
- if (
161
- ts.isImportDeclaration(stmt) &&
162
- ts.isStringLiteral(stmt.moduleSpecifier) &&
163
- stmt.moduleSpecifier.text === "typia" &&
164
- stmt.importClause
165
- ) {
166
- const newImportClause = filterTypiaImportClause(
167
- stmt.importClause,
168
- nonTransformableUsage,
169
- );
170
- if (newImportClause)
171
- return ts.factory.createImportDeclaration(
172
- stmt.modifiers,
173
- newImportClause,
174
- stmt.moduleSpecifier,
175
- stmt.attributes,
176
- );
177
- return null; // Skip adding the import if all imports are unused
178
- }
179
- return stmt;
180
- })
181
- .filter((stmt) => stmt !== null);
182
- return ts.factory.updateSourceFile(
183
- file,
184
- newStatements,
185
- file.isDeclarationFile,
186
- file.referencedFiles,
187
- file.typeReferenceDirectives,
188
- file.hasNoDefaultLib,
189
- file.libReferenceDirectives,
190
- );
191
- };
192
-
193
- /**
194
- * Check if a property access expression looks like a transformable typia call
195
- * This uses heuristics to detect patterns like typia.xxx(),
196
- * typia.namespace.xxx()
197
- */
198
- const isLikelyTransformableCall = (
199
- node: ts.PropertyAccessExpression,
200
- ): boolean => {
201
- // Check if this is eventually part of a call expression
202
- let current: ts.Node = node;
203
-
204
- // Walk up the chain to find if this leads to a call expression
205
- // Handle patterns like: typia.xxx(), typia.namespace.xxx()
206
- while (ts.isPropertyAccessExpression(current)) {
207
- current = current.parent;
208
- }
209
-
210
- // If the final result is a call expression, this is likely transformable
211
- if (
212
- ts.isCallExpression(current) &&
213
- (current.expression === node ||
214
- (ts.isPropertyAccessExpression(current.expression) &&
215
- isTypiaPropertyChain(current.expression)))
216
- ) {
217
- return true;
218
- }
219
-
220
- return false;
221
- };
222
-
223
- /** Check if a property access expression is part of a typia.xxx.yyy chain */
224
- const isTypiaPropertyChain = (node: ts.PropertyAccessExpression): boolean => {
225
- let current: ts.Expression = node;
226
-
227
- while (ts.isPropertyAccessExpression(current)) {
228
- current = current.expression;
229
- }
230
-
231
- return ts.isIdentifier(current) && current.text === "typia";
232
- };
233
-
234
- /** Filter import clause to remove unused default imports */
235
- const filterTypiaImportClause = (
236
- importClause: ts.ImportClause,
237
- usedImports: Set<string>,
238
- ): ts.ImportClause | undefined => {
239
- const hasDefaultImport =
240
- importClause.name && usedImports.has(importClause.name.text);
241
- const namedBindings = importClause.namedBindings; // Always keep named bindings like { tags }
242
-
243
- // Return undefined if no imports are used
244
- if (!hasDefaultImport && !namedBindings) {
245
- return undefined;
246
- }
247
-
248
- return ts.factory.createImportClause(
249
- importClause.isTypeOnly,
250
- hasDefaultImport ? importClause.name : undefined,
251
- namedBindings,
252
- );
253
- };
1
+ import path from "path";
2
+ import ts from "typescript";
3
+
4
+ export namespace ImportTransformer {
5
+ export const transform =
6
+ (props: { from: string; to: string }) =>
7
+ (context: ts.TransformationContext) =>
8
+ (file: ts.SourceFile) =>
9
+ transform_file({
10
+ top: props.from,
11
+ to: props.to,
12
+ context,
13
+ file,
14
+ });
15
+
16
+ const transform_file = (props: {
17
+ top: string;
18
+ to: string;
19
+ context: ts.TransformationContext;
20
+ file: ts.SourceFile;
21
+ }): ts.SourceFile => {
22
+ if (props.file.isDeclarationFile) return props.file;
23
+
24
+ const from: string = get_directory_path(
25
+ path.resolve(props.file.getSourceFile().fileName),
26
+ );
27
+ const to: string = from.replace(props.top, props.to);
28
+
29
+ // First pass: transform relative imports
30
+ let transformedFile = ts.visitEachChild(
31
+ props.file,
32
+ (node) =>
33
+ transform_node({
34
+ top: props.top,
35
+ from,
36
+ to,
37
+ node,
38
+ }),
39
+ props.context,
40
+ );
41
+
42
+ // Second pass: remove unused typia imports
43
+ transformedFile = removeUnusedTypiaImports(transformedFile);
44
+
45
+ return transformedFile;
46
+ };
47
+
48
+ const transform_node = (props: {
49
+ top: string;
50
+ from: string;
51
+ to: string;
52
+ node: ts.Node;
53
+ }) => {
54
+ if (
55
+ !ts.isImportDeclaration(props.node) ||
56
+ !ts.isStringLiteral(props.node.moduleSpecifier)
57
+ )
58
+ return props.node;
59
+
60
+ const text: string = props.node.moduleSpecifier.text;
61
+ if (text[0] !== ".") return props.node;
62
+
63
+ const location: string = path.resolve(props.from, text);
64
+ if (location.indexOf(props.top) === 0) return props.node;
65
+
66
+ const replaced: string = (() => {
67
+ const simple: string = path
68
+ .relative(props.to, location)
69
+ .split(path.sep)
70
+ .join("/");
71
+ return simple[0] === "." ? simple : `./${simple}`;
72
+ })();
73
+
74
+ return ts.factory.createImportDeclaration(
75
+ undefined,
76
+ props.node.importClause,
77
+ ts.factory.createStringLiteral(replaced),
78
+ props.node.assertClause,
79
+ );
80
+ };
81
+ }
82
+
83
+ const get_directory_path = (file: string): string => {
84
+ const split: string[] = path.resolve(file).split(path.sep);
85
+ split.pop();
86
+ return path.resolve(split.join(path.sep));
87
+ };
88
+
89
+ /** Remove unused typia imports that are only used in transformable calls */
90
+ const removeUnusedTypiaImports = (file: ts.SourceFile): ts.SourceFile => {
91
+ // Find typia imports and collect all identifiers
92
+ interface ImportMetadata {
93
+ declaration: ts.ImportDeclaration;
94
+ default: boolean;
95
+ }
96
+ const imports: Map<string, ImportMetadata> = new Map();
97
+ for (const stmt of file.statements) {
98
+ if (
99
+ ts.isImportDeclaration(stmt) === false ||
100
+ ts.isStringLiteral(stmt.moduleSpecifier) === false ||
101
+ stmt.moduleSpecifier.text !== "typia" ||
102
+ stmt.importClause === undefined
103
+ )
104
+ continue;
105
+
106
+ // Track default import (import typia from 'typia')
107
+ if (stmt.importClause.name)
108
+ imports.set(stmt.importClause.name.text, {
109
+ declaration: stmt,
110
+ default: true,
111
+ });
112
+
113
+ // Track named imports (import { tags } from 'typia') - keep these
114
+ if (
115
+ stmt.importClause.namedBindings &&
116
+ ts.isNamedImports(stmt.importClause.namedBindings)
117
+ )
118
+ for (const element of stmt.importClause.namedBindings.elements)
119
+ imports.set(element.name.text, {
120
+ declaration: stmt,
121
+ default: false,
122
+ });
123
+ }
124
+ if (imports.size === 0) return file; // No typia imports to check
125
+
126
+ // Find usage of typia identifiers that are NOT transformable calls
127
+ const nonTransformableUsage = new Set<string>();
128
+ const checkUsage = (node: ts.Node) => {
129
+ if (ts.isIdentifier(node) && imports.has(node.text)) {
130
+ const identifier: string = node.text;
131
+ // Check if this identifier is being used as part of a property access
132
+ if (
133
+ node.parent &&
134
+ ts.isPropertyAccessExpression(node.parent) &&
135
+ node.parent.expression === node
136
+ ) {
137
+ // This is typia.something - check if it's a transformable call pattern
138
+ if (!isLikelyTransformableCall(node.parent))
139
+ nonTransformableUsage.add(identifier);
140
+ } else
141
+ // Direct usage of the typia identifier (not as property access)
142
+ // This is definitely non-transformable usage
143
+ nonTransformableUsage.add(identifier);
144
+ }
145
+ ts.forEachChild(node, checkUsage);
146
+ };
147
+
148
+ // Check all statements except import declarations
149
+ for (const stmt of file.statements)
150
+ if (
151
+ !ts.isImportDeclaration(stmt) ||
152
+ !ts.isStringLiteral(stmt.moduleSpecifier) ||
153
+ stmt.moduleSpecifier.text !== "typia"
154
+ )
155
+ checkUsage(stmt);
156
+
157
+ // Update import statements
158
+ const newStatements: ts.Statement[] = file.statements
159
+ .map((stmt) => {
160
+ if (
161
+ ts.isImportDeclaration(stmt) &&
162
+ ts.isStringLiteral(stmt.moduleSpecifier) &&
163
+ stmt.moduleSpecifier.text === "typia" &&
164
+ stmt.importClause
165
+ ) {
166
+ const newImportClause = filterTypiaImportClause(
167
+ stmt.importClause,
168
+ nonTransformableUsage,
169
+ );
170
+ if (newImportClause)
171
+ return ts.factory.createImportDeclaration(
172
+ stmt.modifiers,
173
+ newImportClause,
174
+ stmt.moduleSpecifier,
175
+ stmt.attributes,
176
+ );
177
+ return null; // Skip adding the import if all imports are unused
178
+ }
179
+ return stmt;
180
+ })
181
+ .filter((stmt) => stmt !== null);
182
+ return ts.factory.updateSourceFile(
183
+ file,
184
+ newStatements,
185
+ file.isDeclarationFile,
186
+ file.referencedFiles,
187
+ file.typeReferenceDirectives,
188
+ file.hasNoDefaultLib,
189
+ file.libReferenceDirectives,
190
+ );
191
+ };
192
+
193
+ /**
194
+ * Check if a property access expression looks like a transformable typia call
195
+ * This uses heuristics to detect patterns like typia.xxx(),
196
+ * typia.namespace.xxx()
197
+ */
198
+ const isLikelyTransformableCall = (
199
+ node: ts.PropertyAccessExpression,
200
+ ): boolean => {
201
+ // Check if this is eventually part of a call expression
202
+ let current: ts.Node = node;
203
+
204
+ // Walk up the chain to find if this leads to a call expression
205
+ // Handle patterns like: typia.xxx(), typia.namespace.xxx()
206
+ while (ts.isPropertyAccessExpression(current)) {
207
+ current = current.parent;
208
+ }
209
+
210
+ // If the final result is a call expression, this is likely transformable
211
+ if (
212
+ ts.isCallExpression(current) &&
213
+ (current.expression === node ||
214
+ (ts.isPropertyAccessExpression(current.expression) &&
215
+ isTypiaPropertyChain(current.expression)))
216
+ ) {
217
+ return true;
218
+ }
219
+
220
+ return false;
221
+ };
222
+
223
+ /** Check if a property access expression is part of a typia.xxx.yyy chain */
224
+ const isTypiaPropertyChain = (node: ts.PropertyAccessExpression): boolean => {
225
+ let current: ts.Expression = node;
226
+
227
+ while (ts.isPropertyAccessExpression(current)) {
228
+ current = current.expression;
229
+ }
230
+
231
+ return ts.isIdentifier(current) && current.text === "typia";
232
+ };
233
+
234
+ /** Filter import clause to remove unused default imports */
235
+ const filterTypiaImportClause = (
236
+ importClause: ts.ImportClause,
237
+ usedImports: Set<string>,
238
+ ): ts.ImportClause | undefined => {
239
+ const hasDefaultImport =
240
+ importClause.name && usedImports.has(importClause.name.text);
241
+ const namedBindings = importClause.namedBindings; // Always keep named bindings like { tags }
242
+
243
+ // Return undefined if no imports are used
244
+ if (!hasDefaultImport && !namedBindings) {
245
+ return undefined;
246
+ }
247
+
248
+ return ts.factory.createImportClause(
249
+ importClause.isTypeOnly,
250
+ hasDefaultImport ? importClause.name : undefined,
251
+ namedBindings,
252
+ );
253
+ };
@@ -1,16 +1,16 @@
1
- /** @internal */
2
- export function NoTransformConfigurationError(name: string): never {
3
- throw new Error(
4
- [
5
- `Error on typia.${name}(): no transform has been configured.`,
6
- "",
7
- "Read and follow https://typia.io/docs/setup please.",
8
- "",
9
- [
10
- "If you've already completed the setup, it means there's",
11
- "a bug in your code. Run `tsc` command so that check what",
12
- "is wrong with your code.",
13
- ].join(" "),
14
- ].join("\n"),
15
- );
16
- }
1
+ /** @internal */
2
+ export function NoTransformConfigurationError(name: string): never {
3
+ throw new Error(
4
+ [
5
+ `Error on typia.${name}(): no transform has been configured.`,
6
+ "",
7
+ "Read and follow https://typia.io/docs/setup please.",
8
+ "",
9
+ [
10
+ "If you've already completed the setup, it means there's",
11
+ "a bug in your code. Run `tsc` command so that check what",
12
+ "is wrong with your code.",
13
+ ].join(" "),
14
+ ].join("\n"),
15
+ );
16
+ }