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.
Files changed (2) hide show
  1. package/dist/transformer.js +112 -6
  2. package/package.json +1 -1
@@ -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
- if (!expr)
131
- return null;
132
- expanded.push(expr);
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.0",
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",