@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/dist/index.js +397 -311
- package/package.json +2 -1
- package/templates/scripts/lint.js +94 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/fullstack-cli",
|
|
3
|
-
"version": "1.1.
|
|
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
|
|
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.
|
|
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
|
|
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();
|