Webpack: split vendor and app code - javascript

I setup React+Webpack project. It take 60s to build an initial bundle, and 1s to append incremental change but I even don't have my application code yet! Seems that bundle of node_modules is very expensive. I tried splitting using common chunks, but it didn't give performance improvement.
How can I make Webpack bundle node_modules only once, and even don't watch for changes in this dir?

If that's a problem, you don't need to bundle external dependencies in your project. You can add them as external dependencies. For that, of course, you should add the scripts to your page.
Let's say you add the react and react-dom scripts to your page, in the webpack config file you could add this:
{
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
}
}
What this tells webpack is every time you require('react') or webpack will return a global variable called React. Same thing if you require('react-dom')
And for every loader, you should either include the files you want or exclude the files you don't need.
Here's an example excluding node_modules:
{
module: {
loaders: [
{
test: /\.jsx?/,
exclude: /node_modules/,
loader: 'babel'
}
]
}
}
Or even more performant, you can just include the files you need:
{
module: {
loaders: [
{
test: /\.jsx?/,
include: './src',
loader: 'babel'
}
]
}
}

Related

what's the workflow of style-loader and css-loader

I'm new to webpack, still a little bit confused that how webpack cooperate with loaders. Let's we have below typescript file index.ts:
//index.ts
import "bootstrap/dist/css/bootstrap.css";
...
// typescript code
and below is the webpack config file:
module.exports = {
mode: "development",
entry: "./src/index.ts",
output: { filename: "bundle.js" },
resolve: { extensions: [".ts", ".js", ".css"] },
module: {
rules: [
{ test: /\.ts/, use: "ts-loader", exclude: /node_modules/ },
{ test: /\.css$/, use: ["style-loader", "css-loader"] }
]
}
};
Below is my personal thought on how webpack works with loaders, please correct me if I'm wrong:
Step 1-Webpack encounter index.ts, so it passes this file to ts-loader, and ts-loader read the file and pass it to ts compiler, ts compiler generates js code file index.js and pass back to ts-loader, then ts-loader passes index.js back to webpack.
Step 2- Webpack reads index.js and needs to resolve the css file, so Webpack passes the task to css-loader, so css-loader reads the css file as a long long string, then passes the task to style-loader, which creates js code that can be embedded in tags in the index.html file.
Step 3- bundle.js is ready, and client sends a http request to get index.html, and the bundle.js is fetched and create a <style> tags to include all css styles.
Is my above understanding correct? If yes, below is my questions:
Q1-after style-loader generates js code, does it pass those js code back to css-loader, then css-loader passes received js code to webpack? or style-loader pass generated js code to webpack directly?
Q2- in the webpack config file:
...
{ test: /\.css$/, use: ["style-loader", "css-loader"] }
...
it seems that the style-loader is used first, then css-loader steps in( I have tried this approach, it worked, not sure why it worked)
isn't that the css-loader should start to work first then style-loader as:
...
{ test: /\.css$/, use: ["css-loader", "style-loader"] }
...
Is my above understanding correct?
Yes
Q1-after style-loader generates js code, does it pass those js code back to css-loader, then css-loader passes received js code to webpack? or style-loader pass generated js code to webpack directly?
Answer: style-loader pass generated js code to webpack directly
Q2 it seems that the style-loader is used first, then css-loader steps in,
It can seem wrong. But its one of those things you need to read the docs for. The last thing to process it is mentioned at the top of the array. Personally I don't think the other way around would be any more intuitive.

How to get Webpack to use already existing source maps when generating source maps?

I have JavaScript code and source maps generated from TypeScript code (using tsc).
I then have a second compilation step which bundles the code using webpack.
I have enabled source maps in webpack.config.js:
module.exports = {
devtool: "source-map"
}
The generated source map isn't entirely right.
Webpack is not taking into account the existing source maps that have been generated from TypeScript code.
This results in a mapping to the JavaScript code instead of the TypeScript code.
How can I get the Webpack source map to include existing mapping?
EDIT:
After renaming my question, and searching for my renamed question on Google, I found an answer.
You can use a preloader with webpack called source-map-loader: https://webpack.js.org/loaders/source-map-loader/
However, after installing source-map-loader and updating webpack.config.js to the following, the existing source maps are still not used:
module.exports = {
devtool: "source-map",
module: {
rules: [
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre"
}
]
}
}
My guess is that because the files my existing source map point to are located outside the entry directory in webpack.config.js, they are ignored...?
If you transpile the typescript as part of webpack, you will get the source maps with it.
devtool: 'source-map',
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
use: ([
{
loader: 'awesome-typescript-loader',
options: { configFileName: 'tsconfig.json' }
},
you need devTool: 'source-map' in webpack
and the "sourceMap": true in tsconfig.json
devtool: 'cheap-module-eval-source-map',
provides a faster build to generate source maps in development. But it will put the source mapping inline. So not for production.
So the big question. Why have a step separated from webpack?
If you use AOT compilation with angular (with the ngc command from #anguler/compiler), and produce .map files in the aot folder, then you want to reuse the map files. I can tell you I tested it to work with the solution below.
Then this will make it work:
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre"
},
And it is important you have sourceMap: true in tsconfig, and in the minimizer if you use one:
optimization: {
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
sourceMap: true, // Must be set to true if using source-maps in production
terserOptions: {
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
}
I have a tsconfig.json in development
a tsconfig_ao1.json for ngc command
I have a tsconfig_ao2.json to compile with aot the main.ts using the aot folder. And I use ngc outside of webpack because I could not use #ngtools/webpack inside webpack without issues.
If you are doing something else, you ened to understand the source-map-load will only load source maps if the files it test matches has source maps, and if the files art part of the tree loaded from the entry. There must be an import from the main.ts to the file that is source mapped.

Set up Webpack and Babel to transpile / polyfill older code

I have an entire legacy AngularJS 1.x application that used to run through gulp and babel. We are transitioning to the newer Angular 2+ but I'm running into an issue trying to get Webpack to actually find and compile the legacy files. I've tried following instructions which are all almost identical like this video:
https://www.youtube.com/watch?v=H_QACBSqRBE
But the webpack config simply doesn't do anything to the existing files. Is there a way to grab a WHOLE FOLDER of older components, that DO NOT have any imports or exports? I feel like entry is supposed to follow the import dependency path but that just doesn't exist for older AngularJS 1.x projects.
Errors are NOT being thrown during the build it just...doesn't transpile or polyfill.
example of what that section of the config looks like:
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
}
]
We did this recently. We created "dummy" entry point files to avoid having to change all of our AngularJS files to have require/import statements.
./entry-points/feature1.ts
export const importAll = (r: any): void => {
r.keys().forEach(r);
};
importAll(require.context('./app/feature1', true, /module\.js$/));
importAll(require.context('./app/feature1', true, /(^(?!.*(spec|module)\.js).*\.js)$/));
webpack.config.js
entry: {
'feature1': './entry-points/feature1.ts'
}
More detail here

Why does webpack have separate loaders for loading css and injecting it to the website?

Why does webpack have separate loaders for loading css (css-loader) and injecting it to the website (style-loader), if first is useless without the second?
Could you point out a scenario in which I'd use css-loader without style-loader?
The style-loader loader is just one of many different ways to ultimately have your styles included on your page. For example, you could use the ExtractTextPlugin to generate a .css file separate from your bundle file:
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: ['css-loader']
})
}
]
},
plugins: [
new ExtractTextPlugin({
filename: 'app.css'
})
]
This gives you the flexibility to load your css separately from the rest of your bundle, so you can prevent FOUC.

Webpack 2: How do I generate a bundle.js from Bootstrap premade template?

I want to generate a bundle.js from this pre-esxisting bootstrap template (it uses less) https://github.com/BlackrockDigital/startbootstrap-sb-admin-2
I tried to generate it but I failed since the styles are never generated and the bundle.js is empty as you can see in the "dist" folder(This was expected since the index.js which is the entry point is empty as well. https://github.com/juanlet/webpack . What should I do in order for webpack2 to include all the js,css and less files that came with the template and put it in a bundle.js?. Should I include every file on the index.js entry file?. I'm running out of ideas. Any article, documentation or instruction will be very welcomed. Thank you very much.
If you want to build this out with webpack, your first step is actually using whatever your entry point is to import or require other libs/files.
So, for example, if your entry point in your wepback.config.js is
entry: {
bundle: './src/js/api/index.js',
vendor: VENDOR_LIBS
},
Then that file needs to contain imports that you wish to include in that file. And then those files include other files and so on, until you have all your files bundled up through the root of your tree (index). In a very simple way, this is what webapack does: it imports/requires your files, bundles them, and loads them depending on your configuration.
In order to load/compile your LESS, you will either have to include it as an import in your JS files, or you could also use extract-text-webpack-plugin to generate a separate CSS bundle.
This is the best overview I can give to this question since I don't know the exact way your want to take your code and bundle it. Feel free to ask questions if you have them, and I will edit my answer to try and help answer them.
EDIT: This is an example of an older config I have extracting SASS into it's own file. It's using v1, but it more or less works the same for webpack 2. I just don't have an example with me right now: (Here is the documentation for using extract in v2. A little different, but not too much).
module.exports = {
devtool: 'eval',
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'babel-polyfill',
'./app/index',
],
resolve: {
extensions: ['', '.js']
},
output: {
path: path.join(__dirname, 'public'),
filename: 'bundle.js',
publicPath: '/public/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new ExtractTextPlugin('public/app.css'),
new DashboardPlugin(dashboard.setData)
],
module: {
loaders: [{
test: /\.js$/,
loaders: ['react-hot', 'babel'],
include: path.join(__dirname, '..', 'app')
},
// JSON
{
test: /\.json$/,
loaders: ['json-loader']
},
// Img
{
test : /\.(png|jpg|svg|eot|ttf|woff|raw)$/,
loader: 'url-loader?limit=4096'
},
// Sass
{
test: /\.scss$/,
loaders: [ 'style', 'css?sourceMap', 'postcss', 'sass?sourceMap' ]
}]
},
postcss: [autoprefixer({ browsers: ['last 2 versions'] })],
}
MAJOR EDIT:
An example repo exists here: https://github.com/thesublimeobject/webpack2example
Running webpack will bundle up your files, imported from an index.js file I created. This bundles all the external libraries which I installed via npm and removed them from the index.html file. I did not test any of the code that was generated since that's way beyond an answer to this question. Your LESS will also be bundled into the dist folder as a separate file you will need to provide a link to in the HTML (that's one thing I forgot to do is add links to the /dist, but I'm sure you can do that.

Categories