@lark-apaas/fullstack-cli 1.1.66 → 1.1.67-alpha.20260915105805

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": "@lark-apaas/fullstack-cli",
3
- "version": "1.1.66",
3
+ "version": "1.1.67-alpha.20260915105805",
4
4
  "description": "CLI tool for fullstack template management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -41,6 +41,7 @@
41
41
  "drizzle-orm": "0.44.6",
42
42
  "es-toolkit": "^1.44.0",
43
43
  "inflection": "^3.0.2",
44
+ "jsonc-parser": "3.2.0",
44
45
  "pinyin-pro": "^3.27.0",
45
46
  "postgres": "^3.4.3",
46
47
  "shadcn": "3.8.2",
@@ -18,7 +18,7 @@ function runCommand(command, args) {
18
18
  shell: false,
19
19
  });
20
20
 
21
- child.on('close', (code) => resolve(code || 0));
21
+ child.on('close', (code) => resolve(code ?? 1));
22
22
  child.on('error', () => resolve(1));
23
23
  });
24
24
  }
@@ -42,8 +42,7 @@ function normalizeProjectFile(filePath) {
42
42
  : path.resolve(cwd, filePath);
43
43
 
44
44
  if (!fs.existsSync(absolutePath)) {
45
- console.warn(`[lint] Skip missing file: ${filePath}`);
46
- return null;
45
+ console.log(`[lint] Deleted file: ${filePath}; checking affected projects`);
47
46
  }
48
47
 
49
48
  const relativePath = path.relative(cwd, absolutePath);
@@ -107,12 +106,85 @@ function canRunStylelint() {
107
106
  return STYLELINT_CONFIG_FILES.some(file => fs.existsSync(path.join(cwd, file)));
108
107
  }
109
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
+
110
179
  async function runDefaultLint() {
111
180
  const taskSpecs = [
112
181
  [getBinName('npm'), ['run', 'eslint']],
113
182
  [getBinName('npm'), ['run', 'type:check']],
114
183
  ];
115
184
 
185
+ // Customized type:check remains untouched; ensure full lint still checks UI.
186
+ taskSpecs.push(...uiTasks({ types: packageScripts()['type:check'] !== UI_TYPECHECK }));
187
+
116
188
  if (canRunStylelint()) {
117
189
  taskSpecs.push([getBinName('npm'), ['run', 'stylelint']]);
118
190
  } else {
@@ -132,15 +204,20 @@ async function runSelectiveLint(inputFiles) {
132
204
  process.exit(0);
133
205
  }
134
206
 
135
- const eslintFiles = normalizedFiles.filter(isEslintTarget);
136
- 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));
137
209
  const typeCheckFiles = normalizedFiles.filter(isTypeCheckTarget);
138
210
 
139
211
  const clientTypeFiles = [];
140
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.'));
141
216
 
142
217
  for (const filePath of typeCheckFiles) {
143
- if (filePath.startsWith('client/')) {
218
+ if (isUiFile(filePath)) {
219
+ checkUi = true;
220
+ } else if (filePath.startsWith('client/')) {
144
221
  clientTypeFiles.push(filePath);
145
222
  } else if (filePath.startsWith('server/')) {
146
223
  serverTypeFiles.push(filePath);
@@ -170,6 +247,8 @@ async function runSelectiveLint(inputFiles) {
170
247
  taskSpecs.push([getBinName('npm'), ['run', 'type:check:server']]);
171
248
  }
172
249
 
250
+ if (checkUi) taskSpecs.push(...uiTasks());
251
+
173
252
  if (taskSpecs.length === 0) {
174
253
  console.log('[lint] No supported files matched for lint');
175
254
  process.exit(0);
@@ -179,6 +258,15 @@ async function runSelectiveLint(inputFiles) {
179
258
  }
180
259
 
181
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 })));
182
270
  const files = parseFilesArg(process.argv.slice(2));
183
271
  if (files === null) {
184
272
  await runDefaultLint();