specra 0.2.13 → 0.2.15
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/config/rehype-base-path.d.ts +1 -0
- package/config/svelte-config.js +26 -8
- package/dist/components/docs/Breadcrumb.svelte +3 -2
- package/dist/components/docs/CategoryIndex.svelte +3 -2
- package/dist/components/docs/DocNavigation.svelte +3 -2
- package/dist/components/docs/Footer.svelte +10 -1
- package/dist/components/docs/Header.svelte +2 -1
- package/dist/components/docs/NotFoundContent.svelte +4 -3
- package/dist/components/docs/ProductSwitcher.svelte +3 -2
- package/dist/components/docs/SidebarMenuItems.svelte +4 -3
- package/dist/components/docs/TabGroups.svelte +3 -2
- package/dist/mdx.js +33 -2
- package/dist/rehype-base-path.d.ts +6 -0
- package/dist/rehype-base-path.js +29 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function rehypeBasePath(options?: {}): (tree: any) => void;
|
package/config/svelte-config.js
CHANGED
|
@@ -19,6 +19,7 @@ import rehypeKatex from 'rehype-katex'
|
|
|
19
19
|
import rehypeRaw from 'rehype-raw'
|
|
20
20
|
import fs from 'fs'
|
|
21
21
|
import path from 'path'
|
|
22
|
+
import { rehypeBasePath } from '../dist/rehype-base-path.js'
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Get mdsvex preprocessor config with all Specra remark/rehype plugins
|
|
@@ -48,18 +49,26 @@ export function specraMdsvexConfig(options = {}) {
|
|
|
48
49
|
* Read the deployment basePath from specra.config.json if present.
|
|
49
50
|
* Falls back to the BASE_PATH environment variable, then empty string.
|
|
50
51
|
*/
|
|
52
|
+
/**
|
|
53
|
+
* Normalize a base path: ensure leading slash, strip trailing slash.
|
|
54
|
+
* "task_flow" → "/task_flow", "/task_flow/" → "/task_flow", "" → ""
|
|
55
|
+
*/
|
|
56
|
+
function normalizeBasePath(bp) {
|
|
57
|
+
if (!bp) return ''
|
|
58
|
+
let normalized = bp.startsWith('/') ? bp : `/${bp}`
|
|
59
|
+
return normalized.replace(/\/+$/, '')
|
|
60
|
+
}
|
|
61
|
+
|
|
51
62
|
function resolveBasePath(configPath = path.join(process.cwd(), 'specra.config.json')) {
|
|
52
63
|
// Environment variable takes priority
|
|
53
|
-
if (process.env.BASE_PATH) return process.env.BASE_PATH
|
|
64
|
+
if (process.env.BASE_PATH) return normalizeBasePath(process.env.BASE_PATH)
|
|
54
65
|
|
|
55
66
|
try {
|
|
56
67
|
if (fs.existsSync(configPath)) {
|
|
57
68
|
const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'))
|
|
58
69
|
if (raw.deployment?.basePath) {
|
|
59
|
-
const bp = raw.deployment.basePath
|
|
60
|
-
// If custom domain is set, ignore basePath
|
|
61
70
|
if (raw.deployment?.customDomain) return ''
|
|
62
|
-
return
|
|
71
|
+
return normalizeBasePath(raw.deployment.basePath)
|
|
63
72
|
}
|
|
64
73
|
}
|
|
65
74
|
} catch {
|
|
@@ -72,7 +81,7 @@ function resolveBasePath(configPath = path.join(process.cwd(), 'specra.config.js
|
|
|
72
81
|
* Scan the docs/ directory and return prerender entries for all version root pages.
|
|
73
82
|
* This ensures adapter-static discovers and prerenders every version, not just the active one.
|
|
74
83
|
*/
|
|
75
|
-
function discoverVersionEntries(
|
|
84
|
+
function discoverVersionEntries(docsDir = path.join(process.cwd(), 'docs')) {
|
|
76
85
|
const entries = ['/']
|
|
77
86
|
try {
|
|
78
87
|
if (!fs.existsSync(docsDir)) return entries
|
|
@@ -80,7 +89,7 @@ function discoverVersionEntries(basePath = '', docsDir = path.join(process.cwd()
|
|
|
80
89
|
const items = fs.readdirSync(docsDir, { withFileTypes: true })
|
|
81
90
|
for (const item of items) {
|
|
82
91
|
if (item.isDirectory() && /^v\d/.test(item.name)) {
|
|
83
|
-
entries.push(
|
|
92
|
+
entries.push(`/docs/${item.name}`)
|
|
84
93
|
}
|
|
85
94
|
}
|
|
86
95
|
} catch {
|
|
@@ -99,11 +108,20 @@ export function specraConfig(options = {}) {
|
|
|
99
108
|
const userPrerender = options.kit?.prerender || {}
|
|
100
109
|
const basePath = options.kit?.paths?.base ?? resolveBasePath()
|
|
101
110
|
|
|
111
|
+
// Inject base path rehype plugin into mdsvex if basePath is set
|
|
112
|
+
const mdsvexOptions = options.mdsvex || {}
|
|
113
|
+
if (basePath) {
|
|
114
|
+
mdsvexOptions.rehypePlugins = [
|
|
115
|
+
...(mdsvexOptions.rehypePlugins || []),
|
|
116
|
+
[rehypeBasePath, { basePath }],
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
|
|
102
120
|
return {
|
|
103
121
|
extensions: ['.svelte', '.md', '.svx', '.mdx'],
|
|
104
122
|
preprocess: [
|
|
105
123
|
...(vitePreprocess ? [vitePreprocess()] : []),
|
|
106
|
-
mdsvex(specraMdsvexConfig(
|
|
124
|
+
mdsvex(specraMdsvexConfig(mdsvexOptions))
|
|
107
125
|
],
|
|
108
126
|
kit: {
|
|
109
127
|
...options.kit,
|
|
@@ -115,7 +133,7 @@ export function specraConfig(options = {}) {
|
|
|
115
133
|
handleHttpError: 'warn',
|
|
116
134
|
handleMissingId: 'warn',
|
|
117
135
|
handleUnseenRoutes: 'warn',
|
|
118
|
-
entries: discoverVersionEntries(
|
|
136
|
+
entries: discoverVersionEntries(),
|
|
119
137
|
...userPrerender,
|
|
120
138
|
}
|
|
121
139
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { ChevronRight } from 'lucide-svelte';
|
|
3
|
+
import { base } from '$app/paths';
|
|
3
4
|
import { getConfigContext } from '../../stores/config.js';
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
@@ -13,8 +14,8 @@
|
|
|
13
14
|
|
|
14
15
|
let docsBase = $derived(
|
|
15
16
|
product && product !== '_default_'
|
|
16
|
-
?
|
|
17
|
-
:
|
|
17
|
+
? `${base}/docs/${product}/${version}`
|
|
18
|
+
: `${base}/docs/${version}`
|
|
18
19
|
);
|
|
19
20
|
|
|
20
21
|
const configStore = getConfigContext();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { FileText, ArrowRight } from 'lucide-svelte';
|
|
3
|
+
import { base } from '$app/paths';
|
|
3
4
|
import type { SpecraConfig } from '../../config.types.js';
|
|
4
5
|
import type { Snippet } from 'svelte';
|
|
5
6
|
|
|
@@ -54,8 +55,8 @@
|
|
|
54
55
|
|
|
55
56
|
const baseUrl = $derived(
|
|
56
57
|
product && product !== '_default_'
|
|
57
|
-
?
|
|
58
|
-
:
|
|
58
|
+
? `${base}/docs/${product}`
|
|
59
|
+
: `${base}/docs`
|
|
59
60
|
);
|
|
60
61
|
|
|
61
62
|
// Note: We always use '/docs' as the base for non-product routes.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { ChevronLeft, ChevronRight } from 'lucide-svelte';
|
|
3
|
+
import { base } from '$app/paths';
|
|
3
4
|
|
|
4
5
|
interface NavDoc {
|
|
5
6
|
title: string;
|
|
@@ -17,8 +18,8 @@
|
|
|
17
18
|
|
|
18
19
|
let docsBase = $derived(
|
|
19
20
|
product && product !== '_default_'
|
|
20
|
-
?
|
|
21
|
-
:
|
|
21
|
+
? `${base}/docs/${product}/${version}`
|
|
22
|
+
: `${base}/docs/${version}`
|
|
22
23
|
);
|
|
23
24
|
</script>
|
|
24
25
|
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { SpecraConfig } from '../../config.types.js';
|
|
3
|
+
import { base } from '$app/paths';
|
|
3
4
|
import Logo from './Logo.svelte';
|
|
4
5
|
|
|
6
|
+
/** Prefix internal links with base path */
|
|
7
|
+
function resolveHref(href: string): string {
|
|
8
|
+
if (href.startsWith('/') && !href.startsWith('//')) {
|
|
9
|
+
return `${base}${href}`;
|
|
10
|
+
}
|
|
11
|
+
return href;
|
|
12
|
+
}
|
|
13
|
+
|
|
5
14
|
interface Props {
|
|
6
15
|
config: SpecraConfig;
|
|
7
16
|
}
|
|
@@ -25,7 +34,7 @@
|
|
|
25
34
|
{#each column.items as item, itemIdx (itemIdx)}
|
|
26
35
|
<li>
|
|
27
36
|
<a
|
|
28
|
-
href={item.href}
|
|
37
|
+
href={resolveHref(item.href)}
|
|
29
38
|
class="text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
30
39
|
>
|
|
31
40
|
{item.label}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { Search, Menu, Github, Twitter, MessageCircle } from 'lucide-svelte';
|
|
3
|
+
import { base } from '$app/paths';
|
|
3
4
|
import { getConfigContext } from '../../stores/config.js';
|
|
4
5
|
import { sidebarStore } from '../../stores/sidebar.js';
|
|
5
6
|
import VersionSwitcher from './VersionSwitcher.svelte';
|
|
@@ -110,7 +111,7 @@
|
|
|
110
111
|
>
|
|
111
112
|
<Menu class="h-5 w-5" />
|
|
112
113
|
</button>
|
|
113
|
-
<a href="/" class="flex items-center gap-2">
|
|
114
|
+
<a href="{base}/" class="flex items-center gap-2">
|
|
114
115
|
{#if !config.site.hideLogo}
|
|
115
116
|
{#if config.site.logo}
|
|
116
117
|
<Logo logo={config.site.logo} alt={config.site.title} className="w-18 object-contain" />
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { FileQuestion, Home, ArrowLeft } from 'lucide-svelte';
|
|
3
|
+
import { base } from '$app/paths';
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
6
|
version?: string;
|
|
@@ -11,10 +12,10 @@
|
|
|
11
12
|
/** URL prefix: /docs/{product}/{version} for named products, /docs/{version} for default */
|
|
12
13
|
const homeLink = $derived(
|
|
13
14
|
product && product !== '_default_' && version
|
|
14
|
-
?
|
|
15
|
+
? `${base}/docs/${product}/${version}`
|
|
15
16
|
: version
|
|
16
|
-
?
|
|
17
|
-
:
|
|
17
|
+
? `${base}/docs/${version}`
|
|
18
|
+
: `${base}/`
|
|
18
19
|
);
|
|
19
20
|
</script>
|
|
20
21
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { ChevronDown, Check } from 'lucide-svelte';
|
|
3
3
|
import { goto } from '$app/navigation';
|
|
4
|
+
import { base } from '$app/paths';
|
|
4
5
|
import { browser } from '$app/environment';
|
|
5
6
|
import Icon from './Icon.svelte';
|
|
6
7
|
|
|
@@ -107,9 +108,9 @@
|
|
|
107
108
|
|
|
108
109
|
const version = product.activeVersion || 'v1.0.0';
|
|
109
110
|
if (product.isDefault) {
|
|
110
|
-
goto(
|
|
111
|
+
goto(`${base}/docs/${version}`);
|
|
111
112
|
} else {
|
|
112
|
-
goto(
|
|
113
|
+
goto(`${base}/docs/${product.slug}/${version}`);
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { page } from '$app/stores';
|
|
3
|
+
import { base } from '$app/paths';
|
|
3
4
|
import { ChevronRight, ChevronDown, Lock } from 'lucide-svelte';
|
|
4
5
|
import type { SpecraConfig } from '../../config.types.js';
|
|
5
6
|
import Icon from './Icon.svelte';
|
|
@@ -50,11 +51,11 @@
|
|
|
50
51
|
|
|
51
52
|
let { docs = [], version, product, onLinkClick, config, activeTabGroup }: Props = $props();
|
|
52
53
|
|
|
53
|
-
/** URL prefix: /docs/{product}/{version} for named products, /docs/{version} for default */
|
|
54
|
+
/** URL prefix: {base}/docs/{product}/{version} for named products, {base}/docs/{version} for default */
|
|
54
55
|
let docsBase = $derived(
|
|
55
56
|
product && product !== '_default_'
|
|
56
|
-
?
|
|
57
|
-
:
|
|
57
|
+
? `${base}/docs/${product}/${version}`
|
|
58
|
+
: `${base}/docs/${version}`
|
|
58
59
|
);
|
|
59
60
|
|
|
60
61
|
const STORAGE_KEY = 'specra-sidebar-collapsed';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { ChevronDown } from 'lucide-svelte';
|
|
3
3
|
import { goto } from '$app/navigation';
|
|
4
|
+
import { base } from '$app/paths';
|
|
4
5
|
import type { TabGroup, SpecraConfig } from '../../config.types.js';
|
|
5
6
|
import Icon from './Icon.svelte';
|
|
6
7
|
|
|
@@ -32,8 +33,8 @@
|
|
|
32
33
|
|
|
33
34
|
let docsBase = $derived(
|
|
34
35
|
product && product !== '_default_' && version
|
|
35
|
-
?
|
|
36
|
-
: version ?
|
|
36
|
+
? `${base}/docs/${product}/${version}`
|
|
37
|
+
: version ? `${base}/docs/${version}` : `${base}/docs`
|
|
37
38
|
);
|
|
38
39
|
|
|
39
40
|
let dropdownOpen = $state(false);
|
package/dist/mdx.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import matter from "gray-matter";
|
|
4
4
|
import yaml from "js-yaml";
|
|
5
|
+
import { rehypeBasePath } from "./rehype-base-path.js";
|
|
5
6
|
import { unified } from "unified";
|
|
6
7
|
import remarkParse from "remark-parse";
|
|
7
8
|
import remarkGfm from "remark-gfm";
|
|
@@ -395,15 +396,41 @@ function parseJsxExpression(expr) {
|
|
|
395
396
|
/**
|
|
396
397
|
* Process markdown content to HTML using remark/rehype pipeline.
|
|
397
398
|
*/
|
|
399
|
+
function normalizeBasePath(bp) {
|
|
400
|
+
if (!bp)
|
|
401
|
+
return '';
|
|
402
|
+
let normalized = bp.startsWith('/') ? bp : `/${bp}`;
|
|
403
|
+
return normalized.replace(/\/+$/, '');
|
|
404
|
+
}
|
|
405
|
+
function resolveDeploymentBasePath() {
|
|
406
|
+
if (process.env.BASE_PATH)
|
|
407
|
+
return normalizeBasePath(process.env.BASE_PATH);
|
|
408
|
+
try {
|
|
409
|
+
const configPath = path.join(process.cwd(), 'specra.config.json');
|
|
410
|
+
if (fs.existsSync(configPath)) {
|
|
411
|
+
const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
412
|
+
if (raw.deployment?.basePath && !raw.deployment?.customDomain) {
|
|
413
|
+
return normalizeBasePath(raw.deployment.basePath);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
catch { /* ignore */ }
|
|
418
|
+
return '';
|
|
419
|
+
}
|
|
398
420
|
async function processMarkdownToHtml(markdown) {
|
|
399
|
-
const
|
|
421
|
+
const basePath = resolveDeploymentBasePath();
|
|
422
|
+
const processor = unified()
|
|
400
423
|
.use(remarkParse)
|
|
401
424
|
.use(remarkGfm)
|
|
402
425
|
.use(remarkMath)
|
|
403
426
|
.use(remarkRehype, { allowDangerousHtml: true })
|
|
404
427
|
.use(rehypeRaw)
|
|
405
428
|
.use(rehypeSlug)
|
|
406
|
-
.use(rehypeKatex)
|
|
429
|
+
.use(rehypeKatex);
|
|
430
|
+
if (basePath) {
|
|
431
|
+
processor.use(rehypeBasePath, { basePath });
|
|
432
|
+
}
|
|
433
|
+
const result = await processor
|
|
407
434
|
.use(rehypeStringify)
|
|
408
435
|
.process(markdown);
|
|
409
436
|
return String(result);
|
|
@@ -1046,6 +1073,7 @@ async function processMarkdownToMdxNodes(markdown) {
|
|
|
1046
1073
|
const dedented = dedentComponentChildren(preprocessed);
|
|
1047
1074
|
// Ensure component block integrity in the markdown
|
|
1048
1075
|
const normalized = ensureComponentBlockIntegrity(dedented);
|
|
1076
|
+
const basePath = resolveDeploymentBasePath();
|
|
1049
1077
|
const processor = unified()
|
|
1050
1078
|
.use(remarkParse)
|
|
1051
1079
|
.use(remarkGfm)
|
|
@@ -1054,6 +1082,9 @@ async function processMarkdownToMdxNodes(markdown) {
|
|
|
1054
1082
|
.use(rehypeRaw)
|
|
1055
1083
|
.use(rehypeSlug)
|
|
1056
1084
|
.use(rehypeKatex);
|
|
1085
|
+
if (basePath) {
|
|
1086
|
+
processor.use(rehypeBasePath, { basePath });
|
|
1087
|
+
}
|
|
1057
1088
|
const mdast = processor.parse(normalized);
|
|
1058
1089
|
const hast = await processor.run(mdast);
|
|
1059
1090
|
// The hast root has children - process them into MdxNodes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rehype plugin that prefixes internal absolute links with a base path.
|
|
3
|
+
* Internal links start with "/" and don't start with "http" or "//".
|
|
4
|
+
*
|
|
5
|
+
* Used for GitHub Pages deployments where the site lives under a subpath.
|
|
6
|
+
*/
|
|
7
|
+
import { visit } from 'unist-util-visit';
|
|
8
|
+
export function rehypeBasePath(options = {}) {
|
|
9
|
+
const { basePath = '' } = options;
|
|
10
|
+
if (!basePath)
|
|
11
|
+
return () => { };
|
|
12
|
+
const cleanBase = basePath.replace(/\/$/, '');
|
|
13
|
+
return (tree) => {
|
|
14
|
+
visit(tree, 'element', (node) => {
|
|
15
|
+
if (node.tagName === 'a' && node.properties?.href) {
|
|
16
|
+
const href = node.properties.href;
|
|
17
|
+
if (typeof href === 'string' && href.startsWith('/') && !href.startsWith('//') && !href.startsWith(cleanBase + '/')) {
|
|
18
|
+
node.properties.href = cleanBase + href;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (node.tagName === 'img' && node.properties?.src) {
|
|
22
|
+
const src = node.properties.src;
|
|
23
|
+
if (typeof src === 'string' && src.startsWith('/') && !src.startsWith('//') && !src.startsWith(cleanBase + '/')) {
|
|
24
|
+
node.properties.src = cleanBase + src;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specra",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"description": "A modern documentation library for SvelteKit with built-in versioning, API reference generation, full-text search, and MDX support",
|
|
5
5
|
"svelte": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|