Skip to content

Commit b04821f

Browse files
author
Kalle Ott
committed
release preparations
1 parent 1fcd0dc commit b04821f

File tree

9 files changed

+233
-128
lines changed

9 files changed

+233
-128
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ node_modules
66
.rts2_cache_esm
77
.rts2_cache_umd
88
dist
9+
coverage

.npmignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
*.log
2+
.DS_Store
3+
node_modules
4+
.cache
5+
.rts2_cache_cjs
6+
.rts2_cache_esm
7+
.rts2_cache_umd
8+
/.vscode
9+
/example
10+
/coverage

README.md

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,70 @@
1-
# 🦥 react-lazy-svg
1+
<p align="center" style="color: #343a40">
2+
<icon style="font-size:100px;" alt="sloth emoji">🦥</icon>
3+
<h1 align="center">react-lazy-svg</h1>
4+
</p>
5+
<p align="center" style="font-size: 1.2rem;">The easy way to use SVG sprite-sheets</p>
26

3-
Create a sprite sheet for used svg sources on your page without any magic in your bundler.
7+
react-lazy-svg is a simple way to use SVGs with the performance benefits of a
8+
sprite-sheet and svg css styling possibilities. Without bloating the bundle. It
9+
automatically creates a sprite-sheet for all used SVGs on the client but also
10+
provides a function to create a server side rendered sprite-sheet for icons used
11+
in the first paint.
12+
13+
## Usage
14+
15+
```bash
16+
npm install --save react-lazy-svg
17+
```
18+
19+
Wrap the App with the contextProvider and provide a function to resolve SVG
20+
strings by URL. If you are using server side rendering you should also call
21+
`initOnClient()` to hydrate the sprite sheet context.
22+
23+
```tsx
24+
import { SpriteContextProvider, initOnClient. Icon } from 'react-lazy-svg';
25+
import icon1 from './icon1.svg';
26+
27+
const loadSVG = async (url: string) => {
28+
return await (await fetch(url)).text();
29+
};
30+
initOnClient();
31+
32+
const Root = () => (
33+
<SpriteContextProvider loadSVG={loadSVG}>
34+
<Icon url={icon1} className="icon"></Icon>
35+
<Icon url={icon1} className="icon red"></Icon>
36+
</SpriteContextProvider>
37+
);
38+
```
39+
40+
On the server the SVG resolver function could look like this, and load the svg
41+
contents from the file system.
42+
43+
```ts
44+
const svgIconFiles = new Map<string, string>();
45+
export const readSvg = async (url: string) => {
46+
if (svgIconFiles.has(url)) {
47+
return svgIconFiles.get(url);
48+
}
49+
50+
const readFile = promisify(fs.readFile);
51+
52+
const cdnBase = 'http://localhost:3001/static/media/';
53+
54+
if (url.startsWith(cdnBase)) {
55+
url = path.join(
56+
process.cwd(),
57+
url.replace(cdnBase, './build/public/static/media/'),
58+
);
59+
}
60+
61+
// ignore external assets on server side
62+
if (!url.startsWith('http')) {
63+
const svgString = await readFile(url, { encoding: 'utf8' });
64+
svgIconFiles.set(url, svgString);
65+
return svgString;
66+
}
67+
68+
return undefined;
69+
};
70+
```

example/yarn.lock

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,16 +1198,27 @@
11981198
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.9.tgz#43896ab87fc82bda1dfd600cdf44a0c8a64e11d2"
11991199
integrity sha512-0sCTiXKXELOBxvZLN4krQ0FPOAA7ij+6WwvD0k/PHd9/KAkr4dXel5J9fh6F4x1FwAQILqAWkmpeuS6mjf1iKA==
12001200

1201+
"@types/prop-types@*":
1202+
version "15.7.3"
1203+
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
1204+
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
1205+
12011206
"@types/q@^1.5.1":
12021207
version "1.5.2"
12031208
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
12041209
integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
12051210

12061211
"@types/react-dom@link:../node_modules/@types/react-dom":
12071212
version "0.0.0"
1213+
uid ""
1214+
1215+
"@types/react@*":
1216+
version "0.0.0"
1217+
uid ""
12081218

12091219
"@types/react@link:../node_modules/@types/react":
12101220
version "0.0.0"
1221+
uid ""
12111222

12121223
"@types/source-list-map@*":
12131224
version "0.1.2"
@@ -2938,6 +2949,11 @@ cssstyle@^1.0.0:
29382949
dependencies:
29392950
cssom "0.3.x"
29402951

2952+
csstype@^2.2.0:
2953+
version "2.6.10"
2954+
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.10.tgz#e63af50e66d7c266edb6b32909cfd0aabe03928b"
2955+
integrity sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==
2956+
29412957
cyclist@^1.0.1:
29422958
version "1.0.1"
29432959
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
@@ -5616,7 +5632,7 @@ loglevel@^1.6.6:
56165632
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312"
56175633
integrity sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==
56185634

5619-
loose-envify@^1.0.0:
5635+
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
56205636
version "1.4.0"
56215637
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
56225638
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@@ -7398,6 +7414,15 @@ prompts@^2.0.1:
73987414
kleur "^3.0.3"
73997415
sisteransi "^1.0.4"
74007416

7417+
prop-types@^15.6.2:
7418+
version "15.7.2"
7419+
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
7420+
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
7421+
dependencies:
7422+
loose-envify "^1.4.0"
7423+
object-assign "^4.1.1"
7424+
react-is "^16.8.1"
7425+
74017426
proxy-addr@^2.0.6:
74027427
version "2.0.6"
74037428
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf"
@@ -7641,22 +7666,25 @@ react-dev-utils@^10.2.0:
76417666

76427667
"react-dom@link:../node_modules/react-dom":
76437668
version "0.0.0"
7669+
uid ""
76447670

76457671
react-error-overlay@^6.0.6, react-error-overlay@^6.0.7:
76467672
version "6.0.7"
76477673
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108"
76487674
integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA==
76497675

7650-
react-is@^16.8.4:
7676+
react-is@^16.8.1, react-is@^16.8.4:
76517677
version "16.13.1"
76527678
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
76537679
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
76547680

76557681
"react-lazy-svg@link:..":
7656-
version "0.1.0"
7682+
version "0.0.0"
7683+
uid ""
76577684

76587685
"react@link:../node_modules/react":
76597686
version "0.0.0"
7687+
uid ""
76607688

76617689
read-pkg-up@^4.0.0:
76627690
version "4.0.0"
@@ -8094,6 +8122,14 @@ sax@^1.2.4, sax@~1.2.4:
80948122
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
80958123
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
80968124

8125+
scheduler@^0.19.1:
8126+
version "0.19.1"
8127+
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
8128+
integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
8129+
dependencies:
8130+
loose-envify "^1.1.0"
8131+
object-assign "^4.1.1"
8132+
80978133
schema-utils@^1.0.0:
80988134
version "1.0.0"
80998135
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"

package.json

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
{
22
"name": "react-lazy-svg",
33
"license": "MIT",
4-
"version": "0.1.0",
4+
"repository": {
5+
"url": "https://github.com/kaoDev/react-lazy-svg"
6+
},
7+
"author": {
8+
"name": "Kalle Ott",
9+
"url": "https://github.com/kaoDev/"
10+
},
11+
"version": "1.0.0",
512
"main": "dist/index.js",
613
"module": "dist/react-lazy-svg.esm.js",
714
"typings": "dist/index.d.ts",
@@ -27,22 +34,22 @@
2734
"printWidth": 80,
2835
"semi": true,
2936
"singleQuote": true,
30-
"trailingComma": "es5"
37+
"trailingComma": "all",
38+
"proseWrap": "always"
3139
},
3240
"devDependencies": {
33-
"@testing-library/jest-dom": "^5.9.0",
34-
"@testing-library/react": "^10.0.6",
35-
"@types/axios": "^0.14.0",
36-
"@types/jest": "^25.2.3",
37-
"@types/react": "^16.9.35",
41+
"@testing-library/jest-dom": "^5.11.0",
42+
"@testing-library/react": "^10.4.3",
43+
"@types/jest": "^26.0.3",
44+
"@types/react": "^16.9.41",
3845
"@types/react-dom": "^16.9.8",
3946
"husky": "^4.2.5",
47+
"prettier": "^2.0.5",
4048
"react": "^16.13.1",
4149
"react-dom": "^16.13.1",
42-
"rimraf": "^3.0.2",
4350
"tsdx": "^0.13.2",
4451
"tslib": "^2.0.0",
45-
"typescript": "^3.9.3"
52+
"typescript": "^3.9.5"
4653
},
4754
"dependencies": {}
4855
}

src/index.tsx

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ const globalIconsCache: IconsCache = new Map();
1717

1818
export const renderSpriteSheetToString = async (
1919
markupString: string,
20-
knownIcons: IconsCache
20+
knownIcons: IconsCache,
2121
) => {
2222
const arr = await Promise.all(Array.from(knownIcons.values()));
2323

2424
const spriteSheet = renderToStaticMarkup(
25-
<SpriteSheet icons={arr.filter((a): a is IconData => a != null)} />
25+
<SpriteSheet icons={arr.filter((a): a is IconData => a != null)} />,
2626
);
2727

2828
return markupString.replace(ssrEmptySpriteSheet, spriteSheet);
@@ -34,7 +34,7 @@ export interface IconData {
3434
attributes: { [key: string]: string };
3535
}
3636

37-
const noop = () => void 0;
37+
const noop = () => undefined;
3838

3939
interface SpriteContextValue {
4040
/**
@@ -78,7 +78,7 @@ const addIcon = (icon: IconData) => {
7878

7979
const parseSVG = (
8080
url: string,
81-
svgString: string | undefined
81+
svgString: string | undefined,
8282
): IconData | undefined => {
8383
if (svgString) {
8484
const svgRegex = /<svg([\s\S]*?)>([\s\S]*?)<\/svg>/gim;
@@ -103,7 +103,7 @@ const parseSVG = (
103103
const registerIconInCache = (
104104
url: string,
105105
svgString: string | undefined,
106-
knownIcons: IconsCache
106+
knownIcons: IconsCache,
107107
) => {
108108
const iconData = parseSVG(url, svgString);
109109

@@ -149,12 +149,12 @@ export const SpriteContextProvider: FC<SpriteContext> = ({
149149
if (knownIcons.has(url)) {
150150
return;
151151
}
152-
const iconPromise = loadSVG(url).then(svgString =>
153-
registerIconInCache(url, svgString, knownIcons)
152+
const iconPromise = loadSVG(url).then((svgString) =>
153+
registerIconInCache(url, svgString, knownIcons),
154154
);
155155
knownIcons.set(url, iconPromise);
156156
},
157-
[knownIcons]
157+
[knownIcons],
158158
);
159159

160160
const contextValue = useMemo(() => ({ registerSVG }), [registerSVG]);
@@ -172,7 +172,14 @@ export const Icon: FC<{ url: string } & React.SVGProps<SVGSVGElement>> = ({
172172
...props
173173
}) => {
174174
const { registerSVG } = useContext(spriteContext);
175-
registerSVG(url);
175+
176+
if (typeof document !== 'undefined') {
177+
registerSVG(url);
178+
} else {
179+
useEffect(() => {
180+
registerSVG(url);
181+
}, [url]);
182+
}
176183

177184
return (
178185
<svg {...props}>
@@ -205,7 +212,7 @@ const SpriteSheet: FC<{ icons: IconData[] }> = ({ icons }) => {
205212
dangerouslySetInnerHTML={svgString}
206213
/>
207214
);
208-
}
215+
},
209216
)}
210217
</svg>
211218
);
@@ -218,7 +225,7 @@ const mapNodeAttributes = (rawAttributes: NamedNodeMap) =>
218225

219226
return attributes;
220227
},
221-
{}
228+
{},
222229
);
223230

224231
export const initOnClient = (knownIcons: IconsCache = globalIconsCache) => {
@@ -228,7 +235,7 @@ export const initOnClient = (knownIcons: IconsCache = globalIconsCache) => {
228235
if (spriteSheet) {
229236
const sprites = spriteSheet.querySelectorAll('symbol');
230237

231-
sprites.forEach(node => {
238+
sprites.forEach((node) => {
232239
const { id, attributes: rawAttributes, innerHTML } = node;
233240
const attributes = mapNodeAttributes(rawAttributes);
234241
const iconData = {
@@ -238,7 +245,7 @@ export const initOnClient = (knownIcons: IconsCache = globalIconsCache) => {
238245
};
239246
addIcon(iconData);
240247

241-
knownIcons.set(id, new Promise(resolve => resolve(iconData)));
248+
knownIcons.set(id, new Promise((resolve) => resolve(iconData)));
242249
});
243250
}
244251
};

0 commit comments

Comments
 (0)