@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,34 @@
|
|
|
1
|
+
import type {Component} from 'svelte';
|
|
2
|
+
|
|
3
|
+
import {create_context} from './context_helpers.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Component registry for custom Svelte components that can be used in mdz content.
|
|
7
|
+
*
|
|
8
|
+
* For example, registering 'Alert' allows using `<Alert>...</Alert>` in mdz content.
|
|
9
|
+
*
|
|
10
|
+
* The Map values are the Svelte component constructors.
|
|
11
|
+
*/
|
|
12
|
+
export type MdzComponents = Map<string, Component<any, any>>; // TODO support params
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Element registry for HTML elements that can be used in mdz content.
|
|
16
|
+
*
|
|
17
|
+
* For example, registering 'div' allows using `<div>...</div>` in mdz content.
|
|
18
|
+
*
|
|
19
|
+
* The Map values are boolean placeholders for future configuration options.
|
|
20
|
+
*/
|
|
21
|
+
export type MdzElements = Map<string, boolean>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Context for providing custom mdz components.
|
|
25
|
+
* Must be set by the application using mdz.
|
|
26
|
+
*/
|
|
27
|
+
export const mdz_components_context = create_context<MdzComponents>();
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Context for providing allowed HTML elements.
|
|
31
|
+
* Must be set by the application using mdz.
|
|
32
|
+
* By default, no HTML elements are allowed.
|
|
33
|
+
*/
|
|
34
|
+
export const mdz_elements_context = create_context<MdzElements>();
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type {ModuleJson} from '@fuzdev/fuz_util/source_json.js';
|
|
2
|
+
|
|
3
|
+
import {Declaration} from './declaration.svelte.js';
|
|
4
|
+
import type {Library} from './library.svelte.js';
|
|
5
|
+
import {url_github_file} from './library_helpers.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Rich runtime representation of a module with computed properties.
|
|
9
|
+
*/
|
|
10
|
+
export class Module {
|
|
11
|
+
readonly library: Library = $state.raw()!;
|
|
12
|
+
readonly module_json: ModuleJson = $state.raw()!;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Canonical module path (e.g., 'Alert.ts', 'helpers/foo.ts').
|
|
16
|
+
*/
|
|
17
|
+
path = $derived(this.module_json.path);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Import-style path with ./ prefix.
|
|
21
|
+
*/
|
|
22
|
+
path_import = $derived('./' + this.path);
|
|
23
|
+
|
|
24
|
+
module_comment = $derived(this.module_json.module_comment);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Array of Declaration instances. Filters out default exports.
|
|
28
|
+
*/
|
|
29
|
+
declarations = $derived(
|
|
30
|
+
this.module_json.declarations
|
|
31
|
+
? this.module_json.declarations
|
|
32
|
+
.filter((declaration_json) => declaration_json.name !== 'default')
|
|
33
|
+
.map((declaration_json) => new Declaration(this, declaration_json))
|
|
34
|
+
: [],
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* API documentation URL for this module.
|
|
39
|
+
*/
|
|
40
|
+
url_api = $derived(`/docs/api/${this.path}`);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* GitHub source URL.
|
|
44
|
+
*/
|
|
45
|
+
url_github = $derived(
|
|
46
|
+
this.library.repo_url
|
|
47
|
+
? url_github_file(this.library.repo_url, `src/lib/${this.path}`)
|
|
48
|
+
: undefined,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
has_declarations: boolean = $derived(
|
|
52
|
+
!!(this.module_json.declarations && this.module_json.declarations.length > 0),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
has_module_comment: boolean = $derived(!!this.module_json.module_comment);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Modules this imports (paths relative to src/lib).
|
|
59
|
+
*/
|
|
60
|
+
dependencies = $derived(this.module_json.dependencies);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Modules that import this (paths relative to src/lib).
|
|
64
|
+
*/
|
|
65
|
+
dependents = $derived(this.module_json.dependents);
|
|
66
|
+
|
|
67
|
+
constructor(library: Library, module_json: ModuleJson) {
|
|
68
|
+
this.library = library;
|
|
69
|
+
this.module_json = module_json;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Look up a declaration by name within this module.
|
|
74
|
+
*/
|
|
75
|
+
get_declaration_by_name(name: string): Declaration | undefined {
|
|
76
|
+
return this.declarations.find((d) => d.name === name);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type {Module} from './module.svelte.js';
|
|
2
|
+
import type {ContextmenuParams} from './contextmenu_state.svelte.js';
|
|
3
|
+
|
|
4
|
+
// TODO @many expand contextmenus for docs
|
|
5
|
+
|
|
6
|
+
export const create_module_contextmenu = (module: Module): Array<ContextmenuParams> => {
|
|
7
|
+
const entries: Array<ContextmenuParams> = [];
|
|
8
|
+
|
|
9
|
+
// View source on GitHub
|
|
10
|
+
if (module.url_github) {
|
|
11
|
+
entries.push({
|
|
12
|
+
snippet: 'link',
|
|
13
|
+
props: {
|
|
14
|
+
href: module.url_github,
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return entries;
|
|
20
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module path and metadata helpers.
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for working with source module paths, file types,
|
|
5
|
+
* and import relationships in the package generation system.
|
|
6
|
+
*
|
|
7
|
+
* All functions are prefixed with `module_` for clarity.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Configuration for module source detection.
|
|
12
|
+
*
|
|
13
|
+
* Allows customizing which paths are considered source modules,
|
|
14
|
+
* useful for projects with non-standard directory structures.
|
|
15
|
+
*/
|
|
16
|
+
export interface ModuleSourceOptions {
|
|
17
|
+
/** Source directory paths to include. @default ['/src/lib/'] */
|
|
18
|
+
source_paths?: Array<string>;
|
|
19
|
+
/** File extensions to analyze. @default ['.ts', '.js', '.svelte'] */
|
|
20
|
+
extensions?: Array<string>;
|
|
21
|
+
/** Patterns to exclude (matched against full path). @default [/\.test\.ts$/] */
|
|
22
|
+
exclude_patterns?: Array<RegExp>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Default options for module source detection.
|
|
27
|
+
*/
|
|
28
|
+
export const MODULE_SOURCE_DEFAULTS: Required<ModuleSourceOptions> = {
|
|
29
|
+
source_paths: ['/src/lib/'],
|
|
30
|
+
extensions: ['.ts', '.js', '.svelte'],
|
|
31
|
+
exclude_patterns: [/\.test\.ts$/],
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Extract module path relative to src/lib from absolute source ID.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* module_extract_path('/home/user/project/src/lib/foo.ts') // => 'foo.ts'
|
|
39
|
+
* module_extract_path('/home/user/project/src/lib/nested/bar.svelte') // => 'nested/bar.svelte'
|
|
40
|
+
*/
|
|
41
|
+
export const module_extract_path = (source_id: string): string => {
|
|
42
|
+
const lib_index = source_id.indexOf('/src/lib/');
|
|
43
|
+
return lib_index !== -1 ? source_id.substring(lib_index + 9) : source_id;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Extract component name from a Svelte module path.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* module_get_component_name('Alert.svelte') // => 'Alert'
|
|
51
|
+
* module_get_component_name('components/Button.svelte') // => 'Button'
|
|
52
|
+
*/
|
|
53
|
+
export const module_get_component_name = (module_path: string): string =>
|
|
54
|
+
module_path.replace(/^.*\//, '').replace(/\.svelte$/, '');
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Convert module path to module key format (with ./ prefix).
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* module_get_key('foo.ts') // => './foo.ts'
|
|
61
|
+
*/
|
|
62
|
+
export const module_get_key = (module_path: string): string => `./${module_path}`;
|
|
63
|
+
|
|
64
|
+
export const module_is_typescript = (path: string): boolean =>
|
|
65
|
+
path.endsWith('.ts') || path.endsWith('.js'); // hackyy but fine?
|
|
66
|
+
|
|
67
|
+
export const module_is_svelte = (path: string): boolean => path.endsWith('.svelte');
|
|
68
|
+
|
|
69
|
+
export const module_is_css = (path: string): boolean => path.endsWith('.css');
|
|
70
|
+
|
|
71
|
+
export const module_is_json = (path: string): boolean => path.endsWith('.json');
|
|
72
|
+
|
|
73
|
+
export const module_is_test = (path: string): boolean => path.endsWith('.test.ts');
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Check if a path matches source criteria.
|
|
77
|
+
*
|
|
78
|
+
* Checks source directory paths, file extensions, and exclusion patterns.
|
|
79
|
+
* Uses defaults if no options provided.
|
|
80
|
+
*
|
|
81
|
+
* Rejects nested repo paths by ensuring the first `/src/` leads to the source directory
|
|
82
|
+
* (e.g. rejects `/src/fixtures/repos/foo/src/lib/index.ts` because `/src/fixtures/` ≠ `/src/lib/`).
|
|
83
|
+
*
|
|
84
|
+
* @param path Full path to check
|
|
85
|
+
* @param options Configuration options (uses defaults if not provided)
|
|
86
|
+
* @returns True if the path matches all criteria
|
|
87
|
+
*/
|
|
88
|
+
export const module_matches_source = (path: string, options?: ModuleSourceOptions): boolean => {
|
|
89
|
+
const opts = {...MODULE_SOURCE_DEFAULTS, ...options};
|
|
90
|
+
|
|
91
|
+
// Check if path is in one of the source directories
|
|
92
|
+
// The first /src/ in the path must lead directly to the source directory
|
|
93
|
+
// This rejects nested repos like /src/fixtures/repos/foo/src/lib/
|
|
94
|
+
const in_source_dir = opts.source_paths.some((source_path) => {
|
|
95
|
+
if (!path.includes(source_path)) return false;
|
|
96
|
+
// Find the first /src/ and verify it's part of the source_path
|
|
97
|
+
const first_src_index = path.indexOf('/src/');
|
|
98
|
+
if (first_src_index === -1) return false;
|
|
99
|
+
// The source_path should start at that position
|
|
100
|
+
return path.substring(first_src_index).startsWith(source_path);
|
|
101
|
+
});
|
|
102
|
+
if (!in_source_dir) return false;
|
|
103
|
+
|
|
104
|
+
// Check if extension matches
|
|
105
|
+
const has_valid_extension = opts.extensions.some((ext) => path.endsWith(ext));
|
|
106
|
+
if (!has_valid_extension) return false;
|
|
107
|
+
|
|
108
|
+
// Check exclusion patterns
|
|
109
|
+
const is_excluded = opts.exclude_patterns.some((pattern) => pattern.test(path));
|
|
110
|
+
if (is_excluded) return false;
|
|
111
|
+
|
|
112
|
+
return true;
|
|
113
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs `fn` in an `$effect`, passing `true` as the `skip` argument for the first `count` runs.
|
|
3
|
+
* Calls `fn` even when skipping so callers can read any dependent signals.
|
|
4
|
+
*/
|
|
5
|
+
export const effect_with_count = (fn: (count: number) => void, initial = 0): void => {
|
|
6
|
+
let count = initial;
|
|
7
|
+
$effect(() => {
|
|
8
|
+
fn(++count);
|
|
9
|
+
});
|
|
10
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {BROWSER} from 'esm-env';
|
|
2
|
+
|
|
3
|
+
// TODO more than just localStorage, maybe rework the module
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Utility function to save a value to `localStorage`.
|
|
7
|
+
*/
|
|
8
|
+
export const save_to_storage = (key: string, value: any, is_json = false): void => {
|
|
9
|
+
if (!BROWSER) return;
|
|
10
|
+
if (value !== null) {
|
|
11
|
+
try {
|
|
12
|
+
localStorage.setItem(key, is_json ? JSON.stringify(value) : value);
|
|
13
|
+
return;
|
|
14
|
+
} catch (_) {}
|
|
15
|
+
}
|
|
16
|
+
localStorage.removeItem(key); // remove if null or error
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Utility function to load a value from `localStorage` with optional parsing
|
|
21
|
+
* @param key The localStorage key
|
|
22
|
+
* @param is_json Whether to parse the value as JSON
|
|
23
|
+
* @param parse_fn Optional custom parsing function to transform the value
|
|
24
|
+
* @returns The parsed value or null if not found or parsing fails
|
|
25
|
+
*/
|
|
26
|
+
export const load_from_storage = <T>(
|
|
27
|
+
key: string,
|
|
28
|
+
is_json = false,
|
|
29
|
+
parse_fn?: (value: unknown) => T | null,
|
|
30
|
+
): T | null => {
|
|
31
|
+
if (!BROWSER) return null;
|
|
32
|
+
try {
|
|
33
|
+
const loaded = localStorage.getItem(key);
|
|
34
|
+
if (loaded === null) return null;
|
|
35
|
+
|
|
36
|
+
// First handle JSON parsing if requested
|
|
37
|
+
const value = is_json ? JSON.parse(loaded) : loaded;
|
|
38
|
+
|
|
39
|
+
// Then apply the custom parse function if provided
|
|
40
|
+
if (parse_fn) {
|
|
41
|
+
return parse_fn(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return value as unknown as T;
|
|
45
|
+
} catch (_) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte component analysis helpers.
|
|
3
|
+
*
|
|
4
|
+
* Extracts metadata from Svelte components using svelte2tsx transformations:
|
|
5
|
+
*
|
|
6
|
+
* - Component props with types and JSDoc
|
|
7
|
+
* - Component-level documentation
|
|
8
|
+
* - Type information
|
|
9
|
+
*
|
|
10
|
+
* Workflow: Transform Svelte to TypeScript via svelte2tsx, parse the transformed
|
|
11
|
+
* TypeScript with the TS Compiler API, extract component-level JSDoc from original source.
|
|
12
|
+
*
|
|
13
|
+
* All functions are prefixed with `svelte_` for clarity.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import ts from 'typescript';
|
|
17
|
+
import {readFileSync} from 'node:fs';
|
|
18
|
+
import {svelte2tsx} from 'svelte2tsx';
|
|
19
|
+
import type {DeclarationJson, ComponentPropInfo} from '@fuzdev/fuz_util/source_json.js';
|
|
20
|
+
|
|
21
|
+
import {tsdoc_parse, tsdoc_apply_to_declaration} from './tsdoc_helpers.js';
|
|
22
|
+
import {module_get_component_name} from './module_helpers.js';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Analyze a Svelte component from its svelte2tsx transformation.
|
|
26
|
+
*/
|
|
27
|
+
export const svelte_analyze_component = (
|
|
28
|
+
ts_code: string,
|
|
29
|
+
source_file: ts.SourceFile,
|
|
30
|
+
checker: ts.TypeChecker,
|
|
31
|
+
component_name: string,
|
|
32
|
+
): DeclarationJson => {
|
|
33
|
+
const result: DeclarationJson = {
|
|
34
|
+
name: component_name,
|
|
35
|
+
kind: 'component',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
// Create a virtual source file from the svelte2tsx output
|
|
40
|
+
// svelte2tsx generates plain TypeScript (not TSX/JSX)
|
|
41
|
+
const virtual_source = ts.createSourceFile(
|
|
42
|
+
source_file.fileName + '.ts',
|
|
43
|
+
ts_code,
|
|
44
|
+
ts.ScriptTarget.Latest,
|
|
45
|
+
true,
|
|
46
|
+
ts.ScriptKind.TS,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Extract component-level TSDoc from svelte2tsx transformed output
|
|
50
|
+
const component_tsdoc = svelte_extract_component_tsdoc(virtual_source);
|
|
51
|
+
tsdoc_apply_to_declaration(result, component_tsdoc);
|
|
52
|
+
|
|
53
|
+
// Extract props from svelte2tsx transformed output
|
|
54
|
+
const props = svelte_extract_props(virtual_source, checker);
|
|
55
|
+
if (props.length > 0) {
|
|
56
|
+
result.props = props;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Extract source line from original file
|
|
60
|
+
const start_pos = source_file.getLineAndCharacterOfPosition(0);
|
|
61
|
+
result.source_line = start_pos.line + 1;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
// If analysis fails, return basic component info
|
|
64
|
+
// eslint-disable-next-line no-console
|
|
65
|
+
console.error(`Error analyzing Svelte component ${component_name}:`, error);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return result;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Extract component-level TSDoc comment from svelte2tsx transformed output.
|
|
73
|
+
*
|
|
74
|
+
* svelte2tsx places component-level JSDoc inside the $$render() function,
|
|
75
|
+
* attached to a variable statement (usually before the props destructuring).
|
|
76
|
+
* This function searches the AST recursively to find it.
|
|
77
|
+
*/
|
|
78
|
+
const svelte_extract_component_tsdoc = (
|
|
79
|
+
source_file: ts.SourceFile,
|
|
80
|
+
): ReturnType<typeof tsdoc_parse> => {
|
|
81
|
+
let found_tsdoc: ReturnType<typeof tsdoc_parse> = undefined;
|
|
82
|
+
|
|
83
|
+
// Recursively search for component-level JSDoc
|
|
84
|
+
function visit(node: ts.Node) {
|
|
85
|
+
if (found_tsdoc) return; // Already found, stop searching
|
|
86
|
+
|
|
87
|
+
// Skip PropertySignature nodes - those are prop-level JSDoc, not component-level
|
|
88
|
+
if (ts.isPropertySignature(node)) {
|
|
89
|
+
return; // Don't recurse into property signatures
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Check for JSDoc on VariableStatement or VariableDeclaration
|
|
93
|
+
// Component-level JSDoc is attached to these node types
|
|
94
|
+
if (ts.isVariableStatement(node) || ts.isVariableDeclaration(node)) {
|
|
95
|
+
const tsdoc = tsdoc_parse(node, source_file);
|
|
96
|
+
if (tsdoc) {
|
|
97
|
+
found_tsdoc = tsdoc;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Continue searching child nodes
|
|
103
|
+
ts.forEachChild(node, visit);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
visit(source_file);
|
|
107
|
+
return found_tsdoc;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Helper to extract prop info from a property signature member.
|
|
112
|
+
*/
|
|
113
|
+
const svelte_extract_prop_from_member = (
|
|
114
|
+
member: ts.PropertySignature,
|
|
115
|
+
source_file: ts.SourceFile,
|
|
116
|
+
checker: ts.TypeChecker,
|
|
117
|
+
): ComponentPropInfo | undefined => {
|
|
118
|
+
if (!ts.isIdentifier(member.name)) return undefined;
|
|
119
|
+
|
|
120
|
+
const prop_name = member.name.text;
|
|
121
|
+
const optional = !!member.questionToken;
|
|
122
|
+
|
|
123
|
+
// Get type string
|
|
124
|
+
let type_string = 'any';
|
|
125
|
+
if (member.type) {
|
|
126
|
+
type_string = member.type.getText(source_file);
|
|
127
|
+
} else {
|
|
128
|
+
// Try to get type from type checker
|
|
129
|
+
try {
|
|
130
|
+
const prop_type = checker.getTypeAtLocation(member);
|
|
131
|
+
type_string = checker.typeToString(prop_type);
|
|
132
|
+
} catch {
|
|
133
|
+
// Fallback to 'any'
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Extract TSDoc description
|
|
138
|
+
let description: string | undefined;
|
|
139
|
+
const tsdoc = tsdoc_parse(member, source_file);
|
|
140
|
+
if (tsdoc) {
|
|
141
|
+
description = tsdoc.text;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Extract default value (if available in TSDoc or initializer)
|
|
145
|
+
let default_value: string | undefined;
|
|
146
|
+
const default_match = description?.match(/@default\s+(.+)/);
|
|
147
|
+
if (default_match) {
|
|
148
|
+
default_value = default_match[1]!.trim();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
name: prop_name,
|
|
153
|
+
type: type_string,
|
|
154
|
+
...(optional && {optional}),
|
|
155
|
+
description,
|
|
156
|
+
default_value,
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Extract bindable prop names from svelte2tsx transformed output.
|
|
162
|
+
*
|
|
163
|
+
* svelte2tsx marks bindable props with `__sveltets_$$bindings('prop1', 'prop2', ...)`.
|
|
164
|
+
* This function extracts those prop names.
|
|
165
|
+
*/
|
|
166
|
+
const svelte_extract_bindable_props = (virtual_source: ts.SourceFile): Set<string> => {
|
|
167
|
+
const bindable_props: Set<string> = new Set();
|
|
168
|
+
|
|
169
|
+
// Search for __sveltets_$$bindings call
|
|
170
|
+
function visit(node: ts.Node) {
|
|
171
|
+
if (ts.isCallExpression(node)) {
|
|
172
|
+
const expr = node.expression;
|
|
173
|
+
if (ts.isIdentifier(expr) && expr.text === '__sveltets_$$bindings') {
|
|
174
|
+
// Extract string literal arguments
|
|
175
|
+
for (const arg of node.arguments) {
|
|
176
|
+
if (ts.isStringLiteral(arg)) {
|
|
177
|
+
bindable_props.add(arg.text);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
ts.forEachChild(node, visit);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
visit(virtual_source);
|
|
186
|
+
return bindable_props;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Extract props from a type node (handles type literals and intersection types).
|
|
191
|
+
*
|
|
192
|
+
* @mutates props - adds extracted prop info to the array
|
|
193
|
+
*/
|
|
194
|
+
const svelte_extract_props_from_type = (
|
|
195
|
+
type_node: ts.TypeNode,
|
|
196
|
+
virtual_source: ts.SourceFile,
|
|
197
|
+
checker: ts.TypeChecker,
|
|
198
|
+
bindable_props: Set<string>,
|
|
199
|
+
props: Array<ComponentPropInfo>,
|
|
200
|
+
): void => {
|
|
201
|
+
if (ts.isTypeLiteralNode(type_node)) {
|
|
202
|
+
// Handle direct type literal: { prop1: type1, prop2: type2 }
|
|
203
|
+
for (const member of type_node.members) {
|
|
204
|
+
if (ts.isPropertySignature(member)) {
|
|
205
|
+
const prop_info = svelte_extract_prop_from_member(member, virtual_source, checker);
|
|
206
|
+
if (prop_info) {
|
|
207
|
+
// Mark as bindable if found in bindings
|
|
208
|
+
if (bindable_props.has(prop_info.name)) {
|
|
209
|
+
prop_info.bindable = true;
|
|
210
|
+
}
|
|
211
|
+
props.push(prop_info);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
} else if (ts.isIntersectionTypeNode(type_node)) {
|
|
216
|
+
// Handle intersection type: TypeA & TypeB & { prop: type }
|
|
217
|
+
for (const type_part of type_node.types) {
|
|
218
|
+
svelte_extract_props_from_type(type_part, virtual_source, checker, bindable_props, props);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Skip other type references like SvelteHTMLElements['details'] since we can't easily resolve them
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Extract props from svelte2tsx transformed output.
|
|
226
|
+
*
|
|
227
|
+
* svelte2tsx generates a `$$ComponentProps` type alias containing the component props.
|
|
228
|
+
* This function extracts prop metadata from that type.
|
|
229
|
+
*/
|
|
230
|
+
const svelte_extract_props = (
|
|
231
|
+
virtual_source: ts.SourceFile,
|
|
232
|
+
checker: ts.TypeChecker,
|
|
233
|
+
): Array<ComponentPropInfo> => {
|
|
234
|
+
const props: Array<ComponentPropInfo> = [];
|
|
235
|
+
const bindable_props = svelte_extract_bindable_props(virtual_source);
|
|
236
|
+
|
|
237
|
+
// Look for $$ComponentProps type alias or Props interface
|
|
238
|
+
ts.forEachChild(virtual_source, (node) => {
|
|
239
|
+
// Check for type alias ($$ComponentProps)
|
|
240
|
+
if (ts.isTypeAliasDeclaration(node) && node.name.text === '$$ComponentProps') {
|
|
241
|
+
svelte_extract_props_from_type(node.type, virtual_source, checker, bindable_props, props);
|
|
242
|
+
}
|
|
243
|
+
// Also check for Props interface (fallback/older format)
|
|
244
|
+
else if (ts.isInterfaceDeclaration(node) && node.name.text === 'Props') {
|
|
245
|
+
for (const member of node.members) {
|
|
246
|
+
if (ts.isPropertySignature(member)) {
|
|
247
|
+
const prop_info = svelte_extract_prop_from_member(member, virtual_source, checker);
|
|
248
|
+
if (prop_info) {
|
|
249
|
+
// Mark as bindable if found in bindings
|
|
250
|
+
if (bindable_props.has(prop_info.name)) {
|
|
251
|
+
prop_info.bindable = true;
|
|
252
|
+
}
|
|
253
|
+
props.push(prop_info);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
return props;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Analyze a Svelte component file from disk.
|
|
265
|
+
*
|
|
266
|
+
* This is a high-level function that handles the complete workflow:
|
|
267
|
+
* 1. Read the Svelte source from disk
|
|
268
|
+
* 2. Transform to TypeScript via svelte2tsx
|
|
269
|
+
* 3. Extract component metadata (props, documentation)
|
|
270
|
+
*
|
|
271
|
+
* Suitable for use in documentation generators, build tools, and analysis.
|
|
272
|
+
*
|
|
273
|
+
* @param file_path Absolute path to the .svelte file
|
|
274
|
+
* @param module_path Module path relative to src/lib (e.g., 'Alert.svelte')
|
|
275
|
+
* @param checker TypeScript type checker for type resolution
|
|
276
|
+
* @returns Complete declaration metadata for the component
|
|
277
|
+
*/
|
|
278
|
+
export const svelte_analyze_file = (
|
|
279
|
+
file_path: string,
|
|
280
|
+
module_path: string,
|
|
281
|
+
checker: ts.TypeChecker,
|
|
282
|
+
): DeclarationJson => {
|
|
283
|
+
const svelte_source = readFileSync(file_path, 'utf-8');
|
|
284
|
+
|
|
285
|
+
// Check if component uses TypeScript
|
|
286
|
+
const is_ts_file = svelte_source.includes('lang="ts"');
|
|
287
|
+
|
|
288
|
+
// Transform Svelte to TS
|
|
289
|
+
const ts_result = svelte2tsx(svelte_source, {
|
|
290
|
+
filename: file_path,
|
|
291
|
+
isTsFile: is_ts_file,
|
|
292
|
+
emitOnTemplateError: true, // Handle malformed templates gracefully
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Get component name from filename
|
|
296
|
+
const component_name = module_get_component_name(module_path);
|
|
297
|
+
|
|
298
|
+
// Create a temporary source file from the original Svelte content for JSDoc extraction
|
|
299
|
+
const temp_source = ts.createSourceFile(file_path, svelte_source, ts.ScriptTarget.Latest, true);
|
|
300
|
+
|
|
301
|
+
// Analyze the component using the existing lower-level function
|
|
302
|
+
return svelte_analyze_component(ts_result.code, temp_source, checker, component_name);
|
|
303
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {color_schemes, type ColorScheme, type Theme} from '@fuzdev/fuz_css/theme.js';
|
|
2
|
+
import {default_themes} from '@fuzdev/fuz_css/themes.js';
|
|
3
|
+
import {BROWSER} from 'esm-env';
|
|
4
|
+
|
|
5
|
+
import {create_context} from './context_helpers.js';
|
|
6
|
+
import {load_from_storage, save_to_storage} from './storage.js';
|
|
7
|
+
|
|
8
|
+
export class Themer {
|
|
9
|
+
theme: Theme = $state()!;
|
|
10
|
+
color_scheme: ColorScheme = $state()!;
|
|
11
|
+
|
|
12
|
+
constructor(theme: Theme = default_themes[0]!, color_scheme: ColorScheme = 'auto') {
|
|
13
|
+
if (parse_color_scheme(color_scheme) === null) {
|
|
14
|
+
throw Error('unknown color scheme: ' + color_scheme);
|
|
15
|
+
}
|
|
16
|
+
this.theme = theme;
|
|
17
|
+
this.color_scheme = color_scheme; // TODO `resolved_color_scheme` if auto? users could then do different things based on the final value - would not be added to `ThemerJson`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
toJSON(): ThemerJson {
|
|
21
|
+
return {
|
|
22
|
+
theme: this.theme,
|
|
23
|
+
color_scheme: this.color_scheme,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ThemerJson {
|
|
29
|
+
theme: Theme;
|
|
30
|
+
color_scheme: ColorScheme;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const themer_context = create_context<Themer>();
|
|
34
|
+
|
|
35
|
+
export const sync_color_scheme = (color_scheme: ColorScheme | null): void => {
|
|
36
|
+
if (!BROWSER) return;
|
|
37
|
+
const dark =
|
|
38
|
+
color_scheme === 'dark' ||
|
|
39
|
+
(color_scheme !== 'light' && matchMedia('(prefers-color-scheme: dark)').matches);
|
|
40
|
+
document.documentElement.classList.add(dark ? 'dark' : 'light');
|
|
41
|
+
document.documentElement.classList.remove(dark ? 'light' : 'dark');
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const COLOR_SCHEME_STORAGE_KEY = 'fuz:color-scheme';
|
|
45
|
+
export const THEME_STORAGE_KEY = 'fuz:theme';
|
|
46
|
+
|
|
47
|
+
export const save_color_scheme = (
|
|
48
|
+
color_scheme: ColorScheme | null,
|
|
49
|
+
key = COLOR_SCHEME_STORAGE_KEY,
|
|
50
|
+
): void => {
|
|
51
|
+
save_to_storage(key, color_scheme);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const load_color_scheme = (
|
|
55
|
+
fallback: ColorScheme = 'auto',
|
|
56
|
+
key = COLOR_SCHEME_STORAGE_KEY,
|
|
57
|
+
): ColorScheme => load_from_storage(key, false, parse_color_scheme) ?? fallback;
|
|
58
|
+
|
|
59
|
+
export const save_theme = (theme: Theme | null, key = THEME_STORAGE_KEY): void => {
|
|
60
|
+
save_to_storage(key, theme, true);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const load_theme = (fallback: Theme = default_themes[0]!, key = THEME_STORAGE_KEY): Theme =>
|
|
64
|
+
load_from_storage<Theme>(key, true) ?? fallback; // TODO use `parse_theme` from moss
|
|
65
|
+
|
|
66
|
+
// TODO move to moss
|
|
67
|
+
const parse_color_scheme = (value: unknown): ColorScheme | null =>
|
|
68
|
+
color_schemes.includes(value as any) ? (value as ColorScheme) : null;
|