@tng-sh/js 0.2.4 → 0.2.6
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 +61 -9
- 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 +2 -0
- package/index.js +2 -1
- package/lib/generateTestsUi.js +50 -36
- package/lib/ignore.js +56 -0
- 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/.idea/js-pie.iml +0 -11
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -6
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.
|
|
32
|
+
.version('0.2.6');
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/index.d.ts
CHANGED
|
@@ -56,6 +56,8 @@ export declare function applyEditsAtomic(operationsJson: string): string
|
|
|
56
56
|
export declare function generateTest(filePath: string, methodName: string, className: string | undefined | null, testType: string | undefined | null, configJson: string, callback: (...args: any[]) => any): string
|
|
57
57
|
/** Analyzes a method and generates a symbolic execution trace. */
|
|
58
58
|
export declare function getSymbolicTrace(filePath: string, methodName: string, className?: string | undefined | null): string
|
|
59
|
+
/** Analyzes a method and generates a symbolic execution trace v2. */
|
|
60
|
+
export declare function getSymbolicTraceV2(filePath: string, methodName: string, className?: string | undefined | null, projectRoot?: string | undefined | null): string
|
|
59
61
|
/**
|
|
60
62
|
* Analyzes a file for code clones (duplicates).
|
|
61
63
|
* Returns a JSON string containing the matches found.
|
package/index.js
CHANGED
|
@@ -310,7 +310,7 @@ if (!nativeBinding) {
|
|
|
310
310
|
throw new Error(`Failed to load native binding`)
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
const { getFileOutline, getProjectMetadata, findCallSites, ping, submitJob, getUserStats, runAudit, applyEdit, applyEditsAtomic, generateTest, getSymbolicTrace, analyzeClones, detectDeadCode, listDeadcodeFiles, analyzeImpact } = nativeBinding
|
|
313
|
+
const { getFileOutline, getProjectMetadata, findCallSites, ping, submitJob, getUserStats, runAudit, applyEdit, applyEditsAtomic, generateTest, getSymbolicTrace, getSymbolicTraceV2, analyzeClones, detectDeadCode, listDeadcodeFiles, analyzeImpact } = nativeBinding
|
|
314
314
|
|
|
315
315
|
module.exports.getFileOutline = getFileOutline
|
|
316
316
|
module.exports.getProjectMetadata = getProjectMetadata
|
|
@@ -323,6 +323,7 @@ module.exports.applyEdit = applyEdit
|
|
|
323
323
|
module.exports.applyEditsAtomic = applyEditsAtomic
|
|
324
324
|
module.exports.generateTest = generateTest
|
|
325
325
|
module.exports.getSymbolicTrace = getSymbolicTrace
|
|
326
|
+
module.exports.getSymbolicTraceV2 = getSymbolicTraceV2
|
|
326
327
|
module.exports.analyzeClones = analyzeClones
|
|
327
328
|
module.exports.detectDeadCode = detectDeadCode
|
|
328
329
|
module.exports.listDeadcodeFiles = listDeadcodeFiles
|
package/lib/generateTestsUi.js
CHANGED
|
@@ -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 {
|
|
@@ -17,7 +18,6 @@ class GenerateTestsUI {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
async show() {
|
|
20
|
-
// Check for API key before showing menu
|
|
21
21
|
const config = loadConfig();
|
|
22
22
|
if (!config.API_KEY) {
|
|
23
23
|
this._showConfigMissing();
|
|
@@ -94,20 +94,26 @@ class GenerateTestsUI {
|
|
|
94
94
|
|
|
95
95
|
async _pingApi() {
|
|
96
96
|
const config = loadConfig();
|
|
97
|
-
if (!config.API_KEY) {
|
|
97
|
+
if (!config || !config.API_KEY) {
|
|
98
98
|
console.log(chalk.red('\nNo API key configured. Run: tng init\n'));
|
|
99
|
-
return;
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!config.API_URL) {
|
|
103
|
+
console.log(chalk.red('\nNo API URL configured in tng.config.js.\n'));
|
|
104
|
+
return false;
|
|
100
105
|
}
|
|
101
106
|
|
|
102
|
-
this.goUiSession.showSpinner('Pinging TNG API...', () => {
|
|
107
|
+
const result = await this.goUiSession.showSpinner('Pinging TNG API...', async () => {
|
|
103
108
|
try {
|
|
104
|
-
const
|
|
105
|
-
return { success: true, message: `API Response: ${
|
|
109
|
+
const res = ping(config.API_URL, config.API_KEY);
|
|
110
|
+
return { success: true, message: `API Response: ${res}` };
|
|
106
111
|
} catch (e) {
|
|
107
112
|
return { success: false, message: e.message };
|
|
108
113
|
}
|
|
109
114
|
});
|
|
110
|
-
|
|
115
|
+
|
|
116
|
+
return result || false;
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
async _showFileSelection(isAudit = false, isXray = false, isTrace = false, isImpact = false) {
|
|
@@ -130,15 +136,19 @@ class GenerateTestsUI {
|
|
|
130
136
|
else if (isXray) title = 'Select File for X-Ray';
|
|
131
137
|
else if (isTrace) title = 'Select File for Symbolic Trace';
|
|
132
138
|
|
|
133
|
-
|
|
139
|
+
while (true) {
|
|
140
|
+
const selectedName = this.goUiSession.showListView(title, items);
|
|
134
141
|
|
|
135
|
-
|
|
136
|
-
|
|
142
|
+
if (selectedName === 'back') return 'back';
|
|
143
|
+
if (!selectedName || selectedName === 'exit') return 'exit';
|
|
137
144
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
145
|
+
const selectedFile = path.resolve(cwd, selectedName);
|
|
146
|
+
const result = await this._showMethodsForFile(selectedFile, isAudit, isXray, isTrace, isImpact);
|
|
147
|
+
|
|
148
|
+
if (result === 'main_menu') return 'main_menu';
|
|
149
|
+
if (result === 'exit') return 'exit';
|
|
150
|
+
// If result is 'back', we loop again
|
|
151
|
+
}
|
|
142
152
|
}
|
|
143
153
|
|
|
144
154
|
async _showMethodsForFile(filePath, isAudit = false, isXray = false, isTrace = false, isImpact = false) {
|
|
@@ -148,13 +158,13 @@ class GenerateTestsUI {
|
|
|
148
158
|
outline = JSON.parse(result);
|
|
149
159
|
} catch (e) {
|
|
150
160
|
console.error(chalk.red(`\nError parsing file: ${e.message}\n`));
|
|
151
|
-
return
|
|
161
|
+
return 'back';
|
|
152
162
|
}
|
|
153
163
|
|
|
154
164
|
const methods = outline.methods || [];
|
|
155
165
|
if (methods.length === 0) {
|
|
156
166
|
this.goUiSession.showNoItems('methods');
|
|
157
|
-
return
|
|
167
|
+
return 'back';
|
|
158
168
|
}
|
|
159
169
|
|
|
160
170
|
const fileName = path.basename(filePath);
|
|
@@ -172,36 +182,36 @@ class GenerateTestsUI {
|
|
|
172
182
|
|
|
173
183
|
const selectedDisplay = this.goUiSession.showListView(title, items);
|
|
174
184
|
|
|
175
|
-
if (selectedDisplay === 'back' || !selectedDisplay) return
|
|
185
|
+
if (selectedDisplay === 'back' || !selectedDisplay) return 'back';
|
|
176
186
|
|
|
177
187
|
const selectedMethod = items.find(i => i.name === selectedDisplay)?.methodData;
|
|
178
188
|
|
|
179
189
|
if (selectedMethod) {
|
|
180
190
|
if (isTrace) {
|
|
181
191
|
await this._launchTrace(filePath, selectedMethod.name, selectedMethod.class_name || null);
|
|
182
|
-
return
|
|
192
|
+
return 'back';
|
|
183
193
|
}
|
|
184
194
|
|
|
185
195
|
if (isXray) {
|
|
186
196
|
const choice = await this._generateTestsForMethod(filePath, selectedMethod, 'visualize', false, true);
|
|
187
197
|
if (choice === 'main_menu') return 'main_menu';
|
|
188
|
-
return
|
|
198
|
+
return 'back';
|
|
189
199
|
}
|
|
190
200
|
|
|
191
201
|
if (isImpact) {
|
|
192
202
|
const choice = await this._generateTestsForMethod(filePath, selectedMethod, null, false, false, true);
|
|
193
203
|
if (choice === 'main_menu') return 'main_menu';
|
|
194
|
-
return
|
|
204
|
+
return 'back';
|
|
195
205
|
}
|
|
196
206
|
|
|
197
207
|
if (isAudit) {
|
|
198
208
|
const choice = await this._generateTestsForMethod(filePath, selectedMethod, null, true);
|
|
199
209
|
if (choice === 'main_menu') return 'main_menu';
|
|
200
|
-
return
|
|
210
|
+
return 'back';
|
|
201
211
|
}
|
|
202
212
|
|
|
203
213
|
const testType = this.goUiSession.showJsTestMenu();
|
|
204
|
-
if (testType === 'back') return
|
|
214
|
+
if (testType === 'back') return 'back';
|
|
205
215
|
|
|
206
216
|
const finalType = testType === 'auto' ? null : testType;
|
|
207
217
|
const choice = await this._generateTestsForMethod(filePath, selectedMethod, finalType, false);
|
|
@@ -210,7 +220,7 @@ class GenerateTestsUI {
|
|
|
210
220
|
this._showPostGenerationMenu(choice);
|
|
211
221
|
}
|
|
212
222
|
}
|
|
213
|
-
return
|
|
223
|
+
return 'back';
|
|
214
224
|
}
|
|
215
225
|
|
|
216
226
|
async _generateTestsForMethod(filePath, method, testType, isAudit = false, isXray = false, isImpact = false) {
|
|
@@ -249,7 +259,7 @@ class GenerateTestsUI {
|
|
|
249
259
|
const { analyzeImpact } = require('../index');
|
|
250
260
|
const projectRoot = process.cwd();
|
|
251
261
|
|
|
252
|
-
console.log(chalk.blue(`\
|
|
262
|
+
console.log(chalk.blue(`\nAnalyzing impact for ${method.name}...`));
|
|
253
263
|
|
|
254
264
|
let impactResult;
|
|
255
265
|
try {
|
|
@@ -280,7 +290,7 @@ class GenerateTestsUI {
|
|
|
280
290
|
const fileName = path.basename(filePath);
|
|
281
291
|
const displayName = method.class_name ? `${method.class_name}#${method.name}` : `${fileName}#${method.name}`;
|
|
282
292
|
|
|
283
|
-
console.log(chalk.blue(`\
|
|
293
|
+
console.log(chalk.blue(`\nAuditing ${displayName}...`));
|
|
284
294
|
|
|
285
295
|
let auditResult;
|
|
286
296
|
try {
|
|
@@ -571,7 +581,7 @@ class GenerateTestsUI {
|
|
|
571
581
|
}
|
|
572
582
|
}
|
|
573
583
|
} catch (e) {
|
|
574
|
-
console.log(chalk.yellow(`\n
|
|
584
|
+
console.log(chalk.yellow(`\n Failed to open editor: ${e.message}`));
|
|
575
585
|
}
|
|
576
586
|
}
|
|
577
587
|
|
|
@@ -588,11 +598,13 @@ class GenerateTestsUI {
|
|
|
588
598
|
];
|
|
589
599
|
|
|
590
600
|
try {
|
|
591
|
-
|
|
601
|
+
const files = await glob(patterns, {
|
|
592
602
|
ignore,
|
|
593
603
|
absolute: true,
|
|
594
604
|
onlyFiles: true
|
|
595
605
|
});
|
|
606
|
+
const config = loadConfig();
|
|
607
|
+
return filterIgnoredPaths(files, config, process.cwd());
|
|
596
608
|
} catch (e) {
|
|
597
609
|
console.error(chalk.red(`\nError searching for files: ${e.message}\n`));
|
|
598
610
|
return [];
|
|
@@ -648,19 +660,21 @@ class GenerateTestsUI {
|
|
|
648
660
|
path: path.dirname(file)
|
|
649
661
|
}));
|
|
650
662
|
|
|
651
|
-
|
|
663
|
+
while (true) {
|
|
664
|
+
const selectedName = this.goUiSession.showListView('Select File for Duplicate Detection', items);
|
|
652
665
|
|
|
653
|
-
|
|
654
|
-
|
|
666
|
+
if (selectedName === 'back') return 'back';
|
|
667
|
+
if (!selectedName || selectedName === 'exit') return 'exit';
|
|
655
668
|
|
|
656
|
-
|
|
669
|
+
const selectedFile = path.resolve(cwd, selectedName);
|
|
657
670
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
671
|
+
// Show level selection
|
|
672
|
+
const level = this.goUiSession.showCloneMenu();
|
|
673
|
+
if (level === 'back') continue;
|
|
661
674
|
|
|
662
|
-
|
|
663
|
-
|
|
675
|
+
await this._runClones(selectedFile, level);
|
|
676
|
+
return 'main_menu';
|
|
677
|
+
}
|
|
664
678
|
}
|
|
665
679
|
|
|
666
680
|
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
|
Binary file
|
|
Binary file
|
package/.idea/js-pie.iml
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<module type="RUBY_MODULE" version="4">
|
|
3
|
-
<component name="ModuleRunConfigurationManager">
|
|
4
|
-
<shared />
|
|
5
|
-
</component>
|
|
6
|
-
<component name="NewModuleRootManager">
|
|
7
|
-
<content url="file://$MODULE_DIR$" />
|
|
8
|
-
<orderEntry type="inheritedJdk" />
|
|
9
|
-
<orderEntry type="sourceFolder" forTests="false" />
|
|
10
|
-
</component>
|
|
11
|
-
</module>
|
package/.idea/modules.xml
DELETED