webpack-contrib / mini-css-extract-plugin

Lightweight CSS extraction plugin
MIT License
4.67k stars 389 forks source link

import empty less file result in undefined #1019

Closed despicable123 closed 1 year ago

despicable123 commented 1 year ago

Bug report

When using css module, e.g.

import type { FC } from 'react';
import { TabBar } from 'antd-mobile';
import { bottomLabels } from '../../constants';
import styles from './index.less';

type BottomBarProps = {
  activeKey: string;
  setActiveKey: (val: string) => void;
};
const BottomBar: FC<BottomBarProps> = (props) => {
  const { activeKey, setActiveKey } = props;
  console.log('bottom style', styles);
  return (
    <div className={styles.bottom}>
      <TabBar
        activeKey={activeKey}
        onChange={(key: string) => setActiveKey(key)}
      >
        {bottomLabels.map((item) => (
          <TabBar.Item key={item.key} title={item.title} />
        ))}
      </TabBar>
    </div>
  );
};

export default BottomBar;

If the less file is empty, print "styles" style-loader produces empty object,
screen_shot_1677820747047

but MiniCssExtractPlugin.loader produces undefined (production) screen_shot_1677821082253

here is my base.js

const Config = require('webpack-chain');
const webpack = require('webpack');
const { getEntryPaths, resolveApp } = require('../../utils/path');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const { isDev } = require('../../utils/env');
const semver = require('semver');

const config = new Config();

config.unitoolShare = {};

async function use() {
  /**
   * entry 
   */
  const pathData = await getEntryPaths('src/projects', [
    'index.js',
    'index.ts',
    'index.jsx',
    'index.tsx',
  ]);
  config.unitoolShare.entryMap = pathData;
  for (const item of Object.entries(pathData)) {
    config.entry([item[0]]).add(item[1]);
  }

  function isAntdMobileV5() {
    try {
      const antdMobilePackageJson = require('antd-mobile/package.json');
      const { version } = antdMobilePackageJson;
      return version && semver.gte(version, '5.0.0'); // version.startsWith('5.')
    } catch (err) {
      console.error(
        'antd-mobile/package.json not found, will use babel-plugin-import.'
      );
    }
  }

  config.resolve.extensions
   .add('.ts')
    .add('.tsx')
     .add('.js')
    .add('.jsx')
    .add('.json')
    .end();

  config.resolve.alias.set('@', resolveApp('src'));

  config.cache({
    type: 'filesystem',
  });

  /**
   * module 
   */

  // 搬运react-scripts-mpa

  config.module
    .rule('src')
    .test(/\.(js|mjs|jsx|ts|tsx)$/)
    .include.add(resolveApp('src'))
    .end()
    .use('babel-loader')
    .loader('babel-loader')
    .options({
      presets: [
        [
          require('@babel/preset-env'),
          {
            useBuiltIns: 'usage', /
            corejs: 3, 
          },
        ],
        [require('@babel/preset-react'), { runtime: 'automatic' }],
        require('@babel/preset-typescript'),
      ],
      plugins: [
        !isAntdMobileV5 && [
          'import',
          { libraryName: 'antd-mobile', style: true },
        ],
        [
          require.resolve('babel-plugin-named-asset-import'),
          {
            loaderMap: {
              svg: {
                ReactComponent: '@svgr/webpack?-svgo,+titleProp,+ref![path]',
              },
            },
          },
        ],
        require.resolve('@babel/plugin-proposal-optional-chaining'),
        isDev() && require.resolve('react-refresh/babel'),
      ].filter(Boolean),
    })
    .end();

  config.module
    .rule('node_module')
    .test(/\.(js|mjs)$/)
    .exclude.add(/@babel(?:\/|\\{1,2})runtime/)
    .add(resolveApp('src'))
    .end()
    .use('babel-loader')
    .loader('babel-loader')
    .options({
      presets: [
        [
          require.resolve('babel-preset-react-app/dependencies'),
          { helpers: true },
        ],
      ],
      plugins: ['@babel/plugin-proposal-optional-chaining'],
    })
    .end();

  config.module.rule('node_module').resolve.set('fullySpecified', false);

  config.module
    .rule('pic')
    .test([/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/])
    .type('asset')
    .parser({
      dataUrlCondition: {
        maxSize: 10000,
      },
    })
    .set('generator', {
      filename: 'img/[name].[ext]',
    });

  config.module
    .rule('font')
    .test(/.(woff2?|eot|ttf|otf)$/)
    .type('asset')
    .parser({
      dataUrlCondition: {
        maxSize: 10000,
      },
    })
    .set('generator', {
      filename: 'fonts/[name].[ext]',
    });

  config.module
    .rule('media')
    .test(/.(mp4|webm|ogg|mp3|wav|flac|aac)$/)
    .type('asset')
    .parser({
      dataUrlCondition: {
        maxSize: 10000,
      },
    })
    .set('generator', {
      filename: 'media/[name].[ext]',
    });

  /**
   * plugins 
   */
  config.plugin('definePlugin').use(webpack.DefinePlugin, [
    {
      'process.env': {
        NODE_ENV: JSON.stringify(process.env.NODE_ENV),
        PLATFORM: JSON.stringify(process.env.PLATFORM),
      },
    },
  ]);

  config
    .plugin('moduleNotFound')
    .use(ModuleNotFoundPlugin, [resolveApp('src')]);

  return config;
}

module.exports = {
  use,
};

here is my build.js

const path = require('path');
const { use: useBase } = require('./base');
const TerserPlugin = require('terser-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ConcatJobIDPlugin = require('@ht/webpack-plugin-concat-jobid');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const postcssNormalize = require('postcss-normalize');
const { isNative } = require('../../utils/env');
const { resolveApp } = require('../../utils/path');

const prjPkgJsonPath = path.resolve(process.cwd(), './package.json');
// eslint-disable-next-line import/no-dynamic-require
const prjPkgJson = require(prjPkgJsonPath);

async function use() {
  const config = await useBase();
  config.mode('production');
  // config.mode('development');

  /**
   * output
   */
  isNative()
    ? config.output
        .path(path.resolve(process.cwd(), `./web_cft/${prjPkgJson.name}`))
        .filename('js/[name].js')
        .chunkFilename('js/common.[id].js')
        .publicPath('./')
    : config.output
        .path(path.resolve(process.cwd(), `./web_browser/${prjPkgJson.name}`))
        .filename('js/[name].[chunkhash].js')
        .chunkFilename('js/common.[id].[chunkhash].js')
        .publicPath('./');

  config.module
    .rule('css')
    .test(/.(css)$/)
    .include.add(resolveApp('src'))
    .end()
    .use('css-extract')
    .loader(MiniCssExtractPlugin.loader)
    .options({
      publicPath: '../',
    })
    .end()
    .use('css-loader')
    .loader('css-loader')
    .options({
      importLoaders: 1,
      modules: {
        mode: 'local',
        localIdentName: '[local]_[hash:base64:5]',
      },
    })
    .end()
    .use('postcss-loader')
    .loader('postcss-loader')
    .options({
      ident: 'postcss',
      plugins: () =>
        [
          process.env.PX_TO_REM &&
            require('postcss-pxtorem')({
              rootValue: process.env.PX_TO_REM_ROOT_VALUE || 37.5,
              propWhiteList: [],
            }),
          require('postcss-flexbugs-fixes'),
          require('postcss-preset-env')({
            autoprefixer: {
              flexbox: 'no-2009',
            },
            stage: 3,
          }),
          // Adds PostCSS Normalize as the reset css with default options,
          // so that it honors browserslist config in package.json
          // which in turn let's users customize the target behavior as per their needs.
          postcssNormalize(),
        ].filter(Boolean),
    })
    .end();

  config.module
    .rule('less')
    .test(/.(less)$/)
    .include.add(resolveApp('src'))
    .end()
    .use('less-extract')
    .loader(MiniCssExtractPlugin.loader)
    .options({
      publicPath: '../',
    })
    .end()
    // .use('style-loader')
    // .loader('style-loader')
    // .end()
    .use('css-loader')
    .loader('css-loader')
    .options({
      importLoaders: 3,
      esModule: true,
      modules: {
        mode: 'local',
        namedExport: true,
        localIdentName: '[local]_[hash:base64:5]',
      },
    })
    .end()
    .use('postcss-loader')
    .loader('postcss-loader')
    .options({
      ident: 'postcss',
      plugins: () =>
        [
          process.env.PX_TO_REM &&
            require('postcss-pxtorem')({
              rootValue: process.env.PX_TO_REM_ROOT_VALUE || 37.5,
              propWhiteList: [],
            }),
          require('postcss-flexbugs-fixes'),
          require('postcss-preset-env')({
            autoprefixer: {
              flexbox: 'no-2009',
            },
            stage: 3,
          }),
          // Adds PostCSS Normalize as the reset css with default options,
          // so that it honors browserslist config in package.json
          // which in turn let's users customize the target behavior as per their needs.
          postcssNormalize(),
        ].filter(Boolean),
    })
    .end()
    .use('less-loader')
    .loader('less-loader')
    .options({
      lessOptions: {
        javascriptEnabled: true,
      },
    })
    .end();

  config.module
    .rule('css_node_module')
    .test(/.(css)$/)
    .exclude.add(resolveApp('src'))
    .end()
    .use('css-extract')
    .loader(MiniCssExtractPlugin.loader)
    .options({
      publicPath: '../',
    })
    .end()
    .use('css-loader')
    .loader('css-loader')
    .options({
      importLoaders: 1,
      modules: false,
    })
    .end()
    .use('postcss-loader')
    .loader('postcss-loader')
    .options({
      ident: 'postcss',
      plugins: () =>
        [
          process.env.PX_TO_REM &&
            require('postcss-pxtorem')({
              rootValue: process.env.PX_TO_REM_ROOT_VALUE || 37.5,
              propWhiteList: [],
            }),
          require('postcss-flexbugs-fixes'),
          require('postcss-preset-env')({
            autoprefixer: {
              flexbox: 'no-2009',
            },
            stage: 3,
          }),
          // Adds PostCSS Normalize as the reset css with default options,
          // so that it honors browserslist config in package.json
          // which in turn let's users customize the target behavior as per their needs.
          postcssNormalize(),
        ].filter(Boolean),
    })
    .end();

  config.module
    .rule('less_node_module')
    .test(/.(less)$/)
    .exclude.add(resolveApp('src'))
    .end()
    .use('less-extract')
    .loader(MiniCssExtractPlugin.loader)
    .options({
      publicPath: '../',
    })
    .end()
    .use('css-loader')
    .loader('css-loader')
    .options({
      importLoaders: 3,
      modules: false,
    })
    .end()
    .use('postcss-loader')
    .loader('postcss-loader')
    .options({
      ident: 'postcss',
      plugins: () =>
        [
          process.env.PX_TO_REM &&
            require('postcss-pxtorem')({
              rootValue: process.env.PX_TO_REM_ROOT_VALUE || 37.5,
              propWhiteList: [],
            }),
          require('postcss-flexbugs-fixes'),
          require('postcss-preset-env')({
            autoprefixer: {
              flexbox: 'no-2009',
            },
            stage: 3,
          }),
          // Adds PostCSS Normalize as the reset css with default options,
          // so that it honors browserslist config in package.json
          // which in turn let's users customize the target behavior as per their needs.
          postcssNormalize(),
        ].filter(Boolean),
    })
    .end()
    .use('less-loader')
    .loader('less-loader')
    .options({
      lessOptions: {
        javascriptEnabled: true,
      },
    })
    .end();
  /**
   * optimization
   */
  // config.optimization.splitChunks({
  //   // 代码分割配置
  //   chunks: 'async',
  //   minSize: 30000,
  //   maxSize: 0,
  //   minChunks: 1,
  //   maxAsyncRequests: 5,
  //   maxInitialRequests: 3,
  //   automaticNameDelimiter: '~',
  //   // name: true,
  //   cacheGroups: {
  //     vendors: {
  //       test: /[\\/]node_modules[\\/]/,
  //       priority: -10,
  //     },
  //     default: {
  //       minChunks: 2,
  //       priority: -20,
  //       // reuseExistingChunk: true,
  //       chunks: 'initial',
  //       name: 'common',
  //     },
  //   },
  // });

  // config.optimization.minimizer('js').use(TerserPlugin);
  // config.optimization.minimizer('css').use(CssMinimizerPlugin);

  /**
   * plugins
   */
  // html plugin
  const { entryMap = {} } = config.unitoolShare;
  Object.keys(entryMap).map((page) => {
    return config.plugin(`htmlWebpackPlugin-${page}`).use(HtmlWebpackPlugin, [
      {
        filename: `./${page}.html`,
        template: isNative()
          ? './public/index.native.html'
          : './public/index.browser.html',
        title: page,
        inject: true,
        chunks: [page, 'common'],
        minify: {
          removeComments: true,
          collapseWhitespace: true,
          removeAttributeQuotes: true,
          // 更多配置:https://github.com/kangax/html-minifier#options-quick-reference
        },
        chunksSortMode: 'auto',
      },
    ]);
  });
  // config.plugin('hashedModuleIdsPlugin').use(webpack.HashedModuleIdsPlugin, []);
  config.plugin('copyWebpackPlugin').use(CopyWebpackPlugin, [
    {
      patterns: [
        {
          from: path.resolve(process.cwd(), './public'),
          to: path.resolve(process.cwd(), `./web_cft/${prjPkgJson.name}`),
          toType: 'dir',
          globOptions: {
            ignore: ['**/*.html'],
            gitignore: true,
          },
        },
      ],
    },
  ]);
  config.plugin('cleanWebpackPlugin').use(CleanWebpackPlugin);
  // config.plugin('concatJobIDPlugin').use(ConcatJobIDPlugin);
  // config
  //   .plugin('optimizeCssAssetsWebpackPlugin')
  //   .use(OptimizeCssAssetsWebpackPlugin);
  config.plugin('miniCssExtractPlugin').use(MiniCssExtractPlugin, [
    {
      filename: 'css/[name].css',
    },
  ]);
  // config.plugin('bundleAnalyzerPlugin').use(BundleAnalyzerPlugin);

  /**
   * 其他
   */
  config.devtool('eval-cheap-source-map');
  return config;
}

// use();

module.exports = {
  use,
};

my webpack config result

{"cache":{"type":"filesystem"},"mode":"production","devtool":"eval-cheap-source-map","output":{"path":"/Users/haowu/Projects/nickname-modify/web_cft/nickname-modify","filename":"js/[name].js","chunkFilename":"js/common.[id].js","publicPath":"./"},"resolve":{"alias":{"@":"/Users/haowu/Projects/nickname-modify/src"},"extensions":[".js",".jsx",".tsx",".ts",".json"]},"module":{"rules":[{"test":{},"include":["/Users/haowu/Projects/nickname-modify/src"],"use":[{"loader":"babel-loader","options":{"presets":[[{},{"useBuiltIns":"usage","corejs":3}],[{},{"runtime":"automatic"}],{}],"plugins":[["/Users/haowu/Projects/unitool/node_modules/babel-plugin-named-asset-import/index.js",{"loaderMap":{"svg":{"ReactComponent":"@svgr/webpack?-svgo,+titleProp,+ref![path]"}}}],"/Users/haowu/Projects/unitool/node_modules/@babel/plugin-proposal-optional-chaining/lib/index.js"]}}],"resolve":{"fullySpecified":false}},{"test":{},"exclude":[{},"/Users/haowu/Projects/nickname-modify/src"],"use":[{"loader":"babel-loader","options":{"presets":[["/Users/haowu/Projects/unitool/node_modules/babel-preset-react-app/dependencies.js",{"helpers":true}]],"plugins":["@babel/plugin-proposal-optional-chaining"]}}],"resolve":{"fullySpecified":false}},{"test":[{},{},{},{}],"type":"asset","parser":{"dataUrlCondition":{"maxSize":10000}},"generator":{"filename":"img/[name].[ext]"}},{"test":{},"type":"asset","parser":{"dataUrlCondition":{"maxSize":10000}},"generator":{"filename":"fonts/[name].[ext]"}},{"test":{},"type":"asset","parser":{"dataUrlCondition":{"maxSize":10000}},"generator":{"filename":"media/[name].[ext]"}},{"test":{},"include":["/Users/haowu/Projects/nickname-modify/src"],"use":[{"loader":"/Users/haowu/Projects/unitool/node_modules/mini-css-extract-plugin/dist/loader.js","options":{"publicPath":"../"}},{"loader":"css-loader","options":{"importLoaders":1,"modules":{"mode":"local","localIdentName":"[local]_[hash:base64:5]"}}},{"loader":"postcss-loader","options":{"ident":"postcss"}}]},{"test":{},"include":["/Users/haowu/Projects/nickname-modify/src"],"use":[{"loader":"/Users/haowu/Projects/unitool/node_modules/mini-css-extract-plugin/dist/loader.js","options":{"publicPath":"../"}},{"loader":"css-loader","options":{"importLoaders":3,"esModule":true,"modules":{"mode":"local","namedExport":true,"localIdentName":"[local]_[hash:base64:5]"}}},{"loader":"postcss-loader","options":{"ident":"postcss"}},{"loader":"less-loader","options":{"lessOptions":{"javascriptEnabled":true}}}]},{"test":{},"exclude":["/Users/haowu/Projects/nickname-modify/src"],"use":[{"loader":"/Users/haowu/Projects/unitool/node_modules/mini-css-extract-plugin/dist/loader.js","options":{"publicPath":"../"}},{"loader":"css-loader","options":{"importLoaders":1,"modules":false}},{"loader":"postcss-loader","options":{"ident":"postcss"}}]},{"test":{},"exclude":["/Users/haowu/Projects/nickname-modify/src"],"use":[{"loader":"/Users/haowu/Projects/unitool/node_modules/mini-css-extract-plugin/dist/loader.js","options":{"publicPath":"../"}},{"loader":"css-loader","options":{"importLoaders":3,"modules":false}},{"loader":"postcss-loader","options":{"ident":"postcss"}},{"loader":"less-loader","options":{"lessOptions":{"javascriptEnabled":true}}}]}]},"plugins":[{"definitions":{"process.env":{"NODE_ENV":"\"production\"","PLATFORM":"\"native\""}}},{"appPath":"/Users/haowu/Projects/nickname-modify/src"},{"userOptions":{"filename":"./demo.html","template":"./public/index.native.html","title":"demo","inject":true,"chunks":["demo","common"],"minify":{"removeComments":true,"collapseWhitespace":true,"removeAttributeQuotes":true},"chunksSortMode":"auto"},"version":5},{"userOptions":{"filename":"./demo2.html","template":"./public/index.native.html","title":"demo2","inject":true,"chunks":["demo2","common"],"minify":{"removeComments":true,"collapseWhitespace":true,"removeAttributeQuotes":true},"chunksSortMode":"auto"},"version":5},{"userOptions":{"filename":"./link-demo.html","template":"./public/index.native.html","title":"link-demo","inject":true,"chunks":["link-demo","common"],"minify":{"removeComments":true,"collapseWhitespace":true,"removeAttributeQuotes":true},"chunksSortMode":"auto"},"version":5},{"userOptions":{"filename":"./tutorial.html","template":"./public/index.native.html","title":"tutorial","inject":true,"chunks":["tutorial","common"],"minify":{"removeComments":true,"collapseWhitespace":true,"removeAttributeQuotes":true},"chunksSortMode":"auto"},"version":5},{"patterns":[{"from":"/Users/haowu/Projects/nickname-modify/public","to":"/Users/haowu/Projects/nickname-modify/web_cft/nickname-modify","toType":"dir","globOptions":{"ignore":["**/*.html"],"gitignore":true}}],"options":{}},{"dangerouslyAllowCleanPatternsOutsideProject":false,"dry":false,"verbose":false,"cleanStaleWebpackAssets":true,"protectWebpackAssets":true,"cleanAfterEveryBuildPatterns":[],"cleanOnceBeforeBuildPatterns":["**/*"],"currentAssets":[],"initialClean":false,"outputPath":""},{"_sortedModulesCache":{},"options":{"filename":"css/[name].css","ignoreOrder":false,"runtime":true,"chunkFilename":"css/[name].css"},"runtimeOptions":{"linkType":"text/css"}}],"entry":{"demo":["/Users/haowu/Projects/nickname-modify/src/projects/demo/index.tsx"],"demo2":["/Users/haowu/Projects/nickname-modify/src/projects/demo2/index.tsx"],"link-demo":["/Users/haowu/Projects/nickname-modify/src/projects/link-demo/index.tsx"],"tutorial":["/Users/haowu/Projects/nickname-modify/src/projects/tutorial/index.tsx"]}}

my package.json

"dependencies": {
    "@babel/core": "^7.20.12",
    "@babel/preset-env": "^7.18.2",
    "@babel/preset-react": "^7.17.12",
    "@babel/preset-typescript": "^7.17.12",
    "@babel/plugin-proposal-optional-chaining": "^7.18.9",
    "babel-plugin-named-asset-import": "^0.3.7",
    "babel-preset-react-app": "10.0.0",
    "react-refresh": "^0.14.0",
    "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
    "@ht/bundle-analyzer-webpack-plugin": "^1.0.6",
    "@ht/html-webpack-append-commit-id": "^1.0.9",
    "@ht/webpack-plugin-concat-jobid": "^2.0.3",
    "@ht/webpack-plugin-rajax-engine": "^0.1.12",
    "babel-loader": "^8.2.2",
    "chalk": "^4.1.2",
    "clean-webpack-plugin": "^4.0.0",
    "compare-versions": "^4.1.3",
    "copy-webpack-plugin": "^11.0.0",
    "css-loader": "^6.7.3",
    "file-loader": "^6.2.0",
    "fs-extra": "^11.1.0",
    "html-webpack-plugin": "^5.5.0",
    "less": "^4.1.3",
    "less-loader": "^11.1.0",
    "mini-css-extract-plugin": "^2.7.2",
    "minimist": "^1.2.7",
    "optimize-css-assets-webpack-plugin": "^6.0.1",
    "css-minimizer-webpack-plugin": "^4.0.0",
    "postcss": "^8.2.6",
    "postcss-loader": "^7.0.2",
    "autoprefixer": "^10.4.7",
    "postcss-pxtorem": "^4.0.1",
    "postcss-flexbugs-fixes": "4.2.1",
    "postcss-preset-env": "6.7.0",
    "postcss-normalize": "8.0.1",
    "react-dev-utils": "^11.0.3",
    "thread-loader": "^3.0.4",
    "style-loader": "^3.3.1",
    "terser-webpack-plugin": "^5.3.6",
    "ts-loader": "^9.4.2",
    "typescript": "^4.9.5",
    "url-loader": "^4.1.1",
    "vue": "^2.7.14",
    "vue-loader": "^15.10.1",
    "vue-template-compiler": "^2.7.14",
    "webpack": "^5.75.0",
    "webpack-bundle-analyzer": "^4.7.0",
    "webpack-chain": "^6.5.1",
    "webpack-cli": "^5.0.1",
    "webpack-dev-server": "^4.11.1",
    "webpack-merge": "^5.8.0"
  },

Please paste the results of npx webpack-cli info here, and mention other relevant information

System: OS: macOS 12.6 CPU: (6) x64 Intel(R) Core(TM) i5-9600K CPU @ 3.70GHz Memory: 5.80 GB / 48.00 GB Binaries: Node: 16.16.0 - ~/.nvm/versions/node/v16.16.0/bin/node Yarn: 1.22.19 - ~/.nvm/versions/node/v16.16.0/bin/yarn npm: 8.11.0 - ~/.nvm/versions/node/v16.16.0/bin/npm Browsers: Chrome: 110.0.5481.177 Firefox Developer Edition: 106.0 Safari: 16.0 Packages: html-webpack-concat-plugin: ^3.0.2 => 3.0.2 less-loader: ^7.3.0 => 7.3.0

despicable123 commented 1 year ago

I notice that it is "em.bottom" when using style-loader screen_shot_1677825761989

It is "e.default.bottom" using mini-css-extract-plugin screen_shot_1677825982285

despicable123 commented 1 year ago

screen_shot_1678066986598 it works