I feel like this should be more straightforward than it is. I need to access all my javascript libraries from the frontend and because I'm integrating it into an old system, I cannot call require("bundle.js");
from the frontend. Everything in the global scope of the bundled files must be accessible from the global scope of the frontend page importing them through the <script>
tag.
So I need to change the old:
<script src="js/jquery.js"></script> <script src="js/silly.js"></script> <script> $(silly()); // Some function in silly.js's global scope </script>
To the new:
<script src="js/bundle.js"></script> <script> $(silly()); // Some function in silly.js's global scope </script>
expose-loader: This would totally work if I didn't have 100 global variables that I don't want to explicitly tell it to look for.
ProvidePlugin: Only really lets the libraries see the other libraries. I also cannot explicitly write all the globals I need with my current setup (More are added constantly).
So for more clarity, I need my webpack.config.js
to look like one of these options:
// Everything is wrapped in module.exports and other irrelevant things plugins: [ new StaticLibraryMergerSuperNeatPlugin("js/*.js") ] // ...
Or:
rules: [ { test: /\.js$/, use: [ "neat-merging-cool-loader", "babel-loader"] } ] // ...
Is there an obvious solution I am missing?
Tl;Dr: How do I make globals from my bundled js files, be exposed to the global scope when imported on a frontend html page via <script src="js/bundle.js"></script>
?
Btw: If anyone is a webpack legend and knows why this is a bad approach, please post below with a brief explanation so I can fix my life.
This is according to their documentation: "libraryTarget: "umd" - This exposes your library under all the module definitions, allowing it to work with CommonJS, AMD and as global variable." Also, I built the exact same code with Webpack 3 and it produced a proper bundle.
In the wake of React—Facebook's UI library—came Webpack, a simple but awesome module bundler. Module bundlers are just what they are called, they bundle up JavaScript modules into one file. This way, when the client makes a request to your server, it doesn't have to make multiple requests for static files.
Webpack allows you to define externals - modules that should not be bundled. When bundling with Webpack for the backend - you usually don't want to bundle its node_modules dependencies. This library creates an externals function that ignores node_modules when bundling in Webpack.
Here's an example of how I do it in my own site. I'm not sure if it's the only way, or even the best way, but it's clean, simple, and it works for me.
Important side note - Use window["propName"]
when declaring things on the window because when you run webpack -p
it will uglify any non-strings, so if you define it as window.propName
, it can get changed to something like s.c
and the rest of your code does not know what it is. Declaring it with bracket notation as a string will force webpack to keep the name intact so you can access it from anywhere with the same name.
site.ts (can be .js, doesn't matter)
/*************************/ /*** JQUERY + JQUERYUI ***/ /*************************/ /* var declaration for typescript - not needed if not using .ts */ declare var $:JQueryStatic; declare var jQuery:JQueryStatic; window["$"] = window["jQuery"] = require("jquery"); require("jquery-ui/effects/effect-slide"); require("jquery-ui/widgets/autocomplete"); require("jquery-ui/widgets/button"); require("jquery-ui/widgets/datepicker"); require("jquery-ui/widgets/tooltip"); /*************************/ /* END JQUERY + JQUERYUI */ /*************************/ /***************/ /*** ANGULAR ***/ /***************/ /* var declaration for typescript - not needed if not using .ts */ declare var angular:ng.IAngularStatic; window["angular"] = require("angular"); require("angular-sanitize"); /***************/ /* END ANGULAR */ /***************/ /************************/ /*** MISC THIRD-PARTY ***/ /************************/ window["moment"] = require("moment"); window["saveAs"] = require("FileSaver").saveAs; window["JSZip"] = require("jszip"); /************************/ /* END MISC THIRD-PARTY */ /************************/ /* var declaration for typescript - not needed if not using .ts */ declare var globals:Globals; window["globals"] = require("./globals");
Layout.html (loaded on every page)
..... <script src="/dist/scripts/site.bundle.js"></script> .....
webpack.config.js
var path = require('path'); var resolve = path.resolve; var AssetsPlugin = require('assets-webpack-plugin'); var WebpackCleanupPlugin = require("webpack-cleanup-plugin"); 'use strict'; var babelOptions = { "presets": [ [ "es2015", { "modules": false } ], "es2016" ] }; module.exports = [{ cache: true, context: resolve('Scripts'), devtool: "source-map", entry: { site: './site.ts', }, output: { path: path.resolve(__dirname, './dist/scripts'), filename: '[name].bundle.js', }, module: { rules: [{ test: /\.ts$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', options: babelOptions }, { loader: 'ts-loader' } ] }, { test: /\.js$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', options: babelOptions } ] }] }, plugins: [ new AssetsPlugin({ path: path.resolve(__dirname, './dist/assets') }), new WebpackCleanupPlugin({}) ], }];
Note: This is not the ideal scenario but because I have a constant amount of new globals being added, I needed to make a plugin to bundle my javascript for me.
This simply stacks your code together to be included on the frontend. Here is my usage example:
From the old:
<script src="js/jquery.js"></script> <script src="js/silly.js"></script> <script> $(silly()); // Some function in silly.js's global scope </script>
To the new:
<script src="js/bundle.js"></script> <script> $(silly()); // Some function in silly.js's global scope </script>
var RawBundlerPlugin = require('webpack-raw-bundler'); module.exports = { plugins: [ new RawBundlerPlugin({ excludedFilenames: [/angulartics/], readEncoding: "utf-8", includeFilePathComments: false, bundles: [ "vendor.js", "styles.css" ], "vendor.js": [ 'js/*.js' ], "styles.css": [ 'css/bootstrap.css', 'css/edits.css' ] }) ] }
A Fair Warning:
This should not be your go-to solution, but I had a bad case that made this the easiest option. Using expose-loader
and import
or window['module'] = require('module.js')
is much safer as that is what webpack was built around. However, if you are having some headaches and just want a simple bundler, feel free to use this plugin.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With