In Webpack v4 with multiple entries, my splitChunks is not optimized, if my two pages includes core-js, they will all get a copy - javascript

You can clone my minimum repo https://github.com/rockmandash/webpack-chunks-question
Or see the code below
page1.js
import 'core-js';
console.log('I am page1');
page2.js
import 'core-js';
import 'react';
import 'react-dom';
console.log('I am page2');
My webpack config:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const entriesFilePath = [
require.resolve('./src/page1.js'),
require.resolve('./src/page2.js'),
];
const mode = 'development';
const webpackConfig = entriesFilePath.map((entrieFilePath) => {
const fileName = path.basename(entrieFilePath, path.extname(entrieFilePath));
// fileName would be page1 and page2
return {
mode,
devtool: 'cheap-module-source-map',
entry: {
[fileName]: entrieFilePath,
},
output: {
filename: 'static/js/[name].js',
chunkFilename: 'static/js/[name].chunk.js',
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
plugins: [
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
filename: `${fileName}.html`,
},
),
),
].filter(Boolean),
};
});
module.exports = webpackConfig; // I have to export an array, because in the future, I need to do something else.
The generated dist folder looks like this:
dist
/page1.html
/page2.html
/static
/js
/page1.js.map
/vendors~page2.chunk.js.map
/vendors~page1.chunk.js // this includes core-js !!!!
/page1.js
/page2.js
/vendors~page1.chunk.js.map
/vendors~page2.chunk.js // this includes core-js too !!!!
/page2.js.map
You see, the generated two chunks both includes core-js, how can I make my webpack config smart enough to automatically separate core-js or other common vendor files out of the box?

You do not need to import core-js anywhere. Create a .babelrc file.
{
"presets": [
[
"#babel/preset-env",
{
"debug": true,
"useBuiltIns": "usage",
"corejs": 3
}
]
]
}
Instal #babel/core, #babel/polyfill, #babel/preset-env, babel-loader
Add to webpack
const optimization = {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'vendor',
chunks: 'all',
}
}
}
};
module: {rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]},
Take a look at my solution, there is a complete code. Use of core-js. He uses only part polyfil when it is needed.
https://github.com/tomik23/webpack-babel-corejs/blob/master/webpack.config.js#L17
And the second solution needed in your code is to use spliChunks
https://github.com/tomik23/photoBlog/blob/master/config/webpack.config.js#L31
P.S. If you import 'core-js' in this way; you download the whole core-js if you use my method then core-js chooses only what is needed and packages are smaller.

Related

Why isn't webpack tree-shaking swiperjs modules?

So, swiperjs, from docs:
"By default Swiper exports only core version without additional modules (like Navigation, Pagination, etc.). So you need to import and configure them too:"
// core version + navigation, pagination modules:
import Swiper, { Navigation, Pagination } from 'swiper';
Well, I did. This is my index.js test file:
import Swiper, { Navigation, Pagination } from 'swiper';
console.log(Swiper);
And this is my webpack config:
const path = require('path');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
//mode: 'development',
mode: 'production',
watch: true,
entry: {
index: './src/index.js',
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
optimization: {
minimize: true,
usedExports: true,
}
plugins: [
new BundleAnalyzerPlugin()
],
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: { presets: ['#babel/preset-env'] }
}
} // babel
] // rules
} // module
};
Well, the BundleAnalyzer graph shows that ALL the swiper modules are bundled. Why? How can I avoid that?
I already disabled sideEffect from package.json and activated providedExports in optimizations.
The source code of swiper.esm.js is copied here for reference: https://jsfiddle.net/fw4zj8qk/
I've found out the issue: with swiper (at least), sideEffects option must be used at loader level and not at package level. Or maybe you can use at package level, but explicitly declaring the files. Didn't try though.
Instead, it did worked by setting sideEffects like this:
module: {
rules: [
// [...] here normally there are babel settings and the like
{ // here doing the swiper loader and declaring no sideEffects
test: /swiper\.esm\.js/,
sideEffects: false
}
] // rules
} // module
Now swiper gets tree shaked.

How to get webpack minimizer to work when bundling css into js

I am trying to figure out webpack. I want to bundle a css and js file while also minimizing them at the same time. I am using optimize-css-assets-webpack-plugin and got it to work together with mini-css-extract-plugin. But, when bundling css into js with style-loader, the css is no longer minified. Does anyone have any ideas on what I can do to make this work?
Index js file:
import './styles.css';
// some js
Webpack config:
const path = require('path');
const OptimizeCss = require('optimize-css-assets-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const bundle = {
entry: './src/index.js',
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, './'),
},
module: {
rules: [
{
test: /\.css$/i,
use: [
'style-loader',
'css-loader'
],
},
],
},
optimization: {
minimizer: [
new OptimizeCss(),
new TerserPlugin()
]
},
mode: 'production',
watch: false
}
module.exports = bundle;
Edit: I am using webpack version 4.39.3

How to add/use aurelia-bootstrap-datepicker in node_modules

I'm new to aurelia and wanted to add a datepicker in my form, I saw this post from danyow, showing how to add datepicker using jquery. But would love to use a datepicker from bootstrap.
And so I saw this another post, haven't tried the second link because as I'm scanning the node_modules (if it matters, I use the spa templates found here), I saw a folder aurelia-bootstrap-datepicker and under the src folder, there's a aurelia-bootstrap-datepicker.js in which the content is pretty much the same with the second link.
Can I use that folder to have a datepicker? If yes, how? Or should I just follow the second link to create one myself?
What I tried:
use it as feature in my boot.ts file aurelia.use.standardConfiguration().feature(PLATFORM.moduleName('aurelia-bootstrap-datepicker'));
use it as a plugin
require it on my html file
Everything above, nothing is working for me.
Please advise. Thanks
EDIT:
boot.ts
import 'isomorphic-fetch';
import { Aurelia, PLATFORM } from 'aurelia-framework';
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap';
declare const IS_DEV_BUILD: boolean; // The value is supplied by Webpack during the build
export function configure(aurelia: Aurelia) {
aurelia.use.standardConfiguration()
.plugin(PLATFORM.moduleName('aurelia-validation'));
if (IS_DEV_BUILD) {
aurelia.use.developmentLogging();
}
aurelia.start().then(() => aurelia.setRoot(PLATFORM.moduleName('app/components/app/app')));
}
webpack.config.js
const path = require('path');
const webpack = require('webpack');
const { AureliaPlugin } = require('aurelia-webpack-plugin');
const bundleOutputDir = './wwwroot/dist';
module.exports = (env) => {
const isDevBuild = !(env && env.prod);
return [{
stats: { modules: false },
entry: { 'app': 'aurelia-bootstrapper' },
resolve: {
extensions: ['.ts', '.js'],
modules: ['ClientApp', 'node_modules'],
},
output: {
path: path.resolve(bundleOutputDir),
publicPath: '/dist/',
filename: '[name].js'
},
module: {
rules: [
{ test: /\.ts$/i, include: /ClientApp/, use: 'ts-loader?silent=true' },
{ test: /\.html$/i, use: 'html-loader' },
{ test: /\.css$/i, use: isDevBuild ? 'css-loader' : 'css-loader?minimize' },
{ test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' }
]
},
plugins: [
new webpack.DefinePlugin({ IS_DEV_BUILD: JSON.stringify(isDevBuild) }),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./wwwroot/dist/vendor-manifest.json')
}),
new AureliaPlugin({ aureliaApp: 'boot' })
].concat(isDevBuild ? [
new webpack.SourceMapDevToolPlugin({
filename: '[file].map', // Remove this line if you prefer inline source maps
moduleFilenameTemplate: path.relative(bundleOutputDir, '[resourcePath]') // Point sourcemap entries to the original file locations on disk
})
] : [
new webpack.optimize.UglifyJsPlugin()
])
}];
}

Compress and Minify multiples javascript files into one, with Webpack?

I'm trying to concatenate, minify multiples javascript files into one, with webpack.
So my question can this be done with webpack? and How?
I tried a lot of ways, but couldn't get it to work as what I wanted.
Best I show examples.
3 javascript files.
app.js
var fnA = function () {
console.log('fnA')
}
global.js
fnA();
main.js
require('./app.js');
require('./global.js');
webpack.config.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry : [
'./app/main.js'
],
output : {
path : path.resolve(__dirname, 'dist'),
filename : 'bundle.js'
},
plugins : [
new webpack.optimize.UglifyJsPlugin(
{
minimize: true,
compress: false,
mangle: {
keep_fnames: true
},
bare_returns : true
}),
]
};
I'm expecting that global.js is able to call to any function in app.js.
Probably this can be done with grunt, but I thought webpack can do this too.
Worry that I'm heading to a total wrong direction. Google around, but can't seem to find any solution, tried with other plugin suc as chunk, which doesn't helps.
Any advices are welcome.
Thanks in advance.
I put together something simple but you need babel.
https://github.com/vpanjganj/simple-webpack-sample
This is your webpack config:
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: [ "./app/main.js" ],
output: {
path: path.join(__dirname, "./dist"),
filename: "bundle.js"
},
module: {
rules: [
{ test: /\.js$/, use: [ { loader: 'babel-loader' } ], exclude: /node_modules/ }
]
},
plugins: [
new webpack.LoaderOptionsPlugin({
minimize: true,
debug: false
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
]
};
here your 2 modules:
First module, moduleOne.js:
export default function sayHello() {
console.log('hello')
}
moduleTwo.js file:
export default function sayBye() {
console.log('bye')
}
and your main.js file:
import sayHello from './moduleOne'
import sayBye from './moduleTwo'
const myApp = ()=>{
sayHello();
sayBye()
};
myApp();
The command to build:
$ ./node_modules/.bin/webpack --color --display-error-details --config ./webpack.js"

Webpack - build bundle without dependencies

I;m wondering if it's possible to build a bundle with some javascript files but without dependencies?
I want to have small bundles with React components (each react component in my case is builded from few react components, for example Comment component incldues comment box, list, and form),
I can split React components to a separate files by specifying few entry points in webpack, but if I have:
1. Component comment
2. Component newsletter
and both of them require ReactDOM, files which will be generated will have like 600kb, where my react components contain only ~100 lines of js code.
I would like to have one more file which will contain all the code which would come from "require('react-dom'), and those two files which will only have the React component code. is that possible?
My current setup:
'use strict';
import path from 'path';
import CommonsChunkPlugin from "webpack/lib/optimize/CommonsChunkPlugin";
module.exports = {
entry: {
app: "./app.js",
newsletter: "./components/renderers/newsletter.renderer.js",
comment: "./components/renderers/comment.renderer.js"
},
output: {
path: path.join(__dirname),
filename: "built/[name].entry.js"
},
devtool: 'sourcemaps',
cache: true,
debug: true,
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: [/(node_modules)/],
loader: 'babel'
}
],
resolve: {
extensions: ['', '.js', '.jsx']
}
},
plugins: [
new CommonsChunkPlugin({
name: "comment.js",
chunks: ["comment", "app"],
minChunks: 2
}),
new CommonsChunkPlugin({
name: "newsletter.js",
chunks: ["newsletter", "app"],
minChunks: 2
})
]
};
Comment.renderer.js:
import CommentBox from './../comment/commentBox';
ReactDOM.render(
<CommentBox/>,
document.getElementById("comment")
);
Newsletter.renderer.js:
import Newsletter from './../newsletter/newsletter';
ReactDOM.render(
<Newsletter/>,
document.getElementById("newsletter")
);
app.js:
'use strict';
import React from 'react';
import ReactDOM from 'react-dom';
import client from './lib/client';
Ok I've managed how to do that:
import path from 'path';
import CommonsChunkPlugin from "webpack/lib/optimize/CommonsChunkPlugin";
module.exports = {
entry: {
vendor: ["react","react-dom", "underscore"],
comment: "./components/renderers/comment.renderer.js",
newsletter: "./components/renderers/newsletter.renderer.js"
},
output: {
path: path.join(__dirname),
filename: "built/[name].bundle.js"
},
devtool: 'sourcemaps',
cache: true,
debug: true,
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: [/(node_modules)/],
loader: 'babel'
}
],
resolve: {
extensions: ['', '.js', '.jsx']
}
},
plugins: [
new CommonsChunkPlugin({
name: "vendor",
minChunks: Infinity
})
]
};
this part:
minChunks: Infinity
will ensure that code included in bundle "vendor" is not included in any other bundle. Thanks to this approach, comment and newsletter will contain only my React components.
I use following code in webpack.config.js to exclude the external dependencies from bundle.
module.exports = {
...
...
externals:{
"react": "React",
"react-dom": "ReactDOM"
},
...
...
}
I found this answer from this link
To complement #AnandShanbhag's answer, you can take all the dependencies from package.json and turn them into externals with a function:
module.exports = {
...
// put everything inside package.json dependencies as externals
externals: Object.keys(require('./package.json').dependencies)
.reduce(
function (acc, cur) {
acc[cur] = cur
return acc
},
new Object()
),
...
}

Categories

Resources