@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
@@ -0,0 +1,177 @@
1
+ import chalk from 'chalk';
2
+ import { Command } from 'commander';
3
+ import fetch from 'node-fetch';
4
+ import { createWriteStream, mkdirSync, readdirSync, readFileSync, rmSync } from 'node:fs';
5
+ import { join, relative } from 'node:path';
6
+ import { pipeline } from 'node:stream/promises';
7
+ import ora from 'ora';
8
+ import os from 'os';
9
+ import { x } from 'tar';
10
+ import { findClosestMetadataDir, getProjectPath, isPackageInstalled, checkPackageReference, installMissingPackage, } from '../utils/helper';
11
+ export function createMigrateCommand() {
12
+ return new Command('migrate')
13
+ .description('Migrate components from pages-kit project')
14
+ .argument('<url>', 'URL to migrate components from')
15
+ .option('--output-dir <dir>', 'Custom output directory for components')
16
+ .action(async (url, options) => {
17
+ const spinner = ora('Preparing to migrate components...').start();
18
+ const projectPath = getProjectPath();
19
+ try {
20
+ // 创建临时目录
21
+ const tempDir = join(os.tmpdir(), `component-studio-migrate-${new Date().getTime()}`);
22
+ const zipPath = join(tempDir, 'components.zip');
23
+ mkdirSync(tempDir, { recursive: true });
24
+ // 下载文件
25
+ spinner.text = 'Downloading migration package...';
26
+ try {
27
+ const response = await fetch(url);
28
+ if (!response.ok) {
29
+ throw new Error(`${response.statusText} (${response.status})`);
30
+ }
31
+ // 检查响应类型
32
+ const contentType = response.headers.get('content-type') || '';
33
+ const isZip = contentType.includes('application/zip') || contentType.includes('application/x-zip');
34
+ if (!isZip) {
35
+ throw new Error('Response is not a ZIP file');
36
+ }
37
+ // 保存ZIP文件
38
+ await pipeline(response.body, createWriteStream(zipPath));
39
+ spinner.succeed('Downloaded migration package successfully!');
40
+ }
41
+ catch (error) {
42
+ spinner.fail(`Download failed: ${error.message}`);
43
+ rmSync(tempDir, { recursive: true, force: true });
44
+ process.exit(1);
45
+ }
46
+ // 解压ZIP文件
47
+ spinner.start('Extracting components...');
48
+ const extractDir = join(tempDir, 'extracted');
49
+ mkdirSync(extractDir, { recursive: true });
50
+ try {
51
+ await x({ file: zipPath, cwd: extractDir });
52
+ spinner.succeed('Extracted migration package successfully!');
53
+ }
54
+ catch (error) {
55
+ spinner.fail(`Extract failed: ${error.message}`);
56
+ rmSync(tempDir, { recursive: true, force: true });
57
+ process.exit(1);
58
+ }
59
+ // 查找组件目录
60
+ let componentsSourceDir = extractDir;
61
+ const extractedFiles = readdirSync(extractDir);
62
+ // 如果有'components'目录,使用它
63
+ if (extractedFiles.includes('components')) {
64
+ componentsSourceDir = join(extractDir, 'components');
65
+ }
66
+ // 检查组件目录
67
+ const componentNames = readdirSync(componentsSourceDir);
68
+ if (componentNames.length === 0) {
69
+ spinner.fail('No components found in the migration package');
70
+ rmSync(tempDir, { recursive: true, force: true });
71
+ process.exit(1);
72
+ }
73
+ spinner.info(`Found ${componentNames.length} components: ${componentNames.join(', ')}`);
74
+ // 确定目标目录
75
+ let targetComponentsDir;
76
+ if (options.outputDir) {
77
+ targetComponentsDir = join(projectPath, options.outputDir);
78
+ mkdirSync(targetComponentsDir, { recursive: true });
79
+ }
80
+ else {
81
+ // 查找最近的组件目录
82
+ spinner.start('Detecting components directory...');
83
+ const closestDir = await findClosestMetadataDir(projectPath);
84
+ if (closestDir) {
85
+ targetComponentsDir = closestDir;
86
+ spinner.succeed(`Found components directory: ${chalk.cyan(relative(projectPath, targetComponentsDir))}`);
87
+ }
88
+ else {
89
+ // 默认使用 src
90
+ targetComponentsDir = join(projectPath, 'src');
91
+ mkdirSync(targetComponentsDir, { recursive: true });
92
+ spinner.info(`Using default components directory: ${chalk.cyan('src')}`);
93
+ }
94
+ }
95
+ // 导入组件
96
+ spinner.start('Importing components...');
97
+ let importCount = 0;
98
+ let skipCount = 0;
99
+ let hasPagesKitDep = false;
100
+ for (const componentName of componentNames) {
101
+ const sourceDir = join(componentsSourceDir, componentName);
102
+ const targetDir = join(targetComponentsDir, componentName);
103
+ // 检查目标目录是否已存在
104
+ try {
105
+ readdirSync(targetDir);
106
+ spinner.info(`Skipping existing component: ${componentName}`);
107
+ skipCount++;
108
+ continue;
109
+ }
110
+ catch (err) {
111
+ // 目录不存在,可以导入
112
+ }
113
+ // 复制组件
114
+ try {
115
+ const copyFiles = (source, target) => {
116
+ mkdirSync(target, { recursive: true });
117
+ for (const file of readdirSync(source)) {
118
+ const sourcePath = join(source, file);
119
+ const targetPath = join(target, file);
120
+ // 检查是否是目录
121
+ try {
122
+ readdirSync(sourcePath);
123
+ copyFiles(sourcePath, targetPath);
124
+ }
125
+ catch (err) {
126
+ // 是文件,直接复制
127
+ const content = readFileSync(sourcePath);
128
+ require('fs').writeFileSync(targetPath, content);
129
+ // 检查index.tsx文件是否引用了@blocklet/pages-kit
130
+ if (file === 'index.tsx') {
131
+ if (checkPackageReference(targetPath, '@blocklet/pages-kit')) {
132
+ hasPagesKitDep = true;
133
+ }
134
+ }
135
+ }
136
+ }
137
+ };
138
+ copyFiles(sourceDir, targetDir);
139
+ importCount++;
140
+ }
141
+ catch (error) {
142
+ spinner.warn(`Failed to import component ${componentName}: ${error.message}`);
143
+ }
144
+ }
145
+ // 检查和安装依赖
146
+ if (hasPagesKitDep && !isPackageInstalled('@blocklet/pages-kit', projectPath)) {
147
+ spinner.info('Detected `@blocklet/pages-kit` dependency in migrated components');
148
+ spinner.stop();
149
+ installMissingPackage('@blocklet/pages-kit', projectPath);
150
+ spinner.start('Installing `@blocklet/pages-kit` dependencies...');
151
+ }
152
+ // 清理临时文件
153
+ rmSync(tempDir, { recursive: true, force: true });
154
+ if (importCount > 0) {
155
+ spinner.succeed(`Successfully imported ${importCount} components to ${chalk.cyan(relative(projectPath, targetComponentsDir))}`);
156
+ if (skipCount > 0) {
157
+ spinner.info(chalk.yellow(`Skipped ${skipCount} components that already exist`));
158
+ }
159
+ console.log(chalk.green('\n🎉 Components migrated successfully!'));
160
+ console.log(`\n💡 Next steps: Check the migrated components in your component studio`);
161
+ }
162
+ else {
163
+ spinner.fail('No components were imported');
164
+ if (skipCount > 0) {
165
+ console.log(chalk.yellow(`All ${skipCount} components already exist in your project`));
166
+ }
167
+ }
168
+ }
169
+ catch (error) {
170
+ spinner.fail(`Migration failed: ${error.message}`);
171
+ console.error(error);
172
+ process.exit(1);
173
+ }
174
+ })
175
+ .showHelpAfterError(true)
176
+ .showSuggestionAfterError(true);
177
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createUpdateCommand(): Command;
@@ -0,0 +1,22 @@
1
+ import { Command } from 'commander';
2
+ import { installDependencies, getProjectPath, getWorkspacePath, installGlobalPackage } from '../utils/helper';
3
+ export function createUpdateCommand() {
4
+ return new Command('update').description('Update cli and project dependencies').action(async () => {
5
+ const projectPath = getProjectPath();
6
+ const workspacePath = getWorkspacePath();
7
+ try {
8
+ await installGlobalPackage('@blocklet/component-studio-cli@latest');
9
+ const dirsToInstall = {};
10
+ dirsToInstall['Workspace'] = workspacePath;
11
+ dirsToInstall['Project'] = projectPath;
12
+ const result = await installDependencies(dirsToInstall, 'update');
13
+ if (!result) {
14
+ process.exit(1);
15
+ }
16
+ }
17
+ catch (error) {
18
+ console.error(error);
19
+ process.exit(1);
20
+ }
21
+ });
22
+ }
@@ -0,0 +1,2 @@
1
+ export declare const asciiLogo: string;
2
+ export declare function displayLogo(): void;
@@ -0,0 +1,24 @@
1
+ import chalk from 'chalk';
2
+ import gradient from 'gradient-string';
3
+ import pkg from '../../package.json' with { type: 'json' };
4
+ const modernGradient = gradient(['#4facfe', '#7367f0', '#f86aad']);
5
+ const logo = `
6
+ ______ __ _____ __ ___
7
+ / ____/___ ____ ___ ____ ____ ____ ___ ____ / /_ / ___// /___ ______/ (_)___
8
+ / / / __ \\/ __ \`__ \\/ __ \\/ __ \\/ __ \\/ _ \\/ __ \\/ __/ \\__ \\/ __/ / / / __ / / __ \\
9
+ / /___/ /_/ / / / / / / /_/ / /_/ / / / / __/ / / / /_ ___/ / /_/ /_/ / /_/ / / /_/ /
10
+ \\____/\\____/_/ /_/ /_/ .___/\\____/_/ /_/\\___/_/ /_/\\__/ /____/\\__/\\__,_/\\__,_/_/\\____/
11
+ /_/
12
+ `;
13
+ const frameworkInfo = `v${pkg.version}`;
14
+ const logoLines = logo.split('\n');
15
+ const maxLength = Math.max(...logoLines.filter((line) => line.trim()).map((line) => line.length));
16
+ const versionText = frameworkInfo;
17
+ const padding = Math.floor((maxLength - versionText.length) / 2);
18
+ const centeredVersion = ' '.repeat(padding) + versionText;
19
+ export const asciiLogo = `${modernGradient(logo)}\n${chalk.cyan(centeredVersion)}\n\n`;
20
+ export function displayLogo() {
21
+ console.log(gradient(['#5383e8', '#22a5f7', '#00c2b8', '#56e39f']).multiline(logo));
22
+ console.log(chalk.cyan(centeredVersion));
23
+ console.log('');
24
+ }
@@ -0,0 +1,97 @@
1
+ export declare function getWorkspaceTemplatePath(): string;
2
+ export declare function getWorkspacePath(): string;
3
+ export declare function getProjectPath(): string;
4
+ /**
5
+ * 复制模板文件,但排除某些特定文件
6
+ * @param sourcePath - 源模板路径
7
+ * @param targetPath - 目标项目路径
8
+ */
9
+ export declare function copyTemplateFilesExcept(sourcePath: string, targetPath: string): Promise<void>;
10
+ /**
11
+ * 合并基础 package.json 和模板的 package.json
12
+ * @param basePath - 基础包路径
13
+ * @param templatePath - 模板路径
14
+ */
15
+ export declare function mergePackageJson(basePath: string, templatePath: string): Promise<void>;
16
+ /**
17
+ * 在指定目录安装依赖
18
+ * @param dirs - 需要安装依赖的目录对象,key为目录描述,value为目录路径
19
+ * @returns 安装结果
20
+ */
21
+ export declare function installDependencies(dirs: Record<string, string>, operation?: 'update' | 'install'): Promise<boolean>;
22
+ /**
23
+ * 模板接口定义
24
+ */
25
+ export interface Template {
26
+ key: string;
27
+ name: string;
28
+ description: string;
29
+ }
30
+ /**
31
+ * 从模板目录读取所有可用模板
32
+ * @param templatesDir - 模板目录路径,默认为 templates/init
33
+ * @returns 可用模板数组
34
+ */
35
+ export declare function loadTemplates(templatesDir: string): Template[];
36
+ /**
37
+ * 交互式选择模板,支持模板分组
38
+ * @param templates - 模板数组
39
+ * @param defaultTemplate - 可选的默认模板键名
40
+ * @returns Promise<string> 选择的模板键名
41
+ */
42
+ export declare function selectTemplate(templates: Template[], defaultTemplate?: string, groupByDir?: boolean): Promise<string>;
43
+ /**
44
+ * 验证所选模板是否存在
45
+ * @param templateKey - 模板键名
46
+ * @param templates - 模板数组
47
+ * @returns 选中的模板或null
48
+ */
49
+ export declare function validateTemplate(templateKey: string, templates: Template[]): Template | null;
50
+ /**
51
+ * 获取模板路径
52
+ * @param templateKey - 模板键名
53
+ * @param templatesBaseDir - 模板基础目录,默认为 templates/init
54
+ * @returns 模板完整路径
55
+ */
56
+ export declare function getTemplatePath(templateKey: string, templatesBaseDir: string): string;
57
+ /**
58
+ * 在文件中批量替换占位符
59
+ * @param filePath - 文件路径
60
+ * @param replacements - 替换对象,键为占位符,值为替换内容
61
+ */
62
+ export declare function replaceInFile(filePath: string, replacements: Record<string, string>): Promise<void>;
63
+ /**
64
+ * 在目录中递归替换所有文件中的占位符
65
+ * @param dirPath - 目录路径
66
+ * @param replacements - 替换对象,键为占位符,值为替换内容
67
+ */
68
+ export declare function replaceInDirectory(dirPath: string, replacements: Record<string, string>): Promise<void>;
69
+ /**
70
+ * 查找最接近指定目录的包含@metadata.json的目录
71
+ * @param startDir - 起始目录
72
+ * @returns 找到的最近目录或null
73
+ */
74
+ export declare function findClosestMetadataDir(startDir: string): Promise<string | null>;
75
+ /**
76
+ * 检查目标目录是否存在,如存在则询问是否覆盖
77
+ * @param targetDir - 目标目录路径
78
+ * @param itemType - 项目类型名称 (如 'component', 'tool')
79
+ * @param targetBaseDir - 基础目录路径(用于显示相对路径)
80
+ * @returns 是否可以继续操作(不存在或用户同意覆盖)
81
+ */
82
+ export declare function checkTargetDirectoryExists(targetDir: string, targetBaseDir: string): Promise<boolean>;
83
+ export declare function isPackageInstalled(packageName: string, projectPath: string): boolean;
84
+ export declare function checkPackageReference(filePath: string, packageName: string): boolean;
85
+ export declare function installMissingPackage(packageName: string, projectPath: string): boolean;
86
+ /**
87
+ * 检查系统中是否存在指定命令
88
+ * @param command - 要检查的命令名称
89
+ * @returns 命令是否存在
90
+ */
91
+ export declare function isCommandAvailable(command: string): boolean;
92
+ /**
93
+ * 安装全局NPM包
94
+ * @param packageName - 要安装的包名
95
+ * @returns 安装是否成功
96
+ */
97
+ export declare function installGlobalPackage(packageName: string): Promise<boolean>;