@zeus-js/output-icons 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,421 @@
1
+ /**
2
+ * output-icons v0.1.0-beta.0
3
+ * (c) 2026 baicie
4
+ * Released under the MIT License.
5
+ **/
6
+ Object.defineProperties(exports, {
7
+ __esModule: { value: true },
8
+ [Symbol.toStringTag]: { value: "Module" }
9
+ });
10
+ //#region \0rolldown/runtime.js
11
+ var __create = Object.create;
12
+ var __defProp = Object.defineProperty;
13
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
14
+ var __getOwnPropNames = Object.getOwnPropertyNames;
15
+ var __getProtoOf = Object.getPrototypeOf;
16
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
19
+ key = keys[i];
20
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
21
+ get: ((k) => from[k]).bind(null, key),
22
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
23
+ });
24
+ }
25
+ return to;
26
+ };
27
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
28
+ value: mod,
29
+ enumerable: true
30
+ }) : target, mod));
31
+ //#endregion
32
+ let node_path = require("node:path");
33
+ node_path = __toESM(node_path, 1);
34
+ //#region packages/web-c/output-icons/src/generateDts.ts
35
+ function generateReactDts(icons) {
36
+ const lines = [];
37
+ lines.push(`import type * as React from 'react'`);
38
+ lines.push("");
39
+ lines.push(`export interface IconProps extends React.SVGAttributes<SVGSVGElement> {`);
40
+ lines.push(` size?: string | number`);
41
+ lines.push(` title?: string`);
42
+ lines.push(`}`);
43
+ lines.push("");
44
+ for (const icon of icons) lines.push(`export declare const ${icon.componentName}: React.ForwardRefExoticComponent<IconProps & React.RefAttributes<SVGSVGElement>>`);
45
+ lines.push("");
46
+ return lines.join("\n");
47
+ }
48
+ function generateVueDts(icons) {
49
+ const lines = [];
50
+ lines.push(`import type { DefineComponent } from 'vue'`);
51
+ lines.push("");
52
+ lines.push(`export interface IconProps {`);
53
+ lines.push(` size?: string | number`);
54
+ lines.push(` title?: string`);
55
+ lines.push(`}`);
56
+ lines.push("");
57
+ for (const icon of icons) lines.push(`export declare const ${icon.componentName}: DefineComponent<IconProps>`);
58
+ lines.push("");
59
+ return lines.join("\n");
60
+ }
61
+ function generateStaticWcDts(icons) {
62
+ const lines = [];
63
+ for (const icon of icons) {
64
+ const className = `${icon.componentName}Element`;
65
+ lines.push(`export interface ${className} extends HTMLElement {`);
66
+ lines.push(` size?: string`);
67
+ lines.push(` label?: string`);
68
+ lines.push(`}`);
69
+ lines.push("");
70
+ lines.push(`export declare const ${icon.componentName}: {`);
71
+ lines.push(` new (): ${className}`);
72
+ lines.push(`}`);
73
+ lines.push("");
74
+ }
75
+ lines.push("declare global {");
76
+ lines.push(" interface HTMLElementTagNameMap {");
77
+ for (const icon of icons) lines.push(` ${JSON.stringify(icon.wcTag)}: ${icon.componentName}Element`);
78
+ lines.push(" }");
79
+ lines.push("}");
80
+ lines.push("");
81
+ lines.push("export {}");
82
+ lines.push("");
83
+ return lines.join("\n");
84
+ }
85
+ //#endregion
86
+ //#region packages/web-c/output-icons/src/svg.ts
87
+ function parseSvg(source) {
88
+ var _svgMatch$, _svgMatch$2$trim, _svgMatch$2, _ref, _readAttribute;
89
+ const svgMatch = sanitizeSvg(source).match(/<svg\b([^>]*)>([\s\S]*?)<\/svg>/i);
90
+ if (!svgMatch) throw new Error("Invalid SVG source. Expected <svg>...</svg>.");
91
+ const attrs = (_svgMatch$ = svgMatch[1]) !== null && _svgMatch$ !== void 0 ? _svgMatch$ : "";
92
+ const innerSvg = (_svgMatch$2$trim = (_svgMatch$2 = svgMatch[2]) === null || _svgMatch$2 === void 0 ? void 0 : _svgMatch$2.trim()) !== null && _svgMatch$2$trim !== void 0 ? _svgMatch$2$trim : "";
93
+ return {
94
+ viewBox: (_ref = (_readAttribute = readAttribute(attrs, "viewBox")) !== null && _readAttribute !== void 0 ? _readAttribute : readAttribute(attrs, "viewbox")) !== null && _ref !== void 0 ? _ref : "0 0 24 24",
95
+ innerSvg
96
+ };
97
+ }
98
+ function sanitizeSvg(source) {
99
+ return source.replace(/<script\b[\s\S]*?<\/script>/gi, "").replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, "").replace(/\son[a-z]+\s*=\s*'[^']*'/gi, "").replace(/\son[a-z]+\s*=\s*\{[^}]*\}/gi, "");
100
+ }
101
+ function readAttribute(attrs, name) {
102
+ const doubleQuote = new RegExp(`${name}\\s*=\\s*"([^"]*)"`, "i").exec(attrs);
103
+ if (doubleQuote) return doubleQuote[1];
104
+ const singleQuote = new RegExp(`${name}\\s*=\\s*'([^']*)'`, "i").exec(attrs);
105
+ if (singleQuote) return singleQuote[1];
106
+ }
107
+ function escapeTemplateLiteral(value) {
108
+ return value.replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
109
+ }
110
+ function svgToDataSource(svg) {
111
+ return svg.trim();
112
+ }
113
+ //#endregion
114
+ //#region packages/web-c/output-icons/src/generateReactIcon.ts
115
+ function generateReactIcon(icon) {
116
+ const inner = escapeTemplateLiteral(icon.innerSvg);
117
+ return `
118
+ import React from 'react';
119
+
120
+ export const ${icon.componentName} = React.forwardRef(function ${icon.componentName}(props, ref) {
121
+ const {
122
+ size = '1em',
123
+ title,
124
+ children,
125
+ ...rest
126
+ } = props;
127
+
128
+ const content = children != null
129
+ ? children
130
+ : React.createElement('g', {
131
+ dangerouslySetInnerHTML: { __html: \`${inner}\` },
132
+ });
133
+
134
+ return React.createElement(
135
+ 'svg',
136
+ {
137
+ ...rest,
138
+ ref,
139
+ width: size,
140
+ height: size,
141
+ viewBox: ${JSON.stringify(icon.viewBox)},
142
+ fill: 'none',
143
+ xmlns: 'http://www.w3.org/2000/svg',
144
+ 'aria-hidden': title ? undefined : true,
145
+ role: title ? 'img' : undefined,
146
+ },
147
+ title ? React.createElement('title', null, title) : null,
148
+ content,
149
+ );
150
+ });
151
+ `.trimStart();
152
+ }
153
+ function generateReactIndex(icons) {
154
+ return `${icons.map((icon) => {
155
+ return `export { ${icon.componentName} } from './${icon.name}.js';`;
156
+ }).join("\n")}\n`;
157
+ }
158
+ //#endregion
159
+ //#region packages/web-c/output-icons/src/generateStaticWcIcon.ts
160
+ function generateStaticWcIcon(icon) {
161
+ const inner = escapeTemplateLiteral(icon.innerSvg);
162
+ const className = `${icon.componentName}Element`;
163
+ return `
164
+ const INNER_SVG = \`${inner}\`;
165
+ const VIEW_BOX = ${JSON.stringify(icon.viewBox)};
166
+
167
+ export class ${className} extends HTMLElement {
168
+ static get observedAttributes() {
169
+ return ['size', 'label'];
170
+ }
171
+
172
+ connectedCallback() {
173
+ this.render();
174
+ }
175
+
176
+ attributeChangedCallback() {
177
+ this.render();
178
+ }
179
+
180
+ get size() {
181
+ return this.getAttribute('size') || '1em';
182
+ }
183
+
184
+ set size(value) {
185
+ if (value == null) {
186
+ this.removeAttribute('size');
187
+ } else {
188
+ this.setAttribute('size', String(value));
189
+ }
190
+ }
191
+
192
+ get label() {
193
+ return this.getAttribute('label') || '';
194
+ }
195
+
196
+ set label(value) {
197
+ if (value == null || value === '') {
198
+ this.removeAttribute('label');
199
+ } else {
200
+ this.setAttribute('label', String(value));
201
+ }
202
+ }
203
+
204
+ render() {
205
+ const size = this.size;
206
+ const label = this.label;
207
+
208
+ this.innerHTML =
209
+ '<svg' +
210
+ ' part="root"' +
211
+ ' width="' + escapeHtml(size) + '"' +
212
+ ' height="' + escapeHtml(size) + '"' +
213
+ ' viewBox="' + escapeHtml(VIEW_BOX) + '"' +
214
+ ' xmlns="http://www.w3.org/2000/svg"' +
215
+ (label ? ' role="img" aria-label="' + escapeHtml(label) + '"' : ' aria-hidden="true"') +
216
+ '>' +
217
+ INNER_SVG +
218
+ '</svg>';
219
+ }
220
+ }
221
+
222
+ if (!customElements.get(${JSON.stringify(icon.wcTag)})) {
223
+ customElements.define(${JSON.stringify(icon.wcTag)}, ${className});
224
+ }
225
+
226
+ export const ${icon.componentName} = ${className};
227
+
228
+ function escapeHtml(value) {
229
+ return String(value)
230
+ .replace(/&/g, '&amp;')
231
+ .replace(/"/g, '&quot;')
232
+ .replace(/</g, '&lt;')
233
+ .replace(/>/g, '&gt;');
234
+ }
235
+ `.trimStart();
236
+ }
237
+ function generateStaticWcIndex(icons) {
238
+ return `${icons.map((icon) => {
239
+ return `export { ${icon.componentName} } from './${icon.name}.js';`;
240
+ }).join("\n")}\n`;
241
+ }
242
+ //#endregion
243
+ //#region packages/web-c/output-icons/src/generateVueIcon.ts
244
+ function generateVueIcon(icon) {
245
+ const inner = escapeTemplateLiteral(icon.innerSvg);
246
+ return `
247
+ import { defineComponent, h } from 'vue';
248
+
249
+ export const ${icon.componentName} = defineComponent({
250
+ name: ${JSON.stringify(icon.componentName)},
251
+
252
+ props: {
253
+ size: {
254
+ type: [String, Number],
255
+ default: '1em',
256
+ },
257
+ title: {
258
+ type: String,
259
+ default: undefined,
260
+ },
261
+ },
262
+
263
+ setup(props, { attrs, slots }) {
264
+ return () => {
265
+ const children = [];
266
+
267
+ if (props.title) {
268
+ children.push(h('title', null, props.title));
269
+ }
270
+
271
+ if (slots.default) {
272
+ children.push(...slots.default());
273
+ }
274
+
275
+ return h(
276
+ 'svg',
277
+ {
278
+ ...attrs,
279
+ width: props.size,
280
+ height: props.size,
281
+ viewBox: ${JSON.stringify(icon.viewBox)},
282
+ xmlns: 'http://www.w3.org/2000/svg',
283
+ 'aria-hidden': props.title ? undefined : true,
284
+ role: props.title ? 'img' : undefined,
285
+ innerHTML: slots.default ? undefined : \`${inner}\`,
286
+ },
287
+ children,
288
+ );
289
+ };
290
+ },
291
+ });
292
+ `.trimStart();
293
+ }
294
+ function generateVueIndex(icons) {
295
+ return `${icons.map((icon) => {
296
+ return `export { ${icon.componentName} } from './${icon.name}.js';`;
297
+ }).join("\n")}\n`;
298
+ }
299
+ //#endregion
300
+ //#region packages/web-c/output-icons/src/naming.ts
301
+ function toPascalCase(value) {
302
+ return value.split(/[-_\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("") || "Icon";
303
+ }
304
+ function toIconComponentName(name) {
305
+ const pascal = toPascalCase(name);
306
+ return pascal.endsWith("Icon") ? pascal : `${pascal}Icon`;
307
+ }
308
+ function sanitizeFileName(value) {
309
+ return value.trim().replace(/[^a-zA-Z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
310
+ }
311
+ function getIconJsFileName(name) {
312
+ return `${sanitizeFileName(name)}.js`;
313
+ }
314
+ function getIconSvgFileName(name) {
315
+ return `${sanitizeFileName(name)}.svg`;
316
+ }
317
+ //#endregion
318
+ //#region packages/web-c/output-icons/src/index.ts
319
+ function icons(options) {
320
+ const normalized = normalizeOptions(options);
321
+ return {
322
+ name: "zeus-output-icons",
323
+ virtualModules() {
324
+ const modules = [];
325
+ if (normalized.react) {
326
+ for (const icon of normalized.icons) modules.push({
327
+ id: `zeus:icons:react:${icon.name}`,
328
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.react.outDir, getIconJsFileName(icon.name)),
329
+ code: generateReactIcon(icon)
330
+ });
331
+ modules.push({
332
+ id: "zeus:icons:react:index",
333
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.react.outDir, "index.js"),
334
+ code: generateReactIndex(normalized.icons)
335
+ });
336
+ }
337
+ if (normalized.vue) {
338
+ for (const icon of normalized.icons) modules.push({
339
+ id: `zeus:icons:vue:${icon.name}`,
340
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.vue.outDir, getIconJsFileName(icon.name)),
341
+ code: generateVueIcon(icon)
342
+ });
343
+ modules.push({
344
+ id: "zeus:icons:vue:index",
345
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.vue.outDir, "index.js"),
346
+ code: generateVueIndex(normalized.icons)
347
+ });
348
+ }
349
+ if (normalized.wc) {
350
+ for (const icon of normalized.icons) modules.push({
351
+ id: `zeus:icons:wc:${icon.name}`,
352
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.wc.outDir, getIconJsFileName(icon.name)),
353
+ code: generateStaticWcIcon(icon)
354
+ });
355
+ modules.push({
356
+ id: "zeus:icons:wc:index",
357
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.wc.outDir, "index.js"),
358
+ code: generateStaticWcIndex(normalized.icons)
359
+ });
360
+ }
361
+ return modules;
362
+ },
363
+ generateBundle() {
364
+ const files = [];
365
+ if (normalized.svg) for (const icon of normalized.icons) files.push({
366
+ type: "asset",
367
+ fileName: node_path.default.posix.join(normalized.outDir, "svg", getIconSvgFileName(icon.name)),
368
+ source: svgToDataSource(icon.svg)
369
+ });
370
+ if (normalized.dts && normalized.react) files.push({
371
+ type: "asset",
372
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.react.outDir, "index.d.ts"),
373
+ source: generateReactDts(normalized.icons)
374
+ });
375
+ if (normalized.dts && normalized.vue) files.push({
376
+ type: "asset",
377
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.vue.outDir, "index.d.ts"),
378
+ source: generateVueDts(normalized.icons)
379
+ });
380
+ if (normalized.dts && normalized.wc) files.push({
381
+ type: "asset",
382
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.wc.outDir, "index.d.ts"),
383
+ source: generateStaticWcDts(normalized.icons)
384
+ });
385
+ return files;
386
+ }
387
+ };
388
+ }
389
+ function normalizeOptions(options) {
390
+ var _options$icons, _options$outDir, _options$svg, _options$dts;
391
+ if (!((_options$icons = options.icons) === null || _options$icons === void 0 ? void 0 : _options$icons.length)) throw new Error("[zeus-output-icons] options.icons is required.");
392
+ const wcOptions = options.wc === false ? false : {
393
+ outDir: typeof options.wc === "object" && options.wc.outDir ? options.wc.outDir : "wc",
394
+ tagPrefix: typeof options.wc === "object" && options.wc.tagPrefix ? options.wc.tagPrefix : "z-icon-"
395
+ };
396
+ return {
397
+ icons: normalizeIcons(options.icons, wcOptions ? wcOptions.tagPrefix : "z-icon-"),
398
+ outDir: (_options$outDir = options.outDir) !== null && _options$outDir !== void 0 ? _options$outDir : "icons",
399
+ svg: (_options$svg = options.svg) !== null && _options$svg !== void 0 ? _options$svg : true,
400
+ react: options.react === false ? false : { outDir: typeof options.react === "object" && options.react.outDir ? options.react.outDir : "react" },
401
+ vue: options.vue === false ? false : { outDir: typeof options.vue === "object" && options.vue.outDir ? options.vue.outDir : "vue" },
402
+ wc: wcOptions,
403
+ dts: (_options$dts = options.dts) !== null && _options$dts !== void 0 ? _options$dts : true
404
+ };
405
+ }
406
+ function normalizeIcons(icons, tagPrefix) {
407
+ return icons.map((icon) => {
408
+ const parsed = parseSvg(icon.svg);
409
+ return {
410
+ name: icon.name,
411
+ componentName: toIconComponentName(icon.name),
412
+ wcTag: `${tagPrefix}${icon.name}`,
413
+ svg: icon.svg,
414
+ title: icon.title,
415
+ viewBox: parsed.viewBox,
416
+ innerSvg: parsed.innerSvg
417
+ };
418
+ });
419
+ }
420
+ //#endregion
421
+ exports.default = icons;
@@ -0,0 +1,421 @@
1
+ /**
2
+ * output-icons v0.1.0-beta.0
3
+ * (c) 2026 baicie
4
+ * Released under the MIT License.
5
+ **/
6
+ Object.defineProperties(exports, {
7
+ __esModule: { value: true },
8
+ [Symbol.toStringTag]: { value: "Module" }
9
+ });
10
+ //#region \0rolldown/runtime.js
11
+ var __create = Object.create;
12
+ var __defProp = Object.defineProperty;
13
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
14
+ var __getOwnPropNames = Object.getOwnPropertyNames;
15
+ var __getProtoOf = Object.getPrototypeOf;
16
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
19
+ key = keys[i];
20
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
21
+ get: ((k) => from[k]).bind(null, key),
22
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
23
+ });
24
+ }
25
+ return to;
26
+ };
27
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
28
+ value: mod,
29
+ enumerable: true
30
+ }) : target, mod));
31
+ //#endregion
32
+ let node_path = require("node:path");
33
+ node_path = __toESM(node_path, 1);
34
+ //#region packages/web-c/output-icons/src/generateDts.ts
35
+ function generateReactDts(icons) {
36
+ const lines = [];
37
+ lines.push(`import type * as React from 'react'`);
38
+ lines.push("");
39
+ lines.push(`export interface IconProps extends React.SVGAttributes<SVGSVGElement> {`);
40
+ lines.push(` size?: string | number`);
41
+ lines.push(` title?: string`);
42
+ lines.push(`}`);
43
+ lines.push("");
44
+ for (const icon of icons) lines.push(`export declare const ${icon.componentName}: React.ForwardRefExoticComponent<IconProps & React.RefAttributes<SVGSVGElement>>`);
45
+ lines.push("");
46
+ return lines.join("\n");
47
+ }
48
+ function generateVueDts(icons) {
49
+ const lines = [];
50
+ lines.push(`import type { DefineComponent } from 'vue'`);
51
+ lines.push("");
52
+ lines.push(`export interface IconProps {`);
53
+ lines.push(` size?: string | number`);
54
+ lines.push(` title?: string`);
55
+ lines.push(`}`);
56
+ lines.push("");
57
+ for (const icon of icons) lines.push(`export declare const ${icon.componentName}: DefineComponent<IconProps>`);
58
+ lines.push("");
59
+ return lines.join("\n");
60
+ }
61
+ function generateStaticWcDts(icons) {
62
+ const lines = [];
63
+ for (const icon of icons) {
64
+ const className = `${icon.componentName}Element`;
65
+ lines.push(`export interface ${className} extends HTMLElement {`);
66
+ lines.push(` size?: string`);
67
+ lines.push(` label?: string`);
68
+ lines.push(`}`);
69
+ lines.push("");
70
+ lines.push(`export declare const ${icon.componentName}: {`);
71
+ lines.push(` new (): ${className}`);
72
+ lines.push(`}`);
73
+ lines.push("");
74
+ }
75
+ lines.push("declare global {");
76
+ lines.push(" interface HTMLElementTagNameMap {");
77
+ for (const icon of icons) lines.push(` ${JSON.stringify(icon.wcTag)}: ${icon.componentName}Element`);
78
+ lines.push(" }");
79
+ lines.push("}");
80
+ lines.push("");
81
+ lines.push("export {}");
82
+ lines.push("");
83
+ return lines.join("\n");
84
+ }
85
+ //#endregion
86
+ //#region packages/web-c/output-icons/src/svg.ts
87
+ function parseSvg(source) {
88
+ var _svgMatch$, _svgMatch$2$trim, _svgMatch$2, _ref, _readAttribute;
89
+ const svgMatch = sanitizeSvg(source).match(/<svg\b([^>]*)>([\s\S]*?)<\/svg>/i);
90
+ if (!svgMatch) throw new Error("Invalid SVG source. Expected <svg>...</svg>.");
91
+ const attrs = (_svgMatch$ = svgMatch[1]) !== null && _svgMatch$ !== void 0 ? _svgMatch$ : "";
92
+ const innerSvg = (_svgMatch$2$trim = (_svgMatch$2 = svgMatch[2]) === null || _svgMatch$2 === void 0 ? void 0 : _svgMatch$2.trim()) !== null && _svgMatch$2$trim !== void 0 ? _svgMatch$2$trim : "";
93
+ return {
94
+ viewBox: (_ref = (_readAttribute = readAttribute(attrs, "viewBox")) !== null && _readAttribute !== void 0 ? _readAttribute : readAttribute(attrs, "viewbox")) !== null && _ref !== void 0 ? _ref : "0 0 24 24",
95
+ innerSvg
96
+ };
97
+ }
98
+ function sanitizeSvg(source) {
99
+ return source.replace(/<script\b[\s\S]*?<\/script>/gi, "").replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, "").replace(/\son[a-z]+\s*=\s*'[^']*'/gi, "").replace(/\son[a-z]+\s*=\s*\{[^}]*\}/gi, "");
100
+ }
101
+ function readAttribute(attrs, name) {
102
+ const doubleQuote = new RegExp(`${name}\\s*=\\s*"([^"]*)"`, "i").exec(attrs);
103
+ if (doubleQuote) return doubleQuote[1];
104
+ const singleQuote = new RegExp(`${name}\\s*=\\s*'([^']*)'`, "i").exec(attrs);
105
+ if (singleQuote) return singleQuote[1];
106
+ }
107
+ function escapeTemplateLiteral(value) {
108
+ return value.replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
109
+ }
110
+ function svgToDataSource(svg) {
111
+ return svg.trim();
112
+ }
113
+ //#endregion
114
+ //#region packages/web-c/output-icons/src/generateReactIcon.ts
115
+ function generateReactIcon(icon) {
116
+ const inner = escapeTemplateLiteral(icon.innerSvg);
117
+ return `
118
+ import React from 'react';
119
+
120
+ export const ${icon.componentName} = React.forwardRef(function ${icon.componentName}(props, ref) {
121
+ const {
122
+ size = '1em',
123
+ title,
124
+ children,
125
+ ...rest
126
+ } = props;
127
+
128
+ const content = children != null
129
+ ? children
130
+ : React.createElement('g', {
131
+ dangerouslySetInnerHTML: { __html: \`${inner}\` },
132
+ });
133
+
134
+ return React.createElement(
135
+ 'svg',
136
+ {
137
+ ...rest,
138
+ ref,
139
+ width: size,
140
+ height: size,
141
+ viewBox: ${JSON.stringify(icon.viewBox)},
142
+ fill: 'none',
143
+ xmlns: 'http://www.w3.org/2000/svg',
144
+ 'aria-hidden': title ? undefined : true,
145
+ role: title ? 'img' : undefined,
146
+ },
147
+ title ? React.createElement('title', null, title) : null,
148
+ content,
149
+ );
150
+ });
151
+ `.trimStart();
152
+ }
153
+ function generateReactIndex(icons) {
154
+ return `${icons.map((icon) => {
155
+ return `export { ${icon.componentName} } from './${icon.name}.js';`;
156
+ }).join("\n")}\n`;
157
+ }
158
+ //#endregion
159
+ //#region packages/web-c/output-icons/src/generateStaticWcIcon.ts
160
+ function generateStaticWcIcon(icon) {
161
+ const inner = escapeTemplateLiteral(icon.innerSvg);
162
+ const className = `${icon.componentName}Element`;
163
+ return `
164
+ const INNER_SVG = \`${inner}\`;
165
+ const VIEW_BOX = ${JSON.stringify(icon.viewBox)};
166
+
167
+ export class ${className} extends HTMLElement {
168
+ static get observedAttributes() {
169
+ return ['size', 'label'];
170
+ }
171
+
172
+ connectedCallback() {
173
+ this.render();
174
+ }
175
+
176
+ attributeChangedCallback() {
177
+ this.render();
178
+ }
179
+
180
+ get size() {
181
+ return this.getAttribute('size') || '1em';
182
+ }
183
+
184
+ set size(value) {
185
+ if (value == null) {
186
+ this.removeAttribute('size');
187
+ } else {
188
+ this.setAttribute('size', String(value));
189
+ }
190
+ }
191
+
192
+ get label() {
193
+ return this.getAttribute('label') || '';
194
+ }
195
+
196
+ set label(value) {
197
+ if (value == null || value === '') {
198
+ this.removeAttribute('label');
199
+ } else {
200
+ this.setAttribute('label', String(value));
201
+ }
202
+ }
203
+
204
+ render() {
205
+ const size = this.size;
206
+ const label = this.label;
207
+
208
+ this.innerHTML =
209
+ '<svg' +
210
+ ' part="root"' +
211
+ ' width="' + escapeHtml(size) + '"' +
212
+ ' height="' + escapeHtml(size) + '"' +
213
+ ' viewBox="' + escapeHtml(VIEW_BOX) + '"' +
214
+ ' xmlns="http://www.w3.org/2000/svg"' +
215
+ (label ? ' role="img" aria-label="' + escapeHtml(label) + '"' : ' aria-hidden="true"') +
216
+ '>' +
217
+ INNER_SVG +
218
+ '</svg>';
219
+ }
220
+ }
221
+
222
+ if (!customElements.get(${JSON.stringify(icon.wcTag)})) {
223
+ customElements.define(${JSON.stringify(icon.wcTag)}, ${className});
224
+ }
225
+
226
+ export const ${icon.componentName} = ${className};
227
+
228
+ function escapeHtml(value) {
229
+ return String(value)
230
+ .replace(/&/g, '&amp;')
231
+ .replace(/"/g, '&quot;')
232
+ .replace(/</g, '&lt;')
233
+ .replace(/>/g, '&gt;');
234
+ }
235
+ `.trimStart();
236
+ }
237
+ function generateStaticWcIndex(icons) {
238
+ return `${icons.map((icon) => {
239
+ return `export { ${icon.componentName} } from './${icon.name}.js';`;
240
+ }).join("\n")}\n`;
241
+ }
242
+ //#endregion
243
+ //#region packages/web-c/output-icons/src/generateVueIcon.ts
244
+ function generateVueIcon(icon) {
245
+ const inner = escapeTemplateLiteral(icon.innerSvg);
246
+ return `
247
+ import { defineComponent, h } from 'vue';
248
+
249
+ export const ${icon.componentName} = defineComponent({
250
+ name: ${JSON.stringify(icon.componentName)},
251
+
252
+ props: {
253
+ size: {
254
+ type: [String, Number],
255
+ default: '1em',
256
+ },
257
+ title: {
258
+ type: String,
259
+ default: undefined,
260
+ },
261
+ },
262
+
263
+ setup(props, { attrs, slots }) {
264
+ return () => {
265
+ const children = [];
266
+
267
+ if (props.title) {
268
+ children.push(h('title', null, props.title));
269
+ }
270
+
271
+ if (slots.default) {
272
+ children.push(...slots.default());
273
+ }
274
+
275
+ return h(
276
+ 'svg',
277
+ {
278
+ ...attrs,
279
+ width: props.size,
280
+ height: props.size,
281
+ viewBox: ${JSON.stringify(icon.viewBox)},
282
+ xmlns: 'http://www.w3.org/2000/svg',
283
+ 'aria-hidden': props.title ? undefined : true,
284
+ role: props.title ? 'img' : undefined,
285
+ innerHTML: slots.default ? undefined : \`${inner}\`,
286
+ },
287
+ children,
288
+ );
289
+ };
290
+ },
291
+ });
292
+ `.trimStart();
293
+ }
294
+ function generateVueIndex(icons) {
295
+ return `${icons.map((icon) => {
296
+ return `export { ${icon.componentName} } from './${icon.name}.js';`;
297
+ }).join("\n")}\n`;
298
+ }
299
+ //#endregion
300
+ //#region packages/web-c/output-icons/src/naming.ts
301
+ function toPascalCase(value) {
302
+ return value.split(/[-_\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("") || "Icon";
303
+ }
304
+ function toIconComponentName(name) {
305
+ const pascal = toPascalCase(name);
306
+ return pascal.endsWith("Icon") ? pascal : `${pascal}Icon`;
307
+ }
308
+ function sanitizeFileName(value) {
309
+ return value.trim().replace(/[^a-zA-Z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
310
+ }
311
+ function getIconJsFileName(name) {
312
+ return `${sanitizeFileName(name)}.js`;
313
+ }
314
+ function getIconSvgFileName(name) {
315
+ return `${sanitizeFileName(name)}.svg`;
316
+ }
317
+ //#endregion
318
+ //#region packages/web-c/output-icons/src/index.ts
319
+ function icons(options) {
320
+ const normalized = normalizeOptions(options);
321
+ return {
322
+ name: "zeus-output-icons",
323
+ virtualModules() {
324
+ const modules = [];
325
+ if (normalized.react) {
326
+ for (const icon of normalized.icons) modules.push({
327
+ id: `zeus:icons:react:${icon.name}`,
328
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.react.outDir, getIconJsFileName(icon.name)),
329
+ code: generateReactIcon(icon)
330
+ });
331
+ modules.push({
332
+ id: "zeus:icons:react:index",
333
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.react.outDir, "index.js"),
334
+ code: generateReactIndex(normalized.icons)
335
+ });
336
+ }
337
+ if (normalized.vue) {
338
+ for (const icon of normalized.icons) modules.push({
339
+ id: `zeus:icons:vue:${icon.name}`,
340
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.vue.outDir, getIconJsFileName(icon.name)),
341
+ code: generateVueIcon(icon)
342
+ });
343
+ modules.push({
344
+ id: "zeus:icons:vue:index",
345
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.vue.outDir, "index.js"),
346
+ code: generateVueIndex(normalized.icons)
347
+ });
348
+ }
349
+ if (normalized.wc) {
350
+ for (const icon of normalized.icons) modules.push({
351
+ id: `zeus:icons:wc:${icon.name}`,
352
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.wc.outDir, getIconJsFileName(icon.name)),
353
+ code: generateStaticWcIcon(icon)
354
+ });
355
+ modules.push({
356
+ id: "zeus:icons:wc:index",
357
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.wc.outDir, "index.js"),
358
+ code: generateStaticWcIndex(normalized.icons)
359
+ });
360
+ }
361
+ return modules;
362
+ },
363
+ generateBundle() {
364
+ const files = [];
365
+ if (normalized.svg) for (const icon of normalized.icons) files.push({
366
+ type: "asset",
367
+ fileName: node_path.default.posix.join(normalized.outDir, "svg", getIconSvgFileName(icon.name)),
368
+ source: svgToDataSource(icon.svg)
369
+ });
370
+ if (normalized.dts && normalized.react) files.push({
371
+ type: "asset",
372
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.react.outDir, "index.d.ts"),
373
+ source: generateReactDts(normalized.icons)
374
+ });
375
+ if (normalized.dts && normalized.vue) files.push({
376
+ type: "asset",
377
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.vue.outDir, "index.d.ts"),
378
+ source: generateVueDts(normalized.icons)
379
+ });
380
+ if (normalized.dts && normalized.wc) files.push({
381
+ type: "asset",
382
+ fileName: node_path.default.posix.join(normalized.outDir, normalized.wc.outDir, "index.d.ts"),
383
+ source: generateStaticWcDts(normalized.icons)
384
+ });
385
+ return files;
386
+ }
387
+ };
388
+ }
389
+ function normalizeOptions(options) {
390
+ var _options$icons, _options$outDir, _options$svg, _options$dts;
391
+ if (!((_options$icons = options.icons) === null || _options$icons === void 0 ? void 0 : _options$icons.length)) throw new Error("[zeus-output-icons] options.icons is required.");
392
+ const wcOptions = options.wc === false ? false : {
393
+ outDir: typeof options.wc === "object" && options.wc.outDir ? options.wc.outDir : "wc",
394
+ tagPrefix: typeof options.wc === "object" && options.wc.tagPrefix ? options.wc.tagPrefix : "z-icon-"
395
+ };
396
+ return {
397
+ icons: normalizeIcons(options.icons, wcOptions ? wcOptions.tagPrefix : "z-icon-"),
398
+ outDir: (_options$outDir = options.outDir) !== null && _options$outDir !== void 0 ? _options$outDir : "icons",
399
+ svg: (_options$svg = options.svg) !== null && _options$svg !== void 0 ? _options$svg : true,
400
+ react: options.react === false ? false : { outDir: typeof options.react === "object" && options.react.outDir ? options.react.outDir : "react" },
401
+ vue: options.vue === false ? false : { outDir: typeof options.vue === "object" && options.vue.outDir ? options.vue.outDir : "vue" },
402
+ wc: wcOptions,
403
+ dts: (_options$dts = options.dts) !== null && _options$dts !== void 0 ? _options$dts : true
404
+ };
405
+ }
406
+ function normalizeIcons(icons, tagPrefix) {
407
+ return icons.map((icon) => {
408
+ const parsed = parseSvg(icon.svg);
409
+ return {
410
+ name: icon.name,
411
+ componentName: toIconComponentName(icon.name),
412
+ wcTag: `${tagPrefix}${icon.name}`,
413
+ svg: icon.svg,
414
+ title: icon.title,
415
+ viewBox: parsed.viewBox,
416
+ innerSvg: parsed.innerSvg
417
+ };
418
+ });
419
+ }
420
+ //#endregion
421
+ exports.default = icons;
@@ -0,0 +1,86 @@
1
+ import { ZeusComponentPlugin } from '@zeus-js/bundler-plugin';
2
+
3
+ export interface IconSource {
4
+ /**
5
+ * Icon name.
6
+ *
7
+ * Example:
8
+ * check
9
+ * chevron-down
10
+ */
11
+ name: string;
12
+ /**
13
+ * Full svg source.
14
+ */
15
+ svg: string;
16
+ /**
17
+ * Optional display name.
18
+ */
19
+ title?: string;
20
+ }
21
+ export interface OutputIconsOptions {
22
+ /**
23
+ * Icon source list.
24
+ *
25
+ * MVP 先支持内联 icons。
26
+ * 后续可以扩展 from: string | string[] 读取 svg 文件。
27
+ */
28
+ icons: IconSource[];
29
+ /**
30
+ * Output root.
31
+ *
32
+ * @default 'icons'
33
+ */
34
+ outDir?: string;
35
+ /**
36
+ * Emit raw svg files.
37
+ *
38
+ * @default true
39
+ */
40
+ svg?: boolean;
41
+ /**
42
+ * Emit React static icon components.
43
+ *
44
+ * @default true
45
+ */
46
+ react?: boolean | ReactIconOutputOptions;
47
+ /**
48
+ * Emit Vue static icon components.
49
+ *
50
+ * @default true
51
+ */
52
+ vue?: boolean | VueIconOutputOptions;
53
+ /**
54
+ * Emit static custom elements.
55
+ *
56
+ * @default false
57
+ */
58
+ wc?: boolean | StaticWcIconOutputOptions;
59
+ /**
60
+ * Whether to emit d.ts files.
61
+ *
62
+ * @default true
63
+ */
64
+ dts?: boolean;
65
+ }
66
+ interface ReactIconOutputOptions {
67
+ outDir?: string;
68
+ }
69
+ interface VueIconOutputOptions {
70
+ outDir?: string;
71
+ }
72
+ interface StaticWcIconOutputOptions {
73
+ outDir?: string;
74
+ /**
75
+ * Example:
76
+ * tagPrefix: 'z-icon-'
77
+ * check -> z-icon-check
78
+ *
79
+ * @default 'z-icon-'
80
+ */
81
+ tagPrefix?: string;
82
+ }
83
+
84
+ export declare function icons(options: OutputIconsOptions): ZeusComponentPlugin;
85
+
86
+ export { icons as default };
@@ -0,0 +1,394 @@
1
+ /**
2
+ * output-icons v0.1.0-beta.0
3
+ * (c) 2026 baicie
4
+ * Released under the MIT License.
5
+ **/
6
+ import path from "node:path";
7
+ //#region packages/web-c/output-icons/src/generateDts.ts
8
+ function generateReactDts(icons) {
9
+ const lines = [];
10
+ lines.push(`import type * as React from 'react'`);
11
+ lines.push("");
12
+ lines.push(`export interface IconProps extends React.SVGAttributes<SVGSVGElement> {`);
13
+ lines.push(` size?: string | number`);
14
+ lines.push(` title?: string`);
15
+ lines.push(`}`);
16
+ lines.push("");
17
+ for (const icon of icons) lines.push(`export declare const ${icon.componentName}: React.ForwardRefExoticComponent<IconProps & React.RefAttributes<SVGSVGElement>>`);
18
+ lines.push("");
19
+ return lines.join("\n");
20
+ }
21
+ function generateVueDts(icons) {
22
+ const lines = [];
23
+ lines.push(`import type { DefineComponent } from 'vue'`);
24
+ lines.push("");
25
+ lines.push(`export interface IconProps {`);
26
+ lines.push(` size?: string | number`);
27
+ lines.push(` title?: string`);
28
+ lines.push(`}`);
29
+ lines.push("");
30
+ for (const icon of icons) lines.push(`export declare const ${icon.componentName}: DefineComponent<IconProps>`);
31
+ lines.push("");
32
+ return lines.join("\n");
33
+ }
34
+ function generateStaticWcDts(icons) {
35
+ const lines = [];
36
+ for (const icon of icons) {
37
+ const className = `${icon.componentName}Element`;
38
+ lines.push(`export interface ${className} extends HTMLElement {`);
39
+ lines.push(` size?: string`);
40
+ lines.push(` label?: string`);
41
+ lines.push(`}`);
42
+ lines.push("");
43
+ lines.push(`export declare const ${icon.componentName}: {`);
44
+ lines.push(` new (): ${className}`);
45
+ lines.push(`}`);
46
+ lines.push("");
47
+ }
48
+ lines.push("declare global {");
49
+ lines.push(" interface HTMLElementTagNameMap {");
50
+ for (const icon of icons) lines.push(` ${JSON.stringify(icon.wcTag)}: ${icon.componentName}Element`);
51
+ lines.push(" }");
52
+ lines.push("}");
53
+ lines.push("");
54
+ lines.push("export {}");
55
+ lines.push("");
56
+ return lines.join("\n");
57
+ }
58
+ //#endregion
59
+ //#region packages/web-c/output-icons/src/svg.ts
60
+ function parseSvg(source) {
61
+ var _svgMatch$, _svgMatch$2$trim, _svgMatch$2, _ref, _readAttribute;
62
+ const svgMatch = sanitizeSvg(source).match(/<svg\b([^>]*)>([\s\S]*?)<\/svg>/i);
63
+ if (!svgMatch) throw new Error("Invalid SVG source. Expected <svg>...</svg>.");
64
+ const attrs = (_svgMatch$ = svgMatch[1]) !== null && _svgMatch$ !== void 0 ? _svgMatch$ : "";
65
+ const innerSvg = (_svgMatch$2$trim = (_svgMatch$2 = svgMatch[2]) === null || _svgMatch$2 === void 0 ? void 0 : _svgMatch$2.trim()) !== null && _svgMatch$2$trim !== void 0 ? _svgMatch$2$trim : "";
66
+ return {
67
+ viewBox: (_ref = (_readAttribute = readAttribute(attrs, "viewBox")) !== null && _readAttribute !== void 0 ? _readAttribute : readAttribute(attrs, "viewbox")) !== null && _ref !== void 0 ? _ref : "0 0 24 24",
68
+ innerSvg
69
+ };
70
+ }
71
+ function sanitizeSvg(source) {
72
+ return source.replace(/<script\b[\s\S]*?<\/script>/gi, "").replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, "").replace(/\son[a-z]+\s*=\s*'[^']*'/gi, "").replace(/\son[a-z]+\s*=\s*\{[^}]*\}/gi, "");
73
+ }
74
+ function readAttribute(attrs, name) {
75
+ const doubleQuote = new RegExp(`${name}\\s*=\\s*"([^"]*)"`, "i").exec(attrs);
76
+ if (doubleQuote) return doubleQuote[1];
77
+ const singleQuote = new RegExp(`${name}\\s*=\\s*'([^']*)'`, "i").exec(attrs);
78
+ if (singleQuote) return singleQuote[1];
79
+ }
80
+ function escapeTemplateLiteral(value) {
81
+ return value.replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
82
+ }
83
+ function svgToDataSource(svg) {
84
+ return svg.trim();
85
+ }
86
+ //#endregion
87
+ //#region packages/web-c/output-icons/src/generateReactIcon.ts
88
+ function generateReactIcon(icon) {
89
+ const inner = escapeTemplateLiteral(icon.innerSvg);
90
+ return `
91
+ import React from 'react';
92
+
93
+ export const ${icon.componentName} = React.forwardRef(function ${icon.componentName}(props, ref) {
94
+ const {
95
+ size = '1em',
96
+ title,
97
+ children,
98
+ ...rest
99
+ } = props;
100
+
101
+ const content = children != null
102
+ ? children
103
+ : React.createElement('g', {
104
+ dangerouslySetInnerHTML: { __html: \`${inner}\` },
105
+ });
106
+
107
+ return React.createElement(
108
+ 'svg',
109
+ {
110
+ ...rest,
111
+ ref,
112
+ width: size,
113
+ height: size,
114
+ viewBox: ${JSON.stringify(icon.viewBox)},
115
+ fill: 'none',
116
+ xmlns: 'http://www.w3.org/2000/svg',
117
+ 'aria-hidden': title ? undefined : true,
118
+ role: title ? 'img' : undefined,
119
+ },
120
+ title ? React.createElement('title', null, title) : null,
121
+ content,
122
+ );
123
+ });
124
+ `.trimStart();
125
+ }
126
+ function generateReactIndex(icons) {
127
+ return `${icons.map((icon) => {
128
+ return `export { ${icon.componentName} } from './${icon.name}.js';`;
129
+ }).join("\n")}\n`;
130
+ }
131
+ //#endregion
132
+ //#region packages/web-c/output-icons/src/generateStaticWcIcon.ts
133
+ function generateStaticWcIcon(icon) {
134
+ const inner = escapeTemplateLiteral(icon.innerSvg);
135
+ const className = `${icon.componentName}Element`;
136
+ return `
137
+ const INNER_SVG = \`${inner}\`;
138
+ const VIEW_BOX = ${JSON.stringify(icon.viewBox)};
139
+
140
+ export class ${className} extends HTMLElement {
141
+ static get observedAttributes() {
142
+ return ['size', 'label'];
143
+ }
144
+
145
+ connectedCallback() {
146
+ this.render();
147
+ }
148
+
149
+ attributeChangedCallback() {
150
+ this.render();
151
+ }
152
+
153
+ get size() {
154
+ return this.getAttribute('size') || '1em';
155
+ }
156
+
157
+ set size(value) {
158
+ if (value == null) {
159
+ this.removeAttribute('size');
160
+ } else {
161
+ this.setAttribute('size', String(value));
162
+ }
163
+ }
164
+
165
+ get label() {
166
+ return this.getAttribute('label') || '';
167
+ }
168
+
169
+ set label(value) {
170
+ if (value == null || value === '') {
171
+ this.removeAttribute('label');
172
+ } else {
173
+ this.setAttribute('label', String(value));
174
+ }
175
+ }
176
+
177
+ render() {
178
+ const size = this.size;
179
+ const label = this.label;
180
+
181
+ this.innerHTML =
182
+ '<svg' +
183
+ ' part="root"' +
184
+ ' width="' + escapeHtml(size) + '"' +
185
+ ' height="' + escapeHtml(size) + '"' +
186
+ ' viewBox="' + escapeHtml(VIEW_BOX) + '"' +
187
+ ' xmlns="http://www.w3.org/2000/svg"' +
188
+ (label ? ' role="img" aria-label="' + escapeHtml(label) + '"' : ' aria-hidden="true"') +
189
+ '>' +
190
+ INNER_SVG +
191
+ '</svg>';
192
+ }
193
+ }
194
+
195
+ if (!customElements.get(${JSON.stringify(icon.wcTag)})) {
196
+ customElements.define(${JSON.stringify(icon.wcTag)}, ${className});
197
+ }
198
+
199
+ export const ${icon.componentName} = ${className};
200
+
201
+ function escapeHtml(value) {
202
+ return String(value)
203
+ .replace(/&/g, '&amp;')
204
+ .replace(/"/g, '&quot;')
205
+ .replace(/</g, '&lt;')
206
+ .replace(/>/g, '&gt;');
207
+ }
208
+ `.trimStart();
209
+ }
210
+ function generateStaticWcIndex(icons) {
211
+ return `${icons.map((icon) => {
212
+ return `export { ${icon.componentName} } from './${icon.name}.js';`;
213
+ }).join("\n")}\n`;
214
+ }
215
+ //#endregion
216
+ //#region packages/web-c/output-icons/src/generateVueIcon.ts
217
+ function generateVueIcon(icon) {
218
+ const inner = escapeTemplateLiteral(icon.innerSvg);
219
+ return `
220
+ import { defineComponent, h } from 'vue';
221
+
222
+ export const ${icon.componentName} = defineComponent({
223
+ name: ${JSON.stringify(icon.componentName)},
224
+
225
+ props: {
226
+ size: {
227
+ type: [String, Number],
228
+ default: '1em',
229
+ },
230
+ title: {
231
+ type: String,
232
+ default: undefined,
233
+ },
234
+ },
235
+
236
+ setup(props, { attrs, slots }) {
237
+ return () => {
238
+ const children = [];
239
+
240
+ if (props.title) {
241
+ children.push(h('title', null, props.title));
242
+ }
243
+
244
+ if (slots.default) {
245
+ children.push(...slots.default());
246
+ }
247
+
248
+ return h(
249
+ 'svg',
250
+ {
251
+ ...attrs,
252
+ width: props.size,
253
+ height: props.size,
254
+ viewBox: ${JSON.stringify(icon.viewBox)},
255
+ xmlns: 'http://www.w3.org/2000/svg',
256
+ 'aria-hidden': props.title ? undefined : true,
257
+ role: props.title ? 'img' : undefined,
258
+ innerHTML: slots.default ? undefined : \`${inner}\`,
259
+ },
260
+ children,
261
+ );
262
+ };
263
+ },
264
+ });
265
+ `.trimStart();
266
+ }
267
+ function generateVueIndex(icons) {
268
+ return `${icons.map((icon) => {
269
+ return `export { ${icon.componentName} } from './${icon.name}.js';`;
270
+ }).join("\n")}\n`;
271
+ }
272
+ //#endregion
273
+ //#region packages/web-c/output-icons/src/naming.ts
274
+ function toPascalCase(value) {
275
+ return value.split(/[-_\s]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("") || "Icon";
276
+ }
277
+ function toIconComponentName(name) {
278
+ const pascal = toPascalCase(name);
279
+ return pascal.endsWith("Icon") ? pascal : `${pascal}Icon`;
280
+ }
281
+ function sanitizeFileName(value) {
282
+ return value.trim().replace(/[^a-zA-Z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
283
+ }
284
+ function getIconJsFileName(name) {
285
+ return `${sanitizeFileName(name)}.js`;
286
+ }
287
+ function getIconSvgFileName(name) {
288
+ return `${sanitizeFileName(name)}.svg`;
289
+ }
290
+ //#endregion
291
+ //#region packages/web-c/output-icons/src/index.ts
292
+ function icons(options) {
293
+ const normalized = normalizeOptions(options);
294
+ return {
295
+ name: "zeus-output-icons",
296
+ virtualModules() {
297
+ const modules = [];
298
+ if (normalized.react) {
299
+ for (const icon of normalized.icons) modules.push({
300
+ id: `zeus:icons:react:${icon.name}`,
301
+ fileName: path.posix.join(normalized.outDir, normalized.react.outDir, getIconJsFileName(icon.name)),
302
+ code: generateReactIcon(icon)
303
+ });
304
+ modules.push({
305
+ id: "zeus:icons:react:index",
306
+ fileName: path.posix.join(normalized.outDir, normalized.react.outDir, "index.js"),
307
+ code: generateReactIndex(normalized.icons)
308
+ });
309
+ }
310
+ if (normalized.vue) {
311
+ for (const icon of normalized.icons) modules.push({
312
+ id: `zeus:icons:vue:${icon.name}`,
313
+ fileName: path.posix.join(normalized.outDir, normalized.vue.outDir, getIconJsFileName(icon.name)),
314
+ code: generateVueIcon(icon)
315
+ });
316
+ modules.push({
317
+ id: "zeus:icons:vue:index",
318
+ fileName: path.posix.join(normalized.outDir, normalized.vue.outDir, "index.js"),
319
+ code: generateVueIndex(normalized.icons)
320
+ });
321
+ }
322
+ if (normalized.wc) {
323
+ for (const icon of normalized.icons) modules.push({
324
+ id: `zeus:icons:wc:${icon.name}`,
325
+ fileName: path.posix.join(normalized.outDir, normalized.wc.outDir, getIconJsFileName(icon.name)),
326
+ code: generateStaticWcIcon(icon)
327
+ });
328
+ modules.push({
329
+ id: "zeus:icons:wc:index",
330
+ fileName: path.posix.join(normalized.outDir, normalized.wc.outDir, "index.js"),
331
+ code: generateStaticWcIndex(normalized.icons)
332
+ });
333
+ }
334
+ return modules;
335
+ },
336
+ generateBundle() {
337
+ const files = [];
338
+ if (normalized.svg) for (const icon of normalized.icons) files.push({
339
+ type: "asset",
340
+ fileName: path.posix.join(normalized.outDir, "svg", getIconSvgFileName(icon.name)),
341
+ source: svgToDataSource(icon.svg)
342
+ });
343
+ if (normalized.dts && normalized.react) files.push({
344
+ type: "asset",
345
+ fileName: path.posix.join(normalized.outDir, normalized.react.outDir, "index.d.ts"),
346
+ source: generateReactDts(normalized.icons)
347
+ });
348
+ if (normalized.dts && normalized.vue) files.push({
349
+ type: "asset",
350
+ fileName: path.posix.join(normalized.outDir, normalized.vue.outDir, "index.d.ts"),
351
+ source: generateVueDts(normalized.icons)
352
+ });
353
+ if (normalized.dts && normalized.wc) files.push({
354
+ type: "asset",
355
+ fileName: path.posix.join(normalized.outDir, normalized.wc.outDir, "index.d.ts"),
356
+ source: generateStaticWcDts(normalized.icons)
357
+ });
358
+ return files;
359
+ }
360
+ };
361
+ }
362
+ function normalizeOptions(options) {
363
+ var _options$icons, _options$outDir, _options$svg, _options$dts;
364
+ if (!((_options$icons = options.icons) === null || _options$icons === void 0 ? void 0 : _options$icons.length)) throw new Error("[zeus-output-icons] options.icons is required.");
365
+ const wcOptions = options.wc === false ? false : {
366
+ outDir: typeof options.wc === "object" && options.wc.outDir ? options.wc.outDir : "wc",
367
+ tagPrefix: typeof options.wc === "object" && options.wc.tagPrefix ? options.wc.tagPrefix : "z-icon-"
368
+ };
369
+ return {
370
+ icons: normalizeIcons(options.icons, wcOptions ? wcOptions.tagPrefix : "z-icon-"),
371
+ outDir: (_options$outDir = options.outDir) !== null && _options$outDir !== void 0 ? _options$outDir : "icons",
372
+ svg: (_options$svg = options.svg) !== null && _options$svg !== void 0 ? _options$svg : true,
373
+ react: options.react === false ? false : { outDir: typeof options.react === "object" && options.react.outDir ? options.react.outDir : "react" },
374
+ vue: options.vue === false ? false : { outDir: typeof options.vue === "object" && options.vue.outDir ? options.vue.outDir : "vue" },
375
+ wc: wcOptions,
376
+ dts: (_options$dts = options.dts) !== null && _options$dts !== void 0 ? _options$dts : true
377
+ };
378
+ }
379
+ function normalizeIcons(icons, tagPrefix) {
380
+ return icons.map((icon) => {
381
+ const parsed = parseSvg(icon.svg);
382
+ return {
383
+ name: icon.name,
384
+ componentName: toIconComponentName(icon.name),
385
+ wcTag: `${tagPrefix}${icon.name}`,
386
+ svg: icon.svg,
387
+ title: icon.title,
388
+ viewBox: parsed.viewBox,
389
+ innerSvg: parsed.innerSvg
390
+ };
391
+ });
392
+ }
393
+ //#endregion
394
+ export { icons as default };
package/index.js ADDED
@@ -0,0 +1,4 @@
1
+ // Re-export from bundled dist
2
+ export * from './dist/output-icons.esm-bundler.js'
3
+ import icons from './dist/output-icons.esm-bundler.js'
4
+ export default icons
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@zeus-js/output-icons",
3
+ "version": "0.1.0-beta.0",
4
+ "description": "No-runtime icon output plugin for Zeus component compiler host",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "module": "dist/output-icons.esm-bundler.js",
8
+ "types": "dist/output-icons.d.ts",
9
+ "files": [
10
+ "index.js",
11
+ "dist"
12
+ ],
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/output-icons.d.ts",
16
+ "node": {
17
+ "production": "./dist/output-icons.cjs.prod.js",
18
+ "development": "./dist/output-icons.cjs.js",
19
+ "default": "./index.js"
20
+ },
21
+ "module": "./dist/output-icons.esm-bundler.js",
22
+ "import": "./dist/output-icons.esm-bundler.js",
23
+ "require": "./index.js"
24
+ },
25
+ "./*": "./*"
26
+ },
27
+ "sideEffects": false,
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/baicie/zeus"
31
+ },
32
+ "buildOptions": {
33
+ "name": "ZeusOutputIcons",
34
+ "formats": [
35
+ "esm-bundler",
36
+ "cjs"
37
+ ]
38
+ },
39
+ "dependencies": {
40
+ "@zeus-js/bundler-plugin": "0.1.0-beta.0"
41
+ },
42
+ "peerDependencies": {
43
+ "react": ">=18 || >=19",
44
+ "vue": ">=3"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "react": {
48
+ "optional": true
49
+ },
50
+ "vue": {
51
+ "optional": true
52
+ }
53
+ },
54
+ "keywords": [
55
+ "zeus",
56
+ "icons",
57
+ "web-components",
58
+ "react",
59
+ "vue"
60
+ ],
61
+ "author": "Baicie",
62
+ "license": "MIT"
63
+ }