Skip to content

Repository files navigation

@e22m4u/js-autoload

npm version license

Модуль автозагрузки для вызова функций из файлов указанной директории.

  • Не требует настройки перед использованием (zero configuration).
  • Обход файлов строго в указанной директории (без рекурсии).
  • Передача любого количества аргументов в вызываемые функции.
  • Гарантированный порядок вызова благодаря алфавитно-числовой сортировке.
  • Поддержка асинхронных функций с ожиданием завершения выполнения.
  • Вызов только export default функций (классы и другие типы пропускаются).
  • Автоматическая фильтрация тестовых файлов (*.test.js, *.spec.js).
  • Быстрая остановка выполнения (fail-fast) при возникновении ошибок.

Содержание

Мотивация

При разработке серверных приложений типична ситуация, когда точка входа превращается в длинный список ручных импортов и вызовов подключения источников данных, регистрации моделей, объявления маршрутов и т.п. Каждый новый файл требует правки главного модуля, что увеличивает риск ошибки и усложняет поддержку кода по мере роста проекта.

import {defineRoleModel} from './models/role-model.js';
import {defineUserModel} from './models/user-model.js';
import {defineRoleRoutes} from './routes/role-routes.js';
import {defineUserRoutes} from './routes/user-routes.js';

// const orm = ...
// const router = ...

defineRoleModel(orm);
defineUserModel(orm);
defineRoleRoutes(router);
defineUserRoutes(router);

Модуль избавляет от необходимости перечислять подключаемые файлы вручную. Достаточно поместить файлы в отдельную директорию, а функция invokeFromDir найдет их, импортирует и вызовет функции, экспортированные по умолчанию, в предсказуемом порядке.

import {invokeFromDir} from '@e22m4u/js-autoload';

// const orm = ...
// const router = ...

await invokeFromDir(`${import.meta.dirname}/models`, orm);
await invokeFromDir(`${import.meta.dirname}/routes`, router);

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

Установка

npm install @e22m4u/js-autoload

Модуль поддерживает ESM и CommonJS стандарты.

ESM

import {invokeFromDir} from '@e22m4u/js-autoload';

CommonJS

const {invokeFromDir} = require('@e22m4u/js-autoload');

Использование

Функция invokeFromDir(dirPath, ...args) обходит указанную директорию, находит JavaScript-файлы, импортирует их и вызывает содержащиеся в них функции, если они переданы как экспорт по умолчанию.

  • Экспорт по умолчанию для ESM export default function() { ... }
  • Экспорт по умолчанию для CJS module.exports = function() { ... }

Примечание. В примерах ниже используются числовые префиксы (01-, 02- и т.д.) исключительно для наглядной демонстрации алфавитно-числового порядка вызова файлов. На практике использовать числовые префиксы не рекомендуется, так как при добавлении нового файла в начало или середину списка может потребоваться переименование нескольких соседних файлов для сохранения нужного порядка. Модуль ориентирован прежде всего на управление порядком вызова групп файлов, где каждая группа обрабатывается отдельным вызовом invokeFromDir. Например, сначала модели, затем маршруты, затем middleware и т.д. Порядок внутри одной группы обычно не имеет значения, если файлы не зависят друг от друга.

Пример работы

Приведенный ниже пример предназначен для стандарта ESM. Данное уточнение обусловлено применением оператора await на верхнем уровне модуля. Подобный синтаксис нативно поддерживается ESM режимом, но вызывает синтаксическую ошибку в CommonJS. Реализация аналогичной логики для CommonJS описана в разделе «Поддержка CommonJS».

Структура файлов:

project/
  scripts/
    01-init.js
    02-process.js
  index.js

Содержимое 01-init.js

export default function(context) {
  context.initialized = true;
}

Содержимое 02-process.js

// имитация асинхронного выполнения
export default async function(context) {
  context.status = 'done';
};

Содержимое index.js

import {invokeFromDir} from '@e22m4u/js-autoload';

const appState = {
  initialized: false,
  status: 'pending',
};

await invokeFromDir(`${import.meta.dirname}/scripts`, appState);
// import.meta.dirname доступен только для Node.js 20.11 и выше

console.log(appState); 
// { initialized: true, status: 'done' }

Рекомендуется передавать абсолютный путь до целевой директории (как это показано выше), так как относительный путь вычисляется от места вызова команды node, а не от файла в котором используется данная утилита.

Передача аргументов

Функция invokeFromDir принимает неограниченное количество аргументов после пути к директории. Все переданные аргументы будут отправлены в каждую вызываемую функцию без изменений.

import {invokeFromDir} from '@e22m4u/js-autoload';

// ...
await invokeFromDir('./src/actions', arg1, arg2, arg3);

Поддержка CommonJS

Современный стандарт ESM позволяет применять оператор await на верхнем уровне модуля. Стандарт CommonJS не поддерживает такую возможность. Использование оператора await в корне файла приводит к синтаксической ошибке.

Для обхода описанного ограничения создается асинхронная функция-обертка. Внутри такой функции оператор await работает в штатном режиме. Вся стартовая логика приложения помещается в тело этой функции. Пример реализации подобного подхода приводится ниже.

Структура файлов:

project/
  scripts/
    01-init.js
    02-process.js
  index.js

Содержимое 01-init.js

module.exports = function(context) {
  context.initialized = true;
};

Содержимое 02-process.js

// имитация асинхронного выполнения
module.exports = async function(context) {
  context.status = 'done';
};

Содержимое index.js

const path = require('path');
const {invokeFromDir} = require('@e22m4u/js-autoload');

const appState = {
  initialized: false,
  status: 'pending'
};

async function main() {
  await invokeFromDir(path.join(__dirname, './scripts'), appState);
  console.log(appState);
  // { initialized: true, status: 'done' }
}

main();

Утилита invokeFromDir выполняет исключительно функции, которые экспортированы по умолчанию. При работе со стандартом CommonJS присвоение целевой функции объекту module.exports автоматически трактуется средой выполнения как экспорт по умолчанию. По этой причине подобные функции успешно распознаются и вызываются данной утилитой.

Правила обработки файлов

При вызове функции invokeFromDir применяются следующие правила:

Вложенность
Обрабатываются только файлы на первом уровне указанной директории. Вложенные каталоги игнорируются.

Расширения
Загружаются только файлы с расширениями .js, .mjs и .cjs. Файлы с другими расширениями игнорируются.

Исключения
Файлы, имена которых оканчиваются на .test.js или .spec.js, пропускаются.

Порядок выполнения
Перед выполнением список путей сортируется по алфавиту с учетом числовых значений в строках. Именование файлов с числовыми префиксами (01-*, 02-* и т.д.) гарантирует строгую последовательность вызова.

Проверка экспорта
Выполняются только функции, предоставленные как export default. Именованные экспорты игнорируются. Строки, объекты или другие типы данных пропускаются.

Классы
Если экспортом по умолчанию является класс (ES6 class), он игнорируется и не инстанцируется.

Асинхронность
Если функция возвращает Promise, выполнение приостанавливается до разрешения промиса. Следующий файл будет обработан только после завершения работы предыдущего.

Ошибки
В случае отсутствия указанной директории или возникновения ошибки внутри выполняемой функции, процесс останавливается, и ошибка пробрасывается в вызывающий код.

Тесты

npm run test

Лицензия

MIT

About

Модуль автозагрузки для вызова функций из директории

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages