Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[question] Difference between externals and globals #1169

Closed
wearhere opened this issue Dec 22, 2016 · 13 comments
Closed

[question] Difference between externals and globals #1169

wearhere opened this issue Dec 22, 2016 · 13 comments

Comments

@wearhere
Copy link

I am loading jQuery from a CDN, as a global ($). However for consistency within my project, I wish to write import $ from '$'.

I originally thought that I could do this using the external option because jQuery is "a module that should remain external to my bundle", no? But Rollup threw an error, saying that I should use globals instead. Which is fine, that works and also seems sensible given that option's documentation—but then what are externals for?

@fskreuz
Copy link

fskreuz commented Dec 22, 2016

I'm under the impression that external tells Rollup that these are modules, but should not be bundled into the app and remain involved with the code via imports/require. globals on the other hand tell Rollup that these things just exist in scope and simply provide reference using that global.

I think it boils down to how the bundle is actually written and what format you are emitting.

@wearhere
Copy link
Author

wearhere commented Dec 22, 2016

Thanks for the quick response!

I think it boils down to how the bundle is actually written and what format you are emitting.

Interesting. So, if my code is import $ from '$' and my configuration uses globals: { $: '$' } and emits an ES module, what gets written is import $ from '$';

(You can see a runnable version of this, albeit produced for a different issue, here.)

Can you describe what would be different, in terms of how the bundle's written, in a case where I was using external? Maybe even provide some sample code? I'm not sure how to try it out—external: ['$'], what I tried originally, throws an error.

@Rich-Harris
Copy link
Contributor

The external option tells Rollup 'don't worry about trying to include this in the bundle, it's an external dependency'. In many cases it's sort of optional – if Rollup can't resolve a module identifier, it will warn you that it's treating it as an external dependency. The external option is your way of saying 'it's okay Rollup, you don't need to warn me about this, I know what I'm doing'. The only time you absolutely have to include it (though it's always recommended) is when the module you want to keep external is imported via a relative path instead of a package ID.

The node-resolve plugin doesn't like failing to resolve module IDs (because it usually indicates a bug, like you forgot to install the package in question), so it will throw an error rather than letting Rollup print a warning. If you use the skip option, it won't throw – but if you're using the external: ['$'] option, the plugin won't try to resolve it at all, so external: ['$'] (on the main config) is better than skip: ['$'] on the plugin config.

So, we have our bundle, and we're generating an iife or a umd output. In order for $ to mean the correct thing inside the bundle, Rollup needs to know what $ corresponds to outside the bundle – in other words, what the global variable name is. That's what globals is for. If you don't include the globals option, Rollup will guess based on the code you wrote, but it will warn you that it's guessing – so again, including the option is just how you say 'don't worry, I got this' and make sure there's no ambiguity.

An aside: it's more common to use the package ID rather than a symbol, so you'd typically import jQuery with

import $ from 'jquery';

and use external: [ 'jquery' ] and globals: { jquery: '$' }. But it works either way.

@wearhere
Copy link
Author

wearhere commented Jan 2, 2017

Awesome @Rich-Harris, I can now see how they differ. I'm still confused about when you'd use them separately though—doesn't globals: { jquery: '$' } imply that 'jquery' is an external module? And how would jQuery be supplied to the bundled code if external: ['jquery'] other than as a global?

On that latter question, your remark about globals only applying to iife / umd bundles is intriguing; I can see that external: ['jquery'], globals: {jquery: '$'}, format: 'es' with input

import $ from 'jquery';

$(document.body).text('hello world');

results in output

import $ from 'jquery';

$(document.body).text('hello world');

so there's one case where I guess you could use just external and no globals (globals doesn't even do anything)… but why would you want to—isn't that just a broken module, without 'jquery' being fulfilled? Is it intended to be further processed somehow?

Anyhow, it still seems to me that globals implies external even if the reverse is not true.

@cool-Blue
Copy link

cool-Blue commented May 27, 2017

@wearhere I'm also confused but maybe an internal module could be exported onto the window by setting it as a global but not external. I mean, maybe it was considered as a future possibility.

So I guess what @Rich-Harris is trying to say is that (sorry, I'm just logging it here for my own benefit):

globals are assumed to have their field value on the window object and can be referenced inside the bundle by their field name

  globals: {
    name: 'Value',
  },

assumes that some other script tag or whatever establishes window.Value and the emitted umd bundle for example, calls the factory like factory(global.Value). So globals is just stuff to bring into the factory on the globals object. It doesn't even make it "global" inside the bundle.
Basically, the resolver does not check the globals object during the loading process.

The resolver needs to be told how to link these globals and that's what the external option is for.

  external: ['name'],

Then you can reference it like

  import myName from 'name';

  myName();

wearhere added a commit to wearhere/rollup-plugin-node-resolve-globals that referenced this issue Jul 13, 2017
@wearhere
Copy link
Author

wearhere commented Jul 13, 2017

globals is just stuff to bring into the factory on the globals object

That makes sense to me @cool-Blue! And since my Jan 1 comment we've learned why you might want to use external but not globals: libraries.

We've started to factor some of our client-side JS as libraries to share between projects. These libraries import $ from 'jquery'. However they don't want to presume how that import might be "fulfilled". In most projects it's fulfilled from a global i.e. a script loaded from a CDN. However in one project it's fulfilled from a local copy of jQuery for reasons I won't get into.

So when these libraries bundle themselves for distribution, as ES6 modules, they mark 'jquery' as an external and not as a global. This leaves the import statements in the bundle. (Warning: Don't bundle as an IIFE or UMD, or Rollup will guess at fulfilling the import from a global, as @Rich-Harris mentions above.)

Then, the projects that use these libraries get to process these import statements how they like when they are bundled. For the ones that wish to load jQuery from a global, we again mark 'jquery' as an external—since we still don't want Rollup to bundle jQuery—and as a global.


I still don't know when you would want to use globals but not external. This branch features a project that does that, also doesn't bother importing 'jquery' since Rollup will error if it can't be resolved and is not an external, and when you bundle the project using npm install && npm run build, you can see that the IIFE in bundle.js isn't executed with $. So, globals without external (+ an import of the external) seems to do nothing.

@backspaces
Copy link

Isn't import $ from 'jquery'; illegal?

module-name
The URL of the module to import; this should be a complete relative or absolute URL to the .js file containing the module, including the .js extension. Certain bundlers, along with Node.js, don't require the use of the extension, but the best practice is usually to use it unless you're certain you dont need it.

All my imports are relative paths, either ./Foo.js or ../bar/Foo.js.

If I use this format, how would I specify the global IDs? Just 'Foo'? Or the full path './Foo.js' or '../bar/Foo.js'?

@wearhere
Copy link
Author

@backspaces in this case import $ from 'jquery'; is not processed by the browser at runtime, but rather by Rollup at build-time. Rollup bundles multiple files that use import and export statements into a single script that no longer contains any import statements, but rather either contains all the JS it needs to function (more-or-less copied from the other files) or "loads" the JS from global variables.

@backspaces
Copy link

Thanks! Wasn't clear that Rollup wasn't using es6 Module syntax. I know webpack and others do the same.

Thanks to your seriously excellent docs I noticed I could have a function for globals so this solved my problems .. thanks:

function globals (id) {
  const jsNames = {
    'stats.wrapper.js': 'Stats',
    'dat.gui.wrapper.js': 'dat',
    'three.wrapper.js': 'THREE'
  }
  const fileName = id.replace(/^.*\//, '')
  return jsNames[fileName]
}

@mesqueeb
Copy link
Contributor

mesqueeb commented Jun 29, 2018

Dear community.
Sorry to post on a closed thread. I'd like a little better understanding on what globals exactly provides.

I am pulling in an NPM package, and added it to "external". Now Rollup is trying to guess it's global variable name. But I have 2 questions:

  1. Will there ever be a use for this global variable name if my package just uses import x from 'some-npm-package' at the top of each file I'm using this external npm package?
  2. One of the packages it's trying to guess the global name for is one of my other npm packages. Is there something I should configure in rollup in this other package to have a global variable name defined from the start? So not everyone who uses this package will receive this warning?

I'm not 100% sure what is meant with this:

globals on the other hand tell Rollup that these things just exist in scope and simply provide reference using that global.

@eight04
Copy link

eight04 commented Jun 29, 2018

If you are bundling into iife format, you would like to map external modules to global variables by using the globals option. For example:

entry

import x from "some-npm-package";
console.log(x);

config

...
output: {
  ...
  globals: {
    // map 'some-npm-package' to 'SomeNPMPackage' global variable
    "some-npm-package": "SomeNPMPackage"
  }
},
external: ["some-npm-package"]

Bundle result:

(function(x) {
  console.log(x);
})(SomeNPMPackage); // the 'SomeNPMPackage' global variable is used as 'x'

One of the packages it's trying to guess the global name for is one of my other npm packages. Is there something I should configure in rollup in this other package to have a global variable name defined from the start? So not everyone who uses this package will receive this warning?

Why users would receive the warning? Isn't the warning is generated by rollup?

@mesqueeb
Copy link
Contributor

@eight04 Thank you, I see that it's only relevant for IIFE builds? (I do get the warning on es and cjs builds as well though.)

And for other users to prevent to receive this warning: Yes, it's only on rollup, I meant if other users use Rollup, can I set up the

globals: {
    // map 'some-npm-package' to 'SomeNPMPackage' global variable
    "some-npm-package": "SomeNPMPackage"
  }

on the package side, so not all users who import it and use rollup on it need to do this themselves?

@eight04
Copy link

eight04 commented Jun 30, 2018

I do get the warning on es and cjs builds as well though

That's weird. globals should only affect iife or umd build.


You can use browser field to tell the bundler that you want to shim specific modules, but I think it is a bad idea to enforce users how to bundle your library.

namjul pushed a commit to namjul/gumption-ui that referenced this issue Mar 2, 2021
Rollup warns about not providing an globals for external modules for
reasons explained here: rollup/rollup#1169 (comment)

In developit/microbundle#223 the suggestion is
to mark the modules as globals to tell rollup that it's ok.

It seems to me that microbundle should also apply globals for scoped
packages as it does for non-scoped.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants