@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.
@@ -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
- globalThis.process = { env: {...Deno.env.toObject(), NEXT_RUNTIME: 'edge', 'NEXT_PRIVATE_MINIMAL_MODE': '1' } }
33
- let _ENTRIES = {}
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
- const nextAuthUrl = `${process.env.DEPLOY_PRIME_URL}${basePath}`;
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.27.2",
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.0",
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.0",
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("./utils.ts").FetchEventResult>}
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: 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
- return new Response(JSON.stringify(transformed), response)
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 = ''