pkg-scaffold 3.1.2 β†’ 3.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 CHANGED
@@ -1,126 +1,41 @@
1
- # πŸ“¦ pkg-scaffold v3.1.2
1
+ # README
2
2
 
3
- **The Ultimate Enterprise Codebase Janitor: OXC-Powered, Type-Aware, and Self-Healing.**
4
-
5
- [![npm version](https://img.shields.io/npm/v/pkg-scaffold.svg?style=flat&color=CB3837)](https://www.npmjs.com/package/pkg-scaffold)
6
- [![License](https://img.shields.io/badge/license-MIT-orange.svg?style=flat)](LICENSE)
7
- [![Performance](https://img.shields.io/badge/performance-OXC--Inside-blueviolet.svg?style=flat)](https://oxc.rs/)
8
-
9
- `pkg-scaffold` is the industry's most advanced codebase optimization engine. Version 3.1.0 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**.
10
-
11
- ---
12
-
13
- ## πŸš€ Why pkg-scaffold@3.1.2?
3
+ ![Logo](./logo.png)
14
4
 
15
- ### 1. Extreme Speed with OXC
16
- By integrating the Rust-based **OXC (Oxc-Parser & Oxc-Resolver)**, pkg-scaffold v3.1.0 achieves a **2-4x performance boost** over previous versions, matching and often exceeding the speed of Knip v6 for single-pass analysis.
17
-
18
- ### 2. True Type-Aware Analysis
19
- Unlike basic linters, pkg-scaffold uses the full **TypeScript Compiler API** to resolve types across your entire project. This ensures that implicitly implemented interfaces, extended objects, and global ambient overrides are correctly tracked, reducing false positives to near zero.
20
-
21
- ### 3. Automated Self-Healing (The "Fix" Loop)
22
- This is the "Knip-Killer" feature. pkg-scaffold doesn't just give you a report; it:
23
- 1. **Identifies** unused code.
24
- 2. **Prunes** it automatically.
25
- 3. **Validates** the change by running your test suite.
26
- 4. **Self-Heals** by rolling back immediately if a test fails.
27
-
28
- ### 4. Modular Plugin Ecosystem
29
- With a dedicated `/pkg-scaffold` directory, you can now manage local configurations and custom plugins. We even support **Knip-style plugins**, allowing you to leverage the existing ecosystem while using our superior engine.
5
+ **The Ultimate Enterprise Codebase Janitor: OXC-Powered, Type-Aware, and Self-Healing.**
30
6
 
31
- ---
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)
32
8
 
33
- ## βš”οΈ Competitive Analysis: pkg-scaffold vs. The Rest
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**.
34
10
 
35
- | Feature | **pkg-scaffold v3.1.0** | Knip v6 | Depcheck |
36
- | :--- | :---: | :---: | :---: |
37
- | **Parsing Engine** | ⚑ **OXC (Rust) + Hybrid TS** | ⚑ OXC (Rust) | ⚠️ Regex/Loose |
38
- | **Type-Awareness** | βœ… **Full Program API** | βœ… Yes | ❌ No |
39
- | **Automated Pruning** | βœ… **Native & Safe** | ⚠️ Experimental | ❌ No |
40
- | **Self-Healing Loop** | βœ… **Yes (Auto-Rollback)** | ❌ No | ❌ No |
41
- | **Plugin Architecture** | βœ… **Modular + Knip-Compat** | βœ… Built-in Only | ❌ No |
42
- | **Namespace Tracking** | βœ… **Sub-Symbol Level** | βœ… Yes | ❌ No |
43
- | **Security Audit** | βœ… **Dynamic Registry Check** | ❌ No | ❌ No |
11
+ ## Features
44
12
 
45
- ### Where Knip.dev is still strong:
46
- - **Maturity:** Knip has a larger set of pre-configured community plugins (150+).
47
- - **Ecosystem:** More integrations with niche tools and legacy build systems.
48
- *However, pkg-scaffold's Knip-compatibility layer is designed to bridge this gap.*
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.
49
17
 
50
- ---
18
+ ## Getting Started
51
19
 
52
- ## πŸ› οΈ Installation & Usage
20
+ ### Installation
53
21
 
54
- ### 1. Add to your project
55
22
  ```bash
56
- npm install --save-dev pkg-scaffold
23
+ npm install -g pkg-scaffold
57
24
  ```
58
25
 
59
- ### 2. Configure (Optional)
60
- Create a `pkg-scaffold/config.json` to customize your experience:
61
- ```json
62
- {
63
- "interface": "CLI",
64
- "options": {
65
- "fastMode": true,
66
- "selfHealing": true
67
- }
68
- }
69
- ```
26
+ ### Usage
70
27
 
71
- ### 3. Run the Engine
72
- Add this to your `package.json` scripts:
73
- ```json
74
- "scripts": {
75
- "pkg-scaffold:run": "pkg-scaffold --fix --test-command 'npm test'"
76
- }
28
+ ```bash
29
+ pkg-scaffold init my-project
30
+ pkg-scaffold --fix --test-command 'npm test'
77
31
  ```
78
32
 
79
- ---
80
-
81
- ## πŸ“‚ Project Structure
82
-
83
- - **`/pkg-scaffold/config.json`**: Your local settings (CLI/GUI, Plugin Toggles).
84
- - **`/pkg-scaffold/plugins/`**: Drop your custom or Knip-style plugins here.
85
- - **`/docs/`**: Full [Plugin Development Guide](https://dreamlongyt.github.io/pkg-scaffold/).
86
-
87
- ---
88
-
89
- ## πŸ›‘οΈ Suppression
33
+ ## Documentation
90
34
 
91
- Protect specific code from the janitor:
92
-
93
- ```javascript
94
- /**
95
- * @scaffold-suppress
96
- */
97
- export const internalHelper = () => { /* Safe from pruning */ };
98
- ```
99
-
100
- ---
101
- ## Depencies
102
- ```json
103
- "ansis": "^3.0.0",
104
- "commander": "^12.0.0",
105
- "enhanced-resolve": "^5.16.0",
106
- "execa": "^8.0.1",
107
- "oxc-parser": "^0.135.0",
108
- "oxc-resolver": "^11.20.0",
109
- "ramda": "^0.29.1",
110
- "yocto-spinner": "^0.1.0"
111
- ```
112
- ---
113
- ## DevDepencies
114
- ```json
115
- "@types/node": "^25.9.3",
116
- "express": "^5.2.1",
117
- "knip": "^6.16.1", //Used to make Plugins from knip compitable with the code
118
- "lodash": "^4.18.1", //For Analysing
119
- "typescript": "^6.0.3", //For Analysing
120
- "vitepress": "^1.6.4" //For the Documentation
121
- ```
122
- ---
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)
123
38
 
124
- ## πŸ“œ License
39
+ ## License
125
40
 
126
41
  MIT Β© DreamLongYT & The Enhanced Contributors.
package/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  /**
4
4
  * ============================================================================
5
- * πŸ“¦ pkg-scaffold v3.1.2: Ultimate Enterprise Codebase Janitor & Self-Healing Engine
5
+ * πŸ“¦ pkg-scaffold v3.1.3: Ultimate Enterprise Codebase Janitor & Self-Healing Engine
6
6
  * ============================================================================
7
7
  * * Eine hochgradig integrierte Code-Analyse- und Projektbootstrapping-Engine.
8
8
  * Kombiniert rekursive Erreichbarkeitsanalysen (Reachability Graphs) auf
package/logo.png ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pkg-scaffold",
3
- "version": "3.1.2",
3
+ "version": "3.2.0",
4
4
  "description": "The ultimate enterprise-grade codebase janitor. Faster than Knip v6 with OXC integration, type-aware analysis, and self-healing capabilities.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -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",
16
+ "docs:build": "vitepress build docs && cp docs/sitemap.xml docs/.vitepress/dist/ && cp docs/robots.txt docs/.vitepress/dist",
17
17
  "docs:preview": "vitepress preview docs"
18
18
  },
19
19
  "keywords": [
@@ -20,6 +20,7 @@
20
20
  "nuxt",
21
21
  "remix",
22
22
  "sveltekit",
23
- "astro"
23
+ "astro",
24
+ "typescript"
24
25
  ]
25
26
  }
@@ -1,53 +1,71 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+
1
4
  /**
2
5
  * Base class for all pkg-scaffold plugins.
3
6
  * Defines the contract for ecosystem detection and entry point mapping.
7
+ * Version 3.2.0: Added support for dynamic custom getters.
4
8
  */
5
9
  export class BasePlugin {
6
- constructor(context) {
7
- this.context = context;
8
- }
10
+ constructor(context) {
11
+ this.context = context;
12
+ this.customGetters = new Map();
13
+ }
14
+
15
+ /**
16
+ * Unique identifier for the plugin (e.g., 'nextjs').
17
+ */
18
+ get name() {
19
+ throw new Error('Plugin must implement name getter');
20
+ }
9
21
 
10
- /**
11
- * Unique identifier for the plugin (e.g., 'nextjs').
12
- */
13
- get name() {
14
- throw new Error('Plugin must implement name getter');
15
- }
22
+ /**
23
+ * Returns a list of configuration files that indicate this ecosystem is active.
24
+ */
25
+ getConfigFiles() {
26
+ return [];
27
+ }
16
28
 
17
- /**
18
- * Returns a list of configuration files that indicate this ecosystem is active.
19
- */
20
- getConfigFiles() {
21
- return [];
22
- }
29
+ /**
30
+ * Returns regex patterns for files that should be treated as entry points.
31
+ */
32
+ getRoutePatterns() {
33
+ return [];
34
+ }
23
35
 
24
- /**
25
- * Returns regex patterns for files that should be treated as entry points.
26
- */
27
- getRoutePatterns() {
28
- return [];
29
- }
36
+ /**
37
+ * Returns symbols that are implicitly required/exported by the framework.
38
+ */
39
+ getRequiredSystemContracts() {
40
+ return ['default'];
41
+ }
30
42
 
31
- /**
32
- * Returns symbols that are implicitly required/exported by the framework.
33
- */
34
- getRequiredSystemContracts() {
35
- return ['default'];
36
- }
43
+ /**
44
+ * Version 3.2.0: Dynamic getter for custom plugin properties.
45
+ * @param {string} key - The property key to retrieve
46
+ * @returns {any} The value of the custom property
47
+ */
48
+ get(key) {
49
+ const methodName = `get${key.charAt(0).toUpperCase() + key.slice(1)}`;
50
+ if (typeof this[methodName] === 'function') {
51
+ return this[methodName]();
52
+ }
53
+ return this.customGetters.get(key);
54
+ }
37
55
 
38
- /**
39
- * Optional: Logic to detect if the plugin should be active in the given directory.
40
- */
41
- async isActive(baseDir) {
42
- const configFiles = this.getConfigFiles();
43
- for (const file of configFiles) {
44
- try {
45
- await fs.access(path.join(baseDir, file));
46
- return true;
47
- } catch {
48
- continue;
49
- }
56
+ /**
57
+ * Optional: Logic to detect if the plugin should be active in the given directory.
58
+ */
59
+ async isActive(baseDir) {
60
+ const configFiles = this.getConfigFiles();
61
+ for (const file of configFiles) {
62
+ try {
63
+ await fs.access(path.join(baseDir, file));
64
+ return true;
65
+ } catch {
66
+ continue;
67
+ }
68
+ }
69
+ return false;
50
70
  }
51
- return false;
52
- }
53
71
  }
@@ -4,92 +4,107 @@ import { pathToFileURL } from 'url';
4
4
 
5
5
  /**
6
6
  * Advanced Plugin Registry supporting Builtin, Custom, and Knip-style plugins.
7
+ * Version 3.2.0: Enhanced with support for dynamic custom getters.
7
8
  */
8
9
  export class PluginRegistry {
9
- constructor(context) {
10
- this.context = context;
11
- this.plugins = new Map();
12
- this.config = null;
13
- }
14
-
15
- async init(projectRoot) {
16
- const configPath = path.join(projectRoot, 'pkg-scaffold', 'config.json');
17
- try {
18
- const configRaw = await fs.readFile(configPath, 'utf8');
19
- this.config = JSON.parse(configRaw);
20
- } catch (e) {
21
- this.config = { useBuiltinPlugins: true, useCustomPlugins: true, supportKnipPlugins: true };
10
+ constructor(context) {
11
+ this.context = context;
12
+ this.plugins = new Map();
13
+ this.config = null;
22
14
  }
23
15
 
24
- if (this.config.useBuiltinPlugins) {
25
- await this.loadBuiltinPlugins();
26
- }
16
+ async init(projectRoot) {
17
+ const configPath = path.join(projectRoot, 'pkg-scaffold', 'config.json');
18
+ try {
19
+ const configRaw = await fs.readFile(configPath, 'utf8');
20
+ this.config = JSON.parse(configRaw);
21
+ } catch (e) {
22
+ this.config = {
23
+ useBuiltinPlugins: true,
24
+ useCustomPlugins: true,
25
+ supportKnipPlugins: true
26
+ };
27
+ }
27
28
 
28
- if (this.config.useCustomPlugins) {
29
- await this.loadCustomPlugins(projectRoot);
30
- }
29
+ if (this.config.useBuiltinPlugins) {
30
+ await this.loadBuiltinPlugins();
31
+ }
32
+
33
+ if (this.config.useCustomPlugins) {
34
+ await this.loadCustomPlugins(projectRoot);
35
+ }
31
36
 
32
- if (this.config.supportKnipPlugins) {
33
- await this.initKnipAdapter();
37
+ if (this.config.supportKnipPlugins) {
38
+ await this.initKnipAdapter();
39
+ }
34
40
  }
35
- }
36
41
 
37
- async loadBuiltinPlugins() {
38
- const { NextJsPlugin } = await import('./ecosystems/NextJsPlugin.js');
39
- const { NuxtPlugin, RemixPlugin, SvelteKitPlugin, AstroPlugin } = await import('./ecosystems/GenericPlugins.js');
42
+ async loadBuiltinPlugins() {
43
+ const { NextJsPlugin } = await import('./ecosystems/NextJsPlugin.js');
44
+ const { NuxtPlugin, RemixPlugin, SvelteKitPlugin, AstroPlugin } = await import('./ecosystems/GenericPlugins.js');
45
+ const { TypeScriptPlugin } = await import('./ecosystems/TypeScriptPlugin.js');
40
46
 
41
- const builtins = [
42
- new NextJsPlugin(this.context),
43
- new NuxtPlugin(this.context),
44
- new RemixPlugin(this.context),
45
- new SvelteKitPlugin(this.context),
46
- new AstroPlugin(this.context)
47
- ];
47
+ const builtins = [
48
+ new NextJsPlugin(this.context),
49
+ new NuxtPlugin(this.context),
50
+ new RemixPlugin(this.context),
51
+ new SvelteKitPlugin(this.context),
52
+ new AstroPlugin(this.context),
53
+ new TypeScriptPlugin(this.context)
54
+ ];
48
55
 
49
- builtins.forEach(p => {
50
- if (!this.config.enabledPlugins || this.config.enabledPlugins.includes(p.name)) {
51
- this.register(p);
52
- }
53
- });
54
- }
56
+ builtins.forEach(p => {
57
+ if (!this.config.enabledPlugins || this.config.enabledPlugins.includes(p.name)) {
58
+ this.register(p);
59
+ }
60
+ });
61
+ }
62
+
63
+ async loadCustomPlugins(projectRoot) {
64
+ const pluginsDir = path.join(projectRoot, 'pkg-scaffold', 'plugins');
65
+ try {
66
+ const files = await fs.readdir(pluginsDir);
67
+ for (const file of files) {
68
+ if (file.endsWith('.js') || file.endsWith('.mjs')) {
69
+ const pluginModule = await import(pathToFileURL(path.join(pluginsDir, file)).href);
70
+ const PluginClass = pluginModule.default || pluginModule;
71
+ const pluginInstance = new PluginClass(this.context);
55
72
 
56
- async loadCustomPlugins(projectRoot) {
57
- const pluginsDir = path.join(projectRoot, 'pkg-scaffold', 'plugins');
58
- try {
59
- const files = await fs.readdir(pluginsDir);
60
- for (const file of files) {
61
- if (file.endsWith('.js') || file.endsWith('.mjs')) {
62
- const pluginModule = await import(pathToFileURL(path.join(pluginsDir, file)).href);
63
- const PluginClass = pluginModule.default || pluginModule;
64
- this.register(new PluginClass(this.context));
73
+ const version = pluginInstance.get('version');
74
+ if (version && this.context.verbose) {
75
+ console.log(`[PluginRegistry] Loaded ${pluginInstance.name} v${version}`);
76
+ }
77
+ this.register(pluginInstance);
78
+ }
79
+ }
80
+ } catch (e) {
81
+ // No custom plugins or dir missing
65
82
  }
66
- }
67
- } catch (e) {
68
- // No custom plugins or dir missing
69
83
  }
70
- }
71
84
 
72
- async initKnipAdapter() {
73
- // This adapter allows running Knip-style plugins by wrapping them
74
- // In a real scenario, this would interface with knip's plugin API
75
- this.context.knipCompatible = true;
76
- }
85
+ async initKnipAdapter() {
86
+ this.context.knipCompatible = true;
87
+ }
88
+
89
+ register(plugin) {
90
+ this.plugins.set(plugin.name, plugin);
91
+ }
77
92
 
78
- register(plugin) {
79
- this.plugins.set(plugin.name, plugin);
80
- }
93
+ getPlugins() {
94
+ return Array.from(this.plugins.values());
95
+ }
81
96
 
82
- getPlugins() {
83
- return Array.from(this.plugins.values());
84
- }
97
+ getPlugin(name) {
98
+ return this.plugins.get(name);
99
+ }
85
100
 
86
- async getActivePlugins(baseDir) {
87
- const active = [];
88
- for (const plugin of this.plugins.values()) {
89
- if (await plugin.isActive(baseDir)) {
90
- active.push(plugin);
91
- }
101
+ async getActivePlugins(baseDir) {
102
+ const active = [];
103
+ for (const plugin of this.plugins.values()) {
104
+ if (await plugin.isActive(baseDir)) {
105
+ active.push(plugin);
106
+ }
107
+ }
108
+ return active;
92
109
  }
93
- return active;
94
- }
95
110
  }
@@ -0,0 +1,56 @@
1
+ import path from 'path';
2
+ import fs from 'fs/promises';
3
+ import { BasePlugin } from '../BasePlugin.js';
4
+
5
+ /**
6
+ * TypeScript Plugin for pkg-scaffold.
7
+ * Handles tsconfig.json detection and TypeScript-specific entry points.
8
+ */
9
+ export class TypeScriptPlugin extends BasePlugin {
10
+ get name() {
11
+ return 'typescript';
12
+ }
13
+
14
+ getConfigFiles() {
15
+ return ['tsconfig.json', 'tsconfig.base.json', 'tsconfig.eslint.json'];
16
+ }
17
+
18
+ getRoutePatterns() {
19
+ // Common TypeScript entry points and declaration files
20
+ return [
21
+ /src\/index\.ts$/,
22
+ /src\/main\.ts$/,
23
+ /src\/lib\.ts$/,
24
+ /.*\.d\.ts$/
25
+ ];
26
+ }
27
+
28
+ getRequiredSystemContracts() {
29
+ // TypeScript specific implicit exports or requirements
30
+ return ['default'];
31
+ }
32
+
33
+ /**
34
+ * Custom Getter for v3.2.0: Get the compiler version from the project.
35
+ */
36
+ async getCompilerVersion() {
37
+ try {
38
+ const packageJsonPath = path.join(this.context.cwd, 'package.json');
39
+ const content = await fs.readFile(packageJsonPath, 'utf8');
40
+ const pkg = JSON.parse(content);
41
+ return pkg.devDependencies?.typescript || pkg.dependencies?.typescript || 'unknown';
42
+ } catch {
43
+ return 'not installed';
44
+ }
45
+ }
46
+
47
+ async isActive(baseDir) {
48
+ for (const file of this.getConfigFiles()) {
49
+ try {
50
+ await fs.access(path.join(baseDir, file));
51
+ return true;
52
+ } catch {}
53
+ }
54
+ return false;
55
+ }
56
+ }