svger-cli 1.0.2 → 1.0.3
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.
package/README.md
CHANGED
|
@@ -118,4 +118,4 @@ Cleans output folder at the end if needed.
|
|
|
118
118
|
|
|
119
119
|
Acknowledgements
|
|
120
120
|
|
|
121
|
-
This project was implemented by Faeze
|
|
121
|
+
This project was implemented by Faeze Mohades, following the ADR authored by Navid Rezadoost and based on the TDR prepared by Ehsan Jafari. Their guidance and documentation on SVG integration methods in React were instrumental in shaping the design and functionality of the svger-cli CLI.
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Generates a React functional component string from an SVG file's content,
|
|
3
|
+
* safely converting any inline `style="..."` to React-compatible object.
|
|
4
|
+
*/
|
|
5
|
+
export declare function reactTemplate({ componentName, svgContent, defaultWidth, defaultHeight, defaultFill, defaultStroke, }: {
|
|
2
6
|
componentName: string;
|
|
3
7
|
svgContent: string;
|
|
4
8
|
defaultWidth?: number;
|
|
5
9
|
defaultHeight?: number;
|
|
6
10
|
defaultFill?: string;
|
|
11
|
+
defaultStroke?: string;
|
|
7
12
|
}): string;
|
|
@@ -1,29 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Generates a React functional component string from an SVG file's content,
|
|
3
|
+
* safely converting any inline `style="..."` to React-compatible object.
|
|
4
|
+
*/
|
|
5
|
+
export function reactTemplate({ componentName, svgContent, defaultWidth = 24, defaultHeight = 24, defaultFill = "currentColor", defaultStroke = "none", }) {
|
|
6
|
+
// helper: convert inline style string to JS object
|
|
7
|
+
function styleStringToObject(style) {
|
|
8
|
+
const obj = {};
|
|
9
|
+
style.split(";").forEach((pair) => {
|
|
10
|
+
if (!pair.trim())
|
|
11
|
+
return;
|
|
12
|
+
const [key, value] = pair.split(":");
|
|
13
|
+
if (!key || value === undefined)
|
|
14
|
+
return;
|
|
15
|
+
// convert kebab-case to camelCase
|
|
16
|
+
const camelKey = key.trim().replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
17
|
+
let v = value.trim();
|
|
18
|
+
if (!isNaN(Number(v)))
|
|
19
|
+
v = Number(v);
|
|
20
|
+
obj[camelKey] = v;
|
|
21
|
+
});
|
|
22
|
+
return obj;
|
|
23
|
+
}
|
|
24
|
+
// clean SVG content
|
|
3
25
|
let cleaned = svgContent
|
|
4
|
-
.replace(/<\?xml.*?\?>/g, "")
|
|
5
|
-
.replace(/<!DOCTYPE.*?>/g, "")
|
|
6
|
-
.replace(/\r?\n|\r/g, "")
|
|
7
|
-
.replace(/\s*style="[^"]*"/g, "") // حذف style inline
|
|
8
|
-
.replace(/\s*(class|id)=["'][^"']*["']/g, "") // حذف class/id
|
|
26
|
+
.replace(/<\?xml.*?\?>/g, "") // remove XML declaration
|
|
27
|
+
.replace(/<!DOCTYPE.*?>/g, "") // remove DOCTYPE
|
|
28
|
+
.replace(/\r?\n|\r/g, "") // remove newlines
|
|
9
29
|
.trim();
|
|
10
|
-
//
|
|
11
|
-
cleaned = cleaned.replace(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
className={props.className}
|
|
17
|
-
{...props}>`);
|
|
30
|
+
// convert all style="..." to React objects
|
|
31
|
+
cleaned = cleaned.replace(/style="([^"]*)"/g, (_, styleContent) => {
|
|
32
|
+
return `{...{style: ${JSON.stringify(styleStringToObject(styleContent))}}}`;
|
|
33
|
+
});
|
|
34
|
+
// inject React props into <svg> tag
|
|
35
|
+
cleaned = cleaned.replace(/<svg([^>]*)>/, `<svg$1 width={props.width || ${defaultWidth}} height={props.height || ${defaultHeight}} fill={props.fill || "${defaultFill}"} stroke={props.stroke || "${defaultStroke}"} {...props}>`);
|
|
18
36
|
return `import * as React from "react";
|
|
19
37
|
import type { SVGProps } from "react";
|
|
20
38
|
|
|
21
|
-
export const ${componentName}: React.FC<SVGProps<SVGSVGElement>> = (
|
|
22
|
-
width = ${defaultWidth},
|
|
23
|
-
height = ${defaultHeight},
|
|
24
|
-
fill = "${defaultFill}",
|
|
25
|
-
...props
|
|
26
|
-
}) => (
|
|
39
|
+
export const ${componentName}: React.FC<SVGProps<SVGSVGElement>> = (props) => (
|
|
27
40
|
${cleaned}
|
|
28
41
|
);
|
|
29
42
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svger-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "CLI and runtime for converting SVGs to React components with watch support",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"type": "module",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"cli",
|
|
18
18
|
"components"
|
|
19
19
|
],
|
|
20
|
-
"author": "faeze
|
|
20
|
+
"author": "faeze mohades",
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"change-case": "^5.4.4",
|
|
@@ -1,46 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a React functional component string from an SVG file's content,
|
|
3
|
+
* safely converting any inline `style="..."` to React-compatible object.
|
|
4
|
+
*/
|
|
1
5
|
export function reactTemplate({
|
|
2
6
|
componentName,
|
|
3
7
|
svgContent,
|
|
4
8
|
defaultWidth = 24,
|
|
5
9
|
defaultHeight = 24,
|
|
6
10
|
defaultFill = "currentColor",
|
|
11
|
+
defaultStroke = "none",
|
|
7
12
|
}: {
|
|
8
13
|
componentName: string;
|
|
9
14
|
svgContent: string;
|
|
10
15
|
defaultWidth?: number;
|
|
11
16
|
defaultHeight?: number;
|
|
12
17
|
defaultFill?: string;
|
|
18
|
+
defaultStroke?: string;
|
|
13
19
|
}) {
|
|
14
|
-
//
|
|
20
|
+
// helper: convert inline style string to JS object
|
|
21
|
+
function styleStringToObject(style: string) {
|
|
22
|
+
const obj: Record<string, string | number> = {};
|
|
23
|
+
style.split(";").forEach((pair) => {
|
|
24
|
+
if (!pair.trim()) return;
|
|
25
|
+
const [key, value] = pair.split(":");
|
|
26
|
+
if (!key || value === undefined) return;
|
|
27
|
+
// convert kebab-case to camelCase
|
|
28
|
+
const camelKey = key.trim().replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
29
|
+
let v: string | number = value.trim();
|
|
30
|
+
if (!isNaN(Number(v))) v = Number(v);
|
|
31
|
+
obj[camelKey] = v;
|
|
32
|
+
});
|
|
33
|
+
return obj;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// clean SVG content
|
|
15
37
|
let cleaned = svgContent
|
|
16
|
-
.replace(/<\?xml.*?\?>/g, "")
|
|
17
|
-
.replace(/<!DOCTYPE.*?>/g, "")
|
|
18
|
-
.replace(/\r?\n|\r/g, "")
|
|
19
|
-
.replace(/\s*style="[^"]*"/g, "") // حذف style inline
|
|
20
|
-
.replace(/\s*(class|id)=["'][^"']*["']/g, "") // حذف class/id
|
|
38
|
+
.replace(/<\?xml.*?\?>/g, "") // remove XML declaration
|
|
39
|
+
.replace(/<!DOCTYPE.*?>/g, "") // remove DOCTYPE
|
|
40
|
+
.replace(/\r?\n|\r/g, "") // remove newlines
|
|
21
41
|
.trim();
|
|
22
42
|
|
|
23
|
-
//
|
|
43
|
+
// convert all style="..." to React objects
|
|
44
|
+
cleaned = cleaned.replace(/style="([^"]*)"/g, (_, styleContent) => {
|
|
45
|
+
return `{...{style: ${JSON.stringify(styleStringToObject(styleContent))}}}`;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// inject React props into <svg> tag
|
|
24
49
|
cleaned = cleaned.replace(
|
|
25
50
|
/<svg([^>]*)>/,
|
|
26
|
-
`<svg$1
|
|
27
|
-
width={width}
|
|
28
|
-
height={height}
|
|
29
|
-
fill={fill}
|
|
30
|
-
stroke={props.stroke || "none"}
|
|
31
|
-
className={props.className}
|
|
32
|
-
{...props}>`
|
|
51
|
+
`<svg$1 width={props.width || ${defaultWidth}} height={props.height || ${defaultHeight}} fill={props.fill || "${defaultFill}"} stroke={props.stroke || "${defaultStroke}"} {...props}>`
|
|
33
52
|
);
|
|
34
53
|
|
|
35
54
|
return `import * as React from "react";
|
|
36
55
|
import type { SVGProps } from "react";
|
|
37
56
|
|
|
38
|
-
export const ${componentName}: React.FC<SVGProps<SVGSVGElement>> = (
|
|
39
|
-
width = ${defaultWidth},
|
|
40
|
-
height = ${defaultHeight},
|
|
41
|
-
fill = "${defaultFill}",
|
|
42
|
-
...props
|
|
43
|
-
}) => (
|
|
57
|
+
export const ${componentName}: React.FC<SVGProps<SVGSVGElement>> = (props) => (
|
|
44
58
|
${cleaned}
|
|
45
59
|
);
|
|
46
60
|
|