pkg-scaffold 3.2.0 → 3.3.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/LICENSE +211 -21
- package/NOTICE +13 -0
- package/package.json +8 -6
- package/src/api/HeadlessAPI.js +365 -0
- package/src/api/PluginSDK.js +299 -0
- package/src/plugins/KnipAdapter.js +106 -0
- package/src/plugins/PluginRegistry.js +22 -4
- package/src/plugins/ecosystems/BackendServices.js +59 -0
- package/src/plugins/ecosystems/ModernFrameworks.js +157 -0
- package/src/resolution/CircularDetector.js +71 -0
- package/src/resolution/PathMapper.js +12 -2
- package/src/resolution/WorkSpaceGraph.js +10 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* Circular Dependency Detector for pkg-scaffold v3.3.0
|
|
4
|
+
*
|
|
5
|
+
* Copyright (C) 2026 DreamLongYT
|
|
6
|
+
* Licensed under the Apache License, Version 2.0.
|
|
7
|
+
* "The original code was from the lovely DreamLong"
|
|
8
|
+
* ============================================================================
|
|
9
|
+
* Implements a high-performance Tarjan-based or DFS-based algorithm to
|
|
10
|
+
* detect circular dependencies in the codebase graph.
|
|
11
|
+
* Addresses Knip Issue #1734.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export class CircularDetector {
|
|
15
|
+
constructor(context) {
|
|
16
|
+
this.context = context;
|
|
17
|
+
this.cycles = [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Detects cycles in the provided dependency graph
|
|
22
|
+
* @param {Map} graph - The codebase dependency graph
|
|
23
|
+
* @returns {Array} List of detected cycles
|
|
24
|
+
*/
|
|
25
|
+
detectCycles(graph) {
|
|
26
|
+
this.cycles = [];
|
|
27
|
+
const visited = new Set();
|
|
28
|
+
const stack = new Set();
|
|
29
|
+
const path = [];
|
|
30
|
+
|
|
31
|
+
for (const filePath of graph.keys()) {
|
|
32
|
+
if (!visited.has(filePath)) {
|
|
33
|
+
this.dfs(filePath, graph, visited, stack, path);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return this.cycles;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
dfs(node, graph, visited, stack, path) {
|
|
41
|
+
visited.add(node);
|
|
42
|
+
stack.add(node);
|
|
43
|
+
path.push(node);
|
|
44
|
+
|
|
45
|
+
const edges = graph.get(node)?.outgoingEdges || [];
|
|
46
|
+
for (const neighbor of edges) {
|
|
47
|
+
if (stack.has(neighbor)) {
|
|
48
|
+
// Cycle detected
|
|
49
|
+
const cycleStartIndex = path.indexOf(neighbor);
|
|
50
|
+
const cycle = path.slice(cycleStartIndex);
|
|
51
|
+
this.cycles.push(cycle);
|
|
52
|
+
} else if (!visited.has(neighbor)) {
|
|
53
|
+
this.dfs(neighbor, graph, visited, stack, path);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
stack.delete(node);
|
|
58
|
+
path.pop();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Formats cycles for reporting
|
|
63
|
+
*/
|
|
64
|
+
formatCycles() {
|
|
65
|
+
return this.cycles.map(cycle => {
|
|
66
|
+
return cycle.join(' -> ') + ' -> ' + cycle[0];
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default CircularDetector;
|
|
@@ -25,15 +25,25 @@ export class PathMapper {
|
|
|
25
25
|
const rawText = await fs.readFile(configPath, 'utf8');
|
|
26
26
|
|
|
27
27
|
// Strip inline single-line and block comments before parsing
|
|
28
|
-
|
|
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
|
+
|
|
29
33
|
const tsconfig = JSON.parse(jsonCleanText);
|
|
30
34
|
|
|
31
35
|
if (!tsconfig.compilerOptions) return;
|
|
32
36
|
|
|
33
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);
|
|
34
42
|
if (opts.baseUrl) {
|
|
35
43
|
this.baseUrl = opts.baseUrl;
|
|
36
|
-
this.absoluteBaseUrl = path.resolve(
|
|
44
|
+
this.absoluteBaseUrl = path.resolve(configDir, this.baseUrl);
|
|
45
|
+
} else {
|
|
46
|
+
this.absoluteBaseUrl = configDir;
|
|
37
47
|
}
|
|
38
48
|
|
|
39
49
|
if (opts.paths) {
|
|
@@ -21,6 +21,16 @@ export class WorkspaceGraph {
|
|
|
21
21
|
const pnpmWorkspacePath = path.join(this.context.cwd, 'pnpm-workspace.yaml');
|
|
22
22
|
|
|
23
23
|
let workspaceGlobs = [];
|
|
24
|
+
this.hoistedDependencies = new Set();
|
|
25
|
+
|
|
26
|
+
// Load hoisted dependencies from root package.json (Knip Issue #1792 fix)
|
|
27
|
+
try {
|
|
28
|
+
const rootPkg = JSON.parse(await fs.readFile(rootPackageJsonPath, 'utf8'));
|
|
29
|
+
const deps = { ...rootPkg.dependencies, ...rootPkg.devDependencies };
|
|
30
|
+
Object.keys(deps).forEach(d => this.hoistedDependencies.add(d));
|
|
31
|
+
} catch (e) {
|
|
32
|
+
// No root package.json or unreadable
|
|
33
|
+
}
|
|
24
34
|
|
|
25
35
|
// Protocol A: Check for pnpm workspace configurations
|
|
26
36
|
try {
|