@platformatic/react-router 0.0.1 → 3.28.2

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.
@@ -0,0 +1,203 @@
1
+ import {
2
+ cleanBasePath,
3
+ createServerListener,
4
+ ensureTrailingSlash,
5
+ errors,
6
+ getServerUrl,
7
+ importFile,
8
+ resolvePackageViaCJS
9
+ } from '@platformatic/basic'
10
+ import { ViteCapability } from '@platformatic/vite'
11
+ import { createRequestHandler } from '@react-router/express'
12
+ import express from 'express'
13
+ import inject from 'light-my-request'
14
+ import { existsSync } from 'node:fs'
15
+ import { readFile, writeFile } from 'node:fs/promises'
16
+ import { dirname, resolve } from 'node:path'
17
+ import { pinoHttp } from 'pino-http'
18
+ import { satisfies } from 'semver'
19
+ import { packageJson } from './schema.js'
20
+
21
+ const supportedVersions = '^7.0.0'
22
+
23
+ export class ReactRouterCapability extends ViteCapability {
24
+ #app
25
+ #server
26
+ #reactRouter
27
+ #basePath
28
+
29
+ constructor (root, config, context) {
30
+ super(root, config, context)
31
+ this.type = 'react-router'
32
+ this.version = packageJson.version
33
+ }
34
+
35
+ async init () {
36
+ await super.init()
37
+
38
+ if (!this.isProduction) {
39
+ this.#reactRouter = resolve(dirname(await resolvePackageViaCJS(this.root, 'react-router')), '../..')
40
+ const reactRouterPackage = JSON.parse(await readFile(resolve(this.#reactRouter, 'package.json'), 'utf-8'))
41
+
42
+ if (!satisfies(reactRouterPackage.version, supportedVersions)) {
43
+ throw new errors.UnsupportedVersion('react-router', reactRouterPackage.version, supportedVersions)
44
+ }
45
+ }
46
+
47
+ const config = this.config
48
+ this.#basePath = config.application?.basePath
49
+ ? ensureTrailingSlash(cleanBasePath(config.application?.basePath))
50
+ : undefined
51
+
52
+ this.registerGlobals({ basePath: this.#basePath })
53
+
54
+ this.subprocessTerminationSignal = 'SIGKILL'
55
+ }
56
+
57
+ async start ({ listen }) {
58
+ const config = this.config
59
+ const reactRouterConfig = await this.#getReactRouterConfig()
60
+
61
+ if (this.isProduction) {
62
+ const command = this.config.application.commands.production
63
+
64
+ this.outputDirectory = resolve(this.root, config.reactRouter.outputDirectory, 'client')
65
+ this.buildInfoPath = resolve(this.root, config.reactRouter.outputDirectory, '.platformatic-build.json')
66
+
67
+ if (command) {
68
+ return this.startWithCommand(command)
69
+ }
70
+
71
+ if (reactRouterConfig.ssr) {
72
+ return this.#startSSRProduction(listen)
73
+ }
74
+ }
75
+
76
+ return super.start({ listen })
77
+ }
78
+
79
+ async stop () {
80
+ const reactRouterConfig = await this.#getReactRouterConfig()
81
+
82
+ if (reactRouterConfig.ssr) {
83
+ await this._stop()
84
+
85
+ if (this.#server?.listening) {
86
+ await this._closeServer(this.#server)
87
+ }
88
+
89
+ return
90
+ }
91
+
92
+ return super.stop()
93
+ }
94
+
95
+ async build () {
96
+ const config = this.config
97
+ const command = config.application.commands.build
98
+ const basePath = config.application?.basePath
99
+ ? ensureTrailingSlash(cleanBasePath(config.application?.basePath))
100
+ : undefined
101
+
102
+ const reactRouterConfig = await this.#getReactRouterConfig()
103
+
104
+ if (command) {
105
+ return this.buildWithCommand(command, basePath)
106
+ }
107
+
108
+ await this.buildWithCommand(`react-router build -m production ${this.root}`, basePath)
109
+
110
+ await writeFile(
111
+ resolve(this.root, config.reactRouter.outputDirectory, '.platformatic-build.json'),
112
+ JSON.stringify({ basePath: reactRouterConfig.basename ?? '/' }),
113
+ 'utf-8'
114
+ )
115
+ }
116
+
117
+ async getMeta () {
118
+ const reactRouterConfig = await this.#getReactRouterConfig()
119
+ return super.getMeta(reactRouterConfig.basename)
120
+ }
121
+
122
+ async inject (injectParams, onInject) {
123
+ const reactRouterConfig = await this.#getReactRouterConfig()
124
+
125
+ if (this.isProduction && reactRouterConfig.ssr) {
126
+ return this.#inject(injectParams, onInject)
127
+ }
128
+
129
+ return super.inject(injectParams, onInject)
130
+ }
131
+
132
+ async #getReactRouterConfig () {
133
+ const ext = ['ts', 'js'].find(ext => existsSync(resolve(this.root, `react-router.config.${ext}`)))
134
+
135
+ if (!ext) {
136
+ return {}
137
+ }
138
+
139
+ const { default: reactRouterConfig } = await importFile(resolve(this.root, `react-router.config.${ext}`))
140
+ return reactRouterConfig ?? {}
141
+ }
142
+
143
+ async #startSSRProduction (listen) {
144
+ // Listen if entrypoint
145
+ if (this.#app && listen) {
146
+ const serverOptions = this.serverConfig
147
+ const listenOptions = { host: serverOptions?.hostname || '127.0.0.1', port: serverOptions?.port || 0 }
148
+
149
+ if (typeof serverOptions?.backlog === 'number') {
150
+ createServerListener(false, false, { backlog: serverOptions.backlog })
151
+ }
152
+
153
+ this.#server = await new Promise((resolve, reject) => {
154
+ return this.#app
155
+ .listen(listenOptions, function () {
156
+ resolve(this)
157
+ })
158
+ .on('error', reject)
159
+ })
160
+
161
+ this.url = getServerUrl(this.#server)
162
+
163
+ return this.url
164
+ }
165
+
166
+ const config = this.config
167
+
168
+ const clientRoot = resolve(this.root, config.reactRouter.outputDirectory, 'client')
169
+ const serverRoot = resolve(this.root, config.reactRouter.outputDirectory, 'server')
170
+ this.verifyOutputDirectory(clientRoot)
171
+ this.verifyOutputDirectory(serverRoot)
172
+ this.#basePath = await this._getBasePathFromBuildInfo()
173
+
174
+ const { entrypoint } = await importFile(resolve(serverRoot, 'index.js'))
175
+
176
+ // Setup express app
177
+ this.#app = express()
178
+ this._setApp(this.#app)
179
+ this.#app.disable('x-powered-by')
180
+ this.#app.use(pinoHttp({ logger: this.logger }))
181
+ this.#app.use(this.#basePath, express.static(clientRoot))
182
+ this.#app.all(
183
+ `${ensureTrailingSlash(cleanBasePath(this.#basePath))}*`,
184
+ createRequestHandler({ build: () => entrypoint })
185
+ )
186
+
187
+ await this._collectMetrics()
188
+ return this.url
189
+ }
190
+
191
+ async #inject (injectParams, onInject) {
192
+ const res = await inject(this.#app, injectParams, onInject)
193
+
194
+ /* c8 ignore next 3 */
195
+ if (onInject) {
196
+ return
197
+ }
198
+
199
+ // Since inject might be called from the main thread directly via ITC, let's clean it up
200
+ const { statusCode, headers, body, payload, rawPayload } = res
201
+ return { statusCode, headers, body, payload, rawPayload }
202
+ }
203
+ }
package/lib/schema.js ADDED
@@ -0,0 +1,47 @@
1
+ import { schemaComponents as basicSchemaComponents } from '@platformatic/basic'
2
+ import { schemaComponents as utilsSchemaComponents } from '@platformatic/foundation'
3
+ import { schemaComponents as viteSchemaComponents } from '@platformatic/vite'
4
+ import { readFileSync } from 'node:fs'
5
+ import { resolve } from 'node:path'
6
+
7
+ export const packageJson = JSON.parse(readFileSync(resolve(import.meta.dirname, '../package.json'), 'utf8'))
8
+ export const version = packageJson.version
9
+
10
+ export const reactRouter = {
11
+ type: 'object',
12
+ properties: {
13
+ outputDirectory: {
14
+ type: 'string',
15
+ default: 'build'
16
+ }
17
+ },
18
+ default: {},
19
+ additionalProperties: false
20
+ }
21
+
22
+ export const schemaComponents = { reactRouter }
23
+
24
+ export const schema = {
25
+ $id: `https://schemas.platformatic.dev/@platformatic/react-router/${packageJson.version}.json`,
26
+ $schema: 'http://json-schema.org/draft-07/schema#',
27
+ title: 'Platformatic React Router Config',
28
+ type: 'object',
29
+ properties: {
30
+ $schema: {
31
+ type: 'string'
32
+ },
33
+ logger: utilsSchemaComponents.logger,
34
+ server: utilsSchemaComponents.server,
35
+ watch: basicSchemaComponents.watch,
36
+ application: basicSchemaComponents.buildableApplication,
37
+ runtime: utilsSchemaComponents.wrappedRuntime,
38
+ vite: viteSchemaComponents.vite,
39
+ reactRouter
40
+ },
41
+ additionalProperties: false
42
+ }
43
+
44
+ /* c8 ignore next 3 */
45
+ if (process.argv[1] === import.meta.filename) {
46
+ console.log(JSON.stringify(schema, null, 2))
47
+ }
package/package.json CHANGED
@@ -1,12 +1,58 @@
1
1
  {
2
2
  "name": "@platformatic/react-router",
3
- "version": "0.0.1",
4
- "description": "",
5
- "license": "MIT",
6
- "author": "Matteo Collina <hello@matteocollina.com>",
7
- "type": "commonjs",
3
+ "version": "3.28.2",
4
+ "description": "Platformatic React Router Capability",
8
5
  "main": "index.js",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/platformatic/platformatic.git"
10
+ },
11
+ "author": "Platformatic Inc. <oss@platformatic.dev> (https://platformatic.dev)",
12
+ "license": "Apache-2.0",
13
+ "bugs": {
14
+ "url": "https://github.com/platformatic/platformatic/issues"
15
+ },
16
+ "homepage": "https://github.com/platformatic/platformatic#readme",
17
+ "dependencies": {
18
+ "express": "^4.19.2",
19
+ "light-my-request": "^6.0.0",
20
+ "pino-http": "^10.2.0",
21
+ "semver": "^7.6.3",
22
+ "@platformatic/basic": "3.28.2",
23
+ "@platformatic/vite": "3.28.2",
24
+ "@platformatic/foundation": "3.28.2"
25
+ },
26
+ "devDependencies": {
27
+ "@react-router/dev": "^7.10.0",
28
+ "@react-router/express": "^7.10.1",
29
+ "@react-router/node": "^7.10.0",
30
+ "@types/react": "^19.1.11",
31
+ "@types/react-dom": "^19.1.7",
32
+ "cleaner-spec-reporter": "^0.5.0",
33
+ "eslint": "9",
34
+ "fastify": "^5.0.0",
35
+ "isbot": "^5.1.17",
36
+ "json-schema-to-typescript": "^15.0.1",
37
+ "neostandard": "^0.12.0",
38
+ "react": "^19.1.0",
39
+ "react-dom": "^19.1.0",
40
+ "react-router": "^7.10.0",
41
+ "react-router-dom": "^7.10.0",
42
+ "typescript": "^5.5.4",
43
+ "vite": "^7.1.7",
44
+ "ws": "^8.18.0",
45
+ "@platformatic/gateway": "3.28.2",
46
+ "@platformatic/service": "3.28.2"
47
+ },
48
+ "engines": {
49
+ "node": ">=22.19.0"
50
+ },
9
51
  "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1"
52
+ "test": "node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/*.test.js test/**/*.test.js",
53
+ "gen-schema": "node lib/schema.js > schema.json",
54
+ "gen-types": "json2ts > config.d.ts < schema.json",
55
+ "build": "npm run gen-schema && npm run gen-types",
56
+ "lint": "eslint"
11
57
  }
12
- }
58
+ }