Parcel: Wrong relative path to URL assets when bundle lives in subdirectory

Created on 26 Jul 2018  ยท  5Comments  ยท  Source: parcel-bundler/parcel

๐Ÿ› Bug Report

When two bundles in different directories with the same file name are generated, the directory structure is reflected in the output. This causes an issue with URL dependencies because their paths are relative to the current directory.

๐ŸŽ› Configuration

Given the following directory structure:

assets
โ”œโ”€โ”€ a
โ”‚ย ย  โ””โ”€โ”€ bundle.css
โ”œโ”€โ”€ b
โ”‚ย ย  โ””โ”€โ”€ bundle.css
โ””โ”€โ”€ img
    โ””โ”€โ”€ foo.png

With the contents of assets/a/bundle.css being:

body {
    background-image: url('../img/foo.png');
}

And the following Parcel command:

parcel build --no-cache --no-source-maps 'assets/*/bundle.css'

The following directory tree is correctly generated:

dist
โ”œโ”€โ”€ a
โ”‚ย ย  โ””โ”€โ”€ bundle.css
โ”œโ”€โ”€ b
โ”‚ย ย  โ””โ”€โ”€ bundle.css
โ””โ”€โ”€ foo.d7b28491.png

So far so good. However...

๐Ÿค” Expected Behavior

I would expect the contents of dist/a/bundle.css to be either:

body{background-image:url(../foo.d7b28491.png)}

Or:

body{background-image:url(/foo.d7b28491.png)}

๐Ÿ˜ฏ Current Behavior

The contents of dist/a/bundle.css is currently:

body{background-image:url(foo.d7b28491.png)}

Which means the browser will request /a/foo.png, but the file is located at the web root โ€“ not in the "a" directory.

๐Ÿ’ Possible Solution

I'm really not familiar enough with Parcel to make an educated recommendation as to how is should be fixed, but I'm guessing the best way to fix the issue is by somehow making calculating the relative path to the URL asset from the bundle. So that (using the example code above), the output URL would be url(../foo.[md5].png)

Another possible solution is to join the URL with the publicURL option. This could be achieved by modifying the Asset#addURLDependency() method's return value, like so:

+ const urlJoin = require('./utils/urlJoin');

...

- return URL.format(parsed);
+ return urlJoin(this.options.publicURL, URL.format(parsed));

This method seems to work, but as said before, I'm not familiar enough with Parcel to know if it's a viable solution. It does require modifying some tests, which indicates it could be a breaking change. Would love some feedback.

I have made a commit and will definitely create a PR if the solution is approved.

๐ŸŒ Your Environment

| Software | Version(s) |
| ---------------- | ---------- |
| Parcel | 1.9.7
| Node | 10.7.0
| npm/Yarn | Yarn 1.7.0
| Operating System | macOS 10.13.6

Bug Confirmed Bug

Most helpful comment

What is the status of this issue? I would like to switch from webpack to parcel but this is really big problem for me. Is there any workaround for the time before any fix?

All 5 comments

I think your solution is the correct one in this case. The result should always have the publicUrl as a prefix

Issues that look related:

  • #1808
  • #1592

What is the status of this issue? I would like to switch from webpack to parcel but this is really big problem for me. Is there any workaround for the time before any fix?

Hi I am currently having the same problem here and I would like to know when the next version with this fix is released.

I would really like to have this feature as well. One of the key benefits of parcel is that it generates unique file paths for assets. This prevents version mismatches and allows aggressive caching. However it is very difficult to set caching headers for these assets because there is no easy pattern to select them with. If there was a directory for non-entrypoints then it would be easy to set cache forever headers on anything in that directory.

The only workaround I can think of right now is setting --public-url=assets/$buildid and then mapping that directory back into the root using rewriting which is quite messy.

Was this page helpful?
0 / 5 - 0 ratings