From 2b1fd7b4fd3482cc3284891791044142bf35c935 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Fri, 20 Oct 2023 14:03:11 +0200 Subject: [PATCH 01/32] initial wip --- packages/gatsby-adapter-netlify/src/index.ts | 4 +- .../gatsby-adapter-netlify/src/pretty-urls.ts | 53 +++++++++++++++++++ .../src/route-handler.ts | 10 ++++ packages/gatsby/src/utils/adapter/manager.ts | 7 +++ 4 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 packages/gatsby-adapter-netlify/src/pretty-urls.ts diff --git a/packages/gatsby-adapter-netlify/src/index.ts b/packages/gatsby-adapter-netlify/src/index.ts index 65494e7df3dbf..0df65492f8b18 100644 --- a/packages/gatsby-adapter-netlify/src/index.ts +++ b/packages/gatsby-adapter-netlify/src/index.ts @@ -102,8 +102,8 @@ const createNetlifyAdapter: AdapterInit = options => { excludeDatastoreFromEngineFunction, deployURL, supports: { - pathPrefix: false, - trailingSlash: [`always`], + pathPrefix: true, + trailingSlash: [`always`, `never`, `ignore`], }, pluginsToDisable: [ `gatsby-plugin-netlify-cache`, diff --git a/packages/gatsby-adapter-netlify/src/pretty-urls.ts b/packages/gatsby-adapter-netlify/src/pretty-urls.ts new file mode 100644 index 0000000000000..2492ed4cab6c6 --- /dev/null +++ b/packages/gatsby-adapter-netlify/src/pretty-urls.ts @@ -0,0 +1,53 @@ +// import fastq from "fastq" +import fs from "fs-extra" + +function generateFilePathForStaticRoute( + routePath: string, + shouldUsePrettyUrl: boolean +): string { + if (shouldUsePrettyUrl) { + if (routePath.endsWith(`.html`)) { + return routePath + } else if (routePath === `/`) { + return `/index.html` + } else if (routePath.endsWith(`/`)) { + return `${routePath}index.html` + } else { + return `${routePath}.html` + } + } + return routePath +} + +export function createStaticAssetsPathHandler(): any { + const promises: Array> = [] + function ensureStaticAssetPath(filePath: string, routePath: string): string { + const shouldUsePrettyUrl = filePath.endsWith(`.html`) + // if (shouldUsePrettyUrl) { + const expectedPath = `public${generateFilePathForStaticRoute( + routePath, + shouldUsePrettyUrl + )}` + + if (expectedPath !== filePath) { + console.log(`pathDiff`, { + expectedPath, + filePath, + routePath, + }) + const p = fs.move(filePath, expectedPath) + promises.push(p) + } + // } + // + return filePath + } + + const fileMovingDone = (): Promise => + Promise.all(promises).then(() => undefined) + + return { + ensureStaticAssetPath, + fileMovingDone, + } +} diff --git a/packages/gatsby-adapter-netlify/src/route-handler.ts b/packages/gatsby-adapter-netlify/src/route-handler.ts index d9e5a6b6d84a2..bf1c24920204a 100644 --- a/packages/gatsby-adapter-netlify/src/route-handler.ts +++ b/packages/gatsby-adapter-netlify/src/route-handler.ts @@ -3,6 +3,7 @@ import { tmpdir } from "os" import { Transform } from "stream" import { join, basename } from "path" import fs from "fs-extra" +import { createStaticAssetsPathHandler } from "./pretty-urls" const NETLIFY_REDIRECT_KEYWORDS_ALLOWLIST = new Set([ `query`, @@ -137,6 +138,11 @@ export async function handleRoutesManifest( }> { const lambdasThatUseCaching = new Map() + fs.writeFileSync(`test.json`, JSON.stringify(routesManifest, null, 2)) + + const { ensureStaticAssetPath, fileMovingDone } = + createStaticAssetsPathHandler() + let _redirects = `` let _headers = `` for (const route of routesManifest) { @@ -211,6 +217,8 @@ export async function handleRoutesManifest( /^public/, `` )} 200\n` + } else { + ensureStaticAssetPath(route.filePath, fromPath) } _headers += `${encodeURI(fromPath)}\n${route.headers.reduce( @@ -223,6 +231,8 @@ export async function handleRoutesManifest( } } + await fileMovingDone() + await injectEntries(`public/_redirects`, _redirects) await injectEntries(`public/_headers`, _headers) diff --git a/packages/gatsby/src/utils/adapter/manager.ts b/packages/gatsby/src/utils/adapter/manager.ts index 3ea04fdf2e757..4114482901cf8 100644 --- a/packages/gatsby/src/utils/adapter/manager.ts +++ b/packages/gatsby/src/utils/adapter/manager.ts @@ -265,6 +265,9 @@ function getRoutesManifest(): RoutesManifest { const routes: Array = [] const state = store.getState() const createHeaders = createHeadersMatcher(state.config.headers) + const pathPrefix = state.program.prefixPaths + ? state.config.pathPrefix ?? `` + : `` const fileAssets = new Set( globSync(`**/**`, { @@ -309,6 +312,10 @@ function getRoutesManifest(): RoutesManifest { pathToFillInPublicDir: string headers: IHeader["headers"] }): void { + if (pathPrefix && !path.startsWith(pathPrefix)) { + path = posix.join(pathPrefix, path) + } + addRoute({ path, type: `static`, From 089bc945d46d7a3db47c379f1d218661b70e66d4 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Fri, 20 Oct 2023 15:53:56 +0200 Subject: [PATCH 02/32] prefix all the things --- packages/gatsby/src/utils/adapter/manager.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/gatsby/src/utils/adapter/manager.ts b/packages/gatsby/src/utils/adapter/manager.ts index 4114482901cf8..ad80844f41fa2 100644 --- a/packages/gatsby/src/utils/adapter/manager.ts +++ b/packages/gatsby/src/utils/adapter/manager.ts @@ -286,6 +286,10 @@ function getRoutesManifest(): RoutesManifest { route.path = `/${route.path}` } + if (pathPrefix && !route.path.startsWith(pathPrefix)) { + route.path = posix.join(pathPrefix, route.path) + } + // Apply trailing slash behavior unless it's a redirect. Redirects should always be exact matches if (route.type !== `redirect`) { route.path = applyTrailingSlashOption( @@ -312,10 +316,6 @@ function getRoutesManifest(): RoutesManifest { pathToFillInPublicDir: string headers: IHeader["headers"] }): void { - if (pathPrefix && !path.startsWith(pathPrefix)) { - path = posix.join(pathPrefix, path) - } - addRoute({ path, type: `static`, From 8ed2f385598caf2b91dd292d726d37c93cfb8510 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Fri, 20 Oct 2023 16:52:17 +0200 Subject: [PATCH 03/32] lambda handler stripping path prefix --- .../src/schema/graphql-engine/bundle-webpack.ts | 6 ++++++ .../gatsby/src/schema/graphql-engine/entry.ts | 16 +++++++++++++++- .../gatsby/src/utils/page-ssr-module/lambda.ts | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/gatsby/src/schema/graphql-engine/bundle-webpack.ts b/packages/gatsby/src/schema/graphql-engine/bundle-webpack.ts index 8b5d99f4e41c2..22df1ddc6323f 100644 --- a/packages/gatsby/src/schema/graphql-engine/bundle-webpack.ts +++ b/packages/gatsby/src/schema/graphql-engine/bundle-webpack.ts @@ -57,6 +57,11 @@ export async function createGraphqlEngineBundle( require.resolve(`gatsby-plugin-typescript`) ) + const state = store.getState() + const pathPrefix = state.program.prefixPaths + ? state.config.pathPrefix ?? `` + : `` + const compiler = webpack({ name: `Query Engine`, // mode: `production`, @@ -221,6 +226,7 @@ export async function createGraphqlEngineBundle( "process.env.GATSBY_SLICES": JSON.stringify( !!process.env.GATSBY_SLICES ), + __PATH_PREFIX__: JSON.stringify(pathPrefix), }), process.env.GATSBY_WEBPACK_LOGGING?.includes(`query-engine`) && new WebpackLoggingPlugin(rootDir, reporter, isVerbose), diff --git a/packages/gatsby/src/schema/graphql-engine/entry.ts b/packages/gatsby/src/schema/graphql-engine/entry.ts index f64a93df57561..67ae0e161fb92 100644 --- a/packages/gatsby/src/schema/graphql-engine/entry.ts +++ b/packages/gatsby/src/schema/graphql-engine/entry.ts @@ -191,7 +191,21 @@ export class GraphQLEngine { }, } as unknown as IGatsbyState - return findPageByPath(state, pathName, false) + let page = findPageByPath(state, pathName, false) + if (page) { + return page + } + + if ( + typeof __PATH_PREFIX__ === `string` && + __PATH_PREFIX__ && + pathName.startsWith(__PATH_PREFIX__) + ) { + const maybePath = pathName.slice(__PATH_PREFIX__.length) + page = findPageByPath(state, maybePath, false) + } + + return page } } diff --git a/packages/gatsby/src/utils/page-ssr-module/lambda.ts b/packages/gatsby/src/utils/page-ssr-module/lambda.ts index fb4c4f0b61944..f9ad1ccc3cacd 100644 --- a/packages/gatsby/src/utils/page-ssr-module/lambda.ts +++ b/packages/gatsby/src/utils/page-ssr-module/lambda.ts @@ -179,7 +179,7 @@ function getPathInfo(req: GatsbyFunctionRequest): } | undefined { // @ts-ignore GatsbyFunctionRequest.path is not in types ... there is no property in types that can be used to get a path currently - const matches = req.url.matchAll(/^\/?page-data\/(.+)\/page-data.json$/gm) + const matches = req.url.matchAll(/\/?page-data\/(.+)\/page-data.json$/gm) for (const [, requestedPagePath] of matches) { return { isPageData: true, From e01db21c80aaf40c6ce50d6ee04944b0ae7295fc Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Wed, 25 Oct 2023 15:54:15 +0200 Subject: [PATCH 04/32] test: adjust e2e setup to also run variant with path prefix and no trailing slashes --- e2e-tests/adapters/cypress/configs/netlify.ts | 3 +- e2e-tests/adapters/cypress/e2e/basics.cy.ts | 8 ++-- .../adapters/cypress/e2e/client-only.cy.ts | 46 +++++++++++-------- .../adapters/cypress/e2e/redirects.cy.ts | 10 ++-- e2e-tests/adapters/cypress/e2e/ssr.cy.ts | 4 +- e2e-tests/adapters/cypress/support/e2e.ts | 4 +- e2e-tests/adapters/gatsby-config.ts | 6 ++- e2e-tests/adapters/package.json | 10 ++-- .../scripts/deploy-and-run/netlify.mjs | 11 +++-- e2e-tests/adapters/src/pages/404.jsx | 3 +- e2e-tests/adapters/src/pages/500.jsx | 2 +- e2e-tests/adapters/src/pages/index.jsx | 14 ++++-- .../src/pages/routes/sub-router/[...].jsx | 4 +- 13 files changed, 76 insertions(+), 49 deletions(-) diff --git a/e2e-tests/adapters/cypress/configs/netlify.ts b/e2e-tests/adapters/cypress/configs/netlify.ts index f4018879e1d4c..6d0f2a8f216a6 100644 --- a/e2e-tests/adapters/cypress/configs/netlify.ts +++ b/e2e-tests/adapters/cypress/configs/netlify.ts @@ -3,8 +3,7 @@ import { defineConfig } from "cypress" export default defineConfig({ e2e: { baseUrl: process.env.DEPLOY_URL || `http://localhost:8888`, - // Netlify doesn't handle trailing slash behaviors really, so no use in testing it - excludeSpecPattern: [`cypress/e2e/trailing-slash.cy.ts`], + excludeSpecPattern: [], projectId: `4enh4m`, videoUploadOnPasses: false, experimentalRunAllSpecs: true, diff --git a/e2e-tests/adapters/cypress/e2e/basics.cy.ts b/e2e-tests/adapters/cypress/e2e/basics.cy.ts index 28bdb3c31c044..5291b7626405b 100644 --- a/e2e-tests/adapters/cypress/e2e/basics.cy.ts +++ b/e2e-tests/adapters/cypress/e2e/basics.cy.ts @@ -1,9 +1,11 @@ import { title } from "../../constants" +const PATH_PREFIX = Cypress.env(`PATH_PREFIX`) || `` + describe("Basics", () => { beforeEach(() => { - cy.intercept("/gatsby-icon.png").as("static-folder-image") - cy.intercept("/static/astro-**.png", req => { + cy.intercept(PATH_PREFIX + "/gatsby-icon.png").as("static-folder-image") + cy.intercept(PATH_PREFIX + "/static/astro-**.png", req => { req.on("before:response", res => { // this generally should be permamently cached, but that cause problems with intercepting // see https://docs.cypress.io/api/commands/intercept#cyintercept-and-request-caching @@ -41,7 +43,7 @@ describe("Basics", () => { failOnStatusCode: false, }) - cy.get("h1").should("have.text", "Page not found") + cy.get("h1").should("have.text", "Page not found (custom)") }) it("should apply CSS", () => { cy.get(`h1`).should(`have.css`, `color`, `rgb(21, 21, 22)`) diff --git a/e2e-tests/adapters/cypress/e2e/client-only.cy.ts b/e2e-tests/adapters/cypress/e2e/client-only.cy.ts index 58b24efa98cb1..fa323d77c59b3 100644 --- a/e2e-tests/adapters/cypress/e2e/client-only.cy.ts +++ b/e2e-tests/adapters/cypress/e2e/client-only.cy.ts @@ -1,15 +1,15 @@ -Cypress.on('uncaught:exception', (err) => { - if (err.message.includes('Minified React error')) { +Cypress.on("uncaught:exception", err => { + if (err.message.includes("Minified React error")) { return false } }) -describe('Sub-Router', () => { +describe("Sub-Router", () => { const routes = [ { path: "/routes/sub-router", marker: "index", - label: "Index route" + label: "Index route", }, { path: `/routes/sub-router/page/profile`, @@ -51,39 +51,47 @@ describe('Sub-Router', () => { }) }) -describe('Paths', () => { +describe("Paths", () => { const routes = [ { - name: 'client-only', - param: 'dune', + name: "client-only", + param: "dune", }, { - name: 'client-only/wildcard', - param: 'atreides/harkonnen', + name: "client-only/wildcard", + param: "atreides/harkonnen", }, { - name: 'client-only/named-wildcard', - param: 'corinno/fenring', + name: "client-only/named-wildcard", + param: "corinno/fenring", }, ] as const for (const route of routes) { it(`should return "${route.name}" result`, () => { - cy.visit(`/routes/${route.name}${route.param ? `/${route.param}` : ''}`).waitForRouteChange() + cy.visit( + `/routes/${route.name}${route.param ? `/${route.param}` : ""}` + ).waitForRouteChange() cy.get("[data-testid=title]").should("have.text", route.name) cy.get("[data-testid=params]").should("have.text", route.param) }) } }) -describe('Prioritize', () => { - it('should prioritize static page over matchPath page with wildcard', () => { - cy.visit('/routes/client-only/prioritize').waitForRouteChange() - cy.get("[data-testid=title]").should("have.text", "client-only/prioritize static") +describe("Prioritize", () => { + it("should prioritize static page over matchPath page with wildcard", () => { + cy.visit("/routes/client-only/prioritize").waitForRouteChange() + cy.get("[data-testid=title]").should( + "have.text", + "client-only/prioritize static" + ) }) - it('should return result for wildcard on nested prioritized path', () => { - cy.visit('/routes/client-only/prioritize/nested').waitForRouteChange() - cy.get("[data-testid=title]").should("have.text", "client-only/prioritize matchpath") + it("should return result for wildcard on nested prioritized path", () => { + cy.visit("/routes/client-only/prioritize/nested").waitForRouteChange() + cy.get("[data-testid=title]").should( + "have.text", + "client-only/prioritize matchpath" + ) cy.get("[data-testid=params]").should("have.text", "nested") }) }) diff --git a/e2e-tests/adapters/cypress/e2e/redirects.cy.ts b/e2e-tests/adapters/cypress/e2e/redirects.cy.ts index 08d9f4a9f19ac..bb0fea6f5c3d0 100644 --- a/e2e-tests/adapters/cypress/e2e/redirects.cy.ts +++ b/e2e-tests/adapters/cypress/e2e/redirects.cy.ts @@ -7,6 +7,7 @@ Cypress.on("uncaught:exception", err => { }) const TRAILING_SLASH = Cypress.env(`TRAILING_SLASH`) || `never` +const PATH_PREFIX = Cypress.env(`PATH_PREFIX`) || `` // Those tests won't work using `gatsby serve` because it doesn't support redirects @@ -122,7 +123,8 @@ describe("Redirects", () => { cy.location(`pathname`).should( `equal`, - applyTrailingSlashOption(`/routes/redirect/hit`, TRAILING_SLASH) + PATH_PREFIX + + applyTrailingSlashOption(`/routes/redirect/hit`, TRAILING_SLASH) ) cy.location(`hash`).should(`equal`, `#anchor`) cy.location(`search`).should(`equal`, ``) @@ -138,7 +140,8 @@ describe("Redirects", () => { cy.location(`pathname`).should( `equal`, - applyTrailingSlashOption(`/routes/redirect/hit`, TRAILING_SLASH) + PATH_PREFIX + + applyTrailingSlashOption(`/routes/redirect/hit`, TRAILING_SLASH) ) cy.location(`hash`).should(`equal`, ``) cy.location(`search`).should(`equal`, `?query_param=hello`) @@ -154,7 +157,8 @@ describe("Redirects", () => { cy.location(`pathname`).should( `equal`, - applyTrailingSlashOption(`/routes/redirect/hit`, TRAILING_SLASH) + PATH_PREFIX + + applyTrailingSlashOption(`/routes/redirect/hit`, TRAILING_SLASH) ) cy.location(`hash`).should(`equal`, `#anchor`) cy.location(`search`).should(`equal`, `?query_param=hello`) diff --git a/e2e-tests/adapters/cypress/e2e/ssr.cy.ts b/e2e-tests/adapters/cypress/e2e/ssr.cy.ts index 7826ae8ec7c19..5eedf9d93932a 100644 --- a/e2e-tests/adapters/cypress/e2e/ssr.cy.ts +++ b/e2e-tests/adapters/cypress/e2e/ssr.cy.ts @@ -34,6 +34,6 @@ describe("Server Side Rendering (SSR)", () => { cy.location(`pathname`) .should(`equal`, errorPath) .get(`h1`) - .should(`have.text`, `INTERNAL SERVER ERROR`) + .should(`have.text`, `INTERNAL SERVER ERROR (custom)`) }) -}) \ No newline at end of file +}) diff --git a/e2e-tests/adapters/cypress/support/e2e.ts b/e2e-tests/adapters/cypress/support/e2e.ts index 198a0c3b8202b..faadac91bf7f8 100644 --- a/e2e-tests/adapters/cypress/support/e2e.ts +++ b/e2e-tests/adapters/cypress/support/e2e.ts @@ -15,6 +15,8 @@ declare global { } } +const PATH_PREFIX = Cypress.env(`PATH_PREFIX`) || `` + Cypress.Commands.add(`assertRoute`, route => { - cy.url().should(`equal`, `${window.location.origin}${route}`) + cy.url().should(`equal`, `${window.location.origin}${PATH_PREFIX}${route}`) }) diff --git a/e2e-tests/adapters/gatsby-config.ts b/e2e-tests/adapters/gatsby-config.ts index 8ccc03130cc54..8ce4201382735 100644 --- a/e2e-tests/adapters/gatsby-config.ts +++ b/e2e-tests/adapters/gatsby-config.ts @@ -3,7 +3,10 @@ import debugAdapter from "./debug-adapter" import { siteDescription, title } from "./constants" const shouldUseDebugAdapter = process.env.USE_DEBUG_ADAPTER ?? false -const trailingSlash = (process.env.TRAILING_SLASH || `never`) as GatsbyConfig["trailingSlash"] +const trailingSlash = (process.env.TRAILING_SLASH || + `never`) as GatsbyConfig["trailingSlash"] +const pathPrefix = (process.env.PATH_PREFIX || + undefined) as GatsbyConfig["pathPrefix"] let configOverrides: GatsbyConfig = {} @@ -20,6 +23,7 @@ const config: GatsbyConfig = { siteDescription, }, trailingSlash, + pathPrefix, plugins: [], ...configOverrides, } diff --git a/e2e-tests/adapters/package.json b/e2e-tests/adapters/package.json index ce195953c5614..353fee447a662 100644 --- a/e2e-tests/adapters/package.json +++ b/e2e-tests/adapters/package.json @@ -6,19 +6,21 @@ "author": "LekoArts", "scripts": { "develop": "cross-env CYPRESS_SUPPORT=y gatsby develop", - "build": "cross-env CYPRESS_SUPPORT=y gatsby build", + "build": "cross-env CYPRESS_SUPPORT=y gatsby build --prefix-paths", "build:debug": "cross-env USE_DEBUG_ADAPTER=y CYPRESS_SUPPORT=y npm run build", "serve": "gatsby serve", "clean": "gatsby clean", "cy:open": "cypress open --browser chrome --e2e", "develop:debug": "start-server-and-test develop http://localhost:8000 'npm run cy:open -- --config baseUrl=http://localhost:8000'", "ssat:debug": "start-server-and-test serve http://localhost:9000 cy:open", - "test:template": "cross-env-shell CYPRESS_GROUP_NAME=$ADAPTER TRAILING_SLASH=$TRAILING_SLASH node ../../scripts/cypress-run-with-conditional-record-flag.js --browser chrome --e2e --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH", - "test:template:debug": "cross-env-shell CYPRESS_GROUP_NAME=$ADAPTER TRAILING_SLASH=$TRAILING_SLASH npm run cy:open -- --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH", + "test:template": "cross-env-shell CYPRESS_GROUP_NAME=$ADAPTER TRAILING_SLASH=$TRAILING_SLASH node ../../scripts/cypress-run-with-conditional-record-flag.js --browser chrome --e2e --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH --env PATH_PREFIX=$PATH_PREFIX", + "test:template:debug": "cross-env-shell CYPRESS_GROUP_NAME=$ADAPTER TRAILING_SLASH=$TRAILING_SLASH npm run cy:open -- --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH --env PATH_PREFIX=$PATH_PREFIX", "test:debug": "npm-run-all -s build:debug ssat:debug", "test:netlify": "cross-env TRAILING_SLASH=always node scripts/deploy-and-run/netlify.mjs test:template", "test:netlify:debug": "cross-env TRAILING_SLASH=always node scripts/deploy-and-run/netlify.mjs test:template:debug", - "test": "npm-run-all -c -s test:netlify" + "test:netlify:prefix-never": "cross-env TRAILING_SLASH=never PATH_PREFIX=/prefix node scripts/deploy-and-run/netlify.mjs test:template", + "test:netlify:prefix-never:debug": "cross-env TRAILING_SLASH=never PATH_PREFIX=/prefix node scripts/deploy-and-run/netlify.mjs test:template:debug", + "test": "npm-run-all -c -s test:netlify test:netlify:prefix-never" }, "dependencies": { "gatsby": "next", diff --git a/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs b/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs index ed09a06299eac..ed198a9cdb48b 100644 --- a/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs +++ b/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs @@ -1,8 +1,10 @@ // @ts-check - import { execa } from "execa" -process.env.NETLIFY_SITE_ID = process.env.E2E_ADAPTERS_NETLIFY_SITE_ID +// only set NETLIFY_SITE_ID from E2E_ADAPTERS_NETLIFY_SITE_ID if it's set +if (process.env.E2E_ADAPTERS_NETLIFY_SITE_ID) { + process.env.NETLIFY_SITE_ID = process.env.E2E_ADAPTERS_NETLIFY_SITE_ID +} process.env.ADAPTER = "netlify" const deployTitle = process.env.CIRCLE_SHA1 || "N/A" @@ -30,9 +32,10 @@ if (deployResults.exitCode !== 0) { const deployInfo = JSON.parse(deployResults.stdout) -process.env.DEPLOY_URL = deployInfo.deploy_url +const deployUrl = deployInfo.deploy_url + (process.env.PATH_PREFIX ?? ``) +process.env.DEPLOY_URL = deployUrl -console.log(`Deployed to ${deployInfo.deploy_url}`) +console.log(`Deployed to ${deployUrl}`) try { await execa(`npm`, [`run`, npmScriptToRun], { stdio: `inherit` }) diff --git a/e2e-tests/adapters/src/pages/404.jsx b/e2e-tests/adapters/src/pages/404.jsx index a9c4c826920b1..cb52d83131f93 100644 --- a/e2e-tests/adapters/src/pages/404.jsx +++ b/e2e-tests/adapters/src/pages/404.jsx @@ -17,11 +17,10 @@ const paragraphStyles = { marginBottom: 48, } - const NotFoundPage = () => { return (
-

Page not found

+

Page not found (custom)

Sorry 😔, we couldn’t find what you were looking for.
diff --git a/e2e-tests/adapters/src/pages/500.jsx b/e2e-tests/adapters/src/pages/500.jsx index 01ebb0f9c7992..9646578ac9970 100644 --- a/e2e-tests/adapters/src/pages/500.jsx +++ b/e2e-tests/adapters/src/pages/500.jsx @@ -19,7 +19,7 @@ const paragraphStyles = { const InternalServerErrorPage = () => (

-

INTERNAL SERVER ERROR

+

INTERNAL SERVER ERROR (custom)

Go home

diff --git a/e2e-tests/adapters/src/pages/index.jsx b/e2e-tests/adapters/src/pages/index.jsx index a6c5365026e47..9cbcccbe6ac45 100644 --- a/e2e-tests/adapters/src/pages/index.jsx +++ b/e2e-tests/adapters/src/pages/index.jsx @@ -1,5 +1,5 @@ import * as React from "react" -import { Link, graphql } from "gatsby" +import { Link, graphql, withPrefix } from "gatsby" import Layout from "../components/layout" import gatsbyAstronaut from "../images/astro.png" import "./index.css" @@ -8,12 +8,12 @@ const routes = [ { text: "Static", url: "/routes/static", - id: "static-without-slash" + id: "static-without-slash", }, { text: "Static (With Slash)", url: "/routes/static/", - id: "static-with-slash" + id: "static-with-slash", }, { text: "SSR", @@ -38,7 +38,7 @@ const routes = [ { text: "Client-Only Named Wildcard", url: "/routes/client-only/named-wildcard/corinno/fenring", - } + }, ] const functions = [ @@ -67,7 +67,11 @@ const IndexPage = ({ data }) => { Gatsby Astronaut
- Gatsby Monogram Logo + Gatsby Monogram Logo

{data.site.siteMetadata.title}

    diff --git a/e2e-tests/adapters/src/pages/routes/sub-router/[...].jsx b/e2e-tests/adapters/src/pages/routes/sub-router/[...].jsx index 564bc2e801504..04f6056fa5f57 100644 --- a/e2e-tests/adapters/src/pages/routes/sub-router/[...].jsx +++ b/e2e-tests/adapters/src/pages/routes/sub-router/[...].jsx @@ -1,10 +1,10 @@ import * as React from "react" import { Router } from "@reach/router" -import { Link } from "gatsby" +import { Link, withPrefix } from "gatsby" import Layout from "../../../components/layout" const routes = [`/`, `/not-found`, `/page/profile`, `/nested`, `/nested/foo`] -const basePath = `/routes/sub-router` +const basePath = withPrefix(`/routes/sub-router`) const Page = ({ page }) => (
    [client-only-path] {page}
    From 8b3ac9ecc65631599601e53c6f338d68266afe09 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Wed, 25 Oct 2023 15:54:58 +0200 Subject: [PATCH 05/32] chore: use queue for file moving to limit concurrency --- packages/gatsby-adapter-netlify/package.json | 1 + .../gatsby-adapter-netlify/src/pretty-urls.ts | 44 ++++++++++++------- .../src/route-handler.ts | 12 +++-- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/packages/gatsby-adapter-netlify/package.json b/packages/gatsby-adapter-netlify/package.json index 5e576ea04459c..fec2930d5bcc5 100644 --- a/packages/gatsby-adapter-netlify/package.json +++ b/packages/gatsby-adapter-netlify/package.json @@ -35,6 +35,7 @@ "@netlify/cache-utils": "^5.1.5", "@netlify/functions": "^1.6.0", "cookie": "^0.5.0", + "fastq": "^1.15.0", "fs-extra": "^11.1.1" }, "devDependencies": { diff --git a/packages/gatsby-adapter-netlify/src/pretty-urls.ts b/packages/gatsby-adapter-netlify/src/pretty-urls.ts index 2492ed4cab6c6..4ae509466b985 100644 --- a/packages/gatsby-adapter-netlify/src/pretty-urls.ts +++ b/packages/gatsby-adapter-netlify/src/pretty-urls.ts @@ -1,4 +1,4 @@ -// import fastq from "fastq" +import fastq from "fastq" import fs from "fs-extra" function generateFilePathForStaticRoute( @@ -19,32 +19,46 @@ function generateFilePathForStaticRoute( return routePath } -export function createStaticAssetsPathHandler(): any { - const promises: Array> = [] +interface IMoveTask { + from: string + to: string +} + +export function createStaticAssetsPathHandler(): { + ensureStaticAssetPath: (filePath: string, routePath: string) => string + fileMovingDone: () => Promise +} { + const moveQueue = fastq(async (task, cb) => { + try { + await fs.move(task.from, task.to) + cb(null, undefined) + } catch (error) { + cb(error) + } + }, 2) + function ensureStaticAssetPath(filePath: string, routePath: string): string { const shouldUsePrettyUrl = filePath.endsWith(`.html`) - // if (shouldUsePrettyUrl) { + const expectedPath = `public${generateFilePathForStaticRoute( routePath, shouldUsePrettyUrl )}` if (expectedPath !== filePath) { - console.log(`pathDiff`, { - expectedPath, - filePath, - routePath, - }) - const p = fs.move(filePath, expectedPath) - promises.push(p) + moveQueue.push({ from: filePath, to: expectedPath }) } - // } - // return filePath } - const fileMovingDone = (): Promise => - Promise.all(promises).then(() => undefined) + const fileMovingDone = (): Promise => { + if (moveQueue.idle()) { + return Promise.resolve() + } + return new Promise(resolve => { + moveQueue.drain = resolve + }) + } return { ensureStaticAssetPath, diff --git a/packages/gatsby-adapter-netlify/src/route-handler.ts b/packages/gatsby-adapter-netlify/src/route-handler.ts index e4ed3ea4743ef..cbd40caaf6b30 100644 --- a/packages/gatsby-adapter-netlify/src/route-handler.ts +++ b/packages/gatsby-adapter-netlify/src/route-handler.ts @@ -135,6 +135,7 @@ export function processRoutesManifest(routesManifest: RoutesManifest): { redirects: string headers: string lambdasThatUseCaching: Map + fileMovingPromise: Promise } { const lambdasThatUseCaching = new Map() @@ -230,8 +231,12 @@ export function processRoutesManifest(routesManifest: RoutesManifest): { } } - await fileMovingDone() - return { redirects: _redirects, headers: _headers, lambdasThatUseCaching } + return { + redirects: _redirects, + headers: _headers, + lambdasThatUseCaching, + fileMovingPromise: fileMovingDone(), + } } export async function handleRoutesManifest( @@ -239,10 +244,11 @@ export async function handleRoutesManifest( ): Promise<{ lambdasThatUseCaching: Map }> { - const { redirects, headers, lambdasThatUseCaching } = + const { redirects, headers, lambdasThatUseCaching, fileMovingPromise } = processRoutesManifest(routesManifest) await injectEntries(`public/_redirects`, redirects) await injectEntries(`public/_headers`, headers) + await fileMovingPromise return { lambdasThatUseCaching, From 301687d2c97790b8febc428a621dd1ae9a6e320e Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Wed, 25 Oct 2023 15:56:19 +0200 Subject: [PATCH 06/32] tmp: don't clean up deploys while debugging things --- e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs b/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs index ed198a9cdb48b..e7b1e2bc2e4ba 100644 --- a/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs +++ b/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs @@ -40,7 +40,8 @@ console.log(`Deployed to ${deployUrl}`) try { await execa(`npm`, [`run`, npmScriptToRun], { stdio: `inherit` }) } finally { - if (!process.env.GATSBY_TEST_SKIP_CLEANUP) { + // temporaraily disable cleanup for debugging + if (false && !process.env.GATSBY_TEST_SKIP_CLEANUP) { console.log(`Deleting project with deploy_id ${deployInfo.deploy_id}`) const deleteResponse = await execa("ntl", [ From b9d46f4906af0b0e1e26b10a0ebb9205d6e0a8f8 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Wed, 25 Oct 2023 16:00:11 +0200 Subject: [PATCH 07/32] test: added variant variables to deploy title --- e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs b/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs index e7b1e2bc2e4ba..12f9a4b079cea 100644 --- a/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs +++ b/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs @@ -7,7 +7,11 @@ if (process.env.E2E_ADAPTERS_NETLIFY_SITE_ID) { } process.env.ADAPTER = "netlify" -const deployTitle = process.env.CIRCLE_SHA1 || "N/A" +const deployTitle = `${ + process.env.CIRCLE_SHA1 || "N/A commit" +} - trailingSlash:${process.env.TRAILING_SLASH || `always`} / pathPrefix:${ + process.env.PATH_PREFIX || `-` +}` const npmScriptToRun = process.argv[2] || "test:netlify" From 7bd9e4653c375b0df4a60aed4ad561285df93636 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Wed, 25 Oct 2023 16:28:19 +0200 Subject: [PATCH 08/32] fix TS --- packages/gatsby/src/schema/graphql-engine/entry.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/gatsby/src/schema/graphql-engine/entry.ts b/packages/gatsby/src/schema/graphql-engine/entry.ts index 67ae0e161fb92..ded2de7de1313 100644 --- a/packages/gatsby/src/schema/graphql-engine/entry.ts +++ b/packages/gatsby/src/schema/graphql-engine/entry.ts @@ -21,10 +21,13 @@ import { gatsbyNodes, gatsbyWorkers, flattenedPlugins, - // @ts-ignore + // @ts-ignore .cache/query-engine-plugins is generated before bundling } from ".cache/query-engine-plugins" import { initTracer } from "../../utils/tracer" +// @ts-ignore __PATH_PREFIX__ is injected by webpack +const PathPrefix = __PATH_PREFIX__ + type MaybePhantomActivity = | ReturnType | undefined @@ -197,11 +200,11 @@ export class GraphQLEngine { } if ( - typeof __PATH_PREFIX__ === `string` && - __PATH_PREFIX__ && - pathName.startsWith(__PATH_PREFIX__) + typeof PathPrefix === `string` && + PathPrefix && + pathName.startsWith(PathPrefix) ) { - const maybePath = pathName.slice(__PATH_PREFIX__.length) + const maybePath = pathName.slice(PathPrefix.length) page = findPageByPath(state, maybePath, false) } From 558406980781f7b9147b163e74e0d793828be46f Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Wed, 25 Oct 2023 16:49:39 +0200 Subject: [PATCH 09/32] fix unit tests --- packages/gatsby/src/utils/adapter/__tests__/fixtures/state.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/gatsby/src/utils/adapter/__tests__/fixtures/state.ts b/packages/gatsby/src/utils/adapter/__tests__/fixtures/state.ts index f4aef674d18d8..d24966a853ad1 100644 --- a/packages/gatsby/src/utils/adapter/__tests__/fixtures/state.ts +++ b/packages/gatsby/src/utils/adapter/__tests__/fixtures/state.ts @@ -169,4 +169,5 @@ export const state = { slices, html, components, + program: {} } as unknown as IGatsbyState From c011592c3800d2a9e313067c4ffe9410f5715be0 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Wed, 25 Oct 2023 18:04:39 +0200 Subject: [PATCH 10/32] try different cypress group --- e2e-tests/adapters/package.json | 4 ++-- e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/e2e-tests/adapters/package.json b/e2e-tests/adapters/package.json index 353fee447a662..461785520fe05 100644 --- a/e2e-tests/adapters/package.json +++ b/e2e-tests/adapters/package.json @@ -13,8 +13,8 @@ "cy:open": "cypress open --browser chrome --e2e", "develop:debug": "start-server-and-test develop http://localhost:8000 'npm run cy:open -- --config baseUrl=http://localhost:8000'", "ssat:debug": "start-server-and-test serve http://localhost:9000 cy:open", - "test:template": "cross-env-shell CYPRESS_GROUP_NAME=$ADAPTER TRAILING_SLASH=$TRAILING_SLASH node ../../scripts/cypress-run-with-conditional-record-flag.js --browser chrome --e2e --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH --env PATH_PREFIX=$PATH_PREFIX", - "test:template:debug": "cross-env-shell CYPRESS_GROUP_NAME=$ADAPTER TRAILING_SLASH=$TRAILING_SLASH npm run cy:open -- --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH --env PATH_PREFIX=$PATH_PREFIX", + "test:template": "cross-env-shell CYPRESS_GROUP_NAME=\"$ADAPTER - trailingSlash:${$TRAILING_SLASH:-always} / pathPrefix:${$PATH_PREFIX:--}\" TRAILING_SLASH=$TRAILING_SLASH PATH_PREFIX=$PATH_PREFIX node ../../scripts/cypress-run-with-conditional-record-flag.js --browser chrome --e2e --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH --env PATH_PREFIX=$PATH_PREFIX", + "test:template:debug": "cross-env-shell CYPRESS_GROUP_NAME=\"$ADAPTER - trailingSlash:${$TRAILING_SLASH:-always} / pathPrefix:${$PATH_PREFIX:--}\" TRAILING_SLASH=$TRAILING_SLASH PATH_PREFIX=$PATH_PREFIX npm run cy:open -- --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH --env PATH_PREFIX=$PATH_PREFIX", "test:debug": "npm-run-all -s build:debug ssat:debug", "test:netlify": "cross-env TRAILING_SLASH=always node scripts/deploy-and-run/netlify.mjs test:template", "test:netlify:debug": "cross-env TRAILING_SLASH=always node scripts/deploy-and-run/netlify.mjs test:template:debug", diff --git a/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs b/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs index 12f9a4b079cea..24ba4aec54fe3 100644 --- a/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs +++ b/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs @@ -15,6 +15,9 @@ const deployTitle = `${ const npmScriptToRun = process.argv[2] || "test:netlify" +// ensure clean build +await execa(`npm`, [`run`, `clean`], { stdio: `inherit` }) + const deployResults = await execa( "ntl", ["deploy", "--build", "--json", "--message", deployTitle], From f9a0080cdf117f7d1c66a6444b8269529c6bed5f Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Wed, 25 Oct 2023 18:44:57 +0200 Subject: [PATCH 11/32] try different cypress group2 --- e2e-tests/adapters/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e-tests/adapters/package.json b/e2e-tests/adapters/package.json index 461785520fe05..aba6e21fd001b 100644 --- a/e2e-tests/adapters/package.json +++ b/e2e-tests/adapters/package.json @@ -13,8 +13,8 @@ "cy:open": "cypress open --browser chrome --e2e", "develop:debug": "start-server-and-test develop http://localhost:8000 'npm run cy:open -- --config baseUrl=http://localhost:8000'", "ssat:debug": "start-server-and-test serve http://localhost:9000 cy:open", - "test:template": "cross-env-shell CYPRESS_GROUP_NAME=\"$ADAPTER - trailingSlash:${$TRAILING_SLASH:-always} / pathPrefix:${$PATH_PREFIX:--}\" TRAILING_SLASH=$TRAILING_SLASH PATH_PREFIX=$PATH_PREFIX node ../../scripts/cypress-run-with-conditional-record-flag.js --browser chrome --e2e --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH --env PATH_PREFIX=$PATH_PREFIX", - "test:template:debug": "cross-env-shell CYPRESS_GROUP_NAME=\"$ADAPTER - trailingSlash:${$TRAILING_SLASH:-always} / pathPrefix:${$PATH_PREFIX:--}\" TRAILING_SLASH=$TRAILING_SLASH PATH_PREFIX=$PATH_PREFIX npm run cy:open -- --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH --env PATH_PREFIX=$PATH_PREFIX", + "test:template": "cross-env-shell CYPRESS_GROUP_NAME=\"adapter:$ADAPTER / trailingSlash:${TRAILING_SLASH:-always} / pathPrefix:${PATH_PREFIX:--}\" TRAILING_SLASH=$TRAILING_SLASH PATH_PREFIX=$PATH_PREFIX node ../../scripts/cypress-run-with-conditional-record-flag.js --browser chrome --e2e --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH --env PATH_PREFIX=$PATH_PREFIX", + "test:template:debug": "cross-env-shell CYPRESS_GROUP_NAME=\"adapter:$ADAPTER / trailingSlash:${TRAILING_SLASH:-always} / pathPrefix:${PATH_PREFIX:--}\" TRAILING_SLASH=$TRAILING_SLASH PATH_PREFIX=$PATH_PREFIX npm run cy:open -- --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH --env PATH_PREFIX=$PATH_PREFIX", "test:debug": "npm-run-all -s build:debug ssat:debug", "test:netlify": "cross-env TRAILING_SLASH=always node scripts/deploy-and-run/netlify.mjs test:template", "test:netlify:debug": "cross-env TRAILING_SLASH=always node scripts/deploy-and-run/netlify.mjs test:template:debug", From f4cbebb2a18b10bfc76e4c6dc381e45ca87436a7 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Thu, 26 Oct 2023 09:41:54 +0200 Subject: [PATCH 12/32] maybe fix passing cypress env? --- e2e-tests/adapters/package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/e2e-tests/adapters/package.json b/e2e-tests/adapters/package.json index aba6e21fd001b..c3f4911265acd 100644 --- a/e2e-tests/adapters/package.json +++ b/e2e-tests/adapters/package.json @@ -13,8 +13,8 @@ "cy:open": "cypress open --browser chrome --e2e", "develop:debug": "start-server-and-test develop http://localhost:8000 'npm run cy:open -- --config baseUrl=http://localhost:8000'", "ssat:debug": "start-server-and-test serve http://localhost:9000 cy:open", - "test:template": "cross-env-shell CYPRESS_GROUP_NAME=\"adapter:$ADAPTER / trailingSlash:${TRAILING_SLASH:-always} / pathPrefix:${PATH_PREFIX:--}\" TRAILING_SLASH=$TRAILING_SLASH PATH_PREFIX=$PATH_PREFIX node ../../scripts/cypress-run-with-conditional-record-flag.js --browser chrome --e2e --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH --env PATH_PREFIX=$PATH_PREFIX", - "test:template:debug": "cross-env-shell CYPRESS_GROUP_NAME=\"adapter:$ADAPTER / trailingSlash:${TRAILING_SLASH:-always} / pathPrefix:${PATH_PREFIX:--}\" TRAILING_SLASH=$TRAILING_SLASH PATH_PREFIX=$PATH_PREFIX npm run cy:open -- --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH --env PATH_PREFIX=$PATH_PREFIX", + "test:template": "cross-env-shell CYPRESS_GROUP_NAME=\"adapter:$ADAPTER / trailingSlash:${TRAILING_SLASH:-always} / pathPrefix:${PATH_PREFIX:--}\" TRAILING_SLASH=$TRAILING_SLASH PATH_PREFIX=$PATH_PREFIX node ../../scripts/cypress-run-with-conditional-record-flag.js --browser chrome --e2e --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH,PATH_PREFIX=$PATH_PREFIX", + "test:template:debug": "cross-env-shell CYPRESS_GROUP_NAME=\"adapter:$ADAPTER / trailingSlash:${TRAILING_SLASH:-always} / pathPrefix:${PATH_PREFIX:--}\" TRAILING_SLASH=$TRAILING_SLASH PATH_PREFIX=$PATH_PREFIX npm run cy:open -- --config-file \"cypress/configs/$ADAPTER.ts\" --env TRAILING_SLASH=$TRAILING_SLASH,PATH_PREFIX=$PATH_PREFIX", "test:debug": "npm-run-all -s build:debug ssat:debug", "test:netlify": "cross-env TRAILING_SLASH=always node scripts/deploy-and-run/netlify.mjs test:template", "test:netlify:debug": "cross-env TRAILING_SLASH=always node scripts/deploy-and-run/netlify.mjs test:template:debug", @@ -31,6 +31,7 @@ "devDependencies": { "cross-env": "^7.0.3", "cypress": "^12.14.0", + "dotenv": "^8.6.0", "gatsby-cypress": "^3.11.0", "netlify-cli": "^15.8.0", "npm-run-all": "^4.1.5", From 12384e6ff4b2143c03f114dcd7da6a0c8b11e59c Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Thu, 26 Oct 2023 13:50:48 +0200 Subject: [PATCH 13/32] fix ssr path_prefix --- .../schema/graphql-engine/bundle-webpack.ts | 6 -- .../gatsby/src/schema/graphql-engine/entry.ts | 21 +------ .../utils/page-ssr-module/bundle-webpack.ts | 17 ++++-- .../src/utils/page-ssr-module/lambda.ts | 58 +++++++++++++++---- 4 files changed, 59 insertions(+), 43 deletions(-) diff --git a/packages/gatsby/src/schema/graphql-engine/bundle-webpack.ts b/packages/gatsby/src/schema/graphql-engine/bundle-webpack.ts index 22df1ddc6323f..8b5d99f4e41c2 100644 --- a/packages/gatsby/src/schema/graphql-engine/bundle-webpack.ts +++ b/packages/gatsby/src/schema/graphql-engine/bundle-webpack.ts @@ -57,11 +57,6 @@ export async function createGraphqlEngineBundle( require.resolve(`gatsby-plugin-typescript`) ) - const state = store.getState() - const pathPrefix = state.program.prefixPaths - ? state.config.pathPrefix ?? `` - : `` - const compiler = webpack({ name: `Query Engine`, // mode: `production`, @@ -226,7 +221,6 @@ export async function createGraphqlEngineBundle( "process.env.GATSBY_SLICES": JSON.stringify( !!process.env.GATSBY_SLICES ), - __PATH_PREFIX__: JSON.stringify(pathPrefix), }), process.env.GATSBY_WEBPACK_LOGGING?.includes(`query-engine`) && new WebpackLoggingPlugin(rootDir, reporter, isVerbose), diff --git a/packages/gatsby/src/schema/graphql-engine/entry.ts b/packages/gatsby/src/schema/graphql-engine/entry.ts index ded2de7de1313..f64a93df57561 100644 --- a/packages/gatsby/src/schema/graphql-engine/entry.ts +++ b/packages/gatsby/src/schema/graphql-engine/entry.ts @@ -21,13 +21,10 @@ import { gatsbyNodes, gatsbyWorkers, flattenedPlugins, - // @ts-ignore .cache/query-engine-plugins is generated before bundling + // @ts-ignore } from ".cache/query-engine-plugins" import { initTracer } from "../../utils/tracer" -// @ts-ignore __PATH_PREFIX__ is injected by webpack -const PathPrefix = __PATH_PREFIX__ - type MaybePhantomActivity = | ReturnType | undefined @@ -194,21 +191,7 @@ export class GraphQLEngine { }, } as unknown as IGatsbyState - let page = findPageByPath(state, pathName, false) - if (page) { - return page - } - - if ( - typeof PathPrefix === `string` && - PathPrefix && - pathName.startsWith(PathPrefix) - ) { - const maybePath = pathName.slice(PathPrefix.length) - page = findPageByPath(state, maybePath, false) - } - - return page + return findPageByPath(state, pathName, false) } } diff --git a/packages/gatsby/src/utils/page-ssr-module/bundle-webpack.ts b/packages/gatsby/src/utils/page-ssr-module/bundle-webpack.ts index b9f2f612857ec..7e98eca469cf6 100644 --- a/packages/gatsby/src/utils/page-ssr-module/bundle-webpack.ts +++ b/packages/gatsby/src/utils/page-ssr-module/bundle-webpack.ts @@ -77,6 +77,9 @@ export async function createPageSSRBundle({ isVerbose?: boolean }): Promise { const state = store.getState() + const pathPrefix = state.program.prefixPaths + ? state.config.pathPrefix ?? `` + : `` const slicesStateObject = {} for (const [key, value] of state.slices) { slicesStateObject[key] = value @@ -224,12 +227,14 @@ export async function createPageSSRBundle({ `utf-8` ) - functionCode = functionCode.replace( - `%CDN_DATASTORE_PATH%`, - shouldBundleDatastore() - ? `` - : `${state.adapter.config.deployURL ?? ``}/${LmdbOnCdnPath}` - ) + functionCode = functionCode + .replace( + `%CDN_DATASTORE_PATH%`, + shouldBundleDatastore() + ? `` + : `${state.adapter.config.deployURL ?? ``}/${LmdbOnCdnPath}` + ) + .replace(`%PATH_PREFIX`, pathPrefix) await fs.outputFile(path.join(outputDir, `lambda.js`), functionCode) diff --git a/packages/gatsby/src/utils/page-ssr-module/lambda.ts b/packages/gatsby/src/utils/page-ssr-module/lambda.ts index f9ad1ccc3cacd..da7a6bd321761 100644 --- a/packages/gatsby/src/utils/page-ssr-module/lambda.ts +++ b/packages/gatsby/src/utils/page-ssr-module/lambda.ts @@ -13,6 +13,7 @@ import type { ISSRData } from "./entry" import { link } from "linkfs" const cdnDatastore = `%CDN_DATASTORE_PATH%` +const PATH_PREFIX = `%PATH_PREFIX` function setupFsWrapper(): string { // setup global._fsWrapper @@ -172,14 +173,13 @@ function reverseFixedPagePath(pageDataRequestPath: string): string { return pageDataRequestPath === `index` ? `/` : pageDataRequestPath } -function getPathInfo(req: GatsbyFunctionRequest): +function getPathInfo(requestPath: string): | { isPageData: boolean pagePath: string } | undefined { - // @ts-ignore GatsbyFunctionRequest.path is not in types ... there is no property in types that can be used to get a path currently - const matches = req.url.matchAll(/\/?page-data\/(.+)\/page-data.json$/gm) + const matches = requestPath.matchAll(/^\/?page-data\/(.+)\/page-data.json$/gm) for (const [, requestedPagePath] of matches) { return { isPageData: true, @@ -190,8 +190,7 @@ function getPathInfo(req: GatsbyFunctionRequest): // if not matched return { isPageData: false, - // @ts-ignore GatsbyFunctionRequest.path is not in types ... there is no property in types that can be used to get a path currently - pagePath: req.url, + pagePath: requestPath, } } @@ -232,26 +231,61 @@ function getErrorBody(statusCode: number): string { return body } +interface IPageInfo { + page: IGatsbyPage + isPageData: boolean + pagePath: string +} + +function getPage( + pathname: string, + graphqlEngine: GraphQLEngineType +): IPageInfo | undefined { + const pathInfo = getPathInfo(pathname) + if (!pathInfo) { + return undefined + } + + const { isPageData, pagePath } = pathInfo + + const page = graphqlEngine.findPageByPath(pagePath) + if (!page) { + return undefined + } + + return { + page, + isPageData, + pagePath, + } +} + async function engineHandler( req: GatsbyFunctionRequest, res: GatsbyFunctionResponse ): Promise { try { const graphqlEngine = await engineReadyPromise - const pathInfo = getPathInfo(req) - if (!pathInfo) { - res.status(404).send(getErrorBody(404)) - return + let pageInfo: IPageInfo | undefined + + const originalPathName = req.url ?? `` + + if (PATH_PREFIX && originalPathName.startsWith(PATH_PREFIX)) { + const maybePath = originalPathName.slice(PATH_PREFIX.length) + pageInfo = getPage(maybePath, graphqlEngine) } - const { isPageData, pagePath } = pathInfo + if (!pageInfo) { + pageInfo = getPage(originalPathName, graphqlEngine) + } - const page = graphqlEngine.findPageByPath(pagePath) - if (!page) { + if (!pageInfo) { res.status(404).send(getErrorBody(404)) return } + const { pagePath, isPageData, page } = pageInfo + const data = await getData({ pathName: pagePath, graphqlEngine, From 5df4e65f62001dcc17041baf503a9e49aec9ee55 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Thu, 26 Oct 2023 14:27:51 +0200 Subject: [PATCH 14/32] keep 404/500 status pages in original place --- packages/gatsby-adapter-netlify/src/pretty-urls.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/gatsby-adapter-netlify/src/pretty-urls.ts b/packages/gatsby-adapter-netlify/src/pretty-urls.ts index 4ae509466b985..159fca4fc992a 100644 --- a/packages/gatsby-adapter-netlify/src/pretty-urls.ts +++ b/packages/gatsby-adapter-netlify/src/pretty-urls.ts @@ -22,6 +22,7 @@ function generateFilePathForStaticRoute( interface IMoveTask { from: string to: string + keepOriginalFile: boolean } export function createStaticAssetsPathHandler(): { @@ -30,7 +31,11 @@ export function createStaticAssetsPathHandler(): { } { const moveQueue = fastq(async (task, cb) => { try { - await fs.move(task.from, task.to) + if (task.keepOriginalFile) { + await fs.copy(task.from, task.to) + } else { + await fs.move(task.from, task.to) + } cb(null, undefined) } catch (error) { cb(error) @@ -46,7 +51,12 @@ export function createStaticAssetsPathHandler(): { )}` if (expectedPath !== filePath) { - moveQueue.push({ from: filePath, to: expectedPath }) + moveQueue.push({ + from: filePath, + to: expectedPath, + keepOriginalFile: + filePath === `public/404.html` || filePath === `public/400.html`, + }) } return filePath } From 2eed87b1e3c2b13bc62111eeb05803457d337eb4 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Thu, 26 Oct 2023 14:48:19 +0200 Subject: [PATCH 15/32] fix typo --- packages/gatsby-adapter-netlify/src/pretty-urls.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gatsby-adapter-netlify/src/pretty-urls.ts b/packages/gatsby-adapter-netlify/src/pretty-urls.ts index 159fca4fc992a..4874a28526b6e 100644 --- a/packages/gatsby-adapter-netlify/src/pretty-urls.ts +++ b/packages/gatsby-adapter-netlify/src/pretty-urls.ts @@ -55,7 +55,7 @@ export function createStaticAssetsPathHandler(): { from: filePath, to: expectedPath, keepOriginalFile: - filePath === `public/404.html` || filePath === `public/400.html`, + filePath === `public/404.html` || filePath === `public/500.html`, }) } return filePath From 9751481bfe7fcb4b02bcdf8cb1befd15538172e1 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Thu, 26 Oct 2023 17:32:36 +0200 Subject: [PATCH 16/32] fix assertion? --- e2e-tests/adapters/cypress/e2e/ssr.cy.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/e2e-tests/adapters/cypress/e2e/ssr.cy.ts b/e2e-tests/adapters/cypress/e2e/ssr.cy.ts index 5eedf9d93932a..892906e07f1ae 100644 --- a/e2e-tests/adapters/cypress/e2e/ssr.cy.ts +++ b/e2e-tests/adapters/cypress/e2e/ssr.cy.ts @@ -1,6 +1,8 @@ const staticPath = "/routes/ssr/static" const paramPath = "/routes/ssr/param" +const PATH_PREFIX = Cypress.env(`PATH_PREFIX`) || `` + describe("Server Side Rendering (SSR)", () => { it(`direct visit no query params (${staticPath})`, () => { cy.visit(staticPath).waitForRouteChange() @@ -32,7 +34,7 @@ describe("Server Side Rendering (SSR)", () => { cy.visit(errorPath, { failOnStatusCode: false }).waitForRouteChange() cy.location(`pathname`) - .should(`equal`, errorPath) + .should(`equal`, PATH_PREFIX + errorPath) .get(`h1`) .should(`have.text`, `INTERNAL SERVER ERROR (custom)`) }) From d09b7917b2d4e8f9b2709de05b1551f514388111 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Fri, 27 Oct 2023 10:44:48 +0200 Subject: [PATCH 17/32] streamline file moving logic --- .../gatsby-adapter-netlify/src/pretty-urls.ts | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/packages/gatsby-adapter-netlify/src/pretty-urls.ts b/packages/gatsby-adapter-netlify/src/pretty-urls.ts index 4874a28526b6e..53f0eed787097 100644 --- a/packages/gatsby-adapter-netlify/src/pretty-urls.ts +++ b/packages/gatsby-adapter-netlify/src/pretty-urls.ts @@ -1,22 +1,12 @@ import fastq from "fastq" import fs from "fs-extra" -function generateFilePathForStaticRoute( - routePath: string, - shouldUsePrettyUrl: boolean -): string { - if (shouldUsePrettyUrl) { - if (routePath.endsWith(`.html`)) { - return routePath - } else if (routePath === `/`) { - return `/index.html` - } else if (routePath.endsWith(`/`)) { - return `${routePath}index.html` - } else { - return `${routePath}.html` - } +function generatePrettyUrlFilePath(routePath: string): string { + if (routePath.endsWith(`/`)) { + return `${routePath}index.html` + } else { + return `${routePath}.html` } - return routePath } interface IMoveTask { @@ -32,9 +22,9 @@ export function createStaticAssetsPathHandler(): { const moveQueue = fastq(async (task, cb) => { try { if (task.keepOriginalFile) { - await fs.copy(task.from, task.to) + await fs.copy(task.from, task.to, { overwrite: true }) } else { - await fs.move(task.from, task.to) + await fs.move(task.from, task.to, { overwrite: true }) } cb(null, undefined) } catch (error) { @@ -43,17 +33,19 @@ export function createStaticAssetsPathHandler(): { }, 2) function ensureStaticAssetPath(filePath: string, routePath: string): string { - const shouldUsePrettyUrl = filePath.endsWith(`.html`) + const shouldUsePrettyUrl = + filePath.endsWith(`.html`) && !routePath.endsWith(`.html`) - const expectedPath = `public${generateFilePathForStaticRoute( - routePath, - shouldUsePrettyUrl - )}` + const expectedPath = `public${ + shouldUsePrettyUrl ? generatePrettyUrlFilePath(routePath) : routePath + }` if (expectedPath !== filePath) { moveQueue.push({ from: filePath, to: expectedPath, + // 404.html should stay in root of PUBLISH_DIR to be used as custom 404 page + // both 404.html and 500.html should stay in root of PUBLISH_DIR to be bundled correctly for SSR/DSG keepOriginalFile: filePath === `public/404.html` || filePath === `public/500.html`, }) From d9f013d4b18d349b588ef2aeab0349addfcf8d87 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Fri, 27 Oct 2023 10:45:19 +0200 Subject: [PATCH 18/32] cache (and restore) publishdir locally too --- packages/gatsby-adapter-netlify/src/index.ts | 10 ++++++++-- packages/gatsby/src/commands/build.ts | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/gatsby-adapter-netlify/src/index.ts b/packages/gatsby-adapter-netlify/src/index.ts index 190aad02a9c81..9acd462c92d74 100644 --- a/packages/gatsby-adapter-netlify/src/index.ts +++ b/packages/gatsby-adapter-netlify/src/index.ts @@ -1,3 +1,4 @@ +import { join } from "path" import type { AdapterInit, IAdapterConfig } from "gatsby" import { prepareFunctionVariants } from "./lambda-handler" import { handleRoutesManifest } from "./route-handler" @@ -17,12 +18,17 @@ async function getCacheUtils(): Promise { if (_cacheUtils) { return _cacheUtils } + let CACHE_DIR: string | undefined if (process.env.NETLIFY) { - const CACHE_DIR = `/opt/build/cache` + CACHE_DIR = `/opt/build/cache` + } else if (process.env.NETLIFY_LOCAL) { + CACHE_DIR = join(process.cwd(), `.netlify`, `build-cache`) + } + if (CACHE_DIR) { + console.log({ CACHE_DIR }) _cacheUtils = (await import(`@netlify/cache-utils`)).bindOpts({ cacheDir: CACHE_DIR, }) - return _cacheUtils } return undefined diff --git a/packages/gatsby/src/commands/build.ts b/packages/gatsby/src/commands/build.ts index 05dbc1f7bb2a6..364329e7b26cc 100644 --- a/packages/gatsby/src/commands/build.ts +++ b/packages/gatsby/src/commands/build.ts @@ -698,8 +698,8 @@ module.exports = async function build( } if (adapterManager) { - await adapterManager.adapt() await adapterManager.storeCache() + await adapterManager.adapt() } showExperimentNotices() From 0fcec3174862a86add11bf74e403b3926ecb4a6f Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Fri, 27 Oct 2023 12:26:30 +0200 Subject: [PATCH 19/32] add pretty-url unit tests --- packages/gatsby-adapter-netlify/package.json | 1 + .../src/__tests__/pretty-urls.ts | 152 ++++++++++++++++++ .../gatsby-adapter-netlify/src/pretty-urls.ts | 2 +- yarn.lock | 31 ++++ 4 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts diff --git a/packages/gatsby-adapter-netlify/package.json b/packages/gatsby-adapter-netlify/package.json index fec2930d5bcc5..e13f4d8f29eed 100644 --- a/packages/gatsby-adapter-netlify/package.json +++ b/packages/gatsby-adapter-netlify/package.json @@ -43,6 +43,7 @@ "@babel/core": "^7.20.12", "babel-preset-gatsby-package": "^3.13.0-next.0", "cross-env": "^7.0.3", + "memfs": "^4.6.0", "rimraf": "^5.0.1", "typescript": "^5.1.6" }, diff --git a/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts b/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts new file mode 100644 index 0000000000000..6afe97e7ed7ad --- /dev/null +++ b/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts @@ -0,0 +1,152 @@ +import fse from "fs-extra" +import { vol } from "memfs" +import { + generatePrettyUrlFilePath, + createStaticAssetsPathHandler, +} from "../pretty-urls" + +jest.mock(`fs`, () => jest.requireActual(`memfs`).fs) + +describe(`generatePrettyUrlFilePath`, () => { + it(`/`, () => { + expect(generatePrettyUrlFilePath(`/`)).toEqual(`/index.html`) + }) + + it(`/foo`, () => { + expect(generatePrettyUrlFilePath(`/foo`)).toEqual(`/foo.html`) + }) + + it(`/foo/`, () => { + expect(generatePrettyUrlFilePath(`/foo/`)).toEqual(`/foo/index.html`) + }) +}) + +describe(`createStaticAssetsPathHandler`, () => { + it(`no-op if filepath is already coorect for given route`, async () => { + const copySpy = jest.spyOn(fse, `copy`) + const moveSpy = jest.spyOn(fse, `move`) + + vol.fromJSON({ "public/index.html": `index` }, process.cwd()) + + const { ensureStaticAssetPath, fileMovingDone } = + createStaticAssetsPathHandler() + + ensureStaticAssetPath(`public/index.html`, `/`) + ensureStaticAssetPath(`public/index.css`, `/index.css`) + ensureStaticAssetPath( + `public/_gatsby/slices/slice-1.html`, + `/_gatsby/slices/slice-1.html` + ) + + await fileMovingDone() + + expect(moveSpy).not.toBeCalled() + expect(copySpy).not.toBeCalled() + }) + + describe(`moves or copies file if filepath is incorrect for given route`, () => { + it(`removes trailing slash`, async () => { + vol.fromJSON({ "public/foo/index.html": `foo` }, process.cwd()) + + const { ensureStaticAssetPath, fileMovingDone } = + createStaticAssetsPathHandler() + + expect(vol.existsSync(`public/foo/index.html`)).toEqual(true) + expect(vol.existsSync(`public/foo.html`)).toEqual(false) + + ensureStaticAssetPath(`public/foo/index.html`, `/foo`) + + await fileMovingDone() + + expect(vol.existsSync(`public/foo/index.html`)).toEqual(false) + expect(vol.existsSync(`public/foo.html`)).toEqual(true) + }) + + it(`adds path prefix`, async () => { + vol.fromJSON({ "public/foo/index.html": `foo` }, process.cwd()) + + const { ensureStaticAssetPath, fileMovingDone } = + createStaticAssetsPathHandler() + + expect(vol.existsSync(`public/foo/index.html`)).toEqual(true) + expect(vol.existsSync(`public/prefix/foo/index.html`)).toEqual(false) + + ensureStaticAssetPath(`public/foo/index.html`, `/prefix/foo/`) + + await fileMovingDone() + + expect(vol.existsSync(`public/foo/index.html`)).toEqual(false) + expect(vol.existsSync(`public/prefix/foo/index.html`)).toEqual(true) + }) + + it(`adds path prefix and removes trailing slash`, async () => { + vol.fromJSON({ "public/foo/index.html": `foo` }, process.cwd()) + + const { ensureStaticAssetPath, fileMovingDone } = + createStaticAssetsPathHandler() + + expect(vol.existsSync(`public/foo/index.html`)).toEqual(true) + expect(vol.existsSync(`public/prefix/foo.html`)).toEqual(false) + + ensureStaticAssetPath(`public/foo/index.html`, `/prefix/foo`) + + await fileMovingDone() + + expect(vol.existsSync(`public/foo/index.html`)).toEqual(false) + expect(vol.existsSync(`public/prefix/foo.html`)).toEqual(true) + }) + + it(`handles non html assets for path prefix`, async () => { + vol.fromJSON({ "public/index.css": `body {}` }, process.cwd()) + + const { ensureStaticAssetPath, fileMovingDone } = + createStaticAssetsPathHandler() + + expect(vol.existsSync(`public/index.css`)).toEqual(true) + expect(vol.existsSync(`public/prefix/index.css`)).toEqual(false) + + ensureStaticAssetPath(`public/index.css`, `/prefix/index.css`) + + await fileMovingDone() + + expect(vol.existsSync(`public/index.css`)).toEqual(false) + expect(vol.existsSync(`public/prefix/index.css`)).toEqual(true) + }) + + it(`keeps 404.html in root`, async () => { + vol.fromJSON({ "public/404.html": `404` }, process.cwd()) + + const { ensureStaticAssetPath, fileMovingDone } = + createStaticAssetsPathHandler() + + expect(vol.existsSync(`public/404.html`)).toEqual(true) + expect(vol.existsSync(`public/foo/404.html`)).toEqual(false) + + ensureStaticAssetPath(`public/404.html`, `/foo/404.html`) + + await fileMovingDone() + + expect(vol.existsSync(`public/404.html`)).toEqual(true) + // 404 page is coped so it exists in both locations + expect(vol.existsSync(`public/foo/404.html`)).toEqual(true) + }) + + it(`keeps 500.html in root`, async () => { + vol.fromJSON({ "public/500.html": `500` }, process.cwd()) + + const { ensureStaticAssetPath, fileMovingDone } = + createStaticAssetsPathHandler() + + expect(vol.existsSync(`public/500.html`)).toEqual(true) + expect(vol.existsSync(`public/foo/500.html`)).toEqual(false) + + ensureStaticAssetPath(`public/500.html`, `/foo/500.html`) + + await fileMovingDone() + + expect(vol.existsSync(`public/500.html`)).toEqual(true) + // 404 page is coped so it exists in both locations + expect(vol.existsSync(`public/foo/500.html`)).toEqual(true) + }) + }) +}) diff --git a/packages/gatsby-adapter-netlify/src/pretty-urls.ts b/packages/gatsby-adapter-netlify/src/pretty-urls.ts index 53f0eed787097..91b4fe48b246e 100644 --- a/packages/gatsby-adapter-netlify/src/pretty-urls.ts +++ b/packages/gatsby-adapter-netlify/src/pretty-urls.ts @@ -1,7 +1,7 @@ import fastq from "fastq" import fs from "fs-extra" -function generatePrettyUrlFilePath(routePath: string): string { +export function generatePrettyUrlFilePath(routePath: string): string { if (routePath.endsWith(`/`)) { return `${routePath}index.html` } else { diff --git a/yarn.lock b/yarn.lock index d13fe809e3c81..55d476539b4ee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6070,6 +6070,11 @@ arg@^5.0.0: resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.0.tgz#a20e2bb5710e82950a516b3f933fee5ed478be90" integrity sha512-4P8Zm2H+BRS+c/xX1LrHw0qKpEhdlZjLCgWy+d78T9vqa2Z2SiD2wMrYuWIAFy5IZUD7nnNXroRttz+0RzlrzQ== +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + argparse@^1.0.10, argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -13296,6 +13301,11 @@ husky@3.1.0: run-node "^1.0.0" slash "^3.0.0" +hyperdyperid@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hyperdyperid/-/hyperdyperid-1.2.0.tgz#59668d323ada92228d2a869d3e474d5a33b69e6b" + integrity sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A== + hyperlinker@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e" @@ -15037,6 +15047,14 @@ json-diff@^1.0.6: colors "^1.4.0" dreamopt "~0.8.0" +json-joy@^9.2.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/json-joy/-/json-joy-9.6.0.tgz#b691310024205b2d082737ca3c7e72cac0e364ac" + integrity sha512-vJtJD89T0OOZFMaENe95xKCOdibMev/lELkclTdhZxLplwbBPxneWNuctUPizk2nLqtGfBxwCXVO42G9LBoFBA== + dependencies: + arg "^5.0.2" + hyperdyperid "^1.2.0" + json-loader@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" @@ -16523,6 +16541,14 @@ memfs@^3.1.2, memfs@^3.2.2: dependencies: fs-monkey "1.0.3" +memfs@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.6.0.tgz#e812d438f73482a7110420d13d381c730b9a4de5" + integrity sha512-I6mhA1//KEZfKRQT9LujyW6lRbX7RkC24xKododIDO3AGShcaFAMKElv1yFGWX8fD4UaSiwasr3NeQ5TdtHY1A== + dependencies: + json-joy "^9.2.0" + thingies "^1.11.1" + memoizee@^0.4.15: version "0.4.15" resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.15.tgz#e6f3d2da863f318d02225391829a6c5956555b72" @@ -23307,6 +23333,11 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +thingies@^1.11.1: + version "1.12.0" + resolved "https://registry.yarnpkg.com/thingies/-/thingies-1.12.0.tgz#a815c224482d607aa70f563d3cbb351a338e4710" + integrity sha512-AiGqfYC1jLmJagbzQGuoZRM48JPsr9yB734a7K6wzr34NMhjUPrWSQrkF7ZBybf3yCerCL2Gcr02kMv4NmaZfA== + thread-stream@^0.15.1: version "0.15.2" resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-0.15.2.tgz#fb95ad87d2f1e28f07116eb23d85aba3bc0425f4" From 4223283c4904c89646135244239abc7f7326ccba Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Tue, 31 Oct 2023 11:04:06 +0100 Subject: [PATCH 20/32] handle dynamic paths when generating pretty url file names --- .../src/__tests__/pretty-urls.ts | 58 +++++++++++++++++++ .../gatsby-adapter-netlify/src/pretty-urls.ts | 33 +++++++++-- .../src/route-handler.ts | 12 ++-- 3 files changed, 92 insertions(+), 11 deletions(-) diff --git a/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts b/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts index 6afe97e7ed7ad..47e890877478d 100644 --- a/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts +++ b/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts @@ -22,6 +22,10 @@ describe(`generatePrettyUrlFilePath`, () => { }) describe(`createStaticAssetsPathHandler`, () => { + beforeEach(() => { + vol.reset() + }) + it(`no-op if filepath is already coorect for given route`, async () => { const copySpy = jest.spyOn(fse, `copy`) const moveSpy = jest.spyOn(fse, `move`) @@ -113,6 +117,60 @@ describe(`createStaticAssetsPathHandler`, () => { expect(vol.existsSync(`public/prefix/index.css`)).toEqual(true) }) + it(`handles dynamic param paths syntax (is not using reserved characters for file paths)`, async () => { + vol.fromJSON({ "public/[param]/index.html": `:param` }, process.cwd()) + + const { ensureStaticAssetPath, fileMovingDone } = + createStaticAssetsPathHandler() + + expect(vol.existsSync(`public/[param]/index.html`)).toEqual(true) + expect(vol.existsSync(`public/foo/[param]/index.html`)).toEqual(false) + + ensureStaticAssetPath(`public/[param]/index.html`, `/foo/:param/`) + + await fileMovingDone() + + expect(vol.existsSync(`public/foo/[param]/index.html`)).toEqual(true) + expect(vol.existsSync(`public/[param]/index.html`)).toEqual(false) + }) + + it(`handles dynamic named wildcard paths syntax (is not using reserved characters for file paths)`, async () => { + vol.fromJSON( + { "public/[...wildcard]/index.html": `*wildcard` }, + process.cwd() + ) + + const { ensureStaticAssetPath, fileMovingDone } = + createStaticAssetsPathHandler() + + expect(vol.existsSync(`public/[...wildcard]/index.html`)).toEqual(true) + expect(vol.existsSync(`public/foo/[...].html`)).toEqual(false) + + ensureStaticAssetPath(`public/[...wildcard]/index.html`, `/foo/*`) + + await fileMovingDone() + + expect(vol.existsSync(`public/foo/[...].html`)).toEqual(true) + expect(vol.existsSync(`public/[...wildcard]/index.html`)).toEqual(false) + }) + + it(`handles dynamic unnamed wildcard paths syntax (is not using reserved characters for file paths)`, async () => { + vol.fromJSON({ "public/[...]/index.html": `*` }, process.cwd()) + + const { ensureStaticAssetPath, fileMovingDone } = + createStaticAssetsPathHandler() + + expect(vol.existsSync(`public/[...]/index.html`)).toEqual(true) + expect(vol.existsSync(`public/foo/[...].html`)).toEqual(false) + + ensureStaticAssetPath(`public/[...]/index.html`, `/foo/*`) + + await fileMovingDone() + + expect(vol.existsSync(`public/foo/[...].html`)).toEqual(true) + expect(vol.existsSync(`public/[...]/index.html`)).toEqual(false) + }) + it(`keeps 404.html in root`, async () => { vol.fromJSON({ "public/404.html": `404` }, process.cwd()) diff --git a/packages/gatsby-adapter-netlify/src/pretty-urls.ts b/packages/gatsby-adapter-netlify/src/pretty-urls.ts index 91b4fe48b246e..d6d20f57c287f 100644 --- a/packages/gatsby-adapter-netlify/src/pretty-urls.ts +++ b/packages/gatsby-adapter-netlify/src/pretty-urls.ts @@ -16,7 +16,10 @@ interface IMoveTask { } export function createStaticAssetsPathHandler(): { - ensureStaticAssetPath: (filePath: string, routePath: string) => string + ensureStaticAssetPath: ( + filePath: string, + routePath: string + ) => { finalFilePath: string; isDynamic: boolean } fileMovingDone: () => Promise } { const moveQueue = fastq(async (task, cb) => { @@ -32,25 +35,43 @@ export function createStaticAssetsPathHandler(): { } }, 2) - function ensureStaticAssetPath(filePath: string, routePath: string): string { + function ensureStaticAssetPath( + filePath: string, + routePath: string + ): { finalFilePath: string; isDynamic: boolean } { const shouldUsePrettyUrl = filePath.endsWith(`.html`) && !routePath.endsWith(`.html`) - const expectedPath = `public${ + let isDynamic = false + // dynamic routes syntax use characters that are reserved in a lot of filesystems + // so if route is dynamic we should normalize filepath + if (routePath.includes(`:`) || routePath.includes(`*`)) { + routePath = routePath + // replace `:param` with `[param]` + .replace(/:([^:/\\]+)/gm, `[$1]`) + // replace `*param` with `[...param]` and `*` with `[...]` + .replace(/\*([^:/\\]*|$)/gm, `[...$1]`) + isDynamic = true + } + + const finalFilePath = `public${ shouldUsePrettyUrl ? generatePrettyUrlFilePath(routePath) : routePath }` - if (expectedPath !== filePath) { + if (finalFilePath !== filePath) { moveQueue.push({ from: filePath, - to: expectedPath, + to: finalFilePath, // 404.html should stay in root of PUBLISH_DIR to be used as custom 404 page // both 404.html and 500.html should stay in root of PUBLISH_DIR to be bundled correctly for SSR/DSG keepOriginalFile: filePath === `public/404.html` || filePath === `public/500.html`, }) } - return filePath + return { + finalFilePath, + isDynamic, + } } const fileMovingDone = (): Promise => { diff --git a/packages/gatsby-adapter-netlify/src/route-handler.ts b/packages/gatsby-adapter-netlify/src/route-handler.ts index cbd40caaf6b30..9d3d6c57a9d86 100644 --- a/packages/gatsby-adapter-netlify/src/route-handler.ts +++ b/packages/gatsby-adapter-netlify/src/route-handler.ts @@ -211,14 +211,16 @@ export function processRoutesManifest(routesManifest: RoutesManifest): { } _redirects += pieces.join(` `) + `\n` } else if (route.type === `static`) { - // regular static asset without dynamic paths will just work, so skipping those - if (route.path.includes(`:`) || route.path.includes(`*`)) { - _redirects += `${encodeURI(fromPath)} ${route.filePath.replace( + const { finalFilePath, isDynamic } = ensureStaticAssetPath( + route.filePath, + fromPath + ) + + if (isDynamic) { + _redirects += `${encodeURI(fromPath)} ${finalFilePath.replace( /^public/, `` )} 200\n` - } else { - ensureStaticAssetPath(route.filePath, fromPath) } _headers += `${encodeURI(fromPath)}\n${route.headers.reduce( From e407ef09d06822093683d8576ca8859fc460f1f3 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Tue, 31 Oct 2023 12:08:47 +0100 Subject: [PATCH 21/32] update intercepting glob to handle path prefix --- e2e-tests/adapters/cypress/e2e/headers.cy.ts | 48 +++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/e2e-tests/adapters/cypress/e2e/headers.cy.ts b/e2e-tests/adapters/cypress/e2e/headers.cy.ts index 6ff0002d19ce6..bcfb20aafb987 100644 --- a/e2e-tests/adapters/cypress/e2e/headers.cy.ts +++ b/e2e-tests/adapters/cypress/e2e/headers.cy.ts @@ -1,5 +1,7 @@ import { WorkaroundCachedResponse } from "../utils/dont-cache-responses-in-browser" +const PATH_PREFIX = Cypress.env(`PATH_PREFIX`) || `` + describe("Headers", () => { const defaultHeaders = { "x-xss-protection": "1; mode=block", @@ -73,23 +75,37 @@ describe("Headers", () => { } beforeEach(() => { - cy.intercept("/", WorkaroundCachedResponse).as("index") - cy.intercept("routes/ssr/static", WorkaroundCachedResponse).as("ssr") - cy.intercept("routes/dsg/static", WorkaroundCachedResponse).as("dsg") - - cy.intercept("**/page-data.json", WorkaroundCachedResponse).as("page-data") - cy.intercept("**/app-data.json", WorkaroundCachedResponse).as("app-data") - cy.intercept("**/slice-data/*.json", WorkaroundCachedResponse).as( - "slice-data" - ) - cy.intercept("**/page-data/sq/d/*.json", WorkaroundCachedResponse).as( - "static-query-result" - ) - - cy.intercept("/static/astro-**.png", WorkaroundCachedResponse).as( - "img-webpack-import" + cy.intercept(PATH_PREFIX + "/", WorkaroundCachedResponse).as("index") + cy.intercept( + PATH_PREFIX + "routes/ssr/static", + WorkaroundCachedResponse + ).as("ssr") + cy.intercept( + PATH_PREFIX + "routes/dsg/static", + WorkaroundCachedResponse + ).as("dsg") + + cy.intercept( + PATH_PREFIX + "**/page-data.json", + WorkaroundCachedResponse + ).as("page-data") + cy.intercept(PATH_PREFIX + "**/app-data.json", WorkaroundCachedResponse).as( + "app-data" ) - cy.intercept("*.js", WorkaroundCachedResponse).as("js") + cy.intercept( + PATH_PREFIX + "**/slice-data/*.json", + WorkaroundCachedResponse + ).as("slice-data") + cy.intercept( + PATH_PREFIX + "**/page-data/sq/d/*.json", + WorkaroundCachedResponse + ).as("static-query-result") + + cy.intercept( + PATH_PREFIX + "/static/astro-**.png", + WorkaroundCachedResponse + ).as("img-webpack-import") + cy.intercept(PATH_PREFIX + "*.js", WorkaroundCachedResponse).as("js") }) it("should contain correct headers for index page", () => { From 3c06ad2d98112045e1b62bff35fe19840a868a4b Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Tue, 31 Oct 2023 12:45:08 +0100 Subject: [PATCH 22/32] update intercepting glob to handle path prefix 2 --- e2e-tests/adapters/cypress/e2e/headers.cy.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/e2e-tests/adapters/cypress/e2e/headers.cy.ts b/e2e-tests/adapters/cypress/e2e/headers.cy.ts index bcfb20aafb987..a2cd58cdecbb8 100644 --- a/e2e-tests/adapters/cypress/e2e/headers.cy.ts +++ b/e2e-tests/adapters/cypress/e2e/headers.cy.ts @@ -77,27 +77,28 @@ describe("Headers", () => { beforeEach(() => { cy.intercept(PATH_PREFIX + "/", WorkaroundCachedResponse).as("index") cy.intercept( - PATH_PREFIX + "routes/ssr/static", + PATH_PREFIX + "/routes/ssr/static", WorkaroundCachedResponse ).as("ssr") cy.intercept( - PATH_PREFIX + "routes/dsg/static", + PATH_PREFIX + "/routes/dsg/static", WorkaroundCachedResponse ).as("dsg") cy.intercept( - PATH_PREFIX + "**/page-data.json", + PATH_PREFIX + "/**/page-data.json", WorkaroundCachedResponse ).as("page-data") - cy.intercept(PATH_PREFIX + "**/app-data.json", WorkaroundCachedResponse).as( - "app-data" - ) cy.intercept( - PATH_PREFIX + "**/slice-data/*.json", + PATH_PREFIX + "/**/app-data.json", + WorkaroundCachedResponse + ).as("app-data") + cy.intercept( + PATH_PREFIX + "/**/slice-data/*.json", WorkaroundCachedResponse ).as("slice-data") cy.intercept( - PATH_PREFIX + "**/page-data/sq/d/*.json", + PATH_PREFIX + "/**/page-data/sq/d/*.json", WorkaroundCachedResponse ).as("static-query-result") @@ -105,7 +106,7 @@ describe("Headers", () => { PATH_PREFIX + "/static/astro-**.png", WorkaroundCachedResponse ).as("img-webpack-import") - cy.intercept(PATH_PREFIX + "*.js", WorkaroundCachedResponse).as("js") + cy.intercept(PATH_PREFIX + "/**/*.js", WorkaroundCachedResponse).as("js") }) it("should contain correct headers for index page", () => { From 7713dcff541427fd573656692f77004a88f26a3f Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Tue, 31 Oct 2023 13:18:55 +0100 Subject: [PATCH 23/32] restore automatic deploys deletion --- e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs b/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs index 24ba4aec54fe3..f3a5525f48081 100644 --- a/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs +++ b/e2e-tests/adapters/scripts/deploy-and-run/netlify.mjs @@ -47,8 +47,7 @@ console.log(`Deployed to ${deployUrl}`) try { await execa(`npm`, [`run`, npmScriptToRun], { stdio: `inherit` }) } finally { - // temporaraily disable cleanup for debugging - if (false && !process.env.GATSBY_TEST_SKIP_CLEANUP) { + if (!process.env.GATSBY_TEST_SKIP_CLEANUP) { console.log(`Deleting project with deploy_id ${deployInfo.deploy_id}`) const deleteResponse = await execa("ntl", [ From 53adcbb530031aeb8e4c2d7ff830a8d33354a69c Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Tue, 31 Oct 2023 13:34:09 +0100 Subject: [PATCH 24/32] test: jest ensure we mount files that we test filepaths for --- .../gatsby-adapter-netlify/src/__tests__/pretty-urls.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts b/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts index 47e890877478d..10bb948e513c7 100644 --- a/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts +++ b/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts @@ -30,7 +30,14 @@ describe(`createStaticAssetsPathHandler`, () => { const copySpy = jest.spyOn(fse, `copy`) const moveSpy = jest.spyOn(fse, `move`) - vol.fromJSON({ "public/index.html": `index` }, process.cwd()) + vol.fromJSON( + { + "public/index.html": `index`, + "public/_gatsby/slices/slice-1.html": `slice`, + "public/index.css": `body {}`, + }, + process.cwd() + ) const { ensureStaticAssetPath, fileMovingDone } = createStaticAssetsPathHandler() From 44658759d7f52bbdc545e34bd30f7bcff273f8dc Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Tue, 31 Oct 2023 13:50:57 +0100 Subject: [PATCH 25/32] handle path prefix in header rules --- .../src/utils/adapter/__tests__/manager.ts | 78 +++++++++++++++++++ packages/gatsby/src/utils/adapter/manager.ts | 16 ++-- 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/packages/gatsby/src/utils/adapter/__tests__/manager.ts b/packages/gatsby/src/utils/adapter/__tests__/manager.ts index 2ea7545ab39f6..b71a4ba7b0d08 100644 --- a/packages/gatsby/src/utils/adapter/__tests__/manager.ts +++ b/packages/gatsby/src/utils/adapter/__tests__/manager.ts @@ -182,6 +182,84 @@ describe(`getRoutesManifest`, () => { ) }) + it(`should return header rules (path prefix variant)`, () => { + mockStoreState(stateDefault, { + program: { + ...stateDefault.program, + prefixPaths: true, + }, + config: { + ...stateDefault.config, + pathPrefix: `/prefix`, + headers: [ + { + source: `/ssr/*`, + headers: [ + { + key: `x-ssr-header`, + value: `my custom header value from config`, + }, + ], + }, + ], + }, + }) + process.chdir(fixturesDir) + setWebpackAssets(new Set([`app-123.js`, `static/app-456.js`])) + + const { headers } = getRoutesManifest() + + expect(headers).toContainEqual({ + headers: [ + { key: `x-xss-protection`, value: `1; mode=block` }, + { key: `x-content-type-options`, value: `nosniff` }, + { key: `referrer-policy`, value: `same-origin` }, + { key: `x-frame-options`, value: `DENY` }, + ], + path: `/prefix/*`, + }) + expect(headers).toContainEqual({ + headers: [ + { + key: `cache-control`, + value: `public, max-age=31536000, immutable`, + }, + ], + path: `/prefix/static/*`, + }) + expect(headers).toContainEqual({ + headers: [ + { + key: `cache-control`, + value: `public, max-age=0, must-revalidate`, + }, + ], + path: `/prefix/page-data/index/page-data.json`, + }) + expect(headers).toContainEqual({ + headers: [ + { + key: `cache-control`, + value: `public, max-age=31536000, immutable`, + }, + ], + path: `/prefix/app-123.js`, + }) + expect(headers).not.toContainEqual({ + headers: [ + { key: `x-xss-protection`, value: `1; mode=block` }, + { key: `x-content-type-options`, value: `nosniff` }, + { key: `referrer-policy`, value: `same-origin` }, + { key: `x-frame-options`, value: `DENY` }, + ], + path: `/prefix/ssr/*`, + }) + + expect(headers).not.toContain( + expect.objectContaining({ path: `/prefix/static/app-456.js` }) + ) + }) + it(`should respect "force" redirects parameter`, () => { mockStoreState(stateDefault, { config: { ...stateDefault.config }, diff --git a/packages/gatsby/src/utils/adapter/manager.ts b/packages/gatsby/src/utils/adapter/manager.ts index b11e69a5e114a..ed388f6655503 100644 --- a/packages/gatsby/src/utils/adapter/manager.ts +++ b/packages/gatsby/src/utils/adapter/manager.ts @@ -278,26 +278,26 @@ type RouteWithScore = { score: number } & Route const headersAreEqual = (a, b): boolean => a.key === b.key && a.value === b.value -const defaultHeaderRoutes: HeaderRoutes = [ +const getDefaultHeaderRoutes = (pathPrefix: string): HeaderRoutes => [ { - path: `/*`, + path: `${pathPrefix}/*`, headers: BASE_HEADERS, }, { - path: `/static/*`, + path: `${pathPrefix}/static/*`, headers: PERMANENT_CACHE_CONTROL_HEADER, }, ] const customHeaderFilter = - (route: Route) => + (route: Route, pathPrefix: string) => (h: IHeader["headers"][0]): boolean => { for (const baseHeader of BASE_HEADERS) { if (headersAreEqual(baseHeader, h)) { return false } } - if (route.path.startsWith(`/static/`)) { + if (route.path.startsWith(`${pathPrefix}/static/`)) { for (const cachingHeader of PERMAMENT_CACHING_HEADERS) { if (headersAreEqual(cachingHeader, h)) { return false @@ -318,7 +318,7 @@ function getRoutesManifest(): { ? state.config.pathPrefix ?? `` : `` - const headerRoutes: HeaderRoutes = [...defaultHeaderRoutes] + const headerRoutes: HeaderRoutes = [...getDefaultHeaderRoutes(pathPrefix)] const fileAssets = new Set( globSync(`**/**`, { @@ -351,7 +351,9 @@ function getRoutesManifest(): { if (route.type !== `function`) { route.headers = createHeaders(route.path, route.headers) - const customHeaders = route.headers.filter(customHeaderFilter(route)) + const customHeaders = route.headers.filter( + customHeaderFilter(route, pathPrefix) + ) if (customHeaders.length > 0) { headerRoutes.push({ path: route.path, headers: customHeaders }) } From 479c086ed7565bcef6c82efa820b98f591dc82ab Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Tue, 31 Oct 2023 16:26:15 +0100 Subject: [PATCH 26/32] drop debug helpers --- packages/gatsby-adapter-netlify/src/route-handler.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/gatsby-adapter-netlify/src/route-handler.ts b/packages/gatsby-adapter-netlify/src/route-handler.ts index 2e8256efe8a3d..f6320dc146c77 100644 --- a/packages/gatsby-adapter-netlify/src/route-handler.ts +++ b/packages/gatsby-adapter-netlify/src/route-handler.ts @@ -149,8 +149,6 @@ export function processRoutesManifest( } { const lambdasThatUseCaching = new Map() - fs.writeFileSync(`test.json`, JSON.stringify(routesManifest, null, 2)) - const { ensureStaticAssetPath, fileMovingDone } = createStaticAssetsPathHandler() From 83e8b4d940d3031c554893cc44e6b26bfcdde887 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Tue, 31 Oct 2023 16:28:48 +0100 Subject: [PATCH 27/32] first check if local --- packages/gatsby-adapter-netlify/src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/gatsby-adapter-netlify/src/index.ts b/packages/gatsby-adapter-netlify/src/index.ts index 896e3c8b54883..4b79ae2095bb5 100644 --- a/packages/gatsby-adapter-netlify/src/index.ts +++ b/packages/gatsby-adapter-netlify/src/index.ts @@ -19,10 +19,10 @@ async function getCacheUtils(): Promise { return _cacheUtils } let CACHE_DIR: string | undefined - if (process.env.NETLIFY) { - CACHE_DIR = `/opt/build/cache` - } else if (process.env.NETLIFY_LOCAL) { + if (process.env.NETLIFY_LOCAL) { CACHE_DIR = join(process.cwd(), `.netlify`, `build-cache`) + } else if (process.env.NETLIFY) { + CACHE_DIR = `/opt/build/cache` } if (CACHE_DIR) { console.log({ CACHE_DIR }) From a6f36b6f512756c338bd0bf715bbc250412da8e0 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Tue, 31 Oct 2023 16:30:26 +0100 Subject: [PATCH 28/32] drop debug log --- packages/gatsby-adapter-netlify/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/gatsby-adapter-netlify/src/index.ts b/packages/gatsby-adapter-netlify/src/index.ts index 4b79ae2095bb5..2e20a63a35508 100644 --- a/packages/gatsby-adapter-netlify/src/index.ts +++ b/packages/gatsby-adapter-netlify/src/index.ts @@ -25,7 +25,6 @@ async function getCacheUtils(): Promise { CACHE_DIR = `/opt/build/cache` } if (CACHE_DIR) { - console.log({ CACHE_DIR }) _cacheUtils = (await import(`@netlify/cache-utils`)).bindOpts({ cacheDir: CACHE_DIR, }) From f0e355c105f01db54b843a5542b458a4f5aabcd0 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Tue, 7 Nov 2023 12:04:55 +0100 Subject: [PATCH 29/32] handle external redirects when pathPrefix is used. Thanks @techfg --- .../src/utils/adapter/__tests__/manager.ts | 23 +++++++++++++++++++ packages/gatsby/src/utils/adapter/manager.ts | 11 +++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/packages/gatsby/src/utils/adapter/__tests__/manager.ts b/packages/gatsby/src/utils/adapter/__tests__/manager.ts index b71a4ba7b0d08..348a6cb442667 100644 --- a/packages/gatsby/src/utils/adapter/__tests__/manager.ts +++ b/packages/gatsby/src/utils/adapter/__tests__/manager.ts @@ -109,6 +109,29 @@ describe(`getRoutesManifest`, () => { ) }) + it(`should not prepend '\\' to external redirects (path prefix variant)`, () => { + mockStoreState(stateDefault, { + program: { + ...stateDefault.program, + prefixPaths: true, + }, + config: { + ...stateDefault.config, + pathPrefix: `/prefix`, + }, + }) + process.chdir(fixturesDir) + setWebpackAssets(new Set([`app-123.js`])) + + const { routes } = getRoutesManifest() + expect(routes).toEqual( + expect.arrayContaining([ + expect.objectContaining({ path: `https://old-url` }), + expect.objectContaining({ path: `http://old-url` }), + ]) + ) + }) + it(`should return header rules`, () => { mockStoreState(stateDefault, { config: { diff --git a/packages/gatsby/src/utils/adapter/manager.ts b/packages/gatsby/src/utils/adapter/manager.ts index ed388f6655503..5c56f16d64c2d 100644 --- a/packages/gatsby/src/utils/adapter/manager.ts +++ b/packages/gatsby/src/utils/adapter/manager.ts @@ -331,14 +331,15 @@ function getRoutesManifest(): { // TODO: This could be a "addSortedRoute" function that would add route to the list in sorted order. TBD if necessary performance-wise function addRoute(route: Route): void { if ( - !route.path.startsWith(`/`) && !(route.path.startsWith(`https://`) || route.path.startsWith(`http://`)) ) { - route.path = `/${route.path}` - } + if (!route.path.startsWith(`/`)) { + route.path = `/${route.path}` + } - if (pathPrefix && !route.path.startsWith(pathPrefix)) { - route.path = posix.join(pathPrefix, route.path) + if (pathPrefix && !route.path.startsWith(pathPrefix)) { + route.path = posix.join(pathPrefix, route.path) + } } // Apply trailing slash behavior unless it's a redirect. Redirects should always be exact matches From 63b5cce83b7caf40df41bdebf8efc5fb3221dd65 Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Fri, 10 Nov 2023 10:25:10 +0100 Subject: [PATCH 30/32] make placeholder syntax consistent --- packages/gatsby/src/utils/page-ssr-module/bundle-webpack.ts | 2 +- packages/gatsby/src/utils/page-ssr-module/lambda.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/gatsby/src/utils/page-ssr-module/bundle-webpack.ts b/packages/gatsby/src/utils/page-ssr-module/bundle-webpack.ts index 7e98eca469cf6..6b253093f6c2a 100644 --- a/packages/gatsby/src/utils/page-ssr-module/bundle-webpack.ts +++ b/packages/gatsby/src/utils/page-ssr-module/bundle-webpack.ts @@ -234,7 +234,7 @@ export async function createPageSSRBundle({ ? `` : `${state.adapter.config.deployURL ?? ``}/${LmdbOnCdnPath}` ) - .replace(`%PATH_PREFIX`, pathPrefix) + .replace(`%PATH_PREFIX%`, pathPrefix) await fs.outputFile(path.join(outputDir, `lambda.js`), functionCode) diff --git a/packages/gatsby/src/utils/page-ssr-module/lambda.ts b/packages/gatsby/src/utils/page-ssr-module/lambda.ts index da7a6bd321761..c051aff44010b 100644 --- a/packages/gatsby/src/utils/page-ssr-module/lambda.ts +++ b/packages/gatsby/src/utils/page-ssr-module/lambda.ts @@ -13,7 +13,7 @@ import type { ISSRData } from "./entry" import { link } from "linkfs" const cdnDatastore = `%CDN_DATASTORE_PATH%` -const PATH_PREFIX = `%PATH_PREFIX` +const PATH_PREFIX = `%PATH_PREFIX%` function setupFsWrapper(): string { // setup global._fsWrapper From 28856c21b8eddafcfdb902ba020192bc902fabda Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Fri, 10 Nov 2023 10:25:50 +0100 Subject: [PATCH 31/32] update comment --- packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts b/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts index 10bb948e513c7..25c730ca9c4c8 100644 --- a/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts +++ b/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts @@ -210,7 +210,7 @@ describe(`createStaticAssetsPathHandler`, () => { await fileMovingDone() expect(vol.existsSync(`public/500.html`)).toEqual(true) - // 404 page is coped so it exists in both locations + // 500 page is coped so it exists in both locations expect(vol.existsSync(`public/foo/500.html`)).toEqual(true) }) }) From 9e9115a3b15b312422963907d1b1c14c16f3762a Mon Sep 17 00:00:00 2001 From: Michal Piechowiak Date: Fri, 10 Nov 2023 10:50:16 +0100 Subject: [PATCH 32/32] move dynamic route path normalization to its own function --- .../src/__tests__/pretty-urls.ts | 27 ++++++++++++++++--- .../gatsby-adapter-netlify/src/pretty-urls.ts | 16 +++++++---- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts b/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts index 25c730ca9c4c8..ad32941243cf7 100644 --- a/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts +++ b/packages/gatsby-adapter-netlify/src/__tests__/pretty-urls.ts @@ -3,6 +3,7 @@ import { vol } from "memfs" import { generatePrettyUrlFilePath, createStaticAssetsPathHandler, + normalizeDynamicRoutePath, } from "../pretty-urls" jest.mock(`fs`, () => jest.requireActual(`memfs`).fs) @@ -21,6 +22,26 @@ describe(`generatePrettyUrlFilePath`, () => { }) }) +describe(`normalizeRoutePath`, () => { + it(`static path return path as-is`, () => { + expect(normalizeDynamicRoutePath(`/foo/`)).toEqual(`/foo/`) + }) + + it(`replaces ':param: with [param]`, () => { + expect(normalizeDynamicRoutePath(`/foo/:param/bar/`)).toEqual( + `/foo/[param]/bar/` + ) + }) + + it(`replaces '*' with [...]`, () => { + expect(normalizeDynamicRoutePath(`/foo/*`)).toEqual(`/foo/[...]`) + }) + + it(`replaces '*named' with [...named]`, () => { + expect(normalizeDynamicRoutePath(`/foo/*named`)).toEqual(`/foo/[...named]`) + }) +}) + describe(`createStaticAssetsPathHandler`, () => { beforeEach(() => { vol.reset() @@ -151,13 +172,13 @@ describe(`createStaticAssetsPathHandler`, () => { createStaticAssetsPathHandler() expect(vol.existsSync(`public/[...wildcard]/index.html`)).toEqual(true) - expect(vol.existsSync(`public/foo/[...].html`)).toEqual(false) + expect(vol.existsSync(`public/foo/[...wildcard].html`)).toEqual(false) - ensureStaticAssetPath(`public/[...wildcard]/index.html`, `/foo/*`) + ensureStaticAssetPath(`public/[...wildcard]/index.html`, `/foo/*wildcard`) await fileMovingDone() - expect(vol.existsSync(`public/foo/[...].html`)).toEqual(true) + expect(vol.existsSync(`public/foo/[...wildcard].html`)).toEqual(true) expect(vol.existsSync(`public/[...wildcard]/index.html`)).toEqual(false) }) diff --git a/packages/gatsby-adapter-netlify/src/pretty-urls.ts b/packages/gatsby-adapter-netlify/src/pretty-urls.ts index d6d20f57c287f..a4658d447df3f 100644 --- a/packages/gatsby-adapter-netlify/src/pretty-urls.ts +++ b/packages/gatsby-adapter-netlify/src/pretty-urls.ts @@ -15,6 +15,16 @@ interface IMoveTask { keepOriginalFile: boolean } +export function normalizeDynamicRoutePath(routePath: string): string { + return ( + routePath + // replace `:param` with `[param]` + .replace(/:([^:/\\]+)/gm, `[$1]`) + // replace `*param` with `[...param]` and `*` with `[...]` + .replace(/\*([^:/\\]*)/gm, `[...$1]`) + ) +} + export function createStaticAssetsPathHandler(): { ensureStaticAssetPath: ( filePath: string, @@ -46,11 +56,7 @@ export function createStaticAssetsPathHandler(): { // dynamic routes syntax use characters that are reserved in a lot of filesystems // so if route is dynamic we should normalize filepath if (routePath.includes(`:`) || routePath.includes(`*`)) { - routePath = routePath - // replace `:param` with `[param]` - .replace(/:([^:/\\]+)/gm, `[$1]`) - // replace `*param` with `[...param]` and `*` with `[...]` - .replace(/\*([^:/\\]*|$)/gm, `[...$1]`) + routePath = normalizeDynamicRoutePath(routePath) isDynamic = true }