Webpack is not actually my strength, I bumped into a problem today where I'm completeley stuck on a gigantic project.
Basically I just ran the webpack analyzer and our bundle size is way too big because we have around 200 SVGs in our project build.
I wanted to come up with a simple solution to decrease the bundle size and compress the SVG using webpack since that's what we are using.
After many failures I figured it would be as simple as it is to include
test: /\.(gif|png|jpe?g|svg)$/i,
type: 'asset/resource',
},
I can now see that my bundle is significantly decreased, however ALL my SVG's are hidden when I load up the project.
What could be the reason? Also what are the alternatives to compress SVG's using Webpack 5???
here is the whole webpack config
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
const hashSubstr = '.[contenthash:8]';
const svgoPlugins = [
{ cleanUpAttrs: true },
{ removeDoctype: true },
{ removeXMLProcInst: true },
{ removeComments: true },
{ removeMetadata: true },
{ removeDesc: true },
{ removeEditorsNSData: true },
{ removeEmptyAttrs: true },
{ removeHiddenElems: true },
{ removeEmptyText: true },
{ removeEmptyContainers: true },
{ cleanupNumericValues: true },
{ moveElemsAttrsToGroup: true },
{ convertColors: { shorthex: true } },
];
module.exports = (env) => ({
entry: ['./scripts/responsive/index.ts', './scripts/pwa/serviceworker.ts'],
output: {
filename: `[name]${!env.development ? hashSubstr : ''}.js`,
globalObject: 'this',
path: path.resolve(__dirname, './bundles/responsive'),
publicPath: '/',
assetModuleFilename: '[hash][ext][query]',
},
mode: !env.development ? 'production' : 'development',
devtool: 'inline-source-map',
optimization: {
minimize: true,
},
module: {
rules: [
// {
// test: /\.(gif|png|jpe?g|svg)$/i,
// type: 'asset/resource',
// },
{
test: /\.(jsx?|tsx?)$/,
loader: 'babel-loader',
options: {
presets: ['#babel/typescript', '#babel/env'],
},
},
{
test: /\.s[ac]ss$/i,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../',
},
},
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
},
},
],
},
{
test: /\-colou?r\.svg$/,
type: 'asset/resource',
include: [path.resolve(__dirname, 'Content/responsive/svg')],
use: [
{
loader: 'svg-sprite-loader',
options: {
spriteFilename: 'sprite.svg',
esModule: false,
symbolId: (fileName) => {
return `r-icon-${path.basename(fileName, '.svg')}`;
},
},
},
{
loader: 'svgo-loader',
options: {
plugins: svgoPlugins,
},
},
],
},
{
test: /\.svg$/,
type: 'asset/resource',
exclude: /-colou?r\.svg$/,
include: [path.resolve(__dirname, 'Content/responsive/svg')],
use: [
{
loader: 'svg-sprite-loader',
options: {
spriteFilename: 'sprite.svg',
esModule: false,
symbolId: (fileName) => {
return `r-icon-${path.basename(fileName, '.svg')}`;
},
},
},
{
loader: 'svgo-loader',
options: {
plugins: [
{
removeAttrs: {
attrs: '(?!mask).*:(stroke|fill)',
},
},
...svgoPlugins,
],
},
},
],
},
],
},
//stats: 'verbose',
plugins: [
new ForkTsCheckerWebpackPlugin(),
new WebpackManifestPlugin({
fileName: 'asset-manifest.json',
generate: (seed, files) => {
const manifestFiles = files.reduce((manifest, file) => {
manifest[file.name] = file.path;
return manifest;
}, seed);
const entrypointFiles = files
.filter((x) => x.isInitial && !x.name.endsWith('.map'))
.map((x) => x.path);
return {
files: manifestFiles,
entrypoints: entrypointFiles,
};
},
}),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: `css/[name]${!env.development ? hashSubstr : ''}.css`,
chunkFilename: `css/[id]${!env.development ? hashSubstr : ''}.css`,
}),
new SpriteLoaderPlugin({
plainSprite: true,
}),
],
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
alias: {
Svg: path.resolve(__dirname, './Content/responsive/svg'),
},
},
});
In my case, it seems that when using type: 'asset/resource' the module is exported as commonjs and not default import of esmodule
Related
I work with webpack 5. Pug works well - after autosave or cmd+S I get new version of page. And it doesn't work so with JS and SCSS: SCSS autosave works well but after cmd+S I get some old version of page(usually everytime the same). With JS both autosave and cmd+S return some old version of page. Can you help me to find a solution? Below I dropped webpack.config.js
const path = require("path");
const fs = require("fs");
const HTMLWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const isDev = process.env.NODE_ENV === "development";
const isProd = !isDev;
const PATHS = {
src: path.join(__dirname, "./src"),
dist: path.join(__dirname, "./dist"),
};
const PAGES_DIR = `${PATHS.src}/`;
const PAGES = fs.readdirSync(PAGES_DIR).filter((fileName) => fileName.endsWith(".pug"));
const filename = (ext) => `[name].${ext}`;
const plugins = () => {
const basePlugins = [
...PAGES.map(
(page) =>
new HTMLWebpackPlugin({
template: `${PAGES_DIR}/${page}`,
filename: `./${page.replace(/\.pug/, ".html")}`,
})
),
new MiniCssExtractPlugin({
filename: `./css/${filename("css")}`,
}),
new CopyWebpackPlugin({
patterns: [
{ from: path.resolve(__dirname, "src/assets"), to: path.resolve(`${PATHS.dist}`) },
{ from: path.resolve(__dirname, "src/img"), to: path.resolve(`${PATHS.dist}/img/`) },
],
}),
];
return basePlugins;
};
module.exports = {
context: path.resolve(`${PATHS.src}`),
mode: "development",
entry: "./js/main.js",
output: {
filename: `./js/${filename("js")}`,
path: path.resolve(`${PATHS.dist}`),
publicPath: "",
clean: true,
},
devServer: {
historyApiFallback: true,
static: path.resolve(`${PATHS.dist}`),
open: true,
compress: true,
hot: true,
port: 3000,
},
optimization: {
splitChunks: {
chunks: "all",
},
},
plugins: plugins(),
devtool: isProd ? false : "source-map",
module: {
rules: [
{
test: /\.pug$/,
loader: "pug-loader",
},
{
test: /\.s[ac]ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: (resourcePath, context) => {
return path.relative(path.dirname(resourcePath), context) + "/";
},
},
},
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: ["postcss-preset-env"],
},
},
},
"sass-loader",
],
},
{
test: /\.js$/,
exclude: /node_modules/,
use: ["babel-loader"],
},
{
test: /\.(?:|gif|png|jpg|jpeg|svg|webp)$/,
use: [
{
loader: "file-loader",
options: {
name: `./img/${filename("[ext]")}`,
},
},
],
},
{
test: /\.(?:|woff2)$/,
use: [
{
loader: "file-loader",
options: {
name: `./fonts/${filename("[ext]")}`,
},
},
],
},
],
},
};
I don't know what to write here. I described my problem above.
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"
I'm using #teamsupercell/typings-for-css-modules-loader to generate TS types for my SASS modules. The output looks something like:
declare namespace LoginRouteStylesScssModule {
export interface ILoginRouteStylesScss {
card: string;
emailForm: string;
info: string;
input: string;
}
}
declare const LoginRouteStylesScssModule: LoginRouteStylesScssModule.ILoginRouteStylesScss & {
/** WARNING: Only available when `css-loader` is used without `style-loader` or `mini-css-extract-plugin` */
locals: LoginRouteStylesScssModule.ILoginRouteStylesScss;
};
export = LoginRouteStylesScssModule;
This lets it pass tsc. However, Babel is telling me 'export =' is not supported by #babel/plugin-transform-typescript. Please consider using 'export <value>;'. I want to just ignore .d.ts files since they're intended for tsc and not Webpack/Babel.
I tried exclude in .babelrc, exclude and ignore in webpack.config.js, and webpack.IgnorePlugin, but none of them work. How can I configure either Webpack or Babel to ignore them?
`webpack.config.js':
const path = require('path');
const childProcess = require('child_process');
const webpack = require('webpack');
const APP_ROOT = path.resolve('./src');
module.exports = {
entry: {
index: path.resolve('./src/main.tsx'),
},
output: {
path: path.resolve('./public/js'),
publicPath: '/js/',
},
module: {
rules: [
{
test: /\.(j|t)sx?$/,
include: [APP_ROOT],
exclude: [path.resolve('./node_modules')],
use: {
loader: 'babel-loader',
options: {},
},
},
{
test: /\.s?css$/,
include: [APP_ROOT],
exclude: [/node_modules/],
use: [
'#teamsupercell/typings-for-css-modules-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: {
localIdentName: '[name]__[local]',
},
},
},
'postcss-loader',
'sass-loader',
{
loader: 'sass-resources-loader',
options: {
resources: [
path.resolve('./src/styles/imports/variables.scss'),
path.resolve('./src/styles/imports/mixins.scss'),
],
},
},
],
},
{
test: /\.png$/,
use: [
'file-loader?name=../css/sprites/[name].png',
],
},
],
},
resolve: {
modules: [APP_ROOT, path.resolve('./node_modules')],
extensions: ['.js', '.jsx', '.ts', '.tsx'],
alias: {
sprites: path.resolve('./public/css/sprites'),
},
},
optimization: {},
context: APP_ROOT,
plugins: [
new webpack.optimize.ModuleConcatenationPlugin(),
new webpack.DefinePlugin({
MODULE_CONFIG: null,
}),
new webpack.EnvironmentPlugin({
NODE_ENV_ACTUAL: process.env.NODE_ENV || null,
API_URL: process.env.API_URL || null,
ASSETS_URL: process.env.ASSETS_URL || null,
JS_VERSION: parseInt(
(childProcess.execSync('git rev-list --count master').toString()).trim(),
10,
),
}),
],
cache: true,
devtool: false,
watchOptions: {
ignored: [
path.resolve('./public'),
path.resolve('./src/styles/generated/**'),
path.resolve('./src/**/*.d.ts'),
],
},
};
babel.config.js:
module.exports = {
presets: [
['#babel/preset-env', {
useBuiltIns: 'usage',
corejs: '3',
exclude: [
'babel-plugin-transform-async-to-generator',
'babel-plugin-transform-regenerator',
],
}],
'#babel/preset-typescript',
],
plugins: [
'#babel/plugin-syntax-dynamic-import',
['#babel/plugin-transform-react-jsx', { pragma: 'h' }],
'#babel/plugin-proposal-do-expressions',
'#babel/plugin-proposal-class-properties',
'#babel/plugin-proposal-optional-chaining',
/* Async/await increases file size by a lot.
["module:fast-async", {
"compiler": { "promises": true, "generators": false, "useRuntimeModule": true },
}],
["#babel/plugin-transform-modules-commonjs", {
"strictMode": false,
}], */
],
env: {
production: {
plugins: ['transform-react-remove-prop-types'],
},
},
};
Use "null-loader" plugin to ignore ".d.ts" files
{
test: /\.(d.ts)$/,
include: [path.join(__dirname, 'src')],
use: [
{
loader: 'null-loader',
},
],
},
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()
}