foundation / foundation-sites

The most advanced responsive front-end framework in the world. Quickly create prototypes and production code for sites that work on any kind of device.
https://get.foundation
MIT License
29.66k stars 5.48k forks source link

Problems while importing foundation js in webpack. #7386

Closed medihack closed 7 years ago

medihack commented 8 years ago

I would like to import some foundation js files in a webpack app.

In my entry js file I do the following:

import 'foundation-sites/js/foundation.core.js'; // this works
import 'foundation-sites/js/foundation.tabs.js'; // this works
import 'foundation-sites/js/foundation.reveal.js'; // this fails

The last one fails with the following error:

ERROR in ./~/foundation-sites/js/foundation.reveal.js
Module not found: Error: Cannot resolve module 'foundation' in /home/kai/Projects/myapp/node_modules/foundation-sites/js
 @ ./~/foundation-sites/js/foundation.reveal.js 474:4-476:6

So it seems there is a problem with all files that contain the code define(['foundation'] ... as foundation is not defined anywhere.

I installed foundation-sites (v6.0.5) using NPM.

How do I import those files that have the define(['foundation'] correctly?

kirsebaer commented 8 years ago

The same obviously happens when adding foundation.min.js or foundation.js. Have you found a workaround?

ERROR in ./~/foundation-sites/dist/foundation.js
Module not found: Error: Cannot resolve module 'foundation' in
/home/kirsebaer/testproject/node_modules/foundation-sites/dist
@ ./~/foundation-sites/dist/foundation.js 2045:4-2047:6 3747:4-3749:6 3932:4-3934:6 4145:4-4147:6 5619:4-5621:6 7027:4-7029:6
Nerdinacan commented 8 years ago

+1

You guys have done so much nice work in allowing us to modularly import SCSS, it's a tragedy that I can't use this library because your javascript is essentially un-importable as a CommonJS module. This is going to severely limit its adoption in both webpack and browserify build environments.

mhamann commented 8 years ago

+1

asolberg commented 8 years ago

+1

name-k commented 8 years ago

+1 For now this is the biggest problem for me and the reason why I can't completely switch to foundation. Can't imagine why JS modularity is so bad, while SASS modularity is so good... Would love to see ES2015 modules support, but any other webpack compatible modules support will do the job.

MTyson commented 8 years ago

Seeing the same problem. Having to use Bower to work-around it so far.

Added a StackOverflow question: http://stackoverflow.com/questions/34297788/npm-zurb-foundation-webpack-cannot-resolve-module-foundation

Nerdinacan commented 8 years ago

@MTyson I posted my temporary workaround on your StackOverflow entry. Enjoy

kty commented 8 years ago

+1 @Nerdinacan Thanks, you put me on the right track! I've posted my workaround on the StackOverflow question adding some steps and explanation, hope you don't mind.

ribbedcrown commented 8 years ago

+1

CoderRoman commented 8 years ago

+1

ghost commented 8 years ago

My solution using a single js file (no need a app.js and vendor.js): http://stackoverflow.com/a/34611081/4794469

zs-sz commented 8 years ago

+1 here... it took me a while to find this post. i took on one of the workarounds posted above. Anyone have any ideas what is the problem with the current JS base? As I can see the module definitions are there, but webpack seems to have a problem with the 'foundation' dependency.

(p.s. I'm a babel/webpack/commonjs noob so be nice if I'm talking silly here)

MikePugs commented 8 years ago

+1

tomByrer commented 8 years ago

I've noticed on the SCSS side switching from v5.x to v6.x there were compiling issues; the root file (foundation.scss) didn't automatically pull dependencies. Did webpack work with v5?

MTyson commented 8 years ago

Actually, yeah, I rolled back to v5 and things are working. I had to expose jQuery and $ as globals, but that was the only kludge. I just did:

$ = jQuery = require('jquery');

At the head of my entry file.

Jhony0311 commented 8 years ago

+1

tomByrer commented 8 years ago

Solution: https://github.com/rollup/rollup Reasoning: http://pouchdb.com/2016/01/13/pouchdb-5.2.0-a-better-build-system-with-rollup.html

burxtx commented 8 years ago

+1

zs-sz commented 8 years ago

@tomByrer

that's an other story. you can just delete the mixin around the import and you`re good to go... or just use the mixin in your style.scss

mjsarfatti commented 8 years ago

UPDATED: feb 29 2016

A simpler solution (no vendor.js, no externals, simply an alias). 1 - Use this in your webpack config:

// this will force the export of the jQuery 'foundation' function, which we'll use later
loaders: [
  {
    test: /(foundation\.core)/,
    loader: 'exports?foundation=jQuery.fn.foundation'
  },
],
// this makes sure that every module can resolve define(['foundation']) calls
resolve: {
  extensions: ['', '.js'],
  alias: {
    foundation: 'foundation-sites/js/foundation.core'
  }
},
// this makes sure 'jQuery' is available to any jQuery plugin you might want to load 
// (including Foundation files) regardless of how they are written
plugins: [
  new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    'window.jQuery': 'jquery'
  })
]

2 - In your index.js:

// thanks to the ProvidePlugin we don't need to
// > import $ from 'jquery';

// import core foundation files
import { foundation } from 'foundation-sites/js/foundation.core';
import 'foundation-sites/js/foundation.util.mediaQuery';

/* import here any additional module */

// we need to attach the function we exported above to the jQuery object in use in this file
$.fn.foundation = foundation;

// ready to go
$(document).ready(function() {
  $(document).foundation();
  …
});
keimlink commented 8 years ago

Thanks for the nice solution @mjsarfatti! :+1:

Got it working after installing babel-loader and exports-loader. I also had to quote the $ key in the ProvidePlugin options object to get it working.

But a single module is still complaining about jQuery missing:

import 'foundation-sites/js/foundation.util.triggers'

The bundle contents look like webpack does not inject jQuery here. Do you know how to solve this problem?

timaschew commented 8 years ago

Hey guys, it's much easier than you think ;) Just npm i script-loader and prefix the import with a script! everywhere where you need to load the script in the global scope. No need to configure externals, aliases or ProvidePlugins in webpack.

If you want to load everything from foundation it would look like this:

import 'script!jquery'
import 'script!what-input'
import 'script!foundation-sites'

You can checkout my boilerplate project https://github.com/timaschew/r3-foundation-boilerplate

@tomByrer

I've noticed on the SCSS side switching from v5.x to v6.x there were compiling issues; the root file

Didn't see any errors. It's working fine with v6 in my boilerplate project.

mjsarfatti commented 8 years ago

@keimlink can you try modifying the loaders > test as such: test: /foundation\.(core|utils)/ (or even simply test: /foundation/)

@timaschew great, even better! My solution can actually cause problems when you attach foundation plugins to elements via data- attributes instead of with pure javascript, yours could solve that.

agaurav commented 8 years ago

where and how does one declare $(document).foundation(); ? inside an angular2 app

mjsarfatti commented 8 years ago

I guess you would still declare it inside the $(document).ready(function() { ... }) call

anselmdk commented 8 years ago

I'm going with @timaschew's solution for now, seems to be working.

timaschew commented 8 years ago

@medihack so is this solved for you, can this issue be closed?

keimlink commented 8 years ago

@timaschew This thread is just a collection of different workarounds/hacks to circumvent the initial problem. IMO @Nerdinacan described it in the best way:

it's a tragedy that I can't use this library because your javascript is essentially un-importable as a CommonJS module

So my expectation is that the zurb people will come up with a fix or explain what should be fixed so that someone else can do it.

keimlink commented 8 years ago

@mjsarfatti Thanks for the update! Will try it ASAP and post feedback here!

timaschew commented 8 years ago

This thread is just a collection of different workarounds/hacks

@keimlink It's a decision of the authors which module types they want to support and maintain.

Of course they could provide to bundle foundation with the UMD format. But this doesn't make sense in combination with modular components (tabs, accordion, magellan), because every file would be exist additionally in the generated UMD format.

It would only make sense for the file (in the dist directory) which contains all the components. But in that case there is no support for picking/selecting particular components but this is what CommonJS should be, so this case doesn't make sense as well.

If there is a library, which is only available in one module type, in this case: using the global window scope - doesn't fit in your build pipeline, you need to use an adapter.

Every good bundler provides adapters: browserify and webpack and for webpack it's the script-loader it does exactly this job.

This thread is just a collection of different workarounds/hacks

So these solutions of browserify and webpack are no hacks, they are adapters.

mjsarfatti commented 8 years ago

@timaschew I disagree, it's a pity that in 2016 such a big project still hasn't adopted UMD. Every module should require the core and the core should require jquery. I'm not saying it's easy, it's very possible that adopting UMD would require a big rewrite and for this reason they haven't done it yet. But it has to be done or someone else will come forward (see lodash - which I can import function by function in my project - vs the monolithic underscore)

mica16 commented 8 years ago

@mjsarfatti In your solution, why do you need to do : import $ from 'jquery'; in index.js if you already declared jquery in : new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' })

? Can't you remove this import line?

mjsarfatti commented 8 years ago

Good catch, in fact you most probably don't in this case.

I import it by default in all my projects (I don't always need the ProvidePlugin) and it became an automation I guess :) I haven't tested it but it should definitely work without that import.

mjsarfatti commented 8 years ago

@timaschew I finally tested your solution, and while it looked great I could not find a way to make it work when you import jQuery plugins written using the UMD alongside others written in the "legacy" way (in my specific case scrollToFixed and selectize).

At least when you define the script-loader in the webpack config: there might be a way if you specify which loader to use inline case by case, but I wanted a universal solution. The error I got was a "require is not defined", very weird.

@keimlink please disregard my previous reply :) I revised my code above, you just need to add 'window.jQuery': 'jquery' to the ProvidePlugin

@mica16 you are most definitely right, I commented out that line and obviously it works.

keimlink commented 8 years ago

Thanks for the update @mjsarfatti ! I will test it as soon as I have the time!

keimlink commented 8 years ago

I haven't tried it so far, but it seems release 6.2 has converted everything to ES2015 which should also solve the import problem.

fchengpc commented 8 years ago

I'm very new to webpack and foundation. im trying to get foundation 6.2 work with webpack and npm. Can someone help me out here. in my webpack.config.js

 module: {
        loaders: [{
            test: /(foundation\.core)/,
            loader: 'exports?foundation=jQuery.fn.foundation'
        }],
        resolve: {
            extensions: ['', '.js'],
            modulesDirectories: ['node_modules'],
            alias: {
                foundation: 'foundation-sites/js/foundation.core'
            }
        },
    },
    plugins: debug ? [
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery',
            'window.jQuery': 'jquery'
        })
    ] : 

then in my index.js my webpack entry file

require('!!script!jquery/dist/jquery.min.js');
require('!!script!foundation-sites/js/foundation.core.js');
require('!!script!foundation-sites/js/foundation.accordion.js');
require('!!script!foundation-sites/js/foundation.util.keyboard.js');
require('!!script!foundation-sites/js/foundation.util.motion.js');

$.fn.foundation = foundation;

// ready to go
$(document).ready(function() {
    $(document).foundation();
});

but im getting index.js: Uncaught ReferenceError: foundation is not defined what im doing wrong here??

anselmdk commented 8 years ago

@fchengpc this is what I'm doing, based on what's been described in this thread (I'm on 6.1.x though, not sure if it makes a difference):

import 'script!jquery'
import 'script!foundation-sites'

$(document).ready(function ($) {
  $(document).foundation();
});

I'm not doing anything specific in the webpack file.

priedthirdeye commented 8 years ago

Try

var foundation = require('!!script!foundation-sites/js/foundation.core.js');

or possibly adding foundation to your provide plugin

        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery',
            'window.jQuery': 'jquery',
            foundation: 'Foundation'    
        })
fchengpc commented 8 years ago

@anselmdk Thanks you. finally got it working now. You are right nothing special in webpack config.(i took most of stuff out). Here is what i have in entry file.

require('script!jquery');
foundation = require('foundation-sites/dist/foundation.min.js');

$(document).ready(function($) {
    $(document).foundation();
});

not sure why i need to require jquery again. since i do use (webpack.ProvidePlugin) in webpack config. But if i took it out it throw jQuery undefined error. (assume b/c foundation use $, JQuery and window.jQuery

@priedthirdeye I actually end up use both. first ProvidePlugin in webpack config file then define foundation variable in entry file. Hope this help out someone the future.

anselmdk commented 8 years ago

@fchengpc that's cool. It could be that you're getting away with calling Foundation directly, because you're using 6.2. I'll try that out when I upgrade.

conrman commented 8 years ago

I'm able to get Foundation 6 working with webpack if I use the scripts-loader via @timaschew's method. But I'm unable to specify the files I want explicitly. I have to include the entire JS library.

So, in short this works:

import 'script!jquery'
import 'script!what-input'
import 'script!foundation-sites';

but this doesn't:

import 'script!jquery'
import 'script!what-input'
import '!!script!foundation-sites/js/foundation.core.js';
import '!!script!foundation-sites/js/foundation.util.box.js';
import '!!script!foundation-sites/js/foundation.util.keyboard.js';
import '!!script!foundation-sites/js/foundation.util.mediaQuery.js';
import '!!script!foundation-sites/js/foundation.util.motion.js';
import '!!script!foundation-sites/js/foundation.util.timerAndImageLoader.js';
import '!!script!foundation-sites/js/foundation.util.touch.js';
import '!!script!foundation-sites/js/foundation.util.triggers.js';
import '!!script!foundation-sites/js/foundation.abide.js';
import '!!script!foundation-sites/js/foundation.accordion.js';
import '!!script!foundation-sites/js/foundation.accordionMenu.js';
import '!!script!foundation-sites/js/foundation.drilldown.js';
import '!!script!foundation-sites/js/foundation.dropdown.js';
import '!!script!foundation-sites/js/foundation.dropdownMenu.js';
import '!!script!foundation-sites/js/foundation.equalizer.js';
import '!!script!foundation-sites/js/foundation.interchange.js';
import '!!script!foundation-sites/js/foundation.magellan.js';
import '!!script!foundation-sites/js/foundation.offcanvas.js';
import '!!script!foundation-sites/js/foundation.orbit.js';
import '!!script!foundation-sites/js/foundation.responsiveMenu.js';
import '!!script!foundation-sites/js/foundation.responsiveToggle.js';
import '!!script!foundation-sites/js/foundation.reveal.js';
import '!!script!foundation-sites/js/foundation.slider.js';
import '!!script!foundation-sites/js/foundation.sticky.js';
import '!!script!foundation-sites/js/foundation.tabs.js';
import '!!script!foundation-sites/js/foundation.toggler.js';
import '!!script!foundation-sites/js/foundation.tooltip.js';

When including files specifically, the dropdownMenu doesn't work. It returns this error: foundation.core.js:189 TypeError: Cannot read property 'Feather' of undefined.

However, when importing individually there are other JS components that do work (orbit and interchange, for example).

timaschew commented 8 years ago

does it help if you put the dropdown import to the end or change the order somehow?

conrman commented 8 years ago

@timaschew no, changing the order doesn't have any effect and still returns the same error: foundation.core.js:189 TypeError: Cannot read property 'Feather' of undefined(…)

priedthirdeye commented 8 years ago

This is how I currently do it using Foundation 6.2.1

var foundation = require("babel!foundation-sites/js/foundation.core"); require('babel!what-input/what-input.js'); require("babel!foundation-sites/js/foundation.util.mediaquery.js"); require("babel!foundation-sites/js/foundation.util.nest.js"); require("babel!foundation-sites/js/foundation.util.box.js"); require("babel!foundation-sites/js/foundation.util.keyboard.js"); require("babel!foundation-sites/js/foundation.util.motion.js"); require("babel!foundation-sites/js/foundation.util.timerandimageloader.js"); require("babel!foundation-sites/js/foundation.util.touch.js"); require("babel!foundation-sites/js/foundation.util.triggers.js"); require('babel!foundation-sites/js/foundation.offcanvas.js'); require('babel!foundation-sites/js/foundation.dropdownMenu.js'); require('babel!foundation-sites/js/foundation.dropdown.js'); require('babel!foundation-sites/js/foundation.equalizer.js');

// Related dependencies From package.json

"devDependencies": { "babel": "^6.5.2", "babel-core": "^6.6.0", "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.6.0", "gulp-babel": "^6.1.2", [...]

I'm sure there's other ways to do it, but this is what works for me after many wasted hours trying to get it to work.

agualbbus commented 8 years ago

@priedthirdeye that is the best solution to be used with

new webpack.ProvidePlugin({
    $: "jquery",
    jQuery: "jquery",
    "window.jQuery": "jquery"
})
TheLarkInn commented 8 years ago

Thanks for all the support here! Could we get this documented somewhere in the repo. Then I'd anyone comes and submits an issue or question on our (webpack) repo, we can link them to a solution.

kball commented 8 years ago

@TheLarkInn @priedthirdeye @agualbbus We'd happily accept a PR to the docs adding this, perhaps on this page? (http://foundation.zurb.com/sites/docs/installation.html)

File found here: https://github.com/zurb/foundation-sites/blob/develop/docs/pages/installation.md

severen commented 8 years ago

The problem I ran into specifically was Uncaught TypeError: $(...).foundation is not a function, in other words, $.fn.foundation wasn't being set. I tried every solution here and across the internet, and couldn't get anything to work.

I managed to get it working (after some code diving) by essentially pulling out the jQuery plugin code from ./js/foundation.core.js#253 and doing this:

// Foundation Sites Initialisation
// FIXME: Currently breaks, see: https://github.com/zurb/foundation-sites/issues/7386.
import 'what-input';
import 'foundation-sites';

$.fn.foundation = function(method) {
  var type = typeof method,
      meta = $('meta.foundation-mq'),
      noJS = $('.no-js');

  if(!meta.length){
    $('<meta class="foundation-mq">').appendTo(document.head);
  }
  if(noJS.length){
    noJS.removeClass('no-js');
  }

  if(type === 'undefined'){//needs to initialize the Foundation object, or an individual plugin.
    Foundation.MediaQuery._init();
    Foundation.reflow(this);
  }else if(type === 'string'){//an individual method to invoke on a plugin or group of plugins
    var args = Array.prototype.slice.call(arguments, 1);//collect all the arguments, if necessary
    var plugClass = this.data('zfPlugin');//determine the class of plugin

    if(plugClass !== undefined && plugClass[method] !== undefined){//make sure both the class and method exist
      if(this.length === 1){//if there's only one, call it directly.
          plugClass[method].apply(plugClass, args);
      }else{
        this.each(function(i, el){//otherwise loop through the jQuery collection and invoke the method on each
          plugClass[method].apply($(el).data('zfPlugin'), args);
        });
      }
    }else{//error for no class or no method
      throw new ReferenceError("We're sorry, '" + method + "' is not an available method for " + (plugClass ? functionName(plugClass) : 'this element') + '.');
    }
  }else{//error for invalid argument type
    throw new TypeError(`We're sorry, ${type} is not a valid parameter. You must use a string representing the method you wish to invoke.`);
  }
  return this;
};
$(document).foundation();

I should be able to do the same hack, sans the copy-pasting, if the jQuery plugin function is exported in the Foundation module for AMD. I suspect this would be kind of gross though, especially since it would require the user manually assigning $.fn.foundation.

kball commented 8 years ago

It looks as though @nueverest solved this in https://github.com/vuejs-templates/webpack/issues/254 with the config

  externals: {
    foundation: 'Foundation'
  },

in webpack.conf

Can some of the other folks who are running into this check this solution out? If it works we'll add it to the docs