Подтвердить что ты не робот

NPM + Zurb Foundation + WebPack: невозможно разрешить создание модуля '

Я работаю над использованием Zurb Foundation с помощью WebPack и NPM без Bower.

Проблема, с которой я сталкиваюсь, такая же, как и ниже:

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

По сути, при установке базовых сайтов через NPM есть ссылки на модуль "фундамент", который не найден. Ошибка:

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 

Здесь 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"
  }
}

И вот 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
            }
        }
    }
};

Я могу обойти это, включив фундамент через беседку вместо этого, но я хочу устранить беседу и использовать только NPM.

4b9b3361

Ответ 1

У меня такая же проблема, но я не хочу иметь два файла .js(поставщик и приложение)!

Для меня все должно быть в одном файле, поэтому я делаю это:

В webpack.conf.js используйте внешние (возможно, другой вариант без внешнего, но для меня это достаточно):

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

создайте файл в исходной папке (любое имя, например /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;

теперь вы можете использовать Foundation в любом js, используя следующий синтаксис:

import Foundation from './libs/foundation';

Ответ 2

Я смог сделать это с помощью webpack, выполнив, в конце концов, его загрузку в качестве модуля.

Это, в основном, хак, но Foundation действительно нуждается в обновлении своего JS для загрузки в качестве модуля commonJS.

Проблема связана с зависимостями Foundation JS от ссылок с ошибками изнутри вложенных IFFE в коде сума. Иногда jQuery - это локальный параметр jQuery, иногда это $, иногда это window.jQuery. Это действительно смешанная сумка. Комбинация всех разных механизмов означает, что нет единственного решения для shimming, кроме как просто загрузить вещь не модульно.

Честно говоря, это довольно частый любительский час, но на момент написания этой статьи они только что выпустили эту вещь на прошлой неделе, поэтому, надеюсь, скоро она будет исправлена.

Anyhoo... к взлому:

Я делаю отдельный пакет поставщиков и загружаю все любительские часовые сторонние библиотеки npm там, потому что я просто устаю от борьбы со всеми различными механизмами shimming, необходимыми для того, чтобы обернуть плохой отгруженный код пакета npm с открытым исходным кодом.

Мой пакет поставщиков - это отдельная точка входа, которую я регистрирую в webpack, и она содержит все библиотеки, которые не играют в качестве модулей.

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.

Убедитесь, что установлен script -loader

npm install script-loader -D

The!! означает "Игнорировать все другие правила, которые я уже определил в конфиге". Использование script -loader сообщает webpack загружать и выполнять файл в области окна по существу так же, как если бы вы только что добавили тег script на свою страницу. (Но это не так, очевидно.)

Вы можете полюбить себя и написать свои собственные правила разрешения, чтобы он проверял только что-то в библиотеке фундамента, но я не беспокоился, потому что надеюсь, что библиотека, столь распространенная, как Фонд, будет действовать вместе в ближайшем будущем, поэтому я может просто удалить этот хак.

Также... в вашей основной конфигурации webpack вам нужно будет ссылаться на jQuery и любые другие глобальные переменные окна, загруженные таким образом как внешние.

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

Ответ 3

Я выложу свое полное обходное решение, основанное на Мейсоне Хаутсе и замечательных ответах, в случае, если это поможет кому-то, так как я немного с этим боролся, изучая Webpack в процессе.

В моем случае у меня было дополнительное осложнение, потому что другие плагины jQuery каким-то образом работали только внутри своего собственного модуля, а вне их свойств были undefined. По-видимому, они использовали локальный, повторяющийся объект jQuery.

В любом случае, вот что вам нужно сделать:

  • Установить скрипт-загрузчик: npm install --save-dev script-loader

  • В конфигурации Webpack:

    • Добавьте новую запись, позвоните ей vendor. При каждом запуске Webpack это скомпилирует новый vendor.js.

      entry: {
          ...,
          "vendor": [
              "!!script!jquery/dist/jquery.min.js",
              "!!script!foundation-sites/dist/foundation.min.js"
          ]
      },
      
    • Добавьте jquery к внешним. Это гарантирует, что ссылки на jquery внутри вашего основного JS будут заменены ссылкой на глобальную переменную jquery, которая становится доступной vendor.js выше.

      entry : {
          // ...
      },
      externals: {
          jquery: "jQuery"
      }
      
  • Убедитесь, что каждый модуль, использующий jQuery, импортирует его:

    var $ = require('jquery');
    

Конфигурация externals выше заменит его ссылкой на глобальную переменную jquery вместо повторного импорта дубликата jQuery "правильно". При желании вы можете использовать ProvidePlugin, который будет автоматически выполнять вышеописанное всякий раз, когда он сталкивается с jQuery в модуле, сохраняя несколько нажатий клавиш. Если вы хотите это, поместите в конфигурацию Webpack следующее:

plugins: [
    // ...,
    new webpack.ProvidePlugin({
      '$': 'jquery', 
      jQuery: 'jquery'
    })
]
  1. Включите новый vendor.js на свою страницу, очевидно, перед основным JS.

Вполне возможно, что это проще или более изящным способом сделать это, но я просто хотел получить быстрое рабочее решение, пока Фонд, надеюсь, не исправил проблему в ближайшее время.

Ответ 4

Просто используйте script -loader (npm i script-loader) и префикс вашего импорта с помощью script!. Затем он будет оцениваться в глобальном масштабе.

Чтобы загрузить все js файлы из базы, используйте

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

Как я делаю это в моей точке входа

Вы можете проверить мой проект шаблона, чтобы попробовать его: https://github.com/timaschew/r3-foundation-boilerplate

Ответ 5

В то время как ответ @roberto выглядит великолепно, я хотел бы предложить более простое решение (в котором он не требует каких-либо дополнительных файлов для разработчиков/фондов).

В конфигурации вашего webpack используйте это:

// 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'
  })
]

И в вашем 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();
  …
});

ПРИМЕЧАНИЕ # 1 (спасибо @mtyson)
Вам нужно использовать экспортный загрузчик: $ npm install --save exports-loader или $ npm install --save-dev exports-loader

ПРИМЕЧАНИЕ # 2
Поскольку jQuery не является глобальным внутри отдельных модулей (или по какой-либо другой причине, которая не соответствует моему пониманию), могут возникнуть проблемы, связанные с атрибутами data- для компонентов Foundation JS. Если это так, вы всегда можете использовать чистый javascript путь, как описано в документах Foundation.

Ответ 6

Вот как я использую хак. Я поместил основание и jquery в отдельную точку входа под названием vendor и загрузил их с помощью script -loader. Единственные соответствующие биты находятся в точке входа поставщика.

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]' }
    ]
  }
};

Ответ 7

Он отлично работает для webpack, если вы можете сказать ему проигнорировать тест define для нижеописанного ниже кода:

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

Лучший способ сделать это - использовать imports-loader и установить define на 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');

Ответ 8

Если вы внимательно изучите модули base-sites 6.2.0, вы найдете изменения в пути, как показано ниже:

  • фундаментные-сайты/расстояние/CSS/foundation.min.css
  • фундаментные-сайты/DIST/JS/foundation.min.js

Таким образом, в основном вы должны вносить изменения в запись в веб-папке, как указано ниже.

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

и запись для стиля должна быть такой

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

Ответ 9

Для себя я использовал это решение:

Я использую фреймворк Laravel, поэтому сначала я добавил метод .webpackConfig(...) в файл webpack.mix.js:

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()
                }
            ]
        }]
    }
});

Примечание. Для установки Foundation я использовал пакет https://github.com/laravel-frontend-presets/zurb-foundation. И добавил код для загрузки Foundation в файл /resources/assets/js/bootstrap.js:

/**
 * 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) {}

Во-вторых, я создал файл /resources/assets/js/plugins/entries/foundation.js (файл включен в приведенный выше код //The app plugins for the Foundation.). В которые я включил свои модули (пример):

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

В-третьих, я создал два файла для включения плагинов 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};

В четвертом я создал файл для своего плагина, используя шаблон плагинов Foundation, который включает в себя 2 из вышеуказанных файлов:

/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};

Все это. Далее мне просто нужно включить стандартный файл JavaScript в HTML-код страницы и инициализировать основание (пример):

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

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

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

PS Извините за мой английский ;-), я использовал Google Translate.

Ответ 10

Это обычное место для поиска в Интернете с вопросами для начинающих, связанными с технологиями, упомянутыми в этом вопросе.

Вот мой Zurb Foundation с примером проекта SCSS и Webpack.

Не стесняйтесь клонировать это. Он лицензируется с Unlicense.

Фон

Хотя проблемы совместимости модулей решаются с помощью последних (> 6.4) версий Zurb Foundation, базовая настройка для начинающего может показаться чёрной магией. Я думаю, что где-то должны быть другие и, возможно, лучшие примеры, как у меня, но я просто не мог их найти. Я добавляю плоды моего учебного путешествия здесь в надежде, что это заставит кого-то кататься немного неровно.