Search code examples
htmlwebpackvuejs2webpack-loaderpug-loader

Webpack Pug/HTML loaders converts capital letters to lowercase on production mode


I am using both vue single-file components and separating of markup and logic to .pug and .ts files respectively. If you interesting why I don't unify is please see the comments section.

Problem

import template from "@UI_Framework/Components/Controls/InputFields/InputField.vue.pug";
import { Component, Vue } from "vue-property-decorator";

console.log(template);

@Component({
  template,
  components: {
    CompoundControlBase
  }
})
export default class InputField extends Vue {
    // ...
}

In development building mode exported template is correct (I beautified it for readability):

<CompoundControlBase 
  :required="required" 
  :displayInputIsRequiredBadge="displayInputIsRequiredBadge"
    <TextareaAutosize 
      v-if="multiline" 
      :value="value" 
    /><TextareaAutosize>
</CompoundControlBase>

In production mode, my markup has been lowercased. So, the console.log(template) outputs:

<compoundcontrolbase 
    :required=required 
    :displayinputisrequiredbadge=displayInputIsRequiredBadge 
    <textareaautosize 
        v-if=multiline 
        :value=value 
    ></textareaautosize>
</compoundcontrolbase>

Off course, I got broken view.

Webpack config

const WebpackConfig = {

  // ...
  optimization: {
    noEmitOnErrors: !isDevelopmentBuildingMode,
    minimize: !isDevelopmentBuildingMode
  },
  module: {
    rules: [
      
      {
        test: /\.vue$/u,
        loader: "vue-loader"
      },
      {
        test: /\.pug$/u,
        oneOf: [
          // for ".vue" files
          {
            resourceQuery: /^\?vue/u,
            use: [ "pug-plain-loader" ]
          },
          // for ".pug" files
          {
            use: [ "html-loader", "pug-html-loader" ]
          }
        ]
      },

      // ...
    ]
  }
}

Comments

To be honest, I don't know why we need ? in resourceQuery: /^\?vue/u, (explanations are welcome). However, in development building mode above config works property for both xxxx.vue and xxxx.vue.pug files.

I am using below files naming convention:

  • xxx.pug: pug file which will not be used as vue component template.
  • xxx.vue.pug: pug file which will be used as vue component template.
  • xxx.vue: single-file vue component.
  • xxx.vue.ts: the logic of vue component. Required exported template from xxx.vue.pug as in InputField case.

Why I need xxx.vue.ts? Because of this:

declare module "*.vue" {
  import Vue from "vue";
  export default Vue;
}

Neither public methods/fields nor non-default methods are visible for TypeScrpt xxx.vue files. For the common (non-applied) components, I can't accept it.

Repro

🌎 GitHub

Step 1: Install dependencies

npm i

Step 2: Let's check the development building first

npm run DevelopmentBuild

In line 156 of DevelopmentBuild\EntryPoint.js, you can check that below pug template:

Alpha
  Bravo OK

has been compiled properly:

image

Step 3: Problem on production build

npm run ProuductionBuild

You can find the lowercased tags in the column 13:

image

You can also open index.html in your browser and check the console.log() output with compiled TestComponent.

image


Solution

  • The problem is the "html-loader". It has the option minimize set to true in production mode (html-loader/#minimize).

    I had a similar problem in angular and had to unset some options like (see for reference html-minifier-terser#options-quick-reference).

    // webpack.config.js
    {
      test: /\.pug$/u,
      oneOf: [
        // for ".vue" files
        {
          resourceQuery: /^\?vue/u,
          use: [ "pug-plain-loader" ]
        },
        // for ".pug" files
        {
          use: [ "html-loader", "pug-html-loader" ]
        }
      ],
      options: {
        minimize: {                   // <----
          caseSensitive: false        // <----
        }                             // <----
      }
    },