wooorm / npm-esm-vs-cjs

Data on the share of ESM vs CJS on the public npm registry
MIT License
141 stars 0 forks source link

Many modules labeled as esm can be loaded by `require()` (prior to `require(esm)`) #11

Open joyeecheung opened 12 hours ago

joyeecheung commented 12 hours ago

When I was testing out https://github.com/nodejs/node/pull/55085/, to get a sense of what modules couldn't be loaded before require(esm) is supported but could be loaded after it's unflagged, I have this script to test loading some high-impact packages locally using require(), and it seems out of 662 packages labeled as esm, 103 could already be loaded by require() even without require(esm), so they are probably dual.

Here is the list I've found:

@adobe/css-tools
@algolia/client-analytics
@algolia/client-common
@algolia/client-personalization
@algolia/client-search
@algolia/requester-node-http
@apollo/client
@assemblyscript/loader
@codemirror/language
@codemirror/state
@codemirror/view
@commitlint/cli
@es-joy/jsdoccomment
@eslint/config-array
@eslint/object-schema
@fal-works/esbuild-plugin-global-externals
@headlessui/react
@humanwhocodes/module-importer
@humanwhocodes/retry
@kurkle/color
@lezer/common
@lezer/lr
@octokit/tsconfig
@pkgr/core
@pkgr/utils
@selderee/plugin-htmlparser2
@sentry/nextjs
@ungap/promise-all-settled
@wry/caches
@wry/context
@wry/equality
@wry/trie
algoliasearch
array-back
axios
binaryextensions
byte-size
camel-case
canvg
capital-case
codemirror
command-line-args
command-line-usage
constant-case
crelt
css-declaration-sorter
dot-case
filesize
find-replace
fs-extra
get-package-type
get-tsconfig
git-hooks-list
header-case
html-escaper
html-to-text
is-lower-case
is-upper-case
leac
lines-and-columns
lmdb
marked-terminal
mlly
no-case
optimism
orderedmap
p-finally
param-case
parse5
parse5-htmlparser2-tree-adapter
parse5-parser-stream
parseley
pascal-case
path-case
prosemirror-commands
prosemirror-history
prosemirror-keymap
prosemirror-model
prosemirror-state
prosemirror-transform
react-chartjs-2
react-easy-router
resolve-pkg-maps
rope-sequence
selderee
sentence-case
snake-case
superstruct
synckit
table-layout
textextensions
tiny-lru
tinybench
tinyspy
trim-right
ts-invariant
ts-node
typical
unicode-properties
upper-case
upper-case-first
w3c-keyname
wordwrapjs
joyeecheung commented 12 hours ago

From a simple glance of the packages, it seems they mostly have "type": "module" but also a "require" exports pointing to a CJS version (typically, .cjs, as they probably want to use .js for ESM), thus being actually dual.

wooorm commented 12 hours ago

Hi again! 👋

Ah, yeah. I guess, when making this initially, the idea was more to track ESM. Not dual. ESM being the “highest to achieve”. So indeed, if "type": "module", is seen, that’s enough to classify it as ESM.

Here’s an example from: @humanwhocodes/retry:

2  "name": "@humanwhocodes/retry",
3  "version": "0.3.0",
4  "description": "A utility to retry failed async methods.",
5  "type": "module",
6  "main": "dist/retrier.cjs",
7  "module": "dist/retrier.js",
8  "types": "dist/retrier.d.ts",
9  "exports": {
10    "require": {
11      "types": "./dist/retrier.d.cts",
12      "default": "./dist/retrier.cjs"
13    },
14    "import": {
15      "types": "./dist/retrier.d.ts",
16      "default": "./dist/retrier.js"
17    }
18  },

Looking at my code, my above assumption may not be true:

https://github.com/wooorm/npm-esm-vs-cjs/blob/c0a92334da4979f7614143734bbe7931d2a0dcde/script/crawl.js#L177-L180

New theory: perhaps it’s the nested conditions? 🤷‍♂️

joyeecheung commented 11 hours ago

Yeah I think when the exports conditions contain require as a key (either nested, or not), it's likely to be dual.

Ah, yeah. I guess, when making this initially, the idea was more to track ESM. Not dual. ESM being the “highest to achieve”.

Got it - I think the difference is between "shipping ESM" v.s. "authoring in ESM". Technically the ones that are labeled as "esm"/"faux"/"dual" are probably all authored in ESM, but differ in whether they ship themselves as ESM-only. "type": "module" is a good indicator that the module is authored in ESM, but not necessarily for the way the module is shipped.