@ox-content/vite-plugin 2.7.0 → 2.9.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.cjs +8 -2
- package/dist/github.cjs.map +1 -1
- package/dist/github.mjs +299 -2
- package/dist/github.mjs.map +1 -0
- package/dist/index.cjs +99 -1088
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +5 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +180 -1206
- 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.cjs +24 -3
- package/dist/ogp.cjs.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/youtube.mjs +117 -2
- package/dist/youtube.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/github2.mjs +0 -286
- package/dist/github2.mjs.map +0 -1
- package/dist/mermaid2.mjs +0 -2
- package/dist/ogp2.mjs +0 -279
- 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,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as transformYouTube, t as extractVideoId } from "./
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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
6
|
import { createRequire } from "node:module";
|
|
7
|
-
import * as path$
|
|
8
|
-
import path from "path";
|
|
7
|
+
import * as path$1 from "path";
|
|
9
8
|
import { unified } from "unified";
|
|
10
9
|
import rehypeParse from "rehype-parse";
|
|
11
10
|
import rehypeStringify from "rehype-stringify";
|
|
12
11
|
import { createHighlighter } from "shiki";
|
|
13
|
-
import * as path
|
|
12
|
+
import * as path from "node:path";
|
|
14
13
|
import { dirname, join } from "node:path";
|
|
15
14
|
import * as fs$2 from "fs";
|
|
16
|
-
import { createHash } from "node:crypto";
|
|
17
15
|
import * as fs$1 from "fs/promises";
|
|
18
16
|
import { glob } from "glob";
|
|
19
17
|
import * as crypto from "crypto";
|
|
20
18
|
import * as fs from "node:fs/promises";
|
|
21
19
|
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
20
|
//#region src/environment.ts
|
|
58
21
|
/**
|
|
59
22
|
* Creates the Markdown processing environment configuration.
|
|
@@ -7112,241 +7075,11 @@ if (import.meta.hot) {
|
|
|
7112
7075
|
}
|
|
7113
7076
|
//#endregion
|
|
7114
7077
|
//#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
7078
|
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;
|
|
7079
|
+
return importNapiModuleSync().generateDocsNavMetadata(docs.map((doc) => doc.file), basePath);
|
|
7279
7080
|
}
|
|
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
7081
|
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
|
-
`;
|
|
7082
|
+
return importNapiModuleSync().generateDocsNavCode(navItems, exportName);
|
|
7350
7083
|
}
|
|
7351
7084
|
//#endregion
|
|
7352
7085
|
//#region src/docs.ts
|
|
@@ -7673,86 +7406,6 @@ function buildDocsData(docs) {
|
|
|
7673
7406
|
}))
|
|
7674
7407
|
};
|
|
7675
7408
|
}
|
|
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
7409
|
/**
|
|
7757
7410
|
* Extracts JSDoc documentation from source files in specified directories.
|
|
7758
7411
|
*
|
|
@@ -7819,13 +7472,13 @@ function mergeParam(params, next) {
|
|
|
7819
7472
|
* ```
|
|
7820
7473
|
*/
|
|
7821
7474
|
async function extractDocs(srcDirs, options) {
|
|
7822
|
-
const
|
|
7823
|
-
if (!
|
|
7475
|
+
const extractFileDocEntries = (await importNapiModule()).extractFileDocEntries;
|
|
7476
|
+
if (!extractFileDocEntries) throw new Error("[ox-content] extractFileDocEntries is not available from @ox-content/napi.");
|
|
7824
7477
|
const results = [];
|
|
7825
7478
|
for (const srcDir of srcDirs) {
|
|
7826
7479
|
const files = await findFiles(srcDir, options);
|
|
7827
7480
|
for (const file of files) {
|
|
7828
|
-
const entries =
|
|
7481
|
+
const entries = extractFileDocEntries(file, options.private);
|
|
7829
7482
|
if (entries.length > 0) results.push({
|
|
7830
7483
|
file,
|
|
7831
7484
|
entries
|
|
@@ -7849,7 +7502,7 @@ async function findFiles(dir, options) {
|
|
|
7849
7502
|
return;
|
|
7850
7503
|
}
|
|
7851
7504
|
for (const entry of entries) {
|
|
7852
|
-
const fullPath = path$
|
|
7505
|
+
const fullPath = path$1.join(currentDir, entry.name);
|
|
7853
7506
|
if (entry.isDirectory()) {
|
|
7854
7507
|
if (!isExcluded(fullPath, options.exclude)) await walk(fullPath);
|
|
7855
7508
|
} else if (entry.isFile()) {
|
|
@@ -7876,132 +7529,6 @@ function isExcluded(file, patterns) {
|
|
|
7876
7529
|
return false;
|
|
7877
7530
|
});
|
|
7878
7531
|
}
|
|
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
7532
|
/**
|
|
8006
7533
|
* Generates Markdown documentation from extracted docs.
|
|
8007
7534
|
*/
|
|
@@ -8012,7 +7539,7 @@ function generateMarkdown(docs, options) {
|
|
|
8012
7539
|
if (options.groupBy === "file") {
|
|
8013
7540
|
const docToFile = /* @__PURE__ */ new Map();
|
|
8014
7541
|
for (const doc of sortedDocs) {
|
|
8015
|
-
let fileName = path$
|
|
7542
|
+
let fileName = path$1.basename(doc.file, path$1.extname(doc.file));
|
|
8016
7543
|
if (fileName === "index") fileName = "index-module";
|
|
8017
7544
|
docToFile.set(doc, fileName);
|
|
8018
7545
|
const markdown = generateFileMarkdown(doc, options, fileName, symbolMap);
|
|
@@ -8045,10 +7572,10 @@ function sortExtractedDocs(docs) {
|
|
|
8045
7572
|
return [...docs].map((doc) => ({
|
|
8046
7573
|
...doc,
|
|
8047
7574
|
entries: [...doc.entries].sort(compareEntriesByName)
|
|
8048
|
-
})).sort((a, b) => compareStrings(path$
|
|
7575
|
+
})).sort((a, b) => compareStrings(path$1.basename(a.file), path$1.basename(b.file)));
|
|
8049
7576
|
}
|
|
8050
7577
|
function generateFileMarkdown(doc, options, currentFileName, symbolMap) {
|
|
8051
|
-
let md = `# ${path$
|
|
7578
|
+
let md = `# ${path$1.basename(doc.file)}\n\n`;
|
|
8052
7579
|
if (options.githubUrl) {
|
|
8053
7580
|
const sourceLink = generateSourceLink(doc.file, options.githubUrl);
|
|
8054
7581
|
if (sourceLink) md += sourceLink + "\n\n";
|
|
@@ -8200,7 +7727,7 @@ function generateIndex(docs, docToFile) {
|
|
|
8200
7727
|
md += "## Modules\n\n";
|
|
8201
7728
|
if (docs.length > 1) md += renderDetailsControlsHtml(".ox-api-module") + "\n\n";
|
|
8202
7729
|
for (const doc of docs) {
|
|
8203
|
-
const displayName = path$
|
|
7730
|
+
const displayName = path$1.basename(doc.file, path$1.extname(doc.file));
|
|
8204
7731
|
let fileName = displayName;
|
|
8205
7732
|
if (docToFile && docToFile.has(doc)) fileName = docToFile.get(doc);
|
|
8206
7733
|
else if (fileName === "index") fileName = "index-module";
|
|
@@ -8283,7 +7810,7 @@ function convertSymbolLinks(text, currentFileName, symbolMap) {
|
|
|
8283
7810
|
function buildSymbolMap(docs) {
|
|
8284
7811
|
const map = /* @__PURE__ */ new Map();
|
|
8285
7812
|
for (const doc of docs) {
|
|
8286
|
-
let fileName = path$
|
|
7813
|
+
let fileName = path$1.basename(doc.file, path$1.extname(doc.file));
|
|
8287
7814
|
if (fileName === "index") fileName = "index-module";
|
|
8288
7815
|
for (const entry of doc.entries) map.set(entry.name, {
|
|
8289
7816
|
name: entry.name,
|
|
@@ -8301,7 +7828,7 @@ async function writeDocs(docs, outDir, extractedDocs, options) {
|
|
|
8301
7828
|
const generatedFiles = new Set(Object.keys(docs));
|
|
8302
7829
|
if (extractedDocs && options?.generateNav && options.groupBy === "file") generatedFiles.add("nav.ts");
|
|
8303
7830
|
if (extractedDocs) generatedFiles.add(DOCS_DATA_FILE);
|
|
8304
|
-
const manifestPath = path$
|
|
7831
|
+
const manifestPath = path$1.join(outDir, DOCS_MANIFEST_FILE);
|
|
8305
7832
|
let previousFiles = [];
|
|
8306
7833
|
try {
|
|
8307
7834
|
previousFiles = JSON.parse(await fs$2.promises.readFile(manifestPath, "utf-8"));
|
|
@@ -8310,18 +7837,18 @@ async function writeDocs(docs, outDir, extractedDocs, options) {
|
|
|
8310
7837
|
}
|
|
8311
7838
|
for (const staleFile of previousFiles) {
|
|
8312
7839
|
if (generatedFiles.has(staleFile)) continue;
|
|
8313
|
-
await fs$2.promises.rm(path$
|
|
7840
|
+
await fs$2.promises.rm(path$1.join(outDir, staleFile), { force: true });
|
|
8314
7841
|
}
|
|
8315
7842
|
for (const [fileName, content] of Object.entries(docs)) {
|
|
8316
|
-
const filePath = path$
|
|
7843
|
+
const filePath = path$1.join(outDir, fileName);
|
|
8317
7844
|
await fs$2.promises.writeFile(filePath, content, "utf-8");
|
|
8318
7845
|
}
|
|
8319
7846
|
if (extractedDocs && options?.generateNav && options.groupBy === "file") {
|
|
8320
7847
|
const navCode = generateNavCode(generateNavMetadata(extractedDocs, "/api"), "apiNav");
|
|
8321
|
-
const navFilePath = path$
|
|
7848
|
+
const navFilePath = path$1.join(outDir, "nav.ts");
|
|
8322
7849
|
await fs$2.promises.writeFile(navFilePath, navCode, "utf-8");
|
|
8323
7850
|
}
|
|
8324
|
-
if (extractedDocs) await fs$2.promises.writeFile(path$
|
|
7851
|
+
if (extractedDocs) await fs$2.promises.writeFile(path$1.join(outDir, DOCS_DATA_FILE), JSON.stringify(buildDocsData(extractedDocs), null, 2), "utf-8");
|
|
8325
7852
|
await fs$2.promises.writeFile(manifestPath, JSON.stringify([...generatedFiles].sort(), null, 2), "utf-8");
|
|
8326
7853
|
}
|
|
8327
7854
|
/**
|
|
@@ -8407,10 +7934,10 @@ async function renderHtmlToPng(page, html, width, height, publicDir) {
|
|
|
8407
7934
|
await route.continue();
|
|
8408
7935
|
return;
|
|
8409
7936
|
}
|
|
8410
|
-
const filePath = path$
|
|
7937
|
+
const filePath = path$1.join(publicDir, url.pathname);
|
|
8411
7938
|
try {
|
|
8412
7939
|
const body = await fs.readFile(filePath);
|
|
8413
|
-
const ext = path$
|
|
7940
|
+
const ext = path$1.extname(filePath).toLowerCase();
|
|
8414
7941
|
await route.fulfill({
|
|
8415
7942
|
body,
|
|
8416
7943
|
contentType: {
|
|
@@ -8595,7 +8122,7 @@ function computeCacheKey(templateSource, props, width, height) {
|
|
|
8595
8122
|
* Returns the cached file path if found, null otherwise.
|
|
8596
8123
|
*/
|
|
8597
8124
|
async function getCached(cacheDir, key) {
|
|
8598
|
-
const filePath = path$
|
|
8125
|
+
const filePath = path$1.join(cacheDir, `${key}.png`);
|
|
8599
8126
|
try {
|
|
8600
8127
|
return await fs$1.readFile(filePath);
|
|
8601
8128
|
} catch {
|
|
@@ -8607,7 +8134,7 @@ async function getCached(cacheDir, key) {
|
|
|
8607
8134
|
*/
|
|
8608
8135
|
async function writeCache(cacheDir, key, png) {
|
|
8609
8136
|
await fs$1.mkdir(cacheDir, { recursive: true });
|
|
8610
|
-
const filePath = path$
|
|
8137
|
+
const filePath = path$1.join(cacheDir, `${key}.png`);
|
|
8611
8138
|
await fs$1.writeFile(filePath, png);
|
|
8612
8139
|
}
|
|
8613
8140
|
//#endregion
|
|
@@ -8698,14 +8225,14 @@ function resolveOgImageOptions(options) {
|
|
|
8698
8225
|
*/
|
|
8699
8226
|
async function resolveTemplate(options, root) {
|
|
8700
8227
|
if (!options.template) return getDefaultTemplate();
|
|
8701
|
-
const templatePath = path$
|
|
8228
|
+
const templatePath = path$1.resolve(root, options.template);
|
|
8702
8229
|
const fs = await import("fs/promises");
|
|
8703
8230
|
try {
|
|
8704
8231
|
await fs.access(templatePath);
|
|
8705
8232
|
} catch {
|
|
8706
8233
|
throw new Error(`[ox-content:og-image] Template file not found: ${templatePath}`);
|
|
8707
8234
|
}
|
|
8708
|
-
switch (path$
|
|
8235
|
+
switch (path$1.extname(templatePath).toLowerCase()) {
|
|
8709
8236
|
case ".vue": return resolveVueTemplate(templatePath, options, root);
|
|
8710
8237
|
case ".svelte": return resolveSvelteTemplate(templatePath, root);
|
|
8711
8238
|
case ".tsx":
|
|
@@ -8719,9 +8246,9 @@ async function resolveTemplate(options, root) {
|
|
|
8719
8246
|
async function resolveTsTemplate(templatePath, options, root) {
|
|
8720
8247
|
const fs = await import("fs/promises");
|
|
8721
8248
|
const { rolldown } = await import("rolldown");
|
|
8722
|
-
const cacheDir = path$
|
|
8249
|
+
const cacheDir = path$1.join(root, ".cache", "og-images");
|
|
8723
8250
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
8724
|
-
const outfile = path$
|
|
8251
|
+
const outfile = path$1.join(cacheDir, "_template.mjs");
|
|
8725
8252
|
const bundle = await rolldown({
|
|
8726
8253
|
input: templatePath,
|
|
8727
8254
|
platform: "node"
|
|
@@ -8744,9 +8271,9 @@ async function resolveTsTemplate(templatePath, options, root) {
|
|
|
8744
8271
|
async function resolveVueTemplate(templatePath, options, root) {
|
|
8745
8272
|
const fs = await import("fs/promises");
|
|
8746
8273
|
const { rolldown } = await import("rolldown");
|
|
8747
|
-
const cacheDir = path$
|
|
8274
|
+
const cacheDir = path$1.join(root, ".cache", "og-images");
|
|
8748
8275
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
8749
|
-
const outfile = path$
|
|
8276
|
+
const outfile = path$1.join(cacheDir, "_template_vue.mjs");
|
|
8750
8277
|
const bundle = await rolldown({
|
|
8751
8278
|
input: templatePath,
|
|
8752
8279
|
platform: "node",
|
|
@@ -8842,9 +8369,9 @@ async function getVizejsPlugin() {
|
|
|
8842
8369
|
async function resolveSvelteTemplate(templatePath, root) {
|
|
8843
8370
|
const fs = await import("fs/promises");
|
|
8844
8371
|
const { rolldown } = await import("rolldown");
|
|
8845
|
-
const cacheDir = path$
|
|
8372
|
+
const cacheDir = path$1.join(root, ".cache", "og-images");
|
|
8846
8373
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
8847
|
-
const outfile = path$
|
|
8374
|
+
const outfile = path$1.join(cacheDir, "_template_svelte.mjs");
|
|
8848
8375
|
const bundle = await rolldown({
|
|
8849
8376
|
input: templatePath,
|
|
8850
8377
|
platform: "node",
|
|
@@ -8900,9 +8427,9 @@ function createSvelteCompilerPlugin() {
|
|
|
8900
8427
|
async function resolveReactTemplate(templatePath, root) {
|
|
8901
8428
|
const fs = await import("fs/promises");
|
|
8902
8429
|
const { rolldown } = await import("rolldown");
|
|
8903
|
-
const cacheDir = path$
|
|
8430
|
+
const cacheDir = path$1.join(root, ".cache", "og-images");
|
|
8904
8431
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
8905
|
-
const outfile = path$
|
|
8432
|
+
const outfile = path$1.join(cacheDir, "_template_react.mjs");
|
|
8906
8433
|
const bundle = await rolldown({
|
|
8907
8434
|
input: templatePath,
|
|
8908
8435
|
platform: "node",
|
|
@@ -8952,7 +8479,7 @@ async function resolveReactTemplate(templatePath, root) {
|
|
|
8952
8479
|
async function computeTemplateSource(options, root) {
|
|
8953
8480
|
if (!options.template) return "__default__";
|
|
8954
8481
|
const fs = await import("fs/promises");
|
|
8955
|
-
const templatePath = path$
|
|
8482
|
+
const templatePath = path$1.resolve(root, options.template);
|
|
8956
8483
|
const content = await fs.readFile(templatePath, "utf-8");
|
|
8957
8484
|
return crypto.createHash("sha256").update(content).digest("hex");
|
|
8958
8485
|
}
|
|
@@ -8970,7 +8497,7 @@ async function generateOgImages(pages, options, root) {
|
|
|
8970
8497
|
if (pages.length === 0) return [];
|
|
8971
8498
|
const templateFn = await resolveTemplate(options, root);
|
|
8972
8499
|
const templateSource = await computeTemplateSource(options, root);
|
|
8973
|
-
const cacheDir = path$
|
|
8500
|
+
const cacheDir = path$1.join(root, ".cache", "og-images");
|
|
8974
8501
|
if (options.cache) {
|
|
8975
8502
|
const allCached = await tryServeAllFromCache(pages, templateSource, options, cacheDir);
|
|
8976
8503
|
if (allCached) return allCached;
|
|
@@ -8982,7 +8509,7 @@ async function generateOgImages(pages, options, root) {
|
|
|
8982
8509
|
error: "Chromium not available"
|
|
8983
8510
|
}));
|
|
8984
8511
|
const results = [];
|
|
8985
|
-
const publicDir = path$
|
|
8512
|
+
const publicDir = path$1.join(root, "public");
|
|
8986
8513
|
const concurrency = Math.max(1, options.concurrency);
|
|
8987
8514
|
for (let i = 0; i < pages.length; i += concurrency) {
|
|
8988
8515
|
const batch = pages.slice(i, i + concurrency);
|
|
@@ -9006,7 +8533,7 @@ async function tryServeAllFromCache(pages, templateSource, options, cacheDir) {
|
|
|
9006
8533
|
for (const entry of pages) {
|
|
9007
8534
|
const cached = await getCached(cacheDir, computeCacheKey(templateSource, entry.props, options.width, options.height));
|
|
9008
8535
|
if (!cached) return null;
|
|
9009
|
-
await fs.mkdir(path$
|
|
8536
|
+
await fs.mkdir(path$1.dirname(entry.outputPath), { recursive: true });
|
|
9010
8537
|
await fs.writeFile(entry.outputPath, cached);
|
|
9011
8538
|
results.push({
|
|
9012
8539
|
outputPath: entry.outputPath,
|
|
@@ -9024,7 +8551,7 @@ async function renderSinglePage(entry, templateFn, templateSource, options, cach
|
|
|
9024
8551
|
if (options.cache) {
|
|
9025
8552
|
const cached = await getCached(cacheDir, computeCacheKey(templateSource, entry.props, options.width, options.height));
|
|
9026
8553
|
if (cached) {
|
|
9027
|
-
await fs.mkdir(path$
|
|
8554
|
+
await fs.mkdir(path$1.dirname(entry.outputPath), { recursive: true });
|
|
9028
8555
|
await fs.writeFile(entry.outputPath, cached);
|
|
9029
8556
|
return {
|
|
9030
8557
|
outputPath: entry.outputPath,
|
|
@@ -9034,7 +8561,7 @@ async function renderSinglePage(entry, templateFn, templateSource, options, cach
|
|
|
9034
8561
|
}
|
|
9035
8562
|
const html = await templateFn(entry.props);
|
|
9036
8563
|
const png = await session.renderPage(html, options.width, options.height, publicDir);
|
|
9037
|
-
await fs.mkdir(path$
|
|
8564
|
+
await fs.mkdir(path$1.dirname(entry.outputPath), { recursive: true });
|
|
9038
8565
|
await fs.writeFile(entry.outputPath, png);
|
|
9039
8566
|
if (options.cache) await writeCache(cacheDir, computeCacheKey(templateSource, entry.props, options.width, options.height), png);
|
|
9040
8567
|
return {
|
|
@@ -9058,23 +8585,23 @@ async function transformAllPlugins(html, options = {}) {
|
|
|
9058
8585
|
const { tabs = true, youtube = true, github = true, ogp = true, mermaid = true, githubToken } = options;
|
|
9059
8586
|
let result = html;
|
|
9060
8587
|
if (tabs) {
|
|
9061
|
-
const { transformTabs } = await import("./tabs.mjs");
|
|
8588
|
+
const { transformTabs } = await import("./tabs.mjs").then((n) => n.r);
|
|
9062
8589
|
result = await transformTabs(result);
|
|
9063
8590
|
}
|
|
9064
8591
|
if (youtube) {
|
|
9065
|
-
const { transformYouTube } = await import("./youtube.mjs");
|
|
8592
|
+
const { transformYouTube } = await import("./youtube.mjs").then((n) => n.r);
|
|
9066
8593
|
result = await transformYouTube(result);
|
|
9067
8594
|
}
|
|
9068
8595
|
if (github) {
|
|
9069
|
-
const { transformGitHub } = await import("./github.mjs");
|
|
8596
|
+
const { transformGitHub } = await import("./github.mjs").then((n) => n.r);
|
|
9070
8597
|
result = await transformGitHub(result, void 0, { token: githubToken });
|
|
9071
8598
|
}
|
|
9072
8599
|
if (ogp) {
|
|
9073
|
-
const { transformOgp } = await import("./ogp.mjs");
|
|
8600
|
+
const { transformOgp } = await import("./ogp.mjs").then((n) => n.r);
|
|
9074
8601
|
result = await transformOgp(result);
|
|
9075
8602
|
}
|
|
9076
8603
|
if (mermaid) {
|
|
9077
|
-
const { transformMermaidStatic } = await import("./
|
|
8604
|
+
const { transformMermaidStatic } = await import("./mermaid.mjs").then((n) => n.n);
|
|
9078
8605
|
result = await transformMermaidStatic(result);
|
|
9079
8606
|
}
|
|
9080
8607
|
return result;
|
|
@@ -10882,10 +10409,7 @@ function renderTemplate(template, data) {
|
|
|
10882
10409
|
* Extracts title from content or frontmatter.
|
|
10883
10410
|
*/
|
|
10884
10411
|
function extractTitle$1(content, frontmatter) {
|
|
10885
|
-
|
|
10886
|
-
const h1Match = content.match(/<h1[^>]*>([^<]+)<\/h1>/i);
|
|
10887
|
-
if (h1Match) return h1Match[1].trim();
|
|
10888
|
-
return "Untitled";
|
|
10412
|
+
return importNapiModuleSync().extractSsgTitle(content, typeof frontmatter.title === "string" ? frontmatter.title : void 0);
|
|
10889
10413
|
}
|
|
10890
10414
|
/**
|
|
10891
10415
|
* Generates bare HTML page (no navigation, no styles).
|
|
@@ -10899,7 +10423,7 @@ function generateBareHtmlPage(content, title) {
|
|
|
10899
10423
|
/**
|
|
10900
10424
|
* Generates HTML page with navigation using Rust NAPI bindings.
|
|
10901
10425
|
*/
|
|
10902
|
-
async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, theme) {
|
|
10426
|
+
async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, theme, locale, availableLocales) {
|
|
10903
10427
|
const mod = await importNapiModule();
|
|
10904
10428
|
const tocForRust = pageData.toc.map((entry) => ({
|
|
10905
10429
|
depth: entry.depth,
|
|
@@ -10962,227 +10486,50 @@ async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, th
|
|
|
10962
10486
|
siteName,
|
|
10963
10487
|
base,
|
|
10964
10488
|
ogImage,
|
|
10965
|
-
theme: themeForRust
|
|
10489
|
+
theme: themeForRust,
|
|
10490
|
+
locale,
|
|
10491
|
+
availableLocales: availableLocales?.map((l) => ({
|
|
10492
|
+
code: l.code,
|
|
10493
|
+
name: l.name,
|
|
10494
|
+
dir: l.dir ?? "ltr"
|
|
10495
|
+
}))
|
|
10966
10496
|
});
|
|
10967
10497
|
}
|
|
10968
|
-
const SSG_STYLE_BLOCK_RE = /[ \t]*<!-- ox-content:styles:start -->\s*<style>([\s\S]*?)<\/style>\s*<!-- ox-content:styles:end -->/;
|
|
10969
|
-
const SSG_SCRIPT_BLOCK_RE = /[ \t]*<!-- ox-content:scripts:start -->\s*<script>([\s\S]*?)<\/script>\s*<!-- ox-content:scripts:end -->/;
|
|
10970
|
-
const FIRST_INLINE_STYLE_RE = /[ \t]*<style>([\s\S]*?)<\/style>/;
|
|
10971
|
-
const LAST_INLINE_BODY_SCRIPT_RE = /[ \t]*<script>([\s\S]*?)<\/script>\s*<\/body>/;
|
|
10972
|
-
const CSS_SECTION_RE = /\/\* ox-content:css:([a-z0-9-]+):start \*\/\s*([\s\S]*?)\s*\/\* ox-content:css:\1:end \*\//g;
|
|
10973
|
-
const SEARCH_CHUNK_RE = /\/\/ ox-content:search:start\s*([\s\S]*?)\s*\/\/ ox-content:search:end/;
|
|
10974
|
-
const SEARCH_CHUNK_PLACEHOLDER = "__OX_CONTENT_SEARCH_CHUNK__";
|
|
10975
|
-
const CORE_CSS_SECTION_NAMES = new Set(["base", "footer"]);
|
|
10976
|
-
const THEME_INLINE_CSS_MAX_BYTES = 2048;
|
|
10977
|
-
function createContentHash(content) {
|
|
10978
|
-
return createHash("sha256").update(content).digest("hex").slice(0, 10);
|
|
10979
|
-
}
|
|
10980
|
-
function sanitizeChunkLabel(label) {
|
|
10981
|
-
return label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "asset";
|
|
10982
|
-
}
|
|
10983
|
-
function toPublicAssetPath(base, fileName) {
|
|
10984
|
-
return `${base.endsWith("/") ? base : `${base}/`}assets/${fileName}`;
|
|
10985
|
-
}
|
|
10986
|
-
function hasRelativeCssUrls(css) {
|
|
10987
|
-
let cursor = 0;
|
|
10988
|
-
while (cursor < css.length) {
|
|
10989
|
-
const urlIndex = css.indexOf("url(", cursor);
|
|
10990
|
-
if (urlIndex === -1) return false;
|
|
10991
|
-
let valueStart = urlIndex + 4;
|
|
10992
|
-
while (valueStart < css.length && /\s/.test(css[valueStart])) valueStart++;
|
|
10993
|
-
const quote = css[valueStart] === "\"" || css[valueStart] === "'" ? css[valueStart] : "";
|
|
10994
|
-
if (quote) valueStart++;
|
|
10995
|
-
let valueEnd = valueStart;
|
|
10996
|
-
while (valueEnd < css.length) {
|
|
10997
|
-
const char = css[valueEnd];
|
|
10998
|
-
if (quote) {
|
|
10999
|
-
if (char === "\\") {
|
|
11000
|
-
valueEnd += 2;
|
|
11001
|
-
continue;
|
|
11002
|
-
}
|
|
11003
|
-
if (char === quote) break;
|
|
11004
|
-
} else if (char === ")") break;
|
|
11005
|
-
valueEnd++;
|
|
11006
|
-
}
|
|
11007
|
-
const value = css.slice(valueStart, valueEnd).trim();
|
|
11008
|
-
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;
|
|
11009
|
-
cursor = valueEnd + 1;
|
|
11010
|
-
}
|
|
11011
|
-
return false;
|
|
11012
|
-
}
|
|
11013
|
-
function createSharedAssetChunk(type, label, content, outDir, base) {
|
|
11014
|
-
const hash = createContentHash(content);
|
|
11015
|
-
const fileName = `ox-content-${sanitizeChunkLabel(label)}-${hash}.${type}`;
|
|
11016
|
-
return {
|
|
11017
|
-
outputPath: path$2.join(outDir, "assets", fileName),
|
|
11018
|
-
publicPath: toPublicAssetPath(base, fileName),
|
|
11019
|
-
content
|
|
11020
|
-
};
|
|
11021
|
-
}
|
|
11022
|
-
function extractCssSections(cssContent) {
|
|
11023
|
-
return Array.from(cssContent.matchAll(CSS_SECTION_RE)).map(([, name, content]) => ({
|
|
11024
|
-
name,
|
|
11025
|
-
content: content.trim()
|
|
11026
|
-
})).filter((section) => section.content.length > 0);
|
|
11027
|
-
}
|
|
11028
|
-
function getOrCreateSharedChunk(chunks, type, label, content, outDir, base) {
|
|
11029
|
-
let chunk = chunks.get(content);
|
|
11030
|
-
if (!chunk) {
|
|
11031
|
-
chunk = createSharedAssetChunk(type, label, content, outDir, base);
|
|
11032
|
-
chunks.set(content, chunk);
|
|
11033
|
-
}
|
|
11034
|
-
return chunk;
|
|
11035
|
-
}
|
|
11036
|
-
function buildStyleReplacement(cssContent, cssChunks, outDir, base) {
|
|
11037
|
-
const sections = extractCssSections(cssContent);
|
|
11038
|
-
const effectiveSections = sections.length > 0 ? sections : [{
|
|
11039
|
-
name: "css",
|
|
11040
|
-
content: cssContent.trim()
|
|
11041
|
-
}];
|
|
11042
|
-
const coreContent = effectiveSections.filter((section) => CORE_CSS_SECTION_NAMES.has(section.name)).map((section) => section.content).join("\n").trim();
|
|
11043
|
-
const fragments = [];
|
|
11044
|
-
if (coreContent) {
|
|
11045
|
-
const coreChunk = getOrCreateSharedChunk(cssChunks, "css", "core", coreContent, outDir, base);
|
|
11046
|
-
fragments.push(` <link rel="stylesheet" href="${coreChunk.publicPath}">`);
|
|
11047
|
-
}
|
|
11048
|
-
for (const section of effectiveSections) {
|
|
11049
|
-
if (CORE_CSS_SECTION_NAMES.has(section.name)) continue;
|
|
11050
|
-
if (section.name === "theme" && (hasRelativeCssUrls(section.content) || section.content.length <= THEME_INLINE_CSS_MAX_BYTES) || hasRelativeCssUrls(section.content)) {
|
|
11051
|
-
fragments.push(` <style>${section.content}</style>`);
|
|
11052
|
-
continue;
|
|
11053
|
-
}
|
|
11054
|
-
const chunk = getOrCreateSharedChunk(cssChunks, "css", section.name, section.content, outDir, base);
|
|
11055
|
-
fragments.push(` <link rel="stylesheet" href="${chunk.publicPath}">`);
|
|
11056
|
-
}
|
|
11057
|
-
return fragments.join("\n");
|
|
11058
|
-
}
|
|
11059
|
-
function buildScriptReplacement(jsContent, jsChunks, outDir, base) {
|
|
11060
|
-
const searchMatch = jsContent.match(SEARCH_CHUNK_RE);
|
|
11061
|
-
if (searchMatch && jsContent.includes(SEARCH_CHUNK_PLACEHOLDER)) {
|
|
11062
|
-
const searchContent = searchMatch[1].trim();
|
|
11063
|
-
if (searchContent) {
|
|
11064
|
-
const searchChunk = getOrCreateSharedChunk(jsChunks, "js", "search", searchContent, outDir, base);
|
|
11065
|
-
const coreContent = jsContent.replace(SEARCH_CHUNK_RE, "").replaceAll(SEARCH_CHUNK_PLACEHOLDER, searchChunk.publicPath).trim();
|
|
11066
|
-
if (coreContent) return ` <script defer src="${getOrCreateSharedChunk(jsChunks, "js", "core", coreContent, outDir, base).publicPath}"><\/script>`;
|
|
11067
|
-
}
|
|
11068
|
-
}
|
|
11069
|
-
const fallbackContent = jsContent.trim();
|
|
11070
|
-
if (!fallbackContent) return "";
|
|
11071
|
-
return ` <script defer src="${getOrCreateSharedChunk(jsChunks, "js", "js", fallbackContent, outDir, base).publicPath}"><\/script>`;
|
|
11072
|
-
}
|
|
11073
10498
|
async function externalizeSharedPageAssets(pages, outDir, base) {
|
|
11074
|
-
const
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
const styleMatch = html.match(SSG_STYLE_BLOCK_RE);
|
|
11079
|
-
if (styleMatch) {
|
|
11080
|
-
const replacement = buildStyleReplacement(styleMatch[1], cssChunks, outDir, base);
|
|
11081
|
-
html = html.replace(SSG_STYLE_BLOCK_RE, replacement);
|
|
11082
|
-
} else {
|
|
11083
|
-
const inlineStyleMatch = html.match(FIRST_INLINE_STYLE_RE);
|
|
11084
|
-
if (inlineStyleMatch) {
|
|
11085
|
-
const replacement = buildStyleReplacement(inlineStyleMatch[1], cssChunks, outDir, base);
|
|
11086
|
-
html = html.replace(FIRST_INLINE_STYLE_RE, replacement);
|
|
11087
|
-
}
|
|
11088
|
-
}
|
|
11089
|
-
const scriptMatch = html.match(SSG_SCRIPT_BLOCK_RE);
|
|
11090
|
-
if (scriptMatch) {
|
|
11091
|
-
const replacement = buildScriptReplacement(scriptMatch[1], jsChunks, outDir, base);
|
|
11092
|
-
html = html.replace(SSG_SCRIPT_BLOCK_RE, replacement);
|
|
11093
|
-
} else {
|
|
11094
|
-
const inlineScriptMatch = html.match(LAST_INLINE_BODY_SCRIPT_RE);
|
|
11095
|
-
if (inlineScriptMatch) {
|
|
11096
|
-
const replacement = buildScriptReplacement(inlineScriptMatch[1], jsChunks, outDir, base);
|
|
11097
|
-
html = html.replace(LAST_INLINE_BODY_SCRIPT_RE, replacement ? `${replacement}\n</body>` : "</body>");
|
|
11098
|
-
}
|
|
11099
|
-
}
|
|
11100
|
-
return {
|
|
11101
|
-
...page,
|
|
11102
|
-
html
|
|
11103
|
-
};
|
|
11104
|
-
});
|
|
11105
|
-
const chunks = [...cssChunks.values(), ...jsChunks.values()];
|
|
11106
|
-
await Promise.all(chunks.map(async (chunk) => {
|
|
11107
|
-
await fs$1.mkdir(path$2.dirname(chunk.outputPath), { recursive: true });
|
|
11108
|
-
await fs$1.writeFile(chunk.outputPath, chunk.content, "utf-8");
|
|
10499
|
+
const optimized = (await importNapiModule()).externalizeSsgAssets(pages, outDir, base);
|
|
10500
|
+
await Promise.all(optimized.assets.map(async (asset) => {
|
|
10501
|
+
await fs$1.mkdir(path$1.dirname(asset.outputPath), { recursive: true });
|
|
10502
|
+
await fs$1.writeFile(asset.outputPath, asset.content, "utf-8");
|
|
11109
10503
|
}));
|
|
11110
10504
|
return {
|
|
11111
|
-
pages:
|
|
11112
|
-
assets:
|
|
10505
|
+
pages: optimized.pages,
|
|
10506
|
+
assets: optimized.assets.map((asset) => asset.outputPath)
|
|
11113
10507
|
};
|
|
11114
10508
|
}
|
|
11115
10509
|
/**
|
|
11116
|
-
* Converts a markdown file path to its corresponding HTML output path.
|
|
11117
|
-
*/
|
|
11118
|
-
function getOutputPath(inputPath, srcDir, outDir, extension) {
|
|
11119
|
-
const baseName = path$2.relative(srcDir, inputPath).replace(/\.(?:md|markdown)$/i, extension);
|
|
11120
|
-
if (baseName.endsWith(`index${extension}`)) return path$2.join(outDir, baseName);
|
|
11121
|
-
const dirName = baseName.replace(new RegExp(`\\${extension}$`), "");
|
|
11122
|
-
return path$2.join(outDir, dirName, `index${extension}`);
|
|
11123
|
-
}
|
|
11124
|
-
/**
|
|
11125
10510
|
* Converts a markdown file path to a relative URL path.
|
|
11126
10511
|
*/
|
|
11127
10512
|
function getUrlPath$1(inputPath, srcDir) {
|
|
11128
|
-
|
|
11129
|
-
if (baseName === "index" || baseName.endsWith("/index")) return baseName.replace(/\/?index$/, "") || "/";
|
|
11130
|
-
return baseName;
|
|
10513
|
+
return importNapiModuleSync().getSsgUrlPath(inputPath, srcDir);
|
|
11131
10514
|
}
|
|
11132
|
-
|
|
11133
|
-
|
|
11134
|
-
|
|
11135
|
-
function getHref(inputPath, srcDir, base, extension) {
|
|
11136
|
-
const urlPath = getUrlPath$1(inputPath, srcDir);
|
|
11137
|
-
if (urlPath === "/" || urlPath === "") return `${base}index${extension}`;
|
|
11138
|
-
return `${base}${urlPath}/index${extension}`;
|
|
10515
|
+
function getPageLocale(urlPath, i18n) {
|
|
10516
|
+
if (!i18n) return void 0;
|
|
10517
|
+
return importNapiModuleSync().getSsgPageLocale(urlPath, i18n.defaultLocale, i18n.locales.map((locale) => locale.code)) ?? void 0;
|
|
11139
10518
|
}
|
|
11140
|
-
|
|
11141
|
-
|
|
11142
|
-
*/
|
|
11143
|
-
function getOgImagePath(inputPath, srcDir, outDir) {
|
|
11144
|
-
const baseName = path$2.relative(srcDir, inputPath).replace(/\.(?:md|markdown)$/i, "");
|
|
11145
|
-
if (baseName === "index" || baseName.endsWith("/index")) {
|
|
11146
|
-
const dirPath = baseName.replace(/\/?index$/, "") || "";
|
|
11147
|
-
return path$2.join(outDir, dirPath, "og-image.png");
|
|
11148
|
-
}
|
|
11149
|
-
return path$2.join(outDir, baseName, "og-image.png");
|
|
11150
|
-
}
|
|
11151
|
-
/**
|
|
11152
|
-
* Gets the OG image URL for use in meta tags.
|
|
11153
|
-
* If siteUrl is provided, returns an absolute URL (required for SNS sharing).
|
|
11154
|
-
*/
|
|
11155
|
-
function getOgImageUrl(inputPath, srcDir, base, siteUrl) {
|
|
11156
|
-
const urlPath = getUrlPath$1(inputPath, srcDir);
|
|
11157
|
-
let relativePath;
|
|
11158
|
-
if (urlPath === "/" || urlPath === "") relativePath = `${base}og-image.png`;
|
|
11159
|
-
else relativePath = `${base}${urlPath}/og-image.png`;
|
|
11160
|
-
if (siteUrl) return `${siteUrl.replace(/\/$/, "")}${relativePath}`;
|
|
11161
|
-
return relativePath;
|
|
11162
|
-
}
|
|
11163
|
-
/**
|
|
11164
|
-
* Gets display title from file path.
|
|
11165
|
-
*/
|
|
11166
|
-
function getDisplayTitle(filePath) {
|
|
11167
|
-
const fileName = path$2.basename(filePath, path$2.extname(filePath));
|
|
11168
|
-
if (fileName === "index") {
|
|
11169
|
-
const dirName = path$2.basename(path$2.dirname(filePath));
|
|
11170
|
-
if (dirName && dirName !== ".") return formatTitle(dirName);
|
|
11171
|
-
return "Home";
|
|
11172
|
-
}
|
|
11173
|
-
return formatTitle(fileName);
|
|
10519
|
+
function getRoutePaths(inputPath, srcDir, outDir, base, extension, siteUrl) {
|
|
10520
|
+
return importNapiModuleSync().resolveSsgRoutePaths(inputPath, srcDir, outDir, base, extension, siteUrl);
|
|
11174
10521
|
}
|
|
11175
10522
|
/**
|
|
11176
10523
|
* Formats a file/dir name as a title.
|
|
11177
10524
|
*/
|
|
11178
10525
|
function formatTitle(name) {
|
|
11179
|
-
return
|
|
10526
|
+
return importNapiModuleSync().formatSsgTitle(name);
|
|
11180
10527
|
}
|
|
11181
10528
|
/**
|
|
11182
10529
|
* Collects all markdown files from the source directory.
|
|
11183
10530
|
*/
|
|
11184
10531
|
async function collectMarkdownFiles$1(srcDir) {
|
|
11185
|
-
return (await glob(path$
|
|
10532
|
+
return (await glob(path$1.join(srcDir, "**/*.{md,markdown}"), {
|
|
11186
10533
|
nodir: true,
|
|
11187
10534
|
ignore: [
|
|
11188
10535
|
"**/node_modules/**",
|
|
@@ -11195,110 +10542,13 @@ async function collectMarkdownFiles$1(srcDir) {
|
|
|
11195
10542
|
* Builds navigation items from markdown files, grouped by directory.
|
|
11196
10543
|
*/
|
|
11197
10544
|
function buildNavItems(markdownFiles, srcDir, base, extension) {
|
|
11198
|
-
|
|
11199
|
-
const groupOrder = [
|
|
11200
|
-
"",
|
|
11201
|
-
"examples",
|
|
11202
|
-
"packages",
|
|
11203
|
-
"api"
|
|
11204
|
-
];
|
|
11205
|
-
for (const file of markdownFiles) {
|
|
11206
|
-
const parts = path$2.relative(srcDir, file).split(path$2.sep);
|
|
11207
|
-
let groupKey = "";
|
|
11208
|
-
if (parts.length > 1) groupKey = parts[0];
|
|
11209
|
-
if (!groups.has(groupKey)) groups.set(groupKey, []);
|
|
11210
|
-
const urlPath = getUrlPath$1(file, srcDir);
|
|
11211
|
-
let title;
|
|
11212
|
-
if (urlPath === "/" || urlPath === "") title = "Overview";
|
|
11213
|
-
else title = getDisplayTitle(file);
|
|
11214
|
-
groups.get(groupKey).push({
|
|
11215
|
-
title,
|
|
11216
|
-
path: urlPath,
|
|
11217
|
-
href: getHref(file, srcDir, base, extension)
|
|
11218
|
-
});
|
|
11219
|
-
}
|
|
11220
|
-
const sortItems = (items) => {
|
|
11221
|
-
return items.sort((a, b) => {
|
|
11222
|
-
const aIsRoot = a.path === "/" || a.path === "";
|
|
11223
|
-
const bIsRoot = b.path === "/" || b.path === "";
|
|
11224
|
-
if (aIsRoot && !bIsRoot) return -1;
|
|
11225
|
-
if (!aIsRoot && bIsRoot) return 1;
|
|
11226
|
-
return a.title.localeCompare(b.title);
|
|
11227
|
-
});
|
|
11228
|
-
};
|
|
11229
|
-
const result = [];
|
|
11230
|
-
for (const key of groupOrder) {
|
|
11231
|
-
const items = groups.get(key);
|
|
11232
|
-
if (items && items.length > 0) {
|
|
11233
|
-
result.push({
|
|
11234
|
-
title: key === "" ? "Guide" : formatTitle(key),
|
|
11235
|
-
items: sortItems(items)
|
|
11236
|
-
});
|
|
11237
|
-
groups.delete(key);
|
|
11238
|
-
}
|
|
11239
|
-
}
|
|
11240
|
-
for (const [key, items] of groups) if (items.length > 0) result.push({
|
|
11241
|
-
title: formatTitle(key),
|
|
11242
|
-
items: sortItems(items)
|
|
11243
|
-
});
|
|
11244
|
-
return result;
|
|
11245
|
-
}
|
|
11246
|
-
function isSafeSidebarLink(link) {
|
|
11247
|
-
const trimmed = link.trim();
|
|
11248
|
-
if (trimmed.startsWith("//")) return false;
|
|
11249
|
-
return !/^[a-z][a-z0-9+.-]*:/i.test(trimmed) || /^(https?:|mailto:)/i.test(trimmed);
|
|
11250
|
-
}
|
|
11251
|
-
function sidebarPath(link) {
|
|
11252
|
-
if (!link || !isSafeSidebarLink(link)) return "";
|
|
11253
|
-
if (/^(https?:|mailto:|#)/i.test(link.trim())) return "";
|
|
11254
|
-
const bare = link.trim().split("#", 1)[0].split("?", 1)[0].replace(/^\/+/, "").replace(/\/$/, "").replace(/\.(md|markdown)$/i, "");
|
|
11255
|
-
if (!bare || bare === "index") return "/";
|
|
11256
|
-
return bare.replace(/\/index$/, "");
|
|
11257
|
-
}
|
|
11258
|
-
function sidebarHref(link, base, extension) {
|
|
11259
|
-
if (!link) return "#";
|
|
11260
|
-
const trimmed = link.trim();
|
|
11261
|
-
if (!isSafeSidebarLink(trimmed)) return "#";
|
|
11262
|
-
if (/^(https?:|mailto:|#)/i.test(trimmed)) return trimmed;
|
|
11263
|
-
const hash = trimmed.includes("#") ? `#${trimmed.split("#").slice(1).join("#")}` : "";
|
|
11264
|
-
const withoutExt = trimmed.split("#", 1)[0].replace(/^\/+/, "").replace(/\/$/, "").replace(/\.(md|markdown)$/i, "");
|
|
11265
|
-
return `${base}${!withoutExt || withoutExt === "index" ? "index" : `${withoutExt.replace(/\/index$/, "")}/index`}${extension}${hash}`;
|
|
10545
|
+
return importNapiModuleSync().buildSsgNavItems(markdownFiles, srcDir, base, extension);
|
|
11266
10546
|
}
|
|
11267
10547
|
/**
|
|
11268
10548
|
* Builds navigation items from an explicit theme sidebar tree.
|
|
11269
10549
|
*/
|
|
11270
10550
|
function buildThemeNavItems(sidebar, base, extension) {
|
|
11271
|
-
|
|
11272
|
-
const navItem = {
|
|
11273
|
-
title: item.text ?? item.link ?? "Untitled",
|
|
11274
|
-
path: sidebarPath(item.link),
|
|
11275
|
-
href: sidebarHref(item.link, base, extension)
|
|
11276
|
-
};
|
|
11277
|
-
if (item.items?.length) navItem.children = item.items.map(toNavItem);
|
|
11278
|
-
if (item.collapsed !== void 0) navItem.collapsed = item.collapsed;
|
|
11279
|
-
return navItem;
|
|
11280
|
-
};
|
|
11281
|
-
const groups = [];
|
|
11282
|
-
let looseItems = [];
|
|
11283
|
-
const flushLooseItems = () => {
|
|
11284
|
-
if (looseItems.length > 0) {
|
|
11285
|
-
groups.push({
|
|
11286
|
-
title: "Guide",
|
|
11287
|
-
items: looseItems
|
|
11288
|
-
});
|
|
11289
|
-
looseItems = [];
|
|
11290
|
-
}
|
|
11291
|
-
};
|
|
11292
|
-
for (const item of sidebar) if (item.items?.length && !item.link) {
|
|
11293
|
-
flushLooseItems();
|
|
11294
|
-
groups.push({
|
|
11295
|
-
title: item.text ?? "Guide",
|
|
11296
|
-
items: item.items.map(toNavItem),
|
|
11297
|
-
collapsed: item.collapsed
|
|
11298
|
-
});
|
|
11299
|
-
} else looseItems.push(toNavItem(item));
|
|
11300
|
-
flushLooseItems();
|
|
11301
|
-
return groups;
|
|
10551
|
+
return importNapiModuleSync().buildSsgThemeNavItems(sidebar, base, extension);
|
|
11302
10552
|
}
|
|
11303
10553
|
/**
|
|
11304
10554
|
* Builds all markdown files to static HTML.
|
|
@@ -11309,8 +10559,8 @@ async function buildSsg(options, root) {
|
|
|
11309
10559
|
files: [],
|
|
11310
10560
|
errors: []
|
|
11311
10561
|
};
|
|
11312
|
-
const srcDir = path$
|
|
11313
|
-
const outDir = path$
|
|
10562
|
+
const srcDir = path$1.resolve(root, options.srcDir);
|
|
10563
|
+
const outDir = path$1.resolve(root, options.outDir);
|
|
11314
10564
|
const base = options.base.endsWith("/") ? options.base : options.base + "/";
|
|
11315
10565
|
const generatedFiles = [];
|
|
11316
10566
|
const generatedPages = [];
|
|
@@ -11325,7 +10575,7 @@ async function buildSsg(options, root) {
|
|
|
11325
10575
|
const navItems = ssgOptions.theme?.sidebar.length ? buildThemeNavItems(ssgOptions.theme.sidebar, base, ssgOptions.extension) : buildNavItems(markdownFiles, srcDir, base, ssgOptions.extension);
|
|
11326
10576
|
let siteName = ssgOptions.siteName ?? "Documentation";
|
|
11327
10577
|
if (!ssgOptions.siteName) try {
|
|
11328
|
-
const pkgPath = path$
|
|
10578
|
+
const pkgPath = path$1.join(root, "package.json");
|
|
11329
10579
|
const pkg = JSON.parse(await fs$1.readFile(pkgPath, "utf-8"));
|
|
11330
10580
|
if (pkg.name) siteName = formatTitle(pkg.name);
|
|
11331
10581
|
} catch {}
|
|
@@ -11357,8 +10607,10 @@ async function buildSsg(options, root) {
|
|
|
11357
10607
|
transformedHtml = restoreMermaidSvgs(transformedHtml, mermaidSvgs);
|
|
11358
10608
|
const title = extractTitle$1(transformedHtml, result.frontmatter);
|
|
11359
10609
|
const description = result.frontmatter.description;
|
|
10610
|
+
const routePaths = getRoutePaths(inputPath, srcDir, outDir, base, ssgOptions.extension, ssgOptions.siteUrl);
|
|
11360
10611
|
pageResults.push({
|
|
11361
10612
|
inputPath,
|
|
10613
|
+
routePaths,
|
|
11362
10614
|
transformedHtml,
|
|
11363
10615
|
title,
|
|
11364
10616
|
description,
|
|
@@ -11367,7 +10619,6 @@ async function buildSsg(options, root) {
|
|
|
11367
10619
|
toc: result.toc
|
|
11368
10620
|
});
|
|
11369
10621
|
if (shouldGenerateOgImages) {
|
|
11370
|
-
const ogImageOutputPath = getOgImagePath(inputPath, srcDir, outDir);
|
|
11371
10622
|
const { layout: _layout, ...frontmatterRest } = result.frontmatter;
|
|
11372
10623
|
ogImageEntries.push({
|
|
11373
10624
|
props: {
|
|
@@ -11376,10 +10627,10 @@ async function buildSsg(options, root) {
|
|
|
11376
10627
|
description,
|
|
11377
10628
|
siteName
|
|
11378
10629
|
},
|
|
11379
|
-
outputPath:
|
|
10630
|
+
outputPath: routePaths.ogImagePath
|
|
11380
10631
|
});
|
|
11381
10632
|
ogImageInputPaths.push(inputPath);
|
|
11382
|
-
ogImageUrlMap.set(inputPath,
|
|
10633
|
+
ogImageUrlMap.set(inputPath, routePaths.ogImageUrl);
|
|
11383
10634
|
}
|
|
11384
10635
|
} catch (err) {
|
|
11385
10636
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
@@ -11408,7 +10659,7 @@ async function buildSsg(options, root) {
|
|
|
11408
10659
|
ogImageUrlMap.clear();
|
|
11409
10660
|
}
|
|
11410
10661
|
for (const pageResult of pageResults) try {
|
|
11411
|
-
const { inputPath, transformedHtml, title, description, lastUpdated, frontmatter, toc } = pageResult;
|
|
10662
|
+
const { inputPath, routePaths, transformedHtml, title, description, lastUpdated, frontmatter, toc } = pageResult;
|
|
11412
10663
|
let pageOgImage = ssgOptions.ogImage;
|
|
11413
10664
|
if (shouldGenerateOgImages && ogImageUrlMap.has(inputPath)) pageOgImage = ogImageUrlMap.get(inputPath);
|
|
11414
10665
|
let entryPage;
|
|
@@ -11418,21 +10669,23 @@ async function buildSsg(options, root) {
|
|
|
11418
10669
|
};
|
|
11419
10670
|
let html;
|
|
11420
10671
|
if (ssgOptions.bare) html = generateBareHtmlPage(transformedHtml, title);
|
|
11421
|
-
else
|
|
11422
|
-
|
|
11423
|
-
|
|
11424
|
-
|
|
11425
|
-
|
|
11426
|
-
|
|
11427
|
-
|
|
11428
|
-
|
|
11429
|
-
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
|
|
10672
|
+
else {
|
|
10673
|
+
const pageData = {
|
|
10674
|
+
title,
|
|
10675
|
+
description,
|
|
10676
|
+
content: transformedHtml,
|
|
10677
|
+
toc,
|
|
10678
|
+
lastUpdated,
|
|
10679
|
+
frontmatter,
|
|
10680
|
+
path: routePaths.urlPath,
|
|
10681
|
+
href: routePaths.href,
|
|
10682
|
+
entryPage
|
|
10683
|
+
};
|
|
10684
|
+
html = await generateHtmlPage(pageData, navItems, siteName, base, pageOgImage, ssgOptions.theme, getPageLocale(pageData.path, options.i18n), options.i18n ? options.i18n.locales : void 0);
|
|
10685
|
+
}
|
|
11433
10686
|
generatedPages.push({
|
|
11434
10687
|
inputPath,
|
|
11435
|
-
outputPath,
|
|
10688
|
+
outputPath: routePaths.outputPath,
|
|
11436
10689
|
html
|
|
11437
10690
|
});
|
|
11438
10691
|
} catch (err) {
|
|
@@ -11442,7 +10695,7 @@ async function buildSsg(options, root) {
|
|
|
11442
10695
|
const optimizedOutput = await externalizeSharedPageAssets(generatedPages, outDir, base);
|
|
11443
10696
|
generatedFiles.push(...optimizedOutput.assets);
|
|
11444
10697
|
for (const page of optimizedOutput.pages) {
|
|
11445
|
-
await fs$1.mkdir(path$
|
|
10698
|
+
await fs$1.mkdir(path$1.dirname(page.outputPath), { recursive: true });
|
|
11446
10699
|
await fs$1.writeFile(page.outputPath, page.html, "utf-8");
|
|
11447
10700
|
generatedFiles.push(page.outputPath);
|
|
11448
10701
|
}
|
|
@@ -11497,7 +10750,7 @@ async function collectMarkdownFiles(dir) {
|
|
|
11497
10750
|
try {
|
|
11498
10751
|
const entries = await fs$1.readdir(currentDir, { withFileTypes: true });
|
|
11499
10752
|
for (const entry of entries) {
|
|
11500
|
-
const fullPath = path$
|
|
10753
|
+
const fullPath = path$1.join(currentDir, entry.name);
|
|
11501
10754
|
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") await walk(fullPath);
|
|
11502
10755
|
else if (entry.isFile() && entry.name.endsWith(".md")) files.push(fullPath);
|
|
11503
10756
|
}
|
|
@@ -11522,7 +10775,7 @@ async function buildSearchIndex(srcDir, base) {
|
|
|
11522
10775
|
const documents = [];
|
|
11523
10776
|
for (const file of files) try {
|
|
11524
10777
|
const content = await fs$1.readFile(file, "utf-8");
|
|
11525
|
-
const relativePath = path$
|
|
10778
|
+
const relativePath = path$1.relative(srcDir, file);
|
|
11526
10779
|
const url = base + relativePath.replace(/\.md$/, "").replace(/\\/g, "/");
|
|
11527
10780
|
const id = relativePath.replace(/\.md$/, "").replace(/\\/g, "/");
|
|
11528
10781
|
const extractSearchContent = napi.extractSearchContent;
|
|
@@ -11551,7 +10804,7 @@ async function buildSearchIndex(srcDir, base) {
|
|
|
11551
10804
|
* Writes the search index to a file.
|
|
11552
10805
|
*/
|
|
11553
10806
|
async function writeSearchIndex(indexJson, outDir) {
|
|
11554
|
-
const indexPath = path$
|
|
10807
|
+
const indexPath = path$1.join(outDir, "search-index.json");
|
|
11555
10808
|
await fs$1.mkdir(outDir, { recursive: true });
|
|
11556
10809
|
await fs$1.writeFile(indexPath, indexJson, "utf-8");
|
|
11557
10810
|
}
|
|
@@ -11560,234 +10813,7 @@ async function writeSearchIndex(indexJson, outDir) {
|
|
|
11560
10813
|
* This is injected into the bundle as a virtual module.
|
|
11561
10814
|
*/
|
|
11562
10815
|
function generateSearchModule(options, indexPath) {
|
|
11563
|
-
return
|
|
11564
|
-
// Search module generated by ox-content
|
|
11565
|
-
const searchOptions = ${JSON.stringify(options)};
|
|
11566
|
-
|
|
11567
|
-
let searchIndex = null;
|
|
11568
|
-
let indexPromise = null;
|
|
11569
|
-
|
|
11570
|
-
function parseScopedQuery(query) {
|
|
11571
|
-
const scopes = [];
|
|
11572
|
-
const terms = [];
|
|
11573
|
-
|
|
11574
|
-
for (const part of query.trim().split(/\\s+/).filter(Boolean)) {
|
|
11575
|
-
if (part.startsWith('@') && part.length > 1) {
|
|
11576
|
-
scopes.push(part.slice(1).toLowerCase());
|
|
11577
|
-
} else {
|
|
11578
|
-
terms.push(part);
|
|
11579
|
-
}
|
|
11580
|
-
}
|
|
11581
|
-
|
|
11582
|
-
return {
|
|
11583
|
-
text: terms.join(' ').trim(),
|
|
11584
|
-
scopes: [...new Set(scopes)],
|
|
11585
|
-
};
|
|
11586
|
-
}
|
|
11587
|
-
|
|
11588
|
-
function getScopesForDoc(doc) {
|
|
11589
|
-
const source = (doc.id || doc.url || '').replace(/^\\/+/, '').toLowerCase();
|
|
11590
|
-
const segments = source.split('/').filter(Boolean);
|
|
11591
|
-
|
|
11592
|
-
if (segments.length <= 1) {
|
|
11593
|
-
return [];
|
|
11594
|
-
}
|
|
11595
|
-
|
|
11596
|
-
const scopes = [];
|
|
11597
|
-
let current = '';
|
|
11598
|
-
for (const segment of segments.slice(0, -1)) {
|
|
11599
|
-
current = current ? current + '/' + segment : segment;
|
|
11600
|
-
scopes.push(current);
|
|
11601
|
-
}
|
|
11602
|
-
|
|
11603
|
-
return scopes;
|
|
11604
|
-
}
|
|
11605
|
-
|
|
11606
|
-
function matchesScopes(doc, scopes) {
|
|
11607
|
-
if (!scopes.length) {
|
|
11608
|
-
return true;
|
|
11609
|
-
}
|
|
11610
|
-
|
|
11611
|
-
const docScopes = new Set(getScopesForDoc(doc));
|
|
11612
|
-
return scopes.some(scope => docScopes.has(scope));
|
|
11613
|
-
}
|
|
11614
|
-
|
|
11615
|
-
// Tokenizer for queries
|
|
11616
|
-
function tokenizeQuery(text) {
|
|
11617
|
-
const tokens = [];
|
|
11618
|
-
let current = '';
|
|
11619
|
-
|
|
11620
|
-
for (const char of text) {
|
|
11621
|
-
const isCjk = /[\\u4E00-\\u9FFF\\u3400-\\u4DBF\\u3040-\\u309F\\u30A0-\\u30FF\\uAC00-\\uD7AF]/.test(char);
|
|
11622
|
-
|
|
11623
|
-
if (isCjk) {
|
|
11624
|
-
if (current) {
|
|
11625
|
-
tokens.push(current.toLowerCase());
|
|
11626
|
-
current = '';
|
|
11627
|
-
}
|
|
11628
|
-
tokens.push(char);
|
|
11629
|
-
} else if (/[a-zA-Z0-9_]/.test(char)) {
|
|
11630
|
-
current += char;
|
|
11631
|
-
} else if (current) {
|
|
11632
|
-
tokens.push(current.toLowerCase());
|
|
11633
|
-
current = '';
|
|
11634
|
-
}
|
|
11635
|
-
}
|
|
11636
|
-
|
|
11637
|
-
if (current) {
|
|
11638
|
-
tokens.push(current.toLowerCase());
|
|
11639
|
-
}
|
|
11640
|
-
|
|
11641
|
-
return tokens;
|
|
11642
|
-
}
|
|
11643
|
-
|
|
11644
|
-
// BM25 scoring
|
|
11645
|
-
function computeIdf(df, docCount) {
|
|
11646
|
-
return Math.log((docCount - df + 0.5) / (df + 0.5) + 1.0);
|
|
11647
|
-
}
|
|
11648
|
-
|
|
11649
|
-
function getFieldBoost(field) {
|
|
11650
|
-
switch (field) {
|
|
11651
|
-
case 'Title': return 10.0;
|
|
11652
|
-
case 'Heading': return 5.0;
|
|
11653
|
-
case 'Body': return 1.0;
|
|
11654
|
-
case 'Code': return 0.5;
|
|
11655
|
-
default: return 1.0;
|
|
11656
|
-
}
|
|
11657
|
-
}
|
|
11658
|
-
|
|
11659
|
-
// Load the index
|
|
11660
|
-
async function loadIndex() {
|
|
11661
|
-
if (searchIndex) return searchIndex;
|
|
11662
|
-
if (indexPromise) return indexPromise;
|
|
11663
|
-
|
|
11664
|
-
indexPromise = fetch('${indexPath}')
|
|
11665
|
-
.then(res => res.json())
|
|
11666
|
-
.then(data => {
|
|
11667
|
-
searchIndex = data;
|
|
11668
|
-
return data;
|
|
11669
|
-
})
|
|
11670
|
-
.catch(err => {
|
|
11671
|
-
console.error('[ox-content] Failed to load search index:', err);
|
|
11672
|
-
return null;
|
|
11673
|
-
});
|
|
11674
|
-
|
|
11675
|
-
return indexPromise;
|
|
11676
|
-
}
|
|
11677
|
-
|
|
11678
|
-
// Search function
|
|
11679
|
-
export async function search(query, options = {}) {
|
|
11680
|
-
const index = await loadIndex();
|
|
11681
|
-
|
|
11682
|
-
if (!index) {
|
|
11683
|
-
return [];
|
|
11684
|
-
}
|
|
11685
|
-
|
|
11686
|
-
const parsedQuery = parseScopedQuery(query);
|
|
11687
|
-
|
|
11688
|
-
if (!parsedQuery.text && parsedQuery.scopes.length === 0) {
|
|
11689
|
-
return [];
|
|
11690
|
-
}
|
|
11691
|
-
|
|
11692
|
-
const limit = options.limit ?? searchOptions.limit;
|
|
11693
|
-
const prefix = options.prefix ?? searchOptions.prefix;
|
|
11694
|
-
const tokens = tokenizeQuery(parsedQuery.text);
|
|
11695
|
-
|
|
11696
|
-
const k1 = 1.2;
|
|
11697
|
-
const b = 0.75;
|
|
11698
|
-
const docScores = new Map();
|
|
11699
|
-
|
|
11700
|
-
if (tokens.length === 0) {
|
|
11701
|
-
index.documents.forEach((doc, docIdx) => {
|
|
11702
|
-
if (matchesScopes(doc, parsedQuery.scopes)) {
|
|
11703
|
-
docScores.set(docIdx, { score: 0, matches: new Set() });
|
|
11704
|
-
}
|
|
11705
|
-
});
|
|
11706
|
-
}
|
|
11707
|
-
|
|
11708
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
11709
|
-
const token = tokens[i];
|
|
11710
|
-
const isLast = i === tokens.length - 1;
|
|
11711
|
-
|
|
11712
|
-
// Find matching terms
|
|
11713
|
-
let matchingTerms = [];
|
|
11714
|
-
if (prefix && isLast && token.length >= 2) {
|
|
11715
|
-
matchingTerms = Object.keys(index.index).filter(term => term.startsWith(token));
|
|
11716
|
-
} else if (index.index[token]) {
|
|
11717
|
-
matchingTerms = [token];
|
|
11718
|
-
}
|
|
11719
|
-
|
|
11720
|
-
for (const term of matchingTerms) {
|
|
11721
|
-
const postings = index.index[term] || [];
|
|
11722
|
-
const df = index.df[term] || 1;
|
|
11723
|
-
const idf = computeIdf(df, index.doc_count);
|
|
11724
|
-
|
|
11725
|
-
for (const posting of postings) {
|
|
11726
|
-
const doc = index.documents[posting.doc_idx];
|
|
11727
|
-
if (!doc) continue;
|
|
11728
|
-
if (!matchesScopes(doc, parsedQuery.scopes)) continue;
|
|
11729
|
-
|
|
11730
|
-
const docLen = doc.body.length;
|
|
11731
|
-
const tf = posting.tf;
|
|
11732
|
-
const boost = getFieldBoost(posting.field);
|
|
11733
|
-
|
|
11734
|
-
const score = idf * ((tf * (k1 + 1.0)) / (tf + k1 * (1.0 - b + b * docLen / index.avg_dl))) * boost;
|
|
11735
|
-
|
|
11736
|
-
if (!docScores.has(posting.doc_idx)) {
|
|
11737
|
-
docScores.set(posting.doc_idx, { score: 0, matches: new Set() });
|
|
11738
|
-
}
|
|
11739
|
-
const entry = docScores.get(posting.doc_idx);
|
|
11740
|
-
entry.score += score;
|
|
11741
|
-
entry.matches.add(term);
|
|
11742
|
-
}
|
|
11743
|
-
}
|
|
11744
|
-
}
|
|
11745
|
-
|
|
11746
|
-
// Convert to results
|
|
11747
|
-
const results = Array.from(docScores.entries())
|
|
11748
|
-
.map(([docIdx, data]) => {
|
|
11749
|
-
const doc = index.documents[docIdx];
|
|
11750
|
-
const matches = Array.from(data.matches);
|
|
11751
|
-
const scopes = getScopesForDoc(doc);
|
|
11752
|
-
|
|
11753
|
-
// Generate snippet
|
|
11754
|
-
let snippet = '';
|
|
11755
|
-
if (doc.body) {
|
|
11756
|
-
const bodyLower = doc.body.toLowerCase();
|
|
11757
|
-
let firstPos = -1;
|
|
11758
|
-
for (const match of matches) {
|
|
11759
|
-
const pos = bodyLower.indexOf(match);
|
|
11760
|
-
if (pos !== -1 && (firstPos === -1 || pos < firstPos)) {
|
|
11761
|
-
firstPos = pos;
|
|
11762
|
-
}
|
|
11763
|
-
}
|
|
11764
|
-
|
|
11765
|
-
const start = firstPos === -1 ? 0 : Math.max(0, firstPos - 50);
|
|
11766
|
-
const end = Math.min(doc.body.length, start + 150);
|
|
11767
|
-
snippet = doc.body.slice(start, end);
|
|
11768
|
-
if (start > 0) snippet = '...' + snippet;
|
|
11769
|
-
if (end < doc.body.length) snippet = snippet + '...';
|
|
11770
|
-
}
|
|
11771
|
-
|
|
11772
|
-
return {
|
|
11773
|
-
id: doc.id,
|
|
11774
|
-
title: doc.title,
|
|
11775
|
-
url: doc.url,
|
|
11776
|
-
score: data.score,
|
|
11777
|
-
matches,
|
|
11778
|
-
snippet,
|
|
11779
|
-
scopes,
|
|
11780
|
-
};
|
|
11781
|
-
})
|
|
11782
|
-
.sort((a, b) => b.score - a.score || a.title.localeCompare(b.title))
|
|
11783
|
-
.slice(0, limit);
|
|
11784
|
-
|
|
11785
|
-
return results;
|
|
11786
|
-
}
|
|
11787
|
-
|
|
11788
|
-
export { searchOptions };
|
|
11789
|
-
export default { search, searchOptions, loadIndex };
|
|
11790
|
-
`;
|
|
10816
|
+
return importNapiModuleSync().generateSearchModule(JSON.stringify(options), indexPath);
|
|
11791
10817
|
}
|
|
11792
10818
|
//#endregion
|
|
11793
10819
|
//#region src/dev-server.ts
|
|
@@ -11853,12 +10879,12 @@ async function resolveMarkdownFile(url, srcDir) {
|
|
|
11853
10879
|
let relativePath;
|
|
11854
10880
|
if (pathname === "/") relativePath = "index.md";
|
|
11855
10881
|
else relativePath = pathname.slice(1) + ".md";
|
|
11856
|
-
const filePath = path$
|
|
10882
|
+
const filePath = path$1.join(srcDir, relativePath);
|
|
11857
10883
|
try {
|
|
11858
10884
|
await fs$1.access(filePath);
|
|
11859
10885
|
return filePath;
|
|
11860
10886
|
} catch {
|
|
11861
|
-
const indexPath = path$
|
|
10887
|
+
const indexPath = path$1.join(srcDir, pathname === "/" ? "" : pathname.slice(1), "index.md");
|
|
11862
10888
|
try {
|
|
11863
10889
|
await fs$1.access(indexPath);
|
|
11864
10890
|
return indexPath;
|
|
@@ -11902,7 +10928,7 @@ function invalidatePageCache(cache, filePath) {
|
|
|
11902
10928
|
async function resolveSiteName(options, root) {
|
|
11903
10929
|
if (options.ssg.siteName) return options.ssg.siteName;
|
|
11904
10930
|
try {
|
|
11905
|
-
const pkgPath = path$
|
|
10931
|
+
const pkgPath = path$1.join(root, "package.json");
|
|
11906
10932
|
const pkg = JSON.parse(await fs$1.readFile(pkgPath, "utf-8"));
|
|
11907
10933
|
if (pkg.name) return formatTitle(pkg.name);
|
|
11908
10934
|
} catch {}
|
|
@@ -11912,7 +10938,7 @@ async function resolveSiteName(options, root) {
|
|
|
11912
10938
|
* Render a single markdown page to full HTML.
|
|
11913
10939
|
*/
|
|
11914
10940
|
async function renderPage$1(filePath, options, navGroups, siteName, base, root) {
|
|
11915
|
-
const srcDir = path$
|
|
10941
|
+
const srcDir = path$1.resolve(root, options.srcDir);
|
|
11916
10942
|
resetTabGroupCounter();
|
|
11917
10943
|
resetIslandCounter();
|
|
11918
10944
|
const result = await transformMarkdown(await fs$1.readFile(filePath, "utf-8"), filePath, options, {
|
|
@@ -11957,7 +10983,7 @@ async function renderPage$1(filePath, options, navGroups, siteName, base, root)
|
|
|
11957
10983
|
* Create the dev server middleware for SSG page serving.
|
|
11958
10984
|
*/
|
|
11959
10985
|
function createDevServerMiddleware(options, root, cache) {
|
|
11960
|
-
const srcDir = path$
|
|
10986
|
+
const srcDir = path$1.resolve(root, options.srcDir);
|
|
11961
10987
|
const base = options.base.endsWith("/") ? options.base : options.base + "/";
|
|
11962
10988
|
return async (req, res, next) => {
|
|
11963
10989
|
const url = req.url;
|
|
@@ -12022,7 +11048,7 @@ function extractTitle(content, frontmatter) {
|
|
|
12022
11048
|
return match ? match[1].trim() : "";
|
|
12023
11049
|
}
|
|
12024
11050
|
function getUrlPath(filePath, srcDir) {
|
|
12025
|
-
let rel = path$
|
|
11051
|
+
let rel = path$1.relative(srcDir, filePath).replace(/\\/g, "/");
|
|
12026
11052
|
rel = rel.replace(/\.md$/, "");
|
|
12027
11053
|
if (rel === "index") return "/";
|
|
12028
11054
|
if (rel.endsWith("/index")) rel = rel.slice(0, -6);
|
|
@@ -12062,7 +11088,7 @@ function validatePage(page, options) {
|
|
|
12062
11088
|
return warnings;
|
|
12063
11089
|
}
|
|
12064
11090
|
async function collectPages(options, root) {
|
|
12065
|
-
const srcDir = path$
|
|
11091
|
+
const srcDir = path$1.resolve(root, options.srcDir);
|
|
12066
11092
|
const files = await glob("**/*.md", {
|
|
12067
11093
|
cwd: srcDir,
|
|
12068
11094
|
absolute: true
|
|
@@ -12080,7 +11106,7 @@ async function collectPages(options, root) {
|
|
|
12080
11106
|
const urlPath = getUrlPath(file, srcDir);
|
|
12081
11107
|
const ogImageUrl = computeOgImageUrl(urlPath, options.base, options.ssg.siteUrl, generateOgImage, options.ssg.ogImage);
|
|
12082
11108
|
const page = {
|
|
12083
|
-
path: path$
|
|
11109
|
+
path: path$1.relative(srcDir, file),
|
|
12084
11110
|
urlPath,
|
|
12085
11111
|
title,
|
|
12086
11112
|
description,
|
|
@@ -12393,7 +11419,7 @@ function createI18nPlugin(resolvedOptions) {
|
|
|
12393
11419
|
},
|
|
12394
11420
|
async buildStart() {
|
|
12395
11421
|
if (!i18nOptions || !i18nOptions.check) return;
|
|
12396
|
-
const dictDir = path$
|
|
11422
|
+
const dictDir = path$1.resolve(root, i18nOptions.dir);
|
|
12397
11423
|
if (!fs$2.existsSync(dictDir)) {
|
|
12398
11424
|
console.warn(`[ox-content:i18n] Dictionary directory not found: ${dictDir}`);
|
|
12399
11425
|
return;
|
|
@@ -12415,7 +11441,7 @@ function createI18nPlugin(resolvedOptions) {
|
|
|
12415
11441
|
},
|
|
12416
11442
|
configureServer(server) {
|
|
12417
11443
|
if (!i18nOptions) return;
|
|
12418
|
-
const dictDir = path$
|
|
11444
|
+
const dictDir = path$1.resolve(root, i18nOptions.dir);
|
|
12419
11445
|
if (fs$2.existsSync(dictDir)) {
|
|
12420
11446
|
server.watcher.add(dictDir);
|
|
12421
11447
|
server.watcher.on("change", (filePath) => {
|
|
@@ -12428,7 +11454,7 @@ function createI18nPlugin(resolvedOptions) {
|
|
|
12428
11454
|
}
|
|
12429
11455
|
server.middlewares.use((req, _res, next) => {
|
|
12430
11456
|
if (!req.url) return next();
|
|
12431
|
-
const localeMatch = req.url.match(/^\/([
|
|
11457
|
+
const localeMatch = req.url.match(/^\/([A-Za-z]{2,3}(?:-[A-Za-z0-9]+)*)(\/|$)/);
|
|
12432
11458
|
if (localeMatch) {
|
|
12433
11459
|
const localeCode = localeMatch[1];
|
|
12434
11460
|
if (i18nOptions.locales.some((l) => l.code === localeCode)) req.__oxLocale = localeCode;
|
|
@@ -12442,117 +11468,31 @@ function createI18nPlugin(resolvedOptions) {
|
|
|
12442
11468
|
* Generates the virtual module for i18n configuration.
|
|
12443
11469
|
*/
|
|
12444
11470
|
function generateI18nModule(options, root) {
|
|
12445
|
-
const dictDir = path$
|
|
12446
|
-
const
|
|
12447
|
-
|
|
12448
|
-
|
|
11471
|
+
const dictDir = path$1.resolve(root, options.dir);
|
|
11472
|
+
const config = {
|
|
11473
|
+
defaultLocale: options.defaultLocale,
|
|
11474
|
+
locales: options.locales,
|
|
11475
|
+
hideDefaultLocale: options.hideDefaultLocale
|
|
11476
|
+
};
|
|
12449
11477
|
try {
|
|
12450
11478
|
const napi = __require("@ox-content/napi");
|
|
12451
|
-
if (napi.
|
|
12452
|
-
|
|
12453
|
-
|
|
12454
|
-
} else dictionariesCode = JSON.stringify(loadDictionariesFallback(options, dictDir));
|
|
12455
|
-
} catch {
|
|
12456
|
-
try {
|
|
12457
|
-
dictionariesCode = JSON.stringify(loadDictionariesFallback(options, dictDir));
|
|
12458
|
-
} catch {}
|
|
12459
|
-
}
|
|
12460
|
-
return `
|
|
12461
|
-
export const i18nConfig = {
|
|
12462
|
-
enabled: true,
|
|
12463
|
-
defaultLocale: ${defaultLocale},
|
|
12464
|
-
locales: ${localesJson},
|
|
12465
|
-
hideDefaultLocale: ${JSON.stringify(options.hideDefaultLocale)},
|
|
12466
|
-
};
|
|
12467
|
-
|
|
12468
|
-
export const dictionaries = ${dictionariesCode};
|
|
12469
|
-
|
|
12470
|
-
export function t(key, params, locale) {
|
|
12471
|
-
const dict = dictionaries[locale || i18nConfig.defaultLocale] || {};
|
|
12472
|
-
let message = dict[key];
|
|
12473
|
-
if (!message) {
|
|
12474
|
-
const fallback = dictionaries[i18nConfig.defaultLocale] || {};
|
|
12475
|
-
message = fallback[key] || key;
|
|
12476
|
-
}
|
|
12477
|
-
if (params) {
|
|
12478
|
-
for (const [k, v] of Object.entries(params)) {
|
|
12479
|
-
message = message.replace(new RegExp('\\\\{\\\\$' + k + '\\\\}', 'g'), String(v));
|
|
12480
|
-
}
|
|
12481
|
-
}
|
|
12482
|
-
return message;
|
|
12483
|
-
}
|
|
12484
|
-
|
|
12485
|
-
export function getLocaleFromPath(pathname) {
|
|
12486
|
-
const match = pathname.match(/^\\/([a-z]{2}(?:-[a-zA-Z]+)?)(\\//|$)/);
|
|
12487
|
-
if (match) {
|
|
12488
|
-
const code = match[1];
|
|
12489
|
-
if (i18nConfig.locales.some(l => l.code === code)) {
|
|
12490
|
-
return code;
|
|
12491
|
-
}
|
|
12492
|
-
}
|
|
12493
|
-
return i18nConfig.defaultLocale;
|
|
12494
|
-
}
|
|
12495
|
-
|
|
12496
|
-
export function localePath(pathname, locale) {
|
|
12497
|
-
const current = getLocaleFromPath(pathname);
|
|
12498
|
-
let clean = pathname;
|
|
12499
|
-
if (current !== i18nConfig.defaultLocale || !i18nConfig.hideDefaultLocale) {
|
|
12500
|
-
clean = pathname.replace(new RegExp('^/' + current + '(/|$)'), '/');
|
|
12501
|
-
}
|
|
12502
|
-
if (locale === i18nConfig.defaultLocale && i18nConfig.hideDefaultLocale) {
|
|
12503
|
-
return clean || '/';
|
|
12504
|
-
}
|
|
12505
|
-
return '/' + locale + (clean.startsWith('/') ? clean : '/' + clean);
|
|
12506
|
-
}
|
|
12507
|
-
|
|
12508
|
-
export default { i18nConfig, dictionaries, t, getLocaleFromPath, localePath };
|
|
12509
|
-
`;
|
|
12510
|
-
}
|
|
12511
|
-
/**
|
|
12512
|
-
* Flattens a nested object into dot-separated keys.
|
|
12513
|
-
*/
|
|
12514
|
-
function flattenObject(obj, prefix, result) {
|
|
12515
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
12516
|
-
const fullKey = `${prefix}.${key}`;
|
|
12517
|
-
if (typeof value === "string") result[fullKey] = value;
|
|
12518
|
-
else if (typeof value === "object" && value !== null && !Array.isArray(value)) flattenObject(value, fullKey, result);
|
|
12519
|
-
else result[fullKey] = String(value);
|
|
12520
|
-
}
|
|
12521
|
-
}
|
|
12522
|
-
/**
|
|
12523
|
-
* Fallback dictionary loading using TS-based JSON file reading.
|
|
12524
|
-
*/
|
|
12525
|
-
function loadDictionariesFallback(options, dictDir) {
|
|
12526
|
-
const dictData = {};
|
|
12527
|
-
for (const locale of options.locales) {
|
|
12528
|
-
const localeDir = path$2.join(dictDir, locale.code);
|
|
12529
|
-
if (!fs$2.existsSync(localeDir)) continue;
|
|
12530
|
-
const files = fs$2.readdirSync(localeDir);
|
|
12531
|
-
const localeDict = {};
|
|
12532
|
-
for (const file of files) {
|
|
12533
|
-
if (!file.endsWith(".json")) continue;
|
|
12534
|
-
const filePath = path$2.join(localeDir, file);
|
|
12535
|
-
const content = fs$2.readFileSync(filePath, "utf-8");
|
|
12536
|
-
const namespace = path$2.basename(file, ".json");
|
|
12537
|
-
try {
|
|
12538
|
-
flattenObject(JSON.parse(content), namespace, localeDict);
|
|
12539
|
-
} catch {}
|
|
12540
|
-
}
|
|
12541
|
-
dictData[locale.code] = localeDict;
|
|
11479
|
+
if (typeof napi.generateI18nModule === "function") return napi.generateI18nModule(dictDir, config);
|
|
11480
|
+
} catch (error) {
|
|
11481
|
+
throw new Error(`[ox-content:i18n] Failed to load @ox-content/napi for i18n module generation: ${String(error)}`);
|
|
12542
11482
|
}
|
|
12543
|
-
|
|
11483
|
+
throw new Error("[ox-content:i18n] @ox-content/napi does not expose generateI18nModule. Please rebuild the NAPI package.");
|
|
12544
11484
|
}
|
|
12545
11485
|
/**
|
|
12546
11486
|
* Collects translation keys from source files using NAPI extractTranslationKeys.
|
|
12547
11487
|
*/
|
|
12548
11488
|
function collectKeysFromSource(root, extractTranslationKeys, options) {
|
|
12549
|
-
const srcDir = path$
|
|
11489
|
+
const srcDir = path$1.resolve(root, "src");
|
|
12550
11490
|
const keys = /* @__PURE__ */ new Set();
|
|
12551
11491
|
if (fs$2.existsSync(srcDir)) walkDir(srcDir, /\.(ts|tsx|js|jsx)$/, (filePath) => {
|
|
12552
11492
|
const usages = extractTranslationKeys(fs$2.readFileSync(filePath, "utf-8"), filePath, options.functionNames);
|
|
12553
11493
|
for (const usage of usages) keys.add(usage.key);
|
|
12554
11494
|
});
|
|
12555
|
-
const contentDir = path$
|
|
11495
|
+
const contentDir = path$1.resolve(root, "content");
|
|
12556
11496
|
if (fs$2.existsSync(contentDir)) {
|
|
12557
11497
|
const tPattern = /\{\{t\(['"]([^'"]+)['"]\)\}\}/g;
|
|
12558
11498
|
walkDir(contentDir, /\.(md|mdx)$/, (filePath) => {
|
|
@@ -12570,7 +11510,7 @@ function collectKeysFromSource(root, extractTranslationKeys, options) {
|
|
|
12570
11510
|
function walkDir(dir, pattern, callback) {
|
|
12571
11511
|
const entries = fs$2.readdirSync(dir, { withFileTypes: true });
|
|
12572
11512
|
for (const entry of entries) {
|
|
12573
|
-
const fullPath = path$
|
|
11513
|
+
const fullPath = path$1.join(dir, entry.name);
|
|
12574
11514
|
if (entry.isDirectory()) {
|
|
12575
11515
|
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
12576
11516
|
walkDir(fullPath, pattern, callback);
|
|
@@ -12823,7 +11763,7 @@ const DEFAULT_LINT_FILE_EXCLUDE = [
|
|
|
12823
11763
|
*/
|
|
12824
11764
|
function shouldLintMarkdownFile(filePath, options = {}) {
|
|
12825
11765
|
const resolvedOptions = resolveMarkdownLintFileOptions(options);
|
|
12826
|
-
return shouldLintAbsoluteFile(path
|
|
11766
|
+
return shouldLintAbsoluteFile(path.resolve(resolvedOptions.cwd, filePath), resolvedOptions);
|
|
12827
11767
|
}
|
|
12828
11768
|
/**
|
|
12829
11769
|
* Lints a single Markdown file using project-style include/exclude settings.
|
|
@@ -12833,7 +11773,7 @@ function shouldLintMarkdownFile(filePath, options = {}) {
|
|
|
12833
11773
|
*/
|
|
12834
11774
|
async function lintMarkdownFile(filePath, options = {}) {
|
|
12835
11775
|
const resolvedOptions = resolveMarkdownLintFileOptions(options);
|
|
12836
|
-
return lintMarkdownFileWithResolvedOptions(path
|
|
11776
|
+
return lintMarkdownFileWithResolvedOptions(path.resolve(resolvedOptions.cwd, filePath), resolvedOptions);
|
|
12837
11777
|
}
|
|
12838
11778
|
/**
|
|
12839
11779
|
* Lints all Markdown files matched by the configured include/exclude patterns.
|
|
@@ -12864,7 +11804,7 @@ async function lintMarkdownFiles(options = {}) {
|
|
|
12864
11804
|
}
|
|
12865
11805
|
function resolveMarkdownLintFileOptions(options) {
|
|
12866
11806
|
return {
|
|
12867
|
-
cwd: path
|
|
11807
|
+
cwd: path.resolve(options.cwd ?? process.cwd()),
|
|
12868
11808
|
exclude: [...new Set([...options.exclude ?? DEFAULT_LINT_FILE_EXCLUDE, ...options.ignore ?? []])],
|
|
12869
11809
|
include: [...new Set(options.include ?? DEFAULT_LINT_FILE_INCLUDE)],
|
|
12870
11810
|
lintOptions: {
|
|
@@ -12875,8 +11815,8 @@ function resolveMarkdownLintFileOptions(options) {
|
|
|
12875
11815
|
};
|
|
12876
11816
|
}
|
|
12877
11817
|
async function lintMarkdownFileWithResolvedOptions(filePath, options) {
|
|
12878
|
-
const absoluteFilePath = path
|
|
12879
|
-
const relativePath = normalizePath(path
|
|
11818
|
+
const absoluteFilePath = path.resolve(filePath);
|
|
11819
|
+
const relativePath = normalizePath(path.relative(options.cwd, absoluteFilePath));
|
|
12880
11820
|
if (!shouldLintAbsoluteFile(absoluteFilePath, options)) return {
|
|
12881
11821
|
...createEmptyLintResult(),
|
|
12882
11822
|
filePath: absoluteFilePath,
|
|
@@ -12900,26 +11840,26 @@ async function collectMarkdownLintFileEntries(options) {
|
|
|
12900
11840
|
nodir: true
|
|
12901
11841
|
});
|
|
12902
11842
|
for (const filePath of matches) {
|
|
12903
|
-
const absoluteFilePath = path
|
|
11843
|
+
const absoluteFilePath = path.resolve(filePath);
|
|
12904
11844
|
if (shouldLintAbsoluteFile(absoluteFilePath, options)) files.set(absoluteFilePath, {
|
|
12905
11845
|
filePath: absoluteFilePath,
|
|
12906
|
-
relativePath: normalizePath(path
|
|
11846
|
+
relativePath: normalizePath(path.relative(options.cwd, absoluteFilePath))
|
|
12907
11847
|
});
|
|
12908
11848
|
}
|
|
12909
11849
|
}
|
|
12910
11850
|
return [...files.values()].sort((left, right) => left.filePath.localeCompare(right.filePath));
|
|
12911
11851
|
}
|
|
12912
11852
|
function shouldLintAbsoluteFile(filePath, options) {
|
|
12913
|
-
const absolutePath = normalizePath(path
|
|
12914
|
-
const relativePath = normalizePath(path
|
|
11853
|
+
const absolutePath = normalizePath(path.resolve(filePath));
|
|
11854
|
+
const relativePath = normalizePath(path.relative(options.cwd, absolutePath));
|
|
12915
11855
|
const matches = (patterns) => patterns.some((pattern) => {
|
|
12916
11856
|
const normalizedPattern = normalizePath(pattern);
|
|
12917
|
-
return path
|
|
11857
|
+
return path.matchesGlob(relativePath, normalizedPattern) || path.matchesGlob(absolutePath, normalizedPattern);
|
|
12918
11858
|
});
|
|
12919
11859
|
return matches(options.include) && !matches(options.exclude);
|
|
12920
11860
|
}
|
|
12921
11861
|
function normalizePath(value) {
|
|
12922
|
-
return value.split(path
|
|
11862
|
+
return value.split(path.sep).join("/");
|
|
12923
11863
|
}
|
|
12924
11864
|
function createEmptyLintResult() {
|
|
12925
11865
|
return {
|
|
@@ -13504,8 +12444,8 @@ function oxContent(options = {}) {
|
|
|
13504
12444
|
async function regenerateDocs(root) {
|
|
13505
12445
|
const docsOptions = resolvedOptions.docs;
|
|
13506
12446
|
if (!docsOptions || !docsOptions.enabled) return 0;
|
|
13507
|
-
const srcDirs = docsOptions.src.map((src) => path$
|
|
13508
|
-
const outDir = path$
|
|
12447
|
+
const srcDirs = docsOptions.src.map((src) => path$1.resolve(root, src));
|
|
12448
|
+
const outDir = path$1.resolve(root, docsOptions.out);
|
|
13509
12449
|
const extracted = await extractDocs(srcDirs, docsOptions);
|
|
13510
12450
|
const generated = generateMarkdown(extracted, docsOptions);
|
|
13511
12451
|
await writeDocs(generated, outDir, extracted, docsOptions);
|
|
@@ -13574,7 +12514,7 @@ function oxContent(options = {}) {
|
|
|
13574
12514
|
const docsOptions = resolvedOptions.docs;
|
|
13575
12515
|
if (!docsOptions || !docsOptions.enabled) return;
|
|
13576
12516
|
const root = config?.root || process.cwd();
|
|
13577
|
-
const srcDirs = docsOptions.src.map((src) => path$
|
|
12517
|
+
const srcDirs = docsOptions.src.map((src) => path$1.resolve(root, src));
|
|
13578
12518
|
for (const srcDir of srcDirs) devServer.watcher.add(srcDir);
|
|
13579
12519
|
devServer.watcher.on("all", async (event, file) => {
|
|
13580
12520
|
if (event !== "add" && event !== "change" && event !== "unlink") return;
|
|
@@ -13590,7 +12530,7 @@ function oxContent(options = {}) {
|
|
|
13590
12530
|
configureServer(devServer) {
|
|
13591
12531
|
if (!resolvedOptions.ssg.enabled) return;
|
|
13592
12532
|
const root = config?.root || process.cwd();
|
|
13593
|
-
const srcDir = path$
|
|
12533
|
+
const srcDir = path$1.resolve(root, resolvedOptions.srcDir);
|
|
13594
12534
|
devServer.middlewares.use(createDevServerMiddleware(resolvedOptions, root, ssgDevCache));
|
|
13595
12535
|
devServer.watcher.on("add", (file) => {
|
|
13596
12536
|
if (file.startsWith(srcDir) && file.endsWith(".md")) {
|
|
@@ -13657,7 +12597,7 @@ function oxContent(options = {}) {
|
|
|
13657
12597
|
async buildStart() {
|
|
13658
12598
|
if (!resolvedOptions.search.enabled) return;
|
|
13659
12599
|
const root = config?.root || process.cwd();
|
|
13660
|
-
const srcDir = path$
|
|
12600
|
+
const srcDir = path$1.resolve(root, resolvedOptions.srcDir);
|
|
13661
12601
|
try {
|
|
13662
12602
|
searchIndexJson = await buildSearchIndex(srcDir, resolvedOptions.base);
|
|
13663
12603
|
console.log("[ox-content] Search index built");
|
|
@@ -13668,10 +12608,10 @@ function oxContent(options = {}) {
|
|
|
13668
12608
|
async closeBundle() {
|
|
13669
12609
|
if (!resolvedOptions.search.enabled || !searchIndexJson) return;
|
|
13670
12610
|
const root = config?.root || process.cwd();
|
|
13671
|
-
const outDir = path$
|
|
12611
|
+
const outDir = path$1.resolve(root, resolvedOptions.outDir);
|
|
13672
12612
|
try {
|
|
13673
12613
|
await writeSearchIndex(searchIndexJson, outDir);
|
|
13674
|
-
console.log("[ox-content] Search index written to", path$
|
|
12614
|
+
console.log("[ox-content] Search index written to", path$1.join(outDir, "search-index.json"));
|
|
13675
12615
|
} catch (err) {
|
|
13676
12616
|
console.warn("[ox-content] Failed to write search index:", err);
|
|
13677
12617
|
}
|
|
@@ -13738,19 +12678,53 @@ function resolveCodeAnnotationsOptions(options) {
|
|
|
13738
12678
|
*/
|
|
13739
12679
|
function generateVirtualModule(path, options) {
|
|
13740
12680
|
if (path === "config") return `export default ${JSON.stringify(options)};`;
|
|
13741
|
-
if (path === "runtime")
|
|
12681
|
+
if (path === "runtime") {
|
|
12682
|
+
const base = normalizeRuntimeBase(options.base);
|
|
12683
|
+
return `
|
|
12684
|
+
export const base = ${JSON.stringify(base)};
|
|
12685
|
+
export const runtimeConfig = { base };
|
|
12686
|
+
|
|
12687
|
+
export function isExternalUrl(value) {
|
|
12688
|
+
return /^(?:https?:)?\\/\\//i.test(value) || /^(?:mailto|tel):/i.test(value);
|
|
12689
|
+
}
|
|
12690
|
+
|
|
12691
|
+
export function withBase(pathname = "") {
|
|
12692
|
+
const value = String(pathname);
|
|
12693
|
+
if (!value || value === "/") return base;
|
|
12694
|
+
if (value.startsWith("#") || isExternalUrl(value)) return value;
|
|
12695
|
+
return base + (value.startsWith("/") ? value.slice(1) : value);
|
|
12696
|
+
}
|
|
12697
|
+
|
|
12698
|
+
export function withoutBase(pathname = "") {
|
|
12699
|
+
const value = String(pathname);
|
|
12700
|
+
if (base === "/" || value.startsWith("#") || isExternalUrl(value)) return value;
|
|
12701
|
+
const bareBase = base.slice(0, -1);
|
|
12702
|
+
if (value === bareBase) return "/";
|
|
12703
|
+
if (value.startsWith(base)) return "/" + value.slice(base.length);
|
|
12704
|
+
return value;
|
|
12705
|
+
}
|
|
12706
|
+
|
|
13742
12707
|
export function useMarkdown() {
|
|
13743
12708
|
return {
|
|
12709
|
+
base,
|
|
12710
|
+
withBase,
|
|
12711
|
+
withoutBase,
|
|
13744
12712
|
render: (content) => {
|
|
13745
|
-
// Client-side rendering if needed
|
|
13746
12713
|
return content;
|
|
13747
12714
|
},
|
|
13748
12715
|
};
|
|
13749
12716
|
}
|
|
13750
12717
|
`;
|
|
12718
|
+
}
|
|
13751
12719
|
return "export default {};";
|
|
13752
12720
|
}
|
|
12721
|
+
function normalizeRuntimeBase(base) {
|
|
12722
|
+
const trimmed = base.trim();
|
|
12723
|
+
if (!trimmed || trimmed === "/") return "/";
|
|
12724
|
+
const withLeading = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
12725
|
+
return withLeading.endsWith("/") ? withLeading : `${withLeading}/`;
|
|
12726
|
+
}
|
|
13753
12727
|
//#endregion
|
|
13754
|
-
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, 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 };
|
|
12728
|
+
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 };
|
|
13755
12729
|
|
|
13756
12730
|
//# sourceMappingURL=index.mjs.map
|