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

Screwey "node" support in various files #95

Closed gilbox closed 9 years ago

gilbox commented 9 years ago

I'm trying to require TimelineLite and CSSPlugin in a webpack-based project. Here's the pattern that's causing me headaches:

    if (typeof(define) === "function" && define.amd) { //AMD
        define(["TweenLite"], getGlobal);
    } else if (typeof(module) !== "undefined" && module.exports) { //node
        require("./TweenLite.js"); //dependency
        module.exports = getGlobal();
    }

I haven't tried this in nodejs, but in webpack this doesn't work. Is there any notion of supporting applications that want to require individual files from the gsap dir? Here's what I'm doing:

require('gsap/src/uncompressed/TimelineLite');

begets the following error:

ERROR in ./~/gsap/src/uncompressed/TimelineLite.js
Module not found: Error: Cannot resolve module 'TweenLite' in /xxxxxxx/node_modules/gsap/src/uncompressed
 @ ./~/gsap/src/uncompressed/TimelineLite.js 744:2-34

I would think that the TimelineLite.js code should actually be:

    if (typeof(define) === "function" && define.amd) { //AMD
        define(["TweenLite"], getGlobal);
    } else if (typeof(module) !== "undefined" && module.exports) { //node
        require("gsap/src/uncompressed/TweenLite"); //dependency
        module.exports = getGlobal();
    }

Note that I could also make this work by aliasing ./TweenLite i think but it seems silly to require users to do that.

jackdoyle commented 9 years ago

Disclaimer: I have zero experience with webpack and I'm not a node/require expert either

From my understanding wouldn't your suggestion not really change anything except that it'd make everyone put the files in the exact directory structure of /gsap/src/uncompressed/ whereas the existing code simply tells it to look in the current directory (whatever that happens to be)? The goal was to allow flexibility for people to name their directories as they please (for the most part at least). The existing code was working fine in Browserify. Did you try your suggested edit and verify that it works?

If anyone else has ideas/suggestions here, I'm all ears.

gilbox commented 9 years ago

Sorry I should have actually tried it before posting this issue. The way you're using require is fine.

wouldn't your suggestion not really change anything except that it'd make everyone put the files in the exact directory structure of /gsap/src/uncompressed/

No, because browserify, nodejs, and webpack will look in the node_modules and find gsap, but it doesn't really matter because require('./TweenLite.js') is not the problem.

The problem is that I misunderstood how webpack works with define, and that when I posted this issue I misread the line number of the error. It's actually balking at the define call, not the require call because webpack supports both requirejs and commonjs syntax.

Anyway, this is still a problem for people using webpack but I don't have any suggestion to solve this right now so I'll close this issue.

gilbox commented 9 years ago

Since I was stirring up trouble, I may as well post the solution. Originally I thought that defining an alias in the webpack config was silly. However, since this problem really is specific to webpack (and not a general commonjs issue as I had thought) it's not really that silly...

// in your project:
require('gsap/src/uncompressed/TimelineLite');

in webpack.config.js:

  resolve: {
    alias: {
      'TweenLite': 'gsap/src/uncompressed/TweenLite'
    }
  }

Thanks for looking at this issue.

jackdoyle commented 9 years ago

That's great, thanks for sharing the solution here.

jeaber commented 9 years ago

thanks

vjpr commented 9 years ago

Thanks @gilbox - solved my problem too.

puredazzle commented 9 years ago

:+1:

ElliotChong commented 9 years ago

Thanks for the solution @gilbox! It'd be nice if GSAP's package on NPM worked a bit more like a standard Node-compatible library.

Ex:

var TweenMax = require("gsap/tween-max");
{ TweenMax } = require("gsap");

Plus treating TweenMax as that required object, rather than a global.

danehansen commented 9 years ago

@gilbox @jackdoyle I'm having trouble with this right now, and the posted solution is not working for me. Trying to get TweenLite, TimelineLite, AttrPlugin, and ScrollToPlugin all playing nicely in a webpack project. Any other breakthroughs on this lately? Logging out my TweenLite and ScrollToPlugin imports both come out as plain empty objects.

gilbox commented 9 years ago

@danehansen I have no idea since you have provided almost no information about errors you're getting.

NicholasBoll commented 8 years ago

@gilbox I ran into the same issue with Webpack. I followed the code path and found that webpack actually polyfills for AMD as well. Greensock is testing for AMD support first and goes down the define(["TweenLite"], getGlobal); path first. Browserify doesn't have this issue because it doesn't polyfill AMD.

It has been a long time since I've used requirejs, but it should support relative pathing. Updating the line to define(["../TweenLite"], getGlobal); works. It should still work for requirejs as well (r.js should understand relative pathing).

NicholasBoll commented 8 years ago

The AMD portion is a little wonky - it does not contain relative paths and not all dependencies. Webpack polyfills both CommonJS and AMD and Greensock checks AMD first. To get around this, I used the imports-loader and in my require statements I did this:

require('imports?define=>false!gsap/src/uncompressed/plugins/CSSPlugins');

This forces define to fail the AMD check and instead goes to CommonJS which seems to be implemented mostly correctly...

EasePack is a little strange and requires the following:

require('gsap/TweenLite');
require('gsap/easing/EasePack');

TweenLite defines _gsScope._gsDefine that is required by EasePack to actually define the global variables... And then easing like Elastic.easeOut are just defined on global... Nothing is returned...

You may want to create an easing export for files that need it Ex:

require('gsap/TweenLite');
require('gsap/easing/EasePack');

module.exports = {
  Elastic: window.Elastic,
  Cubic: window.Cubic,
  Quad: window.Quad,
  Back: window.Back,
  Linear: window.Linear
};
jackdoyle commented 8 years ago

I did add the global export(s) to EasePack in 1.18.2. Hopefully that helps a bit.

NicholasBoll commented 8 years ago

@jackdoyle that does help.

It took me some experiments and digging to figure out why some circumstances a simple require('gsap/easing/Easepack') worked and others didn't. Since the dependency wasn't explicit, it was possible for a file that required EasePack to be parsed in the output file before TweenLite was parsed thus silently failing this if check: if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); and just casing the global ease functions to not exist.