@ox-content/vite-plugin 2.8.0 → 2.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/dist/github.mjs +299 -2
- package/dist/github.mjs.map +1 -0
- package/dist/index.cjs +122 -1391
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +108 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +108 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +192 -1502
- package/dist/index.mjs.map +1 -1
- package/dist/mermaid.cjs +22 -0
- package/dist/mermaid.cjs.map +1 -1
- package/dist/mermaid.mjs +57 -1
- package/dist/mermaid.mjs.map +1 -1
- package/dist/ogp.mjs +307 -2
- package/dist/ogp.mjs.map +1 -0
- package/dist/tabs.mjs +188 -2
- package/dist/tabs.mjs.map +1 -0
- package/dist/vitepress-cli.cjs +186 -0
- package/dist/vitepress-cli.cjs.map +1 -0
- package/dist/vitepress-cli.d.cts +1 -0
- package/dist/vitepress-cli.d.mts +1 -0
- package/dist/vitepress-cli.mjs +185 -0
- package/dist/vitepress-cli.mjs.map +1 -0
- package/dist/vitepress.cjs +548 -0
- package/dist/vitepress.cjs.map +1 -0
- package/dist/vitepress.mjs +489 -0
- package/dist/vitepress.mjs.map +1 -0
- package/dist/youtube.mjs +117 -2
- package/dist/youtube.mjs.map +1 -0
- package/package.json +9 -2
- package/dist/github2.mjs +0 -291
- package/dist/github2.mjs.map +0 -1
- package/dist/mermaid2.mjs +0 -2
- package/dist/ogp2.mjs +0 -299
- package/dist/ogp2.mjs.map +0 -1
- package/dist/tabs2.mjs +0 -182
- package/dist/tabs2.mjs.map +0 -1
- package/dist/youtube2.mjs +0 -112
- package/dist/youtube2.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,59 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as transformYouTube, t as extractVideoId } from "./
|
|
4
|
-
import { a as transformGitHub, i as prefetchGitHubRepos, n as fetchRepoData, t as collectGitHubRepos } from "./
|
|
5
|
-
import { a as transformOgp, i as prefetchOgpData, n as fetchOgpData, t as collectOgpUrls } from "./
|
|
1
|
+
import { a as importNapiModuleSync, c as __exportAll, d as __toESM, i as importNapiModule, l as __require, o as __commonJSMin, r as transformMermaidStatic, s as __esmMin, t as mermaidClientScript, u as __toCommonJS } from "./mermaid.mjs";
|
|
2
|
+
import { i as transformTabs, n as resetTabGroupCounter, t as generateTabsCSS } from "./tabs.mjs";
|
|
3
|
+
import { n as transformYouTube, t as extractVideoId } from "./youtube.mjs";
|
|
4
|
+
import { a as transformGitHub, i as prefetchGitHubRepos, n as fetchRepoData, t as collectGitHubRepos } from "./github.mjs";
|
|
5
|
+
import { a as transformOgp, i as prefetchOgpData, n as fetchOgpData, t as collectOgpUrls } from "./ogp.mjs";
|
|
6
|
+
import { a as normalizeVitePressFrontmatter, c as mergeThemes, i as generateVitePressMigrationConfig, l as resolveTheme, n as convertVitePressSidebar, o as defaultTheme, r as fromVitePressConfig, s as defineTheme, t as convertVitePressNav, u as themeToNapi } from "./vitepress.mjs";
|
|
6
7
|
import { createRequire } from "node:module";
|
|
7
|
-
import * as path$
|
|
8
|
-
import path from "path";
|
|
8
|
+
import * as path$1 from "path";
|
|
9
9
|
import { unified } from "unified";
|
|
10
10
|
import rehypeParse from "rehype-parse";
|
|
11
11
|
import rehypeStringify from "rehype-stringify";
|
|
12
12
|
import { createHighlighter } from "shiki";
|
|
13
|
-
import * as path
|
|
13
|
+
import * as path from "node:path";
|
|
14
14
|
import { dirname, join } from "node:path";
|
|
15
15
|
import * as fs$2 from "fs";
|
|
16
|
-
import { createHash } from "node:crypto";
|
|
17
16
|
import * as fs$1 from "fs/promises";
|
|
18
17
|
import { glob } from "glob";
|
|
19
18
|
import * as crypto from "crypto";
|
|
20
19
|
import * as fs from "node:fs/promises";
|
|
21
20
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
22
|
-
//#region \0rolldown/runtime.js
|
|
23
|
-
var __create = Object.create;
|
|
24
|
-
var __defProp = Object.defineProperty;
|
|
25
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
26
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
27
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
28
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
29
|
-
var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
30
|
-
var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
31
|
-
var __exportAll = (all, no_symbols) => {
|
|
32
|
-
let target = {};
|
|
33
|
-
for (var name in all) __defProp(target, name, {
|
|
34
|
-
get: all[name],
|
|
35
|
-
enumerable: true
|
|
36
|
-
});
|
|
37
|
-
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
38
|
-
return target;
|
|
39
|
-
};
|
|
40
|
-
var __copyProps = (to, from, except, desc) => {
|
|
41
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
42
|
-
key = keys[i];
|
|
43
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
44
|
-
get: ((k) => from[k]).bind(null, key),
|
|
45
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
return to;
|
|
49
|
-
};
|
|
50
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
51
|
-
value: mod,
|
|
52
|
-
enumerable: true
|
|
53
|
-
}) : target, mod));
|
|
54
|
-
var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
55
|
-
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
56
|
-
//#endregion
|
|
57
21
|
//#region src/environment.ts
|
|
58
22
|
/**
|
|
59
23
|
* Creates the Markdown processing environment configuration.
|
|
@@ -7112,241 +7076,11 @@ if (import.meta.hot) {
|
|
|
7112
7076
|
}
|
|
7113
7077
|
//#endregion
|
|
7114
7078
|
//#region src/nav-generator.ts
|
|
7115
|
-
/**
|
|
7116
|
-
* Navigation Metadata Generator for API Documentation
|
|
7117
|
-
*
|
|
7118
|
-
* This module provides utilities for generating sidebar navigation structures
|
|
7119
|
-
* from extracted documentation. It automatically:
|
|
7120
|
-
*
|
|
7121
|
-
* - **Extracts file information**: Gets display names and file paths
|
|
7122
|
-
* - **Formats names**: Converts technical names to readable titles
|
|
7123
|
-
* - **Generates TypeScript**: Creates importable nav.ts files
|
|
7124
|
-
* - **Maintains hierarchy**: Supports nested navigation structures
|
|
7125
|
-
*
|
|
7126
|
-
* ## Generated Navigation Format
|
|
7127
|
-
*
|
|
7128
|
-
* The generated navigation is TypeScript-based for type safety and IDE support:
|
|
7129
|
-
*
|
|
7130
|
-
* ```typescript
|
|
7131
|
-
* export const apiNav: NavItem[] = [
|
|
7132
|
-
* { title: 'Overview', path: '/api/index' },
|
|
7133
|
-
* { title: 'Transform', path: '/api/transform' },
|
|
7134
|
-
* { title: 'Types', path: '/api/types' },
|
|
7135
|
-
* // ... auto-generated from documentation
|
|
7136
|
-
* ] as const;
|
|
7137
|
-
* ```
|
|
7138
|
-
*
|
|
7139
|
-
* ## Integration
|
|
7140
|
-
*
|
|
7141
|
-
* The generated nav.ts file can be imported directly:
|
|
7142
|
-
*
|
|
7143
|
-
* ```typescript
|
|
7144
|
-
* // In your Vue/React component
|
|
7145
|
-
* import { apiNav } from '../api/nav';
|
|
7146
|
-
*
|
|
7147
|
-
* const apiItems = apiNav.map(item => ({
|
|
7148
|
-
* ...item,
|
|
7149
|
-
* file: () => import(`../api/${item.path.split('/').pop()}.md`)
|
|
7150
|
-
* }));
|
|
7151
|
-
* ```
|
|
7152
|
-
*
|
|
7153
|
-
* @example
|
|
7154
|
-
* ```typescript
|
|
7155
|
-
* import { generateNavMetadata, generateNavCode } from './nav-generator';
|
|
7156
|
-
*
|
|
7157
|
-
* const extracted = [
|
|
7158
|
-
* { file: 'transform.ts', entries: [...] },
|
|
7159
|
-
* { file: 'types.ts', entries: [...] },
|
|
7160
|
-
* ];
|
|
7161
|
-
*
|
|
7162
|
-
* const navItems = generateNavMetadata(extracted);
|
|
7163
|
-
* // => [
|
|
7164
|
-
* // { title: 'Transform', path: '/api/transform' },
|
|
7165
|
-
* // { title: 'Types', path: '/api/types' },
|
|
7166
|
-
* // ]
|
|
7167
|
-
*
|
|
7168
|
-
* const code = generateNavCode(navItems);
|
|
7169
|
-
* // => TypeScript code ready to write to nav.ts
|
|
7170
|
-
* ```
|
|
7171
|
-
*/
|
|
7172
|
-
/**
|
|
7173
|
-
* Generates sidebar navigation metadata from extracted documentation.
|
|
7174
|
-
*
|
|
7175
|
-
* Takes an array of extracted documentation and produces a flat navigation
|
|
7176
|
-
* structure suitable for sidebar menus. Items are:
|
|
7177
|
-
* - Sorted alphabetically by display name
|
|
7178
|
-
* - Formatted with readable titles
|
|
7179
|
-
* - Prefixed with the specified base path
|
|
7180
|
-
*
|
|
7181
|
-
* ## Naming Conventions
|
|
7182
|
-
*
|
|
7183
|
-
* - `transform.ts` → `{ title: 'Transform', path: '/api/transform' }`
|
|
7184
|
-
* - `nav-generator.ts` → `{ title: 'Nav Generator', path: '/api/nav-generator' }`
|
|
7185
|
-
* - `index.ts` or `index-module.ts` → `{ title: 'Overview', path: '/api/index' }`
|
|
7186
|
-
* - `types.ts` → `{ title: 'Types', path: '/api/types' }`
|
|
7187
|
-
*
|
|
7188
|
-
* ## Sorting
|
|
7189
|
-
*
|
|
7190
|
-
* Items are sorted alphabetically by display title for consistent ordering.
|
|
7191
|
-
* Special item 'Overview' sorts naturally with others (O comes after most letters).
|
|
7192
|
-
*
|
|
7193
|
-
* ## Path Generation
|
|
7194
|
-
*
|
|
7195
|
-
* The generated paths are used to import corresponding Markdown files:
|
|
7196
|
-
* - Path `/api/transform` → Import from `../api/transform.md`
|
|
7197
|
-
* - Path `/api/index` → Import from `../api/index.md`
|
|
7198
|
-
*
|
|
7199
|
-
* @param docs - Array of extracted documentation (file + entries)
|
|
7200
|
-
* @param basePath - Base path prefix for navigation URLs (default: '/api')
|
|
7201
|
-
* Use '/api' for main API docs, '/helpers' for utilities, etc.
|
|
7202
|
-
*
|
|
7203
|
-
* @returns Array of navigation items ready to use or export to TypeScript
|
|
7204
|
-
*
|
|
7205
|
-
* @example
|
|
7206
|
-
* ```typescript
|
|
7207
|
-
* const navItems = generateNavMetadata(
|
|
7208
|
-
* [
|
|
7209
|
-
* { file: 'transform.ts', entries: [...] },
|
|
7210
|
-
* { file: 'docs.ts', entries: [...] },
|
|
7211
|
-
* { file: 'types.ts', entries: [...] },
|
|
7212
|
-
* ],
|
|
7213
|
-
* '/api'
|
|
7214
|
-
* );
|
|
7215
|
-
*
|
|
7216
|
-
* // Returns:
|
|
7217
|
-
* // [
|
|
7218
|
-
* // { title: 'Docs', path: '/api/docs' },
|
|
7219
|
-
* // { title: 'Transform', path: '/api/transform' },
|
|
7220
|
-
* // { title: 'Types', path: '/api/types' },
|
|
7221
|
-
* // ]
|
|
7222
|
-
* ```
|
|
7223
|
-
*
|
|
7224
|
-
* @see generateNavCode For converting these items to TypeScript code
|
|
7225
|
-
*/
|
|
7226
7079
|
function generateNavMetadata(docs, basePath = "/api") {
|
|
7227
|
-
return
|
|
7228
|
-
const aName = getDocDisplayName(a.file);
|
|
7229
|
-
const bName = getDocDisplayName(b.file);
|
|
7230
|
-
return aName.localeCompare(bName);
|
|
7231
|
-
}).map((doc) => ({
|
|
7232
|
-
title: getDocDisplayName(doc.file),
|
|
7233
|
-
path: `${basePath}/${getDocFileName(doc.file)}`
|
|
7234
|
-
}));
|
|
7235
|
-
}
|
|
7236
|
-
/**
|
|
7237
|
-
* Gets the human-readable display name for a documentation file.
|
|
7238
|
-
*
|
|
7239
|
-
* Transforms file paths and names into proper title case:
|
|
7240
|
-
* - Extracts base name (e.g., 'transform.ts' → 'transform')
|
|
7241
|
-
* - Converts kebab-case to Title Case (e.g., 'nav-generator' → 'Nav Generator')
|
|
7242
|
-
* - Converts camelCase to Title Case (e.g., 'transformMarkdown' → 'Transform Markdown')
|
|
7243
|
-
* - Handles special cases (index → 'Overview')
|
|
7244
|
-
*
|
|
7245
|
-
* ## Examples
|
|
7246
|
-
*
|
|
7247
|
-
* - `'/path/to/transform.ts'` → `'Transform'`
|
|
7248
|
-
* - `'nav-generator.ts'` → `'Nav Generator'`
|
|
7249
|
-
* - `'index.ts'` → `'Overview'`
|
|
7250
|
-
* - `'index-module.ts'` → `'Overview'`
|
|
7251
|
-
* - `'myFunction.ts'` → `'My Function'` (with camelCase handling)
|
|
7252
|
-
*
|
|
7253
|
-
* @param filePath - Full or relative file path
|
|
7254
|
-
* @returns Formatted display name suitable for UI labels
|
|
7255
|
-
*
|
|
7256
|
-
* @internal
|
|
7257
|
-
*/
|
|
7258
|
-
function getDocDisplayName(filePath) {
|
|
7259
|
-
const fileName = path.basename(filePath, path.extname(filePath));
|
|
7260
|
-
if (fileName === "index" || fileName === "index-module") return "Overview";
|
|
7261
|
-
return fileName.replace(/[-_]([a-z])/g, (_, char) => " " + char.toUpperCase()).replace(/^[a-z]/, (char) => char.toUpperCase());
|
|
7262
|
-
}
|
|
7263
|
-
/**
|
|
7264
|
-
* Gets the file name (without extension) for use in navigation paths.
|
|
7265
|
-
*
|
|
7266
|
-
* This handles filename conflicts that may occur during generation:
|
|
7267
|
-
* - Preserves most names as-is
|
|
7268
|
-
* - Special handling for index files to maintain consistency
|
|
7269
|
-
*
|
|
7270
|
-
* @param filePath - Source file path
|
|
7271
|
-
* @returns File name without extension, ready for URL paths
|
|
7272
|
-
*
|
|
7273
|
-
* @internal
|
|
7274
|
-
*/
|
|
7275
|
-
function getDocFileName(filePath) {
|
|
7276
|
-
const fileName = path.basename(filePath, path.extname(filePath));
|
|
7277
|
-
if (fileName === "index") return "index";
|
|
7278
|
-
return fileName;
|
|
7080
|
+
return importNapiModuleSync().generateDocsNavMetadata(docs.map((doc) => doc.file), basePath);
|
|
7279
7081
|
}
|
|
7280
|
-
/**
|
|
7281
|
-
* Generates TypeScript code for navigation metadata export.
|
|
7282
|
-
*
|
|
7283
|
-
* Creates a complete, self-contained TypeScript file that:
|
|
7284
|
-
* - Defines the NavItem interface
|
|
7285
|
-
* - Exports navigation items as a const
|
|
7286
|
-
* - Uses `as const` for type-safe literal types
|
|
7287
|
-
* - Includes auto-generation notice
|
|
7288
|
-
*
|
|
7289
|
-
* The generated code is production-ready and suitable for direct import
|
|
7290
|
-
* in Vue, React, or vanilla TypeScript applications.
|
|
7291
|
-
*
|
|
7292
|
-
* ## Generated Code Example
|
|
7293
|
-
*
|
|
7294
|
-
* ```typescript
|
|
7295
|
-
* export interface NavItem {
|
|
7296
|
-
* title: string;
|
|
7297
|
-
* path: string;
|
|
7298
|
-
* children?: NavItem[];
|
|
7299
|
-
* }
|
|
7300
|
-
*
|
|
7301
|
-
* export const apiNav: NavItem[] = [
|
|
7302
|
-
* { "title": "Docs", "path": "/api/docs" },
|
|
7303
|
-
* { "title": "Transform", "path": "/api/transform" },
|
|
7304
|
-
* // ...
|
|
7305
|
-
* ] as const;
|
|
7306
|
-
* ```
|
|
7307
|
-
*
|
|
7308
|
-
* ## Features
|
|
7309
|
-
*
|
|
7310
|
-
* - **Type Safety**: Includes NavItem interface definition
|
|
7311
|
-
* - **Readonly**: Uses `as const` to ensure immutability
|
|
7312
|
-
* - **IDE Support**: Full IntelliSense and autocomplete
|
|
7313
|
-
* - **Self-Documenting**: Includes notice that file is auto-generated
|
|
7314
|
-
*
|
|
7315
|
-
* @param navItems - Array of navigation items to export
|
|
7316
|
-
* @param exportName - Name of the exported const (default: 'apiNav')
|
|
7317
|
-
* Use custom names for different navigation sections
|
|
7318
|
-
*
|
|
7319
|
-
* @returns Complete TypeScript source code as string,
|
|
7320
|
-
* ready to write to a .ts file
|
|
7321
|
-
*
|
|
7322
|
-
* @example
|
|
7323
|
-
* ```typescript
|
|
7324
|
-
* const navItems = [
|
|
7325
|
-
* { title: 'Home', path: '/api/index' },
|
|
7326
|
-
* { title: 'Transform', path: '/api/transform' },
|
|
7327
|
-
* ];
|
|
7328
|
-
*
|
|
7329
|
-
* const code = generateNavCode(navItems, 'apiNav');
|
|
7330
|
-
* await fs.promises.writeFile('docs/api/nav.ts', code, 'utf-8');
|
|
7331
|
-
* ```
|
|
7332
|
-
*
|
|
7333
|
-
* @see generateNavMetadata For generating NavItem arrays from extracted docs
|
|
7334
|
-
*/
|
|
7335
7082
|
function generateNavCode(navItems, exportName = "apiNav") {
|
|
7336
|
-
return
|
|
7337
|
-
* Auto-generated API documentation navigation.
|
|
7338
|
-
* This file is automatically generated by the docs plugin.
|
|
7339
|
-
* Do not edit manually.
|
|
7340
|
-
*/
|
|
7341
|
-
|
|
7342
|
-
export interface NavItem {
|
|
7343
|
-
title: string;
|
|
7344
|
-
path: string;
|
|
7345
|
-
children?: NavItem[];
|
|
7346
|
-
}
|
|
7347
|
-
|
|
7348
|
-
export const ${exportName}: NavItem[] = ${JSON.stringify(navItems, null, 2)} as const;
|
|
7349
|
-
`;
|
|
7083
|
+
return importNapiModuleSync().generateDocsNavCode(navItems, exportName);
|
|
7350
7084
|
}
|
|
7351
7085
|
//#endregion
|
|
7352
7086
|
//#region src/docs.ts
|
|
@@ -7673,86 +7407,6 @@ function buildDocsData(docs) {
|
|
|
7673
7407
|
}))
|
|
7674
7408
|
};
|
|
7675
7409
|
}
|
|
7676
|
-
function consumeJSDocType(value) {
|
|
7677
|
-
const trimmed = value.trimStart();
|
|
7678
|
-
if (!trimmed.startsWith("{")) return { rest: trimmed };
|
|
7679
|
-
let depth = 0;
|
|
7680
|
-
for (let index = 0; index < trimmed.length; index++) {
|
|
7681
|
-
const char = trimmed[index];
|
|
7682
|
-
if (char === "{") depth++;
|
|
7683
|
-
else if (char === "}") {
|
|
7684
|
-
depth--;
|
|
7685
|
-
if (depth === 0) return {
|
|
7686
|
-
type: trimmed.slice(1, index).trim() || void 0,
|
|
7687
|
-
rest: trimmed.slice(index + 1).trimStart()
|
|
7688
|
-
};
|
|
7689
|
-
}
|
|
7690
|
-
}
|
|
7691
|
-
return { rest: trimmed };
|
|
7692
|
-
}
|
|
7693
|
-
function cleanTagDescription(value) {
|
|
7694
|
-
return value.trim().replace(/^-\s*/, "").trim();
|
|
7695
|
-
}
|
|
7696
|
-
function splitTagNameAndDescription(value) {
|
|
7697
|
-
const trimmed = value.trimStart();
|
|
7698
|
-
if (trimmed.startsWith("[")) {
|
|
7699
|
-
const closeIndex = trimmed.indexOf("]");
|
|
7700
|
-
if (closeIndex >= 0) return {
|
|
7701
|
-
name: trimmed.slice(0, closeIndex + 1),
|
|
7702
|
-
description: trimmed.slice(closeIndex + 1).trimStart()
|
|
7703
|
-
};
|
|
7704
|
-
}
|
|
7705
|
-
const match = /^(\S+)(?:\s+([\s\S]*))?$/u.exec(trimmed);
|
|
7706
|
-
return {
|
|
7707
|
-
name: match?.[1] ?? "",
|
|
7708
|
-
description: match?.[2] ?? ""
|
|
7709
|
-
};
|
|
7710
|
-
}
|
|
7711
|
-
function parseParamTagValue(value) {
|
|
7712
|
-
const { type, rest } = consumeJSDocType(value);
|
|
7713
|
-
const { name: rawName, description } = splitTagNameAndDescription(rest);
|
|
7714
|
-
let name = rawName.trim();
|
|
7715
|
-
if (!name) return null;
|
|
7716
|
-
let optional = false;
|
|
7717
|
-
let defaultValue;
|
|
7718
|
-
const optionalMatch = /^\[(.*)\]$/u.exec(name);
|
|
7719
|
-
if (optionalMatch) {
|
|
7720
|
-
optional = true;
|
|
7721
|
-
const [innerName, innerDefault] = optionalMatch[1].split(/=(.*)/su);
|
|
7722
|
-
name = innerName.trim();
|
|
7723
|
-
defaultValue = innerDefault?.trim() || void 0;
|
|
7724
|
-
}
|
|
7725
|
-
if (!name) return null;
|
|
7726
|
-
return {
|
|
7727
|
-
name,
|
|
7728
|
-
type: type || "unknown",
|
|
7729
|
-
description: cleanTagDescription(description),
|
|
7730
|
-
optional: optional || void 0,
|
|
7731
|
-
default: defaultValue
|
|
7732
|
-
};
|
|
7733
|
-
}
|
|
7734
|
-
function parseReturnsTagValue(value) {
|
|
7735
|
-
const { type, rest } = consumeJSDocType(value);
|
|
7736
|
-
return {
|
|
7737
|
-
type: type || "unknown",
|
|
7738
|
-
description: cleanTagDescription(rest)
|
|
7739
|
-
};
|
|
7740
|
-
}
|
|
7741
|
-
function normalizeReturnType(value) {
|
|
7742
|
-
const parsed = parseReturnsTagValue(value);
|
|
7743
|
-
return parsed.type === "unknown" ? value : parsed.type;
|
|
7744
|
-
}
|
|
7745
|
-
function mergeParam(params, next) {
|
|
7746
|
-
const existing = params.find((param) => param.name === next.name);
|
|
7747
|
-
if (!existing) {
|
|
7748
|
-
params.push(next);
|
|
7749
|
-
return;
|
|
7750
|
-
}
|
|
7751
|
-
if (next.type && (existing.type === "unknown" || next.type !== "unknown")) existing.type = next.type;
|
|
7752
|
-
if (next.description) existing.description = next.description;
|
|
7753
|
-
if (next.optional) existing.optional = true;
|
|
7754
|
-
if (next.default) existing.default = next.default;
|
|
7755
|
-
}
|
|
7756
7410
|
/**
|
|
7757
7411
|
* Extracts JSDoc documentation from source files in specified directories.
|
|
7758
7412
|
*
|
|
@@ -7819,13 +7473,13 @@ function mergeParam(params, next) {
|
|
|
7819
7473
|
* ```
|
|
7820
7474
|
*/
|
|
7821
7475
|
async function extractDocs(srcDirs, options) {
|
|
7822
|
-
const
|
|
7823
|
-
if (!
|
|
7476
|
+
const extractFileDocEntries = (await importNapiModule()).extractFileDocEntries;
|
|
7477
|
+
if (!extractFileDocEntries) throw new Error("[ox-content] extractFileDocEntries is not available from @ox-content/napi.");
|
|
7824
7478
|
const results = [];
|
|
7825
7479
|
for (const srcDir of srcDirs) {
|
|
7826
7480
|
const files = await findFiles(srcDir, options);
|
|
7827
7481
|
for (const file of files) {
|
|
7828
|
-
const entries =
|
|
7482
|
+
const entries = extractFileDocEntries(file, options.private);
|
|
7829
7483
|
if (entries.length > 0) results.push({
|
|
7830
7484
|
file,
|
|
7831
7485
|
entries
|
|
@@ -7849,7 +7503,7 @@ async function findFiles(dir, options) {
|
|
|
7849
7503
|
return;
|
|
7850
7504
|
}
|
|
7851
7505
|
for (const entry of entries) {
|
|
7852
|
-
const fullPath = path$
|
|
7506
|
+
const fullPath = path$1.join(currentDir, entry.name);
|
|
7853
7507
|
if (entry.isDirectory()) {
|
|
7854
7508
|
if (!isExcluded(fullPath, options.exclude)) await walk(fullPath);
|
|
7855
7509
|
} else if (entry.isFile()) {
|
|
@@ -7876,132 +7530,6 @@ function isExcluded(file, patterns) {
|
|
|
7876
7530
|
return false;
|
|
7877
7531
|
});
|
|
7878
7532
|
}
|
|
7879
|
-
function parseNapiDocItem(item) {
|
|
7880
|
-
const kind = normalizeNapiKind(item.kind);
|
|
7881
|
-
if (!kind) return null;
|
|
7882
|
-
const params = [];
|
|
7883
|
-
const examples = [];
|
|
7884
|
-
const tags = {};
|
|
7885
|
-
let description = "";
|
|
7886
|
-
let returns;
|
|
7887
|
-
let isPrivate = false;
|
|
7888
|
-
const rawLines = (item.jsdoc ?? "").split("\n").map((line) => {
|
|
7889
|
-
const trimmedStart = line.trimStart();
|
|
7890
|
-
const withoutStar = trimmedStart.startsWith("*") ? trimmedStart.slice(1) : trimmedStart;
|
|
7891
|
-
return withoutStar.startsWith(" ") ? withoutStar.slice(1) : withoutStar;
|
|
7892
|
-
});
|
|
7893
|
-
const cleanedLines = rawLines.map((line) => line.trim()).filter(Boolean);
|
|
7894
|
-
let currentExample = "";
|
|
7895
|
-
let inExample = false;
|
|
7896
|
-
let rawLineIndex = 0;
|
|
7897
|
-
for (const lineText of cleanedLines) {
|
|
7898
|
-
while (rawLineIndex < rawLines.length && rawLines[rawLineIndex].trim() !== lineText) rawLineIndex++;
|
|
7899
|
-
const rawLine = rawLineIndex < rawLines.length ? rawLines[rawLineIndex] : lineText;
|
|
7900
|
-
rawLineIndex++;
|
|
7901
|
-
if (lineText.startsWith("@")) {
|
|
7902
|
-
if (inExample) {
|
|
7903
|
-
examples.push(currentExample.trim());
|
|
7904
|
-
currentExample = "";
|
|
7905
|
-
inExample = false;
|
|
7906
|
-
}
|
|
7907
|
-
const tagMatch = /^@(\S+)\s*([\s\S]*)$/u.exec(lineText);
|
|
7908
|
-
if (tagMatch) {
|
|
7909
|
-
const [, tagName, tagValue = ""] = tagMatch;
|
|
7910
|
-
switch (tagName) {
|
|
7911
|
-
case "param":
|
|
7912
|
-
case "arg":
|
|
7913
|
-
case "argument": {
|
|
7914
|
-
const param = parseParamTagValue(tagValue);
|
|
7915
|
-
if (param) mergeParam(params, param);
|
|
7916
|
-
break;
|
|
7917
|
-
}
|
|
7918
|
-
case "returns":
|
|
7919
|
-
case "return":
|
|
7920
|
-
returns = parseReturnsTagValue(tagValue);
|
|
7921
|
-
break;
|
|
7922
|
-
case "example":
|
|
7923
|
-
inExample = true;
|
|
7924
|
-
currentExample = tagValue.trim() ? `${tagValue.trim()}\n` : "";
|
|
7925
|
-
break;
|
|
7926
|
-
case "private":
|
|
7927
|
-
isPrivate = true;
|
|
7928
|
-
break;
|
|
7929
|
-
default: tags[tagName] = tagValue.trim();
|
|
7930
|
-
}
|
|
7931
|
-
}
|
|
7932
|
-
} else if (inExample) currentExample += rawLine + "\n";
|
|
7933
|
-
else if (!description) description = lineText;
|
|
7934
|
-
else description += "\n" + lineText;
|
|
7935
|
-
}
|
|
7936
|
-
if (inExample && currentExample) examples.push(currentExample.trim());
|
|
7937
|
-
for (const param of item.params) {
|
|
7938
|
-
if (params.length > 0 && param.name === "param" && !param.typeAnnotation && !param.description && !param.defaultValue) continue;
|
|
7939
|
-
mergeParam(params, {
|
|
7940
|
-
name: param.name,
|
|
7941
|
-
type: param.typeAnnotation ?? "unknown",
|
|
7942
|
-
description: param.description ?? "",
|
|
7943
|
-
optional: param.optional || void 0,
|
|
7944
|
-
default: param.defaultValue
|
|
7945
|
-
});
|
|
7946
|
-
}
|
|
7947
|
-
if (!returns && item.returnType) returns = {
|
|
7948
|
-
type: normalizeReturnType(item.returnType),
|
|
7949
|
-
description: ""
|
|
7950
|
-
};
|
|
7951
|
-
else if (returns && item.returnType) returns.type = normalizeReturnType(item.returnType);
|
|
7952
|
-
if (!description) description = item.doc ?? "";
|
|
7953
|
-
for (const tag of item.tags) {
|
|
7954
|
-
if (tag.tag === "param" || tag.tag === "arg" || tag.tag === "argument" || tag.tag === "returns" || tag.tag === "return") {
|
|
7955
|
-
if (tag.tag === "param" || tag.tag === "arg" || tag.tag === "argument") {
|
|
7956
|
-
const param = parseParamTagValue(tag.value);
|
|
7957
|
-
if (param) mergeParam(params, param);
|
|
7958
|
-
} else {
|
|
7959
|
-
const parsedReturns = parseReturnsTagValue(tag.value);
|
|
7960
|
-
if (!returns) returns = parsedReturns;
|
|
7961
|
-
else {
|
|
7962
|
-
returns.type = returns.type === "unknown" ? parsedReturns.type : returns.type;
|
|
7963
|
-
returns.description ||= parsedReturns.description;
|
|
7964
|
-
}
|
|
7965
|
-
}
|
|
7966
|
-
continue;
|
|
7967
|
-
}
|
|
7968
|
-
if (tag.tag === "example") {
|
|
7969
|
-
if (tag.value && !examples.includes(tag.value)) examples.push(tag.value);
|
|
7970
|
-
continue;
|
|
7971
|
-
}
|
|
7972
|
-
if (tag.tag === "private") {
|
|
7973
|
-
isPrivate = true;
|
|
7974
|
-
continue;
|
|
7975
|
-
}
|
|
7976
|
-
if (!tags[tag.tag]) tags[tag.tag] = tag.value;
|
|
7977
|
-
}
|
|
7978
|
-
return {
|
|
7979
|
-
name: item.name,
|
|
7980
|
-
kind,
|
|
7981
|
-
description,
|
|
7982
|
-
params: params.length > 0 ? params : void 0,
|
|
7983
|
-
returns,
|
|
7984
|
-
examples: examples.length > 0 ? examples : void 0,
|
|
7985
|
-
tags: Object.keys(tags).length > 0 ? tags : void 0,
|
|
7986
|
-
private: isPrivate,
|
|
7987
|
-
file: item.sourcePath,
|
|
7988
|
-
line: item.line,
|
|
7989
|
-
endLine: item.endLine,
|
|
7990
|
-
signature: item.signature
|
|
7991
|
-
};
|
|
7992
|
-
}
|
|
7993
|
-
function normalizeNapiKind(kind) {
|
|
7994
|
-
switch (kind) {
|
|
7995
|
-
case "function":
|
|
7996
|
-
case "class":
|
|
7997
|
-
case "interface":
|
|
7998
|
-
case "type":
|
|
7999
|
-
case "variable":
|
|
8000
|
-
case "module": return kind;
|
|
8001
|
-
case "enum": return "type";
|
|
8002
|
-
default: return null;
|
|
8003
|
-
}
|
|
8004
|
-
}
|
|
8005
7533
|
/**
|
|
8006
7534
|
* Generates Markdown documentation from extracted docs.
|
|
8007
7535
|
*/
|
|
@@ -8012,7 +7540,7 @@ function generateMarkdown(docs, options) {
|
|
|
8012
7540
|
if (options.groupBy === "file") {
|
|
8013
7541
|
const docToFile = /* @__PURE__ */ new Map();
|
|
8014
7542
|
for (const doc of sortedDocs) {
|
|
8015
|
-
let fileName = path$
|
|
7543
|
+
let fileName = path$1.basename(doc.file, path$1.extname(doc.file));
|
|
8016
7544
|
if (fileName === "index") fileName = "index-module";
|
|
8017
7545
|
docToFile.set(doc, fileName);
|
|
8018
7546
|
const markdown = generateFileMarkdown(doc, options, fileName, symbolMap);
|
|
@@ -8045,10 +7573,10 @@ function sortExtractedDocs(docs) {
|
|
|
8045
7573
|
return [...docs].map((doc) => ({
|
|
8046
7574
|
...doc,
|
|
8047
7575
|
entries: [...doc.entries].sort(compareEntriesByName)
|
|
8048
|
-
})).sort((a, b) => compareStrings(path$
|
|
7576
|
+
})).sort((a, b) => compareStrings(path$1.basename(a.file), path$1.basename(b.file)));
|
|
8049
7577
|
}
|
|
8050
7578
|
function generateFileMarkdown(doc, options, currentFileName, symbolMap) {
|
|
8051
|
-
let md = `# ${path$
|
|
7579
|
+
let md = `# ${path$1.basename(doc.file)}\n\n`;
|
|
8052
7580
|
if (options.githubUrl) {
|
|
8053
7581
|
const sourceLink = generateSourceLink(doc.file, options.githubUrl);
|
|
8054
7582
|
if (sourceLink) md += sourceLink + "\n\n";
|
|
@@ -8200,7 +7728,7 @@ function generateIndex(docs, docToFile) {
|
|
|
8200
7728
|
md += "## Modules\n\n";
|
|
8201
7729
|
if (docs.length > 1) md += renderDetailsControlsHtml(".ox-api-module") + "\n\n";
|
|
8202
7730
|
for (const doc of docs) {
|
|
8203
|
-
const displayName = path$
|
|
7731
|
+
const displayName = path$1.basename(doc.file, path$1.extname(doc.file));
|
|
8204
7732
|
let fileName = displayName;
|
|
8205
7733
|
if (docToFile && docToFile.has(doc)) fileName = docToFile.get(doc);
|
|
8206
7734
|
else if (fileName === "index") fileName = "index-module";
|
|
@@ -8283,7 +7811,7 @@ function convertSymbolLinks(text, currentFileName, symbolMap) {
|
|
|
8283
7811
|
function buildSymbolMap(docs) {
|
|
8284
7812
|
const map = /* @__PURE__ */ new Map();
|
|
8285
7813
|
for (const doc of docs) {
|
|
8286
|
-
let fileName = path$
|
|
7814
|
+
let fileName = path$1.basename(doc.file, path$1.extname(doc.file));
|
|
8287
7815
|
if (fileName === "index") fileName = "index-module";
|
|
8288
7816
|
for (const entry of doc.entries) map.set(entry.name, {
|
|
8289
7817
|
name: entry.name,
|
|
@@ -8301,7 +7829,7 @@ async function writeDocs(docs, outDir, extractedDocs, options) {
|
|
|
8301
7829
|
const generatedFiles = new Set(Object.keys(docs));
|
|
8302
7830
|
if (extractedDocs && options?.generateNav && options.groupBy === "file") generatedFiles.add("nav.ts");
|
|
8303
7831
|
if (extractedDocs) generatedFiles.add(DOCS_DATA_FILE);
|
|
8304
|
-
const manifestPath = path$
|
|
7832
|
+
const manifestPath = path$1.join(outDir, DOCS_MANIFEST_FILE);
|
|
8305
7833
|
let previousFiles = [];
|
|
8306
7834
|
try {
|
|
8307
7835
|
previousFiles = JSON.parse(await fs$2.promises.readFile(manifestPath, "utf-8"));
|
|
@@ -8310,18 +7838,18 @@ async function writeDocs(docs, outDir, extractedDocs, options) {
|
|
|
8310
7838
|
}
|
|
8311
7839
|
for (const staleFile of previousFiles) {
|
|
8312
7840
|
if (generatedFiles.has(staleFile)) continue;
|
|
8313
|
-
await fs$2.promises.rm(path$
|
|
7841
|
+
await fs$2.promises.rm(path$1.join(outDir, staleFile), { force: true });
|
|
8314
7842
|
}
|
|
8315
7843
|
for (const [fileName, content] of Object.entries(docs)) {
|
|
8316
|
-
const filePath = path$
|
|
7844
|
+
const filePath = path$1.join(outDir, fileName);
|
|
8317
7845
|
await fs$2.promises.writeFile(filePath, content, "utf-8");
|
|
8318
7846
|
}
|
|
8319
7847
|
if (extractedDocs && options?.generateNav && options.groupBy === "file") {
|
|
8320
7848
|
const navCode = generateNavCode(generateNavMetadata(extractedDocs, "/api"), "apiNav");
|
|
8321
|
-
const navFilePath = path$
|
|
7849
|
+
const navFilePath = path$1.join(outDir, "nav.ts");
|
|
8322
7850
|
await fs$2.promises.writeFile(navFilePath, navCode, "utf-8");
|
|
8323
7851
|
}
|
|
8324
|
-
if (extractedDocs) await fs$2.promises.writeFile(path$
|
|
7852
|
+
if (extractedDocs) await fs$2.promises.writeFile(path$1.join(outDir, DOCS_DATA_FILE), JSON.stringify(buildDocsData(extractedDocs), null, 2), "utf-8");
|
|
8325
7853
|
await fs$2.promises.writeFile(manifestPath, JSON.stringify([...generatedFiles].sort(), null, 2), "utf-8");
|
|
8326
7854
|
}
|
|
8327
7855
|
/**
|
|
@@ -8407,10 +7935,10 @@ async function renderHtmlToPng(page, html, width, height, publicDir) {
|
|
|
8407
7935
|
await route.continue();
|
|
8408
7936
|
return;
|
|
8409
7937
|
}
|
|
8410
|
-
const filePath = path$
|
|
7938
|
+
const filePath = path$1.join(publicDir, url.pathname);
|
|
8411
7939
|
try {
|
|
8412
7940
|
const body = await fs.readFile(filePath);
|
|
8413
|
-
const ext = path$
|
|
7941
|
+
const ext = path$1.extname(filePath).toLowerCase();
|
|
8414
7942
|
await route.fulfill({
|
|
8415
7943
|
body,
|
|
8416
7944
|
contentType: {
|
|
@@ -8595,7 +8123,7 @@ function computeCacheKey(templateSource, props, width, height) {
|
|
|
8595
8123
|
* Returns the cached file path if found, null otherwise.
|
|
8596
8124
|
*/
|
|
8597
8125
|
async function getCached(cacheDir, key) {
|
|
8598
|
-
const filePath = path$
|
|
8126
|
+
const filePath = path$1.join(cacheDir, `${key}.png`);
|
|
8599
8127
|
try {
|
|
8600
8128
|
return await fs$1.readFile(filePath);
|
|
8601
8129
|
} catch {
|
|
@@ -8607,7 +8135,7 @@ async function getCached(cacheDir, key) {
|
|
|
8607
8135
|
*/
|
|
8608
8136
|
async function writeCache(cacheDir, key, png) {
|
|
8609
8137
|
await fs$1.mkdir(cacheDir, { recursive: true });
|
|
8610
|
-
const filePath = path$
|
|
8138
|
+
const filePath = path$1.join(cacheDir, `${key}.png`);
|
|
8611
8139
|
await fs$1.writeFile(filePath, png);
|
|
8612
8140
|
}
|
|
8613
8141
|
//#endregion
|
|
@@ -8698,14 +8226,14 @@ function resolveOgImageOptions(options) {
|
|
|
8698
8226
|
*/
|
|
8699
8227
|
async function resolveTemplate(options, root) {
|
|
8700
8228
|
if (!options.template) return getDefaultTemplate();
|
|
8701
|
-
const templatePath = path$
|
|
8229
|
+
const templatePath = path$1.resolve(root, options.template);
|
|
8702
8230
|
const fs = await import("fs/promises");
|
|
8703
8231
|
try {
|
|
8704
8232
|
await fs.access(templatePath);
|
|
8705
8233
|
} catch {
|
|
8706
8234
|
throw new Error(`[ox-content:og-image] Template file not found: ${templatePath}`);
|
|
8707
8235
|
}
|
|
8708
|
-
switch (path$
|
|
8236
|
+
switch (path$1.extname(templatePath).toLowerCase()) {
|
|
8709
8237
|
case ".vue": return resolveVueTemplate(templatePath, options, root);
|
|
8710
8238
|
case ".svelte": return resolveSvelteTemplate(templatePath, root);
|
|
8711
8239
|
case ".tsx":
|
|
@@ -8719,9 +8247,9 @@ async function resolveTemplate(options, root) {
|
|
|
8719
8247
|
async function resolveTsTemplate(templatePath, options, root) {
|
|
8720
8248
|
const fs = await import("fs/promises");
|
|
8721
8249
|
const { rolldown } = await import("rolldown");
|
|
8722
|
-
const cacheDir = path$
|
|
8250
|
+
const cacheDir = path$1.join(root, ".cache", "og-images");
|
|
8723
8251
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
8724
|
-
const outfile = path$
|
|
8252
|
+
const outfile = path$1.join(cacheDir, "_template.mjs");
|
|
8725
8253
|
const bundle = await rolldown({
|
|
8726
8254
|
input: templatePath,
|
|
8727
8255
|
platform: "node"
|
|
@@ -8744,9 +8272,9 @@ async function resolveTsTemplate(templatePath, options, root) {
|
|
|
8744
8272
|
async function resolveVueTemplate(templatePath, options, root) {
|
|
8745
8273
|
const fs = await import("fs/promises");
|
|
8746
8274
|
const { rolldown } = await import("rolldown");
|
|
8747
|
-
const cacheDir = path$
|
|
8275
|
+
const cacheDir = path$1.join(root, ".cache", "og-images");
|
|
8748
8276
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
8749
|
-
const outfile = path$
|
|
8277
|
+
const outfile = path$1.join(cacheDir, "_template_vue.mjs");
|
|
8750
8278
|
const bundle = await rolldown({
|
|
8751
8279
|
input: templatePath,
|
|
8752
8280
|
platform: "node",
|
|
@@ -8842,9 +8370,9 @@ async function getVizejsPlugin() {
|
|
|
8842
8370
|
async function resolveSvelteTemplate(templatePath, root) {
|
|
8843
8371
|
const fs = await import("fs/promises");
|
|
8844
8372
|
const { rolldown } = await import("rolldown");
|
|
8845
|
-
const cacheDir = path$
|
|
8373
|
+
const cacheDir = path$1.join(root, ".cache", "og-images");
|
|
8846
8374
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
8847
|
-
const outfile = path$
|
|
8375
|
+
const outfile = path$1.join(cacheDir, "_template_svelte.mjs");
|
|
8848
8376
|
const bundle = await rolldown({
|
|
8849
8377
|
input: templatePath,
|
|
8850
8378
|
platform: "node",
|
|
@@ -8900,9 +8428,9 @@ function createSvelteCompilerPlugin() {
|
|
|
8900
8428
|
async function resolveReactTemplate(templatePath, root) {
|
|
8901
8429
|
const fs = await import("fs/promises");
|
|
8902
8430
|
const { rolldown } = await import("rolldown");
|
|
8903
|
-
const cacheDir = path$
|
|
8431
|
+
const cacheDir = path$1.join(root, ".cache", "og-images");
|
|
8904
8432
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
8905
|
-
const outfile = path$
|
|
8433
|
+
const outfile = path$1.join(cacheDir, "_template_react.mjs");
|
|
8906
8434
|
const bundle = await rolldown({
|
|
8907
8435
|
input: templatePath,
|
|
8908
8436
|
platform: "node",
|
|
@@ -8952,7 +8480,7 @@ async function resolveReactTemplate(templatePath, root) {
|
|
|
8952
8480
|
async function computeTemplateSource(options, root) {
|
|
8953
8481
|
if (!options.template) return "__default__";
|
|
8954
8482
|
const fs = await import("fs/promises");
|
|
8955
|
-
const templatePath = path$
|
|
8483
|
+
const templatePath = path$1.resolve(root, options.template);
|
|
8956
8484
|
const content = await fs.readFile(templatePath, "utf-8");
|
|
8957
8485
|
return crypto.createHash("sha256").update(content).digest("hex");
|
|
8958
8486
|
}
|
|
@@ -8970,7 +8498,7 @@ async function generateOgImages(pages, options, root) {
|
|
|
8970
8498
|
if (pages.length === 0) return [];
|
|
8971
8499
|
const templateFn = await resolveTemplate(options, root);
|
|
8972
8500
|
const templateSource = await computeTemplateSource(options, root);
|
|
8973
|
-
const cacheDir = path$
|
|
8501
|
+
const cacheDir = path$1.join(root, ".cache", "og-images");
|
|
8974
8502
|
if (options.cache) {
|
|
8975
8503
|
const allCached = await tryServeAllFromCache(pages, templateSource, options, cacheDir);
|
|
8976
8504
|
if (allCached) return allCached;
|
|
@@ -8982,7 +8510,7 @@ async function generateOgImages(pages, options, root) {
|
|
|
8982
8510
|
error: "Chromium not available"
|
|
8983
8511
|
}));
|
|
8984
8512
|
const results = [];
|
|
8985
|
-
const publicDir = path$
|
|
8513
|
+
const publicDir = path$1.join(root, "public");
|
|
8986
8514
|
const concurrency = Math.max(1, options.concurrency);
|
|
8987
8515
|
for (let i = 0; i < pages.length; i += concurrency) {
|
|
8988
8516
|
const batch = pages.slice(i, i + concurrency);
|
|
@@ -9006,7 +8534,7 @@ async function tryServeAllFromCache(pages, templateSource, options, cacheDir) {
|
|
|
9006
8534
|
for (const entry of pages) {
|
|
9007
8535
|
const cached = await getCached(cacheDir, computeCacheKey(templateSource, entry.props, options.width, options.height));
|
|
9008
8536
|
if (!cached) return null;
|
|
9009
|
-
await fs.mkdir(path$
|
|
8537
|
+
await fs.mkdir(path$1.dirname(entry.outputPath), { recursive: true });
|
|
9010
8538
|
await fs.writeFile(entry.outputPath, cached);
|
|
9011
8539
|
results.push({
|
|
9012
8540
|
outputPath: entry.outputPath,
|
|
@@ -9024,7 +8552,7 @@ async function renderSinglePage(entry, templateFn, templateSource, options, cach
|
|
|
9024
8552
|
if (options.cache) {
|
|
9025
8553
|
const cached = await getCached(cacheDir, computeCacheKey(templateSource, entry.props, options.width, options.height));
|
|
9026
8554
|
if (cached) {
|
|
9027
|
-
await fs.mkdir(path$
|
|
8555
|
+
await fs.mkdir(path$1.dirname(entry.outputPath), { recursive: true });
|
|
9028
8556
|
await fs.writeFile(entry.outputPath, cached);
|
|
9029
8557
|
return {
|
|
9030
8558
|
outputPath: entry.outputPath,
|
|
@@ -9034,7 +8562,7 @@ async function renderSinglePage(entry, templateFn, templateSource, options, cach
|
|
|
9034
8562
|
}
|
|
9035
8563
|
const html = await templateFn(entry.props);
|
|
9036
8564
|
const png = await session.renderPage(html, options.width, options.height, publicDir);
|
|
9037
|
-
await fs.mkdir(path$
|
|
8565
|
+
await fs.mkdir(path$1.dirname(entry.outputPath), { recursive: true });
|
|
9038
8566
|
await fs.writeFile(entry.outputPath, png);
|
|
9039
8567
|
if (options.cache) await writeCache(cacheDir, computeCacheKey(templateSource, entry.props, options.width, options.height), png);
|
|
9040
8568
|
return {
|
|
@@ -9058,23 +8586,23 @@ async function transformAllPlugins(html, options = {}) {
|
|
|
9058
8586
|
const { tabs = true, youtube = true, github = true, ogp = true, mermaid = true, githubToken } = options;
|
|
9059
8587
|
let result = html;
|
|
9060
8588
|
if (tabs) {
|
|
9061
|
-
const { transformTabs } = await import("./tabs.mjs");
|
|
8589
|
+
const { transformTabs } = await import("./tabs.mjs").then((n) => n.r);
|
|
9062
8590
|
result = await transformTabs(result);
|
|
9063
8591
|
}
|
|
9064
8592
|
if (youtube) {
|
|
9065
|
-
const { transformYouTube } = await import("./youtube.mjs");
|
|
8593
|
+
const { transformYouTube } = await import("./youtube.mjs").then((n) => n.r);
|
|
9066
8594
|
result = await transformYouTube(result);
|
|
9067
8595
|
}
|
|
9068
8596
|
if (github) {
|
|
9069
|
-
const { transformGitHub } = await import("./github.mjs");
|
|
8597
|
+
const { transformGitHub } = await import("./github.mjs").then((n) => n.r);
|
|
9070
8598
|
result = await transformGitHub(result, void 0, { token: githubToken });
|
|
9071
8599
|
}
|
|
9072
8600
|
if (ogp) {
|
|
9073
|
-
const { transformOgp } = await import("./ogp.mjs");
|
|
8601
|
+
const { transformOgp } = await import("./ogp.mjs").then((n) => n.r);
|
|
9074
8602
|
result = await transformOgp(result);
|
|
9075
8603
|
}
|
|
9076
8604
|
if (mermaid) {
|
|
9077
|
-
const { transformMermaidStatic } = await import("./
|
|
8605
|
+
const { transformMermaidStatic } = await import("./mermaid.mjs").then((n) => n.n);
|
|
9078
8606
|
result = await transformMermaidStatic(result);
|
|
9079
8607
|
}
|
|
9080
8608
|
return result;
|
|
@@ -9271,213 +8799,6 @@ initIslands((el, props) => {
|
|
|
9271
8799
|
`;
|
|
9272
8800
|
}
|
|
9273
8801
|
//#endregion
|
|
9274
|
-
//#region src/theme.ts
|
|
9275
|
-
/**
|
|
9276
|
-
* Default theme configuration.
|
|
9277
|
-
* Based on the current ox-content SSG styles.
|
|
9278
|
-
*/
|
|
9279
|
-
const defaultTheme = {
|
|
9280
|
-
name: "default",
|
|
9281
|
-
colors: {
|
|
9282
|
-
primary: "#4f6fae",
|
|
9283
|
-
primaryHover: "#425f96",
|
|
9284
|
-
background: "#ffffff",
|
|
9285
|
-
backgroundAlt: "#f5f7fb",
|
|
9286
|
-
text: "#131a30",
|
|
9287
|
-
textMuted: "#4f607b",
|
|
9288
|
-
border: "#d2dbea",
|
|
9289
|
-
codeBackground: "#101a31",
|
|
9290
|
-
codeText: "#edf3ff"
|
|
9291
|
-
},
|
|
9292
|
-
darkColors: {
|
|
9293
|
-
primary: "#86a4da",
|
|
9294
|
-
primaryHover: "#a3bbe8",
|
|
9295
|
-
background: "#060816",
|
|
9296
|
-
backgroundAlt: "#0d1528",
|
|
9297
|
-
text: "#ebf2ff",
|
|
9298
|
-
textMuted: "#8ea0bf",
|
|
9299
|
-
border: "#223252",
|
|
9300
|
-
codeBackground: "#0a1020",
|
|
9301
|
-
codeText: "#e7f0ff"
|
|
9302
|
-
},
|
|
9303
|
-
fonts: {
|
|
9304
|
-
sans: "\"IBM Plex Sans\", \"Avenir Next\", \"Segoe UI Variable\", \"Segoe UI\", sans-serif",
|
|
9305
|
-
mono: "\"IBM Plex Mono\", \"SFMono-Regular\", Consolas, monospace"
|
|
9306
|
-
},
|
|
9307
|
-
entryPage: { mode: "default" },
|
|
9308
|
-
layout: {
|
|
9309
|
-
sidebarWidth: "260px",
|
|
9310
|
-
headerHeight: "60px",
|
|
9311
|
-
maxContentWidth: "960px"
|
|
9312
|
-
},
|
|
9313
|
-
header: {
|
|
9314
|
-
logo: void 0,
|
|
9315
|
-
logoLight: void 0,
|
|
9316
|
-
logoDark: void 0,
|
|
9317
|
-
showSiteNameText: true,
|
|
9318
|
-
logoWidth: 28,
|
|
9319
|
-
logoHeight: 28
|
|
9320
|
-
},
|
|
9321
|
-
footer: {
|
|
9322
|
-
message: void 0,
|
|
9323
|
-
copyright: void 0
|
|
9324
|
-
},
|
|
9325
|
-
socialLinks: {},
|
|
9326
|
-
embed: {},
|
|
9327
|
-
css: "",
|
|
9328
|
-
js: ""
|
|
9329
|
-
};
|
|
9330
|
-
/**
|
|
9331
|
-
* Deep merge two objects.
|
|
9332
|
-
*/
|
|
9333
|
-
function deepMerge(target, source) {
|
|
9334
|
-
const result = { ...target };
|
|
9335
|
-
for (const key of Object.keys(source)) {
|
|
9336
|
-
const sourceValue = source[key];
|
|
9337
|
-
const targetValue = target[key];
|
|
9338
|
-
if (sourceValue !== void 0 && typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(sourceValue) && typeof targetValue === "object" && targetValue !== null && !Array.isArray(targetValue)) result[key] = deepMerge(targetValue, sourceValue);
|
|
9339
|
-
else if (sourceValue !== void 0) result[key] = sourceValue;
|
|
9340
|
-
}
|
|
9341
|
-
return result;
|
|
9342
|
-
}
|
|
9343
|
-
/**
|
|
9344
|
-
* Defines a theme configuration with type checking.
|
|
9345
|
-
*
|
|
9346
|
-
* @example
|
|
9347
|
-
* ```ts
|
|
9348
|
-
* const myTheme = defineTheme({
|
|
9349
|
-
* extends: defaultTheme,
|
|
9350
|
-
* colors: {
|
|
9351
|
-
* primary: '#3498db',
|
|
9352
|
-
* },
|
|
9353
|
-
* footer: {
|
|
9354
|
-
* copyright: '2025 My Company',
|
|
9355
|
-
* },
|
|
9356
|
-
* });
|
|
9357
|
-
* ```
|
|
9358
|
-
*/
|
|
9359
|
-
function defineTheme(config) {
|
|
9360
|
-
return config;
|
|
9361
|
-
}
|
|
9362
|
-
/**
|
|
9363
|
-
* Merges multiple theme configurations.
|
|
9364
|
-
* Later themes override earlier ones.
|
|
9365
|
-
*
|
|
9366
|
-
* @example
|
|
9367
|
-
* ```ts
|
|
9368
|
-
* const merged = mergeThemes(defaultTheme, customTheme, overrides);
|
|
9369
|
-
* ```
|
|
9370
|
-
*/
|
|
9371
|
-
function mergeThemes(...themes) {
|
|
9372
|
-
if (themes.length === 0) return { ...defaultTheme };
|
|
9373
|
-
let result = {};
|
|
9374
|
-
for (const theme of themes) result = deepMerge(result, theme);
|
|
9375
|
-
return result;
|
|
9376
|
-
}
|
|
9377
|
-
/**
|
|
9378
|
-
* Resolves a theme configuration by merging with its extends chain and defaults.
|
|
9379
|
-
*/
|
|
9380
|
-
function resolveTheme(config) {
|
|
9381
|
-
if (!config) return resolveTheme(defaultTheme);
|
|
9382
|
-
const chain = [];
|
|
9383
|
-
let current = config;
|
|
9384
|
-
while (current) {
|
|
9385
|
-
chain.unshift(current);
|
|
9386
|
-
current = current.extends;
|
|
9387
|
-
}
|
|
9388
|
-
if (chain[0] !== defaultTheme && chain[0]?.name !== "default") chain.unshift(defaultTheme);
|
|
9389
|
-
const merged = mergeThemes(...chain);
|
|
9390
|
-
return {
|
|
9391
|
-
name: merged.name ?? "custom",
|
|
9392
|
-
colors: merged.colors ?? defaultTheme.colors,
|
|
9393
|
-
darkColors: merged.darkColors ?? defaultTheme.darkColors,
|
|
9394
|
-
fonts: merged.fonts ?? defaultTheme.fonts,
|
|
9395
|
-
entryPage: merged.entryPage ?? defaultTheme.entryPage,
|
|
9396
|
-
layout: merged.layout ?? defaultTheme.layout,
|
|
9397
|
-
header: merged.header ?? defaultTheme.header,
|
|
9398
|
-
footer: merged.footer ?? defaultTheme.footer,
|
|
9399
|
-
socialLinks: merged.socialLinks ?? defaultTheme.socialLinks,
|
|
9400
|
-
sidebar: merged.sidebar ?? [],
|
|
9401
|
-
embed: merged.embed ?? {},
|
|
9402
|
-
css: merged.css ?? "",
|
|
9403
|
-
js: merged.js ?? ""
|
|
9404
|
-
};
|
|
9405
|
-
}
|
|
9406
|
-
/**
|
|
9407
|
-
* Converts resolved theme to the format expected by Rust NAPI.
|
|
9408
|
-
*/
|
|
9409
|
-
function themeToNapi(theme) {
|
|
9410
|
-
const socialLinks = socialLinksToNapi(theme.socialLinks);
|
|
9411
|
-
return {
|
|
9412
|
-
colors: theme.colors.primary ? {
|
|
9413
|
-
primary: theme.colors.primary,
|
|
9414
|
-
primaryHover: theme.colors.primaryHover,
|
|
9415
|
-
background: theme.colors.background,
|
|
9416
|
-
backgroundAlt: theme.colors.backgroundAlt,
|
|
9417
|
-
text: theme.colors.text,
|
|
9418
|
-
textMuted: theme.colors.textMuted,
|
|
9419
|
-
border: theme.colors.border,
|
|
9420
|
-
codeBackground: theme.colors.codeBackground,
|
|
9421
|
-
codeText: theme.colors.codeText
|
|
9422
|
-
} : void 0,
|
|
9423
|
-
darkColors: theme.darkColors.primary ? {
|
|
9424
|
-
primary: theme.darkColors.primary,
|
|
9425
|
-
primaryHover: theme.darkColors.primaryHover,
|
|
9426
|
-
background: theme.darkColors.background,
|
|
9427
|
-
backgroundAlt: theme.darkColors.backgroundAlt,
|
|
9428
|
-
text: theme.darkColors.text,
|
|
9429
|
-
textMuted: theme.darkColors.textMuted,
|
|
9430
|
-
border: theme.darkColors.border,
|
|
9431
|
-
codeBackground: theme.darkColors.codeBackground,
|
|
9432
|
-
codeText: theme.darkColors.codeText
|
|
9433
|
-
} : void 0,
|
|
9434
|
-
fonts: theme.fonts.sans ? {
|
|
9435
|
-
sans: theme.fonts.sans,
|
|
9436
|
-
mono: theme.fonts.mono
|
|
9437
|
-
} : void 0,
|
|
9438
|
-
entryPage: theme.entryPage.mode ? { mode: theme.entryPage.mode } : void 0,
|
|
9439
|
-
layout: theme.layout.sidebarWidth ? {
|
|
9440
|
-
sidebarWidth: theme.layout.sidebarWidth,
|
|
9441
|
-
headerHeight: theme.layout.headerHeight,
|
|
9442
|
-
maxContentWidth: theme.layout.maxContentWidth
|
|
9443
|
-
} : void 0,
|
|
9444
|
-
header: theme.header.logo || theme.header.logoLight || theme.header.logoDark ? {
|
|
9445
|
-
logo: theme.header.logo,
|
|
9446
|
-
logoLight: theme.header.logoLight,
|
|
9447
|
-
logoDark: theme.header.logoDark,
|
|
9448
|
-
showSiteNameText: theme.header.showSiteNameText,
|
|
9449
|
-
logoWidth: theme.header.logoWidth,
|
|
9450
|
-
logoHeight: theme.header.logoHeight
|
|
9451
|
-
} : void 0,
|
|
9452
|
-
footer: theme.footer.message || theme.footer.copyright ? {
|
|
9453
|
-
message: theme.footer.message,
|
|
9454
|
-
copyright: theme.footer.copyright
|
|
9455
|
-
} : void 0,
|
|
9456
|
-
socialLinks,
|
|
9457
|
-
embed: Object.keys(theme.embed).length > 0 ? theme.embed : void 0,
|
|
9458
|
-
css: theme.css || void 0,
|
|
9459
|
-
js: theme.js || void 0
|
|
9460
|
-
};
|
|
9461
|
-
}
|
|
9462
|
-
function socialLinksToNapi(links) {
|
|
9463
|
-
if (Array.isArray(links)) {
|
|
9464
|
-
const items = links.map((item) => {
|
|
9465
|
-
return {
|
|
9466
|
-
icon: typeof item.icon === "string" ? item.icon : void 0,
|
|
9467
|
-
iconSvg: typeof item.icon === "object" ? item.icon.svg : void 0,
|
|
9468
|
-
link: item.link,
|
|
9469
|
-
ariaLabel: item.ariaLabel
|
|
9470
|
-
};
|
|
9471
|
-
});
|
|
9472
|
-
return items.length > 0 ? { links: items } : void 0;
|
|
9473
|
-
}
|
|
9474
|
-
return links.github || links.twitter || links.discord ? {
|
|
9475
|
-
github: links.github,
|
|
9476
|
-
twitter: links.twitter,
|
|
9477
|
-
discord: links.discord
|
|
9478
|
-
} : void 0;
|
|
9479
|
-
}
|
|
9480
|
-
//#endregion
|
|
9481
8802
|
//#region src/ssg.ts
|
|
9482
8803
|
/**
|
|
9483
8804
|
* SSG (Static Site Generation) module for ox-content
|
|
@@ -10854,7 +10175,8 @@ function resolveSsgOptions(ssg) {
|
|
|
10854
10175
|
generateOgImage: ssg.generateOgImage ?? false,
|
|
10855
10176
|
lastUpdated: ssg.lastUpdated ?? false,
|
|
10856
10177
|
siteUrl: ssg.siteUrl,
|
|
10857
|
-
theme: resolveTheme(ssg.theme)
|
|
10178
|
+
theme: resolveTheme(ssg.theme),
|
|
10179
|
+
navigation: ssg.navigation
|
|
10858
10180
|
};
|
|
10859
10181
|
}
|
|
10860
10182
|
/**
|
|
@@ -10882,10 +10204,7 @@ function renderTemplate(template, data) {
|
|
|
10882
10204
|
* Extracts title from content or frontmatter.
|
|
10883
10205
|
*/
|
|
10884
10206
|
function extractTitle$1(content, frontmatter) {
|
|
10885
|
-
|
|
10886
|
-
const h1Match = content.match(/<h1[^>]*>([^<]+)<\/h1>/i);
|
|
10887
|
-
if (h1Match) return h1Match[1].trim();
|
|
10888
|
-
return "Untitled";
|
|
10207
|
+
return importNapiModuleSync().extractSsgTitle(content, typeof frontmatter.title === "string" ? frontmatter.title : void 0);
|
|
10889
10208
|
}
|
|
10890
10209
|
/**
|
|
10891
10210
|
* Generates bare HTML page (no navigation, no styles).
|
|
@@ -10971,229 +10290,94 @@ async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, th
|
|
|
10971
10290
|
}))
|
|
10972
10291
|
});
|
|
10973
10292
|
}
|
|
10974
|
-
const SSG_STYLE_BLOCK_RE = /[ \t]*<!-- ox-content:styles:start -->\s*<style>([\s\S]*?)<\/style>\s*<!-- ox-content:styles:end -->/;
|
|
10975
|
-
const SSG_SCRIPT_BLOCK_RE = /[ \t]*<!-- ox-content:scripts:start -->\s*<script>([\s\S]*?)<\/script>\s*<!-- ox-content:scripts:end -->/;
|
|
10976
|
-
const FIRST_INLINE_STYLE_RE = /[ \t]*<style>([\s\S]*?)<\/style>/;
|
|
10977
|
-
const LAST_INLINE_BODY_SCRIPT_RE = /[ \t]*<script>([\s\S]*?)<\/script>\s*<\/body>/;
|
|
10978
|
-
const CSS_SECTION_RE = /\/\* ox-content:css:([a-z0-9-]+):start \*\/\s*([\s\S]*?)\s*\/\* ox-content:css:\1:end \*\//g;
|
|
10979
|
-
const SEARCH_CHUNK_RE = /\/\/ ox-content:search:start\s*([\s\S]*?)\s*\/\/ ox-content:search:end/;
|
|
10980
|
-
const SEARCH_CHUNK_PLACEHOLDER = "__OX_CONTENT_SEARCH_CHUNK__";
|
|
10981
|
-
const CORE_CSS_SECTION_NAMES = new Set(["base", "footer"]);
|
|
10982
|
-
const THEME_INLINE_CSS_MAX_BYTES = 2048;
|
|
10983
|
-
function createContentHash(content) {
|
|
10984
|
-
return createHash("sha256").update(content).digest("hex").slice(0, 10);
|
|
10985
|
-
}
|
|
10986
|
-
function sanitizeChunkLabel(label) {
|
|
10987
|
-
return label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "asset";
|
|
10988
|
-
}
|
|
10989
|
-
function toPublicAssetPath(base, fileName) {
|
|
10990
|
-
return `${base.endsWith("/") ? base : `${base}/`}assets/${fileName}`;
|
|
10991
|
-
}
|
|
10992
|
-
function hasRelativeCssUrls(css) {
|
|
10993
|
-
let cursor = 0;
|
|
10994
|
-
while (cursor < css.length) {
|
|
10995
|
-
const urlIndex = css.indexOf("url(", cursor);
|
|
10996
|
-
if (urlIndex === -1) return false;
|
|
10997
|
-
let valueStart = urlIndex + 4;
|
|
10998
|
-
while (valueStart < css.length && /\s/.test(css[valueStart])) valueStart++;
|
|
10999
|
-
const quote = css[valueStart] === "\"" || css[valueStart] === "'" ? css[valueStart] : "";
|
|
11000
|
-
if (quote) valueStart++;
|
|
11001
|
-
let valueEnd = valueStart;
|
|
11002
|
-
while (valueEnd < css.length) {
|
|
11003
|
-
const char = css[valueEnd];
|
|
11004
|
-
if (quote) {
|
|
11005
|
-
if (char === "\\") {
|
|
11006
|
-
valueEnd += 2;
|
|
11007
|
-
continue;
|
|
11008
|
-
}
|
|
11009
|
-
if (char === quote) break;
|
|
11010
|
-
} else if (char === ")") break;
|
|
11011
|
-
valueEnd++;
|
|
11012
|
-
}
|
|
11013
|
-
const value = css.slice(valueStart, valueEnd).trim();
|
|
11014
|
-
if (value && !value.startsWith("data:") && !value.startsWith("http:") && !value.startsWith("https:") && !value.startsWith("//") && !value.startsWith("/") && !value.startsWith("#") && !value.startsWith("blob:") && !value.startsWith("var(")) return true;
|
|
11015
|
-
cursor = valueEnd + 1;
|
|
11016
|
-
}
|
|
11017
|
-
return false;
|
|
11018
|
-
}
|
|
11019
|
-
function createSharedAssetChunk(type, label, content, outDir, base) {
|
|
11020
|
-
const hash = createContentHash(content);
|
|
11021
|
-
const fileName = `ox-content-${sanitizeChunkLabel(label)}-${hash}.${type}`;
|
|
11022
|
-
return {
|
|
11023
|
-
outputPath: path$2.join(outDir, "assets", fileName),
|
|
11024
|
-
publicPath: toPublicAssetPath(base, fileName),
|
|
11025
|
-
content
|
|
11026
|
-
};
|
|
11027
|
-
}
|
|
11028
|
-
function extractCssSections(cssContent) {
|
|
11029
|
-
return Array.from(cssContent.matchAll(CSS_SECTION_RE)).map(([, name, content]) => ({
|
|
11030
|
-
name,
|
|
11031
|
-
content: content.trim()
|
|
11032
|
-
})).filter((section) => section.content.length > 0);
|
|
11033
|
-
}
|
|
11034
|
-
function getOrCreateSharedChunk(chunks, type, label, content, outDir, base) {
|
|
11035
|
-
let chunk = chunks.get(content);
|
|
11036
|
-
if (!chunk) {
|
|
11037
|
-
chunk = createSharedAssetChunk(type, label, content, outDir, base);
|
|
11038
|
-
chunks.set(content, chunk);
|
|
11039
|
-
}
|
|
11040
|
-
return chunk;
|
|
11041
|
-
}
|
|
11042
|
-
function buildStyleReplacement(cssContent, cssChunks, outDir, base) {
|
|
11043
|
-
const sections = extractCssSections(cssContent);
|
|
11044
|
-
const effectiveSections = sections.length > 0 ? sections : [{
|
|
11045
|
-
name: "css",
|
|
11046
|
-
content: cssContent.trim()
|
|
11047
|
-
}];
|
|
11048
|
-
const coreContent = effectiveSections.filter((section) => CORE_CSS_SECTION_NAMES.has(section.name)).map((section) => section.content).join("\n").trim();
|
|
11049
|
-
const fragments = [];
|
|
11050
|
-
if (coreContent) {
|
|
11051
|
-
const coreChunk = getOrCreateSharedChunk(cssChunks, "css", "core", coreContent, outDir, base);
|
|
11052
|
-
fragments.push(` <link rel="stylesheet" href="${coreChunk.publicPath}">`);
|
|
11053
|
-
}
|
|
11054
|
-
for (const section of effectiveSections) {
|
|
11055
|
-
if (CORE_CSS_SECTION_NAMES.has(section.name)) continue;
|
|
11056
|
-
if (section.name === "theme" && (hasRelativeCssUrls(section.content) || section.content.length <= THEME_INLINE_CSS_MAX_BYTES) || hasRelativeCssUrls(section.content)) {
|
|
11057
|
-
fragments.push(` <style>${section.content}</style>`);
|
|
11058
|
-
continue;
|
|
11059
|
-
}
|
|
11060
|
-
const chunk = getOrCreateSharedChunk(cssChunks, "css", section.name, section.content, outDir, base);
|
|
11061
|
-
fragments.push(` <link rel="stylesheet" href="${chunk.publicPath}">`);
|
|
11062
|
-
}
|
|
11063
|
-
return fragments.join("\n");
|
|
11064
|
-
}
|
|
11065
|
-
function buildScriptReplacement(jsContent, jsChunks, outDir, base) {
|
|
11066
|
-
const searchMatch = jsContent.match(SEARCH_CHUNK_RE);
|
|
11067
|
-
if (searchMatch && jsContent.includes(SEARCH_CHUNK_PLACEHOLDER)) {
|
|
11068
|
-
const searchContent = searchMatch[1].trim();
|
|
11069
|
-
if (searchContent) {
|
|
11070
|
-
const searchChunk = getOrCreateSharedChunk(jsChunks, "js", "search", searchContent, outDir, base);
|
|
11071
|
-
const coreContent = jsContent.replace(SEARCH_CHUNK_RE, "").replaceAll(SEARCH_CHUNK_PLACEHOLDER, searchChunk.publicPath).trim();
|
|
11072
|
-
if (coreContent) return ` <script defer src="${getOrCreateSharedChunk(jsChunks, "js", "core", coreContent, outDir, base).publicPath}"><\/script>`;
|
|
11073
|
-
}
|
|
11074
|
-
}
|
|
11075
|
-
const fallbackContent = jsContent.trim();
|
|
11076
|
-
if (!fallbackContent) return "";
|
|
11077
|
-
return ` <script defer src="${getOrCreateSharedChunk(jsChunks, "js", "js", fallbackContent, outDir, base).publicPath}"><\/script>`;
|
|
11078
|
-
}
|
|
11079
10293
|
async function externalizeSharedPageAssets(pages, outDir, base) {
|
|
11080
|
-
const
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
const styleMatch = html.match(SSG_STYLE_BLOCK_RE);
|
|
11085
|
-
if (styleMatch) {
|
|
11086
|
-
const replacement = buildStyleReplacement(styleMatch[1], cssChunks, outDir, base);
|
|
11087
|
-
html = html.replace(SSG_STYLE_BLOCK_RE, replacement);
|
|
11088
|
-
} else {
|
|
11089
|
-
const inlineStyleMatch = html.match(FIRST_INLINE_STYLE_RE);
|
|
11090
|
-
if (inlineStyleMatch) {
|
|
11091
|
-
const replacement = buildStyleReplacement(inlineStyleMatch[1], cssChunks, outDir, base);
|
|
11092
|
-
html = html.replace(FIRST_INLINE_STYLE_RE, replacement);
|
|
11093
|
-
}
|
|
11094
|
-
}
|
|
11095
|
-
const scriptMatch = html.match(SSG_SCRIPT_BLOCK_RE);
|
|
11096
|
-
if (scriptMatch) {
|
|
11097
|
-
const replacement = buildScriptReplacement(scriptMatch[1], jsChunks, outDir, base);
|
|
11098
|
-
html = html.replace(SSG_SCRIPT_BLOCK_RE, replacement);
|
|
11099
|
-
} else {
|
|
11100
|
-
const inlineScriptMatch = html.match(LAST_INLINE_BODY_SCRIPT_RE);
|
|
11101
|
-
if (inlineScriptMatch) {
|
|
11102
|
-
const replacement = buildScriptReplacement(inlineScriptMatch[1], jsChunks, outDir, base);
|
|
11103
|
-
html = html.replace(LAST_INLINE_BODY_SCRIPT_RE, replacement ? `${replacement}\n</body>` : "</body>");
|
|
11104
|
-
}
|
|
11105
|
-
}
|
|
11106
|
-
return {
|
|
11107
|
-
...page,
|
|
11108
|
-
html
|
|
11109
|
-
};
|
|
11110
|
-
});
|
|
11111
|
-
const chunks = [...cssChunks.values(), ...jsChunks.values()];
|
|
11112
|
-
await Promise.all(chunks.map(async (chunk) => {
|
|
11113
|
-
await fs$1.mkdir(path$2.dirname(chunk.outputPath), { recursive: true });
|
|
11114
|
-
await fs$1.writeFile(chunk.outputPath, chunk.content, "utf-8");
|
|
10294
|
+
const optimized = (await importNapiModule()).externalizeSsgAssets(pages, outDir, base);
|
|
10295
|
+
await Promise.all(optimized.assets.map(async (asset) => {
|
|
10296
|
+
await fs$1.mkdir(path$1.dirname(asset.outputPath), { recursive: true });
|
|
10297
|
+
await fs$1.writeFile(asset.outputPath, asset.content, "utf-8");
|
|
11115
10298
|
}));
|
|
11116
10299
|
return {
|
|
11117
|
-
pages:
|
|
11118
|
-
assets:
|
|
10300
|
+
pages: optimized.pages,
|
|
10301
|
+
assets: optimized.assets.map((asset) => asset.outputPath)
|
|
11119
10302
|
};
|
|
11120
10303
|
}
|
|
11121
10304
|
/**
|
|
11122
|
-
* Converts a markdown file path to its corresponding HTML output path.
|
|
11123
|
-
*/
|
|
11124
|
-
function getOutputPath(inputPath, srcDir, outDir, extension) {
|
|
11125
|
-
const baseName = path$2.relative(srcDir, inputPath).replace(/\.(?:md|markdown)$/i, extension);
|
|
11126
|
-
if (baseName.endsWith(`index${extension}`)) return path$2.join(outDir, baseName);
|
|
11127
|
-
const dirName = baseName.replace(new RegExp(`\\${extension}$`), "");
|
|
11128
|
-
return path$2.join(outDir, dirName, `index${extension}`);
|
|
11129
|
-
}
|
|
11130
|
-
/**
|
|
11131
10305
|
* Converts a markdown file path to a relative URL path.
|
|
11132
10306
|
*/
|
|
11133
10307
|
function getUrlPath$1(inputPath, srcDir) {
|
|
11134
|
-
|
|
11135
|
-
if (baseName === "index" || baseName.endsWith("/index")) return baseName.replace(/\/?index$/, "") || "/";
|
|
11136
|
-
return baseName;
|
|
10308
|
+
return importNapiModuleSync().getSsgUrlPath(inputPath, srcDir);
|
|
11137
10309
|
}
|
|
11138
|
-
|
|
11139
|
-
|
|
11140
|
-
*/
|
|
11141
|
-
function getHref(inputPath, srcDir, base, extension) {
|
|
11142
|
-
const urlPath = getUrlPath$1(inputPath, srcDir);
|
|
11143
|
-
if (urlPath === "/" || urlPath === "") return `${base}index${extension}`;
|
|
11144
|
-
return `${base}${urlPath}/index${extension}`;
|
|
10310
|
+
function isExternalHref(value) {
|
|
10311
|
+
return /^[a-z][a-z0-9+.-]*:/i.test(value) || value.startsWith("//");
|
|
11145
10312
|
}
|
|
11146
|
-
function
|
|
11147
|
-
|
|
11148
|
-
|
|
11149
|
-
|
|
10313
|
+
function splitHrefSuffix(value) {
|
|
10314
|
+
const match = /^([^?#]*)([?#].*)?$/.exec(value);
|
|
10315
|
+
return {
|
|
10316
|
+
pathname: match?.[1] ?? value,
|
|
10317
|
+
suffix: match?.[2] ?? ""
|
|
10318
|
+
};
|
|
11150
10319
|
}
|
|
11151
|
-
|
|
11152
|
-
|
|
11153
|
-
|
|
11154
|
-
|
|
11155
|
-
|
|
11156
|
-
if (
|
|
11157
|
-
|
|
11158
|
-
|
|
11159
|
-
|
|
11160
|
-
|
|
10320
|
+
function normalizeNavigationPath(value) {
|
|
10321
|
+
const { pathname, suffix } = splitHrefSuffix(value.trim());
|
|
10322
|
+
let normalized = pathname || "/";
|
|
10323
|
+
if (!normalized.startsWith("/")) normalized = `/${normalized}`;
|
|
10324
|
+
normalized = normalized.replace(/\/index(?:\.(?:html?|md|markdown))?$/i, "/").replace(/\.(?:html?|md|markdown)$/i, "");
|
|
10325
|
+
if (normalized !== "/") normalized = normalized.replace(/\/+$/, "");
|
|
10326
|
+
return {
|
|
10327
|
+
path: normalized || "/",
|
|
10328
|
+
suffix
|
|
10329
|
+
};
|
|
11161
10330
|
}
|
|
11162
|
-
|
|
11163
|
-
|
|
11164
|
-
|
|
11165
|
-
*/
|
|
11166
|
-
function getOgImageUrl(inputPath, srcDir, base, siteUrl) {
|
|
11167
|
-
const urlPath = getUrlPath$1(inputPath, srcDir);
|
|
11168
|
-
let relativePath;
|
|
11169
|
-
if (urlPath === "/" || urlPath === "") relativePath = `${base}og-image.png`;
|
|
11170
|
-
else relativePath = `${base}${urlPath}/og-image.png`;
|
|
11171
|
-
if (siteUrl) return `${siteUrl.replace(/\/$/, "")}${relativePath}`;
|
|
11172
|
-
return relativePath;
|
|
10331
|
+
function buildHrefFromNavigationPath(pathname, base, extension) {
|
|
10332
|
+
if (pathname === "/" || pathname === "") return `${base}index${extension}`;
|
|
10333
|
+
return `${base}${pathname.replace(/^\/+/, "")}/index${extension}`;
|
|
11173
10334
|
}
|
|
11174
10335
|
/**
|
|
11175
|
-
*
|
|
10336
|
+
* Resolves manual navigation config to the format used by the built-in SSG renderer.
|
|
11176
10337
|
*/
|
|
11177
|
-
function
|
|
11178
|
-
|
|
11179
|
-
|
|
11180
|
-
|
|
11181
|
-
|
|
11182
|
-
|
|
11183
|
-
|
|
11184
|
-
|
|
10338
|
+
function resolveNavigationGroups(navigation, base, extension) {
|
|
10339
|
+
if (!navigation) return;
|
|
10340
|
+
return navigation.map((group) => ({
|
|
10341
|
+
title: group.title,
|
|
10342
|
+
items: group.items.flatMap((item) => {
|
|
10343
|
+
const rawHref = item.href ?? item.path;
|
|
10344
|
+
if (!rawHref) return [];
|
|
10345
|
+
if (isExternalHref(rawHref) || rawHref.startsWith("#")) return [{
|
|
10346
|
+
title: item.title,
|
|
10347
|
+
path: item.path ?? rawHref,
|
|
10348
|
+
href: rawHref
|
|
10349
|
+
}];
|
|
10350
|
+
const { path } = normalizeNavigationPath(item.path ?? rawHref);
|
|
10351
|
+
const href = item.href ? (() => {
|
|
10352
|
+
const normalized = normalizeNavigationPath(item.href);
|
|
10353
|
+
return `${buildHrefFromNavigationPath(normalized.path, base, extension)}${normalized.suffix}`;
|
|
10354
|
+
})() : buildHrefFromNavigationPath(path, base, extension);
|
|
10355
|
+
return [{
|
|
10356
|
+
title: item.title,
|
|
10357
|
+
path,
|
|
10358
|
+
href
|
|
10359
|
+
}];
|
|
10360
|
+
})
|
|
10361
|
+
}));
|
|
10362
|
+
}
|
|
10363
|
+
function getPageLocale(urlPath, i18n) {
|
|
10364
|
+
if (!i18n) return void 0;
|
|
10365
|
+
return importNapiModuleSync().getSsgPageLocale(urlPath, i18n.defaultLocale, i18n.locales.map((locale) => locale.code)) ?? void 0;
|
|
10366
|
+
}
|
|
10367
|
+
function getRoutePaths(inputPath, srcDir, outDir, base, extension, siteUrl) {
|
|
10368
|
+
return importNapiModuleSync().resolveSsgRoutePaths(inputPath, srcDir, outDir, base, extension, siteUrl);
|
|
11185
10369
|
}
|
|
11186
10370
|
/**
|
|
11187
10371
|
* Formats a file/dir name as a title.
|
|
11188
10372
|
*/
|
|
11189
10373
|
function formatTitle(name) {
|
|
11190
|
-
return
|
|
10374
|
+
return importNapiModuleSync().formatSsgTitle(name);
|
|
11191
10375
|
}
|
|
11192
10376
|
/**
|
|
11193
10377
|
* Collects all markdown files from the source directory.
|
|
11194
10378
|
*/
|
|
11195
10379
|
async function collectMarkdownFiles$1(srcDir) {
|
|
11196
|
-
return (await glob(path$
|
|
10380
|
+
return (await glob(path$1.join(srcDir, "**/*.{md,markdown}"), {
|
|
11197
10381
|
nodir: true,
|
|
11198
10382
|
ignore: [
|
|
11199
10383
|
"**/node_modules/**",
|
|
@@ -11206,110 +10390,13 @@ async function collectMarkdownFiles$1(srcDir) {
|
|
|
11206
10390
|
* Builds navigation items from markdown files, grouped by directory.
|
|
11207
10391
|
*/
|
|
11208
10392
|
function buildNavItems(markdownFiles, srcDir, base, extension) {
|
|
11209
|
-
|
|
11210
|
-
const groupOrder = [
|
|
11211
|
-
"",
|
|
11212
|
-
"examples",
|
|
11213
|
-
"packages",
|
|
11214
|
-
"api"
|
|
11215
|
-
];
|
|
11216
|
-
for (const file of markdownFiles) {
|
|
11217
|
-
const parts = path$2.relative(srcDir, file).split(path$2.sep);
|
|
11218
|
-
let groupKey = "";
|
|
11219
|
-
if (parts.length > 1) groupKey = parts[0];
|
|
11220
|
-
if (!groups.has(groupKey)) groups.set(groupKey, []);
|
|
11221
|
-
const urlPath = getUrlPath$1(file, srcDir);
|
|
11222
|
-
let title;
|
|
11223
|
-
if (urlPath === "/" || urlPath === "") title = "Overview";
|
|
11224
|
-
else title = getDisplayTitle(file);
|
|
11225
|
-
groups.get(groupKey).push({
|
|
11226
|
-
title,
|
|
11227
|
-
path: urlPath,
|
|
11228
|
-
href: getHref(file, srcDir, base, extension)
|
|
11229
|
-
});
|
|
11230
|
-
}
|
|
11231
|
-
const sortItems = (items) => {
|
|
11232
|
-
return items.sort((a, b) => {
|
|
11233
|
-
const aIsRoot = a.path === "/" || a.path === "";
|
|
11234
|
-
const bIsRoot = b.path === "/" || b.path === "";
|
|
11235
|
-
if (aIsRoot && !bIsRoot) return -1;
|
|
11236
|
-
if (!aIsRoot && bIsRoot) return 1;
|
|
11237
|
-
return a.title.localeCompare(b.title);
|
|
11238
|
-
});
|
|
11239
|
-
};
|
|
11240
|
-
const result = [];
|
|
11241
|
-
for (const key of groupOrder) {
|
|
11242
|
-
const items = groups.get(key);
|
|
11243
|
-
if (items && items.length > 0) {
|
|
11244
|
-
result.push({
|
|
11245
|
-
title: key === "" ? "Guide" : formatTitle(key),
|
|
11246
|
-
items: sortItems(items)
|
|
11247
|
-
});
|
|
11248
|
-
groups.delete(key);
|
|
11249
|
-
}
|
|
11250
|
-
}
|
|
11251
|
-
for (const [key, items] of groups) if (items.length > 0) result.push({
|
|
11252
|
-
title: formatTitle(key),
|
|
11253
|
-
items: sortItems(items)
|
|
11254
|
-
});
|
|
11255
|
-
return result;
|
|
11256
|
-
}
|
|
11257
|
-
function isSafeSidebarLink(link) {
|
|
11258
|
-
const trimmed = link.trim();
|
|
11259
|
-
if (trimmed.startsWith("//")) return false;
|
|
11260
|
-
return !/^[a-z][a-z0-9+.-]*:/i.test(trimmed) || /^(https?:|mailto:)/i.test(trimmed);
|
|
11261
|
-
}
|
|
11262
|
-
function sidebarPath(link) {
|
|
11263
|
-
if (!link || !isSafeSidebarLink(link)) return "";
|
|
11264
|
-
if (/^(https?:|mailto:|#)/i.test(link.trim())) return "";
|
|
11265
|
-
const bare = link.trim().split("#", 1)[0].split("?", 1)[0].replace(/^\/+/, "").replace(/\/$/, "").replace(/\.(md|markdown)$/i, "");
|
|
11266
|
-
if (!bare || bare === "index") return "/";
|
|
11267
|
-
return bare.replace(/\/index$/, "");
|
|
11268
|
-
}
|
|
11269
|
-
function sidebarHref(link, base, extension) {
|
|
11270
|
-
if (!link) return "#";
|
|
11271
|
-
const trimmed = link.trim();
|
|
11272
|
-
if (!isSafeSidebarLink(trimmed)) return "#";
|
|
11273
|
-
if (/^(https?:|mailto:|#)/i.test(trimmed)) return trimmed;
|
|
11274
|
-
const hash = trimmed.includes("#") ? `#${trimmed.split("#").slice(1).join("#")}` : "";
|
|
11275
|
-
const withoutExt = trimmed.split("#", 1)[0].replace(/^\/+/, "").replace(/\/$/, "").replace(/\.(md|markdown)$/i, "");
|
|
11276
|
-
return `${base}${!withoutExt || withoutExt === "index" ? "index" : `${withoutExt.replace(/\/index$/, "")}/index`}${extension}${hash}`;
|
|
10393
|
+
return importNapiModuleSync().buildSsgNavItems(markdownFiles, srcDir, base, extension);
|
|
11277
10394
|
}
|
|
11278
10395
|
/**
|
|
11279
10396
|
* Builds navigation items from an explicit theme sidebar tree.
|
|
11280
10397
|
*/
|
|
11281
10398
|
function buildThemeNavItems(sidebar, base, extension) {
|
|
11282
|
-
|
|
11283
|
-
const navItem = {
|
|
11284
|
-
title: item.text ?? item.link ?? "Untitled",
|
|
11285
|
-
path: sidebarPath(item.link),
|
|
11286
|
-
href: sidebarHref(item.link, base, extension)
|
|
11287
|
-
};
|
|
11288
|
-
if (item.items?.length) navItem.children = item.items.map(toNavItem);
|
|
11289
|
-
if (item.collapsed !== void 0) navItem.collapsed = item.collapsed;
|
|
11290
|
-
return navItem;
|
|
11291
|
-
};
|
|
11292
|
-
const groups = [];
|
|
11293
|
-
let looseItems = [];
|
|
11294
|
-
const flushLooseItems = () => {
|
|
11295
|
-
if (looseItems.length > 0) {
|
|
11296
|
-
groups.push({
|
|
11297
|
-
title: "Guide",
|
|
11298
|
-
items: looseItems
|
|
11299
|
-
});
|
|
11300
|
-
looseItems = [];
|
|
11301
|
-
}
|
|
11302
|
-
};
|
|
11303
|
-
for (const item of sidebar) if (item.items?.length && !item.link) {
|
|
11304
|
-
flushLooseItems();
|
|
11305
|
-
groups.push({
|
|
11306
|
-
title: item.text ?? "Guide",
|
|
11307
|
-
items: item.items.map(toNavItem),
|
|
11308
|
-
collapsed: item.collapsed
|
|
11309
|
-
});
|
|
11310
|
-
} else looseItems.push(toNavItem(item));
|
|
11311
|
-
flushLooseItems();
|
|
11312
|
-
return groups;
|
|
10399
|
+
return importNapiModuleSync().buildSsgThemeNavItems(sidebar, base, extension);
|
|
11313
10400
|
}
|
|
11314
10401
|
/**
|
|
11315
10402
|
* Builds all markdown files to static HTML.
|
|
@@ -11320,8 +10407,8 @@ async function buildSsg(options, root) {
|
|
|
11320
10407
|
files: [],
|
|
11321
10408
|
errors: []
|
|
11322
10409
|
};
|
|
11323
|
-
const srcDir = path$
|
|
11324
|
-
const outDir = path$
|
|
10410
|
+
const srcDir = path$1.resolve(root, options.srcDir);
|
|
10411
|
+
const outDir = path$1.resolve(root, options.outDir);
|
|
11325
10412
|
const base = options.base.endsWith("/") ? options.base : options.base + "/";
|
|
11326
10413
|
const generatedFiles = [];
|
|
11327
10414
|
const generatedPages = [];
|
|
@@ -11333,10 +10420,10 @@ async function buildSsg(options, root) {
|
|
|
11333
10420
|
});
|
|
11334
10421
|
} catch {}
|
|
11335
10422
|
const markdownFiles = await collectMarkdownFiles$1(srcDir);
|
|
11336
|
-
const navItems = ssgOptions.theme?.sidebar.length ? buildThemeNavItems(ssgOptions.theme.sidebar, base, ssgOptions.extension) : buildNavItems(markdownFiles, srcDir, base, ssgOptions.extension);
|
|
10423
|
+
const navItems = resolveNavigationGroups(ssgOptions.navigation, base, ssgOptions.extension) ?? (ssgOptions.theme?.sidebar.length ? buildThemeNavItems(ssgOptions.theme.sidebar, base, ssgOptions.extension) : buildNavItems(markdownFiles, srcDir, base, ssgOptions.extension));
|
|
11337
10424
|
let siteName = ssgOptions.siteName ?? "Documentation";
|
|
11338
10425
|
if (!ssgOptions.siteName) try {
|
|
11339
|
-
const pkgPath = path$
|
|
10426
|
+
const pkgPath = path$1.join(root, "package.json");
|
|
11340
10427
|
const pkg = JSON.parse(await fs$1.readFile(pkgPath, "utf-8"));
|
|
11341
10428
|
if (pkg.name) siteName = formatTitle(pkg.name);
|
|
11342
10429
|
} catch {}
|
|
@@ -11352,6 +10439,7 @@ async function buildSsg(options, root) {
|
|
|
11352
10439
|
baseUrl: base,
|
|
11353
10440
|
sourcePath: inputPath
|
|
11354
10441
|
});
|
|
10442
|
+
const frontmatter = normalizeVitePressFrontmatter(result.frontmatter);
|
|
11355
10443
|
let transformedHtml = result.html;
|
|
11356
10444
|
const { html: protectedHtml, svgs: mermaidSvgs } = protectMermaidSvgs(transformedHtml);
|
|
11357
10445
|
transformedHtml = protectedHtml;
|
|
@@ -11366,20 +10454,21 @@ async function buildSsg(options, root) {
|
|
|
11366
10454
|
transformedHtml = await transformAllPlugins(transformedHtml, pluginOptions);
|
|
11367
10455
|
if (hasIslands(transformedHtml)) transformedHtml = (await transformIslands(transformedHtml)).html;
|
|
11368
10456
|
transformedHtml = restoreMermaidSvgs(transformedHtml, mermaidSvgs);
|
|
11369
|
-
const title = extractTitle$1(transformedHtml,
|
|
11370
|
-
const description =
|
|
10457
|
+
const title = extractTitle$1(transformedHtml, frontmatter);
|
|
10458
|
+
const description = frontmatter.description;
|
|
10459
|
+
const routePaths = getRoutePaths(inputPath, srcDir, outDir, base, ssgOptions.extension, ssgOptions.siteUrl);
|
|
11371
10460
|
pageResults.push({
|
|
11372
10461
|
inputPath,
|
|
10462
|
+
routePaths,
|
|
11373
10463
|
transformedHtml,
|
|
11374
10464
|
title,
|
|
11375
10465
|
description,
|
|
11376
10466
|
lastUpdated: napi?.getGitLastUpdated(inputPath, root) ?? void 0,
|
|
11377
|
-
frontmatter
|
|
10467
|
+
frontmatter,
|
|
11378
10468
|
toc: result.toc
|
|
11379
10469
|
});
|
|
11380
10470
|
if (shouldGenerateOgImages) {
|
|
11381
|
-
const
|
|
11382
|
-
const { layout: _layout, ...frontmatterRest } = result.frontmatter;
|
|
10471
|
+
const { layout: _layout, ...frontmatterRest } = frontmatter;
|
|
11383
10472
|
ogImageEntries.push({
|
|
11384
10473
|
props: {
|
|
11385
10474
|
...frontmatterRest,
|
|
@@ -11387,10 +10476,10 @@ async function buildSsg(options, root) {
|
|
|
11387
10476
|
description,
|
|
11388
10477
|
siteName
|
|
11389
10478
|
},
|
|
11390
|
-
outputPath:
|
|
10479
|
+
outputPath: routePaths.ogImagePath
|
|
11391
10480
|
});
|
|
11392
10481
|
ogImageInputPaths.push(inputPath);
|
|
11393
|
-
ogImageUrlMap.set(inputPath,
|
|
10482
|
+
ogImageUrlMap.set(inputPath, routePaths.ogImageUrl);
|
|
11394
10483
|
}
|
|
11395
10484
|
} catch (err) {
|
|
11396
10485
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -11419,7 +10508,7 @@ async function buildSsg(options, root) {
|
|
|
11419
10508
|
ogImageUrlMap.clear();
|
|
11420
10509
|
}
|
|
11421
10510
|
for (const pageResult of pageResults) try {
|
|
11422
|
-
const { inputPath, transformedHtml, title, description, lastUpdated, frontmatter, toc } = pageResult;
|
|
10511
|
+
const { inputPath, routePaths, transformedHtml, title, description, lastUpdated, frontmatter, toc } = pageResult;
|
|
11423
10512
|
let pageOgImage = ssgOptions.ogImage;
|
|
11424
10513
|
if (shouldGenerateOgImages && ogImageUrlMap.has(inputPath)) pageOgImage = ogImageUrlMap.get(inputPath);
|
|
11425
10514
|
let entryPage;
|
|
@@ -11437,16 +10526,15 @@ async function buildSsg(options, root) {
|
|
|
11437
10526
|
toc,
|
|
11438
10527
|
lastUpdated,
|
|
11439
10528
|
frontmatter,
|
|
11440
|
-
path:
|
|
11441
|
-
href:
|
|
10529
|
+
path: routePaths.urlPath,
|
|
10530
|
+
href: routePaths.href,
|
|
11442
10531
|
entryPage
|
|
11443
10532
|
};
|
|
11444
10533
|
html = await generateHtmlPage(pageData, navItems, siteName, base, pageOgImage, ssgOptions.theme, getPageLocale(pageData.path, options.i18n), options.i18n ? options.i18n.locales : void 0);
|
|
11445
10534
|
}
|
|
11446
|
-
const outputPath = getOutputPath(inputPath, srcDir, outDir, ssgOptions.extension);
|
|
11447
10535
|
generatedPages.push({
|
|
11448
10536
|
inputPath,
|
|
11449
|
-
outputPath,
|
|
10537
|
+
outputPath: routePaths.outputPath,
|
|
11450
10538
|
html
|
|
11451
10539
|
});
|
|
11452
10540
|
} catch (err) {
|
|
@@ -11456,7 +10544,7 @@ async function buildSsg(options, root) {
|
|
|
11456
10544
|
const optimizedOutput = await externalizeSharedPageAssets(generatedPages, outDir, base);
|
|
11457
10545
|
generatedFiles.push(...optimizedOutput.assets);
|
|
11458
10546
|
for (const page of optimizedOutput.pages) {
|
|
11459
|
-
await fs$1.mkdir(path$
|
|
10547
|
+
await fs$1.mkdir(path$1.dirname(page.outputPath), { recursive: true });
|
|
11460
10548
|
await fs$1.writeFile(page.outputPath, page.html, "utf-8");
|
|
11461
10549
|
generatedFiles.push(page.outputPath);
|
|
11462
10550
|
}
|
|
@@ -11511,7 +10599,7 @@ async function collectMarkdownFiles(dir) {
|
|
|
11511
10599
|
try {
|
|
11512
10600
|
const entries = await fs$1.readdir(currentDir, { withFileTypes: true });
|
|
11513
10601
|
for (const entry of entries) {
|
|
11514
|
-
const fullPath = path$
|
|
10602
|
+
const fullPath = path$1.join(currentDir, entry.name);
|
|
11515
10603
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") await walk(fullPath);
|
|
11516
10604
|
else if (entry.isFile() && entry.name.endsWith(".md")) files.push(fullPath);
|
|
11517
10605
|
}
|
|
@@ -11536,7 +10624,7 @@ async function buildSearchIndex(srcDir, base) {
|
|
|
11536
10624
|
const documents = [];
|
|
11537
10625
|
for (const file of files) try {
|
|
11538
10626
|
const content = await fs$1.readFile(file, "utf-8");
|
|
11539
|
-
const relativePath = path$
|
|
10627
|
+
const relativePath = path$1.relative(srcDir, file);
|
|
11540
10628
|
const url = base + relativePath.replace(/\.md$/, "").replace(/\\/g, "/");
|
|
11541
10629
|
const id = relativePath.replace(/\.md$/, "").replace(/\\/g, "/");
|
|
11542
10630
|
const extractSearchContent = napi.extractSearchContent;
|
|
@@ -11565,7 +10653,7 @@ async function buildSearchIndex(srcDir, base) {
|
|
|
11565
10653
|
* Writes the search index to a file.
|
|
11566
10654
|
*/
|
|
11567
10655
|
async function writeSearchIndex(indexJson, outDir) {
|
|
11568
|
-
const indexPath = path$
|
|
10656
|
+
const indexPath = path$1.join(outDir, "search-index.json");
|
|
11569
10657
|
await fs$1.mkdir(outDir, { recursive: true });
|
|
11570
10658
|
await fs$1.writeFile(indexPath, indexJson, "utf-8");
|
|
11571
10659
|
}
|
|
@@ -11574,234 +10662,7 @@ async function writeSearchIndex(indexJson, outDir) {
|
|
|
11574
10662
|
* This is injected into the bundle as a virtual module.
|
|
11575
10663
|
*/
|
|
11576
10664
|
function generateSearchModule(options, indexPath) {
|
|
11577
|
-
return
|
|
11578
|
-
// Search module generated by ox-content
|
|
11579
|
-
const searchOptions = ${JSON.stringify(options)};
|
|
11580
|
-
|
|
11581
|
-
let searchIndex = null;
|
|
11582
|
-
let indexPromise = null;
|
|
11583
|
-
|
|
11584
|
-
function parseScopedQuery(query) {
|
|
11585
|
-
const scopes = [];
|
|
11586
|
-
const terms = [];
|
|
11587
|
-
|
|
11588
|
-
for (const part of query.trim().split(/\\s+/).filter(Boolean)) {
|
|
11589
|
-
if (part.startsWith('@') && part.length > 1) {
|
|
11590
|
-
scopes.push(part.slice(1).toLowerCase());
|
|
11591
|
-
} else {
|
|
11592
|
-
terms.push(part);
|
|
11593
|
-
}
|
|
11594
|
-
}
|
|
11595
|
-
|
|
11596
|
-
return {
|
|
11597
|
-
text: terms.join(' ').trim(),
|
|
11598
|
-
scopes: [...new Set(scopes)],
|
|
11599
|
-
};
|
|
11600
|
-
}
|
|
11601
|
-
|
|
11602
|
-
function getScopesForDoc(doc) {
|
|
11603
|
-
const source = (doc.id || doc.url || '').replace(/^\\/+/, '').toLowerCase();
|
|
11604
|
-
const segments = source.split('/').filter(Boolean);
|
|
11605
|
-
|
|
11606
|
-
if (segments.length <= 1) {
|
|
11607
|
-
return [];
|
|
11608
|
-
}
|
|
11609
|
-
|
|
11610
|
-
const scopes = [];
|
|
11611
|
-
let current = '';
|
|
11612
|
-
for (const segment of segments.slice(0, -1)) {
|
|
11613
|
-
current = current ? current + '/' + segment : segment;
|
|
11614
|
-
scopes.push(current);
|
|
11615
|
-
}
|
|
11616
|
-
|
|
11617
|
-
return scopes;
|
|
11618
|
-
}
|
|
11619
|
-
|
|
11620
|
-
function matchesScopes(doc, scopes) {
|
|
11621
|
-
if (!scopes.length) {
|
|
11622
|
-
return true;
|
|
11623
|
-
}
|
|
11624
|
-
|
|
11625
|
-
const docScopes = new Set(getScopesForDoc(doc));
|
|
11626
|
-
return scopes.some(scope => docScopes.has(scope));
|
|
11627
|
-
}
|
|
11628
|
-
|
|
11629
|
-
// Tokenizer for queries
|
|
11630
|
-
function tokenizeQuery(text) {
|
|
11631
|
-
const tokens = [];
|
|
11632
|
-
let current = '';
|
|
11633
|
-
|
|
11634
|
-
for (const char of text) {
|
|
11635
|
-
const isCjk = /[\\u4E00-\\u9FFF\\u3400-\\u4DBF\\u3040-\\u309F\\u30A0-\\u30FF\\uAC00-\\uD7AF]/.test(char);
|
|
11636
|
-
|
|
11637
|
-
if (isCjk) {
|
|
11638
|
-
if (current) {
|
|
11639
|
-
tokens.push(current.toLowerCase());
|
|
11640
|
-
current = '';
|
|
11641
|
-
}
|
|
11642
|
-
tokens.push(char);
|
|
11643
|
-
} else if (/[a-zA-Z0-9_]/.test(char)) {
|
|
11644
|
-
current += char;
|
|
11645
|
-
} else if (current) {
|
|
11646
|
-
tokens.push(current.toLowerCase());
|
|
11647
|
-
current = '';
|
|
11648
|
-
}
|
|
11649
|
-
}
|
|
11650
|
-
|
|
11651
|
-
if (current) {
|
|
11652
|
-
tokens.push(current.toLowerCase());
|
|
11653
|
-
}
|
|
11654
|
-
|
|
11655
|
-
return tokens;
|
|
11656
|
-
}
|
|
11657
|
-
|
|
11658
|
-
// BM25 scoring
|
|
11659
|
-
function computeIdf(df, docCount) {
|
|
11660
|
-
return Math.log((docCount - df + 0.5) / (df + 0.5) + 1.0);
|
|
11661
|
-
}
|
|
11662
|
-
|
|
11663
|
-
function getFieldBoost(field) {
|
|
11664
|
-
switch (field) {
|
|
11665
|
-
case 'Title': return 10.0;
|
|
11666
|
-
case 'Heading': return 5.0;
|
|
11667
|
-
case 'Body': return 1.0;
|
|
11668
|
-
case 'Code': return 0.5;
|
|
11669
|
-
default: return 1.0;
|
|
11670
|
-
}
|
|
11671
|
-
}
|
|
11672
|
-
|
|
11673
|
-
// Load the index
|
|
11674
|
-
async function loadIndex() {
|
|
11675
|
-
if (searchIndex) return searchIndex;
|
|
11676
|
-
if (indexPromise) return indexPromise;
|
|
11677
|
-
|
|
11678
|
-
indexPromise = fetch('${indexPath}')
|
|
11679
|
-
.then(res => res.json())
|
|
11680
|
-
.then(data => {
|
|
11681
|
-
searchIndex = data;
|
|
11682
|
-
return data;
|
|
11683
|
-
})
|
|
11684
|
-
.catch(err => {
|
|
11685
|
-
console.error('[ox-content] Failed to load search index:', err);
|
|
11686
|
-
return null;
|
|
11687
|
-
});
|
|
11688
|
-
|
|
11689
|
-
return indexPromise;
|
|
11690
|
-
}
|
|
11691
|
-
|
|
11692
|
-
// Search function
|
|
11693
|
-
export async function search(query, options = {}) {
|
|
11694
|
-
const index = await loadIndex();
|
|
11695
|
-
|
|
11696
|
-
if (!index) {
|
|
11697
|
-
return [];
|
|
11698
|
-
}
|
|
11699
|
-
|
|
11700
|
-
const parsedQuery = parseScopedQuery(query);
|
|
11701
|
-
|
|
11702
|
-
if (!parsedQuery.text && parsedQuery.scopes.length === 0) {
|
|
11703
|
-
return [];
|
|
11704
|
-
}
|
|
11705
|
-
|
|
11706
|
-
const limit = options.limit ?? searchOptions.limit;
|
|
11707
|
-
const prefix = options.prefix ?? searchOptions.prefix;
|
|
11708
|
-
const tokens = tokenizeQuery(parsedQuery.text);
|
|
11709
|
-
|
|
11710
|
-
const k1 = 1.2;
|
|
11711
|
-
const b = 0.75;
|
|
11712
|
-
const docScores = new Map();
|
|
11713
|
-
|
|
11714
|
-
if (tokens.length === 0) {
|
|
11715
|
-
index.documents.forEach((doc, docIdx) => {
|
|
11716
|
-
if (matchesScopes(doc, parsedQuery.scopes)) {
|
|
11717
|
-
docScores.set(docIdx, { score: 0, matches: new Set() });
|
|
11718
|
-
}
|
|
11719
|
-
});
|
|
11720
|
-
}
|
|
11721
|
-
|
|
11722
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
11723
|
-
const token = tokens[i];
|
|
11724
|
-
const isLast = i === tokens.length - 1;
|
|
11725
|
-
|
|
11726
|
-
// Find matching terms
|
|
11727
|
-
let matchingTerms = [];
|
|
11728
|
-
if (prefix && isLast && token.length >= 2) {
|
|
11729
|
-
matchingTerms = Object.keys(index.index).filter(term => term.startsWith(token));
|
|
11730
|
-
} else if (index.index[token]) {
|
|
11731
|
-
matchingTerms = [token];
|
|
11732
|
-
}
|
|
11733
|
-
|
|
11734
|
-
for (const term of matchingTerms) {
|
|
11735
|
-
const postings = index.index[term] || [];
|
|
11736
|
-
const df = index.df[term] || 1;
|
|
11737
|
-
const idf = computeIdf(df, index.doc_count);
|
|
11738
|
-
|
|
11739
|
-
for (const posting of postings) {
|
|
11740
|
-
const doc = index.documents[posting.doc_idx];
|
|
11741
|
-
if (!doc) continue;
|
|
11742
|
-
if (!matchesScopes(doc, parsedQuery.scopes)) continue;
|
|
11743
|
-
|
|
11744
|
-
const docLen = doc.body.length;
|
|
11745
|
-
const tf = posting.tf;
|
|
11746
|
-
const boost = getFieldBoost(posting.field);
|
|
11747
|
-
|
|
11748
|
-
const score = idf * ((tf * (k1 + 1.0)) / (tf + k1 * (1.0 - b + b * docLen / index.avg_dl))) * boost;
|
|
11749
|
-
|
|
11750
|
-
if (!docScores.has(posting.doc_idx)) {
|
|
11751
|
-
docScores.set(posting.doc_idx, { score: 0, matches: new Set() });
|
|
11752
|
-
}
|
|
11753
|
-
const entry = docScores.get(posting.doc_idx);
|
|
11754
|
-
entry.score += score;
|
|
11755
|
-
entry.matches.add(term);
|
|
11756
|
-
}
|
|
11757
|
-
}
|
|
11758
|
-
}
|
|
11759
|
-
|
|
11760
|
-
// Convert to results
|
|
11761
|
-
const results = Array.from(docScores.entries())
|
|
11762
|
-
.map(([docIdx, data]) => {
|
|
11763
|
-
const doc = index.documents[docIdx];
|
|
11764
|
-
const matches = Array.from(data.matches);
|
|
11765
|
-
const scopes = getScopesForDoc(doc);
|
|
11766
|
-
|
|
11767
|
-
// Generate snippet
|
|
11768
|
-
let snippet = '';
|
|
11769
|
-
if (doc.body) {
|
|
11770
|
-
const bodyLower = doc.body.toLowerCase();
|
|
11771
|
-
let firstPos = -1;
|
|
11772
|
-
for (const match of matches) {
|
|
11773
|
-
const pos = bodyLower.indexOf(match);
|
|
11774
|
-
if (pos !== -1 && (firstPos === -1 || pos < firstPos)) {
|
|
11775
|
-
firstPos = pos;
|
|
11776
|
-
}
|
|
11777
|
-
}
|
|
11778
|
-
|
|
11779
|
-
const start = firstPos === -1 ? 0 : Math.max(0, firstPos - 50);
|
|
11780
|
-
const end = Math.min(doc.body.length, start + 150);
|
|
11781
|
-
snippet = doc.body.slice(start, end);
|
|
11782
|
-
if (start > 0) snippet = '...' + snippet;
|
|
11783
|
-
if (end < doc.body.length) snippet = snippet + '...';
|
|
11784
|
-
}
|
|
11785
|
-
|
|
11786
|
-
return {
|
|
11787
|
-
id: doc.id,
|
|
11788
|
-
title: doc.title,
|
|
11789
|
-
url: doc.url,
|
|
11790
|
-
score: data.score,
|
|
11791
|
-
matches,
|
|
11792
|
-
snippet,
|
|
11793
|
-
scopes,
|
|
11794
|
-
};
|
|
11795
|
-
})
|
|
11796
|
-
.sort((a, b) => b.score - a.score || a.title.localeCompare(b.title))
|
|
11797
|
-
.slice(0, limit);
|
|
11798
|
-
|
|
11799
|
-
return results;
|
|
11800
|
-
}
|
|
11801
|
-
|
|
11802
|
-
export { searchOptions };
|
|
11803
|
-
export default { search, searchOptions, loadIndex };
|
|
11804
|
-
`;
|
|
10665
|
+
return importNapiModuleSync().generateSearchModule(JSON.stringify(options), indexPath);
|
|
11805
10666
|
}
|
|
11806
10667
|
//#endregion
|
|
11807
10668
|
//#region src/dev-server.ts
|
|
@@ -11867,12 +10728,12 @@ async function resolveMarkdownFile(url, srcDir) {
|
|
|
11867
10728
|
let relativePath;
|
|
11868
10729
|
if (pathname === "/") relativePath = "index.md";
|
|
11869
10730
|
else relativePath = pathname.slice(1) + ".md";
|
|
11870
|
-
const filePath = path$
|
|
10731
|
+
const filePath = path$1.join(srcDir, relativePath);
|
|
11871
10732
|
try {
|
|
11872
10733
|
await fs$1.access(filePath);
|
|
11873
10734
|
return filePath;
|
|
11874
10735
|
} catch {
|
|
11875
|
-
const indexPath = path$
|
|
10736
|
+
const indexPath = path$1.join(srcDir, pathname === "/" ? "" : pathname.slice(1), "index.md");
|
|
11876
10737
|
try {
|
|
11877
10738
|
await fs$1.access(indexPath);
|
|
11878
10739
|
return indexPath;
|
|
@@ -11916,7 +10777,7 @@ function invalidatePageCache(cache, filePath) {
|
|
|
11916
10777
|
async function resolveSiteName(options, root) {
|
|
11917
10778
|
if (options.ssg.siteName) return options.ssg.siteName;
|
|
11918
10779
|
try {
|
|
11919
|
-
const pkgPath = path$
|
|
10780
|
+
const pkgPath = path$1.join(root, "package.json");
|
|
11920
10781
|
const pkg = JSON.parse(await fs$1.readFile(pkgPath, "utf-8"));
|
|
11921
10782
|
if (pkg.name) return formatTitle(pkg.name);
|
|
11922
10783
|
} catch {}
|
|
@@ -11926,7 +10787,7 @@ async function resolveSiteName(options, root) {
|
|
|
11926
10787
|
* Render a single markdown page to full HTML.
|
|
11927
10788
|
*/
|
|
11928
10789
|
async function renderPage$1(filePath, options, navGroups, siteName, base, root) {
|
|
11929
|
-
const srcDir = path$
|
|
10790
|
+
const srcDir = path$1.resolve(root, options.srcDir);
|
|
11930
10791
|
resetTabGroupCounter();
|
|
11931
10792
|
resetIslandCounter();
|
|
11932
10793
|
const result = await transformMarkdown(await fs$1.readFile(filePath, "utf-8"), filePath, options, {
|
|
@@ -11934,6 +10795,7 @@ async function renderPage$1(filePath, options, navGroups, siteName, base, root)
|
|
|
11934
10795
|
baseUrl: base,
|
|
11935
10796
|
sourcePath: filePath
|
|
11936
10797
|
});
|
|
10798
|
+
const frontmatter = normalizeVitePressFrontmatter(result.frontmatter);
|
|
11937
10799
|
let transformedHtml = result.html;
|
|
11938
10800
|
const { html: protectedHtml, svgs: mermaidSvgs } = protectMermaidSvgs(transformedHtml);
|
|
11939
10801
|
transformedHtml = protectedHtml;
|
|
@@ -11947,19 +10809,19 @@ async function renderPage$1(filePath, options, navGroups, siteName, base, root)
|
|
|
11947
10809
|
});
|
|
11948
10810
|
if (hasIslands(transformedHtml)) transformedHtml = (await transformIslands(transformedHtml)).html;
|
|
11949
10811
|
transformedHtml = restoreMermaidSvgs(transformedHtml, mermaidSvgs);
|
|
11950
|
-
const title = extractTitle$1(transformedHtml,
|
|
11951
|
-
const description =
|
|
10812
|
+
const title = extractTitle$1(transformedHtml, frontmatter);
|
|
10813
|
+
const description = frontmatter.description;
|
|
11952
10814
|
let entryPage;
|
|
11953
|
-
if (
|
|
11954
|
-
hero:
|
|
11955
|
-
features:
|
|
10815
|
+
if (frontmatter.layout === "entry") entryPage = {
|
|
10816
|
+
hero: frontmatter.hero,
|
|
10817
|
+
features: frontmatter.features
|
|
11956
10818
|
};
|
|
11957
10819
|
let html = await generateHtmlPage({
|
|
11958
10820
|
title,
|
|
11959
10821
|
description,
|
|
11960
10822
|
content: transformedHtml,
|
|
11961
10823
|
toc: result.toc,
|
|
11962
|
-
frontmatter
|
|
10824
|
+
frontmatter,
|
|
11963
10825
|
path: getUrlPath$1(filePath, srcDir),
|
|
11964
10826
|
href: getUrlPath$1(filePath, srcDir) || "/",
|
|
11965
10827
|
entryPage
|
|
@@ -11971,7 +10833,7 @@ async function renderPage$1(filePath, options, navGroups, siteName, base, root)
|
|
|
11971
10833
|
* Create the dev server middleware for SSG page serving.
|
|
11972
10834
|
*/
|
|
11973
10835
|
function createDevServerMiddleware(options, root, cache) {
|
|
11974
|
-
const srcDir = path$
|
|
10836
|
+
const srcDir = path$1.resolve(root, options.srcDir);
|
|
11975
10837
|
const base = options.base.endsWith("/") ? options.base : options.base + "/";
|
|
11976
10838
|
return async (req, res, next) => {
|
|
11977
10839
|
const url = req.url;
|
|
@@ -11990,7 +10852,10 @@ function createDevServerMiddleware(options, root, cache) {
|
|
|
11990
10852
|
return;
|
|
11991
10853
|
}
|
|
11992
10854
|
if (!cache.siteName) cache.siteName = await resolveSiteName(options, root);
|
|
11993
|
-
if (!cache.navGroups)
|
|
10855
|
+
if (!cache.navGroups) {
|
|
10856
|
+
const markdownFiles = await collectMarkdownFiles$1(srcDir);
|
|
10857
|
+
cache.navGroups = resolveNavigationGroups(options.ssg.navigation, base, options.ssg.extension) ?? (options.ssg.theme?.sidebar.length ? buildThemeNavItems(options.ssg.theme.sidebar, base, options.ssg.extension) : buildNavItems(markdownFiles, srcDir, base, options.ssg.extension));
|
|
10858
|
+
}
|
|
11994
10859
|
const html = await renderPage$1(filePath, options, cache.navGroups, cache.siteName, base, root);
|
|
11995
10860
|
cache.pages.set(filePath, html);
|
|
11996
10861
|
res.setHeader("Content-Type", "text/html");
|
|
@@ -12036,7 +10901,7 @@ function extractTitle(content, frontmatter) {
|
|
|
12036
10901
|
return match ? match[1].trim() : "";
|
|
12037
10902
|
}
|
|
12038
10903
|
function getUrlPath(filePath, srcDir) {
|
|
12039
|
-
let rel = path$
|
|
10904
|
+
let rel = path$1.relative(srcDir, filePath).replace(/\\/g, "/");
|
|
12040
10905
|
rel = rel.replace(/\.md$/, "");
|
|
12041
10906
|
if (rel === "index") return "/";
|
|
12042
10907
|
if (rel.endsWith("/index")) rel = rel.slice(0, -6);
|
|
@@ -12076,7 +10941,7 @@ function validatePage(page, options) {
|
|
|
12076
10941
|
return warnings;
|
|
12077
10942
|
}
|
|
12078
10943
|
async function collectPages(options, root) {
|
|
12079
|
-
const srcDir = path$
|
|
10944
|
+
const srcDir = path$1.resolve(root, options.srcDir);
|
|
12080
10945
|
const files = await glob("**/*.md", {
|
|
12081
10946
|
cwd: srcDir,
|
|
12082
10947
|
absolute: true
|
|
@@ -12085,7 +10950,7 @@ async function collectPages(options, root) {
|
|
|
12085
10950
|
const generateOgImage = options.ogImage || options.ssg.generateOgImage;
|
|
12086
10951
|
for (const file of files.sort()) {
|
|
12087
10952
|
const content = fs$2.readFileSync(file, "utf-8");
|
|
12088
|
-
const frontmatter = parseFrontmatter(content);
|
|
10953
|
+
const frontmatter = normalizeVitePressFrontmatter(parseFrontmatter(content));
|
|
12089
10954
|
if (frontmatter.layout === "entry") continue;
|
|
12090
10955
|
const title = extractTitle(content, frontmatter);
|
|
12091
10956
|
const description = typeof frontmatter.description === "string" ? frontmatter.description : "";
|
|
@@ -12094,7 +10959,7 @@ async function collectPages(options, root) {
|
|
|
12094
10959
|
const urlPath = getUrlPath(file, srcDir);
|
|
12095
10960
|
const ogImageUrl = computeOgImageUrl(urlPath, options.base, options.ssg.siteUrl, generateOgImage, options.ssg.ogImage);
|
|
12096
10961
|
const page = {
|
|
12097
|
-
path: path$
|
|
10962
|
+
path: path$1.relative(srcDir, file),
|
|
12098
10963
|
urlPath,
|
|
12099
10964
|
title,
|
|
12100
10965
|
description,
|
|
@@ -12407,7 +11272,7 @@ function createI18nPlugin(resolvedOptions) {
|
|
|
12407
11272
|
},
|
|
12408
11273
|
async buildStart() {
|
|
12409
11274
|
if (!i18nOptions || !i18nOptions.check) return;
|
|
12410
|
-
const dictDir = path$
|
|
11275
|
+
const dictDir = path$1.resolve(root, i18nOptions.dir);
|
|
12411
11276
|
if (!fs$2.existsSync(dictDir)) {
|
|
12412
11277
|
console.warn(`[ox-content:i18n] Dictionary directory not found: ${dictDir}`);
|
|
12413
11278
|
return;
|
|
@@ -12429,7 +11294,7 @@ function createI18nPlugin(resolvedOptions) {
|
|
|
12429
11294
|
},
|
|
12430
11295
|
configureServer(server) {
|
|
12431
11296
|
if (!i18nOptions) return;
|
|
12432
|
-
const dictDir = path$
|
|
11297
|
+
const dictDir = path$1.resolve(root, i18nOptions.dir);
|
|
12433
11298
|
if (fs$2.existsSync(dictDir)) {
|
|
12434
11299
|
server.watcher.add(dictDir);
|
|
12435
11300
|
server.watcher.on("change", (filePath) => {
|
|
@@ -12456,206 +11321,31 @@ function createI18nPlugin(resolvedOptions) {
|
|
|
12456
11321
|
* Generates the virtual module for i18n configuration.
|
|
12457
11322
|
*/
|
|
12458
11323
|
function generateI18nModule(options, root) {
|
|
12459
|
-
const dictDir = path$
|
|
12460
|
-
const
|
|
12461
|
-
|
|
12462
|
-
|
|
11324
|
+
const dictDir = path$1.resolve(root, options.dir);
|
|
11325
|
+
const config = {
|
|
11326
|
+
defaultLocale: options.defaultLocale,
|
|
11327
|
+
locales: options.locales,
|
|
11328
|
+
hideDefaultLocale: options.hideDefaultLocale
|
|
11329
|
+
};
|
|
12463
11330
|
try {
|
|
12464
11331
|
const napi = __require("@ox-content/napi");
|
|
12465
|
-
if (napi.
|
|
12466
|
-
|
|
12467
|
-
|
|
12468
|
-
} else dictionariesCode = JSON.stringify(loadDictionariesFallback(options, dictDir));
|
|
12469
|
-
} catch {
|
|
12470
|
-
try {
|
|
12471
|
-
dictionariesCode = JSON.stringify(loadDictionariesFallback(options, dictDir));
|
|
12472
|
-
} catch {}
|
|
12473
|
-
}
|
|
12474
|
-
return `
|
|
12475
|
-
export const i18nConfig = {
|
|
12476
|
-
enabled: true,
|
|
12477
|
-
defaultLocale: ${defaultLocale},
|
|
12478
|
-
locales: ${localesJson},
|
|
12479
|
-
hideDefaultLocale: ${JSON.stringify(options.hideDefaultLocale)},
|
|
12480
|
-
};
|
|
12481
|
-
|
|
12482
|
-
export const dictionaries = ${dictionariesCode};
|
|
12483
|
-
|
|
12484
|
-
export function t(key, params, locale) {
|
|
12485
|
-
const dict = dictionaries[locale || i18nConfig.defaultLocale] || {};
|
|
12486
|
-
let message = dict[key];
|
|
12487
|
-
if (!message) {
|
|
12488
|
-
const fallback = dictionaries[i18nConfig.defaultLocale] || {};
|
|
12489
|
-
message = fallback[key] || key;
|
|
12490
|
-
}
|
|
12491
|
-
if (params) {
|
|
12492
|
-
for (const [k, v] of Object.entries(params)) {
|
|
12493
|
-
message = message.split('{$' + k + '}').join(String(v));
|
|
12494
|
-
}
|
|
12495
|
-
}
|
|
12496
|
-
return message;
|
|
12497
|
-
}
|
|
12498
|
-
|
|
12499
|
-
export function getLocaleFromPath(pathname) {
|
|
12500
|
-
const match = pathname.match(new RegExp('^/([A-Za-z]{2,3}(?:-[A-Za-z0-9]+)*)(/|$)'));
|
|
12501
|
-
if (match) {
|
|
12502
|
-
const code = match[1];
|
|
12503
|
-
if (i18nConfig.locales.some(l => l.code === code)) {
|
|
12504
|
-
return code;
|
|
12505
|
-
}
|
|
12506
|
-
}
|
|
12507
|
-
return i18nConfig.defaultLocale;
|
|
12508
|
-
}
|
|
12509
|
-
|
|
12510
|
-
export function localePath(pathname, locale) {
|
|
12511
|
-
const current = getLocaleFromPath(pathname);
|
|
12512
|
-
let clean = pathname;
|
|
12513
|
-
if (current !== i18nConfig.defaultLocale || !i18nConfig.hideDefaultLocale) {
|
|
12514
|
-
const prefix = '/' + current;
|
|
12515
|
-
if (clean === prefix) clean = '/';
|
|
12516
|
-
else if (clean.startsWith(prefix + '/')) clean = clean.slice(prefix.length);
|
|
12517
|
-
}
|
|
12518
|
-
if (locale === i18nConfig.defaultLocale && i18nConfig.hideDefaultLocale) {
|
|
12519
|
-
return clean || '/';
|
|
12520
|
-
}
|
|
12521
|
-
return '/' + locale + (clean.startsWith('/') ? clean : '/' + clean);
|
|
12522
|
-
}
|
|
12523
|
-
|
|
12524
|
-
const formatterCache = new Map();
|
|
12525
|
-
|
|
12526
|
-
function getFormatter(kind, locale, options) {
|
|
12527
|
-
const key = kind + ':' + locale + ':' + JSON.stringify(options || {});
|
|
12528
|
-
if (!formatterCache.has(key)) {
|
|
12529
|
-
formatterCache.set(key, new Intl[kind](locale, options));
|
|
12530
|
-
}
|
|
12531
|
-
return formatterCache.get(key);
|
|
12532
|
-
}
|
|
12533
|
-
|
|
12534
|
-
export function getLocaleMeta(locale) {
|
|
12535
|
-
const code = locale || i18nConfig.defaultLocale;
|
|
12536
|
-
return i18nConfig.locales.find(l => l.code === code) || { code, name: code, dir: 'ltr' };
|
|
12537
|
-
}
|
|
12538
|
-
|
|
12539
|
-
export function formatDate(value, options, locale) {
|
|
12540
|
-
return getFormatter('DateTimeFormat', locale || i18nConfig.defaultLocale, options).format(
|
|
12541
|
-
value instanceof Date ? value : new Date(value),
|
|
12542
|
-
);
|
|
12543
|
-
}
|
|
12544
|
-
|
|
12545
|
-
export function formatDateParts(value, options, locale) {
|
|
12546
|
-
return getFormatter('DateTimeFormat', locale || i18nConfig.defaultLocale, options).formatToParts(
|
|
12547
|
-
value instanceof Date ? value : new Date(value),
|
|
12548
|
-
);
|
|
12549
|
-
}
|
|
12550
|
-
|
|
12551
|
-
export function formatNumber(value, options, locale) {
|
|
12552
|
-
return getFormatter('NumberFormat', locale || i18nConfig.defaultLocale, options).format(value);
|
|
12553
|
-
}
|
|
12554
|
-
|
|
12555
|
-
export function formatNumberParts(value, options, locale) {
|
|
12556
|
-
return getFormatter('NumberFormat', locale || i18nConfig.defaultLocale, options).formatToParts(value);
|
|
12557
|
-
}
|
|
12558
|
-
|
|
12559
|
-
export function formatRelativeTime(value, unit, options, locale) {
|
|
12560
|
-
return getFormatter('RelativeTimeFormat', locale || i18nConfig.defaultLocale, options).format(value, unit);
|
|
12561
|
-
}
|
|
12562
|
-
|
|
12563
|
-
export function formatList(values, options, locale) {
|
|
12564
|
-
return getFormatter('ListFormat', locale || i18nConfig.defaultLocale, options).format(values);
|
|
12565
|
-
}
|
|
12566
|
-
|
|
12567
|
-
export function formatListParts(values, options, locale) {
|
|
12568
|
-
return getFormatter('ListFormat', locale || i18nConfig.defaultLocale, options).formatToParts(values);
|
|
12569
|
-
}
|
|
12570
|
-
|
|
12571
|
-
export function formatDisplayName(value, type, options, locale) {
|
|
12572
|
-
if (!Intl.DisplayNames) return String(value);
|
|
12573
|
-
const displayType = type || 'language';
|
|
12574
|
-
return getFormatter('DisplayNames', locale || i18nConfig.defaultLocale, { type: displayType, ...options }).of(value) || String(value);
|
|
12575
|
-
}
|
|
12576
|
-
|
|
12577
|
-
export function createIntl(locale, defaults = {}) {
|
|
12578
|
-
const meta = getLocaleMeta(locale);
|
|
12579
|
-
const code = meta.code;
|
|
12580
|
-
return {
|
|
12581
|
-
locale: code,
|
|
12582
|
-
meta,
|
|
12583
|
-
dir: meta.dir || 'ltr',
|
|
12584
|
-
date: (value, options) => formatDate(value, { ...defaults.date, ...options }, code),
|
|
12585
|
-
dateParts: (value, options) => formatDateParts(value, { ...defaults.date, ...options }, code),
|
|
12586
|
-
number: (value, options) => formatNumber(value, { ...defaults.number, ...options }, code),
|
|
12587
|
-
numberParts: (value, options) => formatNumberParts(value, { ...defaults.number, ...options }, code),
|
|
12588
|
-
relativeTime: (value, unit, options) => formatRelativeTime(value, unit, { ...defaults.relativeTime, ...options }, code),
|
|
12589
|
-
list: (values, options) => formatList(values, { ...defaults.list, ...options }, code),
|
|
12590
|
-
listParts: (values, options) => formatListParts(values, { ...defaults.list, ...options }, code),
|
|
12591
|
-
displayName: (value, type, options) => formatDisplayName(value, type, { ...defaults.displayName, ...options }, code),
|
|
12592
|
-
};
|
|
12593
|
-
}
|
|
12594
|
-
|
|
12595
|
-
export default {
|
|
12596
|
-
i18nConfig,
|
|
12597
|
-
dictionaries,
|
|
12598
|
-
t,
|
|
12599
|
-
getLocaleFromPath,
|
|
12600
|
-
localePath,
|
|
12601
|
-
getLocaleMeta,
|
|
12602
|
-
createIntl,
|
|
12603
|
-
formatDate,
|
|
12604
|
-
formatDateParts,
|
|
12605
|
-
formatNumber,
|
|
12606
|
-
formatNumberParts,
|
|
12607
|
-
formatRelativeTime,
|
|
12608
|
-
formatList,
|
|
12609
|
-
formatListParts,
|
|
12610
|
-
formatDisplayName,
|
|
12611
|
-
};
|
|
12612
|
-
`;
|
|
12613
|
-
}
|
|
12614
|
-
/**
|
|
12615
|
-
* Flattens a nested object into dot-separated keys.
|
|
12616
|
-
*/
|
|
12617
|
-
function flattenObject(obj, prefix, result) {
|
|
12618
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
12619
|
-
const fullKey = `${prefix}.${key}`;
|
|
12620
|
-
if (typeof value === "string") result[fullKey] = value;
|
|
12621
|
-
else if (typeof value === "object" && value !== null && !Array.isArray(value)) flattenObject(value, fullKey, result);
|
|
12622
|
-
else result[fullKey] = String(value);
|
|
12623
|
-
}
|
|
12624
|
-
}
|
|
12625
|
-
/**
|
|
12626
|
-
* Fallback dictionary loading using TS-based JSON file reading.
|
|
12627
|
-
*/
|
|
12628
|
-
function loadDictionariesFallback(options, dictDir) {
|
|
12629
|
-
const dictData = {};
|
|
12630
|
-
for (const locale of options.locales) {
|
|
12631
|
-
const localeDir = path$2.join(dictDir, locale.code);
|
|
12632
|
-
if (!fs$2.existsSync(localeDir)) continue;
|
|
12633
|
-
const files = fs$2.readdirSync(localeDir);
|
|
12634
|
-
const localeDict = {};
|
|
12635
|
-
for (const file of files) {
|
|
12636
|
-
if (!file.endsWith(".json")) continue;
|
|
12637
|
-
const filePath = path$2.join(localeDir, file);
|
|
12638
|
-
const content = fs$2.readFileSync(filePath, "utf-8");
|
|
12639
|
-
const namespace = path$2.basename(file, ".json");
|
|
12640
|
-
try {
|
|
12641
|
-
flattenObject(JSON.parse(content), namespace, localeDict);
|
|
12642
|
-
} catch {}
|
|
12643
|
-
}
|
|
12644
|
-
dictData[locale.code] = localeDict;
|
|
11332
|
+
if (typeof napi.generateI18nModule === "function") return napi.generateI18nModule(dictDir, config);
|
|
11333
|
+
} catch (error) {
|
|
11334
|
+
throw new Error(`[ox-content:i18n] Failed to load @ox-content/napi for i18n module generation: ${String(error)}`);
|
|
12645
11335
|
}
|
|
12646
|
-
|
|
11336
|
+
throw new Error("[ox-content:i18n] @ox-content/napi does not expose generateI18nModule. Please rebuild the NAPI package.");
|
|
12647
11337
|
}
|
|
12648
11338
|
/**
|
|
12649
11339
|
* Collects translation keys from source files using NAPI extractTranslationKeys.
|
|
12650
11340
|
*/
|
|
12651
11341
|
function collectKeysFromSource(root, extractTranslationKeys, options) {
|
|
12652
|
-
const srcDir = path$
|
|
11342
|
+
const srcDir = path$1.resolve(root, "src");
|
|
12653
11343
|
const keys = /* @__PURE__ */ new Set();
|
|
12654
11344
|
if (fs$2.existsSync(srcDir)) walkDir(srcDir, /\.(ts|tsx|js|jsx)$/, (filePath) => {
|
|
12655
11345
|
const usages = extractTranslationKeys(fs$2.readFileSync(filePath, "utf-8"), filePath, options.functionNames);
|
|
12656
11346
|
for (const usage of usages) keys.add(usage.key);
|
|
12657
11347
|
});
|
|
12658
|
-
const contentDir = path$
|
|
11348
|
+
const contentDir = path$1.resolve(root, "content");
|
|
12659
11349
|
if (fs$2.existsSync(contentDir)) {
|
|
12660
11350
|
const tPattern = /\{\{t\(['"]([^'"]+)['"]\)\}\}/g;
|
|
12661
11351
|
walkDir(contentDir, /\.(md|mdx)$/, (filePath) => {
|
|
@@ -12673,7 +11363,7 @@ function collectKeysFromSource(root, extractTranslationKeys, options) {
|
|
|
12673
11363
|
function walkDir(dir, pattern, callback) {
|
|
12674
11364
|
const entries = fs$2.readdirSync(dir, { withFileTypes: true });
|
|
12675
11365
|
for (const entry of entries) {
|
|
12676
|
-
const fullPath = path$
|
|
11366
|
+
const fullPath = path$1.join(dir, entry.name);
|
|
12677
11367
|
if (entry.isDirectory()) {
|
|
12678
11368
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
12679
11369
|
walkDir(fullPath, pattern, callback);
|
|
@@ -12926,7 +11616,7 @@ const DEFAULT_LINT_FILE_EXCLUDE = [
|
|
|
12926
11616
|
*/
|
|
12927
11617
|
function shouldLintMarkdownFile(filePath, options = {}) {
|
|
12928
11618
|
const resolvedOptions = resolveMarkdownLintFileOptions(options);
|
|
12929
|
-
return shouldLintAbsoluteFile(path
|
|
11619
|
+
return shouldLintAbsoluteFile(path.resolve(resolvedOptions.cwd, filePath), resolvedOptions);
|
|
12930
11620
|
}
|
|
12931
11621
|
/**
|
|
12932
11622
|
* Lints a single Markdown file using project-style include/exclude settings.
|
|
@@ -12936,7 +11626,7 @@ function shouldLintMarkdownFile(filePath, options = {}) {
|
|
|
12936
11626
|
*/
|
|
12937
11627
|
async function lintMarkdownFile(filePath, options = {}) {
|
|
12938
11628
|
const resolvedOptions = resolveMarkdownLintFileOptions(options);
|
|
12939
|
-
return lintMarkdownFileWithResolvedOptions(path
|
|
11629
|
+
return lintMarkdownFileWithResolvedOptions(path.resolve(resolvedOptions.cwd, filePath), resolvedOptions);
|
|
12940
11630
|
}
|
|
12941
11631
|
/**
|
|
12942
11632
|
* Lints all Markdown files matched by the configured include/exclude patterns.
|
|
@@ -12967,7 +11657,7 @@ async function lintMarkdownFiles(options = {}) {
|
|
|
12967
11657
|
}
|
|
12968
11658
|
function resolveMarkdownLintFileOptions(options) {
|
|
12969
11659
|
return {
|
|
12970
|
-
cwd: path
|
|
11660
|
+
cwd: path.resolve(options.cwd ?? process.cwd()),
|
|
12971
11661
|
exclude: [...new Set([...options.exclude ?? DEFAULT_LINT_FILE_EXCLUDE, ...options.ignore ?? []])],
|
|
12972
11662
|
include: [...new Set(options.include ?? DEFAULT_LINT_FILE_INCLUDE)],
|
|
12973
11663
|
lintOptions: {
|
|
@@ -12978,8 +11668,8 @@ function resolveMarkdownLintFileOptions(options) {
|
|
|
12978
11668
|
};
|
|
12979
11669
|
}
|
|
12980
11670
|
async function lintMarkdownFileWithResolvedOptions(filePath, options) {
|
|
12981
|
-
const absoluteFilePath = path
|
|
12982
|
-
const relativePath = normalizePath(path
|
|
11671
|
+
const absoluteFilePath = path.resolve(filePath);
|
|
11672
|
+
const relativePath = normalizePath(path.relative(options.cwd, absoluteFilePath));
|
|
12983
11673
|
if (!shouldLintAbsoluteFile(absoluteFilePath, options)) return {
|
|
12984
11674
|
...createEmptyLintResult(),
|
|
12985
11675
|
filePath: absoluteFilePath,
|
|
@@ -13003,26 +11693,26 @@ async function collectMarkdownLintFileEntries(options) {
|
|
|
13003
11693
|
nodir: true
|
|
13004
11694
|
});
|
|
13005
11695
|
for (const filePath of matches) {
|
|
13006
|
-
const absoluteFilePath = path
|
|
11696
|
+
const absoluteFilePath = path.resolve(filePath);
|
|
13007
11697
|
if (shouldLintAbsoluteFile(absoluteFilePath, options)) files.set(absoluteFilePath, {
|
|
13008
11698
|
filePath: absoluteFilePath,
|
|
13009
|
-
relativePath: normalizePath(path
|
|
11699
|
+
relativePath: normalizePath(path.relative(options.cwd, absoluteFilePath))
|
|
13010
11700
|
});
|
|
13011
11701
|
}
|
|
13012
11702
|
}
|
|
13013
11703
|
return [...files.values()].sort((left, right) => left.filePath.localeCompare(right.filePath));
|
|
13014
11704
|
}
|
|
13015
11705
|
function shouldLintAbsoluteFile(filePath, options) {
|
|
13016
|
-
const absolutePath = normalizePath(path
|
|
13017
|
-
const relativePath = normalizePath(path
|
|
11706
|
+
const absolutePath = normalizePath(path.resolve(filePath));
|
|
11707
|
+
const relativePath = normalizePath(path.relative(options.cwd, absolutePath));
|
|
13018
11708
|
const matches = (patterns) => patterns.some((pattern) => {
|
|
13019
11709
|
const normalizedPattern = normalizePath(pattern);
|
|
13020
|
-
return path
|
|
11710
|
+
return path.matchesGlob(relativePath, normalizedPattern) || path.matchesGlob(absolutePath, normalizedPattern);
|
|
13021
11711
|
});
|
|
13022
11712
|
return matches(options.include) && !matches(options.exclude);
|
|
13023
11713
|
}
|
|
13024
11714
|
function normalizePath(value) {
|
|
13025
|
-
return value.split(path
|
|
11715
|
+
return value.split(path.sep).join("/");
|
|
13026
11716
|
}
|
|
13027
11717
|
function createEmptyLintResult() {
|
|
13028
11718
|
return {
|
|
@@ -13607,8 +12297,8 @@ function oxContent(options = {}) {
|
|
|
13607
12297
|
async function regenerateDocs(root) {
|
|
13608
12298
|
const docsOptions = resolvedOptions.docs;
|
|
13609
12299
|
if (!docsOptions || !docsOptions.enabled) return 0;
|
|
13610
|
-
const srcDirs = docsOptions.src.map((src) => path$
|
|
13611
|
-
const outDir = path$
|
|
12300
|
+
const srcDirs = docsOptions.src.map((src) => path$1.resolve(root, src));
|
|
12301
|
+
const outDir = path$1.resolve(root, docsOptions.out);
|
|
13612
12302
|
const extracted = await extractDocs(srcDirs, docsOptions);
|
|
13613
12303
|
const generated = generateMarkdown(extracted, docsOptions);
|
|
13614
12304
|
await writeDocs(generated, outDir, extracted, docsOptions);
|
|
@@ -13677,7 +12367,7 @@ function oxContent(options = {}) {
|
|
|
13677
12367
|
const docsOptions = resolvedOptions.docs;
|
|
13678
12368
|
if (!docsOptions || !docsOptions.enabled) return;
|
|
13679
12369
|
const root = config?.root || process.cwd();
|
|
13680
|
-
const srcDirs = docsOptions.src.map((src) => path$
|
|
12370
|
+
const srcDirs = docsOptions.src.map((src) => path$1.resolve(root, src));
|
|
13681
12371
|
for (const srcDir of srcDirs) devServer.watcher.add(srcDir);
|
|
13682
12372
|
devServer.watcher.on("all", async (event, file) => {
|
|
13683
12373
|
if (event !== "add" && event !== "change" && event !== "unlink") return;
|
|
@@ -13693,7 +12383,7 @@ function oxContent(options = {}) {
|
|
|
13693
12383
|
configureServer(devServer) {
|
|
13694
12384
|
if (!resolvedOptions.ssg.enabled) return;
|
|
13695
12385
|
const root = config?.root || process.cwd();
|
|
13696
|
-
const srcDir = path$
|
|
12386
|
+
const srcDir = path$1.resolve(root, resolvedOptions.srcDir);
|
|
13697
12387
|
devServer.middlewares.use(createDevServerMiddleware(resolvedOptions, root, ssgDevCache));
|
|
13698
12388
|
devServer.watcher.on("add", (file) => {
|
|
13699
12389
|
if (file.startsWith(srcDir) && file.endsWith(".md")) {
|
|
@@ -13760,7 +12450,7 @@ function oxContent(options = {}) {
|
|
|
13760
12450
|
async buildStart() {
|
|
13761
12451
|
if (!resolvedOptions.search.enabled) return;
|
|
13762
12452
|
const root = config?.root || process.cwd();
|
|
13763
|
-
const srcDir = path$
|
|
12453
|
+
const srcDir = path$1.resolve(root, resolvedOptions.srcDir);
|
|
13764
12454
|
try {
|
|
13765
12455
|
searchIndexJson = await buildSearchIndex(srcDir, resolvedOptions.base);
|
|
13766
12456
|
console.log("[ox-content] Search index built");
|
|
@@ -13771,10 +12461,10 @@ function oxContent(options = {}) {
|
|
|
13771
12461
|
async closeBundle() {
|
|
13772
12462
|
if (!resolvedOptions.search.enabled || !searchIndexJson) return;
|
|
13773
12463
|
const root = config?.root || process.cwd();
|
|
13774
|
-
const outDir = path$
|
|
12464
|
+
const outDir = path$1.resolve(root, resolvedOptions.outDir);
|
|
13775
12465
|
try {
|
|
13776
12466
|
await writeSearchIndex(searchIndexJson, outDir);
|
|
13777
|
-
console.log("[ox-content] Search index written to", path$
|
|
12467
|
+
console.log("[ox-content] Search index written to", path$1.join(outDir, "search-index.json"));
|
|
13778
12468
|
} catch (err) {
|
|
13779
12469
|
console.warn("[ox-content] Failed to write search index:", err);
|
|
13780
12470
|
}
|
|
@@ -13888,6 +12578,6 @@ function normalizeRuntimeBase(base) {
|
|
|
13888
12578
|
return withLeading.endsWith("/") ? withLeading : `${withLeading}/`;
|
|
13889
12579
|
}
|
|
13890
12580
|
//#endregion
|
|
13891
|
-
export { DEFAULT_HTML_TEMPLATE, DefaultTheme, Fragment, buildSearchIndex, buildSsg, clearRenderContext, collectGitHubRepos, collectOgpUrls, createI18nPlugin, createMarkdownEnvironment, createTheme, defaultTheme, defineTheme, each, extractDocs, extractIslandInfo, extractVideoId, fetchOgpData, fetchRepoData, generateFrontmatterTypes, generateHydrationScript, generateMarkdown, generateOgImages, generateTabsCSS, generateTypes, generateVirtualModule, hasIslands, inferType, jsx, jsxs, lintMarkdown, lintMarkdownAsync, lintMarkdownFile, lintMarkdownFiles, mergeThemes, mermaidClientScript, oxContent, prefetchGitHubRepos, prefetchOgpData, raw, renderAllPages, renderPage, renderToString, resolveDocsOptions, resolveI18nOptions, resolveOgImageOptions, resolveSearchOptions, resolveSsgOptions, resolveTheme, setRenderContext, shouldLintMarkdownFile, transformAllPlugins, transformGitHub, transformIslands, transformMarkdown, transformMermaidStatic, transformOgp, transformTabs, transformYouTube, useIsActive, useNav, usePageProps, useRenderContext, useSiteConfig, when, writeDocs, writeSearchIndex };
|
|
12581
|
+
export { DEFAULT_HTML_TEMPLATE, DefaultTheme, Fragment, buildSearchIndex, buildSsg, clearRenderContext, collectGitHubRepos, collectOgpUrls, convertVitePressNav, convertVitePressSidebar, createI18nPlugin, createMarkdownEnvironment, createTheme, defaultTheme, defineTheme, each, extractDocs, extractIslandInfo, extractVideoId, fetchOgpData, fetchRepoData, fromVitePressConfig, generateFrontmatterTypes, generateHydrationScript, generateMarkdown, generateOgImages, generateTabsCSS, generateTypes, generateVirtualModule, generateVitePressMigrationConfig, hasIslands, inferType, jsx, jsxs, lintMarkdown, lintMarkdownAsync, lintMarkdownFile, lintMarkdownFiles, mergeThemes, mermaidClientScript, normalizeVitePressFrontmatter, oxContent, prefetchGitHubRepos, prefetchOgpData, raw, renderAllPages, renderPage, renderToString, resolveDocsOptions, resolveI18nOptions, resolveOgImageOptions, resolveSearchOptions, resolveSsgOptions, resolveTheme, setRenderContext, shouldLintMarkdownFile, transformAllPlugins, transformGitHub, transformIslands, transformMarkdown, transformMermaidStatic, transformOgp, transformTabs, transformYouTube, useIsActive, useNav, usePageProps, useRenderContext, useSiteConfig, when, writeDocs, writeSearchIndex };
|
|
13892
12582
|
|
|
13893
12583
|
//# sourceMappingURL=index.mjs.map
|