diff --git a/.changeset/witty-teachers-film.md b/.changeset/witty-teachers-film.md new file mode 100644 index 000000000000..62e0c79a9ea4 --- /dev/null +++ b/.changeset/witty-teachers-film.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/adapter-netlify': patch +--- + +fix: avoid unnecessary Netlify edge function invocations for static files, which resolves a conflict between Netlify Edge Functions and Netlify Identity diff --git a/packages/adapter-netlify/index.js b/packages/adapter-netlify/index.js index 8fc380cc270f..034acd70ab94 100644 --- a/packages/adapter-netlify/index.js +++ b/packages/adapter-netlify/index.js @@ -14,15 +14,19 @@ import toml from '@iarna/toml'; */ /** + * TODO(serhalp) Replace this custom type with an import from `@netlify/edge-functions`, + * once that type is fixed to include `excludedPath` and `function`. * @typedef {{ * functions: Array< * | { * function: string; * path: string; + * excludedPath?: string | string[]; * } * | { * function: string; * pattern: string; + * excludedPattern?: string | string[]; * } * >; * version: 1; @@ -122,23 +126,6 @@ async function generate_edge_functions({ builder }) { builder.mkdirp('.netlify/edge-functions'); - // Don't match the static directory - const pattern = '^/.*$'; - - // Go doesn't support lookarounds, so we can't do this - // const pattern = appDir ? `^/(?!${escapeStringRegexp(appDir)}).*$` : '^/.*$'; - - /** @type {HandlerManifest} */ - const edge_manifest = { - functions: [ - { - function: 'render', - pattern - } - ], - version: 1 - }; - builder.log.minor('Generating Edge Function...'); const relativePath = posix.relative(tmp, builder.getServerDirectory()); @@ -153,12 +140,43 @@ async function generate_edge_functions({ builder }) { relativePath }); - writeFileSync( - `${tmp}/manifest.js`, - `export const manifest = ${manifest};\n\nexport const prerendered = new Set(${JSON.stringify( - builder.prerendered.paths - )});\n` - ); + writeFileSync(`${tmp}/manifest.js`, `export const manifest = ${manifest};\n`); + + /** @type {{ assets: Set }} */ + const { assets } = (await import(`${tmp}/manifest.js`)).manifest; + + const path = '/*'; + // We only need to specify paths without the trailing slash because + // Netlify will handle the optional trailing slash for us + const excludedPath = [ + // Contains static files + `/${builder.getAppPath()}/*`, + ...builder.prerendered.paths, + ...Array.from(assets).flatMap((asset) => { + if (asset.endsWith('/index.html')) { + const dir = asset.replace(/\/index\.html$/, ''); + return [ + `${builder.config.kit.paths.base}/${asset}`, + `${builder.config.kit.paths.base}/${dir}` + ]; + } + return `${builder.config.kit.paths.base}/${asset}`; + }), + // Should not be served by SvelteKit at all + '/.netlify/*' + ]; + + /** @type {HandlerManifest} */ + const edge_manifest = { + functions: [ + { + function: 'render', + path, + excludedPath + } + ], + version: 1 + }; await esbuild.build({ entryPoints: [`${tmp}/entry.js`], diff --git a/packages/adapter-netlify/internal.d.ts b/packages/adapter-netlify/internal.d.ts index 450140da9871..55da8ba1fbf5 100644 --- a/packages/adapter-netlify/internal.d.ts +++ b/packages/adapter-netlify/internal.d.ts @@ -6,5 +6,4 @@ declare module 'MANIFEST' { import { SSRManifest } from '@sveltejs/kit'; export const manifest: SSRManifest; - export const prerendered: Set; } diff --git a/packages/adapter-netlify/src/edge.js b/packages/adapter-netlify/src/edge.js index 85f75483ea35..f6aeb2b25655 100644 --- a/packages/adapter-netlify/src/edge.js +++ b/packages/adapter-netlify/src/edge.js @@ -1,8 +1,7 @@ import { Server } from '0SERVER'; -import { manifest, prerendered } from 'MANIFEST'; +import { manifest } from 'MANIFEST'; const server = new Server(manifest); -const prefix = `/${manifest.appPath}/`; const initialized = server.init({ // @ts-ignore @@ -15,12 +14,6 @@ const initialized = server.init({ * @returns { Promise } */ export default async function handler(request, context) { - if (is_static_file(request)) { - // Static files can skip the handler - // TODO can we serve _app/immutable files with an immutable cache header? - return; - } - await initialized; return server.respond(request, { platform: { context }, @@ -29,33 +22,3 @@ export default async function handler(request, context) { } }); } - -/** - * @param {Request} request - */ -function is_static_file(request) { - const url = new URL(request.url); - - // Assets in the app dir - if (url.pathname.startsWith(prefix)) { - return true; - } - - // prerendered pages and index.html files - const pathname = url.pathname.replace(/\/$/, ''); - let file = pathname.substring(1); - - try { - file = decodeURIComponent(file); - } catch { - // ignore - } - - return ( - manifest.assets.has(file) || - manifest.assets.has(file + '/index.html') || - file in manifest._.server_assets || - file + '/index.html' in manifest._.server_assets || - prerendered.has(pathname || '/') - ); -}