pkg-scaffold 3.1.3 → 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pkg-scaffold",
|
|
3
|
-
"version": "3.
|
|
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": [
|
package/pkg-scaffold/config.json
CHANGED
|
@@ -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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Returns symbols that are implicitly required/exported by the framework.
|
|
38
|
+
*/
|
|
39
|
+
getRequiredSystemContracts() {
|
|
40
|
+
return ['default'];
|
|
41
|
+
}
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
33
|
-
|
|
37
|
+
if (this.config.supportKnipPlugins) {
|
|
38
|
+
await this.initKnipAdapter();
|
|
39
|
+
}
|
|
34
40
|
}
|
|
35
|
-
}
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
93
|
+
getPlugins() {
|
|
94
|
+
return Array.from(this.plugins.values());
|
|
95
|
+
}
|
|
81
96
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
97
|
+
getPlugin(name) {
|
|
98
|
+
return this.plugins.get(name);
|
|
99
|
+
}
|
|
85
100
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
+
}
|