Skip to content

Commit

Permalink
astro: move “astroRouteCompressor” into “astro.ts”
Browse files Browse the repository at this point in the history
  • Loading branch information
fwang committed Feb 28, 2024
1 parent e99ed06 commit 74dd225
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 128 deletions.
153 changes: 120 additions & 33 deletions pkg/platform/src/components/aws/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { Cdn } from "./cdn.js";
import { Bucket } from "./bucket.js";
import { Component, transform } from "./../component.js";
import { Hint } from "./../hint.js";
import { getStringifiedRouteTree } from "./helpers/astroRouteCompressor.js";
import { Link } from "../link.js";
import { Cache } from "./providers/cache.js";

Expand Down Expand Up @@ -553,38 +552,6 @@ export class Astro extends Component implements Link.Linkable {
return validatePlan(transform(args?.transform?.plan, plan));
});
}

function useCloudFrontRoutingInjection({
routes,
pageResolution,
}: BuildMetaConfig) {
return `
var routeData = ${getStringifiedRouteTree(routes)};
var findMatch = (path, routeData) => {
var match = routeData.find((route) => route[0].test(path));
return match && Array.isArray(match[1]) ? findMatch(path, match[1]) : match;
};
var matchedRoute = findMatch(request.uri, routeData);
if (matchedRoute) {
if (!matchedRoute[1] && !/^.*\\.[^\\/]+$/.test(request.uri)) {
${
pageResolution === "file"
? `request.uri = request.uri === "/" ? "/index.html" : request.uri.replace(/\\/?$/, ".html");`
: `request.uri = request.uri.replace(/\\/?$/, "/index.html");`
}
} else if (matchedRoute[1] === 2) {
var redirectPath = matchedRoute[2];
matchedRoute[0].exec(request.uri).forEach((match, index) => {
redirectPath = redirectPath.replace(\`\\\${\${index}}\`, match);
});
return {
statusCode: matchedRoute[3] || 308,
headers: { location: { value: redirectPath } },
};
}
}`;
}
}

/**
Expand Down Expand Up @@ -624,3 +591,123 @@ export class Astro extends Component implements Link.Linkable {
};
}
}

type TreeNode = {
branches: Record<string, TreeNode>;
nodes: BuildMetaConfig["routes"][number][];
};

type FlattenedRoute =
| [string] // Page with prerendering
| [string, 1] // Endpoint with prerendering
| [string, 2, string | undefined, number | undefined]; // Redirect
type FlattenedRouteTree = Array<FlattenedRoute | [string, FlattenedRouteTree]>;

function useCloudFrontRoutingInjection(buildMetadata: BuildMetaConfig) {
const tree = buildRouteTree(buildMetadata.routes);
const flatTree = flattenRouteTree(tree);
const stringifiedTree = stringifyFlattenedRouteTree(flatTree);
return `
var routeData = ${stringifiedTree};
var findMatch = (path, routeData) => {
var match = routeData.find((route) => route[0].test(path));
return match && Array.isArray(match[1]) ? findMatch(path, match[1]) : match;
};
var matchedRoute = findMatch(request.uri, routeData);
if (matchedRoute) {
if (!matchedRoute[1] && !/^.*\\.[^\\/]+$/.test(request.uri)) {
${
buildMetadata.pageResolution === "file"
? `request.uri = request.uri === "/" ? "/index.html" : request.uri.replace(/\\/?$/, ".html");`
: `request.uri = request.uri.replace(/\\/?$/, "/index.html");`
}
} else if (matchedRoute[1] === 2) {
var redirectPath = matchedRoute[2];
matchedRoute[0].exec(request.uri).forEach((match, index) => {
redirectPath = redirectPath.replace(\`\\\${\${index}}\`, match);
});
return {
statusCode: matchedRoute[3] || 308,
headers: { location: { value: redirectPath } },
};
}
}`;
}

function buildRouteTree(routes: BuildMetaConfig["routes"], level = 0) {
const routeTree = routes.reduce<TreeNode>(
(tree, route) => {
const routePatternWithoutCaptureGroups = route.pattern
.replace(/\((?:\?:)?(.*?[^\\])\)/g, (_, content) => content.trim())
.replace(/\/\^/g, "")
.replace(/\$\//g, "");
const routeParts = routePatternWithoutCaptureGroups
.split(/(?=\\\/)/g)
.filter((part) => part !== "/^" && part !== "/$/");

tree.branches[routeParts[level]] = tree.branches[routeParts[level]] || {
branches: {},
nodes: [],
};
tree.branches[routeParts[level]].nodes.push(route);
return tree;
},
{ branches: {}, nodes: [] },
);

for (const [key, branch] of Object.entries(routeTree.branches)) {
if (
!branch.nodes.some((node) => node.prerender || node.type === "redirect")
) {
delete routeTree.branches[key];
} else if (branch.nodes.length > 1) {
routeTree.branches[key] = buildRouteTree(branch.nodes, level + 1);
branch.nodes = [];
}
}

return routeTree;
}

function flattenRouteTree(tree: TreeNode, parentKey = "") {
const flatTree: FlattenedRouteTree = [];
for (const [key, branch] of Object.entries(tree.branches)) {
if (branch.nodes.length === 1) {
const node = branch.nodes[0];
if (node.type === "page") {
flatTree.push([node.pattern]);
}
if (node.type === "endpoint") {
flatTree.push([node.pattern, 1]);
} else if (node.type === "redirect") {
flatTree.push([
node.pattern,
2,
node.redirectPath,
node.redirectStatus,
]);
}
} else {
const flatKey = parentKey + key;
flatTree.push([flatKey, flattenRouteTree(branch, flatKey)]);
}
}
return flatTree;
}

function stringifyFlattenedRouteTree(tree: FlattenedRouteTree): string {
return `[${tree
.map((tuple) => {
if (Array.isArray(tuple[1])) {
return `[/^${tuple[0]}/,${stringifyFlattenedRouteTree(tuple[1])}]`;
}
if (typeof tuple[1] === "undefined") {
return `[${tuple[0]}]`;
} else if (tuple[1] === 1) {
return `[${tuple[0]},1]`;
}
return `[${tuple[0]},2,"${tuple[2]}"${tuple[3] ? `,${tuple[3]}` : ""}]`;
})
.join(",")}]`;
}
95 changes: 0 additions & 95 deletions pkg/platform/src/components/aws/helpers/astroRouteCompressor.ts

This file was deleted.

0 comments on commit 74dd225

Please sign in to comment.