@waveso/docs 0.1.0 → 0.3.0

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 (79) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +592 -88
  3. package/dist/code-frame.d.ts +29 -0
  4. package/dist/code-frame.js +41 -0
  5. package/dist/code-meta.d.ts +48 -0
  6. package/dist/code-meta.js +72 -0
  7. package/dist/docs-content-id.d.ts +19 -0
  8. package/dist/docs-content-id.js +19 -0
  9. package/dist/docs-error.d.ts +19 -0
  10. package/dist/docs-error.js +28 -0
  11. package/dist/errors.d.ts +94 -0
  12. package/dist/errors.js +45 -0
  13. package/dist/frontmatter.d.ts +39 -7
  14. package/dist/frontmatter.js +51 -24
  15. package/dist/highlighter.d.ts +2 -2
  16. package/dist/highlighter.js +3 -2
  17. package/dist/map-pooled.d.ts +26 -0
  18. package/dist/map-pooled.js +45 -0
  19. package/dist/meta.d.ts +7 -3
  20. package/dist/meta.js +61 -15
  21. package/dist/next.d.ts +182 -35
  22. package/dist/next.js +177 -49
  23. package/dist/plugins/rehype-capture-toc.js +52 -20
  24. package/dist/plugins/rehype-code-frame.d.ts +10 -0
  25. package/dist/plugins/rehype-code-frame.js +88 -0
  26. package/dist/plugins/rehype-code-language.d.ts +24 -0
  27. package/dist/plugins/rehype-code-language.js +54 -0
  28. package/dist/plugins/rehype-fallback-heading-ids.d.ts +6 -0
  29. package/dist/plugins/rehype-fallback-heading-ids.js +51 -0
  30. package/dist/plugins/rehype-flatten-roots.d.ts +7 -0
  31. package/dist/plugins/rehype-flatten-roots.js +39 -0
  32. package/dist/plugins/remark-doc-links.d.ts +12 -1
  33. package/dist/plugins/remark-doc-links.js +147 -20
  34. package/dist/react/code-runtime.d.ts +14 -0
  35. package/dist/react/code-runtime.js +161 -0
  36. package/dist/react/doc-content.d.ts +39 -2
  37. package/dist/react/doc-content.js +42 -10
  38. package/dist/react/layout.d.ts +44 -0
  39. package/dist/react/layout.js +65 -0
  40. package/dist/react/markdown-components.js +71 -6
  41. package/dist/react/nav.d.ts +28 -0
  42. package/dist/react/nav.js +70 -0
  43. package/dist/react/nearest-scroll-top.d.ts +45 -0
  44. package/dist/react/nearest-scroll-top.js +44 -0
  45. package/dist/react/next-link.d.ts +34 -0
  46. package/dist/react/next-link.js +30 -0
  47. package/dist/react/next-nav.d.ts +11 -0
  48. package/dist/react/next-nav.js +32 -0
  49. package/dist/react/next-search.d.ts +22 -0
  50. package/dist/react/next-search.js +52 -0
  51. package/dist/react/search-dialog.d.ts +35 -7
  52. package/dist/react/search-dialog.js +55 -33
  53. package/dist/react/shell-labels.d.ts +43 -0
  54. package/dist/react/shell-labels.js +27 -0
  55. package/dist/react/sidebar.d.ts +38 -3
  56. package/dist/react/sidebar.js +104 -12
  57. package/dist/react/skip-link.d.ts +1 -9
  58. package/dist/react/skip-link.js +6 -5
  59. package/dist/react/toc.d.ts +12 -4
  60. package/dist/react/toc.js +46 -12
  61. package/dist/react/youtube.d.ts +31 -5
  62. package/dist/react/youtube.js +76 -52
  63. package/dist/render.d.ts +78 -10
  64. package/dist/render.js +137 -54
  65. package/dist/route-path.d.ts +46 -0
  66. package/dist/route-path.js +51 -0
  67. package/dist/search-index.d.ts +22 -21
  68. package/dist/search-index.js +27 -78
  69. package/dist/search-options.d.ts +32 -1
  70. package/dist/search-options.js +66 -3
  71. package/dist/section-boundary.d.ts +17 -0
  72. package/dist/section-boundary.js +43 -0
  73. package/dist/sitemap-limit.d.ts +34 -0
  74. package/dist/sitemap-limit.js +37 -0
  75. package/dist/source.d.ts +12 -22
  76. package/dist/source.js +165 -72
  77. package/dist/styles.css +1117 -125
  78. package/dist/types.d.ts +52 -29
  79. package/package.json +70 -34
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { StandardSchemaV1 } from "@standard-schema/spec";
2
1
  import { Root } from "hast";
2
+ import { StandardSchemaV1 } from "@standard-schema/spec";
3
3
  //#region src/types.d.ts
4
4
  /**
5
5
  * The frontmatter fields the package itself understands.
@@ -7,17 +7,24 @@ import { Root } from "hast";
7
7
  * Consumers extend this with their own schema — {@link DocsConfig.frontmatterSchema} —
8
8
  * and the extra fields flow through the generic parameter on {@link DocFile}
9
9
  * and friends rather than widening this interface.
10
+ *
11
+ * The optionals are spelled `?: T | undefined` because a schema written with
12
+ * `.optional()` instead of `.exactOptional()` infers exactly that shape, and
13
+ * under `exactOptionalPropertyTypes` the narrow spelling rejects it — as a
14
+ * nine-line error through Standard Schema's internals at the config, plus a
15
+ * `Property 'audience' does not exist on type 'DocFrontmatter'` at every read
16
+ * site, because the inference then collapses to this default.
10
17
  */
11
18
  interface DocFrontmatter {
12
19
  /** Page title. Used for `<h1>` fallbacks, `<title>`, and search. */
13
20
  title: string;
14
21
  /** One-line summary. Used for `<meta name="description">` and search. */
15
- description?: string;
22
+ description?: string | undefined;
16
23
  /**
17
24
  * Sidebar label, when it should differ from {@link DocFrontmatter.title}.
18
25
  * Sidebars are narrow; page ancestors are not.
19
26
  */
20
- label?: string;
27
+ label?: string | undefined;
21
28
  /**
22
29
  * Excluded from navigation, search and `generateStaticParams`.
23
30
  *
@@ -25,18 +32,18 @@ interface DocFrontmatter {
25
32
  * production builds, so branching on it would hide drafts in exactly the
26
33
  * place reviewers look. Gate on an explicit config flag instead.
27
34
  */
28
- draft?: boolean;
35
+ draft?: boolean | undefined;
29
36
  /**
30
37
  * Previous URLs for this page, relative to the docs base path
31
38
  * (e.g. `['old-name', 'legacy/old-name']`). The Next adapter turns these
32
39
  * into permanent redirects so a rename never becomes a silent 404.
33
40
  */
34
- aliases?: string[];
41
+ aliases?: string[] | undefined;
35
42
  /**
36
43
  * Sort weight within its directory, for directories without a `meta.json`.
37
44
  * Lower sorts first; pages without an order sort last, alphabetically.
38
45
  */
39
- order?: number;
46
+ order?: number | undefined;
40
47
  }
41
48
  /**
42
49
  * A single documentation page discovered on disk.
@@ -75,7 +82,7 @@ interface DocNavPage {
75
82
  interface DocNavGroup {
76
83
  type: 'group';
77
84
  title: string;
78
- href?: string;
85
+ href?: string | undefined;
79
86
  children: DocNavNode[];
80
87
  }
81
88
  /** A non-interactive heading between groups, from `"---Label---"` in meta.json. */
@@ -108,12 +115,12 @@ type DocNavNode = DocNavPage | DocNavGroup | DocNavSeparator | DocNavLink;
108
115
  */
109
116
  interface DocsMeta {
110
117
  /** Directory title, shown as the group heading. Defaults to the dirname. */
111
- title?: string;
118
+ title?: string | undefined;
112
119
  /** Ordered entries. Omit to sort by frontmatter `order`, then alphabetically. */
113
120
  pages?: Array<string | {
114
121
  title: string;
115
122
  href: string;
116
- }>;
123
+ }> | undefined;
117
124
  }
118
125
  /**
119
126
  * A heading captured from the rendered tree.
@@ -126,7 +133,16 @@ interface DocsMeta {
126
133
  interface TocEntry {
127
134
  id: string;
128
135
  text: string;
129
- /** Heading level, 2–6. `h1` is the page title and is never in the TOC. */
136
+ /**
137
+ * Heading level. `h1` is the page title and is never in the TOC.
138
+ *
139
+ * In practice **2 or 3**: the capture stops at `h3`, measured on a synthetic
140
+ * API reference where taking `h2`–`h6` gave 104 entries against 32. Deeper
141
+ * headings keep their ids and permalinks, so they stay deep-linkable and
142
+ * still open their own sections in search — only the rail entry is dropped.
143
+ * `number` rather than `2 | 3` because `rehypePlugins` is the escape hatch
144
+ * for putting them back, and a literal union would make that a type error.
145
+ */
130
146
  depth: number;
131
147
  children: TocEntry[];
132
148
  }
@@ -244,12 +260,12 @@ href: string, from: DocLinkContext) => string | undefined;
244
260
  */
245
261
  type ImageResolver = (src: string, from: DocLinkContext) => Promise<{
246
262
  src: string;
247
- width?: number;
248
- height?: number;
263
+ width?: number | undefined;
264
+ height?: number | undefined;
249
265
  } | undefined> | {
250
266
  src: string;
251
- width?: number;
252
- height?: number;
267
+ width?: number | undefined;
268
+ height?: number | undefined;
253
269
  } | undefined;
254
270
  /**
255
271
  * How a documentation tree is read.
@@ -267,18 +283,18 @@ interface DocsConfig<TFrontmatter extends DocFrontmatter = DocFrontmatter> {
267
283
  */
268
284
  contentDir: string;
269
285
  /** URL prefix the docs are mounted at. Defaults to `'/docs'`. */
270
- basePath?: string;
286
+ basePath?: string | undefined;
271
287
  /**
272
288
  * Include pages marked `draft: true`. Defaults to `false`.
273
289
  *
274
290
  * Drive this from your own env check — deliberately not `NODE_ENV`.
275
291
  */
276
- includeDrafts?: boolean;
292
+ includeDrafts?: boolean | undefined;
277
293
  /**
278
294
  * Fail the build when an internal link resolves to a page that does not
279
295
  * exist. Defaults to `true`; there is no good reason to turn it off.
280
296
  */
281
- assertLinks?: boolean;
297
+ assertLinks?: boolean | undefined;
282
298
  /**
283
299
  * Validates every page's frontmatter. Defaults to `docFrontmatterSchema`
284
300
  * from `@waveso/docs/frontmatter`.
@@ -301,21 +317,28 @@ interface DocsConfig<TFrontmatter extends DocFrontmatter = DocFrontmatter> {
301
317
  *
302
318
  * Three things are worth knowing before you write one:
303
319
  *
304
- * - **The output must still satisfy {@link DocFrontmatter}.** `title` drives
305
- * the `<h1>` fallback and `<title>`, `draft` the visibility filter,
306
- * `aliases` the redirects, `order`/`label` the sidebar. A schema that
307
- * drops them is a compile error here, not a mystery at render time.
320
+ * - **The package's fields are not yours to drop.** `title` drives the
321
+ * `<h1>` fallback and `<title>`, `draft` the visibility filter, `aliases`
322
+ * the redirects, `order`/`label` the sidebar so all six are re-read from
323
+ * the YAML by `docFrontmatterSchema` and laid back over your output. A
324
+ * bare `z.object({ title, audience })` therefore cannot publish a draft or
325
+ * lose a redirect; it only costs you the six in the inferred type. Nothing
326
+ * in the type system could have caught that: `TFrontmatter extends
327
+ * DocFrontmatter` constrains `title` and nothing else, because the other
328
+ * five are optional. The price is that a `.default()`, `.transform()` or
329
+ * `.coerce` aimed at one of the six is not honoured — the YAML wins.
308
330
  * - **Unknown keys are stripped, by every validator worth using.** The
309
- * parsed frontmatter is exactly what the schema declares, so declare every
310
- * field you intend to read — extending
311
- * `docFrontmatterSchema` is the shortest way to keep the built-ins.
331
+ * parsed frontmatter is exactly what the schema declares plus the six, so
332
+ * declare every field you intend to read — extending
333
+ * `docFrontmatterSchema` is the shortest way to get the built-ins back in
334
+ * the type as well.
312
335
  * - **Identity is load-bearing.** The filesystem scan is memoised per
313
336
  * resolved config, and two schema objects are only "the same schema" when
314
337
  * they are the same object. Export one from a shared module (as above)
315
338
  * rather than building it inline in each route file, or each file pays for
316
339
  * its own scan.
317
340
  */
318
- frontmatterSchema?: StandardSchemaV1<unknown, TFrontmatter>;
341
+ frontmatterSchema?: StandardSchemaV1<unknown, TFrontmatter> | undefined;
319
342
  }
320
343
  /** {@link DocsConfig} with defaults applied. */
321
344
  interface ResolvedDocsConfig<TFrontmatter extends DocFrontmatter = DocFrontmatter> {
@@ -324,11 +347,11 @@ interface ResolvedDocsConfig<TFrontmatter extends DocFrontmatter = DocFrontmatte
324
347
  includeDrafts: boolean;
325
348
  assertLinks: boolean;
326
349
  /**
327
- * As supplied. Absent never explicitly `undefined`, per
328
- * `exactOptionalPropertyTypes` when the built-in `docFrontmatterSchema`
329
- * applies, so the default lives in one place: `parseFrontmatter`.
350
+ * As supplied. `resolveDocsConfig` omits the key rather than setting it to
351
+ * `undefined` when the built-in `docFrontmatterSchema` applies, so the
352
+ * default lives in exactly one place: `parseFrontmatter`.
330
353
  */
331
- frontmatterSchema?: StandardSchemaV1<unknown, TFrontmatter>;
354
+ frontmatterSchema?: StandardSchemaV1<unknown, TFrontmatter> | undefined;
332
355
  }
333
356
  //#endregion
334
357
  export { DocFile, DocFrontmatter, DocLinkContext, DocNavGroup, DocNavLink, DocNavNode, DocNavPage, DocNavSeparator, DocsConfig, DocsMeta, ImageResolver, LinkResolver, RenderedDoc, ResolvedDocsConfig, SearchRecord, TocEntry };
package/package.json CHANGED
@@ -1,22 +1,19 @@
1
1
  {
2
2
  "name": "@waveso/docs",
3
- "version": "0.1.0",
4
- "description": "Markdown documentation for Next.js parsed to hast in Node at build time, rendered as your own React components, zero parser bytes in the browser",
3
+ "version": "0.3.0",
4
+ "description": "Zero parser bytes in the browser: markdown docs for Next.js, built to hast in Node and rendered as your components",
5
5
  "type": "module",
6
6
  "sideEffects": [
7
7
  "*.css"
8
8
  ],
9
9
  "license": "MIT",
10
+ "author": "Wave (https://github.com/wavedotso)",
10
11
  "keywords": [
12
+ "nextjs",
11
13
  "documentation",
12
- "docs",
13
14
  "markdown",
14
- "remark",
15
- "rehype",
16
- "shiki",
17
- "nextjs",
18
15
  "react-server-components",
19
- "static-site"
16
+ "shiki"
20
17
  ],
21
18
  "repository": {
22
19
  "type": "git",
@@ -25,7 +22,7 @@
25
22
  "homepage": "https://github.com/wavedotso/wave-docs#readme",
26
23
  "bugs": "https://github.com/wavedotso/wave-docs/issues",
27
24
  "engines": {
28
- "node": ">=20.19.0"
25
+ "node": ">=22.12.0"
29
26
  },
30
27
  "exports": {
31
28
  "./package.json": "./package.json",
@@ -34,6 +31,10 @@
34
31
  "types": "./dist/types.d.ts",
35
32
  "default": "./dist/types.js"
36
33
  },
34
+ "./errors": {
35
+ "types": "./dist/errors.d.ts",
36
+ "default": "./dist/errors.js"
37
+ },
37
38
  "./frontmatter": {
38
39
  "types": "./dist/frontmatter.d.ts",
39
40
  "default": "./dist/frontmatter.js"
@@ -58,30 +59,59 @@
58
59
  "browser": null,
59
60
  "default": "./dist/search-index.js"
60
61
  },
61
- "./search-options": {
62
- "types": "./dist/search-options.d.ts",
63
- "default": "./dist/search-options.js"
64
- },
65
- "./markdown-links": {
66
- "types": "./dist/plugins/remark-doc-links.d.ts",
67
- "default": "./dist/plugins/remark-doc-links.js"
68
- },
69
62
  "./next": {
70
63
  "types": "./dist/next.d.ts",
71
64
  "browser": null,
72
65
  "default": "./dist/next.js"
73
66
  },
74
- "./react/*": {
75
- "types": "./dist/react/*.d.ts",
76
- "default": "./dist/react/*.js"
67
+ "./react/callout": {
68
+ "types": "./dist/react/callout.d.ts",
69
+ "default": "./dist/react/callout.js"
70
+ },
71
+ "./react/doc-content": {
72
+ "types": "./dist/react/doc-content.d.ts",
73
+ "default": "./dist/react/doc-content.js"
74
+ },
75
+ "./react/markdown-components": {
76
+ "types": "./dist/react/markdown-components.d.ts",
77
+ "default": "./dist/react/markdown-components.js"
78
+ },
79
+ "./react/next-search": {
80
+ "types": "./dist/react/next-search.d.ts",
81
+ "default": "./dist/react/next-search.js"
82
+ },
83
+ "./react/search-dialog": {
84
+ "types": "./dist/react/search-dialog.d.ts",
85
+ "default": "./dist/react/search-dialog.js"
86
+ },
87
+ "./react/sidebar": {
88
+ "types": "./dist/react/sidebar.d.ts",
89
+ "default": "./dist/react/sidebar.js"
90
+ },
91
+ "./react/skip-link": {
92
+ "types": "./dist/react/skip-link.d.ts",
93
+ "default": "./dist/react/skip-link.js"
94
+ },
95
+ "./react/toc": {
96
+ "types": "./dist/react/toc.d.ts",
97
+ "default": "./dist/react/toc.js"
98
+ },
99
+ "./react/youtube": {
100
+ "types": "./dist/react/youtube.d.ts",
101
+ "default": "./dist/react/youtube.js"
77
102
  }
78
103
  },
79
104
  "files": [
80
105
  "dist",
81
106
  "package.json",
82
107
  "README.md",
108
+ "CHANGELOG.md",
83
109
  "LICENSE"
84
110
  ],
111
+ "publishConfig": {
112
+ "access": "public",
113
+ "provenance": true
114
+ },
85
115
  "dependencies": {
86
116
  "@shikijs/langs": "4.4.3",
87
117
  "@shikijs/rehype": "4.4.3",
@@ -89,7 +119,6 @@
89
119
  "@standard-schema/spec": "^1.1.0",
90
120
  "@types/hast": "^3.0.5",
91
121
  "@types/mdast": "^4.0.4",
92
- "gray-matter": "^4.0.3",
93
122
  "hast-util-to-jsx-runtime": "^2.3.6",
94
123
  "hast-util-to-string": "^3.0.1",
95
124
  "minisearch": "^7.2.0",
@@ -102,21 +131,18 @@
102
131
  "shiki": "4.4.3",
103
132
  "unified": "^11.0.5",
104
133
  "unist-util-visit": "^5.1.0",
105
- "vfile": "^6.0.3"
134
+ "vfile": "^6.0.3",
135
+ "vfile-matter": "^5.0.1",
136
+ "zod": "^4.4.3"
106
137
  },
107
138
  "peerDependencies": {
108
139
  "next": "^16.0.0",
109
140
  "react": "^19.0.0",
110
- "react-dom": "^19.0.0",
111
- "tailwindcss": "^4.0.0",
112
- "zod": "^4.4.3"
141
+ "react-dom": "^19.0.0"
113
142
  },
114
143
  "peerDependenciesMeta": {
115
144
  "next": {
116
145
  "optional": true
117
- },
118
- "tailwindcss": {
119
- "optional": true
120
146
  }
121
147
  },
122
148
  "devDependencies": {
@@ -129,22 +155,25 @@
129
155
  "@types/node": "^24.0.0",
130
156
  "@types/react": "^19.2.18",
131
157
  "@types/react-dom": "^19.2.4",
158
+ "@vitest/browser": "4.1.10",
159
+ "@vitest/browser-playwright": "^4.1.10",
160
+ "hast-util-to-html": "^9.0.5",
132
161
  "image-size": "^2.0.2",
133
162
  "jsdom": "^30.0.1",
134
163
  "next": "^16.3.0",
164
+ "playwright": "^1.62.1",
135
165
  "publint": "^0.3.23",
136
166
  "react": "^19.2.3",
137
167
  "react-dom": "^19.2.3",
168
+ "rolldown": "^1.2.4",
138
169
  "tsdown": "^0.22.14",
139
170
  "typescript": "^5.9.3",
140
- "unrun": "^0.3.1",
141
- "vitest": "^4.1.10",
142
- "zod": "^4.4.3"
171
+ "vitest": "^4.1.10"
143
172
  },
144
173
  "devEngines": {
145
174
  "runtime": {
146
175
  "name": "node",
147
- "version": ">=22.18.0",
176
+ "version": ">=26.0.0",
148
177
  "onFail": "warn"
149
178
  }
150
179
  },
@@ -155,12 +184,19 @@
155
184
  "lint": "biome check",
156
185
  "lint:fix": "biome check --write",
157
186
  "format": "biome format --write",
158
- "test": "vitest run",
187
+ "test": "vitest run --project node --project dom",
159
188
  "test:watch": "vitest",
160
189
  "check:package": "publint && attw --pack . --ignore-rules no-resolution cjs-resolves-to-esm",
190
+ "check:readme": "node scripts/check-readme.ts",
161
191
  "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
162
192
  "changeset": "changeset",
163
193
  "version": "changeset version",
164
- "release": "pnpm run build && changeset publish"
194
+ "release": "pnpm run build && changeset publish",
195
+ "test:browser": "vitest run --project browser",
196
+ "test:smoke": "node smoke/check.ts",
197
+ "size": "node scripts/size.ts",
198
+ "build:site": "next build site",
199
+ "dev:site": "next dev site",
200
+ "shoot": "node scripts/shoot.ts"
165
201
  }
166
202
  }