Миграция на date-fns в крупных проектах редко
представляет собой одномоментную замену одной библиотеки другой. Обычно
процесс затрагивает множество слоёв: утилиты форматирования, парсинг
дат, работу с таймзонами, бизнес-логику и тестовые сценарии.
Автоматизация в этом контексте направлена на снижение количества ручных
правок и минимизацию риска регрессий.
Ключевые стратегии автоматизации сводятся к трём направлениям:
Каждое направление решает свою задачу и часто используется совместно.
Перед автоматизацией требуется формализовать зоны, где используется работа с датами. В реальных проектах встречаются разные паттерны:
moment() и цепочки вызовов (add,
subtract, format)new Date() с ручными вычислениямиТипичная задача — выявить все места, где требуется замена на функции
date-fns, такие как:
formatparseaddDays, subDaysdifferenceInDaysisAfter, isBeforeАвтоматизация невозможна без полного понимания покрытия.
Наиболее мощный инструмент автоматизации — codemod-скрипты на базе
jscodeshift. Они позволяют модифицировать AST
JavaScript-кода и выполнять структурные замены.
Пример базового кодмода, заменяющего moment().add на
date-fns add:
export default function transformer(file, api) {
const j = api.jscodeshift;
const root = j(file.source);
root
.find(j.CallExpression, {
callee: {
object: {
callee: { name: 'moment' }
},
property: { name: 'add' }
}
})
.forEach(path => {
const args = path.node.arguments;
j(path).replaceWith(
j.callEx * pression(
j.identifier('add'),
[
j.callEx * pression(j.identifier('moment'), []),
...args
]
)
);
});
return root.toSource();
}
В реальных миграциях такие трансформации усложняются:
Одним из самых сложных этапов является миграция цепочечных API
moment в функциональный стиль date-fns.
Moment.js:
moment(date).add(2, 'days').format('yyyy-MM-DD');
Date-fns:
import { addDays, format } from 'date-fns';
format(addDays(date, 2), 'yyyy-MM-dd');
Автоматизация такой трансформации требует перестройки AST в несколько шагов:
add, subtractformatОтдельная задача — управление импортами. При миграции важно не только заменить вызовы, но и корректно добавить нужные функции:
import { addDays, format, parseISO } from 'date-fns';
Codemod может автоматически агрегировать используемые функции:
Дополнительно применяются правила сортировки и дедупликации импортов.
Одной из проблем миграции является несовместимость форматов.
moment использует собственную систему токенов, тогда как
date-fns требует стандартизированных шаблонов.
Пример автоматической замены:
| Moment | date-fns |
|---|---|
YYYY |
yyyy |
DD |
dd |
HH |
HH |
mm |
mm |
Codemod может выполнять строковые преобразования:
function convertFormatTokens(formatStr) {
return formatStr
.replace(/YYYY/g, 'yyyy')
.replace(/DD/g, 'dd')
.replace(/mm/g, 'MM');
}
При масштабной миграции важно учитывать контексты, где строка форматирования может быть динамической.
date-fns требует явного подключения локалей, в отличие
от некоторых старых библиотек.
Автоматизация включает:
Пример:
import { ru } from 'date-fns/locale';
и использование:
format(date, 'dd MMMM yyyy', { locale: ru });
Codemod может автоматически определять региональные параметры, если они зашиты в конфигурационных файлах или переменных окружения.
После первичной автоматической трансформации требуется предотвращение возврата к старым паттернам.
Для этого используются кастомные ESLint-правила:
moment()new Date() в бизнес-логикеПример правила:
module.exports = {
create(context) {
return {
CallEx * pression(node) {
if (node.callee.name === 'moment') {
context.report({
node,
message: 'Использование moment запрещено, используйте date-fns'
});
}
}
};
}
};
ESLint становится инструментом поддержания консистентности после миграции.
В больших системах миграция редко происходит мгновенно. Для снижения риска вводится compatibility layer:
import { addDays as dfAddDays } from 'date-fns';
export function addDays(date, amount) {
return dfAddDays(date, amount);
}
Преимущества подхода:
Автоматизация может генерировать такие обёртки на основе анализа использования API.
Автоматизация часто строится по модульному принципу:
Каждый этап сопровождается отдельными codemod-скриптами.
Пример стратегии:
Такой порядок снижает количество конфликтов и упрощает отладку.
Любая автоматизация требует валидации корректности преобразований.
Используются следующие методы:
Пример теста:
expect(format(addDays(new Date('2020-01-01'), 2), 'yyyy-MM-dd'))
.toBe('2020-01-03');
Также применяются тестовые наборы с историческими данными, чтобы исключить дрейф поведения.
Автоматизация миграции становится частью сборочного процесса:
Типичный сценарий:
Таким образом миграция превращается из разовой операции в контролируемый процесс.
В проектах с сотнями тысяч строк кода появляются дополнительные сложности:
Для решения применяются:
Автоматизация в таких условиях становится не скриптом, а системой трансформации кода, включающей анализ, изменение и проверку результата на каждом этапе.