@lark-apaas/fullstack-cli 1.1.67-alpha.20260917143103 → 1.1.67-alpha.20260918073527
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 +3 -3
- package/dist/index.js +4 -33
- package/package.json +1 -1
- package/templates/scripts/build.sh +0 -25
- package/templates/scripts/lint.js +16 -144
- package/templates/scripts/typecheck-mcp-ui.js +0 -32
- package/templates/tsconfig.mcp-ui.json +0 -33
package/README.md
CHANGED
|
@@ -399,10 +399,10 @@ MIT
|
|
|
399
399
|
|
|
400
400
|
## MCP UI 检查与存量配置同步
|
|
401
401
|
|
|
402
|
-
MCP UI
|
|
402
|
+
创建 MCP UI 时,Agent 在 `server/mcp/ui` 中同时生成 `tsconfig.json` 和 `eslint.config.cjs`,修改 UI 或其 shared 依赖后主动运行独立检查。模板和 CLI 不生成 UI 配置,也不将 UI 检查接入主工程 lint/type:check。
|
|
403
403
|
|
|
404
|
-
|
|
404
|
+
主工程保留原 client/server 检查。Agent 从工程根执行 `npx --no-install tsc --noEmit --project server/mcp/ui/tsconfig.json`,再执行 `npx --no-install eslint --config server/mcp/ui/eslint.config.cjs "server/mcp/ui/**/*.{ts,tsx,js,jsx}" --max-warnings 0`;必须检查退出码并修复错误。无需辅助脚本或新增 npm 命令。
|
|
405
405
|
|
|
406
|
-
|
|
406
|
+
CLI 不注入 MCP UI 检查命令;主工程保留原有 client/server 检查,UI 由 Agent 按独立配置执行标准 tsc/eslint。
|
|
407
407
|
|
|
408
408
|
存量 sync 不改写 ESLint 源码;UI 全局排除项由平台 ESLint 预设提供。TS 仅向已有显式 `exclude` 数组追加目录,保留注释和原项;依赖继承且未显式声明 `exclude` 的自定义配置保持不变并提示人工调整。自定义检查命令保留。lint.js 和 build.sh 仍由平台脚本模板维护,不依赖预设包中的脚本执行入口。
|
package/dist/index.js
CHANGED
|
@@ -2581,38 +2581,9 @@ function buildDefaultRules(opts) {
|
|
|
2581
2581
|
type: "file",
|
|
2582
2582
|
overwrite: true
|
|
2583
2583
|
},
|
|
2584
|
-
// UI
|
|
2584
|
+
// UI 位于 server/mcp/ui,检查配置由 Agent 随 UI 代码创建。
|
|
2585
2585
|
// 不生成 UI 目录;仅给已有 Nest 工程增量补服务端排除项。
|
|
2586
2586
|
{ onlyNestjs: true, type: "typescript-exclude", to: "tsconfig.node.json", path: "server/mcp/ui" },
|
|
2587
|
-
{ onlyNestjs: true, type: "file", from: "templates/tsconfig.mcp-ui.json", to: "tsconfig.mcp-ui.json", overwrite: false },
|
|
2588
|
-
{ onlyNestjs: true, type: "add-script", name: "type:check:mcp-ui", command: "node ./scripts/typecheck-mcp-ui.js", overwrite: false },
|
|
2589
|
-
{
|
|
2590
|
-
onlyNestjs: true,
|
|
2591
|
-
type: "patch-script",
|
|
2592
|
-
name: "type:check:mcp-ui",
|
|
2593
|
-
to: "node ./scripts/typecheck-mcp-ui.js",
|
|
2594
|
-
ifEquals: ["node ./scripts/check.cjs --typecheck-mcp-ui", "node ./scripts/lint.js --typecheck-mcp-ui"]
|
|
2595
|
-
},
|
|
2596
|
-
{
|
|
2597
|
-
onlyNestjs: true,
|
|
2598
|
-
type: "patch-script",
|
|
2599
|
-
name: "type:check",
|
|
2600
|
-
to: 'concurrently -n "server,client,mcp-ui" -c "blue,green,magenta" "npm run type:check:server" "npm run type:check:client" "npm run type:check:mcp-ui"',
|
|
2601
|
-
ifEquals: [
|
|
2602
|
-
"node ./scripts/check.cjs --typecheck",
|
|
2603
|
-
"node ./scripts/lint.js --typecheck",
|
|
2604
|
-
'concurrently -n "server,client" -c "blue,green" "npm run type:check:server" "npm run type:check:client"',
|
|
2605
|
-
'concurrently "npm run type:check:server" "npm run type:check:client"',
|
|
2606
|
-
'concurrently "npm run type:check:client" "npm run type:check:server"'
|
|
2607
|
-
]
|
|
2608
|
-
},
|
|
2609
|
-
{
|
|
2610
|
-
onlyNestjs: true,
|
|
2611
|
-
type: "patch-script",
|
|
2612
|
-
name: "eslint",
|
|
2613
|
-
to: "eslint . --quiet",
|
|
2614
|
-
ifEquals: ['eslint . --quiet --ignore-pattern "server/mcp/ui/**"']
|
|
2615
|
-
},
|
|
2616
2587
|
// 9. 把模板版本的 lint 脚本替换为支持 --files 的 runner
|
|
2617
2588
|
// 只识别平台模板生成的 `concurrently ...` 形态,用户真正改写过的脚本保持原样
|
|
2618
2589
|
{
|
|
@@ -2906,7 +2877,7 @@ async function syncRule(rule, pluginRoot, userProjectRoot) {
|
|
|
2906
2877
|
}
|
|
2907
2878
|
if (rule.type === "patch-script") {
|
|
2908
2879
|
const packageJsonPath = path6.join(userProjectRoot, "package.json");
|
|
2909
|
-
patchScript(packageJsonPath, rule.name, rule.to, rule.ifStartsWith
|
|
2880
|
+
patchScript(packageJsonPath, rule.name, rule.to, rule.ifStartsWith);
|
|
2910
2881
|
return;
|
|
2911
2882
|
}
|
|
2912
2883
|
if (rule.type === "add-line") {
|
|
@@ -3046,7 +3017,7 @@ function addScript(packageJsonPath, name, command, overwrite) {
|
|
|
3046
3017
|
fs9.writeFileSync(packageJsonPath, JSON.stringify(pkg2, null, 2) + "\n");
|
|
3047
3018
|
console.log(`[fullstack-cli] \u2713 scripts.${name}`);
|
|
3048
3019
|
}
|
|
3049
|
-
function patchScript(packageJsonPath, name, to, ifStartsWith
|
|
3020
|
+
function patchScript(packageJsonPath, name, to, ifStartsWith) {
|
|
3050
3021
|
if (!fs9.existsSync(packageJsonPath)) {
|
|
3051
3022
|
console.log(`[fullstack-cli] \u25CB package.json (not found)`);
|
|
3052
3023
|
return;
|
|
@@ -3063,7 +3034,7 @@ function patchScript(packageJsonPath, name, to, ifStartsWith, ifEquals) {
|
|
|
3063
3034
|
console.log(`[fullstack-cli] \u25CB scripts.${name} (already patched)`);
|
|
3064
3035
|
return;
|
|
3065
3036
|
}
|
|
3066
|
-
if (!
|
|
3037
|
+
if (!current.startsWith(ifStartsWith)) {
|
|
3067
3038
|
console.warn(
|
|
3068
3039
|
`[fullstack-cli] \u26A0 Skipped patching scripts.${name} because it has been customized`
|
|
3069
3040
|
);
|
package/package.json
CHANGED
|
@@ -210,28 +210,3 @@ else
|
|
|
210
210
|
echo " node_modules: $NODE_MODULES_SIZE"
|
|
211
211
|
echo ""
|
|
212
212
|
fi
|
|
213
|
-
|
|
214
|
-
# 仅成功的全构建生成 backendBuildID;必须在构建、产物准备和依赖裁剪之后。
|
|
215
|
-
# UI/Skill 内容由 SDK 独立计入总 hash;纯前端发布不替换已部署的 backendBuildID。
|
|
216
|
-
if [[ "${only_frontend_change:-false}" != "true" ]]; then
|
|
217
|
-
node - "$ROOT_DIR/dist" <<'NODE'
|
|
218
|
-
const fs = require('node:fs');
|
|
219
|
-
const path = require('node:path');
|
|
220
|
-
const { randomUUID } = require('node:crypto');
|
|
221
|
-
const dist = process.argv[2];
|
|
222
|
-
// 防止 build:server 空跑成功、却没有可交付的服务端入口。
|
|
223
|
-
if (!fs.statSync(path.join(dist, 'server/main.js')).isFile()) {
|
|
224
|
-
throw new Error('Missing server build output: dist/server/main.js');
|
|
225
|
-
}
|
|
226
|
-
// runtime cwd 分别为工程根、dist/(或整包解压到 dist-server/)、旧版 dist/server/。
|
|
227
|
-
// 与 MCP UI/Skill 的交付布局一致:复制产物时保留同一个 UUID,不在启动时生成。
|
|
228
|
-
const files = [
|
|
229
|
-
path.join(dist, 'mcp-build-id'),
|
|
230
|
-
path.join(dist, 'dist/mcp-build-id'),
|
|
231
|
-
path.join(dist, 'server/dist/mcp-build-id'),
|
|
232
|
-
];
|
|
233
|
-
for (const file of files) fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
234
|
-
const buildID = randomUUID();
|
|
235
|
-
for (const file of files) fs.writeFileSync(file, buildID, 'utf8');
|
|
236
|
-
NODE
|
|
237
|
-
fi
|
|
@@ -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,14 +11,14 @@ 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
|
}
|
|
@@ -36,25 +36,18 @@ async function runTasksSerially(taskSpecs) {
|
|
|
36
36
|
return exitCode;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
function normalizeProjectFile(filePath
|
|
39
|
+
function normalizeProjectFile(filePath) {
|
|
40
40
|
const absolutePath = path.isAbsolute(filePath)
|
|
41
41
|
? filePath
|
|
42
42
|
: path.resolve(cwd, filePath);
|
|
43
43
|
|
|
44
44
|
if (!fs.existsSync(absolutePath)) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
console.log(`[lint] Deleted file: ${filePath}; checking affected projects`);
|
|
45
|
+
console.warn(`[lint] Skip missing file: ${filePath}`);
|
|
46
|
+
return null;
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
const relativePath = path.relative(cwd, absolutePath);
|
|
53
|
-
if (
|
|
54
|
-
relativePath === '..' ||
|
|
55
|
-
relativePath.startsWith(`..${path.sep}`) ||
|
|
56
|
-
path.isAbsolute(relativePath)
|
|
57
|
-
) {
|
|
50
|
+
if (relativePath.startsWith('..')) {
|
|
58
51
|
console.warn(`[lint] Skip file outside project: ${filePath}`);
|
|
59
52
|
return null;
|
|
60
53
|
}
|
|
@@ -111,81 +104,7 @@ function canRunStylelint() {
|
|
|
111
104
|
|
|
112
105
|
if (!pkg.scripts || !pkg.scripts.stylelint) return false;
|
|
113
106
|
|
|
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/');
|
|
107
|
+
return STYLELINT_CONFIG_FILES.some(file => fs.existsSync(path.join(cwd, file)));
|
|
189
108
|
}
|
|
190
109
|
|
|
191
110
|
async function runDefaultLint() {
|
|
@@ -194,23 +113,18 @@ async function runDefaultLint() {
|
|
|
194
113
|
[getBinName('npm'), ['run', 'type:check']],
|
|
195
114
|
];
|
|
196
115
|
|
|
197
|
-
taskSpecs.push(...uiTasks({ aggregate: true }));
|
|
198
|
-
|
|
199
116
|
if (canRunStylelint()) {
|
|
200
117
|
taskSpecs.push([getBinName('npm'), ['run', 'stylelint']]);
|
|
201
118
|
} else {
|
|
202
|
-
console.warn(
|
|
203
|
-
'[lint] Skip stylelint: missing scripts.stylelint or stylelint config'
|
|
204
|
-
);
|
|
119
|
+
console.warn('[lint] Skip stylelint: missing scripts.stylelint or stylelint config');
|
|
205
120
|
}
|
|
206
121
|
|
|
207
122
|
process.exit(await runTasksSerially(taskSpecs));
|
|
208
123
|
}
|
|
209
124
|
|
|
210
125
|
async function runSelectiveLint(inputFiles) {
|
|
211
|
-
const hasUiSources = uiSourceFiles().length > 0;
|
|
212
126
|
const normalizedFiles = Array.from(
|
|
213
|
-
new Set(inputFiles.map(
|
|
127
|
+
new Set(inputFiles.map(normalizeProjectFile).filter(Boolean)),
|
|
214
128
|
);
|
|
215
129
|
|
|
216
130
|
if (normalizedFiles.length === 0) {
|
|
@@ -218,40 +132,15 @@ async function runSelectiveLint(inputFiles) {
|
|
|
218
132
|
process.exit(0);
|
|
219
133
|
}
|
|
220
134
|
|
|
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
|
-
);
|
|
135
|
+
const eslintFiles = normalizedFiles.filter(isEslintTarget);
|
|
136
|
+
const stylelintFiles = normalizedFiles.filter(isStylelintTarget);
|
|
230
137
|
const typeCheckFiles = normalizedFiles.filter(isTypeCheckTarget);
|
|
231
138
|
|
|
232
139
|
const clientTypeFiles = [];
|
|
233
140
|
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
141
|
|
|
251
142
|
for (const filePath of typeCheckFiles) {
|
|
252
|
-
if (
|
|
253
|
-
checkUi = true;
|
|
254
|
-
} else if (filePath.startsWith('client/')) {
|
|
143
|
+
if (filePath.startsWith('client/')) {
|
|
255
144
|
clientTypeFiles.push(filePath);
|
|
256
145
|
} else if (filePath.startsWith('server/')) {
|
|
257
146
|
serverTypeFiles.push(filePath);
|
|
@@ -268,14 +157,9 @@ async function runSelectiveLint(inputFiles) {
|
|
|
268
157
|
}
|
|
269
158
|
|
|
270
159
|
if (stylelintFiles.length > 0 && !canRunStylelint()) {
|
|
271
|
-
console.warn(
|
|
272
|
-
'[lint] Skip stylelint: missing scripts.stylelint or stylelint config'
|
|
273
|
-
);
|
|
160
|
+
console.warn('[lint] Skip stylelint: missing scripts.stylelint or stylelint config');
|
|
274
161
|
} else if (stylelintFiles.length > 0) {
|
|
275
|
-
taskSpecs.push([
|
|
276
|
-
getBinName('npx'),
|
|
277
|
-
['stylelint', '--quiet', ...stylelintFiles],
|
|
278
|
-
]);
|
|
162
|
+
taskSpecs.push([getBinName('npx'), ['stylelint', '--quiet', ...stylelintFiles]]);
|
|
279
163
|
}
|
|
280
164
|
|
|
281
165
|
if (clientTypeFiles.length > 0) {
|
|
@@ -286,8 +170,6 @@ async function runSelectiveLint(inputFiles) {
|
|
|
286
170
|
taskSpecs.push([getBinName('npm'), ['run', 'type:check:server']]);
|
|
287
171
|
}
|
|
288
172
|
|
|
289
|
-
if (checkUi) taskSpecs.push(...uiTasks());
|
|
290
|
-
|
|
291
173
|
if (taskSpecs.length === 0) {
|
|
292
174
|
console.log('[lint] No supported files matched for lint');
|
|
293
175
|
process.exit(0);
|
|
@@ -297,16 +179,6 @@ async function runSelectiveLint(inputFiles) {
|
|
|
297
179
|
}
|
|
298
180
|
|
|
299
181
|
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
182
|
const files = parseFilesArg(process.argv.slice(2));
|
|
311
183
|
if (files === null) {
|
|
312
184
|
await runDefaultLint();
|
|
@@ -321,7 +193,7 @@ async function main() {
|
|
|
321
193
|
await runSelectiveLint(files);
|
|
322
194
|
}
|
|
323
195
|
|
|
324
|
-
main().catch(error => {
|
|
196
|
+
main().catch((error) => {
|
|
325
197
|
const message = error instanceof Error ? error.message : String(error);
|
|
326
198
|
console.error(`[lint] Failed to run lint: ${message}`);
|
|
327
199
|
process.exit(1);
|
|
@@ -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
|
-
}
|