I'm trying to import jsx files without using the file extensions, like import LandingPage from './components/LandingPage'; and as long as the original file is named LandingPage.js this works fine, but if I rename it to LandingPage.jsx it doesn't work anymore. I already added resolve.extensions: ['.js', '.jsx'] to my webpack config file and nothing changed. If I try to import a jsx file, I get this error:
ERROR in ./src/App.js
Module not found: Error: Can't resolve './components/LandingPage' in 'C:\Users\myself\Documents\Projects\react-app-starter\src'
webpack.config.js:
/* eslint-disable import/no-extraneous-dependencies */
const path = require('path');
const BrotliPlugin = require('brotli-webpack-plugin');
const Dotenv = require('dotenv-webpack');
const HtmlWebPackPlugin = require('html-webpack-plugin');
require('dotenv-defaults').config({
path: `${__dirname}/.env`,
encoding: 'utf8',
defaults: `${__dirname}/.env.sample`,
});
const commonConfig = {
entry: './src/index.js',
resolve: {
extensions: ['.js', '.jsx'],
},
module: {
rules: [
{
test: /\.(js|jsx)?$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.html$/,
exclude: /template\.html$/,
use: {
loader: 'html-loader',
options: {
minimize: true,
removeComments: false,
collapseWhitespace: true,
},
},
},
{
test: /\.(png|jpg|gif|ico)$/,
use: 'file-loader',
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader'],
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(woff|woff2|ttf|eot|svg|otf)(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/',
},
},
},
{
test: /\.geojson$/,
loader: 'json-loader',
},
],
},
plugins: [
new Dotenv({
defaults: `${__dirname}/.env.sample`,
path: `${__dirname}/.env`,
}),
new HtmlWebPackPlugin({
template: './src/template.html',
title: process.env.APP_TITLE,
filename: 'index.html',
favicon: './src/favicon.ico',
}),
],
};
if (process.env.BABEL_USE_MATERIAL_UI_ES_MODULES) {
commonConfig.resolve = {
alias: {
'#material-ui/core': '#material-ui/core/es',
},
};
}
module.exports = (env, argv = { mode: 'development' }) => {
switch (argv.mode) {
default:
case 'development': {
return {
...commonConfig,
devServer: {
compress: process.env.WEBPACK_DEV_SERVER_COMPRESS === 'true',
host: process.env.WEBPACK_DEV_SERVER_HOST,
open: process.env.WEBPACK_DEV_SERVER_OPEN === 'true',
port: process.env.WEBPACK_DEV_SERVER_PORT,
historyApiFallback: true,
},
devtool: 'eval-source-map',
};
}
case 'production': {
return {
...commonConfig,
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].[contenthash].bundle.js',
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
plugins: [
...commonConfig.plugins,
new BrotliPlugin({
asset: '[path].br[query]',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8,
}),
],
};
}
}
};
babel.config.js:
module.exports = (api) => {
api.cache(false);
const conditionalPresets = [];
const presets = [
'#babel/preset-react',
'#babel/preset-env',
...conditionalPresets,
];
const conditionalPlugins = (process.env.BABEL_USE_MATERIAL_UI_ES_MODULES === 'true') ?
[
[
'import', {
libraryName: '#material-ui/icons',
libraryDirectory: '',
camel2DashComponentName: false,
},
],
]
: [
[
'import',
{
libraryName: '#material-ui/core',
libraryDirectory: '',
camel2DashComponentName: false,
},
'#material-ui/core',
],
[
'import',
{
libraryName: '#material-ui/core/colors',
libraryDirectory: '',
camel2DashComponentName: false,
},
'#material-ui/core/colors',
],
[
'import',
{
libraryName: '#material-ui/core/styles',
libraryDirectory: '',
camel2DashComponentName: false,
},
'#material-ui/core/styles',
],
[
'import',
{
libraryName: '#material-ui/icons',
libraryDirectory: '',
camel2DashComponentName: false,
},
'#material-ui/icons',
],
];
const plugins = [
'#babel/proposal-class-properties',
'#babel/syntax-dynamic-import',
'#babel/transform-runtime',
'#babel/plugin-transform-react-jsx',
...conditionalPlugins,
];
return {
presets,
plugins,
};
};
My build script: "dev": "webpack-dev-server --mode development --progress"
Related
Recently we have updated webpack to v4 and soon noticed unexpected errors from webpack during development, which disappears after rebuild of entire bundle.
Our application build using lazy loading and code splitting, which can cause the issue, though I couldn't find anything related to this in the official documentations.
Here the error we get
react_devtools_backend.js:2273 TypeError: Cannot read property 'call' of undefined
our webpack webpack.config.js file.
const webpack = require('webpack');
const path = require('path');
const glob = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const AssetsPlugin = require('assets-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
// minification plugins
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// image optimization plugins
const ImageminPlugin = require("imagemin-webpack-plugin").default;
const imageminGifsicle = require("imagemin-gifsicle");
const imageminPngquant = require("imagemin-pngquant");
const imageminSvgo = require("imagemin-svgo");
const imageminMozjpeg = require('imagemin-mozjpeg');
const env = require('dotenv').config();
const isProd = process.env.NODE_ENV === 'production';
const isDev = !isProd;
const environment = {
NODE_ENV: process.env.NODE_ENV || 'development',
CONFIG: process.env.CONFIG || 'development',
DEBUG: process.env.DEBUG || false,
};
const plugins = () => {
let plugins = [
new CleanWebpackPlugin(['build', 'cachedImages'], {
root: path.resolve(__dirname, '../dist'),
verbose: true,
dry: false,
}),
new webpack.EnvironmentPlugin(Object.assign(environment, env.parsed)),
new MiniCssExtractPlugin('[chunkhash:5].[name].[hash].css'),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
moment: 'moment',
_: 'lodash',
}),
new webpack.ContextReplacementPlugin(/moment[\\\/]locale$/, /^\.\/(en)$/),
new AssetsPlugin({
filename: 'assets.json',
}),
new ImageminPlugin({
cacheFolder: isDev ? path.resolve(__dirname, '../dist/cachedImages') : null,
externalImages: {
context: 'src',
sources: glob.sync('src/assets/img/**/*.*'),
destination: 'dist/img/',
fileName: (filepath) => {
let name = filepath.split(/img(\/|\\)/).pop();
return name;
},
},
plugins: [
imageminGifsicle({
interlaced: false
}),
imageminMozjpeg({
progressive: true,
arithmetic: false
}),
imageminPngquant({
floyd: 0.5,
speed: 2
}),
imageminSvgo({
plugins: [
{ removeTitle: true },
{ convertPathData: false }
]
}),
],
}),
];
if (isProd) {
plugins.push(
new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: path.resolve(__dirname, 'analysis.html'),
generateStatsFile: false,
logLevel: 'info',
})
);
}
return plugins;
};
const optimization = () => {
let optimizations = {
concatenateModules: true,
splitChunks: {
cacheGroups: {
vendor: {
name: 'vendor',
test: /[\\/]node_modules[\\/](react|react-dom|lodash|moment)[\\/]/,
chunks: 'all',
},
commons: {
chunks: 'async',
}
}
}
};
if (isProd) {
optimizations.minimizer = [
new TerserJSPlugin({
terserOptions: {
compress: {
pure_funcs: ['console.log'],
drop_console: true,
drop_debugger: true
},
warnings: false
},
parallel: true
}),
new OptimizeCSSAssetsPlugin({})
];
}
return optimizations;
};
const fontLoaders = [
{
test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url?limit=10000&mimetype=application/font-woff',
}
},
{
test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url?limit=10000&mimetype=application/font-woff',
}
},
{
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url?limit=10000&mimetype=application/octet-stream',
}
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'url?limit=10000&mimetype=image/svg+xml',
}
},
{
test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
use: {
loader: 'file',
}
}
];
const config = {
mode: process.env.NODE_ENV,
devtool: isDev ? 'source-map' : '',
context: path.resolve(__dirname, '../src'),
entry: {
bundle: './app.jsx',
embed: './embed.jsx',
styles: './sass_new/main.scss',
vendor: [
'react',
'react-dom',
'redux',
'redux-saga',
'react-redux',
'react-router',
'react-tap-event-plugin',
'lodash',
'moment',
]
},
output: {
publicPath: '/build/',
filename: '[name].[hash].js',
chunkFilename: '[name].[hash].js',
path: path.resolve(__dirname, '../dist/build'),
},
resolve: {
extensions: ['.js', '.jsx', '.json'],
alias: {
config: path.resolve(__dirname, '../config.js'),
utils: path.resolve(__dirname, '../src/utils'),
shared: path.resolve(__dirname, '../src/components/shared'),
services: path.resolve(__dirname, '../src/services'),
store: path.resolve(__dirname, '../src/store'),
constants: path.resolve(__dirname, '../src/constants'),
actions: path.resolve(__dirname, '../src/actions'),
components: path.resolve(__dirname, '../src/components'),
},
},
optimization: optimization(),
plugins: plugins(),
module: {
rules: [
{
test: /\.jsx?$/,
exclude: [/node_modules/, /libs/],
use: {
loader: path.join(__dirname, '../helpers/custom-loader.js'),
options: {
presets: ['#babel/preset-env', '#babel/preset-react'],
plugins: [
'#babel/plugin-proposal-object-rest-spread',
'#babel/plugin-proposal-class-properties',
'#babel/plugin-transform-destructuring',
'#babel/plugin-syntax-dynamic-import',
'#babel/plugin-transform-runtime',
'syntax-async-functions'
]
}
}
},
{
test: /\.scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
'css-loader',
'sass-loader',
]
},
{
test: /\.css$/,
use: [
'css-loader'
]
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
loader: "url-loader",
options: {
name: "[path][name].[ext]"
},
},
...fontLoaders
]
},
watchOptions: {
ignored: /node_modules/,
},
};
module.exports = config;
I have no idea how to fix this, and it's taking a lot of time to rebuild entire app after getting this error.
I've recently developed a react + webpack application that is deployed using
AWS Amplify. I've been getting a strange error that is logged on Sentry, but can't
find a way to replicate the bug.
RuntimeError: memory access out of bounds
I suspect it has something to do with my webpack configuration, but I don't know whats wrong.
I never used wasm, but it seems to be related to it.
Here is my production level webpack configuration.
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const env = require('../environment/prod.env');
const commonPaths = require('./paths');
const webpack = require('webpack');
const SentryWebpackPlugin = require('#sentry/webpack-plugin');
module.exports = {
mode: 'production',
devtool: 'source-map',
output: {
filename: `${commonPaths.jsFolder}/[name].[hash].js`,
path: commonPaths.outputPath,
chunkFilename: `${commonPaths.jsFolder}/[name].[chunkhash].js`,
},
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
cache: true,
sourceMap: true,
}),
new OptimizeCSSAssetsPlugin(),
],
splitChunks: {
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'initial',
},
async: {
test: /[\\/]node_modules[\\/]/,
name: 'async',
chunks: 'async',
minChunks: 4,
},
},
},
runtimeChunk: true,
},
module: {
rules: [
{
test: /\.scss$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
},
},
],
},
{
test: /\.css$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
],
},
],
},
plugins: [
new webpack.DefinePlugin({
'process.env': env,
}),
new MiniCssExtractPlugin({
filename: `${commonPaths.cssFolder}/[name].css`,
chunkFilename: `${commonPaths.cssFolder}/[name].css`,
}),
],
};
Here is also my common webpack configuration
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
const commonPaths = require('./paths');
module.exports = {
context: commonPaths.srcPath,
entry: commonPaths.entryPath,
output: {
path: commonPaths.outputPath,
filename: 'js/[name].js',
},
resolve: {
extensions: ['.ts', '.js', '.html', '.vue'],
alias: {
'~': commonPaths.srcPath,
},
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
include: commonPaths.srcPath,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
plugins: ['react-hot-loader/babel'],
},
},
{
test: /\.(png|jpg|gif|svg)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'assets/img/[name].[hash:8].[ext]',
publicPath: '/',
},
},
],
},
{
test: /\.(mp3)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'assets/audio/[name].[hash:8].[ext]',
publicPath: '/',
},
},
],
},
{
test: /\.(ttc|ttf|eot)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: [
{
loader: 'file-loader',
options: {
name: 'assets/fonts/[name].[hash:8].[ext]',
publicPath: '/',
},
},
],
},
],
},
serve: {
content: commonPaths.entryPath,
dev: {
publicPath: commonPaths.outputPath,
},
open: true,
},
resolve: {
modules: ['src', 'node_modules', 'bower_components', 'shared', '/shared/vendor/modules'],
extensions: ['*', '.js', '.jsx'],
},
plugins: [
new webpack.ProgressPlugin(),
new HtmlWebpackPlugin({
favicon: './icon.png',
template: commonPaths.templatePath,
}),
new ScriptExtHtmlWebpackPlugin({
defaultAttribute: 'async',
}),
],
};
Any help would really help. Thanks.
Try to restart your server and make sure you reinstall node modules.
Please tell me, here is the config.js file in it, the exported variable with a reference to the API, but there is an API for the dev version, and there is a separate API for the prod version, as I understand it, you need to make 2 separate files, but how to make it in webpack so that when dev he used one file, and prod another?
Below added webpack configuration files, a project on react.js (if that makes any difference :) )
rules.js
const rules = [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: ['babel-loader'],
},
{
test: /\.eot(\?v=\d+.\d+.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
name: '[name].[ext]',
},
},
],
},
{
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/font-woff',
name: '[name].[ext]',
},
},
],
},
{
test: /\.[ot]tf(\?v=\d+.\d+.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'application/octet-stream',
name: '[name].[ext]',
},
},
],
},
{
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
mimetype: 'image/svg+xml',
name: '[name].[ext]',
},
},
],
},
{
test: /\.(jpe?g|png|gif|ico)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
],
},
];
export default rules;
webpack.config.common.babel.js
import path from 'path';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import Dotenv from 'dotenv-webpack';
import rules from './utils/rules';
const config = (env) => {
const PUBLIC_PATH = env.production ? '/' : '/';
return {
entry: {
index: './src/index.js'
},
output: {
publicPath: PUBLIC_PATH,
path: path.resolve(process.cwd(), 'dist'),
filename: env.production ? '[name].js' : '[name].bundle.js'
},
resolve: {
alias: {
'react-dom': '#hot-loader/react-dom',
app: path.resolve(process.cwd(), 'src/app/'),
store: path.resolve(process.cwd(), 'src/store/'),
ui: path.resolve(process.cwd(), 'src/ui/')
},
extensions: ['*', '.js', '.jsx', '.json', 'scss']
},
module: {
rules
},
node: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
},
plugins: [
new Dotenv({
systemvars: true
}),
new HtmlWebpackPlugin({
template: 'public/index.html',
favicon: 'public/favicon.ico',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
}
})
]
};
};
export default config;
webpack.config.dev.babel.js
import webpack from 'webpack';
import path from 'path';
import merge from 'webpack-merge';
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import common from './webpack.config.common.babel';
const config = env => merge(common(env), {
devtool: 'cheap-module-eval-source-map',
target: 'web',
mode: 'development',
optimization: {
minimize: false
},
performance: {
hints: false
},
devServer: {
historyApiFallback: true,
contentBase: '../dist',
port: 3009,
hot: true,
clientLogLevel: 'none'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader', 'eslint-loader']
},
{
test: /(\.css|\.scss|\.sass)$/,
use: [
{
loader: 'style-loader',
options: { singleton: true }
},
{
loader: 'css-loader'
},
{
loader: 'postcss-loader',
options: {
/* eslint-disable global-require */
plugins: () => [require('autoprefixer')],
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
includePaths: [path.resolve(__dirname, 'src', 'scss')]
}
}
]
}
]
},
plugins: [
new webpack.NamedModulesPlugin(),
// new BundleAnalyzerPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
new webpack.HotModuleReplacementPlugin()
]
});
export default config;
webpack.config.common.babel.js
import path from 'path';
import webpack from 'webpack';
import merge from 'webpack-merge';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import common from './webpack.config.common.babel';
const config = env => merge(common(env), {
devtool: 'source-map',
target: 'web',
mode: 'production',
optimization: {
minimize: true,
nodeEnv: 'production',
sideEffects: true,
concatenateModules: true,
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
module: {
rules: [
{
test: /(\.css|\.scss|\.sass)$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'postcss-loader',
options: {
/* eslint-disable global-require */
plugins: () => [require('cssnano'), require('autoprefixer')],
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: {
includePaths: [path.resolve(__dirname, 'src', 'scss')],
sourceMap: true,
},
},
],
},
],
},
plugins: [
new webpack.HashedModuleIdsPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[chunkhash].css',
})
],
});
export default config;
You can define two different npm scripts and use the --config flag for custom files (/file-paths):
"build:dev": "webpack --config ./webpack.client.dev.js",
"build:prod": "webpack --config ./webpack.client.prod.js"
For the common you have to use webpack-merge, for example:
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
...
});
The Webpack Bulid runs pretty slow, project background is a community portal developed in Vue.js...
Can anybody tell if there is any potential for improvement and if so, what?
I wonder if the time for the build process of 37401ms can still be changed by changing the codebase?
const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const glob = require('glob');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const autoprefixer = require('autoprefixer');
const statsSettings = {
all: false,
modules: true,
maxModules: 0,
errors: true,
warnings: false,
moduleTrace: true,
errorDetails: true,
timings: true,
performance: true,
builtAt: true,
};
const {
rootDir,
srcDir,
assetsDir,
stylesDir,
buildDir,
sitepackageDir,
publicPath,
} = require('./config');
const chunks = glob.sync(path.join(rootDir, srcDir, 'pages/**/index.js'))
.reduce((obj, file) => {
const name = path.basename(path.dirname(file));
return {
...obj,
[name]: file,
};
}, {});
module.exports = env => {
return {
mode: env.production ? 'production' : 'development',
context: path.join(rootDir, srcDir),
entry: chunks,
output: {
path: path.join(rootDir, buildDir),
filename: '[name].js',
publicPath: env.production ? publicPath : '',
},
devtool: env.production ? false : 'cheap-module-eval-source-map',
devServer: {
contentBase: path.join(rootDir, buildDir),
inline: true,
proxy: {
'/api/v0': 'http://localhost:4000',
},
},
watchOptions: {
ignored: env.watch ? 'node_modules' : '',
aggregateTimeout: 300,
},
stats: env.watch ? statsSettings : 'normal',
module: {
rules: [
{
test: /\.vue$/,
include: [
path.join(rootDir, srcDir),
require.resolve('bootstrap-vue'),
],
loader: 'vue-loader',
},
{
test: /\.js$/,
include: [
path.join(rootDir, srcDir),
require.resolve('bootstrap-vue'),
],
loader: 'babel-loader',
},
{
test: /\.(css|scss)$/,
use: [
env.production ? MiniCssExtractPlugin.loader : 'vue-style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: [
autoprefixer({
browsers: ['>1%', 'last 2 versions', 'not ie < 11'],
}),
],
},
},
{
loader: 'sass-loader',
options: {
includePaths: [
path.join(rootDir, srcDir, stylesDir),
],
},
},
],
},
{
test: /\.(jpg|jpeg|png|gif|webp|svg|eot|otf|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$/,
loader: 'file-loader',
options: {
name: `${assetsDir}/_processed_/[name].[hash:4].[ext]`,
},
},
],
},
optimization: {
runtimeChunk: {
name: '_runtime',
},
splitChunks: {
cacheGroups: {
// we need to figure out if it's worth having a common chunk
// or if each entry chunk should be somewhat self-contained
common: {
chunks: 'initial',
name: '_common',
minChunks: 2,
minSize: 0,
},
vendor: {
test: /node_modules/,
chunks: 'initial',
name: '_vendor',
enforce: true,
},
},
},
},
resolve: {
extensions: ['.js', '.json', '.vue'],
alias: {
'#app': path.join(rootDir, srcDir),
// although we're using single file components that can be pre-compiled,
// we want to dynamically mounting them in the DOM html.
// this is way we need to alias 'vue' to use the runtime + compiler build here.
// see: https://vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only
vue$: 'vue/dist/vue.esm.js',
},
},
plugins: [
new webpack.DefinePlugin({
PAGES: JSON.stringify(Object.keys(chunks)),
}),
new VueLoaderPlugin(),
...plugHtmlTemplates(),
...plugExtractCss(env),
...plugCopyAssets(),
],
};
};
function plugHtmlTemplates () {
return glob.sync(path.join(rootDir, srcDir, 'pages/**/template.html'))
.map(template => {
const name = path.basename(path.dirname(template));
return {
template,
filename: `${name}.html`,
chunks: ['_runtime', '_vendor', '_styles', '_common', name],
};
})
.map(htmlConfig => new HtmlWebpackPlugin(htmlConfig));
}
function plugExtractCss (env) {
if (!env.production) return [];
return [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[name].css',
}),
];
}
function plugCopyAssets () {
const assetsSrcPath = path.join(rootDir, srcDir, assetsDir);
if (!fs.existsSync(assetsSrcPath)) return [];
return [
new CopyWebpackPlugin([
{ from: assetsSrcPath, to: path.join(rootDir, buildDir, path.basename(assetsDir)) },
// this is required for the icons to be selectable in the backend
{ from: path.join(assetsSrcPath, 'icons/'), to: path.join(rootDir, sitepackageDir, 'Resources/Public/Icons/') },
// this is required for avatars to be available; we must not check them in in fileadmin since they would
// prevent having the dir linked by Deployer during a deployment
{ from: path.join(rootDir, '../packages/users/Resources/Private/Images/Avatars/'), to: path.join(rootDir, buildDir, 'static/avatars/') },
]),
];
}
The question is whether you can improve the performance by using different plugins or summarizing steps...
I have a similar configuration. My configuration built ~33 seconds. I added a cache-loader package and decrease build time to ~17 seconds.
npm install --save-dev cache-loader
config:
rules: [
//rules here
{
test: /\.vue$/,
use: [
{
loader: 'cache-loader',
options: {}
},
{
loader: 'vue-loader',
options: vueLoaderConfig
}
],
},
{
test: /\.js$/,
use: [
{
loader: 'cache-loader',
options: {}
},
{
loader: 'babel-loader'
}
],
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
}
]
When i bundle split my webpack vendor ( node_modules ) my main.js file prints out the following message. When i disable bundle splitting, it works. But when I bundle split and i console.log the module (responsiveMenu) It complains about callbacks. I'm using the webpack bundle-loader to create my code splitted bundle.
my dist folder ususlly ends up like the following
main.js
bundle.1.js
bundle.2.js
bundle.x.js etc
below is my webpack file, the top of main.js and responsiveMenu.js, my babelrc.js is also present
babelrc.js
api.cache(true);
const presets = [['#babel/preset-env', {
"targets": {
"esmodules": true
}
}]];
const plugins = ['#babel/plugin-proposal-export-default-from', '#babel/plugin-transform-runtime'];
return {
presets,
plugins,
};
};
Webpack config
const webpack = require('webpack');
const minimatch = require('minimatch');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const StyleLintPlugin = require('stylelint-webpack-plugin');
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const ImageminPlugin = require('imagemin-webpack-plugin').default;
const TerserPlugin = require('terser-webpack-plugin');
const LiveReloadPlugin = require('webpack-livereload-plugin');
const globImporter = require('node-sass-glob-importer');
const CopyPlugin = require('copy-webpack-plugin');
const HappyPack = require('happypack');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const PhpManifestPlugin = require('webpack-php-manifest');
const phpManifest = new PhpManifestPlugin();
const devMode = 'development' === process.env.NODE_ENV;
module.exports = {
resolve: {
alias: {
assets: path.resolve(__dirname, 'assets'),
fonts: path.resolve(__dirname, 'assets/fonts'),
modernizr$: path.resolve(__dirname, './.modernizrrc')
}
},
mode: process.env.NODE_ENV,
entry: {
'js/main': './assets/js/scripts/main.js',
'css/style': './assets/scss/style.scss'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
chunkFilename: 'js/bundle.[id].js',
publicPath: 'wp-content/themes/strafe-boilerplate/dist/'
},
devtool: devMode ? 'source-map' : 'cheap-eval-source-map',
performance: {
maxAssetSize: 1000000
},
optimization: {
minimize: !devMode,
removeAvailableModules: !devMode,
removeEmptyChunks: !devMode,
splitChunks: !devMode ? {
maxInitialRequests: Infinity,
minSize: 0,
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
name: 'vendors',
vendors: {
test: /node_modules/,
name(module, chunks, cacheGroupKey) {
const moduleFileName = module
.identifier()
.split('/')
.reduceRight(item => item);
const allChunksNames = chunks.map(item => item.name).join('');
let name = `${cacheGroupKey}-${allChunksNames}-${moduleFileName}`;
return name;
}
},
}
} : false,
minimizer: !devMode ? [
new TerserPlugin({
sourceMap: !devMode,
cache: !devMode,
parallel: true
})
] : []
},
stats: {
assets: !devMode,
builtAt: !devMode,
children: false,
chunks: false,
colors: true,
entrypoints: !devMode,
env: false,
errors: !devMode,
errorDetails: false,
hash: false,
maxModules: 20,
modules: false,
performance: !devMode,
publicPath: false,
reasons: false,
source: false,
timings: !devMode,
version: false,
warnings: !devMode
},
module: {
rules: [
!devMode ?
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
include: path.resolve(__dirname, 'assets/js'),
use: ['happypack/loader', {
loader : 'bundle-loader'
}]
} : {
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
include: path.resolve(__dirname, 'assets/js'),
use: ['happypack/loader']
},
{
test: /\.(sa|sc|c)ss$/,
exclude: /(node_modules|bower_components)/,
include: path.resolve(__dirname, 'assets/scss'),
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
url: true
}
},
{
loader: 'postcss-loader',
options: {
sourceMap: true
}
},
{
loader: 'resolve-url-loader',
options: {
engine: 'postcss',
root: path.resolve(__dirname, 'assets')
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
importer: globImporter(),
outputPath: 'css',
debug: true
}
}
]
},
{
test: /\.(png|jpg|gif)$/,
include: path.resolve(__dirname, 'assets/img'),
use: [
{
loader: 'file-loader',
options: {
outputPath: 'img',
name: '[folder]/[name].[ext]',
publicPath: '../img/',
useRelativePath: true
}
}
]
},
{
test: /\.(eot|woff|woff2|ttf)([\?]?.*)$/,
include: path.resolve(__dirname, 'assets/fonts'),
use: [
{
loader: 'file-loader',
options: {
outputPath: 'fonts',
name: '[folder]/[name].[ext]',
debug: true,
publicPath: '../fonts/',
useRelativePath: true
}
}
]
},
{
test: /\.(svg)$/,
use: [
{
loader: 'file-loader',
options: {
outputPath: 'img',
name: '[folder]/[name].[ext]',
publicPath: '../img/',
useRelativePath: true
}
},
{
loader: 'svgo-loader',
options: {
plugins: [
{ removeTitle: false },
{ convertColors: { shorthex: false } },
{ convertPathData: false },
{ removeViewBox: false }
]
}
}
]
},
{
test: /\.modernizrrc.js$/,
use: ['modernizr-loader']
},
{
test: /\.modernizrrc(\.json)?$/,
use: ['modernizr-loader', 'json-loader']
}
]
},
plugins: [
new HappyPack({
loaders: [
{
loader: 'babel-loader',
options: {
presets: [ '#babel/preset-react' ]
}
}
]
}),
new webpack.ProvidePlugin({
'Promise': 'bluebird'
}),
phpManifest,
devMode && new FriendlyErrorsPlugin({
clearConsole: false
}),
new CleanWebpackPlugin(['dist'], {
cleanStaleWebpackAssets: false,
}),
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: !devMode ? 'css/bundle.[id].css' : false
}),
!devMode && new ImageminPlugin({
test: /\.(jpe?g|png|gif)$/i,
cacheFolder: './imgcache'
}),
devMode && new LiveReloadPlugin(),
new CopyPlugin([{ from: 'assets/img', to: 'img' }])
].filter(Boolean)
};
responsiveMenu.js
function responsiveMenu() {
// Responsive Menu
// Setup Doc, call out functions
function init() {
const toggleTrigger = $('.mobile-toggle');
const menuWrap = $('.menu-responsive');
menuPositionSetup(menuWrap);
triggerMenu(toggleTrigger, menuWrap);
childItemsHandler();
}
function menuPositionSetup(menu) {
let headerHeight = $('.header').outerHeight(true);
$(menu).css('top', headerHeight);
$(menu).css({ height: 'calc(100% - ' + headerHeight + 'px)' });
}
function triggerMenu(toggle, menu) {
$(toggle).on('click', function(e) {
mobileToggle($(this), menu);
});
}
function mobileToggle(trigger, menu) {
$(menu).toggleClass('is-active');
$(trigger).toggleClass('is-active');
$('body').toggleClass('is-overflow-h');
}
function childItemsHandler() {
$('.menu-item-has-children > a').on('click', function(e) {
if ($(this).hasClass('is-active')) {
// Go to the link
} else {
// Open the sub menu
$(this).addClass('is-active');
$(this)
.parent()
.find('.sub-menu')
.slideDown();
e.preventDefault();
}
});
}
};
export default responsiveMenu
main.js
import * as $ from 'jquery';
import responsiveMenu from './responsiveMenu';
console.log(responsiveMenu)
$(document).ready(function() {
init();
});
function init() {
responsiveMenu()
}