@netlify/plugin-nextjs 3.9.1 → 4.0.0-beta.3
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/README.md +18 -56
- package/lib/constants.js +21 -0
- package/lib/helpers/cache.js +20 -0
- package/lib/helpers/config.js +115 -0
- package/lib/helpers/files.js +48 -0
- package/lib/helpers/functions.js +64 -0
- package/lib/helpers/verification.js +79 -0
- package/lib/index.js +47 -0
- package/lib/templates/getHandler.js +164 -0
- package/lib/templates/getPageResolver.js +24 -0
- package/lib/templates/ipx.js +8 -0
- package/manifest.yml +1 -1
- package/package.json +46 -53
- package/LICENSE.md +0 -7
- package/helpers/cacheBuild.js +0 -31
- package/helpers/checkNxConfig.js +0 -56
- package/helpers/copyUnstableIncludedDirs.js +0 -27
- package/helpers/doesNotNeedPlugin.js +0 -49
- package/helpers/doesSiteUseNextOnNetlify.js +0 -20
- package/helpers/getNextConfig.js +0 -46
- package/helpers/getNextRoot.js +0 -20
- package/helpers/isStaticExportProject.js +0 -20
- package/helpers/resolveNextModule.js +0 -30
- package/helpers/usesBuildCommand.js +0 -37
- package/helpers/validateNextUsage.js +0 -53
- package/helpers/verifyBuildTarget.js +0 -69
- package/index.js +0 -175
- package/src/index.js +0 -94
- package/src/lib/config.js +0 -58
- package/src/lib/constants/regex.js +0 -9
- package/src/lib/helpers/addDefaultLocaleRedirect.js +0 -25
- package/src/lib/helpers/addLocaleRedirects.js +0 -19
- package/src/lib/helpers/asyncForEach.js +0 -7
- package/src/lib/helpers/convertToBasePathRedirects.js +0 -80
- package/src/lib/helpers/copyDynamicImportChunks.js +0 -39
- package/src/lib/helpers/formatRedirectTarget.js +0 -9
- package/src/lib/helpers/getDataRouteForRoute.js +0 -30
- package/src/lib/helpers/getFilePathForRoute.js +0 -10
- package/src/lib/helpers/getI18n.js +0 -10
- package/src/lib/helpers/getNetlifyFunctionName.js +0 -38
- package/src/lib/helpers/getNetlifyRoutes.js +0 -45
- package/src/lib/helpers/getNextDistDir.js +0 -12
- package/src/lib/helpers/getNextSrcDir.js +0 -5
- package/src/lib/helpers/getPagesManifest.js +0 -19
- package/src/lib/helpers/getPrerenderManifest.js +0 -40
- package/src/lib/helpers/getPreviewModeFunctionName.js +0 -5
- package/src/lib/helpers/getRoutesManifest.js +0 -12
- package/src/lib/helpers/getSortedRedirects.js +0 -37
- package/src/lib/helpers/handleFileTracking.js +0 -61
- package/src/lib/helpers/isApiRoute.js +0 -4
- package/src/lib/helpers/isDynamicRoute.js +0 -6
- package/src/lib/helpers/isFrameworkRoute.js +0 -5
- package/src/lib/helpers/isHtmlFile.js +0 -4
- package/src/lib/helpers/isRootCatchAllRedirect.js +0 -6
- package/src/lib/helpers/isRouteInPrerenderManifest.js +0 -23
- package/src/lib/helpers/isRouteWithDataRoute.js +0 -13
- package/src/lib/helpers/isRouteWithFallback.js +0 -12
- package/src/lib/helpers/logger.js +0 -44
- package/src/lib/helpers/removeFileExtension.js +0 -4
- package/src/lib/helpers/runJobsQueue.js +0 -18
- package/src/lib/helpers/setupNetlifyFunctionForPage.js +0 -44
- package/src/lib/helpers/setupStaticFileForPage.js +0 -18
- package/src/lib/pages/api/pages.js +0 -22
- package/src/lib/pages/api/redirects.js +0 -13
- package/src/lib/pages/api/setup.js +0 -15
- package/src/lib/pages/getInitialProps/pages.js +0 -40
- package/src/lib/pages/getInitialProps/redirects.js +0 -26
- package/src/lib/pages/getInitialProps/setup.js +0 -15
- package/src/lib/pages/getServerSideProps/pages.js +0 -43
- package/src/lib/pages/getServerSideProps/redirects.js +0 -44
- package/src/lib/pages/getServerSideProps/setup.js +0 -15
- package/src/lib/pages/getStaticProps/pages.js +0 -26
- package/src/lib/pages/getStaticProps/redirects.js +0 -70
- package/src/lib/pages/getStaticProps/setup.js +0 -68
- package/src/lib/pages/getStaticPropsWithFallback/pages.js +0 -24
- package/src/lib/pages/getStaticPropsWithFallback/redirects.js +0 -51
- package/src/lib/pages/getStaticPropsWithFallback/setup.js +0 -29
- package/src/lib/pages/getStaticPropsWithRevalidate/pages.js +0 -45
- package/src/lib/pages/getStaticPropsWithRevalidate/redirects.js +0 -55
- package/src/lib/pages/getStaticPropsWithRevalidate/setup.js +0 -40
- package/src/lib/pages/withoutProps/pages.js +0 -27
- package/src/lib/pages/withoutProps/redirects.js +0 -51
- package/src/lib/pages/withoutProps/setup.js +0 -34
- package/src/lib/pages/worker.js +0 -19
- package/src/lib/steps/copyNextAssets.js +0 -31
- package/src/lib/steps/copyPublicFiles.js +0 -18
- package/src/lib/steps/prepareFolders.js +0 -39
- package/src/lib/steps/setupHeaders.js +0 -37
- package/src/lib/steps/setupImageFunction.js +0 -17
- package/src/lib/steps/setupPages.js +0 -25
- package/src/lib/steps/setupRedirects.js +0 -105
- package/src/lib/templates/getHandlerFunction.js +0 -209
- package/src/lib/templates/getTemplate.js +0 -15
- package/src/lib/templates/imageFunction.js +0 -135
- package/src/next-on-netlify.js +0 -22
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
const { join, resolve } = require('path')
|
|
2
|
-
|
|
3
|
-
const { copySync, existsSync } = require('fs-extra')
|
|
4
|
-
|
|
5
|
-
const getNextDistDir = require('../helpers/getNextDistDir')
|
|
6
|
-
const { logTitle } = require('../helpers/logger')
|
|
7
|
-
|
|
8
|
-
// Copy the NextJS static assets from NextJS distDir to Netlify publish folder.
|
|
9
|
-
// These need to be available for NextJS to work.
|
|
10
|
-
|
|
11
|
-
// Note: If you're using a custom build setup, such as a monorepo,
|
|
12
|
-
// you may need to explicitly set distDir in your next.config.js.
|
|
13
|
-
|
|
14
|
-
const copyNextAssets = async (publishPath) => {
|
|
15
|
-
const nextDistDir = await getNextDistDir()
|
|
16
|
-
const staticAssetsPath = join(nextDistDir, 'static')
|
|
17
|
-
if (!existsSync(staticAssetsPath)) {
|
|
18
|
-
throw new Error(
|
|
19
|
-
`No static assets found in your distDir (missing /static in ${resolve(
|
|
20
|
-
nextDistDir,
|
|
21
|
-
)}). Please check your project configuration. Your next.config.js must be one of \`serverless\` or \`experimental-serverless-trace\`. Your build command should include \`next build\`.`,
|
|
22
|
-
)
|
|
23
|
-
}
|
|
24
|
-
logTitle('💼 Copying static NextJS assets to', publishPath)
|
|
25
|
-
copySync(staticAssetsPath, join(publishPath, '_next', 'static'), {
|
|
26
|
-
overwrite: false,
|
|
27
|
-
errorOnExist: true,
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
module.exports = copyNextAssets
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
const { existsSync, copySync } = require('fs-extra')
|
|
2
|
-
|
|
3
|
-
const { PUBLIC_PATH } = require('../config')
|
|
4
|
-
const { logTitle } = require('../helpers/logger')
|
|
5
|
-
|
|
6
|
-
// Copy files from public folder to Netlify publish folder
|
|
7
|
-
const copyPublicFiles = (publishPath) => {
|
|
8
|
-
// Abort if no public/ folder
|
|
9
|
-
if (!existsSync(PUBLIC_PATH)) return
|
|
10
|
-
|
|
11
|
-
// Perform copy operation
|
|
12
|
-
if (publishPath !== PUBLIC_PATH) {
|
|
13
|
-
logTitle('🌍️ Copying', PUBLIC_PATH, 'folder to', publishPath)
|
|
14
|
-
copySync(PUBLIC_PATH, publishPath)
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
module.exports = copyPublicFiles
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
const { join } = require('path')
|
|
2
|
-
|
|
3
|
-
const findCacheDir = require('find-cache-dir')
|
|
4
|
-
const { emptyDirSync } = require('fs-extra')
|
|
5
|
-
|
|
6
|
-
const { NETLIFY_PUBLISH_PATH, NETLIFY_FUNCTIONS_PATH } = require('../config')
|
|
7
|
-
const handleFileTracking = require('../helpers/handleFileTracking')
|
|
8
|
-
const { logTitle, log } = require('../helpers/logger')
|
|
9
|
-
|
|
10
|
-
// Clean existing publish and functions folders
|
|
11
|
-
const prepareFolders = ({ functionsPath, publishPath }) => {
|
|
12
|
-
logTitle('🚀 Next on Netlify 🚀')
|
|
13
|
-
|
|
14
|
-
const isNotConfiguredFunctionsDir = functionsPath === NETLIFY_FUNCTIONS_PATH
|
|
15
|
-
const isNotConfiguredPublishDir = publishPath === NETLIFY_PUBLISH_PATH
|
|
16
|
-
|
|
17
|
-
if (isNotConfiguredFunctionsDir) {
|
|
18
|
-
log(' ', 'Functions directory: ', functionsPath)
|
|
19
|
-
}
|
|
20
|
-
if (isNotConfiguredPublishDir) {
|
|
21
|
-
log(' ', 'Publish directory: ', publishPath)
|
|
22
|
-
}
|
|
23
|
-
if (isNotConfiguredFunctionsDir || isNotConfiguredPublishDir) {
|
|
24
|
-
log(' ', 'Make sure these are set in your netlify.toml file.')
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// We can empty these dirs knowing there will only be stale NoN-generated files inside
|
|
28
|
-
if (isNotConfiguredPublishDir) {
|
|
29
|
-
emptyDirSync(publishPath)
|
|
30
|
-
}
|
|
31
|
-
if (isNotConfiguredFunctionsDir) {
|
|
32
|
-
emptyDirSync(functionsPath)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// This returns a function that runs as the last step of nextOnNetlify()
|
|
36
|
-
return handleFileTracking({ functionsPath, publishPath })
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
module.exports = prepareFolders
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
const { join } = require('path')
|
|
2
|
-
|
|
3
|
-
const { existsSync, readFileSync, writeFileSync } = require('fs-extra')
|
|
4
|
-
|
|
5
|
-
const { CUSTOM_HEADERS_PATH } = require('../config')
|
|
6
|
-
const { logTitle, logItem } = require('../helpers/logger')
|
|
7
|
-
|
|
8
|
-
const indentNewLine = (s) => `\n ${s}`
|
|
9
|
-
|
|
10
|
-
// Setup _headers file to override specific header rules for next assets
|
|
11
|
-
const setupHeaders = (publishPath) => {
|
|
12
|
-
logTitle('🔀 Setting up headers')
|
|
13
|
-
|
|
14
|
-
// Collect custom redirects defined by the user
|
|
15
|
-
const headers = []
|
|
16
|
-
if (existsSync(CUSTOM_HEADERS_PATH)) {
|
|
17
|
-
logItem('# Prepending custom headers')
|
|
18
|
-
headers.push(readFileSync(CUSTOM_HEADERS_PATH, 'utf8'))
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Add NoN section heading
|
|
22
|
-
headers.push('# Next-on-Netlify Headers')
|
|
23
|
-
|
|
24
|
-
// Add rule to override cache control for static chunks
|
|
25
|
-
const staticChunkRule = [
|
|
26
|
-
`/_next/static/chunks/*`,
|
|
27
|
-
indentNewLine(`cache-control: public`),
|
|
28
|
-
indentNewLine(`cache-control: max-age=31536000`),
|
|
29
|
-
indentNewLine(`cache-control: immutable`),
|
|
30
|
-
].join('')
|
|
31
|
-
headers.push(staticChunkRule)
|
|
32
|
-
|
|
33
|
-
// Write redirects to _redirects file
|
|
34
|
-
writeFileSync(join(publishPath, '_headers'), headers.join('\n'))
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
module.exports = setupHeaders
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
const { join } = require('path')
|
|
2
|
-
|
|
3
|
-
const { copyFile, writeJSON, ensureDir } = require('fs-extra')
|
|
4
|
-
|
|
5
|
-
const { NEXT_IMAGE_FUNCTION_NAME, TEMPLATES_DIR } = require('../config')
|
|
6
|
-
|
|
7
|
-
// Move our next/image function into the correct functions directory
|
|
8
|
-
const setupImageFunction = async (functionsPath, imageconfig = {}) => {
|
|
9
|
-
const functionName = `${NEXT_IMAGE_FUNCTION_NAME}.js`
|
|
10
|
-
const functionDirectory = join(functionsPath, NEXT_IMAGE_FUNCTION_NAME)
|
|
11
|
-
|
|
12
|
-
await ensureDir(functionDirectory)
|
|
13
|
-
await writeJSON(join(functionDirectory, 'imageconfig.json'), imageconfig)
|
|
14
|
-
await copyFile(join(TEMPLATES_DIR, 'imageFunction.js'), join(functionDirectory, functionName))
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
module.exports = setupImageFunction
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
const runJobsQueue = require('../helpers/runJobsQueue')
|
|
2
|
-
const apiSetup = require('../pages/api/setup')
|
|
3
|
-
const getInitialPropsSetup = require('../pages/getInitialProps/setup')
|
|
4
|
-
const getServerSidePropsSetup = require('../pages/getServerSideProps/setup')
|
|
5
|
-
const getStaticPropsSetup = require('../pages/getStaticProps/setup')
|
|
6
|
-
const getSPFallbackSetup = require('../pages/getStaticPropsWithFallback/setup')
|
|
7
|
-
const getSPRevalidateSetup = require('../pages/getStaticPropsWithRevalidate/setup')
|
|
8
|
-
const withoutPropsSetup = require('../pages/withoutProps/setup')
|
|
9
|
-
|
|
10
|
-
// Set up all our NextJS pages according to the recipes defined in pages/
|
|
11
|
-
const setupPages = async ({ functionsPath, publishPath }) => {
|
|
12
|
-
const jobs = [
|
|
13
|
-
...(await apiSetup(functionsPath)),
|
|
14
|
-
...(await getInitialPropsSetup(functionsPath)),
|
|
15
|
-
...(await getServerSidePropsSetup(functionsPath)),
|
|
16
|
-
...(await getStaticPropsSetup({ functionsPath, publishPath })),
|
|
17
|
-
...(await getSPFallbackSetup(functionsPath)),
|
|
18
|
-
...(await getSPRevalidateSetup(functionsPath)),
|
|
19
|
-
...(await withoutPropsSetup(publishPath)),
|
|
20
|
-
]
|
|
21
|
-
|
|
22
|
-
return runJobsQueue(jobs)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
module.exports = setupPages
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
const { join } = require('path')
|
|
2
|
-
|
|
3
|
-
const { existsSync, readFileSync, writeFileSync } = require('fs-extra')
|
|
4
|
-
|
|
5
|
-
const getNextConfig = require('../../../helpers/getNextConfig')
|
|
6
|
-
const { CUSTOM_REDIRECTS_PATH, NEXT_IMAGE_FUNCTION_NAME } = require('../config')
|
|
7
|
-
const convertToBasePathRedirects = require('../helpers/convertToBasePathRedirects')
|
|
8
|
-
const formatRedirectTarget = require('../helpers/formatRedirectTarget')
|
|
9
|
-
const getNetlifyRoutes = require('../helpers/getNetlifyRoutes')
|
|
10
|
-
const getSortedRedirects = require('../helpers/getSortedRedirects')
|
|
11
|
-
const isDynamicRoute = require('../helpers/isDynamicRoute')
|
|
12
|
-
const isRootCatchAllRedirect = require('../helpers/isRootCatchAllRedirect')
|
|
13
|
-
const { logTitle, logItem } = require('../helpers/logger')
|
|
14
|
-
const removeFileExtension = require('../helpers/removeFileExtension')
|
|
15
|
-
|
|
16
|
-
// Setup _redirects file that routes all requests to the appropriate location,
|
|
17
|
-
// such as one of the Netlify functions or one of the static files.
|
|
18
|
-
const setupRedirects = async (publishPath) => {
|
|
19
|
-
logTitle('🔀 Setting up redirects')
|
|
20
|
-
|
|
21
|
-
// Collect custom redirects defined by the user
|
|
22
|
-
const redirects = []
|
|
23
|
-
if (existsSync(CUSTOM_REDIRECTS_PATH)) {
|
|
24
|
-
logItem('# Prepending custom redirects')
|
|
25
|
-
redirects.push(readFileSync(CUSTOM_REDIRECTS_PATH, 'utf8'))
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Collect redirects for NextJS pages
|
|
29
|
-
const getApiRedirects = require('../pages/api/redirects')
|
|
30
|
-
const getInitialPropsRedirects = require('../pages/getInitialProps/redirects')
|
|
31
|
-
const getServerSidePropsRedirects = require('../pages/getServerSideProps/redirects')
|
|
32
|
-
const getStaticPropsRedirects = require('../pages/getStaticProps/redirects')
|
|
33
|
-
const getSPFallbackRedirects = require('../pages/getStaticPropsWithFallback/redirects')
|
|
34
|
-
const getSPRevalidateRedirects = require('../pages/getStaticPropsWithRevalidate/redirects')
|
|
35
|
-
const getWithoutPropsRedirects = require('../pages/withoutProps/redirects')
|
|
36
|
-
|
|
37
|
-
let nextRedirects = [
|
|
38
|
-
...(await getApiRedirects()),
|
|
39
|
-
...(await getInitialPropsRedirects()),
|
|
40
|
-
...(await getServerSidePropsRedirects()),
|
|
41
|
-
...(await getStaticPropsRedirects()),
|
|
42
|
-
...(await getSPFallbackRedirects()),
|
|
43
|
-
...(await getSPRevalidateRedirects()),
|
|
44
|
-
...(await getWithoutPropsRedirects()),
|
|
45
|
-
]
|
|
46
|
-
|
|
47
|
-
// Add _redirect section heading
|
|
48
|
-
redirects.push('# Next-on-Netlify Redirects')
|
|
49
|
-
|
|
50
|
-
const { basePath } = await getNextConfig()
|
|
51
|
-
if (basePath && basePath !== '') {
|
|
52
|
-
nextRedirects = convertToBasePathRedirects({ basePath, nextRedirects })
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const staticRedirects = nextRedirects.filter(({ route }) => !isDynamicRoute(removeFileExtension(route)))
|
|
56
|
-
const dynamicRedirects = nextRedirects.filter(({ route }) => isDynamicRoute(removeFileExtension(route)))
|
|
57
|
-
|
|
58
|
-
// Add necessary next/image redirects for our image function
|
|
59
|
-
dynamicRedirects.push({
|
|
60
|
-
route: `${basePath || ''}/_next/image* url=:url w=:width q=:quality`,
|
|
61
|
-
target: `/nextimg/:url/:width/:quality`,
|
|
62
|
-
statusCode: '301',
|
|
63
|
-
force: true,
|
|
64
|
-
})
|
|
65
|
-
dynamicRedirects.push({
|
|
66
|
-
route: '/nextimg/*',
|
|
67
|
-
target: `/.netlify/functions/${NEXT_IMAGE_FUNCTION_NAME}`,
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
const sortedStaticRedirects = getSortedRedirects(staticRedirects)
|
|
71
|
-
const sortedDynamicRedirects = getSortedRedirects(dynamicRedirects)
|
|
72
|
-
|
|
73
|
-
// Assemble redirects for each route
|
|
74
|
-
;[...sortedStaticRedirects, ...sortedDynamicRedirects].forEach((nextRedirect) => {
|
|
75
|
-
// One route may map to multiple Netlify routes: e.g., catch-all pages
|
|
76
|
-
// require two Netlify routes in the _redirects file
|
|
77
|
-
getNetlifyRoutes(nextRedirect.route).forEach((netlifyRoute) => {
|
|
78
|
-
const { conditions = [], force = false, statusCode = '200', target } = nextRedirect
|
|
79
|
-
const redirectPieces = [
|
|
80
|
-
netlifyRoute,
|
|
81
|
-
formatRedirectTarget({ basePath, target }),
|
|
82
|
-
`${statusCode}${force ? '!' : ''}`,
|
|
83
|
-
conditions.join(' '),
|
|
84
|
-
]
|
|
85
|
-
const redirect = redirectPieces.join(' ').trim()
|
|
86
|
-
logItem(redirect)
|
|
87
|
-
redirects.push(redirect)
|
|
88
|
-
})
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
// This takes care of this issue: https://github.com/netlify/next-on-netlify/issues/43
|
|
92
|
-
// where the page chunk for a root level catch-all is served incorrectly to the client.
|
|
93
|
-
// NOTE: Netlify is also investigating this issue internally.
|
|
94
|
-
const hasRootCatchAll = redirects.some(isRootCatchAllRedirect)
|
|
95
|
-
if (hasRootCatchAll) {
|
|
96
|
-
const rootCatchAllIndex = redirects.findIndex(isRootCatchAllRedirect)
|
|
97
|
-
// Add general "no-op" redirect before the root catch-all redirect
|
|
98
|
-
redirects.splice(rootCatchAllIndex, 0, '/_next/* /_next/:splat 200')
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Write redirects to _redirects file
|
|
102
|
-
writeFileSync(join(publishPath, '_redirects'), redirects.join('\n'))
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
module.exports = setupRedirects
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
// TEMPLATE: This file will be copied to the Netlify functions directory
|
|
2
|
-
|
|
3
|
-
// Render function for the Next.js page
|
|
4
|
-
const { Buffer } = require('buffer')
|
|
5
|
-
const http = require('http')
|
|
6
|
-
const queryString = require('querystring')
|
|
7
|
-
const Stream = require('stream')
|
|
8
|
-
|
|
9
|
-
// Mock a HTTP IncomingMessage object from the Netlify Function event parameters
|
|
10
|
-
// Based on API Gateway Lambda Compat
|
|
11
|
-
// Source: https://github.com/serverless-nextjs/serverless-next.js/blob/master/packages/compat-layers/apigw-lambda-compat/lib/compatLayer.js
|
|
12
|
-
|
|
13
|
-
const createRequestObject = ({ event, context }) => {
|
|
14
|
-
const {
|
|
15
|
-
requestContext = {},
|
|
16
|
-
path = '',
|
|
17
|
-
multiValueQueryStringParameters,
|
|
18
|
-
pathParameters,
|
|
19
|
-
httpMethod,
|
|
20
|
-
multiValueHeaders = {},
|
|
21
|
-
body,
|
|
22
|
-
isBase64Encoded,
|
|
23
|
-
} = event
|
|
24
|
-
|
|
25
|
-
const newStream = new Stream.Readable()
|
|
26
|
-
const req = Object.assign(newStream, http.IncomingMessage.prototype)
|
|
27
|
-
req.url = (requestContext.path || path || '').replace(new RegExp(`^/${requestContext.stage}`), '') || '/'
|
|
28
|
-
|
|
29
|
-
let qs = ''
|
|
30
|
-
|
|
31
|
-
if (multiValueQueryStringParameters) {
|
|
32
|
-
qs += queryString.stringify(multiValueQueryStringParameters)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (pathParameters) {
|
|
36
|
-
const pathParametersQs = queryString.stringify(pathParameters)
|
|
37
|
-
|
|
38
|
-
qs += qs.length === 0 ? pathParametersQs : `&${pathParametersQs}`
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const hasQueryString = qs.length !== 0
|
|
42
|
-
|
|
43
|
-
if (hasQueryString) {
|
|
44
|
-
req.url += `?${qs}`
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
req.method = httpMethod
|
|
48
|
-
req.rawHeaders = []
|
|
49
|
-
req.headers = {}
|
|
50
|
-
|
|
51
|
-
// Expose Netlify Function event and callback on request object.
|
|
52
|
-
// This makes it possible to access the clientContext, for example.
|
|
53
|
-
// See: https://github.com/netlify/next-on-netlify/issues/20
|
|
54
|
-
// It also allows users to change the behavior of waiting for empty event
|
|
55
|
-
// loop.
|
|
56
|
-
// See: https://github.com/netlify/next-on-netlify/issues/66#issuecomment-719988804
|
|
57
|
-
req.netlifyFunctionParams = { event, context }
|
|
58
|
-
|
|
59
|
-
for (const key of Object.keys(multiValueHeaders)) {
|
|
60
|
-
for (const value of multiValueHeaders[key]) {
|
|
61
|
-
req.rawHeaders.push(key)
|
|
62
|
-
req.rawHeaders.push(value)
|
|
63
|
-
}
|
|
64
|
-
req.headers[key.toLowerCase()] = multiValueHeaders[key].toString()
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
req.getHeader = (name) => req.headers[name.toLowerCase()]
|
|
68
|
-
req.getHeaders = () => req.headers
|
|
69
|
-
|
|
70
|
-
req.connection = {}
|
|
71
|
-
|
|
72
|
-
if (body) {
|
|
73
|
-
req.push(body, isBase64Encoded ? 'base64' : undefined)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
req.push(null)
|
|
77
|
-
|
|
78
|
-
return req
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Mock a HTTP ServerResponse object that returns a Netlify Function-compatible
|
|
82
|
-
// response via the onResEnd callback when res.end() is called.
|
|
83
|
-
// Based on API Gateway Lambda Compat
|
|
84
|
-
// Source: https://github.com/serverless-nextjs/serverless-next.js/blob/master/packages/compat-layers/apigw-lambda-compat/lib/compatLayer.js
|
|
85
|
-
|
|
86
|
-
const createResponseObject = ({ onResEnd }) => {
|
|
87
|
-
const response = {
|
|
88
|
-
isBase64Encoded: true,
|
|
89
|
-
multiValueHeaders: {},
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const res = new Stream()
|
|
93
|
-
Object.defineProperty(res, 'statusCode', {
|
|
94
|
-
get() {
|
|
95
|
-
return response.statusCode
|
|
96
|
-
},
|
|
97
|
-
set(statusCode) {
|
|
98
|
-
response.statusCode = statusCode
|
|
99
|
-
},
|
|
100
|
-
})
|
|
101
|
-
res.headers = {}
|
|
102
|
-
res.writeHead = (status, headers) => {
|
|
103
|
-
response.statusCode = status
|
|
104
|
-
if (headers) res.headers = Object.assign(res.headers, headers)
|
|
105
|
-
|
|
106
|
-
// Return res object to allow for chaining
|
|
107
|
-
// Fixes: https://github.com/netlify/next-on-netlify/pull/74
|
|
108
|
-
return res
|
|
109
|
-
}
|
|
110
|
-
res.write = (chunk) => {
|
|
111
|
-
if (!response.body) {
|
|
112
|
-
response.body = Buffer.from('')
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
response.body = Buffer.concat([
|
|
116
|
-
Buffer.isBuffer(response.body) ? response.body : Buffer.from(response.body),
|
|
117
|
-
Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk),
|
|
118
|
-
])
|
|
119
|
-
}
|
|
120
|
-
res.setHeader = (name, value) => {
|
|
121
|
-
res.headers[name.toLowerCase()] = value
|
|
122
|
-
}
|
|
123
|
-
res.removeHeader = (name) => {
|
|
124
|
-
delete res.headers[name.toLowerCase()]
|
|
125
|
-
}
|
|
126
|
-
res.getHeader = (name) => res.headers[name.toLowerCase()]
|
|
127
|
-
res.getHeaders = () => res.headers
|
|
128
|
-
res.hasHeader = (name) => Boolean(res.getHeader(name))
|
|
129
|
-
res.end = (text) => {
|
|
130
|
-
if (text) res.write(text)
|
|
131
|
-
if (!res.statusCode) {
|
|
132
|
-
res.statusCode = 200
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (response.body) {
|
|
136
|
-
response.body = Buffer.from(response.body).toString('base64')
|
|
137
|
-
}
|
|
138
|
-
response.multiValueHeaders = res.headers
|
|
139
|
-
res.writeHead(response.statusCode)
|
|
140
|
-
|
|
141
|
-
// Convert all multiValueHeaders into arrays
|
|
142
|
-
for (const key of Object.keys(response.multiValueHeaders)) {
|
|
143
|
-
if (!Array.isArray(response.multiValueHeaders[key])) {
|
|
144
|
-
response.multiValueHeaders[key] = [response.multiValueHeaders[key]]
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
res.finished = true
|
|
149
|
-
res.writableEnded = true
|
|
150
|
-
// Call onResEnd handler with the response object
|
|
151
|
-
onResEnd(response)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return res
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Render the Next.js page
|
|
158
|
-
const renderNextPage = ({ event, context }, nextPage) => {
|
|
159
|
-
// The Next.js page is rendered inside a promise that is resolved when the
|
|
160
|
-
// Next.js page ends the response via `res.end()`
|
|
161
|
-
const promise = new Promise((resolve) => {
|
|
162
|
-
// Create a Next.js-compatible request and response object
|
|
163
|
-
// These mock the ClientRequest and ServerResponse classes from node http
|
|
164
|
-
// See: https://nodejs.org/api/http.html
|
|
165
|
-
const req = createRequestObject({ event, context })
|
|
166
|
-
const res = createResponseObject({
|
|
167
|
-
onResEnd: (response) => resolve(response),
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
// Check if page is a Next.js page or an API route
|
|
171
|
-
const isNextPage = nextPage.render instanceof Function
|
|
172
|
-
const isApiRoute = !isNextPage
|
|
173
|
-
|
|
174
|
-
// Perform the render: render() for Next.js page or default() for API route
|
|
175
|
-
if (isNextPage) return nextPage.render(req, res)
|
|
176
|
-
if (isApiRoute) return nextPage.default(req, res)
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
// Return the promise
|
|
180
|
-
return promise
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const getHandlerFunction = (nextPage) => async (event, context) => {
|
|
184
|
-
// x-forwarded-host is undefined on Netlify for proxied apps that need it
|
|
185
|
-
// fixes https://github.com/netlify/next-on-netlify/issues/46
|
|
186
|
-
if (!event.multiValueHeaders.hasOwnProperty('x-forwarded-host')) {
|
|
187
|
-
// eslint-disable-next-line no-param-reassign
|
|
188
|
-
event.multiValueHeaders['x-forwarded-host'] = [event.headers.host]
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Get the request URL
|
|
192
|
-
const { path } = event
|
|
193
|
-
console.log('[request]', path)
|
|
194
|
-
|
|
195
|
-
// Render the Next.js page
|
|
196
|
-
const response = await renderNextPage({ event, context }, nextPage)
|
|
197
|
-
|
|
198
|
-
// Convert header values to string. Netlify does not support integers as
|
|
199
|
-
// header values. See: https://github.com/netlify/cli/issues/451
|
|
200
|
-
Object.keys(response.multiValueHeaders).forEach((key) => {
|
|
201
|
-
response.multiValueHeaders[key] = response.multiValueHeaders[key].map((value) => String(value))
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
response.multiValueHeaders['Cache-Control'] = ['no-cache']
|
|
205
|
-
|
|
206
|
-
return response
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
exports.getHandlerFunction = getHandlerFunction
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
const getTemplate = ({ filePath, isODB }) => {
|
|
2
|
-
if (isODB) {
|
|
3
|
-
return `// Auto-generated file. DO NOT MODIFY.
|
|
4
|
-
const { getHandlerFunction } = require('./getHandlerFunction')
|
|
5
|
-
const { builder } = require('@netlify/functions')
|
|
6
|
-
exports.handler = builder(getHandlerFunction(require("./nextPage/${filePath}")))
|
|
7
|
-
`
|
|
8
|
-
}
|
|
9
|
-
return `// Auto-generated file. DO NOT MODIFY.
|
|
10
|
-
const { getHandlerFunction } = require('./getHandlerFunction')
|
|
11
|
-
exports.handler = getHandlerFunction(require("./nextPage/${filePath}"))
|
|
12
|
-
`
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
module.exports = getTemplate
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
const { builder } = require('@netlify/functions')
|
|
2
|
-
const etag = require('etag')
|
|
3
|
-
const config = require('./imageconfig.json')
|
|
4
|
-
const imageType = require('image-type')
|
|
5
|
-
const isSvg = require('is-svg')
|
|
6
|
-
const fetch = require('node-fetch')
|
|
7
|
-
const sharp = require('sharp')
|
|
8
|
-
// 6MB is hard max Lambda response size
|
|
9
|
-
const MAX_RESPONSE_SIZE = 6291456
|
|
10
|
-
|
|
11
|
-
function getImageType(buffer) {
|
|
12
|
-
const type = imageType(buffer)
|
|
13
|
-
if (type) {
|
|
14
|
-
return type
|
|
15
|
-
}
|
|
16
|
-
if (isSvg(buffer)) {
|
|
17
|
-
return { ext: 'svg', mime: 'image/svg' }
|
|
18
|
-
}
|
|
19
|
-
return null
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const IGNORED_FORMATS = new Set(['svg', 'gif'])
|
|
23
|
-
const OUTPUT_FORMATS = new Set(['png', 'jpg', 'webp', 'avif'])
|
|
24
|
-
|
|
25
|
-
// Function used to mimic next/image
|
|
26
|
-
const handler = async (event) => {
|
|
27
|
-
const [, , url, w = 500, q = 75] = event.path.split('/')
|
|
28
|
-
// Work-around a bug in redirect handling. Remove when fixed.
|
|
29
|
-
const parsedUrl = decodeURIComponent(url).replace('+', '%20')
|
|
30
|
-
const width = Number.parseInt(w)
|
|
31
|
-
|
|
32
|
-
if (!width) {
|
|
33
|
-
return {
|
|
34
|
-
statusCode: 400,
|
|
35
|
-
body: 'Invalid image parameters',
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const quality = Number.parseInt(q) || 60
|
|
40
|
-
|
|
41
|
-
let imageUrl
|
|
42
|
-
let isRemoteImage = false
|
|
43
|
-
// Relative image
|
|
44
|
-
if (parsedUrl.startsWith('/')) {
|
|
45
|
-
const protocol = event.headers['x-nf-connection-proto'] || event.headers['x-forwarded-proto'] || 'http'
|
|
46
|
-
imageUrl = `${protocol}://${event.headers.host || event.hostname}${parsedUrl}`
|
|
47
|
-
} else {
|
|
48
|
-
isRemoteImage = true
|
|
49
|
-
// Remote images need to be in the allowlist
|
|
50
|
-
let allowedDomains = config.domains || []
|
|
51
|
-
|
|
52
|
-
if (process.env.NEXT_IMAGE_ALLOWED_DOMAINS) {
|
|
53
|
-
console.log('Combining `NEXT_IMAGE_ALLOWED_DOMAINS` with any domains found in `next.config.js`')
|
|
54
|
-
allowedDomains = allowedDomains.concat(
|
|
55
|
-
process.env.NEXT_IMAGE_ALLOWED_DOMAINS.split(',').map((domain) => domain.trim()),
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!allowedDomains.includes(new URL(parsedUrl).hostname)) {
|
|
60
|
-
return {
|
|
61
|
-
statusCode: 403,
|
|
62
|
-
body: 'Image is not from a permitted domain',
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
imageUrl = parsedUrl
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const imageData = await fetch(imageUrl)
|
|
69
|
-
|
|
70
|
-
if (!imageData.ok) {
|
|
71
|
-
console.error(`Failed to download image ${imageUrl}. Status ${imageData.status} ${imageData.statusText}`)
|
|
72
|
-
return {
|
|
73
|
-
statusCode: imageData.status,
|
|
74
|
-
body: imageData.statusText,
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const bufferData = await imageData.buffer()
|
|
79
|
-
|
|
80
|
-
const type = getImageType(bufferData)
|
|
81
|
-
|
|
82
|
-
if (!type) {
|
|
83
|
-
return { statusCode: 400, body: 'Source does not appear to be an image' }
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
let { ext } = type
|
|
87
|
-
|
|
88
|
-
// For unsupported formats (gif, svg) we redirect to the original
|
|
89
|
-
if (IGNORED_FORMATS.has(ext)) {
|
|
90
|
-
return {
|
|
91
|
-
statusCode: 302,
|
|
92
|
-
headers: {
|
|
93
|
-
Location: isRemoteImage ? imageUrl : parsedUrl,
|
|
94
|
-
},
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (process.env.FORCE_WEBP_OUTPUT === 'true' || process.env.FORCE_WEBP_OUTPUT === '1') {
|
|
99
|
-
ext = 'webp'
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (!OUTPUT_FORMATS.has(ext)) {
|
|
103
|
-
ext = 'jpg'
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// The format methods are just to set options: they don't
|
|
107
|
-
// make it return that format.
|
|
108
|
-
const { info, data: imageBuffer } = await sharp(bufferData)
|
|
109
|
-
.rotate()
|
|
110
|
-
.jpeg({ quality, force: ext === 'jpg' })
|
|
111
|
-
.png({ quality, force: ext === 'png' })
|
|
112
|
-
.webp({ quality, force: ext === 'webp' })
|
|
113
|
-
.avif({ quality, force: ext === 'avif' })
|
|
114
|
-
.resize(width, null, { withoutEnlargement: true })
|
|
115
|
-
.toBuffer({ resolveWithObject: true })
|
|
116
|
-
|
|
117
|
-
if (imageBuffer.length > MAX_RESPONSE_SIZE) {
|
|
118
|
-
return {
|
|
119
|
-
statusCode: 400,
|
|
120
|
-
body: 'Requested image is too large. Maximum size is 6MB.',
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
statusCode: 200,
|
|
126
|
-
headers: {
|
|
127
|
-
'Content-Type': `image/${info.format}`,
|
|
128
|
-
etag: etag(imageBuffer),
|
|
129
|
-
},
|
|
130
|
-
body: imageBuffer.toString('base64'),
|
|
131
|
-
isBase64Encoded: true,
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
exports.handler = builder(handler)
|
package/src/next-on-netlify.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const { program } = require('commander')
|
|
3
|
-
|
|
4
|
-
const nextOnNetlify = require('./index')
|
|
5
|
-
|
|
6
|
-
program.option('--max-log-lines [number]', 'lines of build output to show for each section', 50)
|
|
7
|
-
|
|
8
|
-
program
|
|
9
|
-
.command('watch')
|
|
10
|
-
.description('re-runs next-on-netlify on changes')
|
|
11
|
-
.action(async () => {
|
|
12
|
-
await nextOnNetlify({ watch: true })
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
program
|
|
16
|
-
.command('build', { isDefault: true })
|
|
17
|
-
.description('runs next-on-netlify')
|
|
18
|
-
.action(async () => {
|
|
19
|
-
await nextOnNetlify()
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
program.parse(process.argv)
|