Итак, зачем создавать загрузчик модулей поверх nodejs, когда «требование» уже готово для этого !! это не изобретать велосипед!

моей причиной для создания загрузчика модулей было следующее: -

  1. Мне нужно было объединить скрипты модуля nodejs в один автономный файл скрипта для упрощения распространения/развертывания скриптов вместе с их зависимостями.
  2. Мне нужно было переопределить существующий модуль зависимостей (без внесения каких-либо изменений в модули зависимостей), переопределив путь поиска модуля, чтобы упростить написание прокладок модуля.

но подождите, браузер не охватывает пункт 1?

  1. Нет, и большое нет. browserify не эмулирует систему пути модуля узла, поэтому сценарии на стороне сервера не могут быть объединены с browserify. Например, браузер не сможет запустить ‘__dirname’ или ‘__filename’. также, по моему опыту, браузеру не удалось загрузить упоминания относительного пути без имени файла.

2. В Browserify отсутствует надлежащая система обмена сообщениями об ошибках, если ему не удается загрузить модуль, который «требуется» для определенного файла, он упоминает имя каталога, из которого исходит вызов для загрузки модуля, но никогда не показывает имя файла, который инициализировал вызов для загрузки модуля, в результате отладка затрудняет его.

3. Мне нужно было что-то молниеносно быстрое.

Итак, теперь, когда установлено, что колесо не изобретается заново, давайте разберем, что происходит внутри узла «require».

1. resolves path requested
2. find the possibilities of paths to search for the resolved path
3. finds the file and fetches the file source
4. wraps the source
5. build a module object from the file source containing the meta-data about the file
6. caches the module
7. executes the module and returns the value

Первый вопрос: где nodejs ищет файл, когда инициируется требование, такое как (‘./foo’)?

предполагая, что у нас есть следующая папка и файлы

abc (directory)
 -- def (directory)
 -- -- ghi (directory)
 -- -- - bar.js (file)
 -- -- - foo.js (file)

и bar.js имеет следующий скрипт

require('./foo')

а) начнем с разрешения ./foo из каталога abc/def/ghi/

  1. объединить abc/def/ghi/ с ./foo => abc/def/ghi/./foo
  2. замените любой ‘//’ ‘/./’ на ‘/’, так что abc/def/ghi/./foo => abc/def/ghi/foo
  3. замените любую «строку/../» на «/»

вот простой скрипт для разрешения пути

function resolvePath(path) {
   var tempPath = path.replace(/\/+/g, '/').replace(/\/\.\//g, '/');
   var regex = /(^|\/)(?!\.?\.\/)([^\/]+)\/\.\.\/*/g;
  do {
     path = tempPath;
     tempPath = tempPath.replace(regex, '$1')
  } while (path != tempPath);
   return path.replace(/\.\//g, '').replace(/\/$/g, '');
}
resolvePath('abc/def/ghi/./foo')  // => abc/def/ghi/foo
resolvePath('abc/def/ghi/../foo') // => abc/def/foo

б) теперь, когда мы определили путь, давайте найдем модуль foo

  1. для require('./foo') из каталога 'abc/def/ghi/' разрешенный путь даст нам 'abc/def/ghi/foo', и поскольку разрешенный путь не имеет расширения файла, мы будем искать эти два возможных пути

я) abc/def/ghi/foo.js

ii) abc/def/ghi/foo/index.js

Короче говоря, если метод require вызывается с именем модуля без упоминания расширения, nodejs либо предполагает, что это файл с таким именем, например «foo.js», либо файл «index.js» внутри каталога foo, такой foo/index.js

но что, если в bar.js есть следующий скрипт

require('./foo.js') // the require is called with file extension

разрешение пути даст нам abc/def/ghi/foo.js, а поскольку путь уже имеет расширение, загрузчик модуля напрямую получит исходный код из пути abc/def/ghi/foo.js.

Итак, мы разобрались с поиском пути для относительного упоминания (например, что-то, начинающееся с ./), но как насчет не относительных упоминаний? что, если bar.js инициировал вызов как

require('foo') // < see, no relative mention not ./foo but foo

в этом случае nodejs будет искать внутри папки node_modules

за

  1. node_modules/foo.js
  2. node_modules/foo/index.js

Вау! это было просто.

соединив все это, мы имеем

function getExtension(modulePath) {
    var moduleSegments = modulePath.split('.');
    var ext = moduleSegments[moduleSegments.length - 1];
    if (ext == "js" || ext == "json") {
        return ext;
    }
    return false;
}
function getPossiblePaths (modulePath) {
     if (getExtension(modulePath)) {
           return [modulePath];
     }
     return [
        util.concat(util.resolveRelativePath(modulePath), '.js'),
        util.buildPath(modulePath, 'index.js'),
     ];
}
function resolvePath(path) {
   var tempPath = path.replace(/\/+/g, '/').replace(/\/\.\//g, '/');
   var regex = /(^|\/)(?!\.?\.\/)([^\/]+)\/\.\.\/*/g;
do {
     path = tempPath;
     tempPath = tempPath.replace(regex, '$1')
  } while (path != tempPath);
  return path.replace(/\.\//g, '').replace(/\/$/g, '');
}
getPossiblePaths(resolvePath('abc/def/ghi/./foo'))  
// => [abc/def/ghi/foo.js, abc/def/ghi/foo/index.js]
getPossiblePaths(resolvePath('abc/def/ghi/./foo.js'))
// => [abc/def/ghi/foo.js]
getPossiblePaths(resolvePath('node_modules/foo'))
// => [node_modules/foo.js, node_modules/foo/index.js]