@mondaydotcomorg/atp-compiler 0.18.2 → 0.18.4-rc.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/__tests__/unit/default-compiler.test.ts +259 -0
- package/__tests__/unit/plugin-system.test.ts +401 -0
- package/dist/atp-compiler/src/index.d.ts +8 -0
- package/dist/atp-compiler/src/index.d.ts.map +1 -1
- package/dist/atp-compiler/src/index.js +9 -0
- package/dist/atp-compiler/src/index.js.map +1 -1
- package/dist/atp-compiler/src/plugin-system/create-default-compiler.d.ts +40 -0
- package/dist/atp-compiler/src/plugin-system/create-default-compiler.d.ts.map +1 -0
- package/dist/atp-compiler/src/plugin-system/create-default-compiler.js +40 -0
- package/dist/atp-compiler/src/plugin-system/create-default-compiler.js.map +1 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/array-transformer-plugin.d.ts +18 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/array-transformer-plugin.d.ts.map +1 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/array-transformer-plugin.js +45 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/array-transformer-plugin.js.map +1 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/detection-plugin.d.ts +17 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/detection-plugin.d.ts.map +1 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/detection-plugin.js +33 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/detection-plugin.js.map +1 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/index.d.ts +11 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/index.d.ts.map +1 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/index.js +11 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/index.js.map +1 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/loop-transformer-plugin.d.ts +17 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/loop-transformer-plugin.d.ts.map +1 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/loop-transformer-plugin.js +36 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/loop-transformer-plugin.js.map +1 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/promise-transformer-plugin.d.ts +19 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/promise-transformer-plugin.d.ts.map +1 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/promise-transformer-plugin.js +49 -0
- package/dist/atp-compiler/src/plugin-system/default-plugins/promise-transformer-plugin.js.map +1 -0
- package/dist/atp-compiler/src/plugin-system/examples/loop-transformer-plugin.d.ts +31 -0
- package/dist/atp-compiler/src/plugin-system/examples/loop-transformer-plugin.d.ts.map +1 -0
- package/dist/atp-compiler/src/plugin-system/examples/loop-transformer-plugin.js +60 -0
- package/dist/atp-compiler/src/plugin-system/examples/loop-transformer-plugin.js.map +1 -0
- package/dist/atp-compiler/src/plugin-system/examples/security-validator-plugin.d.ts +48 -0
- package/dist/atp-compiler/src/plugin-system/examples/security-validator-plugin.d.ts.map +1 -0
- package/dist/atp-compiler/src/plugin-system/examples/security-validator-plugin.js +108 -0
- package/dist/atp-compiler/src/plugin-system/examples/security-validator-plugin.js.map +1 -0
- package/dist/atp-compiler/src/plugin-system/examples/timeout-plugin.d.ts +53 -0
- package/dist/atp-compiler/src/plugin-system/examples/timeout-plugin.d.ts.map +1 -0
- package/dist/atp-compiler/src/plugin-system/examples/timeout-plugin.js +106 -0
- package/dist/atp-compiler/src/plugin-system/examples/timeout-plugin.js.map +1 -0
- package/dist/atp-compiler/src/plugin-system/index.d.ts +14 -0
- package/dist/atp-compiler/src/plugin-system/index.d.ts.map +1 -0
- package/dist/atp-compiler/src/plugin-system/index.js +16 -0
- package/dist/atp-compiler/src/plugin-system/index.js.map +1 -0
- package/dist/atp-compiler/src/plugin-system/pluggable-compiler.d.ts +102 -0
- package/dist/atp-compiler/src/plugin-system/pluggable-compiler.d.ts.map +1 -0
- package/dist/atp-compiler/src/plugin-system/pluggable-compiler.js +280 -0
- package/dist/atp-compiler/src/plugin-system/pluggable-compiler.js.map +1 -0
- package/dist/atp-compiler/src/plugin-system/plugin-api.d.ts +165 -0
- package/dist/atp-compiler/src/plugin-system/plugin-api.d.ts.map +1 -0
- package/dist/atp-compiler/src/plugin-system/plugin-api.js +180 -0
- package/dist/atp-compiler/src/plugin-system/plugin-api.js.map +1 -0
- package/dist/atp-compiler/src/transformer/index.d.ts +15 -1
- package/dist/atp-compiler/src/transformer/index.d.ts.map +1 -1
- package/dist/atp-compiler/src/transformer/index.js +17 -0
- package/dist/atp-compiler/src/transformer/index.js.map +1 -1
- package/dist/atp-compiler/src/types/compiler-interface.d.ts +61 -0
- package/dist/atp-compiler/src/types/compiler-interface.d.ts.map +1 -0
- package/dist/atp-compiler/src/types/compiler-interface.js +18 -0
- package/dist/atp-compiler/src/types/compiler-interface.js.map +1 -0
- package/dist/runtime/src/approval/index.js +2 -1
- package/dist/runtime/src/approval/index.js.map +1 -1
- package/dist/runtime/src/index.d.ts +1 -1
- package/dist/runtime/src/index.d.ts.map +1 -1
- package/dist/runtime/src/index.js +1 -1
- package/dist/runtime/src/index.js.map +1 -1
- package/dist/runtime/src/llm/index.d.ts +1 -1
- package/dist/runtime/src/llm/index.d.ts.map +1 -1
- package/dist/runtime/src/llm/index.js +1 -1
- package/dist/runtime/src/llm/index.js.map +1 -1
- package/dist/runtime/src/llm/replay.d.ts +75 -0
- package/dist/runtime/src/llm/replay.d.ts.map +1 -1
- package/dist/runtime/src/llm/replay.js +187 -5
- package/dist/runtime/src/llm/replay.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -5
- package/src/index.ts +26 -0
- package/src/plugin-system/create-default-compiler.ts +57 -0
- package/src/plugin-system/default-plugins/array-transformer-plugin.ts +57 -0
- package/src/plugin-system/default-plugins/detection-plugin.ts +41 -0
- package/src/plugin-system/default-plugins/index.ts +12 -0
- package/src/plugin-system/default-plugins/loop-transformer-plugin.ts +47 -0
- package/src/plugin-system/default-plugins/promise-transformer-plugin.ts +63 -0
- package/src/plugin-system/examples/loop-transformer-plugin.ts +76 -0
- package/src/plugin-system/examples/security-validator-plugin.ts +168 -0
- package/src/plugin-system/examples/timeout-plugin.ts +158 -0
- package/src/plugin-system/index.ts +19 -0
- package/src/plugin-system/pluggable-compiler.ts +318 -0
- package/src/plugin-system/plugin-api.ts +330 -0
- package/src/transformer/index.ts +21 -1
- package/src/types/compiler-interface.ts +79 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests to verify createDefaultCompiler() provides identical functionality to ATPCompiler
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { ATPCompiler } from '../../src/transformer/index.js';
|
|
6
|
+
import { createDefaultCompiler } from '../../src/plugin-system/create-default-compiler.js';
|
|
7
|
+
import type { TransformationPlugin } from '../../src/plugin-system/plugin-api.js';
|
|
8
|
+
|
|
9
|
+
describe('Default Compiler - Functional Equivalence', () => {
|
|
10
|
+
describe('Detection', () => {
|
|
11
|
+
it('should detect for-of-await patterns like ATPCompiler', async () => {
|
|
12
|
+
const code = `
|
|
13
|
+
for (const item of items) {
|
|
14
|
+
await processItem(item);
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const atpCompiler = new ATPCompiler();
|
|
19
|
+
const pluggableCompiler = createDefaultCompiler();
|
|
20
|
+
|
|
21
|
+
const atpResult = atpCompiler.detect(code);
|
|
22
|
+
const pluggableResult = await pluggableCompiler.detect(code);
|
|
23
|
+
|
|
24
|
+
expect(pluggableResult.needsTransform).toBe(atpResult.needsTransform);
|
|
25
|
+
expect(pluggableResult.patterns).toEqual(expect.arrayContaining(atpResult.patterns));
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should detect map-async patterns like ATPCompiler', async () => {
|
|
29
|
+
const code = `
|
|
30
|
+
const results = items.map(async (item) => {
|
|
31
|
+
return await processItem(item);
|
|
32
|
+
});
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const atpCompiler = new ATPCompiler();
|
|
36
|
+
const pluggableCompiler = createDefaultCompiler();
|
|
37
|
+
|
|
38
|
+
const atpResult = atpCompiler.detect(code);
|
|
39
|
+
const pluggableResult = await pluggableCompiler.detect(code);
|
|
40
|
+
|
|
41
|
+
expect(pluggableResult.needsTransform).toBe(atpResult.needsTransform);
|
|
42
|
+
expect(pluggableResult.patterns).toEqual(expect.arrayContaining(atpResult.patterns));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should detect Promise.all patterns like ATPCompiler', async () => {
|
|
46
|
+
const code = `
|
|
47
|
+
const results = await Promise.all(promises);
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
const atpCompiler = new ATPCompiler();
|
|
51
|
+
const pluggableCompiler = createDefaultCompiler();
|
|
52
|
+
|
|
53
|
+
const atpResult = atpCompiler.detect(code);
|
|
54
|
+
const pluggableResult = await pluggableCompiler.detect(code);
|
|
55
|
+
|
|
56
|
+
expect(pluggableResult.needsTransform).toBe(atpResult.needsTransform);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('Transformation', () => {
|
|
61
|
+
it('should transform for-of loops like ATPCompiler', async () => {
|
|
62
|
+
const code = `
|
|
63
|
+
for (const item of items) {
|
|
64
|
+
await processItem(item);
|
|
65
|
+
}
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
const atpCompiler = new ATPCompiler();
|
|
69
|
+
const pluggableCompiler = createDefaultCompiler();
|
|
70
|
+
|
|
71
|
+
const atpResult = atpCompiler.transform(code);
|
|
72
|
+
const pluggableResult = await pluggableCompiler.transform(code);
|
|
73
|
+
|
|
74
|
+
// Both should transform
|
|
75
|
+
expect(pluggableResult.transformed).toBe(true);
|
|
76
|
+
expect(atpResult.transformed).toBe(true);
|
|
77
|
+
|
|
78
|
+
// Both should produce transformed code (not exact match due to formatting)
|
|
79
|
+
expect(pluggableResult.code).toContain('__runtime.resumableForOf');
|
|
80
|
+
expect(atpResult.code).toContain('__runtime.resumableForOf');
|
|
81
|
+
|
|
82
|
+
// Metadata should be similar
|
|
83
|
+
expect(pluggableResult.metadata.loopCount).toBeGreaterThan(0);
|
|
84
|
+
expect(atpResult.metadata.loopCount).toBeGreaterThan(0);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should transform array methods like ATPCompiler', async () => {
|
|
88
|
+
const code = `
|
|
89
|
+
const results = items.map(async (item) => {
|
|
90
|
+
return await processItem(item);
|
|
91
|
+
});
|
|
92
|
+
`;
|
|
93
|
+
|
|
94
|
+
const atpCompiler = new ATPCompiler();
|
|
95
|
+
const pluggableCompiler = createDefaultCompiler();
|
|
96
|
+
|
|
97
|
+
const atpResult = atpCompiler.transform(code);
|
|
98
|
+
const pluggableResult = await pluggableCompiler.transform(code);
|
|
99
|
+
|
|
100
|
+
expect(pluggableResult.transformed).toBe(true);
|
|
101
|
+
expect(atpResult.transformed).toBe(true);
|
|
102
|
+
|
|
103
|
+
// Code should contain transformation
|
|
104
|
+
expect(pluggableResult.code).toContain('map');
|
|
105
|
+
expect(atpResult.code).toContain('map');
|
|
106
|
+
|
|
107
|
+
// Metadata counts should match
|
|
108
|
+
expect(pluggableResult.metadata).toMatchObject({
|
|
109
|
+
loopCount: atpResult.metadata.loopCount,
|
|
110
|
+
arrayMethodCount: atpResult.metadata.arrayMethodCount,
|
|
111
|
+
parallelCallCount: atpResult.metadata.parallelCallCount,
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should handle code that needs no transformation', async () => {
|
|
116
|
+
const code = `
|
|
117
|
+
const x = 1 + 1;
|
|
118
|
+
console.log(x);
|
|
119
|
+
`;
|
|
120
|
+
|
|
121
|
+
const atpCompiler = new ATPCompiler();
|
|
122
|
+
const pluggableCompiler = createDefaultCompiler();
|
|
123
|
+
|
|
124
|
+
const atpResult = atpCompiler.transform(code);
|
|
125
|
+
const pluggableResult = await pluggableCompiler.transform(code);
|
|
126
|
+
|
|
127
|
+
expect(pluggableResult.transformed).toBe(false);
|
|
128
|
+
expect(atpResult.transformed).toBe(false);
|
|
129
|
+
|
|
130
|
+
// Code should be unchanged
|
|
131
|
+
expect(pluggableResult.code).toBe(code);
|
|
132
|
+
expect(atpResult.code).toBe(code);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('Configuration', () => {
|
|
137
|
+
it('should respect enableBatchParallel config', async () => {
|
|
138
|
+
const code = `
|
|
139
|
+
const results = await Promise.all([
|
|
140
|
+
fetch('/api/1'),
|
|
141
|
+
fetch('/api/2'),
|
|
142
|
+
]);
|
|
143
|
+
`;
|
|
144
|
+
|
|
145
|
+
const compilerWithBatch = createDefaultCompiler({ enableBatchParallel: true });
|
|
146
|
+
const compilerWithoutBatch = createDefaultCompiler({ enableBatchParallel: false });
|
|
147
|
+
|
|
148
|
+
const resultWith = await compilerWithBatch.transform(code);
|
|
149
|
+
const resultWithout = await compilerWithoutBatch.transform(code);
|
|
150
|
+
|
|
151
|
+
// Both should transform
|
|
152
|
+
expect(resultWith.transformed).toBe(true);
|
|
153
|
+
expect(resultWithout.transformed).toBe(true);
|
|
154
|
+
|
|
155
|
+
// The code output might differ based on batch config
|
|
156
|
+
// (exact differences depend on implementation)
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should respect batchSizeThreshold config', async () => {
|
|
160
|
+
const code = `
|
|
161
|
+
for (const item of items) {
|
|
162
|
+
await processItem(item);
|
|
163
|
+
}
|
|
164
|
+
`;
|
|
165
|
+
|
|
166
|
+
const compilerSmall = createDefaultCompiler({ batchSizeThreshold: 5 });
|
|
167
|
+
const compilerLarge = createDefaultCompiler({ batchSizeThreshold: 100 });
|
|
168
|
+
|
|
169
|
+
const resultSmall = await compilerSmall.transform(code);
|
|
170
|
+
const resultLarge = await compilerLarge.transform(code);
|
|
171
|
+
|
|
172
|
+
// Both should transform
|
|
173
|
+
expect(resultSmall.transformed).toBe(true);
|
|
174
|
+
expect(resultLarge.transformed).toBe(true);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe('Extensibility', () => {
|
|
179
|
+
it('should allow adding custom plugins on top of defaults', async () => {
|
|
180
|
+
const compiler = createDefaultCompiler();
|
|
181
|
+
|
|
182
|
+
let customPluginExecuted = false;
|
|
183
|
+
|
|
184
|
+
// Add custom transformation plugin that counts identifiers
|
|
185
|
+
const customPlugin: TransformationPlugin = {
|
|
186
|
+
name: 'custom-plugin',
|
|
187
|
+
version: '1.0.0',
|
|
188
|
+
priority: 50, // Lower priority to run after defaults
|
|
189
|
+
getVisitor: () => ({
|
|
190
|
+
Identifier: (path: any) => {
|
|
191
|
+
if (path.node.name === 'item') {
|
|
192
|
+
customPluginExecuted = true;
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
}),
|
|
196
|
+
getMetadata: () => ({}),
|
|
197
|
+
reset: () => {
|
|
198
|
+
customPluginExecuted = false;
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
compiler.use(customPlugin);
|
|
203
|
+
|
|
204
|
+
// Use code that will trigger transformation
|
|
205
|
+
const code = `
|
|
206
|
+
for (const item of items) {
|
|
207
|
+
await processItem(item);
|
|
208
|
+
}
|
|
209
|
+
`;
|
|
210
|
+
const result = await compiler.transform(code);
|
|
211
|
+
|
|
212
|
+
// Verify plugin was registered and executed
|
|
213
|
+
expect(customPluginExecuted).toBe(true);
|
|
214
|
+
expect(result).toHaveProperty('code');
|
|
215
|
+
expect(result.transformed).toBe(true);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('should work with AST caching', async () => {
|
|
219
|
+
const compiler = createDefaultCompiler();
|
|
220
|
+
|
|
221
|
+
const code = `const x = 42;`;
|
|
222
|
+
|
|
223
|
+
// First call - parses and caches
|
|
224
|
+
await compiler.detect(code);
|
|
225
|
+
expect(compiler.getCacheStats().size).toBe(1);
|
|
226
|
+
|
|
227
|
+
// Second call - uses cache
|
|
228
|
+
await compiler.transform(code);
|
|
229
|
+
expect(compiler.getCacheStats().size).toBe(1);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
describe('Drop-in Replacement', () => {
|
|
234
|
+
it('should be usable as ATPCompilerLike type', async () => {
|
|
235
|
+
// This test verifies the type compatibility
|
|
236
|
+
const compiler = createDefaultCompiler({
|
|
237
|
+
enableBatchParallel: true,
|
|
238
|
+
batchSizeThreshold: 10,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const code = `
|
|
242
|
+
for (const item of items) {
|
|
243
|
+
await processItem(item);
|
|
244
|
+
}
|
|
245
|
+
`;
|
|
246
|
+
|
|
247
|
+
// Should work with same API
|
|
248
|
+
const detection = await compiler.detect(code);
|
|
249
|
+
expect(detection).toHaveProperty('needsTransform');
|
|
250
|
+
expect(detection).toHaveProperty('patterns');
|
|
251
|
+
|
|
252
|
+
const result = await compiler.transform(code);
|
|
253
|
+
expect(result).toHaveProperty('code');
|
|
254
|
+
expect(result).toHaveProperty('transformed');
|
|
255
|
+
expect(result).toHaveProperty('metadata');
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the Plugin System
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { PluginRegistry } from '../../src/plugin-system/plugin-api.js';
|
|
6
|
+
import { PluggableCompiler } from '../../src/plugin-system/pluggable-compiler.js';
|
|
7
|
+
import type {
|
|
8
|
+
DetectionPlugin,
|
|
9
|
+
TransformationPlugin,
|
|
10
|
+
OptimizerPlugin,
|
|
11
|
+
ValidatorPlugin,
|
|
12
|
+
BabelVisitor,
|
|
13
|
+
} from '../../src/plugin-system/plugin-api.js';
|
|
14
|
+
import type { CompilerConfig, TransformMetadata, DetectionResult } from '../../src/types.js';
|
|
15
|
+
import * as t from '@babel/types';
|
|
16
|
+
|
|
17
|
+
describe('PluginRegistry', () => {
|
|
18
|
+
let registry: PluginRegistry;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
registry = new PluginRegistry();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('Plugin Registration', () => {
|
|
25
|
+
it('should register a detection plugin', () => {
|
|
26
|
+
const plugin: DetectionPlugin = {
|
|
27
|
+
name: 'test-detector',
|
|
28
|
+
version: '1.0.0',
|
|
29
|
+
priority: 100,
|
|
30
|
+
patterns: ['for-of-await'],
|
|
31
|
+
detect: async (code: string, ast: t.File): Promise<DetectionResult> => ({
|
|
32
|
+
needsTransform: true,
|
|
33
|
+
patterns: ['for-of-await'],
|
|
34
|
+
batchableParallel: false,
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
registry.register(plugin);
|
|
39
|
+
const plugins = registry.getDetectors();
|
|
40
|
+
expect(plugins).toHaveLength(1);
|
|
41
|
+
expect(plugins[0]!.name).toBe('test-detector');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should register a transformation plugin', () => {
|
|
45
|
+
const plugin: TransformationPlugin = {
|
|
46
|
+
name: 'test-transformer',
|
|
47
|
+
version: '1.0.0',
|
|
48
|
+
priority: 100,
|
|
49
|
+
getVisitor: (config: CompilerConfig): BabelVisitor => ({}),
|
|
50
|
+
getMetadata: () => ({ loopCount: 0 }),
|
|
51
|
+
reset: () => {},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
registry.register(plugin);
|
|
55
|
+
const plugins = registry.getTransformers();
|
|
56
|
+
expect(plugins).toHaveLength(1);
|
|
57
|
+
expect(plugins[0]!.name).toBe('test-transformer');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should throw error when registering duplicate plugin names', () => {
|
|
61
|
+
const plugin1: DetectionPlugin = {
|
|
62
|
+
name: 'duplicate',
|
|
63
|
+
version: '1.0.0',
|
|
64
|
+
priority: 100,
|
|
65
|
+
patterns: ['for-of-await'],
|
|
66
|
+
detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const plugin2: DetectionPlugin = {
|
|
70
|
+
name: 'duplicate',
|
|
71
|
+
version: '2.0.0',
|
|
72
|
+
priority: 50,
|
|
73
|
+
patterns: ['map-async'],
|
|
74
|
+
detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
registry.register(plugin1);
|
|
78
|
+
expect(() => registry.register(plugin2)).toThrow('Plugin "duplicate" is already registered');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should sort plugins by priority (higher first)', () => {
|
|
82
|
+
const plugin1: DetectionPlugin = {
|
|
83
|
+
name: 'low-priority',
|
|
84
|
+
version: '1.0.0',
|
|
85
|
+
priority: 10,
|
|
86
|
+
patterns: [],
|
|
87
|
+
detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const plugin2: DetectionPlugin = {
|
|
91
|
+
name: 'high-priority',
|
|
92
|
+
version: '1.0.0',
|
|
93
|
+
priority: 100,
|
|
94
|
+
patterns: [],
|
|
95
|
+
detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
registry.register(plugin1);
|
|
99
|
+
registry.register(plugin2);
|
|
100
|
+
|
|
101
|
+
const plugins = registry.getDetectors();
|
|
102
|
+
expect(plugins[0]!.name).toBe('high-priority');
|
|
103
|
+
expect(plugins[1]!.name).toBe('low-priority');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('Plugin Unregistration', () => {
|
|
108
|
+
it('should unregister a plugin by name', () => {
|
|
109
|
+
const plugin: DetectionPlugin = {
|
|
110
|
+
name: 'to-remove',
|
|
111
|
+
version: '1.0.0',
|
|
112
|
+
priority: 100,
|
|
113
|
+
patterns: [],
|
|
114
|
+
detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
registry.register(plugin);
|
|
118
|
+
expect(registry.getDetectors()).toHaveLength(1);
|
|
119
|
+
|
|
120
|
+
const removed = registry.unregister('to-remove');
|
|
121
|
+
expect(removed).toBe(true);
|
|
122
|
+
expect(registry.getDetectors()).toHaveLength(0);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should return false when unregistering non-existent plugin', () => {
|
|
126
|
+
const removed = registry.unregister('non-existent');
|
|
127
|
+
expect(removed).toBe(false);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('Plugin Retrieval', () => {
|
|
132
|
+
it('should find plugin by name', () => {
|
|
133
|
+
const plugin: DetectionPlugin = {
|
|
134
|
+
name: 'findable',
|
|
135
|
+
version: '1.0.0',
|
|
136
|
+
priority: 100,
|
|
137
|
+
patterns: [],
|
|
138
|
+
detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
registry.register(plugin);
|
|
142
|
+
const found = registry.findPlugin('findable');
|
|
143
|
+
expect(found).toBeDefined();
|
|
144
|
+
expect(found?.name).toBe('findable');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should return undefined for non-existent plugin', () => {
|
|
148
|
+
const found = registry.findPlugin('non-existent');
|
|
149
|
+
expect(found).toBeUndefined();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe('PluggableCompiler', () => {
|
|
155
|
+
let compiler: PluggableCompiler;
|
|
156
|
+
|
|
157
|
+
beforeEach(() => {
|
|
158
|
+
compiler = new PluggableCompiler();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe('Plugin Management', () => {
|
|
162
|
+
it('should register plugin via use() method (chainable)', () => {
|
|
163
|
+
const plugin: DetectionPlugin = {
|
|
164
|
+
name: 'test-plugin',
|
|
165
|
+
version: '1.0.0',
|
|
166
|
+
patterns: [],
|
|
167
|
+
detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const result = compiler.use(plugin);
|
|
171
|
+
expect(result).toBe(compiler); // Chainable
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should unregister plugin via remove() method', () => {
|
|
175
|
+
const plugin: DetectionPlugin = {
|
|
176
|
+
name: 'removable',
|
|
177
|
+
version: '1.0.0',
|
|
178
|
+
patterns: [],
|
|
179
|
+
detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
compiler.use(plugin);
|
|
183
|
+
const removed = compiler.remove('removable');
|
|
184
|
+
expect(removed).toBe(true);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe('Detection', () => {
|
|
189
|
+
it('should detect patterns using registered plugins', async () => {
|
|
190
|
+
const plugin: DetectionPlugin = {
|
|
191
|
+
name: 'for-of-detector',
|
|
192
|
+
version: '1.0.0',
|
|
193
|
+
priority: 100,
|
|
194
|
+
patterns: ['for-of-await'],
|
|
195
|
+
detect: async (code: string): Promise<DetectionResult> => {
|
|
196
|
+
return code.includes('for')
|
|
197
|
+
? { needsTransform: true, patterns: ['for-of-await'], batchableParallel: false }
|
|
198
|
+
: { needsTransform: false, patterns: [], batchableParallel: false };
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
compiler.use(plugin);
|
|
203
|
+
|
|
204
|
+
const code = `
|
|
205
|
+
for (const item of items) {
|
|
206
|
+
console.log(item);
|
|
207
|
+
}
|
|
208
|
+
`;
|
|
209
|
+
|
|
210
|
+
const result = await compiler.detect(code);
|
|
211
|
+
expect(result.needsTransform).toBe(true);
|
|
212
|
+
expect(result.patterns).toContain('for-of-await');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should handle detection errors gracefully', async () => {
|
|
216
|
+
const plugin: DetectionPlugin = {
|
|
217
|
+
name: 'failing-detector',
|
|
218
|
+
version: '1.0.0',
|
|
219
|
+
priority: 100,
|
|
220
|
+
patterns: [],
|
|
221
|
+
detect: async () => {
|
|
222
|
+
throw new Error('Detection failed');
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
compiler.use(plugin);
|
|
227
|
+
|
|
228
|
+
const result = await compiler.detect('some code');
|
|
229
|
+
expect(result.needsTransform).toBe(false);
|
|
230
|
+
expect(result.patterns).toEqual([]);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe('Transformation', () => {
|
|
235
|
+
it('should execute transform with registered transformation plugins', async () => {
|
|
236
|
+
const plugin: TransformationPlugin = {
|
|
237
|
+
name: 'simple-transformer',
|
|
238
|
+
version: '1.0.0',
|
|
239
|
+
priority: 100,
|
|
240
|
+
getVisitor: (): BabelVisitor => ({}),
|
|
241
|
+
getMetadata: () => ({ loopCount: 1 }),
|
|
242
|
+
reset: () => {},
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
compiler.use(plugin);
|
|
246
|
+
|
|
247
|
+
const code = `const foo = 42;`;
|
|
248
|
+
const result = await compiler.transform(code);
|
|
249
|
+
expect(result.code).toBeTruthy(); // Code was processed
|
|
250
|
+
expect(result.metadata).toBeDefined(); // Metadata is present
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe('Lifecycle Management', () => {
|
|
255
|
+
it('should initialize plugins automatically on first use', async () => {
|
|
256
|
+
let initialized = false;
|
|
257
|
+
|
|
258
|
+
const plugin: DetectionPlugin = {
|
|
259
|
+
name: 'lifecycle-plugin',
|
|
260
|
+
version: '1.0.0',
|
|
261
|
+
priority: 100,
|
|
262
|
+
patterns: [],
|
|
263
|
+
detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
|
|
264
|
+
initialize: async () => {
|
|
265
|
+
initialized = true;
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
compiler.use(plugin);
|
|
270
|
+
await compiler.detect('test code');
|
|
271
|
+
|
|
272
|
+
expect(initialized).toBe(true);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('should dispose plugins on compiler disposal', async () => {
|
|
276
|
+
let disposed = false;
|
|
277
|
+
|
|
278
|
+
const plugin: DetectionPlugin = {
|
|
279
|
+
name: 'disposable-plugin',
|
|
280
|
+
version: '1.0.0',
|
|
281
|
+
patterns: [],
|
|
282
|
+
detect: async () => ({ needsTransform: false, patterns: [], batchableParallel: false }),
|
|
283
|
+
dispose: async () => {
|
|
284
|
+
disposed = true;
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
compiler.use(plugin);
|
|
289
|
+
await compiler.dispose();
|
|
290
|
+
|
|
291
|
+
expect(disposed).toBe(true);
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
describe('AST Caching', () => {
|
|
297
|
+
it('should cache AST between detect() and transform()', async () => {
|
|
298
|
+
const compiler = new PluggableCompiler();
|
|
299
|
+
|
|
300
|
+
const code = `const x = 42;`;
|
|
301
|
+
|
|
302
|
+
// First call - should parse and cache
|
|
303
|
+
await compiler.detect(code);
|
|
304
|
+
expect(compiler.getCacheStats().size).toBe(1);
|
|
305
|
+
|
|
306
|
+
// Second call - should use cache
|
|
307
|
+
await compiler.transform(code);
|
|
308
|
+
expect(compiler.getCacheStats().size).toBe(1); // Still 1, not 2
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('should cache different code snippets separately', async () => {
|
|
312
|
+
const compiler = new PluggableCompiler();
|
|
313
|
+
|
|
314
|
+
const code1 = `const x = 1;`;
|
|
315
|
+
const code2 = `const y = 2;`;
|
|
316
|
+
|
|
317
|
+
await compiler.detect(code1);
|
|
318
|
+
await compiler.detect(code2);
|
|
319
|
+
|
|
320
|
+
expect(compiler.getCacheStats().size).toBe(2);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('should clear cache on clearCache() call', async () => {
|
|
324
|
+
const compiler = new PluggableCompiler();
|
|
325
|
+
|
|
326
|
+
await compiler.detect(`const x = 1;`);
|
|
327
|
+
expect(compiler.getCacheStats().size).toBe(1);
|
|
328
|
+
|
|
329
|
+
compiler.clearCache();
|
|
330
|
+
expect(compiler.getCacheStats().size).toBe(0);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('should clear cache on dispose()', async () => {
|
|
334
|
+
const compiler = new PluggableCompiler();
|
|
335
|
+
|
|
336
|
+
await compiler.detect(`const x = 1;`);
|
|
337
|
+
expect(compiler.getCacheStats().size).toBe(1);
|
|
338
|
+
|
|
339
|
+
await compiler.dispose();
|
|
340
|
+
expect(compiler.getCacheStats().size).toBe(0);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it('should return cache statistics', () => {
|
|
344
|
+
const compiler = new PluggableCompiler();
|
|
345
|
+
|
|
346
|
+
const stats = compiler.getCacheStats();
|
|
347
|
+
expect(stats).toEqual({
|
|
348
|
+
size: 0,
|
|
349
|
+
enabled: true,
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
describe('Plugin Integration', () => {
|
|
355
|
+
it('should work with detection + transformation plugins', async () => {
|
|
356
|
+
const compiler = new PluggableCompiler();
|
|
357
|
+
|
|
358
|
+
// Detection plugin
|
|
359
|
+
compiler.use({
|
|
360
|
+
name: 'detector',
|
|
361
|
+
version: '1.0.0',
|
|
362
|
+
priority: 100,
|
|
363
|
+
patterns: ['for-of-await'],
|
|
364
|
+
detect: async (code: string) =>
|
|
365
|
+
code.includes('for')
|
|
366
|
+
? { needsTransform: true, patterns: ['for-of-await'], batchableParallel: false }
|
|
367
|
+
: { needsTransform: false, patterns: [], batchableParallel: false },
|
|
368
|
+
} as DetectionPlugin);
|
|
369
|
+
|
|
370
|
+
// Transformation plugin
|
|
371
|
+
let loopCount = 0;
|
|
372
|
+
compiler.use({
|
|
373
|
+
name: 'transformer',
|
|
374
|
+
version: '1.0.0',
|
|
375
|
+
priority: 100,
|
|
376
|
+
getVisitor: () => ({
|
|
377
|
+
ForOfStatement: (path: any) => {
|
|
378
|
+
loopCount++;
|
|
379
|
+
},
|
|
380
|
+
}),
|
|
381
|
+
getMetadata: () => ({ loopCount }),
|
|
382
|
+
reset: () => {
|
|
383
|
+
loopCount = 0;
|
|
384
|
+
},
|
|
385
|
+
} as TransformationPlugin);
|
|
386
|
+
|
|
387
|
+
const code = `
|
|
388
|
+
for (const item of items) {
|
|
389
|
+
console.log(item);
|
|
390
|
+
}
|
|
391
|
+
`;
|
|
392
|
+
|
|
393
|
+
const detection = await compiler.detect(code);
|
|
394
|
+
expect(detection.needsTransform).toBe(true);
|
|
395
|
+
|
|
396
|
+
const result = await compiler.transform(code);
|
|
397
|
+
expect(result.transformed).toBe(true);
|
|
398
|
+
expect(result.metadata.loopCount).toBeGreaterThan(0);
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
export * from './types.js';
|
|
2
2
|
export * from './transformer/index.js';
|
|
3
3
|
export * from './runtime/index.js';
|
|
4
|
+
export * from './types/compiler-interface.js';
|
|
5
|
+
export * from './plugin-system/index.js';
|
|
4
6
|
export { ATPCompiler } from './transformer/index.js';
|
|
5
7
|
export { initializeRuntime, cleanupRuntime } from './runtime/index.js';
|
|
8
|
+
export { PluggableCompiler } from './plugin-system/pluggable-compiler.js';
|
|
9
|
+
export { PluginRegistry } from './plugin-system/plugin-api.js';
|
|
10
|
+
export { createDefaultCompiler } from './plugin-system/create-default-compiler.js';
|
|
11
|
+
export type { CompilerPlugin, DetectionPlugin, TransformationPlugin, OptimizerPlugin, ValidatorPlugin, PluginContext, BabelVisitor, } from './plugin-system/plugin-api.js';
|
|
12
|
+
export type { ATPCompilerLike } from './plugin-system/create-default-compiler.js';
|
|
13
|
+
export type { ICompiler, CacheStats } from './types/compiler-interface.js';
|
|
6
14
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,+BAA+B,CAAC;AAG9C,cAAc,0BAA0B,CAAC;AAGzC,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AAGnF,YAAY,EACX,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,eAAe,EACf,aAAa,EACb,YAAY,GACZ,MAAM,+BAA+B,CAAC;AACvC,YAAY,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAGlF,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC"}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
// Core exports
|
|
1
2
|
export * from './types.js';
|
|
2
3
|
export * from './transformer/index.js';
|
|
3
4
|
export * from './runtime/index.js';
|
|
5
|
+
// Compiler interface exports
|
|
6
|
+
export * from './types/compiler-interface.js';
|
|
7
|
+
// Plugin system exports
|
|
8
|
+
export * from './plugin-system/index.js';
|
|
9
|
+
// Main exports
|
|
4
10
|
export { ATPCompiler } from './transformer/index.js';
|
|
5
11
|
export { initializeRuntime, cleanupRuntime } from './runtime/index.js';
|
|
12
|
+
export { PluggableCompiler } from './plugin-system/pluggable-compiler.js';
|
|
13
|
+
export { PluginRegistry } from './plugin-system/plugin-api.js';
|
|
14
|
+
export { createDefaultCompiler } from './plugin-system/create-default-compiler.js';
|
|
6
15
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AAEnC,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA,eAAe;AACf,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AAEnC,6BAA6B;AAC7B,cAAc,+BAA+B,CAAC;AAE9C,wBAAwB;AACxB,cAAc,0BAA0B,CAAC;AAEzC,eAAe;AACf,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC"}
|