173

Suppose that I have class like this (written in typescript) and I bundle it with webpack into bundle.js.

export class EntryPoint {
    static run() {
        ...
    }
}

In my index.html I will include the bundle, but then I would also like to call that static method.

<script src="build/bundle.js"></script>
<script>
    window.onload = function() {
        EntryPoint.run();
    }
</script>

However, the EntryPoint is undefined in this case. How would I call the bundled javascript from another script then?

Added: Webpack config file.

Raven
  • 4,783
  • 8
  • 44
  • 75
  • Please add your webpack configuration. I believe that something along the line of `var EntryPoint = require('EntryPoint')` is missing in your `onload` method. – MartyIX Dec 18 '15 at 14:39
  • 3
    @MartinVseticka I have added my config. Indeed something like `require` might be necessary but same as with import below, it says `require is not defined`. What I'm trying to do is to use bundled content from plain javascript, wouldn't I need some framework again to use `require`? But I'm trying to avoid that. Hope it makes sense. – Raven Dec 18 '15 at 16:25

9 Answers9

178

It seems that you want to expose the webpack bundle as a library. You can configure webpack to expose your library in the global context within a variable of your own, like EntryPoint.

I don't know TypeScript so the example uses plain JavaScript instead. But the important piece here is the webpack configuration file, and specifically the output section:

webpack.config.js

module.exports = {
  entry: './index.js',
  output: {
    path: './lib',
    filename: 'yourlib.js',
    libraryTarget: 'var',
    library: 'EntryPoint'
  }
};

index.js

module.exports = {
  run: function () {
    console.log('run from library');
  }
};

Then you will be able to access your library methods like you expect:

<script src="lib/yourlib.js"></script>
<script>
  window.onload = function () {
    EntryPoint.run();
  };
</script>

Check the gist with the actual code.

Legends
  • 21,202
  • 16
  • 97
  • 123
dreyescat
  • 13,558
  • 5
  • 50
  • 38
  • Thanks! This was what I was trying to do. – Raven Dec 18 '15 at 21:50
  • 21
    We have multiple entry points, so in the output section, I instead made it `library: ["GlobalAccess", "[name]"],`. That then make the var be an object with members for each entrypoint: GlobalAccess.EntryPointFoo, GlobalAccess.EntryPointBar, etc. – John Hatton Jan 27 '16 at 22:44
  • 4
    This works for `nam run build` but does not work in dev env using `webpack-dev-server`. My exported EntryPoint is an empty object. Any ideas? – nkint Oct 11 '16 at 15:34
  • Does this solution work with webpack 2? My EntryPoint is not empty, but the exported functions are missing ( I'm getting a "EntryPoint.run is not a function" error message). – Trantor Mar 14 '17 at 13:08
  • What if we don't want to instantiate the class? We want it to have a variable name. How would we go about doing that? – Philll_t Mar 15 '17 at 18:11
  • 1
    what about the situation where entry: { page1: [ 'module1.js', 'module2.js' ], page2: 'module3.js' } @JohnHatton suggestion doesn't seem to work then. I get access to page1.module2, but not to page1.module1. It seems to just take the last one. – sheamus May 11 '17 at 06:56
  • @sheamus did u ever find a solution to this – Ernest Okot Jun 12 '17 at 18:47
  • 2
    followed steps , changed config , rebuild it , but still getting uncaught ReferenceError: EntryPoint is not defined – user889030 Dec 27 '17 at 12:59
  • 2
    I've gotten a similar example to work in babel + webpack v3.10.0 by changing index.js to `export function run() {}` from `module.exports = ...` – dworvos Apr 04 '18 at 17:52
  • What if we have typescript as an entry point like export default class? What to do in this case? – Teoman shipahi Oct 07 '18 at 23:58
  • You all people should to check [expose-loader](https://webpack.js.org/loaders/expose-loader/) before to use this solution. – dani herrera Oct 21 '20 at 16:56
  • I have tried this solution, but getting => Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema. Webpack version is ^5.6.0, Webpack CLI ^4.2.0, could you please provide solution for this webpack version? – santanu bera Dec 17 '20 at 13:54
63

I managed to get this working without any further webpack.config.js modifications, by simply using the import statement which i called from my main/index.js file:

import EntryPoint from './EntryPoint.js';
window.EntryPoint = EntryPoint;

enter image description here

For reference, here's my weback.config.js file.

Initially I tried accomplishing the same using require, however it assigned the module wrapper to window.EntryPoint as opposed to the actual class.

Matt
  • 3,079
  • 4
  • 30
  • 36
  • 3
    Any chance doing this without es6? Otherwise I get `Uncaught SyntaxError: Unexpected token import`. Or is your `index.js` also bundled (I do see it as entry point, but not sure)? – Raven Dec 18 '15 at 16:18
  • Yeah, index.js is bundled up too - that's where I've included the import statement – Matt Dec 18 '15 at 16:48
  • 4
    Well you see, I'm trying to access something that is bundled from a script that doesn't belong to the bundle. Like the bundle was a library and I would try to access its methods from outside. Is that possible? – Raven Dec 18 '15 at 16:57
  • 4
    This solution is really simple and I'm ashamed of myself for not thinking about it as soon as the problem arose. – cav_dan May 09 '17 at 17:35
  • @Raven How did you solve your issue? I also try to access methods from the bundle as if it were a library from another script outside the bundle – MADforFUNandHappy Nov 03 '17 at 21:35
  • @MADforFUNandHappy Well, the accepted answer solved my issue. You can find the config file for bundle in my question and just edit it with the provided info. – Raven Nov 05 '17 at 17:28
  • 2
    I had been stuck on this problem for hours. Was just going to move the script into my bundle but that would have caused a bunch more problems. Thanks for the simple answer!! – Stephen Agwu Dec 05 '17 at 04:52
  • 1
    This solved the problem for me. Doesn't matter if you use "require" or "import", the key is the "window.EntryPoint = EntryPoint". That forces EntryPoint into the global context (namespace pollution, gasp!), which allows you to reference it from within script tags. (Which is the whole point, after all!) – Duncan Mar 26 '19 at 21:20
  • This didn't solve the issue for me. Can someone please show a full example? How does your external script look like, how do you import it into the HTML page, how do you access the functions from the external script in HTML script tags? Thanks in advance! – Fred Aug 31 '22 at 02:18
22

In my circumstance I was able to call a function from within the bundled JavaScript from another script by writing the function to the window when creating it.

// In the bundled script:
function foo() {
    var modal = document.createElement('div');
}
// Bind to the window
window.foo = foo;
// Then, in the other script where I want to reference the bundled function I just call it as a normal function
<button onClick="window.foo()">Click Me</button>

I wasn't able to use Babel so this worked for me.

Kurt William
  • 703
  • 6
  • 10
2

I had a similar challenge, I wanted to create a bundle for multiple pages within a journey and wanted each page to have it's own entry point into the code, and without a separate bundle for each page.

Here's my approach, which is very similar to Kurt Williams but from a slightly different angle, also without changing webpack config:

JourneyMaster.js

import { getViewData } from './modules/common';
import { VIEW_DATA_API_URL } from './modules/constants';
import { createLandingPage, createAnotherPage } from './modules/components/pageBuilder';

window.landingPageInit = () => {
    getViewData(VIEW_DATA_API_URL).then(viewData => {
        createLandingPage(viewData);
    });
};

window.anotherPageInit = () => {
    getViewData(VIEW_DATA_API_URL).then(viewData => {
        createAnotherPage(viewData);
    });
};

// I appreciate the above could be one liners,
// but readable at a glance is important to me

Then an example of how I call these methods at the end of the html page:

<script src="/js/JourneyMaster.js"></script>
<script>window.landingPageInit();</script>
StudioTime
  • 22,603
  • 38
  • 120
  • 207
2

WEBPACK.CONFIG.JS

1.USING UMD

module.exports={
            mode:'development',
            entry:'./yourentry.js',
            output:{
                path:path.resolve(__dirname,"dist"),
                filename:'main.js',
                publicPath:'/dist/',
                libraryTarget:'umd', 
                library:'rstate',
                umdNamedDefine: true,
                libraryExport: 'default' 
            }
    }

index.html

<script src="dist/main.js"></script>
<script>
  window.onload = function () {
  rstate()=>{}
</script>

main.js

export default function rstate(){
console.log("i called from html")
}

2.USING VAR

module.exports={
            mode:'development',
            entry:'./yourentry.js',
            output:{
                path:path.resolve(__dirname,"dist"),
                filename:'main.js',
                publicPath:'/dist/',
                libraryTarget:'var', 
                library: 'EntryPoint'
            }
    }

index.html

<script>
  window.onload = function () {
  EntryPoint.rstate()=>{}
</script>

main.js

module.exports={
    rstate=function(){
        console.log("hi module")
    }
}

3.USING AMD as library we use like(for those who want to make lib)

define(['jquery', './aux-lib.js'], function ($) { ..(1).. });
Stals
  • 1,543
  • 4
  • 27
  • 52
Balaji
  • 9,657
  • 5
  • 47
  • 47
1

This took me forever to figure out as the accepted answer wasn't working for me. Just make sure the function name is the same as the library in the config and it's bundled with the config specified -- npx webpack --config webpack.config.js --mode=development -- hopefully this saves people a few hours.

index.js (function to be bundled) >>

function EntryPoint() {
    console.log('called from bundle');
}

module.exports = EntryPoint;

webpack.config.js >>

const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist'),
        libraryTarget: 'var',
        library: 'EntryPoint'
    },
};

start.html (where the bundled function is called) >>

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Azure SDK Storage Example</title>
    <script type="text/javascript" src="./dist/main.js"></script>
  </head>
  <body>
      <h1>Azure SDK Storage Example</h1>
  </body>
</html>

<script>
 EntryPoint();
</script>
TabulaRasa
  • 61
  • 1
  • 10
0

Many of the answers so far work, it would only be necessary to clarify that Webpack will not recognize the library until it is built once declared. You should use npm run build right after creating your library, before continuing to work with npm start.

At least that's how it works for me, using only webpack.

0

Maybe this is some impostor syndrome on my part, but I think 'real' coders will cringe at my answer. Regardless, I found this solution to be the best fitting for being pragmatic about my time with my hobby project:

Chane your JS function declaration from:

function renderValue(value) {

to:

global.renderValue = function(value) {

Of course, you'll want to require('path/to/your_custom_js') as you would any file.

I found this answer here: https://www.fastruby.io/blog/rails/webpack/from-sprockets-to-webpacker.html

ianrandmckenzie
  • 472
  • 3
  • 12
-4

App.ts:

namespace mytypescript.Pages {

        export class Manage {

     public Initialise() {
     $("#btnNewActivity").click(() => {
                    alert("sdc'");
                });
        }
    }
}

mypage.html:

 <input class="button" type="button" id="btnNewActivity" value="Register New Activity" />

 <script type="text/javascript">
    var page = new mytypescript.Pages.Manage();
    page.Initialise();
</script>
Liam
  • 27,717
  • 28
  • 128
  • 190
Veera Induvasi
  • 822
  • 7
  • 19