Skip to content

Instantly share code, notes, and snippets.

@ryanflorence
Last active December 19, 2015 21:39
Show Gist options
  • Save ryanflorence/6021229 to your computer and use it in GitHub Desktop.
Save ryanflorence/6021229 to your computer and use it in GitHub Desktop.

ES6 -> CJS Tranpilation for UMD

ES6 and AMD require paths are raw, you can make them mean whatever you want, but by default mean baseUrl+path. In npm the paths are relative to the file calling require if the path starts with a directory separater, if there is no separator it looks in node_modules. For example:

// looks in node_modules
// we'll call this a "vendor require"
var handlebars = require('handlebars');
// looks relative to this file
// we'll call this a "relative require"
var local = require('./handlebars');

There is no such convention in AMD or ES6. Given this ambiguity we will need to be more intelligent with the umd transpilation.

Here's an idea, look in package.json

Perhaps we can check the dependencies in package.json to distinguish between local and vendor requires.

Given this package.json:

{
  "dependencies": {
    "rsvp": "*"
  }
}

The following module code...

import { EventTarget } from 'rsvp/events'
import format from 'time/format'

becomes:

var EventTarget = require('rsvp/events').EventTarget;
var format = require('./time/format');

(amd/global umd code omitted for clarity.)

Nested Files

Because node modules require relative, the transpiler needs to be aware of the current file's path.

Given a file nested two directories from root with the same code:

import { EventTarget } from 'rsvp/events'
import format from 'time/format'

The output becomes:

var EventTarget = require('rsvp/events').EventTarget;
var format = require('../../time/format');

Limitations

Vendor requires for global modules will not work. All vendor requires must map to an entry in package.json's dependencies or devDependencies.

@thomasboyt
Copy link

you can just use relative paths with the es6 transpiler & AMD just like you do with CJS modules. RSVP does this: https://github.com/tildeio/rsvp.js/blob/master/lib/rsvp.js

@domenic
Copy link

domenic commented Jul 21, 2013

I don't think this is the way you want to go. In Node, once ES6 hits, code will almost certainly be using a custom module loader that matches the current semantics. That is, import format from 'time/format' will mean the same as var format = require("time/format") does today; it will not mean var format = require('../../time/format'). Transpiling to the latter is likely to break code when we switch to real ES6, and the meaning becomes that of the former.

@ryanflorence
Copy link
Author

Ah crap that's right.

So is it hopeless? You either write a package for node or you write for the browser?

@domenic
Copy link

domenic commented Jul 21, 2013

I think if you want something truly universal, you do what @thomasboyt suggested, and use relative paths, reserving absolute paths only for packages. (Basically, follow Node style, since it's the lowest common denominator.)

Besides, I sincerely doubt anyone's going to use the default module loader in the browser. That is, nobody is going to lay out their app in the format

/backbone.js
/underscore.js
/ember.js
/my-code/
  /...

Some relevant discussion here: http://esdiscuss.org/topic/modulenaminganddeclarations#content-100

@ryanflorence
Copy link
Author

Yes, I prefer npm's handling of vendor requires. I have enormous path configs to move all vendor code out of my app with RequireJS and its silly.

RSVP ships totally different builds to different environments. If what we're saying is "the default module resolver won't be used" then 1) change the default (won't happen I don't think) or 2) get bower to act like node and then people can author once, ship everywhere.

@jrburke
Copy link

jrburke commented Jul 22, 2013

Giant paths configs in requirejs are antipatterns to me. The code should just be laid out so the default resolution works. This is possible with a package manager that writes an adapter module at 'dependency.js' that points to the dependency's main file inside of dependency/ directory.

In short, it works out, no giant configs and no custom module loader needed in browsers, which should be a goal for normal use.

But in general, for single file JS "packages" (which hopefully are the majority of third party code over time), package managers should just get smarter about how they are installed. It avoids an extra module hop in browsers.

More detail here:

desandro/requirejs-bower-homework#1 (comment)

@geddski
Copy link

geddski commented Jul 22, 2013

@domenic problem with relative paths is they're relative to the document.baseURI, which changes every time you use pushState/replaceState.

@domenic
Copy link

domenic commented Jul 22, 2013

@geddski I can't find any reference to that in the ECMAScript wiki, and it seems like a horrible idea anyway---if that's the plan, we need to talk them out of it, and into the sensible relative-to-the-current-module approach used by other systems.

@addyosmani
Copy link

@geddski I haven't been able to locate any references to that either. Do you have a link/citation that we could read up on? I agree completely - that would be a terrible idea.

My interpretation of current module loader plans are inline with what @domenic described - the current semantics are very likely to be matched per import format from 'time/format' => var format = require("time/format") and opting for the relative path solution makes the most sense.

I need to catch-up with http://esdiscuss.org/topic/modulenaminganddeclarations#content-100.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment