docpensieve 0.1.0 → 0.1.1
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/README.md +5 -2
- package/bin/docpensieve.js +1 -0
- package/package.json +4 -2
- package/src/commands/dev.js +1 -1
- package/src/commands/init.js +214 -39
- package/starter/01-guide/01-installation.md +79 -0
- package/starter/01-guide/02-first-site.md +97 -0
- package/starter/01-guide/03-writing-pages.md +138 -0
- package/starter/01-guide/04-versions.md +179 -0
- package/starter/01-guide/05-themes.md +116 -0
- package/starter/01-guide/06-deployment.md +124 -0
- package/starter/01-guide/index.md +38 -0
- package/starter/02-components/01-card.mdx +195 -0
- package/starter/02-components/02-columns.mdx +193 -0
- package/starter/02-components/03-time-timer.mdx +120 -0
- package/starter/02-components/04-tooltip.mdx +100 -0
- package/starter/02-components/05-tree.mdx +163 -0
- package/starter/02-components/06-scroll-to-top.mdx +103 -0
- package/starter/02-components/07-skill.mdx +214 -0
- package/starter/02-components/08-logo-icon.mdx +122 -0
- package/starter/02-components/icons/banner.svg +15 -0
- package/starter/02-components/icons/book.svg +4 -0
- package/starter/02-components/icons/lightning.svg +3 -0
- package/starter/02-components/icons/shield.svg +4 -0
- package/starter/02-components/icons/star.svg +3 -0
- package/starter/02-components/index.md +41 -0
- package/starter/03-reference/01-cli.md +134 -0
- package/starter/03-reference/02-configuration.md +132 -0
- package/starter/03-reference/03-frontmatter.md +110 -0
- package/starter/03-reference/04-theme.md +149 -0
- package/starter/03-reference/index.md +32 -0
- package/starter/04-architecture.md +106 -0
- package/starter/icons/blocks.svg +6 -0
- package/starter/icons/book.svg +4 -0
- package/starter/icons/branch.svg +6 -0
- package/starter/icons/compass.svg +4 -0
- package/starter/icons/lightning.svg +3 -0
- package/starter/icons/list.svg +4 -0
- package/starter/icons/shield.svg +4 -0
- package/starter/icons/star.svg +3 -0
- package/starter/index.mdx +244 -0
- package/types/commands/init.d.ts +7 -4
package/README.md
CHANGED
|
@@ -22,8 +22,11 @@ npx docpensieve check # reads the produced site back: links, markup
|
|
|
22
22
|
npx docpensieve serve # serves the output folder
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
`init` asks for the project name, its URL and the CSS framework
|
|
26
|
-
|
|
25
|
+
`init` asks for the project name, its URL and the CSS framework, and installs
|
|
26
|
+
DocPensieve's documentation in a section of the new site's menu, matching the
|
|
27
|
+
installed version — `--minimal` leaves it out. The configuration file it writes
|
|
28
|
+
lists every option, each with a comment. In a script or in CI,
|
|
29
|
+
`--yes --theme tailwind` skips the dialogue.
|
|
27
30
|
|
|
28
31
|
The development server reloads the browser after every rebuild, through a
|
|
29
32
|
script injected **at serving time**: the output of `build` stays free of
|
package/bin/docpensieve.js
CHANGED
|
@@ -33,6 +33,7 @@ program
|
|
|
33
33
|
.option('--version-name <version>', 'first version, e.g. 1.0')
|
|
34
34
|
.option('-y, --yes', 'accept the defaults without a dialogue')
|
|
35
35
|
.option('-f, --force', 'overwrite an existing configuration')
|
|
36
|
+
.option('--minimal', "leave DocPensieve's documentation out of the new site")
|
|
36
37
|
.action(async (dir, options) => {
|
|
37
38
|
await init(dir, { ...options, version: options.versionName });
|
|
38
39
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docpensieve",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "DocPensieve command-line interface (init, build, check, dev, serve)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"files": [
|
|
21
21
|
"bin",
|
|
22
22
|
"src",
|
|
23
|
+
"starter",
|
|
23
24
|
"types"
|
|
24
25
|
],
|
|
25
26
|
"dependencies": {
|
|
@@ -54,6 +55,7 @@
|
|
|
54
55
|
},
|
|
55
56
|
"types": "./types/index.d.ts",
|
|
56
57
|
"scripts": {
|
|
57
|
-
"prepack": "tsc -b tsconfig.build.json"
|
|
58
|
+
"prepack": "tsc -b tsconfig.build.json && node scripts/starter.mjs",
|
|
59
|
+
"postpack": "node scripts/starter.mjs --clean"
|
|
58
60
|
}
|
|
59
61
|
}
|
package/src/commands/dev.js
CHANGED
|
@@ -80,7 +80,7 @@ export async function dev(options = {}) {
|
|
|
80
80
|
console.log(`served at ${url}`);
|
|
81
81
|
|
|
82
82
|
const watched = [
|
|
83
|
-
path.resolve(cwd, CONFIG_FILENAME),
|
|
83
|
+
config.configFile ?? path.resolve(cwd, CONFIG_FILENAME),
|
|
84
84
|
...config.versions.map((version) => path.resolve(cwd, version.folder)),
|
|
85
85
|
];
|
|
86
86
|
const watcher = chokidar.watch(watched, { ignoreInitial: true });
|
package/src/commands/init.js
CHANGED
|
@@ -4,12 +4,19 @@
|
|
|
4
4
|
* @module docpensieve/commands/init
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { existsSync } from 'node:fs';
|
|
7
|
+
import { cpSync, existsSync, readdirSync, rmSync } from 'node:fs';
|
|
8
8
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
9
|
+
import { createRequire } from 'node:module';
|
|
9
10
|
import path from 'node:path';
|
|
10
11
|
import { createInterface } from 'node:readline/promises';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
11
13
|
|
|
12
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
CONFIG_FILENAME,
|
|
16
|
+
CONFIG_FILENAMES,
|
|
17
|
+
DocPensieveError,
|
|
18
|
+
THEME_FRAMEWORKS,
|
|
19
|
+
} from '@docpensieve/shared';
|
|
13
20
|
|
|
14
21
|
/**
|
|
15
22
|
* Description shown next to each framework.
|
|
@@ -23,6 +30,23 @@ const FRAMEWORK_LABELS = {
|
|
|
23
30
|
/** Answers used when there is no dialogue. */
|
|
24
31
|
const DEFAULTS = { name: 'My documentation', siteUrl: '', theme: 'tailwind', version: '1.0' };
|
|
25
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Folder of the installed DocPensieve documentation, inside the version folder.
|
|
35
|
+
*
|
|
36
|
+
* The prefix puts it last in the menu, after the project's own pages, and
|
|
37
|
+
* vanishes from the URL: the section is served under `/docpensieve/`.
|
|
38
|
+
*/
|
|
39
|
+
const DOCS_FOLDER = '99-docpensieve';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Entries of DocPensieve's documentation that are not installed: its home page
|
|
43
|
+
* and the icons only that page uses belong to DocPensieve's own site.
|
|
44
|
+
*/
|
|
45
|
+
const NOT_INSTALLED = new Set(['index.md', 'index.mdx', 'icons']);
|
|
46
|
+
|
|
47
|
+
/** Where the generated configuration sends readers for every field. */
|
|
48
|
+
const DOCUMENTATION_URL = 'https://juniors017.github.io/docpensieve/';
|
|
49
|
+
|
|
26
50
|
/**
|
|
27
51
|
* Asks a question, with a default value shown between brackets.
|
|
28
52
|
*
|
|
@@ -66,11 +90,33 @@ async function askFramework(rl) {
|
|
|
66
90
|
}
|
|
67
91
|
}
|
|
68
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Asks whether to install DocPensieve's documentation in the new site.
|
|
95
|
+
*
|
|
96
|
+
* @param {import('node:readline/promises').Interface} rl
|
|
97
|
+
* @returns {Promise<boolean>}
|
|
98
|
+
*/
|
|
99
|
+
async function askDocumentation(rl) {
|
|
100
|
+
for (;;) {
|
|
101
|
+
const answer = (await rl.question("Install DocPensieve's documentation in the site? [Y/n]: "))
|
|
102
|
+
.trim()
|
|
103
|
+
.toLowerCase();
|
|
104
|
+
if (answer === '' || answer === 'y' || answer === 'yes') return true;
|
|
105
|
+
if (answer === 'n' || answer === 'no') return false;
|
|
106
|
+
console.log('Answer not understood. Expected: y or n.');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
69
110
|
/**
|
|
70
111
|
* Gathers the answers, through a dialogue or from the options.
|
|
71
112
|
*
|
|
72
|
-
* @param {{
|
|
73
|
-
*
|
|
113
|
+
* @param {{
|
|
114
|
+
* name?: string, theme?: string, siteUrl?: string, version?: string,
|
|
115
|
+
* yes?: boolean, minimal?: boolean,
|
|
116
|
+
* }} options
|
|
117
|
+
* @returns {Promise<{
|
|
118
|
+
* name: string, theme: string, siteUrl: string, version: string, docs: boolean,
|
|
119
|
+
* }>}
|
|
74
120
|
*/
|
|
75
121
|
async function collect(options) {
|
|
76
122
|
const fromOptions = {
|
|
@@ -78,6 +124,7 @@ async function collect(options) {
|
|
|
78
124
|
siteUrl: options.siteUrl ?? DEFAULTS.siteUrl,
|
|
79
125
|
theme: options.theme ?? DEFAULTS.theme,
|
|
80
126
|
version: options.version ?? DEFAULTS.version,
|
|
127
|
+
docs: !options.minimal,
|
|
81
128
|
};
|
|
82
129
|
|
|
83
130
|
// Without a terminal — script, CI, pipe — the dialogue would never complete:
|
|
@@ -90,7 +137,8 @@ async function collect(options) {
|
|
|
90
137
|
const siteUrl = await ask(rl, 'Public URL of the site (optional)', fromOptions.siteUrl);
|
|
91
138
|
const version = await ask(rl, 'First version', fromOptions.version);
|
|
92
139
|
const theme = options.theme ?? (await askFramework(rl));
|
|
93
|
-
|
|
140
|
+
const docs = options.minimal ? false : await askDocumentation(rl);
|
|
141
|
+
return { name, siteUrl, version, theme, docs };
|
|
94
142
|
} finally {
|
|
95
143
|
rl.close();
|
|
96
144
|
}
|
|
@@ -108,8 +156,14 @@ async function collect(options) {
|
|
|
108
156
|
const quote = (value) => `'${String(value).replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
|
|
109
157
|
|
|
110
158
|
/**
|
|
159
|
+
* Renders `docpensieve.config.mjs`.
|
|
160
|
+
*
|
|
161
|
+
* Every field the configuration accepts appears in it — set to its default,
|
|
162
|
+
* or commented out with an example — so that the first file a user opens also
|
|
163
|
+
* tells them everything they can change.
|
|
164
|
+
*
|
|
111
165
|
* @param {{ name: string, theme: string, siteUrl: string, version: string }} answers
|
|
112
|
-
* @returns {string} Contents of `docpensieve.config.
|
|
166
|
+
* @returns {string} Contents of `docpensieve.config.mjs`.
|
|
113
167
|
*/
|
|
114
168
|
function renderConfig({ name, theme, siteUrl, version }) {
|
|
115
169
|
const slug = versionSlug(version);
|
|
@@ -121,45 +175,74 @@ function renderConfig({ name, theme, siteUrl, version }) {
|
|
|
121
175
|
const lines = [
|
|
122
176
|
"/** @type {import('@docpensieve/core').DocPensieveConfig} */",
|
|
123
177
|
'export default {',
|
|
178
|
+
' // Name shown in the header, in the page titles and in the structured data.',
|
|
124
179
|
` projectName: ${quote(name)},`,
|
|
125
|
-
];
|
|
126
|
-
|
|
127
|
-
if (siteUrl) {
|
|
128
|
-
lines.push(
|
|
129
|
-
` siteUrl: ${quote(siteUrl)},`,
|
|
130
|
-
' // baseUrl is derived from the path of siteUrl. Set it to force it.',
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
lines.push(
|
|
135
180
|
'',
|
|
136
|
-
' //
|
|
137
|
-
' //
|
|
181
|
+
' // Public address of the site. It feeds the canonical links and the',
|
|
182
|
+
' // structured data, and its path gives the deployment prefix.',
|
|
183
|
+
siteUrl ? ` siteUrl: ${quote(siteUrl)},` : " // siteUrl: 'https://example.com/my-project',",
|
|
184
|
+
" // baseUrl: '/my-project/', // only to depart from the path of siteUrl",
|
|
185
|
+
'',
|
|
186
|
+
' // Language of the pages, in <html lang>. The labels of the page shell',
|
|
187
|
+
' // stay in English.',
|
|
188
|
+
" lang: 'en',",
|
|
189
|
+
'',
|
|
190
|
+
' // One entry per version, each one a folder of Markdown and MDX pages.',
|
|
191
|
+
' // current the version the site root leads to — at most one',
|
|
192
|
+
' // prerelease in preparation: a banner on every page, kept out of search',
|
|
193
|
+
' // archived no longer maintained: a banner, still indexed',
|
|
138
194
|
' versions: [',
|
|
139
|
-
|
|
140
|
-
` slug: ${quote(slug)}
|
|
141
|
-
` name: ${quote(version)}
|
|
195
|
+
' {',
|
|
196
|
+
` slug: ${quote(slug)}, // URL segment, and name of the version's branch`,
|
|
197
|
+
` name: ${quote(version)}, // label in the version switcher`,
|
|
142
198
|
` folder: ${quote(`docs/${slug}`)},`,
|
|
143
|
-
|
|
144
|
-
|
|
199
|
+
' current: true,',
|
|
200
|
+
' },',
|
|
145
201
|
' ],',
|
|
146
202
|
'',
|
|
203
|
+
' // Output folder of "docpensieve build".',
|
|
147
204
|
" outDir: 'dist',",
|
|
148
205
|
'',
|
|
149
206
|
' theme: {',
|
|
207
|
+
" // 'tailwind' compiles the utilities your pages use; 'custom' is a plain",
|
|
208
|
+
' // stylesheet with no dependency.',
|
|
150
209
|
` framework: ${quote(theme)},`,
|
|
151
210
|
" darkMode: 'class',",
|
|
152
|
-
|
|
211
|
+
'',
|
|
212
|
+
' // Design tokens to override, for instance the accent colour:',
|
|
213
|
+
" // tokens: { '--dp-accent': '#008060', '--dp-radius': '0.75rem' },",
|
|
214
|
+
'',
|
|
215
|
+
' // CSS appended to the stylesheet, outside any layer: it wins over the',
|
|
216
|
+
' // default rules.',
|
|
217
|
+
" // css: '.dp-article h2 { letter-spacing: -0.01em; }',",
|
|
218
|
+
...(theme === 'tailwind'
|
|
219
|
+
? [
|
|
220
|
+
'',
|
|
221
|
+
' // Entry stylesheet handed to Tailwind, to add a @theme block for',
|
|
222
|
+
' // instance.',
|
|
223
|
+
` // source: '@import "tailwindcss";',`,
|
|
224
|
+
]
|
|
225
|
+
: []),
|
|
153
226
|
' },',
|
|
154
227
|
'',
|
|
155
|
-
" // 'auto': the
|
|
228
|
+
" // 'auto': the menu follows the folders and the 01-, 02- prefixes.",
|
|
156
229
|
" sidebar: 'auto',",
|
|
157
230
|
'',
|
|
231
|
+
' // The shipped components — Card, Columns, Tooltip… — usable in any .mdx',
|
|
232
|
+
' // page without an import. false removes them, to use your own names.',
|
|
158
233
|
' globalComponents: true,',
|
|
234
|
+
'',
|
|
235
|
+
' // Back-to-top button on every page.',
|
|
236
|
+
' scrollToTop: true,',
|
|
237
|
+
'',
|
|
238
|
+
' // Structured data (JSON-LD) generated from the frontmatter of each page.',
|
|
159
239
|
' jsonld: { enabled: true },',
|
|
160
240
|
'};',
|
|
161
241
|
'',
|
|
162
|
-
|
|
242
|
+
'// Every field is described in the reference of the DocPensieve documentation:',
|
|
243
|
+
`// the DocPensieve section of your site, or ${DOCUMENTATION_URL}`,
|
|
244
|
+
'',
|
|
245
|
+
];
|
|
163
246
|
|
|
164
247
|
return lines.join('\n');
|
|
165
248
|
}
|
|
@@ -180,9 +263,10 @@ function versionSlug(version) {
|
|
|
180
263
|
|
|
181
264
|
/**
|
|
182
265
|
* @param {string} name Project name, for the title of the home page.
|
|
266
|
+
* @param {boolean} docs Whether DocPensieve's documentation is installed.
|
|
183
267
|
* @returns {string}
|
|
184
268
|
*/
|
|
185
|
-
const renderIndex = (name) => `---
|
|
269
|
+
const renderIndex = (name, docs) => `---
|
|
186
270
|
title: Introduction
|
|
187
271
|
description: Documentation of ${name}.
|
|
188
272
|
date: ${new Date().toISOString().slice(0, 10)}
|
|
@@ -202,7 +286,17 @@ Pages live in \`docs/\`. The \`01-\` prefix of a file orders the menu
|
|
|
202
286
|
without appearing in the URL.
|
|
203
287
|
|
|
204
288
|
See the [installation guide](/guide/installation/).
|
|
205
|
-
|
|
289
|
+
${
|
|
290
|
+
docs
|
|
291
|
+
? `
|
|
292
|
+
## Learning DocPensieve
|
|
293
|
+
|
|
294
|
+
The [DocPensieve](/docpensieve/) section of the menu is the documentation of the
|
|
295
|
+
tool that builds this site, installed along with it. Delete its folder,
|
|
296
|
+
\`${DOCS_FOLDER}\`, when you no longer need it.
|
|
297
|
+
`
|
|
298
|
+
: ''
|
|
299
|
+
}`;
|
|
206
300
|
|
|
207
301
|
/** @returns {string} Sample page, showing ordering and highlighting. */
|
|
208
302
|
const renderGuide = () => `---
|
|
@@ -227,23 +321,85 @@ npm run dev
|
|
|
227
321
|
\`\`\`
|
|
228
322
|
`;
|
|
229
323
|
|
|
324
|
+
/**
|
|
325
|
+
* @param {string} slug Version slug, to name the folder to delete.
|
|
326
|
+
* @returns {string} Entry page of the installed documentation section.
|
|
327
|
+
*/
|
|
328
|
+
const renderDocsIndex = (slug) => `---
|
|
329
|
+
title: DocPensieve
|
|
330
|
+
description: Documentation of the tool this site is built with, installed along with it.
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
# DocPensieve
|
|
334
|
+
|
|
335
|
+
This section is the documentation of DocPensieve, the tool this site is built
|
|
336
|
+
with. \`docpensieve init\` installed it, and it matches the version you use.
|
|
337
|
+
|
|
338
|
+
- [Guide](./guide/) — from installation to deployment, in order.
|
|
339
|
+
- [Components](./components/) — the components usable in any page.
|
|
340
|
+
- [Reference](./reference/) — commands, configuration, frontmatter and theme.
|
|
341
|
+
- [Architecture](./architecture/) — how a page becomes HTML.
|
|
342
|
+
|
|
343
|
+
When you no longer need it, delete the \`docs/${slug}/${DOCS_FOLDER}\` folder:
|
|
344
|
+
nothing else depends on it.
|
|
345
|
+
`;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Folder holding DocPensieve's documentation, ready to be installed.
|
|
349
|
+
*
|
|
350
|
+
* The published package carries it in `starter/`, copied at packing time from
|
|
351
|
+
* the documentation of its own version. In this repository, outside packing,
|
|
352
|
+
* the same pages are read straight from `docs/`.
|
|
353
|
+
*
|
|
354
|
+
* @returns {string | null} The folder, or `null` when neither exists.
|
|
355
|
+
*/
|
|
356
|
+
function documentationSource() {
|
|
357
|
+
const packed = fileURLToPath(new URL('../../starter/', import.meta.url));
|
|
358
|
+
if (existsSync(packed)) return packed;
|
|
359
|
+
|
|
360
|
+
const { version } = createRequire(import.meta.url)('../../package.json');
|
|
361
|
+
const [major, minor] = String(version).split('.');
|
|
362
|
+
const repository = fileURLToPath(
|
|
363
|
+
new URL(`../../../../docs/v${major}.${minor}/`, import.meta.url),
|
|
364
|
+
);
|
|
365
|
+
return existsSync(repository) ? repository : null;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Copies DocPensieve's documentation into the new project, in its own folder.
|
|
370
|
+
*
|
|
371
|
+
* @param {string} source Folder of the documentation to install.
|
|
372
|
+
* @param {string} target Folder of the section, in the version folder.
|
|
373
|
+
* @param {string} slug Version slug.
|
|
374
|
+
*/
|
|
375
|
+
async function installDocumentation(source, target, slug) {
|
|
376
|
+
await mkdir(target, { recursive: true });
|
|
377
|
+
for (const entry of readdirSync(source)) {
|
|
378
|
+
if (NOT_INSTALLED.has(entry)) continue;
|
|
379
|
+
cpSync(path.join(source, entry), path.join(target, entry), { recursive: true });
|
|
380
|
+
}
|
|
381
|
+
await writeFile(path.join(target, 'index.md'), renderDocsIndex(slug), 'utf8');
|
|
382
|
+
}
|
|
383
|
+
|
|
230
384
|
/**
|
|
231
385
|
* Sets up a documentation project.
|
|
232
386
|
*
|
|
233
387
|
* @param {string} [dir] Target folder, created if needed.
|
|
234
388
|
* @param {{
|
|
235
389
|
* name?: string, theme?: string, siteUrl?: string, version?: string,
|
|
236
|
-
* yes?: boolean, force?: boolean,
|
|
237
|
-
* }} [options]
|
|
238
|
-
* @returns {Promise<{ dir: string, theme: string }>}
|
|
239
|
-
* @throws {DocPensieveError} Unknown framework,
|
|
390
|
+
* yes?: boolean, force?: boolean, minimal?: boolean,
|
|
391
|
+
* }} [options] `minimal` leaves DocPensieve's documentation out of the site.
|
|
392
|
+
* @returns {Promise<{ dir: string, theme: string, docs: boolean }>}
|
|
393
|
+
* @throws {DocPensieveError} Unknown framework, project already initialised,
|
|
394
|
+
* or documentation to install missing.
|
|
240
395
|
*/
|
|
241
396
|
export async function init(dir = '.', options = {}) {
|
|
242
397
|
const target = path.resolve(dir);
|
|
243
398
|
const configPath = path.join(target, CONFIG_FILENAME);
|
|
399
|
+
const existing = CONFIG_FILENAMES.filter((name) => existsSync(path.join(target, name)));
|
|
244
400
|
|
|
245
|
-
if (
|
|
246
|
-
throw new DocPensieveError(`${
|
|
401
|
+
if (existing.length > 0 && !options.force) {
|
|
402
|
+
throw new DocPensieveError(`${existing[0]} already exists in ${target}.`, {
|
|
247
403
|
hint: 'Use --force to overwrite it, or pick another folder.',
|
|
248
404
|
});
|
|
249
405
|
}
|
|
@@ -259,23 +415,42 @@ export async function init(dir = '.', options = {}) {
|
|
|
259
415
|
|
|
260
416
|
const answers = await collect(options);
|
|
261
417
|
|
|
418
|
+
// Located before anything is written: a project left half set up would be
|
|
419
|
+
// worse than a clear error.
|
|
420
|
+
const documentation = answers.docs ? documentationSource() : null;
|
|
421
|
+
if (answers.docs && !documentation) {
|
|
422
|
+
throw new DocPensieveError('The DocPensieve documentation to install cannot be found.', {
|
|
423
|
+
hint: 'Reinstall docpensieve, or run init with --minimal to go without it.',
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
262
427
|
const slug = versionSlug(answers.version);
|
|
263
428
|
const docsDir = path.join(target, 'docs', slug);
|
|
264
429
|
|
|
265
|
-
|
|
430
|
+
// Overwritten, a project keeps a single configuration file: the one written
|
|
431
|
+
// here. An older spelling left behind would make the next build refuse both.
|
|
432
|
+
for (const name of existing) rmSync(path.join(target, name), { force: true });
|
|
433
|
+
|
|
434
|
+
await mkdir(path.join(docsDir, '01-guide'), { recursive: true });
|
|
266
435
|
await writeFile(configPath, renderConfig(answers), 'utf8');
|
|
267
|
-
await writeFile(path.join(docsDir, 'index.md'), renderIndex(answers.name), 'utf8');
|
|
268
|
-
await writeFile(path.join(docsDir, 'guide', '01-installation.md'), renderGuide(), 'utf8');
|
|
436
|
+
await writeFile(path.join(docsDir, 'index.md'), renderIndex(answers.name, answers.docs), 'utf8');
|
|
437
|
+
await writeFile(path.join(docsDir, '01-guide', '01-installation.md'), renderGuide(), 'utf8');
|
|
438
|
+
if (documentation) {
|
|
439
|
+
await installDocumentation(documentation, path.join(docsDir, DOCS_FOLDER), slug);
|
|
440
|
+
}
|
|
269
441
|
await ignoreOutput(target);
|
|
270
442
|
|
|
271
443
|
console.log(`\nProject initialised in ${target}`);
|
|
272
444
|
console.log(` ${CONFIG_FILENAME}`);
|
|
273
445
|
console.log(` docs/${slug}/index.md`);
|
|
274
|
-
console.log(` docs/${slug}/guide/01-installation.md`);
|
|
446
|
+
console.log(` docs/${slug}/01-guide/01-installation.md`);
|
|
447
|
+
if (documentation) {
|
|
448
|
+
console.log(` docs/${slug}/${DOCS_FOLDER}/ DocPensieve's documentation, to delete when done`);
|
|
449
|
+
}
|
|
275
450
|
console.log(`\nTheme: ${answers.theme} — ${FRAMEWORK_LABELS[answers.theme]}`);
|
|
276
451
|
console.log('\nNext: npx docpensieve dev');
|
|
277
452
|
|
|
278
|
-
return { dir: target, theme: answers.theme };
|
|
453
|
+
return { dir: target, theme: answers.theme, docs: answers.docs };
|
|
279
454
|
}
|
|
280
455
|
|
|
281
456
|
/**
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Installation
|
|
3
|
+
description: What you need, and how to set up a documentation project.
|
|
4
|
+
date: 2026-09-09
|
|
5
|
+
tags: [guide, installation]
|
|
6
|
+
|
|
7
|
+
jsonld:
|
|
8
|
+
type: TechArticle
|
|
9
|
+
breadcrumbs: true
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Installation
|
|
13
|
+
|
|
14
|
+
## What you need
|
|
15
|
+
|
|
16
|
+
**Node.js 22 or later.** It is the only requirement. The generator runs at
|
|
17
|
+
build time, not on the reader's side: nothing else to install on the server
|
|
18
|
+
that will host the site, which only has to serve files.
|
|
19
|
+
|
|
20
|
+
## Setting up a project
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx docpensieve init my-site
|
|
24
|
+
cd my-site
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The command creates the folder and writes a configuration, a first
|
|
28
|
+
documentation folder and a home page into it. It asks a few questions; `--yes`
|
|
29
|
+
skips them and accepts the defaults.
|
|
30
|
+
|
|
31
|
+
It also installs this very documentation, in a **DocPensieve** section at the
|
|
32
|
+
end of the new site's menu. It matches the version you installed, and its
|
|
33
|
+
folder, `99-docpensieve`, can be deleted as soon as you no longer need it;
|
|
34
|
+
`--minimal` leaves it out. The configuration file lists every option, each with
|
|
35
|
+
a comment, set to its default or given as an example.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx docpensieve init my-site --yes --name "My documentation"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
| Option | Effect |
|
|
42
|
+
| -------------------------- | ------------------------------------------------------- |
|
|
43
|
+
| `-n, --name <name>` | Project name, shown in the header |
|
|
44
|
+
| `-t, --theme <framework>` | `tailwind` or `custom` |
|
|
45
|
+
| `-u, --site-url <url>` | Public URL, from which the deployment prefix is derived |
|
|
46
|
+
| `--version-name <version>` | First version, `1.0` for instance |
|
|
47
|
+
| `-y, --yes` | Accepts the defaults without a dialogue |
|
|
48
|
+
| `-f, --force` | Overwrites an existing configuration |
|
|
49
|
+
| `--minimal` | Leaves DocPensieve's documentation out of the site |
|
|
50
|
+
|
|
51
|
+
## In an existing project
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install docpensieve
|
|
55
|
+
npx docpensieve init . --force
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`init` on an occupied folder refuses to overwrite an existing configuration:
|
|
59
|
+
you have to ask for it with `--force`. The refusal is deliberate — a
|
|
60
|
+
configuration overwritten by mistake is only noticed at the next deployment.
|
|
61
|
+
|
|
62
|
+
## Choosing the theme
|
|
63
|
+
|
|
64
|
+
`tailwind` is the default and installs Tailwind as a dependency. `custom` does
|
|
65
|
+
without it entirely: the site is then styled by a stylesheet written in the
|
|
66
|
+
package, with no styling dependency.
|
|
67
|
+
|
|
68
|
+
Both are equivalent in use — the templates are the same, only the styling
|
|
69
|
+
changes. The choice is not final: it fits in one field of the configuration,
|
|
70
|
+
described in [Themes](./themes/).
|
|
71
|
+
|
|
72
|
+
## Checking
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx docpensieve build
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
If the output folder appears with an `index.html` inside, everything is in
|
|
79
|
+
place. Next: [First site](./first-site/).
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: First site
|
|
3
|
+
description: Build, serve, and understand what was produced.
|
|
4
|
+
tags: [guide]
|
|
5
|
+
|
|
6
|
+
jsonld:
|
|
7
|
+
type: TechArticle
|
|
8
|
+
breadcrumbs: true
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# First site
|
|
12
|
+
|
|
13
|
+
## Building
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx docpensieve build
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Every declared version is built. To build just one, pass its slug:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx docpensieve build v1.0
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Looking at the result
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx docpensieve serve
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The output folder is served statically on port 4000. It is exactly what a host
|
|
32
|
+
will do: no difference between this server and going live.
|
|
33
|
+
|
|
34
|
+
## Working
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx docpensieve dev
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The development server watches the sources and rebuilds on every save. It
|
|
41
|
+
listens on port 3000; `--port` changes that.
|
|
42
|
+
|
|
43
|
+
The difference with `serve` is this: `dev` rebuilds, `serve` only serves. To
|
|
44
|
+
check what will really be published, chain `build` then `serve`.
|
|
45
|
+
|
|
46
|
+
## What was produced
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
dist/
|
|
50
|
+
├── index.html redirect to the current version
|
|
51
|
+
├── versions.json the versions and their URLs
|
|
52
|
+
└── versions/
|
|
53
|
+
└── v1.0/
|
|
54
|
+
├── index.html the home page of the version
|
|
55
|
+
├── assets/
|
|
56
|
+
│ └── docpensieve.css
|
|
57
|
+
└── guide/
|
|
58
|
+
└── installation/
|
|
59
|
+
└── index.html
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Three things are worth noticing.
|
|
63
|
+
|
|
64
|
+
**Each page is a folder with an `index.html`.** That is what gives URLs without
|
|
65
|
+
an extension — `/guide/installation/` rather than `/guide/installation.html` —
|
|
66
|
+
without asking anything of the host.
|
|
67
|
+
|
|
68
|
+
**A single stylesheet for the whole version.** It is compiled last, once the
|
|
69
|
+
pages are written, because a utility theme needs to know which classes were
|
|
70
|
+
actually used in order to emit only those.
|
|
71
|
+
|
|
72
|
+
**The `versions.json` at the root** describes the available versions. It is
|
|
73
|
+
what lets versions built long ago stay online without ever rebuilding them.
|
|
74
|
+
|
|
75
|
+
## Reading the output back
|
|
76
|
+
|
|
77
|
+
A successful build says nothing of a dead link: nothing in the chain looks at
|
|
78
|
+
them. A command takes care of it:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npx docpensieve build
|
|
82
|
+
npx docpensieve check
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
It walks the produced pages, removes the deployment prefix from every internal
|
|
86
|
+
target and checks that the file exists. It also reports the targets that
|
|
87
|
+
**ignore** that prefix — the file is there, but the link will lead nowhere once
|
|
88
|
+
online.
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
index.html
|
|
92
|
+
/guide/installation/
|
|
93
|
+
→ ignores the deployment prefix "/my-project/"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
It is the check to run after any change of `baseUrl`, of folder structure or of
|
|
97
|
+
URL.
|