esnext / es6-module-transpiler

Tomorrow’s JavaScript module syntax today
http://esnext.github.io/es6-module-transpiler/
Other
1.21k stars 85 forks source link

Support cycles #102

Closed thomasboyt closed 10 years ago

thomasboyt commented 10 years ago

Here's what we need to even get started:

Implementation details

thomasboyt commented 10 years ago

Prior work from #85 discussion:

thomasboyt commented 10 years ago

test case from harmony wiki (which tends to be super out of date, but this one seems okay): http://wiki.ecmascript.org/doku.php?id=harmony:modules_examples#cyclic_dependencies

// easy!
module 'Even' {
    import odd from 'Odd';
    export default function even(n) {
        return n == 0 || odd(n - 1);
    }
}

// woo-hoo!
module 'Odd' {
    import even from 'Even';
    export default function odd(n) {
        return n != 0 && even(n - 1);
    }
}
thomasboyt commented 10 years ago

current output:

// a.js
"use strict";
var even = require("./a")["default"];

exports["default"] = function odd(n) {
  return n === 0 || even(n - 1);
}

// b.js
"use strict";
var odd = require("b")["default"];

exports["default"] = function even(n) {
  return n === 0 || odd(n - 1);
}

err:

> require('./tmp/b')['default'](5)
TypeError: undefined is not a function
    at Object.odd [as default] (/Users/thomasboyt/Code/es6-module-transpiler/test/cycles/tmp/b.js:5:21)
thomasboyt commented 10 years ago

I'm currently working on a solution using @wycats' Gist, seems like it should work well. It looks like the exports special argument in AMD modules should be able to use the same solution, too: http://requirejs.org/docs/api.html#circular

thomasboyt commented 10 years ago

branch to watch: https://github.com/square/es6-module-transpiler/tree/cycles

thomasboyt commented 10 years ago

Worth mentioning this branch has regressed by removing compatFix:

import DefaultExport from "non-es6-module";

new DefaultExport();

becomes

__imports__['non-es6-module'] = require('non-es6-module');
__imports__['non-es6-module'] = __imports__['non-es6-module'].__es6_module__ || __imports__['non-es6-module'];

new __imports__['non-es6-module'].default();

which, of course, explodes. Potential fix:

__imports__['non-es6-module'] = require('non-es6-module');
if (__imports__['non-es6-module'].__es6_module__) {
  __imports__['non-es6-module'] = __imports__['non-es6-module'].__es6_module__;
  __imports__['non-es6-module'].__is_es6__ = true;
}

new (__imports__['non-es6-module'].__is_es6__ ? __imports__['non-es6-module'].default || __imports__['non-es6-module'])();

which is unwieldy, to say the least.

(also, it occurs to me that if you need this behavior right now, you could, uh, just use require() for non-es6 modules. I wonder if this is actually not a crazy solution, considering that Node presumedly will continue supporting require after it actually introduces ES6 modules support sometime in 2018).

hax commented 10 years ago

Why not use getter?

//Even.js
"use strict";
Object.defineProperty(exports, "default", {
    get: function () {
        return even
    }
})
var odd = require("Odd")["default"];
function even(n) {
    return n == 0 || odd(n - 1);
}
// Odd.js
"use strict";
Object.defineProperty(exports, "default", {
    get: function () {
        return odd
    }
})
var even = require("Even")["default"];

function odd(n) {
    return n != 0 && even(n - 1);
}
rwaldron commented 10 years ago

Quick nit: If using Object.defineProperty then the platform is modern enough that default can be used as an IdentifierName instead of a string property accessor

merlinchen commented 10 years ago

Porting my project from requirejs to es6 module. Stuck with the cycle dep problem. @thomasboyt Are you still working on this ? :)

caridy commented 10 years ago

@merlinchen this work is now part of the recast refactor: https://github.com/square/es6-module-transpiler/pull/126

and yes, we are committed to preserve the semantics of ES6 modules for ES5 runtimes, and it might come in different flavors :)

eventualbuddha commented 10 years ago

This should now work properly in v0.5.1. Please give it a shot!