Files
Umbraco-CMS/src/Umbraco.Web.UI.Client/devops/icons/index.js
Lee Kelleher 3da8f41292 Deprecations: Client-side removal of v17 deprecated code (#20294)
* Webhooks: Removal of client-side deprecations for v17

* User: Removal of client-side deprecations for v17

* UFM: Removal of client-side deprecations for v17

* Tiptap: Removal of client-side deprecations for v17

* Templating: Removal of client-side deprecations for v17

* RTE: Removal of client-side deprecations for v17

* Relations: Removal of client-side deprecations for v17

* Search: Removal of client-side deprecations for v17

* Property Editors: Removal of client-side deprecations for v17

* URL Picker: Removal of client-side deprecations for v17

* Members: Removal of client-side deprecations for v17

* Media: Removal of client-side deprecations for v17

* Extension Insights: Removal of client-side deprecations for v17

* Documents: Removal of client-side deprecations for v17

* Media: Removal of client-side deprecations for v17

(part 2)

* Data Types: Removal of client-side deprecations for v17

* Core: Removal of client-side deprecations for v17

* Content: Removal of client-side deprecations for v17

* Clipboard: Removal of client-side deprecations for v17

* Blocks: Removal of client-side deprecations for v17

* Mocks: Removal of client-side deprecations for v17

* Libs: Removal of client-side deprecations for v17

* Apps: Removal of client-side deprecations for v17

* DevOps: Removal of client-side deprecations for v17

* Document Publishing Workspace: Removal of client-side deprecations for v17

Refactored to use `UmbDocumentPublishingWorkspaceContext`

* Reverted/modified some of my TODO comments

* Updated TODO comment

* Code cleanup sweep of TODO comments and tweaks

* Updated OpenApi.json, re-gen TS client

Tried to fix up mock data.

* Refactored the document variant name/fields

* Implemented co-pilot suggestions

---------

Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com>
2025-10-02 09:14:55 +02:00

226 lines
5.7 KiB
JavaScript

/* eslint-disable local-rules/enforce-umbraco-external-imports */
import { readFileSync, writeFile, mkdir, rmSync } from 'fs';
import * as pathModule from 'path';
import * as globModule from 'tiny-glob';
import { optimize } from 'svgo';
const path = pathModule.default;
const getDirName = path.dirname;
const glob = globModule.default;
const moduleDirectory = 'src/packages/core/icon-registry';
const iconsOutputDirectory = `${moduleDirectory}/icons`;
const umbracoSvgDirectory = `${moduleDirectory}/svgs`;
const iconMapJson = `${moduleDirectory}/icon-dictionary.json`;
const lucideSvgDirectory = 'node_modules/lucide-static/icons';
const simpleIconsSvgDirectory = 'node_modules/simple-icons/icons';
const IS_GITHUB_ACTIONS = process.env.GITHUB_ACTIONS === 'true';
const errors = [];
const run = async () => {
// Empty output directory:
rmSync(iconsOutputDirectory, { recursive: true });
let icons = await collectDictionaryIcons();
icons = await collectDiskIcons(icons);
writeIconsToDisk(icons);
generateJS(icons);
};
const collectDictionaryIcons = async () => {
const rawData = readFileSync(iconMapJson);
const fileRaw = rawData.toString();
const fileJSON = JSON.parse(fileRaw);
let icons = [];
// Lucide:
fileJSON.lucide.forEach((iconDef) => {
if (iconDef.file && iconDef.name) {
const path = lucideSvgDirectory + '/' + iconDef.file;
try {
const rawData = readFileSync(path);
// For Lucide icons specially we adjust the icons a bit for them to work in our case: [NL]
let svg = rawData.toString().replace(' width="24"\n', '');
svg = svg.replace(' height="24"\n', '');
svg = svg.replace('stroke-width="2"', 'stroke-width="1.75"');
const iconFileName = iconDef.name;
const icon = {
name: iconDef.name,
hidden: iconDef.legacy ?? iconDef.internal,
fileName: iconFileName,
svg,
output: `${iconsOutputDirectory}/${iconFileName}.ts`,
};
icons.push(icon);
} catch {
errors.push(`[Lucide] Could not load file: '${path}'`);
console.log(`[Lucide] Could not load file: '${path}'`);
}
}
});
// SimpleIcons:
fileJSON.simpleIcons.forEach((iconDef) => {
if (iconDef.file && iconDef.name) {
const path = simpleIconsSvgDirectory + '/' + iconDef.file;
try {
const rawData = readFileSync(path);
let svg = rawData.toString();
const iconFileName = iconDef.name;
// SimpleIcons need to use fill="currentColor"
const pattern = /fill=/g;
if (!pattern.test(svg)) {
svg = svg.replace(/<path/g, '<path fill="currentColor"');
}
const icon = {
name: iconDef.name,
legacy: iconDef.legacy,
fileName: iconFileName,
svg,
output: `${iconsOutputDirectory}/${iconFileName}.ts`,
};
icons.push(icon);
} catch {
errors.push(`[SimpleIcons] Could not load file: '${path}'`);
console.log(`[SimpleIcons] Could not load file: '${path}'`);
}
}
});
// Umbraco:
fileJSON.umbraco.forEach((iconDef) => {
if (iconDef.file && iconDef.name) {
const path = umbracoSvgDirectory + '/' + iconDef.file;
try {
const rawData = readFileSync(path);
const svg = rawData.toString();
const iconFileName = iconDef.name;
const icon = {
name: iconDef.name,
legacy: iconDef.legacy,
fileName: iconFileName,
svg,
output: `${iconsOutputDirectory}/${iconFileName}.ts`,
};
icons.push(icon);
} catch {
errors.push(`[Umbraco] Could not load file: '${path}'`);
console.log(`[Umbraco] Could not load file: '${path}'`);
}
}
});
return icons;
};
const collectDiskIcons = async (icons) => {
const iconPaths = await glob(`${umbracoSvgDirectory}/icon-*.svg`);
iconPaths.forEach((path) => {
const rawData = readFileSync(path);
const svg = rawData.toString();
const parsed = pathModule.parse(path);
if (!parsed) {
console.log('No match found for: ', path);
return;
}
const SVGFileName = parsed.name;
const iconFileName = SVGFileName.replace('.svg', '');
const iconName = iconFileName;
// Only append not already defined icons:
if (!icons.find((x) => x.name === iconName)) {
const icon = {
name: iconName,
hidden: true,
fileName: iconFileName,
svg,
output: `${iconsOutputDirectory}/${iconFileName}.ts`,
};
icons.push(icon);
}
});
return icons;
};
const writeIconsToDisk = (icons) => {
icons.forEach((icon) => {
const optimizedResult = optimize(icon.svg);
const content = 'export default `' + optimizedResult.data + '`;';
writeFileWithDir(icon.output, content, (err) => {
if (err) {
console.log(err);
}
//console.log(`icon: ${icon.name} generated`);
});
});
};
const generateJS = (icons) => {
const JSPath = `${moduleDirectory}/icons.ts`;
const iconDescriptors = icons.map((icon) => {
return `{
name: "${icon.name}",
${icon.hidden || icon.legacy ? 'hidden: true,' : ''}
path: () => import("./icons/${icon.fileName}.js"),
}`
.replace(/\t/g, '') // Regex removes white space [NL]
.replace(/^\s*[\r\n]/gm, ''); // Regex that removes empty lines. [NL]
});
const content = `export default [${iconDescriptors.join(',')}];`;
writeFileWithDir(JSPath, content, (err) => {
if (err) {
console.log(err);
}
console.log('Icons outputted and Icon Manifests generated!');
});
};
const writeFileWithDir = (path, contents, cb) => {
mkdir(getDirName(path), { recursive: true }, function (err) {
if (err) {
errors.push(err);
return cb(err);
}
writeFile(path, contents, cb);
});
};
await run();
if (errors.length > 0) {
if (IS_GITHUB_ACTIONS) {
const msg = errors.join('\n');
console.log(`::error title=Failed to generate all icons::${msg}`);
process.exit(1);
} else {
console.error('Failed to generate all icons, please see the error log');
}
}