pkg-scaffold 3.3.3 β†’ 3.3.5

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 CHANGED
@@ -1,41 +1,81 @@
1
- # README
1
+ # πŸ“¦ pkg-scaffold
2
2
 
3
3
  ![Logo](./logo.png)
4
4
 
5
- **The Ultimate Enterprise Codebase Janitor: OXC-Powered, Type-Aware, and Self-Healing.**
5
+ > **The Ultimate Enterprise Codebase Janitor.** Faster than Knip with OXC integration, type-aware analysis, and automated structural healing. Fully standalone - solving what Knip cannot.
6
6
 
7
- ![Version](https://img.shields.io/badge/version-3.1.3-blue.svg) ![License](https://img.shields.io/badge/license-MIT-green.svg) ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/DreamLongYT/pkg-scaffold/npm-publish.yml?branch=main) ![Performance](https://img.shields.io/badge/performance-OXC--Inside-blueviolet.svg)
7
+ ![Version](https://img.shields.io/npm/v/pkg-scaffold) ![License](https://img.shields.io/badge/license-Apache--2.0-green.svg) ![Performance](https://img.shields.io/badge/performance-OXC--Inside-blueviolet.svg)
8
8
 
9
- `pkg-scaffold` is the industry's most advanced codebase optimization engine. Version 3.1.3 marks a massive leap forward, outperforming Knip v6 with a hybrid **OXC + TypeScript** architecture. It doesn't just find dead codeβ€”it safely prunes it and validates your project's integrity through a unique **Self-Healing Loop**.
9
+ `pkg-scaffold` is a next-generation tool designed to declutter your JavaScript and TypeScript projects. It finds unused files, unused dependencies, dead code, circular dependencies, and more. It is built to be a direct and superior competitor to `knip.dev`.
10
10
 
11
- ## Features
11
+ ## πŸš€ Why pkg-scaffold over Knip?
12
12
 
13
- - **Extreme Speed with OXC**: By integrating the Rust-based OXC parser, `pkg-scaffold` achieves a 2-4x performance boost.
14
- - **True Type-Aware Analysis**: Uses the full TypeScript Compiler API to resolve types across your entire project.
15
- - **Automated Self-Healing**: Identifies unused code, removes it, and validates the change through tests.
16
- - **Modular Plugin Ecosystem**: Supports local configurations and custom plugins, including Knip compatibility.
13
+ * **⚑ Blazing Fast:** Powered by `oxc-parser` (Rust-based) for lightning-fast AST traversal. Fallback to TypeScript compiler API when needed.
14
+ * **πŸ”Œ Massive Plugin Ecosystem:** Over 20+ built-in plugins (Next.js, Nuxt, SvelteKit, Tailwind, Jest, Vitest, Playwright, GitHub Actions, Webpack, Babel, Rollup, ESLint, Prettier, Husky, and many more).
15
+ * **πŸ’€ True Dead Code Detection:** Advanced graph-based reachability analysis to find truly dead files and unused exports, even deep within your codebase.
16
+ * **πŸ”„ Circular Dependency Detection:** High-performance Tarjan-based algorithm to detect and report circular dependencies.
17
+ * **πŸ›‘οΈ Supply Chain Guard:** Detects typosquatting and verifies integrity lockfile hashes.
18
+ * **πŸ› οΈ Automated Structural Healing:** Not just reporting, but automatically fixing structural issues (removing dead files, pruning unused dependencies) with git-based rollback protection.
19
+ * **βš™οΈ Flexible Configuration:** Supports `pkg-scaffold.json`, `pkg-scaffold.ts`, `scaffold.config.js`, and more.
17
20
 
18
- ## Getting Started
19
-
20
- ### Installation
21
+ ## πŸ“¦ Installation
21
22
 
22
23
  ```bash
23
- npm install -g pkg-scaffold
24
+ npm install -D pkg-scaffold
25
+ # or
26
+ pnpm add -D pkg-scaffold
27
+ # or
28
+ pnpm add -D pkg-scaffold
24
29
  ```
25
30
 
26
- ### Usage
31
+ ## πŸš€ Usage
32
+
33
+ Run the CLI at the root of your project:
27
34
 
28
35
  ```bash
29
- pkg-scaffold init my-project
30
- pkg-scaffold --fix --test-command 'npm test'
36
+ npx pkg-scaffold --run
31
37
  ```
32
38
 
33
- ## Documentation
39
+ ### CLI Options
40
+
41
+ * `-c, --cwd <path>`: Specify the execution context root directory.
42
+ * `--fix`: Enable atomic code updates, structural file pruning, and active type sanitization.
43
+ * `--no-fix`: Disable direct file manipulation (dry-run reporting mode).
44
+ * `--tsconfig <filename>`: Specify path to custom layout configurations.
45
+ * `--verbose`: Toggle expanded trace telemetry for debug operational diagnostics.
46
+ * `-r, --run`: Execute the primary operational pipeline loop.
47
+ * `-y, --yes`: Skip confirmation prompts.
48
+
49
+ ## βš™οΈ Configuration
50
+
51
+ Create a `pkg-scaffold.json` (or `.js`, `.ts`) in your project root:
52
+
53
+ ```json
54
+ {
55
+ "entryPoints": ["src/index.ts"],
56
+ "exclude": ["node_modules/**", "dist/**", "**/*.test.ts"],
57
+ "rules": {
58
+ "no-unused-exports": "error",
59
+ "no-dead-code": "error"
60
+ }
61
+ }
62
+ ```
63
+
64
+ ## πŸ”Œ Supported Plugins
65
+
66
+ `pkg-scaffold` automatically detects your ecosystem and enables the relevant plugins:
67
+
68
+ * **Frameworks:** Next.js, Nuxt, Remix, SvelteKit, Astro, Vue, Angular
69
+ * **Testing:** Jest, Vitest, Playwright, Cypress
70
+ * **Build Tools:** Webpack, Rollup, Babel, PostCSS, TailwindCSS
71
+ * **Linters/Formatters:** ESLint, Prettier, Commitlint, Lint-Staged, Husky
72
+ * **CI/CD:** GitHub Actions
73
+ * **And more!**
74
+
75
+ ## 🀝 Contributing
34
76
 
35
- - [home](https://dreamlongyt.github.io/pkg-scaffold/)
36
- - [guide](https://dreamlongyt.github.io/pkg-scaffold/guide)
37
- - [references](https://dreamlongyt.github.io/pkg-scaffold/reference)
77
+ Contributions are welcome! Please open an issue or submit a pull request.
38
78
 
39
- ## License
79
+ ## πŸ“„ License
40
80
 
41
- MIT Β© DreamLongYT & The Enhanced Contributors.
81
+ Apache-2.0 License.
package/bin/cli.js CHANGED
@@ -28,7 +28,7 @@ async function bootstrap() {
28
28
  program
29
29
  .name('pkg-scaffold')
30
30
  .description(ansis.cyan('Enterprise-Grade AST Syntax Refactoring & Self-Healing Engine'))
31
- .version(packageJsonContent.version || '3.3.2');
31
+ .version(packageJsonContent.version || '3.3.5');
32
32
 
33
33
  program
34
34
  .option('-c, --cwd <path>', 'Specify the execution context root directory', process.cwd())
@@ -105,9 +105,9 @@ async function bootstrap() {
105
105
  // Load local config if available
106
106
  let localConfig = {};
107
107
  try {
108
- const configPath = path.join(targetCwd, 'pkg-scaffold', 'config.json');
109
- const configData = await fs.readFile(configPath, 'utf8');
110
- localConfig = JSON.parse(configData);
108
+ const { ConfigLoader } = await import('../src/resolution/ConfigLoader.js');
109
+ const loader = new ConfigLoader(targetCwd);
110
+ localConfig = await loader.loadConfig(targetCwd);
111
111
  } catch (e) {}
112
112
 
113
113
  // Merge options with local config
@@ -130,9 +130,9 @@ async function bootstrap() {
130
130
  }, timeoutMs);
131
131
  timeoutTimer.unref(); // Allow process to exit if work finishes
132
132
 
133
- console.log(ansis.bold.green(`\nπŸ“¦ pkg-scaffold v${packageJsonContent.version || '3.3.2'} Engine Activation`));
133
+ console.log(ansis.bold.green(`\nπŸ“¦ pkg-scaffold v${packageJsonContent.version || '3.3.5'} Engine Activation`));
134
134
  console.log(ansis.dim('------------------------------------------------------------'));
135
- console.log(`${ansis.bold('Target Workspace Root :')} ${ansis.blue(path.resolve(options.cwd))}`);
135
+ console.log(`${ansis.bold('Target Workspace Root :')} ${ansis.blue(targetCwd)}`);
136
136
  console.log(`${ansis.bold('Refactoring Mode :')} ${options.fix ? ansis.yellow('Active Fixing & Self-Healing Enabled') : ansis.gray('Dry-Run Reporting Only')}`);
137
137
  console.log(`${ansis.bold('Validation Sandbox :')} ${ansis.magenta(options.testCommand)}`);
138
138
  console.log(ansis.dim('------------------------------------------------------------\n'));
@@ -140,7 +140,7 @@ async function bootstrap() {
140
140
  const { RefactoringEngine } = await import('../src/index.js');
141
141
 
142
142
  const engine = new RefactoringEngine({
143
- cwd: path.resolve(options.cwd),
143
+ cwd: targetCwd,
144
144
  autoFix: options.fix,
145
145
  tsconfig: options.tsconfig,
146
146
  testCommand: options.testCommand,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pkg-scaffold",
3
- "version": "3.3.3",
4
- "description": "The Ultimate Enterprise Codebase Janitor. Faster than Knip with OXC integration, type-aware analysis, and self-healing capabilities. Fully standalone - solving what Knip cannot.",
3
+ "version": "3.3.5",
4
+ "description": "The Ultimate Enterprise Codebase Janitor. Faster than Knip with OXC integration, type-aware analysis, and automated structural healing. Fully standalone - solving what Knip cannot.",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "bin": {
@@ -13,7 +13,7 @@
13
13
  "test": "echo \"Error: no test specified\" && exit 0",
14
14
  "test:stability": "npm run test",
15
15
  "docs:dev": "vitepress dev docs",
16
- "docs:build": "vitepress build docs && cp docs/sitemap.xml docs/.vitepress/dist/ && cp docs/robots.txt docs/.vitepress/dist",
16
+ "docs:build": "vitepress build docs && cp docs/sitemap.xml docs/.vitepress/dist/ && cp docs/robots.txt docs/.vitepress/dist && cp logo.png docs/.vitepress/dist",
17
17
  "docs:preview": "vitepress preview docs"
18
18
  },
19
19
  "keywords": [
@@ -48,7 +48,8 @@
48
48
  "package.json",
49
49
  "packages",
50
50
  "scan",
51
- "self-healing",
51
+
52
+ "structural-healing",
52
53
  "setup",
53
54
  "type",
54
55
  "types",
@@ -0,0 +1,2 @@
1
+ allowBuilds:
2
+ esbuild: set this to true or false
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * ============================================================================
3
- * πŸ“¦ pkg-scaffold v3.3.0: Enterprise In-Memory Codebase State Manifest
3
+ * πŸ“¦ pkg-scaffold v3.3.5: Enterprise In-Memory Codebase State Manifest
4
4
  * ============================================================================
5
5
  * Implements a high-density, centralized graph database context for tracking
6
6
  * software engineering debt, dependencies, types, and vulnerabilities.
@@ -8,6 +8,7 @@
8
8
 
9
9
  import path from 'path';
10
10
  import fs from 'fs/promises';
11
+ import { DependencyProfiler } from './resolution/DependencyProfiler.js';
11
12
 
12
13
  /**
13
14
  * High-Fidelity Graph Element Node representing a single file asset boundary.
@@ -25,6 +26,9 @@ export class GraphNode {
25
26
  this.explicitImports = new Set();
26
27
  this.dynamicImports = new Set();
27
28
  this.importedSymbols = new Set(); // Format: 'specifier:symbol' or 'specifier:*'
29
+ this.jsxComponents = new Set();
30
+ this.jsxProps = new Set();
31
+ this.decorators = new Set();
28
32
 
29
33
  // Internal API Exposed Interfaces (Symbol Name -> ExportMetadata)
30
34
  this.internalExports = new Map();
@@ -39,8 +43,7 @@ export class GraphNode {
39
43
  this.incomingEdges = new Set(); // Set of absolute filePaths depending on this component
40
44
  this.outgoingEdges = new Set(); // Set of absolute internal filePaths this component calls
41
45
 
42
- // Security & Compliance Anomaly Matrices
43
- this.securityThreats = [];
46
+ // Structural Syntax Boundaries
44
47
  this.calculatedDynamicImports = [];
45
48
  this.localSuppressedRules = new Set();
46
49
  this.externalPackageUsage = new Set(); // Tracked third-party package names
@@ -87,6 +90,17 @@ export class GraphNode {
87
90
 
88
91
  // Safe fallback lookup inside string reference caches (e.g., obj['databaseUrl'])
89
92
  if (parentNode.rawStringReferences.has(symbolName)) return true;
93
+
94
+ // Check for JSX component usage
95
+ if (parentNode.jsxComponents.has(symbolName)) return true;
96
+
97
+ // Check for JSX prop usage (e.g., <MyComponent myProp={symbolName} />)
98
+ for (const jsxProp of parentNode.jsxProps) {
99
+ if (jsxProp.endsWith(`:${symbolName}`)) return true;
100
+ }
101
+
102
+ // Check for decorator usage
103
+ if (parentNode.decorators.has(symbolName)) return true;
90
104
  }
91
105
 
92
106
  return false;
@@ -146,6 +160,9 @@ export class EngineContext {
146
160
  };
147
161
  this.usedExternalPackages = new Set(); // Global set of used npm packages
148
162
  this.manifestDependencies = new Map(); // Package.json path -> { dependencies, devDependencies, peerDependencies, optionalDependencies }
163
+
164
+ // DependencyProfiler instance for implicit invocation tracing
165
+ this._depProfiler = new DependencyProfiler(this);
149
166
  }
150
167
 
151
168
  /**
@@ -223,7 +240,7 @@ export class EngineContext {
223
240
  * Processes the entire active dependency map to compile structural issue indices.
224
241
  * Evaluates orphaned components, dead exports, and supply-chain threats.
225
242
  */
226
- generateSummaryReport() {
243
+ async generateSummaryReport() {
227
244
  this.metrics.endTime = Date.now();
228
245
  const durationSeconds = ((this.metrics.endTime - this.metrics.startTime) / 1000).toFixed(2);
229
246
 
@@ -240,7 +257,6 @@ export class EngineContext {
240
257
  structuralIssuesDetected: {
241
258
  deadFiles: [],
242
259
  deadExports: [],
243
- securityThreats: [],
244
260
  unusedDependencies: []
245
261
  },
246
262
  modificationsExecuted: {
@@ -252,6 +268,11 @@ export class EngineContext {
252
268
  for (const [filePath, node] of this.graph.entries()) {
253
269
  // Skip package control files from standard structural dead-code checks
254
270
  if (filePath.endsWith('package.json')) continue;
271
+
272
+ // Fix: Always track external package usage from all files, even if they are orphaned,
273
+ // to prevent false-positive unused dependency reports.
274
+ node.externalPackageUsage.forEach(pkg => this.usedExternalPackages.add(pkg));
275
+
255
276
  if (this.isPathIgnored(filePath)) continue;
256
277
 
257
278
  const relativePath = path.relative(this.cwd, filePath);
@@ -285,28 +306,34 @@ export class EngineContext {
285
306
  }
286
307
  }
287
308
 
288
- // Category C: Security Vulnerabilities
289
- if (node.securityThreats && node.securityThreats.length > 0) {
290
- node.securityThreats.forEach(threat => {
291
- summary.structuralIssuesDetected.securityThreats.push({
292
- file: relativePath,
293
- identifier: threat.variableKey,
294
- riskCode: threat.riskCode || 'HIGH_RISK_SECRET_LEAK',
295
- entropy: threat.entropyValue,
296
- line: threat.line || 1
297
- });
298
- });
299
- }
300
309
  }
301
310
 
302
311
  // Category D: Unused Dependencies Audit (Enhanced Classification)
303
312
  for (const [manifestPath, manifestData] of this.manifestDependencies.entries()) {
304
313
  const relativeManifest = path.relative(this.cwd, manifestPath);
305
-
314
+ const packageRoot = path.dirname(manifestPath);
315
+
316
+ // Collect packages that are implicitly used via scripts / config files
317
+ const implicitlyUsed = await this._depProfiler.traceImplicitInvocations(packageRoot);
318
+
319
+ // Resolve peer dependencies of all used packages so they are not flagged
320
+ const allUsedForPeerResolution = new Set([...this.usedExternalPackages, ...implicitlyUsed]);
321
+ const peerDepsOfUsed = await this._depProfiler.resolvePeerDependencies(allUsedForPeerResolution, packageRoot);
322
+
306
323
  const checkDeps = (deps, type) => {
307
324
  if (!deps) return;
308
325
  for (const dep of deps) {
309
- if (!this.usedExternalPackages.has(dep)) {
326
+ // Skip peer and optional dependencies – they are never "unused" in the
327
+ // traditional sense because they are not required to be imported directly.
328
+ if (this._depProfiler.shouldExcludeFromUnusedCheck(dep, type)) {
329
+ continue;
330
+ }
331
+
332
+ const isUsedInCode = this.usedExternalPackages.has(dep);
333
+ const isUsedImplicitly = implicitlyUsed.has(dep);
334
+ const isRequiredAsPeer = peerDepsOfUsed.has(dep);
335
+
336
+ if (!isUsedInCode && !isUsedImplicitly && !isRequiredAsPeer) {
310
337
  summary.structuralIssuesDetected.unusedDependencies.push({
311
338
  manifest: relativeManifest,
312
339
  package: dep,
@@ -319,6 +346,7 @@ export class EngineContext {
319
346
 
320
347
  checkDeps(manifestData.dependencies, 'dependency');
321
348
  checkDeps(manifestData.devDependencies, 'devDependency');
349
+ // peerDependencies and optionalDependencies are excluded via shouldExcludeFromUnusedCheck
322
350
  checkDeps(manifestData.peerDependencies, 'peerDependency');
323
351
  checkDeps(manifestData.optionalDependencies, 'optionalDependency');
324
352
  }