HOWTO: Consuming CJS in a Typescript module
Tags: javascript, typescript, tutorial, beginners
Published: 2022-01-26 12:36:00

At [Plasmo](https://www.plasmo.com/) we leverage Typescript for our web projects.
CJS project like [humanparser](https://github.com/ralyodio/humanparser) would have example like this:

```js
const human = require('humanparser');
const fullName = 'Mr. William R. Hearst, III';
const attrs = human.parseName(fullName);

console.log(attrs);
```

The `require` statement is how we import the `humanparser` module into a CJS codebase. This statement can be translated into TypeScript/ESM 2 ways:

```ts
import * as humanParser from 'humanparser'
```

OR

```ts
import humanParser from 'humanparser'
```

## Which is it?

The answer lies in the source code of the `humanparser` module itself. This is an artifact from the evolution of ES, from ES3-> ES5 -> ES6 and beyond (now ESM). There were a couple of ways a module could be exported. 

The first, commonly used during the transition between ES3 -> ES5 (early nodejs days), was to assign an entry function or object into `module.exports` global:
```js
module.exports = stuff
```
In this example, the `module.exports` global was used to bridge into the `require` statement. Because `module.exports`  represent "everything" that was exported from this module, we must use the import all statement:
```ts
import * as humanParser from 'humanparser'
```
The `*` represents the `module.exports` object.

Another way, which occurred during the transition between ES5->ES6, is to export a `default` property as the entry for your module:
```js
module.exports = {
	default : stuff
}
```
This assigned `stuff` into the `defaults` property exported by the module. The reason why this was done is because in ES6, when doing: 
```ts
import humanParser from 'humanparser'
```

The above code is actually importing the `default` props exported by a module. (The export statement in ES6 would be: `export default stuff`). The import statement above is doing something like this in equivalent CJS code:
```js
const humanParser = require('humanparser').default
```

Now, back to `humanparser`'s source code, their export statement looks like this:

```
const parser = module.exports = {};
parser.parseName = function(){}
parser.getFullestName = (str) => {}
parser.parseAddress = (str) => {}
```

Because there's no `default` prop being exported, we can import the parser two ways, either importing the whole object:
```ts
import * as parser from "humanparser"
```
OR, importing the props that was exported separately:
```ts
import {parseName, parseAddress} from "humanparser"
```
Personally, I like to import the named export, it helps with code intelligent, and I don't have to deal with the import all module namespace issue.

---
## Back Story

Back in the olden day, pre-2015 to be exact, where angular.js were still trendy, react was still the new kid in the block, and people were comparing Corodva phonegap to react native, there was a transition from ES3/ES5 (commonJS or CJS) to ES6 (ES2015, or ESM, or MJS - modulejs). I suspect whoever named the extension was a huge fan of the late pop king.

## Cautions
If you are looking at new projects, be cautious of pure ESM module. They will only allow you to import them with:

```ts
import mod from "module-name"
```

The tricky part is that, your project must also be a module - i.e, it cannot be compiled into common js (which converted all `import` statement into cjs `require` call). It must be an MJS/ecmascript module file (with the .mjs extension) OR that your `package.json` have specified the module property. Otherwise, it would simply fail to compile, and you won't be able to import it using require because ESM code looks like this:

```ts
export default stuff
```

Instead of this:
```js
module.exports = stuff
```

In the CJS example, `module.exports` are bridged into the `require` statement for importing in other modules. Meanwhile, in the ESM example, the `stuff` is being exported by the `export` statement , and thus can only be imported via the import statement.

The dilemma here is that if you're using TypeScript and was hoping that it would play well with ESM only module, you are in for some sour candy - it does not. Typescript compiler doesn't yet care if a module it imported is ESM or not -  it converts all to CJS import unless you configured it properly. You would also need to tell nodejs to use an experimental flag when running your compiled code: https://nodejs.org/api/esm.html#customizing-esm-specifier-resolution-algorithm

P.s: this post is a brain-dump. If it's useful, you're welcome. If not, here's the MIT license for this otherwise unreadable post:

```
Copyright 2022 L❤☮🤚

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

```