feat: add strict mode for data building
This commit is contained in:
committed by
Mathias Schopmans
parent
c844e6dcbe
commit
3efe7b5b61
@@ -267,4 +267,5 @@ If you want to change core functionality of the radar, you can clone this reposi
|
|||||||
radar's markdown-files, config.json and about.md in the `data` folder. Run `npm run build:data` to
|
radar's markdown-files, config.json and about.md in the `data` folder. Run `npm run build:data` to
|
||||||
parse the markdown files and create a `data.json` and then run `npm run dev` to start the
|
parse the markdown files and create a `data.json` and then run `npm run dev` to start the
|
||||||
development server, which will be available at `http://localhost:3000/techradar` or the path
|
development server, which will be available at `http://localhost:3000/techradar` or the path
|
||||||
you specified via `basePath`.
|
you specified via `basePath`. Run `npm run build:data -- --strict` to break the build process
|
||||||
|
when encountering errors.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import path from "path";
|
|||||||
|
|
||||||
import nextConfig from "../next.config.js";
|
import nextConfig from "../next.config.js";
|
||||||
import config from "../src/lib/config";
|
import config from "../src/lib/config";
|
||||||
|
import ErrorHandler, { ErrorType, TextColor } from "./errorHandler.js";
|
||||||
import Positioner from "./positioner";
|
import Positioner from "./positioner";
|
||||||
|
|
||||||
import { Flag, Item } from "@/lib/types";
|
import { Flag, Item } from "@/lib/types";
|
||||||
@@ -21,6 +22,7 @@ const quadrants = config.quadrants.map((q, i) => ({ ...q, position: i + 1 }));
|
|||||||
const quadrantIds = quadrants.map((q) => q.id);
|
const quadrantIds = quadrants.map((q) => q.id);
|
||||||
const tags = (config as { tags?: string[] }).tags || [];
|
const tags = (config as { tags?: string[] }).tags || [];
|
||||||
const positioner = new Positioner(size, quadrants, rings);
|
const positioner = new Positioner(size, quadrants, rings);
|
||||||
|
const errorHandler = new ErrorHandler(quadrants, rings);
|
||||||
|
|
||||||
const marked = new Marked(
|
const marked = new Marked(
|
||||||
markedHighlight({
|
markedHighlight({
|
||||||
@@ -170,17 +172,25 @@ function postProcessItems(items: Item[]): {
|
|||||||
const filteredItems = items.filter((item) => {
|
const filteredItems = items.filter((item) => {
|
||||||
// check if the items' quadrant and ring are valid
|
// check if the items' quadrant and ring are valid
|
||||||
if (!item.quadrant || !item.ring) {
|
if (!item.quadrant || !item.ring) {
|
||||||
console.warn(`Item ${item.id} has no quadrant or ring`);
|
errorHandler.processBuildErrors(ErrorType.NoQuadrant, item.id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!quadrantIds.includes(item.quadrant)) {
|
if (!quadrantIds.includes(item.quadrant)) {
|
||||||
console.warn(`Item ${item.id} has invalid quadrant ${item.quadrant}`);
|
errorHandler.processBuildErrors(
|
||||||
|
ErrorType.InvalidQuadrant,
|
||||||
|
item.id,
|
||||||
|
item.quadrant,
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ringIds.includes(item.ring)) {
|
if (!ringIds.includes(item.ring)) {
|
||||||
console.warn(`Item ${item.id} has invalid ring ${item.ring}`);
|
errorHandler.processBuildErrors(
|
||||||
|
ErrorType.InvalidRing,
|
||||||
|
item.id,
|
||||||
|
item.ring,
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +203,8 @@ function postProcessItems(items: Item[]): {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
errorHandler.checkForBuildErrors();
|
||||||
|
|
||||||
const releases = getUniqueReleases(filteredItems);
|
const releases = getUniqueReleases(filteredItems);
|
||||||
const uniqueTags = getUniqueTags(filteredItems);
|
const uniqueTags = getUniqueTags(filteredItems);
|
||||||
const processedItems = filteredItems.map((item) => {
|
const processedItems = filteredItems.map((item) => {
|
||||||
@@ -230,21 +242,30 @@ function postProcessItems(items: Item[]): {
|
|||||||
return { releases, tags: uniqueTags, items: processedItems };
|
return { releases, tags: uniqueTags, items: processedItems };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
// Parse the data and write radar data to JSON file
|
// Parse the data and write radar data to JSON file
|
||||||
parseDirectory(dataPath("radar")).then((items) => {
|
const items = await parseDirectory(dataPath("radar"));
|
||||||
const data = postProcessItems(items);
|
const data = postProcessItems(items);
|
||||||
|
|
||||||
if (data.items.length === 0) {
|
if (data.items.length === 0) {
|
||||||
console.error("No valid radar items found.");
|
errorHandler.processBuildErrors(ErrorType.NoRadarItems);
|
||||||
console.log("Please check the markdown files in the `radar` directory.");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
errorHandler.checkForBuildErrors(true);
|
||||||
|
|
||||||
const json = JSON.stringify(data, null, 2);
|
const json = JSON.stringify(data, null, 2);
|
||||||
fs.writeFileSync(dataPath("data.json"), json);
|
fs.writeFileSync(dataPath("data.json"), json);
|
||||||
});
|
|
||||||
|
|
||||||
// write about data to JSON file
|
// write about data to JSON file
|
||||||
const about = readMarkdownFile(dataPath("about.md"));
|
const about = readMarkdownFile(dataPath("about.md"));
|
||||||
fs.writeFileSync(dataPath("about.json"), JSON.stringify(about, null, 2));
|
fs.writeFileSync(dataPath("about.json"), JSON.stringify(about, null, 2));
|
||||||
console.log("ℹ️ Data written to data/data.json and data/about.json");
|
console.log(
|
||||||
|
"ℹ️ Data written to data/data.json and data/about.json\n\n" +
|
||||||
|
errorHandler.colorizeBackground(
|
||||||
|
" Build was successfull ",
|
||||||
|
TextColor.Green,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
|||||||
108
scripts/errorHandler.ts
Normal file
108
scripts/errorHandler.ts
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import { Quadrant, Ring } from "@/lib/types";
|
||||||
|
|
||||||
|
export enum ErrorType {
|
||||||
|
NoQuadrant = "Item {0} has no quadrant or ring",
|
||||||
|
InvalidQuadrant = "Item {0} has invalid quadrant {1}\n\tvalid quadrants are: {2}",
|
||||||
|
InvalidRing = "Item {0} has invalid ring {1}\n\tvalid rings are: {2}",
|
||||||
|
NoRadarItems = "No valid radar items found. Please check the markdown files in the `radar` directory.",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TextColor {
|
||||||
|
Default = 0,
|
||||||
|
Black,
|
||||||
|
Red = 31,
|
||||||
|
Green = 32,
|
||||||
|
Yellow = 33,
|
||||||
|
Blue = 34,
|
||||||
|
Mangenta = 35,
|
||||||
|
Cyan = 36,
|
||||||
|
White = 37,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ErrorHandler {
|
||||||
|
private buildErrors: string[] = [];
|
||||||
|
private quadrants: Quadrant[];
|
||||||
|
private rings: Ring[];
|
||||||
|
private isStrict: boolean;
|
||||||
|
private supportsColor: boolean;
|
||||||
|
|
||||||
|
constructor(quadrants: Quadrant[], rings: Ring[]) {
|
||||||
|
this.isStrict = process.argv.slice(2).includes("--strict");
|
||||||
|
this.supportsColor = process.stdout.isTTY && process.env.TERM !== "dumb";
|
||||||
|
this.quadrants = quadrants;
|
||||||
|
this.rings = rings;
|
||||||
|
console.log(`ℹ️ Build is${this.isStrict ? "" : " not"} in strict mode\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public processBuildErrors(errorType: ErrorType, ...args: string[]) {
|
||||||
|
const errorHint = this.getErrorHint(errorType);
|
||||||
|
const errorMsg = this.formatString(
|
||||||
|
errorType.toString(),
|
||||||
|
errorHint ? [...args, errorHint] : args,
|
||||||
|
);
|
||||||
|
this.buildErrors.push(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public checkForBuildErrors(exitOnErr: boolean = false) {
|
||||||
|
if (this.buildErrors.length > 0) {
|
||||||
|
console.warn(
|
||||||
|
this.colorizeBackground(
|
||||||
|
`There ${this.buildErrors.length > 1 ? "are" : "is"} ${this.buildErrors.length} error${this.buildErrors.length > 1 ? "s" : ""} in your data build`,
|
||||||
|
TextColor.Red,
|
||||||
|
) +
|
||||||
|
"\n\n" +
|
||||||
|
this.buildErrors
|
||||||
|
.map((error, index) => `${index + 1}. ${error}`)
|
||||||
|
.join("\n") +
|
||||||
|
"\n",
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.isStrict || exitOnErr) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.buildErrors = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getErrorHint(errorType: ErrorType) {
|
||||||
|
switch (errorType) {
|
||||||
|
case ErrorType.InvalidQuadrant:
|
||||||
|
return this.quadrants.map((quadrant) => quadrant.id).join(", ");
|
||||||
|
case ErrorType.InvalidRing:
|
||||||
|
return this.rings.map((ring) => ring.id).join(", ");
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public colorizeBackground(str: string, backgroundColor: TextColor) {
|
||||||
|
if (this.supportsColor) {
|
||||||
|
return `\x1b[${backgroundColor + 10}m${str}\x1b[${TextColor.Default}m`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatString(msg: string, inserts: string[]) {
|
||||||
|
return inserts.reduce(
|
||||||
|
(acc, cur, index) =>
|
||||||
|
acc.replaceAll(
|
||||||
|
`{${index}}`,
|
||||||
|
this.colorizeString(
|
||||||
|
cur,
|
||||||
|
index === 2 ? TextColor.Green : TextColor.Red,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
msg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private colorizeString(str: string, textColor: TextColor) {
|
||||||
|
if (this.supportsColor) {
|
||||||
|
return `\x1b[${textColor}m${str}\x1b[${TextColor.Default}m`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user