prev-cli 0.16.3 → 0.17.0
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 +119 -0
- package/package.json +1 -1
- package/src/theme/Preview.tsx +2 -2
- package/src/theme/Toolbar.tsx +4 -2
- package/src/theme/entry.tsx +30 -3
package/dist/cli.js
CHANGED
|
@@ -859,6 +859,28 @@ async function createViteConfig(options) {
|
|
|
859
859
|
});
|
|
860
860
|
}
|
|
861
861
|
},
|
|
862
|
+
{
|
|
863
|
+
name: "prev-spa-fallback",
|
|
864
|
+
configureServer(server) {
|
|
865
|
+
return () => {
|
|
866
|
+
server.middlewares.use((req, res, next) => {
|
|
867
|
+
const urlPath = req.url?.split("?")[0] || "";
|
|
868
|
+
if (urlPath.startsWith("/__") || urlPath.startsWith("/@") || urlPath.startsWith("/node_modules") || urlPath.includes(".")) {
|
|
869
|
+
return next();
|
|
870
|
+
}
|
|
871
|
+
const indexPath = path7.join(srcRoot2, "theme/index.html");
|
|
872
|
+
if (existsSync5(indexPath)) {
|
|
873
|
+
server.transformIndexHtml(req.url, readFileSync4(indexPath, "utf-8")).then((html) => {
|
|
874
|
+
res.setHeader("Content-Type", "text/html");
|
|
875
|
+
res.end(html);
|
|
876
|
+
}).catch(next);
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
next();
|
|
880
|
+
});
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
},
|
|
862
884
|
{
|
|
863
885
|
name: "prev-preview-server",
|
|
864
886
|
resolveId(id) {
|
|
@@ -1171,6 +1193,7 @@ async function previewSite(rootDir, options = {}) {
|
|
|
1171
1193
|
}
|
|
1172
1194
|
|
|
1173
1195
|
// src/cli.ts
|
|
1196
|
+
import yaml2 from "js-yaml";
|
|
1174
1197
|
function getVersion() {
|
|
1175
1198
|
try {
|
|
1176
1199
|
let dir = path9.dirname(fileURLToPath3(import.meta.url));
|
|
@@ -1209,9 +1232,16 @@ Usage:
|
|
|
1209
1232
|
prev build [options] Build for production
|
|
1210
1233
|
prev preview [options] Preview production build
|
|
1211
1234
|
prev create [name] Create preview in previews/<name>/ (default: "example")
|
|
1235
|
+
prev config [subcommand] Manage configuration
|
|
1212
1236
|
prev clearcache Clear Vite cache (.vite directory)
|
|
1213
1237
|
prev clean [options] Remove old prev-cli caches
|
|
1214
1238
|
|
|
1239
|
+
Config subcommands:
|
|
1240
|
+
prev config Show current configuration
|
|
1241
|
+
prev config show Show current configuration (same as above)
|
|
1242
|
+
prev config init Create .prev.yaml with defaults
|
|
1243
|
+
prev config path Show config file path
|
|
1244
|
+
|
|
1215
1245
|
Options:
|
|
1216
1246
|
-c, --cwd <path> Set working directory
|
|
1217
1247
|
-p, --port <port> Specify port (dev/preview)
|
|
@@ -1318,6 +1348,92 @@ async function clearViteCache(rootDir2) {
|
|
|
1318
1348
|
Cleared ${cleared} cache director${cleared === 1 ? "y" : "ies"}`);
|
|
1319
1349
|
}
|
|
1320
1350
|
}
|
|
1351
|
+
function handleConfig(rootDir2, subcommand) {
|
|
1352
|
+
const configPath = findConfigFile(rootDir2);
|
|
1353
|
+
const config = loadConfig(rootDir2);
|
|
1354
|
+
switch (subcommand) {
|
|
1355
|
+
case undefined:
|
|
1356
|
+
case "show": {
|
|
1357
|
+
console.log(`
|
|
1358
|
+
\uD83D\uDCC4 Configuration
|
|
1359
|
+
`);
|
|
1360
|
+
if (configPath) {
|
|
1361
|
+
console.log(` File: ${configPath}
|
|
1362
|
+
`);
|
|
1363
|
+
} else {
|
|
1364
|
+
console.log(` File: (none - using defaults)
|
|
1365
|
+
`);
|
|
1366
|
+
}
|
|
1367
|
+
const yamlOutput = yaml2.dump(config, {
|
|
1368
|
+
indent: 2,
|
|
1369
|
+
lineWidth: -1,
|
|
1370
|
+
quotingType: '"',
|
|
1371
|
+
forceQuotes: false
|
|
1372
|
+
});
|
|
1373
|
+
const indented = yamlOutput.split(`
|
|
1374
|
+
`).map((line) => ` ${line}`).join(`
|
|
1375
|
+
`);
|
|
1376
|
+
console.log(indented);
|
|
1377
|
+
if (!configPath) {
|
|
1378
|
+
console.log(`
|
|
1379
|
+
Run 'prev config init' to create a config file.
|
|
1380
|
+
`);
|
|
1381
|
+
}
|
|
1382
|
+
break;
|
|
1383
|
+
}
|
|
1384
|
+
case "init": {
|
|
1385
|
+
const targetPath = path9.join(rootDir2, ".prev.yaml");
|
|
1386
|
+
if (configPath) {
|
|
1387
|
+
console.log(`
|
|
1388
|
+
Config already exists: ${configPath}
|
|
1389
|
+
`);
|
|
1390
|
+
process.exit(1);
|
|
1391
|
+
}
|
|
1392
|
+
const configContent = `# prev-cli configuration
|
|
1393
|
+
# See: https://github.com/lagz0ne/prev-cli
|
|
1394
|
+
|
|
1395
|
+
# Theme: light | dark | system
|
|
1396
|
+
theme: system
|
|
1397
|
+
|
|
1398
|
+
# Content width: constrained | full
|
|
1399
|
+
contentWidth: constrained
|
|
1400
|
+
|
|
1401
|
+
# Port for dev server (optional, can be overridden with -p flag)
|
|
1402
|
+
# port: 3000
|
|
1403
|
+
|
|
1404
|
+
# Hidden pages (glob patterns)
|
|
1405
|
+
hidden: []
|
|
1406
|
+
# - "internal/**"
|
|
1407
|
+
# - "wip-*.md"
|
|
1408
|
+
|
|
1409
|
+
# Custom page ordering per directory
|
|
1410
|
+
order: {}
|
|
1411
|
+
# "/":
|
|
1412
|
+
# - "getting-started.md"
|
|
1413
|
+
# - "guides/"
|
|
1414
|
+
`;
|
|
1415
|
+
writeFileSync4(targetPath, configContent, "utf-8");
|
|
1416
|
+
console.log(`
|
|
1417
|
+
✨ Created ${targetPath}
|
|
1418
|
+
`);
|
|
1419
|
+
break;
|
|
1420
|
+
}
|
|
1421
|
+
case "path": {
|
|
1422
|
+
if (configPath) {
|
|
1423
|
+
console.log(configPath);
|
|
1424
|
+
} else {
|
|
1425
|
+
console.log(`(no config file found in ${rootDir2})`);
|
|
1426
|
+
process.exit(1);
|
|
1427
|
+
}
|
|
1428
|
+
break;
|
|
1429
|
+
}
|
|
1430
|
+
default:
|
|
1431
|
+
console.error(`Unknown config subcommand: ${subcommand}`);
|
|
1432
|
+
console.log(`
|
|
1433
|
+
Available subcommands: show, init, path`);
|
|
1434
|
+
process.exit(1);
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1321
1437
|
function createPreview(rootDir2, name) {
|
|
1322
1438
|
const previewDir = path9.join(rootDir2, "previews", name);
|
|
1323
1439
|
if (existsSync7(previewDir)) {
|
|
@@ -1497,6 +1613,9 @@ async function main() {
|
|
|
1497
1613
|
case "clearcache":
|
|
1498
1614
|
await clearViteCache(rootDir);
|
|
1499
1615
|
break;
|
|
1616
|
+
case "config":
|
|
1617
|
+
handleConfig(rootDir, positionals[1]);
|
|
1618
|
+
break;
|
|
1500
1619
|
default:
|
|
1501
1620
|
console.error(`Unknown command: ${command}`);
|
|
1502
1621
|
printHelp();
|
package/package.json
CHANGED
package/src/theme/Preview.tsx
CHANGED
|
@@ -32,8 +32,8 @@ export function Preview({ src, height = 400, title, mode = 'wasm', showHeader =
|
|
|
32
32
|
|
|
33
33
|
const iframeRef = useRef<HTMLIFrameElement>(null)
|
|
34
34
|
|
|
35
|
-
// URL depends on mode
|
|
36
|
-
const previewUrl = mode === 'wasm' ?
|
|
35
|
+
// URL depends on mode - wasm mode needs src param
|
|
36
|
+
const previewUrl = mode === 'wasm' ? `/_preview-runtime?src=${src}` : `/_preview/${src}`
|
|
37
37
|
const displayTitle = title || src
|
|
38
38
|
|
|
39
39
|
// Calculate current width
|
package/src/theme/Toolbar.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useRef, useEffect } from 'react'
|
|
2
|
-
import { Link } from '@tanstack/react-router'
|
|
2
|
+
import { Link, useLocation } from '@tanstack/react-router'
|
|
3
3
|
import type { PageTree } from 'fumadocs-core/server'
|
|
4
4
|
import { previews } from 'virtual:prev-previews'
|
|
5
5
|
import { IconMenu2, IconLayoutGrid, IconSun, IconMoon, IconArrowsMaximize, IconArrowsMinimize } from '@tabler/icons-react'
|
|
@@ -20,6 +20,8 @@ export function Toolbar({ tree, onThemeToggle, onWidthToggle, isDark, isFullWidt
|
|
|
20
20
|
const [dragging, setDragging] = useState(false)
|
|
21
21
|
const dragStart = useRef({ x: 0, y: 0 })
|
|
22
22
|
const toolbarRef = useRef<HTMLDivElement>(null)
|
|
23
|
+
const location = useLocation()
|
|
24
|
+
const isOnPreviews = location.pathname.startsWith('/previews')
|
|
23
25
|
|
|
24
26
|
const handleMouseDown = (e: React.MouseEvent) => {
|
|
25
27
|
if ((e.target as HTMLElement).closest('button, a')) return
|
|
@@ -63,7 +65,7 @@ export function Toolbar({ tree, onThemeToggle, onWidthToggle, isDark, isFullWidt
|
|
|
63
65
|
</button>
|
|
64
66
|
|
|
65
67
|
{previews && previews.length > 0 && (
|
|
66
|
-
<Link to="/previews" className=
|
|
68
|
+
<Link to="/previews" className={`toolbar-btn ${isOnPreviews ? 'active' : ''}`} title="Previews">
|
|
67
69
|
<IconLayoutGrid size={18} />
|
|
68
70
|
</Link>
|
|
69
71
|
)}
|
package/src/theme/entry.tsx
CHANGED
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
createRootRoute,
|
|
7
7
|
createRoute,
|
|
8
8
|
Outlet,
|
|
9
|
+
redirect,
|
|
10
|
+
Navigate,
|
|
9
11
|
} from '@tanstack/react-router'
|
|
10
12
|
import { MDXProvider } from '@mdx-js/react'
|
|
11
13
|
import { pages, sidebar } from 'virtual:prev-pages'
|
|
@@ -257,6 +259,10 @@ const previewDetailRoute = createRoute({
|
|
|
257
259
|
component: PreviewPage,
|
|
258
260
|
})
|
|
259
261
|
|
|
262
|
+
// Check if we have an index page (route '/')
|
|
263
|
+
const hasIndexPage = pages.some((page: { route: string }) => page.route === '/')
|
|
264
|
+
const firstPage = pages[0] as { route: string; file: string; title?: string; description?: string; frontmatter?: Record<string, unknown> } | undefined
|
|
265
|
+
|
|
260
266
|
// Create routes from pages
|
|
261
267
|
const pageRoutes = pages.map((page: { route: string; file: string; title?: string; description?: string; frontmatter?: Record<string, unknown> }) => {
|
|
262
268
|
const Component = getPageComponent(page.file)
|
|
@@ -272,9 +278,30 @@ const pageRoutes = pages.map((page: { route: string; file: string; title?: strin
|
|
|
272
278
|
})
|
|
273
279
|
})
|
|
274
280
|
|
|
275
|
-
//
|
|
276
|
-
const
|
|
277
|
-
|
|
281
|
+
// If no index page exists, create a redirect from '/' to the first page
|
|
282
|
+
const indexRedirectRoute = !hasIndexPage && firstPage ? createRoute({
|
|
283
|
+
getParentRoute: () => rootRoute,
|
|
284
|
+
path: '/',
|
|
285
|
+
component: () => <Navigate to={firstPage.route} />,
|
|
286
|
+
}) : null
|
|
287
|
+
|
|
288
|
+
// Not found component - redirect to first page or index
|
|
289
|
+
function NotFoundPage() {
|
|
290
|
+
const targetRoute = firstPage?.route || '/'
|
|
291
|
+
return <Navigate to={targetRoute} />
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Create router with notFoundRoute
|
|
295
|
+
const routeTree = rootRoute.addChildren([
|
|
296
|
+
previewsRoute,
|
|
297
|
+
previewDetailRoute,
|
|
298
|
+
...(indexRedirectRoute ? [indexRedirectRoute] : []),
|
|
299
|
+
...pageRoutes,
|
|
300
|
+
])
|
|
301
|
+
const router = createRouter({
|
|
302
|
+
routeTree,
|
|
303
|
+
defaultNotFoundComponent: NotFoundPage,
|
|
304
|
+
})
|
|
278
305
|
|
|
279
306
|
// Mount app - RouterProvider must be outermost so TanStack Router context is available
|
|
280
307
|
const container = document.getElementById('root')
|