I am using Vue with Webpack (without Vue CLI) and I ran into an issue.
I am building a Design System library, in which I would like to have async chunks so they get small.
When I import as usual the component in the index.ts of the lib
//index.ts
import RedBox from '#/components/RedBox.vue';
export { RedBox };
everything works perfectly.
But when I try to do
const RedBox = () => import('#/components/RedBox.vue');
export { RedBox };
i get
The file is actually there in the bundle.
Here is my webpack config
/* eslint-disable #typescript-eslint/no-var-requires */
const path = require('path');
const { VueLoaderPlugin } = require('vue-loader');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = (env) => {
const plugins = [
new VueLoaderPlugin(),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: 'assets/css/[name].css',
chunkFilename: 'assets/css/[name].css',
}),
];
if (env.ANALYZE_BUNDLE) { plugins.push(new BundleAnalyzerPlugin()); }
return {
entry: './src/index.ts',
mode: 'production',
devtool: 'source-map',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'design-system.js',
library: {
name: 'design-system',
type: 'umd',
},
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
},
{
test: /\.ts?$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
transpileOnly: true,
},
},
{
test: /\.(sa|sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {},
},
'css-loader',
'postcss-loader',
{
loader: 'sass-loader',
options: {},
},
],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
],
},
plugins,
resolve: {
alias: {
'#': path.resolve(__dirname, 'src'),
},
extensions: [
'.vue',
'.ts',
'.js',
'.css',
'.ttf',
'.otf',
],
},
externals: {
vue: 'Vue',
},
};
};
Related
I have this structure:
https://i.stack.imgur.com/oj2KH.png\
And this webpack configs:
base:
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');
const PATHS = {
src: path.join(__dirname, '../src'),
dist: path.join(__dirname, '../dist'),
assets: 'assets/'
}
const PostcssLoaderConfig = {
loader: 'postcss-loader',
options: { sourceMap: true, postcssOptions: { config: `${PATHS.src}/assets/js/configs/postcss.config.js` } }
}
module.exports = {
externals: {
paths: PATHS,
plugins: {
miniCssExtract: MiniCssExtractPlugin
},
configs: {
postCssLoader: PostcssLoaderConfig,
}
},
entry: {
app: PATHS.src
},
output: {
path: PATHS.dist,
filename: `${PATHS.assets}js/[name].js`,
publicPath: '/'
},
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
name: 'vendors',
test: /node_modules/,
chunks: 'all',
enforce: true
}
}
}
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: `${PATHS.assets}css/[name].css`
}),
new HtmlWebpackPlugin({
hash: false,
template: `${PATHS.src}/index.html`,
filename: './index.html'
}),
new CopyWebpackPlugin({
patterns: [
{ from: `${PATHS.src}/assets/img/`, to: `${PATHS.assets}img` },
{ from: `${PATHS.src}/assets/fonts/`, to: `${PATHS.assets}fonts` },
{ from: `${PATHS.src}/static/`, to: `` }
]
})
],
module: {
rules: [
{
test: /\.js$/,
use: { loader: 'babel-loader', options: { presets: ['#babel/preset-env'] } }
},
{
test: /\.((c|sa|sc)ss)$/i,
use: [MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: true } },
PostcssLoaderConfig, { loader: 'sass-loader', options: { sourceMap: true } } ]
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loader: {
scss: 'vue-style-loader!css-loader!sass-loader'
}
}
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]'
}
},
{
test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
loader: 'file-loader',
options: {
name: '[name].[ext]'
}
},
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.js'
}
}
};
dev:
const webpack = require('webpack');
const webpackServer = require('webpack-dev-server');
const { merge } = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
const devWebpackConfig = merge(baseWebpackConfig, {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
devServer: {
contentBase: baseWebpackConfig.externals.paths.dist,
port: 8081,
overlay: true
},
plugins: [
new webpack.SourceMapDevToolPlugin({
filename: '[file].map'
})
]
})
module.exports = new Promise((resolve, reject) => {
resolve(devWebpackConfig)
})
build:
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
const buildWebpackConfig = merge(baseWebpackConfig, {
mode: 'production',
plugins: []
})
module.exports = new Promise((resolve, reject) => {
resolve(buildWebpackConfig)
})
I have no issues with dev config, it works fine, but build version looks like:
https://i.imgur.com/IS1t1Fk.png
HTML:
https://i.imgur.com/NZ0CBAW.png
Console:
https://i.imgur.com/5TyeHax.png
Also, I noticed that some files used in SCSS are in the dist root, although they are not needed there (since the assets folder copied)
What can I do to make the build work properly?
Config was created for Webpack 4, but Webpack 5 was installed.
I'm currently using TS + React to make a simple application with some API requests to a server. When I try to use io-ts to decode the response, webpack responds with Module not found: Error: Can't resolve '../shared/Response' - if I remove the usage of io-ts to decode the response, I don't get that error.
My folder structure is
src
client
PlayerTimer.tsx
<skipped>
server
<skipped>
shared
Phase.d.ts
Response.d.ts
Phase.d.ts contains the following:
import * as t from 'io-ts';
export const PhaseDecode = t.union([
t.literal(1),
t.literal(2),
t.literal(3),
t.literal(4),
t.literal(5),
]);
export type Phase = t.TypeOf<typeof PhaseDecode>
Response.d.ts contains the following:
import * as t from 'io-ts';
import { DateFromISOString as IoDate } from 'io-ts-types/DateFromISOString';
import { PhaseDecode } from './Phase';
const ApiResponseDecode = t.type({
turnNumber: t.number,
phase: PhaseDecode,
breakingNews: t.union([t.string, t.null]),
active: t.boolean,
phaseEnd: IoDate
});
type ApiResponse = t.TypeOf<typeof ApiResponseDecode>
export { ApiResponseDecode, ApiResponse as default };
PlayerTimer.tsx contains a bunch of React components, but this is reproducible with just the following code at the top
import { ApiResponseDecode } from '../shared/Response';
const temp = {};
if (ApiResponseDecode.is(temp)) {
console.log('Webpack fails');
}
My webpack config is:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const outputDirectory = 'dist';
module.exports = {
entry: ['babel-polyfill', './src/client/index.tsx'],
output: {
path: path.join(__dirname, outputDirectory),
filename: './js/[name].bundle.js'
},
devtool: 'source-map',
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
},
{
enforce: 'pre',
test: /\.js$/,
loader: 'source-map-loader'
},
{
test: /\.less$/,
use: [
{ loader: 'style-loader' },
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: './Less',
hmr: process.env.NODE_ENV === 'development',
},
},
{ loader: 'css-loader' },
{
loader: 'less-loader',
options: {
strictMath: true,
noIeCompat: true,
}
},
]
},
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
],
},
{
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000'
},
]
},
resolve: {
extensions: ['*', '.ts', '.tsx', '.js', '.jsx', '.json', '.less']
},
devServer: {
port: 3000,
open: true,
hot: true,
historyApiFallback: true,
proxy: {
'/api/**': {
target: 'http://localhost:8050',
secure: false,
changeOrigin: true
}
}
},
plugins: [
new CleanWebpackPlugin([outputDirectory]),
new HtmlWebpackPlugin({
template: './public/index.html',
favicon: './public/favicon.ico',
title: 'Test application',
}),
new MiniCssExtractPlugin({
filename: './css/[name].css',
chunkFilename: './css/[id].css',
}),
new CopyPlugin([
{ from: './src/client/Assets', to: 'assets' },
])
],
};
I fixed this by moving the type definitions and the io-ts definitions into separate files. I don't really understand why that works but I'll add this incase someone else finds this
Im trying to use url-loader to convert file URLs into inline data URLs for my CSS backgrounds. When I run the build command, it generates the CSS & JS files without any error but the background image URLs remain unchanged in the CSS output.
Here's my webpack config:
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const ManifestPlugin = require('webpack-manifest-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = (env, argv) => ({
entry: {
app: ['./app/Resources/js/app.js', './app/Resources/scss/app.scss']
},
output: {
path: path.join(__dirname, "/web/build"),
filename: argv.mode === 'production' ? '[name].[hash].js' : '[name].js'
},
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: false // set to true if you want JS source maps
}),
new OptimizeCSSAssetsPlugin({
cssProcessorPluginOptions: {
preset: ['default', { discardComments: { removeAll: true } }],
},
})
]
},
devServer: {
contentBase: './web',
port: 9000,
disableHostCheck: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
},
resolve: {
extensions: ['.js']
}
},
{
test: /\.scss$/,
resolve: {
extensions: ['.scss']
},
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: "css-loader"
},
{
loader: "sass-loader",
options: {
sassOptions: {
outputStyle: 'expanded',
includePaths: ['./node_modules/foundation-sites/scss']
}
}
}
]
},
{
test: /\.(gif|png|jpe?g|svg)$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 8192
},
}
],
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: argv.mode === 'production' ? '[name].[contenthash].css' : '[name].css'
}),
new ManifestPlugin(),
new CleanWebpackPlugin()
],
});
My CSS is just a standard SCSS file:
.icon { background:url('path/to/icon.svg'); }
In the final output, I'd expect path/to/icon.svg to be replaced by a data URL. What am I doing wrong?
I have posted my Webpack configs below for a production environment. I am attempting to use background-image: url(../img/chevron-thin-right.svg); in one of my SCSS files but it is being resolved to background-image: url([object Module]) and therefore not working. I am trying to port a purely SCSS and HTML project into a react app being bundled by Webpack so this above approach used to work. Any fixes would be greatly appreciated.
webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const outputDirectory = 'dist';
module.exports = {
entry: './src/client/index.js',
output: {
path: path.join(__dirname, outputDirectory),
filename: 'bundle.js'
},
plugins: [
new HtmlWebpackPlugin({
title: 'Production',
template: './public/index.html',
favicon: './public/favicon.ico',
hash: true,
filename: 'index.html'
})
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.(jpg|png|woff|woff2|eot|ttf)$/,
use: {
loader: 'file-loader?limit=100000&name=images/[name].[ext]'
}
},
{
test: /\.svg$/,
use: [
"babel-loader",
{
loader: "react-svg-loader",
options: {
svgo: {
plugins: [
{
removeTitle: true,
removeComments: true
}
],
floatPrecision: 2
}
}
}
]
}
]
}
};
webpack.prod.js
const merge = require('webpack-merge');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const common = require('./webpack.common.js');
const path = require('path');
const autoprefixer = require('autoprefixer');
module.exports = {};
module.exports = merge(common, {
mode: 'production',
optimization: {
splitChunks: {
cacheGroups: {
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true
}
}
}
},
plugins: [
new CleanWebpackPlugin(['dist']),
new MiniCssExtractPlugin({
filename: '[name].css'
})
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
root: path.resolve(__dirname),
},
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
autoprefixer({
'browsers': ['last 2 versions', 'ie >= 10'],
}),
],
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: {
outputStyle: 'compressed',
sourceMap: true,
includePaths: [
'./src/client/style/scss',
],
},
}
]
}
]
}
});
In case anybody else is looking for an answer I was able to solve it. It was due to not loading the associated SVG through the url-loader. Another problem arose when adding svg back in because my inline SVGs I wanted to use as React components were running into errors as they were now going through the url-loader instead of my react-svg-loader.
Below is the working webpack.common.js and webpack.prod.js stayed the same. The solution was to differentiate my inline and external SVGs by turning all inline extensions from .svg to .inline.svg and adjusting webpack config accordingly
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const outputDirectory = 'dist';
module.exports = {
entry: './src/client/index.js',
output: {
path: path.join(__dirname, outputDirectory),
filename: 'bundle.js'
},
plugins: [
new HtmlWebpackPlugin({
title: 'Production',
template: './public/index.html',
favicon: './public/favicon.ico',
hash: true,
filename: 'index.html'
})
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.(jpg|png|woff|woff2|eot|ttf|svg)$/,
exclude: /\.inline.svg$/,
use: {
loader: 'url-loader?limit=1000&name=images/[name].[ext]'
}
},
{
test: /\.inline.svg$/,
use: [
"babel-loader",
{
loader: "react-svg-loader",
options: {
svgo: {
plugins: [
{
removeTitle: true,
removeComments: true
}
],
floatPrecision: 2
}
}
}
]
}
]
}
};
I've recently tried to add proper code splitting for the first time to one of my projects.
It's worked, however I think it's worked a little TOO well!
When I run bundle-analyser, my dist folder contains a bunch of other bundles that contain snapshots and test files and I can't find any information that would explain why this is happening!
here is my webpack.config.base.js
const path = require('path');
const postcssNested = require('postcss-nested');
const postcssSimpleVars = require('postcss-simple-vars');
const postcssPresetEnv = require('postcss-preset-env');
const postcssImport = require('postcss-import');
const postcssFor = require('postcss-for');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractCssPluginConfig = new ExtractTextPlugin({
filename: '[name].[hash].css',
disable: false,
});
const HtmlWebpackPluginConfig = new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
inject: 'body',
});
module.exports = {
entry: {
main: './src/index.jsx',
},
output: {
path: path.resolve('_dist'),
chunkFilename: '[name].[hash].bundle.min.js',
filename: '[name].[hash].min.js',
publicPath: '/',
},
module: {
rules: [
{
test: /\.(js|jsx)?$/,
loader: 'babel-loader',
exclude: /node_modules\/(?!(dom7|ssr-window|swiper)\/).*/,
},
{
test: /\.css$/,
loader: 'style-loader',
},
{
test: /\.css$/,
loader: 'css-loader',
query: {
modules: true,
importLoaders: 1,
localIdentName: '[path]___[name]__[local]___[hash:base64:5]',
},
},
{
test: /\.css$/,
loader: 'postcss-loader',
options: {
plugins: () => [
postcssNested,
postcssImport,
postcssFor,
postcssPresetEnv({
browsers: '>1%',
autoprefixer: {
grid: true,
browsers: ['>1%'],
},
insertBefore: {
'all-property': postcssSimpleVars,
},
}),
],
},
},
{
test: /\.(png|jp(e*)g|svg)$/,
use: [{
loader: 'url-loader',
options: {
limit: 8000,
name: 'images/[hash]-[name].[ext]',
},
}],
},
{
test: /\.(woff|woff2|ttf|eot|otf)$/,
use: 'file-loader?name=fonts/[name].[ext]',
},
{
test: /\.(pdf)$/,
use: [{
loader: 'url-loader',
options: { limit: 8000, name: 'documents/[hash]-[name].[ext]' },
}],
},
],
},
resolve: {
extensions: ['.js', '.jsx', '.css', '.svg'],
modules: [path.resolve('./node_modules')],
alias: {
Pages: path.resolve(__dirname, 'src/pages/'),
Sections: path.resolve(__dirname, 'src/sections/'),
Components: path.resolve(__dirname, 'src/components/'),
Images: path.resolve(__dirname, 'src/images/'),
Downloads: path.resolve(__dirname, 'src/downloads/'),
Services: path.resolve(__dirname, 'src/services/'),
Enum: path.resolve(__dirname, 'src/enum/'),
Data: path.resolve(__dirname, 'src/data/'),
Utils: path.resolve(__dirname, 'src/utils/'),
Config: path.resolve(__dirname, 'src/config/'),
},
},
plugins: [
ExtractCssPluginConfig,
HtmlWebpackPluginConfig,
],
};
and here is my webpack.config.build.js
const webpack = require('webpack');
const merge = require('webpack-merge');
const WebpackCleanupPlugin = require('webpack-cleanup-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const baseConfig = require('./webpack.config.base');
const WebpackCleanupPluginConfig = new WebpackCleanupPlugin({
exclude: ['webpack.stats.json'],
quiet: true,
});
const CopyWebpackPluginConfig = new CopyWebpackPlugin([
{ from: './src/documents', to: './documents' },
]);
module.exports = () => {
const DefinePluginConfig = new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production'),
});
return (
merge(baseConfig, {
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'initial',
enforce: true,
},
},
},
noEmitOnErrors: true,
concatenateModules: true,
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: false,
}),
],
},
plugins: [
DefinePluginConfig,
WebpackCleanupPluginConfig,
CopyWebpackPluginConfig,
],
})
);
};
I've tried adding a regex that matches any files with .test.js/jsx and .snap to my module.rules object but that did nothing.
Any help would be greatly appreciated!
If your test files are in a different directory, you might want to consider adding exclude property within the modules rules for js files.
Refer the following document: https://webpack.js.org/configuration/module/?_sm_au_=iVVKrMW7ZsbHQPbq#rule-exclude
or the following example: https://webpack.js.org/configuration/?_sm_au_=iVVKrMW7ZsbHQPbq#options
Another solution would be to use the IgnorePlugin. Docs for IgnorePlugin here: https://webpack.js.org/plugins/ignore-plugin/?_sm_au_=iVVKrMW7ZsbHQPbq