@netlify/plugin-nextjs 5.6.0 → 5.7.0-ipx.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.
@@ -392,6 +392,7 @@ var import_path_to_regexp = __toESM(require_dist(), 1);
392
392
  import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
393
393
  import { dirname, join } from "node:path";
394
394
  import { EDGE_HANDLER_NAME } from "../plugin-context.js";
395
+ import { createIpxEdgeAcceptHandler } from "./ipx.js";
395
396
  var writeEdgeManifest = async (ctx, manifest) => {
396
397
  await mkdir(ctx.edgeFunctionsDir, { recursive: true });
397
398
  await writeFile(join(ctx.edgeFunctionsDir, "manifest.json"), JSON.stringify(manifest, null, 2));
@@ -513,6 +514,9 @@ var createEdgeHandlers = async (ctx) => {
513
514
  version: 1,
514
515
  functions: netlifyDefinitions
515
516
  };
517
+ if (ctx.imageService === "ipx") {
518
+ await createIpxEdgeAcceptHandler(ctx, netlifyManifest);
519
+ }
516
520
  await writeEdgeManifest(ctx, netlifyManifest);
517
521
  };
518
522
  export {
@@ -0,0 +1,83 @@
1
+
2
+ var require = await (async () => {
3
+ var { createRequire } = await import("node:module");
4
+ return createRequire(import.meta.url);
5
+ })();
6
+
7
+ import "../../esm-chunks/chunk-OEQOKJGE.js";
8
+
9
+ // src/build/functions/ipx.ts
10
+ import { cp, mkdir, writeFile } from "fs/promises";
11
+ import { join } from "path";
12
+ import { IPX_HANDLER_NAME } from "../plugin-context.js";
13
+ var sanitizeEdgePath = (imagesPath) => new URL(imagesPath, process.env.URL || "http://n").pathname;
14
+ var getAdjustedImageConfig = (ctx) => {
15
+ return {
16
+ ...ctx.buildConfig.images,
17
+ basePath: [ctx.buildConfig.basePath, IPX_HANDLER_NAME].join("/")
18
+ };
19
+ };
20
+ var createIpxHandler = async (ctx) => {
21
+ await mkdir(ctx.ipxHandlerRootDir, { recursive: true });
22
+ await cp(
23
+ join(ctx.pluginDir, "dist/build/templates/ipx.ts"),
24
+ join(ctx.ipxHandlerRootDir, "_ipx.ts")
25
+ );
26
+ await writeFile(
27
+ join(ctx.ipxHandlerRootDir, "imageconfig.json"),
28
+ JSON.stringify(getAdjustedImageConfig(ctx))
29
+ );
30
+ await writeFile(
31
+ join(ctx.ipxHandlerRootDir, "_ipx.json"),
32
+ JSON.stringify({
33
+ version: 1,
34
+ config: {
35
+ name: "next/image handler",
36
+ generator: `${ctx.pluginName}@${ctx.pluginVersion}`,
37
+ timeout: 120
38
+ }
39
+ })
40
+ );
41
+ ctx.netlifyConfig.redirects.push(
42
+ {
43
+ from: ctx.buildConfig.images.path,
44
+ // eslint-disable-next-line id-length
45
+ query: { url: ":url", w: ":width", q: ":quality" },
46
+ to: `${ctx.buildConfig.basePath}/${IPX_HANDLER_NAME}/w_:width,q_:quality/:url`,
47
+ status: 301
48
+ },
49
+ {
50
+ from: `${ctx.buildConfig.basePath}/${IPX_HANDLER_NAME}/*`,
51
+ to: `/.netlify/builders/${IPX_HANDLER_NAME}`,
52
+ status: 200
53
+ }
54
+ );
55
+ };
56
+ var createIpxEdgeAcceptHandler = async (ctx, netlifyManifest) => {
57
+ await mkdir(ctx.ipxEdgeHandlerRootDir, { recursive: true });
58
+ await cp(
59
+ join(ctx.pluginDir, "dist/build/templates/ipx-edge-accept-handler.ts"),
60
+ join(ctx.ipxEdgeHandlerRootDir, "index.ts")
61
+ );
62
+ await writeFile(
63
+ join(ctx.ipxEdgeHandlerRootDir, "imageconfig.json"),
64
+ JSON.stringify(getAdjustedImageConfig(ctx))
65
+ );
66
+ netlifyManifest.functions.push({
67
+ function: IPX_HANDLER_NAME,
68
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
69
+ // @ts-ignore
70
+ name: "next/image handler",
71
+ path: ctx.buildConfig.images.path ? sanitizeEdgePath(ctx.buildConfig.images.path) : "/_next/image",
72
+ generator: `${ctx.pluginName}@${ctx.pluginVersion}`
73
+ });
74
+ netlifyManifest.layers ??= [];
75
+ netlifyManifest.layers.push({
76
+ name: `https://ipx-edge-function-layer.netlify.app/mod.ts`,
77
+ flag: "ipx-edge-function-layer-url"
78
+ });
79
+ };
80
+ export {
81
+ createIpxEdgeAcceptHandler,
82
+ createIpxHandler
83
+ };
@@ -1528,6 +1528,7 @@ var require_picomatch2 = __commonJS({
1528
1528
 
1529
1529
  // src/build/image-cdn.ts
1530
1530
  var import_picomatch = __toESM(require_picomatch2(), 1);
1531
+ import { createIpxHandler } from "./functions/ipx.js";
1531
1532
  function generateRegexFromPattern(pattern) {
1532
1533
  return (0, import_picomatch.makeRe)(pattern).source;
1533
1534
  }
@@ -1538,67 +1539,71 @@ var setImageConfig = async (ctx) => {
1538
1539
  if (imageLoader !== "default") {
1539
1540
  return;
1540
1541
  }
1541
- ctx.netlifyConfig.redirects.push(
1542
- {
1543
- from: imageEndpointPath,
1544
- // w and q are too short to be used as params with id-length rule
1545
- // but we are forced to do so because of the next/image loader decides on their names
1546
- // eslint-disable-next-line id-length
1547
- query: { url: ":url", w: ":width", q: ":quality" },
1548
- to: "/.netlify/images?url=:url&w=:width&q=:quality",
1549
- status: 200
1550
- },
1551
- // when migrating from @netlify/plugin-nextjs@4 image redirect to ipx might be cached in the browser
1552
- {
1553
- from: "/_ipx/*",
1554
- // w and q are too short to be used as params with id-length rule
1555
- // but we are forced to do so because of the next/image loader decides on their names
1556
- // eslint-disable-next-line id-length
1557
- query: { url: ":url", w: ":width", q: ":quality" },
1558
- to: "/.netlify/images?url=:url&w=:width&q=:quality",
1559
- status: 200
1560
- }
1561
- );
1562
- if (remotePatterns?.length !== 0 || domains?.length !== 0) {
1563
- ctx.netlifyConfig.images ||= { remote_images: [] };
1564
- ctx.netlifyConfig.images.remote_images ||= [];
1565
- if (remotePatterns && remotePatterns.length !== 0) {
1566
- for (const remotePattern of remotePatterns) {
1567
- let { protocol, hostname, port, pathname } = remotePattern;
1568
- if (pathname) {
1569
- pathname = pathname.startsWith("/") ? pathname : `/${pathname}`;
1570
- }
1571
- const combinedRemotePattern = `${protocol ?? "http?(s)"}://${hostname}${port ? `:${port}` : ""}${pathname ?? "/**"}`;
1572
- try {
1573
- ctx.netlifyConfig.images.remote_images.push(
1574
- generateRegexFromPattern(combinedRemotePattern)
1575
- );
1576
- } catch (error) {
1577
- ctx.failBuild(
1578
- `Failed to generate Image CDN remote image regex from Next.js remote pattern: ${JSON.stringify(
1579
- { remotePattern, combinedRemotePattern },
1580
- null,
1581
- 2
1582
- )}`,
1583
- error
1584
- );
1542
+ if (ctx.imageService === "ipx") {
1543
+ await createIpxHandler(ctx);
1544
+ } else {
1545
+ ctx.netlifyConfig.redirects.push(
1546
+ {
1547
+ from: imageEndpointPath,
1548
+ // w and q are too short to be used as params with id-length rule
1549
+ // but we are forced to do so because of the next/image loader decides on their names
1550
+ // eslint-disable-next-line id-length
1551
+ query: { url: ":url", w: ":width", q: ":quality" },
1552
+ to: "/.netlify/images?url=:url&w=:width&q=:quality",
1553
+ status: 200
1554
+ },
1555
+ // when migrating from @netlify/plugin-nextjs@4 image redirect to ipx might be cached in the browser
1556
+ {
1557
+ from: "/_ipx/*",
1558
+ // w and q are too short to be used as params with id-length rule
1559
+ // but we are forced to do so because of the next/image loader decides on their names
1560
+ // eslint-disable-next-line id-length
1561
+ query: { url: ":url", w: ":width", q: ":quality" },
1562
+ to: "/.netlify/images?url=:url&w=:width&q=:quality",
1563
+ status: 200
1564
+ }
1565
+ );
1566
+ if (remotePatterns?.length !== 0 || domains?.length !== 0) {
1567
+ ctx.netlifyConfig.images ||= { remote_images: [] };
1568
+ ctx.netlifyConfig.images.remote_images ||= [];
1569
+ if (remotePatterns && remotePatterns.length !== 0) {
1570
+ for (const remotePattern of remotePatterns) {
1571
+ let { protocol, hostname, port, pathname } = remotePattern;
1572
+ if (pathname) {
1573
+ pathname = pathname.startsWith("/") ? pathname : `/${pathname}`;
1574
+ }
1575
+ const combinedRemotePattern = `${protocol ?? "http?(s)"}://${hostname}${port ? `:${port}` : ""}${pathname ?? "/**"}`;
1576
+ try {
1577
+ ctx.netlifyConfig.images.remote_images.push(
1578
+ generateRegexFromPattern(combinedRemotePattern)
1579
+ );
1580
+ } catch (error) {
1581
+ ctx.failBuild(
1582
+ `Failed to generate Image CDN remote image regex from Next.js remote pattern: ${JSON.stringify(
1583
+ { remotePattern, combinedRemotePattern },
1584
+ null,
1585
+ 2
1586
+ )}`,
1587
+ error
1588
+ );
1589
+ }
1585
1590
  }
1586
1591
  }
1587
- }
1588
- if (domains && domains.length !== 0) {
1589
- for (const domain of domains) {
1590
- const patternFromDomain = `http?(s)://${domain}/**`;
1591
- try {
1592
- ctx.netlifyConfig.images.remote_images.push(generateRegexFromPattern(patternFromDomain));
1593
- } catch (error) {
1594
- ctx.failBuild(
1595
- `Failed to generate Image CDN remote image regex from Next.js domain: ${JSON.stringify(
1596
- { domain, patternFromDomain },
1597
- null,
1598
- 2
1599
- )}`,
1600
- error
1601
- );
1592
+ if (domains && domains.length !== 0) {
1593
+ for (const domain of domains) {
1594
+ const patternFromDomain = `http?(s)://${domain}/**`;
1595
+ try {
1596
+ ctx.netlifyConfig.images.remote_images.push(generateRegexFromPattern(patternFromDomain));
1597
+ } catch (error) {
1598
+ ctx.failBuild(
1599
+ `Failed to generate Image CDN remote image regex from Next.js domain: ${JSON.stringify(
1600
+ { domain, patternFromDomain },
1601
+ null,
1602
+ 2
1603
+ )}`,
1604
+ error
1605
+ );
1606
+ }
1602
1607
  }
1603
1608
  }
1604
1609
  }
@@ -23,6 +23,7 @@ var MODULE_DIR = fileURLToPath(new URL(".", import.meta.url));
23
23
  var PLUGIN_DIR = join(MODULE_DIR, "../..");
24
24
  var DEFAULT_PUBLISH_DIR = ".next";
25
25
  var SERVER_HANDLER_NAME = "___netlify-server-handler";
26
+ var IPX_HANDLER_NAME = "_ipx";
26
27
  var EDGE_HANDLER_NAME = "___netlify-edge-handler";
27
28
  var PluginContext = class {
28
29
  featureFlags;
@@ -116,6 +117,9 @@ var PluginContext = class {
116
117
  const REQUIRED_BUILD_VERSION = ">=29.41.5";
117
118
  return (0, import_semver.satisfies)(this.buildVersion, REQUIRED_BUILD_VERSION, { includePrerelease: true });
118
119
  }
120
+ get imageService() {
121
+ return "ipx";
122
+ }
119
123
  /**
120
124
  * Absolute path of the directory containing the files for the serverless lambda function
121
125
  * `.netlify/functions-internal`
@@ -127,6 +131,10 @@ var PluginContext = class {
127
131
  get serverHandlerRootDir() {
128
132
  return join(this.serverFunctionsDir, SERVER_HANDLER_NAME);
129
133
  }
134
+ /** Absolute path of the ipx handler */
135
+ get ipxHandlerRootDir() {
136
+ return join(this.serverFunctionsDir, IPX_HANDLER_NAME);
137
+ }
130
138
  get serverHandlerDir() {
131
139
  if (this.relativeAppDir.length === 0) {
132
140
  return this.serverHandlerRootDir;
@@ -153,6 +161,10 @@ var PluginContext = class {
153
161
  get edgeHandlerDir() {
154
162
  return join(this.edgeFunctionsDir, EDGE_HANDLER_NAME);
155
163
  }
164
+ /** Absolute path of the ipx edge handler */
165
+ get ipxEdgeHandlerRootDir() {
166
+ return join(this.edgeFunctionsDir, IPX_HANDLER_NAME);
167
+ }
156
168
  constructor(options) {
157
169
  this.constants = options.constants;
158
170
  this.featureFlags = options.featureFlags;
@@ -276,6 +288,7 @@ var PluginContext = class {
276
288
  };
277
289
  export {
278
290
  EDGE_HANDLER_NAME,
291
+ IPX_HANDLER_NAME,
279
292
  PluginContext,
280
293
  SERVER_HANDLER_NAME
281
294
  };
@@ -0,0 +1,13 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-ignore
3
+ import { getHandler } from 'https://ipx-edge-function-layer.netlify.app/mod.ts'
4
+
5
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
6
+ // @ts-ignore Injected at build time
7
+ import imageconfig from './imageconfig.json' assert { type: 'json' }
8
+
9
+ export default getHandler({
10
+ formats: imageconfig?.formats,
11
+ basePath: imageconfig?.basePath,
12
+ imageCDNCompat: true,
13
+ })
@@ -0,0 +1,11 @@
1
+ import { createIPXHandler } from '@netlify/ipx'
2
+
3
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
4
+ // @ts-ignore Injected at build time
5
+ import { basePath, domains, remotePatterns } from './imageconfig.json'
6
+
7
+ export const handler = createIPXHandler({
8
+ basePath,
9
+ domains,
10
+ remotePatterns,
11
+ })
@@ -8,7 +8,7 @@ import "./chunk-OEQOKJGE.js";
8
8
 
9
9
  // package.json
10
10
  var name = "@netlify/plugin-nextjs";
11
- var version = "5.6.0";
11
+ var version = "5.7.0-ipx.0";
12
12
  var description = "Run Next.js seamlessly on Netlify";
13
13
  var main = "./dist/index.js";
14
14
  var type = "module";
@@ -55,6 +55,9 @@ var bugs = {
55
55
  url: "https://github.com/netlify/next-runtime/issues"
56
56
  };
57
57
  var homepage = "https://github.com/netlify/next-runtime#readme";
58
+ var dependencies = {
59
+ "@netlify/ipx": "^1.4.6"
60
+ };
58
61
  var devDependencies = {
59
62
  "@fastly/http-compute-js": "1.1.4",
60
63
  "@netlify/blobs": "^7.3.0",
@@ -104,7 +107,6 @@ var clean_package = {
104
107
  indent: 2,
105
108
  remove: [
106
109
  "clean-package",
107
- "dependencies",
108
110
  "devDependencies",
109
111
  "scripts"
110
112
  ]
@@ -123,6 +125,7 @@ var package_default = {
123
125
  license,
124
126
  bugs,
125
127
  homepage,
128
+ dependencies,
126
129
  devDependencies,
127
130
  "clean-package": clean_package
128
131
  };
@@ -130,6 +133,7 @@ export {
130
133
  bugs,
131
134
  clean_package as "clean-package",
132
135
  package_default as default,
136
+ dependencies,
133
137
  description,
134
138
  devDependencies,
135
139
  engines,
package/dist/index.js CHANGED
@@ -62,7 +62,7 @@ var onBuild = async (options) => {
62
62
  await saveBuildCache(ctx);
63
63
  }
64
64
  if (ctx.buildConfig.output === "export") {
65
- return copyStaticExport(ctx);
65
+ return Promise.all([copyStaticExport(ctx), setImageConfig(ctx)]);
66
66
  }
67
67
  await verifyAdvancedAPIRoutes(ctx);
68
68
  await verifyNetlifyFormsWorkaround(ctx);
@@ -67385,7 +67385,7 @@ var import_semantic_conventions = __toESM(require_src(), 1);
67385
67385
  import { getLogger } from "./request-context.cjs";
67386
67386
  var {
67387
67387
  default: { version, name }
67388
- } = await import("../../esm-chunks/package-OHGYB5OD.js");
67388
+ } = await import("../../esm-chunks/package-O63J727E.js");
67389
67389
  var sdk = new import_sdk_node.NodeSDK({
67390
67390
  resource: new import_resources.Resource({
67391
67391
  [import_semantic_conventions.SEMRESATTRS_SERVICE_NAME]: name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/plugin-nextjs",
3
- "version": "5.6.0",
3
+ "version": "5.7.0-ipx.0",
4
4
  "description": "Run Next.js seamlessly on Netlify",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",
@@ -26,5 +26,8 @@
26
26
  "bugs": {
27
27
  "url": "https://github.com/netlify/next-runtime/issues"
28
28
  },
29
- "homepage": "https://github.com/netlify/next-runtime#readme"
29
+ "homepage": "https://github.com/netlify/next-runtime#readme",
30
+ "dependencies": {
31
+ "@netlify/ipx": "^1.4.6"
32
+ }
30
33
  }