node-ractify
node-ractify copied to clipboard
Ractive plugins?
Hi there,
Any special trick to using Ractive plugins with Ractify? I'm trying to require a transition plugin per the docs, but I keep getting the same console warning:
Missing "slide" transition. You may need to download a plugin via https://github.com/RactiveJS/Ractive/wiki/Plugins#transitions
Not sure, I haven't used the plugins myself, and it looks like it should just work. What version of Ractive do you have installed? I suggest using the latest on npm: 0.4.0-pre2
Any ideas, @Rich-Harris?
Looking at https://github.com/RactiveJS/Ractive-transitions-slide/blob/master/Ractive-transitions-slide.js, I see a potential bug: require("Ractive")
. The npm package name is lowercase, so on case-sensitive systems, it won't find "Ractive"
.
Still trying to debug it, but I was using 0.4.0-pre1. I'll try the update.
This is the great irony/tragedy about the current hodge-podge of front end package management systems - they've made it harder to share code, not easier! npm decided that all packages needed to be lowercase, which meant that names needed to be changed in package.json, which had knock-on effects on other package managers... and at that point I'm afraid I totally lost interest.
I guess in the long term everything needs to become lowercase for the sake of interoperability (even though I think Ractive = require('ractive')
is ugly as hell). Sigh. In the meantime, is it an option to simply edit the Ractive-transitions-slide.js file so that it requires ractive
and not Ractive
? Or maybe even create a stub Ractive
module whose content is module.exports = require('ractive')
?
Apologies for my undisguised lack of enthusiasm here! Using 0.4.0-pre2 won't make any difference I'm afraid. I've opened a thread on the mailing list to see how everyone feels about lowercasing everything to avoid this problem in future.
Ok, a bit more info:
I have a browserify module with the following requires:
// Ractive
var Ractive = require("ractive");
require("ractive-transitions-fade");
require("ractive-transitions-slide");
console.log(Ractive.transitions);
On the browser, the log shows that both transitions are present, but the warning is present, and the transitions never fire:
@Rich-Harris the weird part is that he's not getting this error: throw new Error( 'Could not find Ractive! It must be loaded before the Ractive-transitions-slide plugin' );
Clearly a Ractive instance exists or else this line would also fail: Ractive.transitions.slide = slide;
Which would lead me to believe the case sensitivity might not be the issue here?
As an aside, var Ractive = require("ractive")
doesn't look too bad in the node ecosystem, since everything is that way. And it definitely beats the alternative of having all the bugs associated with case-sensitive or even case-insensitive module names (already have enough os x vs linux problems with that).
@doctyper could you gist the browserify output of that code you just pasted? Maybe something will reveal there...
Yup. I took a minute to strip the testbed down to the minimum. I have it down to four files. The following sample still generates the warning and does not apply the transition:
https://gist.github.com/doctyper/9752355
Progress! In the example, the warning goes away and the transition applies if I replace:
var Ractive = require("ractive");
With:
var Ractive = require("Ractive/build/Ractive.runtime.js");
I ran a require.resolve
to check the paths:
require.resolve("ractive"); // ./node_modules/ractive/build/Ractive.js
So it looks like there is some clobbering happening between the Ractive files? Maybe? And now I'm unsure if this is a Ractify issue, a Ractive issue, or a plugin issue.
Ah, that sounds promising - I came here to say it must have been a problem of multiple Ractive instances existing simultaneously, one with the slide plugin attached and one without. @marcello3d does Ractify require the runtime-only build?
Related question: does Browserify have an equivalent to the paths
config you get with most AMD loaders?
You're right, ractify is requiring the runtime-only build, so that will cause a problem (from the gist, it also looks like there's a problem due to OS X case-sensitivity).
I don't have a good solution in mind for the first problem...
Thinking a little more about it, the "node" way would be for the plugin to be added by the developer, not the plugin adding itself. What if you just exported the factory function instead of calling it with require() (e.g. module.exports = factory
)? Then you could do:
var Ractive = require('ractive') // or require('ractify') if you're using ractify
require('ractive-transitions-slide')(Ractive)
require('ractive-transitions-fade')(Ractive)
Alternatively in the case of the transitions you could just export the transition itself, and have the developer write:
Ractive.transitions.slide = require('ractive-transitions-slide')
Ractive.transitions.fade = require('ractive-transitions-fade')
This makes it more explicit.
There's definitely merit in those suggestions - it would avoid certain problems and give developers more control. At the same time, developers would need to understand that they needed to inject the same copy of Ractive that was being used with Ractified components. (Do you make the Ractive variable available inside Ractify? At least then component authors wouldn't have that worry if the dependency was stated inside the component definition.)
Though I also like the fact that I can currently have a single fire-and-forget require call for each plugin that I'm using across my entire app. Maybe it's possible to have the best of both worlds...
Ractive.transitions.foo = foo; // attach to current copy of Ractive...
// export a function that...
return function ( transitionOrRactive ) {
if ( transitionOrRactive.isTransition ) {
return foo( transitionOrRactive ); // ...can be used as transition
}
transitionOrRactive.transitions.foo = foo; // ...or to attach to any Ractive copy
};
Actually that's hideous, but you get the idea. Anyway if we did change the pattern, I think it should apply to all plugins consistently.
To be honest I'm more concerned about the fact that there's more than one copy of Ractive floating around in the app due to plugins requiring ractive
or Ractive
instead of ractify
or the equivalent path/to/Ractive.runtime.js
. That's extra bytes to download and more JavaScript to parse before any work can begin. Any thoughts on how we solve that problem? If we can fix that, we don't need to change how plugins behave (or at least it's less urgent).
I wouldn't worry about the ractify api too much, I can change it. The way it works today: require('ractify')
is a shorthand for, and will return the same js object as require('Ractive/build/Ractive.runtime.js')
. So if you want to access the same Ractive instance ractify uses, you can just require ractify.
Alternatively I could change it so ractified components simply return the { object:structure }
, and have the developer pass it into Ractive.extend(...)
. This might make the relationship between ractify and Ractive clearer. :-)
That's independent of the problem of requiring the full Ractive vs the runtime-only Ractive. They're currently two completely separate javascript files with repeated code, and they're going to export as two completely separate modules.
Food for thought: the "node" solution would be to make three npm separate modules: ractive-parser, ractive-runtime, and ractive (which would depend on the prior 2). That way there would be no duplication and you could have the plugins inject stuff into ractive-runtime. Then people just require the pieces they want and they should all play nicely together.
I could imagine the plugin pattern behaving a little differently with require/npm than with
As an aside, npm wasn't always case sensitive: https://github.com/npm/npm/issues/3914#issuecomment-24878907
I think returning a component definition rather than a constructor would be totally valid. One potential wrinkle is that (in the ractive.load and requirejs-ractive implementations, at least), the contents of the <script>
block (if there is one) are wrapped in an IIFE, inside which there are three variables - component
, require
, and Ractive
. This needs to be documented somewhere, but for the time being:
-
component
is just an object to which you attach the definition as theexports
property, same as you would withmodule
-
require
depends on context. In ractive.load if you do e.g.d3 = require('d3')
it will look forRactive.lib.d3
then fall back towindow.d3
. In requirejs-ractive it will treat it the same as a regularrequire()
call - in other words it will search forrequire()
calls and load those modules (asynchronously if necessary) before executing the script. The idea is to allow component authors to use a consistent interface for declaring dependencies outside Ractive without having to worry about the environment the component will be consumed in. -
Ractive
. In ractive.load this iswindow.Ractive
. In requirejs-ractive this is whatever theRactive
module ID resolves to, which is usually determined by thepaths
config you specify when setting up RequireJS (or curl.js or whatever).
I don't know if Ractify makes require
and Ractive
available? The question is what Ractive
should be. Though it also prompts a question of whether Ractive
should be available at all - truth be told it's probably only useful in extremely contrived circumstances, and could probably be handled with Ractive = require('ractive')
.
Separating the library into runtime and parser makes sense - I haven't bothered creating a standalone parser because you'd only ever run it in environments where you don't care about saving a few kb, but it's all AMD modules so it should be as straightforward as running the RequireJS optimiser with parse.js as a target instead of Ractive.js (the runtime version is built by 'stubbing out' that file). There's a bit of code overlap, so it wouldn't be quite as straightforward as recombining the two parts, but not far off. And it might pave the way for allowing different languages to be parsed (Handlebars, Jade, etc... as long as they can be represented with the same DSL primitives). I'll look into this.
We've digressed quite far from @doctyper's original question! The verdict was pretty unanimous on the mailing list - I'm in the process of lowercasing everything. So this issue should be resolved before long.
Because ractify works in browserify, the script code is wrapped in a module wrapper function which exposes require
(and exports
and module
). Ractify just generates the javascript that goes in that function.
See raw ractify output here (before being bundled up): https://github.com/marcello3d/node-ractify/blob/master/test/Clock-component.ract-output
I do not expose Ractive
, but it would be trivial to do so.
Did a determination ever come out of this? I'm running into plugin issues and I'd hate to need to add a script tag for ractive and the plugin, and then load the browserify/ractify version after those.
The latest version of ractify does not require any version of ractive for you, that means you can use the non-runtime version of Ractive (require('ractive')
), which in theory should match with the version the plugins are injecting themselves into.
That said, I don't think the plugins should be requiring ractive and injecting themselves since that prevents you from using the runtime ractive... feel free to contribute and help resolve that!
Oh ok, i was still trying to use the runtime. I'll give it a shot with just requiring ractive. Thanks!