Closed despicable123 closed 1 year ago
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,
but MiniCssExtractPlugin.loader produces undefined (production)
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" },
npx webpack-cli info
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
I notice that it is "em.bottom" when using style-loader
It is "e.default.bottom" using mini-css-extract-plugin
it works
Bug report
When using css module, e.g.
If the less file is empty, print "styles" style-loader produces empty object,
but MiniCssExtractPlugin.loader produces undefined (production)
here is my base.js
here is my build.js
my webpack config result
my package.json
Please paste the results of
npx webpack-cli info
here, and mention other relevant informationSystem: 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