velocity icon indicating copy to clipboard operation
velocity copied to clipboard

Can't require velocity within node.js due to document.createElement calls

Open KyleAMathews opened this issue 9 years ago • 15 comments

I'm building an isomorphic site using React.js and Velocity for some animations. I'm running into a problem however when rendering pages on the server, pages that require Velocity throw errors on the server due to document not being defined.

KyleAMathews avatar Mar 21 '15 21:03 KyleAMathews

why should you require Velocity on the server?

ydaniv avatar Mar 22 '15 08:03 ydaniv

In order to render my React pages on the server.

KyleAMathews avatar Mar 22 '15 15:03 KyleAMathews

Velocity is used for animation, so I'm confused here. How do you use it for rendering?

ydaniv avatar Mar 22 '15 15:03 ydaniv

It isn't used for rendering on the server but as I'm using commonjs modules, it still has to be required server-side so that Webpack can bundle it together for sending to the client.

KyleAMathews avatar Mar 23 '15 19:03 KyleAMathews

@KyleAMathews have you tried simply patching this module and replace all requires with a mock?

ydaniv avatar Mar 24 '15 07:03 ydaniv

The solution I've had to use repeatedly in other open-source projects used isomorphically is adding an early return in a conditional that checks for window or document or some such. Honestly, this is going to get worse and worse as more people attempt isomorphic apps, so it's probably a good thing to keep in mind.

It would be great if I didn't have to make my own fork of this that is node-compatible. I can't conditionally import modules in my codebase so everything the client uses, the server must also at least be able to require.

Polyrhythm avatar May 07 '15 23:05 Polyrhythm

@KyleAMathews

It isn't used for rendering on the server but as I'm using commonjs modules, it still has to be required server-side so that Webpack can bundle it together for sending to the client.

I'm using React in a similar way (but with browserify). But I just move call require calls for client-only stuff into the actual client-only methods that use them.

Alternatively require('is-browser') would work.

There's a plethora of client-only js libraries that probably will never fix themselves to mock load server-side. And the idea of loading a client-only module server-side that you don't actually use sounds terrible from what I think should be a best practice. So honestly I think the solution is going to have to be something generic, probably related to the loaders. Instead of going around telling libraries they should make their library a no-op when window/document aren't available.

dantman avatar May 16 '15 02:05 dantman

Webpack doesn't support conditional requires like browserify does afaik.

Agree that a general workaround would be nice.

KyleAMathews avatar May 16 '15 03:05 KyleAMathews

@KyleAMathews (asking again) have you tried patching the require() of Velocity, e.g. by using proxyquire?

ydaniv avatar May 16 '15 21:05 ydaniv

Just to toss in an additional usage case and show how it might be integrated in a React application: https://gist.github.com/tkafka/0d94c6ec94297bb67091

Something like that should be able to be rendered on the server (the functions componentWillEnter and componentWillLeave that trigger the animation are only called from the client side).

andrewburgess avatar Jun 26 '15 18:06 andrewburgess

@KyleAMathews

I have a similar use-case (using velocity-animate in an isomorphic app). You can use conditional require in webpack, ~~but you have to create two separate webpack bundles:~~, you just need to mock the library

Here's what I've done:

The javascript

var Velocity;
if (typeof window !== 'undefined') {
  Velocity = require('velocity-animate');
} else {
  // mocked velocity library
  Velocity = function() {
    return Promise().resolve(true);
  };
}

// code that uses the library below
...

sogko avatar Jun 28 '15 09:06 sogko

#578 I created seems to be related to this issue too. Can't do dojo builds without code modifications, because dojo builds are created on the server through nodejs or rhino.

psoares avatar Sep 04 '15 16:09 psoares

From a quick look:

Most of the createElement calls are relating to IE8 support. There is one used to help determine browser prefixes. If the prefix detection gets pulled into a separate module and does feature detection then that should clean up things nicely.

Rycochet avatar Sep 20 '16 08:09 Rycochet

For those interested, I was able to get this working by using the (modified) shim (similar to the above solution) from velocity-react and using patched versions of velocity.js and velocity-ui.js.

alampros avatar Oct 26 '16 20:10 alampros

Something I did that worked well was use webpack's code splitting inside of React's componentDidMount lifecycle method. componentDidMount does not execute server side.

  componentDidMount() {
    import('velocity-animate').then(Velocity => {
      this.velocityScroll = () => {
        Velocity(this.selection, 'scroll', {
          duration: 1000,
          easing: 'easeOutExpo',
        });
      };
    });
  }

https://webpack.js.org/guides/code-splitting-async/

pizzarob avatar May 22 '17 04:05 pizzarob