const process = require('node:process'); const Ajv = require('ajv'); const addFormats = require('ajv-formats'); const fs = require('fs'); const path = require('path'); const sizeOf = require('image-size'); const schema = require('./token-schema.json'); const ajv = new Ajv(); addFormats(ajv); const validate = ajv.compile(schema); const dataDirectoryPath = path.resolve(process.cwd(), 'data'); const iconsDirectoryPath = path.resolve(dataDirectoryPath, 'icons'); async function validateJsonFile(filePath, iconUsage) { // exclude file "list.json" if (path.basename(filePath) === 'list.json') { return; } const data = JSON.parse(fs.readFileSync(filePath, 'utf8')); const valid = validate(data); if (valid) { console.log(`File ${path.basename(filePath)} is valid.`); const iconFileName = data.iconFileName.toLowerCase(); const iconFilePath = path.join(iconsDirectoryPath, iconFileName); if (!fs.existsSync(iconFilePath)) { throw new Error(`File ${path.basename(filePath)} is invalid: Icon file ${iconFileName} does not exist.`); } if (iconUsage[iconFileName]) { iconUsage[iconFileName]++; } else { iconUsage[iconFileName] = 1; } } else { throw new Error(`File ${path.basename(filePath)} is invalid: ${JSON.stringify(validate.errors)}`); } } function validatePngFile(filePath, iconUsage) { if (path.extname(filePath) !== '.png') { throw new Error(`File ${path.basename(filePath)} is invalid: Image must be in PNG format.`); } const dimensions = sizeOf(filePath); const fileName = path.basename(filePath).toLowerCase(); const MIN_SIZE = 128; const MAX_SIZE = 1024; if (dimensions.width !== dimensions.height || dimensions.width < MIN_SIZE || dimensions.width > MAX_SIZE) { throw new Error(`File ${fileName} is invalid: Image must be square and at least ${MIN_SIZE}x${MIN_SIZE} pixels, Your image is ${dimensions.width}x${dimensions.height} pixels.`); } else if (!iconUsage[fileName] || iconUsage[fileName] > 1) { throw new Error(`File ${fileName} is invalid: Icon must be used in exactly one JSON file.`); } else { console.log(`File ${fileName} is valid.`); } } function validateAllFilesInDirectory(directoryPath, extension, validationFunction, iconUsage) { const files = fs.readdirSync(directoryPath); const fileNames = new Set(); files.forEach(file => { const filePath = path.join(directoryPath, file); const fileName = path.basename(file, extension).toLowerCase(); if (fileNames.has(fileName)) { throw new Error(`File ${file} is invalid: Duplicate file name (case insensitive).`); } else { fileNames.add(fileName); if (path.extname(file) === extension) { validationFunction(filePath, iconUsage); } } }); } async function validateJsonFiles(iconUsage) { const files = fs.readdirSync(dataDirectoryPath); const fileNames = new Set(); for (const file of files) { const filePath = path.join(dataDirectoryPath, file); const fileName = path.basename(file, '.json').toLowerCase(); if (fileNames.has(fileName)) { throw new Error(`File ${file} is invalid: Duplicate file name (case insensitive).`); } else { fileNames.add(fileName); if (path.extname(file) === '.json') { await validateJsonFile(filePath, iconUsage); } } } } function validatePngFiles(iconUsage) { validateAllFilesInDirectory(iconsDirectoryPath, '.png', validatePngFile, iconUsage); } (async () => { const iconUsage = {}; try { await validateJsonFiles(iconUsage); validatePngFiles(iconUsage); console.log('\x1b[32m%s\x1b[0m', 'All files are valid.'); } catch (error) { console.error(error.message); process.exit(1); } })();