0

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: {}
  })
})
fiedl
  • 5,667
  • 4
  • 44
  • 57

3 Answers3

1

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!

fiedl
  • 5,667
  • 4
  • 44
  • 57
0

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

Mikel Wohlschlegel
  • 1,364
  • 1
  • 11
  • 23
  • Thanks! To be clear: I do want to use the `sass` syntax, not the `scss` syntax. See: https://sass-lang.com/guide. Do I somehow need to specify this, now? – fiedl Apr 17 '21 at 17:56
  • 1
    Ah, I see. I do not know webpacker, but can you overwrite or extend the webpack configuration somehow? Maybe you have to set the indentWidth and / or indentedSyntax to true: https://webpack.js.org/loaders/sass-loader/#sassoptions – Mikel Wohlschlegel Apr 17 '21 at 18:22
0

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

I think it will work.

Nico Peck
  • 538
  • 3
  • 16
  • Thanks! I had this in my layout, already. I'll update my question to include the `application.html.haml` layout file and the `application.js` pack. – fiedl Apr 17 '21 at 17:58
  • Can you add the webpack config too on your question? – Nico Peck Apr 17 '21 at 18:03
  • Thanks, I'll add the `config/webpack/environment.js`, but I didn't change it manually with respect to the files generated by webpacker. – fiedl Apr 17 '21 at 18:06
  • I can see you are not loading sass-loader. you can try to load the sass-loader. but I am not sure how to do this like a option of vue-loader. `module.exports = { test: /\.vue(\.erb)?$/, use: [ { loader: "vue-loader", options: { extract_css: true }, }, ], };` – Nico Peck Apr 17 '21 at 18:20
  • I think this is done by webpacker already: https://github.com/rails/webpacker/blob/master/package/rules/sass.js. Also, my `application.sass` pack is also sass and this works fine. – fiedl Apr 17 '21 at 18:24
  • https://css-tricks.com/how-to-import-a-sass-file-into-every-vue-component-in-an-app/ Please refer this url – Nico Peck Apr 17 '21 at 18:29