vue-loader 15 with laravel-mix

Никита Гулис picture Никита Гулис · May 29, 2018 · Viewed 10k times · Source

So i am trying to update my vue-loader in laravel project to version 15.2.1. After updating dependencies and running npm run watch first i get an error that i shoul use VueLoaderPlugin. I added it like official documentation suggests.
After trying to run build command again i get this error for each one of my single file components:

    ERROR in ./node_modules/css-loader!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/sass-loader/lib/loader.js!./node_modules/vue-loader/lib??vue-loader-options!./resources/assets/js/components/users/User.vue?vue&type=style&index=0&id=d0ee1f54&scoped=true&lang=sass
Module build failed:
<template lang="pug">
^
      Invalid CSS after "": expected 1 selector or at-rule, was ".user-container"
      in C:\MAMP\htdocs\lightCRM\resources\assets\js\components\users\User.vue (line 1, column 1)
 @ ./node_modules/style-loader!./node_modules/css-loader!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/sass-loader/lib/loader.js!./node_modules/vue-loader/lib??vue-loader-options!./resources/assets/js/components/users/User.vue?vue&type=style&index=0&id=d0ee1f54&scoped=true&lang=sass 4:14-338
 @ ./resources/assets/js/components/users/User.vue?vue&type=style&index=0&id=d0ee1f54&scoped=true&lang=sass
 @ ./resources/assets/js/components/users/User.vue
 @ ./resources/assets/js/routes/routes.js
 @ ./resources/assets/js/app.js
 @ multi ./resources/assets/js/app.js

If iam adding this rule to my laravel-mix config

rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]

Then compilation runs successfully, but in console I get:

[vue warn]: failed to mount component: template or render function not defined.  

I use sass and pug in my vue components with appropriate loaders. Adding more rules to laravel-mix config seems to make no difference. All dependencies are up-to date and work well with vue-loader v.14.2.2. Node.js version is 10.1.0 and npm is 6.1.0.
Also here is my package.json:

    {
    "private": true,
    "scripts": {
        "dev": "npm run development",
        "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "watch-poll": "npm run watch -- --watch-poll",
        "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
        "prod": "npm run production",
        "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
    },
    "devDependencies": {
        "axios": "^0.18.0",
        "babel-core": "^6.26.3",
        "babel-loader": "^7.1.4",
        "babel-plugin-transform-runtime": "^6.23.0",
        "babel-preset-es2015": "^6.24.1",
        "cross-env": "^5.1.6",
        "css-loader": "^0.28.11",
        "laravel-mix": "2.1",
        "lodash": "^4.17.10",
        "node-sass": "^4.9.0",
        "pug": "^2.0.3",
        "pug-loader": "^2.4.0",
        "pug-plain-loader": "^1.0.0",
        "sass-loader": "^7.0.1",
        "sass-resources-loader": "^1.3.3",
        "vue": "^2.5.16",
        "vue-html-loader": "^1.2.4",
        "vue-loader": "^15.2.1",
        "vue-style-loader": "^4.1.0"
    },
    "dependencies": {
        "es6-promise": "^4.2.4",
        "vue-multiselect": "^2.1.0",
        "vue-router": "^3.0.1",
        "vuex": "^3.0.1"
    }
}  

Here is resolve statement from WebpackConfig.js

buildResolving() {
    this.webpackConfig.resolve = {
        extensions: ['*', '.js', '.jsx', '.vue'],

        alias: {
            vue$: 'vue/dist/vue.common.js'
        }
    };

    return this;
}  

And here is all of my webpack.mix.js

    mix.js('resources/assets/js/app.js', 'public/js')
   .copy('resources/assets/fonts/', 'public/fonts/')
   .webpackConfig({
     plugins: [
       new VueLoaderPlugin()
     ],
     resolve: {
       alias: {
         'Global': path.resolve('resources/assets/sass')
       }
     }
   })

Answer

Noogen picture Noogen · Nov 13, 2018

I have it working fine. Here's a library project using laravel mix with Vue Loader 15 - https://github.com/niiknow/vue-datatables-net

I don't have a public Laravel project to demonstrate but here's my private project package.json

{
    "private": true,
    "scripts": {
        "clean": "rm -rf ./public/js/parts/*",
        "dev": "npm run development",
        "development": "npm run clean && cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "watch": "npm run clean && cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "watch-poll": "npm run watch -- --watch-poll",
        "hot": "npm run clean && cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
        "prod": "npm run production",
        "production": "npm run clean && cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "test": "npm run clean && cross-env NODE_ENV=test mocha-webpack --webpack-config=node_modules/laravel-mix/setup/webpack.config.js --require tests/Vue/setup.js tests/Vue/**/*.spec.js",
        "watch-test": "npm run clean && cross-env NODE_ENV=test mocha-webpack --webpack-config=node_modules/laravel-mix/setup/webpack.config.js --watch --require tests/Vue/setup.js tests/Vue/**/*.spec.js",
        "lint": "eslint --ext .js,.vue resources/js",
        "lint-fix": "eslint --fix --ext .js,.vue resources/js",
        "check-outdated": "npm outdated"
    },
    "devDependencies": {
        "autotrack": "^2.4.1",
        "axios": "^0.18.0",
        "axios-jsonp": "^1.0.2",
        "babel-core": "^6.26.3",
        "babel-eslint": "^10.0.1",
        "babel-loader": "^7.1.5",
        "babel-plugin-transform-imports": "^1.5.1",
        "babel-polyfill": "^6.26.0",
        "babel-preset-vue-app": "^2.0.0",
        "bootstrap": "^4.1.3",
        "bootstrap-vue": "^2.0.0-rc.11",
        "browser-sync": "^2.26.0",
        "browser-sync-webpack-plugin": "^2.0.1",
        "cross-env": "^5.1.6",
        "datatables.net-bs4": "^1.10.19",
        "datatables.net-buttons-bs4": "^1.5.4",
        "datatables.net-fixedheader-bs4": "^3.1.5",
        "datatables.net-responsive-bs4": "^2.2.3",
        "eslint": "^5.9.0",
        "eslint-config-prettier": "^3.3.0",
        "eslint-friendly-formatter": "^4.0.1",
        "eslint-loader": "^2.1.0",
        "eslint-plugin-vue": "^5.0.0-beta.4",
        "inputmask": "^4.0.3",
        "jQuery-QueryBuilder": "^2.5.2",
        "jquery": "^3.3.1",
        "laravel-mix": "^2.1.14",
        "lodash": "^4.17.11",
        "noty": "^3.2.0-beta",
        "popper.js": "^1.14.5",
        "sweetalert2": "^7.29.0",
        "tinycolor2": "^1.4.1",
        "vee-validate": "2.1.2",
        "vform": "^1.0.0",
        "vue": "^2.5.17",
        "vue-authenticate": "^1.3.4",
        "vue-axios": "^2.0.2",
        "vue-datatables-net": "^1.0.1",
        "vue-element-loading": "^1.0.5",
        "vue-i18n": "^8.3.1",
        "vue-loader": "^15.4.2",
        "vue-router": "^3.0.1",
        "vue-template-compiler": "^2.5.17",
        "vue2-google-maps": "^0.10.2",
        "vuedraggable": "^2.16.0",
        "vuex": "^3.0.1",
        "vuex-persistedstate": "^2.5.4",
        "vuex-router-sync": "^5.0.0"
    },
    "dependencies": {},
    "browserslist": [
        "> 1%",
        "last 2 versions",
        "not ie <= 9"
    ],
    "engines": {
        "node": ">= 8.10.x",
        "npm": ">= 5.6.0"
    }
}

And here's my webpack.mix.js

const path = require('path');
const mix = require('laravel-mix');
const { VueLoaderPlugin } = require('vue-loader');
const source = 'resources';
const public = 'public';

mix.options({
  processCssUrls: false,
  uglify: {
    uglifyOptions: {
      compress: {
        drop_console: true
      }
    }
  }
});

mix.setPublicPath(path.normalize(public));

mix.webpackConfig({
  externals: {
    'jquery': 'jQuery'
  },
  output: { chunkFilename: 'js/parts/[name].js' },
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.(vue|js)$/,
        exclude: /(node_modules|bower_components)/,
        loader: 'eslint-loader',
        options: {
          fix: false,
          cache: false,
          formatter: require('eslint-friendly-formatter')
        }
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ],
  devServer: { overlay: true },
  devtool: 'source-map',
  resolve: {
    /* Path Shortcuts */
    alias:{
      /* root */
      '~': path.resolve(__dirname, `${ source }/js`),
      Components: path.resolve(__dirname, `${ source }/js/components`),
      Layouts: path.resolve(__dirname, `${ source }/js/layouts`),
      Pages: path.resolve(__dirname, `${ source }/js/pages`)
    }
  }
});

mix.js(`${ source }/js/myapp.js`, `${ public }/js`);
mix.sass(`${ source }/sass/myapp.scss`, `${ public }/css`, {
  outputStyle: mix.inProduction() ? 'compact' : 'expanded'
});
mix.sourceMaps();
mix.browserSync({
  proxy: 'yourproject.test',
  host: 'yourproject.test',
  files: [
    `${ source }/views/**/*.php`,
    `${ public }/js/**/*.js`,
    `${ public }/css/**/*.css`
  ],
  browser: 'firefox',
  ghostMode: false,
  open: 'external'
});

mix.extract([
  'autotrack',
  'axios',
  'axios-jsonp',
  'noty',
  'popper.js',
  'sweetalert2',
  'tinycolor2',
  'vee-validate',
  'vform',
  'vue',
  'vue-authenticate',
  'vue-axios',
  'vue-element-loading',
  'vue-i18n',
  'vue-router',
  'vue2-google-maps',
  'vuedraggable',
  'vuex',
  'vuex-persistedstate',
  'vuex-router-sync'
]);

if (mix.inProduction()) {
  mix.version();
  mix.disableNotifications();
}

And here's my .babelrc

{
    "presets":
    [
        [
            "env",
            {
                "targets": {
                    "browsers": ["last 2 versions"]
                },
                "debug": true,
                "useBuiltIns": "entry"
            }
        ]
    ],
    "plugins": ["transform-runtime", "syntax-dynamic-import"]
}

Here's .eslint.js

module.exports = {
  root: true,
  env: {
    browser: true,
  },
  plugins: ['vue'], // enable vue plugin
  extends: ["plugin:vue/recommended", "prettier"], // activate vue related rules
  parserOptions: {
    "parser": "babel-eslint",
    "ecmaVersion": 7,
    "sourceType": "module",
    "ecmaFeatures": {
      "globalReturn": false,
      "impliedStrict": false,
      "jsx": false,
      "experimentalObjectRestSpread": false,
      "allowImportExportEverywhere": false
    }
  },
  rules: {
    // allow paren-less arrow functions
    "arrow-parens": 0,
    // allow async-await
    "generator-star-spacing": 0,
    // allow debugger during development
    "no-debugger": process.env.NODE_ENV === 'production' ? 2 : 0,
    "semi": [2, "never"],
    "quotes": [2, "single"],
    "vue/require-default-prop": 0,
    "vue/require-prop-types": 0,
    "vue/no-v-html": 0
  }
};