Created
November 8, 2023 14:56
-
-
Save timostamm/8f4ba7b08a1cf429c80fac4fa44fa2cd to your computer and use it in GitHub Desktop.
Analyze "module" exports of high-impact npm packages
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ node check.mjs | |
High-impact npm packages: 7007 | |
Packages with exports: 1126 | |
Packages with "module" exports: 44 | |
- tslib | |
- uuid | |
- @emotion/memoize | |
- es-module-lexer | |
- @emotion/unitless | |
- @emotion/hash | |
- underscore | |
- @emotion/is-prop-valid | |
- @emotion/utils | |
- @emotion/serialize | |
- @emotion/cache | |
- @emotion/sheet | |
- @emotion/weak-memoize | |
- react-select | |
- @emotion/react | |
- @emotion/babel-plugin | |
- @emotion/use-insertion-effect-with-fallbacks | |
- idb | |
- @emotion/styled | |
- @floating-ui/dom | |
- @floating-ui/core | |
- @emotion/css | |
- @azure/msal-common | |
- react-error-boundary | |
- mitt | |
- @floating-ui/react-dom | |
- react-textarea-autosize | |
- react-i18next | |
- @azure/msal-browser | |
- zustand | |
- @azure/msal-node | |
- @floating-ui/utils | |
- vuex | |
- @fortawesome/fontawesome-svg-core | |
- @firebase/messaging | |
- swr | |
- use-debounce | |
- i18next-browser-languagedetector | |
- redux-saga | |
- react-virtualized-auto-sizer | |
- dexie | |
- @emotion/babel-preset-css-prop | |
- @polkadot/util | |
- @polkadot/api | |
Packages with "module" export before "import": 34 | |
Packages with "module" export before "default": 5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {npmHighImpact} from 'npm-high-impact' | |
import {readFileSync, existsSync, writeFileSync} from "node:fs"; | |
void main(); | |
async function main() { | |
const pkgs = await getPackages(); | |
const exports = pkgs.filter(pkg => | |
hasExports(pkg, (obj => { | |
return true; | |
})) | |
); | |
const moduleExport = pkgs.filter(pkg => | |
hasExports(pkg, (obj => { | |
return "module" in obj; | |
})) | |
); | |
const moduleBeforeImport = pkgs.filter(pkg => | |
hasExports(pkg, (obj => { | |
const keys = Object.keys(obj); | |
const moduleIndex = keys.indexOf("module"); | |
const importIndex = keys.indexOf("import"); | |
if (moduleIndex >= 0 && importIndex >= 0) { | |
return moduleIndex < importIndex; | |
} | |
return false; | |
})) | |
); | |
const moduleBeforeDefault = pkgs.filter(pkg => | |
pkg.type === "module" && | |
hasExports(pkg, (obj => { | |
const keys = Object.keys(obj); | |
const moduleIndex = keys.indexOf("module"); | |
const defaultIndex = keys.indexOf("default"); | |
if (moduleIndex >= 0 && defaultIndex >= 0) { | |
return moduleIndex < defaultIndex; | |
} | |
return false; | |
})) | |
); | |
console.log(`High-impact npm packages: ${npmHighImpact.length}`); | |
console.log(`Packages with exports: ${exports.length}`); | |
console.log(`Packages with "module" exports: ${moduleExport.length}`); | |
for (const pkg of moduleExport) { | |
console.log(`- ${pkg.name}`); | |
} | |
console.log(`Packages with "module" export before "import": ${moduleBeforeImport.length}`); | |
console.log(`Packages with "module" export before "default": ${moduleBeforeDefault.length}`); | |
} | |
/** | |
* @return {Promise<object[]>} | |
*/ | |
async function getPackages() { | |
const pkgsFile = "pkgs.json"; | |
let pkgs = {}; | |
if (existsSync(pkgsFile)) { | |
pkgs = JSON.parse(readFileSync(pkgsFile, "utf-8")); | |
} | |
for (const name of npmHighImpact) { | |
if (name in pkgs) { | |
continue; | |
} | |
console.log(`fetching ${name}`); | |
const res = await fetch(`https://registry.npmjs.com/${name}/latest`); | |
pkgs[name] = await res.json(); | |
writeFileSync(pkgsFile, JSON.stringify(pkgs)); | |
} | |
return Object.values(pkgs); | |
} | |
/** | |
* @callback hasExports~fn | |
* @param {object} obj | |
* @return {boolean} | |
*/ | |
/** | |
* @param {object} pkg | |
* @param {hasExports~fn} fn | |
* @return {boolean} | |
*/ | |
function hasExports(pkg, fn) { | |
if (!("exports" in pkg)) { | |
return false; | |
} | |
function recurse(obj) { | |
if (obj === null) { | |
return false; | |
} | |
if (typeof obj !== "object") { | |
return false; | |
} | |
const r = fn(obj); | |
if (r) { | |
return true; | |
} | |
for (const val of Object.values(obj)) { | |
if (recurse(val)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
return recurse(pkg.exports); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment