频道
bg

NodeJS ESM

coding十月 20, 20231mins
nodejs

缘由H1

When importing CommonJS modules, the module.exports object is provided as the default export. Named exports may be available, provided by static analysis as a convenience for better ecosystem compatibility.

For better compatibility with existing usage in the JS ecosystem, Node.js in addition attempts to determine the CommonJS named exports of every imported CommonJS module to provide them as separate ES module exports using a static analysis process.

NodeJS为把CommonJS模块的module.exports 对象导出到default ,但是为了更好的兼容性,也会导出到ES模块命名空间。这是使用的是静态分析进程来实现的,动态添加到module.exports 的对象是不起作用的。

jsx

import { name } from './cjs.cjs';
console.log(name);
// Prints: 'exported'
import cjs from './cjs.cjs';
console.log(cjs);
// Prints: { name: 'exported' }
import * as m from './cjs.cjs';
console.log(m);
// Prints: [Module] { default: { name: 'exported' }, name: 'exported' }

问题H2

jsx

// internal/modules/esm/translator
return new ModuleWrap(url, undefined, namesWithDefault, function() {
debug(`Loading CJSModule ${url}`);
let exports;
if (asyncESM.esmLoader.cjsCache.has(module)) {
exports = asyncESM.esmLoader.cjsCache.get(module);
asyncESM.esmLoader.cjsCache.delete(module);
} else {
try {
exports = CJSModule._load(filename, undefined, isMain);
} catch (err) {
enrichCJSError(err, undefined, filename);
throw err;
}
}
for (const exportName of exportNames) {
if (!ObjectPrototypeHasOwnProperty(exports, exportName) ||
exportName === 'default')
continue;
// We might trigger a getter -> dont fail.
let value;
try {
value = exports[exportName];
} catch {
// Continue regardless of error.
}
this.setExport(exportName, value);
}
this.setExport('default', exports);
});

exportNames为[]

原因H3

jsx

// rtk-query-react.cjs.development.js
__export(exports, {
ApiProvider: function () { return ApiProvider; },
createApi: function () { return createApi; },
reactHooksModule: function () { return reactHooksModule; }
});

这里使用的动态导出,导致静态分析失败了

解决H2

配置deps.interopDefaulttrue

vitest使用vite-node(Vite as Node runtime)作为Node Runtime

jsx

async interopedImport(path: string) {
const importedModule = await import(path)
if (!this.shouldInterop(path, importedModule))
return importedModule
const { mod, defaultExport } = interopModule(importedModule)
return new Proxy(mod, {
get(mod, prop) {
if (prop === 'default')
return defaultExport
return mod[prop] ?? defaultExport?.[prop]
},
has(mod, prop) {
if (prop === 'default')
return defaultExport !== undefined
return prop in mod || (defaultExport && prop in defaultExport)
},
getOwnPropertyDescriptor(mod, prop) {
const descriptor = Reflect.getOwnPropertyDescriptor(mod, prop)
if (descriptor)
return descriptor
if (prop === 'default' && defaultExport !== undefined) {
return {
value: defaultExport,
enumerable: true,
configurable: true,
}
}
},
})
}

评论


新的评论

匹配您的Gravatar头像

Joen Yu

@2022 JoenYu, all rights reserved. Made with love.