@bams-app/work-cli 0.0.1
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/README.md +235 -0
- package/bin/work-cli.js +74 -0
- package/commands/add.js +143 -0
- package/commands/build.js +83 -0
- package/commands/create.js +195 -0
- package/commands/dev.js +95 -0
- package/commands/env.js +39 -0
- package/commands/pages-entry.js +37 -0
- package/commands/pages-from-components.js +37 -0
- package/commands/umd.js +172 -0
- package/commands/update.js +119 -0
- package/index.js +8 -0
- package/lib/utils.js +196 -0
- package/package.json +29 -0
- package/templates/project/.envs/.env.dev-demo +14 -0
- package/templates/project/.envs/README.md +45 -0
- package/templates/project/.proxy.js.example +11 -0
- package/templates/project/README.md +58 -0
- package/templates/project/gitignore.tpl +35 -0
- package/templates/project/package.json +20 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const { copyDir, exists, isDirectory, toKebabCase, error, success, info, log } = require('../lib/utils');
|
|
5
|
+
|
|
6
|
+
const WORK_CLI_ROOT = path.resolve(__dirname, '../');
|
|
7
|
+
const TEMPLATES_DIR = path.join(WORK_CLI_ROOT, 'templates');
|
|
8
|
+
const PROJECT_TEMPLATE_DIR = path.join(TEMPLATES_DIR, 'project');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 探测 work 仓库根目录(本地验证模式定位 file: 引用来源)
|
|
12
|
+
* 1. WORK_PROJECT_ROOT 环境变量优先
|
|
13
|
+
* 2. 通过 file: 安装的 @bams-app/ui-dev-server 包真实路径上溯(ui-dev-server -> cli -> work -> 仓库根)
|
|
14
|
+
* 3. 均不可用(registry 安装)时返回空,走发布模式
|
|
15
|
+
* @returns {string} work 仓库根目录或空字符串
|
|
16
|
+
*/
|
|
17
|
+
function detectWorkRoot() {
|
|
18
|
+
if (process.env.WORK_PROJECT_ROOT) {
|
|
19
|
+
return process.env.WORK_PROJECT_ROOT;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const pkgPath = require.resolve('@bams-app/ui-dev-server/package.json');
|
|
23
|
+
const candidate = path.resolve(path.dirname(pkgPath), '../../..');
|
|
24
|
+
if (isDirectory(path.join(candidate, 'work', 'packages'))) {
|
|
25
|
+
return candidate;
|
|
26
|
+
}
|
|
27
|
+
} catch (e) {
|
|
28
|
+
// 忽略探测失败,回退发布模式
|
|
29
|
+
}
|
|
30
|
+
return '';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 生成 ui-dev-server 依赖声明
|
|
35
|
+
* - 本地验证模式(探测到 work 仓库)使用 file: 引用 work 仓库本地包
|
|
36
|
+
* - 否则使用 registry 版本号(发布后模式)
|
|
37
|
+
*/
|
|
38
|
+
function buildUiDevServerDep() {
|
|
39
|
+
const workRoot = detectWorkRoot();
|
|
40
|
+
if (workRoot) {
|
|
41
|
+
const pkgDir = path.join(workRoot, 'work', 'cli', 'ui-dev-server');
|
|
42
|
+
if (isDirectory(pkgDir)) {
|
|
43
|
+
return `"@bams-app/ui-dev-server": "file:${pkgDir}"`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return `"@bams-app/ui-dev-server": "^0.1.0"`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 生成能力包依赖声明(单一来源:ui-dev-server 的 dependencies 中 @bams-app/*)
|
|
51
|
+
* - 本地验证模式(探测到 work 仓库)使用 file: 引用 work 仓库本地包(覆盖未发布包)
|
|
52
|
+
* - 否则返回空(发布后能力包由 ui-dev-server 的 dependencies 自动安装)
|
|
53
|
+
*/
|
|
54
|
+
function buildCapabilityDeps() {
|
|
55
|
+
const workRoot = detectWorkRoot();
|
|
56
|
+
if (!workRoot) {
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
const uiDevServerPkg = require(require.resolve('@bams-app/ui-dev-server/package.json'));
|
|
60
|
+
return Object.entries(uiDevServerPkg.dependencies || {})
|
|
61
|
+
.filter(([name]) => name.startsWith('@bams-app/'))
|
|
62
|
+
.map(([name]) => {
|
|
63
|
+
const short = name.replace('@bams-app/', '');
|
|
64
|
+
// 在 work/configs、work/packages、work/ui、work/cli 多个位置依次查找包目录
|
|
65
|
+
const pkgDir = short === 'configs' && isDirectory(path.join(workRoot, 'work', 'configs'))
|
|
66
|
+
? path.join(workRoot, 'work', 'configs')
|
|
67
|
+
: [
|
|
68
|
+
path.join(workRoot, 'work', 'packages', short),
|
|
69
|
+
path.join(workRoot, 'work', 'ui', short),
|
|
70
|
+
path.join(workRoot, 'work', 'cli', short)
|
|
71
|
+
].find(dir => isDirectory(dir));
|
|
72
|
+
return `,\n "${name}": "file:${pkgDir}"`;
|
|
73
|
+
})
|
|
74
|
+
.join('');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 委托 @bams-app/create-ui-component 创建示例组件(复用 work 的模板与命令)
|
|
79
|
+
*/
|
|
80
|
+
function createDemoComponent(targetDir) {
|
|
81
|
+
const createComponentBin = require.resolve('@bams-app/create-ui-component/bin/create-ui-component.js');
|
|
82
|
+
|
|
83
|
+
return new Promise((resolve) => {
|
|
84
|
+
const child = spawn(
|
|
85
|
+
process.execPath,
|
|
86
|
+
[
|
|
87
|
+
createComponentBin,
|
|
88
|
+
'--dir', path.join(targetDir, 'bams-components'),
|
|
89
|
+
'--name', 'ui-component-demo',
|
|
90
|
+
'--desc', 'componentDemo',
|
|
91
|
+
'--no-install'
|
|
92
|
+
],
|
|
93
|
+
{
|
|
94
|
+
cwd: targetDir,
|
|
95
|
+
stdio: 'inherit'
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
child.on('error', (err) => {
|
|
99
|
+
error(`创建示例组件失败: ${err.message}`);
|
|
100
|
+
resolve(1);
|
|
101
|
+
});
|
|
102
|
+
child.on('exit', (code) => {
|
|
103
|
+
resolve(code ?? 1);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function main(args) {
|
|
109
|
+
const force = args.includes('--force') || args.includes('-f');
|
|
110
|
+
const projectNameArg = args.find((a) => !a.startsWith('-'));
|
|
111
|
+
|
|
112
|
+
if (!projectNameArg) {
|
|
113
|
+
error('用法: bams-work create <project-name> [--force]');
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const projectName = toKebabCase(projectNameArg);
|
|
118
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(projectName)) {
|
|
119
|
+
error(`项目名不合法: ${projectNameArg}(仅允许小写字母、数字、连字符)`);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const targetDir = path.resolve(process.cwd(), projectName);
|
|
124
|
+
|
|
125
|
+
if (exists(targetDir)) {
|
|
126
|
+
if (!isDirectory(targetDir)) {
|
|
127
|
+
error(`目标路径已存在且不是目录: ${targetDir}`);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
const hasContent = fs.readdirSync(targetDir).length > 0;
|
|
131
|
+
if (hasContent && !force) {
|
|
132
|
+
error(`目录已存在且非空: ${targetDir}(如需覆盖请加 --force)`);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
if (hasContent) {
|
|
136
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 1. 拷贝项目模板
|
|
141
|
+
copyDir(PROJECT_TEMPLATE_DIR, targetDir);
|
|
142
|
+
|
|
143
|
+
const gitignoreTpl = path.join(targetDir, 'gitignore.tpl');
|
|
144
|
+
if (fs.existsSync(gitignoreTpl)) {
|
|
145
|
+
fs.renameSync(gitignoreTpl, path.join(targetDir, '.gitignore'));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 2. 生成 .bams-work 标记目录(findProjectRoot 定位项目根)
|
|
149
|
+
fs.mkdirSync(path.join(targetDir, '.bams-work'), { recursive: true });
|
|
150
|
+
fs.writeFileSync(
|
|
151
|
+
path.join(targetDir, '.bams-work', 'README.md'),
|
|
152
|
+
'本目录为 BAMS-Work 项目标记,请勿删除。\n',
|
|
153
|
+
'utf8'
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// 3. 替换项目 package.json 占位符
|
|
157
|
+
const projectPackageJson = path.join(targetDir, 'package.json');
|
|
158
|
+
const pkgContent = fs.readFileSync(projectPackageJson, 'utf8')
|
|
159
|
+
.split('__PROJECT_NAME__').join(projectName)
|
|
160
|
+
.split('__UI_DEV_SERVER_DEP__').join(buildUiDevServerDep())
|
|
161
|
+
.split('__CAPABILITY_DEPS__').join(buildCapabilityDeps());
|
|
162
|
+
fs.writeFileSync(projectPackageJson, pkgContent, 'utf8');
|
|
163
|
+
|
|
164
|
+
// 4. 创建示例组件(复用 work 的 create-ui-component 模板与命令)
|
|
165
|
+
const demoCode = await createDemoComponent(targetDir);
|
|
166
|
+
if (demoCode !== 0) {
|
|
167
|
+
error('示例组件创建失败,请检查后重试');
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
log('');
|
|
172
|
+
success(`项目创建成功: ${projectName}`);
|
|
173
|
+
log('');
|
|
174
|
+
info('下一步:');
|
|
175
|
+
log(` cd ${projectName}`);
|
|
176
|
+
log(' npm install # 或 yarn install(ui-dev-server 与能力包将安装到根 node_modules)');
|
|
177
|
+
log('');
|
|
178
|
+
log('启动 work 开发环境:');
|
|
179
|
+
log(` bams-work dev demo ui-component-demo # 加载 .envs/.env.dev-demo`);
|
|
180
|
+
log('');
|
|
181
|
+
log('创建新组件:');
|
|
182
|
+
log(' bams-work add --dir energy-ui --name ui-order-list');
|
|
183
|
+
log('');
|
|
184
|
+
log('更多命令:bams-work --help');
|
|
185
|
+
log('');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
module.exports = main;
|
|
189
|
+
|
|
190
|
+
if (require.main === module) {
|
|
191
|
+
main(process.argv.slice(2)).catch((err) => {
|
|
192
|
+
error(err.message || err);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
});
|
|
195
|
+
}
|
package/commands/dev.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { findProjectRoot, error, info, run } = require('../lib/utils');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 用法: bams-work dev <env> [component] [--layout pc|touch] [-- <vue-cli args>]
|
|
7
|
+
* 委托 @bams-app/shared-env 加载环境变量,并启动 @bams-app/ui-dev-server 开发服务
|
|
8
|
+
*/
|
|
9
|
+
async function main(args) {
|
|
10
|
+
const projectRoot = findProjectRoot(process.cwd());
|
|
11
|
+
if (!projectRoot) {
|
|
12
|
+
error('未找到 BAMS-Work 项目(缺少 .bams-work 目录),请先执行 bams-work create <name>');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let envAlias = null;
|
|
17
|
+
let component = '';
|
|
18
|
+
let layout = 'pc';
|
|
19
|
+
let serviceArgs = [];
|
|
20
|
+
let i = 0;
|
|
21
|
+
|
|
22
|
+
while (i < args.length) {
|
|
23
|
+
const arg = args[i];
|
|
24
|
+
if (arg === '--') {
|
|
25
|
+
serviceArgs = args.slice(i + 1);
|
|
26
|
+
break;
|
|
27
|
+
} else if (arg === '--layout' || arg === '-l') {
|
|
28
|
+
layout = args[i + 1];
|
|
29
|
+
i += 2;
|
|
30
|
+
} else if (arg === '--component' || arg === '-c') {
|
|
31
|
+
component = args[i + 1];
|
|
32
|
+
i += 2;
|
|
33
|
+
} else if (arg.startsWith('--')) {
|
|
34
|
+
serviceArgs = args.slice(i);
|
|
35
|
+
break;
|
|
36
|
+
} else if (!envAlias) {
|
|
37
|
+
envAlias = arg;
|
|
38
|
+
} else if (!component) {
|
|
39
|
+
component = arg;
|
|
40
|
+
} else {
|
|
41
|
+
serviceArgs = args.slice(i);
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
i++;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!envAlias) {
|
|
48
|
+
error('用法: bams-work dev <env> [component] [--layout pc|touch]');
|
|
49
|
+
error('示例: bams-work dev demo ui-component-demo');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const envFile = `.envs/.env.dev-${envAlias}`;
|
|
54
|
+
if (!fs.existsSync(path.join(projectRoot, envFile))) {
|
|
55
|
+
error(`环境文件不存在: ${envFile}(项目根: ${projectRoot})`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let sharedEnvBin;
|
|
60
|
+
let uiDevServerBin;
|
|
61
|
+
try {
|
|
62
|
+
sharedEnvBin = require.resolve('@bams-app/shared-env/bin/shared-env.js');
|
|
63
|
+
uiDevServerBin = require.resolve('@bams-app/ui-dev-server/bin/ui-dev-server.js');
|
|
64
|
+
} catch (err) {
|
|
65
|
+
error('未安装 work 依赖包,请先在 work-cli 目录执行 npm install');
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
info(`环境: ${envAlias} 组件: ${component || 'ui-component-demo'} 布局: ${layout}`);
|
|
70
|
+
info(`工作目录: ${projectRoot}`);
|
|
71
|
+
|
|
72
|
+
const uiArgs = [uiDevServerBin];
|
|
73
|
+
if (component) {
|
|
74
|
+
uiArgs.push('-c', component);
|
|
75
|
+
}
|
|
76
|
+
uiArgs.push('-l', layout);
|
|
77
|
+
uiArgs.push(...serviceArgs);
|
|
78
|
+
|
|
79
|
+
const env = { ...process.env, WORK_PROJECT_ROOT: projectRoot };
|
|
80
|
+
const code = await run(
|
|
81
|
+
process.execPath,
|
|
82
|
+
[sharedEnvBin, '--project', '.', '--env', envFile, '--', ...uiArgs],
|
|
83
|
+
{ cwd: projectRoot, env }
|
|
84
|
+
);
|
|
85
|
+
process.exit(code);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = main;
|
|
89
|
+
|
|
90
|
+
if (require.main === module) {
|
|
91
|
+
main(process.argv.slice(2)).catch((err) => {
|
|
92
|
+
error(err.message || err);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
});
|
|
95
|
+
}
|
package/commands/env.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const { spawn } = require('child_process');
|
|
3
|
+
const { error, findProjectRoot } = require('../lib/utils');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 委托 @bams-app/create-env 创建开发环境(.envs/.env.dev-<alias>)
|
|
7
|
+
* 环境文件始终写入用户项目根(.bams-work 标记目录所在处)
|
|
8
|
+
*/
|
|
9
|
+
function main(args) {
|
|
10
|
+
const binPath = require.resolve('@bams-app/create-env/bin/create-env.js');
|
|
11
|
+
const projectRoot = findProjectRoot(process.cwd()) || process.cwd();
|
|
12
|
+
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
const child = spawn(
|
|
15
|
+
process.execPath,
|
|
16
|
+
[binPath, ...args],
|
|
17
|
+
{
|
|
18
|
+
cwd: projectRoot,
|
|
19
|
+
stdio: 'inherit'
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
child.on('error', (err) => {
|
|
23
|
+
error(`启动 create-env 失败: ${err.message}`);
|
|
24
|
+
resolve(1);
|
|
25
|
+
});
|
|
26
|
+
child.on('exit', (code) => {
|
|
27
|
+
resolve(code ?? 1);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = main;
|
|
33
|
+
|
|
34
|
+
if (require.main === module) {
|
|
35
|
+
main(process.argv.slice(2)).catch((err) => {
|
|
36
|
+
error(err.message || err);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const { spawn } = require('child_process');
|
|
3
|
+
const { error } = require('../lib/utils');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 委托 @bams-app/create-pages-entry 扫描 page-* 与 ui-* 目录生成 webpack 动态入口
|
|
7
|
+
*/
|
|
8
|
+
function main(args) {
|
|
9
|
+
const binPath = require.resolve('@bams-app/create-pages-entry/bin/create-pages-entry.js');
|
|
10
|
+
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
const child = spawn(
|
|
13
|
+
process.execPath,
|
|
14
|
+
[binPath, ...args],
|
|
15
|
+
{
|
|
16
|
+
cwd: process.cwd(),
|
|
17
|
+
stdio: 'inherit'
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
child.on('error', (err) => {
|
|
21
|
+
error(`启动 create-pages-entry 失败: ${err.message}`);
|
|
22
|
+
resolve(1);
|
|
23
|
+
});
|
|
24
|
+
child.on('exit', (code) => {
|
|
25
|
+
resolve(code ?? 1);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = main;
|
|
31
|
+
|
|
32
|
+
if (require.main === module) {
|
|
33
|
+
main(process.argv.slice(2)).catch((err) => {
|
|
34
|
+
error(err.message || err);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const { spawn } = require('child_process');
|
|
3
|
+
const { error } = require('../lib/utils');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 委托 @bams-app/create-pages-from-components 批量将 ui-* 组件转为 page-* 页面
|
|
7
|
+
*/
|
|
8
|
+
function main(args) {
|
|
9
|
+
const binPath = require.resolve('@bams-app/create-pages-from-components/bin/create-pages-from-components.js');
|
|
10
|
+
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
const child = spawn(
|
|
13
|
+
process.execPath,
|
|
14
|
+
[binPath, ...args],
|
|
15
|
+
{
|
|
16
|
+
cwd: process.cwd(),
|
|
17
|
+
stdio: 'inherit'
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
child.on('error', (err) => {
|
|
21
|
+
error(`启动 create-pages-from-components 失败: ${err.message}`);
|
|
22
|
+
resolve(1);
|
|
23
|
+
});
|
|
24
|
+
child.on('exit', (code) => {
|
|
25
|
+
resolve(code ?? 1);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = main;
|
|
31
|
+
|
|
32
|
+
if (require.main === module) {
|
|
33
|
+
main(process.argv.slice(2)).catch((err) => {
|
|
34
|
+
error(err.message || err);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
});
|
|
37
|
+
}
|
package/commands/umd.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { findProjectRoot, toCamelCase, exists, isDirectory, readJson, error, info, run } = require('../lib/utils');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 用法: bams-work umd [component] [--out <dir>] [--name <name>] [--entry <file>]
|
|
7
|
+
* component 缺省时自动收集 workspaces 下所有组件
|
|
8
|
+
* 委托 @bams-app/ui-dev-server/bin/build-umd.js 构建组件 UMD 产物
|
|
9
|
+
*/
|
|
10
|
+
function parseArgs(args) {
|
|
11
|
+
let component = '';
|
|
12
|
+
let outDir = '';
|
|
13
|
+
let outputName = '';
|
|
14
|
+
let entryFile = '';
|
|
15
|
+
let serviceArgs = [];
|
|
16
|
+
let i = 0;
|
|
17
|
+
|
|
18
|
+
while (i < args.length) {
|
|
19
|
+
const arg = args[i];
|
|
20
|
+
if (arg === '--') {
|
|
21
|
+
serviceArgs = args.slice(i + 1);
|
|
22
|
+
break;
|
|
23
|
+
} else if (arg === '--out' || arg === '-o') {
|
|
24
|
+
outDir = args[i + 1];
|
|
25
|
+
i += 2;
|
|
26
|
+
} else if (arg === '--name' || arg === '-n') {
|
|
27
|
+
outputName = args[i + 1];
|
|
28
|
+
i += 2;
|
|
29
|
+
} else if (arg === '--entry' || arg === '-e') {
|
|
30
|
+
entryFile = args[i + 1];
|
|
31
|
+
i += 2;
|
|
32
|
+
} else if (arg.startsWith('--')) {
|
|
33
|
+
serviceArgs = args.slice(i);
|
|
34
|
+
break;
|
|
35
|
+
} else if (!component) {
|
|
36
|
+
component = arg;
|
|
37
|
+
} else {
|
|
38
|
+
serviceArgs = args.slice(i);
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
i++;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return { component, outDir, outputName, entryFile, serviceArgs };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 收集 workspaces 下所有组件目录(scope 目录模式,兼容旧 apps/ 结构)
|
|
49
|
+
* @returns {Array<{name: string, relPath: string}>}
|
|
50
|
+
*/
|
|
51
|
+
function collectComponents(projectRoot) {
|
|
52
|
+
const packageJson = readJson(path.join(projectRoot, 'package.json'));
|
|
53
|
+
const workspaces = packageJson.workspaces || [];
|
|
54
|
+
const components = [];
|
|
55
|
+
const seen = new Set();
|
|
56
|
+
|
|
57
|
+
const scanDir = (dir) => {
|
|
58
|
+
if (!isDirectory(dir)) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
62
|
+
if (!entry.isDirectory() || !/^(ui-|page-)/.test(entry.name)) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const componentDir = path.join(dir, entry.name);
|
|
66
|
+
if (
|
|
67
|
+
fs.existsSync(path.join(componentDir, 'index.js')) ||
|
|
68
|
+
fs.existsSync(path.join(componentDir, 'index.vue')) ||
|
|
69
|
+
fs.existsSync(path.join(componentDir, 'src', 'component.vue'))
|
|
70
|
+
) {
|
|
71
|
+
if (!seen.has(entry.name)) {
|
|
72
|
+
seen.add(entry.name);
|
|
73
|
+
components.push({ name: entry.name, relPath: path.relative(projectRoot, componentDir) });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
for (const pattern of workspaces) {
|
|
80
|
+
if (pattern.startsWith('!')) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const match = pattern.match(/^([^/*]+)(?:\/\*)?$/);
|
|
84
|
+
if (match) {
|
|
85
|
+
scanDir(path.join(projectRoot, match[1]));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 兼容旧 apps/ 结构
|
|
90
|
+
scanDir(path.join(projectRoot, 'apps'));
|
|
91
|
+
|
|
92
|
+
return components;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 生成 build-components-entry.js(import 路径相对项目根)
|
|
97
|
+
*/
|
|
98
|
+
function generateEntryFile({ projectRoot, components }) {
|
|
99
|
+
const entries = components.map(({ name, relPath }) => {
|
|
100
|
+
const camelCaseName = toCamelCase(name);
|
|
101
|
+
const importPath = `./${relPath.split(path.sep).join('/')}`;
|
|
102
|
+
return `export const ${camelCaseName} = () => import(/* webpackChunkName: "${name}" */ "${importPath}");`;
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const entryFile = path.join(projectRoot, 'build-components-entry.js');
|
|
106
|
+
fs.writeFileSync(entryFile, entries.join('\n') + '\n', 'utf8');
|
|
107
|
+
return entryFile;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function main(args) {
|
|
111
|
+
const projectRoot = findProjectRoot(process.cwd());
|
|
112
|
+
if (!projectRoot) {
|
|
113
|
+
error('未找到 BAMS-Work 项目(缺少 .bams-work 目录),请先执行 bams-work create <name>');
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const { component, outDir, outputName, entryFile, serviceArgs } = parseArgs(args);
|
|
118
|
+
|
|
119
|
+
let components = [];
|
|
120
|
+
if (component) {
|
|
121
|
+
const collected = collectComponents(projectRoot).find((c) => c.name === component);
|
|
122
|
+
components = collected ? [collected] : [{ name: component, relPath: component }];
|
|
123
|
+
} else {
|
|
124
|
+
components = collectComponents(projectRoot);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (components.length === 0) {
|
|
128
|
+
error('未找到任何组件(workspaces 下需存在 ui-* / page-* 目录),请先执行 bams-work add --dir <scope> --name ui-xxx');
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const finalEntryFile = entryFile
|
|
133
|
+
? (path.isAbsolute(entryFile) ? entryFile : path.resolve(process.cwd(), entryFile))
|
|
134
|
+
: generateEntryFile({ projectRoot, components });
|
|
135
|
+
|
|
136
|
+
if (!exists(finalEntryFile)) {
|
|
137
|
+
error(`入口文件不存在: ${finalEntryFile}`);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let buildUmdBin;
|
|
142
|
+
try {
|
|
143
|
+
buildUmdBin = require.resolve('@bams-app/ui-dev-server/bin/build-umd.js');
|
|
144
|
+
} catch (err) {
|
|
145
|
+
error('未安装 work 依赖包,请先在 work-cli 目录执行 npm install');
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
info(`构建 UMD 产物,组件: ${components.map((c) => c.name).join(', ')}`);
|
|
150
|
+
info(`工作目录: ${projectRoot}`);
|
|
151
|
+
|
|
152
|
+
const umdArgs = [buildUmdBin, '--entry', finalEntryFile];
|
|
153
|
+
if (outDir) {
|
|
154
|
+
umdArgs.push('--output-dir', path.isAbsolute(outDir) ? outDir : path.resolve(projectRoot, outDir));
|
|
155
|
+
}
|
|
156
|
+
if (outputName) {
|
|
157
|
+
umdArgs.push('--name', outputName);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const env = { ...process.env, WORK_PROJECT_ROOT: projectRoot };
|
|
161
|
+
const code = await run(process.execPath, umdArgs.concat(serviceArgs), { cwd: projectRoot, env });
|
|
162
|
+
process.exit(code);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
module.exports = main;
|
|
166
|
+
|
|
167
|
+
if (require.main === module) {
|
|
168
|
+
main(process.argv.slice(2)).catch((err) => {
|
|
169
|
+
error(err.message || err);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const { findProjectRoot, readJson, writeJson, run, error, warn, success, info, log } = require('../lib/utils');
|
|
5
|
+
|
|
6
|
+
const UI_DEV_SERVER = '@bams-app/ui-dev-server';
|
|
7
|
+
|
|
8
|
+
// 解析版本范围中声明的基础版本(如 ^0.1.0 -> 0.1.0)
|
|
9
|
+
function parseRangeVersion(range) {
|
|
10
|
+
if (!range) return null;
|
|
11
|
+
const match = String(range)
|
|
12
|
+
.replace(/^[~^]/, '')
|
|
13
|
+
.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
14
|
+
return match ? `${match[1]}.${match[2]}.${match[3]}` : null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// 简单三段的版本比较:a > b 返回 1,相等 0,小于 -1
|
|
18
|
+
function compareVersions(a, b) {
|
|
19
|
+
const pa = a.split('.').map(Number);
|
|
20
|
+
const pb = b.split('.').map(Number);
|
|
21
|
+
for (let i = 0; i < 3; i++) {
|
|
22
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return 1;
|
|
23
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return -1;
|
|
24
|
+
}
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 查询 registry 上的最新版本(继承项目 .npmrc 的 @bams-app:registry 配置)
|
|
29
|
+
function fetchLatestVersion(projectRoot) {
|
|
30
|
+
try {
|
|
31
|
+
const output = execSync(`npm view ${UI_DEV_SERVER} version --no-audit --no-fund`, {
|
|
32
|
+
cwd: projectRoot,
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
stdio: ['ignore', 'pipe', 'ignore']
|
|
35
|
+
});
|
|
36
|
+
return String(output).trim() || null;
|
|
37
|
+
} catch (e) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function main(args) {
|
|
43
|
+
const skipInstall = args.includes('--skip-install');
|
|
44
|
+
const projectRoot = findProjectRoot();
|
|
45
|
+
if (!projectRoot) {
|
|
46
|
+
error('未找到 BAMS-Work 项目(缺少 .bams-work 目录),请在用户项目根目录执行');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
51
|
+
const pkg = readJson(pkgPath);
|
|
52
|
+
const declared = (pkg.dependencies || {})[UI_DEV_SERVER];
|
|
53
|
+
|
|
54
|
+
if (!declared) {
|
|
55
|
+
warn(`未在 package.json 中找到 ${UI_DEV_SERVER} 依赖`);
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 本地验证模式:file: 引用,升级 = 清除缓存 + 重装以同步 work 最新源码
|
|
60
|
+
if (declared.startsWith('file:')) {
|
|
61
|
+
if (skipInstall) {
|
|
62
|
+
warn('本地验证模式需重装才能同步源码,忽略 --skip-install');
|
|
63
|
+
}
|
|
64
|
+
info(`当前为本地验证模式(${declared})`);
|
|
65
|
+
// npm 对已安装的 file: 依赖会直接跳过(即使源码已变化),
|
|
66
|
+
// 需先清除 node_modules 中的安装缓存,再重装强制重新复制
|
|
67
|
+
const fileDeps = Object.entries(pkg.dependencies || {})
|
|
68
|
+
.filter(([, v]) => typeof v === 'string' && v.startsWith('file:'))
|
|
69
|
+
.map(([name]) => name);
|
|
70
|
+
let removed = 0;
|
|
71
|
+
for (const name of fileDeps) {
|
|
72
|
+
const target = path.join(projectRoot, 'node_modules', name);
|
|
73
|
+
try {
|
|
74
|
+
if (fs.lstatSync(target)) {
|
|
75
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
76
|
+
removed++;
|
|
77
|
+
}
|
|
78
|
+
} catch (e) {
|
|
79
|
+
// 目标不存在,跳过
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
info(`已清除 ${removed} 个 file: 依赖缓存,将重新同步 work 源码`);
|
|
83
|
+
} else {
|
|
84
|
+
// 发布后模式:查询 registry 最新版本并升级声明
|
|
85
|
+
const current = parseRangeVersion(declared);
|
|
86
|
+
const latest = fetchLatestVersion(projectRoot);
|
|
87
|
+
if (!latest) {
|
|
88
|
+
warn(`查询 ${UI_DEV_SERVER} 最新版本失败(请检查项目 .npmrc 的 @bams-app:registry 配置与网络)`);
|
|
89
|
+
} else if (current && compareVersions(latest, current) > 0) {
|
|
90
|
+
info(`发现新版本: ${current} -> ${latest}`);
|
|
91
|
+
pkg.dependencies[UI_DEV_SERVER] = `^${latest}`;
|
|
92
|
+
writeJson(pkgPath, pkg);
|
|
93
|
+
success(`已更新 ${UI_DEV_SERVER} 为 ^${latest}`);
|
|
94
|
+
} else {
|
|
95
|
+
info(`${UI_DEV_SERVER} 已是最新版本(${latest})`);
|
|
96
|
+
}
|
|
97
|
+
if (skipInstall) {
|
|
98
|
+
info('已跳过依赖安装(--skip-install),请手动执行 npm install');
|
|
99
|
+
process.exit(0);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
log('正在重新安装依赖(能力包为 * 范围,将自动对齐最新版)...');
|
|
104
|
+
const code = await run('npm', ['install'], { cwd: projectRoot });
|
|
105
|
+
if (code !== 0) {
|
|
106
|
+
error('依赖安装失败');
|
|
107
|
+
process.exit(code);
|
|
108
|
+
}
|
|
109
|
+
success('升级完成');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = main;
|
|
113
|
+
|
|
114
|
+
if (require.main === module) {
|
|
115
|
+
main(process.argv.slice(2)).catch((err) => {
|
|
116
|
+
error(err.message || err);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
});
|
|
119
|
+
}
|