@tng-sh/js 0.2.1 → 0.2.4
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/bin/tng.js +143 -110
- package/binaries/go-ui-darwin-amd64 +0 -0
- package/binaries/go-ui-darwin-arm64 +0 -0
- package/binaries/go-ui-linux-amd64 +0 -0
- package/binaries/go-ui-linux-arm64 +0 -0
- package/index.d.ts +1 -1
- package/lib/generateTestsUi.js +8 -9
- package/package.json +1 -1
- package/tng_sh_js.linux-arm64-gnu.node +0 -0
- package/tng_sh_js.linux-x64-gnu.node +0 -0
package/bin/tng.js
CHANGED
|
@@ -28,7 +28,7 @@ process.on('uncaughtException', (err) => {
|
|
|
28
28
|
program
|
|
29
29
|
.name('tng')
|
|
30
30
|
.description('TNG - Advanced Code Audit, Test Generation, Visualization, Clone Detection, and Dead Code Analysis for JavaScript/TypeScript')
|
|
31
|
-
.version('0.2.
|
|
31
|
+
.version('0.2.4');
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Copy text to system clipboard
|
|
@@ -256,139 +256,172 @@ program
|
|
|
256
256
|
.option('--all', 'Run dead code detection across the entire repo (respects .gitignore)')
|
|
257
257
|
.option('--xray', 'Generate X-Ray visualization (Mermaid flowchart)')
|
|
258
258
|
.option('--impact', 'Run impact analysis (blast radius check) on a method')
|
|
259
|
+
.option('--callsites', 'Find in-repo call sites for a method')
|
|
260
|
+
.option('--class <name>', 'Class/module name to disambiguate methods when multiple classes define the same method in a file')
|
|
259
261
|
.option('--json', 'Output results as JSON events (machine-readable)')
|
|
260
262
|
|
|
261
263
|
.action(async (options) => {
|
|
262
|
-
if (options.
|
|
263
|
-
if (options.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
try {
|
|
268
|
-
const traceJson = getSymbolicTrace(
|
|
269
|
-
path.resolve(options.file),
|
|
270
|
-
options.method,
|
|
271
|
-
null
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
if (options.json) {
|
|
275
|
-
console.log(traceJson);
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Create temp file for the trace
|
|
280
|
-
const tmpDir = require('os').tmpdir();
|
|
281
|
-
const tmpFile = path.join(tmpDir, `trace-${Date.now()}.json`);
|
|
282
|
-
fs.writeFileSync(tmpFile, traceJson);
|
|
264
|
+
if (options.callsites) {
|
|
265
|
+
if (!options.method) {
|
|
266
|
+
console.log(chalk.red('Error: --method is required for call site analysis.'));
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
283
269
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
270
|
+
try {
|
|
271
|
+
const { findCallSites } = require('../index');
|
|
272
|
+
const projectRoot = process.cwd();
|
|
273
|
+
const resultJson = findCallSites(projectRoot, options.method);
|
|
287
274
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
275
|
+
if (options.json) {
|
|
276
|
+
console.log(resultJson);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
292
279
|
|
|
293
|
-
|
|
294
|
-
|
|
280
|
+
const parsed = JSON.parse(resultJson);
|
|
281
|
+
const sites = Array.isArray(parsed) ? parsed : (parsed.call_sites || parsed.sites || parsed);
|
|
282
|
+
if (!sites || sites.length === 0) {
|
|
283
|
+
console.log(chalk.green('\nNo call sites found in this project. You might be safe!'));
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
295
286
|
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
|
|
287
|
+
console.log(chalk.blue(`\nFound ${sites.length} call sites:\n`));
|
|
288
|
+
sites.slice(0, 200).forEach((site) => {
|
|
289
|
+
const file = site.file || 'unknown';
|
|
290
|
+
const line = site.line || 0;
|
|
291
|
+
const content = site.content || '';
|
|
292
|
+
console.log(`${chalk.yellow(file)}:${chalk.cyan(line)} ${content}`);
|
|
293
|
+
});
|
|
294
|
+
if (sites.length > 200) {
|
|
295
|
+
console.log(chalk.dim(`\n... ${sites.length - 200} more`));
|
|
299
296
|
}
|
|
300
297
|
return;
|
|
298
|
+
} catch (e) {
|
|
299
|
+
console.log(chalk.red(`Error: ${e.message}`));
|
|
300
|
+
process.exit(1);
|
|
301
301
|
}
|
|
302
|
+
}
|
|
303
|
+
// 1. Symbolic Trace
|
|
304
|
+
if (options.method && options.file && options.trace) {
|
|
305
|
+
const { getSymbolicTrace } = require('../index');
|
|
306
|
+
const GoUISession = require('../lib/goUiSession');
|
|
302
307
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
308
|
+
try {
|
|
309
|
+
const traceJson = getSymbolicTrace(
|
|
310
|
+
path.resolve(options.file),
|
|
311
|
+
options.method,
|
|
312
|
+
options.class || null
|
|
313
|
+
);
|
|
306
314
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
315
|
+
if (options.json) {
|
|
316
|
+
console.log(traceJson);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Create temp file for the trace
|
|
321
|
+
const tmpDir = require('os').tmpdir();
|
|
322
|
+
const tmpFile = path.join(tmpDir, `trace-${Date.now()}.json`);
|
|
323
|
+
fs.writeFileSync(tmpFile, traceJson);
|
|
324
|
+
|
|
325
|
+
// Launch Go UI
|
|
326
|
+
const session = new GoUISession();
|
|
327
|
+
const binaryPath = session._binaryPath;
|
|
328
|
+
|
|
329
|
+
const { spawnSync } = require('child_process');
|
|
330
|
+
spawnSync(binaryPath, ['js-trace-results', '--file', tmpFile], {
|
|
331
|
+
stdio: 'inherit'
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// Cleanup
|
|
335
|
+
try { fs.unlinkSync(tmpFile); } catch (e) { }
|
|
336
|
+
|
|
337
|
+
} catch (e) {
|
|
338
|
+
console.log(chalk.red(`Trace failed: ${e.message}`));
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
324
343
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
344
|
+
// 2. X-Ray
|
|
345
|
+
if (options.method && options.file && options.xray) {
|
|
346
|
+
const config = loadConfig();
|
|
347
|
+
const { generateTest: nativeGenerateTest } = require('../index');
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
const resultJson = nativeGenerateTest(
|
|
351
|
+
path.resolve(options.file),
|
|
352
|
+
options.method,
|
|
353
|
+
options.class || null,
|
|
354
|
+
'visualize',
|
|
355
|
+
JSON.stringify(config),
|
|
356
|
+
(msg, percent) => {
|
|
357
|
+
if (options.json) return;
|
|
358
|
+
if (percent % 20 === 0) console.log(chalk.dim(`[${percent}%] ${msg}`));
|
|
336
359
|
}
|
|
360
|
+
);
|
|
337
361
|
|
|
338
|
-
|
|
339
|
-
|
|
362
|
+
if (options.json) {
|
|
363
|
+
console.log(resultJson);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
340
366
|
|
|
341
|
-
|
|
367
|
+
const GoUISession = require('../lib/goUiSession');
|
|
368
|
+
const session = new GoUISession();
|
|
369
|
+
let mermaidCode = "";
|
|
370
|
+
let explanation = "";
|
|
371
|
+
try {
|
|
372
|
+
const parsed = JSON.parse(resultJson);
|
|
373
|
+
mermaidCode = parsed.mermaid_code || resultJson;
|
|
374
|
+
explanation = parsed.explanation || "";
|
|
342
375
|
} catch (e) {
|
|
343
|
-
|
|
344
|
-
process.exit(1);
|
|
376
|
+
mermaidCode = resultJson;
|
|
345
377
|
}
|
|
346
|
-
|
|
378
|
+
|
|
379
|
+
copyToClipboard(mermaidCode);
|
|
380
|
+
await session.showXrayResults(options.method, '', mermaidCode, explanation);
|
|
381
|
+
} catch (e) {
|
|
382
|
+
console.log(chalk.red(`X-Ray failed: ${e.message}`));
|
|
383
|
+
process.exit(1);
|
|
347
384
|
}
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
348
387
|
|
|
388
|
+
// 3. Impact Analysis
|
|
389
|
+
if (options.method && options.file && options.impact) {
|
|
390
|
+
await runImpact(options.file, options.method, options.json, options.class || null);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
349
393
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
394
|
+
// 4. Clone Detection
|
|
395
|
+
if (options.file && options.clones) {
|
|
396
|
+
await runClones(options.file, options.level || 'all', options.json);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
354
399
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
if (options.clones) {
|
|
362
|
-
await runClones(options.file, options.level || 'all', options.json);
|
|
363
|
-
} else if (options.deadcode) {
|
|
364
|
-
if (options.all) {
|
|
365
|
-
await runDeadCodeRepo(options.json);
|
|
366
|
-
} else {
|
|
367
|
-
await runDeadCode(options.file, options.json);
|
|
368
|
-
}
|
|
400
|
+
// 5. Dead Code Analysis
|
|
401
|
+
if (options.deadcode) {
|
|
402
|
+
if (options.all) {
|
|
403
|
+
await runDeadCodeRepo(options.json);
|
|
404
|
+
} else if (options.file) {
|
|
405
|
+
await runDeadCode(options.file, options.json);
|
|
369
406
|
} else {
|
|
370
|
-
console.log(chalk.
|
|
371
|
-
|
|
372
|
-
if (options.xray) {
|
|
373
|
-
// ... (existing xray logic) ...
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
if (options.impact) {
|
|
378
|
-
await runImpact(options.file, options.method, options.json);
|
|
379
|
-
return;
|
|
407
|
+
console.log(chalk.red('Error: --file <path> or --all is required for deadcode analysis.'));
|
|
408
|
+
process.exit(1);
|
|
380
409
|
}
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
381
412
|
|
|
413
|
+
// 6. Test Generation / Audit (Method + File required)
|
|
414
|
+
if (options.method && options.file) {
|
|
382
415
|
if (!options.type && !options.audit) {
|
|
383
416
|
console.log(chalk.red('Error: --type <type> is required.'));
|
|
384
417
|
process.exit(1);
|
|
385
418
|
}
|
|
386
|
-
generateTest(options.file, options.method, options.type, options.audit, options.json);
|
|
387
|
-
|
|
388
|
-
await runDeadCodeRepo(options.json);
|
|
389
|
-
} else if (options.file && !options.method) {
|
|
390
|
-
launchInteractive();
|
|
419
|
+
generateTest(options.file, options.method, options.type, options.audit, options.json, options.class || null);
|
|
420
|
+
return;
|
|
391
421
|
}
|
|
422
|
+
|
|
423
|
+
// 7. Interactive Fallback
|
|
424
|
+
launchInteractive();
|
|
392
425
|
});
|
|
393
426
|
|
|
394
427
|
/**
|
|
@@ -674,7 +707,7 @@ program
|
|
|
674
707
|
/**
|
|
675
708
|
* Logic to generate test or run audit (delegates to native binary)
|
|
676
709
|
*/
|
|
677
|
-
async function generateTest(filePath, methodName, testType, auditMode = false, jsonMode = false) {
|
|
710
|
+
async function generateTest(filePath, methodName, testType, auditMode = false, jsonMode = false, className = null) {
|
|
678
711
|
const config = loadConfig();
|
|
679
712
|
|
|
680
713
|
// Initialize JSON session if requested
|
|
@@ -744,7 +777,7 @@ async function generateTest(filePath, methodName, testType, auditMode = false, j
|
|
|
744
777
|
resultJson = runAudit(
|
|
745
778
|
absolutePath,
|
|
746
779
|
methodName,
|
|
747
|
-
|
|
780
|
+
className,
|
|
748
781
|
testType || null,
|
|
749
782
|
JSON.stringify(config),
|
|
750
783
|
callback
|
|
@@ -753,7 +786,7 @@ async function generateTest(filePath, methodName, testType, auditMode = false, j
|
|
|
753
786
|
resultJson = nativeGenerateTest(
|
|
754
787
|
absolutePath,
|
|
755
788
|
methodName,
|
|
756
|
-
|
|
789
|
+
className,
|
|
757
790
|
testType || null,
|
|
758
791
|
JSON.stringify(config),
|
|
759
792
|
callback
|
|
@@ -824,7 +857,7 @@ program.on('--help', () => {
|
|
|
824
857
|
/**
|
|
825
858
|
* Logic to run impact analysis
|
|
826
859
|
*/
|
|
827
|
-
async function runImpact(filePath, methodName, jsonMode = false) {
|
|
860
|
+
async function runImpact(filePath, methodName, jsonMode = false, className = null) {
|
|
828
861
|
const { analyzeImpact } = require('../index');
|
|
829
862
|
const absolutePath = path.resolve(filePath);
|
|
830
863
|
const projectRoot = process.cwd();
|
|
@@ -836,7 +869,7 @@ async function runImpact(filePath, methodName, jsonMode = false) {
|
|
|
836
869
|
|
|
837
870
|
if (jsonMode) {
|
|
838
871
|
try {
|
|
839
|
-
const resultJson = analyzeImpact(projectRoot, absolutePath, methodName);
|
|
872
|
+
const resultJson = analyzeImpact(projectRoot, absolutePath, methodName, className);
|
|
840
873
|
console.log(resultJson);
|
|
841
874
|
} catch (e) {
|
|
842
875
|
console.error(JSON.stringify({ error: e.message }));
|
|
@@ -848,7 +881,7 @@ async function runImpact(filePath, methodName, jsonMode = false) {
|
|
|
848
881
|
console.log(chalk.blue(`💥 Analyzing impact for ${methodName} in ${filePath}...`));
|
|
849
882
|
|
|
850
883
|
try {
|
|
851
|
-
const resultJson = analyzeImpact(projectRoot, absolutePath, methodName);
|
|
884
|
+
const resultJson = analyzeImpact(projectRoot, absolutePath, methodName, className);
|
|
852
885
|
const result = JSON.parse(resultJson);
|
|
853
886
|
|
|
854
887
|
if (result.status === 'error') {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/index.d.ts
CHANGED
|
@@ -69,4 +69,4 @@ export declare function listDeadcodeFiles(projectRoot: string): string
|
|
|
69
69
|
* Analyzes the impact of changes in a specific method compared to git HEAD.
|
|
70
70
|
* Returns a JSON string containing breaking changes and impacted files.
|
|
71
71
|
*/
|
|
72
|
-
export declare function analyzeImpact(projectRoot: string, filePath: string, methodName: string): string
|
|
72
|
+
export declare function analyzeImpact(projectRoot: string, filePath: string, methodName: string, className?: string | undefined | null): string
|
package/lib/generateTestsUi.js
CHANGED
|
@@ -6,7 +6,6 @@ const GoUISession = require('./goUiSession');
|
|
|
6
6
|
const { getFileOutline, generateTest, ping, getUserStats } = require('../index');
|
|
7
7
|
const { loadConfig } = require('./config');
|
|
8
8
|
const { saveTestFile } = require('./saveFile');
|
|
9
|
-
const { applyFix } = require('./fixApplier');
|
|
10
9
|
|
|
11
10
|
class GenerateTestsUI {
|
|
12
11
|
constructor(cliMode = false) {
|
|
@@ -127,7 +126,7 @@ class GenerateTestsUI {
|
|
|
127
126
|
|
|
128
127
|
let title = 'Select JavaScript File';
|
|
129
128
|
if (isAudit) title = 'Select JavaScript File to Audit';
|
|
130
|
-
else if (isImpact) title = 'Select JavaScript File
|
|
129
|
+
else if (isImpact) title = 'Select JavaScript File for Regression Check';
|
|
131
130
|
else if (isXray) title = 'Select File for X-Ray';
|
|
132
131
|
else if (isTrace) title = 'Select File for Symbolic Trace';
|
|
133
132
|
|
|
@@ -160,14 +159,14 @@ class GenerateTestsUI {
|
|
|
160
159
|
|
|
161
160
|
const fileName = path.basename(filePath);
|
|
162
161
|
const items = methods.map(m => ({
|
|
163
|
-
name: m.class_name ? `${m.class_name}
|
|
162
|
+
name: m.class_name ? `${m.class_name}#${m.name}` : m.name,
|
|
164
163
|
path: `Function in ${fileName}`,
|
|
165
164
|
methodData: m
|
|
166
165
|
}));
|
|
167
166
|
|
|
168
167
|
let title = 'Select Method';
|
|
169
168
|
if (isAudit) title = `Select Method to Audit for ${fileName}`;
|
|
170
|
-
else if (isImpact) title = `Select Method
|
|
169
|
+
else if (isImpact) title = `Select Method for Regression Check for ${fileName}`;
|
|
171
170
|
else if (isXray) title = `Select Method to X-Ray for ${fileName}`;
|
|
172
171
|
else if (isTrace) title = `Select Method to Trace for ${fileName}`;
|
|
173
172
|
|
|
@@ -179,7 +178,7 @@ class GenerateTestsUI {
|
|
|
179
178
|
|
|
180
179
|
if (selectedMethod) {
|
|
181
180
|
if (isTrace) {
|
|
182
|
-
await this._launchTrace(filePath, selectedMethod.name);
|
|
181
|
+
await this._launchTrace(filePath, selectedMethod.name, selectedMethod.class_name || null);
|
|
183
182
|
return this._showFileSelection(isAudit, isXray, isTrace, isImpact);
|
|
184
183
|
}
|
|
185
184
|
|
|
@@ -259,7 +258,7 @@ class GenerateTestsUI {
|
|
|
259
258
|
// Artificial delay for UX
|
|
260
259
|
await new Promise(r => setTimeout(r, 300));
|
|
261
260
|
|
|
262
|
-
const json = analyzeImpact(projectRoot, filePath, method.name);
|
|
261
|
+
const json = analyzeImpact(projectRoot, filePath, method.name, method.class_name || null);
|
|
263
262
|
progress.update('Comparing logic...', { percent: 80 });
|
|
264
263
|
|
|
265
264
|
return json;
|
|
@@ -272,7 +271,7 @@ class GenerateTestsUI {
|
|
|
272
271
|
}
|
|
273
272
|
|
|
274
273
|
await this.goUiSession.showImpactResults(impactResult);
|
|
275
|
-
return { message: '
|
|
274
|
+
return { message: 'Regression check complete' };
|
|
276
275
|
}
|
|
277
276
|
|
|
278
277
|
async _handleAuditFlow(filePath, method, testType) {
|
|
@@ -600,7 +599,7 @@ class GenerateTestsUI {
|
|
|
600
599
|
}
|
|
601
600
|
}
|
|
602
601
|
|
|
603
|
-
async _launchTrace(filePath, methodName) {
|
|
602
|
+
async _launchTrace(filePath, methodName, className) {
|
|
604
603
|
const { getSymbolicTrace } = require('../index');
|
|
605
604
|
const fs = require('fs');
|
|
606
605
|
const path = require('path');
|
|
@@ -610,7 +609,7 @@ class GenerateTestsUI {
|
|
|
610
609
|
// 1. Generate Trace (with Spinner)
|
|
611
610
|
const result = this.goUiSession.showSpinner(`Tracing ${methodName}...`, () => {
|
|
612
611
|
try {
|
|
613
|
-
const traceJson = getSymbolicTrace(filePath, methodName, null);
|
|
612
|
+
const traceJson = getSymbolicTrace(filePath, methodName, className || null);
|
|
614
613
|
const tmpDir = require('os').tmpdir();
|
|
615
614
|
const f = path.join(tmpDir, `trace-${Date.now()}.json`);
|
|
616
615
|
fs.writeFileSync(f, traceJson);
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|