How to Import node_modules with Webpacker - ruby-on-rails

I'm new to the whole JS/webpacker game and am failing to understand how to import and use a javascript package using webpacker.
I am trying to include and use the Animate On Scroll Library.
I have Webpacker installed and working (I know it's working because I am able to happily use StimulusJs).
Here's what my /javascript/packs/application.js file looks like:
import {
Application
} from "stimulus"
import {
definitionsFromContext
} from "stimulus/webpack-helpers"
import {
trix
} from "trix"
import AOS from "aos"
const application = Application.start()
const context = require.context("controllers", true, /.js$/)
application.load(definitionsFromContext(context))
AOS.init();
I have my javascript_pack_tag included on my application.html.erb as
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload', defer: true %>
I imported the required css files using my /assets/css/application.scss with #import 'aos/dist/aos'; so that shouldn't be the issue.
When I try and use the AOS package by doing something like <h1 class="text-center" data-aos="fade-in">This is a header.</h1> nothing happens. What am I missing?
Thanks.

Upon reading this again I guess it's more a JS issue you are encountering. I'll leave my points about the CSS below in case useful. See the fourth point about webpack-dev-server if you aren't using it already. Does your browser support defer? You could try moving the scripts to right before </body> to see. I'd probably just cut-to-the-chase and set a javascript breakpoint in AOS.init() and see what's happening.
A few points I've leaned recently to help better understand webpacker/css/sprockets...
First, the CSS in the assets folder is outside of webpack. It's sprockets. That's not a problem but often mixing the two presents challenges.
At the very least you'll need a stylesheet_link_tag 'application'... in addition to the pack tags.
Second, is AOS added via yarn or is it a gem? Gems with webpack can be a bit tricky. I and others I encountered gave up trying to use gem assets in webpack. Best to stick to yarn/npm modules for webpack. Otherwise, move all the assets into the sprockets pipeline (ie in the assets/ folder) and use that for this portion of your site. (it's ok to mix them, just keep them separate).
Third, if the AOS module is added via yarn add ... (ie it resides in the nodule_modules directory) then try replacing the CSS import to just
#import '~aos';
This works because node_modules is in the search path and if the plugin folder has a package.json manifest and it includes a "style" entry, it pulls the css file path from there.
Third, you can try moving the CSS to webpack. Try this...
Make an application.scss file in your components subfolder
Add to your packs/application.js file: import '../components/application.scss
Add a stylesheet_pack_tag 'application' .... to your layout
Put your css imports (from node modules) in your new application.scss
Fourth: Use bin/webpack-dev-server. This compiles webpack on the fly whenever any of your source files changes instead of on every page load (saves you a lot time). Since your CSS is now under webpack, it will give you errors if the import isn't right (though sprockets should do this too in your server logs).
Good luck! It gets easier and yarn/webpack is cool, better than the old ruby-gems-for-front-end-components, IMO

Related

How to integrate JqTree in Rails 6 with webpacker, tree is not a function

I am really wanting to know how to integrate jqTree as a webpacker webpack in my Rails 6 application
UPDATE:-
Using yarn add jqtree seems to have magically cleared up most of my issues however I am currently facing tree is not a function error
I am using the Ancestry gem to organise menu items and I need a drag and drop javascript tree view solution that will work nicely with the Ancestry gem. I have picked on jqTree as my desired solutions but I am happy to be persuaded to use an alternative as there seem to be a lot around but initially I would just like to be able to at least get a tree view working within Rails 6
Presumably I have to start by setting up jQuery, for which there are plenty of resources on how to do this so I guess this is more about how to get any jQuery component up and running in a Rails 6 app
I guess I'll have to start with a jsx file and import some stuff and import some css into application.scss but just what this should look like I really am unsure of
So far I have setup jQuery according to the instructions here https://www.botreetechnologies.com/blog/introducing-jquery-in-rails-6-using-webpacker
I can confirm with a simple alert that this is all hooked up and working
I have made some more progress
Instead of downloading the jqTree files, I have used yarn to install jqTree
replacing
I have downloaded the jqTree files and unpacked them to folder called jqTree inside my javascript/packs folder
with
yarn add jqtree
and now I have sorted out the require which is as it should be without the ;
So
require ;'jqTree/tree.jquery.js'
becomes
require('jqtree')
also in my javascript/packs folder I have created a sortable.js file which did contain the following
require ;'jqTree/tree.jquery.js'
jQuery(window).on('load', function () {
alert("Done"); //This works!
});
$(function() {
$('#tree1').tree({
data: data,
autoOpen: true,
dragAndDrop: true
});
})
the ; in the require statement confuses me a lot but the console error was demanding it
This now looks like this
require("jqtree");
$(function() {
alert($('#tree1').data('items'));
});
$('#tree1').tree({
data: $('#tree1').data('items'),
autoOpen: true,
dragAndDrop: true
});
With the above code I get an reference error: data is not defined
In a view I have the following code
<%=javascript_pack_tag("sortable")%>
<%= content_tag "div", id: "tree1", data: {items: #menu_items} do %>
Loading items...
<% end %>
The issue I have now is that my browser is reporting an error that tree is not a function.
In my application.css.scss I have
*= require "jqtree.css"
Which doesn't work
I have a detailed answer for this that has taken me quite sometime to put together, I will update this answer with that detail over the coming days but it starts with getting everything hooked up properly with jQuery, jQuery-ui and the componment itself which in this case is jqTree
Yarn is definitely the answer
Starting with the command line to add the relevant packages
yarn add jquery
yarn add jquery-ui
yarn add jqtree
Once the relevant yarn packages are installed I needed to make jQuery and jQuery-ui available for reasons that I am yet to fully comprehend, a simple require is not enough
Following this post https://www.botreetechnologies.com/blog/introducing-jquery-in-rails-6-using-webpacker
I setup my environment.js file in the config/webpacker folder to look like this
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
);
const aliasConfig = {
'jquery': 'jquery-ui-dist/external/jquery/jquery.js',
'jquery-ui': 'jquery-ui-dist/jquery-ui.js'
};
environment.config.set('resolve.alias', aliasConfig);
module.exports = environment
After restarting my server I created a simple js file called test.js in the app/javascript/packs folder which as far as I can tell is where all javascript now resides although there is still the possibility of using sprockets by putting javascript in the assets/javascript folder, I have been advised, with no explanation as to why, that it is not a good idea to mix webpacker with sprockets for serving javascript.
Webpacker does has the ability to serve stylesheets as well, however I still prefer my stylesheets to be served by sprockets in the app/assets folder and using stylesheet link tags as traditionally done in Rails apps as testing proved to be more efficient this way so mixing sprockets with webpacker in this way doesn't seem to be an issue.
I just chucked a simple alert message into the test.js file just to check that webpacker and jQuery was all hooked up properly.
So test.js looks like this
require("jquery-ui");
require("jqtree");
$(function() {
alert($('#tree1').data('items'));
});
Then to use the javascript I just included a javascript pack tag in the view I wanted to use it in like so.
in a random edit.html.erb view
<h1>Editing Menu Item</h1>
<%= render 'form', menu_item: #menu_item %>
<%= link_to 'Show', [:admin, #menu_item] %> |
<%= link_to 'Back', admin_menu_items_path %>
<%=javascript_pack_tag("test")%>
No need for paths or extensions to the file name, it just works!
I find this approach to be really neat for two reasons
1) I am not unnecessarily bloating out web pages by including page specific javascript in every single page by including it in the application.js file which gets included by default in the layout and therefore in every page.
2) I don't seem to have to worry about DOM loaded events and so far no issues with turbolinks interfering with it, although I suspect that may be more by luck than judgement and I may have to revise that thought in the future and make use of 'data-turbolinks-track': 'reload' option, but so far so good and this has not been necessary.
So now that all is hooked up and working it was time to make the jqTree component work with ancestry gem to be able to structure and organise the menu items for the site,
But up to this point it has been a simple matter of using yarn top install components and hooking up jQuery properly. I didn't use yarn initially and that led me to all sorts of problems resulting in my original question.
The rest is to follow...

When to use stylesheet_pack_tag instead stylesheet_link_tag with Rails 6

When creating a new rails project with Rails 6, it creates an application.html.erb with stylesheet_link_tag to load CSS and javascript_pack_tag for javascript files.
Now, rails 6 also provide a stylesheet_pack_tag, so my question is, when to use it? And if we use it, do we need to add all CSS files in app/javascript folder?
What is the best practice to load css, images with rails 6 and webpacker?
You should use stylesheet_pack_tag if you are importing any CSS in Webpack AND if you have enabled extract_css: true for any environment in config/webpacker.yml.
Given the following directory structure:
app/
javascript/
packs/
application.js
styles/
site.css
And the following code in application.js:
import '../styles/site.css'
You would use <%= stylesheet_pack_tag 'application' %> in your view, i.e., the name of the stylesheet matches the name of the "pack".
At this point, I also recommend renaming app/javascript to something like app/frontend. So, the key changes in config/webpacker.yml:
source_path: app/frontend
extract_css: true
app/
frontend/
packs/
application.js
styles/
site.css
Just to try to clarify this a little. This is based on my current understanding, which seems to work well enough but still might not be fully accurate.
CSS processed by the asset pipeline gets a css_link_tag and css that is imported via a Webpacker javascript bundle is referenced with css_pack_tag.
Asset pipeline css is in app/assets/stylesheets. Webpack css is in app/javascripts/wherever_you_want.
So in webpack, all of the css imported into a javascript bundle is eventually extracted to a servable file that can be referenced via the same name as the js bundle.
So if in app/javascripts/application.js you have:
import 'app/javascripts/css/one.css'
import 'app/javascripts/css/two.css'
import 'app/javascripts/css/three.css'
These will be referenced with
css_pack_tag 'application'
This comes out looking like this in my deploy logs
Entrypoints:
application (430 KiB)
css/application-9d82d487.css
js/application-9f45d7ff735a7a203f28.js
It also bears noting, as was mentioned above that this behavior is affected by the extract_css setting.
Presumably this is false in development by default and true in production. One big GOTCHA with this is that, at least in my case, the css_pack_tag wasn't actually "required" in development, in the sense that removing it had no effect because it wasn't extracted locally. It still "worked" because the css was loaded in the javascript and seemed to be applied somehow that way. So I removed these tags thinking they were unnecessary before my understanding improved. Unfortunately when I deployed to production on heroku some time later, none of my css was working and it took me a while to figure out why and remember that I had removed these css_pack_tag lines.

Importing CSS with webpacker in Rails 6

I use webpacker with Rails and am installing taildwindcss right now. Their installation guide says to use an #import method if I'm using postcss-import. I must say I get confused whenever I have to import CSS to Rails with webpacker, so I have a few questions:
1) The #import method from the docs - is it a JavaScript or CSS import method?
2) If it's a CSS method, why do I have to paste it inside javascript folder (e.g. javascript/stylesheets? I tried to put it inside the application.css file and it doesn't work. I assume it is somehow related to the fact that it's using PostCSS and package was installed via yarn?
3) If the above is true, does it mean that I have to import every CSS package that way if it's installed via yarn?
You will likely want to actually want to import into the CSS and javascript!
The typically setup will have app/javascript/styles/application.css for example which will bootstrap your global css:
#import "tailwindcss/base";
#import "tailwindcss/components";
#import "components/buttons"; // custom components that map to path app/javascript/styles/components/buttons.css for example
#import "tailwindcss/utilities";
In your app/javascript/packs/application.js you will import this:
// other js
import('styles/application.css');
// other js
In your layout you will add <%= stylesheet_pack_tag 'application' %> to add the css from application.js and <%= javascript_pack_tag 'application' %> to add the javascript from application.js.
The reason for this setup is that webpack is going to process application.js and it will handle the CSS and JS separately. Think of javascript/pack/application.js more of a bootstrap/dependencies file than a running javascript file. It's saying here's a list of stuff I need to work. In this case, one of the things is app/javascript/styles/xyz.css, and by the way, use post-css to manage how it is processed.

rails engines and assets

I have a rails engine that has a widget in it. I'm rendering the widget using a layout: false property in render and to style the widget, I'm recycling CSS on my main app.
Now here's the tricky part, I only needed 3 stylesheets for my widget and 2 of them are in my main app, those are bootstrap.css and _ss_font.css. On development, it works fine but when I push it to production, I'm having an error on bootstrap.css, it seems that it's dependent on another file on my vendor assets. How do I include those assets?
Note: If I use =stylesheet_link_tag "application" my problem is partially resolved but I have to handle conflicting class names and it will include all assets on my application. I just want to load the necessary stylesheets to style my widget in the engine.
EDIT 1
As suggested I'm including a more descriptive explanation on this:
So this is the error I'm having.
ActionView::Template::Error (File to import not found or unreadable:
ss-variables. Load path:
/data/serviceseeking_au_staging/releases/20140604114806 (in
/data/serviceseeking_au_staging/releases/20140604114806/vendor/assets/stylesheets/bootstrap.scss)):
2: !!! 5
3: %html
4: %head
5: = stylesheet_link_tag "bootstrap", "_ss_font", "feedback/reviews-widget"
6:
7: %body
8: .reviews-container
lib/action_logger.rb:31:in `call'
This view is inside the engine. As you can see, this is my ideal goal, to only include 3 stylesheets. There's not much to see on the reviews-widget, it's just sass without any external file dependency so I will not show it. So looking at the error message, where is this "ss-variable"? Well, let me take you guys on a field trip first before we arrive there.
I first traced where bootstrap.scss is and found it sitting on the main app under the vendor/assets/stylesheets folder. It's contents are as follows:
#import "bootstrap/variables";
#import "bootstrap/mixins";
#import "bootstrap/scaffolding";
#import "bootstrap/grid";
#import "bootstrap/layouts";
etcetera...
So by tracing boostrap.scss and opening it, I saw that it's importing a partial named _variables.scss, so on with the tour and here's _variable.scss
#import "ss-variables";
// Grays
// -------------------------
$black: #000 !default;
$grayDarker: #222 !default;
(Note: I saw another boostrap.scss inside the /vendor/assets/stylesheets/bootstrap where _variables.ss is located) Aha! There's that little freak, "ss-variable"!! We're getting close on this wild partial chase. So I searched where ss-variables.scss is and found it on my lib/assets/stylesheets/ in the main app and it contains more scss variables.
So tracing them, I formulated this hypothesis that when trying to include bootstrap using stylesheet_link_tag on my engine, the engine doesn't see the dependent files that lies on the main app.
Bonus: I have a remarkable feeling that after this, the _ss_font css will be next. But that's for another story. Thanks.
From the looks of it, your problem might be caused by the asset_fingerprinting feature of the asset pipeline - basically appends all the production assets with an MD5 hash
--
Asset Fingerprinting
Essentially, if you're using the likes of Heroku (which requires your assets to be precompiled for them to be served), you will basically get a series of files in your public/assets folder like this:
|-public
|--assets
|---images
|---stylesheets
|---javascripts
The files contained in this folder, when precompiled, all have the fingerprinted filename. This means if you wanted to call one of these, you will need to use one of the ERB / SCSS preprocessors to access them:
#app/assets/stylesheets/application.css.scss
.style {
background: asset_url("background.png");
}
This will reference the file, regardless of whether it's been precompiled (& fingerprinted) or not. Although not exactly your issue, it is an important factor in what you're asking
--
Fix
I would suggest your issue is likely that you're referencing "static" CSS files, which really need to be dynamically referenced.
How do I include those assets?
You'll be best using #import like this:
#gem/app/assets/gem/widget.css.scss
#import "bootstrap.css" /* will probably need a relative path for this */
#import "other.css" /* again, will need a relative path */

Rails asset pipleline: compile to multiple stylesheets

Due to a specific setup, I would like to split the compiled stylesheets in two files. This is because (a part of) the CSS is needed for a Java application which can parse the CSS, but it is a bit buggy and can't handle some css-(hack)-syntax. Because I am unable to modify this Java application, I want to feed it only the CSS which it needs and of which I can make sure it is correct.
So, normally the assets pipeline would produce just one '/assets/application-[..].css' file. It would to let it also generate '/assets/custom-[..].css', based on a file selection I make. This still can be pre-compiled.
Is there a way to do this? Although I understand this is not the ideal setup..
To tell rails about additional files you wish to have precompiled, you can add them to the config.assets.precompile setting.
config.assets.precompile += ["other_application.css"]
You only see application.css in your HTML because that's the only file you're including
<%= stylesheet_link_tag "application" %>
If you have some custom.css.scss in your apps/assets/stylesheets directory, it will be compiled just like application.css.
For example, I might have
- _common.css.scss
- application.css.erb.scss
- other_application.css.erb.scss
in app/assets/stylesheets. In the top of the non-partial files I will put
#import "common";
to include _common.css.scss. I can now reference either stylesheet independent of one another in a layout.
<%= stylesheet_link_tag "application" %>
# or
<%= stylesheet_link_tag "other_application" %>

Resources