diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 3cfba83..d1ac36a 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -12,13 +12,15 @@ jobs: timeout-minutes: 30 strategy: matrix: - os: [ubuntu-latest, macOS-latest] + os: [ubuntu-latest, macOS-latest, windows-latest] # We should test on 10.13.0 but don't due to a bug in Jest # https://github.com/facebook/jest/issues/9453 node-version: [10.15.0, 14.x] exclude: - os: macOS-latest node-version: 10.15.0 + - os: windows-latest + node-version: 10.15.0 fail-fast: false steps: - name: Git checkout diff --git a/cypress/fixtures/package.json b/cypress/fixtures/package.json index 0f7b11b..51a82df 100644 --- a/cypress/fixtures/package.json +++ b/cypress/fixtures/package.json @@ -1,10 +1,10 @@ { "name": "next-on-netlify-test", "scripts": { - "build": "../../../node_modules/.bin/next build", + "build": "next build", "postbuild": "node ../../../next-on-netlify", - "preview": "../../../node_modules/.bin/netlify dev", + "preview": "netlify dev", "predeploy": "mkdir -p .git", - "deploy": "../../../node_modules/.bin/netlify deploy --json > deployment.json" + "deploy": "netlify deploy --json > deployment.json" } } diff --git a/cypress/plugins/buildProject.js b/cypress/plugins/buildProject.js index 7d3d29d..2fcb4c6 100644 --- a/cypress/plugins/buildProject.js +++ b/cypress/plugins/buildProject.js @@ -1,13 +1,14 @@ const { join } = require("path"); -const { spawnSync } = require("child_process"); +const execa = require("execa"); // Build the given NextJS project const buildProject = ({ project }, config) => { process.stdout.write(`Building project: ${project}...`); // Build project - spawnSync("npm", ["run", "build"], { + execa.sync("npm", ["run", "build"], { cwd: join(config.buildsFolder, project), + preferLocal: true, }); console.log(" Done! ✅"); diff --git a/cypress/plugins/deployProject.js b/cypress/plugins/deployProject.js index 31bf691..bc9ff39 100644 --- a/cypress/plugins/deployProject.js +++ b/cypress/plugins/deployProject.js @@ -1,5 +1,5 @@ const waitOn = require("wait-on"); -const { spawn, spawnSync } = require("child_process"); +const execa = require("execa"); const { join } = require("path"); const getBaseUrl = require("./getBaseUrl"); @@ -10,9 +10,10 @@ const deployLocally = ({ project }, config) => { // Start server. Must start in detached mode, so that we can kill it later. // Otherwise, we seem unable to kill it. // See: https://medium.com/@almenon214/killing-processes-with-node-772ffdd19aad - const server = spawn("npm", ["run", "preview"], { + const server = execa("npm", ["run", "preview"], { cwd: join(config.buildsFolder, project), detached: true, + localDir: true, }); // Set deployment @@ -36,9 +37,9 @@ const deployOnNetlify = ({ project }, config) => { process.stdout.write(`Deploying project: ${project}...`); // Trigger deploy - const deploy = spawnSync("npm", ["run", "deploy"], { + const deploy = execa.sync("npm", ["run", "deploy"], { cwd: join(config.buildsFolder, project), - encoding: "utf-8", + localDir: true, }); // Verify success diff --git a/lib/helpers/getDataRouteForRoute.js b/lib/helpers/getDataRouteForRoute.js index b661a1b..ad6640b 100644 --- a/lib/helpers/getDataRouteForRoute.js +++ b/lib/helpers/getDataRouteForRoute.js @@ -10,8 +10,7 @@ const buildId = fileContents.toString(); // Return the data route for the given route const getDataRouteForRoute = (route) => { const filePath = getFilePathForRoute(route, "json"); - - return join("/_next", "data", buildId, filePath); + return `/_next/data/${buildId}${filePath}`; }; module.exports = getDataRouteForRoute; diff --git a/package-lock.json b/package-lock.json index 6fdea94..ea1d42f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10807,18 +10807,69 @@ "dev": true }, "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + } } }, "executable": { @@ -17540,12 +17591,20 @@ } }, "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "^3.0.0" + }, + "dependencies": { + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + } } }, "npmlog": { @@ -19548,6 +19607,21 @@ "normalize-path": "^2.1.1" } }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, "normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", @@ -19556,6 +19630,15 @@ "requires": { "remove-trailing-separator": "^1.0.1" } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } } } }, @@ -22193,6 +22276,32 @@ "dev": true, "requires": { "execa": "^1.0.0" + }, + "dependencies": { + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + } } }, "winston": { diff --git a/package.json b/package.json index e619d34..8d52ce9 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ }, "devDependencies": { "cypress": "^5.1.0", + "execa": "^4.1.0", "folder-hash": "^3.3.3", "husky": "^4.3.0", "jest": "^26.4.2", diff --git a/tests/customRedirects.test.js b/tests/customRedirects.test.js index c15c02a..74b6997 100644 --- a/tests/customRedirects.test.js +++ b/tests/customRedirects.test.js @@ -1,5 +1,6 @@ // Test next-on-netlify when a custom distDir is set in next.config.js +const { EOL } = require("os"); const { parse, join } = require("path"); const { readFileSync } = require("fs-extra"); const buildNextApp = require("./helpers/buildNextApp"); @@ -40,10 +41,11 @@ describe("Routing", () => { test("includes custom redirect rules", async () => { // Read _redirects file const contents = readFileSync( - join(PROJECT_PATH, "out_publish", "_redirects") + join(PROJECT_PATH, "out_publish", "_redirects"), + "utf8" ); - const redirects = contents.toString().trim().split(/\n/); + const redirects = contents.trim().split(EOL); expect(redirects[0]).toEqual("# Custom Redirect Rules"); expect(redirects[1]).toEqual( "https://old.example.com/* https://new.example.com/:splat 301!" diff --git a/tests/defaults.test.js b/tests/defaults.test.js index 5f39218..3f1126f 100644 --- a/tests/defaults.test.js +++ b/tests/defaults.test.js @@ -1,6 +1,6 @@ // Test default next-on-netlify configuration -const { parse, join } = require("path"); +const { parse, join, sep } = require("path"); const { existsSync, readdirSync, @@ -44,28 +44,32 @@ describe("next-on-netlify", () => { describe("next-on-netlify", () => { test("builds successfully", () => { expect(buildOutput).toMatch("Next on Netlify"); - expect(buildOutput).toMatch("Copying public/ folder to out_publish/"); - expect(buildOutput).toMatch("Copying static NextJS assets to out_publish/"); expect(buildOutput).toMatch( - "Setting up API endpoints as Netlify Functions in out_functions/" + `Copying public${sep} folder to out_publish${sep}` ); expect(buildOutput).toMatch( - "Setting up pages with getInitialProps as Netlify Functions in out_functions/" + `Copying static NextJS assets to out_publish${sep}` ); expect(buildOutput).toMatch( - "Setting up pages with getServerSideProps as Netlify Functions in out_functions/" + `Setting up API endpoints as Netlify Functions in out_functions${sep}` ); expect(buildOutput).toMatch( - "Copying pre-rendered pages with getStaticProps and JSON data to out_publish/" + `Setting up pages with getInitialProps as Netlify Functions in out_functions${sep}` ); expect(buildOutput).toMatch( - "Setting up pages with getStaticProps and fallback: true as Netlify Functions in out_functions/" + `Setting up pages with getServerSideProps as Netlify Functions in out_functions${sep}` ); expect(buildOutput).toMatch( - "Setting up pages with getStaticProps and revalidation interval as Netlify Functions in out_functions/" + `Copying pre-rendered pages with getStaticProps and JSON data to out_publish${sep}` ); expect(buildOutput).toMatch( - "Copying pre-rendered pages without props to out_publish/" + `Setting up pages with getStaticProps and fallback: true as Netlify Functions in out_functions${sep}` + ); + expect(buildOutput).toMatch( + `Setting up pages with getStaticProps and revalidation interval as Netlify Functions in out_functions${sep}` + ); + expect(buildOutput).toMatch( + `Copying pre-rendered pages without props to out_publish${sep}` ); expect(buildOutput).toMatch("Setting up redirects"); expect(buildOutput).toMatch("Success! All done!"); diff --git a/tests/fixtures/package.json b/tests/fixtures/package.json index 8b0135a..c79fdc2 100644 --- a/tests/fixtures/package.json +++ b/tests/fixtures/package.json @@ -1,7 +1,7 @@ { "name": "next-on-netlify-test", "scripts": { - "next-build": "../../../../node_modules/.bin/next build", + "next-build": "next build", "next-on-netlify": "node ../../../next-on-netlify" } } diff --git a/tests/helpers/buildNextApp.js b/tests/helpers/buildNextApp.js index 9fe4d7e..eaee609 100644 --- a/tests/helpers/buildNextApp.js +++ b/tests/helpers/buildNextApp.js @@ -73,7 +73,7 @@ class NextAppBuilder { // cache the result if (!existsSync(this.__cachePath)) { // Build the nextJS app - npmRun("next-build", this.__stagingPath); + await npmRun("next-build", this.__stagingPath); // Cache the build copySync(this.__stagingPath, this.__cachePath); @@ -84,7 +84,7 @@ class NextAppBuilder { copySync(this.__cachePath, this.__appPath); // Run next-on-netlify - const { stdout } = npmRun("next-on-netlify", this.__appPath); + const { stdout } = await npmRun("next-on-netlify", this.__appPath); return stdout; } diff --git a/tests/helpers/npmRun.js b/tests/helpers/npmRun.js index 8d20df5..7ff3e2c 100644 --- a/tests/helpers/npmRun.js +++ b/tests/helpers/npmRun.js @@ -1,21 +1,18 @@ -const { spawnSync } = require("child_process"); +const execa = require("execa"); // Run the given npm command from the given directory -const npmRun = (command, fromDirectory) => { +const npmRun = async (command, fromDirectory) => { // Execute the command - const results = spawnSync("npm", ["run", command], { - cwd: fromDirectory, - encoding: "utf-8", - }); - - // Catch errors - if (results.status != 0) { - console.log(results.stdout); - console.log(results.stderr); - throw `An error occurred during -npm run ${command}- in ${fromDirectory}`; + try { + return await execa("npm", ["run", command], { + cwd: fromDirectory, + preferLocal: true, + }); + } catch (error) { + throw new Error( + `An error occurred during "npm run ${command}" in ${fromDirectory}: ${error.message}` + ); } - - return results; }; module.exports = npmRun;