vuejs/vue-loader

[VueLoaderPlugin Error] No matching rule for .vue files found - when files have different extension than .vue

roysanchez opened this issue ยท 17 comments

Version

15.0.0-rc.1

Reproduction link

N/A

Steps to reproduce

const path = require('path');
const webpack = require('webpack');
const { VueLoaderPlugin } = require('vue-loader')


module.exports = (env) => {
   const isDevBuild = !(env && env.prod);

   return [{
       stats: { modules: false },
       context: __dirname,
       resolve: { extensions: [ '.js', '.ts' ] },
       entry: { 'main': './ClientApp/boot.ts' },
       module: {
            rules: [
                { test: /\.vue\.html$/, loader: 'vue-loader' },  //Change this line to { test: /\.vue/, loader: 'vue-loader' } to make it work
                { test: /\.ts$/, use: 'ts-loader?silent=true' }
            ]
        },
       output: {
           path: path.join(__dirname,  './wwwroot/dist'),
           filename: '[name].js',
           publicPath: 'dist/'
       },
       plugins: [
           new VueLoaderPlugin()
       ]
   }];

};

What is expected?

The bundle is successful

What is actually happening?

Webpack throws the following error:
[VueLoaderPlugin Error] No matching rule for .vue files found.
Make sure there is at least one root-level rule that matches .vue files.

Why are you using .vue.html extension instead of just .vue?

Well, Vue SFCs are not technically HTML. The recommended approach is using the proper .vue extension and VSCode (which has excellent SFC support via the Vetur extension) instead Visual Studio.

If you really want to stick with vue.html, you can workaround this by having both rules:

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

Thanks that worked even thought it looks like a hack ๐Ÿ˜… as I don't have any .vue files in the project

@roysanchez I added a special case for .vue.html. Will work without the extra rule in the next release.

@yyx990803 I have a similar issue. My .vue files have become .pug instead. Is there any chance we can have a configurable option instead of just handling special cases?

Why not just add an optional regex to the current instruction?
rules: [
{ test: /.vue(.html)?$/, loader: 'vue-loader' },
]

@Flamenco the loader was working only when there was a rule for .vue specifically, hence the need to have multiple rules. I don't know if this is still the case.

Hi, I encountered the same error. The plugin is too restrictive in checking the rules. This is what I have:

        // We need to fool the VueLoaderPlugin, as that checks if a rule is available
        // for vue components, but it can't see our more restricted one....
        // It even checks the name of the loader, so aliasing also doesn't work.
        {
          test: /\.vue$/,
          include: path.resolve(__dirname),
          loader: 'vue-loader',
        },
        {
          test: /\.vue$/,
          include: options.sourceDirectory,
          issuer: /\.docs\.md$/,
          use: [
            { loader: 'fb-inject-vue-loader' },
            { loader: 'fb-vue-loader' },
          ],
        },

This comes from a package we make that is to be used with other packages, so we do aliasing to be sure that the others can use different versions of loaders etc...

้ƒฝๆ˜ฏๅคง็ฅž

I fund the cause of this issue in /node_modules/vue-loader/lib/plugin.js there is this checking point vueLoaderUseIndex < 0 under const vueUse = vueRule.use I make this following changes

let ListIndex = [];

    const vueLoaderUseIndex = vueUse.findIndex(u => {
      console.log(u)
      let vueChecker =  /^vue-loader|(\/|\\|@)vue-loader/.test(u.loader)

      console.log({u:u.loader})

      ListIndex[u.loader] = vueChecker
      return vueChecker
    })
    console.log({ListIndex:ListIndex['vue-loader']})

in my terminal I have this

{ ListIndex: [ 'vue-loader': true ] }
{ ListIndex: [ 'html-loader': false ] }

this show to rules are been fire when I npm run dev and this html-loader is not defined in my web pack config file so vueLoaderUseIndex < 0 stop the processing since 'html-loader': false

am currently working on the solution or if there is fast fix for this please share

am able to bypass the error by doing this but I dont know if it right thing to do


const vueLoaderUseIndex = vueUse.findIndex(u => {
      let vueChecker =  /^vue-loader|(\/|\\|@)vue-loader/.test(u.loader)
                       || /^babel-loader|(\/|\\|@)babel-loader/.test(u.loader)
                       || /^html-loader|(\/|\\|@)html-loader/.test(u.loader)
                       || /^file-loader|(\/|\\|@)file-loader/.test(u.loader)
                       || /^img-loader|(\/|\\|@)img-loader/.test(u.loader)
      return vueChecker
    })

I do not believe this is fixed. I see the same behavior as @diadal in that the array of rules is processed as 1 item arrays one at a time, so always fails on the non-vue rules. So some guidance on usage is desired if that is expected as it implies no other rules can be used in presence of Vue Plugin.

I wonder though if this is only in the context of storybook loading of the webpack config incorrectly. Will investigate

I just encountered the same error on vue-loader 15.8.3. While inspecting the source for my webpack version at plugin-webpack4.js

let vueRuleIndex = rawRules.findIndex(createMatcher(`foo.vue`))

I came to the conclusion that the matcher created for foo.vue allows a rule for html-loader to pass the test for some reasons I don't know. I was able to get it work by replace the line55 of the piece of code below with @diadal's comment #1238 (comment)
const vueLoaderUseIndex = vueUse.findIndex(u => {
return /^vue-loader|(\/|\\|@)vue-loader/.test(u.loader)
})

But I don't think this can be a decent solution to this issue.

actually to me npm update corrected the error

also to me just npm update

For me the fix was to move the rule in my config to the top of the array of rules. Can't tell you why, but it did the trick!

  • Webpack v5.52.0
  • webpack-dev-server 4.1.0
  • yarn v11.22.11

In my webpack.common.js:

const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
    entry: {
        main  : "./src/main.js",
        vendor: "./src/vendor.js",
    },

    module: {
        rules: [

            // Moved this to the top of all rules
            {
                test  : /\.vue$/,
                loader: "vue-loader",
            },

            {
                test: /\.html$/,
                use : ["html-loader"],
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: "babel-loader",
                }
            },
            {
                test: /\.(svg|png|jpe?g|gif)$/,
                use : {
                    loader : "file-loader",
                    options: {
                        name      : "[name].[contentHash:8].[ext]",
                        outputPath: "assets/img",
                        esModule  : false,
                    },
                },
            },
        ],
    },

    plugins: [
        new VueLoaderPlugin(),
    ],
}

And my webpack.dev.js (just for reference since i'm using configs for different enviornments):

const autoprefixer = require("autoprefixer")
const common       = require("./webpack.common")
const { merge }    = require("webpack-merge")
const path         = require('path')

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = merge(common, {
    mode   : "development",
    devtool: "source-map",

    output: {
        filename: "[name].bundle.js",
        path    : path.resolve(__dirname, "dist", "index.html"),
    },

    module: {
        rules: [
            {
                test: /\.scss$/,
                use : [
                    "style-loader",
                    "css-loader",
                    {
                        loader: "postcss-loader",
                        options: {
                            plugins: () => [autoprefixer()],
                        },
                    },
                    "sass-loader",
                ],
            },
        ],
    },

    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/template.html"
        }),
    ],
})