Skip to content

An Vitest util to get all exported APIs of a package and prevent unintended breaking changes.

License

Notifications You must be signed in to change notification settings

antfu/vitest-package-exports

Repository files navigation

vitest-package-exports

npm version npm downloads bundle JSDocs License

An Vitest util to get all exported APIs of a package and prevent unintended breaking changes.

Install

npm i -D vitest-package-exports

Update your Vitest config:

export default defineConfig({
  test: {
    server: {
      deps: {
        inline: [
          'vitest-package-exports',
        ],
      },
    },
  },
})

Usage

A typical usage is to make a snapshot to freeze the exported APIs of a package:

import { fileURLToPath } from 'node:url'
import { expect, it } from 'vitest'
import { getPackageExportsManifest } from 'vitest-package-exports'

it('exports snapshot', async () => {
  const manifest = await getPackageExportsManifest({
    importMode: 'src', // or 'dist' or 'package'
    cwd: fileURLToPath(import.meta.url),
  })

  expect(manifest.exports)
    .toMatchInlineSnapshot(`
      {
        ".": {
          "getPackageExportsManifest": "function",
        },
      }
    `)
})

Everytime you adding or remove the exported APIs, the snapshot will break and requires explicit review. This would help you to catch unintended breaking changes, or unintentional internal leaks.

For example, if I renamed the getPackageExportsManifest function to getPackageExportsManifestRenamed, the test will fail until I update the snapshot:

Image

If you want a cleaner snapshot:

You can use js-yaml to format the object:

import { fileURLToPath } from 'node:url'
import yaml from 'js-yaml' // <---
import { expect, it } from 'vitest'
import { getPackageExportsManifest } from 'vitest-package-exports'

it('exports snapshot', async () => {
  const manifest = await getPackageExportsManifest({
    importMode: 'src',
    cwd: fileURLToPath(import.meta.url),
  })

  expect(yaml.dump(manifest.exports)) // <---
    .toMatchInlineSnapshot(`
      .:
        getPackageExportsManifest: function
    `)
})

A more complete usage example in Shiki

How it works

When getPackageExportsManifest is called, it will:

  1. Find the package.json file in the current working directory.
  2. Parse the exports field in the package.json file.
  3. Depend on the importMode, it will resolve the exports to the corresponding import path.
  • package: Import from package name directly, for example import('vitest/config')
  • dist: Import the module from the defined exports entry, for example import('./dist/config.mjs')
  • src: Import the module from the src directory (replace dist with src), for example import('./src/config.ts')
  1. For each entry, it will import the module at runtime to get the exports object. Essentially Object.keys(await import(entry)) (so it's better to only use this in sandboxed environments like Vitest)
  2. Return the manifest of the exported APIs.

Sponsors

License

MIT License © Anthony Fu

About

An Vitest util to get all exported APIs of a package and prevent unintended breaking changes.

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Sponsor this project