entkapp 4.5.1 → 5.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.
- package/bin/cli.js +4 -4
- package/entkapp/config.json +0 -1
- package/package.json +5 -6
- package/src/EngineContext.js +276 -78
- package/src/analyzers/OxcAnalyzer.js +8 -380
- package/src/api/HeadlessAPI.js +1 -1
- package/src/api/PluginSDK.js +23 -187
- package/src/ast/ASTAnalyzer.js +481 -252
- package/src/ast/AdvancedAnalysis.js +6 -5
- package/src/ast/BarrelParser.js +39 -30
- package/src/ast/DeadCodeDetector.js +30 -18
- package/src/ast/MagicDetector.js +1 -1
- package/src/ast/OxcAnalyzer.js +335 -273
- package/src/index.js +279 -365
- package/src/performance/GraphCache.js +21 -2
- package/src/performance/WorkerPool.js +11 -1
- package/src/performance/WorkerTaskRunner.js +72 -25
- package/src/plugins/PluginRegistry.js +5 -16
- package/src/plugins/ecosystems/GenericPlugins.js +61 -0
- package/src/refractor/TransactionManager.js +3 -136
- package/src/refractor/TypeIntegrity.js +2 -73
- package/src/resolution/CircularDetector.js +44 -44
- package/src/resolution/ConfigLoader.js +2 -85
- package/src/resolution/DepencyResolver.js +28 -121
- package/src/resolution/EntryPointDetector.js +134 -0
- package/src/resolution/PathMapper.js +31 -107
- package/src/resolution/TSConfigLoader.js +76 -0
- package/src/resolution/WorkSpaceGraph.js +6 -472
- package/src/resolution/WorkspaceDiagnostic.js +3 -57
- package/src/plugins/KnipAdapter.js +0 -106
|
@@ -1,32 +1,35 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
1
3
|
export class CircularDetector {
|
|
2
4
|
constructor(context) {
|
|
3
5
|
this.context = context;
|
|
4
6
|
this.cycles = [];
|
|
5
7
|
}
|
|
6
8
|
|
|
7
|
-
detectCycles(
|
|
8
|
-
if (context) this.context = context;
|
|
9
|
-
this.cwd = context?.cwd || this.context?.cwd || process.cwd();
|
|
10
|
-
this.cycles = [];
|
|
11
|
-
|
|
12
|
-
let index = 0;
|
|
9
|
+
detectCycles(projectGraph) {
|
|
13
10
|
const stack = [];
|
|
11
|
+
const onStack = new Set();
|
|
14
12
|
const indices = new Map();
|
|
15
13
|
const lowlink = new Map();
|
|
16
|
-
const
|
|
14
|
+
const sccs = [];
|
|
15
|
+
let index = 0;
|
|
17
16
|
|
|
18
|
-
const
|
|
17
|
+
const strongConnect = (v) => {
|
|
19
18
|
indices.set(v, index);
|
|
20
19
|
lowlink.set(v, index);
|
|
21
20
|
index++;
|
|
22
21
|
stack.push(v);
|
|
23
22
|
onStack.add(v);
|
|
24
23
|
|
|
25
|
-
const node =
|
|
24
|
+
const node = projectGraph.get(v);
|
|
26
25
|
if (node && node.outgoingEdges) {
|
|
27
|
-
|
|
26
|
+
// Ensure outgoingEdges is treated as an array-like structure
|
|
27
|
+
const edges = Array.isArray(node.outgoingEdges) ? node.outgoingEdges : Array.from(node.outgoingEdges);
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < edges.length; i++) {
|
|
30
|
+
const w = edges[i];
|
|
28
31
|
if (!indices.has(w)) {
|
|
29
|
-
|
|
32
|
+
strongConnect(w);
|
|
30
33
|
lowlink.set(v, Math.min(lowlink.get(v), lowlink.get(w)));
|
|
31
34
|
} else if (onStack.has(w)) {
|
|
32
35
|
lowlink.set(v, Math.min(lowlink.get(v), indices.get(w)));
|
|
@@ -35,57 +38,54 @@ export class CircularDetector {
|
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
if (lowlink.get(v) === indices.get(v)) {
|
|
38
|
-
const
|
|
41
|
+
const scc = [];
|
|
39
42
|
let w;
|
|
40
43
|
do {
|
|
41
44
|
w = stack.pop();
|
|
42
45
|
onStack.delete(w);
|
|
43
|
-
|
|
46
|
+
scc.push(w);
|
|
44
47
|
} while (w !== v);
|
|
45
48
|
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
if (scc.length > 1) {
|
|
50
|
+
const cycle = scc.slice().reverse();
|
|
51
|
+
cycle.push(cycle[0]);
|
|
52
|
+
sccs.push(cycle);
|
|
53
|
+
} else if (scc.length === 1) {
|
|
54
|
+
const singleNode = projectGraph.get(scc[0]);
|
|
55
|
+
if (singleNode && singleNode.outgoingEdges) {
|
|
56
|
+
const edges = Array.isArray(singleNode.outgoingEdges) ? singleNode.outgoingEdges : Array.from(singleNode.outgoingEdges);
|
|
57
|
+
let hasSelfLoop = false;
|
|
58
|
+
for (let i = 0; i < edges.length; i++) {
|
|
59
|
+
if (edges[i] === scc[0]) {
|
|
60
|
+
hasSelfLoop = true;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (hasSelfLoop) {
|
|
65
|
+
sccs.push([scc[0], scc[0]]);
|
|
66
|
+
}
|
|
52
67
|
}
|
|
53
68
|
}
|
|
54
69
|
}
|
|
55
70
|
};
|
|
56
71
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
72
|
+
const keys = Array.from(projectGraph.keys());
|
|
73
|
+
for (let i = 0; i < keys.length; i++) {
|
|
74
|
+
if (!indices.has(keys[i])) {
|
|
75
|
+
strongConnect(keys[i]);
|
|
60
76
|
}
|
|
61
77
|
}
|
|
62
78
|
|
|
63
|
-
|
|
79
|
+
this.cycles = sccs;
|
|
80
|
+
return sccs;
|
|
64
81
|
}
|
|
65
82
|
|
|
66
83
|
formatCycles() {
|
|
67
84
|
return this.cycles.map(cycle => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
});
|
|
73
|
-
if (cycle.length === 1) return `${paths[0]} -> (self-loop)`;
|
|
74
|
-
return paths.join(' -> ') + ' -> ' + paths[0];
|
|
85
|
+
return cycle.map(p => {
|
|
86
|
+
const relativePath = path.relative(this.context.cwd, p);
|
|
87
|
+
return relativePath.replace(/\\/g, '/');
|
|
88
|
+
}).join(' -> ');
|
|
75
89
|
});
|
|
76
90
|
}
|
|
77
|
-
|
|
78
|
-
getCycleDetails() {
|
|
79
|
-
return this.cycles.map((cycle, idx) => ({
|
|
80
|
-
cycleId: idx + 1,
|
|
81
|
-
files: cycle.map(p => {
|
|
82
|
-
let rel = p.replace(this.context.cwd, '').replace(/^\//, '');
|
|
83
|
-
if (rel.includes(':\\')) rel = rel.split(':\\')[1] || rel;
|
|
84
|
-
return rel;
|
|
85
|
-
}),
|
|
86
|
-
length: cycle.length,
|
|
87
|
-
isSelfLoop: cycle.length === 1
|
|
88
|
-
}));
|
|
89
|
-
}
|
|
90
91
|
}
|
|
91
|
-
export default CircularDetector;
|
|
@@ -1,87 +1,4 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { pathToFileURL } from 'url';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Loads and parses entkapp configuration files.
|
|
7
|
-
* Supports scaffold.config.js, .scaffoldrc.json, and .scaffoldrc.
|
|
8
|
-
*/
|
|
9
1
|
export class ConfigLoader {
|
|
10
|
-
constructor(
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async loadConfig(projectRoot) {
|
|
15
|
-
const searchPaths = [
|
|
16
|
-
'entkapp.json',
|
|
17
|
-
'entkapp.jsonc',
|
|
18
|
-
'.entkapp.json',
|
|
19
|
-
'.entkapp.jsonc',
|
|
20
|
-
'entkapp.js',
|
|
21
|
-
'entkapp.ts',
|
|
22
|
-
'entkapp/config.json',
|
|
23
|
-
'scaffold.config.js',
|
|
24
|
-
'scaffold.config.mjs',
|
|
25
|
-
'.scaffoldrc.json',
|
|
26
|
-
'.scaffoldrc'
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
let config = this.getDefaultConfig();
|
|
30
|
-
|
|
31
|
-
// Try package.json first
|
|
32
|
-
try {
|
|
33
|
-
const pkgPath = path.join(projectRoot, 'package.json');
|
|
34
|
-
const pkgContent = await fs.readFile(pkgPath, 'utf8');
|
|
35
|
-
const pkg = JSON.parse(pkgContent);
|
|
36
|
-
if (pkg['entkapp']) {
|
|
37
|
-
Object.assign(config, pkg['entkapp']);
|
|
38
|
-
}
|
|
39
|
-
} catch (e) {
|
|
40
|
-
// ignore
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
for (const fileName of searchPaths) {
|
|
44
|
-
const fullPath = path.join(projectRoot, fileName);
|
|
45
|
-
try {
|
|
46
|
-
await fs.access(fullPath);
|
|
47
|
-
|
|
48
|
-
if (fileName.endsWith('.js') || fileName.endsWith('.mjs') || fileName.endsWith('.ts')) {
|
|
49
|
-
const module = await import(pathToFileURL(fullPath).href);
|
|
50
|
-
Object.assign(config, module.default || module);
|
|
51
|
-
break;
|
|
52
|
-
} else {
|
|
53
|
-
const content = await fs.readFile(fullPath, 'utf8');
|
|
54
|
-
// Very basic JSONC parsing (strip comments)
|
|
55
|
-
const stripped = content.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '');
|
|
56
|
-
Object.assign(config, JSON.parse(stripped));
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
} catch (e) {
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return config;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
getDefaultConfig() {
|
|
68
|
-
return {
|
|
69
|
-
entryPoints: ['src/index.ts', 'index.js', 'src/index.js', 'src/main.ts', 'src/main.js'],
|
|
70
|
-
exclude: [
|
|
71
|
-
'node_modules/**',
|
|
72
|
-
'dist/**',
|
|
73
|
-
'build/**',
|
|
74
|
-
'**/*.test.ts',
|
|
75
|
-
'**/*.spec.ts',
|
|
76
|
-
'**/*.test.js',
|
|
77
|
-
'**/*.spec.js'
|
|
78
|
-
],
|
|
79
|
-
plugins: [],
|
|
80
|
-
rules: {
|
|
81
|
-
'no-unused-exports': 'error',
|
|
82
|
-
'no-unused-vars': 'warn',
|
|
83
|
-
'no-dead-code': 'error'
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
}
|
|
2
|
+
constructor(cwd) { this.cwd = cwd; }
|
|
3
|
+
async loadConfig() { return {}; }
|
|
87
4
|
}
|
|
@@ -1,144 +1,51 @@
|
|
|
1
|
-
import resolve from 'enhanced-resolve';
|
|
2
1
|
import path from 'path';
|
|
3
2
|
import { existsSync } from 'fs';
|
|
4
3
|
|
|
5
|
-
/**
|
|
6
|
-
* Industrial-Strength Module Resolution Supervisor
|
|
7
|
-
* Integrates path mapping and workspace topologies using enhanced-resolve.
|
|
8
|
-
*/
|
|
9
4
|
export class DependencyResolver {
|
|
10
5
|
constructor(context, pathMapper, workspaceGraph) {
|
|
11
6
|
this.context = context;
|
|
12
7
|
this.pathMapper = pathMapper;
|
|
13
8
|
this.workspaceGraph = workspaceGraph;
|
|
14
|
-
|
|
15
|
-
// Instantiate production-grade enhanced-resolve workspace parameters
|
|
16
|
-
this.nativeResolver = resolve.create.sync({
|
|
17
|
-
conditionNames: ['import', 'module', 'require', 'node', 'types'],
|
|
18
|
-
// Extensions prioritized: TS then JS
|
|
19
|
-
extensions: ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs', '.json', '.vue'],
|
|
20
|
-
mainFields: ['module', 'main', 'types'],
|
|
21
|
-
exportsFields: ['exports'],
|
|
22
|
-
symlinks: true
|
|
23
|
-
});
|
|
24
9
|
}
|
|
25
10
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'fs/promises', 'http', 'http2',
|
|
34
|
-
'https', 'inspector', 'module', 'net', 'os', 'path', 'perf_hooks', 'process',
|
|
35
|
-
'punycode', 'querystring', 'readline', 'repl', 'stream', 'string_decoder',
|
|
36
|
-
'timers', 'tls', 'trace_events', 'tty', 'url', 'util', 'v8', 'vm', 'worker_threads', 'zlib'
|
|
37
|
-
].includes(importSpecifier)) {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const containingDir = path.dirname(containingFile);
|
|
42
|
-
|
|
43
|
-
// --- NEW: Fix for JS-in-TS import trap ---
|
|
44
|
-
// If we are in a TS file and importing something ending in .js,
|
|
45
|
-
// we should try to resolve the .ts version first.
|
|
46
|
-
let effectiveSpecifier = importSpecifier;
|
|
47
|
-
const isTsFile = /\.(ts|tsx|mts|cts)$/.test(containingFile);
|
|
48
|
-
if (importSpecifier.endsWith('.js')) {
|
|
49
|
-
const tsSpecifier = importSpecifier.replace(/\.js$/, '.ts');
|
|
50
|
-
try {
|
|
51
|
-
const resolvedTs = this.nativeResolver(containingDir, tsSpecifier);
|
|
52
|
-
if (this.isAbsoluteInternalPath(resolvedTs)) return resolvedTs;
|
|
53
|
-
} catch (e) {
|
|
54
|
-
// Fall back to original specifier
|
|
55
|
-
}
|
|
11
|
+
normalizePath(p) {
|
|
12
|
+
if (!p) return p;
|
|
13
|
+
const original = p;
|
|
14
|
+
// Handle Windows drive letters and backslashes
|
|
15
|
+
let normalized = p.replace(/\\/g, '/');
|
|
16
|
+
if (/^[a-z]:\//i.test(normalized)) {
|
|
17
|
+
normalized = normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
56
18
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// Rule A: Intercept and resolve local monorepo workspace cross-links
|
|
60
|
-
if (this.workspaceGraph.isLocalWorkspaceSpecifier(effectiveSpecifier)) {
|
|
61
|
-
const match = this.workspaceGraph.getWorkspacePackageMatch(effectiveSpecifier);
|
|
62
|
-
if (match) {
|
|
63
|
-
if (effectiveSpecifier === match.packageName) {
|
|
64
|
-
return match.entryPoints[0] || null;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const subPathOffset = effectiveSpecifier.slice(match.packageName.length + 1);
|
|
68
|
-
try {
|
|
69
|
-
return this.nativeResolver(match.rootDirectory, `./${subPathOffset}`);
|
|
70
|
-
} catch {
|
|
71
|
-
}
|
|
72
|
-
}
|
|
19
|
+
if (this.context.verbose && original !== normalized) {
|
|
20
|
+
// console.log(`[Path-DEBUG] Normalized: ${original} -> ${normalized}`);
|
|
73
21
|
}
|
|
22
|
+
return normalized;
|
|
23
|
+
}
|
|
74
24
|
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
25
|
+
resolveModulePath(sourceFile, specifier) {
|
|
26
|
+
const cleanSource = this.normalizePath(sourceFile);
|
|
27
|
+
|
|
28
|
+
// UPGRADE: Use PathMapper for sophisticated resolution (TS-to-JS, aliases, etc.)
|
|
29
|
+
if (this.pathMapper) {
|
|
30
|
+
const dir = path.dirname(cleanSource);
|
|
31
|
+
const target = path.resolve(dir, specifier);
|
|
32
|
+
const resolved = this.pathMapper.resolvePath(target);
|
|
33
|
+
if (resolved && existsSync(resolved)) {
|
|
34
|
+
return this.normalizePath(resolved);
|
|
86
35
|
}
|
|
87
36
|
}
|
|
88
37
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const
|
|
38
|
+
if (specifier.startsWith('.')) {
|
|
39
|
+
const dir = path.dirname(cleanSource);
|
|
40
|
+
const target = path.resolve(dir, specifier);
|
|
41
|
+
const normalizedTarget = this.normalizePath(target);
|
|
92
42
|
|
|
93
|
-
|
|
94
|
-
if (this.isAbsoluteInternalPath(resolvedPath)) {
|
|
95
|
-
return resolvedPath;
|
|
96
|
-
}
|
|
97
|
-
} catch (err) {
|
|
98
|
-
// Fallback: Try to resolve with common extensions if enhanced-resolve fails
|
|
99
|
-
const extensions = ['.ts', '.tsx', '.js', '.jsx'];
|
|
43
|
+
const extensions = ['', '.js', '.ts', '.tsx', '.jsx', '/index.js', '/index.ts', '/index.tsx'];
|
|
100
44
|
for (const ext of extensions) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const resolvedPath = path.resolve(containingDir, trialPath);
|
|
104
|
-
if (existsSync(resolvedPath)) return resolvedPath;
|
|
105
|
-
} catch {}
|
|
106
|
-
}
|
|
107
|
-
if (this.context.verbose) {
|
|
108
|
-
console.log(`[Resolution Trace Skip] Specifier unresolvable: ${effectiveSpecifier} inside ${containingFile}`);
|
|
45
|
+
const p = normalizedTarget + ext;
|
|
46
|
+
if (existsSync(p)) return this.normalizePath(p);
|
|
109
47
|
}
|
|
110
48
|
}
|
|
111
|
-
|
|
112
49
|
return null;
|
|
113
50
|
}
|
|
114
|
-
|
|
115
|
-
isAbsoluteInternalPath(resolvedPath) {
|
|
116
|
-
if (!resolvedPath) return false;
|
|
117
|
-
const normalized = resolvedPath.replace(/\\/g, '/');
|
|
118
|
-
|
|
119
|
-
if (normalized.includes('/node_modules/')) {
|
|
120
|
-
for (const [name, meta] of this.workspaceGraph.packageManifests.entries()) {
|
|
121
|
-
if (normalized.startsWith(meta.rootDirectory.replace(/\\/g, '/'))) {
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return path.isAbsolute(resolvedPath);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
determineIntentProfile(filePath, declaredExportsManifest) {
|
|
132
|
-
const fileName = path.basename(filePath);
|
|
133
|
-
const isPublicContractFile = /(^index|^public\-api|^entry)\.(ts|js|tsx|jsx)$/i.test(fileName);
|
|
134
|
-
|
|
135
|
-
if (isPublicContractFile) {
|
|
136
|
-
for (const [symbolKey, metadata] of declaredExportsManifest.entries()) {
|
|
137
|
-
metadata.isLibraryContract = true;
|
|
138
|
-
}
|
|
139
|
-
return 'LIBRARY_CONSUMPTION_TARGET';
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return 'INTERNAL_CODEBASE_ELEMENT';
|
|
143
|
-
}
|
|
144
51
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
export class EntryPointDetector {
|
|
5
|
+
constructor(targetDir, packageJson, stats) {
|
|
6
|
+
this.targetDir = targetDir;
|
|
7
|
+
this.packageJson = packageJson || {};
|
|
8
|
+
this.stats = stats;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Ermittelt alle potenziellen Entry-Points eines Projekts.
|
|
13
|
+
* @returns {Set<string>} Set von absoluten Pfaden.
|
|
14
|
+
*/
|
|
15
|
+
detect() {
|
|
16
|
+
const entries = new Set();
|
|
17
|
+
|
|
18
|
+
// 1. package.json Standards
|
|
19
|
+
this._addFromPackageJson(entries);
|
|
20
|
+
|
|
21
|
+
// 2. Framework-spezifische Pfade
|
|
22
|
+
this._addFromFrameworks(entries);
|
|
23
|
+
|
|
24
|
+
// 3. Fallbacks / Konventionen
|
|
25
|
+
this._addFromConventions(entries);
|
|
26
|
+
|
|
27
|
+
return entries;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_addFromPackageJson(entries) {
|
|
31
|
+
const fields = ['main', 'module', 'browser', 'bin'];
|
|
32
|
+
fields.forEach(field => {
|
|
33
|
+
const val = this.packageJson[field];
|
|
34
|
+
if (typeof val === 'string') {
|
|
35
|
+
this._addIfExist(entries, val);
|
|
36
|
+
} else if (typeof val === 'object' && val !== null) {
|
|
37
|
+
Object.values(val).forEach(v => {
|
|
38
|
+
if (typeof v === 'string') this._addIfExist(entries, v);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Exports field (modern Node.js)
|
|
44
|
+
if (this.packageJson.exports) {
|
|
45
|
+
this._parseExports(this.packageJson.exports, entries);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
_parseExports(exports, entries) {
|
|
50
|
+
if (typeof exports === 'string') {
|
|
51
|
+
this._addIfExist(entries, exports);
|
|
52
|
+
} else if (typeof exports === 'object' && exports !== null) {
|
|
53
|
+
for (const key in exports) {
|
|
54
|
+
const val = exports[key];
|
|
55
|
+
if (typeof val === 'string') {
|
|
56
|
+
this._addIfExist(entries, val);
|
|
57
|
+
} else if (typeof val === 'object') {
|
|
58
|
+
this._parseExports(val, entries);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
_addFromFrameworks(entries) {
|
|
65
|
+
if (!this.stats || !this.stats.detectedFrameworks) return;
|
|
66
|
+
|
|
67
|
+
const frameworks = this.stats.detectedFrameworks;
|
|
68
|
+
|
|
69
|
+
if (frameworks.includes('next')) {
|
|
70
|
+
this._scanDir(path.join(this.targetDir, 'pages'), entries);
|
|
71
|
+
this._scanDir(path.join(this.targetDir, 'app'), entries, ['page.tsx', 'page.js', 'route.ts', 'route.js']);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (frameworks.includes('nuxt')) {
|
|
75
|
+
this._scanDir(path.join(this.targetDir, 'pages'), entries);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (frameworks.includes('svelte')) {
|
|
79
|
+
this._scanDir(path.join(this.targetDir, 'src/routes'), entries);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_addFromConventions(entries) {
|
|
84
|
+
const fallbacks = [
|
|
85
|
+
'index.js', 'index.ts', 'index.jsx', 'index.tsx',
|
|
86
|
+
'src/index.js', 'src/index.ts', 'src/index.jsx', 'src/index.tsx',
|
|
87
|
+
'main.js', 'main.ts', 'src/main.js', 'src/main.ts',
|
|
88
|
+
'app.js', 'app.ts', 'src/app.js', 'src/app.ts',
|
|
89
|
+
'server.js', 'server.ts', 'src/server.js', 'src/server.ts'
|
|
90
|
+
];
|
|
91
|
+
fallbacks.forEach(f => this._addIfExist(entries, f));
|
|
92
|
+
|
|
93
|
+
// If no entries found yet, and we are in a simple src structure,
|
|
94
|
+
// protect common top-level files in src/
|
|
95
|
+
if (entries.size === 0) {
|
|
96
|
+
const commonSrcFiles = ['src/app.ts', 'src/main.ts', 'src/index.ts', 'src/app.js', 'src/main.js', 'src/index.js'];
|
|
97
|
+
commonSrcFiles.forEach(f => this._addIfExist(entries, f));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
_addIfExist(entries, relativePath) {
|
|
102
|
+
// Bereinige Pfad (entferne ./ etc)
|
|
103
|
+
const cleanPath = relativePath.replace(/^(\.\/|\/)/, '');
|
|
104
|
+
const absolutePath = path.resolve(this.targetDir, cleanPath);
|
|
105
|
+
|
|
106
|
+
// Prüfe verschiedene Erweiterungen falls keine angegeben
|
|
107
|
+
const extensions = ['', '.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs'];
|
|
108
|
+
for (const ext of extensions) {
|
|
109
|
+
const p = absolutePath + ext;
|
|
110
|
+
if (fs.existsSync(p) && fs.statSync(p).isFile()) {
|
|
111
|
+
entries.add(p);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
_scanDir(dir, entries, specificFiles = null) {
|
|
118
|
+
if (!fs.existsSync(dir)) return;
|
|
119
|
+
|
|
120
|
+
const files = fs.readdirSync(dir, { recursive: true });
|
|
121
|
+
files.forEach(file => {
|
|
122
|
+
const fullPath = path.join(dir, file);
|
|
123
|
+
if (fs.statSync(fullPath).isFile()) {
|
|
124
|
+
if (specificFiles) {
|
|
125
|
+
if (specificFiles.includes(path.basename(file))) {
|
|
126
|
+
entries.add(fullPath);
|
|
127
|
+
}
|
|
128
|
+
} else if (/\.(js|ts|jsx|tsx|vue|svelte)$/.test(file)) {
|
|
129
|
+
entries.add(fullPath);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|