Split assets into bundles in Production with Webpack - node.js

I see on almost every production website made in React (or in almost every other framework), that there are splitted css and js bundles inserted into HTML website.
I am using Webpack 4 with this configuration:
module.exports = {
entry: "./src/client/index.js",
output: {
path: outputPath,
filename: "[name].js"
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader']
}, {
test: /\.(png|woff|woff2|eot|ttf|svg)$/,
loader: 'url-loader?limit=100000'
}
]
},
resolve: {
extensions: ['*', '.js', '.jsx']
},
devServer: {
port: 3000,
open: true,
proxy: {
'/api': 'http://localhost:8080'
}
},
plugins: [
new CleanWebpackPlugin([outputPath]),
new HtmlWebPackPlugin({
template: "./public/index.html"
})
]
};
But when I build my frontend and serve it by Express, my CSS styles are inlined by <style></style> and also included in main.js file.
How to split CSS,JS and other assets from each other (bundle.css, bundle.js, img/image1.png, etc) instead of inlining CSS and converting images into base64 format?

To create a new file for css, remove style-loader and add https://github.com/webpack-contrib/mini-css-extract-plugin
To do the same with files, remove url-loader and replace it with file-loader

Related

Vite serves up jsx files in network traffic

I'm not sure what is going on but I have a very basic Vite setup, yet I'm seeing the local server serve up all my jsx files, which takes a good 10-15s when I do a fresh reload. At first I thought this just might be the way Vite works for development but even when trying to build for production it does the same thing and doesn't minimize or uglify the files but just serves the jsx as is.
enter image description here
The following is my vite.config.js. The commented out code is other things that I've tried with no success:
export default ({mode}) => {
return defineConfig({
root: 'app',
define: {global: 'window'},
// esbuild: {
// loader: "jsx",
// minify: true,
// minifySyntax: true,
// },
// optimizeDeps: {
// esbuildOptions: {
// minify: true,
// minifySyntax: true,
// loader: {
// ".js": "jsx",
// ".ts": "tsx",
// },
// },
// },
plugins: [react()],
// build: {
// outDir: '../dist',
// minify: true,
// },
server: {
host: '127.0.0.1',
port: 3000
}
})
};
I've tried numerous configuration options and tried every rollup/vite config option I could find on stack overflow and the internet. I'd expect vite to serve only the produced index.html and generated index.jsx file, not get all my source files as is.
This project originally did use webpack, but even then I wasn't doing anything special. Here is my webpack config incase that's helpful:
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'development',
devtool: 'eval-source-map',
entry: [
'webpack-dev-server/client?http://vesta-dev.localhost.com:3000',
'webpack/hot/dev-server',
'react-hot-loader/patch',
path.join(__dirname, 'app/index.js')
],
output: {
path: path.join(__dirname, '/dist/'),
filename: '[name].[hash].js',
publicPath: '/'
},
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser',
}),
new webpack.DefinePlugin({
...
})
],
module: {
rules: [
{
enforce: 'pre',
test: /\.js$/,
exclude: [/node_modules/, /__tests__/],
loader: 'eslint-loader',
options: {
configFile: path.resolve(__dirname, '.eslintrc'),
failOnWarning: false,
failOnError: false,
emitError: false,
emitWarning: true
}
},
{
test: /\.js?$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
cacheDirectory: true,
plugins: ['react-hot-loader/babel']
}
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
{
test: /\.scss$/,
use: ['style-loader','css-loader','sass-loader?modules&localIdentName=[name]---[local]---[hash:base64:5]']
},
{ test: /\.(jpe?g|png|gif)$/i, loader: 'file-loader' },
{
test: /\.woff(2)?(\?[a-z0-9#=&.]+)?$/,
loader: 'url-loader',
options: {
limit: '10000',
mimetype: 'application/font-woff'
}
},
{ test: /\.(ttf|eot|svg)(\?[a-z0-9#=&.]+)?$/, loader: 'file-loader' },
]
}
};
Looks like this is normal for when you use vite serve as that always serves the development environment, no matter what flags you set or command line options you pass in.
I thought it was interesting that no one talks about the jsx files actually being served in the network traffic and even with all my searching never came across anyone talking about this, nor in the Vite documents so I thought something was wrong.

hot reload pug with webpack

I created a web application and i'm generating my index.html with a pug file.
The pug template needs to get data from a json file, and so far I managed to inject the data with both pug-loader and pug-html-loader.
The problem comes when I run the application with hot-reloading (using webpack-hot-middleware). the application dosen't reload when I change the json file, which is really annoying because any other file change triggers a reload, and I don't want to restart the application manually everytime I change the json file.
this is my webpack configuration:
{
context: '/src',
entry: 'index.js',
output: {
filename: 'index.js',
path: '/dist'
},
resolve: {
extensions: ['.js', '.vue', '.scss', 'json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'#': config.pwd,
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
scss: [
'style-loader',
'css-loader',
'sass-loader',
{
loader: 'sass-resources-loader',
options: {
resources: ['./src/variables.scss', './src/mixins.scss']
}
}
]
}
}
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader',
{
loader: 'sass-resources-loader',
options: {
resources: ['./src/variables.scss', './src/mixins.scss']
}
}
]
},
{
test: /\.pug$/,
use: [
{
loader: 'pug-loader',
options: {}
}
]
}
]
},
{
[
new HtmlWebpackPlugin({
template: 'index.pug',
inject: true,
data: require('./pug.json')
})
]
}
}
does any one know how i can make a change in pug.json trigger a hot reload?

Webpack not including all our js and jsx file, even those in the same directory

We are trying to upgrade our React.js application which uses WebPack to build. In our upgrades we are moving from Webpack 1.0 to 2.0 and I have made the "necessary" changes for the upgrade. It is building, and compiling, however, when I look at the files included, it is a very smaller scale of the files it was including before.
For instance, we have 34 files in our React Flux Actions directory. Some files have .js extension some .jsx. However, of the 34 files, only 1 is showing up in the build. What happened to the other 33. This one has .js extension but there are more .js files in that directory too.
What am I missing?
This is our main config file.
var path = require('path');
var webpack = require('webpack');
var StringReplacePlugin = require("string-replace-webpack-plugin");
var Environment = require('./js/environment');
module.exports = {
entry: [
'./js'
],
output: {
path: path.join(__dirname, 'build'),
filename: 'bundle.js',
},
plugins: [
new StringReplacePlugin(),
new webpack.LoaderOptionsPlugin({
options: {
tslint: {
emitErrors: true,
failOnHint: true
}
}
})
],
resolve: {
extensions: ['*', '.js', '.jsx'],
modules: [
path.join(__dirname, 'node_modules'),
path.join(__dirname, 'js'),
path.join(__dirname, 'jsx')
]
},
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loaders: ['react-hot-loader/webpack', 'babel-loader' ]
},
{
test: /js\/constants.js$/,
loader: StringReplacePlugin.replace({
replacements: [{
pattern: /localhost/g,
replacement: Environment.getBackendURL
}]
})
}]
}
};
This is our hot reload local version, I think both files are used, the one above and this one. But I am only doing "npm run build" command right now, then running "npm run local"
var path = require('path');
var webpack = require('webpack');
module.exports = {
devtool: 'eval',
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./js/index'
],
output: {
path: path.join(__dirname, 'build-hot'),
filename: 'bundle.js',
publicPath: '/build/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
// new webpack.NoErrorsPlugin()
new webpack.LoaderOptionsPlugin({
options: {
tslint: {
emitErrors: true,
failOnHint: true
}
}
})
],
resolve: {
extensions: ['*', '.js', '.jsx'],
modules: [
path.join(__dirname, 'node_modules'),
path.join(__dirname, 'js'),
path.join(__dirname, 'jsx')
]
},
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /node_modules/,
loaders: ['react-hot-loader/webpack', 'babel-loader' ]
}]
}
};
Based on Michael's answer below, there is one I did find that is different and feels like could be the reason, but I did not see anything in Webpack2 for pre-loaders. Here is it.
preLoaders: [
{
test: /\.jsx?$/,
loader: "source-map-loader"
}
],
Webpack starts looking at your entry point(s) and only includes files that are being imported, it doesn't just include every file in your project (as described in Concepts - Entry of the official docs).
As your entry point is ./js it will start with ./js/index.js (that's how Node.js and therefore webpack handles importing Folders as Modules), so you're not including every file in that directory. And if you don't import them in ./js/index.js or in its dependencies, the files won't be included at all. Presumably you don't and that's why only this one file is being included in the bundle.
This behaviour hasn't changed from webpack 1 to webpack 2. It's rather surprising that it worked differently with webpack 1, but maybe you changed something in the migration process that you aren't aware of.

Configure Webpack Loaders For a Shared Folder?

Help appreciated!
My question is how do I get webpack to compile assets from a shared folder into the appropriate content script?
I have a Chrome Extension using react-redux-chrome. Their file structure is:
-build
-content
-event
-popup
package.json
gulpfile.babel.js
manifest.json
The gulp file watches for changes and compiles the appropriate javascript, manifest, and html files into build.
Content, event, and popup all have their own webpack configs.
// content/webpack.config.js
const path = require('path');
module.exports = {
entry: [
'./content/src/scripts/index.js'
],
output: {
filename: 'content.js',
path: path.join(__dirname, '../', 'build'),
publicPath: '/'
},
resolve: {
extensions: ['', '.js', '.jsx', '.scss', '.json'],
modulesDirectories: ['node_modules'],
},
module: {
loaders: [
{
test: /\.(jsx|js)?$/,
loader: 'babel',
exclude: /(node_modules)/,
include: path.join(__dirname, 'src'),
query: {
presets: ['es2015', 'react']
}
},
{ test: /\.css$/, loader: "style-loader!css-loader" },
]
}
};
My extension has two content scripts, so my folders look like this...
-build
-content1
-content2
-shared
-event
-popup
package.json
gulpfile.babel.js
manifest.json
Everything works fine until I try to import a React module from the shared folder. The shared folder does not have a webpack config. I get the error: Unexpected token. You may need an appropriate loader to handle this file type.
How do I get webpack to compile assets from the shared folder into the appropriate content script? I tried an alias, but it did not work.
alias: {
shared: path.normalize('../../../../../../shared')
}
I had a brain fart... just needed to make a new loader for the directory. In the content1/webpack.config.js and content2/webpack.config.js files...
{
test: /\.(jsx|js)?$/,
loader: 'babel',
exclude: /(node_modules)/,
include: path.join(__dirname, '../shared'),
query: {
presets: ['es2015', 'react']
}
},

Webpack not including ProvidePlugins

I have a small trial web application that I'm working on that uses the vue webpack template (https://github.com/vuejs-templates/webpack). I'm pretty new to webpack so I was assuming that I could add in to plugins a new webpack.ProvidePlugin and it would be available globally but when I do a npm run dev I get the following error:
/var/www/public/leadsStatsDashboard/liveleadstats/src/components/Hello.vue
18:17 error 'd3' is not defined no-undef
Which sounds like to me that it can't find the d3 reference. I'm no sure if there's some configuration I skipped over or what but any help would be appreciated. Here is the source for my files
Webpack.dev.conf.js:
var path = require('path')
var config = require('../config')
var utils = require('./utils')
var webpack = require('webpack')
var projectRoot = path.resolve(__dirname, '../')
module.exports = {
plugins: [
new webpack.ProvidePlugin({
d3: 'd3',
crossfilter: 'crossfilter',
dc: 'dc'
})
],
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
publicPath: config.build.assetsPublicPath,
filename: '[name].js'
},
resolve: {
extensions: ['', '.js', '.vue'],
fallback: [path.join(__dirname, '../node_modules')],
alias: {
'src': path.resolve(__dirname, '../src'),
'assets': path.resolve(__dirname, '../src/assets'),
'components': path.resolve(__dirname, '../src/components'),
'd3': path.resolve(__dirname, '../bower_components/d3/d3.min.js'),
'crossfilter': path.resolve(__dirname, '../bower_components/crossfilter/crossfilter.min.js'),
'dc': path.resolve(__dirname, '../bower_components/dcjs/dc.js')
}
},
resolveLoader: {
fallback: [path.join(__dirname, '../node_modules')]
},
module: {
preLoaders: [
{
test: /\.vue$/,
loader: 'eslint',
include: projectRoot,
exclude: /node_modules/
},
{
test: /\.js$/,
loader: 'eslint',
include: projectRoot,
exclude: /node_modules/
}
],
loaders: [
{
test: /\.vue$/,
loader: 'vue'
},
{
test: /\.js$/,
loader: 'babel',
include: projectRoot,
exclude: /node_modules/
},
{
test: /\.json$/,
loader: 'json'
},
{
test: /\.html$/,
loader: 'vue-html'
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url',
query: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url',
query: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
eslint: {
formatter: require('eslint-friendly-formatter')
},
vue: {
loaders: utils.cssLoaders()
}
}
Hello.vue
<template>
<div id="pieChartContainer">
</div>
</template>
<script>
export default {
data () {
return {
// note: changing this line won't causes changes
// with hot-reload because the reloaded component
// preserves its current state and we are modifying
// its initial state.
msg: 'Hello World! This is a test'
}
},
ready () {
console.log(d3.version)
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1 {
color: #42b983;
}
</style>
Your error isn't emitted from webpack, but from eslint.
I think the webpack part works as it should, in fact!
no-undef complains that you are using the global d3 without importing or defining it somewhere.
The good news is, that's easy to fix. Use any of the following three possibilities:
Just add the following block to your .eslintrc.js:
"globals": {
"d3": true
}
...or use eslint comments within the file that requires d3 implicitly (but that doesn't make much sense as you made it available globally and you would need to do this in every file you wish to use the global var):
/* eslint-disable no-undef */
...or you could relax the eslint rule in your .eslintrc.js config:
'rules': {
// all other rules...
'no-undef': 0
}
Additional links:
Direct link to the template's eslintrc file
The eslint 'standard' file the template extends
Further reading on eslint's no-undef rule

Resources