@frontlit/preview 0.1.0-alpha.1 → 0.1.0-alpha.2

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.
Files changed (4) hide show
  1. package/README.md +113 -17
  2. package/package.json +2 -2
  3. package/server.mjs +10 -2
  4. package/site.mjs +26 -1
package/README.md CHANGED
@@ -1,40 +1,136 @@
1
1
  # @frontlit/preview
2
2
 
3
- Read-only local previews of FrontLit repository pages using the existing page-builder React renderer, compiled styles, fonts and shadow-root embeds. Node 20.19+ (or 22.12+) is required.
3
+ See your FrontLit pages in the browser while you edit them.
4
4
 
5
- After publication:
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
- npx @frontlit/preview ./frontlit
9
- npx @frontlit/preview ./frontlit --port 4311 --context ./preview.context.json
12
+ node --version
10
13
  ```
11
14
 
12
- In the monorepo, build `@frontlit/text-editor` and `@frontlit/page-builder`, then run `bun packages/preview/cli.mjs <site-folder>`. The default address is http://127.0.0.1:4310. Changes to page JSON, site JSON, images or context refresh the browser. Invalid files display diagnostics and recover on the next edit. Use the toolbar to select pages or switch color mode; browser device emulation works normally.
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 site folder contains `pages/*.json` and optional `site.json`. Shared chrome and theme come from `site.json`. Missing values can fall back to a local context file (`preview.context.json` in the site folder, or `--context`). Branding lives outside FrontLit's Git site format, so provide it explicitly:
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": "John's Piping Pizza",
20
- "subtitle": "Dough. Heat. Repeat.",
21
- "logo": { "url": "assets/logo.svg", "alt": "John's Piping Pizza" }
22
- },
23
- "site": {},
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 pizza notebook", "href": "/", "description": "A local feed fixture." }
73
+ { "title": "Our first post", "href": "/", "description": "A short summary." }
27
74
  ]
28
75
  }
29
76
  }
30
77
  ```
31
78
 
32
- `site` accepts the same chrome/theme structure as `site.json`; local site fields win. An optional `themeId` selects a bundled preset when there is no theme object. This is a local snapshot, not an authenticated export endpoint. No FrontLit credentials or backend are required. Use a gitignored context file for private fixture data.
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
- Repository images resolve relative to their owning JSON file, or relative to the repository root for paths starting with `/`. Only image files inside that root are served through the asset endpoint, including symlink checks. Without a Git root, the site folder's parent is the root. HTTPS media remains remote. URLs inside raw embed HTML are not rewritten; use a permanent HTTPS URL or the local asset URL (`/__frontlit/assets/<repository-relative-path>`).
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
- Page routes correspond to `pages/*.json`. Draft pages are deliberately previewable. Backend article/doc detail routes, redirects, chatbot, search and production image optimization are not implemented. Feeds use explicit local fixtures or a visible placeholder; forms receive no team/API identity and use page-builder's local success behavior. This previews the client renderer; the hosted site uses server rendering, so the release preview remains the final integration check. Keep the renderer dependency aligned with the release being targeted.
129
+ ## For FrontLit contributors
37
130
 
38
- The package ships its Vite client and CLI as source and needs no separate build. `bun run --cwd packages/preview test` checks normalization, local media boundaries and missing/invalid input behavior.
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
- Release prerequisite: the renderer dependency must include its existing `schema` and `theme` entry points (the current workspace alpha.2); the older npm alpha.1 does not export them. Publish compatible workspace dependencies before this utility. No page-builder implementation changes are needed.
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.1",
3
+ "version": "0.1.0-alpha.2",
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.3",
34
+ "@frontlit/page-builder": "0.1.0-alpha.4",
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(fileURLToPath(import.meta.resolve("@frontlit/page-builder/styles.css"))),
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
- await server.listen();
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
  }