We're thinking to give to every single module the capability to declare
for which application is designed for (e.g. Firefox, Fennec, etc).
At the moment, this is done at runtime:
"use strict";
if (!require("api-utils/xul-app").is("Firefox")) {
throw new Error(...);
}
// module code
"use strict";
if (!require("api-utils/xul-app").isOneOf(["Firefox", "Fennec"])) {
throw new Error(...);
}
// module code
But we think it should be better having this information at packaging
time, using the `cfx` tool, that display a warning about that, without
having to run the code. In addition, is some kind of information that is
useful when we build the dependencies graph; and for sharing modules.
In that way, the `cfx` can package the addon for the proper target(s)
automatically.
Note that if a module claims to be compatible with an application, `cfx`
will assume implicitly that also the dependencies of that module are
compatible as well. Only if the declaration is missing the `cfx` will
check the dependencies compatibility.
So we can have a module with multiple implementation for different
application, for example:
// we are in tabs.js, that claims to be compatible with Firefox
// and Fennec
let app = require("api-utils/xul-app");
if (app.is("Firefox")) {
module.setExports(require("tabs-firefox")); // compatible with FF
} else if (app.is("Fennec")){
// Ideally we already claim the compatibility for both, so this `if`
// could be unnecessary
module.setExports(require("tabs-fennec")); // compatible with Fennec
}
And if we have some generic approach for that information, we could
extend it in the future to include other data like "experimental" flag,
for example.
So guess what, we end up with module metadata. :)
So here, some proposal, that I'd like to discuss with you. Feel free to
add other approach!
1. Using "require" to define the meta data as javascript object. The
module we
require is a "special one" that we use just to "trigger" `cfx`.
"use strict";
require("@meta").data = {"compatibility" : ["Firefox", "Fennec"]};
2. Adding `meta` property to the `module` object. In this way the module
itself
can access to this information.
"use strict";
module.meta = {"compatibility" : ["Firefox", "Fennec"]};
// module code
3. Adding `meta` property to the `exports` object. In this way both
module and the module that require this one can access to this information.
"use strict";
exports.meta = {
"compatibility" : ["Firefox", "Fennec"]
};
// module code
4. Use a JavaScript object at the beginning, like "use strict"; without
assign it, so it will be discarded. No one can access to this
information at runtime.
"use strict";
{"compatibility" : ["Firefox", "Fennec"]};
// module code
5. Use jsdoc-style comment:
"use strict";
/**
* @application Firefox, Fennec
*/
//module code
6. Last but not least, using the Module Metadata proposal by CommonJS:
<http://wiki.commonjs.org/wiki/Modules/Metadata>
"use strict";
META({
"compatibility" : ["Firefox", "Fennec"]
});
// module code
Note that the JavaScript object I used is not the definitive one.
Hereis my feedback:
1. -1 too magical & contradicts with idea that module exports are immutable after the are exported
2. +1 I like that, also metadata could be shared if desired exports.module = module
3. +1 Can't decide if I prefer 2. or not, depends how often requirer will need this
4. +1 I like this as it makes clear that metadata is immutable at runtime, also I would go for the folllowing instead
meta: ({ firefox: true })
Label makes it easier to parse and read
5. -1 no syntax highlighting, no parse errors on mistypes
6. -1 yet another global :(
7. Mabe something like:
module.meta({ .... })
Just like 2. & also makes it more obvious that metadata is immutable.
Typed on touchscreen device
--
You received this message because you are subscribed to the Google Groups "mozilla-labs-jetpack" group.
To post to this group, send email to mozilla-la...@googlegroups.com.
To unsubscribe from this group, send email to mozilla-labs-jet...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/mozilla-labs-jetpack?hl=en.
> 2. +1 I like that, also metadata could be shared if desired
> exports.module = module
> 3. +1 Can't decide if I prefer 2. or not, depends how often requirer
> will need this
> 4. +1 I like this as it makes clear that metadata is immutable at runtime,
>
I like these too. Between 2 and 3, 2 seems preferable to me, since it
doesn't add a symbol to every module's `exports` object, so it reduces
the surface area that modules expose by default.
Plus, currently there are no symbols in the `exports` namespace that are
special in any way, and the module has complete control over what to put
in that namespace; whereas 3 would make the `meta` symbol special.
And the "module" object seems like the more logical home for metadata
about the module anyway.
We could still implement some other way for modules to retrieve metadata
about other modules at runtime if that seems useful, f.e. via a
getMetadata method on the require method:
require.getMetadata("module")
And I wonder whether we need a "meta" object at all. Isn't this the
purpose of the 'module` object: to collect metadata about the module? It
seems like we should be able to hang this information directly off that
object.
> also I would go for the folllowing instead
>
> meta: ({ firefox: true })
>
> Label makes it easier to parse and read
>
I like the simplicity of this approach, but I'm concerned that it would
be ambiguous, since it doesn't mention compatibility.
Nevertheless, it's true that the word "compatibility" is fairly long and
complex. Since modules "require" other modules and "export"
functionality, we might say they "support" host applications and make
the meta object look like:
{ supports: ["firefox", "fennec"] }
Or:
{ supports: { firefox: true, fennec: true } }
> 5. -1 no syntax highlighting, no parse errors on mistypes
> 6. -1 yet another global :(
>
Indeed!
> 7. Mabe something like:
>
> module.meta({ .... })
>
> Just like 2. & also makes it more obvious that metadata is immutable.
>
Since this method, like the property assignment, happens at runtime,
might developers think they can call it again later?
Perhaps making developers define a constant would send the clearest signal:
const meta = { ... };
But I suspect it's sufficient to document that the object is immutable
and throw a friendly and informative exception if code tries to modify
it after defining it.
> Typed on touchscreen device
>
Wow, impressive! :-)
-myk
[adding metadata to module object]
>> 2. +1 I like that, also metadata could be shared if desired
>> exports.module = module
> I like these too. Between 2 and 3, 2 seems preferable to me, since it
> doesn't add a symbol to every module's `exports` object, so it reduces
> the surface area that modules expose by default.
> Plus, currently there are no symbols in the `exports` namespace that are
> special in any way, and the module has complete control over what to put
> in that namespace; whereas 3 would make the `meta` symbol special.
> And the "module" object seems like the more logical home for metadata
> about the module anyway.
Good to see we're all on the same page! I also prefer use `module`
instead of `require`.
> We could still implement some other way for modules to retrieve metadata
> about other modules at runtime if that seems useful, f.e. via a
> getMetadata method on the require method:
>
> require.getMetadata("module")
Indeed!
> And I wonder whether we need a "meta" object at all. Isn't this the
> purpose of the 'module` object: to collect metadata about the module? It
> seems like we should be able to hang this information directly off that
> object.
True. I'm just worried that in this way `module` could be transformed in
a sort of "junk collector", where people add arbitrary stuff. Also, it
should be clear that `supports` is immutable and has a specific meaning
for the SDK.
Of course, we could `seal` the module itself.
>> also I would go for the folllowing instead
>>
>> meta: ({ firefox: true })
>> Label makes it easier to parse and read
Note that this approach made the label unusable as "label" (but I guess
it's perfectly fine in our case, we don't want use `continue` on that).
> I like the simplicity of this approach, but I'm concerned that it would
> be ambiguous, since it doesn't mention compatibility.
Well, can be easily transformed in:
supports: {firefox : true, fennec : true};
Or (I personally prefer this one):
supports: ["firefox", "fennec"];
I guess we agreed that the options available at the moment are:
a. Using module to store this information: adding a `supports` property
directly, or using a `metadata` property as middleman, decided if we
want seal it or not.
b. Using a JavaScript object (� la "use strict")
c. Using a label.
We should also think how the approach we will choose it will works in
case of additional future information (e.g. `experiment` boolean value,
or module's `version`).
After a meeting, we filtered out the proposals to the one that use labels.
One proposal use one label to point to a JS object. So, an example of
module, could be:
"use strict";
// The module supports both Firefox and Fennec
meta: {
supports : ["Firefox", "Fennec"]
};
let app = require("api-utils/xul-app");
if (app.is("Firefox")) {
module.exports = require("foo-firefox");
} else if (app.is("Fennec")) {
module.exports = require("foo-fennec");
}
with more properties, will be:
"use strict";
// The module supports both Firefox and Fennec,
// and is marked as experimental
meta: {
supports : ["Firefox", "Fennec"],
flags: ["experimental"],
version: "0.1"
};
let app = require("api-utils/xul-app");
if (app.is("Firefox")) {
module.exports = require("foo-firefox");
} else if (app.is("Fennec")) {
module.exports = require("foo-fennec");
}
The second proposal, is use different labels without having a javascript
object but just primitives/array:
"use strict";
// The module supports both Firefox and Fennec
supports: ["Firefox", "Fennec"];
let app = require("api-utils/xul-app");
if (app.is("Firefox")) {
module.exports = require("foo-firefox");
} else if (app.is("Fennec")) {
module.exports = require("foo-fennec");
}
In that case, different properties will have different labels:
"use strict";
// The module supports both Firefox and Fennec
// and is marked as experimental
supports: ["Firefox", "Fennec"];
flags: ["experimental"];
version: "1.0";
let app = require("api-utils/xul-app");
if (app.is("Firefox")) {
module.exports = require("foo-firefox");
} else if (app.is("Fennec")) {
module.exports = require("foo-fennec");
}
Personally, although initially I was more in favor of the last approach,
now that I can see them on real code, if we can be strict about
definition of the meta's object, it's better have these metadata delimited.
Not sure honestly about the term "meta" as label's name.
What are you thoughts about it?
Not sure honestly about the term "meta" as label's name. What are you thoughts about it?
>> Not sure honestly about the term "meta" as label's name.
>> What are you thoughts about it?
> Bear in mind that you are asking a broad audience to help you paint a
> bicycle shed. ;-) Having said that...
Not sure what it means.. :) However, I was asking about the whole ideas,
not just the name, if it's that what you meant! :)
> How about "manifest"? The structure isn't a classic manifest file
> <http://en.wikipedia.org/wiki/Manifest_file>, but it contains the kind
> of information that would normally be contained in such a file. It's a
> kind of "inline manifest."
Ehe, when I wrote this email, it was my first attempt. :) Then I decided
to rollback to "meta" because was short, and I was worried that
"manifest" could be misleading. Honestly, the term that I personally
find to fit best is "module" itself, but of course can't be used. :)
> This syntax doesn't look valid: "meta: {}".
> Did you meant: "meta = {}" ?
I mean exactly "meta: {}", it's a label.
> Having said that I prefer the solution using module as it limits the
> number of special globals.
It's not a special global in fact! :)
> Then I don't see that much value in such feature? The important part is
> about how we are going to load different pieces of code for each
> platform
The important part is how we're going to add metadata to a specific
single module, and not to the whole package (with packages.json).
Then, we can use this information to specify which platform this module
support. As I wrote in my first post, this information will be parsed by
cfx in order to package the addon for the right platform, and have a
dependencies graph with the compatibilities for each module.
Loading different module's implementation "automagically" is not covered
by this proposal directly. As I shown in the code, is something you can
do it by your own, and you're interested in the running application at
runtime.
Not sure honestly about the term "meta" as label's name.
Alex,Primary reason we for choosing labeled anonymous object over other options listed above was:It's explicitness about the fact that metadata, is immutable (as there is no reference to it) and it's not part of the module implementation. Also, this is not something average developer will have to deal with. Explaining what code does should be as easy as explaining what "strict mode", in fact your link will be quite helpful:BTW do you have a better option in mind ?
On Thursday, 2011-11-10 at 09:03 , Alexandre poirot wrote:
2011/11/10 Irakli Gozalishvili <rfo...@gmail.com>Alex,Primary reason we for choosing labeled anonymous object over other options listed above was:It's explicitness about the fact that metadata, is immutable (as there is no reference to it) and it's not part of the module implementation. Also, this is not something average developer will have to deal with. Explaining what code does should be as easy as explaining what "strict mode", in fact your link will be quite helpful:BTW do you have a better option in mind ?
I don't know what we are going to do with these data, but I'd expect to use them during runtime.
So that this choice won't simplify final implementation of it. Are we sure that this syntax is correct? Aren't we just lucky that something is accepted after a label?
Then we have to be carefull when it comes to compile time parsing. Currently we only parse `require()`. It is simplier to match than this metadata object but we are not doing it very nicely! (I don't think we can build a regexp for JSON!)
Then about exclicitness, I don't see much differences between exports and meta, both are immutables.
Finally, if we start suggesting modules over packages for libraries, average developers will have to deal with this metadata object.
TBH I like the initial option 2:
module.meta = {"compatibility" : ["Firefox", "Fennec"]};
On Sunday, 2011-11-13 at 18:01 , James Burke wrote:
On Nov 3, 7:39 am, ZER0 <z...@mozilla.com> wrote:We're thinking to give to every single module the capability to declarefor which application is designed for (e.g. Firefox, Fennec, etc).[snip]But we think it should be better having this information at packagingtime, using the `cfx` tool, that display a warning about that, withouthaving to run the code. In addition, is some kind of information that isuseful when we build the dependencies graph; and for sharing modules.I am unclear on the need for individual modules in a package to needto declare themselves with individual metadata. I can see the case forsaying "do it as a different package, or reserve a spot in thepackage.json for this".
That said, in the AMD world, it is common that people will want todeliver just one JS file as "the package", and requiring apackage.json file for just one file is not ergonomic. I was looking atsupporting the contents of the package.json in a JS comment, somethinglike:/**package.json:{...}*/Just an example, I do not really care what the comment format was,just that it was a comment with a particular name that had a JSON blobin it.I favor a comment over JS code because this info is needed to set up aproject, but not needed at runtime, and for web deployment, using acomment meant that it automatically got stripped when the code isminified for deployment.
Jetpack's needs may be different, so feel free to disregard this. Ifthere was something that Jetpack chose that would fit for the AMDworld though, I would look at trying to reuse it.
James
Coming back to this thread since ZER0 reminded me to do that :)So I do like loader plugins as well, but it's something that we may get in a future and is independent of metadata proposal.So my opinion still is the same I think, here is my list ordered by preference:
1. labeled objects meta: ({ � })� � Pros:�-�Explicit about the immutability� � � � - GC-ed on first gc cycle�� � � � - Minifies should be able to strip it out as unreferenced objects�� � � � - Editors may help catching errors� � � � - Easy to parse� � Cons:� � � � - Depends on non very popular JS feature (labels)� � � � - Some may confuse with meta = {} (I think we should wrap in parens, maybe even double meta: (({})) to make it stand out)� 2. Define as meta property for module: module.meta = { � }� � � Pros:�- Plain JS, no globals no exports just convention� � � Cons:� � � � �- May be defined in multiple expressions, making it hard to parse both by code and by eyes� � � � �- We will look like plain js but we'll have to add limitations that may be confusingWith all pros and cons listed I think it would make sense to go with #1 and maybe refine that later. I could also be convinced in` #2�
Regards