greensock / GSAP

GSAP (GreenSock Animation Platform), a JavaScript animation library for the modern web
https://gsap.com
19.83k stars 1.72k forks source link

gsap 1.20.5 server side rendering error #268

Closed hadimostafapour closed 6 years ago

hadimostafapour commented 6 years ago

after updating to version 1.20.5, I got this error on SSR.

webpack:///./node_modules/gsap/TweenLite.js?:42
                        _doc = window.document,
                                      ^

TypeError: Cannot read property 'document' of undefined
jackdoyle commented 6 years ago

Hm, would you mind sending me a reduced test case? I can't seem to reproduce that. What environment is this running in? Sounds browserless (?)

hadimostafapour commented 6 years ago

@jackdoyle yes, it's running in NodeJS environment.

As I said it was a server side rendering error, so there is no window object. Passing _doc = window.document as globals in UMD is the issue, that try to accessing window.document on construction, so whenever package got imported and window object isn't available it throws error.

jackdoyle commented 6 years ago

And are you saying that it worked in a previous version? Did you have a workaround in mind?

hadimostafapour commented 6 years ago

Ok, I just try to contribute a fix but I think repository codes are pre-built.

For fix server-side-rendering issues we need to control variables initialization on construction. In UMD an IIFE called when module got loaded and either module exports. We have to import file because of isomorphic, but methods should not be called in server-side-rendering. So its not matter there is no DOM or window object.

So to fix IIFE In "Ex. TweenLite.js"

"use strict";
window = window || {}; // if window object is not available || browserless
var _exports = {},
    _doc =  window.document...

Exports at very top of file ( _gsScope was undefined in browserless environment, while TweenMaxBase needs it to be an object.)

export const _gsScope = (typeof(window) !== "undefined") ? window : (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : (typeof this === 'object' ? this : {});

Exports at very bottom of file "Weird :(, _gsScope is undefined (not an object) in browserless environment "

const isGsScopeAvailable = typeof _gsScope === 'object';

const gs = isGsScopeAvailable ? _gsScope.com.greensock : false;
export {TweenLite as default};
export const SimpleTimeline = isGsScopeAvailable ? gs.core.SimpleTimeline : false;
export const Animation = isGsScopeAvailable ? gs.core.Animation : false;
export const {Ease, Linear, Power0, Power1, Power2, Power3, Power4, TweenPlugin} = isGsScopeAvailable ? _gsScope : {};
export const EventDispatcher = isGsScopeAvailable ? gs.events.EventDispatcher : false;
jackdoyle commented 6 years ago

So you're saying that window, global, and "this" are all undefined in your environment? I'm not sure GSAP should be expected to work there, but might it be enough to just add " || {}" at the very end of the current _gsScope declaration?

It would be super helpful if you could provide a reduced test case that'd allow me to see the error being thrown.

jonatasnardi commented 6 years ago

I'm getting same error, but I couldn't fix it. I'm building an Angular Universal server with NodeJS and webpack, running this command: npm run build:client-and-server-bundles && npm run webpack:server

This is the error message: **_doc = window.document, ^

TypeError: Cannot read property 'document' of undefined**

jackdoyle commented 6 years ago

@jonatasnardi any chance you (or anyone) could provide a reduced test case so that I could reproduce the error?

And you want to use GSAP in a completely browserless environment, right? For animating raw data?

hadimostafapour commented 6 years ago

@jackdoyle

And you want to use GSAP in a completely browserless environment, right? For animating raw data?

I explain the issue already, we don't animate anything in server side rendering, but we have to import the package, because the same file will be used in client side.

For example:

This source should be executed in both server side and client side.

import {TweenLite} from 'gsap';

export default class Test {

   ifBrowser(){
     TweenLite//... Perform animate
   }
    render() {
      return <div><span className="performAnimateOnBrowser">Animate</span></div>;
   }
}

In above example, when execute in NodeJs environment, simply return a RAW HTML as response to http://foo.bar/animate url and ifBrowser method will never get called.

And when our js file execute in browser environment, firstly append HTML code from render method to document and then execute ifBrowser method to perform animations.

As you know there is no way to say hey don't import gsap package on NodeJS because of we using build systems like webpack to generate an universal bundle for both browser and nodeJs.

GSAP v2, try to set some variables like _doc = window.document when got imported (or loaded in browsers), this made issues in server side rendering.

For example if we have a file like this:

TweenLite.js

_doc = window.document;
export default class TweenLite {

   animate(){
      _doc.querySelector('...') // ...
   }
}

App.js

import TweenLite from 'TweenLite.js';

if(`browser`) {
  const animate = new TweenLite;
  animate.doSomething();
}

Surly we got error on server side rendering because there is no window or window.document.

So in order to fix this problem I will rewrite my TweenLite.js as blew: TweenLite.js

class TweenLite {

   animate() {
     const _doc = window.document;
      _doc.querySelector('...') // ...
  }
}

and now we can use it safely in NodeJs because window.document only needed when calling animate method, not on package import.

hope this be helpful.

jackdoyle commented 6 years ago

Thanks, @hadimostafapour. I asked earlier...

"might it be enough to just add " || {}" at the very end of the current _gsScope declaration?" (at the top of TweenLite.js)

...but nobody answered that yet. Seems like it would likely resolve things here but in the absence of a reduced test case (which, again, nobody has provided), I'm not 100% positive.

vasturiano commented 6 years ago

@jackdoyle I'm having a similar issue in bundling a module that depends on TweenLite (built using Rollup), because of https://github.com/rollup/rollup/wiki/Troubleshooting#this-is-undefined

The error that occurs is:

(!) this has been rewritten to undefined https://github.com/rollup/rollup/wiki/Troubleshooting#this-is-undefined node_modules/gsap/esm/TweenLite.js 19: - remove the "export to multiple environments" in Definition(). 20: */ 21: export const _gsScope = (typeof(window) !== "undefined") ? window : (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this;

It leads to _gsScope being undefined. It seems that your suggestion above would fix the issue.

jackdoyle commented 6 years ago

This should be resolved in 2.0.1 (just published). Please let me know if you run into any other issues.