mirror of
https://github.com/pheralb/svgl.git
synced 2025-12-29 08:01:36 +08:00
✨ Migrate /utils to Typescript + use tsx to run all scripts + improve types
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import { readdir, stat } from "fs/promises";
|
||||
import { join } from "path";
|
||||
|
||||
// ⚙️ Settings:
|
||||
const dir = join(process.cwd(), "static", "library");
|
||||
const sizeLimit = 21 * 1024; // 21 KB
|
||||
|
||||
function convertBytes(bytes: number, format: "KB" | "MB" = "KB"): string {
|
||||
if (format === "KB") {
|
||||
return (bytes / 1024).toFixed(2) + " KB";
|
||||
} else if (format === "MB") {
|
||||
return (bytes / (1024 * 1024)).toFixed(2) + " MB";
|
||||
} else {
|
||||
return 'Invalid format. Use "KB" or "MB".';
|
||||
}
|
||||
}
|
||||
|
||||
async function checkSize(): Promise<void> {
|
||||
const files = await readdir(dir);
|
||||
let maxSize = 0;
|
||||
const maxFiles: { filename: string; size: string }[] = [];
|
||||
|
||||
try {
|
||||
for (const file of files) {
|
||||
const filePath = join(dir, file);
|
||||
const stats = await stat(filePath);
|
||||
|
||||
if (stats.size >= sizeLimit) {
|
||||
maxFiles.push({
|
||||
filename: file,
|
||||
size: convertBytes(stats.size),
|
||||
});
|
||||
|
||||
if (stats.size > maxSize) {
|
||||
maxSize = stats.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maxFiles.length === 0) {
|
||||
console.log(`✅ All files are smaller than ${convertBytes(sizeLimit)}.`);
|
||||
} else {
|
||||
const message = `❌ There are files bigger than ${convertBytes(sizeLimit)}.`;
|
||||
throw new Error(message);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
} else {
|
||||
console.error("❌ Unexpected error:", error);
|
||||
}
|
||||
throw error;
|
||||
} finally {
|
||||
if (maxFiles.length > 0) {
|
||||
console.log("🔎 Files found:");
|
||||
console.table(maxFiles);
|
||||
}
|
||||
|
||||
console.log("⚙️ Settings:");
|
||||
console.log(`- 📁 Directory: ${dir}`);
|
||||
console.log(`- 🧱 Size limit: ${convertBytes(sizeLimit)}`);
|
||||
if (maxSize > 0) {
|
||||
console.log(`- 🔔 Max size found: ${convertBytes(maxSize)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run the function
|
||||
checkSize();
|
||||
@@ -1,67 +0,0 @@
|
||||
const { readdir, stat } = require('fs').promises;
|
||||
const { join } = require('path');
|
||||
|
||||
// For GitHub Actions:
|
||||
const core = require('@actions/core');
|
||||
|
||||
// 🔎 Settings:
|
||||
const dir = '../../static/library';
|
||||
const sizeLimit = 21504; // 21kb;
|
||||
|
||||
function convertBytes(bytes, format = 'KB') {
|
||||
if (format === 'KB') {
|
||||
return (bytes / 1024).toFixed(2) + ' KB';
|
||||
} else if (format === 'MB') {
|
||||
return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
|
||||
} else {
|
||||
return 'Invalid format. Use "KB" or "MB".';
|
||||
}
|
||||
}
|
||||
|
||||
async function checkSize() {
|
||||
const files = await readdir(dir);
|
||||
let maxSize = 0;
|
||||
let maxFiles = [];
|
||||
let message = '';
|
||||
|
||||
try {
|
||||
for (const file of files) {
|
||||
const filePath = join(dir, file);
|
||||
const stats = await stat(filePath);
|
||||
|
||||
if (stats.size >= sizeLimit) {
|
||||
maxFiles.push({
|
||||
filename: file,
|
||||
size: convertBytes(stats.size)
|
||||
});
|
||||
if (stats.size > maxSize) {
|
||||
maxSize = stats.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maxFiles.length === 0) {
|
||||
message = `- ✅ All files are smaller than ${convertBytes(sizeLimit)}`;
|
||||
core.setOutput('message', message);
|
||||
} else {
|
||||
message = `- ❌ There are files bigger than ${convertBytes(sizeLimit)}.`;
|
||||
throw new Error(message);
|
||||
}
|
||||
} catch (err) {
|
||||
core.setFailed(message);
|
||||
} finally {
|
||||
if (maxFiles.length > 0) {
|
||||
console.log('🔎 Files found:');
|
||||
console.table(maxFiles);
|
||||
}
|
||||
console.log('⚙️ Settings:');
|
||||
console.log(`- 📁 Directory: ${dir}`);
|
||||
console.log(`- 🧱 Size limit: ${convertBytes(sizeLimit)} bytes`);
|
||||
if (maxSize > 0) {
|
||||
console.log(`- 🔔 Max size found: ${convertBytes(maxSize, 'KB')}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run the function
|
||||
checkSize();
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "@svgl/check-size",
|
||||
"version": "1.0.0",
|
||||
"description": "Limit the size of your SVG files",
|
||||
"main": "index.js",
|
||||
"author": "@pheralb_",
|
||||
"license": "ISC",
|
||||
"keywords": [
|
||||
"svg",
|
||||
"size",
|
||||
"limit",
|
||||
"check"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "node index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "1.10.1",
|
||||
"@actions/github": "6.0.0"
|
||||
}
|
||||
}
|
||||
Generated
-193
@@ -1,193 +0,0 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@actions/core':
|
||||
specifier: 1.10.1
|
||||
version: 1.10.1
|
||||
'@actions/github':
|
||||
specifier: 6.0.0
|
||||
version: 6.0.0
|
||||
|
||||
packages:
|
||||
|
||||
'@actions/core@1.10.1':
|
||||
resolution: {integrity: sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==}
|
||||
|
||||
'@actions/github@6.0.0':
|
||||
resolution: {integrity: sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g==}
|
||||
|
||||
'@actions/http-client@2.2.0':
|
||||
resolution: {integrity: sha512-q+epW0trjVUUHboliPb4UF9g2msf+w61b32tAkFEwL/IwP0DQWgbCMM0Hbe3e3WXSKz5VcUXbzJQgy8Hkra/Lg==}
|
||||
|
||||
'@fastify/busboy@2.1.0':
|
||||
resolution: {integrity: sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
'@octokit/auth-token@4.0.0':
|
||||
resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/core@5.0.2':
|
||||
resolution: {integrity: sha512-cZUy1gUvd4vttMic7C0lwPed8IYXWYp8kHIMatyhY8t8n3Cpw2ILczkV5pGMPqef7v0bLo0pOHrEHarsau2Ydg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/endpoint@9.0.4':
|
||||
resolution: {integrity: sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/graphql@7.0.2':
|
||||
resolution: {integrity: sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/openapi-types@19.1.0':
|
||||
resolution: {integrity: sha512-6G+ywGClliGQwRsjvqVYpklIfa7oRPA0vyhPQG/1Feh+B+wU0vGH1JiJ5T25d3g1JZYBHzR2qefLi9x8Gt+cpw==}
|
||||
|
||||
'@octokit/plugin-paginate-rest@9.1.5':
|
||||
resolution: {integrity: sha512-WKTQXxK+bu49qzwv4qKbMMRXej1DU2gq017euWyKVudA6MldaSSQuxtz+vGbhxV4CjxpUxjZu6rM2wfc1FiWVg==}
|
||||
engines: {node: '>= 18'}
|
||||
peerDependencies:
|
||||
'@octokit/core': '>=5'
|
||||
|
||||
'@octokit/plugin-rest-endpoint-methods@10.2.0':
|
||||
resolution: {integrity: sha512-ePbgBMYtGoRNXDyKGvr9cyHjQ163PbwD0y1MkDJCpkO2YH4OeXX40c4wYHKikHGZcpGPbcRLuy0unPUuafco8Q==}
|
||||
engines: {node: '>= 18'}
|
||||
peerDependencies:
|
||||
'@octokit/core': '>=5'
|
||||
|
||||
'@octokit/request-error@5.0.1':
|
||||
resolution: {integrity: sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/request@8.1.6':
|
||||
resolution: {integrity: sha512-YhPaGml3ncZC1NfXpP3WZ7iliL1ap6tLkAp6MvbK2fTTPytzVUyUesBBogcdMm86uRYO5rHaM1xIWxigWZ17MQ==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/types@12.4.0':
|
||||
resolution: {integrity: sha512-FLWs/AvZllw/AGVs+nJ+ELCDZZJk+kY0zMen118xhL2zD0s1etIUHm1odgjP7epxYU1ln7SZxEUWYop5bhsdgQ==}
|
||||
|
||||
before-after-hook@2.2.3:
|
||||
resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==}
|
||||
|
||||
deprecation@2.3.1:
|
||||
resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==}
|
||||
|
||||
once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
|
||||
tunnel@0.0.6:
|
||||
resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==}
|
||||
engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'}
|
||||
|
||||
undici@5.28.2:
|
||||
resolution: {integrity: sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==}
|
||||
engines: {node: '>=14.0'}
|
||||
|
||||
universal-user-agent@6.0.1:
|
||||
resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==}
|
||||
|
||||
uuid@8.3.2:
|
||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||
hasBin: true
|
||||
|
||||
wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@actions/core@1.10.1':
|
||||
dependencies:
|
||||
'@actions/http-client': 2.2.0
|
||||
uuid: 8.3.2
|
||||
|
||||
'@actions/github@6.0.0':
|
||||
dependencies:
|
||||
'@actions/http-client': 2.2.0
|
||||
'@octokit/core': 5.0.2
|
||||
'@octokit/plugin-paginate-rest': 9.1.5(@octokit/core@5.0.2)
|
||||
'@octokit/plugin-rest-endpoint-methods': 10.2.0(@octokit/core@5.0.2)
|
||||
|
||||
'@actions/http-client@2.2.0':
|
||||
dependencies:
|
||||
tunnel: 0.0.6
|
||||
undici: 5.28.2
|
||||
|
||||
'@fastify/busboy@2.1.0': {}
|
||||
|
||||
'@octokit/auth-token@4.0.0': {}
|
||||
|
||||
'@octokit/core@5.0.2':
|
||||
dependencies:
|
||||
'@octokit/auth-token': 4.0.0
|
||||
'@octokit/graphql': 7.0.2
|
||||
'@octokit/request': 8.1.6
|
||||
'@octokit/request-error': 5.0.1
|
||||
'@octokit/types': 12.4.0
|
||||
before-after-hook: 2.2.3
|
||||
universal-user-agent: 6.0.1
|
||||
|
||||
'@octokit/endpoint@9.0.4':
|
||||
dependencies:
|
||||
'@octokit/types': 12.4.0
|
||||
universal-user-agent: 6.0.1
|
||||
|
||||
'@octokit/graphql@7.0.2':
|
||||
dependencies:
|
||||
'@octokit/request': 8.1.6
|
||||
'@octokit/types': 12.4.0
|
||||
universal-user-agent: 6.0.1
|
||||
|
||||
'@octokit/openapi-types@19.1.0': {}
|
||||
|
||||
'@octokit/plugin-paginate-rest@9.1.5(@octokit/core@5.0.2)':
|
||||
dependencies:
|
||||
'@octokit/core': 5.0.2
|
||||
'@octokit/types': 12.4.0
|
||||
|
||||
'@octokit/plugin-rest-endpoint-methods@10.2.0(@octokit/core@5.0.2)':
|
||||
dependencies:
|
||||
'@octokit/core': 5.0.2
|
||||
'@octokit/types': 12.4.0
|
||||
|
||||
'@octokit/request-error@5.0.1':
|
||||
dependencies:
|
||||
'@octokit/types': 12.4.0
|
||||
deprecation: 2.3.1
|
||||
once: 1.4.0
|
||||
|
||||
'@octokit/request@8.1.6':
|
||||
dependencies:
|
||||
'@octokit/endpoint': 9.0.4
|
||||
'@octokit/request-error': 5.0.1
|
||||
'@octokit/types': 12.4.0
|
||||
universal-user-agent: 6.0.1
|
||||
|
||||
'@octokit/types@12.4.0':
|
||||
dependencies:
|
||||
'@octokit/openapi-types': 19.1.0
|
||||
|
||||
before-after-hook@2.2.3: {}
|
||||
|
||||
deprecation@2.3.1: {}
|
||||
|
||||
once@1.4.0:
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
|
||||
tunnel@0.0.6: {}
|
||||
|
||||
undici@5.28.2:
|
||||
dependencies:
|
||||
'@fastify/busboy': 2.1.0
|
||||
|
||||
universal-user-agent@6.0.1: {}
|
||||
|
||||
uuid@8.3.2: {}
|
||||
|
||||
wrappy@1.0.2: {}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { readdir, stat, readFile, writeFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
|
||||
// ⚙️ Settings:
|
||||
const dir = join(process.cwd(), "static", "library");
|
||||
const fileType = ".svg";
|
||||
|
||||
async function fixViewbox(): Promise<void> {
|
||||
try {
|
||||
const files = await readdir(dir);
|
||||
|
||||
let totalSVGs = 0;
|
||||
let fixedCount = 0;
|
||||
const fixedFiles: string[] = [];
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = join(dir, file);
|
||||
const fileStat = await stat(filePath);
|
||||
|
||||
if (!fileStat.isFile() || !file.endsWith(fileType)) continue;
|
||||
|
||||
totalSVGs++;
|
||||
|
||||
const fileContent = await readFile(filePath, "utf-8");
|
||||
const viewBox = getViewBox(fileContent);
|
||||
const width = getWidth(fileContent);
|
||||
const height = getHeight(fileContent);
|
||||
|
||||
if (!width || !height) {
|
||||
continue; // saltar archivos inválidos
|
||||
}
|
||||
|
||||
if (!viewBox) {
|
||||
const newFileContent = fileContent.replace(
|
||||
"<svg",
|
||||
`<svg viewBox="0 0 ${width} ${height}"`,
|
||||
);
|
||||
await writeFile(filePath, newFileContent, "utf-8");
|
||||
fixedCount++;
|
||||
fixedFiles.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
console.table([{ "Total SVGs": totalSVGs, "Fixed SVGs:": fixedCount }]);
|
||||
|
||||
if (fixedFiles.length > 0) {
|
||||
console.table(fixedFiles.map((f) => ({ "Fixed SVG:": f })));
|
||||
}
|
||||
|
||||
console.log("🚀 Done.");
|
||||
} catch (error) {
|
||||
console.error("❌ Error while processing files:", error);
|
||||
}
|
||||
}
|
||||
|
||||
function getViewBox(content: string): string | null {
|
||||
const viewBoxRegex = /viewBox="([^"]+)"/;
|
||||
return viewBoxRegex.exec(content)?.[1] ?? null;
|
||||
}
|
||||
|
||||
function getWidth(content: string): string | null {
|
||||
const widthRegex = /width="([^"]+)"/;
|
||||
return widthRegex.exec(content)?.[1] ?? null;
|
||||
}
|
||||
|
||||
function getHeight(content: string): string | null {
|
||||
const heightRegex = /height="([^"]+)"/;
|
||||
return heightRegex.exec(content)?.[1] ?? null;
|
||||
}
|
||||
|
||||
fixViewbox();
|
||||
@@ -1,62 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const { readdir, stat } = require('fs').promises;
|
||||
const { readFile, writeFile } = require('fs/promises');
|
||||
const { join } = require('path');
|
||||
|
||||
// 🔎 Settings:
|
||||
const dir = '../../static/library';
|
||||
|
||||
async function fixViewbox() {
|
||||
const files = await readdir(dir);
|
||||
const fileType = 'svg';
|
||||
let message = '';
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = join(dir, file);
|
||||
const fileStat = await stat(filePath);
|
||||
if (fileStat.isFile() && file.endsWith(fileType)) {
|
||||
const fileContent = await readFile(filePath);
|
||||
const viewBox = getViewBox(fileContent);
|
||||
const width = getWidth(fileContent);
|
||||
const height = getHeight(fileContent);
|
||||
if (!viewBox) {
|
||||
const newFileContent = fileContent
|
||||
.toString()
|
||||
.replace('<svg', `<svg viewBox="0 0 ${width} ${height}"`);
|
||||
await writeFile(filePath, newFileContent);
|
||||
message = `🔔 File ${file} has been fixed.`;
|
||||
console.log(message);
|
||||
} else {
|
||||
message = `✅ File ${file} has already a viewBox.`;
|
||||
console.log(message);
|
||||
}
|
||||
} else {
|
||||
message = `❌ File ${file} is not a ${fileType} file.`;
|
||||
console.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
// Log the result:
|
||||
console.log('🚀 Done.');
|
||||
}
|
||||
|
||||
function getViewBox(fileContent) {
|
||||
const viewBoxRegex = /viewBox="(.+?)"/;
|
||||
const viewBox = viewBoxRegex.exec(fileContent);
|
||||
return viewBox ? viewBox[1] : null;
|
||||
}
|
||||
|
||||
function getWidth(fileContent) {
|
||||
const widthRegex = /width="(.+?)"/;
|
||||
const width = widthRegex.exec(fileContent);
|
||||
return width ? width[1] : null;
|
||||
}
|
||||
|
||||
function getHeight(fileContent) {
|
||||
const heightRegex = /height="(.+?)"/;
|
||||
const height = heightRegex.exec(fileContent);
|
||||
return height ? height[1] : null;
|
||||
}
|
||||
|
||||
// Run the function
|
||||
fixViewbox();
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"name": "@svgl/fix-viewbox",
|
||||
"version": "1.0.0",
|
||||
"description": "Add viewbox to svg files if not present.",
|
||||
"main": "index.js",
|
||||
"author": "@pheralb_",
|
||||
"license": "ISC",
|
||||
"keywords": [
|
||||
"svg",
|
||||
"size",
|
||||
"limit",
|
||||
"check"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "node index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "1.10.1",
|
||||
"@actions/github": "6.0.0"
|
||||
}
|
||||
}
|
||||
Generated
-193
@@ -1,193 +0,0 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@actions/core':
|
||||
specifier: 1.10.1
|
||||
version: 1.10.1
|
||||
'@actions/github':
|
||||
specifier: 6.0.0
|
||||
version: 6.0.0
|
||||
|
||||
packages:
|
||||
|
||||
'@actions/core@1.10.1':
|
||||
resolution: {integrity: sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==}
|
||||
|
||||
'@actions/github@6.0.0':
|
||||
resolution: {integrity: sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g==}
|
||||
|
||||
'@actions/http-client@2.2.0':
|
||||
resolution: {integrity: sha512-q+epW0trjVUUHboliPb4UF9g2msf+w61b32tAkFEwL/IwP0DQWgbCMM0Hbe3e3WXSKz5VcUXbzJQgy8Hkra/Lg==}
|
||||
|
||||
'@fastify/busboy@2.1.0':
|
||||
resolution: {integrity: sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
'@octokit/auth-token@4.0.0':
|
||||
resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/core@5.0.2':
|
||||
resolution: {integrity: sha512-cZUy1gUvd4vttMic7C0lwPed8IYXWYp8kHIMatyhY8t8n3Cpw2ILczkV5pGMPqef7v0bLo0pOHrEHarsau2Ydg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/endpoint@9.0.4':
|
||||
resolution: {integrity: sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/graphql@7.0.2':
|
||||
resolution: {integrity: sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/openapi-types@19.1.0':
|
||||
resolution: {integrity: sha512-6G+ywGClliGQwRsjvqVYpklIfa7oRPA0vyhPQG/1Feh+B+wU0vGH1JiJ5T25d3g1JZYBHzR2qefLi9x8Gt+cpw==}
|
||||
|
||||
'@octokit/plugin-paginate-rest@9.1.5':
|
||||
resolution: {integrity: sha512-WKTQXxK+bu49qzwv4qKbMMRXej1DU2gq017euWyKVudA6MldaSSQuxtz+vGbhxV4CjxpUxjZu6rM2wfc1FiWVg==}
|
||||
engines: {node: '>= 18'}
|
||||
peerDependencies:
|
||||
'@octokit/core': '>=5'
|
||||
|
||||
'@octokit/plugin-rest-endpoint-methods@10.2.0':
|
||||
resolution: {integrity: sha512-ePbgBMYtGoRNXDyKGvr9cyHjQ163PbwD0y1MkDJCpkO2YH4OeXX40c4wYHKikHGZcpGPbcRLuy0unPUuafco8Q==}
|
||||
engines: {node: '>= 18'}
|
||||
peerDependencies:
|
||||
'@octokit/core': '>=5'
|
||||
|
||||
'@octokit/request-error@5.0.1':
|
||||
resolution: {integrity: sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/request@8.1.6':
|
||||
resolution: {integrity: sha512-YhPaGml3ncZC1NfXpP3WZ7iliL1ap6tLkAp6MvbK2fTTPytzVUyUesBBogcdMm86uRYO5rHaM1xIWxigWZ17MQ==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/types@12.4.0':
|
||||
resolution: {integrity: sha512-FLWs/AvZllw/AGVs+nJ+ELCDZZJk+kY0zMen118xhL2zD0s1etIUHm1odgjP7epxYU1ln7SZxEUWYop5bhsdgQ==}
|
||||
|
||||
before-after-hook@2.2.3:
|
||||
resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==}
|
||||
|
||||
deprecation@2.3.1:
|
||||
resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==}
|
||||
|
||||
once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
|
||||
tunnel@0.0.6:
|
||||
resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==}
|
||||
engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'}
|
||||
|
||||
undici@5.28.2:
|
||||
resolution: {integrity: sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==}
|
||||
engines: {node: '>=14.0'}
|
||||
|
||||
universal-user-agent@6.0.1:
|
||||
resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==}
|
||||
|
||||
uuid@8.3.2:
|
||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||
hasBin: true
|
||||
|
||||
wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@actions/core@1.10.1':
|
||||
dependencies:
|
||||
'@actions/http-client': 2.2.0
|
||||
uuid: 8.3.2
|
||||
|
||||
'@actions/github@6.0.0':
|
||||
dependencies:
|
||||
'@actions/http-client': 2.2.0
|
||||
'@octokit/core': 5.0.2
|
||||
'@octokit/plugin-paginate-rest': 9.1.5(@octokit/core@5.0.2)
|
||||
'@octokit/plugin-rest-endpoint-methods': 10.2.0(@octokit/core@5.0.2)
|
||||
|
||||
'@actions/http-client@2.2.0':
|
||||
dependencies:
|
||||
tunnel: 0.0.6
|
||||
undici: 5.28.2
|
||||
|
||||
'@fastify/busboy@2.1.0': {}
|
||||
|
||||
'@octokit/auth-token@4.0.0': {}
|
||||
|
||||
'@octokit/core@5.0.2':
|
||||
dependencies:
|
||||
'@octokit/auth-token': 4.0.0
|
||||
'@octokit/graphql': 7.0.2
|
||||
'@octokit/request': 8.1.6
|
||||
'@octokit/request-error': 5.0.1
|
||||
'@octokit/types': 12.4.0
|
||||
before-after-hook: 2.2.3
|
||||
universal-user-agent: 6.0.1
|
||||
|
||||
'@octokit/endpoint@9.0.4':
|
||||
dependencies:
|
||||
'@octokit/types': 12.4.0
|
||||
universal-user-agent: 6.0.1
|
||||
|
||||
'@octokit/graphql@7.0.2':
|
||||
dependencies:
|
||||
'@octokit/request': 8.1.6
|
||||
'@octokit/types': 12.4.0
|
||||
universal-user-agent: 6.0.1
|
||||
|
||||
'@octokit/openapi-types@19.1.0': {}
|
||||
|
||||
'@octokit/plugin-paginate-rest@9.1.5(@octokit/core@5.0.2)':
|
||||
dependencies:
|
||||
'@octokit/core': 5.0.2
|
||||
'@octokit/types': 12.4.0
|
||||
|
||||
'@octokit/plugin-rest-endpoint-methods@10.2.0(@octokit/core@5.0.2)':
|
||||
dependencies:
|
||||
'@octokit/core': 5.0.2
|
||||
'@octokit/types': 12.4.0
|
||||
|
||||
'@octokit/request-error@5.0.1':
|
||||
dependencies:
|
||||
'@octokit/types': 12.4.0
|
||||
deprecation: 2.3.1
|
||||
once: 1.4.0
|
||||
|
||||
'@octokit/request@8.1.6':
|
||||
dependencies:
|
||||
'@octokit/endpoint': 9.0.4
|
||||
'@octokit/request-error': 5.0.1
|
||||
'@octokit/types': 12.4.0
|
||||
universal-user-agent: 6.0.1
|
||||
|
||||
'@octokit/types@12.4.0':
|
||||
dependencies:
|
||||
'@octokit/openapi-types': 19.1.0
|
||||
|
||||
before-after-hook@2.2.3: {}
|
||||
|
||||
deprecation@2.3.1: {}
|
||||
|
||||
once@1.4.0:
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
|
||||
tunnel@0.0.6: {}
|
||||
|
||||
undici@5.28.2:
|
||||
dependencies:
|
||||
'@fastify/busboy': 2.1.0
|
||||
|
||||
universal-user-agent@6.0.1: {}
|
||||
|
||||
uuid@8.3.2: {}
|
||||
|
||||
wrappy@1.0.2: {}
|
||||
@@ -0,0 +1,343 @@
|
||||
import type { iSVG } from "../src/types/svg";
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
|
||||
import { svgs } from "../src/data/svgs";
|
||||
import { optimizeSvg } from "../src/utils/optimizeSvg";
|
||||
import { parseSvgFilename } from "../src/utils/parseSvgFilename";
|
||||
import { parseReactSvgContent } from "../src/utils/parseReactSvgContent";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
// ⚙️ Settings:
|
||||
const REGENERATE_ALL = true;
|
||||
const SVGS_DATA = svgs;
|
||||
const PUBLIC_FOLDER = "static";
|
||||
const SHADCN_COMMAND = "shadcn build --output ./static/r";
|
||||
const OUTPUT_DIR = "./static/components-generated";
|
||||
|
||||
// 🛠️ Shadcn Schema:
|
||||
interface RegistryFile {
|
||||
path: string;
|
||||
type: string;
|
||||
target: string;
|
||||
}
|
||||
|
||||
interface RegistryItem {
|
||||
name: string;
|
||||
type: string;
|
||||
title: string;
|
||||
files: RegistryFile[];
|
||||
}
|
||||
|
||||
interface ShadcnSchema {
|
||||
$schema: string;
|
||||
name: string;
|
||||
homepage: string;
|
||||
items: RegistryItem[];
|
||||
}
|
||||
|
||||
const shadcnSchema: ShadcnSchema = {
|
||||
$schema: "https://ui.shadcn.com/schema/registry.json",
|
||||
name: "svgl",
|
||||
homepage: "https://svgl.app",
|
||||
items: [],
|
||||
};
|
||||
|
||||
// 🧑🚀 Function to prepare registry.json content:
|
||||
function prepareRegistryJson(): ShadcnSchema {
|
||||
const registryItems: RegistryItem[] = [];
|
||||
|
||||
SVGS_DATA.forEach((svg) => {
|
||||
if (!REGENERATE_ALL) return;
|
||||
const componentName = svg.title
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, "-")
|
||||
.replace(/[^a-z0-9-]/g, "");
|
||||
|
||||
const files: RegistryFile[] = [];
|
||||
|
||||
const svgPaths = extractSvgPaths(svg);
|
||||
|
||||
svgPaths.forEach((svgFile) => {
|
||||
const tsxComponentName = parseSvgFilename({
|
||||
file: svgFile.filename,
|
||||
log: false,
|
||||
});
|
||||
files.push({
|
||||
path: `./${OUTPUT_DIR}/${tsxComponentName}.tsx`,
|
||||
type: "registry:component",
|
||||
target: `components/ui/svgs/${tsxComponentName}.tsx`,
|
||||
});
|
||||
});
|
||||
|
||||
if (files.length > 0) {
|
||||
registryItems.push({
|
||||
name: componentName,
|
||||
type: "registry:component",
|
||||
title: componentName,
|
||||
files: files,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
...shadcnSchema,
|
||||
items: registryItems,
|
||||
};
|
||||
}
|
||||
|
||||
// 🧑🚀 Function to generate registry.json:
|
||||
async function generateRegistryJson(): Promise<void> {
|
||||
try {
|
||||
const registryContent = prepareRegistryJson();
|
||||
const registryPath = "./registry.json";
|
||||
|
||||
await fs.promises.writeFile(
|
||||
registryPath,
|
||||
JSON.stringify(registryContent, null, 2),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
console.log(
|
||||
`[📄] File registry.json generated with ${registryContent.items.length} TSX components`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("[❌] Error generating registry.json:", error);
|
||||
throw new Error(error as string);
|
||||
}
|
||||
}
|
||||
|
||||
// 🧑🚀 Utility functions for extracting SVG paths:
|
||||
function extractSvgPaths(svg: iSVG): { path: string; filename: string }[] {
|
||||
const paths: { path: string; filename: string }[] = [];
|
||||
|
||||
if (typeof svg.route === "string") {
|
||||
paths.push({
|
||||
path: svg.route,
|
||||
filename: svg.route.split("/").pop() || "",
|
||||
});
|
||||
} else if (
|
||||
typeof svg.route === "object" &&
|
||||
svg.route.light &&
|
||||
svg.route.dark
|
||||
) {
|
||||
paths.push(
|
||||
{
|
||||
path: svg.route.light,
|
||||
filename: svg.route.light.split("/").pop() || "",
|
||||
},
|
||||
{
|
||||
path: svg.route.dark,
|
||||
filename: svg.route.dark.split("/").pop() || "",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (svg.wordmark) {
|
||||
if (typeof svg.wordmark === "string") {
|
||||
paths.push({
|
||||
path: svg.wordmark,
|
||||
filename: svg.wordmark.split("/").pop() || "",
|
||||
});
|
||||
} else if (
|
||||
typeof svg.wordmark === "object" &&
|
||||
svg.wordmark.light &&
|
||||
svg.wordmark.dark
|
||||
) {
|
||||
paths.push(
|
||||
{
|
||||
path: svg.wordmark.light,
|
||||
filename: svg.wordmark.light.split("/").pop() || "",
|
||||
},
|
||||
{
|
||||
path: svg.wordmark.dark,
|
||||
filename: svg.wordmark.dark.split("/").pop() || "",
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
function getAllSvgFiles(): { path: string; filename: string }[] {
|
||||
const allPaths: { path: string; filename: string }[] = [];
|
||||
|
||||
SVGS_DATA.forEach((svg) => {
|
||||
const paths = extractSvgPaths(svg);
|
||||
allPaths.push(...paths);
|
||||
});
|
||||
|
||||
const uniquePaths = allPaths.filter(
|
||||
(path, index, self) =>
|
||||
index === self.findIndex((p) => p.filename === path.filename),
|
||||
);
|
||||
|
||||
return uniquePaths;
|
||||
}
|
||||
|
||||
function convertToFilesystemPath(svgPath: string): string {
|
||||
const cleanPath = svgPath.startsWith("/") ? svgPath.slice(1) : svgPath;
|
||||
return `./${PUBLIC_FOLDER}/${cleanPath}`;
|
||||
}
|
||||
|
||||
async function convertSvgToReact(svgPath: string): Promise<string> {
|
||||
const rawSvg = await fs.promises.readFile(svgPath, "utf-8");
|
||||
const optimizedSvg = optimizeSvg({ svgCode: rawSvg });
|
||||
const componentName = parseSvgFilename({
|
||||
file: path.basename(svgPath, ".svg"),
|
||||
log: true,
|
||||
firstUpperCase: true,
|
||||
});
|
||||
const code = await parseReactSvgContent({
|
||||
componentName,
|
||||
svgCode: optimizedSvg,
|
||||
typescript: true,
|
||||
});
|
||||
return code;
|
||||
}
|
||||
|
||||
async function cleanupDirectory(dirPath: string) {
|
||||
try {
|
||||
if (
|
||||
await fs.promises
|
||||
.access(dirPath)
|
||||
.then(() => true)
|
||||
.catch(() => false)
|
||||
) {
|
||||
await fs.promises.rm(dirPath, { recursive: true, force: true });
|
||||
console.log(`[🗑️] Folder ${dirPath} deleted successfully`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`[⚠️] Could not delete folder ${dirPath}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function runShadcnBuild() {
|
||||
try {
|
||||
console.log("[🔨] Running shadcn build...");
|
||||
const { stdout, stderr } = await execAsync(SHADCN_COMMAND);
|
||||
|
||||
if (stdout) {
|
||||
console.log("[✅] shadcn build completed:");
|
||||
console.log(stdout);
|
||||
}
|
||||
|
||||
if (stderr && !stderr.includes("warning")) {
|
||||
console.error("[❌] Errors in shadcn build:");
|
||||
console.error(stderr);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[❌] Error running shadcn build:", error);
|
||||
throw new Error(error as string);
|
||||
}
|
||||
}
|
||||
|
||||
const checkFinallyDirs = async () => {
|
||||
// Check if static/r directory exists
|
||||
const rDirExists = await fs.promises
|
||||
.access(`./${PUBLIC_FOLDER}/r`)
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
if (!rDirExists) {
|
||||
console.error("[🔎] Error - Directory ./static/r does not exist");
|
||||
return;
|
||||
} else {
|
||||
console.log("[🔎] Directory ./static/r exists");
|
||||
}
|
||||
|
||||
// Check if registry.json exists
|
||||
const registryExists = await fs.promises
|
||||
.access("./registry.json")
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
if (!registryExists) {
|
||||
console.error("[🔎] Error - File registry.json does not exist");
|
||||
return;
|
||||
} else {
|
||||
console.log("[🔎] File registry.json exists");
|
||||
}
|
||||
};
|
||||
|
||||
async function run() {
|
||||
let convertedCount = 0;
|
||||
let totalCount = 0;
|
||||
|
||||
try {
|
||||
await fs.promises.mkdir(OUTPUT_DIR, { recursive: true });
|
||||
|
||||
const svgFiles = REGENERATE_ALL
|
||||
? getAllSvgFiles()
|
||||
: getAllSvgFiles().filter((svgFile) => {
|
||||
const svgObj = SVGS_DATA.find((svg) => {
|
||||
const paths = extractSvgPaths(svg);
|
||||
return paths.some((p) => p.filename === svgFile.filename);
|
||||
});
|
||||
return svgObj && !svgObj.shadcnCommand;
|
||||
});
|
||||
totalCount = svgFiles.length;
|
||||
|
||||
if (totalCount === 0) {
|
||||
console.log("[❌] No SVG files found in SVGS_DATA.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`[📦] Converting ${totalCount} SVGs converted to TSX...`);
|
||||
|
||||
// Process files
|
||||
for (const svgFile of svgFiles) {
|
||||
try {
|
||||
const filesystemPath = convertToFilesystemPath(svgFile.path);
|
||||
|
||||
// Check if file exists before processing
|
||||
const fileExists = await fs.promises
|
||||
.access(filesystemPath)
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
|
||||
if (!fileExists) {
|
||||
console.error(`\n[⚠️] File not found: ${filesystemPath}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const tsx = await convertSvgToReact(filesystemPath);
|
||||
const outPath = path.join(
|
||||
OUTPUT_DIR,
|
||||
parseSvgFilename({ file: svgFile.filename, log: false }) + ".tsx",
|
||||
);
|
||||
await fs.promises.writeFile(outPath, tsx, "utf-8");
|
||||
convertedCount++;
|
||||
} catch (error) {
|
||||
console.error(`\n[❌] Error processing ${svgFile.filename}:`, error);
|
||||
throw new Error(error as string);
|
||||
}
|
||||
}
|
||||
console.log(
|
||||
`\n[📦] ✨ Conversion completed: ${convertedCount}/${totalCount} SVGs processed`,
|
||||
);
|
||||
|
||||
if (convertedCount < totalCount) {
|
||||
console.log(`[⚠️] ${totalCount - convertedCount} SVGs had errors.`);
|
||||
}
|
||||
|
||||
if (convertedCount > 0) {
|
||||
await generateRegistryJson();
|
||||
await runShadcnBuild();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[❌] Error:", error);
|
||||
throw new Error(error as string);
|
||||
} finally {
|
||||
await checkFinallyDirs();
|
||||
await cleanupDirectory(OUTPUT_DIR);
|
||||
console.log("[🎉] Process completed");
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
Reference in New Issue
Block a user