@ubean/cli 0.5.1 → 0.5.2

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.
Files changed (2) hide show
  1. package/dist/cli.js +116 -21
  2. package/package.json +12 -12
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@ import { A as summarizeBundle, D as compareBundleBaseline, E as createFsOps, O a
2
2
  import { existsSync, mkdirSync, readFileSync, watch } from "node:fs";
3
3
  import { basename, join, relative, resolve } from "pathe";
4
4
  import { extname, normalize, sep } from "node:path";
5
- import { getLogger, setDebugLogging } from "@ubean/shared/logger";
5
+ import { getLogger, setDebugLogging, setMinLevel } from "@ubean/shared/logger";
6
6
  import { defineCommand, runMain } from "citty";
7
7
  import { BUILTIN_MODULES, isBuiltinDisabled, loadUbeanConfig, resolveModules, resolvePrerenderConfig, resolveSsrConfig } from "@ubean/config";
8
8
  import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
@@ -490,6 +490,32 @@ const buildCommand = {
490
490
  //#region src/dev-server/vite-server.ts
491
491
  const logger$8 = getLogger("dev-server");
492
492
  /**
493
+ * 收编 Vite 原生日志(hmr update、依赖预构建提示等)到 ubean 分类闸门:
494
+ * - info 级别(如 `[vite] hmr update /src/x.vue`)默认静默;`logging.lifecycle` 开启时以 `[vite]` 前缀透传
495
+ * - warn/error 始终透传(不可静默)
496
+ */
497
+ function createGatedViteLogger(logging) {
498
+ return {
499
+ info(msg) {
500
+ if (logging.lifecycle) logger$8.info(`[vite] ${msg}`);
501
+ },
502
+ warn(msg) {
503
+ logger$8.warn(msg);
504
+ },
505
+ warnOnce(msg) {
506
+ logger$8.warn(msg);
507
+ },
508
+ error(msg) {
509
+ logger$8.error(msg);
510
+ },
511
+ clearScreen() {},
512
+ hasWarned: false,
513
+ hasErrorLogged() {
514
+ return false;
515
+ }
516
+ };
517
+ }
518
+ /**
493
519
  * Compute the vue-router locale param (e.g. `:locale(zh)?`) from the resolved
494
520
  * i18n routing config, mirroring `localeVueParamFromI18n` (`@ubean/build`).
495
521
  * Returns `''` when i18n is disabled or no prefix is needed.
@@ -760,6 +786,7 @@ async function createViteDevServer(options) {
760
786
  viteServer = await createServer$1({
761
787
  root: cwd,
762
788
  configFile: userViteConfig ?? false,
789
+ customLogger: createGatedViteLogger({ lifecycle: config.logging.lifecycle }),
763
790
  resolve: { dedupe: ["vue", "vue-router"] },
764
791
  server: {
765
792
  middlewareMode: { server: httpServer },
@@ -1100,7 +1127,13 @@ function formatDiagnostics(diagnostics) {
1100
1127
  warnings
1101
1128
  };
1102
1129
  }
1103
- function logDiagnostics(diagnostics) {
1130
+ /**
1131
+ * 输出能力诊断结果。
1132
+ *
1133
+ * `'auto'` 语义(默认):健康时静默 —— "All capability checks passed" 仅在
1134
+ * `verbose` 时输出;告警/错误属于 warn/error 级别,始终输出。
1135
+ */
1136
+ function logDiagnostics(diagnostics, options = {}) {
1104
1137
  const { errors, warnings } = formatDiagnostics(diagnostics);
1105
1138
  if (errors.length > 0) {
1106
1139
  logger$7.error(`Capability errors (${errors.length}):`);
@@ -1110,7 +1143,7 @@ function logDiagnostics(diagnostics) {
1110
1143
  logger$7.warn(`Capability warnings (${warnings.length}):`);
1111
1144
  for (const w of warnings) logger$7.warn(w);
1112
1145
  }
1113
- if (errors.length === 0 && warnings.length === 0) logger$7.info("All capability checks passed");
1146
+ if (errors.length === 0 && warnings.length === 0 && options.verbose) logger$7.info("All capability checks passed");
1114
1147
  }
1115
1148
  //#endregion
1116
1149
  //#region src/dev.ts
@@ -1164,6 +1197,14 @@ const devCommand = {
1164
1197
  description: "Exit if the port is already in use, instead of auto-incrementing",
1165
1198
  default: false
1166
1199
  },
1200
+ verbose: {
1201
+ type: "boolean",
1202
+ description: "Show detailed startup logs (directories, scan, codegen, diagnostics, reload events)"
1203
+ },
1204
+ logRequests: {
1205
+ type: "boolean",
1206
+ description: "Log each request (GET /path 200 12ms). Ignored in ssg/spa modes."
1207
+ },
1167
1208
  open: {
1168
1209
  type: "boolean",
1169
1210
  description: "Open browser on startup"
@@ -1176,27 +1217,47 @@ const devCommand = {
1176
1217
  },
1177
1218
  async run({ args }) {
1178
1219
  const cwd = resolve(args.cwd || process.cwd());
1179
- logger$6.info("Starting ubean dev server...");
1180
1220
  registerBuiltinPresets();
1181
1221
  const config = await loadUbeanConfig(cwd);
1222
+ const logging = config.logging;
1223
+ if (args.verbose) {
1224
+ logging.scan = true;
1225
+ logging.lifecycle = true;
1226
+ logging.diagnostics = true;
1227
+ }
1228
+ if (args.logRequests) {
1229
+ logging.request = true;
1230
+ logging.requestSuppressed = false;
1231
+ }
1232
+ const backendCapable = config.mode === "fullstack" || config.mode === "backend";
1233
+ if (logging.request && !backendCapable) {
1234
+ logging.request = false;
1235
+ logging.requestSuppressed = true;
1236
+ }
1237
+ if (logging.requestSuppressed) logger$6.warn(`Request logging is not applicable in "${config.mode}" mode — skipping.`);
1238
+ setMinLevel(logging.level);
1239
+ const startedAt = Date.now();
1240
+ if (logging.lifecycle) logger$6.info("Starting ubean dev server...");
1182
1241
  const port = Number(args.port) || config.dev.port;
1183
1242
  const host = args.host || config.dev.host;
1184
- logger$6.info(`Root directory: ${config.rootDir}`);
1185
- logger$6.info(`Source directory: ${config.srcDir}`);
1186
1243
  const preset = resolvePresetByName(config.build.preset);
1187
- logger$6.info(`Preset: ${preset.name}`);
1244
+ if (logging.scan) {
1245
+ logger$6.info(`Root directory: ${config.rootDir}`);
1246
+ logger$6.info(`Source directory: ${config.srcDir}`);
1247
+ logger$6.info(`Preset: ${preset.name}`);
1248
+ }
1188
1249
  const capabilities = createCapabilitySet(preset.capabilities || {});
1189
- logger$6.info("Running capability diagnostics...");
1250
+ if (logging.scan) logger$6.info("Running capability diagnostics...");
1190
1251
  const diagnostics = diagnoseCapabilities(preset.name, preset.capabilities || {}, NODE_REQUIREMENTS);
1191
- logDiagnostics(diagnostics);
1252
+ logDiagnostics(diagnostics, { verbose: logging.diagnostics });
1192
1253
  if (!diagnostics.valid) logger$6.warn("Some capability requirements are not met. Dev server may not function correctly.");
1193
- let { app: currentApp, layouts: currentLayouts, scanResult: currentScanResult } = await buildApp(cwd, config);
1254
+ let { app: currentApp, layouts: currentLayouts, scanResult: currentScanResult } = await buildApp(cwd, config, logging);
1194
1255
  let rescanInProgress = false;
1195
1256
  async function rescan() {
1196
1257
  if (rescanInProgress) return;
1197
1258
  rescanInProgress = true;
1198
1259
  try {
1199
- const { app: newApp, layouts: newLayouts, scanResult } = await buildApp(cwd, config);
1260
+ const { app: newApp, layouts: newLayouts, scanResult } = await buildApp(cwd, config, logging);
1200
1261
  currentApp = newApp;
1201
1262
  currentLayouts = newLayouts;
1202
1263
  currentScanResult = scanResult;
@@ -1209,6 +1270,8 @@ const devCommand = {
1209
1270
  rescanInProgress = false;
1210
1271
  }
1211
1272
  }
1273
+ let reloadStartedAt = Date.now();
1274
+ let lastChangedFile = null;
1212
1275
  const runner = await createDevRunner({
1213
1276
  cwd,
1214
1277
  srcDir: config.srcDir,
@@ -1238,8 +1301,9 @@ const devCommand = {
1238
1301
  },
1239
1302
  onListen({ url, networkUrls }) {
1240
1303
  const label = (text) => dim(text);
1304
+ const modeSuffix = dim(` (${config.mode} · ${preset.name})`);
1241
1305
  const lines = [
1242
- `${green(bold("🚀 ubean dev server ready"))}\n`,
1306
+ `${green(bold(`🚀 ubean dev server ready in ${Date.now() - startedAt}ms`))}${modeSuffix}\n`,
1243
1307
  ` → ${label("Local:")} ${cyan(url)}`,
1244
1308
  ...(networkUrls || []).map((n) => ` → ${label("Network:")} ${cyan(n)}`)
1245
1309
  ];
@@ -1249,16 +1313,22 @@ const devCommand = {
1249
1313
  lines.push(` → ${dim("Press Ctrl+C to stop")}`);
1250
1314
  logger$6.info(lines.join("\n"));
1251
1315
  if (hasBackend) generateOpenApiTypesFromServer(url, { outDir: resolve(cwd, ".ubean") }).then((filePath) => {
1252
- if (filePath) logger$6.info(`OpenAPI types generated: ${filePath}`);
1316
+ if (filePath && logging.scan) logger$6.info(`OpenAPI types generated: ${filePath}`);
1253
1317
  }).catch((err) => {
1254
1318
  logger$6.warn(`Failed to generate OpenAPI types: ${err instanceof Error ? err.message : String(err)}`);
1255
1319
  });
1256
1320
  },
1257
1321
  onBeforeReload() {
1258
- logger$6.info("Reloading...");
1322
+ reloadStartedAt = Date.now();
1323
+ if (logging.lifecycle) logger$6.info("Reloading...");
1259
1324
  },
1260
1325
  onAfterReload() {
1261
- logger$6.info("Reloaded");
1326
+ if (logging.lifecycle) logger$6.info("Reloaded");
1327
+ else {
1328
+ const file = lastChangedFile ? dim(` · ${lastChangedFile}`) : "";
1329
+ logger$6.info(dim(`↻ Reloaded in ${Date.now() - reloadStartedAt}ms${file}`));
1330
+ }
1331
+ lastChangedFile = null;
1262
1332
  }
1263
1333
  });
1264
1334
  try {
@@ -1287,13 +1357,14 @@ const devCommand = {
1287
1357
  async onChange(events) {
1288
1358
  const relevantEvents = events.filter((e) => /\.(ts|js|vue|mjs|cjs|json)$/.test(e.relativePath) && !e.relativePath.includes(".bak"));
1289
1359
  if (relevantEvents.length === 0) return;
1290
- logger$6.info(`File change detected: ${relevantEvents[0].relativePath}`);
1360
+ lastChangedFile = relevantEvents[0].relativePath;
1361
+ if (logging.lifecycle) logger$6.info(`File change detected: ${lastChangedFile}`);
1291
1362
  await rescan();
1292
1363
  }
1293
1364
  });
1294
1365
  watcher.start();
1295
1366
  const cleanup = async () => {
1296
- logger$6.info("\nShutting down...");
1367
+ if (logging.lifecycle) logger$6.info("\nShutting down...");
1297
1368
  watcher.stop();
1298
1369
  await runner.stop();
1299
1370
  process.exit(0);
@@ -1302,16 +1373,20 @@ const devCommand = {
1302
1373
  process.on("SIGTERM", cleanup);
1303
1374
  }
1304
1375
  };
1305
- async function buildApp(cwd, config) {
1306
- logger$6.info("Scanning project files...");
1376
+ /** 默认跳过请求日志的内部路径前缀(`/_health`、`/_devtools`、`/_openapi.json`、Vite 内部 `/@id/...` 等)。 */
1377
+ const REQUEST_LOG_INTERNAL_PREFIXES = ["/_", "/@"];
1378
+ /** 请求日志中,超过该毫秒数的请求提升为 warn(慢请求告警)。 */
1379
+ const REQUEST_LOG_SLOW_THRESHOLD = 1e3;
1380
+ async function buildApp(cwd, config, logging) {
1381
+ if (logging.scan) logger$6.info("Scanning project files...");
1307
1382
  const result = await scanProject({
1308
1383
  cwd,
1309
1384
  srcDir: config.srcDir,
1310
1385
  dirs: config.dir,
1311
1386
  ignore: config.scanOptions?.ignore
1312
1387
  });
1313
- logger$6.info(`Found ${result.apiRoutes.length} API routes, ${result.pages.length} pages, ${result.layouts.length} layouts, ${result.middlewares.length} middlewares, ${result.plugins.length} plugins`);
1314
- logger$6.info("Generating type definitions...");
1388
+ if (logging.scan) logger$6.info(`Found ${result.apiRoutes.length} API routes, ${result.pages.length} pages, ${result.layouts.length} layouts, ${result.middlewares.length} middlewares, ${result.plugins.length} plugins`);
1389
+ if (logging.scan) logger$6.info("Generating type definitions...");
1315
1390
  await generateTypes(result, {
1316
1391
  cwd,
1317
1392
  srcDir: config.srcDir,
@@ -1344,9 +1419,29 @@ async function buildApp(cwd, config) {
1344
1419
  cache: config.cache,
1345
1420
  seoConventions: { srcDir: resolve(cwd, config.srcDir) }
1346
1421
  });
1422
+ const requestStartTimes = /* @__PURE__ */ new WeakMap();
1423
+ const isInternalPath = (path) => REQUEST_LOG_INTERNAL_PREFIXES.some((p) => path.startsWith(p));
1347
1424
  app.hooks.hook("request:start", (c) => {
1425
+ requestStartTimes.set(c, Date.now());
1348
1426
  logger$6.debug(`${c.req.method} ${c.req.path}`);
1349
1427
  });
1428
+ app.hooks.hook("request:end", (c, res) => {
1429
+ if (!logging.request) return;
1430
+ const start = requestStartTimes.get(c);
1431
+ const duration = start === void 0 ? 0 : Date.now() - start;
1432
+ const line = `${c.req.method} ${c.req.path} ${res.status} ${duration}ms`;
1433
+ if (res.status >= 500) {
1434
+ logger$6.error(line);
1435
+ return;
1436
+ }
1437
+ if (isInternalPath(c.req.path)) return;
1438
+ if (res.status >= 400 || duration >= REQUEST_LOG_SLOW_THRESHOLD) logger$6.warn(line);
1439
+ else logger$6.info(line);
1440
+ });
1441
+ app.hooks.hook("request:error", (c, err) => {
1442
+ if (!logging.request) return;
1443
+ logger$6.error(`${c.req.method} ${c.req.path} ERROR ${err instanceof Error ? err.message : String(err)}`);
1444
+ });
1350
1445
  return {
1351
1446
  app,
1352
1447
  layouts: result.layouts,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ubean/cli",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "CLI for ubean (ubean-next binary: dev, build, prepare, init, preview, page, env, config, devtools, api/layout/middleware/cron/plugin scaffolds) plus the Vite dev server runner",
5
5
  "bin": {
6
6
  "ubean-next": "./bin/ubean-next.mjs"
@@ -24,15 +24,15 @@
24
24
  }
25
25
  },
26
26
  "dependencies": {
27
- "@ubean/app": "0.5.1",
28
- "@ubean/build": "0.5.1",
29
- "@ubean/client": "0.5.1",
30
- "@ubean/config": "0.5.1",
31
- "@ubean/i18n": "0.5.1",
32
- "@ubean/islands": "0.5.1",
33
- "@ubean/preset": "0.5.1",
34
- "@ubean/scan": "0.5.1",
35
- "@ubean/shared": "0.5.1",
27
+ "@ubean/app": "0.5.2",
28
+ "@ubean/build": "0.5.2",
29
+ "@ubean/client": "0.5.2",
30
+ "@ubean/config": "0.5.2",
31
+ "@ubean/i18n": "0.5.2",
32
+ "@ubean/islands": "0.5.2",
33
+ "@ubean/preset": "0.5.2",
34
+ "@ubean/scan": "0.5.2",
35
+ "@ubean/shared": "0.5.2",
36
36
  "citty": "^0.2.2",
37
37
  "kolorist": "^1.8.0",
38
38
  "pathe": "^2.0.3"
@@ -45,8 +45,8 @@
45
45
  "vite-plus": "0.3.1"
46
46
  },
47
47
  "peerDependencies": {
48
- "@ubean/content": "0.5.1",
49
- "@ubean/devtools": "0.5.1",
48
+ "@ubean/content": "0.5.2",
49
+ "@ubean/devtools": "0.5.2",
50
50
  "@vitejs/plugin-vue": "^6.0.8",
51
51
  "vite": "npm:@voidzero-dev/vite-plus-core@0.3.1"
52
52
  },