@platformatic/next 3.4.1 → 3.5.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 +348 -3
- package/index.js +25 -249
- package/lib/caching/valkey.js +382 -0
- package/lib/capability.js +339 -0
- package/lib/create-context-patch.js +31 -0
- package/lib/loader-next-15.cjs +52 -0
- package/lib/loader.js +48 -19
- package/lib/schema.js +55 -6
- package/package.json +23 -17
- package/schema.json +1404 -7
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import { createRequire } from 'node:module'
|
|
4
|
+
import { getGlobalDispatcher } from 'undici'
|
|
5
|
+
|
|
6
|
+
// Next.js runs middlewares in it's own patched vm context. So the global dispatcher in
|
|
7
|
+
// the middleware context is different from an application global dispatcher. This
|
|
8
|
+
// method sets an application global dispatcher after next.js defines it's own version of
|
|
9
|
+
// fetch function.
|
|
10
|
+
export function patchVmCreateContext () {
|
|
11
|
+
const _require = createRequire(process.cwd())
|
|
12
|
+
const vm = _require('node:vm')
|
|
13
|
+
|
|
14
|
+
const originalCreateContext = vm.createContext
|
|
15
|
+
vm.createContext = (contextObject, opts) => {
|
|
16
|
+
const globalDispatcher = getGlobalDispatcher()
|
|
17
|
+
const context = originalCreateContext(contextObject, opts)
|
|
18
|
+
queueMicrotask(() => {
|
|
19
|
+
if (contextObject.fetch === undefined) return
|
|
20
|
+
|
|
21
|
+
const originalFetch = contextObject.fetch
|
|
22
|
+
contextObject.fetch = (input, init = {}) => {
|
|
23
|
+
init.dispatcher = globalDispatcher
|
|
24
|
+
return originalFetch(input, init)
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
return context
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
patchVmCreateContext()
|
|
@@ -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
|
@@ -11,12 +11,14 @@ import {
|
|
|
11
11
|
variableDeclarator
|
|
12
12
|
} from '@babel/types'
|
|
13
13
|
import { readFile, realpath } from 'node:fs/promises'
|
|
14
|
+
import { sep } from 'node:path'
|
|
14
15
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
15
16
|
|
|
16
17
|
const originalId = '__pltOriginalNextConfig'
|
|
17
18
|
|
|
19
|
+
let config
|
|
18
20
|
let candidates
|
|
19
|
-
let basePath
|
|
21
|
+
let basePath = ''
|
|
20
22
|
|
|
21
23
|
function parseSingleExpression (expr) {
|
|
22
24
|
return parse(expr, { allowAwaitOutsideFunction: true }).program.body[0]
|
|
@@ -35,37 +37,63 @@ function parseSingleExpression (expr) {
|
|
|
35
37
|
__pltOriginalNextConfig.basePath = basePath
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
if(typeof __pltOriginalNextConfig.cacheHandler === 'undefined') {
|
|
41
|
+
__pltOriginalNextConfig.cacheHandler = $PATH
|
|
42
|
+
__pltOriginalNextConfig.cacheMaxMemorySize = 0
|
|
43
|
+
}
|
|
40
44
|
|
|
41
|
-
// This is to send the configuraion when Next is executed in
|
|
42
|
-
|
|
45
|
+
// This is to send the configuraion when Next is executed in a child process
|
|
46
|
+
globalThis.platformatic.notifyConfig(__pltOriginalNextConfig)
|
|
43
47
|
|
|
44
48
|
return __pltOriginalNextConfig;
|
|
45
49
|
}
|
|
46
50
|
*/
|
|
47
51
|
function createEvaluatorWrapperFunction (original) {
|
|
52
|
+
const cacheHandler = config?.cache?.adapter
|
|
53
|
+
? fileURLToPath(new URL(`./caching/${config.cache.adapter}.js`, import.meta.url)).replaceAll(sep, '/')
|
|
54
|
+
: undefined
|
|
55
|
+
|
|
56
|
+
const trailingSlash = config?.next?.trailingSlash
|
|
57
|
+
|
|
48
58
|
return functionDeclaration(
|
|
49
59
|
null,
|
|
50
60
|
[restElement(identifier('args'))],
|
|
51
|
-
blockStatement(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
blockStatement(
|
|
62
|
+
[
|
|
63
|
+
// This is to avoid https://github.com/vercel/next.js/issues/76981
|
|
64
|
+
parseSingleExpression("Headers.prototype[Symbol.for('nodejs.util.inspect.custom')] = undefined"),
|
|
65
|
+
variableDeclaration('let', [variableDeclarator(identifier(originalId), original)]),
|
|
66
|
+
parseSingleExpression(
|
|
67
|
+
`if (typeof ${originalId} === 'function') { ${originalId} = await ${originalId}(...args) }`
|
|
68
|
+
),
|
|
69
|
+
parseSingleExpression(
|
|
70
|
+
`if (typeof ${originalId}.basePath === 'undefined') { ${originalId}.basePath = "${basePath}" }`
|
|
71
|
+
),
|
|
72
|
+
cacheHandler
|
|
73
|
+
? parseSingleExpression(`
|
|
74
|
+
if (typeof ${originalId}.cacheHandler === 'undefined') {
|
|
75
|
+
${originalId}.cacheHandler = '${cacheHandler}'
|
|
76
|
+
${originalId}.cacheMaxMemorySize = 0
|
|
77
|
+
}
|
|
78
|
+
`)
|
|
79
|
+
: undefined,
|
|
80
|
+
trailingSlash
|
|
81
|
+
? parseSingleExpression(`
|
|
82
|
+
if (typeof ${originalId}.trailingSlash === 'undefined') {
|
|
83
|
+
${originalId}.trailingSlash = true
|
|
84
|
+
}
|
|
85
|
+
`)
|
|
86
|
+
: undefined,
|
|
87
|
+
parseSingleExpression(`globalThis.platformatic.notifyConfig(${originalId})`),
|
|
88
|
+
returnStatement(identifier(originalId))
|
|
89
|
+
].filter(e => e)
|
|
90
|
+
),
|
|
63
91
|
false,
|
|
64
92
|
true
|
|
65
93
|
)
|
|
66
94
|
}
|
|
67
95
|
|
|
68
|
-
function transformCJS (source) {
|
|
96
|
+
export function transformCJS (source) {
|
|
69
97
|
const ast = parse(source.toString(), { sourceType: 'module' })
|
|
70
98
|
|
|
71
99
|
// Manipulate the AST
|
|
@@ -84,7 +112,7 @@ function transformCJS (source) {
|
|
|
84
112
|
return generate.default(ast).code
|
|
85
113
|
}
|
|
86
114
|
|
|
87
|
-
function transformESM (source) {
|
|
115
|
+
export function transformESM (source) {
|
|
88
116
|
const ast = parse(source.toString(), { sourceType: 'module' })
|
|
89
117
|
|
|
90
118
|
// Manipulate the AST
|
|
@@ -125,6 +153,7 @@ export async function initialize (data) {
|
|
|
125
153
|
// Keep in sync with https://github.com/vercel/next.js/blob/main/packages/next/src/shared/lib/constants.ts
|
|
126
154
|
candidates = ['next.config.js', 'next.config.mjs'].map(c => new URL(c, realRoot).toString())
|
|
127
155
|
basePath = data.basePath ?? ''
|
|
156
|
+
config = data.config
|
|
128
157
|
}
|
|
129
158
|
|
|
130
159
|
export async function load (url, context, nextLoad) {
|
package/lib/schema.js
CHANGED
|
@@ -1,13 +1,59 @@
|
|
|
1
|
-
import { schemaComponents } from '@platformatic/basic'
|
|
2
|
-
import { schemaComponents as utilsSchemaComponents } from '@platformatic/
|
|
1
|
+
import { schemaComponents as basicSchemaComponents } from '@platformatic/basic'
|
|
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(
|
|
6
|
+
export const packageJson = JSON.parse(readFileSync(resolve(import.meta.dirname, '../package.json'), 'utf8'))
|
|
7
|
+
export const version = packageJson.version
|
|
8
|
+
|
|
9
|
+
export const cache = {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
adapter: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
enum: ['redis', 'valkey']
|
|
15
|
+
},
|
|
16
|
+
url: {
|
|
17
|
+
type: 'string'
|
|
18
|
+
},
|
|
19
|
+
prefix: {
|
|
20
|
+
type: 'string'
|
|
21
|
+
},
|
|
22
|
+
maxTTL: {
|
|
23
|
+
default: 86400 * 7, // One week
|
|
24
|
+
anyOf: [
|
|
25
|
+
{
|
|
26
|
+
type: 'number',
|
|
27
|
+
minimum: 0
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
type: 'string'
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
required: ['adapter', 'url'],
|
|
36
|
+
additionalProperties: false
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const next = {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
trailingSlash: {
|
|
43
|
+
type: 'boolean',
|
|
44
|
+
default: false
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
default: {},
|
|
48
|
+
additionalProperties: false
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const schemaComponents = { next }
|
|
6
52
|
|
|
7
53
|
export const schema = {
|
|
8
54
|
$id: `https://schemas.platformatic.dev/@platformatic/next/${packageJson.version}.json`,
|
|
9
55
|
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
10
|
-
title: 'Platformatic Next.js
|
|
56
|
+
title: 'Platformatic Next.js Config',
|
|
11
57
|
type: 'object',
|
|
12
58
|
properties: {
|
|
13
59
|
$schema: {
|
|
@@ -15,8 +61,11 @@ export const schema = {
|
|
|
15
61
|
},
|
|
16
62
|
logger: utilsSchemaComponents.logger,
|
|
17
63
|
server: utilsSchemaComponents.server,
|
|
18
|
-
watch:
|
|
19
|
-
application:
|
|
64
|
+
watch: basicSchemaComponents.watch,
|
|
65
|
+
application: basicSchemaComponents.buildableApplication,
|
|
66
|
+
runtime: utilsSchemaComponents.wrappedRuntime,
|
|
67
|
+
next,
|
|
68
|
+
cache
|
|
20
69
|
},
|
|
21
70
|
additionalProperties: false
|
|
22
71
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/next",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "Platformatic Next.js
|
|
3
|
+
"version": "3.5.0",
|
|
4
|
+
"description": "Platformatic Next.js 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": "
|
|
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"
|
|
@@ -19,32 +19,38 @@
|
|
|
19
19
|
"@babel/parser": "^7.25.3",
|
|
20
20
|
"@babel/traverse": "^7.25.3",
|
|
21
21
|
"@babel/types": "^7.25.2",
|
|
22
|
+
"amaro": "^0.3.0",
|
|
23
|
+
"iovalkey": "^0.3.0",
|
|
24
|
+
"msgpackr": "^1.11.2",
|
|
22
25
|
"semver": "^7.6.3",
|
|
23
|
-
"@platformatic/basic": "3.
|
|
24
|
-
"@platformatic/
|
|
25
|
-
"@platformatic/utils": "3.4.1"
|
|
26
|
+
"@platformatic/basic": "3.5.0",
|
|
27
|
+
"@platformatic/foundation": "3.5.0"
|
|
26
28
|
},
|
|
27
29
|
"devDependencies": {
|
|
28
|
-
"@fastify/reply-from": "^
|
|
29
|
-
"
|
|
30
|
+
"@fastify/reply-from": "^12.0.0",
|
|
31
|
+
"@types/node": "^22.5.0",
|
|
32
|
+
"@types/react": "^19.1.11",
|
|
33
|
+
"@types/react-dom": "^19.1.7",
|
|
34
|
+
"cleaner-spec-reporter": "^0.5.0",
|
|
30
35
|
"eslint": "9",
|
|
36
|
+
"execa": "^9.5.1",
|
|
31
37
|
"fastify": "^5.0.0",
|
|
32
38
|
"json-schema-to-typescript": "^15.0.1",
|
|
33
|
-
"neostandard": "^0.
|
|
34
|
-
"next": "^
|
|
35
|
-
"react": "^18.3.1",
|
|
36
|
-
"react-dom": "^18.3.1",
|
|
39
|
+
"neostandard": "^0.12.0",
|
|
40
|
+
"next": "^15.0.0",
|
|
37
41
|
"typescript": "^5.5.4",
|
|
38
42
|
"ws": "^8.18.0",
|
|
39
|
-
"@platformatic/
|
|
40
|
-
"@platformatic/
|
|
43
|
+
"@platformatic/service": "3.5.0",
|
|
44
|
+
"@platformatic/gateway": "3.5.0"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=22.19.0"
|
|
41
48
|
},
|
|
42
49
|
"scripts": {
|
|
43
|
-
"test": "
|
|
44
|
-
"coverage": "npm run lint && borp -C -X test -X test/fixtures --concurrency=1 --no-timeout",
|
|
50
|
+
"test": "node --test --test-reporter=cleaner-spec-reporter --test-concurrency=1 --test-timeout=2000000 test/*.test.js test/**/*.test.js",
|
|
45
51
|
"gen-schema": "node lib/schema.js > schema.json",
|
|
46
52
|
"gen-types": "json2ts > config.d.ts < schema.json",
|
|
47
|
-
"build": "
|
|
53
|
+
"build": "npm run gen-schema && npm run gen-types",
|
|
48
54
|
"lint": "eslint"
|
|
49
55
|
}
|
|
50
56
|
}
|