An Vitest util to get all exported APIs of a package and prevent unintended breaking changes.
npm i -D vitest-package-exports
Update your Vitest config:
export default defineConfig({
test: {
server: {
deps: {
inline: [
'vitest-package-exports',
],
},
},
},
})
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:
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
When getPackageExportsManifest
is called, it will:
- Find the
package.json
file in the current working directory. - Parse the
exports
field in thepackage.json
file. - Depend on the
importMode
, it will resolve the exports to the corresponding import path.
package
: Import from package name directly, for exampleimport('vitest/config')
dist
: Import the module from the definedexports
entry, for exampleimport('./dist/config.mjs')
src
: Import the module from thesrc
directory (replacedist
withsrc
), for exampleimport('./src/config.ts')
- 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) - Return the manifest of the exported APIs.
MIT License © Anthony Fu