prev-cli 0.24.4 → 0.24.5

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/cli.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { parseArgs } from "util";
5
- import path10 from "path";
6
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, writeFileSync as writeFileSync4, rmSync as rmSync3, readFileSync as readFileSync5 } from "fs";
5
+ import path11 from "path";
6
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5, rmSync as rmSync3, readFileSync as readFileSync5 } from "fs";
7
7
  import { fileURLToPath as fileURLToPath3 } from "url";
8
8
 
9
9
  // src/vite/start.ts
@@ -15,7 +15,7 @@ import react from "@vitejs/plugin-react";
15
15
  import mdx from "@mdx-js/rollup";
16
16
  import remarkGfm from "remark-gfm";
17
17
  import rehypeHighlight from "rehype-highlight";
18
- import path8 from "path";
18
+ import path9 from "path";
19
19
  import { fileURLToPath as fileURLToPath2 } from "url";
20
20
  import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
21
21
 
@@ -697,6 +697,56 @@ function createConfigPlugin(config) {
697
697
  };
698
698
  }
699
699
 
700
+ // src/vite/plugins/debug-plugin.ts
701
+ function debugPlugin(collector) {
702
+ return {
703
+ name: "prev-debug",
704
+ enforce: "pre",
705
+ configResolved() {
706
+ collector.startPhase("configResolved");
707
+ },
708
+ buildStart() {
709
+ collector.startPhase("buildStart");
710
+ },
711
+ resolveId(id, importer) {
712
+ const start = performance.now();
713
+ collector.trackFile(id, "resolve", start);
714
+ if (importer) {
715
+ collector.trackFile(`${id} <- ${importer}`, "resolve", start);
716
+ }
717
+ return null;
718
+ },
719
+ load(id) {
720
+ const start = performance.now();
721
+ collector.trackFile(id, "load", start);
722
+ return null;
723
+ },
724
+ transform(_code, id) {
725
+ const start = performance.now();
726
+ collector.trackFile(id, "transform", start);
727
+ return null;
728
+ },
729
+ configureServer(server) {
730
+ collector.startPhase("configureServer");
731
+ server.httpServer?.once("listening", () => {
732
+ collector.startPhase("serverListening");
733
+ });
734
+ server.middlewares.use((req, _res, next) => {
735
+ if (req.url && !req.url.startsWith("/@") && !req.url.includes("__")) {
736
+ collector.trackFile(req.url, "resolve", performance.now());
737
+ }
738
+ next();
739
+ });
740
+ },
741
+ handleHotUpdate({ file }) {
742
+ collector.trackFile(file, "transform", performance.now());
743
+ },
744
+ buildEnd() {
745
+ collector.endPhase("build");
746
+ }
747
+ };
748
+ }
749
+
700
750
  // src/config/schema.ts
701
751
  var defaultConfig = {
702
752
  theme: "system",
@@ -778,6 +828,104 @@ function updateOrder(rootDir, pathKey, order) {
778
828
  config.order[pathKey] = order;
779
829
  saveConfig(rootDir, config);
780
830
  }
831
+ // src/utils/debug.ts
832
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
833
+ import path8 from "path";
834
+
835
+ class DebugCollector {
836
+ startTime;
837
+ files = [];
838
+ phases = {};
839
+ currentPhase = "init";
840
+ rootDir;
841
+ constructor(rootDir) {
842
+ this.startTime = performance.now();
843
+ this.rootDir = rootDir;
844
+ this.startPhase("init");
845
+ }
846
+ startPhase(name) {
847
+ const now = performance.now();
848
+ if (this.phases[this.currentPhase] && !this.phases[this.currentPhase].endMs) {
849
+ this.phases[this.currentPhase].endMs = Math.round(now - this.startTime);
850
+ }
851
+ this.currentPhase = name;
852
+ this.phases[name] = { startMs: Math.round(now - this.startTime) };
853
+ }
854
+ endPhase(name) {
855
+ const phaseName = name || this.currentPhase;
856
+ const now = performance.now();
857
+ if (this.phases[phaseName]) {
858
+ this.phases[phaseName].endMs = Math.round(now - this.startTime);
859
+ }
860
+ }
861
+ trackFile(filePath, event, startTime) {
862
+ const now = performance.now();
863
+ this.files.push({
864
+ path: filePath,
865
+ event,
866
+ ms: Math.round(now - startTime),
867
+ phase: this.currentPhase,
868
+ timestamp: Math.round(startTime - this.startTime)
869
+ });
870
+ }
871
+ generateSummary() {
872
+ const byDirectory = {};
873
+ const byEvent = { resolve: 0, load: 0, transform: 0 };
874
+ const fileTimeAccum = {};
875
+ for (const file of this.files) {
876
+ byEvent[file.event] = (byEvent[file.event] || 0) + 1;
877
+ fileTimeAccum[file.path] = (fileTimeAccum[file.path] || 0) + file.ms;
878
+ const relativePath = file.path.startsWith(this.rootDir) ? file.path.slice(this.rootDir.length) : file.path;
879
+ let dir;
880
+ if (relativePath.includes("node_modules")) {
881
+ const nmIndex = relativePath.indexOf("node_modules");
882
+ const prefix = relativePath.slice(0, nmIndex + "node_modules".length);
883
+ const afterNm = relativePath.slice(nmIndex + "node_modules/".length);
884
+ const pkgName = afterNm.split("/")[0].startsWith("@") ? afterNm.split("/").slice(0, 2).join("/") : afterNm.split("/")[0];
885
+ dir = `${prefix}/${pkgName}`;
886
+ } else {
887
+ const parts = relativePath.split("/").filter(Boolean);
888
+ dir = parts.length > 1 ? `/${parts[0]}` : "/";
889
+ }
890
+ byDirectory[dir] = (byDirectory[dir] || 0) + 1;
891
+ }
892
+ const slowest = Object.entries(fileTimeAccum).map(([path9, totalMs]) => ({ path: path9, totalMs })).sort((a, b) => b.totalMs - a.totalMs).slice(0, 20);
893
+ return {
894
+ totalFiles: this.files.length,
895
+ byDirectory,
896
+ byEvent,
897
+ slowest
898
+ };
899
+ }
900
+ writeReport() {
901
+ const now = performance.now();
902
+ const totalStartupMs = Math.round(now - this.startTime);
903
+ this.endPhase();
904
+ const report = {
905
+ timestamp: new Date().toISOString(),
906
+ totalStartupMs,
907
+ phases: this.phases,
908
+ files: this.files,
909
+ summary: this.generateSummary()
910
+ };
911
+ const debugDir = path8.join(this.rootDir, ".prev-debug");
912
+ mkdirSync2(debugDir, { recursive: true });
913
+ const date = new Date;
914
+ const filename = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}-${String(date.getHours()).padStart(2, "0")}-${String(date.getMinutes()).padStart(2, "0")}-${String(date.getSeconds()).padStart(2, "0")}.json`;
915
+ const filepath = path8.join(debugDir, filename);
916
+ writeFileSync4(filepath, JSON.stringify(report, null, 2));
917
+ return filepath;
918
+ }
919
+ }
920
+ var currentCollector = null;
921
+ function createDebugCollector(rootDir) {
922
+ currentCollector = new DebugCollector(rootDir);
923
+ return currentCollector;
924
+ }
925
+ function getDebugCollector() {
926
+ return currentCollector;
927
+ }
928
+
781
929
  // src/vite/config.ts
782
930
  function createFriendlyLogger() {
783
931
  const logger = createLogger("info", { allowClearScreen: false });
@@ -833,9 +981,9 @@ function createFriendlyLogger() {
833
981
  };
834
982
  }
835
983
  function findCliRoot2() {
836
- let dir = path8.dirname(fileURLToPath2(import.meta.url));
984
+ let dir = path9.dirname(fileURLToPath2(import.meta.url));
837
985
  for (let i = 0;i < 10; i++) {
838
- const pkgPath = path8.join(dir, "package.json");
986
+ const pkgPath = path9.join(dir, "package.json");
839
987
  if (existsSync5(pkgPath)) {
840
988
  try {
841
989
  const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
@@ -844,24 +992,24 @@ function findCliRoot2() {
844
992
  }
845
993
  } catch {}
846
994
  }
847
- const parent = path8.dirname(dir);
995
+ const parent = path9.dirname(dir);
848
996
  if (parent === dir)
849
997
  break;
850
998
  dir = parent;
851
999
  }
852
- return path8.dirname(path8.dirname(fileURLToPath2(import.meta.url)));
1000
+ return path9.dirname(path9.dirname(fileURLToPath2(import.meta.url)));
853
1001
  }
854
1002
  function findNodeModules(cliRoot2) {
855
- const localNodeModules = path8.join(cliRoot2, "node_modules");
856
- if (existsSync5(path8.join(localNodeModules, "react"))) {
1003
+ const localNodeModules = path9.join(cliRoot2, "node_modules");
1004
+ if (existsSync5(path9.join(localNodeModules, "react"))) {
857
1005
  return localNodeModules;
858
1006
  }
859
1007
  let dir = cliRoot2;
860
1008
  for (let i = 0;i < 10; i++) {
861
- const parent = path8.dirname(dir);
1009
+ const parent = path9.dirname(dir);
862
1010
  if (parent === dir)
863
1011
  break;
864
- if (path8.basename(parent) === "node_modules" && existsSync5(path8.join(parent, "react"))) {
1012
+ if (path9.basename(parent) === "node_modules" && existsSync5(path9.join(parent, "react"))) {
865
1013
  return parent;
866
1014
  }
867
1015
  dir = parent;
@@ -870,11 +1018,15 @@ function findNodeModules(cliRoot2) {
870
1018
  }
871
1019
  var cliRoot2 = findCliRoot2();
872
1020
  var cliNodeModules = findNodeModules(cliRoot2);
873
- var srcRoot2 = path8.join(cliRoot2, "src");
1021
+ var srcRoot2 = path9.join(cliRoot2, "src");
874
1022
  async function createViteConfig(options) {
875
- const { rootDir, mode, port, include, base } = options;
1023
+ const { rootDir, mode, port, include, base, debug } = options;
876
1024
  const cacheDir = await ensureCacheDir(rootDir);
877
1025
  const config = loadConfig(rootDir);
1026
+ const debugCollector = debug ? createDebugCollector(rootDir) : null;
1027
+ if (debugCollector) {
1028
+ debugCollector.startPhase("configLoad");
1029
+ }
878
1030
  return {
879
1031
  root: rootDir,
880
1032
  mode,
@@ -885,13 +1037,14 @@ async function createViteConfig(options) {
885
1037
  envDir: cliRoot2,
886
1038
  envPrefix: "PREV_",
887
1039
  plugins: [
1040
+ ...debugCollector ? [debugPlugin(debugCollector)] : [],
888
1041
  mdx({
889
1042
  remarkPlugins: [remarkGfm],
890
1043
  rehypePlugins: [rehypeHighlight],
891
1044
  providerImportSource: "@mdx-js/react",
892
1045
  include: [
893
- path8.join(rootDir, "**/*.md"),
894
- path8.join(rootDir, "**/*.mdx")
1046
+ path9.join(rootDir, "**/*.md"),
1047
+ path9.join(rootDir, "**/*.mdx")
895
1048
  ],
896
1049
  exclude: [
897
1050
  "**/node_modules/**",
@@ -939,7 +1092,7 @@ async function createViteConfig(options) {
939
1092
  if (urlPath.startsWith("/__") || urlPath.startsWith("/@") || urlPath.startsWith("/node_modules") || urlPath.includes(".")) {
940
1093
  return next();
941
1094
  }
942
- const indexPath = path8.join(srcRoot2, "theme/index.html");
1095
+ const indexPath = path9.join(srcRoot2, "theme/index.html");
943
1096
  if (existsSync5(indexPath)) {
944
1097
  server.transformIndexHtml(req.url, readFileSync4(indexPath, "utf-8")).then((html) => {
945
1098
  res.setHeader("Content-Type", "text/html");
@@ -957,8 +1110,8 @@ async function createViteConfig(options) {
957
1110
  resolveId(id) {
958
1111
  if (id.startsWith("/_preview/")) {
959
1112
  const relativePath = id.slice("/_preview/".length);
960
- const previewsDir = path8.join(rootDir, "previews");
961
- const resolved = path8.resolve(previewsDir, relativePath);
1113
+ const previewsDir = path9.join(rootDir, "previews");
1114
+ const resolved = path9.resolve(previewsDir, relativePath);
962
1115
  if (resolved.startsWith(previewsDir)) {
963
1116
  return resolved;
964
1117
  }
@@ -968,7 +1121,7 @@ async function createViteConfig(options) {
968
1121
  server.middlewares.use(async (req, res, next) => {
969
1122
  const urlPath = req.url?.split("?")[0] || "";
970
1123
  if (urlPath === "/_preview-runtime") {
971
- const templatePath = path8.join(srcRoot2, "preview-runtime/template.html");
1124
+ const templatePath = path9.join(srcRoot2, "preview-runtime/template.html");
972
1125
  if (existsSync5(templatePath)) {
973
1126
  const html = readFileSync4(templatePath, "utf-8");
974
1127
  res.setHeader("Content-Type", "text/html");
@@ -978,8 +1131,8 @@ async function createViteConfig(options) {
978
1131
  }
979
1132
  if (urlPath.startsWith("/_preview-config/")) {
980
1133
  const previewName = decodeURIComponent(urlPath.slice("/_preview-config/".length));
981
- const previewsDir = path8.join(rootDir, "previews");
982
- const previewDir = path8.resolve(previewsDir, previewName);
1134
+ const previewsDir = path9.join(rootDir, "previews");
1135
+ const previewDir = path9.resolve(previewsDir, previewName);
983
1136
  if (!previewDir.startsWith(previewsDir)) {
984
1137
  res.statusCode = 403;
985
1138
  res.end("Forbidden");
@@ -1000,11 +1153,11 @@ async function createViteConfig(options) {
1000
1153
  }
1001
1154
  }
1002
1155
  if (urlPath.startsWith("/_preview/")) {
1003
- const isHtmlRequest = !path8.extname(urlPath) || urlPath.endsWith("/");
1156
+ const isHtmlRequest = !path9.extname(urlPath) || urlPath.endsWith("/");
1004
1157
  if (isHtmlRequest) {
1005
1158
  const previewName = decodeURIComponent(urlPath.slice("/_preview/".length).replace(/\/$/, ""));
1006
- const previewsDir = path8.join(rootDir, "previews");
1007
- const htmlPath = path8.resolve(previewsDir, previewName, "index.html");
1159
+ const previewsDir = path9.join(rootDir, "previews");
1160
+ const htmlPath = path9.resolve(previewsDir, previewName, "index.html");
1008
1161
  if (!htmlPath.startsWith(previewsDir)) {
1009
1162
  return next();
1010
1163
  }
@@ -1031,15 +1184,15 @@ async function createViteConfig(options) {
1031
1184
  ],
1032
1185
  resolve: {
1033
1186
  alias: {
1034
- "@prev/ui": path8.join(srcRoot2, "ui"),
1035
- "@prev/theme": path8.join(srcRoot2, "theme"),
1036
- react: path8.join(cliNodeModules, "react"),
1037
- "react-dom": path8.join(cliNodeModules, "react-dom"),
1038
- "@tanstack/react-router": path8.join(cliNodeModules, "@tanstack/react-router"),
1039
- "@mdx-js/react": path8.join(cliNodeModules, "@mdx-js/react"),
1040
- mermaid: path8.join(cliNodeModules, "mermaid"),
1041
- dayjs: path8.join(cliNodeModules, "dayjs"),
1042
- "@terrastruct/d2": path8.join(cliNodeModules, "@terrastruct/d2")
1187
+ "@prev/ui": path9.join(srcRoot2, "ui"),
1188
+ "@prev/theme": path9.join(srcRoot2, "theme"),
1189
+ react: path9.join(cliNodeModules, "react"),
1190
+ "react-dom": path9.join(cliNodeModules, "react-dom"),
1191
+ "@tanstack/react-router": path9.join(cliNodeModules, "@tanstack/react-router"),
1192
+ "@mdx-js/react": path9.join(cliNodeModules, "@mdx-js/react"),
1193
+ mermaid: path9.join(cliNodeModules, "mermaid"),
1194
+ dayjs: path9.join(cliNodeModules, "dayjs"),
1195
+ "@terrastruct/d2": path9.join(cliNodeModules, "@terrastruct/d2")
1043
1196
  },
1044
1197
  dedupe: [
1045
1198
  "react",
@@ -1048,20 +1201,8 @@ async function createViteConfig(options) {
1048
1201
  ]
1049
1202
  },
1050
1203
  optimizeDeps: {
1051
- entries: [],
1052
1204
  noDiscovery: true,
1053
- include: [
1054
- "react",
1055
- "react-dom",
1056
- "react-dom/client",
1057
- "@tanstack/react-router",
1058
- "react/jsx-runtime",
1059
- "react/jsx-dev-runtime",
1060
- "@mdx-js/react",
1061
- "mermaid",
1062
- "dayjs",
1063
- "@terrastruct/d2"
1064
- ],
1205
+ include: [],
1065
1206
  exclude: [
1066
1207
  "virtual:prev-config",
1067
1208
  "virtual:prev-previews",
@@ -1081,8 +1222,8 @@ async function createViteConfig(options) {
1081
1222
  },
1082
1223
  warmup: {
1083
1224
  clientFiles: [
1084
- path8.join(srcRoot2, "theme/entry.tsx"),
1085
- path8.join(srcRoot2, "theme/styles.css")
1225
+ path9.join(srcRoot2, "theme/entry.tsx"),
1226
+ path9.join(srcRoot2, "theme/styles.css")
1086
1227
  ]
1087
1228
  }
1088
1229
  },
@@ -1091,12 +1232,12 @@ async function createViteConfig(options) {
1091
1232
  strictPort: false
1092
1233
  },
1093
1234
  build: {
1094
- outDir: path8.join(rootDir, "dist"),
1235
+ outDir: path9.join(rootDir, "dist"),
1095
1236
  reportCompressedSize: false,
1096
1237
  chunkSizeWarningLimit: 1e4,
1097
1238
  rollupOptions: {
1098
1239
  input: {
1099
- main: path8.join(srcRoot2, "theme/index.html")
1240
+ main: path9.join(srcRoot2, "theme/index.html")
1100
1241
  }
1101
1242
  }
1102
1243
  },
@@ -1141,7 +1282,7 @@ async function findAvailablePort(minPort, maxPort) {
1141
1282
  // src/vite/start.ts
1142
1283
  import { exec as exec2 } from "child_process";
1143
1284
  import { existsSync as existsSync6, rmSync as rmSync2, copyFileSync } from "fs";
1144
- import path9 from "path";
1285
+ import path10 from "path";
1145
1286
  function printWelcome(type) {
1146
1287
  console.log();
1147
1288
  console.log(" ✨ prev");
@@ -1174,8 +1315,8 @@ function openBrowser(url) {
1174
1315
  console.log(` ↗ Opened ${url}`);
1175
1316
  }
1176
1317
  function clearCache(rootDir) {
1177
- const viteCacheDir = path9.join(rootDir, ".vite");
1178
- const nodeModulesVite = path9.join(rootDir, "node_modules", ".vite");
1318
+ const viteCacheDir = path10.join(rootDir, ".vite");
1319
+ const nodeModulesVite = path10.join(rootDir, "node_modules", ".vite");
1179
1320
  let cleared = 0;
1180
1321
  if (existsSync6(viteCacheDir)) {
1181
1322
  rmSync2(viteCacheDir, { recursive: true });
@@ -1229,10 +1370,17 @@ async function startDev(rootDir, options = {}) {
1229
1370
  rootDir,
1230
1371
  mode: "development",
1231
1372
  port,
1232
- include: options.include
1373
+ include: options.include,
1374
+ debug: options.debug
1233
1375
  });
1234
1376
  const server = await createServer2(config);
1235
1377
  await server.listen();
1378
+ const debugCollector = getDebugCollector();
1379
+ if (debugCollector) {
1380
+ debugCollector.startPhase("serverReady");
1381
+ const reportPath = debugCollector.writeReport();
1382
+ console.log(` \uD83D\uDCCA Debug trace written to: ${reportPath}`);
1383
+ }
1236
1384
  const actualPort = server.config.server.port || port;
1237
1385
  const url = `http://localhost:${actualPort}/`;
1238
1386
  printWelcome("dev");
@@ -1281,12 +1429,19 @@ async function buildSite(rootDir, options = {}) {
1281
1429
  rootDir,
1282
1430
  mode: "production",
1283
1431
  include: options.include,
1284
- base: options.base
1432
+ base: options.base,
1433
+ debug: options.debug
1285
1434
  });
1286
1435
  await build2(config);
1287
- const distDir = path9.join(rootDir, "dist");
1288
- const indexPath = path9.join(distDir, "index.html");
1289
- const notFoundPath = path9.join(distDir, "404.html");
1436
+ const debugCollector = getDebugCollector();
1437
+ if (debugCollector) {
1438
+ debugCollector.startPhase("buildComplete");
1439
+ const reportPath = debugCollector.writeReport();
1440
+ console.log(` \uD83D\uDCCA Debug trace written to: ${reportPath}`);
1441
+ }
1442
+ const distDir = path10.join(rootDir, "dist");
1443
+ const indexPath = path10.join(distDir, "index.html");
1444
+ const notFoundPath = path10.join(distDir, "404.html");
1290
1445
  if (existsSync6(indexPath)) {
1291
1446
  copyFileSync(indexPath, notFoundPath);
1292
1447
  }
@@ -1301,9 +1456,16 @@ async function previewSite(rootDir, options = {}) {
1301
1456
  rootDir,
1302
1457
  mode: "production",
1303
1458
  port,
1304
- include: options.include
1459
+ include: options.include,
1460
+ debug: options.debug
1305
1461
  });
1306
1462
  const server = await preview(config);
1463
+ const debugCollector = getDebugCollector();
1464
+ if (debugCollector) {
1465
+ debugCollector.startPhase("previewReady");
1466
+ const reportPath = debugCollector.writeReport();
1467
+ console.log(` \uD83D\uDCCA Debug trace written to: ${reportPath}`);
1468
+ }
1307
1469
  printWelcome("preview");
1308
1470
  server.printUrls();
1309
1471
  console.log();
@@ -1316,15 +1478,15 @@ async function previewSite(rootDir, options = {}) {
1316
1478
  import yaml2 from "js-yaml";
1317
1479
  function getVersion() {
1318
1480
  try {
1319
- let dir = path10.dirname(fileURLToPath3(import.meta.url));
1481
+ let dir = path11.dirname(fileURLToPath3(import.meta.url));
1320
1482
  for (let i = 0;i < 5; i++) {
1321
- const pkgPath = path10.join(dir, "package.json");
1483
+ const pkgPath = path11.join(dir, "package.json");
1322
1484
  if (existsSync7(pkgPath)) {
1323
1485
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
1324
1486
  if (pkg.name === "prev-cli")
1325
1487
  return pkg.version;
1326
1488
  }
1327
- dir = path10.dirname(dir);
1489
+ dir = path11.dirname(dir);
1328
1490
  }
1329
1491
  } catch {}
1330
1492
  return "unknown";
@@ -1336,13 +1498,14 @@ var { values, positionals } = parseArgs({
1336
1498
  days: { type: "string", short: "d" },
1337
1499
  cwd: { type: "string", short: "c" },
1338
1500
  base: { type: "string", short: "b" },
1501
+ debug: { type: "boolean" },
1339
1502
  help: { type: "boolean", short: "h" },
1340
1503
  version: { type: "boolean", short: "v" }
1341
1504
  },
1342
1505
  allowPositionals: true
1343
1506
  });
1344
1507
  var command = positionals[0] || "dev";
1345
- var rootDir = path10.resolve(values.cwd || (command === "config" || command === "create" ? "." : positionals[1]) || ".");
1508
+ var rootDir = path11.resolve(values.cwd || (command === "config" || command === "create" ? "." : positionals[1]) || ".");
1346
1509
  function printHelp() {
1347
1510
  console.log(`
1348
1511
  prev - Zero-config documentation site generator
@@ -1367,6 +1530,7 @@ Options:
1367
1530
  -p, --port <port> Specify port (dev/preview)
1368
1531
  -b, --base <path> Base path for deployment (e.g., /repo-name/ for GitHub Pages)
1369
1532
  -d, --days <days> Cache age threshold for clean (default: 30)
1533
+ --debug Write debug trace to .prev-debug/ for performance analysis
1370
1534
  -h, --help Show this help message
1371
1535
  -v, --version Show version number
1372
1536
 
@@ -1451,8 +1615,8 @@ async function clearViteCache(rootDir2) {
1451
1615
  console.log(` ✓ Removed ${prevCacheDir}`);
1452
1616
  }
1453
1617
  } catch {}
1454
- const viteCacheDir = path10.join(rootDir2, ".vite");
1455
- const nodeModulesVite = path10.join(rootDir2, "node_modules", ".vite");
1618
+ const viteCacheDir = path11.join(rootDir2, ".vite");
1619
+ const nodeModulesVite = path11.join(rootDir2, "node_modules", ".vite");
1456
1620
  if (existsSync7(viteCacheDir)) {
1457
1621
  rmSync3(viteCacheDir, { recursive: true });
1458
1622
  cleared++;
@@ -1504,7 +1668,7 @@ function handleConfig(rootDir2, subcommand) {
1504
1668
  break;
1505
1669
  }
1506
1670
  case "init": {
1507
- const targetPath = path10.join(rootDir2, ".prev.yaml");
1671
+ const targetPath = path11.join(rootDir2, ".prev.yaml");
1508
1672
  if (configPath) {
1509
1673
  console.log(`
1510
1674
  Config already exists: ${configPath}
@@ -1540,7 +1704,7 @@ order: {}
1540
1704
  # - "getting-started.md"
1541
1705
  # - "guides/"
1542
1706
  `;
1543
- writeFileSync4(targetPath, configContent, "utf-8");
1707
+ writeFileSync5(targetPath, configContent, "utf-8");
1544
1708
  console.log(`
1545
1709
  ✨ Created ${targetPath}
1546
1710
  `);
@@ -1563,12 +1727,12 @@ Available subcommands: show, init, path`);
1563
1727
  }
1564
1728
  }
1565
1729
  function createPreview(rootDir2, name) {
1566
- const previewDir = path10.join(rootDir2, "previews", name);
1730
+ const previewDir = path11.join(rootDir2, "previews", name);
1567
1731
  if (existsSync7(previewDir)) {
1568
1732
  console.error(`Preview "${name}" already exists at: ${previewDir}`);
1569
1733
  process.exit(1);
1570
1734
  }
1571
- mkdirSync2(previewDir, { recursive: true });
1735
+ mkdirSync3(previewDir, { recursive: true });
1572
1736
  const appTsx = `import { useState } from 'react'
1573
1737
  import './styles.css'
1574
1738
 
@@ -1689,8 +1853,8 @@ export default function App() {
1689
1853
  .dark\\:text-white { color: #fff; }
1690
1854
  }
1691
1855
  `;
1692
- writeFileSync4(path10.join(previewDir, "App.tsx"), appTsx);
1693
- writeFileSync4(path10.join(previewDir, "styles.css"), stylesCss);
1856
+ writeFileSync5(path11.join(previewDir, "App.tsx"), appTsx);
1857
+ writeFileSync5(path11.join(previewDir, "styles.css"), stylesCss);
1694
1858
  console.log(`
1695
1859
  ✨ Created preview: previews/${name}/
1696
1860
 
@@ -1722,13 +1886,13 @@ async function main() {
1722
1886
  try {
1723
1887
  switch (command) {
1724
1888
  case "dev":
1725
- await startDev(rootDir, { port, include });
1889
+ await startDev(rootDir, { port, include, debug: values.debug });
1726
1890
  break;
1727
1891
  case "build":
1728
- await buildSite(rootDir, { include, base: values.base });
1892
+ await buildSite(rootDir, { include, base: values.base, debug: values.debug });
1729
1893
  break;
1730
1894
  case "preview":
1731
- await previewSite(rootDir, { port, include });
1895
+ await previewSite(rootDir, { port, include, debug: values.debug });
1732
1896
  break;
1733
1897
  case "clean":
1734
1898
  const removed = await cleanCache({ maxAgeDays: days });
@@ -0,0 +1,42 @@
1
+ export interface FileEvent {
2
+ path: string;
3
+ event: 'resolve' | 'load' | 'transform';
4
+ ms: number;
5
+ phase: string;
6
+ timestamp: number;
7
+ }
8
+ export interface PhaseInfo {
9
+ startMs: number;
10
+ endMs?: number;
11
+ }
12
+ export interface DebugSummary {
13
+ totalFiles: number;
14
+ byDirectory: Record<string, number>;
15
+ byEvent: Record<string, number>;
16
+ slowest: Array<{
17
+ path: string;
18
+ totalMs: number;
19
+ }>;
20
+ }
21
+ export interface DebugReport {
22
+ timestamp: string;
23
+ totalStartupMs: number;
24
+ phases: Record<string, PhaseInfo>;
25
+ files: FileEvent[];
26
+ summary: DebugSummary;
27
+ }
28
+ export declare class DebugCollector {
29
+ private startTime;
30
+ private files;
31
+ private phases;
32
+ private currentPhase;
33
+ private rootDir;
34
+ constructor(rootDir: string);
35
+ startPhase(name: string): void;
36
+ endPhase(name?: string): void;
37
+ trackFile(filePath: string, event: 'resolve' | 'load' | 'transform', startTime: number): void;
38
+ private generateSummary;
39
+ writeReport(): string;
40
+ }
41
+ export declare function createDebugCollector(rootDir: string): DebugCollector;
42
+ export declare function getDebugCollector(): DebugCollector | null;
@@ -1,9 +1,12 @@
1
1
  import type { InlineConfig } from 'vite';
2
+ import { getDebugCollector } from '../utils/debug';
3
+ export { getDebugCollector };
2
4
  export interface ConfigOptions {
3
5
  rootDir: string;
4
6
  mode: 'development' | 'production';
5
7
  port?: number;
6
8
  include?: string[];
7
9
  base?: string;
10
+ debug?: boolean;
8
11
  }
9
12
  export declare function createViteConfig(options: ConfigOptions): Promise<InlineConfig>;
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from 'vite';
2
+ import { DebugCollector } from '../../utils/debug';
3
+ export declare function debugPlugin(collector: DebugCollector): Plugin;
@@ -1,10 +1,12 @@
1
1
  export interface DevOptions {
2
2
  port?: number;
3
3
  include?: string[];
4
+ debug?: boolean;
4
5
  }
5
6
  export interface BuildOptions {
6
7
  include?: string[];
7
8
  base?: string;
9
+ debug?: boolean;
8
10
  }
9
11
  export declare function startDev(rootDir: string, options?: DevOptions): Promise<import("vite").ViteDevServer>;
10
12
  export declare function buildSite(rootDir: string, options?: BuildOptions): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prev-cli",
3
- "version": "0.24.4",
3
+ "version": "0.24.5",
4
4
  "description": "Transform MDX directories into beautiful documentation websites",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -36,6 +36,7 @@
36
36
  "scripts": {
37
37
  "build": "bun build src/cli.ts --outdir dist --target node --packages external && tsc --emitDeclarationOnly",
38
38
  "build:docs": "bun run build && bun ./dist/cli.js build",
39
+ "prepublishOnly": "bun run build",
39
40
  "dev": "tsc --watch",
40
41
  "test": "bun test src",
41
42
  "test:integration": "bun run build && bun test test/integration.test.ts",