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

'Webpacker can't find application' in a brand new project with extract_css: true #2605

Closed
thebravoman opened this issue May 28, 2020 · 7 comments · Fixed by #2608
Closed

Comments

@thebravoman
Copy link

thebravoman commented May 28, 2020

There is a lot of confusion in this thread #2071 and many blog post about extract_css and stylesheet_pack_tag and why it is not working. People are facing the problem on production and they are just removing the "stylesheet_pack_tag" from their layouts which - even 40 people liked this (#2071 (comment), #2071 (comment)). I think is the wrong decision. I also get a lot of errors and it's been a few hours trying to get it working with SOME success at the end and a summary - but it is still not resolved.

The whole point of me fighting this that I have a FOUC - flash of unstyled content.

Using rails 6.0.3.1, ruby 2.6.5

So I started with a brand new project to see how to make it work

Create project:

$ rails new webpacker_css --webpack
$ cd webpacker_css/
$ rails g scaffold book
$ rake db:migrate

Just added a console log here to make sure it is working.

// app/javascripts/packs/application.js

// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")


// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)

console.log("I am here")
# start server
$ rails s

Go to localhost:3000/books and you see "I am here" in browser console

This is the content of the head

// localhost:3000/books

<head>
    <title>WebpackerCss</title>
    <meta name="csrf-param" content="authenticity_token">
    <meta name="csrf-token" content="Ru2kGTJmJrOu78bxxBvamNr/mgOQOsprBoF3DZAd2g18HkYN2NNpqFwe41bC5ERed3NlQGFHxV6dGv3cdqQbhg==">
    <link rel="stylesheet" media="all" href="/assets/application.debug-e31ba9c2203a6c80d85e12146c3b4ee366154dbcd476da4eb770d63ca6f5dd61.css" data-turbolinks-track="reload">
    <script src="/packs/js/application-9fae71cb0f19dfa22153.js" data-turbolinks-track="reload"></script>
  </head>

Change stylesheet_link_tag to stylesheet_pack_tag

Remove the stylesheet_link_tag and add stylesheet_pack_tag from the layout. This will use webpacker for the stylesheets

// app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title>WebpackerCss</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>

It is all ok. Visit the server and the content of head is as expected. There is no styleheet

<head>
  <title>WebpackerCss</title>
  <meta name="csrf-param" content="authenticity_token">
  <meta name="csrf-token" content="0zAALG0jc+24tfgO4F52sPeyPdAiM6wrzf+xPVtkeKnpw+I4h5Y89kpE3anmoeh2Wj7Ck9NOox5WZDvsvd25Ig==">
  <script src="/packs/js/application-9fae71cb0f19dfa22153.js" data-turbolinks-track="reload"></script>
</head>

extract_css: true

Change extract_css to true. What I would expect here after reading many comments on different issues I would expect to have link stylesheet in the head.

development:
  <<: *default
  compile: true

  extract_css: true

Now go to localhost:3000/books and this error occurs.

Webpacker can't find application in /home/user/webpacker_css/public/packs/manifest.json. Possible causes:
1. You want to set webpacker.yml value of compile to true for your environment
   unless you are using the `webpack -w` or the webpack-dev-server.
2. webpack has not yet re-run to reflect updates.
3. You have misconfigured Webpacker's config/webpacker.yml file.
4. Your webpack configuration is not creating a manifest.
Your manifest contains:
{
  "application.js": "/packs/js/application-9fae71cb0f19dfa22153.js",
  "application.js.map": "/packs/js/application-9fae71cb0f19dfa22153.js.map",
  "entrypoints": {
    "application": {
      "js": [
        "/packs/js/application-9fae71cb0f19dfa22153.js"
      ],
      "js.map": [
        "/packs/js/application-9fae71cb0f19dfa22153.js.map"
      ]
    }
  }
}

Thinking about the error it makes sense. After all there is no such file.
The documentation at https://github.com/rails/webpacker/blob/master/docs/css.md#css-sass-and-scss says I need application.scss in scss. So I create one:

$ mkdir app/javascript/scss
$ touch app/javascript/scss/application.scss

It is empty, but lets restart server and refresh.

Same error occurs!

Read the documentation

The documentation says I need to

You need to tell webpack which file(s) it has to compile and know how to load
When you do import '../scss/application.scss'

So I modify my application.js to do the import

// app/javascript/packs/application.js

// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")


// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)

import('../scss/application.scss') // <-- This here is the m new like
console.log("I am here")

Restart and go to localhost:3000/books

and the same error occurs

Webpacker can't find application in /home/user/webpacker_css/public/packs/manifest.json. Possible causes:
1. You want to set webpacker.yml value of compile to true for your environment
   unless you are using the `webpack -w` or the webpack-dev-server.
2. webpack has not yet re-run to reflect updates.
3. You have misconfigured Webpacker's config/webpacker.yml file.
4. Your webpack configuration is not creating a manifest.
Your manifest contains:
{
  "application.js": "/packs/js/application-ebd46f9316e1ef81cf3d.js",
  "application.js.map": "/packs/js/application-ebd46f9316e1ef81cf3d.js.map",
  "entrypoints": {
    "application": {
      "js": [
        "/packs/js/application-ebd46f9316e1ef81cf3d.js"
      ],
      "js.map": [
        "/packs/js/application-ebd46f9316e1ef81cf3d.js.map"
      ]
    }
  }
}

Update to webpacker 5

I then go to the Gemfile. It has

# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'

I read somewhere that there is a webpacker 5 version so I tried to update.

gem 'webpacker', '~> 5.0'

bundle update, restart, go to localhost:3000/books and the same error occurs.

Updated 1

Strange stackoverflow solution 1

I then tried this solution https://stackoverflow.com/a/58507538/1266681 which basically says:

Multiple packs with the same name but a different extension.

i.e: application.scss and application.js only the last one will make it to the webpack entry path configuration.

I followed the approach:

 1. Rename stylesheets/application.scss into stylesheets/style.scss
 2. Import style.scss in application.js like this import '../stylesheets/style'
 3. config/webpacker.yml make below changes
      compile: true
      cache_manifest: true
      extract_css: true

I got

Webpacker can't find style.css in /home/user/webpacker_css/public/packs/manifest.json. Possible causes:

But people keep mentioning that If you put them in the ../stylesheets and if it is an application.css it might just work.

$ mv app/javascript/scss/ app/javascript/stylesheets
$ mv app/javascript/stylesheets/style.scss app/javascript/stylesheets/application.css
<!DOCTYPE html>
<html>
  <head>
    <title>WebpackerCss</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>

Restart and go to localhost:3000/books

Same error:

Webpacker can't find application.css in /home/user/webpacker_css/public/packs/manifest.json. Possible causes:
1. You want to set webpacker.yml value of compile to true for your environment
   unless you are using the `webpack -w` or the webpack-dev-server.
2. webpack has not yet re-run to reflect updates.
3. You have misconfigured Webpacker's config/webpacker.yml file.
4. Your webpack configuration is not creating a manifest.
Your manifest contains:
{
  "application.js": "/packs/js/application-7ce98ff94f79e659e04b.js",
  "application.js.map": "/packs/js/application-7ce98ff94f79e659e04b.js.map",
  "entrypoints": {
    "application": {
      "js": [
        "/packs/js/application-7ce98ff94f79e659e04b.js"
      ],
      "js.map": [
        "/packs/js/application-7ce98ff94f79e659e04b.js.map"
      ]
    }
  }
}

Check out tree structure

Here is the tree structure:

$ tree app/javascript/
app/javascript/
├── channels
│   ├── consumer.js
│   └── index.js
├── packs
│   └── application.js
└── stylesheets
    └── application.css

Then I remembered I should change application.js

+ import('../stylesheets/application.css')
- import('../scss/style.scss')

Restart and go to localhost:3000/books. Same error

Webpacker can't find application.css

Change application.css to application

I decide to change applicaiton.css to just application in the import. Just an idea.

+ import('../stylesheets/application')
+ import('../stylesheets/application.css')

Same error

Clean webpacker

I decide to clean the webpacker

$ rake webpacker:clean

Again result is

Webpacker can't find application.css

Out of ideas 1

I am out of ideas. As a result I still can not get it working.
I tried webpacker 4, webpacker 5. ../scss as in the documentation, read the documentation, used ../stylesheets/, user .css, used .scss, webpacker:clean, but can't get it working

Looking again at the error - file is not found - create an application.css entry point

So it seems that there is no application.css. That is clear. This files should be in the manifest.

Let's generate such an entry point

$ touch app/javascript/packs/application.css

There is no such entry point generated

[Webpacker] Compiling...
[Webpacker] Compiled all packs in /home/kireto/axles/tmp/webpacker_css/public/packs
[Webpacker] Hash: e9ff20abd64bc21bd25e
Version: webpack 4.43.0
Time: 1554ms
Built at: 05/28/2020 9:07:36 AM
                                     Asset       Size       Chunks                         Chunk Names
                  css/0-7528517c.chunk.css   49 bytes            0  [emitted] [immutable]  
              css/0-7528517c.chunk.css.map   91 bytes            0  [emitted] [dev]        
        js/0-3ca0d53a78c82ecd7d99.chunk.js  496 bytes            0  [emitted] [immutable]  
    js/0-3ca0d53a78c82ecd7d99.chunk.js.map  236 bytes            0  [emitted] [dev]        
    js/application-dd330a0622242ce2572b.js    131 KiB  application  [emitted] [immutable]  application
js/application-dd330a0622242ce2572b.js.map    146 KiB  application  [emitted] [dev]        application
                             manifest.json  364 bytes               [emitted]              
Entrypoint application = js/application-dd330a0622242ce2572b.js js/application-dd330a0622242ce2572b.js.map
[./app/javascript/channels sync recursive _channel\.js$] ./app/javascript/channels sync _channel\.js$ 160 bytes {application} [built]
[./app/javascript/channels/index.js] 211 bytes {application} [built]
[./app/javascript/packs/application.js] 815 bytes {application} [built]
[./app/javascript/stylesheets/application.css] 39 bytes {0} [built]
[./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 552 bytes {application} [built]
    + 4 hidden modules
Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--6-1!node_modules/postcss-loader/src/index.js??ref--6-2!app/javascript/stylesheets/application.css:
    Entrypoint mini-css-extract-plugin = *
    [./node_modules/css-loader/dist/cjs.js?!./node_modules/postcss-loader/src/index.js?!./app/javascript/stylesheets/application.css] ./node_modules/css-loader/dist/cjs.js??ref--6-1!./node_modules/postcss-loader/src??ref--6-2!./app/javascript/stylesheets/application.css 314 bytes {mini-css-extract-plugin} [built]
        + 1 hidden module

The manifest still contains

{
  "application.js": "/packs/js/application-dd330a0622242ce2572b.js",
  "application.js.map": "/packs/js/application-dd330a0622242ce2572b.js.map",
  "entrypoints": {
    "application": {
      "js": [
        "/packs/js/application-dd330a0622242ce2572b.js"
      ],
      "js.map": [
        "/packs/js/application-dd330a0622242ce2572b.js.map"
      ]
    }
  }
}

Create an entry point with a different name

$ touch app/javascript/packs/some.css

There is now a some.css entry point

{
  "application.js": "/packs/js/application-529ce2c4ffdc203eedfd.js",
  "application.js.map": "/packs/js/application-529ce2c4ffdc203eedfd.js.map",
  "entrypoints": {
    "application": {
      "js": [
        "/packs/js/application-529ce2c4ffdc203eedfd.js"
      ],
      "js.map": [
        "/packs/js/application-529ce2c4ffdc203eedfd.js.map"
      ]
    },
    "some": {
      "css": [
        "/packs/css/some-832d3de7.css"
      ],
      "js": [
        "/packs/js/some-bf6444ed6f415c810292.js"
      ],
      "css.map": [
        "/packs/css/some-832d3de7.css.map"
      ],
      "js.map": [
        "/packs/js/some-bf6444ed6f415c810292.js.map"
      ]
    }
  },
  "some.css": "/packs/css/some-832d3de7.css",
  "some.css.map": "/packs/css/some-832d3de7.css.map",
  "some.js": "/packs/js/some-bf6444ed6f415c810292.js",
  "some.js.map": "/packs/js/some-bf6444ed6f415c810292.js.map"
}

I modify the layout to

+ <%= stylesheet_pack_tag 'application, media: 'all', 'data-turbolinks-track': 'reload' %>
- <%= stylesheet_pack_tag 'some', media: 'all', 'data-turbolinks-track': 'reload' %>

Go to localhost:3000/books

It works

<head>
    <title>WebpackerCss</title>
    <meta name="csrf-param" content="authenticity_token">
    <meta name="csrf-token" content="1gZC1XfRegtz6y1jRZr9IiBqrnpgpL8qYuY4Nsk+xpjs9aDBnWQ1EIEaCMRDZWPkjeZROZHZsB/5fbLnL4cHEw==">
    <link rel="stylesheet" media="all" href="/packs/css/some-832d3de7.css" data-turbolinks-track="reload">
    <script src="/packs/js/application-529ce2c4ffdc203eedfd.js" data-turbolinks-track="reload"></script><link rel="stylesheet" type="text/css" href="/packs/css/0-bdf68acc.chunk.css"><script charset="utf-8" src="/packs/js/0-5d927d5c1ca9edbbb14c.chunk.js"></script>
  </head>

It even works with scss

$ mv app/javascript/packs/some.css app/javascript/packs/some.scss

where the generated file is:

.my_style {
  color: blue; }
/*# sourceMappingURL=some-3af5d93a.css.map*/

Let's change to extract_css: false

So I got it working with extract_css: true and a second entry point
But then if we change

+ extract_css: false
- extract_css: true

and start the server again the head of the page is

<head>
    <title>WebpackerCss</title>
    <meta name="csrf-param" content="authenticity_token">
    <meta name="csrf-token" content="N+J/V4pWVhqYVQVYKUno54x4vwq3wj+P+v+nIcBAQbINEZ1DYOMZAWqkIP8vtnYhIfRASUa/MLphZC3wJvmAOQ==">
    <script src="/packs/js/application-529ce2c4ffdc203eedfd.js" data-turbolinks-track="reload"></script>
    <link rel="stylesheet" type="text/css" href="/packs/css/0-bdf68acc.chunk.css"><script charset="utf-8" src="/packs/js/0-5d927d5c1ca9edbbb14c.chunk.js"></script>
  </head>

So there is no some.css. As expected because extract_css is set to false. But then I do not have my css class anywhere :

.my_style {
  color: blue; }

Change the application.js and now it works

I remember that I should also change the application.js

import('../styles/application')
import('./some')

If extract_css is false the head looks like

<head>
    <title>WebpackerCss</title>
    <meta name="csrf-param" content="authenticity_token">
<meta name="csrf-token" content="ZPoaPsgPcd4pu0osVYpltQAG21hjH3IF982NsSFBoM1eCfgqIro+xdtKb4tTdftzrYokG5JifTBsVgdgx/hhRg==">
    

    
    <script src="/packs/js/application-d3fbd49ce11f47bb7780.js" data-turbolinks-track="reload"></script><script charset="utf-8" src="/packs/js/0-ef9c2fc826d70bf4668d.chunk.js"></script>
  <style>.my_style {
  color: blue; }

/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9ob21lL2tpcmV0by9heGxlcy90bXAvd2VicGFja2VyX2Nzcy9hcHAvamF2YXNjcmlwdC9wYWNrcy9zb21lLnNjc3MiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7RUFBWSxXQUFXLEVBQUEiLCJmaWxlIjoic29tZS5zY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLm15X3N0eWxlIHsgY29sb3I6IGJsdWV9Il19 */</style></head>

If extract_css is set to true with the import('../some') i get

Webpacker can't find some.css in /home/user/webpacker_css/public/packs/manifest.json. Possible causes:
1. You want to set webpacker.yml value of compile to true for your environment
   unless you are using the `webpack -w` or the webpack-dev-server.
2. webpack has not yet re-run to reflect updates.
3. You have misconfigured Webpacker's config/webpacker.yml file.
4. Your webpack configuration is not creating a manifest.
Your manifest contains:
{
  "application.js": "/packs/js/application-d3fbd49ce11f47bb7780.js",
  "application.js.map": "/packs/js/application-d3fbd49ce11f47bb7780.js.map",
  "entrypoints": {
    "application": {
      "js": [
        "/packs/js/application-d3fbd49ce11f47bb7780.js"
      ],
      "js.map": [
        "/packs/js/application-d3fbd49ce11f47bb7780.js.map"
      ]
    },
    "some": {
      "js": [
        "/packs/js/some-bc0a27dabebad8f5d13f.js"
      ],
      "js.map": [
        "/packs/js/some-bc0a27dabebad8f5d13f.js.map"
      ]
    }
  },
  "some.js": "/packs/js/some-bc0a27dabebad8f5d13f.js",
  "some.js.map": "/packs/js/some-bc0a27dabebad8f5d13f.js.map"
}

If I now remove

// app/javascript/packs/application.js
- import("./some") 

I get
the following head

    <title>WebpackerCss</title>
    <meta name="csrf-param" content="authenticity_token">
<meta name="csrf-token" content="b3WxEHPrIhjdo6KmZ6b2YbYXQfNKkeGkm13K2qyox+tVhlMEmV5tAy9ShwFhWWinG5u+sLvs7pEAxkALShEGYA==">
   
    <link rel="stylesheet" media="all" href="/packs/css/some-49f8f0b6.css" data-turbolinks-track="reload">
    <script src="/packs/js/application-4d2748c6033473f44cd6.js" data-turbolinks-track="reload"></script>
  </head>

So my style is missing.

Current state and summary

I know it got complex and long the whole story. Let me try to summarize it
I can't find a way to deliver the same style when extract_css is true, or false.
The style is delivered in one of the cases but not the other

Case 1

<%= stylesheet_pack_tag 'some', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

extract_css: false

import('./some')

<head>
  <title>WebpackerCss</title>
  <meta name="csrf-param" content="authenticity_token">
  <meta name="csrf-token" content="JSQi+4kN64d6XEQmwB35ZlCXdD5bEjmTc6MYfyTO/wEf18DvY7iknIitYYHG4meg/RuLfapvNqboOJKuwnc+ig==">
  <script src="/packs/js/application-7093eb9982a06d5bf25d.js" data-turbolinks-track="reload"></script><link rel="stylesheet" type="text/css" href="/packs/css/0-49f8f0b6.chunk.css"><script charset="utf-8" src="/packs/js/0-1bc6b104668542a9d05b.chunk.js"></script>
</head>

I don have my .my_style { color: blue} anywhere

Case 2

<%= stylesheet_pack_tag 'some', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

extract_css: false

// import('./some')

<head>
  <title>WebpackerCss</title>
  <meta name="csrf-param" content="authenticity_token">
  <meta name="csrf-token" content="s3nhPw85URHL+DJRbA9yNSwA1IcMrRdpF30Nwtp9JYOJigMr5YweCjkJF/Zq8OzzgYwrxP3QGFyM5ocTPMTkCA==">
  <script src="/packs/js/application-7093eb9982a06d5bf25d.js" data-turbolinks-track="reload"></script><link rel="stylesheet" type="text/css" href="/packs/css/0-49f8f0b6.chunk.css"><script charset="utf-8" src="/packs/js/0-1bc6b104668542a9d05b.chunk.js"></script>
</head>

I don't have my .my_style { color: blue} anywhere

Case 3

<%= stylesheet_pack_tag 'some', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

extract_css: false

import('./some.scss') <-- THIS HERE IS WITH EXTENSION .scss

I have the style .my_style because the ./some.scss is used and not ./some

<head>
    <title>WebpackerCss</title>
    <meta name="csrf-param" content="authenticity_token">
    <meta name="csrf-token" content="R3Ai++eRWohPFTVwZTYk5GWLqj+0FgKz7aEJDSaYdAF9g8DvDSQVk73kENdjyboiyAdVfEVrDYZ2OoPcwCG1ig==">
    <script src="/packs/js/application-db7a6edc43abf62f2616.js" data-turbolinks-track="reload"></script><script charset="utf-8" src="/packs/js/0-ef9c2fc826d70bf4668d.chunk.js"></script>
  <style>.my_style {
  color: blue; }
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9ob21lL2tpcmV0by9heGxlcy90bXAvd2VicGFja2VyX2Nzcy9hcHAvamF2YXNjcmlwdC9wYWNrcy9zb21lLnNjc3MiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7RUFBWSxXQUFXLEVBQUEiLCJmaWxlIjoic29tZS5zY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLm15X3N0eWxlIHsgY29sb3I6IGJsdWV9Il19 */</style>
</head>

Case 4

<%= stylesheet_pack_tag 'some', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

extract_css: true
import('./some.scss')

Error occurs

Webpacker can't find some.css in /home/kireto/axles/tmp/webpacker_css/public/packs/manifest.json. Possible causes:
1. You want to set webpacker.yml value of compile to true for your environment
   unless you are using the `webpack -w` or the webpack-dev-server.
2. webpack has not yet re-run to reflect updates.
3. You have misconfigured Webpacker's config/webpacker.yml file.
4. Your webpack configuration is not creating a manifest.
Your manifest contains:
{
  "application.js": "/packs/js/application-db7a6edc43abf62f2616.js",
  "application.js.map": "/packs/js/application-db7a6edc43abf62f2616.js.map",
  "entrypoints": {
    "application": {
      "js": [
        "/packs/js/application-db7a6edc43abf62f2616.js"
      ],
      "js.map": [
        "/packs/js/application-db7a6edc43abf62f2616.js.map"
      ]
    },
    "some": {
      "js": [
        "/packs/js/some-bc0a27dabebad8f5d13f.js"
      ],
      "js.map": [
        "/packs/js/some-bc0a27dabebad8f5d13f.js.map"
      ]
    }
  },
  "some.js": "/packs/js/some-bc0a27dabebad8f5d13f.js",
  "some.js.map": "/packs/js/some-bc0a27dabebad8f5d13f.js.map"
}

I have the style .my_style

Case 5

<%= stylesheet_pack_tag 'some', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

extract_css: true
// import('./some')

<head>
  <title>WebpackerCss</title>
  <meta name="csrf-param" content="authenticity_token">
  <meta name="csrf-token" content="U+qqUFoSPEQPi76klUpvLgSIAjt+SuA69IK89b1Gee1pGUhEsKdzX/16mwOTtfHoqQT9eI837w9vGTYkW/+4Zg==">
  <link rel="stylesheet" media="all" href="/packs/css/some-49f8f0b6.css" data-turbolinks-track="reload">
  <script src="/packs/js/application-7093eb9982a06d5bf25d.js" data-turbolinks-track="reload"></script><link rel="stylesheet" type="text/css" href="/packs/css/0-49f8f0b6.chunk.css"><script charset="utf-8" src="/packs/js/0-1bc6b104668542a9d05b.chunk.js"></script>
</head>

I have the style .my_style

Case 6

<%= stylesheet_pack_tag 'some', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

extract_css: true

import('./some')

<head>
  <title>WebpackerCss</title>
  <meta name="csrf-param" content="authenticity_token">
  <meta name="csrf-token" content="u0y5+RjXJL7JcDA/uGiEHQ5ndqvIb4zRxpvmIqHcrY+Bv1vt8mJrpTuBFZi+lxrbo+uJ6DkSg+RdAGzzR2VsBA==">
  <link rel="stylesheet" media="all" href="/packs/css/some-49f8f0b6.css" data-turbolinks-track="reload">
  <script src="/packs/js/application-7093eb9982a06d5bf25d.js" data-turbolinks-track="reload"></script><link rel="stylesheet" type="text/css" href="/packs/css/0-49f8f0b6.chunk.css"><script charset="utf-8" src="/packs/js/0-1bc6b104668542a9d05b.chunk.js"></script>
</head>

I have the style .my_style

I can't find a way to deliver the same style when extract_css is true, or false.
The style is delivered in one of the cases but not the other

Summary of the cases

Case 1

extract_css: false
import('./some')
.my_style is NOT available

Cast 2

extract_css: false
// import('./some')
.my_style IS NOT available

Case 3

extract_css: false
import('./some.scss') <-- THIS HERE IS WITH EXTENSION .scss
.my_style IS available as inlinded <script>

Case 4

extract_css: true
import('./some.scss')
Error occurs - Webpacker can't find some.css

Case 5

extract_css: true
// import('./some')
.my_style IS available from href="/packs/css/some-49f8f0b6.css"

Case 6

extract_css: true
import('./some')
.my_style IS available from href="/packs/css/some-49f8f0b6.css"

Case 7

extract_css: false
import('./some.scss')

Did it again an the style is not available

<head>
  <title>WebpackerCss</title>
  <script src="/packs/js/application-4ef3c8b3912795b5c117.js" data-turbolinks-track="reload"></script>
  <link rel="stylesheet" type="text/css" href="/packs/css/0-49f8f0b6.chunk.css">
  <script charset="utf-8" src="/packs/js/0-1bc6b104668542a9d05b.chunk.js"></script>
</head>

Case 8

extract_css: true
import('./some.scss')

<head>
  <title>WebpackerCss</title>
  <link rel="stylesheet" media="all" href="/packs/css/some-49f8f0b6.css" data-turbolinks-track="reload">
  <script src="/packs/js/application-4ef3c8b3912795b5c117.js" data-turbolinks-track="reload"></script>
  <link rel="stylesheet" type="text/css" href="/packs/css/0-49f8f0b6.chunk.css">
  <script charset="utf-8" src="/packs/js/0-1bc6b104668542a9d05b.chunk.js"></script>
</head>

Same as case 4 but this time there is a link to href="/packs/css/some-49f8f0b6.css" and there is no error

Case 9

extract_css: false
import('./some.scss')

<head>
  <title>WebpackerCss</title>
  <link rel="stylesheet" media="all" href="/packs/css/some-49f8f0b6.css" data-turbolinks-track="reload">
  <script src="/packs/js/application-4ef3c8b3912795b5c117.js" data-turbolinks-track="reload"></script>
  <link rel="stylesheet" type="text/css" href="/packs/css/0-49f8f0b6.chunk.css">
  <script charset="utf-8" src="/packs/js/0-1bc6b104668542a9d05b.chunk.js"></script>
</head>

What is the issue

The issue is that I've managed to get the same configuration cases 9,3 and cases 8, 4 that have same configuration
but behave in a different way depending on something

@thebravoman
Copy link
Author

thebravoman commented May 28, 2020

I think I got a reproducible script to show the issues. There are three issues:

What are the issues:

Case 1 - export_css: false; no import - NO style
Case 2 - export_css: true; no import - NO style
Case 3 - export_css: true; import('./some') - NO style
Case 4 - export_css: false; import('./some') - NO style
Case 5 - export_css: false; import('./some.scss') - YES style - inline script
Case 6 - export_css: true; import('./some.scss') - Webpacker can't find some in
Case 7 - export_css: true; import('./some.css') - YES style - link
Case 8 - export_css: false; import('./some.css') - NO style
Case 9 - export_css: true; no import - YES style - link

Based on the above results there are several problems.

  1. Case 2 and case 9 are the same, but case 9 works. Why - some caching probably.
  2. Case 5 and case 6 are incompatible. The first works. The second does not. You can't just change the configuration. You must also change the content of the application.js
  3. Cast 7 and case 8 are incompatible. The first works, but the second does not. Again - you can't just change the configuration. You must also change the content of application.js

Problem 1 is some caching issue.
Problem 2 and 3 is that you can have "export_css: false; import('./some.scss')" or "export_css: true; import('./some.css')" but you can't have "export_css: true; import('./some.scss')" and "export_css: false; import('./some.css')"

This means you have to change the content of application.js depending on the configuration of the environment. and you can't start the same application.js and application.html.erb in different configuration - dev, production, test.

The scripts to reproduce

Please restart the server between each of the scripts.

$ rails new webpacker_css1 --webpack
$ cd webpacker_css1/
$ rails g scaffold book
$ rake db:migrate
$ echo ".my_style {color: blue}" > app/javascript/packs/some.scss
# Replace stylesheet_link_tag with stylesheet_pack_tag
$ sed -i "s/stylesheet_link_tag 'application'/stylesheet_pack_tag 'some'/g" app/views/layouts/application.html.erb

Case 1

$ echo "export_css: false; no import in application" > case1.log
$ rails s
$ curl localhost:3000/books >> case1.log

Result: no style

Case 2

$ echo "export_css: true; no import in application" > case2.log
# Replace extract_css: false with extract_css: true
$ sed -i 's/extract_css: false/extract_css: true/g' config/webpacker.yml
$ rails s
$ curl localhost:3000/books >> case2.log

Result: error occurs "Webpacker can't find some in"

Case 3

# There already is export_css: true in config/webpacker.yml
$ echo "export_css: true; import('./some') in application" > case3.log
$ echo "import('./some')" >> app/javascript/packs/application.js
$ rails s
$ curl localhost:3000/books >> case3.log

Result: some.css is imported with

Case 4

$ echo "export_css: false; import('./some') in application" > case4.log
# Replace extract_css: true with extract_css: false
$ sed -i 's/extract_css: true/extract_css: false/g' config/webpacker.yml
# There already is import('./some') in app/javascript/packs/application.js
$ rails s
$ curl localhost:3000/books >> case4.log

Result: There is NO style. No <script>
You open the browser as curl is not doing executing the js:

<head>
  <title>WebpackerCss1</title>
  <meta name="csrf-param" content="authenticity_token">
  <meta name="csrf-token" content="tjlPRDCzhypzayHg0dY3O3FaopjBuSVUxINEbnCcn9lga1c+FS4wty8E/OGEHL42iiRDgVV+yMqHKaUqJf6tOQ==">
  <script src="/packs/js/application-1cbf96d3e2109440ea2c.js" data-turbolinks-track="reload"></script>
  <link rel="stylesheet" type="text/css" href="/packs/css/0-1bd9bff2.chunk.css">
  <script charset="utf-8" src="/packs/js/0-1bc6b104668542a9d05b.chunk.js"></script>
</head>

Question: Where is the <script> inlining the css?

Case 5

$ echo "export_css: false; import('./some.scss') in application" > case5.log
# Replace import('./some') with import('./some.scss') true
$ sed -i "s/some/some.scss/g" app/javascript/packs/application.js
$ rails s
$ curl localhost:3000/books >> case5.log

Curl Result: There is no style. No <script> because curl does not execute the js.
Browser Result: There is an inline script

<head>
    <title>WebpackerCss1</title>
    <meta name="csrf-param" content="authenticity_token">
    <meta name="csrf-token" content="BgXKJm/rYtP6D6zek4TMWRkqIw9dPnBcwbfwByxdXyTQV9JcSnbVTqZgcd/GTkVU4lTCFsn5ncKCHRFDeT9txA==">
    <script src="/packs/js/application-0053cf706d6f48a5886a.js" data-turbolinks-track="reload"></script><script charset="utf-8" src="/packs/js/0-e8c819fe7947a5da32d9.chunk.js"></script>
  <style>.my_style {
  color: blue; }
/*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9ob21lL2tpcmV0by9heGxlcy90bXAvd2VicGFja2VyX2NzczEvYXBwL2phdmFzY3JpcHQvcGFja3Mvc29tZS5zY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0VBQVcsV0FBVyxFQUFBIiwiZmlsZSI6InNvbWUuc2NzcyIsInNvdXJjZXNDb250ZW50IjpbIi5teV9zdHlsZSB7Y29sb3I6IGJsdWV9XG4iXX0= */</style></head>

Case 6

$ echo "export_css: true; import('./some.scss') in application" > case6.log
# Replace extract_css: false with extract_css: true
$ sed -i 's/extract_css: false/extract_css: true/g' config/webpacker.yml
$ rails s
$ curl localhost:3000/books >> case6.log

Result: Webpacker can't find some in
Conclusion if you have import('./some.scss') you can have export_css: true - Case 6 and case 5 contradict

Case 7

# Change the some.scss to some.css
$ echo "export_css: true; import('./some.css') in application" > case7.log
# Replace import('./some.scss') with import('./some.css')
$ sed -i "s/some.scss/some.css/g" app/javascript/packs/application.js
$ rails s
$ curl localhost:3000/books >> case7.log

Result: There is a
But there is an error in compilation

                                   Asset       Size       Chunks                         Chunk Names
                     css/some-1bd9bff2.css   75 bytes         some  [emitted] [immutable]  some
                 css/some-1bd9bff2.css.map  190 bytes         some  [emitted] [dev]        some
    js/application-bb8f70666c8a53092174.js    124 KiB  application  [emitted] [immutable]  application
js/application-bb8f70666c8a53092174.js.map    139 KiB  application  [emitted] [dev]        application
           js/some-254e34049def418d0032.js   3.92 KiB         some  [emitted] [immutable]  some
       js/some-254e34049def418d0032.js.map   3.61 KiB         some  [emitted] [dev]        some
                             manifest.json  888 bytes               [emitted]              
Entrypoint application = js/application-bb8f70666c8a53092174.js js/application-bb8f70666c8a53092174.js.map
Entrypoint some = css/some-1bd9bff2.css js/some-254e34049def418d0032.js css/some-1bd9bff2.css.map js/some-254e34049def418d0032.js.map
[./app/javascript/channels sync recursive _channel\.js$] ./app/javascript/channels sync _channel\.js$ 160 bytes {application} [built]
[./app/javascript/channels/index.js] 211 bytes {application} [built]
[./app/javascript/packs/application.js] 773 bytes {application} [built]
[./app/javascript/packs/some.scss] 39 bytes {some} [built]
[./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 552 bytes {application} [built]
    + 4 hidden modules

ERROR in ./app/javascript/packs/application.js
Module not found: Error: Can't resolve './some.css' in '/home/user/webpacker_css1/app/javascript/packs'
 @ ./app/javascript/packs/application.js 19:0-20
Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--7-1!node_modules/postcss-loader/src/index.js??ref--7-2!node_modules/sass-loader/dist/cjs.js??ref--7-3!app/javascript/packs/some.scss:
    Entrypoint mini-css-extract-plugin = *
    [./node_modules/css-loader/dist/cjs.js?!./node_modules/postcss-loader/src/index.js?!./node_modules/sass-loader/dist/cjs.js?!./app/javascript/packs/some.scss] ./node_modules/css-loader/dist/cjs.js??ref--7-1!./node_modules/postcss-loader/src??ref--7-2!./node_modules/sass-loader/dist/cjs.js??ref--7-3!./app/javascript/packs/some.scss 475 bytes {mini-css-extract-plugin} [built]
        + 1 hidden module

Question: Why is there and error and in the same time the some.css is returned in the . It stopped occurring after the first request?

Case 8

# Change export_css to false
$ echo "export_css: false; import('./some.css') in application" > case8.log
# Replace extract_css: true with extract_css: false
$ sed -i 's/extract_css: true/extract_css: false/g' config/webpacker.yml
$ rails s
$ curl localhost:3000/books >> case8.log

Curl result: There is no style. No <script> because curl does not execute the js.
Browser Result: There is no inline style

<head>
  <title>WebpackerCss1</title>
  <meta name="csrf-param" content="authenticity_token">
  <meta name="csrf-token" content="BneWEpW7kuz9/KqrO0bPlea0TAfWisVXBYWzbzYs+rzQJY5osCYlcaGTd6pujEaYHcqtHkJNKMlGL1IrY07IXA==">
  <script src="/packs/js/application-bb8f70666c8a53092174.js" data-turbolinks-track="reload"></script>
</head>

Case 9

# Return to case two Case 2
$ echo "export_css: true; no import in application" > case9.log
# Replace extract_css: false with extract_css: true
$ sed -i 's/extract_css: false/extract_css: true/g' config/webpacker.yml
# Remove import('./some.css')  - replace it with nothing 
$ sed -i "s/import('.\/some.css')//g" app/javascript/packs/application.js
$ rails s
$ curl localhost:3000/books >> case9.log

Result: there is now

@rossta
Copy link
Member

rossta commented May 28, 2020

@thebravoman I'm sorry to hear about the difficulties you've been experiencing getting your stylesheet to compile properly. This sounds pretty painful.

Also, thank you, for documenting your workflow so thoroughly. This is really helpful information that I imagine would help the core team understand your experience better and will hopefully lead to some improvements in code and documentation to ensure a better experience.

I went through each of your examples and added my summaries:

Case 1

With extract_css: false, a matching javascript pack tag is needed because webpack will insert a link tag for you dynamically from some.js. This is not well-documented:

<%= stylesheet_pack_tag 'some', media: 'all' %>
<%= javascript_pack_tag 'some' %> <%# <---- needed when extract_css: false %>
<%= javascript_pack_tag 'application' %>

My opinion is that extract_css: true should be the default for all environments. Why do you want extract_css: false? I believe the main use case is for hot-module reloading in development. Maybe webpack compiles faster without it in development? Mileage-may-vary. Mostly, it adds confusion due to the javascript requirement.

Summary:

  • Missing javascript tag
  • Documentation could help avoid confusion

Case 2

I'm not entirely sure what happened here, since, as you said, this is the same as Case 9. I would expect this to work.

Case 3

I had trouble following this case or reproducing. In the descriptions, you shared that extract_css: false and in the script instructions, it appears extract_css: true. In either case, I was able to see the styles load.

My comment here is that I would not recommend this approach for two reasons:

  1. You're dynamically importing the CSS with the form import('./my-css'). The correct form for your use case is import '.my-css' (no parens). The two syntaxes are very different. The first is a dynamic import which webpack will convert to a generically named separate file like 0-1bd9bff2.chunk.css. Webpack will load this file asynchronously when the import is evaluated in the browser (and may result in FOUC). The second form says "include this file in my bundle!", which will include the css in the named bundle, some.css and be available on the initial page load when using extract_css: true.

  2. You're importing one pack from another. I would not recommend doing this for any reason. I don't believe this is well-understood or documented. This tells webpack to insert two dependency graphs on the page. It may work for a small example like this, but mostly, can lead to bugs.

What adds to this confusion is that you're attempting to add the 'some' pack twice, once through <%= stylesheet_link_tag 'some' %> and once through <%= javascript_link_tag 'application' %> (which is importing the "pack" again).

Summary:

  • use import '../path/to/css' instead of import(...)
  • don't import one pack from another
  • better documentation could help

Case 4

Similar to Case 3. Here, you can see the dynamic imports inserted into the head in your example:

...
<link rel="stylesheet" type="text/css" href="/packs/css/0-1bd9bff2.chunk.css">
<script charset="utf-8" src="/packs/js/0-1bc6b104668542a9d05b.chunk.js"></script>
</head>

Summary:

  • use import '../path/to/css' instead of import(...)
  • don't import one pack from another
  • again, better documentation could help

Case 5

Same as Case 3 and 4. I wouldn't expect the extension to matter for an unambiguously named import, but being explicit is always a good idea.

Case 6

This results in an error for the same reason as Case 1; a javascript tag for the same bundle, some, is needed when extract_css: false. Similar recommendations to avoid dynamic import and/or importing one pack from another as Case 3, 4, 5.

Case 7

I wasn't able to reproduce this error.

Case 8

Similar to Case 1 again.

Case 9

I would expect this to work.


Recommendation

My recommendation for you is to rename some.css to application.css (or application.scss) at this point, with no import in application.js. This is supported in Webpacker 5.

$ tree app/javascript/packs
app/javascript/packs
├── application.js
└── application.scss
<%= stylesheet_pack_tag 'application', media: 'all' %>
<%= javascript_pack_tag 'application' %>
extract_css: true

Also, I recommend using import 'path/to/css' when importing styles from javascript.

@thebravoman
Copy link
Author

Tried to reproduce it. Follow the exact same commands but this time the error on Case 2 is

error Command "webpack" not found.
Completed 500 Internal Server Error in 1124ms (ActiveRecord: 0.4ms | Allocations: 8380)

It's like a random component exists when creating a rails project with webpacker

@thebravoman
Copy link
Author

thebravoman commented May 28, 2020

I have found a solution. Here is how to make it work. I guess it should be added in the documentation.

Webpacker 5 is release about a two months ago and with it you can get it working

$ rails new webpacker_css --webpack
$ cd webpacker_css

Change Gemfile

- gem 'webpacker', '~> 4.0'
+gem 'webpacker', '~> 5.0'
$ bundle update
# Create your style in an scss  
$ echo ".my_style { color: blue} " > app/javascript/packs/some.scss 

Change application layout

// app/views/layouts/application.html.erb
-  <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
+ <%= stylesheet_pack_tag 'some', media: 'all', 'data-turbolinks-track': 'reload' %>

// note that the file here is 'some'

Change extract_css to true

# config/webpacker.yml
development:
  <<: *default
  compile: true

+  extract_css: true

Start server and now you will have stylesheet separated from js.

<head>
    <title>WebpackerCss5</title>
    <link rel="stylesheet" media="all" href="/packs/css/some-a2dd1d89.css" data-turbolinks-track="reload">
    <script src="/packs/js/application-9afcbb5693aa87623e69.js" data-turbolinks-track="reload"></script>
</head>

Compilation looks like this

[Webpacker] Compiling...
[Webpacker] Compiled all packs in /home/user/webpacker_css5/public/packs
[Webpacker] Hash: b2c960f973e5e3c3c25c
Version: webpack 4.43.0
Time: 3272ms
Built at: 05/28/2020 4:18:38 PM
                                     Asset       Size       Chunks                         Chunk Names
                     css/some-a2dd1d89.css   75 bytes         some  [emitted] [immutable]  some
                 css/some-a2dd1d89.css.map  188 bytes         some  [emitted] [dev]        some
    js/application-9afcbb5693aa87623e69.js    124 KiB  application  [emitted] [immutable]  application
js/application-9afcbb5693aa87623e69.js.map    139 KiB  application  [emitted] [dev]        application
           js/some-254e34049def418d0032.js   3.92 KiB         some  [emitted] [immutable]  some
       js/some-254e34049def418d0032.js.map   3.61 KiB         some  [emitted] [dev]        some
                             manifest.json  888 bytes               [emitted]              
Entrypoint application = js/application-9afcbb5693aa87623e69.js js/application-9afcbb5693aa87623e69.js.map
Entrypoint some = css/some-a2dd1d89.css js/some-254e34049def418d0032.js css/some-a2dd1d89.css.map js/some-254e34049def418d0032.js.map
[./app/javascript/channels sync recursive _channel\.js$] ./app/javascript/channels sync _channel\.js$ 160 bytes {application} [built]
[./app/javascript/channels/index.js] 211 bytes {application} [built]
[./app/javascript/packs/application.js] 749 bytes {application} [built]
[./app/javascript/packs/some.scss] 39 bytes {some} [built]
[./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 552 bytes {application} [built]
    + 4 hidden modules
Child mini-css-extract-plugin node_modules/css-loader/dist/cjs.js??ref--7-1!node_modules/postcss-loader/src/index.js??ref--7-2!node_modules/sass-loader/dist/cjs.js??ref--7-3!app/javascript/packs/some.scss:
    Entrypoint mini-css-extract-plugin = *
    [./node_modules/css-loader/dist/cjs.js?!./node_modules/postcss-loader/src/index.js?!./node_modules/sass-loader/dist/cjs.js?!./app/javascript/packs/some.scss] ./node_modules/css-loader/dist/cjs.js??ref--7-1!./node_modules/postcss-loader/src??ref--7-2!./node_modules/sass-loader/dist/cjs.js??ref--7-3!./app/javascript/packs/some.scss 473 bytes {mini-css-extract-plugin} [built]
        + 1 hidden module

[Webpacker] Everything's up-to-date. Nothing to do

I will think of a PR for the documentation if this fixes my FOUC.

@rossta
Copy link
Member

rossta commented May 28, 2020

Also, make sure to use import 'path/to/css' instead of import('path/to/css') in general (unless you really want async CSS!)

@rossta
Copy link
Member

rossta commented May 29, 2020

I opened #2608 with the hopes of improving the out-of-box experience and helping developers avoid situations like @thebravoman encountered.

@qideng
Copy link

qideng commented Nov 9, 2022

Was able to fix the error Command "webpack" not found. issue by running

bundle exec rails webpacker:install

credit: https://stackoverflow.com/a/65166138/20455528

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

Successfully merging a pull request may close this issue.

3 participants