@tng-sh/js 0.2.4 → 0.2.5

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 CHANGED
@@ -5,6 +5,7 @@ const chalk = require('chalk');
5
5
  const fs = require('fs');
6
6
  const path = require('path');
7
7
  const { loadConfig } = require('../lib/config');
8
+ const { isIgnoredPath, filterIgnoredPaths, ignoredMessage } = require('../lib/ignore');
8
9
  const { saveTestFile } = require('../lib/saveFile');
9
10
  const { ping, getUserStats } = require('../index');
10
11
  const { applyFix } = require('../lib/fixApplier');
@@ -28,7 +29,7 @@ process.on('uncaughtException', (err) => {
28
29
  program
29
30
  .name('tng')
30
31
  .description('TNG - Advanced Code Audit, Test Generation, Visualization, Clone Detection, and Dead Code Analysis for JavaScript/TypeScript')
31
- .version('0.2.4');
32
+ .version('0.2.5');
32
33
 
33
34
  /**
34
35
  * Copy text to system clipboard
@@ -92,7 +93,11 @@ module.exports = {
92
93
  TEST_FRAMEWORK: "jest",
93
94
 
94
95
  // Test Directory
95
- TEST_DIRECTORY: "tests"
96
+ TEST_DIRECTORY: "tests",
97
+
98
+ // Ignore paths (relative to project root)
99
+ IGNORE_FILES: [],
100
+ IGNORE_FOLDERS: []
96
101
  };
97
102
  `;
98
103
  fs.writeFileSync(configPath, content);
@@ -189,11 +194,16 @@ program
189
194
  process.exit(1);
190
195
  }
191
196
 
197
+ const config = loadConfig();
198
+ const resolvedPath = path.resolve(options.file);
199
+ if (isIgnoredPath(resolvedPath, config)) {
200
+ console.log(chalk.red(ignoredMessage()));
201
+ process.exit(1);
202
+ }
203
+
192
204
  console.log(chalk.blue(`šŸ” Generating X-Ray for ${options.method} in ${options.file}...`));
193
205
 
194
206
  try {
195
- const config = loadConfig();
196
-
197
207
  // Re-use logic similar to _handleXrayFlow but tailored for CLI non-interactive output
198
208
  const { generateTest } = require('../index');
199
209
 
@@ -304,10 +314,16 @@ program
304
314
  if (options.method && options.file && options.trace) {
305
315
  const { getSymbolicTrace } = require('../index');
306
316
  const GoUISession = require('../lib/goUiSession');
317
+ const config = loadConfig();
318
+ const resolvedPath = path.resolve(options.file);
319
+ if (isIgnoredPath(resolvedPath, config)) {
320
+ console.log(chalk.red(ignoredMessage()));
321
+ process.exit(1);
322
+ }
307
323
 
308
324
  try {
309
325
  const traceJson = getSymbolicTrace(
310
- path.resolve(options.file),
326
+ resolvedPath,
311
327
  options.method,
312
328
  options.class || null
313
329
  );
@@ -344,11 +360,16 @@ program
344
360
  // 2. X-Ray
345
361
  if (options.method && options.file && options.xray) {
346
362
  const config = loadConfig();
363
+ const resolvedPath = path.resolve(options.file);
364
+ if (isIgnoredPath(resolvedPath, config)) {
365
+ console.log(chalk.red(ignoredMessage()));
366
+ process.exit(1);
367
+ }
347
368
  const { generateTest: nativeGenerateTest } = require('../index');
348
369
 
349
370
  try {
350
371
  const resultJson = nativeGenerateTest(
351
- path.resolve(options.file),
372
+ resolvedPath,
352
373
  options.method,
353
374
  options.class || null,
354
375
  'visualize',
@@ -429,6 +450,7 @@ program
429
450
  */
430
451
  async function runClones(filePath, level, jsonMode = false) {
431
452
  const { analyzeClones } = require('../index');
453
+ const config = loadConfig();
432
454
  const absolutePath = path.resolve(filePath);
433
455
  const projectRoot = process.cwd();
434
456
 
@@ -436,6 +458,10 @@ async function runClones(filePath, level, jsonMode = false) {
436
458
  console.log(chalk.red(`File not found: ${filePath}`));
437
459
  process.exit(1);
438
460
  }
461
+ if (isIgnoredPath(absolutePath, config)) {
462
+ console.log(chalk.red(ignoredMessage()));
463
+ process.exit(1);
464
+ }
439
465
 
440
466
  const GoUISession = require('../lib/goUiSession');
441
467
  const session = new GoUISession();
@@ -485,12 +511,17 @@ async function runClones(filePath, level, jsonMode = false) {
485
511
  */
486
512
  async function runDeadCode(filePath, jsonMode = false) {
487
513
  const { detectDeadCode } = require('../index');
514
+ const config = loadConfig();
488
515
  const absolutePath = path.resolve(filePath);
489
516
 
490
517
  if (!fs.existsSync(absolutePath)) {
491
518
  console.log(chalk.red(`File not found: ${filePath}`));
492
519
  process.exit(1);
493
520
  }
521
+ if (isIgnoredPath(absolutePath, config)) {
522
+ console.log(chalk.red(ignoredMessage()));
523
+ process.exit(1);
524
+ }
494
525
 
495
526
  const GoUISession = require('../lib/goUiSession');
496
527
  const session = new GoUISession();
@@ -531,13 +562,14 @@ async function runDeadCode(filePath, jsonMode = false) {
531
562
  }
532
563
  }
533
564
 
534
- function listDeadCodeFiles(projectRoot) {
565
+ function listDeadCodeFiles(projectRoot, config) {
535
566
  const { listDeadcodeFiles } = require('../index');
536
567
  try {
537
568
  const filesJson = listDeadcodeFiles(projectRoot);
538
569
  const files = JSON.parse(filesJson);
539
570
  if (!Array.isArray(files)) return [];
540
- return files.filter((f) => !f.endsWith('.d.ts'));
571
+ const withoutTypes = files.filter((f) => !f.endsWith('.d.ts'));
572
+ return filterIgnoredPaths(withoutTypes, config, projectRoot);
541
573
  } catch (e) {
542
574
  return [];
543
575
  }
@@ -546,7 +578,8 @@ function listDeadCodeFiles(projectRoot) {
546
578
  async function runDeadCodeRepo(jsonMode = false) {
547
579
  const { detectDeadCode } = require('../index');
548
580
  const projectRoot = process.cwd();
549
- const files = listDeadCodeFiles(projectRoot);
581
+ const config = loadConfig();
582
+ const files = listDeadCodeFiles(projectRoot, config);
550
583
 
551
584
  if (files.length === 0) {
552
585
  console.log(chalk.yellow('No files found for dead code analysis.'));
@@ -739,6 +772,16 @@ async function generateTest(filePath, methodName, testType, auditMode = false, j
739
772
  }
740
773
  process.exit(1);
741
774
  }
775
+ if (isIgnoredPath(absolutePath, config)) {
776
+ const message = ignoredMessage();
777
+ if (jsonSession) {
778
+ jsonSession.displayError(message);
779
+ jsonSession.stop();
780
+ } else {
781
+ console.log(chalk.red(message));
782
+ }
783
+ process.exit(1);
784
+ }
742
785
 
743
786
  const { runAudit, generateTest: nativeGenerateTest } = require('../index');
744
787
 
@@ -847,6 +890,10 @@ program.on('--help', () => {
847
890
  console.log(' 3: Fuzzy (Fuzzy structural, catches patterns with small variations)');
848
891
  console.log(' all: Runs all detection levels (default)');
849
892
  console.log('');
893
+ console.log('Ignore Paths (from tng.config.js):');
894
+ console.log(' IGNORE_FILES: [ "relative/path/file.js", ... ]');
895
+ console.log(' IGNORE_FOLDERS: [ "relative/path/folder", ... ]');
896
+ console.log('');
850
897
  console.log('X-Ray Visualization:');
851
898
  console.log(' tng src/components/MyComponent.js render --xray');
852
899
  console.log(' tng --file=api/handler.js --method=post --xray --json');
@@ -859,6 +906,7 @@ program.on('--help', () => {
859
906
  */
860
907
  async function runImpact(filePath, methodName, jsonMode = false, className = null) {
861
908
  const { analyzeImpact } = require('../index');
909
+ const config = loadConfig();
862
910
  const absolutePath = path.resolve(filePath);
863
911
  const projectRoot = process.cwd();
864
912
 
@@ -866,6 +914,10 @@ async function runImpact(filePath, methodName, jsonMode = false, className = nul
866
914
  console.log(chalk.red(`File not found: ${filePath}`));
867
915
  process.exit(1);
868
916
  }
917
+ if (isIgnoredPath(absolutePath, config)) {
918
+ console.log(chalk.red(ignoredMessage()));
919
+ process.exit(1);
920
+ }
869
921
 
870
922
  if (jsonMode) {
871
923
  try {
@@ -5,6 +5,7 @@ const chalk = require('chalk');
5
5
  const GoUISession = require('./goUiSession');
6
6
  const { getFileOutline, generateTest, ping, getUserStats } = require('../index');
7
7
  const { loadConfig } = require('./config');
8
+ const { filterIgnoredPaths } = require('./ignore');
8
9
  const { saveTestFile } = require('./saveFile');
9
10
 
10
11
  class GenerateTestsUI {
@@ -130,15 +131,19 @@ class GenerateTestsUI {
130
131
  else if (isXray) title = 'Select File for X-Ray';
131
132
  else if (isTrace) title = 'Select File for Symbolic Trace';
132
133
 
133
- const selectedName = this.goUiSession.showListView(title, items);
134
+ while (true) {
135
+ const selectedName = this.goUiSession.showListView(title, items);
134
136
 
135
- if (selectedName === 'back') return 'back';
136
- if (!selectedName || selectedName === 'exit') return 'exit';
137
+ if (selectedName === 'back') return 'back';
138
+ if (!selectedName || selectedName === 'exit') return 'exit';
137
139
 
138
- const selectedFile = path.resolve(cwd, selectedName);
139
- const result = await this._showMethodsForFile(selectedFile, isAudit, isXray, isTrace, isImpact);
140
- if (result === 'main_menu') return 'main_menu';
141
- return result;
140
+ const selectedFile = path.resolve(cwd, selectedName);
141
+ const result = await this._showMethodsForFile(selectedFile, isAudit, isXray, isTrace, isImpact);
142
+
143
+ if (result === 'main_menu') return 'main_menu';
144
+ if (result === 'exit') return 'exit';
145
+ // If result is 'back', we loop again
146
+ }
142
147
  }
143
148
 
144
149
  async _showMethodsForFile(filePath, isAudit = false, isXray = false, isTrace = false, isImpact = false) {
@@ -148,13 +153,13 @@ class GenerateTestsUI {
148
153
  outline = JSON.parse(result);
149
154
  } catch (e) {
150
155
  console.error(chalk.red(`\nError parsing file: ${e.message}\n`));
151
- return this._showFileSelection(isAudit, isXray, isTrace, isImpact);
156
+ return 'back';
152
157
  }
153
158
 
154
159
  const methods = outline.methods || [];
155
160
  if (methods.length === 0) {
156
161
  this.goUiSession.showNoItems('methods');
157
- return this._showFileSelection(isAudit, isXray, isTrace, isImpact);
162
+ return 'back';
158
163
  }
159
164
 
160
165
  const fileName = path.basename(filePath);
@@ -172,36 +177,36 @@ class GenerateTestsUI {
172
177
 
173
178
  const selectedDisplay = this.goUiSession.showListView(title, items);
174
179
 
175
- if (selectedDisplay === 'back' || !selectedDisplay) return this._showFileSelection(isAudit, isXray, isTrace, isImpact);
180
+ if (selectedDisplay === 'back' || !selectedDisplay) return 'back';
176
181
 
177
182
  const selectedMethod = items.find(i => i.name === selectedDisplay)?.methodData;
178
183
 
179
184
  if (selectedMethod) {
180
185
  if (isTrace) {
181
186
  await this._launchTrace(filePath, selectedMethod.name, selectedMethod.class_name || null);
182
- return this._showFileSelection(isAudit, isXray, isTrace, isImpact);
187
+ return 'back';
183
188
  }
184
189
 
185
190
  if (isXray) {
186
191
  const choice = await this._generateTestsForMethod(filePath, selectedMethod, 'visualize', false, true);
187
192
  if (choice === 'main_menu') return 'main_menu';
188
- return this._showFileSelection(isAudit, isXray, isTrace, isImpact);
193
+ return 'back';
189
194
  }
190
195
 
191
196
  if (isImpact) {
192
197
  const choice = await this._generateTestsForMethod(filePath, selectedMethod, null, false, false, true);
193
198
  if (choice === 'main_menu') return 'main_menu';
194
- return this._showFileSelection(isAudit, isXray, isTrace, isImpact);
199
+ return 'back';
195
200
  }
196
201
 
197
202
  if (isAudit) {
198
203
  const choice = await this._generateTestsForMethod(filePath, selectedMethod, null, true);
199
204
  if (choice === 'main_menu') return 'main_menu';
200
- return this._showFileSelection(isAudit, isXray, isTrace, isImpact);
205
+ return 'back';
201
206
  }
202
207
 
203
208
  const testType = this.goUiSession.showJsTestMenu();
204
- if (testType === 'back') return this._showFileSelection(isAudit, isXray, isTrace, isImpact);
209
+ if (testType === 'back') return 'back';
205
210
 
206
211
  const finalType = testType === 'auto' ? null : testType;
207
212
  const choice = await this._generateTestsForMethod(filePath, selectedMethod, finalType, false);
@@ -210,7 +215,7 @@ class GenerateTestsUI {
210
215
  this._showPostGenerationMenu(choice);
211
216
  }
212
217
  }
213
- return this._showFileSelection(isAudit, isXray, isTrace, isImpact);
218
+ return 'back';
214
219
  }
215
220
 
216
221
  async _generateTestsForMethod(filePath, method, testType, isAudit = false, isXray = false, isImpact = false) {
@@ -249,7 +254,7 @@ class GenerateTestsUI {
249
254
  const { analyzeImpact } = require('../index');
250
255
  const projectRoot = process.cwd();
251
256
 
252
- console.log(chalk.blue(`\nšŸ” Analyzing impact for ${method.name}...`));
257
+ console.log(chalk.blue(`\nAnalyzing impact for ${method.name}...`));
253
258
 
254
259
  let impactResult;
255
260
  try {
@@ -280,7 +285,7 @@ class GenerateTestsUI {
280
285
  const fileName = path.basename(filePath);
281
286
  const displayName = method.class_name ? `${method.class_name}#${method.name}` : `${fileName}#${method.name}`;
282
287
 
283
- console.log(chalk.blue(`\nšŸ” Auditing ${displayName}...`));
288
+ console.log(chalk.blue(`\nAuditing ${displayName}...`));
284
289
 
285
290
  let auditResult;
286
291
  try {
@@ -571,7 +576,7 @@ class GenerateTestsUI {
571
576
  }
572
577
  }
573
578
  } catch (e) {
574
- console.log(chalk.yellow(`\nāš ļø Failed to open editor: ${e.message}`));
579
+ console.log(chalk.yellow(`\n Failed to open editor: ${e.message}`));
575
580
  }
576
581
  }
577
582
 
@@ -588,11 +593,13 @@ class GenerateTestsUI {
588
593
  ];
589
594
 
590
595
  try {
591
- return await glob(patterns, {
596
+ const files = await glob(patterns, {
592
597
  ignore,
593
598
  absolute: true,
594
599
  onlyFiles: true
595
600
  });
601
+ const config = loadConfig();
602
+ return filterIgnoredPaths(files, config, process.cwd());
596
603
  } catch (e) {
597
604
  console.error(chalk.red(`\nError searching for files: ${e.message}\n`));
598
605
  return [];
@@ -648,19 +655,21 @@ class GenerateTestsUI {
648
655
  path: path.dirname(file)
649
656
  }));
650
657
 
651
- const selectedName = this.goUiSession.showListView('Select File for Duplicate Detection', items);
658
+ while (true) {
659
+ const selectedName = this.goUiSession.showListView('Select File for Duplicate Detection', items);
652
660
 
653
- if (selectedName === 'back') return 'back';
654
- if (!selectedName || selectedName === 'exit') return 'exit';
661
+ if (selectedName === 'back') return 'back';
662
+ if (!selectedName || selectedName === 'exit') return 'exit';
655
663
 
656
- const selectedFile = path.resolve(cwd, selectedName);
664
+ const selectedFile = path.resolve(cwd, selectedName);
657
665
 
658
- // Show level selection
659
- const level = this.goUiSession.showCloneMenu();
660
- if (level === 'back') return this._showClonesSelection();
666
+ // Show level selection
667
+ const level = this.goUiSession.showCloneMenu();
668
+ if (level === 'back') continue;
661
669
 
662
- await this._runClones(selectedFile, level);
663
- return 'main_menu';
670
+ await this._runClones(selectedFile, level);
671
+ return 'main_menu';
672
+ }
664
673
  }
665
674
 
666
675
  async _runClones(filePath, level) {
package/lib/ignore.js ADDED
@@ -0,0 +1,56 @@
1
+ const path = require('path');
2
+
3
+ function normalizeEntry(entry) {
4
+ if (typeof entry !== 'string') return null;
5
+ const trimmed = entry.trim();
6
+ if (!trimmed) return null;
7
+ return trimmed.replace(/\\/g, '/');
8
+ }
9
+
10
+ function resolveConfigPath(entry, projectRoot) {
11
+ const normalized = normalizeEntry(entry);
12
+ if (!normalized) return null;
13
+ if (path.isAbsolute(normalized)) {
14
+ return path.resolve(normalized);
15
+ }
16
+ return path.resolve(projectRoot, normalized);
17
+ }
18
+
19
+ function isIgnoredPath(filePath, config = {}, projectRoot = process.cwd()) {
20
+ if (!filePath) return false;
21
+
22
+ const absolutePath = path.resolve(filePath);
23
+ const ignoreFiles = Array.isArray(config.IGNORE_FILES) ? config.IGNORE_FILES : [];
24
+ const ignoreFolders = Array.isArray(config.IGNORE_FOLDERS) ? config.IGNORE_FOLDERS : [];
25
+
26
+ for (const entry of ignoreFiles) {
27
+ const resolved = resolveConfigPath(entry, projectRoot);
28
+ if (resolved && resolved === absolutePath) {
29
+ return true;
30
+ }
31
+ }
32
+
33
+ for (const entry of ignoreFolders) {
34
+ const resolved = resolveConfigPath(entry, projectRoot);
35
+ if (!resolved) continue;
36
+ if (absolutePath === resolved) return true;
37
+ if (absolutePath.startsWith(resolved + path.sep)) return true;
38
+ }
39
+
40
+ return false;
41
+ }
42
+
43
+ function filterIgnoredPaths(paths, config = {}, projectRoot = process.cwd()) {
44
+ if (!Array.isArray(paths) || paths.length === 0) return [];
45
+ return paths.filter((p) => !isIgnoredPath(p, config, projectRoot));
46
+ }
47
+
48
+ function ignoredMessage() {
49
+ return 'Ignored by TNG config (ignore_files/ignore_folders). Remove it from config to proceed.';
50
+ }
51
+
52
+ module.exports = {
53
+ isIgnoredPath,
54
+ filterIgnoredPaths,
55
+ ignoredMessage
56
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tng-sh/js",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "TNG JavaScript CLI",
5
5
  "repository": {
6
6
  "type": "git",
Binary file
Binary file