1import { readdir, readFile } from "node:fs/promises";
2import path from "node:path";
3
4const packagesDirectory = path.resolve(import.meta.dir, "../packages");
5const packageDirectories = await readdir(packagesDirectory, {
6 withFileTypes: true,
7});
8const publishable: Array<{ name: string; version: string }> = [];
9
10for (const entry of packageDirectories) {
11 if (!entry.isDirectory()) continue;
12 const manifestPath = path.join(packagesDirectory, entry.name, "package.json");
13 const manifest = JSON.parse(await readFile(manifestPath, "utf8")) as Record<
14 string,
15 unknown
16 >;
17 if (manifest.private === true) continue;
18
19 const missing = [
20 "name",
21 "version",
22 "description",
23 "homepage",
24 "license",
25 "author",
26 "keywords",
27 "repository",
28 "bugs",
29 "files",
30 "exports",
31 ].filter((field) => manifest[field] == null);
32 const publishConfig = manifest.publishConfig as
33 | { access?: unknown }
34 | undefined;
35 if (publishConfig?.access !== "public") {
36 missing.push("publishConfig.access=public");
37 }
38 if (missing.length > 0) {
39 throw new Error(
40 `${entry.name}: missing publish metadata: ${missing.join(", ")}`,
41 );
42 }
43
44 publishable.push({
45 name: manifest.name as string,
46 version: manifest.version as string,
47 });
48}
49
50const versions = new Set(publishable.map(({ version }) => version));
51if (versions.size !== 1) {
52 throw new Error(
53 `Publishable packages must share one version: ${publishable
54 .map(({ name, version }) => `${name}@${version}`)
55 .join(", ")}`,
56 );
57}
58
59console.log(
60 `Validated ${publishable.length} publishable packages at ${publishable[0]?.version}`,
61);