Skip to content

Conversation

@Sidnioulz
Copy link
Member

@Sidnioulz Sidnioulz commented Dec 1, 2025

What I did

Adds a sitemap.xml to help with the indexing of Storybook instances.

Part of an experiment on SEO improvements through the About page.

TODO list

Now

  • create canary
  • apply to icons pkg

Later

  • fix E2E builds so sitemap.spec.ts can run
  • fix main.ts's build.siteUrl not being used
  • add documentation for the option (main.ts + --site-url + ENV var)
  • if experiment works, add onboarding/init step to encourage setting url?
  • review deploy/prod documentation to encourage use of sitemap?
  • change getEnvConfig in bin/core to read Netlify/Vercel/GH pages URL env vars
  • encourage chromatic to set SBCONFIG_SITE_URL env var for permabuilds?

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

  1. Run local instance
  2. Visit http://localhost:6006/sitemap.xml

Documentation

Caution

Documentation is missing for now.

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This pull request has been released as version 0.0.0-pr-33243-sha-278c6240. Try it out in a new sandbox by running npx [email protected] sandbox or in an existing project with npx [email protected] upgrade.

More information
Published version 0.0.0-pr-33243-sha-278c6240
Triggered by @shilman
Repository storybookjs/storybook
Branch sidnioulz/growth-exp-sitemap
Commit 278c6240
Datetime Mon Dec 8 06:22:56 UTC 2025 (1765174976)
Workflow run 20018778053

To request a new release of this pull request, mention the @storybookjs/core team.

core team members can create a new canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=33243

Summary by CodeRabbit

  • New Features

    • Added a /sitemap.xml endpoint that returns a gzipped XML sitemap of stories and docs for SEO.
    • Sitemap generation added to static builds and the dev server, with configurable site URL and cached gzipped output.
  • Tests

    • Added unit/integration tests covering sitemap generation, headers, compression, host/protocol handling, caching, debounce/simultaneous access, and an end-to-end sitemap gzip/format test.
  • Chores

    • Added a runtime dependency for sitemap generation and a new CLI/build option to supply site URL.

✏️ Tip: You can customize this high-level summary in your review settings.

@Sidnioulz Sidnioulz requested a review from shilman December 1, 2025 13:54
@Sidnioulz Sidnioulz added feature request ci:normal experiment This issue/PR is an experiment; it doesn't always follow production standards but will be monitored. labels Dec 1, 2025
@nx-cloud
Copy link

nx-cloud bot commented Dec 1, 2025

View your CI Pipeline Execution ↗ for commit 48d1d32

Command Status Duration Result
nx run-many -t check -c production --parallel=7 ✅ Succeeded 1m 1s View ↗
nx run-many -t compile -c production --parallel=3 ✅ Succeeded 3m 21s View ↗

☁️ Nx Cloud last updated this comment at 2025-12-08 08:40:47 UTC

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 1, 2025

📝 Walkthrough

Walkthrough

Adds sitemap generation and serving: new runtime dependency, build-time sitemap extraction, a dev-server /sitemap.xml route that serves gzipped cached sitemaps, CLI/siteUrl plumbing, type updates, and unit + e2e tests validating gzip, XML structure, hosts/protocols, and caching.

Changes

Cohort / File(s) Summary
Dependency
code/core/package.json
Added runtime dependency sitemap version ^9.0.0.
Sitemap runtime & public API
code/core/src/core-server/utils/stories-json.ts
Implemented sitemap generation (SitemapStream + gzip), in-memory cached gzipped sitemap, useSitemap route handler, extractSitemap for build-time output, and extended useStoriesJson signature to accept options.
Dev server integration
code/core/src/core-server/dev-server.ts
Registered useSitemap(...) during dev server setup (passes initialized index generator and options).
Build/static integration
code/core/src/core-server/build-static.ts
Added extractSitemap invocation to static build flow; BuildStaticStandaloneOptions extended with optional siteUrl.
CLI and config plumbing
code/core/src/bin/core.ts, code/core/src/cli/build.ts
Added --site-url <string> build option, environment variable mappings, and passed siteUrl through build options.
Story index generator wiring
code/core/src/core-server/utils/getStoryIndexGenerator.ts
Propagated options into useStoriesJson invocation so sitemap logic can compute host/port.
Types update
code/core/src/types/modules/core-common.ts
Added optional siteUrl?: string to TestBuildConfig (commented for sitemap computation).
CLI util behavior
code/core/src/common/utils/cli.ts
Broadened getEnvConfig types and logic to accept string or string[] env-name candidates and pick the first present.
Unit tests (sitemap behavior)
code/core/src/core-server/utils/stories-json.test.ts
Extended tests to pass options, expect two route registrations, and cover sitemap: gzipped response, headers, XML content, host/protocol handling, caching, exclusion of test stories, debounce/concurrency. Added gunzipSync import and defaultOptions.
E2E tests
code/e2e-tests/sitemap.spec.ts
Added Playwright test fetching /sitemap.xml, asserting 200, application/xml, gzip, and verifying decompressed XML structure, <loc>, <lastmod>, and <priority> elements.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client
    participant DevServer as Storybook Server
    participant IndexGen as StoryIndexGenerator
    participant SitemapGen as SitemapStream+Gzip
    participant Cache as cachedSitemap

    Client->>DevServer: GET /sitemap.xml
    alt cached sitemap exists
        DevServer->>Cache: read cached buffer
        Cache-->>DevServer: gzipped buffer
        DevServer-->>Client: 200 + application/xml + content-encoding:gzip + buffer
    else cached sitemap missing
        DevServer->>IndexGen: request story entries
        IndexGen-->>DevServer: story list
        DevServer->>SitemapGen: build XML & gzip from entries (+ static pages)
        SitemapGen-->>DevServer: gzipped buffer
        DevServer->>Cache: store gzipped buffer
        DevServer-->>Client: 200 + application/xml + content-encoding:gzip + buffer
    end
    Note over DevServer,IndexGen: File watcher / index updates invalidate cache -> triggers regeneration on next request
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • code/core/src/core-server/utils/stories-json.ts: correctness of SitemapStream pipeline, gzip handling, headers, caching semantics, concurrency/debounce, and memory usage.
    • Public API change: useStoriesJson(options) signature—ensure all callers and types are updated (e.g., getStoryIndexGenerator.ts).
    • Build/static flow: extractSitemap usage, siteUrl propagation, and BuildStaticStandaloneOptions type change.
    • Tests: potential flakiness in concurrency/debounce unit tests and e2e gzip/XML assertions.
✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
code/core/src/core-server/utils/stories-json.ts (2)

32-69: Sitemap cache integration with file/config watchers looks solid

Introducing cachedSitemap in the useStoriesJson closure and clearing it from both watchStorySpecifiers and watchConfig callbacks keeps the sitemap in sync with story and preview changes without recomputing on every request. If there are other code paths that can invalidate the StoryIndex outside these watchers, consider also clearing cachedSitemap in those paths (e.g., on a generic STORY_INDEX_INVALIDATED listener) to avoid any chance of stale sitemap content.


83-132: /sitemap.xml implementation correctly streams, compresses, and caches sitemap entries

The /sitemap.xml handler uses SitemapStream + createGzip() with an in-memory cached buffer, sets appropriate Content-Type/Content-Encoding headers, includes both static settings URLs and dynamic docs/story URLs (skipping subtype === 'test'), and safely reuses the cached gzip across requests. As a minor enhancement, you could optionally respect Accept-Encoding and fall back to an uncompressed response for clients that don’t advertise gzip support, though for search engine crawlers this is unlikely to matter.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 33cc93d and 5e317fe.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (5)
  • code/core/package.json (1 hunks)
  • code/core/src/core-server/utils/getStoryIndexGenerator.ts (1 hunks)
  • code/core/src/core-server/utils/stories-json.test.ts (8 hunks)
  • code/core/src/core-server/utils/stories-json.ts (3 hunks)
  • code/e2e-tests/sitemap.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESLint and Prettier configurations that are enforced in the codebase

Files:

  • code/core/package.json
  • code/core/src/core-server/utils/getStoryIndexGenerator.ts
  • code/core/src/core-server/utils/stories-json.test.ts
  • code/e2e-tests/sitemap.spec.ts
  • code/core/src/core-server/utils/stories-json.ts
code/**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier with --write flag to format code before committing
Run ESLint with yarn lint:js:cmd to check for linting issues and fix errors before committing

Files:

  • code/core/package.json
  • code/core/src/core-server/utils/getStoryIndexGenerator.ts
  • code/core/src/core-server/utils/stories-json.test.ts
  • code/e2e-tests/sitemap.spec.ts
  • code/core/src/core-server/utils/stories-json.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode

Files:

  • code/core/src/core-server/utils/getStoryIndexGenerator.ts
  • code/core/src/core-server/utils/stories-json.test.ts
  • code/e2e-tests/sitemap.spec.ts
  • code/core/src/core-server/utils/stories-json.ts
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{ts,tsx,js,jsx,mjs}: Use server-side logger from 'storybook/internal/node-logger' for Node.js code
Use client-side logger from 'storybook/internal/client-logger' for browser code
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/core-server/utils/getStoryIndexGenerator.ts
  • code/core/src/core-server/utils/stories-json.test.ts
  • code/e2e-tests/sitemap.spec.ts
  • code/core/src/core-server/utils/stories-json.ts
code/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions that need to be tested from their modules

Files:

  • code/core/src/core-server/utils/getStoryIndexGenerator.ts
  • code/core/src/core-server/utils/stories-json.test.ts
  • code/e2e-tests/sitemap.spec.ts
  • code/core/src/core-server/utils/stories-json.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{test,spec}.{ts,tsx}: Test files should follow the naming pattern *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx
Follow the spy mocking rules defined in .cursor/rules/spy-mocking.mdc for consistent mocking patterns with Vitest

Files:

  • code/core/src/core-server/utils/stories-json.test.ts
  • code/e2e-tests/sitemap.spec.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/spy-mocking.mdc)

**/*.test.{ts,tsx,js,jsx}: Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests
Place all mocks at the top of the test file before any test cases
Use vi.mocked() to type and access the mocked functions in Vitest tests
Implement mock behaviors in beforeEach blocks in Vitest tests
Mock all required dependencies that the test subject uses
Each mock implementation should return a Promise for async functions in Vitest
Mock implementations should match the expected return type of the original function
Mock all required properties and methods that the test subject uses in Vitest tests
Avoid direct function mocking without vi.mocked() in Vitest tests
Avoid mock implementations outside of beforeEach blocks in Vitest tests
Avoid mocking without the spy: true option in Vitest tests
Avoid inline mock implementations within test cases in Vitest tests
Avoid mocking only a subset of required dependencies in Vitest tests
Mock at the highest level of abstraction needed in Vitest tests
Keep mock implementations simple and focused in Vitest tests
Use type-safe mocking with vi.mocked() in Vitest tests
Document complex mock behaviors in Vitest tests
Group related mocks together in Vitest tests

Files:

  • code/core/src/core-server/utils/stories-json.test.ts
code/**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{test,spec}.{ts,tsx,js,jsx}: Write meaningful unit tests that actually import and call the functions being tested
Mock external dependencies using vi.mock() for file system, loggers, and other external dependencies in tests
Aim for high test coverage of business logic (75%+ for statements/lines) using coverage reports

Files:

  • code/core/src/core-server/utils/stories-json.test.ts
  • code/e2e-tests/sitemap.spec.ts
🧠 Learnings (17)
📚 Learning: 2025-11-28T14:50:24.872Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.872Z
Learning: Applies to code/**/*.{test,spec}.{ts,tsx,js,jsx} : Mock external dependencies using vi.mock() for file system, loggers, and other external dependencies in tests

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Implement mock behaviors in `beforeEach` blocks in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking only a subset of required dependencies in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Document complex mock behaviors in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mock implementations outside of `beforeEach` blocks in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Group related mocks together in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use `vi.mock()` with the `spy: true` option for all package and file mocks in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required properties and methods that the test subject uses in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use `vi.mocked()` to type and access the mocked functions in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Use type-safe mocking with `vi.mocked()` in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking without the `spy: true` option in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Each mock implementation should return a Promise for async functions in Vitest

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid direct function mocking without `vi.mocked()` in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Keep mock implementations simple and focused in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:31.838Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T17:49:31.838Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Follow the spy mocking rules defined in `.cursor/rules/spy-mocking.mdc` for consistent mocking patterns with Vitest

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid inline mock implementations within test cases in Vitest tests

Applied to files:

  • code/core/src/core-server/utils/stories-json.test.ts
📚 Learning: 2025-11-28T14:50:24.872Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.872Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx,mjs} : Use server-side logger from 'storybook/internal/node-logger' for Node.js code

Applied to files:

  • code/core/src/core-server/utils/stories-json.ts
🧬 Code graph analysis (1)
code/core/src/core-server/utils/stories-json.test.ts (1)
code/core/src/core-server/utils/stories-json.ts (1)
  • useStoriesJson (32-133)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: normal
  • GitHub Check: nx
  • GitHub Check: nx
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: nx
🔇 Additional comments (4)
code/core/package.json (1)

321-321: sitemap dependency addition aligns with new /sitemap.xml endpoint

stories-json.ts imports SitemapStream and streamToPromise from sitemap, so adding "sitemap": "^9.0.0" is consistent and in the right place in the manifest. Please confirm this version is compatible with your supported Node/bundler targets and passes your usual dependency/security checks.

code/core/src/core-server/utils/getStoryIndexGenerator.ts (1)

35-43: Forwarding options into useStoriesJson keeps server configuration consistent

Passing the same options object used to build the StoryIndexGenerator into useStoriesJson ensures the sitemap route can reliably derive host/port/protocol from a single source of truth without additional plumbing.

code/core/src/core-server/utils/stories-json.test.ts (1)

2-3: Test setup changes correctly exercise the new sitemap-aware useStoriesJson API

Importing gunzipSync, introducing a shared defaultOptions, and passing options into all useStoriesJson calls keeps the tests explicit about server host/port/protocol and in sync with the added /sitemap.xml route (reflected in the updated use call counts). This is a clean way to adapt the existing suites to the new API surface.

Also applies to: 83-89, 102-117, 481-506, 515-636

code/e2e-tests/sitemap.spec.ts (1)

1-46: End-to-end sitemap.xml test provides good coverage of headers and content

The Playwright spec verifies status, XML + gzip headers, decompresses the response, and asserts both structural markers (<urlset>, </urlset>) and representative doc/story/settings URLs along with <lastmod> and <priority>. This gives solid end-to-end confidence that the new /sitemap.xml endpoint behaves as intended in a real browser environment.

Comment on lines +639 to +784
describe('Sitemap endpoint', () => {
beforeEach(() => {
use.mockClear();
end.mockClear();
});

it('generates sitemap with http://localhost', async () => {
const mockServerChannel = { emit: vi.fn() } as any as ServerChannel;
useStoriesJson({
app,
serverChannel: mockServerChannel,
workingDir,
normalizedStories,
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
options: defaultOptions,
});

expect(use).toHaveBeenCalledTimes(2);
const sitemapRoute = use.mock.calls[1][1];

await sitemapRoute(request, response);

expect(end).toHaveBeenCalledTimes(1);
expect(response.setHeader).toHaveBeenCalledWith('Content-Type', 'application/xml');
expect(response.setHeader).toHaveBeenCalledWith('Content-Encoding', 'gzip');

const gzippedBuffer = end.mock.calls[0][0];
const decompressed = gunzipSync(gzippedBuffer).toString('utf-8');

expect(decompressed).toContain('<?xml version="1.0" encoding="UTF-8"?>');
expect(decompressed).toContain('<urlset');
expect(decompressed).toContain('http://localhost:6006');
expect(decompressed).toContain('/?path=/docs/a--metaof');
expect(decompressed).toContain('/?path=/story/a--story-one');
expect(decompressed).toContain('/?path=/settings/about');
expect(decompressed).toContain('/?path=/settings/whats-new');
expect(decompressed).toContain('/?path=/settings/guide');
expect(decompressed).toContain('/?path=/settings/shortcuts');
});

it('generates sitemap with https protocol', async () => {
const mockServerChannel = { emit: vi.fn() } as any as ServerChannel;
const httpsOptions = { ...defaultOptions, https: true };

useStoriesJson({
app,
serverChannel: mockServerChannel,
workingDir,
normalizedStories,
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
options: httpsOptions,
});

const sitemapRoute = use.mock.calls[1][1];
await sitemapRoute(request, response);

const gzippedBuffer = end.mock.calls[0][0];
const decompressed = gunzipSync(gzippedBuffer).toString('utf-8');

expect(decompressed).toContain('https://localhost:6006');
expect(decompressed).not.toContain('http://localhost:6006');
});

it('generates sitemap for production website (example.com)', async () => {
const mockServerChannel = { emit: vi.fn() } as any as ServerChannel;
const productionOptions = {
host: 'example.com',
port: 443,
https: true,
initialPath: '',
} as any;

useStoriesJson({
app,
serverChannel: mockServerChannel,
workingDir,
normalizedStories,
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
options: productionOptions,
});

const sitemapRoute = use.mock.calls[1][1];
await sitemapRoute(request, response);

const gzippedBuffer = end.mock.calls[0][0];
const decompressed = gunzipSync(gzippedBuffer).toString('utf-8');

expect(decompressed).toContain('https://example.com');
expect(decompressed).toContain('/?path=/docs/a--metaof');
expect(decompressed).toContain('/?path=/story/a--story-one');
});

it('uses cached sitemap on subsequent requests', async () => {
const mockServerChannel = { emit: vi.fn() } as any as ServerChannel;
useStoriesJson({
app,
serverChannel: mockServerChannel,
workingDir,
normalizedStories,
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
options: defaultOptions,
});

const sitemapRoute = use.mock.calls[1][1];

// First request
await sitemapRoute(request, response);
const firstBuffer = end.mock.calls[0][0] as Buffer;

// Second request
const secondResponse = {
...response,
end: vi.fn(),
setHeader: vi.fn(),
on: vi.fn(),
};
await sitemapRoute(request, secondResponse);
const secondBuffer = secondResponse.end.mock.calls[0][0] as Buffer;

// Should return the same cached buffer
expect(firstBuffer).toBe(secondBuffer);
});

it('excludes test stories from sitemap', async () => {
const mockServerChannel = { emit: vi.fn() } as any as ServerChannel;
useStoriesJson({
app,
serverChannel: mockServerChannel,
workingDir,
normalizedStories,
initializedStoryIndexGenerator: getInitializedStoryIndexGenerator(),
options: defaultOptions,
});

const sitemapRoute = use.mock.calls[1][1];
await sitemapRoute(request, response);

const gzippedBuffer = end.mock.calls[0][0];
const decompressed = gunzipSync(gzippedBuffer).toString('utf-8');

// Verify test stories are not included
// Note: If test stories exist in mock data with subtype: 'test', they should not appear
expect(decompressed).toContain('/?path=/story/a--story-one');
expect(decompressed).toContain('/?path=/docs/a--metaof');
});
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Sitemap endpoint tests are strong overall; tighten the “excludes test stories” assertion

The new sitemap tests nicely validate hostname/protocol handling, presence of key paths, and cache reuse. However, in it('excludes test stories from sitemap') you only assert that known non-test doc/story URLs are present; you never assert that any “test” stories are actually missing, and the current mock index may not include entries with subtype: 'test'. To make this test meaningful, either (a) add a fixture entry with subtype: 'test' and assert that its /story/ URL is not present in the decompressed sitemap, or (b) rename the test to reflect that it’s only checking inclusion of specific non-test entries.

🤖 Prompt for AI Agents
In code/core/src/core-server/utils/stories-json.test.ts around lines 639 to 784,
the "excludes test stories from sitemap" test never verifies that a story with
subtype: 'test' is actually excluded; update the test to either add a mock story
entry with subtype: 'test' to normalizedStories (so it would generate a
/?path=/story/... URL if not excluded) and assert that the decompressed sitemap
does NOT contain that test-story path, or change the test name to reflect it
only asserts presence of known non-test entries; preferred fix: add a test-only
fixture story with subtype: 'test' in the normalizedStories passed into
useStoriesJson and add an
expect(decompressed).not.toContain('/?path=/story/<test-story-id>') assertion to
prove exclusion.

@storybook-app-bot
Copy link

storybook-app-bot bot commented Dec 1, 2025

Package Benchmarks

Commit: 48d1d32, ran on 8 December 2025 at 08:37:02 UTC

The following packages have significant changes to their size or dependencies:

storybook

Before After Difference
Dependency count 39 39 0
Self size 20.52 MB 20.59 MB 🚨 +65 KB 🚨
Dependency size 16.41 MB 16.41 MB 0 B
Bundle Size Analyzer Link Link

@storybook/cli

Before After Difference
Dependency count 173 173 0
Self size 774 KB 774 KB 🎉 -68 B 🎉
Dependency size 67.62 MB 67.69 MB 🚨 +64 KB 🚨
Bundle Size Analyzer Link Link

@storybook/codemod

Before After Difference
Dependency count 166 166 0
Self size 30 KB 30 KB 0 B
Dependency size 66.19 MB 66.26 MB 🚨 +65 KB 🚨
Bundle Size Analyzer Link Link

create-storybook

Before After Difference
Dependency count 40 40 0
Self size 1000 KB 1000 KB 🎉 -67 B 🎉
Dependency size 36.93 MB 36.99 MB 🚨 +65 KB 🚨
Bundle Size Analyzer node node


const now = new Date();

// static settings pages; those with backlinks to storybook.js.org are the most important.
Copy link
Member Author

Choose a reason for hiding this comment

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


export interface TestBuildConfig {
/** URL of the deployed website (needed for sitemap computation). */
siteUrl?: string;
Copy link
Member Author

Choose a reason for hiding this comment

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

  • Find out how to get those build options merged with the SB cli build command

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
code/core/src/core-server/utils/stories-json.ts (1)

101-116: Unused options parameter in useStoriesJson.

The options: Options parameter was added to the function signature (line 115) but is never used within the function body. Either remove it or use it as intended.

 export function useStoriesJson({
   app,
   initializedStoryIndexGenerator,
   workingDir = process.cwd(),
   configDir,
   serverChannel,
   normalizedStories,
-  options,
 }: {
   app: Polka;
   initializedStoryIndexGenerator: Promise<StoryIndexGenerator>;
   serverChannel: ServerChannel;
   workingDir?: string;
   configDir?: string;
   normalizedStories: NormalizedStoriesSpecifier[];
-  options: Options;
 }) {
🧹 Nitpick comments (4)
code/core/src/cli/build.ts (1)

12-12: Minor: Redundant fallback to undefined.

The || undefined is unnecessary since cliOptions.siteUrl will already be undefined if not provided, and falsy values like empty strings would also become undefined (which may or may not be intended behavior).

-    siteUrl: cliOptions.siteUrl || undefined,
+    siteUrl: cliOptions.siteUrl,
code/core/src/core-server/utils/stories-json.ts (3)

24-60: Consider renaming isProduction parameter for clarity.

The parameter name isProduction is misleading—it actually controls whether to skip gzip compression (true = no gzip for static file output, false = gzip for dev server streaming). A name like skipGzip or forStaticFile would better convey the intent.

 async function generateSitemap(
   initializedStoryIndexGenerator: Promise<StoryIndexGenerator>,
   networkAddress: string,
-  isProduction: boolean
+  skipGzip: boolean
 ): Promise<Buffer | undefined> {
   const generator = await initializedStoryIndexGenerator;
   const index = await generator.getIndex();

   const smStream = new SitemapStream({ hostname: networkAddress });
-  // const smStream = new SitemapStream();
-  const pipeline = isProduction ? smStream : smStream.pipe(createGzip());
+  const pipeline = skipGzip ? smStream : smStream.pipe(createGzip());

33-33: Remove commented-out code.

Line 33 contains commented-out code that should be removed.

   const smStream = new SitemapStream({ hostname: networkAddress });
-  // const smStream = new SitemapStream();
   const pipeline = isProduction ? smStream : smStream.pipe(createGzip());

22-22: Consider adding a reset function for test isolation.

The cachedSitemap variable is module-level mutable state. While the cache includes invalidation logic via file watchers (triggered when stories or preview config changes), there is no explicit mechanism for tests to reset the cache between test runs. In test environments with parallel execution or multiple test files, this could lead to stale cache state being shared across tests. Consider exporting a function like __resetCacheForTesting() to allow tests to clear the cache explicitly, or document this cache behavior and its invalidation triggers.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e78833 and 278c624.

📒 Files selected for processing (6)
  • code/core/src/bin/core.ts (2 hunks)
  • code/core/src/cli/build.ts (1 hunks)
  • code/core/src/core-server/build-static.ts (2 hunks)
  • code/core/src/core-server/dev-server.ts (2 hunks)
  • code/core/src/core-server/utils/stories-json.ts (4 hunks)
  • code/core/src/types/modules/core-common.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESLint and Prettier configurations that are enforced in the codebase

Files:

  • code/core/src/types/modules/core-common.ts
  • code/core/src/bin/core.ts
  • code/core/src/core-server/dev-server.ts
  • code/core/src/cli/build.ts
  • code/core/src/core-server/build-static.ts
  • code/core/src/core-server/utils/stories-json.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode

Files:

  • code/core/src/types/modules/core-common.ts
  • code/core/src/bin/core.ts
  • code/core/src/core-server/dev-server.ts
  • code/core/src/cli/build.ts
  • code/core/src/core-server/build-static.ts
  • code/core/src/core-server/utils/stories-json.ts
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{ts,tsx,js,jsx,mjs}: Use server-side logger from 'storybook/internal/node-logger' for Node.js code
Use client-side logger from 'storybook/internal/client-logger' for browser code
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/types/modules/core-common.ts
  • code/core/src/bin/core.ts
  • code/core/src/core-server/dev-server.ts
  • code/core/src/cli/build.ts
  • code/core/src/core-server/build-static.ts
  • code/core/src/core-server/utils/stories-json.ts
code/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions that need to be tested from their modules

Files:

  • code/core/src/types/modules/core-common.ts
  • code/core/src/bin/core.ts
  • code/core/src/core-server/dev-server.ts
  • code/core/src/cli/build.ts
  • code/core/src/core-server/build-static.ts
  • code/core/src/core-server/utils/stories-json.ts
code/**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier with --write flag to format code before committing
Run ESLint with yarn lint:js:cmd to check for linting issues and fix errors before committing

Files:

  • code/core/src/types/modules/core-common.ts
  • code/core/src/bin/core.ts
  • code/core/src/core-server/dev-server.ts
  • code/core/src/cli/build.ts
  • code/core/src/core-server/build-static.ts
  • code/core/src/core-server/utils/stories-json.ts
🧠 Learnings (3)
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx,mjs} : Use server-side logger from 'storybook/internal/node-logger' for Node.js code

Applied to files:

  • code/core/src/core-server/dev-server.ts
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/core/src/core-server/dev-server.ts
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx,mjs} : Use client-side logger from 'storybook/internal/client-logger' for browser code

Applied to files:

  • code/core/src/core-server/dev-server.ts
🧬 Code graph analysis (1)
code/core/src/core-server/dev-server.ts (2)
code/core/src/core-server/utils/stories-json.ts (1)
  • useSitemap (150-183)
code/core/src/core-server/index.ts (1)
  • StoryIndexGenerator (11-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: normal
  • GitHub Check: nx
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (8)
code/core/src/types/modules/core-common.ts (1)

336-341: LGTM!

The siteUrl property addition to TestBuildConfig is well-documented and correctly typed as optional.

code/core/src/core-server/dev-server.ts (1)

142-143: LGTM with a note on the type assertion.

The placement after the indexError check is correct. Since errors from getStoryIndexGenerator set indexError and return undefined, and we throw on indexError, the generator should be defined at this point. The type assertion is justified.

code/core/src/bin/core.ts (2)

156-159: LGTM!

The --site-url CLI option is clearly documented and follows the existing pattern for build command options.


176-176: LGTM!

The environment variable mapping follows the established SBCONFIG_* naming convention.

code/core/src/core-server/build-static.ts (3)

28-28: LGTM!

Import addition aligns with the new sitemap extraction capability.


31-36: LGTM!

The type extension properly exposes siteUrl as an optional property for sitemap generation.


169-175: LGTM!

Sitemap extraction is correctly added to the effects array for parallel execution. The extractSitemap function handles the missing siteUrl case gracefully by logging and returning early.

code/core/src/core-server/utils/stories-json.ts (1)

150-183: LGTM!

The useSitemap function correctly:

  • Sets appropriate headers for gzipped XML
  • Serves cached sitemap when available
  • Generates and caches the sitemap on first request
  • Handles errors with 500 status code

Comment on lines +156 to +159
.option(
'--site-url <string>',
'The URL where the built site will be deployed, for sitemap generation'
)
Copy link
Contributor

Choose a reason for hiding this comment

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

@Sidnioulz, minor item here. Don't forget to add the appropriate documentation to reflect this CLI flag, to avoid potential questions/issues, and ensure parity between what's available and what's documented.

Copy link
Member Author

Choose a reason for hiding this comment

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

Of course 👍

I will copy my TODO list for this feature into the description to dispel any concern :)

@storybook-bot
Copy link
Contributor

Failed to publish canary version of this pull request, triggered by @Sidnioulz. See the failed workflow run at: https://github.com/storybookjs/storybook/actions/runs/20018620346

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
code/core/src/core-server/utils/stories-json.ts (2)

22-22: Module-level mutable state creates potential issues in concurrent/multi-instance scenarios.

The cachedSitemap variable is module-level mutable state. If multiple Storybook instances run in the same process or if tests run in parallel, they would share this cache, potentially serving stale or incorrect sitemaps.

Consider encapsulating the cache within the useSitemap closure or passing it as part of the options/context to avoid shared state issues.


33-34: Remove commented-out code and clarify gzip logic.

Line 33 contains dead commented-out code. The isProduction parameter name is misleading—it actually controls whether gzip is disabled (true = no gzip for file output, false = gzip for HTTP response). Consider renaming to skipGzip or forFileOutput for clarity.

-  // const smStream = new SitemapStream();
-  const pipeline = isProduction ? smStream : smStream.pipe(createGzip());
+  const pipeline = skipGzip ? smStream : smStream.pipe(createGzip());
code/core/src/common/utils/cli.ts (1)

74-80: Variable naming is misleading.

The variable envVarValue actually holds the name of the environment variable, not its value. The value is retrieved on line 80. Consider renaming for clarity.

-    const envVarValue = envVarNames.find((envVarName) => process.env[envVarName]);
-    if (envVarValue) {
-      program[fieldName] = process.env[envVarValue];
+    const matchingEnvVar = envVarNames.find((name) => process.env[name]);
+    if (matchingEnvVar) {
+      program[fieldName] = process.env[matchingEnvVar];
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 278c624 and 48d1d32.

📒 Files selected for processing (3)
  • code/core/src/bin/core.ts (2 hunks)
  • code/core/src/common/utils/cli.ts (1 hunks)
  • code/core/src/core-server/utils/stories-json.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/core/src/bin/core.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESLint and Prettier configurations that are enforced in the codebase

Files:

  • code/core/src/common/utils/cli.ts
  • code/core/src/core-server/utils/stories-json.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode

Files:

  • code/core/src/common/utils/cli.ts
  • code/core/src/core-server/utils/stories-json.ts
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{ts,tsx,js,jsx,mjs}: Use server-side logger from 'storybook/internal/node-logger' for Node.js code
Use client-side logger from 'storybook/internal/client-logger' for browser code
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/core/src/common/utils/cli.ts
  • code/core/src/core-server/utils/stories-json.ts
code/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions that need to be tested from their modules

Files:

  • code/core/src/common/utils/cli.ts
  • code/core/src/core-server/utils/stories-json.ts
code/**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier with --write flag to format code before committing
Run ESLint with yarn lint:js:cmd to check for linting issues and fix errors before committing

Files:

  • code/core/src/common/utils/cli.ts
  • code/core/src/core-server/utils/stories-json.ts
🧠 Learnings (1)
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx,mjs} : Use server-side logger from 'storybook/internal/node-logger' for Node.js code

Applied to files:

  • code/core/src/core-server/utils/stories-json.ts
🧬 Code graph analysis (1)
code/core/src/core-server/utils/stories-json.ts (2)
code/core/src/core-server/build-static.ts (1)
  • BuildStaticStandaloneOptions (31-36)
code/core/src/core-server/utils/watch-story-specifiers.ts (1)
  • watchStorySpecifiers (42-134)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: nx
🔇 Additional comments (3)
code/core/src/core-server/utils/stories-json.ts (2)

72-108: LGTM!

The URL normalization logic correctly handles trailing slashes and protocol detection, including the Vercel edge case. The function appropriately generates uncompressed XML for file output.


175-175: The port default of 80 is never used in practice. In dev-server.ts, useSitemap is called only after an explicit invariant(port, 'expected options to have a port') check, which guarantees that port is always present in the options object passed to the function. The default value exists as a defensive fallback but poses no actual risk since the function is only called from one location with a validated port value.

Likely an incorrect or invalid review comment.

code/core/src/common/utils/cli.ts (1)

69-83: LGTM!

The updated getEnvConfig function correctly handles both single environment variable names and arrays, enabling flexible multi-provider environment variable resolution (e.g., for site URL across Vercel, Netlify, etc.).

workingDir?: string;
configDir?: string;
normalizedStories: NormalizedStoriesSpecifier[];
options: Options;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Unused options parameter in function signature.

The options: Options parameter is declared in the destructured parameters but is never used within the useStoriesJson function body. Either remove the unused parameter or use it if intended.

 }: {
   app: Polka;
   initializedStoryIndexGenerator: Promise<StoryIndexGenerator>;
   serverChannel: ServerChannel;
   workingDir?: string;
   configDir?: string;
   normalizedStories: NormalizedStoriesSpecifier[];
-  options: Options;
 }) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
options: Options;
}: {
app: Polka;
initializedStoryIndexGenerator: Promise<StoryIndexGenerator>;
serverChannel: ServerChannel;
workingDir?: string;
configDir?: string;
normalizedStories: NormalizedStoriesSpecifier[];
}) {
🤖 Prompt for AI Agents
In code/core/src/core-server/utils/stories-json.ts around line 124, the
destructured function signature includes an unused parameter `options: Options`;
remove `options` from the parameter list (and any related type annotation) if it
is not needed, or if it was intended to influence behavior, thread its usage
into the function body (e.g., apply options to configure file parsing/filters)
and update the implementation and tests accordingly so the parameter is actually
used.

Comment on lines +180 to +186
const sitemapBuffer = await generateSitemap(
initializedStoryIndexGenerator,
networkAddress,
false
);
cachedSitemap = sitemapBuffer;
res.end(sitemapBuffer);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle undefined sitemap buffer to prevent malformed gzip response.

If generateSitemap returns undefined, the response will have Content-Encoding: gzip header but send an empty body, causing client decompression errors. Add a check before caching and sending.

       const sitemapBuffer = await generateSitemap(
         initializedStoryIndexGenerator,
         networkAddress,
         false
       );
+      if (!sitemapBuffer) {
+        res.statusCode = 500;
+        res.end('Failed to generate sitemap');
+        return;
+      }
       cachedSitemap = sitemapBuffer;
       res.end(sitemapBuffer);
🤖 Prompt for AI Agents
In code/core/src/core-server/utils/stories-json.ts around lines 180 to 186,
guard against generateSitemap returning undefined: check if sitemapBuffer is
undefined before assigning cachedSitemap and res.end; if undefined, log or
report the error, clear or avoid setting the gzip Content-Encoding header and
respond with a 204 No Content (or 500 if preferred) without caching, otherwise
proceed to cache and send the buffer as before.

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

Labels

ci:normal experiment This issue/PR is an experiment; it doesn't always follow production standards but will be monitored. feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants