@platformatic/vite 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,422 @@
1
+ import fastifyStatic from '@fastify/static'
2
+ import {
3
+ BaseCapability,
4
+ cleanBasePath,
5
+ createServerListener,
6
+ ensureTrailingSlash,
7
+ errors,
8
+ getServerUrl,
9
+ importFile,
10
+ resolvePackage
11
+ } from '@platformatic/basic'
12
+ import { ensureLoggableError, features } from '@platformatic/foundation'
13
+ import { NodeCapability } from '@platformatic/node'
14
+ import fastify from 'fastify'
15
+ import { existsSync } from 'node:fs'
16
+ import { readFile, writeFile } from 'node:fs/promises'
17
+ import { dirname, resolve } from 'node:path'
18
+ import { parse, satisfies } from 'semver'
19
+ import { version } from './schema.js'
20
+
21
+ const supportedVersions = ['^5.0.0', '^6.0.0', '^7.0.0']
22
+
23
+ export class ViteCapability extends BaseCapability {
24
+ #vite
25
+ #viteVersion
26
+ #app
27
+ #server
28
+ #basePath
29
+
30
+ constructor (root, config, context) {
31
+ super('vite', version, root, config, context)
32
+ }
33
+
34
+ async init () {
35
+ await super.init()
36
+
37
+ if (this.isProduction) {
38
+ return
39
+ }
40
+
41
+ this.#vite = dirname(resolvePackage(this.root, 'vite'))
42
+
43
+ // In Vite 6, module resolving changed, adjust it
44
+ if (!existsSync(resolve(this.#vite, 'dist/node/index.js'))) {
45
+ this.#vite = resolve(this.#vite, '../..')
46
+ }
47
+
48
+ const vitePackage = JSON.parse(await readFile(resolve(this.#vite, 'package.json'), 'utf-8'))
49
+ this.#viteVersion = parse(vitePackage.version)
50
+
51
+ if (!supportedVersions.some(v => satisfies(vitePackage.version, v))) {
52
+ throw new errors.UnsupportedVersion('vite', vitePackage.version, supportedVersions)
53
+ }
54
+ }
55
+
56
+ async start ({ listen }) {
57
+ // Make this idempotent
58
+ if (this.url) {
59
+ return this.url
60
+ }
61
+
62
+ if (this.isProduction) {
63
+ await this.#startProduction(listen)
64
+ } else {
65
+ await this.#startDevelopment(listen)
66
+ }
67
+
68
+ await this._collectMetrics()
69
+ }
70
+
71
+ async stop () {
72
+ await super.stop()
73
+
74
+ if (this.childManager) {
75
+ return this.stopCommand()
76
+ } else if (this.#app) {
77
+ // This is needed if the capability was subclassed
78
+ return this.#app.close()
79
+ }
80
+ }
81
+
82
+ async build () {
83
+ const config = this.config
84
+ const command = config.application.commands.build
85
+ const configFile = config.vite.configFile ? resolve(this.root, config.vite.configFile) : undefined
86
+ let basePath = config.application?.basePath
87
+ ? ensureTrailingSlash(cleanBasePath(config.application?.basePath))
88
+ : undefined
89
+ let outDir
90
+
91
+ if (command) {
92
+ return this.buildWithCommand(command, basePath)
93
+ }
94
+
95
+ await this.init()
96
+ const { build } = await importFile(resolve(this.#vite, 'dist/node/index.js'))
97
+
98
+ try {
99
+ globalThis.platformatic.isBuilding = true
100
+
101
+ await build({
102
+ root: this.root,
103
+ base: basePath,
104
+ mode: 'production',
105
+ configFile,
106
+ logLevel: this.logger.level,
107
+ build: {
108
+ outDir: config.application.outputDirectory
109
+ },
110
+ plugins: [
111
+ {
112
+ name: 'platformatic-build',
113
+ configResolved: config => {
114
+ basePath = ensureTrailingSlash(cleanBasePath(config.base))
115
+ outDir = resolve(this.root, config.build.outDir)
116
+ }
117
+ }
118
+ ]
119
+ })
120
+ } finally {
121
+ globalThis.platformatic.isBuilding = false
122
+ }
123
+
124
+ await writeFile(resolve(outDir, '.platformatic-build.json'), JSON.stringify({ basePath }), 'utf-8')
125
+ }
126
+
127
+ /* c8 ignore next 5 */
128
+ async getWatchConfig () {
129
+ return {
130
+ enabled: false
131
+ }
132
+ }
133
+
134
+ // This is only used in production mode
135
+ async inject (injectParams, onInject) {
136
+ const res = await this.#app.inject(injectParams, onInject)
137
+
138
+ /* c8 ignore next 3 */
139
+ if (onInject) {
140
+ return
141
+ }
142
+
143
+ // Since inject might be called from the main thread directly via ITC, let's clean it up
144
+ const { statusCode, headers, body, payload, rawPayload } = res
145
+ return { statusCode, headers, body, payload, rawPayload }
146
+ }
147
+
148
+ getMeta () {
149
+ const config = this.subprocessConfig ?? this.#app?.config
150
+
151
+ const gateway = {
152
+ tcp: typeof this.url !== 'undefined',
153
+ url: this.url,
154
+ prefix: this.basePath ?? config?.base ?? this.#basePath,
155
+ wantsAbsoluteUrls: true,
156
+ needsRootTrailingSlash: true
157
+ }
158
+
159
+ return { gateway }
160
+ }
161
+
162
+ _getVite () {
163
+ return this.#app
164
+ }
165
+
166
+ async #startDevelopment () {
167
+ const config = this.config
168
+ const command = this.config.application.commands.development
169
+
170
+ this.#basePath = config.application?.basePath
171
+ ? ensureTrailingSlash(cleanBasePath(config.application?.basePath))
172
+ : undefined
173
+
174
+ this.registerGlobals({ basePath: this.#basePath })
175
+
176
+ if (command) {
177
+ return this.startWithCommand(command)
178
+ }
179
+
180
+ // Prepare options
181
+ const { hostname, port, https, cors } = this.serverConfig ?? {}
182
+ const configFile = config.vite.configFile ? resolve(this.root, config.vite.configFile) : undefined
183
+
184
+ const serverOptions = {
185
+ host: hostname || '127.0.0.1',
186
+ port: port || 0,
187
+ strictPort: false,
188
+ https,
189
+ cors,
190
+ hmr: true,
191
+ allowedHosts: ['.plt.local'],
192
+ fs: {
193
+ strict: config.vite.devServer.strict
194
+ }
195
+ }
196
+
197
+ // Require Vite
198
+ const serverPromise = createServerListener(
199
+ (this.isEntrypoint ? serverOptions?.port : undefined) ?? true,
200
+ (this.isEntrypoint ? serverOptions?.hostname : undefined) ?? true
201
+ )
202
+ const { createServer } = await importFile(resolve(this.#vite, 'dist/node/index.js'))
203
+
204
+ // Create the server and listen
205
+ this.#app = await createServer({
206
+ root: this.root,
207
+ base: this.#basePath,
208
+ mode: 'development',
209
+ configFile,
210
+ logLevel: this.logger.level,
211
+ clearScreen: false,
212
+ optimizeDeps: { force: false },
213
+ server: serverOptions
214
+ })
215
+
216
+ await this.#app.listen()
217
+ this.#server = await serverPromise
218
+ this.url = getServerUrl(this.#server)
219
+ }
220
+
221
+ async #startProduction (listen) {
222
+ const config = this.config
223
+ const command = this.config.application.commands.production
224
+
225
+ this.#basePath = config.application?.basePath
226
+ ? ensureTrailingSlash(cleanBasePath(config.application?.basePath))
227
+ : undefined
228
+
229
+ this.registerGlobals({ basePath: this.#basePath })
230
+
231
+ if (command) {
232
+ return this.startWithCommand(command)
233
+ }
234
+
235
+ if (this.#app && listen) {
236
+ const serverOptions = this.serverConfig
237
+ const listenOptions = { host: serverOptions?.hostname || '127.0.0.1', port: serverOptions?.port || 0 }
238
+
239
+ if (this.isProduction && features.node.reusePort) {
240
+ listenOptions.reusePort = true
241
+ }
242
+
243
+ await this.#app.listen(listenOptions)
244
+ this.url = getServerUrl(this.#app.server)
245
+ return this.url
246
+ }
247
+
248
+ this.#app = fastify({ loggerInstance: this.logger })
249
+
250
+ const outputDirectory = resolve(this.root, config.application.outputDirectory)
251
+ this.verifyOutputDirectory(outputDirectory)
252
+ const buildInfoPath = resolve(outputDirectory, '.platformatic-build.json')
253
+
254
+ if (!this.#basePath && existsSync(buildInfoPath)) {
255
+ try {
256
+ const buildInfo = JSON.parse(await readFile(buildInfoPath, 'utf-8'))
257
+ this.#basePath = buildInfo.basePath
258
+ } catch (e) {
259
+ globalThis.platformatic.logger.error({ err: ensureLoggableError(e) }, 'Reading build info failed.')
260
+ }
261
+ }
262
+
263
+ await this.#app.register(fastifyStatic, {
264
+ root: outputDirectory,
265
+ prefix: this.#basePath,
266
+ prefixAvoidTrailingSlash: true,
267
+ schemaHide: true
268
+ })
269
+
270
+ await this.#app.ready()
271
+ }
272
+ }
273
+
274
+ export class ViteSSRCapability extends NodeCapability {
275
+ #basePath
276
+
277
+ constructor (root, config, context) {
278
+ super(root, config, context)
279
+ this.type = 'vite'
280
+ }
281
+
282
+ _getWantsAbsoluteUrls () {
283
+ return true
284
+ }
285
+
286
+ async init () {
287
+ await super.init()
288
+
289
+ const config = this.config
290
+
291
+ this.#basePath = config.application?.basePath
292
+ ? ensureTrailingSlash(cleanBasePath(config.application?.basePath))
293
+ : undefined
294
+
295
+ this.registerGlobals({ basePath: this.#basePath })
296
+ }
297
+
298
+ async start ({ listen }) {
299
+ // Make this idempotent
300
+ /* c8 ignore next 3 */
301
+ if (this.url) {
302
+ return this.url
303
+ }
304
+
305
+ const config = this.config
306
+ const command = config.application.commands[this.isProduction ? 'production' : 'development']
307
+
308
+ if (command) {
309
+ return this.startWithCommand(command)
310
+ }
311
+
312
+ if (this.isProduction) {
313
+ const clientDirectory = config.vite.ssr.clientDirectory
314
+ const clientOutDir = resolve(this.root, clientDirectory, config.application.outputDirectory, clientDirectory)
315
+
316
+ this.verifyOutputDirectory(clientOutDir)
317
+
318
+ const buildInfoPath = resolve(clientOutDir, '.platformatic-build.json')
319
+ if (!this.#basePath && existsSync(buildInfoPath)) {
320
+ try {
321
+ const buildInfo = JSON.parse(await readFile(buildInfoPath, 'utf-8'))
322
+ this.#basePath = buildInfo.basePath
323
+ } catch (e) {
324
+ globalThis.platformatic.logger.error({ err: ensureLoggableError(e) }, 'Reading build info failed.')
325
+ }
326
+ }
327
+ }
328
+
329
+ await super.start({ listen })
330
+ await super._listen()
331
+ }
332
+
333
+ async build () {
334
+ const config = this.config
335
+ const command = config.application.commands.build
336
+ const configFile = config.vite.configFile ? resolve(this.root, config.vite.configFile) : undefined
337
+ let basePath = config.application?.basePath
338
+ ? ensureTrailingSlash(cleanBasePath(config.application?.basePath))
339
+ : undefined
340
+
341
+ if (command) {
342
+ return this.buildWithCommand(command, basePath)
343
+ }
344
+
345
+ const clientDirectory = config.vite.ssr.clientDirectory
346
+ const serverDirectory = config.vite.ssr.serverDirectory
347
+ let clientOutDir = resolve(this.root, clientDirectory, config.application.outputDirectory, clientDirectory)
348
+
349
+ await this.init()
350
+
351
+ let vite = dirname(resolvePackage(this.root, 'vite'))
352
+ // In Vite 6, module resolving changed, adjust it
353
+ if (!existsSync(resolve(vite, 'dist/node/index.js'))) {
354
+ vite = resolve(vite, '../..')
355
+ }
356
+
357
+ const { build } = await importFile(resolve(vite, 'dist/node/index.js'))
358
+
359
+ // Build the client
360
+ try {
361
+ globalThis.platformatic.isBuilding = true
362
+
363
+ await build({
364
+ root: resolve(this.root, clientDirectory),
365
+ base: basePath,
366
+ mode: 'production',
367
+ configFile,
368
+ logLevel: this.logger.level,
369
+ build: {
370
+ outDir: clientOutDir,
371
+ ssrManifest: true
372
+ },
373
+ plugins: [
374
+ {
375
+ name: 'platformatic-build',
376
+ configResolved: config => {
377
+ basePath = ensureTrailingSlash(cleanBasePath(config.base))
378
+ clientOutDir = resolve(this.root, clientDirectory, config.build.outDir)
379
+ }
380
+ }
381
+ ]
382
+ })
383
+ } finally {
384
+ globalThis.platformatic.isBuilding = false
385
+ }
386
+
387
+ await writeFile(resolve(clientOutDir, '.platformatic-build.json'), JSON.stringify({ basePath }), 'utf-8')
388
+
389
+ // Build the server
390
+ await build({
391
+ root: this.root,
392
+ base: basePath,
393
+ mode: 'production',
394
+ configFile,
395
+ logLevel: this.logger.level,
396
+ build: {
397
+ outDir: resolve(this.root, clientDirectory, config.application.outputDirectory, serverDirectory),
398
+ ssr: resolve(this.root, clientDirectory, 'index.js')
399
+ }
400
+ })
401
+ }
402
+
403
+ getMeta () {
404
+ const vite = this._getApplication()?.vite
405
+ const applicationBasePath = vite?.config?.base
406
+
407
+ const gateway = {
408
+ tcp: typeof this.url !== 'undefined',
409
+ url: this.url,
410
+ prefix: this.basePath ?? applicationBasePath ?? this.#basePath,
411
+ wantsAbsoluteUrls: true,
412
+ needsRootTrailingSlash: true
413
+ }
414
+
415
+ return { gateway }
416
+ }
417
+
418
+ _findEntrypoint () {
419
+ const config = this.config.vite ?? {}
420
+ return resolve(this.root, config.ssr.entrypoint)
421
+ }
422
+ }
package/lib/schema.js CHANGED
@@ -1,8 +1,10 @@
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 { readFileSync } from 'node:fs'
4
+ import { resolve } from 'node:path'
4
5
 
5
- export const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8'))
6
+ export const packageJson = JSON.parse(readFileSync(resolve(import.meta.dirname, '../package.json'), 'utf8'))
7
+ export const version = packageJson.version
6
8
 
7
9
  const vite = {
8
10
  type: 'object',
@@ -50,7 +52,7 @@ export const schemaComponents = { vite }
50
52
  export const schema = {
51
53
  $id: `https://schemas.platformatic.dev/@platformatic/vite/${packageJson.version}.json`,
52
54
  $schema: 'http://json-schema.org/draft-07/schema#',
53
- title: 'Platformatic Vite Stackable',
55
+ title: 'Platformatic Vite Config',
54
56
  type: 'object',
55
57
  properties: {
56
58
  $schema: {
@@ -59,7 +61,8 @@ export const schema = {
59
61
  logger: utilsSchemaComponents.logger,
60
62
  server: utilsSchemaComponents.server,
61
63
  watch: basicSchemaComponents.watch,
62
- application: basicSchemaComponents.application,
64
+ application: basicSchemaComponents.buildableApplication,
65
+ runtime: utilsSchemaComponents.wrappedRuntime,
63
66
  vite
64
67
  },
65
68
  additionalProperties: false
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@platformatic/vite",
3
- "version": "3.4.1",
4
- "description": "Platformatic Vite Stackable",
3
+ "version": "3.5.1",
4
+ "description": "Platformatic Vite 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"
@@ -18,31 +18,31 @@
18
18
  "@fastify/static": "^8.0.0",
19
19
  "fastify": "^5.0.0",
20
20
  "semver": "^7.6.3",
21
- "@platformatic/basic": "3.4.1",
22
- "@platformatic/config": "3.4.1",
23
- "@platformatic/node": "3.4.1",
24
- "@platformatic/utils": "3.4.1"
21
+ "@platformatic/basic": "3.5.1",
22
+ "@platformatic/foundation": "3.5.1",
23
+ "@platformatic/node": "3.5.1"
25
24
  },
26
25
  "devDependencies": {
27
- "@fastify/vite": "^7.0.1",
28
- "borp": "^0.17.0",
26
+ "cleaner-spec-reporter": "^0.5.0",
29
27
  "eslint": "9",
30
28
  "json-schema-to-typescript": "^15.0.1",
31
- "neostandard": "^0.11.1",
32
- "react": "^18.3.1",
33
- "react-dom": "^18.3.1",
29
+ "neostandard": "^0.12.0",
30
+ "react": "^19.1.0",
31
+ "react-dom": "^19.1.0",
34
32
  "typescript": "^5.5.4",
35
- "vite": "^5.4.0",
33
+ "vite": "^7.1.4",
36
34
  "ws": "^8.18.0",
37
- "@platformatic/composer": "3.4.1",
38
- "@platformatic/service": "3.4.1"
35
+ "@platformatic/gateway": "3.5.1",
36
+ "@platformatic/service": "3.5.1"
37
+ },
38
+ "engines": {
39
+ "node": ">=22.19.0"
39
40
  },
40
41
  "scripts": {
41
- "test": "npm run lint && borp --concurrency=1 --no-timeout",
42
- "coverage": "npm run lint && borp -C -X test -X test/fixtures --concurrency=1 --no-timeout",
42
+ "test": "node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/*.test.js test/**/*.test.js",
43
43
  "gen-schema": "node lib/schema.js > schema.json",
44
44
  "gen-types": "json2ts > config.d.ts < schema.json",
45
- "build": "pnpm run gen-schema && pnpm run gen-types",
45
+ "build": "npm run gen-schema && npm run gen-types",
46
46
  "lint": "eslint"
47
47
  }
48
48
  }