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

Inline CSS in JavaScript #2864

Closed
jhpratt opened this issue Mar 28, 2019 · 16 comments
Closed

Inline CSS in JavaScript #2864

jhpratt opened this issue Mar 28, 2019 · 16 comments

Comments

@jhpratt
Copy link

jhpratt commented Mar 28, 2019

This is a nearly identical proposal to #1370, which was closed without resolution. I pinged a Parcel member months ago, and received no response. As such, I'm filing a new issue to avoid the appearance of it being resolved.

Currently, CSS must be loaded via a separate file. Using a text plugin (as indicated here), it's possible to inline vanilla CSS. However, that fails as soon as you want any preprocessor, be it minifying, autoprefixing, or something more complicated.

Though this may seem silly, as it only removes a single HTTP request, it can add up quickly when using WebComponents with a shadow DOM; their CSS is not inherited. Anything in a shadow DOM must either be inlined directly or loaded via an external file (the latter of which has nontrivial overhead when done dozens of times).

TLDR, it would be great to be able to load CSS with a transformer into a JavaScript file. There are advantages to doing so.

@jhpratt
Copy link
Author

jhpratt commented Apr 9, 2019

@mischnic Is it currently possible to retrieve the output file name of a given input file? This would (somewhat) solve this use case by allowing for a <link rel='stylesheet'> to be placed in the relevant location. Alternatively, is it possible to avoid adding the hash to the file name?

@mischnic
Copy link
Member

mischnic commented Apr 9, 2019

Is it currently possible to retrieve the output file name of a given input file?

Using the API, you can get all assets inside a bundle (there is 1:1 relationship between asset and bundle, so you have to search for your asset): https://parceljs.org/api.html#properties

(Not tested:)

const bundler = new Bundler(...);
bundler.on('bundled', (bundle) => {

  console.log(
    [...bundle.childBundles]
      .find(b => b.type === "css" &&
        [...b.assets].find(a => a.relativeName = "styles/something.css")
      ).name
  );

});
// Call this to start bundling
bundler.bundle();

Alternatively, is it possible to avoid adding the hash to the file name?

Will be is Parcel 2.

@jhpratt
Copy link
Author

jhpratt commented Apr 9, 2019

@mischnic Is it possible to get the output name from within the file, or would it be necessary to create a custom transformer to inline it? Just getting the output name isn't enough, I'd need to be able to use it inside my JS/TS file (in order to generate the link tag).

Also, is there an ETA on Parcel 2?

@mischnic
Copy link
Member

mischnic commented Apr 9, 2019

Is it possible to get the output name from within the file, or would it be necessary to create a custom transformer to inline it? Just getting the output name isn't enough, I'd need to be able to use it inside my JS/TS file (in order to generate the link tag).

If you want to import CSS within JS to return the file contents:

import fileContent from "./style.css";
console.log(fileContent);

You could write a plugin that overrides the default CSSAsset (not tested, should work):

// plugin/index.js
module.exports = function(bundler) {
  bundler.addAssetType("css", require.resolve("./MyCSSAsset"));
};

// plugin/MyCSSAsset.js
const CSSAsset = require("parcel-bundler/src/assets/CSSAsset.js");

module.exports = class MyCSSAsset extends CSSAsset {
  async generate() {
    const result = super.generate();
    const cssResult = result.find(v => v.type === "css");
    const jsResult = result.find(v => v.type === "js");
    jsResult.value = `module.exports = ${JSON.stringify(cssResult.value)}`;

    return [cssResult, jsResult];
  }
};

Reference for return value of CSSAsset#generate:

return [
{
type: 'css',
value: css,
cssModules: this.cssModules,
map: this.sourceMap
},
{
type: 'js',
value: js,
hasDependencies: false
}
];

Also, is there an ETA on Parcel 2?

"Alpha will be soon" - @​devongovett

@jhpratt
Copy link
Author

jhpratt commented Apr 9, 2019

If you want to import CSS within JS to return the file contents:

import data from "./style.css";
console.log(data);

The problem with this, as previously mentioned, is that it doesn't perform any transformations. That's a deal breaker for many, including myself.

"Alpha will be soon" - @​devongovett

I was aware of that comment; I wasn't sure if there was anything more concrete than "soon".


I think for simplicity's sake, I'm going to stick with declaring multiple entry points, even though that's not technically correct (it's the only way to avoid mangling the name). I very much look forward to Parcel 2; even an alpha release is better than nothing. I'm currently fighting with tooling (be it Gulp or Parcel) to do what I want, as most preprocessors aren't prepared for the use case of a ShadowDOM.

@mischnic
Copy link
Member

mischnic commented Apr 9, 2019

The problem with this, as previously mentioned, is that it doesn't perform any transformations.

If you create a plugin as I've just outlined/(written), it should work (don't have time to test it myself).

I wasn't sure if there was anything more concrete than "soon".

https://github.com/parcel-bundler/parcel/milestone/5

@stevenvachon
Copy link

@mischnic that milestone link currently goes nowhere informative. Is there a corresponding issue/PR?

@mischnic
Copy link
Member

@stevenvachon That link was merely the answer to Also, is there an ETA on Parcel 2?, of which alpha 1 was just released so the milestone is finished.
That has really nothing to do with this issue itself, we might add the possibility to specify how you want to import something (e.g. import css from "./style.css?url), which would solve this (other asset types for which this is commonly needed are Markdown and HTML).

@stevenvachon
Copy link

I find this feature particularly useful for native web components where we would want :root{...} to be within a shadowRoot <style> and not part of the CSS bundle.

@mischnic
Copy link
Member

mischnic commented Oct 5, 2019

we might add the possibility to specify how you want to import something

Follow #3477 for that

@devongovett
Copy link
Member

This should be possible with #3726.

import css from ‘bundle-text:./styles.css’;

// css is now the processed text content of styles.css

Should also work or anything that compiles to CSS, or other types of resources as well (e.g. HTML or even another JS file).

import css from ‘bundle-text:./styles.sass’

@stevenvachon
Copy link

@devongovett is that file's processed text content still included in any CSS bundle?

@mischnic
Copy link
Member

If you want both a string containing the CSS and the css to be applied:

import 'styles.css';
import cssString from ‘bundle-text:./styles.css’;

(This would duplicate the CSS though).
Not sure if that answers your question.

@devongovett
Copy link
Member

@stevenvachon nope! When you use bundle-text: the bundle is marked as inline, so no separate CSS file would be created.

@wbinnssmith
Copy link
Contributor

#3726 has been merged — please try out a bundle-text: import on the v2 branch 😄

@AndyOGo
Copy link

AndyOGo commented Jan 26, 2022

@mischnic
Inspired by your comment I just released a Parcel V1 plugin, which inlines the css into the JS bundle and injects a <style> node into the document's head.

https://github.com/AndyOGo/parcel-plugin-inject-style-tag

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

No branches or pull requests

6 participants