Skip to content

Commit 3efe7b5

Browse files
felix.rufmathiasschopmans
authored andcommitted
feat: add strict mode for data building
1 parent c844e6d commit 3efe7b5

File tree

3 files changed

+144
-14
lines changed

3 files changed

+144
-14
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,5 @@ If you want to change core functionality of the radar, you can clone this reposi
267267
radar's markdown-files, config.json and about.md in the `data` folder. Run `npm run build:data` to
268268
parse the markdown files and create a `data.json` and then run `npm run dev` to start the
269269
development server, which will be available at `http://localhost:3000/techradar` or the path
270-
you specified via `basePath`.
270+
you specified via `basePath`. Run `npm run build:data -- --strict` to break the build process
271+
when encountering errors.

scripts/buildData.ts

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import path from "path";
77

88
import nextConfig from "../next.config.js";
99
import config from "../src/lib/config";
10+
import ErrorHandler, { ErrorType, TextColor } from "./errorHandler.js";
1011
import Positioner from "./positioner";
1112

1213
import { Flag, Item } from "@/lib/types";
@@ -21,6 +22,7 @@ const quadrants = config.quadrants.map((q, i) => ({ ...q, position: i + 1 }));
2122
const quadrantIds = quadrants.map((q) => q.id);
2223
const tags = (config as { tags?: string[] }).tags || [];
2324
const positioner = new Positioner(size, quadrants, rings);
25+
const errorHandler = new ErrorHandler(quadrants, rings);
2426

2527
const marked = new Marked(
2628
markedHighlight({
@@ -170,17 +172,25 @@ function postProcessItems(items: Item[]): {
170172
const filteredItems = items.filter((item) => {
171173
// check if the items' quadrant and ring are valid
172174
if (!item.quadrant || !item.ring) {
173-
console.warn(`Item ${item.id} has no quadrant or ring`);
175+
errorHandler.processBuildErrors(ErrorType.NoQuadrant, item.id);
174176
return false;
175177
}
176178

177179
if (!quadrantIds.includes(item.quadrant)) {
178-
console.warn(`Item ${item.id} has invalid quadrant ${item.quadrant}`);
180+
errorHandler.processBuildErrors(
181+
ErrorType.InvalidQuadrant,
182+
item.id,
183+
item.quadrant,
184+
);
179185
return false;
180186
}
181187

182188
if (!ringIds.includes(item.ring)) {
183-
console.warn(`Item ${item.id} has invalid ring ${item.ring}`);
189+
errorHandler.processBuildErrors(
190+
ErrorType.InvalidRing,
191+
item.id,
192+
item.ring,
193+
);
184194
return false;
185195
}
186196

@@ -193,6 +203,8 @@ function postProcessItems(items: Item[]): {
193203
return true;
194204
});
195205

206+
errorHandler.checkForBuildErrors();
207+
196208
const releases = getUniqueReleases(filteredItems);
197209
const uniqueTags = getUniqueTags(filteredItems);
198210
const processedItems = filteredItems.map((item) => {
@@ -230,21 +242,30 @@ function postProcessItems(items: Item[]): {
230242
return { releases, tags: uniqueTags, items: processedItems };
231243
}
232244

233-
// Parse the data and write radar data to JSON file
234-
parseDirectory(dataPath("radar")).then((items) => {
245+
async function main() {
246+
// Parse the data and write radar data to JSON file
247+
const items = await parseDirectory(dataPath("radar"));
235248
const data = postProcessItems(items);
236249

237250
if (data.items.length === 0) {
238-
console.error("No valid radar items found.");
239-
console.log("Please check the markdown files in the `radar` directory.");
240-
process.exit(1);
251+
errorHandler.processBuildErrors(ErrorType.NoRadarItems);
241252
}
242253

254+
errorHandler.checkForBuildErrors(true);
255+
243256
const json = JSON.stringify(data, null, 2);
244257
fs.writeFileSync(dataPath("data.json"), json);
245-
});
246258

247-
// write about data to JSON file
248-
const about = readMarkdownFile(dataPath("about.md"));
249-
fs.writeFileSync(dataPath("about.json"), JSON.stringify(about, null, 2));
250-
console.log("ℹ️ Data written to data/data.json and data/about.json");
259+
// write about data to JSON file
260+
const about = readMarkdownFile(dataPath("about.md"));
261+
fs.writeFileSync(dataPath("about.json"), JSON.stringify(about, null, 2));
262+
console.log(
263+
"ℹ️ Data written to data/data.json and data/about.json\n\n" +
264+
errorHandler.colorizeBackground(
265+
" Build was successfull ",
266+
TextColor.Green,
267+
),
268+
);
269+
}
270+
271+
main();

scripts/errorHandler.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { Quadrant, Ring } from "@/lib/types";
2+
3+
export enum ErrorType {
4+
NoQuadrant = "Item {0} has no quadrant or ring",
5+
InvalidQuadrant = "Item {0} has invalid quadrant {1}\n\tvalid quadrants are: {2}",
6+
InvalidRing = "Item {0} has invalid ring {1}\n\tvalid rings are: {2}",
7+
NoRadarItems = "No valid radar items found. Please check the markdown files in the `radar` directory.",
8+
}
9+
10+
export enum TextColor {
11+
Default = 0,
12+
Black,
13+
Red = 31,
14+
Green = 32,
15+
Yellow = 33,
16+
Blue = 34,
17+
Mangenta = 35,
18+
Cyan = 36,
19+
White = 37,
20+
}
21+
22+
export default class ErrorHandler {
23+
private buildErrors: string[] = [];
24+
private quadrants: Quadrant[];
25+
private rings: Ring[];
26+
private isStrict: boolean;
27+
private supportsColor: boolean;
28+
29+
constructor(quadrants: Quadrant[], rings: Ring[]) {
30+
this.isStrict = process.argv.slice(2).includes("--strict");
31+
this.supportsColor = process.stdout.isTTY && process.env.TERM !== "dumb";
32+
this.quadrants = quadrants;
33+
this.rings = rings;
34+
console.log(`ℹ️ Build is${this.isStrict ? "" : " not"} in strict mode\n`);
35+
}
36+
37+
public processBuildErrors(errorType: ErrorType, ...args: string[]) {
38+
const errorHint = this.getErrorHint(errorType);
39+
const errorMsg = this.formatString(
40+
errorType.toString(),
41+
errorHint ? [...args, errorHint] : args,
42+
);
43+
this.buildErrors.push(errorMsg);
44+
}
45+
46+
public checkForBuildErrors(exitOnErr: boolean = false) {
47+
if (this.buildErrors.length > 0) {
48+
console.warn(
49+
this.colorizeBackground(
50+
`There ${this.buildErrors.length > 1 ? "are" : "is"} ${this.buildErrors.length} error${this.buildErrors.length > 1 ? "s" : ""} in your data build`,
51+
TextColor.Red,
52+
) +
53+
"\n\n" +
54+
this.buildErrors
55+
.map((error, index) => `${index + 1}. ${error}`)
56+
.join("\n") +
57+
"\n",
58+
);
59+
60+
if (this.isStrict || exitOnErr) {
61+
process.exit(1);
62+
}
63+
64+
this.buildErrors = [];
65+
}
66+
}
67+
68+
private getErrorHint(errorType: ErrorType) {
69+
switch (errorType) {
70+
case ErrorType.InvalidQuadrant:
71+
return this.quadrants.map((quadrant) => quadrant.id).join(", ");
72+
case ErrorType.InvalidRing:
73+
return this.rings.map((ring) => ring.id).join(", ");
74+
default:
75+
break;
76+
}
77+
}
78+
79+
public colorizeBackground(str: string, backgroundColor: TextColor) {
80+
if (this.supportsColor) {
81+
return `\x1b[${backgroundColor + 10}m${str}\x1b[${TextColor.Default}m`;
82+
}
83+
84+
return str;
85+
}
86+
87+
private formatString(msg: string, inserts: string[]) {
88+
return inserts.reduce(
89+
(acc, cur, index) =>
90+
acc.replaceAll(
91+
`{${index}}`,
92+
this.colorizeString(
93+
cur,
94+
index === 2 ? TextColor.Green : TextColor.Red,
95+
),
96+
),
97+
msg,
98+
);
99+
}
100+
101+
private colorizeString(str: string, textColor: TextColor) {
102+
if (this.supportsColor) {
103+
return `\x1b[${textColor}m${str}\x1b[${TextColor.Default}m`;
104+
}
105+
106+
return str;
107+
}
108+
}

0 commit comments

Comments
 (0)