sourcey 3.6.4 → 3.6.6
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 +38 -5
- package/dist/adapters/shared.js +1 -1
- package/dist/astro/index.d.ts +89 -0
- package/dist/astro/index.d.ts.map +1 -0
- package/dist/astro/index.js +299 -0
- package/dist/cli.js +8 -0
- package/dist/client/reader-controls.js +81 -0
- package/dist/client/reader.d.ts +5 -0
- package/dist/client/reader.d.ts.map +1 -0
- package/dist/client/reader.js +5 -0
- package/dist/client/tabs.js +144 -95
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +1 -1
- package/dist/components/layout/Head.d.ts.map +1 -1
- package/dist/components/layout/Head.js +6 -4
- package/dist/components/layout/Page.d.ts.map +1 -1
- package/dist/components/layout/Page.js +20 -14
- package/dist/components/layout/Reader.d.ts +5 -0
- package/dist/components/layout/Reader.d.ts.map +1 -0
- package/dist/components/layout/Reader.js +64 -0
- package/dist/components/layout/SearchDialog.d.ts +2 -0
- package/dist/components/layout/SearchDialog.d.ts.map +1 -0
- package/dist/components/layout/SearchDialog.js +4 -0
- package/dist/components/openapi/Operation.d.ts.map +1 -1
- package/dist/components/openapi/Operation.js +2 -2
- package/dist/components/openapi/Tags.js +1 -1
- package/dist/config.d.ts +56 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +18 -10
- package/dist/core/doxygen-loader.js +1 -1
- package/dist/core/loader.js +2 -2
- package/dist/core/markdown-loader.d.ts +2 -0
- package/dist/core/markdown-loader.d.ts.map +1 -1
- package/dist/core/markdown-loader.js +1 -0
- package/dist/core/navigation.js +1 -1
- package/dist/core/parser.js +1 -1
- package/dist/dev-server.d.ts.map +1 -1
- package/dist/dev-server.js +3 -4
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -112
- package/dist/init.js +4 -4
- package/dist/renderer/html-builder.d.ts.map +1 -1
- package/dist/renderer/html-builder.js +6 -27
- package/dist/renderer/static-files.d.ts +6 -0
- package/dist/renderer/static-files.d.ts.map +1 -0
- package/dist/renderer/static-files.js +78 -0
- package/dist/site.d.ts +38 -0
- package/dist/site.d.ts.map +1 -0
- package/dist/site.js +159 -0
- package/dist/themes/assets.d.ts +8 -0
- package/dist/themes/assets.d.ts.map +1 -0
- package/dist/themes/assets.js +16 -0
- package/dist/themes/default/sourcey.css +305 -60
- package/dist/themes/reader/sourcey.css +1024 -0
- package/dist/themes/registry.d.ts +20 -0
- package/dist/themes/registry.d.ts.map +1 -0
- package/dist/themes/registry.js +8 -0
- package/dist/utils/server-warnings.d.ts +4 -0
- package/dist/utils/server-warnings.d.ts.map +1 -0
- package/dist/utils/server-warnings.js +41 -0
- package/dist/vite-plugin.d.ts.map +1 -1
- package/dist/vite-plugin.js +1 -53
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ npx sourcey init
|
|
|
22
22
|
## Features
|
|
23
23
|
|
|
24
24
|
- **OpenAPI 2.0, 3.0, 3.1, and 3.2**: full spec coverage including `QUERY` operations, response summaries, hierarchical tags, `deviceAuthorization` OAuth, `querystring` parameters, and `$self`-aware refs for multi-document APIs
|
|
25
|
-
- **API reference from OpenAPI**: endpoints, parameters, request/response schemas, auto-generated code samples
|
|
25
|
+
- **API reference from OpenAPI**: endpoints, parameters, request/response schemas, and auto-generated code samples across 10 supported languages. The default set is cURL, JavaScript, and Python; configure TypeScript, Go, Ruby, Java, PHP, Rust, and C# when you want them
|
|
26
26
|
- **MCP server documentation**: tools, resources, prompts rendered as browsable reference with JSON-RPC, TypeScript, and Python code samples. Color-coded method types, annotation badges, connection config cards
|
|
27
27
|
- **Rich guides**: markdown pages with steps, cards, accordions, syntax-highlighted code blocks, and prose alongside your API reference
|
|
28
28
|
- **MkDocs source import**: point a tab at `mkdocs.yml`; Sourcey reads `docs_dir` and `nav` so existing MkDocs markdown sites can render without hand-copying the sidebar structure
|
|
@@ -32,7 +32,7 @@ npx sourcey init
|
|
|
32
32
|
- **Rust and rustdoc**: native API documentation from nightly rustdoc JSON, with doctests as a first-class extracted view. URL-encoded parametric impl anchors for deep-link parity with doc.rust-lang.org. Snapshot mode lets CI build on stable Rust toolchains. Aggregated doctests index across the workspace
|
|
33
33
|
- **Context exports**: auto-generate llms.txt and llms-full.txt alongside your HTML as alternate views of the same documentation graph
|
|
34
34
|
- **TypeScript config**: `sourcey.config.ts` with `defineConfig()` autocomplete; theme, navbar, CTA buttons, footer
|
|
35
|
-
- **
|
|
35
|
+
- **Themes**: default (sidebar + TOC), minimal (single column), api-first (three column), and reader (editorial specification); colors, fonts, layout dimensions, and custom CSS on top
|
|
36
36
|
- **Vite dev server**: SSR hot reload on every component and CSS change; spec and markdown changes trigger instant refresh
|
|
37
37
|
- **Dark mode**: semantic design tokens, light/dark logo variants, localStorage persistence
|
|
38
38
|
- **Client-side search**: instant fuzzy search across all pages and API operations; Cmd+K
|
|
@@ -92,6 +92,39 @@ sourcey build api.yaml -o dist/
|
|
|
92
92
|
|
|
93
93
|
In CI, the [`sourcey/build-docs`](https://github.com/sourcey/build-docs) GitHub Action runs the build and deploys to GitHub Pages; see [deploying](https://sourcey.com/docs/deploying).
|
|
94
94
|
|
|
95
|
+
### Astro integration
|
|
96
|
+
|
|
97
|
+
Astro sites can mount Sourcey directly instead of running a separate docs CI job. The integration reads the same `sourcey.config.ts`, derives Sourcey's public `siteUrl` and `baseUrl` from Astro's `site`/`base` plus `routeBase`, serves docs in Astro dev, and writes docs into Astro's final build output.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// astro.config.ts
|
|
101
|
+
import { defineConfig } from "astro/config";
|
|
102
|
+
import sourcey from "sourcey/astro";
|
|
103
|
+
|
|
104
|
+
export default defineConfig({
|
|
105
|
+
site: "https://sourcey.com",
|
|
106
|
+
integrations: [
|
|
107
|
+
sourcey({
|
|
108
|
+
config: "./sourcey.config.ts",
|
|
109
|
+
routeBase: "/docs",
|
|
110
|
+
}),
|
|
111
|
+
],
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
You can also share an imported config object when you want one config module to feed both the CLI and Astro:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { defineConfig } from "astro/config";
|
|
119
|
+
import sourcey from "sourcey/astro";
|
|
120
|
+
import docs from "./sourcey.config";
|
|
121
|
+
|
|
122
|
+
export default defineConfig({
|
|
123
|
+
site: "https://sourcey.com",
|
|
124
|
+
integrations: [sourcey({ config: docs, configDir: ".", routeBase: "/docs" })],
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
95
128
|
## Configuration
|
|
96
129
|
|
|
97
130
|
Create `sourcey.config.ts` in your project root:
|
|
@@ -144,11 +177,11 @@ Guides support rich components in standard markdown:
|
|
|
144
177
|
|
|
145
178
|
### Theme
|
|
146
179
|
|
|
147
|
-
All visual configuration lives under `theme`.
|
|
180
|
+
All visual configuration lives under `theme`. `theme.name` selects the complete renderer; colors, fonts, layout dimensions, and custom CSS apply on top:
|
|
148
181
|
|
|
149
182
|
```typescript
|
|
150
183
|
theme: {
|
|
151
|
-
|
|
184
|
+
name: "api-first",
|
|
152
185
|
colors: { primary: "#f59e0b", light: "#fbbf24", dark: "#d97706" },
|
|
153
186
|
fonts: { sans: "'Lexend', sans-serif", mono: "'Fira Code', monospace" },
|
|
154
187
|
layout: { sidebar: "16rem", content: "48rem" },
|
|
@@ -156,7 +189,7 @@ theme: {
|
|
|
156
189
|
}
|
|
157
190
|
```
|
|
158
191
|
|
|
159
|
-
|
|
192
|
+
Themes control the complete layout structure: `"default"` (sidebar + TOC), `"minimal"` (single centered column), `"api-first"` (three-column with persistent code panels), and `"reader"` (editorial specification reader). Shared brand settings apply on top. Theme-specific options are only interpreted by the selected theme.
|
|
160
193
|
|
|
161
194
|
## CLI
|
|
162
195
|
|
package/dist/adapters/shared.js
CHANGED
|
@@ -97,7 +97,7 @@ async function expandGlob(pattern, configDir) {
|
|
|
97
97
|
}
|
|
98
98
|
catch (err) {
|
|
99
99
|
if (err.code === "ENOENT") {
|
|
100
|
-
throw new Error(`Glob directory not found: ${dir}
|
|
100
|
+
throw new Error(`Glob directory not found: ${dir}`, { cause: err });
|
|
101
101
|
}
|
|
102
102
|
throw err;
|
|
103
103
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
import type { PrettyUrls, ResolvedConfig, SourceyConfig } from "../config.js";
|
|
3
|
+
export interface SourceyAstroOptions {
|
|
4
|
+
/**
|
|
5
|
+
* A path to `sourcey.config.ts`, a directory containing it, or an already
|
|
6
|
+
* imported Sourcey config object. Defaults to `sourcey.config.ts` in the
|
|
7
|
+
* Astro project root.
|
|
8
|
+
*/
|
|
9
|
+
config?: string | SourceyConfig | ResolvedConfig;
|
|
10
|
+
/**
|
|
11
|
+
* Directory used to resolve relative paths when `config` is an object.
|
|
12
|
+
* Defaults to the Astro project root.
|
|
13
|
+
*/
|
|
14
|
+
configDir?: string;
|
|
15
|
+
/**
|
|
16
|
+
* URL mount point within the Astro site. Defaults to the Sourcey config
|
|
17
|
+
* `baseUrl` when present, otherwise `/docs`.
|
|
18
|
+
*/
|
|
19
|
+
routeBase?: string;
|
|
20
|
+
/** Override the public Sourcey `baseUrl`. Defaults to Astro `base` + `routeBase`. */
|
|
21
|
+
baseUrl?: string;
|
|
22
|
+
/** Override the public Sourcey `siteUrl`. Defaults to Astro `site`. */
|
|
23
|
+
siteUrl?: string | false;
|
|
24
|
+
/** Override Sourcey pretty URL behavior for the Astro-mounted output. */
|
|
25
|
+
prettyUrls?: PrettyUrls;
|
|
26
|
+
/** Treat changelog warnings as build errors. */
|
|
27
|
+
strictChangelog?: boolean;
|
|
28
|
+
/** Enable or configure Astro dev-server integration. */
|
|
29
|
+
dev?: boolean | {
|
|
30
|
+
enabled?: boolean;
|
|
31
|
+
generateOgImages?: boolean;
|
|
32
|
+
};
|
|
33
|
+
/** Enable or configure Astro build integration. */
|
|
34
|
+
build?: boolean | {
|
|
35
|
+
enabled?: boolean;
|
|
36
|
+
generateOgImages?: boolean;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Allow writing Sourcey at Astro's output root. Off by default because the
|
|
40
|
+
* standalone renderer prunes its output directory before writing.
|
|
41
|
+
*/
|
|
42
|
+
allowRootOutput?: boolean;
|
|
43
|
+
}
|
|
44
|
+
interface AstroIntegration {
|
|
45
|
+
name: string;
|
|
46
|
+
hooks: {
|
|
47
|
+
"astro:config:setup"?: (options: AstroConfigSetupOptions) => void | Promise<void>;
|
|
48
|
+
"astro:build:done"?: (options: AstroBuildDoneOptions) => void | Promise<void>;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
interface AstroConfigSetupOptions {
|
|
52
|
+
command: "dev" | "build" | "preview" | "sync" | string;
|
|
53
|
+
config: AstroResolvedConfig;
|
|
54
|
+
logger: AstroLogger;
|
|
55
|
+
addWatchFile: (path: URL | string) => void;
|
|
56
|
+
createCodegenDir?: () => URL;
|
|
57
|
+
updateConfig: (config: {
|
|
58
|
+
vite?: {
|
|
59
|
+
plugins?: Plugin[];
|
|
60
|
+
};
|
|
61
|
+
}) => void;
|
|
62
|
+
}
|
|
63
|
+
interface AstroBuildDoneOptions {
|
|
64
|
+
dir: URL;
|
|
65
|
+
logger: AstroLogger;
|
|
66
|
+
}
|
|
67
|
+
interface AstroResolvedConfig {
|
|
68
|
+
root: URL;
|
|
69
|
+
site?: string;
|
|
70
|
+
base?: string;
|
|
71
|
+
}
|
|
72
|
+
interface AstroLogger {
|
|
73
|
+
info(message: string): void;
|
|
74
|
+
warn(message: string): void;
|
|
75
|
+
error(message: string): void;
|
|
76
|
+
debug?(message: string): void;
|
|
77
|
+
}
|
|
78
|
+
interface PreparedAstroSourcey {
|
|
79
|
+
config: ResolvedConfig;
|
|
80
|
+
configPath?: string;
|
|
81
|
+
routeBase: string;
|
|
82
|
+
outputRoute: string;
|
|
83
|
+
devOutputDir: string;
|
|
84
|
+
watchPaths: string[];
|
|
85
|
+
}
|
|
86
|
+
export default function sourceyAstro(options?: SourceyAstroOptions): AstroIntegration;
|
|
87
|
+
export declare function prepareAstroSourcey(options: SourceyAstroOptions, astroConfig: AstroResolvedConfig, codegenDir?: URL): Promise<PreparedAstroSourcey>;
|
|
88
|
+
export {};
|
|
89
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/astro/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, extname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { loadConfig, resolveConfigFromRaw } from "../config.js";
|
|
5
|
+
import { contentTypeForPath, outputPathCandidatesForRequest, requestPathMatchesBase, } from "../renderer/static-files.js";
|
|
6
|
+
import { buildSourceySite, collectSourceyWatchPaths, writeSourceySite } from "../site.js";
|
|
7
|
+
import { normalizeBaseUrl, normalizeSiteUrl } from "../site-url.js";
|
|
8
|
+
export default function sourceyAstro(options = {}) {
|
|
9
|
+
let prepared = null;
|
|
10
|
+
const prepareOnce = (astroConfig, codegenDir) => {
|
|
11
|
+
prepared ??= prepareAstroSourcey(options, astroConfig, codegenDir);
|
|
12
|
+
return prepared;
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
name: "sourcey",
|
|
16
|
+
hooks: {
|
|
17
|
+
async "astro:config:setup"({ command, config, logger, addWatchFile, createCodegenDir, updateConfig, }) {
|
|
18
|
+
const codegenDir = createCodegenDir?.() ?? new URL("./.sourcey/", config.root);
|
|
19
|
+
const preparedSourcey = await prepareOnce(config, codegenDir);
|
|
20
|
+
for (const path of preparedSourcey.watchPaths) {
|
|
21
|
+
addWatchFile(path);
|
|
22
|
+
}
|
|
23
|
+
if (command === "dev" && phaseEnabled(options.dev, true)) {
|
|
24
|
+
updateConfig({
|
|
25
|
+
vite: {
|
|
26
|
+
plugins: [
|
|
27
|
+
sourceyAstroDevPlugin({
|
|
28
|
+
prepared: preparedSourcey,
|
|
29
|
+
logger,
|
|
30
|
+
strictChangelog: options.strictChangelog,
|
|
31
|
+
generateOgImages: phaseGenerateOgImages(options.dev, false),
|
|
32
|
+
}),
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
async "astro:build:done"({ dir, logger }) {
|
|
39
|
+
if (!phaseEnabled(options.build, true))
|
|
40
|
+
return;
|
|
41
|
+
const fallbackConfig = {
|
|
42
|
+
root: new URL("./", dir),
|
|
43
|
+
};
|
|
44
|
+
const preparedSourcey = await (prepared ?? prepareOnce(fallbackConfig));
|
|
45
|
+
if (preparedSourcey.outputRoute === "" && !options.allowRootOutput) {
|
|
46
|
+
throw new Error(`sourcey/astro routeBase "/" would prune Astro's full output directory. ` +
|
|
47
|
+
`Use routeBase: "/docs" or set allowRootOutput: true intentionally.`);
|
|
48
|
+
}
|
|
49
|
+
const outputDir = fileURLToPath(new URL(`./${preparedSourcey.outputRoute}`, dir));
|
|
50
|
+
logger.info(`Sourcey: building docs at ${displayRoute(preparedSourcey.routeBase)}`);
|
|
51
|
+
const sourceySite = await buildSourceySite({
|
|
52
|
+
config: preparedSourcey.config,
|
|
53
|
+
outputDir,
|
|
54
|
+
strictChangelog: options.strictChangelog,
|
|
55
|
+
generateOgImages: phaseGenerateOgImages(options.build, true),
|
|
56
|
+
});
|
|
57
|
+
await writeSourceySite(sourceySite);
|
|
58
|
+
await writeAstroRouteAlias({
|
|
59
|
+
outputRoot: fileURLToPath(dir),
|
|
60
|
+
outputDir,
|
|
61
|
+
routeBase: preparedSourcey.routeBase,
|
|
62
|
+
publicBase: preparedSourcey.config.baseUrl,
|
|
63
|
+
});
|
|
64
|
+
logger.info(`Sourcey: wrote ${sourceySite.pageCount} page${sourceySite.pageCount === 1 ? "" : "s"}`);
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export async function prepareAstroSourcey(options, astroConfig, codegenDir = new URL("./.sourcey/", astroConfig.root)) {
|
|
70
|
+
const rootDir = fileURLToPath(astroConfig.root);
|
|
71
|
+
const { config, configPath } = await loadSourceyConfigForAstro(options, rootDir);
|
|
72
|
+
const routeBase = normalizeRouteBase(options.routeBase ?? (config.baseUrl || "/docs"));
|
|
73
|
+
const baseUrl = normalizeBaseUrl(options.baseUrl ?? joinBasePaths(astroConfig.base, routeBase));
|
|
74
|
+
const siteUrl = options.siteUrl === false
|
|
75
|
+
? undefined
|
|
76
|
+
: normalizeSiteUrl(options.siteUrl ?? astroConfig.site ?? config.siteUrl);
|
|
77
|
+
const mergedConfig = {
|
|
78
|
+
...config,
|
|
79
|
+
baseUrl,
|
|
80
|
+
siteUrl,
|
|
81
|
+
prettyUrls: options.prettyUrls ?? config.prettyUrls,
|
|
82
|
+
};
|
|
83
|
+
const outputRoute = routeBase === "/" ? "" : `${routeBase.replace(/^\/+|\/+$/g, "")}/`;
|
|
84
|
+
const devOutputDir = fileURLToPath(new URL("./sourcey-output/", codegenDir));
|
|
85
|
+
const watchPaths = collectSourceyWatchPaths(mergedConfig, configPath);
|
|
86
|
+
return {
|
|
87
|
+
config: mergedConfig,
|
|
88
|
+
configPath,
|
|
89
|
+
routeBase,
|
|
90
|
+
outputRoute,
|
|
91
|
+
devOutputDir,
|
|
92
|
+
watchPaths,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async function loadSourceyConfigForAstro(options, rootDir) {
|
|
96
|
+
if (typeof options.config === "string") {
|
|
97
|
+
const configPath = resolveConfigPath(rootDir, options.config);
|
|
98
|
+
return { config: await loadConfig(configPath), configPath };
|
|
99
|
+
}
|
|
100
|
+
if (!options.config) {
|
|
101
|
+
const configPath = resolve(rootDir, "sourcey.config.ts");
|
|
102
|
+
return { config: await loadConfig(configPath), configPath };
|
|
103
|
+
}
|
|
104
|
+
if (isResolvedConfig(options.config)) {
|
|
105
|
+
return { config: options.config };
|
|
106
|
+
}
|
|
107
|
+
const configDir = resolve(rootDir, options.configDir ?? ".");
|
|
108
|
+
return { config: await resolveConfigFromRaw(options.config, configDir) };
|
|
109
|
+
}
|
|
110
|
+
function sourceyAstroDevPlugin(options) {
|
|
111
|
+
const { prepared, logger } = options;
|
|
112
|
+
let buildPromise = null;
|
|
113
|
+
let built = false;
|
|
114
|
+
async function rebuild() {
|
|
115
|
+
buildPromise ??= (async () => {
|
|
116
|
+
const sourceySite = await buildSourceySite({
|
|
117
|
+
config: prepared.config,
|
|
118
|
+
outputDir: prepared.devOutputDir,
|
|
119
|
+
strictChangelog: options.strictChangelog,
|
|
120
|
+
generateOgImages: options.generateOgImages,
|
|
121
|
+
});
|
|
122
|
+
await writeSourceySite(sourceySite);
|
|
123
|
+
built = true;
|
|
124
|
+
logger.info(`Sourcey: ready at ${displayRoute(prepared.routeBase)} (${sourceySite.pageCount} pages)`);
|
|
125
|
+
})().finally(() => {
|
|
126
|
+
buildPromise = null;
|
|
127
|
+
});
|
|
128
|
+
return buildPromise;
|
|
129
|
+
}
|
|
130
|
+
async function ensureBuilt() {
|
|
131
|
+
if (built)
|
|
132
|
+
return;
|
|
133
|
+
await rebuild();
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
name: "sourcey:astro-dev",
|
|
137
|
+
configureServer(server) {
|
|
138
|
+
for (const path of prepared.watchPaths) {
|
|
139
|
+
server.watcher.add(path);
|
|
140
|
+
}
|
|
141
|
+
server.watcher.on("change", (file) => {
|
|
142
|
+
if (!shouldRebuildForChange(file, prepared.watchPaths))
|
|
143
|
+
return;
|
|
144
|
+
built = false;
|
|
145
|
+
rebuild()
|
|
146
|
+
.then(() => {
|
|
147
|
+
server.ws.send({ type: "full-reload" });
|
|
148
|
+
})
|
|
149
|
+
.catch((error) => {
|
|
150
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
151
|
+
server.ssrFixStacktrace(err);
|
|
152
|
+
logger.error(`Sourcey: ${err.message}`);
|
|
153
|
+
server.ws.send({
|
|
154
|
+
type: "error",
|
|
155
|
+
err: { message: err.message, stack: err.stack ?? "" },
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
server.middlewares.use(async (req, res, next) => {
|
|
160
|
+
const url = req.url ?? "/";
|
|
161
|
+
const pathname = url.split("?", 1)[0] ?? "/";
|
|
162
|
+
if (url.startsWith("/@") || url.startsWith("/__vite") || url.startsWith("/node_modules/")) {
|
|
163
|
+
return next();
|
|
164
|
+
}
|
|
165
|
+
if (!requestPathMatchesBase(pathname, prepared.config.baseUrl)) {
|
|
166
|
+
return next();
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
await ensureBuilt();
|
|
170
|
+
const file = await readGeneratedFile(prepared.devOutputDir, pathname, prepared.config.baseUrl, prepared.config.prettyUrls);
|
|
171
|
+
if (!file)
|
|
172
|
+
return next();
|
|
173
|
+
res.writeHead(200, {
|
|
174
|
+
"Content-Type": contentTypeForPath(file.outputPath),
|
|
175
|
+
"Cache-Control": "no-cache",
|
|
176
|
+
});
|
|
177
|
+
res.end(file.data);
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
181
|
+
server.ssrFixStacktrace(err);
|
|
182
|
+
logger.error(`Sourcey: ${err.message}`);
|
|
183
|
+
server.ws.send({ type: "error", err: { message: err.message, stack: err.stack ?? "" } });
|
|
184
|
+
res.writeHead(500, {
|
|
185
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
186
|
+
"Cache-Control": "no-cache",
|
|
187
|
+
});
|
|
188
|
+
res.end(`<!DOCTYPE html><html><head><script type="module" src="/@vite/client"></script></head><body></body></html>`);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
async function readGeneratedFile(outputDir, pathname, baseUrl, prettyUrls) {
|
|
195
|
+
for (const outputPath of outputPathCandidatesForRequest(pathname, baseUrl, prettyUrls)) {
|
|
196
|
+
const path = resolve(outputDir, outputPath);
|
|
197
|
+
if (!(await exists(path)))
|
|
198
|
+
continue;
|
|
199
|
+
const data = await readFile(path);
|
|
200
|
+
return {
|
|
201
|
+
outputPath,
|
|
202
|
+
data: shouldReadAsText(outputPath) ? data.toString("utf-8") : data,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
async function exists(path) {
|
|
208
|
+
try {
|
|
209
|
+
await access(path);
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function resolveConfigPath(rootDir, config) {
|
|
217
|
+
const candidate = resolve(rootDir, config);
|
|
218
|
+
return candidate.endsWith(".ts") ? candidate : resolve(candidate, "sourcey.config.ts");
|
|
219
|
+
}
|
|
220
|
+
function normalizeRouteBase(value) {
|
|
221
|
+
const normalized = normalizeBaseUrl(value);
|
|
222
|
+
return normalized || "/";
|
|
223
|
+
}
|
|
224
|
+
function joinBasePaths(...paths) {
|
|
225
|
+
const joined = paths
|
|
226
|
+
.map((path) => path?.trim())
|
|
227
|
+
.filter((path) => Boolean(path))
|
|
228
|
+
.flatMap((path) => path.split("/"))
|
|
229
|
+
.map((part) => part.trim())
|
|
230
|
+
.filter(Boolean)
|
|
231
|
+
.join("/");
|
|
232
|
+
return joined ? `/${joined}/` : "";
|
|
233
|
+
}
|
|
234
|
+
function displayRoute(routeBase) {
|
|
235
|
+
return routeBase === "/" ? "/" : routeBase.slice(0, -1);
|
|
236
|
+
}
|
|
237
|
+
async function writeAstroRouteAlias(options) {
|
|
238
|
+
if (options.routeBase === "/")
|
|
239
|
+
return;
|
|
240
|
+
const route = options.routeBase.replace(/^\/+|\/+$/g, "");
|
|
241
|
+
if (!route)
|
|
242
|
+
return;
|
|
243
|
+
const indexPath = resolve(options.outputDir, "index.html");
|
|
244
|
+
if (!(await exists(indexPath)))
|
|
245
|
+
return;
|
|
246
|
+
const aliasPath = resolve(options.outputRoot, `${route}.html`);
|
|
247
|
+
const html = await readFile(indexPath, "utf-8");
|
|
248
|
+
await mkdir(dirname(aliasPath), { recursive: true });
|
|
249
|
+
await writeFile(aliasPath, renderAstroRouteAlias(html, options.publicBase));
|
|
250
|
+
}
|
|
251
|
+
function renderAstroRouteAlias(html, routeBase) {
|
|
252
|
+
const basePath = routeBase.endsWith("/") ? routeBase : `${routeBase}/`;
|
|
253
|
+
return html
|
|
254
|
+
.replace(/\b(href|src)="([^"]+)"/g, (match, attr, value) => {
|
|
255
|
+
if (!shouldPrefixAliasUrl(value))
|
|
256
|
+
return match;
|
|
257
|
+
return `${attr}="${basePath}${value.replace(/^\.\//, "")}"`;
|
|
258
|
+
})
|
|
259
|
+
.replace(/(<meta\s+name="sourcey-search"\s+content=")([^"]+)(")/g, (match, prefix, value, suffix) => {
|
|
260
|
+
if (!shouldPrefixAliasUrl(value))
|
|
261
|
+
return match;
|
|
262
|
+
return `${prefix}${basePath}${value.replace(/^\.\//, "")}${suffix}`;
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
function shouldPrefixAliasUrl(value) {
|
|
266
|
+
return !/^(?:#|\/|[a-z][a-z0-9+.-]*:)/i.test(value);
|
|
267
|
+
}
|
|
268
|
+
function isResolvedConfig(config) {
|
|
269
|
+
return Array.isArray(config.tabs);
|
|
270
|
+
}
|
|
271
|
+
function phaseEnabled(phase, defaultEnabled) {
|
|
272
|
+
if (typeof phase === "boolean")
|
|
273
|
+
return phase;
|
|
274
|
+
return phase?.enabled ?? defaultEnabled;
|
|
275
|
+
}
|
|
276
|
+
function phaseGenerateOgImages(phase, defaultEnabled) {
|
|
277
|
+
return typeof phase === "object" ? (phase.generateOgImages ?? defaultEnabled) : defaultEnabled;
|
|
278
|
+
}
|
|
279
|
+
function shouldRebuildForChange(file, watchPaths) {
|
|
280
|
+
if (watchPaths.includes(file))
|
|
281
|
+
return true;
|
|
282
|
+
const ext = extname(file);
|
|
283
|
+
return ext === ".md" || ext === ".mdx" || ext === ".json" || ext === ".yml" || ext === ".yaml";
|
|
284
|
+
}
|
|
285
|
+
function shouldReadAsText(outputPath) {
|
|
286
|
+
switch (extname(outputPath).toLowerCase()) {
|
|
287
|
+
case ".html":
|
|
288
|
+
case ".css":
|
|
289
|
+
case ".js":
|
|
290
|
+
case ".mjs":
|
|
291
|
+
case ".json":
|
|
292
|
+
case ".svg":
|
|
293
|
+
case ".txt":
|
|
294
|
+
case ".xml":
|
|
295
|
+
return true;
|
|
296
|
+
default:
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ import { runIntrospector, GodocIntrospectorError } from "./core/godoc-introspect
|
|
|
8
8
|
import { GODOC_SCHEMA_VERSION } from "./core/godoc-types.js";
|
|
9
9
|
import { init } from "./init.js";
|
|
10
10
|
import { formatChangelogDiagnostic, formatGodocDiagnostic, formatRustdocDiagnostic, } from "./site-assembly.js";
|
|
11
|
+
import { findPlaceholderServerUrls } from "./utils/server-warnings.js";
|
|
11
12
|
import pkg from "../package.json" with { type: "json" };
|
|
12
13
|
const build = defineCommand({
|
|
13
14
|
meta: {
|
|
@@ -63,6 +64,7 @@ const build = defineCommand({
|
|
|
63
64
|
strictChangelog: args.strictChangelog,
|
|
64
65
|
});
|
|
65
66
|
if (!args.quiet) {
|
|
67
|
+
logQuickBuildServerWarnings(result.spec);
|
|
66
68
|
logChangelogDiagnostics(result.changelogDiagnostics);
|
|
67
69
|
const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
68
70
|
console.log(` Spec: ${result.spec.info.title} v${result.spec.info.version}`);
|
|
@@ -308,6 +310,12 @@ function logRustdocDiagnostics(diagnostics) {
|
|
|
308
310
|
writer(` ${formatRustdocDiagnostic(diagnostic)}`);
|
|
309
311
|
}
|
|
310
312
|
}
|
|
313
|
+
function logQuickBuildServerWarnings(spec) {
|
|
314
|
+
const placeholderUrls = findPlaceholderServerUrls(spec);
|
|
315
|
+
for (const url of placeholderUrls) {
|
|
316
|
+
console.warn(` Warning: quick build is using placeholder server URL "${url}". Generated code samples will target that host. Use sourcey.config.ts or correct the source document before publishing.`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
311
319
|
function parsePort(value) {
|
|
312
320
|
if (!/^\d+$/.test(value)) {
|
|
313
321
|
throw new Error(`Invalid port "${value}". Expected an integer from 1 to 65535.`);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
var root = document.querySelector(".sourcey-reader");
|
|
3
|
+
if (!root) return;
|
|
4
|
+
root.querySelectorAll("[data-reader-enhancement]").forEach(function (el) {
|
|
5
|
+
el.hidden = false;
|
|
6
|
+
});
|
|
7
|
+
root.querySelectorAll("[data-reader-fallback]").forEach(function (el) {
|
|
8
|
+
el.hidden = true;
|
|
9
|
+
});
|
|
10
|
+
var toggle = root.querySelector(".menu-toggle");
|
|
11
|
+
var menu = root.querySelector("#reader-mobile-nav");
|
|
12
|
+
function closeMenu() {
|
|
13
|
+
menu.hidden = true;
|
|
14
|
+
toggle.setAttribute("aria-expanded", "false");
|
|
15
|
+
toggle.setAttribute("aria-label", "Open navigation");
|
|
16
|
+
}
|
|
17
|
+
toggle.addEventListener("click", function () {
|
|
18
|
+
var open = toggle.getAttribute("aria-expanded") !== "true";
|
|
19
|
+
menu.hidden = !open;
|
|
20
|
+
toggle.setAttribute("aria-expanded", String(open));
|
|
21
|
+
toggle.setAttribute("aria-label", open ? "Close navigation" : "Open navigation");
|
|
22
|
+
});
|
|
23
|
+
window.matchMedia("(min-width: 901px)").addEventListener("change", function (event) {
|
|
24
|
+
if (event.matches) closeMenu();
|
|
25
|
+
});
|
|
26
|
+
document.addEventListener("keydown", function (event) {
|
|
27
|
+
if (event.key === "Escape" && !menu.hidden) {
|
|
28
|
+
closeMenu();
|
|
29
|
+
toggle.focus();
|
|
30
|
+
}
|
|
31
|
+
var search = root.querySelector("a.search-link");
|
|
32
|
+
if (
|
|
33
|
+
search &&
|
|
34
|
+
event.key === "/" &&
|
|
35
|
+
!event.metaKey &&
|
|
36
|
+
!event.ctrlKey &&
|
|
37
|
+
!event.altKey &&
|
|
38
|
+
!event.target.closest("input, textarea, select, [contenteditable]")
|
|
39
|
+
) {
|
|
40
|
+
event.preventDefault();
|
|
41
|
+
window.location.href = search.href;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
var select = root.querySelector("#chapter-select");
|
|
45
|
+
select.addEventListener("change", function () {
|
|
46
|
+
window.location.href = select.value;
|
|
47
|
+
});
|
|
48
|
+
var copy = root.querySelector("[data-copy-page]");
|
|
49
|
+
if (copy)
|
|
50
|
+
copy.addEventListener("click", async function () {
|
|
51
|
+
var label = copy.querySelector("span");
|
|
52
|
+
try {
|
|
53
|
+
await navigator.clipboard.writeText(window.location.href);
|
|
54
|
+
label.textContent = "Copied";
|
|
55
|
+
} catch {
|
|
56
|
+
label.textContent = "Copy the address bar URL";
|
|
57
|
+
}
|
|
58
|
+
setTimeout(function () {
|
|
59
|
+
label.textContent = "Copy link";
|
|
60
|
+
}, 2500);
|
|
61
|
+
});
|
|
62
|
+
var links = Array.from(root.querySelectorAll(".docs-toc nav a"));
|
|
63
|
+
if ("IntersectionObserver" in window) {
|
|
64
|
+
var observer = new window.IntersectionObserver(
|
|
65
|
+
function (entries) {
|
|
66
|
+
var active = entries.find(function (entry) {
|
|
67
|
+
return entry.isIntersecting;
|
|
68
|
+
});
|
|
69
|
+
if (!active) return;
|
|
70
|
+
links.forEach(function (link) {
|
|
71
|
+
if (link.hash === "#" + active.target.id) link.setAttribute("aria-current", "location");
|
|
72
|
+
else link.removeAttribute("aria-current");
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
{ rootMargin: "-100px 0px -65% 0px" },
|
|
76
|
+
);
|
|
77
|
+
root.querySelectorAll(".reader-prose h2, .reader-prose h3").forEach(function (heading) {
|
|
78
|
+
observer.observe(heading);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
})();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../../src/client/reader.ts"],"names":[],"mappings":"AACA,OAAO,4BAA4B,CAAC;AACpC,OAAO,WAAW,CAAC;AACnB,OAAO,aAAa,CAAC;AACrB,OAAO,sBAAsB,CAAC"}
|