prev-cli 0.24.6 → 0.24.8
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 +165 -151
- package/dist/ui/button.d.ts +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -15,68 +15,18 @@ 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
|
|
18
|
+
import path8 from "path";
|
|
19
|
+
import os from "os";
|
|
19
20
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
20
21
|
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
21
22
|
|
|
22
|
-
// src/utils/cache.ts
|
|
23
|
-
import { createHash } from "crypto";
|
|
24
|
-
import { readdir, rm, stat, mkdir } from "fs/promises";
|
|
25
|
-
import { exec } from "child_process";
|
|
26
|
-
import { promisify } from "util";
|
|
27
|
-
import path from "path";
|
|
28
|
-
import os from "os";
|
|
29
|
-
var execAsync = promisify(exec);
|
|
30
|
-
var DEFAULT_CACHE_ROOT = path.join(os.homedir(), ".cache/prev");
|
|
31
|
-
async function getCacheDir(rootDir, branch) {
|
|
32
|
-
const resolvedBranch = branch ?? await getCurrentBranch(rootDir);
|
|
33
|
-
const hash = createHash("sha1").update(`${rootDir}:${resolvedBranch}`).digest("hex").slice(0, 12);
|
|
34
|
-
return path.join(DEFAULT_CACHE_ROOT, hash);
|
|
35
|
-
}
|
|
36
|
-
async function getCurrentBranch(rootDir) {
|
|
37
|
-
try {
|
|
38
|
-
const { stdout } = await execAsync("git branch --show-current", { cwd: rootDir });
|
|
39
|
-
return stdout.trim() || "detached";
|
|
40
|
-
} catch {
|
|
41
|
-
return "no-git";
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
async function cleanCache(options) {
|
|
45
|
-
const cacheRoot = options.cacheRoot ?? DEFAULT_CACHE_ROOT;
|
|
46
|
-
const maxAge = options.maxAgeDays * 24 * 60 * 60 * 1000;
|
|
47
|
-
const now = Date.now();
|
|
48
|
-
let dirs;
|
|
49
|
-
try {
|
|
50
|
-
dirs = await readdir(cacheRoot);
|
|
51
|
-
} catch {
|
|
52
|
-
return 0;
|
|
53
|
-
}
|
|
54
|
-
let removed = 0;
|
|
55
|
-
for (const dir of dirs) {
|
|
56
|
-
const fullPath = path.join(cacheRoot, dir);
|
|
57
|
-
try {
|
|
58
|
-
const info = await stat(fullPath);
|
|
59
|
-
if (info.isDirectory() && now - info.mtimeMs > maxAge) {
|
|
60
|
-
await rm(fullPath, { recursive: true });
|
|
61
|
-
removed++;
|
|
62
|
-
}
|
|
63
|
-
} catch {}
|
|
64
|
-
}
|
|
65
|
-
return removed;
|
|
66
|
-
}
|
|
67
|
-
async function ensureCacheDir(rootDir) {
|
|
68
|
-
const cacheDir = await getCacheDir(rootDir);
|
|
69
|
-
await mkdir(cacheDir, { recursive: true });
|
|
70
|
-
return cacheDir;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
23
|
// src/vite/plugins/pages-plugin.ts
|
|
74
|
-
import
|
|
24
|
+
import path2 from "path";
|
|
75
25
|
|
|
76
26
|
// src/vite/pages.ts
|
|
77
27
|
import fg from "fast-glob";
|
|
78
28
|
import { readFile } from "fs/promises";
|
|
79
|
-
import
|
|
29
|
+
import path from "path";
|
|
80
30
|
import picomatch from "picomatch";
|
|
81
31
|
function parseValue(value) {
|
|
82
32
|
const trimmed = value.trim();
|
|
@@ -138,9 +88,9 @@ function fileToRoute(file) {
|
|
|
138
88
|
normalizedFile = normalizedFile.slice(firstDir.length + 1) || normalizedFile;
|
|
139
89
|
}
|
|
140
90
|
const withoutExt = normalizedFile.replace(/\.mdx?$/, "");
|
|
141
|
-
const basename =
|
|
91
|
+
const basename = path.basename(withoutExt).toLowerCase();
|
|
142
92
|
if (basename === "index" || basename === "readme") {
|
|
143
|
-
const dir =
|
|
93
|
+
const dir = path.dirname(withoutExt);
|
|
144
94
|
if (dir === "." || dir === "") {
|
|
145
95
|
return "/";
|
|
146
96
|
}
|
|
@@ -156,9 +106,30 @@ async function scanPages(rootDir, options = {}) {
|
|
|
156
106
|
patterns.push(`${dir}/**/*.{md,mdx}`);
|
|
157
107
|
}
|
|
158
108
|
const ignore = [
|
|
159
|
-
"node_modules/**",
|
|
160
|
-
"
|
|
161
|
-
"
|
|
109
|
+
"**/node_modules/**",
|
|
110
|
+
"**/.worktrees/**",
|
|
111
|
+
"**/.claude/**",
|
|
112
|
+
"**/dist/**",
|
|
113
|
+
"**/.cache/**",
|
|
114
|
+
"**/.git/**",
|
|
115
|
+
"**/coverage/**",
|
|
116
|
+
"**/build/**",
|
|
117
|
+
"**/.next/**",
|
|
118
|
+
"**/.nuxt/**",
|
|
119
|
+
"**/.turbo/**",
|
|
120
|
+
"**/.vercel/**",
|
|
121
|
+
"**/.svelte-kit/**",
|
|
122
|
+
"**/vendor/**",
|
|
123
|
+
"**/target/**",
|
|
124
|
+
"apps/**",
|
|
125
|
+
"packages/**",
|
|
126
|
+
"services/**",
|
|
127
|
+
"libs/**",
|
|
128
|
+
"src/**",
|
|
129
|
+
"test/**",
|
|
130
|
+
"tests/**",
|
|
131
|
+
"__tests__/**",
|
|
132
|
+
"e2e/**",
|
|
162
133
|
"CLAUDE.md",
|
|
163
134
|
"CHANGELOG.md",
|
|
164
135
|
"CONTRIBUTING.md",
|
|
@@ -182,16 +153,16 @@ async function scanPages(rootDir, options = {}) {
|
|
|
182
153
|
const routeMap = new Map;
|
|
183
154
|
for (const file of filteredFiles) {
|
|
184
155
|
const route = fileToRoute(file);
|
|
185
|
-
const basename =
|
|
156
|
+
const basename = path.basename(file, path.extname(file)).toLowerCase();
|
|
186
157
|
const priority = basename === "index" ? 1 : basename === "readme" ? 2 : 0;
|
|
187
158
|
const existing = routeMap.get(route);
|
|
188
159
|
if (!existing || priority > 0 && priority < existing.priority) {
|
|
189
160
|
routeMap.set(route, { file, priority });
|
|
190
161
|
}
|
|
191
162
|
}
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
const fullPath =
|
|
163
|
+
const fileEntries = Array.from(routeMap.values());
|
|
164
|
+
const pages = await Promise.all(fileEntries.map(async ({ file }) => {
|
|
165
|
+
const fullPath = path.join(rootDir, file);
|
|
195
166
|
const rawContent = await readFile(fullPath, "utf-8");
|
|
196
167
|
const { frontmatter, content } = parseFrontmatter(rawContent);
|
|
197
168
|
const title = extractTitle(content, file, frontmatter);
|
|
@@ -209,8 +180,8 @@ async function scanPages(rootDir, options = {}) {
|
|
|
209
180
|
if (frontmatter.hidden === true) {
|
|
210
181
|
page.hidden = true;
|
|
211
182
|
}
|
|
212
|
-
|
|
213
|
-
}
|
|
183
|
+
return page;
|
|
184
|
+
}));
|
|
214
185
|
return pages.sort((a, b) => a.route.localeCompare(b.route));
|
|
215
186
|
}
|
|
216
187
|
function extractTitle(content, file, frontmatter) {
|
|
@@ -221,12 +192,12 @@ function extractTitle(content, file, frontmatter) {
|
|
|
221
192
|
if (match) {
|
|
222
193
|
return match[1].trim();
|
|
223
194
|
}
|
|
224
|
-
const basename =
|
|
195
|
+
const basename = path.basename(file, path.extname(file)).toLowerCase();
|
|
225
196
|
if (isIndexFile(basename)) {
|
|
226
|
-
const dirname =
|
|
227
|
-
return dirname === "." ? "Home" : capitalize(
|
|
197
|
+
const dirname = path.dirname(file);
|
|
198
|
+
return dirname === "." ? "Home" : capitalize(path.basename(dirname));
|
|
228
199
|
}
|
|
229
|
-
return capitalize(
|
|
200
|
+
return capitalize(path.basename(file, path.extname(file)));
|
|
230
201
|
}
|
|
231
202
|
function capitalize(str) {
|
|
232
203
|
return str.charAt(0).toUpperCase() + str.slice(1).replace(/-/g, " ");
|
|
@@ -295,7 +266,7 @@ function pagesPlugin(rootDir, options = {}) {
|
|
|
295
266
|
if (id === RESOLVED_VIRTUAL_MODULES_ID) {
|
|
296
267
|
const pages = await getPages();
|
|
297
268
|
const imports = pages.map((page, i) => {
|
|
298
|
-
const absolutePath =
|
|
269
|
+
const absolutePath = path2.join(rootDir, page.file);
|
|
299
270
|
return `import * as _page${i} from ${JSON.stringify(absolutePath)};`;
|
|
300
271
|
}).join(`
|
|
301
272
|
`);
|
|
@@ -331,13 +302,13 @@ ${entries}
|
|
|
331
302
|
}
|
|
332
303
|
|
|
333
304
|
// src/vite/plugins/entry-plugin.ts
|
|
334
|
-
import
|
|
305
|
+
import path3 from "path";
|
|
335
306
|
import { fileURLToPath } from "url";
|
|
336
307
|
import { existsSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
337
308
|
function findCliRoot() {
|
|
338
|
-
let dir =
|
|
309
|
+
let dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
339
310
|
for (let i = 0;i < 10; i++) {
|
|
340
|
-
const pkgPath =
|
|
311
|
+
const pkgPath = path3.join(dir, "package.json");
|
|
341
312
|
if (existsSync(pkgPath)) {
|
|
342
313
|
try {
|
|
343
314
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
@@ -346,15 +317,15 @@ function findCliRoot() {
|
|
|
346
317
|
}
|
|
347
318
|
} catch {}
|
|
348
319
|
}
|
|
349
|
-
const parent =
|
|
320
|
+
const parent = path3.dirname(dir);
|
|
350
321
|
if (parent === dir)
|
|
351
322
|
break;
|
|
352
323
|
dir = parent;
|
|
353
324
|
}
|
|
354
|
-
return
|
|
325
|
+
return path3.dirname(path3.dirname(fileURLToPath(import.meta.url)));
|
|
355
326
|
}
|
|
356
327
|
var cliRoot = findCliRoot();
|
|
357
|
-
var srcRoot =
|
|
328
|
+
var srcRoot = path3.join(cliRoot, "src");
|
|
358
329
|
function getHtml(entryPath, forBuild = false) {
|
|
359
330
|
const scriptSrc = forBuild ? entryPath : `/@fs${entryPath}`;
|
|
360
331
|
return `<!DOCTYPE html>
|
|
@@ -377,13 +348,13 @@ function getHtml(entryPath, forBuild = false) {
|
|
|
377
348
|
</html>`;
|
|
378
349
|
}
|
|
379
350
|
function entryPlugin(rootDir) {
|
|
380
|
-
const entryPath =
|
|
351
|
+
const entryPath = path3.join(srcRoot, "theme/entry.tsx");
|
|
381
352
|
let tempHtmlPath = null;
|
|
382
353
|
return {
|
|
383
354
|
name: "prev-entry",
|
|
384
355
|
config(config, { command }) {
|
|
385
356
|
if (command === "build" && rootDir) {
|
|
386
|
-
tempHtmlPath =
|
|
357
|
+
tempHtmlPath = path3.join(rootDir, "index.html");
|
|
387
358
|
writeFileSync(tempHtmlPath, getHtml(entryPath, true));
|
|
388
359
|
const existingInput = config.build?.rollupOptions?.input || {};
|
|
389
360
|
const inputObj = typeof existingInput === "string" ? { _original: existingInput } : Array.isArray(existingInput) ? Object.fromEntries(existingInput.map((f, i) => [`entry${i}`, f])) : existingInput;
|
|
@@ -430,10 +401,10 @@ function entryPlugin(rootDir) {
|
|
|
430
401
|
|
|
431
402
|
// src/vite/previews.ts
|
|
432
403
|
import fg2 from "fast-glob";
|
|
433
|
-
import
|
|
404
|
+
import path4 from "path";
|
|
434
405
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
435
406
|
async function scanPreviews(rootDir) {
|
|
436
|
-
const previewsDir =
|
|
407
|
+
const previewsDir = path4.join(rootDir, "previews");
|
|
437
408
|
if (!existsSync2(previewsDir)) {
|
|
438
409
|
return [];
|
|
439
410
|
}
|
|
@@ -443,17 +414,17 @@ async function scanPreviews(rootDir) {
|
|
|
443
414
|
});
|
|
444
415
|
const previewDirs = new Map;
|
|
445
416
|
for (const file of entryFiles) {
|
|
446
|
-
const dir =
|
|
417
|
+
const dir = path4.dirname(file);
|
|
447
418
|
if (!previewDirs.has(dir)) {
|
|
448
419
|
previewDirs.set(dir, file);
|
|
449
420
|
}
|
|
450
421
|
}
|
|
451
422
|
return Array.from(previewDirs.entries()).map(([dir, file]) => {
|
|
452
|
-
const name = dir === "." ?
|
|
423
|
+
const name = dir === "." ? path4.basename(previewsDir) : dir;
|
|
453
424
|
return {
|
|
454
425
|
name,
|
|
455
426
|
route: `/_preview/${name}`,
|
|
456
|
-
htmlPath:
|
|
427
|
+
htmlPath: path4.join(previewsDir, file)
|
|
457
428
|
};
|
|
458
429
|
});
|
|
459
430
|
}
|
|
@@ -463,8 +434,8 @@ async function scanPreviewFiles(previewDir) {
|
|
|
463
434
|
ignore: ["node_modules/**", "dist/**"]
|
|
464
435
|
});
|
|
465
436
|
return files.map((file) => {
|
|
466
|
-
const content = readFileSync2(
|
|
467
|
-
const ext =
|
|
437
|
+
const content = readFileSync2(path4.join(previewDir, file), "utf-8");
|
|
438
|
+
const ext = path4.extname(file).slice(1);
|
|
468
439
|
return {
|
|
469
440
|
path: file,
|
|
470
441
|
content,
|
|
@@ -608,7 +579,7 @@ async function buildPreviewHtml(config) {
|
|
|
608
579
|
|
|
609
580
|
// src/vite/plugins/previews-plugin.ts
|
|
610
581
|
import { existsSync as existsSync3, mkdirSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
611
|
-
import
|
|
582
|
+
import path5 from "path";
|
|
612
583
|
var VIRTUAL_MODULE_ID2 = "virtual:prev-previews";
|
|
613
584
|
var RESOLVED_VIRTUAL_MODULE_ID2 = "\x00" + VIRTUAL_MODULE_ID2;
|
|
614
585
|
function previewsPlugin(rootDir) {
|
|
@@ -641,10 +612,10 @@ function previewsPlugin(rootDir) {
|
|
|
641
612
|
async closeBundle() {
|
|
642
613
|
if (!isBuild)
|
|
643
614
|
return;
|
|
644
|
-
const distDir =
|
|
645
|
-
const targetDir =
|
|
646
|
-
const previewsDir =
|
|
647
|
-
const oldPreviewsDir =
|
|
615
|
+
const distDir = path5.join(rootDir, "dist");
|
|
616
|
+
const targetDir = path5.join(distDir, "_preview");
|
|
617
|
+
const previewsDir = path5.join(rootDir, "previews");
|
|
618
|
+
const oldPreviewsDir = path5.join(distDir, "previews");
|
|
648
619
|
if (existsSync3(oldPreviewsDir)) {
|
|
649
620
|
rmSync(oldPreviewsDir, { recursive: true });
|
|
650
621
|
}
|
|
@@ -657,7 +628,7 @@ function previewsPlugin(rootDir) {
|
|
|
657
628
|
console.log(`
|
|
658
629
|
Building ${previews.length} preview(s)...`);
|
|
659
630
|
for (const preview of previews) {
|
|
660
|
-
const previewDir =
|
|
631
|
+
const previewDir = path5.join(previewsDir, preview.name);
|
|
661
632
|
try {
|
|
662
633
|
const config = await buildPreviewConfig(previewDir);
|
|
663
634
|
const result = await buildPreviewHtml(config);
|
|
@@ -665,9 +636,9 @@ function previewsPlugin(rootDir) {
|
|
|
665
636
|
console.error(` ✗ ${preview.name}: ${result.error}`);
|
|
666
637
|
continue;
|
|
667
638
|
}
|
|
668
|
-
const outputDir =
|
|
639
|
+
const outputDir = path5.join(targetDir, preview.name);
|
|
669
640
|
mkdirSync(outputDir, { recursive: true });
|
|
670
|
-
writeFileSync2(
|
|
641
|
+
writeFileSync2(path5.join(outputDir, "index.html"), result.html);
|
|
671
642
|
console.log(` ✓ ${preview.name}`);
|
|
672
643
|
} catch (err) {
|
|
673
644
|
console.error(` ✗ ${preview.name}: ${err}`);
|
|
@@ -788,11 +759,11 @@ function validateConfig(raw) {
|
|
|
788
759
|
}
|
|
789
760
|
// src/config/loader.ts
|
|
790
761
|
import { readFileSync as readFileSync3, existsSync as existsSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
791
|
-
import
|
|
762
|
+
import path6 from "path";
|
|
792
763
|
import yaml from "js-yaml";
|
|
793
764
|
function findConfigFile(rootDir) {
|
|
794
|
-
const yamlPath =
|
|
795
|
-
const ymlPath =
|
|
765
|
+
const yamlPath = path6.join(rootDir, ".prev.yaml");
|
|
766
|
+
const ymlPath = path6.join(rootDir, ".prev.yml");
|
|
796
767
|
if (existsSync4(yamlPath))
|
|
797
768
|
return yamlPath;
|
|
798
769
|
if (existsSync4(ymlPath))
|
|
@@ -814,7 +785,7 @@ function loadConfig(rootDir) {
|
|
|
814
785
|
}
|
|
815
786
|
}
|
|
816
787
|
function saveConfig(rootDir, config) {
|
|
817
|
-
const configPath = findConfigFile(rootDir) ||
|
|
788
|
+
const configPath = findConfigFile(rootDir) || path6.join(rootDir, ".prev.yaml");
|
|
818
789
|
const content = yaml.dump(config, {
|
|
819
790
|
indent: 2,
|
|
820
791
|
lineWidth: -1,
|
|
@@ -830,7 +801,7 @@ function updateOrder(rootDir, pathKey, order) {
|
|
|
830
801
|
}
|
|
831
802
|
// src/utils/debug.ts
|
|
832
803
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
833
|
-
import
|
|
804
|
+
import path7 from "path";
|
|
834
805
|
|
|
835
806
|
class DebugCollector {
|
|
836
807
|
startTime;
|
|
@@ -889,7 +860,7 @@ class DebugCollector {
|
|
|
889
860
|
}
|
|
890
861
|
byDirectory[dir] = (byDirectory[dir] || 0) + 1;
|
|
891
862
|
}
|
|
892
|
-
const slowest = Object.entries(fileTimeAccum).map(([
|
|
863
|
+
const slowest = Object.entries(fileTimeAccum).map(([path8, totalMs]) => ({ path: path8, totalMs })).sort((a, b) => b.totalMs - a.totalMs).slice(0, 20);
|
|
893
864
|
return {
|
|
894
865
|
totalFiles: this.files.length,
|
|
895
866
|
byDirectory,
|
|
@@ -908,11 +879,11 @@ class DebugCollector {
|
|
|
908
879
|
files: this.files,
|
|
909
880
|
summary: this.generateSummary()
|
|
910
881
|
};
|
|
911
|
-
const debugDir =
|
|
882
|
+
const debugDir = path7.join(this.rootDir, ".prev-debug");
|
|
912
883
|
mkdirSync2(debugDir, { recursive: true });
|
|
913
884
|
const date = new Date;
|
|
914
885
|
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 =
|
|
886
|
+
const filepath = path7.join(debugDir, filename);
|
|
916
887
|
writeFileSync4(filepath, JSON.stringify(report, null, 2));
|
|
917
888
|
return filepath;
|
|
918
889
|
}
|
|
@@ -981,9 +952,9 @@ function createFriendlyLogger() {
|
|
|
981
952
|
};
|
|
982
953
|
}
|
|
983
954
|
function findCliRoot2() {
|
|
984
|
-
let dir =
|
|
955
|
+
let dir = path8.dirname(fileURLToPath2(import.meta.url));
|
|
985
956
|
for (let i = 0;i < 10; i++) {
|
|
986
|
-
const pkgPath =
|
|
957
|
+
const pkgPath = path8.join(dir, "package.json");
|
|
987
958
|
if (existsSync5(pkgPath)) {
|
|
988
959
|
try {
|
|
989
960
|
const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
@@ -992,24 +963,24 @@ function findCliRoot2() {
|
|
|
992
963
|
}
|
|
993
964
|
} catch {}
|
|
994
965
|
}
|
|
995
|
-
const parent =
|
|
966
|
+
const parent = path8.dirname(dir);
|
|
996
967
|
if (parent === dir)
|
|
997
968
|
break;
|
|
998
969
|
dir = parent;
|
|
999
970
|
}
|
|
1000
|
-
return
|
|
971
|
+
return path8.dirname(path8.dirname(fileURLToPath2(import.meta.url)));
|
|
1001
972
|
}
|
|
1002
973
|
function findNodeModules(cliRoot2) {
|
|
1003
|
-
const localNodeModules =
|
|
1004
|
-
if (existsSync5(
|
|
974
|
+
const localNodeModules = path8.join(cliRoot2, "node_modules");
|
|
975
|
+
if (existsSync5(path8.join(localNodeModules, "react"))) {
|
|
1005
976
|
return localNodeModules;
|
|
1006
977
|
}
|
|
1007
978
|
let dir = cliRoot2;
|
|
1008
979
|
for (let i = 0;i < 10; i++) {
|
|
1009
|
-
const parent =
|
|
980
|
+
const parent = path8.dirname(dir);
|
|
1010
981
|
if (parent === dir)
|
|
1011
982
|
break;
|
|
1012
|
-
if (
|
|
983
|
+
if (path8.basename(parent) === "node_modules" && existsSync5(path8.join(parent, "react"))) {
|
|
1013
984
|
return parent;
|
|
1014
985
|
}
|
|
1015
986
|
dir = parent;
|
|
@@ -1018,19 +989,19 @@ function findNodeModules(cliRoot2) {
|
|
|
1018
989
|
}
|
|
1019
990
|
var cliRoot2 = findCliRoot2();
|
|
1020
991
|
var cliNodeModules = findNodeModules(cliRoot2);
|
|
1021
|
-
var srcRoot2 =
|
|
992
|
+
var srcRoot2 = path8.join(cliRoot2, "src");
|
|
1022
993
|
async function createViteConfig(options) {
|
|
1023
994
|
const { rootDir, mode, port, include, base, debug } = options;
|
|
1024
|
-
const cacheDir = await ensureCacheDir(rootDir);
|
|
1025
995
|
const config = loadConfig(rootDir);
|
|
1026
996
|
const debugCollector = debug ? createDebugCollector(rootDir) : null;
|
|
1027
997
|
if (debugCollector) {
|
|
1028
998
|
debugCollector.startPhase("configLoad");
|
|
1029
999
|
}
|
|
1000
|
+
const globalCacheDir = path8.join(os.homedir(), ".cache/prev/deps");
|
|
1030
1001
|
return {
|
|
1031
1002
|
root: rootDir,
|
|
1032
1003
|
mode,
|
|
1033
|
-
cacheDir,
|
|
1004
|
+
cacheDir: globalCacheDir,
|
|
1034
1005
|
base: base || "/",
|
|
1035
1006
|
customLogger: createFriendlyLogger(),
|
|
1036
1007
|
logLevel: mode === "production" ? "silent" : "info",
|
|
@@ -1043,8 +1014,8 @@ async function createViteConfig(options) {
|
|
|
1043
1014
|
rehypePlugins: [rehypeHighlight],
|
|
1044
1015
|
providerImportSource: "@mdx-js/react",
|
|
1045
1016
|
include: [
|
|
1046
|
-
|
|
1047
|
-
|
|
1017
|
+
path8.join(rootDir, "**/*.md"),
|
|
1018
|
+
path8.join(rootDir, "**/*.mdx")
|
|
1048
1019
|
],
|
|
1049
1020
|
exclude: [
|
|
1050
1021
|
"**/node_modules/**",
|
|
@@ -1092,7 +1063,7 @@ async function createViteConfig(options) {
|
|
|
1092
1063
|
if (urlPath.startsWith("/__") || urlPath.startsWith("/@") || urlPath.startsWith("/node_modules") || urlPath.includes(".")) {
|
|
1093
1064
|
return next();
|
|
1094
1065
|
}
|
|
1095
|
-
const indexPath =
|
|
1066
|
+
const indexPath = path8.join(srcRoot2, "theme/index.html");
|
|
1096
1067
|
if (existsSync5(indexPath)) {
|
|
1097
1068
|
server.transformIndexHtml(req.url, readFileSync4(indexPath, "utf-8")).then((html) => {
|
|
1098
1069
|
res.setHeader("Content-Type", "text/html");
|
|
@@ -1110,8 +1081,8 @@ async function createViteConfig(options) {
|
|
|
1110
1081
|
resolveId(id) {
|
|
1111
1082
|
if (id.startsWith("/_preview/")) {
|
|
1112
1083
|
const relativePath = id.slice("/_preview/".length);
|
|
1113
|
-
const previewsDir =
|
|
1114
|
-
const resolved =
|
|
1084
|
+
const previewsDir = path8.join(rootDir, "previews");
|
|
1085
|
+
const resolved = path8.resolve(previewsDir, relativePath);
|
|
1115
1086
|
if (resolved.startsWith(previewsDir)) {
|
|
1116
1087
|
return resolved;
|
|
1117
1088
|
}
|
|
@@ -1121,7 +1092,7 @@ async function createViteConfig(options) {
|
|
|
1121
1092
|
server.middlewares.use(async (req, res, next) => {
|
|
1122
1093
|
const urlPath = req.url?.split("?")[0] || "";
|
|
1123
1094
|
if (urlPath === "/_preview-runtime") {
|
|
1124
|
-
const templatePath =
|
|
1095
|
+
const templatePath = path8.join(srcRoot2, "preview-runtime/template.html");
|
|
1125
1096
|
if (existsSync5(templatePath)) {
|
|
1126
1097
|
const html = readFileSync4(templatePath, "utf-8");
|
|
1127
1098
|
res.setHeader("Content-Type", "text/html");
|
|
@@ -1131,8 +1102,8 @@ async function createViteConfig(options) {
|
|
|
1131
1102
|
}
|
|
1132
1103
|
if (urlPath.startsWith("/_preview-config/")) {
|
|
1133
1104
|
const previewName = decodeURIComponent(urlPath.slice("/_preview-config/".length));
|
|
1134
|
-
const previewsDir =
|
|
1135
|
-
const previewDir =
|
|
1105
|
+
const previewsDir = path8.join(rootDir, "previews");
|
|
1106
|
+
const previewDir = path8.resolve(previewsDir, previewName);
|
|
1136
1107
|
if (!previewDir.startsWith(previewsDir)) {
|
|
1137
1108
|
res.statusCode = 403;
|
|
1138
1109
|
res.end("Forbidden");
|
|
@@ -1153,11 +1124,11 @@ async function createViteConfig(options) {
|
|
|
1153
1124
|
}
|
|
1154
1125
|
}
|
|
1155
1126
|
if (urlPath.startsWith("/_preview/")) {
|
|
1156
|
-
const isHtmlRequest = !
|
|
1127
|
+
const isHtmlRequest = !path8.extname(urlPath) || urlPath.endsWith("/");
|
|
1157
1128
|
if (isHtmlRequest) {
|
|
1158
1129
|
const previewName = decodeURIComponent(urlPath.slice("/_preview/".length).replace(/\/$/, ""));
|
|
1159
|
-
const previewsDir =
|
|
1160
|
-
const htmlPath =
|
|
1130
|
+
const previewsDir = path8.join(rootDir, "previews");
|
|
1131
|
+
const htmlPath = path8.resolve(previewsDir, previewName, "index.html");
|
|
1161
1132
|
if (!htmlPath.startsWith(previewsDir)) {
|
|
1162
1133
|
return next();
|
|
1163
1134
|
}
|
|
@@ -1184,15 +1155,15 @@ async function createViteConfig(options) {
|
|
|
1184
1155
|
],
|
|
1185
1156
|
resolve: {
|
|
1186
1157
|
alias: {
|
|
1187
|
-
"@prev/ui":
|
|
1188
|
-
"@prev/theme":
|
|
1189
|
-
react:
|
|
1190
|
-
"react-dom":
|
|
1191
|
-
"@tanstack/react-router":
|
|
1192
|
-
"@mdx-js/react":
|
|
1193
|
-
mermaid:
|
|
1194
|
-
dayjs:
|
|
1195
|
-
"@terrastruct/d2":
|
|
1158
|
+
"@prev/ui": path8.join(srcRoot2, "ui"),
|
|
1159
|
+
"@prev/theme": path8.join(srcRoot2, "theme"),
|
|
1160
|
+
react: path8.join(cliNodeModules, "react"),
|
|
1161
|
+
"react-dom": path8.join(cliNodeModules, "react-dom"),
|
|
1162
|
+
"@tanstack/react-router": path8.join(cliNodeModules, "@tanstack/react-router"),
|
|
1163
|
+
"@mdx-js/react": path8.join(cliNodeModules, "@mdx-js/react"),
|
|
1164
|
+
mermaid: path8.join(cliNodeModules, "mermaid"),
|
|
1165
|
+
dayjs: path8.join(cliNodeModules, "dayjs"),
|
|
1166
|
+
"@terrastruct/d2": path8.join(cliNodeModules, "@terrastruct/d2")
|
|
1196
1167
|
},
|
|
1197
1168
|
dedupe: [
|
|
1198
1169
|
"react",
|
|
@@ -1202,16 +1173,11 @@ async function createViteConfig(options) {
|
|
|
1202
1173
|
},
|
|
1203
1174
|
optimizeDeps: {
|
|
1204
1175
|
noDiscovery: true,
|
|
1176
|
+
holdUntilCrawlEnd: false,
|
|
1205
1177
|
include: [
|
|
1206
|
-
"react",
|
|
1207
|
-
"react-dom",
|
|
1208
1178
|
"react-dom/client",
|
|
1209
|
-
"react/jsx-runtime",
|
|
1210
|
-
"react/jsx-dev-runtime",
|
|
1211
|
-
"@tanstack/react-router",
|
|
1212
1179
|
"use-sync-external-store",
|
|
1213
|
-
"use-sync-external-store/shim/with-selector.js"
|
|
1214
|
-
"@mdx-js/react"
|
|
1180
|
+
"use-sync-external-store/shim/with-selector.js"
|
|
1215
1181
|
],
|
|
1216
1182
|
exclude: [
|
|
1217
1183
|
"virtual:prev-config",
|
|
@@ -1232,8 +1198,8 @@ async function createViteConfig(options) {
|
|
|
1232
1198
|
},
|
|
1233
1199
|
warmup: {
|
|
1234
1200
|
clientFiles: [
|
|
1235
|
-
|
|
1236
|
-
|
|
1201
|
+
path8.join(srcRoot2, "theme/entry.tsx"),
|
|
1202
|
+
path8.join(srcRoot2, "theme/styles.css")
|
|
1237
1203
|
]
|
|
1238
1204
|
}
|
|
1239
1205
|
},
|
|
@@ -1242,12 +1208,12 @@ async function createViteConfig(options) {
|
|
|
1242
1208
|
strictPort: false
|
|
1243
1209
|
},
|
|
1244
1210
|
build: {
|
|
1245
|
-
outDir:
|
|
1211
|
+
outDir: path8.join(rootDir, "dist"),
|
|
1246
1212
|
reportCompressedSize: false,
|
|
1247
1213
|
chunkSizeWarningLimit: 1e4,
|
|
1248
1214
|
rollupOptions: {
|
|
1249
1215
|
input: {
|
|
1250
|
-
main:
|
|
1216
|
+
main: path8.join(srcRoot2, "theme/index.html")
|
|
1251
1217
|
}
|
|
1252
1218
|
}
|
|
1253
1219
|
},
|
|
@@ -1290,9 +1256,9 @@ async function findAvailablePort(minPort, maxPort) {
|
|
|
1290
1256
|
}
|
|
1291
1257
|
|
|
1292
1258
|
// src/vite/start.ts
|
|
1293
|
-
import { exec
|
|
1259
|
+
import { exec } from "child_process";
|
|
1294
1260
|
import { existsSync as existsSync6, rmSync as rmSync2, copyFileSync } from "fs";
|
|
1295
|
-
import
|
|
1261
|
+
import path9 from "path";
|
|
1296
1262
|
function printWelcome(type) {
|
|
1297
1263
|
console.log();
|
|
1298
1264
|
console.log(" ✨ prev");
|
|
@@ -1321,12 +1287,12 @@ function printReady() {
|
|
|
1321
1287
|
function openBrowser(url) {
|
|
1322
1288
|
const platform = process.platform;
|
|
1323
1289
|
const cmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
|
|
1324
|
-
|
|
1290
|
+
exec(`${cmd} ${url}`);
|
|
1325
1291
|
console.log(` ↗ Opened ${url}`);
|
|
1326
1292
|
}
|
|
1327
1293
|
function clearCache(rootDir) {
|
|
1328
|
-
const viteCacheDir =
|
|
1329
|
-
const nodeModulesVite =
|
|
1294
|
+
const viteCacheDir = path9.join(rootDir, ".vite");
|
|
1295
|
+
const nodeModulesVite = path9.join(rootDir, "node_modules", ".vite");
|
|
1330
1296
|
let cleared = 0;
|
|
1331
1297
|
if (existsSync6(viteCacheDir)) {
|
|
1332
1298
|
rmSync2(viteCacheDir, { recursive: true });
|
|
@@ -1385,6 +1351,8 @@ async function startDev(rootDir, options = {}) {
|
|
|
1385
1351
|
});
|
|
1386
1352
|
const server = await createServer2(config);
|
|
1387
1353
|
await server.listen();
|
|
1354
|
+
const warmupPort = server.config.server.port;
|
|
1355
|
+
fetch(`http://localhost:${warmupPort}/`).catch(() => {});
|
|
1388
1356
|
const debugCollector = getDebugCollector();
|
|
1389
1357
|
if (debugCollector) {
|
|
1390
1358
|
debugCollector.startPhase("serverReady");
|
|
@@ -1449,9 +1417,9 @@ async function buildSite(rootDir, options = {}) {
|
|
|
1449
1417
|
const reportPath = debugCollector.writeReport();
|
|
1450
1418
|
console.log(` \uD83D\uDCCA Debug trace written to: ${reportPath}`);
|
|
1451
1419
|
}
|
|
1452
|
-
const distDir =
|
|
1453
|
-
const indexPath =
|
|
1454
|
-
const notFoundPath =
|
|
1420
|
+
const distDir = path9.join(rootDir, "dist");
|
|
1421
|
+
const indexPath = path9.join(distDir, "index.html");
|
|
1422
|
+
const notFoundPath = path9.join(distDir, "404.html");
|
|
1455
1423
|
if (existsSync6(indexPath)) {
|
|
1456
1424
|
copyFileSync(indexPath, notFoundPath);
|
|
1457
1425
|
}
|
|
@@ -1484,6 +1452,52 @@ async function previewSite(rootDir, options = {}) {
|
|
|
1484
1452
|
return server;
|
|
1485
1453
|
}
|
|
1486
1454
|
|
|
1455
|
+
// src/utils/cache.ts
|
|
1456
|
+
import { createHash } from "crypto";
|
|
1457
|
+
import { readdir, rm, stat, mkdir } from "fs/promises";
|
|
1458
|
+
import { exec as exec2 } from "child_process";
|
|
1459
|
+
import { promisify } from "util";
|
|
1460
|
+
import path10 from "path";
|
|
1461
|
+
import os2 from "os";
|
|
1462
|
+
var execAsync = promisify(exec2);
|
|
1463
|
+
var DEFAULT_CACHE_ROOT = path10.join(os2.homedir(), ".cache/prev");
|
|
1464
|
+
async function getCacheDir(rootDir, branch) {
|
|
1465
|
+
const resolvedBranch = branch ?? await getCurrentBranch(rootDir);
|
|
1466
|
+
const hash = createHash("sha1").update(`${rootDir}:${resolvedBranch}`).digest("hex").slice(0, 12);
|
|
1467
|
+
return path10.join(DEFAULT_CACHE_ROOT, hash);
|
|
1468
|
+
}
|
|
1469
|
+
async function getCurrentBranch(rootDir) {
|
|
1470
|
+
try {
|
|
1471
|
+
const { stdout } = await execAsync("git branch --show-current", { cwd: rootDir });
|
|
1472
|
+
return stdout.trim() || "detached";
|
|
1473
|
+
} catch {
|
|
1474
|
+
return "no-git";
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
async function cleanCache(options) {
|
|
1478
|
+
const cacheRoot = options.cacheRoot ?? DEFAULT_CACHE_ROOT;
|
|
1479
|
+
const maxAge = options.maxAgeDays * 24 * 60 * 60 * 1000;
|
|
1480
|
+
const now = Date.now();
|
|
1481
|
+
let dirs;
|
|
1482
|
+
try {
|
|
1483
|
+
dirs = await readdir(cacheRoot);
|
|
1484
|
+
} catch {
|
|
1485
|
+
return 0;
|
|
1486
|
+
}
|
|
1487
|
+
let removed = 0;
|
|
1488
|
+
for (const dir of dirs) {
|
|
1489
|
+
const fullPath = path10.join(cacheRoot, dir);
|
|
1490
|
+
try {
|
|
1491
|
+
const info = await stat(fullPath);
|
|
1492
|
+
if (info.isDirectory() && now - info.mtimeMs > maxAge) {
|
|
1493
|
+
await rm(fullPath, { recursive: true });
|
|
1494
|
+
removed++;
|
|
1495
|
+
}
|
|
1496
|
+
} catch {}
|
|
1497
|
+
}
|
|
1498
|
+
return removed;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1487
1501
|
// src/cli.ts
|
|
1488
1502
|
import yaml2 from "js-yaml";
|
|
1489
1503
|
function getVersion() {
|
package/dist/ui/button.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { type VariantProps } from 'class-variance-authority';
|
|
3
3
|
declare const buttonVariants: (props?: ({
|
|
4
|
-
variant?: "
|
|
4
|
+
variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
5
5
|
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
7
|
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|