From 0c6a64e1c54741b0ff9805e8c1978a384166e1ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E5=BB=B7=E5=AE=89?= <73953029+nrps9909@users.noreply.github.com> Date: Thu, 27 Aug 2026 17:07:23 +0800 Subject: [PATCH 1/2] fix: make package exports loadable in Node --- package.json | 16 +++++++++++++++- scripts/test-node-exports.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 scripts/test-node-exports.js diff --git a/package.json b/package.json index 2cdd25634..b6c8d79b4 100644 --- a/package.json +++ b/package.json @@ -13,67 +13,80 @@ "exports": { ".": { "types": "./es/index.d.ts", + "node": "./lib/index.js", "import": "./es/index.js", "require": "./lib/index.js" }, "./assets/*": "./assets/*", "./generate": { "types": "./es/generate/index.d.ts", + "node": "./lib/generate/index.js", "import": "./es/generate/index.js", "require": "./lib/generate/index.js" }, "./generate/*": { "types": "./es/generate/*.d.ts", + "node": "./lib/generate/*.js", "import": "./es/generate/*.js", "require": "./lib/generate/*.js" }, "./interface": { "types": "./es/interface.d.ts", + "node": "./lib/interface.js", "import": "./es/interface.js", "require": "./lib/interface.js" }, "./locale/*": { "types": "./es/locale/*.d.ts", + "node": "./lib/locale/*.js", "import": "./es/locale/*.js", "require": "./lib/locale/*.js" }, "./lib/generate": { "types": "./es/generate/index.d.ts", + "node": "./lib/generate/index.js", "import": "./es/generate/index.js", "require": "./lib/generate/index.js" }, "./lib/generate/*": { "types": "./es/generate/*.d.ts", + "node": "./lib/generate/*.js", "import": "./es/generate/*.js", "require": "./lib/generate/*.js" }, "./lib/interface": { "types": "./es/interface.d.ts", + "node": "./lib/interface.js", "import": "./es/interface.js", "require": "./lib/interface.js" }, "./lib/locale/*": { "types": "./es/locale/*.d.ts", + "node": "./lib/locale/*.js", "import": "./es/locale/*.js", "require": "./lib/locale/*.js" }, "./es/generate": { "types": "./es/generate/index.d.ts", + "node": "./lib/generate/index.js", "import": "./es/generate/index.js", "require": "./lib/generate/index.js" }, "./es/generate/*": { "types": "./es/generate/*.d.ts", + "node": "./lib/generate/*.js", "import": "./es/generate/*.js", "require": "./lib/generate/*.js" }, "./es/interface": { "types": "./es/interface.d.ts", + "node": "./lib/interface.js", "import": "./es/interface.js", "require": "./lib/interface.js" }, "./es/locale/*": { "types": "./es/locale/*.d.ts", + "node": "./lib/locale/*.js", "import": "./es/locale/*.js", "require": "./lib/locale/*.js" } @@ -101,11 +114,12 @@ "compile": "father build && lessc assets/index.less assets/index.css", "browser-field": "node scripts/update-browser-field.js", "gh-pages": "cross-env GH_PAGES=1 npm run docs:build && npm run docs:deploy", - "prepublishOnly": "npm run compile && npm run browser-field && rc-np", + "prepublishOnly": "npm run compile && npm run test:node-exports && npm run browser-field && rc-np", "lint": "eslint src/ --ext .ts,.tsx,.jsx,.js,.md", "lint:tsc": "tsc -p tsconfig.json --noEmit", "prettier": "prettier --write --ignore-unknown .", "test": "rc-test", + "test:node-exports": "node scripts/test-node-exports.js", "coverage": "father test --coverage", "prepare": "husky", "tsc": "tsc --noEmit" diff --git a/scripts/test-node-exports.js b/scripts/test-node-exports.js new file mode 100644 index 000000000..7cc22fb1c --- /dev/null +++ b/scripts/test-node-exports.js @@ -0,0 +1,30 @@ +const assert = require('node:assert/strict'); + +const entryPoints = [ + '@rc-component/picker', + '@rc-component/picker/generate', + '@rc-component/picker/generate/moment', + '@rc-component/picker/interface', + '@rc-component/picker/locale/en_US', + '@rc-component/picker/lib/generate', + '@rc-component/picker/lib/generate/moment', + '@rc-component/picker/lib/interface', + '@rc-component/picker/lib/locale/en_US', + '@rc-component/picker/es/generate', + '@rc-component/picker/es/generate/moment', + '@rc-component/picker/es/interface', + '@rc-component/picker/es/locale/en_US', +]; + +async function testNodeExports() { + const commonJSModules = entryPoints.map((entryPoint) => require(entryPoint)); + const esModules = await Promise.all(entryPoints.map((entryPoint) => import(entryPoint))); + + assert.equal(commonJSModules.length, entryPoints.length); + assert.equal(esModules.length, entryPoints.length); +} + +testNodeExports().catch((error) => { + console.error(error); + process.exitCode = 1; +}); From 13028ec8925909e3ed86e3991ecf05e6b5da528c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E5=BB=B7=E5=AE=89?= <73953029+nrps9909@users.noreply.github.com> Date: Thu, 27 Aug 2026 17:28:41 +0800 Subject: [PATCH 2/2] fix: preserve native ESM export shapes --- package.json | 15 +-------- scripts/rewrite-esm-imports.js | 57 ++++++++++++++++++++++++++++++++++ scripts/test-node-exports.js | 16 ++++++++++ 3 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 scripts/rewrite-esm-imports.js diff --git a/package.json b/package.json index b6c8d79b4..1559e8e6b 100644 --- a/package.json +++ b/package.json @@ -13,80 +13,67 @@ "exports": { ".": { "types": "./es/index.d.ts", - "node": "./lib/index.js", "import": "./es/index.js", "require": "./lib/index.js" }, "./assets/*": "./assets/*", "./generate": { "types": "./es/generate/index.d.ts", - "node": "./lib/generate/index.js", "import": "./es/generate/index.js", "require": "./lib/generate/index.js" }, "./generate/*": { "types": "./es/generate/*.d.ts", - "node": "./lib/generate/*.js", "import": "./es/generate/*.js", "require": "./lib/generate/*.js" }, "./interface": { "types": "./es/interface.d.ts", - "node": "./lib/interface.js", "import": "./es/interface.js", "require": "./lib/interface.js" }, "./locale/*": { "types": "./es/locale/*.d.ts", - "node": "./lib/locale/*.js", "import": "./es/locale/*.js", "require": "./lib/locale/*.js" }, "./lib/generate": { "types": "./es/generate/index.d.ts", - "node": "./lib/generate/index.js", "import": "./es/generate/index.js", "require": "./lib/generate/index.js" }, "./lib/generate/*": { "types": "./es/generate/*.d.ts", - "node": "./lib/generate/*.js", "import": "./es/generate/*.js", "require": "./lib/generate/*.js" }, "./lib/interface": { "types": "./es/interface.d.ts", - "node": "./lib/interface.js", "import": "./es/interface.js", "require": "./lib/interface.js" }, "./lib/locale/*": { "types": "./es/locale/*.d.ts", - "node": "./lib/locale/*.js", "import": "./es/locale/*.js", "require": "./lib/locale/*.js" }, "./es/generate": { "types": "./es/generate/index.d.ts", - "node": "./lib/generate/index.js", "import": "./es/generate/index.js", "require": "./lib/generate/index.js" }, "./es/generate/*": { "types": "./es/generate/*.d.ts", - "node": "./lib/generate/*.js", "import": "./es/generate/*.js", "require": "./lib/generate/*.js" }, "./es/interface": { "types": "./es/interface.d.ts", - "node": "./lib/interface.js", "import": "./es/interface.js", "require": "./lib/interface.js" }, "./es/locale/*": { "types": "./es/locale/*.d.ts", - "node": "./lib/locale/*.js", "import": "./es/locale/*.js", "require": "./lib/locale/*.js" } @@ -111,7 +98,7 @@ "build": "npm run docs:build", "docs:build": "dumi build", "docs:deploy": "gh-pages -d docs-dist", - "compile": "father build && lessc assets/index.less assets/index.css", + "compile": "father build && node scripts/rewrite-esm-imports.js && lessc assets/index.less assets/index.css", "browser-field": "node scripts/update-browser-field.js", "gh-pages": "cross-env GH_PAGES=1 npm run docs:build && npm run docs:deploy", "prepublishOnly": "npm run compile && npm run test:node-exports && npm run browser-field && rc-np", diff --git a/scripts/rewrite-esm-imports.js b/scripts/rewrite-esm-imports.js new file mode 100644 index 000000000..5a4ee4752 --- /dev/null +++ b/scripts/rewrite-esm-imports.js @@ -0,0 +1,57 @@ +const fs = require('node:fs'); +const path = require('node:path'); + +const esDirectory = path.resolve(__dirname, '../es'); +const moduleSpecifierPattern = /(\b(?:from|import)\s*(?:\(\s*)?)(['"])(\.\.?\/[^'"]+)\2(\s*\)?)/g; + +fs.writeFileSync(path.join(esDirectory, 'package.json'), '{\n "type": "module"\n}\n'); + +function collectJavaScriptFiles(directory) { + return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { + const entryPath = path.join(directory, entry.name); + + if (entry.isDirectory()) { + return collectJavaScriptFiles(entryPath); + } + + return entry.isFile() && entry.name.endsWith('.js') ? [entryPath] : []; + }); +} + +function resolveModuleSpecifier(filePath, specifier) { + if (path.extname(specifier)) { + return specifier; + } + + const absoluteSpecifier = path.resolve(path.dirname(filePath), specifier); + if (fs.existsSync(`${absoluteSpecifier}.js`)) { + return `${specifier}.js`; + } + if (fs.existsSync(path.join(absoluteSpecifier, 'index.js'))) { + return `${specifier}/index.js`; + } + + throw new Error(`Cannot resolve ${specifier} from ${path.relative(esDirectory, filePath)}`); +} + +let rewriteCount = 0; + +collectJavaScriptFiles(esDirectory).forEach((filePath) => { + const source = fs.readFileSync(filePath, 'utf8'); + const rewrittenSource = source.replace( + moduleSpecifierPattern, + (match, prefix, quote, specifier, suffix) => { + const rewrittenSpecifier = resolveModuleSpecifier(filePath, specifier); + if (rewrittenSpecifier !== specifier) { + rewriteCount += 1; + } + return `${prefix}${quote}${rewrittenSpecifier}${quote}${suffix}`; + }, + ); + + if (rewrittenSource !== source) { + fs.writeFileSync(filePath, rewrittenSource); + } +}); + +console.log(`Rewrote ${rewriteCount} ESM module specifiers.`); diff --git a/scripts/test-node-exports.js b/scripts/test-node-exports.js index 7cc22fb1c..9f7084e04 100644 --- a/scripts/test-node-exports.js +++ b/scripts/test-node-exports.js @@ -22,6 +22,22 @@ async function testNodeExports() { assert.equal(commonJSModules.length, entryPoints.length); assert.equal(esModules.length, entryPoints.length); + + const publicKeys = (module) => + Object.keys(module) + .filter((key) => key !== '__esModule' && key !== 'module.exports') + .sort(); + + commonJSModules.forEach((commonJSModule, index) => { + assert.deepEqual(publicKeys(esModules[index]), publicKeys(commonJSModule)); + }); + + assert.equal(esModules[0].default, esModules[0].Picker); + assert.equal(commonJSModules[0].default, commonJSModules[0].Picker); + assert.equal(typeof esModules[2].default.getNow, 'function'); + assert.equal(typeof commonJSModules[2].default.getNow, 'function'); + assert.equal(esModules[4].default.locale, 'en_US'); + assert.equal(commonJSModules[4].default.locale, 'en_US'); } testNodeExports().catch((error) => {