diff --git a/.editorconfig b/.editorconfig index a5f2abdd6e..824352beac 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,6 +5,6 @@ indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 -max_line_length = 120 +max_line_length = 100 trim_trailing_whitespace = true insert_final_newline = true diff --git a/.eslintrc.cjs b/.eslintrc.cjs index b4c1322281..fc85df9982 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -6,11 +6,12 @@ module.exports = { sourceType: 'module', }, rules: { + 'arrow-body-style': 'off', 'no-param-reassign': ['error', { props: false }], 'no-underscore-dangle': 'off', + 'no-magic-numbers': 'off', 'n/no-sync': 'off', 'n/prefer-global/process': 'off', - 'no-magic-numbers': 'off', 'unicorn/numeric-separators-style': 'off', 'unicorn/filename-case': ['error', { case: 'kebabCase' }], 'import/no-namespace': 'off', diff --git a/.prettierrc.cjs b/.prettierrc.cjs new file mode 100644 index 0000000000..6b9c1c9904 --- /dev/null +++ b/.prettierrc.cjs @@ -0,0 +1,4 @@ +module.exports = { + ...require('@netlify/eslint-config-node/.prettierrc.json'), + printWidth: 100, +} diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index c63bdb88f0..0000000000 --- a/.prettierrc.json +++ /dev/null @@ -1 +0,0 @@ -"@netlify/eslint-config-node/.prettierrc.json" diff --git a/package-lock.json b/package-lock.json index 248caa977a..d965959bd3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,8 @@ "@netlify/build": "^29.20.6", "@netlify/functions": "^2.0.1", "@vercel/nft": "^0.24.3", - "fs-extra": "^11.1.1" + "fs-extra": "^11.1.1", + "globby": "^13.2.2" }, "devDependencies": { "@netlify/eslint-config-node": "^7.0.1", @@ -17610,7 +17611,7 @@ }, "@fastly/http-compute-js": { "version": "git+ssh://git@github.com/orinokai/http-compute-js.git#ce2bb37c121cfa776d9a3272a6dbb42cb8a9b6b9", - "from": "@fastly/http-compute-js@orinokai/http-compute-js", + "from": "@fastly/http-compute-js@github:orinokai/http-compute-js", "requires": { "buffer": "^6.0.3", "node-inspect-extracted": "^1.1.0", diff --git a/package.json b/package.json index a9399d6d87..39e5ad09c5 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,8 @@ "@netlify/build": "^29.20.6", "@netlify/functions": "^2.0.1", "@vercel/nft": "^0.24.3", - "fs-extra": "^11.1.1" + "fs-extra": "^11.1.1", + "globby": "^13.2.2" }, "devDependencies": { "@netlify/eslint-config-node": "^7.0.1", diff --git a/src/helpers/files.ts b/src/helpers/files.ts index 0e979ad303..386833bc7f 100644 --- a/src/helpers/files.ts +++ b/src/helpers/files.ts @@ -1,23 +1,43 @@ -import { existsSync } from 'node:fs' - import { NetlifyPluginConstants } from '@netlify/build' -import { copySync, moveSync } from 'fs-extra/esm' +import { copy, move, remove } from 'fs-extra/esm' +import { globby } from 'globby' import { BUILD_DIR } from './constants.js' /** * Move the Next.js build output from the publish dir to a temp dir */ -export const stashBuildOutput = ({ PUBLISH_DIR }: NetlifyPluginConstants) => { - moveSync(PUBLISH_DIR, BUILD_DIR, { overwrite: true }) +export const stashBuildOutput = async ({ PUBLISH_DIR }: NetlifyPluginConstants) => { + await move(PUBLISH_DIR, BUILD_DIR, { overwrite: true }) + + // remove prerendered content from the standalone build (it's also in the main build dir) + await Promise.all( + getPrerenderedContent(`${BUILD_DIR}/standalone/`).map((filename: string) => remove(filename)), + ) +} + +/** + * Glob for prerendered content in the build output + */ +const getPrerenderedContent = (cwd: string): string[] => { + // TODO: test this + // return globby('**/*.+(html|json|rsc|body|meta)', { cwd, extglob: true }) + return [] +} + +/** + * Upload prerendered content from the main build dir to the blob store + */ +export const storePrerenderedContent = () => { + // TODO: implement } /** - * Move static assets to the publish dir so they upload to the CDN + * Move static assets to the publish dir so they are uploaded to the CDN */ export const publishStaticAssets = ({ PUBLISH_DIR }: NetlifyPluginConstants) => { - if (existsSync('public')) { - copySync('public', PUBLISH_DIR) - } - copySync(`${BUILD_DIR}/static/`, `${PUBLISH_DIR}/_next/static`) + return Promise.all([ + copy('public', PUBLISH_DIR), + copy(`${BUILD_DIR}/static/`, `${PUBLISH_DIR}/_next/static`), + ]) } diff --git a/src/helpers/functions.ts b/src/helpers/functions.ts index 4457912bbf..69d4c0f1be 100644 --- a/src/helpers/functions.ts +++ b/src/helpers/functions.ts @@ -1,18 +1,19 @@ -import { writeFileSync } from 'fs' +import { writeFile } from 'fs/promises' import { nodeFileTrace } from '@vercel/nft' -import { copySync, emptyDirSync, readJsonSync, writeJSONSync } from 'fs-extra/esm' +import { copy, emptyDir, ensureDir, readJson, writeJSON } from 'fs-extra/esm' import { BUILD_DIR, SERVER_HANDLER_DIR, SERVER_HANDLER_NAME, PLUGIN_DIR } from './constants.js' -const pkg = readJsonSync(`${PLUGIN_DIR}/package.json`) +const pkg = await readJson(`${PLUGIN_DIR}/package.json`) /** * Create a Netlify function to run the Next.js server */ export const createServerHandler = async () => { - // clear the handler directory - emptyDirSync(SERVER_HANDLER_DIR) + // reset the handler directory + await emptyDir(SERVER_HANDLER_DIR) + await ensureDir(`${SERVER_HANDLER_DIR}/node_modules`) // trace the handler dependencies const { fileList } = await nodeFileTrace( @@ -20,32 +21,38 @@ export const createServerHandler = async () => { { base: PLUGIN_DIR, ignore: ['package.json', 'node_modules/next/**'] }, ) - // copy the handler dependencies - fileList.forEach((path) => { - copySync(`${PLUGIN_DIR}/${path}`, `${SERVER_HANDLER_DIR}/${path}`) - }) + await Promise.all( + // copy the handler dependencies + [...fileList].map((path) => copy(`${PLUGIN_DIR}/${path}`, `${SERVER_HANDLER_DIR}/${path}`)), + ) // copy the next.js standalone build output to the handler directory - copySync(`${BUILD_DIR}/standalone/.next`, `${SERVER_HANDLER_DIR}/.next`) - copySync(`${BUILD_DIR}/standalone/node_modules`, `${SERVER_HANDLER_DIR}/node_modules`) + await copy(`${BUILD_DIR}/standalone/.next`, `${SERVER_HANDLER_DIR}/.next`) + await copy(`${BUILD_DIR}/standalone/node_modules`, `${SERVER_HANDLER_DIR}/node_modules`) // create the handler metadata file - writeJSONSync(`${SERVER_HANDLER_DIR}/${SERVER_HANDLER_NAME}.json`, { + await writeJSON(`${SERVER_HANDLER_DIR}/${SERVER_HANDLER_NAME}.json`, { config: { name: 'Next.js Server Handler', generator: `${pkg.name}@${pkg.version}`, nodeBundler: 'none', - includedFiles: [`${SERVER_HANDLER_NAME}*`, 'package.json', 'dist/**', '.next/**', 'node_modules/**'], + includedFiles: [ + `${SERVER_HANDLER_NAME}*`, + 'package.json', + 'dist/**', + '.next/**', + 'node_modules/**', + ], includedFilesBasePath: SERVER_HANDLER_DIR, }, version: 1, }) // configure ESM - writeFileSync(`${SERVER_HANDLER_DIR}/package.json`, JSON.stringify({ type: 'module' })) + await writeFile(`${SERVER_HANDLER_DIR}/package.json`, JSON.stringify({ type: 'module' })) // write the root handler file - writeFileSync( + await writeFile( `${SERVER_HANDLER_DIR}/${SERVER_HANDLER_NAME}.js`, `import handler from './dist/handlers/server.js';export default handler;export const config = {path:'/*'}`, ) diff --git a/src/index.ts b/src/index.ts index 16a16f9115..edcadec1a0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,23 @@ import type { NetlifyPluginOptions } from '@netlify/build' import { setBuildConfig } from './helpers/config.js' -import { publishStaticAssets, stashBuildOutput } from './helpers/files.js' +import { stashBuildOutput, publishStaticAssets, storePrerenderedContent } from './helpers/files.js' import { createServerHandler } from './helpers/functions.js' -type NetlifyPluginOptionsWithFlags = NetlifyPluginOptions & { featureFlags?: Record } +type NetlifyPluginOptionsWithFlags = NetlifyPluginOptions & { + featureFlags?: Record +} export const onPreBuild = () => { setBuildConfig() } export const onBuild = async ({ constants }: NetlifyPluginOptionsWithFlags) => { - stashBuildOutput(constants) - publishStaticAssets(constants) - await createServerHandler() + await stashBuildOutput(constants) + + return Promise.all([ + publishStaticAssets(constants), + storePrerenderedContent(), + createServerHandler(), + ]) }