Product
Socket Now Supports uv.lock Files
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
Сборщик проектов. С помощью ENB можно собрать любой проект, который строится на модели node / target.
Возможности
YENV
).Зачем нужен этот проект, если есть bem-tools?
ENB работает гораздо быстрее, чем bem-tools. Причем, как в режиме сборки, так и в режиме сервера.
Почему?
Некоторые отличия от bem-tools
deps.js
в рамках одной ноды на основе различных bemdecl.js
.Как потестить?
Специально для этого я подготовил сборку project-stub на ENB: https://github.com/mdevils/project-stub
Пакеты для ENB
vow
, vow-fs
, inherit
, советы, поддержку и мотивацию.git
, modules
, поддержку и полезные ссылки.borschik
, советы и правильные вопросы.Предполагается, что вы установили npm
-пакет enb
и находитесь в корне проекта.
Сборка всех страниц проекта:
./node_modules/.bin/enb make
Сборка всех страниц проекта со сбросом кэша:
./node_modules/.bin/enb make --no-cache
Сборка всех страниц проекта с построением графа сборки:
./node_modules/.bin/enb make --graph
Сборка всех страниц проекта с включённым профайлером (считает время работы таргетов и технологий):
./node_modules/.bin/enb make --profiler
Сборка всех страниц проекта и запись данных о сборке (результаты профайлера) в файл:
./node_modules/.bin/enb make --build-info-file="build-info.json"
Сборка конкретной страницы проекта:
./node_modules/.bin/enb make pages/index
Сборка конкретного файла:
./node_modules/.bin/enb make pages/index/index.html
Запуск в режиме сервера:
./node_modules/.bin/enb server
Отключение цветового форматирования при выводе прогресса в консоль:
NOCOLOR=1 ./node_modules/.bin/enb make
Установка лимита открытых файлов для асинхронных операций. Правильно выбранный лимит позволяет избежать EMFILE
-ошибок:
ENB_FILE_LIMIT=100 ./node_modules/.bin/enb make
index.js
в рамках ноды pages/index
..pages/index
.js
.?
. Знак ?
заменяется на имя ноды в процессе настройки технологии, а с помощью подстроки {lang}
можно создать несколько копий технологии для каждого из языков, где {lang}
заменится на аббревиатуру языка в каждой из копий технологии. Например, таргет ?.js
заменяется на search.js
, если нода — pages/search
. Такой подход удобен, когда мы настраиваем несколько нод через nodeMask
.<project_root>/.enb/make.js
.ENB
узнаёт из команды enb make [target]
. Если вы запустили enb make
без указания конкретного таргета, ENB
будет собирать все таргеты, определенные в make.js
.ENB
инициализирует ноды, участвующие в сборке указанных таргетов. В это время каждая нода спрашивает у технологий (которые регистрировались внутри ноды) список таргетов.requireSources
). В таком случае технология приостанавливается, нода запускает технологии, отвечающие за требуемые таргеты (если они не запущены) и после того, как технологии заканчивают сборку нужных таргетов, продолжает свою работу искомая технология.resolveTarget
).package.json
проекта зависимость от пакета enb
(желательно в виде ">=последняя_версия").npm install
.ENB
установлен. Команда node_modules/.bin/enb
должна выполниться без ошибок..bem/enb-make.js
вида:module.exports = function(config) {
};
ENB
работает. Команда node_modules/.bin/enb make
должна выполниться без ошибок.pages/index
.module.exports = function(config) {
config.node('pages/index', function(nodeConfig) {
});
};
Так объявляется нода в рамках make-платформы. В данный момент она не настроена, а лишь объявлена. 7. Объявим таргеты, которые надо собрать для ноды:
module.exports = function(config) {
config.node('pages/index', function(nodeConfig) {
nodeConfig.addTargets(['_?.js', '_?.css']);
});
};
Таргеты объявлены, но при попытке выполнить node_modules/.bin/enb make
будет ошибка, т.к. не зарегистрированы технологии, которые могут предоставить таргеты.
8. Зарегистрируем базовые технологии:
module.exports = function(config) {
config.node('pages/index', function(nodeConfig) {
nodeConfig.addTechs([
[ require('enb/techs/levels'), { levels: getLevels(config) } ],
[ require('enb/techs/file-provider'), { target: '?.bemdecl.js' } ],
require('enb/techs/deps-old'),
require('enb/techs/files')
]);
nodeConfig.addTargets(['_?.js', '_?.css']);
});
};
function getLevels(config) {
return [
{path: 'bem-bl/blocks-common', check: false},
{path: 'bem-bl/blocks-desktop', check: false},
{path: 'lego/blocks-common', check: false},
{path: 'lego/blocks-desktop', check: false},
'common.blocks',
'desktop.blocks'
].map(function(levelPath) { return config.resolvePath(levelPath); });
}
Чтобы не засорять конфиг ноды, функцию getLevels
мы выносим в нижнюю часть файла.
Рассмотрим каждую технологию:
enb/techs/levels — собирает информацию об уровнях переопределения проекта. Результат выполнения этой технологии необходим технологиям enb/techs/deps
, enb/techs/deps-old
и enb/techs/files
. Для каждой ноды по умолчанию добавляется уровень <путь_к_ноде>/blocks
. Например, для ноды pages/index
— pages/index/blocks
.
enb/techs/file-provider — сообщает make-платформе, что таргет (переданный в опции target
) уже готов. В нашем случае, исходным файлом для сборки является index.bemdecl.js
. Он лежит в репозитории и отдельная сборка для него не требуется.
enb/techs/deps-old — собирает ?.deps.js
(index.deps.js
) на основе index.bemdecl.js
и index.levels
. Почему deps-old
? В lego не хватает ряда зависимостей, поэтому ваш проект может не собраться с более быстрый технологией deps
без модификации lego. Технология deps-old
повторяет алгоритм сборки из bem-tools
и нехватка зависимостей становится незаметной, как раньше.
enb/techs/files — собирает полный список файлов со всех уровней переопределения в том порядке, в котором они идут в финальном index.deps.js
. Результат этой технологии может использоваться, например, в технологии enb/techs/js
.
module.exports = function(config) {
config.node('pages/index', function(nodeConfig) {
nodeConfig.addTechs([
[ require('enb/techs/levels'), { levels: getLevels(config) } ],
[ require('enb/techs/file-provider'), { target: '?.bemdecl.js' } ],
require('enb/techs/deps-old'),
require('enb/techs/files'),
require('enb/techs/js'),
require('enb/techs/css')
]);
nodeConfig.addTargets(['_?.js', '_?.css']);
});
};
function getLevels(config) {
return [
{path: 'bem-bl/blocks-common', check: false},
{path: 'bem-bl/blocks-desktop', check: false},
{path: 'lego/blocks-common', check: false},
{path: 'lego/blocks-desktop', check: false},
'common.blocks',
'desktop.blocks'
].map(function(levelPath) { return config.resolvePath(levelPath); });
}
Теперь файлы index.js
и index.css
могут собираться с помощью технологий enb/techs/js
и enb/techs/css
соответственно.
Но мы регистрировали иные таргеты: _?.js
(_index.js
) и _?.css
(_index.css
). Для их сборки воспользуемся технологией enb/techs/file-copy
.
module.exports = function(config) {
config.node('pages/index', function(nodeConfig) {
nodeConfig.addTechs([
[ require('enb/techs/levels'), { levels: getLevels(config) } ],
[ require('enb/techs/file-provider'), { target: '?.bemdecl.js' } ],
require('enb/techs/deps-old'),
require('enb/techs/files'),
require('enb/techs/js'),
require('enb/techs/css'),
[ require('enb/techs/file-copy'), { sourceTarget: '?.js', destTarget: '_?.js' } ],
[ require('enb/techs/file-copy'), { sourceTarget: '?.css', destTarget: '_?.css' } ]
]);
nodeConfig.addTargets(['_?.js', '_?.css']);
});
};
function getLevels(config) {
return [
{path: 'bem-bl/blocks-common', check: false},
{path: 'bem-bl/blocks-desktop', check: false},
{path: 'lego/blocks-common', check: false},
{path: 'lego/blocks-desktop', check: false},
'common.blocks',
'desktop.blocks'
].map(function(levelPath) { return config.resolvePath(levelPath); });
}
Теперь можно выполнить команду node_modules/.bin/enb make
и в папке pages/index
будут столь нужные нам _index.js
и _index.css
.
Окей, мы получили результат, с которым можно работать. Но как же production-режим?
module.exports = function(config) {
config.node('pages/index', function(nodeConfig) {
nodeConfig.addTechs([
[ require('enb/techs/levels'), { levels: getLevels(config) } ],
[ require('enb/techs/file-provider'), { target: '?.bemdecl.js' } ],
require('enb/techs/deps-old'),
require('enb/techs/files'),
require('enb/techs/js'),
require('enb/techs/css')
]);
nodeConfig.mode('development', function(nodeConfig) {
nodeConfig.addTechs([
[ require('enb/techs/file-copy'), { sourceTarget: '?.js', destTarget: '_?.js' } ],
[ require('enb/techs/file-copy'), { sourceTarget: '?.css', destTarget: '_?.css' } ]
]);
});
nodeConfig.mode('production', function(nodeConfig) {
nodeConfig.addTechs([
[ require('enb-borschik/techs/borschik'), { sourceTarget: '?.js', destTarget: '_?.js', minify: true } ],
[ require('enb-borschik/techs/borschik'), { sourceTarget: '?.css', destTarget: '_?.css', minify: true } ]
]);
});
nodeConfig.addTargets(['_?.js', '_?.css']);
});
};
function getLevels(config) {
return [
{path: 'bem-bl/blocks-common', check: false},
{path: 'bem-bl/blocks-desktop', check: false},
{path: 'lego/blocks-common', check: false},
{path: 'lego/blocks-desktop', check: false},
'common.blocks',
'desktop.blocks'
].map(function(levelPath) { return config.resolvePath(levelPath); });
}
Теперь для production-режима конечные файлы обрабатываются Борщиком. Production-режим запускается командой YENV=production node_modules/.bin/enb make
js
и css
работает. Если в вашем проекте присутствуют другие цели или мультиязычность, то можно продолжить чтение данной документации в поисках информации о небходимых технологиях.node_modules/.bin/enb make
.module.exports = function(config) {
// Языки для проекта.
config.setLanguages(['ru', 'en']);
// Добавление набора нод в сборку.
config.nodes('pages/*');
// Добавление ноды в сборку + конфигурирование ноды.
config.node('pages/index', function(nodeConfig) {
// Переопределение языков для конкретной ноды.
nodeConfig.setLanguages(['ru']);
// Добавление одной технологии с опциями.
nodeConfig.addTech([ require('enb/techs/file-provider'), { target: '?.bemdecl.js' } ]);
// Добавление нескольких технологий.
nodeConfig.addTechs([
[ require('enb/techs/levels'), {
levels: [
'common.blocks',
'desktop.blocks'
].map(function(config) { return config.resolvePath(level); }) // Резолвинг путей от корня проекта.
}],
require('enb/techs/deps'),
require('enb/techs/files'),
// Добавление технологии с опциями
[ require('enb/techs/js'), { target: '?.new.js' } ],
require('enb/techs/css')
]);
// Добавление одного таргета.
nodeConfig.addTarget('?.css');
// Добавление нескольких таргетов.
nodeConfig.addTargets(['?.css', '?.js']);
});
// Настройки для режима development.
config.mode('development', function() {
// Настройка нод по маске (regex).
config.nodeMask(/pages\/.*/, function(nodeConfig) {
nodeConfig.addTechs([
[ require('enb/techs/file-copy'), { sourceTarget: '?.css', destTarget: '_?.css'} ],
[ require('enb/techs/file-copy'), { sourceTarget: '?.js', destTarget: '_?.js'} ]
]);
});
});
// Настройки для режима production.
config.mode('production', function() {
// Настройка нод по маске (regex).
config.nodeMask(/pages\/.*/, function(nodeConfig) {
nodeConfig.addTechs([
[ require('enb/techs/borschik'), { sourceTarget: '?.css', destTarget: '_?.css'} ],
[ require('enb/techs/borschik'), { sourceTarget: '?.js', destTarget: '_?.js'} ]
]);
});
});
// Регистрация таска.
config.task('i18n.get', function(task) {
// Выполнение shell-команды.
return task.shell('./blocks/lego/tools/get-tanker.js');
});
// Установка переменных среды для shell-команд.
config.setEnv({
PRJ_ROOT : config.resolvePath(), // Получение абсолютного пути к папке с проектом.
TANKER_HOST : 'tanker-test.yandex-team.ru',
TANKER_PRJ : 'super-project',
TANKER_PRJ_REV : 'master'
});
};
При разработке nodejs
-приложений на базе express
можно сильно упростить использование enb
в development
-режиме.
Суть в том, что можно забыть о пересборке проекта, о других портах для статики и т.п. Можно просто отправлять в ENB
запросы на сборку тогда, когда это необходимо. То есть, когда вы открываете в браузере свой проект.
Для этого можно воспользоваться express
-совместимым middleware
. Его возвращает метод createMiddleware
модуля
lib/server/server-middleware
.
/**
* @param {Object} options
* @param {String} options.cdir Корневая директория проекта.
* @param {Boolean} options.noLog Не логгировать в консоль процесс сборки.
* @returns {Function}
*/
module.exports.createMiddleware = function(options) { /* ... */ };
Пример использования:
app
.use(require('enb/lib/server/middleware').createMiddleware())
.get('/', function (req, res) {
/* ... */
});
Помимо упрощения сборки статики в dev
-режиме с помощью ENB
в express
-приложениях,
можно собирать по требованию различные ресурсы, например, шаблоны.
Если nodejs
приложению в процессе работы требуется собирать шаблоны или локализацию (или что-нибудь еще),
то можно воспользоваться методом createBuilder
модуля lib/server/server-middleware
.
/**
* @param {Object} options
* @param {String} options.cdir Корневая директория проекта.
* @param {Boolean} options.noLog Не логгировать в консоль процесс сборки.
* @returns {Function}
*/
module.exports.createBuilder = function(options) { /* ... */ };
Пример использования:
var clearRequire = require('clear-require');
var enbBuilder = require('enb/lib/server/server-middleware').createBuilder();
app
.get('/', function (req, res, next) {
var bemhtmlFilePath = 'pages/index/index.bemhtml.js';
enbBuilder(bemhtmlFilePath).then(function() {
var bemhtmlAbsFilePath = process.process.cwd() + '/' + bemhtmlFilePath;
clearRequire(bemhtmlAbsFilePath);
var bemhtml = require(bemhtmlAbsFilePath);
res.end(bemhtml.BEMHTML.apply({block: 'b-page', content: 'Hello World'}));
next();
}, next);
});
В алфавитном порядке.
Все технологии, включенные в пакет ENB
, находятся в папке techs
пакета. Подключаются из make-файла с помощью require('enb/techs/<tech-name>')
. Например, require('enb/techs/js')
. Подключаются к ноде указанием класса и опций: nodeConfig.addTech([ require('enb/techs/<tech-name>'), {/* [options] */} ]);
, либо без опций: nodeConfig.addTech(require('enb/techs/<tech-name>'));
.
Если при настройке технологии в опциях указана подстрока {lang}
, то будет создано столько копий технологии, сколько языков установлено для ноды или проекта (если у ноды не указаны языки).
Например:
nodeConfig.setLanguages(['ru', 'en', 'tk']);
nodeConfig.addTech([require('js-i18n'), { target: '?.{lang}.js', lang: '{lang}' }]);
Эквивалентно:
nodeConfig.addTech([require('js-i18n'), { target: '?.ru.js', lang: 'ru' }]);
nodeConfig.addTech([require('js-i18n'), { target: '?.en.js', lang: 'en' }]);
nodeConfig.addTech([require('js-i18n'), { target: '?.tk.js', lang: 'tk' }]);
Копирует один таргет в другой.
Может, например, использоваться для построения _?.css
из ?.css
для development-режима.
Опции
Пример
nodeConfig.addTech([ require('enb/techs/file-copy'), {
sourceTarget: '?.css',
destTarget: '_?.css'
} ]);
Склеивает набор файлов в один.
Опции
Пример
nodeConfig.addTech([ require('enb/techs/file-merge'), {
sources: ['?.bemhtml', '?.pre.js']
target: '?.js'
} ]);
Предоставляет существующий файл для make-платформы. Может, например, использоваться для предоставления исходного bemdecl-файла.
Опции
Пример
nodeConfig.addTech([ require('enb/techs/file-provider'), { target: '?.bemdecl.js' } ]);
Создает симлинк из одного таргета в другой. Может, например, использоваться для построения _?.css
из ?.css
для development-режима.
Опции
Пример
nodeConfig.addTech([ require('enb/techs/symlink'), {
fileTarget: '?.css',
symlinkTarget: '_?.css'
} ]);
С версии 0.8 технологии рекомендуется писать с использованием хэлпера BuildFlow
.
Исходный код хэлпера: https://github.com/enb/enb/blob/master/lib/build-flow.js
В данном руководстве охвачены не все возможности BuildFlow
. Полный перечень методов с описанием находится
в JSDoc файла build-flow.js
.
Цель технологии — собирать таргет в ноде. Например, технология css
может собрать index.css
в ноде pages/index
на основе css
-файлов по уровням переопределения.
Каждая технология умеет принимать настройки.
Хэлпер BuildFlow
способствует тому, чтобы максимальное количество параметров было настраиваемым.
Технологии могут использовать результат выполнения других технологий. Например, список исходных css
-файлов
строится с помощью технологии files
.
В общем случае, технологии создавать несложно. Бывают необычные ситуации. В этом руководстве я постараюсь охватить и такие случаи.
В общем случае технология для склеивания файлов по нужному суффиксу выглядит следующим образом:
module.exports = require('enb/lib/build-flow').create() // Создаем инстанцию BuildFlow
.name('js') // Выбираем имя для технологии
.target('target', '?.js') // Имя опции для задания имени результирующего файла и значение по умолчанию
.useFileList('js') // Указываем, какие суффиксы нас интересуют при сборке
.justJoinFilesWithComments() // Еще один хэлпер. Склеивает результат, обрамляя комментариями вида /* ... */
// в которых указывается путь к исходному файлу, из которого был сформирован фрагмент.
.createTech(); // Создаем технологию с помощью хэлпера
Этот пример, конечно очень общий и слишком упрощенный.
Рассмотрим аналог этой технологии без использования justJoinFilesWithComments
:
var Vow = require('vow'); // Используемая в ENB библиотека промисов
var vowFs = require('vow-fs'); // Работа с файловой системой на основе Vow
module.exports = require('enb/lib/build-flow').create()
.name('js')
.target('target', '?.js')
.useFileList('js')
.builder(function(jsFiles) { // Будем возвращать промис, чтобы ENB ждал выполнения асинхронной технологии
var node = this.node; // Сохраняем ссылку на инстанцию класса `Node`.
return Vow.all(jsFiles.map(function(file) { // Ждем выполнения всех промисов
return vowFs.read(file.fullname, 'utf8').then(function(data) { // Читаем каждый исходный файл
var filename = node.relativePath(file.fullname); // Получаем путь относительно ноды
// Строим фрагменты из содержимого исходных файлов
return '/* begin: ' + filename + ' *' + '/\n' + data + '\n/* end: ' + filename + ' *' + '/';
});
})).then(function(contents) { // Получили результат обработки всех исходных файлов
return contents.join('\n'); // Объединяем полученные фрагменты с помощью перевода строки
});
})
.createTech();
Так как мы использовали метод useFileList
, в builder
пришел аргумент со списком файлов по указанному суффиксу.
Каждый use
-метод добавляет аргумент в builder
. Тип и содержимое аргументов зависят от того, какой use
-метод был
использован.
Добавим к получившейся технологии файлы интернационализации:
var Vow = require('vow'); // Используемая в ENB библиотека промисов
var vowFs = require('vow-fs'); // Работа с файловой системой на основе Vow
module.exports = require('enb/lib/build-flow').create()
.name('js')
.target('target', '?.js')
.defineRequiredOption('lang') // Определяем обязательную опцию lang для задания языка
.useFileList('js')
.useSourceText('allLangTarget', '?.lang.all.js') // Подключаем общую для всех языков интернационализацию,
// используя метод useSourceText, который добавляет в
// builder содержимое указанного файла в виде аргумента
.useSourceText('langTarget', '?.lang.{lang}.js') // Подключаем кейсеты конкретного языка;
// здесь используется значение опции lang для того,
// чтобы сформировать значение по умолчанию
.builder(function(jsFiles, allLangText, langText) {
var node = this.node;
return Vow.all(jsFiles.map(function(file) {
return vowFs.read(file.fullname, 'utf8').then(function(data) {
var filename = node.relativePath(file.fullname);
return '/* begin: ' + filename + ' *' + '/\n' + data + '\n/* end: ' + filename + ' *' + '/';
});
})).then(function(contents) {
return contents
.concat([allLangText, langText]) // Добавляем фрагменты содержимого файлов интернационализации
.join('\n');
});
})
.createTech();
Рассмотрим готовый пример:
// В данном примере строится локализованный priv.js
module.exports = require('enb/lib/build-flow').create()
.name('priv-js-i18n')
.target('target', '?.{lang}.priv.js')
.defineRequiredOption('lang')
// Все эти цели подготавливаются другими технологиями:
.useSourceFilename('allLangTarget', '?.lang.all.js') // Устанавливаем зависимость от имени файла
// общей интернационализации
.useSourceFilename('langTarget', '?.lang.{lang}.js') // Устанавливаем зависимость от имени файла
// конкретного языка
.useSourceFilename('privJsTarget', '?.priv.js') // Устанавливаем зависимость от имени файла
// priv-js файла
.justJoinFilesWithComments() // Пользуемся хэлпером для склеивания
.createTech();
Реализуем склеивание без хэлпера:
module.exports = require('enb/lib/build-flow').create()
.name('priv-js-i18n')
.target('target', '?.{lang}.priv.js')
.defineRequiredOption('lang')
.useSourceFilename('allLangTarget', '?.lang.all.js')
.useSourceFilename('langTarget', '?.lang.{lang}.js')
.useSourceFilename('privJsTarget', '?.priv.js')
.builder(function(allLangFilename, langFilename, privJsFilename) {
var node = this.node;
// Перебираем исходные файлы
return Vow.all([allLangFilename, langFilename, privJsFilename].map(function(absoluteFilename) {
// Читаем каждый исходный файл
return vowFs.read(absoluteFilename, 'utf8').then(function(data) {
// Получаем относительный путь к файлу
var filename = node.relativePath(absoluteFilename);
// Формируем фрагмент
return '/* begin: ' + filename + ' *' + '/\n' + data + '\n/* end: ' + filename + ' *' + '/';
});
})).then(function(contents) {
return contents.join('\n'); // Склеиваем фрагменты
});
})
.createTech();
Например, нам надо добавить модульную систему в начало какого-нибудь файла и сохранить результат под новым именем:
var vowFs = require('vow-fs'); // Подключаем модуль для работы с файловой системой
var path = require('path'); // Подключаем утилиты работу с путями
module.exports = require('enb/lib/build-flow').create()
.name('prepend-modules')
.target('target', '?.js')
.defineRequiredOption('source') // Указываем обязательную опцию
.useSourceText('source', '?') // Устанавливаем зависимость от содержимого цели, задаваемой опцией source
.needRebuild(function(cache) { // Указываем дополнительную проверку кэша
// В данном случае модульная система не находится в исходных уровнях переопределения,
// но ее можно найти в пакете ym; для того, чтобы пересборка правильно работало в случае
// изменения содержимого файла modules.js, добавляем проверку
this._modulesFile = path.join(__dirname, '..', 'node_modules', 'ym', 'modules.js'); // Формируем путь
return cache.needRebuildFile( // Проверяем, изменился ли файл
'modules-file', // Ключ для кэширования данных о файле; должен быть уникален в рамках технологии
this._modulesFile // Путь к файлу, для которого необходимо проверить кэш
);
})
.saveCache(function(cache) { // Сохраняем в кэш данные об использованном файле
cache.cacheFileInfo( // Сохраняем в кэш информацию о файле
'modules-file', // Ключ для кэширования данных о файле; должен быть уникален в рамках технологии
this._modulesFile // Путь к файлу, для которого необходимо проверить кэш
);
})
.builder(function(preTargetSource) {
// Считываем содержимое файла модульной системы
return vowFs.read(this._modulesFile, 'utf8').then(function(modulesRes) {
return modulesRes + preTargetSource; // Объединяем результаты
});
})
.createTech();
Время от времени возникают ситуации, когда надо немного дополнить существующие технологии.
Например, нам нравится, как работает технология css
:
module.exports = require('enb/lib/build-flow').create()
.name('css')
.target('target', '?.css')
.useFileList('css')
.builder(function(cssFiles) {
// ...
})
.methods({
// ...
})
.createTech();
В каждой технологии, сделанной с помощью BuildFlow
, есть метод buildFlow()
, который можно вызвать для того,
чтобы создать новую технологию на основе функционала существующей.
В какой-то момент нам понадобилось вместе с суффиксами css
собирать еще и light.css
.
Для этого надо написать новую технологию, заимствуя функционал старой:
module.exports = require('enb/techs/css').buildFlow()
.name('css-light') // Изменяем имя
.useFileList(['css', 'light.css']) // Изменяем нужные параметры
.createTech();
Каждой технологии в init
приходит инстанция ноды, для которой необходимо собирать таргеты.
Через ноду технология взаимодействует с процессом сборки.
Основные методы класса Node:
// Возвращает имя таргета ноды без суффикса. Например, для ноды 'pages/index' результат — index.
String Node::getTargetName()
// Возвращает имя таргета ноды с суффиксом. Например, для ноды 'pages/index' с суффиксом 'js' результат — 'index.js'.
String Node::getTargetName(String suffix)
// Демаскирует имя таргета ноды. Например, для ноды 'pages/index' и maskedTargetName='?.css', результат — 'index.css'.
String Node::unmaskTargetName(String maskedTargetName)
// Возвращает абсолютный путь к таргету.
String Node::resolvePath(String targetName)
Пример
var fs = require('fs');
fs.writeFileSync(this.node.resolvePath(this.node.getTargetName('js')), 'alert("Hello World!");', 'utf8');
// Оповещает ноду о том, что таргет собран. Опционально принимает результат сборки.
// Результатом может быть любой объект, который может быть полезен другим технологиям для продолжения сборки.
undefined Node::resolveTarget(String targetName[, Object result])
Примеры
// #1
this.node.resolveTarget('index.css');
// #2 Получаем имя таргета динамически с помощью суффикса.
this.node.resolveTarget(this.node.getTargetName('css'));
// #3 Получаем имя таргета путем демаскирования таргета.
this.node.resolveTarget(this.node.unmaskTargetName('?.css'));
// #4 Передаем значение.
var target = this.node.unmaskTargetName('?.deps.js'),
targetPath = this.node.resolvePath(target);
delete require.cache[targetPath]; // Избавляемся от кэширования в nodejs.
this.node.resolveTarget(target, require(targetPath));
// Оповещает ноду о том, что таргет не может быть собран из-за ошибки.
undefined Node::rejectTarget(String targetName, Error error)
Примеры
// #1
this.node.rejectTarget('index.css', new Error('Could not find CSS Tools.'));
// #2 Получаем имя таргета динамически с помощью суффикса.
this.node.rejectTarget(this.node.getTargetName('css'), new Error('Could not find CSS Tools.'));
// Требует у ноды таргеты для дальнейшей сборки, возвращает промис.
// Промис выполняется, возвращая массив результатов, которыми резолвились требуемые таргеты.
// ВАЖНО: Не все технологии резолвят таргеты с результатом.
// В данный момент резолвят с результатом технологии: levels, deps*, files.
Promise(Object[]) Node::requireSources(String[] targetNames)
Пример
Например, нам надо объединить в один файл index.css
и index.ie.css
и записать в index.all.css
.
var vowFs = require('vow-fs');
// ...
build: function() {
var _this = this;
return this.node.requireSources(['index.css', 'index.ie.css']).then(function() {
return Vow.all([vowFs.read(_this.node.resolvePath('index.css'), 'utf8'), vowFs.read(_this.node.resolvePath('index.ie.css'), 'utf8')]).then(function(res) {
return vowFs.write(_this.node.resolvePath('index.all.css'), res.join('\n'), 'utf8').then(function() {
_this.node.resolveTarget('index.all.css');
});
});
});
}
// ...
// Возвращает относительный путь к таргету относительно ноды.
String Node::relativePath(String targetName)
// Возвращает полный путь к папке ноды.
String Node::getDir()
// Возвращает полный путь к корневой папке проекта.
String Node::getRootDir()
// Возвращает инстанцию логгера для ноды.
Logger Node::getLogger()
Пример
this.node.getLogger().log('Hello World');
// Возвращает инстанцию кэша для таргета ноды.
Cache Node::getNodeCache(String targetName)
Кэширование необходимо для того, чтобы избегать повторной сборки файлов, для которых сборка не требуется. Кэшируется время изменения исходных и конечных файлов после окончания сборки каждой технологии. Логика кэширования реализуется в каждой технологии индивидуально для максимальной гибкости.
С помощью методов Boolean needRebuildFile(String cacheKey, String filePath)
и Boolean needRebuildFileList(String cacheKey, FileInfo[] files)
производится валидация кэша.
С помощью методов undefined cacheFileInfo(String cacheKey, String filePath)
и undefined cacheFileList(String cacheKey, FileInfo[] files)
производится сохранение информации о файлах в кэш.
Набор ресурсов, которые могут быть использованы в технологиях:
Пример
Контент файла some-processor.js
:
module.exports = function(arg1, arg2) {
var res = null;
// Здесь какая-то нагруженная работа, возможно с использованием промисов
return res;
}
В технологии:
var jobQueue = this.node.getSharedResources().jobQueue;
// Выполнить таску в отдельном процессе, возвращается промис с результатом
return jobQueue.push(require.resolve('./path/to/processor'), arg1, arg2);
FAQs
Faster BEM/BEViS assembler
The npm package enb receives a total of 75 weekly downloads. As such, enb popularity was classified as not popular.
We found that enb demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
Research
Security News
Socket researchers have discovered multiple malicious npm packages targeting Solana private keys, abusing Gmail to exfiltrate the data and drain Solana wallets.
Security News
PEP 770 proposes adding SBOM support to Python packages to improve transparency and catch hidden non-Python dependencies that security tools often miss.