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.
- package/README.md +63 -20
- package/bin/cli.js +2 -2
- package/index.js +38 -2241
- package/package.json +1 -1
- package/src/EngineContext.js +1 -1
- package/src/Initializer.js +82 -0
- package/src/analyzers/CodeSmellAnalyzer.js +106 -0
- package/src/ast/ASTAnalyzer.js +29 -14
- package/src/ast/BarrelParser.js +22 -20
- package/src/ast/OxcAnalyzer.js +33 -409
- package/src/index.js +78 -8
- 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/CircularDetector.js +64 -25
- package/src/resolution/ConfigGenerator.js +83 -0
- package/src/resolution/DepencyResolver.js +12 -1
- 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 +47 -3
- package/src/resolution/WorkSpaceGraph.js +4 -1
- package/docs.zip +0 -0
|
@@ -64,7 +64,7 @@ async function runTask() {
|
|
|
64
64
|
success = await oxcAnalyzer.parseFile(filePath, content, node);
|
|
65
65
|
}
|
|
66
66
|
} catch (oxcError) {
|
|
67
|
-
success = false; //
|
|
67
|
+
success = false; // Catch error to force TS fallback
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// UPGRADE: Improved fallback logic for CommonJS files in worker threads
|
|
@@ -78,8 +78,8 @@ async function runTask() {
|
|
|
78
78
|
// 3. OXC found no dependencies but file has CommonJS keywords
|
|
79
79
|
if (!success || (oxcFailedToFindDependencies && (hasImportExportKeywords || hasCommonJSKeywords))) {
|
|
80
80
|
try {
|
|
81
|
-
// CRITICAL FIX: Scope
|
|
82
|
-
//
|
|
81
|
+
// CRITICAL FIX: Scope reset for the TS parser in isolated thread context
|
|
82
|
+
// Prevents incomplete scope chains from the previous file from leading to 'children of undefined'
|
|
83
83
|
astAnalyzer.currentScope = { symbols: new Map(), parent: null, children: [] };
|
|
84
84
|
astAnalyzer.scopeStack = [astAnalyzer.currentScope];
|
|
85
85
|
astAnalyzer.scopeCounter = 0;
|
|
@@ -87,14 +87,14 @@ async function runTask() {
|
|
|
87
87
|
await astAnalyzer.parseFile(filePath, content, node);
|
|
88
88
|
} catch (tsError) {
|
|
89
89
|
if (contextOptions.verbose) {
|
|
90
|
-
console.error(`[Worker-Fallback-Error] TS
|
|
90
|
+
console.error(`[Worker-Fallback-Error] TS parser failed at ${filePath}: ${tsError.message}`);
|
|
91
91
|
}
|
|
92
92
|
results.push(null);
|
|
93
93
|
continue;
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
//
|
|
97
|
+
// Safe serialization: Prevents crashes if internalExports or symbolSourceLocations are no longer Maps
|
|
98
98
|
const serializedExports = node.internalExports instanceof Map
|
|
99
99
|
? Object.fromEntries(node.internalExports)
|
|
100
100
|
: {};
|
|
@@ -129,9 +129,9 @@ async function runTask() {
|
|
|
129
129
|
});
|
|
130
130
|
} catch (err) {
|
|
131
131
|
if (contextOptions.verbose) {
|
|
132
|
-
console.error(`[Worker-Loop-Exception]
|
|
132
|
+
console.error(`[Worker-Loop-Exception] Error in file ${filePath}: ${err.message}`);
|
|
133
133
|
}
|
|
134
|
-
results.push(null); //
|
|
134
|
+
results.push(null); // Skip module, keep thread alive
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
|
|
@@ -3,8 +3,9 @@ import path from 'path';
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Base class for all entkapp plugins.
|
|
6
|
-
* Defines the contract for ecosystem detection
|
|
7
|
-
*
|
|
6
|
+
* Defines the contract for ecosystem detection, entry point mapping,
|
|
7
|
+
* and missing dependency / devDependency detection.
|
|
8
|
+
* Version 5.0.0: Added getMissingDependencies(), getOrphanedConfigs(), runDependencyDiagnostics().
|
|
8
9
|
*/
|
|
9
10
|
export class BasePlugin {
|
|
10
11
|
constructor(context) {
|
|
@@ -40,6 +41,174 @@ export class BasePlugin {
|
|
|
40
41
|
return ['default'];
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Returns the npm package names required for this plugin to work.
|
|
46
|
+
* Each entry is either a string or an object:
|
|
47
|
+
* { name: string, dev?: boolean, optional?: boolean }
|
|
48
|
+
* - dev: true => expected in devDependencies
|
|
49
|
+
* - dev: false => expected in dependencies
|
|
50
|
+
* - optional => only warn, not error
|
|
51
|
+
*
|
|
52
|
+
* Override in subclasses to enable automatic dependency checking.
|
|
53
|
+
* @returns {Array<string | {name: string, dev?: boolean, optional?: boolean}>}
|
|
54
|
+
*/
|
|
55
|
+
getRequiredPackages() {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Version 5.0.0: Checks whether all required packages are present in package.json.
|
|
61
|
+
* Returns an array of diagnostic objects describing missing or misplaced dependencies.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} baseDir - The project root directory
|
|
64
|
+
* @returns {Promise<Array>}
|
|
65
|
+
*/
|
|
66
|
+
async getMissingDependencies(baseDir) {
|
|
67
|
+
const diagnostics = [];
|
|
68
|
+
const requiredPackages = this.getRequiredPackages();
|
|
69
|
+
if (requiredPackages.length === 0) return diagnostics;
|
|
70
|
+
|
|
71
|
+
let pkg = null;
|
|
72
|
+
try {
|
|
73
|
+
const raw = await fs.readFile(path.join(baseDir, 'package.json'), 'utf8');
|
|
74
|
+
pkg = JSON.parse(raw);
|
|
75
|
+
} catch {
|
|
76
|
+
return diagnostics;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const deps = pkg.dependencies || {};
|
|
80
|
+
const devDeps = pkg.devDependencies || {};
|
|
81
|
+
|
|
82
|
+
for (const entry of requiredPackages) {
|
|
83
|
+
const pkgName = typeof entry === 'string' ? entry : entry.name;
|
|
84
|
+
const isDev = typeof entry === 'object' ? entry.dev : undefined;
|
|
85
|
+
const isOptional = typeof entry === 'object' ? (entry.optional ?? false) : false;
|
|
86
|
+
|
|
87
|
+
const inDeps = pkgName in deps;
|
|
88
|
+
const inDevDeps = pkgName in devDeps;
|
|
89
|
+
const found = inDeps ? 'dependencies' : inDevDeps ? 'devDependencies' : null;
|
|
90
|
+
|
|
91
|
+
if (!found) {
|
|
92
|
+
let triggerFile = null;
|
|
93
|
+
for (const cfgFile of this.getConfigFiles()) {
|
|
94
|
+
try {
|
|
95
|
+
await fs.access(path.join(baseDir, cfgFile));
|
|
96
|
+
triggerFile = cfgFile;
|
|
97
|
+
break;
|
|
98
|
+
} catch { /* not found */ }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const expectedIn = isDev === true ? 'devDependencies' : isDev === false ? 'dependencies' : 'either';
|
|
102
|
+
const severity = isOptional ? 'warning' : 'error';
|
|
103
|
+
|
|
104
|
+
let message = `Plugin "${this.name}": package "${pkgName}" is not installed`;
|
|
105
|
+
if (triggerFile) {
|
|
106
|
+
message += ` (config file "${triggerFile}" was found)`;
|
|
107
|
+
}
|
|
108
|
+
if (isDev === true) {
|
|
109
|
+
message += ` — expected in devDependencies`;
|
|
110
|
+
} else if (isDev === false) {
|
|
111
|
+
message += ` — expected in dependencies`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
diagnostics.push({
|
|
115
|
+
plugin: this.name,
|
|
116
|
+
package: pkgName,
|
|
117
|
+
expectedIn,
|
|
118
|
+
foundIn: null,
|
|
119
|
+
configFile: triggerFile,
|
|
120
|
+
severity,
|
|
121
|
+
message,
|
|
122
|
+
});
|
|
123
|
+
} else if (isDev === true && found === 'dependencies') {
|
|
124
|
+
diagnostics.push({
|
|
125
|
+
plugin: this.name,
|
|
126
|
+
package: pkgName,
|
|
127
|
+
expectedIn: 'devDependencies',
|
|
128
|
+
foundIn: 'dependencies',
|
|
129
|
+
configFile: null,
|
|
130
|
+
severity: 'warning',
|
|
131
|
+
message: `Plugin "${this.name}": package "${pkgName}" is in "dependencies" but should be in "devDependencies"`,
|
|
132
|
+
});
|
|
133
|
+
} else if (isDev === false && found === 'devDependencies') {
|
|
134
|
+
diagnostics.push({
|
|
135
|
+
plugin: this.name,
|
|
136
|
+
package: pkgName,
|
|
137
|
+
expectedIn: 'dependencies',
|
|
138
|
+
foundIn: 'devDependencies',
|
|
139
|
+
configFile: null,
|
|
140
|
+
severity: 'warning',
|
|
141
|
+
message: `Plugin "${this.name}": package "${pkgName}" is in "devDependencies" but should be in "dependencies"`,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return diagnostics;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Version 5.0.0: Checks for config files that exist without the corresponding package.
|
|
151
|
+
* Detects "orphaned" configs – e.g. .prettierrc exists but prettier is not installed.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} baseDir
|
|
154
|
+
* @returns {Promise<Array>}
|
|
155
|
+
*/
|
|
156
|
+
async getOrphanedConfigs(baseDir) {
|
|
157
|
+
const diagnostics = [];
|
|
158
|
+
const requiredPackages = this.getRequiredPackages();
|
|
159
|
+
if (requiredPackages.length === 0) return diagnostics;
|
|
160
|
+
|
|
161
|
+
let pkg = null;
|
|
162
|
+
try {
|
|
163
|
+
const raw = await fs.readFile(path.join(baseDir, 'package.json'), 'utf8');
|
|
164
|
+
pkg = JSON.parse(raw);
|
|
165
|
+
} catch {
|
|
166
|
+
return diagnostics;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
170
|
+
|
|
171
|
+
const anyPackagePresent = requiredPackages.some(entry => {
|
|
172
|
+
const pkgName = typeof entry === 'string' ? entry : entry.name;
|
|
173
|
+
return pkgName in allDeps;
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
if (!anyPackagePresent) {
|
|
177
|
+
for (const cfgFile of this.getConfigFiles()) {
|
|
178
|
+
try {
|
|
179
|
+
await fs.access(path.join(baseDir, cfgFile));
|
|
180
|
+
const missingPkgs = requiredPackages
|
|
181
|
+
.map(e => typeof e === 'string' ? e : e.name)
|
|
182
|
+
.filter(n => !(n in allDeps));
|
|
183
|
+
|
|
184
|
+
diagnostics.push({
|
|
185
|
+
plugin: this.name,
|
|
186
|
+
configFile: cfgFile,
|
|
187
|
+
severity: 'error',
|
|
188
|
+
message: `Plugin "${this.name}": config file "${cfgFile}" exists but required package(s) [${missingPkgs.join(', ')}] are not installed`,
|
|
189
|
+
});
|
|
190
|
+
} catch { /* file not found */ }
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return diagnostics;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Version 5.0.0: Run all dependency diagnostics (missing + orphaned).
|
|
199
|
+
* Convenience method combining getMissingDependencies and getOrphanedConfigs.
|
|
200
|
+
*
|
|
201
|
+
* @param {string} baseDir
|
|
202
|
+
* @returns {Promise<Array>}
|
|
203
|
+
*/
|
|
204
|
+
async runDependencyDiagnostics(baseDir) {
|
|
205
|
+
const [missing, orphaned] = await Promise.all([
|
|
206
|
+
this.getMissingDependencies(baseDir),
|
|
207
|
+
this.getOrphanedConfigs(baseDir),
|
|
208
|
+
]);
|
|
209
|
+
return [...missing, ...orphaned];
|
|
210
|
+
}
|
|
211
|
+
|
|
43
212
|
/**
|
|
44
213
|
* Version 3.2.0: Dynamic getter for custom plugin properties.
|
|
45
214
|
* @param {string} key - The property key to retrieve
|
|
@@ -2,11 +2,13 @@ import { loadAdditionalPlugins } from "./ecosystems/PluginLoader.js";
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import fs from 'fs/promises';
|
|
4
4
|
import { pathToFileURL } from 'url';
|
|
5
|
-
import { execa } from 'execa';
|
|
6
5
|
|
|
7
6
|
/**
|
|
7
|
+
* ============================================================================
|
|
8
|
+
* Plugin Registry for entkapp v5.0.0
|
|
9
|
+
* ============================================================================
|
|
8
10
|
* Advanced Plugin Registry supporting Builtin and Custom plugins.
|
|
9
|
-
*
|
|
11
|
+
* v5.0.0: Added runAllDependencyDiagnostics() for project-wide dependency checks.
|
|
10
12
|
*/
|
|
11
13
|
export class PluginRegistry {
|
|
12
14
|
constructor(context) {
|
|
@@ -26,69 +28,118 @@ export class PluginRegistry {
|
|
|
26
28
|
useCustomPlugins: true,
|
|
27
29
|
};
|
|
28
30
|
}
|
|
29
|
-
|
|
30
|
-
if (this.config.useBuiltinPlugins) {
|
|
31
|
+
if (this.config.useBuiltinPlugins !== false) {
|
|
31
32
|
await this.loadBuiltinPlugins();
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
-
if (this.config.useCustomPlugins) {
|
|
34
|
+
if (this.config.useCustomPlugins !== false) {
|
|
35
35
|
await this.loadCustomPlugins(projectRoot);
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
async loadBuiltinPlugins() {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const { GraphQLPlugin, DatabasePlugin } = await import('./ecosystems/
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
40
|
+
// Delegate to PluginLoader which registers all built-in plugins
|
|
41
|
+
loadAdditionalPlugins(this);
|
|
42
|
+
|
|
43
|
+
// Also load TypeScript and Next.js plugins (primary framework plugins)
|
|
44
|
+
const { ReactPlugin, VuePlugin, SveltePlugin, AngularPlugin, PreactPlugin, SolidPlugin, QwikPlugin, LitPlugin, NuxtPlugin, RemixPlugin, SvelteKitPlugin, AstroPlugin, VitepressPlugin, GatsbyPlugin, RedwoodPlugin, NextJsPlugin, TypeScriptPlugin, ExpressPlugin, FastifyPlugin, NestJsPlugin, HonoPlugin, KoaPlugin, ElysiaPlugin, GraphQLPlugin, ApolloPlugin, TRPCPlugin, DatabasePlugin, PrismaPlugin, DrizzlePlugin, MongoosePlugin, SupabasePlugin, FirebasePlugin, ClerkPlugin, ReduxPlugin, ZustandPlugin, JotaiPlugin, RecoilPlugin, MobXPlugin, PiniaPlugin, TanStackQueryPlugin, ReactRouterPlugin, TanStackRouterPlugin, VueRouterPlugin, AntdPlugin, MuiPlugin, ShadcnPlugin, RadixUIPlugin, ChakraUIPlugin, FramerMotionPlugin, GSAPPlugin, ZodPlugin, YupPlugin, ValibotPlugin, I18nextPlugin, VueI18nPlugin, SentryPlugin, OpenTelemetryPlugin, SocketIoPlugin, TailwindPlugin, PostcssPlugin, UnoCSSPlugin, StylelintPlugin, EslintPlugin, PrettierPlugin, BiomePlugin, OxlintPlugin, HuskyPlugin, LintStagedPlugin, CommitlintPlugin, ChangesetPlugin, BabelPlugin, SWCPlugin, VitePlugin, EsbuildPlugin, RollupPlugin, WebpackPlugin, ParcelPlugin, TurboPlugin, NxPlugin, JestPlugin, VitestPlugin, PlaywrightPlugin, CypressPlugin, StorybookPlugin, MswPlugin, GithubActionsPlugin, DockerPlugin, TerraformPlugin, EditorConfigPlugin, NvmPlugin, VoltaPlugin, DotenvPlugin, PnpmPlugin, YarnPlugin, BunPlugin, SwiperPlugin, QuillPlugin, EnvelopPlugin } = await import('./ecosystems/UltimateBundle.js');
|
|
45
|
+
this.register(new ReactPlugin(this.context));
|
|
46
|
+
this.register(new VuePlugin(this.context));
|
|
47
|
+
this.register(new SveltePlugin(this.context));
|
|
48
|
+
this.register(new AngularPlugin(this.context));
|
|
49
|
+
this.register(new PreactPlugin(this.context));
|
|
50
|
+
this.register(new SolidPlugin(this.context));
|
|
51
|
+
this.register(new QwikPlugin(this.context));
|
|
52
|
+
this.register(new LitPlugin(this.context));
|
|
53
|
+
this.register(new NuxtPlugin(this.context));
|
|
54
|
+
this.register(new RemixPlugin(this.context));
|
|
55
|
+
this.register(new SvelteKitPlugin(this.context));
|
|
56
|
+
this.register(new AstroPlugin(this.context));
|
|
57
|
+
this.register(new VitepressPlugin(this.context));
|
|
58
|
+
this.register(new GatsbyPlugin(this.context));
|
|
59
|
+
this.register(new RedwoodPlugin(this.context));
|
|
60
|
+
this.register(new NextJsPlugin(this.context));
|
|
61
|
+
this.register(new TypeScriptPlugin(this.context));
|
|
62
|
+
this.register(new ExpressPlugin(this.context));
|
|
63
|
+
this.register(new FastifyPlugin(this.context));
|
|
64
|
+
this.register(new NestJsPlugin(this.context));
|
|
65
|
+
this.register(new HonoPlugin(this.context));
|
|
66
|
+
this.register(new KoaPlugin(this.context));
|
|
67
|
+
this.register(new ElysiaPlugin(this.context));
|
|
68
|
+
this.register(new GraphQLPlugin(this.context));
|
|
69
|
+
this.register(new ApolloPlugin(this.context));
|
|
70
|
+
this.register(new TRPCPlugin(this.context));
|
|
71
|
+
this.register(new DatabasePlugin(this.context));
|
|
72
|
+
this.register(new PrismaPlugin(this.context));
|
|
73
|
+
this.register(new DrizzlePlugin(this.context));
|
|
74
|
+
this.register(new MongoosePlugin(this.context));
|
|
75
|
+
this.register(new SupabasePlugin(this.context));
|
|
76
|
+
this.register(new FirebasePlugin(this.context));
|
|
77
|
+
this.register(new ClerkPlugin(this.context));
|
|
78
|
+
this.register(new ReduxPlugin(this.context));
|
|
79
|
+
this.register(new ZustandPlugin(this.context));
|
|
80
|
+
this.register(new JotaiPlugin(this.context));
|
|
81
|
+
this.register(new RecoilPlugin(this.context));
|
|
82
|
+
this.register(new MobXPlugin(this.context));
|
|
83
|
+
this.register(new PiniaPlugin(this.context));
|
|
84
|
+
this.register(new TanStackQueryPlugin(this.context));
|
|
85
|
+
this.register(new ReactRouterPlugin(this.context));
|
|
86
|
+
this.register(new TanStackRouterPlugin(this.context));
|
|
87
|
+
this.register(new VueRouterPlugin(this.context));
|
|
88
|
+
this.register(new AntdPlugin(this.context));
|
|
89
|
+
this.register(new MuiPlugin(this.context));
|
|
90
|
+
this.register(new ShadcnPlugin(this.context));
|
|
91
|
+
this.register(new RadixUIPlugin(this.context));
|
|
92
|
+
this.register(new ChakraUIPlugin(this.context));
|
|
93
|
+
this.register(new FramerMotionPlugin(this.context));
|
|
94
|
+
this.register(new GSAPPlugin(this.context));
|
|
95
|
+
this.register(new ZodPlugin(this.context));
|
|
96
|
+
this.register(new YupPlugin(this.context));
|
|
97
|
+
this.register(new ValibotPlugin(this.context));
|
|
98
|
+
this.register(new I18nextPlugin(this.context));
|
|
99
|
+
this.register(new VueI18nPlugin(this.context));
|
|
100
|
+
this.register(new SentryPlugin(this.context));
|
|
101
|
+
this.register(new OpenTelemetryPlugin(this.context));
|
|
102
|
+
this.register(new SocketIoPlugin(this.context));
|
|
103
|
+
this.register(new TailwindPlugin(this.context));
|
|
104
|
+
this.register(new PostcssPlugin(this.context));
|
|
105
|
+
this.register(new UnoCSSPlugin(this.context));
|
|
106
|
+
this.register(new StylelintPlugin(this.context));
|
|
107
|
+
this.register(new EslintPlugin(this.context));
|
|
108
|
+
this.register(new PrettierPlugin(this.context));
|
|
109
|
+
this.register(new BiomePlugin(this.context));
|
|
110
|
+
this.register(new OxlintPlugin(this.context));
|
|
111
|
+
this.register(new HuskyPlugin(this.context));
|
|
112
|
+
this.register(new LintStagedPlugin(this.context));
|
|
113
|
+
this.register(new CommitlintPlugin(this.context));
|
|
114
|
+
this.register(new ChangesetPlugin(this.context));
|
|
115
|
+
this.register(new BabelPlugin(this.context));
|
|
116
|
+
this.register(new SWCPlugin(this.context));
|
|
117
|
+
this.register(new VitePlugin(this.context));
|
|
118
|
+
this.register(new EsbuildPlugin(this.context));
|
|
119
|
+
this.register(new RollupPlugin(this.context));
|
|
120
|
+
this.register(new WebpackPlugin(this.context));
|
|
121
|
+
this.register(new ParcelPlugin(this.context));
|
|
122
|
+
this.register(new TurboPlugin(this.context));
|
|
123
|
+
this.register(new NxPlugin(this.context));
|
|
124
|
+
this.register(new JestPlugin(this.context));
|
|
125
|
+
this.register(new VitestPlugin(this.context));
|
|
126
|
+
this.register(new PlaywrightPlugin(this.context));
|
|
127
|
+
this.register(new CypressPlugin(this.context));
|
|
128
|
+
this.register(new StorybookPlugin(this.context));
|
|
129
|
+
this.register(new MswPlugin(this.context));
|
|
130
|
+
this.register(new GithubActionsPlugin(this.context));
|
|
131
|
+
this.register(new DockerPlugin(this.context));
|
|
132
|
+
this.register(new TerraformPlugin(this.context));
|
|
133
|
+
this.register(new EditorConfigPlugin(this.context));
|
|
134
|
+
this.register(new NvmPlugin(this.context));
|
|
135
|
+
this.register(new VoltaPlugin(this.context));
|
|
136
|
+
this.register(new DotenvPlugin(this.context));
|
|
137
|
+
this.register(new PnpmPlugin(this.context));
|
|
138
|
+
this.register(new YarnPlugin(this.context));
|
|
139
|
+
this.register(new BunPlugin(this.context));
|
|
140
|
+
this.register(new SwiperPlugin(this.context));
|
|
141
|
+
this.register(new QuillPlugin(this.context));
|
|
142
|
+
this.register(new EnvelopPlugin(this.context));
|
|
92
143
|
}
|
|
93
144
|
|
|
94
145
|
async loadCustomPlugins(projectRoot) {
|
|
@@ -97,20 +148,17 @@ export class PluginRegistry {
|
|
|
97
148
|
const entries = await fs.readdir(pluginsDir, { withFileTypes: true });
|
|
98
149
|
for (const entry of entries) {
|
|
99
150
|
let pluginPath = path.join(pluginsDir, entry.name);
|
|
100
|
-
|
|
101
|
-
// --- NEW: Folder-based plugin support ---
|
|
151
|
+
// Folder-based plugin support
|
|
102
152
|
if (entry.isDirectory()) {
|
|
103
|
-
// Look for index.ts or index.js in the folder
|
|
104
153
|
const files = await fs.readdir(pluginPath);
|
|
105
154
|
if (files.includes('index.ts')) {
|
|
106
155
|
pluginPath = path.join(pluginPath, 'index.ts');
|
|
107
156
|
} else if (files.includes('index.js')) {
|
|
108
157
|
pluginPath = path.join(pluginPath, 'index.js');
|
|
109
158
|
} else {
|
|
110
|
-
continue;
|
|
159
|
+
continue;
|
|
111
160
|
}
|
|
112
161
|
}
|
|
113
|
-
|
|
114
162
|
if (pluginPath.endsWith('.ts')) {
|
|
115
163
|
await this.loadTypeScriptPlugin(pluginPath);
|
|
116
164
|
} else if (pluginPath.endsWith('.js') || pluginPath.endsWith('.mjs')) {
|
|
@@ -134,27 +182,11 @@ export class PluginRegistry {
|
|
|
134
182
|
}
|
|
135
183
|
|
|
136
184
|
async loadTypeScriptPlugin(pluginPath) {
|
|
137
|
-
// --- TypeScript Plugin Support ---
|
|
138
|
-
// Transpile TS to JS on the fly using esbuild or similar if available,
|
|
139
|
-
// or use a simple wrapper that uses ts-node/register if we were in that env.
|
|
140
|
-
// For this sandbox, we'll simulate a "wrap it" approach by using a temporary JS file.
|
|
141
185
|
try {
|
|
142
|
-
|
|
143
|
-
// We use a simple trick: if we have oxc or esbuild, we could transpile.
|
|
144
|
-
// For now, let's assume we can use a basic transpilation or just inform the user.
|
|
145
|
-
// In a real scenario, we'd use `tsx` or `esbuild` to run this.
|
|
146
|
-
if (this.context.verbose) {
|
|
186
|
+
if (this.context?.verbose) {
|
|
147
187
|
console.log(`[PluginRegistry] Transpiling TS plugin: ${pluginPath}`);
|
|
148
188
|
}
|
|
149
|
-
|
|
150
|
-
// For the sake of "wrapping it", we'll use a dynamic loader if possible.
|
|
151
|
-
// In this implementation, we'll just try to import it if the runtime supports it (like node with --loader ts-node/esm)
|
|
152
|
-
// But since we want it to "just work", let's implement a more robust loading logic.
|
|
153
|
-
|
|
154
|
-
// Implementation detail: we could use `esbuild` to bundle it to a string and then import.
|
|
155
|
-
// Since we don't want to add too many dependencies, we'll just log and try a standard import
|
|
156
|
-
// which might fail without a loader, but it's the right direction.
|
|
157
|
-
await this.loadJavaScriptPlugin(pluginPath);
|
|
189
|
+
await this.loadJavaScriptPlugin(pluginPath);
|
|
158
190
|
} catch (e) {
|
|
159
191
|
console.error(`[PluginRegistry] Failed to load TS plugin ${pluginPath}:`, e);
|
|
160
192
|
}
|
|
@@ -183,4 +215,84 @@ export class PluginRegistry {
|
|
|
183
215
|
}
|
|
184
216
|
return active;
|
|
185
217
|
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Version 5.0.0: Run dependency diagnostics across all registered plugins.
|
|
221
|
+
* Returns a consolidated list of missing/misplaced dependencies and orphaned configs.
|
|
222
|
+
*
|
|
223
|
+
* @param {string} baseDir - The project root directory to check
|
|
224
|
+
* @returns {Promise<Array<{
|
|
225
|
+
* plugin: string,
|
|
226
|
+
* severity: 'error' | 'warning',
|
|
227
|
+
* message: string,
|
|
228
|
+
* package?: string,
|
|
229
|
+
* configFile?: string,
|
|
230
|
+
* expectedIn?: string,
|
|
231
|
+
* foundIn?: string | null
|
|
232
|
+
* }>>}
|
|
233
|
+
*/
|
|
234
|
+
async runAllDependencyDiagnostics(baseDir) {
|
|
235
|
+
const allDiagnostics = [];
|
|
236
|
+
const seenMessages = new Set();
|
|
237
|
+
|
|
238
|
+
for (const plugin of this.plugins.values()) {
|
|
239
|
+
if (typeof plugin.runDependencyDiagnostics === 'function') {
|
|
240
|
+
try {
|
|
241
|
+
const diagnostics = await plugin.runDependencyDiagnostics(baseDir);
|
|
242
|
+
for (const d of diagnostics) {
|
|
243
|
+
// Deduplicate by message to avoid noise from overlapping plugins
|
|
244
|
+
if (!seenMessages.has(d.message)) {
|
|
245
|
+
seenMessages.add(d.message);
|
|
246
|
+
allDiagnostics.push(d);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
} catch (e) {
|
|
250
|
+
if (this.context?.verbose) {
|
|
251
|
+
console.warn(`[PluginRegistry] Dependency diagnostic failed for plugin "${plugin.name}":`, e.message);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Sort: errors first, then warnings; then alphabetically by plugin name
|
|
258
|
+
return allDiagnostics.sort((a, b) => {
|
|
259
|
+
if (a.severity !== b.severity) return a.severity === 'error' ? -1 : 1;
|
|
260
|
+
return a.plugin.localeCompare(b.plugin);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Version 5.0.0: Format dependency diagnostics for console output.
|
|
266
|
+
* @param {Array} diagnostics - Result of runAllDependencyDiagnostics()
|
|
267
|
+
* @returns {string} Formatted string ready for console output
|
|
268
|
+
*/
|
|
269
|
+
formatDependencyDiagnostics(diagnostics) {
|
|
270
|
+
if (diagnostics.length === 0) {
|
|
271
|
+
return '[entkapp] No missing dependencies detected.';
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const errors = diagnostics.filter(d => d.severity === 'error');
|
|
275
|
+
const warnings = diagnostics.filter(d => d.severity === 'warning');
|
|
276
|
+
|
|
277
|
+
const lines = [];
|
|
278
|
+
lines.push(`[entkapp] Dependency Diagnostics: ${errors.length} error(s), ${warnings.length} warning(s)`);
|
|
279
|
+
lines.push('');
|
|
280
|
+
|
|
281
|
+
if (errors.length > 0) {
|
|
282
|
+
lines.push(' Errors:');
|
|
283
|
+
for (const d of errors) {
|
|
284
|
+
lines.push(` [ERROR] ${d.message}`);
|
|
285
|
+
}
|
|
286
|
+
lines.push('');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (warnings.length > 0) {
|
|
290
|
+
lines.push(' Warnings:');
|
|
291
|
+
for (const d of warnings) {
|
|
292
|
+
lines.push(` [WARN] ${d.message}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return lines.join('\n');
|
|
297
|
+
}
|
|
186
298
|
}
|