entkapp 5.1.0 → 5.2.1
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.md +63 -20
- package/index.js +38 -2241
- package/package.json +1 -1
- package/src/EngineContext.js +20 -6
- package/src/Initializer.js +82 -0
- package/src/analyzers/CodeSmellAnalyzer.js +106 -0
- package/src/ast/OxcAnalyzer.js +33 -415
- package/src/index.js +73 -4
- package/src/performance/WorkerTaskRunner.js +7 -7
- package/src/plugins/BasePlugin.js +171 -2
- package/src/plugins/PluginRegistry.js +193 -81
- package/src/plugins/ecosystems/BackendServices.js +168 -32
- package/src/plugins/ecosystems/GenericPlugins.js +51 -34
- package/src/plugins/ecosystems/ModernFrameworks.js +97 -94
- package/src/plugins/ecosystems/MorePlugins.js +429 -51
- package/src/plugins/ecosystems/NewPlugins.js +526 -0
- package/src/plugins/ecosystems/NextJsPlugin.js +18 -6
- package/src/plugins/ecosystems/PluginLoader.js +190 -17
- package/src/plugins/ecosystems/TypeScriptPlugin.js +10 -10
- package/src/plugins/ecosystems/UltimateBundle.js +168 -0
- package/src/resolution/BuildOrchestrator.js +46 -0
- package/src/resolution/ConfigGenerator.js +83 -0
- package/src/resolution/DependencyFixer.js +88 -0
- package/src/resolution/EntryPointDetector.js +4 -4
- package/src/resolution/GraphAnalyzer.js +80 -0
- package/src/resolution/MigrationAnalyzer.js +60 -0
- package/src/resolution/PathMapper.js +5 -5
|
@@ -1,61 +1,197 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ============================================================================
|
|
3
|
-
* Backend Services Plugins for entkapp
|
|
3
|
+
* Backend Services Plugins for entkapp v5.0.0
|
|
4
4
|
* ============================================================================
|
|
5
|
-
* Built-in support for GraphQL, REST APIs, and
|
|
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
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
52
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3
|
+
* Modern Frameworks Plugins for entkapp v5.0.0
|
|
4
4
|
* ============================================================================
|
|
5
|
-
* Built-in support for React, Vue, Svelte,
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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',
|
|
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
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
138
|
-
|
|
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?.['
|
|
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
|
-
|
|
151
|
-
|
|
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
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
}
|