Webpack: Include vendor in only one module - angularjs

I try to migrate one of my project building from browserify to webpack. It's AngularJS#1.7 project and I have multiple bundles: core with AngularJS and its dependenciy imports and other specific lazy-loading modules with its specific dependencies. So I have multiple entry points but I only load core.js script in index.html. Another modules resolve while routing by my resolver.
One of the lazy-loading modules imports AngularJS and it leads to duplicate code and WARNING: Tried to load AngularJS more than once. I want to Webpack check if modules were imported in core and load them from it.

I made it work but need to import one extra file common-vendors.js in index.html.
My config:
export default {
context: path.resolve(__dirname, './src/app'),
//...
entry: {
'core': './core/core.module',
'back-office': './back-office/back-office.module',
'front-office-1': './front-office-2/front-office-2.module',
'front-office-2': './front-office-3/front-office-3.module'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'www')
},
mode: config.env,
optimization: {
splitChunks: {
cacheGroups: {
'common-vendors': {
test: /[\\/]node_modules[\\/]/,
name: 'common-vendors',
chunks: 'initial',
minChunks: 2
}
}
},
runtimeChunk: {name: 'core'},
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {mangle: false}
})
]
},
plugins: [
new CompressionPlugin()
]
}
If you know a better solution, feel free to comment.

Related

Module Federation, React, and Apollo 3 warnings

I'm building an app with micro-frontends using webpack 5's module federation plugin. Everything was fine until I started adding react hooks into my remote app. At that point I received errors about "invalid usage of hooks", i.e. I discovered I had TWO versions of react loaded, one from the remote and one from the app consuming the remote.
That problem was solved by adding a shared key to the ModuleFederationPlugin section of my webpack config that marked React as a singleton. Now everything compiles and seems to run just fine.
However, the webpack compiler is throwing some annoying warnings at me now. Its saying:
No required version specified and unable to automatically determine one. Unable to find required version for "react" in description file (/Users/myComputer/Development/myapp/node_modules/#apollo/client/react/context/package.json). It need to be in dependencies, devDependencies or peerDependencies.
Here is what my webpack config (in the remote) looks like currently:
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin')
const deps = require('./package.json').dependencies
module.exports = {
mode: 'development',
devServer: { port: 3001 },
entry: './src/index.tsx',
output: {
path: __dirname + '/dist/',
},
module: {
rules: [
{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json'],
},
use: 'ts-loader',
},
]
},
devtool: 'source-map',
plugins: [
new ModuleFederationPlugin(
{
name: 'myRemote',
filename: 'remoteEntry.js',
exposes: {
'./App':
'./src/App/App.tsx',
},
shared: {
'react': {
singleton: true,
version: deps['react'],
},
'react-dom': {
singleton: true,
version: deps['react-dom'],
},
},
}
),
new HtmlWebpackPlugin({
template:
'./index.html',
})
]
}
The consuming app's webpack config is almost the same, especially the shared section (there are some slight differences in that it declares the remotes).
What would be the way to tell webpack that the apollo package will be getting its react dependency from somewhere else? Or if thats not the right thing to tell webpack, what is and how can I get rid of these warnings?
Fixed my own problem by changing the key version to requiredVersion

Webpack build for exisitng AngularJS project

I am looking to migrate the existing Ruby based build system in our AngularJS(1.4.X) project to Webpack. The project code is not using JS modules and being with old-school Angular code patter I am not sure how Webpack will find all the controller and factory files in the project.
Folder structure is like,
-app
- assets
- javascripts
- ctrl
- controllerA.js
- controllerB.js
-services
-serviceA.js
-serviceB.js
- angular.min.js
- angular-route.js
- main.js
Wen I use the main.js in my entry point it get copied into the build folder but none of the other files as processed by Webpack even if I use babel-loader to .js rule.
One option I can think of is to use all other files into a separate bundle file using something like
https://www.npmjs.com/package/webpack-merge-and-include-globally, but I want to know whether there is a better way of doing it.
My current webpack config is as below.
module.exports = {
context: __dirname +'/app',
entry: {
'app-portal': [
'/assets/javascripts/main.js',
'/assets/javascripts/angular.min.js',
'/assets/stylesheets/portal/style.css',
'/assets/stylesheets/portal/navbar.css',
'/assets/stylesheets/portal/animation.css',
'/assets/stylesheets/portal/bootstrap.min.css',
'/assets/stylesheets/portal/bootstrap-notify.css',
'/assets/stylesheets/portal/fontello.css',
]
},
output: {
path: __dirname + "/dist/assets",
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader:'css-loader',
options: {
sourceMap: true,
url: false,
},
},
],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './views/portal/index.html',
filename: '../index.html',
}),
new MiniCssExtractPlugin({
filename: './[name].css',
linkType: false,
ignoreOrder: false,
}),
new CopyPlugin({
patterns: [
{
from: './views/portal/**/*.*',
to: "../[name].[ext]",
globOptions: {
ignore: [
'**/index.*',
],
},
},
{
from: './assets/fonts/*.*',
to: "./[name].[ext]",
},
{
from: './assets/images/portal/*.*',
to: "./[name].[ext]",
},
{
from: './assets/theme/*.*',
to: "./[name].[ext]",
}
]
}),
],
Probably Webpack is not the right solution for me as I don;t want to change the source code as suggested in Webpack plugins and/or strategies for AngularJS
You can try something like this (we use it for running tests):
bundle.js:
const jsConext= require.context('.', true, /\.js/);
ng1Context.keys().forEach(ng1Context);
const cssConext= require.context('.', true, /\.css/);
ng1Context.keys().forEach(ng1Context);
...
entry: { 'app-portal': 'bundle.js' }
This should work in general (You might need fix order for css or in case of multiple angular modules etc.)

webpack 4 react loadable is not spliting vendor base on chucking point

I am currently using webpack 4 with react loadable to create chunks.
It actually chunks depends on the break point. However, vendor size remains same. React loadable is not supporting webpack 4 yet ? or I am missing some setup ?
css actually seems to splited into the chunk, though
{
output: {
path: 'tothe path',
publicPath: `/publicPath/`,
filename: '[name] + '.js',
chunkFilename: '[name]',
},
resolve: {
extensions: ['.js', '.json', '.css'],
alias: aliases
},
stats: {
warnings: false,
children: false,
},
optimization: {
splitChunks: {
chunks: "all",
name: true,
cacheGroups: {
common: {
name: "vendor" + ".js",
test: /[\\/]node_modules[\\/]/,
chunks: "all",
enforce: true,
reuseExistingChunk: false,
},
}
}
}
}
React-loadable doesn't work well with Webpack 4 yet,
take a look at this pull request.
There's a fork of react-loadable (by the author of PR),
but it didn't work for me either. I had a problem that some components wrapped in Loadable won't load.
#Bohdan Other
I also ran into this problem.
I found that the components can't load all import style. If I remove the style, the component will load normally.
I move all the styles to the entry file as a workaround.

How to prepare react component as external package using webpack?

I have react component hosted on github, it is simple wrapper around <table> with few features. I use webpack to build my project. This is webpack config inherited after react-create-app:
module.exports = {
bail: true,
devtool: 'source-map',
entry: {
index: paths.appIndexJs
},
externals : {
react: 'react'
},
output: {
path: paths.appBuild,
filename: 'index.js',
publicPath: publicPath,
library: "ReactSimpleTable",
libraryTarget: "umd"
},
resolve: {
fallback: paths.nodePaths,
extensions: ['.js', '.json', '.jsx', '']
},
module: {
preLoaders: [
... preloaders ...
],
loaders: [
... loaders ...
]
},
plugins: [
new webpack.DefinePlugin(env),
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.DedupePlugin(),
new ExtractTextPlugin('static/css/[name].[contenthash:8].css'),
],
node: {
fs: 'empty',
net: 'empty',
tls: 'empty'
}
};
And now when I used my component in another project (as external package from github) I've got warnings related to that issue: https://facebook.github.io/react/warnings/dont-call-proptypes.html .
If I remove externals from webpack config everything works fine, but my output file is about 130kB (sic!). With externals in webpack config react is excluded from index.js and it weights about 35kB (non minified). But I got warnings :/
I wonder how to exclude react from build and mitigate warnings. I don't use PropTypes in any unusual way, so advices from https://facebook.github.io/react/warnings/dont-call-proptypes.html are not relevant.
I just want to let users import my component and assume, that they have react already in dependencies...

Webpack - react works even when it's not in 'vendors'

I'm working with Webpack+React and I'm using the CommonsChunkPlugin. The thing is that react works even when I don't put it in the 'vendors' entry (same for other packages). Does that make sense?
My config looks like this:
config.entry.vendors = ['mobx', 'jquery', 'highcharts', 'react-highcharts', 'moment', 'numeral', 'jquery-ui', 'jquery.cookie', 'lodash', 'jquery.waitforimages', 'raven-js'];
config.module.loaders = config.module.loaders.concat([
{
test : /\.less$/,
loader: ExtractTextPlugin.extract({
fallback: "style-loader",
loader : "css-loader?sourceMap!postcss-loader!less-loader?sourceMap"
})
}
]);
config.plugins = config.plugins.concat([
new ExtractTextPlugin('[name]-[chunkhash].min.css'),
new webpack.optimize.UglifyJsPlugin({
minimize : true,
mangle : false, // { except: ['$super', '$', 'exports', 'require'] },
compressor: {
warnings : false,
screw_ie8: true
},
sourceMap : true
}),
new StatsPlugin('webpack.stats.json', {
source : false,
modules: false
}),
new webpack.optimize.CommonsChunkPlugin({name: 'vendors', filename: 'vendors-[chunkhash].min.js'}),
new WebpackMd5Hash(),
new ManifestPlugin(),
new InlineManifestWebpackPlugin({
name: 'webpackManifest'
})
]);
The output value of webpack is:
output: {
filename : '[name].bundle.js',
publicPath : '/',
path : paths.dist,
sourceMapFilename: "[name].js.map",
},
CommonsChunkPlugin is smart enough, you didn't specify chunks property for CommonsChunkPlugin, that means that plugin will try to go through all your entries and move common parts to vendors chunk and then into vendors-[chunkhash].min.js file.
e.g. you have 2 entry points: index.js, signin.js and in both you have next code:
const React = require('react')
const ReactDOM = require('react-dom')
So with configuration like
entry: {
app: './index.js',
signin: './signin.js',
vendor: ['react']
},
*****
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: 'vendor-[chunkhash].js'
})
]
you will still have react-dom in vendor chunk.
On another hand if you have third entry point without requiring react-dom, only react will be moved to vendor chunk.
But even in this case you will still have react-dom included into first two entry chunks.
The thing is that react works event when I don't put it in the 'vendors' entry (same for other packages).
So react will work in any case, the only difference will be, will react chunk be moved to vendors or not, if not, it still will be included in your entry point file.
Hope it helps.

Resources