Expose a function from a webpack bundle - javascript

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.

Related

Vite add assets path prefix / change assets path in compiled files

Could you tell me please, how to change how vite assets path is built, but only for compiled files?
I mean, for example, I have file index.html like:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<script type="module" src="/src/main.js"></script>
</body>
</html>
and here I have script with src="/src/main.js"
When I compile it, I get src="/assets/index-c371877d.js"
I am making ESP32 webserver, and because of some internal moments I need to put compiled files in another folder, on SD card.
I can change output directory using vite.config.js, here I have:
export default defineConfig({
plugins: [vue(), vueJsx()],
resolve: {
alias: {
'#': fileURLToPath(new URL('./src', import.meta.url))
}
},
build: {
outDir: "../../SD/modules/test", // test is project name
},
})
But the problem is, that compiled files have same relative path, while I need to have modules/%moduleName%/%relative path%
So instead of src="/assets/index-c371877d.js" I need src="module/test/assets/index-c371877d.js"
I tried to change vite assetsDir:
export default defineConfig({
plugins: [vue(), vueJsx()],
resolve: {
alias: {
'#': fileURLToPath(new URL('./src', import.meta.url))
}
},
build: {
outDir: "../../SD/modules/test",
assetsDir: "modules/test"
})
Now it adds modules/test before path, but compiled files are put into outDir + assetsDir directory, what I don't want.
Tell me please, how could I just prepend necessary path data without changing real assets directory? Thank you in advance.
Solved, finally.
It was needed to provide base property for config
export default defineConfig({
plugins: [vue(), vueJsx()],
resolve: {
alias: {
'#': fileURLToPath(new URL('./src', import.meta.url))
}
},
build: {
outDir: Config.modulesDirRelativePath + "/vue-project"
},
base: "/modules/vue-project"
})

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

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)

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 properly split common dependencies with webpack4

I am having difficulty configuring webpack4 to properly bundle shared dependencies.
I have two pages in my application (Page1 and Page2). Both require bootstrap, jquery as well as a custom JavaScript app called core.
Page 2 requires the same but also a custom JavaScript application called my-app and also lodash.
Since my core app will be included in all pages, I want to have jquery and bootstrap in the same bundle.
Since lodash is only required for pages running my-app, I want to include that dependency in the my-app bundle.
So I setup my app like this:
webpack.config.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
'core': './src/core/index.js',
'my-app': './src/my-app/index.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
resolve: {
alias: {
jquery: 'jquery/src/jquery',
}
},
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
}),
],
mode: 'development',
}
page1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page1</title>
<script src="dist/core.bundle.js"></script>
</head>
<body>
<h1>Page1</h1>
<span id="test"></span>
</body>
<script>
$(document).ready(function() {
$('#test').text('jQuery Works!');
});
</script>
</html>
page2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Page1</title>
<script src="dist/core.bundle.js"></script>
<script src="dist/my-app.bundle.js"></script>
</head>
<body>
<h1>Page2</h1>
<span id="test"></span>
</body>
<script>
$(document).ready(function() {
$('#test').text('jQuery Works!');
});
</script>
</html>
(Full project: https://github.com/LondonAppDev/webpack-split-example)
When I run npx webpack, it creates core.bundle.js and my-app.bundle.js, however both of these include jquery.
Is it possible to put all "global" dependencies in core.bundle.js?
Just one thing to remember here, with webpack 4 you don't add vendor scripts as an entry to your webpack.config, just real entry scripts to your application.
WP will create an optimized bundle output for your app using the default settings.
You have to add vendor cache group to your config, in order to extract jQuery, Lodash, Bootstrap,Popper into a separate bundle:
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /node_modules/,
name: "vendor",
chunks: "all",
enforce: true
}
}
}
},

Webpack publicPath don't work

I have a demo like this:
my webpack config:
module.exports = {
entry: './app.js',
output: {
filename: 'bundle.js',
path: './build',
publicPath: 'http://localhost:3000/'
},
module: {
rules: [{
test: /static\.html/,
use: 'file-loader'
}, {
test: /\.png/,
use: 'file-loader?name=[name].[ext]'
}, {
test: /\.css/,
use: 'file-loader?name=[name].[ext]'
}],
},
resolveLoader: {
modules: ['node_modules'],
}
}
this is my entry app.js:
import './static.html';
import './img.png';
import './index.css';
static.html:
<!DOCTYPE html>
<html>
<head>
<title>static</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="./index.css">
</head>
<body>
<img src="./img.png">
</body>
</html>
when i run npm run build, i got a build folder. I think the build/static.html should be
<img src="http://localhost:3000/img.png">
<link href="http://localhost:3000/index.css">
But, actually the build/static.html is the same as static.html. Both of the src of img and the href of link are not changed.
Any one knows why?
====
I have known the answer. Webpack publicPath just work for output file. So just the url in the bundle.js will be replaced.
The webpackHtmlPlugin will not resolve this problem, because this plugin will generate a html page with a script links to the output bundle.js that I don't need it.
To resolve this problem, I have wrote a custom loader to transform the html page output by the file-loader dynamicly.
The file-loader would not change the file. If you want a html file with the publicPath added, you should use html-webpack-plugin.

Categories

Resources