@clidey/dory 1.0.8 → 1.10.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 +59 -164
- package/bin/chat-server.ts +270 -0
- package/bin/dist/analytics-server.d.ts +11 -0
- package/bin/dist/analytics-server.d.ts.map +1 -0
- package/bin/dist/analytics-server.js +121 -0
- package/bin/dist/analytics-server.js.map +1 -0
- package/bin/dist/automation.d.ts +35 -0
- package/bin/dist/automation.d.ts.map +1 -0
- package/bin/dist/automation.js +259 -0
- package/bin/dist/automation.js.map +1 -0
- package/bin/dist/chat-server.d.ts +8 -0
- package/bin/dist/chat-server.d.ts.map +1 -0
- package/bin/dist/chat-server.js +322 -0
- package/bin/dist/chat-server.js.map +1 -0
- package/bin/dist/check.d.ts +16 -0
- package/bin/dist/check.d.ts.map +1 -0
- package/bin/dist/check.js +247 -0
- package/bin/dist/check.js.map +1 -0
- package/bin/dist/dependency-overlay.d.ts +7 -0
- package/bin/dist/dependency-overlay.d.ts.map +1 -0
- package/bin/dist/dependency-overlay.js +137 -0
- package/bin/dist/dependency-overlay.js.map +1 -0
- package/bin/dist/dory.js +254 -402
- package/bin/dist/dory.js.map +1 -1
- package/bin/dist/i18n-init.d.ts +6 -0
- package/bin/dist/i18n-init.d.ts.map +1 -0
- package/bin/dist/i18n-init.js +56 -0
- package/bin/dist/i18n-init.js.map +1 -0
- package/bin/dist/mcp-http.d.ts +8 -0
- package/bin/dist/mcp-http.d.ts.map +1 -0
- package/bin/dist/mcp-http.js +72 -0
- package/bin/dist/mcp-http.js.map +1 -0
- package/bin/dist/mcp.d.ts +2 -0
- package/bin/dist/mcp.d.ts.map +1 -0
- package/bin/dist/mcp.js +81 -0
- package/bin/dist/mcp.js.map +1 -0
- package/bin/dist/preview-entry.d.ts +2 -0
- package/bin/dist/preview-entry.d.ts.map +1 -0
- package/bin/dist/preview-entry.js +6 -0
- package/bin/dist/preview-entry.js.map +1 -0
- package/bin/dist/preview-server.d.ts +5 -0
- package/bin/dist/preview-server.d.ts.map +1 -0
- package/bin/dist/preview-server.js +146 -0
- package/bin/dist/preview-server.js.map +1 -0
- package/bin/dist/preview-static.d.ts +7 -0
- package/bin/dist/preview-static.d.ts.map +1 -0
- package/bin/dist/preview-static.js +70 -0
- package/bin/dist/preview-static.js.map +1 -0
- package/bin/dist/project-config.d.ts +22 -0
- package/bin/dist/project-config.d.ts.map +1 -0
- package/bin/dist/project-config.js +44 -0
- package/bin/dist/project-config.js.map +1 -0
- package/dist/index.html +57 -0
- package/dist/runtime/dory-runtime.css +2 -0
- package/dist/runtime/dory-runtime.html +14 -0
- package/dist/runtime/dory-runtime.js +5490 -0
- package/docs/api-playground-demo.mdx +119 -0
- package/docs/api-reference/asyncapi.json +324 -0
- package/docs/api-reference/openapi.json +366 -0
- package/docs/api-reference/operations/create.mdx +5 -0
- package/docs/api-reference/operations/delete.mdx +5 -0
- package/docs/api-reference/operations/get.mdx +5 -0
- package/docs/api-reference/operations/update.mdx +5 -0
- package/docs/api-reference/websocket-operations/receive-chat-message.mdx +5 -0
- package/docs/api-reference/websocket-operations/receive-notifications.mdx +5 -0
- package/docs/api-reference/websocket-operations/receive-presence-update.mdx +5 -0
- package/docs/api-reference/websocket-operations/send-chat-message.mdx +5 -0
- package/docs/api-reference/websocket-operations/send-presence-update.mdx +5 -0
- package/docs/assets/dory.jpeg +0 -0
- package/docs/components/accordion.mdx +145 -0
- package/docs/components/callouts.mdx +134 -0
- package/docs/components/cards.mdx +153 -0
- package/docs/components/code-blocks.mdx +281 -0
- package/docs/components/expandable.mdx +189 -0
- package/docs/components/icons.mdx +71 -0
- package/docs/components/images.mdx +82 -0
- package/docs/components/latex.mdx +148 -0
- package/docs/components/layout.mdx +178 -0
- package/docs/components/lists.mdx +134 -0
- package/docs/components/mermaid.mdx +149 -0
- package/docs/components/overview.mdx +81 -0
- package/docs/components/source.mdx +130 -0
- package/docs/components/steps.mdx +122 -0
- package/docs/components/tables.mdx +114 -0
- package/docs/components/tags.mdx +179 -0
- package/docs/components/typography.mdx +185 -0
- package/docs/configuration/analytics.mdx +45 -0
- package/docs/configuration/chat.mdx +59 -0
- package/docs/configuration/dory-json.mdx +278 -0
- package/docs/configuration/embed.mdx +73 -0
- package/docs/configuration/fonts.mdx +86 -0
- package/docs/configuration/internationalization.mdx +61 -0
- package/docs/configuration/navigation.mdx +217 -0
- package/docs/configuration/theming.mdx +166 -0
- package/docs/contributing.mdx +47 -0
- package/docs/dory.json +136 -0
- package/docs/embed-example.html +176 -0
- package/docs/favicon.svg +18 -0
- package/docs/features.mdx +440 -0
- package/docs/getting-started/automation.mdx +76 -0
- package/docs/getting-started/configuration.mdx +95 -0
- package/docs/getting-started/custom-components.mdx +60 -0
- package/docs/getting-started/installation.mdx +88 -0
- package/docs/getting-started/quick-start.mdx +53 -0
- package/docs/introduction.mdx +50 -0
- package/docs/logo/logo.svg +18 -0
- package/docs/playgrounds/api-playground.mdx +122 -0
- package/docs/playgrounds/websocket-playground.mdx +98 -0
- package/docs/social-card.png +0 -0
- package/docs/social-card.svg +17 -0
- package/docs/websocket-playground-demo.mdx +135 -0
- package/package.json +23 -17
- package/scripts/verify-component-examples.mjs +126 -0
- package/scripts/verify-runtime-parity.mjs +147 -0
- package/src/app.css +22 -1
- package/src/assets/fonts/HankenGrotesk.ttf +0 -0
- package/src/assets/fonts/OFL.txt +94 -0
- package/src/components/chat-assistant.tsx +229 -0
- package/src/components/dropdown.tsx +3 -3
- package/src/components/error-boundary.tsx +2 -2
- package/src/components/hooks.tsx +20 -1
- package/src/components/navigation.tsx +82 -51
- package/src/components/notification.tsx +2 -3
- package/src/components/routes.tsx +17 -19
- package/src/components/search-index.test.ts +15 -0
- package/src/components/search-index.ts +49 -48
- package/src/components/search.tsx +208 -104
- package/src/components/store.ts +27 -9
- package/src/config/mdx-common.ts +11 -0
- package/src/config/mdx.ts +10 -11
- package/src/embed/loader.ts +0 -3
- package/src/embed/types.ts +0 -2
- package/src/embed/widget-iframe.ts +19 -21
- package/src/embedded-runtime/config.ts +138 -0
- package/src/embedded-runtime/index.tsx +680 -0
- package/src/embedded-runtime/runtime.html +14 -0
- package/src/entry-server.tsx +46 -124
- package/src/main.tsx +17 -5
- package/src/mdx/accordion.tsx +3 -3
- package/src/mdx/api-playground.tsx +18 -16
- package/src/mdx/async-api.tsx +2 -2
- package/src/mdx/card.tsx +3 -2
- package/src/mdx/code.tsx +11 -9
- package/src/mdx/fence.tsx +6 -5
- package/src/mdx/icon-grid.tsx +4 -1
- package/src/mdx/icon-names.test.ts +15 -0
- package/src/mdx/icon-names.ts +60 -0
- package/src/mdx/icon-source-variants.ts +102 -0
- package/src/mdx/icon.tsx +12 -73
- package/src/mdx/latex.test.ts +38 -0
- package/src/mdx/latex.tsx +42 -64
- package/src/mdx/mdx.tsx +1 -1
- package/src/mdx/mermaid.tsx +12 -6
- package/src/mdx/open-api.tsx +130 -74
- package/src/mdx/preprocess.test.ts +16 -0
- package/src/mdx/preprocess.ts +61 -0
- package/src/mdx/processor.test.ts +135 -37
- package/src/mdx/processor.ts +54 -92
- package/src/mdx/source.tsx +5 -4
- package/src/mdx/specimen.tsx +57 -43
- package/src/mdx/tabs.tsx +32 -21
- package/src/mdx/text.tsx +11 -11
- package/src/mdx/try-it-panel.tsx +284 -0
- package/src/mdx/type-scale.tsx +7 -7
- package/src/mdx/websocket-playground.tsx +21 -14
- package/src/plugins/analytics-injector.ts +8 -0
- package/src/plugins/chat-dev-server.ts +32 -0
- package/src/plugins/docs-assets.test.ts +25 -0
- package/src/plugins/docs-assets.ts +6 -2
- package/src/plugins/frontmatter-dev-server.ts +2 -2
- package/src/plugins/frontmatter-generator.ts +1 -1
- package/src/plugins/html-files-middleware.test.ts +21 -1
- package/src/plugins/html-files-middleware.ts +26 -6
- package/src/plugins/html-metadata-injector.ts +24 -4
- package/src/plugins/llm-txt-dev-server.ts +2 -2
- package/src/plugins/llm-txt-generator.ts +21 -4
- package/src/plugins/prerender.ts +47 -20
- package/src/plugins/robots-generator.ts +1 -1
- package/src/plugins/sanitize.ts +6 -5
- package/src/plugins/shared.ts +6 -18
- package/src/plugins/sitemap-generator.ts +1 -1
- package/src/plugins/social-preview.test.ts +71 -0
- package/src/plugins/social-preview.ts +87 -0
- package/src/plugins/unified-mdx-plugin.ts +1 -1
- package/src/theme.scss +630 -207
- package/src/types/config.ts +53 -1
- package/src/ui/breadcrumb.tsx +17 -0
- package/src/ui/docs-header-view.tsx +32 -0
- package/src/ui/docs-navigation-view.tsx +69 -0
- package/src/ui/docs-page-frame.tsx +50 -0
- package/src/ui/dory-mark.tsx +9 -0
- package/src/ui/header.tsx +33 -90
- package/src/ui/layout.tsx +99 -153
- package/src/ui/mobile-navigation.tsx +10 -8
- package/src/ui/page-actions.tsx +67 -0
- package/src/ui/page-feedback.tsx +50 -0
- package/src/ui/page-footer.tsx +23 -0
- package/src/ui/prev-next-link.tsx +15 -69
- package/src/ui/prev-next-view.tsx +27 -0
- package/src/ui/table-of-content-view.tsx +167 -0
- package/src/ui/table-of-content.tsx +25 -183
- package/src/utils/analytics.test.ts +36 -0
- package/src/utils/analytics.ts +35 -0
- package/src/utils/colors.ts +7 -0
- package/src/utils/content-scope.test.ts +26 -0
- package/src/utils/content-scope.ts +39 -0
- package/src/utils/search-content.ts +12 -0
- package/src/utils/theme.ts +1 -7
- package/vite.config.ssr.ts +4 -4
- package/vite.config.ts +22 -16
package/bin/dist/dory.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { existsSync, rmSync, mkdirSync, cpSync, readFileSync, readdirSync, writeFileSync } from 'fs';
|
|
2
|
+
import { execFileSync, spawn } from 'child_process';
|
|
3
|
+
import { existsSync, rmSync, mkdirSync, mkdtempSync, cpSync, readFileSync, readdirSync, lstatSync, writeFileSync, watch } from 'fs';
|
|
4
4
|
import { resolve, dirname, basename } from 'path';
|
|
5
5
|
import { tmpdir } from 'os';
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
7
|
+
import { discoverSite, listPages, projectInfo, readPage, screenshot } from './automation.js';
|
|
8
|
+
import { serveMcp } from './mcp.js';
|
|
9
|
+
import { checkProject, formatCheckResult } from './check.js';
|
|
10
|
+
import { initLocale } from './i18n-init.js';
|
|
11
|
+
import { startPreview } from './preview-server.js';
|
|
12
|
+
import { validateDoryConfig } from './project-config.js';
|
|
13
|
+
import { createDependencyOverlay } from './dependency-overlay.js';
|
|
9
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
15
|
const __dirname = dirname(__filename);
|
|
11
16
|
// Get the root directory of the Dory package
|
|
@@ -18,437 +23,261 @@ const getDoryRoot = () => {
|
|
|
18
23
|
};
|
|
19
24
|
// Get the user's current working directory
|
|
20
25
|
const getUserRoot = () => process.cwd();
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
else {
|
|
29
|
-
count++;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return count;
|
|
26
|
+
const workspaceFiles = [
|
|
27
|
+
'src', 'bin', 'scripts', 'index.html', 'package.json', 'vite.config.ts',
|
|
28
|
+
'vite.config.ssr.ts', 'vite.config.embed-loader.ts', 'vite.config.embed-widget.ts',
|
|
29
|
+
];
|
|
30
|
+
const projectDocsDir = (userRoot) => {
|
|
31
|
+
const docsDir = resolve(userRoot, 'docs');
|
|
32
|
+
return existsSync(docsDir) ? docsDir : userRoot;
|
|
33
33
|
};
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
console.error('❌ No package manager found');
|
|
47
|
-
console.error(' Install npm or pnpm to use Dory');
|
|
48
|
-
console.error(' npm: https://nodejs.org/');
|
|
49
|
-
console.error(' pnpm: npm install -g pnpm');
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
34
|
+
const findDependencyDir = (doryRoot) => {
|
|
35
|
+
let current = doryRoot;
|
|
36
|
+
while (true) {
|
|
37
|
+
const candidate = resolve(current, 'node_modules');
|
|
38
|
+
if (existsSync(resolve(candidate, 'vite', 'bin', 'vite.js')) && existsSync(resolve(candidate, 'preact', 'package.json')))
|
|
39
|
+
return candidate;
|
|
40
|
+
const parent = dirname(current);
|
|
41
|
+
if (parent === current)
|
|
42
|
+
break;
|
|
43
|
+
current = parent;
|
|
52
44
|
}
|
|
45
|
+
throw new Error('Dory dependencies were not found. Install @clidey/dory in this project first.');
|
|
53
46
|
};
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
47
|
+
const loadProjectEnv = (userRoot) => {
|
|
48
|
+
for (const name of ['.env', '.env.local']) {
|
|
49
|
+
const file = resolve(userRoot, name);
|
|
50
|
+
if (existsSync(file))
|
|
51
|
+
process.loadEnvFile(file);
|
|
57
52
|
}
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
};
|
|
54
|
+
const stageDocs = (userRoot, workspace) => {
|
|
55
|
+
const source = projectDocsDir(userRoot);
|
|
56
|
+
const destination = resolve(workspace, 'docs');
|
|
57
|
+
rmSync(destination, { recursive: true, force: true });
|
|
58
|
+
mkdirSync(destination, { recursive: true });
|
|
59
|
+
const excluded = new Set(['node_modules', 'dist', 'dist-ssr', '.git', '.github', '.DS_Store', '.env', '.env.local', 'pnpm-lock.yaml', 'package-lock.json', 'yarn.lock']);
|
|
60
|
+
for (const item of readdirSync(source)) {
|
|
61
|
+
if (excluded.has(item) || item.startsWith('.env.') || (source === userRoot && item === 'docs'))
|
|
62
|
+
continue;
|
|
63
|
+
cpSync(resolve(source, item), resolve(destination, item), {
|
|
64
|
+
recursive: true,
|
|
65
|
+
force: true,
|
|
66
|
+
filter: (path) => {
|
|
67
|
+
const name = basename(path);
|
|
68
|
+
if (excluded.has(name) || name.startsWith('.env.'))
|
|
69
|
+
return false;
|
|
70
|
+
if (!lstatSync(path).isSymbolicLink())
|
|
71
|
+
return true;
|
|
72
|
+
console.warn(`Skipping symbolic link in docs: ${path}`);
|
|
73
|
+
return false;
|
|
74
|
+
},
|
|
75
|
+
});
|
|
60
76
|
}
|
|
77
|
+
cpSync(resolve(userRoot, 'dory.json'), resolve(destination, 'dory.json'), { force: true });
|
|
61
78
|
};
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
return null;
|
|
79
|
+
const createWorkspace = (userRoot) => {
|
|
80
|
+
const doryRoot = getDoryRoot();
|
|
81
|
+
const workspace = mkdtempSync(resolve(tmpdir(), 'dory-workspace-'));
|
|
66
82
|
try {
|
|
67
|
-
|
|
83
|
+
for (const item of workspaceFiles) {
|
|
84
|
+
const source = resolve(doryRoot, item);
|
|
85
|
+
if (!existsSync(source))
|
|
86
|
+
throw new Error(`Installed Dory package is missing ${item}`);
|
|
87
|
+
cpSync(source, resolve(workspace, item), { recursive: true, force: true });
|
|
88
|
+
}
|
|
89
|
+
createDependencyOverlay(resolve(workspace, 'node_modules'), findDependencyDir(doryRoot), userRoot, process.env.DORY_PACKAGE_OVERRIDES);
|
|
90
|
+
stageDocs(userRoot, workspace);
|
|
91
|
+
return workspace;
|
|
68
92
|
}
|
|
69
|
-
catch {
|
|
70
|
-
|
|
93
|
+
catch (error) {
|
|
94
|
+
rmSync(workspace, { recursive: true, force: true });
|
|
95
|
+
throw error;
|
|
71
96
|
}
|
|
72
97
|
};
|
|
73
|
-
|
|
74
|
-
const normalizeBasePath = (value) => {
|
|
75
|
-
if (typeof value !== 'string')
|
|
76
|
-
return '';
|
|
77
|
-
const trimmed = value.trim().replace(/^\/+|\/+$/g, '');
|
|
78
|
-
return trimmed ? `/${trimmed}` : '';
|
|
79
|
-
};
|
|
98
|
+
const viteCli = (workspace) => resolve(workspace, 'node_modules', 'vite', 'bin', 'vite.js');
|
|
80
99
|
const commands = {
|
|
81
|
-
|
|
82
|
-
|
|
100
|
+
capabilities: () => console.log(JSON.stringify({ customComponents: true, packageOverrides: true, specimenSourceLanguages: ['mdx', 'tsx', 'jsx'] })),
|
|
101
|
+
reference: () => {
|
|
102
|
+
const root = resolve(getDoryRoot(), 'docs');
|
|
103
|
+
const page = process.argv[3];
|
|
104
|
+
console.log(page ? readPage(root, page) : listPages(root).join('\n'));
|
|
105
|
+
},
|
|
106
|
+
init: () => {
|
|
83
107
|
const userRoot = getUserRoot();
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
const configContent = readFileSync(doryConfigPath, 'utf8');
|
|
101
|
-
config = JSON.parse(configContent);
|
|
102
|
-
validateDoryConfig(config);
|
|
103
|
-
console.log(`📋 Building: ${config.name || 'Documentation'}`);
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
console.error('❌ Failed to parse dory.json:');
|
|
107
|
-
if (error instanceof Error) {
|
|
108
|
-
console.error(` ${error.message}`);
|
|
109
|
-
}
|
|
110
|
-
process.exit(1);
|
|
111
|
-
}
|
|
112
|
-
// The build stages user files into the package's own docs/ directory, so a
|
|
113
|
-
// docs/ folder in the user's project cannot be included. Fail loudly
|
|
114
|
-
// instead of silently dropping it.
|
|
115
|
-
const userDocsDir = resolve(userRoot, 'docs');
|
|
116
|
-
if (existsSync(userDocsDir) && userDocsDir !== tempDocsDir) {
|
|
117
|
-
console.error('❌ Your project contains a docs/ directory');
|
|
118
|
-
console.error(' Dory currently cannot include a top-level docs/ folder in the build');
|
|
119
|
-
console.error(' Rename it (e.g. to guides/) and update dory.json navigation paths');
|
|
120
|
-
process.exit(1);
|
|
121
|
-
}
|
|
122
|
-
// Detect package manager
|
|
123
|
-
const pm = getPackageManager();
|
|
124
|
-
// Guard against concurrent builds mutating the package's docs/ directory
|
|
125
|
-
const lockPath = resolve(doryRoot, '.dory-build-lock');
|
|
126
|
-
if (existsSync(lockPath)) {
|
|
127
|
-
console.error('❌ Another dory build appears to be in progress');
|
|
128
|
-
console.error(` Lock file: ${lockPath}`);
|
|
129
|
-
console.error(' If no other build is running, delete the lock file and retry');
|
|
130
|
-
process.exit(1);
|
|
131
|
-
}
|
|
132
|
-
writeFileSync(lockPath, String(process.pid), 'utf8');
|
|
133
|
-
// Step 2: Backup existing docs directory to a safe temp location
|
|
134
|
-
console.log('🧹 Preparing workspace...');
|
|
135
|
-
const docsExistedBefore = existsSync(tempDocsDir);
|
|
136
|
-
const safeBackupDir = docsExistedBefore
|
|
137
|
-
? resolve(tmpdir(), `dory-backup-${Date.now()}`)
|
|
138
|
-
: null;
|
|
139
|
-
if (docsExistedBefore && safeBackupDir) {
|
|
140
|
-
console.log('💾 Backing up existing docs directory...');
|
|
141
|
-
cpSync(tempDocsDir, safeBackupDir, { recursive: true, force: true });
|
|
142
|
-
// Verify backup integrity by comparing file counts
|
|
143
|
-
const origCount = countFiles(tempDocsDir);
|
|
144
|
-
const backupCount = countFiles(safeBackupDir);
|
|
145
|
-
if (origCount !== backupCount) {
|
|
146
|
-
console.error(`❌ Backup verification failed (expected ${origCount} files, got ${backupCount})`);
|
|
147
|
-
console.error(` Backup location: ${safeBackupDir}`);
|
|
148
|
-
rmSync(lockPath, { force: true });
|
|
149
|
-
process.exit(1);
|
|
150
|
-
}
|
|
151
|
-
rmSync(tempDocsDir, { recursive: true, force: true });
|
|
152
|
-
}
|
|
153
|
-
// If the process is interrupted while the package's docs/ is mutated,
|
|
154
|
-
// restore the backup before exiting so no data is lost.
|
|
155
|
-
const restoreOnSignal = () => {
|
|
156
|
-
console.error('\n⚠️ Build interrupted — restoring docs directory...');
|
|
157
|
-
try {
|
|
158
|
-
if (docsExistedBefore && safeBackupDir && existsSync(safeBackupDir)) {
|
|
159
|
-
rmSync(tempDocsDir, { recursive: true, force: true });
|
|
160
|
-
cpSync(safeBackupDir, tempDocsDir, { recursive: true, force: true });
|
|
161
|
-
rmSync(safeBackupDir, { recursive: true, force: true });
|
|
162
|
-
}
|
|
163
|
-
else if (!docsExistedBefore && existsSync(tempDocsDir)) {
|
|
164
|
-
rmSync(tempDocsDir, { recursive: true, force: true });
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
catch {
|
|
168
|
-
if (safeBackupDir) {
|
|
169
|
-
console.error(` Restore failed — backup preserved at: ${safeBackupDir}`);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
rmSync(lockPath, { force: true });
|
|
173
|
-
process.exit(1);
|
|
108
|
+
const configPath = resolve(userRoot, 'dory.json');
|
|
109
|
+
if (existsSync(configPath))
|
|
110
|
+
throw new Error('dory.json already exists in this directory');
|
|
111
|
+
const docsDir = resolve(userRoot, 'docs');
|
|
112
|
+
const introduction = resolve(docsDir, 'introduction.mdx');
|
|
113
|
+
if (existsSync(introduction))
|
|
114
|
+
throw new Error('docs/introduction.mdx already exists; initialization would overwrite it');
|
|
115
|
+
mkdirSync(docsDir, { recursive: true });
|
|
116
|
+
const name = basename(userRoot).replace(/[-_]/g, ' ').replace(/\b\w/g, letter => letter.toUpperCase());
|
|
117
|
+
const config = {
|
|
118
|
+
name,
|
|
119
|
+
description: `Documentation for ${name}`,
|
|
120
|
+
favicon: './logo.svg',
|
|
121
|
+
logo: { light: './logo.svg', dark: './logo.svg' },
|
|
122
|
+
navigation: { tabs: [{ tab: 'Guides', groups: [{ group: 'Getting started', pages: ['introduction'] }] }] },
|
|
174
123
|
};
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (excludeItems.has(item)) {
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
const srcPath = resolve(userRoot, item);
|
|
202
|
-
const destPath = resolve(tempDocsDir, item);
|
|
124
|
+
writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
125
|
+
writeFileSync(introduction, `---\ntitle: Introduction\ndescription: Get started with ${name}.\n---\n\n# Welcome to ${name}\n\nStart writing your documentation here.\n`);
|
|
126
|
+
const logoPath = resolve(docsDir, 'logo.svg');
|
|
127
|
+
if (!existsSync(logoPath))
|
|
128
|
+
writeFileSync(logoPath, '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="14" fill="#2C6BD4"/><text x="32" y="43" text-anchor="middle" fill="white" font-family="sans-serif" font-size="36" font-weight="700">D</text></svg>\n');
|
|
129
|
+
console.log('Created dory.json and docs/introduction.mdx. Run `dory dev` to start writing.');
|
|
130
|
+
},
|
|
131
|
+
dev: () => {
|
|
132
|
+
const userRoot = getUserRoot();
|
|
133
|
+
const configPath = resolve(userRoot, 'dory.json');
|
|
134
|
+
if (!existsSync(configPath))
|
|
135
|
+
throw new Error('dory.json not found; run `dory init` first');
|
|
136
|
+
loadProjectEnv(userRoot);
|
|
137
|
+
const workspace = createWorkspace(userRoot);
|
|
138
|
+
const server = spawn(process.execPath, [viteCli(workspace)], {
|
|
139
|
+
cwd: workspace,
|
|
140
|
+
stdio: 'inherit',
|
|
141
|
+
env: { ...process.env, DORY_PROJECT_ROOT: userRoot },
|
|
142
|
+
});
|
|
143
|
+
let timer;
|
|
144
|
+
const sync = () => {
|
|
145
|
+
clearTimeout(timer);
|
|
146
|
+
timer = setTimeout(() => {
|
|
203
147
|
try {
|
|
204
|
-
|
|
205
|
-
copiedCount++;
|
|
148
|
+
stageDocs(userRoot, workspace);
|
|
206
149
|
}
|
|
207
150
|
catch (error) {
|
|
208
|
-
console.
|
|
151
|
+
console.error('Could not sync documentation:', error);
|
|
209
152
|
}
|
|
153
|
+
}, 100);
|
|
154
|
+
};
|
|
155
|
+
const watchers = [watch(userRoot, (_event, file) => { if (file === 'dory.json')
|
|
156
|
+
sync(); })];
|
|
157
|
+
const docsDir = resolve(userRoot, 'docs');
|
|
158
|
+
if (existsSync(docsDir))
|
|
159
|
+
watchers.push(watch(docsDir, { recursive: true }, sync));
|
|
160
|
+
else
|
|
161
|
+
watchers.push(watch(userRoot, { recursive: true }, (_event, file) => {
|
|
162
|
+
if (file && !file.startsWith('dist/') && !file.startsWith('node_modules/'))
|
|
163
|
+
sync();
|
|
164
|
+
}));
|
|
165
|
+
const cleanup = () => {
|
|
166
|
+
clearTimeout(timer);
|
|
167
|
+
watchers.forEach(watcher => watcher.close());
|
|
168
|
+
if (!server.killed)
|
|
169
|
+
server.kill('SIGTERM');
|
|
170
|
+
rmSync(workspace, { recursive: true, force: true });
|
|
171
|
+
};
|
|
172
|
+
server.once('exit', code => { cleanup(); process.exitCode = code || 0; });
|
|
173
|
+
process.once('SIGINT', () => { cleanup(); process.exit(130); });
|
|
174
|
+
process.once('SIGTERM', () => { cleanup(); process.exit(143); });
|
|
175
|
+
},
|
|
176
|
+
check: () => {
|
|
177
|
+
const result = checkProject(getUserRoot());
|
|
178
|
+
console.log(formatCheckResult(result));
|
|
179
|
+
if (result.errors > 0)
|
|
180
|
+
process.exitCode = 1;
|
|
181
|
+
},
|
|
182
|
+
'i18n:init': () => {
|
|
183
|
+
const locale = process.argv[3];
|
|
184
|
+
if (!locale)
|
|
185
|
+
throw new Error('Usage: dory i18n:init <locale>');
|
|
186
|
+
const result = initLocale(getUserRoot(), locale);
|
|
187
|
+
console.log(`Created ${result.templatesCreated} translation template(s) in ${result.directory}.`);
|
|
188
|
+
console.log('Translate each .mdx.template file, then rename it to .mdx to publish.');
|
|
189
|
+
},
|
|
190
|
+
inspect: () => {
|
|
191
|
+
console.log(JSON.stringify(projectInfo(getUserRoot()), null, 2));
|
|
192
|
+
},
|
|
193
|
+
discover: async () => {
|
|
194
|
+
const url = process.argv[3];
|
|
195
|
+
if (!url)
|
|
196
|
+
throw new Error('Usage: dory discover <site-url>');
|
|
197
|
+
console.log(JSON.stringify(await discoverSite(url), null, 2));
|
|
198
|
+
},
|
|
199
|
+
screenshot: async () => {
|
|
200
|
+
const args = process.argv.slice(3);
|
|
201
|
+
const value = (flag) => {
|
|
202
|
+
const index = args.indexOf(flag);
|
|
203
|
+
return index < 0 ? undefined : args[index + 1];
|
|
204
|
+
};
|
|
205
|
+
const width = value('--width');
|
|
206
|
+
const height = value('--height');
|
|
207
|
+
const theme = value('--theme');
|
|
208
|
+
if (theme && theme !== 'light' && theme !== 'dark')
|
|
209
|
+
throw new Error('--theme must be light or dark');
|
|
210
|
+
const result = await screenshot(getUserRoot(), {
|
|
211
|
+
route: value('--route'), output: value('--output'),
|
|
212
|
+
width: width ? Number(width) : undefined,
|
|
213
|
+
height: height ? Number(height) : undefined,
|
|
214
|
+
theme: theme,
|
|
215
|
+
});
|
|
216
|
+
console.log(`Screenshot saved: ${result.output}`);
|
|
217
|
+
},
|
|
218
|
+
mcp: () => {
|
|
219
|
+
const action = process.argv[3];
|
|
220
|
+
if (action === 'serve') {
|
|
221
|
+
const projectIndex = process.argv.indexOf('--project', 4);
|
|
222
|
+
if (projectIndex >= 0)
|
|
223
|
+
process.env.DORY_PROJECT_ROOT = resolve(process.argv[projectIndex + 1]);
|
|
224
|
+
const version = JSON.parse(readFileSync(resolve(getDoryRoot(), 'package.json'), 'utf8')).version;
|
|
225
|
+
return serveMcp(__filename, version);
|
|
226
|
+
}
|
|
227
|
+
if (action === 'config') {
|
|
228
|
+
console.log(JSON.stringify({ mcpServers: { dory: { command: process.execPath, args: [__filename, 'mcp', 'serve'], env: { DORY_PROJECT_ROOT: getUserRoot() } } } }, null, 2));
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
throw new Error('Usage: dory mcp serve | dory mcp config');
|
|
232
|
+
},
|
|
233
|
+
build: () => {
|
|
234
|
+
const userRoot = getUserRoot();
|
|
235
|
+
const configPath = resolve(userRoot, 'dory.json');
|
|
236
|
+
if (!existsSync(configPath))
|
|
237
|
+
throw new Error(`dory.json not found: ${configPath}`);
|
|
238
|
+
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
239
|
+
validateDoryConfig(config);
|
|
240
|
+
const workspace = createWorkspace(userRoot);
|
|
241
|
+
try {
|
|
242
|
+
console.log(`Building ${config.name || 'Documentation'} from an isolated workspace...`);
|
|
243
|
+
const runVite = (...args) => execFileSync(process.execPath, [viteCli(workspace), ...args], { cwd: workspace, stdio: 'inherit', env: process.env });
|
|
244
|
+
runVite('build');
|
|
245
|
+
runVite('build', '--config', 'vite.config.ssr.ts');
|
|
246
|
+
execFileSync(process.execPath, [resolve(workspace, 'scripts', 'render-routes.mjs')], { cwd: workspace, stdio: 'inherit' });
|
|
247
|
+
const distDir = resolve(workspace, 'dist');
|
|
248
|
+
if (!existsSync(resolve(distDir, 'index.html'))) {
|
|
249
|
+
throw new Error('Build did not produce dist/index.html');
|
|
210
250
|
}
|
|
211
|
-
console.log(`✅ Copied ${copiedCount} items`);
|
|
212
|
-
// Verify critical files exist
|
|
213
|
-
const doryJsonPath = resolve(tempDocsDir, 'dory.json');
|
|
214
|
-
if (!existsSync(doryJsonPath)) {
|
|
215
|
-
throw new Error(`dory.json was not copied to docs directory (expected: ${doryJsonPath})`);
|
|
216
|
-
}
|
|
217
|
-
// Step 4: Run the build
|
|
218
|
-
console.log('⚡ Building documentation...');
|
|
219
|
-
try {
|
|
220
|
-
execSync(`${pm.run} build:docs`, {
|
|
221
|
-
stdio: 'inherit',
|
|
222
|
-
cwd: doryRoot,
|
|
223
|
-
// Don't let corepack's strict packageManager pin fail the build for
|
|
224
|
-
// users running a different pnpm version.
|
|
225
|
-
env: { ...process.env, COREPACK_ENABLE_STRICT: '0' }
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
catch (error) {
|
|
229
|
-
console.error('❌ Build failed');
|
|
230
|
-
console.error(' Check the error messages above for details');
|
|
231
|
-
throw error;
|
|
232
|
-
}
|
|
233
|
-
// Step 5: Verify build output
|
|
234
|
-
console.log('🔍 Verifying build output...');
|
|
235
|
-
if (!existsSync(doryDistDir)) {
|
|
236
|
-
throw new Error(`Build completed but dist folder was not created (expected: ${doryDistDir})`);
|
|
237
|
-
}
|
|
238
|
-
const indexHtml = resolve(doryDistDir, 'index.html');
|
|
239
|
-
if (!existsSync(indexHtml)) {
|
|
240
|
-
throw new Error('Build incomplete: index.html not found — the build may have failed silently');
|
|
241
|
-
}
|
|
242
|
-
// Check if dist has content
|
|
243
|
-
const distFiles = readdirSync(doryDistDir);
|
|
244
|
-
if (distFiles.length === 0) {
|
|
245
|
-
throw new Error('Build completed but dist folder is empty');
|
|
246
|
-
}
|
|
247
|
-
// Verify SSR actually rendered content into at least one route
|
|
248
251
|
const hasSsrContent = (dir) => {
|
|
249
252
|
for (const item of readdirSync(dir, { withFileTypes: true })) {
|
|
250
253
|
const itemPath = resolve(dir, item.name);
|
|
251
|
-
if (item.isDirectory())
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
else if (item.name.endsWith('.html')) {
|
|
256
|
-
if (/<div id="app">\s*<\w/.test(readFileSync(itemPath, 'utf8')))
|
|
257
|
-
return true;
|
|
258
|
-
}
|
|
254
|
+
if (item.isDirectory() && hasSsrContent(itemPath))
|
|
255
|
+
return true;
|
|
256
|
+
if (item.name.endsWith('.html') && /<div id="app">\s*<\w/.test(readFileSync(itemPath, 'utf8')))
|
|
257
|
+
return true;
|
|
259
258
|
}
|
|
260
259
|
return false;
|
|
261
260
|
};
|
|
262
|
-
if (!hasSsrContent(
|
|
263
|
-
throw new Error('Build verification failed: no
|
|
264
|
-
}
|
|
265
|
-
console.log('🎉 Build completed successfully!');
|
|
266
|
-
// Step 6: Copy dist to user's directory
|
|
267
|
-
console.log('📦 Copying build output...');
|
|
268
|
-
// Only copy if they're different directories
|
|
269
|
-
if (doryDistDir !== userDistDir) {
|
|
270
|
-
if (existsSync(userDistDir)) {
|
|
271
|
-
rmSync(userDistDir, { recursive: true, force: true });
|
|
272
|
-
}
|
|
273
|
-
cpSync(doryDistDir, userDistDir, { recursive: true, force: true });
|
|
261
|
+
if (!hasSsrContent(distDir)) {
|
|
262
|
+
throw new Error('Build verification failed: no server-rendered route content was found');
|
|
274
263
|
}
|
|
275
|
-
console.log('✨ Documentation ready in dist/');
|
|
276
|
-
// Step 6.5: Build embed files
|
|
277
|
-
console.log('📦 Building embed files...');
|
|
278
264
|
try {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
execSync(`${pm.exec} vite build -c vite.config.embed-loader.ts`, {
|
|
282
|
-
stdio: 'inherit',
|
|
283
|
-
cwd: doryRoot,
|
|
284
|
-
env: { ...process.env, COREPACK_ENABLE_STRICT: '0' }
|
|
285
|
-
});
|
|
286
|
-
// Build embed widget
|
|
287
|
-
console.log(' Building embed widget...');
|
|
288
|
-
execSync(`${pm.exec} vite build -c vite.config.embed-widget.ts`, {
|
|
289
|
-
stdio: 'inherit',
|
|
290
|
-
cwd: doryRoot,
|
|
291
|
-
env: { ...process.env, COREPACK_ENABLE_STRICT: '0' }
|
|
292
|
-
});
|
|
293
|
-
// Copy embed files to user's dist if different
|
|
294
|
-
if (doryDistDir !== userDistDir) {
|
|
295
|
-
const embedFiles = ['embed.js', 'embed-widget.js', 'embed.css'];
|
|
296
|
-
embedFiles.forEach(file => {
|
|
297
|
-
const srcPath = resolve(doryDistDir, file);
|
|
298
|
-
const destPath = resolve(userDistDir, file);
|
|
299
|
-
if (existsSync(srcPath)) {
|
|
300
|
-
cpSync(srcPath, destPath, { force: true });
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
// Copy any chunk files (embed-*.js)
|
|
304
|
-
const files = readdirSync(doryDistDir);
|
|
305
|
-
files.forEach(file => {
|
|
306
|
-
if (file.startsWith('embed-') && file.endsWith('.js')) {
|
|
307
|
-
const srcPath = resolve(doryDistDir, file);
|
|
308
|
-
const destPath = resolve(userDistDir, file);
|
|
309
|
-
cpSync(srcPath, destPath, { force: true });
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
console.log('✨ Embed files built successfully!');
|
|
314
|
-
console.log('');
|
|
315
|
-
console.log(' 📝 Add to your site:');
|
|
316
|
-
console.log(' <script src="https://your-docs.com/embed.js"></script>');
|
|
317
|
-
console.log(' <button onclick="DoryDocs.open()">Help</button>');
|
|
318
|
-
console.log('');
|
|
265
|
+
runVite('build', '-c', 'vite.config.embed-loader.ts');
|
|
266
|
+
runVite('build', '-c', 'vite.config.embed-widget.ts');
|
|
319
267
|
}
|
|
320
268
|
catch (error) {
|
|
321
|
-
console.warn(
|
|
322
|
-
console.warn(' Your documentation site is still available at dist/');
|
|
323
|
-
if (error instanceof Error) {
|
|
324
|
-
console.warn(` Error: ${error.message}`);
|
|
325
|
-
}
|
|
269
|
+
console.warn(`Embed build failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
326
270
|
}
|
|
271
|
+
const userDistDir = resolve(userRoot, 'dist');
|
|
272
|
+
rmSync(userDistDir, { recursive: true, force: true });
|
|
273
|
+
cpSync(distDir, userDistDir, { recursive: true });
|
|
274
|
+
console.log(`Documentation ready in ${userDistDir}`);
|
|
327
275
|
}
|
|
328
276
|
finally {
|
|
329
|
-
|
|
330
|
-
console.log('🧹 Cleaning up...');
|
|
331
|
-
process.removeListener('SIGINT', restoreOnSignal);
|
|
332
|
-
process.removeListener('SIGTERM', restoreOnSignal);
|
|
333
|
-
if (docsExistedBefore && safeBackupDir && existsSync(safeBackupDir)) {
|
|
334
|
-
console.log('📦 Restoring original docs directory...');
|
|
335
|
-
try {
|
|
336
|
-
if (existsSync(tempDocsDir)) {
|
|
337
|
-
rmSync(tempDocsDir, { recursive: true, force: true });
|
|
338
|
-
}
|
|
339
|
-
cpSync(safeBackupDir, tempDocsDir, { recursive: true, force: true });
|
|
340
|
-
// Verify restoration succeeded before deleting backup
|
|
341
|
-
const restoredCount = countFiles(tempDocsDir);
|
|
342
|
-
const backupCount = countFiles(safeBackupDir);
|
|
343
|
-
if (restoredCount === backupCount) {
|
|
344
|
-
rmSync(safeBackupDir, { recursive: true, force: true });
|
|
345
|
-
}
|
|
346
|
-
else {
|
|
347
|
-
console.warn(`⚠️ Restore verification mismatch — backup preserved at: ${safeBackupDir}`);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
catch (error) {
|
|
351
|
-
console.error('❌ Failed to restore docs directory!');
|
|
352
|
-
console.error(` Backup is preserved at: ${safeBackupDir}`);
|
|
353
|
-
console.error(' You can manually restore by running:');
|
|
354
|
-
console.error(` cp -r "${safeBackupDir}" "${tempDocsDir}"`);
|
|
355
|
-
if (error instanceof Error) {
|
|
356
|
-
console.error(` Error: ${error.message}`);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
else if (!docsExistedBefore) {
|
|
361
|
-
try {
|
|
362
|
-
if (existsSync(tempDocsDir)) {
|
|
363
|
-
rmSync(tempDocsDir, { recursive: true, force: true });
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
catch (error) {
|
|
367
|
-
console.warn('⚠️ Could not remove temporary docs directory');
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
// Clean up doryDistDir if it's different from userDistDir
|
|
371
|
-
try {
|
|
372
|
-
if (doryDistDir !== userDistDir && existsSync(doryDistDir)) {
|
|
373
|
-
rmSync(doryDistDir, { recursive: true, force: true });
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
catch (error) {
|
|
377
|
-
console.warn('⚠️ Could not clean up dory dist directory');
|
|
378
|
-
}
|
|
379
|
-
rmSync(lockPath, { force: true });
|
|
380
|
-
console.log('✅ Done!');
|
|
381
|
-
}
|
|
382
|
-
},
|
|
383
|
-
preview: () => {
|
|
384
|
-
console.log('👀 Starting docs preview...');
|
|
385
|
-
const userRoot = getUserRoot();
|
|
386
|
-
const distDir = resolve(userRoot, 'dist');
|
|
387
|
-
const port = parseInt(process.env.PORT || '3000', 10);
|
|
388
|
-
// Validate dist folder exists
|
|
389
|
-
if (!existsSync(distDir)) {
|
|
390
|
-
console.error('❌ dist folder not found');
|
|
391
|
-
console.error(` Looking for: ${distDir}`);
|
|
392
|
-
console.error(' Run "dory build" first to generate the documentation');
|
|
393
|
-
process.exit(1);
|
|
394
|
-
}
|
|
395
|
-
// Check if dist has content
|
|
396
|
-
const distFiles = readdirSync(distDir);
|
|
397
|
-
if (distFiles.length === 0) {
|
|
398
|
-
console.error('❌ dist folder is empty');
|
|
399
|
-
console.error(' Run "dory build" to generate documentation');
|
|
400
|
-
process.exit(1);
|
|
277
|
+
rmSync(workspace, { recursive: true, force: true });
|
|
401
278
|
}
|
|
402
|
-
const serve = sirv(distDir, {
|
|
403
|
-
dev: false,
|
|
404
|
-
single: true,
|
|
405
|
-
etag: true,
|
|
406
|
-
gzip: true,
|
|
407
|
-
brotli: true,
|
|
408
|
-
});
|
|
409
|
-
// Mirror the production layout: a `basePath` build is served under that
|
|
410
|
-
// prefix, so strip it before handing the request to sirv.
|
|
411
|
-
const basePath = normalizeBasePath(readOptionalDoryConfig(resolve(userRoot, 'dory.json'))?.basePath);
|
|
412
|
-
const maxAttempts = 20;
|
|
413
|
-
const tryPort = (currentPort, attempt) => {
|
|
414
|
-
// Fresh server per attempt — reusing one server object stacks listeners
|
|
415
|
-
// and produces duplicate servers on retry.
|
|
416
|
-
const server = createServer((req, res) => {
|
|
417
|
-
if (basePath && req.url) {
|
|
418
|
-
if (req.url === basePath || req.url.startsWith(`${basePath}/`) || req.url.startsWith(`${basePath}?`)) {
|
|
419
|
-
req.url = req.url.slice(basePath.length) || '/';
|
|
420
|
-
}
|
|
421
|
-
else {
|
|
422
|
-
res.statusCode = 302;
|
|
423
|
-
res.setHeader('Location', `${basePath}/`);
|
|
424
|
-
res.end();
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
serve(req, res);
|
|
429
|
-
});
|
|
430
|
-
server.once('error', (err) => {
|
|
431
|
-
if (err.code === 'EADDRINUSE' && attempt + 1 < maxAttempts) {
|
|
432
|
-
console.log(`⚠️ Port ${currentPort} in use, trying ${currentPort + 1}...`);
|
|
433
|
-
tryPort(currentPort + 1, attempt + 1);
|
|
434
|
-
}
|
|
435
|
-
else if (err.code === 'EADDRINUSE') {
|
|
436
|
-
console.error(`❌ No free port found in range ${port}-${currentPort}`);
|
|
437
|
-
process.exit(1);
|
|
438
|
-
}
|
|
439
|
-
else {
|
|
440
|
-
console.error('❌ Failed to start server:', err.message);
|
|
441
|
-
process.exit(1);
|
|
442
|
-
}
|
|
443
|
-
});
|
|
444
|
-
server.once('listening', () => {
|
|
445
|
-
console.log(`🚀 Documentation live at http://localhost:${currentPort}${basePath}`);
|
|
446
|
-
console.log(' Press Ctrl+C to stop the server');
|
|
447
|
-
});
|
|
448
|
-
server.listen(currentPort);
|
|
449
|
-
};
|
|
450
|
-
tryPort(port, 0);
|
|
451
279
|
},
|
|
280
|
+
preview: () => startPreview({ version: JSON.parse(readFileSync(resolve(getDoryRoot(), 'package.json'), 'utf8')).version }),
|
|
452
281
|
'verify:content': async () => {
|
|
453
282
|
const args = process.argv.slice(3);
|
|
454
283
|
let content = '';
|
|
@@ -535,6 +364,20 @@ Usage:
|
|
|
535
364
|
dory <command> [options]
|
|
536
365
|
|
|
537
366
|
Commands:
|
|
367
|
+
init Create dory.json and starter docs in the current directory
|
|
368
|
+
dev Start a live development server for this project
|
|
369
|
+
check Check navigation, links, and content before publishing
|
|
370
|
+
i18n:init <code> Create translation templates for a locale
|
|
371
|
+
reference [page] List or read packaged Dory MDX reference pages
|
|
372
|
+
inspect Show dory.json and local MDX pages as JSON
|
|
373
|
+
discover <url> Find llms.txt and MDX source links from one hosted docs URL
|
|
374
|
+
screenshot Capture a built route as a full-page PNG
|
|
375
|
+
Options: --route <path> --output <file> --theme light|dark
|
|
376
|
+
--width <pixels> --height <pixels>
|
|
377
|
+
mcp serve Run the local MCP server over stdio
|
|
378
|
+
Option: --project <directory>
|
|
379
|
+
mcp config Print an MCP client config for this project
|
|
380
|
+
|
|
538
381
|
build Build your documentation site
|
|
539
382
|
Requirements: dory.json in current directory
|
|
540
383
|
Output: dist/ folder with static site
|
|
@@ -552,6 +395,15 @@ Commands:
|
|
|
552
395
|
help Show this help message
|
|
553
396
|
|
|
554
397
|
Examples:
|
|
398
|
+
dory init
|
|
399
|
+
dory dev
|
|
400
|
+
dory check
|
|
401
|
+
dory i18n:init es
|
|
402
|
+
dory reference components/cards.mdx
|
|
403
|
+
dory inspect
|
|
404
|
+
dory discover https://docs.example.com
|
|
405
|
+
dory build && dory screenshot --route /getting-started --theme dark
|
|
406
|
+
dory mcp config
|
|
555
407
|
dory build
|
|
556
408
|
dory preview
|
|
557
409
|
dory verify:content --content "# Hello World"
|