@netlify/plugin-nextjs 4.27.2 → 4.28.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.
package/lib/helpers/edge.js
CHANGED
|
@@ -12,6 +12,7 @@ const destr_1 = __importDefault(require("destr"));
|
|
|
12
12
|
const fs_extra_1 = require("fs-extra");
|
|
13
13
|
const outdent_1 = require("outdent");
|
|
14
14
|
const config_1 = require("./config");
|
|
15
|
+
const matchers_1 = require("./matchers");
|
|
15
16
|
const loadMiddlewareManifest = (netlifyConfig) => {
|
|
16
17
|
const middlewarePath = (0, path_1.resolve)(netlifyConfig.build.publish, 'server', 'middleware-manifest.json');
|
|
17
18
|
if (!(0, fs_1.existsSync)(middlewarePath)) {
|
|
@@ -28,13 +29,33 @@ const sanitizeName = (name) => `next_${name.replace(/\W/g, '_')}`;
|
|
|
28
29
|
* Initialization added to the top of the edge function bundle
|
|
29
30
|
*/
|
|
30
31
|
const preamble = /* js */ `
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
import {
|
|
33
|
+
decode as _base64Decode,
|
|
34
|
+
} from "https://deno.land/std@0.159.0/encoding/base64.ts";
|
|
34
35
|
// Deno defines "window", but naughty libraries think this means it's a browser
|
|
35
36
|
delete globalThis.window
|
|
37
|
+
globalThis.process = { env: {...Deno.env.toObject(), NEXT_RUNTIME: 'edge', 'NEXT_PRIVATE_MINIMAL_MODE': '1' } }
|
|
36
38
|
// Next uses "self" as a function-scoped global-like object
|
|
37
39
|
const self = {}
|
|
40
|
+
let _ENTRIES = {}
|
|
41
|
+
|
|
42
|
+
// Next uses blob: urls to refer to local assets, so we need to intercept these
|
|
43
|
+
const _fetch = globalThis.fetch
|
|
44
|
+
const fetch = async (url, init) => {
|
|
45
|
+
try {
|
|
46
|
+
if (typeof url === 'object' && url.href?.startsWith('blob:')) {
|
|
47
|
+
const key = url.href.slice(5)
|
|
48
|
+
if (key in _ASSETS) {
|
|
49
|
+
return new Response(_base64Decode(_ASSETS[key]))
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return await _fetch(url, init)
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error(error)
|
|
55
|
+
throw error
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
38
59
|
`;
|
|
39
60
|
// Slightly different spacing in different versions!
|
|
40
61
|
const IMPORT_UNSUPPORTED = [
|
|
@@ -47,6 +68,19 @@ const IMPORT_UNSUPPORTED = [
|
|
|
47
68
|
const getMiddlewareBundle = async ({ edgeFunctionDefinition, netlifyConfig, }) => {
|
|
48
69
|
const { publish } = netlifyConfig.build;
|
|
49
70
|
const chunks = [preamble];
|
|
71
|
+
if ('wasm' in edgeFunctionDefinition) {
|
|
72
|
+
for (const { name, filePath } of edgeFunctionDefinition.wasm) {
|
|
73
|
+
const wasm = await fs_1.promises.readFile((0, path_1.join)(publish, filePath));
|
|
74
|
+
chunks.push(`const ${name} = _base64Decode(${JSON.stringify(wasm.toString('base64'))}).buffer`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if ('assets' in edgeFunctionDefinition) {
|
|
78
|
+
chunks.push(`const _ASSETS = {}`);
|
|
79
|
+
for (const { name, filePath } of edgeFunctionDefinition.assets) {
|
|
80
|
+
const wasm = await fs_1.promises.readFile((0, path_1.join)(publish, filePath));
|
|
81
|
+
chunks.push(`_ASSETS[${JSON.stringify(name)}] = ${JSON.stringify(wasm.toString('base64'))}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
50
84
|
for (const file of edgeFunctionDefinition.files) {
|
|
51
85
|
const filePath = (0, path_1.join)(publish, file);
|
|
52
86
|
let data = await fs_1.promises.readFile(filePath, 'utf8');
|
|
@@ -61,7 +95,7 @@ const getMiddlewareBundle = async ({ edgeFunctionDefinition, netlifyConfig, }) =
|
|
|
61
95
|
};
|
|
62
96
|
const getEdgeTemplatePath = (file) => (0, path_1.join)(__dirname, '..', '..', 'src', 'templates', 'edge', file);
|
|
63
97
|
const copyEdgeSourceFile = ({ file, target, edgeFunctionDir, }) => fs_1.promises.copyFile(getEdgeTemplatePath(file), (0, path_1.join)(edgeFunctionDir, target !== null && target !== void 0 ? target : file));
|
|
64
|
-
const writeEdgeFunction = async ({ edgeFunctionDefinition, edgeFunctionRoot, netlifyConfig, }) => {
|
|
98
|
+
const writeEdgeFunction = async ({ edgeFunctionDefinition, edgeFunctionRoot, netlifyConfig, nextConfig, }) => {
|
|
65
99
|
const name = sanitizeName(edgeFunctionDefinition.name);
|
|
66
100
|
const edgeFunctionDir = (0, path_1.join)(edgeFunctionRoot, name);
|
|
67
101
|
const bundle = await getMiddlewareBundle({
|
|
@@ -80,13 +114,19 @@ const writeEdgeFunction = async ({ edgeFunctionDefinition, edgeFunctionRoot, net
|
|
|
80
114
|
if ('regexp' in edgeFunctionDefinition) {
|
|
81
115
|
matchers.push({ regexp: edgeFunctionDefinition.regexp });
|
|
82
116
|
}
|
|
117
|
+
else if (nextConfig.i18n) {
|
|
118
|
+
matchers.push(...edgeFunctionDefinition.matchers.map((matcher) => ({
|
|
119
|
+
...matcher,
|
|
120
|
+
regexp: (0, matchers_1.makeLocaleOptional)(matcher.regexp),
|
|
121
|
+
})));
|
|
122
|
+
}
|
|
83
123
|
else {
|
|
84
124
|
matchers.push(...edgeFunctionDefinition.matchers);
|
|
85
125
|
}
|
|
86
126
|
await (0, fs_extra_1.writeJson)((0, path_1.join)(edgeFunctionDir, 'matchers.json'), matchers);
|
|
87
127
|
// We add a defintion for each matching path
|
|
88
128
|
return matchers.map((matcher) => {
|
|
89
|
-
const pattern = matcher.regexp;
|
|
129
|
+
const pattern = (0, matchers_1.stripLookahead)(matcher.regexp);
|
|
90
130
|
return { function: name, pattern, name: edgeFunctionDefinition.name };
|
|
91
131
|
});
|
|
92
132
|
};
|
|
@@ -155,6 +195,7 @@ const writeEdgeFunctions = async (netlifyConfig) => {
|
|
|
155
195
|
edgeFunctionDefinition,
|
|
156
196
|
edgeFunctionRoot,
|
|
157
197
|
netlifyConfig,
|
|
198
|
+
nextConfig,
|
|
158
199
|
});
|
|
159
200
|
manifest.functions.push(...functionDefinitions);
|
|
160
201
|
}
|
|
@@ -167,6 +208,7 @@ const writeEdgeFunctions = async (netlifyConfig) => {
|
|
|
167
208
|
edgeFunctionDefinition,
|
|
168
209
|
edgeFunctionRoot,
|
|
169
210
|
netlifyConfig,
|
|
211
|
+
nextConfig,
|
|
170
212
|
});
|
|
171
213
|
manifest.functions.push(...functionDefinitions);
|
|
172
214
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makeLocaleOptional = exports.stripLookahead = void 0;
|
|
4
|
+
const regexp_tree_1 = require("regexp-tree");
|
|
5
|
+
// The Go regexp lib doesn't support lookaheads, so we need to remove them
|
|
6
|
+
const stripLookahead = (regex) => {
|
|
7
|
+
// Early return if there's no lookahead
|
|
8
|
+
if (!(regex === null || regex === void 0 ? void 0 : regex.includes('(?!'))) {
|
|
9
|
+
return regex;
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
// Parse the regexp into an AST
|
|
13
|
+
const re = (0, regexp_tree_1.transform)(`/${regex}/`, {
|
|
14
|
+
Assertion(path) {
|
|
15
|
+
// Remove the lookahead
|
|
16
|
+
if (path.node.kind === 'Lookahead') {
|
|
17
|
+
path.remove();
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
// Strip the leading and trailing slashes
|
|
22
|
+
return re.toString().slice(1, -1);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// Failed to parse regex, so return unchanged
|
|
26
|
+
return regex;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
exports.stripLookahead = stripLookahead;
|
|
30
|
+
const LOCALIZED_REGEX_PREFIX = '(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))';
|
|
31
|
+
const OPTIONAL_REGEX_PREFIX = '(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))?';
|
|
32
|
+
// Make the locale section of the matcher regex optional
|
|
33
|
+
const makeLocaleOptional = (regex) => regex.replace(LOCALIZED_REGEX_PREFIX, OPTIONAL_REGEX_PREFIX);
|
|
34
|
+
exports.makeLocaleOptional = makeLocaleOptional;
|
package/lib/index.js
CHANGED
|
@@ -82,7 +82,9 @@ const plugin = {
|
|
|
82
82
|
await (0, config_1.updateRequiredServerFiles)(publish, config);
|
|
83
83
|
}
|
|
84
84
|
else {
|
|
85
|
-
|
|
85
|
+
// Using the deploy prime url in production leads to issues because the unique deploy ID is part of the generated URL
|
|
86
|
+
// and will not match the expected URL in the callback URL of an OAuth application.
|
|
87
|
+
const nextAuthUrl = `${process.env.CONTEXT === 'production' ? process.env.URL : process.env.DEPLOY_PRIME_URL}${basePath}`;
|
|
86
88
|
console.log(`NextAuth package detected, setting NEXTAUTH_URL environment variable to ${nextAuthUrl}`);
|
|
87
89
|
config.config.env.NEXTAUTH_URL = nextAuthUrl;
|
|
88
90
|
await (0, config_1.updateRequiredServerFiles)(publish, config);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/plugin-nextjs",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.28.1",
|
|
4
4
|
"description": "Run Next.js seamlessly on Netlify",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -28,18 +28,19 @@
|
|
|
28
28
|
"p-limit": "^3.1.0",
|
|
29
29
|
"pathe": "^0.2.0",
|
|
30
30
|
"pretty-bytes": "^5.6.0",
|
|
31
|
+
"regexp-tree": "^0.1.24",
|
|
31
32
|
"semver": "^7.3.5",
|
|
32
33
|
"slash": "^3.0.0",
|
|
33
34
|
"tiny-glob": "^0.2.9"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@delucis/if-env": "^1.1.2",
|
|
37
|
-
"@netlify/build": "^28.1.
|
|
38
|
+
"@netlify/build": "^28.1.2",
|
|
38
39
|
"@types/fs-extra": "^9.0.13",
|
|
39
40
|
"@types/jest": "^27.4.1",
|
|
40
41
|
"@types/merge-stream": "^1.1.2",
|
|
41
42
|
"@types/node": "^17.0.25",
|
|
42
|
-
"next": "^12.3.
|
|
43
|
+
"next": "^12.3.2-canary.43",
|
|
43
44
|
"npm-run-all": "^4.1.5",
|
|
44
45
|
"typescript": "^4.6.3"
|
|
45
46
|
},
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* This placeholder is replaced with the compiled Next.js bundle at build time
|
|
3
3
|
* @param {Object} props
|
|
4
4
|
* @param {import("./runtime.ts").RequestData} props.request
|
|
5
|
-
* @returns {Promise<import("
|
|
5
|
+
* @returns {Promise<import("../edge-shared/utils.ts").FetchEventResult>}
|
|
6
6
|
*/
|
|
7
7
|
export default async ({ request }) => {}
|
|
@@ -33,7 +33,7 @@ export interface RequestData {
|
|
|
33
33
|
name?: string
|
|
34
34
|
params?: { [key: string]: string }
|
|
35
35
|
}
|
|
36
|
-
url:
|
|
36
|
+
url: string
|
|
37
37
|
body?: ReadableStream<Uint8Array>
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -82,7 +82,7 @@ const handler = async (req: Request, context: Context) => {
|
|
|
82
82
|
const request: RequestData = {
|
|
83
83
|
headers: Object.fromEntries(req.headers.entries()),
|
|
84
84
|
geo,
|
|
85
|
-
url,
|
|
85
|
+
url: req.url,
|
|
86
86
|
method: req.method,
|
|
87
87
|
ip: context.ip,
|
|
88
88
|
body: req.body ?? undefined,
|
|
@@ -83,7 +83,10 @@ export const buildResponse = async ({
|
|
|
83
83
|
const transformed = response.dataTransforms.reduce((prev, transform) => {
|
|
84
84
|
return transform(prev)
|
|
85
85
|
}, props)
|
|
86
|
-
|
|
86
|
+
const body = JSON.stringify(transformed)
|
|
87
|
+
const headers = new Headers(response.headers)
|
|
88
|
+
headers.set('content-length', String(body.length))
|
|
89
|
+
return new Response(body, { ...response, headers })
|
|
87
90
|
}
|
|
88
91
|
// This var will hold the contents of the script tag
|
|
89
92
|
let buffer = ''
|