@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
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NavSection } from '../stores/nav';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
sections?: NavSection[];
|
|
4
|
+
currentPath?: string;
|
|
5
|
+
onNavigate?: (path: string) => void;
|
|
6
|
+
};
|
|
7
|
+
declare const Sidebar: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
type Sidebar = ReturnType<typeof Sidebar>;
|
|
9
|
+
export default Sidebar;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tabs component for switching between content panels
|
|
3
|
+
* Usage:
|
|
4
|
+
* <Tabs items={[{label: 'Tab 1', content: 'Content 1'}]} />
|
|
5
|
+
*/
|
|
6
|
+
interface TabItem {
|
|
7
|
+
label: string;
|
|
8
|
+
content?: string;
|
|
9
|
+
}
|
|
10
|
+
interface Props {
|
|
11
|
+
items: TabItem[];
|
|
12
|
+
defaultIndex?: number;
|
|
13
|
+
}
|
|
14
|
+
declare const Tabs: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type Tabs = ReturnType<typeof Tabs>;
|
|
16
|
+
export default Tabs;
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration schema and types for the documentation system
|
|
3
|
+
*/
|
|
4
|
+
export interface ThemeConfig {
|
|
5
|
+
/** Primary color (hex, rgb, or CSS color name) */
|
|
6
|
+
primary?: string;
|
|
7
|
+
/** Secondary color */
|
|
8
|
+
secondary?: string;
|
|
9
|
+
/** Text color for light mode */
|
|
10
|
+
textLight?: string;
|
|
11
|
+
/** Text color for dark mode */
|
|
12
|
+
textDark?: string;
|
|
13
|
+
/** Background color for light mode */
|
|
14
|
+
bgLight?: string;
|
|
15
|
+
/** Background color for dark mode */
|
|
16
|
+
bgDark?: string;
|
|
17
|
+
/** Font family for body text */
|
|
18
|
+
fontFamily?: string;
|
|
19
|
+
/** Font family for headings */
|
|
20
|
+
headingFont?: string;
|
|
21
|
+
/** Sidebar background color */
|
|
22
|
+
sidebarBg?: string;
|
|
23
|
+
/** Navbar background color */
|
|
24
|
+
navbarBg?: string;
|
|
25
|
+
/** Code block background color */
|
|
26
|
+
codeBg?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface NavItem {
|
|
29
|
+
label: string;
|
|
30
|
+
href?: string;
|
|
31
|
+
children?: NavItem[];
|
|
32
|
+
icon?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface NavSection {
|
|
35
|
+
title: string;
|
|
36
|
+
items: NavItem[];
|
|
37
|
+
}
|
|
38
|
+
export interface NavigationConfig {
|
|
39
|
+
title?: string;
|
|
40
|
+
logo?: string;
|
|
41
|
+
sections?: NavSection[];
|
|
42
|
+
/** Auto-generate navigation from folder structure */
|
|
43
|
+
autoGenerate?: boolean;
|
|
44
|
+
/** Exclude paths from auto-generation */
|
|
45
|
+
excludePaths?: string[];
|
|
46
|
+
}
|
|
47
|
+
export interface ConfigOptions {
|
|
48
|
+
/** Name of the documentation project */
|
|
49
|
+
name?: string;
|
|
50
|
+
/** Mount point for docs (e.g., /docs, /help, /guides) */
|
|
51
|
+
docsRoute?: string;
|
|
52
|
+
/** Path to docs folder relative to project root */
|
|
53
|
+
docsFolderPath?: string;
|
|
54
|
+
/** Base URL path for links */
|
|
55
|
+
basePath?: string;
|
|
56
|
+
/** Theme configuration */
|
|
57
|
+
theme?: ThemeConfig;
|
|
58
|
+
/** Navigation configuration */
|
|
59
|
+
navigation?: NavigationConfig;
|
|
60
|
+
/** Enable search functionality */
|
|
61
|
+
enableSearch?: boolean;
|
|
62
|
+
/** Custom CSS file path */
|
|
63
|
+
customCss?: string;
|
|
64
|
+
/** Layout template name (default, minimal, full-width) */
|
|
65
|
+
layout?: 'default' | 'minimal' | 'full-width';
|
|
66
|
+
/** Enable dark mode toggle */
|
|
67
|
+
darkMode?: boolean;
|
|
68
|
+
/** Default theme (light or dark) */
|
|
69
|
+
defaultTheme?: 'light' | 'dark';
|
|
70
|
+
/** Enable breadcrumbs */
|
|
71
|
+
breadcrumbs?: boolean;
|
|
72
|
+
/** Enable prev/next navigation */
|
|
73
|
+
prevNext?: boolean;
|
|
74
|
+
/** Site title for header */
|
|
75
|
+
siteTitle?: string;
|
|
76
|
+
/** Site description for meta tags */
|
|
77
|
+
siteDescription?: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Default configuration values
|
|
81
|
+
*/
|
|
82
|
+
export declare const DEFAULT_CONFIG: ConfigOptions;
|
|
83
|
+
/**
|
|
84
|
+
* Validates a configuration object
|
|
85
|
+
*/
|
|
86
|
+
export declare function validateConfig(config: unknown): {
|
|
87
|
+
valid: boolean;
|
|
88
|
+
errors: string[];
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Merges user config with defaults
|
|
92
|
+
*/
|
|
93
|
+
export declare function mergeConfig(userConfig: ConfigOptions, defaults?: ConfigOptions): ConfigOptions;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration schema and types for the documentation system
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Default configuration values
|
|
6
|
+
*/
|
|
7
|
+
export const DEFAULT_CONFIG = {
|
|
8
|
+
name: 'Documentation',
|
|
9
|
+
docsRoute: '/docs',
|
|
10
|
+
docsFolderPath: './docs',
|
|
11
|
+
basePath: '/docs',
|
|
12
|
+
enableSearch: true,
|
|
13
|
+
layout: 'default',
|
|
14
|
+
darkMode: true,
|
|
15
|
+
defaultTheme: 'light',
|
|
16
|
+
breadcrumbs: true,
|
|
17
|
+
prevNext: true,
|
|
18
|
+
theme: {
|
|
19
|
+
primary: '#0066cc',
|
|
20
|
+
secondary: '#ff6b6b',
|
|
21
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
22
|
+
headingFont: 'system-ui, -apple-system, sans-serif',
|
|
23
|
+
},
|
|
24
|
+
navigation: {
|
|
25
|
+
autoGenerate: true,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Validates a configuration object
|
|
30
|
+
*/
|
|
31
|
+
export function validateConfig(config) {
|
|
32
|
+
const errors = [];
|
|
33
|
+
if (typeof config !== 'object' || config === null) {
|
|
34
|
+
errors.push('Configuration must be an object');
|
|
35
|
+
return { valid: false, errors };
|
|
36
|
+
}
|
|
37
|
+
const cfg = config;
|
|
38
|
+
// Validate docsRoute
|
|
39
|
+
if (cfg.docsRoute !== undefined) {
|
|
40
|
+
if (typeof cfg.docsRoute !== 'string') {
|
|
41
|
+
errors.push('docsRoute must be a string');
|
|
42
|
+
}
|
|
43
|
+
else if (!cfg.docsRoute.startsWith('/')) {
|
|
44
|
+
errors.push('docsRoute must start with /');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Validate layout
|
|
48
|
+
if (cfg.layout !== undefined) {
|
|
49
|
+
if (!['default', 'minimal', 'full-width'].includes(cfg.layout)) {
|
|
50
|
+
errors.push('layout must be one of: default, minimal, full-width');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Validate defaultTheme
|
|
54
|
+
if (cfg.defaultTheme !== undefined) {
|
|
55
|
+
if (!['light', 'dark'].includes(cfg.defaultTheme)) {
|
|
56
|
+
errors.push('defaultTheme must be either light or dark');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Validate theme config
|
|
60
|
+
if (cfg.theme !== undefined) {
|
|
61
|
+
if (typeof cfg.theme !== 'object' || cfg.theme === null) {
|
|
62
|
+
errors.push('theme must be an object');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Validate navigation config
|
|
66
|
+
if (cfg.navigation !== undefined) {
|
|
67
|
+
if (typeof cfg.navigation !== 'object' || cfg.navigation === null) {
|
|
68
|
+
errors.push('navigation must be an object');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return { valid: errors.length === 0, errors };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Merges user config with defaults
|
|
75
|
+
*/
|
|
76
|
+
export function mergeConfig(userConfig, defaults = DEFAULT_CONFIG) {
|
|
77
|
+
return {
|
|
78
|
+
...defaults,
|
|
79
|
+
...userConfig,
|
|
80
|
+
theme: {
|
|
81
|
+
...defaults.theme,
|
|
82
|
+
...(userConfig.theme || {}),
|
|
83
|
+
},
|
|
84
|
+
navigation: {
|
|
85
|
+
...defaults.navigation,
|
|
86
|
+
...(userConfig.navigation || {}),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loader and manager
|
|
3
|
+
* Loads, validates, and manages the docs system configuration
|
|
4
|
+
*/
|
|
5
|
+
import type { ConfigOptions } from './config';
|
|
6
|
+
export interface ConfigLoadResult {
|
|
7
|
+
success: boolean;
|
|
8
|
+
config: ConfigOptions;
|
|
9
|
+
errors: string[];
|
|
10
|
+
warnings: string[];
|
|
11
|
+
source: 'default' | 'loaded' | 'merged';
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Load configuration from a file
|
|
15
|
+
*/
|
|
16
|
+
export declare function loadConfiguration(configPath?: string): Promise<ConfigLoadResult>;
|
|
17
|
+
export declare function getConfiguration(configPath?: string): Promise<ConfigOptions>;
|
|
18
|
+
/**
|
|
19
|
+
* Clear configuration cache (useful for testing or reloading)
|
|
20
|
+
*/
|
|
21
|
+
export declare function clearConfigCache(): void;
|
|
22
|
+
/**
|
|
23
|
+
* Parse frontmatter from markdown content and merge with config
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseMarkdownFrontmatter(content: string): {
|
|
26
|
+
frontmatter: Record<string, unknown>;
|
|
27
|
+
body: string;
|
|
28
|
+
errors: string[];
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Validate a custom config object
|
|
32
|
+
*/
|
|
33
|
+
export declare function validateCustomConfig(config: unknown): {
|
|
34
|
+
valid: boolean;
|
|
35
|
+
errors: string[];
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Create a configuration from minimal user input
|
|
39
|
+
*/
|
|
40
|
+
export declare function createMinimalConfig(options?: Partial<ConfigOptions>): ConfigOptions;
|
|
41
|
+
/**
|
|
42
|
+
* Export configuration as JSON
|
|
43
|
+
*/
|
|
44
|
+
export declare function exportConfigAsJSON(config: ConfigOptions): string;
|
|
45
|
+
/**
|
|
46
|
+
* Export configuration as YAML
|
|
47
|
+
*/
|
|
48
|
+
export declare function exportConfigAsYAML(config: ConfigOptions): string;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loader and manager
|
|
3
|
+
* Loads, validates, and manages the docs system configuration
|
|
4
|
+
*/
|
|
5
|
+
import { DEFAULT_CONFIG, mergeConfig, validateConfig } from './config';
|
|
6
|
+
import { parseConfigFile, loadConfigFile, parseFrontmatter } from './configParser';
|
|
7
|
+
import { createRouteConfig, validateRoute } from './routing';
|
|
8
|
+
import { validateTheme } from './themeCustomization';
|
|
9
|
+
import { validateNavigation } from './navigationBuilder';
|
|
10
|
+
/**
|
|
11
|
+
* Load configuration from a file
|
|
12
|
+
*/
|
|
13
|
+
export async function loadConfiguration(configPath = '/docs/_config.json') {
|
|
14
|
+
const errors = [];
|
|
15
|
+
const warnings = [];
|
|
16
|
+
let userConfig = {};
|
|
17
|
+
// Try to load the config file
|
|
18
|
+
const result = await loadConfigFile(configPath);
|
|
19
|
+
if (result.errors.length > 0 && !configPath.includes('_config')) {
|
|
20
|
+
// If it's not a standard config file, it might not exist - that's ok
|
|
21
|
+
errors.push(...result.errors);
|
|
22
|
+
}
|
|
23
|
+
else if (result.errors.length > 0) {
|
|
24
|
+
// For standard config file, log errors but continue with defaults
|
|
25
|
+
warnings.push(...result.errors);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
userConfig = result.config;
|
|
29
|
+
}
|
|
30
|
+
// Validate the loaded config
|
|
31
|
+
const validation = validateConfig(userConfig);
|
|
32
|
+
if (!validation.valid) {
|
|
33
|
+
warnings.push(...validation.errors);
|
|
34
|
+
}
|
|
35
|
+
// Validate sub-configs
|
|
36
|
+
if (userConfig.theme) {
|
|
37
|
+
const themeValidation = validateTheme(userConfig.theme);
|
|
38
|
+
if (!themeValidation.valid) {
|
|
39
|
+
warnings.push(`Theme validation errors: ${themeValidation.errors.join(', ')}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (userConfig.navigation) {
|
|
43
|
+
const navValidation = validateNavigation(userConfig.navigation);
|
|
44
|
+
if (!navValidation.valid) {
|
|
45
|
+
warnings.push(`Navigation validation errors: ${navValidation.errors.join(', ')}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Validate route
|
|
49
|
+
if (userConfig.docsRoute && !validateRoute(userConfig.docsRoute)) {
|
|
50
|
+
errors.push(`Invalid docsRoute: ${userConfig.docsRoute}`);
|
|
51
|
+
}
|
|
52
|
+
// Merge with defaults
|
|
53
|
+
const finalConfig = mergeConfig(userConfig, DEFAULT_CONFIG);
|
|
54
|
+
// Validate final config
|
|
55
|
+
const finalValidation = validateConfig(finalConfig);
|
|
56
|
+
if (!finalValidation.valid) {
|
|
57
|
+
errors.push(...finalValidation.errors);
|
|
58
|
+
}
|
|
59
|
+
const success = errors.length === 0;
|
|
60
|
+
return {
|
|
61
|
+
success,
|
|
62
|
+
config: finalConfig,
|
|
63
|
+
errors,
|
|
64
|
+
warnings,
|
|
65
|
+
source: success ? 'loaded' : warnings.length > 0 ? 'merged' : 'default',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Load and cache configuration (for browser environment)
|
|
70
|
+
*/
|
|
71
|
+
let cachedConfig = null;
|
|
72
|
+
let configLoadPromise = null;
|
|
73
|
+
export async function getConfiguration(configPath = '/docs/_config.json') {
|
|
74
|
+
if (cachedConfig) {
|
|
75
|
+
return cachedConfig;
|
|
76
|
+
}
|
|
77
|
+
if (!configLoadPromise) {
|
|
78
|
+
configLoadPromise = loadConfiguration(configPath);
|
|
79
|
+
}
|
|
80
|
+
const result = await configLoadPromise;
|
|
81
|
+
cachedConfig = result.config;
|
|
82
|
+
return result.config;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Clear configuration cache (useful for testing or reloading)
|
|
86
|
+
*/
|
|
87
|
+
export function clearConfigCache() {
|
|
88
|
+
cachedConfig = null;
|
|
89
|
+
configLoadPromise = null;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Parse frontmatter from markdown content and merge with config
|
|
93
|
+
*/
|
|
94
|
+
export function parseMarkdownFrontmatter(content) {
|
|
95
|
+
const result = parseFrontmatter(content);
|
|
96
|
+
if (result.error) {
|
|
97
|
+
return {
|
|
98
|
+
frontmatter: {},
|
|
99
|
+
body: result.body,
|
|
100
|
+
errors: [result.error],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
frontmatter: result.frontmatter || {},
|
|
105
|
+
body: result.body,
|
|
106
|
+
errors: [],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Validate a custom config object
|
|
111
|
+
*/
|
|
112
|
+
export function validateCustomConfig(config) {
|
|
113
|
+
const validation = validateConfig(config);
|
|
114
|
+
if (!validation.valid) {
|
|
115
|
+
return validation;
|
|
116
|
+
}
|
|
117
|
+
const cfg = config;
|
|
118
|
+
const errors = [];
|
|
119
|
+
// Additional validations
|
|
120
|
+
if (cfg.theme) {
|
|
121
|
+
const themeValidation = validateTheme(cfg.theme);
|
|
122
|
+
if (!themeValidation.valid) {
|
|
123
|
+
errors.push(`Theme: ${themeValidation.errors.join(', ')}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (cfg.navigation) {
|
|
127
|
+
const navValidation = validateNavigation(cfg.navigation);
|
|
128
|
+
if (!navValidation.valid) {
|
|
129
|
+
errors.push(`Navigation: ${navValidation.errors.join(', ')}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (cfg.docsRoute && !validateRoute(cfg.docsRoute)) {
|
|
133
|
+
errors.push(`Invalid docsRoute: ${cfg.docsRoute}`);
|
|
134
|
+
}
|
|
135
|
+
return { valid: errors.length === 0, errors };
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Create a configuration from minimal user input
|
|
139
|
+
*/
|
|
140
|
+
export function createMinimalConfig(options = {}) {
|
|
141
|
+
return mergeConfig(options, DEFAULT_CONFIG);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Export configuration as JSON
|
|
145
|
+
*/
|
|
146
|
+
export function exportConfigAsJSON(config) {
|
|
147
|
+
return JSON.stringify(config, null, 2);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Export configuration as YAML
|
|
151
|
+
*/
|
|
152
|
+
export function exportConfigAsYAML(config) {
|
|
153
|
+
const lines = [];
|
|
154
|
+
function addValue(key, value, indent = 0) {
|
|
155
|
+
const indentStr = ' '.repeat(indent);
|
|
156
|
+
if (value === null || value === undefined) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (typeof value === 'string') {
|
|
160
|
+
lines.push(`${indentStr}${key}: ${value}`);
|
|
161
|
+
}
|
|
162
|
+
else if (typeof value === 'boolean' || typeof value === 'number') {
|
|
163
|
+
lines.push(`${indentStr}${key}: ${value}`);
|
|
164
|
+
}
|
|
165
|
+
else if (Array.isArray(value)) {
|
|
166
|
+
lines.push(`${indentStr}${key}:`);
|
|
167
|
+
value.forEach((item, idx) => {
|
|
168
|
+
if (typeof item === 'object') {
|
|
169
|
+
addValue(`- `, item, indent + 1);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
lines.push(`${indentStr} - ${item}`);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
else if (typeof value === 'object') {
|
|
177
|
+
lines.push(`${indentStr}${key}:`);
|
|
178
|
+
Object.entries(value).forEach(([k, v]) => {
|
|
179
|
+
addValue(k, v, indent + 1);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
Object.entries(config).forEach(([key, value]) => {
|
|
184
|
+
addValue(key, value);
|
|
185
|
+
});
|
|
186
|
+
return lines.join('\n');
|
|
187
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config file parser supporting JSON and YAML formats
|
|
3
|
+
*/
|
|
4
|
+
import type { ConfigOptions } from './config';
|
|
5
|
+
export interface ParseResult {
|
|
6
|
+
config: ConfigOptions;
|
|
7
|
+
errors: string[];
|
|
8
|
+
source: 'json' | 'yaml' | 'unknown';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parse frontmatter from markdown content
|
|
12
|
+
* Supports both JSON and YAML frontmatter
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseFrontmatter(content: string): {
|
|
15
|
+
frontmatter: Record<string, unknown> | null;
|
|
16
|
+
body: string;
|
|
17
|
+
error?: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Parse config file content (JSON or YAML)
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseConfigFile(content: string, filename?: string): ParseResult;
|
|
23
|
+
/**
|
|
24
|
+
* Load and parse a config file from a file path (browser-safe)
|
|
25
|
+
* In a browser environment, use fetch; in Node, use fs
|
|
26
|
+
*/
|
|
27
|
+
export declare function loadConfigFile(filePath: string): Promise<ParseResult>;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config file parser supporting JSON and YAML formats
|
|
3
|
+
*/
|
|
4
|
+
import { validateConfig } from './config';
|
|
5
|
+
/**
|
|
6
|
+
* Parse JSON config file
|
|
7
|
+
*/
|
|
8
|
+
function parseJSON(content) {
|
|
9
|
+
try {
|
|
10
|
+
return { data: JSON.parse(content) };
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
return {
|
|
14
|
+
data: null,
|
|
15
|
+
error: `Invalid JSON: ${err instanceof Error ? err.message : 'Unknown error'}`,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse YAML config file (simplified parser)
|
|
21
|
+
* Handles basic YAML key-value pairs and nested objects
|
|
22
|
+
*/
|
|
23
|
+
function parseYAML(content) {
|
|
24
|
+
try {
|
|
25
|
+
const result = {};
|
|
26
|
+
const lines = content.split('\n');
|
|
27
|
+
let currentObject = null;
|
|
28
|
+
let currentKey = null;
|
|
29
|
+
let indentStack = [];
|
|
30
|
+
for (let i = 0; i < lines.length; i++) {
|
|
31
|
+
const line = lines[i];
|
|
32
|
+
// Skip empty lines and comments
|
|
33
|
+
if (!line.trim() || line.trim().startsWith('#')) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
// Calculate indentation
|
|
37
|
+
const indent = line.length - line.trimLeft().length;
|
|
38
|
+
const trimmedLine = line.trim();
|
|
39
|
+
// Pop stack if we've decreased indentation
|
|
40
|
+
while (indentStack.length > 0 && indentStack[indentStack.length - 1].indent >= indent) {
|
|
41
|
+
indentStack.pop();
|
|
42
|
+
}
|
|
43
|
+
// Parse key: value
|
|
44
|
+
if (trimmedLine.includes(':')) {
|
|
45
|
+
const [key, ...valueParts] = trimmedLine.split(':');
|
|
46
|
+
const trimmedKey = key.trim();
|
|
47
|
+
const value = valueParts.join(':').trim();
|
|
48
|
+
// Determine parent object
|
|
49
|
+
const parent = indentStack.length > 0 ? indentStack[indentStack.length - 1].obj : result;
|
|
50
|
+
// Parse value
|
|
51
|
+
let parsedValue = value;
|
|
52
|
+
if (value === '') {
|
|
53
|
+
// Empty value - next items might be children
|
|
54
|
+
parsedValue = {};
|
|
55
|
+
indentStack.push({ indent, key: trimmedKey, obj: parsedValue });
|
|
56
|
+
}
|
|
57
|
+
else if (value === 'true') {
|
|
58
|
+
parsedValue = true;
|
|
59
|
+
}
|
|
60
|
+
else if (value === 'false') {
|
|
61
|
+
parsedValue = false;
|
|
62
|
+
}
|
|
63
|
+
else if (/^\d+$/.test(value)) {
|
|
64
|
+
parsedValue = parseInt(value, 10);
|
|
65
|
+
}
|
|
66
|
+
else if (/^\d+\.\d+$/.test(value)) {
|
|
67
|
+
parsedValue = parseFloat(value);
|
|
68
|
+
}
|
|
69
|
+
else if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
70
|
+
parsedValue = value.slice(1, -1);
|
|
71
|
+
}
|
|
72
|
+
else if (value.startsWith('[') && value.endsWith(']')) {
|
|
73
|
+
try {
|
|
74
|
+
parsedValue = JSON.parse(value);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
parsedValue = value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else if (value.startsWith('{') && value.endsWith('}')) {
|
|
81
|
+
try {
|
|
82
|
+
parsedValue = JSON.parse(value);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
parsedValue = value;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
parent[trimmedKey] = parsedValue;
|
|
89
|
+
currentKey = trimmedKey;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { data: result };
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
return {
|
|
96
|
+
data: null,
|
|
97
|
+
error: `Invalid YAML: ${err instanceof Error ? err.message : 'Unknown error'}`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Parse frontmatter from markdown content
|
|
103
|
+
* Supports both JSON and YAML frontmatter
|
|
104
|
+
*/
|
|
105
|
+
export function parseFrontmatter(content) {
|
|
106
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
107
|
+
if (!frontmatterMatch) {
|
|
108
|
+
return { frontmatter: null, body: content };
|
|
109
|
+
}
|
|
110
|
+
const frontmatterContent = frontmatterMatch[1];
|
|
111
|
+
const body = frontmatterMatch[2];
|
|
112
|
+
// Try JSON first
|
|
113
|
+
let parsed = parseJSON(frontmatterContent);
|
|
114
|
+
// If JSON fails, try YAML
|
|
115
|
+
if (parsed.error) {
|
|
116
|
+
parsed = parseYAML(frontmatterContent);
|
|
117
|
+
}
|
|
118
|
+
if (parsed.error) {
|
|
119
|
+
return {
|
|
120
|
+
frontmatter: null,
|
|
121
|
+
body: content,
|
|
122
|
+
error: `Failed to parse frontmatter: ${parsed.error}`,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
frontmatter: parsed.data,
|
|
127
|
+
body,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Detect file format from content or filename
|
|
132
|
+
*/
|
|
133
|
+
function detectFormat(content, filename) {
|
|
134
|
+
// Check filename extension
|
|
135
|
+
if (filename.endsWith('.json'))
|
|
136
|
+
return 'json';
|
|
137
|
+
if (filename.endsWith('.yaml') || filename.endsWith('.yml'))
|
|
138
|
+
return 'yaml';
|
|
139
|
+
// Try to detect by content
|
|
140
|
+
const trimmed = content.trim();
|
|
141
|
+
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
|
142
|
+
return 'json';
|
|
143
|
+
}
|
|
144
|
+
return 'yaml';
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Parse config file content (JSON or YAML)
|
|
148
|
+
*/
|
|
149
|
+
export function parseConfigFile(content, filename = 'config.json') {
|
|
150
|
+
const format = detectFormat(content, filename);
|
|
151
|
+
let parsed;
|
|
152
|
+
if (format === 'json') {
|
|
153
|
+
parsed = parseJSON(content);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
parsed = parseYAML(content);
|
|
157
|
+
}
|
|
158
|
+
if (parsed.error) {
|
|
159
|
+
return {
|
|
160
|
+
config: {},
|
|
161
|
+
errors: [parsed.error],
|
|
162
|
+
source: format,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const validation = validateConfig(parsed.data);
|
|
166
|
+
return {
|
|
167
|
+
config: parsed.data || {},
|
|
168
|
+
errors: validation.errors,
|
|
169
|
+
source: format,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Load and parse a config file from a file path (browser-safe)
|
|
174
|
+
* In a browser environment, use fetch; in Node, use fs
|
|
175
|
+
*/
|
|
176
|
+
export async function loadConfigFile(filePath) {
|
|
177
|
+
try {
|
|
178
|
+
let content;
|
|
179
|
+
if (typeof window !== 'undefined') {
|
|
180
|
+
// Browser environment - use fetch
|
|
181
|
+
const response = await fetch(filePath);
|
|
182
|
+
if (!response.ok) {
|
|
183
|
+
return {
|
|
184
|
+
config: {},
|
|
185
|
+
errors: [`Failed to load config file: ${response.statusText}`],
|
|
186
|
+
source: 'unknown',
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
content = await response.text();
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
// Node environment - would need fs module
|
|
193
|
+
return {
|
|
194
|
+
config: {},
|
|
195
|
+
errors: ['Cannot load config file in this environment'],
|
|
196
|
+
source: 'unknown',
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return parseConfigFile(content, filePath);
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
return {
|
|
203
|
+
config: {},
|
|
204
|
+
errors: [`Error loading config file: ${err instanceof Error ? err.message : 'Unknown error'}`],
|
|
205
|
+
source: 'unknown',
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|