@alliance-droid/svelte-docs-system 0.0.2 → 0.1.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/README.md +155 -23
- package/dist/components/APITable.svelte.d.ts +21 -0
- package/dist/components/Breadcrumbs.svelte.d.ts +14 -0
- package/dist/components/Callout.svelte.d.ts +15 -0
- package/dist/components/CodeBlock.svelte.d.ts +12 -0
- package/{src/lib → dist}/components/DocLayout.svelte +18 -6
- package/dist/components/DocLayout.svelte.d.ts +18 -0
- package/dist/components/DocsPage.svelte +39 -0
- package/dist/components/DocsPage.svelte.d.ts +8 -0
- package/dist/components/Documentation.svelte +639 -0
- package/dist/components/Footer.svelte.d.ts +10 -0
- package/dist/components/Image.svelte.d.ts +15 -0
- package/{src/lib → dist}/components/Navbar.svelte +4 -4
- package/dist/components/Navbar.svelte.d.ts +10 -0
- package/{src/lib → dist}/components/Search.svelte +2 -2
- package/dist/components/Search.svelte.d.ts +6 -0
- package/{template-starter/src/lib → dist}/components/Sidebar.svelte +2 -2
- package/dist/components/Sidebar.svelte.d.ts +9 -0
- package/dist/components/Tabs.svelte.d.ts +16 -0
- package/dist/config.d.ts +93 -0
- package/dist/config.js +89 -0
- package/dist/configLoader.d.ts +48 -0
- package/dist/configLoader.js +187 -0
- package/dist/configParser.d.ts +27 -0
- package/dist/configParser.js +208 -0
- package/{template-starter/src/lib/index.ts → dist/index.d.ts} +6 -7
- package/dist/index.js +18 -0
- package/dist/navigationBuilder.d.ts +64 -0
- package/dist/navigationBuilder.js +225 -0
- package/dist/plugin.d.ts +30 -0
- package/dist/plugin.js +172 -0
- package/dist/routing.d.ts +48 -0
- package/dist/routing.js +92 -0
- package/dist/stores/i18n.d.ts +20 -0
- package/dist/stores/i18n.js +119 -0
- package/dist/stores/nav.d.ts +20 -0
- package/dist/stores/nav.js +15 -0
- package/dist/stores/search.d.ts +49 -0
- package/dist/stores/search.js +127 -0
- package/dist/stores/theme.d.ts +7 -0
- package/dist/stores/theme.js +152 -0
- package/dist/stores/version.d.ts +18 -0
- package/dist/stores/version.js +93 -0
- package/dist/themeCustomization.d.ts +46 -0
- package/dist/themeCustomization.js +188 -0
- package/dist/utils/highlight.d.ts +13 -0
- package/dist/utils/highlight.js +83 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/markdown.d.ts +40 -0
- package/dist/utils/markdown.js +165 -0
- package/package.json +44 -23
- package/COMPONENTS.md +0 -365
- package/COVERAGE_REPORT.md +0 -663
- package/SEARCH_VERIFICATION.md +0 -229
- package/TEST_SUMMARY.md +0 -344
- package/bin/init.js +0 -821
- package/docs/COMPONENT_LIBRARY_INTEGRATION_REPORT.md +0 -153
- package/docs/DARK_MODE_AUDIT_REPORT.md +0 -403
- package/docs/E2E_TESTS.md +0 -354
- package/docs/TESTING.md +0 -754
- package/docs/THEME_INHERITANCE.md +0 -192
- package/docs/de/index.md +0 -41
- package/docs/en/COMPONENTS.md +0 -443
- package/docs/en/api/examples.md +0 -100
- package/docs/en/api/overview.md +0 -69
- package/docs/en/components/index.md +0 -622
- package/docs/en/config/navigation.md +0 -505
- package/docs/en/config/theme-and-colors.md +0 -395
- package/docs/en/getting-started/integration.md +0 -406
- package/docs/en/guides/common-setups.md +0 -651
- package/docs/en/index.md +0 -243
- package/docs/en/markdown.md +0 -102
- package/docs/en/routing.md +0 -64
- package/docs/en/setup.md +0 -52
- package/docs/en/troubleshooting.md +0 -704
- package/docs/es/index.md +0 -41
- package/docs/fr/index.md +0 -41
- package/docs/ja/index.md +0 -41
- package/pagefind.toml +0 -8
- package/postcss.config.js +0 -5
- package/src/app.css +0 -119
- package/src/app.d.ts +0 -13
- package/src/app.html +0 -11
- package/src/lib/components/APITable.test.ts +0 -153
- package/src/lib/components/Breadcrumbs.test.ts +0 -148
- package/src/lib/components/Callout.test.ts +0 -100
- package/src/lib/components/CodeBlock.test.ts +0 -133
- package/src/lib/components/Image.test.ts +0 -163
- package/src/lib/components/Sidebar.svelte +0 -110
- package/src/lib/components/Tabs.test.ts +0 -102
- package/src/lib/config.test.ts +0 -140
- package/src/lib/config.ts +0 -179
- package/src/lib/configIntegration.test.ts +0 -272
- package/src/lib/configLoader.ts +0 -231
- package/src/lib/configParser.test.ts +0 -217
- package/src/lib/configParser.ts +0 -234
- package/src/lib/index.ts +0 -37
- package/src/lib/integration.test.ts +0 -426
- package/src/lib/navigationBuilder.test.ts +0 -338
- package/src/lib/navigationBuilder.ts +0 -268
- package/src/lib/performance.test.ts +0 -369
- package/src/lib/routing.test.ts +0 -202
- package/src/lib/routing.ts +0 -127
- package/src/lib/search-functionality.test.ts +0 -493
- package/src/lib/stores/i18n.test.ts +0 -180
- package/src/lib/stores/i18n.ts +0 -143
- package/src/lib/stores/nav.ts +0 -36
- package/src/lib/stores/search.test.ts +0 -140
- package/src/lib/stores/search.ts +0 -162
- package/src/lib/stores/theme.test.ts +0 -117
- package/src/lib/stores/theme.ts +0 -167
- package/src/lib/stores/version.test.ts +0 -139
- package/src/lib/stores/version.ts +0 -111
- package/src/lib/themeCustomization.test.ts +0 -223
- package/src/lib/themeCustomization.ts +0 -212
- package/src/lib/utils/highlight.test.ts +0 -136
- package/src/lib/utils/highlight.ts +0 -100
- package/src/lib/utils/index.ts +0 -7
- package/src/lib/utils/markdown.test.ts +0 -357
- package/src/lib/utils/markdown.ts +0 -77
- package/src/routes/+layout.server.ts +0 -1
- package/src/routes/+layout.svelte +0 -29
- package/src/routes/+page.svelte +0 -165
- package/src/routes/quote-demo/+page.svelte +0 -141
- package/static/robots.txt +0 -3
- package/svelte.config.js +0 -15
- package/tailwind.config.ts +0 -55
- package/template-starter/.github/workflows/build.yml +0 -40
- package/template-starter/.github/workflows/deploy-github-pages.yml +0 -47
- package/template-starter/.github/workflows/deploy-netlify.yml +0 -41
- package/template-starter/.github/workflows/deploy-vercel.yml +0 -64
- package/template-starter/NPM-PACKAGE-SETUP.md +0 -233
- package/template-starter/README.md +0 -320
- package/template-starter/docs/_config.json +0 -39
- package/template-starter/docs/api/components.md +0 -257
- package/template-starter/docs/api/overview.md +0 -169
- package/template-starter/docs/guides/configuration.md +0 -145
- package/template-starter/docs/guides/github-pages-deployment.md +0 -254
- package/template-starter/docs/guides/netlify-deployment.md +0 -159
- package/template-starter/docs/guides/vercel-deployment.md +0 -131
- package/template-starter/docs/index.md +0 -49
- package/template-starter/docs/setup.md +0 -149
- package/template-starter/package.json +0 -31
- package/template-starter/pagefind.toml +0 -3
- package/template-starter/postcss.config.js +0 -5
- package/template-starter/src/app.css +0 -34
- package/template-starter/src/app.d.ts +0 -13
- package/template-starter/src/app.html +0 -11
- package/template-starter/src/lib/components/APITable.svelte +0 -120
- package/template-starter/src/lib/components/APITable.test.ts +0 -96
- package/template-starter/src/lib/components/Breadcrumbs.svelte +0 -85
- package/template-starter/src/lib/components/Breadcrumbs.test.ts +0 -82
- package/template-starter/src/lib/components/Callout.svelte +0 -60
- package/template-starter/src/lib/components/Callout.test.ts +0 -91
- package/template-starter/src/lib/components/CodeBlock.svelte +0 -68
- package/template-starter/src/lib/components/CodeBlock.test.ts +0 -62
- package/template-starter/src/lib/components/DocLayout.svelte +0 -84
- package/template-starter/src/lib/components/Footer.svelte +0 -78
- package/template-starter/src/lib/components/Image.svelte +0 -100
- package/template-starter/src/lib/components/Image.test.ts +0 -81
- package/template-starter/src/lib/components/Navbar.svelte +0 -141
- package/template-starter/src/lib/components/Search.svelte +0 -248
- package/template-starter/src/lib/components/Tabs.svelte +0 -48
- package/template-starter/src/lib/components/Tabs.test.ts +0 -89
- package/template-starter/src/routes/+layout.svelte +0 -28
- package/template-starter/src/routes/+page.svelte +0 -92
- package/template-starter/svelte.config.js +0 -17
- package/template-starter/tailwind.config.ts +0 -17
- package/template-starter/tsconfig.json +0 -13
- package/template-starter/vite.config.ts +0 -6
- package/tests/e2e/example.spec.ts +0 -345
- package/tsconfig.json +0 -20
- package/vite.config.ts +0 -6
- package/vitest.config.ts +0 -33
- package/vitest.setup.ts +0 -21
- /package/{src/lib → dist}/assets/favicon.svg +0 -0
- /package/{src/lib → dist}/components/APITable.svelte +0 -0
- /package/{src/lib → dist}/components/Breadcrumbs.svelte +0 -0
- /package/{src/lib → dist}/components/Callout.svelte +0 -0
- /package/{src/lib → dist}/components/CodeBlock.svelte +0 -0
- /package/{src/lib → dist}/components/Footer.svelte +0 -0
- /package/{src/lib → dist}/components/Image.svelte +0 -0
- /package/{src/lib → dist}/components/Tabs.svelte +0 -0
- /package/{src/lib → dist}/svelte-component-library.d.ts +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
export { default as Documentation } from './components/Documentation.svelte';
|
|
2
|
+
export { svelteDocsPlugin, default as docsPlugin } from './plugin.js';
|
|
3
|
+
export type { DocsPluginOptions, DocFile, DocFolder } from './plugin.js';
|
|
3
4
|
export { default as Callout } from './components/Callout.svelte';
|
|
4
5
|
export { default as Tabs } from './components/Tabs.svelte';
|
|
5
6
|
export { default as CodeBlock } from './components/CodeBlock.svelte';
|
|
@@ -7,9 +8,7 @@ export { default as Image } from './components/Image.svelte';
|
|
|
7
8
|
export { default as APITable } from './components/APITable.svelte';
|
|
8
9
|
export { default as Breadcrumbs } from './components/Breadcrumbs.svelte';
|
|
9
10
|
export { default as Search } from './components/Search.svelte';
|
|
10
|
-
|
|
11
|
-
// Re-export theme components
|
|
12
|
-
export { default as DocLayout } from './components/DocLayout.svelte';
|
|
13
|
-
export { default as Navbar } from './components/Navbar.svelte';
|
|
14
11
|
export { default as Sidebar } from './components/Sidebar.svelte';
|
|
15
|
-
export {
|
|
12
|
+
export { renderMarkdown, renderMarkdownSync, extractMetadata, getExcerpt, type MarkdownMetadata, type MarkdownResult, } from './utils/index.js';
|
|
13
|
+
export { theme } from './stores/theme';
|
|
14
|
+
export { sidebarOpen } from './stores/nav';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Main component - drop this into your +page.svelte
|
|
2
|
+
export { default as Documentation } from './components/Documentation.svelte';
|
|
3
|
+
// Vite plugin - add to vite.config.ts
|
|
4
|
+
export { svelteDocsPlugin, default as docsPlugin } from './plugin.js';
|
|
5
|
+
// Content components for custom layouts
|
|
6
|
+
export { default as Callout } from './components/Callout.svelte';
|
|
7
|
+
export { default as Tabs } from './components/Tabs.svelte';
|
|
8
|
+
export { default as CodeBlock } from './components/CodeBlock.svelte';
|
|
9
|
+
export { default as Image } from './components/Image.svelte';
|
|
10
|
+
export { default as APITable } from './components/APITable.svelte';
|
|
11
|
+
export { default as Breadcrumbs } from './components/Breadcrumbs.svelte';
|
|
12
|
+
export { default as Search } from './components/Search.svelte';
|
|
13
|
+
export { default as Sidebar } from './components/Sidebar.svelte';
|
|
14
|
+
// Utilities
|
|
15
|
+
export { renderMarkdown, renderMarkdownSync, extractMetadata, getExcerpt, } from './utils/index.js';
|
|
16
|
+
// Stores (for advanced usage)
|
|
17
|
+
export { theme } from './stores/theme';
|
|
18
|
+
export { sidebarOpen } from './stores/nav';
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation builder for generating navigation from folder structure or config
|
|
3
|
+
*/
|
|
4
|
+
import type { NavItem, NavSection, NavigationConfig } from './config';
|
|
5
|
+
export interface FileInfo {
|
|
6
|
+
/** Full file path (e.g., docs/api/overview.md) */
|
|
7
|
+
path: string;
|
|
8
|
+
/** File name without extension (e.g., overview) */
|
|
9
|
+
name: string;
|
|
10
|
+
/** File title (from frontmatter or derived from name) */
|
|
11
|
+
title?: string;
|
|
12
|
+
/** File order (from frontmatter) */
|
|
13
|
+
order?: number;
|
|
14
|
+
/** Is this the index file? */
|
|
15
|
+
isIndex: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Convert a file name to a display title
|
|
19
|
+
* Example: "api-reference" -> "API Reference"
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatFileName(name: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Parse file information from path
|
|
24
|
+
* Example: "docs/api/reference.md" -> { path: "docs/api/reference.md", name: "reference", isIndex: false }
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseFileInfo(filePath: string, fileMetadata?: {
|
|
27
|
+
title?: string;
|
|
28
|
+
order?: number;
|
|
29
|
+
}): FileInfo;
|
|
30
|
+
/**
|
|
31
|
+
* Generate navigation from a list of files
|
|
32
|
+
* Useful for auto-generating sidebar from folder structure
|
|
33
|
+
*/
|
|
34
|
+
export declare function generateNavigationFromFiles(files: FileInfo[], baseRoute?: string): NavSection[];
|
|
35
|
+
/**
|
|
36
|
+
* Flatten navigation sections to a single array of NavItems
|
|
37
|
+
*/
|
|
38
|
+
export declare function flattenNavigation(sections: NavSection[]): NavItem[];
|
|
39
|
+
/**
|
|
40
|
+
* Validate navigation configuration
|
|
41
|
+
*/
|
|
42
|
+
export declare function validateNavigation(nav: NavigationConfig): {
|
|
43
|
+
valid: boolean;
|
|
44
|
+
errors: string[];
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Filter files based on exclude patterns
|
|
48
|
+
*/
|
|
49
|
+
export declare function filterFiles(files: FileInfo[], excludePatterns?: string[]): FileInfo[];
|
|
50
|
+
/**
|
|
51
|
+
* Build navigation from a config object
|
|
52
|
+
*/
|
|
53
|
+
export declare function buildNavigationFromConfig(config: NavigationConfig, files?: FileInfo[], baseRoute?: string): NavSection[];
|
|
54
|
+
/**
|
|
55
|
+
* Find a navigation item by href
|
|
56
|
+
*/
|
|
57
|
+
export declare function findNavItem(sections: NavSection[], href: string): NavItem | null;
|
|
58
|
+
/**
|
|
59
|
+
* Get previous and next navigation items in order
|
|
60
|
+
*/
|
|
61
|
+
export declare function getPrevNext(sections: NavSection[], currentHref: string): {
|
|
62
|
+
prev: NavItem | null;
|
|
63
|
+
next: NavItem | null;
|
|
64
|
+
};
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation builder for generating navigation from folder structure or config
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Convert a file name to a display title
|
|
6
|
+
* Example: "api-reference" -> "API Reference"
|
|
7
|
+
*/
|
|
8
|
+
export function formatFileName(name) {
|
|
9
|
+
return name
|
|
10
|
+
.replace(/[-_]/g, ' ') // Replace hyphens and underscores with spaces
|
|
11
|
+
.replace(/\b\w/g, (char) => char.toUpperCase()); // Capitalize first letter of each word
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Parse file information from path
|
|
15
|
+
* Example: "docs/api/reference.md" -> { path: "docs/api/reference.md", name: "reference", isIndex: false }
|
|
16
|
+
*/
|
|
17
|
+
export function parseFileInfo(filePath, fileMetadata) {
|
|
18
|
+
const cleanPath = filePath.replace(/^\.\//, ''); // Remove leading ./
|
|
19
|
+
const lastSlash = cleanPath.lastIndexOf('/');
|
|
20
|
+
const fileName = cleanPath.substring(lastSlash + 1);
|
|
21
|
+
const name = fileName.replace(/\.md$/, '');
|
|
22
|
+
return {
|
|
23
|
+
path: cleanPath,
|
|
24
|
+
name,
|
|
25
|
+
title: fileMetadata?.title || formatFileName(name),
|
|
26
|
+
order: fileMetadata?.order,
|
|
27
|
+
isIndex: name === 'index',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Group files by directory
|
|
32
|
+
*/
|
|
33
|
+
function groupFilesByDirectory(files) {
|
|
34
|
+
const groups = new Map();
|
|
35
|
+
files.forEach((file) => {
|
|
36
|
+
const match = file.path.match(/^docs\/(.+)\/[^/]+\.md$/);
|
|
37
|
+
const dir = match ? match[1] : 'root';
|
|
38
|
+
if (!groups.has(dir)) {
|
|
39
|
+
groups.set(dir, []);
|
|
40
|
+
}
|
|
41
|
+
groups.get(dir).push(file);
|
|
42
|
+
});
|
|
43
|
+
return groups;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Sort files by order, then alphabetically
|
|
47
|
+
*/
|
|
48
|
+
function sortFiles(files) {
|
|
49
|
+
return [...files].sort((a, b) => {
|
|
50
|
+
// Priority to index files at the top
|
|
51
|
+
if (a.isIndex && !b.isIndex)
|
|
52
|
+
return -1;
|
|
53
|
+
if (!a.isIndex && b.isIndex)
|
|
54
|
+
return 1;
|
|
55
|
+
// Then sort by order if specified
|
|
56
|
+
if (a.order !== undefined && b.order !== undefined) {
|
|
57
|
+
return a.order - b.order;
|
|
58
|
+
}
|
|
59
|
+
if (a.order !== undefined)
|
|
60
|
+
return -1;
|
|
61
|
+
if (b.order !== undefined)
|
|
62
|
+
return 1;
|
|
63
|
+
// Finally alphabetically
|
|
64
|
+
return a.name.localeCompare(b.name);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Generate navigation from a list of files
|
|
69
|
+
* Useful for auto-generating sidebar from folder structure
|
|
70
|
+
*/
|
|
71
|
+
export function generateNavigationFromFiles(files, baseRoute = '/docs') {
|
|
72
|
+
const groups = groupFilesByDirectory(files);
|
|
73
|
+
const sections = [];
|
|
74
|
+
// Process files in natural order
|
|
75
|
+
const sortedDirs = Array.from(groups.keys()).sort((a, b) => {
|
|
76
|
+
if (a === 'root')
|
|
77
|
+
return -1;
|
|
78
|
+
if (b === 'root')
|
|
79
|
+
return 1;
|
|
80
|
+
return a.localeCompare(b);
|
|
81
|
+
});
|
|
82
|
+
sortedDirs.forEach((dir) => {
|
|
83
|
+
const dirFiles = sortFiles(groups.get(dir));
|
|
84
|
+
if (dir === 'root') {
|
|
85
|
+
// Root files go to main section
|
|
86
|
+
dirFiles.forEach((file) => {
|
|
87
|
+
if (!file.isIndex) {
|
|
88
|
+
if (!sections[0]) {
|
|
89
|
+
sections.push({ title: 'Documentation', items: [] });
|
|
90
|
+
}
|
|
91
|
+
sections[0].items.push({
|
|
92
|
+
label: file.title || file.name,
|
|
93
|
+
href: `${baseRoute}/${file.name}`,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Directory files go to their own section
|
|
100
|
+
const sectionTitle = formatFileName(dir);
|
|
101
|
+
const items = dirFiles.map((file) => ({
|
|
102
|
+
label: file.title || file.name,
|
|
103
|
+
href: `${baseRoute}/${dir}/${file.name}`,
|
|
104
|
+
}));
|
|
105
|
+
sections.push({ title: sectionTitle, items });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
return sections;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Flatten navigation sections to a single array of NavItems
|
|
112
|
+
*/
|
|
113
|
+
export function flattenNavigation(sections) {
|
|
114
|
+
const items = [];
|
|
115
|
+
sections.forEach((section) => {
|
|
116
|
+
items.push({
|
|
117
|
+
label: section.title,
|
|
118
|
+
children: section.items,
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
return items;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Validate navigation configuration
|
|
125
|
+
*/
|
|
126
|
+
export function validateNavigation(nav) {
|
|
127
|
+
const errors = [];
|
|
128
|
+
if (!nav || typeof nav !== 'object') {
|
|
129
|
+
return { valid: false, errors: ['Navigation must be an object'] };
|
|
130
|
+
}
|
|
131
|
+
if (nav.sections !== undefined) {
|
|
132
|
+
if (!Array.isArray(nav.sections)) {
|
|
133
|
+
errors.push('sections must be an array');
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
nav.sections.forEach((section, idx) => {
|
|
137
|
+
if (!section.title) {
|
|
138
|
+
errors.push(`Section ${idx}: missing title`);
|
|
139
|
+
}
|
|
140
|
+
if (!Array.isArray(section.items)) {
|
|
141
|
+
errors.push(`Section ${idx}: items must be an array`);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
section.items.forEach((item, itemIdx) => {
|
|
145
|
+
if (!item.label) {
|
|
146
|
+
errors.push(`Section ${idx}, item ${itemIdx}: missing label`);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (nav.excludePaths !== undefined && !Array.isArray(nav.excludePaths)) {
|
|
154
|
+
errors.push('excludePaths must be an array');
|
|
155
|
+
}
|
|
156
|
+
return { valid: errors.length === 0, errors };
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Filter files based on exclude patterns
|
|
160
|
+
*/
|
|
161
|
+
export function filterFiles(files, excludePatterns = []) {
|
|
162
|
+
return files.filter((file) => {
|
|
163
|
+
return !excludePatterns.some((pattern) => {
|
|
164
|
+
// Simple pattern matching (could be extended to regex)
|
|
165
|
+
return file.path.includes(pattern);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Build navigation from a config object
|
|
171
|
+
*/
|
|
172
|
+
export function buildNavigationFromConfig(config, files, baseRoute = '/docs') {
|
|
173
|
+
// If explicit sections are provided, use them
|
|
174
|
+
if (config.sections && config.sections.length > 0) {
|
|
175
|
+
return config.sections;
|
|
176
|
+
}
|
|
177
|
+
// Otherwise auto-generate from files
|
|
178
|
+
if (files && files.length > 0) {
|
|
179
|
+
const filtered = filterFiles(files, config.excludePaths);
|
|
180
|
+
return generateNavigationFromFiles(filtered, baseRoute);
|
|
181
|
+
}
|
|
182
|
+
// Return empty navigation
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Find a navigation item by href
|
|
187
|
+
*/
|
|
188
|
+
export function findNavItem(sections, href) {
|
|
189
|
+
for (const section of sections) {
|
|
190
|
+
for (const item of section.items) {
|
|
191
|
+
if (item.href === href) {
|
|
192
|
+
return item;
|
|
193
|
+
}
|
|
194
|
+
if (item.children) {
|
|
195
|
+
const found = item.children.find((child) => child.href === href);
|
|
196
|
+
if (found)
|
|
197
|
+
return found;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Get previous and next navigation items in order
|
|
205
|
+
*/
|
|
206
|
+
export function getPrevNext(sections, currentHref) {
|
|
207
|
+
const allItems = [];
|
|
208
|
+
sections.forEach((section) => {
|
|
209
|
+
section.items.forEach((item) => {
|
|
210
|
+
if (item.href)
|
|
211
|
+
allItems.push(item);
|
|
212
|
+
if (item.children) {
|
|
213
|
+
item.children.forEach((child) => {
|
|
214
|
+
if (child.href)
|
|
215
|
+
allItems.push(child);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
const currentIndex = allItems.findIndex((item) => item.href === currentHref);
|
|
221
|
+
return {
|
|
222
|
+
prev: currentIndex > 0 ? allItems[currentIndex - 1] : null,
|
|
223
|
+
next: currentIndex < allItems.length - 1 ? allItems[currentIndex + 1] : null,
|
|
224
|
+
};
|
|
225
|
+
}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin for svelte-docs-system
|
|
3
|
+
* Auto-scans /docs folder and generates navigation structure
|
|
4
|
+
*/
|
|
5
|
+
import type { Plugin } from 'vite';
|
|
6
|
+
export interface DocFile {
|
|
7
|
+
slug: string;
|
|
8
|
+
path: string;
|
|
9
|
+
title: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
order?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface DocFolder {
|
|
14
|
+
name: string;
|
|
15
|
+
slug: string;
|
|
16
|
+
files: DocFile[];
|
|
17
|
+
folders: DocFolder[];
|
|
18
|
+
order?: number;
|
|
19
|
+
}
|
|
20
|
+
export interface DocsPluginOptions {
|
|
21
|
+
/** Path to docs folder relative to project root. Default: './docs' */
|
|
22
|
+
docsPath?: string;
|
|
23
|
+
/** Route where docs will be served. Default: '/documentation' */
|
|
24
|
+
route?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Vite plugin for svelte-docs-system
|
|
28
|
+
*/
|
|
29
|
+
export declare function svelteDocsPlugin(options?: DocsPluginOptions): Plugin;
|
|
30
|
+
export default svelteDocsPlugin;
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin for svelte-docs-system
|
|
3
|
+
* Auto-scans /docs folder and generates navigation structure
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import matter from 'gray-matter';
|
|
8
|
+
const VIRTUAL_MODULE_ID = 'virtual:svelte-docs';
|
|
9
|
+
const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID;
|
|
10
|
+
/**
|
|
11
|
+
* Extract frontmatter and title from markdown file
|
|
12
|
+
*/
|
|
13
|
+
function parseMarkdownFile(filePath) {
|
|
14
|
+
try {
|
|
15
|
+
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
16
|
+
const { data, content } = matter(fileContent);
|
|
17
|
+
// Get title from frontmatter or first H1
|
|
18
|
+
let title = data.title;
|
|
19
|
+
if (!title) {
|
|
20
|
+
const h1Match = content.match(/^#\s+(.+)$/m);
|
|
21
|
+
title = h1Match ? h1Match[1] : path.basename(filePath, '.md');
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
title,
|
|
25
|
+
description: data.description,
|
|
26
|
+
order: data.order ?? data.sidebar_position,
|
|
27
|
+
content
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return {
|
|
32
|
+
title: path.basename(filePath, '.md'),
|
|
33
|
+
content: ''
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Convert filename to readable title
|
|
39
|
+
*/
|
|
40
|
+
function slugToTitle(slug) {
|
|
41
|
+
return slug
|
|
42
|
+
.replace(/-/g, ' ')
|
|
43
|
+
.replace(/_/g, ' ')
|
|
44
|
+
.replace(/\b\w/g, c => c.toUpperCase());
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Recursively scan docs folder
|
|
48
|
+
*/
|
|
49
|
+
function scanDocsFolder(dirPath, basePath = '') {
|
|
50
|
+
const name = path.basename(dirPath);
|
|
51
|
+
const slug = basePath || '/';
|
|
52
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
53
|
+
const files = [];
|
|
54
|
+
const folders = [];
|
|
55
|
+
// Check for folder config (index.md or _category_.json)
|
|
56
|
+
let folderOrder;
|
|
57
|
+
const indexPath = path.join(dirPath, 'index.md');
|
|
58
|
+
if (fs.existsSync(indexPath)) {
|
|
59
|
+
const { order } = parseMarkdownFile(indexPath);
|
|
60
|
+
folderOrder = order;
|
|
61
|
+
}
|
|
62
|
+
for (const entry of entries) {
|
|
63
|
+
const entryPath = path.join(dirPath, entry.name);
|
|
64
|
+
if (entry.isDirectory() && !entry.name.startsWith('_') && !entry.name.startsWith('.')) {
|
|
65
|
+
// Recurse into subdirectory
|
|
66
|
+
const subSlug = basePath ? `${basePath}/${entry.name}` : entry.name;
|
|
67
|
+
folders.push(scanDocsFolder(entryPath, subSlug));
|
|
68
|
+
}
|
|
69
|
+
else if (entry.isFile() && entry.name.endsWith('.md') && entry.name !== 'index.md') {
|
|
70
|
+
// Parse markdown file
|
|
71
|
+
const fileSlug = entry.name.replace(/\.md$/, '');
|
|
72
|
+
const fullSlug = basePath ? `${basePath}/${fileSlug}` : fileSlug;
|
|
73
|
+
const { title, description, order } = parseMarkdownFile(entryPath);
|
|
74
|
+
files.push({
|
|
75
|
+
slug: fullSlug,
|
|
76
|
+
path: entryPath,
|
|
77
|
+
title,
|
|
78
|
+
description,
|
|
79
|
+
order
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Sort by order, then alphabetically
|
|
84
|
+
files.sort((a, b) => {
|
|
85
|
+
if (a.order !== undefined && b.order !== undefined)
|
|
86
|
+
return a.order - b.order;
|
|
87
|
+
if (a.order !== undefined)
|
|
88
|
+
return -1;
|
|
89
|
+
if (b.order !== undefined)
|
|
90
|
+
return 1;
|
|
91
|
+
return a.title.localeCompare(b.title);
|
|
92
|
+
});
|
|
93
|
+
folders.sort((a, b) => {
|
|
94
|
+
if (a.order !== undefined && b.order !== undefined)
|
|
95
|
+
return a.order - b.order;
|
|
96
|
+
if (a.order !== undefined)
|
|
97
|
+
return -1;
|
|
98
|
+
if (b.order !== undefined)
|
|
99
|
+
return 1;
|
|
100
|
+
return a.name.localeCompare(b.name);
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
name: slugToTitle(name),
|
|
104
|
+
slug,
|
|
105
|
+
files,
|
|
106
|
+
folders,
|
|
107
|
+
order: folderOrder
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Generate the docs structure as a module
|
|
112
|
+
*/
|
|
113
|
+
function generateDocsModule(docsPath, route) {
|
|
114
|
+
if (!fs.existsSync(docsPath)) {
|
|
115
|
+
// Return empty structure if docs folder doesn't exist
|
|
116
|
+
return `
|
|
117
|
+
export const docsStructure = { name: 'Documentation', slug: '/', files: [], folders: [] };
|
|
118
|
+
export const docsRoute = '${route}';
|
|
119
|
+
export const allDocs = [];
|
|
120
|
+
`;
|
|
121
|
+
}
|
|
122
|
+
const structure = scanDocsFolder(docsPath);
|
|
123
|
+
// Flatten all docs for search/lookup
|
|
124
|
+
const allDocs = [];
|
|
125
|
+
function collectDocs(folder) {
|
|
126
|
+
allDocs.push(...folder.files);
|
|
127
|
+
folder.folders.forEach(collectDocs);
|
|
128
|
+
}
|
|
129
|
+
collectDocs(structure);
|
|
130
|
+
return `
|
|
131
|
+
export const docsStructure = ${JSON.stringify(structure, null, 2)};
|
|
132
|
+
export const docsRoute = '${route}';
|
|
133
|
+
export const allDocs = ${JSON.stringify(allDocs, null, 2)};
|
|
134
|
+
`;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Vite plugin for svelte-docs-system
|
|
138
|
+
*/
|
|
139
|
+
export function svelteDocsPlugin(options = {}) {
|
|
140
|
+
const docsPath = options.docsPath || './docs';
|
|
141
|
+
const route = options.route || '/documentation';
|
|
142
|
+
let root;
|
|
143
|
+
return {
|
|
144
|
+
name: 'svelte-docs-system',
|
|
145
|
+
configResolved(config) {
|
|
146
|
+
root = config.root;
|
|
147
|
+
},
|
|
148
|
+
resolveId(id) {
|
|
149
|
+
if (id === VIRTUAL_MODULE_ID) {
|
|
150
|
+
return RESOLVED_VIRTUAL_MODULE_ID;
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
load(id) {
|
|
154
|
+
if (id === RESOLVED_VIRTUAL_MODULE_ID) {
|
|
155
|
+
const fullDocsPath = path.resolve(root, docsPath);
|
|
156
|
+
return generateDocsModule(fullDocsPath, route);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
handleHotUpdate({ file, server }) {
|
|
160
|
+
// Reload when docs files change
|
|
161
|
+
const fullDocsPath = path.resolve(root, docsPath);
|
|
162
|
+
if (file.startsWith(fullDocsPath) && file.endsWith('.md')) {
|
|
163
|
+
const module = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_MODULE_ID);
|
|
164
|
+
if (module) {
|
|
165
|
+
server.moduleGraph.invalidateModule(module);
|
|
166
|
+
return [module];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export default svelteDocsPlugin;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routing utilities for customizable documentation mount points
|
|
3
|
+
*/
|
|
4
|
+
import type { ConfigOptions } from './config';
|
|
5
|
+
export interface RouteConfig {
|
|
6
|
+
/** Mount point for documentation (e.g., /docs, /help, /guides) */
|
|
7
|
+
docsRoute: string;
|
|
8
|
+
/** Path to docs folder relative to project root */
|
|
9
|
+
docsFolderPath: string;
|
|
10
|
+
/** Base path for links */
|
|
11
|
+
basePath: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Converts a route slug to a markdown file path
|
|
15
|
+
* Example: slug="api/overview", docsFolderPath="./docs" -> "./docs/api/overview.md"
|
|
16
|
+
*/
|
|
17
|
+
export declare function slugToFilePath(slug: string | string[], docsFolderPath: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Converts a file path to a route URL
|
|
20
|
+
* Example: filePath="./docs/api/overview.md", docsRoute="/docs" -> "/docs/api/overview"
|
|
21
|
+
*/
|
|
22
|
+
export declare function filePathToRoute(filePath: string, docsRoute: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Generates a list of all doc routes from a folder structure
|
|
25
|
+
* This would typically be called at build time to pre-generate routes
|
|
26
|
+
*/
|
|
27
|
+
export declare function generateDocRoutes(files: string[], docsRoute?: string, docsFolderPath?: string): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Validates a docs route
|
|
30
|
+
*/
|
|
31
|
+
export declare function validateRoute(route: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Create route configuration from ConfigOptions
|
|
34
|
+
*/
|
|
35
|
+
export declare function createRouteConfig(config: ConfigOptions): RouteConfig;
|
|
36
|
+
/**
|
|
37
|
+
* Build a navigation link using the docs route
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildNavLink(slug: string, docsRoute?: string, absolute?: boolean): string;
|
|
40
|
+
/**
|
|
41
|
+
* Extract slug from a docs route
|
|
42
|
+
* Example: "/docs/api/overview" with docsRoute="/docs" -> "api/overview"
|
|
43
|
+
*/
|
|
44
|
+
export declare function extractSlugFromRoute(fullRoute: string, docsRoute: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Check if a route is a documentation route
|
|
47
|
+
*/
|
|
48
|
+
export declare function isDocsRoute(route: string, docsRoute: string): boolean;
|
package/dist/routing.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routing utilities for customizable documentation mount points
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Converts a route slug to a markdown file path
|
|
6
|
+
* Example: slug="api/overview", docsFolderPath="./docs" -> "./docs/api/overview.md"
|
|
7
|
+
*/
|
|
8
|
+
export function slugToFilePath(slug, docsFolderPath) {
|
|
9
|
+
const pathParts = Array.isArray(slug) ? slug : slug.split('/').filter(Boolean);
|
|
10
|
+
const fileName = pathParts.join('/') || 'index';
|
|
11
|
+
return `${docsFolderPath}/${fileName}.md`;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Converts a file path to a route URL
|
|
15
|
+
* Example: filePath="./docs/api/overview.md", docsRoute="/docs" -> "/docs/api/overview"
|
|
16
|
+
*/
|
|
17
|
+
export function filePathToRoute(filePath, docsRoute) {
|
|
18
|
+
// Remove docs folder prefix and .md extension
|
|
19
|
+
let relativePath = filePath.replace(/^\.\//, ''); // Remove leading ./
|
|
20
|
+
relativePath = relativePath.replace(/^docs\//, ''); // Remove docs/ prefix
|
|
21
|
+
relativePath = relativePath.replace(/\.md$/, ''); // Remove .md extension
|
|
22
|
+
relativePath = relativePath.replace(/\/index$/, ''); // Remove /index suffix
|
|
23
|
+
relativePath = relativePath.replace(/^index$/, ''); // Remove index if it's the only part
|
|
24
|
+
if (relativePath === '' || relativePath === 'index') {
|
|
25
|
+
return docsRoute || '/docs';
|
|
26
|
+
}
|
|
27
|
+
return `${docsRoute || '/docs'}/${relativePath}`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Generates a list of all doc routes from a folder structure
|
|
31
|
+
* This would typically be called at build time to pre-generate routes
|
|
32
|
+
*/
|
|
33
|
+
export function generateDocRoutes(files, docsRoute = '/docs', docsFolderPath = './docs') {
|
|
34
|
+
return files.map((file) => filePathToRoute(file, docsRoute)).filter((route) => route !== docsRoute || !route.endsWith('/'));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Validates a docs route
|
|
38
|
+
*/
|
|
39
|
+
export function validateRoute(route) {
|
|
40
|
+
// Must start with /
|
|
41
|
+
if (!route.startsWith('/')) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
// Should not have trailing slash (except root)
|
|
45
|
+
if (route !== '/' && route.endsWith('/')) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
// No double slashes
|
|
49
|
+
if (route.includes('//')) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Create route configuration from ConfigOptions
|
|
56
|
+
*/
|
|
57
|
+
export function createRouteConfig(config) {
|
|
58
|
+
return {
|
|
59
|
+
docsRoute: config.docsRoute || '/docs',
|
|
60
|
+
docsFolderPath: config.docsFolderPath || './docs',
|
|
61
|
+
basePath: config.basePath || '/docs',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Build a navigation link using the docs route
|
|
66
|
+
*/
|
|
67
|
+
export function buildNavLink(slug, docsRoute = '/docs', absolute = false) {
|
|
68
|
+
if (slug === '' || slug === 'index') {
|
|
69
|
+
return docsRoute;
|
|
70
|
+
}
|
|
71
|
+
const cleanSlug = slug.replace(/\.md$/, '').replace(/\/index$/, '');
|
|
72
|
+
const link = `${docsRoute}/${cleanSlug}`.replace(/\/+/g, '/'); // Remove double slashes
|
|
73
|
+
return link;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Extract slug from a docs route
|
|
77
|
+
* Example: "/docs/api/overview" with docsRoute="/docs" -> "api/overview"
|
|
78
|
+
*/
|
|
79
|
+
export function extractSlugFromRoute(fullRoute, docsRoute) {
|
|
80
|
+
if (fullRoute === docsRoute) {
|
|
81
|
+
return '';
|
|
82
|
+
}
|
|
83
|
+
const pattern = `^${docsRoute.replace(/\//g, '\\/')}\\/`;
|
|
84
|
+
const regex = new RegExp(pattern);
|
|
85
|
+
return fullRoute.replace(regex, '');
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Check if a route is a documentation route
|
|
89
|
+
*/
|
|
90
|
+
export function isDocsRoute(route, docsRoute) {
|
|
91
|
+
return route === docsRoute || route.startsWith(`${docsRoute}/`);
|
|
92
|
+
}
|