at main 5.2 kB view raw
1/* eslint-disable no-restricted-syntax, no-await-in-loop */ 2import fs from 'fs'; 3import path from 'path'; 4import { simpleGit } from 'simple-git'; 5import semver from 'semver'; 6import { readSvgDirectory } from '@lucide/helpers'; 7 8const DATE_OF_FORK = '2020-06-08T16:39:52+0100'; 9 10const git = simpleGit(); 11 12const currentDir = process.cwd(); 13const ICONS_DIR = path.resolve(currentDir, '../icons'); 14const iconJsonFiles = readSvgDirectory(ICONS_DIR, '.json'); 15const location = path.resolve(currentDir, '.vitepress/data', 'releaseMetaData.json'); 16const releaseMetaDataDirectory = path.resolve(currentDir, '.vitepress/data', 'releaseMetadata'); 17 18const allowedIconNameWithDoubleRelease = ['slash']; 19 20if (fs.existsSync(location)) { 21 fs.unlinkSync(location); 22} 23 24if (fs.existsSync(releaseMetaDataDirectory)) { 25 fs.rmSync(releaseMetaDataDirectory, { recursive: true, force: true }); 26} 27 28if (!fs.existsSync(releaseMetaDataDirectory)) { 29 fs.mkdirSync(releaseMetaDataDirectory); 30} 31 32const fetchAllReleases = async () => { 33 await git.fetch('https://github.com/lucide-icons/lucide.git', '--tags'); 34 35 return Promise.all( 36 (await git.tag(['-l'])) 37 .trim() 38 .split(/\n/) 39 .filter((tag) => semver.valid(tag)) 40 .sort(semver.compare), 41 ); 42}; 43 44const tags = await fetchAllReleases(); 45 46const comparisonsPromises = tags.map(async (tag, index) => { 47 const previousTag = tags[index - 1]; 48 49 if (!previousTag) return undefined; 50 51 const diff = await git.diff(['--name-status', '--oneline', previousTag, tag]); 52 const files = diff.split('\n').map((line) => { 53 const [status, file, renamedFile] = line.split('\t'); 54 55 return { status, file, renamedFile }; 56 }); 57 58 const iconFiles = files.filter(({ file }) => file != null && file.startsWith('icons/')); 59 let date = (await git.show(['-s', '--format=%cI', tag])).trim(); 60 61 // Fallback to dat of fork if date is not valid 62 if (!date.startsWith('20')) { 63 date = DATE_OF_FORK; 64 } 65 66 return { 67 tag, 68 date, 69 iconFiles, 70 }; 71}); 72 73const comparisons = await Promise.all(comparisonsPromises); 74const newReleaseMetaData = {}; 75 76comparisons.forEach(({ tag, iconFiles, date } = {}) => { 77 if (tag == null) return; 78 79 iconFiles.forEach(({ status, file, renamedFile }) => { 80 if (file.endsWith('.json')) return; 81 82 const version = tag.replace('v', ''); 83 const iconName = path.basename(file, '.svg'); 84 85 if (newReleaseMetaData[iconName] == null) newReleaseMetaData[iconName] = {}; 86 87 const releaseData = { 88 version, 89 date, 90 }; 91 92 if (status.startsWith('R')) { 93 // Make sure set the old one as well 94 newReleaseMetaData[iconName].changedRelease = { 95 version, 96 date, 97 }; 98 99 const renamedIconName = path.basename(renamedFile, '.svg'); 100 101 if (newReleaseMetaData[renamedIconName] == null) { 102 newReleaseMetaData[renamedIconName] = {}; 103 } 104 105 newReleaseMetaData[renamedIconName].changedRelease = { 106 version, 107 date, 108 }; 109 } 110 111 if (status === 'A') { 112 if ( 113 'changedRelease' in newReleaseMetaData[iconName] && 114 !allowedIconNameWithDoubleRelease.includes(iconName) 115 ) { 116 throw new Error(`Icon '${iconName}' has already changedRelease set.`); 117 } 118 119 newReleaseMetaData[iconName].createdRelease = releaseData; 120 newReleaseMetaData[iconName].changedRelease = releaseData; 121 } 122 if (status === 'M') { 123 newReleaseMetaData[iconName].changedRelease = { 124 version, 125 date, 126 }; 127 } 128 }); 129}); 130 131const defaultReleaseMetaData = { 132 createdRelease: { 133 version: '0.0.0', 134 date: DATE_OF_FORK, 135 }, 136 changedRelease: { 137 version: '0.0.0', 138 date: DATE_OF_FORK, 139 }, 140}; 141 142try { 143 const releaseMetaData = await Promise.all( 144 iconJsonFiles.map(async (iconJsonFile) => { 145 const iconName = path.basename(iconJsonFile, '.json'); 146 const metaDir = path.resolve(releaseMetaDataDirectory, `${iconName}.json`); 147 148 if (!(iconName in newReleaseMetaData)) { 149 console.error(`Could not find release metadata for icon '${iconName}'.`); 150 } 151 152 const contents = { 153 ...defaultReleaseMetaData, 154 ...(newReleaseMetaData[iconName] ?? {}), 155 }; 156 157 const metaData = await fs.promises.readFile(path.join(ICONS_DIR, iconJsonFile), 'utf-8'); 158 const iconMetaData = JSON.parse(metaData); 159 const aliases = iconMetaData.aliases ?? []; 160 161 if (aliases.length) { 162 aliases 163 .map((alias) => (typeof alias === 'string' ? alias : alias.name)) 164 .forEach((alias) => { 165 if (!(alias in newReleaseMetaData)) { 166 return; 167 } 168 169 contents.createdRelease = 170 newReleaseMetaData[alias].createdRelease ?? defaultReleaseMetaData.createdRelease; 171 }); 172 } 173 174 const output = JSON.stringify(contents, null, 2); 175 await fs.promises.writeFile(metaDir, output, 'utf-8'); 176 177 return [iconName, contents]; 178 }), 179 ); 180 await fs.promises.writeFile( 181 location, 182 JSON.stringify(Object.fromEntries(releaseMetaData), null, 2), 183 'utf-8', 184 ); 185 186 console.log('Successfully written icon release meta files'); 187} catch (error) { 188 throw new Error(`Something went wrong generating icon release meta cache file,\n ${error}`); 189}