Архитектура плагинов

Архитектура плагинов Day.js построена вокруг принципа минимального ядра, которое не содержит избыточной функциональности, а расширяется через подключаемые модули. Основная идея заключается в том, что каждый плагин представляет собой функцию, которая модифицирует поведение экземпляра Day.js или его прототипа.

Базовый механизм подключения реализуется через метод расширения:

dayjs.extend(plugin)

При вызове extend плагин регистрируется внутри ядра и получает доступ к внутреннему контексту библиотеки. Это позволяет добавлять новые методы, изменять парсинг, форматирование и поведение вычислений дат.


Структура плагина

Плагин в Day.js представляет собой функцию с фиксированным контрактом. Типовая форма выглядит следующим образом:

function plugin(option, dayjsClass, dayjsFactory) {
  // расширение прототипа
}

Передаваемые параметры:

  • option — конфигурация плагина (если предусмотрена)
  • dayjsClass — конструктор Day.js (прототип экземпляров)
  • dayjsFactory — основная фабрика создания объектов

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


Механизм расширения прототипа

Основной способ добавления функциональности — модификация прототипа Day.js. Это позволяет всем экземплярам автоматически получать новые методы.

Пример добавления метода:

function examplePlugin(option, dayjsClass) {
  dayjsClass.prototype.isExample = function () {
    return this.format() === 'example'
  }
}

dayjs.extend(examplePlugin)

После подключения каждый объект Day.js получает метод isExample.

Особенность архитектуры заключается в том, что расширение происходит один раз на уровне конструктора, а не на каждом экземпляре, что снижает накладные расходы.


Порядок инициализации плагинов

Плагины применяются последовательно в порядке регистрации. Это влияет на итоговое состояние API.

dayjs.extend(pluginA)
dayjs.extend(pluginB)

Если оба плагина изменяют один и тот же метод, результат зависит от очередности подключения. Такая модель делает систему детерминированной, но требует контроля зависимостей между расширениями.

Некоторые плагины требуют предварительного подключения других модулей. Например, расширения работы с временными зонами опираются на базовые механизмы UTC.


Расширение через статические методы

Помимо прототипа экземпляров, плагины могут добавлять статические методы:

function staticPlugin(option, dayjsClass, dayjsFactory) {
  dayjsFactory.customParse = function (input) {
    return dayjsFactory(input)
  }
}

dayjs.extend(staticPlugin)

Статические методы позволяют работать с библиотекой как с фабрикой, расширяя возможности создания и преобразования объектов.


Модель неизменяемости и плагины

Day.js использует иммутабельную модель: каждый вызов метода возвращает новый экземпляр. Плагины обязаны соблюдать это правило. Методы, добавляемые через плагины, не должны изменять текущий объект.

dayjs().add(1, 'day') // возвращает новый объект

Плагин должен сохранять эту семантику, иначе нарушается предсказуемость цепочек вызовов.


Примеры типовых расширений

Работа с UTC

Плагин UTC изменяет поведение создания объектов, добавляя поддержку координированного времени:

  • переопределение методов парсинга
  • добавление флага UTC
  • модификация арифметики дат

Форматирование с расширенным синтаксисом

Плагин advancedFormat добавляет дополнительные токены форматирования:

  • порядковые числа
  • расширенные представления месяцев и дней
  • локализованные шаблоны

Относительное время

Плагин relativeTime вводит метод вычисления разницы между датами в человекочитаемом виде:

  • секунды
  • минуты
  • часы
  • дни

Реализация основана на сравнении временных меток и наборе правил округления.


Изоляция плагинов

Каждый плагин действует в рамках общего пространства Day.js, но не имеет собственного изолированного контекста. Это означает:

  • отсутствует sandbox-модель
  • все изменения применяются глобально к экземплярам
  • возможны конфликты при перекрытии методов

Для минимизации конфликтов используется соглашение о неймспейсах методов и строгая модульность.


Взаимодействие плагинов между собой

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

Некоторые плагины проверяют наличие других расширений через наличие методов:

if (!dayjs().utc) {
  throw new Error('UTC plugin required')
}

Влияние архитектуры на tree-shaking

Модульная структура Day.js позволяет подключать только необходимые плагины, что положительно влияет на размер итогового бандла. Каждый плагин является независимым модулем, импортируемым отдельно.

import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'

dayjs.extend(utc)

Такой подход снижает количество неиспользуемого кода и улучшает производительность загрузки.


Расширение парсинга и форматирования

Часть плагинов работает не только с прототипом, но и с внутренними функциями парсинга:

  • добавление новых форматов входных строк
  • расширение токенов форматирования
  • изменение логики разбора дат

Это достигается через модификацию внутренних фабрик, передаваемых в плагин как аргументы.


Ограничения архитектуры плагинов

Несмотря на гибкость, модель имеет ряд ограничений:

  • отсутствие динамического отключения плагинов после регистрации
  • глобальное влияние на все экземпляры
  • необходимость строгого контроля порядка подключения
  • невозможность частичной активации внутри отдельных объектов

Эти ограничения компенсируются простотой реализации и предсказуемостью поведения системы.