We currently rely on the TS behavior of matching `d.ts` files based on the `.js` file names. e.g. `foo.js` matches `foo.d.ts`. However, it isn't working for all tools so we should explicitly set it. Most modern packages are still setting it even though it is not technically needed. e.g. [Nuxt](https://unpkg.com/browse/nuxt@3.12.4/package.json) Note: If both ESM and CJS are present, then prefer `*.esm.d.ts` files since the generated types are in ESM format. ## Current Behavior `exports` entries are missing `types` field ## Expected Behavior `exports` entries have `types` field set ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #18835
321 lines
7.6 KiB
TypeScript
321 lines
7.6 KiB
TypeScript
import { updatePackageJson } from './update-package-json';
|
|
import * as utils from 'nx/src/utils/fileutils';
|
|
import { PackageJson } from 'nx/src/utils/package-json';
|
|
|
|
describe('updatePackageJson', () => {
|
|
const commonOptions = {
|
|
outputPath: 'dist/index.js',
|
|
tsConfig: './tsconfig.json',
|
|
project: './package.json',
|
|
main: './index.js',
|
|
entryRoot: '.',
|
|
projectRoot: '.',
|
|
assets: [],
|
|
rollupConfig: [],
|
|
};
|
|
|
|
describe('generateExportsField: true', () => {
|
|
it('should support ESM', () => {
|
|
const spy = jest.spyOn(utils, 'writeJsonFile');
|
|
|
|
updatePackageJson(
|
|
{
|
|
...commonOptions,
|
|
generateExportsField: true,
|
|
format: ['esm'],
|
|
},
|
|
{} as unknown as PackageJson
|
|
);
|
|
|
|
expect(utils.writeJsonFile).toHaveBeenCalledWith(expect.anything(), {
|
|
exports: {
|
|
'./package.json': './package.json',
|
|
'.': {
|
|
import: './index.esm.js',
|
|
types: './index.esm.d.ts',
|
|
},
|
|
},
|
|
main: './index.esm.js',
|
|
module: './index.esm.js',
|
|
type: 'module',
|
|
types: './index.esm.d.ts',
|
|
});
|
|
|
|
spy.mockRestore();
|
|
});
|
|
|
|
it('should support CJS', () => {
|
|
const spy = jest.spyOn(utils, 'writeJsonFile');
|
|
|
|
updatePackageJson(
|
|
{
|
|
...commonOptions,
|
|
generateExportsField: true,
|
|
format: ['cjs'],
|
|
},
|
|
{} as unknown as PackageJson
|
|
);
|
|
|
|
expect(utils.writeJsonFile).toHaveBeenCalledWith(expect.anything(), {
|
|
exports: {
|
|
'./package.json': './package.json',
|
|
'.': './index.cjs.js',
|
|
},
|
|
main: './index.cjs.js',
|
|
type: 'commonjs',
|
|
types: './index.cjs.d.ts',
|
|
});
|
|
|
|
spy.mockRestore();
|
|
});
|
|
|
|
it('should support ESM + CJS', () => {
|
|
const spy = jest.spyOn(utils, 'writeJsonFile');
|
|
|
|
updatePackageJson(
|
|
{
|
|
...commonOptions,
|
|
generateExportsField: true,
|
|
format: ['esm', 'cjs'],
|
|
},
|
|
{} as unknown as PackageJson
|
|
);
|
|
|
|
expect(utils.writeJsonFile).toHaveBeenCalledWith(expect.anything(), {
|
|
exports: {
|
|
'./package.json': './package.json',
|
|
'.': {
|
|
module: './index.esm.js',
|
|
import: './index.cjs.mjs',
|
|
default: './index.cjs.js',
|
|
types: './index.esm.d.ts',
|
|
},
|
|
},
|
|
main: './index.cjs.js',
|
|
module: './index.esm.js',
|
|
types: './index.esm.d.ts',
|
|
});
|
|
|
|
spy.mockRestore();
|
|
});
|
|
|
|
it('should support custom exports field', () => {
|
|
const spy = jest.spyOn(utils, 'writeJsonFile');
|
|
|
|
updatePackageJson(
|
|
{
|
|
...commonOptions,
|
|
generateExportsField: true,
|
|
format: ['esm'],
|
|
},
|
|
{
|
|
exports: {
|
|
'./foo': {
|
|
import: './some/custom/path/foo.esm.js',
|
|
types: './some/custom/path/foo.d.ts',
|
|
},
|
|
},
|
|
} as unknown as PackageJson
|
|
);
|
|
|
|
expect(utils.writeJsonFile).toHaveBeenCalledWith(expect.anything(), {
|
|
exports: {
|
|
'./package.json': './package.json',
|
|
'.': {
|
|
import: './index.esm.js',
|
|
types: './index.esm.d.ts',
|
|
},
|
|
'./foo': {
|
|
import: './some/custom/path/foo.esm.js',
|
|
types: './some/custom/path/foo.d.ts',
|
|
},
|
|
},
|
|
main: './index.esm.js',
|
|
module: './index.esm.js',
|
|
type: 'module',
|
|
types: './index.esm.d.ts',
|
|
});
|
|
|
|
spy.mockRestore();
|
|
});
|
|
});
|
|
|
|
describe('generateExportsField: false', () => {
|
|
it('should support ESM', () => {
|
|
const spy = jest.spyOn(utils, 'writeJsonFile');
|
|
|
|
updatePackageJson(
|
|
{
|
|
...commonOptions,
|
|
format: ['esm'],
|
|
},
|
|
{} as unknown as PackageJson
|
|
);
|
|
|
|
expect(utils.writeJsonFile).toHaveBeenCalledWith(expect.anything(), {
|
|
main: './index.esm.js',
|
|
module: './index.esm.js',
|
|
type: 'module',
|
|
types: './index.esm.d.ts',
|
|
});
|
|
|
|
spy.mockRestore();
|
|
});
|
|
|
|
it('should support CJS', () => {
|
|
const spy = jest.spyOn(utils, 'writeJsonFile');
|
|
|
|
updatePackageJson(
|
|
{
|
|
...commonOptions,
|
|
format: ['cjs'],
|
|
},
|
|
{} as unknown as PackageJson
|
|
);
|
|
|
|
expect(utils.writeJsonFile).toHaveBeenCalledWith(expect.anything(), {
|
|
main: './index.cjs.js',
|
|
type: 'commonjs',
|
|
types: './index.cjs.d.ts',
|
|
});
|
|
|
|
spy.mockRestore();
|
|
});
|
|
|
|
it('should support ESM + CJS', () => {
|
|
const spy = jest.spyOn(utils, 'writeJsonFile');
|
|
|
|
updatePackageJson(
|
|
{
|
|
...commonOptions,
|
|
format: ['esm', 'cjs'],
|
|
},
|
|
{} as unknown as PackageJson
|
|
);
|
|
|
|
expect(utils.writeJsonFile).toHaveBeenCalledWith(expect.anything(), {
|
|
main: './index.cjs.js',
|
|
module: './index.esm.js',
|
|
types: './index.esm.d.ts',
|
|
});
|
|
|
|
spy.mockRestore();
|
|
});
|
|
|
|
it('should support custom exports field', () => {
|
|
const spy = jest.spyOn(utils, 'writeJsonFile');
|
|
|
|
updatePackageJson(
|
|
{
|
|
...commonOptions,
|
|
format: ['esm'],
|
|
},
|
|
{
|
|
exports: {
|
|
'./foo': './foo.esm.js',
|
|
},
|
|
} as unknown as PackageJson
|
|
);
|
|
|
|
expect(utils.writeJsonFile).toHaveBeenCalledWith(expect.anything(), {
|
|
main: './index.esm.js',
|
|
module: './index.esm.js',
|
|
type: 'module',
|
|
types: './index.esm.d.ts',
|
|
exports: {
|
|
'./foo': './foo.esm.js',
|
|
},
|
|
});
|
|
|
|
spy.mockRestore();
|
|
});
|
|
});
|
|
|
|
describe('skipTypeField', () => {
|
|
it('should not include type field if skipTypeField is true', () => {
|
|
const spy = jest.spyOn(utils, 'writeJsonFile');
|
|
|
|
updatePackageJson(
|
|
{
|
|
...commonOptions,
|
|
format: ['esm'],
|
|
skipTypeField: true,
|
|
},
|
|
{
|
|
exports: {
|
|
'./foo': './foo.esm.js',
|
|
},
|
|
} as unknown as PackageJson
|
|
);
|
|
|
|
expect(utils.writeJsonFile).toHaveBeenCalledWith(expect.anything(), {
|
|
main: './index.esm.js',
|
|
module: './index.esm.js',
|
|
types: './index.esm.d.ts',
|
|
exports: {
|
|
'./foo': './foo.esm.js',
|
|
},
|
|
});
|
|
|
|
spy.mockRestore();
|
|
});
|
|
|
|
it('should include type field if skipTypeField is undefined', () => {
|
|
const spy = jest.spyOn(utils, 'writeJsonFile');
|
|
|
|
updatePackageJson(
|
|
{
|
|
...commonOptions,
|
|
format: ['esm'],
|
|
},
|
|
{
|
|
exports: {
|
|
'./foo': './foo.esm.js',
|
|
},
|
|
} as unknown as PackageJson
|
|
);
|
|
|
|
expect(utils.writeJsonFile).toHaveBeenCalledWith(expect.anything(), {
|
|
main: './index.esm.js',
|
|
module: './index.esm.js',
|
|
type: 'module',
|
|
types: './index.esm.d.ts',
|
|
exports: {
|
|
'./foo': './foo.esm.js',
|
|
},
|
|
});
|
|
|
|
spy.mockRestore();
|
|
});
|
|
|
|
it('should include type field if skipTypeField is false', () => {
|
|
const spy = jest.spyOn(utils, 'writeJsonFile');
|
|
|
|
updatePackageJson(
|
|
{
|
|
...commonOptions,
|
|
format: ['esm'],
|
|
skipTypeField: false,
|
|
},
|
|
{
|
|
exports: {
|
|
'./foo': './foo.esm.js',
|
|
},
|
|
} as unknown as PackageJson
|
|
);
|
|
|
|
expect(utils.writeJsonFile).toHaveBeenCalledWith(expect.anything(), {
|
|
main: './index.esm.js',
|
|
module: './index.esm.js',
|
|
type: 'module',
|
|
types: './index.esm.d.ts',
|
|
exports: {
|
|
'./foo': './foo.esm.js',
|
|
},
|
|
});
|
|
|
|
spy.mockRestore();
|
|
});
|
|
});
|
|
});
|