@lark-apaas/coding-template-nestjs-react-fullstack 0.1.40-alpha.20260915093425 → 0.1.40-alpha.20260915110512
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 +1 -1
- package/template/package-lock.json +12 -12
- package/template/package.json +6 -4
- package/template/scripts/lint.js +120 -18
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"name": "fullstack-nestjs-template",
|
|
9
9
|
"version": "2.3.0",
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@lark-apaas/fullstack-nestjs-core": "1.1.62-alpha.
|
|
11
|
+
"@lark-apaas/fullstack-nestjs-core": "1.1.62-alpha.20260915105802",
|
|
12
12
|
"@nestjs/axios": "^4.0.1",
|
|
13
13
|
"@nestjs/cache-manager": "^3.1.3",
|
|
14
14
|
"@nestjs/common": "^10.4.20",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@hookform/resolvers": "^5.2.2",
|
|
35
35
|
"@lark-apaas/client-toolkit": "^1.2.70",
|
|
36
|
-
"@lark-apaas/coding-preset-vite-react": "1.0.23-alpha.
|
|
36
|
+
"@lark-apaas/coding-preset-vite-react": "1.0.23-alpha.20260915105803",
|
|
37
37
|
"@lark-apaas/fullstack-presets": "^1.1.0",
|
|
38
38
|
"@nestjs/cli": "^10.4.9",
|
|
39
39
|
"@radix-ui/react-accordion": "^1.2.12",
|
|
@@ -1634,9 +1634,9 @@
|
|
|
1634
1634
|
"license": "MIT"
|
|
1635
1635
|
},
|
|
1636
1636
|
"node_modules/@lark-apaas/coding-preset-vite-react": {
|
|
1637
|
-
"version": "1.0.23-alpha.
|
|
1638
|
-
"resolved": "https://registry.npmmirror.com/@lark-apaas/coding-preset-vite-react/-/coding-preset-vite-react-1.0.23-alpha.
|
|
1639
|
-
"integrity": "sha512-
|
|
1637
|
+
"version": "1.0.23-alpha.20260915105803",
|
|
1638
|
+
"resolved": "https://registry.npmmirror.com/@lark-apaas/coding-preset-vite-react/-/coding-preset-vite-react-1.0.23-alpha.20260915105803.tgz",
|
|
1639
|
+
"integrity": "sha512-GwUV/4DHWibTx0GrhflevI2kWC+4UqjtHucAwHWJpvD7rd8Q+AdQEIAvYVj5nKZ6QuCc8yQEAMKSwvREDTwcEg==",
|
|
1640
1640
|
"dev": true,
|
|
1641
1641
|
"license": "MIT",
|
|
1642
1642
|
"dependencies": {
|
|
@@ -1697,9 +1697,9 @@
|
|
|
1697
1697
|
}
|
|
1698
1698
|
},
|
|
1699
1699
|
"node_modules/@lark-apaas/fullstack-nestjs-core": {
|
|
1700
|
-
"version": "1.1.62-alpha.
|
|
1701
|
-
"resolved": "https://registry.npmmirror.com/@lark-apaas/fullstack-nestjs-core/-/fullstack-nestjs-core-1.1.62-alpha.
|
|
1702
|
-
"integrity": "sha512-
|
|
1700
|
+
"version": "1.1.62-alpha.20260915105802",
|
|
1701
|
+
"resolved": "https://registry.npmmirror.com/@lark-apaas/fullstack-nestjs-core/-/fullstack-nestjs-core-1.1.62-alpha.20260915105802.tgz",
|
|
1702
|
+
"integrity": "sha512-7eJM0cJ0jqjhKCslzyl5SX6l7RE0CRCWXWW4I6u5SZN0NDogNcYU11uWjXbscZnqIsLwNiQj+tOnk5iKeSkSYA==",
|
|
1703
1703
|
"license": "MIT",
|
|
1704
1704
|
"dependencies": {
|
|
1705
1705
|
"@lark-apaas/cache-service": "^0.1.1",
|
|
@@ -1712,7 +1712,7 @@
|
|
|
1712
1712
|
"@lark-apaas/nestjs-datapaas": "^1.0.21",
|
|
1713
1713
|
"@lark-apaas/nestjs-http-forwarder": "^0.1.3",
|
|
1714
1714
|
"@lark-apaas/nestjs-logger": "^1.0.17",
|
|
1715
|
-
"@lark-apaas/nestjs-mcp": "0.1.1-alpha.
|
|
1715
|
+
"@lark-apaas/nestjs-mcp": "0.1.1-alpha.20260915105801",
|
|
1716
1716
|
"@lark-apaas/nestjs-observable": "^0.0.12",
|
|
1717
1717
|
"@lark-apaas/nestjs-openapi-devtools": "^1.0.10",
|
|
1718
1718
|
"@lark-apaas/nestjs-trigger": "^0.0.3",
|
|
@@ -1945,9 +1945,9 @@
|
|
|
1945
1945
|
}
|
|
1946
1946
|
},
|
|
1947
1947
|
"node_modules/@lark-apaas/nestjs-mcp": {
|
|
1948
|
-
"version": "0.1.1-alpha.
|
|
1949
|
-
"resolved": "https://registry.npmmirror.com/@lark-apaas/nestjs-mcp/-/nestjs-mcp-0.1.1-alpha.
|
|
1950
|
-
"integrity": "sha512-
|
|
1948
|
+
"version": "0.1.1-alpha.20260915105801",
|
|
1949
|
+
"resolved": "https://registry.npmmirror.com/@lark-apaas/nestjs-mcp/-/nestjs-mcp-0.1.1-alpha.20260915105801.tgz",
|
|
1950
|
+
"integrity": "sha512-VjW7VlIb24fFktFtzVxySetClX0yD0l5Eixx80LtnsdyKaOKWRW3GCZaVLb0Kn6k5cAao8kF/bImPp55u47Olg==",
|
|
1951
1951
|
"license": "MIT",
|
|
1952
1952
|
"dependencies": {
|
|
1953
1953
|
"@modelcontextprotocol/ext-apps": "^1.7.5",
|
package/template/package.json
CHANGED
|
@@ -20,15 +20,17 @@
|
|
|
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": "
|
|
23
|
+
"type:check": "node ./scripts/lint.js --typecheck",
|
|
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"
|
|
28
|
+
"upgrade": "npx -y @lark-apaas/fullstack-cli sync --disable-gen-openapi",
|
|
29
|
+
"type:check:mcp-ui": "node ./scripts/lint.js --typecheck-mcp-ui",
|
|
30
|
+
"eslint:mcp-ui": "node ./scripts/lint.js --eslint-mcp-ui"
|
|
29
31
|
},
|
|
30
32
|
"dependencies": {
|
|
31
|
-
"@lark-apaas/fullstack-nestjs-core": "1.1.62-alpha.
|
|
33
|
+
"@lark-apaas/fullstack-nestjs-core": "1.1.62-alpha.20260915105802",
|
|
32
34
|
"@nestjs/axios": "^4.0.1",
|
|
33
35
|
"@nestjs/cache-manager": "^3.1.3",
|
|
34
36
|
"@nestjs/common": "^10.4.20",
|
|
@@ -53,7 +55,7 @@
|
|
|
53
55
|
"devDependencies": {
|
|
54
56
|
"@hookform/resolvers": "^5.2.2",
|
|
55
57
|
"@lark-apaas/client-toolkit": "^1.2.70",
|
|
56
|
-
"@lark-apaas/coding-preset-vite-react": "1.0.23-alpha.
|
|
58
|
+
"@lark-apaas/coding-preset-vite-react": "1.0.23-alpha.20260915105803",
|
|
57
59
|
"@lark-apaas/fullstack-presets": "^1.1.0",
|
|
58
60
|
"@nestjs/cli": "^10.4.9",
|
|
59
61
|
"@radix-ui/react-accordion": "^1.2.12",
|
package/template/scripts/lint.js
CHANGED
|
@@ -18,19 +18,31 @@ function runCommand(command, args) {
|
|
|
18
18
|
shell: false,
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
child.on('close', (code) => resolve(code
|
|
21
|
+
child.on('close', (code) => resolve(code ?? 1));
|
|
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
|
+
|
|
26
39
|
function normalizeProjectFile(filePath) {
|
|
27
40
|
const absolutePath = path.isAbsolute(filePath)
|
|
28
41
|
? filePath
|
|
29
42
|
: path.resolve(cwd, filePath);
|
|
30
43
|
|
|
31
44
|
if (!fs.existsSync(absolutePath)) {
|
|
32
|
-
console.
|
|
33
|
-
return null;
|
|
45
|
+
console.log(`[lint] Deleted file: ${filePath}; checking affected projects`);
|
|
34
46
|
}
|
|
35
47
|
|
|
36
48
|
const relativePath = path.relative(cwd, absolutePath);
|
|
@@ -94,17 +106,92 @@ function canRunStylelint() {
|
|
|
94
106
|
return STYLELINT_CONFIG_FILES.some(file => fs.existsSync(path.join(cwd, file)));
|
|
95
107
|
}
|
|
96
108
|
|
|
109
|
+
const UI_DIR = 'server/mcp/ui';
|
|
110
|
+
const UI_TYPECHECK = 'node ./scripts/lint.js --typecheck';
|
|
111
|
+
|
|
112
|
+
function isUiFile(file) {
|
|
113
|
+
return file.startsWith(`${UI_DIR}/`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function uiSourceFiles() {
|
|
117
|
+
const files = [];
|
|
118
|
+
function visit(relative) {
|
|
119
|
+
const absolute = path.join(cwd, relative);
|
|
120
|
+
if (!fs.existsSync(absolute)) return;
|
|
121
|
+
if (fs.lstatSync(absolute).isSymbolicLink()) {
|
|
122
|
+
throw new Error(`MCP UI 检查不支持符号链接: ${relative}`);
|
|
123
|
+
}
|
|
124
|
+
for (const entry of fs.readdirSync(absolute, { withFileTypes: true })) {
|
|
125
|
+
if (['node_modules', 'dist', '.git'].includes(entry.name)) continue;
|
|
126
|
+
const file = `${relative}/${entry.name}`;
|
|
127
|
+
if (entry.isSymbolicLink()) throw new Error(`MCP UI 检查不支持符号链接: ${file}`);
|
|
128
|
+
if (entry.isDirectory()) visit(file);
|
|
129
|
+
else if (isEslintTarget(file) && !/\.d\.(?:ts|mts|cts)$/.test(file)) files.push(file);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
visit(UI_DIR);
|
|
133
|
+
return files.sort();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function uiTasks({ types = true, eslint = true } = {}) {
|
|
137
|
+
const sources = uiSourceFiles();
|
|
138
|
+
if (!sources.length) return [];
|
|
139
|
+
const tasks = [];
|
|
140
|
+
if (types) {
|
|
141
|
+
const config = `${UI_DIR}/tsconfig.json`;
|
|
142
|
+
if (!fs.existsSync(path.join(cwd, config))) {
|
|
143
|
+
throw new Error(`MCP UI 缺少 ${config},请按 mcp-guide 补齐独立浏览器 TypeScript 配置。`);
|
|
144
|
+
}
|
|
145
|
+
const ts = require('typescript');
|
|
146
|
+
const parsed = ts.getParsedCommandLineOfConfigFile(path.join(cwd, config), {}, {
|
|
147
|
+
...ts.sys,
|
|
148
|
+
onUnRecoverableConfigFileDiagnostic: d => { throw new Error(ts.flattenDiagnosticMessageText(d.messageText, '\n')); },
|
|
149
|
+
});
|
|
150
|
+
if (!parsed || parsed.errors.length) throw new Error(`MCP UI TypeScript 配置无效: ${config}`);
|
|
151
|
+
const included = new Set(parsed.fileNames.map(file => path.resolve(file)));
|
|
152
|
+
const missing = sources.filter(file => !included.has(path.resolve(cwd, file)));
|
|
153
|
+
if (missing.length) throw new Error(`MCP UI TypeScript 配置未覆盖源码: ${missing.join(', ')};请检查 include/exclude,JS 源码需开启 allowJs。`);
|
|
154
|
+
tasks.push([getBinName('npx'), ['--no-install', 'tsc', '--noEmit', '-p', config]]);
|
|
155
|
+
}
|
|
156
|
+
if (eslint) {
|
|
157
|
+
const config = ['eslint.mcp-ui.config.js', 'eslint.mcp-ui.config.cjs', 'eslint.mcp-ui.config.mjs']
|
|
158
|
+
.find(file => fs.existsSync(path.join(cwd, file)));
|
|
159
|
+
if (!config) throw new Error('MCP UI 缺少独立 ESLint 配置,请按 mcp-guide 创建 eslint.mcp-ui.config.cjs。');
|
|
160
|
+
tasks.push([getBinName('npx'), ['--no-install', 'eslint', '--config', config, '--quiet', '--max-warnings', '0', ...sources]]);
|
|
161
|
+
}
|
|
162
|
+
return tasks;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function packageScripts() {
|
|
166
|
+
return JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8')).scripts || {};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function runTypeCheck() {
|
|
170
|
+
const scripts = packageScripts();
|
|
171
|
+
const tasks = ['type:check:server', 'type:check:client'].map(name => {
|
|
172
|
+
if (!scripts[name]) throw new Error(`缺少 scripts.${name},请保留应用原有类型检查命令。`);
|
|
173
|
+
return [getBinName('npm'), ['run', name]];
|
|
174
|
+
});
|
|
175
|
+
tasks.push(...uiTasks({ eslint: false }));
|
|
176
|
+
process.exit(await runTasksSerially(tasks));
|
|
177
|
+
}
|
|
178
|
+
|
|
97
179
|
async function runDefaultLint() {
|
|
98
|
-
const
|
|
180
|
+
const taskSpecs = [
|
|
181
|
+
[getBinName('npm'), ['run', 'eslint']],
|
|
182
|
+
[getBinName('npm'), ['run', 'type:check']],
|
|
183
|
+
];
|
|
184
|
+
|
|
185
|
+
// Customized type:check remains untouched; ensure full lint still checks UI.
|
|
186
|
+
taskSpecs.push(...uiTasks({ types: packageScripts()['type:check'] !== UI_TYPECHECK }));
|
|
99
187
|
|
|
100
188
|
if (canRunStylelint()) {
|
|
101
|
-
|
|
189
|
+
taskSpecs.push([getBinName('npm'), ['run', 'stylelint']]);
|
|
102
190
|
} else {
|
|
103
191
|
console.warn('[lint] Skip stylelint: missing scripts.stylelint or stylelint config');
|
|
104
192
|
}
|
|
105
193
|
|
|
106
|
-
|
|
107
|
-
process.exit(code);
|
|
194
|
+
process.exit(await runTasksSerially(taskSpecs));
|
|
108
195
|
}
|
|
109
196
|
|
|
110
197
|
async function runSelectiveLint(inputFiles) {
|
|
@@ -117,15 +204,20 @@ async function runSelectiveLint(inputFiles) {
|
|
|
117
204
|
process.exit(0);
|
|
118
205
|
}
|
|
119
206
|
|
|
120
|
-
const eslintFiles = normalizedFiles.filter(isEslintTarget);
|
|
121
|
-
const stylelintFiles = normalizedFiles.filter(isStylelintTarget);
|
|
207
|
+
const eslintFiles = normalizedFiles.filter(file => fs.existsSync(path.join(cwd, file)) && isEslintTarget(file) && !isUiFile(file) && !file.startsWith('eslint.mcp-ui.config.'));
|
|
208
|
+
const stylelintFiles = normalizedFiles.filter(file => fs.existsSync(path.join(cwd, file)) && isStylelintTarget(file));
|
|
122
209
|
const typeCheckFiles = normalizedFiles.filter(isTypeCheckTarget);
|
|
123
210
|
|
|
124
211
|
const clientTypeFiles = [];
|
|
125
212
|
const serverTypeFiles = [];
|
|
213
|
+
const rootConfigChanged = normalizedFiles.some(file => /^tsconfig(?:\.[^/]*)?\.json$/.test(file));
|
|
214
|
+
if (rootConfigChanged) { clientTypeFiles.push('config'); serverTypeFiles.push('config'); }
|
|
215
|
+
let checkUi = rootConfigChanged || normalizedFiles.some(file => isUiFile(file) || file.startsWith('shared/') || file.startsWith('eslint.mcp-ui.config.'));
|
|
126
216
|
|
|
127
217
|
for (const filePath of typeCheckFiles) {
|
|
128
|
-
if (filePath
|
|
218
|
+
if (isUiFile(filePath)) {
|
|
219
|
+
checkUi = true;
|
|
220
|
+
} else if (filePath.startsWith('client/')) {
|
|
129
221
|
clientTypeFiles.push(filePath);
|
|
130
222
|
} else if (filePath.startsWith('server/')) {
|
|
131
223
|
serverTypeFiles.push(filePath);
|
|
@@ -135,36 +227,46 @@ async function runSelectiveLint(inputFiles) {
|
|
|
135
227
|
}
|
|
136
228
|
}
|
|
137
229
|
|
|
138
|
-
const
|
|
230
|
+
const taskSpecs = [];
|
|
139
231
|
|
|
140
232
|
if (eslintFiles.length > 0) {
|
|
141
|
-
|
|
233
|
+
taskSpecs.push([getBinName('npx'), ['eslint', '--quiet', ...eslintFiles]]);
|
|
142
234
|
}
|
|
143
235
|
|
|
144
236
|
if (stylelintFiles.length > 0 && !canRunStylelint()) {
|
|
145
237
|
console.warn('[lint] Skip stylelint: missing scripts.stylelint or stylelint config');
|
|
146
238
|
} else if (stylelintFiles.length > 0) {
|
|
147
|
-
|
|
239
|
+
taskSpecs.push([getBinName('npx'), ['stylelint', '--quiet', ...stylelintFiles]]);
|
|
148
240
|
}
|
|
149
241
|
|
|
150
242
|
if (clientTypeFiles.length > 0) {
|
|
151
|
-
|
|
243
|
+
taskSpecs.push([getBinName('npm'), ['run', 'type:check:client']]);
|
|
152
244
|
}
|
|
153
245
|
|
|
154
246
|
if (serverTypeFiles.length > 0) {
|
|
155
|
-
|
|
247
|
+
taskSpecs.push([getBinName('npm'), ['run', 'type:check:server']]);
|
|
156
248
|
}
|
|
157
249
|
|
|
158
|
-
if (
|
|
250
|
+
if (checkUi) taskSpecs.push(...uiTasks());
|
|
251
|
+
|
|
252
|
+
if (taskSpecs.length === 0) {
|
|
159
253
|
console.log('[lint] No supported files matched for lint');
|
|
160
254
|
process.exit(0);
|
|
161
255
|
}
|
|
162
256
|
|
|
163
|
-
|
|
164
|
-
process.exit(results.some(code => code !== 0) ? 1 : 0);
|
|
257
|
+
process.exit(await runTasksSerially(taskSpecs));
|
|
165
258
|
}
|
|
166
259
|
|
|
167
260
|
async function main() {
|
|
261
|
+
const args = process.argv.slice(2);
|
|
262
|
+
const modes = ['--typecheck', '--typecheck-mcp-ui', '--eslint-mcp-ui'];
|
|
263
|
+
if (args.length && !(args[0] === '--files' && args.length > 1) && !(args.length === 1 && modes.includes(args[0]))) {
|
|
264
|
+
throw new Error('用法: lint.js [--files <paths...> | --typecheck | --typecheck-mcp-ui | --eslint-mcp-ui]');
|
|
265
|
+
}
|
|
266
|
+
if (args[0] === '--files' && args.slice(1).some(arg => arg.startsWith('--'))) throw new Error('--files 不可与其他检查模式混用');
|
|
267
|
+
if (args.includes('--typecheck')) return runTypeCheck();
|
|
268
|
+
if (args.includes('--typecheck-mcp-ui')) return process.exit(await runTasksSerially(uiTasks({ eslint: false })));
|
|
269
|
+
if (args.includes('--eslint-mcp-ui')) return process.exit(await runTasksSerially(uiTasks({ types: false })));
|
|
168
270
|
const files = parseFilesArg(process.argv.slice(2));
|
|
169
271
|
if (files === null) {
|
|
170
272
|
await runDefaultLint();
|