@writedocs/generator 0.4.10 → 0.4.12
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/astro.config.mjs +5 -4
- package/bin/writedocs.js +44 -25
- package/package.json +1 -1
- package/src/cli/api-pages-output.js +11 -0
- package/src/cli/astro-output.js +146 -0
- package/src/cli/astro-worker.js +78 -0
- package/src/cli/build-auth.js +7 -5
- package/src/cli/build.js +104 -7
- package/src/cli/convert.js +37 -26
- package/src/cli/dev-lock.js +51 -0
- package/src/cli/dev.js +158 -6
- package/src/cli/generate-api-pages.js +15 -10
- package/src/cli/init.js +7 -5
- package/src/cli/output.js +160 -0
- package/src/cli/preflight.js +11 -8
- package/src/cli/run-astro.js +76 -46
- package/src/cli/run-pagefind.js +14 -9
- package/src/components/AppIcon.astro +2 -1
- package/src/lib/astro-log-destination.js +25 -0
- package/src/lib/cli-report.js +20 -0
- package/src/lib/mdx-inline-react.js +2 -1
- package/src/lib/mdx-unknown-components.js +5 -3
package/src/cli/run-astro.js
CHANGED
|
@@ -1,57 +1,87 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';
|
|
2
1
|
import path from 'node:path';
|
|
3
2
|
import { spawn } from 'node:child_process';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const workerPath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'astro-worker.js');
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
8
|
+
* Runs Astro (`command`: 'dev' or 'build') in a child process -
|
|
9
|
+
* astro-worker.js - and turns everything it reports into events for
|
|
10
|
+
* `onEvent`, so the CLI decides what reaches the terminal:
|
|
11
|
+
*
|
|
12
|
+
* { type: 'astro-log', level, label, message } - an Astro/Vite log line
|
|
13
|
+
* { type: 'report', level, message, where } - writedocs' own code
|
|
14
|
+
* (lib/cli-report.js)
|
|
15
|
+
* { type: 'raw', stream, line } - anything printed directly
|
|
16
|
+
* { type: 'ready', urls } / { type: 'done' } / { type: 'fatal', error }
|
|
17
|
+
*
|
|
18
|
+
* A raw "[writedocs] ..." line is turned into a 'report' too, so a
|
|
19
|
+
* console.warn that doesn't go through cli-report.js still shows up.
|
|
20
|
+
*
|
|
21
|
+
* Returns { child, exited } - `exited` resolves with the exit code.
|
|
9
22
|
*/
|
|
10
|
-
function
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
23
|
+
export function runAstro(command, { packageRoot, contentDir, port, onEvent }) {
|
|
24
|
+
const child = spawn(process.execPath, [workerPath, command], {
|
|
25
|
+
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
|
26
|
+
// Explicit, not inherited: astro.config.mjs's own `outDir` lives
|
|
27
|
+
// inside packageRoot (writedocsBuildStagingDir() - see its own
|
|
28
|
+
// comment in writedocs-temp-dir.js for the full EXDEV story this is
|
|
29
|
+
// one half of), and Astro's static builder only stages its
|
|
30
|
+
// prerendered output *inside* that outDir - rather than falling
|
|
31
|
+
// back to `<process.cwd()>/.astro/.prerender/` - when outDir starts
|
|
32
|
+
// with process.cwd() (getOutDirWithinCwd, astro/dist/core/build/
|
|
33
|
+
// common.js). Left unset, this child would just inherit whatever
|
|
34
|
+
// directory the *parent* writedocs CLI process happened to be
|
|
35
|
+
// launched from - unrelated to packageRoot for a real globally-
|
|
36
|
+
// installed CLI invoked from wherever the user's shell happens to
|
|
37
|
+
// be - so the fallback branch would fire regardless, staging
|
|
38
|
+
// outside packageRoot again and reintroducing the exact module-
|
|
39
|
+
// resolution problem this whole design is meant to avoid. Pinning
|
|
40
|
+
// cwd to packageRoot here guarantees the "starts with cwd" check
|
|
41
|
+
// passes deterministically, independent of the parent process's own
|
|
42
|
+
// cwd.
|
|
43
|
+
cwd: packageRoot,
|
|
44
|
+
env: {
|
|
45
|
+
...process.env,
|
|
46
|
+
WRITEDOCS_CONTENT_DIR: contentDir,
|
|
47
|
+
// Astro is always run with root: packageRoot (astro-worker.js), so
|
|
48
|
+
// every CollectionEntry's `filePath` comes back relative to *this*,
|
|
49
|
+
// not to contentDir or the process's cwd. Exposed so lib/config.ts's
|
|
50
|
+
// fileIdForEntry() can resolve it back into a docs/-relative file id
|
|
51
|
+
// - see that function for why.
|
|
52
|
+
WRITEDOCS_PACKAGE_ROOT: packageRoot,
|
|
53
|
+
...(port ? { WRITEDOCS_PORT: String(port) } : {}),
|
|
54
|
+
},
|
|
55
|
+
});
|
|
16
56
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// resolution problem this whole design is meant to avoid. Pinning
|
|
36
|
-
// cwd to packageRoot here guarantees the "starts with cwd" check
|
|
37
|
-
// passes deterministically, independent of the parent process's own
|
|
38
|
-
// cwd.
|
|
39
|
-
cwd: packageRoot,
|
|
40
|
-
env: {
|
|
41
|
-
...process.env,
|
|
42
|
-
WRITEDOCS_CONTENT_DIR: contentDir,
|
|
43
|
-
// Astro is always invoked with `--root packageRoot` (see build.js/
|
|
44
|
-
// dev.js), so every CollectionEntry's `filePath` comes back
|
|
45
|
-
// relative to *this*, not to contentDir or the process's cwd.
|
|
46
|
-
// Exposed so lib/config.ts's fileIdForEntry() can resolve it back
|
|
47
|
-
// into a docs/-relative file id - see that function for why.
|
|
48
|
-
WRITEDOCS_PACKAGE_ROOT: packageRoot,
|
|
49
|
-
},
|
|
57
|
+
child.on('message', (message) => {
|
|
58
|
+
if (message && typeof message === 'object' && typeof message.type === 'string') onEvent(message);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
for (const streamName of ['stdout', 'stderr']) {
|
|
62
|
+
let pending = '';
|
|
63
|
+
const emit = (line) => {
|
|
64
|
+
if (line.trim() === '') return;
|
|
65
|
+
const own = line.match(/^\[writedocs\]\s+(?:(\S+:\d+(?::\d+)?) - )?(.*)$/);
|
|
66
|
+
if (own) onEvent({ type: 'report', level: streamName === 'stderr' ? 'warn' : 'info', message: own[2], where: own[1] ?? null });
|
|
67
|
+
else onEvent({ type: 'raw', stream: streamName, line });
|
|
68
|
+
};
|
|
69
|
+
child[streamName].setEncoding('utf8');
|
|
70
|
+
child[streamName].on('data', (chunk) => {
|
|
71
|
+
pending += chunk;
|
|
72
|
+
const lines = pending.split(/\r?\n/);
|
|
73
|
+
pending = lines.pop();
|
|
74
|
+
lines.forEach(emit);
|
|
50
75
|
});
|
|
51
|
-
child.on('
|
|
52
|
-
if (
|
|
53
|
-
|
|
76
|
+
child[streamName].on('end', () => {
|
|
77
|
+
if (pending) emit(pending);
|
|
78
|
+
pending = '';
|
|
54
79
|
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const exited = new Promise((resolve, reject) => {
|
|
55
83
|
child.on('error', reject);
|
|
84
|
+
child.on('close', (code, signal) => resolve(code ?? (signal ? 1 : 0)));
|
|
56
85
|
});
|
|
86
|
+
return { child, exited };
|
|
57
87
|
}
|
package/src/cli/run-pagefind.js
CHANGED
|
@@ -7,12 +7,11 @@ import { spawn } from 'node:child_process';
|
|
|
7
7
|
* directories from packageRoot - the same lookup algorithm Node itself
|
|
8
8
|
* uses for bare-specifier resolution, just done by hand with fs instead
|
|
9
9
|
* of require.resolve()/import.meta.resolve(). This is necessary (rather
|
|
10
|
-
* than
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* require.resolve('pagefind') also fails under CJS require() semantics
|
|
10
|
+
* than a require.resolve()-based lookup) because pagefind's own
|
|
11
|
+
* package.json declares an "exports" map with only a "." entry scoped to
|
|
12
|
+
* the "import" condition: require.resolve('pagefind/package.json') is
|
|
13
|
+
* blocked outright (exports doesn't list a package.json subpath at all),
|
|
14
|
+
* and require.resolve('pagefind') also fails under CJS require() semantics
|
|
16
15
|
* (no "require"/"default" condition for it to satisfy).
|
|
17
16
|
* import.meta.resolve() would sidestep both, but needs Node 20.6+ -
|
|
18
17
|
* newer than this package's documented minimum (20.3.0, see engines in
|
|
@@ -50,16 +49,22 @@ function resolvePagefindBin(packageRoot) {
|
|
|
50
49
|
* `.wd-article`) - without that scoping, Pagefind falls back to indexing
|
|
51
50
|
* every page's entire <body>, polluting every result with sidebar/topbar
|
|
52
51
|
* chrome text repeated on every page.
|
|
52
|
+
*
|
|
53
|
+
* Pagefind's output is captured, not printed: the CLI prints its own step
|
|
54
|
+
* line, and Pagefind's text only when it fails (in the rejected Error).
|
|
53
55
|
*/
|
|
54
56
|
export function runPagefind(siteDir, { packageRoot }) {
|
|
55
57
|
const bin = resolvePagefindBin(packageRoot);
|
|
56
58
|
return new Promise((resolve, reject) => {
|
|
57
59
|
const child = spawn(process.execPath, [bin, '--site', siteDir, '--quiet'], {
|
|
58
|
-
stdio: '
|
|
60
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
59
61
|
});
|
|
60
|
-
|
|
62
|
+
let output = '';
|
|
63
|
+
child.stdout.on('data', (d) => (output += d));
|
|
64
|
+
child.stderr.on('data', (d) => (output += d));
|
|
65
|
+
child.on('close', (code) => {
|
|
61
66
|
if (code === 0) resolve();
|
|
62
|
-
else reject(new Error(`pagefind exited with code ${code}`));
|
|
67
|
+
else reject(new Error(`pagefind exited with code ${code}${output.trim() ? `:\n${output.trim()}` : ''}`));
|
|
63
68
|
});
|
|
64
69
|
child.on('error', reject);
|
|
65
70
|
});
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// don't each need their own copy of the resolveIcon() call + branch.
|
|
9
9
|
import { Icon } from 'astro-icon/components';
|
|
10
10
|
import { resolveIcon, iconExists } from '../lib/config';
|
|
11
|
+
import { report } from '../lib/cli-report.js';
|
|
11
12
|
|
|
12
13
|
interface Props {
|
|
13
14
|
icon?: string;
|
|
@@ -30,7 +31,7 @@ if (icon && resolved?.kind === 'iconify' && !iconExists(icon)) {
|
|
|
30
31
|
const warned: Set<string> = ((globalThis as any).__wdWarnedIcons ??= new Set());
|
|
31
32
|
if (!warned.has(icon)) {
|
|
32
33
|
warned.add(icon);
|
|
33
|
-
|
|
34
|
+
report('warn', `Unknown icon "${icon}" - no installed icon set has it, so it's left out.`);
|
|
34
35
|
}
|
|
35
36
|
resolved = null;
|
|
36
37
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Astro logger destination for the writedocs CLI (Astro's `logger` config -
|
|
2
|
+
// see src/cli/astro-worker.js, which sets it). Every message Astro and Vite
|
|
3
|
+
// log - build steps, route lists, dev request logs, warnings, errors - comes
|
|
4
|
+
// through write() as a { level, label, message } event; instead of printing
|
|
5
|
+
// it, this hands the event to the writedocs CLI process over IPC, and the CLI
|
|
6
|
+
// (src/cli/output.js) decides what the author actually sees.
|
|
7
|
+
//
|
|
8
|
+
// Plain JS with no imports: Astro also bundles this module into the build's
|
|
9
|
+
// own server code (virtual:astro:logger), and it has to load there too.
|
|
10
|
+
// Outside the CLI - no IPC channel - it falls back to printing the message,
|
|
11
|
+
// so nothing is ever lost silently.
|
|
12
|
+
export default function writedocsLogDestination() {
|
|
13
|
+
return {
|
|
14
|
+
write(event) {
|
|
15
|
+
const message = String(event.message ?? '');
|
|
16
|
+
if (typeof process !== 'undefined' && typeof process.send === 'function' && process.connected) {
|
|
17
|
+
process.send({ type: 'astro-log', level: event.level, label: event.label ?? null, message });
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const line = event.label && event.label !== 'SKIP_FORMAT' ? `[${event.label}] ${message}` : message;
|
|
21
|
+
if (event.level === 'error' || event.level === 'warn') console.error(line);
|
|
22
|
+
else console.log(line);
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// How writedocs code that runs inside Astro - astro.config.mjs, remark
|
|
2
|
+
// plugins, components - tells the author something. Under the writedocs CLI
|
|
3
|
+
// the message goes over IPC to the CLI process (src/cli/run-astro.js), which
|
|
4
|
+
// prints it in its own format: warnings are listed at the end of a build,
|
|
5
|
+
// or as they happen in `writedocs dev`. Anywhere else (Astro run directly,
|
|
6
|
+
// a test) it's printed with a [writedocs] prefix.
|
|
7
|
+
//
|
|
8
|
+
// level: 'warn' - something the author should fix or know about;
|
|
9
|
+
// 'info' - a note about how the site was built (shown after a build);
|
|
10
|
+
// 'debug' - only with --verbose.
|
|
11
|
+
// where: optional "file:line" the message is about, relative to the project.
|
|
12
|
+
export function report(level, message, where) {
|
|
13
|
+
if (typeof process !== 'undefined' && typeof process.send === 'function' && process.connected) {
|
|
14
|
+
process.send({ type: 'report', level, message, where: where ?? null });
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const text = `[writedocs] ${where ? `${where} - ` : ''}${message}`;
|
|
18
|
+
if (level === 'warn') console.warn(text);
|
|
19
|
+
else console.log(text);
|
|
20
|
+
}
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import crypto from 'node:crypto';
|
|
4
4
|
import { parse as acornParse } from 'acorn';
|
|
5
|
+
import { report } from './cli-report.js';
|
|
5
6
|
import { writedocsTempDir } from './writedocs-temp-dir.js';
|
|
6
7
|
import { MINTLIFY_HOOKS } from './mdx-mintlify.js';
|
|
7
8
|
import { BUILTIN_COMPONENT_NAMES } from './mdx-inject-builtins.js';
|
|
@@ -137,7 +138,7 @@ export function remarkExtractInlineReactComponents() {
|
|
|
137
138
|
const relPath = path.relative(contentDir, file.path).split(path.sep).join('/');
|
|
138
139
|
for (const use of simplified) {
|
|
139
140
|
const line = source.slice(0, use.offset).split('\n').length;
|
|
140
|
-
|
|
141
|
+
report('warn', simplifiedBuiltinMessage(use).message, `${relPath}:${line}`);
|
|
141
142
|
}
|
|
142
143
|
|
|
143
144
|
const pageDir = path.dirname(file.path);
|
|
@@ -2,6 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
import { visit } from 'unist-util-visit';
|
|
3
3
|
import { parse as acornParse } from 'acorn';
|
|
4
4
|
import { BUILTIN_COMPONENT_NAMES } from './mdx-inject-builtins.js';
|
|
5
|
+
import { report } from './cli-report.js';
|
|
5
6
|
|
|
6
7
|
// Which JSX elements in an MDX file are components writedocs can't resolve
|
|
7
8
|
// - neither a built-in nor something the file imports or defines itself.
|
|
@@ -120,9 +121,10 @@ export function remarkUnknownComponentFallback() {
|
|
|
120
121
|
: '(unknown file)';
|
|
121
122
|
const stubs = new Set();
|
|
122
123
|
for (const { node, name, line, inCode, root } of found) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
report(
|
|
125
|
+
'warn',
|
|
126
|
+
`Unknown component <${name}> - showing only its content. Remove it, or define it in a snippet.`,
|
|
127
|
+
`${filePath}${line ? `:${line}` : ''}`
|
|
126
128
|
);
|
|
127
129
|
if (inCode) {
|
|
128
130
|
stubs.add(root);
|