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,61 +1,197 @@
1
1
  /**
2
2
  * ============================================================================
3
- * Backend Services Plugins for entkapp v4.1.0
3
+ * Backend Services Plugins for entkapp v5.0.0
4
4
  * ============================================================================
5
- * Built-in support for GraphQL, REST APIs, and Databases.
5
+ * Built-in support for GraphQL, REST APIs, Databases, and BaaS providers.
6
+ * v5.0.0: All plugins implement getRequiredPackages() for dependency detection.
6
7
  */
7
-
8
8
  import { BasePlugin } from '../BasePlugin.js';
9
9
  import fs from 'fs/promises';
10
10
  import path from 'path';
11
11
 
12
- /**
13
- * GraphQL Ecosystem Plugin
14
- */
15
12
  export class GraphQLPlugin extends BasePlugin {
16
- get name() {
17
- return 'graphql';
18
- }
19
-
20
- getConfigFiles() {
21
- return ['package.json', 'graphql.config.js', '.graphqlconfig'];
13
+ get name() { return 'graphql'; }
14
+ getConfigFiles() { return ['package.json', 'graphql.config.js', 'graphql.config.ts', '.graphqlconfig', 'graphql.config.yml']; }
15
+ getRequiredPackages() {
16
+ return [{ name: 'graphql', dev: false }];
22
17
  }
23
-
24
18
  async isActive(baseDir) {
25
19
  try {
26
20
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
27
21
  return !!(pkgJson.dependencies?.graphql || pkgJson.devDependencies?.graphql || pkgJson.dependencies?.['@apollo/client']);
28
- } catch {
29
- return false;
30
- }
22
+ } catch { return false; }
31
23
  }
32
-
33
24
  async analyze(node, filePath) {
34
- // Detect GraphQL tagged templates
35
25
  const gqlPattern = /gql\s*`([\s\S]*?)`/g;
36
26
  const matches = node.rawCode?.match(gqlPattern) || [];
37
- if (matches.length > 0) {
38
- node.graphqlQueries = matches;
39
- }
27
+ if (matches.length > 0) node.graphqlQueries = matches;
40
28
  }
41
29
  }
42
30
 
43
- /**
44
- * Database Ecosystem Plugin (Prisma, Drizzle, TypeORM)
45
- */
46
- export class DatabasePlugin extends BasePlugin {
47
- get name() {
48
- return 'database';
31
+ export class ApolloPlugin extends BasePlugin {
32
+ get name() { return 'apollo'; }
33
+ getConfigFiles() { return ['package.json', 'apollo.config.js', 'apollo.config.ts']; }
34
+ getRequiredPackages() {
35
+ return [
36
+ { name: '@apollo/client', dev: false },
37
+ { name: '@apollo/server', dev: false, optional: true },
38
+ ];
49
39
  }
50
-
51
- getConfigFiles() {
52
- return ['package.json', 'prisma/schema.prisma', 'drizzle.config.ts', 'ormconfig.json'];
40
+ async isActive(baseDir) {
41
+ try {
42
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
43
+ return !!(pkgJson.dependencies?.['@apollo/client'] || pkgJson.dependencies?.['@apollo/server'] ||
44
+ pkgJson.devDependencies?.['@apollo/client'] || pkgJson.devDependencies?.['@apollo/server']);
45
+ } catch { return false; }
46
+ }
47
+ async analyze(node) {
48
+ if (node.rawCode?.includes('ApolloClient') || node.rawCode?.includes('ApolloServer')) node.isEntry = true;
53
49
  }
50
+ }
54
51
 
52
+ export class DatabasePlugin extends BasePlugin {
53
+ get name() { return 'database'; }
54
+ getConfigFiles() { return ['package.json', 'prisma/schema.prisma', 'drizzle.config.ts', 'drizzle.config.js', 'ormconfig.json']; }
55
+ getRequiredPackages() {
56
+ return [
57
+ { name: '@prisma/client', dev: false, optional: true },
58
+ { name: 'drizzle-orm', dev: false, optional: true },
59
+ { name: 'typeorm', dev: false, optional: true },
60
+ ];
61
+ }
55
62
  async analyze(node, filePath) {
56
- // Detect DB usage
57
- if (node.explicitImports.has('@prisma/client') || node.explicitImports.has('drizzle-orm')) {
63
+ if (node.explicitImports?.has('@prisma/client') || node.explicitImports?.has('drizzle-orm'))
58
64
  node.usesDatabase = true;
65
+ }
66
+ }
67
+
68
+ export class PrismaPlugin extends BasePlugin {
69
+ get name() { return 'prisma'; }
70
+ getConfigFiles() { return ['prisma/schema.prisma', 'schema.prisma']; }
71
+ getRequiredPackages() {
72
+ return [
73
+ { name: '@prisma/client', dev: false },
74
+ { name: 'prisma', dev: true },
75
+ ];
76
+ }
77
+ async isActive(baseDir) {
78
+ for (const f of this.getConfigFiles()) {
79
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
80
+ }
81
+ try {
82
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
83
+ return !!(pkgJson.dependencies?.['@prisma/client'] || pkgJson.devDependencies?.prisma);
84
+ } catch { return false; }
85
+ }
86
+ async analyze(node) {
87
+ if (node.rawCode?.includes('new PrismaClient()')) node.isEntry = true;
88
+ }
89
+ }
90
+
91
+ export class DrizzlePlugin extends BasePlugin {
92
+ get name() { return 'drizzle'; }
93
+ getConfigFiles() { return ['drizzle.config.ts', 'drizzle.config.js', 'drizzle.config.mjs']; }
94
+ getRequiredPackages() {
95
+ return [
96
+ { name: 'drizzle-orm', dev: false },
97
+ { name: 'drizzle-kit', dev: true },
98
+ ];
99
+ }
100
+ async isActive(baseDir) {
101
+ for (const f of this.getConfigFiles()) {
102
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
103
+ }
104
+ try {
105
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
106
+ return !!(pkgJson.dependencies?.['drizzle-orm'] || pkgJson.devDependencies?.['drizzle-orm']);
107
+ } catch { return false; }
108
+ }
109
+ async analyze(node) {
110
+ if (node.rawCode?.includes('drizzle(')) node.isEntry = true;
111
+ }
112
+ }
113
+
114
+ export class MongoosePlugin extends BasePlugin {
115
+ get name() { return 'mongoose'; }
116
+ getConfigFiles() { return ['package.json']; }
117
+ getRequiredPackages() {
118
+ return [{ name: 'mongoose', dev: false }];
119
+ }
120
+ async isActive(baseDir) {
121
+ try {
122
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
123
+ return !!(pkgJson.dependencies?.mongoose || pkgJson.devDependencies?.mongoose);
124
+ } catch { return false; }
125
+ }
126
+ async analyze(node) {
127
+ if (node.rawCode?.includes('mongoose.model(')) node.isEntry = true;
128
+ }
129
+ }
130
+
131
+ export class SupabasePlugin extends BasePlugin {
132
+ get name() { return 'supabase'; }
133
+ getConfigFiles() { return ['supabase/config.toml', '.env', '.env.local', 'package.json']; }
134
+ getRequiredPackages() {
135
+ return [{ name: '@supabase/supabase-js', dev: false }];
136
+ }
137
+ async isActive(baseDir) {
138
+ try {
139
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
140
+ return !!(pkgJson.dependencies?.['@supabase/supabase-js'] || pkgJson.devDependencies?.['@supabase/supabase-js']);
141
+ } catch { return false; }
142
+ }
143
+ async analyze(node) {
144
+ if (node.rawCode?.includes('createClient') && node.rawCode?.includes('supabase')) node.isEntry = true;
145
+ }
146
+ }
147
+
148
+ export class FirebasePlugin extends BasePlugin {
149
+ get name() { return 'firebase'; }
150
+ getConfigFiles() { return ['firebase.json', '.firebaserc', 'package.json']; }
151
+ getRequiredPackages() {
152
+ return [{ name: 'firebase', dev: false }];
153
+ }
154
+ async isActive(baseDir) {
155
+ for (const f of ['firebase.json', '.firebaserc']) {
156
+ try { await fs.access(path.join(baseDir, f)); return true; } catch {}
59
157
  }
158
+ try {
159
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
160
+ return !!(pkgJson.dependencies?.firebase || pkgJson.devDependencies?.firebase);
161
+ } catch { return false; }
162
+ }
163
+ async analyze(node) {
164
+ if (node.rawCode?.includes('initializeApp') && node.rawCode?.includes('firebase')) node.isEntry = true;
165
+ }
166
+ }
167
+
168
+ export class ClerkPlugin extends BasePlugin {
169
+ get name() { return 'clerk'; }
170
+ getConfigFiles() { return ['package.json', '.env', '.env.local']; }
171
+ getRequiredPackages() {
172
+ return [{ name: '@clerk/nextjs', dev: false, optional: true }, { name: '@clerk/clerk-react', dev: false, optional: true }];
173
+ }
174
+ async isActive(baseDir) {
175
+ try {
176
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
177
+ const allDeps = { ...(pkgJson.dependencies || {}), ...(pkgJson.devDependencies || {}) };
178
+ return Object.keys(allDeps).some(k => k.startsWith('@clerk/'));
179
+ } catch { return false; }
180
+ }
181
+ }
182
+
183
+ export class TRPCPlugin extends BasePlugin {
184
+ get name() { return 'trpc'; }
185
+ getConfigFiles() { return ['package.json']; }
186
+ getRequiredPackages() {
187
+ return [{ name: '@trpc/server', dev: false }, { name: '@trpc/client', dev: false }];
188
+ }
189
+ async isActive(baseDir) {
190
+ try {
191
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
192
+ const allDeps = { ...(pkgJson.dependencies || {}), ...(pkgJson.devDependencies || {}) };
193
+ return Object.keys(allDeps).some(k => k.startsWith('@trpc/'));
194
+ } catch { return false; }
60
195
  }
196
+ getRequiredSystemContracts() { return ['router', 'procedure', 'createTRPCRouter']; }
61
197
  }
@@ -1,3 +1,10 @@
1
+ /**
2
+ * ============================================================================
3
+ * Generic Ecosystem Plugins for entkapp v5.0.0
4
+ * ============================================================================
5
+ * Nuxt, Remix, SvelteKit, Astro, VitePress, and more.
6
+ * v5.0.0: All plugins implement getRequiredPackages() for dependency detection.
7
+ */
1
8
  import path from 'path';
2
9
  import fs from 'fs/promises';
3
10
  import { BasePlugin } from '../BasePlugin.js';
@@ -5,6 +12,7 @@ import { BasePlugin } from '../BasePlugin.js';
5
12
  export class NuxtPlugin extends BasePlugin {
6
13
  get name() { return 'nuxt'; }
7
14
  getConfigFiles() { return ['nuxt.config.js', 'nuxt.config.ts']; }
15
+ getRequiredPackages() { return [{ name: 'nuxt', dev: false }]; }
8
16
  getRoutePatterns() {
9
17
  return [/\/pages\//, /\/server\/(api|routes|middleware)\//, /\/components\/[a-zA-Z0-9_\-\/]+\.vue$/];
10
18
  }
@@ -20,6 +28,12 @@ export class NuxtPlugin extends BasePlugin {
20
28
  export class RemixPlugin extends BasePlugin {
21
29
  get name() { return 'remix'; }
22
30
  getConfigFiles() { return ['remix.config.js', 'vite.config.js', 'vite.config.ts']; }
31
+ getRequiredPackages() {
32
+ return [
33
+ { name: '@remix-run/react', dev: false },
34
+ { name: '@remix-run/node', dev: false, optional: true },
35
+ ];
36
+ }
23
37
  getRoutePatterns() { return [/\/app\/routes\//, /\/app\/root\.(tsx|jsx)$/]; }
24
38
  getRequiredSystemContracts() { return ['default', 'loader', 'action', 'meta', 'links']; }
25
39
  async isActive(baseDir) {
@@ -36,23 +50,25 @@ export class RemixPlugin extends BasePlugin {
36
50
  export class SvelteKitPlugin extends BasePlugin {
37
51
  get name() { return 'sveltekit'; }
38
52
  getConfigFiles() { return ['svelte.config.js', 'vite.config.ts']; }
53
+ getRequiredPackages() {
54
+ return [
55
+ { name: '@sveltejs/kit', dev: false },
56
+ { name: 'svelte', dev: false },
57
+ ];
58
+ }
39
59
  getRoutePatterns() {
40
60
  return [/\+page\.(svelte|ts|js)$/, /\+page\.server\.(ts|js)$/, /\+layout\.(svelte|ts|js)$/, /\+server\.(ts|js)$/];
41
61
  }
42
62
  getRequiredSystemContracts() { return ['load', 'actions', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH']; }
43
63
  async isActive(baseDir) {
44
- try {
45
- await fs.access(path.join(baseDir, 'svelte.config.js'));
46
- return true;
47
- } catch {
48
- return false;
49
- }
64
+ try { await fs.access(path.join(baseDir, 'svelte.config.js')); return true; } catch { return false; }
50
65
  }
51
66
  }
52
67
 
53
68
  export class AstroPlugin extends BasePlugin {
54
69
  get name() { return 'astro'; }
55
70
  getConfigFiles() { return ['astro.config.mjs', 'astro.config.cjs', 'astro.config.ts']; }
71
+ getRequiredPackages() { return [{ name: 'astro', dev: false }]; }
56
72
  getRoutePatterns() { return [/\/src\/pages\/.*\.astro$/, /\/src\/pages\/.*\.(ts|js)$/]; }
57
73
  getRequiredSystemContracts() { return ['default', 'getStaticPaths']; }
58
74
  async isActive(baseDir) {
@@ -63,63 +79,64 @@ export class AstroPlugin extends BasePlugin {
63
79
  }
64
80
  }
65
81
 
66
- /**
67
- * Vitepress Ecosystem Plugin
68
- */
69
82
  export class VitepressPlugin extends BasePlugin {
70
- get name() {
71
- return 'vitepress';
72
- }
73
-
74
- getConfigFiles() {
75
- return ['package.json'];
76
- }
77
-
83
+ get name() { return 'vitepress'; }
84
+ getConfigFiles() { return ['package.json']; }
85
+ getRequiredPackages() { return [{ name: 'vitepress', dev: true }]; }
78
86
  async isActive(baseDir) {
79
- // Vitepress is active if it's in package.json OR if .vitepress folder exists
80
87
  try {
81
88
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
82
89
  const hasDep = !!(pkgJson.dependencies?.vitepress || pkgJson.devDependencies?.vitepress);
83
-
84
- // Also check for .vitepress folder existence in common locations
85
90
  const possibleDirs = ['.vitepress', 'docs/.vitepress', '.docs/.vitepress'];
86
91
  for (const d of possibleDirs) {
87
- try {
88
- await fs.access(path.join(baseDir, d));
89
- return true;
90
- } catch {}
92
+ try { await fs.access(path.join(baseDir, d)); return true; } catch {}
91
93
  }
92
-
93
94
  return hasDep;
94
- } catch {
95
- return false;
96
- }
95
+ } catch { return false; }
97
96
  }
98
-
99
97
  async onDiscovery({ pkgDir, data, reachableFiles, queue, projectGraph, context }) {
100
98
  const hasVitepressDir = Array.from(projectGraph.keys()).some(f => f.includes('/.vitepress/'));
101
99
  const hasVitepressDep = !!(data.devDependencies?.vitepress || data.dependencies?.vitepress);
102
-
103
100
  if (hasVitepressDir && hasVitepressDep) {
104
- // Legitimate usage: Mark .vitepress files as reachable
105
101
  for (const [filePath, _] of projectGraph.entries()) {
106
102
  if (filePath.includes('/.vitepress/') && !reachableFiles.has(filePath)) {
107
103
  reachableFiles.add(filePath);
108
104
  queue.push(filePath);
109
105
  }
110
106
  }
111
- // Mark vue as used since it's required by vitepress
112
107
  if (!context.consumedRootPackages) context.consumedRootPackages = new Set();
113
108
  context.consumedRootPackages.add('vitepress');
114
109
  context.consumedRootPackages.add('vue');
115
110
  } else if (hasVitepressDir && !hasVitepressDep) {
116
- // Missing dependency case
117
111
  if (!context.unlistedDependencies) context.unlistedDependencies = [];
118
112
  context.unlistedDependencies.push({
119
113
  name: 'vitepress',
120
114
  reason: 'Found .vitepress configuration directory but package is not in package.json'
121
115
  });
122
116
  }
123
- // If hasVitepressDep but !hasVitepressDir, it remains "unused" by default logic
124
117
  }
125
118
  }
119
+
120
+ export class GatsbyPlugin extends BasePlugin {
121
+ get name() { return 'gatsby'; }
122
+ getConfigFiles() { return ['gatsby-config.js', 'gatsby-config.ts', 'gatsby-node.js', 'gatsby-node.ts']; }
123
+ getRequiredPackages() { return [{ name: 'gatsby', dev: false }]; }
124
+ getRoutePatterns() { return [/\/src\/pages\//, /\/src\/templates\//]; }
125
+ getRequiredSystemContracts() { return ['default', 'getServerData', 'config']; }
126
+ async isActive(baseDir) {
127
+ for (const file of this.getConfigFiles()) {
128
+ try { await fs.access(path.join(baseDir, file)); return true; } catch {}
129
+ }
130
+ return false;
131
+ }
132
+ }
133
+
134
+ export class RedwoodPlugin extends BasePlugin {
135
+ get name() { return 'redwood'; }
136
+ getConfigFiles() { return ['redwood.toml']; }
137
+ getRequiredPackages() { return [{ name: '@redwoodjs/core', dev: true }]; }
138
+ async isActive(baseDir) {
139
+ try { await fs.access(path.join(baseDir, 'redwood.toml')); return true; } catch { return false; }
140
+ }
141
+ getRoutePatterns() { return [/\/web\/src\/pages\//, /\/api\/src\/functions\//]; }
142
+ }
@@ -1,159 +1,162 @@
1
1
  /**
2
2
  * ============================================================================
3
- * Modern Frameworks Plugins for entkapp v4.0.0
3
+ * Modern Frameworks Plugins for entkapp v5.0.0
4
4
  * ============================================================================
5
- * Built-in support for React, Vue, Svelte, and Angular.
5
+ * Built-in support for React, Vue, Svelte, Angular, Preact, Solid, Qwik, Lit.
6
+ * v5.0.0: All plugins now implement getRequiredPackages() for dependency detection.
6
7
  */
7
-
8
8
  import { BasePlugin } from '../BasePlugin.js';
9
9
  import fs from 'fs/promises';
10
10
  import path from 'path';
11
11
 
12
- /**
13
- * React Ecosystem Plugin
14
- */
15
12
  export class ReactPlugin extends BasePlugin {
16
- get name() {
17
- return 'react';
18
- }
19
-
20
- getConfigFiles() {
21
- return ['package.json'];
13
+ get name() { return 'react'; }
14
+ getConfigFiles() { return ['package.json']; }
15
+ getRequiredPackages() {
16
+ return [
17
+ { name: 'react', dev: false },
18
+ { name: 'react-dom', dev: false },
19
+ ];
22
20
  }
23
-
24
21
  async isActive(baseDir) {
25
22
  try {
26
23
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
27
24
  return !!(pkgJson.dependencies?.react || pkgJson.devDependencies?.react);
28
- } catch {
29
- return false;
30
- }
31
- }
32
-
33
- getRoutePatterns() {
34
- return [/\.(tsx?|jsx?)$/];
25
+ } catch { return false; }
35
26
  }
36
-
27
+ getRoutePatterns() { return [/\.(tsx?|jsx?)$/]; }
37
28
  getRequiredSystemContracts() {
38
- return ['default', 'Component', 'PureComponent', 'Fragment', 'useEffect', 'useState', 'useContext', 'useReducer', 'useCallback', 'useMemo', 'useRef', 'useImperativeHandle', 'useLayoutEffect', 'useDebugValue'];
29
+ return ['default', 'Component', 'PureComponent', 'Fragment', 'useEffect', 'useState',
30
+ 'useContext', 'useReducer', 'useCallback', 'useMemo', 'useRef',
31
+ 'useImperativeHandle', 'useLayoutEffect', 'useDebugValue'];
39
32
  }
40
-
41
33
  async analyze(node, filePath) {
42
- if (node.explicitImports.has('react')) {
43
- node.isReactComponent = true;
44
- }
45
-
46
- // Detect JSX
47
- if (node.rawCode && (node.rawCode.includes('</') || node.rawCode.includes('/>'))) {
48
- node.hasJSX = true;
49
- }
50
-
51
- // Detect Hooks
34
+ if (node.explicitImports?.has('react')) node.isReactComponent = true;
35
+ if (node.rawCode && (node.rawCode.includes('</') || node.rawCode.includes('/>'))) node.hasJSX = true;
52
36
  const hookMatches = node.rawCode?.match(/use[A-Z]\w+/g) || [];
53
- if (hookMatches.length > 0) {
54
- node.reactHooks = new Set(hookMatches);
55
- }
37
+ if (hookMatches.length > 0) node.reactHooks = new Set(hookMatches);
56
38
  }
57
39
  }
58
40
 
59
- /**
60
- * Vue Ecosystem Plugin
61
- */
62
41
  export class VuePlugin extends BasePlugin {
63
- get name() {
64
- return 'vue';
65
- }
66
-
67
- getConfigFiles() {
68
- return ['package.json', 'vue.config.js', 'vite.config.ts', 'vite.config.js'];
42
+ get name() { return 'vue'; }
43
+ getConfigFiles() { return ['package.json', 'vue.config.js', 'vite.config.ts', 'vite.config.js']; }
44
+ getRequiredPackages() {
45
+ return [{ name: 'vue', dev: false }];
69
46
  }
70
-
71
47
  async isActive(baseDir) {
72
48
  try {
73
49
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
74
50
  return !!(pkgJson.dependencies?.vue || pkgJson.devDependencies?.vue);
75
- } catch {
76
- return false;
77
- }
78
- }
79
-
80
- getRoutePatterns() {
81
- return [/\.vue$/, /\.(tsx?|jsx?)$/];
51
+ } catch { return false; }
82
52
  }
83
-
53
+ getRoutePatterns() { return [/\.vue$/, /\.(tsx?|jsx?)$/]; }
84
54
  async analyze(node, filePath) {
85
55
  if (filePath.endsWith('.vue')) {
86
56
  node.isVueSFC = true;
87
- // Extract template/script/style sections
88
57
  const templateMatch = node.rawCode?.match(/<template>([\s\S]*)<\/template>/);
89
58
  if (templateMatch) node.vueTemplate = templateMatch[1];
90
-
91
59
  const scriptMatch = node.rawCode?.match(/<script(?: setup)?(?: lang=['"]\w+['"])?>([\s\S]*)<\/script>/);
92
60
  if (scriptMatch) node.vueScript = scriptMatch[1];
93
61
  }
94
62
  }
95
63
  }
96
64
 
97
- /**
98
- * Svelte Ecosystem Plugin
99
- */
100
65
  export class SveltePlugin extends BasePlugin {
101
- get name() {
102
- return 'svelte';
103
- }
104
-
105
- getConfigFiles() {
106
- return ['package.json', 'svelte.config.js'];
66
+ get name() { return 'svelte'; }
67
+ getConfigFiles() { return ['package.json', 'svelte.config.js']; }
68
+ getRequiredPackages() {
69
+ return [{ name: 'svelte', dev: false }];
107
70
  }
108
-
109
71
  async isActive(baseDir) {
110
72
  try {
111
73
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
112
74
  return !!(pkgJson.dependencies?.svelte || pkgJson.devDependencies?.svelte);
113
- } catch {
114
- return false;
115
- }
116
- }
117
-
118
- getRoutePatterns() {
119
- return [/\.svelte$/, /\.(tsx?|jsx?)$/];
75
+ } catch { return false; }
120
76
  }
121
-
77
+ getRoutePatterns() { return [/\.svelte$/, /\.(tsx?|jsx?)$/]; }
122
78
  async analyze(node, filePath) {
123
- if (filePath.endsWith('.svelte')) {
124
- node.isSvelteComponent = true;
125
- }
79
+ if (filePath.endsWith('.svelte')) node.isSvelteComponent = true;
126
80
  }
127
81
  }
128
82
 
129
- /**
130
- * Angular Ecosystem Plugin
131
- */
132
83
  export class AngularPlugin extends BasePlugin {
133
- get name() {
134
- return 'angular';
84
+ get name() { return 'angular'; }
85
+ getConfigFiles() { return ['package.json', 'angular.json']; }
86
+ getRequiredPackages() {
87
+ return [
88
+ { name: '@angular/core', dev: false },
89
+ { name: '@angular/common', dev: false },
90
+ { name: '@angular/cli', dev: true, optional: true },
91
+ ];
92
+ }
93
+ async isActive(baseDir) {
94
+ try {
95
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
96
+ return !!(pkgJson.dependencies?.['@angular/core'] || pkgJson.devDependencies?.['@angular/core']);
97
+ } catch { return false; }
98
+ }
99
+ getRoutePatterns() { return [/\.ts$/]; }
100
+ async analyze(node, filePath) {
101
+ if (node.rawCode?.includes('@Component') || node.rawCode?.includes('@Injectable') || node.rawCode?.includes('@NgModule'))
102
+ node.isAngularEntity = true;
135
103
  }
104
+ }
136
105
 
137
- getConfigFiles() {
138
- return ['package.json', 'angular.json'];
106
+ export class PreactPlugin extends BasePlugin {
107
+ get name() { return 'preact'; }
108
+ getConfigFiles() { return ['package.json']; }
109
+ getRequiredPackages() { return [{ name: 'preact', dev: false }]; }
110
+ async isActive(baseDir) {
111
+ try {
112
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
113
+ return !!(pkgJson.dependencies?.preact || pkgJson.devDependencies?.preact);
114
+ } catch { return false; }
139
115
  }
116
+ getRoutePatterns() { return [/\.(tsx?|jsx?)$/]; }
117
+ async analyze(node) {
118
+ if (node.rawCode?.includes('render(') && node.rawCode?.includes('preact')) node.isEntry = true;
119
+ }
120
+ }
140
121
 
122
+ export class SolidPlugin extends BasePlugin {
123
+ get name() { return 'solid'; }
124
+ getConfigFiles() { return ['package.json', 'vite.config.ts', 'vite.config.js']; }
125
+ getRequiredPackages() { return [{ name: 'solid-js', dev: false }]; }
141
126
  async isActive(baseDir) {
142
127
  try {
143
128
  const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
144
- return !!(pkgJson.dependencies?.['@angular/core'] || pkgJson.devDependencies?.['@angular/core']);
145
- } catch {
146
- return false;
147
- }
129
+ return !!(pkgJson.dependencies?.['solid-js'] || pkgJson.devDependencies?.['solid-js']);
130
+ } catch { return false; }
148
131
  }
132
+ getRoutePatterns() { return [/\.(tsx?|jsx?)$/]; }
133
+ getRequiredSystemContracts() { return ['default', 'createSignal', 'createEffect', 'createMemo', 'createResource']; }
134
+ }
149
135
 
150
- getRoutePatterns() {
151
- return [/\.ts$/];
136
+ export class QwikPlugin extends BasePlugin {
137
+ get name() { return 'qwik'; }
138
+ getConfigFiles() { return ['package.json', 'vite.config.ts']; }
139
+ getRequiredPackages() { return [{ name: '@builder.io/qwik', dev: false }]; }
140
+ async isActive(baseDir) {
141
+ try {
142
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
143
+ return !!(pkgJson.dependencies?.['@builder.io/qwik'] || pkgJson.devDependencies?.['@builder.io/qwik']);
144
+ } catch { return false; }
152
145
  }
146
+ getRoutePatterns() { return [/\.(tsx?|jsx?)$/]; }
147
+ getRequiredSystemContracts() { return ['default', 'component$', 'useSignal', 'useStore', 'useTask$']; }
148
+ }
153
149
 
154
- async analyze(node, filePath) {
155
- if (node.rawCode?.includes('@Component') || node.rawCode?.includes('@Injectable') || node.rawCode?.includes('@NgModule')) {
156
- node.isAngularEntity = true;
157
- }
150
+ export class LitPlugin extends BasePlugin {
151
+ get name() { return 'lit'; }
152
+ getConfigFiles() { return ['package.json']; }
153
+ getRequiredPackages() { return [{ name: 'lit', dev: false }]; }
154
+ async isActive(baseDir) {
155
+ try {
156
+ const pkgJson = JSON.parse(await fs.readFile(path.join(baseDir, 'package.json'), 'utf8'));
157
+ return !!(pkgJson.dependencies?.lit || pkgJson.devDependencies?.lit);
158
+ } catch { return false; }
158
159
  }
160
+ getRoutePatterns() { return [/\.(ts|js)$/]; }
161
+ getRequiredSystemContracts() { return ['default', 'LitElement', 'html', 'css']; }
159
162
  }