create-bunli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +302 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +310 -0
  4. package/dist/create-project.d.ts +13 -0
  5. package/dist/create.d.ts +13 -0
  6. package/dist/index.d.ts +4 -0
  7. package/dist/index.js +217 -0
  8. package/dist/template-engine.d.ts +27 -0
  9. package/dist/templates/advanced/README.md +114 -0
  10. package/dist/templates/advanced/package.json +36 -0
  11. package/dist/templates/advanced/src/commands/config.ts +145 -0
  12. package/dist/templates/advanced/src/commands/init.ts +153 -0
  13. package/dist/templates/advanced/src/commands/serve.ts +176 -0
  14. package/dist/templates/advanced/src/commands/validate.ts +116 -0
  15. package/dist/templates/advanced/src/index.ts +44 -0
  16. package/dist/templates/advanced/src/utils/config.ts +83 -0
  17. package/dist/templates/advanced/src/utils/constants.ts +12 -0
  18. package/dist/templates/advanced/src/utils/glob.ts +49 -0
  19. package/dist/templates/advanced/src/utils/validator.ts +131 -0
  20. package/dist/templates/advanced/template.json +37 -0
  21. package/dist/templates/advanced/test/commands.test.ts +34 -0
  22. package/dist/templates/advanced/tsconfig.json +23 -0
  23. package/dist/templates/basic/README.md +41 -0
  24. package/dist/templates/basic/package.json +29 -0
  25. package/dist/templates/basic/src/commands/hello.ts +29 -0
  26. package/dist/templates/basic/src/index.ts +13 -0
  27. package/dist/templates/basic/template.json +31 -0
  28. package/dist/templates/basic/test/hello.test.ts +26 -0
  29. package/dist/templates/basic/tsconfig.json +19 -0
  30. package/dist/templates/monorepo/README.md +74 -0
  31. package/dist/templates/monorepo/package.json +28 -0
  32. package/dist/templates/monorepo/packages/cli/package.json +34 -0
  33. package/dist/templates/monorepo/packages/cli/src/index.ts +22 -0
  34. package/dist/templates/monorepo/packages/cli/tsconfig.json +15 -0
  35. package/dist/templates/monorepo/packages/core/package.json +32 -0
  36. package/dist/templates/monorepo/packages/core/scripts/build.ts +18 -0
  37. package/dist/templates/monorepo/packages/core/src/commands/analyze.ts +84 -0
  38. package/dist/templates/monorepo/packages/core/src/commands/process.ts +64 -0
  39. package/dist/templates/monorepo/packages/core/src/index.ts +3 -0
  40. package/dist/templates/monorepo/packages/core/src/types.ts +21 -0
  41. package/dist/templates/monorepo/packages/core/tsconfig.json +15 -0
  42. package/dist/templates/monorepo/packages/utils/package.json +26 -0
  43. package/dist/templates/monorepo/packages/utils/scripts/build.ts +17 -0
  44. package/dist/templates/monorepo/packages/utils/src/format.ts +27 -0
  45. package/dist/templates/monorepo/packages/utils/src/index.ts +3 -0
  46. package/dist/templates/monorepo/packages/utils/src/json.ts +11 -0
  47. package/dist/templates/monorepo/packages/utils/src/logger.ts +19 -0
  48. package/dist/templates/monorepo/packages/utils/tsconfig.json +12 -0
  49. package/dist/templates/monorepo/template.json +24 -0
  50. package/dist/templates/monorepo/tsconfig.json +14 -0
  51. package/dist/templates/monorepo/turbo.json +28 -0
  52. package/dist/types.d.ts +48 -0
  53. package/package.json +57 -0
  54. package/templates/advanced/README.md +114 -0
  55. package/templates/advanced/package.json +36 -0
  56. package/templates/advanced/src/commands/config.ts +145 -0
  57. package/templates/advanced/src/commands/init.ts +153 -0
  58. package/templates/advanced/src/commands/serve.ts +176 -0
  59. package/templates/advanced/src/commands/validate.ts +116 -0
  60. package/templates/advanced/src/index.ts +44 -0
  61. package/templates/advanced/src/utils/config.ts +83 -0
  62. package/templates/advanced/src/utils/constants.ts +12 -0
  63. package/templates/advanced/src/utils/glob.ts +49 -0
  64. package/templates/advanced/src/utils/validator.ts +131 -0
  65. package/templates/advanced/template.json +37 -0
  66. package/templates/advanced/test/commands.test.ts +34 -0
  67. package/templates/advanced/tsconfig.json +23 -0
  68. package/templates/basic/README.md +41 -0
  69. package/templates/basic/package.json +29 -0
  70. package/templates/basic/src/commands/hello.ts +29 -0
  71. package/templates/basic/src/index.ts +13 -0
  72. package/templates/basic/template.json +31 -0
  73. package/templates/basic/test/hello.test.ts +26 -0
  74. package/templates/basic/tsconfig.json +19 -0
  75. package/templates/monorepo/README.md +74 -0
  76. package/templates/monorepo/package.json +28 -0
  77. package/templates/monorepo/packages/cli/package.json +34 -0
  78. package/templates/monorepo/packages/cli/src/index.ts +22 -0
  79. package/templates/monorepo/packages/cli/tsconfig.json +15 -0
  80. package/templates/monorepo/packages/core/package.json +32 -0
  81. package/templates/monorepo/packages/core/scripts/build.ts +18 -0
  82. package/templates/monorepo/packages/core/src/commands/analyze.ts +84 -0
  83. package/templates/monorepo/packages/core/src/commands/process.ts +64 -0
  84. package/templates/monorepo/packages/core/src/index.ts +3 -0
  85. package/templates/monorepo/packages/core/src/types.ts +21 -0
  86. package/templates/monorepo/packages/core/tsconfig.json +15 -0
  87. package/templates/monorepo/packages/utils/package.json +26 -0
  88. package/templates/monorepo/packages/utils/scripts/build.ts +17 -0
  89. package/templates/monorepo/packages/utils/src/format.ts +27 -0
  90. package/templates/monorepo/packages/utils/src/index.ts +3 -0
  91. package/templates/monorepo/packages/utils/src/json.ts +11 -0
  92. package/templates/monorepo/packages/utils/src/logger.ts +19 -0
  93. package/templates/monorepo/packages/utils/tsconfig.json +12 -0
  94. package/templates/monorepo/template.json +24 -0
  95. package/templates/monorepo/tsconfig.json +14 -0
  96. package/templates/monorepo/turbo.json +28 -0
package/dist/index.js ADDED
@@ -0,0 +1,217 @@
1
+ // @bun
2
+ // src/template-engine.ts
3
+ import { downloadTemplate } from "giget";
4
+ import { readdir } from "fs/promises";
5
+ import { join } from "path";
6
+ async function processTemplate(options) {
7
+ const { source, dir, offline, variables = {} } = options;
8
+ let templateDir;
9
+ if (source.startsWith("/") || source.startsWith("./") || source.startsWith("../")) {
10
+ const sourceDir = source.startsWith("/") ? source : join(process.cwd(), source);
11
+ await Bun.spawn(["cp", "-r", sourceDir + "/.", dir], {
12
+ stdout: "inherit",
13
+ stderr: "inherit"
14
+ }).exited;
15
+ templateDir = dir;
16
+ } else {
17
+ const result = await downloadTemplate(source, {
18
+ dir,
19
+ offline,
20
+ preferOffline: true,
21
+ force: true
22
+ });
23
+ templateDir = result.dir;
24
+ }
25
+ const manifest = await loadTemplateManifest(templateDir);
26
+ if (manifest?.files || Object.keys(variables).length > 0) {
27
+ await processTemplateFiles(templateDir, variables, manifest);
28
+ }
29
+ if (manifest?.hooks?.postInstall) {
30
+ await runPostInstallHooks(templateDir, manifest.hooks.postInstall);
31
+ }
32
+ return { dir: templateDir, manifest };
33
+ }
34
+ async function loadTemplateManifest(dir) {
35
+ const possiblePaths = [
36
+ join(dir, "template.json"),
37
+ join(dir, ".template.json"),
38
+ join(dir, "template.yaml"),
39
+ join(dir, ".template.yaml")
40
+ ];
41
+ for (const path of possiblePaths) {
42
+ const file = Bun.file(path);
43
+ if (await file.exists()) {
44
+ const content = await file.text();
45
+ if (path.endsWith(".json")) {
46
+ const manifest = JSON.parse(content);
47
+ try {
48
+ await Bun.spawn(["rm", "-f", path], {
49
+ stdout: "ignore",
50
+ stderr: "ignore"
51
+ }).exited;
52
+ } catch {}
53
+ return manifest;
54
+ }
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+ async function processTemplateFiles(dir, variables, manifest) {
60
+ const files = await getFilesToProcess(dir, manifest);
61
+ for (const file of files) {
62
+ const filePath = join(dir, file);
63
+ const content = await Bun.file(filePath).text();
64
+ let processedContent = content;
65
+ for (const [key, value] of Object.entries(variables)) {
66
+ processedContent = processedContent.replaceAll(`{{${key}}}`, value).replaceAll(`<%= ${key} %>`, value).replaceAll(`$${key}`, value).replaceAll(`__${key}__`, value);
67
+ }
68
+ let newFilePath = filePath;
69
+ for (const [key, value] of Object.entries(variables)) {
70
+ newFilePath = newFilePath.replaceAll(`__${key}__`, value);
71
+ }
72
+ await Bun.write(newFilePath, processedContent);
73
+ if (newFilePath !== filePath) {
74
+ await Bun.spawn(["rm", filePath]).exited;
75
+ }
76
+ }
77
+ }
78
+ async function getFilesToProcess(dir, manifest) {
79
+ if (manifest?.files?.include) {
80
+ return manifest.files.include;
81
+ }
82
+ const files = [];
83
+ async function walk(currentDir, prefix = "") {
84
+ const entries = await readdir(currentDir, { withFileTypes: true });
85
+ for (const entry of entries) {
86
+ const path = join(prefix, entry.name);
87
+ if (entry.isDirectory()) {
88
+ if (!["node_modules", ".git", ".next", "dist", "build"].includes(entry.name)) {
89
+ await walk(join(currentDir, entry.name), path);
90
+ }
91
+ } else {
92
+ if (!path.match(/^(template\.json|\.template\.json|\.DS_Store|Thumbs\.db)$/)) {
93
+ files.push(path);
94
+ }
95
+ }
96
+ }
97
+ }
98
+ await walk(dir);
99
+ return files;
100
+ }
101
+ async function runPostInstallHooks(dir, hooks) {
102
+ for (const hook of hooks) {
103
+ const proc = Bun.spawn(hook.split(" "), {
104
+ cwd: dir,
105
+ stdout: "inherit",
106
+ stderr: "inherit"
107
+ });
108
+ await proc.exited;
109
+ }
110
+ }
111
+ function resolveTemplateSource(template) {
112
+ const specialTemplates = {
113
+ basic: "github:bunli/templates/basic",
114
+ advanced: "github:bunli/templates/advanced",
115
+ monorepo: "github:bunli/templates/monorepo"
116
+ };
117
+ if (specialTemplates[template]) {
118
+ return specialTemplates[template];
119
+ }
120
+ if (template.startsWith("npm:")) {
121
+ return template.replace("npm:", "npm:/");
122
+ }
123
+ if (template.includes("/") && !template.includes(":")) {
124
+ return `github:${template}`;
125
+ }
126
+ return template;
127
+ }
128
+ function getBundledTemplatePath(name) {
129
+ return join(import.meta.dir, "..", "templates", name);
130
+ }
131
+ async function isLocalTemplate(template) {
132
+ if (template.startsWith("file:") || template.startsWith("./") || template.startsWith("../")) {
133
+ return true;
134
+ }
135
+ const bundledPath = getBundledTemplatePath(template);
136
+ return await Bun.file(join(bundledPath, "package.json")).exists();
137
+ }
138
+
139
+ // src/create-project.ts
140
+ async function createProject(options) {
141
+ const { name, dir, template, git, install, packageManager, prompt, spinner, colors, shell, offline } = options;
142
+ try {
143
+ await shell`test -d ${dir}`.quiet();
144
+ const overwrite = await prompt.confirm(`Directory ${dir} already exists. Overwrite?`, { default: false });
145
+ if (!overwrite) {
146
+ console.log(colors.red("Cancelled"));
147
+ process.exit(1);
148
+ }
149
+ await shell`rm -rf ${dir}`;
150
+ } catch {}
151
+ const spin = spinner("Creating project structure...");
152
+ spin.start();
153
+ await shell`mkdir -p ${dir}`;
154
+ try {
155
+ let templateSource = template;
156
+ if (await isLocalTemplate(template)) {
157
+ templateSource = getBundledTemplatePath(template);
158
+ } else {
159
+ templateSource = resolveTemplateSource(template);
160
+ }
161
+ const { manifest } = await processTemplate({
162
+ source: templateSource,
163
+ dir,
164
+ offline,
165
+ variables: {
166
+ projectName: name,
167
+ description: `A CLI built with Bunli`,
168
+ author: "",
169
+ packageManager: packageManager || "bun",
170
+ year: new Date().getFullYear().toString()
171
+ }
172
+ });
173
+ spin.succeed("Project structure created");
174
+ if (git) {
175
+ const gitSpin = spinner("Initializing git repository...");
176
+ gitSpin.start();
177
+ try {
178
+ await shell`cd ${dir} && git init`.quiet();
179
+ await shell`cd ${dir} && git add .`.quiet();
180
+ await shell`cd ${dir} && git commit -m "Initial commit"`.quiet();
181
+ gitSpin.succeed("Git repository initialized");
182
+ } catch (error) {
183
+ gitSpin.fail("Failed to initialize git repository");
184
+ console.error(colors.dim(` ${error}`));
185
+ }
186
+ }
187
+ if (install) {
188
+ const installSpin = spinner(`Installing dependencies with ${packageManager}...`);
189
+ installSpin.start();
190
+ try {
191
+ const installCmd = packageManager === "bun" ? "bun install" : packageManager === "pnpm" ? "pnpm install" : packageManager === "yarn" ? "yarn install" : "npm install";
192
+ await shell`cd ${dir} && ${installCmd}`;
193
+ installSpin.succeed("Dependencies installed");
194
+ } catch (error) {
195
+ installSpin.fail("Failed to install dependencies");
196
+ console.error(colors.dim(` You can install them manually by running: ${packageManager} install`));
197
+ }
198
+ }
199
+ } catch (error) {
200
+ spin.fail("Failed to create project");
201
+ console.error(colors.red(`Error: ${error}`));
202
+ try {
203
+ await shell`rm -rf ${dir}`.quiet();
204
+ } catch {}
205
+ process.exit(1);
206
+ }
207
+ }
208
+
209
+ // src/index.ts
210
+ var version = "0.1.0";
211
+ export {
212
+ version,
213
+ resolveTemplateSource,
214
+ processTemplate,
215
+ isLocalTemplate,
216
+ createProject
217
+ };
@@ -0,0 +1,27 @@
1
+ import type { TemplateManifest } from './types.js';
2
+ export interface TemplateOptions {
3
+ source: string;
4
+ type?: 'github' | 'npm' | 'local' | 'bundled';
5
+ dir: string;
6
+ offline?: boolean;
7
+ variables?: Record<string, string>;
8
+ }
9
+ /**
10
+ * Download and process a template
11
+ */
12
+ export declare function processTemplate(options: TemplateOptions): Promise<{
13
+ dir: string;
14
+ manifest: TemplateManifest | null;
15
+ }>;
16
+ /**
17
+ * Resolve template source to giget-compatible format
18
+ */
19
+ export declare function resolveTemplateSource(template: string): string;
20
+ /**
21
+ * Get bundled template path
22
+ */
23
+ export declare function getBundledTemplatePath(name: string): string;
24
+ /**
25
+ * Check if template exists locally (for development)
26
+ */
27
+ export declare function isLocalTemplate(template: string): Promise<boolean>;
@@ -0,0 +1,114 @@
1
+ # {{projectName}}
2
+
3
+ {{description}}
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # Install globally
9
+ bun add -g {{projectName}}
10
+
11
+ # Or use directly with bunx
12
+ bunx {{projectName}} [command]
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```bash
18
+ {{projectName}} <command> [options]
19
+ ```
20
+
21
+ ### Commands
22
+
23
+ #### `init`
24
+ Initialize a new configuration file in the current directory.
25
+
26
+ ```bash
27
+ {{projectName}} init [options]
28
+
29
+ Options:
30
+ -f, --force Overwrite existing config
31
+ -t, --template Config template to use
32
+ ```
33
+
34
+ #### `validate`
35
+ Validate files against defined rules.
36
+
37
+ ```bash
38
+ {{projectName}} validate <files...> [options]
39
+
40
+ Options:
41
+ -c, --config Path to config file
42
+ -f, --fix Auto-fix issues
43
+ --no-cache Disable caching
44
+ ```
45
+
46
+ #### `serve`
47
+ Start a development server.
48
+
49
+ ```bash
50
+ {{projectName}} serve [options]
51
+
52
+ Options:
53
+ -p, --port Port to listen on (default: 3000)
54
+ -h, --host Host to bind to (default: localhost)
55
+ --no-open Don't open browser
56
+ ```
57
+
58
+ #### `config`
59
+ Manage configuration settings.
60
+
61
+ ```bash
62
+ {{projectName}} config <action> [key] [value]
63
+
64
+ Actions:
65
+ get <key> Get a config value
66
+ set <key> <value> Set a config value
67
+ list List all config values
68
+ reset Reset to defaults
69
+ ```
70
+
71
+ ### Global Options
72
+
73
+ - `-v, --version` - Show version
74
+ - `-h, --help` - Show help
75
+ - `--verbose` - Enable verbose output
76
+ - `--quiet` - Suppress output
77
+ - `--no-color` - Disable colored output
78
+
79
+ ## Configuration
80
+
81
+ Create a `{{projectName}}.config.js` file in your project root:
82
+
83
+ ```javascript
84
+ export default {
85
+ // Configuration options
86
+ rules: {
87
+ // Define your rules
88
+ },
89
+ server: {
90
+ port: 3000,
91
+ host: 'localhost'
92
+ }
93
+ }
94
+ ```
95
+
96
+ ## Development
97
+
98
+ ```bash
99
+ # Install dependencies
100
+ bun install
101
+
102
+ # Run in development
103
+ bun dev
104
+
105
+ # Run tests
106
+ bun test
107
+
108
+ # Build for production
109
+ bun run build
110
+ ```
111
+
112
+ ## License
113
+
114
+ {{license}}
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "{{description}}",
6
+ "author": "{{author}}",
7
+ "license": "{{license}}",
8
+ "bin": {
9
+ "{{projectName}}": "./dist/index.js"
10
+ },
11
+ "scripts": {
12
+ "dev": "bun run src/index.ts",
13
+ "build": "bunli build",
14
+ "test": "bun test",
15
+ "test:watch": "bun test --watch",
16
+ "type-check": "tsc --noEmit",
17
+ "lint": "tsc --noEmit",
18
+ "prepare": "bun run build"
19
+ },
20
+ "dependencies": {
21
+ "@bunli/core": "latest",
22
+ "@bunli/utils": "latest",
23
+ "zod": "^3.22.0"
24
+ },
25
+ "devDependencies": {
26
+ "@bunli/test": "latest",
27
+ "@types/bun": "latest",
28
+ "bunli": "latest",
29
+ "typescript": "^5.0.0"
30
+ },
31
+ "bunli": {
32
+ "entry": "./src/index.ts",
33
+ "outDir": "./dist",
34
+ "external": ["@bunli/core", "@bunli/utils", "zod"]
35
+ }
36
+ }
@@ -0,0 +1,145 @@
1
+ import { defineCommand, option } from '@bunli/core'
2
+ import { z } from 'zod'
3
+ import { loadConfig, saveConfig, getConfigPath } from '../utils/config.js'
4
+
5
+ export const configCommand = defineCommand({
6
+ name: 'config',
7
+ description: 'Manage configuration',
8
+ subcommands: [
9
+ defineCommand({
10
+ name: 'get',
11
+ description: 'Get a config value',
12
+ args: z.tuple([z.string()]).describe('Config key to get'),
13
+ handler: async ({ args, colors }) => {
14
+ const [key] = args
15
+
16
+ try {
17
+ const config = await loadConfig()
18
+ const value = getNestedValue(config, key)
19
+
20
+ if (value === undefined) {
21
+ console.log(colors.yellow(`Config key '${key}' not found`))
22
+ } else {
23
+ console.log(JSON.stringify(value, null, 2))
24
+ }
25
+ } catch (error) {
26
+ console.error(colors.red(`Failed to load config: ${error}`))
27
+ process.exit(1)
28
+ }
29
+ }
30
+ }),
31
+
32
+ defineCommand({
33
+ name: 'set',
34
+ description: 'Set a config value',
35
+ args: z.tuple([z.string(), z.string()]).describe('Config key and value'),
36
+ handler: async ({ args, colors, spinner }) => {
37
+ const [key, value] = args
38
+
39
+ const spin = spinner('Updating config...')
40
+ spin.start()
41
+
42
+ try {
43
+ const config = await loadConfig()
44
+ setNestedValue(config, key, JSON.parse(value))
45
+ await saveConfig(config)
46
+
47
+ spin.succeed(`Config '${key}' updated`)
48
+ } catch (error) {
49
+ spin.fail('Failed to update config')
50
+ console.error(colors.red(String(error)))
51
+ process.exit(1)
52
+ }
53
+ }
54
+ }),
55
+
56
+ defineCommand({
57
+ name: 'list',
58
+ description: 'List all config values',
59
+ handler: async ({ colors }) => {
60
+ try {
61
+ const config = await loadConfig()
62
+ const configPath = await getConfigPath()
63
+
64
+ console.log(colors.bold('Configuration:'))
65
+ console.log(colors.dim(` File: ${configPath}`))
66
+ console.log()
67
+ console.log(JSON.stringify(config, null, 2))
68
+ } catch (error) {
69
+ console.error(colors.red(`Failed to load config: ${error}`))
70
+ process.exit(1)
71
+ }
72
+ }
73
+ }),
74
+
75
+ defineCommand({
76
+ name: 'reset',
77
+ description: 'Reset config to defaults',
78
+ options: {
79
+ force: option(
80
+ z.boolean().default(false),
81
+ {
82
+ short: 'f',
83
+ description: 'Skip confirmation'
84
+ }
85
+ )
86
+ },
87
+ handler: async ({ flags, colors, prompt, spinner }) => {
88
+ if (!flags.force) {
89
+ const confirmed = await prompt.confirm(
90
+ 'This will reset all config to defaults. Continue?',
91
+ { default: false }
92
+ )
93
+
94
+ if (!confirmed) {
95
+ console.log(colors.yellow('Reset cancelled'))
96
+ return
97
+ }
98
+ }
99
+
100
+ const spin = spinner('Resetting config...')
101
+ spin.start()
102
+
103
+ try {
104
+ const { DEFAULT_CONFIG } = await import('../utils/constants.js')
105
+ await saveConfig(DEFAULT_CONFIG)
106
+
107
+ spin.succeed('Config reset to defaults')
108
+ } catch (error) {
109
+ spin.fail('Failed to reset config')
110
+ console.error(colors.red(String(error)))
111
+ process.exit(1)
112
+ }
113
+ }
114
+ })
115
+ ]
116
+ })
117
+
118
+ function getNestedValue(obj: any, path: string): any {
119
+ const keys = path.split('.')
120
+ let current = obj
121
+
122
+ for (const key of keys) {
123
+ if (current === null || current === undefined) {
124
+ return undefined
125
+ }
126
+ current = current[key]
127
+ }
128
+
129
+ return current
130
+ }
131
+
132
+ function setNestedValue(obj: any, path: string, value: any): void {
133
+ const keys = path.split('.')
134
+ const lastKey = keys.pop()!
135
+ let current = obj
136
+
137
+ for (const key of keys) {
138
+ if (!(key in current) || typeof current[key] !== 'object') {
139
+ current[key] = {}
140
+ }
141
+ current = current[key]
142
+ }
143
+
144
+ current[lastKey] = value
145
+ }
@@ -0,0 +1,153 @@
1
+ import { defineCommand, option } from '@bunli/core'
2
+ import { z } from 'zod'
3
+ import { CONFIG_FILE_NAME, DEFAULT_CONFIG } from '../utils/constants.js'
4
+
5
+ export const initCommand = defineCommand({
6
+ name: 'init',
7
+ description: 'Initialize a new configuration file',
8
+ options: {
9
+ force: option(
10
+ z.boolean().default(false),
11
+ {
12
+ short: 'f',
13
+ description: 'Overwrite existing config'
14
+ }
15
+ ),
16
+ template: option(
17
+ z.enum(['minimal', 'default', 'full']).default('default'),
18
+ {
19
+ short: 't',
20
+ description: 'Config template to use'
21
+ }
22
+ )
23
+ },
24
+ handler: async ({ flags, colors, prompt, spinner }) => {
25
+ const configPath = `${process.cwd()}/${CONFIG_FILE_NAME}`
26
+
27
+ // Check if config already exists
28
+ const configFile = Bun.file(configPath)
29
+ if (await configFile.exists() && !flags.force) {
30
+ const overwrite = await prompt.confirm(
31
+ `Config file already exists. Overwrite?`,
32
+ { default: false }
33
+ )
34
+
35
+ if (!overwrite) {
36
+ console.log(colors.yellow('Init cancelled'))
37
+ return
38
+ }
39
+ }
40
+
41
+ const spin = spinner('Creating config file...')
42
+ spin.start()
43
+
44
+ try {
45
+ // Get template content
46
+ const configContent = getConfigTemplate(flags.template)
47
+
48
+ // Write config file
49
+ await Bun.write(configPath, configContent)
50
+
51
+ spin.succeed('Config file created')
52
+ console.log(colors.dim(` ${CONFIG_FILE_NAME}`))
53
+
54
+ // Next steps
55
+ console.log()
56
+ console.log('Next steps:')
57
+ console.log(colors.gray(` 1. Edit ${CONFIG_FILE_NAME} to customize your configuration`))
58
+ console.log(colors.gray(` 2. Run '{{projectName}} validate' to check your files`))
59
+
60
+ } catch (error) {
61
+ spin.fail('Failed to create config file')
62
+ console.error(colors.red(String(error)))
63
+ process.exit(1)
64
+ }
65
+ }
66
+ })
67
+
68
+ function getConfigTemplate(template: 'minimal' | 'default' | 'full'): string {
69
+ const templates = {
70
+ minimal: `export default ${JSON.stringify(DEFAULT_CONFIG, null, 2)}`,
71
+
72
+ default: `export default {
73
+ // Validation rules
74
+ rules: {
75
+ // Add your validation rules here
76
+ noConsoleLog: true,
77
+ requireFileHeader: false,
78
+ },
79
+
80
+ // Server configuration
81
+ server: {
82
+ port: 3000,
83
+ host: 'localhost',
84
+ open: true,
85
+ },
86
+
87
+ // File patterns
88
+ include: ['src/**/*.{js,ts}'],
89
+ exclude: ['node_modules', 'dist', 'test'],
90
+ }`,
91
+
92
+ full: `import { defineConfig } from '{{projectName}}'
93
+
94
+ export default defineConfig({
95
+ // Validation rules
96
+ rules: {
97
+ // Code style rules
98
+ noConsoleLog: true,
99
+ noDebugger: true,
100
+ requireFileHeader: true,
101
+ maxLineLength: 100,
102
+
103
+ // Import rules
104
+ noUnusedImports: true,
105
+ sortImports: true,
106
+
107
+ // Function rules
108
+ maxFunctionLength: 50,
109
+ maxComplexity: 10,
110
+ },
111
+
112
+ // Server configuration
113
+ server: {
114
+ port: process.env.PORT || 3000,
115
+ host: process.env.HOST || 'localhost',
116
+ open: !process.env.CI,
117
+ cors: true,
118
+ },
119
+
120
+ // File patterns
121
+ include: [
122
+ 'src/**/*.{js,ts,jsx,tsx}',
123
+ 'scripts/**/*.{js,ts}',
124
+ ],
125
+ exclude: [
126
+ 'node_modules',
127
+ 'dist',
128
+ 'build',
129
+ 'coverage',
130
+ '**/*.test.{js,ts}',
131
+ '**/*.spec.{js,ts}',
132
+ ],
133
+
134
+ // Caching
135
+ cache: {
136
+ enabled: true,
137
+ directory: '.cache',
138
+ },
139
+
140
+ // Hooks
141
+ hooks: {
142
+ beforeValidate: async (files) => {
143
+ console.log(\`Validating \${files.length} files...\`)
144
+ },
145
+ afterValidate: async (results) => {
146
+ console.log(\`Found \${results.errors} errors and \${results.warnings} warnings\`)
147
+ },
148
+ },
149
+ })`
150
+ }
151
+
152
+ return templates[template]
153
+ }