I have a very simple project I'm using to test out webpack. When running against my code, I get 2 output files, 0.bundle.js and bundle.js.
How do I prevent this and get webpack to only output a single js file?
Folder Structure
>- dist
>- node_modules
v- src
v- libs
BlackTriangle.js
app.js
index.html
main.js
package.json
webpack.config.js
webpack.config.js
var path = require("path"),
webpack = require("webpack");
module.exports = {
entry: "./src/main.js",
devtool: "source-map",
resolve: {
extensions: [ ".js", ],
modules: [ "node_modules", ],
alias: {
"BlackTriangle" : "./libs/BlackTriangle",
},
},
output: {
path: path.join(__dirname, "dist"),
filename: "main.js",
},
module: {
rules: [
{
test: /\.js$/,
loader: "babel-loader",
options: {
presets: ["es2015"],
},
},
],
},
};
index.html
<!DOCTYPE html>
<html>
<head>
<base href="/">
<meta charset="utf-8">
<title>Webpack Black Triangle</title>
<link rel="stylesheet" type="text/css" href="main.css">
<script type="text/javascript" data-main="main" src="node_modules/requirejs/require.js"></script>
</head>
<body>
<div id="triangle" class="BlackTriangle">
<div class="BlackTriangle-inner"></div>
</div>
</body>
</html>
main.js
// Configure requirejs to work with external libraries
require.config({
//
baseUrl: "",
paths: {
"BlackTriangle" : "libs/BlackTriangle",
},
});
(function() {
"use strict";
// Call the main function when the page load has completed
if (document.readyState == "complete") {
main();
}
else if (window.addEventListener) {
window.addEventListener("load", main, false);
} else if (window.attachEvent) {
window.attachEvent("onload", main);
}
else
{
var oldOnload = window.onload;
window.onload = function() {
oldOnload();
main();
};
}
function main() {
require([ './app' ], function(app) {
app.init();
});
}
})();
app.js
define((require) => {
'use strict';
return {
init: () => {
const BlackTriangle = require("BlackTriangle");
const triangle = new BlackTriangle('#triangle');
window.setInterval(
() => {
triangle.rotate(1);
triangle.render();
},
20
);
},
};
});
BlackTriangle.js
define((require) => {
const blackTriangle = function(selector) {
this.angle = 0;
this.innerEl = document.querySelector(selector).querySelector('.BlackTriangle-inner');
};
blackTriangle.prototype.rotate = function(amount) {
this.angle = (this.angle + amount) % 360;
};
blackTriangle.prototype.render = function() {
this.innerEl.style.transform = `rotate(${this.angle}deg)`;
};
return blackTriangle;
});
You can force webpack to create only one chunk by using LimitChunkCountPlugin plugin:
plugins: [
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1, // disable creating additional chunks
})
],
Using define instead of require fix the problem on my project (thanks #GetFuzzy)
Before:
require(["jquery"], function($) {
...
});
After:
define(["jquery"], function($) {
...
});
Related
After using Webpack for lazy-loading (green coding) my vanilla.js client side router fails on advanced routes (.../blog/2 as one example) because the necessary .js file is blocked as described in the title. By using a button-link everything is fine ( data-btnref="navlink" ) but by direct enter the address in the browser it replies with the error.
GET http://localhost:3000/blog/main8f1360562bd664fccb25.js [HTTP/1.1 404 Not Found 3ms]
The resource from “http://localhost:3000/blog/main8f1360562bd664fccb25.js” was blocked due to MIME type (“text/html”) mismatch (X-Content-Type-Options: nosniff).
Loading failed for the <script> with source “http://localhost:3000/blog/main8f1360562bd664fccb25.js”.
I know that this is an advanced question but maybe some professional coder likes my "self-learned" approach to handle things and help me out. I cleaned my approach as far as i can.
code:
index.html:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Client side routing with vanilla.js + webpack lazy load</title>
</head>
<body>
<header>
<nav id="nav">
Home
Blog
Contact
</nav>
</header>
<main id="main"></main>
</body>
</html>
index.js:
import { navigateTo } from "./js/functionality/router.js";
document.addEventListener("DOMContentLoaded", () => {
document.body.addEventListener("click", e => {
switch (e.target.dataset.btnref) {
case ("navlink"):
e.preventDefault();
navigateTo(e.target.href, "navlink");
break;
default:
break;
};
});
navigateTo(location.pathname);
});
router.js:
const Home = () => import('../views/home');
const Blog = () => import('../views/blog');
const Blogpost = () => import('../views/blogpost');
const Contact = () => import('../views/contact');
const Login = () => import('../views/login');
const PageNotFound = () => import('../views/404');
export const navigateTo = (url, btnref) => {
switch (btnref) {
case ("navlink"):
const raw_url = url.split("/");
if (raw_url.length > 4) {
const corrected_url = "/blog/" + raw_url[raw_url.length - 1];
history.pushState(null, null, corrected_url);
} else {
history.pushState(null, null, url);
};
break;
default:
break;
};
router();
};
const router = async () => {
const routes = {
"/": Home,
"/blog": Blog,
"/blog/:id": Blogpost,
"/contact": Contact,
"/login": Login,
"/404": PageNotFound,
};
let match;
if (routes[location.pathname]) {
match = await routes[location.pathname]().then(m => { return m.default() });
} else {
if (location.pathname.split("/")[1] === "blog") {
const id = location.pathname.split("/")[2];
match = await routes["/blog/:id"]().then(m => { return m.default(id) });
} else {
match = await routes["/404"]().then(m => { return m.default(`${location.pathname}`) });
};
};
document.getElementById('main').innerHTML = match;
};
window.addEventListener("popstate", router);
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
mode: 'development',
entry: {
main: path.resolve(__dirname, 'src/index.js'),
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name][contenthash].js',
clean: true,
assetModuleFilename: '[name][ext]',
},
devtool: 'source-map',
devServer: {
static: {
directory: path.resolve(__dirname, 'dist'),
},
port: 3000,
open: true,
hot: true,
compress: true,
historyApiFallback: true
},
module: {
rules: [
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader',
],
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{
test: /\.(svg|png|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
],
},
plugins: [
new HtmlWebpackPlugin({
title: 'Webpack App',
filename: 'index.html',
template: 'src/template.html',
}),
new BundleAnalyzerPlugin(),
]
}
The solution to this issue is to add <base href="/" /> inside the <head> of the template.html. This fixed the 404 - error:
The resource from “http://localhost:3000/blog/main8f1360562bd664fccb25.js” ....
to the correct path:
http://localhost:3000/main8f1360562bd664fccb25.js
This problem is also known as "Webpack chunks fail to load in nested routes #333 - GitHub".
Apologies if this is a really basic question, but I've been struggling to find an answer.
I am trying to call a method/function inside a class client side but when I try to call it, I get TypeError testFunction is not a function
The entry point is client.ts, which (to keep it simple for now) has a single export:
export * from "./Test"
Test.ts has a class and function:
export class TestClass {
public testFunction() {
// do stuff
}
}
My webpack.config.js is as follows:
var webpack = require('webpack');
var argv = require('yargs').argv;
var debug = argv.debug !== undefined;
var config = [
{
entry: {
client: [
__dirname + '/src/scripts/client.ts'
]
},
mode: debug ? 'development' : 'production',
output: {
path: __dirname + '/dist/web/scripts',
filename: '[name].js',
libraryTarget: 'umd',
library: 'webpack',
publicPath: '/scripts/'
},
externals: {},
devtool: 'source-map',
resolve: {
extensions: [".ts", ".tsx", ".js"],
alias: {}
},
target: 'web',
module: {
rules: [{
test: /\.tsx?$/,
exclude: [/lib/, /dist/],
loader: "ts-loader",
options: {
configFile: "tsconfig-client.json"
}
},
{
test: /\.(eot|svg|ttf|woff|woff2)$/,
loader: 'file-loader',
options: {
name: 'public/fonts/[name].[ext]'
}
},
{
test: /\.s[ac]ss$/i,
use: [
'style-loader',
'css-loader',
'sass-loader',
]
}
]
},
performance: {
maxEntrypointSize: 400000,
maxAssetSize: 400000,
assetFilter: function (assetFilename) {
return assetFilename.endsWith('.js');
}
}
}
];
module.exports = config;
I am them embedding this in the html page:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>My Bookings</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/styles/main.css">
</head>
<body>
<script src="/scripts/client.js"></script>
<script type='text/javascript'>
webpack.TestClass.testFunction();
</script>
</body>
</html>
Any pointers on what I am doing wrong would be great. If I browse the generated client.js I can see the class and method, so I am at a loss!
/*!*****************************!*\
!*** ./src/scripts/Test.ts ***!
\*****************************/
/*! namespace exports */
/*! export TestClass [provided] [no usage info] [missing usage info prevents renaming] */
/*! other exports [not provided] [no usage info] */
/*! runtime requirements: __webpack_require__.r, __webpack_exports__, __webpack_require__.d, __webpack_require__.* */
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "TestClass": () => /* binding */ TestClass
/* harmony export */ });
var TestClass = /** #class */ (function () {
function TestClass() {
}
TestClass.prototype.testFunction = function () {
// do stuff
};
return TestClass;
}());
The problem is that you are trying to call a non-static method directly from TestClass instead of creating a new instance of TestClass first.
You need to either call the testFunction method on a new instance of TestClass or declare the testFunction method as a static method.
i have a node js function -
const BpmnModdle = require('bpmn-moddle')
var bpmn = function () {
var bm = new BpmnModdle()
console.log(bm)
}
module.exports = bpmn
I want to call this function in pure vanilla js.
What i have tried so far-
i have created a fileData javascript file in which i have tried to call bpmn function
fileData.js
function createData(xml, node) {
var bp = bpmn();
console.log(bp)
}
i have tried to bundle both in webpack. Where my webpack config file is
module.exports = {
entry: [
'./javascript/examples/editors/js/bpmn.js',
'./javascript/examples/editors/js/app.js',
'./javascript/examples/editors/js/deletes.js',
'./javascript/examples/editors/js/fileData.js',
'./javascript/examples/editors/js/jsonData.js',
'./javascript/examples/editors/js/new.js',
'./javascript/examples/editors/js/open.js',
'./javascript/examples/editors/js/save.js',
'./javascript/examples/editors/js/saveas.js',
'./javascript/examples/editors/src/js/mxClient.js',
'./node_modules/bpmn-moddle/dist/index.js'
],
output: {
path: __dirname,
publicPath: '/',
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "script-loader"
}
},
{
test: /\.css$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader",
options: {
modules: true,
importLoaders: 1,
localIdentName: "[name]_[local]_[hash:base64]",
sourceMap: true,
minimize: true
}
}
]
}
]
}
};
I am unable to call this function in pure js and i am getting an error saying
"bpmn is not defined".
Include the bpmn module in the calling function file and then call it.
In your code your are not telling the webpack about the bpmn module dependency.
To add the module in the webpack bundle you have to add the module in the calling function file/Module.
Example
Create the file structure like this.
Create these file and paste the code.
webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
Package.json
{
"name": "Stackoverflow",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack --config webpack.config.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"bpmn-moddle": "^6.0.0"
},
"devDependencies": {
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10"
}
}
src/index.js
import bpmn from './bpmnModdle.js';
function createData(xml, node) {
var bp = bpmn();
console.log(bp)
console.log('Module called');
}
createData();
src/bpmnModdle.js
import BpmnModdle from 'bpmn-moddle';
var bpmn = function () {
var bm = new BpmnModdle();
console.log('bm', bm)
console.log('From inside the module');
return 'exported'
}
export default bpmn;
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="../dist/bundle.js"></script>
<title>Document</title>
</head>
<body>
</body>
</html>
Run npm install
Run npm run build
Open the index.html file in the browser
I am using ES6 module as the bpmn-moddle package doesn't support commanJS module system.
I have been given a chatbot project in javascript using webpack and for some reason I am unable to make it run succesfully! Web pack is running by starting the script with npm start but the page is blank.
Here is the index.html
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<title>Chatbot</title>
<script src="assets/lib/jquery.min.js"></script>
<script></script>
</head>
<body>
<div id="app">
</div>
</body>
</html>
this is index.js
import Dialogflow from './dialogflow';
import './assets/styles/main.css';
console.log("helloooo 2")
if (!$) {
console.error("Bot: This module requires jQuery.");
}
let default_messages = {
header: {
'en-US': 'Hi! How can I help you?',
},
inputPlaceholder: {
'fi': 'Kirjoita jotain (paina enter lähettääksesi)',
'en-US': 'Write something (press Enter to send)',
}
}
let lang = document.documentElement.lang;
class Bot{
renderHumanMessage(message) {
if (!message) {
return;
}
$('#sc-chb-response').append(`
<div class="sc-chb-conversationPlaceholder">
<div class="sc-chb-userRequest">
<div class="sc-chb-userReply">
<div class="sc-chb-chatBubbleHuman"></div>
` + message + `
</div>
</div>
<div class="sc-chb-usrImg">
<div class="sc-chb-responseIcon">
<div class="sc-chb-iconUserImg"></div>
</div>
</div>
</div>
`);
}
addOptionClickListener(chatBot) {
return function(e) {
$('#sc-chb-inputMessage').val($(this).attr('data-option'));
chatBot.sendMessage();
};
}
open() {
if (!this.hasToggleButton) {
$('#sc-chb-chat-box-toggle').hide();
} else {
$('.sc-chb-chatState').hide();
$('#sc-chb-chatStateClose').show();
}
$('#sc-chb-chat-box').show();
if (this.triggerWelcomeEvent === true) {
// Request the welcome message by triggering the welcome event
Dialogflow.triggerEvent('Welcome').then(res => {
this.renderBotMessage(res);
});
this.triggerWelcomeEvent = false;
}
}
toggle() {
if (this.isOpen()) {
this.close();
} else {
this.open();
}
}
isOpen() {
return $('#sc-chb-chat-box').is(":visible");
}
renderFulfillmentText(text) {
let processedText = text.replace(new RegExp('\n', 'g'), '<br>');
// renders [[text]] to a clickable chat bubble
processedText = processedText.replace(/\[\[([^\[\]]+)\]\](<br>)?/g, '<div class="sc-chb-chatbotOption" data-dialogflow="\$1" data-option="\$1">\$1</div>');
// renders {{{name|url}}} as name
return processedText.replace(/\{\{([^\{\}]+)\|([^\{\}]+)\}\}/g, '\$1');
}
}
export { Bot};
and there is an example.js file that shows how to use the bot class:
import { Bot} from './Bot';
let bot = new Bot({
"baseUrl": "xxxxx"
});
bot.init();
Webpack config:
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const autoprefixer = require('autoprefixer');
const CopyWebpackPlugin = require('copy-webpack-plugin')
const path = require('path');
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: {minimize: true}
}
]
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader",
{
loader: 'postcss-loader',
options: {
config: {
path: './postcss.config.js'
}
}
}
]
},
{
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'url-loader?name=assets/[name].[hash].[ext]'
}
]
},
devServer: {
contentBase: 'src/',
// historyApiFallback: true,
// disableHostCheck: true,
port: process.env.PORT || 8080,
// host: '0.0.0.0',
},
plugins: [
new HtmlWebPackPlugin({
template: "./src/index.html",
filename: "./index.html"
}),
require('autoprefixer'),
new MiniCssExtractPlugin({
filename: "main.css",
chunkFilename: "main.css"
}),
new CopyWebpackPlugin([
{ from: 'src/assets', to: 'assets' },
]),
],
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
library: 'sc-chatbot-frontend',
libraryTarget: 'umd'
},
externals: {
'jquery': 'jQuery',
'url-loader': 'url-loader'
}
};
I am not sure from where do I use this example.js or where does this needs to be defined? I tried to inlclude to index.html but still page is blank.
Basically i run npm start and a blank page appears, it should show the chatbot instead!
I'm working with Webpack for JS minification. I'm importing 3 modules in one single minified bundle, but the final result is a huge file size for what is being imported (over 90kb) - they each look like this:
1:
var $ = require('jquery');
$(function() {
// main expansion element
$(".expander").click(function() {
var subShown = $(this).children(".indented").css("display");
if (subShown != "block") {
$(this).children(".indented").css("display", "block");
$(this).children(".caret").addClass("reversedCaret");
} else {
$(this).children(".indented").css("display", "none");
$(this).children(".caret").removeClass("reversedCaret");
}
});
// sub expansion element - .caret class is targeted as well due to issue with clicking on it not registering as a .sub-expander click
$(".sub-expander, .caret").click(function() {
var subSelectText = $(".sub-expander").text();
if (subSelectText != "More") {
$(".sub-expander").text("More");
} else {
$(".sub-expander").text("Show Less");
}
});
// stop propagation on the link element within .expander class
$(".indented").click(function(event) {
event.stopPropagation();
});
});
2:
console.log("a test message");
var clickButton = document.getElementById("clicker");
clickButton.addEventListener("click", (e) => {
e.target.classList.toggle("modified");
});
3:
class ColorPick extends React.Component {
constructor(props) {
super(props);
this.state = {
color: "Let's pick a color"
};
}
changeColor(event) {
var colorInput = document.getElementById('colorInput').value;
this.setState({
color: event.target.value,
backgroundColor: event.target.value
});
if (colorInput === '') {
this.setState({
color: "Let's pick a color",
backgroundColor: "#fff",
});
}
}
render () {
var styleObj = {
backgroundColor: this.state.backgroundColor,
};
return (
<section style={styleObj} id="Profile" >
<h2 className="colorHeader">{this.state.color}</h2>
<input id="colorInput" placeholder="Enter Hex Code Here" onChange={this.changeColor.bind(this)}/>
<div id="slider"></div>
</section>
);
}
}
ReactDOM.render(<ColorPick name="connorcolorpicker" />, document.getElementById('color-picker'));
These three files by themselves clock in at 12kb total. However in the webpack minified bundle it clocks out at 90kb. That doesn't make sense to me. My webpack config looks like this:
webpack.config.js:
var debug = false;
var path = require('path');
var webpack = require('webpack');
module.exports = {
context: __dirname,
// entry is already defined in gulpfile.js - leave this line commented out.
// entry: "/app/js/script.js",
devtool: debug ? "inline-sourcemap" : null,
module: {
rules: [{
test: /\.js$/,
use: 'babel-loader'
}],
loaders: [
{
test: /\.jsx?$/,
loader: 'babel-loader',
query :{
presets:['react','es2015']
},
exclude: /node_modules/
}
]
},
resolve: {
root: [path.resolve(__dirname, 'app'), path.resolve(__dirname, 'node_modules')],
extensions: ['', '.js']
},
output: {
path: __dirname + "public/javascripts",
filename: "scripts.min.js"
},
plugins: debug ? [] : [
// new webpack.optimize.DedupePlugin(),
// new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({ minimize: true, mangle: true })
],
};
[15:20:33] Version: webpack 1.15.0
Asset Size Chunks Chunk Names scripts.min.js 90.1 kB 0 [emitted] main
I was having issues with this in the past and the answers suggested to set debug to false. I did that but it isn't behaving as expected.
Why is 12kb worth of JS/imports totaling out to 90kb in Webpack?