ngx-locatorjs 0.3.0 ā 0.5.0
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/README.ko.md +50 -61
- package/README.md +46 -57
- package/dist/browser/index.d.ts +1 -0
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +24 -5
- package/dist/node/cmp-scan.js +60 -19
- package/dist/node/config-setup.js +344 -67
- package/dist/node/file-opener.js +193 -42
- package/package.json +5 -2
|
@@ -3,12 +3,27 @@ import fs from 'fs';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import readline from 'readline';
|
|
5
5
|
import { spawn } from 'child_process';
|
|
6
|
+
import crypto from 'crypto';
|
|
6
7
|
import { fileURLToPath } from 'url';
|
|
7
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
9
|
const __dirname = path.dirname(__filename);
|
|
9
10
|
const root = process.cwd();
|
|
10
11
|
const CONFIG_FILENAME = 'ngx-locatorjs.config.json';
|
|
11
|
-
const PROXY_FILENAME = 'ngx-locatorjs.proxy.
|
|
12
|
+
const PROXY_FILENAME = 'ngx-locatorjs.proxy.cjs';
|
|
13
|
+
const ANSI_ENABLED = Boolean(process.stdout.isTTY);
|
|
14
|
+
const IGNORED_SCAN_DIR_NAMES = new Set([
|
|
15
|
+
'node_modules',
|
|
16
|
+
'dist',
|
|
17
|
+
'coverage',
|
|
18
|
+
'.angular',
|
|
19
|
+
'.git',
|
|
20
|
+
'.nx',
|
|
21
|
+
'.turbo',
|
|
22
|
+
'.cache',
|
|
23
|
+
'build',
|
|
24
|
+
'out',
|
|
25
|
+
'tmp',
|
|
26
|
+
]);
|
|
12
27
|
const configPath = path.resolve(root, CONFIG_FILENAME);
|
|
13
28
|
const proxyConfigPath = resolveProxyConfigPath();
|
|
14
29
|
console.log('š LocatorJs (Open-in-Editor) Configuration Setup\n');
|
|
@@ -33,36 +48,22 @@ else {
|
|
|
33
48
|
async function startSetup() {
|
|
34
49
|
try {
|
|
35
50
|
logDefaults();
|
|
51
|
+
const authToken = generateAuthToken();
|
|
36
52
|
const config = {
|
|
37
53
|
port: 4123,
|
|
54
|
+
host: '127.0.0.1',
|
|
38
55
|
workspaceRoot: '.',
|
|
39
56
|
editor: await selectEditor(),
|
|
40
57
|
fallbackEditor: 'code',
|
|
58
|
+
authToken,
|
|
41
59
|
scan: await promptScanSettings(),
|
|
42
60
|
};
|
|
43
61
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
44
|
-
|
|
45
|
-
'/__open-in-editor': {
|
|
46
|
-
target: `http://localhost:${config.port}`,
|
|
47
|
-
secure: false,
|
|
48
|
-
changeOrigin: true,
|
|
49
|
-
},
|
|
50
|
-
'/__open-in-editor-search': {
|
|
51
|
-
target: `http://localhost:${config.port}`,
|
|
52
|
-
secure: false,
|
|
53
|
-
changeOrigin: true,
|
|
54
|
-
},
|
|
55
|
-
'/__cmp-map': {
|
|
56
|
-
target: `http://localhost:${config.port}`,
|
|
57
|
-
secure: false,
|
|
58
|
-
changeOrigin: true,
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
const mergedProxyConfig = mergeProxyConfig(proxyConfigPath, proxyConfig);
|
|
62
|
-
fs.writeFileSync(proxyConfigPath, JSON.stringify(mergedProxyConfig, null, 2));
|
|
62
|
+
fs.writeFileSync(proxyConfigPath, buildDynamicProxyModule());
|
|
63
63
|
console.log('\nā
Configuration saved successfully!');
|
|
64
64
|
console.log(`š Config: ${path.relative(root, configPath)}`);
|
|
65
|
-
console.log(`š Proxy: ${path.relative(root, proxyConfigPath)} (
|
|
65
|
+
console.log(`š Proxy: ${path.relative(root, proxyConfigPath)} (dynamic from config port)`);
|
|
66
|
+
console.log('š Request auth token generated and wired into proxy headers');
|
|
66
67
|
ensureGitignoreEntries(['.open-in-editor/']);
|
|
67
68
|
console.log('\nš Running component scan...');
|
|
68
69
|
const scanScript = path.resolve(__dirname, 'cmp-scan.js');
|
|
@@ -108,45 +109,65 @@ function printNextSteps(proxyPath) {
|
|
|
108
109
|
console.log(' npx locatorjs-open-in-editor');
|
|
109
110
|
console.log(` (run your Angular dev server with --proxy-config ${path.relative(root, proxyPath)})`);
|
|
110
111
|
}
|
|
111
|
-
function
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return
|
|
112
|
+
function buildDynamicProxyModule() {
|
|
113
|
+
return `'use strict';
|
|
114
|
+
|
|
115
|
+
const fs = require('fs');
|
|
116
|
+
const path = require('path');
|
|
117
|
+
|
|
118
|
+
function loadLocatorConfig() {
|
|
119
|
+
const configPath = path.resolve(process.cwd(), '${CONFIG_FILENAME}');
|
|
120
|
+
try {
|
|
121
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
122
|
+
} catch {
|
|
123
|
+
return {};
|
|
124
|
+
}
|
|
123
125
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
126
|
+
|
|
127
|
+
const cfg = loadLocatorConfig();
|
|
128
|
+
const host = typeof cfg.host === 'string' && cfg.host.trim() ? cfg.host.trim() : '127.0.0.1';
|
|
129
|
+
const port = Number(process.env.OPEN_IN_EDITOR_PORT || cfg.port || 4123);
|
|
130
|
+
const authToken = typeof cfg.authToken === 'string' ? cfg.authToken : '';
|
|
131
|
+
const headers = authToken ? { 'x-locatorjs-token': authToken } : {};
|
|
132
|
+
const target = \`http://\${host}:\${port}\`;
|
|
133
|
+
|
|
134
|
+
module.exports = {
|
|
135
|
+
'/__open-in-editor': {
|
|
136
|
+
target,
|
|
137
|
+
secure: false,
|
|
138
|
+
changeOrigin: true,
|
|
139
|
+
headers,
|
|
140
|
+
},
|
|
141
|
+
'/__open-in-editor-search': {
|
|
142
|
+
target,
|
|
143
|
+
secure: false,
|
|
144
|
+
changeOrigin: true,
|
|
145
|
+
headers,
|
|
146
|
+
},
|
|
147
|
+
'/__cmp-map': {
|
|
148
|
+
target,
|
|
149
|
+
secure: false,
|
|
150
|
+
changeOrigin: true,
|
|
151
|
+
headers,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
`;
|
|
137
155
|
}
|
|
138
156
|
function resolveProxyConfigPath() {
|
|
139
157
|
const angularProxyPath = findProxyConfigFromAngularJson();
|
|
140
158
|
if (angularProxyPath) {
|
|
141
|
-
|
|
142
|
-
|
|
159
|
+
const ext = path.extname(angularProxyPath).toLowerCase();
|
|
160
|
+
if (ext === '.js' || ext === '.cjs') {
|
|
161
|
+
return angularProxyPath;
|
|
162
|
+
}
|
|
163
|
+
if (ext === '.json') {
|
|
164
|
+
console.log(`ā ļø angular.json points to JSON proxy (${path.basename(angularProxyPath)}).`);
|
|
165
|
+
console.log(` Creating dynamic proxy module ${PROXY_FILENAME} instead. Update angular.json proxyConfig to use it for single-source port management.`);
|
|
143
166
|
return path.resolve(root, PROXY_FILENAME);
|
|
144
167
|
}
|
|
145
|
-
|
|
168
|
+
console.log(`ā ļø Unsupported proxy extension (${path.basename(angularProxyPath)}). Creating ${PROXY_FILENAME} instead.`);
|
|
169
|
+
return path.resolve(root, PROXY_FILENAME);
|
|
146
170
|
}
|
|
147
|
-
const defaultProxy = path.resolve(root, 'proxy.conf.json');
|
|
148
|
-
if (fs.existsSync(defaultProxy))
|
|
149
|
-
return defaultProxy;
|
|
150
171
|
return path.resolve(root, PROXY_FILENAME);
|
|
151
172
|
}
|
|
152
173
|
function findProxyConfigFromAngularJson() {
|
|
@@ -198,8 +219,12 @@ function ensureGitignoreEntries(entries) {
|
|
|
198
219
|
function logDefaults() {
|
|
199
220
|
console.log('āļø Defaults applied:');
|
|
200
221
|
console.log(' ā Port: 4123');
|
|
222
|
+
console.log(' ā Host: 127.0.0.1');
|
|
201
223
|
console.log(' ā Workspace root: .');
|
|
202
224
|
}
|
|
225
|
+
function generateAuthToken() {
|
|
226
|
+
return crypto.randomBytes(24).toString('hex');
|
|
227
|
+
}
|
|
203
228
|
function selectEditor() {
|
|
204
229
|
const availableEditors = [
|
|
205
230
|
{ name: 'Cursor', value: 'cursor' },
|
|
@@ -276,28 +301,280 @@ function selectEditor() {
|
|
|
276
301
|
process.stdin.on('data', handleKeypress);
|
|
277
302
|
});
|
|
278
303
|
}
|
|
279
|
-
function promptScanSettings() {
|
|
304
|
+
async function promptScanSettings() {
|
|
280
305
|
const defaultInclude = [
|
|
281
|
-
'src/**/*.{ts,tsx}',
|
|
282
|
-
'projects/**/*.{ts,tsx}',
|
|
283
|
-
'apps/**/*.{ts,tsx}',
|
|
284
|
-
'libs/**/*.{ts,tsx}',
|
|
306
|
+
'src/**/*.{ts,tsx,js,jsx}',
|
|
307
|
+
'projects/**/*.{ts,tsx,js,jsx}',
|
|
308
|
+
'apps/**/*.{ts,tsx,js,jsx}',
|
|
309
|
+
'libs/**/*.{ts,tsx,js,jsx}',
|
|
285
310
|
];
|
|
286
311
|
const defaultExclude = [
|
|
287
312
|
'**/node_modules/**',
|
|
288
313
|
'**/dist/**',
|
|
289
314
|
'**/.angular/**',
|
|
290
315
|
'**/coverage/**',
|
|
291
|
-
'**/*.spec.ts',
|
|
292
|
-
'**/*.test.ts',
|
|
293
|
-
'**/*.e2e.ts',
|
|
316
|
+
'**/*.spec.{ts,tsx,js,jsx}',
|
|
317
|
+
'**/*.test.{ts,tsx,js,jsx}',
|
|
318
|
+
'**/*.e2e.{ts,tsx,js,jsx}',
|
|
294
319
|
];
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
320
|
+
const includeGlobs = detectIncludeGlobs(defaultInclude);
|
|
321
|
+
const excludeGlobs = detectExcludeGlobs(defaultExclude);
|
|
322
|
+
printScanSettingsSummary(includeGlobs, excludeGlobs);
|
|
323
|
+
return {
|
|
324
|
+
includeGlobs: includeGlobs.values,
|
|
325
|
+
excludeGlobs: excludeGlobs.values,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
function detectIncludeGlobs(defaultInclude) {
|
|
329
|
+
const sources = [];
|
|
330
|
+
let detectedBaseDirs = detectIncludeBaseDirsFromAngularJson();
|
|
331
|
+
if (detectedBaseDirs.size > 0) {
|
|
332
|
+
sources.push('angular.json');
|
|
333
|
+
const supplemental = detectSupplementalWorkspaceBaseDirs(detectedBaseDirs);
|
|
334
|
+
if (supplemental.size > 0) {
|
|
335
|
+
supplemental.forEach((dir) => detectedBaseDirs.add(dir));
|
|
336
|
+
sources.push('workspace directories (not declared in angular.json)');
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
detectedBaseDirs = detectIncludeBaseDirsFromConventionalLayout();
|
|
341
|
+
if (detectedBaseDirs.size > 0) {
|
|
342
|
+
sources.push('existing directories');
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (detectedBaseDirs.size > 0) {
|
|
346
|
+
return {
|
|
347
|
+
values: toGlobPatterns(detectedBaseDirs),
|
|
348
|
+
autoDetected: true,
|
|
349
|
+
sources,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
values: defaultInclude,
|
|
354
|
+
autoDetected: false,
|
|
355
|
+
sources: [],
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function detectExcludeGlobs(defaultExclude) {
|
|
359
|
+
const extraExcludeCandidates = [
|
|
360
|
+
{ pattern: '**/.nx/**', reason: 'nx.json', when: fileExists('nx.json') },
|
|
361
|
+
{ pattern: '**/.turbo/**', reason: 'turbo.json', when: fileExists('turbo.json') },
|
|
362
|
+
{ pattern: '**/.cache/**', reason: '.cache directory', when: directoryExists('.cache') },
|
|
363
|
+
{ pattern: '**/build/**', reason: 'build directory', when: directoryExists('build') },
|
|
364
|
+
{ pattern: '**/out/**', reason: 'out directory', when: directoryExists('out') },
|
|
365
|
+
{ pattern: '**/tmp/**', reason: 'tmp directory', when: directoryExists('tmp') },
|
|
366
|
+
];
|
|
367
|
+
const values = [...defaultExclude];
|
|
368
|
+
const reasons = [];
|
|
369
|
+
for (const candidate of extraExcludeCandidates) {
|
|
370
|
+
if (!candidate.when)
|
|
371
|
+
continue;
|
|
372
|
+
if (!values.includes(candidate.pattern)) {
|
|
373
|
+
values.push(candidate.pattern);
|
|
374
|
+
reasons.push(`${candidate.pattern} (${candidate.reason})`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return {
|
|
378
|
+
values,
|
|
379
|
+
autoDetected: reasons.length > 0,
|
|
380
|
+
sources: reasons,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function detectIncludeBaseDirsFromAngularJson() {
|
|
384
|
+
const angularJsonPath = path.resolve(root, 'angular.json');
|
|
385
|
+
if (!fs.existsSync(angularJsonPath))
|
|
386
|
+
return new Set();
|
|
387
|
+
try {
|
|
388
|
+
const angularJson = JSON.parse(fs.readFileSync(angularJsonPath, 'utf8'));
|
|
389
|
+
const projects = angularJson?.projects ?? {};
|
|
390
|
+
const detectedBaseDirs = new Set();
|
|
391
|
+
for (const project of Object.values(projects)) {
|
|
392
|
+
const sourceRoot = normalizeRelativePath(project.sourceRoot);
|
|
393
|
+
const projectRoot = normalizeRelativePath(project.root);
|
|
394
|
+
if (sourceRoot && directoryExists(sourceRoot)) {
|
|
395
|
+
detectedBaseDirs.add(sourceRoot);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
if (!projectRoot)
|
|
399
|
+
continue;
|
|
400
|
+
const srcUnderProjectRoot = normalizeRelativePath(path.posix.join(projectRoot, 'src'));
|
|
401
|
+
if (srcUnderProjectRoot && directoryExists(srcUnderProjectRoot)) {
|
|
402
|
+
detectedBaseDirs.add(srcUnderProjectRoot);
|
|
403
|
+
}
|
|
404
|
+
else if (directoryExists(projectRoot)) {
|
|
405
|
+
detectedBaseDirs.add(projectRoot);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return detectedBaseDirs;
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
return new Set();
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function detectIncludeBaseDirsFromConventionalLayout() {
|
|
415
|
+
const conventionalDirs = ['src', 'projects', 'apps', 'libs'];
|
|
416
|
+
const detectedBaseDirs = new Set();
|
|
417
|
+
for (const dir of conventionalDirs) {
|
|
418
|
+
if (directoryExists(dir)) {
|
|
419
|
+
detectedBaseDirs.add(dir);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return detectedBaseDirs;
|
|
423
|
+
}
|
|
424
|
+
function detectSupplementalWorkspaceBaseDirs(knownBaseDirs) {
|
|
425
|
+
const roots = ['projects', 'apps', 'libs'];
|
|
426
|
+
const supplemental = new Set();
|
|
427
|
+
for (const rootDir of roots) {
|
|
428
|
+
const rootPath = path.resolve(root, rootDir);
|
|
429
|
+
if (!directoryExists(rootDir))
|
|
430
|
+
continue;
|
|
431
|
+
let entries;
|
|
432
|
+
try {
|
|
433
|
+
entries = fs.readdirSync(rootPath, { withFileTypes: true });
|
|
434
|
+
}
|
|
435
|
+
catch {
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
for (const entry of entries) {
|
|
439
|
+
if (!entry.isDirectory())
|
|
440
|
+
continue;
|
|
441
|
+
const candidate = normalizeRelativePath(path.posix.join(rootDir, entry.name));
|
|
442
|
+
if (!candidate)
|
|
443
|
+
continue;
|
|
444
|
+
if (isCoveredByKnownBaseDirs(candidate, knownBaseDirs))
|
|
445
|
+
continue;
|
|
446
|
+
if (!directoryContainsScanCandidateFiles(candidate))
|
|
447
|
+
continue;
|
|
448
|
+
supplemental.add(candidate);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return supplemental;
|
|
452
|
+
}
|
|
453
|
+
function isCoveredByKnownBaseDirs(candidate, knownBaseDirs) {
|
|
454
|
+
for (const known of knownBaseDirs) {
|
|
455
|
+
if (candidate === known)
|
|
456
|
+
return true;
|
|
457
|
+
if (candidate.startsWith(`${known}/`))
|
|
458
|
+
return true;
|
|
459
|
+
if (known.startsWith(`${candidate}/`))
|
|
460
|
+
return true;
|
|
461
|
+
}
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
function directoryContainsScanCandidateFiles(relPath, maxDepth = 6) {
|
|
465
|
+
const absolute = path.resolve(root, relPath);
|
|
466
|
+
return directoryContainsScanCandidateFilesRecursive(absolute, 0, maxDepth);
|
|
467
|
+
}
|
|
468
|
+
function directoryContainsScanCandidateFilesRecursive(absolutePath, currentDepth, maxDepth) {
|
|
469
|
+
let entries;
|
|
470
|
+
try {
|
|
471
|
+
entries = fs.readdirSync(absolutePath, { withFileTypes: true });
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
for (const entry of entries) {
|
|
477
|
+
if (entry.isFile() && /\.(ts|tsx|js|jsx)$/.test(entry.name)) {
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
if (!entry.isDirectory())
|
|
481
|
+
continue;
|
|
482
|
+
if (currentDepth >= maxDepth)
|
|
483
|
+
continue;
|
|
484
|
+
if (IGNORED_SCAN_DIR_NAMES.has(entry.name))
|
|
485
|
+
continue;
|
|
486
|
+
const childPath = path.join(absolutePath, entry.name);
|
|
487
|
+
if (directoryContainsScanCandidateFilesRecursive(childPath, currentDepth + 1, maxDepth)) {
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
function toGlobPatterns(baseDirs) {
|
|
494
|
+
if (baseDirs.size === 0)
|
|
495
|
+
return [];
|
|
496
|
+
const preferredOrder = ['src', 'projects', 'apps', 'libs'];
|
|
497
|
+
return Array.from(baseDirs)
|
|
498
|
+
.sort((a, b) => {
|
|
499
|
+
const aIdx = preferredOrder.indexOf(a);
|
|
500
|
+
const bIdx = preferredOrder.indexOf(b);
|
|
501
|
+
const aRank = aIdx === -1 ? Number.MAX_SAFE_INTEGER : aIdx;
|
|
502
|
+
const bRank = bIdx === -1 ? Number.MAX_SAFE_INTEGER : bIdx;
|
|
503
|
+
if (aRank !== bRank)
|
|
504
|
+
return aRank - bRank;
|
|
505
|
+
return a.localeCompare(b);
|
|
506
|
+
})
|
|
507
|
+
.map((baseDir) => `${baseDir}/**/*.{ts,tsx,js,jsx}`);
|
|
508
|
+
}
|
|
509
|
+
function normalizeRelativePath(value) {
|
|
510
|
+
if (!value)
|
|
511
|
+
return null;
|
|
512
|
+
const normalized = value
|
|
513
|
+
.trim()
|
|
514
|
+
.replace(/\\/g, '/')
|
|
515
|
+
.replace(/^\.\/+/, '')
|
|
516
|
+
.replace(/^\/+/, '')
|
|
517
|
+
.replace(/\/+$/, '');
|
|
518
|
+
return normalized.length > 0 ? normalized : null;
|
|
519
|
+
}
|
|
520
|
+
function directoryExists(relPath) {
|
|
521
|
+
try {
|
|
522
|
+
const stat = fs.statSync(path.resolve(root, relPath));
|
|
523
|
+
return stat.isDirectory();
|
|
524
|
+
}
|
|
525
|
+
catch {
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
function fileExists(relPath) {
|
|
530
|
+
try {
|
|
531
|
+
const stat = fs.statSync(path.resolve(root, relPath));
|
|
532
|
+
return stat.isFile();
|
|
533
|
+
}
|
|
534
|
+
catch {
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
function colorize(text, code) {
|
|
539
|
+
if (!ANSI_ENABLED)
|
|
540
|
+
return text;
|
|
541
|
+
return `\x1b[${code}m${text}\x1b[0m`;
|
|
542
|
+
}
|
|
543
|
+
function bold(text) {
|
|
544
|
+
return colorize(text, '1');
|
|
545
|
+
}
|
|
546
|
+
function cyan(text) {
|
|
547
|
+
return colorize(text, '36');
|
|
548
|
+
}
|
|
549
|
+
function green(text) {
|
|
550
|
+
return colorize(text, '32');
|
|
551
|
+
}
|
|
552
|
+
function yellow(text) {
|
|
553
|
+
return colorize(text, '33');
|
|
554
|
+
}
|
|
555
|
+
function dim(text) {
|
|
556
|
+
return colorize(text, '2');
|
|
557
|
+
}
|
|
558
|
+
function printScanSettingsSummary(includeGlobs, excludeGlobs) {
|
|
559
|
+
const mode = includeGlobs.autoDetected ? 'AUTO-DETECTED' : 'DEFAULTS';
|
|
560
|
+
console.log(`\n${bold(cyan('Scan Settings Applied'))} ${dim(`[${mode}]`)}`);
|
|
561
|
+
console.log(dim('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
562
|
+
console.log(`${bold(green('Include globs'))}:`);
|
|
563
|
+
includeGlobs.values.forEach((value) => {
|
|
564
|
+
console.log(` ${green('ā¢')} ${value}`);
|
|
565
|
+
});
|
|
566
|
+
console.log(`\n${bold(yellow('Exclude globs'))}:`);
|
|
567
|
+
excludeGlobs.values.forEach((value) => {
|
|
568
|
+
console.log(` ${yellow('ā¢')} ${value}`);
|
|
302
569
|
});
|
|
570
|
+
if (includeGlobs.autoDetected) {
|
|
571
|
+
console.log(`\n${bold('Detected from')}: ${includeGlobs.sources.join(', ')}`);
|
|
572
|
+
}
|
|
573
|
+
if (excludeGlobs.autoDetected) {
|
|
574
|
+
console.log(`${bold('Auto-added excludes')}:`);
|
|
575
|
+
excludeGlobs.sources.forEach((value) => {
|
|
576
|
+
console.log(` ${yellow('ā¢')} ${value}`);
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
console.log(`\n${dim(`Edit scan globs anytime in ${CONFIG_FILENAME}`)}`);
|
|
303
580
|
}
|