Started a new Rails 6.0.3 project and added jQuery and Bootstrap. jQuery works fine and the Bootstrap CSS seems to render properly but whenever I try running $.fn.tooltip.Constructor.VERSION in the browser javascript console I get an error Uncaught TypeError: Cannot read property 'tooltip' of undefined. Also trying to add plugins like bootstrap-select, Bootstrap Tags Input, etc. all fail. I'm not sure if it's related but I've also had the same trouble adding awesome-fonts, the CSS works but Javascript does not. Below are configs that might be relevant:
// package.json
{
"name": "myproject",
"private": true,
"dependencies": {
"#rails/actioncable": "^6.0.0",
"#rails/activestorage": "^6.0.0",
"#rails/ujs": "^6.0.0",
"#rails/webpacker": "5.2.1",
"bootstrap": "^4.5.2",
"channels": "^0.0.4",
"jquery": "^3.5.1",
"popper.js": "^1.16.1",
"turbolinks": "^5.2.0"
},
"version": "0.1.0",
"devDependencies": {
"axios": "^0.20.0",
"cypress": "^5.1.0",
"webpack-dev-server": "^3.11.0"
}
}
// config/webpack/environment.js
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery/src/jquery',
jQuery: 'jquery/src/jquery',
Popper: ['popper.js', 'default']
})
)
module.exports = environment
// config/webpack/development.js
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
const environment = require('./environment')
module.exports = environment.toWebpackConfig()
// app/javascript/packs/application.js
require("jquery")
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
require("bootstrap")
// app/views/layouts/application.html.erb
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<title>My Project</title>
</head>
<body>
<%= yield %>
</body>
</html>
Got it working through the following addition...
// config/webpack/custom.js
module.exports = {
resolve: {
alias: {
jquery: 'jquery/src/jquery'
}
}
};
... and changes:
// config/webpack/environment.js
const { environment } = require('#rails/webpacker')
const customConfig = require('./custom')
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery/src/jquery',
jQuery: 'jquery/src/jquery',
Popper: ['popper.js', 'default']
})
)
environment.config.merge(customConfig)
module.exports = environment
// config/webpack/environment.js
require("jquery")
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
require("bootstrap")
import $ from "jquery"
document.addEventListener("turbolinks:load", () => {
$('[data-toggle="tooltip"]').tooltip()
$('[data-toggle="popover"]').popover()
})
Related question that was helpful in getting to a solution: $(...).tooltip is not a function rails 6 webpack
Related
I'm having some problems with JavaScript and actually the way it works on my setup.
When the .js file is inserted directly at the bottom of HTML page then all scripts works as they should. when the file is imported like this:
import "./modules/toDoList"
then I'm getting errors in the console, screenshot of this error is attached at the bottom.
My webpack setup looks like this:
const currentTask = process.env.npm_lifecycle_event
const path = require('path')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const HtmlWebpackPlugin = require("html-webpack-plugin")
const fse = require("fs-extra")
const postCSSPlugins = [
require("postcss-import"),
require("postcss-mixins"),
require("postcss-simple-vars"),
require("postcss-nested"),
require("autoprefixer")
]
class RunAfterCompile {
apply(compiler) {
compiler.hooks.done.tap("Copy images", function() {
fse.copySync("./app/assets/images", "./docs/assets/images") //changed dist to docs for git
})
}
}
let cssConfig = {
test: /\.css$/i,
use: ["css-loader?url=false", { loader: "postcss-loader", options: { postcssOptions: { plugins: postCSSPlugins } } }]
}
let pages = fse.readdirSync("./app").filter(function(file) {
return file.endsWith(".html")
}).map(function(page) {
return new HtmlWebpackPlugin({
filename: page,
template: `./app/${page}`
})
})
let config = {
entry: './app/assets/scripts/App.js',
plugins: pages,
module: {
rules: [
cssConfig
]
}
}
if (currentTask == "dev") {
cssConfig.use.unshift('style-loader')
config.output = {
filename: "bundled.js",
path: path.resolve(__dirname, "app") //generating absolute path
}
config.devServer = {
onAfterSetupMiddleware: function(app, server) {
watchFiles: ("./app/**/*.html") // /**/ any folder if nessesery, /* any html file
},
static: {
directory: path.join(__dirname, "app"),
},
hot: true, //Injecting JSS and CSS to the browser memory on the fly / Hot Module Replacement
port: 3005,
host: '192.168.1.11'
}
config.mode = "development"
}
if (currentTask == 'build') {
config.module.rules.push({
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"]
}
}
})
cssConfig.use.unshift(MiniCssExtractPlugin.loader)
config.output = {
filename: '[name].[chunkhash].js',
chunkFilename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'docs') //changed dist to docs for git
}
config.mode = 'production'
config.optimization = {
splitChunks: {chunks: 'all'},
minimize: true,
minimizer: [`...`, new CssMinimizerPlugin()]
}
config.plugins.push(
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({filename: 'styles.[chunkhash].css'}),
new RunAfterCompile()
)
}
module.exports = config
My package.json file looks like this:
{
"name": "to-do-list",
"version": "1.0.0",
"scripts": {
"dev": "webpack serve",
"build": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"axios": "^0.21.1",
"html-webpack-plugin": "^5.5.0",
"lazysizes": "^5.3.0",
"lodash": "^4.17.20",
"normalize.css": "^8.0.1"
},
"devDependencies": {
"#babel/core": "^7.20.12",
"#babel/preset-env": "^7.20.2",
"autoprefixer": "^10.2.4",
"babel-loader": "^9.1.2",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^5.0.1",
"css-minimizer-webpack-plugin": "^4.2.2",
"fs-extra": "^11.1.0",
"mini-css-extract-plugin": "^1.3.5",
"postcss-import": "^14.0.0",
"postcss-loader": "^5.0.0",
"postcss-mixins": "^7.0.2",
"postcss-nested": "^5.0.3",
"postcss-simple-vars": "^6.0.2",
"style-loader": "^2.0.0",
"webpack": "^5.20.0",
"webpack-cli": "^4.5.0",
"webpack-dev-server": "^4.11.1"
}
}
This is my App.js file:
import "../styles/style.css"
import 'lazysizes'
import "./modules/toDoList"
if (module.hot) {
module.hot.accept()
}
And my folder structure is like per image below:
This is the simple .js to do list app code:
let ourForm = document.getElementById("ourForm")
let ourField = document.getElementById("ourField")
let ourList = document.getElementById("ourList")
ourForm.addEventListener("submit", (e) => {
e.preventDefault()
createItem(ourField.value)
})
function createItem(x) {
let ourHTML = `<li>${x} <button onclick="deleteItems(this)">Delete</button> </li>`
ourList.insertAdjacentHTML("beforeend", ourHTML)
ourField.value = ""
ourField.focus()
}
function deleteItems(elementToDelete) {
elementToDelete.parentElement.remove()
}
This is html code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MariusĀ® Web Developer</title>
<link href='https://fonts.googleapis.com/css2?family=Montserrat:wght#100;200;300;400;500;600;700;800;900' rel='stylesheet' type='text/css'>
</head>
<body>
<div class="content-wrapper">
<div class="todo">
<h1>To-Do App</h1>
<form id="ourForm">
<input type="text" autocomplete="off" name="" id="ourField">
<button>Create Item</button>
</form>
<h3>Need To Do</h3>
<ul id="ourList">
</ul>
</div>
</div>
<!-- <script src="assets/scripts/modules/toDoList.js"></script> -->
</body>
</html>
And this is the error in console when I try to delete the item from the list:
With this setup I'm also having problem with mixins, basically they works fine but VS code is marking them as an error as below:
Can anyone help me with this please? The most important thing is why JS is not working as it should. Like I've said it works when .js file is imported directly at the bottom of html page.
So I'm a big fan of webpack but I have to use only gulp in this work I'm doing and I'm living a nightmare configuring this to actually start working.
On my index.js there is just import Swiper from 'swiper' And this error returns:
Uncaught TypeError: Failed to resolve module specifier "swiper".
Relative references must start with either "/", "./", or "../".
I'm using this configuration in my gulpfile :
const gulp = require('gulp');
const { src, dest, watch, series, parallel } = require('gulp');
const imagemin = require('gulp-imagemin');
const sourcemaps = require('gulp-sourcemaps');
const concat = require('gulp-concat');
const rename = require('gulp-rename');
const replace = require('gulp-replace');
const terser = require('gulp-terser');
const sass = require('gulp-sass')(require('sass'));
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const browsersync = require('browser-sync').create()
const paths = {
html: {
src: ['./src/**/*.html'],
dest: './dist/',
},
images: {
src: ['./src/images/**/*'],
dest: './dist/content/images/',
},
styles: {
src: ['./src/scss/**/*.scss'],
dest: './dist/css/',
},
scripts: {
src: ['./src/scripts/**/*.js'],
dest: './dist/js/',
},
cachebust: {
src: ['./dist/**/*.html'],
dest: './dist/',
},
};
function copyHtml() {
return src(paths.html.src).pipe(dest(paths.html.dest)).pipe(browsersync.stream())
}
function optimizeImages() {
return src(paths.images.src)
.pipe(imagemin().on('error', (error) => console.log(error)))
.pipe(dest(paths.images.dest))
.pipe(browsersync.stream())
}
function compileStyles() {
return src(paths.styles.src)
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(dest('./src/css/'))
.pipe(postcss([autoprefixer(), cssnano()]))
.pipe(rename({ suffix: '.min' }))
.pipe(sourcemaps.write('.'))
.pipe(dest(paths.styles.dest))
.pipe(browsersync.stream())
}
function minifyScripts() {
return src(paths.scripts.src)
.pipe(sourcemaps.init())
.pipe(terser().on('error', (error) => console.log(error)))
.pipe(rename({ suffix: '.min' }))
.pipe(sourcemaps.write('.'))
.pipe(concat('bundle.js'))
.pipe(dest(paths.scripts.dest))
.pipe(browsersync.stream())
}
function cacheBust() {
return src(paths.cachebust.src)
.pipe(replace(/cache_bust=\d+/g, 'cache_bust=' + new Date().getTime()))
.pipe(dest(paths.cachebust.dest));
}
function browserSync() {
browsersync.init({
server: {
baseDir: './src'
}
})
}
function watcher() {
watch(paths.html.src, series(copyHtml, cacheBust));
watch(paths.images.src, optimizeImages);
watch(paths.styles.src, parallel(compileStyles, cacheBust));
watch(paths.scripts.src, parallel(minifyScripts, cacheBust));
}
exports.copyHtml = copyHtml;
exports.optimizeImages = optimizeImages;
exports.compileStyles = compileStyles;
exports.minifyScripts = minifyScripts;
exports.cacheBust = cacheBust;
exports.watcher = watcher;
exports.default = series(
parallel(copyHtml, optimizeImages, compileStyles, minifyScripts),
cacheBust,
parallel(watcher, browserSync)
);
My index.html :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<script type="module" src="./scripts/index.js"></script>
</body>
</html>
package.json
{
"name": "frontend-challenge-heflerdev",
"version": "1.0.0",
"description": "Pontos importantes antes de iniciar",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "gulp"
},
"repository": {
"type": "git",
"url": "git+https://github.com/HeflerDev/frontend-challenge-junior.git"
},
"author": "HeflerDev",
"license": "MIT",
"bugs": {
"url": "https://github.com/HeflerDev/frontend-challenge-junior/issues"
},
"homepage": "https://github.com/HeflerDev/frontend-challenge-junior#readme",
"devDependencies": {
"autoprefixer": "^10.4.2",
"browser-sync": "^2.27.7",
"cssnano": "^5.1.0",
"eslint": "^8.10.0",
"gulp": "^4.0.2",
"gulp-concat": "^2.6.1",
"gulp-imagemin": "^7.0.0",
"gulp-postcss": "^9.0.1",
"gulp-rename": "^2.0.0",
"gulp-replace": "^1.1.3",
"gulp-sass": "^5.1.0",
"gulp-sourcemaps": "^3.0.0",
"gulp-terser": "^2.1.0",
"node": "^17.5.0",
"node-sass": "^7.0.1",
"postcss": "^8.4.8",
"sass": "^1.49.9"
},
"dependencies": {
"babel-core": "^6.26.3",
"babel-preset-es2015": "^6.24.1",
"require": "^2.4.20",
"swiper": "^8.0.7"
}
}
EDIT
Some of the folder structure
|dist/
|src/
|scripts
|index.js
|css
|scss
|images
|index.html
I tried a lot of things and I'm just banging my head on the wall, could someone help me?
Since you don't show the actual source file where you're importing swiper, I will assume that it's inside of this file on the client:
<script type="module" src="./scripts/index.js"></script>
From client-side JS files, you can't just do:
import Swiper from 'swiper'
As the error message from the browser says, you have to specify some type of path and you have to make sure your server is then serving the swiper module from that path.
Probably you want to do something like:
import Swiper from '/scripts/swiper'
since you rarely want to use relative paths for serving files on the client. And, then you also have to configure your server to respond to the request for /scripts/swiper to serve the swiper module.
Remember that a nodejs web server does not serve ANY files by default. You have to configure it to serve any file you want to server back to the client. You can often use something like express.static() to serve a whole directory or directory hierarchy of files with one statement, but you have to configure that properly.
I'm using Snowpack + Svelte. After upgrading to Snowpack 3 is not working anymore and I can't configure mount in snowpack.config.js properly.
Don't understand exactly why it cannot find App.js also it's trying to find .jsx, .ts...
[404] /_dist_/screens/App.js
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="global.css">
<script type="module" defer src="_dist_/main.js"></script>
</head>
<body>
</body>
</html>
And then in my snowpack.config.js
/** #type {import("snowpack").SnowpackUserConfig } */
module.exports = {
mount: {
public: '/',
src: '/_dist_',
},
plugins: [
'#snowpack/plugin-svelte',
'#snowpack/plugin-dotenv',
],
routes: [
/* Enable an SPA Fallback in development: */
// {"match": "routes", "src": ".*", "dest": "/index.html"},
],
optimize: {
/* Example: Bundle your final build: */
// "bundle": true,
},
packageOptions: {
/* ... */
},
devOptions: {
/* ... */
},
buildOptions: {
/* ... */
},
alias: {
components: './src/components',
screens: './src/screens',
lib: './src/lib'
},
};
I also tried:
mount: {
// Same behavior as the "src" example above:
"src": {url: "/dist"},
// Mount "public" to the root URL path ("/*") and serve files with zero transformations:
"public": {url: "/", static: true, resolve: false}
},
So now it's complaining less but still not working
[snowpack] [404] /_dist_/main.js
Files structure
root
--node-modules
--public
----global.css
----index.html
--src
----components
----lib
----screens
----main.js
--snowpack.config.js
...
package.json
"devDependencies": {
"#snowpack/plugin-dotenv": "^2.0.5",
"#snowpack/plugin-svelte": "^3.5.0",
"#snowpack/web-test-runner-plugin": "^0.2.1",
"#testing-library/svelte": "^3.0.3",
"#web/test-runner": "^0.12.2",
"chai": "^4.2.0",
"smart-webcomponents": "^9.0.0",
"snowpack": "^3.0.10",
"svelte": "^3.31.2",
"svelte-i18n": "^3.3.0"
}
Any help or idea will be really appreciated.
Just add .svelte when you import your components.
import App from 'screens/App.svelte'
Please, check this https://github.com/snowpackjs/snowpack/pull/2014#issuecomment-756624333 if you wanna dive deeper.
This a continuation of a related problem that #rossta fixed part of, but now the problem is moved so thought I'd start over.
No errors and the script is completing (verified by console.log outputs in the script). The body element shows up. This worked with gem leaflet and now webpack in Rails 5.2, but not now in Rails 6 with webpack
I moved the script into the page to isolate the problem map/index.html.erb
<p id="notice"><%= notice %></p>
<% provide(:title, 'Map') %>
<h4>This is map/index.html.erb and is put in layouts/map.html.erb.</h4>
<div id="map_two" class="map clearfix"></div> -->
<script>
function makeMapTwo() {
console.log('Hello from makeMapTwo in map/index.html.erb')
var mapVar = L.map("map_two", { center: [34.040951, -118.258579], zoom: 13 });
L.tileLayer('https://crores.s3.amazonaws.com/tiles/bkm/{z}/{x}/{y}.png').addTo(mapVar);
$.getJSON("line_data.geojson", function (data_data) {
var timelineData = L.timeline(data_data, {
style: function(data_data){
return {
stroke: true,
fillOpacity: 0.5
}
}, // end style: function(data_data)
waitToUpdateMap: true,
onEachFeature: function(data_data, layer) {
layer.bindTooltip(data_data.properties.popup, { direction: 'top' } );
} // onEachFeature:
}); // end let timelineData = L.timeline
var timelineControl = L.timelineSliderControl({
enableKeyboardControls: true,
steps: 100,
start: 1885,
end: 1928,
});
timelineData.addTo(mapVar);
timelineControl.addTo(mapVar);
timelineControl.addTimelines(timelineData);
}); // end $.getJSON
}; // end function makeMapTwo()
$(document).ready(function() {
makeMapTwo();
});
</script>
views/layouts/map.html.erb
<!DOCTYPE html>
<html>
<head>
<title><%= full_title(yield(:title)) %></title>
<h6>This is layouts/map.html.erb. A note to remind me that header is happening twice in maps TODO</h6>
<%= favicon_link_tag 'favicon.ico' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload', 'data-turbolinks-suppress-warning': true %>
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<style>
.map {
height: 400px;
width: 100%
}
</style>
<%= csrf_meta_tags %>
</head>
<%= render 'layouts/header' %> <!-- the navbar -->
<body class="container" data-mapbox-token="<%= ENV['MAPBOX_TOKEN'] %>">
<%= yield %>
<%= render 'layouts/footer' %>
</body>
</html>
and app/javascript/packs/application.js:
import "core-js/stable"
import "regenerator-runtime/runtime"
import '../stylesheets/application'
window.jQuery = $
window.$ = $
import 'leaflet'
import "leaflet.timeline"
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("trix")
require("#rails/actiontext")
require("jquery")
import "bootstrap"
import 'bootstrap/dist/js/bootstrap'
document.addEventListener("turbolinks:load", () => {
$('[data-toggle="tooltip"]').tooltip()
$('[data-toggle="popover"]').popover()
})
config/webpack/environment.js:
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
environment.plugins.append('Provide',
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
jquery: 'jquery',
Popper: ['popper.js' ,'default'],
}))
module.exports = environment
package.json
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
environment.plugins.append('Provide',
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
jquery: 'jquery',
Popper: ['popper.js' ,'default'],
}))
module.exports = environment
Debugging needed, but I'm not sure where to start.
I can't believe that this change had any effect but it did.
Changed the map_two in <div id="map_two"... var mapVar = L.map("map_two"... to just map and it loads. I was using map_two because when I was experimenting earlier I thought the two pages I was using with just map were getting confused. I also tried map-two and it didn't work either (not that I would have expected it, but I still do't understand what is happening. I restarted the server between the changes to be more sure of what is going on.
I follow this example(http://courses.reactjsprogram.com/courses/reactjsfundamentals/lectures/760301) to start one reactj app, so this is my
webpack.config.js
var HtmlWebpackPlugin = require('html-webpack-plugin');
var HtmlWebpackPluginConfig = new HtmlWebpackPlugin({
tempalte : __dirname + '/app/index.html',
filename : 'index.html',
inject : 'body'
})
module.exports = {
entry: [
'./app/index.js'
],
output: {
path : __dirname + '/dist',
filename : "index_bundle.js"
},
module : {
loaders :[
{test: /\.js$/, include: __dirname + '/app', loader: "babel-loader"}
]
},
plugins : [HtmlWebpackPluginConfig]
}
And this is my index.html template:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> teste</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
and this is my index.html generated by webpack
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<script src="index_bundle.js"></script></body>
</html>
Note: my are removed, so when I try to run my app I got the error:
Uncaught Invariant Violation: _registerComponent(...): Target container is not a DOM element.
How can I fix to don`t remove my div?
I using
"babel-core": "^6.7.6",
"babel-loader": "^6.2.4",
"babel-preset-react": "^6.5.0",
"html-webpack-plugin": "^2.15.0",
"webpack": "^1.13.0",
"webpack-dev-server": "^1.14.1"
tks
A typo in the template key in your configuration means that it is using a default template and not the one you were trying to include. The default behavior makes this error difficult to spot.
tempalte : __dirname + '/app/index.html'
should be
template : __dirname + '/app/index.html'