@libria/scaffold 0.1.2 → 0.2.1

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.
Files changed (36) hide show
  1. package/.clean-publish.hash +1 -1
  2. package/README.md +46 -0
  3. package/dist/cli/cli.mjs +3 -3
  4. package/dist/cli/cli.mjs.map +1 -1
  5. package/dist/cli/index.cjs +2 -2
  6. package/dist/cli/index.cjs.map +1 -1
  7. package/dist/cli/index.d.cts +51 -8
  8. package/dist/cli/index.d.mts +51 -8
  9. package/dist/cli/index.mjs +2 -2
  10. package/dist/cli/index.mjs.map +1 -1
  11. package/package.json +6 -2
  12. package/dist/templates/angular/index.cjs +0 -20
  13. package/dist/templates/angular/index.cjs.map +0 -1
  14. package/dist/templates/angular/index.d.cts +0 -31
  15. package/dist/templates/angular/index.d.mts +0 -31
  16. package/dist/templates/angular/index.mjs +0 -20
  17. package/dist/templates/angular/index.mjs.map +0 -1
  18. package/dist/templates/angular/plugin.json +0 -5
  19. package/dist/templates/ts-lib/files/.prettierrc +0 -8
  20. package/dist/templates/ts-lib/files/LICENSE +0 -21
  21. package/dist/templates/ts-lib/files/README.md +0 -58
  22. package/dist/templates/ts-lib/files/eslint.config.mjs +0 -106
  23. package/dist/templates/ts-lib/files/package.json +0 -61
  24. package/dist/templates/ts-lib/files/src/index.ts +0 -31
  25. package/dist/templates/ts-lib/files/tests/my.test.ts +0 -44
  26. package/dist/templates/ts-lib/files/tsconfig.json +0 -22
  27. package/dist/templates/ts-lib/files/tsconfig.test.json +0 -8
  28. package/dist/templates/ts-lib/files/tsdown.config.ts +0 -10
  29. package/dist/templates/ts-lib/files/vitest.config.ts +0 -12
  30. package/dist/templates/ts-lib/index.cjs +0 -19
  31. package/dist/templates/ts-lib/index.cjs.map +0 -1
  32. package/dist/templates/ts-lib/index.d.cts +0 -29
  33. package/dist/templates/ts-lib/index.d.mts +0 -29
  34. package/dist/templates/ts-lib/index.mjs +0 -19
  35. package/dist/templates/ts-lib/index.mjs.map +0 -1
  36. package/dist/templates/ts-lib/plugin.json +0 -5
@@ -1 +1 @@
1
- 81fb90099d5aafefad5baa8e8a0fed424dfa1a5cbd7a224c2e23946de7c1d923
1
+ 77f4de1b63792b7a20d590d1e0c539ca380236303edc9951fc4bca19e994f4c7
package/README.md CHANGED
@@ -93,6 +93,52 @@ A modern TypeScript library template with:
93
93
  - Package.json with proper exports
94
94
  - Comprehensive README and LICENSE
95
95
 
96
+ ### angular
97
+
98
+ A complete Angular application template using the official Angular CLI. Supports:
99
+
100
+ - Angular versions: Latest, 20, 19, 18, 17, 16
101
+ - Stylesheet formats: SCSS, CSS, Sass, Less
102
+ - Optional routing module
103
+ - Optional Server-Side Rendering (SSR)
104
+ - Git initialization (optional)
105
+ - Dependency installation (optional)
106
+
107
+ ```bash
108
+ lb-scaffold create -t angular -n my-angular-app
109
+ ```
110
+
111
+ **Interactive prompts:**
112
+ - Angular version selection
113
+ - Stylesheet format (SCSS recommended)
114
+ - Add routing module?
115
+ - Enable SSR?
116
+ - Skip git initialization?
117
+ - Skip npm install?
118
+
119
+ ### nestjs
120
+
121
+ A production-ready NestJS backend application using the official NestJS CLI. Includes:
122
+
123
+ - TypeScript with strict mode (optional)
124
+ - Package manager choice: npm, Yarn, or pnpm
125
+ - Controller, Service, and Module structure
126
+ - Unit test setup with Jest
127
+ - E2E test configuration
128
+ - Git initialization (optional)
129
+ - Dependency installation (optional)
130
+
131
+ ```bash
132
+ lb-scaffold create -t nestjs -n my-nest-api
133
+ ```
134
+
135
+ **Interactive prompts:**
136
+ - Package manager selection (npm, Yarn, pnpm)
137
+ - Enable strict TypeScript mode?
138
+ - Skip git initialization?
139
+ - Skip package installation?
140
+
141
+
96
142
  ## Configuration
97
143
 
98
144
  The scaffold CLI supports a configuration file (`.lbscaffold`) that allows you to register custom plugin directories. This enables you to use your own templates alongside the built-in ones.
package/dist/cli/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
2
- import e from"path";import{fileURLToPath as t}from"url";import{loadAllPlugins as n}from"@libria/plugin-loader";import{InteractiveCommand as r,InteractiveOption as i,Option as a}from"interactive-commander";import o from"fs/promises";const s=`.lbscaffold`;async function c(t=process.cwd()){let n=t;for(;;){let t=e.join(n,s);try{return await o.access(t),t}catch{let t=e.dirname(n);if(t===n)return null;n=t}}}function l(){return e.join(process.cwd(),s)}async function u(e){let t=e??await c();if(!t)return{};try{let e=await o.readFile(t,`utf-8`);return JSON.parse(e)}catch(e){if(e.code===`ENOENT`)return{};throw Error(`Failed to load config from ${t}: ${e.message}`)}}async function d(e,t){let n=t??l();await o.writeFile(n,JSON.stringify(e,null,2)+`
3
- `,`utf-8`)}async function f(e){let t=e??l();try{throw await o.access(t),Error(`Config file already exists at ${t}`)}catch(e){if(e.code!==`ENOENT`)throw e}return await d({plugins:[`./plugins/**`]},t),t}async function p(e,t){let n=await u(t);if(n.plugins||=[],n.plugins.includes(e))throw Error(`Plugin pattern '${e}' already exists in config`);n.plugins.push(e),await d(n,t??await c()??l())}async function m(e,t){let n=t??await c();if(!n)throw Error(`No config file found`);let r=await u(n);if(!r.plugins||!r.plugins.includes(e))throw Error(`Plugin pattern '${e}' not found in config`);r.plugins=r.plugins.filter(t=>t!==e),await d(r,n)}async function h(e){return(await u(e)).plugins??[]}async function g(t){let n=t??await c();if(!n)return[];let r=await u(n),i=e.dirname(n);return(r.plugins??[]).map(t=>e.isAbsolute(t)?t.replace(/\\/g,`/`):e.resolve(i,t).replace(/\\/g,`/`))}const _=`scaffold-template`,v=e.dirname(t(import.meta.url)),y=await n(`${e.resolve(v,`../templates`).replace(/\\/g,`/`)}/**`,_),b=await g(),x=[];for(let e of b)try{let t=await n(e,_);x.push(...t)}catch(t){console.warn(`Warning: Failed to load plugins from '${e}': ${t.message}`)}const S=new Map;for(let e of y)S.set(e.name,e);for(let e of x)S.set(e.name,e);const C=Array.from(S.values()).sort((e,t)=>e.name.localeCompare(t.name)),w=C.map(e=>e.name),T=new r;T.name(`lb-scaffold`).description(`Scaffold new projects from templates`),T.command(`create`).description(`Create a new project from a template`).addOption(new i(`-t, --template <name>`,`Template to use`).choices(w)).addOption(new i(`-n, --name <project-name>`,`Name of the new project folder`).makeOptionMandatory(!0)).addOption(new a(`--dry-run`,`Show what would be generated without writing files`).default(!1)).addOption(new i(`--force`,`Overwrite existing project folder if it exists`).default(!1)).action(async e=>{let t=C.find(t=>t.api.argument===e.template);t||(console.error(`Template '${e.template}' not found.`),process.exit(1));try{await t.api.execute(e)}catch(e){console.error(`Error creating project:`,e),process.exit(1)}});const E=T.command(`config`).description(`Manage lb-scaffold configuration`);E.command(`init`).description(`Initialize a new .lbscaffold config file in the current directory`).action(async()=>{try{let e=await f();console.log(`Created config file: ${e}`)}catch(e){console.error(e.message),process.exit(1)}}),E.command(`add <glob>`).description(`Add a plugin glob pattern to the config`).action(async e=>{try{await p(e),console.log(`Added plugin pattern: ${e}`)}catch(e){console.error(e.message),process.exit(1)}}),E.command(`remove <glob>`).description(`Remove a plugin glob pattern from the config`).action(async e=>{try{await m(e),console.log(`Removed plugin pattern: ${e}`)}catch(e){console.error(e.message),process.exit(1)}}),E.command(`list`).description(`List all plugin glob patterns in the config`).action(async()=>{let e=await c();if(!e){console.log(`No .lbscaffold config file found.`),console.log(`Run "lb-scaffold config init" to create one.`);return}console.log(`Config file: ${e}\n`);let t=await h();if(t.length===0)console.log(`No plugin patterns configured.`);else{console.log(`Plugin patterns:`);for(let e of t)console.log(` - ${e}`)}}),E.command(`show`).description(`Show the full config file contents`).action(async()=>{let e=await c();if(!e){console.log(`No .lbscaffold config file found.`),console.log(`Run "lb-scaffold config init" to create one.`);return}console.log(`Config file: ${e}\n`);let t=await u(e);console.log(JSON.stringify(t,null,2))}),T.addOption(new a(`-i, --interactive`,`Run in interactive mode`).default(!0)),await T.interactive().parseAsync();export{};
1
+ import{createRequire as e}from"node:module";import t from"path";import{fileURLToPath as n}from"url";import{PluginManager as r}from"@libria/plugin-loader";import{InteractiveCommand as i,Option as a}from"interactive-commander";import o from"fs/promises";import"fs-extra";import{execFile as s}from"node:child_process";import{promisify as c}from"node:util";import*as l from"node:readline";const u=`.lbscaffold`;async function d(e=process.cwd()){let n=e;for(;;){let e=t.join(n,u);try{return await o.access(e),e}catch{let e=t.dirname(n);if(e===n)return null;n=e}}}function f(){return t.join(process.cwd(),u)}async function p(e){let t=e??await d();if(!t)return{};try{let e=await o.readFile(t,`utf-8`);return JSON.parse(e)}catch(e){if(e.code===`ENOENT`)return{};throw Error(`Failed to load config from ${t}: ${e.message}`)}}async function m(e,t){let n=t??f();await o.writeFile(n,JSON.stringify(e,null,2)+`
2
+ `,`utf-8`)}async function h(e){let t=e??f();try{throw await o.access(t),Error(`Config file already exists at ${t}`)}catch(e){if(e.code!==`ENOENT`)throw e}return await m({plugins:[`./plugins/**`],packages:[]},t),t}async function g(e,t){let n=await p(t);if(n.plugins||=[],n.plugins.includes(e))throw Error(`Plugin pattern '${e}' already exists in config`);n.plugins.push(e),await m(n,t??await d()??f())}async function _(e,t){let n=t??await d();if(!n)throw Error(`No config file found`);let r=await p(n);if(!r.plugins||!r.plugins.includes(e))throw Error(`Plugin pattern '${e}' not found in config`);r.plugins=r.plugins.filter(t=>t!==e),await m(r,n)}async function v(e){return(await p(e)).plugins??[]}async function y(e){let n=e??await d();if(!n)return[];let r=await p(n),i=t.dirname(n);return(r.plugins??[]).map(e=>t.isAbsolute(e)?e.replace(/\\/g,`/`):t.resolve(i,e).replace(/\\/g,`/`))}async function b(e,t){let n=await p(t);if(n.packages||=[],n.packages.includes(e))throw Error(`Package '${e}' already exists in config`);n.packages.push(e),await m(n,t??await d()??f())}async function x(e,t){let n=t??await d();if(!n)throw Error(`No config file found`);let r=await p(n);if(!r.packages||!r.packages.includes(e))throw Error(`Package '${e}' not found in config`);r.packages=r.packages.filter(t=>t!==e),await m(r,n)}async function S(e){return(await p(e)).packages??[]}const C=c(s);async function w(n){let r=e(t.join(process.cwd(),`package.json`));try{let e=r.resolve(`${n}/package.json`);return t.dirname(e)}catch{}let i=await T();if(i){let r=e(t.join(i,`_virtual.js`));try{let e=r.resolve(`${n}/package.json`);return t.dirname(e)}catch{}}throw Error(`Package '${n}' not found. Install it locally (npm install ${n}) or globally (npm install -g ${n}).`)}async function T(){try{let{stdout:e}=await C(`npm`,[`root`,`-g`],{encoding:`utf-8`}),t=e.trim();return await o.access(t),t}catch{return null}}async function E(e){let t=e.command(`config`).description(`Manage lb-scaffold configuration`);return t.command(`init`).description(`Initialize a new .lbscaffold config file in the current directory`).action(async()=>{try{let e=await h();console.log(`Created config file: ${e}`)}catch(e){console.error(e.message),process.exit(1)}}),t.command(`add <glob>`).description(`Add a plugin glob pattern to the config`).action(async e=>{try{await g(e),console.log(`Added plugin pattern: ${e}`)}catch(e){console.error(e.message),process.exit(1)}}),t.command(`remove <glob>`).description(`Remove a plugin glob pattern from the config`).action(async e=>{try{await _(e),console.log(`Removed plugin pattern: ${e}`)}catch(e){console.error(e.message),process.exit(1)}}),t.command(`list`).description(`List all plugin patterns and packages in the config`).action(async()=>{let e=await d();if(!e){console.log(`No .lbscaffold config file found.`),console.log(`Run "lb-scaffold config init" to create one.`);return}console.log(`Config file: ${e}\n`);let t=await v();if(t.length===0)console.log(`No plugin patterns configured.`);else{console.log(`Plugin patterns:`);for(let e of t)console.log(` - ${e}`)}let n=await S();if(n.length>0){console.log(`
3
+ Packages:`);for(let e of n)console.log(` - ${e}`)}}),t.command(`show`).description(`Show the full config file contents`).action(async()=>{let e=await d();if(!e){console.log(`No .lbscaffold config file found.`),console.log(`Run "lb-scaffold config init" to create one.`);return}console.log(`Config file: ${e}\n`);let t=await p(e);console.log(JSON.stringify(t,null,2))}),t.command(`add-package <name>`).description(`Add an npm package as a plugin source`).action(async e=>{try{await w(e),await b(e),console.log(`Added package: ${e}`)}catch(e){console.error(e.message),process.exit(1)}}),t.command(`remove-package <name>`).description(`Remove an npm package from plugin sources`).action(async e=>{try{await x(e),console.log(`Removed package: ${e}`)}catch(e){console.error(e.message),process.exit(1)}}),t.command(`list-packages`).description(`List all npm packages configured as plugin sources`).action(async()=>{if(!await d()){console.log(`No .lbscaffold config file found.`),console.log(`Run "lb-scaffold config init" to create one.`);return}let e=await S();if(e.length===0)console.log(`No packages configured.`);else{console.log(`Packages:`);for(let t of e)console.log(` - ${t}`)}}),t}let D=null;function O(){return D||=l.createInterface({input:process.stdin,output:process.stdout}),D}function k(){D?.close(),D=null}function A(e){return new Promise(t=>O().question(e,t))}async function j(e,t,n){let r=t.map(String);console.log(`\n ${e}`),r.forEach((e,t)=>{let r=e===String(n)?` (default)`:``;console.log(` ${t+1}) ${e}${r}`)});let i=await A(` Choice [${n??r[0]}]: `);if(!i.trim())return String(n??r[0]);let a=parseInt(i,10);return a>=1&&a<=r.length?r[a-1]:r.includes(i.trim())?i.trim():String(n??r[0])}async function M(e,t){let n=await A(` ${e} (Y/n) [${t===!1?`N`:`Y`}]: `);return n.trim()?n.trim().toLowerCase().startsWith(`y`):t!==!1}async function N(e,t){return(await A(` ${e}${t===void 0?``:` [${t}]`}: `)).trim()||String(t??``)}function P(e){return!e.flags.includes(`<`)&&!e.choices?.length}async function F(e,t){return t.choices?.length?j(t.description,t.choices,t.defaultValue):P(t)?M(t.description,t.defaultValue):N(t.description,t.defaultValue)}async function I(e,t,n){for(;;){let r=await e.getOptions(t),i=!1;for(let[e,a]of Object.entries(r))a!==void 0&&t[e]===void 0&&(i=!0,n[e]===void 0?t[e]=await F(e,a):t[e]=n[e]);if(!i)break}}function L(e){let t=process.argv.slice(2),n={};for(let[r,i]of Object.entries(e)){if(i===void 0)continue;let e=i.flags.match(/--([a-z][a-z-]*)/i);if(!e)continue;let a=`--${e[1]}`,o=t.find(e=>e.startsWith(`${a}=`));if(o){n[r]=o.split(`=`)[1];continue}let s=t.indexOf(a);if(s!==-1&&s+1<t.length){if(!i.flags.includes(`<`))continue;n[r]=t[s+1]}}return n}function R(e,t){let n=new a(t.flags,t.description);t.defaultValue!==void 0&&n.default(t.defaultValue),t.choices?.length&&n.choices(t.choices.map(String)),t.required&&n.makeOptionMandatory(),e.addOption(n)}async function z(e,t){let n=e.command(`new`).description(`Create a new project from a template`),r=t.getPluginsByType(`scaffold-template`);for(let e of r){let r=t.getPlugin(e.id),i=n.command(r.argument).description(`Create a new ${r.argument} project`).argument(`<name>`,`Project name`).option(`--dry-run`,`Simulate without writing files`,!1).option(`--force`,`Overwrite existing files`,!1).allowUnknownOption(!0),a=await r.getOptions({name:``}),o={name:``},s=new Set;for(;;){let e=new Set(Object.keys(a));if([...e].filter(e=>!s.has(e)).length===0)break;let t=L(a);o={...o,...t},s=e,a=await r.getOptions(o)}for(let[,e]of Object.entries(a))e!==void 0&&R(i,e);i.action(async(e,t)=>{let n={name:e,dryRun:t.dryRun,force:t.force};await I(r,n,t),k(),await r.execute(n)})}return n}const B=t.dirname(n(import.meta.url)),V=t.resolve(B,`../templates`).replace(/\\/g,`/`),H=await y(),U=await S(),W=await Promise.all(U.map(async e=>await w(e))),G=new r;await G.loadPlugins([V,...H,...W]);const K=new i;K.name(`lb-scaffold`).description(`Scaffold new projects from templates`),await z(K,G),await E(K),K.addOption(new a(`-i, --interactive`,`Run in interactive mode`).default(!0)),await K.interactive().parseAsync();export{};
4
4
  //# sourceMappingURL=cli.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":[],"sources":["../../src/config.ts","../../src/core/scaffold-template-plugin.ts","../../src/cli.ts"],"sourcesContent":["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","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","#!/usr/bin/env node\r\nimport path from 'path';\r\nimport { fileURLToPath } from 'url';\r\n\r\nimport { LibriaPlugin, loadAllPlugins } from '@libria/plugin-loader';\r\nimport { InteractiveCommand, InteractiveOption, Option } from 'interactive-commander';\r\n\r\nimport {\r\n initConfig,\r\n addPluginGlob,\r\n removePluginGlob,\r\n listPluginGlobs,\r\n getPluginPaths,\r\n findConfigPath,\r\n loadConfig,\r\n} from './config';\r\nimport {\r\n SCAFFOLD_TEMPLATE_PLUGIN_TYPE,\r\n ScaffoldTemplatePlugin,\r\n ScaffoldTemplatePluginOptions,\r\n} from './core';\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\r\nconst PLUGINS_FOLDER = path.resolve(__dirname, '../templates').replace(/\\\\/g, '/');\r\n\r\n// Load built-in plugins\r\nconst builtInPlugins = await loadAllPlugins<ScaffoldTemplatePlugin>(\r\n `${PLUGINS_FOLDER}/**`,\r\n SCAFFOLD_TEMPLATE_PLUGIN_TYPE\r\n);\r\n\r\n// Load user plugins from config\r\nconst userPluginPaths = await getPluginPaths();\r\nconst userPlugins: LibriaPlugin<ScaffoldTemplatePlugin>[] = [];\r\n\r\nfor (const pluginPath of userPluginPaths) {\r\n try {\r\n const loaded = await loadAllPlugins<ScaffoldTemplatePlugin>(\r\n pluginPath,\r\n SCAFFOLD_TEMPLATE_PLUGIN_TYPE\r\n );\r\n userPlugins.push(...loaded);\r\n } catch (error) {\r\n console.warn(\r\n `Warning: Failed to load plugins from '${pluginPath}': ${(error as Error).message}`\r\n );\r\n }\r\n}\r\n\r\n// Merge and deduplicate plugins (user plugins override built-in with same name)\r\nconst pluginMap = new Map<string, LibriaPlugin<ScaffoldTemplatePlugin>>();\r\nfor (const plugin of builtInPlugins) {\r\n pluginMap.set(plugin.name, plugin);\r\n}\r\nfor (const plugin of userPlugins) {\r\n pluginMap.set(plugin.name, plugin);\r\n}\r\nconst plugins = Array.from(pluginMap.values()).sort((a, b) => a.name.localeCompare(b.name));\r\n\r\nconst templateChoices = plugins.map(plugin => plugin.name);\r\n\r\nconst program = new InteractiveCommand();\r\n\r\nprogram.name('lb-scaffold').description('Scaffold new projects from templates');\r\n\r\n// Create subcommand to enable interactive hooks\r\nprogram\r\n .command('create')\r\n .description('Create a new project from a template')\r\n .addOption(\r\n new InteractiveOption('-t, --template <name>', 'Template to use').choices(templateChoices)\r\n )\r\n .addOption(\r\n new InteractiveOption(\r\n '-n, --name <project-name>',\r\n 'Name of the new project folder'\r\n ).makeOptionMandatory(true)\r\n )\r\n .addOption(\r\n new Option('--dry-run', 'Show what would be generated without writing files').default(false)\r\n )\r\n .addOption(\r\n new InteractiveOption('--force', 'Overwrite existing project folder if it exists').default(\r\n false\r\n )\r\n )\r\n .action(async (options: ScaffoldTemplatePluginOptions & { template: string }) => {\r\n const plugin = plugins.find(plugin => plugin.api.argument === options.template);\r\n if (!plugin) {\r\n console.error(`Template '${options.template}' not found.`);\r\n process.exit(1);\r\n }\r\n try {\r\n await plugin.api.execute(options);\r\n } catch (error) {\r\n console.error('Error creating project:', error);\r\n process.exit(1);\r\n }\r\n });\r\n\r\n// Config command\r\nconst configCommand = program.command('config').description('Manage lb-scaffold configuration');\r\n\r\nconfigCommand\r\n .command('init')\r\n .description('Initialize a new .lbscaffold config file in the current directory')\r\n .action(async () => {\r\n try {\r\n const configPath = await initConfig();\r\n console.log(`Created config file: ${configPath}`);\r\n } catch (error) {\r\n console.error((error as Error).message);\r\n process.exit(1);\r\n }\r\n });\r\n\r\nconfigCommand\r\n .command('add <glob>')\r\n .description('Add a plugin glob pattern to the config')\r\n .action(async (glob: string) => {\r\n try {\r\n await addPluginGlob(glob);\r\n console.log(`Added plugin pattern: ${glob}`);\r\n } catch (error) {\r\n console.error((error as Error).message);\r\n process.exit(1);\r\n }\r\n });\r\n\r\nconfigCommand\r\n .command('remove <glob>')\r\n .description('Remove a plugin glob pattern from the config')\r\n .action(async (glob: string) => {\r\n try {\r\n await removePluginGlob(glob);\r\n console.log(`Removed plugin pattern: ${glob}`);\r\n } catch (error) {\r\n console.error((error as Error).message);\r\n process.exit(1);\r\n }\r\n });\r\n\r\nconfigCommand\r\n .command('list')\r\n .description('List all plugin glob patterns in the config')\r\n .action(async () => {\r\n const configPath = await findConfigPath();\r\n if (!configPath) {\r\n console.log('No .lbscaffold config file found.');\r\n console.log('Run \"lb-scaffold config init\" to create one.');\r\n return;\r\n }\r\n\r\n console.log(`Config file: ${configPath}\\n`);\r\n\r\n const globs = await listPluginGlobs();\r\n if (globs.length === 0) {\r\n console.log('No plugin patterns configured.');\r\n } else {\r\n console.log('Plugin patterns:');\r\n for (const glob of globs) {\r\n console.log(` - ${glob}`);\r\n }\r\n }\r\n });\r\n\r\nconfigCommand\r\n .command('show')\r\n .description('Show the full config file contents')\r\n .action(async () => {\r\n const configPath = await findConfigPath();\r\n if (!configPath) {\r\n console.log('No .lbscaffold config file found.');\r\n console.log('Run \"lb-scaffold config init\" to create one.');\r\n return;\r\n }\r\n\r\n console.log(`Config file: ${configPath}\\n`);\r\n const config = await loadConfig(configPath);\r\n console.log(JSON.stringify(config, null, 2));\r\n });\r\n\r\n// Enable interactive mode by default\r\nprogram.addOption(new Option('-i, --interactive', 'Run in interactive mode').default(true));\r\n\r\nawait program.interactive().parseAsync();\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../../src/config.ts","../../src/utils/resolve-package.ts","../../src/commands/register-config.command.ts","../../src/core/types.ts","../../src/commands/register-new.command.ts","../../src/cli.ts"],"sourcesContent":["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 fs from 'fs/promises';\nimport { execFile } from 'node:child_process';\nimport { createRequire } from 'node:module';\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 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 localRequire = createRequire(path.join(process.cwd(), 'package.json'));\n try {\n const packageJsonPath = localRequire.resolve(`${packageName}/package.json`);\n return path.dirname(packageJsonPath);\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 globalRequire = createRequire(path.join(globalDir, '_virtual.js'));\n try {\n const packageJsonPath = globalRequire.resolve(`${packageName}/package.json`);\n return path.dirname(packageJsonPath);\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","import { InteractiveCommand } from 'interactive-commander';\n\nimport {\n addPackage,\n addPluginGlob,\n findConfigPath,\n initConfig,\n listPackages,\n listPluginGlobs,\n loadConfig,\n removePackage,\n removePluginGlob,\n} from '../config';\nimport { resolvePackageDir } from '../utils';\n\nexport async function registerConfigCommand(\n program: InteractiveCommand\n): Promise<InteractiveCommand> {\n const configCommand = program.command('config').description('Manage lb-scaffold configuration');\n\n configCommand\n .command('init')\n .description('Initialize a new .lbscaffold config file in the current directory')\n .action(async () => {\n try {\n const configPath = await initConfig();\n console.log(`Created config file: ${configPath}`);\n } catch (error) {\n console.error((error as Error).message);\n process.exit(1);\n }\n });\n\n configCommand\n .command('add <glob>')\n .description('Add a plugin glob pattern to the config')\n .action(async (glob: string) => {\n try {\n await addPluginGlob(glob);\n console.log(`Added plugin pattern: ${glob}`);\n } catch (error) {\n console.error((error as Error).message);\n process.exit(1);\n }\n });\n\n configCommand\n .command('remove <glob>')\n .description('Remove a plugin glob pattern from the config')\n .action(async (glob: string) => {\n try {\n await removePluginGlob(glob);\n console.log(`Removed plugin pattern: ${glob}`);\n } catch (error) {\n console.error((error as Error).message);\n process.exit(1);\n }\n });\n\n configCommand\n .command('list')\n .description('List all plugin patterns and packages in the config')\n .action(async () => {\n const configPath = await findConfigPath();\n if (!configPath) {\n console.log('No .lbscaffold config file found.');\n console.log('Run \"lb-scaffold config init\" to create one.');\n return;\n }\n\n console.log(`Config file: ${configPath}\\n`);\n\n const globs = await listPluginGlobs();\n if (globs.length === 0) {\n console.log('No plugin patterns configured.');\n } else {\n console.log('Plugin patterns:');\n for (const glob of globs) {\n console.log(` - ${glob}`);\n }\n }\n\n const packages = await listPackages();\n if (packages.length > 0) {\n console.log('\\nPackages:');\n for (const pkg of packages) {\n console.log(` - ${pkg}`);\n }\n }\n });\n\n configCommand\n .command('show')\n .description('Show the full config file contents')\n .action(async () => {\n const configPath = await findConfigPath();\n if (!configPath) {\n console.log('No .lbscaffold config file found.');\n console.log('Run \"lb-scaffold config init\" to create one.');\n return;\n }\n\n console.log(`Config file: ${configPath}\\n`);\n const config = await loadConfig(configPath);\n console.log(JSON.stringify(config, null, 2));\n });\n\n configCommand\n .command('add-package <name>')\n .description('Add an npm package as a plugin source')\n .action(async (name: string) => {\n try {\n await resolvePackageDir(name);\n await addPackage(name);\n console.log(`Added package: ${name}`);\n } catch (error) {\n console.error((error as Error).message);\n process.exit(1);\n }\n });\n\n configCommand\n .command('remove-package <name>')\n .description('Remove an npm package from plugin sources')\n .action(async (name: string) => {\n try {\n await removePackage(name);\n console.log(`Removed package: ${name}`);\n } catch (error) {\n console.error((error as Error).message);\n process.exit(1);\n }\n });\n\n configCommand\n .command('list-packages')\n .description('List all npm packages configured as plugin sources')\n .action(async () => {\n const configPath = await findConfigPath();\n if (!configPath) {\n console.log('No .lbscaffold config file found.');\n console.log('Run \"lb-scaffold config init\" to create one.');\n return;\n }\n\n const packages = await listPackages();\n if (packages.length === 0) {\n console.log('No packages configured.');\n } else {\n console.log('Packages:');\n for (const pkg of packages) {\n console.log(` - ${pkg}`);\n }\n }\n });\n\n return configCommand;\n}\n","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 * as readline from 'node:readline';\n\nimport { PluginManager } from '@libria/plugin-loader';\nimport { Command, InteractiveCommand, Option } from 'interactive-commander';\n\nimport {\n ScaffoldTemplatePlugin,\n SCAFFOLD_TEMPLATE_PLUGIN_TYPE,\n ScaffoldTemplatePluginOption,\n ScaffoldTemplatePluginOptions,\n} from '../core';\n\n// ── Prompting helpers (readline-based, zero dependencies) ────────────────────\n\nlet rl: readline.Interface | null = null;\n\nfunction getRL(): readline.Interface {\n if (!rl) rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n return rl;\n}\n\nfunction closeRL(): void {\n rl?.close();\n rl = null;\n}\n\nfunction ask(question: string): Promise<string> {\n return new Promise(resolve => getRL().question(question, resolve));\n}\n\nasync function promptSelect(\n message: string,\n choices: unknown[],\n defaultValue?: unknown\n): Promise<string> {\n const items = choices.map(String);\n console.log(`\\n ${message}`);\n items.forEach((c, i) => {\n const marker = c === String(defaultValue) ? ' (default)' : '';\n console.log(` ${i + 1}) ${c}${marker}`);\n });\n const answer = await ask(` Choice [${defaultValue ?? items[0]}]: `);\n if (!answer.trim()) return String(defaultValue ?? items[0]);\n const idx = parseInt(answer, 10);\n if (idx >= 1 && idx <= items.length) return items[idx - 1];\n if (items.includes(answer.trim())) return answer.trim();\n return String(defaultValue ?? items[0]);\n}\n\nasync function promptConfirm(message: string, defaultValue?: unknown): Promise<boolean> {\n const def = defaultValue === false ? 'N' : 'Y';\n const answer = await ask(` ${message} (Y/n) [${def}]: `);\n if (!answer.trim()) return defaultValue !== false;\n return answer.trim().toLowerCase().startsWith('y');\n}\n\nasync function promptInput(message: string, defaultValue?: unknown): Promise<string> {\n const suffix = defaultValue !== undefined ? ` [${defaultValue}]` : '';\n const answer = await ask(` ${message}${suffix}: `);\n return answer.trim() || String(defaultValue ?? '');\n}\n\n// ── Option type detection ────────────────────────────────────────────────────\n\nfunction isBooleanFlag(opt: ScaffoldTemplatePluginOption): boolean {\n return !opt.flags.includes('<') && !opt.choices?.length;\n}\n\n// ── Prompt for a single option ───────────────────────────────────────────────\n\nasync function promptForOption(key: string, def: ScaffoldTemplatePluginOption): Promise<unknown> {\n if (def.choices?.length) {\n return promptSelect(def.description, def.choices, def.defaultValue);\n }\n if (isBooleanFlag(def)) {\n return promptConfirm(def.description, def.defaultValue);\n }\n return promptInput(def.description, def.defaultValue);\n}\n\n// ── Iterative option resolution ──────────────────────────────────────────────\n\n/**\n * Resolves plugin options iteratively:\n * 1. Call getOptions(collected) to get currently-relevant options\n * 2. Prompt for any unanswered options (or use CLI-provided values)\n * 3. If new options appeared, repeat — the plugin may reveal more\n * options based on the newly-collected answers\n * 4. Stop when no new unanswered options appear\n */\nasync function resolveOptions(\n plugin: ScaffoldTemplatePlugin,\n collected: Record<string, unknown>,\n cliOpts: Record<string, unknown>\n): Promise<void> {\n while (true) {\n const optionDefs = await plugin.getOptions(\n collected as ScaffoldTemplatePluginOptions & Partial<Record<string, unknown>>\n );\n\n let hasNewOptions = false;\n\n for (const [key, def] of Object.entries(optionDefs)) {\n if (def === undefined) continue;\n if (collected[key] !== undefined) continue;\n\n hasNewOptions = true;\n\n // Use CLI-provided value if available, otherwise prompt\n if (cliOpts[key] !== undefined) {\n collected[key] = cliOpts[key];\n } else {\n collected[key] = await promptForOption(key, def);\n }\n }\n\n if (!hasNewOptions) break;\n }\n}\n\n// ── Argv pre-parsing ─────────────────────────────────────────────────────────\n/**\n * Pre-parse process.argv to extract values for a set of option definitions.\n * This is a lightweight scan — no full Commander parse — so we can call\n * getOptions() with the values before Commander registers anything.\n */\nfunction preParseArgv(\n optionDefs: Record<string, ScaffoldTemplatePluginOption>\n): Record<string, string> {\n const args = process.argv.slice(2);\n const result: Record<string, string> = {};\n\n for (const [key, def] of Object.entries(optionDefs)) {\n if (def === undefined) continue;\n\n // Extract the --flag-name from the flags string\n const flagMatch = def.flags.match(/--([a-z][a-z-]*)/i);\n if (!flagMatch) continue;\n const flagName = `--${flagMatch[1]}`;\n\n // Check for --flag value or --flag=value\n const eqArg = args.find(a => a.startsWith(`${flagName}=`));\n if (eqArg) {\n result[key] = eqArg.split('=')[1];\n continue;\n }\n\n const idx = args.indexOf(flagName);\n if (idx !== -1 && idx + 1 < args.length) {\n // Boolean flags (no <value> in flags string) don't consume next arg\n if (!def.flags.includes('<')) continue;\n result[key] = args[idx + 1];\n }\n }\n\n return result;\n}\n\n// ── Commander option registration ────────────────────────────────────────────\n\nfunction registerOption(cmd: Command, def: ScaffoldTemplatePluginOption): void {\n const cmdOption = new Option(def.flags, def.description);\n\n if (def.defaultValue !== undefined) {\n cmdOption.default(def.defaultValue);\n }\n if (def.choices?.length) {\n cmdOption.choices(def.choices.map(String));\n }\n if (def.required) {\n cmdOption.makeOptionMandatory();\n }\n\n cmd.addOption(cmdOption);\n}\n\nexport async function registerNewCommand(\n program: InteractiveCommand,\n pluginManager: PluginManager\n): Promise<Command> {\n const newCmd = program.command('new').description('Create a new project from a template');\n\n const templates = pluginManager.getPluginsByType(SCAFFOLD_TEMPLATE_PLUGIN_TYPE);\n\n for (const meta of templates) {\n const plugin = pluginManager.getPlugin<ScaffoldTemplatePlugin>(meta.id);\n\n const sub = newCmd\n .command(plugin.argument)\n .description(`Create a new ${plugin.argument} project`)\n .argument('<name>', 'Project name')\n .option('--dry-run', 'Simulate without writing files', false)\n .option('--force', 'Overwrite existing files', false)\n .allowUnknownOption(true);\n\n // Iteratively resolve options from argv so --help shows the right set.\n // Phase 1: get initial options, pre-parse their values from argv\n // Phase N: feed values back into getOptions until options stabilize\n let optionDefs = await plugin.getOptions({ name: '' } as ScaffoldTemplatePluginOptions &\n Partial<Record<string, unknown>>);\n let preCollected: Record<string, unknown> = { name: '' };\n let previousKeys = new Set<string>();\n\n while (true) {\n const currentKeys = new Set(Object.keys(optionDefs));\n const newKeys = [...currentKeys].filter(k => !previousKeys.has(k));\n if (newKeys.length === 0) break;\n\n // Pre-parse any new option values from argv\n const parsed = preParseArgv(optionDefs as Record<string, ScaffoldTemplatePluginOption>);\n preCollected = { ...preCollected, ...parsed };\n previousKeys = currentKeys;\n\n // Ask plugin for options again with the parsed values\n optionDefs = await plugin.getOptions(\n preCollected as ScaffoldTemplatePluginOptions & Partial<Record<string, unknown>>\n );\n }\n\n // Register the fully-resolved options with Commander\n for (const [, def] of Object.entries(optionDefs)) {\n if (def === undefined) continue;\n registerOption(sub, def);\n }\n\n sub.action(async (name: string, cliOpts: Record<string, unknown>) => {\n const collected: Record<string, unknown> = {\n name,\n dryRun: cliOpts.dryRun,\n force: cliOpts.force,\n };\n\n // Iteratively resolve all options (prompt for missing ones)\n await resolveOptions(plugin, collected, cliOpts);\n\n closeRL();\n await plugin.execute(\n collected as ScaffoldTemplatePluginOptions & Record<string, unknown>\n );\n });\n }\n\n return newCmd;\n}\n","import path from 'path';\nimport { fileURLToPath } from 'url';\n\nimport { PluginManager } from '@libria/plugin-loader';\nimport { InteractiveCommand, Option } from 'interactive-commander';\n\nimport { registerConfigCommand, registerNewCommand } from './commands';\nimport { getPluginPaths, listPackages } from './config';\nimport { resolvePackageDir } from './utils';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst PLUGINS_FOLDER = path.resolve(__dirname, '../templates').replace(/\\\\/g, '/');\n\n// Load user plugins from config\nconst userPluginPaths = await getPluginPaths();\n\n// Load plugins from npm packages\nconst packageNames = await listPackages();\nconst packagePaths = await Promise.all(\n packageNames.map(async name => {\n const dir = await resolvePackageDir(name);\n return dir;\n })\n);\n\nconst pluginManager = new PluginManager();\nawait pluginManager.loadPlugins([PLUGINS_FOLDER, ...userPluginPaths, ...packagePaths]);\n\nconst program = new InteractiveCommand();\nprogram.name('lb-scaffold').description('Scaffold new projects from templates');\n\nawait registerNewCommand(program, pluginManager);\nawait registerConfigCommand(program);\n\n// Enable interactive mode by default\nprogram.addOption(new Option('-i, --interactive', 'Run in interactive mode').default(true));\n\nawait program.interactive().parseAsync();\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`fs/promises`);c=s(c);let l=require(`path`);l=s(l);const u=`.lbscaffold`;async function d(e=process.cwd()){let t=e;for(;;){let e=l.default.join(t,u);try{return await c.default.access(e),e}catch{let e=l.default.dirname(t);if(e===t)return null;t=e}}}function f(){return l.default.join(process.cwd(),u)}async function p(e){let t=e??await d();if(!t)return{};try{let e=await c.default.readFile(t,`utf-8`);return JSON.parse(e)}catch(e){if(e.code===`ENOENT`)return{};throw Error(`Failed to load config from ${t}: ${e.message}`)}}async function m(e,t){let n=t??f();await c.default.writeFile(n,JSON.stringify(e,null,2)+`
2
- `,`utf-8`)}async function h(e){let t=e??f();try{throw await c.default.access(t),Error(`Config file already exists at ${t}`)}catch(e){if(e.code!==`ENOENT`)throw e}return await m({plugins:[`./plugins/**`]},t),t}async function g(e,t){let n=await p(t);if(n.plugins||=[],n.plugins.includes(e))throw Error(`Plugin pattern '${e}' already exists in config`);n.plugins.push(e),await m(n,t??await d()??f())}async function _(e,t){let n=t??await d();if(!n)throw Error(`No config file found`);let r=await p(n);if(!r.plugins||!r.plugins.includes(e))throw Error(`Plugin pattern '${e}' not found in config`);r.plugins=r.plugins.filter(t=>t!==e),await m(r,n)}async function v(e){return(await p(e)).plugins??[]}async function y(e){let t=e??await d();if(!t)return[];let n=await p(t),r=l.default.dirname(t);return(n.plugins??[]).map(e=>l.default.isAbsolute(e)?e.replace(/\\/g,`/`):l.default.resolve(r,e).replace(/\\/g,`/`))}exports.SCAFFOLD_TEMPLATE_PLUGIN_TYPE=`scaffold-template`,exports.addPluginGlob=g,exports.findConfigPath=d,exports.getDefaultConfigPath=f,exports.getPluginPaths=y,exports.initConfig=h,exports.listPluginGlobs=v,exports.loadConfig=p,exports.removePluginGlob=_,exports.saveConfig=m;
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`fs/promises`);c=s(c);let l=require(`path`);l=s(l);let u=require(`fs-extra`);u=s(u);let d=require(`node:child_process`),f=require(`node:module`),p=require(`node:util`),m=require(`@libria/plugin-loader`);const h=`.lbscaffold`;async function g(e=process.cwd()){let t=e;for(;;){let e=l.default.join(t,h);try{return await c.default.access(e),e}catch{let e=l.default.dirname(t);if(e===t)return null;t=e}}}function _(){return l.default.join(process.cwd(),h)}async function v(e){let t=e??await g();if(!t)return{};try{let e=await c.default.readFile(t,`utf-8`);return JSON.parse(e)}catch(e){if(e.code===`ENOENT`)return{};throw Error(`Failed to load config from ${t}: ${e.message}`)}}async function y(e,t){let n=t??_();await c.default.writeFile(n,JSON.stringify(e,null,2)+`
2
+ `,`utf-8`)}async function b(e){let t=e??_();try{throw await c.default.access(t),Error(`Config file already exists at ${t}`)}catch(e){if(e.code!==`ENOENT`)throw e}return await y({plugins:[`./plugins/**`],packages:[]},t),t}async function x(e,t){let n=await v(t);if(n.plugins||=[],n.plugins.includes(e))throw Error(`Plugin pattern '${e}' already exists in config`);n.plugins.push(e),await y(n,t??await g()??_())}async function S(e,t){let n=t??await g();if(!n)throw Error(`No config file found`);let r=await v(n);if(!r.plugins||!r.plugins.includes(e))throw Error(`Plugin pattern '${e}' not found in config`);r.plugins=r.plugins.filter(t=>t!==e),await y(r,n)}async function C(e){return(await v(e)).plugins??[]}async function w(e){let t=e??await g();if(!t)return[];let n=await v(t),r=l.default.dirname(t);return(n.plugins??[]).map(e=>l.default.isAbsolute(e)?e.replace(/\\/g,`/`):l.default.resolve(r,e).replace(/\\/g,`/`))}async function T(e,t){let n=await v(t);if(n.packages||=[],n.packages.includes(e))throw Error(`Package '${e}' already exists in config`);n.packages.push(e),await y(n,t??await g()??_())}async function E(e,t){let n=t??await g();if(!n)throw Error(`No config file found`);let r=await v(n);if(!r.packages||!r.packages.includes(e))throw Error(`Package '${e}' not found in config`);r.packages=r.packages.filter(t=>t!==e),await y(r,n)}async function D(e){return(await v(e)).packages??[]}async function O(e,t){async function n(e){let r=await u.default.readdir(e);for(let i of r){let r=l.default.join(e,i);if((await u.default.stat(r)).isDirectory())await n(r);else if(/\.(ts|js|json|md|txt)$/i.test(i)){let e=await u.default.readFile(r,`utf-8`);for(let[n,r]of Object.entries(t))e=e.replaceAll(n,r);await u.default.writeFile(r,e,`utf-8`)}}}await n(e)}const k=(0,p.promisify)(d.execFile);async function A(e){let t=(0,f.createRequire)(l.default.join(process.cwd(),`package.json`));try{let n=t.resolve(`${e}/package.json`);return l.default.dirname(n)}catch{}let n=await j();if(n){let t=(0,f.createRequire)(l.default.join(n,`_virtual.js`));try{let n=t.resolve(`${e}/package.json`);return l.default.dirname(n)}catch{}}throw Error(`Package '${e}' not found. Install it locally (npm install ${e}) or globally (npm install -g ${e}).`)}async function j(){try{let{stdout:e}=await k(`npm`,[`root`,`-g`],{encoding:`utf-8`}),t=e.trim();return await c.default.access(t),t}catch{return null}}async function M(e,t){let n=l.default.join(e,`plugin.json`);try{let r=await c.default.readFile(n,`utf-8`),i=JSON.parse(r);return t&&i.pluginType!==t?[]:[await(0,m.loadPlugin)({...i,__dir:e})]}catch(e){if(e.code!==`ENOENT`)throw e}return(0,m.loadAllPlugins)(e,t)}exports.SCAFFOLD_TEMPLATE_PLUGIN_TYPE=`scaffold-template`,exports.addPackage=T,exports.addPluginGlob=x,exports.findConfigPath=g,exports.getDefaultConfigPath=_,exports.getPluginPaths=w,exports.initConfig=b,exports.listPackages=D,exports.listPluginGlobs=C,exports.loadConfig=v,exports.loadPackagePlugins=M,exports.removePackage=E,exports.removePluginGlob=S,exports.replacePlaceholders=O,exports.resolvePackageDir=A,exports.saveConfig=y;
3
3
  //# sourceMappingURL=index.cjs.map
@@ -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","fs"],"sources":["../../src/core/types.ts","../../src/config.ts","../../src/utils/replace-placeholders.ts","../../src/utils/resolve-package.ts","../../src/utils/load-package-plugins.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 { createRequire } from 'node:module';\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 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 localRequire = createRequire(path.join(process.cwd(), 'package.json'));\n try {\n const packageJsonPath = localRequire.resolve(`${packageName}/package.json`);\n return path.dirname(packageJsonPath);\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 globalRequire = createRequire(path.join(globalDir, '_virtual.js'));\n try {\n const packageJsonPath = globalRequire.resolve(`${packageName}/package.json`);\n return path.dirname(packageJsonPath);\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","import fs from 'fs/promises';\nimport path from 'path';\n\nimport { LibriaPlugin, PluginManifest, loadPlugin, loadAllPlugins } from '@libria/plugin-loader';\n\n/**\n * Load plugins from an npm package directory.\n *\n * Handles two package structures:\n * 1. Single-template: plugin.json at package root -> use loadPlugin directly\n * 2. Multi-template: subdirectories with plugin.json -> use loadAllPlugins\n */\nexport async function loadPackagePlugins<T>(\n packageDir: string,\n pluginType?: string\n): Promise<LibriaPlugin<T>[]> {\n const rootPluginJson = path.join(packageDir, 'plugin.json');\n\n try {\n const content = await fs.readFile(rootPluginJson, 'utf-8');\n const rawManifest = JSON.parse(content);\n\n // Filter by pluginType if specified\n if (pluginType && rawManifest.pluginType !== pluginType) {\n return [];\n }\n\n const manifest: PluginManifest = {\n ...rawManifest,\n __dir: packageDir,\n };\n\n const plugin = await loadPlugin<T>(manifest);\n return [plugin];\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n // plugin.json exists but failed to load/parse -- rethrow\n throw error;\n }\n }\n\n // No root plugin.json -- try multi-template: scan subdirectories\n return loadAllPlugins<T>(packageDir, pluginType);\n}\n"],"mappings":"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"}
@@ -1,20 +1,31 @@
1
+ import { LibriaPlugin } from "@libria/plugin-loader";
2
+
1
3
  //#region src/core/types.d.ts
4
+ declare const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = "scaffold-template";
5
+ type ScaffoldTemplatePluginOption<TValue = string | boolean | number> = {
6
+ readonly flags: string;
7
+ readonly required?: boolean;
8
+ readonly description: string;
9
+ readonly defaultValue?: TValue | TValue[];
10
+ readonly choices?: TValue[];
11
+ };
12
+ type ResolvedOptions<TOpt extends object> = { [k in keyof TOpt]: TOpt[k] extends ScaffoldTemplatePluginOption<infer TValue> ? TValue : never };
13
+ type ExecuteOptions<TOpt extends object> = ScaffoldTemplatePluginOptions & { [k in keyof TOpt]: TOpt[k] extends ScaffoldTemplatePluginOption<infer TValue> ? TValue : never };
14
+ interface ScaffoldTemplatePlugin<TOpt extends object = object> {
15
+ readonly argument: string;
16
+ getOptions(options: ScaffoldTemplatePluginOptions & Partial<ResolvedOptions<TOpt>>): Promise<Partial<{ [k in keyof TOpt]: ScaffoldTemplatePluginOption }>>;
17
+ execute(options: ExecuteOptions<TOpt>): Promise<void>;
18
+ }
2
19
  type ScaffoldTemplatePluginOptions = {
3
20
  name: string;
4
21
  dryRun?: boolean;
5
22
  force?: boolean;
6
23
  };
7
24
  //#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
25
  //#region src/config.d.ts
16
26
  interface LbScaffoldConfig {
17
27
  plugins?: string[];
28
+ packages?: string[];
18
29
  }
19
30
  /**
20
31
  * Find the config file by searching up the directory tree
@@ -52,6 +63,38 @@ declare function listPluginGlobs(configPath?: string): Promise<string[]>;
52
63
  * Get absolute plugin paths from config globs
53
64
  */
54
65
  declare function getPluginPaths(configPath?: string): Promise<string[]>;
66
+ /**
67
+ * Add an npm package name to the config
68
+ */
69
+ declare function addPackage(packageName: string, configPath?: string): Promise<void>;
70
+ /**
71
+ * Remove an npm package name from the config
72
+ */
73
+ declare function removePackage(packageName: string, configPath?: string): Promise<void>;
74
+ /**
75
+ * List all npm package names in the config
76
+ */
77
+ declare function listPackages(configPath?: string): Promise<string[]>;
78
+ //#endregion
79
+ //#region src/utils/replace-placeholders.d.ts
80
+ declare function replacePlaceholders<T extends Record<string, string>>(targetDir: string, replacements: T): Promise<void>;
81
+ //#endregion
82
+ //#region src/utils/resolve-package.d.ts
83
+ /**
84
+ * Resolve an npm package name to its installed directory.
85
+ * Tries local node_modules first (relative to cwd), then global.
86
+ */
87
+ declare function resolvePackageDir(packageName: string): Promise<string>;
88
+ //#endregion
89
+ //#region src/utils/load-package-plugins.d.ts
90
+ /**
91
+ * Load plugins from an npm package directory.
92
+ *
93
+ * Handles two package structures:
94
+ * 1. Single-template: plugin.json at package root -> use loadPlugin directly
95
+ * 2. Multi-template: subdirectories with plugin.json -> use loadAllPlugins
96
+ */
97
+ declare function loadPackagePlugins<T>(packageDir: string, pluginType?: string): Promise<LibriaPlugin<T>[]>;
55
98
  //#endregion
56
- export { LbScaffoldConfig, SCAFFOLD_TEMPLATE_PLUGIN_TYPE, ScaffoldTemplatePlugin, ScaffoldTemplatePluginOptions, addPluginGlob, findConfigPath, getDefaultConfigPath, getPluginPaths, initConfig, listPluginGlobs, loadConfig, removePluginGlob, saveConfig };
99
+ export { ExecuteOptions, LbScaffoldConfig, ResolvedOptions, SCAFFOLD_TEMPLATE_PLUGIN_TYPE, ScaffoldTemplatePlugin, ScaffoldTemplatePluginOption, ScaffoldTemplatePluginOptions, addPackage, addPluginGlob, findConfigPath, getDefaultConfigPath, getPluginPaths, initConfig, listPackages, listPluginGlobs, loadConfig, loadPackagePlugins, removePackage, removePluginGlob, replacePlaceholders, resolvePackageDir, saveConfig };
57
100
  //# sourceMappingURL=index.d.cts.map
@@ -1,20 +1,31 @@
1
+ import { LibriaPlugin } from "@libria/plugin-loader";
2
+
1
3
  //#region src/core/types.d.ts
4
+ declare const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = "scaffold-template";
5
+ type ScaffoldTemplatePluginOption<TValue = string | boolean | number> = {
6
+ readonly flags: string;
7
+ readonly required?: boolean;
8
+ readonly description: string;
9
+ readonly defaultValue?: TValue | TValue[];
10
+ readonly choices?: TValue[];
11
+ };
12
+ type ResolvedOptions<TOpt extends object> = { [k in keyof TOpt]: TOpt[k] extends ScaffoldTemplatePluginOption<infer TValue> ? TValue : never };
13
+ type ExecuteOptions<TOpt extends object> = ScaffoldTemplatePluginOptions & { [k in keyof TOpt]: TOpt[k] extends ScaffoldTemplatePluginOption<infer TValue> ? TValue : never };
14
+ interface ScaffoldTemplatePlugin<TOpt extends object = object> {
15
+ readonly argument: string;
16
+ getOptions(options: ScaffoldTemplatePluginOptions & Partial<ResolvedOptions<TOpt>>): Promise<Partial<{ [k in keyof TOpt]: ScaffoldTemplatePluginOption }>>;
17
+ execute(options: ExecuteOptions<TOpt>): Promise<void>;
18
+ }
2
19
  type ScaffoldTemplatePluginOptions = {
3
20
  name: string;
4
21
  dryRun?: boolean;
5
22
  force?: boolean;
6
23
  };
7
24
  //#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
25
  //#region src/config.d.ts
16
26
  interface LbScaffoldConfig {
17
27
  plugins?: string[];
28
+ packages?: string[];
18
29
  }
19
30
  /**
20
31
  * Find the config file by searching up the directory tree
@@ -52,6 +63,38 @@ declare function listPluginGlobs(configPath?: string): Promise<string[]>;
52
63
  * Get absolute plugin paths from config globs
53
64
  */
54
65
  declare function getPluginPaths(configPath?: string): Promise<string[]>;
66
+ /**
67
+ * Add an npm package name to the config
68
+ */
69
+ declare function addPackage(packageName: string, configPath?: string): Promise<void>;
70
+ /**
71
+ * Remove an npm package name from the config
72
+ */
73
+ declare function removePackage(packageName: string, configPath?: string): Promise<void>;
74
+ /**
75
+ * List all npm package names in the config
76
+ */
77
+ declare function listPackages(configPath?: string): Promise<string[]>;
78
+ //#endregion
79
+ //#region src/utils/replace-placeholders.d.ts
80
+ declare function replacePlaceholders<T extends Record<string, string>>(targetDir: string, replacements: T): Promise<void>;
81
+ //#endregion
82
+ //#region src/utils/resolve-package.d.ts
83
+ /**
84
+ * Resolve an npm package name to its installed directory.
85
+ * Tries local node_modules first (relative to cwd), then global.
86
+ */
87
+ declare function resolvePackageDir(packageName: string): Promise<string>;
88
+ //#endregion
89
+ //#region src/utils/load-package-plugins.d.ts
90
+ /**
91
+ * Load plugins from an npm package directory.
92
+ *
93
+ * Handles two package structures:
94
+ * 1. Single-template: plugin.json at package root -> use loadPlugin directly
95
+ * 2. Multi-template: subdirectories with plugin.json -> use loadAllPlugins
96
+ */
97
+ declare function loadPackagePlugins<T>(packageDir: string, pluginType?: string): Promise<LibriaPlugin<T>[]>;
55
98
  //#endregion
56
- export { LbScaffoldConfig, SCAFFOLD_TEMPLATE_PLUGIN_TYPE, ScaffoldTemplatePlugin, ScaffoldTemplatePluginOptions, addPluginGlob, findConfigPath, getDefaultConfigPath, getPluginPaths, initConfig, listPluginGlobs, loadConfig, removePluginGlob, saveConfig };
99
+ export { ExecuteOptions, LbScaffoldConfig, ResolvedOptions, SCAFFOLD_TEMPLATE_PLUGIN_TYPE, ScaffoldTemplatePlugin, ScaffoldTemplatePluginOption, ScaffoldTemplatePluginOptions, addPackage, addPluginGlob, findConfigPath, getDefaultConfigPath, getPluginPaths, initConfig, listPackages, listPluginGlobs, loadConfig, loadPackagePlugins, removePackage, removePluginGlob, replacePlaceholders, resolvePackageDir, saveConfig };
57
100
  //# sourceMappingURL=index.d.mts.map
@@ -1,3 +1,3 @@
1
- import e from"fs/promises";import t from"path";const n=`scaffold-template`,r=`.lbscaffold`;async function i(n=process.cwd()){let i=n;for(;;){let n=t.join(i,r);try{return await e.access(n),n}catch{let e=t.dirname(i);if(e===i)return null;i=e}}}function a(){return t.join(process.cwd(),r)}async function o(t){let n=t??await i();if(!n)return{};try{let t=await e.readFile(n,`utf-8`);return JSON.parse(t)}catch(e){if(e.code===`ENOENT`)return{};throw Error(`Failed to load config from ${n}: ${e.message}`)}}async function s(t,n){let r=n??a();await e.writeFile(r,JSON.stringify(t,null,2)+`
2
- `,`utf-8`)}async function c(t){let n=t??a();try{throw await e.access(n),Error(`Config file already exists at ${n}`)}catch(e){if(e.code!==`ENOENT`)throw e}return await s({plugins:[`./plugins/**`]},n),n}async function l(e,t){let n=await o(t);if(n.plugins||=[],n.plugins.includes(e))throw Error(`Plugin pattern '${e}' already exists in config`);n.plugins.push(e),await s(n,t??await i()??a())}async function u(e,t){let n=t??await i();if(!n)throw Error(`No config file found`);let r=await o(n);if(!r.plugins||!r.plugins.includes(e))throw Error(`Plugin pattern '${e}' not found in config`);r.plugins=r.plugins.filter(t=>t!==e),await s(r,n)}async function d(e){return(await o(e)).plugins??[]}async function f(e){let n=e??await i();if(!n)return[];let r=await o(n),a=t.dirname(n);return(r.plugins??[]).map(e=>t.isAbsolute(e)?e.replace(/\\/g,`/`):t.resolve(a,e).replace(/\\/g,`/`))}export{n as SCAFFOLD_TEMPLATE_PLUGIN_TYPE,l as addPluginGlob,i as findConfigPath,a as getDefaultConfigPath,f as getPluginPaths,c as initConfig,d as listPluginGlobs,o as loadConfig,u as removePluginGlob,s as saveConfig};
1
+ import{createRequire as e}from"node:module";import t from"fs/promises";import n from"path";import r from"fs-extra";import{execFile as i}from"node:child_process";import{promisify as a}from"node:util";import{loadAllPlugins as o,loadPlugin as s}from"@libria/plugin-loader";const c=`scaffold-template`,l=`.lbscaffold`;async function u(e=process.cwd()){let r=e;for(;;){let e=n.join(r,l);try{return await t.access(e),e}catch{let e=n.dirname(r);if(e===r)return null;r=e}}}function d(){return n.join(process.cwd(),l)}async function f(e){let n=e??await u();if(!n)return{};try{let e=await t.readFile(n,`utf-8`);return JSON.parse(e)}catch(e){if(e.code===`ENOENT`)return{};throw Error(`Failed to load config from ${n}: ${e.message}`)}}async function p(e,n){let r=n??d();await t.writeFile(r,JSON.stringify(e,null,2)+`
2
+ `,`utf-8`)}async function m(e){let n=e??d();try{throw await t.access(n),Error(`Config file already exists at ${n}`)}catch(e){if(e.code!==`ENOENT`)throw e}return await p({plugins:[`./plugins/**`],packages:[]},n),n}async function h(e,t){let n=await f(t);if(n.plugins||=[],n.plugins.includes(e))throw Error(`Plugin pattern '${e}' already exists in config`);n.plugins.push(e),await p(n,t??await u()??d())}async function g(e,t){let n=t??await u();if(!n)throw Error(`No config file found`);let r=await f(n);if(!r.plugins||!r.plugins.includes(e))throw Error(`Plugin pattern '${e}' not found in config`);r.plugins=r.plugins.filter(t=>t!==e),await p(r,n)}async function _(e){return(await f(e)).plugins??[]}async function v(e){let t=e??await u();if(!t)return[];let r=await f(t),i=n.dirname(t);return(r.plugins??[]).map(e=>n.isAbsolute(e)?e.replace(/\\/g,`/`):n.resolve(i,e).replace(/\\/g,`/`))}async function y(e,t){let n=await f(t);if(n.packages||=[],n.packages.includes(e))throw Error(`Package '${e}' already exists in config`);n.packages.push(e),await p(n,t??await u()??d())}async function b(e,t){let n=t??await u();if(!n)throw Error(`No config file found`);let r=await f(n);if(!r.packages||!r.packages.includes(e))throw Error(`Package '${e}' not found in config`);r.packages=r.packages.filter(t=>t!==e),await p(r,n)}async function x(e){return(await f(e)).packages??[]}async function S(e,t){async function i(e){let a=await r.readdir(e);for(let o of a){let a=n.join(e,o);if((await r.stat(a)).isDirectory())await i(a);else if(/\.(ts|js|json|md|txt)$/i.test(o)){let e=await r.readFile(a,`utf-8`);for(let[n,r]of Object.entries(t))e=e.replaceAll(n,r);await r.writeFile(a,e,`utf-8`)}}}await i(e)}const C=a(i);async function w(t){let r=e(n.join(process.cwd(),`package.json`));try{let e=r.resolve(`${t}/package.json`);return n.dirname(e)}catch{}let i=await T();if(i){let r=e(n.join(i,`_virtual.js`));try{let e=r.resolve(`${t}/package.json`);return n.dirname(e)}catch{}}throw Error(`Package '${t}' not found. Install it locally (npm install ${t}) or globally (npm install -g ${t}).`)}async function T(){try{let{stdout:e}=await C(`npm`,[`root`,`-g`],{encoding:`utf-8`}),n=e.trim();return await t.access(n),n}catch{return null}}async function E(e,r){let i=n.join(e,`plugin.json`);try{let n=await t.readFile(i,`utf-8`),a=JSON.parse(n);return r&&a.pluginType!==r?[]:[await s({...a,__dir:e})]}catch(e){if(e.code!==`ENOENT`)throw e}return o(e,r)}export{c as SCAFFOLD_TEMPLATE_PLUGIN_TYPE,y as addPackage,h as addPluginGlob,u as findConfigPath,d as getDefaultConfigPath,v as getPluginPaths,m as initConfig,x as listPackages,_ as listPluginGlobs,f as loadConfig,E as loadPackagePlugins,b as removePackage,g as removePluginGlob,S as replacePlaceholders,w as resolvePackageDir,p as saveConfig};
3
3
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"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":"+CAEA,MAAa,EAAgC,oBCKvC,EAAkB,cAKxB,eAAsB,EAAe,EAAmB,QAAQ,KAAK,CAA0B,CAC3F,IAAI,EAAa,EAEjB,OAAa,CACT,IAAM,EAAa,EAAK,KAAK,EAAY,EAAgB,CACzD,GAAI,CAEA,OADA,MAAM,EAAG,OAAO,EAAW,CACpB,OACH,CACJ,IAAM,EAAY,EAAK,QAAQ,EAAW,CAC1C,GAAI,IAAc,EAEd,OAAO,KAEX,EAAa,IAQzB,SAAgB,GAA+B,CAC3C,OAAO,EAAK,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,MAAM,EAAG,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,MAAM,EAAG,UAAU,EAAc,KAAK,UAAU,EAAQ,KAAM,EAAE,CAAG;EAAM,QAAQ,CAMrF,eAAsB,EAAW,EAAsC,CACnE,IAAM,EAAe,GAAc,GAAsB,CAEzD,GAAI,CAEA,MADA,MAAM,EAAG,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,EAAK,QAAQ,EAAmB,CAElD,OAAQ,EAAO,SAAW,EAAE,EAAE,IAAI,GAE1B,EAAK,WAAW,EAAK,CACd,EAAK,QAAQ,MAAO,IAAI,CAE5B,EAAK,QAAQ,EAAW,EAAK,CAAC,QAAQ,MAAO,IAAI,CAC1D"}
1
+ {"version":3,"file":"index.mjs","names":["fs"],"sources":["../../src/core/types.ts","../../src/config.ts","../../src/utils/replace-placeholders.ts","../../src/utils/resolve-package.ts","../../src/utils/load-package-plugins.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 { createRequire } from 'node:module';\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 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 localRequire = createRequire(path.join(process.cwd(), 'package.json'));\n try {\n const packageJsonPath = localRequire.resolve(`${packageName}/package.json`);\n return path.dirname(packageJsonPath);\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 globalRequire = createRequire(path.join(globalDir, '_virtual.js'));\n try {\n const packageJsonPath = globalRequire.resolve(`${packageName}/package.json`);\n return path.dirname(packageJsonPath);\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","import fs from 'fs/promises';\nimport path from 'path';\n\nimport { LibriaPlugin, PluginManifest, loadPlugin, loadAllPlugins } from '@libria/plugin-loader';\n\n/**\n * Load plugins from an npm package directory.\n *\n * Handles two package structures:\n * 1. Single-template: plugin.json at package root -> use loadPlugin directly\n * 2. Multi-template: subdirectories with plugin.json -> use loadAllPlugins\n */\nexport async function loadPackagePlugins<T>(\n packageDir: string,\n pluginType?: string\n): Promise<LibriaPlugin<T>[]> {\n const rootPluginJson = path.join(packageDir, 'plugin.json');\n\n try {\n const content = await fs.readFile(rootPluginJson, 'utf-8');\n const rawManifest = JSON.parse(content);\n\n // Filter by pluginType if specified\n if (pluginType && rawManifest.pluginType !== pluginType) {\n return [];\n }\n\n const manifest: PluginManifest = {\n ...rawManifest,\n __dir: packageDir,\n };\n\n const plugin = await loadPlugin<T>(manifest);\n return [plugin];\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n // plugin.json exists but failed to load/parse -- rethrow\n throw error;\n }\n }\n\n // No root plugin.json -- try multi-template: scan subdirectories\n return loadAllPlugins<T>(packageDir, pluginType);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@libria/scaffold",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
4
4
  "description": "A pluggable CLI that transforms blank directories into production-ready codebases in seconds",
5
5
  "main": "dist/cli/index.cjs",
6
6
  "module": "dist/cli/index.mjs",
@@ -35,10 +35,14 @@
35
35
  },
36
36
  "homepage": "https://github.com/LibriaForge/scaffold#readme",
37
37
  "dependencies": {
38
- "@libria/plugin-loader": "^0.1.1",
38
+ "@libria/plugin-loader": "^2.0.0-alpha",
39
39
  "commander": "^14.0.3",
40
40
  "fast-glob": "^3.3.3",
41
41
  "fs-extra": "^11.3.3",
42
42
  "interactive-commander": "^0.6.0"
43
+ },
44
+ "optionalDependencies": {
45
+ "@libria/scaffold-ts-lib": "^0.0.2",
46
+ "@libria/scaffold-angular": "^0.0.2"
43
47
  }
44
48
  }