Skip to content

⚡ Performance: Page-level optimization#1738

Closed
adriangohjw wants to merge 81 commits intomainfrom
perf/next-renderer
Closed

⚡ Performance: Page-level optimization#1738
adriangohjw wants to merge 81 commits intomainfrom
perf/next-renderer

Conversation

@adriangohjw
Copy link
Copy Markdown
Contributor

@adriangohjw adriangohjw commented Nov 20, 2025

Important

This is dropped because having just 2k+ pages resulted in nextjs build to spike by 10 minutes during the Creating an optimized production build ... phase and require us bumping the CodeBuild instance size from medium to large for the increased RAM usage

Problem

Closes [insert issue #]

Solution

Breaking Changes

  • Yes - this PR contains breaking changes
    • Details ...
  • No - this PR is backwards compatible

Features:

  • Details ...

Improvements:

  • Details ...

Bug Fixes:

  • Details ...

Before & After Screenshots

BEFORE:

AFTER:

Tests

New scripts:

  • script : script details

New dependencies:

  • dependency : dependency details

New dev dependencies:

  • dependency : dependency details

Note

Medium Risk
Introduces a new build-time codegen pipeline that rewrites/duplicates Next.js app/*/page.tsx files and prunes imports/switch cases via AST transforms, which could break routing/rendering if the analysis or transforms are wrong. Also changes public export paths for @opengovsg/isomer-components, requiring consumers to use subpath imports for correct bundling.

Overview
Enables page-level renderer optimization for the Next.js base template by adding a build:prepare step that analyzes public/sitemap.json, generates missing app/.../page.tsx files, and rewrites each page to set STATIC_ROUTE_PERMALINK and prune unused layout/component imports and switch cases (via new scripts/analyze-sitemap.ts, generate-pages.ts, update-pages.ts + Vitest coverage).

Refactors @opengovsg/isomer-components to be more tree-shakeable: adds explicit subpath exports for engine/*, types, and Next template internals, splits getRobotsTxt/getSitemapXml/shouldBlockIndexing into standalone engine modules, and extracts layout/page-content logic into reusable *Skeleton helpers.

Updates the template to stop root-importing @opengovsg/isomer-components, adds ESLint enforcement against root imports, and wires new build steps into tooling/build scripts and CI (new unit tests for isomer-base-template). Also bumps next and updates related lockfile dependencies (e.g. tsx/esbuild).

Written by Cursor Bugbot for commit 69e800b. This will update automatically on new commits. Configure here.

…add-js-extension, modify tsconfig and import paths
…data handling; update ESLint config and package dependencies
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.


# Build the site
echo "Building site..."
# Installing dependencies
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

build.sh deletes scripts directory before build:prepare runs

High Severity

The pre-existing rm -rf scripts cleanup step (line 48) deletes the entire scripts/ directory after running generate-sitemap.js. The newly added npm run build:prepare (line 69) then tries to execute tsx scripts/analyze-sitemap.ts, tsx scripts/generate-pages.ts, and tsx scripts/update-pages.ts — but those files no longer exist. This will crash the Amplify build pipeline for every site using build.sh.

Additional Locations (1)
Fix in Cursor Fix in Web

…in compatibility and improve comment preservation during parsing
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

} catch {
if (permalink === "") {
return null
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Dead code: readSchema checks empty string instead of root

Low Severity

In readSchema, the condition permalink === "" on the catch path is dead code. All permalinks from extractPages come from the sitemap and start with "/" (e.g., the root is "/"), so this condition is never true. The intended check was likely permalink === "/" to skip the redundant fallback retry for the root page, since getSchemaPath("/") already constructs the _index.json path, and the fallback constructs the identical path.

Fix in Cursor Fix in Web

Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

"parentPath" in e && typeof e.parentPath === "string" ? e.parentPath : dir
return path.join(parentPath, e.name)
})
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Incorrect nested file paths on Node 18

Medium Severity

findPageFiles checks for parentPath on Dirent entries, but this property was only added in Node 20.12/21.4. On Node 18 (which is supported per the next peer dependency ^18.18.0), parentPath doesn't exist, so the fallback uses the root dir for every entry. This causes all nested page files (e.g., app/about/page.tsx) to be incorrectly resolved to the root app/page.tsx path. The Dirent.path property (available since Node 18.17) is the correct fallback but is never checked.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants