Javascript - How to use a variable across two functions in webpack? - javascript

I have to migrate old, plain HTML with plain JS files to webpack but I have troubles when I have a variable declared outside functions and used between multiple functions below them.
One of the example:
// import stuff
var recorder;
var blobs = [];
function recordStart() {
recorder = new MediaRecorder(...);
blobs = []
recorder.ondataavailable = (event) => {
console.log("recording");
if (event.data) blobs.push(event.data);
};
recorder.onstop = function(){...};
recorder.start();
}
function recordEnd() {
recorder.stop();
}
$('#capture_button').on('touchstart mousedown', function() {
recordStart();
})
$('#capture_button').on('touchend mouseup', function() {
recordEnd();
})
However, everytime recordEnd is called, JS console always throw undefined error on recorder object, as if the variable never even touched at all by the recordStart function.
Is there something that I do wrong here? I just learn webpack for a week so please bear with me if this is a rookie mistake.
PS. jQuery runs fine, if I run console.log() in them they would fire properly.
Edit: I forgot to mention this issue happens only after I run npx webpack on it with this configuration:
const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
minimize: false,
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|svg|jpg|jpeg|gif|glb|gltf|usdz)$/i,
type: 'asset/resource',
}
]
},
plugins: [
new CopyPlugin({
patterns: [
{from: "src/images", to: "images"},
{from: "src/maps", to: "maps"},
{from: "src/models", to: "models"}
]
})
]
};
It runs well before I migrate to webpack, but after I bundled with it and include the dist/bundle.js in the HTML in dist/index.html the undefined error happens.
<!DOCTYPE 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>Webpack Title</title>
<script src="./bundle.js"></script>
</head>
<body>
...
</body>
</html>

One of the main selling points of modules is to give your code an explicit dependency chain, and to avoid global variables. For variables that you want to be able to access in other modules, you should export them, and import them where they're needed.
export let recorder;
// do stuff
// assign to recorder
and in the other module
import { recorder } from './theFirstModule';
function recordEnd() {
recorder.stop();
}
The other (bad) option is to make recorder explicitly global, so it'll be accessible anywhere - but that defeats the purpose of using modules and makes code harder to reason about, so I wouldn't recommend it.
Instead of doing
var recorder;
, instead, wherever you assign to recorder, assign to window.recorder. (But if I were you I'd really try working within the module system first)

Related

Expose a function from a webpack bundle

I'm trying to expose a function to a webpage, that can be called externally, or on window.load, or at anypoint.
DoThing.ts
import * as lib from "libs";
export default function DoAThingFunc():void{
console.log('Do a thing)'
}
This is then imported thing
ExposeDoThing.js
import DoAThingFunc from './DoThing'
window.exposeFunc = DoAThing
window.exposeFunc():
webpack 4 bundle
entry: {
main: './src/MainEntry.tsx',
popupcallback: './src/ExposeDoThing.js'
},
output: {
path: path.join(__dirname, outputDir + "/js"),
filename: '[name].js',
publicPath: "/js/",
library:'pclGlobal'
},
MyPage.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pop up</title>
</head>
<body>
<script src="/js/vendor-main.js" type="text/javascript"></script>
<script src="/js/popupcallback.js" type="text/javascript"></script>
</body>
</html>
Nothing is being called, pclGlobal is undefined even though:
var pclGlobal=(window.webpackJsonppclGlobal=window.webpackJsonppclGloba...
is present in the output. And nothing is being called from the functions.
I just want the function DoAThingFunc() to fire when the script has loaded, what am I missing?
I think you need to expose your bundle as a library. check this link about output section in webpack config, and check the right way to do it.
Maybe a config like this: (pay attention to libraryExport attr)
module.exports = {
entry: './index.js',
output: {
path: './lib',
filename: 'yourlib.js',
libraryTarget: 'umd',
library: 'YourLibraryName',
umdNamedDefine: true,
libraryExport: 'default'
}
};
I noticed the vendor-main.js in my quoyted html example, except we don't have one in the weback entry and yet there is file output...
It looks like we used to have a vendors bundle, and then stopped but left the following the webpack.config.js
runtimeChunk: {
name: entrypoint => `vendor-${entrypoint.name}`
}
This has a weird effect. If your entry is not called main, then it wouldn't any execute anyexport default functions.

How to keep all functions after webpack build?

Webpack checks the usage of functions and remove (as dead code) the "unused" functions. But if I'm using the function inside HTML, the same function will be removed if none scripts calls it.
For example, I have the script.js:
function doSomething() {
console.log("clicked button");
}
function explicitUsedFunction() {
console.log("Hey, function called!");
}
explicitUsedFunction();
And index.html:
<html>
<head>
<title>Test</title>
<script src="script.js"></script>
</head>
<body>
<button onclick="doSomething()">Button</button>
</body>
</html>
doSomething function is used by onclick button event.
here is my webpack.config.js:
const path = require('path');
const TerserMinimizer = require('terser-webpack-plugin');
module.exports = {
mode: 'production',
entry: ["./script.js"],
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
optimization: {
minimize: true,
minimizer: [
new TerserMinimizer({
terserOptions: {
keep_classnames: true,
keep_fnames: true
}
})
]
}
};
I'm using TerserPlugin to keep function name (because HTML will not be modified). So, the bundle.js file will be:
!function explicitUsedFunction(){console.log("Hey, function called!")}();
doSomething function was removed, the question is, how can I keep all declared functions in bundle.js using Webpack?
Some points about answer need be understood:
the example above is just for easy code reading, I don't will use addEventListener to the button (because if I have about 20 different buttons (using the function) is not a helpful answer addEventListener to all buttons)
I'm not using import/export keyword because is just a simple javascript file imported by script tag, the use of import/export keyword causes "SyntaxError: Invalid token"
After a lot of fights with webpack, I found a simple solution.
I need 2 things: send all function to minified file and make event functions acessible on window's scope. A just added the following line for every function I need:
function doSomething() {
console.log("clicked button");
}
function explicitUsedFunction() {
console.log("Hey, function called!");
}
explicitUsedFunction();
/*NEW LINE HERE:*/
window.doSomething = doSomething;
with this simple change I tell to webpack that the function was used and I dont need Terser anymore (webpack.config.js):
const path = require('path');
module.exports = {
mode: 'production',
entry: ["./script.js"],
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
};

Webpack cannot use jQuery

I'm starting using webpack (version 3) and when I try to use jQuery from my "app.js" file then nothing happens:
I npm install --save jquery and:
import $ from 'jquery'
$('body').css('backgroundColor', '#DD0000')
document.body.style.backgroundColor = "red";
And when I try changing the css using document.body.style.backgroundColor = "red";
it tells me "cannot read property style of null"
But for the rest it's working, I mean I tried this successfully :
import json from "./test"
console.log(json)
Here is my HTML head part:
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Webpack</title>
<link rel="stylesheet" href="./styles.css">
<script src="./dist/bundle.js"></script>
</head>
Here is my webpack config :
const path = require("path");
const uglify = require("uglifyjs-webpack-plugin");
module.exports = {
watch: true,
entry: './app.js',
output: {
path: path.resolve('./dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
use: ["babel-loader"],
}
]
},
plugins: [
new uglify(),
]
}
Do you know what I'm doing wrong?
You inserted your script in the <head> section. And since JS is blocking, it's executed right there, before the <body> is parsed and exists (so it's null).
If you want this to work, either put your script at the end of the document, right before the closing </body> tag, or wait for the document to be loaded:
window.addEventListener('DOMContentLoaded', function() {
// Here, you can use document.body
});
or, in jQuery syntax:
$(function() {
// Here, you can use document.body
});

How to access Method of a Class in HTML Element using TypeScript? [duplicate]

I'm new to webpack and similar tools. I wanted to reorganize my project. Currently all my JS-code lives in App.js. So before splitting it into modules and improving, I wanted just to set up the workflow for copying it.
This is the content of webpack.config.js:
const path = require('path');
module.exports = {
mode: 'development',
entry: {
App: "./app/assets/scripts/App.js"
},
output: {
path: path.resolve(__dirname, './app/temp/scripts'),
filename: '[name].js',
},
module: {
rules: [{
loader: 'babel-loader',
query: {
presets: ['es2015']
},
test: /\.js$/,
include: [
path.resolve(__dirname, "app")
],
exclude: [
path.resolve(__dirname, "node_modules")
],
}],
},
};
which I learned at this video course. But afterwards not all functions work. For instance, the functions called by event listeners work:
function initOnClickForVersionBtns() {
$('#btn_soprano').click(function () {
voice = 1;
loadFile();
});
$('#btn_basset').click(function () {
voice = 2;
loadFile();
});
}
But those called from HTML do not:
Score
Note that I am still referencing few others js files from HTML:
<script src="../javascript/basic-events.js" type="text/javascript">/**/</script>
<script src="../javascript/bootstrap.min.js" type="text/javascript">/**/</script>
<script src="../javascript/jquery-3.3.1.min.js" type="text/javascript">/**/</script>
I'd like it to change later, but currently I thought it should not be a problem. Maybe it is?
Webpack wraps all code in an IIFE, for more predictable behavior and to avoid global pollution, among other things. In the bundled code, the place where your module's functions are defined is not the top level of the <script>.
Inline attribute event handlers may only reference global variables., and are quite a bad idea in nearly all cases. While you could fix it by explicitly assigning the function to window, eg:
window.switchToScore = function() {
// ...
instead of
function switchToScore() {
// ...
It would be much better to remove the inline attribute event handlers completely, and attach listeners with Javascript, just like you're doing with
$('#btn_soprano').click(function () {
voice = 1;
loadFile();
});

How to make one bundle from several scripts using webpack?

How to make one bundle from several scripts using webpack? I'm trying to point several entries, but include just last, other just not seen how functions.
That's my app1:
function setCats() {
alert('cat');
}
exports.setCats = setCats;
My app2:
function setDogs() {
alert('dog');
}
exports.setDogs = setDogs;
My index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="bin/app.bundle.js" charset="utf-8"></script>
</head>
<body>
<script>app.setCats()</script>
<script>app.setDogs()</script>
</body>
</html>
My webpack.config.js:
module.exports = {
entry: {
app: [
'./src/app1.js',
'./src/app2.js',
]
},
output: {
path: './bin',
filename: 'app.bundle.js',
library: 'app'
},
module: {
loaders: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
}]
},
};
After building and running in browser alerted just dog and in console I have this mistake:
index.html:8 Uncaught TypeError: app.setCats is not a function
Thanks :)
It's simple, you can only load one entry point in one webpack output bundle. If you use multiple, they are all loaded but only the last entry point is exported. This is often used to load polyfills before libraries, for example, since they don't export anything anyway.
To do what you want to do, you should simply make another higher-level file where you do this:
var setCats = require('./app1.js').setCats;
var setDogs = require('./app2.js').setDogs;
module.exports = {
setCats: setCats,
setDogs: setDogs,
};
Then simply use only that file as a single entry point (entry: './src/app.js'). After building it the bundled library will work and contain app.setCats and app.setDogs.

Categories

Resources