@fastify/react 1.1.2 → 1.1.4
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 +1 -1
- package/client.js +4 -6
- package/index.js +3 -12
- package/package.json +33 -38
- package/plugin/index.js +42 -44
- package/plugin/parsers.js +2 -2
- package/plugin/parsers.test.js +1 -1
- package/plugin/preload.js +9 -15
- package/plugin/stores.js +1 -2
- package/plugin/virtual.js +1 -6
- package/rendering.js +20 -39
- package/routing.js +11 -17
- package/server.js +44 -47
- package/templating.js +3 -3
- package/virtual/core.jsx +3 -12
- package/virtual/create.jsx +3 -9
- package/virtual/layouts.js +1 -5
- package/virtual/mount.js +13 -13
- package/virtual/resource.js +1 -6
- package/virtual/root.jsx +1 -5
- package/virtual-ts/core.tsx +3 -12
- package/virtual-ts/create.tsx +3 -9
- package/virtual-ts/layouts.ts +1 -5
- package/virtual-ts/mount.ts +13 -13
- package/virtual-ts/resource.ts +1 -6
- package/virtual-ts/root.tsx +1 -5
package/README.md
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
|
|
3
3
|
**`@fastify/react`** is the official [**`@fastify/vite`**](https://github.com/fastify/fastify-vite) renderer for React.
|
|
4
4
|
|
|
5
|
-
See the [documentation suite](https://fastify.github.io/fastify-vite/react/) to learn more.
|
|
5
|
+
See the [documentation suite](https://fastify.github.io/fastify-vite/react/) to learn more.
|
package/client.js
CHANGED
|
@@ -8,18 +8,16 @@ export function useRouteContext() {
|
|
|
8
8
|
const routeContext = useContext(RouteContext)
|
|
9
9
|
if (routeContext.state) {
|
|
10
10
|
routeContext.snapshot = isServer
|
|
11
|
-
? routeContext.state ?? {}
|
|
11
|
+
? (routeContext.state ?? {})
|
|
12
12
|
: useSnapshot(routeContext.state ?? {})
|
|
13
13
|
}
|
|
14
14
|
return routeContext
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export async function hydrateRoutes
|
|
17
|
+
export async function hydrateRoutes(fromInput) {
|
|
18
18
|
let from = fromInput
|
|
19
19
|
if (Array.isArray(from)) {
|
|
20
|
-
from = Object.fromEntries(
|
|
21
|
-
from.map((route) => [route.path, route]),
|
|
22
|
-
)
|
|
20
|
+
from = Object.fromEntries(from.map((route) => [route.path, route]))
|
|
23
21
|
}
|
|
24
22
|
return window.routes.map((route) => {
|
|
25
23
|
route.loader = memoImport(from[route.id])
|
|
@@ -28,7 +26,7 @@ export async function hydrateRoutes (fromInput) {
|
|
|
28
26
|
})
|
|
29
27
|
}
|
|
30
28
|
|
|
31
|
-
function memoImport
|
|
29
|
+
function memoImport(func) {
|
|
32
30
|
// Otherwise we get a ReferenceError, but since this function
|
|
33
31
|
// is only ran once for each route, there's no overhead
|
|
34
32
|
const kFuncExecuted = Symbol('kFuncExecuted')
|
package/index.js
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
export {
|
|
2
|
-
prepareServer,
|
|
3
|
-
} from './server.js'
|
|
1
|
+
export { prepareServer } from './server.js'
|
|
4
2
|
|
|
5
|
-
export {
|
|
6
|
-
prepareClient,
|
|
7
|
-
createErrorHandler,
|
|
8
|
-
createRoute,
|
|
9
|
-
} from './routing.js'
|
|
3
|
+
export { prepareClient, createErrorHandler, createRoute } from './routing.js'
|
|
10
4
|
|
|
11
|
-
export {
|
|
12
|
-
createRenderFunction,
|
|
13
|
-
createHtmlFunction,
|
|
14
|
-
} from './rendering.js'
|
|
5
|
+
export { createRenderFunction, createHtmlFunction } from './rendering.js'
|
|
15
6
|
|
|
16
7
|
export const clientModule = '$app/index.js'
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastify/react",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "The official @fastify/vite renderer for React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fastify",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"react",
|
|
8
|
+
"vite"
|
|
9
9
|
],
|
|
10
10
|
"homepage": "https://fastify.github.io/fastify-vite/react/",
|
|
11
11
|
"bugs": {
|
|
@@ -16,22 +16,29 @@
|
|
|
16
16
|
"type": "git",
|
|
17
17
|
"url": "git+https://github.com/fastify/fastify-vite.git"
|
|
18
18
|
},
|
|
19
|
-
"type": "module",
|
|
20
|
-
"main": "index.js",
|
|
21
|
-
"exports": {
|
|
22
|
-
".": "./index.js",
|
|
23
|
-
"./plugin": "./plugin/index.js",
|
|
24
|
-
"./client": "./client.js",
|
|
25
|
-
"./server": "./server.js"
|
|
26
|
-
},
|
|
27
19
|
"files": [
|
|
20
|
+
"client.js",
|
|
21
|
+
"context.js",
|
|
22
|
+
"index.js",
|
|
23
|
+
"rendering.js",
|
|
24
|
+
"routing.js",
|
|
25
|
+
"server.js",
|
|
26
|
+
"templating.js",
|
|
28
27
|
"plugin/index.js",
|
|
29
28
|
"plugin/parsers.js",
|
|
30
29
|
"plugin/parsers.test.js",
|
|
31
30
|
"plugin/preload.js",
|
|
32
31
|
"plugin/stores.js",
|
|
33
32
|
"plugin/virtual.js",
|
|
34
|
-
"virtual/
|
|
33
|
+
"virtual-ts/context.ts",
|
|
34
|
+
"virtual-ts/core.tsx",
|
|
35
|
+
"virtual-ts/create.tsx",
|
|
36
|
+
"virtual-ts/index.ts",
|
|
37
|
+
"virtual-ts/layouts.ts",
|
|
38
|
+
"virtual-ts/mount.ts",
|
|
39
|
+
"virtual-ts/resource.ts",
|
|
40
|
+
"virtual-ts/root.tsx",
|
|
41
|
+
"virtual-ts/routes.ts",
|
|
35
42
|
"virtual/context.js",
|
|
36
43
|
"virtual/core.jsx",
|
|
37
44
|
"virtual/create.jsx",
|
|
@@ -42,23 +49,19 @@
|
|
|
42
49
|
"virtual/root.jsx",
|
|
43
50
|
"virtual/routes.js",
|
|
44
51
|
"virtual-ts/layouts/default.tsx",
|
|
45
|
-
"virtual
|
|
46
|
-
"virtual-ts/core.tsx",
|
|
47
|
-
"virtual-ts/create.tsx",
|
|
48
|
-
"virtual-ts/index.ts",
|
|
49
|
-
"virtual-ts/layouts.ts",
|
|
50
|
-
"virtual-ts/mount.ts",
|
|
51
|
-
"virtual-ts/resource.ts",
|
|
52
|
-
"virtual-ts/root.tsx",
|
|
53
|
-
"virtual-ts/routes.ts",
|
|
54
|
-
"client.js",
|
|
55
|
-
"context.js",
|
|
56
|
-
"index.js",
|
|
57
|
-
"rendering.js",
|
|
58
|
-
"routing.js",
|
|
59
|
-
"server.js",
|
|
60
|
-
"templating.js"
|
|
52
|
+
"virtual/layouts/default.jsx"
|
|
61
53
|
],
|
|
54
|
+
"type": "module",
|
|
55
|
+
"main": "index.js",
|
|
56
|
+
"exports": {
|
|
57
|
+
".": "./index.js",
|
|
58
|
+
"./plugin": "./plugin/index.js",
|
|
59
|
+
"./client": "./client.js",
|
|
60
|
+
"./server": "./server.js"
|
|
61
|
+
},
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public"
|
|
64
|
+
},
|
|
62
65
|
"dependencies": {
|
|
63
66
|
"@unhead/react": "^2.0.8",
|
|
64
67
|
"acorn": "^8.14.1",
|
|
@@ -74,15 +77,7 @@
|
|
|
74
77
|
"react-router": "^7.5.1",
|
|
75
78
|
"valtio": "latest",
|
|
76
79
|
"youch": "^3.3.4",
|
|
77
|
-
"@fastify/vite": "^8.2.
|
|
78
|
-
},
|
|
79
|
-
"devDependencies": {
|
|
80
|
-
"@biomejs/biome": "^1.9.2"
|
|
81
|
-
},
|
|
82
|
-
"publishConfig": {
|
|
83
|
-
"access": "public"
|
|
80
|
+
"@fastify/vite": "^8.2.3"
|
|
84
81
|
},
|
|
85
|
-
"scripts": {
|
|
86
|
-
"lint": "biome check --apply-unsafe ."
|
|
87
|
-
}
|
|
82
|
+
"scripts": {}
|
|
88
83
|
}
|
package/plugin/index.js
CHANGED
|
@@ -6,51 +6,54 @@ import {
|
|
|
6
6
|
resolveId,
|
|
7
7
|
loadSource,
|
|
8
8
|
loadVirtualModule,
|
|
9
|
-
createPlaceholderExports
|
|
9
|
+
createPlaceholderExports,
|
|
10
10
|
} from './virtual.js'
|
|
11
11
|
import { closeBundle } from './preload.js'
|
|
12
12
|
import { parseStateKeys } from './parsers.js'
|
|
13
13
|
import { generateStores } from './stores.js'
|
|
14
14
|
|
|
15
|
-
export default function viteFastifyReactPlugin
|
|
15
|
+
export default function viteFastifyReactPlugin({ ts } = {}) {
|
|
16
16
|
const context = {
|
|
17
17
|
root: null,
|
|
18
18
|
}
|
|
19
|
-
return [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const source = loadSource(id)
|
|
34
|
-
return createPlaceholderExports(source)
|
|
35
|
-
}
|
|
36
|
-
if (prefix.test(id)) {
|
|
37
|
-
const [, virtual] = id.split(prefix)
|
|
38
|
-
if (virtual) {
|
|
39
|
-
return loadVirtualModule(virtual)
|
|
19
|
+
return [
|
|
20
|
+
viteFastify({
|
|
21
|
+
clientModule: ts ? '$app/index.ts' : '$app/index.js',
|
|
22
|
+
}),
|
|
23
|
+
{
|
|
24
|
+
// https://vite.dev/guide/api-plugin#conventions
|
|
25
|
+
name: 'vite-plugin-react-fastify',
|
|
26
|
+
config,
|
|
27
|
+
configResolved: configResolved.bind(context),
|
|
28
|
+
resolveId: resolveId.bind(context),
|
|
29
|
+
async load(id) {
|
|
30
|
+
if (id.includes('?server') && !this.environment.config.build?.ssr) {
|
|
31
|
+
const source = loadSource(id)
|
|
32
|
+
return createPlaceholderExports(source)
|
|
40
33
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
34
|
+
if (id.includes('?client') && this.environment.config.build?.ssr) {
|
|
35
|
+
const source = loadSource(id)
|
|
36
|
+
return createPlaceholderExports(source)
|
|
37
|
+
}
|
|
38
|
+
if (prefix.test(id)) {
|
|
39
|
+
const [, virtual] = id.split(prefix)
|
|
40
|
+
if (virtual) {
|
|
41
|
+
return loadVirtualModule(virtual)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
transformIndexHtml: {
|
|
46
|
+
order: 'post',
|
|
47
|
+
handler: transformIndexHtml.bind(context),
|
|
48
|
+
},
|
|
49
|
+
closeBundle() {
|
|
50
|
+
closeBundle.call(this, context.resolvedBundle)
|
|
51
|
+
},
|
|
49
52
|
},
|
|
50
|
-
|
|
53
|
+
]
|
|
51
54
|
}
|
|
52
55
|
|
|
53
|
-
function transformIndexHtml
|
|
56
|
+
function transformIndexHtml(html, { bundle }) {
|
|
54
57
|
if (!bundle) {
|
|
55
58
|
return
|
|
56
59
|
}
|
|
@@ -58,12 +61,12 @@ function transformIndexHtml (html, { bundle }) {
|
|
|
58
61
|
this.resolvedBundle = bundle
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
function configResolved
|
|
64
|
+
function configResolved(config) {
|
|
62
65
|
this.resolvedConfig = config
|
|
63
66
|
this.root = config.root
|
|
64
67
|
}
|
|
65
68
|
|
|
66
|
-
function config
|
|
69
|
+
function config(config, { command }) {
|
|
67
70
|
if (command === 'build') {
|
|
68
71
|
if (!config.build) {
|
|
69
72
|
config.build = {}
|
|
@@ -75,22 +78,17 @@ function config (config, { command }) {
|
|
|
75
78
|
}
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
function onwarn
|
|
81
|
+
function onwarn(warning, rollupWarn) {
|
|
79
82
|
if (
|
|
80
83
|
!(
|
|
81
84
|
warning.code == 'MISSING_EXPORT' &&
|
|
82
85
|
warning.message?.includes?.('"scrollBehavior" is not exported')
|
|
83
|
-
)
|
|
84
|
-
&&
|
|
86
|
+
) &&
|
|
85
87
|
!(
|
|
86
88
|
warning.code == 'PLUGIN_WARNING' &&
|
|
87
89
|
warning.message?.includes?.('dynamic import will not move module into another chunk')
|
|
88
|
-
)
|
|
89
|
-
&&
|
|
90
|
-
!(
|
|
91
|
-
warning.code == 'UNUSED_EXTERNAL_IMPORT' &&
|
|
92
|
-
warning.exporter === 'vue'
|
|
93
|
-
)
|
|
90
|
+
) &&
|
|
91
|
+
!(warning.code == 'UNUSED_EXTERNAL_IMPORT' && warning.exporter === 'vue')
|
|
94
92
|
) {
|
|
95
93
|
rollupWarn(warning)
|
|
96
94
|
}
|
package/plugin/parsers.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as acorn from 'acorn'
|
|
2
2
|
import * as walk from 'acorn-walk'
|
|
3
3
|
|
|
4
|
-
export function parseStateKeys
|
|
4
|
+
export function parseStateKeys(code) {
|
|
5
5
|
const ast = acorn.parse(code, { sourceType: 'module', ecmaVersion: 2020 })
|
|
6
6
|
|
|
7
7
|
let objectKeys = []
|
|
@@ -25,7 +25,7 @@ export function parseStateKeys (code) {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
}
|
|
28
|
+
},
|
|
29
29
|
})
|
|
30
30
|
|
|
31
31
|
return objectKeys
|
package/plugin/parsers.test.js
CHANGED
package/plugin/preload.js
CHANGED
|
@@ -18,19 +18,18 @@ export async function closeBundle(resolvedBundle) {
|
|
|
18
18
|
}
|
|
19
19
|
const indexHtml = readFileSync(join(distDir, 'index.html'), 'utf8')
|
|
20
20
|
const pages = Object.fromEntries(
|
|
21
|
-
Object.entries(resolvedBundle ?? {})
|
|
22
|
-
.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
})
|
|
21
|
+
Object.entries(resolvedBundle ?? {}).filter(([id, meta]) => {
|
|
22
|
+
if (meta.facadeModuleId?.includes('/pages/')) {
|
|
23
|
+
meta.htmlPath = meta.facadeModuleId.replace(/.*pages\/(.*)\.(j|t)sx$/, 'html/$1.html')
|
|
24
|
+
return true
|
|
25
|
+
}
|
|
26
|
+
}),
|
|
28
27
|
)
|
|
29
28
|
for (const page of Object.values(pages)) {
|
|
30
29
|
const jsImports = page.imports
|
|
31
30
|
const cssImports = page.viteMetadata.importedCss
|
|
32
31
|
const images = page.moduleIds.filter((img) => {
|
|
33
|
-
return
|
|
32
|
+
return page.modules[img].originalLength > assetsInlineLimit && imageFileRE.test(img)
|
|
34
33
|
})
|
|
35
34
|
let imagePreloads = '\n'
|
|
36
35
|
for (let image of images) {
|
|
@@ -45,17 +44,12 @@ export async function closeBundle(resolvedBundle) {
|
|
|
45
44
|
for (const js of jsImports) {
|
|
46
45
|
jsPreloads += ` <link rel="modulepreload" crossorigin href="${base}${js}">\n`
|
|
47
46
|
}
|
|
48
|
-
const pageHtml = await appendHead(
|
|
49
|
-
indexHtml,
|
|
50
|
-
imagePreloads,
|
|
51
|
-
cssPreloads,
|
|
52
|
-
jsPreloads
|
|
53
|
-
)
|
|
47
|
+
const pageHtml = await appendHead(indexHtml, imagePreloads, cssPreloads, jsPreloads)
|
|
54
48
|
writeHtml(page, pageHtml, distDir)
|
|
55
49
|
}
|
|
56
50
|
}
|
|
57
51
|
|
|
58
|
-
async function appendHead
|
|
52
|
+
async function appendHead(html, ...tags) {
|
|
59
53
|
const encoder = new TextEncoder()
|
|
60
54
|
const decoder = new TextDecoder()
|
|
61
55
|
let output = ''
|
package/plugin/stores.js
CHANGED
package/plugin/virtual.js
CHANGED
|
@@ -2,7 +2,6 @@ import { readFileSync, existsSync } from 'node:fs'
|
|
|
2
2
|
import { resolve } from 'node:path'
|
|
3
3
|
import { findExports } from 'mlly'
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
const virtualRoot = resolve(import.meta.dirname, '..', 'virtual')
|
|
7
6
|
const virtualRootTS = resolve(import.meta.dirname, '..', 'virtual-ts')
|
|
8
7
|
|
|
@@ -68,7 +67,6 @@ export function loadVirtualModule(virtualInput) {
|
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
|
|
72
70
|
virtualModulesTS.includes = function (virtual) {
|
|
73
71
|
if (!virtual) {
|
|
74
72
|
return false
|
|
@@ -81,7 +79,6 @@ virtualModulesTS.includes = function (virtual) {
|
|
|
81
79
|
return false
|
|
82
80
|
}
|
|
83
81
|
|
|
84
|
-
|
|
85
82
|
virtualModules.includes = function (virtual) {
|
|
86
83
|
if (!virtual) {
|
|
87
84
|
return false
|
|
@@ -110,9 +107,7 @@ function loadVirtualModuleOverride(viteProjectRoot, virtualInput) {
|
|
|
110
107
|
}
|
|
111
108
|
|
|
112
109
|
export function loadSource(id) {
|
|
113
|
-
const filePath = id
|
|
114
|
-
.replace(/\?client$/, '')
|
|
115
|
-
.replace(/\?server$/, '')
|
|
110
|
+
const filePath = id.replace(/\?client$/, '').replace(/\?server$/, '')
|
|
116
111
|
return readFileSync(filePath, 'utf8')
|
|
117
112
|
}
|
|
118
113
|
|
package/rendering.js
CHANGED
|
@@ -43,9 +43,9 @@ export function onAllReady(app) {
|
|
|
43
43
|
})
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
export async function createRenderFunction
|
|
46
|
+
export async function createRenderFunction({ routes, create }) {
|
|
47
47
|
// Used when hydrating React Router on the client
|
|
48
|
-
const routeMap = Object.fromEntries(routes.map(_ => [_.path, _]))
|
|
48
|
+
const routeMap = Object.fromEntries(routes.map((_) => [_.path, _]))
|
|
49
49
|
|
|
50
50
|
// Registered as reply.render()
|
|
51
51
|
return function () {
|
|
@@ -56,13 +56,13 @@ export async function createRenderFunction ({ routes, create }) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
async function createStreamingResponse
|
|
59
|
+
async function createStreamingResponse(req, routes) {
|
|
60
60
|
// SSR stream
|
|
61
61
|
const body = await onShellReady(req.route.app)
|
|
62
62
|
return { routes, context: req.route, body }
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
async function createResponse
|
|
65
|
+
async function createResponse(req, routes) {
|
|
66
66
|
let body
|
|
67
67
|
if (!req.route.clientOnly) {
|
|
68
68
|
// SSR string
|
|
@@ -72,7 +72,7 @@ async function createResponse (req, routes) {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// The return value of this function gets registered as reply.html()
|
|
75
|
-
export async function createHtmlFunction
|
|
75
|
+
export async function createHtmlFunction(source, _, config) {
|
|
76
76
|
// Creates `universal` and `serverOnly` sets of
|
|
77
77
|
// HTML `beforeElement` and `afterElement` templates
|
|
78
78
|
const templates = await createHtmlTemplates(source, config)
|
|
@@ -89,23 +89,17 @@ export async function createHtmlFunction (source, _, config) {
|
|
|
89
89
|
// Turn off hydration
|
|
90
90
|
context.hydration = ''
|
|
91
91
|
|
|
92
|
-
return streamShell(
|
|
93
|
-
templates.serverOnly,
|
|
94
|
-
context,
|
|
95
|
-
body,
|
|
96
|
-
)
|
|
92
|
+
return streamShell(templates.serverOnly, context, body)
|
|
97
93
|
}
|
|
98
94
|
|
|
99
95
|
// Embed full hydration script
|
|
100
|
-
context.hydration =
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}\n</script>`
|
|
108
|
-
)
|
|
96
|
+
context.hydration = `<script>\nwindow.route = ${
|
|
97
|
+
// Server data payload
|
|
98
|
+
devalue.uneval(context.toJSON())
|
|
99
|
+
}\nwindow.routes = ${
|
|
100
|
+
// Universal router payload
|
|
101
|
+
devalue.uneval(routes.toJSON())
|
|
102
|
+
}\n</script>`
|
|
109
103
|
|
|
110
104
|
// In all other cases use universal,
|
|
111
105
|
// template which works the same for SSR and CSR.
|
|
@@ -118,35 +112,22 @@ export async function createHtmlFunction (source, _, config) {
|
|
|
118
112
|
}
|
|
119
113
|
}
|
|
120
114
|
|
|
121
|
-
export async function sendClientOnlyShell
|
|
115
|
+
export async function sendClientOnlyShell(templates, context) {
|
|
122
116
|
return await transformHtmlTemplate(
|
|
123
117
|
context.useHead,
|
|
124
|
-
`${
|
|
125
|
-
templates.beforeElement(context)
|
|
126
|
-
}${
|
|
127
|
-
templates.afterElement(context)
|
|
128
|
-
}`
|
|
118
|
+
`${templates.beforeElement(context)}${templates.afterElement(context)}`,
|
|
129
119
|
)
|
|
130
120
|
}
|
|
131
121
|
|
|
132
|
-
export function streamShell
|
|
122
|
+
export function streamShell(templates, context, body) {
|
|
133
123
|
return Readable.from(createShellStream(templates, context, body))
|
|
134
124
|
}
|
|
135
125
|
|
|
136
|
-
async function
|
|
137
|
-
yield await transformHtmlTemplate(
|
|
138
|
-
context.useHead,
|
|
139
|
-
templates.beforeElement(context)
|
|
140
|
-
)
|
|
126
|
+
async function* createShellStream(templates, context, body) {
|
|
127
|
+
yield await transformHtmlTemplate(context.useHead, templates.beforeElement(context))
|
|
141
128
|
|
|
142
129
|
for await (const chunk of body) {
|
|
143
|
-
yield await transformHtmlTemplate(
|
|
144
|
-
context.useHead,
|
|
145
|
-
chunk.toString()
|
|
146
|
-
)
|
|
130
|
+
yield await transformHtmlTemplate(context.useHead, chunk.toString())
|
|
147
131
|
}
|
|
148
|
-
yield await transformHtmlTemplate(
|
|
149
|
-
context.useHead,
|
|
150
|
-
templates.afterElement(context)
|
|
151
|
-
)
|
|
132
|
+
yield await transformHtmlTemplate(context.useHead, templates.afterElement(context))
|
|
152
133
|
}
|
package/routing.js
CHANGED
|
@@ -4,7 +4,7 @@ import Youch from 'youch'
|
|
|
4
4
|
import RouteContext from './context.js'
|
|
5
5
|
import { createHtmlFunction } from './rendering.js'
|
|
6
6
|
|
|
7
|
-
export async function prepareClient
|
|
7
|
+
export async function prepareClient(entries, _) {
|
|
8
8
|
const client = entries.ssr
|
|
9
9
|
if (client.context instanceof Promise) {
|
|
10
10
|
client.context = await client.context
|
|
@@ -19,7 +19,7 @@ export async function prepareClient (entries, _) {
|
|
|
19
19
|
return client
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export function createErrorHandler
|
|
22
|
+
export function createErrorHandler(_, scope, config) {
|
|
23
23
|
return async (error, req, reply) => {
|
|
24
24
|
req.log.error(error)
|
|
25
25
|
if (config.dev) {
|
|
@@ -35,25 +35,19 @@ export function createErrorHandler (_, scope, config) {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export async function createRoute
|
|
38
|
+
export async function createRoute({ client, errorHandler, route }, scope, config) {
|
|
39
39
|
if (route.configure) {
|
|
40
40
|
await route.configure(scope)
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// Used when hydrating Vue Router on the client
|
|
44
|
-
const routeMap = Object.fromEntries(client.routes.map(_ => [_.path, _]))
|
|
44
|
+
const routeMap = Object.fromEntries(client.routes.map((_) => [_.path, _]))
|
|
45
45
|
|
|
46
46
|
// Extend with route context initialization module
|
|
47
47
|
RouteContext.extend(client.context)
|
|
48
48
|
|
|
49
49
|
const onRequest = async (req, reply) => {
|
|
50
|
-
req.route = await RouteContext.create(
|
|
51
|
-
scope,
|
|
52
|
-
req,
|
|
53
|
-
reply,
|
|
54
|
-
route,
|
|
55
|
-
client.context,
|
|
56
|
-
)
|
|
50
|
+
req.route = await RouteContext.create(scope, req, reply, route, client.context)
|
|
57
51
|
}
|
|
58
52
|
|
|
59
53
|
const preHandler = [
|
|
@@ -67,7 +61,7 @@ export async function createRoute ({ client, errorHandler, route }, scope, confi
|
|
|
67
61
|
})
|
|
68
62
|
req.route.app = app
|
|
69
63
|
}
|
|
70
|
-
}
|
|
64
|
+
},
|
|
71
65
|
]
|
|
72
66
|
|
|
73
67
|
if (route.getData) {
|
|
@@ -111,7 +105,7 @@ export async function createRoute ({ client, errorHandler, route }, scope, confi
|
|
|
111
105
|
handler = (_, reply) => reply.html()
|
|
112
106
|
} else {
|
|
113
107
|
const { id } = route
|
|
114
|
-
const htmlPath = id.replace(/
|
|
108
|
+
const htmlPath = id.replace('pages/', 'html/').replace(/\.(j|t)sx$/, '.html')
|
|
115
109
|
let distDir = config.vite.build.outDir
|
|
116
110
|
if (!isAbsolute(config.vite.build.outDir)) {
|
|
117
111
|
distDir = join(config.vite.root, distDir)
|
|
@@ -122,8 +116,8 @@ export async function createRoute ({ client, errorHandler, route }, scope, confi
|
|
|
122
116
|
}
|
|
123
117
|
|
|
124
118
|
// Replace wildcard routes with Fastify compatible syntax
|
|
125
|
-
const routePath = route.path.replace(
|
|
126
|
-
|
|
119
|
+
const routePath = route.path.replace(/:\w[\w-]*\+/, '*')
|
|
120
|
+
|
|
127
121
|
unshiftHook(route, 'onRequest', onRequest)
|
|
128
122
|
unshiftHook(route, 'preHandler', preHandler)
|
|
129
123
|
|
|
@@ -139,14 +133,14 @@ export async function createRoute ({ client, errorHandler, route }, scope, confi
|
|
|
139
133
|
// If getData is provided, register JSON endpoint for it
|
|
140
134
|
scope.get(`/-/data${routePath}`, {
|
|
141
135
|
onRequest,
|
|
142
|
-
async handler
|
|
136
|
+
async handler(req, reply) {
|
|
143
137
|
reply.send(await route.getData(req.route))
|
|
144
138
|
},
|
|
145
139
|
})
|
|
146
140
|
}
|
|
147
141
|
}
|
|
148
142
|
|
|
149
|
-
function unshiftHook
|
|
143
|
+
function unshiftHook(route, hookName, hook) {
|
|
150
144
|
if (!route[hookName]) {
|
|
151
145
|
route[hookName] = []
|
|
152
146
|
}
|
package/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Otherwise we get a ReferenceError, but since
|
|
2
2
|
// this function is only ran once, there's no overhead
|
|
3
3
|
class Routes extends Array {
|
|
4
|
-
toJSON
|
|
4
|
+
toJSON() {
|
|
5
5
|
return this.map((route) => {
|
|
6
6
|
return {
|
|
7
7
|
id: route.id,
|
|
@@ -39,71 +39,68 @@ export function prepareServer(server) {
|
|
|
39
39
|
})
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
export async function createRoutes
|
|
42
|
+
export async function createRoutes(fromPromise, { param } = { param: /\[([.\w]+\+?)\]/ }) {
|
|
43
43
|
const { default: from } = await fromPromise
|
|
44
44
|
const importPaths = Object.keys(from)
|
|
45
45
|
const promises = []
|
|
46
46
|
if (Array.isArray(from)) {
|
|
47
47
|
for (const routeDef of from) {
|
|
48
48
|
promises.push(
|
|
49
|
-
getRouteModule(routeDef.path, routeDef.component)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}),
|
|
49
|
+
getRouteModule(routeDef.path, routeDef.component).then((routeModule) => {
|
|
50
|
+
return {
|
|
51
|
+
id: routeDef.path,
|
|
52
|
+
name: routeDef.path ?? routeModule.path,
|
|
53
|
+
path: routeDef.path ?? routeModule.path,
|
|
54
|
+
...routeModule,
|
|
55
|
+
}
|
|
56
|
+
}),
|
|
58
57
|
)
|
|
59
58
|
}
|
|
60
59
|
} else {
|
|
61
60
|
// Ensure that static routes have precedence over the dynamic ones
|
|
62
|
-
for (const path of importPaths.sort((a, b) => a > b ? -1 : 1)) {
|
|
61
|
+
for (const path of importPaths.sort((a, b) => (a > b ? -1 : 1))) {
|
|
63
62
|
promises.push(
|
|
64
|
-
getRouteModule(path, from[path])
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
getRouteModule(path, from[path]).then((routeModule) => {
|
|
64
|
+
const route = {
|
|
65
|
+
id: path,
|
|
66
|
+
layout: routeModule.layout,
|
|
67
|
+
name: path
|
|
68
|
+
// Remove /pages and .vue extension
|
|
69
|
+
.slice(6, -4)
|
|
70
|
+
// Remove params
|
|
71
|
+
.replace(param, '')
|
|
72
|
+
// Remove leading and trailing slashes
|
|
73
|
+
.replace(/^\/*|\/*$/g, '')
|
|
74
|
+
// Replace slashes with underscores
|
|
75
|
+
.replace(/\//g, '_'),
|
|
76
|
+
path:
|
|
77
|
+
routeModule.path ??
|
|
78
|
+
path
|
|
70
79
|
// Remove /pages and .vue extension
|
|
71
80
|
.slice(6, -4)
|
|
72
|
-
//
|
|
73
|
-
.replace(param,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
// Remove /pages and .vue extension
|
|
82
|
-
.slice(6, -4)
|
|
83
|
-
// Replace [id] with :id and [slug+] with :slug+
|
|
84
|
-
.replace(param, (_, m) => `:${m}`)
|
|
85
|
-
.replace(/:\w+\+/, (_, m) => `*`)
|
|
86
|
-
// Replace '/index' with '/'
|
|
87
|
-
.replace(/\/index$/, '/')
|
|
88
|
-
// Remove trailing slashs
|
|
89
|
-
.replace(/(.+)\/+$/, (...m) => m[1]),
|
|
90
|
-
...routeModule,
|
|
91
|
-
}
|
|
81
|
+
// Replace [id] with :id and [slug+] with :slug+
|
|
82
|
+
.replace(param, (_, m) => `:${m}`)
|
|
83
|
+
.replace(/:\w+\+/, (_, m) => `*`)
|
|
84
|
+
// Replace '/index' with '/'
|
|
85
|
+
.replace(/\/index$/, '/')
|
|
86
|
+
// Remove trailing slashs
|
|
87
|
+
.replace(/(.+)\/+$/, (...m) => m[1]),
|
|
88
|
+
...routeModule,
|
|
89
|
+
}
|
|
92
90
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
if (route.name === '') {
|
|
92
|
+
route.name = 'catch-all'
|
|
93
|
+
}
|
|
96
94
|
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
return route
|
|
96
|
+
}),
|
|
99
97
|
)
|
|
100
98
|
}
|
|
101
99
|
}
|
|
102
|
-
return new Routes(...await Promise.all(promises))
|
|
100
|
+
return new Routes(...(await Promise.all(promises)))
|
|
103
101
|
}
|
|
104
102
|
|
|
105
|
-
|
|
106
|
-
function getRouteModuleExports (routeModule) {
|
|
103
|
+
function getRouteModuleExports(routeModule) {
|
|
107
104
|
return {
|
|
108
105
|
// The Route component (default export)
|
|
109
106
|
component: routeModule.default,
|
|
@@ -133,7 +130,7 @@ function getRouteModuleExports (routeModule) {
|
|
|
133
130
|
}
|
|
134
131
|
}
|
|
135
132
|
|
|
136
|
-
async function getRouteModule
|
|
133
|
+
async function getRouteModule(path, routeModuleInput) {
|
|
137
134
|
if (typeof routeModuleInput === 'function') {
|
|
138
135
|
const routeModule = await routeModuleInput()
|
|
139
136
|
return getRouteModuleExports(routeModule)
|
package/templating.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHtmlTemplateFunction } from '@fastify/vite/utils'
|
|
2
2
|
import { HTMLRewriter } from 'html-rewriter-wasm'
|
|
3
3
|
|
|
4
|
-
export async function createHtmlTemplates
|
|
4
|
+
export async function createHtmlTemplates(source, config) {
|
|
5
5
|
const el = '<!-- element -->'
|
|
6
6
|
|
|
7
7
|
const universal = source.split(el)
|
|
@@ -22,7 +22,7 @@ export async function createHtmlTemplates (source, config) {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
async function removeClientModule
|
|
25
|
+
async function removeClientModule(html, config) {
|
|
26
26
|
const decoder = new TextDecoder()
|
|
27
27
|
|
|
28
28
|
let output = ''
|
|
@@ -31,7 +31,7 @@ async function removeClientModule (html, config) {
|
|
|
31
31
|
})
|
|
32
32
|
|
|
33
33
|
rewriter.on('script', {
|
|
34
|
-
element
|
|
34
|
+
element(element) {
|
|
35
35
|
for (const [attr, value] of element.attributes) {
|
|
36
36
|
if (attr === 'type' && value === 'module') {
|
|
37
37
|
element.replace('')
|
package/virtual/core.jsx
CHANGED
|
@@ -18,11 +18,7 @@ export function createServerAction(name) {
|
|
|
18
18
|
export function useServerAction(action, options = {}) {
|
|
19
19
|
if (import.meta.env.SSR) {
|
|
20
20
|
const { req, server } = useRouteContext()
|
|
21
|
-
req.route.actionData[action] = waitFetch(
|
|
22
|
-
`${server.serverURL}${action}`,
|
|
23
|
-
options,
|
|
24
|
-
req.fetchMap,
|
|
25
|
-
)
|
|
21
|
+
req.route.actionData[action] = waitFetch(`${server.serverURL}${action}`, options, req.fetchMap)
|
|
26
22
|
return req.route.actionData[action]
|
|
27
23
|
}
|
|
28
24
|
const { actionData } = useRouteContext()
|
|
@@ -43,9 +39,7 @@ export function AppRoute({ ctxHydration, ctx, children }) {
|
|
|
43
39
|
value={{
|
|
44
40
|
...ctx,
|
|
45
41
|
...ctxHydration,
|
|
46
|
-
state: isServer
|
|
47
|
-
? ctxHydration.state ?? {}
|
|
48
|
-
: proxy(ctxHydration.state ?? {}),
|
|
42
|
+
state: isServer ? (ctxHydration.state ?? {}) : proxy(ctxHydration.state ?? {}),
|
|
49
43
|
}}
|
|
50
44
|
>
|
|
51
45
|
<Layout>{children}</Layout>
|
|
@@ -72,7 +66,6 @@ export function AppRoute({ ctxHydration, ctx, children }) {
|
|
|
72
66
|
|
|
73
67
|
// When the next route renders client-side,
|
|
74
68
|
// force it to execute all URMA hooks again
|
|
75
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: I'm inclined to believe you, Biome, but I'm not risking it.
|
|
76
69
|
useEffect(() => {
|
|
77
70
|
window.route.firstRender = false
|
|
78
71
|
window.route.actionData = {}
|
|
@@ -125,9 +118,7 @@ export function AppRoute({ ctxHydration, ctx, children }) {
|
|
|
125
118
|
value={{
|
|
126
119
|
...ctxHydration,
|
|
127
120
|
...ctx,
|
|
128
|
-
state: isServer
|
|
129
|
-
? ctxHydration.state ?? {}
|
|
130
|
-
: proxy(ctxHydration.state ?? {}),
|
|
121
|
+
state: isServer ? (ctxHydration.state ?? {}) : proxy(ctxHydration.state ?? {}),
|
|
131
122
|
}}
|
|
132
123
|
>
|
|
133
124
|
<Layout>{children}</Layout>
|
package/virtual/create.jsx
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
} from '@unhead/react/client'
|
|
4
|
-
import {
|
|
5
|
-
UnheadProvider as ServerUnheadProvider
|
|
6
|
-
} from '@unhead/react/server?server'
|
|
1
|
+
import { UnheadProvider as ClientUnheadProvider } from '@unhead/react/client'
|
|
2
|
+
import { UnheadProvider as ServerUnheadProvider } from '@unhead/react/server?server'
|
|
7
3
|
|
|
8
4
|
import Root from '$app/root.jsx'
|
|
9
5
|
|
|
10
6
|
export default function create({ url, ...serverInit }) {
|
|
11
|
-
const UnheadProvider = import.meta.env.SSR
|
|
12
|
-
? ServerUnheadProvider
|
|
13
|
-
: ClientUnheadProvider
|
|
7
|
+
const UnheadProvider = import.meta.env.SSR ? ServerUnheadProvider : ClientUnheadProvider
|
|
14
8
|
return (
|
|
15
9
|
<UnheadProvider value={serverInit.ctxHydration.useHead}>
|
|
16
10
|
<Root url={url} {...serverInit} />
|
package/virtual/layouts.js
CHANGED
|
@@ -4,11 +4,7 @@ const DefaultLayout = () => import('$app/layouts/default.jsx')
|
|
|
4
4
|
|
|
5
5
|
const appLayouts = import.meta.glob('/layouts/*.{jsx,tsx}')
|
|
6
6
|
|
|
7
|
-
if (
|
|
8
|
-
!Object.keys(appLayouts).some((path) =>
|
|
9
|
-
path.match(/\/layouts\/default\.(j|t)sx/),
|
|
10
|
-
)
|
|
11
|
-
) {
|
|
7
|
+
if (!Object.keys(appLayouts).some((path) => path.match(/\/layouts\/default\.(j|t)sx/))) {
|
|
12
8
|
appLayouts['/layouts/default.jsx'] = DefaultLayout
|
|
13
9
|
}
|
|
14
10
|
|
package/virtual/mount.js
CHANGED
|
@@ -5,12 +5,10 @@ import routes from '$app/routes.js'
|
|
|
5
5
|
import create from '$app/create.jsx'
|
|
6
6
|
import * as context from '$app/context.js'
|
|
7
7
|
|
|
8
|
-
async function mountApp
|
|
8
|
+
async function mountApp(...targets) {
|
|
9
9
|
const ctxHydration = await extendContext(window.route, context)
|
|
10
10
|
const resolvedRoutes = await hydrateRoutes(routes)
|
|
11
|
-
const routeMap = Object.fromEntries(
|
|
12
|
-
resolvedRoutes.map((route) => [route.path, route]),
|
|
13
|
-
)
|
|
11
|
+
const routeMap = Object.fromEntries(resolvedRoutes.map((route) => [route.path, route]))
|
|
14
12
|
const useHead = createHead()
|
|
15
13
|
ctxHydration.useHead = useHead
|
|
16
14
|
ctxHydration.useHead.push(window.route.head)
|
|
@@ -21,7 +19,6 @@ async function mountApp (...targets) {
|
|
|
21
19
|
routeMap,
|
|
22
20
|
})
|
|
23
21
|
|
|
24
|
-
|
|
25
22
|
let mountTargetFound = false
|
|
26
23
|
for (const target of targets) {
|
|
27
24
|
const targetElem = document.querySelector(target)
|
|
@@ -42,14 +39,17 @@ async function mountApp (...targets) {
|
|
|
42
39
|
|
|
43
40
|
mountApp('#root', 'main')
|
|
44
41
|
|
|
45
|
-
async function extendContext
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
async function extendContext(
|
|
43
|
+
ctx,
|
|
44
|
+
{
|
|
45
|
+
// The route context initialization function
|
|
46
|
+
default: setter,
|
|
47
|
+
// We destructure state here just to discard it from extra
|
|
48
|
+
state,
|
|
49
|
+
// Other named exports from context.js
|
|
50
|
+
...extra
|
|
51
|
+
},
|
|
52
|
+
) {
|
|
53
53
|
Object.assign(ctx, extra)
|
|
54
54
|
if (setter) {
|
|
55
55
|
await setter(ctx)
|
package/virtual/resource.js
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
const clientFetchMap = new Map()
|
|
2
2
|
const clientResourceMap = new Map()
|
|
3
3
|
|
|
4
|
-
export function waitResource(
|
|
5
|
-
path,
|
|
6
|
-
id,
|
|
7
|
-
promise,
|
|
8
|
-
resourceMap = clientResourceMap,
|
|
9
|
-
) {
|
|
4
|
+
export function waitResource(path, id, promise, resourceMap = clientResourceMap) {
|
|
10
5
|
const resourceId = `${path}:${id}`
|
|
11
6
|
const loaderStatus = resourceMap.get(resourceId)
|
|
12
7
|
if (loaderStatus) {
|
package/virtual/root.jsx
CHANGED
|
@@ -12,11 +12,7 @@ export default function Root({ url, routes, head, ctxHydration, routeMap }) {
|
|
|
12
12
|
key={path}
|
|
13
13
|
path={path}
|
|
14
14
|
element={
|
|
15
|
-
<AppRoute
|
|
16
|
-
head={head}
|
|
17
|
-
ctxHydration={ctxHydration}
|
|
18
|
-
ctx={routeMap[path]}
|
|
19
|
-
>
|
|
15
|
+
<AppRoute head={head} ctxHydration={ctxHydration} ctx={routeMap[path]}>
|
|
20
16
|
<Component />
|
|
21
17
|
</AppRoute>
|
|
22
18
|
}
|
package/virtual-ts/core.tsx
CHANGED
|
@@ -18,11 +18,7 @@ export function createServerAction(name) {
|
|
|
18
18
|
export function useServerAction(action, options = {}) {
|
|
19
19
|
if (import.meta.env.SSR) {
|
|
20
20
|
const { req, server } = useRouteContext()
|
|
21
|
-
req.route.actionData[action] = waitFetch(
|
|
22
|
-
`${server.serverURL}${action}`,
|
|
23
|
-
options,
|
|
24
|
-
req.fetchMap,
|
|
25
|
-
)
|
|
21
|
+
req.route.actionData[action] = waitFetch(`${server.serverURL}${action}`, options, req.fetchMap)
|
|
26
22
|
return req.route.actionData[action]
|
|
27
23
|
}
|
|
28
24
|
const { actionData } = useRouteContext()
|
|
@@ -43,9 +39,7 @@ export function AppRoute({ ctxHydration, ctx, children }) {
|
|
|
43
39
|
value={{
|
|
44
40
|
...ctx,
|
|
45
41
|
...ctxHydration,
|
|
46
|
-
state: isServer
|
|
47
|
-
? ctxHydration.state ?? {}
|
|
48
|
-
: proxy(ctxHydration.state ?? {}),
|
|
42
|
+
state: isServer ? (ctxHydration.state ?? {}) : proxy(ctxHydration.state ?? {}),
|
|
49
43
|
}}
|
|
50
44
|
>
|
|
51
45
|
<Layout>{children}</Layout>
|
|
@@ -72,7 +66,6 @@ export function AppRoute({ ctxHydration, ctx, children }) {
|
|
|
72
66
|
|
|
73
67
|
// When the next route renders client-side,
|
|
74
68
|
// force it to execute all URMA hooks again
|
|
75
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: I'm inclined to believe you, Biome, but I'm not risking it.
|
|
76
69
|
useEffect(() => {
|
|
77
70
|
window.route.firstRender = false
|
|
78
71
|
window.route.actionData = {}
|
|
@@ -125,9 +118,7 @@ export function AppRoute({ ctxHydration, ctx, children }) {
|
|
|
125
118
|
value={{
|
|
126
119
|
...ctxHydration,
|
|
127
120
|
...ctx,
|
|
128
|
-
state: isServer
|
|
129
|
-
? ctxHydration.state ?? {}
|
|
130
|
-
: proxy(ctxHydration.state ?? {}),
|
|
121
|
+
state: isServer ? (ctxHydration.state ?? {}) : proxy(ctxHydration.state ?? {}),
|
|
131
122
|
}}
|
|
132
123
|
>
|
|
133
124
|
<Layout>{children}</Layout>
|
package/virtual-ts/create.tsx
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
} from '@unhead/react/client'
|
|
4
|
-
import {
|
|
5
|
-
UnheadProvider as ServerUnheadProvider
|
|
6
|
-
} from '@unhead/react/server?server'
|
|
1
|
+
import { UnheadProvider as ClientUnheadProvider } from '@unhead/react/client'
|
|
2
|
+
import { UnheadProvider as ServerUnheadProvider } from '@unhead/react/server?server'
|
|
7
3
|
|
|
8
4
|
import Root from '$app/root.jsx'
|
|
9
5
|
|
|
10
6
|
export default function create({ url, ...serverInit }) {
|
|
11
|
-
const UnheadProvider = import.meta.env.SSR
|
|
12
|
-
? ServerUnheadProvider
|
|
13
|
-
: ClientUnheadProvider
|
|
7
|
+
const UnheadProvider = import.meta.env.SSR ? ServerUnheadProvider : ClientUnheadProvider
|
|
14
8
|
return (
|
|
15
9
|
<UnheadProvider value={serverInit.ctxHydration.useHead}>
|
|
16
10
|
<Root url={url} {...serverInit} />
|
package/virtual-ts/layouts.ts
CHANGED
|
@@ -4,11 +4,7 @@ const DefaultLayout = () => import('$app/layouts/default.tsx')
|
|
|
4
4
|
|
|
5
5
|
const appLayouts = import.meta.glob('/layouts/*.{jsx,tsx}')
|
|
6
6
|
|
|
7
|
-
if (
|
|
8
|
-
!Object.keys(appLayouts).some((path) =>
|
|
9
|
-
path.match(/\/layouts\/default\.(j|t)sx/),
|
|
10
|
-
)
|
|
11
|
-
) {
|
|
7
|
+
if (!Object.keys(appLayouts).some((path) => path.match(/\/layouts\/default\.(j|t)sx/))) {
|
|
12
8
|
appLayouts['/layouts/default.tsx'] = DefaultLayout
|
|
13
9
|
}
|
|
14
10
|
|
package/virtual-ts/mount.ts
CHANGED
|
@@ -5,12 +5,10 @@ import routes from '$app/routes.js'
|
|
|
5
5
|
import create from '$app/create.jsx'
|
|
6
6
|
import * as context from '$app/context.js'
|
|
7
7
|
|
|
8
|
-
async function mountApp
|
|
8
|
+
async function mountApp(...targets) {
|
|
9
9
|
const ctxHydration = await extendContext(window.route, context)
|
|
10
10
|
const resolvedRoutes = await hydrateRoutes(routes)
|
|
11
|
-
const routeMap = Object.fromEntries(
|
|
12
|
-
resolvedRoutes.map((route) => [route.path, route]),
|
|
13
|
-
)
|
|
11
|
+
const routeMap = Object.fromEntries(resolvedRoutes.map((route) => [route.path, route]))
|
|
14
12
|
const useHead = createHead()
|
|
15
13
|
ctxHydration.useHead = useHead
|
|
16
14
|
ctxHydration.useHead.push(window.route.head)
|
|
@@ -21,7 +19,6 @@ async function mountApp (...targets) {
|
|
|
21
19
|
routeMap,
|
|
22
20
|
})
|
|
23
21
|
|
|
24
|
-
|
|
25
22
|
let mountTargetFound = false
|
|
26
23
|
for (const target of targets) {
|
|
27
24
|
const targetElem = document.querySelector(target)
|
|
@@ -42,14 +39,17 @@ async function mountApp (...targets) {
|
|
|
42
39
|
|
|
43
40
|
mountApp('#root', 'main')
|
|
44
41
|
|
|
45
|
-
async function extendContext
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
async function extendContext(
|
|
43
|
+
ctx,
|
|
44
|
+
{
|
|
45
|
+
// The route context initialization function
|
|
46
|
+
default: setter,
|
|
47
|
+
// We destructure state here just to discard it from extra
|
|
48
|
+
state,
|
|
49
|
+
// Other named exports from context.js
|
|
50
|
+
...extra
|
|
51
|
+
},
|
|
52
|
+
) {
|
|
53
53
|
Object.assign(ctx, extra)
|
|
54
54
|
if (setter) {
|
|
55
55
|
await setter(ctx)
|
package/virtual-ts/resource.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
const clientFetchMap = new Map()
|
|
2
2
|
const clientResourceMap = new Map()
|
|
3
3
|
|
|
4
|
-
export function waitResource(
|
|
5
|
-
path,
|
|
6
|
-
id,
|
|
7
|
-
promise,
|
|
8
|
-
resourceMap = clientResourceMap,
|
|
9
|
-
) {
|
|
4
|
+
export function waitResource(path, id, promise, resourceMap = clientResourceMap) {
|
|
10
5
|
const resourceId = `${path}:${id}`
|
|
11
6
|
const loaderStatus = resourceMap.get(resourceId)
|
|
12
7
|
if (loaderStatus) {
|
package/virtual-ts/root.tsx
CHANGED
|
@@ -12,11 +12,7 @@ export default function Root({ url, routes, head, ctxHydration, routeMap }) {
|
|
|
12
12
|
key={path}
|
|
13
13
|
path={path}
|
|
14
14
|
element={
|
|
15
|
-
<AppRoute
|
|
16
|
-
head={head}
|
|
17
|
-
ctxHydration={ctxHydration}
|
|
18
|
-
ctx={routeMap[path]}
|
|
19
|
-
>
|
|
15
|
+
<AppRoute head={head} ctxHydration={ctxHydration} ctx={routeMap[path]}>
|
|
20
16
|
<Component />
|
|
21
17
|
</AppRoute>
|
|
22
18
|
}
|