cuth / postcss-pxtorem

Convert pixel units to rem (root em) units using PostCSS
MIT License
2.02k stars 174 forks source link

[Bug Report: 6.0] Nested components and views may cause rootValue miscalculated (root in Once Event and decl in Declaration Event may be different) #103

Open zhaojjiang opened 7 months ago

zhaojjiang commented 7 months ago

When project is complex enough, there will be nested views and components.

The event order can be Parent-Once --> Children-Once --> Children-Declaration --> Parent-Declaration, which result the rootValue miscalculated from function and so is rem.

I tried many times, and this problem happened many times, but I cannot provide a minimum code to stably reproduce it.

I have noticed there is runtime-defined listeners prepare in postcss may help solve this problem, and tried in another private project which can reproduce this problem. It works. The code like below. Move events wrapped by prepare().

I don't if this logic is right, so just leave it here. In my project, I will just downgrade package to 5.x in a short peried of time to avoid this problem.

module.exports = (options = {}) => {
  convertLegacyOptions(options);
  const opts = Object.assign({}, defaults, options);
  const satisfyPropList = createPropListMatcher(opts.propList);
  const exclude = opts.exclude;

  return {
    postcssPlugin: "postcss-pxtorem",
    prepare(result) {
      // console.log(result);

      let isExcludeFile = false;
      let pxReplace;

      return {
        Once(css) {
          // console.log('once--------', css.source.input.file);

          const filePath = css.source.input.file;
          if (
            exclude &&
            ((type.isFunction(exclude) && exclude(filePath)) ||
              (type.isString(exclude) && filePath.indexOf(exclude) !== -1) ||
              filePath.match(exclude) !== null)
          ) {
            isExcludeFile = true;
          } else {
            isExcludeFile = false;
          }

          const rootValue =
            typeof opts.rootValue === "function"
              ? opts.rootValue(css.source.input)
              : opts.rootValue;
          // console.log(css.source.input.file, rootValue);

          pxReplace = createPxReplace(
            rootValue,
            opts.unitPrecision,
            opts.minPixelValue
          );
        },
        Declaration(decl) {
          // console.log('decl', decl.source.input.file);
          if (isExcludeFile) return;

          if (
            decl.value.indexOf("px") === -1 ||
            !satisfyPropList(decl.prop) ||
            blacklistedSelector(opts.selectorBlackList, decl.parent.selector)
          )
            return;

          const value = decl.value.replace(pxRegex, pxReplace);

          // if rem unit already exists, do not add or replace
          if (declarationExists(decl.parent, decl.prop, value)) return;

          if (opts.replace) {
            decl.value = value;
          } else {
            decl.cloneAfter({ value: value });
          }
        },
        AtRule(atRule) {
          if (isExcludeFile) return;

          if (opts.mediaQuery && atRule.name === "media") {
            if (atRule.params.indexOf("px") === -1) return;
            atRule.params = atRule.params.replace(pxRegex, pxReplace);
          }
        }
      }
    },
  };
};
zhaojjiang commented 7 months ago

additional info: this problem happens only when reuse postcss or postcss-loader (I am not sure how to describe this concept)

Test code:

// ...

let pxReplace;
let testInc = 0; // add line
return {
  postcssPlugin: "poxtcss-pxtorem",
  Once(css) {
    console.log('inc---------------', testInc); // add console
    testInc++; // add line

   // ...
  },
  // ...
};
// ...

the console in vue by webpack & vue-cli is always 0, but increased to 1, 2, 3, ... in vue by vite & rollup