ts-transform-args 0.1.0
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/LICENSE +21 -0
- package/README.md +69 -0
- package/dist/compile.d.ts +6 -0
- package/dist/compile.js +81 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +10 -0
- package/dist/transformer.d.ts +8 -0
- package/dist/transformer.js +190 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.js +2 -0
- package/dist/util/call-args.d.ts +6 -0
- package/dist/util/call-args.js +34 -0
- package/dist/util/ts-helpers.d.ts +19 -0
- package/dist/util/ts-helpers.js +82 -0
- package/dist/util/update-node.d.ts +5 -0
- package/dist/util/update-node.js +23 -0
- package/package.json +27 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 swe
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# ts-transform-args
|
|
2
|
+
|
|
3
|
+
TypeScript compiler transformer that rewrites option objects into positional arguments at compile time. Eliminates short-lived allocations on hot paths while keeping source code readable.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
// source
|
|
7
|
+
createParticle({ x: pos.x, y: pos.y, vx: 0.1, vy: -0.3, ttl: 60 });
|
|
8
|
+
|
|
9
|
+
// emitted JS
|
|
10
|
+
createParticle(pos.x, pos.y, 0.1, -0.3, 60);
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Function bodies are rewritten too (`opts.x` becomes `x`), so the option object is fully eliminated.
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install ts-transform-args typescript ts-patch
|
|
19
|
+
npx ts-patch install
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**tsconfig.json:**
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"compilerOptions": {
|
|
27
|
+
"types": ["ts-transform-args"],
|
|
28
|
+
"plugins": [{ "transform": "ts-transform-args" }]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Adding `"ts-transform-args"` to `types` makes the `CallArgs` interface available globally without imports. If you use other type packages (e.g. `@types/node`), include them too since an explicit `types` array overrides automatic `@types` resolution.
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
Define an interface extending `CallArgs`. Property declaration order determines argument position.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
interface Opts extends CallArgs {
|
|
41
|
+
name: string;
|
|
42
|
+
age: number;
|
|
43
|
+
active: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
declare function createUser(opts: Opts): void;
|
|
47
|
+
|
|
48
|
+
createUser({ active: true, name: "alice", age: 30 });
|
|
49
|
+
// emits: createUser("alice", 30, true)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Multiple CallArgs parameters, plain parameters mixed with CallArgs, constructors, and methods are all supported.
|
|
53
|
+
|
|
54
|
+
## Programmatic API
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { transformSource } from 'ts-transform-args/compile';
|
|
58
|
+
|
|
59
|
+
const js = transformSource(`
|
|
60
|
+
interface Opts extends CallArgs { x: number; y: number; }
|
|
61
|
+
declare function move(opts: Opts): void;
|
|
62
|
+
move({ x: 10, y: 20 });
|
|
63
|
+
`);
|
|
64
|
+
// js => 'move(10, 20);'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## License
|
|
68
|
+
|
|
69
|
+
MIT
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import type { TransformerOptions } from './transformer';
|
|
3
|
+
/** Compile files with the args transformer applied. */
|
|
4
|
+
export declare function compile(fileNames: string[], compilerOptions: ts.CompilerOptions, transformerOpts?: TransformerOptions): ts.EmitResult;
|
|
5
|
+
/** Transform a single source string and return the emitted JS. */
|
|
6
|
+
export declare function transformSource(source: string, transformerOpts?: TransformerOptions): string;
|
package/dist/compile.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.compile = compile;
|
|
40
|
+
exports.transformSource = transformSource;
|
|
41
|
+
const ts = __importStar(require("typescript"));
|
|
42
|
+
const transformer_1 = __importDefault(require("./transformer"));
|
|
43
|
+
/** Compile files with the args transformer applied. */
|
|
44
|
+
function compile(fileNames, compilerOptions, transformerOpts) {
|
|
45
|
+
const program = ts.createProgram(fileNames, compilerOptions);
|
|
46
|
+
const result = program.emit(undefined, undefined, undefined, false, {
|
|
47
|
+
before: [(0, transformer_1.default)(program, transformerOpts)],
|
|
48
|
+
});
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
/** Transform a single source string and return the emitted JS. */
|
|
52
|
+
function transformSource(source, transformerOpts) {
|
|
53
|
+
const fileName = 'input.ts';
|
|
54
|
+
const sourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true);
|
|
55
|
+
const compilerOptions = {
|
|
56
|
+
target: ts.ScriptTarget.ES2020,
|
|
57
|
+
module: ts.ModuleKind.CommonJS,
|
|
58
|
+
strict: false,
|
|
59
|
+
};
|
|
60
|
+
const defaultHost = ts.createCompilerHost(compilerOptions);
|
|
61
|
+
const customHost = {
|
|
62
|
+
...defaultHost,
|
|
63
|
+
getSourceFile: (name, languageVersion) => {
|
|
64
|
+
if (name === fileName)
|
|
65
|
+
return sourceFile;
|
|
66
|
+
return defaultHost.getSourceFile(name, languageVersion);
|
|
67
|
+
},
|
|
68
|
+
fileExists: (name) => name === fileName || defaultHost.fileExists(name),
|
|
69
|
+
readFile: (name) => name === fileName ? source : defaultHost.readFile(name),
|
|
70
|
+
};
|
|
71
|
+
const program = ts.createProgram([fileName], compilerOptions, customHost);
|
|
72
|
+
let output = '';
|
|
73
|
+
program.emit(undefined, (emittedFileName, data) => {
|
|
74
|
+
if (emittedFileName.endsWith('.js')) {
|
|
75
|
+
output = data;
|
|
76
|
+
}
|
|
77
|
+
}, undefined, false, {
|
|
78
|
+
before: [(0, transformer_1.default)(program, transformerOpts)],
|
|
79
|
+
});
|
|
80
|
+
return output;
|
|
81
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = exports.transformer = void 0;
|
|
7
|
+
var transformer_1 = require("./transformer");
|
|
8
|
+
Object.defineProperty(exports, "transformer", { enumerable: true, get: function () { return __importDefault(transformer_1).default; } });
|
|
9
|
+
var transformer_2 = require("./transformer");
|
|
10
|
+
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(transformer_2).default; } });
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type ts from 'typescript';
|
|
2
|
+
export interface TransformerOptions {
|
|
3
|
+
/** @default "CallArgs" */
|
|
4
|
+
markerName?: string;
|
|
5
|
+
}
|
|
6
|
+
/** ts-patch / ttypescript compatible transformer factory. */
|
|
7
|
+
declare function transformer(program: ts.Program, opts?: TransformerOptions): ts.TransformerFactory<ts.SourceFile>;
|
|
8
|
+
export default transformer;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const call_args_1 = require("./util/call-args");
|
|
4
|
+
const update_node_1 = require("./util/update-node");
|
|
5
|
+
const ts_helpers_1 = require("./util/ts-helpers");
|
|
6
|
+
/** ts-patch / ttypescript compatible transformer factory. */
|
|
7
|
+
function transformer(program, opts) {
|
|
8
|
+
const markerName = opts?.markerName ?? 'CallArgs';
|
|
9
|
+
const checker = program.getTypeChecker();
|
|
10
|
+
return (context) => {
|
|
11
|
+
const { factory } = context;
|
|
12
|
+
/** Rewrites `paramName.prop` and `paramName["prop"]` to `prop`. */
|
|
13
|
+
function createBodyRewriter(paramName, propOrder) {
|
|
14
|
+
const propSet = new Set(propOrder);
|
|
15
|
+
const bodyVisitor = (node) => {
|
|
16
|
+
if ((0, ts_helpers_1.isPropertyAccessExpression)(node)) {
|
|
17
|
+
const propAccess = node;
|
|
18
|
+
if ((0, ts_helpers_1.isIdentifier)(propAccess.expression) &&
|
|
19
|
+
propAccess.expression.text === paramName &&
|
|
20
|
+
propSet.has(propAccess.name.text)) {
|
|
21
|
+
return factory.createIdentifier(propAccess.name.text);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if ((0, ts_helpers_1.isElementAccessExpression)(node)) {
|
|
25
|
+
const elemAccess = node;
|
|
26
|
+
if ((0, ts_helpers_1.isIdentifier)(elemAccess.expression) &&
|
|
27
|
+
elemAccess.expression.text === paramName &&
|
|
28
|
+
(0, ts_helpers_1.isStringLiteral)(elemAccess.argumentExpression) &&
|
|
29
|
+
propSet.has(elemAccess.argumentExpression.text)) {
|
|
30
|
+
return factory.createIdentifier(elemAccess.argumentExpression.text);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return (0, ts_helpers_1.visitEachChild)(node, bodyVisitor, context);
|
|
34
|
+
};
|
|
35
|
+
return bodyVisitor;
|
|
36
|
+
}
|
|
37
|
+
/** Analyze params and return info about which are CallArgs. */
|
|
38
|
+
function analyzeParams(params) {
|
|
39
|
+
const infos = [];
|
|
40
|
+
let hasAny = false;
|
|
41
|
+
for (const param of params) {
|
|
42
|
+
const paramSymbol = checker.getSymbolAtLocation(param.name);
|
|
43
|
+
if (!paramSymbol) {
|
|
44
|
+
infos.push({ isCallArgs: false, paramName: '', propOrder: null, original: param });
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const paramType = checker.getTypeOfSymbolAtLocation(paramSymbol, param);
|
|
48
|
+
if ((0, call_args_1.extendsCallArgs)(paramType, checker, markerName)) {
|
|
49
|
+
const propOrder = (0, call_args_1.getPropertyOrder)(paramType, checker);
|
|
50
|
+
if (!propOrder) {
|
|
51
|
+
infos.push({ isCallArgs: false, paramName: '', propOrder: null, original: param });
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
hasAny = true;
|
|
55
|
+
infos.push({
|
|
56
|
+
isCallArgs: true,
|
|
57
|
+
paramName: param.name.getText(),
|
|
58
|
+
propOrder,
|
|
59
|
+
original: param,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
infos.push({ isCallArgs: false, paramName: '', propOrder: null, original: param });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return hasAny ? infos : null;
|
|
67
|
+
}
|
|
68
|
+
/** Expand CallArgs params into positional params, keep others. */
|
|
69
|
+
function buildNewParams(infos) {
|
|
70
|
+
const result = [];
|
|
71
|
+
for (const info of infos) {
|
|
72
|
+
if (info.isCallArgs && info.propOrder) {
|
|
73
|
+
for (const name of info.propOrder) {
|
|
74
|
+
result.push(factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier(name), undefined, undefined, undefined));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
result.push(factory.createParameterDeclaration(info.original.modifiers, info.original.dotDotDotToken, info.original.name, info.original.questionToken, undefined, info.original.initializer));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
/** Rewrite function body, replacing all CallArgs param accesses. */
|
|
84
|
+
function rewriteBody(body, infos) {
|
|
85
|
+
let result = body;
|
|
86
|
+
for (const info of infos) {
|
|
87
|
+
if (info.isCallArgs && info.propOrder) {
|
|
88
|
+
const rewriter = createBodyRewriter(info.paramName, info.propOrder);
|
|
89
|
+
result = (0, ts_helpers_1.visitNode)(result, rewriter);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
/** Transform a function-like declaration if it has CallArgs params. */
|
|
95
|
+
function tryTransformFunction(node) {
|
|
96
|
+
if (node.parameters.length === 0)
|
|
97
|
+
return null;
|
|
98
|
+
const infos = analyzeParams(node.parameters);
|
|
99
|
+
if (!infos)
|
|
100
|
+
return null;
|
|
101
|
+
const newParams = buildNewParams(infos);
|
|
102
|
+
const newBody = node.body ? rewriteBody(node.body, infos) : node.body;
|
|
103
|
+
return (0, update_node_1.updateFunctionLike)(node, factory, newParams, newBody);
|
|
104
|
+
}
|
|
105
|
+
/** Try expanding a CallArgs object literal arg into positional args. */
|
|
106
|
+
function tryExpandArg(arg, sigParam) {
|
|
107
|
+
if (!(0, ts_helpers_1.isObjectLiteralExpression)(arg))
|
|
108
|
+
return null;
|
|
109
|
+
const paramType = checker.getTypeOfSymbolAtLocation(sigParam, sigParam.valueDeclaration);
|
|
110
|
+
if (!(0, call_args_1.extendsCallArgs)(paramType, checker, markerName))
|
|
111
|
+
return null;
|
|
112
|
+
const propOrder = (0, call_args_1.getPropertyOrder)(paramType, checker);
|
|
113
|
+
if (!propOrder)
|
|
114
|
+
return null;
|
|
115
|
+
const objLiteral = arg;
|
|
116
|
+
const propExpressions = new Map();
|
|
117
|
+
for (const prop of objLiteral.properties) {
|
|
118
|
+
if ((0, ts_helpers_1.isPropertyAssignment)(prop) && prop.name) {
|
|
119
|
+
const name = prop.name.getText();
|
|
120
|
+
propExpressions.set(name, (0, ts_helpers_1.visitNode)(prop.initializer, visitor));
|
|
121
|
+
}
|
|
122
|
+
else if ((0, ts_helpers_1.isShorthandPropertyAssignment)(prop)) {
|
|
123
|
+
const name = prop.name.getText();
|
|
124
|
+
propExpressions.set(name, (0, ts_helpers_1.visitNode)(prop.name, visitor));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const expanded = [];
|
|
128
|
+
for (const name of propOrder) {
|
|
129
|
+
const expr = propExpressions.get(name);
|
|
130
|
+
if (!expr)
|
|
131
|
+
return null;
|
|
132
|
+
expanded.push(expr);
|
|
133
|
+
}
|
|
134
|
+
return expanded;
|
|
135
|
+
}
|
|
136
|
+
/** Transform call/new expression args, expanding CallArgs objects. */
|
|
137
|
+
function tryTransformCallArgs(callOrNew) {
|
|
138
|
+
if (!callOrNew.arguments || callOrNew.arguments.length === 0)
|
|
139
|
+
return null;
|
|
140
|
+
const signature = checker.getResolvedSignature(callOrNew);
|
|
141
|
+
if (!signature)
|
|
142
|
+
return null;
|
|
143
|
+
const sigParams = signature.getParameters();
|
|
144
|
+
if (sigParams.length === 0)
|
|
145
|
+
return null;
|
|
146
|
+
const newArgs = [];
|
|
147
|
+
let anyExpanded = false;
|
|
148
|
+
for (let i = 0; i < callOrNew.arguments.length; i++) {
|
|
149
|
+
const arg = callOrNew.arguments[i];
|
|
150
|
+
const sigParam = i < sigParams.length ? sigParams[i] : null;
|
|
151
|
+
if (sigParam) {
|
|
152
|
+
const expanded = tryExpandArg(arg, sigParam);
|
|
153
|
+
if (expanded) {
|
|
154
|
+
newArgs.push(...expanded);
|
|
155
|
+
anyExpanded = true;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
newArgs.push((0, ts_helpers_1.visitNode)(arg, visitor));
|
|
160
|
+
}
|
|
161
|
+
return anyExpanded ? newArgs : null;
|
|
162
|
+
}
|
|
163
|
+
const visitor = (node) => {
|
|
164
|
+
if ((0, ts_helpers_1.isFunctionLike)(node)) {
|
|
165
|
+
const transformed = tryTransformFunction(node);
|
|
166
|
+
if (transformed)
|
|
167
|
+
return transformed;
|
|
168
|
+
return (0, ts_helpers_1.visitEachChild)(node, visitor, context);
|
|
169
|
+
}
|
|
170
|
+
if (!(0, ts_helpers_1.isCallExpression)(node) && !(0, ts_helpers_1.isNewExpression)(node)) {
|
|
171
|
+
return (0, ts_helpers_1.visitEachChild)(node, visitor, context);
|
|
172
|
+
}
|
|
173
|
+
const callOrNew = node;
|
|
174
|
+
const newArgs = tryTransformCallArgs(callOrNew);
|
|
175
|
+
if (!newArgs) {
|
|
176
|
+
return (0, ts_helpers_1.visitEachChild)(node, visitor, context);
|
|
177
|
+
}
|
|
178
|
+
if ((0, ts_helpers_1.isCallExpression)(node)) {
|
|
179
|
+
const call = node;
|
|
180
|
+
return factory.updateCallExpression(call, (0, ts_helpers_1.visitNode)(call.expression, visitor), call.typeArguments, newArgs);
|
|
181
|
+
}
|
|
182
|
+
const newExpr = node;
|
|
183
|
+
return factory.updateNewExpression(newExpr, (0, ts_helpers_1.visitNode)(newExpr.expression, visitor), newExpr.typeArguments, newArgs);
|
|
184
|
+
};
|
|
185
|
+
return (sourceFile) => {
|
|
186
|
+
return (0, ts_helpers_1.visitNode)(sourceFile, visitor);
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
exports.default = transformer;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marker interface. Extending this opts-in an interface for
|
|
3
|
+
* object-to-positional-args transformation at compile time.
|
|
4
|
+
*/
|
|
5
|
+
export interface CallArgs {
|
|
6
|
+
readonly __callArgs?: never;
|
|
7
|
+
}
|
|
8
|
+
declare global {
|
|
9
|
+
interface CallArgs {
|
|
10
|
+
readonly __callArgs?: never;
|
|
11
|
+
}
|
|
12
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type ts from 'typescript';
|
|
2
|
+
export declare const BRAND_FIELD = "__callArgs";
|
|
3
|
+
/** Structural + nominal check for CallArgs marker. */
|
|
4
|
+
export declare function extendsCallArgs(type: ts.Type, checker: ts.TypeChecker, markerName: string): boolean;
|
|
5
|
+
/** Property names in declaration order (excluding brand). */
|
|
6
|
+
export declare function getPropertyOrder(type: ts.Type, _checker: ts.TypeChecker): string[] | null;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BRAND_FIELD = void 0;
|
|
4
|
+
exports.extendsCallArgs = extendsCallArgs;
|
|
5
|
+
exports.getPropertyOrder = getPropertyOrder;
|
|
6
|
+
exports.BRAND_FIELD = '__callArgs';
|
|
7
|
+
/** Structural + nominal check for CallArgs marker. */
|
|
8
|
+
function extendsCallArgs(type, checker, markerName) {
|
|
9
|
+
if (type.getProperty(exports.BRAND_FIELD))
|
|
10
|
+
return true;
|
|
11
|
+
const baseTypes = type.getBaseTypes?.() ?? [];
|
|
12
|
+
for (const base of baseTypes) {
|
|
13
|
+
const sym = base.getSymbol?.();
|
|
14
|
+
if (sym && sym.getName() === markerName)
|
|
15
|
+
return true;
|
|
16
|
+
if (extendsCallArgs(base, checker, markerName))
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
/** Property names in declaration order (excluding brand). */
|
|
22
|
+
function getPropertyOrder(type, _checker) {
|
|
23
|
+
const properties = type.getProperties();
|
|
24
|
+
if (properties.length === 0)
|
|
25
|
+
return null;
|
|
26
|
+
const names = [];
|
|
27
|
+
for (const prop of properties) {
|
|
28
|
+
const name = prop.getName();
|
|
29
|
+
if (name === exports.BRAND_FIELD)
|
|
30
|
+
continue;
|
|
31
|
+
names.push(name);
|
|
32
|
+
}
|
|
33
|
+
return names.length > 0 ? names : null;
|
|
34
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type ts from 'typescript';
|
|
2
|
+
export declare function getTS(): typeof ts;
|
|
3
|
+
export declare function isCallExpression(node: ts.Node): node is ts.CallExpression;
|
|
4
|
+
export declare function isNewExpression(node: ts.Node): node is ts.NewExpression;
|
|
5
|
+
export declare function isObjectLiteralExpression(node: ts.Node): node is ts.ObjectLiteralExpression;
|
|
6
|
+
export declare function isPropertyAssignment(node: ts.Node): node is ts.PropertyAssignment;
|
|
7
|
+
export declare function isShorthandPropertyAssignment(node: ts.Node): node is ts.ShorthandPropertyAssignment;
|
|
8
|
+
export declare function isPropertyAccessExpression(node: ts.Node): node is ts.PropertyAccessExpression;
|
|
9
|
+
export declare function isElementAccessExpression(node: ts.Node): node is ts.ElementAccessExpression;
|
|
10
|
+
export declare function isIdentifier(node: ts.Node): node is ts.Identifier;
|
|
11
|
+
export declare function isStringLiteral(node: ts.Node): node is ts.StringLiteral;
|
|
12
|
+
export declare function isFunctionDeclaration(node: ts.Node): node is ts.FunctionDeclaration;
|
|
13
|
+
export declare function isFunctionExpression(node: ts.Node): node is ts.FunctionExpression;
|
|
14
|
+
export declare function isArrowFunction(node: ts.Node): node is ts.ArrowFunction;
|
|
15
|
+
export declare function isMethodDeclaration(node: ts.Node): node is ts.MethodDeclaration;
|
|
16
|
+
export declare function isConstructorDeclaration(node: ts.Node): node is ts.ConstructorDeclaration;
|
|
17
|
+
export declare function isFunctionLike(node: ts.Node): node is ts.FunctionDeclaration | ts.FunctionExpression | ts.ArrowFunction | ts.MethodDeclaration | ts.ConstructorDeclaration;
|
|
18
|
+
export declare function visitNode(node: ts.Node, visitor: ts.Visitor): ts.Node | undefined;
|
|
19
|
+
export declare function visitEachChild(node: ts.Node, visitor: ts.Visitor, context: ts.TransformationContext): ts.Node;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getTS = getTS;
|
|
4
|
+
exports.isCallExpression = isCallExpression;
|
|
5
|
+
exports.isNewExpression = isNewExpression;
|
|
6
|
+
exports.isObjectLiteralExpression = isObjectLiteralExpression;
|
|
7
|
+
exports.isPropertyAssignment = isPropertyAssignment;
|
|
8
|
+
exports.isShorthandPropertyAssignment = isShorthandPropertyAssignment;
|
|
9
|
+
exports.isPropertyAccessExpression = isPropertyAccessExpression;
|
|
10
|
+
exports.isElementAccessExpression = isElementAccessExpression;
|
|
11
|
+
exports.isIdentifier = isIdentifier;
|
|
12
|
+
exports.isStringLiteral = isStringLiteral;
|
|
13
|
+
exports.isFunctionDeclaration = isFunctionDeclaration;
|
|
14
|
+
exports.isFunctionExpression = isFunctionExpression;
|
|
15
|
+
exports.isArrowFunction = isArrowFunction;
|
|
16
|
+
exports.isMethodDeclaration = isMethodDeclaration;
|
|
17
|
+
exports.isConstructorDeclaration = isConstructorDeclaration;
|
|
18
|
+
exports.isFunctionLike = isFunctionLike;
|
|
19
|
+
exports.visitNode = visitNode;
|
|
20
|
+
exports.visitEachChild = visitEachChild;
|
|
21
|
+
let _ts;
|
|
22
|
+
function getTS() {
|
|
23
|
+
if (!_ts) {
|
|
24
|
+
_ts = require('typescript');
|
|
25
|
+
}
|
|
26
|
+
return _ts;
|
|
27
|
+
}
|
|
28
|
+
function isCallExpression(node) {
|
|
29
|
+
return node.kind === getTS().SyntaxKind.CallExpression;
|
|
30
|
+
}
|
|
31
|
+
function isNewExpression(node) {
|
|
32
|
+
return node.kind === getTS().SyntaxKind.NewExpression;
|
|
33
|
+
}
|
|
34
|
+
function isObjectLiteralExpression(node) {
|
|
35
|
+
return node.kind === getTS().SyntaxKind.ObjectLiteralExpression;
|
|
36
|
+
}
|
|
37
|
+
function isPropertyAssignment(node) {
|
|
38
|
+
return node.kind === getTS().SyntaxKind.PropertyAssignment;
|
|
39
|
+
}
|
|
40
|
+
function isShorthandPropertyAssignment(node) {
|
|
41
|
+
return node.kind === getTS().SyntaxKind.ShorthandPropertyAssignment;
|
|
42
|
+
}
|
|
43
|
+
function isPropertyAccessExpression(node) {
|
|
44
|
+
return node.kind === getTS().SyntaxKind.PropertyAccessExpression;
|
|
45
|
+
}
|
|
46
|
+
function isElementAccessExpression(node) {
|
|
47
|
+
return node.kind === getTS().SyntaxKind.ElementAccessExpression;
|
|
48
|
+
}
|
|
49
|
+
function isIdentifier(node) {
|
|
50
|
+
return node.kind === getTS().SyntaxKind.Identifier;
|
|
51
|
+
}
|
|
52
|
+
function isStringLiteral(node) {
|
|
53
|
+
return node.kind === getTS().SyntaxKind.StringLiteral;
|
|
54
|
+
}
|
|
55
|
+
function isFunctionDeclaration(node) {
|
|
56
|
+
return node.kind === getTS().SyntaxKind.FunctionDeclaration;
|
|
57
|
+
}
|
|
58
|
+
function isFunctionExpression(node) {
|
|
59
|
+
return node.kind === getTS().SyntaxKind.FunctionExpression;
|
|
60
|
+
}
|
|
61
|
+
function isArrowFunction(node) {
|
|
62
|
+
return node.kind === getTS().SyntaxKind.ArrowFunction;
|
|
63
|
+
}
|
|
64
|
+
function isMethodDeclaration(node) {
|
|
65
|
+
return node.kind === getTS().SyntaxKind.MethodDeclaration;
|
|
66
|
+
}
|
|
67
|
+
function isConstructorDeclaration(node) {
|
|
68
|
+
return node.kind === getTS().SyntaxKind.Constructor;
|
|
69
|
+
}
|
|
70
|
+
function isFunctionLike(node) {
|
|
71
|
+
return (isFunctionDeclaration(node) ||
|
|
72
|
+
isFunctionExpression(node) ||
|
|
73
|
+
isArrowFunction(node) ||
|
|
74
|
+
isMethodDeclaration(node) ||
|
|
75
|
+
isConstructorDeclaration(node));
|
|
76
|
+
}
|
|
77
|
+
function visitNode(node, visitor) {
|
|
78
|
+
return getTS().visitNode(node, visitor);
|
|
79
|
+
}
|
|
80
|
+
function visitEachChild(node, visitor, context) {
|
|
81
|
+
return getTS().visitEachChild(node, visitor, context);
|
|
82
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type ts from 'typescript';
|
|
2
|
+
type FunctionLikeNode = ts.FunctionDeclaration | ts.FunctionExpression | ts.ArrowFunction | ts.MethodDeclaration | ts.ConstructorDeclaration;
|
|
3
|
+
/** Update any function-like node with new params and body. */
|
|
4
|
+
export declare function updateFunctionLike(node: FunctionLikeNode, factory: ts.NodeFactory, newParams: ts.ParameterDeclaration[], newBody: typeof node.body): ts.Node | null;
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.updateFunctionLike = updateFunctionLike;
|
|
4
|
+
const ts_helpers_1 = require("./ts-helpers");
|
|
5
|
+
/** Update any function-like node with new params and body. */
|
|
6
|
+
function updateFunctionLike(node, factory, newParams, newBody) {
|
|
7
|
+
if ((0, ts_helpers_1.isFunctionDeclaration)(node)) {
|
|
8
|
+
return factory.updateFunctionDeclaration(node, node.modifiers, node.asteriskToken, node.name, node.typeParameters, newParams, undefined, newBody);
|
|
9
|
+
}
|
|
10
|
+
if ((0, ts_helpers_1.isFunctionExpression)(node)) {
|
|
11
|
+
return factory.updateFunctionExpression(node, node.modifiers, node.asteriskToken, node.name, node.typeParameters, newParams, undefined, newBody);
|
|
12
|
+
}
|
|
13
|
+
if ((0, ts_helpers_1.isArrowFunction)(node)) {
|
|
14
|
+
return factory.updateArrowFunction(node, node.modifiers, node.typeParameters, newParams, undefined, node.equalsGreaterThanToken, newBody);
|
|
15
|
+
}
|
|
16
|
+
if ((0, ts_helpers_1.isMethodDeclaration)(node)) {
|
|
17
|
+
return factory.updateMethodDeclaration(node, node.modifiers, node.asteriskToken, node.name, node.questionToken, node.typeParameters, newParams, undefined, newBody);
|
|
18
|
+
}
|
|
19
|
+
if ((0, ts_helpers_1.isConstructorDeclaration)(node)) {
|
|
20
|
+
return factory.updateConstructorDeclaration(node, node.modifiers, newParams, newBody);
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ts-transform-args",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript transformer that eliminates option object allocations by converting them into positional arguments at compile time. Readable code, zero GC overhead",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"prepublish": "npm run build",
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"test": "npm run build && cd example-project && npm install && npm run build && npm start && cd .. && node tests/snapshot-test.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"typescript",
|
|
14
|
+
"transformer",
|
|
15
|
+
"ts-patch",
|
|
16
|
+
"arguments"
|
|
17
|
+
],
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"typescript": ">=4.7.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^25.3.0",
|
|
24
|
+
"ts-patch": "^3.1.0",
|
|
25
|
+
"typescript": "^5.3.0"
|
|
26
|
+
}
|
|
27
|
+
}
|