Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ jobs:
name: package
path: |
dist/
src/data/
package.json
README.md
LICENSE
Expand Down
52 changes: 52 additions & 0 deletions src/__tests__/package-exports.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { execSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";

const repoRoot = join(__dirname, "../..");

interface PackedFile {
path: string;
}

/**
* Collect every string file path referenced anywhere in the package.json
* `exports` map (recursing through conditional-export objects like
* `{ import: { types, default } }`).
*/
function collectExportTargets(exportsField: unknown, out: string[] = []): string[] {
if (typeof exportsField === "string") {
out.push(exportsField);
} else if (exportsField && typeof exportsField === "object") {
for (const value of Object.values(exportsField as Record<string, unknown>)) {
collectExportTargets(value, out);
}
}
return out;
}

describe("published package", () => {
// Run the real `npm pack` so we assert against the actual tarball contents,
// not the source tree. Regression guard for the `./data/*` exports that
// shipped dead in 0.2.5–0.2.7 because `src/data/` was missing at publish time.
Comment on lines +30 to +31

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// not the source tree. Regression guard for the `./data/*` exports that
// shipped dead in 0.2.5–0.2.7 because `src/data/` was missing at publish time.
// not the source tree.

const packed: PackedFile[] = JSON.parse(
execSync("npm pack --dry-run --json", { cwd: repoRoot, encoding: "utf8" }),
)[0].files;
const packedPaths = new Set(packed.map((f) => f.path));

const pkg = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8"));

// Only assert source-committed targets (e.g. src/data/*.json). `dist/*`
// targets are intentionally skipped: in CI `pnpm test` runs before
// `pnpm build`, so dist does not exist yet when this test executes.
const sourceTargets = collectExportTargets(pkg.exports)
.map((p) => p.replace(/^\.\//, ""))
.filter((p) => p.startsWith("src/"));

it("includes every src/ export target in the tarball", () => {
expect(sourceTargets.length).toBeGreaterThan(0);
for (const target of sourceTargets) {
expect(packedPaths, `${target} is referenced by exports but missing from the npm tarball`).toContain(target);
}
});
});