@takazudo/zfb 0.1.0-next.10
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/CHANGELOG.md +45 -0
- package/LICENSE +21 -0
- package/README.md +207 -0
- package/bin/zfb.mjs +82 -0
- package/dist/config.d.ts +542 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +24 -0
- package/dist/config.js.map +1 -0
- package/dist/content.d.ts +240 -0
- package/dist/content.d.ts.map +1 -0
- package/dist/content.js +460 -0
- package/dist/content.js.map +1 -0
- package/dist/frontmatter.d.ts +23 -0
- package/dist/frontmatter.d.ts.map +1 -0
- package/dist/frontmatter.js +142 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/island.d.ts +121 -0
- package/dist/island.d.ts.map +1 -0
- package/dist/island.js +273 -0
- package/dist/island.js.map +1 -0
- package/dist/jsx-types.d.ts +37 -0
- package/dist/jsx-types.d.ts.map +1 -0
- package/dist/jsx-types.js +12 -0
- package/dist/jsx-types.js.map +1 -0
- package/dist/paginate.d.ts +43 -0
- package/dist/paginate.d.ts.map +1 -0
- package/dist/paginate.js +44 -0
- package/dist/paginate.js.map +1 -0
- package/dist/plugins.d.ts +259 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +42 -0
- package/dist/plugins.js.map +1 -0
- package/dist/runtime.d.ts +101 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +454 -0
- package/dist/runtime.js.map +1 -0
- package/dist/types.d.ts +27 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +33 -0
- package/dist/types.js.map +1 -0
- package/package.json +98 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
> **Newer releases:** see https://takazudomodular.com/pj/zudo-front-builder/docs/changelog/ for v0.1.0-next.5 and later. Entries below are historical (kept for npm readers).
|
|
4
|
+
|
|
5
|
+
## 0.1.0-next.4
|
|
6
|
+
|
|
7
|
+
### Bug fixes
|
|
8
|
+
|
|
9
|
+
**Binary executable bit + launcher EACCES** (#441, #444 §1):
|
|
10
|
+
|
|
11
|
+
- #441: The bundled `bin/zfb.mjs` launcher was missing its executable bit in the published tarball, causing `zfb: command not found` after `npm install -g`.
|
|
12
|
+
- #444 §1: Companion fix ensuring the per-platform native binary receives its executable bit correctly on POSIX systems.
|
|
13
|
+
|
|
14
|
+
**`--version` stamping** (#445, #444 §2):
|
|
15
|
+
|
|
16
|
+
- #445: `zfb --version` printed `0.0.0` instead of the actual release version; the binary is now stamped with `ZFB_RELEASE_VERSION` at build time.
|
|
17
|
+
- #444 §2: Ensures the version reported by `--version` matches the npm package version for all platforms.
|
|
18
|
+
|
|
19
|
+
**`paths()` worker / `zfb/content` snapshot flow** (#442):
|
|
20
|
+
|
|
21
|
+
- #442: Fixed a race in the content-snapshot flow where `paths()` could be invoked before the worker finished writing the snapshot, causing intermittent empty-route tables.
|
|
22
|
+
|
|
23
|
+
**`@/` tsconfig path-alias regression** (#443):
|
|
24
|
+
|
|
25
|
+
- #443: The `@/` TypeScript path alias was dropped during the build pipeline refactor in 0.1.0-next.3, breaking imports that relied on the alias in user projects.
|
|
26
|
+
|
|
27
|
+
**`create-zfb` scaffold dist-tag** (#343):
|
|
28
|
+
|
|
29
|
+
- #343: `npm create zfb@latest` was resolving the wrong dist-tag on the first install; scaffolded projects now pin to the exact CLI version (`=<ver>` rather than `^<ver>`) to prevent silent downgrade once the stable release lands.
|
|
30
|
+
|
|
31
|
+
## 0.1.0-next.1
|
|
32
|
+
|
|
33
|
+
Initial public prerelease on npm.
|
|
34
|
+
|
|
35
|
+
- Rust-built static-site engine, distributed per-platform via npm optional-deps.
|
|
36
|
+
- TypeScript SDK with subpath exports for `runtime`, `content`, `paginate`, `config`, `plugins`, `frontmatter`.
|
|
37
|
+
- Bundled `basic-blog` template via `zfb new my-site` / `npm create zfb@latest my-site`.
|
|
38
|
+
|
|
39
|
+
## Behavior change
|
|
40
|
+
|
|
41
|
+
**Extra-dirs pass now honors `.gitignore`** (Fix B for #428, closes #433):
|
|
42
|
+
|
|
43
|
+
- Gitignored top-level directories (e.g. `worktrees/`) are no longer copied into the shadow build tree. Previously the bundler would unconditionally materialise every non-infrastructure top-level directory.
|
|
44
|
+
- Global git ignore (`~/.config/git/ignore`) and hidden-directory rules now apply at the top level in addition to `.gitignore`.
|
|
45
|
+
- **Negation caveat:** if your `.gitignore` contains a pattern like `!worktrees/keep/` to opt a sub-path back in, the negation is silently ignored by this pass. The extra-dirs walk operates whole-directory-or-nothing at `max_depth=1`; the parent directory is excluded before the negation can apply. Consumers relying on negated sub-path opt-ins in an otherwise-excluded directory will need an alternative arrangement (e.g. move the directory outside the gitignored subtree).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Takeshi Takatsudo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# @takazudo/zfb
|
|
2
|
+
|
|
3
|
+
> Rust-built static-site engine for Astro and Next.js users — millisecond rebuilds, single binary.
|
|
4
|
+
|
|
5
|
+
The public SDK module for [zfb][zfb-site]: islands, content collections,
|
|
6
|
+
pagination, config, plugins, and frontmatter helpers. User pages reach this
|
|
7
|
+
package through the bare specifier `"zfb"` — the `zfb-render` runtime loader
|
|
8
|
+
registers the source under that name at build time so user TSX can write:
|
|
9
|
+
|
|
10
|
+
```tsx
|
|
11
|
+
import { Island } from "zfb";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Full documentation: <https://takazudomodular.com/pj/zudo-front-builder/>.
|
|
15
|
+
Source: <https://github.com/Takazudo/zudo-front-builder>.
|
|
16
|
+
|
|
17
|
+
[zfb-site]: https://takazudomodular.com/pj/zudo-front-builder/
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npm install @takazudo/zfb
|
|
23
|
+
# or: pnpm add @takazudo/zfb
|
|
24
|
+
# or: yarn add @takazudo/zfb
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## What lives here
|
|
28
|
+
|
|
29
|
+
This package is the canonical TypeScript source for the `zfb` SDK
|
|
30
|
+
surface. Today it covers:
|
|
31
|
+
|
|
32
|
+
- `<Island when="visible|idle|load">` — JSX wrapper that marks a region
|
|
33
|
+
for client-side hydration.
|
|
34
|
+
- `scheduleHydrate(target, when, fire)` — the runtime branching helper
|
|
35
|
+
consumed by the hydration runtime.
|
|
36
|
+
- `When`, `WHEN_VALUES`, `DEFAULT_WHEN`, `isWhen`, `resolveWhen` — type
|
|
37
|
+
and runtime utilities pinning the spelling of the three modes.
|
|
38
|
+
- `getCollection(name)`, `parseFrontmatter(raw)` — content collection
|
|
39
|
+
helpers exported from `zfb/content`. `parseFrontmatter` is part of the
|
|
40
|
+
public SDK surface so consumers can write custom content loaders that
|
|
41
|
+
reuse the v0 frontmatter parser without re-implementing it.
|
|
42
|
+
- `defaultComponents` — eleven-entry per-element override map (`h2`, `h3`,
|
|
43
|
+
`h4`, `p`, `a`, `strong`, `blockquote`, `ul`, `ol`, `table`, `code`)
|
|
44
|
+
ported from zudo-doc's `htmlOverrides` convention. **`h1` is deliberately
|
|
45
|
+
omitted** because page titles render `<h1>` from frontmatter. Each entry
|
|
46
|
+
is a thin passthrough and is also exported as a named const
|
|
47
|
+
(`ContentParagraph`, `ContentLink`, …) so consumers can tree-shake-import a
|
|
48
|
+
single override. Spread into a `components` prop to compose with custom
|
|
49
|
+
overrides:
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { defaultComponents } from "zfb";
|
|
53
|
+
|
|
54
|
+
<entry.Content components={{ ...defaultComponents, h2: MyFancyH2 }} />
|
|
55
|
+
```
|
|
56
|
+
- `paginate(items, opts)`, plus `PaginatedPage<T>` / `PaginateRoute<T>` —
|
|
57
|
+
exported from `zfb/paginate`.
|
|
58
|
+
- `defineConfig(config)` — exported from `zfb/config` for the
|
|
59
|
+
`zfb.config.ts` form (the recommended way to author a zfb project's
|
|
60
|
+
configuration; the back-compat `zfb.config.json` form is still
|
|
61
|
+
supported).
|
|
62
|
+
|
|
63
|
+
The package is JSX-runtime-agnostic: the `Island` component does not
|
|
64
|
+
import preact or react, so it works under either framework adapter
|
|
65
|
+
without bundling the wrong runtime.
|
|
66
|
+
|
|
67
|
+
## Usage
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { Island } from "zfb";
|
|
71
|
+
import { Counter } from "../components/Counter.tsx"; // a "use client" component
|
|
72
|
+
|
|
73
|
+
export default function Page() {
|
|
74
|
+
return (
|
|
75
|
+
<>
|
|
76
|
+
<h1>Welcome</h1>
|
|
77
|
+
|
|
78
|
+
{/* Hydrate immediately on page load (default). */}
|
|
79
|
+
<Island>
|
|
80
|
+
<Counter />
|
|
81
|
+
</Island>
|
|
82
|
+
|
|
83
|
+
{/* Hydrate during the next idle callback. */}
|
|
84
|
+
<Island when="idle">
|
|
85
|
+
<Counter />
|
|
86
|
+
</Island>
|
|
87
|
+
|
|
88
|
+
{/* Hydrate only when the island first scrolls into view. */}
|
|
89
|
+
<Island when="visible">
|
|
90
|
+
<Counter />
|
|
91
|
+
</Island>
|
|
92
|
+
</>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## The three `when=` modes
|
|
98
|
+
|
|
99
|
+
| `when` | Trigger | Fallback |
|
|
100
|
+
| ----------- | --------------------------------------------------------------- | ---------------------------------------------- |
|
|
101
|
+
| `"load"` | Synchronous, immediate fire after registration. **Default.** | n/a |
|
|
102
|
+
| `"idle"` | `requestIdleCallback` | `setTimeout(0)` when not available |
|
|
103
|
+
| `"visible"` | `IntersectionObserver`, threshold 0, first intersection only | Immediate fire when `IntersectionObserver` is missing |
|
|
104
|
+
|
|
105
|
+
Unknown values produce a `console.warn` in development builds and fall
|
|
106
|
+
back to `"load"`.
|
|
107
|
+
|
|
108
|
+
## Build-time output
|
|
109
|
+
|
|
110
|
+
The wrapper is intentionally type-erased at the JSX boundary. At the
|
|
111
|
+
call site, `<Island when="visible">{children}</Island>` renders as:
|
|
112
|
+
|
|
113
|
+
```html
|
|
114
|
+
<div data-zfb-island data-when="visible"><!-- children --></div>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The `data-zfb-island` attribute is empty here. The hydration emit step
|
|
118
|
+
in the `zfb-render` runtime walks rendered HTML and replaces it with
|
|
119
|
+
`data-zfb-island="ComponentName"` so the client-side hydration runtime
|
|
120
|
+
can look up the right module to call.
|
|
121
|
+
|
|
122
|
+
## Runtime helper
|
|
123
|
+
|
|
124
|
+
The hydration runtime imports (or inlines) `scheduleHydrate` from this
|
|
125
|
+
package:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { scheduleHydrate } from "@takazudo/zfb/runtime";
|
|
129
|
+
|
|
130
|
+
for (const el of document.querySelectorAll<HTMLElement>("[data-zfb-island]")) {
|
|
131
|
+
const when = el.getAttribute("data-when") ?? "load";
|
|
132
|
+
scheduleHydrate(el, when, () => hydrateOne(el));
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`scheduleHydrate` returns a `cancel` function that aborts the schedule
|
|
137
|
+
if hydration has not fired yet. After firing, calling `cancel` is a
|
|
138
|
+
no-op.
|
|
139
|
+
|
|
140
|
+
## Markdown / GFM config
|
|
141
|
+
|
|
142
|
+
`ZfbConfig.markdown.gfm` controls which GitHub-Flavored-Markdown
|
|
143
|
+
constructs the MDX parser recognises. The field accepts three shapes:
|
|
144
|
+
|
|
145
|
+
1. **Shorthand boolean** — turn every GFM construct on or off in one
|
|
146
|
+
step. Use this when you want the full GFM surface.
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
// zfb.config.ts
|
|
150
|
+
import { defineConfig } from "zfb/config";
|
|
151
|
+
|
|
152
|
+
export default defineConfig({
|
|
153
|
+
markdown: {
|
|
154
|
+
gfm: true, // strikethrough + table + autolink-literal + task-list-item + footnote-definition
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
2. **Partial object** — toggle individual constructs. Fields you omit
|
|
160
|
+
fall back to the conservative default (`strikethrough: true`,
|
|
161
|
+
`table: true`, everything else off).
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
// zfb.config.ts
|
|
165
|
+
import { defineConfig } from "zfb/config";
|
|
166
|
+
|
|
167
|
+
export default defineConfig({
|
|
168
|
+
markdown: {
|
|
169
|
+
gfm: {
|
|
170
|
+
strikethrough: true,
|
|
171
|
+
table: true,
|
|
172
|
+
autolinkLiteral: false, // explicit opt-out
|
|
173
|
+
taskListItem: false,
|
|
174
|
+
footnoteDefinition: false,
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
3. **Omitted entirely** — the parser uses the conservative default.
|
|
181
|
+
`~~text~~` parses as `<del>text</del>` and pipe tables render as
|
|
182
|
+
`<table>`; every other GFM construct stays off.
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
export default defineConfig({
|
|
186
|
+
// no `markdown` field — strikethrough + table on, everything else off
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The five constructs you can toggle are: `strikethrough`, `table`,
|
|
191
|
+
`autolinkLiteral`, `taskListItem`, `footnoteDefinition`.
|
|
192
|
+
|
|
193
|
+
Projects that previously relied on raw `~~text~~` passing through as
|
|
194
|
+
literal characters should set `markdown: { gfm: { strikethrough: false } }`
|
|
195
|
+
or `markdown: { gfm: false }` to restore the old behaviour.
|
|
196
|
+
|
|
197
|
+
## Tests
|
|
198
|
+
|
|
199
|
+
```sh
|
|
200
|
+
pnpm --filter @takazudo/zfb test
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
The tests run under [vitest][vitest] with [happy-dom][happy-dom] as the
|
|
204
|
+
DOM implementation; no real browser is required.
|
|
205
|
+
|
|
206
|
+
[vitest]: https://vitest.dev/
|
|
207
|
+
[happy-dom]: https://github.com/capricorn86/happy-dom
|
package/bin/zfb.mjs
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Followed biome's pattern: pure os/cpu lookup → resolve platform package → spawn binary.
|
|
3
|
+
// See: https://github.com/biomejs/biome (packages/js/biome/bin/biome.mjs reference)
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
|
|
11
|
+
// Map of os-cpu keys to the corresponding optional platform package name.
|
|
12
|
+
// On Windows, npm generates zfb.cmd / zfb.ps1 wrappers from the bin field;
|
|
13
|
+
// the shebang above is used on Unix only.
|
|
14
|
+
const platformPackages = {
|
|
15
|
+
"darwin-arm64": "@takazudo/zfb-darwin-arm64",
|
|
16
|
+
"darwin-x64": "@takazudo/zfb-darwin-x64",
|
|
17
|
+
"linux-arm64": "@takazudo/zfb-linux-arm64-gnu",
|
|
18
|
+
"linux-x64": "@takazudo/zfb-linux-x64-gnu",
|
|
19
|
+
"win32-x64": "@takazudo/zfb-win32-x64-msvc",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const key = `${process.platform}-${process.arch}`;
|
|
23
|
+
const pkg = platformPackages[key];
|
|
24
|
+
|
|
25
|
+
if (!pkg) {
|
|
26
|
+
console.error(`[zfb] unsupported platform: ${key}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let binPath;
|
|
31
|
+
try {
|
|
32
|
+
// Resolve the platform package's package.json to get the install directory.
|
|
33
|
+
const pkgJsonPath = require.resolve(`${pkg}/package.json`);
|
|
34
|
+
const pkgDir = pkgJsonPath.replace(/[\\/]package\.json$/, "");
|
|
35
|
+
const binName = process.platform === "win32" ? "zfb.exe" : "zfb";
|
|
36
|
+
binPath = join(pkgDir, binName);
|
|
37
|
+
} catch {
|
|
38
|
+
console.error(
|
|
39
|
+
`[zfb] platform binary not installed: ${pkg}\n` +
|
|
40
|
+
" Some installers skip optionalDependencies. Reinstall with full deps,\n" +
|
|
41
|
+
" e.g. `npm install --include=optional` or `pnpm install` without\n" +
|
|
42
|
+
" `--no-optional` / `--ignore-scripts`.",
|
|
43
|
+
);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Explicit existence check: the package directory may be linked (e.g. via pnpm
|
|
48
|
+
// workspace) even before the Wave-5 workflow places the real binary. Without
|
|
49
|
+
// this guard, spawnSync would silently ENOENT instead of printing a clear error.
|
|
50
|
+
if (!existsSync(binPath)) {
|
|
51
|
+
console.error(
|
|
52
|
+
`[zfb] platform binary not installed: ${pkg}\n` +
|
|
53
|
+
" Some installers skip optionalDependencies. Reinstall with full deps,\n" +
|
|
54
|
+
" e.g. `npm install --include=optional` or `pnpm install` without\n" +
|
|
55
|
+
" `--no-optional` / `--ignore-scripts`.",
|
|
56
|
+
);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const result = spawnSync(binPath, process.argv.slice(2), { stdio: "inherit" });
|
|
61
|
+
|
|
62
|
+
// Surface spawn errors that spawnSync stores in result.error rather than
|
|
63
|
+
// propagating to stderr. Without this, a 0644 binary (EACCES) is silently
|
|
64
|
+
// swallowed and the process exits with code 1 and no message — making it
|
|
65
|
+
// impossible for the user to diagnose a corrupted/incomplete npm install.
|
|
66
|
+
// (Issue #447 / #441 — pnpm publish strips the executable bit.)
|
|
67
|
+
if (result.error) {
|
|
68
|
+
if (result.error.code === "EACCES") {
|
|
69
|
+
process.stderr.write(
|
|
70
|
+
`[zfb] binary is not executable; was the install corrupt?\n` +
|
|
71
|
+
` ${binPath}\n` +
|
|
72
|
+
` Try reinstalling: npm install --include=optional\n`,
|
|
73
|
+
);
|
|
74
|
+
} else {
|
|
75
|
+
process.stderr.write(
|
|
76
|
+
`[zfb] failed to spawn binary: ${result.error.message}\n` + ` ${binPath}\n`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
process.exit(result.status ?? 1);
|