docpensieve 0.5.0-beta.2 → 0.5.0-beta.3
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/bin/docpensieve.js +2 -0
- package/package.json +5 -5
- package/src/commands/build.js +2 -0
- package/src/commands/dev.js +14 -3
- package/src/commands/init.js +10 -0
- package/src/commands/serve.js +5 -3
- package/src/server.js +33 -3
- package/starter/01-guide/02-first-site.md +4 -0
- package/starter/01-guide/06-deployment.md +26 -0
- package/starter/01-guide/07-migrate-to-beta.md +17 -2
- package/starter/02-components/01-card.mdx +3 -0
- package/starter/02-components/13-snippet.mdx +230 -0
- package/starter/02-components/14-hero.mdx +230 -0
- package/starter/02-components/15-calendar.mdx +130 -0
- package/starter/02-components/index.mdx +9 -3
- package/starter/02-components/snippet-example.js +32 -0
- package/starter/02-components/snippet-example.py +19 -0
- package/starter/03-reference/01-cli.md +13 -7
- package/starter/03-reference/02-configuration.md +19 -0
- package/starter/03-reference/05-api.md +266 -3
- package/starter/05-whats-new.md +47 -0
- package/types/commands/dev.d.ts +2 -1
- package/types/commands/serve.d.ts +2 -1
- package/types/server.d.ts +24 -1
package/bin/docpensieve.js
CHANGED
|
@@ -63,6 +63,7 @@ program
|
|
|
63
63
|
.command('dev')
|
|
64
64
|
.description('Development server with reload')
|
|
65
65
|
.option('-p, --port <number>', 'listening port', Number, 3000)
|
|
66
|
+
.option('--host <address>', 'address to listen on; 0.0.0.0 opens it to the network', '127.0.0.1')
|
|
66
67
|
.action(async (options) => {
|
|
67
68
|
await dev(options);
|
|
68
69
|
});
|
|
@@ -71,6 +72,7 @@ program
|
|
|
71
72
|
.command('serve')
|
|
72
73
|
.description('Serves the output folder statically')
|
|
73
74
|
.option('-p, --port <number>', 'listening port', Number, 4000)
|
|
75
|
+
.option('--host <address>', 'address to listen on; 0.0.0.0 opens it to the network', '127.0.0.1')
|
|
74
76
|
.option('-d, --dir <dir>', 'folder to serve')
|
|
75
77
|
.action(async (options) => {
|
|
76
78
|
await serve(options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docpensieve",
|
|
3
|
-
"version": "0.5.0-beta.
|
|
3
|
+
"version": "0.5.0-beta.3",
|
|
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.
|
|
28
|
-
"@docpensieve/core": "0.5.0-beta.
|
|
29
|
-
"@docpensieve/shared": "0.5.0-beta.
|
|
30
|
-
"@docpensieve/theme": "0.5.0-beta.
|
|
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",
|
|
31
31
|
"chalk": "^6.0.0",
|
|
32
32
|
"chokidar": "^5.0.0",
|
|
33
33
|
"commander": "^15.0.0"
|
package/src/commands/build.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
createRegistry,
|
|
12
12
|
setSiteContext,
|
|
13
13
|
setAdmonitionKinds,
|
|
14
|
+
setSnippetIcons,
|
|
14
15
|
} from '@docpensieve/components';
|
|
15
16
|
import { SiteGenerator, loadConfig, resolveVersion } from '@docpensieve/core';
|
|
16
17
|
|
|
@@ -28,6 +29,7 @@ export async function build(versionSlug, options = {}) {
|
|
|
28
29
|
|
|
29
30
|
// The kinds a project declares, beside the ones shipped.
|
|
30
31
|
setAdmonitionKinds(config.admonitions);
|
|
32
|
+
setSnippetIcons(config.snippetIcons);
|
|
31
33
|
|
|
32
34
|
const generator = new SiteGenerator(config, {
|
|
33
35
|
// `globalComponents: false` removes the shipped components: a project that
|
package/src/commands/dev.js
CHANGED
|
@@ -12,12 +12,20 @@ import {
|
|
|
12
12
|
createRegistry,
|
|
13
13
|
setSiteContext,
|
|
14
14
|
setAdmonitionKinds,
|
|
15
|
+
setSnippetIcons,
|
|
15
16
|
} from '@docpensieve/components';
|
|
16
17
|
import { SiteGenerator, loadConfig } from '@docpensieve/core';
|
|
17
18
|
import { CONFIG_FILENAME, DocPensieveError, THEME_FOLDER } from '@docpensieve/shared';
|
|
18
19
|
import chokidar from 'chokidar';
|
|
19
20
|
|
|
20
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
DEFAULT_HOST,
|
|
23
|
+
RELOAD_PATH,
|
|
24
|
+
createStaticServer,
|
|
25
|
+
exposedNotice,
|
|
26
|
+
isExposed,
|
|
27
|
+
listen,
|
|
28
|
+
} from '../server.js';
|
|
21
29
|
import { createTheme } from '../theme.js';
|
|
22
30
|
|
|
23
31
|
/** Default port of `dev`, distinct from that of `serve`. */
|
|
@@ -40,7 +48,7 @@ const RELOAD_SCRIPT =
|
|
|
40
48
|
`.addEventListener("message",()=>location.reload())</script>`;
|
|
41
49
|
|
|
42
50
|
/**
|
|
43
|
-
* @param {{ port?: number, cwd?: string }} [options]
|
|
51
|
+
* @param {{ port?: number, host?: string, cwd?: string }} [options]
|
|
44
52
|
* @returns {Promise<{
|
|
45
53
|
* server: import('node:http').Server,
|
|
46
54
|
* watcher: import('chokidar').FSWatcher,
|
|
@@ -62,6 +70,7 @@ export async function dev(options = {}) {
|
|
|
62
70
|
// Read again with the rest: a kind added to the configuration must show
|
|
63
71
|
// without restarting the command.
|
|
64
72
|
setAdmonitionKinds(current.admonitions);
|
|
73
|
+
setSnippetIcons(current.snippetIcons);
|
|
65
74
|
const generator = new SiteGenerator(current, {
|
|
66
75
|
// `globalComponents: false` removes the shipped components: a project
|
|
67
76
|
// that defines its own thus avoids a name collision. The option was
|
|
@@ -87,8 +96,10 @@ export async function dev(options = {}) {
|
|
|
87
96
|
},
|
|
88
97
|
});
|
|
89
98
|
|
|
90
|
-
const
|
|
99
|
+
const host = options.host ?? DEFAULT_HOST;
|
|
100
|
+
const port = await listen(server, options.port ?? DEFAULT_PORT, 10, host);
|
|
91
101
|
const url = `http://localhost:${port}${config.baseUrl}`;
|
|
102
|
+
if (isExposed(host)) console.log(exposedNotice(host));
|
|
92
103
|
console.log(`served at ${url}`);
|
|
93
104
|
|
|
94
105
|
// The project's own stylesheets, re-read by every rebuild.
|
package/src/commands/init.js
CHANGED
|
@@ -368,6 +368,16 @@ function renderConfig({ name, theme, siteUrl, version, translation = '' }) {
|
|
|
368
368
|
' // Back-to-top button on every page.',
|
|
369
369
|
' scrollToTop: true,',
|
|
370
370
|
'',
|
|
371
|
+
' // A button copying each block of code. It is the one thing that makes a',
|
|
372
|
+
' // content page load a script — a small file, shared by every page of the',
|
|
373
|
+
' // version — so it stays off until you ask for it.',
|
|
374
|
+
' copyCode: false,',
|
|
375
|
+
'',
|
|
376
|
+
' // Icon collection a code block takes the mark of its language from, for',
|
|
377
|
+
" // instance 'simple-icons' once @iconify-json/simple-icons is installed.",
|
|
378
|
+
' // Left empty, the short name of the language is shown instead.',
|
|
379
|
+
" snippetIcons: '',",
|
|
380
|
+
'',
|
|
371
381
|
' // The header stays at the top of the screen. false lets it scroll away',
|
|
372
382
|
' // with the page, and gives its height back to the text.',
|
|
373
383
|
' stickyHeader: true,',
|
package/src/commands/serve.js
CHANGED
|
@@ -10,13 +10,13 @@ import path from 'node:path';
|
|
|
10
10
|
import { loadConfig } from '@docpensieve/core';
|
|
11
11
|
import { DocPensieveError } from '@docpensieve/shared';
|
|
12
12
|
|
|
13
|
-
import { createStaticServer, listen } from '../server.js';
|
|
13
|
+
import { DEFAULT_HOST, createStaticServer, exposedNotice, isExposed, listen } from '../server.js';
|
|
14
14
|
|
|
15
15
|
/** Default port of `serve`, distinct from that of `dev`. */
|
|
16
16
|
const DEFAULT_PORT = 4000;
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* @param {{ port?: number, dir?: string, cwd?: string }} [options]
|
|
19
|
+
* @param {{ port?: number, host?: string, dir?: string, cwd?: string }} [options]
|
|
20
20
|
* @returns {Promise<{ server: import('node:http').Server, port: number, url: string }>}
|
|
21
21
|
* @throws {DocPensieveError} When the folder to serve does not exist.
|
|
22
22
|
*/
|
|
@@ -34,11 +34,13 @@ export async function serve(options = {}) {
|
|
|
34
34
|
// The site is mounted under the configuration's baseUrl: otherwise the links
|
|
35
35
|
// of the generated pages would not resolve locally.
|
|
36
36
|
const server = createStaticServer({ root, basePath: config.baseUrl });
|
|
37
|
-
const
|
|
37
|
+
const host = options.host ?? DEFAULT_HOST;
|
|
38
|
+
const port = await listen(server, options.port ?? DEFAULT_PORT, 10, host);
|
|
38
39
|
const url = `http://localhost:${port}${config.baseUrl}`;
|
|
39
40
|
|
|
40
41
|
console.log(`${root}`);
|
|
41
42
|
console.log(`served at ${url}`);
|
|
43
|
+
if (isExposed(host)) console.log(exposedNotice(host));
|
|
42
44
|
|
|
43
45
|
return { server, port, url };
|
|
44
46
|
}
|
package/src/server.js
CHANGED
|
@@ -162,16 +162,35 @@ export function createStaticServer({ root, basePath = '/', inject = null, onRelo
|
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
/** The loopback address: the site is served to this machine and to no other. */
|
|
166
|
+
export const DEFAULT_HOST = '127.0.0.1';
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Whether an address reaches beyond this machine.
|
|
170
|
+
*
|
|
171
|
+
* @param {string} host
|
|
172
|
+
* @returns {boolean}
|
|
173
|
+
*/
|
|
174
|
+
export function isExposed(host) {
|
|
175
|
+
return !['127.0.0.1', 'localhost', '::1'].includes(host);
|
|
176
|
+
}
|
|
177
|
+
|
|
165
178
|
/**
|
|
166
179
|
* Starts listening, looking for a free port if needed.
|
|
167
180
|
*
|
|
181
|
+
* On this machine only, unless asked otherwise. Called without a host, Node
|
|
182
|
+
* listens on every interface: anyone on the same network — a café, a
|
|
183
|
+
* coworking space — could read the site being written, drafts included.
|
|
184
|
+
*
|
|
168
185
|
* @param {import('node:http').Server} server
|
|
169
186
|
* @param {number} port Desired port.
|
|
170
187
|
* @param {number} [attempts] Number of ports tried from `port` on.
|
|
188
|
+
* @param {string} [host] Address to listen on. `0.0.0.0` opens it to the
|
|
189
|
+
* network, which a phone on the same wifi needs.
|
|
171
190
|
* @returns {Promise<number>} The port actually used.
|
|
172
191
|
* @throws {DocPensieveError} When no port is free in the range.
|
|
173
192
|
*/
|
|
174
|
-
export function listen(server, port, attempts = 10) {
|
|
193
|
+
export function listen(server, port, attempts = 10, host = DEFAULT_HOST) {
|
|
175
194
|
return new Promise((resolve, reject) => {
|
|
176
195
|
let current = port;
|
|
177
196
|
|
|
@@ -181,7 +200,7 @@ export function listen(server, port, attempts = 10) {
|
|
|
181
200
|
// the next one beats forcing --port.
|
|
182
201
|
if (error.code === 'EADDRINUSE' && current < port + attempts - 1) {
|
|
183
202
|
current += 1;
|
|
184
|
-
server.listen(current);
|
|
203
|
+
server.listen(current, host);
|
|
185
204
|
return;
|
|
186
205
|
}
|
|
187
206
|
server.off('error', onError);
|
|
@@ -202,6 +221,17 @@ export function listen(server, port, attempts = 10) {
|
|
|
202
221
|
const address = server.address();
|
|
203
222
|
resolve(typeof address === 'object' && address !== null ? address.port : current);
|
|
204
223
|
});
|
|
205
|
-
server.listen(current);
|
|
224
|
+
server.listen(current, host);
|
|
206
225
|
});
|
|
207
226
|
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Said when the site is opened to the network, so that it is never done
|
|
230
|
+
* without the person knowing.
|
|
231
|
+
*
|
|
232
|
+
* @param {string} host
|
|
233
|
+
* @returns {string}
|
|
234
|
+
*/
|
|
235
|
+
export function exposedNotice(host) {
|
|
236
|
+
return `listening on ${host}: anyone on this network can read the site.`;
|
|
237
|
+
}
|
|
@@ -98,6 +98,10 @@ online.
|
|
|
98
98
|
→ ignores the deployment prefix "/my-project/"
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
+
Two commands, and the second reads what the first wrote — so they belong
|
|
102
|
+
together. [Deployment](./deployment/) shows how to say that once, in a
|
|
103
|
+
`package.json`, and have a workflow run the same thing you run.
|
|
104
|
+
|
|
101
105
|
It is the check to run after any change of `baseUrl`, of folder structure or of
|
|
102
106
|
URL. When something does not add up, [When it breaks](./when-it-breaks/) lists
|
|
103
107
|
the failures this tool produces, by the symptom you see.
|
|
@@ -88,6 +88,32 @@ the tool through `npx`, which takes the latest published version. To pin it
|
|
|
88
88
|
from one build to the next, declare `docpensieve` in a `package.json` and add
|
|
89
89
|
`npm ci` before the build.
|
|
90
90
|
|
|
91
|
+
## One command, for you and for the machine
|
|
92
|
+
|
|
93
|
+
Two commands are one too many to remember, and a workflow should run exactly
|
|
94
|
+
what you run. A `package.json` is the place to say it once:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"scripts": {
|
|
99
|
+
"docs": "docpensieve dev",
|
|
100
|
+
"check": "docpensieve build && docpensieve check"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`npm run check` then generates the site and reads it back. A dead link, a
|
|
106
|
+
nesting the markup cannot carry, a page that stops the build: each one fails
|
|
107
|
+
the command, on your machine and in your workflow, in the same way and with
|
|
108
|
+
the same message.
|
|
109
|
+
|
|
110
|
+
The `&&` is the point of it. `check` reads what `build` wrote, so a check run
|
|
111
|
+
on its own tells you about the site as it was the last time it was generated —
|
|
112
|
+
which, the day you forget, is the day it matters.
|
|
113
|
+
|
|
114
|
+
With that in place the recipe above becomes `npm ci` and `npm run check`, and
|
|
115
|
+
the version of the tool stops depending on the day the workflow ran.
|
|
116
|
+
|
|
91
117
|
## Reading back before publishing
|
|
92
118
|
|
|
93
119
|
A generated site can compile without error and contain dead links.
|
|
@@ -31,8 +31,23 @@ npx docpensieve check
|
|
|
31
31
|
|
|
32
32
|
## What changes on its own
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
- **`dev` and `serve` listen on this machine only.** They used to listen on
|
|
35
|
+
every interface, so anyone on the same network could read the site being
|
|
36
|
+
written. To look at it from a phone on the same wifi, say so:
|
|
37
|
+
`npx docpensieve dev --host 0.0.0.0`.
|
|
38
|
+
- **Code blocks carry a background of their own.** A block in a language the
|
|
39
|
+
highlighter does not know used to sit on the bare page; it now looks like
|
|
40
|
+
every other block.
|
|
41
|
+
- **An error keeps its hint.** An error the tool raises while a page compiles
|
|
42
|
+
no longer comes out as a generic syntax complaint.
|
|
43
|
+
|
|
44
|
+
## What is worth turning on
|
|
45
|
+
|
|
46
|
+
- [Snippet](../components/snippet/), to show a real file rather than a copy.
|
|
47
|
+
- [Hero](../components/hero/) and [Calendar](../components/calendar/).
|
|
48
|
+
- `copyCode`, a copy button on every code block — off by default, since it is
|
|
49
|
+
what makes a page load a script. See [the reference](../reference/configuration/).
|
|
50
|
+
- `snippetIcons`, if your project installs an icon collection.
|
|
36
51
|
|
|
37
52
|
## What to check afterwards
|
|
38
53
|
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Snippet
|
|
3
|
+
description: A code block that names a real file of your project, read when the page is generated, so that the example cannot go stale.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Snippet
|
|
7
|
+
|
|
8
|
+
An example written by hand is a copy. It stops being true the day the code
|
|
9
|
+
changes, and nothing reports it: the page goes on showing something that
|
|
10
|
+
worked once. `Snippet` names the file instead, and the file is read when the
|
|
11
|
+
page is generated — there is no copy left to drift.
|
|
12
|
+
|
|
13
|
+
The idea comes from **Christophe Avonture**, who built it for his own site.
|
|
14
|
+
|
|
15
|
+
## Naming a file
|
|
16
|
+
|
|
17
|
+
<Snippet source="./snippet-example.js" />
|
|
18
|
+
|
|
19
|
+
```mdx
|
|
20
|
+
<Snippet source="./snippet-example.js" />
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The file name labels the block, and the highlighting is the one every code
|
|
24
|
+
block gets. A path beginning with `./` or `../` is read **from the page**, as
|
|
25
|
+
a link is; any other path is read **from the root of your project**, so that
|
|
26
|
+
`src/index.js` means the same file from every page.
|
|
27
|
+
|
|
28
|
+
## Part of a file
|
|
29
|
+
|
|
30
|
+
Two ways, and they do not age the same.
|
|
31
|
+
|
|
32
|
+
A **range of lines** is quick to write and means line 12 of the file as it is
|
|
33
|
+
today:
|
|
34
|
+
|
|
35
|
+
<Snippet source="./snippet-example.js" lines="9-10" title="The words it drops" />
|
|
36
|
+
|
|
37
|
+
```mdx
|
|
38
|
+
<Snippet source="./snippet-example.js" lines="9-10" title="The words it drops" />
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Write `lines="9"` for one line, `"9-10"` for a range, `"9-"` from there to the
|
|
42
|
+
end, `"-10"` from the start. It says what the file holds today, and only
|
|
43
|
+
today.
|
|
44
|
+
|
|
45
|
+
A **region** is a marker left in the source file itself, in a comment. It
|
|
46
|
+
follows the code when the code moves, which is the whole point of naming a
|
|
47
|
+
file rather than copying it:
|
|
48
|
+
|
|
49
|
+
<Snippet source="./snippet-example.js" region="guard" title="The guard" />
|
|
50
|
+
|
|
51
|
+
```mdx
|
|
52
|
+
<Snippet source="./snippet-example.js" region="guard" title="The guard" />
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
In the file, the marker is an ordinary comment — the one editors already fold
|
|
56
|
+
on:
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
// #region guard
|
|
60
|
+
if (!name) {
|
|
61
|
+
throw new Error('greet needs a name');
|
|
62
|
+
}
|
|
63
|
+
// #endregion
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The indentation the kept lines share is removed, so a region marked inside a
|
|
67
|
+
function does not come out shifted.
|
|
68
|
+
|
|
69
|
+
## Folding a long file
|
|
70
|
+
|
|
71
|
+
`collapsed` puts the block inside a `details`. No script: a long file can sit
|
|
72
|
+
in a page without burying what follows it.
|
|
73
|
+
|
|
74
|
+
<Snippet source="./snippet-example.js" collapsed title="The whole file" />
|
|
75
|
+
|
|
76
|
+
```mdx
|
|
77
|
+
<Snippet source="./snippet-example.js" collapsed title="The whole file" />
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Code you write yourself
|
|
81
|
+
|
|
82
|
+
Without `source`, the component frames the code written in the page. A fenced
|
|
83
|
+
block inside the tag is the dependable way to write it: it carries anything,
|
|
84
|
+
braces and `<` included, and no formatter touches what is inside it.
|
|
85
|
+
|
|
86
|
+
<Snippet title="greet.py">
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
def greet(name, greetings={'en': 'Hello'}):
|
|
90
|
+
return f"{greetings['en']}, {name}!"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
</Snippet>
|
|
94
|
+
|
|
95
|
+
````mdx
|
|
96
|
+
<Snippet title="greet.py">
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
def greet(name, greetings={'en': 'Hello'}):
|
|
100
|
+
return f"{greetings['en']}, {name}!"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
</Snippet>
|
|
104
|
+
````
|
|
105
|
+
|
|
106
|
+
The extension of the title names the language; `lang` says it where the title
|
|
107
|
+
does not, as for a shell:
|
|
108
|
+
|
|
109
|
+
<Snippet title="Build the site" lang="bash">
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm install
|
|
113
|
+
npx docpensieve build
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
</Snippet>
|
|
117
|
+
|
|
118
|
+
````mdx
|
|
119
|
+
<Snippet title="Build the site" lang="bash">
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npm install
|
|
123
|
+
npx docpensieve build
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
</Snippet>
|
|
127
|
+
````
|
|
128
|
+
|
|
129
|
+
### Code typed straight into the tag
|
|
130
|
+
|
|
131
|
+
Code written bare inside the tag is read too, from the page as you typed it —
|
|
132
|
+
indentation kept, nothing taken for Markdown:
|
|
133
|
+
|
|
134
|
+
```mdx
|
|
135
|
+
<Snippet title="who_are_you.py">name = input("What's your name? ")</Snippet>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Two things it cannot carry, both of which happen before any component sees the
|
|
139
|
+
code. A page is MDX, so `{`, `}` and `<` are read as MDX and stop the build on
|
|
140
|
+
a syntax error. And a formatter run over your pages reflows what it takes for
|
|
141
|
+
prose: two lines of shell come back as one. Where either applies — which is
|
|
142
|
+
most real code — use the fenced form above.
|
|
143
|
+
|
|
144
|
+
## The mark of the language
|
|
145
|
+
|
|
146
|
+
A block carries the colour of its language — a rule above it, and a short mark
|
|
147
|
+
beside the file name. The colour is set as a variable on the frame, so the
|
|
148
|
+
theme decides what it paints and a project can set another.
|
|
149
|
+
|
|
150
|
+
The same function, in the other file this page reads. Nothing was said about
|
|
151
|
+
the language: the extension carried it, and with it the mark and the colour.
|
|
152
|
+
|
|
153
|
+
<Snippet source="./snippet-example.py" region="guard" title="The same guard, in Python" />
|
|
154
|
+
|
|
155
|
+
```mdx
|
|
156
|
+
<Snippet source="./snippet-example.py" region="guard" title="The same guard, in Python" />
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
A block the page writes says its language in the fence, and is marked the same
|
|
160
|
+
way:
|
|
161
|
+
|
|
162
|
+
<Snippet title="deploy.yml">
|
|
163
|
+
|
|
164
|
+
```yaml
|
|
165
|
+
steps:
|
|
166
|
+
- run: npx docpensieve build
|
|
167
|
+
- run: npx docpensieve check
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
</Snippet>
|
|
171
|
+
|
|
172
|
+
<Snippet title="A first build" lang="bash">
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
npx docpensieve init my-site
|
|
176
|
+
npx docpensieve dev
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
</Snippet>
|
|
180
|
+
|
|
181
|
+
Where a project installs an icon collection and names it, the drawing replaces
|
|
182
|
+
the mark:
|
|
183
|
+
|
|
184
|
+
```js
|
|
185
|
+
snippetIcons: 'simple-icons',
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
It is never a reason to fail. The icon is the tool's idea, not something your
|
|
189
|
+
page asked for: a collection you do not have, or a language it does not draw,
|
|
190
|
+
brings back the mark. `icon="simple-icons:docker"` on a single block wins over
|
|
191
|
+
both.
|
|
192
|
+
|
|
193
|
+
### A language nobody marked
|
|
194
|
+
|
|
195
|
+
Twenty-one languages have a mark and a colour. Any other is shown without
|
|
196
|
+
one — the frame, the name, and the code.
|
|
197
|
+
|
|
198
|
+
Highlighting is a separate matter, and a wider one: the highlighter knows
|
|
199
|
+
several hundred languages, and a block in one it does not know comes out as
|
|
200
|
+
plain text rather than stopping the build. You can write a snippet in anything
|
|
201
|
+
your project is written in.
|
|
202
|
+
|
|
203
|
+
## What it refuses
|
|
204
|
+
|
|
205
|
+
Nothing fails quietly, because a snippet that resolved to nothing would leave
|
|
206
|
+
an empty frame — and an empty frame looks deliberate.
|
|
207
|
+
|
|
208
|
+
| Written | What happens |
|
|
209
|
+
| ----------------------------------- | --------------------------------------------------------------- |
|
|
210
|
+
| a file that is not there | The build stops, saying which path was looked for. |
|
|
211
|
+
| a path leading outside the project | Refused: a page is content, and content does not read the disk. |
|
|
212
|
+
| a region no marker opens, or closes | The build stops, naming the region and the file. |
|
|
213
|
+
| a range that reads backwards | The build stops. |
|
|
214
|
+
|
|
215
|
+
## Properties
|
|
216
|
+
|
|
217
|
+
| Property | Type | Effect |
|
|
218
|
+
| ----------- | --------- | --------------------------------------------------------------------------- |
|
|
219
|
+
| `source` | `string` | File to read. From the page with `./` or `../`, from the project otherwise. |
|
|
220
|
+
| `title` | `string` | Label above the block. Defaults to the name of the file. |
|
|
221
|
+
| `lines` | `string` | `8`, `8-14`, `8-` or `-14`. |
|
|
222
|
+
| `region` | `string` | Name marked by `#region` and `#endregion` in the file. |
|
|
223
|
+
| `lang` | `string` | Language for the highlighting, when the extension says the wrong thing. |
|
|
224
|
+
| `collapsed` | `boolean` | Folds the block into a `details`. |
|
|
225
|
+
| `icon` | `string` | An icon for this block, instead of the mark of its language. |
|
|
226
|
+
| `className` | `string` | Classes added to the frame. |
|
|
227
|
+
| `style` | `object` | Inline styles on the frame. |
|
|
228
|
+
|
|
229
|
+
The file this page reads is installed along with it, so every example above
|
|
230
|
+
works in your project exactly as it does here.
|