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

css import assertions #2314

Closed
thebinarysearchtree opened this issue Jun 14, 2022 · 10 comments
Closed

css import assertions #2314

thebinarysearchtree opened this issue Jun 14, 2022 · 10 comments

Comments

@thebinarysearchtree
Copy link

thebinarysearchtree commented Jun 14, 2022

I am trying to get css import assertions to work. I found a comment that says to use --bundle --external:*.css --format=esm --target=esnext, but that has the following issue:

the build folder is /build. When I use those arguments, it seems to look for /build/Page.css for example, instead of the actual path to the css, which is not in the build directory. That also isn't ideal from a production point of view, as the css does need to be copied to the build folder, not just left as an external resource. Am I going to have to write a custom handler that copies the css manually and renames it so it is unique and so on?

@thebinarysearchtree thebinarysearchtree changed the title css assertions css import assertions Jun 14, 2022
@thebinarysearchtree
Copy link
Author

thebinarysearchtree commented Jun 15, 2022

I ended up just creating a plugin. I need it for web components because if you just use a text loader for css, you have to append a script tag to every component instance, which is slow and unnecessary duplication. Some people seem to do what my plugin does, but in the web component constructor, which will get called every time a new instance is created so that is also a waste of resources. My plugin converts

import styles from './Page.css' assert { type: 'css' };

at build time by reading the css into a string and then creating a new CSSStyleSheet with that string.

import esbuild from 'esbuild';
import { readFile } from 'fs/promises';

const plugin = {
  name: 'css import assertions',
  setup(build) {
    build.onLoad({ filter: /\.css$/ }, async (args) => {
      const css = await readFile(args.path, 'utf8');
      const contents = `
        const styles = new CSSStyleSheet();
        styles.replaceSync(\`${css.replaceAll(/[`$]/gm, '\\$&')}\`);
        export default styles;`;
      return { contents };
    });
  }
}

esbuild.build({
  entryPoints: ['src/renderer.ts'],
  watch: true,
  sourcemap: true,
  bundle: true,
  plugins: [plugin],
  outfile: 'build/renderer.js'
}).catch(() => process.exit(1));

@evanw
Copy link
Owner

evanw commented Jun 15, 2022

I think the right way to do what the original post is asking for will be to use --loader:.css=copy once it's implemented: #2255. Doing what your plugin ended up doing is different, but is also something esbuild should probably have a built-in way of doing. Using a plugin is appropriate for now though.

@thebinarysearchtree
Copy link
Author

thebinarysearchtree commented Jun 16, 2022

Yeah, the problem is that Safari doesn't support constructable style sheets.

@calebdwilliams
Copy link

This might be a separate issue, but is there any way the plugin API could get access to assertion metadata?

@evanw
Copy link
Owner

evanw commented Sep 12, 2022

This might be a separate issue, but is there any way the plugin API could get access to assertion metadata?

No. The specification forbids import assertions from triggering any behavior changes. From the import assertion specification:

moduleRequest.[[Assertions]] must not influence the interpretation of the module or the module specifier; instead, it may be used to determine whether the algorithm completes normally or with an abrupt completion.

Import assertions are only supposed to be used to assert qualities of the import. So for example it would be a valid use of import assertions for esbuild to interpret assert { type: 'css' } to mean "cause a build error if the loader isn't css". Exposing import assertions to plugins would mean people would use import assertions to change import behavior, which directly violates the specification.

@evanw
Copy link
Owner

evanw commented Mar 24, 2023

Since this issue was created, import assertions have been demoted from stage 3 (candidate for implementation) back to stage 2 (draft work in progress): https://github.com/tc39/proposal-import-attributes. So I’m closing ties issue as I don’t think esbuild should implement this anymore given that new development.

For what it’s worth I fully support their decision, and I’m happy they are reconsidering it. It was a shame to see them burn valuable syntax space for such a limited purpose. Hopefully their next iteration gives us something more powerful and flexible that’s intended for bundlers like esbuild to make better use of!

@evanw evanw closed this as not planned Won't fix, can't repro, duplicate, stale Mar 24, 2023
@Mwni
Copy link

Mwni commented May 8, 2023

It's back at stage 3. Using with again. Rollup added it as well: rollup/rollup#4646. Worth reconsidering?

@evanw
Copy link
Owner

evanw commented May 9, 2023

I don't think it's ready to implement, sorry. If I recall correctly, it's not just a simple switch from assert to with. I believe they also changed import assertions attributes to be part of the module identity. This means that importing the same module with different import attributes could potentially now import that module twice instead of once. From Rollup's PR description, it sounds like perhaps Rollup isn't implementing that part correctly (although there is a warning). I think given the history of this feature, it makes sense for esbuild to wait to implement this until it's implemented in a real JS environment. That way esbuild can match the behavior of a real implementation instead of just guessing how it should work.

@wydengyre
Copy link

@evanw

it makes sense for esbuild to wait to implement this until it's implemented in a real JS environment

This is now supported in Node 20.10.0+ and Deno 1.37+. Can it be reopened?

@evanw
Copy link
Owner

evanw commented Jan 15, 2024

Do you mean like this? https://github.com/evanw/esbuild/releases/tag/v0.19.7 Support for bundling code that uses the with keyword was already added to esbuild months ago. One of my motivations for shipping this was precisely that it's now supported in node.

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

5 participants