@sys-designer/create-vue 1.0.2 → 1.1.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sys-designer/create-vue",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Scaffold a sys-designer Vue3 + Vite + TS project from the command line.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,5 @@
1
+ # 强制脚本与源码使用 LF 行尾,避免 Windows / git autocrlf 把 .sh 转成 CRLF
2
+ # 导致 "syntax error near unexpected token '$'in\r''" 等错误。
3
+ * text=auto eol=lf
4
+ *.sh text eol=lf
5
+ *.bat text eol=crlf
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env bash
2
+ # 若文件含 CRLF(Windows 拷贝 / git autocrlf 导致),自动转 LF 后重跑。
3
+ # 本自修正必须放在第 2 行(shebang 之后、任何注释/命令之前),确保最先执行。
4
+ # 约束:
5
+ # 1) 必须用纯 && 链式,不能写 if/then/fi —— CRLF 会让 then 失效,自修正无法触发。
6
+ # 2) 不能用 grep 检测 CR —— Git Bash 的 grep 以文本模式读文件,会把 CRLF 的 CR 剥离,检测永远失败。
7
+ # 3) 不能用 sed 's/\r$//' —— Git Bash 的 GNU sed 正则不识别 \r。
8
+ # 故采用「字节数对比」:tr 去掉所有 CR 后字节数变少,说明原文件含 CRLF。
9
+ n1=$(wc -c < "$0"); tr -d '\r' < "$0" > "$0.tmp"; n2=$(wc -c < "$0.tmp"); [ "$n1" != "$n2" ] && mv "$0.tmp" "$0" && exec bash "$0" "$@"; rm -f "$0.tmp"
10
+
11
+ # build.sh —— 构建项目并将产物打包为压缩包,输出到 output/ 目录
12
+ #
13
+ # 用法:
14
+ # bash scripts/build.sh # 输出到 ./output/<name>-<version>.tar.gz
15
+ # bash scripts/build.sh myapp # 输出到 ./output/myapp.tar.gz
16
+ # bash scripts/build.sh /path/out # 输出目录改为 /path/out
17
+ # bash scripts/build.sh /path/out myapp # 输出目录 /path/out,名称 myapp
18
+ # bash scripts/build.sh --name myapp --out /path/out
19
+ #
20
+ # 说明:
21
+ # - 自动选择包管理器(按 lockfile 判定,优先 pnpm/yarn/npm);含 pnpm 的 link: 等协议时
22
+ # 必须用 pnpm,否则 npm 会报 EUNSUPPORTEDPROTOCOL。也可用 PKG_MANAGER 环境变量强制指定。
23
+ # - 执行 安装 + 构建,产物在 dist/。
24
+ # - 安装后会把 package.json 中所有 link: 依赖的 lib.config.ts 合并,
25
+ # 输出到 common/lib.config.ts(由 scripts/merge-lib-config.mjs 处理)。
26
+ # - 包名默认取 package.json 的 name-version;可用第二位置参数或 --name 指定。
27
+ # - 输出目录默认 ./output;可用第一位置参数(像路径)或 --out 指定。
28
+ # - 若环境存在 zip 命令,会额外生成一个同名 .zip 包。
29
+ # - 若脚本从 Windows 拷贝带 CRLF 行尾,本脚本会自动转为 LF 后重新执行。
30
+
31
+ set -euo pipefail
32
+
33
+ # 切到项目根目录(scripts/ 的上一级)
34
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
35
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
36
+ cd "$PROJECT_ROOT"
37
+
38
+ # 选择包管理器:优先按项目里的 lockfile 判定,避免 npm 不识别 pnpm 的 link: 等协议
39
+ # (会报 EUNSUPPORTEDPROTOCOL)。也可用环境变量 PKG_MANAGER=npm|pnpm|yarn 强制指定。
40
+ detect_pm() {
41
+ if [ -n "${PKG_MANAGER:-}" ]; then
42
+ PM="$PKG_MANAGER"
43
+ elif [ -f pnpm-lock.yaml ]; then
44
+ PM=pnpm
45
+ elif [ -f yarn.lock ]; then
46
+ PM=yarn
47
+ elif [ -f package-lock.json ]; then
48
+ PM=npm
49
+ elif command -v pnpm >/dev/null 2>&1; then
50
+ PM=pnpm
51
+ elif command -v yarn >/dev/null 2>&1; then
52
+ PM=yarn
53
+ else
54
+ PM=npm
55
+ fi
56
+ case "$PM" in
57
+ pnpm) PM_INSTALL="pnpm install"; PM_RUN="pnpm run" ;;
58
+ yarn) PM_INSTALL="yarn install"; PM_RUN="yarn" ;;
59
+ *) PM_INSTALL="npm install"; PM_RUN="npm run" ;;
60
+ esac
61
+ }
62
+ detect_pm
63
+
64
+ OUT_DIR="$PROJECT_ROOT/output"
65
+ ARCHIVE_NAME=""
66
+ POSITIONAL=()
67
+ while [ "$#" -gt 0 ]; do
68
+ case "$1" in
69
+ $'\r') shift ;; # 丢弃 CRLF 自修正重跑时可能带入的孤立 CR 参数
70
+ --name) ARCHIVE_NAME="${2:-}"; shift 2 ;;
71
+ --name=*) ARCHIVE_NAME="${1#*=}"; shift ;;
72
+ --out) OUT_DIR="${2:-}"; shift 2 ;;
73
+ --out=*) OUT_DIR="${1#*=}"; shift ;;
74
+ -*) echo "未知参数: $1" >&2; exit 1 ;;
75
+ *) POSITIONAL+=("$1"); shift ;;
76
+ esac
77
+ done
78
+
79
+ # 位置参数兜底: $1 = 输出目录(像路径) 或 名称, $2 = 名称
80
+ if [ -z "$ARCHIVE_NAME" ] && [ "${#POSITIONAL[@]}" -ge 1 ]; then
81
+ first="${POSITIONAL[0]}"
82
+ if [ "${#POSITIONAL[@]}" -ge 2 ]; then
83
+ OUT_DIR="$first"; ARCHIVE_NAME="${POSITIONAL[1]}"
84
+ else
85
+ case "$first" in
86
+ /*|./*|../*|*/|*/*) OUT_DIR="$first" ;;
87
+ *) ARCHIVE_NAME="$first" ;;
88
+ esac
89
+ fi
90
+ fi
91
+
92
+ echo "==> 工作目录: $PROJECT_ROOT"
93
+
94
+ echo "==> 安装依赖 ($PM_INSTALL)"
95
+ $PM_INSTALL
96
+
97
+ # 合并所有 link: 依赖(如 new-app)的 lib.config.ts -> common/lib.config.ts
98
+ echo "==> 合并 link 依赖的 lib.config.ts"
99
+ node "$(dirname "$0")/merge-lib-config.mjs"
100
+
101
+ echo "==> 构建生产包 ($PM_RUN build)"
102
+ $PM_RUN build
103
+
104
+ DIST_DIR="$PROJECT_ROOT/dist"
105
+ if [ ! -d "$DIST_DIR" ]; then
106
+ echo "错误: 构建产物目录 $DIST_DIR 不存在,构建可能失败。" >&2
107
+ exit 1
108
+ fi
109
+
110
+ # 读取包名与版本(构建依赖 node,必可用);未指定名称时作为默认名
111
+ PKG_NAME=$(node -p "require('./package.json').name" 2>/dev/null || echo app)
112
+ PKG_VERSION=$(node -p "require('./package.json').version" 2>/dev/null || echo 1.0.0)
113
+ if [ -z "$ARCHIVE_NAME" ]; then
114
+ ARCHIVE_NAME="$PKG_NAME-$PKG_VERSION"
115
+ else
116
+ # 去掉用户可能带入的扩展名,避免双重后缀
117
+ ARCHIVE_NAME="${ARCHIVE_NAME%.tar.gz}"
118
+ ARCHIVE_NAME="${ARCHIVE_NAME%.tgz}"
119
+ ARCHIVE_NAME="${ARCHIVE_NAME%.zip}"
120
+ fi
121
+
122
+ mkdir -p "$OUT_DIR"
123
+ TAR_ARCHIVE="$OUT_DIR/$ARCHIVE_NAME.tar.gz"
124
+
125
+ echo "==> 打包 dist/ -> $TAR_ARCHIVE"
126
+ tar -czf "$TAR_ARCHIVE" -C "$DIST_DIR" .
127
+
128
+ # 若环境有 zip,额外生成 zip 包
129
+ if command -v zip >/dev/null 2>&1; then
130
+ ZIP_ARCHIVE="$OUT_DIR/$ARCHIVE_NAME.zip"
131
+ echo "==> 打包 dist/ -> $ZIP_ARCHIVE"
132
+ (cd "$DIST_DIR" && zip -r -q "$ZIP_ARCHIVE" .)
133
+ else
134
+ echo "==> 未检测到 zip 命令,跳过 zip 包(已生成 tar.gz)"
135
+ fi
136
+
137
+ echo "==> 构建完成,压缩包输出在: $OUT_DIR"
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env bash
2
+ # 若文件含 CRLF(Windows 拷贝 / git autocrlf 导致),自动转 LF 后重跑。
3
+ # 本自修正必须放在第 2 行(shebang 之后、任何注释/命令之前),确保最先执行。
4
+ # 约束:
5
+ # 1) 必须用纯 && 链式,不能写 if/then/fi —— CRLF 会让 then 失效,自修正无法触发。
6
+ # 2) 不能用 grep 检测 CR —— Git Bash 的 grep 以文本模式读文件,会把 CRLF 的 CR 剥离,检测永远失败。
7
+ # 3) 不能用 sed 's/\r$//' —— Git Bash 的 GNU sed 正则不识别 \r。
8
+ # 故采用「字节数对比」:tr 去掉所有 CR 后字节数变少,说明原文件含 CRLF。
9
+ n1=$(wc -c < "$0"); tr -d '\r' < "$0" > "$0.tmp"; n2=$(wc -c < "$0.tmp"); [ "$n1" != "$n2" ] && mv "$0.tmp" "$0" && exec bash "$0" "$@"; rm -f "$0.tmp"
10
+
11
+ # deploy.sh —— 仅发布(解压)已由 build.sh 构建好的压缩包到 nginx html 目录
12
+ #
13
+ # 用法:
14
+ # bash scripts/deploy.sh # 仅发布 output/ 中已有的压缩包到自动检测的 nginx 目录
15
+ # bash scripts/deploy.sh /usr/share/nginx/html # 发布到指定 nginx 目录
16
+ # bash scripts/deploy.sh /usr/share/nginx/html myapp # 发布到 nginx 的 myapp 子目录
17
+ # bash scripts/deploy.sh myapp # 自动检测 nginx 目录, 发布到其 myapp 子目录
18
+ #
19
+ # 说明:
20
+ # - 本脚本【不负责构建】,只能使用 build.sh 已在 output/ 下生成的压缩包。
21
+ # 若找不到压缩包会直接报错,请先运行: bash scripts/build.sh
22
+ # - 发布时把压缩包【解压】到目标 nginx 静态目录(可直接 serve), 不拷贝 .tar.gz。
23
+ # - 未传 nginx 目录时尝试自动检测常见目录; 检测到才发布, 否则仅提示压缩包位置。
24
+ # - --clean 会先清空目标目录(或子目录)现有内容。
25
+ # - 若脚本从 Windows 拷贝带 CRLF 行尾, 本脚本会自动转为 LF 后重新执行。
26
+
27
+ set -euo pipefail
28
+
29
+ # 切到项目根目录(scripts/ 的上一级)
30
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
31
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
32
+ cd "$PROJECT_ROOT"
33
+
34
+ DEPLOY_DIR=""
35
+ SUBDIR=""
36
+ ARCHIVE_NAME=""
37
+ CLEAN=0
38
+ POSITIONAL=()
39
+ while [ "$#" -gt 0 ]; do
40
+ case "$1" in
41
+ $'\r') shift ;; # 丢弃 CRLF 自修正重跑时可能带入的孤立 CR 参数
42
+ --name) ARCHIVE_NAME="${2:-}"; shift 2 ;;
43
+ --name=*) ARCHIVE_NAME="${1#*=}"; shift ;;
44
+ --clean) CLEAN=1; shift ;;
45
+ -*) echo "未知参数: $1" >&2; exit 1 ;;
46
+ *) POSITIONAL+=("$1"); shift ;;
47
+ esac
48
+ done
49
+
50
+ # 位置参数: $1 = nginx 目录(可选, 省略则自动检测), $2 = 子目录名(可选)
51
+ if [ "${#POSITIONAL[@]}" -ge 1 ]; then
52
+ first="${POSITIONAL[0]}"
53
+ if [ "${#POSITIONAL[@]}" -ge 2 ]; then
54
+ DEPLOY_DIR="$first"; SUBDIR="${POSITIONAL[1]}"
55
+ else
56
+ case "$first" in
57
+ # 含路径分隔符(/ 或 \)的视为目标目录;否则视为 nginx 下的子目录名
58
+ /*|./*|../*|*/|*/*|*\\*) DEPLOY_DIR="$first" ;;
59
+ *) SUBDIR="$first" ;;
60
+ esac
61
+ fi
62
+ fi
63
+
64
+ echo "==> 工作目录: $PROJECT_ROOT"
65
+
66
+ # 若未显式指定目录,尝试自动检测常见的 nginx html 目录
67
+ if [ -z "$DEPLOY_DIR" ]; then
68
+ for d in /usr/share/nginx/html /var/www/html /usr/local/nginx/html /opt/nginx/html /data/nginx/html; do
69
+ if [ -d "$d" ]; then
70
+ DEPLOY_DIR="$d"
71
+ echo "==> 未指定目录,自动检测到 nginx html 目录: $DEPLOY_DIR"
72
+ break
73
+ fi
74
+ done
75
+ if [ -z "$DEPLOY_DIR" ]; then
76
+ echo "==> 未指定目录且未检测到 nginx html 目录,将只保留压缩包不发布"
77
+ fi
78
+ fi
79
+
80
+ # 读取包名与版本(node 必可用),用于推导压缩包名
81
+ PKG_NAME=$(node -p "require('./package.json').name" 2>/dev/null || echo app)
82
+ PKG_VERSION=$(node -p "require('./package.json').version" 2>/dev/null || echo 1.0.0)
83
+ if [ -z "$ARCHIVE_NAME" ]; then
84
+ ARCHIVE_NAME="$PKG_NAME-$PKG_VERSION"
85
+ else
86
+ # 去掉用户可能带入的扩展名,避免双重后缀
87
+ ARCHIVE_NAME="${ARCHIVE_NAME%.tar.gz}"
88
+ ARCHIVE_NAME="${ARCHIVE_NAME%.tgz}"
89
+ ARCHIVE_NAME="${ARCHIVE_NAME%.zip}"
90
+ fi
91
+ TAR_ARCHIVE="$PROJECT_ROOT/output/$ARCHIVE_NAME.tar.gz"
92
+
93
+ # 本脚本不构建,必须使用 build.sh 已生成的压缩包
94
+ if [ ! -f "$TAR_ARCHIVE" ]; then
95
+ echo "错误: 未找到构建好的压缩包 $TAR_ARCHIVE" >&2
96
+ echo " deploy.sh 不负责构建,请先运行: bash scripts/build.sh" >&2
97
+ exit 1
98
+ fi
99
+ echo "==> 使用构建好的压缩包: $TAR_ARCHIVE"
100
+
101
+ if [ -n "$DEPLOY_DIR" ]; then
102
+ # 统一用 / 作为分隔符,避免 Windows 反斜杠路径与子目录 / 拼接时混用导致 mkdir/tar 失败
103
+ DEPLOY_DIR="${DEPLOY_DIR//\\//}"
104
+ # nginx 根目录必须存在;不存在则报错,避免 mkdir -p 误建出假的 nginx 目录
105
+ if [ ! -d "$DEPLOY_DIR" ]; then
106
+ echo "错误: nginx 目录不存在: $DEPLOY_DIR" >&2
107
+ exit 1
108
+ fi
109
+ TARGET="$DEPLOY_DIR"
110
+ if [ -n "$SUBDIR" ]; then
111
+ TARGET="$DEPLOY_DIR/$SUBDIR"
112
+ # 子目录不存在时自动创建(nginx 目录已存在的前提下)
113
+ if [ ! -d "$TARGET" ]; then
114
+ echo "==> 目标子目录不存在,自动创建: $TARGET"
115
+ fi
116
+ echo "==> 目标子目录: $SUBDIR"
117
+ fi
118
+ echo "==> 解压压缩包到目标目录(nginx 可直接 serve): $TARGET"
119
+ mkdir -p "$TARGET"
120
+ if [ "$CLEAN" -eq 1 ]; then
121
+ echo "==> 清空目标目录现有内容"
122
+ find "$TARGET" -mindepth 1 -maxdepth 1 -exec rm -rf {} +
123
+ fi
124
+ tar -xzf "$TAR_ARCHIVE" -C "$TARGET"
125
+ echo "==> 已发布(解压): $TARGET"
126
+ else
127
+ echo "==> 压缩包已就绪: $TAR_ARCHIVE (未指定发布目录,跳过发布)"
128
+ fi
@@ -0,0 +1,248 @@
1
+ #!/usr/bin/env node
2
+ // merge-lib-config.mjs
3
+ // 读取项目 package.json 中所有依赖,逐个解析其 lib.config.ts,
4
+ // 把其中的 libResolves / libModules 合并后输出到 <项目根>/common/lib.config.ts。
5
+ //
6
+ // 支持两种依赖形式:
7
+ // 1) "new-app": "link:../new-app" -> 直接解析相对路径下的 lib.config.ts
8
+ // 2) "new-app": "^0.2.1" -> 视为已安装,按候选目录依次查找其 lib.config.ts:
9
+ // a) Node 模块解析(require.resolve) —— 最权威,能穿透 pnpm 软链 / .pnpm store
10
+ // b) <项目>/node_modules/<name> (项目内安装)
11
+ // c) 全局安装目录(由 `pnpm/npm ls -g --parseable` 解析,兼容 pnpm 11 哈希布局)
12
+ // —— 即 new-app 通过 `pnpm add -g .` 装成全局包的情况
13
+ // (workspace:/file:/npm: 前缀同样按上述位置处理)
14
+ // 找不到时脚本会打印所有尝试过的目录,便于定位(未安装 / 或包的 files 未含 lib.config.ts)。
15
+ //
16
+ // 用法: node scripts/merge-lib-config.mjs
17
+ // - 在 build.sh 中于 install 之后、build 之前被调用。
18
+ // - 即使没有任何可合并的依赖 / 没有可合并的 lib.config.ts,也会输出一个空的
19
+ // common/lib.config.ts(不跳过),保证下游构建始终有该文件可用。
20
+ // - resolve 参数保留原样(如 resolve(process.cwd(), '.', './node_modules/...'))。
21
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
22
+ import { resolve as pathResolve, join, sep } from 'node:path';
23
+ import { execSync } from 'node:child_process';
24
+ import { createRequire } from 'node:module';
25
+
26
+ const projectRoot = process.cwd();
27
+ // 用项目根作为解析基,require.resolve 可权威定位已安装包(穿透 pnpm 软链 / .pnpm store)
28
+ const requireFromRoot = createRequire(join(projectRoot, 'package.json'));
29
+
30
+ // 解析全局已安装的包: 包名 -> 真实安装目录。
31
+ // 兼容两种布局:
32
+ // - npm 全局: <npmRoot>/<name>
33
+ // - pnpm 全局(含 11.x 哈希布局): <pnpmRoot>/<hash>/node_modules/<name>
34
+ // 例如 new-app 通过 `pnpm add -g .` 安装后位于此类路径下。
35
+ // 通过 `pnpm ls -g --parseable` / `npm ls -g --parseable` 的输出按行解析得到。
36
+ function getGlobalPkgs() {
37
+ const map = new Map(); // name -> dir
38
+ const tryCmd = (cmd) => {
39
+ try {
40
+ const out = execSync(cmd, {
41
+ encoding: 'utf-8',
42
+ stdio: ['ignore', 'pipe', 'ignore'],
43
+ });
44
+ for (const raw of out.split(/\r?\n/)) {
45
+ const p = raw.trim();
46
+ if (!p) continue;
47
+ const marker = `node_modules${sep}`;
48
+ const idx = p.indexOf(marker);
49
+ if (idx === -1) continue; // 跳过根目录等非包行
50
+ const name = p.slice(idx + marker.length); // @scope/name 或 name
51
+ if (name && !map.has(name)) map.set(name, p);
52
+ }
53
+ } catch {
54
+ // 该命令不可用则忽略
55
+ }
56
+ };
57
+ tryCmd('pnpm ls -g --parseable');
58
+ tryCmd('npm ls -g --parseable');
59
+ return map;
60
+ }
61
+ const globalPkgs = getGlobalPkgs();
62
+ if (globalPkgs.size > 0) {
63
+ console.log(`merge-lib-config: 全局已安装包 ${globalPkgs.size} 个`);
64
+ }
65
+
66
+ // 收集某依赖名的所有候选安装目录(按优先级),用于查找其 lib.config.ts:
67
+ // 1) Node 模块解析(最权威:node_modules 顶层软链 / .pnpm store / NODE_PATH)
68
+ // 2) <项目>/node_modules/<name>
69
+ // 3) 全局安装目录(pnpm/npm ls -g)
70
+ function candidateDirsFor(name) {
71
+ const dirs = [];
72
+ const push = (d) => {
73
+ const a = pathResolve(projectRoot, d);
74
+ if (!dirs.includes(a)) dirs.push(a);
75
+ };
76
+ // 1) Node 解析(能找到 pnpm 实际落盘位置,含软链目标)
77
+ try {
78
+ const pkgJson = requireFromRoot.resolve(`${name}/package.json`);
79
+ push(join(pkgJson, '..'));
80
+ } catch {
81
+ // 项目 node_modules 中不存在该包(可能仅全局安装或根本未装)
82
+ }
83
+ // 2) 项目内 node_modules
84
+ push(join('node_modules', name));
85
+ // 3) 全局安装目录
86
+ if (globalPkgs.has(name)) push(globalPkgs.get(name));
87
+ return dirs;
88
+ }
89
+
90
+ // 读取 package.json,收集所有依赖的候选目录
91
+ const pkgPath = join(projectRoot, 'package.json');
92
+ const candidateDirs = []; // { name, path }
93
+ const seenPaths = new Set();
94
+ function addCandidate(name, dir) {
95
+ const abs = pathResolve(projectRoot, dir);
96
+ if (seenPaths.has(abs)) return; // 同一目录只处理一次
97
+ seenPaths.add(abs);
98
+ candidateDirs.push({ name, path: abs });
99
+ }
100
+ if (!existsSync(pkgPath)) {
101
+ console.log('merge-lib-config: 未找到 package.json,将输出空的 common/lib.config.ts');
102
+ } else {
103
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
104
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
105
+ for (const [name, spec] of Object.entries(deps)) {
106
+ if (typeof spec !== 'string') continue;
107
+ if (spec.startsWith('link:')) {
108
+ // link:../new-app -> 直接解析相对路径
109
+ addCandidate(name, spec.slice('link:'.length));
110
+ } else if (
111
+ // 版本范围 / workspace: / file: / npm: -> 视为已安装,按候选目录依次查找
112
+ /^[\^~><=*\s\d]/.test(spec) ||
113
+ spec.startsWith('workspace:') ||
114
+ spec.startsWith('file:') ||
115
+ spec.startsWith('npm:')
116
+ ) {
117
+ for (const d of candidateDirsFor(name)) addCandidate(name, d);
118
+ }
119
+ // 其它形式(如 git url)暂忽略
120
+ }
121
+ if (candidateDirs.length === 0) {
122
+ console.log('merge-lib-config: 未找到可合并的依赖,将输出空的 common/lib.config.ts');
123
+ }
124
+ }
125
+
126
+ // 2) 解析单个 lib.config.ts 的 libResolves / libModules
127
+ function parseLibConfig(content) {
128
+ const resolves = {};
129
+ const modules = [];
130
+
131
+ // libResolves = { ... }
132
+ const resMatch = content.match(/export\s+const\s+libResolves\s*=\s*\{([\s\S]*?)\n\s*\};/);
133
+ if (resMatch) {
134
+ for (const line of resMatch[1].split('\n')) {
135
+ const m = line.match(/'([^']+)'\s*:\s*([\s\S]+?)\s*,?\s*$/);
136
+ if (m) resolves[m[1]] = m[2].trim();
137
+ }
138
+ }
139
+
140
+ // libModules = [ ... ]
141
+ const modMatch = content.match(/export\s+const\s+libModules\s*=\s*\[([\s\S]*?)\n\s*\];/);
142
+ if (modMatch) {
143
+ const reObj = /\{([\s\S]*?)\}/g;
144
+ let m;
145
+ while ((m = reObj.exec(modMatch[1])) !== null) {
146
+ const entry = {};
147
+ const propRe = /(\w+)\s*:\s*(true|false|'([^']*)')/g;
148
+ let p;
149
+ while ((p = propRe.exec(m[1])) !== null) {
150
+ const key = p[1];
151
+ if (p[2] === 'true') entry[key] = true;
152
+ else if (p[2] === 'false') entry[key] = false;
153
+ else entry[key] = p[3];
154
+ }
155
+ if (entry.name) modules.push(entry);
156
+ }
157
+ }
158
+
159
+ return { resolves, modules };
160
+ }
161
+
162
+ // 3) 合并(resolves 按 key 去重取先到者;modules 按 name 去重)
163
+ const mergedResolves = {};
164
+ const mergedModules = [];
165
+ const seenModules = new Set();
166
+ let conflictWarned = false;
167
+
168
+ const tries = []; // 诊断用: 记录每个候选目录的查找结果
169
+ for (const { name, path } of candidateDirs) {
170
+ const cfgPath = join(path, 'lib.config.ts');
171
+ const dirExists = existsSync(path);
172
+ const hasLib = dirExists && existsSync(cfgPath);
173
+ tries.push({ name, dir: path, dirExists, hasLib });
174
+ if (!hasLib) {
175
+ if (dirExists) {
176
+ // 包已安装,但 lib.config.ts 不在其中 —— 多半是包的 package.json "files" 没包含它
177
+ console.log(
178
+ `merge-lib-config: [${name}] 目录存在但无 lib.config.ts: ${path} ` +
179
+ `(检查该包 package.json 的 "files" 是否包含 lib.config.ts)`
180
+ );
181
+ }
182
+ continue;
183
+ }
184
+ const { resolves, modules } = parseLibConfig(readFileSync(cfgPath, 'utf-8'));
185
+ for (const [k, v] of Object.entries(resolves)) {
186
+ if (k in mergedResolves) {
187
+ if (!conflictWarned) {
188
+ console.warn(`merge-lib-config: libResolves 键 "${k}" 重复,保留首个定义`);
189
+ conflictWarned = true;
190
+ }
191
+ continue;
192
+ }
193
+ mergedResolves[k] = v;
194
+ }
195
+ for (const mod of modules) {
196
+ if (seenModules.has(mod.name)) continue;
197
+ seenModules.add(mod.name);
198
+ mergedModules.push(mod);
199
+ }
200
+ }
201
+
202
+ // 诊断:若最终未合并到任何内容,打印尝试过的、确实存在的目录,便于定位问题
203
+ if (Object.keys(mergedResolves).length === 0 && mergedModules.length === 0) {
204
+ const existing = tries.filter((t) => t.dirExists);
205
+ if (existing.length > 0) {
206
+ console.warn('merge-lib-config: 未合并到任何 lib.config.ts。已安装但无 lib.config.ts 的包:');
207
+ for (const t of existing) {
208
+ console.warn(` - [${t.name}] ${t.dir}`);
209
+ }
210
+ console.warn(' => 请确认目标包的 package.json "files" 包含 lib.config.ts,或改用 link: 引用源码目录。');
211
+ } else if (tries.length > 0) {
212
+ console.warn('merge-lib-config: 未合并到任何 lib.config.ts。所有候选目录均不存在:');
213
+ for (const t of tries) console.warn(` - [${t.name}] ${t.dir}`);
214
+ console.warn(' => 该依赖似乎未安装(项目 node_modules 与全局都没有)。请先安装它再构建。');
215
+ }
216
+ }
217
+
218
+ // 4) 渲染并写出到 common/lib.config.ts
219
+ function render() {
220
+ const lines = [];
221
+ if (Object.keys(mergedResolves).length > 0) {
222
+ lines.push("import { resolve } from 'path';");
223
+ lines.push('');
224
+ }
225
+ lines.push('export const libResolves = {');
226
+ for (const [k, v] of Object.entries(mergedResolves)) {
227
+ lines.push(` '${k}': ${v},`);
228
+ }
229
+ lines.push('};');
230
+ lines.push('');
231
+ lines.push('export const libModules = [');
232
+ for (const mod of mergedModules) {
233
+ const props = Object.entries(mod)
234
+ .map(([k, val]) => (val === true ? `${k}: true` : val === false ? `${k}: false` : `${k}: '${val}'`))
235
+ .join(', ');
236
+ lines.push(` { ${props} },`);
237
+ }
238
+ lines.push('];');
239
+ return lines.join('\n') + '\n';
240
+ }
241
+
242
+ const outDir = join(projectRoot, 'common');
243
+ mkdirSync(outDir, { recursive: true });
244
+ const outPath = join(outDir, 'lib.config.ts');
245
+ writeFileSync(outPath, render(), 'utf-8');
246
+ console.log(
247
+ `merge-lib-config: 已合并 ${Object.keys(mergedResolves).length} 个 resolve / ${mergedModules.length} 个 module -> ${outPath}`
248
+ );
@@ -1,4 +1,4 @@
1
- import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
1
+ import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
2
2
  import { routes } from '@common/routes';
3
3
 
4
4
  export const constantRoutes: RouteRecordRaw[] = [
@@ -10,7 +10,7 @@ export const constantRoutes: RouteRecordRaw[] = [
10
10
  ]
11
11
 
12
12
  const router = createRouter({
13
- history: createWebHistory(),
13
+ history: createWebHashHistory(),
14
14
  routes: constantRoutes
15
15
  })
16
16
 
@@ -17,7 +17,7 @@ const options = [
17
17
  const message = useMessage();
18
18
 
19
19
  const showMsg = () => {
20
- message.success('NaiveUI + Vue3 + TS + Vite 搭建成功!')
20
+ message.success('Sys-Designer 搭建成功!')
21
21
  }
22
22
 
23
23
  </script>