Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(vercel): allow external redirects #11422

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

wotschofsky
Copy link

Based on issue #11313

Changes

Before:

Redirects with schema defined in astro.config.mjs were converted into paths on the same site: https://google.com -> /https://google.com

After:

External redirects are detected and don't have the project base path prepended.

Testing

The output (esp. .vercel/output/config.json) of building a project with the updated integration was manually inspected.

A test was added

Docs

The Astro docs generally state that supporting external links configured through astro.config.mjs isn't a goal. While the Cloudflare adapter already supports external redirects, the Vercel adapter handled external redirects differently so far. Therefore this could potentially be considered a breaking change.

/cc @withastro/maintainers-docs for feedback!

Copy link

changeset-bot bot commented Jul 5, 2024

🦋 Changeset detected

Latest commit: 1463b0e

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added the pkg: integration Related to any renderer integration (scope) label Jul 5, 2024
Copy link
Member

@Fryuni Fryuni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @wotschofsky! Thanks for opening this PR and congrats for your fist contribution on Astro's codebase, we are glad to have you here 🎉 🎉 🎉

Sorry for the delayed review, the code overall is great and simple. I left two little notes, one for improved readability and another about a potential problem with the proposed solution along with an alternative.

packages/integrations/vercel/src/lib/redirects.ts Outdated Show resolved Hide resolved
Comment on lines +88 to +98
let redirectPath: string;
let forceTrailingSlash = false;

if (route.redirectRoute) {
const pattern = getReplacePattern(route.redirectRoute.segments);
const path = config.trailingSlash === 'always' ? appendForwardSlash(pattern) : pattern;
return pathJoin(config.base, path);
redirectPath = getReplacePattern(route.redirectRoute.segments);
if (config.trailingSlash === 'always') forceTrailingSlash = true;
} else if (typeof route.redirect === 'object') {
return pathJoin(config.base, route.redirect.destination);
redirectPath = route.redirect.destination;
} else {
return pathJoin(config.base, route.redirect || '');
redirectPath = route.redirect || '';
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code seems correct, but it might be more readable if we didn't use mutable state. What about splitting this:

function getRedirectDestination(route: RouteData): string {
	if (route.redirectRoute) {
		return getReplacePattern(route.redirectRoute.segments);
	}
	if (typeof route.redirect === 'object') {
		return route.redirect.destination;
	}
	return route.redirect || '';
}

function getRedirectLocation(route: RouteData, config: AstroConfig): string {
	const redirectDestination = getRedirectDestination(route);
	const forceTrailingSlash = config.trailingSlash === 'always';

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the trailingSlash rule is only considered in the first case. However I don't see a reason not to always apply it. If so forceTrailingSlash can be eliminated completely.

This version has significantly less mutable state. The only option I see for avoiding that completely, that doesn't introduce another level of complexity through a helper function, would be using ternary expression for conditionally appending the slash.

function getDestinationFromRoute(route: RouteData) {
	if (route.redirectRoute) {
		return getReplacePattern(route.redirectRoute.segments);
	}
	if (typeof route.redirect === 'object') {
		return route.redirect.destination;
	}
	return route.redirect || '';
}

function getRedirectLocation(route: RouteData, config: AstroConfig): string {
	let destination = getDestinationFromRoute(route);

	// Is a full URL - do not transform
	if (URL.canParse(destination)) {
		return destination;
	}

	if (config.trailingSlash === 'always') {
		destination = appendForwardSlash(destination);
	}

	return pathJoin(config.base, destination);
}

@Fryuni Fryuni added the pkg: vercel Related to Vercel adapter (scope) label Jul 16, 2024
Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is the correct way to implement the external redirects. The test (and later the docs) shows that users can have external URLs in the astro.config.mjs, which is confusing because by design Astro doesn't support external redirects.

These directs should be passed to the adapter instead.

@Fryuni
Copy link
Member

Fryuni commented Jul 19, 2024

I don't think this is the correct way to implement the external redirects. The test (and later the docs) shows that users can have external URLs in the astro.config.mjs, which is confusing because by design Astro doesn't support external redirects.

This doesn't sound right. Astro itself, without any adapters, works with external redirects. The following configuration works exactly as the user declares when building in SSG without any adapter:

export default defineConfig({
  redirects: {
    '/foo': 'https://example.com'
  }
});

Using another adapter like Node also works and redirects to the external URL with the following config:

export default defineConfig({
  redirects: {
    '/foo': 'https://example.com'
  },
  output: "server",
  adapter: node({
    mode: "standalone"
  })
});

So it does look, from a user's perspective, like a bug on the Vercel adapter that currently would redirect to <your site>/https:/example.com. Which is fixed by this PR.

I know that this is not "officially"/"intentionally" supported (it warned as such when used), but the diverging behavior is weird.

@wotschofsky wotschofsky requested a review from Fryuni July 27, 2024 13:01
Copy link
Member

@alexanderniebuhr alexanderniebuhr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have moved the adapter to https://github.com/withastro/adapters, so this PR can't be merged here anymore.
I would offer to port over the changes to the new repo for you, just let me know if you want me to do that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pkg: integration Related to any renderer integration (scope) pkg: vercel Related to Vercel adapter (scope)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants