@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 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 build
13
- ```
14
-
15
- The npm package is `@writedocs/generator`; the CLI command it installs is still `writedocs` (see the `bin` field in `package.json`).
16
-
17
- `npx @writedocs/generator init/dev/build` still works too, for a one-off run with no install at all.
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
- .command('init')
64
- .description('Scaffold a writedocs.json and starter docs/ folder')
65
- .argument('[dir]', 'directory to scaffold into', '.')
66
- .action(async (dir) => {
67
- await runInit({ targetDir: path.resolve(process.cwd(), dir) });
68
- });
69
-
70
- program.parseAsync(process.argv).catch((err) => {
71
- console.error(`[writedocs] ${err.message}`);
72
- process.exit(1);
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.1.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
- "files": [
13
- "bin",
14
- "src",
15
- "astro.config.mjs"
16
- ],
17
- "scripts": {
18
- "dev": "node bin/writedocs.js dev",
19
- "build": "node bin/writedocs.js build",
20
- "init": "node bin/writedocs.js init"
21
- },
22
- "keywords": [
23
- "docs",
24
- "documentation",
25
- "static-site-generator",
26
- "mdx",
27
- "astro"
28
- ],
29
- "homepage": "https://github.com/writedocs/writedocs#readme",
30
- "repository": {
31
- "type": "git",
32
- "url": "git+https://github.com/writedocs/writedocs.git"
33
- },
34
- "bugs": {
35
- "url": "https://github.com/writedocs/writedocs/issues"
36
- },
37
- "author": "Gabriel Raeder",
38
- "license": "ISC",
39
- "publishConfig": {
40
- "access": "public"
41
- },
42
- "dependencies": {
43
- "@apidevtools/swagger-parser": "^12.1.0",
44
- "@astrojs/markdown-remark": "7.2.2",
45
- "@astrojs/mdx": "^7.0.5",
46
- "@astrojs/react": "^6.0.2",
47
- "@astrojs/sitemap": "^3.7.3",
48
- "@iconify-json/bi": "^1.2.7",
49
- "@iconify-json/fa6-brands": "^1.2.6",
50
- "@iconify-json/fa6-solid": "^1.2.4",
51
- "@iconify-json/heroicons": "^1.2.3",
52
- "@iconify-json/ion": "^1.2.7",
53
- "@iconify-json/lucide": "^1.2.116",
54
- "@iconify-json/mdi": "^1.2.3",
55
- "@iconify-json/ri": "^1.2.10",
56
- "@iconify-json/simple-icons": "^1.2.89",
57
- "@iconify-json/tabler": "^1.2.35",
58
- "@shikijs/transformers": "^4.3.1",
59
- "@tailwindcss/vite": "^4.3.2",
60
- "astro": "^7.2.2",
61
- "astro-icon": "^1.1.5",
62
- "commander": "^15.0.0",
63
- "dotenv": "^17.4.2",
64
- "gray-matter": "^4.0.3",
65
- "katex": "^0.16.47",
66
- "mermaid": "^11.16.0",
67
- "pagefind": "^1.5.2",
68
- "react": "^19.2.8",
69
- "react-dom": "^19.2.8",
70
- "rehype-katex": "^7.0.1",
71
- "remark-math": "^6.0.0",
72
- "shiki": "^4.3.1",
73
- "tailwindcss": "^4.3.2",
74
- "zod": "^4.4.3"
75
- },
76
- "engines": {
77
- "node": ">=20.3.0"
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
+ }