@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
package/src/lib/tome.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {resolve} from '$app/paths';
|
|
2
|
+
import type {Component} from 'svelte';
|
|
3
|
+
import {z} from 'zod';
|
|
4
|
+
|
|
5
|
+
import {create_context} from './context_helpers.js';
|
|
6
|
+
import {DOCS_PATH_DEFAULT, docs_slugify} from './docs_helpers.svelte.js';
|
|
7
|
+
|
|
8
|
+
export const Tome = z.object({
|
|
9
|
+
name: z.string(),
|
|
10
|
+
// TODO maybe now this makes sense with mdz? summary: z.string(),
|
|
11
|
+
category: z.string(),
|
|
12
|
+
Component: z.custom<Component<any, any>>(),
|
|
13
|
+
related_tomes: z.array(z.string()),
|
|
14
|
+
related_modules: z.array(z.string()),
|
|
15
|
+
related_declarations: z.array(z.string()),
|
|
16
|
+
});
|
|
17
|
+
export type Tome = z.infer<typeof Tome>;
|
|
18
|
+
|
|
19
|
+
export const to_tome_pathname = (
|
|
20
|
+
item: Tome | string,
|
|
21
|
+
docs_path = DOCS_PATH_DEFAULT,
|
|
22
|
+
hash?: string,
|
|
23
|
+
): string => {
|
|
24
|
+
const name = typeof item === 'string' ? item : item.name;
|
|
25
|
+
const path = docs_path + '/' + docs_slugify(name);
|
|
26
|
+
return resolve((hash ? path + '#' + hash : path) as any);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const tomes_context = create_context<Map<string, Tome>>();
|
|
30
|
+
|
|
31
|
+
export const get_tome_by_name = (name: string): Tome => {
|
|
32
|
+
const tomes = tomes_context.get();
|
|
33
|
+
const tome = tomes.get(name);
|
|
34
|
+
if (!tome) throw Error(`unable to find tome "${name}"`);
|
|
35
|
+
return tome;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const tome_context = create_context<Tome>();
|
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript compiler API helpers for extracting metadata from source code.
|
|
3
|
+
*
|
|
4
|
+
* All functions are prefixed with `ts_` for clarity.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import ts from 'typescript';
|
|
8
|
+
import type {
|
|
9
|
+
DeclarationJson,
|
|
10
|
+
GenericParamInfo,
|
|
11
|
+
DeclarationKind,
|
|
12
|
+
} from '@fuzdev/fuz_util/source_json.js';
|
|
13
|
+
|
|
14
|
+
import {tsdoc_parse, tsdoc_apply_to_declaration} from './tsdoc_helpers.js';
|
|
15
|
+
import {module_extract_path, module_matches_source} from './module_helpers.js';
|
|
16
|
+
|
|
17
|
+
const ts_parse_generic_param = (param: ts.TypeParameterDeclaration): GenericParamInfo => {
|
|
18
|
+
const result: GenericParamInfo = {
|
|
19
|
+
name: param.name.text,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
if (param.constraint) {
|
|
23
|
+
result.constraint = param.constraint.getText();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (param.default) {
|
|
27
|
+
result.default_type = param.default.getText();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Extract modifier keywords from a node's modifiers.
|
|
35
|
+
*
|
|
36
|
+
* Returns an array of modifier strings like ['public', 'readonly', 'static']
|
|
37
|
+
*/
|
|
38
|
+
const ts_extract_modifiers = (
|
|
39
|
+
modifiers: ReadonlyArray<ts.ModifierLike> | undefined,
|
|
40
|
+
): Array<string> => {
|
|
41
|
+
const modifier_flags: Array<string> = [];
|
|
42
|
+
if (!modifiers) return modifier_flags;
|
|
43
|
+
|
|
44
|
+
for (const mod of modifiers) {
|
|
45
|
+
if (mod.kind === ts.SyntaxKind.PublicKeyword) modifier_flags.push('public');
|
|
46
|
+
else if (mod.kind === ts.SyntaxKind.PrivateKeyword) modifier_flags.push('private');
|
|
47
|
+
else if (mod.kind === ts.SyntaxKind.ProtectedKeyword) modifier_flags.push('protected');
|
|
48
|
+
else if (mod.kind === ts.SyntaxKind.ReadonlyKeyword) modifier_flags.push('readonly');
|
|
49
|
+
else if (mod.kind === ts.SyntaxKind.StaticKeyword) modifier_flags.push('static');
|
|
50
|
+
else if (mod.kind === ts.SyntaxKind.AbstractKeyword) modifier_flags.push('abstract');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return modifier_flags;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Infer declaration kind from symbol and node.
|
|
58
|
+
*/
|
|
59
|
+
export const ts_infer_declaration_kind = (symbol: ts.Symbol, node: ts.Node): DeclarationKind => {
|
|
60
|
+
// Check symbol flags
|
|
61
|
+
if (symbol.flags & ts.SymbolFlags.Class) return 'class';
|
|
62
|
+
if (symbol.flags & ts.SymbolFlags.Function) return 'function';
|
|
63
|
+
if (symbol.flags & ts.SymbolFlags.Interface) return 'type';
|
|
64
|
+
if (symbol.flags & ts.SymbolFlags.TypeAlias) return 'type';
|
|
65
|
+
|
|
66
|
+
// Check node kind
|
|
67
|
+
if (ts.isFunctionDeclaration(node) || ts.isArrowFunction(node) || ts.isFunctionExpression(node))
|
|
68
|
+
return 'function';
|
|
69
|
+
if (ts.isClassDeclaration(node)) return 'class';
|
|
70
|
+
if (ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) return 'type';
|
|
71
|
+
if (ts.isVariableDeclaration(node)) {
|
|
72
|
+
// Check if it's a function-valued variable
|
|
73
|
+
const init = node.initializer;
|
|
74
|
+
if (init && (ts.isArrowFunction(init) || ts.isFunctionExpression(init))) {
|
|
75
|
+
return 'function';
|
|
76
|
+
}
|
|
77
|
+
return 'variable';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return 'variable';
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Extract function/method information including parameters
|
|
85
|
+
* with descriptions and default values.
|
|
86
|
+
*
|
|
87
|
+
* @mutates declaration - adds type_signature, return_type, return_description, throws, since, parameters, generic_params
|
|
88
|
+
*/
|
|
89
|
+
export const ts_extract_function_info = (
|
|
90
|
+
node: ts.Node,
|
|
91
|
+
symbol: ts.Symbol,
|
|
92
|
+
checker: ts.TypeChecker,
|
|
93
|
+
declaration: DeclarationJson,
|
|
94
|
+
tsdoc: ReturnType<typeof tsdoc_parse>,
|
|
95
|
+
): void => {
|
|
96
|
+
try {
|
|
97
|
+
const type = checker.getTypeOfSymbolAtLocation(symbol, node);
|
|
98
|
+
const signatures = type.getCallSignatures();
|
|
99
|
+
|
|
100
|
+
if (signatures.length > 0) {
|
|
101
|
+
const sig = signatures[0]!;
|
|
102
|
+
declaration.type_signature = checker.signatureToString(sig);
|
|
103
|
+
|
|
104
|
+
const return_type = checker.getReturnTypeOfSignature(sig);
|
|
105
|
+
declaration.return_type = checker.typeToString(return_type);
|
|
106
|
+
|
|
107
|
+
// Extract return description from TSDoc
|
|
108
|
+
if (tsdoc?.returns) {
|
|
109
|
+
declaration.return_description = tsdoc.returns;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Extract throws and since from TSDoc
|
|
113
|
+
if (tsdoc?.throws?.length) {
|
|
114
|
+
declaration.throws = tsdoc.throws;
|
|
115
|
+
}
|
|
116
|
+
if (tsdoc?.since) {
|
|
117
|
+
declaration.since = tsdoc.since;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Extract parameters with descriptions and default values
|
|
121
|
+
declaration.parameters = sig.parameters.map((param) => {
|
|
122
|
+
const param_decl = param.valueDeclaration;
|
|
123
|
+
const param_type = checker.getTypeOfSymbolAtLocation(param, param_decl!);
|
|
124
|
+
|
|
125
|
+
// Get TSDoc description for this parameter
|
|
126
|
+
const description = tsdoc?.params.get(param.name);
|
|
127
|
+
|
|
128
|
+
// Extract default value from AST
|
|
129
|
+
let default_value: string | undefined;
|
|
130
|
+
if (param_decl && ts.isParameter(param_decl) && param_decl.initializer) {
|
|
131
|
+
default_value = param_decl.initializer.getText();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const optional = !!(param_decl && ts.isParameter(param_decl) && param_decl.questionToken);
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
name: param.name,
|
|
138
|
+
type: checker.typeToString(param_type),
|
|
139
|
+
...(optional && {optional}),
|
|
140
|
+
description,
|
|
141
|
+
default_value,
|
|
142
|
+
};
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
} catch (_err) {
|
|
146
|
+
// Ignore: Type checker errors are expected when analyzing incomplete or complex signatures
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Extract generic type parameters
|
|
150
|
+
if (ts.isFunctionDeclaration(node) || ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
|
|
151
|
+
if (node.typeParameters?.length) {
|
|
152
|
+
declaration.generic_params = node.typeParameters.map(ts_parse_generic_param);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Extract type/interface information with rich property metadata.
|
|
159
|
+
*
|
|
160
|
+
* @mutates declaration - adds type_signature, generic_params, extends, properties
|
|
161
|
+
*/
|
|
162
|
+
export const ts_extract_type_info = (
|
|
163
|
+
node: ts.Node,
|
|
164
|
+
_symbol: ts.Symbol,
|
|
165
|
+
checker: ts.TypeChecker,
|
|
166
|
+
declaration: DeclarationJson,
|
|
167
|
+
): void => {
|
|
168
|
+
try {
|
|
169
|
+
const type = checker.getTypeAtLocation(node);
|
|
170
|
+
declaration.type_signature = checker.typeToString(type);
|
|
171
|
+
} catch (_err) {
|
|
172
|
+
// Ignore: Type checker may fail on complex or recursive types
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node)) {
|
|
176
|
+
if (node.typeParameters?.length) {
|
|
177
|
+
declaration.generic_params = node.typeParameters.map(ts_parse_generic_param);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
182
|
+
if (node.heritageClauses) {
|
|
183
|
+
declaration.extends = node.heritageClauses
|
|
184
|
+
.filter((hc) => hc.token === ts.SyntaxKind.ExtendsKeyword)
|
|
185
|
+
.flatMap((hc) => hc.types.map((t) => t.getText()));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Extract properties with full metadata
|
|
189
|
+
declaration.properties = [];
|
|
190
|
+
for (const member of node.members) {
|
|
191
|
+
if (ts.isPropertySignature(member) && ts.isIdentifier(member.name)) {
|
|
192
|
+
const prop_name = member.name.text;
|
|
193
|
+
const prop_declaration: DeclarationJson = {
|
|
194
|
+
name: prop_name,
|
|
195
|
+
kind: 'variable',
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// Extract modifiers
|
|
199
|
+
const modifier_flags = ts_extract_modifiers(ts.getModifiers(member));
|
|
200
|
+
if (modifier_flags.length > 0) {
|
|
201
|
+
prop_declaration.modifiers = modifier_flags;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Extract type
|
|
205
|
+
if (member.type) {
|
|
206
|
+
prop_declaration.type_signature = member.type.getText();
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Extract TSDoc
|
|
210
|
+
const prop_tsdoc = tsdoc_parse(member, node.getSourceFile());
|
|
211
|
+
if (prop_tsdoc) {
|
|
212
|
+
prop_declaration.doc_comment = prop_tsdoc.text;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
declaration.properties.push(prop_declaration);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Extract class information with rich member metadata.
|
|
223
|
+
*
|
|
224
|
+
* @mutates declaration - adds extends, implements, generic_params, members
|
|
225
|
+
*/
|
|
226
|
+
export const ts_extract_class_info = (
|
|
227
|
+
node: ts.Node,
|
|
228
|
+
_symbol: ts.Symbol,
|
|
229
|
+
checker: ts.TypeChecker,
|
|
230
|
+
declaration: DeclarationJson,
|
|
231
|
+
): void => {
|
|
232
|
+
if (!ts.isClassDeclaration(node)) return;
|
|
233
|
+
|
|
234
|
+
if (node.heritageClauses) {
|
|
235
|
+
declaration.extends = node.heritageClauses
|
|
236
|
+
.filter((hc) => hc.token === ts.SyntaxKind.ExtendsKeyword)
|
|
237
|
+
.flatMap((hc) => hc.types.map((t) => t.getText()));
|
|
238
|
+
|
|
239
|
+
declaration.implements = node.heritageClauses
|
|
240
|
+
.filter((hc) => hc.token === ts.SyntaxKind.ImplementsKeyword)
|
|
241
|
+
.flatMap((hc) => hc.types.map((t) => t.getText()));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (node.typeParameters?.length) {
|
|
245
|
+
declaration.generic_params = node.typeParameters.map(ts_parse_generic_param);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Extract members with full metadata
|
|
249
|
+
declaration.members = [];
|
|
250
|
+
for (const member of node.members) {
|
|
251
|
+
if (
|
|
252
|
+
ts.isPropertyDeclaration(member) ||
|
|
253
|
+
ts.isMethodDeclaration(member) ||
|
|
254
|
+
ts.isConstructorDeclaration(member)
|
|
255
|
+
) {
|
|
256
|
+
const is_constructor = ts.isConstructorDeclaration(member);
|
|
257
|
+
const member_name = is_constructor
|
|
258
|
+
? 'constructor'
|
|
259
|
+
: ts.isIdentifier(member.name)
|
|
260
|
+
? member.name.text
|
|
261
|
+
: member.name.getText();
|
|
262
|
+
if (!member_name) continue;
|
|
263
|
+
|
|
264
|
+
// Skip private fields (those starting with #)
|
|
265
|
+
if (member_name.startsWith('#')) continue;
|
|
266
|
+
|
|
267
|
+
const member_declaration: DeclarationJson = {
|
|
268
|
+
name: member_name,
|
|
269
|
+
kind: is_constructor
|
|
270
|
+
? 'constructor'
|
|
271
|
+
: ts.isMethodDeclaration(member)
|
|
272
|
+
? 'function'
|
|
273
|
+
: 'variable',
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
// Extract visibility and modifiers
|
|
277
|
+
const modifier_flags = ts_extract_modifiers(ts.getModifiers(member));
|
|
278
|
+
if (modifier_flags.length > 0) {
|
|
279
|
+
member_declaration.modifiers = modifier_flags;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Extract TSDoc
|
|
283
|
+
const member_tsdoc = tsdoc_parse(member, node.getSourceFile());
|
|
284
|
+
if (member_tsdoc) {
|
|
285
|
+
member_declaration.doc_comment = member_tsdoc.text;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Extract type information and parameters for methods and constructors
|
|
289
|
+
try {
|
|
290
|
+
if (ts.isPropertyDeclaration(member) && member.type) {
|
|
291
|
+
member_declaration.type_signature = member.type.getText();
|
|
292
|
+
} else if (ts.isMethodDeclaration(member) || ts.isConstructorDeclaration(member)) {
|
|
293
|
+
let signatures: ReadonlyArray<ts.Signature> = [];
|
|
294
|
+
|
|
295
|
+
if (is_constructor) {
|
|
296
|
+
// For constructors, get construct signatures from the class symbol
|
|
297
|
+
const class_symbol = checker.getSymbolAtLocation(node.name!);
|
|
298
|
+
if (class_symbol) {
|
|
299
|
+
const class_type = checker.getTypeOfSymbolAtLocation(class_symbol, node);
|
|
300
|
+
signatures = class_type.getConstructSignatures();
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
// For methods, get call signatures from the method symbol
|
|
304
|
+
const member_symbol = checker.getSymbolAtLocation(member.name);
|
|
305
|
+
if (member_symbol) {
|
|
306
|
+
const member_type = checker.getTypeOfSymbolAtLocation(member_symbol, member);
|
|
307
|
+
signatures = member_type.getCallSignatures();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (signatures.length > 0) {
|
|
312
|
+
const sig = signatures[0]!;
|
|
313
|
+
|
|
314
|
+
// Extract type signature for both constructors and methods
|
|
315
|
+
member_declaration.type_signature = checker.signatureToString(sig);
|
|
316
|
+
|
|
317
|
+
// For methods (but not constructors), also extract return info separately
|
|
318
|
+
if (!is_constructor) {
|
|
319
|
+
// Extract return type for methods
|
|
320
|
+
const return_type = checker.getReturnTypeOfSignature(sig);
|
|
321
|
+
member_declaration.return_type = checker.typeToString(return_type);
|
|
322
|
+
|
|
323
|
+
// Extract return description from TSDoc
|
|
324
|
+
if (member_tsdoc?.returns) {
|
|
325
|
+
member_declaration.return_description = member_tsdoc.returns;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Extract parameters with descriptions and default values
|
|
330
|
+
member_declaration.parameters = sig.parameters.map((param) => {
|
|
331
|
+
const param_decl = param.valueDeclaration;
|
|
332
|
+
const param_type = checker.getTypeOfSymbolAtLocation(param, param_decl!);
|
|
333
|
+
|
|
334
|
+
// Get TSDoc description for this parameter
|
|
335
|
+
const description = member_tsdoc?.params.get(param.name);
|
|
336
|
+
|
|
337
|
+
// Extract default value from AST
|
|
338
|
+
let default_value: string | undefined;
|
|
339
|
+
if (param_decl && ts.isParameter(param_decl) && param_decl.initializer) {
|
|
340
|
+
default_value = param_decl.initializer.getText();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const optional = !!(
|
|
344
|
+
param_decl &&
|
|
345
|
+
ts.isParameter(param_decl) &&
|
|
346
|
+
param_decl.questionToken
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
name: param.name,
|
|
351
|
+
type: checker.typeToString(param_type),
|
|
352
|
+
...(optional && {optional}),
|
|
353
|
+
description,
|
|
354
|
+
default_value,
|
|
355
|
+
};
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// Extract throws and since from TSDoc (for both methods and constructors)
|
|
359
|
+
if (member_tsdoc?.throws?.length) {
|
|
360
|
+
member_declaration.throws = member_tsdoc.throws;
|
|
361
|
+
}
|
|
362
|
+
if (member_tsdoc?.since) {
|
|
363
|
+
member_declaration.since = member_tsdoc.since;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
} catch (_err) {
|
|
368
|
+
// Ignore: Type checker may fail on complex member signatures
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
declaration.members.push(member_declaration);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Extract variable information.
|
|
378
|
+
*
|
|
379
|
+
* @mutates declaration - adds type_signature
|
|
380
|
+
*/
|
|
381
|
+
export const ts_extract_variable_info = (
|
|
382
|
+
node: ts.Node,
|
|
383
|
+
symbol: ts.Symbol,
|
|
384
|
+
checker: ts.TypeChecker,
|
|
385
|
+
declaration: DeclarationJson,
|
|
386
|
+
): void => {
|
|
387
|
+
try {
|
|
388
|
+
const type = checker.getTypeOfSymbolAtLocation(symbol, node);
|
|
389
|
+
declaration.type_signature = checker.typeToString(type);
|
|
390
|
+
} catch (_err) {
|
|
391
|
+
// Ignore: Type checker may fail on complex variable types
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Result of analyzing a single declaration.
|
|
397
|
+
*/
|
|
398
|
+
export interface TsDeclarationAnalysis {
|
|
399
|
+
/** The analyzed declaration metadata. */
|
|
400
|
+
declaration: DeclarationJson;
|
|
401
|
+
/** Whether the declaration is marked @nodocs (should be excluded from documentation). */
|
|
402
|
+
nodocs: boolean;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Analyze a TypeScript symbol and extract rich metadata.
|
|
407
|
+
*
|
|
408
|
+
* This is a high-level function that combines TSDoc parsing with TypeScript
|
|
409
|
+
* type analysis to produce complete declaration metadata. Suitable for use
|
|
410
|
+
* in documentation generators, IDE integrations, and other tooling.
|
|
411
|
+
*
|
|
412
|
+
* @param symbol The TypeScript symbol to analyze
|
|
413
|
+
* @param source_file The source file containing the symbol
|
|
414
|
+
* @param checker The TypeScript type checker
|
|
415
|
+
* @returns Complete declaration metadata including docs, types, and parameters, plus nodocs flag
|
|
416
|
+
*/
|
|
417
|
+
export const ts_analyze_declaration = (
|
|
418
|
+
symbol: ts.Symbol,
|
|
419
|
+
source_file: ts.SourceFile,
|
|
420
|
+
checker: ts.TypeChecker,
|
|
421
|
+
): TsDeclarationAnalysis => {
|
|
422
|
+
const name = symbol.name;
|
|
423
|
+
const decl_node = symbol.valueDeclaration || symbol.declarations?.[0];
|
|
424
|
+
|
|
425
|
+
// Determine kind (fallback to 'variable' if no declaration node)
|
|
426
|
+
const kind = decl_node ? ts_infer_declaration_kind(symbol, decl_node) : 'variable';
|
|
427
|
+
|
|
428
|
+
const result: DeclarationJson = {
|
|
429
|
+
name,
|
|
430
|
+
kind,
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
if (!decl_node) {
|
|
434
|
+
return {declaration: result, nodocs: false};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Extract TSDoc
|
|
438
|
+
const tsdoc = tsdoc_parse(decl_node, source_file);
|
|
439
|
+
const nodocs = tsdoc?.nodocs ?? false;
|
|
440
|
+
tsdoc_apply_to_declaration(result, tsdoc);
|
|
441
|
+
|
|
442
|
+
// Extract source line
|
|
443
|
+
const start = decl_node.getStart(source_file);
|
|
444
|
+
const start_pos = source_file.getLineAndCharacterOfPosition(start);
|
|
445
|
+
result.source_line = start_pos.line + 1;
|
|
446
|
+
|
|
447
|
+
// Extract type-specific info
|
|
448
|
+
if (result.kind === 'function') {
|
|
449
|
+
ts_extract_function_info(decl_node, symbol, checker, result, tsdoc);
|
|
450
|
+
} else if (result.kind === 'type') {
|
|
451
|
+
ts_extract_type_info(decl_node, symbol, checker, result);
|
|
452
|
+
} else if (result.kind === 'class') {
|
|
453
|
+
ts_extract_class_info(decl_node, symbol, checker, result);
|
|
454
|
+
} else if (result.kind === 'variable') {
|
|
455
|
+
ts_extract_variable_info(decl_node, symbol, checker, result);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return {declaration: result, nodocs};
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Information about a same-name re-export.
|
|
463
|
+
* Used for post-processing to build `also_exported_from` arrays.
|
|
464
|
+
*/
|
|
465
|
+
export interface ReExportInfo {
|
|
466
|
+
/** Name of the re-exported declaration. */
|
|
467
|
+
name: string;
|
|
468
|
+
/** Module path (relative to src/lib) where the declaration is originally declared. */
|
|
469
|
+
original_module: string;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Result of analyzing a module's exports.
|
|
474
|
+
*/
|
|
475
|
+
export interface ModuleExportsAnalysis {
|
|
476
|
+
/** Module-level documentation comment. */
|
|
477
|
+
module_comment?: string;
|
|
478
|
+
/** All exported declarations with their metadata (excludes same-name re-exports). */
|
|
479
|
+
declarations: Array<DeclarationJson>;
|
|
480
|
+
/** Same-name re-exports (for building also_exported_from in post-processing). */
|
|
481
|
+
re_exports: Array<ReExportInfo>;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Analyze all exports from a TypeScript source file.
|
|
486
|
+
*
|
|
487
|
+
* Extracts the module-level comment and all exported declarations with
|
|
488
|
+
* complete metadata. Handles re-exports by:
|
|
489
|
+
* - Same-name re-exports: tracked in `re_exports` for `also_exported_from` building
|
|
490
|
+
* - Renamed re-exports: included as new declarations with `alias_of` metadata
|
|
491
|
+
*
|
|
492
|
+
* This is a high-level function suitable for building documentation, API explorers, or analysis tools.
|
|
493
|
+
*
|
|
494
|
+
* @param source_file The TypeScript source file to analyze
|
|
495
|
+
* @param checker The TypeScript type checker
|
|
496
|
+
* @returns Module comment, array of analyzed declarations, and re-export information
|
|
497
|
+
*/
|
|
498
|
+
export const ts_analyze_module_exports = (
|
|
499
|
+
source_file: ts.SourceFile,
|
|
500
|
+
checker: ts.TypeChecker,
|
|
501
|
+
): ModuleExportsAnalysis => {
|
|
502
|
+
const declarations: Array<DeclarationJson> = [];
|
|
503
|
+
const re_exports: Array<ReExportInfo> = [];
|
|
504
|
+
|
|
505
|
+
// Extract module-level comment
|
|
506
|
+
const module_comment = ts_extract_module_comment(source_file);
|
|
507
|
+
|
|
508
|
+
// Get all exported symbols
|
|
509
|
+
const symbol = checker.getSymbolAtLocation(source_file);
|
|
510
|
+
if (symbol) {
|
|
511
|
+
const exports = checker.getExportsOfModule(symbol);
|
|
512
|
+
for (const export_symbol of exports) {
|
|
513
|
+
// Check if this is an alias (potential re-export) using the Alias flag
|
|
514
|
+
const is_alias = (export_symbol.flags & ts.SymbolFlags.Alias) !== 0;
|
|
515
|
+
|
|
516
|
+
if (is_alias) {
|
|
517
|
+
// This might be a re-export - use getAliasedSymbol to find the original
|
|
518
|
+
const aliased_symbol = checker.getAliasedSymbol(export_symbol);
|
|
519
|
+
const aliased_decl = aliased_symbol.valueDeclaration || aliased_symbol.declarations?.[0];
|
|
520
|
+
|
|
521
|
+
if (aliased_decl) {
|
|
522
|
+
const original_source = aliased_decl.getSourceFile();
|
|
523
|
+
|
|
524
|
+
// Check if this is a CROSS-FILE re-export (original in different file)
|
|
525
|
+
if (original_source.fileName !== source_file.fileName) {
|
|
526
|
+
// Only track if the original is from a source module (not node_modules)
|
|
527
|
+
if (module_matches_source(original_source.fileName)) {
|
|
528
|
+
const original_module = module_extract_path(original_source.fileName);
|
|
529
|
+
const original_name = aliased_symbol.name;
|
|
530
|
+
const is_renamed = export_symbol.name !== original_name;
|
|
531
|
+
|
|
532
|
+
if (is_renamed) {
|
|
533
|
+
// Renamed re-export (export {foo as bar}) - create new declaration with alias_of
|
|
534
|
+
const kind = ts_infer_declaration_kind(aliased_symbol, aliased_decl);
|
|
535
|
+
const decl: DeclarationJson = {
|
|
536
|
+
name: export_symbol.name,
|
|
537
|
+
kind,
|
|
538
|
+
alias_of: {module: original_module, name: original_name},
|
|
539
|
+
};
|
|
540
|
+
declarations.push(decl);
|
|
541
|
+
} else {
|
|
542
|
+
// Same-name re-export - track for also_exported_from, skip from declarations
|
|
543
|
+
re_exports.push({
|
|
544
|
+
name: export_symbol.name,
|
|
545
|
+
original_module,
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
// Re-export from external module (node_modules) - skip entirely
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
// Within-file alias (export { x as y }) - fall through to normal analysis
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Normal export or within-file alias - declared in this file
|
|
558
|
+
const {declaration, nodocs} = ts_analyze_declaration(export_symbol, source_file, checker);
|
|
559
|
+
// Skip @nodocs declarations - they're excluded from documentation
|
|
560
|
+
if (nodocs) continue;
|
|
561
|
+
declarations.push(declaration);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return {
|
|
566
|
+
module_comment,
|
|
567
|
+
declarations,
|
|
568
|
+
re_exports,
|
|
569
|
+
};
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Extract module-level comment.
|
|
574
|
+
*
|
|
575
|
+
* Only accepts JSDoc/TSDoc comments (`/** ... *\/`) followed by a blank line to distinguish
|
|
576
|
+
* them from identifier-level comments. This prevents accidentally treating function/class
|
|
577
|
+
* comments as module comments. Module comments can appear after imports.
|
|
578
|
+
*/
|
|
579
|
+
export const ts_extract_module_comment = (source_file: ts.SourceFile): string | undefined => {
|
|
580
|
+
const full_text = source_file.getFullText();
|
|
581
|
+
|
|
582
|
+
// Check for comments at the start of the file (before any statements)
|
|
583
|
+
const leading_comments = ts.getLeadingCommentRanges(full_text, 0);
|
|
584
|
+
if (leading_comments?.length) {
|
|
585
|
+
for (const comment of leading_comments) {
|
|
586
|
+
const comment_text = full_text.substring(comment.pos, comment.end);
|
|
587
|
+
if (!comment_text.trimStart().startsWith('/**')) continue;
|
|
588
|
+
|
|
589
|
+
// Check if there's a blank line after this comment
|
|
590
|
+
const first_statement = source_file.statements[0];
|
|
591
|
+
if (first_statement) {
|
|
592
|
+
const between = full_text.substring(comment.end, first_statement.getStart());
|
|
593
|
+
if (between.includes('\n\n')) {
|
|
594
|
+
return extract_and_clean_jsdoc(full_text, comment);
|
|
595
|
+
}
|
|
596
|
+
} else {
|
|
597
|
+
// No statements, just return the comment
|
|
598
|
+
return extract_and_clean_jsdoc(full_text, comment);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Check for comments before each statement (e.g., after imports)
|
|
604
|
+
for (const statement of source_file.statements) {
|
|
605
|
+
const statement_start = statement.getFullStart();
|
|
606
|
+
const statement_pos = statement.getStart();
|
|
607
|
+
|
|
608
|
+
// Get comments in the trivia before this statement
|
|
609
|
+
const comments = ts.getLeadingCommentRanges(full_text, statement_start);
|
|
610
|
+
if (!comments?.length) continue;
|
|
611
|
+
|
|
612
|
+
for (const comment of comments) {
|
|
613
|
+
const comment_text = full_text.substring(comment.pos, comment.end);
|
|
614
|
+
if (!comment_text.trimStart().startsWith('/**')) continue;
|
|
615
|
+
|
|
616
|
+
// Check if there's a blank line between comment and statement
|
|
617
|
+
const between = full_text.substring(comment.end, statement_pos);
|
|
618
|
+
if (between.includes('\n\n')) {
|
|
619
|
+
return extract_and_clean_jsdoc(full_text, comment);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
return undefined;
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Extract and clean JSDoc comment text.
|
|
629
|
+
*/
|
|
630
|
+
const extract_and_clean_jsdoc = (
|
|
631
|
+
full_text: string,
|
|
632
|
+
comment: {pos: number; end: number},
|
|
633
|
+
): string | undefined => {
|
|
634
|
+
let text = full_text.substring(comment.pos, comment.end);
|
|
635
|
+
|
|
636
|
+
// Clean comment markers
|
|
637
|
+
text = text
|
|
638
|
+
.replace(/^\/\*\*/, '')
|
|
639
|
+
.replace(/\*\/$/, '')
|
|
640
|
+
.split('\n')
|
|
641
|
+
.map((line) => line.replace(/^\s*\*\s?/, ''))
|
|
642
|
+
.join('\n')
|
|
643
|
+
.trim();
|
|
644
|
+
|
|
645
|
+
return text || undefined;
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Create TypeScript program for analysis.
|
|
650
|
+
*/
|
|
651
|
+
export const ts_create_program = (log: {warn: (message: string) => void}): ts.Program | null => {
|
|
652
|
+
const config_path = ts.findConfigFile('./', ts.sys.fileExists, 'tsconfig.json');
|
|
653
|
+
if (!config_path) {
|
|
654
|
+
log.warn('No tsconfig.json found');
|
|
655
|
+
return null;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
const config_file = ts.readConfigFile(config_path, ts.sys.readFile);
|
|
659
|
+
const parsed_config = ts.parseJsonConfigFileContent(config_file.config, ts.sys, './');
|
|
660
|
+
|
|
661
|
+
return ts.createProgram(parsed_config.fileNames, parsed_config.options);
|
|
662
|
+
};
|