entkapp 5.0.0 → 5.2.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.
Files changed (34) hide show
  1. package/README.md +63 -20
  2. package/bin/cli.js +2 -2
  3. package/index.js +38 -2241
  4. package/package.json +1 -1
  5. package/src/EngineContext.js +1 -1
  6. package/src/Initializer.js +82 -0
  7. package/src/analyzers/CodeSmellAnalyzer.js +106 -0
  8. package/src/ast/ASTAnalyzer.js +29 -14
  9. package/src/ast/BarrelParser.js +22 -20
  10. package/src/ast/OxcAnalyzer.js +33 -409
  11. package/src/index.js +78 -8
  12. package/src/performance/WorkerTaskRunner.js +7 -7
  13. package/src/plugins/BasePlugin.js +171 -2
  14. package/src/plugins/PluginRegistry.js +193 -81
  15. package/src/plugins/ecosystems/BackendServices.js +168 -32
  16. package/src/plugins/ecosystems/GenericPlugins.js +51 -34
  17. package/src/plugins/ecosystems/ModernFrameworks.js +97 -94
  18. package/src/plugins/ecosystems/MorePlugins.js +429 -51
  19. package/src/plugins/ecosystems/NewPlugins.js +526 -0
  20. package/src/plugins/ecosystems/NextJsPlugin.js +18 -6
  21. package/src/plugins/ecosystems/PluginLoader.js +190 -17
  22. package/src/plugins/ecosystems/TypeScriptPlugin.js +10 -10
  23. package/src/plugins/ecosystems/UltimateBundle.js +168 -0
  24. package/src/resolution/BuildOrchestrator.js +46 -0
  25. package/src/resolution/CircularDetector.js +64 -25
  26. package/src/resolution/ConfigGenerator.js +83 -0
  27. package/src/resolution/DepencyResolver.js +12 -1
  28. package/src/resolution/DependencyFixer.js +88 -0
  29. package/src/resolution/EntryPointDetector.js +4 -4
  30. package/src/resolution/GraphAnalyzer.js +80 -0
  31. package/src/resolution/MigrationAnalyzer.js +60 -0
  32. package/src/resolution/PathMapper.js +47 -3
  33. package/src/resolution/WorkSpaceGraph.js +4 -1
  34. package/docs.zip +0 -0
@@ -1,93 +1,175 @@
1
- import fs from 'fs/promises';
1
+ /**
2
+ * ============================================================================
3
+ * More Ecosystem Plugins for entkapp v5.0.0
4
+ * ============================================================================
5
+ * Build tools, testing, linting, formatting, and dev tooling plugins.
6
+ * v5.0.0: All plugins implement getRequiredPackages() for dependency detection.
7
+ */
2
8
  import path from 'path';
9
+ import fs from 'fs/promises';
3
10
  import { BasePlugin } from '../BasePlugin.js';
4
11
 
5
- export class TailwindPlugin extends BasePlugin {
6
- get name() { return 'tailwind'; }
7
- getConfigFiles() { return ['tailwind.config.js', 'tailwind.config.ts', 'tailwind.config.cjs', 'tailwind.config.mjs']; }
12
+ // ─── BUILD TOOLS ────────────────────────────────────────────────────────────
13
+
14
+ export class VitePlugin extends BasePlugin {
15
+ get name() { return 'vite'; }
16
+ getConfigFiles() { return ['vite.config.js', 'vite.config.ts', 'vite.config.mjs', 'vite.config.cjs']; }
17
+ getRequiredPackages() { return [{ name: 'vite', dev: true }]; }
8
18
  async isActive(baseDir) {
19
+ for (const f of this.getConfigFiles()) {
20
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
21
+ }
22
+ return false;
23
+ }
24
+ }
25
+
26
+ export class EsbuildPlugin extends BasePlugin {
27
+ get name() { return 'esbuild'; }
28
+ getConfigFiles() { return ['esbuild.config.js', 'esbuild.config.mjs', 'esbuild.config.ts']; }
29
+ getRequiredPackages() { return [{ name: 'esbuild', dev: true }]; }
30
+ async isActive(baseDir) {
31
+ for (const f of this.getConfigFiles()) {
32
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
33
+ }
9
34
  try {
10
35
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
11
- return !!(pkgJson.dependencies?.tailwindcss || pkgJson.devDependencies?.tailwindcss);
36
+ return !!(pkgJson.dependencies?.esbuild || pkgJson.devDependencies?.esbuild);
12
37
  } catch { return false; }
13
38
  }
14
39
  }
15
40
 
16
- export class PostcssPlugin extends BasePlugin {
17
- get name() { return 'postcss'; }
18
- getConfigFiles() { return ['postcss.config.js', 'postcss.config.cjs', 'postcss.config.mjs']; }
41
+ export class RollupPlugin extends BasePlugin {
42
+ get name() { return 'rollup'; }
43
+ getConfigFiles() { return ['rollup.config.js', 'rollup.config.mjs', 'rollup.config.ts']; }
44
+ getRequiredPackages() { return [{ name: 'rollup', dev: true }]; }
19
45
  async isActive(baseDir) {
46
+ for (const f of this.getConfigFiles()) {
47
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
48
+ }
20
49
  try {
21
50
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
22
- return !!(pkgJson.dependencies?.postcss || pkgJson.devDependencies?.postcss);
51
+ return !!(pkgJson.dependencies?.rollup || pkgJson.devDependencies?.rollup);
23
52
  } catch { return false; }
24
53
  }
25
54
  }
26
55
 
27
- export class JestPlugin extends BasePlugin {
28
- get name() { return 'jest'; }
29
- getConfigFiles() { return ['jest.config.js', 'jest.config.ts', 'jest.config.mjs', 'jest.config.cjs', 'package.json']; }
30
- getRoutePatterns() { return [/\.(test|spec)\.[jt]sx?$/]; }
56
+ export class WebpackPlugin extends BasePlugin {
57
+ get name() { return 'webpack'; }
58
+ getConfigFiles() { return ['webpack.config.js', 'webpack.config.ts', 'webpack.config.mjs', 'webpack.config.cjs']; }
59
+ getRequiredPackages() { return [{ name: 'webpack', dev: true }]; }
31
60
  async isActive(baseDir) {
61
+ for (const f of this.getConfigFiles()) {
62
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
63
+ }
32
64
  try {
33
65
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
34
- return !!(pkgJson.dependencies?.jest || pkgJson.devDependencies?.jest);
66
+ return !!(pkgJson.dependencies?.webpack || pkgJson.devDependencies?.webpack);
35
67
  } catch { return false; }
36
68
  }
37
69
  }
38
70
 
39
- export class VitestPlugin extends BasePlugin {
40
- get name() { return 'vitest'; }
41
- getConfigFiles() { return ['vitest.config.ts', 'vitest.config.js', 'vitest.config.mjs', 'vitest.workspace.ts']; }
42
- getRoutePatterns() { return [/\.(test|spec)\.[jt]sx?$/]; }
71
+ export class ParcelPlugin extends BasePlugin {
72
+ get name() { return 'parcel'; }
73
+ getConfigFiles() { return ['.parcelrc', '.parcelrc.json']; }
74
+ getRequiredPackages() { return [{ name: 'parcel', dev: true }]; }
43
75
  async isActive(baseDir) {
76
+ for (const f of this.getConfigFiles()) {
77
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
78
+ }
44
79
  try {
45
80
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
46
- return !!(pkgJson.dependencies?.vitest || pkgJson.devDependencies?.vitest);
81
+ return !!(pkgJson.dependencies?.parcel || pkgJson.devDependencies?.parcel);
47
82
  } catch { return false; }
48
83
  }
49
84
  }
50
85
 
51
- export class PlaywrightPlugin extends BasePlugin {
52
- get name() { return 'playwright'; }
53
- getConfigFiles() { return ['playwright.config.ts', 'playwright.config.js']; }
54
- getRoutePatterns() { return [/.*\.spec\.[jt]s$/]; }
86
+ export class TurboPlugin extends BasePlugin {
87
+ get name() { return 'turbo'; }
88
+ getConfigFiles() { return ['turbo.json']; }
89
+ getRequiredPackages() { return [{ name: 'turbo', dev: true }]; }
55
90
  async isActive(baseDir) {
91
+ try { await fs.access(path.join(baseDir, 'turbo.json')); return true; } catch { return false; }
92
+ }
93
+ }
94
+
95
+ export class NxPlugin extends BasePlugin {
96
+ get name() { return 'nx'; }
97
+ getConfigFiles() { return ['nx.json', 'workspace.json']; }
98
+ getRequiredPackages() { return [{ name: 'nx', dev: true }]; }
99
+ async isActive(baseDir) {
100
+ try { await fs.access(path.join(baseDir, 'nx.json')); return true; } catch { return false; }
101
+ }
102
+ }
103
+
104
+ // ─── CSS / STYLING ───────────────────────────────────────────────────────────
105
+
106
+ export class TailwindPlugin extends BasePlugin {
107
+ get name() { return 'tailwind'; }
108
+ getConfigFiles() { return ['tailwind.config.js', 'tailwind.config.ts', 'tailwind.config.mjs', 'tailwind.config.cjs']; }
109
+ getRequiredPackages() { return [{ name: 'tailwindcss', dev: true }]; }
110
+ async isActive(baseDir) {
111
+ for (const f of this.getConfigFiles()) {
112
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
113
+ }
56
114
  try {
57
115
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
58
- return !!(pkgJson.dependencies?.['@playwright/test'] || pkgJson.devDependencies?.['@playwright/test']);
116
+ return !!(pkgJson.dependencies?.tailwindcss || pkgJson.devDependencies?.tailwindcss);
59
117
  } catch { return false; }
60
118
  }
61
119
  }
62
120
 
63
- export class CypressPlugin extends BasePlugin {
64
- get name() { return 'cypress'; }
65
- getConfigFiles() { return ['cypress.config.ts', 'cypress.config.js']; }
66
- getRoutePatterns() { return [/cypress\/e2e\/.*\.cy\.[jt]s$/]; }
121
+ export class PostcssPlugin extends BasePlugin {
122
+ get name() { return 'postcss'; }
123
+ getConfigFiles() { return ['postcss.config.js', 'postcss.config.cjs', 'postcss.config.mjs', 'postcss.config.ts', '.postcssrc', '.postcssrc.json', '.postcssrc.js']; }
124
+ getRequiredPackages() { return [{ name: 'postcss', dev: true }]; }
67
125
  async isActive(baseDir) {
126
+ for (const f of this.getConfigFiles()) {
127
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
128
+ }
68
129
  try {
69
130
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
70
- return !!(pkgJson.dependencies?.cypress || pkgJson.devDependencies?.cypress);
131
+ return !!(pkgJson.dependencies?.postcss || pkgJson.devDependencies?.postcss);
71
132
  } catch { return false; }
72
133
  }
73
134
  }
74
135
 
75
- export class StorybookPlugin extends BasePlugin {
76
- get name() { return 'storybook'; }
77
- getConfigFiles() { return ['.storybook/main.js', '.storybook/main.ts', '.storybook/preview.js', '.storybook/preview.ts']; }
78
- getRoutePatterns() { return [/\.stories\.[jt]sx?$/]; }
136
+ export class UnoCSSPlugin extends BasePlugin {
137
+ get name() { return 'unocss'; }
138
+ getConfigFiles() { return ['uno.config.ts', 'uno.config.js', 'unocss.config.ts', 'unocss.config.js']; }
139
+ getRequiredPackages() { return [{ name: 'unocss', dev: true }]; }
79
140
  async isActive(baseDir) {
141
+ for (const f of this.getConfigFiles()) {
142
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
143
+ }
144
+ return false;
145
+ }
146
+ }
147
+
148
+ export class StylelintPlugin extends BasePlugin {
149
+ get name() { return 'stylelint'; }
150
+ getConfigFiles() { return ['.stylelintrc', '.stylelintrc.js', '.stylelintrc.cjs', '.stylelintrc.json', '.stylelintrc.yml', '.stylelintrc.yaml', 'stylelint.config.js', 'stylelint.config.cjs']; }
151
+ getRequiredPackages() { return [{ name: 'stylelint', dev: true }]; }
152
+ async isActive(baseDir) {
153
+ for (const f of this.getConfigFiles()) {
154
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
155
+ }
80
156
  try {
81
157
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
82
- return !!(pkgJson.dependencies?.storybook || pkgJson.devDependencies?.storybook || pkgJson.devDependencies?.['@storybook/react']);
158
+ return !!(pkgJson.dependencies?.stylelint || pkgJson.devDependencies?.stylelint);
83
159
  } catch { return false; }
84
160
  }
85
161
  }
86
162
 
163
+ // ─── LINTING / FORMATTING ────────────────────────────────────────────────────
164
+
87
165
  export class EslintPlugin extends BasePlugin {
88
166
  get name() { return 'eslint'; }
89
- getConfigFiles() { return ['.eslintrc', '.eslintrc.js', '.eslintrc.cjs', '.eslintrc.json', 'eslint.config.js', 'eslint.config.mjs']; }
167
+ getConfigFiles() { return ['.eslintrc', '.eslintrc.js', '.eslintrc.cjs', '.eslintrc.json', '.eslintrc.yml', '.eslintrc.yaml', 'eslint.config.js', 'eslint.config.mjs', 'eslint.config.cjs']; }
168
+ getRequiredPackages() { return [{ name: 'eslint', dev: true }]; }
90
169
  async isActive(baseDir) {
170
+ for (const f of this.getConfigFiles()) {
171
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
172
+ }
91
173
  try {
92
174
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
93
175
  return !!(pkgJson.dependencies?.eslint || pkgJson.devDependencies?.eslint);
@@ -97,8 +179,12 @@ export class EslintPlugin extends BasePlugin {
97
179
 
98
180
  export class PrettierPlugin extends BasePlugin {
99
181
  get name() { return 'prettier'; }
100
- getConfigFiles() { return ['.prettierrc', '.prettierrc.js', '.prettierrc.cjs', '.prettierrc.json', 'prettier.config.js']; }
182
+ getConfigFiles() { return ['.prettierrc', '.prettierrc.js', '.prettierrc.cjs', '.prettierrc.json', '.prettierrc.yml', '.prettierrc.yaml', '.prettierrc.toml', 'prettier.config.js', 'prettier.config.cjs', 'prettier.config.mjs']; }
183
+ getRequiredPackages() { return [{ name: 'prettier', dev: true }]; }
101
184
  async isActive(baseDir) {
185
+ for (const f of this.getConfigFiles()) {
186
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
187
+ }
102
188
  try {
103
189
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
104
190
  return !!(pkgJson.dependencies?.prettier || pkgJson.devDependencies?.prettier);
@@ -106,10 +192,43 @@ export class PrettierPlugin extends BasePlugin {
106
192
  }
107
193
  }
108
194
 
195
+ export class BiomePlugin extends BasePlugin {
196
+ get name() { return 'biome'; }
197
+ getConfigFiles() { return ['biome.json', 'biome.jsonc']; }
198
+ getRequiredPackages() { return [{ name: '@biomejs/biome', dev: true }]; }
199
+ async isActive(baseDir) {
200
+ for (const f of this.getConfigFiles()) {
201
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
202
+ }
203
+ return false;
204
+ }
205
+ }
206
+
207
+ export class OxlintPlugin extends BasePlugin {
208
+ get name() { return 'oxlint'; }
209
+ getConfigFiles() { return ['.oxlintrc', '.oxlintrc.json', 'oxlint.json']; }
210
+ getRequiredPackages() { return [{ name: 'oxlint', dev: true }]; }
211
+ async isActive(baseDir) {
212
+ for (const f of this.getConfigFiles()) {
213
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
214
+ }
215
+ try {
216
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
217
+ return !!(pkgJson.dependencies?.oxlint || pkgJson.devDependencies?.oxlint);
218
+ } catch { return false; }
219
+ }
220
+ }
221
+
222
+ // ─── GIT HOOKS / COMMIT TOOLING ──────────────────────────────────────────────
223
+
109
224
  export class HuskyPlugin extends BasePlugin {
110
225
  get name() { return 'husky'; }
111
- getConfigFiles() { return ['.husky/pre-commit', '.husky/pre-push']; }
226
+ getConfigFiles() { return ['.husky/pre-commit', '.husky/pre-push', '.husky/commit-msg', '.husky/_/husky.sh']; }
227
+ getRequiredPackages() { return [{ name: 'husky', dev: true }]; }
112
228
  async isActive(baseDir) {
229
+ for (const f of this.getConfigFiles()) {
230
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
231
+ }
113
232
  try {
114
233
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
115
234
  return !!(pkgJson.dependencies?.husky || pkgJson.devDependencies?.husky);
@@ -119,10 +238,15 @@ export class HuskyPlugin extends BasePlugin {
119
238
 
120
239
  export class LintStagedPlugin extends BasePlugin {
121
240
  get name() { return 'lint-staged'; }
122
- getConfigFiles() { return ['.lintstagedrc', '.lintstagedrc.js', '.lintstagedrc.json', 'lint-staged.config.js', 'package.json']; }
241
+ getConfigFiles() { return ['.lintstagedrc', '.lintstagedrc.js', '.lintstagedrc.cjs', '.lintstagedrc.json', '.lintstagedrc.yml', '.lintstagedrc.yaml', 'lint-staged.config.js', 'lint-staged.config.cjs']; }
242
+ getRequiredPackages() { return [{ name: 'lint-staged', dev: true }]; }
123
243
  async isActive(baseDir) {
244
+ for (const f of this.getConfigFiles()) {
245
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
246
+ }
124
247
  try {
125
248
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
249
+ if (pkgJson['lint-staged']) return true;
126
250
  return !!(pkgJson.dependencies?.['lint-staged'] || pkgJson.devDependencies?.['lint-staged']);
127
251
  } catch { return false; }
128
252
  }
@@ -130,8 +254,12 @@ export class LintStagedPlugin extends BasePlugin {
130
254
 
131
255
  export class CommitlintPlugin extends BasePlugin {
132
256
  get name() { return 'commitlint'; }
133
- getConfigFiles() { return ['.commitlintrc', '.commitlintrc.js', '.commitlintrc.json', 'commitlint.config.js', 'package.json']; }
257
+ getConfigFiles() { return ['.commitlintrc', '.commitlintrc.js', '.commitlintrc.cjs', '.commitlintrc.json', '.commitlintrc.yml', '.commitlintrc.yaml', 'commitlint.config.js', 'commitlint.config.cjs', 'commitlint.config.ts']; }
258
+ getRequiredPackages() { return [{ name: '@commitlint/cli', dev: true }, { name: '@commitlint/config-conventional', dev: true, optional: true }]; }
134
259
  async isActive(baseDir) {
260
+ for (const f of this.getConfigFiles()) {
261
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
262
+ }
135
263
  try {
136
264
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
137
265
  return !!(pkgJson.dependencies?.['@commitlint/cli'] || pkgJson.devDependencies?.['@commitlint/cli']);
@@ -139,10 +267,25 @@ export class CommitlintPlugin extends BasePlugin {
139
267
  }
140
268
  }
141
269
 
270
+ export class ChangesetPlugin extends BasePlugin {
271
+ get name() { return 'changeset'; }
272
+ getConfigFiles() { return ['.changeset/config.json']; }
273
+ getRequiredPackages() { return [{ name: '@changesets/cli', dev: true }]; }
274
+ async isActive(baseDir) {
275
+ try { await fs.access(path.join(baseDir, '.changeset/config.json')); return true; } catch { return false; }
276
+ }
277
+ }
278
+
279
+ // ─── TRANSPILERS ─────────────────────────────────────────────────────────────
280
+
142
281
  export class BabelPlugin extends BasePlugin {
143
282
  get name() { return 'babel'; }
144
- getConfigFiles() { return ['.babelrc', '.babelrc.js', '.babelrc.json', 'babel.config.js', 'babel.config.json', 'package.json']; }
283
+ getConfigFiles() { return ['.babelrc', '.babelrc.js', '.babelrc.cjs', '.babelrc.json', '.babelrc.mjs', 'babel.config.js', 'babel.config.cjs', 'babel.config.json', 'babel.config.mjs']; }
284
+ getRequiredPackages() { return [{ name: '@babel/core', dev: true }]; }
145
285
  async isActive(baseDir) {
286
+ for (const f of this.getConfigFiles()) {
287
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
288
+ }
146
289
  try {
147
290
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
148
291
  return !!(pkgJson.dependencies?.['@babel/core'] || pkgJson.devDependencies?.['@babel/core']);
@@ -150,35 +293,270 @@ export class BabelPlugin extends BasePlugin {
150
293
  }
151
294
  }
152
295
 
153
- export class RollupPlugin extends BasePlugin {
154
- get name() { return 'rollup'; }
155
- getConfigFiles() { return ['rollup.config.js', 'rollup.config.mjs', 'rollup.config.ts']; }
296
+ export class SWCPlugin extends BasePlugin {
297
+ get name() { return 'swc'; }
298
+ getConfigFiles() { return ['.swcrc', '.swcrc.json']; }
299
+ getRequiredPackages() { return [{ name: '@swc/core', dev: true }]; }
156
300
  async isActive(baseDir) {
301
+ for (const f of this.getConfigFiles()) {
302
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
303
+ }
157
304
  try {
158
305
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
159
- return !!(pkgJson.dependencies?.rollup || pkgJson.devDependencies?.rollup);
306
+ return !!(pkgJson.dependencies?.['@swc/core'] || pkgJson.devDependencies?.['@swc/core']);
160
307
  } catch { return false; }
161
308
  }
162
309
  }
163
310
 
164
- export class WebpackPlugin extends BasePlugin {
165
- get name() { return 'webpack'; }
166
- getConfigFiles() { return ['webpack.config.js', 'webpack.config.ts', 'webpack.config.mjs', 'webpack.config.cjs']; }
311
+ // ─── TESTING ─────────────────────────────────────────────────────────────────
312
+
313
+ export class JestPlugin extends BasePlugin {
314
+ get name() { return 'jest'; }
315
+ getConfigFiles() { return ['jest.config.js', 'jest.config.ts', 'jest.config.mjs', 'jest.config.cjs', 'jest.config.json']; }
316
+ getRequiredPackages() { return [{ name: 'jest', dev: true }]; }
317
+ getRoutePatterns() { return [/\.test\.[jt]sx?$/, /\.spec\.[jt]sx?$/]; }
167
318
  async isActive(baseDir) {
319
+ for (const f of this.getConfigFiles()) {
320
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
321
+ }
168
322
  try {
169
323
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
170
- return !!(pkgJson.dependencies?.webpack || pkgJson.devDependencies?.webpack);
324
+ if (pkgJson.jest) return true;
325
+ return !!(pkgJson.dependencies?.jest || pkgJson.devDependencies?.jest);
171
326
  } catch { return false; }
172
327
  }
173
328
  }
174
329
 
330
+ export class VitestPlugin extends BasePlugin {
331
+ get name() { return 'vitest'; }
332
+ getConfigFiles() { return ['vitest.config.js', 'vitest.config.ts', 'vitest.config.mjs', 'vitest.workspace.ts', 'vitest.workspace.js']; }
333
+ getRequiredPackages() { return [{ name: 'vitest', dev: true }]; }
334
+ getRoutePatterns() { return [/\.test\.[jt]sx?$/, /\.spec\.[jt]sx?$/]; }
335
+ async isActive(baseDir) {
336
+ for (const f of this.getConfigFiles()) {
337
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
338
+ }
339
+ try {
340
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
341
+ return !!(pkgJson.dependencies?.vitest || pkgJson.devDependencies?.vitest);
342
+ } catch { return false; }
343
+ }
344
+ }
345
+
346
+ export class PlaywrightPlugin extends BasePlugin {
347
+ get name() { return 'playwright'; }
348
+ getConfigFiles() { return ['playwright.config.ts', 'playwright.config.js']; }
349
+ getRequiredPackages() { return [{ name: '@playwright/test', dev: true }]; }
350
+ getRoutePatterns() { return [/\.spec\.ts$/, /\.spec\.js$/]; }
351
+ async isActive(baseDir) {
352
+ for (const f of this.getConfigFiles()) {
353
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
354
+ }
355
+ try {
356
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
357
+ return !!(pkgJson.dependencies?.['@playwright/test'] || pkgJson.devDependencies?.['@playwright/test']);
358
+ } catch { return false; }
359
+ }
360
+ }
361
+
362
+ export class CypressPlugin extends BasePlugin {
363
+ get name() { return 'cypress'; }
364
+ getConfigFiles() { return ['cypress.config.ts', 'cypress.config.js', 'cypress.json']; }
365
+ getRequiredPackages() { return [{ name: 'cypress', dev: true }]; }
366
+ getRoutePatterns() { return [/cypress\/e2e\/.*\.cy\.[jt]s$/]; }
367
+ async isActive(baseDir) {
368
+ for (const f of this.getConfigFiles()) {
369
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
370
+ }
371
+ try {
372
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
373
+ return !!(pkgJson.dependencies?.cypress || pkgJson.devDependencies?.cypress);
374
+ } catch { return false; }
375
+ }
376
+ }
377
+
378
+ export class StorybookPlugin extends BasePlugin {
379
+ get name() { return 'storybook'; }
380
+ getConfigFiles() { return ['.storybook/main.js', '.storybook/main.ts', '.storybook/main.cjs', '.storybook/preview.js', '.storybook/preview.ts']; }
381
+ getRequiredPackages() { return [{ name: 'storybook', dev: true, optional: true }, { name: '@storybook/react', dev: true, optional: true }]; }
382
+ getRoutePatterns() { return [/\.stories\.[jt]sx?$/]; }
383
+ async isActive(baseDir) {
384
+ for (const f of this.getConfigFiles()) {
385
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
386
+ }
387
+ try {
388
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
389
+ const allDeps = { ...(pkgJson.dependencies || {}), ...(pkgJson.devDependencies || {}) };
390
+ return Object.keys(allDeps).some(k => k.startsWith('@storybook/') || k === 'storybook');
391
+ } catch { return false; }
392
+ }
393
+ }
394
+
395
+ export class MswPlugin extends BasePlugin {
396
+ get name() { return 'msw'; }
397
+ getConfigFiles() { return ['msw.config.ts', 'msw.config.js', 'public/mockServiceWorker.js']; }
398
+ getRequiredPackages() { return [{ name: 'msw', dev: true }]; }
399
+ async isActive(baseDir) {
400
+ for (const f of this.getConfigFiles()) {
401
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
402
+ }
403
+ try {
404
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
405
+ return !!(pkgJson.dependencies?.msw || pkgJson.devDependencies?.msw);
406
+ } catch { return false; }
407
+ }
408
+ }
409
+
410
+ // ─── CI / INFRA ──────────────────────────────────────────────────────────────
411
+
175
412
  export class GithubActionsPlugin extends BasePlugin {
176
413
  get name() { return 'github-actions'; }
177
- getConfigFiles() { return ['.github/workflows/*.yml', '.github/workflows/*.yaml']; }
414
+ getConfigFiles() { return ['.github/workflows']; }
415
+ async isActive(baseDir) {
416
+ try { await fs.access(path.join(baseDir, '.github/workflows')); return true; } catch { return false; }
417
+ }
418
+ }
419
+
420
+ export class DockerPlugin extends BasePlugin {
421
+ get name() { return 'docker'; }
422
+ getConfigFiles() { return ['Dockerfile', 'docker-compose.yml', 'docker-compose.yaml', 'docker-compose.dev.yml', '.dockerignore']; }
423
+ async isActive(baseDir) {
424
+ for (const f of this.getConfigFiles()) {
425
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
426
+ }
427
+ return false;
428
+ }
429
+ }
430
+
431
+ export class TerraformPlugin extends BasePlugin {
432
+ get name() { return 'terraform'; }
433
+ getConfigFiles() { return ['main.tf', 'variables.tf', 'outputs.tf', 'terraform.tfvars']; }
434
+ getRoutePatterns() { return [/\.tf$/]; }
435
+ async isActive(baseDir) {
436
+ for (const f of this.getConfigFiles()) {
437
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
438
+ }
439
+ return false;
440
+ }
441
+ }
442
+
443
+ // ─── DEV ENVIRONMENT ─────────────────────────────────────────────────────────
444
+
445
+ export class EditorConfigPlugin extends BasePlugin {
446
+ get name() { return 'editorconfig'; }
447
+ getConfigFiles() { return ['.editorconfig']; }
448
+ async isActive(baseDir) {
449
+ try { await fs.access(path.join(baseDir, '.editorconfig')); return true; } catch { return false; }
450
+ }
451
+ }
452
+
453
+ export class NvmPlugin extends BasePlugin {
454
+ get name() { return 'nvm'; }
455
+ getConfigFiles() { return ['.nvmrc', '.node-version']; }
456
+ async isActive(baseDir) {
457
+ for (const f of this.getConfigFiles()) {
458
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
459
+ }
460
+ return false;
461
+ }
462
+ }
463
+
464
+ export class VoltaPlugin extends BasePlugin {
465
+ get name() { return 'volta'; }
466
+ getConfigFiles() { return ['package.json']; }
467
+ async isActive(baseDir) {
468
+ try {
469
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
470
+ return !!(pkgJson.volta);
471
+ } catch { return false; }
472
+ }
473
+ }
474
+
475
+ export class DotenvPlugin extends BasePlugin {
476
+ get name() { return 'dotenv'; }
477
+ getConfigFiles() { return ['.env', '.env.local', '.env.development', '.env.production', '.env.test', '.env.example', '.env.sample']; }
478
+ getRequiredPackages() { return [{ name: 'dotenv', dev: false, optional: true }]; }
479
+ async isActive(baseDir) {
480
+ for (const f of this.getConfigFiles()) {
481
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
482
+ }
483
+ return false;
484
+ }
485
+ }
486
+
487
+ // ─── PACKAGE MANAGERS ────────────────────────────────────────────────────────
488
+
489
+ export class PnpmPlugin extends BasePlugin {
490
+ get name() { return 'pnpm'; }
491
+ getConfigFiles() { return ['pnpm-lock.yaml', 'pnpm-workspace.yaml']; }
492
+ async isActive(baseDir) {
493
+ try { await fs.access(path.join(baseDir, 'pnpm-lock.yaml')); return true; } catch { return false; }
494
+ }
495
+ }
496
+
497
+ export class YarnPlugin extends BasePlugin {
498
+ get name() { return 'yarn'; }
499
+ getConfigFiles() { return ['yarn.lock', '.yarnrc.yml', '.yarnrc']; }
500
+ async isActive(baseDir) {
501
+ try { await fs.access(path.join(baseDir, 'yarn.lock')); return true; } catch { return false; }
502
+ }
503
+ }
504
+
505
+ export class BunPlugin extends BasePlugin {
506
+ get name() { return 'bun'; }
507
+ getConfigFiles() { return ['bun.lockb', 'bunfig.toml']; }
508
+ async isActive(baseDir) {
509
+ for (const f of this.getConfigFiles()) {
510
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
511
+ }
512
+ return false;
513
+ }
514
+ }
515
+
516
+ // ─── MISC UTILITIES ──────────────────────────────────────────────────────────
517
+
518
+ export class SwiperPlugin extends BasePlugin {
519
+ get name() { return 'swiper'; }
520
+ getConfigFiles() { return ['package.json']; }
521
+ getRequiredPackages() { return [{ name: 'swiper', dev: false }]; }
178
522
  async isActive(baseDir) {
179
523
  try {
180
- await fs.access(path.join(baseDir, '.github/workflows'));
181
- return true;
524
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
525
+ return !!(pkgJson.dependencies?.swiper || pkgJson.devDependencies?.swiper);
182
526
  } catch { return false; }
183
527
  }
528
+ async analyze(node) {
529
+ if (node.rawCode?.includes('new Swiper(')) node.isEntry = true;
530
+ }
531
+ }
532
+
533
+ export class QuillPlugin extends BasePlugin {
534
+ get name() { return 'quill'; }
535
+ getConfigFiles() { return ['package.json']; }
536
+ getRequiredPackages() { return [{ name: 'quill', dev: false }]; }
537
+ async isActive(baseDir) {
538
+ try {
539
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
540
+ return !!(pkgJson.dependencies?.quill || pkgJson.devDependencies?.quill);
541
+ } catch { return false; }
542
+ }
543
+ async analyze(node) {
544
+ if (node.rawCode?.includes('new Quill(')) node.isEntry = true;
545
+ }
546
+ }
547
+
548
+ export class EnvelopPlugin extends BasePlugin {
549
+ get name() { return 'envelop'; }
550
+ getConfigFiles() { return ['package.json']; }
551
+ getRequiredPackages() { return [{ name: '@envelop/core', dev: false }]; }
552
+ async isActive(baseDir) {
553
+ try {
554
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
555
+ const allDeps = { ...(pkgJson.dependencies || {}), ...(pkgJson.devDependencies || {}) };
556
+ return Object.keys(allDeps).some(k => k.startsWith('@envelop/'));
557
+ } catch { return false; }
558
+ }
559
+ async analyze(node) {
560
+ if (node.rawCode?.includes('useEnvelop(')) node.isEntry = true;
561
+ }
184
562
  }