"Import a global variable" in Webpack [duplicate] - javascript

This question already has answers here:
Exclude react from webpack bundle
(3 answers)
Closed 5 years ago.
We have a webpack application that we're integrating into a site that uses traditional JS scripts, and those scripts define global variables.
Inside our webpack application, we want to import some libraries that happen to already be on the page as scripts. Rather than download the same code twice, once as a script and again embedded inside the webpack bundle, we'd like webpack to not include the code for the library already on the page as a script, but still allow us to use the ES import syntax to "import" the code in our modules, even though the script code in question actually made the library available by a global variable.
So, we have <script src="jquery.js"> on our page. jquery.js supposedly does something like this:
window.jQuery = {};
And in our code compiled by Webpack, we'd like to do:
import jQuery from 'jquery';
jQuery === window.jQuery // true
Is there any way for Webpack to be configured to resolve certain dependencies, like "jquery" in the above example, and determine that it should transform the import code into a reference to that global variable?
The intent here is to gradually opt into the new ES module syntax in new code in our legacy application, while still taking advantage of the fact that the same code is already available on the page as a script.
I checked Webpack's guide on "shimming" but it seems to offer every capability except the one I've described in this post.

Use webpack's externals:
Prevent bundling of certain imported packages and instead retrieve
these external dependencies at runtime.
externals: {
jquery: 'jQuery'
}

Related

Should I use import jQuery in a script if jQuery is being added manually?

I am maintaining an existing project, and I saw this:
The site is already including jQuery the traditional way (it's WordPress, and WordPress includes jQuery by default unless you change stuff).
<script src="cdn-or-local/jquery.js"></script>
And then the site is including another javascript file, which has been compiled with Webpack and Babel.
<script src="wordpress-theme/dist/whatever.js"></script>
On the original whatever.js file (not the compiled one, but the human-created one), it states:
import $ from 'jquery';
$(document).ready(() => {
...
}
And this confused me a lot.
As far as I know, if jQuery is already being loaded before the whatever.js file is loaded, then $ will already be defined.
Question: can I just safely omit import $ from 'jquery';?
Yes, it's a good practice to import jQuery if your module needs jQuery - explicit declarations are good for readability, and will allow tooling to know what $ refers to! However, you can (should) configure webpack to not bundle jQuery as a dependency, but assume it is already loaded and available from a global variable.

What exactly is a "webpack module" in webpack's terminology?

I am a newbie to webpack and currently trying to understand basic concepts.
Looking at the official docs, on the page Concepts it uses module term and gives link to read more about modules on page Modules.
So on this page we have question "What is a module" but no explicit answer to that is given. Rather, it describes modules by how they "express their dependencies":
What is a webpack Module
In contrast to Node.js modules, webpack modules can express their
dependencies in a variety of ways. A few examples are:
An ES2015 import statement
A CommonJS require() statement
An AMD define and require statement
An #import statement inside of a css/sass/less file.
An image url in a stylesheet url(...) or HTML file.
So it doesn't explicitly defines what exactly is the module and I am confused now.
Is module just a javascript file?
Or is it any type of file like .css or images?
Or is module some logical concept not related to physical files at all?
The simple answer to your question is that a Webpack Module is a Javascript file that is processed by Webpack.
As for why that's a thing, well, consider the following Javascript:
if (window.matchMedia('(max-width: 600px)')) {
const img = document.querySelector('#header-responsive-image');
img.src = 'different/image/url/file.jpg';
img.classList.add('some-class');
}
Note that this code has dependencies on specific markup, image files, and CSS rules. But crucially you have to read the code to find out what they are. There's no (easy) way to statically analyze the dependencies (or even do it by hand!).
This may not seem like a big deal in small apps, but when you're working with a large Javascript codebase or authoring a component library, the ability to statically analyze the real dependency graph and have your tools warn you immediately when your JS, CSS, markup, and files on disk get out of sync is a lifesaver.
With Webpack at the top of the file you're going to see something more like this:
import header600Width from '../img/header-600-width.jpg';
import '../styles/has-some-class.css';
// etc.
You can load images, csv, xml, toml, json, css, and the list goes on. This is something that other modules systems by-and-large can't or won't do. So a Webpack module is, in a sense, a superset of a Javascript module.

Can not include JQuery without npm in Vue JS project

I'm trying to import JQuery without using npm to my Vue JS project. Here's what I'm trying:
***jquery-functions.js***
import '../../public/js/jquery.min.js'
export function bar(){ $(".foo"){...} }
/$ is not defined as function/
var hello is evaluated in module scope, this prevents variables from leaking to global scope.
In order for a global to be defined, it should be assigned explicitly as such:
window.hello = function (x){x+5};
As for jQuery, it's specific to how the module works. It's UMD module and it isn't exposed as a global when jquery.min.js is imported as a module.
It should be assigned explicitly as a global either:
import jQuery from '../../public/js/jquery.min.js';
window.$ = window.jQuery = jQuery;
Or this can be done by means of Webpack that is used by Vue CLI internally, as the answer in related question suggests.
I tried everything above but somehow I couldn't get it working. Either eslint was giving function not defined error for $() or bootstrap couldn't see the included jQuery. I don't know if it's ok to include jQuery globally but I found the solution by including jQuery to my index.html file with <script> tags. Then in my jquery-functions.js file I disabled eslint then everything was working just fine on run time.
***jquery-functions.js***
/* eslint-disable */
export function bar(){ $(".foo"){...} }
I did it like that so that I can separate jQuery methods from from Vue JS methods. It might be better to use npm to avoid including jQuery globally so that you can include it only in components that you need. For me I was concerned with compatibility of all other (6 of them) jQuery dependent libraries included in the template that I downloaded from internet.

Javascript ES6 import without a name [duplicate]

This question already has answers here:
import module just to run it
(2 answers)
Closed 6 years ago.
I am running Webpack, Babel and Vue.js and I want to split up my entry file. Currently I have an app.js file which is the starting point to my application.
I have some bits and pieces of Code I want to put into a bootstrap.js file which I want to include in my main app.js file can I can have a clean file to start out with Vue and add components in it as I go.
Some examples of what I would want to put in my bootstrap.js file:
import messagesNL from './translations/nl';
Vue.use(VeeValidate, {
locale: 'nl',
dictionary: {
nl: {
messages: messagesNL
}
}
});
window.Vue = Vue;
So pretty much setup for plugins, global configuration, etc. I feel like this is not your typical module and I find it hard to create a module like structure for this file so I basically use this in my app.js file:
import bootstrap from './bootstrap';
Having no idea if this would work, it seems to just import everything neatly without me having done a module exports {} like syntax.
Now the bootstrap variable that I assigned to that file is unused in app.js since it's only used to require the file and my IDE kind of 'greys` it out to let me know it is unused.
Is there another syntax for this so that I don't have to assign a name to it? Is this approach ok to split up my file, or should I be doing something else?
I haven't put it into a proper module yet because then it would have it's own local scope and I would be unsure how to set up Vue with all the plugins, etc. If anybody has a better suggestion I am open to it.
Cheers.
To include a file without importing anything you can just drop the <name> from part of the statement:
import './bootstrap';
This will execute the target module without affecting the scope of the active module, but it may have side-effects such as declaring globals or modifying existing globals.
As stated in the MDN docs for Import a module for its side effects only:
Import an entire module for side effects only, without importing anything. This runs the module's global code, but doesn't actually import any values.
import '/modules/my-module.js';

Module definition to work with node.js, require.js and with plain scripttags too

I am working on a javascript module/library that should work in 3 environments:
in node.js
in requirejs
when simply included using tags into the webpage. In this case the whole module should be hooked up under window.myModule
Do you have any suggestions as to how to write the structure of the library so that it works in all these environments?
EDIT: basically I mean some sort of wrapper code around the library so that I can call the file form any of those three methods and I'm fine...
This requirement and its solution is known as Universal Module Definition (UMD). It is currently a draft proposal. Background and current status is described in Addy Osmani - Writing Modular JavaScript With AMD, CommonJS & ES Harmony article. Look for "UMD" link pointing to various templates you can use.
Quite many other templates can be found on the web - UMD is the search keyword.
(did not find the final link myself yet :)
We're working on the same thing, I think.
And we have some success. We have library (we call it 'slib'), compiled to AMD js files. It does not depend on npm modules or browser, so it can be called from node and from browser.
1) To call it from node, we use requirejs:
file require.conf.js
module.exports = function(nodeRequire){
global.requirejs = require('requirejs');
requirejs.config({
baseUrl: __dirname+"/../web/slib/",
paths: {
slib: "."
},
nodeRequire: nodeRequire
});
}
In any other serverside (nodejs) file we add this line at the beginning
require("./require.conf")(require);
then we call slib's code by:
var Computation = requirejs("slib/Computation");
2) To call slib from browser, we just use requirejs. It handles everything fine.
3) We do not need to call slib from < script > directly.
For production, we use r.js to make a bundle js file with most of dependencies and use it on the page with one < script >. And this script downloads all other deps, if they are not included, using standard requirejs and it does not need requirejs (as far as I remember), it just works alone. This is very flexible for large projects: use requirejs while development, use r.js to bundle core files in production to speed up page load, use whole bundle if you need only one < script > without any other requests. r.js bundles all dependencies correctly, including old js libraries, which were commonly loading using only < script > and accessible by window.myOldLibrary using shim param on config.
It seems you can use browserfy to make some npm modules accessible from slib's code, but we did not tried yet.
Also, using requirejs on node's side, I think, can be simpler (why we need second 'requirejs' function together with node's one?) We just have not investigated it well, but this works.
In any slib module you can write
if (window)
window.module1 = this // or whatever
and it will be exported as old js lib upon load

Categories

Resources