typia 9.7.0 → 9.7.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.
Files changed (73) hide show
  1. package/lib/programmers/json/JsonStringifyProgrammer.js +2 -2
  2. package/lib/programmers/json/JsonStringifyProgrammer.js.map +1 -1
  3. package/lib/programmers/json/JsonStringifyProgrammer.mjs +2 -2
  4. package/lib/tags/Constant.d.mts +32 -0
  5. package/lib/tags/Constant.d.ts +32 -0
  6. package/lib/tags/ContentMediaType.d.mts +17 -0
  7. package/lib/tags/ContentMediaType.d.ts +17 -0
  8. package/lib/tags/Default.d.mts +30 -0
  9. package/lib/tags/Default.d.ts +30 -0
  10. package/lib/tags/Example.d.mts +32 -0
  11. package/lib/tags/Example.d.ts +32 -0
  12. package/lib/tags/Examples.d.mts +40 -0
  13. package/lib/tags/Examples.d.ts +40 -0
  14. package/lib/tags/ExclusiveMaximum.d.mts +18 -0
  15. package/lib/tags/ExclusiveMaximum.d.ts +18 -0
  16. package/lib/tags/ExclusiveMinimum.d.mts +18 -0
  17. package/lib/tags/ExclusiveMinimum.d.ts +18 -0
  18. package/lib/tags/Format.d.mts +24 -0
  19. package/lib/tags/Format.d.ts +24 -0
  20. package/lib/tags/JsonSchemaPlugin.d.mts +26 -0
  21. package/lib/tags/JsonSchemaPlugin.d.ts +26 -0
  22. package/lib/tags/MaxItems.d.mts +19 -0
  23. package/lib/tags/MaxItems.d.ts +19 -0
  24. package/lib/tags/MaxLength.d.mts +12 -0
  25. package/lib/tags/MaxLength.d.ts +12 -0
  26. package/lib/tags/Maximum.d.mts +18 -0
  27. package/lib/tags/Maximum.d.ts +18 -0
  28. package/lib/tags/MinItems.d.mts +19 -0
  29. package/lib/tags/MinItems.d.ts +19 -0
  30. package/lib/tags/MinLength.d.mts +12 -0
  31. package/lib/tags/MinLength.d.ts +12 -0
  32. package/lib/tags/Minimum.d.mts +18 -0
  33. package/lib/tags/Minimum.d.ts +18 -0
  34. package/lib/tags/MultipleOf.d.mts +18 -0
  35. package/lib/tags/MultipleOf.d.ts +18 -0
  36. package/lib/tags/Pattern.d.mts +15 -0
  37. package/lib/tags/Pattern.d.ts +15 -0
  38. package/lib/tags/Sequence.d.mts +25 -0
  39. package/lib/tags/Sequence.d.ts +25 -0
  40. package/lib/tags/TagBase.d.mts +27 -0
  41. package/lib/tags/TagBase.d.ts +27 -0
  42. package/lib/tags/Type.d.mts +26 -0
  43. package/lib/tags/Type.d.ts +26 -0
  44. package/lib/tags/UniqueItems.d.mts +19 -0
  45. package/lib/tags/UniqueItems.d.ts +19 -0
  46. package/lib/transformers/ImportTransformer.js +119 -1
  47. package/lib/transformers/ImportTransformer.js.map +1 -1
  48. package/lib/transformers/ImportTransformer.mjs +119 -1
  49. package/package.json +1 -1
  50. package/src/IRandomGenerator.ts +13 -5
  51. package/src/programmers/json/JsonStringifyProgrammer.ts +2 -2
  52. package/src/tags/Constant.ts +32 -0
  53. package/src/tags/ContentMediaType.ts +17 -0
  54. package/src/tags/Default.ts +30 -0
  55. package/src/tags/Example.ts +32 -0
  56. package/src/tags/Examples.ts +40 -0
  57. package/src/tags/ExclusiveMaximum.ts +18 -0
  58. package/src/tags/ExclusiveMinimum.ts +18 -0
  59. package/src/tags/Format.ts +24 -0
  60. package/src/tags/JsonSchemaPlugin.ts +26 -0
  61. package/src/tags/MaxItems.ts +19 -0
  62. package/src/tags/MaxLength.ts +12 -0
  63. package/src/tags/Maximum.ts +18 -0
  64. package/src/tags/MinItems.ts +19 -0
  65. package/src/tags/MinLength.ts +12 -0
  66. package/src/tags/Minimum.ts +18 -0
  67. package/src/tags/MultipleOf.ts +18 -0
  68. package/src/tags/Pattern.ts +15 -0
  69. package/src/tags/Sequence.ts +25 -0
  70. package/src/tags/TagBase.ts +27 -0
  71. package/src/tags/Type.ts +26 -0
  72. package/src/tags/UniqueItems.ts +19 -0
  73. package/src/transformers/ImportTransformer.ts +178 -1
@@ -1,5 +1,29 @@
1
1
  import type { TagBase } from "./TagBase";
2
2
 
3
+ /**
4
+ * String format constraint tag.
5
+ *
6
+ * Validates strings against predefined formats for common use cases.
7
+ * This tag provides built-in validation for standard string formats without
8
+ * needing to write custom regular expressions.
9
+ *
10
+ * Examples:
11
+ * type Email = string & Format<"email">; // user@example.com
12
+ * type WebURL = string & Format<"url">; // https://example.com
13
+ * type DateTime = string & Format<"date-time">; // 2024-01-15T10:30:00Z
14
+ *
15
+ * Supported formats include:
16
+ * - Network: email, url, hostname, ipv4, ipv6, uri
17
+ * - Identifiers: uuid, byte, password
18
+ * - Date/Time: date, time, date-time, duration
19
+ * - Data: json-pointer, regex
20
+ * - Internationalized: idn-email, idn-hostname, iri
21
+ *
22
+ * Note: This tag is mutually exclusive with the Pattern tag. You cannot use both
23
+ * Format and Pattern on the same type.
24
+ *
25
+ * @author Jeongho Nam - https://github.com/samchon
26
+ */
3
27
  export type Format<Value extends Format.Value> = TagBase<{
4
28
  target: "string";
5
29
  kind: "format";
@@ -1,5 +1,31 @@
1
1
  import { TagBase } from "./TagBase";
2
2
 
3
+ /**
4
+ * Injects custom properties into generated JSON Schema.
5
+ *
6
+ * The JsonSchemaPlugin tag allows you to add vendor-specific extensions or custom metadata
7
+ * to the generated JSON Schema output. These properties are merged at the root level of
8
+ * the schema and are commonly used for documentation, tooling hints, or API-specific metadata.
9
+ * The custom properties only affect schema generation and do not impact runtime validation.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // Add OpenAPI vendor extensions
14
+ * type UserId = string & JsonSchemaPlugin<{
15
+ * "x-internal-id": true,
16
+ * "x-deprecated": "Use UUID instead"
17
+ * }>;
18
+ *
19
+ * // Add custom documentation metadata
20
+ * type Price = number & JsonSchemaPlugin<{
21
+ * "x-format": "currency",
22
+ * "x-example": 19.99
23
+ * }>;
24
+ * ```
25
+ *
26
+ * @template Schema - Object containing custom properties to add to the JSON Schema
27
+ * @author Jeongho Nam - https://github.com/samchon
28
+ */
3
29
  export type JsonSchemaPlugin<Schema extends object> = TagBase<{
4
30
  target: "string" | "boolean" | "bigint" | "number" | "array" | "object";
5
31
  kind: "jsonPlugin";
@@ -1,5 +1,24 @@
1
1
  import { TagBase } from "./TagBase";
2
2
 
3
+ /**
4
+ * Maximum items validation tag for arrays.
5
+ *
6
+ * Enforces that an array contains at most the specified number of items.
7
+ * This tag is useful for limiting array sizes, such as restricting the
8
+ * number of uploaded files, limiting selections in a form, or capping
9
+ * the size of collections to prevent performance issues.
10
+ *
11
+ * @example
12
+ * // Allow maximum 5 file uploads
13
+ * type FileList = File[] & MaxItems<5>;
14
+ *
15
+ * @example
16
+ * // Limit tags to 10 items
17
+ * type ProductTags = string[] & MaxItems<10>;
18
+ *
19
+ * @template Value - The maximum number of items allowed
20
+ * @author Jeongho Nam - https://github.com/samchon
21
+ */
3
22
  export type MaxItems<Value extends number> = TagBase<{
4
23
  target: "array";
5
24
  kind: "maxItems";
@@ -1,5 +1,17 @@
1
1
  import { TagBase } from "./TagBase";
2
2
 
3
+ /**
4
+ * String maximum length constraint tag.
5
+ *
6
+ * Validates that a string's length is less than or equal to the specified value.
7
+ * This tag enforces an upper limit on the number of characters in a string.
8
+ *
9
+ * Examples:
10
+ * type ShortComment = string & MaxLength<200>; // Comment limited to 200 characters
11
+ * type ZipCode = string & MaxLength<10>; // Zip code with max 10 characters
12
+ *
13
+ * @author Jeongho Nam - https://github.com/samchon
14
+ */
3
15
  export type MaxLength<Value extends number> = TagBase<{
4
16
  target: "string";
5
17
  kind: "maxLength";
@@ -1,5 +1,23 @@
1
1
  import { TagBase } from "./TagBase";
2
2
 
3
+ /**
4
+ * Maximum value constraint tag.
5
+ *
6
+ * Enforces that a numeric value must be less than or equal to the specified maximum.
7
+ * This constraint validates that the input value satisfies: input <= maximum.
8
+ *
9
+ * Example usage:
10
+ * ```typescript
11
+ * type Percentage = number & tags.Maximum<100>; // Must be <= 100
12
+ * type SmallInt = bigint & tags.Maximum<255n>; // BigInt must be <= 255
13
+ * ```
14
+ *
15
+ * Note: This tag is mutually exclusive with ExclusiveMaximum. You cannot apply both
16
+ * Maximum and ExclusiveMaximum constraints to the same property.
17
+ *
18
+ * @template Value - The maximum value constraint (number or bigint literal)
19
+ * @author Jeongho Nam - https://github.com/samchon
20
+ */
3
21
  export type Maximum<Value extends number | bigint> = TagBase<{
4
22
  target: Value extends bigint ? "bigint" : "number";
5
23
  kind: "maximum";
@@ -1,5 +1,24 @@
1
1
  import { TagBase } from "./TagBase";
2
2
 
3
+ /**
4
+ * Minimum items validation tag for arrays.
5
+ *
6
+ * Enforces that an array contains at least the specified number of items.
7
+ * This tag is useful for ensuring arrays have a minimum length requirement,
8
+ * such as requiring at least one item in a list or a minimum number of
9
+ * selections in a multi-choice field.
10
+ *
11
+ * @example
12
+ * // Require at least 1 item in the array
13
+ * type Tags = string[] & MinItems<1>;
14
+ *
15
+ * @example
16
+ * // Require at least 3 selections
17
+ * type MultipleChoice = number[] & MinItems<3>;
18
+ *
19
+ * @template Value - The minimum number of items required
20
+ * @author Jeongho Nam - https://github.com/samchon
21
+ */
3
22
  export type MinItems<Value extends number> = TagBase<{
4
23
  target: "array";
5
24
  kind: "minItems";
@@ -1,5 +1,17 @@
1
1
  import { TagBase } from "./TagBase";
2
2
 
3
+ /**
4
+ * String minimum length constraint tag.
5
+ *
6
+ * Validates that a string's length is greater than or equal to the specified value.
7
+ * This tag ensures that string values meet a minimum character count requirement.
8
+ *
9
+ * Examples:
10
+ * type Username = string & MinLength<3>; // Username must be at least 3 characters
11
+ * type Password = string & MinLength<8>; // Password must be at least 8 characters
12
+ *
13
+ * @author Jeongho Nam - https://github.com/samchon
14
+ */
3
15
  export type MinLength<Value extends number> = TagBase<{
4
16
  target: "string";
5
17
  kind: "minLength";
@@ -1,5 +1,23 @@
1
1
  import { TagBase } from "./TagBase";
2
2
 
3
+ /**
4
+ * Minimum value constraint tag.
5
+ *
6
+ * Enforces that a numeric value must be greater than or equal to the specified minimum.
7
+ * This constraint validates that the input value satisfies: input >= minimum.
8
+ *
9
+ * Example usage:
10
+ * ```typescript
11
+ * type Age = number & tags.Minimum<0>; // Age must be 0 or greater
12
+ * type Balance = bigint & tags.Minimum<0n>; // BigInt balance must be non-negative
13
+ * ```
14
+ *
15
+ * Note: This tag is mutually exclusive with ExclusiveMinimum. You cannot apply both
16
+ * Minimum and ExclusiveMinimum constraints to the same property.
17
+ *
18
+ * @template Value - The minimum value constraint (number or bigint literal)
19
+ * @author Jeongho Nam - https://github.com/samchon
20
+ */
3
21
  export type Minimum<Value extends number | bigint> = TagBase<{
4
22
  target: Value extends bigint ? "bigint" : "number";
5
23
  kind: "minimum";
@@ -1,5 +1,23 @@
1
1
  import { TagBase } from "./TagBase";
2
2
 
3
+ /**
4
+ * Multiple of constraint tag.
5
+ *
6
+ * Enforces that a numeric value must be an exact multiple of the specified divisor.
7
+ * This constraint validates that the input value satisfies: input % divisor === 0.
8
+ *
9
+ * Example usage:
10
+ * ```typescript
11
+ * type EvenNumber = number & tags.MultipleOf<2>; // Must be even (2, 4, 6, ...)
12
+ * type DollarAmount = number & tags.MultipleOf<0.01>; // Must be in cents
13
+ * ```
14
+ *
15
+ * Common use cases include validating even/odd numbers, currency amounts,
16
+ * time intervals, or any value that must align to specific increments.
17
+ *
18
+ * @template Value - The divisor value that input must be a multiple of (number or bigint literal)
19
+ * @author Jeongho Nam - https://github.com/samchon
20
+ */
3
21
  export type MultipleOf<Value extends number | bigint> = TagBase<{
4
22
  target: Value extends bigint ? "bigint" : "number";
5
23
  kind: "multipleOf";
@@ -1,5 +1,20 @@
1
1
  import { TagBase } from "./TagBase";
2
2
 
3
+ /**
4
+ * String pattern (regular expression) constraint tag.
5
+ *
6
+ * Validates that a string matches a specified regular expression pattern.
7
+ * Use this tag to enforce custom string formats through regex validation.
8
+ *
9
+ * Examples:
10
+ * type PhoneNumber = string & Pattern<"^\\d{3}-\\d{3}-\\d{4}$">; // 123-456-7890
11
+ * type HexColor = string & Pattern<"^#[0-9A-Fa-f]{6}$">; // #FF5733
12
+ *
13
+ * Note: This tag is mutually exclusive with the Format tag. You cannot use both
14
+ * Pattern and Format on the same type.
15
+ *
16
+ * @author Jeongho Nam - https://github.com/samchon
17
+ */
3
18
  export type Pattern<Value extends string> = TagBase<{
4
19
  target: "string";
5
20
  kind: "pattern";
@@ -1,5 +1,30 @@
1
1
  import { TagBase } from "./TagBase";
2
2
 
3
+ /**
4
+ * Assigns unique field numbers for Protocol Buffer serialization.
5
+ *
6
+ * In Protocol Buffer encoding, each field in a message must have a unique numeric identifier.
7
+ * The Sequence tag assigns these field numbers to TypeScript properties, enabling proper
8
+ * Protocol Buffer serialization and deserialization. Field numbers 1-15 require only one
9
+ * byte to encode, making them ideal for frequently used fields. Numbers 19000-19999 are
10
+ * reserved by the Protocol Buffer specification and should not be used.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * interface User {
15
+ * id: string & Sequence<1>; // Most frequent field uses 1
16
+ * email: string & Sequence<2>; // Common fields use low numbers
17
+ * createdAt: number & Sequence<3>;
18
+ * metadata?: object & Sequence<10>; // Optional fields work too
19
+ * }
20
+ *
21
+ * // Generate Protocol Buffer message
22
+ * const message = typia.protobuf.message<User>();
23
+ * ```
24
+ *
25
+ * @template N - Field number (positive integer from 1 to 536,870,911, excluding 19000-19999)
26
+ * @author Jeongho Nam - https://github.com/samchon
27
+ */
3
28
  export type Sequence<N extends number> = TagBase<{
4
29
  target: "boolean" | "bigint" | "number" | "string" | "array" | "object";
5
30
  kind: "sequence";
@@ -1,3 +1,25 @@
1
+ /**
2
+ * Base type for all validation tags in typia.
3
+ *
4
+ * TagBase provides the foundation for all typia's validation tags. It attaches
5
+ * metadata to TypeScript types that typia's transformer processes at compile-time
6
+ * to generate optimized runtime validation code.
7
+ *
8
+ * @template Props - Tag properties that define validation behavior
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * // Custom tag example
13
+ * type MyCustomTag<Value extends number> = TagBase<{
14
+ * target: "number";
15
+ * kind: "MyCustom";
16
+ * value: Value;
17
+ * validate: `$input === ${Value}`;
18
+ * }>;
19
+ * ```
20
+ *
21
+ * @author Jeongho Nam - https://github.com/samchon
22
+ */
1
23
  export type TagBase<
2
24
  Props extends TagBase.IProps<any, any, any, any, any, any>,
3
25
  > = {
@@ -9,6 +31,11 @@ export type TagBase<
9
31
  "typia.tag"?: Props;
10
32
  };
11
33
  export namespace TagBase {
34
+ /**
35
+ * Properties interface for validation tags.
36
+ *
37
+ * @author Jeongho Nam - https://github.com/samchon
38
+ */
12
39
  export interface IProps<
13
40
  Target extends
14
41
  | "boolean"
package/src/tags/Type.ts CHANGED
@@ -1,5 +1,31 @@
1
1
  import { TagBase } from "./TagBase";
2
2
 
3
+ /**
4
+ * Type tag for specifying numeric bit-width representations.
5
+ *
6
+ * Constrains numeric types to specific bit-width formats used in systems programming
7
+ * and protocol buffers. Ensures numbers conform to platform-specific representations.
8
+ *
9
+ * Supported types:
10
+ * - `int32`: 32-bit signed integer (-2^31 to 2^31-1)
11
+ * - `uint32`: 32-bit unsigned integer (0 to 2^32-1)
12
+ * - `int64`: 64-bit signed integer (supports both bigint and number)
13
+ * - `uint64`: 64-bit unsigned integer (supports both bigint and number)
14
+ * - `float`: 32-bit floating point (single precision)
15
+ * - `double`: 64-bit floating point (double precision, default JS number)
16
+ *
17
+ * @template Value - The numeric type representation
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * type Score = number & Type<"int32">; // -2,147,483,648 to 2,147,483,647
22
+ * type UserId = number & Type<"uint32">; // 0 to 4,294,967,295
23
+ * type FileSize = bigint & Type<"int64">; // Large file sizes
24
+ * type Coordinate = number & Type<"double">; // High precision coordinates
25
+ * ```
26
+ *
27
+ * @author Jeongho Nam - https://github.com/samchon
28
+ */
3
29
  export type Type<
4
30
  Value extends "int32" | "uint32" | "int64" | "uint64" | "float" | "double",
5
31
  > = TagBase<{
@@ -1,5 +1,24 @@
1
1
  import { TagBase } from "./TagBase";
2
2
 
3
+ /**
4
+ * Unique items validation tag for arrays.
5
+ *
6
+ * Enforces that all items in an array are unique, preventing duplicate values.
7
+ * Uniqueness is determined using strict equality (===) for primitives and
8
+ * deep structural comparison for objects and arrays. This means two objects
9
+ * with the same properties and values are considered duplicates.
10
+ *
11
+ * @example
12
+ * // Ensure all IDs are unique
13
+ * type UserIds = number[] & UniqueItems;
14
+ *
15
+ * @example
16
+ * // Prevent duplicate email addresses
17
+ * type EmailList = string[] & UniqueItems;
18
+ *
19
+ * @template Value - Boolean flag to enable/disable uniqueness validation (defaults to true)
20
+ * @author Jeongho Nam - https://github.com/samchon
21
+ */
3
22
  export type UniqueItems<Value extends boolean = true> = TagBase<{
4
23
  target: "array";
5
24
  kind: "uniqueItems";
@@ -26,7 +26,8 @@ export namespace ImportTransformer {
26
26
  );
27
27
  const to: string = from.replace(props.top, props.to);
28
28
 
29
- return ts.visitEachChild(
29
+ // First pass: transform relative imports
30
+ let transformedFile = ts.visitEachChild(
30
31
  props.file,
31
32
  (node) =>
32
33
  transform_node({
@@ -37,6 +38,11 @@ export namespace ImportTransformer {
37
38
  }),
38
39
  props.context,
39
40
  );
41
+
42
+ // Second pass: remove unused typia imports
43
+ transformedFile = removeUnusedTypiaImports(transformedFile);
44
+
45
+ return transformedFile;
40
46
  };
41
47
 
42
48
  const transform_node = (props: {
@@ -79,3 +85,174 @@ const get_directory_path = (file: string): string => {
79
85
  split.pop();
80
86
  return path.resolve(split.join(path.sep));
81
87
  };
88
+
89
+ /**
90
+ * Remove unused typia imports that are only used in transformable calls
91
+ */
92
+ const removeUnusedTypiaImports = (file: ts.SourceFile): ts.SourceFile => {
93
+ // Find typia imports and collect all identifiers
94
+ interface ImportMetadata {
95
+ declaration: ts.ImportDeclaration;
96
+ default: boolean;
97
+ }
98
+ const imports: Map<string, ImportMetadata> = new Map();
99
+ for (const stmt of file.statements) {
100
+ if (
101
+ ts.isImportDeclaration(stmt) === false ||
102
+ ts.isStringLiteral(stmt.moduleSpecifier) === false ||
103
+ stmt.moduleSpecifier.text !== "typia" ||
104
+ stmt.importClause === undefined
105
+ )
106
+ continue;
107
+
108
+ // Track default import (import typia from 'typia')
109
+ if (stmt.importClause.name)
110
+ imports.set(stmt.importClause.name.text, {
111
+ declaration: stmt,
112
+ default: true,
113
+ });
114
+
115
+ // Track named imports (import { tags } from 'typia') - keep these
116
+ if (
117
+ stmt.importClause.namedBindings &&
118
+ ts.isNamedImports(stmt.importClause.namedBindings)
119
+ )
120
+ for (const element of stmt.importClause.namedBindings.elements)
121
+ imports.set(element.name.text, {
122
+ declaration: stmt,
123
+ default: false,
124
+ });
125
+ }
126
+ if (imports.size === 0) return file; // No typia imports to check
127
+
128
+ // Find usage of typia identifiers that are NOT transformable calls
129
+ const nonTransformableUsage = new Set<string>();
130
+ const checkUsage = (node: ts.Node) => {
131
+ if (ts.isIdentifier(node) && imports.has(node.text)) {
132
+ const identifier: string = node.text;
133
+ // Check if this identifier is being used as part of a property access
134
+ if (
135
+ node.parent &&
136
+ ts.isPropertyAccessExpression(node.parent) &&
137
+ node.parent.expression === node
138
+ ) {
139
+ // This is typia.something - check if it's a transformable call pattern
140
+ if (!isLikelyTransformableCall(node.parent))
141
+ nonTransformableUsage.add(identifier);
142
+ } else
143
+ // Direct usage of the typia identifier (not as property access)
144
+ // This is definitely non-transformable usage
145
+ nonTransformableUsage.add(identifier);
146
+ }
147
+ ts.forEachChild(node, checkUsage);
148
+ };
149
+
150
+ // Check all statements except import declarations
151
+ for (const stmt of file.statements)
152
+ if (
153
+ !ts.isImportDeclaration(stmt) ||
154
+ !ts.isStringLiteral(stmt.moduleSpecifier) ||
155
+ stmt.moduleSpecifier.text !== "typia"
156
+ )
157
+ checkUsage(stmt);
158
+
159
+ // Update import statements
160
+ const newStatements: ts.Statement[] = file.statements
161
+ .map((stmt) => {
162
+ if (
163
+ ts.isImportDeclaration(stmt) &&
164
+ ts.isStringLiteral(stmt.moduleSpecifier) &&
165
+ stmt.moduleSpecifier.text === "typia" &&
166
+ stmt.importClause
167
+ ) {
168
+ const newImportClause = filterTypiaImportClause(
169
+ stmt.importClause,
170
+ nonTransformableUsage,
171
+ );
172
+ if (newImportClause)
173
+ return ts.factory.createImportDeclaration(
174
+ stmt.modifiers,
175
+ newImportClause,
176
+ stmt.moduleSpecifier,
177
+ stmt.attributes,
178
+ );
179
+ return null; // Skip adding the import if all imports are unused
180
+ }
181
+ return stmt;
182
+ })
183
+ .filter((stmt) => stmt !== null);
184
+ return ts.factory.updateSourceFile(
185
+ file,
186
+ newStatements,
187
+ file.isDeclarationFile,
188
+ file.referencedFiles,
189
+ file.typeReferenceDirectives,
190
+ file.hasNoDefaultLib,
191
+ file.libReferenceDirectives,
192
+ );
193
+ };
194
+
195
+ /**
196
+ * Check if a property access expression looks like a transformable typia call
197
+ * This uses heuristics to detect patterns like typia.xxx(), typia.namespace.xxx()
198
+ */
199
+ const isLikelyTransformableCall = (
200
+ node: ts.PropertyAccessExpression,
201
+ ): boolean => {
202
+ // Check if this is eventually part of a call expression
203
+ let current: ts.Node = node;
204
+
205
+ // Walk up the chain to find if this leads to a call expression
206
+ // Handle patterns like: typia.xxx(), typia.namespace.xxx()
207
+ while (ts.isPropertyAccessExpression(current)) {
208
+ current = current.parent;
209
+ }
210
+
211
+ // If the final result is a call expression, this is likely transformable
212
+ if (
213
+ ts.isCallExpression(current) &&
214
+ (current.expression === node ||
215
+ (ts.isPropertyAccessExpression(current.expression) &&
216
+ isTypiaPropertyChain(current.expression)))
217
+ ) {
218
+ return true;
219
+ }
220
+
221
+ return false;
222
+ };
223
+
224
+ /**
225
+ * Check if a property access expression is part of a typia.xxx.yyy chain
226
+ */
227
+ const isTypiaPropertyChain = (node: ts.PropertyAccessExpression): boolean => {
228
+ let current: ts.Expression = node;
229
+
230
+ while (ts.isPropertyAccessExpression(current)) {
231
+ current = current.expression;
232
+ }
233
+
234
+ return ts.isIdentifier(current) && current.text === "typia";
235
+ };
236
+
237
+ /**
238
+ * Filter import clause to remove unused default imports
239
+ */
240
+ const filterTypiaImportClause = (
241
+ importClause: ts.ImportClause,
242
+ usedImports: Set<string>,
243
+ ): ts.ImportClause | undefined => {
244
+ const hasDefaultImport =
245
+ importClause.name && usedImports.has(importClause.name.text);
246
+ const namedBindings = importClause.namedBindings; // Always keep named bindings like { tags }
247
+
248
+ // Return undefined if no imports are used
249
+ if (!hasDefaultImport && !namedBindings) {
250
+ return undefined;
251
+ }
252
+
253
+ return ts.factory.createImportClause(
254
+ importClause.isTypeOnly,
255
+ hasDefaultImport ? importClause.name : undefined,
256
+ namedBindings,
257
+ );
258
+ };