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

Build as UMD module and test in AMD #26

Closed
wants to merge 1 commit into from
Closed

Build as UMD module and test in AMD #26

wants to merge 1 commit into from

Conversation

tbranyen
Copy link

Added in --standalone option to Browserify build. Updated the
standalone file to return the selection module. Added a test showing
functionality with AMD.

Added in `--standalone` option to Browserify build.  Updated the
standalone file to return the selection module.  Added a test showing
functionality with AMD.
@mbostock
Copy link
Member

OK, it appears that this can work. Some nuances:

  • The global d3 should be exported in standalone mode, even if a module system is detected. (As you implemented in Using  d3#921.) That’s still happening here thanks to the global.d3 being set regardless of a module system being present. 👍
  • The module’s exported value should be d3, not Selection. Everything should hang off this object, as it did in the past.
  • The exported name should be d3, not d3Selection. I assume this name is only used when no module system is detected? And if we use the name d3 this export should have no effect because we’re already setting global.d3.

The goal of breaking up D3 into separate modules is that users can conveniently pick which parts of D3 they need. For example, say they want to add d3-selection-multi for multi-value map support. In Vanilla mode, that would mean something like this:

<script src="d3-selection.js"></script>
<script src="d3-selection-multi.js"></script>
<script>

d3.select("body").attrs({hello: "world"});

</script>

In RequireJS mode, like this, perhaps?

<script>

require(["d3-selection"], function(d3) {
  require(["d3-selection-multi"], function() {
    d3.select("body").attrs({hello: "world"});
  });
});

</script>

The nested require is unfortunate, but I don’t know how to declare that d3-selection-multi depends on a module named “d3-selection” (without also defining where that named module lives, since I want people to be able to load the d3-selection module from anywhere). See the standalone.js in d3-selection-multi for how it works.

@gmaclennan
Copy link

Yes those nuances make sense for how anyone would expect to use d3, but make it a little trickier. Browserify can create standalone modules in a namespace like browserify --standalone d3.selection and this will wrap the output in UMD code including something like (global.d3 || (global.d3 = {})).selection but that would attach all the exports of standalone.js to the d3.selection object which is not what you want (this code is in the UMD module, which is used by browserify for standalone modules).

You could create every single method of d3 as a standalone module... but no. I think this would need a custom browserify transform to do what you want in a clean way, unless there is already a transform or plugin that would do it. I have a feeling that what you need is somewhere in https://github.com/thlorenz/browserify-shim but I'm getting out of my depth...

@gmaclennan
Copy link

One way (the only way?) of doing this is a custom browserify plugin.

This branch creates a standalone module that attaches all methods to an existing global d3 object, or creates a new one if it does not exist. The export for CommonJS and AMD is an object with the methods defined in standalone.js - note that this is more than just d3.selection. This fulfills the nuances.

This is a bit of a hack. The plugin inserts this code into the UMD created by browserify (with a RegExp replace):

g.d3 = g.d3 || {};
var moduleExports = f();
for (var key in moduleExports) {
    if (moduleExports.hasOwnProperty(key)) {
        g.d3[key] = moduleExports[key];
    }
}

A neater way to do this would require modifying ForbesLindesay/umd to allow standalone modules to optionally extend globals.

Not sure if this is the best way of doing things. Buying into the npm / commonjs ecosystem does have advantages for those who are writing browser code with npm and browserify. Can write code like this to pick components of d3 as needed:

var d3 = {
  selection = require('d3-selection'),
  mouse = require('d3-selection/lib/d3/event').mouse,
  json = require('d3-xhr/lib/d3/json)
}

The more modular the files, the less overhead in the browserified code, approaching what Smash creates.

@mbostock
Copy link
Member

Proposed solution in #30. Seems simple and achieves what we need?

(You can still use Browserify to make custom builds; you’ll be using the files in lib/, though, not standalone.)

@mbostock
Copy link
Member

Superseded by #30.

@mbostock mbostock closed this May 25, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants