node-ractify icon indicating copy to clipboard operation
node-ractify copied to clipboard

Ractive plugins?

Open doctyper opened this issue 10 years ago • 18 comments

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

doctyper avatar Mar 24 '14 20:03 doctyper

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".

marcello3d avatar Mar 24 '14 20:03 marcello3d

Still trying to debug it, but I was using 0.4.0-pre1. I'll try the update.

doctyper avatar Mar 24 '14 21:03 doctyper

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.

Rich-Harris avatar Mar 24 '14 23:03 Rich-Harris

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:

screenshot 2014-03-24 16 52 36

doctyper avatar Mar 24 '14 23:03 doctyper

@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).

marcello3d avatar Mar 25 '14 00:03 marcello3d

@doctyper could you gist the browserify output of that code you just pasted? Maybe something will reveal there...

marcello3d avatar Mar 25 '14 00:03 marcello3d

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

doctyper avatar Mar 25 '14 00:03 doctyper

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.

doctyper avatar Mar 25 '14 00:03 doctyper

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?

Rich-Harris avatar Mar 25 '14 00:03 Rich-Harris

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.

marcello3d avatar Mar 25 '14 01:03 marcello3d

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).

Rich-Harris avatar Mar 25 '14 02:03 Rich-Harris

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

marcello3d avatar Mar 25 '14 03:03 marcello3d

As an aside, npm wasn't always case sensitive: https://github.com/npm/npm/issues/3914#issuecomment-24878907

marcello3d avatar Mar 25 '14 18:03 marcello3d

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 the exports property, same as you would with module
  • require depends on context. In ractive.load if you do e.g. d3 = require('d3') it will look for Ractive.lib.d3 then fall back to window.d3. In requirejs-ractive it will treat it the same as a regular require() call - in other words it will search for require() 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 is window.Ractive. In requirejs-ractive this is whatever the Ractive module ID resolves to, which is usually determined by the paths 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.

Rich-Harris avatar Mar 26 '14 16:03 Rich-Harris

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.

marcello3d avatar Mar 26 '14 16:03 marcello3d

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.

dkleehammer avatar May 09 '14 18:05 dkleehammer

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!

marcello3d avatar May 09 '14 21:05 marcello3d

Oh ok, i was still trying to use the runtime. I'll give it a shot with just requiring ractive. Thanks!

dkleehammer avatar May 10 '14 04:05 dkleehammer