Skip to content

fix(assets): resolve Picture TDZ error when combined with content render()#16171

Merged
alexanderniebuhr merged 8 commits intowithastro:mainfrom
Desel72:fix/issue-16036
Apr 8, 2026
Merged

fix(assets): resolve Picture TDZ error when combined with content render()#16171
alexanderniebuhr merged 8 commits intowithastro:mainfrom
Desel72:fix/issue-16036

Conversation

@Desel72
Copy link
Copy Markdown
Contributor

@Desel72 Desel72 commented Mar 31, 2026

Introduce an internal virtual module (virtual:astro-get-image) that exports only getImage and imageConfig without any Astro component references. The content runtime now imports from this narrower module instead of astro:assets, breaking the circular initialization dependency that caused a TDZ ReferenceError when prerendered pages using <Picture> were bundled in the same chunk as content collection render() calls.

Closes #16036

Changes

  • Added a new internal virtual module virtual:astro-get-image in packages/astro/src/assets/consts.ts that exports only getImage and imageConfig — no Image, Picture, or Font component references.
  • Extended the astro:assets Vite plugin in packages/astro/src/assets/vite-plugin-assets.ts to resolve and load the new virtual module.
  • Changed packages/astro/src/content/runtime.ts to import getImage from virtual:astro-get-image instead of astro:assets, avoiding the TDZ caused by Rollup eagerly referencing $$Picture in the namespace object before its declaration is initialized.
  • Uses virtual: prefix (like existing virtual:image-service) rather than astro: prefix to keep this as an internal module and avoid exposing new public API surface.

Testing

Added a regression test (content-collection-picture-render.test.js) with a fixture that reproduces the exact scenario:

  • A prerendered page using <Picture> from astro:assets
  • A content collection page using getStaticPaths() + render(entry) with images in markdown body

Without the fix, astro build fails with:
ReferenceError: Cannot access '$$Picture' before initialization

With the fix, the build succeeds and all 4 test assertions pass. Existing related test suites also pass with zero regressions:

  • content-collection-tla-svg.test.js (3/3)
  • content-collections-render.test.js (13/13)
  • core-image-picture-emit-file.test.js (6/6)
  • units/content-collections/image-references.test.js (9/9)

Docs

No docs changes needed. This is a bug fix for an internal module resolution issue — no user-facing API changes.

…der (withastro#16036)

Introduce an internal virtual module (virtual:astro-get-image) that exports
only getImage and imageConfig without any Astro component references. The
content runtime now imports from this narrower module instead of astro:assets,
breaking the circular initialization dependency that caused a TDZ
ReferenceError when prerendered pages using <Picture> were bundled in the
same chunk as content collection render() calls.
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 31, 2026

🦋 Changeset detected

Latest commit: 38866ae

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: astro Related to the core `astro` package (scope) label Mar 31, 2026
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 31, 2026

Merging this PR will not alter performance

✅ 18 untouched benchmarks


Comparing Desel72:fix/issue-16036 (38866ae) with main (c2a52d6)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (44fd3b8) during the generation of this report, so c2a52d6 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Desel72 added 2 commits April 1, 2026 05:23
Use colon separator (virtual:astro:*) instead of hyphen so the module
matches existing optimizeDeps.exclude patterns in both Astro core and
the Cloudflare adapter. The hyphenated name was not excluded from
Vite's dependency optimizer, causing esbuild to fail resolving it.
@Desel72
Copy link
Copy Markdown
Contributor Author

Desel72 commented Apr 1, 2026

Hi @matthewp I think this is not related with my change. If you have some free time, please check this PR.

@alexanderniebuhr
Copy link
Copy Markdown
Member

@Desel72 thank you for your PR. Will get this reviewed.

Comment on lines +457 to +459
// @ts-expect-error Internal virtual module that exports only getImage (no component
// references like Image/Picture) to avoid TDZ errors during prerender bundling (#16036).
const { getImage } = await import('virtual:astro:get-image');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We must add this module to dev-only.d.ts, so that we don't need the ts directive

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@Desel72 can you address this comment please?

@Desel72
Copy link
Copy Markdown
Contributor Author

Desel72 commented Apr 1, 2026

Hi @alexanderniebuhr Can this be merged?

@matthewp
Copy link
Copy Markdown
Contributor

matthewp commented Apr 1, 2026

@Desel72 you need to address Ema's feedback: #16171 (comment)

Copy link
Copy Markdown
Contributor

@travisbreaks travisbreaks left a comment

Choose a reason for hiding this comment

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

Circular dependency causing TDZ errors is a nasty class of bug. The virtual module approach is a reasonable solution. A few things:

  1. Virtual module boundary durability: The fix introduces `virtual:astro:get-image` to break the cycle. Future imports could re-introduce it if someone imports from `assets/consts.ts` in a path that also touches `content/runtime.ts`. A comment in the virtual module entry point warning about this would help prevent regression.

  2. The @ts-expect-error question: ematipico's feedback about adding proper TS definitions is the right call. Virtual modules should have type declarations (`declare module "virtual:astro:get-image"`) so the `@ts-expect-error` isn't needed. This is a pattern Astro already uses for other virtual modules.

  3. Test coverage: Does the test reproduce the exact bundling condition (Picture + content render() in the same chunk)? TDZ errors are sensitive to chunk splitting heuristics, so the test should ideally force co-location rather than relying on the current bundler behavior.

Good fix for a tricky problem. The TS definitions point from ematipico would make this merge-ready.

uni added 2 commits April 2, 2026 16:14
…t-error

Add type declaration for the virtual:astro:get-image module so TypeScript
recognizes the import without needing a @ts-expect-error directive.
@Desel72
Copy link
Copy Markdown
Contributor Author

Desel72 commented Apr 2, 2026

Hi @matthewp @ematipico @alexanderniebuhr I've done. Please check.

@alexanderniebuhr alexanderniebuhr added the pr preview Apply this label to a PR to generate a preview release label Apr 2, 2026
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 2, 2026

npm i https://pkg.pr.new/astro@16171

commit: 8f2d9e5

@Desel72
Copy link
Copy Markdown
Contributor Author

Desel72 commented Apr 2, 2026

Hi @alexanderniebuhr Thanks. I am going to move to #16035. I've created PR: #16194. Please review asap.

@alexanderniebuhr
Copy link
Copy Markdown
Member

Tested the preview in a real-life project and it works.

@matthewp
Copy link
Copy Markdown
Contributor

matthewp commented Apr 2, 2026

@ematipico this is ready for another look when you have the chance

@alexanderniebuhr alexanderniebuhr merged commit 5bcd03c into withastro:main Apr 8, 2026
27 checks passed
@astrobot-houston astrobot-houston mentioned this pull request Apr 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg: astro Related to the core `astro` package (scope) pr preview Apply this label to a PR to generate a preview release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Image breaks on prerendering whenever there is another file with getStaticPaths and render(content)

5 participants