Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Emit path of CSS bundle to default export #2817

Open
J-Cake opened this issue Jan 12, 2023 · 6 comments
Open

Feature Request: Emit path of CSS bundle to default export #2817

J-Cake opened this issue Jan 12, 2023 · 6 comments

Comments

@J-Cake
Copy link

J-Cake commented Jan 12, 2023

Hi @evanw

Every time I start a new project with ESBuild I find new uses for it, so thank you so much for it. But I have encountered one small issue with it, in that I was trying to start bundling CSS as well. My setup is as follows:

/css/main.css
@import '/path/to/other/files';

/src/index.tsx
import '/css/main.css';

// load CSS
$(document.head).append(`<link rel="stylesheet" href="/build/bundle.css"/>`);

I was hoping for a way to retrieve the path of the bundled CSS file by using a default export like the file loader does, but without using plugins.

So something like

import bundle from '/css/main.css';

$(document.head).append(`<link rel="stylesheet"/>`).attr("href", bundle)

Let me know what you think, and if that needs any tweaking.

Thanks again

@hyrious
Copy link

hyrious commented Jan 13, 2023

CSS bundle does not always 1:1 with the import statements in the JS entries. There're at least 2 scenarios I can think of:

  1. Multiple CSS files in one JS entry,

    // main.js
    import foo from './foo.css'
    import bar from './bar.css'

    esbuild will only generate one main.css as you can refer the doc or try it yourself. In this case should we make foo and bar the same string?

  2. Multiple JS files importing the same CSS file.

    // foo.js
    import main from './main.css'
    // bar.js
    import main from './main.css'

    They can be bundled with or without --splitting, in any case esbuild will generate both foo.css and bar.css. Which means the main variable should have different value in these files even though they point to the same file. This is counterintuitive with how JS modules works.

If you're sure that all your source code uses 1:1 imports, then you may implement this behavior with the assumption in mind.

@J-Cake
Copy link
Author

J-Cake commented Jan 13, 2023

See that's a huge edge case that I completely missed. I understand now why that's how the system works.

Re your first point about making foo and bar the same string; Personally, I'm using a setup where I dump the imported strings directly into an array, loop over it and insert the link tags into the head, which, when using this approach would mean that I'd end up with the same link tag twice. Which is fine - the browser will just use the cached version, if at all, but it's still not ideal.

As for your second point, I wasn't aware that CSS could be split the same way JavaScript can, because CSS performs no logic, so the whole concept of lazy-loading breaks down, therefore the concept is completely inapplicable. Perhaps I've misunderstood though.

Thanks for your feedback

@evanw
Copy link
Owner

evanw commented Jan 13, 2023

Another consideration is that embedding a <link> tag is a better idea than having the JS link to the CSS, since if the HTML references both then the browser can download them in parallel (and potentially apply the CSS even before the JS has finished downloading). So I don't think esbuild should have the behavior you're suggesting built-in as it would incentivize the less-efficient path.

Ultimately the built-in behavior I have in mind for this is passing a HTML file to esbuild as the entry point where the <script> tag pulls in the JavaScript that then imports the CSS. When all that is bundled, esbuild would automatically generate the appropriate <link> tags in the HTML for you. This is part of what #31 is about.

@J-Cake
Copy link
Author

J-Cake commented Jan 14, 2023

hm. That's true, but I'm still convinced it's technically possible. Maybe it's against standard or good practice, but there's no advantage to not including this feature.

That being said, @hyrious's point about ambiguity is valid, which would simply be addressed by saying How this feature is used is in the eye of the beholder and just let the user figure out how to handle things like two imports referring to the same bundle etc.

Again, if this imposes a technical can-of-worms then I understand, but if not, then I don't see a reason to not include it.

@J-Cake J-Cake closed this as completed Jan 14, 2023
@J-Cake J-Cake reopened this Jan 14, 2023
@evanw
Copy link
Owner

evanw commented Jan 15, 2023

Wait, hang on. Regardless of what happens with this issue, the development model for esbuild definitely isn't "whatever is technically possible to build gets included." I intend to be careful and deliberate about what I add to esbuild's API. I'm balancing many concerns including cognitive complexity, usability for developers, maintenance overhead for me, and yes also what esbuild's API incentivizes in the ecosystem. We can keep this issue open to talk about it if you'd like. I just wanted to set expectations that this is not an "anything goes" type of project.

@J-Cake
Copy link
Author

J-Cake commented Jan 15, 2023

No of course not. I didn't mean to imply that at all. In fact, I've read over a few other feature discussions and you make that abundantly clear, it just seems to me that there's not just space for a new feature here, but it seems that there's a hole here where that feature seems to be missing.

Look, as I say, it's your project, and if you don't think this feature is necessary, or goes against the project ethos, that's your decision, but I just think there's a missing feature here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants