taist 0.1.11 → 0.1.13
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/lib/transform.js +61 -4
- package/package.json +1 -1
package/lib/transform.js
CHANGED
|
@@ -30,7 +30,7 @@ export function hasExports(code) {
|
|
|
30
30
|
/**
|
|
31
31
|
* Find all exports in the source code
|
|
32
32
|
* @param {string} source - Source code
|
|
33
|
-
* @returns {Array<{name: string, type: 'function'|'const'|'class', declaration: string|null}>}
|
|
33
|
+
* @returns {Array<{name: string, type: 'function'|'const'|'class'|'object', declaration: string|null}>}
|
|
34
34
|
*/
|
|
35
35
|
export function findExports(source) {
|
|
36
36
|
const exports = [];
|
|
@@ -49,6 +49,16 @@ export function findExports(source) {
|
|
|
49
49
|
exports.push({ name: match[1], type: "const", declaration: "inline" });
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// Match: export const name = { - object literal exports
|
|
53
|
+
// Negative lookahead to avoid matching functions/arrows already caught above
|
|
54
|
+
const objectExportRegex = /export\s+const\s+(\w+)\s*=\s*\{/g;
|
|
55
|
+
while ((match = objectExportRegex.exec(source)) !== null) {
|
|
56
|
+
const name = match[1];
|
|
57
|
+
// Skip if already found as a function/const export
|
|
58
|
+
if (exports.some(e => e.name === name)) continue;
|
|
59
|
+
exports.push({ name, type: "object", declaration: "inline" });
|
|
60
|
+
}
|
|
61
|
+
|
|
52
62
|
// Match: export class ClassName
|
|
53
63
|
const classExportRegex = /export\s+class\s+(\w+)/g;
|
|
54
64
|
while ((match = classExportRegex.exec(source)) !== null) {
|
|
@@ -66,13 +76,14 @@ export function findExports(source) {
|
|
|
66
76
|
const isFunction = new RegExp(`function\\s+${name}\\s*\\(`).test(source) ||
|
|
67
77
|
new RegExp(`(const|let|var)\\s+${name}\\s*=\\s*(async\\s*)?\\(`).test(source) ||
|
|
68
78
|
new RegExp(`(const|let|var)\\s+${name}\\s*=\\s*(async\\s+)?function`).test(source);
|
|
79
|
+
const isObject = new RegExp(`(const|let|var)\\s+${name}\\s*=\\s*\\{`).test(source);
|
|
69
80
|
|
|
70
81
|
// Skip if already found as inline export
|
|
71
82
|
if (exports.some(e => e.name === name)) continue;
|
|
72
83
|
|
|
73
84
|
exports.push({
|
|
74
85
|
name,
|
|
75
|
-
type: isClass ? "class" : isFunction ? "function" : "unknown",
|
|
86
|
+
type: isClass ? "class" : isFunction ? "function" : isObject ? "object" : "unknown",
|
|
76
87
|
declaration: "named"
|
|
77
88
|
});
|
|
78
89
|
}
|
|
@@ -134,13 +145,16 @@ export function transformSource(source, moduleNameOrOptions, tracerImportPath) {
|
|
|
134
145
|
import { getGlobalReporter as __taist_getReporter } from "${reporterPath}";
|
|
135
146
|
import { getContext as __taist_getContext, runWithContext as __taist_runWithContext, generateId as __taist_generateId } from "${traceContextPath}";
|
|
136
147
|
const __taist_reporter = __taist_getReporter();
|
|
148
|
+
const __taist_debug = process.env.TAIST_DEBUG === 'true';
|
|
137
149
|
// Eagerly connect to collector if socket path is set (build-time instrumentation)
|
|
138
150
|
if (process.env.TAIST_COLLECTOR_SOCKET && !__taist_reporter.isConnected()) {
|
|
139
151
|
__taist_reporter.connectEager();
|
|
140
152
|
}
|
|
141
153
|
const __taist_wrap = (fn, name) => {
|
|
142
154
|
if (typeof fn !== 'function') return fn;
|
|
155
|
+
if (__taist_debug) console.log('[taist] wrapping:', name);
|
|
143
156
|
const wrapped = function(...args) {
|
|
157
|
+
if (__taist_debug) console.log('[taist] CALLED:', name);
|
|
144
158
|
const parentCtx = __taist_getContext();
|
|
145
159
|
const id = __taist_generateId();
|
|
146
160
|
const depth = parentCtx.depth;
|
|
@@ -205,6 +219,21 @@ const __taist_instrumentClass = (cls, name) => {
|
|
|
205
219
|
}
|
|
206
220
|
return cls;
|
|
207
221
|
};
|
|
222
|
+
const __taist_instrumentObject = (obj, name, visited = new WeakSet()) => {
|
|
223
|
+
if (!obj || typeof obj !== 'object' || visited.has(obj)) return obj;
|
|
224
|
+
if (__taist_debug) console.log('[taist] instrumentObject:', name, Object.keys(obj));
|
|
225
|
+
visited.add(obj);
|
|
226
|
+
for (const key of Object.keys(obj)) {
|
|
227
|
+
const value = obj[key];
|
|
228
|
+
if (typeof value === 'function') {
|
|
229
|
+
if (__taist_debug) console.log('[taist] found method:', name + '.' + key);
|
|
230
|
+
obj[key] = __taist_wrap(value, name + '.' + key);
|
|
231
|
+
} else if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
232
|
+
__taist_instrumentObject(value, name + '.' + key, visited);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return obj;
|
|
236
|
+
};
|
|
208
237
|
const __taist_module = "${moduleName}";
|
|
209
238
|
// --- END TAIST ---
|
|
210
239
|
|
|
@@ -227,6 +256,20 @@ const __taist_instrumentClass = (cls, name) => {
|
|
|
227
256
|
__taist.instrument(cls, name);
|
|
228
257
|
return cls;
|
|
229
258
|
};
|
|
259
|
+
const __taist_instrumentObject = (obj, name, visited = new WeakSet()) => {
|
|
260
|
+
if (!__taist?.options?.enabled) return obj;
|
|
261
|
+
if (!obj || typeof obj !== 'object' || visited.has(obj)) return obj;
|
|
262
|
+
visited.add(obj);
|
|
263
|
+
for (const key of Object.keys(obj)) {
|
|
264
|
+
const value = obj[key];
|
|
265
|
+
if (typeof value === 'function') {
|
|
266
|
+
obj[key] = __taist_wrap(value, name + '.' + key);
|
|
267
|
+
} else if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
268
|
+
__taist_instrumentObject(value, name + '.' + key, visited);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return obj;
|
|
272
|
+
};
|
|
230
273
|
const __taist_module = "${moduleName}";
|
|
231
274
|
// --- END TAIST ---
|
|
232
275
|
|
|
@@ -246,9 +289,10 @@ const __taist_module = "${moduleName}";
|
|
|
246
289
|
|
|
247
290
|
let transformed = shebang + injection + sourceWithoutShebang;
|
|
248
291
|
|
|
249
|
-
// Separate
|
|
292
|
+
// Separate exports by type - each needs different handling
|
|
250
293
|
const classExports = exports.filter(e => e.type === "class");
|
|
251
|
-
const
|
|
294
|
+
const objectExports = exports.filter(e => e.type === "object");
|
|
295
|
+
const functionExports = exports.filter(e => e.type !== "class" && e.type !== "object");
|
|
252
296
|
|
|
253
297
|
// For CLASSES: Keep original export, instrument in-place (preserves hoisting)
|
|
254
298
|
// This avoids TDZ issues with circular dependencies
|
|
@@ -355,6 +399,15 @@ const __taist_module = "${moduleName}";
|
|
|
355
399
|
})
|
|
356
400
|
.join("\n");
|
|
357
401
|
|
|
402
|
+
// Add in-place instrumentation for OBJECT LITERALS (wraps nested methods)
|
|
403
|
+
// __taist_instrumentObject recursively wraps function properties
|
|
404
|
+
const objectInstrumentations = objectExports
|
|
405
|
+
.map((exp) => {
|
|
406
|
+
const nameExpr = `(__taist_module === "${exp.name}" ? "${exp.name}" : __taist_module + ".${exp.name}")`;
|
|
407
|
+
return `__taist_instrumentObject(${exp.name}, ${nameExpr});`;
|
|
408
|
+
})
|
|
409
|
+
.join("\n");
|
|
410
|
+
|
|
358
411
|
transformed += `\n\n// --- TAIST INSTRUMENTATION ---\n`;
|
|
359
412
|
|
|
360
413
|
if (functionReexports) {
|
|
@@ -365,6 +418,10 @@ const __taist_module = "${moduleName}";
|
|
|
365
418
|
transformed += `// In-place class instrumentation (preserves hoisting)\n${classInstrumentations}\n`;
|
|
366
419
|
}
|
|
367
420
|
|
|
421
|
+
if (objectInstrumentations) {
|
|
422
|
+
transformed += `// In-place object literal instrumentation (wraps nested methods)\n${objectInstrumentations}\n`;
|
|
423
|
+
}
|
|
424
|
+
|
|
368
425
|
// Add default exports at the end (after the wrapped versions are defined)
|
|
369
426
|
for (const name of defaultExports) {
|
|
370
427
|
transformed += `export default ${name};\n`;
|