Загрузчик модулей для node js с поддержкой локальных модулей и загрузки модулей по требованию
Я — frontend разработчик и в последнее время мне все чаще приходится пользоваться нодой, будь то использование webpack-а для сборки проекта, либо настройка различных gulp тасков. Хоть у меня и нету большого опыта в использование ноды, со временем у меня накопилось три вещи, которые мне хотелось бы улучшить при работе с модулями:
Для решения всех трех задач по отдельности есть уже различные модули и решения, но во-первых, все они имеют, как мне кажется, недостатки, а во-вторых, ни одно из решений не решает все три проблемы вместе.
Например, для подгрузки модулей по требованию(он же lazy load или load on demand), есть модуль gulp-load-plugins. Он решает 1-ую и 2-ую проблему, но не решает 3-юю и имеет еще один недостаток — чтобы подключить модули, нужно в каждом файле, где эти модули нужны, производить инициализацию gulp-load-plugins модуля. Можно, конечно, делать инициализацию в отдельном файле и экспортировать из файла значение, но в таком случае придется подключать этот файл с использованием относительных или абсолютного путей.
Для решения 3-ей проблемы около года назад в npm добавили поддержку так званых локальных модулей. Суть сводится к тому, что
в package.json в dependencies нужно указать в качестве ключей имена модулей, а в значениях — относительные пути к папкам локальных модулей с префиксом «file:», например, вот часть package.json файла:
При этом папки локальных модулей должны быть оформлены, как обычные модули, то есть должны содержать свой package.json и readme.md файлы. После запуска npm i ваши локальные модули будут установлены в папку node_modules, как обычные модули. По мне так это крайне неудобно класть каждый файл проекта в отдельную папку, да еще и заводить на него package.json и readme.md файлы.
Одним словом я не нашел хорошего решения для описанных мною проблем, может плохо искал, но облазив различные форумы и прочитав различные свежие статьи пришел к выводу, что люди до сих пор ищут хорошее решение всех этих проблем.
В итоге я решил написать свое решение, возможно, свой велосипед, о котором и хочу поведать вам. На сколько он хорош или плох судить вам. Итак, позвольте представить вам sp-load. Сразу оговорюсь, префикс sp- не несет в себе никакого сакрального смысла, это всего лишь первые буквы моей фамилии и имени и добавлен не с целью прославить меня, а по причине того, что имена «load», «loader» и прочие были уже заняты.
Итак, вы сделали npm i sp-load -S в своем проекте. Предположим, что вы имеете следующее содержимое package.json файла:
И имеете следующую структуру файлов:
Что вам нужно сделать, чтобы использовать sp-load в простейшем виде? Всего одну вещь, сделать var $ = require('sp-load'); внутри какого-либо файла, например, вот содержимое gulpfile.js:
Содержимое some-module.js:
Содержимое core.js:
Содержимое index.js:
Как вы видите, всё что нужно сделать — подключить sp-load модуль. Он возвращает объект, содержащий список модулей, которые будут подгружены по требованию, то есть модуль будет загружен node-ой при первом обращение по имени модуля, например, $.core().
Также вы, наверное, заметили нестандартное свойство "_localDependencies" в package.json. В этом свойстве вы можете определить список локальных модулей вашего проекта. Ключи объекта — названиям модулей, значения — относительный путь к файлу модуля(путь относительный package.json файла).
Если же вы хотите обращаться к модулям не как к свойствам объекта, а как к переменным, то можете сделать это следующим образом(в примере используется es6 деструктуризация. как включить возможности es6 в nodejs вы можете прочесть в документацие nodejs):
Или с использованием es5:
В обоих этих примерах, модули someModule и core буду подгружены при присвоение, если же вы хотите, чтобы они были подгружены в момент первого их использования(то есть on demand), то обращайтесь к модулям, как к свойствам объекта $.
Это было простейшее использование sp-load, без каких-либо конфигураций, за исключением использования свойства "_localDependencies" в package.json. Теперь же хочу показать какие настройки поддерживает sp-load. Для того, чтобы конфигурировать sp-load, необходимо добавить свойство "_sp-load" в package.json. Ниже приведен пример package.json файла, в котором указаны все возможные настройки с комментариями о назначение каждой из них:
Если же вы не хотите засорять package.json файл, то поместите настройки sp-load и список локальных модулей в файл _sp-load.json, который должен находиться в той же папке, где и package.json, то есть:
Вот пример содержимого _sp-load.json файла:
И последнее, о чем еще не упомянул. Когда вы делаете $ = require('sp-load');, объект $ содержит свойство "_spModulesList" в своем прототипе. Это свойство содержит объект, где ключи — имена модулей, а значения — абсолютный путь к файлу модуля. Вот пример содержимого этого объекта:
Для чего это может пригодиться? Например, при использование System.js загрузчика.
Пожалуй, это всё. Перед тем, как опубликовать модуль на npmjs.com, протестировал его, но в реальном проекте ещё его не использовал, поэтому, если будут какие-либо ошибки — буду рад, если сообщите о них.
Ссылка на сам модуль: sp-load.
P.S.: Может, кто-нибудь подскажет, как удалить опубликованный модуль из npmjs.com? Нигде не нашел, как это сделать, а npm unpublish удаляет модуль, но при последующем npm publish приходится увеличивать версию модуля т.к. npm ругается, что текущая версия уже зарегистрирована.
- Избавиться от кучи 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 ругается, что текущая версия уже зарегистрирована.
Comments 17
Only users with full accounts can post comments. Log in, please.