@frontlit/preview 0.1.0-alpha.1 → 0.1.0-alpha.3
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/README.md +113 -17
- package/package.json +2 -2
- package/server.mjs +10 -2
- package/site.mjs +26 -1
package/README.md
CHANGED
|
@@ -1,40 +1,136 @@
|
|
|
1
1
|
# @frontlit/preview
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
See your FrontLit pages in the browser while you edit them.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
If your website's pages live in a Git repository as JSON files (a `frontlit/` folder with `pages/*.json` and `site.json`), this tool shows them on your computer the way FrontLit draws them: same blocks, theme, fonts, header and footer. Save a file and the browser updates by itself. You don't need a FrontLit account, a login or an internet connection to your site.
|
|
6
|
+
|
|
7
|
+
## Before you start
|
|
8
|
+
|
|
9
|
+
You need [Node.js](https://nodejs.org) **20.19 or newer** (22.12+ also works). Check with:
|
|
6
10
|
|
|
7
11
|
```sh
|
|
8
|
-
|
|
9
|
-
npx @frontlit/preview ./frontlit --port 4311 --context ./preview.context.json
|
|
12
|
+
node --version
|
|
10
13
|
```
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
1. Open a terminal in your repository (the folder that contains `frontlit/`).
|
|
18
|
+
2. Run:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npx @frontlit/preview@alpha
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The first run downloads the tool, which takes a few seconds.
|
|
25
|
+
3. Open **http://127.0.0.1:4310** in your browser.
|
|
26
|
+
4. Edit any file in `frontlit/` and save. The page reloads with your change.
|
|
27
|
+
|
|
28
|
+
Press **Ctrl+C** in the terminal to stop.
|
|
29
|
+
|
|
30
|
+
> **Why `@alpha`?** The tool is in early release. `@alpha` always gets the newest version.
|
|
31
|
+
|
|
32
|
+
## Using the preview
|
|
33
|
+
|
|
34
|
+
- **Switch pages** with the drop-down at the top. Each file in `pages/` is one page: `index.json` is `/`, `pricing.json` is `/pricing`. Pages marked `"published": false` show too, so you can check drafts.
|
|
35
|
+
- **Light and dark mode:** use the button in the top bar.
|
|
36
|
+
- **Phone and tablet sizes:** use your browser's device mode (in Chrome: right-click → *Inspect* → the phone icon, then pick a width such as 390 px).
|
|
37
|
+
- **Mistakes are fine.** If a file has invalid JSON or a setting FrontLit doesn't accept, the page shows the error instead. Fix it, save, and the page comes back.
|
|
38
|
+
|
|
39
|
+
## Show your site name and logo
|
|
13
40
|
|
|
14
|
-
The
|
|
41
|
+
The header shows "Local preview" until you tell the tool your site's name. Your name and logo are stored in your FrontLit settings rather than the repository, so the tool can't find them alone.
|
|
42
|
+
|
|
43
|
+
Create `frontlit/preview.context.json`:
|
|
15
44
|
|
|
16
45
|
```json
|
|
17
46
|
{
|
|
18
47
|
"branding": {
|
|
19
|
-
"title": "
|
|
20
|
-
"subtitle": "
|
|
21
|
-
"logo": { "url": "assets/logo.svg", "alt": "
|
|
22
|
-
}
|
|
23
|
-
|
|
48
|
+
"title": "Acme",
|
|
49
|
+
"subtitle": "Invoices that chase themselves",
|
|
50
|
+
"logo": { "url": "assets/logo.svg", "alt": "Acme" }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- `title` is your site name, the same one set in FrontLit.
|
|
56
|
+
- `subtitle` and `logo` are optional. The logo path is relative to this file, so `assets/logo.svg` means `frontlit/assets/logo.svg`.
|
|
57
|
+
|
|
58
|
+
This file is only for your computer. Add it to `.gitignore` so it isn't committed:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
echo "frontlit/preview.context.json" >> .gitignore
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Optional: sample posts for blog-style pages
|
|
65
|
+
|
|
66
|
+
Pages with a content feed (for example a blog listing) normally show articles from FrontLit. Locally they show a placeholder box. To see real-looking entries, add sample items for that page's address to the same file:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"branding": { "title": "Acme" },
|
|
24
71
|
"feeds": {
|
|
25
72
|
"/blog": [
|
|
26
|
-
{ "title": "Our
|
|
73
|
+
{ "title": "Our first post", "href": "/", "description": "A short summary." }
|
|
27
74
|
]
|
|
28
75
|
}
|
|
29
76
|
}
|
|
30
77
|
```
|
|
31
78
|
|
|
32
|
-
|
|
79
|
+
## Options
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
npx @frontlit/preview@alpha [site-folder] [--port <number>] [--context <file>]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
| Option | What it does | Default |
|
|
86
|
+
| --- | --- | --- |
|
|
87
|
+
| `site-folder` | The folder that has `pages/` in it. Use `.` if your pages are at the repository root. | `frontlit` |
|
|
88
|
+
| `--port` | Serve on another port, if 4310 is busy. | `4310` |
|
|
89
|
+
| `--context` | Use a branding file from somewhere else. | `<site-folder>/preview.context.json` |
|
|
90
|
+
| `--help` | Print the usage line. | |
|
|
91
|
+
|
|
92
|
+
Example: `npx @frontlit/preview@alpha website --port 5000`.
|
|
93
|
+
|
|
94
|
+
## Images
|
|
95
|
+
|
|
96
|
+
Put images in your repository (for example `frontlit/assets/`) and point to them from page JSON:
|
|
97
|
+
|
|
98
|
+
- **Relative paths** start from the JSON file: from `pages/index.json`, use `../assets/hero.webp`.
|
|
99
|
+
- **Paths starting with `/`** start from the repository root: `/frontlit/assets/hero.webp`.
|
|
100
|
+
- **`https://` links** load from the web as usual.
|
|
101
|
+
|
|
102
|
+
Only image files inside your repository are shown (`.png`, `.jpg`, `.jpeg`, `.webp`, `.avif`, `.gif`, `.svg`).
|
|
103
|
+
|
|
104
|
+
**Embed snippets are different.** The tool doesn't change URLs written inside an embed's HTML. Use a full `https://` URL there, or `/__frontlit/assets/frontlit/assets/<file>` for an image in your repository.
|
|
105
|
+
|
|
106
|
+
## What it doesn't show
|
|
107
|
+
|
|
108
|
+
This tool shows your pages, not everything your live site does:
|
|
109
|
+
|
|
110
|
+
- Blog posts, docs pages and other content stored in FrontLit (only the pages in `pages/` exist)
|
|
111
|
+
- Redirects from `site.json`
|
|
112
|
+
- Search and the chat assistant
|
|
113
|
+
- Real form submissions: forms only show their success message
|
|
114
|
+
- FrontLit's production image optimization
|
|
115
|
+
|
|
116
|
+
Before you publish, open your pull request on GitHub and check the **FrontLit / pages preview** link. That's the real thing, built the same way as your live site.
|
|
117
|
+
|
|
118
|
+
## Troubleshooting
|
|
33
119
|
|
|
34
|
-
|
|
120
|
+
| Message or problem | Fix |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| `No pages directory in …` | Run the command from your repository, or pass the folder that contains `pages/`, e.g. `npx @frontlit/preview@alpha website`. |
|
|
123
|
+
| Port 4310 already in use | Another preview is probably still running. Stop it with Ctrl+C, or use `--port 4311`. |
|
|
124
|
+
| `Page not found` | There is no `pages/<name>.json` for that address. Check the file name. Names use lowercase letters, numbers and hyphens. |
|
|
125
|
+
| An image doesn't show | Check the path is relative to the JSON file, and that the file is inside the repository and is an image. |
|
|
126
|
+
| The header says "Local preview" | Add `preview.context.json` with your site name (see above). |
|
|
127
|
+
| `Unsupported engine` or a syntax error on start | Update Node.js to 20.19 or newer. |
|
|
35
128
|
|
|
36
|
-
|
|
129
|
+
## For FrontLit contributors
|
|
37
130
|
|
|
38
|
-
The package ships its Vite client
|
|
131
|
+
The package ships its CLI (`cli.mjs`), server (`server.mjs`, `site.mjs`) and Vite client (`client/`) as source, so it has no build step. It renders with `@frontlit/page-builder`'s client renderer, `schema` and `theme` entry points.
|
|
39
132
|
|
|
40
|
-
|
|
133
|
+
- Run from the monorepo: build `@frontlit/text-editor` and `@frontlit/page-builder`, then `bun packages/preview/cli.mjs <site-folder>`.
|
|
134
|
+
- Tests: `bun run --cwd packages/preview test`.
|
|
135
|
+
- Once installed from npm, the client runs from `node_modules`. The server therefore pre-bundles the JSX runtime and allows page-builder's package folder for its fonts. Test packaging changes from a packed tarball: `bun pm pack`, then `npm install <tarball>` in an empty folder. A workspace run alone isn't enough.
|
|
136
|
+
- The context file can also hold `site` (the same shape as `site.json`; `site.json` values win) and `themeId` (a bundled theme preset, used when there is no theme object).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontlit/preview",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.3",
|
|
4
4
|
"description": "Local, file-watching previews for FrontLit page JSON",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"check-types": "node --check cli.mjs && node --check server.mjs && node --check site.mjs"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@frontlit/page-builder": "0.1.0-alpha.
|
|
34
|
+
"@frontlit/page-builder": "0.1.0-alpha.5",
|
|
35
35
|
"react": "19.2.8",
|
|
36
36
|
"react-dom": "19.2.8",
|
|
37
37
|
"vite": "^7.3.6"
|
package/server.mjs
CHANGED
|
@@ -32,7 +32,9 @@ export async function startPreview(folder, { port = 4310, context } = {}) {
|
|
|
32
32
|
fs: {
|
|
33
33
|
allow: [
|
|
34
34
|
searchForWorkspaceRoot(packageRoot),
|
|
35
|
-
dirname(
|
|
35
|
+
dirname(
|
|
36
|
+
fileURLToPath(import.meta.resolve("@frontlit/page-builder/styles.css")),
|
|
37
|
+
),
|
|
36
38
|
],
|
|
37
39
|
},
|
|
38
40
|
},
|
|
@@ -102,6 +104,12 @@ export async function startPreview(folder, { port = 4310, context } = {}) {
|
|
|
102
104
|
},
|
|
103
105
|
],
|
|
104
106
|
});
|
|
105
|
-
|
|
107
|
+
try {
|
|
108
|
+
await server.listen();
|
|
109
|
+
} catch (error) {
|
|
110
|
+
// Vite's file watcher would otherwise keep the process alive.
|
|
111
|
+
await server.close();
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
106
114
|
return server;
|
|
107
115
|
}
|
package/site.mjs
CHANGED
|
@@ -40,6 +40,31 @@ function media(value, owner, root) {
|
|
|
40
40
|
}),
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
|
+
// The client renders fixtures directly, so reject shapes it would crash on.
|
|
44
|
+
function feedFixtures(feeds, contextFile) {
|
|
45
|
+
if (feeds === undefined) return {};
|
|
46
|
+
const fail = (where) => {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`${contextFile}: ${where} must be an array of { "title", "href", "description" } strings, keyed by route.`,
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
if (!feeds || typeof feeds !== "object" || Array.isArray(feeds)) fail("feeds");
|
|
52
|
+
for (const [route, entries] of Object.entries(feeds)) {
|
|
53
|
+
if (!Array.isArray(entries)) fail(`feeds["${route}"]`);
|
|
54
|
+
entries.forEach((entry, i) => {
|
|
55
|
+
if (
|
|
56
|
+
!entry ||
|
|
57
|
+
typeof entry !== "object" ||
|
|
58
|
+
Array.isArray(entry) ||
|
|
59
|
+
["title", "href", "description"].some(
|
|
60
|
+
(key) => entry[key] !== undefined && typeof entry[key] !== "string",
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
fail(`feeds["${route}"][${i}]`);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return feeds;
|
|
67
|
+
}
|
|
43
68
|
export function loadSite(
|
|
44
69
|
folder,
|
|
45
70
|
pathname = "/",
|
|
@@ -114,7 +139,7 @@ export function loadSite(
|
|
|
114
139
|
path: f === "index.json" ? "/" : "/" + f.slice(0, -5),
|
|
115
140
|
name: f.slice(0, -5),
|
|
116
141
|
})),
|
|
117
|
-
feeds: context.feeds
|
|
142
|
+
feeds: feedFixtures(context.feeds, contextFile),
|
|
118
143
|
missingBranding: !branding.title,
|
|
119
144
|
};
|
|
120
145
|
}
|