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

Can I inline css to js file #1370

Closed
xbl opened this issue May 15, 2018 · 35 comments
Closed

Can I inline css to js file #1370

xbl opened this issue May 15, 2018 · 35 comments

Comments

@xbl
Copy link

xbl commented May 15, 2018

❔ Question

Can I inline css to js file,like webpack?
I want dist 1 js file

@DeMoorJasper
Copy link
Member

DeMoorJasper commented May 15, 2018

You can use fs.readFileSync to get the filecontents of a css file inlined

@targumon
Copy link

targumon commented May 21, 2018

Sorry for piggybacking @xbl question - I'm not sure my use case justifies opening another one:

I am developing a widget. To use it a website will include a <script src=".../link-to-my/widget.js"> on their page.
Currently the dist folder has the CSS in a separate file. I want to avoid the overhead of another network request.

@DeMoorJasper, what in your opinion is the best way to have just the JS generated, but with code similar to this at its bottom:

var css = 'div{border:1px solid tan}' // <-- the generated CSS goes here
var style = document.createElement('style')
style.type = 'text/css'
style.appendChild(document.createTextNode(css))
document.head.appendChild(style)

(Simplified. Should probably do this inside an IIFE and also set an 'id' for style so it's easier to remove it if needed.)

@xbl
Copy link
Author

xbl commented May 22, 2018

@targumon , @DeMoorJasper thank you for commented,I already use document.createElement('style') , It can be used, but not what I think is the best solution。

thank you again!

@DeMoorJasper
Copy link
Member

DeMoorJasper commented May 22, 2018

Well it should work like this, parcel should inline any fs calls inside js

var css = fs.readFileSync('./style.css'); // <-- The css reader
var style = document.createElement('style')
style.type = 'text/css'
style.appendChild(document.createTextNode(css))
document.head.appendChild(style)

Will compile to...

var css = 'div{border:1px solid tan}'; // <-- Parcel got this from the file
var style = document.createElement('style')
style.type = 'text/css'
style.appendChild(document.createTextNode(css))
document.head.appendChild(style)

This however wouldn't work with any non pure css files. But i'm not sure how to achieve this in a clean way (with that I mean having a way to support both inlining and url without creating something parcel specific)

@targumon
Copy link

This pushes me in the right direction.
So I got it to work with:

var css = fs.readFileSync(__dirname + '/../../dist/widget.3c2281aa.css', 'utf8')

But as you can see now I have a problem with the name of the file which I can't control so this will break soon (unless I create a Packager as suggested in another issue)
In any case - thank you! I appreciate the time you took to help me!

@DeMoorJasper
Copy link
Member

DeMoorJasper commented May 22, 2018

If you use multiple entrypoints it should keep a consistent (non-hashed) filename.

For example running parcel style.css index.html
style.css and index.html will maintain the same name

@rodoabad
Copy link

rodoabad commented Jun 22, 2018

@DeMoorJasper how do you disable Parcel from creating a separate file when bundling though?

Like I'd like this to be a single JS file so I do not have to import the styles again.

import React from 'react';
import {manInTheMirror} from './ManInTheMirror.scss';

const ManInTheMirror = () => {
  return (
    <div className={manInTheMirror}>
      {'Man in the mirror.'}
    </div>
  )
};

export default ManInTheMirror;

I'm transpiling this before I publish the package. However, if I need to use the component again, I have to import two files instead of one.

import ManInTheMirror from 'man-in-the-mirror';
import 'man-in-the-mirror/dist/ManInTheMirror.css';

Unless there's a way for Parcel to transpile packages in node_modules so I can use src in my main instead of the dist folder.

@RobertWHurst
Copy link
Contributor

RobertWHurst commented Jul 11, 2018

It would be very nice to have the option of bundling the css and other assets into a single js file.

I'm running into this issue with vue components. I'm writing a vue library and I want to use parcel rather than vue-cli to build the npm package. I want to bundle the css and fonts with my npm package without the need to the end user needing to require these separately.

Currently, I can't see a way to do this. The above workarounds don't work because most of my css is defined in vue components.

@RobertWHurst
Copy link
Contributor

I'm putting together a PR for this functionality. So far I've added a flag --inline-css which swaps out the CSSPackager for a CSSInlinePackager which batches the CSS into a string, then adds js which appends a style tag to the head of the page.

@Chris2011
Copy link

@RobertWHurst what about the PR? Is it still implemented? Want to have the CSS inside a JS file too. I do an import in my TS file, as I did it for webpack.

@RobertWHurst
Copy link
Contributor

@Chris2011 Decided to make a proper RFC since it requires new behavior. Nearly done, but I've been busy with work. Additionally, I want to make sure it's implemented cleanly and "with the grain" so to speak, as this is a big feature.

@dawsbot
Copy link

dawsbot commented Oct 22, 2018

I'm finding this problem interesting, but confused about the current status. The fs method described by @DeMoorJasper doesn't cut it for our use-case because "Imports and requires are not supported inside inline <script> tags yet.".

Given that we have a straightforward html and css setup, what's the inline css pattern which does not involve loading any javascript files?

@DeMoorJasper
Copy link
Member

DeMoorJasper commented Oct 22, 2018

@dawsbot without js I think you can hack it with using a style tag which imports the main css, and set the language of the style tag to scss, sass asset inlines everything instead of using the parcel pipeline. So this method should work

<style type="scss">
@import "./main.css";
</style>

@dawsbot
Copy link

dawsbot commented Oct 22, 2018

@DeMoorJasper I've attempted that method and no luck.

The build html file still contains the untranspiled scss code in the html.

<style type="scss">
  import "./index.css";
</style>

This shows at the bottom of our head after a parcel build. Before yarn build I installed scss as well with yarn add --dev scss, with no difference in the transpiled output.

@DeMoorJasper
Copy link
Member

@dawsbot I'll experiment a bit and get back to you

@DeMoorJasper
Copy link
Member

DeMoorJasper commented Oct 22, 2018

@dawsbot This appears to work.

<style type="text/styl">
  @import "./main.css";
</style>

This is however a hack and should not be considered safe from breaking in incremental builds.
However the chances of someone changing the way stylus asset handles imports are fairly slim as it's a fairly stable asset type.

In Parcel 2 this should work out of the box without any hacks

@RobertWHurst
Copy link
Contributor

With the advent of Parcel 2 in the future, I've decided to hold off on my RFC and PR. The Parcel got this in the bag anyways 💯

@devongovett
Copy link
Member

I'm not really sure what the usecase is for inlining css into javascript instead of producing a separate file. If you use HTML as an entry point, Parcel will even insert the css file into your HTML for you automatically. This will lead to a much faster app as well since you don't have to wait for all of the JavaScript to be loaded, parsed, and executed in order to get styles on the page.

For the library usecase, I don't think you necessarily want to determine in your library how users want to include your css. They might want to import them into their own css build rather than have them inline in JS. I think you should leave this up to users, or build systems of applications rather than only allowing inlined styles.

@devongovett
Copy link
Member

That being said it will be possible to override and extend the JS and CSS transformers in parcel 2, so I think we can potentially solve this narrow usecase in plugins.

@Chris2011
Copy link

The real use case is to have only one JS file at the end, nothing else. With HTTP2 of course you don't need it anymore but for HTTP1 you should avoid round trips. That a use case exists, you can see in this ticket here. Afaik, with webpack this is normal behaviour to have only one file at the end and there is a plugin to generate the css into an external file. So we have both use cases.

Everything in one, single file and everything separeted.

I'm fine with closing the issue too, if there is an easy possibility to change this behaviour in parcel2.

My 2 cents.

@jhpratt
Copy link

jhpratt commented Dec 3, 2018

@devongovett A use case for inlining CSS is webcomponents with a shadow root, which can't inherit styles from the non-shadow DOM.

@FDiskas
Copy link

FDiskas commented Mar 12, 2019

@jhpratt
npm i -D parcel-plugin-text
Then create file
.parcel-plugin-textrc and add text:

{
  "extentions":[
    "css"
  ]
}

And for example usecase is:

import { html, define } from "hybrids";
import styles from "./simple-counter.css";

export function increaseCount(host) {
  host.count += 1;
}

export const SimpleCounter = {
  count: 0,
  render: ({ count }) => html`
    <button onclick="${increaseCount}" class="button">
      Counter: ${count}
    </button>
  `.style(styles)
};

define("simple-counter", SimpleCounter);

Note: The css preprocessors does not works.

@FDiskas
Copy link

FDiskas commented Mar 12, 2019

Also it should be possible to use .parcelrc file
https://github.com/parcel-bundler/parcel/blob/master/PARCEL_2_RFC.md#parcelrc

But I did't succeeded

@jhpratt
Copy link

jhpratt commented Mar 13, 2019

As you noted in your edit, that doesn't work with preprocessors, which is a major limitation. @devongovett any updates on this? My previous comment has 16 +1's, so it certainly seems like there's interest in that specific use case.

@alejandro5042
Copy link

I also need this for the userscript use case.

Userscripts are a single *.user.js file installed locally by Tampermonkey, Violentmonkey, etc. Ideally I would use Parcel to combine various JS and CSS files into a single JS file like these extensions expect. Right now I’m treating my CSS as a string in a JS file that gets bundled in.

@mrassili
Copy link

mrassili commented Jun 6, 2020

I need this to style my shadow dom element since styles won't be applied unless inlined with the shadow root

@mischnic
Copy link
Member

mischnic commented Jun 6, 2020

(Parcel 2 supports this natively:

import style from "bundle-text:./style.css";

class MyTest extends HTMLElement {
  constructor() {
    super();

    let shadow = this.attachShadow({ mode: "open" });

    let style = document.createElement("style");
    style.textContent = style;
    shadow.appendChild(style);

    let info = document.createElement("span");
    info.textContent = this.getAttribute("label");
    shadow.appendChild(info);
  }
}

customElements.define("my-test", MyTest);

)

@mrassili
Copy link

mrassili commented Jun 6, 2020

(Parcel 2 supports this natively:

import style from "bundle-text:./style.css";

class MyTest extends HTMLElement {
  constructor() {
    super();

    let shadow = this.attachShadow({ mode: "open" });

    let style = document.createElement("style");
    style.textContent = style;
    shadow.appendChild(style);

    let info = document.createElement("span");
    info.textContent = this.getAttribute("label");
    shadow.appendChild(info);
  }
}

customElements.define("my-test", MyTest);

)

thank you so much for the snippet, I am using parcel-plugin-web-extension which doesn't support Parcel 2 for now

is there a workaround to inline styles using parcel 1.12.4 ?

@mrassili
Copy link

mrassili commented Jun 7, 2020

(Parcel 2 supports this natively:

import style from "bundle-text:./style.css";

class MyTest extends HTMLElement {
  constructor() {
    super();

    let shadow = this.attachShadow({ mode: "open" });

    let style = document.createElement("style");
    style.textContent = style;
    shadow.appendChild(style);

    let info = document.createElement("span");
    info.textContent = this.getAttribute("label");
    shadow.appendChild(info);
  }
}

customElements.define("my-test", MyTest);

)

@mischnic with parcel 2 I get

@parcel/core: Failed to resolve 'bundle-text:./style.css' from './src/content-script.js'

any clue?

@FDiskas
Copy link

FDiskas commented Jun 10, 2020

@mrassili did you tried? #1370 (comment)

@mrassili
Copy link

mrassili commented Jun 10, 2020

@mrassili did you tried? #1370 (comment)

nah I ended up processing the styles using tailwindcss build (TailwindCSS CLI) and then did import cssText from 'bundle-text:./dist/style.css' which worked fine

@ai-zubair
Copy link

@mrassili did you tried? #1370 (comment)

I tried that for the use case of web components and it works as expected.

@jdowens-commit
Copy link

Yes

@AndyOGo
Copy link

AndyOGo commented Jan 26, 2022

Inspired by that 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

@MarioWork
Copy link

I'm not really sure what the usecase is for inlining css into javascript instead of producing a separate file. If you use HTML as an entry point, Parcel will even insert the css file into your HTML for you automatically. This will lead to a much faster app as well since you don't have to wait for all of the JavaScript to be loaded, parsed, and executed in order to get styles on the page.

For the library usecase, I don't think you necessarily want to determine in your library how users want to include your css. They might want to import them into their own css build rather than have them inline in JS. I think you should leave this up to users, or build systems of applications rather than only allowing inlined styles.

Google Apps Script does not support css files, so inlinning makes sense...

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