@fastify/react 1.1.2 → 1.1.3

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 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 (fromInput) {
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 (func) {
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.2",
3
+ "version": "1.1.3",
4
4
  "description": "The official @fastify/vite renderer for React",
5
5
  "keywords": [
6
6
  "fastify",
7
- "vite",
8
- "react"
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/layouts/default.jsx",
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-ts/context.ts",
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.1"
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 ({ ts } = {}) {
15
+ export default function viteFastifyReactPlugin({ ts } = {}) {
16
16
  const context = {
17
17
  root: null,
18
18
  }
19
- return [viteFastify({
20
- clientModule: ts ? '$app/index.ts' : '$app/index.js'
21
- }), {
22
- // https://vite.dev/guide/api-plugin#conventions
23
- name: 'vite-plugin-react-fastify',
24
- config,
25
- configResolved: configResolved.bind(context),
26
- resolveId: resolveId.bind(context),
27
- async load (id) {
28
- if (id.includes('?server') && !this.environment.config.build?.ssr) {
29
- const source = loadSource(id)
30
- return createPlaceholderExports(source)
31
- }
32
- if (id.includes('?client') && this.environment.config.build?.ssr) {
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
- transformIndexHtml: {
44
- order: 'post',
45
- handler: transformIndexHtml.bind(context)
46
- },
47
- closeBundle () {
48
- closeBundle.call(this, context.resolvedBundle)
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 (html, { bundle }) {
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 (config) {
64
+ function configResolved(config) {
62
65
  this.resolvedConfig = config
63
66
  this.root = config.root
64
67
  }
65
68
 
66
- function config (config, { command }) {
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 (warning, rollupWarn) {
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 (code) {
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
@@ -23,6 +23,6 @@ test('parseStateKeys', (t) => {
23
23
 
24
24
  }
25
25
  }
26
- `;
26
+ `
27
27
  assert.equal(['user', 'todoList'], parseStateKeys(b))
28
28
  })
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
- .filter(([id, meta]) => {
23
- if (meta.facadeModuleId?.includes('/pages/')) {
24
- meta.htmlPath = meta.facadeModuleId.replace(/.*pages\/(.*)\.(j|t)sx$/, 'html/$1.html')
25
- return true
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 (page.modules[img].originalLength > assetsInlineLimit) && imageFileRE.test(img)
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 (html, ...tags) {
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
@@ -1,4 +1,3 @@
1
-
2
1
  export function generateStores(keys) {
3
2
  let code = `
4
3
  import { useRouteContext } from '@fastify/react/client'
@@ -34,6 +33,6 @@ export const ${key} = new Proxy({
34
33
  }
35
34
  return {
36
35
  code,
37
- map: null
36
+ map: null,
38
37
  }
39
38
  }
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 ({ routes, create }) {
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 (req, routes) {
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 (req, routes) {
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 (source, _, config) {
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
- `<script>\nwindow.route = ${
102
- // Server data payload
103
- devalue.uneval(context.toJSON())
104
- }\nwindow.routes = ${
105
- // Universal router payload
106
- devalue.uneval(routes.toJSON())
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 (templates, context) {
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 (templates, context, body) {
122
+ export function streamShell(templates, context, body) {
133
123
  return Readable.from(createShellStream(templates, context, body))
134
124
  }
135
125
 
136
- async function * createShellStream (templates, context, body) {
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 (entries, _) {
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 (_, scope, config) {
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 ({ client, errorHandler, route }, scope, config) {
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) {
@@ -123,7 +117,7 @@ export async function createRoute ({ client, errorHandler, route }, scope, confi
123
117
 
124
118
  // Replace wildcard routes with Fastify compatible syntax
125
119
  const routePath = route.path.replace(/:[^+]+\+/, '*')
126
-
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 (req, reply) {
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 (route, hookName, hook) {
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 (fromPromise, { param } = { param: /\[([.\w]+\+?)\]/ }) {
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
- .then((routeModule) => {
51
- return {
52
- id: routeDef.path,
53
- name: routeDef.path ?? routeModule.path,
54
- path: routeDef.path ?? routeModule.path,
55
- ...routeModule,
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
- .then((routeModule) => {
66
- const route = {
67
- id: path,
68
- layout: routeModule.layout,
69
- name: path
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
- // Remove params
73
- .replace(param, '')
74
- // Remove leading and trailing slashes
75
- .replace(/^\/*|\/*$/g, '')
76
- // Replace slashes with underscores
77
- .replace(/\//g, '_'),
78
- path:
79
- routeModule.path ??
80
- path
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
- if (route.name === '') {
94
- route.name = 'catch-all'
95
- }
91
+ if (route.name === '') {
92
+ route.name = 'catch-all'
93
+ }
96
94
 
97
- return route
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 (path, routeModuleInput) {
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 (source, config) {
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 (html, config) {
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 (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>
@@ -1,16 +1,10 @@
1
- import {
2
- UnheadProvider as ClientUnheadProvider
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} />
@@ -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 (...targets) {
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 (ctx, {
46
- // The route context initialization function
47
- default: setter,
48
- // We destructure state here just to discard it from extra
49
- state,
50
- // Other named exports from context.js
51
- ...extra
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)
@@ -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
  }
@@ -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>
@@ -1,16 +1,10 @@
1
- import {
2
- UnheadProvider as ClientUnheadProvider
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} />
@@ -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
 
@@ -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 (...targets) {
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 (ctx, {
46
- // The route context initialization function
47
- default: setter,
48
- // We destructure state here just to discard it from extra
49
- state,
50
- // Other named exports from context.js
51
- ...extra
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)
@@ -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) {
@@ -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
  }