From 41c5a80fad048c1eea3d011e2c7f0b3c57f5f697 Mon Sep 17 00:00:00 2001 From: wuyangfan Date: Mon, 25 May 2026 17:57:38 +0800 Subject: [PATCH] fix: add .js extensions to es build for Node ESM Closes #2528 --- README.md | 6 +++-- package.json | 2 +- scripts/add-es-import-extensions.js | 41 +++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 scripts/add-es-import-extensions.js diff --git a/README.md b/README.md index 8ef42fd79..b1e2d6707 100644 --- a/README.md +++ b/README.md @@ -46,15 +46,17 @@ import validator from 'validator'; Or, import only a subset of the library: ```javascript -import isEmail from 'validator/lib/isEmail'; +import isEmail from 'validator/lib/isEmail.js'; ``` #### Tree-shakeable ES imports ```javascript -import isEmail from 'validator/es/lib/isEmail'; +import isEmail from 'validator/es/lib/isEmail.js'; ``` +When using native Node.js ESM, include the `.js` file extension in import specifiers. + ### Client-side usage The library can be loaded either as a standalone script, or through an [AMD][amd]-compatible loader diff --git a/package.json b/package.json index 2b5c5c8bb..423851682 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "clean": "run-p clean:*", "minify": "uglifyjs validator.js -o validator.min.js --compress --mangle --comments /Copyright/", "build:browser": "node --require @babel/register build-browser && npm run minify", - "build:es": "babel src -d es --env-name=es", + "build:es": "babel src -d es --env-name=es && node scripts/add-es-import-extensions.js", "build:node": "babel src -d .", "build": "run-p build:*", "pretest": "npm run build && npm run lint", diff --git a/scripts/add-es-import-extensions.js b/scripts/add-es-import-extensions.js new file mode 100644 index 000000000..648700d96 --- /dev/null +++ b/scripts/add-es-import-extensions.js @@ -0,0 +1,41 @@ +const fs = require('fs'); +const path = require('path'); + +const ES_ROOT = path.join(__dirname, '..', 'es'); + +function addExtension(specifier) { + if (specifier.endsWith('.js')) { + return specifier; + } + + return `${specifier}.js`; +} + +function fixFile(filePath) { + const source = fs.readFileSync(filePath, 'utf8'); + const updated = source.replace( + /from ['"](\.\.?\/[^'"]+)['"]/g, + (match, specifier) => match.replace(specifier, addExtension(specifier)), + ); + + if (updated !== source) { + fs.writeFileSync(filePath, updated); + } +} + +function walk(directory) { + for (const entry of fs.readdirSync(directory, { withFileTypes: true })) { + const entryPath = path.join(directory, entry.name); + + if (entry.isDirectory()) { + walk(entryPath); + continue; + } + + if (entry.name.endsWith('.js')) { + fixFile(entryPath); + } + } +} + +walk(ES_ROOT);