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.
Related
I have a static Javascript project (no react, vue, etc.) where I am trying to transpile, bundle, and minify my js with webpack. I would like to have bundle.js on my layout page which will include a bunch of global js that runs on all pages and then a page_x.js file that will be on individual pages as needed. The bundle.js file might consist of several other files and should be transpiled to es5 and minified.
With my current setup, the files are running twice. I'm not sure how to fix this. I want the file included globally but also want to be able to call the function as needed. If I delete the import statement from page.js I get the console error, "doSomething" is undefined. If I only include page.js on page.html and not on _layout.html common.js is only logged out on page.html. I want "common" to be logged once on every page and I want doSomething() to be available only on page.js.
Here is an example of it running twice:
common.js
console.log("common");
export function doSomething() {
console.log("do something");
}
page.js
import {doSomething} from "/common.js";
$(button).click(doSomething);
The expected output on page load (before clicking anything) would be:
"common"
Instead I'm seeing
"common"
"common"
My webpack.config.js file is as follows:
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const RemoveEmptyScriptsPlugin = require("webpack-remove-empty-scripts");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const WebpackWatchedGlobEntries = require("webpack-watched-glob-entries-plugin");
const CssnanoPlugin = require("cssnano");
const TerserPlugin = require("terser-webpack-plugin");
const dirName = "wwwroot/dist";
module.exports = (env, argv) => {
return {
mode: argv.mode === "production" ? "production" : "development",
entry: WebpackWatchedGlobEntries.getEntries(
[
path.resolve(__dirname, "src/scripts/**/*.js"),
path.resolve(__dirname, "src/scss/maincss.scss")
]),
output: {
filename: "[name].js",
path: path.resolve(__dirname, dirName)
},
devtool: "source-map",
module: {
rules: [
{
test: /\.s[c|a]ss$/,
use:
[
MiniCssExtractPlugin.loader,
"css-loader?sourceMap",
{
loader: "postcss-loader?sourceMap",
options: {
postcssOptions: {
plugins: [
CssnanoPlugin
],
config: true
},
sourceMap: true
}
},
{ loader: "sass-loader", options: { sourceMap: true } },
]
},
{
test: /\.(svg|gif|png|eot|woff|ttf)$/,
use: [
"url-loader",
],
},
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"]
}
}
}
]
},
plugins: [
new WebpackWatchedGlobEntries(),
new CleanWebpackPlugin(),
new RemoveEmptyScriptsPlugin(),
new MiniCssExtractPlugin({
filename: "[name].css"
})
],
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
extractComments: false,
})
]
}
};
};
Any help would be greatly appreciated.
Webpack is about building a dependency graph of your application files and finally producing one single bundle.
With your configuration, you are actually trying to use Webpack as a Multi-entry object configuration as explained in Webpack documents. The culprit here is WebpackWatchedGlobEntries plugin. For each file matched by a glob pattern, it would create a bundle which is not what you want ever. For exmaple, if you have following structure:
- src/scripts
- common.js
- some
- page1.js
- other
- page2.js
This plugin will produce multi-page application. So, you configuration:
entry: WebpackWatchedGlobEntries.getEntries(
[
path.resolve(__dirname, "src/scripts/**/*.js"),
path.resolve(__dirname, "src/scss/maincss.scss")
]),
will internally return an object as:
entry: {
"common": "src/scripts/common.js",
"some/page1": "src/scripts/some/page1.js",
"other/page2": "src/scripts/other/page2.js"
}
It means if you import common.js into page1.js and page2.js, then you are in producing three bundles and all those bundles will possess the common module which would be executed three times.
The solution really depends on how to you want to configure your bundle:
If you need to bundle as a multi-page application, then you must use splitChunk optimization that allows you to create page specific bundle while keeping shared code separate (common.js for example). Keep in mind that you do not really need to manually create a separate bundle for common.js. with split chunks, Webpack should do that automatically for you.
If you need a single bundle, you can literally go ahead and create a single bundle for entire application (most typical workflow with Webpack) and use the same bundle on each page. You can have a common function like run that can figure the code to call using URL or some unique page specific identifier. In modern SPA, that is done using routing module.
What I will suggest is to keep things simple. Do not use WebpackWatchedGlobEntries plugin. That will complicate things if you are not familiar with Webpack. Keep entry simple like this:
entry: {
// Note that you don't need common module here. It would be picked up as part of your page1 and page2 dependency graph
"page1": "src/scripts/some/page1.js",
"page2": "src/scripts/other/page2.js"
}
Then, enable the splitchunk optimization as:
optimization: {
splitChunks: {
chunks: 'all'
}
}
Again, there are multiple options to choose from. You can read more details here about preventing code duplication.
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.
We want to migrate from gulp to webpack on a rather big and old website. Gulp just concat and minify JS & CSS files at the moment. I want to migrate to webpack because another front dev made some react modules that are compiled by browserify cmd in another folder:
we work in a webapp/ folder, minified files are located here :
js/app.min.js
jslib/vendors.min.js
css/app.min.css
sources files are :
js/
file1.js
file2.js
...
jslib/
jquery-1.11.2.min.js
jquery.fancybox.js
...
jquery.very-old-dependency-not-on-npm.min.js
css/
style.css
extra.css
my goal is to keep this file split for javascript by using two files, app.js and vendors.js. These 2 files should be, ideally, ES6 files that import other files.
Actually i'm looking for a way to do a simple concatenation / minification of JS files through the 2 es6 files with imports. Webpack is checking for dependencies and to overcome the "jquery is not defined" errors i added the webpack.providePlugin and the resolve.alias part in webpack.config. But this loads jquery in my app.min.js file and i don't want it for performance purposes. I'm aware that all libs should be npm installed and required/imported in es6 context for an optimized webpack use, but some old jquery libs are not available on npm in the version i want so i have to go with the files already in the project.
vendors.js :
import "./jquery-1.11.2.min.js";
import "./jquery.fancybox.js"
app.js :
import "./file1.js"
import "./file2.js"
webpack.config.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: {
'/../../js/app': path.resolve(__dirname + '/../../js/app.js'),
'/../../jslib/vendors': path.resolve(__dirname + '/../../jslib/vendors.js'),
},
output: {
path: path.resolve(__dirname),
filename: '[name].min.js',
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
],
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
}),
],
resolve: {
alias: {
jquery: path.resolve(__dirname+'/../../jslib/jquery-1.11.2.min.js')
},
},
}
all my paths are ok (the folder location of the current gulp / future webpack is not in assets folder ) and webpack is successfully running, i just want to find a clean way to perform a simple concat / minify without including jquery in my app file before going further.
Here is an example of using jquery in an html file external
As for the names of app and vendors, you must change this part
entry: {
'/../../js/app': path.resolve(__dirname + '/../../js/app.js'),
'/../../jslib/vendors': path.resolve(__dirname + '/../../jslib/vendors.js'),
},
for this
entry: {
app: path.resolve(__dirname + '/../../js/app.js'),
vendors: path.resolve(__dirname + '/../../jslib/vendors.js'),
},
Do not add jquery to import if you want it to be added from outside. Remove jquery from vendors.js
I hope I helped.
I don't understand why this is being so complicated I want my project to have 2 separate work spaces where one is a library that will be distributed and the other will be used for testing... this is how i have the file structure
project
--engine
---math
----vec2.js
---dist
----library.js
---main.js
--sandbox
---main.js
I want to build the "engine" project with webpack and es6 modules so I get a "library" file that can be used in "sandbox".
The "engine" main file would look something like this
import vec2 from './math/vec2';
export default class Library {
constructor() {
this.vec2 = vec2;
}
}
An then the sandbox main file would look something like this
import lib from '../engine/dist/library';
const game = new lib();
The problem is when I build the "library.js" file with webpack and import it in the "sandbox" main file I can't call any of the classes therein. I get this error.
Uncaught TypeError: o.default is not a constructor
at Object.<anonymous> (library.js:1)
at e (library.js:1)
at library.js:1
at library.js:1
My webpack.config.js file looks like this
var webpack = require('webpack');
module.exports = {
context: __dirname,
entry: __dirname+"/main.js",
output: {
path: __dirname+"/dist",
filename: "library.js"
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /(node_modules)/,
loader: 'babel-loader',
query: {
presets: ['es2015']
}
}
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin()
]
};
I must be missing some configuration webpack needs or some plugin that will make this work. I simply want to build the library with webpack using es6 modules so it can be used in another project but I have no idea how to configure it. I'm using babel for transpilling es6 to es5
You need to configure output.libraryTarget. In this case the target commonjs-module is appropriate. So your output would be:
output: {
path: __dirname+"/dist",
filename: "library.js",
libraryTarget: "commonjs-module"
},
The different targets are described in the docs. And you might also want to read Guides - Authoring Libraries.
In my Angular2+TypeScript+WebPack project I would like to use / import the Esri ArcGIS JavaScript API from the following url:
https://js.arcgis.com/4.1/
so that a import Map from 'esri/Map'; would import the following module
https://js.arcgis.com/4.1/esri/Map.js
I've seen people are typically using systemjs to load these modules, however, I would prefer not to use systemjs. Is there a way to do that with webpack only?
webpack.config:
var webpack = require("webpack");
module.exports = {
entry: {
'polyfills': './app/polyfills.js',
'vendor': './app/vendor.js',
'app': './app/boot.js'
},
output: {
path: __dirname,
filename: "./prod/[name].js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: ['app', 'vendor', 'polyfills']
}),
new webpack.optimize.UglifyJsPlugin({
beautify: false,
mangle: {screw_ie8: true, keep_fnames: true},
compress: {screw_ie8: true},
comments: false
})
]
};
webpack is a module bundler not a javascript loader. It package files from local disk and don't load files from the web (except its own chunks). Use a javascript loader, i.e. script.js
https://github.com/webpack/webpack/issues/150#issuecomment-32756166
It's also worth noting you won't be able to load a module from an external source and then import using es6 syntax as the remote module needs to go through Webpack. You'll have to make sure you fetch a UMD / global script.
I followed this sample and was able to load dojo, webpack, typescript and angular 2.
Just be patient, is still hellish with imports eg:
import * as Map from 'esri/Map';
import * as Point from 'esri/geometry/Point';
import * as Circle from 'esri/geometry/Circle';