Parcel: Inline CSS in JavaScript

Created on 28 Mar 2019  Â·  15Comments  Â·  Source: parcel-bundler/parcel

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.

Feature

Most helpful comment

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

All 15 comments

@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?

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.

@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?

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: https://github.com/parcel-bundler/parcel/blob/636bb50baeae15730c3b91cd3b58b457c31f7200/packages/core/parcel-bundler/src/assets/CSSAsset.js#L203-L215

Also, is there an ETA on Parcel 2?

"Alpha will be soon" - @​devongovett

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.

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

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

@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).

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.

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

Follow https://github.com/parcel-bundler/parcel/issues/3477 for that

This should be possible with https://github.com/parcel-bundler/parcel/pull/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’

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

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.

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

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

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jsftw86 picture jsftw86  Â·  3Comments

Niggler picture Niggler  Â·  3Comments

algebraic-brain picture algebraic-brain  Â·  3Comments

466023746 picture 466023746  Â·  3Comments

adamreisnz picture adamreisnz  Â·  3Comments