I am working on a JavaScript i18n library which localize dates (among other types and objects).
It is currently relying on moment.js
, which is defined as a peerDependency (localization is one of the features but not the only one, it might not be used)
// package.json
{
"name": "my-i18n-library",
// ...
"scripts": {
// ...
"clean": "rimraf build",
"build": "babel src -d build",
"prepare": "npm run clean && npm run build"
},
"peerDependency": {
"moment": "~2.20.1",
"date-fns": "~1.29.0"
},
// ...
}
// .babelrc
{
"presets": ["env", "stage-1", "react"]
}
Basically something like (a bit more error-proof but I simplified the logic) :
import Moment from 'moment.js'
import 'moment/min/locales'
class Localizer {
localizeDate(value, locale, displayFormat = "L", parseFormat = "LTS") {
return Moment(date, parseFormat, locale).format(displayFormat);
}
}
Problem is, if moment.js
is a nice work, it is like a backpack of stones, you would not bring it on a 50 miles trail, especially if you only need to localize one date in a whole application. Bandwidth-wise, it's not worth it IMO (actually in a lot of people opinions as well).
So I am considering switching to lighter libraries such as date-fns
, but I figured out an option I think is even better :
what if we could let the other choose which library suits him the most?
I was thinking to define different implementations of library-related localizers, and conditionally import them depending of which peerDependency is installed :
// /Date/DateFnsLocalizer.js
import { parse } from 'date-fns/parse'
import { format } from 'date-fns/format'
class DateFnsLocalizer {
localizeDate(value, locale, displayFormat = "L") {
return format(parse(date), displayFormat, { locale })
}
}
Is that even possible in JavaScript ?
// /Localizer.js
if (isModuleDefined('moment.js')) {
import BaseLocalizer from './Date/MomentLocalizer'
} else if (isModuleDefined('date-fns')) {
import BaseLocalizer './Date/DateFnsLocalizer'
} else if (isModuleDefined('some-other-lib')) {
import BaseLocalizer './Date/SomeOtherLibLocalizer'
} else {
throw new Error('No date library defined! Please install at least one of ["moment.js", "date-fns", "some-other-lib"]')
}
export default class Localizer extends BaseLocalizer
I think "import" statements have to be made as first statements in the file. Maybe using require
instead... (as suggested in ES6: Conditional & Dynamic Import Statements) ? And is it possible to test a module existence without importing it (basically how to code that isModuleDefined()
method?)
I have seen those ones as well :
ES6 variable import name in node.js?
How can I conditionally import an ES6 module?
But as we are currently using babel
to transpile this library, if this architecture is possible, could it cause compilation troubles in other building tools such as webpack
, gulp
, grunt
etc.?