@bams-app/ui-dev-server 0.0.9 → 0.1.2

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/bin/build-app.js CHANGED
@@ -1,17 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const { spawn } = require('child_process');
4
- const { getRootDir, getProjectPackageJson, parseCliArgs, resolveComponentInput, colorize } = require('../utils');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { getRootDir, getProjectPackageJson, parseCliArgs, resolveComponentInput, resolveEnvFile, loadProjectEnv, colorize } = require('../utils');
5
7
 
6
8
  const rootDir = getRootDir();
7
9
  const packageJson = getProjectPackageJson(rootDir);
8
10
 
9
11
  async function main() {
10
- const { componentName: rawComponentName, vueCliArgs } = parseCliArgs(process.argv);
12
+ const { componentName: rawComponentName, envFile, vueCliArgs } = parseCliArgs(process.argv);
11
13
  const componentName = await resolveComponentInput(rawComponentName, rootDir, packageJson);
12
14
 
13
- // 设置环境变量
14
- const env = { ...process.env };
15
+ // 打包默认应用 .envs/.env.production(可用 --env <标识> 覆盖);项目缺少该文件时沿用当前进程环境
16
+ const envAlias = envFile || 'production';
17
+ const envFilePath = resolveEnvFile(envAlias, rootDir);
18
+ let env;
19
+ if (fs.existsSync(envFilePath)) {
20
+ env = loadProjectEnv(envAlias, rootDir);
21
+ } else if (envFile) {
22
+ console.error(colorize(`[build-app] 环境文件不存在: ${envFilePath}`, 'red'));
23
+ process.exit(1);
24
+ } else {
25
+ console.warn(colorize(`[build-app] 未找到 ${path.relative(rootDir, envFilePath)},将使用当前进程环境`, 'yellow'));
26
+ env = { ...process.env };
27
+ }
15
28
  env.COMPONENT = componentName;
16
29
  env.WORK_PROJECT_ROOT = rootDir;
17
30
  env.NODE_ENV = 'production';
@@ -24,7 +37,7 @@ async function main() {
24
37
 
25
38
  const child = spawn(process.execPath, [vueCliService, 'build', ...vueCliArgs], {
26
39
  env,
27
- cwd: require('path').resolve(__dirname, '../'),
40
+ cwd: path.resolve(__dirname, '../'),
28
41
  stdio: 'inherit'
29
42
  });
30
43
 
package/bin/build-umd.js CHANGED
@@ -3,13 +3,14 @@
3
3
  const { spawn } = require('child_process');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
- const { getRootDir } = require('../utils');
6
+ const { getRootDir, resolveEnvFile, loadProjectEnv, colorize } = require('../utils');
7
7
 
8
8
  // 解析命令行参数
9
9
  const args = process.argv.slice(2);
10
10
  let entryFile = '';
11
11
  let outputDir = '';
12
12
  let outputName = 'bams-components';
13
+ let envAlias = '';
13
14
 
14
15
  // 处理参数
15
16
  for (let i = 0; i < args.length; i++) {
@@ -34,6 +35,11 @@ for (let i = 0; i < args.length; i++) {
34
35
  outputName = args[i].split('=')[1];
35
36
  } else if (args[i].startsWith('-n=')) {
36
37
  outputName = args[i].split('=')[1];
38
+ } else if (args[i] === '--env') {
39
+ envAlias = args[i + 1];
40
+ i++;
41
+ } else if (args[i].startsWith('--env=')) {
42
+ envAlias = args[i].split('=')[1];
37
43
  }
38
44
  }
39
45
 
@@ -58,8 +64,20 @@ if (outputDir && !path.isAbsolute(outputDir)) {
58
64
  outputDir = path.resolve(process.cwd(), outputDir);
59
65
  }
60
66
 
61
- // 设置环境变量
62
- const env = { ...process.env };
67
+ // 打包默认应用 .envs/.env.production(可通过 --env 指定环境标识或 env 文件路径);项目缺少该文件时沿用当前进程环境
68
+ const resolvedEnvAlias = envAlias || 'production';
69
+ const envFilePath = resolveEnvFile(resolvedEnvAlias, rootDir);
70
+ let env;
71
+ if (fs.existsSync(envFilePath)) {
72
+ env = loadProjectEnv(resolvedEnvAlias, rootDir);
73
+ console.log(colorize(`[build-umd] Env file: ${path.relative(rootDir, envFilePath)}`, 'blue'));
74
+ } else if (envAlias) {
75
+ console.error(`❌ 环境文件不存在:${envFilePath}`);
76
+ process.exit(1);
77
+ } else {
78
+ console.warn(`⚠️ 未找到 ${path.relative(rootDir, envFilePath)},将使用当前进程环境`);
79
+ env = { ...process.env };
80
+ }
63
81
  env.WORK_PROJECT_ROOT = rootDir;
64
82
  env.ENTRY_FILE = entryFile;
65
83
  if (outputDir) env.OUTPUT_DIR = outputDir;
@@ -2,17 +2,48 @@
2
2
 
3
3
  const { spawn } = require('child_process');
4
4
  const path = require('path');
5
- const { getRootDir, getProjectPackageJson, parseCliArgs, resolveComponentInput, colorize } = require('../utils');
5
+ const { getRootDir, getProjectPackageJson, parseCliArgs, resolveComponentInput, resolveEnvFile, listEnvAliases, loadProjectEnv, colorize } = require('../utils');
6
6
 
7
7
  const rootDir = getRootDir();
8
8
  const packageJson = getProjectPackageJson(rootDir);
9
9
 
10
+ /**
11
+ * 校验环境标识是否已指定
12
+ * - 已显式传入(位置参数 / --env),或环境已由 shared-env 注入(BAMS_ENV_FILE)时放行,
13
+ * 保证仓库内 dev:ui 链路行为不变
14
+ * - 项目内存在 .envs/.env.dev-* 却未指定时报错并列出可用环境,避免"静默无环境"
15
+ * - 项目内没有任何环境配置时仅提示,不阻塞启动
16
+ * @param {string} envInput - 环境标识或 env 文件路径
17
+ */
18
+ function assertEnvSpecified(envInput) {
19
+ if (envInput || process.env.BAMS_ENV_FILE) {
20
+ return;
21
+ }
22
+
23
+ const aliases = listEnvAliases(rootDir);
24
+ if (aliases.length === 0) {
25
+ console.warn(colorize('[ui-dev-server] 未找到环境配置(.envs/.env.dev-*),将以无环境变量方式启动', 'yellow'));
26
+ console.warn(colorize('[ui-dev-server] 可先执行 work env <标识> 创建环境配置', 'yellow'));
27
+ return;
28
+ }
29
+
30
+ console.error(colorize('[ui-dev-server] 未指定环境标识,启动已终止', 'red'));
31
+ console.error(colorize(`[ui-dev-server] 可用环境: ${aliases.join(', ')}`, 'yellow'));
32
+ console.error(colorize('[ui-dev-server] 用法: npm run dev:ui <环境标识> [组件名]', 'yellow'));
33
+ process.exit(1);
34
+ }
35
+
10
36
  async function main() {
11
- const { componentName: rawComponentName, layout, vueCliArgs } = parseCliArgs(process.argv);
37
+ const { componentName: rawComponentName, layout, envFile, vueCliArgs } = parseCliArgs(process.argv);
38
+ assertEnvSpecified(envFile);
12
39
  const componentName = await resolveComponentInput(rawComponentName, rootDir, packageJson);
13
40
 
14
- // 设置环境变量
15
- const env = { ...process.env };
41
+ // 环境标识对应的文件键优先于进程已有变量
42
+ const envFilePath = resolveEnvFile(envFile, rootDir);
43
+ const env = loadProjectEnv(envFile, rootDir);
44
+ if (envFilePath) {
45
+ console.log(colorize(`[ui-dev-server] Env file: ${path.relative(rootDir, envFilePath)}`, 'blue'));
46
+ }
16
47
  env.COMPONENT = componentName;
17
48
  env.LAYOUT = layout;
18
49
  env.WORK_PROJECT_ROOT = rootDir;
package/package.json CHANGED
@@ -1,17 +1,12 @@
1
1
  {
2
2
  "name": "@bams-app/ui-dev-server",
3
- "version": "0.0.9",
3
+ "version": "0.1.2",
4
4
  "scripts": {
5
5
  "serve": "node ./bin/ui-dev-server.js",
6
6
  "build": "vue-cli-service build",
7
7
  "build:umd": "node ./bin/build-umd.js",
8
8
  "lint": "vue-cli-service lint"
9
9
  },
10
- "peerDependencies": {
11
- "ant-design-vue": "^4.0.0",
12
- "vue": "^3.5.24",
13
- "vue-router": "^4.0.0"
14
- },
15
10
  "dependencies": {
16
11
  "@babel/core": "^7.12.16",
17
12
  "@babel/eslint-parser": "^7.12.16",
@@ -32,6 +27,7 @@
32
27
  "@bams-app/permission": "*",
33
28
  "@bams-app/router": "*",
34
29
  "@bams-app/service": "*",
30
+ "@bams-app/shared-env": "*",
35
31
  "@bams-app/store": "*",
36
32
  "@bams-app/theme": "*",
37
33
  "@bams-app/ui-business-table": "*",
@@ -46,15 +42,30 @@
46
42
  "@bams-app/utils": "*",
47
43
  "@bams-app/version-check": "*",
48
44
  "@bams-app/websocket": "*",
45
+ "@ant-design/icons-vue": "^7.0.1",
49
46
  "@vue/cli-plugin-babel": "~5.0.0",
50
47
  "@vue/cli-service": "~5.0.0",
48
+ "ant-design-vue": "4.2.6",
49
+ "axios": "^1.13.2",
51
50
  "core-js": "^3.8.3",
51
+ "dayjs": "^1.11.20",
52
+ "devextreme": "25.2.3",
53
+ "devextreme-exceljs-fork": "^4.4.8",
54
+ "devextreme-vue": "25.2.3",
55
+ "file-saver": "^2.0.5",
56
+ "flex.css": "^1.1.7",
52
57
  "inquirer": "9",
58
+ "jsencrypt": "^3.5.4",
53
59
  "less": "^4.5.1",
54
60
  "less-loader": "^7.3.0",
61
+ "marked": "^17.0.4",
62
+ "pinia": "^3.0.4",
63
+ "pinia-plugin-persistedstate": "^4.7.1",
55
64
  "raw-loader": "^4.0.2",
56
65
  "sass": "^1.97.3",
57
- "sass-loader": "^10.1.1"
66
+ "sass-loader": "^10.1.1",
67
+ "vue": "^3.5.24",
68
+ "vue-router": "4"
58
69
  },
59
70
  "files": [
60
71
  "bin/",
package/utils.js CHANGED
@@ -244,6 +244,40 @@ function resolveComponent(componentName, rootDir, packageJson) {
244
244
  return getComponentScope(componentName, rootDir, packageJson);
245
245
  }
246
246
 
247
+ /**
248
+ * 解析 dev/build 时 @target-component 别名实际指向的组件目录
249
+ * - 完整包名(@scope/name):在候选中按 packageName 精确匹配,避免同名组件始终落到第一个
250
+ * - 路径:直接使用该路径
251
+ * - 短名:沿用第一个候选;无候选时回退 resolveComponent 结果(如已发布包从 node_modules 解析)
252
+ * @param {string} componentName - 组件名、完整包名或路径
253
+ * @param {string} rootDir - 项目根目录
254
+ * @param {Object} packageJson - package.json 对象
255
+ * @returns {string} 组件目录绝对路径或可被 webpack 解析的包名
256
+ */
257
+ function resolveTargetComponentPath(componentName, rootDir, packageJson) {
258
+ const resolvedComponent = resolveComponent(componentName, rootDir, packageJson);
259
+
260
+ // 路径形式已指向明确目录,无需再按名字查找候选
261
+ if (isFileSystemPath(componentName)) {
262
+ return resolvedComponent;
263
+ }
264
+
265
+ const candidates = findComponentCandidates(getComponentEntryName(componentName), rootDir, packageJson);
266
+ if (candidates.length === 0) {
267
+ return resolvedComponent;
268
+ }
269
+
270
+ // 完整包名:按 packageName 精确匹配同名组件所属 scope
271
+ if (componentName.startsWith('@')) {
272
+ const matched = candidates.find((candidate) => candidate.packageName === componentName);
273
+ if (matched) {
274
+ return matched.componentPath;
275
+ }
276
+ }
277
+
278
+ return candidates[0].componentPath;
279
+ }
280
+
247
281
  /**
248
282
  * 从组件名或路径中提取组件入口名(用于前端渲染)
249
283
  * @param {string} componentName - 组件名或路径
@@ -291,16 +325,21 @@ function colorize(text, color) {
291
325
 
292
326
  /**
293
327
  * 解析命令行参数
328
+ * 位置参数:第 1 个为环境标识(等价 --env),第 2 个为组件名(等价 --component)
329
+ * 即 `npm run dev:ui demo` → 环境 demo,`npm run dev:ui demo ui-order-list` → 环境 demo + 组件 ui-order-list
294
330
  * @param {string[]} argv - process.argv
295
331
  * @returns {Object} 解析结果
296
332
  * @returns {string} returns.componentName - 组件名
297
333
  * @returns {string} returns.layout - 布局类型
334
+ * @returns {string} returns.envFile - 环境标识或 env 文件路径(第 1 个位置参数或 --env,可为空)
298
335
  * @returns {string[]} returns.vueCliArgs - 传递给 Vue CLI 的参数
299
336
  */
300
337
  function parseCliArgs(argv) {
301
338
  const args = argv.slice(2);
302
339
  let componentName = 'ui-component-demo';
303
340
  let layout = 'pc';
341
+ let envFile = '';
342
+ let envFromFlag = false;
304
343
  const vueCliArgs = [];
305
344
 
306
345
  let i = 0;
@@ -327,7 +366,24 @@ function parseCliArgs(argv) {
327
366
  } else if (arg.startsWith('-l=')) {
328
367
  layout = arg.split('=')[1];
329
368
  i++;
369
+ // --env 或 -e 参数:环境标识(如 demo)或 env 文件路径
370
+ } else if (arg === '--env' || arg === '-e') {
371
+ envFile = args[i + 1];
372
+ envFromFlag = true;
373
+ i += 2;
374
+ } else if (arg.startsWith('--env=')) {
375
+ envFile = arg.split('=')[1];
376
+ envFromFlag = true;
377
+ i++;
378
+ } else if (arg.startsWith('-e=')) {
379
+ envFile = arg.split('=')[1];
380
+ envFromFlag = true;
381
+ i++;
330
382
  // Vue CLI 参数(以 -- 开头)
383
+ } else if (arg === '--') {
384
+ // 分隔符:其后参数原样透传给 vue-cli-service(npm run dev -- 会额外透传一个 --)
385
+ vueCliArgs.push(...args.slice(i + 1));
386
+ break;
331
387
  } else if (arg.startsWith('--')) {
332
388
  vueCliArgs.push(arg);
333
389
  // 处理带值的选项
@@ -337,9 +393,13 @@ function parseCliArgs(argv) {
337
393
  } else {
338
394
  i++;
339
395
  }
340
- // 其他参数:作为组件名
396
+ // 其他参数:位置参数(未显式传 --env 时,第 1 个 = 环境标识;其余 = 组件名)
341
397
  } else {
342
- componentName = arg;
398
+ if (!envFromFlag && envFile === '') {
399
+ envFile = arg;
400
+ } else {
401
+ componentName = arg;
402
+ }
343
403
  i++;
344
404
  }
345
405
  }
@@ -347,10 +407,81 @@ function parseCliArgs(argv) {
347
407
  return {
348
408
  componentName,
349
409
  layout,
410
+ envFile,
350
411
  vueCliArgs
351
412
  };
352
413
  }
353
414
 
415
+ /**
416
+ * 将环境标识解析为 env 文件绝对路径
417
+ * - 路径形式(含路径分隔符,或以 .env 开头):相对项目根解析
418
+ * - production:映射为 <rootDir>/.envs/.env.production(打包使用,开发不适用)
419
+ * - 其他(环境标识,如 demo):映射为 <rootDir>/.envs/.env.dev-<标识>
420
+ * @param {string} envInput - 环境标识或 env 文件路径
421
+ * @param {string} rootDir - 项目根目录
422
+ * @returns {string} env 文件绝对路径,未传时返回空字符串
423
+ */
424
+ function resolveEnvFile(envInput, rootDir) {
425
+ if (!envInput) {
426
+ return '';
427
+ }
428
+
429
+ const isPathLike = envInput.includes('/')
430
+ || envInput.includes('\\')
431
+ || path.basename(envInput).startsWith('.env');
432
+
433
+ if (isPathLike) {
434
+ return path.resolve(rootDir, envInput);
435
+ }
436
+
437
+ const fileName = envInput === 'production' ? '.env.production' : `.env.dev-${envInput}`;
438
+ return path.resolve(rootDir, '.envs', fileName);
439
+ }
440
+
441
+ /**
442
+ * 列出项目可用的开发环境标识(.envs/.env.dev-*)
443
+ * @param {string} rootDir - 项目根目录
444
+ * @returns {string[]} 环境标识列表
445
+ */
446
+ function listEnvAliases(rootDir) {
447
+ const envDir = path.resolve(rootDir, '.envs');
448
+ if (!fs.existsSync(envDir)) {
449
+ return [];
450
+ }
451
+
452
+ return fs.readdirSync(envDir)
453
+ .filter((name) => name.startsWith('.env.dev-') && !name.endsWith('.local'))
454
+ .map((name) => name.replace('.env.dev-', ''))
455
+ .sort();
456
+ }
457
+
458
+ /**
459
+ * 按环境标识加载环境文件(dev/build 共用)
460
+ * - 环境标识为空时返回当前进程环境(不做任何加载)
461
+ * @param {string} envInput - 环境标识或 env 文件路径
462
+ * @param {string} rootDir - 项目根目录
463
+ * @returns {Object} 环境变量对象
464
+ */
465
+ function loadProjectEnv(envInput, rootDir) {
466
+ if (!envInput) {
467
+ return { ...process.env };
468
+ }
469
+
470
+ const envFilePath = resolveEnvFile(envInput, rootDir);
471
+ if (!fs.existsSync(envFilePath)) {
472
+ throw new Error(`环境文件不存在: ${envFilePath}`);
473
+ }
474
+
475
+ let loadEnv;
476
+ try {
477
+ ({ loadEnv } = require('@bams-app/shared-env/lib/env-loader'));
478
+ } catch (err) {
479
+ throw new Error(`加载环境文件需要安装 @bams-app/shared-env(${err.message})`);
480
+ }
481
+
482
+ return loadEnv(rootDir, envFilePath);
483
+ }
484
+
354
485
  /**
355
486
  * 判断输入是否为文件系统路径
356
487
  * @param {string} input - 输入字符串
@@ -456,10 +587,14 @@ module.exports = {
456
587
  findComponentCandidates,
457
588
  getComponentScope,
458
589
  resolveComponent,
590
+ resolveTargetComponentPath,
459
591
  getComponentEntryName,
460
592
  toCamelComponentName,
461
593
  colorize,
462
594
  parseCliArgs,
595
+ resolveEnvFile,
596
+ listEnvAliases,
597
+ loadProjectEnv,
463
598
  isFileSystemPath,
464
599
  promptForCandidate,
465
600
  resolveComponentInput
@@ -1,7 +1,7 @@
1
1
  const { defineConfig } = require('@vue/cli-service')
2
2
  const path = require('path')
3
3
  const { getBaseConfig } = require('./vue.config.base')
4
- const { getRootDir, getProjectPackageJson, resolveComponent, findComponentCandidates, getComponentEntryName } = require('./utils')
4
+ const { getRootDir, getProjectPackageJson, resolveTargetComponentPath } = require('./utils')
5
5
 
6
6
  const rootDir = getRootDir();
7
7
  const packageJson = getProjectPackageJson(rootDir);
@@ -9,12 +9,9 @@ const baseConfig = getBaseConfig({ rootDir, packageJson });
9
9
 
10
10
  const componentName = process.env.COMPONENT || 'ui-component-demo';
11
11
 
12
- const resolvedComponent = resolveComponent(componentName, rootDir, packageJson);
13
- // COMPONENT 环境变量可能是 @scope/name 完整包名,查找候选时需用短名
14
- const candidates = findComponentCandidates(getComponentEntryName(componentName), rootDir, packageJson);
15
- // 命中候选时别名指向组件目录绝对路径(避免从包目录解析用户项目组件失败)
16
- const component = candidates.length > 0 ? candidates[0].componentPath : resolvedComponent;
17
- console.log('🎉 当前开发组件:', resolvedComponent);
12
+ // COMPONENT 可能是 @scope/name 完整包名,同名组件存在多个 scope 时按包名精确匹配
13
+ const component = resolveTargetComponentPath(componentName, rootDir, packageJson);
14
+ console.log('🎉 当前构建组件:', component);
18
15
 
19
16
  module.exports = defineConfig({
20
17
  transpileDependencies: baseConfig.transpileDependencies,
package/vue.config.dev.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const { defineConfig } = require('@vue/cli-service')
2
2
  const path = require('path')
3
3
  const { getBaseConfig } = require('./vue.config.base')
4
- const { getRootDir, getProjectPackageJson, resolveComponent, findComponentCandidates, getComponentEntryName, toCamelComponentName } = require('./utils')
4
+ const { getRootDir, getProjectPackageJson, resolveTargetComponentPath, toCamelComponentName } = require('./utils')
5
5
 
6
6
  const rootDir = getRootDir();
7
7
  const packageJson = getProjectPackageJson(rootDir);
@@ -11,12 +11,9 @@ const componentName = process.env.COMPONENT || 'ui-component-demo';
11
11
  const layout = process.env.LAYOUT || 'pc';
12
12
  const camelComponentName = toCamelComponentName(componentName);
13
13
 
14
- const resolvedComponent = resolveComponent(componentName, rootDir, packageJson);
15
- // COMPONENT 环境变量可能是 @scope/name 完整包名,查找候选时需用短名
16
- const candidates = findComponentCandidates(getComponentEntryName(componentName), rootDir, packageJson);
17
- // 命中候选时别名指向组件目录绝对路径(避免从包目录解析用户项目组件失败)
18
- const component = candidates.length > 0 ? candidates[0].componentPath : resolvedComponent;
19
- console.log('🎉 当前开发组件:', resolvedComponent);
14
+ // COMPONENT 可能是 @scope/name 完整包名,同名组件存在多个 scope 时按包名精确匹配
15
+ const component = resolveTargetComponentPath(componentName, rootDir, packageJson);
16
+ console.log('🎉 当前开发组件:', component);
20
17
 
21
18
  module.exports = defineConfig({
22
19
  transpileDependencies: baseConfig.transpileDependencies,