vike-ripple 0.1.0 → 0.2.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/package.json +15 -15
- package/src/+config.js +40 -0
- package/src/components/ClientOnly.js +5 -0
- package/src/components/Config/Config-client.js +3 -0
- package/src/components/Config/Config-server.js +6 -0
- package/src/components/Head/Head-client.js +3 -0
- package/src/components/Head/Head-server.js +7 -0
- package/src/hooks/useConfig.js +14 -0
- package/src/hooks/useData.js +6 -0
- package/src/hooks/usePageContext.js +6 -0
- package/src/index.js +16 -6
- package/src/integration/applyHeadSettings.js +10 -0
- package/src/integration/getHeadSetting.js +7 -0
- package/src/integration/getPageElement.js +32 -0
- package/src/integration/onRenderClient.js +62 -0
- package/src/integration/onRenderHtml.js +120 -0
- package/src/integration/ssrEffect.js +15 -0
- package/src/types/Config.ts +33 -0
- package/src/types/PageContext.ts +13 -0
- package/src/utils/callCumulativeHooks.js +11 -0
- package/src/utils/getTagAttributesString.js +10 -0
- package/src/renderer/+config.ts +0 -10
- package/src/renderer/+onRenderClient.tsx +0 -29
- package/src/renderer/+onRenderHtml.tsx +0 -43
package/package.json
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vike-ripple",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Vike
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Vike extension for Ripple TS — full parity with vike-react/vike-solid/vike-vue",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"
|
|
6
|
+
"types": "./src/types/Config.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./src/index.js",
|
|
9
|
-
"./
|
|
9
|
+
"./config": "./src/+config.js",
|
|
10
|
+
"./setup": "./src/setup.js",
|
|
11
|
+
"./Head": "./src/components/Head/Head-server.js",
|
|
12
|
+
"./ClientOnly": "./src/components/ClientOnly.js",
|
|
13
|
+
"./usePageContext": "./src/hooks/usePageContext.js",
|
|
14
|
+
"./useData": "./src/hooks/useData.js",
|
|
15
|
+
"./useConfig": "./src/hooks/useConfig.js",
|
|
16
|
+
"./src/integration/onRenderHtml.js": "./src/integration/onRenderHtml.js",
|
|
17
|
+
"./src/integration/onRenderClient.js": "./src/integration/onRenderClient.js"
|
|
10
18
|
},
|
|
11
19
|
"bin": {
|
|
12
|
-
"vike-ripple": "src/setup.js"
|
|
20
|
+
"vike-ripple": "./src/setup.js"
|
|
13
21
|
},
|
|
14
|
-
"files": [
|
|
15
|
-
|
|
16
|
-
],
|
|
17
|
-
"keywords": [
|
|
18
|
-
"vike",
|
|
19
|
-
"ripple",
|
|
20
|
-
"ripplets",
|
|
21
|
-
"ssr",
|
|
22
|
-
"vite"
|
|
23
|
-
],
|
|
22
|
+
"files": ["src"],
|
|
23
|
+
"keywords": ["vike", "ripple", "ripplets", "ssr", "vite"],
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"vike": ">=0.4.259",
|
package/src/+config.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ssrEffect } from './integration/ssrEffect.js'
|
|
2
|
+
|
|
3
|
+
/** @type {import('vike/types').Config} */
|
|
4
|
+
const config = {
|
|
5
|
+
name: 'vike-ripple',
|
|
6
|
+
require: { vike: '>=0.4.250' },
|
|
7
|
+
|
|
8
|
+
onRenderHtml: 'import:vike-ripple/src/integration/onRenderHtml.js:onRenderHtml',
|
|
9
|
+
onRenderClient: 'import:vike-ripple/src/integration/onRenderClient.js:onRenderClient',
|
|
10
|
+
|
|
11
|
+
clientRouting: true,
|
|
12
|
+
hydrationCanBeAborted: true,
|
|
13
|
+
|
|
14
|
+
passToClient: ['_configViaHook'],
|
|
15
|
+
|
|
16
|
+
meta: {
|
|
17
|
+
Head: { env: { server: true }, cumulative: true },
|
|
18
|
+
Layout: { env: { server: true, client: true }, cumulative: true },
|
|
19
|
+
title: { env: { server: true, client: true } },
|
|
20
|
+
description: { env: { server: true } },
|
|
21
|
+
image: { env: { server: true } },
|
|
22
|
+
viewport: { env: { server: true } },
|
|
23
|
+
favicon: { env: { server: true }, global: true },
|
|
24
|
+
lang: { env: { server: true, client: true } },
|
|
25
|
+
ssr: { env: { config: true }, effect: ssrEffect },
|
|
26
|
+
stream: { env: { server: true }, cumulative: true },
|
|
27
|
+
onBeforeRenderHtml: { env: { server: true }, cumulative: true },
|
|
28
|
+
onAfterRenderHtml: { env: { server: true }, cumulative: true },
|
|
29
|
+
onBeforeRenderClient: { env: { server: false, client: true }, cumulative: true },
|
|
30
|
+
onAfterRenderClient: { env: { server: false, client: true }, cumulative: true },
|
|
31
|
+
bodyHtmlBegin: { env: { server: true }, cumulative: true, global: true },
|
|
32
|
+
bodyHtmlEnd: { env: { server: true }, cumulative: true, global: true },
|
|
33
|
+
headHtmlBegin: { env: { server: true }, cumulative: true, global: true },
|
|
34
|
+
headHtmlEnd: { env: { server: true }, cumulative: true, global: true },
|
|
35
|
+
htmlAttributes: { env: { server: true }, global: true, cumulative: true },
|
|
36
|
+
bodyAttributes: { env: { server: true }, global: true, cumulative: true },
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default config
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useConfig } from '../hooks/useConfig.js'
|
|
2
|
+
|
|
3
|
+
export function useConfig() {
|
|
4
|
+
const pageContext = typeof window !== 'undefined'
|
|
5
|
+
? window.__vike_pageContext
|
|
6
|
+
: globalThis.__vike_pageContext
|
|
7
|
+
|
|
8
|
+
return (values) => {
|
|
9
|
+
if (!pageContext._configViaHook) {
|
|
10
|
+
pageContext._configViaHook = {}
|
|
11
|
+
}
|
|
12
|
+
Object.assign(pageContext._configViaHook, values)
|
|
13
|
+
}
|
|
14
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* vike-ripple — Vike extension for Ripple TS.
|
|
3
3
|
*
|
|
4
4
|
* ## Setup
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* 1. Import config in your renderer/+config.ts:
|
|
6
|
+
* import vikeRipple from 'vike-ripple/config'
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* 2. Run setup (once):
|
|
9
|
+
* npx vike-ripple setup
|
|
10
|
+
*
|
|
11
|
+
* 3. Add optimizeDeps to vite.config.ts:
|
|
12
|
+
* optimizeDeps: { exclude: ['ripple'] }
|
|
13
|
+
*
|
|
14
|
+
* ## Usage
|
|
15
|
+
* - +Head.tsrx — inject <head> content
|
|
16
|
+
* - +Layout.tsrx — layout components
|
|
17
|
+
* - +title.ts — per-page title
|
|
18
|
+
* - +description.ts — per-page description
|
|
19
|
+
* - +ssr.ts — per-page SSR toggle
|
|
20
|
+
* - +stream.ts — per-page streaming toggle
|
|
11
21
|
*/
|
|
12
22
|
export default function vikeRipple() {
|
|
13
23
|
return {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function applyHeadSettings(headList, target) {
|
|
2
|
+
if (!headList || !Array.isArray(headList)) return
|
|
3
|
+
for (const head of headList) {
|
|
4
|
+
if (typeof head === 'string') {
|
|
5
|
+
target.insertAdjacentHTML('beforeend', head)
|
|
6
|
+
} else if (head instanceof Node) {
|
|
7
|
+
target.appendChild(head.cloneNode(true))
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export { getPageElement }
|
|
2
|
+
|
|
3
|
+
import { getHeadSetting } from './getHeadSetting.js'
|
|
4
|
+
|
|
5
|
+
function getPageElement(pageContext) {
|
|
6
|
+
const { Page } = pageContext
|
|
7
|
+
if (!Page) {
|
|
8
|
+
return { page: null, pageElement: null }
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const Layout = pageContext.config.Layout
|
|
12
|
+
const Wrapper = pageContext.config.Wrapper
|
|
13
|
+
|
|
14
|
+
let page = Page
|
|
15
|
+
|
|
16
|
+
if (Layout) {
|
|
17
|
+
const layouts = Array.isArray(Layout) ? Layout : [Layout]
|
|
18
|
+
for (let i = layouts.length - 1; i >= 0; i--) {
|
|
19
|
+
const LayoutComponent = layouts[i]
|
|
20
|
+
page = function NestedPage(props) { return LayoutComponent({ ...props, children: page }) }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (Wrapper) {
|
|
25
|
+
const wrappers = Array.isArray(Wrapper) ? Wrapper : [Wrapper]
|
|
26
|
+
for (const W of wrappers) {
|
|
27
|
+
page = function WrappedPage(props) { return W({ ...props, children: page }) }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { page, pageElement: page }
|
|
32
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// https://vike.dev/onRenderClient
|
|
2
|
+
export { onRenderClient }
|
|
3
|
+
|
|
4
|
+
import { hydrate } from 'ripple'
|
|
5
|
+
import { getHeadSetting } from './getHeadSetting.js'
|
|
6
|
+
import { applyHeadSettings } from './applyHeadSettings.js'
|
|
7
|
+
import { callCumulativeHooks } from '../utils/callCumulativeHooks.js'
|
|
8
|
+
|
|
9
|
+
let rendered = false
|
|
10
|
+
|
|
11
|
+
const onRenderClient = async (pageContext) => {
|
|
12
|
+
await callCumulativeHooks(pageContext.config.onBeforeRenderClient, pageContext)
|
|
13
|
+
|
|
14
|
+
const container = document.getElementById('root')
|
|
15
|
+
if (!container) return
|
|
16
|
+
|
|
17
|
+
pageContext._headAlreadySet = pageContext.isHydration
|
|
18
|
+
|
|
19
|
+
if (pageContext.isHydration && container.innerHTML !== '') {
|
|
20
|
+
try {
|
|
21
|
+
hydratePage(pageContext, container)
|
|
22
|
+
rendered = true
|
|
23
|
+
} catch (err) {
|
|
24
|
+
console.warn('[vike-ripple] hydrate failed, falling back to mount:', err)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!rendered) {
|
|
29
|
+
mountPage(pageContext, container)
|
|
30
|
+
rendered = true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
updateHead(pageContext)
|
|
34
|
+
|
|
35
|
+
await callCumulativeHooks(pageContext.config.onAfterRenderClient, pageContext)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function hydratePage(pageContext, container) {
|
|
39
|
+
hydrate(pageContext.Page, { target: container, props: {} })
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function mountPage(pageContext, container) {
|
|
43
|
+
const { mount } = await import('ripple')
|
|
44
|
+
mount(pageContext.Page, { target: container, props: {} })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function updateHead(pageContext) {
|
|
48
|
+
if (pageContext._headAlreadySet) return
|
|
49
|
+
|
|
50
|
+
const title = getHeadSetting('title', pageContext)
|
|
51
|
+
if (title && document.title !== title) {
|
|
52
|
+
document.title = title
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const lang = getHeadSetting('lang', pageContext)
|
|
56
|
+
if (lang) {
|
|
57
|
+
document.documentElement.lang = lang
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
applyHeadSettings(pageContext.config.Head, document.head)
|
|
61
|
+
applyHeadSettings(pageContext._configViaHook?.Head, document.head)
|
|
62
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export { onRenderHtml }
|
|
2
|
+
|
|
3
|
+
import { render, create_ssr_stream } from 'ripple/server'
|
|
4
|
+
import { escapeInject, dangerouslySkipEscape } from 'vike/server'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
import { getHeadSetting } from './getHeadSetting.js'
|
|
8
|
+
import { callCumulativeHooks } from '../utils/callCumulativeHooks.js'
|
|
9
|
+
import { getTagAttributesString } from '../utils/getTagAttributesString.js'
|
|
10
|
+
|
|
11
|
+
const onRenderHtml = async (pageContext) => {
|
|
12
|
+
const pageContext2 = pageContext
|
|
13
|
+
const { Page } = pageContext2
|
|
14
|
+
if (!Page) throw new Error('No Page')
|
|
15
|
+
|
|
16
|
+
await callCumulativeHooks(pageContext2.config.onBeforeRenderHtml, pageContext2)
|
|
17
|
+
|
|
18
|
+
const headHtml = getHeadHtml(pageContext2)
|
|
19
|
+
const { headHtmlBegin, headHtmlEnd, bodyHtmlBegin, bodyHtmlEnd } = await getHtmlInjections(pageContext2)
|
|
20
|
+
const { htmlAttributesString, bodyAttributesString } = getTagAttributes(pageContext2)
|
|
21
|
+
const enableStream = !!(pageContext2.config.stream ?? pageContext2.config.rippleStream)
|
|
22
|
+
|
|
23
|
+
if (enableStream) {
|
|
24
|
+
const rippleStream = create_ssr_stream()
|
|
25
|
+
render(Page, { stream: rippleStream.sink }).catch(e => {
|
|
26
|
+
console.error('[ripple] render err:', e?.message)
|
|
27
|
+
})
|
|
28
|
+
return escapeInject`<!DOCTYPE html>
|
|
29
|
+
<html${dangerouslySkipEscape(htmlAttributesString)}>
|
|
30
|
+
<head>
|
|
31
|
+
<meta charset="UTF-8" />
|
|
32
|
+
${dangerouslySkipEscape(headHtmlBegin)}
|
|
33
|
+
${dangerouslySkipEscape(headHtml)}
|
|
34
|
+
${dangerouslySkipEscape(headHtmlEnd)}
|
|
35
|
+
</head>
|
|
36
|
+
<body${dangerouslySkipEscape(bodyAttributesString)}>
|
|
37
|
+
${dangerouslySkipEscape(bodyHtmlBegin)}
|
|
38
|
+
<div id="root">${rippleStream.stream}</div>
|
|
39
|
+
${dangerouslySkipEscape(bodyHtmlEnd)}
|
|
40
|
+
</body>
|
|
41
|
+
</html>`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const { head, body, css } = await render(Page, {})
|
|
45
|
+
|
|
46
|
+
const cssHtml = css?.size
|
|
47
|
+
? `<style data-ripple-ssr>${[...css].join('')}<` + `/style>`
|
|
48
|
+
: ''
|
|
49
|
+
|
|
50
|
+
const pageHtml = `<div id="root">${body}${cssHtml}</div>`
|
|
51
|
+
|
|
52
|
+
pageContext2.pageHtmlString = body
|
|
53
|
+
await callCumulativeHooks(pageContext2.config.onAfterRenderHtml, pageContext2)
|
|
54
|
+
|
|
55
|
+
return escapeInject`<!DOCTYPE html>
|
|
56
|
+
<html${dangerouslySkipEscape(htmlAttributesString)}>
|
|
57
|
+
<head>
|
|
58
|
+
<meta charset="UTF-8" />
|
|
59
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
60
|
+
${dangerouslySkipEscape(headHtmlBegin)}
|
|
61
|
+
${dangerouslySkipEscape(head)}
|
|
62
|
+
${dangerouslySkipEscape(cssHtml)}
|
|
63
|
+
${dangerouslySkipEscape(headHtml)}
|
|
64
|
+
${dangerouslySkipEscape(headHtmlEnd)}
|
|
65
|
+
</head>
|
|
66
|
+
<body${dangerouslySkipEscape(bodyAttributesString)}>
|
|
67
|
+
${dangerouslySkipEscape(bodyHtmlBegin)}
|
|
68
|
+
<div id="root">${dangerouslySkipEscape(body)}</div>
|
|
69
|
+
${dangerouslySkipEscape(bodyHtmlEnd)}
|
|
70
|
+
</body>
|
|
71
|
+
</html>`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getHeadHtml(pageContext) {
|
|
75
|
+
const favicon = getHeadSetting('favicon', pageContext)
|
|
76
|
+
const title = getHeadSetting('title', pageContext)
|
|
77
|
+
const description = getHeadSetting('description', pageContext)
|
|
78
|
+
const image = getHeadSetting('image', pageContext)
|
|
79
|
+
|
|
80
|
+
const faviconTag = !favicon ? '' : `<link rel="icon" href="${favicon}" />`
|
|
81
|
+
const titleTags = !title ? '' : `<title>${title}</title><meta property="og:title" content="${title}" />`
|
|
82
|
+
const descriptionTags = !description
|
|
83
|
+
? ''
|
|
84
|
+
: `<meta name="description" content="${description}" /><meta property="og:description" content="${description}" />`
|
|
85
|
+
const imageTags = !image
|
|
86
|
+
? ''
|
|
87
|
+
: `<meta property="og:image" content="${image}"><meta name="twitter:card" content="summary_large_image">`
|
|
88
|
+
const viewportTag = getViewportTag(getHeadSetting('viewport', pageContext))
|
|
89
|
+
const langAttr = getHeadSetting('lang', pageContext)
|
|
90
|
+
const headElementsHtml = [
|
|
91
|
+
...(pageContext.config.Head ?? []),
|
|
92
|
+
...(pageContext._configViaHook?.Head ?? []),
|
|
93
|
+
]
|
|
94
|
+
.filter(Boolean)
|
|
95
|
+
.map(head => (typeof head === 'function' ? head(pageContext) : head))
|
|
96
|
+
.join('\n')
|
|
97
|
+
|
|
98
|
+
return `${titleTags}${viewportTag}${headElementsHtml}${faviconTag}${descriptionTags}${imageTags}`
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function getViewportTag(viewport) {
|
|
102
|
+
if (viewport === null || viewport === undefined) return ''
|
|
103
|
+
if (viewport === 'responsive') return '<meta name="viewport" content="width=device-width, initial-scale=1.0" />'
|
|
104
|
+
if (typeof viewport === 'number') return `<meta name="viewport" content="width=${viewport}" />`
|
|
105
|
+
return '<meta name="viewport" content="width=device-width, initial-scale=1.0" />'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function getTagAttributes(pageContext) {
|
|
109
|
+
const htmlAttributesString = getTagAttributesString(pageContext.config.htmlAttributes)
|
|
110
|
+
const bodyAttributesString = getTagAttributesString(pageContext.config.bodyAttributes)
|
|
111
|
+
return { htmlAttributesString, bodyAttributesString }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function getHtmlInjections(pageContext) {
|
|
115
|
+
const headHtmlBegin = (pageContext.config.headHtmlBegin ?? []).join('\n')
|
|
116
|
+
const headHtmlEnd = (pageContext.config.headHtmlEnd ?? []).join('\n')
|
|
117
|
+
const bodyHtmlBegin = (pageContext.config.bodyHtmlBegin ?? []).join('\n')
|
|
118
|
+
const bodyHtmlEnd = (pageContext.config.bodyHtmlEnd ?? []).join('\n')
|
|
119
|
+
return { headHtmlBegin, headHtmlEnd, bodyHtmlBegin, bodyHtmlEnd }
|
|
120
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { ssrEffect }
|
|
2
|
+
|
|
3
|
+
function ssrEffect({ configDefinedAt, configValue }) {
|
|
4
|
+
if (typeof configValue !== 'boolean') throw new Error(`${configDefinedAt} should be a boolean`)
|
|
5
|
+
return {
|
|
6
|
+
meta: {
|
|
7
|
+
Page: {
|
|
8
|
+
env: {
|
|
9
|
+
client: true,
|
|
10
|
+
server: configValue !== false,
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import 'vike/types'
|
|
2
|
+
|
|
3
|
+
declare global {
|
|
4
|
+
namespace Vike {
|
|
5
|
+
interface Config {
|
|
6
|
+
Head?: unknown[]
|
|
7
|
+
Layout?: unknown[]
|
|
8
|
+
title?: string | null
|
|
9
|
+
description?: string | null
|
|
10
|
+
image?: string | null
|
|
11
|
+
viewport?: 'responsive' | number | null
|
|
12
|
+
favicon?: string | null
|
|
13
|
+
lang?: string | null
|
|
14
|
+
ssr?: boolean
|
|
15
|
+
stream?: boolean
|
|
16
|
+
rippleStream?: boolean
|
|
17
|
+
Wrapper?: unknown[]
|
|
18
|
+
Loading?: unknown
|
|
19
|
+
onBeforeRenderHtml?: ((pageContext: unknown) => void | Promise<void>)[]
|
|
20
|
+
onAfterRenderHtml?: ((pageContext: unknown) => void | Promise<void>)[]
|
|
21
|
+
onBeforeRenderClient?: ((pageContext: unknown) => void | Promise<void>)[]
|
|
22
|
+
onAfterRenderClient?: ((pageContext: unknown) => void | Promise<void>)[]
|
|
23
|
+
bodyHtmlBegin?: string[]
|
|
24
|
+
bodyHtmlEnd?: string[]
|
|
25
|
+
headHtmlBegin?: string[]
|
|
26
|
+
headHtmlEnd?: string[]
|
|
27
|
+
htmlAttributes?: Record<string, string | boolean>
|
|
28
|
+
bodyAttributes?: Record<string, string | boolean>
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type __FakeExport_Config = true
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export async function callCumulativeHooks(hooks, ...args) {
|
|
2
|
+
if (!hooks || !Array.isArray(hooks)) return
|
|
3
|
+
for (const hook of hooks) {
|
|
4
|
+
if (typeof hook === 'function') {
|
|
5
|
+
const result = hook(...args)
|
|
6
|
+
if (result && typeof result.then === 'function') {
|
|
7
|
+
await result
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function getTagAttributesString(attributes) {
|
|
2
|
+
if (!attributes) return ''
|
|
3
|
+
return Object.entries(attributes)
|
|
4
|
+
.map(([key, value]) => {
|
|
5
|
+
if (value === true) return ` ${key}`
|
|
6
|
+
if (value === false || value === null || value === undefined) return ''
|
|
7
|
+
return ` ${key}="${String(value).replace(/"/g, '"')}"`
|
|
8
|
+
})
|
|
9
|
+
.join('')
|
|
10
|
+
}
|
package/src/renderer/+config.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// https://vike.dev/onRenderClient
|
|
2
|
-
export { onRenderClient }
|
|
3
|
-
|
|
4
|
-
import { hydrate } from 'ripple'
|
|
5
|
-
|
|
6
|
-
let rendered = false
|
|
7
|
-
|
|
8
|
-
const onRenderClient = async (pageContext) => {
|
|
9
|
-
const { Page } = pageContext
|
|
10
|
-
if (!Page) return
|
|
11
|
-
|
|
12
|
-
const container = document.getElementById('root')
|
|
13
|
-
if (!container) return
|
|
14
|
-
|
|
15
|
-
if (pageContext.isHydration && container.innerHTML !== '') {
|
|
16
|
-
try {
|
|
17
|
-
hydrate(Page, { target: container, props: {} })
|
|
18
|
-
rendered = true
|
|
19
|
-
} catch (err) {
|
|
20
|
-
console.warn('[ripple] hydrate failed, falling back to mount:', err)
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (!rendered) {
|
|
25
|
-
const { mount } = await import('ripple')
|
|
26
|
-
mount(Page, { target: container, props: {} })
|
|
27
|
-
rendered = true
|
|
28
|
-
}
|
|
29
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
// https://vike.dev/onRenderHtml
|
|
2
|
-
export { onRenderHtml }
|
|
3
|
-
|
|
4
|
-
import { render, create_ssr_stream } from 'ripple/server'
|
|
5
|
-
import { escapeInject, dangerouslySkipEscape } from 'vike/server'
|
|
6
|
-
|
|
7
|
-
const onRenderHtml = async (pageContext) => {
|
|
8
|
-
const { Page } = pageContext
|
|
9
|
-
if (!Page) throw new Error('No Page')
|
|
10
|
-
|
|
11
|
-
const enableStream = !!(pageContext.config.rippleStream ?? pageContext.config.stream)
|
|
12
|
-
|
|
13
|
-
if (enableStream) {
|
|
14
|
-
const rippleStream = create_ssr_stream()
|
|
15
|
-
render(Page, { stream: rippleStream.sink }).catch(e => {
|
|
16
|
-
console.error('[ripple] render err:', e?.message)
|
|
17
|
-
})
|
|
18
|
-
return escapeInject`<!DOCTYPE html>
|
|
19
|
-
<html>
|
|
20
|
-
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /></head>
|
|
21
|
-
<body><div id="root">${rippleStream.stream}</div></body>
|
|
22
|
-
</html>`
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const { head, body, css } = await render(Page, {})
|
|
26
|
-
|
|
27
|
-
const cssHtml = css?.size
|
|
28
|
-
? `<style data-ripple-ssr>${[...css].join('')}<` + `/style>`
|
|
29
|
-
: ''
|
|
30
|
-
|
|
31
|
-
return escapeInject`<!DOCTYPE html>
|
|
32
|
-
<html>
|
|
33
|
-
<head>
|
|
34
|
-
<meta charset="UTF-8" />
|
|
35
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
36
|
-
${dangerouslySkipEscape(head)}
|
|
37
|
-
${dangerouslySkipEscape(cssHtml)}
|
|
38
|
-
</head>
|
|
39
|
-
<body>
|
|
40
|
-
<div id="root">${dangerouslySkipEscape(body)}</div>
|
|
41
|
-
</body>
|
|
42
|
-
</html>`
|
|
43
|
-
}
|