whatwg / loader

Loader Standard
https://whatwg.github.io/loader/
Creative Commons Zero v1.0 Universal
609 stars 45 forks source link

[spec] Spec text for ParseExportsDescriptors() and CreateModuleMutator() abtracts #119

Closed caridy closed 8 years ago

caridy commented 8 years ago

Features

ParseExportsDescriptors(obj)

Parses a descriptor object passed into the reflective module record constructor.

Parse as in these examples:

More details here: https://gist.github.com/dherman/fbf3077a2781df74b6d8

CreateModuleMutator(module)

Creates a proxy object to compat-mode with CJS and other module formats.

End-to-end example:

import * as fs from "fs";

let exports;

// reflective API takes environment descriptors and uses revealing constructor pattern
// to expose mutators to the creator of the module instance object
var mod = new Reflect.Module({
  // initialized mutable (includes "uninitialized" var)
  x: { value: undefined },
  y: { value: 42 },

  // uninitialized mutable (let, class)
  z: { },

  // initialized immutable (const)
  PI: { value: Math.PI, const: true },

  // uninitialized immutable (const)
  TAU: { const: true },

  // re-export
  readFile: { module: fs, import: "readFile" }
}, function(mutator, ns) {
  exports = mutator;
});

// mutator object uses setters to write through to the module's encapsulated environment
console.log(mod.y); // 42
exports.y = 43;
console.log(mod.y); // 43
caridy commented 8 years ago

/cc @wycats @dherman

matthewrobb commented 8 years ago

Is ModuleMutator sort of a desugaring for live module bindings?

caridy commented 8 years ago

@matthewrobb yes, this PR is all about allowing the creation of module records using an imperative api, to match what you can do with the declarative import and export declarations. Here is an example of what you can do: https://github.com/caridy/loader.js/blob/master/examples/simple.js

matthewp commented 8 years ago

So the mutator callback allows you to mutate a module's bindings. Cool. Can you add new properties or only mutate the existing ones?

What is the second callback for, evaluate, I'm guessing it's called to execute the module body? Any way to modify that on an existing module (assuming it hasn't been evaluated)? Maybe mod['[[Evaluate]]'] = function() { ... ?

caridy commented 8 years ago

So the mutator callback allows you to mutate a module's bindings. Cool. Can you add new properties or only mutate the existing ones?

you can't not add or remove bindings (just like you can't do it with the declarative form), just modify exiting bindings marked as "mutables" in the export descriptor object passed into the constructor of the reflective module record.

What is the second callback for, evaluate, I'm guessing it's called to execute the module body? Any way to modify that on an existing module (assuming it hasn't been evaluated)? Maybe mod['[[Evaluate]]'] = function() { ... ?

It is an optional callback to match the evaluation step on source text module records. In that callback, you can set new values for some of the bindings in mutator, that's all it is. And yes, it is the internal slot [[Evaluate]] on the module record, but that's just internal, users can't modify that in any way.

guybedford commented 8 years ago

I take it these proxy objects wouldn't handle binding circular references? Or is it entirely not possible to create them with the API?

caridy commented 8 years ago

@guybedford when creating new dynamic module records, you will have to pass a namespace reference in the export descriptor, which means you need access to it ahead of time, but that namespace can be bound to a source text module record that will fulfill its dependencies later on, so it is entirely posible to have dupes via this API.

Aside from that, remember that re-exports descriptors are immutable, so the proxy only has getter for those.

caridy commented 8 years ago

got +1 verbally from @dherman, merging!