-
Notifications
You must be signed in to change notification settings - Fork 16.9k
Expand file tree
/
Copy pathdirectory-entry-ids
More file actions
executable file
·177 lines (149 loc) · 4.77 KB
/
Copy pathdirectory-entry-ids
File metadata and controls
executable file
·177 lines (149 loc) · 4.77 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#! /usr/bin/env node
import { readFileSync, writeFileSync, readdirSync } from "fs";
import { join, dirname } from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Parse CLI flags
const fixMode = process.argv.includes("--fix");
// Character set from the formula
const charset = "abcdefghijkmnopqrstuvwxyzACDEFGHJKLMNPQRTUVWXY34679";
const ID_LENGTH = 6;
const charsetSet = new Set(charset);
// Validate that an ID matches the expected length and character composition.
// Returns an array of human-readable problems (empty if the ID is valid).
function validateId(id) {
const problems = [];
if (id.length !== ID_LENGTH) {
problems.push(`length ${id.length} (expected ${ID_LENGTH})`);
}
const invalidChars = [...new Set([...id].filter((c) => !charsetSet.has(c)))];
if (invalidChars.length > 0) {
problems.push(`invalid character(s): ${invalidChars.join(", ")}`);
}
return problems;
}
// Generate a random 6-character ID
function generateId(existingIds) {
let id = "";
let attempts = 0;
const maxAttempts = 1000;
do {
id = "";
for (let i = 0; i < ID_LENGTH; i++) {
const randomIndex = Math.floor(Math.random() * charset.length);
id += charset[randomIndex];
}
attempts++;
if (attempts > maxAttempts) {
throw new Error(
`Failed to generate unique ID after ${maxAttempts} attempts`,
);
}
} while (existingIds.has(id));
return id;
}
// Process all .yaml files in the directory
const dir = join(__dirname, "../src/content/directory");
const files = readdirSync(dir).filter((f) => f.endsWith(".yaml"));
console.log(`Found ${files.length} .yaml files\n`);
// Track all existing IDs
const existingIds = new Map(); // id -> filename
const errors = [];
let addedCount = 0;
let fixedCount = 0;
// Rewrite a file's first line with a new ID, replacing any existing `id:` line
// (or prepending one if missing).
function writeIdToFile(filePath, newId, content) {
const lines = content.split("\n");
let newContent;
if (lines[0].startsWith("id: ")) {
lines[0] = `id: ${newId}`;
newContent = lines.join("\n");
} else {
newContent = `id: ${newId}\n${content}`;
}
writeFileSync(filePath, newContent, "utf-8");
}
// First pass: collect existing IDs and check for issues
for (const file of files) {
const filePath = join(dir, file);
const content = readFileSync(filePath, "utf-8");
const lines = content.split("\n");
// Check if file has an id on the first line
if (lines[0].startsWith("id: ")) {
const id = lines[0].substring(4).trim();
// Validate length and character composition
const problems = validateId(id);
const isDuplicate = existingIds.has(id);
const isMalformed = problems.length > 0;
if (!isDuplicate && !isMalformed) {
existingIds.set(id, file);
console.log(`✓ ${file} - has id: ${id}`);
continue;
}
// Build a reason string for logging/errors
const reasons = [];
if (isDuplicate) {
reasons.push(`duplicate of ${existingIds.get(id)}`);
}
if (isMalformed) {
reasons.push(`malformed (${problems.join("; ")})`);
}
const reason = reasons.join("; ");
if (fixMode) {
const newId = generateId(existingIds);
existingIds.set(newId, file);
writeIdToFile(filePath, newId, content);
console.log(
`🔧 ${file} - replaced id "${id}" with "${newId}" (${reason})`,
);
fixedCount++;
} else {
errors.push(`❌ ${file} - id "${id}" ${reason}`);
// Only track the first occurrence so later duplicates still flag.
if (!isDuplicate) {
existingIds.set(id, file);
}
}
}
}
// Second pass: add IDs to files that don't have them
for (const file of files) {
const filePath = join(dir, file);
const content = readFileSync(filePath, "utf-8");
// Check if file already has an id on the first line
if (content.startsWith("id: ")) {
continue;
}
// Generate a unique ID
const id = generateId(existingIds);
existingIds.set(id, file);
// Add the id as the first line
const newContent = `id: ${id}\n${content}`;
writeFileSync(filePath, newContent, "utf-8");
console.log(`✓ ${file} - added id: ${id}`);
addedCount++;
}
// Report results
console.log(`\n${"=".repeat(50)}`);
console.log(`Summary:`);
console.log(` Total files: ${files.length}`);
console.log(
` Files with existing IDs: ${existingIds.size - addedCount - fixedCount}`,
);
console.log(` Files with added IDs: ${addedCount}`);
if (fixMode) {
console.log(` Files with fixed IDs: ${fixedCount}`);
}
console.log(` Unique IDs: ${existingIds.size}`);
if (errors.length > 0) {
console.log(`\n${"=".repeat(50)}`);
console.log(`Errors found:`);
errors.forEach((error) => console.log(error));
console.log(`\nRe-run with --fix to regenerate problematic IDs.`);
process.exit(1);
} else {
console.log(` Errors: 0`);
console.log(`\n✅ All IDs are unique and properly placed!`);
}

