Поддерживая CommonJS и AMD

Существует ли способ создания микробиблиотеки javascript (библиотеки, которая не имеет зависимостей), поддерживающей все следующие форматы модулей:

  • Определение асинхронного модуля
  • CommonJS
  • представление экспорта библиотеки как объекта глобального пространства имен (без загрузчика)

7 ответов

Решение

Вот список различных кросс-совместимых форматов модулей.

Я подозреваю, что вы ищете то, что они называют " commonjsStrict.js"

Да, и я обязан этим ответом Деду и его удивительным модулям:

(function(name, definition) {
    if (typeof module != 'undefined') module.exports = definition();
    else if (typeof define == 'function' && typeof define.amd == 'object') define(definition);
    else this[name] = definition();
}('mod', function() {
    //This is the code you would normally have inside define() or add to module.exports
    return {
        sayHi: function(name) {
            console.log('Hi ' + name + '!');
        }
    };
}));

Это может быть использовано:

  1. в AMD (например, с requireJS):

    requirejs(['mod'], function(mod) {
        mod.sayHi('Marc');
    });
    
  2. в общем JS (например, nodeJS):

    var mod = require('./mod');
    mod.sayHi('Marc');
    
  3. глобально (например, в HTML):

    <script src="mod.js"></script>
    <script>mod.sayHi('Marc');</script>
    

Этот метод должен получить больше гласности - если JQuery и Co. начать использовать его жизнь будет намного проще!

uRequire, универсальный модуль и конвертер ресурсов - это инструмент, который делает именно это.

  • Он в основном конвертирует AMD и CommonJS в сценарий UMD / AMD / CommonJS / Plain (загрузчик AMD не требуется).

  • Это позволяет декларативный экспорт модулей, с noConflict() запеченный в.

  • Он может манипулировать модулями (вставлять / заменять / удалять зависимости ИЛИ код) по мере их создания.

  • Он конвертирует из coffeescript, coco, Livescript, icedCoffeescript, и вы можете добавлять свои собственные преобразования в один лайнер!

Просто чтобы немного обновить этот ответ в отношении @marc, я тоже отдаю должное ded и немного обновил его, чтобы быть в последних обновлениях:

(function (name, definition, context, dependencies) {
  if (typeof context['module'] !== 'undefined' && context['module']['exports']) { if (dependencies && context['require']) { for (var i = 0; i < dependencies.length; i++) context[dependencies[i]] = context['require'](dependencies[i]); } context['module']['exports'] = definition.apply(context); }
  else if (typeof context['define'] !== 'undefined' && context['define'] === 'function' && context['define']['amd']) { define(name, (dependencies || []), definition); }
  else { context[name] = definition(); }
})('events', function () {
  // Insert code here
  return {
    sayHi: function(name) {
      console.log('Hi ' + name + '!');
    }
  };
}, (this || {}));

Объект в конце - это ссылка на родительский или текущий контекст, допустим, у вас есть пакет, который вы пишете, и это всего лишь кусок пирога, хорошо, что контекст может быть объектом с пространством имен, а это просто кусок этого пирога.

Также, если вы хотите иметь зависимости, в конце после вашей области видимости есть необязательный параметр, который поддерживает массив, в этом случае параметр определения может использовать каждую зависимость в качестве аргумента. Кроме того, зависимости, перечисленные в массиве, будут необходимы для платформы node-js для вашего удобства.

См. https://gist.github.com/Nijikokun/5192472 для реального примера.

Я решил именно эту проблему и смог легко поддержать:

  • Dojo AMD (ссылается на спецификации RequireJS)
  • jQuery (под $ / jQuery.fn.[your_library_here])
  • node.js, использующий vanilla require('path_to.js')
  • Окно браузера.[Your_library_here]

Это использует комбинацию внедрения зависимости и IIFE, чтобы сделать работу.

Увидеть ниже:

/*global jQuery:false, window:false */
// # A method of loading a basic library in AMD, Node.JS require(), jQuery and Javascript's plain old window namespace.
(function(exporterFunction) {
exporterFunction('cll',
    function(a,b) {
        return a+b;
    }
);
})(
    (function() { // Gets an exportFunction to normalize Node / Dojo / jQuery / window.*

        if ((typeof module != 'undefined') && (module.exports)) { // Node Module
            return function(library_name,what_was_exported) {
                module.exports = what_was_exported;
                return;
            };
        }
        if (typeof define != 'undefined' && define.hasOwnProperty('amd') && define.amd) { // Dojo AMD
            return function(library_name,what_was_exported) {
                define(function() {
                    return what_was_exported;
                });
            };
        }
        if (typeof jQuery === 'function') { // jQuery Plugin
            return function(library_name,source) {
                jQuery.fn[library_name] = source;
                return;
            };
        }
        if (typeof window != 'undefined') { // Fall down to attaching to window...
            return function(library_name,what_was_exported) {
                window[library_name] = what_was_exported;
            };
        }

    })(),
    (function() { 
        // ## Other Parameters Here
        // You could add parameters to the wrapping function, to include extra 
        // functionalilty which is dependant upon the environment... See 
        // https://github.com/forbesmyester/me_map_reduce for ideas.
        return 'this_could_be_more_arguments_to_the_main_function'; 
    })()
);

Общедоступный Gist доступен по адресу https://gist.github.com/forbesmyester/5293746

Я разместил решение на github, которое должно работать с любым браузером. Он основан на том, какjQueryиunderscore.jsреализовали свои библиотеки, но добавили частный анонимный объект, позволяющий использоватьthisссылаться на свое личное пространство имен; создание для легкого автоматизированного расширения и области видимости. Конечно, у него есть_exportобъект для предоставления ваших общедоступных API.

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

Мой код размещен здесь: https://github.com/JavaScriptDude/JSLibraryTemplate

Примечание. Я не тестировал вне браузера, но он должен работать вnode.

Это основано на ответе Ниджикокуна. Поскольку RequireJS не рекомендует использовать явные имена модулей, это было опущено в этой версии. Второй аргумент загрузчику описывает зависимости. Проходить [] если вам не нужно загружать их.

var loader = function(name, dependencies, definition) {
  if (typeof module === 'object' && module && module.exports) {
      dependencies = dependencies.map(require);
      module.exports = definition.apply(context, dependencies);
  } else if (typeof require === 'function') {
    define(dependencies, definition);
  } else {
    window[name] = definition();
  }
};

loader('app', ['jquery', 'moment'], function($, moment) {
   // do your thing
   return something;
}
Другие вопросы по тегам