Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-inlinesvg

Package Overview
Dependencies
Maintainers
1
Versions
78
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-inlinesvg - npm Package Compare versions

Comparing version 4.0.6 to 4.1.0-0

src/hooks.tsx

9

dist/index.d.ts

@@ -17,3 +17,3 @@ import * as react_jsx_runtime from 'react/jsx-runtime';

type PreProcessorCallback = (code: string) => string;
interface Props extends Omit<React.SVGProps<SVGElement>, 'onLoad' | 'onError' | 'ref'> {
type Props = Simplify<Omit<React.SVGProps<SVGElement>, 'onLoad' | 'onError' | 'ref'> & {
baseURL?: string;

@@ -33,3 +33,3 @@ cacheRequests?: boolean;

uniquifyIDs?: boolean;
}
}>;
interface State {

@@ -47,2 +47,5 @@ content: string;

}
type Simplify<T> = {
[KeyType in keyof T]: T[KeyType];
} & {};
type Status = (typeof STATUS)[keyof typeof STATUS];

@@ -76,3 +79,3 @@ interface StorageItem {

export { ErrorCallback, FetchError, LoadCallback, PlainObject, PreProcessorCallback, Props, State, Status, StorageItem, cacheStore, InlineSVG as default };
export { ErrorCallback, FetchError, LoadCallback, PlainObject, PreProcessorCallback, Props, Simplify, State, Status, StorageItem, cacheStore, InlineSVG as default };
export = InlineSVG

@@ -42,3 +42,3 @@ "use strict";

module.exports = __toCommonJS(src_exports);
var React = __toESM(require("react"));
var import_react2 = require("react");
var import_react_from_dom = __toESM(require("react-from-dom"));

@@ -242,255 +242,298 @@

// src/hooks.tsx
var import_react = require("react");
function usePrevious(state) {
const ref = (0, import_react.useRef)();
(0, import_react.useEffect)(() => {
ref.current = state;
});
return ref.current;
}
// src/index.tsx
var import_jsx_runtime = require("react/jsx-runtime");
var cacheStore;
var ReactInlineSVG = class extends React.PureComponent {
constructor(props) {
super(props);
__publicField(this, "hash");
__publicField(this, "isActive", false);
__publicField(this, "isInitialized", false);
__publicField(this, "fetchContent", async () => {
const { fetchOptions, src } = this.props;
const content = await request(src, fetchOptions);
this.handleLoad(content);
});
__publicField(this, "handleError", (error) => {
const { onError } = this.props;
const status = error.message === "Browser does not support SVG" ? STATUS.UNSUPPORTED : STATUS.FAILED;
if (this.isActive) {
this.setState({ status }, () => {
if (typeof onError === "function") {
onError(error);
}
});
}
});
__publicField(this, "handleLoad", (content, hasCache = false) => {
if (this.isActive) {
this.setState(
{
content,
isCached: hasCache,
status: STATUS.LOADED
},
this.getElement
);
}
});
this.state = {
content: "",
element: null,
isCached: !!props.cacheRequests && cacheStore.isCached(props.src),
status: STATUS.IDLE
};
this.hash = props.uniqueHash ?? randomString(8);
function updateSVGAttributes(node, options) {
const { baseURL = "", hash, uniquifyIDs } = options;
const replaceableAttributes = ["id", "href", "xlink:href", "xlink:role", "xlink:arcrole"];
const linkAttributes = ["href", "xlink:href"];
const isDataValue = (name, value) => linkAttributes.includes(name) && (value ? !value.includes("#") : false);
if (!uniquifyIDs) {
return node;
}
componentDidMount() {
this.isActive = true;
if (!canUseDOM() || this.isInitialized) {
return;
}
const { status } = this.state;
const { src } = this.props;
try {
if (status === STATUS.IDLE) {
if (!isSupportedEnvironment()) {
throw new Error("Browser does not support SVG");
[...node.children].forEach((d) => {
if (d.attributes?.length) {
const attributes = Object.values(d.attributes).map((a) => {
const attribute = a;
const match = /url\((.*?)\)/.exec(a.value);
if (match?.[1]) {
attribute.value = a.value.replace(match[0], `url(${baseURL}${match[1]}__${hash})`);
}
if (!src) {
throw new Error("Missing src");
return attribute;
});
replaceableAttributes.forEach((r) => {
const attribute = attributes.find((a) => a.name === r);
if (attribute && !isDataValue(r, attribute.value)) {
attribute.value = `${attribute.value}__${hash}`;
}
this.load();
}
} catch (error) {
this.handleError(error);
});
}
this.isInitialized = true;
}
componentDidUpdate(previousProps, previousState) {
if (!canUseDOM()) {
return;
if (d.children.length) {
return updateSVGAttributes(d, options);
}
const { isCached, status } = this.state;
const { description, onLoad, src, title } = this.props;
if (previousState.status !== STATUS.READY && status === STATUS.READY) {
if (onLoad) {
onLoad(src, isCached);
return d;
});
return node;
}
function getNode(options) {
const {
baseURL,
content,
description,
handleError,
hash,
preProcessor,
title,
uniquifyIDs = false
} = options;
try {
const svgText = processSVG(content, preProcessor);
const node = (0, import_react_from_dom.default)(svgText, { nodeOnly: true });
if (!node || !(node instanceof SVGSVGElement)) {
throw new Error("Could not convert the src to a DOM Node");
}
const svg = updateSVGAttributes(node, { baseURL, hash, uniquifyIDs });
if (description) {
const originalDesc = svg.querySelector("desc");
if (originalDesc?.parentNode) {
originalDesc.parentNode.removeChild(originalDesc);
}
const descElement = document.createElementNS("http://www.w3.org/2000/svg", "desc");
descElement.innerHTML = description;
svg.prepend(descElement);
}
if (previousProps.src !== src) {
if (!src) {
this.handleError(new Error("Missing src"));
return;
if (typeof title !== "undefined") {
const originalTitle = svg.querySelector("title");
if (originalTitle?.parentNode) {
originalTitle.parentNode.removeChild(originalTitle);
}
this.load();
if (title) {
const titleElement = document.createElementNS("http://www.w3.org/2000/svg", "title");
titleElement.innerHTML = title;
svg.prepend(titleElement);
}
}
if (previousProps.title !== title || previousProps.description !== description) {
this.getElement();
}
return svg;
} catch (error) {
return handleError(error);
}
componentWillUnmount() {
this.isActive = false;
}
function processSVG(content, preProcessor) {
if (preProcessor) {
return preProcessor(content);
}
getElement() {
return content;
}
function ReactInlineSVG(props) {
const {
cacheRequests = true,
children = null,
description,
fetchOptions,
innerRef,
loader = null,
onError,
onLoad,
src,
title,
uniqueHash
} = props;
const [state, setState] = (0, import_react2.useReducer)(
(previousState2, nextState) => ({
...previousState2,
...nextState
}),
{
content: "",
element: null,
isCached: cacheRequests && cacheStore.isCached(props.src),
status: STATUS.IDLE
}
);
const { content, element, isCached, status } = state;
const previousProps = usePrevious(props);
const previousState = usePrevious(state);
const hash = (0, import_react2.useRef)(uniqueHash ?? randomString(8));
const isActive = (0, import_react2.useRef)(false);
const isInitialized = (0, import_react2.useRef)(false);
const handleError = (0, import_react2.useCallback)(
(error) => {
if (isActive.current) {
setState({
status: error.message === "Browser does not support SVG" ? STATUS.UNSUPPORTED : STATUS.FAILED
});
onError?.(error);
}
},
[onError]
);
const handleLoad = (0, import_react2.useCallback)((loadedContent, hasCache = false) => {
if (isActive.current) {
setState({
content: loadedContent,
isCached: hasCache,
status: STATUS.LOADED
});
}
}, []);
const getElement = (0, import_react2.useCallback)(() => {
try {
const node = this.getNode();
const element = (0, import_react_from_dom.default)(node);
if (!element || !React.isValidElement(element)) {
const node = getNode({ ...props, handleError, hash: hash.current, content });
const convertedElement = (0, import_react_from_dom.default)(node);
if (!convertedElement || !(0, import_react2.isValidElement)(convertedElement)) {
throw new Error("Could not convert the src to a React element");
}
this.setState({
element,
setState({
element: convertedElement,
status: STATUS.READY
});
} catch (error) {
this.handleError(new Error(error.message));
handleError(new Error(error.message));
}
}
getNode() {
const { description, title } = this.props;
}, [content, handleError, props]);
const fetchContent = (0, import_react2.useCallback)(async () => {
const responseContent = await request(src, fetchOptions);
handleLoad(responseContent);
}, [fetchOptions, handleLoad, src]);
const getContent = (0, import_react2.useCallback)(async () => {
const dataURI = /^data:image\/svg[^,]*?(;base64)?,(.*)/u.exec(src);
let inlineSrc;
if (dataURI) {
inlineSrc = dataURI[1] ? window.atob(dataURI[2]) : decodeURIComponent(dataURI[2]);
} else if (src.includes("<svg")) {
inlineSrc = src;
}
if (inlineSrc) {
handleLoad(inlineSrc);
return;
}
try {
const svgText = this.processSVG();
const node = (0, import_react_from_dom.default)(svgText, { nodeOnly: true });
if (!node || !(node instanceof SVGSVGElement)) {
throw new Error("Could not convert the src to a DOM Node");
if (cacheRequests) {
const cachedContent = await cacheStore.get(src, fetchOptions);
handleLoad(cachedContent, true);
} else {
await fetchContent();
}
const svg = this.updateSVGAttributes(node);
if (description) {
const originalDesc = svg.querySelector("desc");
if (originalDesc?.parentNode) {
originalDesc.parentNode.removeChild(originalDesc);
}
const descElement = document.createElementNS("http://www.w3.org/2000/svg", "desc");
descElement.innerHTML = description;
svg.prepend(descElement);
}
if (typeof title !== "undefined") {
const originalTitle = svg.querySelector("title");
if (originalTitle?.parentNode) {
originalTitle.parentNode.removeChild(originalTitle);
}
if (title) {
const titleElement = document.createElementNS("http://www.w3.org/2000/svg", "title");
titleElement.innerHTML = title;
svg.prepend(titleElement);
}
}
return svg;
} catch (error) {
return this.handleError(error);
handleError(error);
}
}
load() {
if (this.isActive) {
this.setState(
{
content: "",
element: null,
isCached: false,
status: STATUS.LOADING
},
async () => {
const { cacheRequests, fetchOptions, src } = this.props;
const dataURI = /^data:image\/svg[^,]*?(;base64)?,(.*)/u.exec(src);
let inlineSrc;
if (dataURI) {
inlineSrc = dataURI[1] ? window.atob(dataURI[2]) : decodeURIComponent(dataURI[2]);
} else if (src.includes("<svg")) {
inlineSrc = src;
}, [cacheRequests, fetchContent, fetchOptions, handleError, handleLoad, src]);
const load = (0, import_react2.useCallback)(async () => {
if (isActive.current) {
setState({
content: "",
element: null,
isCached: false,
status: STATUS.LOADING
});
}
}, []);
(0, import_react2.useEffect)(
() => {
isActive.current = true;
if (!canUseDOM() || isInitialized.current) {
return () => void 0;
}
try {
if (status === STATUS.IDLE) {
if (!isSupportedEnvironment()) {
throw new Error("Browser does not support SVG");
}
if (inlineSrc) {
this.handleLoad(inlineSrc);
return;
if (!src) {
throw new Error("Missing src");
}
try {
if (cacheRequests) {
const content = await cacheStore.get(src, fetchOptions);
this.handleLoad(content, true);
} else {
await this.fetchContent();
}
} catch (error) {
this.handleError(error);
}
load();
}
);
} catch (error) {
handleError(error);
}
isInitialized.current = true;
return () => {
isActive.current = false;
};
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
(0, import_react2.useEffect)(() => {
if (!canUseDOM()) {
return;
}
}
processSVG() {
const { content } = this.state;
const { preProcessor } = this.props;
if (preProcessor) {
return preProcessor(content);
if (!previousProps) {
return;
}
return content;
}
updateSVGAttributes(node) {
const { baseURL = "", uniquifyIDs } = this.props;
const replaceableAttributes = ["id", "href", "xlink:href", "xlink:role", "xlink:arcrole"];
const linkAttributes = ["href", "xlink:href"];
const isDataValue = (name, value) => linkAttributes.includes(name) && (value ? !value.includes("#") : false);
if (!uniquifyIDs) {
return node;
}
[...node.children].forEach((d) => {
if (d.attributes?.length) {
const attributes = Object.values(d.attributes).map((a) => {
const attribute = a;
const match = /url\((.*?)\)/.exec(a.value);
if (match?.[1]) {
attribute.value = a.value.replace(match[0], `url(${baseURL}${match[1]}__${this.hash})`);
}
return attribute;
});
replaceableAttributes.forEach((r) => {
const attribute = attributes.find((a) => a.name === r);
if (attribute && !isDataValue(r, attribute.value)) {
attribute.value = `${attribute.value}__${this.hash}`;
}
});
if (previousProps.src !== src) {
if (!src) {
handleError(new Error("Missing src"));
return;
}
if (d.children.length) {
return this.updateSVGAttributes(d);
}
return d;
});
return node;
}
render() {
const { element, status } = this.state;
const { children = null, innerRef, loader = null } = this.props;
const elementProps = omit(
this.props,
"baseURL",
"cacheRequests",
"children",
"description",
"fetchOptions",
"innerRef",
"loader",
"onError",
"onLoad",
"preProcessor",
"src",
"title",
"uniqueHash",
"uniquifyIDs"
);
if (!canUseDOM()) {
return loader;
load();
} else if (previousProps.title !== title || previousProps.description !== description) {
getElement();
}
if (element) {
return React.cloneElement(element, { ref: innerRef, ...elementProps });
}, [
description,
getElement,
handleError,
isCached,
load,
onLoad,
previousProps,
previousState,
src,
status,
title
]);
(0, import_react2.useEffect)(() => {
if (!previousState) {
return;
}
if ([STATUS.UNSUPPORTED, STATUS.FAILED].includes(status)) {
return children;
if (previousState.status !== STATUS.LOADING && status === STATUS.LOADING) {
getContent();
}
if (previousState.status !== STATUS.LOADED && status === STATUS.LOADED) {
getElement();
}
if (previousState.status !== STATUS.READY && status === STATUS.READY) {
onLoad?.(src, isCached);
}
}, [getContent, getElement, isCached, onLoad, previousState, src, status]);
const elementProps = omit(
props,
"baseURL",
"cacheRequests",
"children",
"description",
"fetchOptions",
"innerRef",
"loader",
"onError",
"onLoad",
"preProcessor",
"src",
"title",
"uniqueHash",
"uniquifyIDs"
);
if (!canUseDOM()) {
return loader;
}
};
__publicField(ReactInlineSVG, "defaultProps", {
cacheRequests: true,
uniquifyIDs: false
});
if (element) {
return (0, import_react2.cloneElement)(element, { ref: innerRef, ...elementProps });
}
if ([STATUS.UNSUPPORTED, STATUS.FAILED].includes(status)) {
return children;
}
return loader;
}
function InlineSVG(props) {

@@ -501,5 +544,5 @@ if (!cacheStore) {

const { loader } = props;
const hasCallback = React.useRef(false);
const [isReady, setReady] = React.useState(cacheStore.isReady);
React.useEffect(() => {
const hasCallback = (0, import_react2.useRef)(false);
const [isReady, setReady] = (0, import_react2.useState)(cacheStore.isReady);
(0, import_react2.useEffect)(() => {
if (!hasCallback.current) {

@@ -506,0 +549,0 @@ cacheStore.onReady(() => {

{
"name": "react-inlinesvg",
"version": "4.0.6",
"version": "4.1.0-0",
"description": "An SVG loader for React",

@@ -51,3 +51,3 @@ "author": "Gil Barbara <gilbarbara@gmail.com>",

"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
"react": "16.8 - 18"
},

@@ -59,14 +59,11 @@ "dependencies": {

"@gilbarbara/eslint-config": "^0.5.4",
"@gilbarbara/helpers": "^0.8.6",
"@gilbarbara/prettier-config": "^1.0.0",
"@gilbarbara/tsconfig": "^0.2.3",
"@size-limit/preset-small-lib": "^9.0.0",
"@size-limit/preset-small-lib": "^10.0.1",
"@testing-library/jest-dom": "^6.1.4",
"@testing-library/react": "^14.0.0",
"@types/exenv": "^1.2.0",
"@types/fetch-mock": "^7.3.6",
"@types/node": "^20.8.2",
"@types/node-fetch": "^2.6.6",
"@types/react": "^18.2.24",
"@types/react-dom": "^18.2.8",
"@types/node": "^20.8.8",
"@types/react": "^18.2.31",
"@types/react-dom": "^18.2.14",
"@vitejs/plugin-react": "^4.1.0",
"@vitest/coverage-v8": "^0.34.6",

@@ -82,4 +79,4 @@ "browser-cache-mock": "^0.1.7",

"react-dom": "^18.2.0",
"repo-tools": "^0.2.2",
"size-limit": "^9.0.0",
"repo-tools": "^0.3.1",
"size-limit": "^10.0.1",
"start-server-and-test": "^2.0.1",

@@ -86,0 +83,0 @@ "ts-node": "^10.9.1",

@@ -10,18 +10,20 @@ import * as React from 'react';

export interface Props extends Omit<React.SVGProps<SVGElement>, 'onLoad' | 'onError' | 'ref'> {
baseURL?: string;
cacheRequests?: boolean;
children?: React.ReactNode;
description?: string;
fetchOptions?: RequestInit;
innerRef?: React.Ref<SVGElement>;
loader?: React.ReactNode;
onError?: ErrorCallback;
onLoad?: LoadCallback;
preProcessor?: PreProcessorCallback;
src: string;
title?: string | null;
uniqueHash?: string;
uniquifyIDs?: boolean;
}
export type Props = Simplify<
Omit<React.SVGProps<SVGElement>, 'onLoad' | 'onError' | 'ref'> & {
baseURL?: string;
cacheRequests?: boolean;
children?: React.ReactNode;
description?: string;
fetchOptions?: RequestInit;
innerRef?: React.Ref<SVGElement>;
loader?: React.ReactNode;
onError?: ErrorCallback;
onLoad?: LoadCallback;
preProcessor?: PreProcessorCallback;
src: string;
title?: string | null;
uniqueHash?: string;
uniquifyIDs?: boolean;
}
>;

@@ -42,2 +44,4 @@ export interface State {

export type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {};
export type Status = (typeof STATUS)[keyof typeof STATUS];

@@ -44,0 +48,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc