How To Use ScrollMagic with GSAP and Webpack - javascript

In order to use ScrollMagic with GSAP, you need to load the animation.gsap.js plugin. With Webpack you would do something like this to accomplish that (assuming you use the CommonJS syntax and installed everything with npm):
var TweenMax = require('gsap');
var ScrollMagic = require('scrollmagic');
require('ScrollMagicGSAP');
To make sure that this actually works, you have to add an alias to your Webpack configuration, so that Webpack knows where the plugin lives.
resolve: {
alias: {
'ScrollMagicGSAP': 'scrollmagic/scrollmagic/uncompressed/plugins/animation.gsap'
}
}
Unfortunately, ScrollMagic keeps throwing an error, when you are using this configuration and the CommonJS syntax like above.
(ScrollMagic.Scene) -> ERROR calling setTween() due to missing Plugin 'animation.gsap'. Please make sure to include plugins/animation.gsap.js

The Solution
You have to tell Webpack to stop using the AMD syntax by adding the following loader that deactivates the define() method.
// Webpack 4+
module: {
rules: [
{ parser: { amd: false }}
]
}
// Webpack <= 3
// Don’t forget to install the loader with `npm install imports-loader --save-dev`
module: {
loaders: [
{ test: /\.js$/, loader: 'imports-loader?define=>false'}
// Use this instead, if you’re running Webpack v1
// { test: /\.js$/, loader: 'imports?define=>false'}
]
}
Why?
The problem lies in the fact that Webpack supports the AMD (define) and CommonJS (require) syntax. That is why the following factory script within plugins/animation.gsap.js jumps into the first if statement and fails silently. That is why setTween() etc. are never added to the ScrollMagic Constructor.
By telling Webpack not to support the AMD syntax (using the loader mentioned above), the plugin jumps into the second if statement correctly, embracing the CommonJS syntax.
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['ScrollMagic', 'TweenMax', 'TimelineMax'], factory);
} else if (typeof exports === 'object') {
// CommonJS
// Loads whole gsap package onto global scope.
require('gsap');
factory(require('scrollmagic'), TweenMax, TimelineMax);
} else {
// Browser globals
factory(root.ScrollMagic || (root.jQuery && root.jQuery.ScrollMagic), root.TweenMax || root.TweenLite, root.TimelineMax || root.TimelineLite);
}
I hope this prevents other people from spending a whole evening trying to figure out what is going on.

The solution I came across that doesn't require you to alter your webpack.config.js file and actually works for me can be found here: https://github.com/janpaepke/ScrollMagic/issues/665
The gist of it is to make sure you have ScrollMagic and GSAP added via npm (hopefully that's obvious) as well as imports-loader:
npm install --save scrollmagic gsap
npm install --save-dev imports-loader
Then in the file you want to use ScrollMagic with GSAP do the following imports:
import { TimelineMax, TweenMax, Linear } from 'gsap';
import ScrollMagic from 'scrollmagic';
import 'imports-loader?define=>false!scrollmagic/scrollmagic/uncompressed/plugins/animation.gsap';
Using Webpack 4.x and imports-loader 0.8.0

medoingthings solution has since changed syntax to include "-loader" suffix.
module: {
loaders: [
{ test: /\.js$/, loader: 'imports-loader?define=>false'}
]
}
https://webpack.js.org/guides/migrating/#automatic-loader-module-name-extension-removed

In imports-loader 1.1.0, the syntax of the configuration has changed a bit, so now you have to use the following to get the ScrollMagic plugins to work:
{
test: [
path.join(config.root, '/node_modules/scrollmagic/scrollmagic/uncompressed/plugins/jquery.ScrollMagic.js'),
path.join(config.root, '/node_modules/scrollmagic/scrollmagic/uncompressed/plugins/debug.addIndicators.js')
],
use: [
{
loader: 'imports-loader',
options: {
additionalCode: 'var define = false;'
}
}
]
}
Hopefully this helps others.

I was having the same issue and found this question.
For those using Webpack 5 I believe imports-loader is out of date so according to the webpack docs add this code to your js rule to disable AMD:
{
test: /\.js$/,
include: /node_modules/,
parser: {
amd: false
}
}
documentation: https://webpack.js.org/configuration/module/#ruleparser

Related

How to call imported jquery functions in dev tools console? [duplicate]

I want to expose the jQuery object to the global window object that is accessible inside the developer console in the browser. Now in my webpack config I have following lines:
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
]
These lines add the jQuery definitions to each file in my webpack modules.
But when I build the project and try to access jQuery in the developer console like this:
window.$;
window.jQuery;
it says that these properties are undefined...
Is there a way to fix this?
You need to use the expose-loader.
npm install expose-loader --save-dev
You can either do this when you require it:
require("expose?$!jquery");
or you can do this in your config:
loaders: [
{ test: require.resolve('jquery'), loader: 'expose?jQuery!expose?$' }
]
UPDATE: As of webpack 2, you need to use expose-loader instead of expose:
module: {
rules: [{
test: require.resolve('jquery'),
use: [{
loader: 'expose-loader',
options: '$'
}]
}]
}
The ProvidePlugin replaces a symbol in another source through the respective import, but does not expose the symbol on the global namespace.
A classic example are jQuery plugins. Most of them just expect jQuery to be defined globally. With the ProvidePlugin you would make sure that jQuery is a dependency (e.g. loaded before) and the occurence of jQuery in their code would be replaced with the webpack raw equivalent of require('jquery').
If you have external scripts relying on the symbol to be in the global namespace (like let's say an externally hosted JS, Javascript calls in Selenium or simply accessing the symbol in the browser's console) you want to use the expose-loader instead.
In short: ProvidePlugin manages build-time dependencies to global symbols whereas the expose-loader manages runtime dependencies to global symbols.
Looks like the window object is exposed in all modules.
Why not just import/require JQuery and put:
window.$ = window.JQuery = JQuery;
You will need to ensure that this happens before requiring/importing any module that makes use of window.JQuery, either in a requiring module or in the module where it's being used.
This always worked for me. including for webpack 3 window.$ = window.jQuery = require("jquery");
None of the above worked for me. (and I really don't like the expose-loader syntax). Instead,
I added to webpack.config.js:
var webpack = require('webpack');
module.exports = {
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
})
]
}
Than all modules have access through jQuery through $.
You can expose it to the window by adding the following to any of your modules bundled by webpack:
window.$ = window.jQuery = $
Update for Webpack v2
Install expose-loader as described by Matt Derrick:
npm install expose-loader --save-dev
Then insert the following snippet in your webpack.config.js:
module.exports = {
entry: {
// ...
},
output: {
// ...
},
module: {
loaders: [
{ test: require.resolve("jquery"), loader: "expose-loader?$!expose-loader?jQuery" }
]
}
};
(from the expose-loader docs)
In my case works
{ test: require.resolve("jquery"), loader: "expose?$!expose?jQuery" }
Update for Webpack v2
After webpack 5 upgrade, you could face this warning.
[DEP_WEBPACK_RULE_LOADER_OPTIONS_STRING] DeprecationWarning: Using a string as loader options is deprecated (ruleSet[1].rules[7].use[0].options)
Simply change the options to
options: {
exposes: ["$", "jQuery"],
}
will look like this:
module: {
rules: [{
test: require.resolve('jquery'),
use: [{
loader: 'expose-loader',
{
exposes: ["$", "jQuery"],
}
}]
}]
}

How to import dynamically with webpack

I want to use FontAwesome's icons, but the whole package is too large and I have to select only the ones that I'm using
I'm using vue & webpack
Right now I have the standard:
import { library } from '#fortawesome/fontawesome-svg-core'
import { faGem as falFaGem, faDatabase as falFaDatabase } from '#fortawesome/pro-light-svg-icons'
import { faGem as farFaGem } from '#fortawesome/pro-regular-svg-icons'
import { faGem as fasFaGem } from '#fortawesome/pro-solid-svg-icons'
library.add(falFaGem, falFaDatabase, farFaGem, fasFaGem)
The thing is I have around 80 (for now) icons and some of them duplicates like faGem in the example, hence the "faGem as farFaGem"
I tried importing FAS from '#fortawesome/pro-regular-svg-icons' and making a foreach and adding to library only the icons that I need but webpack imports the whole package into the compiled code
Is there an easier, cleaner way to achieve this?
I believe that the title to your question is a bit wrong. What you want to achieve is reduce the size of the FontAwesomes npm package and that is something that can be achieved in different ways.
The most common way nowadays is using Treeshaking. Basically, your code will be '''analysed''' and a graph of dependencies will be generated, before giving you the "compiled" version of your code it will remove all those modules that were not used from FontAwesome.
FontAwesome can perform TreeShaking if your tool (webpack) allows it, sadly it seems that there are some problems with Webpack 4 but they offer some work arounds like setting the variable modules false in your config:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: './bundle.js'
},
module: {
rules: [{
test: /\.js$/,
include: path.resolve(__dirname, 'src'),
use: {
loader: 'babel-loader',
options: {
presets: [
['#babel/preset-env', { modules: false }],
],
plugins: ['#babel/plugin-proposal-class-properties'],
}
}
},
]
}
};
In the other hand I believe that what you want to achieve is quite difficult (you will still need to declare all the icons that you want to use from FontAwesome (to use a require(...) call dynamically is something that the tree shaking algorithm can not work around and it will import the whole npm package), and the same for the name conflicts.
If you want it to be a bit cleaner, maybe declare and add all this icons in a separated file of your project, but as far as I can tell, there is no a better way to achieve what you want.

How to use webpack loaders in nuxt.js?

I am trying to use https://github.com/itgalaxy/webpack-modernizr-loader this loader in my nuxt/webpack project.
Unfortunately loaders are added differently than with a normal webpack project and I don't really understand nuxt's documentation on this issue:
https://nuxtjs.org/api/configuration-build#loaders
I have a sample repo with a minimal nuxt project:
https://github.com/Jones-S/nuxt-modernizr
In my nuxt.config.js (equivalent to webpack config)
I tried to include the loader and use in my pages/index.vue.
Unfortunately I am getting an error that I have modernizr not installed.
This can't be the case though.
I would be really happy if somebody could have short look into this and tell me how I correctly use loaders in nuxt.js.
For brevity my part of the nuxt.config.js
build: {
/*
** You can extend webpack config here
*/
extend(config, { isDev, isClient }) {
// Run ESLint on save
if (isDev && isClient) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
})
config.module.rules.push({
test: /\.modernizrrc\.js$/,
use: { loader: 'webpack-modernizr-loader' }
})
config.resolve.alias['modernizr'] = '/.modernizrrc.js'
}
}
}
I am pushing the new loader and register an alias below. But I would get this error:
ERROR Failed to compile with 1 errors
This dependency was not found:
* modernizr in ./node_modules/babel-loader/lib??ref--2-0!./node_modules/vue-loader/lib??vue-loader-options!./pages/index.vue?vue&type=script&lang=js&
To install it, you can run: npm install --save modernizr
thank you very much in advance.
cheers

Webpack fast import rule

I need to use webpack with snapsvg. According to snapsvg's documentation, to import it with webpack, I need to use a loader:
const Snap = require(`imports-loader?this=>window,fix=>module.exports=0!snapsvg/dist/snap.svg.js`);
So, in every Javascript file I use, I need to write this thing. I would like to avoid it.
Aliased import
Is there a way, maybe in webpack.config.js, to specify that every time this is encoutered:
import snap from "snapsvg"
Then that line is translated into the one mentioned before? So I can avoid typing the same thing in every file.
You can use this rule:
Install Imports Loader (npm i -D imports-loader) and add this to your webpack config:
module: {
rules: [
{
test: require.resolve('snapsvg/dist/snap.svg.js'),
use: 'imports-loader?this=>window,fix=>module.exports=0',
},
],
},
resolve: {
alias: {
snapsvg: 'snapsvg/dist/snap.svg.js',
},
},
With that you can import snap from "snapsvg" without hassle.

Babel 6.0.20 Modules feature not work in IE8

I am trying to export a es6 module in header.js:
export default {
setHeaderHighlight: function (index) {
// do somethings
}
};
And import it in index.js:
import header from "./header.js"
$(function () {
header.setHeaderHighlight(0);
});
Then transformation comes out in index.bundle.js:
var _header = __webpack_require__(129);
var _header2 = _interopRequireDefault(_header);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj }; // crash here
}
So here is the problem, ie8 will rise a Expected identifier Exception at { default: obj }, but everythings is ok >=ie9.
Is there something i can do with this?
By default, Babel 6.x requires you to enable an explicit set of transformations. The standard es2015 preset converts ES6 to ES5, however IE8 is not ES5-compatible. In this case, if you look at the plugins list, you will see
transform-es3-member-expression-literals
transform-es3-property-literals
These will convert your properties to be compatible with IE8. Generally in Babel 6.x you'd do this by passing those names as part of your plugins array, alongside the presets array and install the transforms via
npm install --save-dev babel-plugin-transform-es3-member-expression-literals babel-plugin-transform-es3-property-literals
I use webpack + es3ify-loader as workaround.
loaders: {
{
test: /\.jsx?$/,
exclude: /node_modules/,
loaders: ['es3ify', `babel?${JSON.stringify(babelQuery)}`],
},
}
I also have the problem, and I wrote a webpack plugin to resolve it. I didn't really know if there is a nicer way to handle it, but it works.
The module in node_modules also works well.

Categories

Resources