@repraze/kernelkit 0.1.0-alpha.3 → 0.1.0-alpha.5

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 (203) hide show
  1. package/README.md +17 -0
  2. package/dist/components/alert-dialog.d.ts.map +1 -1
  3. package/dist/components/alert-dialog.js +4 -1
  4. package/dist/components/alert-dialog.js.map +1 -1
  5. package/dist/components/alert.js +3 -3
  6. package/dist/components/alert.js.map +1 -1
  7. package/dist/components/badge.js +1 -1
  8. package/dist/components/badge.js.map +1 -1
  9. package/dist/components/button.js +1 -1
  10. package/dist/components/button.js.map +1 -1
  11. package/dist/components/calendar.d.ts +8 -1
  12. package/dist/components/calendar.d.ts.map +1 -1
  13. package/dist/components/calendar.js +33 -11
  14. package/dist/components/calendar.js.map +1 -1
  15. package/dist/components/callout.js +2 -2
  16. package/dist/components/callout.js.map +1 -1
  17. package/dist/components/combobox.js +1 -1
  18. package/dist/components/combobox.js.map +1 -1
  19. package/dist/components/date-picker.d.ts +11 -5
  20. package/dist/components/date-picker.d.ts.map +1 -1
  21. package/dist/components/date-picker.js +29 -7
  22. package/dist/components/date-picker.js.map +1 -1
  23. package/dist/components/description-list.d.ts +26 -0
  24. package/dist/components/description-list.d.ts.map +1 -0
  25. package/dist/components/description-list.js +59 -0
  26. package/dist/components/description-list.js.map +1 -0
  27. package/dist/components/locale.d.ts +19 -0
  28. package/dist/components/locale.d.ts.map +1 -0
  29. package/dist/components/locale.js +54 -0
  30. package/dist/components/locale.js.map +1 -0
  31. package/dist/components/popover.d.ts +1 -1
  32. package/dist/components/popover.d.ts.map +1 -1
  33. package/dist/components/popover.js +2 -2
  34. package/dist/components/popover.js.map +1 -1
  35. package/dist/components/progress.d.ts +19 -6
  36. package/dist/components/progress.d.ts.map +1 -1
  37. package/dist/components/progress.js +18 -3
  38. package/dist/components/progress.js.map +1 -1
  39. package/dist/components/search-dialog.d.ts +6 -1
  40. package/dist/components/search-dialog.d.ts.map +1 -1
  41. package/dist/components/search-dialog.js +13 -1
  42. package/dist/components/search-dialog.js.map +1 -1
  43. package/dist/components/search-trigger.d.ts +4 -1
  44. package/dist/components/search-trigger.d.ts.map +1 -1
  45. package/dist/components/search-trigger.js +5 -2
  46. package/dist/components/search-trigger.js.map +1 -1
  47. package/dist/components/shimmer.d.ts +13 -0
  48. package/dist/components/shimmer.d.ts.map +1 -0
  49. package/dist/components/shimmer.js +21 -0
  50. package/dist/components/shimmer.js.map +1 -0
  51. package/dist/components/spinner.d.ts +28 -6
  52. package/dist/components/spinner.d.ts.map +1 -1
  53. package/dist/components/spinner.js +66 -14
  54. package/dist/components/spinner.js.map +1 -1
  55. package/dist/components/stepper.d.ts +64 -0
  56. package/dist/components/stepper.d.ts.map +1 -0
  57. package/dist/components/stepper.js +167 -0
  58. package/dist/components/stepper.js.map +1 -0
  59. package/dist/components/tag-input.d.ts +47 -0
  60. package/dist/components/tag-input.d.ts.map +1 -0
  61. package/dist/components/tag-input.js +211 -0
  62. package/dist/components/tag-input.js.map +1 -0
  63. package/dist/components/time-input.d.ts +75 -0
  64. package/dist/components/time-input.d.ts.map +1 -0
  65. package/dist/components/time-input.js +341 -0
  66. package/dist/components/time-input.js.map +1 -0
  67. package/dist/components/time-select.d.ts +49 -0
  68. package/dist/components/time-select.d.ts.map +1 -0
  69. package/dist/components/time-select.js +178 -0
  70. package/dist/components/time-select.js.map +1 -0
  71. package/dist/components/timeline.d.ts +41 -0
  72. package/dist/components/timeline.d.ts.map +1 -0
  73. package/dist/components/timeline.js +83 -0
  74. package/dist/components/timeline.js.map +1 -0
  75. package/dist/components/toast.d.ts.map +1 -1
  76. package/dist/components/toast.js +4 -2
  77. package/dist/components/toast.js.map +1 -1
  78. package/dist/components/tree.d.ts +87 -0
  79. package/dist/components/tree.d.ts.map +1 -0
  80. package/dist/components/tree.js +516 -0
  81. package/dist/components/tree.js.map +1 -0
  82. package/dist/docs/doc-kit.d.ts +72 -0
  83. package/dist/docs/doc-kit.d.ts.map +1 -0
  84. package/dist/docs/doc-kit.js +45 -0
  85. package/dist/docs/doc-kit.js.map +1 -0
  86. package/dist/docs/doc-page.d.ts +27 -0
  87. package/dist/docs/doc-page.d.ts.map +1 -0
  88. package/dist/docs/doc-page.js +33 -0
  89. package/dist/docs/doc-page.js.map +1 -0
  90. package/dist/docs/docs-index.d.ts +60 -0
  91. package/dist/docs/docs-index.d.ts.map +1 -0
  92. package/dist/docs/docs-index.js +45 -0
  93. package/dist/docs/docs-index.js.map +1 -0
  94. package/dist/docs/docs-nav.d.ts +13 -0
  95. package/dist/docs/docs-nav.d.ts.map +1 -0
  96. package/dist/docs/docs-nav.js +8 -0
  97. package/dist/docs/docs-nav.js.map +1 -0
  98. package/dist/docs/docs-search.d.ts +19 -0
  99. package/dist/docs/docs-search.d.ts.map +1 -0
  100. package/dist/docs/docs-search.js +33 -0
  101. package/dist/docs/docs-search.js.map +1 -0
  102. package/dist/docs/index.d.ts +7 -0
  103. package/dist/docs/index.d.ts.map +1 -0
  104. package/dist/docs/index.js +7 -0
  105. package/dist/docs/index.js.map +1 -0
  106. package/dist/docs/review-matrix.d.ts +25 -0
  107. package/dist/docs/review-matrix.d.ts.map +1 -0
  108. package/dist/docs/review-matrix.js +31 -0
  109. package/dist/docs/review-matrix.js.map +1 -0
  110. package/dist/hooks/use-theme-palette.d.ts +8 -0
  111. package/dist/hooks/use-theme-palette.d.ts.map +1 -0
  112. package/dist/hooks/use-theme-palette.js +24 -0
  113. package/dist/hooks/use-theme-palette.js.map +1 -0
  114. package/dist/hooks/use-time-format.d.ts +23 -0
  115. package/dist/hooks/use-time-format.d.ts.map +1 -0
  116. package/dist/hooks/use-time-format.js +52 -0
  117. package/dist/hooks/use-time-format.js.map +1 -0
  118. package/dist/lib/color.d.ts +5 -1
  119. package/dist/lib/color.d.ts.map +1 -1
  120. package/dist/lib/color.js +14 -2
  121. package/dist/lib/color.js.map +1 -1
  122. package/dist/lib/recipes.d.ts +5 -4
  123. package/dist/lib/recipes.d.ts.map +1 -1
  124. package/dist/lib/recipes.js +7 -5
  125. package/dist/lib/recipes.js.map +1 -1
  126. package/dist/provider/theme-provider.d.ts +10 -1
  127. package/dist/provider/theme-provider.d.ts.map +1 -1
  128. package/dist/provider/theme-provider.js +23 -8
  129. package/dist/provider/theme-provider.js.map +1 -1
  130. package/dist/site/brand.d.ts +20 -0
  131. package/dist/site/brand.d.ts.map +1 -0
  132. package/dist/site/brand.js +17 -0
  133. package/dist/site/brand.js.map +1 -0
  134. package/dist/site/github-icon.d.ts +4 -0
  135. package/dist/site/github-icon.d.ts.map +1 -0
  136. package/dist/site/github-icon.js +6 -0
  137. package/dist/site/github-icon.js.map +1 -0
  138. package/dist/site/index.d.ts +6 -0
  139. package/dist/site/index.d.ts.map +1 -0
  140. package/dist/site/index.js +6 -0
  141. package/dist/site/index.js.map +1 -0
  142. package/dist/site/suite-footer.d.ts +24 -0
  143. package/dist/site/suite-footer.d.ts.map +1 -0
  144. package/dist/site/suite-footer.js +19 -0
  145. package/dist/site/suite-footer.js.map +1 -0
  146. package/dist/site/suite-header.d.ts +40 -0
  147. package/dist/site/suite-header.d.ts.map +1 -0
  148. package/dist/site/suite-header.js +19 -0
  149. package/dist/site/suite-header.js.map +1 -0
  150. package/dist/site/use-page-title.d.ts +3 -0
  151. package/dist/site/use-page-title.d.ts.map +1 -0
  152. package/dist/site/use-page-title.js +8 -0
  153. package/dist/site/use-page-title.js.map +1 -0
  154. package/dist/vite/index.d.ts +65 -0
  155. package/dist/vite/index.d.ts.map +1 -0
  156. package/dist/vite/index.js +242 -0
  157. package/dist/vite/index.js.map +1 -0
  158. package/package.json +39 -3
  159. package/src/components/alert-dialog.tsx +4 -1
  160. package/src/components/alert.tsx +3 -3
  161. package/src/components/badge.tsx +1 -1
  162. package/src/components/button.tsx +1 -1
  163. package/src/components/calendar.tsx +48 -4
  164. package/src/components/callout.tsx +2 -2
  165. package/src/components/combobox.tsx +1 -1
  166. package/src/components/date-picker.tsx +43 -9
  167. package/src/components/description-list.tsx +107 -0
  168. package/src/components/locale.tsx +67 -0
  169. package/src/components/popover.tsx +3 -1
  170. package/src/components/progress.tsx +70 -4
  171. package/src/components/search-dialog.tsx +14 -1
  172. package/src/components/search-trigger.tsx +8 -2
  173. package/src/components/shimmer.tsx +36 -0
  174. package/src/components/spinner.tsx +126 -23
  175. package/src/components/stepper.tsx +346 -0
  176. package/src/components/tag-input.tsx +345 -0
  177. package/src/components/time-input.tsx +578 -0
  178. package/src/components/time-select.tsx +291 -0
  179. package/src/components/timeline.tsx +190 -0
  180. package/src/components/toast.tsx +4 -4
  181. package/src/components/tree.tsx +832 -0
  182. package/src/docs/doc-kit.tsx +176 -0
  183. package/src/docs/doc-page.tsx +126 -0
  184. package/src/docs/docs-index.ts +108 -0
  185. package/src/docs/docs-nav.tsx +47 -0
  186. package/src/docs/docs-search.tsx +88 -0
  187. package/src/docs/index.ts +6 -0
  188. package/src/docs/review-matrix.tsx +67 -0
  189. package/src/hooks/use-theme-palette.ts +25 -0
  190. package/src/hooks/use-time-format.ts +72 -0
  191. package/src/lib/color.ts +19 -2
  192. package/src/lib/recipes.ts +7 -5
  193. package/src/provider/theme-provider.tsx +34 -9
  194. package/src/site/brand.tsx +41 -0
  195. package/src/site/github-icon.tsx +10 -0
  196. package/src/site/index.ts +5 -0
  197. package/src/site/suite-footer.tsx +68 -0
  198. package/src/site/suite-header.tsx +114 -0
  199. package/src/site/use-page-title.ts +8 -0
  200. package/src/styles/kernelkit.css +58 -0
  201. package/src/styles/site.css +15 -0
  202. package/src/vite/client.d.ts +11 -0
  203. package/src/vite/index.ts +298 -0
@@ -0,0 +1,298 @@
1
+ /// <reference types="node" />
2
+ /**
3
+ * Vite plugins shared by the suite's sites (Node-side: no React, nothing here reaches the browser).
4
+ *
5
+ * ```ts
6
+ * import {docsMetaPlugin, faviconPlugin, spaFallback, suiteSourceConditions, themeScriptPlugin} from "@repraze/kernelkit/vite";
7
+ * ```
8
+ *
9
+ * `vite` is only imported for its types; `typescript` is loaded lazily by docsMetaPlugin / extractFunction.
10
+ */
11
+ import {lstatSync, readdirSync, readFileSync} from "node:fs";
12
+ import {copyFile} from "node:fs/promises";
13
+ import {createRequire} from "node:module";
14
+ import {dirname, join, resolve} from "node:path";
15
+
16
+ import type TS from "typescript";
17
+ import type {Plugin} from "vite";
18
+
19
+ import {themeScript, type ThemeState} from "../provider/theme.ts";
20
+
21
+ // ─── theme script ───────────────────────────────────────────────────────────────────────────────
22
+
23
+ export interface ThemeScriptPluginOptions {
24
+ /** The ThemeProvider's `storageKey`. */
25
+ storageKey: string;
26
+ /** The ThemeProvider's `defaultTheme`. */
27
+ defaults?: Partial<ThemeState>;
28
+ /** The comment in index.html replaced by the script (default `<!-- kernelkit:theme-script -->`). */
29
+ marker?: string;
30
+ /** Script source run first, in the same tag (a migration of an older storage format). */
31
+ prelude?: string;
32
+ }
33
+
34
+ /**
35
+ * Inline the kit's pre-paint theme script into index.html, so the stored mode, contrast, accent and radius
36
+ * apply before first paint. Replaces the marker comment, or goes first in <head> when there is none.
37
+ */
38
+ export function themeScriptPlugin({
39
+ storageKey,
40
+ defaults,
41
+ marker = "<!-- kernelkit:theme-script -->",
42
+ prelude,
43
+ }: ThemeScriptPluginOptions): Plugin {
44
+ const source = [prelude, themeScript({storageKey, defaults})].filter(Boolean).join("\n");
45
+ return {
46
+ name: "kernelkit-theme-script",
47
+ transformIndexHtml: (html) =>
48
+ html.includes(marker)
49
+ ? html.replace(marker, `<script>${source}</script>`)
50
+ : {html, tags: [{tag: "script", children: source, injectTo: "head-prepend"}]},
51
+ };
52
+ }
53
+
54
+ // ─── favicon ────────────────────────────────────────────────────────────────────────────────────
55
+
56
+ /**
57
+ * Serve (dev) and emit (build) `favicon.svg` from the brand mark's SVG, so the icon never drifts from the
58
+ * logo. Link it with `href="%BASE_URL%favicon.svg"` so it works under a Pages base path.
59
+ */
60
+ export function faviconPlugin(svg: string | (() => string), fileName = "favicon.svg"): Plugin {
61
+ const source = typeof svg === "function" ? svg() : svg;
62
+ return {
63
+ name: "kernelkit-favicon",
64
+ configureServer(server) {
65
+ server.middlewares.use((req, res, next) => {
66
+ if (req.url?.split("?")[0]?.endsWith(`/${fileName}`)) {
67
+ res.setHeader("Content-Type", "image/svg+xml");
68
+ res.end(source);
69
+ return;
70
+ }
71
+ next();
72
+ });
73
+ },
74
+ generateBundle() {
75
+ this.emitFile({type: "asset", fileName, source});
76
+ },
77
+ };
78
+ }
79
+
80
+ // ─── SPA fallback ───────────────────────────────────────────────────────────────────────────────
81
+
82
+ /** GitHub Pages has no SPA rewrites: copy index.html to 404.html so deep links load the app. */
83
+ export function spaFallback(): Plugin {
84
+ return {
85
+ name: "kernelkit-spa-fallback",
86
+ apply: "build",
87
+ async writeBundle({dir}) {
88
+ if (dir) {
89
+ await copyFile(join(dir, "index.html"), join(dir, "404.html"));
90
+ }
91
+ },
92
+ };
93
+ }
94
+
95
+ // ─── linked suite packages ──────────────────────────────────────────────────────────────────────
96
+
97
+ /** Is `node_modules/<name>` a symlink (npm link, a workspace), looking up from `root`? */
98
+ function isLinked(name: string, root: string): boolean {
99
+ for (let dir = resolve(root); ; dir = dirname(dir)) {
100
+ try {
101
+ return lstatSync(join(dir, "node_modules", name)).isSymbolicLink();
102
+ } catch {
103
+ // not installed here: look in the parent
104
+ }
105
+ if (dirname(dir) === dir) {
106
+ return false;
107
+ }
108
+ }
109
+ }
110
+
111
+ export interface SuiteSourceOptions {
112
+ /** Packages with React contexts to keep as one copy when a linked package brings its own (default below). */
113
+ dedupe?: string[];
114
+ /** The exports condition pointing at TypeScript sources (default "@repraze/source"). */
115
+ condition?: string;
116
+ }
117
+
118
+ const DEDUPE = ["react", "react-dom", "@base-ui/react", "cmdk", "lucide-react"];
119
+
120
+ /**
121
+ * For each suite package (`@repraze/kernelkit`…) that is symlinked into node_modules (`npm link`, a
122
+ * workspace): serve it from its TypeScript sources (its `@repraze/source` exports condition), unbundled,
123
+ * so edits there hot-reload; dedupe React-context packages, since a linked package sits outside this tree.
124
+ * Installed from npm: nothing changes, its built dist is used and pre-bundled like any dependency.
125
+ */
126
+ export function suiteSourceConditions(packages: string[], options: SuiteSourceOptions = {}): Plugin {
127
+ const {dedupe = DEDUPE, condition = "@repraze/source"} = options;
128
+ return {
129
+ name: "kernelkit-suite-source-conditions",
130
+ enforce: "pre",
131
+ config(config) {
132
+ const linked = packages.filter((name) => isLinked(name, config.root ?? process.cwd()));
133
+ if (!linked.length) {
134
+ return undefined;
135
+ }
136
+ return {
137
+ resolve: {conditions: [condition, ...DEFAULT_CLIENT_CONDITIONS], dedupe},
138
+ optimizeDeps: {exclude: linked},
139
+ };
140
+ },
141
+ };
142
+ }
143
+
144
+ /** Vite's `defaultClientConditions`, inlined so this module needs no runtime import of vite. */
145
+ const DEFAULT_CLIENT_CONDITIONS = ["module", "browser", "development|production"];
146
+
147
+ // ─── docs manifest and demo sources ─────────────────────────────────────────────────────────────
148
+
149
+ const require = createRequire(import.meta.url);
150
+ let typescript: typeof TS | undefined;
151
+ /** TypeScript, loaded on first use (the sites already have it): only docs sites pay for it. */
152
+ const ts = () => (typescript ??= require("typescript") as typeof TS);
153
+
154
+ /** Static value of a string/number expression: literals, no-substitution templates, "a" + "b". */
155
+ function literal(node: TS.Expression): string | number | undefined {
156
+ const t = ts();
157
+ if (t.isStringLiteral(node) || t.isNoSubstitutionTemplateLiteral(node)) {
158
+ return node.text;
159
+ }
160
+ if (t.isNumericLiteral(node)) {
161
+ return Number(node.text);
162
+ }
163
+ if (t.isParenthesizedExpression(node)) {
164
+ return literal(node.expression);
165
+ }
166
+ if (t.isBinaryExpression(node) && node.operatorToken.kind === t.SyntaxKind.PlusToken) {
167
+ const a = literal(node.left);
168
+ const b = literal(node.right);
169
+ return a !== undefined && b !== undefined ? `${a}${b}` : undefined;
170
+ }
171
+ return undefined;
172
+ }
173
+
174
+ /** What `virtual:docs-meta` lists per page. */
175
+ export interface DocsMetaEntry {
176
+ slug: string;
177
+ title: string;
178
+ category: string;
179
+ description: string;
180
+ order?: number;
181
+ }
182
+
183
+ /** A docs page's manifest entry, read from its `defineDoc({...})` call without running it. */
184
+ export function readDocMeta(file: string, slug: string, source = readFileSync(file, "utf8")): DocsMetaEntry {
185
+ const t = ts();
186
+ const sf = t.createSourceFile(file, source, t.ScriptTarget.Latest, true);
187
+ let meta: DocsMetaEntry | undefined;
188
+ const visit = (node: TS.Node) => {
189
+ if (
190
+ !meta &&
191
+ t.isCallExpression(node) &&
192
+ t.isIdentifier(node.expression) &&
193
+ node.expression.text === "defineDoc" &&
194
+ node.arguments[0] &&
195
+ t.isObjectLiteralExpression(node.arguments[0])
196
+ ) {
197
+ const props: Record<string, string | number | undefined> = {};
198
+ for (const p of node.arguments[0].properties) {
199
+ if (t.isPropertyAssignment(p) && t.isIdentifier(p.name)) {
200
+ props[p.name.text] = literal(p.initializer);
201
+ }
202
+ }
203
+ if (typeof props.title !== "string" || typeof props.category !== "string") {
204
+ throw new Error(`${file}: defineDoc needs literal "title" and "category" (read at build time)`);
205
+ }
206
+ meta = {
207
+ slug,
208
+ title: props.title,
209
+ category: props.category,
210
+ description: typeof props.description === "string" ? props.description : "",
211
+ ...(typeof props.order === "number" ? {order: props.order} : {}),
212
+ };
213
+ }
214
+ t.forEachChild(node, visit);
215
+ };
216
+ visit(sf);
217
+ if (!meta) {
218
+ throw new Error(`${file}: no defineDoc({...}) call found`);
219
+ }
220
+ return meta;
221
+ }
222
+
223
+ /** Each top-level function or const of a file, by name, as written (without `export `). */
224
+ export function extractFunctions(source: string, fileName = "doc.tsx"): Record<string, string> {
225
+ const t = ts();
226
+ const sf = t.createSourceFile(fileName, source, t.ScriptTarget.Latest, true, t.ScriptKind.TSX);
227
+ const out: Record<string, string> = {};
228
+ const text = (node: TS.Node) => source.slice(node.getStart(sf), node.getEnd()).replace(/^export /, "");
229
+ for (const statement of sf.statements) {
230
+ if (t.isFunctionDeclaration(statement) && statement.name) {
231
+ out[statement.name.text] = text(statement);
232
+ } else if (t.isVariableStatement(statement)) {
233
+ for (const decl of statement.declarationList.declarations) {
234
+ if (t.isIdentifier(decl.name)) {
235
+ out[decl.name.text] = text(statement);
236
+ }
237
+ }
238
+ }
239
+ }
240
+ return out;
241
+ }
242
+
243
+ /**
244
+ * A demo's own source: its declaration from `export function Name` (or `export const Name`) to its
245
+ * closing brace, parsed with TypeScript, so code after it (a `Notes()` helper, constants) stays out.
246
+ */
247
+ export function extractFunction(source: string, name: string): string {
248
+ return extractFunctions(source)[name] ?? "";
249
+ }
250
+
251
+ const META_ID = "virtual:docs-meta";
252
+ const META_RESOLVED = `\0${META_ID}`;
253
+ const SOURCES_QUERY = "demo-sources";
254
+
255
+ /**
256
+ * The docs build step, for `@repraze/kernelkit/docs`:
257
+ * - `virtual:docs-meta`: title, category, description and order of every page in `docsDir`, read
258
+ * statically from its `defineDoc({...})` call, so the nav and search don't pull every page's demos into
259
+ * the main bundle;
260
+ * - `<page>.tsx?demo-sources`: `{functions}`, each demo's own source for its Code tab (see extractFunction).
261
+ *
262
+ * Types: add `@repraze/kernelkit/vite/client` to the site's tsconfig `types`.
263
+ */
264
+ export function docsMetaPlugin(docsDir: string): Plugin {
265
+ const dir = resolve(docsDir);
266
+ return {
267
+ name: "kernelkit-docs-meta",
268
+ enforce: "pre",
269
+ resolveId: (id) => (id === META_ID ? META_RESOLVED : undefined),
270
+ load(id) {
271
+ if (id === META_RESOLVED) {
272
+ const files = readdirSync(dir).filter((f) => f.endsWith(".tsx"));
273
+ const metas = files.map((f) => {
274
+ const file = join(dir, f);
275
+ this.addWatchFile(file);
276
+ return readDocMeta(file, f.replace(/\.tsx$/, ""));
277
+ });
278
+ return `export default ${JSON.stringify(metas)};`;
279
+ }
280
+ const [file = "", query = ""] = id.split("?");
281
+ if (query.split("&").includes(SOURCES_QUERY)) {
282
+ this.addWatchFile(file);
283
+ const functions = extractFunctions(readFileSync(file, "utf8"), file);
284
+ return {code: `export default ${JSON.stringify({functions})};`, moduleType: "js"};
285
+ }
286
+ return undefined;
287
+ },
288
+ // a docs page added, removed or retitled → refresh the manifest
289
+ handleHotUpdate({file, server}) {
290
+ if (file.startsWith(dir)) {
291
+ const mod = server.moduleGraph.getModuleById(META_RESOLVED);
292
+ if (mod) {
293
+ server.moduleGraph.invalidateModule(mod);
294
+ }
295
+ }
296
+ },
297
+ };
298
+ }