svedocs 0.1.0-beta.2

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.
Files changed (192) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +38 -0
  3. package/dist/ai/openai-compatible.d.ts +13 -0
  4. package/dist/ai/openai-compatible.d.ts.map +1 -0
  5. package/dist/ai/openai-compatible.js +92 -0
  6. package/dist/ai/providers.d.ts +19 -0
  7. package/dist/ai/providers.d.ts.map +1 -0
  8. package/dist/ai/providers.js +185 -0
  9. package/dist/ai/rate-limit.d.ts +18 -0
  10. package/dist/ai/rate-limit.d.ts.map +1 -0
  11. package/dist/ai/rate-limit.js +52 -0
  12. package/dist/ai/response.d.ts +4 -0
  13. package/dist/ai/response.d.ts.map +1 -0
  14. package/dist/ai/response.js +151 -0
  15. package/dist/ai/runtime.d.ts +30 -0
  16. package/dist/ai/runtime.d.ts.map +1 -0
  17. package/dist/ai/runtime.js +105 -0
  18. package/dist/ai/types.d.ts +93 -0
  19. package/dist/ai/types.d.ts.map +1 -0
  20. package/dist/ai/types.js +1 -0
  21. package/dist/ai.d.ts +7 -0
  22. package/dist/ai.d.ts.map +1 -0
  23. package/dist/ai.js +6 -0
  24. package/dist/ambient.d.ts +5 -0
  25. package/dist/cloudflare.d.ts +27 -0
  26. package/dist/cloudflare.d.ts.map +1 -0
  27. package/dist/cloudflare.js +146 -0
  28. package/dist/config.d.ts +311 -0
  29. package/dist/config.d.ts.map +1 -0
  30. package/dist/config.js +243 -0
  31. package/dist/core/checks.d.ts +4 -0
  32. package/dist/core/checks.d.ts.map +1 -0
  33. package/dist/core/checks.js +252 -0
  34. package/dist/core/config.d.ts +5 -0
  35. package/dist/core/config.d.ts.map +1 -0
  36. package/dist/core/config.js +182 -0
  37. package/dist/core/content.d.ts +7 -0
  38. package/dist/core/content.d.ts.map +1 -0
  39. package/dist/core/content.js +211 -0
  40. package/dist/core/links.d.ts +8 -0
  41. package/dist/core/links.d.ts.map +1 -0
  42. package/dist/core/links.js +94 -0
  43. package/dist/core/navigation.d.ts +5 -0
  44. package/dist/core/navigation.d.ts.map +1 -0
  45. package/dist/core/navigation.js +144 -0
  46. package/dist/core/search.d.ts +5 -0
  47. package/dist/core/search.d.ts.map +1 -0
  48. package/dist/core/search.js +41 -0
  49. package/dist/core/types.d.ts +269 -0
  50. package/dist/core/types.d.ts.map +1 -0
  51. package/dist/core/types.js +4 -0
  52. package/dist/core/utils.d.ts +12 -0
  53. package/dist/core/utils.d.ts.map +1 -0
  54. package/dist/core/utils.js +52 -0
  55. package/dist/core.d.ts +8 -0
  56. package/dist/core.d.ts.map +1 -0
  57. package/dist/core.js +7 -0
  58. package/dist/index.d.ts +6 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +3 -0
  61. package/dist/mdx/ast.d.ts +32 -0
  62. package/dist/mdx/ast.d.ts.map +1 -0
  63. package/dist/mdx/ast.js +148 -0
  64. package/dist/mdx/code.d.ts +14 -0
  65. package/dist/mdx/code.d.ts.map +1 -0
  66. package/dist/mdx/code.js +315 -0
  67. package/dist/mdx/compile.d.ts +26 -0
  68. package/dist/mdx/compile.d.ts.map +1 -0
  69. package/dist/mdx/compile.js +63 -0
  70. package/dist/mdx/diff.d.ts +5 -0
  71. package/dist/mdx/diff.d.ts.map +1 -0
  72. package/dist/mdx/diff.js +160 -0
  73. package/dist/mdx/utils.d.ts +10 -0
  74. package/dist/mdx/utils.d.ts.map +1 -0
  75. package/dist/mdx/utils.js +64 -0
  76. package/dist/og/image.d.ts +21 -0
  77. package/dist/og/image.d.ts.map +1 -0
  78. package/dist/og/image.js +216 -0
  79. package/dist/og/metadata.d.ts +7 -0
  80. package/dist/og/metadata.d.ts.map +1 -0
  81. package/dist/og/metadata.js +160 -0
  82. package/dist/og/types.d.ts +59 -0
  83. package/dist/og/types.d.ts.map +1 -0
  84. package/dist/og/types.js +1 -0
  85. package/dist/og.d.ts +4 -0
  86. package/dist/og.d.ts.map +1 -0
  87. package/dist/og.js +3 -0
  88. package/dist/search/algolia.d.ts +10 -0
  89. package/dist/search/algolia.d.ts.map +1 -0
  90. package/dist/search/algolia.js +82 -0
  91. package/dist/search/cloudflare-sync.d.ts +5 -0
  92. package/dist/search/cloudflare-sync.d.ts.map +1 -0
  93. package/dist/search/cloudflare-sync.js +169 -0
  94. package/dist/search/cloudflare.d.ts +9 -0
  95. package/dist/search/cloudflare.d.ts.map +1 -0
  96. package/dist/search/cloudflare.js +115 -0
  97. package/dist/search/local.d.ts +9 -0
  98. package/dist/search/local.d.ts.map +1 -0
  99. package/dist/search/local.js +122 -0
  100. package/dist/search/runtime.d.ts +41 -0
  101. package/dist/search/runtime.d.ts.map +1 -0
  102. package/dist/search/runtime.js +151 -0
  103. package/dist/search/types.d.ts +172 -0
  104. package/dist/search/types.d.ts.map +1 -0
  105. package/dist/search/types.js +1 -0
  106. package/dist/search/typesense.d.ts +11 -0
  107. package/dist/search/typesense.d.ts.map +1 -0
  108. package/dist/search/typesense.js +91 -0
  109. package/dist/search/utils.d.ts +10 -0
  110. package/dist/search/utils.d.ts.map +1 -0
  111. package/dist/search/utils.js +55 -0
  112. package/dist/search.d.ts +8 -0
  113. package/dist/search.d.ts.map +1 -0
  114. package/dist/search.js +7 -0
  115. package/dist/svelte.d.ts +24 -0
  116. package/dist/svelte.d.ts.map +1 -0
  117. package/dist/svelte.js +51 -0
  118. package/dist/testing.d.ts +3 -0
  119. package/dist/testing.d.ts.map +1 -0
  120. package/dist/testing.js +23 -0
  121. package/dist/theme/AskAiPanel.svelte +433 -0
  122. package/dist/theme/AskAiPanel.svelte.d.ts +27 -0
  123. package/dist/theme/AskAiPanel.svelte.d.ts.map +1 -0
  124. package/dist/theme/Button.svelte +34 -0
  125. package/dist/theme/Button.svelte.d.ts +41 -0
  126. package/dist/theme/Button.svelte.d.ts.map +1 -0
  127. package/dist/theme/Checkbox.svelte +63 -0
  128. package/dist/theme/Checkbox.svelte.d.ts +45 -0
  129. package/dist/theme/Checkbox.svelte.d.ts.map +1 -0
  130. package/dist/theme/CodeBlock.svelte +6 -0
  131. package/dist/theme/CodeBlock.svelte.d.ts +22 -0
  132. package/dist/theme/CodeBlock.svelte.d.ts.map +1 -0
  133. package/dist/theme/DocPage.svelte +121 -0
  134. package/dist/theme/DocPage.svelte.d.ts +33 -0
  135. package/dist/theme/DocPage.svelte.d.ts.map +1 -0
  136. package/dist/theme/DocsApp.svelte +78 -0
  137. package/dist/theme/DocsApp.svelte.d.ts +63 -0
  138. package/dist/theme/DocsApp.svelte.d.ts.map +1 -0
  139. package/dist/theme/DocsLayout.svelte +167 -0
  140. package/dist/theme/DocsLayout.svelte.d.ts +40 -0
  141. package/dist/theme/DocsLayout.svelte.d.ts.map +1 -0
  142. package/dist/theme/FloatingToolbar.svelte +73 -0
  143. package/dist/theme/FloatingToolbar.svelte.d.ts +22 -0
  144. package/dist/theme/FloatingToolbar.svelte.d.ts.map +1 -0
  145. package/dist/theme/FormField.svelte +41 -0
  146. package/dist/theme/FormField.svelte.d.ts +35 -0
  147. package/dist/theme/FormField.svelte.d.ts.map +1 -0
  148. package/dist/theme/HomePage.svelte +228 -0
  149. package/dist/theme/HomePage.svelte.d.ts +58 -0
  150. package/dist/theme/HomePage.svelte.d.ts.map +1 -0
  151. package/dist/theme/Input.svelte +36 -0
  152. package/dist/theme/Input.svelte.d.ts +34 -0
  153. package/dist/theme/Input.svelte.d.ts.map +1 -0
  154. package/dist/theme/PageLayout.svelte +36 -0
  155. package/dist/theme/PageLayout.svelte.d.ts +31 -0
  156. package/dist/theme/PageLayout.svelte.d.ts.map +1 -0
  157. package/dist/theme/RootLayout.svelte +330 -0
  158. package/dist/theme/RootLayout.svelte.d.ts +40 -0
  159. package/dist/theme/RootLayout.svelte.d.ts.map +1 -0
  160. package/dist/theme/ScopeSwitcher.svelte +103 -0
  161. package/dist/theme/ScopeSwitcher.svelte.d.ts +24 -0
  162. package/dist/theme/ScopeSwitcher.svelte.d.ts.map +1 -0
  163. package/dist/theme/SearchDialog.svelte +253 -0
  164. package/dist/theme/SearchDialog.svelte.d.ts +28 -0
  165. package/dist/theme/SearchDialog.svelte.d.ts.map +1 -0
  166. package/dist/theme/Select.svelte +37 -0
  167. package/dist/theme/Select.svelte.d.ts +42 -0
  168. package/dist/theme/Select.svelte.d.ts.map +1 -0
  169. package/dist/theme/SidebarTree.svelte +136 -0
  170. package/dist/theme/SidebarTree.svelte.d.ts +24 -0
  171. package/dist/theme/SidebarTree.svelte.d.ts.map +1 -0
  172. package/dist/theme/Textarea.svelte +33 -0
  173. package/dist/theme/Textarea.svelte.d.ts +33 -0
  174. package/dist/theme/Textarea.svelte.d.ts.map +1 -0
  175. package/dist/theme/ThemeToggle.svelte +39 -0
  176. package/dist/theme/ThemeToggle.svelte.d.ts +21 -0
  177. package/dist/theme/ThemeToggle.svelte.d.ts.map +1 -0
  178. package/dist/theme/forms.d.ts +4 -0
  179. package/dist/theme/forms.d.ts.map +1 -0
  180. package/dist/theme/forms.js +3 -0
  181. package/dist/theme/index.d.ts +21 -0
  182. package/dist/theme/index.d.ts.map +1 -0
  183. package/dist/theme/index.js +19 -0
  184. package/dist/theme/portal.d.ts +5 -0
  185. package/dist/theme/portal.d.ts.map +1 -0
  186. package/dist/theme/portal.js +19 -0
  187. package/dist/theme/styles.css +3697 -0
  188. package/dist/virtual.d.ts +59 -0
  189. package/dist/vite.d.ts +10 -0
  190. package/dist/vite.d.ts.map +1 -0
  191. package/dist/vite.js +304 -0
  192. package/package.json +118 -0
@@ -0,0 +1,252 @@
1
+ import { access, readFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { checkLink, createLinkCheckContext } from './links.js';
4
+ import { findDuplicates, normalizePath } from './utils.js';
5
+ export async function checkSvedocsContent(manifest, projectRoot = process.cwd()) {
6
+ const issues = [];
7
+ const routeDuplicates = findDuplicates(manifest.pages.map((page) => page.routePath));
8
+ const canonicalDuplicates = findDuplicates(manifest.pages
9
+ .map((page) => page.seo.canonical)
10
+ .filter((canonical) => Boolean(canonical)));
11
+ if (manifest.config.build.mode === 'spa') {
12
+ issues.push({
13
+ code: 'spa-risk',
14
+ severity: 'warning',
15
+ message: 'SPA mode prerenders known pages, but server-only routes and provider bindings are unavailable on static hosts.'
16
+ });
17
+ }
18
+ for (const route of routeDuplicates) {
19
+ issues.push({
20
+ code: 'duplicate-route',
21
+ severity: 'error',
22
+ message: `Duplicate route path detected: ${route}`
23
+ });
24
+ }
25
+ for (const canonical of canonicalDuplicates) {
26
+ issues.push({
27
+ code: 'duplicate-canonical',
28
+ severity: 'error',
29
+ message: `Duplicate canonical URL detected: ${canonical}`
30
+ });
31
+ }
32
+ if (manifest.config.checks.translations) {
33
+ issues.push(...createMissingTranslationIssues(manifest));
34
+ }
35
+ const linkContext = createLinkCheckContext(manifest);
36
+ for (const page of manifest.pages) {
37
+ if (!page.description) {
38
+ issues.push({
39
+ code: 'missing-description',
40
+ severity: 'warning',
41
+ message: `${page.routePath} is missing a description for SEO and search excerpts.`,
42
+ pageId: page.id,
43
+ sourcePath: page.sourcePath
44
+ });
45
+ }
46
+ if (page.plainText && page.search.length === 0) {
47
+ issues.push({
48
+ code: 'empty-search',
49
+ severity: 'warning',
50
+ message: `${page.routePath} has content but produced no search records.`,
51
+ pageId: page.id,
52
+ sourcePath: page.sourcePath
53
+ });
54
+ }
55
+ for (const link of page.links) {
56
+ const issue = checkLink(page, link, linkContext)
57
+ ?? await checkAssetLink(projectRoot, page.sourcePath, link, manifest.config.checks.assets)
58
+ ?? await checkExternalLink(link, manifest.config.checks.externalLinks, page.id, page.sourcePath);
59
+ if (issue)
60
+ issues.push(issue);
61
+ }
62
+ }
63
+ return issues;
64
+ }
65
+ function createMissingTranslationIssues(manifest) {
66
+ const locales = manifest.config.i18n.locales.map((locale) => locale.code);
67
+ if (locales.length <= 1)
68
+ return [];
69
+ const groups = new Map();
70
+ for (const page of manifest.pages) {
71
+ if (page.hidden || page.kind !== 'doc')
72
+ continue;
73
+ const key = page.scopePath;
74
+ const group = groups.get(key) ?? [];
75
+ group.push(page);
76
+ groups.set(key, group);
77
+ }
78
+ const issues = [];
79
+ for (const pages of groups.values()) {
80
+ const present = new Set(pages.map((page) => page.locale).filter((locale) => Boolean(locale)));
81
+ const reference = pages[0];
82
+ if (!reference)
83
+ continue;
84
+ for (const locale of locales) {
85
+ if (present.has(locale))
86
+ continue;
87
+ issues.push({
88
+ code: 'missing-translation',
89
+ severity: 'warning',
90
+ message: `${reference.scopePath} is missing locale ${locale}.`,
91
+ pageId: reference.id,
92
+ sourcePath: reference.sourcePath
93
+ });
94
+ }
95
+ }
96
+ return issues;
97
+ }
98
+ export async function checkPackagePublication(packageRoot) {
99
+ const issues = [];
100
+ const packageJsonPath = path.join(packageRoot, 'package.json');
101
+ const pkg = JSON.parse(await readFile(packageJsonPath, 'utf8'));
102
+ if (!pkg.private && pkg.license !== 'MIT') {
103
+ issues.push({
104
+ code: 'package-license-missing',
105
+ severity: 'error',
106
+ message: 'package.json license must be MIT for public svedocs packages.'
107
+ });
108
+ }
109
+ if (!pkg.private && pkg.publishConfig?.access !== 'public') {
110
+ issues.push({
111
+ code: 'package-publish-access-missing',
112
+ severity: 'warning',
113
+ message: 'package.json publishConfig.access should be public before publishing.'
114
+ });
115
+ }
116
+ if (!pkg.private && pkg.publishConfig?.provenance !== true) {
117
+ issues.push({
118
+ code: 'package-provenance-missing',
119
+ severity: 'warning',
120
+ message: 'package.json publishConfig.provenance should be true for npm provenance.'
121
+ });
122
+ }
123
+ for (const target of extractBinTargets(pkg.bin)) {
124
+ if (!await fileExists(path.join(packageRoot, target))) {
125
+ issues.push({
126
+ code: 'package-bin-missing',
127
+ severity: 'error',
128
+ message: `package.json bin target is missing: ${target}.`
129
+ });
130
+ }
131
+ }
132
+ for (const [exportName, value] of Object.entries(pkg.exports ?? {})) {
133
+ const targets = extractExportTargets(value);
134
+ for (const target of targets) {
135
+ if (!await fileExists(path.join(packageRoot, target))) {
136
+ issues.push({
137
+ code: 'package-export-missing',
138
+ severity: 'error',
139
+ message: `${exportName} points to missing package export target ${target}.`
140
+ });
141
+ }
142
+ }
143
+ }
144
+ for (const file of pkg.files ?? []) {
145
+ if (!await fileExists(path.join(packageRoot, file))) {
146
+ issues.push({
147
+ code: 'package-file-missing',
148
+ severity: 'error',
149
+ message: `package.json files entry is missing: ${file}.`
150
+ });
151
+ }
152
+ }
153
+ return issues;
154
+ }
155
+ function extractBinTargets(bin) {
156
+ if (!bin)
157
+ return [];
158
+ if (typeof bin === 'string')
159
+ return [normalizePackageTarget(bin)];
160
+ return Object.values(bin).map(normalizePackageTarget);
161
+ }
162
+ async function checkAssetLink(projectRoot, sourcePath, link, enabled) {
163
+ if (!enabled || link.kind !== 'asset')
164
+ return undefined;
165
+ const pathname = splitHrefPath(link.href);
166
+ const candidates = pathname.startsWith('/')
167
+ ? [
168
+ path.join(projectRoot, 'static', pathname),
169
+ path.join(projectRoot, pathname)
170
+ ]
171
+ : [
172
+ path.join(projectRoot, path.posix.dirname(normalizePath(sourcePath)), pathname),
173
+ path.join(projectRoot, 'static', pathname)
174
+ ];
175
+ if (await anyFileExists(candidates))
176
+ return undefined;
177
+ return {
178
+ code: 'broken-asset',
179
+ severity: 'error',
180
+ message: `${sourcePath}:${link.line} references missing asset ${link.href}.`,
181
+ pageId: sourcePath,
182
+ sourcePath,
183
+ href: link.href
184
+ };
185
+ }
186
+ async function checkExternalLink(link, enabled, pageId, sourcePath) {
187
+ if (!enabled || link.kind !== 'external' || !/^https?:\/\//.test(link.href))
188
+ return undefined;
189
+ const controller = new AbortController();
190
+ const timeout = setTimeout(() => controller.abort(), 4_000);
191
+ try {
192
+ const response = await fetch(link.href, {
193
+ method: 'HEAD',
194
+ redirect: 'follow',
195
+ signal: controller.signal
196
+ });
197
+ if (response.ok || response.status === 405)
198
+ return undefined;
199
+ return {
200
+ code: 'external-link-unchecked',
201
+ severity: 'warning',
202
+ message: `${sourcePath}:${link.line} external link returned ${response.status}: ${link.href}.`,
203
+ pageId,
204
+ sourcePath,
205
+ href: link.href
206
+ };
207
+ }
208
+ catch {
209
+ return {
210
+ code: 'external-link-unchecked',
211
+ severity: 'warning',
212
+ message: `${sourcePath}:${link.line} external link could not be verified: ${link.href}.`,
213
+ pageId,
214
+ sourcePath,
215
+ href: link.href
216
+ };
217
+ }
218
+ finally {
219
+ clearTimeout(timeout);
220
+ }
221
+ }
222
+ function extractExportTargets(value) {
223
+ if (typeof value === 'string')
224
+ return [normalizePackageTarget(value)];
225
+ if (!value || typeof value !== 'object')
226
+ return [];
227
+ return Object.values(value).flatMap(extractExportTargets);
228
+ }
229
+ function normalizePackageTarget(target) {
230
+ return target.replace(/^\.\//, '');
231
+ }
232
+ function splitHrefPath(href) {
233
+ const [withoutHash = ''] = href.trim().split('#');
234
+ const [pathname = ''] = withoutHash.split('?');
235
+ return pathname;
236
+ }
237
+ async function anyFileExists(paths) {
238
+ for (const candidate of paths) {
239
+ if (await fileExists(candidate))
240
+ return true;
241
+ }
242
+ return false;
243
+ }
244
+ async function fileExists(filePath) {
245
+ try {
246
+ await access(filePath);
247
+ return true;
248
+ }
249
+ catch {
250
+ return false;
251
+ }
252
+ }
@@ -0,0 +1,5 @@
1
+ import type { SvedocsConfig } from '../config.js';
2
+ import type { SvedocsResolvedConfig } from './types.js';
3
+ export declare function resolveSvedocsConfig(config?: SvedocsConfig): SvedocsResolvedConfig;
4
+ export declare function isResolvedConfig(config: SvedocsConfig | SvedocsResolvedConfig | undefined): config is SvedocsResolvedConfig;
5
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAiB,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEvE,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,aAAkB,GAAG,qBAAqB,CAmItF;AAeD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,qBAAqB,GAAG,SAAS,GAAG,MAAM,IAAI,qBAAqB,CAY3H"}
@@ -0,0 +1,182 @@
1
+ export function resolveSvedocsConfig(config = {}) {
2
+ const contentRoot = config.content?.root ?? 'content';
3
+ const docsRoot = config.content?.docs ?? `${contentRoot}/docs`;
4
+ const pagesRoot = config.content?.pages ?? `${contentRoot}/pages`;
5
+ const search = config.search === false ? { enabled: false, provider: 'local', scope: 'current' } : {
6
+ enabled: config.search?.enabled ?? true,
7
+ provider: config.search?.provider ?? 'local',
8
+ scope: config.search?.scope ?? 'current'
9
+ };
10
+ const ai = config.ai === false ? {
11
+ enabled: false,
12
+ provider: 'mock',
13
+ scope: 'current',
14
+ label: 'Ask AI',
15
+ placeholder: 'Ask about the docs',
16
+ suggestions: [],
17
+ maxResults: 5
18
+ } : {
19
+ enabled: config.ai?.enabled ?? Boolean(config.ai?.provider),
20
+ provider: config.ai?.provider ?? 'mock',
21
+ scope: config.ai?.scope ?? 'current',
22
+ label: config.ai?.label ?? 'Ask AI',
23
+ placeholder: config.ai?.placeholder ?? 'Ask about the docs',
24
+ suggestions: config.ai?.suggestions ?? [],
25
+ maxResults: config.ai?.maxResults ?? 5,
26
+ ...(config.ai?.systemPrompt ? { systemPrompt: config.ai.systemPrompt } : {}),
27
+ ...(config.ai?.welcomeMessage ? { welcomeMessage: config.ai.welcomeMessage } : {})
28
+ };
29
+ return {
30
+ site: {
31
+ name: config.site?.name ?? 'svedocs',
32
+ title: config.site?.title ?? config.site?.name ?? 'svedocs',
33
+ description: config.site?.description ?? 'SvelteKit-native documentation framework.',
34
+ ...(config.site?.url ? { url: config.site.url } : {})
35
+ },
36
+ content: {
37
+ root: contentRoot,
38
+ docs: docsRoot,
39
+ pages: pagesRoot,
40
+ include: config.content?.include ?? [`${contentRoot}/**/*.{md,mdx,svx}`],
41
+ exclude: config.content?.exclude ?? [
42
+ '**/_*.{md,mdx,svx}',
43
+ '**/node_modules/**',
44
+ '**/dist/**',
45
+ '**/build/**',
46
+ '**/.svelte-kit/**'
47
+ ]
48
+ },
49
+ build: {
50
+ mode: config.build?.mode ?? 'edge'
51
+ },
52
+ theme: {
53
+ defaultMode: config.theme?.defaultMode ?? 'system',
54
+ palette: {
55
+ accent: config.theme?.palette?.accent ?? 'emerald',
56
+ neutral: config.theme?.palette?.neutral ?? 'zinc'
57
+ },
58
+ fonts: {
59
+ sans: config.theme?.fonts?.sans ?? '"IBM Plex Sans", "Avenir Next", sans-serif',
60
+ mono: config.theme?.fonts?.mono ?? '"JetBrains Mono", "SFMono-Regular", monospace',
61
+ display: config.theme?.fonts?.display ?? '"IBM Plex Sans", "Avenir Next", sans-serif'
62
+ },
63
+ radius: config.theme?.radius ?? '0px',
64
+ codeTheme: resolveCodeTheme(config.theme?.codeTheme),
65
+ code: {
66
+ lineNumbers: config.theme?.code?.lineNumbers ?? true,
67
+ wrap: config.theme?.code?.wrap ?? false
68
+ },
69
+ brand: {
70
+ label: config.theme?.brand?.label ?? config.site?.name ?? 'svedocs',
71
+ href: config.theme?.brand?.href ?? '/',
72
+ ...(config.theme?.brand?.logo ? { logo: config.theme.brand.logo } : {}),
73
+ mark: config.theme?.brand?.mark ?? 'pixel'
74
+ },
75
+ nav: config.theme?.nav ?? [
76
+ { label: 'Docs', href: '/docs' }
77
+ ],
78
+ social: config.theme?.social ?? [],
79
+ footer: config.theme?.footer === false
80
+ ? false
81
+ : {
82
+ text: config.theme?.footer?.text ?? `MIT licensed. Built with ${config.site?.name ?? 'svedocs'}.`,
83
+ links: config.theme?.footer?.links ?? []
84
+ },
85
+ home: {
86
+ kicker: config.theme?.home?.kicker ?? 'SvelteKit-native docs',
87
+ ...(config.theme?.home?.primaryAction ? { primaryAction: config.theme.home.primaryAction } : {}),
88
+ ...(config.theme?.home?.secondaryAction ? { secondaryAction: config.theme.home.secondaryAction } : {}),
89
+ visual: {
90
+ type: config.theme?.home?.visual?.type ?? 'pixel',
91
+ ...(config.theme?.home?.visual?.src ? { src: config.theme.home.visual.src } : {}),
92
+ alt: config.theme?.home?.visual?.alt ?? ''
93
+ }
94
+ }
95
+ },
96
+ search,
97
+ ai,
98
+ seo: {
99
+ sitemap: config.seo?.sitemap ?? true,
100
+ robots: config.seo?.robots ?? true,
101
+ ...(config.seo?.defaultAuthor ? { defaultAuthor: config.seo.defaultAuthor } : {}),
102
+ ogImage: config.seo?.ogImage === false
103
+ ? false
104
+ : {
105
+ template: config.seo?.ogImage?.template ?? 'default',
106
+ format: config.seo?.ogImage?.format ?? 'svg',
107
+ outDir: config.seo?.ogImage?.outDir ?? 'static/og',
108
+ renderer: config.seo?.ogImage?.renderer ?? 'svg'
109
+ }
110
+ },
111
+ source: {
112
+ ...(config.source?.editBaseUrl ? { editBaseUrl: config.source.editBaseUrl } : {})
113
+ },
114
+ cloudflare: {
115
+ compatibilityDate: config.cloudflare?.compatibilityDate ?? '2026-05-18',
116
+ aiSearch: {
117
+ binding: config.cloudflare?.aiSearch?.binding ?? 'SVEDOCS_AI_SEARCH',
118
+ instanceName: config.cloudflare?.aiSearch?.instanceName ?? 'svedocs',
119
+ ...(config.cloudflare?.aiSearch?.namespace ? { namespace: config.cloudflare.aiSearch.namespace } : {}),
120
+ remote: config.cloudflare?.aiSearch?.remote ?? false
121
+ }
122
+ },
123
+ checks: {
124
+ assets: config.checks?.assets ?? true,
125
+ externalLinks: config.checks?.externalLinks ?? false,
126
+ translations: config.checks?.translations ?? false,
127
+ },
128
+ i18n: resolveI18nConfig(config)
129
+ };
130
+ }
131
+ function resolveCodeTheme(input) {
132
+ if (typeof input === 'string') {
133
+ return {
134
+ light: input,
135
+ dark: input
136
+ };
137
+ }
138
+ return {
139
+ light: input?.light ?? 'github-light',
140
+ dark: input?.dark ?? 'github-dark'
141
+ };
142
+ }
143
+ export function isResolvedConfig(config) {
144
+ const candidate = config;
145
+ return Boolean(candidate
146
+ && Array.isArray(candidate.content?.include)
147
+ && Array.isArray(candidate.content?.exclude)
148
+ && typeof candidate.search?.enabled === 'boolean'
149
+ && typeof candidate.ai?.enabled === 'boolean'
150
+ && typeof candidate.theme?.fonts?.sans === 'string'
151
+ && typeof candidate.theme?.brand?.label === 'string'
152
+ && typeof candidate.cloudflare?.compatibilityDate === 'string');
153
+ }
154
+ function resolveI18nConfig(config) {
155
+ if (config.i18n === false) {
156
+ return {
157
+ locales: [],
158
+ prefixDefaultLocale: false
159
+ };
160
+ }
161
+ const locales = (config.i18n?.locales ?? []).map(normalizeLocale);
162
+ const defaultLocale = config.i18n?.defaultLocale ?? locales[0]?.code;
163
+ return {
164
+ ...(defaultLocale ? { defaultLocale } : {}),
165
+ locales,
166
+ prefixDefaultLocale: config.i18n?.prefixDefaultLocale ?? false
167
+ };
168
+ }
169
+ function normalizeLocale(locale) {
170
+ if (typeof locale === 'string') {
171
+ return {
172
+ code: locale,
173
+ label: locale,
174
+ path: locale
175
+ };
176
+ }
177
+ return {
178
+ code: locale.code,
179
+ label: locale.label ?? locale.code,
180
+ path: locale.path ?? locale.code
181
+ };
182
+ }
@@ -0,0 +1,7 @@
1
+ import type { SvedocsConfig } from '../config.js';
2
+ import type { SvedocsContentManifest, SvedocsResolvedConfig } from './types.js';
3
+ export declare function loadSvedocsContent(options?: {
4
+ projectRoot?: string;
5
+ config?: SvedocsConfig | SvedocsResolvedConfig;
6
+ }): Promise<SvedocsContentManifest>;
7
+ //# sourceMappingURL=content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/core/content.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAOlD,OAAO,KAAK,EAAE,sBAAsB,EAAe,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAW7F,wBAAsB,kBAAkB,CAAC,OAAO,GAAE;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,aAAa,GAAG,qBAAqB,CAAC;CAC3C,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA+BvC"}
@@ -0,0 +1,211 @@
1
+ import { readFile, stat } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import fg from 'fast-glob';
4
+ import matter from 'gray-matter';
5
+ import { compileMarkdown } from '../mdx/compile.js';
6
+ import { checkSvedocsContent } from './checks.js';
7
+ import { isResolvedConfig, resolveSvedocsConfig } from './config.js';
8
+ import { extractMarkdownLinks } from './links.js';
9
+ import { createPageTree, wirePrevNext } from './navigation.js';
10
+ import { createPageSearchRecords, createSearchRecords } from './search.js';
11
+ import { booleanFrontmatter, normalizePath, numberFrontmatter, stringArrayFrontmatter, stringFrontmatter, stripInlineMarkdown, titleFromSegment } from './utils.js';
12
+ export async function loadSvedocsContent(options = {}) {
13
+ const projectRoot = options.projectRoot ?? process.cwd();
14
+ const rawConfig = isResolvedConfig(options.config) ? undefined : options.config;
15
+ const config = isResolvedConfig(options.config)
16
+ ? options.config
17
+ : resolveSvedocsConfig(options.config ?? {});
18
+ const markdownOptions = createMarkdownCompileOptions(rawConfig, config);
19
+ const files = await fg(config.content.include, {
20
+ cwd: projectRoot,
21
+ absolute: false,
22
+ ignore: config.content.exclude,
23
+ onlyFiles: true
24
+ });
25
+ const pages = await Promise.all(files
26
+ .filter((file) => isContentFile(file))
27
+ .sort()
28
+ .map((file) => loadContentFile(projectRoot, file, config, markdownOptions)));
29
+ const sorted = pages.sort((a, b) => a.routePath.localeCompare(b.routePath));
30
+ const tree = createPageTree(sorted);
31
+ wirePrevNext(sorted, tree);
32
+ const manifest = {
33
+ config,
34
+ pages: sorted,
35
+ tree,
36
+ search: createSearchRecords(sorted),
37
+ issues: []
38
+ };
39
+ manifest.issues = await checkSvedocsContent(manifest, projectRoot);
40
+ return manifest;
41
+ }
42
+ function isContentFile(file) {
43
+ return /\.(md|mdx|svx)$/.test(file);
44
+ }
45
+ async function loadContentFile(projectRoot, file, config, markdownOptions) {
46
+ const sourcePath = path.join(projectRoot, file);
47
+ const raw = await readFile(sourcePath, 'utf8');
48
+ const fileStats = await stat(sourcePath);
49
+ const parsed = matter(raw);
50
+ const markdown = parsed.content.trim();
51
+ const frontmatter = parsed.data;
52
+ const discoveredTitle = extractFirstMarkdownTitle(markdown);
53
+ const titleFromFrontmatter = stringFrontmatter(frontmatter.title);
54
+ const renderMarkdown = stripLeadingTitleHeading(markdown, titleFromFrontmatter ?? discoveredTitle);
55
+ const compiled = await compileMarkdown(renderMarkdown, markdownOptions);
56
+ const kind = inferKind(file, config);
57
+ const route = createRouteInfo(file, kind, config);
58
+ const routePath = route.routePath;
59
+ const title = titleFromFrontmatter ?? discoveredTitle ?? compiled.title ?? titleFromRoute(routePath);
60
+ const navTitle = stringFrontmatter(frontmatter.navTitle) ?? stringFrontmatter(frontmatter.nav_title);
61
+ const description = stringFrontmatter(frontmatter.description);
62
+ const order = numberFrontmatter(frontmatter.order);
63
+ const hidden = booleanFrontmatter(frontmatter.hidden);
64
+ const collapsed = booleanFrontmatter(frontmatter.collapsed);
65
+ const section = booleanFrontmatter(frontmatter.section);
66
+ const icon = stringFrontmatter(frontmatter.icon);
67
+ const keywords = stringArrayFrontmatter(frontmatter.keywords);
68
+ const type = stringFrontmatter(frontmatter.type) ?? stringFrontmatter(frontmatter.ogType) ?? stringFrontmatter(frontmatter.og_type);
69
+ const author = stringFrontmatter(frontmatter.author) ?? config.seo.defaultAuthor;
70
+ const publishedTime = dateFrontmatter(frontmatter.publishedTime)
71
+ ?? dateFrontmatter(frontmatter.published_time)
72
+ ?? dateFrontmatter(frontmatter.published)
73
+ ?? dateFrontmatter(frontmatter.date);
74
+ const updatedTime = dateFrontmatter(frontmatter.updatedTime)
75
+ ?? dateFrontmatter(frontmatter.updated_time)
76
+ ?? dateFrontmatter(frontmatter.updated);
77
+ const canonical = stringFrontmatter(frontmatter.canonical) ?? (config.site.url ? new URL(routePath, config.site.url).href : undefined);
78
+ const image = stringFrontmatter(frontmatter.image);
79
+ const page = {
80
+ id: createPageId(file),
81
+ sourcePath: file,
82
+ routePath,
83
+ scopePath: route.scopePath,
84
+ slug: route.slug,
85
+ ...(route.locale ? { locale: route.locale } : {}),
86
+ kind,
87
+ title,
88
+ ...(navTitle ? { navTitle } : {}),
89
+ ...(description ? { description } : {}),
90
+ ...(typeof order === 'number' ? { order } : {}),
91
+ ...(hidden === true ? { hidden } : {}),
92
+ ...(typeof collapsed === 'boolean' ? { collapsed } : {}),
93
+ ...(section === true ? { section } : {}),
94
+ ...(icon ? { icon } : {}),
95
+ html: compiled.html,
96
+ plainText: compiled.plainText,
97
+ headings: compiled.headings,
98
+ links: extractMarkdownLinks(markdown),
99
+ codeBlocks: compiled.codeBlocks,
100
+ frontmatter,
101
+ seo: {
102
+ title,
103
+ ...(description ? { description } : {}),
104
+ ...(canonical ? { canonical } : {}),
105
+ ...(image ? { image } : {}),
106
+ ...(keywords.length > 0 ? { keywords } : {}),
107
+ ...(type ? { type } : {}),
108
+ ...(author ? { author } : {}),
109
+ ...(publishedTime ? { publishedTime } : {}),
110
+ ...(updatedTime ? { updatedTime } : {})
111
+ },
112
+ search: [],
113
+ lastUpdated: fileStats.mtime.toISOString(),
114
+ ...(config.source.editBaseUrl ? { editUrl: createEditUrl(config.source.editBaseUrl, file) } : {})
115
+ };
116
+ page.search = createPageSearchRecords(page, renderMarkdown);
117
+ return page;
118
+ }
119
+ function createMarkdownCompileOptions(rawConfig, resolvedConfig) {
120
+ return {
121
+ ...(rawConfig?.markdown?.remarkPlugins ? { remarkPlugins: rawConfig.markdown.remarkPlugins } : {}),
122
+ ...(rawConfig?.markdown?.rehypePlugins ? { rehypePlugins: rawConfig.markdown.rehypePlugins } : {}),
123
+ ...(rawConfig?.markdown?.shiki?.transformers ? { shikiTransformers: rawConfig.markdown.shiki.transformers } : {}),
124
+ codeThemes: {
125
+ light: resolvedConfig.theme.codeTheme.light,
126
+ dark: resolvedConfig.theme.codeTheme.dark
127
+ },
128
+ codeLineNumbers: resolvedConfig.theme.code.lineNumbers,
129
+ codeWrap: resolvedConfig.theme.code.wrap
130
+ };
131
+ }
132
+ function createEditUrl(baseUrl, sourcePath) {
133
+ return `${baseUrl.replace(/\/$/, '')}/${normalizePath(sourcePath)}`;
134
+ }
135
+ function dateFrontmatter(value) {
136
+ const text = stringFrontmatter(value);
137
+ if (text)
138
+ return text;
139
+ if (value instanceof Date && !Number.isNaN(value.valueOf()))
140
+ return value.toISOString();
141
+ return undefined;
142
+ }
143
+ function inferKind(file, config) {
144
+ return normalizePath(file).startsWith(`${normalizePath(config.content.docs)}/`) ? 'doc' : 'page';
145
+ }
146
+ function createRouteInfo(file, kind, config) {
147
+ const normalized = normalizePath(file);
148
+ const base = normalizePath(kind === 'doc' ? config.content.docs : config.content.pages);
149
+ const relative = normalized.startsWith(`${base}/`) ? normalized.slice(base.length + 1) : normalized;
150
+ const withoutExt = relative.replace(/\.(md|mdx|svx)$/, '');
151
+ const rawParts = withoutExt.split('/').filter(Boolean);
152
+ const parts = rawParts.at(-1) === 'index' ? rawParts.slice(0, -1) : [...rawParts];
153
+ const routeParts = kind === 'doc' ? ['docs'] : [];
154
+ const locale = consumeLocale(parts, config);
155
+ const scopeParts = [...parts];
156
+ if (locale && shouldPrefixLocale(locale, config)) {
157
+ routeParts.push(findLocalePath(locale, config) ?? locale);
158
+ }
159
+ routeParts.push(...parts);
160
+ const routePath = `/${routeParts.join('/')}`.replace(/\/$/, '') || '/';
161
+ const scopePath = `/${[...(kind === 'doc' ? ['docs'] : []), ...scopeParts].join('/')}`.replace(/\/$/, '') || '/';
162
+ return {
163
+ routePath,
164
+ scopePath,
165
+ slug: routePath.split('/').filter(Boolean),
166
+ ...(locale ? { locale } : {})
167
+ };
168
+ }
169
+ function consumeLocale(parts, config) {
170
+ if (config.i18n.locales.length === 0)
171
+ return undefined;
172
+ const match = config.i18n.locales.find((locale) => locale.path === parts[0] || locale.code === parts[0]);
173
+ if (match) {
174
+ parts.shift();
175
+ return match.code;
176
+ }
177
+ return config.i18n.defaultLocale;
178
+ }
179
+ function shouldPrefixLocale(locale, config) {
180
+ return config.i18n.prefixDefaultLocale || locale !== config.i18n.defaultLocale;
181
+ }
182
+ function findLocalePath(locale, config) {
183
+ return config.i18n.locales.find((item) => item.code === locale)?.path;
184
+ }
185
+ function createPageId(file) {
186
+ return normalizePath(file).replace(/\.(md|mdx|svx)$/, '').replace(/[^a-zA-Z0-9]+/g, '-');
187
+ }
188
+ function titleFromRoute(routePath) {
189
+ const last = routePath.split('/').filter(Boolean).at(-1) ?? 'home';
190
+ return titleFromSegment(last);
191
+ }
192
+ function extractFirstMarkdownTitle(markdown) {
193
+ const match = /^#\s+(.+?)\s*#*$/m.exec(markdown);
194
+ return match?.[1] ? stripInlineMarkdown(match[1]) : undefined;
195
+ }
196
+ function stripLeadingTitleHeading(markdown, title) {
197
+ if (!title)
198
+ return markdown;
199
+ const lines = markdown.split(/\r?\n/);
200
+ const firstContentIndex = lines.findIndex((line) => line.trim().length > 0);
201
+ if (firstContentIndex < 0)
202
+ return markdown;
203
+ const match = /^#\s+(.+?)\s*#*$/.exec(lines[firstContentIndex] ?? '');
204
+ if (!match?.[1])
205
+ return markdown;
206
+ const heading = stripInlineMarkdown(match[1]);
207
+ if (heading !== title)
208
+ return markdown;
209
+ lines.splice(firstContentIndex, 1);
210
+ return lines.join('\n').trim();
211
+ }
@@ -0,0 +1,8 @@
1
+ import type { SvedocsContentIssue, SvedocsContentManifest, SvedocsLinkReference, SvedocsPage } from './types.js';
2
+ export declare function extractMarkdownLinks(markdown: string): SvedocsLinkReference[];
3
+ export declare function createLinkCheckContext(manifest: SvedocsContentManifest): {
4
+ routeMap: Map<string, SvedocsPage>;
5
+ sourceMap: Map<string, SvedocsPage>;
6
+ };
7
+ export declare function checkLink(page: SvedocsPage, link: SvedocsLinkReference, context: ReturnType<typeof createLinkCheckContext>): SvedocsContentIssue | undefined;
8
+ //# sourceMappingURL=links.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../../src/core/links.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAIjH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAE7E;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,sBAAsB;;;EAStE;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,oBAAoB,EAC1B,OAAO,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,GACjD,mBAAmB,GAAG,SAAS,CAwBjC"}