Стратегия миграции определяет, как перейти с одной версии timeago.js на другую (или на другую библиотеку) с минимальным риском для production и минимальными затратами на изменение кода.
Подходит для небольших проектов с ограниченным использованием timeago.js.
chore/timeago-upgrade.git checkout -b chore/timeago-upgrade
npm install timeago.js@^4.0.0
npx tsc --noEmit
npm test
git commit -am "chore: upgrade timeago.js to v4"
Риск: При наличии breaking changes все проблемы выявляются сразу.
Для больших проектов, где timeago.js используется во многих местах.
// src/lib/timeago.ts — адаптер
import { format as _format, render as _render, cancel as _cancel, register } from 'timeago.js';
export type DateInput = Date | string | number;
export type Locale = 'ru' | 'en_US' | 'de' | 'fr';
// Обёртки фиксируют совместимый API для всего приложения
export const format = (date: DateInput, locale: Locale = 'ru'): string => {
return _format(date, locale);
};
export const render = (el: Element | Element[], locale: Locale = 'ru'): void => {
_render(el, locale);
};
export const cancel = (el?: Element | Element[]): void => {
_cancel(el);
};
export { register };
При обновлении изменяется только адаптер. Остальной код не трогается.
Для смены библиотеки (например, переход с timeago.js на
Intl.RelativeTimeFormat):
// Фаза 1: Добавить новую реализацию под флагом
const USE_NATIVE = process.env.FEATURE_NATIVE_RELATIVE_TIME === 'true';
function formatDate(date: Date, locale = 'ru'): string {
if (USE_NATIVE) {
return formatNative(date, locale); // Новая реализация
}
return format(date, locale); // timeago.js
}
Постепенно включать флаг для части пользователей, наблюдать за метриками, затем переключить полностью.
// Обе реализации запускаются одновременно, результаты сравниваются
function dualFormat(date: Date, locale = 'ru'): string {
const v3Result = oldFormat(date, locale);
const v4Result = newFormat(date, locale);
if (v3Result !== v4Result) {
console.warn('Format mismatch:', { v3: v3Result, v4: v4Result, date, locale });
}
return v3Result; // В production используется старый результат
}
Обнаружить все расхождения до переключения.
Если кастомные локали написаны для старого API, их нужно адаптировать:
// Старый формат — проверить, что возвращает именно кортеж
const oldLocale = (n, i) => {
const forms = [...];
return forms[i]; // Возможно, возвращал строку в старых версиях
};
// Новый формат — явный кортеж
const newLocale = (n, i): [string, string] => {
const forms: [string, string][] = [...];
return forms[i] ?? ['давно', 'скоро'];
};
// До: CommonJS стиль или старый путь
const { format } = require('timeago.js');
import ru from 'timeago.js/locales/ru'; // Старый путь
// После: именованный ESM импорт
import { format, register } from 'timeago.js';
import ru from 'timeago.js/esm/lang/ru'; // Новый путь
// До: использование @types/timeago.js или самописные декларации
// @types/timeago.js — удалить
// Самописный .d.ts — заменить
// После: встроенные типы
import type { LocaleFunc, FormatOptions } from 'timeago.js';
// .eslintrc.js — запретить устаревшие паттерны
module.exports = {
rules: {
'no-restricted-imports': ['error', {
name: 'timeago.js',
importNames: ['default'],
message: 'Используйте именованные импорты: import { format } from "timeago.js"',
}],
},
};