Skip to content

Commit 3a04472

Browse files
Light2Darkclaude
andauthored
fix(ci): ship src/data/*.json in published tarball (#148)
Since v0.2.5 the published npm tarball has been missing `src/data/common-keywords.json` and `src/data/duckdb-keywords.json`, leaving the `./data/*` exports dead (404 / unresolved module for consumers). PR #128 split the release into separate `build`/`publish` jobs, but the build job's `upload-artifact` step omitted `src/`, so those files weren't on disk when the publish job ran `pnpm publish` — and npm silently drops `files` entries that don't exist. This adds `src/data/` to the artifact path so the keyword JSON files reach the publish job. It also adds a vitest guard (`src/__tests__/package-exports.test.ts`) that runs `npm pack` and asserts every `src/`-based `exports` target is present in the tarball, so the regression can't recur silently. Verified: `npm pack --dry-run` now lists both JSON files; the new test passes against the current package and fails if `src/data/*.json` is dropped from `files`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent faee1f6 commit 3a04472

2 files changed

Lines changed: 53 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ jobs:
6767
name: package
6868
path: |
6969
dist/
70+
src/data/
7071
package.json
7172
README.md
7273
LICENSE
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { execSync } from "node:child_process";
2+
import { readFileSync } from "node:fs";
3+
import { join } from "node:path";
4+
import { describe, expect, it } from "vitest";
5+
6+
const repoRoot = join(__dirname, "../..");
7+
8+
interface PackedFile {
9+
path: string;
10+
}
11+
12+
/**
13+
* Collect every string file path referenced anywhere in the package.json
14+
* `exports` map (recursing through conditional-export objects like
15+
* `{ import: { types, default } }`).
16+
*/
17+
function collectExportTargets(exportsField: unknown, out: string[] = []): string[] {
18+
if (typeof exportsField === "string") {
19+
out.push(exportsField);
20+
} else if (exportsField && typeof exportsField === "object") {
21+
for (const value of Object.values(exportsField as Record<string, unknown>)) {
22+
collectExportTargets(value, out);
23+
}
24+
}
25+
return out;
26+
}
27+
28+
describe("published package", () => {
29+
// Run the real `npm pack` so we assert against the actual tarball contents,
30+
// not the source tree. Regression guard for the `./data/*` exports that
31+
// shipped dead in 0.2.5–0.2.7 because `src/data/` was missing at publish time.
32+
const packed: PackedFile[] = JSON.parse(
33+
execSync("npm pack --dry-run --json", { cwd: repoRoot, encoding: "utf8" }),
34+
)[0].files;
35+
const packedPaths = new Set(packed.map((f) => f.path));
36+
37+
const pkg = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8"));
38+
39+
// Only assert source-committed targets (e.g. src/data/*.json). `dist/*`
40+
// targets are intentionally skipped: in CI `pnpm test` runs before
41+
// `pnpm build`, so dist does not exist yet when this test executes.
42+
const sourceTargets = collectExportTargets(pkg.exports)
43+
.map((p) => p.replace(/^\.\//, ""))
44+
.filter((p) => p.startsWith("src/"));
45+
46+
it("includes every src/ export target in the tarball", () => {
47+
expect(sourceTargets.length).toBeGreaterThan(0);
48+
for (const target of sourceTargets) {
49+
expect(packedPaths, `${target} is referenced by exports but missing from the npm tarball`).toContain(target);
50+
}
51+
});
52+
});

0 commit comments

Comments
 (0)