@lark-apaas/coding-template-nestjs-react-fullstack 0.1.42-alpha.20260917142620 → 0.1.42
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 +0 -37
- package/package.json +1 -1
- package/template/package-lock.json +105 -747
- package/template/package.json +6 -7
- package/template/scripts/build.sh +43 -81
- package/template/scripts/lint.js +27 -169
- package/template/tsconfig.node.json +1 -2
- package/template/scripts/typecheck-mcp-ui.js +0 -32
- package/template/tsconfig.mcp-ui.json +0 -33
package/template/package.json
CHANGED
|
@@ -20,16 +20,15 @@
|
|
|
20
20
|
"start": "NODE_ENV=production node main.js",
|
|
21
21
|
"eslint": "eslint . --quiet",
|
|
22
22
|
"stylelint": "stylelint client/src/**/*.css --quiet",
|
|
23
|
-
"type:check": "concurrently -n \"server,client
|
|
23
|
+
"type:check": "concurrently -n \"server,client\" -c \"blue,green\" \"npm run type:check:server\" \"npm run type:check:client\"",
|
|
24
24
|
"type:check:client": "tsc --noEmit --project tsconfig.app.json",
|
|
25
25
|
"type:check:server": "tsc --noEmit --project tsconfig.node.json",
|
|
26
26
|
"lint": "node ./scripts/lint.js",
|
|
27
27
|
"precommit": "node scripts/hooks/run-precommit.js",
|
|
28
|
-
"upgrade": "npx -y @lark-apaas/fullstack-cli sync --disable-gen-openapi"
|
|
29
|
-
"type:check:mcp-ui": "node ./scripts/typecheck-mcp-ui.js"
|
|
28
|
+
"upgrade": "npx -y @lark-apaas/fullstack-cli sync --disable-gen-openapi"
|
|
30
29
|
},
|
|
31
30
|
"dependencies": {
|
|
32
|
-
"@lark-apaas/fullstack-nestjs-core": "1.1.62
|
|
31
|
+
"@lark-apaas/fullstack-nestjs-core": "^1.1.62",
|
|
33
32
|
"@nestjs/axios": "^4.0.1",
|
|
34
33
|
"@nestjs/cache-manager": "^3.1.3",
|
|
35
34
|
"@nestjs/common": "^10.4.20",
|
|
@@ -53,9 +52,9 @@
|
|
|
53
52
|
},
|
|
54
53
|
"devDependencies": {
|
|
55
54
|
"@hookform/resolvers": "^5.2.2",
|
|
56
|
-
"@lark-apaas/client-toolkit": "^1.2.
|
|
57
|
-
"@lark-apaas/coding-preset-vite-react": "1.0.
|
|
58
|
-
"@lark-apaas/fullstack-presets": "1.1.
|
|
55
|
+
"@lark-apaas/client-toolkit": "^1.2.71",
|
|
56
|
+
"@lark-apaas/coding-preset-vite-react": "^1.0.23",
|
|
57
|
+
"@lark-apaas/fullstack-presets": "^1.1.0",
|
|
59
58
|
"@nestjs/cli": "^10.4.9",
|
|
60
59
|
"@radix-ui/react-accordion": "^1.2.12",
|
|
61
60
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
@@ -43,43 +43,37 @@ echo ""
|
|
|
43
43
|
echo "🔨 [3/5] 并行构建 server 和 client"
|
|
44
44
|
STEP_START=$(node -e "console.log(Date.now())")
|
|
45
45
|
|
|
46
|
-
#
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
# 检查构建结果
|
|
68
|
-
if [ $SERVER_EXIT -ne 0 ]; then
|
|
69
|
-
echo " ❌ Server 构建失败"
|
|
70
|
-
cat /tmp/build-server.log
|
|
71
|
-
exit 1
|
|
72
|
-
fi
|
|
73
|
-
|
|
74
|
-
if [ $CLIENT_EXIT -ne 0 ]; then
|
|
75
|
-
echo " ❌ Client 构建失败"
|
|
76
|
-
cat /tmp/build-client.log
|
|
77
|
-
exit 1
|
|
78
|
-
fi
|
|
79
|
-
|
|
80
|
-
echo " ✅ Server 构建完成"
|
|
81
|
-
echo " ✅ Client 构建完成"
|
|
46
|
+
# 并行构建
|
|
47
|
+
echo " ├─ 启动 server 构建..."
|
|
48
|
+
npm run build:server > /tmp/build-server.log 2>&1 &
|
|
49
|
+
SERVER_PID=$!
|
|
50
|
+
|
|
51
|
+
echo " ├─ 启动 client 构建..."
|
|
52
|
+
npm run build:client > /tmp/build-client.log 2>&1 &
|
|
53
|
+
CLIENT_PID=$!
|
|
54
|
+
|
|
55
|
+
# 等待两个构建完成
|
|
56
|
+
SERVER_EXIT=0
|
|
57
|
+
CLIENT_EXIT=0
|
|
58
|
+
|
|
59
|
+
wait $SERVER_PID || SERVER_EXIT=$?
|
|
60
|
+
wait $CLIENT_PID || CLIENT_EXIT=$?
|
|
61
|
+
|
|
62
|
+
# 检查构建结果
|
|
63
|
+
if [ $SERVER_EXIT -ne 0 ]; then
|
|
64
|
+
echo " ❌ Server 构建失败"
|
|
65
|
+
cat /tmp/build-server.log
|
|
66
|
+
exit 1
|
|
82
67
|
fi
|
|
68
|
+
|
|
69
|
+
if [ $CLIENT_EXIT -ne 0 ]; then
|
|
70
|
+
echo " ❌ Client 构建失败"
|
|
71
|
+
cat /tmp/build-client.log
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
echo " ✅ Server 构建完成"
|
|
76
|
+
echo " ✅ Client 构建完成"
|
|
83
77
|
print_time $STEP_START
|
|
84
78
|
echo ""
|
|
85
79
|
|
|
@@ -87,21 +81,18 @@ echo ""
|
|
|
87
81
|
echo "📦 [4/5] 准备 server 依赖产物"
|
|
88
82
|
STEP_START=$(node -e "console.log(Date.now())")
|
|
89
83
|
|
|
90
|
-
|
|
91
|
-
mkdir -p "$OUT_DIR/dist/client"
|
|
84
|
+
mkdir -p "$OUT_DIR/dist/client"
|
|
92
85
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
# 构建产物 HTML 随后拷,覆盖 public 里的同名文件(保证入口页是构建版,不被 public 静默覆盖)
|
|
99
|
-
cp "$ROOT_DIR/dist/client/"*.html "$OUT_DIR/dist/client/" || true
|
|
86
|
+
# public 先进(server setBaseViewsDir + nestjs-core 中间件同源 serve)
|
|
87
|
+
if [ -d "$ROOT_DIR/client/public" ]; then
|
|
88
|
+
cp -R "$ROOT_DIR/client/public/." "$OUT_DIR/dist/client/"
|
|
89
|
+
fi
|
|
100
90
|
|
|
101
|
-
|
|
102
|
-
|
|
91
|
+
# 构建产物 HTML 随后拷,覆盖 public 里的同名文件(保证入口页是构建版,不被 public 静默覆盖)
|
|
92
|
+
cp "$ROOT_DIR/dist/client/"*.html "$OUT_DIR/dist/client/" || true
|
|
103
93
|
|
|
104
|
-
|
|
94
|
+
# 拷贝 run.sh 文件
|
|
95
|
+
cp "$ROOT_DIR/scripts/run.sh" "$OUT_DIR/"
|
|
105
96
|
|
|
106
97
|
# 清理无用文件
|
|
107
98
|
rm -rf "$ROOT_DIR/dist/scripts"
|
|
@@ -115,9 +106,7 @@ echo "✂️ [5/5] 智能依赖裁剪"
|
|
|
115
106
|
STEP_START=$(node -e "console.log(Date.now())")
|
|
116
107
|
|
|
117
108
|
# 分析实际依赖、复制并裁剪 node_modules、生成精简的 package.json
|
|
118
|
-
|
|
119
|
-
node "$ROOT_DIR/scripts/prune-smart.js"
|
|
120
|
-
fi
|
|
109
|
+
node "$ROOT_DIR/scripts/prune-smart.js"
|
|
121
110
|
|
|
122
111
|
print_time $STEP_START
|
|
123
112
|
echo ""
|
|
@@ -127,37 +116,10 @@ echo "构建完成"
|
|
|
127
116
|
print_time $TOTAL_START
|
|
128
117
|
|
|
129
118
|
# 输出产物信息
|
|
130
|
-
DIST_SIZE=$(du -sh "$
|
|
119
|
+
DIST_SIZE=$(du -sh "$OUT_DIR" | cut -f1)
|
|
120
|
+
NODE_MODULES_SIZE=$(du -sh "$OUT_DIR/node_modules" | cut -f1)
|
|
131
121
|
echo ""
|
|
132
122
|
echo "📊 构建产物统计:"
|
|
133
123
|
echo " 总大小: $DIST_SIZE"
|
|
134
|
-
|
|
135
|
-
NODE_MODULES_SIZE=$(du -sh "$OUT_DIR/node_modules" | cut -f1)
|
|
136
|
-
echo " node_modules: $NODE_MODULES_SIZE"
|
|
137
|
-
fi
|
|
124
|
+
echo " node_modules: $NODE_MODULES_SIZE"
|
|
138
125
|
echo ""
|
|
139
|
-
|
|
140
|
-
# 仅成功的全构建生成 backendBuildID;必须在构建、产物准备和依赖裁剪之后。
|
|
141
|
-
# UI/Skill 内容由 SDK 独立计入总 hash;纯前端发布不替换已部署的 backendBuildID。
|
|
142
|
-
if [[ "${only_frontend_change:-false}" != "true" ]]; then
|
|
143
|
-
node - "$ROOT_DIR/dist" <<'NODE'
|
|
144
|
-
const fs = require('node:fs');
|
|
145
|
-
const path = require('node:path');
|
|
146
|
-
const { randomUUID } = require('node:crypto');
|
|
147
|
-
const dist = process.argv[2];
|
|
148
|
-
// 防止 build:server 空跑成功、却没有可交付的服务端入口。
|
|
149
|
-
if (!fs.statSync(path.join(dist, 'server/main.js')).isFile()) {
|
|
150
|
-
throw new Error('Missing server build output: dist/server/main.js');
|
|
151
|
-
}
|
|
152
|
-
// runtime cwd 分别为工程根、dist/(或整包解压到 dist-server/)、旧版 dist/server/。
|
|
153
|
-
// 与 MCP UI/Skill 的交付布局一致:复制产物时保留同一个 UUID,不在启动时生成。
|
|
154
|
-
const files = [
|
|
155
|
-
path.join(dist, 'mcp-build-id'),
|
|
156
|
-
path.join(dist, 'dist/mcp-build-id'),
|
|
157
|
-
path.join(dist, 'server/dist/mcp-build-id'),
|
|
158
|
-
];
|
|
159
|
-
for (const file of files) fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
160
|
-
const buildID = randomUUID();
|
|
161
|
-
for (const file of files) fs.writeFileSync(file, buildID, 'utf8');
|
|
162
|
-
NODE
|
|
163
|
-
fi
|
package/template/scripts/lint.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const path = require('node:path');
|
|
4
|
-
const fs = require('node:fs');
|
|
5
4
|
const { spawn } = require('node:child_process');
|
|
5
|
+
const fs = require('node:fs');
|
|
6
6
|
|
|
7
7
|
const cwd = process.cwd();
|
|
8
8
|
|
|
@@ -11,50 +11,30 @@ function getBinName(name) {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
function runCommand(command, args) {
|
|
14
|
-
return new Promise(resolve => {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
15
|
const child = spawn(command, args, {
|
|
16
16
|
cwd,
|
|
17
17
|
stdio: 'inherit',
|
|
18
18
|
shell: false,
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
child.on('close', code => resolve(code
|
|
21
|
+
child.on('close', (code) => resolve(code || 0));
|
|
22
22
|
child.on('error', () => resolve(1));
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
// 降低并发资源占用、让各任务输出按顺序清晰可读,同时保留“一次暴露所有 lint 问题”的行为。
|
|
28
|
-
async function runTasksSerially(taskSpecs) {
|
|
29
|
-
let exitCode = 0;
|
|
30
|
-
for (const [command, args] of taskSpecs) {
|
|
31
|
-
const code = await runCommand(command, args);
|
|
32
|
-
if (code !== 0) {
|
|
33
|
-
exitCode = 1;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return exitCode;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function normalizeProjectFile(filePath, checkDeleted = false) {
|
|
26
|
+
function normalizeProjectFile(filePath) {
|
|
40
27
|
const absolutePath = path.isAbsolute(filePath)
|
|
41
28
|
? filePath
|
|
42
29
|
: path.resolve(cwd, filePath);
|
|
43
30
|
|
|
44
31
|
if (!fs.existsSync(absolutePath)) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
console.log(`[lint] Deleted file: ${filePath}; checking affected projects`);
|
|
32
|
+
console.warn(`[lint] Skip missing file: ${filePath}`);
|
|
33
|
+
return null;
|
|
50
34
|
}
|
|
51
35
|
|
|
52
36
|
const relativePath = path.relative(cwd, absolutePath);
|
|
53
|
-
if (
|
|
54
|
-
relativePath === '..' ||
|
|
55
|
-
relativePath.startsWith(`..${path.sep}`) ||
|
|
56
|
-
path.isAbsolute(relativePath)
|
|
57
|
-
) {
|
|
37
|
+
if (relativePath.startsWith('..')) {
|
|
58
38
|
console.warn(`[lint] Skip file outside project: ${filePath}`);
|
|
59
39
|
return null;
|
|
60
40
|
}
|
|
@@ -111,106 +91,25 @@ function canRunStylelint() {
|
|
|
111
91
|
|
|
112
92
|
if (!pkg.scripts || !pkg.scripts.stylelint) return false;
|
|
113
93
|
|
|
114
|
-
return STYLELINT_CONFIG_FILES.some(file =>
|
|
115
|
-
fs.existsSync(path.join(cwd, file))
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const UI_DIR = 'server/mcp/ui';
|
|
120
|
-
|
|
121
|
-
function uiSourceFiles() {
|
|
122
|
-
const files = [];
|
|
123
|
-
function visit(relative) {
|
|
124
|
-
const absolute = path.join(cwd, relative);
|
|
125
|
-
if (!fs.existsSync(absolute)) return;
|
|
126
|
-
if (fs.lstatSync(absolute).isSymbolicLink()) {
|
|
127
|
-
throw new Error(`MCP UI 检查不支持符号链接: ${relative}`);
|
|
128
|
-
}
|
|
129
|
-
for (const entry of fs.readdirSync(absolute, { withFileTypes: true })) {
|
|
130
|
-
if (['node_modules', 'dist', '.git'].includes(entry.name)) continue;
|
|
131
|
-
const file = `${relative}/${entry.name}`;
|
|
132
|
-
if (entry.isSymbolicLink())
|
|
133
|
-
throw new Error(`MCP UI 检查不支持符号链接: ${file}`);
|
|
134
|
-
if (entry.isDirectory()) visit(file);
|
|
135
|
-
else if (isEslintTarget(file) && !/\.d\.(?:ts|mts|cts)$/.test(file))
|
|
136
|
-
files.push(file);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
visit(UI_DIR);
|
|
140
|
-
return files.sort();
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function uiTasks({ types = true, eslint = true, aggregate = false } = {}) {
|
|
144
|
-
const sources = uiSourceFiles();
|
|
145
|
-
if (!sources.length) return [];
|
|
146
|
-
const tasks = [];
|
|
147
|
-
// 只在有 UI 源码时读取聚合命令;普通应用不依赖 MCP 配置或脚本。
|
|
148
|
-
if (types && aggregate) {
|
|
149
|
-
const scripts = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8')).scripts || {};
|
|
150
|
-
types = !/\bnpm\s+run\s+type:check:mcp-ui(?=[\s"';&|)]|$)/.test(scripts['type:check'] || '');
|
|
151
|
-
}
|
|
152
|
-
if (types) tasks.push([getBinName('npm'), ['run', 'type:check:mcp-ui']]);
|
|
153
|
-
if (eslint) {
|
|
154
|
-
const config = [
|
|
155
|
-
'eslint.mcp-ui.config.js',
|
|
156
|
-
'eslint.mcp-ui.config.cjs',
|
|
157
|
-
'eslint.mcp-ui.config.mjs',
|
|
158
|
-
].find(file => fs.existsSync(path.join(cwd, file)));
|
|
159
|
-
let resolvedConfig = config;
|
|
160
|
-
if (!resolvedConfig) {
|
|
161
|
-
try {
|
|
162
|
-
resolvedConfig =
|
|
163
|
-
require.resolve('@lark-apaas/fullstack-presets/lib/mcp/eslint-ui.js');
|
|
164
|
-
} catch {
|
|
165
|
-
throw new Error(
|
|
166
|
-
'请升级 @lark-apaas/fullstack-presets 以使用 MCP UI 默认 ESLint 配置,或提供 eslint.mcp-ui.config.cjs'
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
tasks.push([
|
|
171
|
-
getBinName('npx'),
|
|
172
|
-
[
|
|
173
|
-
'--no-install',
|
|
174
|
-
'eslint',
|
|
175
|
-
'--config',
|
|
176
|
-
resolvedConfig,
|
|
177
|
-
'--quiet',
|
|
178
|
-
'--max-warnings',
|
|
179
|
-
'0',
|
|
180
|
-
...sources,
|
|
181
|
-
],
|
|
182
|
-
]);
|
|
183
|
-
}
|
|
184
|
-
return tasks;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function isUiFile(file) {
|
|
188
|
-
return file.startsWith('server/mcp/ui/');
|
|
94
|
+
return STYLELINT_CONFIG_FILES.some(file => fs.existsSync(path.join(cwd, file)));
|
|
189
95
|
}
|
|
190
96
|
|
|
191
97
|
async function runDefaultLint() {
|
|
192
|
-
const
|
|
193
|
-
[getBinName('npm'), ['run', 'eslint']],
|
|
194
|
-
[getBinName('npm'), ['run', 'type:check']],
|
|
195
|
-
];
|
|
196
|
-
|
|
197
|
-
taskSpecs.push(...uiTasks({ aggregate: true }));
|
|
98
|
+
const commands = ['npm run eslint', 'npm run type:check'];
|
|
198
99
|
|
|
199
100
|
if (canRunStylelint()) {
|
|
200
|
-
|
|
101
|
+
commands.push('npm run stylelint');
|
|
201
102
|
} else {
|
|
202
|
-
console.warn(
|
|
203
|
-
'[lint] Skip stylelint: missing scripts.stylelint or stylelint config'
|
|
204
|
-
);
|
|
103
|
+
console.warn('[lint] Skip stylelint: missing scripts.stylelint or stylelint config');
|
|
205
104
|
}
|
|
206
105
|
|
|
207
|
-
|
|
106
|
+
const code = await runCommand(getBinName('npx'), ['concurrently', ...commands]);
|
|
107
|
+
process.exit(code);
|
|
208
108
|
}
|
|
209
109
|
|
|
210
110
|
async function runSelectiveLint(inputFiles) {
|
|
211
|
-
const hasUiSources = uiSourceFiles().length > 0;
|
|
212
111
|
const normalizedFiles = Array.from(
|
|
213
|
-
new Set(inputFiles.map(
|
|
112
|
+
new Set(inputFiles.map(normalizeProjectFile).filter(Boolean)),
|
|
214
113
|
);
|
|
215
114
|
|
|
216
115
|
if (normalizedFiles.length === 0) {
|
|
@@ -218,40 +117,15 @@ async function runSelectiveLint(inputFiles) {
|
|
|
218
117
|
process.exit(0);
|
|
219
118
|
}
|
|
220
119
|
|
|
221
|
-
const eslintFiles = normalizedFiles.filter(
|
|
222
|
-
|
|
223
|
-
fs.existsSync(path.join(cwd, file)) &&
|
|
224
|
-
isEslintTarget(file) &&
|
|
225
|
-
(!hasUiSources || (!isUiFile(file) && !file.startsWith('eslint.mcp-ui.config.')))
|
|
226
|
-
);
|
|
227
|
-
const stylelintFiles = normalizedFiles.filter(
|
|
228
|
-
file => fs.existsSync(path.join(cwd, file)) && isStylelintTarget(file)
|
|
229
|
-
);
|
|
120
|
+
const eslintFiles = normalizedFiles.filter(isEslintTarget);
|
|
121
|
+
const stylelintFiles = normalizedFiles.filter(isStylelintTarget);
|
|
230
122
|
const typeCheckFiles = normalizedFiles.filter(isTypeCheckTarget);
|
|
231
123
|
|
|
232
124
|
const clientTypeFiles = [];
|
|
233
125
|
const serverTypeFiles = [];
|
|
234
|
-
const rootConfigChanged = hasUiSources && normalizedFiles.some(file =>
|
|
235
|
-
/^tsconfig(?:\.[^/]*)?\.json$/.test(file)
|
|
236
|
-
);
|
|
237
|
-
if (rootConfigChanged) {
|
|
238
|
-
clientTypeFiles.push('config');
|
|
239
|
-
serverTypeFiles.push('config');
|
|
240
|
-
}
|
|
241
|
-
let checkUi =
|
|
242
|
-
rootConfigChanged ||
|
|
243
|
-
normalizedFiles.some(
|
|
244
|
-
file =>
|
|
245
|
-
isUiFile(file) ||
|
|
246
|
-
file.startsWith('shared/') ||
|
|
247
|
-
file.startsWith('eslint.mcp-ui.config.') ||
|
|
248
|
-
file === 'package.json'
|
|
249
|
-
);
|
|
250
126
|
|
|
251
127
|
for (const filePath of typeCheckFiles) {
|
|
252
|
-
if (
|
|
253
|
-
checkUi = true;
|
|
254
|
-
} else if (filePath.startsWith('client/')) {
|
|
128
|
+
if (filePath.startsWith('client/')) {
|
|
255
129
|
clientTypeFiles.push(filePath);
|
|
256
130
|
} else if (filePath.startsWith('server/')) {
|
|
257
131
|
serverTypeFiles.push(filePath);
|
|
@@ -261,52 +135,36 @@ async function runSelectiveLint(inputFiles) {
|
|
|
261
135
|
}
|
|
262
136
|
}
|
|
263
137
|
|
|
264
|
-
const
|
|
138
|
+
const tasks = [];
|
|
265
139
|
|
|
266
140
|
if (eslintFiles.length > 0) {
|
|
267
|
-
|
|
141
|
+
tasks.push(runCommand(getBinName('npx'), ['eslint', '--quiet', ...eslintFiles]));
|
|
268
142
|
}
|
|
269
143
|
|
|
270
144
|
if (stylelintFiles.length > 0 && !canRunStylelint()) {
|
|
271
|
-
console.warn(
|
|
272
|
-
'[lint] Skip stylelint: missing scripts.stylelint or stylelint config'
|
|
273
|
-
);
|
|
145
|
+
console.warn('[lint] Skip stylelint: missing scripts.stylelint or stylelint config');
|
|
274
146
|
} else if (stylelintFiles.length > 0) {
|
|
275
|
-
|
|
276
|
-
getBinName('npx'),
|
|
277
|
-
['stylelint', '--quiet', ...stylelintFiles],
|
|
278
|
-
]);
|
|
147
|
+
tasks.push(runCommand(getBinName('npx'), ['stylelint', '--quiet', ...stylelintFiles]));
|
|
279
148
|
}
|
|
280
149
|
|
|
281
150
|
if (clientTypeFiles.length > 0) {
|
|
282
|
-
|
|
151
|
+
tasks.push(runCommand(getBinName('npm'), ['run', 'type:check:client']));
|
|
283
152
|
}
|
|
284
153
|
|
|
285
154
|
if (serverTypeFiles.length > 0) {
|
|
286
|
-
|
|
155
|
+
tasks.push(runCommand(getBinName('npm'), ['run', 'type:check:server']));
|
|
287
156
|
}
|
|
288
157
|
|
|
289
|
-
if (
|
|
290
|
-
|
|
291
|
-
if (taskSpecs.length === 0) {
|
|
158
|
+
if (tasks.length === 0) {
|
|
292
159
|
console.log('[lint] No supported files matched for lint');
|
|
293
160
|
process.exit(0);
|
|
294
161
|
}
|
|
295
162
|
|
|
296
|
-
|
|
163
|
+
const results = await Promise.all(tasks);
|
|
164
|
+
process.exit(results.some(code => code !== 0) ? 1 : 0);
|
|
297
165
|
}
|
|
298
166
|
|
|
299
167
|
async function main() {
|
|
300
|
-
const args = process.argv.slice(2);
|
|
301
|
-
if (args.length === 1 && args[0] === '--eslint-mcp-ui') {
|
|
302
|
-
process.exit(await runTasksSerially(uiTasks({ types: false })));
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
if (args.length && !(args[0] === '--files' && args.length > 1)) {
|
|
306
|
-
throw new Error('用法: lint.js [--files <paths...>]');
|
|
307
|
-
}
|
|
308
|
-
if (args[0] === '--files' && args.slice(1).some(arg => arg.startsWith('--')))
|
|
309
|
-
throw new Error('--files 不可与其他检查模式混用');
|
|
310
168
|
const files = parseFilesArg(process.argv.slice(2));
|
|
311
169
|
if (files === null) {
|
|
312
170
|
await runDefaultLint();
|
|
@@ -321,7 +179,7 @@ async function main() {
|
|
|
321
179
|
await runSelectiveLint(files);
|
|
322
180
|
}
|
|
323
181
|
|
|
324
|
-
main().catch(error => {
|
|
182
|
+
main().catch((error) => {
|
|
325
183
|
const message = error instanceof Error ? error.message : String(error);
|
|
326
184
|
console.error(`[lint] Failed to run lint: ${message}`);
|
|
327
185
|
process.exit(1);
|
|
@@ -13,6 +13,5 @@
|
|
|
13
13
|
"excludeDirectories": ["node_modules/**"]
|
|
14
14
|
},
|
|
15
15
|
"include": ["server/**/*", "shared/**/*.ts"],
|
|
16
|
-
|
|
17
|
-
"exclude": ["server/mcp/ui", "node_modules", "dist", "client", "source_package", "**/*.spec.ts", "**/*.e2e-spec.ts"],
|
|
16
|
+
"exclude": ["node_modules", "dist", "client", "source_package", "**/*.spec.ts", "**/*.e2e-spec.ts"],
|
|
18
17
|
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
// MCP UI 是可选浏览器工程;没有源码时跳过,否则直接交给 tsc。
|
|
2
|
-
const fs = require('node:fs');
|
|
3
|
-
const path = require('node:path');
|
|
4
|
-
const { spawnSync } = require('node:child_process');
|
|
5
|
-
|
|
6
|
-
function hasSource(dir) {
|
|
7
|
-
if (!fs.existsSync(dir)) return false;
|
|
8
|
-
return fs.readdirSync(dir, { withFileTypes: true }).some(entry => {
|
|
9
|
-
if (['node_modules', 'dist'].includes(entry.name)) return false;
|
|
10
|
-
if (entry.isDirectory()) return hasSource(path.join(dir, entry.name));
|
|
11
|
-
return entry.isFile() && /\.(?:[cm]?[jt]sx?)$/.test(entry.name);
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (hasSource('server/mcp/ui')) {
|
|
16
|
-
// 已有应用的独立 UI 配置继续生效,避免升级覆盖自定义 paths/types。
|
|
17
|
-
const config = fs.existsSync('server/mcp/ui/tsconfig.json')
|
|
18
|
-
? 'server/mcp/ui/tsconfig.json'
|
|
19
|
-
: 'tsconfig.mcp-ui.json';
|
|
20
|
-
const result = spawnSync(
|
|
21
|
-
process.execPath,
|
|
22
|
-
[
|
|
23
|
-
require.resolve('typescript/bin/tsc'),
|
|
24
|
-
'--noEmit',
|
|
25
|
-
'--project',
|
|
26
|
-
config,
|
|
27
|
-
],
|
|
28
|
-
{ stdio: 'inherit' }
|
|
29
|
-
);
|
|
30
|
-
if (result.error) console.error(result.error.message);
|
|
31
|
-
process.exitCode = result.status ?? 1;
|
|
32
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "Bundler",
|
|
6
|
-
"jsx": "react-jsx",
|
|
7
|
-
"types": [],
|
|
8
|
-
"strict": true,
|
|
9
|
-
"checkJs": true,
|
|
10
|
-
"lib": [
|
|
11
|
-
"ES2020",
|
|
12
|
-
"DOM",
|
|
13
|
-
"DOM.Iterable"
|
|
14
|
-
],
|
|
15
|
-
"allowJs": true,
|
|
16
|
-
"skipLibCheck": true,
|
|
17
|
-
"esModuleInterop": true,
|
|
18
|
-
"noEmit": true,
|
|
19
|
-
"baseUrl": ".",
|
|
20
|
-
"paths": {
|
|
21
|
-
"@shared/*": [
|
|
22
|
-
"shared/*"
|
|
23
|
-
]
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
"include": [
|
|
27
|
-
"server/mcp/ui/**/*"
|
|
28
|
-
],
|
|
29
|
-
"exclude": [
|
|
30
|
-
"node_modules",
|
|
31
|
-
"dist"
|
|
32
|
-
]
|
|
33
|
-
}
|