@zeropress/build-pages 0.5.1 → 0.5.4

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 CHANGED
@@ -2,71 +2,156 @@
2
2
 
3
3
  Build ZeroPress static output for modern hosting platforms.
4
4
 
5
- `@zeropress/build-pages` turns a directory of Markdown files and public assets into static ZeroPress output. It discovers Markdown files, converts them to preview-data v0.5, stages public files, and runs `@zeropress/build`.
5
+ `@zeropress/build-pages` turns a directory of Markdown files and public assets into a static ZeroPress site. It discovers Markdown pages, prepares the site data, stages public files, and runs `@zeropress/build`.
6
6
 
7
7
  The generated output is plain static files that can be deployed to GitHub Pages, Cloudflare Pages, Netlify, Vercel, or any static hosting provider.
8
8
 
9
- ## GitHub Action
9
+ ## Build Flow
10
+
11
+ ```txt
12
+ source directory
13
+ Markdown pages + .zeropress/config.json + public files
14
+ |
15
+ v
16
+ @zeropress/build-pages
17
+ generates .zeropress/preview-data.json
18
+ stages public files
19
+ |
20
+ v
21
+ @zeropress/build + ZeroPress theme
22
+ |
23
+ v
24
+ static output directory
25
+ HTML pages + assets + copied public files
26
+ ```
27
+
28
+ ```mermaid
29
+ flowchart TD
30
+ source["Source directory"] --> markdown["Markdown pages (*.md)"]
31
+ source --> config[".zeropress/config.json"]
32
+ source --> publicFiles["Public files<br/>images, CSS, JS, PDF, JSON, Markdown"]
33
+
34
+ markdown --> buildPages["@zeropress/build-pages"]
35
+ config --> buildPages
36
+ publicFiles --> buildPages
37
+
38
+ buildPages --> previewData[".zeropress/preview-data.json<br/>internal generated build input"]
39
+ buildPages --> stagedPublic["Staged public files"]
40
+
41
+ previewData --> build["@zeropress/build"]
42
+ stagedPublic --> build
43
+ theme["ZeroPress theme"] --> build
44
+
45
+ build --> output["Static output directory"]
46
+ output --> html["HTML pages"]
47
+ output --> assets["Theme assets"]
48
+ output --> copied["Copied public files"]
49
+ output --> special["sitemap.xml / robots.txt / feed.xml"]
50
+ ```
51
+
52
+ ## Usage
53
+
54
+ ### GitHub Action
55
+
56
+ A basic Pages deployment workflow with the `zeropress-build-pages` action looks like this.
10
57
 
11
58
  ```yaml
59
+ name: Build and Deploy Docs to GitHub Pages
60
+ on:
61
+ push:
62
+ branches: ["main"]
63
+ workflow_dispatch:
64
+ permissions:
65
+ contents: read
66
+ pages: write
67
+ id-token: write
68
+ concurrency:
69
+ group: "pages"
70
+ cancel-in-progress: false
12
71
  jobs:
13
72
  build:
14
73
  runs-on: ubuntu-latest
15
74
  steps:
16
- - uses: actions/checkout@v4
17
- - uses: actions/configure-pages@v5
75
+ - name: Checkout
76
+ uses: actions/checkout@v6
77
+ - name: Setup Pages
78
+ uses: actions/configure-pages@v6
18
79
  - name: Build ZeroPress Pages
19
80
  uses: zeropress-app/zeropress-build-pages@v0
20
81
  with:
21
- source: ./
82
+ source: ./documents
22
83
  destination: ./_site
23
- theme: docs
24
- - uses: actions/upload-pages-artifact@v3
25
- with:
26
- path: ./_site
84
+ - name: Upload artifact
85
+ uses: actions/upload-pages-artifact@v5
86
+ deploy:
87
+ runs-on: ubuntu-latest
88
+ needs: build
89
+ environment:
90
+ name: github-pages
91
+ url: ${{ steps.deployment.outputs.page_url }}
92
+ steps:
93
+ - name: Deploy to GitHub Pages
94
+ id: deployment
95
+ uses: actions/deploy-pages@v5
27
96
  ```
28
97
 
29
- The action builds the static files only. Uploading and deploying are handled by your hosting provider's deployment action or CLI.
98
+ The action `zeropress-build-pages` builds the static files only. Uploading and deploying are handled by your hosting provider's deployment action or CLI.
99
+
100
+ In the action inputs:
101
+
102
+ - `source` is the directory that contains your Markdown pages, public files, and optional `.zeropress/config.json`. The default is `./docs`.
103
+ - `destination` is the directory where the generated static site is written. The default is `./_site`.
30
104
 
31
105
  For GitHub Pages, the generated `destination` directory can be passed to `actions/upload-pages-artifact`. For Cloudflare Pages, Netlify, Vercel, or another static host, pass the same `destination` directory to that provider's deploy step.
32
106
 
33
- ## CLI
107
+ ### npx
108
+
109
+ Use `npx` when you want to run Build Pages without adding it to your project dependencies.
34
110
 
35
111
  ```bash
36
- npx @zeropress/build-pages --source ./docs --destination ./_site
112
+ npx @zeropress/build-pages --source ./documents --destination ./_site
37
113
  ```
38
114
 
39
- Options:
115
+ ### package.json script
116
+
117
+ Use a package script when your project already has a Node.js toolchain.
118
+
119
+ ```bash
120
+ npm install --save-dev @zeropress/build-pages
121
+ ```
122
+
123
+ ```json
124
+ {
125
+ "scripts": {
126
+ "build": "zeropress-build-pages --source ./documents --destination ./_site"
127
+ }
128
+ }
129
+ ```
130
+
131
+ ```bash
132
+ npm run build
133
+ ```
134
+
135
+ ## CLI Options
136
+
137
+ The CLI requires explicit input and output paths. The GitHub Action keeps safe defaults for workflow convenience.
40
138
 
41
139
  | Option | Default | Purpose |
42
140
  | --- | --- | --- |
43
- | `--source <dir>` | `.` | Source directory containing Markdown and public files |
44
- | `--destination <dir>` | `_site` | Output directory |
45
- | `--out <dir>` | `_site` | Alias for `--destination` |
141
+ | `--source <dir>` | required | Dedicated source directory containing Markdown and public files |
142
+ | `--destination <dir>` | required | Output directory |
46
143
  | `--theme docs` | `docs` | Bundled docs theme |
47
144
  | `--theme-path <dir>` | none | Custom ZeroPress theme directory |
48
145
  | `--config <path>` | `<source>/.zeropress/config.json` | Build Pages config |
49
146
  | `--site-url <url>` | config `site.url` | Canonical URL override |
50
- | `--skip-untitled-markdown` | `false` | Skip Markdown without an H1 |
51
- | `--check-links` | `true` | Warn about broken internal links |
52
- | `--no-check-links` | false | Skip link checking |
53
-
54
- Equivalent environment variables:
55
-
56
- | Env | Maps to |
57
- | --- | --- |
58
- | `ZEROPRESS_PUBLIC_DIR` | `--source` |
59
- | `ZEROPRESS_OUT_DIR` | `--destination` |
60
- | `ZEROPRESS_THEME_DIR` | `--theme-path` |
61
- | `ZEROPRESS_BUILD_PAGES_CONFIG` | `--config` |
62
- | `ZEROPRESS_SITE_URL` | `--site-url` |
63
- | `ZEROPRESS_SKIP_UNTITLED_MARKDOWN=true` | `--skip-untitled-markdown` |
64
-
65
- CLI options take precedence over environment variables.
147
+ | `--skip-untitled-markdown` | `false` | Skip Markdown without a page title |
148
+ | `--skip-link-check` | `false` | Skip link checking |
66
149
 
67
150
  ## Source Tree
68
151
 
69
- The source directory is both the Markdown source root and the public passthrough root.
152
+ The source directory is both the Markdown source root and the public passthrough root. GitHub Action usage defaults to `./docs`; CLI usage requires `--source`.
153
+
154
+ Use a dedicated content directory such as `docs/` or `documents/`. Repository root source (`--source ./`) is not supported, because Build Pages uses `.zeropress/` in the current working directory for internal working files.
70
155
 
71
156
  ```txt
72
157
  docs/
@@ -77,7 +162,9 @@ docs/
77
162
  config.json
78
163
  ```
79
164
 
80
- Build Pages stages the source tree before calling `@zeropress/build`, so `--source ./` and `--destination ./_site` are supported. Generated ZeroPress output wins over staged public files.
165
+ Build Pages stages the source tree before calling `@zeropress/build`. Generated ZeroPress output wins over staged public files.
166
+
167
+ The source directory must not overlap the destination directory, the selected theme directory, or the internal `.zeropress/` working directory.
81
168
 
82
169
  Ignored while staging and Markdown discovery:
83
170
 
@@ -95,24 +182,54 @@ Additional Markdown discovery ignores:
95
182
  - path segments ending with `~`
96
183
  - `vendor`
97
184
 
98
- ## Markdown Discovery
185
+ ## Markdown Pages
99
186
 
100
187
  - `*.md` files are discovered recursively.
101
- - Each Markdown page needs an ATX H1 (`# Title`) or Setext H1 (`Title` + `====`).
102
- - Missing or empty H1 fails the build unless `--skip-untitled-markdown` is used.
188
+ - Each Markdown page needs a page title. Build Pages uses front matter `title`, then an ATX H1 (`# Title`), then a Setext H1 (`Title` + `====`).
189
+ - If no title can be found, the build fails unless `--skip-untitled-markdown` is used.
190
+ - `--skip-untitled-markdown` skips those Markdown files. It does not create untitled pages.
103
191
  - Root `index.md` becomes the front page when no config is present.
104
192
  - Nested `index.md` maps to a directory route, such as `cli/index.md` -> `/cli/`.
105
193
  - Other Markdown files map to extensionless routes, such as `cli/tool.md` -> `/cli/tool`.
106
194
  - Markdown links to other discovered `.md` files are rewritten to generated public URLs.
107
195
  - Original Markdown files remain available as public passthrough files.
108
196
 
197
+ Optional YAML front matter is supported at the top of Markdown files:
198
+
199
+ ```md
200
+ ---
201
+ title: Install ZeroPress
202
+ description: Build a static docs site from Markdown.
203
+ path: guides/install
204
+ status: published
205
+ meta:
206
+ source: docs
207
+ ---
208
+
209
+ Body content...
210
+ ```
211
+
212
+ All supported front matter fields are optional. When `status` is omitted, the page is treated as `published`.
213
+
214
+ Supported front matter fields:
215
+
216
+ | Field | Purpose |
217
+ | --- | --- |
218
+ | `title` | Page title. Takes priority over Markdown H1. |
219
+ | `description` | Page excerpt and description. |
220
+ | `path` | Generated route path, such as `guides/install` for `/guides/install`. |
221
+ | `status` | `published` includes the page. `draft` skips the page. Other values warn and skip. |
222
+ | `meta` | Optional scalar/null metadata copied to the generated page. |
223
+
224
+ Unknown front matter fields are ignored to make migration from existing Markdown sites easier.
225
+
109
226
  ## Config
110
227
 
111
228
  Build Pages reads `<source>/.zeropress/config.json` when present. Missing config falls back to defaults.
112
229
 
113
230
  ```json
114
231
  {
115
- "$schema": "../schemas/zeropress-build-pages.config.v0.1.schema.json",
232
+ "$schema": "https://zeropress.dev/schemas/zeropress-build-pages.config.v0.1.schema.json",
116
233
  "version": "0.1",
117
234
  "site": {
118
235
  "title": "My Docs",
@@ -162,18 +279,29 @@ Schemas:
162
279
  - `schemas/zeropress-build-pages.config.v0.1.schema.json`
163
280
  - `schemas/zeropress-build-pages.config.schema.json`
164
281
 
165
- ## Generated Files
282
+ ## Internal `.zeropress/` Files
166
283
 
167
- Build Pages writes:
284
+ Build Pages writes internal working files to `.zeropress/` in the current working directory. These files are not the final deploy output. The final static site is written to the `destination` directory.
168
285
 
169
286
  ```txt
170
287
  .zeropress/
288
+ build-pages-config.json
171
289
  preview-data.json
172
- prebuild-report.json
173
- build-pages-public/
290
+ build-report.json
291
+ public-assets/
174
292
  ```
175
293
 
176
- `preview-data.json` is the generated preview-data v0.5 input passed to `@zeropress/build`.
294
+ `build-pages-config.json` is the resolved user-facing Build Pages config used for the current run. It combines source config, defaults, and CLI or Action input overrides where applicable.
295
+
296
+ `preview-data.json` is an internal generated build input for the ZeroPress renderer. Most users do not need to edit or understand this file.
297
+
298
+ `build-report.json` records discovered Markdown counts, skipped Markdown files, front page resolution, and custom HTML slots.
299
+
300
+ `public-assets/` is a temporary staged public root used before the final ZeroPress render.
301
+
302
+ ## Destination Output
303
+
304
+ The `destination` directory contains the deployable static site. It includes generated ZeroPress HTML, copied public files, and original Markdown files unless they are excluded by the public passthrough rules.
177
305
 
178
306
  ## Development
179
307
 
package/action.yml CHANGED
@@ -6,9 +6,9 @@ branding:
6
6
  color: blue
7
7
  inputs:
8
8
  source:
9
- description: Source directory containing Markdown files and optional .zeropress/config.json.
9
+ description: Dedicated source directory containing Markdown files and optional .zeropress/config.json. Repository root source is not supported.
10
10
  required: false
11
- default: ./
11
+ default: ./docs
12
12
  destination:
13
13
  description: Output directory for the generated static site.
14
14
  required: false
@@ -27,13 +27,13 @@ inputs:
27
27
  description: Canonical site URL override.
28
28
  required: false
29
29
  skip-untitled-markdown:
30
- description: Skip Markdown files without an H1 instead of failing.
30
+ description: Skip Markdown files without a page title instead of failing.
31
31
  required: false
32
32
  default: "false"
33
- check-links:
34
- description: Warn about broken internal links after build.
33
+ skip-link-check:
34
+ description: Skip internal link checking after the site is built.
35
35
  required: false
36
- default: "true"
36
+ default: "false"
37
37
  runs:
38
38
  using: node24
39
39
  main: dist/action.js
package/dist/action.js CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __zeropressCreateRequire } from "node:module";
3
+ const require = __zeropressCreateRequire(import.meta.url);
2
4
  var __create = Object.create;
3
5
  var __defProp = Object.defineProperty;
4
6
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -62137,9 +62139,9 @@ async function linkExists(siteDir, link2) {
62137
62139
  // src/index.js
62138
62140
  var __dirname = path3.dirname(fileURLToPath(import.meta.url));
62139
62141
  var packageDir = path3.resolve(__dirname, "..");
62140
- var prebuildScript = path3.join(packageDir, "src", "prebuild.js");
62142
+ var prebuildScript = __dirname === path3.join(packageDir, "dist") ? path3.join(__dirname, "prebuild.js") : path3.join(packageDir, "src", "prebuild.js");
62141
62143
  var PREVIEW_DATA_PATH = ".zeropress/preview-data.json";
62142
- var STAGING_DIR = ".zeropress/build-pages-public";
62144
+ var STAGING_DIR = ".zeropress/public-assets";
62143
62145
  var DEFAULT_THEME = "docs";
62144
62146
  async function runBuildPages(options2) {
62145
62147
  const cwd = path3.resolve(options2.cwd || process.cwd());
@@ -62149,6 +62151,13 @@ async function runBuildPages(options2) {
62149
62151
  const stagingDir = path3.join(cwd, STAGING_DIR);
62150
62152
  const previewDataPath = path3.join(cwd, PREVIEW_DATA_PATH);
62151
62153
  const themeDir = resolveThemeDir(cwd, options2);
62154
+ assertBuildPagesPathLayout({
62155
+ cwd,
62156
+ sourceDir,
62157
+ destinationDir,
62158
+ themeDir,
62159
+ generatedDir
62160
+ });
62152
62161
  await assertDirectory(sourceDir, "Source directory");
62153
62162
  await fs3.rm(generatedDir, { recursive: true, force: true });
62154
62163
  await fs3.mkdir(generatedDir, { recursive: true });
@@ -62195,7 +62204,7 @@ async function runBuildPages(options2) {
62195
62204
  process.env.ZEROPRESS_PUBLIC_DIR = previousPublicDir;
62196
62205
  }
62197
62206
  }
62198
- if (options2.checkLinks) {
62207
+ if (!options2.skipLinkCheck) {
62199
62208
  const result = await checkInternalLinks(destinationDir);
62200
62209
  if (result.brokenLinks.length) {
62201
62210
  console.warn("Warning: broken internal links found:");
@@ -62229,6 +62238,26 @@ async function assertDirectory(dir, label) {
62229
62238
  throw new Error(`${label} is not a directory: ${dir}`);
62230
62239
  }
62231
62240
  }
62241
+ function assertBuildPagesPathLayout({ cwd, sourceDir, destinationDir, themeDir, generatedDir }) {
62242
+ if (samePath(sourceDir, cwd)) {
62243
+ throw new Error(
62244
+ `Source directory must be a dedicated content directory, not the current working directory. Received: ${formatPath(cwd, sourceDir)}`
62245
+ );
62246
+ }
62247
+ assertNoPathOverlap(cwd, "Source directory", sourceDir, "internal .zeropress working directory", generatedDir);
62248
+ assertNoPathOverlap(cwd, "Destination directory", destinationDir, "internal .zeropress working directory", generatedDir);
62249
+ assertNoPathOverlap(cwd, "Theme directory", themeDir, "internal .zeropress working directory", generatedDir);
62250
+ assertNoPathOverlap(cwd, "Source directory", sourceDir, "destination directory", destinationDir);
62251
+ assertNoPathOverlap(cwd, "Source directory", sourceDir, "theme directory", themeDir);
62252
+ }
62253
+ function assertNoPathOverlap(cwd, firstLabel, firstPath, secondLabel, secondPath) {
62254
+ if (!pathsOverlap2(firstPath, secondPath)) {
62255
+ return;
62256
+ }
62257
+ throw new Error(
62258
+ `${firstLabel} must not overlap the ${secondLabel}. ${firstLabel}: ${formatPath(cwd, firstPath)}; ${secondLabel}: ${formatPath(cwd, secondPath)}`
62259
+ );
62260
+ }
62232
62261
  async function copyPublicStaging(sourceDir, targetDir, options2) {
62233
62262
  const entries = await fs3.readdir(sourceDir, { withFileTypes: true });
62234
62263
  for (const entry of entries) {
@@ -62265,6 +62294,9 @@ function pathsOverlap2(firstPath, secondPath) {
62265
62294
  const second = path3.resolve(secondPath);
62266
62295
  return first === second || isPathInside2(first, second) || isPathInside2(second, first);
62267
62296
  }
62297
+ function samePath(firstPath, secondPath) {
62298
+ return path3.resolve(firstPath) === path3.resolve(secondPath);
62299
+ }
62268
62300
  function isPathInside2(parentPath, childPath) {
62269
62301
  const relativePath = path3.relative(parentPath, childPath);
62270
62302
  return Boolean(relativePath) && !relativePath.startsWith("..") && !path3.isAbsolute(relativePath);
@@ -62276,14 +62308,14 @@ function formatPath(cwd, targetPath) {
62276
62308
 
62277
62309
  // src/action.js
62278
62310
  var options = {
62279
- source: input("source") || "./",
62311
+ source: input("source") || "./docs",
62280
62312
  destination: input("destination") || "./_site",
62281
62313
  theme: input("theme") || "docs",
62282
62314
  themePath: input("theme-path"),
62283
62315
  config: input("config"),
62284
62316
  siteUrl: input("site-url"),
62285
62317
  skipUntitledMarkdown: booleanInput("skip-untitled-markdown", false),
62286
- checkLinks: booleanInput("check-links", true)
62318
+ skipLinkCheck: booleanInput("skip-link-check", false)
62287
62319
  };
62288
62320
  try {
62289
62321
  await runBuildPages(options);