-
Notifications
You must be signed in to change notification settings - Fork 154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make output code independent of all globals #45
Conversation
So this puts all those functions in every single compiled function? Wouldn't it make more sense to just reference them on the |
Oh, it doesn't do per function, it wraps the group of all built messages with a set of the functions. That seems fine if you use the |
The problem with that approach is that Maybe we're talking a bit past each other here, as we're clearly using the library in different ways. Could you point me at some pre-existing Handlebars or other extension that uses messageformat.js, so I could see how the code is used there? That might help me to understand what you're looking for. |
Sure. So I do two things: <div>
{{#mf "thekey" .}}
There are {count} results.
{{/mf}}
</div> I parse the AST of the Handlebars templates, and find instances of Handlebars.registerHelper('_mf_thekey', function(data) {
var msg = function(d){ /* precompiled mf function */ };
return msg(data);
}); <div>
{{_mf_thekey .}}
</div> Technically I could output all of those Secondly, I can request messages as modules with an AMD plugin. require(['mf!thekey'], function(msg) {
console.log(msg({count: 5});
}); When I run this in dev mode, it's just compiling stuff on the fly and eval'ing. But when I run the optimizer across these plugins, they output static modules that look like this: define('mf!thekey', ['messageformat'], function(MessageFormat) {
return function(){ /* Precompiled messageformat func here */ };
}); In that case, I actually don't use the global, but internally in the old precompiled output, the function referenced a few properties on the define('mf!thekey', ['messageformat'], function(MessageFormat) {
return function(){ ... MessageFormat.p(...); ... };
}); I end up not using the There is not a pretty way to have the require.js optimizer wrap a set of |
So if I've understood right, you need to either include everything inside each function call, or define a global somewhere. Can you define/modify that global after reading the template, or does it need to be static from the very beginning? With something like that, you could read the template for all the Handlebars.registerHelper('_mf_thekey', function(data) {
return i18n['thekey'](data);
}); And for the static modules: define('mf!thekey', ['messageformat'], function(i18n) {
return i18n['thekey'];
}); Where We could generalise code from Or we could change all the internal refs to point to something like Hmmm... It might still be possible to support the model currently used in the CLI output with such a scheme... It gets a bit tricky sometimes, writing JavaScript that outputs JavaScript that may or may not be immediately executed, or it may get written to disk for later use. Btw, about the gzipped size; you're right that the cost is minimal if all the functions are prefixed with |
(sorry for long delay, just got back on a 3 week trip) Hey @eemeli let's compromise on this maybe? Let's keep in the closure style Seem crazy? Too much? Good compromise? |
Actually, I sort of came around to your point of view, after getting rather deep into how closures work in JS, and how adding something to a closure after it's been, well, closed, just doesn't work. I've not worked on this in about two weeks, but I've a nearly ready branch that should fix this in a way that makes everyone happy. What I've done there is add a function for now called But like I said, that branch requires some cleanup and now re-basing, so shouldn't rely on it yet. |
I've submitted pull request #51, which replaces this one. |
This fixes the issues raised here by making the one-letter utility functions properly local to the output, rather than leaking into the global namespace.
This is done by adding a new function
MessageFormat.LocalFunctions()
, which returns said function declarations as a string that's included bycompile()
inlib/messageformat.dev.js
as well asbuild()
inbin/messageformat.js
. In the former, the functions are included within the scope of thenew Function()
-generated function; in the latter, they're scoped within the wrapping anonymous function.As a side product,
lib/messageformat.include.js
is now obsolete and hence removed; this is good because it means that the utility functions are only defined in one place.This also enabled me to remove references to
MessageFormat.locale
from the generated code, as they're now wrapped up byMessageFormat.LocalFunctions()
.If this pull request is merged,
MessageFormat.prototype.compile()
will always return a function that's independent of all globals, includingMessageFormat
itself. However, to write that function to disk for later use, you'll need to also write the output ofMessageFormat.LocalFunctions()
to a namespace accessible from the same place, just likebuild()
does.While I was poking at the code, I also simplified things a bit by inlining the utility functions that are each only used once and could be replaced by single lines of code. As a result,
fallbackLocale
,pluralFunc
,SafeString
andUtils
have been removed.