diff --git a/.talismanrc b/.talismanrc index 684d117..90dfe27 100644 --- a/.talismanrc +++ b/.talismanrc @@ -3,10 +3,12 @@ fileignoreconfig: ignore_detectors: - filecontent - filename: package-lock.json - checksum: 54777ceb615ca49bc21baf255b1ee9781da7e2868ff3d4292a6715bcbed33196 + checksum: 67614c9c8ad75adf4479b309dbea1d8baac81c28175b21f51fbb487840ca6bda - filename: src/entry-editable.ts checksum: 3ba7af9ed1c1adef2e2bd5610099716562bebb8ba750d4b41ddda99fc9eaf115 - filename: .husky/pre-commit checksum: 5baabd7d2c391648163f9371f0e5e9484f8fb90fa2284cfc378732ec3192c193 - filename: src/endpoints.ts checksum: 721a1df93b02d04c1c19a76c171fe2748e4abb1fc3e43452e76fecfd8f384751 +- filename: src/options/default-node-options.ts + checksum: d455330cc4f9306889fb299171364a37ad2c3bafe1fbd334033edc94f21694a6 \ No newline at end of file diff --git a/__test__/default-node-options.test.ts b/__test__/default-node-options.test.ts index 4a6ccce..d329065 100644 --- a/__test__/default-node-options.test.ts +++ b/__test__/default-node-options.test.ts @@ -3,6 +3,7 @@ import Node from '../src/nodes/node' import NodeType from '../src/nodes/node-type' import { Next, RenderMark, RenderNode } from '../src/options' import { defaultNodeOption } from '../src/options/default-node-options' +import { Attributes } from '../src/Models/metadata-model' const text = 'text' const next : Next = () => text @@ -233,4 +234,337 @@ describe('Default node render options', () => { expect(renderString).toEqual('text') done() }) +}) + +describe('Default node render options - Missing attrs cases', () => { + // Create nodes with undefined attrs using type assertion + const nodeWithoutAttrs = { + type: NodeType.PARAGRAPH, + children: [] + } as unknown as Node + + const nodeWithUndefinedAttrs = { + type: NodeType.PARAGRAPH, + attrs: undefined as unknown as Attributes, + children: [] + } as Node + + it('Should handle paragraph without attrs', done => { + const renderString = (defaultNodeOption[NodeType.PARAGRAPH] as RenderNode)(nodeWithoutAttrs, next) + expect(renderString).toEqual('

text

') + done() + }) + + it('Should handle paragraph with undefined attrs', done => { + const renderString = (defaultNodeOption[NodeType.PARAGRAPH] as RenderNode)(nodeWithUndefinedAttrs, next) + expect(renderString).toEqual('

text

') + done() + }) + + it('Should handle link without attrs', done => { + const linkNodeNoAttrs = { + type: NodeType.LINK, + children: [] + } as unknown as Node + const renderString = (defaultNodeOption[NodeType.LINK] as RenderNode)(linkNodeNoAttrs, next) + expect(renderString).toEqual('text') + done() + }) + + it('Should handle link without href or url', done => { + const linkNodePartialAttrs = { + type: NodeType.LINK, + attrs: { style: 'color: red;' }, + children: [] + } as Node + const renderString = (defaultNodeOption[NodeType.LINK] as RenderNode)(linkNodePartialAttrs, next) + expect(renderString).toEqual('text') + done() + }) + + it('Should handle image without attrs', done => { + const imgNodeNoAttrs = { + type: NodeType.IMAGE, + children: [] + } as unknown as Node + const renderString = (defaultNodeOption[NodeType.IMAGE] as RenderNode)(imgNodeNoAttrs, next) + expect(renderString).toEqual('text') + done() + }) + + it('Should handle image without src or url', done => { + const imgNodePartialAttrs = { + type: NodeType.IMAGE, + attrs: { id: 'img-id' }, + children: [] + } as Node + const renderString = (defaultNodeOption[NodeType.IMAGE] as RenderNode)(imgNodePartialAttrs, next) + expect(renderString).toEqual('text') + done() + }) + + it('Should handle embed without attrs', done => { + const embedNodeNoAttrs = { + type: NodeType.EMBED, + children: [] + } as unknown as Node + const renderString = (defaultNodeOption[NodeType.EMBED] as RenderNode)(embedNodeNoAttrs, next) + expect(renderString).toEqual('') + done() + }) + + it('Should handle headings without attrs', done => { + const headingNodeNoAttrs = { + type: NodeType.HEADING_1, + children: [] + } as unknown as Node + const renderString = (defaultNodeOption[NodeType.HEADING_1] as RenderNode)(headingNodeNoAttrs, next) + expect(renderString).toEqual('

text

') + done() + }) + + it('Should handle headings with partial attrs', done => { + const headingNodePartialAttrs = { + type: NodeType.HEADING_2, + attrs: { style: 'font-weight: bold;' }, + children: [] + } as Node + const renderString = (defaultNodeOption[NodeType.HEADING_2] as RenderNode)(headingNodePartialAttrs, next) + expect(renderString).toEqual('

text

') + done() + }) + + it('Should handle lists without attrs', done => { + const listNodeNoAttrs = { + type: NodeType.ORDER_LIST, + children: [] + } as unknown as Node + const renderString = (defaultNodeOption[NodeType.ORDER_LIST] as RenderNode)(listNodeNoAttrs, next) + expect(renderString).toEqual('
    text
') + done() + }) + + it('Should handle table without attrs', done => { + const tableNodeNoAttrs = { + type: NodeType.TABLE, + children: [] + } as unknown as Node + const renderString = (defaultNodeOption[NodeType.TABLE] as RenderNode)(tableNodeNoAttrs, next) + expect(renderString).toEqual('text
') + done() + }) + + it('Should handle table without colWidths', done => { + const tableNodeNoColWidths = { + type: NodeType.TABLE, + attrs: { style: 'border: 1px solid;' }, + children: [] + } as Node + const renderString = (defaultNodeOption[NodeType.TABLE] as RenderNode)(tableNodeNoColWidths, next) + expect(renderString).toEqual('text
') + done() + }) + + it('Should handle table head without attrs', done => { + const tableHeadNoAttrs = { + type: NodeType.TABLE_HEAD, + children: [] + } as unknown as Node + const renderString = (defaultNodeOption[NodeType.TABLE_HEAD] as RenderNode)(tableHeadNoAttrs, next) + expect(renderString).toEqual('text') + done() + }) + + it('Should handle table head without rowSpan or colSpan', done => { + const tableHeadPartialAttrs = { + type: NodeType.TABLE_HEAD, + attrs: { style: 'background: gray;' }, + children: [] + } as Node + const renderString = (defaultNodeOption[NodeType.TABLE_HEAD] as RenderNode)(tableHeadPartialAttrs, next) + expect(renderString).toEqual('text') + done() + }) + + it('Should handle table data without attrs', done => { + const tableDataNoAttrs = { + type: NodeType.TABLE_DATA, + children: [] + } as unknown as Node + const renderString = (defaultNodeOption[NodeType.TABLE_DATA] as RenderNode)(tableDataNoAttrs, next) + expect(renderString).toEqual('text') + done() + }) + + it('Should handle table data with void attribute', done => { + const tableDataVoidNoAttrs = { + type: NodeType.TABLE_DATA, + attrs: { void: true }, + children: [] + } as Node + const renderString = (defaultNodeOption[NodeType.TABLE_DATA] as RenderNode)(tableDataVoidNoAttrs, next) + expect(renderString).toEqual('') + done() + }) + + it('Should handle blockquote without attrs', done => { + const blockquoteNoAttrs = { + type: NodeType.BLOCK_QUOTE, + children: [] + } as unknown as Node + const renderString = (defaultNodeOption[NodeType.BLOCK_QUOTE] as RenderNode)(blockquoteNoAttrs, next) + expect(renderString).toEqual('
text
') + done() + }) + + it('Should handle code without attrs', done => { + const codeNoAttrs = { + type: NodeType.CODE, + children: [] + } as unknown as Node + const renderString = (defaultNodeOption[NodeType.CODE] as RenderNode)(codeNoAttrs, next) + expect(renderString).toEqual('text') + done() + }) + + it('Should handle reference without attrs', done => { + const referenceNoAttrs = { + type: NodeType.REFERENCE, + children: [] + } as unknown as Node + const renderString = (defaultNodeOption.reference as RenderNode)(referenceNoAttrs, next) + expect(renderString).toEqual('') + done() + }) + + it('Should handle reference with type but no display-type', done => { + const referencePartialAttrs = { + type: NodeType.REFERENCE, + attrs: { type: 'entry' }, + children: [] + } as Node + const renderString = (defaultNodeOption.reference as RenderNode)(referencePartialAttrs, next) + expect(renderString).toEqual('') + done() + }) + + it('Should handle reference with entry type and link display-type but missing href', done => { + const referenceLinkNoHref = { + type: NodeType.REFERENCE, + attrs: { type: 'entry', 'display-type': 'link' }, + children: [] + } as Node + const renderString = (defaultNodeOption.reference as RenderNode)(referenceLinkNoHref, next) + expect(renderString).toEqual('text') + done() + }) + + it('Should handle reference with asset type and link display-type but missing href', done => { + const referenceAssetLinkNoHref = { + type: NodeType.REFERENCE, + attrs: { type: 'asset', 'display-type': 'link' }, + children: [] + } as Node + const renderString = (defaultNodeOption.reference as RenderNode)(referenceAssetLinkNoHref, next) + expect(renderString).toEqual('text') + done() + }) + + it('Should handle reference with asset type but missing asset-link', done => { + const referenceAssetNoLink = { + type: NodeType.REFERENCE, + attrs: { type: 'asset' }, + children: [] + } as Node + const renderString = (defaultNodeOption.reference as RenderNode)(referenceAssetNoLink, next) + expect(renderString).toEqual('
') + done() + }) + + it('Should handle reference with asset type but partial attributes', done => { + const referenceAssetPartial = { + type: NodeType.REFERENCE, + attrs: { + type: 'asset', + 'asset-link': 'https://example.com/image.jpg', + style: 'border: 1px;' + }, + children: [] + } as Node + const renderString = (defaultNodeOption.reference as RenderNode)(referenceAssetPartial, next) + expect(renderString).toContain(' { + const linkWithTarget = { + type: NodeType.LINK, + attrs: { href: 'https://example.com', target: '_blank' }, + children: [] + } as Node + const renderString = (defaultNodeOption[NodeType.LINK] as RenderNode)(linkWithTarget, next) + expect(renderString).toEqual('text') + done() + }) + + it('Should handle link without target attribute', done => { + const linkWithoutTarget = { + type: NodeType.LINK, + attrs: { href: 'https://example.com' }, + children: [] + } as Node + const renderString = (defaultNodeOption[NodeType.LINK] as RenderNode)(linkWithoutTarget, next) + expect(renderString).toEqual('text') + done() + }) + + it('Should handle elements with only class-name attribute', done => { + const nodeWithClass = { + type: NodeType.PARAGRAPH, + attrs: { 'class-name': 'custom-class' }, + children: [] + } as Node + const renderString = (defaultNodeOption[NodeType.PARAGRAPH] as RenderNode)(nodeWithClass, next) + expect(renderString).toEqual('

text

') + done() + }) + + it('Should handle elements with only id attribute', done => { + const nodeWithId = { + type: NodeType.PARAGRAPH, + attrs: { id: 'custom-id' }, + children: [] + } as Node + const renderString = (defaultNodeOption[NodeType.PARAGRAPH] as RenderNode)(nodeWithId, next) + expect(renderString).toEqual('

text

') + done() + }) + + it('Should handle elements with only style attribute', done => { + const nodeWithStyle = { + type: NodeType.PARAGRAPH, + attrs: { style: 'color: blue;' }, + children: [] + } as Node + const renderString = (defaultNodeOption[NodeType.PARAGRAPH] as RenderNode)(nodeWithStyle, next) + expect(renderString).toEqual('

text

') + done() + }) + + it('Should handle all common attributes together', done => { + const nodeWithAllAttrs = { + type: NodeType.PARAGRAPH, + attrs: { + style: 'color: blue;', + 'class-name': 'custom-class', + id: 'custom-id' + }, + children: [] + } as Node + const renderString = (defaultNodeOption[NodeType.PARAGRAPH] as RenderNode)(nodeWithAllAttrs, next) + expect(renderString).toEqual('

text

') + done() + }) }) \ No newline at end of file diff --git a/__test__/default-options.test.ts b/__test__/default-options.test.ts index dcfc9f6..8b21da5 100644 --- a/__test__/default-options.test.ts +++ b/__test__/default-options.test.ts @@ -27,7 +27,7 @@ describe('Default Option test', () => { it('Default options Entry with only uid test', done => { expect(entryBlockFunction(entryContentBlank, embedAttributes)).toEqual(`

${entryContentBlank.uid}

Content type: ${entryContentBlank._content_type_uid}

`) expect(entryInlineFunction(entryContentBlank, embedAttributes)).toEqual(`${entryContentBlank.uid}`) - expect(entryLinkFunction(entryContentBlank, embedAttributes)).toEqual(`${entryContentBlank.uid}`) + expect(entryLinkFunction(entryContentBlank, embedAttributes)).toEqual(`${entryContentBlank.uid}`) done() }) @@ -41,7 +41,7 @@ describe('Default Option test', () => { it('Default options Entry with only uid, title test', done => { expect(entryBlockFunction(entryContentTitle, embedAttributes)).toEqual(`

${entryContentTitle.title}

Content type: ${entryContentTitle._content_type_uid}

`) expect(entryInlineFunction(entryContentTitle, embedAttributes)).toEqual(`${entryContentTitle.title}`) - expect(entryLinkFunction(entryContentTitle, embedAttributes)).toEqual(`${entryContentTitle.title}`) + expect(entryLinkFunction(entryContentTitle, embedAttributes)).toEqual(`${entryContentTitle.title}`) done() }) @@ -58,8 +58,8 @@ describe('Default Option test', () => { }) it('Default options Asset with only uid test', done => { - expect(assetDisplaableFunction(assetContentBlank, embedAttributes)).toEqual(`${assetContentBlank.uid}`) - expect(assetDownloadFunction(assetContentBlank, embedAttributes)).toEqual(`${assetContentBlank.uid}`) + expect(assetDisplaableFunction(assetContentBlank, embedAttributes)).toEqual(`${assetContentBlank.uid}`) + expect(assetDownloadFunction(assetContentBlank, embedAttributes)).toEqual(`${assetContentBlank.uid}`) done() }) @@ -71,8 +71,8 @@ describe('Default Option test', () => { it('Default options Asset with uid and filename test', done => { - expect(assetDisplaableFunction(assetContentonlyFileName, embedAttributes)).toEqual(`${assetContentonlyFileName.filename}`) - expect(assetDownloadFunction(assetContentonlyFileName, embedAttributes)).toEqual(`${assetContentonlyFileName.uid}`) + expect(assetDisplaableFunction(assetContentonlyFileName, embedAttributes)).toEqual(`${assetContentonlyFileName.filename}`) + expect(assetDownloadFunction(assetContentonlyFileName, embedAttributes)).toEqual(`${assetContentonlyFileName.uid}`) done() }) @@ -83,8 +83,8 @@ describe('Default Option test', () => { }) it('Default options Asset with uid and title test', done => { - expect(assetDisplaableFunction(assetContentonlyTitle, embedAttributes)).toEqual(`${assetContentonlyTitle.title}`) - expect(assetDownloadFunction(assetContentonlyTitle, embedAttributes)).toEqual(`${assetContentonlyTitle.title || assetContentonlyTitle.uid}`) + expect(assetDisplaableFunction(assetContentonlyTitle, embedAttributes)).toEqual(`${assetContentonlyTitle.title}`) + expect(assetDownloadFunction(assetContentonlyTitle, embedAttributes)).toEqual(`${assetContentonlyTitle.title || assetContentonlyTitle.uid}`) done() }) @@ -101,11 +101,11 @@ describe('Default Option test', () => { it('Default options Link text test', done => { expect(entryLinkFunction(entryContentURL, embedAttributesText)).toEqual(`${linkText}`) - expect(entryLinkFunction(entryContentTitle, embedAttributesText)).toEqual(`${linkText}`) - expect(entryLinkFunction(entryContentBlank, embedAttributesText)).toEqual(`${linkText}`) + expect(entryLinkFunction(entryContentTitle, embedAttributesText)).toEqual(`${linkText}`) + expect(entryLinkFunction(entryContentBlank, embedAttributesText)).toEqual(`${linkText}`) expect(entryLinkFunction(entryContentTitleURL, embedAttributesText)).toEqual(`${linkText}`) - expect(assetDisplaableFunction(assetContentBlank, embedAttributesText)).toEqual(`${linkText}`) - expect(assetDownloadFunction(assetContentBlank, embedAttributesText)).toEqual(`${linkText}`) + expect(assetDisplaableFunction(assetContentBlank, embedAttributesText)).toEqual(`${linkText}`) + expect(assetDownloadFunction(assetContentBlank, embedAttributesText)).toEqual(`${linkText}`) done() }) }) \ No newline at end of file diff --git a/__test__/mock/entry-mock.ts b/__test__/mock/entry-mock.ts index 2cb3f23..36a976e 100644 --- a/__test__/mock/entry-mock.ts +++ b/__test__/mock/entry-mock.ts @@ -644,6 +644,6 @@ export const entrymultipleRTERenderOption = `

entry_uid_21

Content type: 1234

entry_uid_21



-\"asset_uid_12\"
+\"asset_uid_12\"

` \ No newline at end of file diff --git a/__test__/mock/json-element-mock-result.ts b/__test__/mock/json-element-mock-result.ts index 635c955..97c3ea2 100644 --- a/__test__/mock/json-element-mock-result.ts +++ b/__test__/mock/json-element-mock-result.ts @@ -22,7 +22,7 @@ const htmlTextIdInAttrs = "

data

" const classAndIdAttrsHtml = "link

heading1

heading2

heading3

heading4

heading5
heading6
" const styleObjHtml = "

heading1

heading2

heading3

heading4

heading5
heading6
" const referenceObjHtml = "

Embed entry as a link

Open entry as a link in new tab

Bold entry

Bold entry open in new tab

" -const referenceObjHtmlBlock = "

Embed entry as a link

Embed entry as a link open in new tab

" +const referenceObjHtmlBlock = "

Embed entry as a link

Embed entry as a link open in new tab

" const imagetags = "
\"batman\"
The Batman
" const escapeHtml = "

<p>Welcome to Contentstack! <script>console.log(/"Hello from Contentstack!/");</script> Explore our platform to create, manage, and publish content seamlessly.</p>

" const breakTestHtml = "

Normal text with
break tag after break.

" diff --git a/eslint.config.js b/eslint.config.js index cd7fbf7..a91c6e2 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -18,6 +18,26 @@ export default [ require: 'readonly', }, }, + }, + { + files: ['**/__test__/**/*.ts', '**/__test__/**/*.tsx'], + languageOptions: { + parser: tsparser, + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + globals: { + describe: 'readonly', + it: 'readonly', + expect: 'readonly', + beforeEach: 'readonly', + afterEach: 'readonly', + beforeAll: 'readonly', + afterAll: 'readonly', + jest: 'readonly', + }, + }, plugins: { '@typescript-eslint': tseslint, }, diff --git a/package-lock.json b/package-lock.json index 7596cc7..e742d33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "rimraf": "^6.0.1", "rollup": "^4.21.3", "rollup-plugin-typescript2": "^0.36.0", + "snyk": "^1.1300.2", "ts-jest": "^29.2.5", "ts-node": "^10.9.2", "typescript": "^4.9.5" @@ -88,6 +89,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -2274,6 +2276,91 @@ "win32" ] }, + "node_modules/@sentry-internal/tracing": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.120.4.tgz", + "integrity": "sha512-Fz5+4XCg3akeoFK+K7g+d7HqGMjmnLoY2eJlpONJmaeT9pXY7yfUyXKZMmMajdE2LxxKJgQ2YKvSCaGVamTjHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry/core": "7.120.4", + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/core": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.120.4.tgz", + "integrity": "sha512-TXu3Q5kKiq8db9OXGkWyXUbIxMMuttB5vJ031yolOl5T/B69JRyAoKuojLBjRv1XX583gS1rSSoX8YXX7ATFGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/integrations": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.120.4.tgz", + "integrity": "sha512-kkBTLk053XlhDCg7OkBQTIMF4puqFibeRO3E3YiVc4PGLnocXMaVpOSCkMqAc1k1kZ09UgGi8DxfQhnFEjUkpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry/core": "7.120.4", + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4", + "localforage": "^1.8.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/node": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.120.4.tgz", + "integrity": "sha512-qq3wZAXXj2SRWhqErnGCSJKUhPSlZ+RGnCZjhfjHpP49KNpcd9YdPTIUsFMgeyjdh6Ew6aVCv23g1hTP0CHpYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry-internal/tracing": "7.120.4", + "@sentry/core": "7.120.4", + "@sentry/integrations": "7.120.4", + "@sentry/types": "7.120.4", + "@sentry/utils": "7.120.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/types": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.120.4.tgz", + "integrity": "sha512-cUq2hSSe6/qrU6oZsEP4InMI5VVdD86aypE+ENrQ6eZEVLTCYm1w6XhW1NvIu3UuWh7gZec4a9J7AFpYxki88Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/utils": { + "version": "7.120.4", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.120.4.tgz", + "integrity": "sha512-zCKpyDIWKHwtervNK2ZlaK8mMV7gVUijAgFeJStH+CU/imcdquizV3pFLlSQYRswG+Lbyd6CT/LGRh3IbtkCFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sentry/types": "7.120.4" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -3146,6 +3233,14 @@ "readable-stream": "^3.4.0" } }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -3990,6 +4085,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -4000,6 +4113,24 @@ "node": ">=8" } }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -4040,6 +4171,13 @@ "node": ">=8" } }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT" + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -4208,6 +4346,13 @@ "node": ">= 0.4" } }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT" + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -5102,6 +5247,24 @@ "node": "*" } }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, "node_modules/global-dirs": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", @@ -5173,6 +5336,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -5242,6 +5422,19 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -5448,6 +5641,13 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -7283,6 +7483,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -7390,6 +7597,16 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -7397,6 +7614,16 @@ "dev": true, "license": "MIT" }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lie": "3.1.1" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -7587,6 +7814,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -7890,6 +8130,16 @@ "dev": true, "license": "MIT" }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -8793,6 +9043,31 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/roarr/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/rollup": { "version": "4.52.5", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", @@ -9004,6 +9279,13 @@ "node": ">=10" } }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT" + }, "node_modules/semver/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -9024,6 +9306,35 @@ "dev": true, "license": "ISC" }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -9071,6 +9382,24 @@ "node": ">=8" } }, + "node_modules/snyk": { + "version": "1.1300.2", + "resolved": "https://registry.npmjs.org/snyk/-/snyk-1.1300.2.tgz", + "integrity": "sha512-wkJ1hTooKFaNZl8cl+G1EE15qhOLB54YCIcT0GsX/sTPUAgK7pTm7902QrBtODdL68+wzqazazyDRlElXC1U2w==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@sentry/node": "^7.36.0", + "global-agent": "^3.0.0" + }, + "bin": { + "snyk": "bin/snyk" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/package.json b/package.json index c40cdcc..943b44f 100644 --- a/package.json +++ b/package.json @@ -41,11 +41,13 @@ "devDependencies": { "@commitlint/cli": "^17.8.1", "@commitlint/config-conventional": "^17.8.1", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^15.2.3", "@types/jest": "^26.0.24", - "commitizen": "^4.3.1", - "eslint": "^9.11.1", "@typescript-eslint/eslint-plugin": "^8.6.0", "@typescript-eslint/parser": "^8.6.0", + "commitizen": "^4.3.1", + "eslint": "^9.11.1", "husky": "^8.0.3", "jest": "^29.7.0", "jest-coverage-badges": "^1.0.0", @@ -57,9 +59,8 @@ "prettier": "^3.3.3", "rimraf": "^6.0.1", "rollup": "^4.21.3", - "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", "rollup-plugin-typescript2": "^0.36.0", + "snyk": "^1.1300.2", "ts-jest": "^29.2.5", "ts-node": "^10.9.2", "typescript": "^4.9.5" diff --git a/src/helper/enumerate-entries.ts b/src/helper/enumerate-entries.ts index c2b6339..31f41e9 100644 --- a/src/helper/enumerate-entries.ts +++ b/src/helper/enumerate-entries.ts @@ -110,7 +110,8 @@ export function referenceToHTML( return sendToRenderOption(node); } - let aTagAttrs = `${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} href="${node.attrs.href || node.attrs.url}"`; + const href = node.attrs.href || node.attrs.url; + let aTagAttrs = `${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}${href ? ` href="${href}"` : ``}`; if (node.attrs.target) { aTagAttrs +=` target="${node.attrs.target}"`; } diff --git a/src/options/default-node-options.ts b/src/options/default-node-options.ts index 3f19f37..668f505 100644 --- a/src/options/default-node-options.ts +++ b/src/options/default-node-options.ts @@ -4,68 +4,105 @@ import Node from "../nodes/node"; import NodeType from "../nodes/node-type"; import { sanitizeHTML } from "../helper/sanitize"; +/** + * Safely gets an attribute value from node.attrs + */ +function getAttr(node: Node, key: string): unknown { + return node.attrs?.[key]; +} + +/** + * Safely gets a string attribute value from node.attrs + */ +function getAttrString(node: Node, key: string): string | undefined { + const value = node.attrs?.[key]; + return typeof value === 'string' ? value : undefined; +} + +/** + * Builds common HTML attributes string (style, class-name, id) + */ +function buildCommonAttrs(node: Node): string { + if (!node.attrs) return ''; + + const attrs: string[] = []; + if (node.attrs.style) { + attrs.push(` style="${node.attrs.style}"`); + } + if (node.attrs['class-name']) { + attrs.push(` class="${node.attrs['class-name']}"`); + } + if (node.attrs.id) { + attrs.push(` id="${node.attrs.id}"`); + } + return attrs.join(''); +} + export const defaultNodeOption: RenderOption = { - [NodeType.DOCUMENT]:(node: Node) => { + [NodeType.DOCUMENT]:() => { return `` }, [NodeType.PARAGRAPH]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}

` + return `${sanitizeHTML(next(node.children))}

` }, [NodeType.LINK]:(node: Node, next: Next) => { - const sanitizedHref = sanitizeHTML(node.attrs.href || node.attrs.url); - if (node.attrs.target) { - return `${sanitizeHTML(next(node.children))}` - } - return `${sanitizeHTML(next(node.children))}` + const href = getAttrString(node, 'href') || getAttrString(node, 'url') || ''; + const sanitizedHref = sanitizeHTML(href); + const target = getAttrString(node, 'target'); + const targetAttr = target ? ` target="${target}"` : ''; + return `${sanitizeHTML(next(node.children))}` }, [NodeType.IMAGE]:(node: Node, next: Next) => { - const sanitizedSrc = encodeURI(sanitizeHTML(node.attrs.src || node.attrs.url)); - return `${sanitizeHTML(next(node.children))}` + const src = getAttrString(node, 'src') || getAttrString(node, 'url'); + const sanitizedSrc = src ? encodeURI(sanitizeHTML(src)) : ''; + return `${sanitizeHTML(next(node.children))}` }, [NodeType.EMBED]:(node: Node, next: Next) => { - const sanitizedSrc = encodeURI(sanitizeHTML(node.attrs.src || node.attrs.url)); - return `${sanitizeHTML(next(node.children))}` + const src = getAttrString(node, 'src') || getAttrString(node, 'url'); + const sanitizedSrc = src ? encodeURI(sanitizeHTML(src)) : ''; + return `${sanitizeHTML(next(node.children))}` }, [NodeType.HEADING_1]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, [NodeType.HEADING_2]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, [NodeType.HEADING_3]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, [NodeType.HEADING_4]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, [NodeType.HEADING_5]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, [NodeType.HEADING_6]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, [NodeType.ORDER_LIST]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, [NodeType.FRAGMENT]:(node: Node, next: Next) => { return `${sanitizeHTML(next(node.children))}` }, [NodeType.UNORDER_LIST]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, [NodeType.LIST_ITEM]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, - [NodeType.HR]:(node: Node, next: Next) => { + [NodeType.HR]:() => { return `
` }, [NodeType.TABLE]: (node: Node, next: Next) => { // Generate colgroup if colWidths attribute is present let colgroupHTML = ''; - if (node.attrs.colWidths && Array.isArray(node.attrs.colWidths)) { - const totalWidth = node.attrs.colWidths.reduce((sum, width) => sum + width, 0); + const colWidths = getAttr(node, 'colWidths'); + if (colWidths && Array.isArray(colWidths)) { + const totalWidth = colWidths.reduce((sum: number, width: number) => sum + width, 0); colgroupHTML = `<${NodeType.COL_GROUP} data-width="${totalWidth}">`; - node.attrs.colWidths.forEach(colWidth => { + colWidths.forEach((colWidth: number) => { const widthPercentage = (colWidth / totalWidth) * 100; colgroupHTML += `<${NodeType.COL} style="width:${widthPercentage.toFixed(2)}%"/>`; }); @@ -73,78 +110,94 @@ export const defaultNodeOption: RenderOption = { } // Generate table with colgroup and other attributes - return `` + - `${colgroupHTML}` + - `${sanitizeHTML(next(node.children))}` + - ``; + return `${colgroupHTML}${sanitizeHTML(next(node.children))}`; }, [NodeType.TABLE_HEADER]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, [NodeType.TABLE_BODY]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, [NodeType.TABLE_FOOTER]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, [NodeType.TABLE_ROW]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, [NodeType.TABLE_HEAD]:(node: Node, next: Next) => { - if (node.attrs.void) return ''; + if (getAttr(node, 'void')) return ''; - return `${sanitizeHTML(next(node.children))}` + - `` + const rowSpan = getAttr(node, 'rowSpan'); + const colSpan = getAttr(node, 'colSpan'); + const rowSpanAttr = rowSpan ? ` rowspan="${rowSpan}"` : ''; + const colSpanAttr = colSpan ? ` colspan="${colSpan}"` : ''; + + return `${sanitizeHTML(next(node.children))}` }, [NodeType.TABLE_DATA]:(node: Node, next: Next) => { - if (node.attrs.void) return ''; + if (getAttr(node, 'void')) return ''; - return `${sanitizeHTML(next(node.children))}` + - `` + const rowSpan = getAttr(node, 'rowSpan'); + const colSpan = getAttr(node, 'colSpan'); + const rowSpanAttr = rowSpan ? ` rowspan="${rowSpan}"` : ''; + const colSpanAttr = colSpan ? ` colspan="${colSpan}"` : ''; + + return `${sanitizeHTML(next(node.children))}` }, [NodeType.BLOCK_QUOTE]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, [NodeType.CODE]:(node: Node, next: Next) => { - return `${sanitizeHTML(next(node.children))}` + return `${sanitizeHTML(next(node.children))}` }, ['reference']:(node: Node, next: Next) => { - if ((node.attrs.type === 'entry' || node.attrs.type === 'asset') && node.attrs['display-type'] === 'link'){ - let aTagAttrs = `${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} href="${node.attrs.href || node.attrs.url}"`; - if (node.attrs.target) { - aTagAttrs +=` target="${node.attrs.target}"`; + const type = getAttr(node, 'type'); + const displayType = getAttr(node, 'display-type'); + + if ((type === 'entry' || type === 'asset') && displayType === 'link'){ + const href = getAttrString(node, 'href') || getAttrString(node, 'url') || ''; + const target = getAttrString(node, 'target'); + const assetUid = getAttrString(node, 'asset-uid'); + + let aTagAttrs = buildCommonAttrs(node); + if (href) aTagAttrs += ` href="${href}"`; + if (target) { + aTagAttrs += ` target="${target}"`; } - if(node.attrs.type == 'asset') { - aTagAttrs += ` type="asset" content-type-uid="sys_assets" ${node.attrs['asset-uid'] ? `data-sys-asset-uid="${node.attrs['asset-uid']}"` : ``} sys-style-type="download"` + if (type === 'asset') { + aTagAttrs += ` type="asset" content-type-uid="sys_assets"`; + if (assetUid) { + aTagAttrs += ` data-sys-asset-uid="${assetUid}"`; + } + aTagAttrs += ` sys-style-type="download"`; } - const aTag = `${sanitizeHTML(next(node.children))}`; - return aTag; + return `${sanitizeHTML(next(node.children))}`; } - if (node.attrs.type === 'asset') { - const src = encodeURI(node.attrs['asset-link']); - const alt = node.attrs?.['redactor-attributes']?.['alt']; - const link = node.attrs.link; - const target = node.attrs.target || ""; - const caption = node.attrs?.['redactor-attributes']?.['asset-caption'] || node.attrs?.['asset-caption'] || ""; - const style = node.attrs.style; - const asset_uid= node.attrs['asset-uid']; + + if (type === 'asset') { + const assetLink = getAttrString(node, 'asset-link'); + const src = assetLink ? encodeURI(assetLink) : ''; + const redactorAttrs = getAttr(node, 'redactor-attributes') as Record | undefined; + const alt = redactorAttrs?.['alt'] as string | undefined; + const link = getAttrString(node, 'link'); + const target = getAttrString(node, 'target') || ""; + const caption = (redactorAttrs?.['asset-caption'] as string | undefined) || getAttrString(node, 'asset-caption') || ""; + const style = getAttrString(node, 'style'); + const assetUid = getAttrString(node, 'asset-uid'); + const className = getAttrString(node, 'class-name'); - let imageTag = ``; + const assetUidAttr = assetUid ? ` asset_uid="${assetUid}"` : ''; + const classAttr = className ? ` class="${sanitizeHTML(className)}"` : ''; + const srcAttr = src ? ` src="${sanitizeHTML(src)}"` : ''; + const altAttr = alt ? ` alt="${alt}"` : ''; + const targetAttr = target === "_blank" ? ` target="_blank"` : ''; + const styleAttr = style ? ` style="${style}"` : ''; + + const imageTag = ``; + const styleAttrFig = style ? ` style="${style}"` : ''; - return `` + + return `` + (link ? `` : "") + imageTag + (link ? `` : "") + diff --git a/src/options/default-options.ts b/src/options/default-options.ts index bed35f3..a665d22 100644 --- a/src/options/default-options.ts +++ b/src/options/default-options.ts @@ -16,19 +16,22 @@ export const defaultOptions: RenderOption = { return `${title}`; }, [StyleType.LINK]: (item: EmbeddedItem | EntryNode, metadata: Metadata) => { - const url = encodeURI(sanitizeHTML(item.url || 'undefined')); + const url = item.url ? encodeURI(sanitizeHTML(item.url)) : null; const text = sanitizeHTML(metadata.text || item.title || item.uid || (item.system ? item.system.uid : '')); - return `${text}`; + const hrefAttr = url ? ` href="${url}"` : ''; + return `${text}`; }, [StyleType.DISPLAY]: (item: EmbeddedItem | EntryNode, metadata: Metadata) => { - const url = encodeURI(sanitizeHTML(item.url || 'undefined')); + const url = item.url ? encodeURI(sanitizeHTML(item.url)) : null; const alt = sanitizeHTML(metadata.attributes.alt || item.title || item.filename || item.uid || (item.system ? item.system.uid : '')); - return `${alt}`; + const srcAttr = url ? ` src="${url}"` : ''; + return ``; }, [StyleType.DOWNLOAD]: (item: EmbeddedItem | EntryNode, metadata: Metadata) => { - const href = encodeURI(sanitizeHTML(item.url || 'undefined')); + const href = item.url ? encodeURI(sanitizeHTML(item.url)) : null; const text = sanitizeHTML(metadata.text || item.title || item.uid || (item.system ? item.system.content_type_uid : '')); - return `${text}`; + const hrefAttr = href ? ` href="${href}"` : ''; + return `${text}`; }, };