Я — frontend разработчик и в последнее время мне все чаще приходится пользоваться нодой, будь то использование webpack-а для сборки проекта, либо настройка различных gulp тасков. Хоть у меня и нету большого опыта в использование ноды, со временем у меня накопилось три вещи, которые мне хотелось бы улучшить при работе с модулями:
- Избавиться от кучи require-ов в начале каждого файла
- Подгружать модули только тогда, когда они нужны(особенно это актуально для gulp тасков)
- Иметь возможность работать с локальными модулями проекта, как с внешними модулями, то есть вместо, например,
вызова var core = require('../../deep/deep/deep/core/core'), вызывать этот же модуль вот так var core = require('core')
Для решения всех трех задач по отдельности есть уже различные модули и решения, но во-первых, все они имеют, как мне кажется, недостатки, а во-вторых, ни одно из решений не решает все три проблемы вместе.
Например, для подгрузки модулей по требованию(он же lazy load или load on demand), есть модуль gulp-load-plugins. Он решает 1-ую и 2-ую проблему, но не решает 3-юю и имеет еще один недостаток — чтобы подключить модули, нужно в каждом файле, где эти модули нужны, производить инициализацию gulp-load-plugins модуля. Можно, конечно, делать инициализацию в отдельном файле и экспортировать из файла значение, но в таком случае придется подключать этот файл с использованием относительных или абсолютного путей.
Для решения 3-ей проблемы около года назад в npm добавили поддержку так званых локальных модулей. Суть сводится к тому, что
в package.json в dependencies нужно указать в качестве ключей имена модулей, а в значениях — относительные пути к папкам локальных модулей с префиксом «file:», например, вот часть package.json файла:
"dependencies": {
"lodash": "^2.0.0",
"core": "file:deep/deep/deep/core",
"my-other-module": "file:/my-other-module"
}
При этом папки локальных модулей должны быть оформлены, как обычные модули, то есть должны содержать свой package.json и readme.md файлы. После запуска npm i ваши локальные модули будут установлены в папку node_modules, как обычные модули. По мне так это крайне неудобно класть каждый файл проекта в отдельную папку, да еще и заводить на него package.json и readme.md файлы.
Одним словом я не нашел хорошего решения для описанных мною проблем, может плохо искал, но облазив различные форумы и прочитав различные свежие статьи пришел к выводу, что люди до сих пор ищут хорошее решение всех этих проблем.
В итоге я решил написать свое решение, возможно, свой велосипед, о котором и хочу поведать вам. На сколько он хорош или плох судить вам. Итак, позвольте представить вам sp-load. Сразу оговорюсь, префикс sp- не несет в себе никакого сакрального смысла, это всего лишь первые буквы моей фамилии и имени и добавлен не с целью прославить меня, а по причине того, что имена «load», «loader» и прочие были уже заняты.
Итак, вы сделали npm i sp-load -S в своем проекте. Предположим, что вы имеете следующее содержимое package.json файла:
{
"name": "your-project",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"lodash": "^3.10.1",
"sp-load": "^1.0.0"
},
"devDependencies": {
"gulp": "^3.9.0",
"webpack": "^1.12.9"
},
"_localDependencies": {
"core": "./core/core",
"some-module": "./deep/deep/deep/deep/deep/deep/deep/some-module"
}
}
И имеете следующую структуру файлов:
your-project/
node_modules
sp-load/
...
gulp/
...
lodash/
...
webpack/
...
package.json
core/
core.js
deep/
deep/
deep/
deep/
deep/
deep/
deep/
some-module.js
gulpfile.js
index.js
Что вам нужно сделать, чтобы использовать sp-load в простейшем виде? Всего одну вещь, сделать var $ = require('sp-load'); внутри какого-либо файла, например, вот содержимое gulpfile.js:
'use strict';
var $ = require('sp-load'),
webpackConfig = {};
$.gulp.task("webpack", function (callback) {
$.webpack(webpackConfig, function (err, stats) {
callback();
});
});
Содержимое some-module.js:
'use strict';
function someModuleFuction() {
console.log('I'm some module function call!');
}
module.exports = someModuleFuction;
Содержимое core.js:
'use strict';
function coreModuleFuction() {
console.log('I'm core module function call!');
}
module.exports = coreModuleFuction;
Содержимое index.js:
'use strict';
var $ = require('sp-load');
$.someModule();
$.core();
Как вы видите, всё что нужно сделать — подключить sp-load модуль. Он возвращает объект, содержащий список модулей, которые будут подгружены по требованию, то есть модуль будет загружен node-ой при первом обращение по имени модуля, например, $.core().
Также вы, наверное, заметили нестандартное свойство "_localDependencies" в package.json. В этом свойстве вы можете определить список локальных модулей вашего проекта. Ключи объекта — названиям модулей, значения — относительный путь к файлу модуля(путь относительный package.json файла).
Если же вы хотите обращаться к модулям не как к свойствам объекта, а как к переменным, то можете сделать это следующим образом(в примере используется es6 деструктуризация. как включить возможности es6 в nodejs вы можете прочесть в документацие nodejs):
'use strict';
var {someModule, core} = require('sp-load');
someModule();
core();
Или с использованием es5:
'use strict';
var $ = require('sp-load'),
someModule = $.someModule,
core = $.core;
someModule();
core();
В обоих этих примерах, модули someModule и core буду подгружены при присвоение, если же вы хотите, чтобы они были подгружены в момент первого их использования(то есть on demand), то обращайтесь к модулям, как к свойствам объекта $.
Это было простейшее использование sp-load, без каких-либо конфигураций, за исключением использования свойства "_localDependencies" в package.json. Теперь же хочу показать какие настройки поддерживает sp-load. Для того, чтобы конфигурировать sp-load, необходимо добавить свойство "_sp-load" в package.json. Ниже приведен пример package.json файла, в котором указаны все возможные настройки с комментариями о назначение каждой из них:
{
"name": "your-project",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"lodash": "^3.10.1",
"sp-load": "^1.0.0"
},
"devDependencies": {
"gulp": "^3.9.0",
"webpack": "^1.12.9"
},
"_localDependencies": {
"core": "./core/core",
"some-module": "./deep/deep/deep/deep/deep/deep/deep/some-module"
},
"_sp-load": {
/*
если значение true, имена модулей будут в виде camel case.
например, вместо $['some-module'] будет $.someModule.
дефолтное значение - true.
*/
"camelizing": false,
/*
эта настройка отвечает за переименование имен модулей. например, вместо $.lodash модуль будет доступен
как $._
*/
"renaming": {
"lodash": "_",
"gulp": "supergulp"
},
/*
если вы хотите заменить часть названия модулей, используйте эту настройку. ключи - регулярные выражения,
значения - строки, на которые будет произведена замена. наиболее частый случай использования - gulp
плагины, большинство из которых начинаются с префикса gulp-, например, gulp-concat, а вы хотите обращаться
к нему как $.concat вместо $.gulpConcat.
*/
"replacing": {
"/^gulp-/": ""
}
}
}
Если же вы не хотите засорять package.json файл, то поместите настройки sp-load и список локальных модулей в файл _sp-load.json, который должен находиться в той же папке, где и package.json, то есть:
yourProject/
package.json
_sp-load.json
Вот пример содержимого _sp-load.json файла:
{
"_localDependencies": {
"core": "./core/core",
"some-module": "./deep/deep/deep/deep/deep/deep/deep/some-module"
},
"_sp-load": {
"camelizing": false,
"renaming": {
"lodash": "_",
"gulp": "supergulp"
},
"replacing": {
"/^gulp-/": ""
}
}
}
И последнее, о чем еще не упомянул. Когда вы делаете $ = require('sp-load');, объект $ содержит свойство "_spModulesList" в своем прототипе. Это свойство содержит объект, где ключи — имена модулей, а значения — абсолютный путь к файлу модуля. Вот пример содержимого этого объекта:
{
"lodash": "lodash",
"sp-load": "sp-load",
"gulp": "gulp",
"webpack": "webpack",
"core": "D://your-project//core//core.js",
"some-module": "D://your-project//deep//deep//deep//deep//deep//deep//deep//some-module.js"
}
Для чего это может пригодиться? Например, при использование System.js загрузчика.
Пожалуй, это всё. Перед тем, как опубликовать модуль на npmjs.com, протестировал его, но в реальном проекте ещё его не использовал, поэтому, если будут какие-либо ошибки — буду рад, если сообщите о них.
Ссылка на сам модуль: sp-load.
P.S.: Может, кто-нибудь подскажет, как удалить опубликованный модуль из npmjs.com? Нигде не нашел, как это сделать, а npm unpublish удаляет модуль, но при последующем npm publish приходится увеличивать версию модуля т.к. npm ругается, что текущая версия уже зарегистрирована.
Автор: pavel06081991