@rimelight/ui 0.0.33 → 0.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +157 -157
- package/package.json +105 -105
- package/src/components/astro/RLAAspectRatio.astro +2 -2
- package/src/components/astro/RLAButton.astro +141 -139
- package/src/components/astro/RLAChart.astro +19 -34
- package/src/components/astro/RLAHead.astro +427 -196
- package/src/components/astro/RLAHeader.astro +66 -59
- package/src/components/astro/RLAImage.astro +61 -19
- package/src/components/astro/RLALink.astro +22 -23
- package/src/components/astro/RLALinkGroup.astro +66 -4
- package/src/components/astro/RLALocaleSelector.astro +136 -113
- package/src/components/astro/RLAMain.astro +8 -3
- package/src/components/astro/RLAMarquee.astro +9 -2
- package/src/components/astro/RLANavigationMenu.astro +245 -56
- package/src/components/astro/RLAPagination.astro +2 -4
- package/src/components/astro/RLAProgress.astro +8 -2
- package/src/components/astro/RLASelect.astro +38 -24
- package/src/components/astro/RLASlideover.astro +66 -1
- package/src/components/astro/RLASlider.astro +14 -14
- package/src/components/astro/RLASplitter.astro +14 -2
- package/src/components/astro/RLAStickySurface.astro +9 -2
- package/src/components/vue/RLVCheckbox.vue +68 -68
- package/src/components/vue/RLVForm.vue +44 -44
- package/src/components/vue/RLVFormField.vue +58 -58
- package/src/components/vue/RLVInput.vue +93 -93
- package/src/components/vue/RLVPost.vue +63 -63
- package/src/components/vue/RLVPosts.vue +21 -21
- package/src/components/vue/RLVToast.vue +68 -68
- package/src/components/vue/RLVToaster.vue +29 -29
- package/src/composables/index.ts +3 -3
- package/src/composables/useHeaderStore.ts +13 -14
- package/src/composables/useScrollToTop.ts +62 -62
- package/src/composables/useToast.ts +43 -42
- package/src/config/index.ts +1 -0
- package/src/config/security.ts +220 -0
- package/src/integrations/ui.ts +199 -199
- package/src/middleware/security.ts +75 -15
- package/src/plugins/index.ts +2 -2
- package/src/plugins/starlightDocsApi/index.ts +272 -272
- package/src/presets/defaults.ts +297 -297
- package/src/presets/index.ts +683 -637
- package/src/styles/index.css +160 -160
- package/src/themes/3d-hover.theme.ts +13 -13
- package/src/themes/accordion.theme.ts +13 -13
- package/src/themes/alert.theme.ts +131 -125
- package/src/themes/app.theme.ts +13 -13
- package/src/themes/audio.theme.ts +13 -13
- package/src/themes/avatar.theme.ts +84 -84
- package/src/themes/badge.theme.ts +169 -163
- package/src/themes/banner.theme.ts +13 -13
- package/src/themes/browser-mockup.theme.ts +13 -13
- package/src/themes/button.theme.ts +224 -202
- package/src/themes/calendar.theme.ts +13 -13
- package/src/themes/card.theme.ts +42 -42
- package/src/themes/changelog.theme.ts +13 -13
- package/src/themes/chart.theme.ts +22 -22
- package/src/themes/chat-message.theme.ts +13 -13
- package/src/themes/chat-messages.theme.ts +13 -13
- package/src/themes/chat-palette.theme.ts +13 -13
- package/src/themes/chat-prompt-submit.theme.ts +13 -13
- package/src/themes/chat-prompt.theme.ts +13 -13
- package/src/themes/chat-reasoning.theme.ts +13 -13
- package/src/themes/chat-shimmer.theme.ts +13 -13
- package/src/themes/chat-tool.theme.ts +13 -13
- package/src/themes/chat.theme.ts +13 -13
- package/src/themes/checkbox.theme.ts +50 -50
- package/src/themes/chip.theme.ts +74 -74
- package/src/themes/color-picker.theme.ts +13 -13
- package/src/themes/command-palette.theme.ts +13 -13
- package/src/themes/compare.theme.ts +13 -13
- package/src/themes/confirm.theme.ts +48 -48
- package/src/themes/container.theme.ts +13 -13
- package/src/themes/context-menu.theme.ts +13 -13
- package/src/themes/dashboard-group.theme.ts +13 -13
- package/src/themes/dashboard-navbar.theme.ts +13 -13
- package/src/themes/dashboard-panel.theme.ts +13 -13
- package/src/themes/dashboard-resize-handle.theme.ts +13 -13
- package/src/themes/dashboard-search-button.theme.ts +13 -13
- package/src/themes/dashboard-search.theme.ts +13 -13
- package/src/themes/dashboard-sidebar-collapse.theme.ts +13 -13
- package/src/themes/dashboard-sidebar-toggle.theme.ts +13 -13
- package/src/themes/dashboard-sidebar.theme.ts +13 -13
- package/src/themes/dashboard-toolbar.theme.ts +13 -13
- package/src/themes/dock.theme.ts +13 -13
- package/src/themes/drawer.theme.ts +61 -61
- package/src/themes/dropdown-menu.theme.ts +29 -29
- package/src/themes/editor.theme.ts +13 -13
- package/src/themes/empty.theme.ts +13 -13
- package/src/themes/error.theme.ts +13 -13
- package/src/themes/field-group.theme.ts +24 -24
- package/src/themes/fieldset.theme.ts +13 -13
- package/src/themes/floating-action.theme.ts +13 -13
- package/src/themes/footer.theme.ts +30 -30
- package/src/themes/form.theme.ts +13 -13
- package/src/themes/gallery.theme.ts +13 -13
- package/src/themes/head.theme.ts +13 -13
- package/src/themes/header.theme.ts +45 -46
- package/src/themes/icon.theme.ts +23 -23
- package/src/themes/image.theme.ts +23 -19
- package/src/themes/input-date.theme.ts +13 -13
- package/src/themes/input-menu.theme.ts +35 -35
- package/src/themes/input-number.theme.ts +13 -13
- package/src/themes/input-tags.theme.ts +13 -13
- package/src/themes/input-time.theme.ts +13 -13
- package/src/themes/input.theme.ts +42 -42
- package/src/themes/kbd.theme.ts +95 -89
- package/src/themes/knob.theme.ts +13 -13
- package/src/themes/link-group.theme.ts +32 -32
- package/src/themes/link.theme.ts +13 -13
- package/src/themes/listbox.theme.ts +13 -13
- package/src/themes/locale-selector.theme.ts +49 -49
- package/src/themes/logo.theme.ts +13 -13
- package/src/themes/main.theme.ts +13 -13
- package/src/themes/megamenu.theme.ts +13 -13
- package/src/themes/meter.theme.ts +13 -13
- package/src/themes/navigation-menu.theme.ts +44 -27
- package/src/themes/page-aside.theme.ts +13 -13
- package/src/themes/page-body.theme.ts +13 -13
- package/src/themes/page-header.theme.ts +13 -13
- package/src/themes/page-section.theme.ts +187 -187
- package/src/themes/page.theme.ts +13 -13
- package/src/themes/password.theme.ts +13 -13
- package/src/themes/phone-mockup.theme.ts +13 -13
- package/src/themes/placeholder.theme.ts +15 -15
- package/src/themes/post.theme.ts +33 -33
- package/src/themes/pricing-plan.theme.ts +13 -13
- package/src/themes/pricing-table.theme.ts +13 -13
- package/src/themes/progress.theme.ts +38 -38
- package/src/themes/prose.theme.ts +13 -13
- package/src/themes/qr-code.theme.ts +13 -13
- package/src/themes/quote.theme.ts +13 -13
- package/src/themes/radio-group.theme.ts +13 -13
- package/src/themes/rating.theme.ts +13 -13
- package/src/themes/scroll-area.theme.ts +26 -26
- package/src/themes/scroll-to-top.theme.ts +52 -52
- package/src/themes/select.theme.ts +29 -29
- package/src/themes/separator.theme.ts +147 -147
- package/src/themes/sidebar.theme.ts +38 -38
- package/src/themes/skeleton.theme.ts +21 -21
- package/src/themes/slideover.theme.ts +4 -4
- package/src/themes/slider.theme.ts +36 -36
- package/src/themes/speed-dial.theme.ts +13 -13
- package/src/themes/spinner.theme.ts +34 -34
- package/src/themes/splitter.theme.ts +23 -23
- package/src/themes/spoiler.theme.ts +13 -13
- package/src/themes/stepper.theme.ts +13 -13
- package/src/themes/switch.theme.ts +67 -67
- package/src/themes/table-of-contents.theme.ts +48 -48
- package/src/themes/tabs.theme.ts +52 -52
- package/src/themes/textarea.theme.ts +27 -27
- package/src/themes/theme-selector.theme.ts +15 -15
- package/src/themes/timeline.theme.ts +13 -13
- package/src/themes/toast.theme.ts +31 -31
- package/src/themes/tooltip.theme.ts +28 -28
- package/src/themes/tour.theme.ts +13 -13
- package/src/themes/tree.theme.ts +13 -13
- package/src/themes/user.theme.ts +13 -13
- package/src/themes/video.theme.ts +17 -17
- package/src/themes/watermark.theme.ts +13 -13
- package/src/themes/window-mockup.theme.ts +13 -13
- package/src/types/components/3d-hover.ts +11 -11
- package/src/types/components/alert.ts +2 -2
- package/src/types/components/app.ts +11 -11
- package/src/types/components/audio.ts +11 -11
- package/src/types/components/avatar-group.ts +11 -11
- package/src/types/components/avatar.ts +60 -60
- package/src/types/components/badge.ts +2 -2
- package/src/types/components/banner.ts +11 -11
- package/src/types/components/browser-mockup.ts +11 -11
- package/src/types/components/button.ts +74 -81
- package/src/types/components/calendar.ts +11 -11
- package/src/types/components/changelog.ts +11 -11
- package/src/types/components/chart.ts +2 -1
- package/src/types/components/chat-message.ts +11 -11
- package/src/types/components/chat-messages.ts +11 -11
- package/src/types/components/chat-palette.ts +11 -11
- package/src/types/components/chat-prompt-submit.ts +11 -11
- package/src/types/components/chat-prompt.ts +11 -11
- package/src/types/components/chat-reasoning.ts +11 -11
- package/src/types/components/chat-shimmer.ts +11 -11
- package/src/types/components/chat-tool.ts +11 -11
- package/src/types/components/chat.ts +11 -11
- package/src/types/components/checkbox.ts +2 -2
- package/src/types/components/chip.ts +17 -17
- package/src/types/components/collapsible.ts +2 -1
- package/src/types/components/color-picker.ts +11 -11
- package/src/types/components/command-palette.ts +11 -11
- package/src/types/components/compare.ts +11 -11
- package/src/types/components/container.ts +17 -17
- package/src/types/components/context-menu.ts +11 -11
- package/src/types/components/dashboard-group.ts +11 -11
- package/src/types/components/dashboard-navbar.ts +11 -11
- package/src/types/components/dashboard-panel.ts +11 -11
- package/src/types/components/dashboard-resize-handle.ts +11 -11
- package/src/types/components/dashboard-search-button.ts +11 -11
- package/src/types/components/dashboard-search.ts +11 -11
- package/src/types/components/dashboard-sidebar-collapse.ts +11 -11
- package/src/types/components/dashboard-sidebar-toggle.ts +11 -11
- package/src/types/components/dashboard-sidebar.ts +11 -11
- package/src/types/components/dashboard-toolbar.ts +11 -11
- package/src/types/components/dock.ts +11 -11
- package/src/types/components/dropdown-menu.ts +2 -1
- package/src/types/components/editor.ts +11 -11
- package/src/types/components/empty.ts +11 -11
- package/src/types/components/error.ts +11 -11
- package/src/types/components/fieldset.ts +11 -11
- package/src/types/components/file-upload.ts +2 -1
- package/src/types/components/floating-action.ts +11 -11
- package/src/types/components/form.ts +19 -19
- package/src/types/components/gallery.ts +11 -11
- package/src/types/components/icon.ts +19 -19
- package/src/types/components/image.ts +32 -28
- package/src/types/components/index.ts +134 -135
- package/src/types/components/input-date.ts +11 -11
- package/src/types/components/input-menu.ts +44 -43
- package/src/types/components/input-number.ts +11 -11
- package/src/types/components/input-pin.ts +36 -35
- package/src/types/components/input-tags.ts +11 -11
- package/src/types/components/input-time.ts +11 -11
- package/src/types/components/input.ts +2 -2
- package/src/types/components/knob.ts +11 -11
- package/src/types/components/link-group.ts +45 -23
- package/src/types/components/link.ts +4 -0
- package/src/types/components/listbox.ts +11 -11
- package/src/types/components/locale-selector.ts +33 -33
- package/src/types/components/logo.ts +32 -31
- package/src/types/components/main.ts +17 -17
- package/src/types/components/megamenu.ts +11 -11
- package/src/types/components/meter.ts +11 -11
- package/src/types/components/modal.ts +39 -39
- package/src/types/components/navigation-menu.ts +59 -54
- package/src/types/components/page-aside.ts +11 -11
- package/src/types/components/page-body.ts +11 -11
- package/src/types/components/page-header.ts +11 -11
- package/src/types/components/page.ts +11 -11
- package/src/types/components/password.ts +11 -11
- package/src/types/components/phone-mockup.ts +5 -5
- package/src/types/components/pin-input.ts +11 -11
- package/src/types/components/popover.ts +23 -23
- package/src/types/components/post.ts +35 -35
- package/src/types/components/pricing-plan.ts +11 -11
- package/src/types/components/pricing-table.ts +11 -11
- package/src/types/components/progress.ts +33 -33
- package/src/types/components/prose.ts +11 -11
- package/src/types/components/qr-code.ts +11 -11
- package/src/types/components/radio-group.ts +11 -11
- package/src/types/components/rating.ts +11 -11
- package/src/types/components/scroll-area.ts +23 -23
- package/src/types/components/scroll-to-top.ts +2 -2
- package/src/types/components/select.ts +2 -1
- package/src/types/components/separator.ts +19 -19
- package/src/types/components/sidebar.ts +87 -72
- package/src/types/components/slideover.ts +31 -31
- package/src/types/components/slider.ts +2 -2
- package/src/types/components/speed-dial.ts +11 -11
- package/src/types/components/spinner.ts +25 -25
- package/src/types/components/splitter.ts +19 -19
- package/src/types/components/spoiler.ts +11 -11
- package/src/types/components/stepper.ts +11 -11
- package/src/types/components/sticky-surface.ts +19 -19
- package/src/types/components/switch.ts +41 -41
- package/src/types/components/table-of-contents.ts +17 -17
- package/src/types/components/tabs.ts +2 -1
- package/src/types/components/textarea.ts +2 -1
- package/src/types/components/theme-selector.ts +15 -15
- package/src/types/components/timeline.ts +11 -11
- package/src/types/components/toast.ts +2 -1
- package/src/types/components/tooltip.ts +23 -23
- package/src/types/components/tour.ts +11 -11
- package/src/types/components/tree.ts +11 -11
- package/src/types/components/user.ts +11 -11
- package/src/types/components/watermark.ts +11 -11
- package/src/types/components/window-mockup.ts +11 -11
- package/src/types/theme.ts +10 -0
- package/src/utils/docs.ts +215 -189
- package/src/utils/headerStack.ts +95 -95
- package/src/utils/index.ts +5 -5
- package/src/components/astro/RLABlogPost.astro +0 -7
- package/src/components/astro/RLABlogPosts.astro +0 -9
- package/src/components/astro/RLAPosts.astro +0 -23
- package/src/themes/posts.theme.ts +0 -13
- package/src/types/components/posts.ts +0 -11
|
@@ -38,28 +38,75 @@ export const security = defineMiddleware(async (context, next) => {
|
|
|
38
38
|
return addSecurityHeaders(response)
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
const cspHeader = response.headers.get("content-security-policy")
|
|
42
|
+
const nonceValue = cspHeader ? crypto.randomUUID().replace(/-/g, "") : null
|
|
43
|
+
const newHeaders = new Headers(response.headers)
|
|
44
|
+
|
|
45
|
+
if (cspHeader && nonceValue) {
|
|
46
|
+
let newCsp = cspHeader
|
|
47
|
+
if (newCsp.includes("script-src ")) {
|
|
48
|
+
newCsp = newCsp.replace("script-src ", `script-src 'nonce-${nonceValue}' `)
|
|
49
|
+
} else {
|
|
50
|
+
newCsp = newCsp + `; script-src 'nonce-${nonceValue}'`
|
|
51
|
+
}
|
|
52
|
+
if (newCsp.includes("style-src ")) {
|
|
53
|
+
newCsp = newCsp.replace("style-src ", `style-src 'nonce-${nonceValue}' `)
|
|
54
|
+
} else {
|
|
55
|
+
newCsp = newCsp + `; style-src 'nonce-${nonceValue}'`
|
|
56
|
+
}
|
|
57
|
+
newHeaders.set("content-security-policy", newCsp)
|
|
58
|
+
}
|
|
59
|
+
|
|
41
60
|
if (typeof HTMLRewriter !== "undefined") {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
61
|
+
let rewriter = new HTMLRewriter()
|
|
62
|
+
.on("script[src]", {
|
|
63
|
+
element(el: any) {
|
|
64
|
+
const src = el.getAttribute("src")
|
|
65
|
+
if (src && manifest[src]) {
|
|
66
|
+
el.setAttribute("integrity", manifest[src])
|
|
67
|
+
el.setAttribute("crossorigin", "anonymous")
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
.on('link[rel="stylesheet"][href]', {
|
|
72
|
+
element(el: any) {
|
|
73
|
+
const href = el.getAttribute("href")
|
|
74
|
+
if (href && manifest[href]) {
|
|
75
|
+
el.setAttribute("integrity", manifest[href])
|
|
76
|
+
el.setAttribute("crossorigin", "anonymous")
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
if (nonceValue) {
|
|
82
|
+
rewriter = rewriter
|
|
83
|
+
.on("head", {
|
|
45
84
|
element(el: any) {
|
|
46
|
-
|
|
47
|
-
if (src && manifest[src]) {
|
|
48
|
-
el.setAttribute("integrity", manifest[src])
|
|
49
|
-
el.setAttribute("crossorigin", "anonymous")
|
|
50
|
-
}
|
|
85
|
+
el.append(`<meta name="csp-nonce" content="${nonceValue}">`, { html: true })
|
|
51
86
|
}
|
|
52
87
|
})
|
|
53
|
-
.on(
|
|
88
|
+
.on("script", {
|
|
54
89
|
element(el: any) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
el.setAttribute("integrity", manifest[href])
|
|
58
|
-
el.setAttribute("crossorigin", "anonymous")
|
|
90
|
+
if (!el.getAttribute("nonce")) {
|
|
91
|
+
el.setAttribute("nonce", nonceValue)
|
|
59
92
|
}
|
|
60
93
|
}
|
|
61
94
|
})
|
|
62
|
-
.
|
|
95
|
+
.on("style", {
|
|
96
|
+
element(el: any) {
|
|
97
|
+
el.setAttribute("nonce", nonceValue)
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return addSecurityHeaders(
|
|
103
|
+
rewriter.transform(
|
|
104
|
+
new Response(response.body, {
|
|
105
|
+
status: response.status,
|
|
106
|
+
statusText: response.statusText,
|
|
107
|
+
headers: newHeaders
|
|
108
|
+
})
|
|
109
|
+
)
|
|
63
110
|
)
|
|
64
111
|
}
|
|
65
112
|
|
|
@@ -84,11 +131,24 @@ export const security = defineMiddleware(async (context, next) => {
|
|
|
84
131
|
)
|
|
85
132
|
}
|
|
86
133
|
|
|
134
|
+
if (nonceValue) {
|
|
135
|
+
modifiedHtml = modifiedHtml.replace(/<head([^>]*)>/i, `<head$1>\n<meta name="csp-nonce" content="${nonceValue}">`)
|
|
136
|
+
modifiedHtml = modifiedHtml.replace(
|
|
137
|
+
/<script(?![^>]*nonce=)([^>]*)>/gi,
|
|
138
|
+
`<script$1 nonce="${nonceValue}">`
|
|
139
|
+
)
|
|
140
|
+
modifiedHtml = modifiedHtml.replace(
|
|
141
|
+
/<style(?![^>]*nonce=)([^>]*)>/gi,
|
|
142
|
+
`<style$1 nonce="${nonceValue}">`
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
87
146
|
return addSecurityHeaders(
|
|
88
147
|
new Response(modifiedHtml, {
|
|
89
148
|
status: response.status,
|
|
90
149
|
statusText: response.statusText,
|
|
91
|
-
headers:
|
|
150
|
+
headers: newHeaders
|
|
92
151
|
})
|
|
93
152
|
)
|
|
94
153
|
})
|
|
154
|
+
|
package/src/plugins/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { default as starlightDocsApi } from "./starlightDocsApi"
|
|
2
|
-
export type { StarlightDocsApiConfig } from "./starlightDocsApi"
|
|
1
|
+
export { default as starlightDocsApi } from "./starlightDocsApi"
|
|
2
|
+
export type { StarlightDocsApiConfig } from "./starlightDocsApi"
|
|
@@ -1,272 +1,272 @@
|
|
|
1
|
-
import type { StarlightPlugin } from "@astrojs/starlight/types"
|
|
2
|
-
import virtual from "vite-plugin-virtual"
|
|
3
|
-
import path from "node:path"
|
|
4
|
-
import { fileURLToPath } from "node:url"
|
|
5
|
-
import { readFileSync, existsSync, readdirSync } from "node:fs"
|
|
6
|
-
import ts from "typescript"
|
|
7
|
-
import type { ComponentMeta, PropMeta, SlotMeta, EmitMeta, ThemeMeta } from "./types"
|
|
8
|
-
import { extractVueMetaDirect } from "./extract-vue"
|
|
9
|
-
|
|
10
|
-
const pathDirname = path.dirname(fileURLToPath(import.meta.url))
|
|
11
|
-
const uiRoot = path.resolve(pathDirname, "..", "..", "..")
|
|
12
|
-
|
|
13
|
-
function pascalToKebab(name: string): string {
|
|
14
|
-
return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase()
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function extractAstroPropsFromInterface(sourceFile: ts.SourceFile): PropMeta[] {
|
|
18
|
-
const props: PropMeta[] = []
|
|
19
|
-
|
|
20
|
-
function visit(node: ts.Node) {
|
|
21
|
-
if (
|
|
22
|
-
(ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) &&
|
|
23
|
-
node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
|
|
24
|
-
) {
|
|
25
|
-
const name = node.name.text
|
|
26
|
-
if (name.endsWith("Props") || name === "Props") {
|
|
27
|
-
if (ts.isInterfaceDeclaration(node)) {
|
|
28
|
-
for (const member of node.members) {
|
|
29
|
-
if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
|
|
30
|
-
const propName = member.name.text
|
|
31
|
-
if (propName === "class" || propName.startsWith("data-")) continue
|
|
32
|
-
const type = member.type ? member.type.getText().replace(/^[^:]+:\s*/, "") : "unknown"
|
|
33
|
-
const isOptional = member.questionToken !== undefined
|
|
34
|
-
props.push({ name: propName, type, required: !isOptional, description: "" })
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
} else if (ts.isTypeAliasDeclaration(node) && ts.isTypeLiteralNode(node.type)) {
|
|
38
|
-
for (const member of node.type.members) {
|
|
39
|
-
if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
|
|
40
|
-
const propName = member.name.text
|
|
41
|
-
if (propName === "class" || propName.startsWith("data-")) continue
|
|
42
|
-
const type = member.type ? member.type.getText().replace(/^[^:]+:\s*/, "") : "unknown"
|
|
43
|
-
const isOptional = member.questionToken !== undefined
|
|
44
|
-
props.push({ name: propName, type, required: !isOptional, description: "" })
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
ts.forEachChild(node, visit)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
visit(sourceFile)
|
|
54
|
-
return props
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function extractAstroMeta(filePath: string): { props: PropMeta[]; slots: SlotMeta[] } {
|
|
58
|
-
const content = readFileSync(filePath, "utf-8")
|
|
59
|
-
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/)
|
|
60
|
-
if (!frontmatterMatch) return { props: [], slots: [] }
|
|
61
|
-
|
|
62
|
-
const frontmatter = frontmatterMatch[1]
|
|
63
|
-
const sourceFile = ts.createSourceFile(
|
|
64
|
-
"component.ts",
|
|
65
|
-
frontmatter ?? "",
|
|
66
|
-
ts.ScriptTarget.Latest,
|
|
67
|
-
true,
|
|
68
|
-
ts.ScriptKind.TS
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
const props = extractAstroPropsFromInterface(sourceFile)
|
|
72
|
-
|
|
73
|
-
const slots: SlotMeta[] = []
|
|
74
|
-
const slotMatches = content.matchAll(/<slot(?:\s+name=["']([^"']+)["'])?/g)
|
|
75
|
-
for (const match of slotMatches) {
|
|
76
|
-
slots.push({ name: match[1] ?? "default", bindings: {}, description: "" })
|
|
77
|
-
}
|
|
78
|
-
if (!slots.length) slots.push({ name: "default", bindings: {}, description: "" })
|
|
79
|
-
|
|
80
|
-
return { props, slots }
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function safeParse(code: string): unknown {
|
|
84
|
-
try {
|
|
85
|
-
const normalized = code
|
|
86
|
-
.replace(/'/g, '"')
|
|
87
|
-
.replace(/,\s*\}/g, "}")
|
|
88
|
-
.replace(/,\s*\]/g, "]")
|
|
89
|
-
return JSON.parse(normalized)
|
|
90
|
-
} catch {
|
|
91
|
-
return {}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function isStringOrArray(value: unknown): value is string | string[] {
|
|
96
|
-
return typeof value === "string" || Array.isArray(value)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function isStringRecord(value: unknown): value is Record<string, string | string[]> {
|
|
100
|
-
if (typeof value !== "object" || value === null) return false
|
|
101
|
-
return Object.values(value).every(isStringOrArray)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function isVariantRecord(
|
|
105
|
-
value: unknown
|
|
106
|
-
): value is Record<string, Record<string, Record<string, string | string[]>>> {
|
|
107
|
-
if (typeof value !== "object" || value === null) return false
|
|
108
|
-
return Object.values(value).every(
|
|
109
|
-
(v) => typeof v === "object" && v !== null && Object.values(v).every(isStringRecord)
|
|
110
|
-
)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function isCompoundArray(value: unknown): value is Record<string, unknown>[] {
|
|
114
|
-
return Array.isArray(value) && value.every((v) => typeof v === "object" && v !== null)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function isDefaultVariants(value: unknown): value is Record<string, unknown> {
|
|
118
|
-
return typeof value === "object" && value !== null
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function extractThemeMeta(componentName: string, themesDir: string): ThemeMeta | null {
|
|
122
|
-
if (!existsSync(themesDir)) return null
|
|
123
|
-
|
|
124
|
-
const themeFiles = readdirSync(themesDir).filter((f) => f.endsWith(".theme.ts"))
|
|
125
|
-
let themePath: string | null = null
|
|
126
|
-
for (const file of themeFiles) {
|
|
127
|
-
const baseName = file.replace(".theme.ts", "")
|
|
128
|
-
if (baseName === componentName.toLowerCase()) {
|
|
129
|
-
themePath = path.join(themesDir, file)
|
|
130
|
-
break
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (!themePath || !existsSync(themePath)) return null
|
|
135
|
-
|
|
136
|
-
const content = readFileSync(themePath, "utf-8")
|
|
137
|
-
const slotsMatch = content.match(/slots:\s*\[([^\]]*)\]/)
|
|
138
|
-
const baseMatch = content.match(/base:\s*(\{[\s\S]*?\n[ \t]*\})/)
|
|
139
|
-
const variantsMatch = content.match(/variants:\s*(\{[\s\S]*?\n[ \t]*\})/)
|
|
140
|
-
const compoundMatch = content.match(/compoundVariants:\s*(\[[\s\S]*?\n[ \t]*\])/)
|
|
141
|
-
const defaultVariantsMatch = content.match(/defaultVariants:\s*(\{[\s\S]*?\n[ \t]*\})/)
|
|
142
|
-
|
|
143
|
-
const slots = slotsMatch
|
|
144
|
-
? slotsMatch[1]!
|
|
145
|
-
.split(",")
|
|
146
|
-
.map((s) => s.trim().replace(/["']/g, ""))
|
|
147
|
-
.filter(Boolean)
|
|
148
|
-
: []
|
|
149
|
-
|
|
150
|
-
const parsedBase = baseMatch ? safeParse(baseMatch[1]!) : {}
|
|
151
|
-
const parsedVariants = variantsMatch ? safeParse(variantsMatch[1]!) : {}
|
|
152
|
-
const parsedCompound = compoundMatch ? safeParse(compoundMatch[1]!) : []
|
|
153
|
-
const parsedDefaults = defaultVariantsMatch ? safeParse(defaultVariantsMatch[1]!) : {}
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
slots,
|
|
157
|
-
base: isStringRecord(parsedBase) ? parsedBase : {},
|
|
158
|
-
variants: isVariantRecord(parsedVariants) ? parsedVariants : {},
|
|
159
|
-
compoundVariants: isCompoundArray(parsedCompound) ? parsedCompound : [],
|
|
160
|
-
defaultVariants: isDefaultVariants(parsedDefaults) ? parsedDefaults : {}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function getComponentNameFromFilename(filename: string): string {
|
|
165
|
-
return filename.replace(/^(RLA|RLV)/, "").replace(/\.(astro|vue)$/, "")
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function extractAllMeta(): Record<string, ComponentMeta> {
|
|
169
|
-
const componentsDir = path.join(uiRoot, "src", "components")
|
|
170
|
-
const themesDir = path.join(uiRoot, "src", "themes")
|
|
171
|
-
|
|
172
|
-
const vueDir = path.join(componentsDir, "vue")
|
|
173
|
-
const astroDir = path.join(componentsDir, "astro")
|
|
174
|
-
|
|
175
|
-
const vueFiles = existsSync(vueDir)
|
|
176
|
-
? readdirSync(vueDir).filter((f: string) => f.endsWith(".vue"))
|
|
177
|
-
: []
|
|
178
|
-
const astroFiles = existsSync(astroDir)
|
|
179
|
-
? readdirSync(astroDir).filter((f: string) => f.endsWith(".astro") && f !== "Head.astro")
|
|
180
|
-
: []
|
|
181
|
-
|
|
182
|
-
const vueComponents = new Map<string, string>()
|
|
183
|
-
const astroComponents = new Map<string, string>()
|
|
184
|
-
|
|
185
|
-
for (const f of vueFiles) vueComponents.set(getComponentNameFromFilename(f), f)
|
|
186
|
-
for (const f of astroFiles) astroComponents.set(getComponentNameFromFilename(f), f)
|
|
187
|
-
|
|
188
|
-
const allNames = new Set([...vueComponents.keys(), ...astroComponents.keys()])
|
|
189
|
-
const result: Record<string, ComponentMeta> = {}
|
|
190
|
-
|
|
191
|
-
for (const name of [...allNames].toSorted()) {
|
|
192
|
-
const vueFile = vueComponents.get(name)
|
|
193
|
-
const astroFile = astroComponents.get(name)
|
|
194
|
-
|
|
195
|
-
let props: PropMeta[] = []
|
|
196
|
-
let slots: SlotMeta[] = []
|
|
197
|
-
let emits: EmitMeta[] = []
|
|
198
|
-
const frameworks: ("astro" | "vue")[] = []
|
|
199
|
-
|
|
200
|
-
if (vueFile) {
|
|
201
|
-
frameworks.push("vue")
|
|
202
|
-
try {
|
|
203
|
-
const vueMeta = extractVueMetaDirect(path.join(vueDir, vueFile))
|
|
204
|
-
props = vueMeta.props
|
|
205
|
-
slots = vueMeta.slots
|
|
206
|
-
emits = vueMeta.emits
|
|
207
|
-
} catch (err) {
|
|
208
|
-
console.warn(`[starlightDocsApi] Failed to extract Vue meta for ${vueFile}:`, err)
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (astroFile) {
|
|
213
|
-
frameworks.push("astro")
|
|
214
|
-
if (!vueFile) {
|
|
215
|
-
try {
|
|
216
|
-
const astroMeta = extractAstroMeta(path.join(astroDir, astroFile))
|
|
217
|
-
props = astroMeta.props
|
|
218
|
-
slots = astroMeta.slots
|
|
219
|
-
} catch (err) {
|
|
220
|
-
console.warn(`[starlightDocsApi] Failed to extract Astro meta for ${astroFile}:`, err)
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const theme = extractThemeMeta(name, themesDir)
|
|
226
|
-
|
|
227
|
-
result[pascalToKebab(name)] = {
|
|
228
|
-
name,
|
|
229
|
-
astroComponent: astroFile ?? null,
|
|
230
|
-
vueComponent: vueFile ?? null,
|
|
231
|
-
frameworks,
|
|
232
|
-
props,
|
|
233
|
-
slots,
|
|
234
|
-
emits,
|
|
235
|
-
theme
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return result
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
export interface StarlightDocsApiConfig {}
|
|
243
|
-
|
|
244
|
-
export default function starlightDocsApi(_config?: StarlightDocsApiConfig): StarlightPlugin {
|
|
245
|
-
return {
|
|
246
|
-
name: "rimelight-ui-starlight-docs-api",
|
|
247
|
-
hooks: {
|
|
248
|
-
"config:setup"({ addIntegration, logger }) {
|
|
249
|
-
logger.info("Initializing @rimelight/ui docs API")
|
|
250
|
-
|
|
251
|
-
const meta = extractAllMeta()
|
|
252
|
-
|
|
253
|
-
addIntegration({
|
|
254
|
-
name: "rimelight-ui-starlight-docs-api-integration",
|
|
255
|
-
hooks: {
|
|
256
|
-
"astro:config:setup": ({ updateConfig }) => {
|
|
257
|
-
updateConfig({
|
|
258
|
-
vite: {
|
|
259
|
-
plugins: [
|
|
260
|
-
virtual({
|
|
261
|
-
"virtual:rimelight-ui-docs-meta": `export default ${JSON.stringify(meta)}`
|
|
262
|
-
})
|
|
263
|
-
]
|
|
264
|
-
}
|
|
265
|
-
} as Record<string, unknown>)
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
})
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
1
|
+
import type { StarlightPlugin } from "@astrojs/starlight/types"
|
|
2
|
+
import virtual from "vite-plugin-virtual"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
import { fileURLToPath } from "node:url"
|
|
5
|
+
import { readFileSync, existsSync, readdirSync } from "node:fs"
|
|
6
|
+
import ts from "typescript"
|
|
7
|
+
import type { ComponentMeta, PropMeta, SlotMeta, EmitMeta, ThemeMeta } from "./types"
|
|
8
|
+
import { extractVueMetaDirect } from "./extract-vue"
|
|
9
|
+
|
|
10
|
+
const pathDirname = path.dirname(fileURLToPath(import.meta.url))
|
|
11
|
+
const uiRoot = path.resolve(pathDirname, "..", "..", "..")
|
|
12
|
+
|
|
13
|
+
function pascalToKebab(name: string): string {
|
|
14
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function extractAstroPropsFromInterface(sourceFile: ts.SourceFile): PropMeta[] {
|
|
18
|
+
const props: PropMeta[] = []
|
|
19
|
+
|
|
20
|
+
function visit(node: ts.Node) {
|
|
21
|
+
if (
|
|
22
|
+
(ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node)) &&
|
|
23
|
+
node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
|
|
24
|
+
) {
|
|
25
|
+
const name = node.name.text
|
|
26
|
+
if (name.endsWith("Props") || name === "Props") {
|
|
27
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
28
|
+
for (const member of node.members) {
|
|
29
|
+
if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
|
|
30
|
+
const propName = member.name.text
|
|
31
|
+
if (propName === "class" || propName.startsWith("data-")) continue
|
|
32
|
+
const type = member.type ? member.type.getText().replace(/^[^:]+:\s*/, "") : "unknown"
|
|
33
|
+
const isOptional = member.questionToken !== undefined
|
|
34
|
+
props.push({ name: propName, type, required: !isOptional, description: "" })
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} else if (ts.isTypeAliasDeclaration(node) && ts.isTypeLiteralNode(node.type)) {
|
|
38
|
+
for (const member of node.type.members) {
|
|
39
|
+
if (ts.isPropertySignature(member) && member.name && ts.isIdentifier(member.name)) {
|
|
40
|
+
const propName = member.name.text
|
|
41
|
+
if (propName === "class" || propName.startsWith("data-")) continue
|
|
42
|
+
const type = member.type ? member.type.getText().replace(/^[^:]+:\s*/, "") : "unknown"
|
|
43
|
+
const isOptional = member.questionToken !== undefined
|
|
44
|
+
props.push({ name: propName, type, required: !isOptional, description: "" })
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
ts.forEachChild(node, visit)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
visit(sourceFile)
|
|
54
|
+
return props
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function extractAstroMeta(filePath: string): { props: PropMeta[]; slots: SlotMeta[] } {
|
|
58
|
+
const content = readFileSync(filePath, "utf-8")
|
|
59
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/)
|
|
60
|
+
if (!frontmatterMatch) return { props: [], slots: [] }
|
|
61
|
+
|
|
62
|
+
const frontmatter = frontmatterMatch[1]
|
|
63
|
+
const sourceFile = ts.createSourceFile(
|
|
64
|
+
"component.ts",
|
|
65
|
+
frontmatter ?? "",
|
|
66
|
+
ts.ScriptTarget.Latest,
|
|
67
|
+
true,
|
|
68
|
+
ts.ScriptKind.TS
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
const props = extractAstroPropsFromInterface(sourceFile)
|
|
72
|
+
|
|
73
|
+
const slots: SlotMeta[] = []
|
|
74
|
+
const slotMatches = content.matchAll(/<slot(?:\s+name=["']([^"']+)["'])?/g)
|
|
75
|
+
for (const match of slotMatches) {
|
|
76
|
+
slots.push({ name: match[1] ?? "default", bindings: {}, description: "" })
|
|
77
|
+
}
|
|
78
|
+
if (!slots.length) slots.push({ name: "default", bindings: {}, description: "" })
|
|
79
|
+
|
|
80
|
+
return { props, slots }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function safeParse(code: string): unknown {
|
|
84
|
+
try {
|
|
85
|
+
const normalized = code
|
|
86
|
+
.replace(/'/g, '"')
|
|
87
|
+
.replace(/,\s*\}/g, "}")
|
|
88
|
+
.replace(/,\s*\]/g, "]")
|
|
89
|
+
return JSON.parse(normalized)
|
|
90
|
+
} catch {
|
|
91
|
+
return {}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function isStringOrArray(value: unknown): value is string | string[] {
|
|
96
|
+
return typeof value === "string" || Array.isArray(value)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function isStringRecord(value: unknown): value is Record<string, string | string[]> {
|
|
100
|
+
if (typeof value !== "object" || value === null) return false
|
|
101
|
+
return Object.values(value).every(isStringOrArray)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isVariantRecord(
|
|
105
|
+
value: unknown
|
|
106
|
+
): value is Record<string, Record<string, Record<string, string | string[]>>> {
|
|
107
|
+
if (typeof value !== "object" || value === null) return false
|
|
108
|
+
return Object.values(value).every(
|
|
109
|
+
(v) => typeof v === "object" && v !== null && Object.values(v).every(isStringRecord)
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function isCompoundArray(value: unknown): value is Record<string, unknown>[] {
|
|
114
|
+
return Array.isArray(value) && value.every((v) => typeof v === "object" && v !== null)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function isDefaultVariants(value: unknown): value is Record<string, unknown> {
|
|
118
|
+
return typeof value === "object" && value !== null
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function extractThemeMeta(componentName: string, themesDir: string): ThemeMeta | null {
|
|
122
|
+
if (!existsSync(themesDir)) return null
|
|
123
|
+
|
|
124
|
+
const themeFiles = readdirSync(themesDir).filter((f) => f.endsWith(".theme.ts"))
|
|
125
|
+
let themePath: string | null = null
|
|
126
|
+
for (const file of themeFiles) {
|
|
127
|
+
const baseName = file.replace(".theme.ts", "")
|
|
128
|
+
if (baseName === componentName.toLowerCase()) {
|
|
129
|
+
themePath = path.join(themesDir, file)
|
|
130
|
+
break
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!themePath || !existsSync(themePath)) return null
|
|
135
|
+
|
|
136
|
+
const content = readFileSync(themePath, "utf-8")
|
|
137
|
+
const slotsMatch = content.match(/slots:\s*\[([^\]]*)\]/)
|
|
138
|
+
const baseMatch = content.match(/base:\s*(\{[\s\S]*?\n[ \t]*\})/)
|
|
139
|
+
const variantsMatch = content.match(/variants:\s*(\{[\s\S]*?\n[ \t]*\})/)
|
|
140
|
+
const compoundMatch = content.match(/compoundVariants:\s*(\[[\s\S]*?\n[ \t]*\])/)
|
|
141
|
+
const defaultVariantsMatch = content.match(/defaultVariants:\s*(\{[\s\S]*?\n[ \t]*\})/)
|
|
142
|
+
|
|
143
|
+
const slots = slotsMatch
|
|
144
|
+
? slotsMatch[1]!
|
|
145
|
+
.split(",")
|
|
146
|
+
.map((s) => s.trim().replace(/["']/g, ""))
|
|
147
|
+
.filter(Boolean)
|
|
148
|
+
: []
|
|
149
|
+
|
|
150
|
+
const parsedBase = baseMatch ? safeParse(baseMatch[1]!) : {}
|
|
151
|
+
const parsedVariants = variantsMatch ? safeParse(variantsMatch[1]!) : {}
|
|
152
|
+
const parsedCompound = compoundMatch ? safeParse(compoundMatch[1]!) : []
|
|
153
|
+
const parsedDefaults = defaultVariantsMatch ? safeParse(defaultVariantsMatch[1]!) : {}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
slots,
|
|
157
|
+
base: isStringRecord(parsedBase) ? parsedBase : {},
|
|
158
|
+
variants: isVariantRecord(parsedVariants) ? parsedVariants : {},
|
|
159
|
+
compoundVariants: isCompoundArray(parsedCompound) ? parsedCompound : [],
|
|
160
|
+
defaultVariants: isDefaultVariants(parsedDefaults) ? parsedDefaults : {}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function getComponentNameFromFilename(filename: string): string {
|
|
165
|
+
return filename.replace(/^(RLA|RLV)/, "").replace(/\.(astro|vue)$/, "")
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function extractAllMeta(): Record<string, ComponentMeta> {
|
|
169
|
+
const componentsDir = path.join(uiRoot, "src", "components")
|
|
170
|
+
const themesDir = path.join(uiRoot, "src", "themes")
|
|
171
|
+
|
|
172
|
+
const vueDir = path.join(componentsDir, "vue")
|
|
173
|
+
const astroDir = path.join(componentsDir, "astro")
|
|
174
|
+
|
|
175
|
+
const vueFiles = existsSync(vueDir)
|
|
176
|
+
? readdirSync(vueDir).filter((f: string) => f.endsWith(".vue"))
|
|
177
|
+
: []
|
|
178
|
+
const astroFiles = existsSync(astroDir)
|
|
179
|
+
? readdirSync(astroDir).filter((f: string) => f.endsWith(".astro") && f !== "Head.astro")
|
|
180
|
+
: []
|
|
181
|
+
|
|
182
|
+
const vueComponents = new Map<string, string>()
|
|
183
|
+
const astroComponents = new Map<string, string>()
|
|
184
|
+
|
|
185
|
+
for (const f of vueFiles) vueComponents.set(getComponentNameFromFilename(f), f)
|
|
186
|
+
for (const f of astroFiles) astroComponents.set(getComponentNameFromFilename(f), f)
|
|
187
|
+
|
|
188
|
+
const allNames = new Set([...vueComponents.keys(), ...astroComponents.keys()])
|
|
189
|
+
const result: Record<string, ComponentMeta> = {}
|
|
190
|
+
|
|
191
|
+
for (const name of [...allNames].toSorted()) {
|
|
192
|
+
const vueFile = vueComponents.get(name)
|
|
193
|
+
const astroFile = astroComponents.get(name)
|
|
194
|
+
|
|
195
|
+
let props: PropMeta[] = []
|
|
196
|
+
let slots: SlotMeta[] = []
|
|
197
|
+
let emits: EmitMeta[] = []
|
|
198
|
+
const frameworks: ("astro" | "vue")[] = []
|
|
199
|
+
|
|
200
|
+
if (vueFile) {
|
|
201
|
+
frameworks.push("vue")
|
|
202
|
+
try {
|
|
203
|
+
const vueMeta = extractVueMetaDirect(path.join(vueDir, vueFile))
|
|
204
|
+
props = vueMeta.props
|
|
205
|
+
slots = vueMeta.slots
|
|
206
|
+
emits = vueMeta.emits
|
|
207
|
+
} catch (err) {
|
|
208
|
+
console.warn(`[starlightDocsApi] Failed to extract Vue meta for ${vueFile}:`, err)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (astroFile) {
|
|
213
|
+
frameworks.push("astro")
|
|
214
|
+
if (!vueFile) {
|
|
215
|
+
try {
|
|
216
|
+
const astroMeta = extractAstroMeta(path.join(astroDir, astroFile))
|
|
217
|
+
props = astroMeta.props
|
|
218
|
+
slots = astroMeta.slots
|
|
219
|
+
} catch (err) {
|
|
220
|
+
console.warn(`[starlightDocsApi] Failed to extract Astro meta for ${astroFile}:`, err)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const theme = extractThemeMeta(name, themesDir)
|
|
226
|
+
|
|
227
|
+
result[pascalToKebab(name)] = {
|
|
228
|
+
name,
|
|
229
|
+
astroComponent: astroFile ?? null,
|
|
230
|
+
vueComponent: vueFile ?? null,
|
|
231
|
+
frameworks,
|
|
232
|
+
props,
|
|
233
|
+
slots,
|
|
234
|
+
emits,
|
|
235
|
+
theme
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return result
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface StarlightDocsApiConfig {}
|
|
243
|
+
|
|
244
|
+
export default function starlightDocsApi(_config?: StarlightDocsApiConfig): StarlightPlugin {
|
|
245
|
+
return {
|
|
246
|
+
name: "rimelight-ui-starlight-docs-api",
|
|
247
|
+
hooks: {
|
|
248
|
+
"config:setup"({ addIntegration, logger }) {
|
|
249
|
+
logger.info("Initializing @rimelight/ui docs API")
|
|
250
|
+
|
|
251
|
+
const meta = extractAllMeta()
|
|
252
|
+
|
|
253
|
+
addIntegration({
|
|
254
|
+
name: "rimelight-ui-starlight-docs-api-integration",
|
|
255
|
+
hooks: {
|
|
256
|
+
"astro:config:setup": ({ updateConfig }) => {
|
|
257
|
+
updateConfig({
|
|
258
|
+
vite: {
|
|
259
|
+
plugins: [
|
|
260
|
+
virtual({
|
|
261
|
+
"virtual:rimelight-ui-docs-meta": `export default ${JSON.stringify(meta)}`
|
|
262
|
+
})
|
|
263
|
+
]
|
|
264
|
+
}
|
|
265
|
+
} as Record<string, unknown>)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
})
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|