sst / sst

Build modern full-stack applications on AWS

Home Page:https://sst.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Astro Site Deploy Fail: Trace: RangeError: Maximum call stack size exceeded

fl0wo opened this issue · comments

Discord Msg Link:
https://discord.com/channels/983865673656705025/1068236979659092038/1217354520086974515

Hi guys, I'm having a problem with an Astro deploy :/

Trace: RangeError: Maximum call stack size exceeded
at String.replace ()
at routes.reduce.branches (file:///Users/x/WebstormProjects/starlight-signals/y/node_modules/sst/constructs/util/astroRouteCompressor.js:4:14)

I'm using: https://starlight.astro.build/
And was able to deploy my website using sst so far without issues.

Before I've added this dependency: https://github.com/HiDeoo/starlight-openapi
build ✅
preview ✅
sst deploy ✅

Now using starlight-openapi inside my astro page:
build ✅
preview ✅
sst deploy ❌

I guess has something to do with sst?

To reproduce the error is enough following this Astro integratio
n example:
https://starlight-openapi.vercel.app/getting-started/

In particular the line:
plugins: [
// Generate the OpenAPI documentation pages.
starlightOpenAPI([

Is what triggers the error while deploying, without this line, the deploy works as expected.

I'm atatching my sst.buildMeta.json file inside the dist folder when attempting the deployment.
sst.buildMeta.json

Thank you.

TLDR; Circular route matching pattern causing infinite loop

The OpenAPI plugin is injecting a very greedy route at the root of the site which conflicts with the root level matching route.

Ref: sst.buildMeta.json

{
  "deploymentStrategy": "static",
  "responseMode": "buffer",
  "outputMode": "static",
  "pageResolution": "directory",
  "trailingSlash": "ignore",
  "serverBuildOutputFile": "dist/server/entry.mjs",
  "clientBuildOutputDir": "dist/",
  "clientBuildVersionedSubDir": "_astro",
  "routes": [
    {
      "route": "/_astro/ec.pjkke.css",
      "type": "endpoint",
      "pattern": "/^\\/_astro\\/ec\\.pjkke\\.css$/",
      "prerender": true
    },
    {
      "route": "/_astro/ec.sgewm.js",
      "type": "endpoint",
      "pattern": "/^\\/_astro\\/ec\\.sgewm\\.js$/",
      "prerender": true
    },
    {
      "route": "/_astro/[...slug]",
      "type": "endpoint",
      "pattern": "/^\\/_astro\\/.*?\\/?$/",
      "prerender": true
    },
    {
      "route": "/404",
      "type": "page",
      "pattern": "/^\\/404\\/?$/",
      "prerender": true
    },
    {
      "route": "/[...openapislug]",
      "type": "page",
      "pattern": "/^(?:\\/(.*?))?\\/?$/",
      "prerender": true
    },
    {
      "route": "/[...slug]",
      "type": "page",
      "pattern": "/^(?:\\/(.*?))?\\/?$/",
      "prerender": true
    }
  ],
  "serverRoutes": []
}

Ref: astroRouteCompressor.js

function buildRouteTree(routes, level = 0) {
    const routeTree = routes.reduce((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;
}