Fixes pre-commit git hook to ensure the optimisation of staged icons (#958)

* testing

* Fixes pre-commit hook

* Removing checkIcons from pre commit hook because it might result in false positives and negatives on an unclean local repository

* Added checkIcons Github action

---------

Co-authored-by: Karsa <karsa@karsa.org>
This commit is contained in:
Karsa
2023-02-28 17:24:17 +01:00
committed by GitHub
parent 2485f6117a
commit 1479a9dbd8
9 changed files with 69 additions and 25 deletions

View File

@@ -1,4 +0,0 @@
#!/bin/sh
pnpm run checkIcons
exit $?

41
.github/actions/check-icons.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: "Check icons"
description: "Cross-checks icon and category references in JSON descriptors"
inputs:
name:
description: “Name of the package”
required: true
runs:
using: "composite"
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: pnpm/action-setup@v2.0.1
name: Install pnpm
id: pnpm-install
with:
version: 7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-lucide-preact-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-lucide-preact-pnpm-store-
- name: Install dependencies
run: pnpm install --filter .
- name: Check icons and categories
run: pnpm checkIcons

5
.husky/pre-commit Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm lint-staged
pnpm checkIcons

View File

@@ -20,7 +20,7 @@
"type": "string" "type": "string"
}, },
"icon": { "icon": {
"$ref": "#/$defs/types/icon-reference" "type": "string"
}, },
"weight": { "weight": {
"type": "integer" "type": "integer"

View File

@@ -24,7 +24,8 @@
"categories2icons": "node scripts/migrateCategoriesToIcons.mjs --presets @babel/env", "categories2icons": "node scripts/migrateCategoriesToIcons.mjs --presets @babel/env",
"generate:changelog": "node ./scripts/generateChangelog.mjs", "generate:changelog": "node ./scripts/generateChangelog.mjs",
"postinstall": "husky install", "postinstall": "husky install",
"lint": "eslint --ext .ts,.js,.mjs ./{packages/lucide,scripts}" "lint": "eslint --ext .ts,.js,.mjs ./{packages/lucide,scripts}",
"prepare": "husky install"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^8.26.0", "eslint": "^8.26.0",
@@ -40,11 +41,6 @@
"svgo": "^3.0.0", "svgo": "^3.0.0",
"svgson": "^5.2.1" "svgson": "^5.2.1"
}, },
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": { "lint-staged": {
"icons/*.svg": "node ./scripts/optimizeStagedSvgs.mjs" "icons/*.svg": "node ./scripts/optimizeStagedSvgs.mjs"
}, },

View File

@@ -7,7 +7,7 @@ const icons = readAllMetadata(ICONS_DIR);
const CATEGORIES_DIR = path.resolve(currentDir, '../categories'); const CATEGORIES_DIR = path.resolve(currentDir, '../categories');
const categories = readAllMetadata(CATEGORIES_DIR); const categories = readAllMetadata(CATEGORIES_DIR);
console.log(`Read all icons`); console.log('Reading all icons')
const svgFiles = readSvgDirectory(ICONS_DIR); const svgFiles = readSvgDirectory(ICONS_DIR);
const iconNames = svgFiles.map(icon => icon.split('.')[0]); const iconNames = svgFiles.map(icon => icon.split('.')[0]);
@@ -47,5 +47,6 @@ Object.keys(categories).forEach(categoryName => {
}); });
if (error) { if (error) {
throw new Error('At least one error in icon JSONs prevents from committing changes.'); console.error('At least one error in icon JSONs prevents from committing changes.');
process.exit(1);
} }

View File

@@ -1,10 +1,11 @@
import fs from 'fs'; import fs from 'fs';
import processSvg from './render/processSvg.mjs'; import processSvg from './render/processSvg.mjs';
const svgFiles = process.argv.slice(4); const svgFiles = process.argv.slice(2);
svgFiles.forEach(async (svgFile) => { svgFiles.forEach(async (svgFile) => {
console.log('Optimizing staged SVG file:', svgFile)
const content = fs.readFileSync(svgFile); const content = fs.readFileSync(svgFile);
const svg = await processSvg(content); const svg = await processSvg(content, svgFile);
fs.writeFileSync(svgFile, svg, 'utf-8'); fs.writeFileSync(svgFile, svg, 'utf-8');
}); });

View File

@@ -11,5 +11,5 @@ const svgFiles = readSvgDirectory(ICONS_DIR);
svgFiles.forEach((svgFile) => { svgFiles.forEach((svgFile) => {
const content = fs.readFileSync(path.join(ICONS_DIR, svgFile)); const content = fs.readFileSync(path.join(ICONS_DIR, svgFile));
processSvg(content).then((svg) => writeSvgFile(svgFile, ICONS_DIR, svg)); processSvg(content, svgFile).then((svg) => writeSvgFile(svgFile, ICONS_DIR, svg));
}); });

View File

@@ -1,6 +1,6 @@
import { optimize } from 'svgo'; import {optimize} from 'svgo';
import prettier from 'prettier'; import prettier from 'prettier';
import { parseSync, stringify } from 'svgson'; import {parseSync, stringify} from 'svgson';
import DEFAULT_ATTRS from './default-attrs.json' assert { type: 'json' }; import DEFAULT_ATTRS from './default-attrs.json' assert { type: 'json' };
/** /**
@@ -8,8 +8,9 @@ import DEFAULT_ATTRS from './default-attrs.json' assert { type: 'json' };
* @param {string} svg - An SVG string. * @param {string} svg - An SVG string.
* @returns {Promise<string>} An optimized svg * @returns {Promise<string>} An optimized svg
*/ */
async function optimizeSvg(svg) { async function optimizeSvg(svg, path) {
const result = optimize(svg, { const result = optimize(svg, {
path,
plugins: [ plugins: [
{ {
name: 'preset-default', name: 'preset-default',
@@ -17,12 +18,15 @@ async function optimizeSvg(svg) {
overrides: { overrides: {
convertShapeToPath: false, convertShapeToPath: false,
mergePaths: false, mergePaths: false,
removeAttrs: {
attrs: '(fill|stroke.*)',
},
}, },
}, },
}, },
{
name: 'removeAttrs',
params: {
attrs: '(fill|stroke.*)',
}
}
], ],
}); });
@@ -47,12 +51,12 @@ function setAttrs(svg) {
* @param {string} svg An SVG string. * @param {string} svg An SVG string.
* @returns {Promise<string>} An optimized svg * @returns {Promise<string>} An optimized svg
*/ */
function processSvg(svg) { function processSvg(svg, path) {
return ( return (
optimizeSvg(svg) optimizeSvg(svg, path)
.then(setAttrs) .then(setAttrs)
.then((optimizedSvg) => .then((optimizedSvg) =>
prettier.format(optimizedSvg, { parser: 'babel' }), prettier.format(optimizedSvg, {parser: 'babel'}),
) )
// remove semicolon inserted by prettier // remove semicolon inserted by prettier
// because prettier thinks it's formatting JSX not HTML // because prettier thinks it's formatting JSX not HTML