@platformatic/next 2.19.0-alpha.5 → 2.19.0-alpha.7

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/index.js CHANGED
@@ -16,14 +16,15 @@ import { once } from 'node:events'
16
16
  import { readFile } from 'node:fs/promises'
17
17
  import { dirname, resolve as pathResolve } from 'node:path'
18
18
  import { pathToFileURL } from 'node:url'
19
- import { satisfies } from 'semver'
19
+ import { parse, satisfies } from 'semver'
20
20
  import { packageJson, schema } from './lib/schema.js'
21
21
 
22
- const supportedVersions = '^14.0.0'
22
+ const supportedVersions = ['^14.0.0', '^15.0.0']
23
23
 
24
24
  export class NextStackable extends BaseStackable {
25
25
  #basePath
26
26
  #next
27
+ #nextVersion
27
28
  #child
28
29
  #server
29
30
 
@@ -34,9 +35,10 @@ export class NextStackable extends BaseStackable {
34
35
  async init () {
35
36
  this.#next = pathResolve(dirname(resolvePackage(this.root, 'next')), '../..')
36
37
  const nextPackage = JSON.parse(await readFile(pathResolve(this.#next, 'package.json'), 'utf-8'))
38
+ this.#nextVersion = parse(nextPackage.version)
37
39
 
38
40
  /* c8 ignore next 3 */
39
- if (!satisfies(nextPackage.version, supportedVersions)) {
41
+ if (!supportedVersions.some(v => satisfies(nextPackage.version, v))) {
40
42
  throw new errors.UnsupportedVersion('next', nextPackage.version, supportedVersions)
41
43
  }
42
44
  }
@@ -79,6 +81,10 @@ export class NextStackable extends BaseStackable {
79
81
  }
80
82
 
81
83
  async build () {
84
+ if (!this.#nextVersion) {
85
+ await this.init()
86
+ }
87
+
82
88
  const config = this.configManager.current
83
89
  const loader = new URL('./lib/loader.js', import.meta.url)
84
90
  this.#basePath = config.application?.basePath ? cleanBasePath(config.application?.basePath) : ''
@@ -90,7 +96,7 @@ export class NextStackable extends BaseStackable {
90
96
  command = ['node', pathResolve(this.#next, './dist/bin/next'), 'build', this.root]
91
97
  }
92
98
 
93
- return this.buildWithCommand(command, this.#basePath, loader)
99
+ return this.buildWithCommand(command, this.#basePath, loader, this.#getChildManagerScripts())
94
100
  }
95
101
 
96
102
  /* c8 ignore next 5 */
@@ -119,7 +125,7 @@ export class NextStackable extends BaseStackable {
119
125
  this.#basePath = config.application?.basePath ? cleanBasePath(config.application?.basePath) : ''
120
126
 
121
127
  if (command) {
122
- return this.startWithCommand(command, loaderUrl)
128
+ return this.startWithCommand(command, loaderUrl, this.#getChildManagerScripts())
123
129
  }
124
130
 
125
131
  const { hostname, port } = this.serverConfig ?? {}
@@ -142,7 +148,8 @@ export class NextStackable extends BaseStackable {
142
148
  runtimeBasePath: this.runtimeConfig.basePath,
143
149
  wantsAbsoluteUrls: true,
144
150
  telemetryConfig: this.telemetryConfig
145
- }
151
+ },
152
+ scripts: this.#getChildManagerScripts()
146
153
  })
147
154
 
148
155
  const promise = once(this.childManager, 'url')
@@ -162,7 +169,17 @@ export class NextStackable extends BaseStackable {
162
169
  try {
163
170
  await this.childManager.inject()
164
171
  const childPromise = createChildProcessListener()
165
- await nextDev(serverOptions, 'default', this.root)
172
+
173
+ if (this.#nextVersion.major === 14 && this.#nextVersion.minor < 2) {
174
+ await nextDev({
175
+ '--hostname': serverOptions.host,
176
+ '--port': serverOptions.port,
177
+ _: [this.root]
178
+ })
179
+ } else {
180
+ await nextDev(serverOptions, 'default', this.root)
181
+ }
182
+
166
183
  this.#child = await childPromise
167
184
  } finally {
168
185
  await this.childManager.eject()
@@ -177,7 +194,7 @@ export class NextStackable extends BaseStackable {
177
194
  this.#basePath = config.application?.basePath ? cleanBasePath(config.application?.basePath) : ''
178
195
 
179
196
  if (command) {
180
- return this.startWithCommand(command, loaderUrl)
197
+ return this.startWithCommand(command, loaderUrl, this.#getChildManagerScripts())
181
198
  }
182
199
 
183
200
  this.childManager = new ChildManager({
@@ -193,7 +210,8 @@ export class NextStackable extends BaseStackable {
193
210
  runtimeBasePath: this.runtimeConfig.basePath,
194
211
  wantsAbsoluteUrls: true,
195
212
  telemetryConfig: this.telemetryConfig
196
- }
213
+ },
214
+ scripts: this.#getChildManagerScripts()
197
215
  })
198
216
 
199
217
  this.verifyOutputDirectory(pathResolve(this.root, '.next'))
@@ -222,7 +240,15 @@ export class NextStackable extends BaseStackable {
222
240
  (this.isEntrypoint ? serverOptions?.hostname : undefined) ?? true
223
241
  )
224
242
 
225
- await nextStart(serverOptions, this.root)
243
+ if (this.#nextVersion.major === 14 && this.#nextVersion.minor < 2) {
244
+ await nextStart({
245
+ '--hostname': serverOptions.host,
246
+ '--port': serverOptions.port,
247
+ _: [this.root]
248
+ })
249
+ } else {
250
+ await nextStart(serverOptions, this.root)
251
+ }
226
252
 
227
253
  this.#server = await serverPromise
228
254
  this.url = getServerUrl(this.#server)
@@ -230,6 +256,16 @@ export class NextStackable extends BaseStackable {
230
256
  await this.childManager.eject()
231
257
  }
232
258
  }
259
+
260
+ #getChildManagerScripts () {
261
+ const scripts = []
262
+
263
+ if (this.#nextVersion.major === 15) {
264
+ scripts.push(new URL('./lib/loader-next-15.cjs', import.meta.url))
265
+ }
266
+
267
+ return scripts
268
+ }
233
269
  }
234
270
 
235
271
  /* c8 ignore next 9 */
@@ -0,0 +1,52 @@
1
+ const { resolve } = require('node:path')
2
+ const { fileURLToPath } = require('node:url')
3
+ const fsPromises = require('node:fs').promises
4
+ const { transformSync } = require('amaro')
5
+ const { parse } = require('@babel/parser')
6
+ const traverse = require('@babel/traverse')
7
+
8
+ const originalReadFile = fsPromises.readFile
9
+ const targetFile = resolve(fileURLToPath(globalThis.platformatic.root), 'next.config.ts')
10
+
11
+ function detectFormat (code) {
12
+ let format = 'esm'
13
+
14
+ const ast = parse(code, { sourceType: 'module' })
15
+
16
+ // Manipulate the AST
17
+ traverse.default(ast, {
18
+ AssignmentExpression (path) {
19
+ const { left } = path.node
20
+
21
+ // module.exports = $EXPRESSION
22
+ if (left.object.name === 'module' && left.property.name === 'exports') {
23
+ format = 'cjs'
24
+ path.stop()
25
+ }
26
+ }
27
+ })
28
+
29
+ return format
30
+ }
31
+
32
+ fsPromises.readFile = async function WTF (url, options) {
33
+ if (url.startsWith('file://')) {
34
+ url = fileURLToPath(url)
35
+ }
36
+
37
+ const contents = await originalReadFile(url, options)
38
+
39
+ if (url !== targetFile) {
40
+ return contents
41
+ }
42
+
43
+ const { code } = transformSync(contents.toString('utf-8'), { mode: 'strip-only' })
44
+
45
+ const { transformESM, transformCJS } = await import('./loader.js')
46
+ const transformer = detectFormat(code) === 'esm' ? transformESM : transformCJS
47
+ const transformed = transformer(code)
48
+
49
+ // Restore the original method
50
+ fsPromises.readFile = originalReadFile
51
+ return transformed
52
+ }
package/lib/loader.js CHANGED
@@ -16,7 +16,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
16
16
  const originalId = '__pltOriginalNextConfig'
17
17
 
18
18
  let candidates
19
- let basePath
19
+ let basePath = ''
20
20
 
21
21
  function parseSingleExpression (expr) {
22
22
  return parse(expr, { allowAwaitOutsideFunction: true }).program.body[0]
@@ -65,7 +65,7 @@ function createEvaluatorWrapperFunction (original) {
65
65
  )
66
66
  }
67
67
 
68
- function transformCJS (source) {
68
+ export function transformCJS (source) {
69
69
  const ast = parse(source.toString(), { sourceType: 'module' })
70
70
 
71
71
  // Manipulate the AST
@@ -84,7 +84,7 @@ function transformCJS (source) {
84
84
  return generate.default(ast).code
85
85
  }
86
86
 
87
- function transformESM (source) {
87
+ export function transformESM (source) {
88
88
  const ast = parse(source.toString(), { sourceType: 'module' })
89
89
 
90
90
  // Manipulate the AST
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/next",
3
- "version": "2.19.0-alpha.5",
3
+ "version": "2.19.0-alpha.7",
4
4
  "description": "Platformatic Next.js Stackable",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -19,25 +19,27 @@
19
19
  "@babel/parser": "^7.25.3",
20
20
  "@babel/traverse": "^7.25.3",
21
21
  "@babel/types": "^7.25.2",
22
+ "amaro": "^0.2.0",
22
23
  "semver": "^7.6.3",
23
- "@platformatic/basic": "2.19.0-alpha.5",
24
- "@platformatic/config": "2.19.0-alpha.5",
25
- "@platformatic/utils": "2.19.0-alpha.5"
24
+ "@platformatic/basic": "2.19.0-alpha.7",
25
+ "@platformatic/config": "2.19.0-alpha.7",
26
+ "@platformatic/utils": "2.19.0-alpha.7"
26
27
  },
27
28
  "devDependencies": {
28
29
  "@fastify/reply-from": "^11.0.0",
29
30
  "borp": "^0.19.0",
30
31
  "eslint": "9",
32
+ "execa": "^9.5.1",
31
33
  "fastify": "^5.0.0",
32
34
  "json-schema-to-typescript": "^15.0.1",
33
35
  "neostandard": "^0.11.1",
34
- "next": "^14.2.5",
36
+ "next": "^15.0.0",
35
37
  "react": "^18.3.1",
36
38
  "react-dom": "^18.3.1",
37
39
  "typescript": "^5.5.4",
38
40
  "ws": "^8.18.0",
39
- "@platformatic/composer": "2.19.0-alpha.5",
40
- "@platformatic/service": "2.19.0-alpha.5"
41
+ "@platformatic/composer": "2.19.0-alpha.7",
42
+ "@platformatic/service": "2.19.0-alpha.7"
41
43
  },
42
44
  "scripts": {
43
45
  "test": "npm run lint && borp --concurrency=1 --no-timeout",
package/schema.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "$id": "https://schemas.platformatic.dev/@platformatic/next/2.19.0-alpha.5.json",
2
+ "$id": "https://schemas.platformatic.dev/@platformatic/next/2.19.0-alpha.7.json",
3
3
  "$schema": "http://json-schema.org/draft-07/schema#",
4
4
  "title": "Platformatic Next.js Stackable",
5
5
  "type": "object",