evanw / evanw/esbuild

--bundle --external:/* breaks relative CSS @import

Open
#4,530 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
40.1k
Forks
1.3k
PR merge metrics
No merged PRs in 30d

Description

I'm using esbuild to bundle many CSS files into one file.

It has relative imports like this: (These should be bundled)
```css
@import "b.css";
```
But it also has absolute paths for images and fonts like this: (These should not be inlined, and kept as-is)
```css
b {
background: url("/images/epic.png");
}
```
I'm using `--external:/*` to make all paths that start with `/` treated as external. But it seems to make all paths external, specifically the `@import "b.css"` from above, which prevents bundling the files together.

---

Repro:

Given this setup with 2 files:
```sh
$ cat a.css
@import "b.css";
$ cat b.css
b { b: b; }
```

[Example 1](https://esbuild.github.io/try/#YgAwLjI4LjIALS1idW5kbGUAZQBhLmNzcwBAaW1wb3J0ICJiLmNzcyI7AABiLmNzcwBiIHsgYjogYjsgfQ): `esbuild --bundle a.css` is correct:
```css
/* b.css */
b {
b: b;
}

/* a.css */
```

[Example 2](https://esbuild.github.io/try/#YgAwLjI4LjIALS1idW5kbGUgLS1leHRlcm5hbDp6KgBlAGEuY3NzAEBpbXBvcnQgImIuY3NzIjsAAGIuY3NzAGIgeyBiOiBiOyB9): `--bundle --external:z* a.css` is also correct. No paths start with `z` here so we inline the import.
```css
/* b.css */
b {
b: b;
}

/* a.css */
```

[Example 3](https://esbuild.github.io/try/#YgAwLjI4LjIALS1idW5kbGUgLS1leHRlcm5hbDpiKgBlAGEuY3NzAEBpbXBvcnQgImIuY3NzIjsAAGIuY3NzAGIgeyBiOiBiOyB9): `--bundle --external:b* a.css` is also correct. The import path starts with `b`, so it's treated as external and does not get inlined.
```css
@import "b.css";

/* a.css */
```

[Example 4](https://esbuild.github.io/try/#YgAwLjI4LjIALS1idW5kbGUgLS1leHRlcm5hbDovKgBlAGEuY3NzAEBpbXBvcnQgImIuY3NzIjsAAGIuY3NzAGIgeyBiOiBiOyB9): `--bundle --external:/* a.css` is not correct. There are no paths that start with `/` here, so it should be inlined.
```css
@import "b.css";

/* a.css */
```

This seems like a bug where all paths are internally expanded and contain / at the start of their representation and match this filter.

Also when I tested this on Windows, the behavior was a bit different and seemed to depend on the current working directory from where esbuild was ran.

Given this setup:

```batch
C:\Users\User\test> tree
C:.
├───bin
│ esbuild.exe
└───css
a.css
b.css
C:\Users\User\test> bin\esbuild.exe --bundle --external:/* css\a.css
@import "./css/b.css";

/* css/a.css */
C:\Users\User\test> cd bin
C:\Users\User\test\bin> esbuild.exe --bundle --external:/* ..\css\a.css
/* ../css/b.css */
b{
b: b;
}

/* ../css/a.css */
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Reproduce the behavior with a.css importing b.css and the four esbuild command examples, then compare the Windows commands from the report. Trace external filter matching and path resolution for relative CSS imports; done means --external:/* preserves absolute URLs while still bundling relative b.css imports consistently across platforms.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
build-system
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.