@platformatic/remix 3.4.1 → 3.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,226 @@
1
+ import {
2
+ cleanBasePath,
3
+ ensureTrailingSlash,
4
+ errors,
5
+ getServerUrl,
6
+ importFile,
7
+ resolvePackage
8
+ } from '@platformatic/basic'
9
+ import { features } from '@platformatic/foundation'
10
+ import { ViteCapability } from '@platformatic/vite'
11
+ import { createRequestHandler } from '@remix-run/express'
12
+ import express from 'express'
13
+ import inject from 'light-my-request'
14
+ import { existsSync } from 'node:fs'
15
+ import { readFile } 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 = '^2.0.0'
22
+
23
+ export class RemixCapability extends ViteCapability {
24
+ #app
25
+ #server
26
+ #remix
27
+ #basePath
28
+
29
+ constructor (root, config, context) {
30
+ super(root, config, context)
31
+ this.type = 'remix'
32
+ this.version = packageJson.version
33
+ }
34
+
35
+ async init () {
36
+ await super.init()
37
+
38
+ if (!this.isProduction) {
39
+ this.#remix = resolve(dirname(resolvePackage(this.root, '@remix-run/dev')), '..')
40
+ const remixPackage = JSON.parse(await readFile(resolve(this.#remix, 'package.json'), 'utf-8'))
41
+
42
+ if (!satisfies(remixPackage.version, supportedVersions)) {
43
+ throw new errors.UnsupportedVersion('@remix-run/dev', remixPackage.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
+ // Make this idempotent
59
+ if (this.url) {
60
+ return this.url
61
+ }
62
+
63
+ const config = this.config
64
+ const command = config.application.commands[this.isProduction ? 'production' : 'development']
65
+
66
+ if (command) {
67
+ return this.startWithCommand(command)
68
+ }
69
+
70
+ return this.isProduction ? this.#startProduction(listen) : this.#startDevelopment(listen)
71
+ }
72
+
73
+ async stop () {
74
+ await super.stop()
75
+
76
+ if (this.childManager) {
77
+ return this.stopCommand()
78
+ }
79
+
80
+ if (this.isProduction) {
81
+ return this.#stopProduction()
82
+ } else if (this.#app) {
83
+ return this.#app.close()
84
+ }
85
+ }
86
+
87
+ async build () {
88
+ const config = this.config
89
+ const command = config.application.commands.build
90
+ const basePath = config.application?.basePath
91
+ ? ensureTrailingSlash(cleanBasePath(config.application?.basePath))
92
+ : undefined
93
+
94
+ if (command) {
95
+ return this.buildWithCommand(command, basePath)
96
+ }
97
+
98
+ await this.init()
99
+ const { viteBuild } = await importFile(resolve(this.#remix, 'dist/cli/commands.js'))
100
+
101
+ try {
102
+ globalThis.platformatic.isBuilding = true
103
+
104
+ await viteBuild(this.root, {
105
+ emptyOutDir: true,
106
+ logLevel: this.logger.level,
107
+ mode: 'production',
108
+ profile: false
109
+ })
110
+ } finally {
111
+ globalThis.platformatic.isBuilding = false
112
+ }
113
+ }
114
+
115
+ async inject (injectParams, onInject) {
116
+ if (!this.isProduction) {
117
+ return super.inject(injectParams, onInject)
118
+ }
119
+
120
+ const res = await inject(this.#app, injectParams, onInject)
121
+
122
+ /* c8 ignore next 3 */
123
+ if (onInject) {
124
+ return
125
+ } // Since inject might be called from the main thread directly via ITC, let's clean it up
126
+
127
+ const { statusCode, headers, body, payload, rawPayload } = res
128
+ return { statusCode, headers, body, payload, rawPayload }
129
+ }
130
+
131
+ getMeta () {
132
+ if (!this.isProduction) {
133
+ return super.getMeta()
134
+ }
135
+
136
+ return {
137
+ gateway: {
138
+ tcp: typeof this.url !== 'undefined',
139
+ url: this.url,
140
+ prefix: this.basePath ?? this.#basePath,
141
+ wantsAbsoluteUrls: true,
142
+ needsRootTrailingSlash: true
143
+ }
144
+ }
145
+ }
146
+
147
+ async #startDevelopment (listen) {
148
+ const preloadViteEsmPath = resolve(this.#remix, './dist/vite/import-vite-esm-sync.js')
149
+
150
+ // Older versions
151
+ if (existsSync(preloadViteEsmPath)) {
152
+ const { preloadViteEsm } = await importFile(resolve(this.#remix, './dist/vite/import-vite-esm-sync.js'))
153
+ await preloadViteEsm()
154
+ } else {
155
+ const { preloadVite } = await importFile(resolve(this.#remix, './dist/vite/vite.js'))
156
+ await preloadVite()
157
+ }
158
+
159
+ await super.start({ listen })
160
+
161
+ /* c8 ignore next 3 */
162
+ if (!this._getVite().config.plugins.some(p => p.name === 'remix')) {
163
+ this.logger.warn('Could not find Remix plugin in your Vite configuration. Continuing as plain Vite application.')
164
+ }
165
+
166
+ await this._collectMetrics()
167
+ }
168
+
169
+ async #startProduction (listen) {
170
+ // Listen if entrypoint
171
+ if (this.#app && listen) {
172
+ const serverOptions = this.serverConfig
173
+ const listenOptions = { host: serverOptions?.hostname || '127.0.0.1', port: serverOptions?.port || 0 }
174
+
175
+ if (this.isProduction && features.node.reusePort) {
176
+ listenOptions.reusePort = true
177
+ }
178
+
179
+ this.#server = await new Promise((resolve, reject) => {
180
+ return this.#app
181
+ .listen(listenOptions, function () {
182
+ resolve(this)
183
+ })
184
+ .on('error', reject)
185
+ })
186
+
187
+ this.url = getServerUrl(this.#server)
188
+
189
+ return this.url
190
+ }
191
+
192
+ const outputDirectory = this.config.remix.outputDirectory
193
+ this.verifyOutputDirectory(resolve(this.root, outputDirectory))
194
+
195
+ const build = await importFile(resolve(this.root, `${outputDirectory}/server/index.js`))
196
+ this.#basePath = ensureTrailingSlash(cleanBasePath(build.basename))
197
+
198
+ // Setup express app
199
+ this.#app = express()
200
+ this.#app.disable('x-powered-by')
201
+ this.#app.use(pinoHttp({ logger: this.logger }))
202
+ this.#app.use(this.#basePath, express.static(resolve(this.root, `${outputDirectory}/client`)))
203
+ this.#app.all(`${ensureTrailingSlash(cleanBasePath(this.#basePath))}*`, createRequestHandler({ build }))
204
+
205
+ await this._collectMetrics()
206
+ return this.url
207
+ }
208
+
209
+ async #stopProduction () {
210
+ /* c8 ignore next 3 */
211
+ if (!this.#server?.listening) {
212
+ return
213
+ }
214
+
215
+ return new Promise((resolve, reject) => {
216
+ this.#server.close(error => {
217
+ /* c8 ignore next 3 */
218
+ if (error) {
219
+ return reject(error)
220
+ }
221
+
222
+ resolve()
223
+ })
224
+ })
225
+ }
226
+ }
package/lib/schema.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { schemaComponents as basicSchemaComponents } from '@platformatic/basic'
2
- import { schemaComponents as utilsSchemaComponents } from '@platformatic/utils'
2
+ import { schemaComponents as utilsSchemaComponents } from '@platformatic/foundation'
3
3
  import { schemaComponents as viteSchemaComponents } from '@platformatic/vite'
4
4
  import { readFileSync } from 'node:fs'
5
+ import { resolve } from 'node:path'
5
6
 
6
- export const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8'))
7
+ export const packageJson = JSON.parse(readFileSync(resolve(import.meta.dirname, '../package.json'), 'utf8'))
8
+ export const version = packageJson.version
7
9
 
8
10
  export const remix = {
9
11
  type: 'object',
@@ -22,7 +24,7 @@ export const schemaComponents = { remix }
22
24
  export const schema = {
23
25
  $id: `https://schemas.platformatic.dev/@platformatic/remix/${packageJson.version}.json`,
24
26
  $schema: 'http://json-schema.org/draft-07/schema#',
25
- title: 'Platformatic Remix Stackable',
27
+ title: 'Platformatic Remix Config',
26
28
  type: 'object',
27
29
  properties: {
28
30
  $schema: {
@@ -31,7 +33,8 @@ export const schema = {
31
33
  logger: utilsSchemaComponents.logger,
32
34
  server: utilsSchemaComponents.server,
33
35
  watch: basicSchemaComponents.watch,
34
- application: basicSchemaComponents.application,
36
+ application: basicSchemaComponents.buildableApplication,
37
+ runtime: utilsSchemaComponents.wrappedRuntime,
35
38
  vite: viteSchemaComponents.vite,
36
39
  remix
37
40
  },
package/package.json CHANGED
@@ -1,54 +1,57 @@
1
1
  {
2
2
  "name": "@platformatic/remix",
3
- "version": "3.4.1",
4
- "description": "Platformatic Remix Stackable",
3
+ "version": "3.5.1",
4
+ "description": "Platformatic Remix Capability",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/platformatic/platformatic.git"
10
10
  },
11
- "author": "Paolo Insogna <paolo@cowtech.it>",
11
+ "author": "Platformatic Inc. <oss@platformatic.dev> (https://platformatic.dev)",
12
12
  "license": "Apache-2.0",
13
13
  "bugs": {
14
14
  "url": "https://github.com/platformatic/platformatic/issues"
15
15
  },
16
16
  "homepage": "https://github.com/platformatic/platformatic#readme",
17
17
  "dependencies": {
18
- "@remix-run/express": "^2.11.2",
19
- "light-my-request": "^6.0.0",
18
+ "@remix-run/express": "^2.16.8",
20
19
  "express": "^4.19.2",
20
+ "light-my-request": "^6.0.0",
21
21
  "pino-http": "^10.2.0",
22
22
  "semver": "^7.6.3",
23
- "@platformatic/basic": "3.4.1",
24
- "@platformatic/vite": "3.4.1",
25
- "@platformatic/config": "3.4.1",
26
- "@platformatic/utils": "3.4.1"
23
+ "@platformatic/basic": "3.5.1",
24
+ "@platformatic/foundation": "3.5.1",
25
+ "@platformatic/vite": "3.5.1"
27
26
  },
28
27
  "devDependencies": {
29
- "@remix-run/dev": "^2.11.2",
30
- "@remix-run/node": "^2.11.2",
31
- "@remix-run/react": "^2.11.2",
32
- "borp": "^0.17.0",
28
+ "@remix-run/dev": "^2.16.8",
29
+ "@remix-run/node": "^2.16.8",
30
+ "@remix-run/react": "^2.16.8",
31
+ "@types/react": "^19.1.11",
32
+ "@types/react-dom": "^19.1.7",
33
+ "cleaner-spec-reporter": "^0.5.0",
33
34
  "eslint": "9",
34
35
  "fastify": "^5.0.0",
35
36
  "isbot": "^5.1.17",
36
37
  "json-schema-to-typescript": "^15.0.1",
37
- "neostandard": "^0.11.1",
38
- "react": "^18.3.1",
39
- "react-dom": "^18.3.1",
38
+ "neostandard": "^0.12.0",
39
+ "react": "^19.1.0",
40
+ "react-dom": "^19.1.0",
40
41
  "typescript": "^5.5.4",
41
42
  "vite": "^5.4.2",
42
43
  "ws": "^8.18.0",
43
- "@platformatic/composer": "3.4.1",
44
- "@platformatic/service": "3.4.1"
44
+ "@platformatic/gateway": "3.5.1",
45
+ "@platformatic/service": "3.5.1"
46
+ },
47
+ "engines": {
48
+ "node": ">=22.19.0"
45
49
  },
46
50
  "scripts": {
47
- "test": "npm run lint && borp --concurrency=1 --no-timeout",
48
- "coverage": "npm run lint && borp -C -X test -X test/fixtures --concurrency=1 --no-timeout",
51
+ "test": "node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/*.test.js test/**/*.test.js",
49
52
  "gen-schema": "node lib/schema.js > schema.json",
50
53
  "gen-types": "json2ts > config.d.ts < schema.json",
51
- "build": "pnpm run gen-schema && pnpm run gen-types",
54
+ "build": "npm run gen-schema && npm run gen-types",
52
55
  "lint": "eslint"
53
56
  }
54
57
  }