docpensieve 0.5.0-beta.3 → 0.5.0-beta.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docpensieve",
3
- "version": "0.5.0-beta.3",
3
+ "version": "0.5.0-beta.4",
4
4
  "description": "DocPensieve command-line interface (init, build, check, dev, serve)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,10 +24,10 @@
24
24
  "types"
25
25
  ],
26
26
  "dependencies": {
27
- "@docpensieve/components": "0.5.0-beta.3",
28
- "@docpensieve/core": "0.5.0-beta.3",
29
- "@docpensieve/shared": "0.5.0-beta.3",
30
- "@docpensieve/theme": "0.5.0-beta.3",
27
+ "@docpensieve/components": "0.5.0-beta.4",
28
+ "@docpensieve/core": "0.5.0-beta.4",
29
+ "@docpensieve/shared": "0.5.0-beta.4",
30
+ "@docpensieve/theme": "0.5.0-beta.4",
31
31
  "chalk": "^6.0.0",
32
32
  "chokidar": "^5.0.0",
33
33
  "commander": "^15.0.0"
@@ -399,6 +399,10 @@ function renderConfig({ name, theme, siteUrl, version, translation = '' }) {
399
399
  ' // A search field in the header, and a search page built with the site.',
400
400
  ' // Content pages load no script: the search page alone does.',
401
401
  ' search: true,',
402
+ '',
403
+ ' // llms.txt at the root of the site, and a Markdown copy beside each',
404
+ ' // page: what a program reads instead of the HTML. It needs no siteUrl.',
405
+ ' llms: false,',
402
406
  '};',
403
407
  '',
404
408
  '// Every field is described in the reference of the DocPensieve documentation:',
package/src/server.js CHANGED
@@ -4,8 +4,7 @@
4
4
  * @module docpensieve/server
5
5
  */
6
6
 
7
- import { createReadStream } from 'node:fs';
8
- import { stat } from 'node:fs/promises';
7
+ import { readFile } from 'node:fs/promises';
9
8
  import { createServer } from 'node:http';
10
9
  import path from 'node:path';
11
10
 
@@ -134,9 +133,15 @@ export function createStaticServer({ root, basePath = '/', inject = null, onRelo
134
133
  return;
135
134
  }
136
135
 
137
- let stats;
136
+ // Read whole, then answer. `dev` empties the version folder at every
137
+ // rebuild while a browser may still be asking for it: between a `stat`
138
+ // and a stream the file could vanish, the stream then threw with nobody
139
+ // listening, and the server went down in the middle of a save. Read at
140
+ // once, a file is either there or answered 404 — and its length is the
141
+ // one actually sent, not the one a rebuild was busy changing.
142
+ let body;
138
143
  try {
139
- stats = await stat(filepath);
144
+ body = await readFile(filepath);
140
145
  } catch {
141
146
  response.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });
142
147
  response.end(`404 — ${url.pathname}`);
@@ -147,18 +152,17 @@ export function createStaticServer({ root, basePath = '/', inject = null, onRelo
147
152
  const headers = { 'content-type': type, 'cache-control': 'no-cache' };
148
153
 
149
154
  if (inject && type.startsWith('text/html')) {
150
- const { readFile } = await import('node:fs/promises');
151
- const html = await readFile(filepath, 'utf8');
152
- const body = html.includes('</body>')
155
+ const html = body.toString('utf8');
156
+ const page = html.includes('</body>')
153
157
  ? html.replace('</body>', `${inject}</body>`)
154
158
  : html + inject;
155
- response.writeHead(200, { ...headers, 'content-length': Buffer.byteLength(body) });
156
- response.end(body);
159
+ response.writeHead(200, { ...headers, 'content-length': Buffer.byteLength(page) });
160
+ response.end(page);
157
161
  return;
158
162
  }
159
163
 
160
- response.writeHead(200, { ...headers, 'content-length': stats.size });
161
- createReadStream(filepath).pipe(response);
164
+ response.writeHead(200, { ...headers, 'content-length': body.length });
165
+ response.end(body);
162
166
  });
163
167
  }
164
168
 
@@ -160,6 +160,10 @@ For pages that are news rather than reference — release notes, a changelog —
160
160
  give them a `date` and set `feed: true`: `feed.xml` then lists them, newest
161
161
  first. The fields are in the [configuration reference](../reference/configuration/).
162
162
 
163
+ For programs that read documentation on someone's behalf, `llms: true` writes
164
+ `llms.txt` at the root and a Markdown copy beside each page — the text
165
+ without the markup. It needs no `siteUrl`.
166
+
163
167
  ## What the reader downloads
164
168
 
165
169
  The build keeps it light on its own. The stylesheet is minified — a third to
@@ -48,6 +48,7 @@ npx docpensieve check
48
48
  - `copyCode`, a copy button on every code block — off by default, since it is
49
49
  what makes a page load a script. See [the reference](../reference/configuration/).
50
50
  - `snippetIcons`, if your project installs an icon collection.
51
+ - `llms`, for `llms.txt` and a Markdown copy of each page.
51
52
 
52
53
  ## What to check afterwards
53
54
 
@@ -344,3 +344,25 @@ in none: a class of yours always wins, even to undo the default style.
344
344
 
345
345
  The commonest row is a row of cards; [Columns](./columns/) shows how to keep
346
346
  them the same height, which a grid alone does not.
347
+
348
+ ## Properties
349
+
350
+ `Card`:
351
+
352
+ | Property | Type | Effect |
353
+ | ----------- | --------- | ---------------------------------------------------------------- |
354
+ | `elevated` | `boolean` | Adds a shadow. |
355
+ | `href` | `string` | Makes the whole card a link, resolved like any link of the page. |
356
+ | `className` | `string` | Classes added to the card. |
357
+ | `style` | `object` | Inline styles on the card. |
358
+
359
+ `CardImage`:
360
+
361
+ | Property | Type | Effect |
362
+ | -------------------------- | -------- | ---------------------------------------------------------------- |
363
+ | `src` | `string` | The image, resolved like any link of the page. |
364
+ | `alt` | `string` | What the image shows. Empty by default: the image is decorative. |
365
+ | `loading` | `string` | `lazy`, the default, or `eager`. |
366
+ | `title`, `srcSet`, `sizes` | `string` | Passed to the image as they are. |
367
+
368
+ `CardHeader`, `CardBody` and `CardFooter` take `className` and `style`.
@@ -287,3 +287,15 @@ window to check.
287
287
 
288
288
  The most frequent row is a row of cards; [Card](./card/) shows how to keep
289
289
  them to the same height, which a grid alone does not do.
290
+
291
+ ## Properties
292
+
293
+ `Column`:
294
+
295
+ | Property | Type | Effect |
296
+ | ----------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
297
+ | `span` | `number` | Tracks taken out of twelve — `6` for a half, `8` for two thirds. Without it, the columns share the space equally. |
298
+ | `className` | `string` | Classes added to the column. |
299
+ | `style` | `object` | Inline styles on the column. |
300
+
301
+ `Columns` takes `className` and `style`. A `Column` outside a `Columns` stops the build.
@@ -122,3 +122,22 @@ names the faulty value and recalls the expected format.
122
122
  Announcing a release is what this is usually for, and [Versions](../guide/versions/)
123
123
  covers the rest of that cycle — the banner a prerelease carries on its own,
124
124
  and the day the two tracks swap.
125
+
126
+ ## Properties
127
+
128
+ `TimeTimer`:
129
+
130
+ | Property | Type | Effect |
131
+ | ---------- | --------- | ------------------------------------------------------------------------------------- |
132
+ | `date` | `string` | A single day, `DD/MM/YYYY`, optionally followed by `HH:mm`. The year may be left out. |
133
+ | `start` | `string` | Start of a period, in the same format, with `duration`. |
134
+ | `duration` | `string` | Length of the period from `start`: `30d`, `2h`, `45m`. |
135
+ | `strict` | `boolean` | Reads the dates in UTC rather than in local time. |
136
+ | `now` | `Date` | For tests only: without it, the moment of the build stands. |
137
+
138
+ The fallbacks each take one bound:
139
+
140
+ | Property | Type | Effect |
141
+ | --------------------------- | -------- | ----------------------------------------------------------------------- |
142
+ | `start` on `FallbackBefore` | `string` | From when the content before the period shows. Without it, from always. |
143
+ | `end` on `FallbackAfter` | `string` | Until when the content after the period shows. Without it, for good. |
@@ -138,3 +138,12 @@ unnoticed for a long time. So does an unknown side.
138
138
 
139
139
  A tooltip explains a term without leaving the sentence. A remark that needs
140
140
  its own space is an [Admonition](./admonition/) instead.
141
+
142
+ ## Properties
143
+
144
+ | Property | Type | Effect |
145
+ | ----------- | -------- | ------------------------------------------------------------------ |
146
+ | `text` | `string` | Content of the bubble. Required: an empty bubble would never show. |
147
+ | `placement` | `string` | `top`, the default, `bottom`, `left` or `right`. |
148
+ | `className` | `string` | Classes added to the term. |
149
+ | `style` | `object` | Inline styles on the term. |
@@ -195,3 +195,16 @@ label is refused too.
195
195
  A tree is how this documentation shows the shape of a project. What those
196
196
  folders become once built — the order, the addresses — is in
197
197
  [Writing pages](../guide/writing-pages/).
198
+
199
+ ## Properties
200
+
201
+ `TreeItem`:
202
+
203
+ | Property | Type | Effect |
204
+ | ----------- | --------------- | ------------------------------------------- |
205
+ | `label` | text or element | What the line shows. |
206
+ | `open` | `boolean` | Opens the branch as soon as the page opens. |
207
+ | `className` | `string` | Classes added to the line. |
208
+ | `style` | `object` | Inline styles on the line. |
209
+
210
+ `Tree` takes `className` and `style`.
@@ -144,3 +144,13 @@ build is enough to keep it right.
144
144
 
145
145
  Setting one up takes a few lines in the pipeline that already builds the
146
146
  site: [Deployment](../guide/deployment/) has the recipe.
147
+
148
+ ## Properties
149
+
150
+ | Property | Type | Effect |
151
+ | ----------- | -------- | ------------------------------------------------------------------------------------------------------------- |
152
+ | `label` | `string` | Name read by a screen reader. By default, the words of the page's language — "Back to top", "Retour en haut". |
153
+ | `className` | `string` | Classes added to the button. |
154
+ | `style` | `object` | Inline styles on the button. |
155
+
156
+ Its children, when it has any, replace the arrow.
@@ -274,3 +274,19 @@ is full from the start — the value stays readable, which is what matters.
274
274
  A level outside 0–100 stops the build, as does a gauge without a name, a level
275
275
  that is not a number, or an unknown `shape`. A silent or overflowing bar goes
276
276
  unnoticed on review.
277
+
278
+ ## Properties
279
+
280
+ | Property | Type | Effect |
281
+ | ----------- | --------------- | ------------------------------------------------------------------------------ |
282
+ | `name` | text or element | What is measured. |
283
+ | `level` | `number` | The level, as a percentage. |
284
+ | `showValue` | `boolean` | Shows the percentage. On by default; off, the gauge still announces its value. |
285
+ | `shape` | `string` | `bar`, the default, or `circle`. |
286
+ | `icon` | element | Shown before the name — a `LogoIcon` fits there. |
287
+ | `color` | `string` | Colour of the fill: any CSS colour, the accent by default. |
288
+ | `label` | `string` | Name for screen readers, when `name` is not text. |
289
+ | `className` | `string` | Classes added to the gauge. |
290
+ | `style` | `object` | Inline styles on the gauge. |
291
+
292
+ Its children stand as a comment under the gauge.
@@ -171,3 +171,13 @@ A component loads no JavaScript, and this one is not going to introduce any.
171
171
 
172
172
  A kind of admonition takes its mark through this same component — see
173
173
  [Admonition](./admonition/).
174
+
175
+ ## Properties
176
+
177
+ | Property | Type | Effect |
178
+ | ----------- | -------- | --------------------------------------------------------------------------------------- |
179
+ | `src` | `string` | An SVG of the project, or `prefix:name` from an installed icon collection. |
180
+ | `label` | `string` | What the icon means. Without it, the icon is decorative and hidden from screen readers. |
181
+ | `size` | `string` | Any CSS length. |
182
+ | `className` | `string` | Classes added to the icon. |
183
+ | `style` | `object` | Inline styles on the icon. |
@@ -46,3 +46,9 @@ without a word.
46
46
 
47
47
  Which theme a project runs is one field of its configuration, and the whole
48
48
  of what it changes is in [Themes](../guide/themes/).
49
+
50
+ ## Properties
51
+
52
+ | Property | Type | Effect |
53
+ | ----------- | -------- | ---------------------------------------------------------------- |
54
+ | `framework` | `string` | `tailwind` or `custom`: the content shows only under that theme. |
@@ -74,3 +74,12 @@ themes, so this needs no variant:
74
74
  Like every component here, the grid is built at the build. What reaches the
75
75
  reader is a list of links: it works with JavaScript turned off, and a card
76
76
  never waits for anything to become clickable.
77
+
78
+ ## Properties
79
+
80
+ | Property | Type | Effect |
81
+ | ----------- | -------- | ----------------------------------------------------------------------------------------------------- |
82
+ | `of` | `string` | `all`, the default; `series` for the sub-folders alone; `pages` for the pages beside the index alone. |
83
+ | `from` | `string` | Slug of another folder to list, instead of the one holding the page. |
84
+ | `className` | `string` | Classes added to the grid. |
85
+ | `style` | `object` | Inline styles on the grid. |
@@ -127,3 +127,25 @@ A `MenuLink` or a `MenuGroup` written outside a `Menu` stops the build, and so
127
127
  does an entry without a target or a group without a title. Each of these would
128
128
  otherwise render something that looks like a menu and leads nowhere — the kind
129
129
  of fault a review does not catch.
130
+
131
+ ## Properties
132
+
133
+ `Menu`:
134
+
135
+ | Property | Type | Effect |
136
+ | -------- | -------- | ----------------------------------------------------------------------------------------------------------- |
137
+ | `label` | `string` | Name for screen readers, and label of the button it folds into. By default, the page's own word for a menu. |
138
+
139
+ `MenuLink`:
140
+
141
+ | Property | Type | Effect |
142
+ | -------- | -------- | ------------------------------------------------- |
143
+ | `href` | `string` | Where it leads, resolved like any link. Required. |
144
+
145
+ `MenuGroup`:
146
+
147
+ | Property | Type | Effect |
148
+ | -------- | -------- | ------------------------------- |
149
+ | `title` | `string` | Heading of the group. Required. |
150
+
151
+ All three take `className` and `style`.
@@ -152,3 +152,12 @@ Its orphan branch keeps what was built, and the history keeps the sources.
152
152
  Like every component here, it is rendered at the build. It is an `aside`
153
153
  announced as a note, with its icon hidden from screen readers — the title
154
154
  beside it already says the kind, and saying it twice helps nobody.
155
+
156
+ ## Properties
157
+
158
+ | Property | Type | Effect |
159
+ | ----------- | -------- | ---------------------------------------------------------------------------------------------------------- |
160
+ | `type` | `string` | The kind: `note`, the default, `info`, `tip`, `attention`, `alert`, `danger`, or one the project declares. |
161
+ | `title` | `string` | Replaces the label of the kind, for this block alone. |
162
+ | `className` | `string` | Classes added to the block. |
163
+ | `style` | `object` | Inline styles on the block. |
@@ -76,6 +76,7 @@ can change.
76
76
  | `sitemap` | `true` | `sitemap.xml` of the published versions, once `siteUrl` is set |
77
77
  | `feed` | `false` | RSS feed of the dated pages. Needs `siteUrl` |
78
78
  | `search` | `true` | Search field in the header, and a search page built with the site |
79
+ | `llms` | `false` | `llms.txt` at the root of the site, and a Markdown copy of each page |
79
80
 
80
81
  ## Images
81
82
 
@@ -137,6 +138,38 @@ The search page is kept out of search engines (`noindex`) and out of the
137
138
  sitemap. A page of your own at `/search/` would take its place: the build
138
139
  refuses it, and `search: false` frees the address.
139
140
 
141
+ ## `llms`
142
+
143
+ ```js
144
+ llms: true,
145
+ ```
146
+
147
+ A program that reads documentation on someone's behalf — an assistant
148
+ answering a question, a tool gathering the pages of a project — does better
149
+ with the text than with the markup around it. `llms: true` gives it two ways
150
+ in:
151
+
152
+ - **`llms.txt`**, at the root of the site: the project name, the description
153
+ of the home page, then one link per page of the current version, with its
154
+ description. Each language of the version has its own section. The file
155
+ follows the llms.txt proposal, at the address programs look for it.
156
+ - **A Markdown copy of each page**, beside its HTML: `/guide/install/` has its
157
+ copy at `/guide/install/index.html.md`, and every page names its copy in its
158
+ head, with `<link rel="alternate" type="text/markdown">`. The copy is the
159
+ source of the page as you wrote it, headed by its title and description.
160
+
161
+ What a component draws at build time is not in the copy — a grid of cards,
162
+ the file a snippet reads: it says what was written, not what was rendered.
163
+
164
+ It needs no `siteUrl`: the links start from the root of the domain until the
165
+ site has an address, and are absolute once it has one. Like the sitemap,
166
+ `llms.txt` is written by the build of the whole site; `build <version>` alone
167
+ writes the copies of that version only.
168
+
169
+ It is off by default. It doubles the files of a version, and whether programs
170
+ should read your pages is yours to decide. It is a proposal, not a standard:
171
+ reading it is up to each program.
172
+
140
173
  ## `copyCode`
141
174
 
142
175
  `copyCode: true` puts a button on every block of code, which copies it.
@@ -571,6 +571,12 @@ humanised from their slug by `StructuredDataBuilder`.
571
571
 
572
572
  **Returns** `FoldedNode[]`
573
573
 
574
+ ### `MARKDOWN_COPY`
575
+
576
+ `MARKDOWN_COPY`
577
+
578
+ File a page's Markdown copy is written to, beside its HTML.
579
+
574
580
  ### `buildFeed`
575
581
 
576
582
  `buildFeed(pages, site)`
@@ -588,6 +594,25 @@ every page again at every build.
588
594
 
589
595
  **Returns** `string`
590
596
 
597
+ ### `buildLlms`
598
+
599
+ `buildLlms(pages, site)`
600
+
601
+ Builds `llms.txt`: the index of a documentation, for a reader that is a
602
+ program.
603
+
604
+ Follows the proposal as it stands: a title, a one-line summary, then a list
605
+ of links, each to the Markdown copy of a page — the address of the page with
606
+ `index.html.md` appended, since the pages are folders. A program reads that
607
+ copy as the author wrote it, instead of prying the text out of the markup.
608
+
609
+ | Parameter | Type | |
610
+ | --- | --- | --- |
611
+ | `pages` | `PublishedPage[]` | Pages of the version the site serves first. |
612
+ | `site` | `{ projectName: string, summary?: string, base?: string }` | `base` makes each link absolute when the site has a public address, and root-relative otherwise. |
613
+
614
+ **Returns** `string`
615
+
591
616
  ### `buildRobots`
592
617
 
593
618
  `buildRobots(sitemapUrl)`
@@ -616,6 +641,30 @@ that carries neither is listed without one rather than with a made-up date.
616
641
 
617
642
  **Returns** `string`
618
643
 
644
+ ### `markdownCopy`
645
+
646
+ `markdownCopy(frontmatter, content, [resolve])`
647
+
648
+ The Markdown copy of a page: its source, as the author wrote it.
649
+
650
+ The title is added when the page does not open on one, since a program
651
+ handed the copy alone has no frontmatter to read it from. What a component
652
+ draws at build time is not in the copy — a snippet's file, a grid of cards:
653
+ the copy says what was written, not what was rendered.
654
+
655
+ The links are the exception. The build resolves them from the folder of the
656
+ source file, and a version-root one from the version: read from where the
657
+ copy is served, as written, they would lead nowhere. `resolve` gives each the
658
+ address the HTML page carries.
659
+
660
+ | Parameter | Type | |
661
+ | --- | --- | --- |
662
+ | `frontmatter` | `{ title?: string, description?: string }` | |
663
+ | `content` | `string` | Source of the page, frontmatter removed. |
664
+ | `[resolve]` | `(target: string) => string \| null` | Address of a target on the site, or `null` to keep it as written. |
665
+
666
+ **Returns** `string`
667
+
619
668
  ### `buildAuthorTable`
620
669
 
621
670
  `buildAuthorTable(description, options)`
@@ -85,6 +85,16 @@ until the `ui` field gives it its own.
85
85
 
86
86
  [Languages](./guide/languages/) · [The init options](./reference/cli/)
87
87
 
88
+ ### Pages a program can read
89
+
90
+ `llms: true` writes `llms.txt` at the root of the site — the project, then a
91
+ link to every page of the current version — and a Markdown copy beside each
92
+ page, `index.html.md`, that its head names. An assistant asked about your
93
+ project reads the text you wrote rather than prying it out of the HTML. Off by
94
+ default, and it needs no `siteUrl`.
95
+
96
+ [The field](./reference/configuration/)
97
+
88
98
  ## For a 0.4 project
89
99
 
90
100
  Nothing to change: a 0.4 configuration builds as it is, and a site already in