At Plasmo we leverage Typescript for our web projects.
CJS project like humanparser would have example like this:
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:
import * as humanParser from 'humanparser'
OR
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:
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:
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:
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:
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:
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:
import * as parser from "humanparser"
OR, importing the props that was exported separately:
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:
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:
export default stuff
Instead of this:
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.
Top comments (0)