Skip to content

Commit 0a658d9

Browse files
Pre generate the HTML markup for webcomponent icons
* Adjust the web component icons build to use contnet and attrs as well * Adjust the web component icons build to use contnet and attrs as well * run generate CMD * Adjust the web component icons generation process. Ensure that the svg tag is generated and placed in the icon component directly * Adjust the custom icons generation process. Ensure that the svg tag is generated and placed in the icon component directly * removing unused code * run ther generation CMD with the new logic * changelog * Add disclaimer that the Icon `SVG` tag is generated via @carbon/icons. * Remove unused packages * genrated the icons again * changelog * Update packages/icons-builder/src/templates.js Co-authored-by: felix-hcl <felix.muller@hcl.com> * generated icons again --------- Co-authored-by: felix-hcl <felix.muller@hcl.com>
1 parent 7da42af commit 0a658d9

File tree

1,916 files changed

+16074
-15802
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,916 files changed

+16074
-15802
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
### Fixed
88

99
### Changed
10+
- Adjusting the web component icon generation logic for carbon and custom icons. The `SVG` tag is now pre-generated and directly part for the Icon web component.
1011

1112
#### Breaking changes
1213

packages/icons-builder/src/generate.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
} from './utils/copyrightYears.js';
3333

3434
const carbonSourcePath = path.resolve(process.cwd(), 'node_modules/@carbon/icons/es');
35+
const carbonPkgJsonPath = path.resolve(process.cwd(), 'node_modules/@carbon/icons/package.json');
3536
const carbonReactDestPath = path.resolve(process.cwd(), '../react/src/carbon/es');
3637
const carbonWcDestPath = path.resolve(process.cwd(), '../web-component/src/carbon/es');
3738

@@ -180,7 +181,7 @@ const processCustomIconDirectory = (
180181
? creationYear
181182
: `${creationYear}, ${lastModifiedYear}`;
182183

183-
const wcContent = createCustomWebComponentIcon(iconName, sizeInt, content, attrs, wcUtilsImportPath, finalCopyright);
184+
const wcContent = createCustomWebComponentIcon(iconName, content, attrs, wcUtilsImportPath, finalCopyright);
184185
ensureDirSync(wcIconDir);
185186
fs.writeFileSync(wcDestFile, wcContent);
186187
generatedPaths.wc.add(path.resolve(wcDestFile));
@@ -203,7 +204,7 @@ const processCustomIconDirectory = (
203204
}
204205
}
205206

206-
const buildIcons = () => {
207+
const buildIcons = async () => {
207208
console.log('START - Generating icons...');
208209

209210
const counters = {
@@ -231,6 +232,7 @@ const buildIcons = () => {
231232

232233
// Generate Carbon Icons
233234
console.log('Generating Carbon icons...');
235+
const carbonVersion = JSON.parse(fs.readFileSync(carbonPkgJsonPath, 'utf8')).version;
234236
const files = fs.readdirSync(carbonSourcePath);
235237

236238
for (const originalName of files) {
@@ -264,11 +266,9 @@ const buildIcons = () => {
264266

265267
// Create web components
266268
if (!wcExcludes.has(originalName) && fs.existsSync(path.join(carbonSourcePath, originalName, '32.js'))) {
267-
let isRenamed = false;
268269
let iconName = originalName;
269270
if (wcRenames.has(originalName)) {
270271
iconName = wcRenames.get(originalName);
271-
isRenamed = true;
272272
}
273273

274274
try {
@@ -279,7 +279,11 @@ const buildIcons = () => {
279279
const existingYear = getCopyrightYear(indexFile, null);
280280
const year = existingYear || new Date().getFullYear().toString();
281281

282-
const wcContent = createCarbonWebComponentIcon(iconName, 32, originalName, year);
282+
// Read carbon icon descriptor at build time to pre-render SVG markup
283+
const carbonIconModule = await import(`@carbon/icons/es/${originalName}/32.js`);
284+
const iconDescriptor = carbonIconModule.default;
285+
286+
const wcContent = createCarbonWebComponentIcon(iconName, iconDescriptor, year, carbonVersion);
283287
ensureDirSync(wcFilePath);
284288
fs.writeFileSync(indexFile, wcContent);
285289

packages/icons-builder/src/templates.js

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,64 @@ export default createSvgIcon(Icon.name, Icon.size, Icon.content, Icon.attrs);
6464
};
6565

6666
/**
67-
* Creates a Web Component carbon icon template.
67+
* Converts a carbon icon descriptor attribute name to kebab-case for SVG output.
68+
* Preserves viewBox and xmlns as-is.
69+
* @param {string} str - The attribute name to convert.
70+
* @returns {string} The kebab-case attribute name.
71+
*/
72+
const toKebabCase = (str) => {
73+
if (str === 'viewBox' || str === 'xmlns' || str === 'preserveAspectRatio') {
74+
return str;
75+
}
76+
return str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();
77+
};
78+
79+
/**
80+
* Formats SVG attributes as an HTML attribute string.
81+
* @param {object} attrs - The attributes object.
82+
* @returns {string} The formatted attribute string.
83+
*/
84+
const formatSvgAttributes = (attrs) => {
85+
return Object.entries(attrs)
86+
.map(([key, value]) => `${toKebabCase(key)}="${value}"`)
87+
.join(' ');
88+
};
89+
90+
/**
91+
* Recursively renders carbon icon content elements as an SVG markup string.
92+
* @param {Array} content - The content array from the carbon icon descriptor.
93+
* @returns {string} The SVG child elements as a markup string.
94+
*/
95+
const renderContentElements = (content) => {
96+
return content.map(item => {
97+
const attrsStr = item.attrs ? formatSvgAttributes(item.attrs) : '';
98+
const children = item.content ? renderContentElements(item.content) : '';
99+
if (children) {
100+
return `<${item.elem}${attrsStr ? ' ' + attrsStr : ''}>${children}</${item.elem}>`;
101+
}
102+
return `<${item.elem}${attrsStr ? ' ' + attrsStr : ''} />`;
103+
}).join('');
104+
};
105+
106+
/**
107+
* Creates a Web Component carbon icon template with pre-rendered SVG markup.
108+
* The SVG is generated at build time, eliminating runtime dependencies on
109+
* @carbon/icons and @carbon/icon-helpers.
68110
* @param {string} iconName - The name of the icon.
69-
* @param {string} size - The size of the icon.
70-
* @param {string} originalName - The original name of the icon.
111+
* @param {object} iconDescriptor - The full carbon icon descriptor ({ elem, attrs, content }).
71112
* @param {string} copyrightYear - The copyright year.
113+
* @param {string} carbonVersion - The version of @carbon/icons used to generate the SVG.
72114
* @returns {string} The Web Component icon template.
73115
*/
74-
export const createCarbonWebComponentIcon = (iconName, size, originalName, copyrightYear) => {
116+
export const createCarbonWebComponentIcon = (iconName, iconDescriptor, copyrightYear, carbonVersion) => {
75117
const iconNameConstant = `icon-${iconName.toLowerCase().replace(/-+/g, '-')}`;
76118
const copyrightLine = formatCopyrightLine(copyrightYear);
119+
120+
// Build SVG attributes, adding preserveAspectRatio
121+
const svgAttrs = { ...iconDescriptor.attrs, preserveAspectRatio: 'xMidYMid' };
122+
const svgAttrsStr = formatSvgAttributes(svgAttrs);
123+
const svgChildren = renderContentElements(iconDescriptor.content);
124+
77125
return `/* ======================================================================== *
78126
${copyrightLine}
79127
* Licensed under the Apache License, Version 2.0 (the "License"); *
@@ -91,22 +139,26 @@ ${copyrightLine}
91139
92140
/* auto generated file - do not edit */
93141
import { html } from 'lit';
94-
import Icon from '@carbon/icons/es/${originalName}/${size}';
95-
import { toSVG } from '@carbon/icon-helpers';
96142
import { BaseIcon } from '../../../utils/base-icon';
97143
import { canDefine } from '../../../utils';
98144
import { ICON_PREFIX } from '../../../utils/tags';
99145
100146
export const ICON_NAME = \`\${ICON_PREFIX}${iconNameConstant}\`;
101147
export class WebComponentIcon extends BaseIcon {
102148
render() {
103-
return html\`\${toSVG({...Icon, attrs: { ...Icon.attrs, preserveAspectRatio: 'xMidYMid'}})}\`;
149+
/**
150+
* The following HTML markup was generated using @carbon/icons ${carbonVersion}.
151+
* @carbon/icons is licensed under Apache 2.0.
152+
* Source: https://github.com/carbon-design-system/carbon
153+
*/
154+
return html\`<svg ${svgAttrsStr}>${svgChildren}</svg>\`;
104155
}
105-
}
156+
}
106157
107158
if (canDefine && !customElements.get(ICON_NAME)) {
108159
customElements.define(ICON_NAME, WebComponentIcon);
109160
}
161+
110162
declare global {
111163
interface HTMLElementTagNameMap {
112164
[ICON_NAME]: WebComponentIcon;
@@ -160,26 +212,22 @@ export default createSvgIcon('${iconName}', ${size}, content, attrs);
160212
};
161213

162214
/**
163-
* Creates a custom Web Component icon template.
215+
* Creates a custom Web Component icon template with pre-rendered SVG markup.
216+
* The SVG is generated at build time, eliminating runtime createSvgIcon calls.
164217
* @param {string} iconName - The name of the icon.
165-
* @param {string} size - The size of the icon.
166-
* @param {object} content - The content of the icon.
167-
* @param {object} attrs - The attributes of the icon.
168-
* @param {string} utilsImportPath - The path to the utils import.
218+
* @param {object} content - The content array from the parsed SVG.
219+
* @param {object} attrs - The SVG attributes from the parsed SVG.
220+
* @param {string} utilsImportPath - The relative path to the utils directory.
169221
* @param {string} copyrightString - The copyright string.
170222
* @returns {string} The custom Web Component icon template.
171223
*/
172-
export const createCustomWebComponentIcon = (iconName, size, content, attrs, utilsImportPath, copyrightString) => {
224+
export const createCustomWebComponentIcon = (iconName, content, attrs, utilsImportPath, copyrightString) => {
173225
const iconNameConstant = `icon-${iconName.toLowerCase().replace(/-+/g, '-')}`;
226+
const copyrightLine = formatCopyrightLine(copyrightString);
174227

175-
const stringifyOptions = {
176-
indent: ' ',
177-
};
228+
const svgAttrsStr = formatSvgAttributes(attrs);
229+
const svgChildren = renderContentElements(content);
178230

179-
const attrsString = stringifyObject(attrs, stringifyOptions);
180-
let contentString = stringifyObject(content, stringifyOptions);
181-
const copyrightLine = formatCopyrightLine(copyrightString);
182-
183231
return`/* ======================================================================== *
184232
${copyrightLine}
185233
* Licensed under the Apache License, Version 2.0 (the "License"); *
@@ -197,18 +245,14 @@ ${copyrightLine}
197245
198246
/* auto generated file - do not edit */
199247
import { html } from 'lit';
200-
import { createSvgIcon, IIconAttrs, IIconContent, canDefine } from '${utilsImportPath}';
201248
import { BaseIcon } from '${utilsImportPath}/base-icon';
249+
import { canDefine } from '${utilsImportPath}';
202250
import { ICON_PREFIX } from '${utilsImportPath}/tags';
203251
204-
const attrs: IIconAttrs = ${attrsString};
205-
206-
const content: IIconContent[] = ${contentString};
207-
208252
export const ICON_NAME = \`\${ICON_PREFIX}${iconNameConstant}\`;
209253
export class WebComponentIcon extends BaseIcon {
210254
render() {
211-
return html\`\${createSvgIcon(content, attrs)}\`;
255+
return html\`<svg ${svgAttrsStr}>${svgChildren}</svg>\`;
212256
}
213257
}
214258

packages/web-component/package-lock.json

Lines changed: 0 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/web-component/package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@
6262
"vite": "^6.3.3"
6363
},
6464
"dependencies": {
65-
"@carbon/icon-helpers": "^10.58.0",
66-
"@carbon/icons": "11.27.0",
6765
"@types/debug": "^4.1.12",
6866
"debug": "^4.4.3"
6967
},

packages/web-component/src/apps/es/Sort--descending--alt/index.ts

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,58 +15,14 @@
1515

1616
/* auto generated file - do not edit */
1717
import { html } from 'lit';
18-
import { createSvgIcon, IIconAttrs, IIconContent, canDefine } from '../../../utils';
1918
import { BaseIcon } from '../../../utils/base-icon';
19+
import { canDefine } from '../../../utils';
2020
import { ICON_PREFIX } from '../../../utils/tags';
2121

22-
const attrs: IIconAttrs = {
23-
xmlns: 'http://www.w3.org/2000/svg',
24-
viewBox: '0 0 32 32',
25-
width: 32,
26-
height: 32
27-
};
28-
29-
const content: IIconContent[] = [
30-
{
31-
elem: 'path',
32-
attrs: {
33-
d: 'M18 22L19.414 20.586L23 24.172V4H25V24.172L28.586 20.586L30 22L24 28L18 22Z',
34-
fill: 'currentColor',
35-
stroke: 'none',
36-
strokeLinejoin: 'round',
37-
strokeMiterlimit: 10
38-
}
39-
},
40-
{
41-
elem: 'path',
42-
attrs: {
43-
d: 'M16 6H2V8H16V6Z',
44-
fill: 'currentColor',
45-
stroke: 'none'
46-
}
47-
},
48-
{
49-
elem: 'path',
50-
attrs: {
51-
d: 'M12 12H2V14H12V12Z',
52-
fill: 'currentColor',
53-
stroke: 'none'
54-
}
55-
},
56-
{
57-
elem: 'path',
58-
attrs: {
59-
d: 'M8 18H2V20H8V18Z',
60-
fill: 'currentColor',
61-
stroke: 'none'
62-
}
63-
}
64-
];
65-
6622
export const ICON_NAME = `${ICON_PREFIX}icon-sort-descending-alt`;
6723
export class WebComponentIcon extends BaseIcon {
6824
render() {
69-
return html`${createSvgIcon(content, attrs)}`;
25+
return html`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32"><path d="M18 22L19.414 20.586L23 24.172V4H25V24.172L28.586 20.586L30 22L24 28L18 22Z" fill="currentColor" stroke="none" stroke-linejoin="round" stroke-miterlimit="10" /><path d="M16 6H2V8H16V6Z" fill="currentColor" stroke="none" /><path d="M12 12H2V14H12V12Z" fill="currentColor" stroke="none" /><path d="M8 18H2V20H8V18Z" fill="currentColor" stroke="none" /></svg>`;
7026
}
7127
}
7228

0 commit comments

Comments
 (0)