web-dev-qa-db-fra.com

NPM + Zurb Foundation + WebPack: impossible de résoudre le module «fondation»

Je travaille sur l'utilisation de Zurb Foundation avec WebPack et NPM, sans Bower.

Le problème que je rencontre est le même que ci-dessous:

https://github.com/zurb/foundation-sites/issues/7386

Essentiellement, lors de l'installation de sites de fondation via NPM, il existe des références à un module "fondation" qui est introuvable. L'erreur:

Module not found: Error: Cannot resolve module 'foundation' in c:\Users\Matt\Documents\Projects\test\node_modules\foundation-sites\dist
 @ ./~/foundation-sites/dist/foundation.js 

Voici le package.json:

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "foundation-sites": "6.0.5",
    "webpack": "~1.12.6",
    "webpack-dev-server": "~1.2",
    "jquery": "2.1.1"
  }
}

Et voici webpack.config.js:

var path = require("path");
var webpack = require("webpack");
module.exports = {
    entry: {
      main: "./app/js/main.js"
    },
    output: {
        path: __dirname,
        filename: "bundle.js"
    },
    module: {
        loaders: [
            { test: /\.css$/, loader: "style!css" },
            {
                test: /\.scss$/,
                loaders: ["style", "css", "sass"]
              },
              { test: /\.vue$/, loader: 'vue' }
        ],
        resolve: {
            modulesDirectories: ['node_modules']
        }
    },
     sassLoader: {
            includePaths: [path.resolve(__dirname, "./node_modules/foundation-sites/scss/")]
          },
    devServer: {
        proxy: {
            '/api/*': {
                target: 'http://localhost:4567',
                secure: false
            }
        }
    }
};

Je peux contourner cela en incluant la fondation via bower à la place, mais je veux éliminer bower et utiliser uniquement NPM.

19
mtyson

J'ai le même problème, mais je ne veux pas avoir deux fichiers .js (fournisseur et application)!

Pour moi, tout doit être sur un seul fichier, donc je fais ceci:

Dans webpack.conf.js, utilisez des externes (peut-être une autre manière sans externe, mais pour moi, cela suffit):

externals: {
    jQuery: 'jQuery',
    foundation: 'Foundation'
},

créez un fichier dans votre dossier source (n'importe quel nom, comme / libs/foundation.js):

// jQuery
var $ = require('jquery');
global.jQuery = $;

// if you want all features of foundation
require('./node_modules_folder/foundation-sites/dist/foundation.js');

// if you want only some features
// require('./node_modules/what-input/what-input');
// require('./node_modules/foundation-sites/js/foundation.core');
// require('./node_modules/foundation-sites/js/....');

export default Foundation;

maintenant, vous pouvez utiliser Foundation dans n'importe quel js en utilisant la syntaxe suivante:

import Foundation from './libs/foundation';
16
Roberto Correia

Je publierai ma solution de contournement complète basée sur les bonnes réponses de Mason Houtz et de pharmakon au cas où cela aiderait quelqu'un, car j'ai un peu lutté avec cela, en apprenant Webpack dans le processus.

Dans mon cas, j'avais une complication supplémentaire, car d'autres plugins jQuery ne fonctionnaient en quelque sorte qu'à l'intérieur de leur propre module, tandis qu'à l'extérieur de leurs propriétés se trouvaient undefined. Apparemment, ils utilisaient un objet jQuery local en double.

Quoi qu'il en soit, voici ce que vous devez faire:

  1. Installez le chargeur de scripts: npm install --save-dev script-loader

  2. Dans la configuration de Webpack:

    • Ajoutez une nouvelle entrée, appelons-la vendor. Cela va compiler un nouveau vendor.js chaque fois que Webpack s'exécute.

      entry: {
          ...,
          "vendor": [
              "!!script!jquery/dist/jquery.min.js",
              "!!script!foundation-sites/dist/foundation.min.js"
          ]
      },
      
    • Ajoutez jquery aux éléments externes. Cela garantit que toutes les références à jquery dans votre JS principal seront remplacées par une référence à la variable globale jQuery, qui est mise à disposition par vendor.js au dessus.

      entry : {
          // ...
      },
      externals: {
          jquery: "jQuery"
      }
      
  3. Assurez-vous que chaque module qui utilise jQuery l'importe:

    var $ = require('jquery');
    

La configuration externals ci-dessus la remplacera par une référence à la variable globale jQuery, au lieu de réimporter "correctement" le jQuery dupliqué. En option, vous pouvez utiliser ProviderPlugin, qui fera automatiquement ce qui précède chaque fois qu'il rencontre jQuery dans un module, vous économisant ainsi quelques frappes. Si vous le souhaitez, mettez ce qui suit dans la configuration de Webpack:

plugins: [
    // ...,
    new webpack.ProvidePlugin({
      '$': 'jquery', 
      jQuery: 'jquery'
    })
]
  1. Inclure le nouveau vendor.js à votre page, évidemment avant le JS principal.

Il est tout à fait possible qu'il existe une manière plus simple ou plus élégante de le faire, mais je voulais juste une solution rapide et fonctionnelle, jusqu'à ce que Foundation espère résoudre le problème bientôt.

7

J'ai pu le faire avec webpack en faisant essentiellement un tour final en le chargeant en tant que module.

C'est essentiellement un hack cependant, Foundation a vraiment besoin de mettre à jour son JS pour être chargeable en tant que module commonJS.

Le problème provient du fait que JS de Foundation fait référence aux dépendances de manière erratique à partir d'IFFE imbriquées dans le code souce. Parfois jQuery est le paramètre local jQuery, parfois c'est $, parfois c'est window.jQuery. C'est vraiment un sac mixte. La combinaison de tous les différents mécanismes signifie qu'il n'y a pas de solution de calage unique autre que de simplement charger la chose de manière non modulaire.

Honnêtement, c'est à peu près l'heure amateur là-dedans, mais à ce jour, ils ont juste publié le truc la semaine dernière, donc j'espère que ce sera corrigé bientôt.

Anyhoo ... au hack:

Je fais un bundle de fournisseur séparé et j'y charge toutes les bibliothèques tierces npm de l'heure amateur parce que je suis fatigué de me battre avec tous les divers mécanismes de calage nécessaires pour envelopper le code du package npm open source mal expédié.

Mon bundle de fournisseur est un point d'entrée distinct que j'enregistre avec webpack, et il contient toutes les bibliothèques qui ne jouent pas Nice en tant que modules.

require('!!script!jquery/dist/jquery.min.js');

require('!!script!uglify!foundation-sites/js/foundation.core.js');
require('!!script!uglify!foundation-sites/js/foundation.accordion.js');
require('!!script!uglify!foundation-sites/js/foundation.util.keyboard.js');
require('!!script!uglify!foundation-sites/js/foundation.util.motion.js');
// etc.

Assurez-vous que le chargeur de script est installé

npm install script-loader -D

Le !! signifie "Ignorer toutes les autres règles que j'ai déjà définies dans la configuration". L'utilisation du chargeur de script indique au webpack de charger et d'exécuter le fichier dans la portée de la fenêtre essentiellement comme si vous veniez d'inclure une balise de script sur votre page. (Mais ce n'est pas le cas, évidemment.)

Vous pourriez devenir plus sophistiqué et écrire vos propres règles de résolution afin qu'il vérifie juste les trucs dans la fondation-bibliothèque, mais je n'ai pas dérangé parce que j'espère qu'une bibliothèque aussi omniprésente que Foundation se réunira dans un proche avenir afin que je puisse simplement supprimer ce hack.

Aussi ... dans votre configuration principale de webpack, vous voudrez référencer jQuery et toute autre variable de fenêtre globale chargée de cette manière en tant qu'externes.

var webpackConfig = {
    entry: { // blah },
    output: { // blah },
    loaders: [ // blah ],
    externals: {
        jquery: "jQuery"
    }
};
7
Mason Houtz

Utilisez simplement le chargeur de script (npm i script-loader) et préfixez vos importations avec un script!. Ensuite, il sera évalué dans la portée globale.

Pour charger tous les fichiers js de la fondation, utilisez ceci

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

Comme je le fais dans mon point d'entrée

Vous pouvez consulter mon projet de passe-partout pour l'essayer: https://github.com/timaschew/r3-foundation-boilerplate

5
timaschew

Bien que la réponse de @ roberto soit superbe, je voulais fournir une solution plus simple (dans laquelle elle ne nécessite aucun fichier fournisseur/fondation supplémentaire).

Dans votre configuration de webpack, utilisez ceci:

// this will force the export of the jQuery 'foundation' function, 
// which we'll use later on
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'
  })
]

Et dans votre index.js:

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

// required 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 force-exported in the config
// above to the jQuery object in use in this file
$.fn.foundation = foundation;

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

NOTE # 1 (merci @mtyson)
Vous devez utiliser le chargeur d'exportations: $ npm install --save exports-loader ou $ npm install --save-dev exports-loader

NOTE # 2
Étant donné que jQuery n'est pas global à l'intérieur de modules uniques (ou pour une autre raison que je ne comprends pas), il peut y avoir des problèmes en s'appuyant sur data- attributs pour les composants Foundation JS. Si tel est le cas, vous pouvez toujours utiliser la méthode javascript pur comme indiqué dans les documents Foundation.

4
mjsarfatti

Voici comment j'utilise le hack. J'ai mis foundation et jquery dans un point d'entrée séparé appelé vendeur et les ai chargés avec le chargeur de script. Les seuls bits pertinents se trouvent dans le point d'entrée du fournisseur.

var path = require('path');
var webpack = require('webpack');
var hotMiddlewareScript = 'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=20000&reload=true';
var autoprefixer = require('autoprefixer');

module.exports = {
  name: 'main',

  devtool: 'eval',

  entry: {
    client: [
      path.resolve(__dirname, 'client', 'index.js'),
      hotMiddlewareScript
    ],
    vendor: [
      'font-awesome/css/font-awesome.css',
      'foundation-sites/dist/foundation-flex.css',
      '!!script!jquery/dist/jquery.min.js',
      '!!script!foundation-sites/dist/foundation.min.js',
    ]
  },

  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
    publicPath: '/dist/'
  },

  resolve: {
    modulesDirectories: ['node_modules', './client'],
    extensions: ['', '.js', '.jsx']
  },

  plugins: [
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin(),
    new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.bundle.js'),
    new webpack.ProvidePlugin({'$': 'jquery', jQuery: 'jquery'})
  ],

  module: {
    loaders: [
      { test: /\.(js|jsx)$/, loaders: ['react-hot', 'babel-loader'], exclude: /node_modules/, include: path.resolve(__dirname, 'client') },
      { test: /\.scss$/, loader: "style!css!autoprefixer-loader?browsers=last 2 versions!sass" },
      { test: /\.css$/, loader: "style!css" },
      // { test: /\.(png|jpg|jpeg|gif)$/, loader: 'file-loader?name=images/[name].[ext]' },
      { test: /\.(webm|mp4|mov|m4v|ogg)$/, loader: 'file-loader?name=videos/[name].[ext]' },
      { test: /\.(eot|svg|ttf|woff|woff2)/, loader: 'file-loader?name=fonts/[name].[ext]' }
    ]
  }
};
2
pharmakon

Si vous voyez attentivement les modules foundation-sites 6.2., vous trouverez les changements de chemin comme suit

  1. fondation-sites/dist/css/foundation.min.css
  2. fondation-sites/dist/js/foundation.min.js

Donc, fondamentalement, vous devez modifier l'entrée des fichiers de confection du webpack comme suit

module.exports =  {
         entry: ['script!jquery/dist/jquery.min.js',
        'script!foundation-sites/dist/js/foundation.min.js',
         './app/app.jsx'
       ]
}

et l'entrée pour le style devrait être comme ça

require('style!css!foundation-sites/dist/css/foundation.min.css');

1
Dhaval Shekhada

Pour moi, j'ai utilisé cette solution:

J'utilise le Laravel framework, donc d'abord j'ai ajouté la méthode .webpackConfig (...) à la webpack.mix.js fichier:

mix.js('resources/assets/js/app.js', 'public/js')
.sass('resources/assets/sass/app.scss', 'public/css')

// By default, files from the directory "/node_modules" are not processed by the Babel loader,
// so in the Webpack configuration,
// an exception was added for loading from the directory "/node_modules/foundation-sites/js".
.webpackConfig({
    module: {
        rules: [{
            // Section "// Babel Compilation." from "/node_modules/laravel-mix/src/builder/webpack-rules.js"
            test: /\.jsx?$/,
            // Thanks for the help with "exclude": http://qaru.site/questions/97960/import-a-module-from-nodemodules-with-babel-but-failed/624982#624982
            exclude(file) {
                if (file.startsWith(__dirname + '/node_modules/foundation-sites/js')) {
                    return false;
                }
                return file.startsWith(__dirname + '/node_modules');
            },
            use: [
                {
                    loader: 'babel-loader',
                    options: Config.babel()
                }
            ]
        }]
    }
});

Remarque: Pour installer Foundation J'ai utilisé le package https://github.com/laravel-frontend-presets/ fondation zurb . Et ajouté le code pour charger Foundation dans le /resources/assets/js/bootstrap.js fichier:

/**
 * We'll load jQuery and the Foundation jQuery plugin which provides support
 * for JavaScript based Foundation features such as modals and tabs. This
 * code may be modified to fit the specific needs of your application.
 */

try {
    window.$ = window.jQuery = require('jquery');

    require('foundation-sites/dist/js/foundation'); // 'foundation.min' can also be used if you like

    // The app plugins for the Foundation
    require('./plugins/entries/foundation');

} catch (e) {}

Deuxièmement , j'ai créé le fichier /resources/assets/js/plugins/entries/foundation.js (Le fichier est inclus dans le code ci-dessus // The app plugins for the Foundation.). Dans lequel j'ai inclus mes modules (exemple):

import { CropText } from '../cropText';
Foundation.plugin(CropText, 'CropText');

Troisièmement , j'ai créé deux fichiers pour inclure les plugins Foundation:

1) /resources/assets/js/plugins/foundation.plugin.js

// By default, files from the directory "/node_modules" are not processed by the Babel loader,
// so in the Webpack configuration,
// an exception was added for loading from the directory "/node_modules/foundation-sites/js".
import { Plugin } from 'foundation-sites/js/foundation.plugin';

export {Plugin};

2) /resources/assets/js/plugins/foundation.util.mediaQuery.js

// By default, files from the directory "/node_modules" are not processed by the Babel loader,
// so in the Webpack configuration,
// an exception was added for loading from the directory "/node_modules/foundation-sites/js".
import { MediaQuery } from 'foundation-sites/js/foundation.util.mediaQuery';

export {MediaQuery};

Dans le quatrième , j'ai créé un fichier pour mon plugin en utilisant le modèle de plugins Foundation, qui comprend 2 des fichiers ci-dessus:

/resources/assets/js/plugins/cropText.js

'use strict';

import $ from 'jquery';
import { MediaQuery } from './foundation.util.mediaQuery';
import { Plugin } from './foundation.plugin';

/**
 * CropText plugin.
 * @plugin app.cropText
 */
class CropText extends Plugin {
    /**
     * Creates a new instance of CropText.
     * @class
     * @name CropText
     * @fires CropText#init
     * @param {Object} element - jQuery object to add the trigger to.
     * @param {Object} options - Overrides to the default plugin settings.
     */
    _setup(element, options = {}) {
        this.$element = element;
        this.options  = $.extend(true, {}, CropText.defaults, this.$element.data(), options);

        this.className = 'CropText'; // ie9 back compat
        this._init();
    }

    /**
     * Initializes the CropText plugin.
     * @private
     */
    _init() {
        MediaQuery._init();

        this.cropText();
    }

    /**
     * Crops the text.
     */
    cropText() {
        var size = +this.options.cropSize;

        $(this.$element).each(function(i, value) {
            var $valueText = $(value).text(),
                $valueHtml = $(value).html();

            if($valueText.length > size){
                $(value).html('<span>' + $valueText.slice(0, size).trim() + '</span>' + '...').wrapInner('<a></a>');

                var revealId = '#' + $(value).attr('data-open');

                if ($(revealId + ' .close-button').attr('data-close') != undefined) {
                    $(revealId + ' .close-button').before($valueHtml);
                } else {
                    $(revealId).append($valueHtml);
                }

                new Foundation.Reveal($(revealId), {
                    'data-overlay' : false
                });
            } else {
                $(value).removeAttr('data-open').removeAttr('tabindex');
            }
        });
    }
}

/**
 * Default settings for plugin
 */
CropText.defaults = {
    /**
     * The size of the cropped text.
     * @option
     * @type {number}
     * @default 255
     */
    cropSize: 255
};

export {CropText};

C'est tout. Ensuite, j'ai juste besoin d'inclure un fichier JavaScript standard dans le code HTML de la page et initialiser la Fondation (exemple):

/resources/views/layouts/app.blade.php

<script src=" {{ mix('/js/app.js') }} "></script>

<script>
    $(document).foundation();
</script>

P.S. Désolé pour mon anglais ;-), j'ai utilisé Google Translate.

1
Grisha_K

Cela fonctionne très bien pour webpack si vous pouvez lui dire d'ignorer le test define pour le code gênant ci-dessous:

  if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
     module.exports = Reveal;
  if (typeof define === 'function')
     define(['foundation'], function() {
     return Reveal;
  });

La meilleure façon de le faire est d'utiliser imports-loader et de définir define sur false.

require('foundation-sites/js/foundation.core.js');
require('foundation-sites/js/foundation.util.keyboard.js');
require('foundation-sites/js/foundation.util.box.js');
require('foundation-sites/js/foundation.util.triggers.js');
require('foundation-sites/js/foundation.util.mediaQuery.js');
require('foundation-sites/js/foundation.util.motion.js');
require('imports?define=>false!foundation-sites/js/foundation.reveal.js');
1
Mike M

C'est l'endroit commun pour atterrir de la recherche sur le Web avec des questions pour débutants liées aux technologies mentionnées dans la question.

Voici mon Zurb Foundation avec SCSS et Webpack Example Project .

N'hésitez pas à le cloner. Il est autorisé avec l'Unlicense.

Contexte

Bien que les problèmes de compatibilité des modules soient résolus avec les versions récentes (> 6.4) de la Fondation Zurb, une configuration de base peut toujours sembler magique pour un débutant. Je pense qu'il doit y avoir d'autres exemples, probablement meilleurs, comme le mien quelque part, mais je n'ai tout simplement pas pu les trouver. J'ajoute les fruits de mon parcours d'apprentissage ici dans l'espoir que cela rendra la conduite de quelqu'un un peu moins cahoteuse.

1
Peter Lamberg