prev-cli 0.24.6 → 0.24.7
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 +137 -144
- 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
|
}
|
|
@@ -182,7 +132,7 @@ async function scanPages(rootDir, options = {}) {
|
|
|
182
132
|
const routeMap = new Map;
|
|
183
133
|
for (const file of filteredFiles) {
|
|
184
134
|
const route = fileToRoute(file);
|
|
185
|
-
const basename =
|
|
135
|
+
const basename = path.basename(file, path.extname(file)).toLowerCase();
|
|
186
136
|
const priority = basename === "index" ? 1 : basename === "readme" ? 2 : 0;
|
|
187
137
|
const existing = routeMap.get(route);
|
|
188
138
|
if (!existing || priority > 0 && priority < existing.priority) {
|
|
@@ -191,7 +141,7 @@ async function scanPages(rootDir, options = {}) {
|
|
|
191
141
|
}
|
|
192
142
|
const pages = [];
|
|
193
143
|
for (const { file } of routeMap.values()) {
|
|
194
|
-
const fullPath =
|
|
144
|
+
const fullPath = path.join(rootDir, file);
|
|
195
145
|
const rawContent = await readFile(fullPath, "utf-8");
|
|
196
146
|
const { frontmatter, content } = parseFrontmatter(rawContent);
|
|
197
147
|
const title = extractTitle(content, file, frontmatter);
|
|
@@ -221,12 +171,12 @@ function extractTitle(content, file, frontmatter) {
|
|
|
221
171
|
if (match) {
|
|
222
172
|
return match[1].trim();
|
|
223
173
|
}
|
|
224
|
-
const basename =
|
|
174
|
+
const basename = path.basename(file, path.extname(file)).toLowerCase();
|
|
225
175
|
if (isIndexFile(basename)) {
|
|
226
|
-
const dirname =
|
|
227
|
-
return dirname === "." ? "Home" : capitalize(
|
|
176
|
+
const dirname = path.dirname(file);
|
|
177
|
+
return dirname === "." ? "Home" : capitalize(path.basename(dirname));
|
|
228
178
|
}
|
|
229
|
-
return capitalize(
|
|
179
|
+
return capitalize(path.basename(file, path.extname(file)));
|
|
230
180
|
}
|
|
231
181
|
function capitalize(str) {
|
|
232
182
|
return str.charAt(0).toUpperCase() + str.slice(1).replace(/-/g, " ");
|
|
@@ -295,7 +245,7 @@ function pagesPlugin(rootDir, options = {}) {
|
|
|
295
245
|
if (id === RESOLVED_VIRTUAL_MODULES_ID) {
|
|
296
246
|
const pages = await getPages();
|
|
297
247
|
const imports = pages.map((page, i) => {
|
|
298
|
-
const absolutePath =
|
|
248
|
+
const absolutePath = path2.join(rootDir, page.file);
|
|
299
249
|
return `import * as _page${i} from ${JSON.stringify(absolutePath)};`;
|
|
300
250
|
}).join(`
|
|
301
251
|
`);
|
|
@@ -331,13 +281,13 @@ ${entries}
|
|
|
331
281
|
}
|
|
332
282
|
|
|
333
283
|
// src/vite/plugins/entry-plugin.ts
|
|
334
|
-
import
|
|
284
|
+
import path3 from "path";
|
|
335
285
|
import { fileURLToPath } from "url";
|
|
336
286
|
import { existsSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
337
287
|
function findCliRoot() {
|
|
338
|
-
let dir =
|
|
288
|
+
let dir = path3.dirname(fileURLToPath(import.meta.url));
|
|
339
289
|
for (let i = 0;i < 10; i++) {
|
|
340
|
-
const pkgPath =
|
|
290
|
+
const pkgPath = path3.join(dir, "package.json");
|
|
341
291
|
if (existsSync(pkgPath)) {
|
|
342
292
|
try {
|
|
343
293
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
@@ -346,15 +296,15 @@ function findCliRoot() {
|
|
|
346
296
|
}
|
|
347
297
|
} catch {}
|
|
348
298
|
}
|
|
349
|
-
const parent =
|
|
299
|
+
const parent = path3.dirname(dir);
|
|
350
300
|
if (parent === dir)
|
|
351
301
|
break;
|
|
352
302
|
dir = parent;
|
|
353
303
|
}
|
|
354
|
-
return
|
|
304
|
+
return path3.dirname(path3.dirname(fileURLToPath(import.meta.url)));
|
|
355
305
|
}
|
|
356
306
|
var cliRoot = findCliRoot();
|
|
357
|
-
var srcRoot =
|
|
307
|
+
var srcRoot = path3.join(cliRoot, "src");
|
|
358
308
|
function getHtml(entryPath, forBuild = false) {
|
|
359
309
|
const scriptSrc = forBuild ? entryPath : `/@fs${entryPath}`;
|
|
360
310
|
return `<!DOCTYPE html>
|
|
@@ -377,13 +327,13 @@ function getHtml(entryPath, forBuild = false) {
|
|
|
377
327
|
</html>`;
|
|
378
328
|
}
|
|
379
329
|
function entryPlugin(rootDir) {
|
|
380
|
-
const entryPath =
|
|
330
|
+
const entryPath = path3.join(srcRoot, "theme/entry.tsx");
|
|
381
331
|
let tempHtmlPath = null;
|
|
382
332
|
return {
|
|
383
333
|
name: "prev-entry",
|
|
384
334
|
config(config, { command }) {
|
|
385
335
|
if (command === "build" && rootDir) {
|
|
386
|
-
tempHtmlPath =
|
|
336
|
+
tempHtmlPath = path3.join(rootDir, "index.html");
|
|
387
337
|
writeFileSync(tempHtmlPath, getHtml(entryPath, true));
|
|
388
338
|
const existingInput = config.build?.rollupOptions?.input || {};
|
|
389
339
|
const inputObj = typeof existingInput === "string" ? { _original: existingInput } : Array.isArray(existingInput) ? Object.fromEntries(existingInput.map((f, i) => [`entry${i}`, f])) : existingInput;
|
|
@@ -430,10 +380,10 @@ function entryPlugin(rootDir) {
|
|
|
430
380
|
|
|
431
381
|
// src/vite/previews.ts
|
|
432
382
|
import fg2 from "fast-glob";
|
|
433
|
-
import
|
|
383
|
+
import path4 from "path";
|
|
434
384
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
435
385
|
async function scanPreviews(rootDir) {
|
|
436
|
-
const previewsDir =
|
|
386
|
+
const previewsDir = path4.join(rootDir, "previews");
|
|
437
387
|
if (!existsSync2(previewsDir)) {
|
|
438
388
|
return [];
|
|
439
389
|
}
|
|
@@ -443,17 +393,17 @@ async function scanPreviews(rootDir) {
|
|
|
443
393
|
});
|
|
444
394
|
const previewDirs = new Map;
|
|
445
395
|
for (const file of entryFiles) {
|
|
446
|
-
const dir =
|
|
396
|
+
const dir = path4.dirname(file);
|
|
447
397
|
if (!previewDirs.has(dir)) {
|
|
448
398
|
previewDirs.set(dir, file);
|
|
449
399
|
}
|
|
450
400
|
}
|
|
451
401
|
return Array.from(previewDirs.entries()).map(([dir, file]) => {
|
|
452
|
-
const name = dir === "." ?
|
|
402
|
+
const name = dir === "." ? path4.basename(previewsDir) : dir;
|
|
453
403
|
return {
|
|
454
404
|
name,
|
|
455
405
|
route: `/_preview/${name}`,
|
|
456
|
-
htmlPath:
|
|
406
|
+
htmlPath: path4.join(previewsDir, file)
|
|
457
407
|
};
|
|
458
408
|
});
|
|
459
409
|
}
|
|
@@ -463,8 +413,8 @@ async function scanPreviewFiles(previewDir) {
|
|
|
463
413
|
ignore: ["node_modules/**", "dist/**"]
|
|
464
414
|
});
|
|
465
415
|
return files.map((file) => {
|
|
466
|
-
const content = readFileSync2(
|
|
467
|
-
const ext =
|
|
416
|
+
const content = readFileSync2(path4.join(previewDir, file), "utf-8");
|
|
417
|
+
const ext = path4.extname(file).slice(1);
|
|
468
418
|
return {
|
|
469
419
|
path: file,
|
|
470
420
|
content,
|
|
@@ -608,7 +558,7 @@ async function buildPreviewHtml(config) {
|
|
|
608
558
|
|
|
609
559
|
// src/vite/plugins/previews-plugin.ts
|
|
610
560
|
import { existsSync as existsSync3, mkdirSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
611
|
-
import
|
|
561
|
+
import path5 from "path";
|
|
612
562
|
var VIRTUAL_MODULE_ID2 = "virtual:prev-previews";
|
|
613
563
|
var RESOLVED_VIRTUAL_MODULE_ID2 = "\x00" + VIRTUAL_MODULE_ID2;
|
|
614
564
|
function previewsPlugin(rootDir) {
|
|
@@ -641,10 +591,10 @@ function previewsPlugin(rootDir) {
|
|
|
641
591
|
async closeBundle() {
|
|
642
592
|
if (!isBuild)
|
|
643
593
|
return;
|
|
644
|
-
const distDir =
|
|
645
|
-
const targetDir =
|
|
646
|
-
const previewsDir =
|
|
647
|
-
const oldPreviewsDir =
|
|
594
|
+
const distDir = path5.join(rootDir, "dist");
|
|
595
|
+
const targetDir = path5.join(distDir, "_preview");
|
|
596
|
+
const previewsDir = path5.join(rootDir, "previews");
|
|
597
|
+
const oldPreviewsDir = path5.join(distDir, "previews");
|
|
648
598
|
if (existsSync3(oldPreviewsDir)) {
|
|
649
599
|
rmSync(oldPreviewsDir, { recursive: true });
|
|
650
600
|
}
|
|
@@ -657,7 +607,7 @@ function previewsPlugin(rootDir) {
|
|
|
657
607
|
console.log(`
|
|
658
608
|
Building ${previews.length} preview(s)...`);
|
|
659
609
|
for (const preview of previews) {
|
|
660
|
-
const previewDir =
|
|
610
|
+
const previewDir = path5.join(previewsDir, preview.name);
|
|
661
611
|
try {
|
|
662
612
|
const config = await buildPreviewConfig(previewDir);
|
|
663
613
|
const result = await buildPreviewHtml(config);
|
|
@@ -665,9 +615,9 @@ function previewsPlugin(rootDir) {
|
|
|
665
615
|
console.error(` ✗ ${preview.name}: ${result.error}`);
|
|
666
616
|
continue;
|
|
667
617
|
}
|
|
668
|
-
const outputDir =
|
|
618
|
+
const outputDir = path5.join(targetDir, preview.name);
|
|
669
619
|
mkdirSync(outputDir, { recursive: true });
|
|
670
|
-
writeFileSync2(
|
|
620
|
+
writeFileSync2(path5.join(outputDir, "index.html"), result.html);
|
|
671
621
|
console.log(` ✓ ${preview.name}`);
|
|
672
622
|
} catch (err) {
|
|
673
623
|
console.error(` ✗ ${preview.name}: ${err}`);
|
|
@@ -788,11 +738,11 @@ function validateConfig(raw) {
|
|
|
788
738
|
}
|
|
789
739
|
// src/config/loader.ts
|
|
790
740
|
import { readFileSync as readFileSync3, existsSync as existsSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
791
|
-
import
|
|
741
|
+
import path6 from "path";
|
|
792
742
|
import yaml from "js-yaml";
|
|
793
743
|
function findConfigFile(rootDir) {
|
|
794
|
-
const yamlPath =
|
|
795
|
-
const ymlPath =
|
|
744
|
+
const yamlPath = path6.join(rootDir, ".prev.yaml");
|
|
745
|
+
const ymlPath = path6.join(rootDir, ".prev.yml");
|
|
796
746
|
if (existsSync4(yamlPath))
|
|
797
747
|
return yamlPath;
|
|
798
748
|
if (existsSync4(ymlPath))
|
|
@@ -814,7 +764,7 @@ function loadConfig(rootDir) {
|
|
|
814
764
|
}
|
|
815
765
|
}
|
|
816
766
|
function saveConfig(rootDir, config) {
|
|
817
|
-
const configPath = findConfigFile(rootDir) ||
|
|
767
|
+
const configPath = findConfigFile(rootDir) || path6.join(rootDir, ".prev.yaml");
|
|
818
768
|
const content = yaml.dump(config, {
|
|
819
769
|
indent: 2,
|
|
820
770
|
lineWidth: -1,
|
|
@@ -830,7 +780,7 @@ function updateOrder(rootDir, pathKey, order) {
|
|
|
830
780
|
}
|
|
831
781
|
// src/utils/debug.ts
|
|
832
782
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
833
|
-
import
|
|
783
|
+
import path7 from "path";
|
|
834
784
|
|
|
835
785
|
class DebugCollector {
|
|
836
786
|
startTime;
|
|
@@ -889,7 +839,7 @@ class DebugCollector {
|
|
|
889
839
|
}
|
|
890
840
|
byDirectory[dir] = (byDirectory[dir] || 0) + 1;
|
|
891
841
|
}
|
|
892
|
-
const slowest = Object.entries(fileTimeAccum).map(([
|
|
842
|
+
const slowest = Object.entries(fileTimeAccum).map(([path8, totalMs]) => ({ path: path8, totalMs })).sort((a, b) => b.totalMs - a.totalMs).slice(0, 20);
|
|
893
843
|
return {
|
|
894
844
|
totalFiles: this.files.length,
|
|
895
845
|
byDirectory,
|
|
@@ -908,11 +858,11 @@ class DebugCollector {
|
|
|
908
858
|
files: this.files,
|
|
909
859
|
summary: this.generateSummary()
|
|
910
860
|
};
|
|
911
|
-
const debugDir =
|
|
861
|
+
const debugDir = path7.join(this.rootDir, ".prev-debug");
|
|
912
862
|
mkdirSync2(debugDir, { recursive: true });
|
|
913
863
|
const date = new Date;
|
|
914
864
|
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 =
|
|
865
|
+
const filepath = path7.join(debugDir, filename);
|
|
916
866
|
writeFileSync4(filepath, JSON.stringify(report, null, 2));
|
|
917
867
|
return filepath;
|
|
918
868
|
}
|
|
@@ -981,9 +931,9 @@ function createFriendlyLogger() {
|
|
|
981
931
|
};
|
|
982
932
|
}
|
|
983
933
|
function findCliRoot2() {
|
|
984
|
-
let dir =
|
|
934
|
+
let dir = path8.dirname(fileURLToPath2(import.meta.url));
|
|
985
935
|
for (let i = 0;i < 10; i++) {
|
|
986
|
-
const pkgPath =
|
|
936
|
+
const pkgPath = path8.join(dir, "package.json");
|
|
987
937
|
if (existsSync5(pkgPath)) {
|
|
988
938
|
try {
|
|
989
939
|
const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
@@ -992,24 +942,24 @@ function findCliRoot2() {
|
|
|
992
942
|
}
|
|
993
943
|
} catch {}
|
|
994
944
|
}
|
|
995
|
-
const parent =
|
|
945
|
+
const parent = path8.dirname(dir);
|
|
996
946
|
if (parent === dir)
|
|
997
947
|
break;
|
|
998
948
|
dir = parent;
|
|
999
949
|
}
|
|
1000
|
-
return
|
|
950
|
+
return path8.dirname(path8.dirname(fileURLToPath2(import.meta.url)));
|
|
1001
951
|
}
|
|
1002
952
|
function findNodeModules(cliRoot2) {
|
|
1003
|
-
const localNodeModules =
|
|
1004
|
-
if (existsSync5(
|
|
953
|
+
const localNodeModules = path8.join(cliRoot2, "node_modules");
|
|
954
|
+
if (existsSync5(path8.join(localNodeModules, "react"))) {
|
|
1005
955
|
return localNodeModules;
|
|
1006
956
|
}
|
|
1007
957
|
let dir = cliRoot2;
|
|
1008
958
|
for (let i = 0;i < 10; i++) {
|
|
1009
|
-
const parent =
|
|
959
|
+
const parent = path8.dirname(dir);
|
|
1010
960
|
if (parent === dir)
|
|
1011
961
|
break;
|
|
1012
|
-
if (
|
|
962
|
+
if (path8.basename(parent) === "node_modules" && existsSync5(path8.join(parent, "react"))) {
|
|
1013
963
|
return parent;
|
|
1014
964
|
}
|
|
1015
965
|
dir = parent;
|
|
@@ -1018,19 +968,19 @@ function findNodeModules(cliRoot2) {
|
|
|
1018
968
|
}
|
|
1019
969
|
var cliRoot2 = findCliRoot2();
|
|
1020
970
|
var cliNodeModules = findNodeModules(cliRoot2);
|
|
1021
|
-
var srcRoot2 =
|
|
971
|
+
var srcRoot2 = path8.join(cliRoot2, "src");
|
|
1022
972
|
async function createViteConfig(options) {
|
|
1023
973
|
const { rootDir, mode, port, include, base, debug } = options;
|
|
1024
|
-
const cacheDir = await ensureCacheDir(rootDir);
|
|
1025
974
|
const config = loadConfig(rootDir);
|
|
1026
975
|
const debugCollector = debug ? createDebugCollector(rootDir) : null;
|
|
1027
976
|
if (debugCollector) {
|
|
1028
977
|
debugCollector.startPhase("configLoad");
|
|
1029
978
|
}
|
|
979
|
+
const globalCacheDir = path8.join(os.homedir(), ".cache/prev/deps");
|
|
1030
980
|
return {
|
|
1031
981
|
root: rootDir,
|
|
1032
982
|
mode,
|
|
1033
|
-
cacheDir,
|
|
983
|
+
cacheDir: globalCacheDir,
|
|
1034
984
|
base: base || "/",
|
|
1035
985
|
customLogger: createFriendlyLogger(),
|
|
1036
986
|
logLevel: mode === "production" ? "silent" : "info",
|
|
@@ -1043,8 +993,8 @@ async function createViteConfig(options) {
|
|
|
1043
993
|
rehypePlugins: [rehypeHighlight],
|
|
1044
994
|
providerImportSource: "@mdx-js/react",
|
|
1045
995
|
include: [
|
|
1046
|
-
|
|
1047
|
-
|
|
996
|
+
path8.join(rootDir, "**/*.md"),
|
|
997
|
+
path8.join(rootDir, "**/*.mdx")
|
|
1048
998
|
],
|
|
1049
999
|
exclude: [
|
|
1050
1000
|
"**/node_modules/**",
|
|
@@ -1092,7 +1042,7 @@ async function createViteConfig(options) {
|
|
|
1092
1042
|
if (urlPath.startsWith("/__") || urlPath.startsWith("/@") || urlPath.startsWith("/node_modules") || urlPath.includes(".")) {
|
|
1093
1043
|
return next();
|
|
1094
1044
|
}
|
|
1095
|
-
const indexPath =
|
|
1045
|
+
const indexPath = path8.join(srcRoot2, "theme/index.html");
|
|
1096
1046
|
if (existsSync5(indexPath)) {
|
|
1097
1047
|
server.transformIndexHtml(req.url, readFileSync4(indexPath, "utf-8")).then((html) => {
|
|
1098
1048
|
res.setHeader("Content-Type", "text/html");
|
|
@@ -1110,8 +1060,8 @@ async function createViteConfig(options) {
|
|
|
1110
1060
|
resolveId(id) {
|
|
1111
1061
|
if (id.startsWith("/_preview/")) {
|
|
1112
1062
|
const relativePath = id.slice("/_preview/".length);
|
|
1113
|
-
const previewsDir =
|
|
1114
|
-
const resolved =
|
|
1063
|
+
const previewsDir = path8.join(rootDir, "previews");
|
|
1064
|
+
const resolved = path8.resolve(previewsDir, relativePath);
|
|
1115
1065
|
if (resolved.startsWith(previewsDir)) {
|
|
1116
1066
|
return resolved;
|
|
1117
1067
|
}
|
|
@@ -1121,7 +1071,7 @@ async function createViteConfig(options) {
|
|
|
1121
1071
|
server.middlewares.use(async (req, res, next) => {
|
|
1122
1072
|
const urlPath = req.url?.split("?")[0] || "";
|
|
1123
1073
|
if (urlPath === "/_preview-runtime") {
|
|
1124
|
-
const templatePath =
|
|
1074
|
+
const templatePath = path8.join(srcRoot2, "preview-runtime/template.html");
|
|
1125
1075
|
if (existsSync5(templatePath)) {
|
|
1126
1076
|
const html = readFileSync4(templatePath, "utf-8");
|
|
1127
1077
|
res.setHeader("Content-Type", "text/html");
|
|
@@ -1131,8 +1081,8 @@ async function createViteConfig(options) {
|
|
|
1131
1081
|
}
|
|
1132
1082
|
if (urlPath.startsWith("/_preview-config/")) {
|
|
1133
1083
|
const previewName = decodeURIComponent(urlPath.slice("/_preview-config/".length));
|
|
1134
|
-
const previewsDir =
|
|
1135
|
-
const previewDir =
|
|
1084
|
+
const previewsDir = path8.join(rootDir, "previews");
|
|
1085
|
+
const previewDir = path8.resolve(previewsDir, previewName);
|
|
1136
1086
|
if (!previewDir.startsWith(previewsDir)) {
|
|
1137
1087
|
res.statusCode = 403;
|
|
1138
1088
|
res.end("Forbidden");
|
|
@@ -1153,11 +1103,11 @@ async function createViteConfig(options) {
|
|
|
1153
1103
|
}
|
|
1154
1104
|
}
|
|
1155
1105
|
if (urlPath.startsWith("/_preview/")) {
|
|
1156
|
-
const isHtmlRequest = !
|
|
1106
|
+
const isHtmlRequest = !path8.extname(urlPath) || urlPath.endsWith("/");
|
|
1157
1107
|
if (isHtmlRequest) {
|
|
1158
1108
|
const previewName = decodeURIComponent(urlPath.slice("/_preview/".length).replace(/\/$/, ""));
|
|
1159
|
-
const previewsDir =
|
|
1160
|
-
const htmlPath =
|
|
1109
|
+
const previewsDir = path8.join(rootDir, "previews");
|
|
1110
|
+
const htmlPath = path8.resolve(previewsDir, previewName, "index.html");
|
|
1161
1111
|
if (!htmlPath.startsWith(previewsDir)) {
|
|
1162
1112
|
return next();
|
|
1163
1113
|
}
|
|
@@ -1184,15 +1134,15 @@ async function createViteConfig(options) {
|
|
|
1184
1134
|
],
|
|
1185
1135
|
resolve: {
|
|
1186
1136
|
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":
|
|
1137
|
+
"@prev/ui": path8.join(srcRoot2, "ui"),
|
|
1138
|
+
"@prev/theme": path8.join(srcRoot2, "theme"),
|
|
1139
|
+
react: path8.join(cliNodeModules, "react"),
|
|
1140
|
+
"react-dom": path8.join(cliNodeModules, "react-dom"),
|
|
1141
|
+
"@tanstack/react-router": path8.join(cliNodeModules, "@tanstack/react-router"),
|
|
1142
|
+
"@mdx-js/react": path8.join(cliNodeModules, "@mdx-js/react"),
|
|
1143
|
+
mermaid: path8.join(cliNodeModules, "mermaid"),
|
|
1144
|
+
dayjs: path8.join(cliNodeModules, "dayjs"),
|
|
1145
|
+
"@terrastruct/d2": path8.join(cliNodeModules, "@terrastruct/d2")
|
|
1196
1146
|
},
|
|
1197
1147
|
dedupe: [
|
|
1198
1148
|
"react",
|
|
@@ -1202,16 +1152,11 @@ async function createViteConfig(options) {
|
|
|
1202
1152
|
},
|
|
1203
1153
|
optimizeDeps: {
|
|
1204
1154
|
noDiscovery: true,
|
|
1155
|
+
holdUntilCrawlEnd: false,
|
|
1205
1156
|
include: [
|
|
1206
|
-
"react",
|
|
1207
|
-
"react-dom",
|
|
1208
1157
|
"react-dom/client",
|
|
1209
|
-
"react/jsx-runtime",
|
|
1210
|
-
"react/jsx-dev-runtime",
|
|
1211
|
-
"@tanstack/react-router",
|
|
1212
1158
|
"use-sync-external-store",
|
|
1213
|
-
"use-sync-external-store/shim/with-selector.js"
|
|
1214
|
-
"@mdx-js/react"
|
|
1159
|
+
"use-sync-external-store/shim/with-selector.js"
|
|
1215
1160
|
],
|
|
1216
1161
|
exclude: [
|
|
1217
1162
|
"virtual:prev-config",
|
|
@@ -1232,8 +1177,8 @@ async function createViteConfig(options) {
|
|
|
1232
1177
|
},
|
|
1233
1178
|
warmup: {
|
|
1234
1179
|
clientFiles: [
|
|
1235
|
-
|
|
1236
|
-
|
|
1180
|
+
path8.join(srcRoot2, "theme/entry.tsx"),
|
|
1181
|
+
path8.join(srcRoot2, "theme/styles.css")
|
|
1237
1182
|
]
|
|
1238
1183
|
}
|
|
1239
1184
|
},
|
|
@@ -1242,12 +1187,12 @@ async function createViteConfig(options) {
|
|
|
1242
1187
|
strictPort: false
|
|
1243
1188
|
},
|
|
1244
1189
|
build: {
|
|
1245
|
-
outDir:
|
|
1190
|
+
outDir: path8.join(rootDir, "dist"),
|
|
1246
1191
|
reportCompressedSize: false,
|
|
1247
1192
|
chunkSizeWarningLimit: 1e4,
|
|
1248
1193
|
rollupOptions: {
|
|
1249
1194
|
input: {
|
|
1250
|
-
main:
|
|
1195
|
+
main: path8.join(srcRoot2, "theme/index.html")
|
|
1251
1196
|
}
|
|
1252
1197
|
}
|
|
1253
1198
|
},
|
|
@@ -1290,9 +1235,9 @@ async function findAvailablePort(minPort, maxPort) {
|
|
|
1290
1235
|
}
|
|
1291
1236
|
|
|
1292
1237
|
// src/vite/start.ts
|
|
1293
|
-
import { exec
|
|
1238
|
+
import { exec } from "child_process";
|
|
1294
1239
|
import { existsSync as existsSync6, rmSync as rmSync2, copyFileSync } from "fs";
|
|
1295
|
-
import
|
|
1240
|
+
import path9 from "path";
|
|
1296
1241
|
function printWelcome(type) {
|
|
1297
1242
|
console.log();
|
|
1298
1243
|
console.log(" ✨ prev");
|
|
@@ -1321,12 +1266,12 @@ function printReady() {
|
|
|
1321
1266
|
function openBrowser(url) {
|
|
1322
1267
|
const platform = process.platform;
|
|
1323
1268
|
const cmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
|
|
1324
|
-
|
|
1269
|
+
exec(`${cmd} ${url}`);
|
|
1325
1270
|
console.log(` ↗ Opened ${url}`);
|
|
1326
1271
|
}
|
|
1327
1272
|
function clearCache(rootDir) {
|
|
1328
|
-
const viteCacheDir =
|
|
1329
|
-
const nodeModulesVite =
|
|
1273
|
+
const viteCacheDir = path9.join(rootDir, ".vite");
|
|
1274
|
+
const nodeModulesVite = path9.join(rootDir, "node_modules", ".vite");
|
|
1330
1275
|
let cleared = 0;
|
|
1331
1276
|
if (existsSync6(viteCacheDir)) {
|
|
1332
1277
|
rmSync2(viteCacheDir, { recursive: true });
|
|
@@ -1385,6 +1330,8 @@ async function startDev(rootDir, options = {}) {
|
|
|
1385
1330
|
});
|
|
1386
1331
|
const server = await createServer2(config);
|
|
1387
1332
|
await server.listen();
|
|
1333
|
+
const warmupPort = server.config.server.port;
|
|
1334
|
+
fetch(`http://localhost:${warmupPort}/`).catch(() => {});
|
|
1388
1335
|
const debugCollector = getDebugCollector();
|
|
1389
1336
|
if (debugCollector) {
|
|
1390
1337
|
debugCollector.startPhase("serverReady");
|
|
@@ -1449,9 +1396,9 @@ async function buildSite(rootDir, options = {}) {
|
|
|
1449
1396
|
const reportPath = debugCollector.writeReport();
|
|
1450
1397
|
console.log(` \uD83D\uDCCA Debug trace written to: ${reportPath}`);
|
|
1451
1398
|
}
|
|
1452
|
-
const distDir =
|
|
1453
|
-
const indexPath =
|
|
1454
|
-
const notFoundPath =
|
|
1399
|
+
const distDir = path9.join(rootDir, "dist");
|
|
1400
|
+
const indexPath = path9.join(distDir, "index.html");
|
|
1401
|
+
const notFoundPath = path9.join(distDir, "404.html");
|
|
1455
1402
|
if (existsSync6(indexPath)) {
|
|
1456
1403
|
copyFileSync(indexPath, notFoundPath);
|
|
1457
1404
|
}
|
|
@@ -1484,6 +1431,52 @@ async function previewSite(rootDir, options = {}) {
|
|
|
1484
1431
|
return server;
|
|
1485
1432
|
}
|
|
1486
1433
|
|
|
1434
|
+
// src/utils/cache.ts
|
|
1435
|
+
import { createHash } from "crypto";
|
|
1436
|
+
import { readdir, rm, stat, mkdir } from "fs/promises";
|
|
1437
|
+
import { exec as exec2 } from "child_process";
|
|
1438
|
+
import { promisify } from "util";
|
|
1439
|
+
import path10 from "path";
|
|
1440
|
+
import os2 from "os";
|
|
1441
|
+
var execAsync = promisify(exec2);
|
|
1442
|
+
var DEFAULT_CACHE_ROOT = path10.join(os2.homedir(), ".cache/prev");
|
|
1443
|
+
async function getCacheDir(rootDir, branch) {
|
|
1444
|
+
const resolvedBranch = branch ?? await getCurrentBranch(rootDir);
|
|
1445
|
+
const hash = createHash("sha1").update(`${rootDir}:${resolvedBranch}`).digest("hex").slice(0, 12);
|
|
1446
|
+
return path10.join(DEFAULT_CACHE_ROOT, hash);
|
|
1447
|
+
}
|
|
1448
|
+
async function getCurrentBranch(rootDir) {
|
|
1449
|
+
try {
|
|
1450
|
+
const { stdout } = await execAsync("git branch --show-current", { cwd: rootDir });
|
|
1451
|
+
return stdout.trim() || "detached";
|
|
1452
|
+
} catch {
|
|
1453
|
+
return "no-git";
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
async function cleanCache(options) {
|
|
1457
|
+
const cacheRoot = options.cacheRoot ?? DEFAULT_CACHE_ROOT;
|
|
1458
|
+
const maxAge = options.maxAgeDays * 24 * 60 * 60 * 1000;
|
|
1459
|
+
const now = Date.now();
|
|
1460
|
+
let dirs;
|
|
1461
|
+
try {
|
|
1462
|
+
dirs = await readdir(cacheRoot);
|
|
1463
|
+
} catch {
|
|
1464
|
+
return 0;
|
|
1465
|
+
}
|
|
1466
|
+
let removed = 0;
|
|
1467
|
+
for (const dir of dirs) {
|
|
1468
|
+
const fullPath = path10.join(cacheRoot, dir);
|
|
1469
|
+
try {
|
|
1470
|
+
const info = await stat(fullPath);
|
|
1471
|
+
if (info.isDirectory() && now - info.mtimeMs > maxAge) {
|
|
1472
|
+
await rm(fullPath, { recursive: true });
|
|
1473
|
+
removed++;
|
|
1474
|
+
}
|
|
1475
|
+
} catch {}
|
|
1476
|
+
}
|
|
1477
|
+
return removed;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1487
1480
|
// src/cli.ts
|
|
1488
1481
|
import yaml2 from "js-yaml";
|
|
1489
1482
|
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> {
|