@netlify/plugin-nextjs 4.30.4-ipx-layer.0 → 4.30.4

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.
@@ -14,7 +14,7 @@ const config_1 = require("./config");
14
14
  const matchers_1 = require("./matchers");
15
15
  const maybeLoadJson = (path) => {
16
16
  if ((0, fs_1.existsSync)(path)) {
17
- return (0, fs_extra_1.readJson)(path);
17
+ return (0, fs_extra_1.readJSON)(path);
18
18
  }
19
19
  };
20
20
  const isAppDirRoute = (route, appPathRoutesManifest) => Boolean(appPathRoutesManifest) && Object.values(appPathRoutesManifest).includes(route);
@@ -29,53 +29,6 @@ exports.loadPrerenderManifest = loadPrerenderManifest;
29
29
  * Convert the Next middleware name into a valid Edge Function name
30
30
  */
31
31
  const sanitizeName = (name) => `next_${name.replace(/\W/g, '_')}`;
32
- /**
33
- * Initialization added to the top of the edge function bundle
34
- */
35
- const preamble = /* js */ `
36
- import {
37
- decode as _base64Decode,
38
- } from "https://deno.land/std@0.159.0/encoding/base64.ts";
39
- // Deno defines "window", but naughty libraries think this means it's a browser
40
- delete globalThis.window
41
- globalThis.process = { env: {...Deno.env.toObject(), NEXT_RUNTIME: 'edge', 'NEXT_PRIVATE_MINIMAL_MODE': '1' } }
42
- globalThis.EdgeRuntime = "netlify-edge"
43
- let _ENTRIES = {}
44
-
45
- // Next.js uses this extension to the Headers API implemented by Cloudflare workerd
46
- if(!('getAll' in Headers.prototype)) {
47
- Headers.prototype.getAll = function getAll(name) {
48
- name = name.toLowerCase();
49
- if (name !== "set-cookie") {
50
- throw new Error("Headers.getAll is only supported for Set-Cookie");
51
- }
52
- return [...this.entries()]
53
- .filter(([key]) => key === name)
54
- .map(([, value]) => value);
55
- };
56
- }
57
- // Next uses blob: urls to refer to local assets, so we need to intercept these
58
- const _fetch = globalThis.fetch
59
- const fetch = async (url, init) => {
60
- try {
61
- if (typeof url === 'object' && url.href?.startsWith('blob:')) {
62
- const key = url.href.slice(5)
63
- if (key in _ASSETS) {
64
- return new Response(_base64Decode(_ASSETS[key]))
65
- }
66
- }
67
- return await _fetch(url, init)
68
- } catch (error) {
69
- console.error(error)
70
- throw error
71
- }
72
- }
73
-
74
- // Next edge runtime uses "self" as a function-scoped global-like object, but some of the older polyfills expect it to equal globalThis
75
- // See https://nextjs.org/docs/basic-features/supported-browsers-features#polyfills
76
- const self = { ...globalThis, fetch }
77
-
78
- `;
79
32
  // Slightly different spacing in different versions!
80
33
  const IMPORT_UNSUPPORTED = [
81
34
  `Object.defineProperty(globalThis,"__import_unsupported"`,
@@ -86,7 +39,8 @@ const IMPORT_UNSUPPORTED = [
86
39
  */
87
40
  const getMiddlewareBundle = async ({ edgeFunctionDefinition, netlifyConfig, }) => {
88
41
  const { publish } = netlifyConfig.build;
89
- const chunks = [preamble];
42
+ const shims = await fs_1.promises.readFile(getEdgeTemplatePath('shims.js'), 'utf8');
43
+ const chunks = [shims];
90
44
  chunks.push(`export const _DEFINITION = ${JSON.stringify(edgeFunctionDefinition)}`);
91
45
  if ('wasm' in edgeFunctionDefinition) {
92
46
  for (const { name, filePath } of edgeFunctionDefinition.wasm) {
@@ -234,7 +188,6 @@ const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
234
188
  var _a;
235
189
  const manifest = {
236
190
  functions: [],
237
- layers: [],
238
191
  version: 1,
239
192
  };
240
193
  const edgeFunctionRoot = (0, path_1.resolve)('.netlify', 'edge-functions');
@@ -257,11 +210,7 @@ const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
257
210
  manifest.functions.push({
258
211
  function: 'ipx',
259
212
  name: 'next/image handler',
260
- path: nextConfig.images.path || '/_next/image',
261
- });
262
- manifest.layers.push({
263
- name: 'https://ipx-edge-function-layer.netlify.app/mod.ts',
264
- flag: 'ipx-edge-function-layer-url',
213
+ path: '/_next/image*',
265
214
  });
266
215
  }
267
216
  if (!(0, destr_1.default)(process.env.NEXT_DISABLE_NETLIFY_EDGE)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/plugin-nextjs",
3
- "version": "4.30.4-ipx-layer.0",
3
+ "version": "4.30.4",
4
4
  "description": "Run Next.js seamlessly on Netlify",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -36,12 +36,12 @@
36
36
  },
37
37
  "devDependencies": {
38
38
  "@delucis/if-env": "^1.1.2",
39
- "@netlify/build": "^29.5.2",
39
+ "@netlify/build": "^29.5.4",
40
40
  "@types/fs-extra": "^9.0.13",
41
41
  "@types/jest": "^27.4.1",
42
42
  "@types/merge-stream": "^1.1.2",
43
43
  "@types/node": "^17.0.25",
44
- "next": "^13.0.7",
44
+ "next": "^13.1.6",
45
45
  "npm-run-all": "^4.1.5",
46
46
  "typescript": "^4.6.3"
47
47
  },
@@ -1,5 +1,73 @@
1
- import { getHandler } from 'https://ipx-edge-function-layer.netlify.app/mod.ts'
2
-
1
+ import { Accepts } from 'https://deno.land/x/accepts@2.1.1/mod.ts'
2
+ import type { Context } from 'https://edge.netlify.com'
3
+ // Available at build time
3
4
  import imageconfig from './imageconfig.json' assert { type: 'json' }
4
5
 
5
- export default getHandler({ formats: imageconfig?.formats })
6
+ const defaultFormat = 'webp'
7
+
8
+ interface ImageConfig extends Record<string, unknown> {
9
+ formats?: string[]
10
+ }
11
+
12
+ // Checks if a URL param is numeric
13
+ const isNumeric = (value: string | null) => Number(value).toString() === value
14
+
15
+ /**
16
+ * Implement content negotiation for images
17
+ */
18
+
19
+ // deno-lint-ignore require-await
20
+ const handler = async (req: Request, context: Context) => {
21
+ const { searchParams } = new URL(req.url)
22
+ const accept = new Accepts(req.headers)
23
+ const { formats = [defaultFormat] } = imageconfig as ImageConfig
24
+ if (formats.length === 0) {
25
+ formats.push(defaultFormat)
26
+ }
27
+ let type = accept.types(formats) || defaultFormat
28
+ if (Array.isArray(type)) {
29
+ type = type[0]
30
+ }
31
+
32
+ const source = searchParams.get('url')
33
+ const width = searchParams.get('w')
34
+ const quality = searchParams.get('q') ?? '75'
35
+
36
+ const errors: Array<string> = []
37
+
38
+ if (!source) {
39
+ errors.push('Missing "url" parameter')
40
+ } else if (!source.startsWith('http') && !source.startsWith('/')) {
41
+ errors.push('The "url" parameter must be a valid URL or path')
42
+ }
43
+
44
+ if (!width) {
45
+ errors.push('Missing "w" parameter')
46
+ } else if (!isNumeric(width)) {
47
+ errors.push('Invalid "w" parameter')
48
+ }
49
+
50
+ if (!isNumeric(quality)) {
51
+ errors.push('Invalid "q" parameter')
52
+ }
53
+
54
+ if (!source || errors.length > 0) {
55
+ return new Response(`Invalid request: \n${errors.join('\n')}`, {
56
+ status: 400,
57
+ })
58
+ }
59
+
60
+ const modifiers = [`w_${width}`, `q_${quality}`]
61
+
62
+ if (type) {
63
+ if (type.includes('/')) {
64
+ // If this is a mimetype, strip "image/"
65
+ type = type.split('/')[1]
66
+ }
67
+ modifiers.push(`f_${type}`)
68
+ }
69
+ const target = `/_ipx/${modifiers.join(',')}/${encodeURIComponent(source)}`
70
+ return context.rewrite(target)
71
+ }
72
+
73
+ export default handler
@@ -0,0 +1,53 @@
1
+ // @ts-check
2
+ // deno-lint-ignore-file no-var prefer-const no-unused-vars no-explicit-any
3
+ import { decode as _base64Decode } from 'https://deno.land/std@0.175.0/encoding/base64.ts'
4
+ import { AsyncLocalStorage as ALSCompat } from 'https://deno.land/std@0.175.0/node/async_hooks.ts'
5
+
6
+ /**
7
+ * These are the shims, polyfills and other kludges to make Next.js work in standards-compliant runtime.
8
+ * This file isn't imported, but is instead inlined along with other chunks into the edge bundle.
9
+ */
10
+
11
+ // Deno defines "window", but naughty libraries think this means it's a browser
12
+ // @ts-ignore
13
+ delete globalThis.window
14
+ globalThis.process = {
15
+ env: { ...Deno.env.toObject(), NEXT_RUNTIME: 'edge', NEXT_PRIVATE_MINIMAL_MODE: '1' },
16
+ }
17
+ globalThis.EdgeRuntime = 'netlify-edge'
18
+ let _ENTRIES = {}
19
+
20
+ // Next.js expects this as a global
21
+ globalThis.AsyncLocalStorage = ALSCompat
22
+
23
+ // Next.js uses this extension to the Headers API implemented by Cloudflare workerd
24
+ if (!('getAll' in Headers.prototype)) {
25
+ // @ts-ignore
26
+ Headers.prototype.getAll = function getAll(name) {
27
+ name = name.toLowerCase()
28
+ if (name !== 'set-cookie') {
29
+ throw new Error('Headers.getAll is only supported for Set-Cookie')
30
+ }
31
+ return [...this.entries()].filter(([key]) => key === name).map(([, value]) => value)
32
+ }
33
+ }
34
+ // Next uses blob: urls to refer to local assets, so we need to intercept these
35
+ const _fetch = globalThis.fetch
36
+ const fetch /* type {typeof globalThis.fetch} */ = async (url, init) => {
37
+ try {
38
+ if (url instanceof URL && url.href?.startsWith('blob:')) {
39
+ const key = url.href.slice(5)
40
+ if (key in _ASSETS) {
41
+ return new Response(_base64Decode(_ASSETS[key]))
42
+ }
43
+ }
44
+ return await _fetch(url, init)
45
+ } catch (error) {
46
+ console.error(error)
47
+ throw error
48
+ }
49
+ }
50
+
51
+ // Next edge runtime uses "self" as a function-scoped global-like object, but some of the older polyfills expect it to equal globalThis
52
+ // See https://nextjs.org/docs/basic-features/supported-browsers-features#polyfills
53
+ const self = { ...globalThis, fetch }
@@ -1,3 +0,0 @@
1
- {
2
- "formats": ["webp"]
3
- }