react-native-nano-icons 0.0.0 → 0.1.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.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/app.plugin.js +1 -0
  4. package/lib/commonjs/cli/build.d.ts +28 -0
  5. package/lib/commonjs/cli/build.js +83 -0
  6. package/lib/commonjs/cli/config.d.ts +9 -0
  7. package/lib/commonjs/cli/config.js +25 -0
  8. package/lib/commonjs/cli/index.d.ts +4 -0
  9. package/lib/commonjs/cli/index.js +13 -0
  10. package/lib/commonjs/cli/link.d.ts +11 -0
  11. package/lib/commonjs/cli/link.js +135 -0
  12. package/lib/commonjs/cli/logger.d.ts +27 -0
  13. package/lib/commonjs/cli/logger.js +83 -0
  14. package/lib/commonjs/index.node.js +8 -0
  15. package/lib/commonjs/plugin/src/buildFonts.d.ts +7 -0
  16. package/lib/commonjs/plugin/src/buildFonts.js +23 -0
  17. package/lib/commonjs/plugin/src/index.d.ts +5 -0
  18. package/lib/commonjs/plugin/src/index.js +43 -0
  19. package/lib/commonjs/plugin/src/types.d.ts +31 -0
  20. package/lib/commonjs/plugin/src/types.js +2 -0
  21. package/lib/commonjs/plugin/src/withNanoIconsFontLinking.d.ts +14 -0
  22. package/lib/commonjs/plugin/src/withNanoIconsFontLinking.js +92 -0
  23. package/lib/commonjs/scripts/cli.js +28 -0
  24. package/lib/commonjs/src/const/colors.d.ts +1 -0
  25. package/lib/commonjs/src/const/colors.js +153 -0
  26. package/lib/commonjs/src/core/font/compile.d.ts +9 -0
  27. package/lib/commonjs/src/core/font/compile.js +68 -0
  28. package/lib/commonjs/src/core/font/metrics.d.ts +1 -0
  29. package/lib/commonjs/src/core/font/metrics.js +33 -0
  30. package/lib/commonjs/src/core/pipeline/config.d.ts +14 -0
  31. package/lib/commonjs/src/core/pipeline/config.js +17 -0
  32. package/lib/commonjs/src/core/pipeline/index.d.ts +3 -0
  33. package/lib/commonjs/src/core/pipeline/index.js +7 -0
  34. package/lib/commonjs/src/core/pipeline/managers.d.ts +11 -0
  35. package/lib/commonjs/src/core/pipeline/managers.js +89 -0
  36. package/lib/commonjs/src/core/pipeline/run.d.ts +14 -0
  37. package/lib/commonjs/src/core/pipeline/run.js +99 -0
  38. package/lib/commonjs/src/core/svg/layers.d.ts +24 -0
  39. package/lib/commonjs/src/core/svg/layers.js +42 -0
  40. package/lib/commonjs/src/core/svg/svg_dom.d.ts +22 -0
  41. package/lib/commonjs/src/core/svg/svg_dom.js +84 -0
  42. package/lib/commonjs/src/core/svg/svg_pathops.d.ts +21 -0
  43. package/lib/commonjs/src/core/svg/svg_pathops.js +611 -0
  44. package/lib/commonjs/src/core/types.d.ts +120 -0
  45. package/lib/commonjs/src/core/types.js +2 -0
  46. package/lib/commonjs/src/utils/fingerPrint.d.ts +1 -0
  47. package/lib/commonjs/src/utils/fingerPrint.js +20 -0
  48. package/lib/commonjs/src/utils/parse.d.ts +2 -0
  49. package/lib/commonjs/src/utils/parse.js +64 -0
  50. package/lib/module/const/colors.js +153 -0
  51. package/lib/module/const/colors.js.map +1 -0
  52. package/lib/module/core/font/compile.js +70 -0
  53. package/lib/module/core/font/compile.js.map +1 -0
  54. package/lib/module/core/font/metrics.js +37 -0
  55. package/lib/module/core/font/metrics.js.map +1 -0
  56. package/lib/module/core/pipeline/config.js +20 -0
  57. package/lib/module/core/pipeline/config.js.map +1 -0
  58. package/lib/module/core/pipeline/index.js +5 -0
  59. package/lib/module/core/pipeline/index.js.map +1 -0
  60. package/lib/module/core/pipeline/managers.js +80 -0
  61. package/lib/module/core/pipeline/managers.js.map +1 -0
  62. package/lib/module/core/pipeline/run.js +114 -0
  63. package/lib/module/core/pipeline/run.js.map +1 -0
  64. package/lib/module/core/shims/pathops.py +181 -0
  65. package/lib/module/core/svg/layers.js +51 -0
  66. package/lib/module/core/svg/layers.js.map +1 -0
  67. package/lib/module/core/svg/svg_dom.js +83 -0
  68. package/lib/module/core/svg/svg_dom.js.map +1 -0
  69. package/lib/module/core/svg/svg_pathops.js +566 -0
  70. package/lib/module/core/svg/svg_pathops.js.map +1 -0
  71. package/lib/module/core/tsconfig.json +32 -0
  72. package/lib/module/core/types.js +2 -0
  73. package/lib/module/core/types.js.map +1 -0
  74. package/lib/module/createNanoIconsSet.js +84 -0
  75. package/lib/module/createNanoIconsSet.js.map +1 -0
  76. package/lib/module/index.js +5 -0
  77. package/lib/module/index.js.map +1 -0
  78. package/lib/module/index.node.js +13 -0
  79. package/lib/module/index.node.js.map +1 -0
  80. package/lib/module/package.json +1 -0
  81. package/lib/module/utils/fingerPrint.js +17 -0
  82. package/lib/module/utils/fingerPrint.js.map +1 -0
  83. package/lib/module/utils/parse.js +53 -0
  84. package/lib/module/utils/parse.js.map +1 -0
  85. package/lib/typescript/__tests__/build.unit.test.d.ts +3 -0
  86. package/lib/typescript/__tests__/build.unit.test.d.ts.map +1 -0
  87. package/lib/typescript/__tests__/clippath.e2e.test.d.ts +3 -0
  88. package/lib/typescript/__tests__/clippath.e2e.test.d.ts.map +1 -0
  89. package/lib/typescript/__tests__/fingerprint.unit.test.d.ts +3 -0
  90. package/lib/typescript/__tests__/fingerprint.unit.test.d.ts.map +1 -0
  91. package/lib/typescript/__tests__/pipeline.e2e.test.d.ts +3 -0
  92. package/lib/typescript/__tests__/pipeline.e2e.test.d.ts.map +1 -0
  93. package/lib/typescript/__tests__/placement.unit.test.d.ts +3 -0
  94. package/lib/typescript/__tests__/placement.unit.test.d.ts.map +1 -0
  95. package/lib/typescript/__tests__/svg_dom.unit.test.d.ts +3 -0
  96. package/lib/typescript/__tests__/svg_dom.unit.test.d.ts.map +1 -0
  97. package/lib/typescript/cli/build.d.ts +29 -0
  98. package/lib/typescript/cli/build.d.ts.map +1 -0
  99. package/lib/typescript/cli/logger.d.ts +28 -0
  100. package/lib/typescript/cli/logger.d.ts.map +1 -0
  101. package/lib/typescript/package.json +1 -0
  102. package/lib/typescript/src/const/colors.d.ts +2 -0
  103. package/lib/typescript/src/const/colors.d.ts.map +1 -0
  104. package/lib/typescript/src/core/font/compile.d.ts +10 -0
  105. package/lib/typescript/src/core/font/compile.d.ts.map +1 -0
  106. package/lib/typescript/src/core/font/metrics.d.ts +2 -0
  107. package/lib/typescript/src/core/font/metrics.d.ts.map +1 -0
  108. package/lib/typescript/src/core/pipeline/config.d.ts +15 -0
  109. package/lib/typescript/src/core/pipeline/config.d.ts.map +1 -0
  110. package/lib/typescript/src/core/pipeline/index.d.ts +4 -0
  111. package/lib/typescript/src/core/pipeline/index.d.ts.map +1 -0
  112. package/lib/typescript/src/core/pipeline/managers.d.ts +12 -0
  113. package/lib/typescript/src/core/pipeline/managers.d.ts.map +1 -0
  114. package/lib/typescript/src/core/pipeline/run.d.ts +15 -0
  115. package/lib/typescript/src/core/pipeline/run.d.ts.map +1 -0
  116. package/lib/typescript/src/core/svg/layers.d.ts +25 -0
  117. package/lib/typescript/src/core/svg/layers.d.ts.map +1 -0
  118. package/lib/typescript/src/core/svg/svg_dom.d.ts +23 -0
  119. package/lib/typescript/src/core/svg/svg_dom.d.ts.map +1 -0
  120. package/lib/typescript/src/core/svg/svg_pathops.d.ts +22 -0
  121. package/lib/typescript/src/core/svg/svg_pathops.d.ts.map +1 -0
  122. package/lib/typescript/src/core/types.d.ts +121 -0
  123. package/lib/typescript/src/core/types.d.ts.map +1 -0
  124. package/lib/typescript/src/createNanoIconsSet.d.ts +19 -0
  125. package/lib/typescript/src/createNanoIconsSet.d.ts.map +1 -0
  126. package/lib/typescript/src/index.d.ts +3 -0
  127. package/lib/typescript/src/index.d.ts.map +1 -0
  128. package/lib/typescript/src/index.node.d.ts +2 -0
  129. package/lib/typescript/src/index.node.d.ts.map +1 -0
  130. package/lib/typescript/src/utils/fingerPrint.d.ts +2 -0
  131. package/lib/typescript/src/utils/fingerPrint.d.ts.map +1 -0
  132. package/lib/typescript/src/utils/parse.d.ts +3 -0
  133. package/lib/typescript/src/utils/parse.d.ts.map +1 -0
  134. package/package.json +160 -1
  135. package/plugin/src/buildFonts.ts +29 -0
  136. package/plugin/src/index.ts +68 -0
  137. package/plugin/src/types.ts +33 -0
  138. package/plugin/src/withNanoIconsFontLinking.ts +119 -0
  139. package/plugin/tsconfig.json +9 -0
  140. package/scripts/cli.ts +34 -0
  141. package/scripts/tsconfig.json +25 -0
  142. package/src/const/colors.ts +150 -0
  143. package/src/core/font/compile.ts +96 -0
  144. package/src/core/font/metrics.ts +44 -0
  145. package/src/core/pipeline/config.ts +24 -0
  146. package/src/core/pipeline/index.ts +3 -0
  147. package/src/core/pipeline/managers.ts +114 -0
  148. package/src/core/pipeline/run.ts +151 -0
  149. package/src/core/shims/pathops.py +181 -0
  150. package/src/core/svg/layers.ts +66 -0
  151. package/src/core/svg/svg_dom.ts +99 -0
  152. package/src/core/svg/svg_pathops.ts +796 -0
  153. package/src/core/tsconfig.json +32 -0
  154. package/src/core/types.ts +138 -0
  155. package/src/createNanoIconsSet.tsx +131 -0
  156. package/src/index.node.ts +14 -0
  157. package/src/index.ts +7 -0
  158. package/src/utils/fingerPrint.ts +20 -0
  159. package/src/utils/parse.ts +67 -0
@@ -0,0 +1,181 @@
1
+ # shims/pathops.py
2
+ #
3
+ # Minimal skia-pathops-compatible API backed by a JS module registered as "_pathops_js".
4
+ # This is designed to satisfy picosvg's usage of pathops (Path, op, enums, iteration).
5
+ #
6
+ # IMPORTANT BEHAVIOR:
7
+ # - skia-pathops simplify(fix_winding=True) forces fillType to WINDING.
8
+ # PathKit does not do that, so we emulate it here by explicitly setting fillType.
9
+
10
+ from __future__ import annotations
11
+ from dataclasses import dataclass
12
+ from typing import Iterable, Iterator, List, Sequence, Tuple, Optional, Any
13
+
14
+ try:
15
+ import _pathops_js # provided via pyodide.registerJsModule(...)
16
+ except Exception as e:
17
+ raise ImportError("Missing JS backend module _pathops_js") from e
18
+
19
+
20
+ # ---- Exceptions ----
21
+ class PathOpsError(RuntimeError):
22
+ pass
23
+
24
+
25
+ # ---- Enums (values chosen to match our JS backend verb mapping) ----
26
+ class PathVerb:
27
+ MOVE = 0
28
+ LINE = 1
29
+ QUAD = 2
30
+ CUBIC = 3
31
+ CLOSE = 4
32
+ CONIC = 5 # should not occur for picosvg after conversions
33
+
34
+
35
+ class FillType:
36
+ WINDING = 0
37
+ EVEN_ODD = 1
38
+
39
+
40
+ class PathOp:
41
+ UNION = 0
42
+ INTERSECTION = 1
43
+ DIFFERENCE = 2
44
+
45
+
46
+ class LineCap:
47
+ BUTT_CAP = 0
48
+ ROUND_CAP = 1
49
+ SQUARE_CAP = 2
50
+
51
+
52
+ class LineJoin:
53
+ MITER_JOIN = 0
54
+ ROUND_JOIN = 1
55
+ BEVEL_JOIN = 2
56
+
57
+
58
+ Point = Tuple[float, float]
59
+ SvgCmd = Tuple[str, Tuple[float, ...]]
60
+
61
+
62
+ @dataclass
63
+ class _Handle:
64
+ h: Any # opaque handle owned by JS backend
65
+
66
+
67
+ class Path:
68
+ def __init__(self, other: "Path" = None, *, fillType: int = FillType.WINDING):
69
+ if other is not None:
70
+ self._handle = _Handle(_pathops_js.clone_path(other._handle.h))
71
+ else:
72
+ self._handle = _Handle(_pathops_js.create_path(int(fillType)))
73
+
74
+ # ---- fillType property ----
75
+ @property
76
+ def fillType(self) -> int:
77
+ return int(_pathops_js.get_fill_type(self._handle.h))
78
+
79
+ @fillType.setter
80
+ def fillType(self, ft: int) -> None:
81
+ _pathops_js.set_fill_type(self._handle.h, int(ft))
82
+
83
+ # ---- drawing commands ----
84
+ def moveTo(self, x: float, y: float) -> None:
85
+ _pathops_js.move_to(self._handle.h, float(x), float(y))
86
+
87
+ def lineTo(self, x: float, y: float) -> None:
88
+ _pathops_js.line_to(self._handle.h, float(x), float(y))
89
+
90
+ def quadTo(self, x1: float, y1: float, x2: float, y2: float) -> None:
91
+ _pathops_js.quad_to(self._handle.h, float(x1), float(y1), float(x2), float(y2))
92
+
93
+ def cubicTo(self, x1: float, y1: float, x2: float, y2: float, x3: float, y3: float) -> None:
94
+ _pathops_js.cubic_to(self._handle.h, float(x1), float(y1), float(x2), float(y2), float(x3), float(y3))
95
+
96
+ def close(self) -> None:
97
+ _pathops_js.close(self._handle.h)
98
+
99
+ # ---- transforms ----
100
+ def transform(self, a: float, b: float, c: float, d: float, e: float, f: float) -> "Path":
101
+ out_h = _pathops_js.transform(self._handle.h, float(a), float(b), float(c), float(d), float(e), float(f))
102
+ out = Path.__new__(Path)
103
+ out._handle = _Handle(out_h)
104
+ return out
105
+
106
+ # ---- simplify / stroke ----
107
+ def simplify(self, fix_winding: bool = False) -> None:
108
+ ok = bool(_pathops_js.simplify(self._handle.h, bool(fix_winding)))
109
+ # In skia-pathops, simplify(fix_winding=True) forces WINDING fill type.
110
+ if fix_winding:
111
+ _pathops_js.set_fill_type(self._handle.h, int(FillType.WINDING))
112
+ if not ok:
113
+ # skia-pathops may throw PathOpsError; picosvg usually expects simplify to succeed
114
+ # but some paths can be tricky. We'll be conservative and not throw.
115
+ return
116
+
117
+ def stroke(
118
+ self,
119
+ stroke_width: float,
120
+ cap: int,
121
+ join: int,
122
+ miter_limit: float,
123
+ dash_array: Sequence[float] = (),
124
+ dash_offset: float = 0.0,
125
+ ) -> None:
126
+ out_h = _pathops_js.stroke(
127
+ self._handle.h,
128
+ float(stroke_width),
129
+ int(cap),
130
+ int(join),
131
+ float(miter_limit),
132
+ list(map(float, dash_array)) if dash_array else [],
133
+ float(dash_offset),
134
+ )
135
+ if out_h is None:
136
+ raise PathOpsError("stroke failed")
137
+ # Replace in-place (matches typical expectations)
138
+ self._handle = _Handle(out_h)
139
+
140
+ def convertConicsToQuads(self, tolerance: float = 0.25) -> None:
141
+ # PathKit roundtrip already avoids conics for our use cases
142
+ _pathops_js.convert_conics_to_quads(self._handle.h, float(tolerance))
143
+
144
+ # ---- geometry ----
145
+ @property
146
+ def bounds(self) -> Tuple[float, float, float, float]:
147
+ b = _pathops_js.bounds(self._handle.h)
148
+ return (float(b[0]), float(b[1]), float(b[2]), float(b[3]))
149
+
150
+ @property
151
+ def area(self) -> float:
152
+ return float(_pathops_js.area(self._handle.h))
153
+
154
+ # ---- iteration ----
155
+ def __iter__(self) -> Iterator[Tuple[int, List[Point]]]:
156
+ # JS backend returns list of [verbInt, [[x,y], ...]]
157
+ segs = _pathops_js.iter_segments(self._handle.h)
158
+ for verb, pts in segs:
159
+ out_pts: List[Point] = [(float(p[0]), float(p[1])) for p in pts]
160
+ yield int(verb), out_pts
161
+
162
+ # ---- cleanup ----
163
+ def __del__(self):
164
+ try:
165
+ _pathops_js.delete_path(self._handle.h)
166
+ except Exception:
167
+ pass
168
+
169
+
170
+ def op(path1: Path, path2: Path, op: int, fix_winding: bool = False) -> Path:
171
+ out_h = _pathops_js.op(path1._handle.h, path2._handle.h, int(op))
172
+ if out_h is None:
173
+ raise PathOpsError("operation did not succeed")
174
+
175
+ out = Path.__new__(Path)
176
+ out._handle = _Handle(out_h)
177
+
178
+ if fix_winding:
179
+ _pathops_js.set_fill_type(out._handle.h, int(FillType.WINDING))
180
+
181
+ return out
@@ -0,0 +1,66 @@
1
+ import fsp from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ function roundInt(n: number): number {
5
+ return Math.round(n);
6
+ }
7
+
8
+ export function computePlacement(opts: {
9
+ upm: number;
10
+ safeZone: number;
11
+ viewBox: [number, number, number, number];
12
+ }) {
13
+ const [vx, vy, vw, vh] = opts.viewBox;
14
+
15
+ // Guard against invalid viewBox height from source svg
16
+ const safeVh = vh === 0 ? 1 : vh;
17
+
18
+ // ✅ Fit-to-height (same visual height for all glyphs)
19
+ const scale = opts.safeZone / safeVh;
20
+
21
+ const totalPadding = (opts.upm - opts.safeZone) / 2;
22
+
23
+ // Scaled dimensions in font units
24
+ const scaledW = vw * scale;
25
+ const scaledH = vh * scale; // ~= safeZone
26
+
27
+ // ✅ proportional advance width (includes same padding concept as vertical)
28
+ // This is the width of the glyph coordinate space we will emit in the layer SVG.
29
+ const adv = Math.max(1, roundInt(scaledW + totalPadding * 2));
30
+
31
+ // Center scaled content within (adv x upm)
32
+ // (when adv == scaledW + 2*padding, xOff is basically padding, with rounding compensation)
33
+ const xOff = (adv - scaledW) / 2;
34
+ const yOff = (opts.upm - scaledH) / 2; // ~= totalPadding
35
+
36
+ return { vx, vy, scale, xOff, yOff, adv };
37
+ }
38
+
39
+ export async function writeLayerSvg(opts: {
40
+ tempDir: string;
41
+ upm: number;
42
+ adv: number;
43
+ vx: number;
44
+ vy: number;
45
+ scale: number;
46
+ xOff: number;
47
+ yOff: number;
48
+ d: string;
49
+ codepoint: number;
50
+ }): Promise<void> {
51
+ const hex = opts.codepoint.toString(16).padStart(4, '0');
52
+
53
+ const layerSvg = `
54
+ <svg viewBox="0 0 ${opts.adv} ${
55
+ opts.upm
56
+ }" xmlns="http://www.w3.org/2000/svg">
57
+ <rect width="${opts.adv}" height="${opts.upm}" fill="none" />
58
+ <g transform="translate(${opts.xOff}, ${opts.yOff}) scale(${
59
+ opts.scale
60
+ }) translate(${-opts.vx}, ${-opts.vy})">
61
+ <path d="${opts.d}" fill="black" />
62
+ </g>
63
+ </svg>`.trim();
64
+
65
+ await fsp.writeFile(path.join(opts.tempDir, `u${hex}.svg`), layerSvg, 'utf8');
66
+ }
@@ -0,0 +1,99 @@
1
+ import { JSDOM } from 'jsdom';
2
+ import { parseColor } from '../../utils/parse';
3
+
4
+ export type ParsedFlatSvg = {
5
+ viewBox: [number, number, number, number];
6
+ paths: Array<{ d: string; fill: string | null }>;
7
+ };
8
+
9
+ // if the fill is implicit, walk ancestors for the first explicit fill value
10
+ function resolveInheritedFill(el: Element): string {
11
+ let current: Element | null = el.parentElement;
12
+ while (current !== null) {
13
+ const fill = current.getAttribute('fill');
14
+ if (fill !== null && fill !== 'inherit') return fill;
15
+ current = current.parentElement;
16
+ }
17
+ return 'black';
18
+ }
19
+
20
+ // bake opacity into the fill as an rgba(...)
21
+ export function calculateOpColor(
22
+ fill: string | null,
23
+ opacity: number,
24
+ el: Element
25
+ ): `rgba(${number},${number},${number},${number})` {
26
+ const resolvedFill = fill ?? resolveInheritedFill(el);
27
+ const [r, g, b, a] = parseColor(resolvedFill);
28
+ const finalAlpha = +(a * opacity).toFixed(4);
29
+ return `rgba(${r},${g},${b},${finalAlpha})`;
30
+ }
31
+
32
+ export const parsePath = (p: Element): { d: string; fill: string | null } => {
33
+ const d = p.getAttribute('d') ?? '';
34
+
35
+ const op = p.getAttribute('opacity');
36
+ const fillOp = p.getAttribute('fill-opacity');
37
+ const fill = p.getAttribute('fill');
38
+
39
+ if (op !== null || fillOp !== null) {
40
+ const opVal = op !== null ? parseFloat(op) : 1;
41
+ const fillOpVal = fillOp !== null ? parseFloat(fillOp) : 1;
42
+ const combinedOpacity = opVal * fillOpVal;
43
+ return {
44
+ d,
45
+ fill: calculateOpColor(fill, combinedOpacity, p),
46
+ };
47
+ }
48
+
49
+ return {
50
+ d,
51
+ fill,
52
+ };
53
+ };
54
+
55
+ export function parseFlattenedSvg(flattenedSvg: string): ParsedFlatSvg {
56
+ const dom = new JSDOM(flattenedSvg);
57
+ const doc = dom.window.document;
58
+
59
+ const svgEl = doc.querySelector('svg');
60
+ const viewBoxRaw = svgEl
61
+ ?.getAttribute('viewBox')
62
+ ?.split(/\s+/)
63
+ .map(Number) ?? [0, 0, 100, 100];
64
+
65
+ const viewBox: [number, number, number, number] =
66
+ viewBoxRaw.length === 4 && viewBoxRaw.every((n) => Number.isFinite(n))
67
+ ? [viewBoxRaw[0]!, viewBoxRaw[1]!, viewBoxRaw[2]!, viewBoxRaw[3]!]
68
+ : [0, 0, 100, 100];
69
+
70
+ const pathEls = Array.from(doc.querySelectorAll('path'));
71
+
72
+ const paths = pathEls.map(parsePath).filter((p) => p.d.trim() !== '');
73
+
74
+ return { viewBox, paths };
75
+ }
76
+
77
+ export function shouldSkipPath(d: string, fill: string | null): boolean {
78
+ if (!d || d.trim() === '') return true;
79
+ const f = (fill ?? '').trim().toLowerCase();
80
+ return f === 'transparent' || f === 'none';
81
+ }
82
+
83
+ export type SvgValidation = { valid: true } | { valid: false; reason: string };
84
+
85
+ export function validateSvg(content: string): SvgValidation {
86
+ if (/<mask[\s>]/i.test(content)) {
87
+ return { valid: false, reason: '<mask> is not supported yet' };
88
+ }
89
+ if (/<filter[\s>]/i.test(content)) {
90
+ return { valid: false, reason: '<filter> is not supported yet' };
91
+ }
92
+ return { valid: true };
93
+ }
94
+
95
+ // ensure the svg has a xmlns attribute
96
+ export function preprocessSvg(content: string): string {
97
+ if (/xmlns\s*=/.test(content)) return content;
98
+ return content.replace(/<svg\b/, '<svg xmlns="http://www.w3.org/2000/svg"');
99
+ }