From b3e16c6d3b8c5ae522cd6359201c67066ca03d58 Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Fri, 19 Jun 2026 02:37:23 +0300 Subject: [PATCH 01/19] fix(atls-ui-generators): stabilize icon generation --- generators/icons/package.json | 2 - .../icons/src/commands/base-icons.command.ts | 1 + .../src/commands/generate-icons.command.ts | 1 + .../commands/generate-replacements.command.ts | 1 + .../compile-icons/compile-icons.constants.ts | 21 --- .../src/svgr-utils/compile-icons/constants.ts | 54 +++++++ .../{compile-icon-jsx.util.ts => icon-jsx.ts} | 4 +- .../{compile-icons.util.ts => icons.ts} | 12 +- .../src/svgr-utils/compile-icons/index.ts | 6 +- .../svgr-utils/compile-icons/interfaces.ts | 7 + ...{compile-icons.template.ts => template.ts} | 0 .../extract-color-from-element.helper.ts | 30 ---- .../get-svg-colors.constants.ts | 4 +- .../get-svg-colors/get-svg-colors.util.ts | 51 +++++-- .../normalize-attribute-name.helper.ts | 4 + .../src/svgr-utils/get-svg-colors/regex.ts | 9 ++ .../get-svg-colors/trim-quotes.helper.ts | 13 ++ yarn.lock | 144 ++---------------- 18 files changed, 153 insertions(+), 211 deletions(-) delete mode 100644 generators/icons/src/svgr-utils/compile-icons/compile-icons.constants.ts create mode 100644 generators/icons/src/svgr-utils/compile-icons/constants.ts rename generators/icons/src/svgr-utils/compile-icons/{compile-icon-jsx.util.ts => icon-jsx.ts} (71%) rename generators/icons/src/svgr-utils/compile-icons/{compile-icons.util.ts => icons.ts} (69%) create mode 100644 generators/icons/src/svgr-utils/compile-icons/interfaces.ts rename generators/icons/src/svgr-utils/compile-icons/{compile-icons.template.ts => template.ts} (100%) delete mode 100644 generators/icons/src/svgr-utils/get-svg-colors/extract-color-from-element.helper.ts create mode 100644 generators/icons/src/svgr-utils/get-svg-colors/normalize-attribute-name.helper.ts create mode 100644 generators/icons/src/svgr-utils/get-svg-colors/regex.ts create mode 100644 generators/icons/src/svgr-utils/get-svg-colors/trim-quotes.helper.ts diff --git a/generators/icons/package.json b/generators/icons/package.json index 109ff2c5d..968f14567 100644 --- a/generators/icons/package.json +++ b/generators/icons/package.json @@ -28,14 +28,12 @@ "@svgr/core": "8.1.0", "@swc-node/register": "1.9.0", "@swc/core": "1.6.1", - "cheerio": "1.1.2", "clipanion": "4.0.0-rc.4", "glob": "11.0.2", "is-svg": "6.1.0" }, "devDependencies": { "@types/node": "22.15.3", - "domhandler": "5.0.3", "tsx": "4.20.5", "typescript": "5.5.4" }, diff --git a/generators/icons/src/commands/base-icons.command.ts b/generators/icons/src/commands/base-icons.command.ts index 41f456a94..5a15aace6 100644 --- a/generators/icons/src/commands/base-icons.command.ts +++ b/generators/icons/src/commands/base-icons.command.ts @@ -49,6 +49,7 @@ export class BaseIconsCommand extends Command { this.resolvePaths() } catch (error) { this.logger.error(error) + process.exitCode = 1 } } } diff --git a/generators/icons/src/commands/generate-icons.command.ts b/generators/icons/src/commands/generate-icons.command.ts index 7af5e650e..92ac78e3e 100644 --- a/generators/icons/src/commands/generate-icons.command.ts +++ b/generators/icons/src/commands/generate-icons.command.ts @@ -57,6 +57,7 @@ export class GenerateIconsCommand extends BaseIconsCommand { this.logger.info(`Generated into ${outputPath}`) } catch (error) { this.logger.error(error) + process.exitCode = 1 } } } diff --git a/generators/icons/src/commands/generate-replacements.command.ts b/generators/icons/src/commands/generate-replacements.command.ts index 7c33ea222..a83e3b2d6 100644 --- a/generators/icons/src/commands/generate-replacements.command.ts +++ b/generators/icons/src/commands/generate-replacements.command.ts @@ -38,6 +38,7 @@ export class GenerateReplacementsCommand extends BaseIconsCommand { this.logger.info(`Generated into ${outputFilePath}`) } catch (error) { this.logger.error(error) + process.exitCode = 1 } } } diff --git a/generators/icons/src/svgr-utils/compile-icons/compile-icons.constants.ts b/generators/icons/src/svgr-utils/compile-icons/compile-icons.constants.ts deleted file mode 100644 index 55edb097c..000000000 --- a/generators/icons/src/svgr-utils/compile-icons/compile-icons.constants.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { Config } from '@svgr/core' - -import { svgrTemplate } from './compile-icons.template.js' - -export const MASK_ID_PATTERN = /mask0/g -export const PROPS_SPREAD_FROM = '...props' -export const PROPS_SPREAD_TO = '...otherProps' - -export const FIFTY_PLUS_PATTERN = '50+' -export const FIFTY_PLUS_REPLACEMENT = 'FiftyPlus' - -export const svgrTransformConfig: Config = { - icon: true, - typescript: true, - template: svgrTemplate, - plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx', '@svgr/plugin-prettier'], - svgProps: { - className: `{clsx(className, String(otherProps.className || ''))}`, - style: '{iconStyle}', - }, -} diff --git a/generators/icons/src/svgr-utils/compile-icons/constants.ts b/generators/icons/src/svgr-utils/compile-icons/constants.ts new file mode 100644 index 000000000..7dc94e773 --- /dev/null +++ b/generators/icons/src/svgr-utils/compile-icons/constants.ts @@ -0,0 +1,54 @@ +import type { Config } from '@svgr/core' + +import type { SvgrPlugin } from './interfaces.js' +import type { SvgrPluginModule } from './interfaces.js' + +import { createRequire } from 'node:module' + +import { svgrTemplate } from './template.js' + +const svgrCorePackageName = '@svgr/core/package.json' + +const svgoPluginName = '@svgr/plugin-svgo' + +const jsxPluginName = '@svgr/plugin-jsx' + +const prettierPluginName = '@svgr/plugin-prettier' + +const require = createRequire(import.meta.url) + +const svgrRequire = createRequire(require.resolve(svgrCorePackageName)) + +const loadSvgrPlugin = (pluginName: string): SvgrPlugin => { + const plugin = svgrRequire(pluginName) as SvgrPlugin | SvgrPluginModule + + if (typeof plugin === 'object' && 'default' in plugin && plugin.default) { + return plugin.default + } + + return plugin as SvgrPlugin +} + +const svgrTransformPlugins = [ + loadSvgrPlugin(svgoPluginName), + loadSvgrPlugin(jsxPluginName), + loadSvgrPlugin(prettierPluginName), +] + +export const MASK_ID_PATTERN = /mask0/g +export const PROPS_SPREAD_FROM = '...props' +export const PROPS_SPREAD_TO = '...otherProps' + +export const FIFTY_PLUS_PATTERN = '50+' +export const FIFTY_PLUS_REPLACEMENT = 'FiftyPlus' + +export const svgrTransformConfig: Config = { + icon: true, + typescript: true, + template: svgrTemplate, + plugins: svgrTransformPlugins, + svgProps: { + className: `{clsx(className, String(otherProps.className || ''))}`, + style: '{iconStyle}', + }, +} diff --git a/generators/icons/src/svgr-utils/compile-icons/compile-icon-jsx.util.ts b/generators/icons/src/svgr-utils/compile-icons/icon-jsx.ts similarity index 71% rename from generators/icons/src/svgr-utils/compile-icons/compile-icon-jsx.util.ts rename to generators/icons/src/svgr-utils/compile-icons/icon-jsx.ts index 3fcdf6008..860d7434f 100644 --- a/generators/icons/src/svgr-utils/compile-icons/compile-icon-jsx.util.ts +++ b/generators/icons/src/svgr-utils/compile-icons/icon-jsx.ts @@ -2,8 +2,8 @@ import type { Icon } from '../../icons.interfaces.js' import { transform } from '@svgr/core' -import { MASK_ID_PATTERN } from './compile-icons.constants.js' -import { svgrTransformConfig } from './compile-icons.constants.js' +import { MASK_ID_PATTERN } from './constants.js' +import { svgrTransformConfig } from './constants.js' export const compileIconJsx = async (icon: Icon): Promise => transform(icon.source.replace(MASK_ID_PATTERN, icon.name), { diff --git a/generators/icons/src/svgr-utils/compile-icons/compile-icons.util.ts b/generators/icons/src/svgr-utils/compile-icons/icons.ts similarity index 69% rename from generators/icons/src/svgr-utils/compile-icons/compile-icons.util.ts rename to generators/icons/src/svgr-utils/compile-icons/icons.ts index 4764f2b10..a51c84ef1 100644 --- a/generators/icons/src/svgr-utils/compile-icons/compile-icons.util.ts +++ b/generators/icons/src/svgr-utils/compile-icons/icons.ts @@ -4,12 +4,12 @@ import type { Source } from '../../icons.interfaces.js' import { transform } from '@svgr/core' -import { MASK_ID_PATTERN } from './compile-icons.constants.js' -import { PROPS_SPREAD_FROM } from './compile-icons.constants.js' -import { PROPS_SPREAD_TO } from './compile-icons.constants.js' -import { FIFTY_PLUS_PATTERN } from './compile-icons.constants.js' -import { FIFTY_PLUS_REPLACEMENT } from './compile-icons.constants.js' -import { svgrTransformConfig } from './compile-icons.constants.js' +import { MASK_ID_PATTERN } from './constants.js' +import { PROPS_SPREAD_FROM } from './constants.js' +import { PROPS_SPREAD_TO } from './constants.js' +import { FIFTY_PLUS_PATTERN } from './constants.js' +import { FIFTY_PLUS_REPLACEMENT } from './constants.js' +import { svgrTransformConfig } from './constants.js' export const compileIcons = async ( icons: Array, diff --git a/generators/icons/src/svgr-utils/compile-icons/index.ts b/generators/icons/src/svgr-utils/compile-icons/index.ts index c5c923fb1..d3871faed 100644 --- a/generators/icons/src/svgr-utils/compile-icons/index.ts +++ b/generators/icons/src/svgr-utils/compile-icons/index.ts @@ -1,3 +1,3 @@ -export * from './compile-icon-jsx.util.js' -export * from './compile-icons.template.js' -export * from './compile-icons.util.js' +export * from './icon-jsx.js' +export * from './icons.js' +export * from './template.js' diff --git a/generators/icons/src/svgr-utils/compile-icons/interfaces.ts b/generators/icons/src/svgr-utils/compile-icons/interfaces.ts new file mode 100644 index 000000000..d68e95b68 --- /dev/null +++ b/generators/icons/src/svgr-utils/compile-icons/interfaces.ts @@ -0,0 +1,7 @@ +import type { Config } from '@svgr/core' + +export type SvgrPlugin = NonNullable[number] + +export interface SvgrPluginModule { + default?: SvgrPlugin +} diff --git a/generators/icons/src/svgr-utils/compile-icons/compile-icons.template.ts b/generators/icons/src/svgr-utils/compile-icons/template.ts similarity index 100% rename from generators/icons/src/svgr-utils/compile-icons/compile-icons.template.ts rename to generators/icons/src/svgr-utils/compile-icons/template.ts diff --git a/generators/icons/src/svgr-utils/get-svg-colors/extract-color-from-element.helper.ts b/generators/icons/src/svgr-utils/get-svg-colors/extract-color-from-element.helper.ts deleted file mode 100644 index 851451655..000000000 --- a/generators/icons/src/svgr-utils/get-svg-colors/extract-color-from-element.helper.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { CheerioAPI } from 'cheerio' -import type { AnyNode } from 'domhandler' - -import type { GetSvgColorsOptions } from './get-svg-colors.interfaces.js' - -import { STYLE_COLOR_ATTRIBUTE_KEY } from './get-svg-colors.constants.js' - -export const extractColorFromElement = ( - cheerio: CheerioAPI, - element: AnyNode, - options: GetSvgColorsOptions -): Set => { - const colors = new Set() - - const attributes = cheerio(element).attr() ?? {} - - Object.entries(attributes).forEach(([name, value]) => { - if (options.colorAttributes?.has(name)) { - colors.add(value) - } - - if (name === STYLE_COLOR_ATTRIBUTE_KEY) { - options.colorAttributes?.forEach((colorAttribute) => { - colors.add(cheerio(element).css(colorAttribute) || '') - }) - } - }) - - return colors -} diff --git a/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.constants.ts b/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.constants.ts index 81dee4055..7ed578980 100644 --- a/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.constants.ts +++ b/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.constants.ts @@ -1 +1,3 @@ -export const STYLE_COLOR_ATTRIBUTE_KEY = 'style' +export const valueGroupName = 'value' + +export const quoteCharacters = ['"', "'"] diff --git a/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.util.ts b/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.util.ts index 935628d59..376ae1132 100644 --- a/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.util.ts +++ b/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.util.ts @@ -1,20 +1,49 @@ -import type { GetSvgColorsOptions } from './get-svg-colors.interfaces.js' +import type { GetSvgColorsOptions } from './get-svg-colors.interfaces.js' + +import { ATTRIBUTE_NAME_PATTERN } from './regex.js' +import { ATTRIBUTE_PATTERN } from './regex.js' +import { STYLE_PROPERTY_NAME_PATTERN } from './regex.js' +import { STYLE_PROPERTY_PATTERN } from './regex.js' +import { valueGroupName } from './get-svg-colors.constants.js' +import { normalizeAttributeName } from './normalize-attribute-name.helper.js' +import { trimQuotes } from './trim-quotes.helper.js' +import { validateColor } from './validate-color.helper.js' + +const getColorAttributeNames = (options: GetSvgColorsOptions): Set => + new Set(Array.from(options.colorAttributes ?? []).map(normalizeAttributeName)) + +const addAttributeColor = ( + colors: Set, + source: string, + colorAttributes: Set +): void => { + for (const match of source.matchAll(ATTRIBUTE_PATTERN)) { + const name = match[0].match(ATTRIBUTE_NAME_PATTERN)?.[0] + const value = match.groups?.[valueGroupName] + + if (name && value && colorAttributes.has(normalizeAttributeName(name))) { + colors.add(trimQuotes(value)) + } + } +} -import { load } from 'cheerio' +const addStyleColor = (colors: Set, source: string, colorAttributes: Set): void => { + for (const match of source.matchAll(STYLE_PROPERTY_PATTERN)) { + const name = match[0].match(STYLE_PROPERTY_NAME_PATTERN)?.[0] + const value = match.groups?.[valueGroupName] -import { extractColorFromElement } from './extract-color-from-element.helper.js' -import { validateColor } from './validate-color.helper.js' + if (name && value && colorAttributes.has(normalizeAttributeName(name))) { + colors.add(trimQuotes(value)) + } + } +} export const getSvgColors = (source: string, options: GetSvgColorsOptions = {}): Array => { const colors = new Set() + const colorAttributes = getColorAttributeNames(options) - const cheerio = load(source, { xml: true }) - - cheerio('*').each((index, element) => { - const elementColors = extractColorFromElement(cheerio, element, options) - - elementColors.forEach((color) => colors.add(color)) - }) + addAttributeColor(colors, source, colorAttributes) + addStyleColor(colors, source, colorAttributes) const colorsArray = Array.from(colors) diff --git a/generators/icons/src/svgr-utils/get-svg-colors/normalize-attribute-name.helper.ts b/generators/icons/src/svgr-utils/get-svg-colors/normalize-attribute-name.helper.ts new file mode 100644 index 000000000..a825178b6 --- /dev/null +++ b/generators/icons/src/svgr-utils/get-svg-colors/normalize-attribute-name.helper.ts @@ -0,0 +1,4 @@ +import { NON_WORD_PATTERN } from './regex.js' + +export const normalizeAttributeName = (name: string): string => + name.replace(NON_WORD_PATTERN, '').toLowerCase() diff --git a/generators/icons/src/svgr-utils/get-svg-colors/regex.ts b/generators/icons/src/svgr-utils/get-svg-colors/regex.ts new file mode 100644 index 000000000..94a75ae65 --- /dev/null +++ b/generators/icons/src/svgr-utils/get-svg-colors/regex.ts @@ -0,0 +1,9 @@ +export const ATTRIBUTE_PATTERN = /[\w:-]+\s*=\s*(["'])(?.*?)\1/g + +export const ATTRIBUTE_NAME_PATTERN = /^[\w:-]+/ + +export const NON_WORD_PATTERN = /[^a-z0-9]/gi + +export const STYLE_PROPERTY_PATTERN = /[\w-]+\s*:\s*(?[^;,}]+)/g + +export const STYLE_PROPERTY_NAME_PATTERN = /^[\w-]+/ diff --git a/generators/icons/src/svgr-utils/get-svg-colors/trim-quotes.helper.ts b/generators/icons/src/svgr-utils/get-svg-colors/trim-quotes.helper.ts new file mode 100644 index 000000000..e0516a874 --- /dev/null +++ b/generators/icons/src/svgr-utils/get-svg-colors/trim-quotes.helper.ts @@ -0,0 +1,13 @@ +import { quoteCharacters } from './get-svg-colors.constants.js' + +export const trimQuotes = (value: string): string => { + const trimmedValue = value.trim() + + const quoteCharacter = quoteCharacters.find((quote) => trimmedValue.startsWith(quote)) + + if (!quoteCharacter || !trimmedValue.endsWith(quoteCharacter)) { + return trimmedValue + } + + return trimmedValue.slice(quoteCharacter.length, -quoteCharacter.length) +} diff --git a/yarn.lock b/yarn.lock index 53fb67c63..f8499c8b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -320,9 +320,7 @@ __metadata: "@swc-node/register": "npm:1.9.0" "@swc/core": "npm:1.6.1" "@types/node": "npm:22.15.3" - cheerio: "npm:1.1.2" clipanion: "npm:4.0.0-rc.4" - domhandler: "npm:5.0.3" glob: "npm:11.0.2" is-svg: "npm:6.1.0" tsx: "npm:4.20.5" @@ -6523,39 +6521,6 @@ __metadata: languageName: node linkType: hard -"cheerio-select@npm:^2.1.0": - version: 2.1.0 - resolution: "cheerio-select@npm:2.1.0" - dependencies: - boolbase: "npm:^1.0.0" - css-select: "npm:^5.1.0" - css-what: "npm:^6.1.0" - domelementtype: "npm:^2.3.0" - domhandler: "npm:^5.0.3" - domutils: "npm:^3.0.1" - checksum: 10/b5d89208c23468c3a32d1e04f88b9e8c6e332e3649650c5cd29255e2cebc215071ae18563f58c3dc3f6ef4c234488fc486035490fceb78755572288245e2931a - languageName: node - linkType: hard - -"cheerio@npm:1.1.2": - version: 1.1.2 - resolution: "cheerio@npm:1.1.2" - dependencies: - cheerio-select: "npm:^2.1.0" - dom-serializer: "npm:^2.0.0" - domhandler: "npm:^5.0.3" - domutils: "npm:^3.2.2" - encoding-sniffer: "npm:^0.2.1" - htmlparser2: "npm:^10.0.0" - parse5: "npm:^7.3.0" - parse5-htmlparser2-tree-adapter: "npm:^7.1.0" - parse5-parser-stream: "npm:^7.1.2" - undici: "npm:^7.12.0" - whatwg-mimetype: "npm:^4.0.0" - checksum: 10/6b654bf5a358d3406eed5a3ae84530bab0d6d2d581d0a92d3c0666c310648d4300a00b11335e15007a35922ad3743931385ef17bec8d67b4fa077f10d1aaf2b5 - languageName: node - linkType: hard - "chokidar@npm:^4.0.1": version: 4.0.3 resolution: "chokidar@npm:4.0.3" @@ -7407,15 +7372,6 @@ __metadata: languageName: node linkType: hard -"domhandler@npm:5.0.3, domhandler@npm:^5.0.2, domhandler@npm:^5.0.3": - version: 5.0.3 - resolution: "domhandler@npm:5.0.3" - dependencies: - domelementtype: "npm:^2.3.0" - checksum: 10/809b805a50a9c6884a29f38aec0a4e1b4537f40e1c861950ed47d10b049febe6b79ab72adaeeebb3cc8fc1cd33f34e97048a72a9265103426d93efafa78d3e96 - languageName: node - linkType: hard - "domhandler@npm:^4.0.0, domhandler@npm:^4.2.0, domhandler@npm:^4.3.1": version: 4.3.1 resolution: "domhandler@npm:4.3.1" @@ -7425,6 +7381,15 @@ __metadata: languageName: node linkType: hard +"domhandler@npm:^5.0.2, domhandler@npm:^5.0.3": + version: 5.0.3 + resolution: "domhandler@npm:5.0.3" + dependencies: + domelementtype: "npm:^2.3.0" + checksum: 10/809b805a50a9c6884a29f38aec0a4e1b4537f40e1c861950ed47d10b049febe6b79ab72adaeeebb3cc8fc1cd33f34e97048a72a9265103426d93efafa78d3e96 + languageName: node + linkType: hard + "domutils@npm:^2.5.2, domutils@npm:^2.8.0": version: 2.8.0 resolution: "domutils@npm:2.8.0" @@ -7447,17 +7412,6 @@ __metadata: languageName: node linkType: hard -"domutils@npm:^3.2.1, domutils@npm:^3.2.2": - version: 3.2.2 - resolution: "domutils@npm:3.2.2" - dependencies: - dom-serializer: "npm:^2.0.0" - domelementtype: "npm:^2.3.0" - domhandler: "npm:^5.0.3" - checksum: 10/2e08842151aa406f50fe5e6d494f4ec73c2373199fa00d1f77b56ec604e566b7f226312ae35ab8160bb7f27a27c7285d574c8044779053e499282ca9198be210 - languageName: node - linkType: hard - "dot-case@npm:^3.0.4": version: 3.0.4 resolution: "dot-case@npm:3.0.4" @@ -7564,16 +7518,6 @@ __metadata: languageName: node linkType: hard -"encoding-sniffer@npm:^0.2.1": - version: 0.2.1 - resolution: "encoding-sniffer@npm:0.2.1" - dependencies: - iconv-lite: "npm:^0.6.3" - whatwg-encoding: "npm:^3.1.1" - checksum: 10/7d747238239408d52e8bceee22fcdc47546049866d19d601e7dc89e55d226922c51912ef046d7b38951970e8fd17e1e761cef3de98a4b2f46fc91c8a1ac143c9 - languageName: node - linkType: hard - "encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -7647,13 +7591,6 @@ __metadata: languageName: node linkType: hard -"entities@npm:^6.0.0": - version: 6.0.1 - resolution: "entities@npm:6.0.1" - checksum: 10/62af1307202884349d2867f0aac5c60d8b57102ea0b0e768b16246099512c28e239254ad772d6834e7e14cb1b6f153fc3d0c031934e3183b086c86d3838d874a - languageName: node - linkType: hard - "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -9708,18 +9645,6 @@ __metadata: languageName: node linkType: hard -"htmlparser2@npm:^10.0.0": - version: 10.0.0 - resolution: "htmlparser2@npm:10.0.0" - dependencies: - domelementtype: "npm:^2.3.0" - domhandler: "npm:^5.0.3" - domutils: "npm:^3.2.1" - entities: "npm:^6.0.0" - checksum: 10/768870f0e020dca19dc45df206cb6ac466c5dba6566c8fca4ca880347eed409f9977028d08644ac516bca8628ac9c7ded5a3847dc3ee1c043f049abf9e817154 - languageName: node - linkType: hard - "htmlparser2@npm:^6.1.0": version: 6.1.0 resolution: "htmlparser2@npm:6.1.0" @@ -12083,34 +12008,6 @@ __metadata: languageName: node linkType: hard -"parse5-htmlparser2-tree-adapter@npm:^7.1.0": - version: 7.1.0 - resolution: "parse5-htmlparser2-tree-adapter@npm:7.1.0" - dependencies: - domhandler: "npm:^5.0.3" - parse5: "npm:^7.0.0" - checksum: 10/75910af9137451e9c53e1e0d712f7393f484e89e592b1809ee62ad6cedd61b98daeaa5206ff5d9f06778002c91fac311afedde4880e1916fdb44fa71199dae73 - languageName: node - linkType: hard - -"parse5-parser-stream@npm:^7.1.2": - version: 7.1.2 - resolution: "parse5-parser-stream@npm:7.1.2" - dependencies: - parse5: "npm:^7.0.0" - checksum: 10/75b232d460bce6bd0e35012750a78ef034f40ccf550b7c6cec3122395af6b4553202ad3663ad468cf537ead5a2e13b6727670395fd0ff548faccad1dc2dc93cf - languageName: node - linkType: hard - -"parse5@npm:^7.0.0, parse5@npm:^7.3.0": - version: 7.3.0 - resolution: "parse5@npm:7.3.0" - dependencies: - entities: "npm:^6.0.0" - checksum: 10/b0e48be20b820c655b138b86fa6fb3a790de6c891aa2aba536524f8027b4dca4fe538f11a0e5cf2f6f847d120dbb9e4822dcaeb933ff1e10850a2ef0154d1d88 - languageName: node - linkType: hard - "parseurl@npm:^1.3.3": version: 1.3.3 resolution: "parseurl@npm:1.3.3" @@ -14787,13 +14684,6 @@ __metadata: languageName: node linkType: hard -"undici@npm:^7.12.0": - version: 7.16.0 - resolution: "undici@npm:7.16.0" - checksum: 10/2bb71672b23d3dc0f56f1b7fb6c936e4487a350db46eaafc03f2f9107f99cdf8e51ecdd32e589e2381ef47a64b6369cfb31f328b2c3ea663023aa47bc5258b9e - languageName: node - linkType: hard - "union@npm:~0.5.0": version: 0.5.0 resolution: "union@npm:0.5.0" @@ -15156,22 +15046,6 @@ __metadata: languageName: node linkType: hard -"whatwg-encoding@npm:^3.1.1": - version: 3.1.1 - resolution: "whatwg-encoding@npm:3.1.1" - dependencies: - iconv-lite: "npm:0.6.3" - checksum: 10/bbef815eb67f91487c7f2ef96329743f5fd8357d7d62b1119237d25d41c7e452dff8197235b2d3c031365a17f61d3bb73ca49d0ed1582475aa4a670815e79534 - languageName: node - linkType: hard - -"whatwg-mimetype@npm:^4.0.0": - version: 4.0.0 - resolution: "whatwg-mimetype@npm:4.0.0" - checksum: 10/894a618e2d90bf444b6f309f3ceb6e58cf21b2beaa00c8b333696958c4076f0c7b30b9d33413c9ffff7c5832a0a0c8569e5bb347ef44beded72aeefd0acd62e8 - languageName: node - linkType: hard - "whatwg-url@npm:^5.0.0": version: 5.0.0 resolution: "whatwg-url@npm:5.0.0" From ae538b647e496d4d8b5815c03c07c30fa69f0ee0 Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Fri, 19 Jun 2026 02:43:11 +0300 Subject: [PATCH 02/19] feat(atls-ui-admin): check generated icons --- ui-admin/icons/package.json | 1 + ui-admin/icons/scripts/check-generated.ts | 87 +++++++++++++++++++ .../icons/scripts/conditions/is-directory.ts | 9 ++ ui-admin/icons/scripts/conditions/is-file.ts | 9 ++ .../scripts/conditions/is-outdated-file.ts | 4 + ui-admin/icons/scripts/constants.ts | 65 ++++++++++++++ .../scripts/errors/command-failed.error.ts | 7 ++ .../errors/directory-required.error.ts | 7 ++ .../scripts/errors/file-required.error.ts | 7 ++ .../errors/generated-files-outdated.error.ts | 13 +++ ui-admin/icons/scripts/interfaces.ts | 19 ++++ ui-admin/icons/scripts/lib/collect-files.ts | 39 +++++++++ .../icons/scripts/lib/compare-directories.ts | 60 +++++++++++++ ui-admin/icons/scripts/lib/compare-files.ts | 49 +++++++++++ ui-admin/icons/scripts/lib/format-files.ts | 12 +++ ui-admin/icons/scripts/lib/generate-icons.ts | 26 ++++++ .../scripts/lib/generate-replacements.ts | 24 +++++ ui-admin/icons/scripts/lib/run-command.ts | 27 ++++++ 18 files changed, 465 insertions(+) create mode 100644 ui-admin/icons/scripts/check-generated.ts create mode 100644 ui-admin/icons/scripts/conditions/is-directory.ts create mode 100644 ui-admin/icons/scripts/conditions/is-file.ts create mode 100644 ui-admin/icons/scripts/conditions/is-outdated-file.ts create mode 100644 ui-admin/icons/scripts/constants.ts create mode 100644 ui-admin/icons/scripts/errors/command-failed.error.ts create mode 100644 ui-admin/icons/scripts/errors/directory-required.error.ts create mode 100644 ui-admin/icons/scripts/errors/file-required.error.ts create mode 100644 ui-admin/icons/scripts/errors/generated-files-outdated.error.ts create mode 100644 ui-admin/icons/scripts/interfaces.ts create mode 100644 ui-admin/icons/scripts/lib/collect-files.ts create mode 100644 ui-admin/icons/scripts/lib/compare-directories.ts create mode 100644 ui-admin/icons/scripts/lib/compare-files.ts create mode 100644 ui-admin/icons/scripts/lib/format-files.ts create mode 100644 ui-admin/icons/scripts/lib/generate-icons.ts create mode 100644 ui-admin/icons/scripts/lib/generate-replacements.ts create mode 100644 ui-admin/icons/scripts/lib/run-command.ts diff --git a/ui-admin/icons/package.json b/ui-admin/icons/package.json index d1d5c2159..3dbc104eb 100644 --- a/ui-admin/icons/package.json +++ b/ui-admin/icons/package.json @@ -9,6 +9,7 @@ ], "scripts": { "build": "yarn library build", + "check-generated": "yarn workspace @atls-ui-generators/icons node --import tsx ../../ui-admin/icons/scripts/check-generated.ts", "generate-icons": "yarn workspace @atls-ui-generators/icons generate-icons ../../ui-admin/icons/src/icons -i ../../ui-admin/icons/svg -r ../../ui-admin/icons/src/replacements.ts && yarn format", "generate-replacements": "yarn workspace @atls-ui-generators/icons generate-icons replacements ../../ui-admin/icons/src -i ../../ui-admin/icons/svg && yarn format", "prepack": "yarn run build", diff --git a/ui-admin/icons/scripts/check-generated.ts b/ui-admin/icons/scripts/check-generated.ts new file mode 100644 index 000000000..8b4c5688b --- /dev/null +++ b/ui-admin/icons/scripts/check-generated.ts @@ -0,0 +1,87 @@ +import { mkdir } from 'node:fs/promises' +import { mkdtemp } from 'node:fs/promises' +import { rm } from 'node:fs/promises' +import { join } from 'node:path' + +import { DirectoryRequiredError } from './errors/directory-required.error.js' +import { FileRequiredError } from './errors/file-required.error.js' +import { GeneratedFilesOutdatedError } from './errors/generated-files-outdated.error.js' +import { isDirectory } from './conditions/is-directory.js' +import { isFile } from './conditions/is-file.js' +import { checkSuccessMessage } from './constants.js' +import { generatedIconsDirectoryName } from './constants.js' +import { generatedReplacementsDirectoryName } from './constants.js' +import { outputLineBreak } from './constants.js' +import { packageRootPath } from './constants.js' +import { replacementsFileName } from './constants.js' +import { sourceIconsPath } from './constants.js' +import { sourceReplacementsPath } from './constants.js' +import { sourceSvgPath } from './constants.js' +import { temporaryDirectoryPrefix } from './constants.js' +import { collectFiles } from './lib/collect-files.js' +import { compareDirectories } from './lib/compare-directories.js' +import { compareFiles } from './lib/compare-files.js' +import { formatFiles } from './lib/format-files.js' +import { generateIcons } from './lib/generate-icons.js' +import { generateReplacements } from './lib/generate-replacements.js' + +const checkGenerated = async (): Promise => { + if (!(await isDirectory(sourceIconsPath))) { + throw new DirectoryRequiredError(sourceIconsPath) + } + + if (!(await isDirectory(sourceSvgPath))) { + throw new DirectoryRequiredError(sourceSvgPath) + } + + if (!(await isFile(sourceReplacementsPath))) { + throw new FileRequiredError(sourceReplacementsPath) + } + + await mkdir(packageRootPath, { recursive: true }) + + const temporaryPath = await mkdtemp(join(packageRootPath, temporaryDirectoryPrefix)) + const generatedIconsPath = join(temporaryPath, generatedIconsDirectoryName) + const generatedReplacementsPath = join(temporaryPath, generatedReplacementsDirectoryName) + + try { + await mkdir(generatedIconsPath) + await mkdir(generatedReplacementsPath) + + await generateReplacements(sourceSvgPath, generatedReplacementsPath) + await generateIcons(sourceSvgPath, generatedIconsPath) + await formatFiles([ + join(generatedReplacementsPath, replacementsFileName), + ...(await collectFiles(generatedIconsPath)).map((filePath) => + join(generatedIconsPath, filePath)), + ]) + + const outdatedFiles = [ + ...(await compareDirectories({ + actualPath: sourceIconsPath, + expectedPath: generatedIconsPath, + })), + ...(await compareFiles( + join(generatedReplacementsPath, replacementsFileName), + sourceReplacementsPath + )), + ] + + if (outdatedFiles.length > 0) { + throw new GeneratedFilesOutdatedError(outdatedFiles) + } + + process.stdout.write(`${checkSuccessMessage}${outputLineBreak}`) + } finally { + await rm(temporaryPath, { force: true, recursive: true }) + } +} + +try { + await checkGenerated() +} catch (error) { + const errorMessage = error instanceof Error ? (error.stack ?? error.message) : String(error) + + process.stderr.write(`${errorMessage}${outputLineBreak}`) + process.exitCode = 1 +} diff --git a/ui-admin/icons/scripts/conditions/is-directory.ts b/ui-admin/icons/scripts/conditions/is-directory.ts new file mode 100644 index 000000000..2a66dc27e --- /dev/null +++ b/ui-admin/icons/scripts/conditions/is-directory.ts @@ -0,0 +1,9 @@ +import { stat } from 'node:fs/promises' + +export const isDirectory = async (targetPath: string): Promise => { + try { + return (await stat(targetPath)).isDirectory() + } catch { + return false + } +} diff --git a/ui-admin/icons/scripts/conditions/is-file.ts b/ui-admin/icons/scripts/conditions/is-file.ts new file mode 100644 index 000000000..8076d1ef0 --- /dev/null +++ b/ui-admin/icons/scripts/conditions/is-file.ts @@ -0,0 +1,9 @@ +import { stat } from 'node:fs/promises' + +export const isFile = async (targetPath: string): Promise => { + try { + return (await stat(targetPath)).isFile() + } catch { + return false + } +} diff --git a/ui-admin/icons/scripts/conditions/is-outdated-file.ts b/ui-admin/icons/scripts/conditions/is-outdated-file.ts new file mode 100644 index 000000000..01d50a195 --- /dev/null +++ b/ui-admin/icons/scripts/conditions/is-outdated-file.ts @@ -0,0 +1,4 @@ +import { readFile } from 'node:fs/promises' + +export const isOutdatedFile = async (expectedPath: string, actualPath: string): Promise => + !(await readFile(expectedPath)).equals(await readFile(actualPath)) diff --git a/ui-admin/icons/scripts/constants.ts b/ui-admin/icons/scripts/constants.ts new file mode 100644 index 000000000..5df1f87bf --- /dev/null +++ b/ui-admin/icons/scripts/constants.ts @@ -0,0 +1,65 @@ +import { dirname } from 'node:path' +import { resolve } from 'node:path' +import { fileURLToPath } from 'node:url' + +const scriptDirectory = dirname(fileURLToPath(import.meta.url)) + +export const outdatedFileReasons = { + different: 'different', + missing: 'missing', + unexpected: 'unexpected', +} as const + +export const packageRootPath = resolve(scriptDirectory, '..') + +export const workspaceRootPath = resolve(packageRootPath, '../..') + +export const sourceIconsPath = resolve(packageRootPath, 'src/icons') + +export const sourceReplacementsPath = resolve(packageRootPath, 'src/replacements.ts') + +export const sourceSvgPath = resolve(packageRootPath, 'svg') + +export const temporaryDirectoryPrefix = '.atls-ui-admin-icons-' + +export const generatedIconsDirectoryName = 'icons' + +export const generatedReplacementsDirectoryName = 'replacements' + +export const replacementsFileName = 'replacements.ts' + +export const yarnCommand = 'yarn' + +export const workspaceArgument = 'workspace' + +export const iconsGeneratorWorkspace = '@atls-ui-generators/icons' + +export const generateIconsCommand = 'generate-icons' + +export const generateReplacementsCommand = 'replacements' + +export const formatCommand = 'format' + +export const iconsPathArgument = '-i' + +export const replacementsPathArgument = '-r' + +export const relativePathSeparator = '/' + +export const outputLineBreak = '\n' + +export const checkSuccessMessage = 'Generated icons are up to date' + +export const commandFailedMessage = 'Command failed' + +export const directoryRequiredMessage = 'Directory is required' + +export const fileRequiredMessage = 'File is required' + +export const outdatedFilesMessage = 'Generated files are outdated' + +export const outdatedFileReasonMessages = { + [outdatedFileReasons.different]: 'content differs', + [outdatedFileReasons.missing]: 'missing committed file', + [outdatedFileReasons.unexpected]: 'unexpected committed file', +} diff --git a/ui-admin/icons/scripts/errors/command-failed.error.ts b/ui-admin/icons/scripts/errors/command-failed.error.ts new file mode 100644 index 000000000..49fbad3be --- /dev/null +++ b/ui-admin/icons/scripts/errors/command-failed.error.ts @@ -0,0 +1,7 @@ +import { commandFailedMessage } from '../constants.js' + +export class CommandFailedError extends Error { + constructor(command: string, args: Array, exitCode: number | null) { + super(`${commandFailedMessage}: ${command} ${args.join(' ')} (${exitCode})`) + } +} diff --git a/ui-admin/icons/scripts/errors/directory-required.error.ts b/ui-admin/icons/scripts/errors/directory-required.error.ts new file mode 100644 index 000000000..c18aa0fd7 --- /dev/null +++ b/ui-admin/icons/scripts/errors/directory-required.error.ts @@ -0,0 +1,7 @@ +import { directoryRequiredMessage } from '../constants.js' + +export class DirectoryRequiredError extends Error { + constructor(targetPath: string) { + super(`${directoryRequiredMessage}: ${targetPath}`) + } +} diff --git a/ui-admin/icons/scripts/errors/file-required.error.ts b/ui-admin/icons/scripts/errors/file-required.error.ts new file mode 100644 index 000000000..d45f16320 --- /dev/null +++ b/ui-admin/icons/scripts/errors/file-required.error.ts @@ -0,0 +1,7 @@ +import { fileRequiredMessage } from '../constants.js' + +export class FileRequiredError extends Error { + constructor(targetPath: string) { + super(`${fileRequiredMessage}: ${targetPath}`) + } +} diff --git a/ui-admin/icons/scripts/errors/generated-files-outdated.error.ts b/ui-admin/icons/scripts/errors/generated-files-outdated.error.ts new file mode 100644 index 000000000..c6cbcd70e --- /dev/null +++ b/ui-admin/icons/scripts/errors/generated-files-outdated.error.ts @@ -0,0 +1,13 @@ +import type { OutdatedFile } from '../interfaces.js' + +import { outdatedFileReasonMessages } from '../constants.js' +import { outdatedFilesMessage } from '../constants.js' + +const formatFile = (file: OutdatedFile): string => + `${file.relativePath}: ${outdatedFileReasonMessages[file.reason]}` + +export class GeneratedFilesOutdatedError extends Error { + constructor(files: Array) { + super([outdatedFilesMessage, ...files.map(formatFile)].join('\n')) + } +} diff --git a/ui-admin/icons/scripts/interfaces.ts b/ui-admin/icons/scripts/interfaces.ts new file mode 100644 index 000000000..f4e9c9b60 --- /dev/null +++ b/ui-admin/icons/scripts/interfaces.ts @@ -0,0 +1,19 @@ +import type { outdatedFileReasons } from './constants.js' + +export type OutdatedFileReason = (typeof outdatedFileReasons)[keyof typeof outdatedFileReasons] + +export interface OutdatedFile { + actualPath?: string + expectedPath?: string + reason: OutdatedFileReason + relativePath: string +} + +export interface DirectoryComparisonOptions { + actualPath: string + expectedPath: string +} + +export interface CommandOptions { + cwd?: string +} diff --git a/ui-admin/icons/scripts/lib/collect-files.ts b/ui-admin/icons/scripts/lib/collect-files.ts new file mode 100644 index 000000000..209b4c0f1 --- /dev/null +++ b/ui-admin/icons/scripts/lib/collect-files.ts @@ -0,0 +1,39 @@ +import { readdir } from 'node:fs/promises' +import { join } from 'node:path' +import { relative } from 'node:path' +import { sep } from 'node:path' + +import { relativePathSeparator } from '../constants.js' + +const toRelativePath = (rootPath: string, filePath: string): string => + relative(rootPath, filePath).split(sep).join(relativePathSeparator) + +const collectDirectoryFiles = async ( + rootPath: string, + directoryPath: string +): Promise> => { + const entries = (await readdir(directoryPath, { withFileTypes: true })).sort((entryA, entryB) => + entryA.name.localeCompare(entryB.name)) + + const files = await Promise.all( + entries.map(async (entry) => { + const entryPath = join(directoryPath, entry.name) + + if (entry.isDirectory()) { + return collectDirectoryFiles(rootPath, entryPath) + } + + if (entry.isFile()) { + return [toRelativePath(rootPath, entryPath)] + } + + return [] + }) + ) + + return files.flat() +} + +export const collectFiles = async (rootPath: string): Promise> => + (await collectDirectoryFiles(rootPath, rootPath)).sort((fileA, fileB) => + fileA.localeCompare(fileB)) diff --git a/ui-admin/icons/scripts/lib/compare-directories.ts b/ui-admin/icons/scripts/lib/compare-directories.ts new file mode 100644 index 000000000..d1a291769 --- /dev/null +++ b/ui-admin/icons/scripts/lib/compare-directories.ts @@ -0,0 +1,60 @@ +import type { DirectoryComparisonOptions } from '../interfaces.js' +import type { OutdatedFile } from '../interfaces.js' + +import { join } from 'node:path' + +import { isOutdatedFile } from '../conditions/is-outdated-file.js' +import { outdatedFileReasons } from '../constants.js' +import { collectFiles } from './collect-files.js' + +export const compareDirectories = async ({ + actualPath, + expectedPath, +}: DirectoryComparisonOptions): Promise> => { + const expectedFiles = new Set(await collectFiles(expectedPath)) + const actualFiles = new Set(await collectFiles(actualPath)) + const relativePaths = [...new Set([...expectedFiles, ...actualFiles])].sort((pathA, pathB) => + pathA.localeCompare(pathB)) + + const outdatedFiles = await Promise.all( + relativePaths.map(async (relativePath) => { + const expectedFilePath = join(expectedPath, relativePath) + const actualFilePath = join(actualPath, relativePath) + + if (!expectedFiles.has(relativePath)) { + return [ + { + actualPath: actualFilePath, + reason: outdatedFileReasons.unexpected, + relativePath, + }, + ] + } + + if (!actualFiles.has(relativePath)) { + return [ + { + expectedPath: expectedFilePath, + reason: outdatedFileReasons.missing, + relativePath, + }, + ] + } + + if (await isOutdatedFile(expectedFilePath, actualFilePath)) { + return [ + { + actualPath: actualFilePath, + expectedPath: expectedFilePath, + reason: outdatedFileReasons.different, + relativePath, + }, + ] + } + + return [] + }) + ) + + return outdatedFiles.flat() +} diff --git a/ui-admin/icons/scripts/lib/compare-files.ts b/ui-admin/icons/scripts/lib/compare-files.ts new file mode 100644 index 000000000..d1f4b6615 --- /dev/null +++ b/ui-admin/icons/scripts/lib/compare-files.ts @@ -0,0 +1,49 @@ +import type { OutdatedFile } from '../interfaces.js' + +import { basename } from 'node:path' + +import { isFile } from '../conditions/is-file.js' +import { isOutdatedFile } from '../conditions/is-outdated-file.js' +import { outdatedFileReasons } from '../constants.js' + +export const compareFiles = async ( + expectedPath: string, + actualPath: string +): Promise> => { + const relativePath = basename(actualPath) + const hasExpectedFile = await isFile(expectedPath) + const hasActualFile = await isFile(actualPath) + + if (!hasExpectedFile) { + return [ + { + actualPath, + reason: outdatedFileReasons.unexpected, + relativePath, + }, + ] + } + + if (!hasActualFile) { + return [ + { + expectedPath, + reason: outdatedFileReasons.missing, + relativePath, + }, + ] + } + + if (await isOutdatedFile(expectedPath, actualPath)) { + return [ + { + actualPath, + expectedPath, + reason: outdatedFileReasons.different, + relativePath, + }, + ] + } + + return [] +} diff --git a/ui-admin/icons/scripts/lib/format-files.ts b/ui-admin/icons/scripts/lib/format-files.ts new file mode 100644 index 000000000..494a7053e --- /dev/null +++ b/ui-admin/icons/scripts/lib/format-files.ts @@ -0,0 +1,12 @@ +import { relative } from 'node:path' + +import { formatCommand } from '../constants.js' +import { workspaceRootPath } from '../constants.js' +import { yarnCommand } from '../constants.js' +import { runCommand } from './run-command.js' + +export const formatFiles = async (filePaths: Array): Promise => { + const relativeFilePaths = filePaths.map((filePath) => relative(workspaceRootPath, filePath)) + + await runCommand(yarnCommand, [formatCommand, ...relativeFilePaths], { cwd: workspaceRootPath }) +} diff --git a/ui-admin/icons/scripts/lib/generate-icons.ts b/ui-admin/icons/scripts/lib/generate-icons.ts new file mode 100644 index 000000000..747bcb89f --- /dev/null +++ b/ui-admin/icons/scripts/lib/generate-icons.ts @@ -0,0 +1,26 @@ +import { generateIconsCommand } from '../constants.js' +import { iconsGeneratorWorkspace } from '../constants.js' +import { iconsPathArgument } from '../constants.js' +import { replacementsPathArgument } from '../constants.js' +import { sourceReplacementsPath } from '../constants.js' +import { workspaceRootPath } from '../constants.js' +import { workspaceArgument } from '../constants.js' +import { yarnCommand } from '../constants.js' +import { runCommand } from './run-command.js' + +export const generateIcons = async (svgPath: string, outputPath: string): Promise => { + await runCommand( + yarnCommand, + [ + workspaceArgument, + iconsGeneratorWorkspace, + generateIconsCommand, + outputPath, + iconsPathArgument, + svgPath, + replacementsPathArgument, + sourceReplacementsPath, + ], + { cwd: workspaceRootPath } + ) +} diff --git a/ui-admin/icons/scripts/lib/generate-replacements.ts b/ui-admin/icons/scripts/lib/generate-replacements.ts new file mode 100644 index 000000000..a50b7a1a1 --- /dev/null +++ b/ui-admin/icons/scripts/lib/generate-replacements.ts @@ -0,0 +1,24 @@ +import { generateIconsCommand } from '../constants.js' +import { generateReplacementsCommand } from '../constants.js' +import { iconsGeneratorWorkspace } from '../constants.js' +import { iconsPathArgument } from '../constants.js' +import { workspaceRootPath } from '../constants.js' +import { workspaceArgument } from '../constants.js' +import { yarnCommand } from '../constants.js' +import { runCommand } from './run-command.js' + +export const generateReplacements = async (svgPath: string, outputPath: string): Promise => { + await runCommand( + yarnCommand, + [ + workspaceArgument, + iconsGeneratorWorkspace, + generateIconsCommand, + generateReplacementsCommand, + outputPath, + iconsPathArgument, + svgPath, + ], + { cwd: workspaceRootPath } + ) +} diff --git a/ui-admin/icons/scripts/lib/run-command.ts b/ui-admin/icons/scripts/lib/run-command.ts new file mode 100644 index 000000000..8808bfa81 --- /dev/null +++ b/ui-admin/icons/scripts/lib/run-command.ts @@ -0,0 +1,27 @@ +import type { CommandOptions } from '../interfaces.js' + +import { spawn } from 'node:child_process' + +import { CommandFailedError } from '../errors/command-failed.error.js' + +export const runCommand = async ( + command: string, + args: Array, + options: CommandOptions = {} +): Promise => { + await new Promise((resolve, reject) => { + const childProcess = spawn(command, args, { cwd: options.cwd, stdio: 'inherit' }) + + childProcess.on('error', reject) + + childProcess.on('close', (exitCode) => { + if (exitCode === 0) { + resolve() + + return + } + + reject(new CommandFailedError(command, args, exitCode)) + }) + }) +} From b4d9e1718e1ddad157615ea3f42753d127df7bae Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Fri, 19 Jun 2026 02:51:27 +0300 Subject: [PATCH 03/19] refactor(atls-ui-generators): flat namings --- generators/icons/package.json | 2 +- .../icons/src/commands/base-icons.command.ts | 2 +- .../src/commands/generate-icons.command.ts | 2 +- .../src/{icons.generator.ts => generator.ts} | 0 .../{icons.interfaces.ts => interfaces.ts} | 0 ...acements.util.ts => build-replacements.ts} | 4 ++-- .../build-replacements/index.ts | 2 +- ...-icon-colors.constants.ts => constants.ts} | 0 ...l.ts => create-replacement-icon-colors.ts} | 6 +++--- .../create-replacement-icon-colors/index.ts | 2 +- ...ments.util.cts => create-replacements.cts} | 0 ...cements.util.ts => create-replacements.ts} | 4 ++-- .../create-replacements/index.cts | 2 +- .../create-replacements/index.ts | 2 +- .../create-replacements.test.interfaces.ts | 2 +- ...ent.util.cts => get-color-replacement.cts} | 0 ...ement.util.ts => get-color-replacement.ts} | 2 +- .../get-color-replacement/index.cts | 2 +- .../get-color-replacement/index.ts | 2 +- ...eplacement.interfaces.ts => interfaces.ts} | 0 .../write-replacements-file/index.ts | 2 +- ...ile.util.ts => write-replacements-file.ts} | 2 +- .../{build-svgr.util.ts => build-svgr.ts} | 6 +++--- .../src/svgr-utils/compile-icons/icon-jsx.ts | 2 +- .../src/svgr-utils/compile-icons/icons.ts | 6 +++--- ...scover-icons.util.ts => discover-icons.ts} | 2 +- .../constants.ts} | 0 .../get-svg-colors/{ => config}/regex.ts | 0 ...t-svg-colors.util.ts => get-svg-colors.ts} | 20 +++++++++---------- .../normalize-attribute-name.ts} | 2 +- .../trim-quotes.ts} | 2 +- .../validate-color.ts} | 2 +- .../src/svgr-utils/get-svg-colors/index.ts | 4 ++-- ...svg-colors.interfaces.ts => interfaces.ts} | 0 generators/icons/src/svgr-utils/index.ts | 4 ++-- ...-icons.util.ts => write-compiled-icons.ts} | 2 +- 36 files changed, 46 insertions(+), 46 deletions(-) rename generators/icons/src/{icons.generator.ts => generator.ts} (100%) rename generators/icons/src/{icons.interfaces.ts => interfaces.ts} (100%) rename generators/icons/src/replacement-utils/build-replacements/{build-replacements.util.ts => build-replacements.ts} (72%) rename generators/icons/src/replacement-utils/create-replacement-icon-colors/{create-replacement-icon-colors.constants.ts => constants.ts} (100%) rename generators/icons/src/replacement-utils/create-replacement-icon-colors/{create-replacement-icon-colors.util.ts => create-replacement-icon-colors.ts} (76%) rename generators/icons/src/replacement-utils/create-replacements/{create-replacements.util.cts => create-replacements.cts} (100%) rename generators/icons/src/replacement-utils/create-replacements/{create-replacements.util.ts => create-replacements.ts} (81%) rename generators/icons/src/replacement-utils/get-color-replacement/{get-color-replacement.util.cts => get-color-replacement.cts} (100%) rename generators/icons/src/replacement-utils/get-color-replacement/{get-color-replacement.util.ts => get-color-replacement.ts} (57%) rename generators/icons/src/replacement-utils/get-color-replacement/{get-color-replacement.interfaces.ts => interfaces.ts} (100%) rename generators/icons/src/replacement-utils/write-replacements-file/{write-replacements-file.util.ts => write-replacements-file.ts} (88%) rename generators/icons/src/svgr-utils/{build-svgr.util.ts => build-svgr.ts} (64%) rename generators/icons/src/svgr-utils/{discover-icons.util.ts => discover-icons.ts} (91%) rename generators/icons/src/svgr-utils/get-svg-colors/{get-svg-colors.constants.ts => config/constants.ts} (100%) rename generators/icons/src/svgr-utils/get-svg-colors/{ => config}/regex.ts (100%) rename generators/icons/src/svgr-utils/get-svg-colors/{get-svg-colors.util.ts => get-svg-colors.ts} (69%) rename generators/icons/src/svgr-utils/get-svg-colors/{normalize-attribute-name.helper.ts => helpers/normalize-attribute-name.ts} (68%) rename generators/icons/src/svgr-utils/get-svg-colors/{trim-quotes.helper.ts => helpers/trim-quotes.ts} (84%) rename generators/icons/src/svgr-utils/get-svg-colors/{validate-color.helper.ts => helpers/validate-color.ts} (75%) rename generators/icons/src/svgr-utils/get-svg-colors/{get-svg-colors.interfaces.ts => interfaces.ts} (100%) rename generators/icons/src/svgr-utils/{write-compiled-icons.util.ts => write-compiled-icons.ts} (95%) diff --git a/generators/icons/package.json b/generators/icons/package.json index 968f14567..c97190fa3 100644 --- a/generators/icons/package.json +++ b/generators/icons/package.json @@ -19,7 +19,7 @@ ], "scripts": { "build": "yarn library build", - "generate-icons": "tsx src/icons.generator.ts", + "generate-icons": "tsx src/generator.ts", "prepack": "yarn run build", "postpack": "rm -rf dist" }, diff --git a/generators/icons/src/commands/base-icons.command.ts b/generators/icons/src/commands/base-icons.command.ts index 5a15aace6..1f61e1df0 100644 --- a/generators/icons/src/commands/base-icons.command.ts +++ b/generators/icons/src/commands/base-icons.command.ts @@ -1,6 +1,6 @@ /* eslint-disable n/no-sync */ -import type { ResolvedIconsPath } from '../icons.interfaces.js' +import type { ResolvedIconsPath } from '../interfaces.js' import assert from 'node:assert/strict' import fs from 'node:fs' diff --git a/generators/icons/src/commands/generate-icons.command.ts b/generators/icons/src/commands/generate-icons.command.ts index 92ac78e3e..9e03d28d1 100644 --- a/generators/icons/src/commands/generate-icons.command.ts +++ b/generators/icons/src/commands/generate-icons.command.ts @@ -1,6 +1,6 @@ /* eslint-disable n/no-sync */ -import type { Replacements } from '../icons.interfaces.js' +import type { Replacements } from '../interfaces.js' import assert from 'node:assert/strict' import { lstatSync } from 'node:fs' diff --git a/generators/icons/src/icons.generator.ts b/generators/icons/src/generator.ts similarity index 100% rename from generators/icons/src/icons.generator.ts rename to generators/icons/src/generator.ts diff --git a/generators/icons/src/icons.interfaces.ts b/generators/icons/src/interfaces.ts similarity index 100% rename from generators/icons/src/icons.interfaces.ts rename to generators/icons/src/interfaces.ts diff --git a/generators/icons/src/replacement-utils/build-replacements/build-replacements.util.ts b/generators/icons/src/replacement-utils/build-replacements/build-replacements.ts similarity index 72% rename from generators/icons/src/replacement-utils/build-replacements/build-replacements.util.ts rename to generators/icons/src/replacement-utils/build-replacements/build-replacements.ts index b7df353d4..cf402d9e9 100644 --- a/generators/icons/src/replacement-utils/build-replacements/build-replacements.util.ts +++ b/generators/icons/src/replacement-utils/build-replacements/build-replacements.ts @@ -1,5 +1,5 @@ -import type { Icon } from '../../icons.interfaces.js' -import type { Replacements } from '../../icons.interfaces.js' +import type { Icon } from '../../interfaces.js' +import type { Replacements } from '../../interfaces.js' import { createReplacementIconColors } from '../create-replacement-icon-colors/index.js' import { createReplacements } from '../create-replacements/index.js' diff --git a/generators/icons/src/replacement-utils/build-replacements/index.ts b/generators/icons/src/replacement-utils/build-replacements/index.ts index daeaeb6ec..fee3eaded 100644 --- a/generators/icons/src/replacement-utils/build-replacements/index.ts +++ b/generators/icons/src/replacement-utils/build-replacements/index.ts @@ -1 +1 @@ -export * from './build-replacements.util.js' +export * from './build-replacements.js' diff --git a/generators/icons/src/replacement-utils/create-replacement-icon-colors/create-replacement-icon-colors.constants.ts b/generators/icons/src/replacement-utils/create-replacement-icon-colors/constants.ts similarity index 100% rename from generators/icons/src/replacement-utils/create-replacement-icon-colors/create-replacement-icon-colors.constants.ts rename to generators/icons/src/replacement-utils/create-replacement-icon-colors/constants.ts diff --git a/generators/icons/src/replacement-utils/create-replacement-icon-colors/create-replacement-icon-colors.util.ts b/generators/icons/src/replacement-utils/create-replacement-icon-colors/create-replacement-icon-colors.ts similarity index 76% rename from generators/icons/src/replacement-utils/create-replacement-icon-colors/create-replacement-icon-colors.util.ts rename to generators/icons/src/replacement-utils/create-replacement-icon-colors/create-replacement-icon-colors.ts index f0a4dd430..3554030ff 100644 --- a/generators/icons/src/replacement-utils/create-replacement-icon-colors/create-replacement-icon-colors.util.ts +++ b/generators/icons/src/replacement-utils/create-replacement-icon-colors/create-replacement-icon-colors.ts @@ -1,9 +1,9 @@ -import type { Icon } from '../../icons.interfaces.js' -import type { ReplacementIconColors } from '../../icons.interfaces.js' +import type { Icon } from '../../interfaces.js' +import type { ReplacementIconColors } from '../../interfaces.js' import { compileIconJsx } from '../../svgr-utils/index.js' import { getSvgColors } from '../../svgr-utils/index.js' -import { getSvgColorsOptions } from './create-replacement-icon-colors.constants.js' +import { getSvgColorsOptions } from './constants.js' export const createReplacementIconColors = async ( icons: Array diff --git a/generators/icons/src/replacement-utils/create-replacement-icon-colors/index.ts b/generators/icons/src/replacement-utils/create-replacement-icon-colors/index.ts index 775024311..a1a0de194 100644 --- a/generators/icons/src/replacement-utils/create-replacement-icon-colors/index.ts +++ b/generators/icons/src/replacement-utils/create-replacement-icon-colors/index.ts @@ -1 +1 @@ -export * from './create-replacement-icon-colors.util.js' +export * from './create-replacement-icon-colors.js' diff --git a/generators/icons/src/replacement-utils/create-replacements/create-replacements.util.cts b/generators/icons/src/replacement-utils/create-replacements/create-replacements.cts similarity index 100% rename from generators/icons/src/replacement-utils/create-replacements/create-replacements.util.cts rename to generators/icons/src/replacement-utils/create-replacements/create-replacements.cts diff --git a/generators/icons/src/replacement-utils/create-replacements/create-replacements.util.ts b/generators/icons/src/replacement-utils/create-replacements/create-replacements.ts similarity index 81% rename from generators/icons/src/replacement-utils/create-replacements/create-replacements.util.ts rename to generators/icons/src/replacement-utils/create-replacements/create-replacements.ts index 269dabd4c..230409b4d 100644 --- a/generators/icons/src/replacement-utils/create-replacements/create-replacements.util.ts +++ b/generators/icons/src/replacement-utils/create-replacements/create-replacements.ts @@ -1,5 +1,5 @@ -import type { Replacements } from '../../icons.interfaces.js' -import type { ReplacementIconColors } from '../../icons.interfaces.js' +import type { Replacements } from '../../interfaces.js' +import type { ReplacementIconColors } from '../../interfaces.js' import { getColorReplacement } from '../get-color-replacement/index.js' diff --git a/generators/icons/src/replacement-utils/create-replacements/index.cts b/generators/icons/src/replacement-utils/create-replacements/index.cts index 79fe819c3..d8303f53a 100644 --- a/generators/icons/src/replacement-utils/create-replacements/index.cts +++ b/generators/icons/src/replacement-utils/create-replacements/index.cts @@ -1 +1 @@ -export * from './create-replacements.util.cjs' +export * from './create-replacements.js' diff --git a/generators/icons/src/replacement-utils/create-replacements/index.ts b/generators/icons/src/replacement-utils/create-replacements/index.ts index 7763a0ef7..d8303f53a 100644 --- a/generators/icons/src/replacement-utils/create-replacements/index.ts +++ b/generators/icons/src/replacement-utils/create-replacements/index.ts @@ -1 +1 @@ -export * from './create-replacements.util.js' +export * from './create-replacements.js' diff --git a/generators/icons/src/replacement-utils/create-replacements/tests/create-replacements.test.interfaces.ts b/generators/icons/src/replacement-utils/create-replacements/tests/create-replacements.test.interfaces.ts index 4d73cf9a2..455d83b45 100644 --- a/generators/icons/src/replacement-utils/create-replacements/tests/create-replacements.test.interfaces.ts +++ b/generators/icons/src/replacement-utils/create-replacements/tests/create-replacements.test.interfaces.ts @@ -1,4 +1,4 @@ -import type { Replacements } from '../../../icons.interfaces.js' +import type { Replacements } from '../../../interfaces.js' export interface TestCase { replacementIcons: Record | string> diff --git a/generators/icons/src/replacement-utils/get-color-replacement/get-color-replacement.util.cts b/generators/icons/src/replacement-utils/get-color-replacement/get-color-replacement.cts similarity index 100% rename from generators/icons/src/replacement-utils/get-color-replacement/get-color-replacement.util.cts rename to generators/icons/src/replacement-utils/get-color-replacement/get-color-replacement.cts diff --git a/generators/icons/src/replacement-utils/get-color-replacement/get-color-replacement.util.ts b/generators/icons/src/replacement-utils/get-color-replacement/get-color-replacement.ts similarity index 57% rename from generators/icons/src/replacement-utils/get-color-replacement/get-color-replacement.util.ts rename to generators/icons/src/replacement-utils/get-color-replacement/get-color-replacement.ts index 2cc1fb56b..e6edca79a 100644 --- a/generators/icons/src/replacement-utils/get-color-replacement/get-color-replacement.util.ts +++ b/generators/icons/src/replacement-utils/get-color-replacement/get-color-replacement.ts @@ -1,4 +1,4 @@ -import type { GetColorReplacement } from './get-color-replacement.interfaces.js' +import type { GetColorReplacement } from './interfaces.js' export const getColorReplacement: GetColorReplacement = (options) => ({ [options.color]: `currentColor`, diff --git a/generators/icons/src/replacement-utils/get-color-replacement/index.cts b/generators/icons/src/replacement-utils/get-color-replacement/index.cts index e3a4de6fa..86b920d9b 100644 --- a/generators/icons/src/replacement-utils/get-color-replacement/index.cts +++ b/generators/icons/src/replacement-utils/get-color-replacement/index.cts @@ -1 +1 @@ -export * from './get-color-replacement.util.cjs' +export * from './get-color-replacement.js' diff --git a/generators/icons/src/replacement-utils/get-color-replacement/index.ts b/generators/icons/src/replacement-utils/get-color-replacement/index.ts index 9178ae1f4..86b920d9b 100644 --- a/generators/icons/src/replacement-utils/get-color-replacement/index.ts +++ b/generators/icons/src/replacement-utils/get-color-replacement/index.ts @@ -1 +1 @@ -export * from './get-color-replacement.util.js' +export * from './get-color-replacement.js' diff --git a/generators/icons/src/replacement-utils/get-color-replacement/get-color-replacement.interfaces.ts b/generators/icons/src/replacement-utils/get-color-replacement/interfaces.ts similarity index 100% rename from generators/icons/src/replacement-utils/get-color-replacement/get-color-replacement.interfaces.ts rename to generators/icons/src/replacement-utils/get-color-replacement/interfaces.ts diff --git a/generators/icons/src/replacement-utils/write-replacements-file/index.ts b/generators/icons/src/replacement-utils/write-replacements-file/index.ts index d988a0be8..b384e9e5e 100644 --- a/generators/icons/src/replacement-utils/write-replacements-file/index.ts +++ b/generators/icons/src/replacement-utils/write-replacements-file/index.ts @@ -1 +1 @@ -export * from './write-replacements-file.util.js' +export * from './write-replacements-file.js' diff --git a/generators/icons/src/replacement-utils/write-replacements-file/write-replacements-file.util.ts b/generators/icons/src/replacement-utils/write-replacements-file/write-replacements-file.ts similarity index 88% rename from generators/icons/src/replacement-utils/write-replacements-file/write-replacements-file.util.ts rename to generators/icons/src/replacement-utils/write-replacements-file/write-replacements-file.ts index d4307333f..74f883edc 100644 --- a/generators/icons/src/replacement-utils/write-replacements-file/write-replacements-file.util.ts +++ b/generators/icons/src/replacement-utils/write-replacements-file/write-replacements-file.ts @@ -1,6 +1,6 @@ /* eslint-disable n/no-sync */ -import type { Replacements } from '../../icons.interfaces.js' +import type { Replacements } from '../../interfaces.js' import { writeFileSync } from 'node:fs' diff --git a/generators/icons/src/svgr-utils/build-svgr.util.ts b/generators/icons/src/svgr-utils/build-svgr.ts similarity index 64% rename from generators/icons/src/svgr-utils/build-svgr.util.ts rename to generators/icons/src/svgr-utils/build-svgr.ts index 805a87e6e..c05321f76 100644 --- a/generators/icons/src/svgr-utils/build-svgr.util.ts +++ b/generators/icons/src/svgr-utils/build-svgr.ts @@ -1,8 +1,8 @@ -import type { Replacements } from '../icons.interfaces.js' +import type { Replacements } from '../interfaces.js' import { compileIcons } from './compile-icons/index.js' -import { discoverIcons } from './discover-icons.util.js' -import { writeCompiledIcons } from './write-compiled-icons.util.js' +import { discoverIcons } from './discover-icons.js' +import { writeCompiledIcons } from './write-compiled-icons.js' export const buildSvgr = async ( iconsPath: string, diff --git a/generators/icons/src/svgr-utils/compile-icons/icon-jsx.ts b/generators/icons/src/svgr-utils/compile-icons/icon-jsx.ts index 860d7434f..9fa0c0777 100644 --- a/generators/icons/src/svgr-utils/compile-icons/icon-jsx.ts +++ b/generators/icons/src/svgr-utils/compile-icons/icon-jsx.ts @@ -1,4 +1,4 @@ -import type { Icon } from '../../icons.interfaces.js' +import type { Icon } from '../../interfaces.js' import { transform } from '@svgr/core' diff --git a/generators/icons/src/svgr-utils/compile-icons/icons.ts b/generators/icons/src/svgr-utils/compile-icons/icons.ts index a51c84ef1..a732c26cd 100644 --- a/generators/icons/src/svgr-utils/compile-icons/icons.ts +++ b/generators/icons/src/svgr-utils/compile-icons/icons.ts @@ -1,6 +1,6 @@ -import type { Icon } from '../../icons.interfaces.js' -import type { Replacements } from '../../icons.interfaces.js' -import type { Source } from '../../icons.interfaces.js' +import type { Icon } from '../../interfaces.js' +import type { Replacements } from '../../interfaces.js' +import type { Source } from '../../interfaces.js' import { transform } from '@svgr/core' diff --git a/generators/icons/src/svgr-utils/discover-icons.util.ts b/generators/icons/src/svgr-utils/discover-icons.ts similarity index 91% rename from generators/icons/src/svgr-utils/discover-icons.util.ts rename to generators/icons/src/svgr-utils/discover-icons.ts index 535221359..39c2459d3 100644 --- a/generators/icons/src/svgr-utils/discover-icons.util.ts +++ b/generators/icons/src/svgr-utils/discover-icons.ts @@ -1,6 +1,6 @@ /* eslint-disable n/no-sync */ -import type { Icon } from '../icons.interfaces.js' +import type { Icon } from '../interfaces.js' import fs from 'node:fs' diff --git a/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.constants.ts b/generators/icons/src/svgr-utils/get-svg-colors/config/constants.ts similarity index 100% rename from generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.constants.ts rename to generators/icons/src/svgr-utils/get-svg-colors/config/constants.ts diff --git a/generators/icons/src/svgr-utils/get-svg-colors/regex.ts b/generators/icons/src/svgr-utils/get-svg-colors/config/regex.ts similarity index 100% rename from generators/icons/src/svgr-utils/get-svg-colors/regex.ts rename to generators/icons/src/svgr-utils/get-svg-colors/config/regex.ts diff --git a/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.util.ts b/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.ts similarity index 69% rename from generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.util.ts rename to generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.ts index 376ae1132..3433f8367 100644 --- a/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.util.ts +++ b/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.ts @@ -1,13 +1,13 @@ -import type { GetSvgColorsOptions } from './get-svg-colors.interfaces.js' - -import { ATTRIBUTE_NAME_PATTERN } from './regex.js' -import { ATTRIBUTE_PATTERN } from './regex.js' -import { STYLE_PROPERTY_NAME_PATTERN } from './regex.js' -import { STYLE_PROPERTY_PATTERN } from './regex.js' -import { valueGroupName } from './get-svg-colors.constants.js' -import { normalizeAttributeName } from './normalize-attribute-name.helper.js' -import { trimQuotes } from './trim-quotes.helper.js' -import { validateColor } from './validate-color.helper.js' +import type { GetSvgColorsOptions } from './interfaces.js' + +import { ATTRIBUTE_NAME_PATTERN } from './config/regex.js' +import { ATTRIBUTE_PATTERN } from './config/regex.js' +import { STYLE_PROPERTY_NAME_PATTERN } from './config/regex.js' +import { STYLE_PROPERTY_PATTERN } from './config/regex.js' +import { valueGroupName } from './config/constants.js' +import { normalizeAttributeName } from './helpers/normalize-attribute-name.js' +import { trimQuotes } from './helpers/trim-quotes.js' +import { validateColor } from './helpers/validate-color.js' const getColorAttributeNames = (options: GetSvgColorsOptions): Set => new Set(Array.from(options.colorAttributes ?? []).map(normalizeAttributeName)) diff --git a/generators/icons/src/svgr-utils/get-svg-colors/normalize-attribute-name.helper.ts b/generators/icons/src/svgr-utils/get-svg-colors/helpers/normalize-attribute-name.ts similarity index 68% rename from generators/icons/src/svgr-utils/get-svg-colors/normalize-attribute-name.helper.ts rename to generators/icons/src/svgr-utils/get-svg-colors/helpers/normalize-attribute-name.ts index a825178b6..99995acfc 100644 --- a/generators/icons/src/svgr-utils/get-svg-colors/normalize-attribute-name.helper.ts +++ b/generators/icons/src/svgr-utils/get-svg-colors/helpers/normalize-attribute-name.ts @@ -1,4 +1,4 @@ -import { NON_WORD_PATTERN } from './regex.js' +import { NON_WORD_PATTERN } from '../config/regex.js' export const normalizeAttributeName = (name: string): string => name.replace(NON_WORD_PATTERN, '').toLowerCase() diff --git a/generators/icons/src/svgr-utils/get-svg-colors/trim-quotes.helper.ts b/generators/icons/src/svgr-utils/get-svg-colors/helpers/trim-quotes.ts similarity index 84% rename from generators/icons/src/svgr-utils/get-svg-colors/trim-quotes.helper.ts rename to generators/icons/src/svgr-utils/get-svg-colors/helpers/trim-quotes.ts index e0516a874..53cf247cf 100644 --- a/generators/icons/src/svgr-utils/get-svg-colors/trim-quotes.helper.ts +++ b/generators/icons/src/svgr-utils/get-svg-colors/helpers/trim-quotes.ts @@ -1,4 +1,4 @@ -import { quoteCharacters } from './get-svg-colors.constants.js' +import { quoteCharacters } from '../config/constants.js' export const trimQuotes = (value: string): string => { const trimmedValue = value.trim() diff --git a/generators/icons/src/svgr-utils/get-svg-colors/validate-color.helper.ts b/generators/icons/src/svgr-utils/get-svg-colors/helpers/validate-color.ts similarity index 75% rename from generators/icons/src/svgr-utils/get-svg-colors/validate-color.helper.ts rename to generators/icons/src/svgr-utils/get-svg-colors/helpers/validate-color.ts index 03a673ae9..8578f0ffe 100644 --- a/generators/icons/src/svgr-utils/get-svg-colors/validate-color.helper.ts +++ b/generators/icons/src/svgr-utils/get-svg-colors/helpers/validate-color.ts @@ -1,4 +1,4 @@ -import type { GetSvgColorsOptions } from './get-svg-colors.interfaces.js' +import type { GetSvgColorsOptions } from '../interfaces.js' export const validateColor = (color: string, options: GetSvgColorsOptions): boolean => { if (!color) { diff --git a/generators/icons/src/svgr-utils/get-svg-colors/index.ts b/generators/icons/src/svgr-utils/get-svg-colors/index.ts index bed5362ce..e416e19bc 100644 --- a/generators/icons/src/svgr-utils/get-svg-colors/index.ts +++ b/generators/icons/src/svgr-utils/get-svg-colors/index.ts @@ -1,2 +1,2 @@ -export * from './get-svg-colors.util.js' -export type * from './get-svg-colors.interfaces.js' +export * from './get-svg-colors.js' +export type * from './interfaces.js' diff --git a/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.interfaces.ts b/generators/icons/src/svgr-utils/get-svg-colors/interfaces.ts similarity index 100% rename from generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.interfaces.ts rename to generators/icons/src/svgr-utils/get-svg-colors/interfaces.ts diff --git a/generators/icons/src/svgr-utils/index.ts b/generators/icons/src/svgr-utils/index.ts index 1ac2b7a22..32e3c4966 100644 --- a/generators/icons/src/svgr-utils/index.ts +++ b/generators/icons/src/svgr-utils/index.ts @@ -1,4 +1,4 @@ -export * from './build-svgr.util.js' +export * from './build-svgr.js' export * from './compile-icons/index.js' -export * from './discover-icons.util.js' +export * from './discover-icons.js' export * from './get-svg-colors/index.js' diff --git a/generators/icons/src/svgr-utils/write-compiled-icons.util.ts b/generators/icons/src/svgr-utils/write-compiled-icons.ts similarity index 95% rename from generators/icons/src/svgr-utils/write-compiled-icons.util.ts rename to generators/icons/src/svgr-utils/write-compiled-icons.ts index a90821009..2a26363e5 100644 --- a/generators/icons/src/svgr-utils/write-compiled-icons.util.ts +++ b/generators/icons/src/svgr-utils/write-compiled-icons.ts @@ -1,6 +1,6 @@ /* eslint-disable n/no-sync */ -import type { Source } from '../icons.interfaces.js' +import type { Source } from '../interfaces.js' import fs from 'node:fs' import path from 'node:path' From 5d548f71935d6170d805a4ca8e16a99d89b0104a Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Fri, 19 Jun 2026 03:29:33 +0300 Subject: [PATCH 04/19] fix(atls-ui-generators): return cheerio generator instead of regex --- generators/icons/package.json | 4 +- .../create-replacements/index.cts | 2 +- .../get-color-replacement/index.cts | 2 +- .../get-svg-colors/config/constants.ts | 3 - .../svgr-utils/get-svg-colors/config/regex.ts | 9 -- .../svgr-utils/get-svg-colors/constants.ts | 1 + .../extract-color-from-element.ts | 30 ++++ .../get-svg-colors/get-svg-colors.ts | 51 ++---- .../helpers/normalize-attribute-name.ts | 4 - .../get-svg-colors/helpers/trim-quotes.ts | 13 -- yarn.lock | 146 ++++++++++++++++-- 11 files changed, 183 insertions(+), 82 deletions(-) delete mode 100644 generators/icons/src/svgr-utils/get-svg-colors/config/constants.ts delete mode 100644 generators/icons/src/svgr-utils/get-svg-colors/config/regex.ts create mode 100644 generators/icons/src/svgr-utils/get-svg-colors/constants.ts create mode 100644 generators/icons/src/svgr-utils/get-svg-colors/extract-color-from-element.ts delete mode 100644 generators/icons/src/svgr-utils/get-svg-colors/helpers/normalize-attribute-name.ts delete mode 100644 generators/icons/src/svgr-utils/get-svg-colors/helpers/trim-quotes.ts diff --git a/generators/icons/package.json b/generators/icons/package.json index c97190fa3..8d6470748 100644 --- a/generators/icons/package.json +++ b/generators/icons/package.json @@ -12,7 +12,7 @@ }, "main": "src/index.ts", "bin": { - "generate-icons": "dist/icons.generator.js" + "generate-icons": "dist/generator.js" }, "files": [ "dist" @@ -28,7 +28,9 @@ "@svgr/core": "8.1.0", "@swc-node/register": "1.9.0", "@swc/core": "1.6.1", + "cheerio": "1.1.2", "clipanion": "4.0.0-rc.4", + "domhandler": "5.0.3", "glob": "11.0.2", "is-svg": "6.1.0" }, diff --git a/generators/icons/src/replacement-utils/create-replacements/index.cts b/generators/icons/src/replacement-utils/create-replacements/index.cts index d8303f53a..40fe4a3a5 100644 --- a/generators/icons/src/replacement-utils/create-replacements/index.cts +++ b/generators/icons/src/replacement-utils/create-replacements/index.cts @@ -1 +1 @@ -export * from './create-replacements.js' +export * from './create-replacements.cjs' diff --git a/generators/icons/src/replacement-utils/get-color-replacement/index.cts b/generators/icons/src/replacement-utils/get-color-replacement/index.cts index 86b920d9b..db67688e4 100644 --- a/generators/icons/src/replacement-utils/get-color-replacement/index.cts +++ b/generators/icons/src/replacement-utils/get-color-replacement/index.cts @@ -1 +1 @@ -export * from './get-color-replacement.js' +export * from './get-color-replacement.cjs' diff --git a/generators/icons/src/svgr-utils/get-svg-colors/config/constants.ts b/generators/icons/src/svgr-utils/get-svg-colors/config/constants.ts deleted file mode 100644 index 7ed578980..000000000 --- a/generators/icons/src/svgr-utils/get-svg-colors/config/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const valueGroupName = 'value' - -export const quoteCharacters = ['"', "'"] diff --git a/generators/icons/src/svgr-utils/get-svg-colors/config/regex.ts b/generators/icons/src/svgr-utils/get-svg-colors/config/regex.ts deleted file mode 100644 index 94a75ae65..000000000 --- a/generators/icons/src/svgr-utils/get-svg-colors/config/regex.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const ATTRIBUTE_PATTERN = /[\w:-]+\s*=\s*(["'])(?.*?)\1/g - -export const ATTRIBUTE_NAME_PATTERN = /^[\w:-]+/ - -export const NON_WORD_PATTERN = /[^a-z0-9]/gi - -export const STYLE_PROPERTY_PATTERN = /[\w-]+\s*:\s*(?[^;,}]+)/g - -export const STYLE_PROPERTY_NAME_PATTERN = /^[\w-]+/ diff --git a/generators/icons/src/svgr-utils/get-svg-colors/constants.ts b/generators/icons/src/svgr-utils/get-svg-colors/constants.ts new file mode 100644 index 000000000..e696588aa --- /dev/null +++ b/generators/icons/src/svgr-utils/get-svg-colors/constants.ts @@ -0,0 +1 @@ +export const styleColorAttributeKey = 'style' diff --git a/generators/icons/src/svgr-utils/get-svg-colors/extract-color-from-element.ts b/generators/icons/src/svgr-utils/get-svg-colors/extract-color-from-element.ts new file mode 100644 index 000000000..1c320d02c --- /dev/null +++ b/generators/icons/src/svgr-utils/get-svg-colors/extract-color-from-element.ts @@ -0,0 +1,30 @@ +import type { CheerioAPI } from 'cheerio/slim' +import type { AnyNode } from 'domhandler' + +import type { GetSvgColorsOptions } from './interfaces.js' + +import { styleColorAttributeKey } from './constants.js' + +export const extractColorFromElement = ( + cheerio: CheerioAPI, + element: AnyNode, + options: GetSvgColorsOptions +): Set => { + const colors = new Set() + + const attributes = cheerio(element).attr() ?? {} + + Object.entries(attributes).forEach(([name, value]) => { + if (options.colorAttributes?.has(name)) { + colors.add(value) + } + + if (name === styleColorAttributeKey) { + options.colorAttributes?.forEach((colorAttribute) => { + colors.add(cheerio(element).css(colorAttribute) || '') + }) + } + }) + + return colors +} diff --git a/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.ts b/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.ts index 3433f8367..51a5bdd8b 100644 --- a/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.ts +++ b/generators/icons/src/svgr-utils/get-svg-colors/get-svg-colors.ts @@ -1,49 +1,20 @@ -import type { GetSvgColorsOptions } from './interfaces.js' - -import { ATTRIBUTE_NAME_PATTERN } from './config/regex.js' -import { ATTRIBUTE_PATTERN } from './config/regex.js' -import { STYLE_PROPERTY_NAME_PATTERN } from './config/regex.js' -import { STYLE_PROPERTY_PATTERN } from './config/regex.js' -import { valueGroupName } from './config/constants.js' -import { normalizeAttributeName } from './helpers/normalize-attribute-name.js' -import { trimQuotes } from './helpers/trim-quotes.js' -import { validateColor } from './helpers/validate-color.js' - -const getColorAttributeNames = (options: GetSvgColorsOptions): Set => - new Set(Array.from(options.colorAttributes ?? []).map(normalizeAttributeName)) - -const addAttributeColor = ( - colors: Set, - source: string, - colorAttributes: Set -): void => { - for (const match of source.matchAll(ATTRIBUTE_PATTERN)) { - const name = match[0].match(ATTRIBUTE_NAME_PATTERN)?.[0] - const value = match.groups?.[valueGroupName] - - if (name && value && colorAttributes.has(normalizeAttributeName(name))) { - colors.add(trimQuotes(value)) - } - } -} +import type { GetSvgColorsOptions } from './interfaces.js' -const addStyleColor = (colors: Set, source: string, colorAttributes: Set): void => { - for (const match of source.matchAll(STYLE_PROPERTY_PATTERN)) { - const name = match[0].match(STYLE_PROPERTY_NAME_PATTERN)?.[0] - const value = match.groups?.[valueGroupName] +import { load } from 'cheerio/slim' - if (name && value && colorAttributes.has(normalizeAttributeName(name))) { - colors.add(trimQuotes(value)) - } - } -} +import { extractColorFromElement } from './extract-color-from-element.js' +import { validateColor } from './helpers/validate-color.js' export const getSvgColors = (source: string, options: GetSvgColorsOptions = {}): Array => { const colors = new Set() - const colorAttributes = getColorAttributeNames(options) - addAttributeColor(colors, source, colorAttributes) - addStyleColor(colors, source, colorAttributes) + const cheerio = load(source, { xml: true }) + + cheerio('*').each((index, element) => { + const elementColors = extractColorFromElement(cheerio, element, options) + + elementColors.forEach((color) => colors.add(color)) + }) const colorsArray = Array.from(colors) diff --git a/generators/icons/src/svgr-utils/get-svg-colors/helpers/normalize-attribute-name.ts b/generators/icons/src/svgr-utils/get-svg-colors/helpers/normalize-attribute-name.ts deleted file mode 100644 index 99995acfc..000000000 --- a/generators/icons/src/svgr-utils/get-svg-colors/helpers/normalize-attribute-name.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { NON_WORD_PATTERN } from '../config/regex.js' - -export const normalizeAttributeName = (name: string): string => - name.replace(NON_WORD_PATTERN, '').toLowerCase() diff --git a/generators/icons/src/svgr-utils/get-svg-colors/helpers/trim-quotes.ts b/generators/icons/src/svgr-utils/get-svg-colors/helpers/trim-quotes.ts deleted file mode 100644 index 53cf247cf..000000000 --- a/generators/icons/src/svgr-utils/get-svg-colors/helpers/trim-quotes.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { quoteCharacters } from '../config/constants.js' - -export const trimQuotes = (value: string): string => { - const trimmedValue = value.trim() - - const quoteCharacter = quoteCharacters.find((quote) => trimmedValue.startsWith(quote)) - - if (!quoteCharacter || !trimmedValue.endsWith(quoteCharacter)) { - return trimmedValue - } - - return trimmedValue.slice(quoteCharacter.length, -quoteCharacter.length) -} diff --git a/yarn.lock b/yarn.lock index f8499c8b4..a4d351395 100644 --- a/yarn.lock +++ b/yarn.lock @@ -320,13 +320,15 @@ __metadata: "@swc-node/register": "npm:1.9.0" "@swc/core": "npm:1.6.1" "@types/node": "npm:22.15.3" + cheerio: "npm:1.1.2" clipanion: "npm:4.0.0-rc.4" + domhandler: "npm:5.0.3" glob: "npm:11.0.2" is-svg: "npm:6.1.0" tsx: "npm:4.20.5" typescript: "npm:5.5.4" bin: - generate-icons: dist/icons.generator.js + generate-icons: dist/generator.js languageName: unknown linkType: soft @@ -6521,6 +6523,39 @@ __metadata: languageName: node linkType: hard +"cheerio-select@npm:^2.1.0": + version: 2.1.0 + resolution: "cheerio-select@npm:2.1.0" + dependencies: + boolbase: "npm:^1.0.0" + css-select: "npm:^5.1.0" + css-what: "npm:^6.1.0" + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.0.1" + checksum: 10/b5d89208c23468c3a32d1e04f88b9e8c6e332e3649650c5cd29255e2cebc215071ae18563f58c3dc3f6ef4c234488fc486035490fceb78755572288245e2931a + languageName: node + linkType: hard + +"cheerio@npm:1.1.2": + version: 1.1.2 + resolution: "cheerio@npm:1.1.2" + dependencies: + cheerio-select: "npm:^2.1.0" + dom-serializer: "npm:^2.0.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.2.2" + encoding-sniffer: "npm:^0.2.1" + htmlparser2: "npm:^10.0.0" + parse5: "npm:^7.3.0" + parse5-htmlparser2-tree-adapter: "npm:^7.1.0" + parse5-parser-stream: "npm:^7.1.2" + undici: "npm:^7.12.0" + whatwg-mimetype: "npm:^4.0.0" + checksum: 10/6b654bf5a358d3406eed5a3ae84530bab0d6d2d581d0a92d3c0666c310648d4300a00b11335e15007a35922ad3743931385ef17bec8d67b4fa077f10d1aaf2b5 + languageName: node + linkType: hard + "chokidar@npm:^4.0.1": version: 4.0.3 resolution: "chokidar@npm:4.0.3" @@ -7372,6 +7407,15 @@ __metadata: languageName: node linkType: hard +"domhandler@npm:5.0.3, domhandler@npm:^5.0.2, domhandler@npm:^5.0.3": + version: 5.0.3 + resolution: "domhandler@npm:5.0.3" + dependencies: + domelementtype: "npm:^2.3.0" + checksum: 10/809b805a50a9c6884a29f38aec0a4e1b4537f40e1c861950ed47d10b049febe6b79ab72adaeeebb3cc8fc1cd33f34e97048a72a9265103426d93efafa78d3e96 + languageName: node + linkType: hard + "domhandler@npm:^4.0.0, domhandler@npm:^4.2.0, domhandler@npm:^4.3.1": version: 4.3.1 resolution: "domhandler@npm:4.3.1" @@ -7381,15 +7425,6 @@ __metadata: languageName: node linkType: hard -"domhandler@npm:^5.0.2, domhandler@npm:^5.0.3": - version: 5.0.3 - resolution: "domhandler@npm:5.0.3" - dependencies: - domelementtype: "npm:^2.3.0" - checksum: 10/809b805a50a9c6884a29f38aec0a4e1b4537f40e1c861950ed47d10b049febe6b79ab72adaeeebb3cc8fc1cd33f34e97048a72a9265103426d93efafa78d3e96 - languageName: node - linkType: hard - "domutils@npm:^2.5.2, domutils@npm:^2.8.0": version: 2.8.0 resolution: "domutils@npm:2.8.0" @@ -7412,6 +7447,17 @@ __metadata: languageName: node linkType: hard +"domutils@npm:^3.2.1, domutils@npm:^3.2.2": + version: 3.2.2 + resolution: "domutils@npm:3.2.2" + dependencies: + dom-serializer: "npm:^2.0.0" + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + checksum: 10/2e08842151aa406f50fe5e6d494f4ec73c2373199fa00d1f77b56ec604e566b7f226312ae35ab8160bb7f27a27c7285d574c8044779053e499282ca9198be210 + languageName: node + linkType: hard + "dot-case@npm:^3.0.4": version: 3.0.4 resolution: "dot-case@npm:3.0.4" @@ -7518,6 +7564,16 @@ __metadata: languageName: node linkType: hard +"encoding-sniffer@npm:^0.2.1": + version: 0.2.1 + resolution: "encoding-sniffer@npm:0.2.1" + dependencies: + iconv-lite: "npm:^0.6.3" + whatwg-encoding: "npm:^3.1.1" + checksum: 10/7d747238239408d52e8bceee22fcdc47546049866d19d601e7dc89e55d226922c51912ef046d7b38951970e8fd17e1e761cef3de98a4b2f46fc91c8a1ac143c9 + languageName: node + linkType: hard + "encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -7591,6 +7647,13 @@ __metadata: languageName: node linkType: hard +"entities@npm:^6.0.0": + version: 6.0.1 + resolution: "entities@npm:6.0.1" + checksum: 10/62af1307202884349d2867f0aac5c60d8b57102ea0b0e768b16246099512c28e239254ad772d6834e7e14cb1b6f153fc3d0c031934e3183b086c86d3838d874a + languageName: node + linkType: hard + "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -9645,6 +9708,18 @@ __metadata: languageName: node linkType: hard +"htmlparser2@npm:^10.0.0": + version: 10.0.0 + resolution: "htmlparser2@npm:10.0.0" + dependencies: + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.2.1" + entities: "npm:^6.0.0" + checksum: 10/768870f0e020dca19dc45df206cb6ac466c5dba6566c8fca4ca880347eed409f9977028d08644ac516bca8628ac9c7ded5a3847dc3ee1c043f049abf9e817154 + languageName: node + linkType: hard + "htmlparser2@npm:^6.1.0": version: 6.1.0 resolution: "htmlparser2@npm:6.1.0" @@ -12008,6 +12083,34 @@ __metadata: languageName: node linkType: hard +"parse5-htmlparser2-tree-adapter@npm:^7.1.0": + version: 7.1.0 + resolution: "parse5-htmlparser2-tree-adapter@npm:7.1.0" + dependencies: + domhandler: "npm:^5.0.3" + parse5: "npm:^7.0.0" + checksum: 10/75910af9137451e9c53e1e0d712f7393f484e89e592b1809ee62ad6cedd61b98daeaa5206ff5d9f06778002c91fac311afedde4880e1916fdb44fa71199dae73 + languageName: node + linkType: hard + +"parse5-parser-stream@npm:^7.1.2": + version: 7.1.2 + resolution: "parse5-parser-stream@npm:7.1.2" + dependencies: + parse5: "npm:^7.0.0" + checksum: 10/75b232d460bce6bd0e35012750a78ef034f40ccf550b7c6cec3122395af6b4553202ad3663ad468cf537ead5a2e13b6727670395fd0ff548faccad1dc2dc93cf + languageName: node + linkType: hard + +"parse5@npm:^7.0.0, parse5@npm:^7.3.0": + version: 7.3.0 + resolution: "parse5@npm:7.3.0" + dependencies: + entities: "npm:^6.0.0" + checksum: 10/b0e48be20b820c655b138b86fa6fb3a790de6c891aa2aba536524f8027b4dca4fe538f11a0e5cf2f6f847d120dbb9e4822dcaeb933ff1e10850a2ef0154d1d88 + languageName: node + linkType: hard + "parseurl@npm:^1.3.3": version: 1.3.3 resolution: "parseurl@npm:1.3.3" @@ -14684,6 +14787,13 @@ __metadata: languageName: node linkType: hard +"undici@npm:^7.12.0": + version: 7.16.0 + resolution: "undici@npm:7.16.0" + checksum: 10/2bb71672b23d3dc0f56f1b7fb6c936e4487a350db46eaafc03f2f9107f99cdf8e51ecdd32e589e2381ef47a64b6369cfb31f328b2c3ea663023aa47bc5258b9e + languageName: node + linkType: hard + "union@npm:~0.5.0": version: 0.5.0 resolution: "union@npm:0.5.0" @@ -15046,6 +15156,22 @@ __metadata: languageName: node linkType: hard +"whatwg-encoding@npm:^3.1.1": + version: 3.1.1 + resolution: "whatwg-encoding@npm:3.1.1" + dependencies: + iconv-lite: "npm:0.6.3" + checksum: 10/bbef815eb67f91487c7f2ef96329743f5fd8357d7d62b1119237d25d41c7e452dff8197235b2d3c031365a17f61d3bb73ca49d0ed1582475aa4a670815e79534 + languageName: node + linkType: hard + +"whatwg-mimetype@npm:^4.0.0": + version: 4.0.0 + resolution: "whatwg-mimetype@npm:4.0.0" + checksum: 10/894a618e2d90bf444b6f309f3ceb6e58cf21b2beaa00c8b333696958c4076f0c7b30b9d33413c9ffff7c5832a0a0c8569e5bb347ef44beded72aeefd0acd62e8 + languageName: node + linkType: hard + "whatwg-url@npm:^5.0.0": version: 5.0.0 resolution: "whatwg-url@npm:5.0.0" From 709036cc546813cad94c296d31f221636a8f8c96 Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Tue, 23 Jun 2026 04:15:57 +0300 Subject: [PATCH 05/19] refactor(atls-ui-generators): extract lib from scripts --- .../comparison}/compare-directories.ts | 4 +- .../comparison}/compare-files.ts | 4 +- .../check-generated}/constants.ts | 2 +- .../errors/command-failed.error.ts | 0 .../errors/directory-required.error.ts | 0 .../errors/file-required.error.ts | 0 .../errors/generated-files-outdated.error.ts | 0 .../filesystem}/collect-files.ts | 0 .../filesystem}/is-directory.ts | 0 .../check-generated/filesystem}/is-file.ts | 0 .../filesystem}/is-outdated-file.ts | 0 .../generation}/format-files.ts | 2 +- .../generation}/generate-icons.ts | 2 +- .../generation}/generate-replacements.ts | 2 +- ui-admin/icons/lib/check-generated/index.ts | 78 ++++++++++++++++++ .../check-generated}/interfaces.ts | 0 .../check-generated/process}/run-command.ts | 0 ui-admin/icons/scripts/check-generated.ts | 80 +------------------ 18 files changed, 88 insertions(+), 86 deletions(-) rename ui-admin/icons/{scripts/lib => lib/check-generated/comparison}/compare-directories.ts (92%) rename ui-admin/icons/{scripts/lib => lib/check-generated/comparison}/compare-files.ts (88%) rename ui-admin/icons/{scripts => lib/check-generated}/constants.ts (96%) rename ui-admin/icons/{scripts => lib/check-generated}/errors/command-failed.error.ts (100%) rename ui-admin/icons/{scripts => lib/check-generated}/errors/directory-required.error.ts (100%) rename ui-admin/icons/{scripts => lib/check-generated}/errors/file-required.error.ts (100%) rename ui-admin/icons/{scripts => lib/check-generated}/errors/generated-files-outdated.error.ts (100%) rename ui-admin/icons/{scripts/lib => lib/check-generated/filesystem}/collect-files.ts (100%) rename ui-admin/icons/{scripts/conditions => lib/check-generated/filesystem}/is-directory.ts (100%) rename ui-admin/icons/{scripts/conditions => lib/check-generated/filesystem}/is-file.ts (100%) rename ui-admin/icons/{scripts/conditions => lib/check-generated/filesystem}/is-outdated-file.ts (100%) rename ui-admin/icons/{scripts/lib => lib/check-generated/generation}/format-files.ts (88%) rename ui-admin/icons/{scripts/lib => lib/check-generated/generation}/generate-icons.ts (92%) rename ui-admin/icons/{scripts/lib => lib/check-generated/generation}/generate-replacements.ts (91%) create mode 100644 ui-admin/icons/lib/check-generated/index.ts rename ui-admin/icons/{scripts => lib/check-generated}/interfaces.ts (100%) rename ui-admin/icons/{scripts/lib => lib/check-generated/process}/run-command.ts (100%) diff --git a/ui-admin/icons/scripts/lib/compare-directories.ts b/ui-admin/icons/lib/check-generated/comparison/compare-directories.ts similarity index 92% rename from ui-admin/icons/scripts/lib/compare-directories.ts rename to ui-admin/icons/lib/check-generated/comparison/compare-directories.ts index d1a291769..254581776 100644 --- a/ui-admin/icons/scripts/lib/compare-directories.ts +++ b/ui-admin/icons/lib/check-generated/comparison/compare-directories.ts @@ -3,9 +3,9 @@ import type { OutdatedFile } from '../interfaces.js' import { join } from 'node:path' -import { isOutdatedFile } from '../conditions/is-outdated-file.js' import { outdatedFileReasons } from '../constants.js' -import { collectFiles } from './collect-files.js' +import { collectFiles } from '../filesystem/collect-files.js' +import { isOutdatedFile } from '../filesystem/is-outdated-file.js' export const compareDirectories = async ({ actualPath, diff --git a/ui-admin/icons/scripts/lib/compare-files.ts b/ui-admin/icons/lib/check-generated/comparison/compare-files.ts similarity index 88% rename from ui-admin/icons/scripts/lib/compare-files.ts rename to ui-admin/icons/lib/check-generated/comparison/compare-files.ts index d1f4b6615..cd3bbc7d6 100644 --- a/ui-admin/icons/scripts/lib/compare-files.ts +++ b/ui-admin/icons/lib/check-generated/comparison/compare-files.ts @@ -2,9 +2,9 @@ import type { OutdatedFile } from '../interfaces.js' import { basename } from 'node:path' -import { isFile } from '../conditions/is-file.js' -import { isOutdatedFile } from '../conditions/is-outdated-file.js' import { outdatedFileReasons } from '../constants.js' +import { isFile } from '../filesystem/is-file.js' +import { isOutdatedFile } from '../filesystem/is-outdated-file.js' export const compareFiles = async ( expectedPath: string, diff --git a/ui-admin/icons/scripts/constants.ts b/ui-admin/icons/lib/check-generated/constants.ts similarity index 96% rename from ui-admin/icons/scripts/constants.ts rename to ui-admin/icons/lib/check-generated/constants.ts index 5df1f87bf..c1c0f37a9 100644 --- a/ui-admin/icons/scripts/constants.ts +++ b/ui-admin/icons/lib/check-generated/constants.ts @@ -10,7 +10,7 @@ export const outdatedFileReasons = { unexpected: 'unexpected', } as const -export const packageRootPath = resolve(scriptDirectory, '..') +export const packageRootPath = resolve(scriptDirectory, '../..') export const workspaceRootPath = resolve(packageRootPath, '../..') diff --git a/ui-admin/icons/scripts/errors/command-failed.error.ts b/ui-admin/icons/lib/check-generated/errors/command-failed.error.ts similarity index 100% rename from ui-admin/icons/scripts/errors/command-failed.error.ts rename to ui-admin/icons/lib/check-generated/errors/command-failed.error.ts diff --git a/ui-admin/icons/scripts/errors/directory-required.error.ts b/ui-admin/icons/lib/check-generated/errors/directory-required.error.ts similarity index 100% rename from ui-admin/icons/scripts/errors/directory-required.error.ts rename to ui-admin/icons/lib/check-generated/errors/directory-required.error.ts diff --git a/ui-admin/icons/scripts/errors/file-required.error.ts b/ui-admin/icons/lib/check-generated/errors/file-required.error.ts similarity index 100% rename from ui-admin/icons/scripts/errors/file-required.error.ts rename to ui-admin/icons/lib/check-generated/errors/file-required.error.ts diff --git a/ui-admin/icons/scripts/errors/generated-files-outdated.error.ts b/ui-admin/icons/lib/check-generated/errors/generated-files-outdated.error.ts similarity index 100% rename from ui-admin/icons/scripts/errors/generated-files-outdated.error.ts rename to ui-admin/icons/lib/check-generated/errors/generated-files-outdated.error.ts diff --git a/ui-admin/icons/scripts/lib/collect-files.ts b/ui-admin/icons/lib/check-generated/filesystem/collect-files.ts similarity index 100% rename from ui-admin/icons/scripts/lib/collect-files.ts rename to ui-admin/icons/lib/check-generated/filesystem/collect-files.ts diff --git a/ui-admin/icons/scripts/conditions/is-directory.ts b/ui-admin/icons/lib/check-generated/filesystem/is-directory.ts similarity index 100% rename from ui-admin/icons/scripts/conditions/is-directory.ts rename to ui-admin/icons/lib/check-generated/filesystem/is-directory.ts diff --git a/ui-admin/icons/scripts/conditions/is-file.ts b/ui-admin/icons/lib/check-generated/filesystem/is-file.ts similarity index 100% rename from ui-admin/icons/scripts/conditions/is-file.ts rename to ui-admin/icons/lib/check-generated/filesystem/is-file.ts diff --git a/ui-admin/icons/scripts/conditions/is-outdated-file.ts b/ui-admin/icons/lib/check-generated/filesystem/is-outdated-file.ts similarity index 100% rename from ui-admin/icons/scripts/conditions/is-outdated-file.ts rename to ui-admin/icons/lib/check-generated/filesystem/is-outdated-file.ts diff --git a/ui-admin/icons/scripts/lib/format-files.ts b/ui-admin/icons/lib/check-generated/generation/format-files.ts similarity index 88% rename from ui-admin/icons/scripts/lib/format-files.ts rename to ui-admin/icons/lib/check-generated/generation/format-files.ts index 494a7053e..1de4e3d37 100644 --- a/ui-admin/icons/scripts/lib/format-files.ts +++ b/ui-admin/icons/lib/check-generated/generation/format-files.ts @@ -3,7 +3,7 @@ import { relative } from 'node:path' import { formatCommand } from '../constants.js' import { workspaceRootPath } from '../constants.js' import { yarnCommand } from '../constants.js' -import { runCommand } from './run-command.js' +import { runCommand } from '../process/run-command.js' export const formatFiles = async (filePaths: Array): Promise => { const relativeFilePaths = filePaths.map((filePath) => relative(workspaceRootPath, filePath)) diff --git a/ui-admin/icons/scripts/lib/generate-icons.ts b/ui-admin/icons/lib/check-generated/generation/generate-icons.ts similarity index 92% rename from ui-admin/icons/scripts/lib/generate-icons.ts rename to ui-admin/icons/lib/check-generated/generation/generate-icons.ts index 747bcb89f..b8a4d2f31 100644 --- a/ui-admin/icons/scripts/lib/generate-icons.ts +++ b/ui-admin/icons/lib/check-generated/generation/generate-icons.ts @@ -6,7 +6,7 @@ import { sourceReplacementsPath } from '../constants.js' import { workspaceRootPath } from '../constants.js' import { workspaceArgument } from '../constants.js' import { yarnCommand } from '../constants.js' -import { runCommand } from './run-command.js' +import { runCommand } from '../process/run-command.js' export const generateIcons = async (svgPath: string, outputPath: string): Promise => { await runCommand( diff --git a/ui-admin/icons/scripts/lib/generate-replacements.ts b/ui-admin/icons/lib/check-generated/generation/generate-replacements.ts similarity index 91% rename from ui-admin/icons/scripts/lib/generate-replacements.ts rename to ui-admin/icons/lib/check-generated/generation/generate-replacements.ts index a50b7a1a1..d42e44cfa 100644 --- a/ui-admin/icons/scripts/lib/generate-replacements.ts +++ b/ui-admin/icons/lib/check-generated/generation/generate-replacements.ts @@ -5,7 +5,7 @@ import { iconsPathArgument } from '../constants.js' import { workspaceRootPath } from '../constants.js' import { workspaceArgument } from '../constants.js' import { yarnCommand } from '../constants.js' -import { runCommand } from './run-command.js' +import { runCommand } from '../process/run-command.js' export const generateReplacements = async (svgPath: string, outputPath: string): Promise => { await runCommand( diff --git a/ui-admin/icons/lib/check-generated/index.ts b/ui-admin/icons/lib/check-generated/index.ts new file mode 100644 index 000000000..df8b436a7 --- /dev/null +++ b/ui-admin/icons/lib/check-generated/index.ts @@ -0,0 +1,78 @@ +import { mkdir } from 'node:fs/promises' +import { mkdtemp } from 'node:fs/promises' +import { rm } from 'node:fs/promises' +import { join } from 'node:path' + +import { DirectoryRequiredError } from './errors/directory-required.error.js' +import { FileRequiredError } from './errors/file-required.error.js' +import { GeneratedFilesOutdatedError } from './errors/generated-files-outdated.error.js' +import { compareDirectories } from './comparison/compare-directories.js' +import { compareFiles } from './comparison/compare-files.js' +import { checkSuccessMessage } from './constants.js' +import { generatedIconsDirectoryName } from './constants.js' +import { generatedReplacementsDirectoryName } from './constants.js' +import { outputLineBreak } from './constants.js' +import { packageRootPath } from './constants.js' +import { replacementsFileName } from './constants.js' +import { sourceIconsPath } from './constants.js' +import { sourceReplacementsPath } from './constants.js' +import { sourceSvgPath } from './constants.js' +import { temporaryDirectoryPrefix } from './constants.js' +import { collectFiles } from './filesystem/collect-files.js' +import { isDirectory } from './filesystem/is-directory.js' +import { isFile } from './filesystem/is-file.js' +import { formatFiles } from './generation/format-files.js' +import { generateIcons } from './generation/generate-icons.js' +import { generateReplacements } from './generation/generate-replacements.js' + +export const checkGenerated = async (): Promise => { + if (!(await isDirectory(sourceIconsPath))) { + throw new DirectoryRequiredError(sourceIconsPath) + } + + if (!(await isDirectory(sourceSvgPath))) { + throw new DirectoryRequiredError(sourceSvgPath) + } + + if (!(await isFile(sourceReplacementsPath))) { + throw new FileRequiredError(sourceReplacementsPath) + } + + await mkdir(packageRootPath, { recursive: true }) + + const temporaryPath = await mkdtemp(join(packageRootPath, temporaryDirectoryPrefix)) + const generatedIconsPath = join(temporaryPath, generatedIconsDirectoryName) + const generatedReplacementsPath = join(temporaryPath, generatedReplacementsDirectoryName) + + try { + await mkdir(generatedIconsPath) + await mkdir(generatedReplacementsPath) + + await generateReplacements(sourceSvgPath, generatedReplacementsPath) + await generateIcons(sourceSvgPath, generatedIconsPath) + await formatFiles([ + join(generatedReplacementsPath, replacementsFileName), + ...(await collectFiles(generatedIconsPath)).map((filePath) => + join(generatedIconsPath, filePath)), + ]) + + const outdatedFiles = [ + ...(await compareDirectories({ + actualPath: sourceIconsPath, + expectedPath: generatedIconsPath, + })), + ...(await compareFiles( + join(generatedReplacementsPath, replacementsFileName), + sourceReplacementsPath + )), + ] + + if (outdatedFiles.length > 0) { + throw new GeneratedFilesOutdatedError(outdatedFiles) + } + + process.stdout.write(`${checkSuccessMessage}${outputLineBreak}`) + } finally { + await rm(temporaryPath, { force: true, recursive: true }) + } +} diff --git a/ui-admin/icons/scripts/interfaces.ts b/ui-admin/icons/lib/check-generated/interfaces.ts similarity index 100% rename from ui-admin/icons/scripts/interfaces.ts rename to ui-admin/icons/lib/check-generated/interfaces.ts diff --git a/ui-admin/icons/scripts/lib/run-command.ts b/ui-admin/icons/lib/check-generated/process/run-command.ts similarity index 100% rename from ui-admin/icons/scripts/lib/run-command.ts rename to ui-admin/icons/lib/check-generated/process/run-command.ts diff --git a/ui-admin/icons/scripts/check-generated.ts b/ui-admin/icons/scripts/check-generated.ts index 8b4c5688b..d3a607f87 100644 --- a/ui-admin/icons/scripts/check-generated.ts +++ b/ui-admin/icons/scripts/check-generated.ts @@ -1,81 +1,5 @@ -import { mkdir } from 'node:fs/promises' -import { mkdtemp } from 'node:fs/promises' -import { rm } from 'node:fs/promises' -import { join } from 'node:path' - -import { DirectoryRequiredError } from './errors/directory-required.error.js' -import { FileRequiredError } from './errors/file-required.error.js' -import { GeneratedFilesOutdatedError } from './errors/generated-files-outdated.error.js' -import { isDirectory } from './conditions/is-directory.js' -import { isFile } from './conditions/is-file.js' -import { checkSuccessMessage } from './constants.js' -import { generatedIconsDirectoryName } from './constants.js' -import { generatedReplacementsDirectoryName } from './constants.js' -import { outputLineBreak } from './constants.js' -import { packageRootPath } from './constants.js' -import { replacementsFileName } from './constants.js' -import { sourceIconsPath } from './constants.js' -import { sourceReplacementsPath } from './constants.js' -import { sourceSvgPath } from './constants.js' -import { temporaryDirectoryPrefix } from './constants.js' -import { collectFiles } from './lib/collect-files.js' -import { compareDirectories } from './lib/compare-directories.js' -import { compareFiles } from './lib/compare-files.js' -import { formatFiles } from './lib/format-files.js' -import { generateIcons } from './lib/generate-icons.js' -import { generateReplacements } from './lib/generate-replacements.js' - -const checkGenerated = async (): Promise => { - if (!(await isDirectory(sourceIconsPath))) { - throw new DirectoryRequiredError(sourceIconsPath) - } - - if (!(await isDirectory(sourceSvgPath))) { - throw new DirectoryRequiredError(sourceSvgPath) - } - - if (!(await isFile(sourceReplacementsPath))) { - throw new FileRequiredError(sourceReplacementsPath) - } - - await mkdir(packageRootPath, { recursive: true }) - - const temporaryPath = await mkdtemp(join(packageRootPath, temporaryDirectoryPrefix)) - const generatedIconsPath = join(temporaryPath, generatedIconsDirectoryName) - const generatedReplacementsPath = join(temporaryPath, generatedReplacementsDirectoryName) - - try { - await mkdir(generatedIconsPath) - await mkdir(generatedReplacementsPath) - - await generateReplacements(sourceSvgPath, generatedReplacementsPath) - await generateIcons(sourceSvgPath, generatedIconsPath) - await formatFiles([ - join(generatedReplacementsPath, replacementsFileName), - ...(await collectFiles(generatedIconsPath)).map((filePath) => - join(generatedIconsPath, filePath)), - ]) - - const outdatedFiles = [ - ...(await compareDirectories({ - actualPath: sourceIconsPath, - expectedPath: generatedIconsPath, - })), - ...(await compareFiles( - join(generatedReplacementsPath, replacementsFileName), - sourceReplacementsPath - )), - ] - - if (outdatedFiles.length > 0) { - throw new GeneratedFilesOutdatedError(outdatedFiles) - } - - process.stdout.write(`${checkSuccessMessage}${outputLineBreak}`) - } finally { - await rm(temporaryPath, { force: true, recursive: true }) - } -} +import { outputLineBreak } from '../lib/check-generated/constants.js' +import { checkGenerated } from '../lib/check-generated/index.js' try { await checkGenerated() From 39742b5238bb965dfc9c7b0ad457a5e63305066b Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Tue, 23 Jun 2026 05:25:09 +0300 Subject: [PATCH 06/19] refactor(icons): move generated check under owner --- .../run-command.ts => generated/check/command/run.ts} | 0 .../{check-generated => generated/check}/constants.ts | 2 +- .../check/drift/directory.ts} | 0 .../compare-files.ts => generated/check/drift/file.ts} | 0 .../check}/errors/command-failed.error.ts | 0 .../check}/errors/directory-required.error.ts | 0 .../check}/errors/file-required.error.ts | 0 .../check}/errors/generated-files-outdated.error.ts | 0 .../check/expected/format.ts} | 2 +- .../check/expected/icons.ts} | 2 +- .../check/expected/replacements.ts} | 2 +- .../check}/filesystem/collect-files.ts | 0 .../check}/filesystem/is-directory.ts | 0 .../check}/filesystem/is-file.ts | 0 .../check}/filesystem/is-outdated-file.ts | 0 .../lib/{check-generated => generated/check}/index.ts | 10 +++++----- .../{check-generated => generated/check}/interfaces.ts | 0 ui-admin/icons/scripts/check-generated.ts | 4 ++-- 18 files changed, 11 insertions(+), 11 deletions(-) rename ui-admin/icons/lib/{check-generated/process/run-command.ts => generated/check/command/run.ts} (100%) rename ui-admin/icons/lib/{check-generated => generated/check}/constants.ts (96%) rename ui-admin/icons/lib/{check-generated/comparison/compare-directories.ts => generated/check/drift/directory.ts} (100%) rename ui-admin/icons/lib/{check-generated/comparison/compare-files.ts => generated/check/drift/file.ts} (100%) rename ui-admin/icons/lib/{check-generated => generated/check}/errors/command-failed.error.ts (100%) rename ui-admin/icons/lib/{check-generated => generated/check}/errors/directory-required.error.ts (100%) rename ui-admin/icons/lib/{check-generated => generated/check}/errors/file-required.error.ts (100%) rename ui-admin/icons/lib/{check-generated => generated/check}/errors/generated-files-outdated.error.ts (100%) rename ui-admin/icons/lib/{check-generated/generation/format-files.ts => generated/check/expected/format.ts} (88%) rename ui-admin/icons/lib/{check-generated/generation/generate-icons.ts => generated/check/expected/icons.ts} (92%) rename ui-admin/icons/lib/{check-generated/generation/generate-replacements.ts => generated/check/expected/replacements.ts} (91%) rename ui-admin/icons/lib/{check-generated => generated/check}/filesystem/collect-files.ts (100%) rename ui-admin/icons/lib/{check-generated => generated/check}/filesystem/is-directory.ts (100%) rename ui-admin/icons/lib/{check-generated => generated/check}/filesystem/is-file.ts (100%) rename ui-admin/icons/lib/{check-generated => generated/check}/filesystem/is-outdated-file.ts (100%) rename ui-admin/icons/lib/{check-generated => generated/check}/index.ts (87%) rename ui-admin/icons/lib/{check-generated => generated/check}/interfaces.ts (100%) diff --git a/ui-admin/icons/lib/check-generated/process/run-command.ts b/ui-admin/icons/lib/generated/check/command/run.ts similarity index 100% rename from ui-admin/icons/lib/check-generated/process/run-command.ts rename to ui-admin/icons/lib/generated/check/command/run.ts diff --git a/ui-admin/icons/lib/check-generated/constants.ts b/ui-admin/icons/lib/generated/check/constants.ts similarity index 96% rename from ui-admin/icons/lib/check-generated/constants.ts rename to ui-admin/icons/lib/generated/check/constants.ts index c1c0f37a9..c8f0d28df 100644 --- a/ui-admin/icons/lib/check-generated/constants.ts +++ b/ui-admin/icons/lib/generated/check/constants.ts @@ -10,7 +10,7 @@ export const outdatedFileReasons = { unexpected: 'unexpected', } as const -export const packageRootPath = resolve(scriptDirectory, '../..') +export const packageRootPath = resolve(scriptDirectory, '../../..') export const workspaceRootPath = resolve(packageRootPath, '../..') diff --git a/ui-admin/icons/lib/check-generated/comparison/compare-directories.ts b/ui-admin/icons/lib/generated/check/drift/directory.ts similarity index 100% rename from ui-admin/icons/lib/check-generated/comparison/compare-directories.ts rename to ui-admin/icons/lib/generated/check/drift/directory.ts diff --git a/ui-admin/icons/lib/check-generated/comparison/compare-files.ts b/ui-admin/icons/lib/generated/check/drift/file.ts similarity index 100% rename from ui-admin/icons/lib/check-generated/comparison/compare-files.ts rename to ui-admin/icons/lib/generated/check/drift/file.ts diff --git a/ui-admin/icons/lib/check-generated/errors/command-failed.error.ts b/ui-admin/icons/lib/generated/check/errors/command-failed.error.ts similarity index 100% rename from ui-admin/icons/lib/check-generated/errors/command-failed.error.ts rename to ui-admin/icons/lib/generated/check/errors/command-failed.error.ts diff --git a/ui-admin/icons/lib/check-generated/errors/directory-required.error.ts b/ui-admin/icons/lib/generated/check/errors/directory-required.error.ts similarity index 100% rename from ui-admin/icons/lib/check-generated/errors/directory-required.error.ts rename to ui-admin/icons/lib/generated/check/errors/directory-required.error.ts diff --git a/ui-admin/icons/lib/check-generated/errors/file-required.error.ts b/ui-admin/icons/lib/generated/check/errors/file-required.error.ts similarity index 100% rename from ui-admin/icons/lib/check-generated/errors/file-required.error.ts rename to ui-admin/icons/lib/generated/check/errors/file-required.error.ts diff --git a/ui-admin/icons/lib/check-generated/errors/generated-files-outdated.error.ts b/ui-admin/icons/lib/generated/check/errors/generated-files-outdated.error.ts similarity index 100% rename from ui-admin/icons/lib/check-generated/errors/generated-files-outdated.error.ts rename to ui-admin/icons/lib/generated/check/errors/generated-files-outdated.error.ts diff --git a/ui-admin/icons/lib/check-generated/generation/format-files.ts b/ui-admin/icons/lib/generated/check/expected/format.ts similarity index 88% rename from ui-admin/icons/lib/check-generated/generation/format-files.ts rename to ui-admin/icons/lib/generated/check/expected/format.ts index 1de4e3d37..2bed8e555 100644 --- a/ui-admin/icons/lib/check-generated/generation/format-files.ts +++ b/ui-admin/icons/lib/generated/check/expected/format.ts @@ -1,9 +1,9 @@ import { relative } from 'node:path' +import { runCommand } from '../command/run.js' import { formatCommand } from '../constants.js' import { workspaceRootPath } from '../constants.js' import { yarnCommand } from '../constants.js' -import { runCommand } from '../process/run-command.js' export const formatFiles = async (filePaths: Array): Promise => { const relativeFilePaths = filePaths.map((filePath) => relative(workspaceRootPath, filePath)) diff --git a/ui-admin/icons/lib/check-generated/generation/generate-icons.ts b/ui-admin/icons/lib/generated/check/expected/icons.ts similarity index 92% rename from ui-admin/icons/lib/check-generated/generation/generate-icons.ts rename to ui-admin/icons/lib/generated/check/expected/icons.ts index b8a4d2f31..a93921d83 100644 --- a/ui-admin/icons/lib/check-generated/generation/generate-icons.ts +++ b/ui-admin/icons/lib/generated/check/expected/icons.ts @@ -1,3 +1,4 @@ +import { runCommand } from '../command/run.js' import { generateIconsCommand } from '../constants.js' import { iconsGeneratorWorkspace } from '../constants.js' import { iconsPathArgument } from '../constants.js' @@ -6,7 +7,6 @@ import { sourceReplacementsPath } from '../constants.js' import { workspaceRootPath } from '../constants.js' import { workspaceArgument } from '../constants.js' import { yarnCommand } from '../constants.js' -import { runCommand } from '../process/run-command.js' export const generateIcons = async (svgPath: string, outputPath: string): Promise => { await runCommand( diff --git a/ui-admin/icons/lib/check-generated/generation/generate-replacements.ts b/ui-admin/icons/lib/generated/check/expected/replacements.ts similarity index 91% rename from ui-admin/icons/lib/check-generated/generation/generate-replacements.ts rename to ui-admin/icons/lib/generated/check/expected/replacements.ts index d42e44cfa..d7a3a8c40 100644 --- a/ui-admin/icons/lib/check-generated/generation/generate-replacements.ts +++ b/ui-admin/icons/lib/generated/check/expected/replacements.ts @@ -1,3 +1,4 @@ +import { runCommand } from '../command/run.js' import { generateIconsCommand } from '../constants.js' import { generateReplacementsCommand } from '../constants.js' import { iconsGeneratorWorkspace } from '../constants.js' @@ -5,7 +6,6 @@ import { iconsPathArgument } from '../constants.js' import { workspaceRootPath } from '../constants.js' import { workspaceArgument } from '../constants.js' import { yarnCommand } from '../constants.js' -import { runCommand } from '../process/run-command.js' export const generateReplacements = async (svgPath: string, outputPath: string): Promise => { await runCommand( diff --git a/ui-admin/icons/lib/check-generated/filesystem/collect-files.ts b/ui-admin/icons/lib/generated/check/filesystem/collect-files.ts similarity index 100% rename from ui-admin/icons/lib/check-generated/filesystem/collect-files.ts rename to ui-admin/icons/lib/generated/check/filesystem/collect-files.ts diff --git a/ui-admin/icons/lib/check-generated/filesystem/is-directory.ts b/ui-admin/icons/lib/generated/check/filesystem/is-directory.ts similarity index 100% rename from ui-admin/icons/lib/check-generated/filesystem/is-directory.ts rename to ui-admin/icons/lib/generated/check/filesystem/is-directory.ts diff --git a/ui-admin/icons/lib/check-generated/filesystem/is-file.ts b/ui-admin/icons/lib/generated/check/filesystem/is-file.ts similarity index 100% rename from ui-admin/icons/lib/check-generated/filesystem/is-file.ts rename to ui-admin/icons/lib/generated/check/filesystem/is-file.ts diff --git a/ui-admin/icons/lib/check-generated/filesystem/is-outdated-file.ts b/ui-admin/icons/lib/generated/check/filesystem/is-outdated-file.ts similarity index 100% rename from ui-admin/icons/lib/check-generated/filesystem/is-outdated-file.ts rename to ui-admin/icons/lib/generated/check/filesystem/is-outdated-file.ts diff --git a/ui-admin/icons/lib/check-generated/index.ts b/ui-admin/icons/lib/generated/check/index.ts similarity index 87% rename from ui-admin/icons/lib/check-generated/index.ts rename to ui-admin/icons/lib/generated/check/index.ts index df8b436a7..e8f9163a2 100644 --- a/ui-admin/icons/lib/check-generated/index.ts +++ b/ui-admin/icons/lib/generated/check/index.ts @@ -6,8 +6,6 @@ import { join } from 'node:path' import { DirectoryRequiredError } from './errors/directory-required.error.js' import { FileRequiredError } from './errors/file-required.error.js' import { GeneratedFilesOutdatedError } from './errors/generated-files-outdated.error.js' -import { compareDirectories } from './comparison/compare-directories.js' -import { compareFiles } from './comparison/compare-files.js' import { checkSuccessMessage } from './constants.js' import { generatedIconsDirectoryName } from './constants.js' import { generatedReplacementsDirectoryName } from './constants.js' @@ -18,12 +16,14 @@ import { sourceIconsPath } from './constants.js' import { sourceReplacementsPath } from './constants.js' import { sourceSvgPath } from './constants.js' import { temporaryDirectoryPrefix } from './constants.js' +import { compareDirectories } from './drift/directory.js' +import { compareFiles } from './drift/file.js' +import { formatFiles } from './expected/format.js' +import { generateIcons } from './expected/icons.js' +import { generateReplacements } from './expected/replacements.js' import { collectFiles } from './filesystem/collect-files.js' import { isDirectory } from './filesystem/is-directory.js' import { isFile } from './filesystem/is-file.js' -import { formatFiles } from './generation/format-files.js' -import { generateIcons } from './generation/generate-icons.js' -import { generateReplacements } from './generation/generate-replacements.js' export const checkGenerated = async (): Promise => { if (!(await isDirectory(sourceIconsPath))) { diff --git a/ui-admin/icons/lib/check-generated/interfaces.ts b/ui-admin/icons/lib/generated/check/interfaces.ts similarity index 100% rename from ui-admin/icons/lib/check-generated/interfaces.ts rename to ui-admin/icons/lib/generated/check/interfaces.ts diff --git a/ui-admin/icons/scripts/check-generated.ts b/ui-admin/icons/scripts/check-generated.ts index d3a607f87..6b323283f 100644 --- a/ui-admin/icons/scripts/check-generated.ts +++ b/ui-admin/icons/scripts/check-generated.ts @@ -1,5 +1,5 @@ -import { outputLineBreak } from '../lib/check-generated/constants.js' -import { checkGenerated } from '../lib/check-generated/index.js' +import { outputLineBreak } from '../lib/generated/check/constants.js' +import { checkGenerated } from '../lib/generated/check/index.js' try { await checkGenerated() From 4770189323a33426c60482f8d4bcd0db0b4f5da9 Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Tue, 23 Jun 2026 16:33:19 +0300 Subject: [PATCH 07/19] refactor(icons): model generated check input constraints --- .../lib/generated/check/drift/directory.ts | 27 +++++++++- .../icons/lib/generated/check/drift/file.ts | 15 +++++- .../lib/generated/check/expected/format.ts | 41 +++++++++++--- .../check/filesystem/collect-files.ts | 39 -------------- .../check/filesystem/is-directory.ts | 9 ---- .../lib/generated/check/filesystem/is-file.ts | 9 ---- .../check/filesystem/is-outdated-file.ts | 4 -- ui-admin/icons/lib/generated/check/index.ts | 28 +++------- .../check/input/constraints/directory.ts | 10 ++++ .../generated/check/input/constraints/file.ts | 10 ++++ .../check/input/file-system-entry.ts | 54 +++++++++++++++++++ 11 files changed, 154 insertions(+), 92 deletions(-) delete mode 100644 ui-admin/icons/lib/generated/check/filesystem/collect-files.ts delete mode 100644 ui-admin/icons/lib/generated/check/filesystem/is-directory.ts delete mode 100644 ui-admin/icons/lib/generated/check/filesystem/is-file.ts delete mode 100644 ui-admin/icons/lib/generated/check/filesystem/is-outdated-file.ts create mode 100644 ui-admin/icons/lib/generated/check/input/constraints/directory.ts create mode 100644 ui-admin/icons/lib/generated/check/input/constraints/file.ts create mode 100644 ui-admin/icons/lib/generated/check/input/file-system-entry.ts diff --git a/ui-admin/icons/lib/generated/check/drift/directory.ts b/ui-admin/icons/lib/generated/check/drift/directory.ts index 254581776..46a47c616 100644 --- a/ui-admin/icons/lib/generated/check/drift/directory.ts +++ b/ui-admin/icons/lib/generated/check/drift/directory.ts @@ -1,11 +1,34 @@ import type { DirectoryComparisonOptions } from '../interfaces.js' import type { OutdatedFile } from '../interfaces.js' +import { readdir } from 'node:fs/promises' import { join } from 'node:path' +import { sep } from 'node:path' import { outdatedFileReasons } from '../constants.js' -import { collectFiles } from '../filesystem/collect-files.js' -import { isOutdatedFile } from '../filesystem/is-outdated-file.js' +import { relativePathSeparator } from '../constants.js' +import { isOutdatedFile } from './file.js' + +const collectFiles = async (directoryPath: string, currentPath = ''): Promise> => { + const entries = await readdir(join(directoryPath, currentPath), { withFileTypes: true }) + const files = await Promise.all( + entries.map(async (entry) => { + const entryPath = currentPath ? join(currentPath, entry.name) : entry.name + + if (entry.isDirectory()) { + return collectFiles(directoryPath, entryPath) + } + + if (entry.isFile()) { + return [entryPath.split(sep).join(relativePathSeparator)] + } + + return [] + }) + ) + + return files.flat().sort((pathA, pathB) => pathA.localeCompare(pathB)) +} export const compareDirectories = async ({ actualPath, diff --git a/ui-admin/icons/lib/generated/check/drift/file.ts b/ui-admin/icons/lib/generated/check/drift/file.ts index cd3bbc7d6..51f3f5d2a 100644 --- a/ui-admin/icons/lib/generated/check/drift/file.ts +++ b/ui-admin/icons/lib/generated/check/drift/file.ts @@ -1,10 +1,21 @@ import type { OutdatedFile } from '../interfaces.js' +import { readFile } from 'node:fs/promises' +import { stat } from 'node:fs/promises' import { basename } from 'node:path' import { outdatedFileReasons } from '../constants.js' -import { isFile } from '../filesystem/is-file.js' -import { isOutdatedFile } from '../filesystem/is-outdated-file.js' + +const isFile = async (targetPath: string): Promise => { + try { + return (await stat(targetPath)).isFile() + } catch { + return false + } +} + +export const isOutdatedFile = async (expectedPath: string, actualPath: string): Promise => + !(await readFile(expectedPath)).equals(await readFile(actualPath)) export const compareFiles = async ( expectedPath: string, diff --git a/ui-admin/icons/lib/generated/check/expected/format.ts b/ui-admin/icons/lib/generated/check/expected/format.ts index 2bed8e555..4f55d65c9 100644 --- a/ui-admin/icons/lib/generated/check/expected/format.ts +++ b/ui-admin/icons/lib/generated/check/expected/format.ts @@ -1,11 +1,40 @@ -import { relative } from 'node:path' +import { readdir } from 'node:fs/promises' +import { join } from 'node:path' +import { relative } from 'node:path' +import { sep } from 'node:path' -import { runCommand } from '../command/run.js' -import { formatCommand } from '../constants.js' -import { workspaceRootPath } from '../constants.js' -import { yarnCommand } from '../constants.js' +import { runCommand } from '../command/run.js' +import { formatCommand } from '../constants.js' +import { relativePathSeparator } from '../constants.js' +import { workspaceRootPath } from '../constants.js' +import { yarnCommand } from '../constants.js' -export const formatFiles = async (filePaths: Array): Promise => { +const collectFiles = async (directoryPath: string, currentPath = ''): Promise> => { + const entries = await readdir(join(directoryPath, currentPath), { withFileTypes: true }) + const files = await Promise.all( + entries.map(async (entry) => { + const entryPath = currentPath ? join(currentPath, entry.name) : entry.name + + if (entry.isDirectory()) { + return collectFiles(directoryPath, entryPath) + } + + if (entry.isFile()) { + return [entryPath.split(sep).join(relativePathSeparator)] + } + + return [] + }) + ) + + return files.flat().sort((pathA, pathB) => pathA.localeCompare(pathB)) +} + +export const formatFiles = async (iconsPath: string, replacementsPath: string): Promise => { + const filePaths = [ + replacementsPath, + ...(await collectFiles(iconsPath)).map((filePath) => join(iconsPath, filePath)), + ] const relativeFilePaths = filePaths.map((filePath) => relative(workspaceRootPath, filePath)) await runCommand(yarnCommand, [formatCommand, ...relativeFilePaths], { cwd: workspaceRootPath }) diff --git a/ui-admin/icons/lib/generated/check/filesystem/collect-files.ts b/ui-admin/icons/lib/generated/check/filesystem/collect-files.ts deleted file mode 100644 index 209b4c0f1..000000000 --- a/ui-admin/icons/lib/generated/check/filesystem/collect-files.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { readdir } from 'node:fs/promises' -import { join } from 'node:path' -import { relative } from 'node:path' -import { sep } from 'node:path' - -import { relativePathSeparator } from '../constants.js' - -const toRelativePath = (rootPath: string, filePath: string): string => - relative(rootPath, filePath).split(sep).join(relativePathSeparator) - -const collectDirectoryFiles = async ( - rootPath: string, - directoryPath: string -): Promise> => { - const entries = (await readdir(directoryPath, { withFileTypes: true })).sort((entryA, entryB) => - entryA.name.localeCompare(entryB.name)) - - const files = await Promise.all( - entries.map(async (entry) => { - const entryPath = join(directoryPath, entry.name) - - if (entry.isDirectory()) { - return collectDirectoryFiles(rootPath, entryPath) - } - - if (entry.isFile()) { - return [toRelativePath(rootPath, entryPath)] - } - - return [] - }) - ) - - return files.flat() -} - -export const collectFiles = async (rootPath: string): Promise> => - (await collectDirectoryFiles(rootPath, rootPath)).sort((fileA, fileB) => - fileA.localeCompare(fileB)) diff --git a/ui-admin/icons/lib/generated/check/filesystem/is-directory.ts b/ui-admin/icons/lib/generated/check/filesystem/is-directory.ts deleted file mode 100644 index 2a66dc27e..000000000 --- a/ui-admin/icons/lib/generated/check/filesystem/is-directory.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { stat } from 'node:fs/promises' - -export const isDirectory = async (targetPath: string): Promise => { - try { - return (await stat(targetPath)).isDirectory() - } catch { - return false - } -} diff --git a/ui-admin/icons/lib/generated/check/filesystem/is-file.ts b/ui-admin/icons/lib/generated/check/filesystem/is-file.ts deleted file mode 100644 index 8076d1ef0..000000000 --- a/ui-admin/icons/lib/generated/check/filesystem/is-file.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { stat } from 'node:fs/promises' - -export const isFile = async (targetPath: string): Promise => { - try { - return (await stat(targetPath)).isFile() - } catch { - return false - } -} diff --git a/ui-admin/icons/lib/generated/check/filesystem/is-outdated-file.ts b/ui-admin/icons/lib/generated/check/filesystem/is-outdated-file.ts deleted file mode 100644 index 01d50a195..000000000 --- a/ui-admin/icons/lib/generated/check/filesystem/is-outdated-file.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { readFile } from 'node:fs/promises' - -export const isOutdatedFile = async (expectedPath: string, actualPath: string): Promise => - !(await readFile(expectedPath)).equals(await readFile(actualPath)) diff --git a/ui-admin/icons/lib/generated/check/index.ts b/ui-admin/icons/lib/generated/check/index.ts index e8f9163a2..7e78f5e11 100644 --- a/ui-admin/icons/lib/generated/check/index.ts +++ b/ui-admin/icons/lib/generated/check/index.ts @@ -3,8 +3,6 @@ import { mkdtemp } from 'node:fs/promises' import { rm } from 'node:fs/promises' import { join } from 'node:path' -import { DirectoryRequiredError } from './errors/directory-required.error.js' -import { FileRequiredError } from './errors/file-required.error.js' import { GeneratedFilesOutdatedError } from './errors/generated-files-outdated.error.js' import { checkSuccessMessage } from './constants.js' import { generatedIconsDirectoryName } from './constants.js' @@ -21,22 +19,14 @@ import { compareFiles } from './drift/file.js' import { formatFiles } from './expected/format.js' import { generateIcons } from './expected/icons.js' import { generateReplacements } from './expected/replacements.js' -import { collectFiles } from './filesystem/collect-files.js' -import { isDirectory } from './filesystem/is-directory.js' -import { isFile } from './filesystem/is-file.js' +import { requireDirectory } from './input/constraints/directory.js' +import { requireFile } from './input/constraints/file.js' +import { readFileSystemEntry } from './input/file-system-entry.js' export const checkGenerated = async (): Promise => { - if (!(await isDirectory(sourceIconsPath))) { - throw new DirectoryRequiredError(sourceIconsPath) - } - - if (!(await isDirectory(sourceSvgPath))) { - throw new DirectoryRequiredError(sourceSvgPath) - } - - if (!(await isFile(sourceReplacementsPath))) { - throw new FileRequiredError(sourceReplacementsPath) - } + requireDirectory(await readFileSystemEntry(sourceIconsPath)) + requireDirectory(await readFileSystemEntry(sourceSvgPath)) + requireFile(await readFileSystemEntry(sourceReplacementsPath)) await mkdir(packageRootPath, { recursive: true }) @@ -50,11 +40,7 @@ export const checkGenerated = async (): Promise => { await generateReplacements(sourceSvgPath, generatedReplacementsPath) await generateIcons(sourceSvgPath, generatedIconsPath) - await formatFiles([ - join(generatedReplacementsPath, replacementsFileName), - ...(await collectFiles(generatedIconsPath)).map((filePath) => - join(generatedIconsPath, filePath)), - ]) + await formatFiles(generatedIconsPath, join(generatedReplacementsPath, replacementsFileName)) const outdatedFiles = [ ...(await compareDirectories({ diff --git a/ui-admin/icons/lib/generated/check/input/constraints/directory.ts b/ui-admin/icons/lib/generated/check/input/constraints/directory.ts new file mode 100644 index 000000000..310fb15e5 --- /dev/null +++ b/ui-admin/icons/lib/generated/check/input/constraints/directory.ts @@ -0,0 +1,10 @@ +import type { FileSystemEntry } from '../file-system-entry.js' + +import { DirectoryRequiredError } from '../../errors/directory-required.error.js' +import { fileSystemEntryKinds } from '../file-system-entry.js' + +export const requireDirectory = (entry: FileSystemEntry): void => { + if (entry.kind !== fileSystemEntryKinds.directory) { + throw new DirectoryRequiredError(entry.path) + } +} diff --git a/ui-admin/icons/lib/generated/check/input/constraints/file.ts b/ui-admin/icons/lib/generated/check/input/constraints/file.ts new file mode 100644 index 000000000..a6387399d --- /dev/null +++ b/ui-admin/icons/lib/generated/check/input/constraints/file.ts @@ -0,0 +1,10 @@ +import type { FileSystemEntry } from '../file-system-entry.js' + +import { FileRequiredError } from '../../errors/file-required.error.js' +import { fileSystemEntryKinds } from '../file-system-entry.js' + +export const requireFile = (entry: FileSystemEntry): void => { + if (entry.kind !== fileSystemEntryKinds.file) { + throw new FileRequiredError(entry.path) + } +} diff --git a/ui-admin/icons/lib/generated/check/input/file-system-entry.ts b/ui-admin/icons/lib/generated/check/input/file-system-entry.ts new file mode 100644 index 000000000..59a8c2948 --- /dev/null +++ b/ui-admin/icons/lib/generated/check/input/file-system-entry.ts @@ -0,0 +1,54 @@ +import { stat } from 'node:fs/promises' + +export const fileSystemEntryKinds = { + directory: 'directory', + file: 'file', + missing: 'missing', + other: 'other', + unavailable: 'unavailable', +} as const + +export type FileSystemEntryKind = (typeof fileSystemEntryKinds)[keyof typeof fileSystemEntryKinds] + +export interface FileSystemEntry { + error?: unknown + kind: FileSystemEntryKind + path: string +} + +const isMissingError = (error: unknown): boolean => + typeof error === 'object' && + error !== null && + 'code' in error && + (error as { code?: unknown }).code === 'ENOENT' + +export const readFileSystemEntry = async (targetPath: string): Promise => { + try { + const stats = await stat(targetPath) + + if (stats.isDirectory()) { + return { + kind: fileSystemEntryKinds.directory, + path: targetPath, + } + } + + if (stats.isFile()) { + return { + kind: fileSystemEntryKinds.file, + path: targetPath, + } + } + + return { + kind: fileSystemEntryKinds.other, + path: targetPath, + } + } catch (error) { + return { + error, + kind: isMissingError(error) ? fileSystemEntryKinds.missing : fileSystemEntryKinds.unavailable, + path: targetPath, + } + } +} From 27d257c37273b73fc104a2b65096d61f53a4acd2 Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Tue, 23 Jun 2026 16:34:55 +0300 Subject: [PATCH 08/19] refactor(icons): colocate generated check role types --- .../lib/generated/check/command/options.ts | 3 +++ .../icons/lib/generated/check/command/run.ts | 2 +- .../icons/lib/generated/check/constants.ts | 14 +++++-------- .../lib/generated/check/drift/directory.ts | 20 +++++++++++-------- .../icons/lib/generated/check/drift/file.ts | 4 ++-- .../{interfaces.ts => drift/outdated-file.ts} | 15 +++++--------- .../errors/generated-files-outdated.error.ts | 2 +- 7 files changed, 29 insertions(+), 31 deletions(-) create mode 100644 ui-admin/icons/lib/generated/check/command/options.ts rename ui-admin/icons/lib/generated/check/{interfaces.ts => drift/outdated-file.ts} (53%) diff --git a/ui-admin/icons/lib/generated/check/command/options.ts b/ui-admin/icons/lib/generated/check/command/options.ts new file mode 100644 index 000000000..1fa977d90 --- /dev/null +++ b/ui-admin/icons/lib/generated/check/command/options.ts @@ -0,0 +1,3 @@ +export interface CommandOptions { + cwd?: string +} diff --git a/ui-admin/icons/lib/generated/check/command/run.ts b/ui-admin/icons/lib/generated/check/command/run.ts index 8808bfa81..14ddd4958 100644 --- a/ui-admin/icons/lib/generated/check/command/run.ts +++ b/ui-admin/icons/lib/generated/check/command/run.ts @@ -1,4 +1,4 @@ -import type { CommandOptions } from '../interfaces.js' +import type { CommandOptions } from './options.js' import { spawn } from 'node:child_process' diff --git a/ui-admin/icons/lib/generated/check/constants.ts b/ui-admin/icons/lib/generated/check/constants.ts index c8f0d28df..62e196215 100644 --- a/ui-admin/icons/lib/generated/check/constants.ts +++ b/ui-admin/icons/lib/generated/check/constants.ts @@ -1,14 +1,10 @@ -import { dirname } from 'node:path' -import { resolve } from 'node:path' -import { fileURLToPath } from 'node:url' +import { dirname } from 'node:path' +import { resolve } from 'node:path' +import { fileURLToPath } from 'node:url' -const scriptDirectory = dirname(fileURLToPath(import.meta.url)) +import { outdatedFileReasons } from './drift/outdated-file.js' -export const outdatedFileReasons = { - different: 'different', - missing: 'missing', - unexpected: 'unexpected', -} as const +const scriptDirectory = dirname(fileURLToPath(import.meta.url)) export const packageRootPath = resolve(scriptDirectory, '../../..') diff --git a/ui-admin/icons/lib/generated/check/drift/directory.ts b/ui-admin/icons/lib/generated/check/drift/directory.ts index 46a47c616..7351810ea 100644 --- a/ui-admin/icons/lib/generated/check/drift/directory.ts +++ b/ui-admin/icons/lib/generated/check/drift/directory.ts @@ -1,13 +1,17 @@ -import type { DirectoryComparisonOptions } from '../interfaces.js' -import type { OutdatedFile } from '../interfaces.js' +import type { OutdatedFile } from './outdated-file.js' -import { readdir } from 'node:fs/promises' -import { join } from 'node:path' -import { sep } from 'node:path' +import { readdir } from 'node:fs/promises' +import { join } from 'node:path' +import { sep } from 'node:path' -import { outdatedFileReasons } from '../constants.js' -import { relativePathSeparator } from '../constants.js' -import { isOutdatedFile } from './file.js' +import { relativePathSeparator } from '../constants.js' +import { isOutdatedFile } from './file.js' +import { outdatedFileReasons } from './outdated-file.js' + +interface DirectoryComparisonOptions { + actualPath: string + expectedPath: string +} const collectFiles = async (directoryPath: string, currentPath = ''): Promise> => { const entries = await readdir(join(directoryPath, currentPath), { withFileTypes: true }) diff --git a/ui-admin/icons/lib/generated/check/drift/file.ts b/ui-admin/icons/lib/generated/check/drift/file.ts index 51f3f5d2a..40054dc09 100644 --- a/ui-admin/icons/lib/generated/check/drift/file.ts +++ b/ui-admin/icons/lib/generated/check/drift/file.ts @@ -1,10 +1,10 @@ -import type { OutdatedFile } from '../interfaces.js' +import type { OutdatedFile } from './outdated-file.js' import { readFile } from 'node:fs/promises' import { stat } from 'node:fs/promises' import { basename } from 'node:path' -import { outdatedFileReasons } from '../constants.js' +import { outdatedFileReasons } from './outdated-file.js' const isFile = async (targetPath: string): Promise => { try { diff --git a/ui-admin/icons/lib/generated/check/interfaces.ts b/ui-admin/icons/lib/generated/check/drift/outdated-file.ts similarity index 53% rename from ui-admin/icons/lib/generated/check/interfaces.ts rename to ui-admin/icons/lib/generated/check/drift/outdated-file.ts index f4e9c9b60..2911d0042 100644 --- a/ui-admin/icons/lib/generated/check/interfaces.ts +++ b/ui-admin/icons/lib/generated/check/drift/outdated-file.ts @@ -1,4 +1,8 @@ -import type { outdatedFileReasons } from './constants.js' +export const outdatedFileReasons = { + different: 'different', + missing: 'missing', + unexpected: 'unexpected', +} as const export type OutdatedFileReason = (typeof outdatedFileReasons)[keyof typeof outdatedFileReasons] @@ -8,12 +12,3 @@ export interface OutdatedFile { reason: OutdatedFileReason relativePath: string } - -export interface DirectoryComparisonOptions { - actualPath: string - expectedPath: string -} - -export interface CommandOptions { - cwd?: string -} diff --git a/ui-admin/icons/lib/generated/check/errors/generated-files-outdated.error.ts b/ui-admin/icons/lib/generated/check/errors/generated-files-outdated.error.ts index c6cbcd70e..bfbf10c9d 100644 --- a/ui-admin/icons/lib/generated/check/errors/generated-files-outdated.error.ts +++ b/ui-admin/icons/lib/generated/check/errors/generated-files-outdated.error.ts @@ -1,4 +1,4 @@ -import type { OutdatedFile } from '../interfaces.js' +import type { OutdatedFile } from '../drift/outdated-file.js' import { outdatedFileReasonMessages } from '../constants.js' import { outdatedFilesMessage } from '../constants.js' From 1f059fe3af5863a05c6298665f08cc6ce2e7cf5d Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Tue, 23 Jun 2026 16:36:03 +0300 Subject: [PATCH 09/19] refactor(icons): keep generated check output in script --- ui-admin/icons/lib/generated/check/index.ts | 4 ---- ui-admin/icons/scripts/check-generated.ts | 6 ++++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ui-admin/icons/lib/generated/check/index.ts b/ui-admin/icons/lib/generated/check/index.ts index 7e78f5e11..d6c9f116f 100644 --- a/ui-admin/icons/lib/generated/check/index.ts +++ b/ui-admin/icons/lib/generated/check/index.ts @@ -4,10 +4,8 @@ import { rm } from 'node:fs/promises' import { join } from 'node:path' import { GeneratedFilesOutdatedError } from './errors/generated-files-outdated.error.js' -import { checkSuccessMessage } from './constants.js' import { generatedIconsDirectoryName } from './constants.js' import { generatedReplacementsDirectoryName } from './constants.js' -import { outputLineBreak } from './constants.js' import { packageRootPath } from './constants.js' import { replacementsFileName } from './constants.js' import { sourceIconsPath } from './constants.js' @@ -56,8 +54,6 @@ export const checkGenerated = async (): Promise => { if (outdatedFiles.length > 0) { throw new GeneratedFilesOutdatedError(outdatedFiles) } - - process.stdout.write(`${checkSuccessMessage}${outputLineBreak}`) } finally { await rm(temporaryPath, { force: true, recursive: true }) } diff --git a/ui-admin/icons/scripts/check-generated.ts b/ui-admin/icons/scripts/check-generated.ts index 6b323283f..b858cbdd7 100644 --- a/ui-admin/icons/scripts/check-generated.ts +++ b/ui-admin/icons/scripts/check-generated.ts @@ -1,8 +1,10 @@ -import { outputLineBreak } from '../lib/generated/check/constants.js' -import { checkGenerated } from '../lib/generated/check/index.js' +import { checkSuccessMessage } from '../lib/generated/check/constants.js' +import { outputLineBreak } from '../lib/generated/check/constants.js' +import { checkGenerated } from '../lib/generated/check/index.js' try { await checkGenerated() + process.stdout.write(`${checkSuccessMessage}${outputLineBreak}`) } catch (error) { const errorMessage = error instanceof Error ? (error.stack ?? error.message) : String(error) From 712a364f930a602337f9fc076914215324609b41 Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Tue, 23 Jun 2026 16:38:02 +0300 Subject: [PATCH 10/19] refactor(icons): add generated check error contract --- .../icons/lib/generated/check/command/run.ts | 2 +- .../icons/lib/generated/check/constants.ts | 22 ++------------ .../icons/lib/generated/check/errors/codes.ts | 8 +++++ .../check/errors/command-failed.error.ts | 7 ----- .../generated/check/errors/command-failed.ts | 23 +++++++++++++++ .../check/errors/directory-required.error.ts | 7 ----- .../check/errors/directory-required.ts | 17 +++++++++++ .../check/errors/file-required.error.ts | 7 ----- .../generated/check/errors/file-required.ts | 17 +++++++++++ .../generated/check/errors/files-outdated.ts | 29 +++++++++++++++++++ .../errors/generated-files-outdated.error.ts | 13 --------- .../lib/generated/check/errors/generated.ts | 17 +++++++++++ ui-admin/icons/lib/generated/check/index.ts | 4 +-- .../check/input/constraints/directory.ts | 2 +- .../generated/check/input/constraints/file.ts | 2 +- 15 files changed, 119 insertions(+), 58 deletions(-) create mode 100644 ui-admin/icons/lib/generated/check/errors/codes.ts delete mode 100644 ui-admin/icons/lib/generated/check/errors/command-failed.error.ts create mode 100644 ui-admin/icons/lib/generated/check/errors/command-failed.ts delete mode 100644 ui-admin/icons/lib/generated/check/errors/directory-required.error.ts create mode 100644 ui-admin/icons/lib/generated/check/errors/directory-required.ts delete mode 100644 ui-admin/icons/lib/generated/check/errors/file-required.error.ts create mode 100644 ui-admin/icons/lib/generated/check/errors/file-required.ts create mode 100644 ui-admin/icons/lib/generated/check/errors/files-outdated.ts delete mode 100644 ui-admin/icons/lib/generated/check/errors/generated-files-outdated.error.ts create mode 100644 ui-admin/icons/lib/generated/check/errors/generated.ts diff --git a/ui-admin/icons/lib/generated/check/command/run.ts b/ui-admin/icons/lib/generated/check/command/run.ts index 14ddd4958..f074d847b 100644 --- a/ui-admin/icons/lib/generated/check/command/run.ts +++ b/ui-admin/icons/lib/generated/check/command/run.ts @@ -2,7 +2,7 @@ import type { CommandOptions } from './options.js' import { spawn } from 'node:child_process' -import { CommandFailedError } from '../errors/command-failed.error.js' +import { CommandFailedError } from '../errors/command-failed.js' export const runCommand = async ( command: string, diff --git a/ui-admin/icons/lib/generated/check/constants.ts b/ui-admin/icons/lib/generated/check/constants.ts index 62e196215..57e690266 100644 --- a/ui-admin/icons/lib/generated/check/constants.ts +++ b/ui-admin/icons/lib/generated/check/constants.ts @@ -1,8 +1,6 @@ -import { dirname } from 'node:path' -import { resolve } from 'node:path' -import { fileURLToPath } from 'node:url' - -import { outdatedFileReasons } from './drift/outdated-file.js' +import { dirname } from 'node:path' +import { resolve } from 'node:path' +import { fileURLToPath } from 'node:url' const scriptDirectory = dirname(fileURLToPath(import.meta.url)) @@ -45,17 +43,3 @@ export const relativePathSeparator = '/' export const outputLineBreak = '\n' export const checkSuccessMessage = 'Generated icons are up to date' - -export const commandFailedMessage = 'Command failed' - -export const directoryRequiredMessage = 'Directory is required' - -export const fileRequiredMessage = 'File is required' - -export const outdatedFilesMessage = 'Generated files are outdated' - -export const outdatedFileReasonMessages = { - [outdatedFileReasons.different]: 'content differs', - [outdatedFileReasons.missing]: 'missing committed file', - [outdatedFileReasons.unexpected]: 'unexpected committed file', -} diff --git a/ui-admin/icons/lib/generated/check/errors/codes.ts b/ui-admin/icons/lib/generated/check/errors/codes.ts new file mode 100644 index 000000000..2e6e17838 --- /dev/null +++ b/ui-admin/icons/lib/generated/check/errors/codes.ts @@ -0,0 +1,8 @@ +export const generatedErrorCodes = { + commandFailed: 'generated.command_failed', + directoryRequired: 'generated.directory_required', + fileRequired: 'generated.file_required', + filesOutdated: 'generated.files_outdated', +} as const + +export type GeneratedErrorCode = (typeof generatedErrorCodes)[keyof typeof generatedErrorCodes] diff --git a/ui-admin/icons/lib/generated/check/errors/command-failed.error.ts b/ui-admin/icons/lib/generated/check/errors/command-failed.error.ts deleted file mode 100644 index 49fbad3be..000000000 --- a/ui-admin/icons/lib/generated/check/errors/command-failed.error.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { commandFailedMessage } from '../constants.js' - -export class CommandFailedError extends Error { - constructor(command: string, args: Array, exitCode: number | null) { - super(`${commandFailedMessage}: ${command} ${args.join(' ')} (${exitCode})`) - } -} diff --git a/ui-admin/icons/lib/generated/check/errors/command-failed.ts b/ui-admin/icons/lib/generated/check/errors/command-failed.ts new file mode 100644 index 000000000..15bbd9e99 --- /dev/null +++ b/ui-admin/icons/lib/generated/check/errors/command-failed.ts @@ -0,0 +1,23 @@ +import { GeneratedError } from './generated.js' +import { generatedErrorCodes } from './codes.js' + +const commandFailedMessage = 'Command failed' + +export class CommandFailedError extends GeneratedError { + readonly args: Array + + readonly command: string + + readonly exitCode: number | null + + constructor(command: string, args: Array, exitCode: number | null) { + super({ + code: generatedErrorCodes.commandFailed, + message: `${commandFailedMessage}: ${command} ${args.join(' ')} (${exitCode})`, + }) + + this.args = args + this.command = command + this.exitCode = exitCode + } +} diff --git a/ui-admin/icons/lib/generated/check/errors/directory-required.error.ts b/ui-admin/icons/lib/generated/check/errors/directory-required.error.ts deleted file mode 100644 index c18aa0fd7..000000000 --- a/ui-admin/icons/lib/generated/check/errors/directory-required.error.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { directoryRequiredMessage } from '../constants.js' - -export class DirectoryRequiredError extends Error { - constructor(targetPath: string) { - super(`${directoryRequiredMessage}: ${targetPath}`) - } -} diff --git a/ui-admin/icons/lib/generated/check/errors/directory-required.ts b/ui-admin/icons/lib/generated/check/errors/directory-required.ts new file mode 100644 index 000000000..13acc4dc9 --- /dev/null +++ b/ui-admin/icons/lib/generated/check/errors/directory-required.ts @@ -0,0 +1,17 @@ +import { GeneratedError } from './generated.js' +import { generatedErrorCodes } from './codes.js' + +const directoryRequiredMessage = 'Directory is required' + +export class DirectoryRequiredError extends GeneratedError { + readonly path: string + + constructor(targetPath: string) { + super({ + code: generatedErrorCodes.directoryRequired, + message: `${directoryRequiredMessage}: ${targetPath}`, + }) + + this.path = targetPath + } +} diff --git a/ui-admin/icons/lib/generated/check/errors/file-required.error.ts b/ui-admin/icons/lib/generated/check/errors/file-required.error.ts deleted file mode 100644 index d45f16320..000000000 --- a/ui-admin/icons/lib/generated/check/errors/file-required.error.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { fileRequiredMessage } from '../constants.js' - -export class FileRequiredError extends Error { - constructor(targetPath: string) { - super(`${fileRequiredMessage}: ${targetPath}`) - } -} diff --git a/ui-admin/icons/lib/generated/check/errors/file-required.ts b/ui-admin/icons/lib/generated/check/errors/file-required.ts new file mode 100644 index 000000000..755ab6498 --- /dev/null +++ b/ui-admin/icons/lib/generated/check/errors/file-required.ts @@ -0,0 +1,17 @@ +import { GeneratedError } from './generated.js' +import { generatedErrorCodes } from './codes.js' + +const fileRequiredMessage = 'File is required' + +export class FileRequiredError extends GeneratedError { + readonly path: string + + constructor(targetPath: string) { + super({ + code: generatedErrorCodes.fileRequired, + message: `${fileRequiredMessage}: ${targetPath}`, + }) + + this.path = targetPath + } +} diff --git a/ui-admin/icons/lib/generated/check/errors/files-outdated.ts b/ui-admin/icons/lib/generated/check/errors/files-outdated.ts new file mode 100644 index 000000000..9f274d56d --- /dev/null +++ b/ui-admin/icons/lib/generated/check/errors/files-outdated.ts @@ -0,0 +1,29 @@ +import type { OutdatedFile } from '../drift/outdated-file.js' + +import { GeneratedError } from './generated.js' +import { outdatedFileReasons } from '../drift/outdated-file.js' +import { generatedErrorCodes } from './codes.js' + +const outdatedFilesMessage = 'Generated files are outdated' + +const outdatedFileReasonMessages = { + [outdatedFileReasons.different]: 'content differs', + [outdatedFileReasons.missing]: 'missing committed file', + [outdatedFileReasons.unexpected]: 'unexpected committed file', +} + +const formatFile = (file: OutdatedFile): string => + `${file.relativePath}: ${outdatedFileReasonMessages[file.reason]}` + +export class FilesOutdatedError extends GeneratedError { + readonly files: Array + + constructor(files: Array) { + super({ + code: generatedErrorCodes.filesOutdated, + message: [outdatedFilesMessage, ...files.map(formatFile)].join('\n'), + }) + + this.files = files + } +} diff --git a/ui-admin/icons/lib/generated/check/errors/generated-files-outdated.error.ts b/ui-admin/icons/lib/generated/check/errors/generated-files-outdated.error.ts deleted file mode 100644 index bfbf10c9d..000000000 --- a/ui-admin/icons/lib/generated/check/errors/generated-files-outdated.error.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { OutdatedFile } from '../drift/outdated-file.js' - -import { outdatedFileReasonMessages } from '../constants.js' -import { outdatedFilesMessage } from '../constants.js' - -const formatFile = (file: OutdatedFile): string => - `${file.relativePath}: ${outdatedFileReasonMessages[file.reason]}` - -export class GeneratedFilesOutdatedError extends Error { - constructor(files: Array) { - super([outdatedFilesMessage, ...files.map(formatFile)].join('\n')) - } -} diff --git a/ui-admin/icons/lib/generated/check/errors/generated.ts b/ui-admin/icons/lib/generated/check/errors/generated.ts new file mode 100644 index 000000000..b9f762749 --- /dev/null +++ b/ui-admin/icons/lib/generated/check/errors/generated.ts @@ -0,0 +1,17 @@ +import type { GeneratedErrorCode } from './codes.js' + +interface GeneratedErrorOptions { + code: GeneratedErrorCode + message: string +} + +export class GeneratedError extends Error { + readonly code: GeneratedErrorCode + + constructor({ code, message }: GeneratedErrorOptions) { + super(message) + + this.code = code + this.name = new.target.name + } +} diff --git a/ui-admin/icons/lib/generated/check/index.ts b/ui-admin/icons/lib/generated/check/index.ts index d6c9f116f..32b9ad493 100644 --- a/ui-admin/icons/lib/generated/check/index.ts +++ b/ui-admin/icons/lib/generated/check/index.ts @@ -3,7 +3,7 @@ import { mkdtemp } from 'node:fs/promises' import { rm } from 'node:fs/promises' import { join } from 'node:path' -import { GeneratedFilesOutdatedError } from './errors/generated-files-outdated.error.js' +import { FilesOutdatedError } from './errors/files-outdated.js' import { generatedIconsDirectoryName } from './constants.js' import { generatedReplacementsDirectoryName } from './constants.js' import { packageRootPath } from './constants.js' @@ -52,7 +52,7 @@ export const checkGenerated = async (): Promise => { ] if (outdatedFiles.length > 0) { - throw new GeneratedFilesOutdatedError(outdatedFiles) + throw new FilesOutdatedError(outdatedFiles) } } finally { await rm(temporaryPath, { force: true, recursive: true }) diff --git a/ui-admin/icons/lib/generated/check/input/constraints/directory.ts b/ui-admin/icons/lib/generated/check/input/constraints/directory.ts index 310fb15e5..bfa2eee4f 100644 --- a/ui-admin/icons/lib/generated/check/input/constraints/directory.ts +++ b/ui-admin/icons/lib/generated/check/input/constraints/directory.ts @@ -1,6 +1,6 @@ import type { FileSystemEntry } from '../file-system-entry.js' -import { DirectoryRequiredError } from '../../errors/directory-required.error.js' +import { DirectoryRequiredError } from '../../errors/directory-required.js' import { fileSystemEntryKinds } from '../file-system-entry.js' export const requireDirectory = (entry: FileSystemEntry): void => { diff --git a/ui-admin/icons/lib/generated/check/input/constraints/file.ts b/ui-admin/icons/lib/generated/check/input/constraints/file.ts index a6387399d..c67e669f9 100644 --- a/ui-admin/icons/lib/generated/check/input/constraints/file.ts +++ b/ui-admin/icons/lib/generated/check/input/constraints/file.ts @@ -1,6 +1,6 @@ import type { FileSystemEntry } from '../file-system-entry.js' -import { FileRequiredError } from '../../errors/file-required.error.js' +import { FileRequiredError } from '../../errors/file-required.js' import { fileSystemEntryKinds } from '../file-system-entry.js' export const requireFile = (entry: FileSystemEntry): void => { From 00e917043abd14ad9ac039ca561fbaf41f46f4ae Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Tue, 23 Jun 2026 17:13:46 +0300 Subject: [PATCH 11/19] refactor(icons): simplify generated check contracts --- .../icons/lib/generated/check/constants.ts | 6 +++ .../generated/check/errors/command-failed.ts | 15 +++--- .../check/errors/directory-required.ts | 12 ++--- .../generated/check/errors/file-required.ts | 8 +-- .../generated/check/errors/files-outdated.ts | 8 +-- .../lib/generated/check/errors/generated.ts | 7 +-- ui-admin/icons/lib/generated/check/index.ts | 8 +-- .../check/input/constraints/directory.ts | 13 ++--- .../generated/check/input/constraints/file.ts | 15 +++--- .../check/input/file-system-entry.ts | 54 ------------------- .../icons/lib/generated/check/input/read.ts | 35 ++++++++++++ 11 files changed, 78 insertions(+), 103 deletions(-) delete mode 100644 ui-admin/icons/lib/generated/check/input/file-system-entry.ts create mode 100644 ui-admin/icons/lib/generated/check/input/read.ts diff --git a/ui-admin/icons/lib/generated/check/constants.ts b/ui-admin/icons/lib/generated/check/constants.ts index 57e690266..8811572d7 100644 --- a/ui-admin/icons/lib/generated/check/constants.ts +++ b/ui-admin/icons/lib/generated/check/constants.ts @@ -43,3 +43,9 @@ export const relativePathSeparator = '/' export const outputLineBreak = '\n' export const checkSuccessMessage = 'Generated icons are up to date' + +export const commandFailedMessage = 'Command failed' + +export const directoryRequiredMessage = 'Directory is required' + +export const fileRequiredMessage = 'File is required' diff --git a/ui-admin/icons/lib/generated/check/errors/command-failed.ts b/ui-admin/icons/lib/generated/check/errors/command-failed.ts index 15bbd9e99..6a9791fa1 100644 --- a/ui-admin/icons/lib/generated/check/errors/command-failed.ts +++ b/ui-admin/icons/lib/generated/check/errors/command-failed.ts @@ -1,7 +1,6 @@ -import { GeneratedError } from './generated.js' -import { generatedErrorCodes } from './codes.js' - -const commandFailedMessage = 'Command failed' +import { GeneratedError } from './generated.js' +import { commandFailedMessage } from '../constants.js' +import { generatedErrorCodes } from './codes.js' export class CommandFailedError extends GeneratedError { readonly args: Array @@ -11,10 +10,10 @@ export class CommandFailedError extends GeneratedError { readonly exitCode: number | null constructor(command: string, args: Array, exitCode: number | null) { - super({ - code: generatedErrorCodes.commandFailed, - message: `${commandFailedMessage}: ${command} ${args.join(' ')} (${exitCode})`, - }) + super( + generatedErrorCodes.commandFailed, + `${commandFailedMessage}: ${command} ${args.join(' ')} (${exitCode})` + ) this.args = args this.command = command diff --git a/ui-admin/icons/lib/generated/check/errors/directory-required.ts b/ui-admin/icons/lib/generated/check/errors/directory-required.ts index 13acc4dc9..97f9e44cf 100644 --- a/ui-admin/icons/lib/generated/check/errors/directory-required.ts +++ b/ui-admin/icons/lib/generated/check/errors/directory-required.ts @@ -1,16 +1,12 @@ -import { GeneratedError } from './generated.js' -import { generatedErrorCodes } from './codes.js' - -const directoryRequiredMessage = 'Directory is required' +import { GeneratedError } from './generated.js' +import { directoryRequiredMessage } from '../constants.js' +import { generatedErrorCodes } from './codes.js' export class DirectoryRequiredError extends GeneratedError { readonly path: string constructor(targetPath: string) { - super({ - code: generatedErrorCodes.directoryRequired, - message: `${directoryRequiredMessage}: ${targetPath}`, - }) + super(generatedErrorCodes.directoryRequired, `${directoryRequiredMessage}: ${targetPath}`) this.path = targetPath } diff --git a/ui-admin/icons/lib/generated/check/errors/file-required.ts b/ui-admin/icons/lib/generated/check/errors/file-required.ts index 755ab6498..5c861c81d 100644 --- a/ui-admin/icons/lib/generated/check/errors/file-required.ts +++ b/ui-admin/icons/lib/generated/check/errors/file-required.ts @@ -1,16 +1,12 @@ import { GeneratedError } from './generated.js' +import { fileRequiredMessage } from '../constants.js' import { generatedErrorCodes } from './codes.js' -const fileRequiredMessage = 'File is required' - export class FileRequiredError extends GeneratedError { readonly path: string constructor(targetPath: string) { - super({ - code: generatedErrorCodes.fileRequired, - message: `${fileRequiredMessage}: ${targetPath}`, - }) + super(generatedErrorCodes.fileRequired, `${fileRequiredMessage}: ${targetPath}`) this.path = targetPath } diff --git a/ui-admin/icons/lib/generated/check/errors/files-outdated.ts b/ui-admin/icons/lib/generated/check/errors/files-outdated.ts index 9f274d56d..437fbbe33 100644 --- a/ui-admin/icons/lib/generated/check/errors/files-outdated.ts +++ b/ui-admin/icons/lib/generated/check/errors/files-outdated.ts @@ -19,10 +19,10 @@ export class FilesOutdatedError extends GeneratedError { readonly files: Array constructor(files: Array) { - super({ - code: generatedErrorCodes.filesOutdated, - message: [outdatedFilesMessage, ...files.map(formatFile)].join('\n'), - }) + super( + generatedErrorCodes.filesOutdated, + [outdatedFilesMessage, ...files.map(formatFile)].join('\n') + ) this.files = files } diff --git a/ui-admin/icons/lib/generated/check/errors/generated.ts b/ui-admin/icons/lib/generated/check/errors/generated.ts index b9f762749..946ba25bf 100644 --- a/ui-admin/icons/lib/generated/check/errors/generated.ts +++ b/ui-admin/icons/lib/generated/check/errors/generated.ts @@ -1,14 +1,9 @@ import type { GeneratedErrorCode } from './codes.js' -interface GeneratedErrorOptions { - code: GeneratedErrorCode - message: string -} - export class GeneratedError extends Error { readonly code: GeneratedErrorCode - constructor({ code, message }: GeneratedErrorOptions) { + constructor(code: GeneratedErrorCode, message: string) { super(message) this.code = code diff --git a/ui-admin/icons/lib/generated/check/index.ts b/ui-admin/icons/lib/generated/check/index.ts index 32b9ad493..1c6d28bc4 100644 --- a/ui-admin/icons/lib/generated/check/index.ts +++ b/ui-admin/icons/lib/generated/check/index.ts @@ -19,12 +19,12 @@ import { generateIcons } from './expected/icons.js' import { generateReplacements } from './expected/replacements.js' import { requireDirectory } from './input/constraints/directory.js' import { requireFile } from './input/constraints/file.js' -import { readFileSystemEntry } from './input/file-system-entry.js' +import { readInputPathKind } from './input/read.js' export const checkGenerated = async (): Promise => { - requireDirectory(await readFileSystemEntry(sourceIconsPath)) - requireDirectory(await readFileSystemEntry(sourceSvgPath)) - requireFile(await readFileSystemEntry(sourceReplacementsPath)) + requireDirectory(sourceIconsPath, await readInputPathKind(sourceIconsPath)) + requireDirectory(sourceSvgPath, await readInputPathKind(sourceSvgPath)) + requireFile(sourceReplacementsPath, await readInputPathKind(sourceReplacementsPath)) await mkdir(packageRootPath, { recursive: true }) diff --git a/ui-admin/icons/lib/generated/check/input/constraints/directory.ts b/ui-admin/icons/lib/generated/check/input/constraints/directory.ts index bfa2eee4f..ef8f04549 100644 --- a/ui-admin/icons/lib/generated/check/input/constraints/directory.ts +++ b/ui-admin/icons/lib/generated/check/input/constraints/directory.ts @@ -1,10 +1,11 @@ -import type { FileSystemEntry } from '../file-system-entry.js' - import { DirectoryRequiredError } from '../../errors/directory-required.js' -import { fileSystemEntryKinds } from '../file-system-entry.js' +import { inputPathKinds } from '../read.js' -export const requireDirectory = (entry: FileSystemEntry): void => { - if (entry.kind !== fileSystemEntryKinds.directory) { - throw new DirectoryRequiredError(entry.path) +export const requireDirectory = ( + targetPath: string, + kind: (typeof inputPathKinds)[keyof typeof inputPathKinds] +): void => { + if (kind !== inputPathKinds.directory) { + throw new DirectoryRequiredError(targetPath) } } diff --git a/ui-admin/icons/lib/generated/check/input/constraints/file.ts b/ui-admin/icons/lib/generated/check/input/constraints/file.ts index c67e669f9..2164d4401 100644 --- a/ui-admin/icons/lib/generated/check/input/constraints/file.ts +++ b/ui-admin/icons/lib/generated/check/input/constraints/file.ts @@ -1,10 +1,11 @@ -import type { FileSystemEntry } from '../file-system-entry.js' +import { FileRequiredError } from '../../errors/file-required.js' +import { inputPathKinds } from '../read.js' -import { FileRequiredError } from '../../errors/file-required.js' -import { fileSystemEntryKinds } from '../file-system-entry.js' - -export const requireFile = (entry: FileSystemEntry): void => { - if (entry.kind !== fileSystemEntryKinds.file) { - throw new FileRequiredError(entry.path) +export const requireFile = ( + targetPath: string, + kind: (typeof inputPathKinds)[keyof typeof inputPathKinds] +): void => { + if (kind !== inputPathKinds.file) { + throw new FileRequiredError(targetPath) } } diff --git a/ui-admin/icons/lib/generated/check/input/file-system-entry.ts b/ui-admin/icons/lib/generated/check/input/file-system-entry.ts deleted file mode 100644 index 59a8c2948..000000000 --- a/ui-admin/icons/lib/generated/check/input/file-system-entry.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { stat } from 'node:fs/promises' - -export const fileSystemEntryKinds = { - directory: 'directory', - file: 'file', - missing: 'missing', - other: 'other', - unavailable: 'unavailable', -} as const - -export type FileSystemEntryKind = (typeof fileSystemEntryKinds)[keyof typeof fileSystemEntryKinds] - -export interface FileSystemEntry { - error?: unknown - kind: FileSystemEntryKind - path: string -} - -const isMissingError = (error: unknown): boolean => - typeof error === 'object' && - error !== null && - 'code' in error && - (error as { code?: unknown }).code === 'ENOENT' - -export const readFileSystemEntry = async (targetPath: string): Promise => { - try { - const stats = await stat(targetPath) - - if (stats.isDirectory()) { - return { - kind: fileSystemEntryKinds.directory, - path: targetPath, - } - } - - if (stats.isFile()) { - return { - kind: fileSystemEntryKinds.file, - path: targetPath, - } - } - - return { - kind: fileSystemEntryKinds.other, - path: targetPath, - } - } catch (error) { - return { - error, - kind: isMissingError(error) ? fileSystemEntryKinds.missing : fileSystemEntryKinds.unavailable, - path: targetPath, - } - } -} diff --git a/ui-admin/icons/lib/generated/check/input/read.ts b/ui-admin/icons/lib/generated/check/input/read.ts new file mode 100644 index 000000000..d781ac329 --- /dev/null +++ b/ui-admin/icons/lib/generated/check/input/read.ts @@ -0,0 +1,35 @@ +import { stat } from 'node:fs/promises' + +export const inputPathKinds = { + directory: 'directory', + file: 'file', + missing: 'missing', + other: 'other', + unavailable: 'unavailable', +} as const + +const isMissingError = (error: unknown): boolean => + typeof error === 'object' && + error !== null && + 'code' in error && + (error as { code?: unknown }).code === 'ENOENT' + +export const readInputPathKind = async ( + targetPath: string +): Promise<(typeof inputPathKinds)[keyof typeof inputPathKinds]> => { + try { + const stats = await stat(targetPath) + + if (stats.isDirectory()) { + return inputPathKinds.directory + } + + if (stats.isFile()) { + return inputPathKinds.file + } + + return inputPathKinds.other + } catch (error) { + return isMissingError(error) ? inputPathKinds.missing : inputPathKinds.unavailable + } +} From d79b94c6a5434eb2bae9261b9e99e86a5d64f54e Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Tue, 23 Jun 2026 18:05:13 +0300 Subject: [PATCH 12/19] refactor(icons): colocate generated error messages --- ui-admin/icons/lib/generated/check/constants.ts | 6 ------ ui-admin/icons/lib/generated/check/errors/command-failed.ts | 2 +- .../icons/lib/generated/check/errors/directory-required.ts | 2 +- ui-admin/icons/lib/generated/check/errors/file-required.ts | 2 +- ui-admin/icons/lib/generated/check/errors/messages.ts | 5 +++++ 5 files changed, 8 insertions(+), 9 deletions(-) create mode 100644 ui-admin/icons/lib/generated/check/errors/messages.ts diff --git a/ui-admin/icons/lib/generated/check/constants.ts b/ui-admin/icons/lib/generated/check/constants.ts index 8811572d7..57e690266 100644 --- a/ui-admin/icons/lib/generated/check/constants.ts +++ b/ui-admin/icons/lib/generated/check/constants.ts @@ -43,9 +43,3 @@ export const relativePathSeparator = '/' export const outputLineBreak = '\n' export const checkSuccessMessage = 'Generated icons are up to date' - -export const commandFailedMessage = 'Command failed' - -export const directoryRequiredMessage = 'Directory is required' - -export const fileRequiredMessage = 'File is required' diff --git a/ui-admin/icons/lib/generated/check/errors/command-failed.ts b/ui-admin/icons/lib/generated/check/errors/command-failed.ts index 6a9791fa1..258875a11 100644 --- a/ui-admin/icons/lib/generated/check/errors/command-failed.ts +++ b/ui-admin/icons/lib/generated/check/errors/command-failed.ts @@ -1,6 +1,6 @@ import { GeneratedError } from './generated.js' -import { commandFailedMessage } from '../constants.js' import { generatedErrorCodes } from './codes.js' +import { commandFailedMessage } from './messages.js' export class CommandFailedError extends GeneratedError { readonly args: Array diff --git a/ui-admin/icons/lib/generated/check/errors/directory-required.ts b/ui-admin/icons/lib/generated/check/errors/directory-required.ts index 97f9e44cf..756590af1 100644 --- a/ui-admin/icons/lib/generated/check/errors/directory-required.ts +++ b/ui-admin/icons/lib/generated/check/errors/directory-required.ts @@ -1,6 +1,6 @@ import { GeneratedError } from './generated.js' -import { directoryRequiredMessage } from '../constants.js' import { generatedErrorCodes } from './codes.js' +import { directoryRequiredMessage } from './messages.js' export class DirectoryRequiredError extends GeneratedError { readonly path: string diff --git a/ui-admin/icons/lib/generated/check/errors/file-required.ts b/ui-admin/icons/lib/generated/check/errors/file-required.ts index 5c861c81d..232e271c8 100644 --- a/ui-admin/icons/lib/generated/check/errors/file-required.ts +++ b/ui-admin/icons/lib/generated/check/errors/file-required.ts @@ -1,6 +1,6 @@ import { GeneratedError } from './generated.js' -import { fileRequiredMessage } from '../constants.js' import { generatedErrorCodes } from './codes.js' +import { fileRequiredMessage } from './messages.js' export class FileRequiredError extends GeneratedError { readonly path: string diff --git a/ui-admin/icons/lib/generated/check/errors/messages.ts b/ui-admin/icons/lib/generated/check/errors/messages.ts new file mode 100644 index 000000000..5fd0a2f98 --- /dev/null +++ b/ui-admin/icons/lib/generated/check/errors/messages.ts @@ -0,0 +1,5 @@ +export const commandFailedMessage = 'Command failed' + +export const directoryRequiredMessage = 'Directory is required' + +export const fileRequiredMessage = 'File is required' From c7bcbf975661bc491d955d3a6e436ff4ba8669cd Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Wed, 1 Jul 2026 17:47:10 +0300 Subject: [PATCH 13/19] fix(icons): align check command interfaces --- .../lib/generated/check/command/{options.ts => interfaces.ts} | 0 ui-admin/icons/lib/generated/check/command/run.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename ui-admin/icons/lib/generated/check/command/{options.ts => interfaces.ts} (100%) diff --git a/ui-admin/icons/lib/generated/check/command/options.ts b/ui-admin/icons/lib/generated/check/command/interfaces.ts similarity index 100% rename from ui-admin/icons/lib/generated/check/command/options.ts rename to ui-admin/icons/lib/generated/check/command/interfaces.ts diff --git a/ui-admin/icons/lib/generated/check/command/run.ts b/ui-admin/icons/lib/generated/check/command/run.ts index f074d847b..3f11aed21 100644 --- a/ui-admin/icons/lib/generated/check/command/run.ts +++ b/ui-admin/icons/lib/generated/check/command/run.ts @@ -1,4 +1,4 @@ -import type { CommandOptions } from './options.js' +import type { CommandOptions } from './interfaces.js' import { spawn } from 'node:child_process' From b93ce1a93ed2550088a4b82bc724d3d6c60f6f44 Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Wed, 1 Jul 2026 17:48:43 +0300 Subject: [PATCH 14/19] fix(icons): model generated file drift state --- .../lib/generated/check/drift/directory.ts | 16 +++++++------- .../lib/generated/check/drift/file-drift.ts | 14 ++++++++++++ .../icons/lib/generated/check/drift/file.ts | 22 +++++++++---------- .../generated/check/drift/outdated-file.ts | 14 ------------ .../generated/check/errors/files-outdated.ts | 20 ++++++++--------- 5 files changed, 43 insertions(+), 43 deletions(-) create mode 100644 ui-admin/icons/lib/generated/check/drift/file-drift.ts delete mode 100644 ui-admin/icons/lib/generated/check/drift/outdated-file.ts diff --git a/ui-admin/icons/lib/generated/check/drift/directory.ts b/ui-admin/icons/lib/generated/check/drift/directory.ts index 7351810ea..936cd7b59 100644 --- a/ui-admin/icons/lib/generated/check/drift/directory.ts +++ b/ui-admin/icons/lib/generated/check/drift/directory.ts @@ -1,12 +1,12 @@ -import type { OutdatedFile } from './outdated-file.js' +import type { FileDrift } from './file-drift.js' import { readdir } from 'node:fs/promises' import { join } from 'node:path' import { sep } from 'node:path' import { relativePathSeparator } from '../constants.js' -import { isOutdatedFile } from './file.js' -import { outdatedFileReasons } from './outdated-file.js' +import { fileDriftStates } from './file-drift.js' +import { hasFileDrift } from './file.js' interface DirectoryComparisonOptions { actualPath: string @@ -37,7 +37,7 @@ const collectFiles = async (directoryPath: string, currentPath = ''): Promise> => { +}: DirectoryComparisonOptions): Promise> => { const expectedFiles = new Set(await collectFiles(expectedPath)) const actualFiles = new Set(await collectFiles(actualPath)) const relativePaths = [...new Set([...expectedFiles, ...actualFiles])].sort((pathA, pathB) => @@ -52,7 +52,7 @@ export const compareDirectories = async ({ return [ { actualPath: actualFilePath, - reason: outdatedFileReasons.unexpected, + state: fileDriftStates.unexpected, relativePath, }, ] @@ -62,18 +62,18 @@ export const compareDirectories = async ({ return [ { expectedPath: expectedFilePath, - reason: outdatedFileReasons.missing, + state: fileDriftStates.missing, relativePath, }, ] } - if (await isOutdatedFile(expectedFilePath, actualFilePath)) { + if (await hasFileDrift(expectedFilePath, actualFilePath)) { return [ { actualPath: actualFilePath, expectedPath: expectedFilePath, - reason: outdatedFileReasons.different, + state: fileDriftStates.different, relativePath, }, ] diff --git a/ui-admin/icons/lib/generated/check/drift/file-drift.ts b/ui-admin/icons/lib/generated/check/drift/file-drift.ts new file mode 100644 index 000000000..b5f18a071 --- /dev/null +++ b/ui-admin/icons/lib/generated/check/drift/file-drift.ts @@ -0,0 +1,14 @@ +export const fileDriftStates = { + different: 'different', + missing: 'missing', + unexpected: 'unexpected', +} as const + +export type FileDriftState = (typeof fileDriftStates)[keyof typeof fileDriftStates] + +export interface FileDrift { + actualPath?: string + expectedPath?: string + relativePath: string + state: FileDriftState +} diff --git a/ui-admin/icons/lib/generated/check/drift/file.ts b/ui-admin/icons/lib/generated/check/drift/file.ts index 40054dc09..11fa8a2e9 100644 --- a/ui-admin/icons/lib/generated/check/drift/file.ts +++ b/ui-admin/icons/lib/generated/check/drift/file.ts @@ -1,10 +1,10 @@ -import type { OutdatedFile } from './outdated-file.js' +import type { FileDrift } from './file-drift.js' -import { readFile } from 'node:fs/promises' -import { stat } from 'node:fs/promises' -import { basename } from 'node:path' +import { readFile } from 'node:fs/promises' +import { stat } from 'node:fs/promises' +import { basename } from 'node:path' -import { outdatedFileReasons } from './outdated-file.js' +import { fileDriftStates } from './file-drift.js' const isFile = async (targetPath: string): Promise => { try { @@ -14,13 +14,13 @@ const isFile = async (targetPath: string): Promise => { } } -export const isOutdatedFile = async (expectedPath: string, actualPath: string): Promise => +export const hasFileDrift = async (expectedPath: string, actualPath: string): Promise => !(await readFile(expectedPath)).equals(await readFile(actualPath)) export const compareFiles = async ( expectedPath: string, actualPath: string -): Promise> => { +): Promise> => { const relativePath = basename(actualPath) const hasExpectedFile = await isFile(expectedPath) const hasActualFile = await isFile(actualPath) @@ -29,7 +29,7 @@ export const compareFiles = async ( return [ { actualPath, - reason: outdatedFileReasons.unexpected, + state: fileDriftStates.unexpected, relativePath, }, ] @@ -39,18 +39,18 @@ export const compareFiles = async ( return [ { expectedPath, - reason: outdatedFileReasons.missing, + state: fileDriftStates.missing, relativePath, }, ] } - if (await isOutdatedFile(expectedPath, actualPath)) { + if (await hasFileDrift(expectedPath, actualPath)) { return [ { actualPath, expectedPath, - reason: outdatedFileReasons.different, + state: fileDriftStates.different, relativePath, }, ] diff --git a/ui-admin/icons/lib/generated/check/drift/outdated-file.ts b/ui-admin/icons/lib/generated/check/drift/outdated-file.ts deleted file mode 100644 index 2911d0042..000000000 --- a/ui-admin/icons/lib/generated/check/drift/outdated-file.ts +++ /dev/null @@ -1,14 +0,0 @@ -export const outdatedFileReasons = { - different: 'different', - missing: 'missing', - unexpected: 'unexpected', -} as const - -export type OutdatedFileReason = (typeof outdatedFileReasons)[keyof typeof outdatedFileReasons] - -export interface OutdatedFile { - actualPath?: string - expectedPath?: string - reason: OutdatedFileReason - relativePath: string -} diff --git a/ui-admin/icons/lib/generated/check/errors/files-outdated.ts b/ui-admin/icons/lib/generated/check/errors/files-outdated.ts index 437fbbe33..6bfabb213 100644 --- a/ui-admin/icons/lib/generated/check/errors/files-outdated.ts +++ b/ui-admin/icons/lib/generated/check/errors/files-outdated.ts @@ -1,24 +1,24 @@ -import type { OutdatedFile } from '../drift/outdated-file.js' +import type { FileDrift } from '../drift/file-drift.js' import { GeneratedError } from './generated.js' -import { outdatedFileReasons } from '../drift/outdated-file.js' +import { fileDriftStates } from '../drift/file-drift.js' import { generatedErrorCodes } from './codes.js' const outdatedFilesMessage = 'Generated files are outdated' -const outdatedFileReasonMessages = { - [outdatedFileReasons.different]: 'content differs', - [outdatedFileReasons.missing]: 'missing committed file', - [outdatedFileReasons.unexpected]: 'unexpected committed file', +const fileDriftStateMessages = { + [fileDriftStates.different]: 'content differs', + [fileDriftStates.missing]: 'missing committed file', + [fileDriftStates.unexpected]: 'unexpected committed file', } -const formatFile = (file: OutdatedFile): string => - `${file.relativePath}: ${outdatedFileReasonMessages[file.reason]}` +const formatFile = (file: FileDrift): string => + `${file.relativePath}: ${fileDriftStateMessages[file.state]}` export class FilesOutdatedError extends GeneratedError { - readonly files: Array + readonly files: Array - constructor(files: Array) { + constructor(files: Array) { super( generatedErrorCodes.filesOutdated, [outdatedFilesMessage, ...files.map(formatFile)].join('\n') From fd4e56bc12882a70e741a20f12f8d845c3bca11f Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Wed, 1 Jul 2026 17:49:44 +0300 Subject: [PATCH 15/19] fix(icons): make input constraints predicates --- ui-admin/icons/lib/generated/check/index.ts | 20 ++++++++++++++----- .../check/input/constraints/directory.ts | 11 ---------- .../generated/check/input/constraints/file.ts | 11 ---------- .../constraints/is-directory-required.ts | 5 +++++ .../input/constraints/is-file-required.ts | 5 +++++ 5 files changed, 25 insertions(+), 27 deletions(-) delete mode 100644 ui-admin/icons/lib/generated/check/input/constraints/directory.ts delete mode 100644 ui-admin/icons/lib/generated/check/input/constraints/file.ts create mode 100644 ui-admin/icons/lib/generated/check/input/constraints/is-directory-required.ts create mode 100644 ui-admin/icons/lib/generated/check/input/constraints/is-file-required.ts diff --git a/ui-admin/icons/lib/generated/check/index.ts b/ui-admin/icons/lib/generated/check/index.ts index 1c6d28bc4..05aa82ed2 100644 --- a/ui-admin/icons/lib/generated/check/index.ts +++ b/ui-admin/icons/lib/generated/check/index.ts @@ -3,6 +3,8 @@ import { mkdtemp } from 'node:fs/promises' import { rm } from 'node:fs/promises' import { join } from 'node:path' +import { DirectoryRequiredError } from './errors/directory-required.js' +import { FileRequiredError } from './errors/file-required.js' import { FilesOutdatedError } from './errors/files-outdated.js' import { generatedIconsDirectoryName } from './constants.js' import { generatedReplacementsDirectoryName } from './constants.js' @@ -17,14 +19,22 @@ import { compareFiles } from './drift/file.js' import { formatFiles } from './expected/format.js' import { generateIcons } from './expected/icons.js' import { generateReplacements } from './expected/replacements.js' -import { requireDirectory } from './input/constraints/directory.js' -import { requireFile } from './input/constraints/file.js' +import { isDirectoryRequired } from './input/constraints/is-directory-required.js' +import { isFileRequired } from './input/constraints/is-file-required.js' import { readInputPathKind } from './input/read.js' export const checkGenerated = async (): Promise => { - requireDirectory(sourceIconsPath, await readInputPathKind(sourceIconsPath)) - requireDirectory(sourceSvgPath, await readInputPathKind(sourceSvgPath)) - requireFile(sourceReplacementsPath, await readInputPathKind(sourceReplacementsPath)) + if (isDirectoryRequired(await readInputPathKind(sourceIconsPath))) { + throw new DirectoryRequiredError(sourceIconsPath) + } + + if (isDirectoryRequired(await readInputPathKind(sourceSvgPath))) { + throw new DirectoryRequiredError(sourceSvgPath) + } + + if (isFileRequired(await readInputPathKind(sourceReplacementsPath))) { + throw new FileRequiredError(sourceReplacementsPath) + } await mkdir(packageRootPath, { recursive: true }) diff --git a/ui-admin/icons/lib/generated/check/input/constraints/directory.ts b/ui-admin/icons/lib/generated/check/input/constraints/directory.ts deleted file mode 100644 index ef8f04549..000000000 --- a/ui-admin/icons/lib/generated/check/input/constraints/directory.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { DirectoryRequiredError } from '../../errors/directory-required.js' -import { inputPathKinds } from '../read.js' - -export const requireDirectory = ( - targetPath: string, - kind: (typeof inputPathKinds)[keyof typeof inputPathKinds] -): void => { - if (kind !== inputPathKinds.directory) { - throw new DirectoryRequiredError(targetPath) - } -} diff --git a/ui-admin/icons/lib/generated/check/input/constraints/file.ts b/ui-admin/icons/lib/generated/check/input/constraints/file.ts deleted file mode 100644 index 2164d4401..000000000 --- a/ui-admin/icons/lib/generated/check/input/constraints/file.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { FileRequiredError } from '../../errors/file-required.js' -import { inputPathKinds } from '../read.js' - -export const requireFile = ( - targetPath: string, - kind: (typeof inputPathKinds)[keyof typeof inputPathKinds] -): void => { - if (kind !== inputPathKinds.file) { - throw new FileRequiredError(targetPath) - } -} diff --git a/ui-admin/icons/lib/generated/check/input/constraints/is-directory-required.ts b/ui-admin/icons/lib/generated/check/input/constraints/is-directory-required.ts new file mode 100644 index 000000000..27a008173 --- /dev/null +++ b/ui-admin/icons/lib/generated/check/input/constraints/is-directory-required.ts @@ -0,0 +1,5 @@ +import { inputPathKinds } from '../read.js' + +export const isDirectoryRequired = ( + kind: (typeof inputPathKinds)[keyof typeof inputPathKinds] +): boolean => kind !== inputPathKinds.directory diff --git a/ui-admin/icons/lib/generated/check/input/constraints/is-file-required.ts b/ui-admin/icons/lib/generated/check/input/constraints/is-file-required.ts new file mode 100644 index 000000000..0172f09c8 --- /dev/null +++ b/ui-admin/icons/lib/generated/check/input/constraints/is-file-required.ts @@ -0,0 +1,5 @@ +import { inputPathKinds } from '../read.js' + +export const isFileRequired = ( + kind: (typeof inputPathKinds)[keyof typeof inputPathKinds] +): boolean => kind !== inputPathKinds.file From 625792771a99f7858940e6eb477ed6a58f4863ac Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Wed, 1 Jul 2026 17:50:47 +0300 Subject: [PATCH 16/19] fix(icons): clarify input path failure handling --- .../constraints/is-directory-required.ts | 9 +++---- .../input/constraints/is-file-required.ts | 8 +++---- .../icons/lib/generated/check/input/read.ts | 24 +++++++++++-------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/ui-admin/icons/lib/generated/check/input/constraints/is-directory-required.ts b/ui-admin/icons/lib/generated/check/input/constraints/is-directory-required.ts index 27a008173..35296434e 100644 --- a/ui-admin/icons/lib/generated/check/input/constraints/is-directory-required.ts +++ b/ui-admin/icons/lib/generated/check/input/constraints/is-directory-required.ts @@ -1,5 +1,6 @@ -import { inputPathKinds } from '../read.js' +import type { InputPathKind } from '../read.js' -export const isDirectoryRequired = ( - kind: (typeof inputPathKinds)[keyof typeof inputPathKinds] -): boolean => kind !== inputPathKinds.directory +import { inputPathKinds } from '../read.js' + +export const isDirectoryRequired = (kind: InputPathKind): boolean => + kind !== inputPathKinds.directory diff --git a/ui-admin/icons/lib/generated/check/input/constraints/is-file-required.ts b/ui-admin/icons/lib/generated/check/input/constraints/is-file-required.ts index 0172f09c8..75ea79e26 100644 --- a/ui-admin/icons/lib/generated/check/input/constraints/is-file-required.ts +++ b/ui-admin/icons/lib/generated/check/input/constraints/is-file-required.ts @@ -1,5 +1,5 @@ -import { inputPathKinds } from '../read.js' +import type { InputPathKind } from '../read.js' -export const isFileRequired = ( - kind: (typeof inputPathKinds)[keyof typeof inputPathKinds] -): boolean => kind !== inputPathKinds.file +import { inputPathKinds } from '../read.js' + +export const isFileRequired = (kind: InputPathKind): boolean => kind !== inputPathKinds.file diff --git a/ui-admin/icons/lib/generated/check/input/read.ts b/ui-admin/icons/lib/generated/check/input/read.ts index d781ac329..993eb9c66 100644 --- a/ui-admin/icons/lib/generated/check/input/read.ts +++ b/ui-admin/icons/lib/generated/check/input/read.ts @@ -8,15 +8,19 @@ export const inputPathKinds = { unavailable: 'unavailable', } as const -const isMissingError = (error: unknown): boolean => - typeof error === 'object' && - error !== null && - 'code' in error && - (error as { code?: unknown }).code === 'ENOENT' - -export const readInputPathKind = async ( - targetPath: string -): Promise<(typeof inputPathKinds)[keyof typeof inputPathKinds]> => { +export type InputPathKind = (typeof inputPathKinds)[keyof typeof inputPathKinds] + +const missingInputPathErrorCode = 'ENOENT' + +const resolveFailedInputPathKind = (error: unknown): InputPathKind => { + if (error instanceof Error && 'code' in error && error.code === missingInputPathErrorCode) { + return inputPathKinds.missing + } + + return inputPathKinds.unavailable +} + +export const readInputPathKind = async (targetPath: string): Promise => { try { const stats = await stat(targetPath) @@ -30,6 +34,6 @@ export const readInputPathKind = async ( return inputPathKinds.other } catch (error) { - return isMissingError(error) ? inputPathKinds.missing : inputPathKinds.unavailable + return resolveFailedInputPathKind(error) } } From 15d4dfa26be12f6084d4b863eb2ada593231ef7d Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Wed, 1 Jul 2026 17:51:51 +0300 Subject: [PATCH 17/19] fix(icons): colocate check generated cli --- .../check-generated.ts => lib/generated/check/cli.ts} | 6 +++--- ui-admin/icons/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename ui-admin/icons/{scripts/check-generated.ts => lib/generated/check/cli.ts} (57%) diff --git a/ui-admin/icons/scripts/check-generated.ts b/ui-admin/icons/lib/generated/check/cli.ts similarity index 57% rename from ui-admin/icons/scripts/check-generated.ts rename to ui-admin/icons/lib/generated/check/cli.ts index b858cbdd7..55af9aaab 100644 --- a/ui-admin/icons/scripts/check-generated.ts +++ b/ui-admin/icons/lib/generated/check/cli.ts @@ -1,6 +1,6 @@ -import { checkSuccessMessage } from '../lib/generated/check/constants.js' -import { outputLineBreak } from '../lib/generated/check/constants.js' -import { checkGenerated } from '../lib/generated/check/index.js' +import { checkSuccessMessage } from './constants.js' +import { outputLineBreak } from './constants.js' +import { checkGenerated } from './index.js' try { await checkGenerated() diff --git a/ui-admin/icons/package.json b/ui-admin/icons/package.json index 3dbc104eb..3d0afa6a3 100644 --- a/ui-admin/icons/package.json +++ b/ui-admin/icons/package.json @@ -9,7 +9,7 @@ ], "scripts": { "build": "yarn library build", - "check-generated": "yarn workspace @atls-ui-generators/icons node --import tsx ../../ui-admin/icons/scripts/check-generated.ts", + "check-generated": "yarn workspace @atls-ui-generators/icons node --import tsx ../../ui-admin/icons/lib/generated/check/cli.ts", "generate-icons": "yarn workspace @atls-ui-generators/icons generate-icons ../../ui-admin/icons/src/icons -i ../../ui-admin/icons/svg -r ../../ui-admin/icons/src/replacements.ts && yarn format", "generate-replacements": "yarn workspace @atls-ui-generators/icons generate-icons replacements ../../ui-admin/icons/src -i ../../ui-admin/icons/svg && yarn format", "prepack": "yarn run build", From 180355a1aa9fcb0a9ca5a9c9a17c1e9a5a759cdf Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Wed, 1 Jul 2026 18:18:22 +0300 Subject: [PATCH 18/19] fix(icons): expose drift comparison interfaces --- ui-admin/icons/lib/generated/check/drift/directory.ts | 4 ++-- ui-admin/icons/lib/generated/check/drift/file.ts | 4 ++-- .../generated/check/drift/{file-drift.ts => interfaces.ts} | 0 ui-admin/icons/lib/generated/check/errors/files-outdated.ts | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) rename ui-admin/icons/lib/generated/check/drift/{file-drift.ts => interfaces.ts} (100%) diff --git a/ui-admin/icons/lib/generated/check/drift/directory.ts b/ui-admin/icons/lib/generated/check/drift/directory.ts index 936cd7b59..655d5f61e 100644 --- a/ui-admin/icons/lib/generated/check/drift/directory.ts +++ b/ui-admin/icons/lib/generated/check/drift/directory.ts @@ -1,12 +1,12 @@ -import type { FileDrift } from './file-drift.js' +import type { FileDrift } from './interfaces.js' import { readdir } from 'node:fs/promises' import { join } from 'node:path' import { sep } from 'node:path' import { relativePathSeparator } from '../constants.js' -import { fileDriftStates } from './file-drift.js' import { hasFileDrift } from './file.js' +import { fileDriftStates } from './interfaces.js' interface DirectoryComparisonOptions { actualPath: string diff --git a/ui-admin/icons/lib/generated/check/drift/file.ts b/ui-admin/icons/lib/generated/check/drift/file.ts index 11fa8a2e9..513373f43 100644 --- a/ui-admin/icons/lib/generated/check/drift/file.ts +++ b/ui-admin/icons/lib/generated/check/drift/file.ts @@ -1,10 +1,10 @@ -import type { FileDrift } from './file-drift.js' +import type { FileDrift } from './interfaces.js' import { readFile } from 'node:fs/promises' import { stat } from 'node:fs/promises' import { basename } from 'node:path' -import { fileDriftStates } from './file-drift.js' +import { fileDriftStates } from './interfaces.js' const isFile = async (targetPath: string): Promise => { try { diff --git a/ui-admin/icons/lib/generated/check/drift/file-drift.ts b/ui-admin/icons/lib/generated/check/drift/interfaces.ts similarity index 100% rename from ui-admin/icons/lib/generated/check/drift/file-drift.ts rename to ui-admin/icons/lib/generated/check/drift/interfaces.ts diff --git a/ui-admin/icons/lib/generated/check/errors/files-outdated.ts b/ui-admin/icons/lib/generated/check/errors/files-outdated.ts index 6bfabb213..aaac94c18 100644 --- a/ui-admin/icons/lib/generated/check/errors/files-outdated.ts +++ b/ui-admin/icons/lib/generated/check/errors/files-outdated.ts @@ -1,7 +1,7 @@ -import type { FileDrift } from '../drift/file-drift.js' +import type { FileDrift } from '../drift/interfaces.js' import { GeneratedError } from './generated.js' -import { fileDriftStates } from '../drift/file-drift.js' +import { fileDriftStates } from '../drift/interfaces.js' import { generatedErrorCodes } from './codes.js' const outdatedFilesMessage = 'Generated files are outdated' From 572f3df473fdbf5859da89b9e2046c0f80777e08 Mon Sep 17 00:00:00 2001 From: Arina Gazhina Date: Wed, 1 Jul 2026 18:25:01 +0300 Subject: [PATCH 19/19] fix(icons): split drift messages and interfaces --- .../lib/generated/check/drift/directory.ts | 20 ++++++++----------- .../lib/generated/check/drift/interfaces.ts | 5 +++++ .../generated/check/errors/files-outdated.ts | 19 ++++++------------ .../lib/generated/check/errors/messages.ts | 10 ++++++++++ 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/ui-admin/icons/lib/generated/check/drift/directory.ts b/ui-admin/icons/lib/generated/check/drift/directory.ts index 655d5f61e..9be2bbe2f 100644 --- a/ui-admin/icons/lib/generated/check/drift/directory.ts +++ b/ui-admin/icons/lib/generated/check/drift/directory.ts @@ -1,17 +1,13 @@ -import type { FileDrift } from './interfaces.js' +import type { DirectoryComparisonOptions } from './interfaces.js' +import type { FileDrift } from './interfaces.js' -import { readdir } from 'node:fs/promises' -import { join } from 'node:path' -import { sep } from 'node:path' +import { readdir } from 'node:fs/promises' +import { join } from 'node:path' +import { sep } from 'node:path' -import { relativePathSeparator } from '../constants.js' -import { hasFileDrift } from './file.js' -import { fileDriftStates } from './interfaces.js' - -interface DirectoryComparisonOptions { - actualPath: string - expectedPath: string -} +import { relativePathSeparator } from '../constants.js' +import { hasFileDrift } from './file.js' +import { fileDriftStates } from './interfaces.js' const collectFiles = async (directoryPath: string, currentPath = ''): Promise> => { const entries = await readdir(join(directoryPath, currentPath), { withFileTypes: true }) diff --git a/ui-admin/icons/lib/generated/check/drift/interfaces.ts b/ui-admin/icons/lib/generated/check/drift/interfaces.ts index b5f18a071..8b63d80a1 100644 --- a/ui-admin/icons/lib/generated/check/drift/interfaces.ts +++ b/ui-admin/icons/lib/generated/check/drift/interfaces.ts @@ -12,3 +12,8 @@ export interface FileDrift { relativePath: string state: FileDriftState } + +export interface DirectoryComparisonOptions { + actualPath: string + expectedPath: string +} diff --git a/ui-admin/icons/lib/generated/check/errors/files-outdated.ts b/ui-admin/icons/lib/generated/check/errors/files-outdated.ts index aaac94c18..47ecdb507 100644 --- a/ui-admin/icons/lib/generated/check/errors/files-outdated.ts +++ b/ui-admin/icons/lib/generated/check/errors/files-outdated.ts @@ -1,16 +1,9 @@ -import type { FileDrift } from '../drift/interfaces.js' +import type { FileDrift } from '../drift/interfaces.js' -import { GeneratedError } from './generated.js' -import { fileDriftStates } from '../drift/interfaces.js' -import { generatedErrorCodes } from './codes.js' - -const outdatedFilesMessage = 'Generated files are outdated' - -const fileDriftStateMessages = { - [fileDriftStates.different]: 'content differs', - [fileDriftStates.missing]: 'missing committed file', - [fileDriftStates.unexpected]: 'unexpected committed file', -} +import { GeneratedError } from './generated.js' +import { generatedErrorCodes } from './codes.js' +import { fileDriftStateMessages } from './messages.js' +import { filesOutdatedMessage } from './messages.js' const formatFile = (file: FileDrift): string => `${file.relativePath}: ${fileDriftStateMessages[file.state]}` @@ -21,7 +14,7 @@ export class FilesOutdatedError extends GeneratedError { constructor(files: Array) { super( generatedErrorCodes.filesOutdated, - [outdatedFilesMessage, ...files.map(formatFile)].join('\n') + [filesOutdatedMessage, ...files.map(formatFile)].join('\n') ) this.files = files diff --git a/ui-admin/icons/lib/generated/check/errors/messages.ts b/ui-admin/icons/lib/generated/check/errors/messages.ts index 5fd0a2f98..a4e3f786c 100644 --- a/ui-admin/icons/lib/generated/check/errors/messages.ts +++ b/ui-admin/icons/lib/generated/check/errors/messages.ts @@ -1,5 +1,15 @@ +import { fileDriftStates } from '../drift/interfaces.js' + export const commandFailedMessage = 'Command failed' export const directoryRequiredMessage = 'Directory is required' export const fileRequiredMessage = 'File is required' + +export const filesOutdatedMessage = 'Generated files are outdated' + +export const fileDriftStateMessages = { + [fileDriftStates.different]: 'content differs', + [fileDriftStates.missing]: 'missing committed file', + [fileDriftStates.unexpected]: 'unexpected committed file', +}