Fix Dev Server to allow serving static assets from a shared folder outside of the extension directory#7386
Conversation
Member
Author
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
8cac9b8 to
5fa082f
Compare
4 tasks
This was referenced Apr 24, 2026
6dd1af8 to
b6d7170
Compare
5fa082f to
26c602f
Compare
…tside of the extension directory
26c602f to
5deb0e5
Compare
b6d7170 to
10a3496
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

WHY are these changes introduced?
When two extension points within the same extension declare assets whose source basenames collide (e.g. both reference a
tools.json, or both compile tomain.js), the dev server previously served whichever file happened to win the filesystem race. The URL shape (/assets/<basename>) gave no way to distinguish which extension point a request belonged to, so one target's asset would silently shadow another's.Additionally, the middleware served files directly from the extension's source directory, which allowed path traversal to files outside the extension's bundle and made it impossible to serve
include_assets-copied files that had been renamed byuniqueBasenameduring the build step.WHAT is this pull request doing?
Opaque, target-scoped asset URLs
Asset URLs are now emitted as
/assets/<target>/<assetKey>[.ext]instead of/assets/<basename>. This makes every URL unique per extension point, even when two targets share a source file.Asset resolver
A new
AssetResolver(Map<string, string>) is introduced per extension. During payload generation, each emitted URL subpath is registered in the resolver alongside the output-relative filesystem path the middleware should read. The resolver is cleared and rebuilt on every payload regeneration so stale entries from removed targets or renamed assets don't persist.The resolver is stored in
ExtensionsPayloadStore(keyed bydevUUID) and exposed viagetAssetResolver(devUUID). It is cleaned up when an extension is deleted.Middleware rewrite
getExtensionAssetMiddlewarenow:assetPathin the extension's resolver to find the actual output-relative filename (falling back to the raw path for direct compiled-artefact fetches).outputDir(the bundle directory thatinclude_assetspopulates).outputDirvia traversal, returning 404 rather than 403 to avoid leaking directory structure.Static directory assets
A new
staticAssetsMapperhandles directory-valued config entries (e.g.assets = "./assets"). It emits a directory-prefix URL with a trailing slash and registers one resolver entry per copied file, including nested subdirectories.resolveOutputDirextractedThe logic for deriving an output directory from
outputPath(file with extension →dirname; no extension → path itself) is extracted into a sharedresolveOutputDirhelper used by both the build step and the middleware.How to test your changes?
toolsasset pointing at different source files.shopify app devand observe that each extension point's payload contains a distinct URL (/<target-a>/tools.jsonvs/<target-b>/tools.json).../secret.txt) returns 404.assets = "./assets"containing nested files; confirm each file is individually servable via its resolver-mapped URL.Checklist
patchfor bug fixes ·minorfor new features ·majorfor breaking changes) and added a changeset withpnpm changeset add