ts-transform-args 0.1.0 → 0.1.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/dist/transformer.js +112 -6
- package/package.json +1 -1
package/dist/transformer.js
CHANGED
|
@@ -7,6 +7,19 @@ const ts_helpers_1 = require("./util/ts-helpers");
|
|
|
7
7
|
function transformer(program, opts) {
|
|
8
8
|
const markerName = opts?.markerName ?? 'CallArgs';
|
|
9
9
|
const checker = program.getTypeChecker();
|
|
10
|
+
const RESERVED_NAMES = new Set([
|
|
11
|
+
'arguments', 'eval',
|
|
12
|
+
'break', 'case', 'catch', 'continue', 'debugger', 'default', 'delete',
|
|
13
|
+
'do', 'else', 'finally', 'for', 'function', 'if', 'in', 'instanceof',
|
|
14
|
+
'new', 'return', 'switch', 'this', 'throw', 'try', 'typeof', 'var',
|
|
15
|
+
'void', 'while', 'with', 'class', 'const', 'enum', 'export', 'extends',
|
|
16
|
+
'import', 'super', 'implements', 'interface', 'let', 'package',
|
|
17
|
+
'private', 'protected', 'public', 'static', 'yield', 'await',
|
|
18
|
+
]);
|
|
19
|
+
/** Return a safe identifier name, prefixing reserved words with `_`. */
|
|
20
|
+
function safeName(name) {
|
|
21
|
+
return RESERVED_NAMES.has(name) ? `_${name}` : name;
|
|
22
|
+
}
|
|
10
23
|
return (context) => {
|
|
11
24
|
const { factory } = context;
|
|
12
25
|
/** Rewrites `paramName.prop` and `paramName["prop"]` to `prop`. */
|
|
@@ -18,7 +31,7 @@ function transformer(program, opts) {
|
|
|
18
31
|
if ((0, ts_helpers_1.isIdentifier)(propAccess.expression) &&
|
|
19
32
|
propAccess.expression.text === paramName &&
|
|
20
33
|
propSet.has(propAccess.name.text)) {
|
|
21
|
-
return factory.createIdentifier(propAccess.name.text);
|
|
34
|
+
return factory.createIdentifier(safeName(propAccess.name.text));
|
|
22
35
|
}
|
|
23
36
|
}
|
|
24
37
|
if ((0, ts_helpers_1.isElementAccessExpression)(node)) {
|
|
@@ -27,9 +40,24 @@ function transformer(program, opts) {
|
|
|
27
40
|
elemAccess.expression.text === paramName &&
|
|
28
41
|
(0, ts_helpers_1.isStringLiteral)(elemAccess.argumentExpression) &&
|
|
29
42
|
propSet.has(elemAccess.argumentExpression.text)) {
|
|
30
|
-
return factory.createIdentifier(elemAccess.argumentExpression.text);
|
|
43
|
+
return factory.createIdentifier(safeName(elemAccess.argumentExpression.text));
|
|
31
44
|
}
|
|
32
45
|
}
|
|
46
|
+
if ((0, ts_helpers_1.isCallExpression)(node) || (0, ts_helpers_1.isNewExpression)(node)) {
|
|
47
|
+
const callOrNew = node;
|
|
48
|
+
const callArgsInfo = analyzeCallArgs(callOrNew);
|
|
49
|
+
// Rewrite children first (property accesses → identifiers)
|
|
50
|
+
const rewritten = (0, ts_helpers_1.visitEachChild)(node, bodyVisitor, context);
|
|
51
|
+
if (callArgsInfo) {
|
|
52
|
+
return applyCallArgsExpansion(rewritten, callArgsInfo);
|
|
53
|
+
}
|
|
54
|
+
return rewritten;
|
|
55
|
+
}
|
|
56
|
+
if ((0, ts_helpers_1.isFunctionLike)(node)) {
|
|
57
|
+
const rewritten = (0, ts_helpers_1.visitEachChild)(node, bodyVisitor, context);
|
|
58
|
+
const transformed = tryTransformFunction(rewritten);
|
|
59
|
+
return transformed ?? rewritten;
|
|
60
|
+
}
|
|
33
61
|
return (0, ts_helpers_1.visitEachChild)(node, bodyVisitor, context);
|
|
34
62
|
};
|
|
35
63
|
return bodyVisitor;
|
|
@@ -71,7 +99,7 @@ function transformer(program, opts) {
|
|
|
71
99
|
for (const info of infos) {
|
|
72
100
|
if (info.isCallArgs && info.propOrder) {
|
|
73
101
|
for (const name of info.propOrder) {
|
|
74
|
-
result.push(factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier(name), undefined, undefined, undefined));
|
|
102
|
+
result.push(factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier(safeName(name)), undefined, undefined, undefined));
|
|
75
103
|
}
|
|
76
104
|
}
|
|
77
105
|
else {
|
|
@@ -127,12 +155,90 @@ function transformer(program, opts) {
|
|
|
127
155
|
const expanded = [];
|
|
128
156
|
for (const name of propOrder) {
|
|
129
157
|
const expr = propExpressions.get(name);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
158
|
+
expanded.push(expr ?? factory.createVoidZero());
|
|
159
|
+
}
|
|
160
|
+
// Trim trailing `void 0` args (optional properties at the end)
|
|
161
|
+
while (expanded.length > 0 &&
|
|
162
|
+
expanded[expanded.length - 1].kind === (0, ts_helpers_1.getTS)().SyntaxKind.VoidExpression) {
|
|
163
|
+
expanded.pop();
|
|
133
164
|
}
|
|
134
165
|
return expanded;
|
|
135
166
|
}
|
|
167
|
+
function analyzeCallArgs(callOrNew) {
|
|
168
|
+
if (!callOrNew.arguments || callOrNew.arguments.length === 0)
|
|
169
|
+
return null;
|
|
170
|
+
const signature = checker.getResolvedSignature(callOrNew);
|
|
171
|
+
if (!signature)
|
|
172
|
+
return null;
|
|
173
|
+
const sigParams = signature.getParameters();
|
|
174
|
+
if (sigParams.length === 0)
|
|
175
|
+
return null;
|
|
176
|
+
const argExpansions = [];
|
|
177
|
+
let anyExpanded = false;
|
|
178
|
+
for (let i = 0; i < callOrNew.arguments.length; i++) {
|
|
179
|
+
const arg = callOrNew.arguments[i];
|
|
180
|
+
const sigParam = i < sigParams.length ? sigParams[i] : null;
|
|
181
|
+
if (sigParam && (0, ts_helpers_1.isObjectLiteralExpression)(arg)) {
|
|
182
|
+
const paramType = sigParam.valueDeclaration
|
|
183
|
+
? checker.getTypeOfSymbolAtLocation(sigParam, sigParam.valueDeclaration)
|
|
184
|
+
: null;
|
|
185
|
+
if (paramType && (0, call_args_1.extendsCallArgs)(paramType, checker, markerName)) {
|
|
186
|
+
const propOrder = (0, call_args_1.getPropertyOrder)(paramType, checker);
|
|
187
|
+
if (propOrder) {
|
|
188
|
+
argExpansions.push(propOrder);
|
|
189
|
+
anyExpanded = true;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
argExpansions.push(null);
|
|
195
|
+
}
|
|
196
|
+
return anyExpanded ? { argExpansions } : null;
|
|
197
|
+
}
|
|
198
|
+
function applyCallArgsExpansion(callOrNew, info) {
|
|
199
|
+
const args = callOrNew.arguments;
|
|
200
|
+
const newArgs = [];
|
|
201
|
+
for (let i = 0; i < args.length; i++) {
|
|
202
|
+
const arg = args[i];
|
|
203
|
+
const propOrder = info.argExpansions[i];
|
|
204
|
+
if (propOrder && (0, ts_helpers_1.isObjectLiteralExpression)(arg)) {
|
|
205
|
+
const objLiteral = arg;
|
|
206
|
+
const propExpressions = new Map();
|
|
207
|
+
for (const prop of objLiteral.properties) {
|
|
208
|
+
if ((0, ts_helpers_1.isPropertyAssignment)(prop) && prop.name) {
|
|
209
|
+
const name = prop.name.getText();
|
|
210
|
+
propExpressions.set(name, prop.initializer);
|
|
211
|
+
}
|
|
212
|
+
else if ((0, ts_helpers_1.isShorthandPropertyAssignment)(prop)) {
|
|
213
|
+
const name = prop.name.getText();
|
|
214
|
+
propExpressions.set(name, prop.name);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const expanded = [];
|
|
218
|
+
for (const name of propOrder) {
|
|
219
|
+
const expr = propExpressions.get(name);
|
|
220
|
+
expanded.push(expr ?? factory.createVoidZero());
|
|
221
|
+
}
|
|
222
|
+
// Trim trailing `void 0` args (optional properties at the end)
|
|
223
|
+
while (expanded.length > 0 &&
|
|
224
|
+
expanded[expanded.length - 1].kind === (0, ts_helpers_1.getTS)().SyntaxKind.VoidExpression) {
|
|
225
|
+
expanded.pop();
|
|
226
|
+
}
|
|
227
|
+
{
|
|
228
|
+
newArgs.push(...expanded);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// Pass through unchanged
|
|
233
|
+
newArgs.push(arg);
|
|
234
|
+
}
|
|
235
|
+
if ((0, ts_helpers_1.isCallExpression)(callOrNew)) {
|
|
236
|
+
const call = callOrNew;
|
|
237
|
+
return factory.updateCallExpression(call, call.expression, call.typeArguments, newArgs);
|
|
238
|
+
}
|
|
239
|
+
const newExpr = callOrNew;
|
|
240
|
+
return factory.updateNewExpression(newExpr, newExpr.expression, newExpr.typeArguments, newArgs);
|
|
241
|
+
}
|
|
136
242
|
/** Transform call/new expression args, expanding CallArgs objects. */
|
|
137
243
|
function tryTransformCallArgs(callOrNew) {
|
|
138
244
|
if (!callOrNew.arguments || callOrNew.arguments.length === 0)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-transform-args",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "TypeScript transformer that eliminates option object allocations by converting them into positional arguments at compile time. Readable code, zero GC overhead",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|