3

I am trying to setup the loading of .pug files by Webpack depending on interim filename extension, herewith it should be default one. In my case, the files which names ends with .vue.pug must me loaded with by different rules that plain .pug.

enter image description here

In above preset, the { test: /\.vue.pug$/, /* ... */ } part is related with Vue framework and has been recommended me by developers of @webdiscus/pug-loader. The last part (test: /(?!.*\.vue\.pug$).*\.pug$/) is the default loader for Pug files.

{

  // ...

  module: {

    //
    rules: [

      // === Loading of Pug from files related with Vue
      {
        test: /\.vue.pug$/,
        oneOf: [
          // allow <template lang="pug"> in Vue components
          {
            resourceQuery: /.vue$/u,
            loader: '@webdiscus/pug-loader', // <-- it is same pug-loader as in PugPlugin.loader
            options: {
              method: 'html', // render Pug into pure HTML string
            },
          },
          // allow import of Pug in JS/TS and for "other cases", if a file hasn't the extension `.vue`
          {
            resourceQuery: ".vue.ts",
            loader: '@webdiscus/pug-loader', // <-- it is same pug-loader as in PugPlugin.loader
            options: {
              method: 'compile', // compile Pug into template function, use it if you want pass custom data into template on clinet-side rendering.
              //method: 'render', // compile Pug into template string, use if your template has't external custom data, generates smallest file
              //esModule: true, // should be `true` for importing in ES module, e.g. `import tmpl from './myTemplate.pug';`
                              // defaults is false, used for CommonJS, e.g.  `const tmpl = require('./myTemplate.pug');`
                              // NOTE: this option is optional, if it is not defined, importing in ES module works too.
            },
          },
        ],
      },

      // Loading of Pug from files not related with Vue
      {
        test: /(?!.*\.vue\.pug$).*\.pug$/,
        loader: '@webdiscus/pug-loader',
        options: {
          method: 'render'
        },
      },

    ]
  },

  // ...

};

Although the building is being executed without error, if to run script in browser, we'll get an error:

Uncaught TypeError: _ExampleMultipleFilesComponent_vue_pug__WEBPACK_IMPORTED_MODULE_0___default(...) is not a function

If to remove below part and do not import .pug files to non-vue files, no error will be.

{
  test: /(?!.*\.vue\.pug$).*\.pug$/,
  loader: '@webdiscus/pug-loader',
  options: {
    method: 'render'
  },
}

Looks like I do not understand how Webpack selects the appropriate loader. Please explain.

Repro repository (please make sure that you are checking out @problem/default_loader branch)

2
  • 1
    They load from bottom to top and that was answered here: stackoverflow.com/a/32234468/258174 as well as in their documentation. I don't understand why you can't simply define two rules with test: /\.vue\.pug$/ and test: /\.pug$/ and then use include or exclude accordingly?
    – morganney
    Commented May 16 at 14:24
  • @morganney, Thank you for the reference. In the answer you have mentioned, both loaders will be activated. But what if only one of them required? Commented May 19 at 1:34

2 Answers 2

1
+50

Although the building is being executed without error, if to run script in browser, we'll get an error:

I can't reproduce the error in browser (FireFox). In the browser I see compiled Vue components:

Example single file component
Example multiple files component with custom variable "someText": Hello Pug!

Explain how works the pug-loader

{
  test: /\.pug$/,
  oneOf: [
    // [1] issuer is VUE file
    {
      resourceQuery: /^\?vue/u,
      loader: '@webdiscus/pug-loader',
      options: {
        method: 'html', // exports rendered HTML sting
      },
    },
    // [2] issuer is TS file
    {
      loader: '@webdiscus/pug-loader',
      options: {
        method: 'compile', // export compiled template function
      },
    },
  ],
}

You load anywhere a Pug file .pug, then Webpack select the rule to process this file via the loader.

Where the Pug file is loaded is important, because for each issuer (parent) will be generate expected result.

You have two issuer types:

  1. Vue file. If you use Pug code in a Vue file, e.g. in ExampleSingleFileComponent.vue, then Webpack select [1] loader with the options to render Pug into pure HTML string, so is expected in this case.

  2. TS file. If you load a Pug file in TS/JS, e.g. in ExampleMultipleFilesComponent.vue.ts then Webpack select [2] loader with the options to compile the Pug file into JS template function. This template function you can call with variables to render it into HTML at the runtime, in browser.

10
  • please update the @webdiscus/pug-loader to 2.11.0 version
    – biodiscus
    Commented May 3 at 16:56
  • Thank you for the answer and comment. I has updated the repro repository including updating of of dependencies. You will get the error in your browser now. About the Webpack preset, in my case, I have set ` test: /\.vue.pug$/,, not test: /\.pug$/, so it must be applied only for files ends with .vue.pug. But for some reason, the loader with test: /(?!.*\.vue\.pug$).*\.pug$/` loads the ExampleMultipleFilesComponent.vue.pug. The mistake in in regular expression? Commented May 11 at 3:03
  • I ready to give the reputation prize for the explanation which will solve this problem. Hope it will be your one. If you will be so kind to check out the repro repository, please make sure that you have checkout the @problem/default_loader branch. Commented May 11 at 3:05
  • @TakeshiTokugawaYD I have created the PR in your branch. Please read my comments in code. Shortly: your webpack config was wrong ;-)
    – biodiscus
    Commented May 11 at 10:29
  • 1
    O'K, thank you for the help! I have accepted and upvoted your answer. Commented May 17 at 0:10
1

To your question, loaders in Webpack, loaders are executed from the bottom to the top of the configuration array. For example, with the array of loaders [1, 2, 3, 4, 5], the execution order would be 5->4->3->2-> 1. Each loader processes the module and then passes the transformed module to the next loader in the sequence, allowing multiple loaders to modify the code.

In your Webpack configuration, you're using the oneOf feature. This allows you to set up a list of loaders that only apply based on specific conditions like resourceQuery. When a top-level loader, such as one for .pug files, finds a match, Webpack checks the oneOf list next. Unlike the main configuration where loaders work from bottom to top, oneOf checks from top to bottom. Only the first loader that matches in the oneOf list is used, and it stops looking after that. So, the changes made by this loader aren't passed to any other loaders in the oneOf list.

Not the answer you're looking for? Browse other questions tagged or ask your own question.