@writedocs/generator 0.1.0 → 0.2.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.
- package/README.md +28 -17
- package/bin/writedocs.js +126 -73
- package/package.json +80 -79
- package/src/lib/config-schema.ts +997 -0
- package/src/lib/config.ts +1290 -2131
package/README.md
CHANGED
|
@@ -1,17 +1,28 @@
|
|
|
1
|
-
# Writedocs
|
|
2
|
-
|
|
3
|
-
A static site generator for documentation: a `writedocs.json` config + a folder of MDX/Markdown in, a fully static site out. Built on [Astro](https://astro.build), in the spirit of Docusaurus/Mintlify.
|
|
4
|
-
|
|
5
|
-
## Quickstart
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install -g @writedocs/generator
|
|
9
|
-
|
|
10
|
-
cd my-docs # any folder containing writedocs.json + docs/
|
|
11
|
-
writedocs dev
|
|
12
|
-
writedocs
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
# Writedocs
|
|
2
|
+
|
|
3
|
+
A static site generator for documentation: a `writedocs.json` config + a folder of MDX/Markdown in, a fully static site out. Built on [Astro](https://astro.build), in the spirit of Docusaurus/Mintlify.
|
|
4
|
+
|
|
5
|
+
## Quickstart
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @writedocs/generator
|
|
9
|
+
|
|
10
|
+
cd my-docs # any folder containing writedocs.json + docs/
|
|
11
|
+
writedocs dev
|
|
12
|
+
writedocs validate
|
|
13
|
+
writedocs build
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Commands
|
|
17
|
+
|
|
18
|
+
| Command | What it does |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `writedocs init [dir]` | Scaffolds a `writedocs.json` and a starter `docs/` folder. |
|
|
21
|
+
| `writedocs dev [dir]` | Starts a local dev server with hot reload. |
|
|
22
|
+
| `writedocs validate [dir]` | Checks `<dir>/writedocs.json` against the schema. Exits `0` when it is valid and `1` when it is not, so it can gate a CI job; prints one line per problem, in the same words the build uses. Runs entirely locally — no key, no network. Configuration only: it does not resolve OpenAPI specs, and it does not check that the pages named in `navigation` exist. |
|
|
23
|
+
|
|
24
|
+
`[dir]` defaults to the current directory in every command.
|
|
25
|
+
|
|
26
|
+
The npm package is `@writedocs/generator`; the CLI command it installs is still `writedocs` (see the `bin` field in `package.json`).
|
|
27
|
+
|
|
28
|
+
`npx @writedocs/generator init/dev/build` still works too, for a one-off run with no install at all.
|
package/bin/writedocs.js
CHANGED
|
@@ -1,73 +1,126 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import dotenv from 'dotenv';
|
|
7
|
-
import { runDev } from '../src/cli/dev.js';
|
|
8
|
-
import { runBuild } from '../src/cli/build.js';
|
|
9
|
-
import { runInit } from '../src/cli/init.js';
|
|
10
|
-
import { requireBuildKey } from '../src/cli/build-auth.js';
|
|
11
|
-
|
|
12
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
-
const packageRoot = path.resolve(__dirname, '..');
|
|
14
|
-
// Read rather than hardcode - a literal version string here silently drifts
|
|
15
|
-
// from package.json's own "version" the moment either one is bumped without
|
|
16
|
-
// the other (exactly what `npm version <bump>` does: it only touches
|
|
17
|
-
// package.json). `writedocs --version` should always reflect what actually
|
|
18
|
-
// got published, not whatever this string happened to say at the time this
|
|
19
|
-
// line was last hand-edited.
|
|
20
|
-
const { version } = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf-8'));
|
|
21
|
-
|
|
22
|
-
const program = new Command();
|
|
23
|
-
program
|
|
24
|
-
.name('writedocs')
|
|
25
|
-
.description('Static site generator for writedocs.json + MDX')
|
|
26
|
-
.version(version);
|
|
27
|
-
|
|
28
|
-
program
|
|
29
|
-
.command('dev')
|
|
30
|
-
.description('Start a local dev server with hot reload')
|
|
31
|
-
.argument('[dir]', 'content directory (contains writedocs.json and docs/)', '.')
|
|
32
|
-
.option('-p, --port <port>', 'port to run on')
|
|
33
|
-
.action(async (dir, opts) => {
|
|
34
|
-
await runDev({
|
|
35
|
-
contentDir: path.resolve(process.cwd(), dir),
|
|
36
|
-
packageRoot,
|
|
37
|
-
port: opts.port,
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
program
|
|
42
|
-
// { hidden: true } keeps `build` out of `writedocs --help` - it's not a
|
|
43
|
-
// command ordinary users are meant to discover or run themselves. See
|
|
44
|
-
// src/cli/build-auth.js for the second layer: even someone who knows the
|
|
45
|
-
// command exists still can't run it without the right key.
|
|
46
|
-
.command('build', { hidden: true })
|
|
47
|
-
.description('Build a static site into <dir>/dist')
|
|
48
|
-
.argument('[dir]', 'content directory (contains writedocs.json and docs/)', '.')
|
|
49
|
-
.option('-k, --key <key>', 'build authorization key (or set WRITEDOCS_API_KEY)')
|
|
50
|
-
.action(async (dir, opts) => {
|
|
51
|
-
const contentDir = path.resolve(process.cwd(), dir);
|
|
52
|
-
// Project-scoped, not global: a .env sitting next to this project's own
|
|
53
|
-
// writedocs.json (WRITEDOCS_KEY_SERVER_URL, WRITEDOCS_API_KEY) is picked
|
|
54
|
-
// up automatically, so build doesn't need those exported by hand every
|
|
55
|
-
// session. dotenv never overwrites a var already set in the real
|
|
56
|
-
// environment - an explicit `export`/CI secret still wins over the file.
|
|
57
|
-
dotenv.config({ path: path.join(contentDir, '.env'), quiet: true });
|
|
58
|
-
await requireBuildKey(opts.key || process.env.WRITEDOCS_API_KEY);
|
|
59
|
-
await runBuild({ contentDir, packageRoot });
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
program
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
.
|
|
66
|
-
.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import dotenv from 'dotenv';
|
|
7
|
+
import { runDev } from '../src/cli/dev.js';
|
|
8
|
+
import { runBuild } from '../src/cli/build.js';
|
|
9
|
+
import { runInit } from '../src/cli/init.js';
|
|
10
|
+
import { requireBuildKey } from '../src/cli/build-auth.js';
|
|
11
|
+
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
14
|
+
// Read rather than hardcode - a literal version string here silently drifts
|
|
15
|
+
// from package.json's own "version" the moment either one is bumped without
|
|
16
|
+
// the other (exactly what `npm version <bump>` does: it only touches
|
|
17
|
+
// package.json). `writedocs --version` should always reflect what actually
|
|
18
|
+
// got published, not whatever this string happened to say at the time this
|
|
19
|
+
// line was last hand-edited.
|
|
20
|
+
const { version } = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf-8'));
|
|
21
|
+
|
|
22
|
+
const program = new Command();
|
|
23
|
+
program
|
|
24
|
+
.name('writedocs')
|
|
25
|
+
.description('Static site generator for writedocs.json + MDX')
|
|
26
|
+
.version(version);
|
|
27
|
+
|
|
28
|
+
program
|
|
29
|
+
.command('dev')
|
|
30
|
+
.description('Start a local dev server with hot reload')
|
|
31
|
+
.argument('[dir]', 'content directory (contains writedocs.json and docs/)', '.')
|
|
32
|
+
.option('-p, --port <port>', 'port to run on')
|
|
33
|
+
.action(async (dir, opts) => {
|
|
34
|
+
await runDev({
|
|
35
|
+
contentDir: path.resolve(process.cwd(), dir),
|
|
36
|
+
packageRoot,
|
|
37
|
+
port: opts.port,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
program
|
|
42
|
+
// { hidden: true } keeps `build` out of `writedocs --help` - it's not a
|
|
43
|
+
// command ordinary users are meant to discover or run themselves. See
|
|
44
|
+
// src/cli/build-auth.js for the second layer: even someone who knows the
|
|
45
|
+
// command exists still can't run it without the right key.
|
|
46
|
+
.command('build', { hidden: true })
|
|
47
|
+
.description('Build a static site into <dir>/dist')
|
|
48
|
+
.argument('[dir]', 'content directory (contains writedocs.json and docs/)', '.')
|
|
49
|
+
.option('-k, --key <key>', 'build authorization key (or set WRITEDOCS_API_KEY)')
|
|
50
|
+
.action(async (dir, opts) => {
|
|
51
|
+
const contentDir = path.resolve(process.cwd(), dir);
|
|
52
|
+
// Project-scoped, not global: a .env sitting next to this project's own
|
|
53
|
+
// writedocs.json (WRITEDOCS_KEY_SERVER_URL, WRITEDOCS_API_KEY) is picked
|
|
54
|
+
// up automatically, so build doesn't need those exported by hand every
|
|
55
|
+
// session. dotenv never overwrites a var already set in the real
|
|
56
|
+
// environment - an explicit `export`/CI secret still wins over the file.
|
|
57
|
+
dotenv.config({ path: path.join(contentDir, '.env'), quiet: true });
|
|
58
|
+
await requireBuildKey(opts.key || process.env.WRITEDOCS_API_KEY);
|
|
59
|
+
await runBuild({ contentDir, packageRoot });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
program
|
|
63
|
+
// Visivel, ao contrario do `build`: validar a propria configuracao e
|
|
64
|
+
// exatamente o tipo de coisa que um usuario deve poder rodar sozinho, no CI
|
|
65
|
+
// inclusive. Sem chave, sem dotenv, sem rede - le um arquivo e sai 0 ou 1.
|
|
66
|
+
.command('validate')
|
|
67
|
+
.description('Validate writedocs.json against the schema')
|
|
68
|
+
.argument('[dir]', 'content directory (contains writedocs.json)', '.')
|
|
69
|
+
.action(async (dir) => {
|
|
70
|
+
const contentDir = path.resolve(process.cwd(), dir);
|
|
71
|
+
const configPath = path.join(contentDir, 'writedocs.json');
|
|
72
|
+
if (!fs.existsSync(configPath)) {
|
|
73
|
+
// Mesma mensagem que loadDocsConfig() ja da pro mesmo caso.
|
|
74
|
+
console.error(
|
|
75
|
+
`[writedocs] writedocs.json not found at ${configPath}. Run "writedocs init" to scaffold one.`
|
|
76
|
+
);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// O MESMO modulo que o build usa (via loadDocsConfig) e que a plataforma
|
|
81
|
+
// importa por `@writedocs/generator/config-schema` - e o que faz os tres
|
|
82
|
+
// reportarem os mesmos problemas com as mesmas palavras, em vez de cada um
|
|
83
|
+
// manter a sua copia do schema.
|
|
84
|
+
//
|
|
85
|
+
// Import dinamico porque isto e um `.ts` sendo carregado por node puro (sem
|
|
86
|
+
// o transform do Astro/Vite): funciona com o type stripping nativo do Node,
|
|
87
|
+
// estavel a partir do 22.18/23.6. O astro deste pacote ja exige >=22.12, mas
|
|
88
|
+
// entre 22.12 e 22.18 o import falharia com uma mensagem cifrada sobre
|
|
89
|
+
// "Unknown file extension .ts" - a mensagem abaixo diz o que fazer.
|
|
90
|
+
let validateDocsConfig;
|
|
91
|
+
let formatValidationIssues;
|
|
92
|
+
try {
|
|
93
|
+
({ validateDocsConfig, formatValidationIssues } = await import('../src/lib/config-schema.ts'));
|
|
94
|
+
} catch (err) {
|
|
95
|
+
console.error(
|
|
96
|
+
`[writedocs] validate needs a Node that can load TypeScript directly (Node 22.18+ or 23.6+); this one is ${process.version}.`
|
|
97
|
+
);
|
|
98
|
+
console.error(`[writedocs] ${err.message}`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const result = validateDocsConfig(fs.readFileSync(configPath, 'utf-8'));
|
|
103
|
+
if (result.ok) {
|
|
104
|
+
console.log(`[writedocs] ${configPath} is valid.`);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Mesmo formato que o build imprime num config invalido - mesmo texto,
|
|
109
|
+
// mesma ordem, montado pelo mesmo formatador.
|
|
110
|
+
console.error('[writedocs] writedocs.json failed validation:');
|
|
111
|
+
console.error(formatValidationIssues(result.issues));
|
|
112
|
+
process.exit(1);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
program
|
|
116
|
+
.command('init')
|
|
117
|
+
.description('Scaffold a writedocs.json and starter docs/ folder')
|
|
118
|
+
.argument('[dir]', 'directory to scaffold into', '.')
|
|
119
|
+
.action(async (dir) => {
|
|
120
|
+
await runInit({ targetDir: path.resolve(process.cwd(), dir) });
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
124
|
+
console.error(`[writedocs] ${err.message}`);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
});
|
package/package.json
CHANGED
|
@@ -1,79 +1,80 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@writedocs/generator",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Static site generator for docs — a writedocs.json + MDX folder in, a static site out.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"writedocs": "./bin/writedocs.js"
|
|
8
|
-
},
|
|
9
|
-
"exports": {
|
|
10
|
-
"./components": "./src/components/index.ts"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"@
|
|
45
|
-
"@astrojs/
|
|
46
|
-
"@astrojs/
|
|
47
|
-
"@astrojs/
|
|
48
|
-
"@
|
|
49
|
-
"@iconify-json/
|
|
50
|
-
"@iconify-json/fa6-
|
|
51
|
-
"@iconify-json/
|
|
52
|
-
"@iconify-json/
|
|
53
|
-
"@iconify-json/
|
|
54
|
-
"@iconify-json/
|
|
55
|
-
"@iconify-json/
|
|
56
|
-
"@iconify-json/
|
|
57
|
-
"@iconify-json/
|
|
58
|
-
"@
|
|
59
|
-
"@
|
|
60
|
-
"
|
|
61
|
-
"astro
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"react
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@writedocs/generator",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Static site generator for docs — a writedocs.json + MDX folder in, a static site out.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"writedocs": "./bin/writedocs.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
"./components": "./src/components/index.ts",
|
|
11
|
+
"./config-schema": "./src/lib/config-schema.ts"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"bin",
|
|
15
|
+
"src",
|
|
16
|
+
"astro.config.mjs"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "node bin/writedocs.js dev",
|
|
20
|
+
"build": "node bin/writedocs.js build",
|
|
21
|
+
"init": "node bin/writedocs.js init"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"docs",
|
|
25
|
+
"documentation",
|
|
26
|
+
"static-site-generator",
|
|
27
|
+
"mdx",
|
|
28
|
+
"astro"
|
|
29
|
+
],
|
|
30
|
+
"homepage": "https://github.com/writedocs/writedocs#readme",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/writedocs/writedocs.git"
|
|
34
|
+
},
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/writedocs/writedocs/issues"
|
|
37
|
+
},
|
|
38
|
+
"author": "Gabriel Raeder",
|
|
39
|
+
"license": "ISC",
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@apidevtools/swagger-parser": "^12.1.0",
|
|
45
|
+
"@astrojs/markdown-remark": "7.2.2",
|
|
46
|
+
"@astrojs/mdx": "^7.0.5",
|
|
47
|
+
"@astrojs/react": "^6.0.2",
|
|
48
|
+
"@astrojs/sitemap": "^3.7.3",
|
|
49
|
+
"@iconify-json/bi": "^1.2.7",
|
|
50
|
+
"@iconify-json/fa6-brands": "^1.2.6",
|
|
51
|
+
"@iconify-json/fa6-solid": "^1.2.4",
|
|
52
|
+
"@iconify-json/heroicons": "^1.2.3",
|
|
53
|
+
"@iconify-json/ion": "^1.2.7",
|
|
54
|
+
"@iconify-json/lucide": "^1.2.116",
|
|
55
|
+
"@iconify-json/mdi": "^1.2.3",
|
|
56
|
+
"@iconify-json/ri": "^1.2.10",
|
|
57
|
+
"@iconify-json/simple-icons": "^1.2.89",
|
|
58
|
+
"@iconify-json/tabler": "^1.2.35",
|
|
59
|
+
"@shikijs/transformers": "^4.3.1",
|
|
60
|
+
"@tailwindcss/vite": "^4.3.2",
|
|
61
|
+
"astro": "7.2.2",
|
|
62
|
+
"astro-icon": "^1.1.5",
|
|
63
|
+
"commander": "^15.0.0",
|
|
64
|
+
"dotenv": "^17.4.2",
|
|
65
|
+
"gray-matter": "^4.0.3",
|
|
66
|
+
"katex": "^0.16.47",
|
|
67
|
+
"mermaid": "^11.16.0",
|
|
68
|
+
"pagefind": "^1.5.2",
|
|
69
|
+
"react": "^19.2.8",
|
|
70
|
+
"react-dom": "^19.2.8",
|
|
71
|
+
"rehype-katex": "^7.0.1",
|
|
72
|
+
"remark-math": "^6.0.0",
|
|
73
|
+
"shiki": "^4.3.1",
|
|
74
|
+
"tailwindcss": "^4.3.2",
|
|
75
|
+
"zod": "^4.4.3"
|
|
76
|
+
},
|
|
77
|
+
"engines": {
|
|
78
|
+
"node": ">=22.18.0"
|
|
79
|
+
}
|
|
80
|
+
}
|