SassError: Invalid CSS after "" with Rails 6, Webpacker, and Vue - ruby-on-rails

I've just started a fresh rails-6 project with webpacker and vue. I wanted to have the vue-component styles in sass, but the sass-loader throughs:
Error: Module build failed (from ./node_modules/sass-loader/dist/cjs.js)
SassError: Invalid CSS after "": expected 1 selector or at-rule, was ".foo"
Has anyone seen this before? Or maybe, how can I debug what goes wrong here?
Component foo.vue
<template>...</template>
<script>...</script>
<style lang="sass">
.foo
margin: 0
padding: 0
</style>
Removing the initial indention of the sass code does not help.
Versions
rails 6.1.3.1
webpacker 5.2.1
node 14.16.1
Project setup
rails new --database=postgresql --skip-test foo
rails webpacker:install:vue
yarn add vue-turbolinks
rails webpacker:install:coffee
yarn add coffee-loader#1
yarn add sass-loader#10
package.json
{
"name": "foo",
"private": true,
"dependencies": {
"#popperjs/core": "^2.9.2",
"#rails/actioncable": "^6.0.0",
"#rails/activestorage": "^6.0.0",
"#rails/ujs": "^6.0.0",
"#rails/webpacker": "5.2.1",
"#tabler/core": "tabler/tabler",
"bootstrap": "^5.0.0-beta3",
"coffee-loader": "1",
"coffeescript": "1.12.7",
"sass-loader": "10",
"tabler": "^1.0.0-alpha.8",
"turbolinks": "^5.2.0",
"vue": "^2.6.12",
"vue-loader": "^15.9.6",
"vue-select": "^3.11.2",
"vue-template-compiler": "^2.6.12",
"vue-turbolinks": "^2.2.2"
},
"version": "0.1.0",
"devDependencies": {
"webpack-dev-server": "^3.11.2"
}
}
I've also added vue-select, bootstrap, and tabler. But I don't think they will interfere.
Webpack configuration config/webpack/environment.js
const { environment } = require('#rails/webpacker')
const coffee = require('./loaders/coffee')
const { VueLoaderPlugin } = require('vue-loader')
const vue = require('./loaders/vue')
environment.plugins.prepend('VueLoaderPlugin', new VueLoaderPlugin())
environment.loaders.prepend('vue', vue)
environment.loaders.prepend('coffee', coffee)
module.exports = environment
Layout application.html.haml
!!!
%html
%head
%title Foo
= csrf_meta_tags
= csp_meta_tag
= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload'
%body
#vue_app
= yield
Pack packs/application.js
import Rails from "#rails/ujs"
import Turbolinks from "turbolinks"
import * as ActiveStorage from "#rails/activestorage"
import "channels"
Rails.start()
Turbolinks.start()
ActiveStorage.start()
import 'bootstrap'
import '#tabler/core'
import TurbolinksAdapter from 'vue-turbolinks'
import Vue from 'vue/dist/vue.esm'
Vue.use(TurbolinksAdapter)
document.addEventListener('turbolinks:load', () => {
const app = new Vue({
el: '#vue_app',
data: () => {
return {}
},
components: {}
})
})

Sass vs. SCSS
There are two syntax variants:
There are two syntaxes available for Sass. The first, known as SCSS (Sassy CSS) and used throughout this reference, is an extension of the syntax of CSS. This means that every valid CSS stylesheet is a valid SCSS file with the same meaning. This syntax is enhanced with the Sass features described below. Files using this syntax have the .scss extension.
The second and older syntax, known as the indented syntax (or sometimes just “Sass”), provides a more concise way of writing CSS. It uses indentation rather than brackets to indicate nesting of selectors, and newlines rather than semicolons to separate properties. Files using this syntax have the .sass extension.
See also: https://stackoverflow.com/a/5654471/2066546, https://sass-lang.com/documentation/syntax
Which syntax does the sass-loader use?
The sass-loader chooses the syntax according to the file extension by default:
The indentedSyntax option has true value for the sass extension.
This means that the original sass syntax (indented syntax) is only used if the file extension is .sass. Within the vue component, which has the extension .vue, the sass-loader uses the newer "scss" syntax by default.
How to configure sass-loader to use the sass syntax (indented syntax)?
The sass-loader documentation has instructions on how to specify indentedSyntax: true in the webpack configuration.
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: [
"style-loader",
"css-loader",
{
loader: "sass-loader",
options: {
sassOptions: {
indentedSyntax: true
},
},
},
],
},
],
},
};
However, with webpacker, the webpack configuration is composed automatically. So, one needs to modify the existing configuration object:
// config/webpack/environment.js
const { environment } = require('#rails/webpacker')
// To get an overview, have a look at `environment.loaders`.
console.log(environment.loaders)
const { merge } = require('webpack-merge')
const sassConfig = environment.loaders.find(el => el.key == 'sass')
const sassLoader = sassConfig.value.use.find(el => el.loader == 'sass-loader')
sassLoader.options = merge(sassLoader.options, {
sassOptions: {
indentedSyntax: true
}
})
module.exports = environment
This requires webpack-merge: yarn add webpack-merge.
I'm sure there are better ways to do it. Please feel free to add answers or comments!

Sass works with curly braces:
.foo {
margin: 0
}

You can try to include <%= stylesheet_pack_tag 'application', media: 'all' %> on your root HTML file.
I think it will work.

Related

Rails Tailwind Setup: Module build failed (from ./node_modules/postcss-loader/src/index.js):

I was following this tutorial (https://web-crunch.com/posts/how-to-install-tailwind-css-using-ruby-on-rails) for setting up tailwind in a rails app.
I am using rails 6.1.3.1 and ruby 3.0.1.
My CSS seems not to load despite I followed all steps. When I inspect (chrome dev tools) my console, the following error is displayed:
Uncaught Error: Module build failed (from ./node_modules/postcss-loader/src/index.js):
Error: ENOENT: no such file or directory, open '/app/javascript/stylesheet/tailwind.config.js'
Any idea what could be the reason?
Here is my postscss.config.js file:
let environment = {
plugins: [
require('tailwindcss')('./app/javascript/stylesheets/tailwind.config.js'),
require('postcss-import'),
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009'
},
stage: 3
})
]
};
// Add everything below!
if (process.env.RAILS_ENV === 'production') {
environment.plugins.push(
require('#fullhuman/postcss-purgecss')({
content: [
'./app/**/.html.erb',
'./app/helpers/**/*.rb',
'./app/javascript/**/*.js',
'./app/javascript/**/*.jsx',
],
defaultExtractor: (content) => content.match(/[A-Za-z0-9-_:/]+/g) || []
})
)
}
module.exports = environment;
And here is my tailwind.config.js file:
module.exports = {
purge: [
'./app/**/*.html.erb',
'./app/helpers/**/*.rb',
'./app/javascript/**/*.js'
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
I just solved it myself and am happy to share:
you have to change stylesheets into stylesheet here:
in application.js: import "stylesheet/application"
in postcss.config.js: require('tailwindcss')('./app/javascript/stylesheet/tailwind.config.js'),
change folder name stylesheets into stylesheet

Not able to access right node_modules path on webpack-rails

I am trying for the first time to work with angularjs with rails as the back end. I am using webpack-rails gem for the webpack configuration for managing css and other style sheets.
First I made a sample angular + rails app to display a simple text "Hello world". By default the font was in times new roman. Then I added bootstrap to the package.json file as follows.
package.json
{
"name": "shine",
"version": "0.0.1",
"license": "MIT",
"dependencies": {
"stats-webpack-plugin": "^0.4.3",
"webpack": "^1.14.0",
"webpack-dev-server": "^1.16.2",
"css-loader": "^0.23.1",
"file-loader": "^0.9.0",
"style-loader": "^0.13.1",
"url-loader": "^0.5.7",
"bootstrap": "3.3.7"
}
}
Then I ran npm install which generated a folder named node_modules
with all the packages mentioned in the package.json in Project/node_modules path.
And webpack configuration to access access these package files is given in the
webpack.config.js
'use strict';
var path = require('path');
var webpack = require('webpack');
var StatsPlugin = require('stats-webpack-plugin');
// must match config.webpack.dev_server.port
var devServerPort = 3808;
// set NODE_ENV=production on the environment to add asset fingerprints
var production = process.env.NODE_ENV === 'production';
var config = {
entry: {
// Sources are expected to live in $app_root/webpack
'application': './webpack/application.js'
},
output: {
// Build assets directly in to public/webpack/, let webpack know
// that all webpacked assets start with webpack/
// must match config.webpack.output_dir
path: path.join(__dirname, '..', 'public', 'webpack'),
publicPath: '/webpack/',
filename: production ? '[name]-[chunkhash].js' : '[name].js'
},
resolve: {
root: path.join(__dirname, '..', 'webpack')
},
plugins: [
// must match config.webpack.manifest_filename
new StatsPlugin('manifest.json', {
// We only need assetsByChunkName
chunkModules: false,
source: false,
chunks: false,
modules: false,
assets: true
})]
};
if (production) {
config.plugins.push(
new webpack.NoErrorsPlugin(),
new webpack.optimize.UglifyJsPlugin({
compressor: { warnings: false },
sourceMap: false
}),
new webpack.DefinePlugin({
'process.env': { NODE_ENV: JSON.stringify('production') }
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin()
);
} else {
config.devServer = {
port: devServerPort,
headers: { 'Access-Control-Allow-Origin': '*' }
};
config.output.publicPath = '//localhost:' + devServerPort + '/webpack/';
// Source maps
config.devtool = 'cheap-module-eval-source-map';
}
module.exports = config;
And in my webpack/application.js I am calling that bootstrap package as follows.
require("bootstrap/dist/css/bootstrap.css");
console.log("Hello world!");
Now when I restart the server and reload the page I don't see any change in the font instead in the console I am getting.
Uncaught Error: Cannot find module "bootstrap/dist/css/bootstrap.css"
at webpackMissingModule
What am I doing wrong here ?
perhaps...
require('boostrap');
if that doesn't work, check to see if the file bootstrap/dist/css/bootstrap.css exists

webpackMissingModule application.js on rails with angular app

I am following the steps like given by the author.
Following is the
package.json
{
"name": "shine",
"version": "0.0.1",
"license": "MIT",
"dependencies": {
"stats-webpack-plugin": "^0.4.3",
"webpack": "^1.14.0",
"webpack-dev-server": "^1.16.2",
"css-loader": "^0.23.1",
"file-loader": "^0.9.0",
"style-loader": "^0.13.1",
"url-loader": "^0.5.7",
"bootstrap": "3.3.7"
}
}
I am trying to change the default font to a different font by requiring the bootstrap.
My application.js looks like this
require("bootstrap/dist/css/bootstrap.css");
console.log("Hello world!");
And then I restarted the server. I see the text content are in same font-family as before nothing has changed when it should be actually a different font. And the console is giving the error as follows.
console
Uncaught Error: Cannot find module "bootstrap/dist/css/bootstrap.css"
at webpackMissingModule
webpack.config.js
// Example webpack configuration with asset fingerprinting in production.
'use strict';
var path = require('path');
var webpack = require('webpack');
var StatsPlugin = require('stats-webpack-plugin');
// must match config.webpack.dev_server.port
var devServerPort = 3808;
// set NODE_ENV=production on the environment to add asset fingerprints
var production = process.env.NODE_ENV === 'production';
var config = {
entry: {
// Sources are expected to live in $app_root/webpack
'application': './webpack/application.js'
},
output: {
// Build assets directly in to public/webpack/, let webpack know
// that all webpacked assets start with webpack/
// must match config.webpack.output_dir
path: path.join(__dirname, '..', 'public', 'webpack'),
publicPath: '/webpack/',
filename: production ? '[name]-[chunkhash].js' : '[name].js'
},
resolve: {
root: path.join(__dirname, '..', 'webpack')
},
plugins: [
// must match config.webpack.manifest_filename
new StatsPlugin('manifest.json', {
// We only need assetsByChunkName
chunkModules: false,
source: false,
chunks: false,
modules: false,
assets: true
})]
};
if (production) {
config.plugins.push(
new webpack.NoErrorsPlugin(),
new webpack.optimize.UglifyJsPlugin({
compressor: { warnings: false },
sourceMap: false
}),
new webpack.DefinePlugin({
'process.env': { NODE_ENV: JSON.stringify('production') }
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin()
);
} else {
config.devServer = {
port: devServerPort,
headers: { 'Access-Control-Allow-Origin': '*' }
};
config.output.publicPath = '//localhost:' + devServerPort + '/webpack/';
// Source maps
config.devtool = 'cheap-module-eval-source-map';
}
module.exports = config;
I am not able to figure out if this is npm installing issue or something else. How can I fix this.
It seems your using older version of webpack, which I infer after seeing the resolve.root property. From webpack 1 docs
The directory (absolute path) that contains your modules. May also be an array of directories. This setting should be used to add individual directories to the search path.
So when your bootstrap.css files is being resolved instead of searching node_modules, it searches in your
resolve: { root: path.join(__dirname, '..', 'webpack')}

How to use css with webpack in a rails app?

Following the github docs, I tried the following to include scss assets in my app:
app/javascript/styles/app.scss:
#import 'https://fonts.googleapis.com/css?family=Roboto:300,400,500';
body {
margin: 0;
}
app/javascript/packs/application.js:
/* eslint no-console:0 */
// 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.
//
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
// layout file, like app/views/layouts/application.html.erb
// Support component names relative to this directory:
var componentRequireContext = require.context("components", true)
var ReactRailsUJS = require("react_ujs")
ReactRailsUJS.useContext(componentRequireContext)
import React from 'react';
import '../styles/app.scss'
My webpack.config.js is the default webpack installs on rails. It is a combination of these 2 directories:
https://github.com/rails/webpacker/tree/master/lib/install/config/loaders/core
https://github.com/rails/webpacker/tree/master/lib/install/config/webpack
I can find the CSS config in config/loaders/sass.js:
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const { env } = require('../configuration.js')
module.exports = {
test: /\.(scss|sass|css)$/i,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{ loader: 'css-loader', options: { minimize: env.NODE_ENV === 'production' } },
{ loader: 'postcss-loader', options: { sourceMap: true } },
'resolve-url-loader',
{ loader: 'sass-loader', options: { sourceMap: true } }
]
})
}
On doing this, I do not get any error but the stylesheet isn't imported. Serving the same file from the asset pipeline works fine.

how to import jquery ui theme with sass

I am trying to import jquery UI theme into my main app.css . This is what has been done so far:
bower.json
{
"name": "Softverk Webportal",
"dependencies": {
"jquery": "~2.1.1",
"jquery-ui":"~1.11.4",
},
"resolutions": {
"jquery": "~2.1.1",
}
}
_jquery_ui.scss
#import url("../../../assets/bower_components/jquery-ui/themes/base/all.css");
app.scss
#import "jquery_ui";
And my sass task in grunt looks like this:
sass: {
options: {
includePaths: [
'assets/bower_components/jquery-ui/themes/base',
]
},
dist: {
options: {
outputStyle: 'compressed',
sourceComments: 'map',
sourcemap: 'file'
},
files: {
'assets/css/app.min.css': [
settings.template.path + 'scss/app.scss',
]
}
}
},
However, when this compiles, none of the jquery ui css classes picked up . my sass version is:
Sass 3.4.13 (Selective Steve)
Can anyone suggest how to use jquery UI with grunt / sass ?
Since there wasn't any response and google search was not very helpful, i ended up doing this:
'sass-convert': {
/**
* NOTE: make sure to change the images to something like this:
* background-image: url("images/ui-icons_444444_256x240.png") --> background-image: url($template-path +"images/ui-icons_444444_256x240.png");
* */
options: {
from: 'css',
to: 'scss'
},
files: { ///home/khan/www/softverk-webportal-remaxth/assets/bower_components/jquery-ui/themes/base
cwd: 'assets/bower_components/jquery-ui/themes/base/',
src: 'jquery-ui.css',
filePrefix: '_',
dest: settings.template.path + 'scss/'
}
},
grunt.registerTask('default', [ 'sass-convert');
Which generates _jquery-ui.scss , which was then added to app.scss like this:
#import "jquery-ui";
Also in package.json , make sure to add:
"grunt-sass": "~0.12.1",
"grunt-sass-convert":"~0.2.0",
#npm install
What i did is simply import css file in my screen.scss file in a symfony project like this:
#import "../../../../../bower_components/jquery-ui/themes/vader/jquery-ui.css";
and it worked.

Resources