@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.
Files changed (45) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE +21 -0
  3. package/README.md +207 -0
  4. package/bin/zfb.mjs +82 -0
  5. package/dist/config.d.ts +542 -0
  6. package/dist/config.d.ts.map +1 -0
  7. package/dist/config.js +24 -0
  8. package/dist/config.js.map +1 -0
  9. package/dist/content.d.ts +240 -0
  10. package/dist/content.d.ts.map +1 -0
  11. package/dist/content.js +460 -0
  12. package/dist/content.js.map +1 -0
  13. package/dist/frontmatter.d.ts +23 -0
  14. package/dist/frontmatter.d.ts.map +1 -0
  15. package/dist/frontmatter.js +142 -0
  16. package/dist/frontmatter.js.map +1 -0
  17. package/dist/index.d.ts +7 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +21 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/island.d.ts +121 -0
  22. package/dist/island.d.ts.map +1 -0
  23. package/dist/island.js +273 -0
  24. package/dist/island.js.map +1 -0
  25. package/dist/jsx-types.d.ts +37 -0
  26. package/dist/jsx-types.d.ts.map +1 -0
  27. package/dist/jsx-types.js +12 -0
  28. package/dist/jsx-types.js.map +1 -0
  29. package/dist/paginate.d.ts +43 -0
  30. package/dist/paginate.d.ts.map +1 -0
  31. package/dist/paginate.js +44 -0
  32. package/dist/paginate.js.map +1 -0
  33. package/dist/plugins.d.ts +259 -0
  34. package/dist/plugins.d.ts.map +1 -0
  35. package/dist/plugins.js +42 -0
  36. package/dist/plugins.js.map +1 -0
  37. package/dist/runtime.d.ts +101 -0
  38. package/dist/runtime.d.ts.map +1 -0
  39. package/dist/runtime.js +454 -0
  40. package/dist/runtime.js.map +1 -0
  41. package/dist/types.d.ts +27 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/types.js +33 -0
  44. package/dist/types.js.map +1 -0
  45. 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);