Repository: svg-sprite/svg-sprite
Pull request #620 (2022-03-20 07:49:15)

Replace `_.escape` with native JS
Refs #604

- [x] confirm this is the same as the lodash method
- [x] see if we can use `entityMap`'s keys
- [x] add more tests?
- [x] should not throw should use `not.throw`

An alternative solution would be to go with a separate (small) package, which is an acceptable solution too, assuming we finish with all other lodash instances.

Kreeg: @XhmikosR OK, there is no problem to squash them, I just though that it would be nice to keep commits from different authors.  
As I think the current realization is decent.

Patch:
diff --git a/lib/svg-sprite/sprite.js b/lib/svg-sprite/sprite.js
index 1497ff7..e8cb07e 100644
--- a/lib/svg-sprite/sprite.js
+++ b/lib/svg-sprite/sprite.js
@@ -10,9 +10,8 @@
  */
 
 const { Buffer } = require('buffer');
-const _ = require('lodash');
 const File = require('vinyl');
-const { isFunction } = require('./utils/index.js');
+const { escapeHtml, isFunction } = require('./utils/index.js');
 
 module.exports = class SVGSprite {
     /**
@@ -69,7 +68,7 @@ module.exports = class SVGSprite {
         let svg = this.xmlDeclaration + this.doctypeDeclaration;
         svg += '<svg';
         for (const attr in this.rootAttributes) {
-            svg += ` ${attr}="${_.escape(this.rootAttributes[attr])}"`;
+            svg += ` ${attr}="${escapeHtml(this.rootAttributes[attr])}"`;
         }
 
         svg += '>';
diff --git a/lib/svg-sprite/utils/index.js b/lib/svg-sprite/utils/index.js
index 4affcc5..9940df8 100644
--- a/lib/svg-sprite/utils/index.js
+++ b/lib/svg-sprite/utils/index.js
@@ -1,5 +1,25 @@
 'use strict';
 
+/**
+ * Escapes HTML characters
+ *
+ * @param {string} str  The string to escape.
+ * @returns {string}    Returns the escaped string.
+ */
+function escapeHtml(str) {
+    const entityMap = {
+        '&': '&amp;',
+        '<': '&lt;',
+        '>': '&gt;',
+        '"': '&quot;',
+        '\'': '&#39;',
+        '/': '&#x2F;'
+    };
+    const regExp = new RegExp(`[${Object.keys(entityMap).join('')}]`, 'g');
+
+    return String(str).replace(regExp, s => entityMap[s]);
+}
+
 /**
  * Checks if value is a callable function.
  *
@@ -55,6 +75,7 @@ function zipObject(array1, array2) {
 }
 
 module.exports = {
+    escapeHtml,
     isFunction,
     isObject,
     isPlainObject,


Test patch:
diff --git a/test/utils.test.js b/test/utils.test.js
index af6662f..b23b4e5 100644
--- a/test/utils.test.js
+++ b/test/utils.test.js
@@ -3,14 +3,34 @@
 /* eslint-disable unicorn/new-for-builtins, no-new-wrappers, prefer-regex-literals, jest/prefer-expect-assertions */
 
 const {
+    escapeHtml,
     isFunction,
     isObject,
     isString,
-    isPlainObject,
+    isPlainObject
+    ,
     zipObject
 } = require('../lib/svg-sprite/utils/index.js');
 
 describe('utils', () => {
+    describe('escapeHtml', () => {
+        it('should escape HTML characters', () => {
+            expect(escapeHtml('fred, barney, & pebbles')).toBe('fred, barney, &amp; pebbles');
+            expect(escapeHtml('<div class="test" />')).toBe('&lt;div class=&quot;test&quot; &#x2F;&gt;');
+            expect(escapeHtml('<span id=\'test\' />')).toBe('&lt;span id=&#39;test&#39; &#x2F;&gt;');
+        });
+
+        it('should return empty string with empty string passed', () => {
+            expect(escapeHtml('')).toBe('');
+        });
+
+        it('should not escape any additional characters', () => {
+            const TEST_STRING = 'My salary increased by 20% up to whopping $10000 per year :(';
+
+            expect(escapeHtml(TEST_STRING)).toBe(TEST_STRING);
+        });
+    });
+
     describe('isFunction', () => {
         it('should return true for a class', () => {
             expect(isFunction(class {})).toBe(true);