Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
338 lines (218 sloc) 20.3 KB

clinch

clinch - еще один упаковщик ComonJS-style проектов для браузера.

Он отлично подходит для небольших модулей благодаря малому оверхеду на имитацию require-логики.

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

Кроме того он быстро компилирует первый проход упаковки и имеет изящное решение кеширования данных.

ВАЖНЫЕ ИЗМЕНЕНИЯ

1.0.1

Больщинство обработчиков вынесено в отдельные модули.

Это может сломать ваш старый сборщик, для восстановления работоспособности которого потребуется добавить используемые вами модули в зависимости, установить их и объявить в сборщике, используя метод .addPlugin() - смотрим секцию с примером.

что в коробке?

  • .js - просто упаковывается в бандл как есть
  • .json - заворачивается в module.exports как node и делает при require('file.json')

Да, с версии 1.0.1 коробка изрядно опустела и по умолчанию сам clinch умеет работать только с этими двумя типами файлов.

где остальные процессоры?

Все остальные обработчки вынесены в отдельные модули. На данный момент их список таков:

  • clinch.coffee - .coffee - компилируется в JavaScript
  • clinch.eco - .eco - прекомпилируется как JavaScript функция
  • clinch.jade - .jade - прекомпилируется как client-mode вариант
  • clinch.jsx - .jsx - компилирует React '.jsx' в JavaScript
  • clinch.csbx - .csbx - компилирует '.jsx', написанный на Coffee с обратными лапками в JavaScript (да, мы так делаем)

Разделение кода сделано с целью предоставления гибкости в целевых версиях необходимых вам сборщиков. Т.е. например вы можете продолжать использовать сборщик .csbx с версией React 0.11.1 для старого проекта и взять для нового 0.14.1, просто прописав нужные вам версии react-процессора для clinch в package.json вашего проекта.

а можно подключить мой любимый шаблонизатор?

Такая возможность скорее всего есть - clinch предоставляет возможность подключения сторонних парсеров, но со стороны парсера должна быть поддержка предкомпиляции щаблона в функцию.

Подробности и пример - ниже, в описании методов addPlugin() и registerProcessor().

Кроме того есть пример использования Handelbars.

installation

npm install clinch clinch.coffee

example

#!/usr/bin/env coffee
Clinch           = require 'clinch'
clinch_coffee = require 'clinch.coffee'

packer = new Clinch runtime : on
# register '.coffee' processor
packer.addPlugin clinch_coffee

pack_config = 
  package_name : 'my_package'
  bundle : 
    main : "#{__dirname}/hello_world"
packer.buildPackage pack_config, (err, data) ->
  if err
    console.log 'Builder, err: ', err
  else
    console.log 'Builder, data: \n', data

Контент файла ./hellow_world

###
This is 'Hello World!' example
###
module.exports = 
  hello_world : -> 'Hello World!'

Даст нам в data примерно такие данные

(function() {
  'use strict';

  var dependencies, sources, require, modules_cache = {};
  dependencies = {};

  sources = {
"JPGt0": function(exports, module, require) {
// /Users/meettya/github/clinch/example/hello_world/hello_world.coffee 

/*
This is 'Hello World!' example
 */
module.exports = {
  hello_world: function() {
    return 'Hello World!';
  }
};

}};

if(this.clinch_runtime_v2 == null) {
  throw Error("Resolve clinch runtime library version |2| first!");
}

require = this.clinch_runtime_v2.require_builder.call(this, dependencies, sources, modules_cache);

/* bundle export */
this.my_package = {
  main : require("JPGt0")
};
}).call(this);

И в браузере функция будет доступна вот так

hello_world = my_package.main.hello_world

Особенности:

только require-based включение модулей

В отличие от stitch в пакет включаются только модули, которые загружаются посредством require() а не все, лежащие в перечисленных папках.

честный AST-парсинг кода модулей

clinch проводит поиск в результирующем CommonJS-коде, что исключает появление в зависимостях закомментированных модулей и прочих странных вещей. Скоро появится настойка для "нечестного" (зато быстрого) поиска.

исходный код модулей не модифицируется

clinch добавляет к исходному коду модуля только комментарий с путем файла, для облегчения отладки. Сам код остается в неприкосновенности, следовательно появление ошибки в результате обработки упаковщиком маловероятно.

но шаблоны прекомпилируются

clinch поддерживает шаблонные движки, прекомпилируя шаблон в быструю функцию. Попробуте Jade, он крут.

есть разные версии и нет дублей

Благодаря использованию деревьев зависимостей и подмене имен файлов на их хеш от содержимого решается проблема одноименных подчиненных модулей с разной версией и исключаются дубликаты модулей, имеющих разные имена и одинаковое содержимое (это чертовски сложно объяснить, но это работает, просто поверьте).

бандл, а не приложение

clinch создает бандл-пак, а не собирает приложение. В чем разница? У вас сколько угодно точек входа. Более того, можно сделать дополнительной точкой входа суб-модуль, и это никак не изменит размер получаемого пакета.

development-mode ready

clinch может быть использован для development-mode http-serverа прямо из коробки. Все асинхронно, везде кеш, умная инвалидация кеша в комплекте. Объявите объект повыше и используйте для сборки кода на лету.

кеширование модулей

Однажды "разрешенные" (с помощью require) модули кешируются и вне зависимости от количества обращений к модулю вы всегда имеете дело с одной и той-же копией объекта (как это и делает node.js). Важно - если что-то пошло не так - можете выключить данную настройку, но, вероятнее всего, в вашем коде есть неоднозначности.

runtime aka external lib

ДА! У нас есть возможность выделить boilerplate-часть бандла ссылкой на внешнюю библиотеку (идет в комплекте, полный файл и минифицированная версия). При использовании более одного бандла профит очевиден. Рекомендую к использванию.

code coverage

clinch идет в комплекте с гиганским набором тестов, и мы знаем что такое regression

API

У clinch очень простой API

constructor

packer = new Clinch clinch_options

clinch_options - настройки для Clinch

buildPackage()

packer.buildPackage package_config, cb
# or old form, will be deprecated in new version
packer.buildPackage package_name, package_config, cb

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

package_config - настройки пакета.

cb - стандартный коллбек, для работы с результатами, все в clinch асинхронно.

addPlugin()

packer.addPlugin clinch_plugin

Данный метод позволяет подключать плагины, по факту являясь синтаксическим сахаром к registerProcessor() clinch_plugin - плагин-процессор для определенного типа файлов

Простой пример:

# declare plugin in place
econ_plugin = 
  extension : '.econ'
  processor : (file_content, filename, cb) ->
    content = Eco.precompile file_content
    cb null, "module.exports = #{content}"

# add .econ processor
packer.addPlugin econ_plugin

registerProcessor()

packer.registerProcessor file_extention, fn

Данный метод позволяет зарегистрировать произвольный обработчик контента файла, который будет использован для обработки файла с указанным разрешением.

file_extention - разрешение файла, которое будет обработано

fn - функция для обработки файла

Простой пример:

# add .econ processor
packer.registerProcessor '.econ', (file_content, filename, cb) ->
  content = Eco.precompile file_content
  cb null, "module.exports = #{content}"

Теперь clinch будет компилировать указаным образом все запрошенные файлы с разрешением .econ

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

template = require './template' # ./template.econ, расширение можно не указывать
res = template data # в res будет какой-то html

Фактически аналогичен методу addPlugin().

flushCache()

packer.flushCache()

Этот метод сбрасывает кеш пакера. Обычно инвалидатор кеша в clinch отлично справляется со своей работой, но если вам по каким-то причинам нужно сделать принудительный ручной сброс - это просто.

getPackageFilesList()

packer.getPackageFilesList package_config, cb

Этот метод вернет (асинхронно) список (массив) всех файлов , которые участвуют в построении бандла. Метод может быть полезен при создании наблюдателя над этими файлами, или для каких-то других целей.

Settings

clinch_options

log           : off  # включает отладочную информацию, пока не реализовано
strict        : on   # управляет строчкой 'use strict;' в шапке бандла
inject        : on   # если изменить на 'off' - бандл не будет инжектить 'package_name' в глобальную переменную. 
runtime       : off  # задействуйте эту настройку для использования внешнего файла с реализацией require 
cache_modules : on   # по умолчанию все модули, разрешенные единожды require будут закешированы (если у вас появятся какие-то проблемы - выключите опцию и сообщите, пожалуйста, мне)

package_config

пример доступных настроек пакета с комментариями

###
Может отсутствовать, в таком случае `clinch` или инжектит ВСЕ перечисленные в `bundle` элементы в глобаль, или, если у нас `inject : off` - просто делает все элементы локальными для пакета (такую технику можно использовать для виджетов, которые сами инициируют себя по содержанию страницы, ninja-hide-style)
###
package_name : 'bundle_pack_name'

# локальные для бандла настройки
strict : on   # управляет строчкой 'use strict;' в шапке бандла
inject : on   # если изменить на 'off' - бандл не будет инжектить 'package_name' в глобальную переменную. 
runtime       : off  # задействуйте эту настройку для использования внешнего файла с реализацией require 
cache_modules : on   # по умолчанию все модули, разрешенные единожды require будут закешированы (если у вас появятся какие-то проблемы - выключите опцию и сообщите, пожалуйста, мне)

###
Ветка bundle перечисляет модули, которые будут включены в пакет 
И будут доступны в браузере из глобального объекта пакета
###
bundle :
  main : './src'              # -> my_package.main
  helper : './src/lib/helper' # -> my_package.helper

###
Ветка replacement перечисляет модули, которые будут подменены.
Кроме того здесь следует указывать любые node.js - core модули,
так как их импорт по умолчанию не производтся.
###
replacement :
  util : './node_modules/js-util'
  lodash : -> @_  # да, можно использовать функцию, а не файл - ( this указывает на ГЛОБАЛЬНЫЙ скоп )

###
Ветка requireless может быть использована для ускорения сборки пакета
перечисленные модули не будут разбираться на предмет наличия в них
require, что существенно сокращает время сборки, особенно с большими файлами
###
requireless : [
  'lodash'
]

###
Ветка environment может использоваться для имитации node.js окружения,
ключи становятся локальными для пакета переменными, с похожим для node.js поведением.
Используйте осторожно, точно понимая что вы делаете.
###
environment :
  process : './node/js-process'
  console : './node_modules/console-shim'

###
Ветка exclude используется для исключения модулей из пакета,
однако ее ценность выглядит сомнительной, возможно в дальнейшем
она будет исключена. Используйте replacement и fake-модули.
###
exclude : [
  'underscore'
]

Что на выходе?

Результатом работы clinch является SIF бандл-пак, который инжектит в this ключ с именем бандл-пака, в содержимом будут ключи, перечисленные в bundle-ветке настроек.

Если проще, то после загрузки файла в браузер необходимые модули станут доступны как то так - var main = my_package.main

Кроме того у нас есть настройка inject которая упрявляет инжектом и можно оставить глобальное пространство чистым, используя привязку, положим, виджетов к data-attribute.

У меня ничего не работает

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

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

Кроме того можете проверить директории example и test на предмет подсказок и примеров использования.

Примеры

Смотри example или test директории.

Кроме того несколько простых примеров результата доступны online тут - clinch_demo.

Так же clinch был использован для упаковки проекта TinyData, смотри исходники на странице или packed lib

Благодарности

Shuvalov Anton

Simakov Konstantin