A snapshot serializer that normalizes system-specific paths into stable, readable placeholders — designed for Vitest, Jest, and Rstest.
- Stabilize pnpm dependencies path in snapshot (including
enableGlobalVirtualStore) - Transform win32 path to posix path
- Replace absolute paths with placeholders (
<ROOT>,<WORKSPACE>,<HOME>,<TEMP>) - Handle
file://protocol URLs - Escape EOL (
\r\n->\n) - Normalize ANSI color codes
// __snapshots__/index.test.ts.snap
// 😭 Without path-serializer — fragile, platform-specific, unreadable
{
"loader" : "D:\\user\\rspack\\node_modules\\.pnpm\\css-loader@6.11.0_@rspack+core@packages+rspack_webpack@5.94.0_@swc+core@1.4.0_@swc+helpers@0._jlcdgjlw2ezzhg43ml3d627wdu\\node_modules\\css-loader\\utils.ts"
}
// 😎 With path-serializer — stable, cross-platform, clean
{
"loader" : "<PNPM_INNER>/css-loader/utils.ts"
}# npm
npm install path-serializer -D
# pnpm
pnpm add path-serializer -D// vitest.setup.ts
import path from 'node:path';
import { expect } from 'vitest';
import { createSnapshotSerializer } from 'path-serializer';
expect.addSnapshotSerializer(
createSnapshotSerializer({
root: path.join(__dirname, '..'),
}),
);expect.addSnapshotSerializer(
createSnapshotSerializer({
root: path.join(__dirname, '../..'),
workspace: path.join(__dirname, '..'),
}),
);This replaces:
- Workspace paths →
<WORKSPACE>/... - Root paths →
<ROOT>/...
Use replace and replacePost to add custom path matchers:
expect.addSnapshotSerializer(
createSnapshotSerializer({
root: path.join(__dirname, '..'),
replace: [
{ match: /port\s\d+/, mark: 'PORT' },
{ match: '/specific/path', mark: 'CUSTOM' },
],
}),
);Use beforeSerialize and afterSerialize for custom pre/post processing:
expect.addSnapshotSerializer(
createSnapshotSerializer({
root: path.join(__dirname, '..'),
beforeSerialize: (val) => val.replace(/hash:\w{8}/g, 'hash:<HASH>'),
afterSerialize: (val) => val.trim(),
}),
);- Type:
string - Default:
process.cwd()
Repository root path. Paths under this directory are replaced with <ROOT>.
- Type:
string - Default:
''
Workspace root path (for monorepos). Paths under this directory are replaced with <WORKSPACE>.
- Type:
PathMatcher[]
Custom matchers applied before built-in replacements.
- Type:
PathMatcher[]
Custom matchers applied after built-in replacements.
- Type:
(val: string) => string
Transform the raw string before any replacements.
- Type:
(val: string) => string
Transform the final string after all replacements.
Toggle individual features (all enabled by default):
| Feature | Default | Description |
|---|---|---|
replaceWorkspace |
true |
/foo/packages/core/src → <WORKSPACE>/src |
replaceRoot |
true |
/foo/node_modules/.pnpm → <ROOT>/node_modules/.pnpm |
replaceWorkspaceWithFileProtocol |
true |
file:///foo/packages/core/src → <WORKSPACE>/src |
replaceRootWithFileProtocol |
true |
file:///foo/node_modules/.pnpm → <ROOT>/node_modules/.pnpm |
replacePnpmInner |
true |
Collapse pnpm's long .pnpm/... and global virtual store pnpm/store/.../links/... paths to <PNPM_INNER> |
replaceTmpDir |
true |
os.tmpdir() paths → <TEMP> |
replaceHomeDir |
true |
os.homedir() paths → <HOME> |
transformWin32Path |
true |
Convert D:\\foo\\bar to /d/foo/bar |
transformCLR |
true |
Normalize ANSI color escape codes |
escapeDoubleQuotes |
true |
Escape " to \" |
escapeEOL |
true |
Normalize \r\n to \n |
addDoubleQuotes |
true |
Wrap output in double quotes |
More details can be found in ./src/types.ts.