extracting libraries to separate file - javascript

I'm trying to extract libraries in a separate vendor.js file.
In webpack.config.js (webpack 4), I do this :
entry: {
app: './resources/assets/js/app.js',
vendor: ['vue', 'axios']
},
output: {
path: path.resolve(__dirname, 'public/js'),
filename: '[name].js',
publicPath: './public'
}
In the end, I have my app.js and vendor.js
The vendor.js file includes both vue and axios.
In my html file, at the end, I add the 2 scripts :
<script src="/js/app.js"></script>
<script src="/js/vendor.js"></script>
But app.js does not know about Vue or axios.
I get "ReferenceError: Vue is not define".
Same if putting vendor.js before app.js in the html file.
Should I add a line in my "app.js" entry point, so that I import vue and axios in some way, without adding the libraries in the "app.js" output ?

The Webpack 4 way of splitting out a "vendor" chunk is via the SplitChunksPlugin. You can configure this plugin through the optimization.splitChunks config path:
// webpack.config.js
module.exports = {
// ...
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/](vue|axios)[\\/]/,
name: 'vendor',
chunks: 'all'
}
}
}
}
};
Perhaps counter-intuitively, the way to reference these modules after this configuration remains the same; how vue and axios are loaded is decided by the webpack config; you don't have to change any code that used to import these modules. require('vue') or import axios from 'axios' will cause these modules to be referenced from your vendor.js file.
Finally, re-order your script tags so that app.js doesn't try to load things from vendor.js before vendor.js is loaded.
<script src="/js/vendor.js"></script>
<script src="/js/app.js"></script>

Related

webpack and importing js files to scripts and bundling up

I'm pretty inexperienced with webpack. I'm actually using Cloudflare Wrangler, which I believe uses Webpack 4 under the hood. In any case I have an src/index.js file and a helpers/script.js file.
my index.js file works fine, builds and compiles etc etc.
When I copy the content of helpers/script.js into the top of the index.js file, again all is good and works.
When I import script.js with either of
import human from "../helpers/script"
const human = require("../helpers/script")
then I use a webpack.config.js file that looks like
module.exports = {
target: 'webworker',
context: __dirname,
entry: './src/index.js',
mode: 'production',
module: {
rules: [
{
test: /\.index\.js$/,
use: { loader: 'worker-loader' }
}
],
},
resolve: {
extensions: ['.js'],
},
};
I can't seem, no matter what I do to get it to 'like' the imported script file.
I continually get:
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
...
Error: webpack returned an error. Try configuring `entry` in your webpack config relative to the current working directory, or setting `context = __dirname` in your webpack config.
Can anyone help me understand the requirements for being able to import a file to another js. Its amazing how hard this is to do :joy:

How to exclude CommonJS libs from production build with Webpack (vue-cli)

I have managed to do that for vue by using Webpack config externals
First I included the CDN for Vue in my html file
index.html
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12"></script>
Then I modified my webpack config
vue.config.js
module.exports = {
configureWebpack: {
// ...
externals: {
vue: 'Vue',
}
// ...
},
}
Things worked perfectly.
But when I tried with vue-class-component and vue-property-decorator, it didn't not worked as expected
index.html
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-class-component#7.2.5"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-property-decorator#9.0.0"></script>
vue.config.js
module.exports = {
configureWebpack: {
// ...
externals: {
vue: 'Vue',
'vue-class-component': 'VueClassComponent',
'vue-property-decorator': 'VuePropertyDecorator',
}
// ...
},
}
I noticed that the names of these files are different, end with .common.js and .umd.js
vue-class-component.common.js
vue-property-decorator.umd.js
Then I tried
vue.config.js
module.exports = {
configureWebpack: {
// ...
externals: {
vue: 'Vue',
'vue-class-component': 'commonjs2 vue-class-component',
'vue-property-decorator': 'umd vue-property-decorator',
}
// ...
},
}
But it did not work as well
Below are how I import these in my src/. Scripts are written in typescript
import Vue from 'vue'
// ...
import { Component } from 'vue-property-decorator'
Anyone knows how to handle externals in webpack with .common.js and .umd.js? Many thanks!
I don't think problem is necessarily in Webpack config...
If I try to load https://cdn.jsdelivr.net/npm/vue-class-component#7.2.5 it gives me Original file: /npm/vue-class-component#7.2.5/dist/vue-class-component.common.js which is CommonJS build - browser will not handle that. Try use link to a "browser compatible" build like https://cdn.jsdelivr.net/npm/vue-class-component#7.2.6/dist/vue-class-component.min.js
vue-property-decorator should be fine as UMD module should work in the browser...
BTW whats the point of all this? Why not let Webpack do its thing ? Its always better do download one big JS file then multiple smaller...

Bootstrap 5 bundled with Webpack doesn't work if included in multiple endpoints

Here is a quick repo I've created to demonstrate the issue: https://github.com/Simpleqode/bootstrap-5-webpack-test.
Basically I have a simple webpack config file with two endpoints one of which imports the whole of Bootstrap, while the second one imports only the Dropdown component to do some magic later. I have Split Chunks plugin in place to move the shared code to a separate file (though the problem persists even if I disable the Split Chunks plugin).
index.js:
import 'bootstrap';
dropdown.js:
import { Dropdown } from 'bootstrap';
webpack.config.js:
const path = require('path');
module.exports = {
entry: {
index: './src/index.js',
dropdown: './src/dropdown.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
};
If you try to run npm run build to compile the files, then open index.html in your favourite browser, you will notice that the dropdown component doesn't work. Now try to remove import { Dropdown } from 'bootstrap'; from dropdown.js or import 'bootstrap'; from index.js and recompile the files - everything will work like a charm. I am new to Webpack and Bootstrap 5 is still an alpha so I am struggling to understand if it's me doing something wrong, Bootstrap or Webpack.

Plotly with webpack

I have installed plotly using npm i plotly.js
Added the line import 'plotly.js/dist/plotly' to my plotly import file
Then in webpack followed the instructions here to bundle the files client side.
Added in a custom js file to test plotly
Then added in the plotly scripts to my html page with the package coming first then my custom js.
However I get the error message ReferenceError: Plotly is not defined.
To test I was using the javascript code from this example. I can get it working when I save the file locally found on the plotly site here but not with webpack.
Is there something I am missing or doing wrong? My other packages seem to work fine and I can see plotly.js has successfully been added into the relvent folder client side.
webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: {
uibundles: path.resolve(__dirname, 'frontend.js'),
plotly: path.resolve(__dirname, 'plotlyimport.js'),
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'public/js')
},
plugins: [new MiniCssExtractPlugin({
filename: '../css/[name].css',
})],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
},
{
test: /\.js$/,
loader: 'ify-loader'
},
]
}
};
You probably need to use webpack resolve (here) to add the details
Could you try this:
resolve: {
modules: ['node_modules'],
extensions: ['.js']
},
It seems like you need to use webpack externals to solve this issue.
webpack externals : Prevent bundling of certain imported packages and instead retrieve these external dependencies at runtime.
For example, to include plotly from a CDN instead of bundling it:
index.html
<script src="../plotly.js"></script>
webpack.config.js
module.exports = {
//...
externals: {
plotly: 'plotly'
}
};
This leaves any dependent modules unchanged, i.e. the code shown below will still work:
var Plotly = require('plotly.js');
..
..
Plotly.newPlot('myDiv', data, layout, config );
Refer to webpack externals for more details.

Webpack and toastr

I am trying to load and bundle toastr as a dependency using webpack.
here is the entire webpack config file
var path = require('path');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var webpack = require('webpack');
const DEVELOPMENT = process.env.NODE_ENV === 'development';
const PRODUCTION = process.env.NODE_ENV === 'production';
module.exports = {
entry: {
main: './wwwroot/js/mainEntry.js',
vendor: ['jquery', 'tether',
'bootstrap', 'jquery-colorbox',
'jquery-confirm', 'jquery-validation',
'jquery-validation-unobtrusive',
'toastr', 'jquery.nicescroll',]
},
output: {
filename: '/js/[name].js',
path: path.resolve(__dirname, 'wwwroot'),
},
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
},
{
test: /\.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
loader: 'file-loader?name=[name].[ext]&publicPath=/fonts/&outputPath=/fonts/'
},
{
test: /\.(png|jpe?g|gif|ico)$/,
loader: 'file-loader?name=[name].[ext]&publicPath=/images/&outputPath=/images/'
}
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "vendor",
// (the commons chunk name)
filename: "/js/vendor.js",
// (the filename of the commons chunk)
minChunks: 2,
}),
new ExtractTextPlugin({
filename: 'css/[name].min.css'
}),
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
],
};
and my entry js file as
//JS
import 'jquery-colorbox';
import 'slick-carousel';
import toastr from 'toastr';
//CSS
import './../../node_modules/bootstrap/dist/css/bootstrap.css';
import './../../node_modules/slick-carousel/slick/slick.css';
import './../../node_modules/jquery-colorbox/colorbox.css';
import './../../node_modules/toastr/build/toastr.css';
import './../../node_modules/jquery-confirm/css/jquery-confirm.css';
import './../../node_modules/font-awesome/css/font-awesome.css';
import './../../Modules/Components/Menu/menu.css';
import './../../wwwroot/css/lahuritv.css';
The bundle is created without any errors. And I can see that the toastr script is included in the bundle when I look at the output bundle. But the problem is that the variable toastr is not available in the browser window.
I tried looking for similar issue but couldn't find any. This is my first time trying to learn webpack. Any help is appreciated.
Simple solution :
//app.js
import toastr from 'toastr'
window.toastr = toastr
Webpack does not expose the modules you import, it bundles your code together with the needed dependencies, but they are still modular and have their own scope. Webpack does not just blindly combine the sources of all the files and certainly does not make everything global. If the entry file you posted is the whole file, then you're not doing anything at all.
Instead with webpack you would do the work in the JavaScript files you bundle. So to speak, all you include in your HTML are the bundles created by webpack, you don't include other scripts that try to access something in the bundle, but you rather do it directly in your bundle.
Of course you could expose it to window by explicitly defining it: window.toastr = toastr after importing toastr, but polluting the global scope is generally not a good idea and it's no different from just including toastr in a <script> tag. If the bundle is just supposed to be used as a library you could have a look at Authoring Libraries. But I think you're just doing a regular web app and you should get all the code together to be bundled by webpack and not rely on it in another <script> tag.
You should go through the Getting Started guide of the official docs. The example is very tiny (creates one DOM element) but shows you the concept of webpack apps and also uses an external dependency.

Categories