@libria/scaffold 0.2.0 → 0.2.2
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/.clean-publish.hash +1 -1
- package/dist/cli/cli.mjs +444 -3
- package/dist/cli/cli.mjs.map +1 -1
- package/dist/cli/index.cjs +242 -2
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +39 -8
- package/dist/cli/index.d.mts +39 -8
- package/dist/cli/index.mjs +198 -2
- package/dist/cli/index.mjs.map +1 -1
- package/package.json +7 -2
- package/dist/templates/angular/index.cjs +0 -20
- package/dist/templates/angular/index.cjs.map +0 -1
- package/dist/templates/angular/index.d.cts +0 -31
- package/dist/templates/angular/index.d.mts +0 -31
- package/dist/templates/angular/index.mjs +0 -20
- package/dist/templates/angular/index.mjs.map +0 -1
- package/dist/templates/angular/plugin.json +0 -5
- package/dist/templates/ts-lib/files/.prettierrc +0 -8
- package/dist/templates/ts-lib/files/LICENSE +0 -21
- package/dist/templates/ts-lib/files/README.md +0 -58
- package/dist/templates/ts-lib/files/eslint.config.mjs +0 -106
- package/dist/templates/ts-lib/files/package.json +0 -61
- package/dist/templates/ts-lib/files/src/index.ts +0 -31
- package/dist/templates/ts-lib/files/tests/my.test.ts +0 -44
- package/dist/templates/ts-lib/files/tsconfig.json +0 -22
- package/dist/templates/ts-lib/files/tsconfig.test.json +0 -8
- package/dist/templates/ts-lib/files/tsdown.config.ts +0 -10
- package/dist/templates/ts-lib/files/vitest.config.ts +0 -12
- package/dist/templates/ts-lib/index.cjs +0 -19
- package/dist/templates/ts-lib/index.cjs.map +0 -1
- package/dist/templates/ts-lib/index.d.cts +0 -29
- package/dist/templates/ts-lib/index.d.mts +0 -29
- package/dist/templates/ts-lib/index.mjs +0 -19
- package/dist/templates/ts-lib/index.mjs.map +0 -1
- package/dist/templates/ts-lib/plugin.json +0 -5
package/dist/cli/index.cjs
CHANGED
|
@@ -1,3 +1,243 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
value: mod,
|
|
24
|
+
enumerable: true
|
|
25
|
+
}) : target, mod));
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
let fs_promises = require("fs/promises");
|
|
29
|
+
fs_promises = __toESM(fs_promises);
|
|
30
|
+
let path = require("path");
|
|
31
|
+
path = __toESM(path);
|
|
32
|
+
let fs_extra = require("fs-extra");
|
|
33
|
+
fs_extra = __toESM(fs_extra);
|
|
34
|
+
let node_child_process = require("node:child_process");
|
|
35
|
+
let node_util = require("node:util");
|
|
36
|
+
|
|
37
|
+
//#region src/core/types.ts
|
|
38
|
+
const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = "scaffold-template";
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/config.ts
|
|
42
|
+
const CONFIG_FILENAME = ".lbscaffold";
|
|
43
|
+
/**
|
|
44
|
+
* Find the config file by searching up the directory tree
|
|
45
|
+
*/
|
|
46
|
+
async function findConfigPath(startDir = process.cwd()) {
|
|
47
|
+
let currentDir = startDir;
|
|
48
|
+
while (true) {
|
|
49
|
+
const configPath = path.default.join(currentDir, CONFIG_FILENAME);
|
|
50
|
+
try {
|
|
51
|
+
await fs_promises.default.access(configPath);
|
|
52
|
+
return configPath;
|
|
53
|
+
} catch {
|
|
54
|
+
const parentDir = path.default.dirname(currentDir);
|
|
55
|
+
if (parentDir === currentDir) return null;
|
|
56
|
+
currentDir = parentDir;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get the default config path in the current directory
|
|
62
|
+
*/
|
|
63
|
+
function getDefaultConfigPath() {
|
|
64
|
+
return path.default.join(process.cwd(), CONFIG_FILENAME);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Load the config file
|
|
68
|
+
*/
|
|
69
|
+
async function loadConfig(configPath) {
|
|
70
|
+
const resolvedPath = configPath ?? await findConfigPath();
|
|
71
|
+
if (!resolvedPath) return {};
|
|
72
|
+
try {
|
|
73
|
+
const content = await fs_promises.default.readFile(resolvedPath, "utf-8");
|
|
74
|
+
return JSON.parse(content);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
if (error.code === "ENOENT") return {};
|
|
77
|
+
throw new Error(`Failed to load config from ${resolvedPath}: ${error.message}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Save the config file
|
|
82
|
+
*/
|
|
83
|
+
async function saveConfig(config, configPath) {
|
|
84
|
+
const resolvedPath = configPath ?? getDefaultConfigPath();
|
|
85
|
+
await fs_promises.default.writeFile(resolvedPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Initialize a new config file
|
|
89
|
+
*/
|
|
90
|
+
async function initConfig(configPath) {
|
|
91
|
+
const resolvedPath = configPath ?? getDefaultConfigPath();
|
|
92
|
+
try {
|
|
93
|
+
await fs_promises.default.access(resolvedPath);
|
|
94
|
+
throw new Error(`Config file already exists at ${resolvedPath}`);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
if (error.code !== "ENOENT") throw error;
|
|
97
|
+
}
|
|
98
|
+
await saveConfig({
|
|
99
|
+
plugins: ["./plugins/**"],
|
|
100
|
+
packages: []
|
|
101
|
+
}, resolvedPath);
|
|
102
|
+
return resolvedPath;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Add a plugin glob pattern to the config
|
|
106
|
+
*/
|
|
107
|
+
async function addPluginGlob(glob, configPath) {
|
|
108
|
+
const config = await loadConfig(configPath);
|
|
109
|
+
if (!config.plugins) config.plugins = [];
|
|
110
|
+
if (config.plugins.includes(glob)) throw new Error(`Plugin pattern '${glob}' already exists in config`);
|
|
111
|
+
config.plugins.push(glob);
|
|
112
|
+
await saveConfig(config, configPath ?? await findConfigPath() ?? getDefaultConfigPath());
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Remove a plugin glob pattern from the config
|
|
116
|
+
*/
|
|
117
|
+
async function removePluginGlob(glob, configPath) {
|
|
118
|
+
const resolvedPath = configPath ?? await findConfigPath();
|
|
119
|
+
if (!resolvedPath) throw new Error("No config file found");
|
|
120
|
+
const config = await loadConfig(resolvedPath);
|
|
121
|
+
if (!config.plugins || !config.plugins.includes(glob)) throw new Error(`Plugin pattern '${glob}' not found in config`);
|
|
122
|
+
config.plugins = config.plugins.filter((p) => p !== glob);
|
|
123
|
+
await saveConfig(config, resolvedPath);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* List all plugin glob patterns in the config
|
|
127
|
+
*/
|
|
128
|
+
async function listPluginGlobs(configPath) {
|
|
129
|
+
return (await loadConfig(configPath)).plugins ?? [];
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get absolute plugin paths from config globs
|
|
133
|
+
*/
|
|
134
|
+
async function getPluginPaths(configPath) {
|
|
135
|
+
const resolvedConfigPath = configPath ?? await findConfigPath();
|
|
136
|
+
if (!resolvedConfigPath) return [];
|
|
137
|
+
const config = await loadConfig(resolvedConfigPath);
|
|
138
|
+
const configDir = path.default.dirname(resolvedConfigPath);
|
|
139
|
+
return (config.plugins ?? []).map((glob) => {
|
|
140
|
+
if (path.default.isAbsolute(glob)) return glob.replace(/\\/g, "/");
|
|
141
|
+
return path.default.resolve(configDir, glob).replace(/\\/g, "/");
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Add an npm package name to the config
|
|
146
|
+
*/
|
|
147
|
+
async function addPackage(packageName, configPath) {
|
|
148
|
+
const config = await loadConfig(configPath);
|
|
149
|
+
if (!config.packages) config.packages = [];
|
|
150
|
+
if (config.packages.includes(packageName)) throw new Error(`Package '${packageName}' already exists in config`);
|
|
151
|
+
config.packages.push(packageName);
|
|
152
|
+
await saveConfig(config, configPath ?? await findConfigPath() ?? getDefaultConfigPath());
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Remove an npm package name from the config
|
|
156
|
+
*/
|
|
157
|
+
async function removePackage(packageName, configPath) {
|
|
158
|
+
const resolvedPath = configPath ?? await findConfigPath();
|
|
159
|
+
if (!resolvedPath) throw new Error("No config file found");
|
|
160
|
+
const config = await loadConfig(resolvedPath);
|
|
161
|
+
if (!config.packages || !config.packages.includes(packageName)) throw new Error(`Package '${packageName}' not found in config`);
|
|
162
|
+
config.packages = config.packages.filter((p) => p !== packageName);
|
|
163
|
+
await saveConfig(config, resolvedPath);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* List all npm package names in the config
|
|
167
|
+
*/
|
|
168
|
+
async function listPackages(configPath) {
|
|
169
|
+
return (await loadConfig(configPath)).packages ?? [];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/utils/replace-placeholders.ts
|
|
174
|
+
async function replacePlaceholders(targetDir, replacements) {
|
|
175
|
+
async function replaceInFiles(folder) {
|
|
176
|
+
const entries = await fs_extra.default.readdir(folder);
|
|
177
|
+
for (const entry of entries) {
|
|
178
|
+
const fullPath = path.default.join(folder, entry);
|
|
179
|
+
if ((await fs_extra.default.stat(fullPath)).isDirectory()) await replaceInFiles(fullPath);
|
|
180
|
+
else if (/\.(ts|js|json|md|txt)$/i.test(entry)) {
|
|
181
|
+
let content = await fs_extra.default.readFile(fullPath, "utf-8");
|
|
182
|
+
for (const [placeholder, value] of Object.entries(replacements)) content = content.replaceAll(placeholder, value);
|
|
183
|
+
await fs_extra.default.writeFile(fullPath, content, "utf-8");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
await replaceInFiles(targetDir);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
//#endregion
|
|
191
|
+
//#region src/utils/resolve-package.ts
|
|
192
|
+
const execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
|
|
193
|
+
/**
|
|
194
|
+
* Resolve an npm package name to its installed root directory.
|
|
195
|
+
* Tries local node_modules first (relative to cwd), then global.
|
|
196
|
+
*/
|
|
197
|
+
async function resolvePackageDir(packageName) {
|
|
198
|
+
const localCandidate = path.default.join(process.cwd(), "node_modules", packageName);
|
|
199
|
+
try {
|
|
200
|
+
await fs_promises.default.access(path.default.join(localCandidate, "package.json"));
|
|
201
|
+
return localCandidate;
|
|
202
|
+
} catch {}
|
|
203
|
+
const globalDir = await getGlobalNodeModulesDir();
|
|
204
|
+
if (globalDir) {
|
|
205
|
+
const globalCandidate = path.default.join(globalDir, packageName);
|
|
206
|
+
try {
|
|
207
|
+
await fs_promises.default.access(path.default.join(globalCandidate, "package.json"));
|
|
208
|
+
return globalCandidate;
|
|
209
|
+
} catch {}
|
|
210
|
+
}
|
|
211
|
+
throw new Error(`Package '${packageName}' not found. Install it locally (npm install ${packageName}) or globally (npm install -g ${packageName}).`);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Get the global node_modules directory using npm root -g.
|
|
215
|
+
*/
|
|
216
|
+
async function getGlobalNodeModulesDir() {
|
|
217
|
+
try {
|
|
218
|
+
const { stdout } = await execFileAsync("npm", ["root", "-g"], { encoding: "utf-8" });
|
|
219
|
+
const dir = stdout.trim();
|
|
220
|
+
await fs_promises.default.access(dir);
|
|
221
|
+
return dir;
|
|
222
|
+
} catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
//#endregion
|
|
228
|
+
exports.SCAFFOLD_TEMPLATE_PLUGIN_TYPE = SCAFFOLD_TEMPLATE_PLUGIN_TYPE;
|
|
229
|
+
exports.addPackage = addPackage;
|
|
230
|
+
exports.addPluginGlob = addPluginGlob;
|
|
231
|
+
exports.findConfigPath = findConfigPath;
|
|
232
|
+
exports.getDefaultConfigPath = getDefaultConfigPath;
|
|
233
|
+
exports.getPluginPaths = getPluginPaths;
|
|
234
|
+
exports.initConfig = initConfig;
|
|
235
|
+
exports.listPackages = listPackages;
|
|
236
|
+
exports.listPluginGlobs = listPluginGlobs;
|
|
237
|
+
exports.loadConfig = loadConfig;
|
|
238
|
+
exports.removePackage = removePackage;
|
|
239
|
+
exports.removePluginGlob = removePluginGlob;
|
|
240
|
+
exports.replacePlaceholders = replacePlaceholders;
|
|
241
|
+
exports.resolvePackageDir = resolvePackageDir;
|
|
242
|
+
exports.saveConfig = saveConfig;
|
|
3
243
|
//# sourceMappingURL=index.cjs.map
|
package/dist/cli/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["fs"],"sources":["../../src/core/scaffold-template-plugin.ts","../../src/config.ts"],"sourcesContent":["import { ScaffoldTemplatePluginOptions } from './types';\r\n\r\nexport const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = 'scaffold-template';\r\n\r\nexport interface ScaffoldTemplatePlugin {\r\n argument: string;\r\n\r\n execute(options: ScaffoldTemplatePluginOptions): Promise<void>;\r\n}\r\n","import fs from 'fs/promises';\r\nimport path from 'path';\r\n\r\nexport interface LbScaffoldConfig {\r\n plugins?: string[];\r\n}\r\n\r\nconst CONFIG_FILENAME = '.lbscaffold';\r\n\r\n/**\r\n * Find the config file by searching up the directory tree\r\n */\r\nexport async function findConfigPath(startDir: string = process.cwd()): Promise<string | null> {\r\n let currentDir = startDir;\r\n\r\n while (true) {\r\n const configPath = path.join(currentDir, CONFIG_FILENAME);\r\n try {\r\n await fs.access(configPath);\r\n return configPath;\r\n } catch {\r\n const parentDir = path.dirname(currentDir);\r\n if (parentDir === currentDir) {\r\n // Reached root\r\n return null;\r\n }\r\n currentDir = parentDir;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Get the default config path in the current directory\r\n */\r\nexport function getDefaultConfigPath(): string {\r\n return path.join(process.cwd(), CONFIG_FILENAME);\r\n}\r\n\r\n/**\r\n * Load the config file\r\n */\r\nexport async function loadConfig(configPath?: string): Promise<LbScaffoldConfig> {\r\n const resolvedPath = configPath ?? (await findConfigPath());\r\n\r\n if (!resolvedPath) {\r\n return {};\r\n }\r\n\r\n try {\r\n const content = await fs.readFile(resolvedPath, 'utf-8');\r\n return JSON.parse(content) as LbScaffoldConfig;\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\r\n return {};\r\n }\r\n throw new Error(`Failed to load config from ${resolvedPath}: ${(error as Error).message}`);\r\n }\r\n}\r\n\r\n/**\r\n * Save the config file\r\n */\r\nexport async function saveConfig(config: LbScaffoldConfig, configPath?: string): Promise<void> {\r\n const resolvedPath = configPath ?? getDefaultConfigPath();\r\n await fs.writeFile(resolvedPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\r\n}\r\n\r\n/**\r\n * Initialize a new config file\r\n */\r\nexport async function initConfig(configPath?: string): Promise<string> {\r\n const resolvedPath = configPath ?? getDefaultConfigPath();\r\n\r\n try {\r\n await fs.access(resolvedPath);\r\n throw new Error(`Config file already exists at ${resolvedPath}`);\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\r\n throw error;\r\n }\r\n }\r\n\r\n const defaultConfig: LbScaffoldConfig = {\r\n plugins: ['./plugins/**'],\r\n };\r\n\r\n await saveConfig(defaultConfig, resolvedPath);\r\n return resolvedPath;\r\n}\r\n\r\n/**\r\n * Add a plugin glob pattern to the config\r\n */\r\nexport async function addPluginGlob(glob: string, configPath?: string): Promise<void> {\r\n const config = await loadConfig(configPath);\r\n\r\n if (!config.plugins) {\r\n config.plugins = [];\r\n }\r\n\r\n if (config.plugins.includes(glob)) {\r\n throw new Error(`Plugin pattern '${glob}' already exists in config`);\r\n }\r\n\r\n config.plugins.push(glob);\r\n await saveConfig(config, configPath ?? (await findConfigPath()) ?? getDefaultConfigPath());\r\n}\r\n\r\n/**\r\n * Remove a plugin glob pattern from the config\r\n */\r\nexport async function removePluginGlob(glob: string, configPath?: string): Promise<void> {\r\n const resolvedPath = configPath ?? (await findConfigPath());\r\n\r\n if (!resolvedPath) {\r\n throw new Error('No config file found');\r\n }\r\n\r\n const config = await loadConfig(resolvedPath);\r\n\r\n if (!config.plugins || !config.plugins.includes(glob)) {\r\n throw new Error(`Plugin pattern '${glob}' not found in config`);\r\n }\r\n\r\n config.plugins = config.plugins.filter(p => p !== glob);\r\n await saveConfig(config, resolvedPath);\r\n}\r\n\r\n/**\r\n * List all plugin glob patterns in the config\r\n */\r\nexport async function listPluginGlobs(configPath?: string): Promise<string[]> {\r\n const config = await loadConfig(configPath);\r\n return config.plugins ?? [];\r\n}\r\n\r\n/**\r\n * Get absolute plugin paths from config globs\r\n */\r\nexport async function getPluginPaths(configPath?: string): Promise<string[]> {\r\n const resolvedConfigPath = configPath ?? (await findConfigPath());\r\n\r\n if (!resolvedConfigPath) {\r\n return [];\r\n }\r\n\r\n const config = await loadConfig(resolvedConfigPath);\r\n const configDir = path.dirname(resolvedConfigPath);\r\n\r\n return (config.plugins ?? []).map(glob => {\r\n // If glob is absolute, use as-is; otherwise resolve relative to config file\r\n if (path.isAbsolute(glob)) {\r\n return glob.replace(/\\\\/g, '/');\r\n }\r\n return path.resolve(configDir, glob).replace(/\\\\/g, '/');\r\n });\r\n}\r\n"],"mappings":"+hBAEA,MCKM,EAAkB,cAKxB,eAAsB,EAAe,EAAmB,QAAQ,KAAK,CAA0B,CAC3F,IAAI,EAAa,EAEjB,OAAa,CACT,IAAM,EAAa,EAAA,QAAK,KAAK,EAAY,EAAgB,CACzD,GAAI,CAEA,OADA,MAAMA,EAAAA,QAAG,OAAO,EAAW,CACpB,OACH,CACJ,IAAM,EAAY,EAAA,QAAK,QAAQ,EAAW,CAC1C,GAAI,IAAc,EAEd,OAAO,KAEX,EAAa,IAQzB,SAAgB,GAA+B,CAC3C,OAAO,EAAA,QAAK,KAAK,QAAQ,KAAK,CAAE,EAAgB,CAMpD,eAAsB,EAAW,EAAgD,CAC7E,IAAM,EAAe,GAAe,MAAM,GAAgB,CAE1D,GAAI,CAAC,EACD,MAAO,EAAE,CAGb,GAAI,CACA,IAAM,EAAU,MAAMA,EAAAA,QAAG,SAAS,EAAc,QAAQ,CACxD,OAAO,KAAK,MAAM,EAAQ,OACrB,EAAO,CACZ,GAAK,EAAgC,OAAS,SAC1C,MAAO,EAAE,CAEb,MAAU,MAAM,8BAA8B,EAAa,IAAK,EAAgB,UAAU,EAOlG,eAAsB,EAAW,EAA0B,EAAoC,CAC3F,IAAM,EAAe,GAAc,GAAsB,CACzD,MAAMA,EAAAA,QAAG,UAAU,EAAc,KAAK,UAAU,EAAQ,KAAM,EAAE,CAAG;EAAM,QAAQ,CAMrF,eAAsB,EAAW,EAAsC,CACnE,IAAM,EAAe,GAAc,GAAsB,CAEzD,GAAI,CAEA,MADA,MAAMA,EAAAA,QAAG,OAAO,EAAa,CACnB,MAAM,iCAAiC,IAAe,OAC3D,EAAO,CACZ,GAAK,EAAgC,OAAS,SAC1C,MAAM,EASd,OADA,MAAM,EAJkC,CACpC,QAAS,CAAC,eAAe,CAC5B,CAE+B,EAAa,CACtC,EAMX,eAAsB,EAAc,EAAc,EAAoC,CAClF,IAAM,EAAS,MAAM,EAAW,EAAW,CAM3C,GAJA,AACI,EAAO,UAAU,EAAE,CAGnB,EAAO,QAAQ,SAAS,EAAK,CAC7B,MAAU,MAAM,mBAAmB,EAAK,4BAA4B,CAGxE,EAAO,QAAQ,KAAK,EAAK,CACzB,MAAM,EAAW,EAAQ,GAAe,MAAM,GAAgB,EAAK,GAAsB,CAAC,CAM9F,eAAsB,EAAiB,EAAc,EAAoC,CACrF,IAAM,EAAe,GAAe,MAAM,GAAgB,CAE1D,GAAI,CAAC,EACD,MAAU,MAAM,uBAAuB,CAG3C,IAAM,EAAS,MAAM,EAAW,EAAa,CAE7C,GAAI,CAAC,EAAO,SAAW,CAAC,EAAO,QAAQ,SAAS,EAAK,CACjD,MAAU,MAAM,mBAAmB,EAAK,uBAAuB,CAGnE,EAAO,QAAU,EAAO,QAAQ,OAAO,GAAK,IAAM,EAAK,CACvD,MAAM,EAAW,EAAQ,EAAa,CAM1C,eAAsB,EAAgB,EAAwC,CAE1E,OADe,MAAM,EAAW,EAAW,EAC7B,SAAW,EAAE,CAM/B,eAAsB,EAAe,EAAwC,CACzE,IAAM,EAAqB,GAAe,MAAM,GAAgB,CAEhE,GAAI,CAAC,EACD,MAAO,EAAE,CAGb,IAAM,EAAS,MAAM,EAAW,EAAmB,CAC7C,EAAY,EAAA,QAAK,QAAQ,EAAmB,CAElD,OAAQ,EAAO,SAAW,EAAE,EAAE,IAAI,GAE1B,EAAA,QAAK,WAAW,EAAK,CACd,EAAK,QAAQ,MAAO,IAAI,CAE5B,EAAA,QAAK,QAAQ,EAAW,EAAK,CAAC,QAAQ,MAAO,IAAI,CAC1D"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["fs","fs","execFile","fs"],"sources":["../../src/core/types.ts","../../src/config.ts","../../src/utils/replace-placeholders.ts","../../src/utils/resolve-package.ts"],"sourcesContent":["export const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = 'scaffold-template';\n\nexport type ScaffoldTemplatePluginOption<TValue = string | boolean | number> = {\n readonly flags: string; // ex: --git-init\n readonly required?: boolean;\n readonly description: string;\n readonly defaultValue?: TValue | TValue[];\n readonly choices?: TValue[];\n};\n\nexport type ResolvedOptions<TOpt extends object> = {\n [k in keyof TOpt]: TOpt[k] extends ScaffoldTemplatePluginOption<infer TValue> ? TValue : never;\n};\n\nexport type ExecuteOptions<TOpt extends object> = ScaffoldTemplatePluginOptions & {\n [k in keyof TOpt]: TOpt[k] extends ScaffoldTemplatePluginOption<infer TValue> ? TValue : never;\n};\n\nexport interface ScaffoldTemplatePlugin<TOpt extends object = object> {\n readonly argument: string;\n\n getOptions(options: ScaffoldTemplatePluginOptions & Partial<ResolvedOptions<TOpt>>): Promise<\n Partial<{\n [k in keyof TOpt]: ScaffoldTemplatePluginOption;\n }>\n >;\n\n execute(options: ExecuteOptions<TOpt>): Promise<void>;\n}\n\nexport type ScaffoldTemplatePluginOptions = {\n name: string;\n dryRun?: boolean;\n force?: boolean;\n};\n","import fs from 'fs/promises';\nimport path from 'path';\n\nexport interface LbScaffoldConfig {\n plugins?: string[];\n packages?: string[];\n}\n\nconst CONFIG_FILENAME = '.lbscaffold';\n\n/**\n * Find the config file by searching up the directory tree\n */\nexport async function findConfigPath(startDir: string = process.cwd()): Promise<string | null> {\n let currentDir = startDir;\n\n while (true) {\n const configPath = path.join(currentDir, CONFIG_FILENAME);\n try {\n await fs.access(configPath);\n return configPath;\n } catch {\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) {\n // Reached root\n return null;\n }\n currentDir = parentDir;\n }\n }\n}\n\n/**\n * Get the default config path in the current directory\n */\nexport function getDefaultConfigPath(): string {\n return path.join(process.cwd(), CONFIG_FILENAME);\n}\n\n/**\n * Load the config file\n */\nexport async function loadConfig(configPath?: string): Promise<LbScaffoldConfig> {\n const resolvedPath = configPath ?? (await findConfigPath());\n\n if (!resolvedPath) {\n return {};\n }\n\n try {\n const content = await fs.readFile(resolvedPath, 'utf-8');\n return JSON.parse(content) as LbScaffoldConfig;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return {};\n }\n throw new Error(`Failed to load config from ${resolvedPath}: ${(error as Error).message}`);\n }\n}\n\n/**\n * Save the config file\n */\nexport async function saveConfig(config: LbScaffoldConfig, configPath?: string): Promise<void> {\n const resolvedPath = configPath ?? getDefaultConfigPath();\n await fs.writeFile(resolvedPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\n/**\n * Initialize a new config file\n */\nexport async function initConfig(configPath?: string): Promise<string> {\n const resolvedPath = configPath ?? getDefaultConfigPath();\n\n try {\n await fs.access(resolvedPath);\n throw new Error(`Config file already exists at ${resolvedPath}`);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n\n const defaultConfig: LbScaffoldConfig = {\n plugins: ['./plugins/**'],\n packages: [],\n };\n\n await saveConfig(defaultConfig, resolvedPath);\n return resolvedPath;\n}\n\n/**\n * Add a plugin glob pattern to the config\n */\nexport async function addPluginGlob(glob: string, configPath?: string): Promise<void> {\n const config = await loadConfig(configPath);\n\n if (!config.plugins) {\n config.plugins = [];\n }\n\n if (config.plugins.includes(glob)) {\n throw new Error(`Plugin pattern '${glob}' already exists in config`);\n }\n\n config.plugins.push(glob);\n await saveConfig(config, configPath ?? (await findConfigPath()) ?? getDefaultConfigPath());\n}\n\n/**\n * Remove a plugin glob pattern from the config\n */\nexport async function removePluginGlob(glob: string, configPath?: string): Promise<void> {\n const resolvedPath = configPath ?? (await findConfigPath());\n\n if (!resolvedPath) {\n throw new Error('No config file found');\n }\n\n const config = await loadConfig(resolvedPath);\n\n if (!config.plugins || !config.plugins.includes(glob)) {\n throw new Error(`Plugin pattern '${glob}' not found in config`);\n }\n\n config.plugins = config.plugins.filter(p => p !== glob);\n await saveConfig(config, resolvedPath);\n}\n\n/**\n * List all plugin glob patterns in the config\n */\nexport async function listPluginGlobs(configPath?: string): Promise<string[]> {\n const config = await loadConfig(configPath);\n return config.plugins ?? [];\n}\n\n/**\n * Get absolute plugin paths from config globs\n */\nexport async function getPluginPaths(configPath?: string): Promise<string[]> {\n const resolvedConfigPath = configPath ?? (await findConfigPath());\n\n if (!resolvedConfigPath) {\n return [];\n }\n\n const config = await loadConfig(resolvedConfigPath);\n const configDir = path.dirname(resolvedConfigPath);\n\n return (config.plugins ?? []).map(glob => {\n // If glob is absolute, use as-is; otherwise resolve relative to config file\n if (path.isAbsolute(glob)) {\n return glob.replace(/\\\\/g, '/');\n }\n return path.resolve(configDir, glob).replace(/\\\\/g, '/');\n });\n}\n\n/**\n * Add an npm package name to the config\n */\nexport async function addPackage(packageName: string, configPath?: string): Promise<void> {\n const config = await loadConfig(configPath);\n\n if (!config.packages) {\n config.packages = [];\n }\n\n if (config.packages.includes(packageName)) {\n throw new Error(`Package '${packageName}' already exists in config`);\n }\n\n config.packages.push(packageName);\n await saveConfig(config, configPath ?? (await findConfigPath()) ?? getDefaultConfigPath());\n}\n\n/**\n * Remove an npm package name from the config\n */\nexport async function removePackage(packageName: string, configPath?: string): Promise<void> {\n const resolvedPath = configPath ?? (await findConfigPath());\n\n if (!resolvedPath) {\n throw new Error('No config file found');\n }\n\n const config = await loadConfig(resolvedPath);\n\n if (!config.packages || !config.packages.includes(packageName)) {\n throw new Error(`Package '${packageName}' not found in config`);\n }\n\n config.packages = config.packages.filter(p => p !== packageName);\n await saveConfig(config, resolvedPath);\n}\n\n/**\n * List all npm package names in the config\n */\nexport async function listPackages(configPath?: string): Promise<string[]> {\n const config = await loadConfig(configPath);\n return config.packages ?? [];\n}\n","import path from 'path';\n\nimport fs from 'fs-extra';\n\nexport async function replacePlaceholders<T extends Record<string, string>>(\n targetDir: string,\n replacements: T\n): Promise<void> {\n async function replaceInFiles(folder: string): Promise<void> {\n const entries = await fs.readdir(folder);\n for (const entry of entries) {\n const fullPath = path.join(folder, entry);\n const stat = await fs.stat(fullPath);\n if (stat.isDirectory()) {\n await replaceInFiles(fullPath);\n } else if (/\\.(ts|js|json|md|txt)$/i.test(entry)) {\n let content = await fs.readFile(fullPath, 'utf-8');\n for (const [placeholder, value] of Object.entries(replacements)) {\n content = content.replaceAll(placeholder, value);\n }\n await fs.writeFile(fullPath, content, 'utf-8');\n }\n }\n }\n\n await replaceInFiles(targetDir);\n}\n","import fs from 'fs/promises';\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport path from 'path';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Resolve an npm package name to its installed root directory.\n * Tries local node_modules first (relative to cwd), then global.\n */\nexport async function resolvePackageDir(packageName: string): Promise<string> {\n // Strategy 1: resolve from local node_modules via cwd\n const localCandidate = path.join(process.cwd(), 'node_modules', packageName);\n try {\n await fs.access(path.join(localCandidate, 'package.json'));\n return localCandidate;\n } catch {\n // Not found locally, try global\n }\n\n // Strategy 2: resolve from global node_modules\n const globalDir = await getGlobalNodeModulesDir();\n if (globalDir) {\n const globalCandidate = path.join(globalDir, packageName);\n try {\n await fs.access(path.join(globalCandidate, 'package.json'));\n return globalCandidate;\n } catch {\n // Not found globally either\n }\n }\n\n throw new Error(\n `Package '${packageName}' not found. ` +\n `Install it locally (npm install ${packageName}) or globally (npm install -g ${packageName}).`\n );\n}\n\n/**\n * Get the global node_modules directory using npm root -g.\n */\nasync function getGlobalNodeModulesDir(): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync('npm', ['root', '-g'], { encoding: 'utf-8' });\n const dir = stdout.trim();\n await fs.access(dir);\n return dir;\n } catch {\n return null;\n }\n}\n"],"mappings":"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"}
|
package/dist/cli/index.d.cts
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
//#region src/core/types.d.ts
|
|
2
|
+
declare const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = "scaffold-template";
|
|
3
|
+
type ScaffoldTemplatePluginOption<TValue = string | boolean | number> = {
|
|
4
|
+
readonly flags: string;
|
|
5
|
+
readonly required?: boolean;
|
|
6
|
+
readonly description: string;
|
|
7
|
+
readonly defaultValue?: TValue | TValue[];
|
|
8
|
+
readonly choices?: TValue[];
|
|
9
|
+
};
|
|
10
|
+
type ResolvedOptions<TOpt extends object> = { [k in keyof TOpt]: TOpt[k] extends ScaffoldTemplatePluginOption<infer TValue> ? TValue : never };
|
|
11
|
+
type ExecuteOptions<TOpt extends object> = ScaffoldTemplatePluginOptions & { [k in keyof TOpt]: TOpt[k] extends ScaffoldTemplatePluginOption<infer TValue> ? TValue : never };
|
|
12
|
+
interface ScaffoldTemplatePlugin<TOpt extends object = object> {
|
|
13
|
+
readonly argument: string;
|
|
14
|
+
getOptions(options: ScaffoldTemplatePluginOptions & Partial<ResolvedOptions<TOpt>>): Promise<Partial<{ [k in keyof TOpt]: ScaffoldTemplatePluginOption }>>;
|
|
15
|
+
execute(options: ExecuteOptions<TOpt>): Promise<void>;
|
|
16
|
+
}
|
|
2
17
|
type ScaffoldTemplatePluginOptions = {
|
|
3
18
|
name: string;
|
|
4
19
|
dryRun?: boolean;
|
|
5
20
|
force?: boolean;
|
|
6
21
|
};
|
|
7
22
|
//#endregion
|
|
8
|
-
//#region src/core/scaffold-template-plugin.d.ts
|
|
9
|
-
declare const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = "scaffold-template";
|
|
10
|
-
interface ScaffoldTemplatePlugin {
|
|
11
|
-
argument: string;
|
|
12
|
-
execute(options: ScaffoldTemplatePluginOptions): Promise<void>;
|
|
13
|
-
}
|
|
14
|
-
//#endregion
|
|
15
23
|
//#region src/config.d.ts
|
|
16
24
|
interface LbScaffoldConfig {
|
|
17
25
|
plugins?: string[];
|
|
26
|
+
packages?: string[];
|
|
18
27
|
}
|
|
19
28
|
/**
|
|
20
29
|
* Find the config file by searching up the directory tree
|
|
@@ -52,6 +61,28 @@ declare function listPluginGlobs(configPath?: string): Promise<string[]>;
|
|
|
52
61
|
* Get absolute plugin paths from config globs
|
|
53
62
|
*/
|
|
54
63
|
declare function getPluginPaths(configPath?: string): Promise<string[]>;
|
|
64
|
+
/**
|
|
65
|
+
* Add an npm package name to the config
|
|
66
|
+
*/
|
|
67
|
+
declare function addPackage(packageName: string, configPath?: string): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Remove an npm package name from the config
|
|
70
|
+
*/
|
|
71
|
+
declare function removePackage(packageName: string, configPath?: string): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* List all npm package names in the config
|
|
74
|
+
*/
|
|
75
|
+
declare function listPackages(configPath?: string): Promise<string[]>;
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/utils/replace-placeholders.d.ts
|
|
78
|
+
declare function replacePlaceholders<T extends Record<string, string>>(targetDir: string, replacements: T): Promise<void>;
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/utils/resolve-package.d.ts
|
|
81
|
+
/**
|
|
82
|
+
* Resolve an npm package name to its installed root directory.
|
|
83
|
+
* Tries local node_modules first (relative to cwd), then global.
|
|
84
|
+
*/
|
|
85
|
+
declare function resolvePackageDir(packageName: string): Promise<string>;
|
|
55
86
|
//#endregion
|
|
56
|
-
export { LbScaffoldConfig, SCAFFOLD_TEMPLATE_PLUGIN_TYPE, ScaffoldTemplatePlugin, ScaffoldTemplatePluginOptions, addPluginGlob, findConfigPath, getDefaultConfigPath, getPluginPaths, initConfig, listPluginGlobs, loadConfig, removePluginGlob, saveConfig };
|
|
87
|
+
export { ExecuteOptions, LbScaffoldConfig, ResolvedOptions, SCAFFOLD_TEMPLATE_PLUGIN_TYPE, ScaffoldTemplatePlugin, ScaffoldTemplatePluginOption, ScaffoldTemplatePluginOptions, addPackage, addPluginGlob, findConfigPath, getDefaultConfigPath, getPluginPaths, initConfig, listPackages, listPluginGlobs, loadConfig, removePackage, removePluginGlob, replacePlaceholders, resolvePackageDir, saveConfig };
|
|
57
88
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/cli/index.d.mts
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
//#region src/core/types.d.ts
|
|
2
|
+
declare const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = "scaffold-template";
|
|
3
|
+
type ScaffoldTemplatePluginOption<TValue = string | boolean | number> = {
|
|
4
|
+
readonly flags: string;
|
|
5
|
+
readonly required?: boolean;
|
|
6
|
+
readonly description: string;
|
|
7
|
+
readonly defaultValue?: TValue | TValue[];
|
|
8
|
+
readonly choices?: TValue[];
|
|
9
|
+
};
|
|
10
|
+
type ResolvedOptions<TOpt extends object> = { [k in keyof TOpt]: TOpt[k] extends ScaffoldTemplatePluginOption<infer TValue> ? TValue : never };
|
|
11
|
+
type ExecuteOptions<TOpt extends object> = ScaffoldTemplatePluginOptions & { [k in keyof TOpt]: TOpt[k] extends ScaffoldTemplatePluginOption<infer TValue> ? TValue : never };
|
|
12
|
+
interface ScaffoldTemplatePlugin<TOpt extends object = object> {
|
|
13
|
+
readonly argument: string;
|
|
14
|
+
getOptions(options: ScaffoldTemplatePluginOptions & Partial<ResolvedOptions<TOpt>>): Promise<Partial<{ [k in keyof TOpt]: ScaffoldTemplatePluginOption }>>;
|
|
15
|
+
execute(options: ExecuteOptions<TOpt>): Promise<void>;
|
|
16
|
+
}
|
|
2
17
|
type ScaffoldTemplatePluginOptions = {
|
|
3
18
|
name: string;
|
|
4
19
|
dryRun?: boolean;
|
|
5
20
|
force?: boolean;
|
|
6
21
|
};
|
|
7
22
|
//#endregion
|
|
8
|
-
//#region src/core/scaffold-template-plugin.d.ts
|
|
9
|
-
declare const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = "scaffold-template";
|
|
10
|
-
interface ScaffoldTemplatePlugin {
|
|
11
|
-
argument: string;
|
|
12
|
-
execute(options: ScaffoldTemplatePluginOptions): Promise<void>;
|
|
13
|
-
}
|
|
14
|
-
//#endregion
|
|
15
23
|
//#region src/config.d.ts
|
|
16
24
|
interface LbScaffoldConfig {
|
|
17
25
|
plugins?: string[];
|
|
26
|
+
packages?: string[];
|
|
18
27
|
}
|
|
19
28
|
/**
|
|
20
29
|
* Find the config file by searching up the directory tree
|
|
@@ -52,6 +61,28 @@ declare function listPluginGlobs(configPath?: string): Promise<string[]>;
|
|
|
52
61
|
* Get absolute plugin paths from config globs
|
|
53
62
|
*/
|
|
54
63
|
declare function getPluginPaths(configPath?: string): Promise<string[]>;
|
|
64
|
+
/**
|
|
65
|
+
* Add an npm package name to the config
|
|
66
|
+
*/
|
|
67
|
+
declare function addPackage(packageName: string, configPath?: string): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Remove an npm package name from the config
|
|
70
|
+
*/
|
|
71
|
+
declare function removePackage(packageName: string, configPath?: string): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* List all npm package names in the config
|
|
74
|
+
*/
|
|
75
|
+
declare function listPackages(configPath?: string): Promise<string[]>;
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/utils/replace-placeholders.d.ts
|
|
78
|
+
declare function replacePlaceholders<T extends Record<string, string>>(targetDir: string, replacements: T): Promise<void>;
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/utils/resolve-package.d.ts
|
|
81
|
+
/**
|
|
82
|
+
* Resolve an npm package name to its installed root directory.
|
|
83
|
+
* Tries local node_modules first (relative to cwd), then global.
|
|
84
|
+
*/
|
|
85
|
+
declare function resolvePackageDir(packageName: string): Promise<string>;
|
|
55
86
|
//#endregion
|
|
56
|
-
export { LbScaffoldConfig, SCAFFOLD_TEMPLATE_PLUGIN_TYPE, ScaffoldTemplatePlugin, ScaffoldTemplatePluginOptions, addPluginGlob, findConfigPath, getDefaultConfigPath, getPluginPaths, initConfig, listPluginGlobs, loadConfig, removePluginGlob, saveConfig };
|
|
87
|
+
export { ExecuteOptions, LbScaffoldConfig, ResolvedOptions, SCAFFOLD_TEMPLATE_PLUGIN_TYPE, ScaffoldTemplatePlugin, ScaffoldTemplatePluginOption, ScaffoldTemplatePluginOptions, addPackage, addPluginGlob, findConfigPath, getDefaultConfigPath, getPluginPaths, initConfig, listPackages, listPluginGlobs, loadConfig, removePackage, removePluginGlob, replacePlaceholders, resolvePackageDir, saveConfig };
|
|
57
88
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,3 +1,199 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs$1 from "fs-extra";
|
|
4
|
+
import { execFile } from "node:child_process";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
|
|
7
|
+
//#region src/core/types.ts
|
|
8
|
+
const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = "scaffold-template";
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/config.ts
|
|
12
|
+
const CONFIG_FILENAME = ".lbscaffold";
|
|
13
|
+
/**
|
|
14
|
+
* Find the config file by searching up the directory tree
|
|
15
|
+
*/
|
|
16
|
+
async function findConfigPath(startDir = process.cwd()) {
|
|
17
|
+
let currentDir = startDir;
|
|
18
|
+
while (true) {
|
|
19
|
+
const configPath = path.join(currentDir, CONFIG_FILENAME);
|
|
20
|
+
try {
|
|
21
|
+
await fs.access(configPath);
|
|
22
|
+
return configPath;
|
|
23
|
+
} catch {
|
|
24
|
+
const parentDir = path.dirname(currentDir);
|
|
25
|
+
if (parentDir === currentDir) return null;
|
|
26
|
+
currentDir = parentDir;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get the default config path in the current directory
|
|
32
|
+
*/
|
|
33
|
+
function getDefaultConfigPath() {
|
|
34
|
+
return path.join(process.cwd(), CONFIG_FILENAME);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Load the config file
|
|
38
|
+
*/
|
|
39
|
+
async function loadConfig(configPath) {
|
|
40
|
+
const resolvedPath = configPath ?? await findConfigPath();
|
|
41
|
+
if (!resolvedPath) return {};
|
|
42
|
+
try {
|
|
43
|
+
const content = await fs.readFile(resolvedPath, "utf-8");
|
|
44
|
+
return JSON.parse(content);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (error.code === "ENOENT") return {};
|
|
47
|
+
throw new Error(`Failed to load config from ${resolvedPath}: ${error.message}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Save the config file
|
|
52
|
+
*/
|
|
53
|
+
async function saveConfig(config, configPath) {
|
|
54
|
+
const resolvedPath = configPath ?? getDefaultConfigPath();
|
|
55
|
+
await fs.writeFile(resolvedPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Initialize a new config file
|
|
59
|
+
*/
|
|
60
|
+
async function initConfig(configPath) {
|
|
61
|
+
const resolvedPath = configPath ?? getDefaultConfigPath();
|
|
62
|
+
try {
|
|
63
|
+
await fs.access(resolvedPath);
|
|
64
|
+
throw new Error(`Config file already exists at ${resolvedPath}`);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (error.code !== "ENOENT") throw error;
|
|
67
|
+
}
|
|
68
|
+
await saveConfig({
|
|
69
|
+
plugins: ["./plugins/**"],
|
|
70
|
+
packages: []
|
|
71
|
+
}, resolvedPath);
|
|
72
|
+
return resolvedPath;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Add a plugin glob pattern to the config
|
|
76
|
+
*/
|
|
77
|
+
async function addPluginGlob(glob, configPath) {
|
|
78
|
+
const config = await loadConfig(configPath);
|
|
79
|
+
if (!config.plugins) config.plugins = [];
|
|
80
|
+
if (config.plugins.includes(glob)) throw new Error(`Plugin pattern '${glob}' already exists in config`);
|
|
81
|
+
config.plugins.push(glob);
|
|
82
|
+
await saveConfig(config, configPath ?? await findConfigPath() ?? getDefaultConfigPath());
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Remove a plugin glob pattern from the config
|
|
86
|
+
*/
|
|
87
|
+
async function removePluginGlob(glob, configPath) {
|
|
88
|
+
const resolvedPath = configPath ?? await findConfigPath();
|
|
89
|
+
if (!resolvedPath) throw new Error("No config file found");
|
|
90
|
+
const config = await loadConfig(resolvedPath);
|
|
91
|
+
if (!config.plugins || !config.plugins.includes(glob)) throw new Error(`Plugin pattern '${glob}' not found in config`);
|
|
92
|
+
config.plugins = config.plugins.filter((p) => p !== glob);
|
|
93
|
+
await saveConfig(config, resolvedPath);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* List all plugin glob patterns in the config
|
|
97
|
+
*/
|
|
98
|
+
async function listPluginGlobs(configPath) {
|
|
99
|
+
return (await loadConfig(configPath)).plugins ?? [];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get absolute plugin paths from config globs
|
|
103
|
+
*/
|
|
104
|
+
async function getPluginPaths(configPath) {
|
|
105
|
+
const resolvedConfigPath = configPath ?? await findConfigPath();
|
|
106
|
+
if (!resolvedConfigPath) return [];
|
|
107
|
+
const config = await loadConfig(resolvedConfigPath);
|
|
108
|
+
const configDir = path.dirname(resolvedConfigPath);
|
|
109
|
+
return (config.plugins ?? []).map((glob) => {
|
|
110
|
+
if (path.isAbsolute(glob)) return glob.replace(/\\/g, "/");
|
|
111
|
+
return path.resolve(configDir, glob).replace(/\\/g, "/");
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Add an npm package name to the config
|
|
116
|
+
*/
|
|
117
|
+
async function addPackage(packageName, configPath) {
|
|
118
|
+
const config = await loadConfig(configPath);
|
|
119
|
+
if (!config.packages) config.packages = [];
|
|
120
|
+
if (config.packages.includes(packageName)) throw new Error(`Package '${packageName}' already exists in config`);
|
|
121
|
+
config.packages.push(packageName);
|
|
122
|
+
await saveConfig(config, configPath ?? await findConfigPath() ?? getDefaultConfigPath());
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Remove an npm package name from the config
|
|
126
|
+
*/
|
|
127
|
+
async function removePackage(packageName, configPath) {
|
|
128
|
+
const resolvedPath = configPath ?? await findConfigPath();
|
|
129
|
+
if (!resolvedPath) throw new Error("No config file found");
|
|
130
|
+
const config = await loadConfig(resolvedPath);
|
|
131
|
+
if (!config.packages || !config.packages.includes(packageName)) throw new Error(`Package '${packageName}' not found in config`);
|
|
132
|
+
config.packages = config.packages.filter((p) => p !== packageName);
|
|
133
|
+
await saveConfig(config, resolvedPath);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* List all npm package names in the config
|
|
137
|
+
*/
|
|
138
|
+
async function listPackages(configPath) {
|
|
139
|
+
return (await loadConfig(configPath)).packages ?? [];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/utils/replace-placeholders.ts
|
|
144
|
+
async function replacePlaceholders(targetDir, replacements) {
|
|
145
|
+
async function replaceInFiles(folder) {
|
|
146
|
+
const entries = await fs$1.readdir(folder);
|
|
147
|
+
for (const entry of entries) {
|
|
148
|
+
const fullPath = path.join(folder, entry);
|
|
149
|
+
if ((await fs$1.stat(fullPath)).isDirectory()) await replaceInFiles(fullPath);
|
|
150
|
+
else if (/\.(ts|js|json|md|txt)$/i.test(entry)) {
|
|
151
|
+
let content = await fs$1.readFile(fullPath, "utf-8");
|
|
152
|
+
for (const [placeholder, value] of Object.entries(replacements)) content = content.replaceAll(placeholder, value);
|
|
153
|
+
await fs$1.writeFile(fullPath, content, "utf-8");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
await replaceInFiles(targetDir);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/utils/resolve-package.ts
|
|
162
|
+
const execFileAsync = promisify(execFile);
|
|
163
|
+
/**
|
|
164
|
+
* Resolve an npm package name to its installed root directory.
|
|
165
|
+
* Tries local node_modules first (relative to cwd), then global.
|
|
166
|
+
*/
|
|
167
|
+
async function resolvePackageDir(packageName) {
|
|
168
|
+
const localCandidate = path.join(process.cwd(), "node_modules", packageName);
|
|
169
|
+
try {
|
|
170
|
+
await fs.access(path.join(localCandidate, "package.json"));
|
|
171
|
+
return localCandidate;
|
|
172
|
+
} catch {}
|
|
173
|
+
const globalDir = await getGlobalNodeModulesDir();
|
|
174
|
+
if (globalDir) {
|
|
175
|
+
const globalCandidate = path.join(globalDir, packageName);
|
|
176
|
+
try {
|
|
177
|
+
await fs.access(path.join(globalCandidate, "package.json"));
|
|
178
|
+
return globalCandidate;
|
|
179
|
+
} catch {}
|
|
180
|
+
}
|
|
181
|
+
throw new Error(`Package '${packageName}' not found. Install it locally (npm install ${packageName}) or globally (npm install -g ${packageName}).`);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get the global node_modules directory using npm root -g.
|
|
185
|
+
*/
|
|
186
|
+
async function getGlobalNodeModulesDir() {
|
|
187
|
+
try {
|
|
188
|
+
const { stdout } = await execFileAsync("npm", ["root", "-g"], { encoding: "utf-8" });
|
|
189
|
+
const dir = stdout.trim();
|
|
190
|
+
await fs.access(dir);
|
|
191
|
+
return dir;
|
|
192
|
+
} catch {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
//#endregion
|
|
198
|
+
export { SCAFFOLD_TEMPLATE_PLUGIN_TYPE, addPackage, addPluginGlob, findConfigPath, getDefaultConfigPath, getPluginPaths, initConfig, listPackages, listPluginGlobs, loadConfig, removePackage, removePluginGlob, replacePlaceholders, resolvePackageDir, saveConfig };
|
|
3
199
|
//# sourceMappingURL=index.mjs.map
|