create-zudo-doc 0.2.21 → 0.2.22
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/dist/features/image-enlarge.d.ts +10 -2
- package/dist/features/image-enlarge.js +10 -2
- package/dist/scaffold.js +20 -22
- package/dist/zfb-config-gen.d.ts +16 -10
- package/dist/zfb-config-gen.js +34 -239
- package/package.json +1 -1
- package/templates/base/pages/lib/_body-end-islands.tsx +6 -13
- package/templates/base/pages/lib/_category-nav.tsx +30 -115
- package/templates/base/pages/lib/_category-tree-nav.tsx +38 -65
- package/templates/base/pages/lib/_compose-meta-title.ts +2 -6
- package/templates/base/pages/lib/_doc-body-end.tsx +3 -35
- package/templates/base/pages/lib/_doc-content-header.tsx +10 -111
- package/templates/base/pages/lib/_doc-history-area.tsx +19 -211
- package/templates/base/pages/lib/_doc-metainfo-area.tsx +10 -113
- package/templates/base/pages/lib/_doc-page-renderer.tsx +22 -183
- package/templates/base/pages/lib/_doc-page-shell.tsx +17 -251
- package/templates/base/pages/lib/_doc-pager.tsx +4 -74
- package/templates/base/pages/lib/_doc-route-entries.ts +43 -177
- package/templates/base/pages/lib/_doc-route-paths.ts +16 -101
- package/templates/base/pages/lib/_doc-tags-area.tsx +14 -77
- package/templates/base/pages/lib/_footer-with-defaults.tsx +37 -225
- package/templates/base/pages/lib/_frontmatter-preview-data.ts +5 -31
- package/templates/base/pages/lib/_head-with-defaults.tsx +13 -138
- package/templates/base/pages/lib/_header-with-defaults.tsx +24 -324
- package/templates/base/pages/lib/_inline-version-switcher.tsx +16 -78
- package/templates/base/pages/lib/_nav-data-prep.ts +32 -51
- package/templates/base/pages/lib/_nav-source-cache.ts +12 -57
- package/templates/base/pages/lib/_nav-source-docs.ts +33 -233
- package/templates/base/pages/lib/_search-widget-script.ts +2 -470
- package/templates/base/pages/lib/_search-widget.tsx +9 -195
- package/templates/base/pages/lib/_sidebar-prepaint.tsx +6 -59
- package/templates/base/pages/lib/_sidebar-with-defaults.tsx +16 -118
- package/templates/base/pages/lib/_site-tree-nav.tsx +29 -80
- package/templates/base/pages/lib/doc-page-props.ts +26 -44
- package/templates/base/pages/lib/locale-merge.ts +32 -145
- package/templates/base/pages/lib/route-enumerators.ts +52 -286
- package/templates/base/pages/robots.txt.tsx +2 -26
- package/templates/base/src/components/ai-chat-modal.tsx +9 -8
- package/templates/base/src/components/doc-history.tsx +9 -8
- package/templates/base/src/components/image-enlarge.tsx +11 -8
- package/templates/base/src/components/sidebar-toggle.tsx +6 -170
- package/templates/base/src/components/sidebar-tree.tsx +6 -548
- package/templates/base/src/components/site-tree-nav.tsx +6 -220
- package/templates/base/src/config/color-scheme-utils.ts +34 -158
- package/templates/base/src/config/i18n.ts +9 -0
- package/templates/base/src/config/z-index-tokens.ts +5 -4
- package/templates/base/src/styles/global.css +5 -579
- package/templates/base/src/utils/base.ts +1 -1
- package/templates/base/src/utils/docs.ts +47 -16
- package/templates/base/src/utils/github.ts +12 -9
- package/templates/base/src/utils/nav-scope.ts +13 -42
- package/templates/base/src/utils/sidebar.ts +18 -86
- package/templates/base/src/utils/slug.ts +10 -53
- package/templates/base/src/utils/smart-break.tsx +12 -120
- package/templates/base/src/utils/tags.ts +25 -68
- package/templates/features/bodyFootUtil/files/src/utils/github.ts +12 -9
- package/templates/features/designTokenPanel/files/src/lib/design-token-panel-bootstrap.ts +13 -39
- package/templates/features/docHistory/files/src/components/doc-history.tsx +8 -636
- package/templates/features/docHistory/files/src/types/doc-history.ts +7 -23
- package/templates/features/docTags/files/pages/lib/_tag-pages.tsx +35 -201
- package/templates/features/imageEnlarge/files/src/components/image-enlarge.tsx +8 -269
- package/templates/features/sidebarToggle/files/src/components/desktop-sidebar-toggle.tsx +6 -99
- package/templates/features/tagGovernance/files/scripts/tags-audit.ts +67 -515
- package/templates/features/versioning/files/pages/lib/_versions-page.tsx +21 -73
- package/templates/base/plugins/connect-adapter.mjs +0 -169
- package/templates/base/plugins/search-index-plugin.mjs +0 -66
- package/templates/base/scripts/gen-z-index.mjs +0 -157
- package/templates/base/src/components/mermaid-enlarge.tsx +0 -490
- package/templates/features/claudeResources/files/plugins/claude-resources-plugin.mjs +0 -47
- package/templates/features/docHistory/files/plugins/doc-history-plugin.mjs +0 -111
- package/templates/features/llmsTxt/files/plugins/llms-txt-plugin.mjs +0 -93
|
@@ -1,85 +1,33 @@
|
|
|
1
1
|
/** @jsxRuntime automatic */
|
|
2
2
|
/** @jsxImportSource preact */
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// that own only their paths() param shapes; URL prefixes and the
|
|
9
|
-
// default-vs-locale href shapes live here.
|
|
3
|
+
// Thin stub — versions-page moved to the package (epic #2344, S8).
|
|
4
|
+
// Calls `createVersionsPageView(deps)` from
|
|
5
|
+
// @takazudo/zudo-doc/versions-page with host singletons injected,
|
|
6
|
+
// then re-exports the resulting component so all existing call sites continue
|
|
7
|
+
// to work unchanged.
|
|
10
8
|
|
|
9
|
+
import { createVersionsPageView } from "@takazudo/zudo-doc/versions-page";
|
|
11
10
|
import { settings } from "@/config/settings";
|
|
12
|
-
import { defaultLocale, t
|
|
11
|
+
import { defaultLocale, t } from "@/config/i18n";
|
|
13
12
|
import { withBase } from "@/utils/base";
|
|
14
|
-
import { DocLayoutWithDefaults } from "@takazudo/zudo-doc/doclayout";
|
|
15
|
-
import { VersionsPageContent } from "@takazudo/zudo-doc/nav-indexing";
|
|
16
|
-
import type { VersionPageEntry, VersionsPageLabels } from "@takazudo/zudo-doc/nav-indexing";
|
|
17
|
-
import type { JSX } from "preact";
|
|
18
13
|
import { FooterWithDefaults } from "./_footer-with-defaults";
|
|
19
14
|
import { HeaderWithDefaults } from "./_header-with-defaults";
|
|
20
15
|
import { HeadWithDefaults } from "./_head-with-defaults";
|
|
21
16
|
import { composeMetaTitle } from "./_compose-meta-title";
|
|
22
17
|
import { BodyEndIslands } from "./_body-end-islands";
|
|
23
18
|
|
|
24
|
-
|
|
25
|
-
* versions configured in settings.versions. */
|
|
26
|
-
export function VersionsPageView({ locale }: { locale: string }): JSX.Element {
|
|
27
|
-
const isDefault = locale === defaultLocale;
|
|
28
|
-
const prefix = isDefault ? "" : `/${locale}`;
|
|
29
|
-
const pageTitle = t("version.page.title", locale);
|
|
19
|
+
export { type VersionsPageViewProps } from "@takazudo/zudo-doc/versions-page";
|
|
30
20
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// Latest docs href — points to the default docs entry point
|
|
46
|
-
const latestHref = withBase(`${prefix}/docs/getting-started`);
|
|
47
|
-
|
|
48
|
-
// Past version entries from settings
|
|
49
|
-
const versions: VersionPageEntry[] = settings.versions
|
|
50
|
-
? settings.versions.map((v) => ({
|
|
51
|
-
slug: v.slug,
|
|
52
|
-
label: v.label ?? v.slug,
|
|
53
|
-
// Version prefix comes BEFORE the locale — the only routed shape is
|
|
54
|
-
// pages/v/[version]/{locale}/docs/...; /{locale}/v/... has no route.
|
|
55
|
-
docsHref: withBase(`/v/${v.slug}${prefix}/docs/getting-started/`),
|
|
56
|
-
banner: v.banner as "unmaintained" | "unreleased" | undefined,
|
|
57
|
-
}))
|
|
58
|
-
: [];
|
|
59
|
-
|
|
60
|
-
return (
|
|
61
|
-
<DocLayoutWithDefaults
|
|
62
|
-
title={composeMetaTitle(pageTitle)}
|
|
63
|
-
head={<HeadWithDefaults title={pageTitle} />}
|
|
64
|
-
lang={locale}
|
|
65
|
-
noindex={settings.noindex}
|
|
66
|
-
hideSidebar={true}
|
|
67
|
-
hideToc={true}
|
|
68
|
-
// Empty fragment suppresses DocLayoutWithDefaults' empty-data default
|
|
69
|
-
// Sidebar island — its marker never hydrates for published-package
|
|
70
|
-
// consumers (zfb#999) and zfb >= next.38 warns about it; the sidebar is
|
|
71
|
-
// hidden on this page anyway (zudolab/zudo-doc#2057).
|
|
72
|
-
sidebarOverride={<></>}
|
|
73
|
-
headerOverride={<HeaderWithDefaults lang={locale as Locale} currentPath={withBase(`${prefix}/docs/versions`)} />}
|
|
74
|
-
footerOverride={<FooterWithDefaults lang={locale} />}
|
|
75
|
-
bodyEndComponents={<BodyEndIslands basePath={settings.base ?? "/"} />}
|
|
76
|
-
enableClientRouter={settings.dynamicPageTransition}
|
|
77
|
-
>
|
|
78
|
-
<VersionsPageContent
|
|
79
|
-
latestHref={latestHref}
|
|
80
|
-
versions={versions}
|
|
81
|
-
labels={labels}
|
|
82
|
-
/>
|
|
83
|
-
</DocLayoutWithDefaults>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
21
|
+
export const VersionsPageView = createVersionsPageView({
|
|
22
|
+
settings,
|
|
23
|
+
defaultLocale,
|
|
24
|
+
t,
|
|
25
|
+
withBase,
|
|
26
|
+
composeMetaTitle,
|
|
27
|
+
components: {
|
|
28
|
+
HeadWithDefaults,
|
|
29
|
+
HeaderWithDefaults,
|
|
30
|
+
FooterWithDefaults,
|
|
31
|
+
BodyEndIslands,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
// Adapter from Connect-style middleware (`(req, res, next) => void`) to
|
|
3
|
-
// the request-response shape zfb's `devMiddleware` lifecycle hook
|
|
4
|
-
// expects (`(req: ZfbDevMiddlewareRequest) => Promise<ZfbDevMiddlewareResponse | undefined>`).
|
|
5
|
-
//
|
|
6
|
-
// zfb's plugin host runs in a separate Node subprocess and only sees a
|
|
7
|
-
// JSON envelope of the request — `{ method, url, headers, body? }` —
|
|
8
|
-
// not real Node IPC. The adapter mocks just enough of `IncomingMessage`
|
|
9
|
-
// and `ServerResponse` for the v2 integration middlewares (which were
|
|
10
|
-
// written against Node's `http` types) to think they are talking to a
|
|
11
|
-
// regular HTTP server, then captures the response status / headers /
|
|
12
|
-
// body and returns them in the shape the host expects.
|
|
13
|
-
//
|
|
14
|
-
// The adapter is shared by every plugin module under this directory so
|
|
15
|
-
// any future Connect-style middleware can be wired into zfb's
|
|
16
|
-
// devMiddleware hook without rewriting it. Lives at the host repo —
|
|
17
|
-
// the v2 integration package keeps its Connect-style API surface so
|
|
18
|
-
// non-zfb embedders (Astro, plain Vite, a unit test) continue to work.
|
|
19
|
-
|
|
20
|
-
/** @import { ZfbDevMiddlewareRequest, ZfbDevMiddlewareResponse } from "@takazudo/zfb/plugins" */
|
|
21
|
-
|
|
22
|
-
import { Buffer } from "node:buffer";
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Connect-style middleware: `(req, res, next) => void`.
|
|
26
|
-
* The req/res parameters are typed as `any` here because this adapter
|
|
27
|
-
* intentionally passes a plain-object shim that structurally satisfies the
|
|
28
|
-
* subset of `IncomingMessage` / `ServerResponse` that the v2 integration
|
|
29
|
-
* middlewares actually access — not the full Node.js types.
|
|
30
|
-
* @typedef {(req: any, res: any, next: (err?: unknown) => void) => void} ConnectMiddleware
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Convert a Connect-style middleware to a zfb devMiddleware handler.
|
|
35
|
-
* The returned async function takes a `ZfbDevMiddlewareRequest` and
|
|
36
|
-
* returns either `undefined` (passthrough — zfb falls through to its
|
|
37
|
-
* built-in routes) or a `ZfbDevMiddlewareResponse` envelope.
|
|
38
|
-
*
|
|
39
|
-
* Behaviour:
|
|
40
|
-
*
|
|
41
|
-
* - `next()` from the middleware → resolves with `undefined`
|
|
42
|
-
* (passthrough).
|
|
43
|
-
* - `res.end(body)` → resolves with `{ status, headers, body }`.
|
|
44
|
-
* `status` defaults to 200 if the middleware didn't set one,
|
|
45
|
-
* mirroring Node's `ServerResponse` default.
|
|
46
|
-
* - `next(err)` or a thrown error → rejects so the host surfaces a
|
|
47
|
-
* 500 with the error message the same way it does for any other
|
|
48
|
-
* plugin throw.
|
|
49
|
-
* - Binary bodies (Buffer / Uint8Array) → encoded as base64 and
|
|
50
|
-
* flagged `bodyEncoding: "base64"` so the JSON envelope round-trip
|
|
51
|
-
* stays loss-less.
|
|
52
|
-
*
|
|
53
|
-
* @param {ConnectMiddleware} middleware
|
|
54
|
-
* @returns {(zfbReq: ZfbDevMiddlewareRequest) => Promise<ZfbDevMiddlewareResponse | undefined>}
|
|
55
|
-
*/
|
|
56
|
-
export function connectToZfbHandler(middleware) {
|
|
57
|
-
return (zfbReq) => {
|
|
58
|
-
return new Promise((resolveResponse, rejectResponse) => {
|
|
59
|
-
// Build a minimal `IncomingMessage` shim. Only the fields the v2
|
|
60
|
-
// integration middlewares actually read are populated — `method`,
|
|
61
|
-
// `url`, and `headers`. Body parsing is not used by any of the
|
|
62
|
-
// three middlewares (they're all GET routes), so we leave the
|
|
63
|
-
// stream surface unimplemented.
|
|
64
|
-
const req = {
|
|
65
|
-
method: zfbReq.method,
|
|
66
|
-
url: zfbReq.url,
|
|
67
|
-
headers: zfbReq.headers ?? {},
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// Build a `ServerResponse` shim that captures status, headers,
|
|
71
|
-
// and body. We expose the API surface the v2 middlewares touch
|
|
72
|
-
// today (`statusCode`, `setHeader`, `getHeader`, `end`) — extend
|
|
73
|
-
// here if a future middleware needs more.
|
|
74
|
-
let statusCode = 200;
|
|
75
|
-
/** @type {Record<string, string>} */
|
|
76
|
-
const headers = {};
|
|
77
|
-
let settled = false;
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* @param {string | Buffer | Uint8Array | null | undefined} body
|
|
81
|
-
* @returns {void}
|
|
82
|
-
*/
|
|
83
|
-
const finish = (body) => {
|
|
84
|
-
if (settled) return;
|
|
85
|
-
settled = true;
|
|
86
|
-
// Lower-case header names so the host's response shape matches
|
|
87
|
-
// axum's expectation (`Record<string, string>` of arbitrary
|
|
88
|
-
// case). Last-wins on collision; with `setHeader` callers this
|
|
89
|
-
// shouldn't happen.
|
|
90
|
-
/** @type {Record<string, string>} */
|
|
91
|
-
const normalisedHeaders = {};
|
|
92
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
93
|
-
normalisedHeaders[k.toLowerCase()] = String(v);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (Buffer.isBuffer(body) || body instanceof Uint8Array) {
|
|
97
|
-
resolveResponse({
|
|
98
|
-
status: statusCode,
|
|
99
|
-
headers: normalisedHeaders,
|
|
100
|
-
body: Buffer.from(body).toString("base64"),
|
|
101
|
-
bodyEncoding: "base64",
|
|
102
|
-
});
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
resolveResponse({
|
|
106
|
-
status: statusCode,
|
|
107
|
-
headers: normalisedHeaders,
|
|
108
|
-
body: body == null ? "" : String(body),
|
|
109
|
-
bodyEncoding: "utf8",
|
|
110
|
-
});
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const res = {
|
|
114
|
-
get statusCode() {
|
|
115
|
-
return statusCode;
|
|
116
|
-
},
|
|
117
|
-
/** @param {number} v */
|
|
118
|
-
set statusCode(v) {
|
|
119
|
-
statusCode = v;
|
|
120
|
-
},
|
|
121
|
-
/**
|
|
122
|
-
* @param {string} name
|
|
123
|
-
* @param {string | number | readonly string[]} value
|
|
124
|
-
*/
|
|
125
|
-
setHeader(name, value) {
|
|
126
|
-
// Store under lowercased key to avoid duplicate-case collisions
|
|
127
|
-
// when finish() merges headers — last-wins is then unambiguous.
|
|
128
|
-
headers[name.toLowerCase()] = String(value);
|
|
129
|
-
},
|
|
130
|
-
/** @param {string} name */
|
|
131
|
-
getHeader(name) {
|
|
132
|
-
// Keys are always stored lowercased (see setHeader), so a direct
|
|
133
|
-
// lowercased lookup is sufficient and avoids an O(n) scan.
|
|
134
|
-
return headers[name.toLowerCase()];
|
|
135
|
-
},
|
|
136
|
-
get headersSent() {
|
|
137
|
-
return settled;
|
|
138
|
-
},
|
|
139
|
-
/** @param {string | Buffer | Uint8Array | null | undefined} [body] */
|
|
140
|
-
end(body) {
|
|
141
|
-
finish(body);
|
|
142
|
-
},
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
/** @param {unknown} [err] */
|
|
146
|
-
const next = (err) => {
|
|
147
|
-
if (settled) return;
|
|
148
|
-
if (err) {
|
|
149
|
-
settled = true;
|
|
150
|
-
rejectResponse(err instanceof Error ? err : new Error(String(err)));
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
// Connect's `next()` with no error means "I did not handle
|
|
154
|
-
// this request". Resolve with `undefined` so zfb's host
|
|
155
|
-
// surfaces a passthrough.
|
|
156
|
-
settled = true;
|
|
157
|
-
resolveResponse(undefined);
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
middleware(req, res, next);
|
|
162
|
-
} catch (err) {
|
|
163
|
-
if (settled) return;
|
|
164
|
-
settled = true;
|
|
165
|
-
rejectResponse(err instanceof Error ? err : new Error(String(err)));
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
};
|
|
169
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
// zfb plugin module: search-index.
|
|
3
|
-
//
|
|
4
|
-
// Wires two lifecycle hooks for the search-index integration:
|
|
5
|
-
//
|
|
6
|
-
// postBuild — invokes `emitSearchIndex` to write `dist/search-index.json`.
|
|
7
|
-
// There is no settings gate — the index is always emitted,
|
|
8
|
-
// matching the legacy Astro behaviour.
|
|
9
|
-
//
|
|
10
|
-
// devMiddleware — rebuilds the in-memory search index from disk on every
|
|
11
|
-
// request so authoring edits surface without a dev-server
|
|
12
|
-
// restart. Registered at `/search-index.json`.
|
|
13
|
-
//
|
|
14
|
-
// `options` carries `{ docsDir, locales, base }` from the matching entry
|
|
15
|
-
// in `zfb.config.ts`.
|
|
16
|
-
//
|
|
17
|
-
// Inline functions are not supported by zfb's plugin runtime; see the
|
|
18
|
-
// sibling `doc-history-plugin.mjs` for the rationale.
|
|
19
|
-
|
|
20
|
-
/** @import { ZfbBuildHookContext, ZfbDevMiddlewareContext, ZfbPlugin } from "@takazudo/zfb/plugins" */
|
|
21
|
-
/** @import { SearchIndexBuildOptions, SearchIndexConfig } from "@takazudo/zudo-doc/integrations/search-index" */
|
|
22
|
-
|
|
23
|
-
import { emitSearchIndex, createSearchIndexDevMiddleware } from "@takazudo/zudo-doc/integrations/search-index";
|
|
24
|
-
import { connectToZfbHandler } from "./connect-adapter.mjs";
|
|
25
|
-
|
|
26
|
-
/** @type {ZfbPlugin} */
|
|
27
|
-
export default {
|
|
28
|
-
name: "search-index",
|
|
29
|
-
|
|
30
|
-
/** @param {ZfbBuildHookContext} ctx */
|
|
31
|
-
async postBuild(ctx) {
|
|
32
|
-
await emitSearchIndex(/** @type {SearchIndexBuildOptions} */ (/** @type {unknown} */ ({
|
|
33
|
-
...ctx.options,
|
|
34
|
-
outDir: ctx.outDir,
|
|
35
|
-
logger: ctx.logger,
|
|
36
|
-
})));
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
/** @param {ZfbDevMiddlewareContext} ctx */
|
|
40
|
-
devMiddleware(ctx) {
|
|
41
|
-
const middleware = createSearchIndexDevMiddleware(
|
|
42
|
-
/** @type {SearchIndexConfig} */ (/** @type {unknown} */ (ctx.options)),
|
|
43
|
-
);
|
|
44
|
-
// zfb's `register(path, handler)` matches against the FULL request
|
|
45
|
-
// URL (no base-stripping). For a non-root base (e.g. "/my-docs/"),
|
|
46
|
-
// requests arrive as `/my-docs/search-index.json`, so we register
|
|
47
|
-
// the full base-prefixed route. For base="/", the prefix is empty
|
|
48
|
-
// and the route is `/search-index.json` as expected. The v2
|
|
49
|
-
// middleware itself is base-tolerant (matches via
|
|
50
|
-
// `endsWith("/search-index.json")`), so it does not need a
|
|
51
|
-
// separate base-stripping pass.
|
|
52
|
-
const basePrefix = stripTrailingSlash(
|
|
53
|
-
typeof ctx.options["base"] === "string" ? ctx.options["base"] : "",
|
|
54
|
-
);
|
|
55
|
-
ctx.register(`${basePrefix}/search-index.json`, connectToZfbHandler(middleware));
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @param {string} s
|
|
61
|
-
* @returns {string}
|
|
62
|
-
*/
|
|
63
|
-
function stripTrailingSlash(s) {
|
|
64
|
-
if (typeof s !== "string" || s.length === 0) return "";
|
|
65
|
-
return s.endsWith("/") ? s.slice(0, -1) : s;
|
|
66
|
-
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// scripts/gen-z-index.mjs
|
|
3
|
-
//
|
|
4
|
-
// Codegen: rewrite the GENERATED:Z_INDEX marker block inside
|
|
5
|
-
// src/styles/global.css from the single source of truth in
|
|
6
|
-
// src/config/z-index-tokens.ts.
|
|
7
|
-
//
|
|
8
|
-
// The block is a Tailwind v4 `@theme { --z-index-<name>: <value>; }` for every
|
|
9
|
-
// tier, so Tailwind generates `z-<name>` utilities (e.g. `--z-index-toolbar: 20`
|
|
10
|
-
// → `.z-toolbar { z-index: 20 }`) and raw CSS can reference the same var via
|
|
11
|
-
// `z-index: var(--z-index-<name>)`.
|
|
12
|
-
//
|
|
13
|
-
// Pure Node (fs only — NO npm deps). Idempotent: running twice produces no diff.
|
|
14
|
-
//
|
|
15
|
-
// Usage:
|
|
16
|
-
// node scripts/gen-z-index.mjs # rewrite the block in global.css
|
|
17
|
-
// node scripts/gen-z-index.mjs --check # verify committed block is up to date
|
|
18
|
-
// # (exit 1 on drift, no write)
|
|
19
|
-
//
|
|
20
|
-
// MAINTENANCE: edit src/config/z-index-tokens.ts (the source of truth), then run
|
|
21
|
-
// `pnpm gen:z-index` and commit the regenerated global.css. Never hand-edit the
|
|
22
|
-
// block between the BEGIN/END markers.
|
|
23
|
-
|
|
24
|
-
import { readFileSync, writeFileSync } from "node:fs";
|
|
25
|
-
import { resolve, dirname } from "node:path";
|
|
26
|
-
import { fileURLToPath } from "node:url";
|
|
27
|
-
|
|
28
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
29
|
-
const ROOT = resolve(__dirname, "..");
|
|
30
|
-
|
|
31
|
-
const TOKENS_PATH = resolve(ROOT, "src/config/z-index-tokens.ts");
|
|
32
|
-
const CSS_PATH = resolve(ROOT, "src/styles/global.css");
|
|
33
|
-
|
|
34
|
-
const BEGIN_MARKER = "GENERATED:Z_INDEX_BEGIN";
|
|
35
|
-
const END_MARKER = "GENERATED:Z_INDEX_END";
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Parse the Z_INDEX_TIERS array out of z-index-tokens.ts WITHOUT importing it
|
|
39
|
-
* (this script is a dependency-free .mjs and cannot resolve TypeScript). Reads
|
|
40
|
-
* each `{ name: "...", value: <n>, ... }` object literal. Throws on a malformed
|
|
41
|
-
* source so drift between the parser and the file surfaces loudly.
|
|
42
|
-
*/
|
|
43
|
-
function parseTiers(src) {
|
|
44
|
-
const arrayMatch = src.match(
|
|
45
|
-
/export const Z_INDEX_TIERS[^=]*=\s*\[([\s\S]*?)\];/,
|
|
46
|
-
);
|
|
47
|
-
if (!arrayMatch) {
|
|
48
|
-
throw new Error(
|
|
49
|
-
`Could not locate "export const Z_INDEX_TIERS = [ ... ]" in ${TOKENS_PATH}`,
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
const body = arrayMatch[1];
|
|
53
|
-
const tiers = [];
|
|
54
|
-
// Each tier is a `{ ... }` object literal; iterate top-level braces.
|
|
55
|
-
const objectRe = /\{([\s\S]*?)\}/g;
|
|
56
|
-
let m;
|
|
57
|
-
while ((m = objectRe.exec(body)) !== null) {
|
|
58
|
-
const obj = m[1];
|
|
59
|
-
const nameMatch = obj.match(/name:\s*"([^"]+)"/);
|
|
60
|
-
const valueMatch = obj.match(/value:\s*(-?\d+)/);
|
|
61
|
-
if (!nameMatch || !valueMatch) {
|
|
62
|
-
throw new Error(
|
|
63
|
-
`Malformed tier object in Z_INDEX_TIERS (missing name/value): ${obj.trim()}`,
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
tiers.push({ name: nameMatch[1], value: Number(valueMatch[1]) });
|
|
67
|
-
}
|
|
68
|
-
if (tiers.length === 0) {
|
|
69
|
-
throw new Error(`Z_INDEX_TIERS in ${TOKENS_PATH} parsed to an empty list`);
|
|
70
|
-
}
|
|
71
|
-
return tiers;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Build the full generated block (markers included). Two leading spaces of
|
|
76
|
-
* indentation match the surrounding `@theme` style in global.css.
|
|
77
|
-
*/
|
|
78
|
-
function buildBlock(tiers) {
|
|
79
|
-
const lines = [];
|
|
80
|
-
lines.push(` /* ${BEGIN_MARKER}`);
|
|
81
|
-
lines.push(
|
|
82
|
-
` * GENERATED:Z_INDEX — do not hand-edit; run pnpm gen:z-index.`,
|
|
83
|
-
);
|
|
84
|
-
lines.push(
|
|
85
|
-
` * Source of truth: src/config/z-index-tokens.ts. Tailwind v4 reads the`,
|
|
86
|
-
);
|
|
87
|
-
lines.push(
|
|
88
|
-
` * --z-index-<name> theme key and generates a z-<name> utility. */`,
|
|
89
|
-
);
|
|
90
|
-
lines.push(` @theme {`);
|
|
91
|
-
for (const tier of tiers) {
|
|
92
|
-
lines.push(` --z-index-${tier.name}: ${tier.value};`);
|
|
93
|
-
}
|
|
94
|
-
lines.push(` }`);
|
|
95
|
-
lines.push(` /* ${END_MARKER} */`);
|
|
96
|
-
return lines.join("\n");
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Replace the existing BEGIN…END block in `css` with `block`. Throws if the
|
|
101
|
-
* markers are missing (the block must be seeded once by hand — see global.css).
|
|
102
|
-
*/
|
|
103
|
-
function replaceBlock(css, block) {
|
|
104
|
-
const beginIdx = css.indexOf(BEGIN_MARKER);
|
|
105
|
-
const endIdx = css.indexOf(END_MARKER);
|
|
106
|
-
if (beginIdx === -1 || endIdx === -1) {
|
|
107
|
-
throw new Error(
|
|
108
|
-
`Could not find ${BEGIN_MARKER} … ${END_MARKER} markers in ${CSS_PATH}.\n` +
|
|
109
|
-
`Seed the marker block once by hand, then re-run the generator.`,
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
// Expand to the full comment line that opens the block (" /* GENERATED:...")
|
|
113
|
-
// and to the end of the closing "*/ " line so the whole region is replaced.
|
|
114
|
-
const lineStart = css.lastIndexOf("\n", beginIdx) + 1;
|
|
115
|
-
const afterEnd = css.indexOf("\n", endIdx);
|
|
116
|
-
const lineEnd = afterEnd === -1 ? css.length : afterEnd;
|
|
117
|
-
return css.slice(0, lineStart) + block + css.slice(lineEnd);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function main() {
|
|
121
|
-
const check = process.argv.includes("--check");
|
|
122
|
-
|
|
123
|
-
const tokensSrc = readFileSync(TOKENS_PATH, "utf8");
|
|
124
|
-
const css = readFileSync(CSS_PATH, "utf8");
|
|
125
|
-
|
|
126
|
-
const tiers = parseTiers(tokensSrc);
|
|
127
|
-
const block = buildBlock(tiers);
|
|
128
|
-
const next = replaceBlock(css, block);
|
|
129
|
-
|
|
130
|
-
if (check) {
|
|
131
|
-
if (next !== css) {
|
|
132
|
-
console.error(
|
|
133
|
-
"z-index codegen drift detected: src/styles/global.css is out of date.",
|
|
134
|
-
);
|
|
135
|
-
console.error("Run `pnpm gen:z-index` and commit the result.");
|
|
136
|
-
return 1;
|
|
137
|
-
}
|
|
138
|
-
console.log(
|
|
139
|
-
`OK — z-index @theme block is up to date (${tiers.length} tiers).`,
|
|
140
|
-
);
|
|
141
|
-
return 0;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (next === css) {
|
|
145
|
-
console.log(
|
|
146
|
-
`z-index @theme block already up to date (${tiers.length} tiers); no change.`,
|
|
147
|
-
);
|
|
148
|
-
return 0;
|
|
149
|
-
}
|
|
150
|
-
writeFileSync(CSS_PATH, next);
|
|
151
|
-
console.log(
|
|
152
|
-
`Wrote z-index @theme block to src/styles/global.css (${tiers.length} tiers).`,
|
|
153
|
-
);
|
|
154
|
-
return 0;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
process.exit(main());
|