@netlify/plugin-nextjs 4.0.0-beta.8 → 4.0.0-rc.2
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/constants.js +19 -10
- package/lib/helpers/cache.js +11 -6
- package/lib/helpers/config.js +20 -77
- package/lib/helpers/files.js +221 -42
- package/lib/helpers/functions.js +36 -30
- package/lib/helpers/redirects.js +130 -0
- package/lib/helpers/utils.js +32 -0
- package/lib/helpers/verification.js +70 -40
- package/lib/index.js +39 -29
- package/lib/templates/getHandler.js +38 -94
- package/lib/templates/getPageResolver.js +20 -11
- package/lib/templates/handlerUtils.js +162 -0
- package/lib/templates/ipx.js +11 -7
- package/package.json +14 -12
|
@@ -1,19 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getPageResolver = void 0;
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
const outdent_1 = require("outdent");
|
|
9
|
+
const slash_1 = __importDefault(require("slash"));
|
|
10
|
+
const tiny_glob_1 = __importDefault(require("tiny-glob"));
|
|
11
|
+
const constants_1 = require("../constants");
|
|
6
12
|
// Generate a file full of require.resolve() calls for all the pages in the
|
|
7
13
|
// build. This is used by the nft bundler to find all the pages.
|
|
8
|
-
|
|
9
|
-
const functionDir = resolve(join('.netlify', 'functions', HANDLER_FUNCTION_NAME));
|
|
10
|
-
const root =
|
|
11
|
-
const pages = await
|
|
14
|
+
const getPageResolver = async ({ publish, target }) => {
|
|
15
|
+
const functionDir = path_1.posix.resolve(path_1.posix.join('.netlify', 'functions', constants_1.HANDLER_FUNCTION_NAME));
|
|
16
|
+
const root = path_1.posix.resolve(slash_1.default(publish), target === 'server' ? 'server' : 'serverless', 'pages');
|
|
17
|
+
const pages = await tiny_glob_1.default('**/*.js', {
|
|
12
18
|
cwd: root,
|
|
13
19
|
dot: true,
|
|
14
20
|
});
|
|
15
|
-
const pageFiles = pages
|
|
16
|
-
|
|
21
|
+
const pageFiles = pages
|
|
22
|
+
.map((page) => `require.resolve('${path_1.posix.relative(functionDir, path_1.posix.join(root, slash_1.default(page)))}')`)
|
|
23
|
+
.sort();
|
|
24
|
+
return outdent_1.outdent `
|
|
17
25
|
// This file is purely to allow nft to know about these pages. It should be temporary.
|
|
18
26
|
exports.resolvePages = () => {
|
|
19
27
|
try {
|
|
@@ -22,3 +30,4 @@ exports.getPageResolver = async ({ netlifyConfig, target }) => {
|
|
|
22
30
|
}
|
|
23
31
|
`;
|
|
24
32
|
};
|
|
33
|
+
exports.getPageResolver = getPageResolver;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getNextServer = exports.augmentFsModule = exports.getMultiValueHeaders = exports.getMaxAge = exports.downloadFile = void 0;
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const http_1 = __importDefault(require("http"));
|
|
9
|
+
const https_1 = __importDefault(require("https"));
|
|
10
|
+
const os_1 = require("os");
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const stream_1 = require("stream");
|
|
13
|
+
const util_1 = require("util");
|
|
14
|
+
const streamPipeline = util_1.promisify(stream_1.pipeline);
|
|
15
|
+
const downloadFile = async (url, destination) => {
|
|
16
|
+
console.log(`Downloading ${url} to ${destination}`);
|
|
17
|
+
const httpx = url.startsWith('https') ? https_1.default : http_1.default;
|
|
18
|
+
await new Promise((resolve, reject) => {
|
|
19
|
+
const req = httpx.get(url, { timeout: 10000 }, (response) => {
|
|
20
|
+
if (response.statusCode < 200 || response.statusCode > 299) {
|
|
21
|
+
reject(new Error(`Failed to download ${url}: ${response.statusCode} ${response.statusMessage || ''}`));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const fileStream = fs_1.createWriteStream(destination);
|
|
25
|
+
streamPipeline(response, fileStream)
|
|
26
|
+
.then(resolve)
|
|
27
|
+
.catch((error) => {
|
|
28
|
+
console.log(`Error downloading ${url}`, error);
|
|
29
|
+
reject(error);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
req.on('error', (error) => {
|
|
33
|
+
console.log(`Error downloading ${url}`, error);
|
|
34
|
+
reject(error);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
exports.downloadFile = downloadFile;
|
|
39
|
+
const getMaxAge = (header) => {
|
|
40
|
+
const parts = header.split(',');
|
|
41
|
+
let maxAge;
|
|
42
|
+
for (const part of parts) {
|
|
43
|
+
const [key, value] = part.split('=');
|
|
44
|
+
if ((key === null || key === void 0 ? void 0 : key.trim()) === 's-maxage') {
|
|
45
|
+
maxAge = value === null || value === void 0 ? void 0 : value.trim();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (maxAge) {
|
|
49
|
+
const result = Number.parseInt(maxAge);
|
|
50
|
+
return Number.isNaN(result) ? 0 : result;
|
|
51
|
+
}
|
|
52
|
+
return 0;
|
|
53
|
+
};
|
|
54
|
+
exports.getMaxAge = getMaxAge;
|
|
55
|
+
const getMultiValueHeaders = (headers) => {
|
|
56
|
+
const multiValueHeaders = {};
|
|
57
|
+
for (const key of Object.keys(headers)) {
|
|
58
|
+
const header = headers[key];
|
|
59
|
+
if (Array.isArray(header)) {
|
|
60
|
+
multiValueHeaders[key] = header;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
multiValueHeaders[key] = [header];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return multiValueHeaders;
|
|
67
|
+
};
|
|
68
|
+
exports.getMultiValueHeaders = getMultiValueHeaders;
|
|
69
|
+
const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
|
|
70
|
+
// Only do this if we have some static files moved to the CDN
|
|
71
|
+
if (staticManifest.length === 0) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// These are static page files that have been removed from the function bundle
|
|
75
|
+
// In most cases these are served from the CDN, but for rewrites Next may try to read them
|
|
76
|
+
// from disk. We need to intercept these and load them from the CDN instead
|
|
77
|
+
// Sadly the only way to do this is to monkey-patch fs.promises. Yeah, I know.
|
|
78
|
+
const staticFiles = new Map(staticManifest);
|
|
79
|
+
const downloadPromises = new Map();
|
|
80
|
+
// Yes, you can cache stuff locally in a Lambda
|
|
81
|
+
const cacheDir = path_1.default.join(os_1.tmpdir(), 'next-static-cache');
|
|
82
|
+
// Grab the real fs.promises.readFile...
|
|
83
|
+
const readfileOrig = promises.readFile;
|
|
84
|
+
const statsOrig = promises.stat;
|
|
85
|
+
// ...then money-patch it to see if it's requesting a CDN file
|
|
86
|
+
promises.readFile = (async (file, options) => {
|
|
87
|
+
const base = getBase();
|
|
88
|
+
// We only care about page files
|
|
89
|
+
if (file.startsWith(pageRoot)) {
|
|
90
|
+
// We only want the part after `pages/`
|
|
91
|
+
const filePath = file.slice(pageRoot.length + 1);
|
|
92
|
+
// Is it in the CDN and not local?
|
|
93
|
+
if (staticFiles.has(filePath) && !fs_1.existsSync(file)) {
|
|
94
|
+
// This name is safe to use, because it's one that was already created by Next
|
|
95
|
+
const cacheFile = path_1.default.join(cacheDir, filePath);
|
|
96
|
+
const url = `${base}/${staticFiles.get(filePath)}`;
|
|
97
|
+
// If it's already downloading we can wait for it to finish
|
|
98
|
+
if (downloadPromises.has(url)) {
|
|
99
|
+
await downloadPromises.get(url);
|
|
100
|
+
}
|
|
101
|
+
// Have we already cached it? We download every time if running locally to avoid staleness
|
|
102
|
+
if ((!fs_1.existsSync(cacheFile) || process.env.NETLIFY_DEV) && base) {
|
|
103
|
+
await promises.mkdir(path_1.default.dirname(cacheFile), { recursive: true });
|
|
104
|
+
try {
|
|
105
|
+
// Append the path to our host and we can load it like a regular page
|
|
106
|
+
const downloadPromise = exports.downloadFile(url, cacheFile);
|
|
107
|
+
downloadPromises.set(url, downloadPromise);
|
|
108
|
+
await downloadPromise;
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
downloadPromises.delete(url);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Return the cache file
|
|
115
|
+
return readfileOrig(cacheFile, options);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return readfileOrig(file, options);
|
|
119
|
+
});
|
|
120
|
+
promises.stat = ((file, options) => {
|
|
121
|
+
// We only care about page files
|
|
122
|
+
if (file.startsWith(pageRoot)) {
|
|
123
|
+
// We only want the part after `pages/`
|
|
124
|
+
const cacheFile = path_1.default.join(cacheDir, file.slice(pageRoot.length + 1));
|
|
125
|
+
if (fs_1.existsSync(cacheFile)) {
|
|
126
|
+
return statsOrig(cacheFile, options);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return statsOrig(file, options);
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
exports.augmentFsModule = augmentFsModule;
|
|
133
|
+
const getNextServer = () => {
|
|
134
|
+
let NextServer;
|
|
135
|
+
try {
|
|
136
|
+
// next >= 11.0.1. Yay breaking changes in patch releases!
|
|
137
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
138
|
+
NextServer = require('next/dist/server/next-server').default;
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
if (!error.message.includes("Cannot find module 'next/dist/server/next-server'")) {
|
|
142
|
+
// A different error, so rethrow it
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
// Probably an old version of next
|
|
146
|
+
}
|
|
147
|
+
if (!NextServer) {
|
|
148
|
+
try {
|
|
149
|
+
// next < 11.0.1
|
|
150
|
+
// eslint-disable-next-line node/no-missing-require, import/no-unresolved, @typescript-eslint/no-var-requires
|
|
151
|
+
NextServer = require('next/dist/next-server/server/next-server').default;
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (!error.message.includes("Cannot find module 'next/dist/next-server/server/next-server'")) {
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
throw new Error('Could not find Next.js server');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return NextServer;
|
|
161
|
+
};
|
|
162
|
+
exports.getNextServer = getNextServer;
|
package/lib/templates/ipx.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handler = void 0;
|
|
4
|
+
/* eslint-disable node/no-missing-import, import/no-unresolved, @typescript-eslint/ban-ts-comment */
|
|
5
|
+
const ipx_1 = require("@netlify/ipx");
|
|
6
|
+
// @ts-ignore Injected at build time
|
|
7
|
+
const imageconfig_json_1 = require("./imageconfig.json");
|
|
8
|
+
exports.handler = ipx_1.createIPXHandler({
|
|
9
|
+
basePath: imageconfig_json_1.basePath,
|
|
10
|
+
domains: imageconfig_json_1.domains,
|
|
8
11
|
});
|
|
12
|
+
/* eslint-enable node/no-missing-import, import/no-unresolved, @typescript-eslint/ban-ts-comment */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/plugin-nextjs",
|
|
3
|
-
"version": "4.0.0-
|
|
3
|
+
"version": "4.0.0-rc.2",
|
|
4
4
|
"description": "Run Next.js seamlessly on Netlify",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"files": [
|
|
@@ -8,10 +8,9 @@
|
|
|
8
8
|
"manifest.yml"
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build:demo": "next build
|
|
11
|
+
"build:demo": "next build demos/default",
|
|
12
12
|
"cy:open": "cypress open --config-file cypress/config/all.json",
|
|
13
|
-
"
|
|
14
|
-
"dev:demo": "next dev demo",
|
|
13
|
+
"dev:demo": "next dev demos/default",
|
|
15
14
|
"format": "run-s format:check-fix:*",
|
|
16
15
|
"format:ci": "run-s format:check:*",
|
|
17
16
|
"format:check-fix:lint": "run-e format:check:lint format:fix:lint",
|
|
@@ -26,14 +25,16 @@
|
|
|
26
25
|
"publish:test": "npm test",
|
|
27
26
|
"test": "run-s build build:demo test:jest",
|
|
28
27
|
"test:jest": "jest",
|
|
28
|
+
"test:jest:update": "jest --updateSnapshot",
|
|
29
|
+
"test:update": "run-s build build:demo test:jest:update",
|
|
29
30
|
"prepare": "npm run build",
|
|
30
31
|
"clean": "rimraf lib",
|
|
31
32
|
"build": "tsc",
|
|
32
33
|
"watch": "tsc --watch"
|
|
33
34
|
},
|
|
34
35
|
"config": {
|
|
35
|
-
"eslint": "--cache --format=codeframe --max-warnings=0 \"{src,scripts,tests,.github}/**/*.{js,md,html}\" \"*.{js,md,html}\" \".*.{js,md,html}\"",
|
|
36
|
-
"prettier": "--loglevel=warn \"{src,scripts,tests,.github}/**/*.{js,md,yml,json,html}\" \"*.{js,yml,json,html}\" \".*.{js,yml,json,html}\" \"!package-lock.json\""
|
|
36
|
+
"eslint": "--cache --format=codeframe --max-warnings=0 \"{src,scripts,tests,.github}/**/*.{ts,js,md,html}\" \"*.{ts,js,md,html}\" \".*.{ts,js,md,html}\"",
|
|
37
|
+
"prettier": "--loglevel=warn \"{src,scripts,tests,.github}/**/*.{ts,js,md,yml,json,html}\" \"*.{ts,js,yml,json,html}\" \".*.{ts,js,yml,json,html}\" \"!package-lock.json\""
|
|
37
38
|
},
|
|
38
39
|
"repository": {
|
|
39
40
|
"type": "git",
|
|
@@ -52,8 +53,8 @@
|
|
|
52
53
|
},
|
|
53
54
|
"homepage": "https://github.com/netlify/netlify-plugin-nextjs#readme",
|
|
54
55
|
"dependencies": {
|
|
55
|
-
"@netlify/functions": "^0.
|
|
56
|
-
"@netlify/ipx": "^0.0.
|
|
56
|
+
"@netlify/functions": "^0.10.0",
|
|
57
|
+
"@netlify/ipx": "^0.0.8",
|
|
57
58
|
"@vercel/node": "^1.11.2-canary.4",
|
|
58
59
|
"chalk": "^4.1.2",
|
|
59
60
|
"fs-extra": "^10.0.0",
|
|
@@ -72,16 +73,17 @@
|
|
|
72
73
|
"devDependencies": {
|
|
73
74
|
"@babel/core": "^7.15.8",
|
|
74
75
|
"@babel/preset-env": "^7.15.8",
|
|
75
|
-
"@
|
|
76
|
-
"@netlify/
|
|
76
|
+
"@babel/preset-typescript": "^7.16.0",
|
|
77
|
+
"@netlify/build": "^20.2.0",
|
|
78
|
+
"@netlify/eslint-config-node": "^4.0.0",
|
|
77
79
|
"@testing-library/cypress": "^8.0.1",
|
|
78
80
|
"@types/fs-extra": "^9.0.13",
|
|
79
81
|
"@types/jest": "^27.0.2",
|
|
80
82
|
"@types/mocha": "^9.0.0",
|
|
81
83
|
"babel-jest": "^27.2.5",
|
|
82
84
|
"cpy": "^8.1.2",
|
|
83
|
-
"cypress": "^
|
|
84
|
-
"eslint-config-next": "^
|
|
85
|
+
"cypress": "^9.0.0",
|
|
86
|
+
"eslint-config-next": "^12.0.0",
|
|
85
87
|
"husky": "^4.3.0",
|
|
86
88
|
"jest": "^27.0.0",
|
|
87
89
|
"netlify-plugin-cypress": "^2.2.0",
|