specra 0.2.11 → 0.2.13

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.
@@ -44,11 +44,35 @@ export function specraMdsvexConfig(options = {}) {
44
44
  }
45
45
  }
46
46
 
47
+ /**
48
+ * Read the deployment basePath from specra.config.json if present.
49
+ * Falls back to the BASE_PATH environment variable, then empty string.
50
+ */
51
+ function resolveBasePath(configPath = path.join(process.cwd(), 'specra.config.json')) {
52
+ // Environment variable takes priority
53
+ if (process.env.BASE_PATH) return process.env.BASE_PATH
54
+
55
+ try {
56
+ if (fs.existsSync(configPath)) {
57
+ const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'))
58
+ if (raw.deployment?.basePath) {
59
+ const bp = raw.deployment.basePath
60
+ // If custom domain is set, ignore basePath
61
+ if (raw.deployment?.customDomain) return ''
62
+ return bp.startsWith('/') ? bp : `/${bp}`
63
+ }
64
+ }
65
+ } catch {
66
+ // Ignore parse errors
67
+ }
68
+ return ''
69
+ }
70
+
47
71
  /**
48
72
  * Scan the docs/ directory and return prerender entries for all version root pages.
49
73
  * This ensures adapter-static discovers and prerenders every version, not just the active one.
50
74
  */
51
- function discoverVersionEntries(docsDir = path.join(process.cwd(), 'docs')) {
75
+ function discoverVersionEntries(basePath = '', docsDir = path.join(process.cwd(), 'docs')) {
52
76
  const entries = ['/']
53
77
  try {
54
78
  if (!fs.existsSync(docsDir)) return entries
@@ -56,7 +80,7 @@ function discoverVersionEntries(docsDir = path.join(process.cwd(), 'docs')) {
56
80
  const items = fs.readdirSync(docsDir, { withFileTypes: true })
57
81
  for (const item of items) {
58
82
  if (item.isDirectory() && /^v\d/.test(item.name)) {
59
- entries.push(`/docs/${item.name}`)
83
+ entries.push(`${basePath}/docs/${item.name}`)
60
84
  }
61
85
  }
62
86
  } catch {
@@ -66,11 +90,14 @@ function discoverVersionEntries(docsDir = path.join(process.cwd(), 'docs')) {
66
90
  }
67
91
 
68
92
  /**
69
- * Create a full SvelteKit config with Specra defaults
93
+ * Create a full SvelteKit config with Specra defaults.
94
+ * Automatically reads deployment.basePath from specra.config.json
95
+ * for GitHub Pages deployments.
70
96
  */
71
97
  export function specraConfig(options = {}) {
72
98
  const { vitePreprocess } = options.vitePreprocess || {}
73
99
  const userPrerender = options.kit?.prerender || {}
100
+ const basePath = options.kit?.paths?.base ?? resolveBasePath()
74
101
 
75
102
  return {
76
103
  extensions: ['.svelte', '.md', '.svx', '.mdx'],
@@ -80,11 +107,15 @@ export function specraConfig(options = {}) {
80
107
  ],
81
108
  kit: {
82
109
  ...options.kit,
110
+ paths: {
111
+ ...options.kit?.paths,
112
+ base: basePath,
113
+ },
83
114
  prerender: {
84
115
  handleHttpError: 'warn',
85
116
  handleMissingId: 'warn',
86
117
  handleUnseenRoutes: 'warn',
87
- entries: discoverVersionEntries(),
118
+ entries: discoverVersionEntries(basePath),
88
119
  ...userPrerender,
89
120
  }
90
121
  }
@@ -21,7 +21,8 @@
21
21
  hidden?: boolean;
22
22
  }
23
23
 
24
- interface ProductItem {
24
+ /** Flat shape (from page components) */
25
+ interface ProductItemFlat {
25
26
  slug: string;
26
27
  label: string;
27
28
  icon?: string;
@@ -30,13 +31,27 @@
30
31
  isDefault: boolean;
31
32
  }
32
33
 
34
+ /** Nested shape (from SDK getProducts()) */
35
+ interface ProductItemNested {
36
+ slug: string;
37
+ config: {
38
+ label: string;
39
+ icon?: string;
40
+ badge?: string;
41
+ activeVersion?: string;
42
+ };
43
+ isDefault: boolean;
44
+ }
45
+
46
+ type ProductInput = ProductItemFlat | ProductItemNested;
47
+
33
48
  interface Props {
34
49
  currentVersion: string;
35
50
  versions: string[];
36
51
  versionsMeta?: VersionMeta[];
37
52
  versionBanner?: BannerConfig;
38
53
  config?: SpecraConfig;
39
- products?: ProductItem[];
54
+ products?: ProductInput[];
40
55
  currentProduct?: string;
41
56
  subheader?: Snippet;
42
57
  }
@@ -7,7 +7,8 @@ interface VersionMeta {
7
7
  badge?: string;
8
8
  hidden?: boolean;
9
9
  }
10
- interface ProductItem {
10
+ /** Flat shape (from page components) */
11
+ interface ProductItemFlat {
11
12
  slug: string;
12
13
  label: string;
13
14
  icon?: string;
@@ -15,13 +16,25 @@ interface ProductItem {
15
16
  activeVersion?: string;
16
17
  isDefault: boolean;
17
18
  }
19
+ /** Nested shape (from SDK getProducts()) */
20
+ interface ProductItemNested {
21
+ slug: string;
22
+ config: {
23
+ label: string;
24
+ icon?: string;
25
+ badge?: string;
26
+ activeVersion?: string;
27
+ };
28
+ isDefault: boolean;
29
+ }
30
+ type ProductInput = ProductItemFlat | ProductItemNested;
18
31
  interface Props {
19
32
  currentVersion: string;
20
33
  versions: string[];
21
34
  versionsMeta?: VersionMeta[];
22
35
  versionBanner?: BannerConfig;
23
36
  config?: SpecraConfig;
24
- products?: ProductItem[];
37
+ products?: ProductInput[];
25
38
  currentProduct?: string;
26
39
  subheader?: Snippet;
27
40
  }
package/dist/mdx.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
3
  import matter from "gray-matter";
4
+ import yaml from "js-yaml";
4
5
  import { unified } from "unified";
5
6
  import remarkParse from "remark-parse";
6
7
  import remarkGfm from "remark-gfm";
@@ -1135,7 +1136,14 @@ function readDocFromFile(filePath, originalSlug) {
1135
1136
  return null;
1136
1137
  }
1137
1138
  const fileContents = fs.readFileSync(filePath, "utf8");
1138
- const { data, content } = matter(fileContents);
1139
+ const { data, content } = matter(fileContents, {
1140
+ engines: {
1141
+ yaml: {
1142
+ parse: (str) => yaml.load(str),
1143
+ stringify: (obj) => yaml.dump(obj),
1144
+ },
1145
+ },
1146
+ });
1139
1147
  // Security: Validate MDX content for dangerous patterns
1140
1148
  const securityCheck = validateMDXSecurity(content, {
1141
1149
  strictMode: process.env.NODE_ENV === 'production',
package/dist/utils.d.ts CHANGED
@@ -1,13 +1,11 @@
1
1
  import { type ClassValue } from 'clsx';
2
2
  export declare function cn(...inputs: ClassValue[]): string;
3
3
  /**
4
- * Get the correct asset path based on deployment configuration
5
- * Handles different deployment scenarios:
6
- * - Vercel/Node.js hosting (standalone build): No basePath needed
7
- * - GitHub Pages without custom domain: Uses basePath from config
8
- * - Static hosting with custom domain: No basePath needed
4
+ * Get the correct asset path based on deployment configuration.
5
+ * Uses SvelteKit's base path (from kit.paths.base) which is resolved
6
+ * from deployment.basePath in specra.config.json or the BASE_PATH env var.
9
7
  *
10
- * @param path - The asset path (can start with or without '/')
11
- * @returns The properly formatted asset path
8
+ * @param assetPath - The asset path (can start with or without '/')
9
+ * @returns The properly formatted asset path with base prefix
12
10
  */
13
- export declare function getAssetPath(path: string): string;
11
+ export declare function getAssetPath(assetPath: string): string;
package/dist/utils.js CHANGED
@@ -4,27 +4,20 @@ export function cn(...inputs) {
4
4
  return twMerge(clsx(inputs));
5
5
  }
6
6
  /**
7
- * Get the correct asset path based on deployment configuration
8
- * Handles different deployment scenarios:
9
- * - Vercel/Node.js hosting (standalone build): No basePath needed
10
- * - GitHub Pages without custom domain: Uses basePath from config
11
- * - Static hosting with custom domain: No basePath needed
7
+ * Get the correct asset path based on deployment configuration.
8
+ * Uses SvelteKit's base path (from kit.paths.base) which is resolved
9
+ * from deployment.basePath in specra.config.json or the BASE_PATH env var.
12
10
  *
13
- * @param path - The asset path (can start with or without '/')
14
- * @returns The properly formatted asset path
11
+ * @param assetPath - The asset path (can start with or without '/')
12
+ * @returns The properly formatted asset path with base prefix
15
13
  */
16
- export function getAssetPath(path) {
17
- // Get basePath from Next.js config (set during build for static exports)
18
- const basePath = process.env.NEXT_PUBLIC_BASE_PATH || process.env.__NEXT_ROUTER_BASEPATH || '';
19
- // Normalize the input path: ensure it starts with '/'
20
- const normalizedPath = path.startsWith('/') ? path : `/${path}`;
21
- // If we have a basePath (GitHub Pages without custom domain), prepend it
14
+ export function getAssetPath(assetPath) {
15
+ const basePath = process.env.BASE_PATH || '';
16
+ const normalizedPath = assetPath.startsWith('/') ? assetPath : `/${assetPath}`;
22
17
  if (basePath) {
23
- // Normalize basePath: remove trailing slash, ensure leading slash
24
18
  const normalizedBase = basePath.startsWith('/') ? basePath : `/${basePath}`;
25
19
  const cleanBase = normalizedBase.replace(/\/$/, '');
26
20
  return `${cleanBase}${normalizedPath}`;
27
21
  }
28
- // Default: return the normalized path (works for Vercel, custom domains, and dev)
29
22
  return normalizedPath;
30
23
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specra",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
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",
@@ -78,13 +78,14 @@
78
78
  "date-fns": "^4.1.0",
79
79
  "embla-carousel-svelte": "^8.5.1",
80
80
  "gray-matter": "^4.0.3",
81
+ "hast-util-to-html": "^9.0.0",
82
+ "js-yaml": "^4.1.1",
81
83
  "katex": "^0.16.27",
82
84
  "lucide-svelte": "^0.454.0",
83
85
  "mdsvex": "^0.12.0",
84
86
  "meilisearch": "^0.54.0",
85
87
  "mermaid": "^11.12.2",
86
88
  "mode-watcher": "^0.5.0",
87
- "hast-util-to-html": "^9.0.0",
88
89
  "rehype-katex": "^7.0.1",
89
90
  "rehype-raw": "^7.0.0",
90
91
  "rehype-slug": "^6.0.0",
@@ -103,6 +104,7 @@
103
104
  "@sveltejs/kit": "^2.0.0",
104
105
  "@sveltejs/package": "^2.0.0",
105
106
  "@sveltejs/vite-plugin-svelte": "^6.0.0",
107
+ "@types/js-yaml": "^4.0.9",
106
108
  "@types/node": "^22",
107
109
  "svelte": "^5.0.0",
108
110
  "svelte-check": "^4.0.0",