From d6b71047227376e3240cae9d0ab1177ba8c81fc1 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 11 May 2023 15:54:10 -0400 Subject: [PATCH] Support in Netlify as well --- packages/astro/src/@types/astro.ts | 7 +++- packages/astro/src/core/build/common.ts | 2 + packages/astro/src/core/build/generate.ts | 12 ++++-- .../src/core/build/plugins/plugin-pages.ts | 3 ++ packages/astro/src/core/config/schema.ts | 2 + packages/astro/src/core/render/core.ts | 9 +++++ .../astro/src/core/routing/manifest/create.ts | 39 +++++++++++++++++++ .../netlify/test/static/redirects.test.js | 8 +++- 8 files changed, 76 insertions(+), 6 deletions(-) diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 56d26415d..da6b593cb 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -447,6 +447,11 @@ export interface AstroUserConfig { */ cacheDir?: string; + /** + * TODO + */ + redirects?: Record; + /** * @docs * @name site @@ -1704,7 +1709,7 @@ export interface AstroPluginOptions { logging: LogOptions; } -export type RouteType = 'page' | 'endpoint'; +export type RouteType = 'page' | 'endpoint' | 'redirect'; export interface RoutePart { content: string; diff --git a/packages/astro/src/core/build/common.ts b/packages/astro/src/core/build/common.ts index 74be830f0..ed0c08d5b 100644 --- a/packages/astro/src/core/build/common.ts +++ b/packages/astro/src/core/build/common.ts @@ -26,6 +26,7 @@ export function getOutFolder( case 'endpoint': return new URL('.' + appendForwardSlash(npath.dirname(pathname)), outRoot); case 'page': + case 'redirect': switch (astroConfig.build.format) { case 'directory': { if (STATUS_CODE_PAGES.has(pathname)) { @@ -51,6 +52,7 @@ export function getOutFile( case 'endpoint': return new URL(npath.basename(pathname), outFolder); case 'page': + case 'redirect': switch (astroConfig.build.format) { case 'directory': { if (STATUS_CODE_PAGES.has(pathname)) { diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 6f12b9275..2741a19cd 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -178,13 +178,17 @@ async function generatePage( .map(({ sheet }) => sheet) .reduce(mergeInlineCss, []); - const pageModule = ssrEntry.pageMap?.get(pageData.component); + let pageModule = ssrEntry.pageMap?.get(pageData.component); const middleware = ssrEntry.middleware; if (!pageModule) { - throw new Error( - `Unable to find the module for ${pageData.component}. This is unexpected and likely a bug in Astro, please report.` - ); + if(pageData.route.type === 'redirect') { + pageModule = { 'default': Function.prototype as any }; + } else { + throw new Error( + `Unable to find the module for ${pageData.component}. This is unexpected and likely a bug in Astro, please report.` + ); + } } if (shouldSkipDraft(pageModule, opts.settings)) { diff --git a/packages/astro/src/core/build/plugins/plugin-pages.ts b/packages/astro/src/core/build/plugins/plugin-pages.ts index 132d03cf8..4efcd8817 100644 --- a/packages/astro/src/core/build/plugins/plugin-pages.ts +++ b/packages/astro/src/core/build/plugins/plugin-pages.ts @@ -35,6 +35,9 @@ export function vitePluginPages(opts: StaticBuildOptions, internals: BuildIntern let imports = []; let i = 0; for (const pageData of eachPageData(internals)) { + if(pageData.route.type === 'redirect') { + continue; + } const variable = `_page${i}`; imports.push(`import * as ${variable} from ${JSON.stringify(pageData.moduleSpecifier)};`); importMap += `[${JSON.stringify(pageData.component)}, ${variable}],`; diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts index fd8d88c4d..a42c0ba79 100644 --- a/packages/astro/src/core/config/schema.ts +++ b/packages/astro/src/core/config/schema.ts @@ -36,6 +36,7 @@ const ASTRO_CONFIG_DEFAULTS: AstroUserConfig & any = { }, vite: {}, legacy: {}, + redirects: {}, experimental: { assets: false, inlineStylesheets: 'never', @@ -133,6 +134,7 @@ export const AstroConfigSchema = z.object({ .optional() .default({}) ), + redirects: z.record(z.string(), z.string()).default(ASTRO_CONFIG_DEFAULTS.redirects), image: z .object({ service: z.object({ diff --git a/packages/astro/src/core/render/core.ts b/packages/astro/src/core/render/core.ts index fd57ad8bc..6f392d0f5 100644 --- a/packages/astro/src/core/render/core.ts +++ b/packages/astro/src/core/render/core.ts @@ -111,6 +111,15 @@ export type RenderPage = { }; export async function renderPage({ mod, renderContext, env, apiContext }: RenderPage) { + if(renderContext.route?.type === 'redirect') { + return new Response(null, { + status: 301, + headers: { + 'location': renderContext.route.redirect! + } + }); + } + // Validate the page component before rendering the page const Component = mod.default; if (!Component) diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index 8c7514969..a4af1583f 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -412,6 +412,45 @@ export function createRouteManifest( }); }); + Object.entries(settings.config.redirects).forEach(([from, to]) => { + const trailingSlash = config.trailingSlash; + + const segments = removeLeadingForwardSlash(from) + .split(path.posix.sep) + .filter(Boolean) + .map((s: string) => { + validateSegment(s); + return getParts(s, from); + }); + + const pattern = getPattern(segments, settings.config.base, trailingSlash); + const generate = getRouteGenerator(segments, trailingSlash); + const pathname = segments.every((segment) => segment.length === 1 && !segment[0].dynamic) + ? `/${segments.map((segment) => segment[0].content).join('/')}` + : null; + const params = segments + .flat() + .filter((p) => p.dynamic) + .map((p) => p.content); + const route = `/${segments + .map(([{ dynamic, content }]) => (dynamic ? `[${content}]` : content)) + .join('/')}`.toLowerCase(); + + + routes.unshift({ + type: 'redirect', + route, + pattern, + segments, + params, + component: '', + generate, + pathname: pathname || void 0, + prerender: false, + redirect: to + }); + }); + return { routes, }; diff --git a/packages/integrations/netlify/test/static/redirects.test.js b/packages/integrations/netlify/test/static/redirects.test.js index 5e0dd12ba..cab238c39 100644 --- a/packages/integrations/netlify/test/static/redirects.test.js +++ b/packages/integrations/netlify/test/static/redirects.test.js @@ -15,6 +15,9 @@ describe('SSG - Redirects', () => { adapter: netlifyStatic(), site: `http://example.com`, integrations: [testIntegration()], + redirects: { + '/other': '/' + } }); await fixture.build(); }); @@ -22,6 +25,9 @@ describe('SSG - Redirects', () => { it('Creates a redirects file', async () => { let redirects = await fixture.readFile('/_redirects'); let parts = redirects.split(/\s+/); - expect(parts).to.deep.equal(['/nope', '/', '301']); + expect(parts).to.deep.equal([ + '/other', '/', '301', + '/nope', '/', '301' + ]); }); });