3

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.?

Flo Schild
  • 5,104
  • 4
  • 40
  • 55
  • Read about dynamic import https://webpack.js.org/guides/code-splitting/#dynamic-imports – ArtemSky Feb 06 '18 at 10:33
  • But then we would have to use webpack. I have nothing against but I am not the only one taking that decision. I can always propose it though. But would that be mandatory? And could it cause compilation problems? – Flo Schild Feb 06 '18 at 10:34
  • 1
    Anyway you can't do this without webpack / rollup for now) You can try zero config software like Parcel, if you don't wanna write your own configuration – ArtemSky Feb 06 '18 at 10:38

0 Answers0