-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2477 from SpareBank1/change-sizes-icons
Nye størrelser på ikoner, lg går fra 40px til 32px og xl går fra 48 til 40
- Loading branch information
Showing
56,797 changed files
with
57,137 additions
and
51,720 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,125 @@ | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
const fs = require('fs/promises'); | ||
const { makedirs } = require('./utils'); | ||
const { getIconNames } = require('./getIconNames'); | ||
const { getDownloads, downloadAll } = require('./downloadSvgs'); | ||
const { | ||
createListOfRemovedIcons, | ||
deleteRemovedIconsFiles, | ||
} = require('./deleteSvg'); | ||
|
||
(async () => { | ||
const weights = [300, 400, 500]; | ||
const sizes = [ | ||
{ name: 'sm', opsz: 20 }, | ||
{ name: 'md', opsz: 24 }, | ||
{ name: 'lg', opsz: 40 }, | ||
{ name: 'xl', opsz: 48 }, | ||
]; | ||
const fill = [0, 1]; | ||
|
||
const iconNames = await getIconNames(); | ||
const listOfRemovedIcons = await createListOfRemovedIcons(iconNames); | ||
let downloads = []; | ||
// eslint-disable-next-line no-unused-vars | ||
for (const weight of weights) { | ||
// eslint-disable-next-line no-unused-vars | ||
for (const fillValue of fill) { | ||
const type = fillValue === 1 ? 'filled' : 'open'; | ||
|
||
// eslint-disable-next-line no-unused-vars | ||
for (const size of sizes) { | ||
let folderPath = `../icons/${type}/${weight}/${size.name}`; | ||
if (type === 'filled') { | ||
folderPath = `../icons/${type}/${size.name}`; | ||
} | ||
const dirPath = path.resolve(__dirname, folderPath); | ||
if (!fs.existsSync(dirPath)) { | ||
await makedirs(dirPath); | ||
} | ||
if (listOfRemovedIcons.length > 0) { | ||
await deleteRemovedIconsFiles(listOfRemovedIcons, dirPath); | ||
} | ||
downloads = downloads.concat( | ||
getDownloads(iconNames, weight, fillValue, size, dirPath), | ||
const { resizeAllSvgs } = require('./resizeSvg'); | ||
|
||
/** | ||
* Configuration for icon sizes and their target dimensions | ||
* @type {Array<{name: string, size: number, download: boolean}>} | ||
*/ | ||
const SIZES = [ | ||
{ name: 'sm', size: 20, download: true }, | ||
{ name: 'md', size: 24, download: true }, | ||
{ name: 'lg', size: 32, download: false }, | ||
{ name: 'xl', size: 40, download: true }, | ||
]; | ||
|
||
const WEIGHTS = [300, 400, 500]; | ||
const FILLS = [0, 1]; // 0 = outlined, 1 = filled | ||
|
||
/** | ||
* Creates the necessary directory structure for icons | ||
* @param {string} type - 'filled' or 'open' | ||
* @param {number} weight - Font weight | ||
* @param {string} size - Size name | ||
* @returns {Promise<string>} - Path to created directory | ||
*/ | ||
async function createIconDirectory(type, weight, size) { | ||
const basePath = path.resolve(__dirname, '../icons'); | ||
const dirPath = | ||
type === 'filled' | ||
? path.join(basePath, type, size) | ||
: path.join(basePath, type, weight.toString(), size); | ||
|
||
await makedirs(dirPath); | ||
return dirPath; | ||
} | ||
|
||
/** | ||
* Downloads and processes icons for a specific configuration | ||
* @param {Array<string>} iconNames - List of icon names to process | ||
* @param {string} type - 'filled' or 'open' | ||
* @param {number} weight - Font weight | ||
* @param {number} fillValue - Fill state (0 or 1) | ||
*/ | ||
async function processIconSet(iconNames, type, weight, fillValue) { | ||
console.log(`\n📦 Processing ${type} icons (weight: ${weight})`); | ||
|
||
// Download directly for sizes marked for download | ||
const downloadSizes = SIZES.filter(s => s.download); | ||
for (const sizeConfig of downloadSizes) { | ||
const dirPath = await createIconDirectory( | ||
type, | ||
weight, | ||
sizeConfig.name, | ||
); | ||
|
||
const downloads = getDownloads( | ||
iconNames, | ||
weight, | ||
fillValue, | ||
{ name: sizeConfig.name, opsz: sizeConfig.size }, | ||
dirPath, | ||
); | ||
|
||
console.log(`\n⬇️ Downloading ${sizeConfig.size}px SVG files...`); | ||
await downloadAll(downloads); | ||
} | ||
|
||
// Create 32px version from 40px | ||
const xlSize = SIZES.find(s => s.name === 'xl'); | ||
const lgSize = SIZES.find(s => s.name === 'lg'); | ||
|
||
if (xlSize && lgSize) { | ||
const xlDirPath = await createIconDirectory(type, weight, xlSize.name); | ||
const lgDirPath = await createIconDirectory(type, weight, lgSize.name); | ||
|
||
// Copy 40px files to 32px directory | ||
const files = await fs.readdir(xlDirPath); | ||
for (const file of files) { | ||
if (file.endsWith('.svg')) { | ||
await fs.copyFile( | ||
path.join(xlDirPath, file), | ||
path.join(lgDirPath, file), | ||
); | ||
} | ||
} | ||
|
||
// Resize 40px to 32px | ||
console.log('\n📐 Creating 32px version from 40px...'); | ||
await resizeAllSvgs(lgDirPath, lgSize.size, { | ||
continueOnError: true, | ||
verbose: true, | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
* Main build function | ||
*/ | ||
async function build() { | ||
try { | ||
console.log('🚀 Starting optimized icon build process...'); | ||
|
||
console.log('📋 Fetching icon names...'); | ||
const iconNames = await getIconNames(); | ||
console.log(`✓ Found ${iconNames.length} icons`); | ||
|
||
for (const weight of WEIGHTS) { | ||
for (const fillValue of FILLS) { | ||
const type = fillValue === 1 ? 'filled' : 'open'; | ||
await processIconSet(iconNames, type, weight, fillValue); | ||
} | ||
} | ||
|
||
console.log('\n✨ Build completed successfully!'); | ||
} catch (error) { | ||
console.error('\n❌ Build failed:', error); | ||
process.exit(1); | ||
} | ||
console.log('Downloading SVG files...'); | ||
await downloadAll(downloads); | ||
console.log('All done!'); | ||
})(); | ||
} | ||
|
||
// Run the build process | ||
build(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
const fs = require('fs/promises'); | ||
const path = require('path'); | ||
|
||
/** | ||
* Validates and cleans SVG content | ||
* @param {string} content - SVG content to validate | ||
* @returns {string} - Cleaned SVG content | ||
*/ | ||
function validateAndCleanSvg(content) { | ||
if (!content || typeof content !== 'string') { | ||
throw new Error('SVG content must be a non-empty string'); | ||
} | ||
|
||
// Remove BOM and whitespace | ||
let cleaned = content.trim().replace(/^\uFEFF/, ''); | ||
|
||
// Handle XML declaration | ||
if (cleaned.startsWith('<?xml')) { | ||
const xmlEnd = cleaned.indexOf('?>'); | ||
if (xmlEnd !== -1) { | ||
cleaned = cleaned.slice(xmlEnd + 2).trim(); | ||
} | ||
} | ||
|
||
// Handle potential doctype | ||
if (cleaned.startsWith('<!DOCTYPE')) { | ||
const doctypeEnd = cleaned.indexOf('>'); | ||
if (doctypeEnd !== -1) { | ||
cleaned = cleaned.slice(doctypeEnd + 1).trim(); | ||
} | ||
} | ||
|
||
// Find SVG tag | ||
const svgStart = cleaned.indexOf('<svg'); | ||
if (svgStart === -1) { | ||
throw new Error(`File does not contain an SVG tag`); | ||
} | ||
|
||
// Only keep from SVG tag onwards | ||
return cleaned.slice(svgStart); | ||
} | ||
|
||
/** | ||
* Updates SVG size while preserving viewBox and aspect ratio | ||
* @param {Buffer|string} svgContent - Original SVG content | ||
* @param {number} size - Target size in pixels | ||
* @returns {string} - Modified SVG content | ||
*/ | ||
function updateSvgSize(svgContent, size) { | ||
try { | ||
// Convert Buffer to string if needed | ||
const content = Buffer.isBuffer(svgContent) | ||
? svgContent.toString('utf8') | ||
: svgContent; | ||
|
||
// Clean and validate content | ||
const cleanedContent = validateAndCleanSvg(content); | ||
|
||
// Find SVG opening tag and its attributes | ||
const svgTagMatch = cleanedContent.match(/<svg([^>]*)>/); | ||
if (!svgTagMatch) { | ||
throw new Error('Could not parse SVG tag'); | ||
} | ||
|
||
const originalAttrs = svgTagMatch[1]; | ||
|
||
// Extract existing attributes | ||
const attrs = new Map(); | ||
const attrRegex = /(\w+)=["']([^"']+)["']/g; | ||
let match; | ||
|
||
// eslint-disable-next-line no-cond-assign | ||
while ((match = attrRegex.exec(originalAttrs)) !== null) { | ||
const [, name, value] = match; | ||
if (name !== 'width' && name !== 'height') { | ||
attrs.set(name, value); | ||
} | ||
} | ||
|
||
// Handle viewBox | ||
if (!attrs.has('viewBox')) { | ||
// Try to get original dimensions | ||
const width = originalAttrs.match(/width=["'](\d+)/)?.[1] || size; | ||
const height = originalAttrs.match(/height=["'](\d+)/)?.[1] || size; | ||
attrs.set('viewBox', `0 0 ${width} ${height}`); | ||
} | ||
|
||
// Set size attributes | ||
attrs.set('width', size); | ||
attrs.set('height', size); | ||
|
||
// Ensure xmlns is present | ||
if (!attrs.has('xmlns')) { | ||
attrs.set('xmlns', 'http://www.w3.org/2000/svg'); | ||
} | ||
|
||
// Build new SVG tag | ||
const newAttrs = Array.from(attrs.entries()) | ||
.map(([name, value]) => `${name}="${value}"`) | ||
.join(' '); | ||
|
||
const newTag = `<svg ${newAttrs}>`; | ||
|
||
// Replace original tag | ||
return cleanedContent.replace(/<svg[^>]*>/, newTag); | ||
} catch (error) { | ||
throw new Error(`SVG processing failed: ${error.message}`); | ||
} | ||
} | ||
|
||
/** | ||
* Processes a single SVG file | ||
* @param {string} filePath - Path to SVG file | ||
* @param {number} targetSize - Desired size in pixels | ||
* @param {Object} options - Processing options | ||
* @returns {Promise<boolean>} - Success status | ||
*/ | ||
async function resizeSvg(filePath, targetSize, options = {}) { | ||
const { skipErrors = false, verbose = false } = options; | ||
|
||
try { | ||
const content = await fs.readFile(filePath); | ||
const resizedContent = updateSvgSize(content, targetSize); | ||
await fs.writeFile(filePath, resizedContent); | ||
|
||
if (verbose) { | ||
console.log( | ||
`✓ Resized ${path.basename(filePath)} to ${targetSize}px`, | ||
); | ||
} | ||
return true; | ||
} catch (error) { | ||
const message = `Failed to process ${path.basename(filePath)}: ${error.message}`; | ||
if (skipErrors) { | ||
console.error(`⚠️ ${message}`); | ||
return false; | ||
} | ||
throw new Error(message); | ||
} | ||
} | ||
|
||
/** | ||
* Processes all SVGs in a directory | ||
* @param {string} directory - Directory containing SVGs | ||
* @param {number} targetSize - Desired size in pixels | ||
* @param {Object} options - Processing options | ||
*/ | ||
async function resizeAllSvgs(directory, targetSize, options = {}) { | ||
const { | ||
continueOnError = true, | ||
verbose = false, | ||
concurrency = 10, | ||
} = options; | ||
|
||
try { | ||
// Ensure directory exists | ||
await fs.access(directory); | ||
|
||
// Get SVG files | ||
const files = await fs.readdir(directory); | ||
const svgFiles = files.filter(file => | ||
file.toLowerCase().endsWith('.svg'), | ||
); | ||
|
||
if (svgFiles.length === 0) { | ||
if (verbose) { | ||
console.log(`No SVG files found in ${directory}`); | ||
} | ||
return; | ||
} | ||
|
||
if (verbose) { | ||
console.log(`Processing ${svgFiles.length} SVG files...`); | ||
} | ||
|
||
// Process files in batches | ||
const batches = []; | ||
for (let i = 0; i < svgFiles.length; i += concurrency) { | ||
const batch = svgFiles.slice(i, i + concurrency); | ||
batches.push(batch); | ||
} | ||
|
||
let successful = 0; | ||
let failed = 0; | ||
|
||
for (const batch of batches) { | ||
const results = await Promise.all( | ||
batch.map(file => | ||
resizeSvg(path.join(directory, file), targetSize, { | ||
skipErrors: continueOnError, | ||
verbose, | ||
}), | ||
), | ||
); | ||
|
||
successful += results.filter(Boolean).length; | ||
failed += results.filter(r => !r).length; | ||
} | ||
|
||
if (verbose) { | ||
console.log(`\nProcessing complete:`); | ||
console.log(`✓ Successfully processed: ${successful} files`); | ||
if (failed > 0) { | ||
console.log(`⚠️ Failed to process: ${failed} files`); | ||
} | ||
} | ||
|
||
if (failed > 0 && !continueOnError) { | ||
throw new Error(`Failed to process ${failed} files`); | ||
} | ||
} catch (error) { | ||
if (!continueOnError) { | ||
throw new Error(`Directory processing failed: ${error.message}`); | ||
} | ||
console.error(`⚠️ ${error.message}`); | ||
} | ||
} | ||
|
||
module.exports = { | ||
resizeSvg, | ||
resizeAllSvgs, | ||
updateSvgSize, | ||
validateAndCleanSvg, | ||
}; |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.