-
Notifications
You must be signed in to change notification settings - Fork 525
Expand file tree
/
Copy pathcheck-exports.ts
More file actions
117 lines (98 loc) · 3.04 KB
/
check-exports.ts
File metadata and controls
117 lines (98 loc) · 3.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { existsSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { readFileSync } from "node:fs";
import fg from "fast-glob";
/**
* Recursively extract all file paths from an exports object
*/
function extractFilePaths(
exports: unknown,
paths: Set<string> = new Set()
): Set<string> {
if (typeof exports === "string") {
// Simple string path
paths.add(exports);
} else if (Array.isArray(exports)) {
// Array of paths
for (const item of exports) {
extractFilePaths(item, paths);
}
} else if (exports && typeof exports === "object") {
// Object with conditions (import, require, types, default, etc.)
for (const value of Object.values(exports)) {
extractFilePaths(value, paths);
}
}
return paths;
}
/**
* Check if all files referenced in a package's exports field exist
*/
function checkPackage(packageJsonPath: string): {
packageName: string;
missing: string[];
} {
const packageDir = dirname(packageJsonPath);
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
const packageName = packageJson.name || packageJsonPath;
const missing: string[] = [];
if (!packageJson.exports) {
// No exports field, nothing to check
return { packageName, missing };
}
// Extract all file paths from exports
const filePaths = extractFilePaths(packageJson.exports);
// Check if each file exists
for (const filePath of filePaths) {
// Skip non-file paths (like package names or special conditions)
if (!filePath.startsWith(".")) {
continue;
}
const fullPath = resolve(packageDir, filePath);
if (!existsSync(fullPath)) {
missing.push(filePath);
}
}
return { packageName, missing };
}
// Main execution
async function main() {
console.log("Checking package exports...\n");
// Find all package.json files in packages directory
const packageJsonFiles: string[] = [];
for await (const file of await fg.glob("packages/*/package.json")) {
if (file.includes("node_modules")) continue;
packageJsonFiles.push(file);
}
if (packageJsonFiles.length === 0) {
console.error("No packages found!");
process.exit(1);
}
const results: Array<{ packageName: string; missing: string[] }> = [];
// Check each package
for (const packageJsonPath of packageJsonFiles) {
const result = checkPackage(packageJsonPath);
results.push(result);
if (result.missing.length === 0) {
console.log(`✓ ${result.packageName} - all exports valid`);
} else {
console.error(`✗ ${result.packageName} - missing files:`);
for (const file of result.missing) {
console.error(` - ${file}`);
}
}
}
// Summary
const failed = results.filter((r) => r.missing.length > 0);
if (failed.length > 0) {
console.error(
`\n${failed.length} of ${results.length} packages have missing export files!`
);
process.exit(1);
}
console.log(`\nAll ${results.length} packages have valid exports!`);
}
main().catch((error) => {
console.error("Error:", error);
process.exit(1);
});