@fuzdev/fuz_ui 0.169.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/LICENSE +21 -0
- package/README.md +93 -0
- package/dist/Alert.svelte +108 -0
- package/dist/Alert.svelte.d.ts +16 -0
- package/dist/Alert.svelte.d.ts.map +1 -0
- package/dist/ApiDeclarationList.svelte +35 -0
- package/dist/ApiDeclarationList.svelte.d.ts +9 -0
- package/dist/ApiDeclarationList.svelte.d.ts.map +1 -0
- package/dist/ApiIndex.svelte +65 -0
- package/dist/ApiIndex.svelte.d.ts +23 -0
- package/dist/ApiIndex.svelte.d.ts.map +1 -0
- package/dist/ApiModule.svelte +124 -0
- package/dist/ApiModule.svelte.d.ts +22 -0
- package/dist/ApiModule.svelte.d.ts.map +1 -0
- package/dist/Breadcrumb.svelte +83 -0
- package/dist/Breadcrumb.svelte.d.ts +23 -0
- package/dist/Breadcrumb.svelte.d.ts.map +1 -0
- package/dist/Card.svelte +157 -0
- package/dist/Card.svelte.d.ts +13 -0
- package/dist/Card.svelte.d.ts.map +1 -0
- package/dist/ColorSchemeInput.svelte +65 -0
- package/dist/ColorSchemeInput.svelte.d.ts +11 -0
- package/dist/ColorSchemeInput.svelte.d.ts.map +1 -0
- package/dist/Contextmenu.svelte +30 -0
- package/dist/Contextmenu.svelte.d.ts +32 -0
- package/dist/Contextmenu.svelte.d.ts.map +1 -0
- package/dist/ContextmenuEntry.svelte +74 -0
- package/dist/ContextmenuEntry.svelte.d.ts +12 -0
- package/dist/ContextmenuEntry.svelte.d.ts.map +1 -0
- package/dist/ContextmenuLinkEntry.svelte +112 -0
- package/dist/ContextmenuLinkEntry.svelte.d.ts +12 -0
- package/dist/ContextmenuLinkEntry.svelte.d.ts.map +1 -0
- package/dist/ContextmenuRoot.svelte +372 -0
- package/dist/ContextmenuRoot.svelte.d.ts +71 -0
- package/dist/ContextmenuRoot.svelte.d.ts.map +1 -0
- package/dist/ContextmenuRootForSafariCompatibility.svelte +541 -0
- package/dist/ContextmenuRootForSafariCompatibility.svelte.d.ts +79 -0
- package/dist/ContextmenuRootForSafariCompatibility.svelte.d.ts.map +1 -0
- package/dist/ContextmenuSeparator.svelte +16 -0
- package/dist/ContextmenuSeparator.svelte.d.ts +4 -0
- package/dist/ContextmenuSeparator.svelte.d.ts.map +1 -0
- package/dist/ContextmenuSubmenu.svelte +116 -0
- package/dist/ContextmenuSubmenu.svelte.d.ts +10 -0
- package/dist/ContextmenuSubmenu.svelte.d.ts.map +1 -0
- package/dist/ContextmenuTextEntry.svelte +21 -0
- package/dist/ContextmenuTextEntry.svelte.d.ts +10 -0
- package/dist/ContextmenuTextEntry.svelte.d.ts.map +1 -0
- package/dist/CopyToClipboard.svelte +81 -0
- package/dist/CopyToClipboard.svelte.d.ts +18 -0
- package/dist/CopyToClipboard.svelte.d.ts.map +1 -0
- package/dist/DeclarationDetail.svelte +340 -0
- package/dist/DeclarationDetail.svelte.d.ts +8 -0
- package/dist/DeclarationDetail.svelte.d.ts.map +1 -0
- package/dist/DeclarationLink.svelte +50 -0
- package/dist/DeclarationLink.svelte.d.ts +8 -0
- package/dist/DeclarationLink.svelte.d.ts.map +1 -0
- package/dist/Details.svelte +51 -0
- package/dist/Details.svelte.d.ts +20 -0
- package/dist/Details.svelte.d.ts.map +1 -0
- package/dist/Dialog.svelte +217 -0
- package/dist/Dialog.svelte.d.ts +30 -0
- package/dist/Dialog.svelte.d.ts.map +1 -0
- package/dist/Dialogs.svelte +28 -0
- package/dist/Dialogs.svelte.d.ts +11 -0
- package/dist/Dialogs.svelte.d.ts.map +1 -0
- package/dist/Docs.svelte +179 -0
- package/dist/Docs.svelte.d.ts +13 -0
- package/dist/Docs.svelte.d.ts.map +1 -0
- package/dist/DocsContent.svelte +40 -0
- package/dist/DocsContent.svelte.d.ts +14 -0
- package/dist/DocsContent.svelte.d.ts.map +1 -0
- package/dist/DocsFooter.svelte +64 -0
- package/dist/DocsFooter.svelte.d.ts +15 -0
- package/dist/DocsFooter.svelte.d.ts.map +1 -0
- package/dist/DocsLink.svelte +41 -0
- package/dist/DocsLink.svelte.d.ts +12 -0
- package/dist/DocsLink.svelte.d.ts.map +1 -0
- package/dist/DocsList.svelte +44 -0
- package/dist/DocsList.svelte.d.ts +11 -0
- package/dist/DocsList.svelte.d.ts.map +1 -0
- package/dist/DocsMenu.svelte +55 -0
- package/dist/DocsMenu.svelte.d.ts +11 -0
- package/dist/DocsMenu.svelte.d.ts.map +1 -0
- package/dist/DocsMenuHeader.svelte +15 -0
- package/dist/DocsMenuHeader.svelte.d.ts +9 -0
- package/dist/DocsMenuHeader.svelte.d.ts.map +1 -0
- package/dist/DocsModulesList.svelte +32 -0
- package/dist/DocsModulesList.svelte.d.ts +7 -0
- package/dist/DocsModulesList.svelte.d.ts.map +1 -0
- package/dist/DocsPageLinks.svelte +61 -0
- package/dist/DocsPageLinks.svelte.d.ts +8 -0
- package/dist/DocsPageLinks.svelte.d.ts.map +1 -0
- package/dist/DocsPrimaryNav.svelte +93 -0
- package/dist/DocsPrimaryNav.svelte.d.ts +11 -0
- package/dist/DocsPrimaryNav.svelte.d.ts.map +1 -0
- package/dist/DocsSearch.svelte +48 -0
- package/dist/DocsSearch.svelte.d.ts +11 -0
- package/dist/DocsSearch.svelte.d.ts.map +1 -0
- package/dist/DocsSecondaryNav.svelte +63 -0
- package/dist/DocsSecondaryNav.svelte.d.ts +9 -0
- package/dist/DocsSecondaryNav.svelte.d.ts.map +1 -0
- package/dist/DocsTertiaryNav.svelte +118 -0
- package/dist/DocsTertiaryNav.svelte.d.ts +10 -0
- package/dist/DocsTertiaryNav.svelte.d.ts.map +1 -0
- package/dist/EcosystemLinks.svelte +53 -0
- package/dist/EcosystemLinks.svelte.d.ts +7 -0
- package/dist/EcosystemLinks.svelte.d.ts.map +1 -0
- package/dist/EcosystemLinksPanel.svelte +22 -0
- package/dist/EcosystemLinksPanel.svelte.d.ts +8 -0
- package/dist/EcosystemLinksPanel.svelte.d.ts.map +1 -0
- package/dist/GithubLink.svelte +75 -0
- package/dist/GithubLink.svelte.d.ts +14 -0
- package/dist/GithubLink.svelte.d.ts.map +1 -0
- package/dist/Glyph.svelte +28 -0
- package/dist/Glyph.svelte.d.ts +9 -0
- package/dist/Glyph.svelte.d.ts.map +1 -0
- package/dist/Hashlink.svelte +41 -0
- package/dist/Hashlink.svelte.d.ts +8 -0
- package/dist/Hashlink.svelte.d.ts.map +1 -0
- package/dist/HiddenPersonalLinks.svelte +6 -0
- package/dist/HiddenPersonalLinks.svelte.d.ts +27 -0
- package/dist/HiddenPersonalLinks.svelte.d.ts.map +1 -0
- package/dist/HueInput.svelte +127 -0
- package/dist/HueInput.svelte.d.ts +11 -0
- package/dist/HueInput.svelte.d.ts.map +1 -0
- package/dist/ImgOrSvg.svelte +58 -0
- package/dist/ImgOrSvg.svelte.d.ts +25 -0
- package/dist/ImgOrSvg.svelte.d.ts.map +1 -0
- package/dist/LibraryDetail.svelte +297 -0
- package/dist/LibraryDetail.svelte.d.ts +15 -0
- package/dist/LibraryDetail.svelte.d.ts.map +1 -0
- package/dist/LibrarySummary.svelte +151 -0
- package/dist/LibrarySummary.svelte.d.ts +16 -0
- package/dist/LibrarySummary.svelte.d.ts.map +1 -0
- package/dist/MdnLink.svelte +40 -0
- package/dist/MdnLink.svelte.d.ts +8 -0
- package/dist/MdnLink.svelte.d.ts.map +1 -0
- package/dist/Mdz.svelte +30 -0
- package/dist/Mdz.svelte.d.ts +10 -0
- package/dist/Mdz.svelte.d.ts.map +1 -0
- package/dist/MdzNodeView.svelte +93 -0
- package/dist/MdzNodeView.svelte.d.ts +9 -0
- package/dist/MdzNodeView.svelte.d.ts.map +1 -0
- package/dist/ModuleLink.svelte +48 -0
- package/dist/ModuleLink.svelte.d.ts +8 -0
- package/dist/ModuleLink.svelte.d.ts.map +1 -0
- package/dist/PasteFromClipboard.svelte +35 -0
- package/dist/PasteFromClipboard.svelte.d.ts +9 -0
- package/dist/PasteFromClipboard.svelte.d.ts.map +1 -0
- package/dist/PendingAnimation.svelte +62 -0
- package/dist/PendingAnimation.svelte.d.ts +13 -0
- package/dist/PendingAnimation.svelte.d.ts.map +1 -0
- package/dist/PendingButton.svelte +75 -0
- package/dist/PendingButton.svelte.d.ts +17 -0
- package/dist/PendingButton.svelte.d.ts.map +1 -0
- package/dist/ProjectLinks.svelte +54 -0
- package/dist/ProjectLinks.svelte.d.ts +19 -0
- package/dist/ProjectLinks.svelte.d.ts.map +1 -0
- package/dist/Redirect.svelte +44 -0
- package/dist/Redirect.svelte.d.ts +23 -0
- package/dist/Redirect.svelte.d.ts.map +1 -0
- package/dist/Spiders.svelte +57 -0
- package/dist/Spiders.svelte.d.ts +9 -0
- package/dist/Spiders.svelte.d.ts.map +1 -0
- package/dist/Svg.svelte +99 -0
- package/dist/Svg.svelte.d.ts +54 -0
- package/dist/Svg.svelte.d.ts.map +1 -0
- package/dist/Teleport.svelte +48 -0
- package/dist/Teleport.svelte.d.ts +15 -0
- package/dist/Teleport.svelte.d.ts.map +1 -0
- package/dist/ThemeInput.svelte +75 -0
- package/dist/ThemeInput.svelte.d.ts +15 -0
- package/dist/ThemeInput.svelte.d.ts.map +1 -0
- package/dist/Themed.svelte +101 -0
- package/dist/Themed.svelte.d.ts +24 -0
- package/dist/Themed.svelte.d.ts.map +1 -0
- package/dist/TomeContent.svelte +67 -0
- package/dist/TomeContent.svelte.d.ts +12 -0
- package/dist/TomeContent.svelte.d.ts.map +1 -0
- package/dist/TomeHeader.svelte +56 -0
- package/dist/TomeHeader.svelte.d.ts +4 -0
- package/dist/TomeHeader.svelte.d.ts.map +1 -0
- package/dist/TomeLink.svelte +29 -0
- package/dist/TomeLink.svelte.d.ts +10 -0
- package/dist/TomeLink.svelte.d.ts.map +1 -0
- package/dist/TomeSection.svelte +65 -0
- package/dist/TomeSection.svelte.d.ts +24 -0
- package/dist/TomeSection.svelte.d.ts.map +1 -0
- package/dist/TomeSectionHeader.svelte +90 -0
- package/dist/TomeSectionHeader.svelte.d.ts +13 -0
- package/dist/TomeSectionHeader.svelte.d.ts.map +1 -0
- package/dist/TypeLink.svelte +19 -0
- package/dist/TypeLink.svelte.d.ts +7 -0
- package/dist/TypeLink.svelte.d.ts.map +1 -0
- package/dist/alert.d.ts +7 -0
- package/dist/alert.d.ts.map +1 -0
- package/dist/alert.js +6 -0
- package/dist/api_search.svelte.d.ts +16 -0
- package/dist/api_search.svelte.d.ts.map +1 -0
- package/dist/api_search.svelte.js +61 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +3 -0
- package/dist/context_helpers.d.ts +17 -0
- package/dist/context_helpers.d.ts.map +1 -0
- package/dist/context_helpers.js +19 -0
- package/dist/contextmenu_helpers.d.ts +16 -0
- package/dist/contextmenu_helpers.d.ts.map +1 -0
- package/dist/contextmenu_helpers.js +39 -0
- package/dist/contextmenu_state.svelte.d.ts +152 -0
- package/dist/contextmenu_state.svelte.d.ts.map +1 -0
- package/dist/contextmenu_state.svelte.js +424 -0
- package/dist/csp.d.ts +160 -0
- package/dist/csp.d.ts.map +1 -0
- package/dist/csp.js +354 -0
- package/dist/csp_of_ryanatkn.d.ts +6 -0
- package/dist/csp_of_ryanatkn.d.ts.map +1 -0
- package/dist/csp_of_ryanatkn.js +14 -0
- package/dist/declaration.svelte.d.ts +84 -0
- package/dist/declaration.svelte.d.ts.map +1 -0
- package/dist/declaration.svelte.js +66 -0
- package/dist/declaration_contextmenu.d.ts +4 -0
- package/dist/declaration_contextmenu.d.ts.map +1 -0
- package/dist/declaration_contextmenu.js +14 -0
- package/dist/dialog.d.ts +24 -0
- package/dist/dialog.d.ts.map +1 -0
- package/dist/dialog.js +12 -0
- package/dist/dimensions.svelte.d.ts +5 -0
- package/dist/dimensions.svelte.d.ts.map +1 -0
- package/dist/dimensions.svelte.js +4 -0
- package/dist/docs_helpers.svelte.d.ts +48 -0
- package/dist/docs_helpers.svelte.d.ts.map +1 -0
- package/dist/docs_helpers.svelte.js +99 -0
- package/dist/helpers.d.ts +2 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +16 -0
- package/dist/intersect.svelte.d.ts +47 -0
- package/dist/intersect.svelte.d.ts.map +1 -0
- package/dist/intersect.svelte.js +92 -0
- package/dist/library.svelte.d.ts +197 -0
- package/dist/library.svelte.d.ts.map +1 -0
- package/dist/library.svelte.js +130 -0
- package/dist/library_gen.d.ts +34 -0
- package/dist/library_gen.d.ts.map +1 -0
- package/dist/library_gen.js +123 -0
- package/dist/library_gen_helpers.d.ts +85 -0
- package/dist/library_gen_helpers.d.ts.map +1 -0
- package/dist/library_gen_helpers.js +188 -0
- package/dist/library_helpers.d.ts +54 -0
- package/dist/library_helpers.d.ts.map +1 -0
- package/dist/library_helpers.js +102 -0
- package/dist/logos.d.ts +134 -0
- package/dist/logos.d.ts.map +1 -0
- package/dist/logos.js +281 -0
- package/dist/mdz.d.ts +106 -0
- package/dist/mdz.d.ts.map +1 -0
- package/dist/mdz.js +1481 -0
- package/dist/mdz_components.d.ts +37 -0
- package/dist/mdz_components.d.ts.map +1 -0
- package/dist/mdz_components.js +12 -0
- package/dist/module.svelte.d.ts +47 -0
- package/dist/module.svelte.d.ts.map +1 -0
- package/dist/module.svelte.js +56 -0
- package/dist/module_contextmenu.d.ts +4 -0
- package/dist/module_contextmenu.d.ts.map +1 -0
- package/dist/module_contextmenu.js +14 -0
- package/dist/module_helpers.d.ts +69 -0
- package/dist/module_helpers.d.ts.map +1 -0
- package/dist/module_helpers.js +87 -0
- package/dist/rune_helpers.svelte.d.ts +6 -0
- package/dist/rune_helpers.svelte.d.ts.map +1 -0
- package/dist/rune_helpers.svelte.js +10 -0
- package/dist/storage.d.ts +13 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +43 -0
- package/dist/svelte_helpers.d.ts +37 -0
- package/dist/svelte_helpers.d.ts.map +1 -0
- package/dist/svelte_helpers.js +245 -0
- package/dist/themer.svelte.d.ts +24 -0
- package/dist/themer.svelte.d.ts.map +1 -0
- package/dist/themer.svelte.js +43 -0
- package/dist/tome.d.ts +80 -0
- package/dist/tome.d.ts.map +1 -0
- package/dist/tome.js +27 -0
- package/dist/ts_helpers.d.ts +110 -0
- package/dist/ts_helpers.d.ts.map +1 -0
- package/dist/ts_helpers.js +533 -0
- package/dist/tsdoc_helpers.d.ts +98 -0
- package/dist/tsdoc_helpers.d.ts.map +1 -0
- package/dist/tsdoc_helpers.js +221 -0
- package/package.json +128 -0
- package/src/lib/alert.ts +14 -0
- package/src/lib/api_search.svelte.ts +85 -0
- package/src/lib/constants.ts +3 -0
- package/src/lib/context_helpers.ts +47 -0
- package/src/lib/contextmenu_helpers.ts +63 -0
- package/src/lib/contextmenu_state.svelte.ts +515 -0
- package/src/lib/csp.ts +576 -0
- package/src/lib/csp_of_ryanatkn.ts +16 -0
- package/src/lib/declaration.svelte.ts +102 -0
- package/src/lib/declaration_contextmenu.ts +22 -0
- package/src/lib/dialog.ts +35 -0
- package/src/lib/dimensions.svelte.ts +4 -0
- package/src/lib/docs_helpers.svelte.ts +149 -0
- package/src/lib/helpers.ts +10 -0
- package/src/lib/intersect.svelte.ts +152 -0
- package/src/lib/library.svelte.ts +162 -0
- package/src/lib/library_gen.ts +160 -0
- package/src/lib/library_gen_helpers.ts +262 -0
- package/src/lib/library_helpers.ts +123 -0
- package/src/lib/logos.ts +302 -0
- package/src/lib/mdz.ts +1819 -0
- package/src/lib/mdz_components.ts +34 -0
- package/src/lib/module.svelte.ts +78 -0
- package/src/lib/module_contextmenu.ts +20 -0
- package/src/lib/module_helpers.ts +113 -0
- package/src/lib/rune_helpers.svelte.ts +10 -0
- package/src/lib/storage.ts +48 -0
- package/src/lib/svelte_helpers.ts +303 -0
- package/src/lib/themer.svelte.ts +68 -0
- package/src/lib/tome.ts +38 -0
- package/src/lib/ts_helpers.ts +662 -0
- package/src/lib/tsdoc_helpers.ts +259 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type {Declaration} from './declaration.svelte.js';
|
|
2
|
+
import type {ContextmenuParams} from './contextmenu_state.svelte.js';
|
|
3
|
+
|
|
4
|
+
// TODO @many expand contextmenus for docs
|
|
5
|
+
|
|
6
|
+
export const create_declaration_contextmenu = (
|
|
7
|
+
declaration: Declaration,
|
|
8
|
+
): Array<ContextmenuParams> => {
|
|
9
|
+
const entries: Array<ContextmenuParams> = [];
|
|
10
|
+
|
|
11
|
+
// View source on GitHub
|
|
12
|
+
if (declaration.url_github) {
|
|
13
|
+
entries.push({
|
|
14
|
+
snippet: 'link',
|
|
15
|
+
props: {
|
|
16
|
+
href: declaration.url_github,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return entries;
|
|
22
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type {ComponentProps, Component} from 'svelte';
|
|
2
|
+
|
|
3
|
+
import type Dialog from './Dialog.svelte';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* This helper function is needed to construct `DialogParams` with type safety.
|
|
7
|
+
* It uses TypeScript's inferred generics for functions,
|
|
8
|
+
* which do not work for plain objects as of v5.0.4.
|
|
9
|
+
* * `ContextmenuParams` uses a similar strategy.
|
|
10
|
+
*/
|
|
11
|
+
export const to_dialog_params = <T extends Component<any>>(
|
|
12
|
+
Component: T,
|
|
13
|
+
props: ComponentProps<T>,
|
|
14
|
+
dialog_props?: Partial<ComponentProps<typeof Dialog>>,
|
|
15
|
+
): DialogParams<T> => ({
|
|
16
|
+
Component,
|
|
17
|
+
props,
|
|
18
|
+
dialog_props,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* This pattern is based on:
|
|
23
|
+
* https://github.com/ivanhofer/sveltekit-typescript-showcase/blob/main/src/01-props/09-svelte-component/Component.svelte
|
|
24
|
+
* The main limitation is that the generic cannot be inferred automatically,
|
|
25
|
+
* so we use `to_dialog_params` to construct instances in most cases.
|
|
26
|
+
* Definining `DialogParams` with no concrete `T` lacks typechecking for `props`.
|
|
27
|
+
*/
|
|
28
|
+
export interface DialogParams<T extends Component<any> = Component<any>> {
|
|
29
|
+
Component: T;
|
|
30
|
+
props: ComponentProps<T>;
|
|
31
|
+
dialog_props?: Partial<ComponentProps<typeof Dialog>> | undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type DialogLayout = 'centered' | 'page';
|
|
35
|
+
export const dialog_layouts: Array<DialogLayout> = ['centered', 'page'];
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {resolve} from '$app/paths';
|
|
2
|
+
import {SvelteMap, SvelteSet} from 'svelte/reactivity';
|
|
3
|
+
import {ensure_end, ensure_start} from '@fuzdev/fuz_util/string.js';
|
|
4
|
+
|
|
5
|
+
import {create_context} from './context_helpers.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Convert a string to a URL-safe fragment identifier, preserving case for API declarations.
|
|
9
|
+
* Only transforms spaces and special characters, keeping valid identifier characters intact.
|
|
10
|
+
* Used for hash anchors in documentation.
|
|
11
|
+
* @param str - The string to convert to a fragment
|
|
12
|
+
* @returns A URL-safe fragment identifier
|
|
13
|
+
*/
|
|
14
|
+
export const docs_slugify = (str: string): string => {
|
|
15
|
+
return (
|
|
16
|
+
str
|
|
17
|
+
.trim()
|
|
18
|
+
// Replace spaces and multiple hyphens with single hyphen
|
|
19
|
+
.replace(/\s+/g, '-')
|
|
20
|
+
.replace(/-+/g, '-')
|
|
21
|
+
// Remove invalid characters (keep letters, digits, _, $, -)
|
|
22
|
+
.replace(/[^\w$-]/g, '')
|
|
23
|
+
// Remove leading/trailing hyphens
|
|
24
|
+
.replace(/^-+|-+$/g, '')
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const DOCS_PATH_DEFAULT = '/docs';
|
|
29
|
+
export const DOCS_PATH = resolve('/docs');
|
|
30
|
+
export const DOCS_API_PATH = DOCS_PATH + '/api';
|
|
31
|
+
|
|
32
|
+
export const to_docs_path_info = (
|
|
33
|
+
path_slug: string,
|
|
34
|
+
pathname: string,
|
|
35
|
+
root_path = DOCS_PATH_DEFAULT,
|
|
36
|
+
): {path: string; path_is_selected: boolean; path_segment: string | undefined} => {
|
|
37
|
+
const path_segment = pathname.split('/').at(-1);
|
|
38
|
+
const path = resolve((ensure_end(ensure_start(root_path, '/'), '/') + path_slug) as any);
|
|
39
|
+
const path_is_selected = path_segment === path_slug; // messy but works
|
|
40
|
+
return {path, path_is_selected, path_segment};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const docs_links_context = create_context<DocsLinks>();
|
|
44
|
+
|
|
45
|
+
export type DocsLinkTag = 'h2' | 'h3' | 'h4';
|
|
46
|
+
|
|
47
|
+
export interface DocsLinkInfo {
|
|
48
|
+
id: string;
|
|
49
|
+
text: string;
|
|
50
|
+
fragment: string;
|
|
51
|
+
tag: DocsLinkTag | undefined;
|
|
52
|
+
depth: number;
|
|
53
|
+
order: number;
|
|
54
|
+
parent_id: string | undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export class DocsLinks {
|
|
58
|
+
readonly root_path: string;
|
|
59
|
+
|
|
60
|
+
readonly links: SvelteMap<string, DocsLinkInfo> = new SvelteMap();
|
|
61
|
+
|
|
62
|
+
// Maps compound keys (pathname#fragment) to their original order
|
|
63
|
+
// This preserves order across component remounts
|
|
64
|
+
#fragment_to_order: Map<string, number> = new Map();
|
|
65
|
+
|
|
66
|
+
// Counter for generating unique IDs
|
|
67
|
+
#next_id = 0;
|
|
68
|
+
|
|
69
|
+
// Counter for generating section IDs (page-scoped, not module-scoped)
|
|
70
|
+
#section_counter = 0;
|
|
71
|
+
|
|
72
|
+
// Counter for generating link order values (page-scoped, not module-scoped)
|
|
73
|
+
// This ensures consistent ordering between SSR and client hydration
|
|
74
|
+
#order_counter = 0;
|
|
75
|
+
|
|
76
|
+
docs_links = $derived.by(() => {
|
|
77
|
+
// Build parent-child map
|
|
78
|
+
const children_map: Map<string | undefined, Array<DocsLinkInfo>> = new Map();
|
|
79
|
+
|
|
80
|
+
for (const link of this.links.values()) {
|
|
81
|
+
if (!children_map.has(link.parent_id)) {
|
|
82
|
+
children_map.set(link.parent_id, []);
|
|
83
|
+
}
|
|
84
|
+
children_map.get(link.parent_id)!.push(link);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Sort siblings by order
|
|
88
|
+
for (const siblings of children_map.values()) {
|
|
89
|
+
siblings.sort((a, b) => a.order - b.order);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Flatten tree with depth-first traversal
|
|
93
|
+
const result: Array<DocsLinkInfo> = [];
|
|
94
|
+
const traverse = (parent_id: string | undefined) => {
|
|
95
|
+
const children = children_map.get(parent_id) || [];
|
|
96
|
+
for (const child of children) {
|
|
97
|
+
result.push(child);
|
|
98
|
+
traverse(child.id); // recursively add children
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
traverse(undefined); // start with root nodes (no parent)
|
|
103
|
+
return result;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
readonly fragments_onscreen: SvelteSet<string> = new SvelteSet();
|
|
107
|
+
|
|
108
|
+
constructor(root_path = DOCS_PATH_DEFAULT) {
|
|
109
|
+
this.root_path = root_path;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
add(
|
|
113
|
+
fragment: string,
|
|
114
|
+
text: string,
|
|
115
|
+
pathname: string,
|
|
116
|
+
tag?: DocsLinkTag,
|
|
117
|
+
depth = 1,
|
|
118
|
+
parent_id?: string,
|
|
119
|
+
explicit_id?: string,
|
|
120
|
+
): string {
|
|
121
|
+
// Use explicit ID if provided, otherwise generate one
|
|
122
|
+
const id = explicit_id ?? 'docs_link_' + this.#next_id++;
|
|
123
|
+
|
|
124
|
+
// Use compound key (pathname#fragment) to preserve order across remounts
|
|
125
|
+
const page_fragment_key = `${pathname}#${fragment}`;
|
|
126
|
+
const existing_order = this.#fragment_to_order.get(page_fragment_key);
|
|
127
|
+
const order = existing_order ?? this.#order_counter++;
|
|
128
|
+
|
|
129
|
+
// Store order for new fragments only
|
|
130
|
+
if (existing_order === undefined) {
|
|
131
|
+
this.#fragment_to_order.set(page_fragment_key, order);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.links.set(id, {id, fragment, text, tag, depth, order, parent_id});
|
|
135
|
+
return id;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
remove(id: string): void {
|
|
139
|
+
this.links.delete(id);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Generate a unique section ID for the current page render.
|
|
144
|
+
* This counter is instance-scoped, ensuring SSR/client consistency.
|
|
145
|
+
*/
|
|
146
|
+
generate_section_id(): string {
|
|
147
|
+
return `section_${this.#section_counter++}`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const render_value_to_string = (value: unknown): string => {
|
|
2
|
+
if (value === null) return 'null';
|
|
3
|
+
if (value === undefined) return 'undefined';
|
|
4
|
+
const type = typeof value;
|
|
5
|
+
if (type === 'string') return `'${value}'`; // eslint-disable-line @typescript-eslint/no-base-to-string
|
|
6
|
+
if (type === 'number' || type === 'boolean') return value + ''; // eslint-disable-line @typescript-eslint/no-base-to-string
|
|
7
|
+
if (Array.isArray(value)) return `[${value.map(render_value_to_string).join(', ')}]`;
|
|
8
|
+
if (type === 'object') return JSON.stringify(value);
|
|
9
|
+
return value + ''; // eslint-disable-line @typescript-eslint/no-base-to-string
|
|
10
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type {Attachment} from 'svelte/attachments';
|
|
2
|
+
import {deep_equal} from '@fuzdev/fuz_util/deep_equal.js';
|
|
3
|
+
|
|
4
|
+
export interface IntersectParams {
|
|
5
|
+
/**
|
|
6
|
+
* Called when the element enters or leaves the viewport until disconnected.
|
|
7
|
+
*/
|
|
8
|
+
onintersect?: OnIntersect;
|
|
9
|
+
/**
|
|
10
|
+
* Called when the attachment's observer is disconnected,
|
|
11
|
+
* either by the user calling disconnect or the attachment being destroyed.
|
|
12
|
+
*/
|
|
13
|
+
ondisconnect?: OnDisconnect;
|
|
14
|
+
/**
|
|
15
|
+
* A value of `1` disconnects after `el` enters and leaves the viewport one time,
|
|
16
|
+
* similar to 'once' for an event.
|
|
17
|
+
* `0` disables and `undefined` or a negative number like `-1` never disconnects.
|
|
18
|
+
*/
|
|
19
|
+
count?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Same as the `options` param to
|
|
22
|
+
* [`IntersectionObserver`](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver#options)
|
|
23
|
+
*/
|
|
24
|
+
options?: IntersectionObserverInit;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type IntersectParamsOrCallback = OnIntersect | IntersectParams;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Creates an attachment that observes element viewport intersection.
|
|
31
|
+
* Uses the lazy function pattern to optimize reactivity:
|
|
32
|
+
* callbacks can update without recreating the observer, preserving state.
|
|
33
|
+
* @param get_params Function that returns callback, params object, or nullish to disable
|
|
34
|
+
*/
|
|
35
|
+
export const intersect =
|
|
36
|
+
(
|
|
37
|
+
get_params: () => IntersectParamsOrCallback | null | undefined,
|
|
38
|
+
): Attachment<HTMLElement | SVGElement> =>
|
|
39
|
+
(el) => {
|
|
40
|
+
// State that persists across callback changes
|
|
41
|
+
let intersecting = false;
|
|
42
|
+
let intersections = 0;
|
|
43
|
+
let observer: IntersectionObserver | null = null;
|
|
44
|
+
let current_options: IntersectionObserverInit | undefined = undefined;
|
|
45
|
+
|
|
46
|
+
// Current callbacks - updated reactively without recreating observer
|
|
47
|
+
let current_onintersect: OnIntersect | undefined;
|
|
48
|
+
let current_ondisconnect: OnDisconnect | undefined;
|
|
49
|
+
let current_count: number | undefined;
|
|
50
|
+
|
|
51
|
+
const disconnect = (): void => {
|
|
52
|
+
if (!observer) return;
|
|
53
|
+
const observer_to_disconnect = observer;
|
|
54
|
+
observer.disconnect();
|
|
55
|
+
if (current_ondisconnect) {
|
|
56
|
+
current_ondisconnect({intersecting, intersections, el, observer: observer_to_disconnect});
|
|
57
|
+
}
|
|
58
|
+
observer = null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
$effect(() => {
|
|
62
|
+
const params = get_params();
|
|
63
|
+
|
|
64
|
+
if (params == null) {
|
|
65
|
+
// Params disabled, disconnect if active
|
|
66
|
+
current_onintersect = undefined;
|
|
67
|
+
current_ondisconnect = undefined;
|
|
68
|
+
current_count = undefined;
|
|
69
|
+
if (observer) {
|
|
70
|
+
disconnect();
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Parse params and update current callbacks
|
|
76
|
+
let options: IntersectionObserverInit | undefined;
|
|
77
|
+
if (typeof params === 'function') {
|
|
78
|
+
current_onintersect = params;
|
|
79
|
+
current_ondisconnect = undefined;
|
|
80
|
+
current_count = undefined;
|
|
81
|
+
options = undefined;
|
|
82
|
+
} else {
|
|
83
|
+
current_onintersect = params.onintersect;
|
|
84
|
+
current_ondisconnect = params.ondisconnect;
|
|
85
|
+
current_count = params.count;
|
|
86
|
+
options = params.options;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Check if count is 0 (disabled)
|
|
90
|
+
if (current_count === 0) {
|
|
91
|
+
if (observer) {
|
|
92
|
+
disconnect();
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check if options changed (requires observer recreation)
|
|
98
|
+
const options_changed = !deep_equal(current_options, options);
|
|
99
|
+
|
|
100
|
+
if (options_changed || !observer) {
|
|
101
|
+
// Disconnect old observer if exists
|
|
102
|
+
if (observer) {
|
|
103
|
+
disconnect();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Reset state on observer recreation
|
|
107
|
+
intersecting = false;
|
|
108
|
+
intersections = 0;
|
|
109
|
+
current_options = options;
|
|
110
|
+
|
|
111
|
+
// Create new observer
|
|
112
|
+
observer = new IntersectionObserver((entries) => {
|
|
113
|
+
intersecting = entries[0]!.isIntersecting;
|
|
114
|
+
if (current_onintersect && observer) {
|
|
115
|
+
current_onintersect({intersecting, intersections, el, observer, disconnect});
|
|
116
|
+
}
|
|
117
|
+
if (intersecting) {
|
|
118
|
+
intersections++;
|
|
119
|
+
} else {
|
|
120
|
+
if (current_count && current_count > 0 && intersections >= current_count) {
|
|
121
|
+
disconnect();
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}, options);
|
|
125
|
+
observer.observe(el);
|
|
126
|
+
}
|
|
127
|
+
// If only callbacks changed, observer stays alive and uses new callbacks from closure
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return disconnect;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// TODO how to forward generic `el` type?
|
|
134
|
+
export type OnIntersect = (state: IntersectState) => void;
|
|
135
|
+
|
|
136
|
+
export interface IntersectState {
|
|
137
|
+
intersecting: boolean;
|
|
138
|
+
intersections: number;
|
|
139
|
+
el: HTMLElement | SVGElement;
|
|
140
|
+
observer: IntersectionObserver;
|
|
141
|
+
disconnect: () => void;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// TODO how to forward generic `el` type?
|
|
145
|
+
export type OnDisconnect = (state: DisconnectState) => void;
|
|
146
|
+
|
|
147
|
+
export interface DisconnectState {
|
|
148
|
+
intersecting: boolean;
|
|
149
|
+
intersections: number;
|
|
150
|
+
el: HTMLElement | SVGElement;
|
|
151
|
+
observer: IntersectionObserver;
|
|
152
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type {LibraryJson} from '@fuzdev/fuz_util/library_json.js';
|
|
2
|
+
|
|
3
|
+
import {create_context} from './context_helpers.js';
|
|
4
|
+
import {Declaration} from './declaration.svelte.js';
|
|
5
|
+
import {Module} from './module.svelte.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Rich runtime representation of a library.
|
|
9
|
+
*
|
|
10
|
+
* Wraps `LibraryJson` with computed properties and provides the root
|
|
11
|
+
* of the API documentation hierarchy: Library → modules → declarations
|
|
12
|
+
*
|
|
13
|
+
* @see module.svelte.ts for Module class
|
|
14
|
+
* @see declaration.svelte.ts for Declaration class
|
|
15
|
+
*/
|
|
16
|
+
export class Library {
|
|
17
|
+
readonly library_json: LibraryJson = $state.raw()!;
|
|
18
|
+
|
|
19
|
+
package_json = $derived(this.library_json.package_json);
|
|
20
|
+
source_json = $derived(this.library_json.source_json);
|
|
21
|
+
|
|
22
|
+
name = $derived(this.library_json.name);
|
|
23
|
+
repo_name = $derived(this.library_json.repo_name);
|
|
24
|
+
repo_url = $derived(this.library_json.repo_url);
|
|
25
|
+
owner_name = $derived(this.library_json.owner_name);
|
|
26
|
+
homepage_url = $derived(this.library_json.homepage_url);
|
|
27
|
+
logo_url = $derived(this.library_json.logo_url);
|
|
28
|
+
logo_alt = $derived(this.library_json.logo_alt);
|
|
29
|
+
npm_url = $derived(this.library_json.npm_url);
|
|
30
|
+
changelog_url = $derived(this.library_json.changelog_url);
|
|
31
|
+
published = $derived(this.library_json.published);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Organization URL (e.g., 'https://github.com/ryanatkn').
|
|
35
|
+
*/
|
|
36
|
+
org_url = $derived(
|
|
37
|
+
this.repo_url && this.repo_name
|
|
38
|
+
? this.repo_url.endsWith('/' + this.repo_name)
|
|
39
|
+
? this.repo_url.slice(0, -this.repo_name.length - 1)
|
|
40
|
+
: null
|
|
41
|
+
: null,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* All modules as rich Module instances.
|
|
46
|
+
*/
|
|
47
|
+
modules = $derived(
|
|
48
|
+
this.source_json.modules
|
|
49
|
+
? this.source_json.modules.map((module_json) => new Module(this, module_json))
|
|
50
|
+
: [],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* All modules sorted alphabetically by path.
|
|
55
|
+
*/
|
|
56
|
+
modules_sorted = $derived([...this.modules].sort((a, b) => a.path.localeCompare(b.path)));
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* All declarations across all modules as a flat array.
|
|
60
|
+
*/
|
|
61
|
+
declarations = $derived(this.modules.flatMap((module) => module.declarations));
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Declaration lookup map by name. Provides O(1) lookup.
|
|
65
|
+
*/
|
|
66
|
+
declaration_map = $derived(new Map(this.declarations.map((d) => [d.name, d])));
|
|
67
|
+
|
|
68
|
+
constructor(library_json: LibraryJson) {
|
|
69
|
+
this.library_json = library_json;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Look up a declaration by name.
|
|
74
|
+
*/
|
|
75
|
+
lookup_declaration(name: string): Declaration | undefined {
|
|
76
|
+
return this.declaration_map.get(name);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if a declaration exists.
|
|
81
|
+
*/
|
|
82
|
+
has_declaration(name: string): boolean {
|
|
83
|
+
return this.declaration_map.has(name);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Look up a module by its canonical path.
|
|
88
|
+
*/
|
|
89
|
+
lookup_module(path: string): Module | undefined {
|
|
90
|
+
return this.modules.find((m) => m.path === path);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Search declarations by query string with multi-term AND logic.
|
|
95
|
+
*/
|
|
96
|
+
search_declarations(query: string): Array<Declaration> {
|
|
97
|
+
return library_search_declarations(this.declarations, query);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const library_context = create_context<Library>();
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Search declarations by query string with multi-term AND logic.
|
|
105
|
+
*/
|
|
106
|
+
const library_search_declarations = (
|
|
107
|
+
declarations: Array<Declaration>,
|
|
108
|
+
query: string,
|
|
109
|
+
): Array<Declaration> => {
|
|
110
|
+
if (!query.trim()) return [];
|
|
111
|
+
|
|
112
|
+
const terms = query.trim().toLowerCase().split(/\s+/);
|
|
113
|
+
|
|
114
|
+
const results = declarations
|
|
115
|
+
.map((declaration) => {
|
|
116
|
+
let total_score = 0;
|
|
117
|
+
|
|
118
|
+
for (const term of terms) {
|
|
119
|
+
const name_score = library_score_match(declaration.name, term);
|
|
120
|
+
const kind_score = library_score_match(declaration.kind, term);
|
|
121
|
+
const module_score = library_score_match(declaration.module_path, term);
|
|
122
|
+
|
|
123
|
+
const term_best = Math.max(name_score, kind_score, module_score);
|
|
124
|
+
|
|
125
|
+
if (term_best === 0) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
total_score += term_best;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {declaration, score: total_score};
|
|
133
|
+
})
|
|
134
|
+
.filter((r) => r !== null)
|
|
135
|
+
.sort((a, b) => b.score - a.score || a.declaration.name.localeCompare(b.declaration.name))
|
|
136
|
+
.map((r) => r.declaration);
|
|
137
|
+
|
|
138
|
+
return results;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Score a search term match against a field value.
|
|
143
|
+
*/
|
|
144
|
+
const library_score_match = (field_value: string, term: string): number => {
|
|
145
|
+
const field_lower = field_value.toLowerCase();
|
|
146
|
+
|
|
147
|
+
if (field_lower === term) return 100;
|
|
148
|
+
if (field_lower.startsWith(term)) return 80;
|
|
149
|
+
if (field_lower.includes(term)) return 60;
|
|
150
|
+
|
|
151
|
+
let term_idx = 0;
|
|
152
|
+
for (const char of field_lower) {
|
|
153
|
+
if (char === term[term_idx]) {
|
|
154
|
+
term_idx++;
|
|
155
|
+
if (term_idx === term.length) {
|
|
156
|
+
return 40;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return 0;
|
|
162
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Library metadata generator helper.
|
|
3
|
+
*
|
|
4
|
+
* Generates package_json and source_json with rich metadata:
|
|
5
|
+
* - JSDoc/TSDoc comments with full tag support
|
|
6
|
+
* - Full type signatures
|
|
7
|
+
* - Source code locations
|
|
8
|
+
* - Parameter information with descriptions and defaults
|
|
9
|
+
* - Return value documentation
|
|
10
|
+
* - Usage examples
|
|
11
|
+
* - Dependency graphs
|
|
12
|
+
* - Svelte component props
|
|
13
|
+
*
|
|
14
|
+
* @see @fuzdev/fuz_util/source_json.js for type definitions
|
|
15
|
+
* @see src/lib/library_gen_helpers.ts for buildtime-only helpers
|
|
16
|
+
* @see src/lib/tsdoc_helpers.ts for JSDoc/TSDoc parsing
|
|
17
|
+
* @see src/lib/ts_helpers.ts for TypeScript analysis
|
|
18
|
+
* @see src/lib/svelte_helpers.ts for Svelte component analysis
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type {Gen} from '@ryanatkn/gro';
|
|
22
|
+
import {load_package_json} from '@ryanatkn/gro/package_json.js';
|
|
23
|
+
import type {SourceJson} from '@fuzdev/fuz_util/source_json.js';
|
|
24
|
+
|
|
25
|
+
import {ts_create_program, type ReExportInfo} from './ts_helpers.js';
|
|
26
|
+
import {module_extract_path, module_is_svelte} from './module_helpers.js';
|
|
27
|
+
import {
|
|
28
|
+
library_gen_collect_source_files,
|
|
29
|
+
library_gen_sort_modules,
|
|
30
|
+
library_gen_validate_no_duplicates,
|
|
31
|
+
library_gen_generate_ts,
|
|
32
|
+
library_gen_analyze_svelte_file,
|
|
33
|
+
library_gen_analyze_typescript_file,
|
|
34
|
+
} from './library_gen_helpers.js';
|
|
35
|
+
|
|
36
|
+
export interface LibraryGenOptions {
|
|
37
|
+
filename?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Creates a Gen object for generating library metadata with full TypeScript analysis.
|
|
42
|
+
*
|
|
43
|
+
* Usage in a `.gen.ts` file:
|
|
44
|
+
* ```ts
|
|
45
|
+
* import {library_gen} from '@ryanatkn/fuz/library_gen.js';
|
|
46
|
+
* export const gen = library_gen();
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export const library_gen = (options?: LibraryGenOptions): Gen => {
|
|
50
|
+
return {
|
|
51
|
+
generate: async ({log, filer}) => {
|
|
52
|
+
log.info('generating library metadata with full TypeScript analysis...');
|
|
53
|
+
|
|
54
|
+
// Ensure filer is initialized
|
|
55
|
+
await filer.init();
|
|
56
|
+
|
|
57
|
+
// Read package.json
|
|
58
|
+
const package_json = await load_package_json();
|
|
59
|
+
|
|
60
|
+
// Create TypeScript program
|
|
61
|
+
const program = ts_create_program(log);
|
|
62
|
+
if (!program) {
|
|
63
|
+
throw new Error('Failed to create TypeScript program - cannot generate library metadata');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const checker = program.getTypeChecker();
|
|
67
|
+
|
|
68
|
+
// Collect source files from filer
|
|
69
|
+
const source_disknodes = library_gen_collect_source_files(filer.files, log);
|
|
70
|
+
|
|
71
|
+
// Build source_json with array-based modules
|
|
72
|
+
// Phase 1: Analyze all modules and collect re-exports
|
|
73
|
+
const source_json: SourceJson = {
|
|
74
|
+
name: package_json.name,
|
|
75
|
+
version: package_json.version,
|
|
76
|
+
modules: [],
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Collect all re-exports: Map<declaration_name, Set<re_exporting_module_path>>
|
|
80
|
+
// The Set tracks which modules re-export each declaration
|
|
81
|
+
const all_re_exports: Array<{re_exporting_module: string; re_export: ReExportInfo}> = [];
|
|
82
|
+
|
|
83
|
+
for (const disknode of source_disknodes) {
|
|
84
|
+
const source_id = disknode.id;
|
|
85
|
+
const module_path = module_extract_path(source_id);
|
|
86
|
+
const is_svelte = module_is_svelte(module_path);
|
|
87
|
+
|
|
88
|
+
// Handle Svelte files separately (before trying to get TypeScript source file)
|
|
89
|
+
if (is_svelte) {
|
|
90
|
+
const mod = library_gen_analyze_svelte_file(disknode, module_path, checker);
|
|
91
|
+
source_json.modules!.push(mod);
|
|
92
|
+
} else {
|
|
93
|
+
// For TypeScript/JS files, get the source file from the program
|
|
94
|
+
const source_file = program.getSourceFile(source_id);
|
|
95
|
+
if (!source_file) {
|
|
96
|
+
log.warn(`Could not get source file: ${source_id}`);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// May throw, which we want to see
|
|
101
|
+
const {module: mod, re_exports} = library_gen_analyze_typescript_file(
|
|
102
|
+
disknode,
|
|
103
|
+
source_file,
|
|
104
|
+
module_path,
|
|
105
|
+
checker,
|
|
106
|
+
);
|
|
107
|
+
source_json.modules!.push(mod);
|
|
108
|
+
|
|
109
|
+
// Collect re-exports for post-processing
|
|
110
|
+
for (const re_export of re_exports) {
|
|
111
|
+
all_re_exports.push({re_exporting_module: module_path, re_export});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Phase 2: Build also_exported_from arrays from re-export data
|
|
117
|
+
// Group re-exports by original module and declaration name
|
|
118
|
+
const re_export_map: Map<string, Map<string, Array<string>>> = new Map();
|
|
119
|
+
for (const {re_exporting_module, re_export} of all_re_exports) {
|
|
120
|
+
const {name, original_module} = re_export;
|
|
121
|
+
if (!re_export_map.has(original_module)) {
|
|
122
|
+
re_export_map.set(original_module, new Map());
|
|
123
|
+
}
|
|
124
|
+
const module_map = re_export_map.get(original_module)!;
|
|
125
|
+
if (!module_map.has(name)) {
|
|
126
|
+
module_map.set(name, []);
|
|
127
|
+
}
|
|
128
|
+
module_map.get(name)!.push(re_exporting_module);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Merge into original declarations
|
|
132
|
+
for (const mod of source_json.modules ?? []) {
|
|
133
|
+
const module_re_exports = re_export_map.get(mod.path);
|
|
134
|
+
if (!module_re_exports) continue;
|
|
135
|
+
|
|
136
|
+
for (const declaration of mod.declarations ?? []) {
|
|
137
|
+
const re_exporters = module_re_exports.get(declaration.name);
|
|
138
|
+
if (re_exporters?.length) {
|
|
139
|
+
declaration.also_exported_from = re_exporters.sort();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Sort modules alphabetically for deterministic output and cleaner diffs
|
|
145
|
+
source_json.modules = source_json.modules
|
|
146
|
+
? library_gen_sort_modules(source_json.modules)
|
|
147
|
+
: undefined;
|
|
148
|
+
|
|
149
|
+
// Validate no duplicate declaration names across modules
|
|
150
|
+
library_gen_validate_no_duplicates(source_json, log);
|
|
151
|
+
|
|
152
|
+
log.info('library metadata generation complete');
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
content: library_gen_generate_ts(package_json, source_json),
|
|
156
|
+
...(options?.filename && {filename: options.filename}),
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
};
|