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

stylesheet_pack_tag not working in webpacker 4?? #2059

Closed
AmirolAhmad opened this issue Apr 17, 2019 · 22 comments
Closed

stylesheet_pack_tag not working in webpacker 4?? #2059

AmirolAhmad opened this issue Apr 17, 2019 · 22 comments
Labels
style loaders support Questions or unspecific problems

Comments

@AmirolAhmad
Copy link

AmirolAhmad commented Apr 17, 2019

based on the usage mention here, you can link them by using stylesheet_pack_tag which mean you add <%= stylesheet_pack_tag 'application' %> under views>layout>application.html.erb

but then when I view-source the web, there is no css loaded inside the <head>.

then to fixed this issue, i need to import import '../src/application.css' into packs/application.js to make the css file to work and load.

my question, is this how it should work? if so, what is the purpose to have <%= stylesheet_pack_tag 'application' %>? if you commented out this line, it is still work.

I have create the sample app for better picture about this

or maybe i miss some point somewhere else?

@hishammalik
Copy link

having the same issue.

@bbugh
Copy link
Contributor

bbugh commented Apr 25, 2019

This sounds to me like misunderstanding how webpack works (which is reasonable, because it's incredibly confusing).

Webpack is used for building many things, and the way we tell webpack what to load are import or require statements. This includes css files, images, and everything else. When you do import '../src/application.css', you're telling webpack include application.css in the build. This does not mean it's going to be compiled into your javascript, only that webpack now knows that you want to compile this file. How that file compilation is handled is depending on how your loaders (css-loader, sass-loader, file-loader, etc.. ) are configured.

When you do <%= stylesheet_pack_tag 'application' %>, that's a run-time inclusion from Rails, that has nothing to do with whether webpack has built the stylesheets or not. All that is doing is saying "if you have a pack named application-*.css, include it here". If webpack didn't build a separate pack of css, then this statement will have nothing to load because no stylesheets were compiled.

You didn't mention what framework you're using, and how this works is dependent on that. As far as I remember, stylesheet_pack_tag is only used for Vue because vue-loader automatically collects all of the stylesheets into one file. I am not sure if any other frameworks require this (I don't think React does, for example).

It is perfectly normal and expected to import or require your stylesheets like you're doing in your entry point file.

@hishammalik
Copy link

hishammalik commented Apr 25, 2019

I saw that its covered in the document as

If you have hmr turned to true, then the stylesheet_pack_tag generates no output, as you will want to configure your styles to be inlined in your JavaScript for hot reloading. During production and testing, the stylesheet_pack_tag will create the appropriate HTML tags.

However, the problem I've observed with this is that if you have stylesheet_pack_tag in your layout as a default, but your javascript doesn't spit out any styles, then you will run into an error on live where rails complains about stylesheet not existing.

This should be consistent for dev and production environment. As in, if error is to be generated on production for no stylesheet generated, then development should also give the error.

@AmirolAhmad
Copy link
Author

@bbugh And yeah, maybe because I missing a loader in the app, so it doesn't compile my scss file. BTW, thanks for your input.

@jakeNiemiec
Copy link
Member

jakeNiemiec commented Apr 25, 2019

From #2062 (comment)

The build chain works like this:

(if extract_css == true) -> application stylesheet -> MiniCssExtractPlugin -> application.css -> stylesheet_pack_tag 'application'

(if extract_css == false) -> application stylesheet -> style-loader -> application.js (loads css in head)

MiniCssExtractPlugin does not support webpack-dev-server or HMR. statement from dev: webpack-contrib/mini-css-extract-plugin#296 (comment)

Good news time: a PR was just merged for this feature webpack-contrib/mini-css-extract-plugin#334, but it won't come to webpacker unless somebody creates a PR here.

@mrsum
Copy link

mrsum commented Jul 11, 2019

still not working stylesheet_pack_tag any updates?

upd.
i have manifest.json file which contents

"entrypoints": {
    "forms/login": {
      "css": [
        "/packs/css/forms/login-c851d7b1.css"
      ],

in my template i have:

= stylesheet_pack_tag 'forms/login' // not working
= javascript_pack_tag 'forms/login' // working

@eronisko
Copy link

For what it's worth, I've noticed that while stylesheet_pack_tag does not render anything in development, it does render a css file in production.

My guess is that this is due to the extract_css setting in config/webpacker.yml.

@jakeNiemiec
Copy link
Member

jakeNiemiec commented Jul 29, 2019

@eronisko when you are in development (depending on settings), the styles are being streamed in via WebSocket and injected into the <head>.

@vfonic
Copy link
Contributor

vfonic commented Aug 6, 2019

So what is the conclusion here?

I'm using webpacker with react and, by importing the scss in my packs/*.js, the styles work...in development. When I deployed to production, I realized there are no styles available.

Do I need to add stylesheet_pack_tag for it to work in production?

I think it would be better if I'd receive any error at any point.

I've also checked https://github.com/rails/webpacker/blob/master/docs/css.md and many other issues. Maybe this should be clarified more?

EDIT: I got it working in production by doing stylesheet_pack_tag 'index', the same as the js file that includes the scss file.
Here's the full solution:

// app/javascript/packs/index.js
import '../scss/index'; // this is index.scss file

// app/views/home/index.html.erb
<%= stylesheet_pack_tag 'index' %>
<%= javascript_pack_tag 'index' %>

I'm not sure if the scss file name matters. I think what matters is that stylesheet_pack_tag includes the same name like the js file name index.js.

@jakeNiemiec
Copy link
Member

When I deployed to production, I realized there are no styles available.

You probably have extract_css: true in your webpacker.yml. This removes import '../scss/index'; and generates the equivalent style sheet. You do need to add stylesheet_pack_tag yourself.

mzagaja added a commit to MAPC/trailmap3 that referenced this issue Aug 9, 2019
Per rails/webpacker#2059 (comment) you need to include a stylesheet_pack_tag if webpacker.yml has its emit_css_file setting set to true.
@hibachrach
Copy link

hibachrach commented Aug 20, 2019

@jakeNiemiec

@eronisko when you are in development (depending on settings), the styles are being streamed in via WebSocket and injected into the <head>.

What does this and/or how is this configured?

@jakeNiemiec
Copy link
Member

See this updated comment where I spell out both ways it can go: #2059 (comment)

streamed in via WebSocket

When webpack-dev-server is running (depending on HMR/dynamic loading settings), your chunks come over a socket allowing faster development. It can be sometimes difficult to set up.

injected into the <head>

This is handled via style-loader. Customization: https://github.com/webpack-contrib/style-loader#options

@hibachrach
Copy link

@jakeNiemiec Thank you very much for your comment!

So if I understand it correctly, there's currently no way to have the CSS in the head without some client-side JS execution?

@jakeNiemiec
Copy link
Member

So if I understand it correctly, there's currently no way to have the CSS in the head without some client-side JS execution?

It is possible, just do the following and it should work in a default install:

  1. extract_css: true
  2. Put <%= stylesheet_pack_tag 'myStylePack' %> in <head>

Here is the mechanism:

if (config.extract_css) {
use.unshift(MiniCssExtractPlugin.loader)
} else {
use.unshift(styleLoader)
}

@jakeNiemiec jakeNiemiec added style loaders support Questions or unspecific problems labels Aug 21, 2019
@hibachrach
Copy link

hibachrach commented Aug 22, 2019

@jakeNiemiec Sorry, I was unclear. That would include a <link> tag in the <head> referencing a separate CSS file. However, if I were to want a <style> tag with the CSS as a child text node, I cannot achieve that via Webpacker (without client-side JS execution)?

@jakeNiemiec
Copy link
Member

jakeNiemiec commented Aug 22, 2019

@HarrisonB if I were to want a <style> tag with the CSS as a child text node, I cannot achieve that via Webpacker (without client-side JS execution)?

  1. You cannot, inline styles are outside of webpacker's scope and are generally considered to be a bad practice in most instances.
  2. If you have an unrelenting urge to do this, try something like:
<%= File.read(Webpacker.manifest.lookup('iliketomakeemailsandpdfs.css'))&.html_safe %>
  1. Edit: numbered bullet points made this sound way too condescending 😅

@hibachrach
Copy link

  1. generally considered to be a bad practice in most instances

I would totally agree, but there are at least two cases we've encountered where reading the CSS outside of precompilation is necessary, and they both involve feeding CSS into a separate "packager". It's also a bit frustrating as it was, in a sense, within the scope of Sprockets, from which we've had to move away because of waning community support.

  1. We use wicked_pdf to generate PDFs from HTML documents, and having the CSS be external to the fed-in HTML creates issues. wicked_pdf bundles helpers that do effectively what you're describing in item (2) or queries the webpack-dev-server if one's available.

  2. We also need to feed CSS into premailer manually as it converts CSS into inline email-safe styling. To grab that CSS, much like wicked_pdf, we branch on the current environment and grab

For both items, this requires that we have the webpack-dev-server running during tests, which we haven't been doing up to now, but might need to.

I think it would be nice to have a centralized way to grab CSS for a given manifest entry for cases like these. Do you think a feature request issue or PR would be welcome?

Anyway, thanks for your help @jakeNiemiec 🙂 Hope you are having a wonderful day!

@jakeNiemiec
Copy link
Member

Thanks @HarrisonB, today is much better.

Rendering emails and PDFs are definitely a webpack-dev-server use-case I hadn't considered. There are serious hurdles that would need to be cleared before it could work since webpack-dev-server uses JS to juggle the styles. I am not sure how it could work in the context of PDFs & emails. You would probably need to run ./bin/webpack with extract_css: true to get what you need. (I'm more from the webpack side of the equation, can you tell? 🙂)

I think it would be nice to have a centralized way to grab CSS for a given manifest entry for cases like these. Do you think a feature request issue or PR would be welcome?

Most definitely! It is especially needed since rails@6 is out with webpacker as a default.

@chevinbrown
Copy link

chevinbrown commented Nov 22, 2019

It's shoddy, but this gets me by for the time being:

module WebpackerHelper
  def inline_stylesheet_pack_tag(name)
    file_name = name + ".css"
    content_tag(:style) do
      if current_webpacker_instance.dev_server.running?
        open(inline_asset_url(file_name)).read.html_safe
      else
        File.read(File.join(Rails.root, "public", asset_pack_path(file_name))).html_safe
      end
    end
  end

  private

  def inline_asset_url(name)
    dev_protocol + current_webpacker_instance.config.dev_server[:public] + asset_pack_path(name)
  end

  def dev_protocol
    current_webpacker_instance.config.dev_server[:https] ? "https://" : "http://"
  end
end

It'd be really nice if there was a helper around this for the pdf/mailer use-cases.

@jakeNiemiec
Copy link
Member

I think it could use a general way to resolve asset names. The equivalent of this bit:

      if current_webpacker_instance.dev_server.running?
        open(inline_asset_url(file_name)).read.html_safe
      else
        File.read(File.join(Rails.root, "public", asset_pack_path(file_name))).html_safe
      end

@PikachuEXE
Copy link

PikachuEXE commented Aug 6, 2020

I tried open but it would complain about self signed cert invalid
So I used net/http

require "net/http"
require "openssl"
require "uri"

def inline_stylesheet_pack_tag(name)
  file_name = name.end_with?(".css") ? name : name + ".css"
  tag.style do
    if current_webpacker_instance.dev_server.running?
      uri = URI(inline_asset_url(file_name))

      ::Net::HTTP.start(
        uri.hostname,
        uri.port,
        use_ssl: uri.scheme == "https",
        # Will be self signed cert
        verify_mode: OpenSSL::SSL::VERIFY_NONE,
      ) do |http|
        http.request_get(uri.path)
      end.body.force_encoding("UTF-8")
    else
      ::File.read(
        ::File.join(
          ::Rails.public_path,
          # Using `asset_pack_path` generates unnecessary info
          # like host and protocol
          current_webpacker_instance.manifest.lookup!(file_name),
        ),
      )
    end.html_safe
  end
end

Edit 1: Updated code for current_webpacker_instance.dev_server.running? == false after some testing
It does skip the conditional inside asset_pack_path

@guillaumebriday
Copy link
Member

Is this still an issue?

Feel free to reopen if needed

rbclark pushed a commit to mitre/vulcan that referenced this issue Aug 17, 2021
Webpacker has a nasty habit of not compiling css files for components and then causing an error in production when they are missing.
rails/webpacker#2342
rails/webpacker#2059 (comment)
This adds an override which causes no errors in production but still allows us to leave the stylesheet_pack_tag for when it is needed.
rbclark pushed a commit to mitre/vulcan that referenced this issue Aug 17, 2021
Webpacker has a nasty habit of not compiling css files for components and then causing an error in production when they are missing.
rails/webpacker#2342
rails/webpacker#2059 (comment)
This adds an override which causes no errors in production but still allows us to leave the stylesheet_pack_tag for when it is needed.
rbclark pushed a commit to mitre/vulcan that referenced this issue Aug 18, 2021
Webpacker has a nasty habit of not compiling css files for components and then causing an error in production when they are missing.
rails/webpacker#2342
rails/webpacker#2059 (comment)

Unfortunately using extract_css: true all the time apparently comes with some performance impacts, however the tradeoff is the application actually works correctly in development mode.
rbclark pushed a commit to mitre/vulcan that referenced this issue Aug 18, 2021
Webpacker has a nasty habit of not compiling css files for components and then causing an error in production when they are missing.
rails/webpacker#2342
rails/webpacker#2059 (comment)

Unfortunately using extract_css: true all the time apparently comes with some performance impacts, however the tradeoff is the application actually works correctly in development mode.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
style loaders support Questions or unspecific problems
Projects
None yet
Development

No branches or pull requests