fix(bundling): add faux-ESM files so "import" in Node works with both named and default exports (#18916)
This commit is contained in:
parent
c9aad2dc8c
commit
99c44f9e88
@ -35,10 +35,14 @@ describe('packaging libs', () => {
|
||||
runCLI(
|
||||
`generate @nx/js:lib ${rollupLib} --bundler=rollup --no-interactive`
|
||||
);
|
||||
updateFile(`libs/${rollupLib}/src/index.ts`, (content) => {
|
||||
// Test that default functions work in ESM (Node).
|
||||
return `${content}\nexport default function f() { return 'rollup default' }`;
|
||||
});
|
||||
|
||||
runCLI(`build ${esbuildLib}`);
|
||||
runCLI(`build ${viteLib}`);
|
||||
runCLI(`build ${rollupLib}`);
|
||||
runCLI(`build ${rollupLib} --generateExportsField`);
|
||||
|
||||
const pmc = getPackageManagerCommand();
|
||||
let output: string;
|
||||
@ -66,10 +70,11 @@ describe('packaging libs', () => {
|
||||
`
|
||||
const { ${esbuildLib} } = require('@proj/${esbuildLib}');
|
||||
const { ${viteLib} } = require('@proj/${viteLib}');
|
||||
const { ${rollupLib} } = require('@proj/${rollupLib}');
|
||||
const { default: rollupDefault, ${rollupLib} } = require('@proj/${rollupLib}');
|
||||
console.log(${esbuildLib}());
|
||||
console.log(${viteLib}());
|
||||
console.log(${rollupLib}());
|
||||
console.log(rollupDefault());
|
||||
`
|
||||
);
|
||||
runCommand(pmc.install, {
|
||||
@ -81,6 +86,7 @@ describe('packaging libs', () => {
|
||||
expect(output).toContain(esbuildLib);
|
||||
expect(output).toContain(viteLib);
|
||||
expect(output).toContain(rollupLib);
|
||||
expect(output).toContain('rollup default');
|
||||
|
||||
// Make sure outputs in esm project
|
||||
createFile(
|
||||
@ -105,10 +111,11 @@ describe('packaging libs', () => {
|
||||
`
|
||||
import { ${esbuildLib} } from '@proj/${esbuildLib}';
|
||||
import { ${viteLib} } from '@proj/${viteLib}';
|
||||
import { ${rollupLib} } from '@proj/${rollupLib}';
|
||||
import rollupDefault, { ${rollupLib} } from '@proj/${rollupLib}';
|
||||
console.log(${esbuildLib}());
|
||||
console.log(${viteLib}());
|
||||
console.log(${rollupLib}());
|
||||
console.log(rollupDefault());
|
||||
`
|
||||
);
|
||||
runCommand(pmc.install, {
|
||||
@ -120,6 +127,7 @@ describe('packaging libs', () => {
|
||||
expect(output).toContain(esbuildLib);
|
||||
expect(output).toContain(viteLib);
|
||||
expect(output).toContain(rollupLib);
|
||||
expect(output).toContain('rollup default');
|
||||
}, 500_000);
|
||||
|
||||
it('should build with tsc, swc and be used in CJS/ESM projects', async () => {
|
||||
|
||||
@ -29,7 +29,8 @@ describe('Rollup Plugin', () => {
|
||||
checkFilesExist(`dist/libs/${myPkg}/index.cjs.d.ts`);
|
||||
expect(readJson(`dist/libs/${myPkg}/package.json`).exports).toEqual({
|
||||
'.': {
|
||||
import: './index.esm.js',
|
||||
module: './index.esm.js',
|
||||
import: './index.cjs.mjs',
|
||||
default: './index.cjs.js',
|
||||
},
|
||||
'./package.json': './package.json',
|
||||
@ -95,15 +96,18 @@ describe('Rollup Plugin', () => {
|
||||
expect(readJson(`dist/libs/${myPkg}/package.json`).exports).toEqual({
|
||||
'./package.json': './package.json',
|
||||
'.': {
|
||||
import: './index.esm.js',
|
||||
module: './index.esm.js',
|
||||
import: './index.cjs.mjs',
|
||||
default: './index.cjs.js',
|
||||
},
|
||||
'./bar': {
|
||||
import: './bar.esm.js',
|
||||
module: './bar.esm.js',
|
||||
import: './bar.cjs.mjs',
|
||||
default: './bar.cjs.js',
|
||||
},
|
||||
'./foo': {
|
||||
import: './foo.esm.js',
|
||||
module: './foo.esm.js',
|
||||
import: './foo.cjs.mjs',
|
||||
default: './foo.cjs.js',
|
||||
},
|
||||
});
|
||||
|
||||
@ -98,7 +98,8 @@ describe('updatePackageJson', () => {
|
||||
exports: {
|
||||
'./package.json': './package.json',
|
||||
'.': {
|
||||
import: './index.esm.js',
|
||||
module: './index.esm.js',
|
||||
import: './index.cjs.mjs',
|
||||
default: './index.cjs.js',
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { basename, dirname, parse, relative } from 'path';
|
||||
import { basename, join, parse } from 'path';
|
||||
import { ExecutorContext } from 'nx/src/config/misc-interfaces';
|
||||
import { ProjectGraphProjectNode } from 'nx/src/config/project-graph';
|
||||
import {
|
||||
@ -6,9 +6,10 @@ import {
|
||||
updateBuildableProjectPackageJsonDependencies,
|
||||
} from '@nx/js/src/utils/buildable-libs-utils';
|
||||
import { writeJsonFile } from 'nx/src/utils/fileutils';
|
||||
import { writeFileSync } from 'fs';
|
||||
import { PackageJson } from 'nx/src/utils/package-json';
|
||||
import { NormalizedRollupExecutorOptions } from './normalize';
|
||||
import { normalizePath } from '@nx/devkit';
|
||||
import { stripIndents } from '@nx/devkit';
|
||||
|
||||
// TODO(jack): Use updatePackageJson from @nx/js instead.
|
||||
export function updatePackageJson(
|
||||
@ -43,7 +44,10 @@ export function updatePackageJson(
|
||||
if (options.generateExportsField) {
|
||||
for (const [exportEntry, filePath] of Object.entries(esmExports)) {
|
||||
packageJson.exports[exportEntry] = hasCjsFormat
|
||||
? { import: filePath }
|
||||
? // If CJS format is used, make sure `import` (from Node) points to same instance of the package.
|
||||
// Otherwise, packages that are required to be singletons (like React, RxJS, etc.) will break.
|
||||
// Reserve `module` entry for bundlers to accommodate tree-shaking.
|
||||
{ [hasCjsFormat ? 'module' : 'import']: filePath }
|
||||
: filePath;
|
||||
}
|
||||
}
|
||||
@ -64,7 +68,35 @@ export function updatePackageJson(
|
||||
if (options.generateExportsField) {
|
||||
for (const [exportEntry, filePath] of Object.entries(cjsExports)) {
|
||||
if (hasEsmFormat) {
|
||||
// If ESM format used, make sure `import` (from Node) points to a wrapped
|
||||
// version of CJS file to ensure the package remains a singleton.
|
||||
// TODO(jack): This can be made into a rollup plugin to re-use in Vite.
|
||||
const relativeFile = parse(filePath).base;
|
||||
const fauxEsmFilePath = filePath.replace(/\.cjs\.js$/, '.cjs.mjs');
|
||||
packageJson.exports[exportEntry]['import'] ??= fauxEsmFilePath;
|
||||
packageJson.exports[exportEntry]['default'] ??= filePath;
|
||||
// Re-export from relative CJS file, and Node will synthetically export it as ESM.
|
||||
// Make sure both ESM and CJS point to same instance of the package because libs like React, RxJS, etc. requires it.
|
||||
// Also need a special .cjs.default.js file that re-exports the `default` from CJS, or else
|
||||
// default import in Node will not work.
|
||||
writeFileSync(
|
||||
join(
|
||||
options.outputPath,
|
||||
filePath.replace(/\.cjs\.js$/, '.cjs.default.js')
|
||||
),
|
||||
`exports._default = require('./${parse(filePath).base}').default;`
|
||||
);
|
||||
writeFileSync(
|
||||
join(options.outputPath, fauxEsmFilePath),
|
||||
// Re-export from relative CJS file, and Node will synthetically export it as ESM.
|
||||
stripIndents`
|
||||
export * from './${relativeFile}';
|
||||
export { _default as default } from './${relativeFile.replace(
|
||||
/\.cjs\.js$/,
|
||||
'.cjs.default.js'
|
||||
)}';
|
||||
`
|
||||
);
|
||||
} else {
|
||||
packageJson.exports[exportEntry] = filePath;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user