entkapp 4.1.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 (46) hide show
  1. package/.scaffold-ignore +22 -0
  2. package/LICENSE +211 -0
  3. package/NOTICE +13 -0
  4. package/README.md +33 -0
  5. package/bin/cli.js +174 -0
  6. package/entkapp/config.json +42 -0
  7. package/entkapp/plugins/README.md +19 -0
  8. package/index.js +2254 -0
  9. package/logo.png +0 -0
  10. package/package.json +96 -0
  11. package/src/EngineContext.js +185 -0
  12. package/src/api/HeadlessAPI.js +376 -0
  13. package/src/api/PluginSDK.js +299 -0
  14. package/src/ast/ASTAnalyzer.js +492 -0
  15. package/src/ast/BarrelParser.js +221 -0
  16. package/src/ast/DeadCodeDetector.js +73 -0
  17. package/src/ast/MagicDetector.js +203 -0
  18. package/src/ast/OxcAnalyzer.js +337 -0
  19. package/src/ast/SecretScanner.js +304 -0
  20. package/src/healing/GitSandbox.js +82 -0
  21. package/src/healing/SelfHealer.js +52 -0
  22. package/src/index.js +723 -0
  23. package/src/performance/GraphCache.js +87 -0
  24. package/src/performance/SupplyChainGuard.js +92 -0
  25. package/src/performance/WorkerPool.js +109 -0
  26. package/src/plugins/BasePlugin.js +71 -0
  27. package/src/plugins/KnipAdapter.js +106 -0
  28. package/src/plugins/PluginRegistry.js +197 -0
  29. package/src/plugins/ecosystems/BackendServices.js +61 -0
  30. package/src/plugins/ecosystems/GenericPlugins.js +64 -0
  31. package/src/plugins/ecosystems/ModernFrameworks.js +159 -0
  32. package/src/plugins/ecosystems/MorePlugins.js +184 -0
  33. package/src/plugins/ecosystems/NextJsPlugin.js +33 -0
  34. package/src/plugins/ecosystems/PluginLoader.js +20 -0
  35. package/src/plugins/ecosystems/TypeScriptPlugin.js +56 -0
  36. package/src/refractor/ImpactAnalyzer.js +92 -0
  37. package/src/refractor/SourceRewriter.js +86 -0
  38. package/src/refractor/TransactionManager.js +138 -0
  39. package/src/refractor/TypeIntegrity.js +75 -0
  40. package/src/resolution/CircularDetector.js +91 -0
  41. package/src/resolution/ConfigLoader.js +87 -0
  42. package/src/resolution/DepencyResolver.js +133 -0
  43. package/src/resolution/DependencyProfiler.js +268 -0
  44. package/src/resolution/PathMapper.js +125 -0
  45. package/src/resolution/WorkSpaceGraph.js +474 -0
  46. package/tsconfig.json +26 -0
@@ -0,0 +1,268 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * Advanced Dependency Profiling Engine.
6
+ * Traces Peer Dependencies and Implicit Tooling Invocations.
7
+ * Now supports "Unused Binaries" detection.
8
+ */
9
+ export class DependencyProfiler {
10
+ constructor(context) {
11
+ this.context = context;
12
+
13
+ this.binaryToPackageMap = {
14
+ 'tsc': 'typescript',
15
+ 'ts-node': 'ts-node',
16
+ 'tsx': 'tsx',
17
+ 'node': 'node',
18
+ 'bun': 'bun',
19
+ 'deno': 'deno',
20
+ 'jest': 'jest',
21
+ 'vitest': 'vitest',
22
+ 'mocha': 'mocha',
23
+ 'jasmine': 'jasmine',
24
+ 'ava': 'ava',
25
+ 'tap': 'tap',
26
+ 'uvu': 'uvu',
27
+ 'c8': 'c8',
28
+ 'nyc': 'nyc',
29
+ 'playwright': '@playwright/test',
30
+ 'cypress': 'cypress',
31
+ 'puppeteer': 'puppeteer',
32
+ 'webdriverio': 'webdriverio',
33
+ 'wdio': '@wdio/cli',
34
+ 'eslint': 'eslint',
35
+ 'prettier': 'prettier',
36
+ 'tslint': 'tslint',
37
+ 'biome': '@biomejs/biome',
38
+ 'oxlint': 'oxlint',
39
+ 'stylelint': 'stylelint',
40
+ 'markdownlint': 'markdownlint-cli',
41
+ 'commitlint': '@commitlint/cli',
42
+ 'lint-staged': 'lint-staged',
43
+ 'vite': 'vite',
44
+ 'webpack': 'webpack',
45
+ 'rollup': 'rollup',
46
+ 'esbuild': 'esbuild',
47
+ 'parcel': 'parcel',
48
+ 'turbo': 'turbo',
49
+ 'nx': 'nx',
50
+ 'lerna': 'lerna',
51
+ 'changesets': '@changesets/cli',
52
+ 'changeset': '@changesets/cli',
53
+ 'tsup': 'tsup',
54
+ 'unbuild': 'unbuild',
55
+ 'pkgroll': 'pkgroll',
56
+ 'microbundle': 'microbundle',
57
+ 'ncc': '@vercel/ncc',
58
+ 'swc': '@swc/cli',
59
+ 'tailwind': 'tailwindcss',
60
+ 'tailwindcss': 'tailwindcss',
61
+ 'postcss': 'postcss',
62
+ 'sass': 'sass',
63
+ 'less': 'less',
64
+ 'next': 'next',
65
+ 'nuxt': 'nuxt',
66
+ 'astro': 'astro',
67
+ 'remix': '@remix-run/dev',
68
+ 'svelte-kit': '@sveltejs/kit',
69
+ 'expo': 'expo',
70
+ 'react-scripts': 'react-scripts',
71
+ 'ng': '@angular/cli',
72
+ 'vue': '@vue/cli-service',
73
+ 'gatsby': 'gatsby',
74
+ 'nodemon': 'nodemon',
75
+ 'ts-node-dev': 'ts-node-dev',
76
+ 'concurrently': 'concurrently',
77
+ 'cross-env': 'cross-env',
78
+ 'dotenv': 'dotenv-cli',
79
+ 'dotenv-cli': 'dotenv-cli',
80
+ 'rimraf': 'rimraf',
81
+ 'del-cli': 'del-cli',
82
+ 'copyfiles': 'copyfiles',
83
+ 'cpy-cli': 'cpy-cli',
84
+ 'mkdirp': 'mkdirp',
85
+ 'shx': 'shx',
86
+ 'npm-run-all': 'npm-run-all',
87
+ 'run-p': 'npm-run-all',
88
+ 'run-s': 'npm-run-all',
89
+ 'typedoc': 'typedoc',
90
+ 'jsdoc': 'jsdoc',
91
+ 'storybook': 'storybook',
92
+ 'sb': 'storybook',
93
+ 'husky': 'husky',
94
+ 'simple-git-hooks': 'simple-git-hooks',
95
+ 'lefthook': 'lefthook',
96
+ 'pnpm': 'pnpm',
97
+ 'yarn': 'yarn',
98
+ 'npm': 'npm',
99
+ 'patch-package': 'patch-package',
100
+ 'syncpack': 'syncpack',
101
+ 'publint': 'publint',
102
+ 'attw': '@arethetypeswrong/cli',
103
+ 'size-limit': 'size-limit',
104
+ 'bundlesize': 'bundlesize',
105
+ 'depcheck': 'depcheck',
106
+ 'knip': 'knip',
107
+ 'entkapp': 'entkapp'
108
+ };
109
+
110
+ this.configFileToPackageMap = {
111
+ 'jest.config': 'jest',
112
+ 'vitest.config': 'vitest',
113
+ 'playwright.config': '@playwright/test',
114
+ 'cypress.config': 'cypress',
115
+ 'webpack.config': 'webpack',
116
+ 'vite.config': 'vite',
117
+ 'rollup.config': 'rollup',
118
+ 'tailwind.config': 'tailwindcss',
119
+ 'postcss.config': 'postcss',
120
+ '.eslintrc': 'eslint',
121
+ 'eslint.config': 'eslint',
122
+ '.prettierrc': 'prettier',
123
+ 'prettier.config': 'prettier',
124
+ '.babelrc': '@babel/core',
125
+ 'babel.config': '@babel/core',
126
+ '.stylelintrc': 'stylelint',
127
+ 'stylelint.config': 'stylelint',
128
+ 'svelte.config': '@sveltejs/kit',
129
+ 'astro.config': 'astro',
130
+ 'nuxt.config': 'nuxt',
131
+ 'next.config': 'next',
132
+ 'remix.config': '@remix-run/dev',
133
+ '.commitlintrc': '@commitlint/cli',
134
+ 'commitlint.config': '@commitlint/cli',
135
+ 'tsup.config': 'tsup',
136
+ 'typedoc': 'typedoc',
137
+ '.lintstagedrc': 'lint-staged',
138
+ 'lint-staged.config': 'lint-staged',
139
+ 'lefthook': 'lefthook',
140
+ 'knip.config': 'knip',
141
+ 'knip.json': 'knip'
142
+ };
143
+ }
144
+
145
+ async traceImplicitInvocations(projectRoot) {
146
+ const usedPackages = new Set();
147
+ const usedBinaries = new Set(); // NEW: Track which binaries are actually used
148
+
149
+ // 1. Scan package.json scripts
150
+ try {
151
+ const pkgJsonPath = path.join(projectRoot, 'package.json');
152
+ const pkg = JSON.parse(await fs.readFile(pkgJsonPath, 'utf8'));
153
+
154
+ if (pkg.scripts) {
155
+ for (const script of Object.values(pkg.scripts)) {
156
+ this.extractPackagesFromScript(script, usedPackages, usedBinaries);
157
+ }
158
+ }
159
+
160
+ // Detect @types/* usage
161
+ if (pkg.dependencies || pkg.devDependencies) {
162
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
163
+ for (const depName of Object.keys(allDeps)) {
164
+ if (depName.startsWith('@types/')) {
165
+ usedPackages.add(depName);
166
+ }
167
+ }
168
+ }
169
+ } catch (e) {}
170
+
171
+ // 2. Scan CI workflows
172
+ try {
173
+ const githubWorkflows = path.join(projectRoot, '.github/workflows');
174
+ const files = await fs.readdir(githubWorkflows).catch(() => []);
175
+ for (const file of files) {
176
+ if (file.endsWith('.yml') || file.endsWith('.yaml')) {
177
+ const content = await fs.readFile(path.join(githubWorkflows, file), 'utf8');
178
+ this.extractPackagesFromScript(content, usedPackages, usedBinaries);
179
+ }
180
+ }
181
+ } catch (e) {}
182
+
183
+ // 3. Scan config files
184
+ try {
185
+ const dirEntries = await fs.readdir(projectRoot, { withFileTypes: true });
186
+ for (const entry of dirEntries) {
187
+ if (!entry.isFile()) continue;
188
+ const fileName = entry.name;
189
+ for (const [fragment, pkgName] of Object.entries(this.configFileToPackageMap)) {
190
+ if (fileName.startsWith(fragment) || fileName === fragment) {
191
+ usedPackages.add(pkgName);
192
+ break;
193
+ }
194
+ }
195
+ }
196
+ } catch (e) {}
197
+
198
+ // 4. Identify Unused Binaries
199
+ // Scan node_modules/.bin for all available binaries
200
+ try {
201
+ const binDir = path.join(projectRoot, 'node_modules', '.bin');
202
+ const availableBinaries = await fs.readdir(binDir).catch(() => []);
203
+
204
+ for (const bin of availableBinaries) {
205
+ // Skip hidden files or package manager internal bins
206
+ if (bin.startsWith('.') || ['npm', 'pnpm', 'yarn', 'bun'].includes(bin)) continue;
207
+
208
+ if (!usedBinaries.has(bin)) {
209
+ this.context.unusedBinaries.add(bin);
210
+ }
211
+ }
212
+ } catch (e) {}
213
+
214
+ return usedPackages;
215
+ }
216
+
217
+ extractPackagesFromScript(script, packageCollector, binaryCollector) {
218
+ const words = script.split(/[\s&|;()\n\r\t]+/);
219
+ for (let i = 0; i < words.length; i++) {
220
+ const rawWord = words[i];
221
+ const cleanWord = rawWord.replace(/['"]/g, '').replace(/^(npx|pnpx|bunx|yarn|pnpm)\s+/, '');
222
+
223
+ if (this.binaryToPackageMap[cleanWord]) {
224
+ packageCollector.add(this.binaryToPackageMap[cleanWord]);
225
+ if (binaryCollector) binaryCollector.add(cleanWord);
226
+ continue;
227
+ }
228
+
229
+ if ((rawWord === 'npx' || rawWord === 'pnpx' || rawWord === 'bunx') && i + 1 < words.length) {
230
+ const nextWord = words[i + 1].replace(/['"]/g, '');
231
+ if (this.binaryToPackageMap[nextWord]) {
232
+ packageCollector.add(this.binaryToPackageMap[nextWord]);
233
+ if (binaryCollector) binaryCollector.add(nextWord);
234
+ }
235
+ if (nextWord.startsWith('@') || nextWord.includes('/')) {
236
+ const pkgName = nextWord.split('/').slice(0, nextWord.startsWith('@') ? 2 : 1).join('/');
237
+ if (pkgName) packageCollector.add(pkgName);
238
+ }
239
+ }
240
+ }
241
+ }
242
+
243
+ async resolvePeerDependencies(usedPackages, projectRoot) {
244
+ const peerDeps = new Set();
245
+ const nodeModules = path.join(projectRoot, 'node_modules');
246
+
247
+ for (const pkgName of usedPackages) {
248
+ try {
249
+ const pkgJsonPath = path.join(nodeModules, pkgName, 'package.json');
250
+ const pkg = JSON.parse(await fs.readFile(pkgJsonPath, 'utf8'));
251
+ if (pkg.peerDependencies) {
252
+ Object.keys(pkg.peerDependencies).forEach(dep => peerDeps.add(dep));
253
+ }
254
+ } catch (e) {}
255
+ }
256
+ return peerDeps;
257
+ }
258
+
259
+ shouldExcludeFromUnusedCheck(packageName, depType) {
260
+ if (depType === 'peerDependency' || depType === 'optionalDependency') {
261
+ return true;
262
+ }
263
+ if (packageName.startsWith('@types/')) {
264
+ return true;
265
+ }
266
+ return false;
267
+ }
268
+ }
@@ -0,0 +1,125 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * Advanced TSConfig / JSConfig Compilation Path Alias Mapper
6
+ * Resolves deeply nested route mappings, wildcards, and base URL overrides.
7
+ */
8
+ export class PathMapper {
9
+ constructor(context) {
10
+ this.context = context;
11
+ this.baseUrl = '.';
12
+ this.absoluteBaseUrl = context.cwd;
13
+ this.mappings = []; // Collection of { prefix, suffix, targets[] }
14
+ }
15
+
16
+ /**
17
+ * Reads, cleans, and indexes custom alias entries from tsconfig.json files.
18
+ * @param {string} tsconfigFilename - Target designator (typically tsconfig.json)
19
+ */
20
+ async loadMappings(tsconfigFilename = 'tsconfig.json') {
21
+ const configPath = path.resolve(this.context.cwd, tsconfigFilename);
22
+
23
+ try {
24
+ await fs.access(configPath);
25
+ const rawText = await fs.readFile(configPath, 'utf8');
26
+
27
+ // Strip inline single-line and block comments before parsing
28
+ // Improved regex to handle more edge cases in tsconfig comments
29
+ const jsonCleanText = rawText
30
+ .replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1')
31
+ .replace(/,(\s*[\]}])/g, '$1'); // Remove trailing commas
32
+
33
+ const tsconfig = JSON.parse(jsonCleanText);
34
+
35
+ if (!tsconfig.compilerOptions) return;
36
+
37
+ const opts = tsconfig.compilerOptions;
38
+
39
+ // v6 Path Resolution Fix (Knip Issue #1794)
40
+ // Ensure baseUrl is correctly resolved relative to the tsconfig file location
41
+ const configDir = path.dirname(configPath);
42
+ if (opts.baseUrl) {
43
+ this.baseUrl = opts.baseUrl;
44
+ this.absoluteBaseUrl = path.resolve(configDir, this.baseUrl);
45
+ } else {
46
+ this.absoluteBaseUrl = configDir;
47
+ }
48
+
49
+ if (opts.paths) {
50
+ for (const [aliasPattern, targetArrays] of Object.entries(opts.paths)) {
51
+ this.registerPatternRule(aliasPattern, targetArrays);
52
+ }
53
+ }
54
+ } catch (error) {
55
+ if (this.context.verbose) {
56
+ console.warn(`⚠️ [PathMapper Override] Proceeding without custom path configurations. Source: ${error.message}`);
57
+ }
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Registers structural lookup parameters for alias strings.
63
+ */
64
+ registerPatternRule(pattern, targets) {
65
+ const wildcardIndex = pattern.indexOf('*');
66
+
67
+ if (wildcardIndex === -1) {
68
+ this.mappings.push({
69
+ isExact: true,
70
+ pattern,
71
+ targets: targets.map(t => path.normalize(t))
72
+ });
73
+ return;
74
+ }
75
+
76
+ const prefix = pattern.slice(0, wildcardIndex);
77
+ const suffix = pattern.slice(wildcardIndex + 1);
78
+
79
+ this.mappings.push({
80
+ isExact: false,
81
+ prefix,
82
+ suffix,
83
+ targets: targets.map(t => path.normalize(t))
84
+ });
85
+ }
86
+
87
+ /**
88
+ * Resolves a raw import specifier against mapped path patterns.
89
+ * @param {string} specifier - Raw text from import declaration (e.g., '@ui/button')
90
+ * @returns {Array<string>} Candidates of absolute filesystem paths to try resolving
91
+ */
92
+ resolveCandidatePaths(specifier) {
93
+ const matchingCandidates = [];
94
+
95
+ for (const rule of this.mappings) {
96
+ if (rule.isExact) {
97
+ if (specifier === rule.pattern) {
98
+ rule.targets.forEach(target => {
99
+ matchingCandidates.push(path.resolve(this.absoluteBaseUrl, target));
100
+ });
101
+ }
102
+ } else {
103
+ // Evaluate wildcard pattern matches
104
+ if (specifier.startsWith(rule.prefix) && specifier.endsWith(rule.suffix)) {
105
+ const extractedWildcardContent = specifier.slice(
106
+ rule.prefix.length,
107
+ specifier.length - rule.suffix.length
108
+ );
109
+
110
+ rule.targets.forEach(targetTemplate => {
111
+ const interpolatedTarget = targetTemplate.replace('*', extractedWildcardContent);
112
+ matchingCandidates.push(path.resolve(this.absoluteBaseUrl, interpolatedTarget));
113
+ });
114
+ }
115
+ }
116
+ }
117
+
118
+ // Fall back to direct lookup relative to the base URL
119
+ if (!specifier.startsWith('.') && !path.isAbsolute(specifier)) {
120
+ matchingCandidates.push(path.resolve(this.absoluteBaseUrl, specifier));
121
+ }
122
+
123
+ return matchingCandidates;
124
+ }
125
+ }