gauravtiwari / webpacker-example-app

An example app to demonstrate webpacker with Rails 5.1
https://webpacker-example-app.herokuapp.com/
36 stars 11 forks source link

Configuration confusion with shared.js and shared_new.js #4

Closed secondstreetmedia closed 7 years ago

secondstreetmedia commented 7 years ago

Hi,

I'm trying to implement your configuration in an application and I've run into a couple of obstacles. Webpack is fairly new to me, so I'm not always successful at deciphering the code. Therefore, I first need to confirm my assumption that for shared.js and shared_new.js, their use is an either/or endeavor, i.e. I don't use both. Is this correct?

If so, I'm attempting to use shared_new.js and initially hit a problem where this line:

entry: fs.readdirSync(path.resolve(paths.entry)).reduce((entries, entry) => {

which, based on this error message:

23:39:28 watcher.1 | fs.js:912
23:39:28 watcher.1 |   return binding.readdir(pathModule._makeLong(path), options.encoding);
23:39:28 watcher.1 |                  ^
23:39:28 watcher.1 |
23:39:28 watcher.1 | Error: ENOENT: no such file or directory, scandir '/Users/my-mac/workspace/rails_on_webpack/bundles'
23:39:28 watcher.1 |     at Object.fs.readdirSync (fs.js:912:18)
23:39:28 watcher.1 |     at Object.<anonymous> (/Users/my-mac/workspace/rails_on_webpack/config/webpack/shared.js:14:13)

seemed to be resulting in a file path of ~/bundles rather than ~/app/javascript/bundles. The solution appears to be amending the line to this:

  entry: fs.readdirSync(path.resolve(paths.source, paths.entry)).reduce((entries, entry) => {

but I don't know for sure because I'm running into an additional error.

To that end, after getting past the previous error, I started getting this error:

23:44:05 watcher.1 | fs.js:912
23:44:05 watcher.1 |   return binding.readdir(pathModule._makeLong(path), options.encoding);
23:44:05 watcher.1 |                  ^
23:44:05 watcher.1 |
23:44:05 watcher.1 | TypeError: path must be a string or Buffer
23:44:05 watcher.1 |     at Object.fs.readdirSync (fs.js:912:18)
23:44:05 watcher.1 |     at Object.<anonymous> (/Users/my-mac/workspace/rails_on_webpack/config/webpack/shared.js:25:15)

Note that though the path indicates shared.js, it is the shared_new.js code.

The problem looks to be occurring here:

module: {
  rules: fs.readdirSync(rulesDir).map((file) => (
      require(path.join(rulesDir, file))
  ))
},

While there is a constant declaration at the top for rulesDir:

const { env, extensions, paths, publicPath, rulesDir } = require('./configuration.js')

within configuration.js, there doesn't appear to be any declaration of rulesDir establishing its value.

Are these indeed issues or am I doing something wrong?

Thanks!

gauravtiwari commented 7 years ago

@secondstreetmedia Sorry, just pushed an update. Please check it out. Yepp, I have been playing around with this lately, shared_new and creeped in accidentally.

gauravtiwari commented 7 years ago

@secondstreetmedia Try out the improved webpacker with new generators. It's all merged and integrated 🎉 https://github.com/rails/webpacker

secondstreetmedia commented 7 years ago

Thanks!! So, I've updated everything (gem and configuration), and I'm making progress, but I'm still not there yet. Since my issues are likely mine and not the gem, what's the best way to get guidance? Normally I would go to Stack Overflow, but since this is evolving so rapidly, I'm not optimistic that anyone will have tackled these issues yet.

gauravtiwari commented 7 years ago

What issues are you having?

secondstreetmedia commented 7 years ago

I should state up front that what I'm attempting to do is handle all of my assets using webpack only -- no sprockets, Rails asset pipeline, etc. Currently, I'm trying to understand how things should be placed in the app/javascript subdirectory. This will give you the general idea of how I currently have it set up:

image

Each of these pack subdirectories (e.g. /daemonite_material) has an index.js file as well as /components, /fonts, /images and/or /styles subdirectories, depending upon what assets are needed for that particular pack. These subdirectories only contain assets I'm supplying, such as font files, custom stylesheets, custom javascript, etc., assets I would have previously placed in the app/assets subdirectories, were I using the Rails asset pipeline. And their index.js files look like this: /daemonite_material/index.js

require.context('daemonite-material/css/images', true, /\.(jpe?g|png|gif|svg)$/);
require.context('./fonts', true, /\.(eot|svg|ttf|woff|woff2)$/);
require('./styles/daemonite_material_local_font_styles.scss');
require('daemonite-material/css/material.min.css');
require('daemonite-material/js/material.min.js');

In this examples, as you can see, I am supplying fonts and a custom stylesheet daemonite_material_local_font_styles.scss. The remaining assets are being drawn from the daemonite_material node module.

Any pack which is simply using assets supplied by a node module (e.g. bootstrap - pack subdirectory name: bootstrap_four_alpha_six ), has only an index.js file that looks like this:

import 'bootstrap/dist/js/bootstrap.min.js';

So, with everything set up in this manner, the fonts, images and stylesheets (both custom and node module supplied) appear to be loading and processing properly. The problem I'm having is with the javascript. I end up with a separate javascript file in the /public/packs for every pack, which seems fine, but they are all the same. That is, they have different file names, but they have the same javascript, which is all of the javascript from all of the packs together, much like a global javascript file, except many of them. Additionally, the javascript doesn't work. None of the jquery modals, dropdowns, etc. are functioning. If I look in Chrome dev tools, however, there are no indications of any javascript errors, such as files that aren't found.

Hopefully this gives you some idea of what my problem is. If you need more, I can show you what the public/packs directory looks like, the contents of the manifest.json file, the stylesheet and javascript pack tags from my view templates, or the webpack console output.

Any help would be greatly appreciated?

Thanks!

gauravtiwari commented 7 years ago

Right lets take a look at this together.

Why you have separate packs directory? You got app modules there or just one index file?

gauravtiwari commented 7 years ago

If you need any entry file and it's inside daemonite_material then you can change the entry directory from config/webpack/paths.yml to daemonite_material and then it will use index.js as entry file.

secondstreetmedia commented 7 years ago

The reason I have set up things the way I have is in an attempt to control the stylesheet loading order. I thought it was necessary due to this still unresolved webpack issue: https://github.com/webpack/webpack/issues/215, which seems to indicate that if I approached my setup in a more typical fashion, I wouldn't be able to control the stylesheet loading order, which I want to do. If that isn't an issue, then what would be the recommended way?

Also, breaking things up in this way seems as though it would enable me to load only the files needed for each template, mimicking what I believe code splitting does. I could be wrong, but my understanding is that code splitting is not yet possible with the webpacker gem. Is this correct? If so, does the way I'm attempting to set up my code get me any closer to mimicking code splitting?

Here are more specifics regarding my setup:

app/javascript (complete)

image image

Packs JS Files

app/javascript/packs/bootstrap_four_alpha_six.js

require('../bootstrap_four_alpha_six');

app/javascript/packs/bootstrap_four_alpha_six_docs.js

require('../bootstrap_four_alpha_six_docs');

app/javascript/packs/bootstrap_four_alpha_six_offcanvas.js

require('../bootstrap_four_alpha_six_offcanvas');

app/javascript/packs/daemonite_material.js

require('../daemonite_material');

app/javascript/packs/daemonite_material_project.js

require('../daemonite_material_project');

app/javascript/packs/global_customization.js

require('../global_customization');

app/javascript/packs/jquery_js.js

require('../jquery_js');

app/javascript/packs/tether_js.js

require('../tether_js');

Index Files

app/javascript/bootstrap_four_alpha_six/index.js

require('bootstrap/dist/js/bootstrap.min.js');

app/javascript/bootstrap_four_alpha_six_docs/index.js

require('./styles/docs.min.css');

app/javascript/bootstrap_four_alpha_six_offcanvas/index.js

require('./styles/offcanvas.scss');
require('./components/offcanvas.js');

app/javascript/daemonite_material/index.js

require.context('daemonite-material/css/images', true, /\.(jpe?g|png|gif|svg)$/);
require.context('./fonts', true, /\.(eot|svg|ttf|woff|woff2)$/);
require('./styles/daemonite_material_local_font_styles.scss');
require('daemonite-material/css/material.min.css');
require('daemonite-material/js/material.min.js');

app/javascript/daemonite_material_project/index.js

require('daemonite-material/css/project.min.css');
require('daemonite-material/js/project.min.js');

app/javascript/global_customization/index.js

require('pygments-rouge-css/github.css');
require('./styles/global_custom_styles.scss');
require('./components/global_custom_javascript.js');

app/javascript/jquery_js/index.js

require('jquery/dist/jquery.min.js');

app/javascript/tether_js/index.js

require('tether/dist/js/tether.min.js');


app/public/packs

image

app/public/packs/manifest.json

{
  "MaterialIcons-Regular.eot": "http://localhost:8080/MaterialIcons-Regular.eot",
  "MaterialIcons-Regular.ttf": "http://localhost:8080/MaterialIcons-Regular.ttf",
  "MaterialIcons-Regular.woff": "http://localhost:8080/MaterialIcons-Regular.woff",
  "MaterialIcons-Regular.woff2": "http://localhost:8080/MaterialIcons-Regular.woff2",
  "bootstrap_four_alpha_six.js": "http://localhost:8080/bootstrap_four_alpha_six.js",
  "bootstrap_four_alpha_six.js.map": "http://localhost:8080/bootstrap_four_alpha_six.js.map",
  "bootstrap_four_alpha_six_docs.css": "http://localhost:8080/bootstrap_four_alpha_six_docs.css",
  "bootstrap_four_alpha_six_docs.css.map": "http://localhost:8080/bootstrap_four_alpha_six_docs.css.map",
  "bootstrap_four_alpha_six_docs.js": "http://localhost:8080/bootstrap_four_alpha_six_docs.js",
  "bootstrap_four_alpha_six_docs.js.map": "http://localhost:8080/bootstrap_four_alpha_six_docs.js.map",
  "bootstrap_four_alpha_six_offcanvas.css": "http://localhost:8080/bootstrap_four_alpha_six_offcanvas.css",
  "bootstrap_four_alpha_six_offcanvas.css.map": "http://localhost:8080/bootstrap_four_alpha_six_offcanvas.css.map",
  "bootstrap_four_alpha_six_offcanvas.js": "http://localhost:8080/bootstrap_four_alpha_six_offcanvas.js",
  "bootstrap_four_alpha_six_offcanvas.js.map": "http://localhost:8080/bootstrap_four_alpha_six_offcanvas.js.map",
  "daemonite_material.css": "http://localhost:8080/daemonite_material.css",
  "daemonite_material.css.map": "http://localhost:8080/daemonite_material.css.map",
  "daemonite_material.js": "http://localhost:8080/daemonite_material.js",
  "daemonite_material.js.map": "http://localhost:8080/daemonite_material.js.map",
  "daemonite_material_project.css": "http://localhost:8080/daemonite_material_project.css",
  "daemonite_material_project.css.map": "http://localhost:8080/daemonite_material_project.css.map",
  "daemonite_material_project.js": "http://localhost:8080/daemonite_material_project.js",
  "daemonite_material_project.js.map": "http://localhost:8080/daemonite_material_project.js.map",
  "doc-jumbotron-bg.jpg": "http://localhost:8080/doc-jumbotron-bg.jpg",
  "global_customization.css": "http://localhost:8080/global_customization.css",
  "global_customization.css.map": "http://localhost:8080/global_customization.css.map",
  "global_customization.js": "http://localhost:8080/global_customization.js",
  "global_customization.js.map": "http://localhost:8080/global_customization.js.map",
  "jquery_js.js": "http://localhost:8080/jquery_js.js",
  "jquery_js.js.map": "http://localhost:8080/jquery_js.js.map",
  "roboto-v15-latin-300.woff": "http://localhost:8080/roboto-v15-latin-300.woff",
  "roboto-v15-latin-300.woff2": "http://localhost:8080/roboto-v15-latin-300.woff2",
  "roboto-v15-latin-300italic.woff": "http://localhost:8080/roboto-v15-latin-300italic.woff",
  "roboto-v15-latin-300italic.woff2": "http://localhost:8080/roboto-v15-latin-300italic.woff2",
  "roboto-v15-latin-500.woff": "http://localhost:8080/roboto-v15-latin-500.woff",
  "roboto-v15-latin-500.woff2": "http://localhost:8080/roboto-v15-latin-500.woff2",
  "roboto-v15-latin-500italic.woff": "http://localhost:8080/roboto-v15-latin-500italic.woff",
  "roboto-v15-latin-500italic.woff2": "http://localhost:8080/roboto-v15-latin-500italic.woff2",
  "roboto-v15-latin-italic.woff": "http://localhost:8080/roboto-v15-latin-italic.woff",
  "roboto-v15-latin-italic.woff2": "http://localhost:8080/roboto-v15-latin-italic.woff2",
  "roboto-v15-latin-regular.woff": "http://localhost:8080/roboto-v15-latin-regular.woff",
  "roboto-v15-latin-regular.woff2": "http://localhost:8080/roboto-v15-latin-regular.woff2",
  "tether_js.js": "http://localhost:8080/tether_js.js",
  "tether_js.js.map": "http://localhost:8080/tether_js.js.map"
}


From my view template header

= stylesheet_pack_tag 'daemonite_material.css'
= stylesheet_pack_tag 'bootstrap_four_alpha_six_docs.css'
= stylesheet_pack_tag 'daemonite_material_project.css'
= stylesheet_pack_tag 'bootstrap_four_alpha_six_offcanvas.css'
= stylesheet_pack_tag 'global_customization.css'


From the bottom of my view template body

= javascript_pack_tag 'jquery_js.js'
= javascript_pack_tag 'tether_js.js'
= javascript_pack_tag 'bootstrap_four_alpha_six.js'
= javascript_pack_tag 'daemonite_material.js'
= javascript_pack_tag 'daemonite_material_project.js'
= javascript_pack_tag 'bootstrap_four_alpha_six_offcanvas.js'
= javascript_pack_tag 'global_customization.js'
secondstreetmedia commented 7 years ago

Okay, so it wasn't doing exactly what I thought it was doing. All of these individual javascript files:

= javascript_pack_tag 'jquery_js.js'
= javascript_pack_tag 'tether_js.js'
= javascript_pack_tag 'bootstrap_four_alpha_six.js'
= javascript_pack_tag 'daemonite_material.js'
= javascript_pack_tag 'daemonite_material_project.js'
= javascript_pack_tag 'bootstrap_four_alpha_six_offcanvas.js'
= javascript_pack_tag 'global_customization.js'

aren't actually identical, they just seemed identical because they all contained the jQuery javascript, which would be, by far, the bulk of the code in each file. So, each of them appear to contain their own, correct javascript, plus the jQuery javascript. Therefore, I'm not sure how to handle jQuery.

When I was first setting this up, I was getting errors like "$ is not defined" and "jQuery is not defined", etc. These errors lead me to finding this solution (placed at the bottom of config/webpack/development.js):

plugins: [
  new webpack.ProvidePlugin({
    $: "jquery",
    jQuery: "jquery",
    "window.jQuery": "jquery",
    "Tether": 'tether'
  })
]

While this solution gets webpack to compile successfully and loads jQuery, it, as I stated above, unfortunately includes it in all of the javascript files. Any suggestions on how I might handle this? Do you know of anyone using the webpacker gem and loading jQuery along with other javascript successfully?