react-inlinesvg 3.0.2 → 3.1.0-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,67 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = function(target, all) {
7
+ for(var name in all)__defProp(target, name, {
8
+ get: all[name],
9
+ enumerable: true
10
+ });
11
+ };
12
+ var __copyProps = function(to, from, except, desc) {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
15
+ try {
16
+ var _loop = function() {
17
+ var key = _step.value;
18
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
19
+ get: function() {
20
+ return from[key];
21
+ },
22
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
23
+ });
24
+ };
25
+ for(var _iterator = __getOwnPropNames(from)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true)_loop();
26
+ } catch (err) {
27
+ _didIteratorError = true;
28
+ _iteratorError = err;
29
+ } finally{
30
+ try {
31
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
32
+ _iterator.return();
33
+ }
34
+ } finally{
35
+ if (_didIteratorError) {
36
+ throw _iteratorError;
37
+ }
38
+ }
39
+ }
40
+ }
41
+ return to;
42
+ };
43
+ var __toCommonJS = function(mod) {
44
+ return __copyProps(__defProp({}, "__esModule", {
45
+ value: true
46
+ }), mod);
47
+ };
48
+ // src/provider.tsx
49
+ var provider_exports = {};
50
+ __export(provider_exports, {
51
+ default: function() {
52
+ return CacheProvider;
53
+ }
54
+ });
55
+ module.exports = __toCommonJS(provider_exports);
56
+ var import_react = require("react");
57
+ function CacheProvider(param) {
58
+ var children = param.children;
59
+ (0, import_react.useEffect)(function() {
60
+ window.REACT_INLINESVG_PERSISTENT_CACHE = true;
61
+ return function() {
62
+ delete window.REACT_INLINESVG_PERSISTENT_CACHE;
63
+ };
64
+ }, []);
65
+ return children;
66
+ }
67
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/provider.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAqC;AAYtB,SAAR,cAA+B,EAAE,SAAS,GAAU;AACzD,8BAAU,MAAM;AACd,WAAO,mCAAmC;AAE1C,WAAO,MAAM;AACX,aAAO,OAAO;AAAA,IAChB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO;AACT","sourcesContent":["import { ReactNode, useEffect } from 'react';\n\ninterface Props {\n children: ReactNode;\n}\n\ndeclare global {\n interface Window {\n REACT_INLINESVG_PERSISTENT_CACHE?: boolean;\n }\n}\n\nexport default function CacheProvider({ children }: Props) {\n useEffect(() => {\n window.REACT_INLINESVG_PERSISTENT_CACHE = true;\n\n return () => {\n delete window.REACT_INLINESVG_PERSISTENT_CACHE;\n };\n }, []);\n\n return children;\n}\n"]}
@@ -0,0 +1,15 @@
1
+ // src/provider.tsx
2
+ import { useEffect } from "react";
3
+ function CacheProvider({ children }) {
4
+ useEffect(() => {
5
+ window.REACT_INLINESVG_PERSISTENT_CACHE = true;
6
+ return () => {
7
+ delete window.REACT_INLINESVG_PERSISTENT_CACHE;
8
+ };
9
+ }, []);
10
+ return children;
11
+ }
12
+ export {
13
+ CacheProvider as default
14
+ };
15
+ //# sourceMappingURL=provider.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/provider.tsx"],"sourcesContent":["import { ReactNode, useEffect } from 'react';\n\ninterface Props {\n children: ReactNode;\n}\n\ndeclare global {\n interface Window {\n REACT_INLINESVG_PERSISTENT_CACHE?: boolean;\n }\n}\n\nexport default function CacheProvider({ children }: Props) {\n useEffect(() => {\n window.REACT_INLINESVG_PERSISTENT_CACHE = true;\n\n return () => {\n delete window.REACT_INLINESVG_PERSISTENT_CACHE;\n };\n }, []);\n\n return children;\n}\n"],"mappings":";AAAA,SAAoB,iBAAiB;AAYtB,SAAR,cAA+B,EAAE,SAAS,GAAU;AACzD,YAAU,MAAM;AACd,WAAO,mCAAmC;AAE1C,WAAO,MAAM;AACX,aAAO,OAAO;AAAA,IAChB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO;AACT;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-inlinesvg",
3
- "version": "3.0.2",
3
+ "version": "3.1.0-0",
4
4
  "description": "An SVG loader for React",
5
5
  "author": "Gil Barbara <gilbarbara@gmail.com>",
6
6
  "contributors": [
@@ -22,14 +22,23 @@
22
22
  "url": "https://github.com/gilbarbara/react-inlinesvg/issues"
23
23
  },
24
24
  "homepage": "https://github.com/gilbarbara/react-inlinesvg#readme",
25
- "main": "lib/index.js",
26
- "module": "esm/index.js",
25
+ "main": "./dist/index.js",
26
+ "module": "./dist/index.mjs",
27
+ "exports": {
28
+ ".": {
29
+ "import": "./dist/index.mjs",
30
+ "require": "./dist/index.js"
31
+ },
32
+ "./provider": {
33
+ "import": "./dist/provider.mjs",
34
+ "default": "./dist/provider.js"
35
+ }
36
+ },
27
37
  "files": [
28
- "esm",
29
- "lib",
38
+ "dist",
30
39
  "src"
31
40
  ],
32
- "types": "esm",
41
+ "types": "dist/index.d.ts",
33
42
  "sideEffects": false,
34
43
  "peerDependencies": {
35
44
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
@@ -39,46 +48,46 @@
39
48
  "react-from-dom": "^0.6.2"
40
49
  },
41
50
  "devDependencies": {
42
- "@gilbarbara/eslint-config": "^0.3.7",
43
- "@gilbarbara/helpers": "^0.6.3",
44
- "@gilbarbara/prettier-config": "^0.1.0",
51
+ "@gilbarbara/eslint-config": "^0.5.2",
52
+ "@gilbarbara/helpers": "^0.8.1",
53
+ "@gilbarbara/prettier-config": "^1.0.0",
45
54
  "@gilbarbara/tsconfig": "^0.1.1",
46
- "@size-limit/preset-small-lib": "^8.2.4",
47
- "@testing-library/jest-dom": "^5.16.5",
55
+ "@size-limit/preset-small-lib": "^8.2.6",
56
+ "@swc/core": "^1.3.74",
57
+ "@testing-library/jest-dom": "^5.17.0",
48
58
  "@testing-library/react": "^14.0.0",
49
59
  "@types/exenv": "^1.2.0",
50
60
  "@types/fetch-mock": "^7.3.5",
51
- "@types/jest": "^29.4.0",
52
- "@types/node": "^18.14.1",
53
- "@types/node-fetch": "^2.6.2",
54
- "@types/react": "^18.0.28",
55
- "@types/react-dom": "^18.0.11",
56
- "cross-fetch": "^3.1.5",
61
+ "@types/jest": "^29.5.3",
62
+ "@types/node": "^20.4.8",
63
+ "@types/node-fetch": "^2.6.4",
64
+ "@types/react": "^18.2.18",
65
+ "@types/react-dom": "^18.2.7",
66
+ "browser-cache-mock": "^0.1.7",
67
+ "cross-fetch": "^4.0.0",
57
68
  "del-cli": "^5.0.0",
58
69
  "http-server": "^14.1.1",
59
70
  "husky": "^8.0.3",
60
- "jest": "^29.4.3",
61
- "jest-environment-jsdom": "^29.4.3",
62
- "jest-extended": "^3.2.4",
71
+ "jest": "^29.6.2",
72
+ "jest-environment-jsdom": "^29.6.2",
73
+ "jest-extended": "^4.0.1",
63
74
  "jest-fetch-mock": "^3.0.3",
64
75
  "jest-serializer-html": "^7.1.0",
65
76
  "jest-watch-typeahead": "^2.2.2",
66
77
  "react": "^18.2.0",
67
78
  "react-dom": "^18.2.0",
68
79
  "repo-tools": "^0.2.2",
69
- "size-limit": "^8.2.4",
70
- "start-server-and-test": "^1.15.4",
71
- "ts-jest": "^29.0.5",
80
+ "size-limit": "^8.2.6",
81
+ "start-server-and-test": "^2.0.0",
82
+ "ts-jest": "^29.1.1",
72
83
  "ts-node": "^10.9.1",
73
- "typescript": "^4.9.5"
84
+ "tsup": "^7.2.0",
85
+ "typescript": "^5.1.6"
74
86
  },
75
87
  "scripts": {
76
- "build": "npm run clean && npm run build:cjs && npm run build:esm",
77
- "build:cjs": "tsc",
78
- "build:esm": "tsc -m es6 --outDir esm",
79
- "clean": "del lib/* && del esm/*",
80
- "watch:cjs": "npm run build:cjs -- -w",
81
- "watch:esm": "npm run build:esm -- -w",
88
+ "build": "npm run clean && tsup",
89
+ "watch": "tsup --watch",
90
+ "clean": "del dist/*",
82
91
  "start": "http-server test/__fixtures__ -p 1337 --cors",
83
92
  "test": "start-server-and-test start http://127.0.0.1:1337 test:coverage",
84
93
  "test:coverage": "jest --coverage --bail",
@@ -90,6 +99,19 @@
90
99
  "prepublishOnly": "npm run validate",
91
100
  "prepare": "husky install"
92
101
  },
102
+ "tsup": {
103
+ "dts": true,
104
+ "entry": [
105
+ "src/index.tsx",
106
+ "src/provider.tsx"
107
+ ],
108
+ "format": [
109
+ "cjs",
110
+ "esm"
111
+ ],
112
+ "sourcemap": true,
113
+ "splitting": false
114
+ },
93
115
  "eslintConfig": {
94
116
  "extends": [
95
117
  "@gilbarbara/eslint-config"
@@ -111,14 +133,14 @@
111
133
  "prettier": "@gilbarbara/prettier-config",
112
134
  "size-limit": [
113
135
  {
114
- "name": "lib",
115
- "path": "./lib/index.js",
116
- "limit": "9 kB"
136
+ "name": "commonjs",
137
+ "path": "./dist/index.js",
138
+ "limit": "11 KB"
117
139
  },
118
140
  {
119
141
  "name": "esm",
120
- "path": "./esm/index.js",
121
- "limit": "9 kB"
142
+ "path": "./dist/index.mjs",
143
+ "limit": "9 KB"
122
144
  }
123
145
  ]
124
146
  }
package/src/cache.ts ADDED
@@ -0,0 +1,164 @@
1
+ import { CACHE_MAX_RETRIES, CACHE_NAME, STATUS } from './config';
2
+ import { request, sleep } from './helpers';
3
+ import { StorageItem } from './types';
4
+
5
+ export default class CacheStore {
6
+ private cacheApi: Cache | undefined;
7
+ private readonly cacheStore: Map<string, StorageItem>;
8
+ private readonly subscribers: Array<() => void> = [];
9
+ private readonly usePersistentCache: boolean;
10
+ public isReady = false;
11
+
12
+ constructor() {
13
+ this.cacheStore = new Map<string, StorageItem>();
14
+
15
+ this.usePersistentCache =
16
+ 'REACT_INLINESVG_PERSISTENT_CACHE' in window && !!window.REACT_INLINESVG_PERSISTENT_CACHE;
17
+
18
+ if (this.usePersistentCache) {
19
+ caches.open(CACHE_NAME).then(cache => {
20
+ this.cacheApi = cache;
21
+ this.isReady = true;
22
+
23
+ this.subscribers.forEach(callback => callback());
24
+ });
25
+ } else {
26
+ this.isReady = true;
27
+ }
28
+ }
29
+
30
+ public onReady(callback: () => void) {
31
+ if (this.isReady) {
32
+ callback();
33
+ } else {
34
+ this.subscribers.push(callback);
35
+ }
36
+ }
37
+
38
+ public async get(url: string, fetchOptions?: RequestInit) {
39
+ await (this.usePersistentCache
40
+ ? this.fetchAndAddToPersistentCache(url, fetchOptions)
41
+ : this.fetchAndAddToInternalCache(url, fetchOptions));
42
+
43
+ return this.cacheStore.get(url)?.content ?? '';
44
+ }
45
+
46
+ public set(url: string, data: StorageItem) {
47
+ this.cacheStore.set(url, data);
48
+ }
49
+
50
+ public isCached(url: string) {
51
+ return this.cacheStore.get(url)?.status === STATUS.LOADED;
52
+ }
53
+
54
+ private async fetchAndAddToInternalCache(url: string, fetchOptions?: RequestInit) {
55
+ const cache = this.cacheStore.get(url);
56
+
57
+ if (cache?.status === STATUS.LOADING) {
58
+ await this.handleLoading(url, async () => {
59
+ this.cacheStore.set(url, { content: '', status: STATUS.IDLE });
60
+ await this.fetchAndAddToInternalCache(url, fetchOptions);
61
+ });
62
+
63
+ return;
64
+ }
65
+
66
+ if (!cache?.content) {
67
+ this.cacheStore.set(url, { content: '', status: STATUS.LOADING });
68
+
69
+ try {
70
+ const content = await request(url, fetchOptions);
71
+
72
+ this.cacheStore.set(url, { content, status: STATUS.LOADED });
73
+ } catch (error: any) {
74
+ this.cacheStore.set(url, { content: '', status: STATUS.FAILED });
75
+ throw error;
76
+ }
77
+ }
78
+ }
79
+
80
+ private async fetchAndAddToPersistentCache(url: string, fetchOptions?: RequestInit) {
81
+ const cache = this.cacheStore.get(url);
82
+
83
+ if (cache?.status === STATUS.LOADED) {
84
+ return;
85
+ }
86
+
87
+ if (cache?.status === STATUS.LOADING) {
88
+ await this.handleLoading(url, async () => {
89
+ this.cacheStore.set(url, { content: '', status: STATUS.IDLE });
90
+ await this.fetchAndAddToPersistentCache(url, fetchOptions);
91
+ });
92
+
93
+ return;
94
+ }
95
+
96
+ this.cacheStore.set(url, { content: '', status: STATUS.LOADING });
97
+
98
+ const data = await this.cacheApi?.match(url);
99
+
100
+ if (data) {
101
+ const content = await data.text();
102
+
103
+ this.cacheStore.set(url, { content, status: STATUS.LOADED });
104
+
105
+ return;
106
+ }
107
+
108
+ try {
109
+ await this.cacheApi?.add(new Request(url, fetchOptions));
110
+
111
+ const response = await this.cacheApi?.match(url);
112
+ const content = (await response?.text()) ?? '';
113
+
114
+ this.cacheStore.set(url, { content, status: STATUS.LOADED });
115
+ } catch (error: any) {
116
+ this.cacheStore.set(url, { content: '', status: STATUS.FAILED });
117
+ throw error;
118
+ }
119
+ }
120
+
121
+ private async handleLoading(url: string, callback: () => Promise<void>) {
122
+ let retryCount = 0;
123
+
124
+ // eslint-disable-next-line no-await-in-loop
125
+ while (this.cacheStore.get(url)?.status === STATUS.LOADING && retryCount < CACHE_MAX_RETRIES) {
126
+ // eslint-disable-next-line no-await-in-loop
127
+ await sleep(0.1);
128
+ retryCount += 1;
129
+ }
130
+
131
+ if (retryCount >= CACHE_MAX_RETRIES) {
132
+ await callback();
133
+ }
134
+ }
135
+
136
+ public keys(): Array<string> {
137
+ return [...this.cacheStore.keys()];
138
+ }
139
+
140
+ public data(): Array<Record<string, StorageItem>> {
141
+ return [...this.cacheStore.entries()].map(([key, value]) => ({ [key]: value }));
142
+ }
143
+
144
+ public async delete(url: string) {
145
+ if (this.cacheApi) {
146
+ await this.cacheApi.delete(url);
147
+ }
148
+
149
+ this.cacheStore.delete(url);
150
+ }
151
+
152
+ public async clear() {
153
+ if (this.cacheApi) {
154
+ const keys = await this.cacheApi.keys();
155
+
156
+ for (const key of keys) {
157
+ // eslint-disable-next-line no-await-in-loop
158
+ await this.cacheApi.delete(key);
159
+ }
160
+ }
161
+
162
+ this.cacheStore.clear();
163
+ }
164
+ }
package/src/config.ts ADDED
@@ -0,0 +1,11 @@
1
+ export const CACHE_NAME = 'react-inlinesvg';
2
+ export const CACHE_MAX_RETRIES = 10;
3
+
4
+ export const STATUS = {
5
+ IDLE: 'idle',
6
+ LOADING: 'loading',
7
+ LOADED: 'loaded',
8
+ FAILED: 'failed',
9
+ READY: 'ready',
10
+ UNSUPPORTED: 'unsupported',
11
+ } as const;
package/src/helpers.ts CHANGED
@@ -1,15 +1,6 @@
1
1
  import { canUseDOM as canUseDOMFlag } from 'exenv';
2
2
 
3
- import { PlainObject } from './types';
4
-
5
- export const STATUS = {
6
- FAILED: 'failed',
7
- LOADED: 'loaded',
8
- LOADING: 'loading',
9
- PENDING: 'pending',
10
- READY: 'ready',
11
- UNSUPPORTED: 'unsupported',
12
- };
3
+ import type { PlainObject } from './types';
13
4
 
14
5
  export function canUseDOM(): boolean {
15
6
  return canUseDOMFlag;
@@ -19,6 +10,28 @@ export function isSupportedEnvironment(): boolean {
19
10
  return supportsInlineSVG() && typeof window !== 'undefined' && window !== null;
20
11
  }
21
12
 
13
+ export async function request(url: string, options?: RequestInit) {
14
+ const response = await fetch(url, options);
15
+ const contentType = response.headers.get('content-type');
16
+ const [fileType] = (contentType || '').split(/ ?; ?/);
17
+
18
+ if (response.status > 299) {
19
+ throw new Error('Not found');
20
+ }
21
+
22
+ if (!['image/svg+xml', 'text/plain'].some(d => fileType.includes(d))) {
23
+ throw new Error(`Content type isn't valid: ${fileType}`);
24
+ }
25
+
26
+ return response.text();
27
+ }
28
+
29
+ export function sleep(seconds = 1) {
30
+ return new Promise(resolve => {
31
+ setTimeout(resolve, seconds * 1000);
32
+ });
33
+ }
34
+
22
35
  export function supportsInlineSVG(): boolean {
23
36
  /* istanbul ignore next */
24
37
  if (!document) {