azproduction / lmd

LMD - JavaScript Module-Assembler for building better web applications :warning: Project is no longer supported :warning:
http://azproduction.ru/lmd/
MIT License
449 stars 27 forks source link

Module wrappers differences #182

Closed jeron-diovis closed 10 years ago

jeron-diovis commented 10 years ago

Добрый день. Мне непонятны некоторые тонкости в обёртывании модулей.

wrap3partyModule

3-party модулем признаётся любой модуль, в конфиг которого добавлен exports, require или bind. При этом заголовок такого модуля устанавливается в '(function (require) {'. В результате этого, отсутствие exports радикально меняет внутреннюю логику экспорта такого модуля, полагающегося на традиционное "if (typeof exports !== 'undefined') {" - а это влечёт необходимость подстраивать конфиг уже под новое поведение.

Пример. Backbone, загружаемый как amd-модуль (он это умеет с v1.1), сам требует underscore и jquery. Загружаемый как commonjs-модуль, jquery он уже не требует (не знаю, зачем они так сделали, но как есть). Допустим, я использую commonjs, но при этом хочу, чтобы он всё же использовал именно jquery. Дописываю "require: 'jquery'" -> модуль становится 3-party -> exports из обёртки выбрасывается -> заголовок backbone решает, что у нас нет ни amd, ни commonjs -> backbone экспортируется вообще в глобалы, при этом ещё и underscore перестаёт сам запрашивать -> приходится дописывать в require ещё и underscore.

Думается, это отнюдь не прозрачное и логичное поведение.

Собственно, вопрос - почему exports и module не включаются в обёртку для 3-party модулей (чем негативным это может грозить)? Можно ли их добавить?

wrapAmdModule

Наличие в конфиге флага amd: true отнюдь не гарантирует применения соответствующей обёртки. Да, в сборку добавляется плагин с функцией define, но в самих модулях после сборки "var define = require.define" отсутствует - что, опять же, меняет логику экспортирования этих модулей, и они грузятся отнюдь не как amd. Так и должно быть, или что-то здесь не так?

azproduction commented 10 years ago

3-party модулем признаётся любой модуль, в конфиг которого добавлен exports, require или bind.

Добавляя, exports или require или bind, ты даешь понять, что данный модуль не совместим с CommonJS (иначе зачем все эти хаки?!) Всю ответственность за его зависимости берешь на себя. require() там оставлен для получения зависимостей, который ты написал в конфиге. exports и module не включается специально, чтобы не вводить в заблуждение модуль в любом случае он будет экспортироваться руками.

Ни Backbone ни Ember не умеют CommonJS или умеют очень криво. Я буквально вчера пытался подружить Ember c LMD - пришлось костылять. Вот примеры адаптации:

See 1

        "backbone": {
            "path": "lib/backbone/backbone.js",
            "require": {
                "underscore": "_",
                "jQuery": "$"
            },
            "exports": "Backbone"
        }

See 2

        "ember": {
            "path": "../bower_components/ember/ember.js",
            "require": {
                "jQuery": "jquery",
                "Handlebars": "handlebars"
            },
            "exports": "Ember"
        }

Наличие в конфиге флага amd: true отнюдь не гарантирует применения соответствующей обёртки.

LMD сканирует контент модуля, если тот AMD, то подставляет этот патч. Те для не-AMD-модулей var define = require.define не добавляется ибо нет смысла. Прим

jeron-diovis commented 10 years ago

не совместим с CommonJS

А раз несовместим, то ему место в глобалах? Логично, ок. Но.

LMD сканирует контент модуля, если тот AMD, то подставляет этот патч.

То есть если в модуле уже присутствует define? Или посложнее? То, что для не-AMD нет смысла - разумеется. Но проблема в сторонних модулях, предусматривающих разные окружения. Пусть хоть тот же backbone. Его ветка для amd-загрузки делает абсолютно всё необходимое:

if (typeof define === 'function' && define.amd) {
    define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
    ...

Но LMD не даёт ему доступа к define. Поэтому данная ветка не может быть использована, и приходится "брать ответственность на себя", как показано выше. Но зачем вся эта писанина руками, когда у нас уже есть всё необходимое, чтобы просто позволить модулю использовать все те удобства, которые он для нас предусмотрел?

Хотя не всё необходимое - свойства "amd" у LMD-шной define нет. Возможно, стоит добавить?

Кроме того, эта проблема масштабируется и на композитные модули ("jquery+plugins"). Они попадают внутрь той же обёртки с вырезанным exports и отсутствующим define, и их логика экспорта тоже меняется. И получается полный бардак. Попробуй, например, собрать композитный модуль backbone c вот этим плагином: https://github.com/rsskga/backbone.validation Для него тоже зависимости вручную прописывать? Какой же это multipath тогда?

Я вижу решение в том, чтобы:

Это полностью решает описанную проблему, а "бессмысленные" define могут появиться только если программист сам этого захочет.

Здраво или нет?

azproduction commented 10 years ago

Но LMD не даёт ему доступа к define.

AMD планировалось использовать для модулей приложения, те когда первый стейтмент define().

добавить свойство "amd" к define

Это можнонужно сделать.

сделать персональный флаг "amd" для отдельных модулей

Я когда-то хотел добавить type-хинт для модулей, чтобы ускорить сборку(LMD не будет сканировать их, чтобы определить тип).

Попробуй, например, собрать композитный модуль backbone c вот этим плагином: https://github.com/rsskga/backbone.validation Для него тоже зависимости вручную прописывать? Какой же это multipath тогда?

        "backbone": {
            "path": ["lib/backbone/backbone.js", "lib/backbone/backbone.validation.js"],
            "require": {
                "underscore": "_",
                "jQuery": "$"
            },
            "exports": "Backbone"
        }

Вот так в теории должно работать.

Итого:

Все так?

jeron-diovis commented 10 years ago

Вот так в теории должно работать

Да, так работает. Пример я не очень выбрал)

Все так?

Всё так. Один уточняющий вопрос: за что будет отвечать type: "commonjs" - за наличие exports в обёртке?

azproduction commented 10 years ago

Один уточняющий вопрос

LMD определяет тип модуля по контенту, но если добавить typе, то он проигнорирует этот этап и возьмет тип из type даже если он будет не верный.

azproduction commented 10 years ago

Закрываю этот таск. См #183 #184

azproduction commented 10 years ago

Fixed in lmd@1.13.1