@blocklet/component-studio-cli 0.4.146 → 0.4.148
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.
- package/dist/commands/add.js +7 -1
- package/dist/commands/dev.js +55 -21
- package/dist/commands/init.js +4 -3
- package/dist/utils/helper.d.ts +5 -0
- package/dist/utils/helper.js +40 -3
- package/package.json +2 -1
- package/templates/init/0-basic/.gitignore_example +36 -0
- package/templates/init/1-professional/.gitignore_example +36 -0
- package/templates/init/2-blank/.gitignore_example +36 -0
- package/templates/workspace/.gitignore_example +29 -0
package/dist/commands/add.js
CHANGED
|
@@ -2,7 +2,8 @@ import chalk from 'chalk';
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import inquirer from 'inquirer';
|
|
4
4
|
import { camelCase, upperFirst } from 'lodash-es';
|
|
5
|
-
import {
|
|
5
|
+
import { nanoid } from 'nanoid';
|
|
6
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
6
7
|
import { join, relative } from 'node:path';
|
|
7
8
|
import ora from 'ora';
|
|
8
9
|
import { copyTemplateFilesExcept, loadTemplates, selectTemplate, validateTemplate, getTemplatePath, getProjectPath, replaceInDirectory, findClosestMetadataDir, checkTargetDirectoryExists, } from '../utils/helper.js';
|
|
@@ -124,6 +125,11 @@ export function createAddCommand() {
|
|
|
124
125
|
'{{component_name}}': componentName.toLowerCase(),
|
|
125
126
|
'{{ComponentName}}': componentName.charAt(0).toUpperCase() + componentName.slice(1),
|
|
126
127
|
});
|
|
128
|
+
// 替换 @metadata.json 中的 id,确保实例不重复
|
|
129
|
+
const metadataPath = join(targetDir, '@metadata.json');
|
|
130
|
+
const metadata = JSON.parse(readFileSync(metadataPath, 'utf-8'));
|
|
131
|
+
metadata.id = nanoid();
|
|
132
|
+
writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
|
127
133
|
spinner.succeed(chalk.green(`Component "${componentName}" created successfully!`));
|
|
128
134
|
console.log(`\n🚀 Component added to ${chalk.cyan(relative(projectPath, targetDir))}`);
|
|
129
135
|
console.log(`\n💡 Next steps: import and use your component in your application`);
|
package/dist/commands/dev.js
CHANGED
|
@@ -5,7 +5,7 @@ import fs from 'fs-extra';
|
|
|
5
5
|
import { existsSync } from 'node:fs';
|
|
6
6
|
import { join } from 'node:path';
|
|
7
7
|
import ora from 'ora';
|
|
8
|
-
import { getWorkspaceTemplatePath, installDependencies, getWorkspacePath, getProjectPath } from '../utils/helper.js';
|
|
8
|
+
import { getWorkspaceTemplatePath, installDependencies, getWorkspacePath, getProjectPath, replaceInDirectoryFileNameWithExample, } from '../utils/helper.js';
|
|
9
9
|
async function createDevServer(projectPath, _options) {
|
|
10
10
|
// 1. 创建工作区
|
|
11
11
|
const workspacePath = getWorkspacePath();
|
|
@@ -15,8 +15,8 @@ async function createDevServer(projectPath, _options) {
|
|
|
15
15
|
if (!existsSync(workspacePath)) {
|
|
16
16
|
const workspaceTemplatePath = getWorkspaceTemplatePath();
|
|
17
17
|
await fs.copy(workspaceTemplatePath, workspacePath);
|
|
18
|
-
// 修改 workspace
|
|
19
|
-
await
|
|
18
|
+
// 修改 workspace 目录中,_example 后缀的文件名,删除 _example 后缀
|
|
19
|
+
await replaceInDirectoryFileNameWithExample(workspacePath);
|
|
20
20
|
workspaceSpinner.succeed(chalk.green(`Workspace created successfully! ${workspacePathText}`));
|
|
21
21
|
}
|
|
22
22
|
else {
|
|
@@ -24,15 +24,36 @@ async function createDevServer(projectPath, _options) {
|
|
|
24
24
|
}
|
|
25
25
|
// 3. 确保目录中有projects目录
|
|
26
26
|
const projectsDir = join(workspacePath, 'projects');
|
|
27
|
+
const projectLinkDir = join(projectsDir, 'user-project');
|
|
28
|
+
const projectLinkSpinner = ora({ text: 'Setting up project to workspace...', color: 'blue' }).start();
|
|
29
|
+
// 检查是否已经正确链接
|
|
30
|
+
let needsNewLink = true;
|
|
27
31
|
if (existsSync(projectsDir)) {
|
|
28
|
-
|
|
32
|
+
try {
|
|
33
|
+
const stats = await fs.lstat(projectLinkDir);
|
|
34
|
+
if (stats.isSymbolicLink()) {
|
|
35
|
+
const currentTarget = await fs.readlink(projectLinkDir);
|
|
36
|
+
if (currentTarget === projectPath) {
|
|
37
|
+
needsNewLink = false;
|
|
38
|
+
projectLinkSpinner.succeed(chalk.green('Project already linked to workspace!'));
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
await fs.remove(projectsDir);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
await fs.remove(projectsDir);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
await fs.remove(projectsDir);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (needsNewLink) {
|
|
53
|
+
await fs.ensureDir(projectsDir);
|
|
54
|
+
await fs.ensureSymlink(projectPath, projectLinkDir);
|
|
55
|
+
projectLinkSpinner.succeed(chalk.green('Project linked to workspace successfully!'));
|
|
29
56
|
}
|
|
30
|
-
await fs.ensureDir(projectsDir);
|
|
31
|
-
// 4. 将用户项目目录链接到目录的projects下
|
|
32
|
-
const projectLinkSpinner = ora({ text: 'Setting up project to workspace...', color: 'blue' }).start();
|
|
33
|
-
const projectLinkDir = join(projectsDir, 'user-project');
|
|
34
|
-
await fs.ensureSymlink(projectPath, projectLinkDir);
|
|
35
|
-
projectLinkSpinner.succeed(chalk.green('Project linked to workspace successfully!'));
|
|
36
57
|
let shouldInstallWorkspaceDeps = !(await fs.exists(join(workspacePath, 'node_modules')));
|
|
37
58
|
let shouldInstallProjectDeps = !(await fs.exists(join(projectPath, 'node_modules')));
|
|
38
59
|
// 5. 安装依赖
|
|
@@ -53,14 +74,24 @@ async function createDevServer(projectPath, _options) {
|
|
|
53
74
|
const devProcess = spawn('pnpm', ['run', 'dev'], {
|
|
54
75
|
cwd: workspacePath,
|
|
55
76
|
stdio: 'inherit',
|
|
77
|
+
detached: false,
|
|
78
|
+
});
|
|
79
|
+
// 确保子进程能正确接收和处理信号
|
|
80
|
+
process.on('SIGINT', () => {
|
|
81
|
+
devProcess.kill('SIGINT');
|
|
82
|
+
});
|
|
83
|
+
process.on('SIGTERM', () => {
|
|
84
|
+
devProcess.kill('SIGTERM');
|
|
56
85
|
});
|
|
57
86
|
devServerSpinner.succeed(chalk.green('Development server created successfully!'));
|
|
58
87
|
// 返回清理函数
|
|
59
88
|
return {
|
|
89
|
+
shouldInstallWorkspaceDeps,
|
|
90
|
+
shouldInstallProjectDeps,
|
|
60
91
|
close: async () => {
|
|
61
|
-
if (devProcess
|
|
92
|
+
if (devProcess) {
|
|
62
93
|
const closeSpinner = ora({ text: 'Shutting down development server...', color: 'yellow' }).start();
|
|
63
|
-
devProcess.kill();
|
|
94
|
+
devProcess.kill('SIGTERM');
|
|
64
95
|
closeSpinner.succeed(chalk.green('Development server stopped'));
|
|
65
96
|
}
|
|
66
97
|
},
|
|
@@ -69,20 +100,23 @@ async function createDevServer(projectPath, _options) {
|
|
|
69
100
|
export function createDevCommand() {
|
|
70
101
|
return new Command('dev').description('Start development server').action(async (options) => {
|
|
71
102
|
const projectPath = getProjectPath();
|
|
103
|
+
const server = await createDevServer(projectPath, options);
|
|
104
|
+
// 处理终止信号
|
|
105
|
+
const handleTermination = async (signal) => {
|
|
106
|
+
console.log(chalk.yellow(`\nReceived ${signal} signal. Shutting down...`));
|
|
107
|
+
await server.close();
|
|
108
|
+
process.exit(0);
|
|
109
|
+
};
|
|
72
110
|
try {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
process.exit(0);
|
|
78
|
-
};
|
|
79
|
-
process.on('SIGINT', handleTermination);
|
|
80
|
-
process.on('SIGTERM', handleTermination);
|
|
81
|
-
process.on('SIGHUP', handleTermination);
|
|
111
|
+
// 使用 once 而不是 on,确保信号处理程序只被注册一次
|
|
112
|
+
process.once('SIGINT', () => handleTermination('SIGINT'));
|
|
113
|
+
process.once('SIGTERM', () => handleTermination('SIGTERM'));
|
|
114
|
+
process.once('SIGHUP', () => handleTermination('SIGHUP'));
|
|
82
115
|
}
|
|
83
116
|
catch (error) {
|
|
84
117
|
console.error(chalk.red('Failed to start development server:'));
|
|
85
118
|
console.error(error);
|
|
119
|
+
await server.close();
|
|
86
120
|
process.exit(1);
|
|
87
121
|
}
|
|
88
122
|
});
|
package/dist/commands/init.js
CHANGED
|
@@ -5,7 +5,7 @@ import inquirer from 'inquirer';
|
|
|
5
5
|
import { existsSync, mkdirSync, readdirSync } from 'node:fs';
|
|
6
6
|
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
7
7
|
import ora from 'ora';
|
|
8
|
-
import { copyTemplateFilesExcept, mergePackageJson, loadTemplates, selectTemplate, validateTemplate, getTemplatePath, } from '../utils/helper.js';
|
|
8
|
+
import { copyTemplateFilesExcept, mergePackageJson, loadTemplates, selectTemplate, validateTemplate, getTemplatePath, replaceInDirectoryFileNameWithExample, } from '../utils/helper.js';
|
|
9
9
|
export function createInitCommand() {
|
|
10
10
|
return new Command('init')
|
|
11
11
|
.description('Initialize a new component project')
|
|
@@ -92,6 +92,8 @@ export function createInitCommand() {
|
|
|
92
92
|
try {
|
|
93
93
|
// 使用新的函数复制文件,排除@template.json
|
|
94
94
|
await copyTemplateFilesExcept(templatePath, path);
|
|
95
|
+
// 替换目录中的文件名带 _example 后缀的文件名,删除 _example 后缀
|
|
96
|
+
await replaceInDirectoryFileNameWithExample(path);
|
|
95
97
|
spinner.succeed('Copy template files successfully!');
|
|
96
98
|
}
|
|
97
99
|
catch (error) {
|
|
@@ -104,10 +106,9 @@ export function createInitCommand() {
|
|
|
104
106
|
await mergePackageJson(path, templatePath);
|
|
105
107
|
spinner.succeed('Merge package.json successfully!');
|
|
106
108
|
console.log(chalk.green('\n🎉 Create project successfully!'));
|
|
107
|
-
console.log(`\
|
|
109
|
+
console.log(`\n💡 Next steps:`);
|
|
108
110
|
console.log(` 1. ${chalk.cyan(`cd ${relative(process.cwd(), path)}`)}`);
|
|
109
111
|
console.log(` 2. ${chalk.cyan('pnpm run dev')}`);
|
|
110
|
-
console.log(`\nHappy coding! 🚀`);
|
|
111
112
|
})
|
|
112
113
|
.showHelpAfterError(true)
|
|
113
114
|
.showSuggestionAfterError(true);
|
package/dist/utils/helper.d.ts
CHANGED
|
@@ -66,6 +66,11 @@ export declare function replaceInFile(filePath: string, replacements: Record<str
|
|
|
66
66
|
* @param replacements - 替换对象,键为占位符,值为替换内容
|
|
67
67
|
*/
|
|
68
68
|
export declare function replaceInDirectory(dirPath: string, replacements: Record<string, string>): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* 替换目录中的文件名带 _example 后缀的文件名,删除 _example 后缀
|
|
71
|
+
* @param dirPath - 目录路径
|
|
72
|
+
*/
|
|
73
|
+
export declare function replaceInDirectoryFileNameWithExample(dirPath: string): Promise<void>;
|
|
69
74
|
/**
|
|
70
75
|
* 查找最接近指定目录的包含@metadata.json的目录
|
|
71
76
|
* @param startDir - 起始目录
|
package/dist/utils/helper.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { execSync, spawn } from 'child_process';
|
|
3
|
+
import fs from 'fs-extra';
|
|
3
4
|
import { glob } from 'glob';
|
|
4
5
|
import inquirer from 'inquirer';
|
|
5
6
|
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
@@ -103,18 +104,33 @@ export async function installDependencies(dirs, operation = 'install') {
|
|
|
103
104
|
try {
|
|
104
105
|
const installPromises = Object.entries(dirs).map(([dirType, dirPath]) => {
|
|
105
106
|
return new Promise((resolve, reject) => {
|
|
107
|
+
let errorOutput = '';
|
|
106
108
|
const process = spawn('pnpm', ['update:deps'], {
|
|
107
109
|
cwd: dirPath,
|
|
108
|
-
|
|
110
|
+
stdio: ['inherit', 'pipe', 'pipe'], // 将 stdout 和 stderr 设为 pipe
|
|
111
|
+
});
|
|
112
|
+
// 收集错误输出
|
|
113
|
+
process.stderr?.on('data', (data) => {
|
|
114
|
+
errorOutput += data.toString();
|
|
115
|
+
});
|
|
116
|
+
// 收集标准输出(可能包含错误信息)
|
|
117
|
+
process.stdout?.on('data', (data) => {
|
|
118
|
+
const output = data.toString();
|
|
119
|
+
if (output.toLowerCase().includes('error')) {
|
|
120
|
+
errorOutput += output;
|
|
121
|
+
}
|
|
109
122
|
});
|
|
110
123
|
process.on('close', (code) => {
|
|
111
124
|
if (code === 0) {
|
|
112
125
|
resolve();
|
|
113
126
|
}
|
|
114
127
|
else {
|
|
115
|
-
reject(new Error(`${dirType} dependencies install failed with code ${code}`));
|
|
128
|
+
reject(new Error(`${dirType} dependencies install failed with code ${code}.\nError details:\n${errorOutput?.trimStart()}`));
|
|
116
129
|
}
|
|
117
130
|
});
|
|
131
|
+
process.on('error', (error) => {
|
|
132
|
+
reject(new Error(`${dirType} process error: ${error.message}\n${errorOutput}`));
|
|
133
|
+
});
|
|
118
134
|
});
|
|
119
135
|
});
|
|
120
136
|
// 制作一个假进度条,每秒增加 1%
|
|
@@ -141,7 +157,7 @@ export async function installDependencies(dirs, operation = 'install') {
|
|
|
141
157
|
}
|
|
142
158
|
catch (error) {
|
|
143
159
|
depsSpinner.fail(chalk.red('Failed to install dependencies'));
|
|
144
|
-
console.error(error);
|
|
160
|
+
console.error(error.message); // 只输出错误消息,包含了我们收集的详细错误信息
|
|
145
161
|
return false;
|
|
146
162
|
}
|
|
147
163
|
}
|
|
@@ -353,6 +369,27 @@ export async function replaceInDirectory(dirPath, replacements) {
|
|
|
353
369
|
throw error;
|
|
354
370
|
}
|
|
355
371
|
}
|
|
372
|
+
/**
|
|
373
|
+
* 替换目录中的文件名带 _example 后缀的文件名,删除 _example 后缀
|
|
374
|
+
* @param dirPath - 目录路径
|
|
375
|
+
*/
|
|
376
|
+
export async function replaceInDirectoryFileNameWithExample(dirPath) {
|
|
377
|
+
try {
|
|
378
|
+
// 读取目录内容
|
|
379
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
380
|
+
// 处理每个条目
|
|
381
|
+
for (const entry of entries) {
|
|
382
|
+
if (entry.isFile() && entry.name.endsWith('_example')) {
|
|
383
|
+
const newName = entry.name.replace('_example', '');
|
|
384
|
+
await fs.rename(join(dirPath, entry.name), join(dirPath, newName));
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
console.error(chalk.red(`Error replacing directory file names in ${dirPath}:`), error);
|
|
390
|
+
throw error;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
356
393
|
/**
|
|
357
394
|
* 查找最接近指定目录的包含@metadata.json的目录
|
|
358
395
|
* @param startDir - 起始目录
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/component-studio-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.148",
|
|
4
4
|
"description": "CLI for Component Studio",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"gradient-string": "^3.0.0",
|
|
38
38
|
"inquirer": "^12.5.2",
|
|
39
39
|
"lodash-es": "^4.17.21",
|
|
40
|
+
"nanoid": "^3.3.7",
|
|
40
41
|
"node-fetch": "^2.7.0",
|
|
41
42
|
"ora": "^8.2.0",
|
|
42
43
|
"pretty-error": "^4.0.0",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules
|
|
3
|
+
.pnp
|
|
4
|
+
.pnp.js
|
|
5
|
+
|
|
6
|
+
# Production
|
|
7
|
+
dist
|
|
8
|
+
build
|
|
9
|
+
lib
|
|
10
|
+
|
|
11
|
+
# Misc
|
|
12
|
+
.DS_Store
|
|
13
|
+
.env.local
|
|
14
|
+
.env.development.local
|
|
15
|
+
.env.test.local
|
|
16
|
+
.env.production.local
|
|
17
|
+
|
|
18
|
+
# Debug
|
|
19
|
+
npm-debug.log*
|
|
20
|
+
yarn-debug.log*
|
|
21
|
+
yarn-error.log*
|
|
22
|
+
|
|
23
|
+
# Editor
|
|
24
|
+
.idea
|
|
25
|
+
.vscode/*
|
|
26
|
+
!.vscode/extensions.json
|
|
27
|
+
!.vscode/settings.json
|
|
28
|
+
*.suo
|
|
29
|
+
*.ntvs*
|
|
30
|
+
*.njsproj
|
|
31
|
+
*.sln
|
|
32
|
+
*.sw?
|
|
33
|
+
|
|
34
|
+
# Logs
|
|
35
|
+
logs
|
|
36
|
+
*.log
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules
|
|
3
|
+
.pnp
|
|
4
|
+
.pnp.js
|
|
5
|
+
|
|
6
|
+
# Production
|
|
7
|
+
dist
|
|
8
|
+
build
|
|
9
|
+
lib
|
|
10
|
+
|
|
11
|
+
# Misc
|
|
12
|
+
.DS_Store
|
|
13
|
+
.env.local
|
|
14
|
+
.env.development.local
|
|
15
|
+
.env.test.local
|
|
16
|
+
.env.production.local
|
|
17
|
+
|
|
18
|
+
# Debug
|
|
19
|
+
npm-debug.log*
|
|
20
|
+
yarn-debug.log*
|
|
21
|
+
yarn-error.log*
|
|
22
|
+
|
|
23
|
+
# Editor
|
|
24
|
+
.idea
|
|
25
|
+
.vscode/*
|
|
26
|
+
!.vscode/extensions.json
|
|
27
|
+
!.vscode/settings.json
|
|
28
|
+
*.suo
|
|
29
|
+
*.ntvs*
|
|
30
|
+
*.njsproj
|
|
31
|
+
*.sln
|
|
32
|
+
*.sw?
|
|
33
|
+
|
|
34
|
+
# Logs
|
|
35
|
+
logs
|
|
36
|
+
*.log
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules
|
|
3
|
+
.pnp
|
|
4
|
+
.pnp.js
|
|
5
|
+
|
|
6
|
+
# Production
|
|
7
|
+
dist
|
|
8
|
+
build
|
|
9
|
+
lib
|
|
10
|
+
|
|
11
|
+
# Misc
|
|
12
|
+
.DS_Store
|
|
13
|
+
.env.local
|
|
14
|
+
.env.development.local
|
|
15
|
+
.env.test.local
|
|
16
|
+
.env.production.local
|
|
17
|
+
|
|
18
|
+
# Debug
|
|
19
|
+
npm-debug.log*
|
|
20
|
+
yarn-debug.log*
|
|
21
|
+
yarn-error.log*
|
|
22
|
+
|
|
23
|
+
# Editor
|
|
24
|
+
.idea
|
|
25
|
+
.vscode/*
|
|
26
|
+
!.vscode/extensions.json
|
|
27
|
+
!.vscode/settings.json
|
|
28
|
+
*.suo
|
|
29
|
+
*.ntvs*
|
|
30
|
+
*.njsproj
|
|
31
|
+
*.sln
|
|
32
|
+
*.sw?
|
|
33
|
+
|
|
34
|
+
# Logs
|
|
35
|
+
logs
|
|
36
|
+
*.log
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
2
|
+
|
|
3
|
+
# dependencies
|
|
4
|
+
.pnp
|
|
5
|
+
.pnp.js
|
|
6
|
+
.DS_Store
|
|
7
|
+
node_modules
|
|
8
|
+
|
|
9
|
+
# testing
|
|
10
|
+
coverage
|
|
11
|
+
|
|
12
|
+
# production
|
|
13
|
+
build
|
|
14
|
+
dist
|
|
15
|
+
dist-ssr
|
|
16
|
+
.blocklet
|
|
17
|
+
.next
|
|
18
|
+
|
|
19
|
+
# local env files
|
|
20
|
+
*.local
|
|
21
|
+
|
|
22
|
+
# Log files
|
|
23
|
+
logs
|
|
24
|
+
*.log
|
|
25
|
+
npm-debug.log*
|
|
26
|
+
yarn-debug.log*
|
|
27
|
+
yarn-error.log*
|
|
28
|
+
pnpm-debug.log*
|
|
29
|
+
lerna-debug.log*
|