diff --git a/.changeset/yellow-shirts-stare.md b/.changeset/yellow-shirts-stare.md
new file mode 100644
index 0000000..e1ec98a
--- /dev/null
+++ b/.changeset/yellow-shirts-stare.md
@@ -0,0 +1,5 @@
+---
+'@graphcms/rich-text-react-renderer': patch
+---
+
+Fix heading with links not being rendered
diff --git a/.eslintrc.js b/.eslintrc.js
index cde48d5..f1429ae 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,10 +1,6 @@
module.exports = {
- extends: [
- 'react-app',
- 'prettier/@typescript-eslint',
- 'plugin:prettier/recommended',
- ],
- plugins: ['testing-library', 'jest-dom'],
+ extends: ['react-app', 'prettier/@typescript-eslint', 'prettier'],
+ plugins: ['testing-library', 'jest-dom', 'prettier'],
settings: {
react: {
version: '999.999.999',
diff --git a/package.json b/package.json
index da473fb..436041e 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,6 @@
"husky": "^6.0.0",
"lerna": "^3.15.0",
"lint-staged": "^11.0.0",
- "prettier": "^2.3.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"size-limit": "^4.10.2",
diff --git a/packages/html-to-slate-ast/src/index.ts b/packages/html-to-slate-ast/src/index.ts
index 70dec9e..a82c1b3 100644
--- a/packages/html-to-slate-ast/src/index.ts
+++ b/packages/html-to-slate-ast/src/index.ts
@@ -6,7 +6,7 @@ import {
} from 'slate';
import { jsx } from 'slate-hyperscript';
import { sanitizeUrl } from '@braintree/sanitize-url';
-import type { Element, Mark } from '@graphcms/rich-text-types';
+import { Element, Mark } from '@graphcms/rich-text-types';
const ELEMENT_TAGS: Record<
HTMLElement['nodeName'],
@@ -16,7 +16,7 @@ const ELEMENT_TAGS: Record<
OL: () => ({ type: 'numbered-list' }),
UL: () => ({ type: 'bulleted-list' }),
P: () => ({ type: 'paragraph' }),
- A: (el) => {
+ A: el => {
const href = el.getAttribute('href');
if (href === null) return {};
return {
@@ -44,7 +44,7 @@ const ELEMENT_TAGS: Record<
TR: () => ({ type: 'table_row' }),
TD: () => ({ type: 'table_cell' }),
TH: () => ({ type: 'table_cell' }),
- IMG: (el) => {
+ IMG: el => {
const href = el.getAttribute('src');
const title = Boolean(el.getAttribute('alt'))
? el.getAttribute('alt')
@@ -108,7 +108,7 @@ function deserialize<
parent = el.childNodes[0];
}
let children = Array.from(parent.childNodes)
- .map((c) => deserialize(c, global))
+ .map(c => deserialize(c, global))
.flat();
if (children.length === 0) {
@@ -124,7 +124,7 @@ function deserialize<
if (
isElementNode(el) &&
Array.from(el.attributes).find(
- (attr) => attr.name == 'role' && attr.value === 'heading'
+ attr => attr.name === 'role' && attr.value === 'heading'
)
) {
const level = el.attributes.getNamedItem('aria-level')?.value;
@@ -190,7 +190,7 @@ function deserialize<
children: [{ text: '' }],
},
]
- : childNodes.map((child) => ({
+ : childNodes.map(child => ({
type: 'paragraph',
children: [{ text: child.textContent ? child.textContent : '' }],
}));
@@ -204,7 +204,7 @@ function deserialize<
if (nodeName === 'DIV') {
const childNodes = Array.from(el.childNodes);
const isParagraph = childNodes.every(
- (child) =>
+ child =>
(isElementNode(child) && isInlineElement(child)) || isTextNode(child)
);
if (isParagraph) {
@@ -240,20 +240,20 @@ function deserialize<
})();
if (tagNames) {
const attrs = tagNames.reduce((acc, current) => {
- return ({...acc, ...TEXT_TAGS[current]() });
+ return { ...acc, ...TEXT_TAGS[current]() };
}, {});
- return children.map((child) => {
+ return children.map(child => {
if (typeof child === 'string') {
return jsx('text', attrs, child);
}
-
+
if (isChildNode(child, global)) return child;
-
+
if (SlateElement.isElement(child) && !SlateText.isText(child)) {
- child.children = child.children.map((c) => ({ ...c, ...attrs }));
+ child.children = child.children.map(c => ({ ...c, ...attrs }));
return child;
}
-
+
return child;
});
}
@@ -261,7 +261,7 @@ function deserialize<
if (TEXT_TAGS[nodeName]) {
const attrs = TEXT_TAGS[nodeName](el as HTMLElement);
- return children.map((child) => {
+ return children.map(child => {
if (typeof child === 'string') {
return jsx('text', attrs, child);
}
@@ -269,7 +269,7 @@ function deserialize<
if (isChildNode(child, global)) return child;
if (SlateElement.isElement(child) && !SlateText.isText(child)) {
- child.children = child.children.map((c) => ({ ...c, ...attrs }));
+ child.children = child.children.map(c => ({ ...c, ...attrs }));
return child;
}
@@ -441,7 +441,7 @@ export async function htmlToSlateAST(html: string) {
const domDocument = await parseDomDocument(normalizedHTML);
const global = await (async () => {
if (typeof window !== 'undefined') return window;
- return await import('jsdom').then((jsdom) => new jsdom.JSDOM().window);
+ return await import('jsdom').then(jsdom => new jsdom.JSDOM().window);
})();
return deserialize(domDocument.body, global);
}
diff --git a/packages/html-to-slate-ast/test/index.test.ts b/packages/html-to-slate-ast/test/index.test.ts
index 1c02d12..988ad44 100644
--- a/packages/html-to-slate-ast/test/index.test.ts
+++ b/packages/html-to-slate-ast/test/index.test.ts
@@ -7,7 +7,7 @@ test('Transforms top level span into paragraph', () => {
in fact, is the very CSS and HTML rendered as I type this blog. There are calls to HTML element classes that style
certain properties. For example, the font-family properties in the ".postArticle-content .graf — p" class has a serif
font value, hence the text rendered in this article is of the serif family. All this to say, if you as a pro`).then(
- (ast) =>
+ ast =>
expect(ast).toEqual([
{
type: 'paragraph',
@@ -30,7 +30,7 @@ test('Transforms inner span into paragraph', () => {
in fact, is the very CSS and HTML rendered as I type this blog. There are calls to HTML element classes that style
certain properties. For example, the font-family properties in the ".postArticle-content .graf — p" class has a serif
font value, hence the text rendered in this article is of the serif family. All this to say, if you as a pro
`).then(
- (ast) =>
+ ast =>
expect(ast).toEqual([
{
type: 'paragraph',
@@ -49,7 +49,7 @@ test('Transforms inner span into paragraph', () => {
test('Transforms inner spans wrapped in a div into paragraph', () => {
const input = fs.readFileSync(__dirname + '/html_input.html').toString();
- return htmlToSlateAST(input).then((ast) =>
+ return htmlToSlateAST(input).then(ast =>
expect(ast).toStrictEqual([
{
type: 'paragraph',
@@ -91,7 +91,7 @@ test('Transforms Google Docs input', () => {
const input = fs
.readFileSync(__dirname + '/google-docs_input.html')
.toString();
- return htmlToSlateAST(input).then((ast) =>
+ return htmlToSlateAST(input).then(ast =>
expect(ast).toEqual([
{
type: 'heading-one',
@@ -362,12 +362,14 @@ test('Transforms Google Docs input', () => {
children: [
{
type: 'link',
- href: 'https://lh6.googleusercontent.com/TkJFBZvkyXTa602F0gkp2phU0O1eHu96RdKFcQ8l_EOS_CBfcI9jYRixN6sNRFnFiZ-ssbLbnLDReb3FrEZ1MnLr70c5gIvPmhJtV7appyVEDSeHLIRdNwdNzbIqs3l2GOgGLGC5=s0',
+ href:
+ 'https://lh6.googleusercontent.com/TkJFBZvkyXTa602F0gkp2phU0O1eHu96RdKFcQ8l_EOS_CBfcI9jYRixN6sNRFnFiZ-ssbLbnLDReb3FrEZ1MnLr70c5gIvPmhJtV7appyVEDSeHLIRdNwdNzbIqs3l2GOgGLGC5=s0',
title: 'Screenshot 2021-06-10 at 15.56.22.png',
openInNewTab: true,
children: [
{
- text: 'https://lh6.googleusercontent.com/TkJFBZvkyXTa602F0gkp2phU0O1eHu96RdKFcQ8l_EOS_CBfcI9jYRixN6sNRFnFiZ-ssbLbnLDReb3FrEZ1MnLr70c5gIvPmhJtV7appyVEDSeHLIRdNwdNzbIqs3l2GOgGLGC5=s0',
+ text:
+ 'https://lh6.googleusercontent.com/TkJFBZvkyXTa602F0gkp2phU0O1eHu96RdKFcQ8l_EOS_CBfcI9jYRixN6sNRFnFiZ-ssbLbnLDReb3FrEZ1MnLr70c5gIvPmhJtV7appyVEDSeHLIRdNwdNzbIqs3l2GOgGLGC5=s0',
},
],
},
@@ -388,7 +390,7 @@ test('Transforms Google Docs input', () => {
test('Converts word documents', () => {
return htmlToSlateAST(
fs.readFileSync(__dirname + '/word-document.html').toString()
- ).then((ast) =>
+ ).then(ast =>
expect(ast).toStrictEqual([
{
type: 'heading-one',
@@ -580,16 +582,18 @@ test('Converts word documents', () => {
test('Converts an image pasted from Google Docs into a link node', () => {
return htmlToSlateAST(
fs.readFileSync(__dirname + '/image.html').toString()
- ).then((ast) =>
+ ).then(ast =>
expect(ast).toStrictEqual([
{
type: 'link',
- href: 'https://lh5.googleusercontent.com/EqByyE2l_VVSU6KoXFlkpPjJIBsbMTb4Dkr0cuvy2K5ctn8BoJsDHBXO0rU2wyck72_ZF1rqJ5kJ0iMEjU4Jwf7mKhRaLWoHJAzX5WvpfMytIR9sw3EwBcdQdRlIwSrsQ3odhUYq',
+ href:
+ 'https://lh5.googleusercontent.com/EqByyE2l_VVSU6KoXFlkpPjJIBsbMTb4Dkr0cuvy2K5ctn8BoJsDHBXO0rU2wyck72_ZF1rqJ5kJ0iMEjU4Jwf7mKhRaLWoHJAzX5WvpfMytIR9sw3EwBcdQdRlIwSrsQ3odhUYq',
title: "this is this image's title",
openInNewTab: true,
children: [
{
- text: 'https://lh5.googleusercontent.com/EqByyE2l_VVSU6KoXFlkpPjJIBsbMTb4Dkr0cuvy2K5ctn8BoJsDHBXO0rU2wyck72_ZF1rqJ5kJ0iMEjU4Jwf7mKhRaLWoHJAzX5WvpfMytIR9sw3EwBcdQdRlIwSrsQ3odhUYq',
+ text:
+ 'https://lh5.googleusercontent.com/EqByyE2l_VVSU6KoXFlkpPjJIBsbMTb4Dkr0cuvy2K5ctn8BoJsDHBXO0rU2wyck72_ZF1rqJ5kJ0iMEjU4Jwf7mKhRaLWoHJAzX5WvpfMytIR9sw3EwBcdQdRlIwSrsQ3odhUYq',
},
],
},
@@ -600,7 +604,7 @@ test('Converts an image pasted from Google Docs into a link node', () => {
test('Reshape an incorrectly structured table', () => {
return htmlToSlateAST(
''
- ).then((ast) =>
+ ).then(ast =>
expect(ast).toStrictEqual([
{
type: 'table',
@@ -654,13 +658,14 @@ test('Reshape an incorrectly structured table', () => {
test('Transforms pre tags into code-block nodes', () => {
const input = fs.readFileSync(__dirname + '/pre.html').toString();
- return htmlToSlateAST(input).then((ast) =>
+ return htmlToSlateAST(input).then(ast =>
expect(ast).toStrictEqual([
{
type: 'code-block',
children: [
{
- text: " L TE\n A A\n C V\n R A\n DOU\n LOU\n REUSE\n QUE TU\n PORTES\n ET QUI T'\n ORNE O CI\n VILISÉ\n OTE- TU VEUX\n LA BIEN\n SI RESPI\n RER - Apollinaire",
+ text:
+ " L TE\n A A\n C V\n R A\n DOU\n LOU\n REUSE\n QUE TU\n PORTES\n ET QUI T'\n ORNE O CI\n VILISÉ\n OTE- TU VEUX\n LA BIEN\n SI RESPI\n RER - Apollinaire",
},
],
},
diff --git a/packages/react-renderer/src/RichText.tsx b/packages/react-renderer/src/RichText.tsx
index 6b08c22..6674163 100644
--- a/packages/react-renderer/src/RichText.tsx
+++ b/packages/react-renderer/src/RichText.tsx
@@ -17,6 +17,7 @@ import {
} from './defaultElements';
import { RenderText } from './RenderText';
import { getElements } from './util/getElements';
+import { elementIsEmpty } from './util/elementIsEmpty';
function RenderNode({
node,
@@ -65,7 +66,7 @@ function RenderElement({
const { nodeId, nodeType } = rest;
/**
- * Checks if element has empty text, so it can be removed.
+ * Checks if the element is empty, so that it can be removed.
*
* Elements that can be removed with empty text are defined in `defaultRemoveEmptyElements`
*/
@@ -73,7 +74,7 @@ function RenderElement({
defaultRemoveEmptyElements?.[
elementKeys[type] as keyof RemoveEmptyElementType
] &&
- children[0].text === ''
+ elementIsEmpty({ children })
) {
return ;
}
@@ -85,7 +86,7 @@ function RenderElement({
* Since there won't be duplicated ID's, it's safe to use the first element.
*/
const referenceValues = isEmbed
- ? references?.filter((ref) => ref.id === nodeId)[0]
+ ? references?.filter(ref => ref.id === nodeId)[0]
: null;
/**
@@ -251,9 +252,7 @@ export function RichText({
If it isn't defined and there's embed elements, it will show a warning
*/
if (__DEV__) {
- const embedElements = elements.filter(
- (element) => element.type === 'embed'
- );
+ const embedElements = elements.filter(element => element.type === 'embed');
if (embedElements.length > 0 && !references) {
console.warn(
diff --git a/packages/react-renderer/src/defaultElements.tsx b/packages/react-renderer/src/defaultElements.tsx
index 77bb27b..6980056 100644
--- a/packages/react-renderer/src/defaultElements.tsx
+++ b/packages/react-renderer/src/defaultElements.tsx
@@ -57,9 +57,9 @@ export const defaultElements: Required = {
),
list_item_child: ({ children }) => <>{children}>,
Asset: {
- audio: (props) => ,
- image: (props) => ,
- video: (props) => ,
+ audio: props => ,
+ image: props => ,
+ video: props => ,
font: FallbackForCustomAsset,
application: FallbackForCustomAsset,
model: FallbackForCustomAsset,
diff --git a/packages/react-renderer/src/util/elementIsEmpty.ts b/packages/react-renderer/src/util/elementIsEmpty.ts
new file mode 100644
index 0000000..574b1c4
--- /dev/null
+++ b/packages/react-renderer/src/util/elementIsEmpty.ts
@@ -0,0 +1,32 @@
+import {
+ ElementNode,
+ isElement,
+ isText,
+ Text,
+} from '@graphcms/rich-text-types';
+
+export function elementIsEmpty({
+ children,
+}: {
+ children: (ElementNode | Text)[];
+}): boolean {
+ // Checks if the children array has more than one element.
+ // It may have a link inside, that's why we need to check this condition.
+ if (children.length > 1) {
+ const hasText = children.filter(function f(child): boolean | number {
+ if (isText(child) && child.text !== '') {
+ return true;
+ }
+
+ if (isElement(child)) {
+ return (child.children = child.children.filter(f)).length;
+ }
+
+ return false;
+ });
+
+ return hasText.length > 0 ? false : true;
+ } else if (children[0].text === '') return true;
+
+ return true;
+}
diff --git a/packages/react-renderer/test/RichText.test.tsx b/packages/react-renderer/test/RichText.test.tsx
index df72979..934c153 100644
--- a/packages/react-renderer/test/RichText.test.tsx
+++ b/packages/react-renderer/test/RichText.test.tsx
@@ -57,11 +57,28 @@ describe('@graphcms/rich-text-react-renderer', () => {
`);
});
- it('renders content correctly if received a object with empty children', () => {
+ it('should not render elements if received a object with empty children', () => {
const { container } = render();
expect(container).toMatchInlineSnapshot(`
+
+
diff --git a/packages/react-renderer/test/content.ts b/packages/react-renderer/test/content.ts
index 4ca89fd..f2624b2 100644
--- a/packages/react-renderer/test/content.ts
+++ b/packages/react-renderer/test/content.ts
@@ -13,6 +13,43 @@ export const defaultContent: RichTextContent = [
];
export const emptyContent: RichTextContent = [
+ {
+ type: 'heading-two',
+ children: [
+ {
+ text: '',
+ },
+ {
+ href: 'https://graphcms.com',
+ type: 'link',
+ children: [
+ {
+ text: 'Testing Link',
+ },
+ ],
+ },
+ ],
+ },
+ {
+ type: 'heading-two',
+ children: [
+ {
+ text: '',
+ },
+ {
+ href: 'https://graphcms.com',
+ type: 'link',
+ children: [
+ {
+ text: 'Link',
+ },
+ ],
+ },
+ {
+ text: ' 2',
+ },
+ ],
+ },
{
type: 'heading-one',
children: [
@@ -147,7 +184,8 @@ export const iframeContent: RichTextContent = [
export const imageContent: RichTextContent = [
{
- src: 'https://media.graphcms.com/output=format:webp/resize=,width:667,height:1000/8xrjYm4CR721mAZ1YAoy',
+ src:
+ 'https://media.graphcms.com/output=format:webp/resize=,width:667,height:1000/8xrjYm4CR721mAZ1YAoy',
type: 'image',
title: 'photo-1564631027894-5bdb17618445.jpg',
width: 667,
diff --git a/yarn.lock b/yarn.lock
index afdc761..ac95a1e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6420,7 +6420,7 @@ glob-to-regexp@^0.3.0:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
-glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
+glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@@ -6432,6 +6432,18 @@ glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
once "^1.3.0"
path-is-absolute "^1.0.0"
+glob@^7.1.6:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
+ integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
global-dirs@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445"
@@ -10062,11 +10074,6 @@ prettier@^1.19.1:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
-prettier@^2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18"
- integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==
-
pretty-format@^25.2.1, pretty-format@^25.5.0:
version "25.5.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a"
@@ -10570,9 +10577,9 @@ regexpp@^2.0.1:
integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==
regexpp@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
- integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
+ integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
regexpu-core@^4.7.1:
version "4.7.1"
@@ -12244,14 +12251,14 @@ typedarray@^0.0.6:
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@^3.7.3:
- version "3.9.9"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.9.tgz#e69905c54bc0681d0518bd4d587cc6f2d0b1a674"
- integrity sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==
+ version "3.9.10"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8"
+ integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==
typescript@^4.2.4:
- version "4.2.4"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961"
- integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==
+ version "4.4.4"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c"
+ integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==
uglify-js@^3.1.4:
version "3.13.5"