webpack bundle size vs requirejs bundle size - javascript

I'm trying to migrate a requireJS based app to webpack.
This app doesn't have many dependencies - actually it only needs a promise polyfill - and I've already figured out how to make webpack using the minified one.
The bundle size with requireJS used to be 43KB, when using webpack it's 121KB.
While 121KB isn't really huge it is a notable size increase.
From running webpack --display-reasons --display-modules I have learned that there seems to be some node_module dependencies included in my bundle. Way more than I expected.
I see things like buffer, readable-stream, stream-http, stream-browserify, core-util-is, buffer-shims, ...
Is this expected / part of the webpack wrapper code?
Is there anything I can do to exclude these dependencies?
This is my webpack.config.js:
var webpack = require('webpack');
module.exports = {
entry: {
"mynexuz": "./js/mynexuz-api.js",
"kws": "./js/kws-api.js",
"main": "./js/main.js",
"quest": "./js/quest.js"
},
output: {
filename: "./dist/[name]-bundle.js",
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production'),
}
})
],
node: {
//stream: false,
//process: false,
//global: false
},
// Enable sourcemaps for debugging webpack's output.
devtool: "source-map",
resolve: {
modules: ['js', 'js/lib', 'node_modules'],
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: [".webpack.js", ".web.js", ".ts", ".tsx", ".js"]
},
module: {
loaders: [
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{
test: /\.js$/,
loader: "source-map-loader",
exclude: /node_modules/
},
// All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
{
test: /\.tsx?$/,
loader: "awesome-typescript-loader",
exclude: /node_modules/
}
]
},
};

This doesn't work for all libraries you are using, but when possible you can save on file size by only importing the actual function/component you need to use.
Here is an example with lodash
import has from 'lodash/has';
That way above will ONLY import the has method.
However if you do either of the following:
import { has } from 'lodash';
Or
import _ from 'lodash';
Then you will import ALL of the lodash library which will bump up your file size.
However with other libraries (i.e. current version of moment.js) it's not so simple to import just the PART of the library you need.
There are a few other ways to try to solve this problem (i.e. tweaking your webpack settings) but I would start with this method.

After looking deeper into the issue I've found the reason for the large size of the bundle. In true requireJS style I had:
define(['http', 'config'], function (Http, Config) { ... });
This 'http' thing was supposed to refer to my own library, but webpack resolved this to some NPM module, bringing in all the aforementioned dependencies.
I've now changed the code to:
define(['./http', 'config'], function (Http, Config) { ... });
And the bundle sizes are back to around 44KB.

Related

Webpack unexpected token react native

I'm trying to integrate a connection to Ledger nano X devices, into a native Swift package. Unfortunately Ledger only provide an SDK as a ReactNative module. I dislike ReactNative and really don't want to integrate it into my entire Swift package, and force it onto my users, just for this one library.
I've had limited success in the past converting NodeModules into plain .js files that I can run directly with iOS's JavascriptContext. I've used webpack to bundle it up into a single plain js file (no require, import, export etc keywords). Adding a native Swift wrapper around that lets me keep all the messy dev setup and tools out of my project and just leverage the plain JS when needed for small use-cases. I've very little experience with this and what i've done in the past isn't working, hours of googling hasn't gotten me any closer.
Building the entire Ledgerjs with webpack was throwing up errors, since I only need pieces of it I started trying to get one of the sub-packages #ledgerhq/react-native-hw-transport-ble to build. I forked the project and added this webpack.config.js
const webpack = require('webpack');
const path = require('path');
module.exports = {
entry: {
"ledger_transport_ble": ['./src/awaitsBleOn.ts', './src/BleTransport.ts', './src/monitorCharacteristic.ts', './src/remapErrors.ts', './src/timer.ts', './src/types.ts']
},
mode: 'production',
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/
},
{
test: /\.(js|jsx)$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
},
devtool: 'source-map',
resolve: {
extensions: ['.tsx', '.ts', '.js', '.jsx'],
modules: ['node_modules'],
fallback: {
"stream": require.resolve("stream-browserify")
}
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
library: ['[name]'],
libraryTarget: "var"
},
plugins: [
new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] })
],
};
with this .babelrc
{
"presets": [
"#babel/preset-env",
"#babel/preset-react",
"#babel/preset-flow"
]
}
and I get back this error:
ERROR in ./node_modules/react-native-ble-plx/index.js 11:7
Module parse failed: Unexpected token (11:7)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| export { State, LogLevel, ConnectionPriority, ScanCallbackType, ScanMode } from './src/TypeDefinition'
|
> export type {
| Subscription,
| DeviceId,
# ./src/BleTransport.ts 87:29-60
Instructions online where to add the .babelrc file that I added above, but it made no difference. Have I missed a step somewhere? Or is there something else I should be doing?

Styles not inserted into HEAD using Webpack MiniCssExtractPlugin with Create React App

I'm using create-react-app to create a component library using Storybook JS. The aim is to publish a NPM package where these components can be used in other projects. SASS is being used for this library, with global variables defined and imported into the src/index.js file.
I'm at the point of testing my NPM package, and trying to bundle it with Webpack V4. This has been semi-successful using npm link on another local project. However, I'm facing an issue with MiniCssExtractPlugin where the styles are not being inserted at all into the HEAD of this project.
The SASS stylesheets are converted into CSS and added to the dist/ folder of my component library project with no issues. But these don't get applied anywhere when importing components via the NPM package to my other project, e.g. import { Button } from '#my-account/components';
There are no issues on my dev environment when using style-loader, and the styles are inserted straight into the DOM with a <style> tag. I'm sure I must be missing something, but I feel like I've exhausted everything to try diagnose this. Is create-react-app not compatible with this plugin? Or are styles not automatically injected to the HEAD of projects via NPM packages with this plugin?
The styling does work if I import the file from the NPM package into my local project, e.g. import '#my-account/components/dist/main.cd2be00655e79c5077cb.css'; - but this doesn't seem maintainable if styles are regularly updated and the file uses a hash in its name?
I attempted to add HtmlWebpackPlugin to create an index.html file, but this didn't resolve the issue.
Any help would be greatly appreciated!
webpack.config.prod.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
devtool: 'source-map',
entry: './src/index.js',
output: {
path: path.resolve('dist'),
filename: 'index.js',
libraryTarget: 'commonjs2'
},
module: {
rules: [
{
test: /\.js?$/,
exclude: /(node_modules)/,
use: 'babel-loader',
}
],
rules: [
{
test: /\.scss$/,
sideEffects: true,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-resources-loader',
options: {
resources: require(path.join(process.cwd(), 'src/assets/sass/WebpackStyles.js')),
hoistUseStatements: true
}
}
],
},
]
},
resolve: {
extensions: ['.js'],
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[hash].css'
})
]
};
Component libraries regularly require css to be imported alongside the js in order to work properly. If you update your plugins as:
new MiniCssExtractPlugin({
filename: '[name].css'
})
Then you can instruct consumers of your library to add import '#my-account/components/dist/main.css' which is a little easier to swallow. I'm not sure there's a magical way to have the styles just show up without extensive custom webpack loaders in your consumers.

Why is my webpack babel setup emitting ESM requires when I have configured my preset to commonjs?

Today I observed babel/babel-loader exhibiting some undesirable behavior. I am bundling some assets for usage on nodejs. Post-compile, a bundle is generated with references to #babel/runtime/**/esm/**. Of course, node cannot import such files, and on node bundle.js I get:
Must use import to load ES Module: /my/project/node_modules/#babel/runtime/helpers/esm/defineProperty.js
require() of ES modules is not supported.
Right. Makes sense. But babel-loader injected those imports. In fact, the parent folder in #babel/runtime has all of the non-ESM things, which I actually probably do want imported! My babel config looks as such:
{
presets: [
[
"#babel/preset-env",
{
modules: 'commonjs',
targets: {
node: "current",
esmodules: false,
},
},
],
"#babel/preset-typescript",
]
}
As you can see, I'm attempting to tell babel via targets.esmodules: false and modules: 'commonjs' to use commonjs. I hoped these entries would tell babel to not expect ESM compatibility! None the less, generated bundles still look like:
...
var _toConsumableArray2 = _interopRequireDefault(__webpack_require__(/*! #babel/runtime/helpers/esm/toConsumableArray */ "#babel/runtime/helpers/esm/toConsumableArray"));
My full webpack config is pretty terse as well:
{
entry: serverFilename,
mode: 'development',
output: {
path: path.dirname(serverBuildFilename),
filename: path.basename(serverBuildFilename)
},
target: "node",
externals: [webpackNodeExternals()],
optimization: {
moduleIds: 'named',
minimize: false
},
resolve: {
extensions: ['.ts', '.tsx', '.wasm', '.mjs', '.js', '.json'],
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: babelConfig.presets // see babel config above
}
}
},
],
},
}
I can't tell if I'm missing configuration, if babel isn't respecting my configuration, or <your ideas here>!
All tips appreciated. Thanks!
It took me a while to figure out the solution and this post helps
https://github.com/webpack/webpack/issues/11696
Copied my solution from the issue here:
I ended up using webpack-node-externals in webpack config to sort of bypass this issue
const nodeExternals = require('webpack-node-externals');
// add these to the webpack config.
externals: [
nodeExternals({
whitelist: [/^#babel\/runtime/],
}),
],
This solution creates duplicated babel/runtime file injections and can also be mitigated by https://webpack.js.org/loaders/babel-loader/#babel-is-injecting-helpers-into-each-file-and-bloating-my-code

Are typescript project references necessary if transpiling with babel? (Electron project with Webpack)

I am still in the process of setting up my project configuration so I don't have any errors to work with right now, but if I am understanding the Typescript docs correctly...
It seems like Project references TypeScript Docs - Project references are not that necessary if transpiling with babel-loader in webpack. (I'm working in VSCode)
I am trying to convert an Electron app to TypeScript and currently reorganizing the folder structure so I have minimal issues.
I am trying to understand if I am on the right track and if I can avoid including "references" and instead just use "extends" to get the functionality I want.🤔
Here is my project structure ignoring all files that are not tsconfig files:
./tsconfig.json
./tsconfig-base.json
./main/tsconfig.json
./src/client/tsconfig.json
./__tests__
./__tests__/__client__/tsconfig.json
./__tests__/__main__/tsconfig.json
In this structure ./tsconfig.json would really just be for references like the example on Microsoft's Github
Electron Main Process and related files are in ./main. The tsconfig here will set "module":"commonjs" for working in node. I think it will also extend from the ./tsconfig-base.json
Electron Renderer Process and my React-Redux app files are in ./src/client. The tsconfig here sets "module":"es2015" or "module":"ESNEXT" for working with es modules. I think it will also extend from the ./tsconfig-base.json
The ./__tests__/__client__/tsconfig.json and ./__tests__/__main__/tsconfig.json would just be duplicates of the non tests folder versions similarly extending from the base config in ./
Webpack config is already set up to handle creating separate bundles for main and renderer processes so that the entire app can be in TypeScript. Is there any reason I should be using "references" in my files in the main or client folders?
Sample snippets of the Webpack config before I switch the tnry files to be .ts files (dev):
const rendererInclude = path.resolve(__dirname, "src");
const mainInclude = path.resolve(__dirname, "main");
Main Process:
module.exports = [
{
mode: "development",
entry: path.join(__dirname, "main", "swell.js"),
output: {
path: path.join(__dirname, "dist"),
filename: "main-bundle.js",
},
target: "electron-main",
node: {
__dirname: false,
__filename: false,
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".json"],
},
module: {
rules: [
{
test: /\.(ts|js)x?$/,
loader: "babel-loader",
include: mainInclude,
exclude: /node_modules/,
}
] } ... },
continued to Renderer Process:
{
mode: "development",
entry: path.join(__dirname, "src", "index.js"),
output: {
path: path.join(__dirname, "dist"),
filename: "renderer-bundle.js",
},
target: "electron-renderer",
resolve: {
extensions: [".ts", ".tsx", ".js", ".json"],
},
module: {
rules: [
{
test: /\.(ts|js)x?$/,
loader: "babel-loader",
include: rendererInclude,
exclude: /node_modules/,
]} ...} ]
Project Reference will help you solve some problems in case where your test projects import src/main modules.
This is what Project Reference solves:
There’s no built-in up-to-date checking, so you end up always running tsc twice
Invoking tsc twice incurs more startup time overhead
tsc -w can’t run on multiple config files at once
read more about Project References

BabelJS is processing the wrong files when working with Webpack 4

I started from a configuration consisting of both webpack 3 and a similarly old babel version. It can be found in the following repo:
https://github.com/konradmi/react-loadable-ssr-code-splitting
I updated both webpack and babel to their latest versions, as well as all the node modules, and migrated the old configuration accordingly. This issue could stem from any of these.
Once I finished migrating, I noticed all the babel plugins traverse my webpack configs files (which are in a separate nested folder) instead of the actual js source files which are properly processed by webpack. (I verified it by doing some logging inside of the babel plugins).
The result is the same regardless of whether I'm using .babelrc or not.
The webpack config files used to be at the root of the project as you can see in the repo I linked to above, and now they are inside of a nested "config" folder.
At first I thought it might be the cause of this issue, so I tried the following:
Using path.resolve() in the entry point in order to use an absolute path to make sure it wouldn't possibly be re-interpreted by babel from a string relative to who knows where.
Putting the webpack config files back in the root of the project and building from that path.
In all the variations I've tried - webpack always does its job perfectly, while babel is traversing the wrong files. I'm not even sure how it's possible, babel-loader should be traversing the files which it got from the previous webpack rule, which emitted the correct files.
Here's my current Webpack 4 config files concatenated into 1 file and stripped of irrelevant rules and plugins for your convenience:
const webpack = require('webpack')
const path = require('path')
const webpackNodeExternals = require('webpack-node-externals')
module.exports = {
name: 'server',
target: 'node',
externals: [webpackNodeExternals()],
entry: './src/server.tsx',
output: {
filename: 'bundle.js',
chunkFilename: '[name].js',
path: path.resolve(__dirname, '../build')
},
mode: 'development',
stats: 'verbose',
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx']
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
babelrc: false,
presets: [
[
'#babel/env',
{
'targets': {
'browsers': ['last 2 versions']
},
'debug': false
}
],
'#babel/preset-react'
],
plugins: [
'#babel/plugin-syntax-dynamic-import',
'#babel/plugin-proposal-class-properties',
'#babel/plugin-transform-object-assign',
'react-loadable/babel'
]
}
}
]
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader'
}
]
}
]
}
}
I was importing webpack in my server source code, the files babel was going over were webpack imports which I added there in the source code. It couldn't find anything else other than those imports because due to previous rules webpack was exporting a bundle with evals containing the processed code (dev mode settings). The babel plugin wasn't looking for eval statements, so the only thing I could see was the processing of webpack related imports.

Categories

Resources