jorendorff / js-loaders

Pseudoimplementation of the proposed ES6 module loaders.
54 stars 7 forks source link

does LinkDynamicModules have to execute code? #63

Closed guybedford closed 10 years ago

guybedford commented 10 years ago

If the module pipeline seems to be built around deferring execution, surely LinkDynamicModules could omit the execution step, and this could be done in EnsureEvaluated rather?

jorendorff commented 10 years ago

LinkDynamicModules only runs for factory-instantiated modules, that is, support for legacy module systems like AMD.

I don't see how we can link a whole dependency graph including AMD modules, producing Module objects, and commit them to the registry, without executing any user code, and retain compatibility with AMD. We have no module objects until we execute the user code. That's just how AMD etc. work -- user code runs and produces modules.

guybedford commented 10 years ago

I understand, it can just be a bit surprising to users that typically:

  <module name="some-module">
    console.log('execute');
    export var p = 5;
  </module>

Doesn't execute until a System.get, while an AMD module:

  <module name="some-module">
    console.log('execute');
    define({
      p: 5;
    });
  </module>

running through an instantiate hook, would execute immediately in the page.

The way I've implemented the ES6 Module Loader, ES6 itself is implemented as a dynamic instantiation in the polyfill, so execution would always happen here, making this difference quite perceptible.

guybedford commented 10 years ago

I've been thinking about this some more, and of course AMD has to execute to get dependencies - that is the nature of the AMD format.

I really don't think this is an issue after all, so am closing this.

guybedford commented 10 years ago

I still think this is possible if we do something like https://github.com/jorendorff/js-loaders/issues/102#issuecomment-36415182

natefaubion commented 10 years ago

I've been looking into using the Loader API for sweet.js. Being able to load without executing is critical for our use case. We need to be able to load a module, which collects compile/translation-time macro transforms, but defer execution until it is actually needed for runtime. Some modules that have macros don't actually have a runtime component, and so don't ever need to be executed.

samth commented 10 years ago

@natefaubion I'm not sure I understand how load without execution is needed for sweet.js. If a module has a macro definition in it, then presumably you want to run the definition, just as if a module has a function definition in it.

Can you say more?

natefaubion commented 10 years ago

Sure! Currently, Sweet "modules" (if you can call them that) have the limitation that they can only contain macros, but that's only because it isn't integrated with any module system. Moving forward, modules will be a combination of compile-time macros and runtime code, with macros possible generating code that relies on the runtime code, or the runtime code of other libs. We are going to use the ES6 module syntax to bridge the gap between runtime imports and compile-time imports. This necessarily creates two separate phases in which you must evaluate the code.

Macros are collected and evaluated during the translate hook. They don't exist anymore by the time you are ready to execute the runtime module. This means, while we are translateing, we need to load imported modules (but not execute them) so that we can collect macro dependencies. While macros are evaluated, the rest of the code still exists as a token stream. The reason we don't want to execute the module is because we probably don't even need to if the code doesn't depend on any of its runtime code. After everything is expanded, then we have a clear idea of what our actual runtime dependencies are (macro expansion can result in new dependencies!), and we can proceed as usual to instantiate and execute the module.

Additionally, Sweet is primarily a compiler (which still must load dependencies), so it would be fantastic if we could use the same code, with the only difference being a call to import to actually run the resulting code, and a roundabout call to load that doesn't run the code.

I'm not saying that deferring execution with load is the answer, but having comparable flexibility is important to us.

As an aside: Sweet is currently totally synchronous, so we can't even use the API yet (I'm messing with a synchronous approximation of the API). But async macros are only a matter of time, at which a mostly drop-in replacement should be possible.