entkapp 5.4.0 → 5.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.
@@ -111,31 +111,6 @@ export class PluginRegistry {
111
111
  return this.plugins.get(name);
112
112
  }
113
113
 
114
- /**
115
- * Version 5.4.0: Collect entry points from all plugins for a given file.
116
- * @param {string} content - The file content
117
- * @param {string} filePath - The absolute path to the file
118
- * @returns {Array<string>} List of detected entry points
119
- */
120
- detectEntryPointsFromContent(content, filePath) {
121
- const entryPoints = [];
122
- for (const plugin of this.plugins.values()) {
123
- if (typeof plugin.detectEntryPoints === 'function') {
124
- try {
125
- const detected = plugin.detectEntryPoints(content, filePath);
126
- if (Array.isArray(detected)) {
127
- entryPoints.push(...detected);
128
- }
129
- } catch (e) {
130
- if (this.context?.verbose) {
131
- console.warn(`[PluginRegistry] Entry detection failed for plugin "${plugin.name}" on ${filePath}:`, e.message);
132
- }
133
- }
134
- }
135
- }
136
- return entryPoints;
137
- }
138
-
139
114
  async getActivePlugins(baseDir) {
140
115
  const active = [];
141
116
  for (const plugin of this.plugins.values()) {
@@ -146,25 +121,6 @@ export class PluginRegistry {
146
121
  return active;
147
122
  }
148
123
 
149
- /**
150
- * Version 5.3.0: Run content analysis on a node using all active plugins.
151
- * @param {GraphNode} node - The node to analyze
152
- * @param {string} filePath - The absolute path to the file
153
- */
154
- async runPluginAnalysis(node, filePath) {
155
- for (const plugin of this.plugins.values()) {
156
- if (typeof plugin.analyze === 'function') {
157
- try {
158
- await plugin.analyze(node, filePath);
159
- } catch (e) {
160
- if (this.context?.verbose) {
161
- console.warn(`[PluginRegistry] Analysis failed for plugin "${plugin.name}" on ${filePath}:`, e.message);
162
- }
163
- }
164
- }
165
- }
166
- }
167
-
168
124
  /**
169
125
  * Version 5.0.0: Run dependency diagnostics across all registered plugins.
170
126
  * Returns a consolidated list of missing/misplaced dependencies and orphaned configs.
@@ -8,7 +8,6 @@
8
8
  import path from 'path';
9
9
  import fs from 'fs/promises';
10
10
  import { BasePlugin } from '../BasePlugin.js';
11
- import { FrameworkConfigParser } from '../../resolution/FrameworkConfigParser.js';
12
11
 
13
12
 
14
13
  export class GraphQLPlugin extends BasePlugin {
@@ -212,27 +211,6 @@ export class NuxtPlugin extends BasePlugin {
212
211
  }
213
212
  return false;
214
213
  }
215
- detectEntryPoints(content, filePath) {
216
- if (this.getConfigFiles().some(f => filePath.endsWith(f))) {
217
- const parser = new FrameworkConfigParser(this.context);
218
- const { entries } = parser.parse(content, filePath);
219
- return Array.from(entries);
220
- }
221
- return [];
222
- }
223
- async analyze(node, filePath) {
224
- if (this.getConfigFiles().some(f => filePath.endsWith(f))) {
225
- node.isEntry = true;
226
- if (node.rawCode) {
227
- const parser = new FrameworkConfigParser(this.context);
228
- const { aliases, entries } = parser.parse(node.rawCode, filePath);
229
- if (aliases.size > 0) node.frameworkAliases = aliases;
230
- if (entries.size > 0) node.frameworkEntries = entries;
231
- if (node.rawCode.includes('modules:')) node.hasNuxtModules = true;
232
- if (node.rawCode.includes('components:')) node.hasNuxtComponentsConfig = true;
233
- }
234
- }
235
- }
236
214
  }
237
215
 
238
216
  export class RemixPlugin extends BasePlugin {
@@ -255,21 +233,6 @@ export class RemixPlugin extends BasePlugin {
255
233
  }
256
234
  return false;
257
235
  }
258
- detectEntryPoints(content, filePath) {
259
- if (filePath.endsWith('remix.config.js') || filePath.includes('vite.config.')) {
260
- const parser = new FrameworkConfigParser(this.context);
261
- const { entries } = parser.parse(content, filePath);
262
- return Array.from(entries);
263
- }
264
- return [];
265
- }
266
- async analyze(node, filePath) {
267
- if (filePath.endsWith('remix.config.js')) {
268
- node.isEntry = true;
269
- if (node.rawCode?.includes('ignoredRouteFiles:')) node.hasRemixIgnoredRoutes = true;
270
- if (node.rawCode?.includes('serverBuildPath:')) node.hasRemixServerBuildPath = true;
271
- }
272
- }
273
236
  }
274
237
 
275
238
  export class SvelteKitPlugin extends BasePlugin {
@@ -288,29 +251,6 @@ export class SvelteKitPlugin extends BasePlugin {
288
251
  async isActive(baseDir) {
289
252
  try { await fs.access(path.join(baseDir, 'svelte.config.js')); return true; } catch { return false; }
290
253
  }
291
- detectEntryPoints(content, filePath) {
292
- if (filePath.includes('svelte.config.')) {
293
- const parser = new FrameworkConfigParser(this.context);
294
- const { entries } = parser.parse(content, filePath);
295
- return Array.from(entries);
296
- }
297
- return [];
298
- }
299
- async analyze(node, filePath) {
300
- if (filePath.includes('svelte.config.')) {
301
- node.isEntry = true;
302
- if (node.rawCode) {
303
- const parser = new FrameworkConfigParser(this.context);
304
- const { aliases, entries } = parser.parse(node.rawCode, filePath);
305
- if (aliases.size > 0) {
306
- node.frameworkAliases = aliases;
307
- node.hasSvelteAliases = true;
308
- }
309
- if (entries.size > 0) node.frameworkEntries = entries;
310
- if (node.rawCode.includes('adapter:')) node.hasSvelteAdapter = true;
311
- }
312
- }
313
- }
314
254
  }
315
255
 
316
256
  export class AstroPlugin extends BasePlugin {
@@ -325,39 +265,12 @@ export class AstroPlugin extends BasePlugin {
325
265
  }
326
266
  return false;
327
267
  }
328
- detectEntryPoints(content, filePath) {
329
- if (this.getConfigFiles().some(f => filePath.endsWith(f))) {
330
- const parser = new FrameworkConfigParser(this.context);
331
- const { entries } = parser.parse(content, filePath);
332
- return Array.from(entries);
333
- }
334
- return [];
335
- }
336
- async analyze(node, filePath) {
337
- if (this.getConfigFiles().some(f => filePath.endsWith(f))) {
338
- node.isEntry = true;
339
- if (node.rawCode) {
340
- const parser = new FrameworkConfigParser(this.context);
341
- const { aliases, entries } = parser.parse(node.rawCode, filePath);
342
- if (aliases.size > 0) node.frameworkAliases = aliases;
343
- if (entries.size > 0) node.frameworkEntries = entries;
344
- if (node.rawCode.includes('integrations:')) node.hasAstroIntegrations = true;
345
- if (node.rawCode.includes('output:')) node.hasAstroOutputConfig = true;
346
- }
347
- }
348
- }
349
268
  }
350
269
 
351
270
  export class VitepressPlugin extends BasePlugin {
352
271
  get name() { return 'vitepress'; }
353
272
  getConfigFiles() { return ['package.json']; }
354
273
  getRequiredPackages() { return [{ name: 'vitepress', dev: true }]; }
355
-
356
- getRoutePatterns() {
357
- // Protection: Every file inside a .vitepress folder is treated as an entry point
358
- return [/\/\.vitepress\//];
359
- }
360
-
361
274
  async isActive(baseDir) {
362
275
  try {
363
276
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
@@ -369,62 +282,6 @@ export class VitepressPlugin extends BasePlugin {
369
282
  return hasDep;
370
283
  } catch { return false; }
371
284
  }
372
-
373
- async runDependencyDiagnostics(baseDir) {
374
- const diagnostics = await super.runDependencyDiagnostics(baseDir);
375
-
376
- let pkg = null;
377
- try {
378
- const raw = await fs.readFile(path.join(baseDir, 'package.json'), 'utf8');
379
- pkg = JSON.parse(raw);
380
- } catch { return diagnostics; }
381
-
382
- const scripts = pkg.scripts || {};
383
- const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
384
- const hasVitepressDep = 'vitepress' in allDeps;
385
-
386
- // Check if any script uses vitepress
387
- const hasVitepressCommand = Object.values(scripts).some(s => s.includes('vitepress'));
388
-
389
- // Logic for .vitepress folder existence
390
- let hasVitepressFolder = false;
391
- const possibleDirs = ['.vitepress', 'docs/.vitepress', 'docs/docs/.vitepress'];
392
- for (const d of possibleDirs) {
393
- try {
394
- await fs.access(path.join(baseDir, d));
395
- hasVitepressFolder = true;
396
- break;
397
- } catch {}
398
- }
399
-
400
- // Scenario 1: build cmd/dependency exists but no folder -> unused dependency
401
- if ((hasVitepressCommand || hasVitepressDep) && !hasVitepressFolder) {
402
- diagnostics.push({
403
- plugin: this.name,
404
- severity: 'warning',
405
- message: `Plugin "${this.name}": "vitepress" is defined in package.json or scripts, but no ".vitepress" folder was found. This might be an unused dependency.`
406
- });
407
- }
408
-
409
- // Scenario 2: folder exists but no dependency -> missing dependency
410
- if (hasVitepressFolder && !hasVitepressDep) {
411
- diagnostics.push({
412
- plugin: this.name,
413
- severity: 'error',
414
- message: `Plugin "${this.name}": ".vitepress" folder exists, but "vitepress" package is not installed in package.json.`
415
- });
416
- }
417
-
418
- return diagnostics;
419
- }
420
-
421
- async analyze(node, filePath) {
422
- // If it's a config file inside .vitepress, mark it
423
- if (filePath.includes('/.vitepress/config.') || filePath.includes('/.vitepress/theme/')) {
424
- node.isEntry = true;
425
- node.isVitepressInternal = true;
426
- }
427
- }
428
285
  }
429
286
 
430
287
  export class GatsbyPlugin extends BasePlugin {
@@ -464,14 +321,6 @@ export class NextJsPlugin extends BasePlugin {
464
321
  }
465
322
  return false;
466
323
  }
467
- async analyze(node, filePath) {
468
- if (this.getConfigFiles().some(f => filePath.endsWith(f))) {
469
- node.isEntry = true;
470
- if (node.rawCode?.includes('rewrites')) node.hasNextRewrites = true;
471
- if (node.rawCode?.includes('redirects')) node.hasNextRedirects = true;
472
- if (node.rawCode?.includes('images:')) node.hasNextImageConfig = true;
473
- }
474
- }
475
324
  }
476
325
 
477
326
  export class ReactPlugin extends BasePlugin {
@@ -594,42 +443,6 @@ export class VitePlugin extends BasePlugin {
594
443
  }
595
444
  return false;
596
445
  }
597
- detectEntryPoints(content, filePath) {
598
- if (filePath.includes('vite.config.')) {
599
- const parser = new FrameworkConfigParser(this.context);
600
- const { entries } = parser.parse(content, filePath);
601
- return Array.from(entries);
602
- }
603
- return [];
604
- }
605
- async analyze(node, filePath) {
606
- if (filePath.includes('vite.config.')) {
607
- // Mark config as an entry so its own dependencies are tracked
608
- node.isEntry = true;
609
-
610
- // UPGRADE: Use FrameworkConfigParser for deep structural extraction
611
- if (node.rawCode) {
612
- const parser = new FrameworkConfigParser(this.context);
613
- const { aliases, entries } = parser.parse(node.rawCode, filePath);
614
-
615
- if (aliases.size > 0) {
616
- node.frameworkAliases = aliases;
617
- node.viteAliasesDetected = true;
618
- }
619
-
620
- if (entries.size > 0) {
621
- node.frameworkEntries = entries;
622
- node.hasCustomViteInputs = true;
623
- }
624
-
625
- // Detect Plugins (Keep existing simple check)
626
- const pluginsPattern = /plugins\s*:\s*\[([\s\S]*?)\]/g;
627
- if (pluginsPattern.test(node.rawCode)) {
628
- node.hasVitePlugins = true;
629
- }
630
- }
631
- }
632
- }
633
446
  }
634
447
 
635
448
  export class EsbuildPlugin extends BasePlugin {
@@ -666,30 +479,6 @@ export class WebpackPlugin extends BasePlugin {
666
479
  }
667
480
  return false;
668
481
  }
669
- detectEntryPoints(content, filePath) {
670
- if (this.getConfigFiles().some(f => filePath.endsWith(f))) {
671
- const parser = new FrameworkConfigParser(this.context);
672
- const { entries } = parser.parse(content, filePath);
673
- return Array.from(entries);
674
- }
675
- return [];
676
- }
677
- detectEntryPoints(content, filePath) {
678
- if (this.getConfigFiles().some(f => filePath.endsWith(f))) {
679
- const parser = new FrameworkConfigParser(this.context);
680
- const { entries } = parser.parse(content, filePath);
681
- return Array.from(entries);
682
- }
683
- return [];
684
- }
685
- async analyze(node, filePath) {
686
- if (this.getConfigFiles().some(f => filePath.endsWith(f))) {
687
- node.isEntry = true;
688
- if (node.rawCode?.includes('entry:')) node.hasWebpackEntries = true;
689
- if (node.rawCode?.includes('resolve:')) node.hasWebpackResolve = true;
690
- if (node.rawCode?.includes('plugins:')) node.hasWebpackPlugins = true;
691
- }
692
- }
693
482
  }
694
483
 
695
484
  export class ParcelPlugin extends BasePlugin {
@@ -732,14 +521,6 @@ export class TailwindPlugin extends BasePlugin {
732
521
  }
733
522
  return false;
734
523
  }
735
- async analyze(node, filePath) {
736
- if (this.getConfigFiles().some(f => filePath.endsWith(f))) {
737
- node.isEntry = true;
738
- if (node.rawCode?.includes('content:')) node.hasTailwindContent = true;
739
- if (node.rawCode?.includes('theme:')) node.hasTailwindTheme = true;
740
- if (node.rawCode?.includes('plugins:')) node.hasTailwindPlugins = true;
741
- }
742
- }
743
524
  }
744
525
 
745
526
  export class PostcssPlugin extends BasePlugin {
@@ -884,13 +665,6 @@ export class JestPlugin extends BasePlugin {
884
665
  async isActive(baseDir) {
885
666
  try { await fs.access(path.join(baseDir, 'jest.config.js')); return true; } catch { return false; }
886
667
  }
887
- async analyze(node, filePath) {
888
- if (filePath.endsWith('jest.config.js')) {
889
- node.isEntry = true;
890
- if (node.rawCode?.includes('testEnvironment:')) node.hasJestTestEnv = true;
891
- if (node.rawCode?.includes('setupFiles:')) node.hasJestSetupFiles = true;
892
- }
893
- }
894
668
  }
895
669
 
896
670
  export class VitestPlugin extends BasePlugin {
@@ -903,39 +677,6 @@ export class VitestPlugin extends BasePlugin {
903
677
  }
904
678
  return false;
905
679
  }
906
- detectEntryPoints(content, filePath) {
907
- if (this.getConfigFiles().some(f => filePath.endsWith(f))) {
908
- const parser = new FrameworkConfigParser(this.context);
909
- const { entries } = parser.parse(content, filePath);
910
- return Array.from(entries);
911
- }
912
- return [];
913
- }
914
- detectEntryPoints(content, filePath) {
915
- if (this.getConfigFiles().some(f => filePath.endsWith(f))) {
916
- const parser = new FrameworkConfigParser(this.context);
917
- const { entries } = parser.parse(content, filePath);
918
- return Array.from(entries);
919
- }
920
- return [];
921
- }
922
- async analyze(node, filePath) {
923
- if (this.getConfigFiles().some(f => filePath.endsWith(f))) {
924
- node.isEntry = true;
925
- if (node.rawCode) {
926
- const parser = new FrameworkConfigParser(this.context);
927
- const { aliases, entries } = parser.parse(node.rawCode, filePath);
928
- if (aliases.size > 0) {
929
- node.frameworkAliases = aliases;
930
- node.hasWebpackAliases = true;
931
- }
932
- if (entries.size > 0) {
933
- node.frameworkEntries = entries;
934
- node.hasWebpackEntries = true;
935
- }
936
- }
937
- }
938
- }
939
680
  }
940
681
 
941
682
  export class PlaywrightPlugin extends BasePlugin {
@@ -3,239 +3,73 @@ import path from 'path';
3
3
 
4
4
  /**
5
5
  * ConfigLoader
6
- *
7
- * Loads and merges entkapp configuration from multiple sources.
8
- * Supports Monorepo detection for pnpm, npm/yarn/bun, Lerna, Turbo, and Nx.
6
+ * Loads and merges entkapp configuration from multiple sources:
7
+ * - entkapp/config.json (project config)
8
+ * - entkapp.config.js / entkapp.config.mjs (JS config)
9
+ * - package.json "entkapp" key
10
+ * - CLI flags (passed as overrides)
9
11
  */
10
12
  export class ConfigLoader {
11
- constructor(cwd) {
12
- this.cwd = cwd;
13
+ constructor(cwd) {
14
+ this.cwd = cwd;
13
15
  }
14
16
 
15
17
  async loadConfig(overrides = {}) {
16
18
  let config = this._defaultConfig();
17
19
 
18
- // 1. entkapp/config.json
20
+ // 1. Try entkapp/config.json
19
21
  const jsonConfigPath = path.join(this.cwd, 'entkapp', 'config.json');
20
22
  try {
21
23
  const raw = await fs.readFile(jsonConfigPath, 'utf8');
24
+ // Strip comments from JSON (JSONC support)
22
25
  const stripped = raw.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
23
- config = this._merge(config, JSON.parse(stripped));
26
+ const parsed = JSON.parse(stripped);
27
+ config = this._merge(config, parsed);
24
28
  } catch (e) {}
25
29
 
26
- // 2. entkapp.config.ts / .mjs / .js / .cjs
27
- for (const configFile of ['entkapp.config.ts', 'entkapp.config.mjs', 'entkapp.config.js', 'entkapp.config.cjs']) {
30
+ // 2. Try entkapp.config.js / entkapp.config.mjs
31
+ for (const configFile of ['entkapp.config.mjs', 'entkapp.config.js', 'entkapp.config.cjs']) {
28
32
  const jsConfigPath = path.join(this.cwd, configFile);
29
33
  try {
30
34
  const mod = await import(jsConfigPath);
31
35
  const jsConfig = mod.default || mod;
32
- if (typeof jsConfig === 'object' && jsConfig !== null) {
36
+ if (typeof jsConfig === 'object') {
33
37
  config = this._merge(config, jsConfig);
34
38
  break;
35
39
  }
36
40
  } catch (e) {}
37
41
  }
38
42
 
39
- // 3. package.json "entkapp" key
43
+ // 3. Try package.json "entkapp" key
44
+ const pkgPath = path.join(this.cwd, 'package.json');
40
45
  try {
41
- const pkg = JSON.parse(await fs.readFile(path.join(this.cwd, 'package.json'), 'utf8'));
46
+ const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
42
47
  if (pkg.entkapp && typeof pkg.entkapp === 'object') {
43
48
  config = this._merge(config, pkg.entkapp);
44
49
  }
45
50
  } catch (e) {}
46
51
 
47
- // 4. Workspace / monorepo packages
48
- const workspaceData = await this.loadWorkspaceConfigs();
49
- if (workspaceData.packages.length > 0) {
50
- config.workspace = true;
51
- config.workspacePackages = workspaceData.packages;
52
- config.workspaceTsConfigs = workspaceData.tsConfigs;
53
- config.workspaceConfigFiles = workspaceData.configFiles;
54
- config.monorepoConfigs = workspaceData.monorepoConfigs;
55
- }
56
-
57
- // 5. CLI overrides
52
+ // 4. Apply CLI overrides
58
53
  config = this._merge(config, overrides);
59
54
 
60
- // Final sanity check: ensure arrays exist
61
- if (!Array.isArray(config.entryPoints)) config.entryPoints = [];
62
- if (!Array.isArray(config.workspacePackages)) config.workspacePackages = [];
63
- if (!Array.isArray(config.workspaceTsConfigs)) config.workspaceTsConfigs = [];
64
- if (!Array.isArray(config.workspaceConfigFiles)) config.workspaceConfigFiles = [];
65
-
66
55
  return config;
67
56
  }
68
57
 
69
- async loadWorkspaceConfigs() {
70
- const result = {
71
- packages: [],
72
- tsConfigs: [],
73
- configFiles: [],
74
- monorepoConfigs: {}
75
- };
76
-
77
- // Load global configs first
78
- const globalConfigs = ['turbo.json', 'nx.json', 'lerna.json'];
79
- for (const file of globalConfigs) {
80
- try {
81
- const content = JSON.parse(await fs.readFile(path.join(this.cwd, file), 'utf8'));
82
- result.monorepoConfigs[file] = content;
83
- } catch (e) {}
84
- }
85
-
86
- const patterns = await this._resolveWorkspacePatterns();
87
- if (patterns.length === 0) return result;
88
-
89
- for (const pattern of patterns) {
90
- const dirs = await this._expandGlob(pattern);
91
- for (const dir of dirs) {
92
- await this._readPackageDir(dir, result);
93
- }
94
- }
95
-
96
- return result;
97
- }
98
-
99
- async _resolveWorkspacePatterns() {
100
- const pnpmPatterns = await this._parseWorkspaceYaml('pnpm-workspace.yaml');
101
- if (pnpmPatterns.length > 0) return pnpmPatterns;
102
-
103
- try {
104
- const pkg = JSON.parse(await fs.readFile(path.join(this.cwd, 'package.json'), 'utf8'));
105
- if (pkg.workspaces) {
106
- return Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces.packages || [];
107
- }
108
- } catch (e) {}
109
-
110
- try {
111
- const lerna = JSON.parse(await fs.readFile(path.join(this.cwd, 'lerna.json'), 'utf8'));
112
- if (lerna.packages) return lerna.packages;
113
- } catch (e) {}
114
-
115
- return this._parseWorkspaceYaml('workspace.yaml');
116
- }
117
-
118
- async _parseWorkspaceYaml(fileName) {
119
- const filePath = path.join(this.cwd, fileName);
120
- try {
121
- const raw = await fs.readFile(filePath, 'utf8');
122
- const patterns = [];
123
- const lines = raw.split('\n');
124
- let inPackages = false;
125
- for (const line of lines) {
126
- const trimmed = line.trim();
127
- if (!trimmed || trimmed.startsWith('#')) continue;
128
- if (/^packages\s*:/.test(trimmed)) {
129
- inPackages = true;
130
- const inlineMatch = trimmed.match(/^packages\s*:\s*\[([^\]]*)\]/);
131
- if (inlineMatch) {
132
- inPackages = false;
133
- for (const item of inlineMatch[1].split(',')) {
134
- const val = item.trim().replace(/^['"]|['"]$/g, '');
135
- if (val) patterns.push(val);
136
- }
137
- }
138
- continue;
139
- }
140
- if (inPackages) {
141
- if (line.match(/^[a-zA-Z0-9_-]/) && trimmed.endsWith(':')) {
142
- inPackages = false;
143
- continue;
144
- }
145
- if (trimmed.startsWith('-')) {
146
- const val = trimmed.substring(1).trim().replace(/^['"]|['"]$/g, '');
147
- if (val) patterns.push(val);
148
- }
149
- }
150
- }
151
- return patterns;
152
- } catch (e) {
153
- return [];
154
- }
155
- }
156
-
157
- async _readPackageDir(dir, result) {
158
- const normalizedDir = dir.replace(/\\/g, '/');
159
- let packageName;
160
-
161
- try {
162
- const pkg = JSON.parse(await fs.readFile(path.join(dir, 'package.json'), 'utf8'));
163
- packageName = pkg.name;
164
- result.packages.push({
165
- dir: normalizedDir,
166
- name: packageName,
167
- entkappConfig: (pkg.entkapp && typeof pkg.entkapp === 'object') ? pkg.entkapp : {}
168
- });
169
- } catch (e) {
170
- // If no package.json, skip as requested in WorkSpaceGraph logic
171
- return;
172
- }
173
-
174
- // tsconfig.json
175
- try {
176
- const tsconfigRaw = await fs.readFile(path.join(dir, 'tsconfig.json'), 'utf8');
177
- const stripped = tsconfigRaw.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
178
- result.tsConfigs.push({ dir: normalizedDir, name: packageName, tsconfig: JSON.parse(stripped) });
179
- } catch (e) {}
180
-
181
- // *.config.ts / .js
182
- try {
183
- const entries = await fs.readdir(dir, { withFileTypes: true });
184
- for (const entry of entries) {
185
- if (!entry.isFile() || !/\.config\.(ts|js|mjs|cjs)$/.test(entry.name)) continue;
186
- const filePath = path.join(dir, entry.name).replace(/\\/g, '/');
187
- try {
188
- const content = await fs.readFile(filePath, 'utf8');
189
- result.configFiles.push({ dir: normalizedDir, name: packageName, fileName: entry.name, filePath, content });
190
- } catch (e) {}
191
- }
192
- } catch (e) {}
193
- }
194
-
195
- async _expandGlob(pattern) {
196
- const results = [];
197
- const cleanPattern = pattern.replace(/\/$/, '');
198
- if (cleanPattern.includes('*')) {
199
- const parts = cleanPattern.split('/');
200
- const wildcardIndex = parts.findIndex(p => p.includes('*'));
201
- const baseDir = path.join(this.cwd, ...parts.slice(0, wildcardIndex));
202
- try {
203
- const entries = await fs.readdir(baseDir, { withFileTypes: true });
204
- for (const entry of entries) {
205
- if (entry.isDirectory() && !entry.name.startsWith('.')) {
206
- const fullPath = path.join(baseDir, entry.name);
207
- try {
208
- await fs.access(path.join(fullPath, 'package.json'));
209
- results.push(fullPath.replace(/\\/g, '/'));
210
- } catch (e) {}
211
- }
212
- }
213
- } catch (e) {}
214
- } else {
215
- const fullPath = path.resolve(this.cwd, cleanPattern);
216
- try {
217
- const stat = await fs.stat(fullPath);
218
- if (stat.isDirectory()) results.push(fullPath.replace(/\\/g, '/'));
219
- } catch (e) {}
220
- }
221
- return results;
222
- }
223
-
224
58
  _defaultConfig() {
225
59
  return {
226
60
  interface: 'CLI',
227
61
  useBuiltinPlugins: true,
228
62
  useCustomPlugins: true,
229
- options: { verbose: false, fastMode: true, selfHealing: true },
63
+ options: {
64
+ verbose: false,
65
+ fastMode: true,
66
+ selfHealing: true
67
+ },
230
68
  enabledPlugins: [],
231
69
  ignoreDependencies: ['entkapp', '@types/*'],
232
70
  exclude: ['node_modules', '.git', 'dist', 'build', 'coverage'],
233
71
  entryPoints: [],
234
- workspace: false,
235
- workspacePackages: [],
236
- workspaceTsConfigs: [],
237
- workspaceConfigFiles: [],
238
- monorepoConfigs: {}
72
+ workspace: false
239
73
  };
240
74
  }
241
75
 
@@ -245,6 +79,8 @@ export class ConfigLoader {
245
79
  for (const key of Object.keys(override)) {
246
80
  if (key === 'options' && typeof override[key] === 'object') {
247
81
  result.options = { ...(base.options || {}), ...override[key] };
82
+ } else if (Array.isArray(override[key])) {
83
+ result[key] = override[key];
248
84
  } else {
249
85
  result[key] = override[key];
250
86
  }