diff --git a/index.js b/index.js index 054b972..f4b464c 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,7 @@ const copyNextAssets = require("./lib/steps/copyNextAssets"); const setupPages = require("./lib/steps/setupPages"); const setupImageFunction = require("./lib/steps/setupImageFunction"); const setupRedirects = require("./lib/steps/setupRedirects"); +const setupHeaders = require("./lib/steps/setupHeaders"); const { NETLIFY_PUBLISH_PATH, NETLIFY_FUNCTIONS_PATH, @@ -30,6 +31,8 @@ const nextOnNetlify = (options = {}) => { setupImageFunction(functionsPath); setupRedirects(publishPath); + + setupHeaders(publishPath); }; module.exports = nextOnNetlify; diff --git a/lib/config.js b/lib/config.js index 096c51d..4edd65e 100644 --- a/lib/config.js +++ b/lib/config.js @@ -28,6 +28,9 @@ const FUNCTION_TEMPLATE_PATH = join(TEMPLATES_DIR, "netlifyFunction.js"); // This is the file where custom redirects can be configured const CUSTOM_REDIRECTS_PATH = join(".", "_redirects"); +// This is the file where custom headers can be configured +const CUSTOM_HEADERS_PATH = join(".", "_headers"); + // This is the name used for copying our imageFunction template and for // creating the next/image redirect const NEXT_IMAGE_FUNCTION_NAME = "next_image"; @@ -41,5 +44,6 @@ module.exports = { TEMPLATES_DIR, FUNCTION_TEMPLATE_PATH, CUSTOM_REDIRECTS_PATH, + CUSTOM_HEADERS_PATH, NEXT_IMAGE_FUNCTION_NAME, }; diff --git a/lib/steps/setupHeaders.js b/lib/steps/setupHeaders.js new file mode 100644 index 0000000..c30319d --- /dev/null +++ b/lib/steps/setupHeaders.js @@ -0,0 +1,33 @@ +const { join } = require("path"); +const { existsSync, readFileSync, writeFileSync } = require("fs-extra"); +const { logTitle, logItem } = require("../helpers/logger"); +const { CUSTOM_HEADERS_PATH } = require("../config"); + +// Setup _headers file to override specific header rules for next assets +const setupHeaders = (publishPath) => { + logTitle("🔀 Setting up headers"); + + // Collect custom redirects defined by the user + const headers = []; + if (existsSync(CUSTOM_HEADERS_PATH)) { + logItem("# Prepending custom headers"); + headers.push(readFileSync(CUSTOM_HEADERS_PATH, "utf8")); + } + + // Add NoN section heading + headers.push("# Next-on-Netlify Headers"); + + // Add rule to override cache control for static chunks + const staticChunkRule = [ + `/_next/static/chunks/*:`, + `\n`, + ` `, + `cache-control: public,max-age=31536000,immutable`, + ].join(""); + headers.push(staticChunkRule); + + // Write redirects to _redirects file + writeFileSync(join(publishPath, "_headers"), headers.join("\n")); +}; + +module.exports = setupHeaders; diff --git a/tests/__snapshots__/defaults.test.js.snap b/tests/__snapshots__/defaults.test.js.snap index 56cba56..70a5665 100644 --- a/tests/__snapshots__/defaults.test.js.snap +++ b/tests/__snapshots__/defaults.test.js.snap @@ -1,5 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Headers creates Netlify headers 1`] = ` +"# Next-on-Netlify Headers +/_next/static/chunks/*: + cache-control: public,max-age=31536000,immutable" +`; + exports[`Routing creates Netlify redirects 1`] = ` "# Next-on-Netlify Redirects / /.netlify/functions/next_index 200 diff --git a/tests/defaults.test.js b/tests/defaults.test.js index 04fbca1..8652214 100644 --- a/tests/defaults.test.js +++ b/tests/defaults.test.js @@ -317,3 +317,16 @@ describe("Routing", () => { expect(redirects).toMatchSnapshot(); }); }); + +describe("Headers", () => { + test("creates Netlify headers", async () => { + // Read _headers file + const contents = readFileSync( + join(PROJECT_PATH, "out_publish", "_headers") + ); + let headers = contents.toString(); + + // Check that headers match + expect(headers).toMatchSnapshot(); + }); +});