@platformatic/react-router 3.29.1 → 3.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/config.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  export interface PlatformaticReactRouterConfig {
9
9
  $schema?: string;
10
10
  logger?: {
11
- level: (
11
+ level?: (
12
12
  | ("fatal" | "error" | "warn" | "info" | "debug" | "trace" | "silent")
13
13
  | {
14
14
  [k: string]: unknown;
@@ -139,7 +139,7 @@ export interface PlatformaticReactRouterConfig {
139
139
  };
140
140
  workersRestartDelay?: number | string;
141
141
  logger?: {
142
- level: (
142
+ level?: (
143
143
  | ("fatal" | "error" | "warn" | "info" | "debug" | "trace" | "silent")
144
144
  | {
145
145
  [k: string]: unknown;
@@ -522,6 +522,18 @@ export interface PlatformaticReactRouterConfig {
522
522
  [k: string]: string | [string, ...string[]];
523
523
  };
524
524
  };
525
+ compileCache?:
526
+ | boolean
527
+ | {
528
+ /**
529
+ * Enable Node.js module compile cache for faster startup
530
+ */
531
+ enabled?: boolean;
532
+ /**
533
+ * Directory to store compile cache. Defaults to .plt/compile-cache in app root
534
+ */
535
+ directory?: string;
536
+ };
525
537
  application?: {
526
538
  reuseTcpPorts?: boolean;
527
539
  workers?:
@@ -580,6 +592,18 @@ export interface PlatformaticReactRouterConfig {
580
592
  )[];
581
593
  [k: string]: unknown;
582
594
  };
595
+ compileCache?:
596
+ | boolean
597
+ | {
598
+ /**
599
+ * Enable Node.js module compile cache for faster startup
600
+ */
601
+ enabled?: boolean;
602
+ /**
603
+ * Directory to store compile cache. Defaults to .plt/compile-cache in app root
604
+ */
605
+ directory?: string;
606
+ };
583
607
  };
584
608
  };
585
609
  vite?: {
package/lib/capability.js CHANGED
@@ -1,3 +1,4 @@
1
+ import fastifyStatic from '@fastify/static'
1
2
  import {
2
3
  cleanBasePath,
3
4
  createServerListener,
@@ -8,13 +9,12 @@ import {
8
9
  resolvePackageViaCJS
9
10
  } from '@platformatic/basic'
10
11
  import { ViteCapability } from '@platformatic/vite'
11
- import { createRequestHandler } from '@react-router/express'
12
- import express from 'express'
13
- import inject from 'light-my-request'
12
+ import fastify from 'fastify'
14
13
  import { existsSync } from 'node:fs'
15
14
  import { readFile, writeFile } from 'node:fs/promises'
16
- import { dirname, resolve } from 'node:path'
17
- import { pinoHttp } from 'pino-http'
15
+ import { dirname, join, resolve } from 'node:path'
16
+ import { Readable } from 'node:stream'
17
+ import { createRequestHandler } from 'react-router'
18
18
  import { satisfies } from 'semver'
19
19
  import { packageJson } from './schema.js'
20
20
 
@@ -22,7 +22,6 @@ const supportedVersions = '^7.0.0'
22
22
 
23
23
  export class ReactRouterCapability extends ViteCapability {
24
24
  #app
25
- #server
26
25
  #reactRouter
27
26
  #basePath
28
27
 
@@ -83,22 +82,6 @@ export class ReactRouterCapability extends ViteCapability {
83
82
  return super.start({ listen })
84
83
  }
85
84
 
86
- async stop () {
87
- const reactRouterConfig = await this.#getReactRouterConfig()
88
-
89
- if (reactRouterConfig.ssr) {
90
- await this._stop()
91
-
92
- if (this.#server?.listening) {
93
- await this._closeServer(this.#server)
94
- }
95
-
96
- return
97
- }
98
-
99
- return super.stop()
100
- }
101
-
102
85
  async build () {
103
86
  const config = this.config
104
87
  const command = config.application.commands.build
@@ -116,7 +99,7 @@ export class ReactRouterCapability extends ViteCapability {
116
99
 
117
100
  await writeFile(
118
101
  resolve(this.root, config.reactRouter.outputDirectory, '.platformatic-build.json'),
119
- JSON.stringify({ basePath: reactRouterConfig.basename ?? '/' }),
102
+ JSON.stringify({ basePath: basePath ?? reactRouterConfig.basename ?? '/' }),
120
103
  'utf-8'
121
104
  )
122
105
  }
@@ -126,16 +109,6 @@ export class ReactRouterCapability extends ViteCapability {
126
109
  return super.getMeta(reactRouterConfig.basename)
127
110
  }
128
111
 
129
- async inject (injectParams, onInject) {
130
- const reactRouterConfig = await this.#getReactRouterConfig()
131
-
132
- if (this.isProduction && reactRouterConfig.ssr) {
133
- return this.#inject(injectParams, onInject)
134
- }
135
-
136
- return super.inject(injectParams, onInject)
137
- }
138
-
139
112
  async #getReactRouterConfig () {
140
113
  const ext = ['ts', 'js'].find(ext => existsSync(resolve(this.root, `react-router.config.${ext}`)))
141
114
 
@@ -157,16 +130,8 @@ export class ReactRouterCapability extends ViteCapability {
157
130
  createServerListener(false, false, { backlog: serverOptions.backlog })
158
131
  }
159
132
 
160
- this.#server = await new Promise((resolve, reject) => {
161
- return this.#app
162
- .listen(listenOptions, function () {
163
- resolve(this)
164
- })
165
- .on('error', reject)
166
- })
167
-
168
- this.url = getServerUrl(this.#server)
169
-
133
+ await this.#app.listen(listenOptions)
134
+ this.url = getServerUrl(this.#app.server)
170
135
  return this.url
171
136
  }
172
137
 
@@ -178,33 +143,110 @@ export class ReactRouterCapability extends ViteCapability {
178
143
  this.verifyOutputDirectory(serverRoot)
179
144
  this.#basePath = await this._getBasePathFromBuildInfo()
180
145
 
181
- const { entrypoint } = await importFile(resolve(serverRoot, 'index.js'))
146
+ const serverModule = await importFile(resolve(serverRoot, 'index.js'))
182
147
 
183
- // Setup express app
184
- this.#app = express()
148
+ // Setup fastify
149
+ this.#app = fastify({ loggerInstance: this.logger })
185
150
  this._setApp(this.#app)
186
- this.#app.disable('x-powered-by')
187
- this.#app.use(pinoHttp({ logger: this.logger }))
188
- this.#app.use(this.#basePath, express.static(clientRoot))
189
- this.#app.all(
190
- `${ensureTrailingSlash(cleanBasePath(this.#basePath))}*`,
191
- createRequestHandler({ build: () => entrypoint })
192
- )
193
151
 
152
+ let assetsRoot = clientRoot
153
+ let publicPath = '/'
154
+ let mainHandler
155
+
156
+ // Since it uses the Fetch API, we don't need to parse the request body.
157
+ this.#app.removeAllContentTypeParsers()
158
+ this.#app.addContentTypeParser('*', function (_, payload, done) {
159
+ done(null, payload)
160
+ })
161
+
162
+ // Custom entrypoint
163
+ if (serverModule.entrypoint) {
164
+ mainHandler = createRequestHandler(() => serverModule.entrypoint, process.env.NODE_ENV)
165
+ // Adapts @react-router/serve to fastify
166
+ } else {
167
+ if (serverModule.assetsBuildDirectory) {
168
+ assetsRoot = resolve(this.root, serverModule.assetsBuildDirectory)
169
+ }
170
+
171
+ if (serverModule.publicPath) {
172
+ publicPath = serverModule.publicPath ?? '/'
173
+ }
174
+
175
+ // RSC build
176
+ if (typeof serverModule.default === 'function') {
177
+ if (serverModule.unstable_reactRouterServeConfig) {
178
+ if (serverModule.unstable_reactRouterServeConfig.assetsBuildDirectory) {
179
+ assetsRoot = resolve(this.root, serverModule.unstable_reactRouterServeConfig.assetsBuildDirectory)
180
+ }
181
+ if (serverModule.unstable_reactRouterServeConfig.publicPath) {
182
+ publicPath = serverModule.unstable_reactRouterServeConfig.publicPath
183
+ }
184
+ }
185
+
186
+ mainHandler = serverModule.default
187
+ } else {
188
+ mainHandler = createRequestHandler(serverModule, process.env.NODE_ENV)
189
+ }
190
+ }
191
+
192
+ await this.#app.register(fastifyStatic, {
193
+ root: resolve(assetsRoot, 'assets'),
194
+ prefix: join(this.#basePath, 'assets'),
195
+ prefixAvoidTrailingSlash: true,
196
+ schemaHide: true,
197
+ decorateReply: false
198
+ })
199
+
200
+ if (publicPath !== '/') {
201
+ await this.#app.register(fastifyStatic, {
202
+ root: resolve(assetsRoot, 'assets'),
203
+ prefix: join(this.#basePath, publicPath),
204
+ prefixAvoidTrailingSlash: true,
205
+ schemaHide: true,
206
+ decorateReply: false
207
+ })
208
+ }
209
+
210
+ this.#app.all(`${ensureTrailingSlash(cleanBasePath(this.#basePath))}*`, this.#handleRequest.bind(this, mainHandler))
211
+
212
+ await this.#app.ready()
194
213
  await this._collectMetrics()
195
- return this.url
196
214
  }
197
215
 
198
- async #inject (injectParams, onInject) {
199
- const res = await inject(this.#app, injectParams, onInject)
216
+ #handleRequest (handle, req) {
217
+ // Support aborting
218
+ const ac = new AbortController()
219
+ let ended = false
220
+
221
+ req.raw.on('aborted', () => ac.abort())
222
+ req.raw.on('end', () => { ended = true })
223
+ req.raw.on('close', () => {
224
+ if (!ended) {
225
+ ac.abort()
226
+ }
227
+ })
200
228
 
201
- /* c8 ignore next 3 */
202
- if (onInject) {
203
- return
229
+ const headers = new Headers()
230
+ for (const [key, value] of Object.entries(req.headers)) {
231
+ if (value) {
232
+ headers.set(key, Array.isArray(value) ? value.join(',') : value)
233
+ }
204
234
  }
205
235
 
206
- // Since inject might be called from the main thread directly via ITC, let's clean it up
207
- const { statusCode, headers, body, payload, rawPayload } = res
208
- return { statusCode, headers, body, payload, rawPayload }
236
+ let body
237
+
238
+ if (!['GET', 'HEAD'].includes(req.method)) {
239
+ body = Readable.toWeb(req.raw)
240
+ }
241
+
242
+ return handle(
243
+ new Request(`${req.protocol}://${req.hostname}${req.raw.url}`, {
244
+ method: req.method,
245
+ headers,
246
+ body,
247
+ duplex: body ? 'half' : undefined,
248
+ signal: ac.signal
249
+ })
250
+ )
209
251
  }
210
252
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/react-router",
3
- "version": "3.29.1",
3
+ "version": "3.31.0",
4
4
  "description": "Platformatic React Router Capability",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -15,23 +15,21 @@
15
15
  },
16
16
  "homepage": "https://github.com/platformatic/platformatic#readme",
17
17
  "dependencies": {
18
- "express": "^4.19.2",
19
- "light-my-request": "^6.0.0",
20
- "pino-http": "^10.2.0",
18
+ "@fastify/static": "^8.0.0",
19
+ "fastify": "^5.7.0",
21
20
  "semver": "^7.6.3",
22
- "@platformatic/foundation": "3.29.1",
23
- "@platformatic/basic": "3.29.1",
24
- "@platformatic/vite": "3.29.1"
21
+ "@platformatic/foundation": "3.31.0",
22
+ "@platformatic/basic": "3.31.0",
23
+ "@platformatic/vite": "3.31.0"
25
24
  },
26
25
  "devDependencies": {
27
26
  "@react-router/dev": "^7.10.0",
28
- "@react-router/express": "^7.10.1",
29
27
  "@react-router/node": "^7.10.0",
30
28
  "@types/react": "^19.1.11",
31
29
  "@types/react-dom": "^19.1.7",
32
30
  "cleaner-spec-reporter": "^0.5.0",
33
31
  "eslint": "9",
34
- "fastify": "^5.0.0",
32
+ "fastify": "^5.7.0",
35
33
  "isbot": "^5.1.17",
36
34
  "json-schema-to-typescript": "^15.0.1",
37
35
  "neostandard": "^0.12.0",
@@ -42,8 +40,8 @@
42
40
  "typescript": "^5.5.4",
43
41
  "vite": "^7.1.7",
44
42
  "ws": "^8.18.0",
45
- "@platformatic/gateway": "3.29.1",
46
- "@platformatic/service": "3.29.1"
43
+ "@platformatic/gateway": "3.31.0",
44
+ "@platformatic/service": "3.31.0"
47
45
  },
48
46
  "engines": {
49
47
  "node": ">=22.19.0"
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/react-router/3.29.1.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/react-router/3.31.0.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic React Router Config",
5
5
  "type": "object",
@@ -12,7 +12,6 @@
12
12
  "properties": {
13
13
  "level": {
14
14
  "type": "string",
15
- "default": "info",
16
15
  "oneOf": [
17
16
  {
18
17
  "enum": [
@@ -155,9 +154,6 @@
155
154
  "additionalProperties": true
156
155
  }
157
156
  },
158
- "required": [
159
- "level"
160
- ],
161
157
  "default": {},
162
158
  "additionalProperties": true
163
159
  },
@@ -698,6 +694,28 @@
698
694
  }
699
695
  }
700
696
  }
697
+ },
698
+ "compileCache": {
699
+ "anyOf": [
700
+ {
701
+ "type": "boolean"
702
+ },
703
+ {
704
+ "type": "object",
705
+ "properties": {
706
+ "enabled": {
707
+ "type": "boolean",
708
+ "default": true,
709
+ "description": "Enable Node.js module compile cache for faster startup"
710
+ },
711
+ "directory": {
712
+ "type": "string",
713
+ "description": "Directory to store compile cache. Defaults to .plt/compile-cache in app root"
714
+ }
715
+ },
716
+ "additionalProperties": false
717
+ }
718
+ ]
701
719
  }
702
720
  }
703
721
  }
@@ -777,7 +795,6 @@
777
795
  "properties": {
778
796
  "level": {
779
797
  "type": "string",
780
- "default": "info",
781
798
  "oneOf": [
782
799
  {
783
800
  "enum": [
@@ -920,9 +937,6 @@
920
937
  "additionalProperties": true
921
938
  }
922
939
  },
923
- "required": [
924
- "level"
925
- ],
926
940
  "default": {},
927
941
  "additionalProperties": true
928
942
  },
@@ -1964,6 +1978,28 @@
1964
1978
  ],
1965
1979
  "additionalProperties": false
1966
1980
  },
1981
+ "compileCache": {
1982
+ "anyOf": [
1983
+ {
1984
+ "type": "boolean"
1985
+ },
1986
+ {
1987
+ "type": "object",
1988
+ "properties": {
1989
+ "enabled": {
1990
+ "type": "boolean",
1991
+ "default": true,
1992
+ "description": "Enable Node.js module compile cache for faster startup"
1993
+ },
1994
+ "directory": {
1995
+ "type": "string",
1996
+ "description": "Directory to store compile cache. Defaults to .plt/compile-cache in app root"
1997
+ }
1998
+ },
1999
+ "additionalProperties": false
2000
+ }
2001
+ ]
2002
+ },
1967
2003
  "application": {
1968
2004
  "type": "object",
1969
2005
  "properties": {
@@ -2228,6 +2264,28 @@
2228
2264
  }
2229
2265
  }
2230
2266
  }
2267
+ },
2268
+ "compileCache": {
2269
+ "anyOf": [
2270
+ {
2271
+ "type": "boolean"
2272
+ },
2273
+ {
2274
+ "type": "object",
2275
+ "properties": {
2276
+ "enabled": {
2277
+ "type": "boolean",
2278
+ "default": true,
2279
+ "description": "Enable Node.js module compile cache for faster startup"
2280
+ },
2281
+ "directory": {
2282
+ "type": "string",
2283
+ "description": "Directory to store compile cache. Defaults to .plt/compile-cache in app root"
2284
+ }
2285
+ },
2286
+ "additionalProperties": false
2287
+ }
2288
+ ]
2231
2289
  }
2232
2290
  },
2233
2291
  "additionalProperties": false