@blocklet/component-studio-cli 0.4.134

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 (84) hide show
  1. package/README.md +84 -0
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +11 -0
  4. package/dist/commands/add.d.ts +2 -0
  5. package/dist/commands/add.js +140 -0
  6. package/dist/commands/component-studio.d.ts +2 -0
  7. package/dist/commands/component-studio.js +70 -0
  8. package/dist/commands/dev.d.ts +2 -0
  9. package/dist/commands/dev.js +87 -0
  10. package/dist/commands/init.d.ts +2 -0
  11. package/dist/commands/init.js +114 -0
  12. package/dist/commands/migrate.d.ts +2 -0
  13. package/dist/commands/migrate.js +177 -0
  14. package/dist/commands/update.d.ts +2 -0
  15. package/dist/commands/update.js +22 -0
  16. package/dist/utils/display-logo.d.ts +2 -0
  17. package/dist/utils/display-logo.js +24 -0
  18. package/dist/utils/helper.d.ts +97 -0
  19. package/dist/utils/helper.js +479 -0
  20. package/package.json +58 -0
  21. package/templates/add/components/HelloWorld/@metadata.json +433 -0
  22. package/templates/add/components/HelloWorld/@template.json +4 -0
  23. package/templates/add/components/HelloWorld/index.tsx +560 -0
  24. package/templates/add/tools/cursor-rules/.cursor/rules/@metadata-json.mdc +549 -0
  25. package/templates/add/tools/cursor-rules/.cursor/rules/component-studio.mdc +138 -0
  26. package/templates/add/tools/cursor-rules/.cursor/rules/index-tsx.mdc +192 -0
  27. package/templates/add/tools/cursor-rules/@template.json +4 -0
  28. package/templates/init/0-basic/@template.json +4 -0
  29. package/templates/init/0-basic/README.md +30 -0
  30. package/templates/init/0-basic/package.json +26 -0
  31. package/templates/init/0-basic/src/HelloWorld/@metadata.json +433 -0
  32. package/templates/init/0-basic/src/HelloWorld/index.tsx +560 -0
  33. package/templates/init/1-professional/@template.json +4 -0
  34. package/templates/init/1-professional/README.md +75 -0
  35. package/templates/init/1-professional/biome.json +36 -0
  36. package/templates/init/1-professional/global.d.ts +131 -0
  37. package/templates/init/1-professional/package.json +73 -0
  38. package/templates/init/1-professional/scripts/bump-version.mjs +35 -0
  39. package/templates/init/1-professional/src/atoms/AnimationWrapper.tsx +95 -0
  40. package/templates/init/1-professional/src/atoms/ArrayTable.tsx +114 -0
  41. package/templates/init/1-professional/src/atoms/Card.tsx +52 -0
  42. package/templates/init/1-professional/src/atoms/ContentWrapper.tsx +72 -0
  43. package/templates/init/1-professional/src/atoms/CopyrightFooter.tsx +31 -0
  44. package/templates/init/1-professional/src/atoms/DataDisplays.tsx +157 -0
  45. package/templates/init/1-professional/src/atoms/GradientTitle.tsx +64 -0
  46. package/templates/init/1-professional/src/atoms/Logo.tsx +58 -0
  47. package/templates/init/1-professional/src/atoms/index.ts +27 -0
  48. package/templates/init/1-professional/src/components/HelloWorld/@metadata.json +433 -0
  49. package/templates/init/1-professional/src/components/HelloWorld/index.tsx +224 -0
  50. package/templates/init/1-professional/src/type.d.ts +42 -0
  51. package/templates/init/1-professional/src/utils/index.ts +1 -0
  52. package/templates/init/1-professional/tsconfig.json +102 -0
  53. package/templates/init/1-professional/version +1 -0
  54. package/templates/init/2-blank/@template.json +4 -0
  55. package/templates/init/2-blank/README.md +27 -0
  56. package/templates/init/2-blank/package.json +26 -0
  57. package/templates/workspace/.component-studio-runtime/_theme.tsx +4 -0
  58. package/templates/workspace/.editorconfig +23 -0
  59. package/templates/workspace/.env +1 -0
  60. package/templates/workspace/.init-component-studio +0 -0
  61. package/templates/workspace/LICENSE +13 -0
  62. package/templates/workspace/README.md +127 -0
  63. package/templates/workspace/api/dev.ts +21 -0
  64. package/templates/workspace/api/src/index.ts +50 -0
  65. package/templates/workspace/api/src/libs/auth.ts +17 -0
  66. package/templates/workspace/api/src/libs/env.ts +6 -0
  67. package/templates/workspace/api/src/libs/logger.ts +3 -0
  68. package/templates/workspace/api/src/routes/index.ts +12 -0
  69. package/templates/workspace/api/third.d.ts +17 -0
  70. package/templates/workspace/biome.json +36 -0
  71. package/templates/workspace/blocklet.md +8 -0
  72. package/templates/workspace/blocklet.yml +58 -0
  73. package/templates/workspace/index.html +15 -0
  74. package/templates/workspace/logo.png +0 -0
  75. package/templates/workspace/package.json +125 -0
  76. package/templates/workspace/pnpm-workspace.yaml +3 -0
  77. package/templates/workspace/scripts/build-clean.mjs +6 -0
  78. package/templates/workspace/scripts/bump-version.mjs +39 -0
  79. package/templates/workspace/scripts/init-component-studio.mjs +36 -0
  80. package/templates/workspace/tsconfig.api.json +12 -0
  81. package/templates/workspace/tsconfig.json +102 -0
  82. package/templates/workspace/version +1 -0
  83. package/templates/workspace/vite-server.config.ts +39 -0
  84. package/templates/workspace/vite.config.ts +68 -0
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # Component Studio CLI
2
+
3
+ A powerful CLI tool for building, developing, and publishing components with [Component Studio](https://github.com/blocklet/pages-kit).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # Global installation
9
+ npm install -g @blocklet/component-studio-cli
10
+
11
+ # Or use npx directly
12
+ npx @blocklet/component-studio-cli init my-component
13
+ ```
14
+
15
+ ## Commands
16
+
17
+ ### Initialize a new project
18
+
19
+ ```bash
20
+ component-studio init [project-name]
21
+ ```
22
+
23
+ Creates a new component project with a minimal directory structure.
24
+
25
+ ### Start development server
26
+
27
+ ```bash
28
+ component-studio dev
29
+ ```
30
+
31
+ Starts a development server with a hidden configuration, making development simpler.
32
+
33
+ ### Add components or tools
34
+
35
+ ```bash
36
+ component-studio add [name] [-t, --template <template>]
37
+ ```
38
+
39
+ Adds a component or tool from template to your project.
40
+
41
+ ### Migrate components
42
+
43
+ ```bash
44
+ component-studio migrate <url> [--output-dir <dir>]
45
+ ```
46
+
47
+ Migrates components from an existing pages-kit project.
48
+
49
+ ### Update dependencies
50
+
51
+ ```bash
52
+ component-studio update
53
+ ```
54
+
55
+ Updates both workspace and project dependencies.
56
+
57
+ ## Features
58
+
59
+ - **Simple Development**: Hide complex configuration from developers
60
+ - **Flexible Templates**: Choose from different component templates
61
+ - **Component Addition**: Easy to add new components from templates
62
+ - **Component Migration**: Import components from existing pages-kit projects
63
+ - **Easy Updates**: Keep dependencies up-to-date with a single command
64
+
65
+ ## Project Structure
66
+
67
+ When using Component Studio CLI, your project structure will look like this:
68
+
69
+ ```
70
+ my-component/
71
+ ├── src/
72
+ │ ├── components/ # Component files
73
+ │ │ ├── MyComponent.tsx # Example component
74
+ │ │ └── @metadata.json # Component metadata
75
+ │ └── index.ts # Main entry point
76
+ ├── package.json
77
+ ├── tsconfig.json
78
+ ├── vite.config.ts # Minimal config, enhanced by CLI
79
+ └── README.md
80
+ ```
81
+
82
+ ## License
83
+
84
+ ISC
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import PrettyError from 'pretty-error';
3
+ import { createComponentStudioCommand } from './commands/component-studio';
4
+ import { displayLogo } from './utils/display-logo';
5
+ displayLogo();
6
+ createComponentStudioCommand()
7
+ .parseAsync()
8
+ .catch((error) => {
9
+ console.error(new PrettyError().render(error));
10
+ process.exit(1);
11
+ });
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createAddCommand(): Command;
@@ -0,0 +1,140 @@
1
+ import chalk from 'chalk';
2
+ import { Command } from 'commander';
3
+ import inquirer from 'inquirer';
4
+ import { camelCase, upperFirst } from 'lodash';
5
+ import { existsSync, mkdirSync, readdirSync } from 'node:fs';
6
+ import { join, relative } from 'node:path';
7
+ import ora from 'ora';
8
+ import { copyTemplateFilesExcept, loadTemplates, selectTemplate, validateTemplate, getTemplatePath, getProjectPath, replaceInDirectory, findClosestMetadataDir, checkTargetDirectoryExists, } from '../utils/helper';
9
+ // 模板目录常量
10
+ const TOOLS_DIR = 'tools';
11
+ const COMPONENTS_DIR = 'components';
12
+ export function createAddCommand() {
13
+ return new Command('add')
14
+ .description('Add the component or tool from template to your project')
15
+ .argument('[name]', 'Component name', '')
16
+ .option('-t, --template <template>', 'Specify template to use')
17
+ .action(async (name, options) => {
18
+ const projectPath = getProjectPath();
19
+ const addTemplateDir = join(__dirname, '../../templates/add');
20
+ // 检查add模板目录是否存在
21
+ if (!existsSync(addTemplateDir)) {
22
+ console.log(chalk.red('Add templates directory not found. Please check your installation.'));
23
+ process.exit(1);
24
+ }
25
+ // 加载可用模板
26
+ const spinner = ora('Loading add templates...').start();
27
+ const templates = loadTemplates(addTemplateDir);
28
+ spinner.succeed(`Templates loaded: ${templates.length} templates available`);
29
+ // 确保至少有一个模板
30
+ if (templates.length === 0) {
31
+ console.log(chalk.red('No add templates found. Please check your installation.'));
32
+ process.exit(1);
33
+ }
34
+ // 让用户选择模板类型
35
+ let templateKey = options.template;
36
+ if (!templateKey) {
37
+ templateKey = await selectTemplate(templates, undefined, true);
38
+ }
39
+ // 验证所选模板是否存在
40
+ const selectedTemplate = validateTemplate(templateKey, templates);
41
+ if (!selectedTemplate) {
42
+ process.exit(1);
43
+ }
44
+ console.log(chalk.blue(`\nSelected template: "${selectedTemplate.name}"`));
45
+ // 解析模板类型
46
+ const templatePathParts = selectedTemplate.key.split('/');
47
+ const templateType = templatePathParts[0]; // 获取第一级目录名
48
+ const templatePath = getTemplatePath(selectedTemplate.key, addTemplateDir);
49
+ if (!existsSync(templatePath)) {
50
+ console.log(chalk.red(`Template directory "${selectedTemplate.name}" not found.`));
51
+ process.exit(1);
52
+ }
53
+ // 处理工具类模板
54
+ if (templateType === TOOLS_DIR) {
55
+ const firstDir = readdirSync(templatePath)[0];
56
+ const targetDir = join(projectPath, firstDir || '');
57
+ // 如果模板名称和项目中同名文件夹冲突,提示是否覆盖
58
+ const canContinue = await checkTargetDirectoryExists(targetDir, projectPath);
59
+ if (!canContinue) {
60
+ process.exit(0);
61
+ }
62
+ // 对于工具类模板,直接复制到项目根目录
63
+ spinner.start(`Adding tool "${selectedTemplate.name}" to project...`);
64
+ try {
65
+ // 复制模板文件,排除元数据
66
+ await copyTemplateFilesExcept(templatePath, projectPath);
67
+ spinner.succeed(chalk.green(`Tool "${selectedTemplate.name}" added successfully!`));
68
+ console.log(`\n🚀 Tool files have been added to your project`);
69
+ }
70
+ catch (error) {
71
+ spinner.fail(chalk.red('Failed to add tool'));
72
+ console.error(error);
73
+ process.exit(1);
74
+ }
75
+ }
76
+ else if (templateType === COMPONENTS_DIR) {
77
+ // 处理组件类模板
78
+ // 如果没有提供名称,询问用户
79
+ let componentName = name;
80
+ if (!componentName) {
81
+ const answers = await inquirer.prompt([
82
+ {
83
+ type: 'input',
84
+ name: 'componentName',
85
+ message: 'Component name:',
86
+ default: upperFirst(camelCase(selectedTemplate.name)) ?? 'NewComponent',
87
+ validate: (input) => {
88
+ if (input.trim() === '')
89
+ return 'Component name cannot be empty.';
90
+ return true;
91
+ },
92
+ },
93
+ ]);
94
+ componentName = answers.componentName;
95
+ }
96
+ // 自动计算目标目录
97
+ let targetBaseDir = join(projectPath, 'src', 'components');
98
+ // 尝试查找最近的包含@metadata.json的目录
99
+ spinner.start('Detecting components directory...');
100
+ const closestDir = await findClosestMetadataDir(projectPath);
101
+ if (closestDir) {
102
+ targetBaseDir = closestDir;
103
+ spinner.succeed(`Found components directory: ${chalk.cyan(relative(projectPath, targetBaseDir))}`);
104
+ }
105
+ else {
106
+ spinner.info(`Using default components directory: ${chalk.cyan('src/components')}`);
107
+ }
108
+ // 确定目标目录
109
+ const targetDir = join(targetBaseDir, componentName);
110
+ // 检查目录
111
+ const canContinue = await checkTargetDirectoryExists(targetDir, targetBaseDir);
112
+ if (!canContinue) {
113
+ process.exit(0);
114
+ }
115
+ // 创建目标目录
116
+ spinner.start(`Creating component "${componentName}"...`);
117
+ try {
118
+ mkdirSync(targetDir, { recursive: true });
119
+ // 复制模板文件,排除元数据
120
+ await copyTemplateFilesExcept(templatePath, targetDir);
121
+ // 替换模板中的占位符
122
+ await replaceInDirectory(targetDir, {
123
+ '{{COMPONENT_NAME}}': componentName,
124
+ '{{component_name}}': componentName.toLowerCase(),
125
+ '{{ComponentName}}': componentName.charAt(0).toUpperCase() + componentName.slice(1),
126
+ });
127
+ spinner.succeed(chalk.green(`Component "${componentName}" created successfully!`));
128
+ console.log(`\n🚀 Component added to ${chalk.cyan(relative(projectPath, targetDir))}`);
129
+ console.log(`\n💡 Next steps: import and use your component in your application`);
130
+ }
131
+ catch (error) {
132
+ spinner.fail(chalk.red('Failed to create component'));
133
+ console.error(error);
134
+ process.exit(1);
135
+ }
136
+ }
137
+ })
138
+ .showHelpAfterError(true)
139
+ .showSuggestionAfterError(true);
140
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createComponentStudioCommand(): Command;
@@ -0,0 +1,70 @@
1
+ import chalk from 'chalk';
2
+ import { Command } from 'commander';
3
+ import inquirer from 'inquirer';
4
+ import pkg from '../../package.json' with { type: 'json' };
5
+ import { isCommandAvailable, installGlobalPackage } from '../utils/helper';
6
+ import { createAddCommand } from './add';
7
+ import { createDevCommand } from './dev';
8
+ import { createInitCommand } from './init';
9
+ import { createMigrateCommand } from './migrate';
10
+ import { createUpdateCommand } from './update';
11
+ /**
12
+ * 检查必要的命令依赖是否已安装
13
+ */
14
+ async function checkDependencies() {
15
+ const missingDeps = [];
16
+ // 检查 blocklet 命令
17
+ if (!isCommandAvailable('blocklet')) {
18
+ missingDeps.push({ name: 'blocklet', package: '@blocklet/cli' });
19
+ }
20
+ // 检查 pnpm 命令
21
+ if (!isCommandAvailable('pnpm')) {
22
+ missingDeps.push({ name: 'pnpm', package: 'pnpm' });
23
+ }
24
+ // 如果有缺失的依赖
25
+ if (missingDeps.length > 0) {
26
+ console.log(chalk.yellow('Missing dependencies detected:'));
27
+ for (const dep of missingDeps) {
28
+ console.log(chalk.yellow(`- ${dep.name} (${dep.package})`));
29
+ }
30
+ // 询问用户是否自动安装
31
+ const answers = await inquirer.prompt([
32
+ {
33
+ type: 'confirm',
34
+ name: 'autoInstall',
35
+ message: 'Do you want to install these dependencies automatically?',
36
+ default: true,
37
+ },
38
+ ]);
39
+ const missingPackagesText = missingDeps.map((dep) => dep.package).join(' ');
40
+ if (answers.autoInstall) {
41
+ await installGlobalPackage(missingPackagesText);
42
+ }
43
+ else {
44
+ // 提供手动安装指令
45
+ console.log(chalk.blue('\nPlease install the following dependencies manually:'));
46
+ console.log(chalk.blue(` npm install -g ${missingPackagesText}`));
47
+ console.log('');
48
+ }
49
+ }
50
+ }
51
+ export function createComponentStudioCommand() {
52
+ const program = new Command()
53
+ .name('component-studio')
54
+ .description('CLI for Component Studio')
55
+ .version(pkg.version)
56
+ .addCommand(createInitCommand()) // 初始化项目
57
+ .addCommand(createDevCommand()) // 开发模式
58
+ .addCommand(createUpdateCommand()) // 更新依赖
59
+ .addCommand(createAddCommand()) // 添加组件
60
+ .addCommand(createMigrateCommand()) // 迁移组件
61
+ .showHelpAfterError(true)
62
+ .showSuggestionAfterError(true);
63
+ // 添加依赖检查逻辑
64
+ const originalParse = program.parseAsync;
65
+ program.parseAsync = async (argv) => {
66
+ await checkDependencies();
67
+ return originalParse.call(program, argv);
68
+ };
69
+ return program;
70
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createDevCommand(): Command;
@@ -0,0 +1,87 @@
1
+ import chalk from 'chalk';
2
+ import { spawn } from 'child_process';
3
+ import { Command } from 'commander';
4
+ import fs from 'fs-extra';
5
+ import { existsSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import ora from 'ora';
8
+ import { getWorkspaceTemplatePath, installDependencies, getWorkspacePath, getProjectPath } from '../utils/helper';
9
+ async function createDevServer(projectPath, _options) {
10
+ // 1. 创建工作区
11
+ const workspacePath = getWorkspacePath();
12
+ const workspaceSpinner = ora({ text: 'Creating workspace...', color: 'blue' }).start();
13
+ const workspacePathText = chalk.blue(`The workspace directory: ${workspacePath}`);
14
+ // 2. 准备基础环境
15
+ if (!existsSync(workspacePath)) {
16
+ const workspaceTemplatePath = getWorkspaceTemplatePath();
17
+ await fs.copy(workspaceTemplatePath, workspacePath);
18
+ workspaceSpinner.succeed(chalk.green(`Workspace created successfully! ${workspacePathText}`));
19
+ }
20
+ else {
21
+ workspaceSpinner.succeed(chalk.green(`Using existing workspace! ${workspacePathText}`));
22
+ }
23
+ // 3. 确保目录中有projects目录
24
+ const projectsDir = join(workspacePath, 'projects');
25
+ if (existsSync(projectsDir)) {
26
+ await fs.remove(projectsDir);
27
+ }
28
+ await fs.ensureDir(projectsDir);
29
+ // 4. 将用户项目目录链接到目录的projects下
30
+ const projectLinkSpinner = ora({ text: 'Setting up project to workspace...', color: 'blue' }).start();
31
+ const projectLinkDir = join(projectsDir, 'user-project');
32
+ await fs.ensureSymlink(projectPath, projectLinkDir);
33
+ projectLinkSpinner.succeed(chalk.green('Project linked to workspace successfully!'));
34
+ let shouldInstallWorkspaceDeps = !(await fs.exists(join(workspacePath, 'node_modules')));
35
+ let shouldInstallProjectDeps = !(await fs.exists(join(projectPath, 'node_modules')));
36
+ // 5. 安装依赖
37
+ if (shouldInstallWorkspaceDeps || shouldInstallProjectDeps) {
38
+ const dirsToInstall = {};
39
+ if (shouldInstallWorkspaceDeps) {
40
+ dirsToInstall['Workspace'] = workspacePath;
41
+ }
42
+ if (shouldInstallProjectDeps) {
43
+ dirsToInstall['Project'] = projectPath;
44
+ }
45
+ const result = await installDependencies(dirsToInstall);
46
+ if (!result) {
47
+ process.exit(1);
48
+ }
49
+ }
50
+ const devServerSpinner = ora({ text: 'Creating development server...', color: 'blue' }).start();
51
+ const devProcess = spawn('pnpm', ['run', 'dev'], {
52
+ cwd: workspacePath,
53
+ stdio: 'inherit',
54
+ });
55
+ devServerSpinner.succeed(chalk.green('Development server created successfully!'));
56
+ // 返回清理函数
57
+ return {
58
+ close: async () => {
59
+ if (devProcess && !devProcess.killed) {
60
+ const closeSpinner = ora({ text: 'Shutting down development server...', color: 'yellow' }).start();
61
+ devProcess.kill();
62
+ closeSpinner.succeed(chalk.green('Development server stopped'));
63
+ }
64
+ },
65
+ };
66
+ }
67
+ export function createDevCommand() {
68
+ return new Command('dev').description('Start development server').action(async (options) => {
69
+ const projectPath = getProjectPath();
70
+ try {
71
+ const server = await createDevServer(projectPath, options);
72
+ // 处理终止信号
73
+ const handleTermination = async () => {
74
+ await server.close();
75
+ process.exit(0);
76
+ };
77
+ process.on('SIGINT', handleTermination);
78
+ process.on('SIGTERM', handleTermination);
79
+ process.on('SIGHUP', handleTermination);
80
+ }
81
+ catch (error) {
82
+ console.error(chalk.red('Failed to start development server:'));
83
+ console.error(error);
84
+ process.exit(1);
85
+ }
86
+ });
87
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createInitCommand(): Command;
@@ -0,0 +1,114 @@
1
+ import chalk from 'chalk';
2
+ import { execSync } from 'child_process';
3
+ import { Command } from 'commander';
4
+ import inquirer from 'inquirer';
5
+ import { existsSync, mkdirSync, readdirSync } from 'node:fs';
6
+ import { isAbsolute, join, relative, resolve } from 'node:path';
7
+ import ora from 'ora';
8
+ import { copyTemplateFilesExcept, mergePackageJson, loadTemplates, selectTemplate, validateTemplate, getTemplatePath, } from '../utils/helper';
9
+ export function createInitCommand() {
10
+ return new Command('init')
11
+ .description('Initialize a new component project')
12
+ .argument('[path]', 'Path to create the project directory', '.')
13
+ .action(async (_path, options) => {
14
+ let path = _path;
15
+ const initTemplateDir = join(__dirname, '../../templates/init');
16
+ if (path === '.') {
17
+ const answers = await inquirer.prompt([
18
+ {
19
+ type: 'input',
20
+ name: 'projectName',
21
+ message: 'Project name:',
22
+ default: _path !== '.' ? _path : 'my-component-project',
23
+ validate: (input) => {
24
+ if (input.trim() === '')
25
+ return 'Project name cannot be empty.';
26
+ // Check for valid package name format
27
+ if (!/^[a-z0-9-_.]+$/.test(input)) {
28
+ return 'Project name can only contain lowercase letters, numbers, hyphens, underscores, and dots.';
29
+ }
30
+ return true;
31
+ },
32
+ },
33
+ ]);
34
+ path = answers.projectName;
35
+ }
36
+ path = isAbsolute(path) ? path : resolve(process.cwd(), path);
37
+ const isPathNotEmpty = existsSync(path) && readdirSync(path).length > 0;
38
+ if (isPathNotEmpty) {
39
+ const answers = await inquirer.prompt([
40
+ {
41
+ type: 'confirm',
42
+ name: 'overwrite',
43
+ message: `The directory "${path}" is not empty. Do you want to remove its contents?`,
44
+ default: false,
45
+ },
46
+ ]);
47
+ if (!answers.overwrite) {
48
+ console.log(chalk.yellow('Operation cancelled.'));
49
+ process.exit(0);
50
+ }
51
+ }
52
+ // 加载可用模板
53
+ const spinner = ora('Loading templates...').start();
54
+ const templates = loadTemplates(initTemplateDir);
55
+ spinner.succeed(`Templates loaded ${templates.length} templates`);
56
+ // 确保至少有一个模板
57
+ if (templates.length === 0) {
58
+ console.log(chalk.red('No templates found. Please check your installation.'));
59
+ process.exit(1);
60
+ }
61
+ // 如果命令行没有指定模板,则提示用户选择
62
+ let templateKey = options.template;
63
+ if (!templateKey) {
64
+ templateKey = await selectTemplate(templates);
65
+ }
66
+ // 验证所选模板是否存在
67
+ const selectedTemplate = validateTemplate(templateKey, templates);
68
+ if (!selectedTemplate) {
69
+ process.exit(1);
70
+ }
71
+ console.log(chalk.blue(`\nCreating project with "${selectedTemplate.name}" template...`));
72
+ // 1. 创建项目目录
73
+ mkdirSync(path, { recursive: true });
74
+ // 2. 使用 npm init 初始化项目
75
+ spinner.start('Initializing project...');
76
+ try {
77
+ execSync('npm init -y', { cwd: path, stdio: 'ignore' });
78
+ spinner.succeed('Initialize npm project successfully!');
79
+ }
80
+ catch (error) {
81
+ spinner.fail('Initialize npm project failed');
82
+ console.error(chalk.red('Error:'), error);
83
+ process.exit(1);
84
+ }
85
+ // 3. 复制模板文件,但排除@template.json
86
+ const templatePath = getTemplatePath(selectedTemplate.key, initTemplateDir);
87
+ if (!existsSync(templatePath)) {
88
+ console.log(chalk.red(`Template directory "${selectedTemplate.name}" not found.`));
89
+ process.exit(1);
90
+ }
91
+ spinner.start('Copying template files...');
92
+ try {
93
+ // 使用新的函数复制文件,排除@template.json
94
+ await copyTemplateFilesExcept(templatePath, path);
95
+ spinner.succeed('Copy template files successfully!');
96
+ }
97
+ catch (error) {
98
+ spinner.fail('Copy template files failed');
99
+ console.error(chalk.red('Error:'), error);
100
+ process.exit(1);
101
+ }
102
+ // 4. 合并 package.json
103
+ spinner.start('Merging package.json...');
104
+ await mergePackageJson(path, templatePath);
105
+ spinner.succeed('Merge package.json successfully!');
106
+ console.log(chalk.green('\n🎉 Create project successfully!'));
107
+ console.log(`\nNext steps:`);
108
+ console.log(` 1. ${chalk.cyan(`cd ${relative(process.cwd(), path)}`)}`);
109
+ console.log(` 2. ${chalk.cyan('pnpm run dev')}`);
110
+ console.log(`\nHappy coding! 🚀`);
111
+ })
112
+ .showHelpAfterError(true)
113
+ .showSuggestionAfterError(true);
114
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createMigrateCommand(): Command;