Removing all data-test attributes from Vue templates during production build in Vue 3
Asked Answered
K

3

8

I work with Vue3 in TS (last vue-cli).

I want to get all nodes (vnodes) elements when vue-loader compile .vue file. I need to read nodes attributes and remove all "data-test".

I have try in vue.config.js to use :

module.exports = {
  chainWebpack: (config) => {
    config.module
      .rule('vue')
      .use('vue-loader')
      // .loader('vue-loader')                       // same with
      .tap((options) => {
        options.compilerOptions = {
          ...(options.compilerOptions || {}),
modules: [                                           // never enter here
            {
              preTransformNode(node) {
                // if (process.env.NODE_ENV === 'production') {
                const { attrsMap, attrsList } = node
                console.log(node)
                if (attrsMap['qa-id']) {
                  delete attrsMap['qa-id']
                  const index = attrsList.findIndex(
                    (x) => x.name === 'data-test'
                  )

                  attrsList.splice(index, 1)
                }
                // }

                return node
              }
            }
          ]
        }

        return options
      })
  }
}

I know the transformation is done inside vue-template-compiler. How can I enter in compile hook ?

I have try to use preTransformNode in module but that fail.

Sources :

Katonah answered 10/6, 2021 at 13:43 Comment(0)
G
21

The main problem here is that you are working with vue-template-compiler documentation, but that package is the compiler for Vue 2!

In Vue 3, compiler is split into multiple packages and is missing proper documentation as of now (or I was just unable to find it)

Also there were significant changes in the API - instead of modules, you pass nodeTransforms (source) and transforms are not objects, just functions.

Luckily for you, there is a interesting video on YT presented by Vue core member Rahul Kadyan which shows the exact use case you need (removing data-test attributes) - code

So I guess the code should look like this:

function removeDataTestAttrs(node) {
  if (node.type === 1 /* NodeTypes.ELEMENT */) {
    node.props = node.props.filter(prop =>
      prop.type === 6 /* NodeTypes.ATTRIBUTE */
        ? prop.name !== 'data-test'
        : true
    )
  }
}

module.exports = {
  parallel: false, // !!IMPORTANT!! - see note below
  chainWebpack: (config) => {
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap((options) => {
        options.compilerOptions = {
          ...(options.compilerOptions || {}),
          nodeTransforms: [removeDataTestAttrs]                                                                                                        
        }

        return options
      })
  }
}

Note - the problem mentioned in comments (solution working with serve but throws errors on build) is caused by Vue CLI using thread-loader for production builds. The problem is that while using thread-loader, you can not pass a functions as part of Webpack config (see this warning in the docs) so setting parallel: false is required to make it work....

Vite (Update - 22.06.22)

// vite.config.ts
function removeDataTestAttrs(node) {
  if (node.type === 1 /* NodeTypes.ELEMENT */) {
    node.props = node.props.filter(prop =>
      prop.type === 6 /* NodeTypes.ATTRIBUTE */
        ? prop.name !== 'data-test'
        : true
    )
  }
}

export default defineConfig(() => {
  return {
    plugins: [
      vue({
        template: {
          compilerOptions: {
            nodeTransforms: isProd ? [removeDataTestAttrs] : [],
          },
        },
      }),
    ]
  }
})
Grigson answered 10/6, 2021 at 15:14 Comment(3)
You are right about vue-template-compiler. Thank you, it's functional. Have a good dayKatonah
That works on serve but not on build : Syntax Error: Thread Loader (Worker 6) nodeTransforms[i] is not a functionKatonah
@BriceChaponneau Found the cause of the problem. Check my updated answer. Works now on my machine :)Bradski
V
1

Vue-CLI 5 + Vue 3.2:

const { defineConfig } = require('@vue/cli-service');

function removeAttributesDuringBuild (node) {
  const attributesToRemove = [
    'data-test',
    ':data-test',
    'v-bind:data-test',
    'data-value',
    ':data-value',
    'v-bind:data-value'
  ];
  const nodeIsElement = node.type === 1; // ENUMS ARE STUPID
  if (nodeIsElement) {
    node.props = node.props.filter(function (prop) {
      const propIsAttribute = prop.type === 6; // ENUMS ARE STUPID
      const propIsDynamicAttribute = prop.name === 'bind';
      if (propIsAttribute) {
        const attributeName = prop.name;
        return !attributesToRemove.includes(attributeName);
      }
      if (propIsDynamicAttribute) {
        const attributeName = prop.arg?.content;
        return !attributesToRemove.includes(attributeName);
      }
      return true;
    });
  }
}

module.exports = defineConfig({
  lintOnSave: false,
  transpileDependencies: true,
  parallel: false, // disabled to allow for node transforms
  chainWebpack: (config) => {
    // Remove comments during build
    config.optimization
      .minimizer('terser')
      .tap((args) => {
        args[0].terserOptions.output = {
          ...args[0].terserOptions.output,
          comments: false
        };
        return args;
      });

    // Remove dev attributes
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap(function (options) {
        options.compilerOptions = {
          ...(options.compilerOptions || {}),
          nodeTransforms: [removeAttributesDuringBuild]
        };
        return options;
      });
  }
});
Varioloid answered 29/8, 2022 at 23:46 Comment(0)
V
0

Vite 4 + Vue 2.7

import vue from '@vitejs/plugin-vue2';
import { defineConfig } from 'vite';

function removeAttributesDuringBuild (astEl) {
  const attributesToRemove = [
    'data-test',
    ':data-test',
    'v-bind:data-test',
    'data-value',
    ':data-value',
    'v-bind:data-value'
  ];

  function removeAttribute (attributesMap, attributesList, attributeToRemove) {
    if (attributesMap[attributeToRemove]) {
      delete attributesMap[attributeToRemove];
      const index = attributesList.findIndex(function (attribute) {
        return attribute.name === attributeToRemove;
      });
      attributesList.splice(index, 1);
    }
  }

  if (process.env.NODE_ENV === 'production') {
    const { attrsMap, attrsList } = astEl;
    attributesToRemove.forEach(function (attributeToRemove) {
      removeAttribute(attrsMap, attrsList, attributeToRemove);
    });
  }
  return astEl;
}

export default defineConfig(() => {
  return {
    plugins: [
      vue({
        template: {
          compilerOptions: {
            modules: [
              {
                preTransformNode: removeAttributesDuringBuild
              }
            ]
          }
        }
      })
    ]
  };
});
Varioloid answered 21/1, 2023 at 21:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.