@typia/transform 12.0.0-dev.20260309 → 12.0.0-dev.20260310
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/package.json +4 -4
- package/src/CallExpressionTransformer.ts +579 -579
- package/src/FileTransformer.ts +143 -143
- package/src/ITransformProps.ts +20 -20
- package/src/ImportTransformer.ts +262 -262
- package/src/NodeTransformer.ts +25 -25
- package/src/TransformerError.ts +85 -85
- package/src/TypiaGenerator.ts +172 -172
- package/src/features/AssertTransformer.ts +24 -24
- package/src/features/CreateAssertTransformer.ts +24 -24
- package/src/features/CreateIsTransformer.ts +18 -18
- package/src/features/CreateRandomTransformer.ts +42 -42
- package/src/features/CreateValidateTransformer.ts +18 -18
- package/src/features/IsTransformer.ts +18 -18
- package/src/features/RandomTransformer.ts +40 -40
- package/src/features/ValidateTransformer.ts +18 -18
- package/src/features/functional/FunctionalGenericTransformer.ts +55 -55
- package/src/features/http/CreateHttpAssertFormDataTransformer.ts +13 -13
- package/src/features/http/CreateHttpAssertHeadersTransformer.ts +13 -13
- package/src/features/http/CreateHttpAssertQueryTransformer.ts +13 -13
- package/src/features/http/CreateHttpFormDataTransformer.ts +13 -13
- package/src/features/http/CreateHttpHeadersTransformer.ts +13 -13
- package/src/features/http/CreateHttpIsFormDataTransformer.ts +13 -13
- package/src/features/http/CreateHttpIsHeadersTransformer.ts +13 -13
- package/src/features/http/CreateHttpIsQueryTransformer.ts +13 -13
- package/src/features/http/CreateHttpParameterTransformer.ts +13 -13
- package/src/features/http/CreateHttpQueryTransformer.ts +13 -13
- package/src/features/http/CreateHttpValidateFormDataTransformer.ts +13 -13
- package/src/features/http/CreateHttpValidateHeadersTransformer.ts +13 -13
- package/src/features/http/CreateHttpValidateQueryTransformer.ts +13 -13
- package/src/features/http/HttpAssertFormDataTransformer.ts +13 -13
- package/src/features/http/HttpAssertHeadersTransformer.ts +13 -13
- package/src/features/http/HttpAssertQueryTransformer.ts +13 -13
- package/src/features/http/HttpFormDataTransformer.ts +13 -13
- package/src/features/http/HttpHeadersTransformer.ts +13 -13
- package/src/features/http/HttpIsFormDataTransformer.ts +13 -13
- package/src/features/http/HttpIsHeadersTransformer.ts +13 -13
- package/src/features/http/HttpIsQueryTransformer.ts +13 -13
- package/src/features/http/HttpParameterTransformer.ts +13 -13
- package/src/features/http/HttpQueryTransformer.ts +13 -13
- package/src/features/http/HttpValidateFormDataTransformer.ts +13 -13
- package/src/features/http/HttpValidateHeadersTransformer.ts +13 -13
- package/src/features/http/HttpValidateQueryTransformer.ts +13 -13
- package/src/features/json/JsonApplicationTransformer.ts +99 -99
- package/src/features/json/JsonAssertParseTransformer.ts +13 -13
- package/src/features/json/JsonAssertStringifyTransformer.ts +13 -13
- package/src/features/json/JsonCreateAssertParseTransformer.ts +13 -13
- package/src/features/json/JsonCreateAssertStringifyTransformer.ts +13 -13
- package/src/features/json/JsonCreateIsParseTransformer.ts +13 -13
- package/src/features/json/JsonCreateIsStringifyTransformer.ts +13 -13
- package/src/features/json/JsonCreateStringifyTransformer.ts +13 -13
- package/src/features/json/JsonCreateValidateParseTransformer.ts +13 -13
- package/src/features/json/JsonCreateValidateStringifyProgrammer.ts +13 -13
- package/src/features/json/JsonIsParseTransformer.ts +13 -13
- package/src/features/json/JsonIsStringifyTransformer.ts +13 -13
- package/src/features/json/JsonSchemaTransformer.ts +120 -120
- package/src/features/json/JsonSchemasTransformer.ts +130 -130
- package/src/features/json/JsonStringifyTransformer.ts +13 -13
- package/src/features/json/JsonValidateParseTransformer.ts +13 -13
- package/src/features/json/JsonValidateStringifyTransformer.ts +13 -13
- package/src/features/llm/LlmApplicationTransformer.ts +224 -224
- package/src/features/llm/LlmCoerceTransformer.ts +95 -95
- package/src/features/llm/LlmControllerTransformer.ts +81 -81
- package/src/features/llm/LlmCreateCoerceTransformer.ts +84 -84
- package/src/features/llm/LlmCreateParseTransformer.ts +84 -84
- package/src/features/llm/LlmParametersTransformer.ts +76 -76
- package/src/features/llm/LlmParseTransformer.ts +95 -95
- package/src/features/llm/LlmSchemaTransformer.ts +87 -87
- package/src/features/misc/MiscAssertCloneTransformer.ts +13 -13
- package/src/features/misc/MiscAssertPruneTransformer.ts +13 -13
- package/src/features/misc/MiscCloneTransformer.ts +13 -13
- package/src/features/misc/MiscCreateAssertCloneTransformer.ts +13 -13
- package/src/features/misc/MiscCreateAssertPruneTransformer.ts +13 -13
- package/src/features/misc/MiscCreateCloneTransformer.ts +13 -13
- package/src/features/misc/MiscCreateIsCloneTransformer.ts +13 -13
- package/src/features/misc/MiscCreateIsPruneTransformer.ts +13 -13
- package/src/features/misc/MiscCreatePruneTransformer.ts +13 -13
- package/src/features/misc/MiscCreateValidateCloneTransformer.ts +13 -13
- package/src/features/misc/MiscCreateValidatePruneTransformer.ts +13 -13
- package/src/features/misc/MiscIsCloneTransformer.ts +13 -13
- package/src/features/misc/MiscIsPruneTransformer.ts +13 -13
- package/src/features/misc/MiscLiteralsTransformer.ts +34 -34
- package/src/features/misc/MiscPruneTransformer.ts +13 -13
- package/src/features/misc/MiscValidateCloneTransformer.ts +13 -13
- package/src/features/misc/MiscValidatePruneTransformer.ts +13 -13
- package/src/features/notations/NotationAssertGeneralTransformer.ts +19 -19
- package/src/features/notations/NotationCreateAssertGeneralTransformer.ts +19 -19
- package/src/features/notations/NotationCreateGeneralTransformer.ts +19 -19
- package/src/features/notations/NotationCreateIsGeneralTransformer.ts +19 -19
- package/src/features/notations/NotationCreateValidateGeneralTransformer.ts +19 -19
- package/src/features/notations/NotationGeneralTransformer.ts +18 -18
- package/src/features/notations/NotationIsGeneralTransformer.ts +19 -19
- package/src/features/notations/NotationValidateGeneralTransformer.ts +19 -19
- package/src/features/protobuf/ProtobufAssertDecodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufAssertEncodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufCreateAssertDecodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufCreateAssertEncodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufCreateDecodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufCreateEncodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufCreateIsDecodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufCreateIsEncodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufCreateValidateDecodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufCreateValidateEncodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufDecodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufEncodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufIsDecodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufIsEncodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufMessageTransformer.ts +34 -34
- package/src/features/protobuf/ProtobufValidateDecodeTransformer.ts +13 -13
- package/src/features/protobuf/ProtobufValidateEncodeTransformer.ts +13 -13
- package/src/features/reflect/ReflectMetadataTransformer.ts +69 -69
- package/src/features/reflect/ReflectNameTransformer.ts +81 -81
- package/src/features/reflect/ReflectSchemaTransformer.ts +65 -65
- package/src/features/reflect/ReflectSchemasTransformer.ts +69 -69
- package/src/index.ts +7 -7
- package/src/internal/GenericTransformer.ts +101 -101
- package/src/transform.ts +68 -68
package/src/ImportTransformer.ts
CHANGED
|
@@ -1,262 +1,262 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import ts from "typescript";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Transforms import paths for typia build output.
|
|
6
|
-
*
|
|
7
|
-
* Rewrites relative import paths when building typia packages. Also removes
|
|
8
|
-
* unused typia imports that only contained transformable function calls (since
|
|
9
|
-
* those are replaced at compile time).
|
|
10
|
-
*
|
|
11
|
-
* @author Jeongho Nam - https://github.com/samchon
|
|
12
|
-
*/
|
|
13
|
-
export namespace ImportTransformer {
|
|
14
|
-
export const transform =
|
|
15
|
-
(props: { from: string; to: string }) =>
|
|
16
|
-
(context: ts.TransformationContext) =>
|
|
17
|
-
(file: ts.SourceFile) =>
|
|
18
|
-
transform_file({
|
|
19
|
-
top: props.from,
|
|
20
|
-
to: props.to,
|
|
21
|
-
context,
|
|
22
|
-
file,
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const transform_file = (props: {
|
|
26
|
-
top: string;
|
|
27
|
-
to: string;
|
|
28
|
-
context: ts.TransformationContext;
|
|
29
|
-
file: ts.SourceFile;
|
|
30
|
-
}): ts.SourceFile => {
|
|
31
|
-
if (props.file.isDeclarationFile) return props.file;
|
|
32
|
-
|
|
33
|
-
const from: string = get_directory_path(
|
|
34
|
-
path.resolve(props.file.getSourceFile().fileName),
|
|
35
|
-
);
|
|
36
|
-
const to: string = from.replace(props.top, props.to);
|
|
37
|
-
|
|
38
|
-
// First pass: transform relative imports
|
|
39
|
-
let transformedFile = ts.visitEachChild(
|
|
40
|
-
props.file,
|
|
41
|
-
(node) =>
|
|
42
|
-
transform_node({
|
|
43
|
-
top: props.top,
|
|
44
|
-
from,
|
|
45
|
-
to,
|
|
46
|
-
node,
|
|
47
|
-
}),
|
|
48
|
-
props.context,
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
// Second pass: remove unused typia imports
|
|
52
|
-
transformedFile = removeUnusedTypiaImports(transformedFile);
|
|
53
|
-
|
|
54
|
-
return transformedFile;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const transform_node = (props: {
|
|
58
|
-
top: string;
|
|
59
|
-
from: string;
|
|
60
|
-
to: string;
|
|
61
|
-
node: ts.Node;
|
|
62
|
-
}) => {
|
|
63
|
-
if (
|
|
64
|
-
!ts.isImportDeclaration(props.node) ||
|
|
65
|
-
!ts.isStringLiteral(props.node.moduleSpecifier)
|
|
66
|
-
)
|
|
67
|
-
return props.node;
|
|
68
|
-
|
|
69
|
-
const text: string = props.node.moduleSpecifier.text;
|
|
70
|
-
if (text[0] !== ".") return props.node;
|
|
71
|
-
|
|
72
|
-
const location: string = path.resolve(props.from, text);
|
|
73
|
-
if (location.indexOf(props.top) === 0) return props.node;
|
|
74
|
-
|
|
75
|
-
const replaced: string = (() => {
|
|
76
|
-
const simple: string = path
|
|
77
|
-
.relative(props.to, location)
|
|
78
|
-
.split(path.sep)
|
|
79
|
-
.join("/");
|
|
80
|
-
return simple[0] === "." ? simple : `./${simple}`;
|
|
81
|
-
})();
|
|
82
|
-
|
|
83
|
-
return ts.factory.createImportDeclaration(
|
|
84
|
-
undefined,
|
|
85
|
-
props.node.importClause,
|
|
86
|
-
ts.factory.createStringLiteral(replaced),
|
|
87
|
-
props.node.assertClause,
|
|
88
|
-
);
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const get_directory_path = (file: string): string => {
|
|
93
|
-
const split: string[] = path.resolve(file).split(path.sep);
|
|
94
|
-
split.pop();
|
|
95
|
-
return path.resolve(split.join(path.sep));
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
/** Remove unused typia imports that are only used in transformable calls */
|
|
99
|
-
const removeUnusedTypiaImports = (file: ts.SourceFile): ts.SourceFile => {
|
|
100
|
-
// Find typia imports and collect all identifiers
|
|
101
|
-
interface ImportMetadata {
|
|
102
|
-
declaration: ts.ImportDeclaration;
|
|
103
|
-
default: boolean;
|
|
104
|
-
}
|
|
105
|
-
const imports: Map<string, ImportMetadata> = new Map();
|
|
106
|
-
for (const stmt of file.statements) {
|
|
107
|
-
if (
|
|
108
|
-
ts.isImportDeclaration(stmt) === false ||
|
|
109
|
-
ts.isStringLiteral(stmt.moduleSpecifier) === false ||
|
|
110
|
-
stmt.moduleSpecifier.text !== "typia" ||
|
|
111
|
-
stmt.importClause === undefined
|
|
112
|
-
)
|
|
113
|
-
continue;
|
|
114
|
-
|
|
115
|
-
// Track default import (import typia from 'typia')
|
|
116
|
-
if (stmt.importClause.name)
|
|
117
|
-
imports.set(stmt.importClause.name.text, {
|
|
118
|
-
declaration: stmt,
|
|
119
|
-
default: true,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// Track named imports (import { tags } from 'typia') - keep these
|
|
123
|
-
if (
|
|
124
|
-
stmt.importClause.namedBindings &&
|
|
125
|
-
ts.isNamedImports(stmt.importClause.namedBindings)
|
|
126
|
-
)
|
|
127
|
-
for (const element of stmt.importClause.namedBindings.elements)
|
|
128
|
-
imports.set(element.name.text, {
|
|
129
|
-
declaration: stmt,
|
|
130
|
-
default: false,
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
if (imports.size === 0) return file; // No typia imports to check
|
|
134
|
-
|
|
135
|
-
// Find usage of typia identifiers that are NOT transformable calls
|
|
136
|
-
const nonTransformableUsage = new Set<string>();
|
|
137
|
-
const checkUsage = (node: ts.Node) => {
|
|
138
|
-
if (ts.isIdentifier(node) && imports.has(node.text)) {
|
|
139
|
-
const identifier: string = node.text;
|
|
140
|
-
// Check if this identifier is being used as part of a property access
|
|
141
|
-
if (
|
|
142
|
-
node.parent &&
|
|
143
|
-
ts.isPropertyAccessExpression(node.parent) &&
|
|
144
|
-
node.parent.expression === node
|
|
145
|
-
) {
|
|
146
|
-
// This is typia.something - check if it's a transformable call pattern
|
|
147
|
-
if (!isLikelyTransformableCall(node.parent))
|
|
148
|
-
nonTransformableUsage.add(identifier);
|
|
149
|
-
} else
|
|
150
|
-
// Direct usage of the typia identifier (not as property access)
|
|
151
|
-
// This is definitely non-transformable usage
|
|
152
|
-
nonTransformableUsage.add(identifier);
|
|
153
|
-
}
|
|
154
|
-
ts.forEachChild(node, checkUsage);
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// Check all statements except import declarations
|
|
158
|
-
for (const stmt of file.statements)
|
|
159
|
-
if (
|
|
160
|
-
!ts.isImportDeclaration(stmt) ||
|
|
161
|
-
!ts.isStringLiteral(stmt.moduleSpecifier) ||
|
|
162
|
-
stmt.moduleSpecifier.text !== "typia"
|
|
163
|
-
)
|
|
164
|
-
checkUsage(stmt);
|
|
165
|
-
|
|
166
|
-
// Update import statements
|
|
167
|
-
const newStatements: ts.Statement[] = file.statements
|
|
168
|
-
.map((stmt) => {
|
|
169
|
-
if (
|
|
170
|
-
ts.isImportDeclaration(stmt) &&
|
|
171
|
-
ts.isStringLiteral(stmt.moduleSpecifier) &&
|
|
172
|
-
stmt.moduleSpecifier.text === "typia" &&
|
|
173
|
-
stmt.importClause
|
|
174
|
-
) {
|
|
175
|
-
const newImportClause = filterTypiaImportClause(
|
|
176
|
-
stmt.importClause,
|
|
177
|
-
nonTransformableUsage,
|
|
178
|
-
);
|
|
179
|
-
if (newImportClause)
|
|
180
|
-
return ts.factory.createImportDeclaration(
|
|
181
|
-
stmt.modifiers,
|
|
182
|
-
newImportClause,
|
|
183
|
-
stmt.moduleSpecifier,
|
|
184
|
-
stmt.attributes,
|
|
185
|
-
);
|
|
186
|
-
return null; // Skip adding the import if all imports are unused
|
|
187
|
-
}
|
|
188
|
-
return stmt;
|
|
189
|
-
})
|
|
190
|
-
.filter((stmt) => stmt !== null);
|
|
191
|
-
return ts.factory.updateSourceFile(
|
|
192
|
-
file,
|
|
193
|
-
newStatements,
|
|
194
|
-
file.isDeclarationFile,
|
|
195
|
-
file.referencedFiles,
|
|
196
|
-
file.typeReferenceDirectives,
|
|
197
|
-
file.hasNoDefaultLib,
|
|
198
|
-
file.libReferenceDirectives,
|
|
199
|
-
);
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Check if a property access expression looks like a transformable typia call
|
|
204
|
-
* This uses heuristics to detect patterns like typia.xxx(),
|
|
205
|
-
* typia.namespace.xxx()
|
|
206
|
-
*/
|
|
207
|
-
const isLikelyTransformableCall = (
|
|
208
|
-
node: ts.PropertyAccessExpression,
|
|
209
|
-
): boolean => {
|
|
210
|
-
// Check if this is eventually part of a call expression
|
|
211
|
-
let current: ts.Node = node;
|
|
212
|
-
|
|
213
|
-
// Walk up the chain to find if this leads to a call expression
|
|
214
|
-
// Handle patterns like: typia.xxx(), typia.namespace.xxx()
|
|
215
|
-
while (ts.isPropertyAccessExpression(current)) {
|
|
216
|
-
current = current.parent;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// If the final result is a call expression, this is likely transformable
|
|
220
|
-
if (
|
|
221
|
-
ts.isCallExpression(current) &&
|
|
222
|
-
(current.expression === node ||
|
|
223
|
-
(ts.isPropertyAccessExpression(current.expression) &&
|
|
224
|
-
isTypiaPropertyChain(current.expression)))
|
|
225
|
-
) {
|
|
226
|
-
return true;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return false;
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
/** Check if a property access expression is part of a typia.xxx.yyy chain */
|
|
233
|
-
const isTypiaPropertyChain = (node: ts.PropertyAccessExpression): boolean => {
|
|
234
|
-
let current: ts.Expression = node;
|
|
235
|
-
|
|
236
|
-
while (ts.isPropertyAccessExpression(current)) {
|
|
237
|
-
current = current.expression;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return ts.isIdentifier(current) && current.text === "typia";
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
/** Filter import clause to remove unused default imports */
|
|
244
|
-
const filterTypiaImportClause = (
|
|
245
|
-
importClause: ts.ImportClause,
|
|
246
|
-
usedImports: Set<string>,
|
|
247
|
-
): ts.ImportClause | undefined => {
|
|
248
|
-
const hasDefaultImport =
|
|
249
|
-
importClause.name && usedImports.has(importClause.name.text);
|
|
250
|
-
const namedBindings = importClause.namedBindings; // Always keep named bindings like { tags }
|
|
251
|
-
|
|
252
|
-
// Return undefined if no imports are used
|
|
253
|
-
if (!hasDefaultImport && !namedBindings) {
|
|
254
|
-
return undefined;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return ts.factory.createImportClause(
|
|
258
|
-
importClause.isTypeOnly,
|
|
259
|
-
hasDefaultImport ? importClause.name : undefined,
|
|
260
|
-
namedBindings,
|
|
261
|
-
);
|
|
262
|
-
};
|
|
1
|
+
import path from "path";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Transforms import paths for typia build output.
|
|
6
|
+
*
|
|
7
|
+
* Rewrites relative import paths when building typia packages. Also removes
|
|
8
|
+
* unused typia imports that only contained transformable function calls (since
|
|
9
|
+
* those are replaced at compile time).
|
|
10
|
+
*
|
|
11
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
12
|
+
*/
|
|
13
|
+
export namespace ImportTransformer {
|
|
14
|
+
export const transform =
|
|
15
|
+
(props: { from: string; to: string }) =>
|
|
16
|
+
(context: ts.TransformationContext) =>
|
|
17
|
+
(file: ts.SourceFile) =>
|
|
18
|
+
transform_file({
|
|
19
|
+
top: props.from,
|
|
20
|
+
to: props.to,
|
|
21
|
+
context,
|
|
22
|
+
file,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const transform_file = (props: {
|
|
26
|
+
top: string;
|
|
27
|
+
to: string;
|
|
28
|
+
context: ts.TransformationContext;
|
|
29
|
+
file: ts.SourceFile;
|
|
30
|
+
}): ts.SourceFile => {
|
|
31
|
+
if (props.file.isDeclarationFile) return props.file;
|
|
32
|
+
|
|
33
|
+
const from: string = get_directory_path(
|
|
34
|
+
path.resolve(props.file.getSourceFile().fileName),
|
|
35
|
+
);
|
|
36
|
+
const to: string = from.replace(props.top, props.to);
|
|
37
|
+
|
|
38
|
+
// First pass: transform relative imports
|
|
39
|
+
let transformedFile = ts.visitEachChild(
|
|
40
|
+
props.file,
|
|
41
|
+
(node) =>
|
|
42
|
+
transform_node({
|
|
43
|
+
top: props.top,
|
|
44
|
+
from,
|
|
45
|
+
to,
|
|
46
|
+
node,
|
|
47
|
+
}),
|
|
48
|
+
props.context,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Second pass: remove unused typia imports
|
|
52
|
+
transformedFile = removeUnusedTypiaImports(transformedFile);
|
|
53
|
+
|
|
54
|
+
return transformedFile;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const transform_node = (props: {
|
|
58
|
+
top: string;
|
|
59
|
+
from: string;
|
|
60
|
+
to: string;
|
|
61
|
+
node: ts.Node;
|
|
62
|
+
}) => {
|
|
63
|
+
if (
|
|
64
|
+
!ts.isImportDeclaration(props.node) ||
|
|
65
|
+
!ts.isStringLiteral(props.node.moduleSpecifier)
|
|
66
|
+
)
|
|
67
|
+
return props.node;
|
|
68
|
+
|
|
69
|
+
const text: string = props.node.moduleSpecifier.text;
|
|
70
|
+
if (text[0] !== ".") return props.node;
|
|
71
|
+
|
|
72
|
+
const location: string = path.resolve(props.from, text);
|
|
73
|
+
if (location.indexOf(props.top) === 0) return props.node;
|
|
74
|
+
|
|
75
|
+
const replaced: string = (() => {
|
|
76
|
+
const simple: string = path
|
|
77
|
+
.relative(props.to, location)
|
|
78
|
+
.split(path.sep)
|
|
79
|
+
.join("/");
|
|
80
|
+
return simple[0] === "." ? simple : `./${simple}`;
|
|
81
|
+
})();
|
|
82
|
+
|
|
83
|
+
return ts.factory.createImportDeclaration(
|
|
84
|
+
undefined,
|
|
85
|
+
props.node.importClause,
|
|
86
|
+
ts.factory.createStringLiteral(replaced),
|
|
87
|
+
props.node.assertClause,
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const get_directory_path = (file: string): string => {
|
|
93
|
+
const split: string[] = path.resolve(file).split(path.sep);
|
|
94
|
+
split.pop();
|
|
95
|
+
return path.resolve(split.join(path.sep));
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/** Remove unused typia imports that are only used in transformable calls */
|
|
99
|
+
const removeUnusedTypiaImports = (file: ts.SourceFile): ts.SourceFile => {
|
|
100
|
+
// Find typia imports and collect all identifiers
|
|
101
|
+
interface ImportMetadata {
|
|
102
|
+
declaration: ts.ImportDeclaration;
|
|
103
|
+
default: boolean;
|
|
104
|
+
}
|
|
105
|
+
const imports: Map<string, ImportMetadata> = new Map();
|
|
106
|
+
for (const stmt of file.statements) {
|
|
107
|
+
if (
|
|
108
|
+
ts.isImportDeclaration(stmt) === false ||
|
|
109
|
+
ts.isStringLiteral(stmt.moduleSpecifier) === false ||
|
|
110
|
+
stmt.moduleSpecifier.text !== "typia" ||
|
|
111
|
+
stmt.importClause === undefined
|
|
112
|
+
)
|
|
113
|
+
continue;
|
|
114
|
+
|
|
115
|
+
// Track default import (import typia from 'typia')
|
|
116
|
+
if (stmt.importClause.name)
|
|
117
|
+
imports.set(stmt.importClause.name.text, {
|
|
118
|
+
declaration: stmt,
|
|
119
|
+
default: true,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Track named imports (import { tags } from 'typia') - keep these
|
|
123
|
+
if (
|
|
124
|
+
stmt.importClause.namedBindings &&
|
|
125
|
+
ts.isNamedImports(stmt.importClause.namedBindings)
|
|
126
|
+
)
|
|
127
|
+
for (const element of stmt.importClause.namedBindings.elements)
|
|
128
|
+
imports.set(element.name.text, {
|
|
129
|
+
declaration: stmt,
|
|
130
|
+
default: false,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
if (imports.size === 0) return file; // No typia imports to check
|
|
134
|
+
|
|
135
|
+
// Find usage of typia identifiers that are NOT transformable calls
|
|
136
|
+
const nonTransformableUsage = new Set<string>();
|
|
137
|
+
const checkUsage = (node: ts.Node) => {
|
|
138
|
+
if (ts.isIdentifier(node) && imports.has(node.text)) {
|
|
139
|
+
const identifier: string = node.text;
|
|
140
|
+
// Check if this identifier is being used as part of a property access
|
|
141
|
+
if (
|
|
142
|
+
node.parent &&
|
|
143
|
+
ts.isPropertyAccessExpression(node.parent) &&
|
|
144
|
+
node.parent.expression === node
|
|
145
|
+
) {
|
|
146
|
+
// This is typia.something - check if it's a transformable call pattern
|
|
147
|
+
if (!isLikelyTransformableCall(node.parent))
|
|
148
|
+
nonTransformableUsage.add(identifier);
|
|
149
|
+
} else
|
|
150
|
+
// Direct usage of the typia identifier (not as property access)
|
|
151
|
+
// This is definitely non-transformable usage
|
|
152
|
+
nonTransformableUsage.add(identifier);
|
|
153
|
+
}
|
|
154
|
+
ts.forEachChild(node, checkUsage);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Check all statements except import declarations
|
|
158
|
+
for (const stmt of file.statements)
|
|
159
|
+
if (
|
|
160
|
+
!ts.isImportDeclaration(stmt) ||
|
|
161
|
+
!ts.isStringLiteral(stmt.moduleSpecifier) ||
|
|
162
|
+
stmt.moduleSpecifier.text !== "typia"
|
|
163
|
+
)
|
|
164
|
+
checkUsage(stmt);
|
|
165
|
+
|
|
166
|
+
// Update import statements
|
|
167
|
+
const newStatements: ts.Statement[] = file.statements
|
|
168
|
+
.map((stmt) => {
|
|
169
|
+
if (
|
|
170
|
+
ts.isImportDeclaration(stmt) &&
|
|
171
|
+
ts.isStringLiteral(stmt.moduleSpecifier) &&
|
|
172
|
+
stmt.moduleSpecifier.text === "typia" &&
|
|
173
|
+
stmt.importClause
|
|
174
|
+
) {
|
|
175
|
+
const newImportClause = filterTypiaImportClause(
|
|
176
|
+
stmt.importClause,
|
|
177
|
+
nonTransformableUsage,
|
|
178
|
+
);
|
|
179
|
+
if (newImportClause)
|
|
180
|
+
return ts.factory.createImportDeclaration(
|
|
181
|
+
stmt.modifiers,
|
|
182
|
+
newImportClause,
|
|
183
|
+
stmt.moduleSpecifier,
|
|
184
|
+
stmt.attributes,
|
|
185
|
+
);
|
|
186
|
+
return null; // Skip adding the import if all imports are unused
|
|
187
|
+
}
|
|
188
|
+
return stmt;
|
|
189
|
+
})
|
|
190
|
+
.filter((stmt) => stmt !== null);
|
|
191
|
+
return ts.factory.updateSourceFile(
|
|
192
|
+
file,
|
|
193
|
+
newStatements,
|
|
194
|
+
file.isDeclarationFile,
|
|
195
|
+
file.referencedFiles,
|
|
196
|
+
file.typeReferenceDirectives,
|
|
197
|
+
file.hasNoDefaultLib,
|
|
198
|
+
file.libReferenceDirectives,
|
|
199
|
+
);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Check if a property access expression looks like a transformable typia call
|
|
204
|
+
* This uses heuristics to detect patterns like typia.xxx(),
|
|
205
|
+
* typia.namespace.xxx()
|
|
206
|
+
*/
|
|
207
|
+
const isLikelyTransformableCall = (
|
|
208
|
+
node: ts.PropertyAccessExpression,
|
|
209
|
+
): boolean => {
|
|
210
|
+
// Check if this is eventually part of a call expression
|
|
211
|
+
let current: ts.Node = node;
|
|
212
|
+
|
|
213
|
+
// Walk up the chain to find if this leads to a call expression
|
|
214
|
+
// Handle patterns like: typia.xxx(), typia.namespace.xxx()
|
|
215
|
+
while (ts.isPropertyAccessExpression(current)) {
|
|
216
|
+
current = current.parent;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// If the final result is a call expression, this is likely transformable
|
|
220
|
+
if (
|
|
221
|
+
ts.isCallExpression(current) &&
|
|
222
|
+
(current.expression === node ||
|
|
223
|
+
(ts.isPropertyAccessExpression(current.expression) &&
|
|
224
|
+
isTypiaPropertyChain(current.expression)))
|
|
225
|
+
) {
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return false;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/** Check if a property access expression is part of a typia.xxx.yyy chain */
|
|
233
|
+
const isTypiaPropertyChain = (node: ts.PropertyAccessExpression): boolean => {
|
|
234
|
+
let current: ts.Expression = node;
|
|
235
|
+
|
|
236
|
+
while (ts.isPropertyAccessExpression(current)) {
|
|
237
|
+
current = current.expression;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return ts.isIdentifier(current) && current.text === "typia";
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/** Filter import clause to remove unused default imports */
|
|
244
|
+
const filterTypiaImportClause = (
|
|
245
|
+
importClause: ts.ImportClause,
|
|
246
|
+
usedImports: Set<string>,
|
|
247
|
+
): ts.ImportClause | undefined => {
|
|
248
|
+
const hasDefaultImport =
|
|
249
|
+
importClause.name && usedImports.has(importClause.name.text);
|
|
250
|
+
const namedBindings = importClause.namedBindings; // Always keep named bindings like { tags }
|
|
251
|
+
|
|
252
|
+
// Return undefined if no imports are used
|
|
253
|
+
if (!hasDefaultImport && !namedBindings) {
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return ts.factory.createImportClause(
|
|
258
|
+
importClause.isTypeOnly,
|
|
259
|
+
hasDefaultImport ? importClause.name : undefined,
|
|
260
|
+
namedBindings,
|
|
261
|
+
);
|
|
262
|
+
};
|
package/src/NodeTransformer.ts
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import { ITypiaContext } from "@typia/core";
|
|
2
|
-
import ts from "typescript";
|
|
3
|
-
|
|
4
|
-
import { CallExpressionTransformer } from "./CallExpressionTransformer";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* TypeScript AST node transformer.
|
|
8
|
-
*
|
|
9
|
-
* Delegates call expression nodes to {@link CallExpressionTransformer} for
|
|
10
|
-
* potential `typia.*` function transformation. Non-call nodes pass through.
|
|
11
|
-
*
|
|
12
|
-
* @author Jeongho Nam - https://github.com/samchon
|
|
13
|
-
*/
|
|
14
|
-
export namespace NodeTransformer {
|
|
15
|
-
export const transform = (props: {
|
|
16
|
-
context: ITypiaContext;
|
|
17
|
-
node: ts.Node;
|
|
18
|
-
}): ts.Node | null =>
|
|
19
|
-
ts.isCallExpression(props.node) && props.node.parent
|
|
20
|
-
? CallExpressionTransformer.transform({
|
|
21
|
-
context: props.context,
|
|
22
|
-
expression: props.node,
|
|
23
|
-
})
|
|
24
|
-
: props.node;
|
|
25
|
-
}
|
|
1
|
+
import { ITypiaContext } from "@typia/core";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
|
|
4
|
+
import { CallExpressionTransformer } from "./CallExpressionTransformer";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* TypeScript AST node transformer.
|
|
8
|
+
*
|
|
9
|
+
* Delegates call expression nodes to {@link CallExpressionTransformer} for
|
|
10
|
+
* potential `typia.*` function transformation. Non-call nodes pass through.
|
|
11
|
+
*
|
|
12
|
+
* @author Jeongho Nam - https://github.com/samchon
|
|
13
|
+
*/
|
|
14
|
+
export namespace NodeTransformer {
|
|
15
|
+
export const transform = (props: {
|
|
16
|
+
context: ITypiaContext;
|
|
17
|
+
node: ts.Node;
|
|
18
|
+
}): ts.Node | null =>
|
|
19
|
+
ts.isCallExpression(props.node) && props.node.parent
|
|
20
|
+
? CallExpressionTransformer.transform({
|
|
21
|
+
context: props.context,
|
|
22
|
+
expression: props.node,
|
|
23
|
+
})
|
|
24
|
+
: props.node;
|
|
25
|
+
}
|