@ubean/cli 0.5.0 → 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 +130 -27
  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";
@@ -22,7 +22,7 @@ import { ubeanPlugin } from "@ubean/build/vite";
22
22
  import { VUE_PLUGIN_INCLUDE, ubeanVite } from "@ubean/build/vue";
23
23
  import { getVueLocaleParam, toVueRouterLocalePath } from "@ubean/i18n";
24
24
  import { ubeanIslandsPlugin } from "@ubean/islands/vite";
25
- import { findAvailablePort, findUserViteConfig, waitForPort } from "@ubean/shared/node";
25
+ import { findAvailablePort, findUserViteConfig, getLanAddresses, isLoopbackHost, isWildcardHost, waitForPort } from "@ubean/shared/node";
26
26
  import { execSync, spawn } from "node:child_process";
27
27
  //#region src/config.ts
28
28
  const logger$11 = getLogger("cli");
@@ -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 },
@@ -867,7 +894,9 @@ async function createViteDevServer(options) {
867
894
  app.resetInit();
868
895
  }
869
896
  enhanceAppWithVite(currentApp, currentLayouts);
870
- const getUrl = () => `http://${host}:${actualPort}`;
897
+ const displayHost = isWildcardHost(host) ? "localhost" : host;
898
+ const getNetworkUrls = () => isLoopbackHost(host) ? [] : getLanAddresses().map((address) => `http://${address}:${actualPort}`);
899
+ const getUrl = () => `http://${displayHost}:${actualPort}`;
871
900
  return {
872
901
  get port() {
873
902
  return actualPort;
@@ -902,7 +931,8 @@ async function createViteDevServer(options) {
902
931
  options.onListen?.({
903
932
  port: actualPort,
904
933
  host,
905
- url: getUrl()
934
+ url: getUrl(),
935
+ networkUrls: getNetworkUrls()
906
936
  });
907
937
  },
908
938
  async stop() {
@@ -969,12 +999,13 @@ var ViteNodeDevRunner = class {
969
999
  app: this.currentApp,
970
1000
  layouts: this.currentLayouts,
971
1001
  devtools: this.options.devtools,
972
- onListen: ({ port, host, url }) => {
1002
+ onListen: ({ port, host, url, networkUrls }) => {
973
1003
  this._port = port;
974
1004
  this.options.onListen?.({
975
1005
  port,
976
1006
  host,
977
- url
1007
+ url,
1008
+ networkUrls
978
1009
  });
979
1010
  }
980
1011
  });
@@ -1096,7 +1127,13 @@ function formatDiagnostics(diagnostics) {
1096
1127
  warnings
1097
1128
  };
1098
1129
  }
1099
- function logDiagnostics(diagnostics) {
1130
+ /**
1131
+ * 输出能力诊断结果。
1132
+ *
1133
+ * `'auto'` 语义(默认):健康时静默 —— "All capability checks passed" 仅在
1134
+ * `verbose` 时输出;告警/错误属于 warn/error 级别,始终输出。
1135
+ */
1136
+ function logDiagnostics(diagnostics, options = {}) {
1100
1137
  const { errors, warnings } = formatDiagnostics(diagnostics);
1101
1138
  if (errors.length > 0) {
1102
1139
  logger$7.error(`Capability errors (${errors.length}):`);
@@ -1106,7 +1143,7 @@ function logDiagnostics(diagnostics) {
1106
1143
  logger$7.warn(`Capability warnings (${warnings.length}):`);
1107
1144
  for (const w of warnings) logger$7.warn(w);
1108
1145
  }
1109
- 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");
1110
1147
  }
1111
1148
  //#endregion
1112
1149
  //#region src/dev.ts
@@ -1160,6 +1197,14 @@ const devCommand = {
1160
1197
  description: "Exit if the port is already in use, instead of auto-incrementing",
1161
1198
  default: false
1162
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
+ },
1163
1208
  open: {
1164
1209
  type: "boolean",
1165
1210
  description: "Open browser on startup"
@@ -1172,27 +1217,47 @@ const devCommand = {
1172
1217
  },
1173
1218
  async run({ args }) {
1174
1219
  const cwd = resolve(args.cwd || process.cwd());
1175
- logger$6.info("Starting ubean dev server...");
1176
1220
  registerBuiltinPresets();
1177
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...");
1178
1241
  const port = Number(args.port) || config.dev.port;
1179
1242
  const host = args.host || config.dev.host;
1180
- logger$6.info(`Root directory: ${config.rootDir}`);
1181
- logger$6.info(`Source directory: ${config.srcDir}`);
1182
1243
  const preset = resolvePresetByName(config.build.preset);
1183
- 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
+ }
1184
1249
  const capabilities = createCapabilitySet(preset.capabilities || {});
1185
- logger$6.info("Running capability diagnostics...");
1250
+ if (logging.scan) logger$6.info("Running capability diagnostics...");
1186
1251
  const diagnostics = diagnoseCapabilities(preset.name, preset.capabilities || {}, NODE_REQUIREMENTS);
1187
- logDiagnostics(diagnostics);
1252
+ logDiagnostics(diagnostics, { verbose: logging.diagnostics });
1188
1253
  if (!diagnostics.valid) logger$6.warn("Some capability requirements are not met. Dev server may not function correctly.");
1189
- let { app: currentApp, layouts: currentLayouts, scanResult: currentScanResult } = await buildApp(cwd, config);
1254
+ let { app: currentApp, layouts: currentLayouts, scanResult: currentScanResult } = await buildApp(cwd, config, logging);
1190
1255
  let rescanInProgress = false;
1191
1256
  async function rescan() {
1192
1257
  if (rescanInProgress) return;
1193
1258
  rescanInProgress = true;
1194
1259
  try {
1195
- const { app: newApp, layouts: newLayouts, scanResult } = await buildApp(cwd, config);
1260
+ const { app: newApp, layouts: newLayouts, scanResult } = await buildApp(cwd, config, logging);
1196
1261
  currentApp = newApp;
1197
1262
  currentLayouts = newLayouts;
1198
1263
  currentScanResult = scanResult;
@@ -1205,6 +1270,8 @@ const devCommand = {
1205
1270
  rescanInProgress = false;
1206
1271
  }
1207
1272
  }
1273
+ let reloadStartedAt = Date.now();
1274
+ let lastChangedFile = null;
1208
1275
  const runner = await createDevRunner({
1209
1276
  cwd,
1210
1277
  srcDir: config.srcDir,
@@ -1232,25 +1299,36 @@ const devCommand = {
1232
1299
  getCustomTabs: () => getCustomTabs(),
1233
1300
  triggerRescan: () => rescan()
1234
1301
  },
1235
- onListen({ url }) {
1302
+ onListen({ url, networkUrls }) {
1236
1303
  const label = (text) => dim(text);
1237
- const lines = [`${green(bold("🚀 ubean dev server ready"))}\n`, ` → ${label("Local:")} ${cyan(url)}`];
1304
+ const modeSuffix = dim(` (${config.mode} · ${preset.name})`);
1305
+ const lines = [
1306
+ `${green(bold(`🚀 ubean dev server ready in ${Date.now() - startedAt}ms`))}${modeSuffix}\n`,
1307
+ ` → ${label("Local:")} ${cyan(url)}`,
1308
+ ...(networkUrls || []).map((n) => ` → ${label("Network:")} ${cyan(n)}`)
1309
+ ];
1238
1310
  const hasBackend = config.mode !== "ssg" && config.mode !== "spa";
1239
1311
  if (hasBackend) lines.push(` → ${label("Scalar UI:")} ${cyan(`${url}/_scalar`)}`, ` → ${label("OpenAPI:")} ${cyan(`${url}/_openapi.json`)}`);
1240
1312
  if (config.devtools.enabled) lines.push(` → ${label("DevTools:")} ${cyan(`${url}${config.devtools.route}`)}`);
1241
1313
  lines.push(` → ${dim("Press Ctrl+C to stop")}`);
1242
1314
  logger$6.info(lines.join("\n"));
1243
1315
  if (hasBackend) generateOpenApiTypesFromServer(url, { outDir: resolve(cwd, ".ubean") }).then((filePath) => {
1244
- if (filePath) logger$6.info(`OpenAPI types generated: ${filePath}`);
1316
+ if (filePath && logging.scan) logger$6.info(`OpenAPI types generated: ${filePath}`);
1245
1317
  }).catch((err) => {
1246
1318
  logger$6.warn(`Failed to generate OpenAPI types: ${err instanceof Error ? err.message : String(err)}`);
1247
1319
  });
1248
1320
  },
1249
1321
  onBeforeReload() {
1250
- logger$6.info("Reloading...");
1322
+ reloadStartedAt = Date.now();
1323
+ if (logging.lifecycle) logger$6.info("Reloading...");
1251
1324
  },
1252
1325
  onAfterReload() {
1253
- 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;
1254
1332
  }
1255
1333
  });
1256
1334
  try {
@@ -1279,13 +1357,14 @@ const devCommand = {
1279
1357
  async onChange(events) {
1280
1358
  const relevantEvents = events.filter((e) => /\.(ts|js|vue|mjs|cjs|json)$/.test(e.relativePath) && !e.relativePath.includes(".bak"));
1281
1359
  if (relevantEvents.length === 0) return;
1282
- 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}`);
1283
1362
  await rescan();
1284
1363
  }
1285
1364
  });
1286
1365
  watcher.start();
1287
1366
  const cleanup = async () => {
1288
- logger$6.info("\nShutting down...");
1367
+ if (logging.lifecycle) logger$6.info("\nShutting down...");
1289
1368
  watcher.stop();
1290
1369
  await runner.stop();
1291
1370
  process.exit(0);
@@ -1294,16 +1373,20 @@ const devCommand = {
1294
1373
  process.on("SIGTERM", cleanup);
1295
1374
  }
1296
1375
  };
1297
- async function buildApp(cwd, config) {
1298
- 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...");
1299
1382
  const result = await scanProject({
1300
1383
  cwd,
1301
1384
  srcDir: config.srcDir,
1302
1385
  dirs: config.dir,
1303
1386
  ignore: config.scanOptions?.ignore
1304
1387
  });
1305
- 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`);
1306
- 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...");
1307
1390
  await generateTypes(result, {
1308
1391
  cwd,
1309
1392
  srcDir: config.srcDir,
@@ -1336,9 +1419,29 @@ async function buildApp(cwd, config) {
1336
1419
  cache: config.cache,
1337
1420
  seoConventions: { srcDir: resolve(cwd, config.srcDir) }
1338
1421
  });
1422
+ const requestStartTimes = /* @__PURE__ */ new WeakMap();
1423
+ const isInternalPath = (path) => REQUEST_LOG_INTERNAL_PREFIXES.some((p) => path.startsWith(p));
1339
1424
  app.hooks.hook("request:start", (c) => {
1425
+ requestStartTimes.set(c, Date.now());
1340
1426
  logger$6.debug(`${c.req.method} ${c.req.path}`);
1341
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
+ });
1342
1445
  return {
1343
1446
  app,
1344
1447
  layouts: result.layouts,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ubean/cli",
3
- "version": "0.5.0",
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.0",
28
- "@ubean/build": "0.5.0",
29
- "@ubean/client": "0.5.0",
30
- "@ubean/config": "0.5.0",
31
- "@ubean/i18n": "0.5.0",
32
- "@ubean/islands": "0.5.0",
33
- "@ubean/preset": "0.5.0",
34
- "@ubean/scan": "0.5.0",
35
- "@ubean/shared": "0.5.0",
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.0",
49
- "@ubean/devtools": "0.5.0",
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
  },