@libria/scaffold 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- 57b3e0c037fdd49e8bafe4dc03e2d06c264ff70b28a71b8ae4ce5843c3a32937
1
+ 81fb90099d5aafefad5baa8e8a0fed424dfa1a5cbd7a224c2e23946de7c1d923
package/README.md CHANGED
@@ -9,6 +9,7 @@ Forge your next project with lightning-fast scaffolding. A pluggable CLI that tr
9
9
 
10
10
  - **Interactive CLI**: Guided project creation with sensible defaults
11
11
  - **Plugin System**: Extensible architecture for custom templates
12
+ - **Configuration File**: Register custom plugin directories via `.lbscaffold`
12
13
  - **Dry Run Mode**: Preview what will be generated before committing
13
14
  - **Force Overwrite**: Safely regenerate existing projects
14
15
  - **Built-in Templates**: TypeScript libraries and more included
@@ -92,6 +93,95 @@ A modern TypeScript library template with:
92
93
  - Package.json with proper exports
93
94
  - Comprehensive README and LICENSE
94
95
 
96
+ ## Configuration
97
+
98
+ 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.
99
+
100
+ ### Config File Location
101
+
102
+ The CLI searches for `.lbscaffold` starting from the current directory and walking up the directory tree. This allows you to have project-specific or workspace-level configurations.
103
+
104
+ ### Config Commands
105
+
106
+ Initialize a new config file:
107
+
108
+ ```bash
109
+ lb-scaffold config init
110
+ ```
111
+
112
+ This creates a `.lbscaffold` file with a default plugin path:
113
+
114
+ ```json
115
+ {
116
+ "plugins": ["./plugins/**"]
117
+ }
118
+ ```
119
+
120
+ Add a custom plugin directory:
121
+
122
+ ```bash
123
+ lb-scaffold config add ./my-templates/**
124
+ ```
125
+
126
+ Remove a plugin directory:
127
+
128
+ ```bash
129
+ lb-scaffold config remove ./my-templates/**
130
+ ```
131
+
132
+ List all configured plugin patterns:
133
+
134
+ ```bash
135
+ lb-scaffold config list
136
+ ```
137
+
138
+ Show the full config file:
139
+
140
+ ```bash
141
+ lb-scaffold config show
142
+ ```
143
+
144
+ ### Config File Format
145
+
146
+ The `.lbscaffold` config file is a JSON file with the following structure:
147
+
148
+ ```json
149
+ {
150
+ "plugins": [
151
+ "./plugins/**",
152
+ "./custom-templates/**",
153
+ "/absolute/path/to/plugins/**"
154
+ ]
155
+ }
156
+ ```
157
+
158
+ The `plugins` array contains glob patterns pointing to directories containing template plugins. Paths can be:
159
+ - **Relative**: Resolved relative to the config file location
160
+ - **Absolute**: Used as-is
161
+
162
+ ### Plugin Override Behavior
163
+
164
+ When a user plugin has the same name as a built-in plugin, the user plugin takes precedence. This allows you to customize or replace built-in templates.
165
+
166
+ ### Example: Using Custom Templates
167
+
168
+ 1. Create a config file:
169
+ ```bash
170
+ lb-scaffold config init
171
+ ```
172
+
173
+ 2. Add your custom templates directory:
174
+ ```bash
175
+ lb-scaffold config add ./my-company-templates/**
176
+ ```
177
+
178
+ 3. Create a template plugin in that directory (see [Creating Custom Template Plugins](#creating-custom-template-plugins))
179
+
180
+ 4. Your template will now appear in the template selection:
181
+ ```bash
182
+ lb-scaffold create
183
+ ```
184
+
95
185
  ## Creating Custom Template Plugins
96
186
 
97
187
  The scaffold CLI uses a plugin system powered by `@libria/plugin-loader`. Each template is a self-contained plugin.
@@ -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';\nimport path from 'path';\n\nexport interface LbScaffoldConfig {\n plugins?: 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 };\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","import { ScaffoldTemplatePluginOptions } from './types';\n\nexport const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = 'scaffold-template';\n\nexport interface ScaffoldTemplatePlugin {\n argument: string;\n\n execute(options: ScaffoldTemplatePluginOptions): Promise<void>;\n}\n","#!/usr/bin/env node\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nimport { LibriaPlugin, loadAllPlugins } from '@libria/plugin-loader';\nimport { InteractiveCommand, InteractiveOption, Option } from 'interactive-commander';\n\nimport {\n initConfig,\n addPluginGlob,\n removePluginGlob,\n listPluginGlobs,\n getPluginPaths,\n findConfigPath,\n loadConfig,\n} from './config';\nimport {\n SCAFFOLD_TEMPLATE_PLUGIN_TYPE,\n ScaffoldTemplatePlugin,\n ScaffoldTemplatePluginOptions,\n} from './core';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst PLUGINS_FOLDER = path.resolve(__dirname, '../templates').replace(/\\\\/g, '/');\n\n// Load built-in plugins\nconst builtInPlugins = await loadAllPlugins<ScaffoldTemplatePlugin>(\n `${PLUGINS_FOLDER}/**`,\n SCAFFOLD_TEMPLATE_PLUGIN_TYPE\n);\n\n// Load user plugins from config\nconst userPluginPaths = await getPluginPaths();\nconst userPlugins: LibriaPlugin<ScaffoldTemplatePlugin>[] = [];\n\nfor (const pluginPath of userPluginPaths) {\n try {\n const loaded = await loadAllPlugins<ScaffoldTemplatePlugin>(\n pluginPath,\n SCAFFOLD_TEMPLATE_PLUGIN_TYPE\n );\n userPlugins.push(...loaded);\n } catch (error) {\n console.warn(\n `Warning: Failed to load plugins from '${pluginPath}': ${(error as Error).message}`\n );\n }\n}\n\n// Merge and deduplicate plugins (user plugins override built-in with same name)\nconst pluginMap = new Map<string, LibriaPlugin<ScaffoldTemplatePlugin>>();\nfor (const plugin of builtInPlugins) {\n pluginMap.set(plugin.name, plugin);\n}\nfor (const plugin of userPlugins) {\n pluginMap.set(plugin.name, plugin);\n}\nconst plugins = Array.from(pluginMap.values()).sort((a, b) => a.name.localeCompare(b.name));\n\nconst templateChoices = plugins.map(plugin => plugin.name);\n\nconst program = new InteractiveCommand();\n\nprogram.name('lb-scaffold').description('Scaffold new projects from templates');\n\n// Create subcommand to enable interactive hooks\nprogram\n .command('create')\n .description('Create a new project from a template')\n .addOption(\n new InteractiveOption('-t, --template <name>', 'Template to use').choices(templateChoices)\n )\n .addOption(\n new InteractiveOption(\n '-n, --name <project-name>',\n 'Name of the new project folder'\n ).makeOptionMandatory(true)\n )\n .addOption(\n new Option('--dry-run', 'Show what would be generated without writing files').default(false)\n )\n .addOption(\n new InteractiveOption('--force', 'Overwrite existing project folder if it exists').default(\n false\n )\n )\n .action(async (options: ScaffoldTemplatePluginOptions & { template: string }) => {\n const plugin = plugins.find(plugin => plugin.api.argument === options.template);\n if (!plugin) {\n console.error(`Template '${options.template}' not found.`);\n process.exit(1);\n }\n try {\n await plugin.api.execute(options);\n } catch (error) {\n console.error('Error creating project:', error);\n process.exit(1);\n }\n });\n\n// Config command\nconst configCommand = program.command('config').description('Manage lb-scaffold configuration');\n\nconfigCommand\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\nconfigCommand\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\nconfigCommand\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\nconfigCommand\n .command('list')\n .description('List all plugin glob patterns 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\nconfigCommand\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// 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
+ {"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 +1 @@
1
- {"version":3,"file":"index.cjs","names":["fs"],"sources":["../../src/core/scaffold-template-plugin.ts","../../src/config.ts"],"sourcesContent":["import { ScaffoldTemplatePluginOptions } from './types';\n\nexport const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = 'scaffold-template';\n\nexport interface ScaffoldTemplatePlugin {\n argument: string;\n\n execute(options: ScaffoldTemplatePluginOptions): Promise<void>;\n}\n","import fs from 'fs/promises';\nimport path from 'path';\n\nexport interface LbScaffoldConfig {\n plugins?: 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 };\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"],"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"],"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 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/core/scaffold-template-plugin.ts","../../src/config.ts"],"sourcesContent":["import { ScaffoldTemplatePluginOptions } from './types';\n\nexport const SCAFFOLD_TEMPLATE_PLUGIN_TYPE = 'scaffold-template';\n\nexport interface ScaffoldTemplatePlugin {\n argument: string;\n\n execute(options: ScaffoldTemplatePluginOptions): Promise<void>;\n}\n","import fs from 'fs/promises';\nimport path from 'path';\n\nexport interface LbScaffoldConfig {\n plugins?: 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 };\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"],"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":[],"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"}