vike-ripple 0.1.0 → 0.2.1
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 +14 -6
- 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/setup.js +10 -14
- 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,15 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vike-ripple",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Vike
|
|
3
|
+
"version": "0.2.1",
|
|
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
22
|
"files": [
|
|
15
23
|
"src"
|
|
@@ -27,4 +35,4 @@
|
|
|
27
35
|
"@ripple-ts/vite-plugin": ">=0.3.0",
|
|
28
36
|
"ripple": ">=0.1.0"
|
|
29
37
|
}
|
|
30
|
-
}
|
|
38
|
+
}
|
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
|
+
}
|
package/src/setup.js
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* vike-ripple setup — patches Vike and Ripple for .tsrx support.
|
|
4
|
-
*
|
|
5
|
-
* Run once: npx vike-ripple setup
|
|
6
|
-
* Or add to project's package.json: "postinstall": "vike-ripple setup"
|
|
7
|
-
*/
|
|
8
1
|
import { createRequire } from 'module'
|
|
9
|
-
import { join
|
|
2
|
+
import { join } from 'path'
|
|
10
3
|
import { readFileSync, writeFileSync, existsSync } from 'fs'
|
|
11
4
|
import { fileURLToPath } from 'url'
|
|
12
5
|
|
|
13
|
-
const __dirname =
|
|
6
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url))
|
|
14
7
|
const projectRoot = process.cwd()
|
|
15
8
|
let exitCode = 0
|
|
16
9
|
|
|
@@ -61,7 +54,7 @@ function patchRippleDirect() {
|
|
|
61
54
|
log('Patched Ripple plugin for ?direct CSS module loading')
|
|
62
55
|
}
|
|
63
56
|
|
|
64
|
-
// ── Patch 3: @apply support (
|
|
57
|
+
// ── Patch 3: @apply support via @reference (no HMR loops) ─────
|
|
65
58
|
function patchRippleApply() {
|
|
66
59
|
const target = resolveRipple('src/index.js')
|
|
67
60
|
if (!target) return
|
|
@@ -75,13 +68,16 @@ function patchRippleApply() {
|
|
|
75
68
|
src = src.replace(
|
|
76
69
|
'// TW_PATCH: prepend tailwindcss so @apply works',
|
|
77
70
|
'// TW_PATCH_APPLY: bring tailwindcss into scope for @apply',
|
|
71
|
+
)
|
|
72
|
+
"css = '@import \"tailwindcss\";\\n' + css;",
|
|
73
|
+
"css = '@import \"tailwindcss\" layer(reference);\\n' + css;",
|
|
78
74
|
)
|
|
79
75
|
writeFileSync(target, src, 'utf-8')
|
|
80
|
-
log('Upgraded @apply patch
|
|
76
|
+
log('Upgraded @apply patch to @reference (prevents HMR loops)')
|
|
81
77
|
return
|
|
82
78
|
}
|
|
83
79
|
|
|
84
|
-
// Fresh install —
|
|
80
|
+
// Fresh install — prepend @reference comment + tailwind for HMR safety
|
|
85
81
|
const orig = (
|
|
86
82
|
'\t\t\t\t\tif (css) {\n' +
|
|
87
83
|
'\t\t\t\t\t\tconst cssId = createVirtualImportId(filename, root, \'style\');\n' +
|
|
@@ -90,7 +86,7 @@ function patchRippleApply() {
|
|
|
90
86
|
const patched = (
|
|
91
87
|
'\t\t\t\t\tif (css) {\n' +
|
|
92
88
|
'\t\t\t\t\t\t// TW_PATCH_APPLY: bring tailwindcss into scope for @apply\n' +
|
|
93
|
-
"\t\t\t\t\t\tcss = '@import \"tailwindcss\";\\n' + css;\n" +
|
|
89
|
+
"\t\t\t\t\t\tcss = '@import \"tailwindcss\" layer(reference);\\n' + css;\n" +
|
|
94
90
|
'\t\t\t\t\t\tconst cssId = createVirtualImportId(filename, root, \'style\');\n' +
|
|
95
91
|
'\t\t\t\t\t\tcssCache.set(cssId, css);'
|
|
96
92
|
)
|
|
@@ -98,7 +94,7 @@ function patchRippleApply() {
|
|
|
98
94
|
const result = src.replace(orig, patched)
|
|
99
95
|
if (result === src) { warn('Could not patch Ripple plugin for @apply'); return }
|
|
100
96
|
writeFileSync(target, result, 'utf-8')
|
|
101
|
-
log('Patched Ripple plugin for @apply support in <style> blocks')
|
|
97
|
+
log('Patched Ripple plugin for @apply support in <style> blocks (HMR-safe)')
|
|
102
98
|
}
|
|
103
99
|
|
|
104
100
|
// ── Resolve helpers ────────────────────────────────────────────
|
|
@@ -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
|
-
}
|