Skip to content

Commit 8b4f238

Browse files
committed
feat: enhance default-node-options.ts
add utility functions for attribute handling and streamline HTML generation
1 parent 5990778 commit 8b4f238

File tree

2 files changed

+125
-70
lines changed

2 files changed

+125
-70
lines changed

.talismanrc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ fileignoreconfig:
1010
checksum: 5baabd7d2c391648163f9371f0e5e9484f8fb90fa2284cfc378732ec3192c193
1111
- filename: src/endpoints.ts
1212
checksum: 721a1df93b02d04c1c19a76c171fe2748e4abb1fc3e43452e76fecfd8f384751
13+
- filename: src/options/default-node-options.ts
14+
checksum: d455330cc4f9306889fb299171364a37ad2c3bafe1fbd334033edc94f21694a6

src/options/default-node-options.ts

Lines changed: 123 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -4,147 +4,200 @@ import Node from "../nodes/node";
44
import NodeType from "../nodes/node-type";
55
import { sanitizeHTML } from "../helper/sanitize";
66

7+
/**
8+
* Safely gets an attribute value from node.attrs
9+
*/
10+
function getAttr(node: Node, key: string): unknown {
11+
return node.attrs?.[key];
12+
}
13+
14+
/**
15+
* Safely gets a string attribute value from node.attrs
16+
*/
17+
function getAttrString(node: Node, key: string): string | undefined {
18+
const value = node.attrs?.[key];
19+
return typeof value === 'string' ? value : undefined;
20+
}
21+
22+
/**
23+
* Builds common HTML attributes string (style, class-name, id)
24+
*/
25+
function buildCommonAttrs(node: Node): string {
26+
if (!node.attrs) return '';
27+
28+
const attrs: string[] = [];
29+
if (node.attrs.style) {
30+
attrs.push(` style="${node.attrs.style}"`);
31+
}
32+
if (node.attrs['class-name']) {
33+
attrs.push(` class="${node.attrs['class-name']}"`);
34+
}
35+
if (node.attrs.id) {
36+
attrs.push(` id="${node.attrs.id}"`);
37+
}
38+
return attrs.join('');
39+
}
40+
741
export const defaultNodeOption: RenderOption = {
8-
[NodeType.DOCUMENT]:(node: Node) => {
42+
[NodeType.DOCUMENT]:() => {
943
return ``
1044
},
1145
[NodeType.PARAGRAPH]:(node: Node, next: Next) => {
12-
return `<p${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</p>`
46+
return `<p${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</p>`
1347
},
1448
[NodeType.LINK]:(node: Node, next: Next) => {
15-
const sanitizedHref = sanitizeHTML(node.attrs.href || node.attrs.url);
16-
if (node.attrs.target) {
17-
return `<a${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} href="${sanitizedHref}" target="${node.attrs.target}">${sanitizeHTML(next(node.children))}</a>`
18-
}
19-
return `<a${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} href="${sanitizedHref}">${sanitizeHTML(next(node.children))}</a>`
49+
const href = getAttrString(node, 'href') || getAttrString(node, 'url') || '';
50+
const sanitizedHref = sanitizeHTML(href);
51+
const target = getAttrString(node, 'target');
52+
const targetAttr = target ? ` target="${target}"` : '';
53+
return `<a${buildCommonAttrs(node)} href="${sanitizedHref}"${targetAttr}>${sanitizeHTML(next(node.children))}</a>`
2054
},
2155
[NodeType.IMAGE]:(node: Node, next: Next) => {
22-
const sanitizedSrc = encodeURI(sanitizeHTML(node.attrs.src || node.attrs.url));
23-
return `<img${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} src="${sanitizedSrc}" />${sanitizeHTML(next(node.children))}`
56+
const src = getAttrString(node, 'src') || getAttrString(node, 'url');
57+
const sanitizedSrc = src ? encodeURI(sanitizeHTML(src)) : '';
58+
return `<img${buildCommonAttrs(node)}${sanitizedSrc ? ` src="${sanitizedSrc}"` : ''} />${sanitizeHTML(next(node.children))}`
2459
},
2560
[NodeType.EMBED]:(node: Node, next: Next) => {
26-
const sanitizedSrc = encodeURI(sanitizeHTML(node.attrs.src || node.attrs.url));
27-
return `<iframe${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} src="${sanitizedSrc}">${sanitizeHTML(next(node.children))}</iframe>`
61+
const src = getAttrString(node, 'src') || getAttrString(node, 'url');
62+
const sanitizedSrc = src ? encodeURI(sanitizeHTML(src)) : '';
63+
return `<iframe${buildCommonAttrs(node)}${sanitizedSrc ? ` src="${sanitizedSrc}"` : ''}>${sanitizeHTML(next(node.children))}</iframe>`
2864
},
2965
[NodeType.HEADING_1]:(node: Node, next: Next) => {
30-
return `<h1${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</h1>`
66+
return `<h1${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</h1>`
3167
},
3268
[NodeType.HEADING_2]:(node: Node, next: Next) => {
33-
return `<h2${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</h2>`
69+
return `<h2${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</h2>`
3470
},
3571
[NodeType.HEADING_3]:(node: Node, next: Next) => {
36-
return `<h3${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</h3>`
72+
return `<h3${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</h3>`
3773
},
3874
[NodeType.HEADING_4]:(node: Node, next: Next) => {
39-
return `<h4${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</h4>`
75+
return `<h4${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</h4>`
4076
},
4177
[NodeType.HEADING_5]:(node: Node, next: Next) => {
42-
return `<h5${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</h5>`
78+
return `<h5${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</h5>`
4379
},
4480
[NodeType.HEADING_6]:(node: Node, next: Next) => {
45-
return `<h6${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</h6>`
81+
return `<h6${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</h6>`
4682
},
4783
[NodeType.ORDER_LIST]:(node: Node, next: Next) => {
48-
return `<ol${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</ol>`
84+
return `<ol${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</ol>`
4985
},
5086
[NodeType.FRAGMENT]:(node: Node, next: Next) => {
5187
return `<fragment>${sanitizeHTML(next(node.children))}</fragment>`
5288
},
5389
[NodeType.UNORDER_LIST]:(node: Node, next: Next) => {
54-
return `<ul${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</ul>`
90+
return `<ul${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</ul>`
5591
},
5692
[NodeType.LIST_ITEM]:(node: Node, next: Next) => {
57-
return `<li${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</li>`
93+
return `<li${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</li>`
5894
},
59-
[NodeType.HR]:(node: Node, next: Next) => {
95+
[NodeType.HR]:() => {
6096
return `<hr>`
6197
},
6298
[NodeType.TABLE]: (node: Node, next: Next) => {
6399
// Generate colgroup if colWidths attribute is present
64100
let colgroupHTML = '';
65-
if (node.attrs.colWidths && Array.isArray(node.attrs.colWidths)) {
66-
const totalWidth = node.attrs.colWidths.reduce((sum, width) => sum + width, 0);
101+
const colWidths = getAttr(node, 'colWidths');
102+
if (colWidths && Array.isArray(colWidths)) {
103+
const totalWidth = colWidths.reduce((sum: number, width: number) => sum + width, 0);
67104
colgroupHTML = `<${NodeType.COL_GROUP} data-width="${totalWidth}">`;
68-
node.attrs.colWidths.forEach(colWidth => {
105+
colWidths.forEach((colWidth: number) => {
69106
const widthPercentage = (colWidth / totalWidth) * 100;
70107
colgroupHTML += `<${NodeType.COL} style="width:${widthPercentage.toFixed(2)}%"/>`;
71108
});
72109
colgroupHTML += `</${NodeType.COL_GROUP}>`;
73110
}
74111

75112
// Generate table with colgroup and other attributes
76-
return `<table${node.attrs.style ? ` style="${node.attrs.style}"` : ``}` +
77-
`${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}` +
78-
`${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>` +
79-
`${colgroupHTML}` +
80-
`${sanitizeHTML(next(node.children))}` +
81-
`</table>`;
113+
return `<table${buildCommonAttrs(node)}>${colgroupHTML}${sanitizeHTML(next(node.children))}</table>`;
82114
},
83115
[NodeType.TABLE_HEADER]:(node: Node, next: Next) => {
84-
return `<thead${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</thead>`
116+
return `<thead${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</thead>`
85117
},
86118
[NodeType.TABLE_BODY]:(node: Node, next: Next) => {
87-
return `<tbody${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</tbody>`
119+
return `<tbody${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</tbody>`
88120
},
89121
[NodeType.TABLE_FOOTER]:(node: Node, next: Next) => {
90-
return `<tfoot${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</tfoot>`
122+
return `<tfoot${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</tfoot>`
91123
},
92124
[NodeType.TABLE_ROW]:(node: Node, next: Next) => {
93-
return `<tr${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</tr>`
125+
return `<tr${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</tr>`
94126
},
95127
[NodeType.TABLE_HEAD]:(node: Node, next: Next) => {
96-
if (node.attrs.void) return '';
128+
if (getAttr(node, 'void')) return '';
97129

98-
return `<th` +
99-
`${node.attrs.rowSpan ? ` rowspan="${node.attrs.rowSpan}"` : ``}` +
100-
`${node.attrs.colSpan ? ` colspan="${node.attrs.colSpan}"` : ``}` +
101-
`${node.attrs.style ? ` style="${node.attrs.style}"` : ``}`+
102-
`${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}`+
103-
`${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}` +
104-
`</th>`
130+
const rowSpan = getAttr(node, 'rowSpan');
131+
const colSpan = getAttr(node, 'colSpan');
132+
const rowSpanAttr = rowSpan ? ` rowspan="${rowSpan}"` : '';
133+
const colSpanAttr = colSpan ? ` colspan="${colSpan}"` : '';
134+
135+
return `<th${rowSpanAttr}${colSpanAttr}${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</th>`
105136
},
106137
[NodeType.TABLE_DATA]:(node: Node, next: Next) => {
107-
if (node.attrs.void) return '';
138+
if (getAttr(node, 'void')) return '';
108139

109-
return `<td` +
110-
`${node.attrs.rowSpan ? ` rowspan="${node.attrs.rowSpan}"` : ``}` +
111-
`${node.attrs.colSpan ? ` colspan="${node.attrs.colSpan}"` : ``}` +
112-
`${node.attrs.style ? ` style="${node.attrs.style}"` : ``}`+
113-
`${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}`+
114-
`${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}` +
115-
`</td>`
140+
const rowSpan = getAttr(node, 'rowSpan');
141+
const colSpan = getAttr(node, 'colSpan');
142+
const rowSpanAttr = rowSpan ? ` rowspan="${rowSpan}"` : '';
143+
const colSpanAttr = colSpan ? ` colspan="${colSpan}"` : '';
144+
145+
return `<td${rowSpanAttr}${colSpanAttr}${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</td>`
116146
},
117147
[NodeType.BLOCK_QUOTE]:(node: Node, next: Next) => {
118-
return `<blockquote${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</blockquote>`
148+
return `<blockquote${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</blockquote>`
119149
},
120150
[NodeType.CODE]:(node: Node, next: Next) => {
121-
return `<code${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</code>`
151+
return `<code${buildCommonAttrs(node)}>${sanitizeHTML(next(node.children))}</code>`
122152
},
123153

124154
['reference']:(node: Node, next: Next) => {
125-
if ((node.attrs.type === 'entry' || node.attrs.type === 'asset') && node.attrs['display-type'] === 'link'){
126-
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}"`;
127-
if (node.attrs.target) {
128-
aTagAttrs +=` target="${node.attrs.target}"`;
155+
const type = getAttr(node, 'type');
156+
const displayType = getAttr(node, 'display-type');
157+
158+
if ((type === 'entry' || type === 'asset') && displayType === 'link'){
159+
const href = getAttrString(node, 'href') || getAttrString(node, 'url') || '';
160+
const target = getAttrString(node, 'target');
161+
const assetUid = getAttrString(node, 'asset-uid');
162+
163+
let aTagAttrs = buildCommonAttrs(node);
164+
aTagAttrs += ` href="${href}"`;
165+
if (target) {
166+
aTagAttrs += ` target="${target}"`;
129167
}
130-
if(node.attrs.type == 'asset') {
131-
aTagAttrs += ` type="asset" content-type-uid="sys_assets" ${node.attrs['asset-uid'] ? `data-sys-asset-uid="${node.attrs['asset-uid']}"` : ``} sys-style-type="download"`
168+
if (type === 'asset') {
169+
aTagAttrs += ` type="asset" content-type-uid="sys_assets"`;
170+
if (assetUid) {
171+
aTagAttrs += ` data-sys-asset-uid="${assetUid}"`;
172+
}
173+
aTagAttrs += ` sys-style-type="download"`;
132174
}
133-
const aTag = `<a${aTagAttrs}>${sanitizeHTML(next(node.children))}</a>`;
134-
return aTag;
175+
return `<a${aTagAttrs}>${sanitizeHTML(next(node.children))}</a>`;
135176
}
136-
if (node.attrs.type === 'asset') {
137-
const src = encodeURI(node.attrs['asset-link']);
138-
const alt = node.attrs?.['redactor-attributes']?.['alt'];
139-
const link = node.attrs.link;
140-
const target = node.attrs.target || "";
141-
const caption = node.attrs?.['redactor-attributes']?.['asset-caption'] || node.attrs?.['asset-caption'] || "";
142-
const style = node.attrs.style;
143-
const asset_uid= node.attrs['asset-uid'];
177+
178+
if (type === 'asset') {
179+
const assetLink = getAttrString(node, 'asset-link');
180+
const src = assetLink ? encodeURI(assetLink) : '';
181+
const redactorAttrs = getAttr(node, 'redactor-attributes') as Record<string, unknown> | undefined;
182+
const alt = redactorAttrs?.['alt'] as string | undefined;
183+
const link = getAttrString(node, 'link');
184+
const target = getAttrString(node, 'target') || "";
185+
const caption = (redactorAttrs?.['asset-caption'] as string | undefined) || getAttrString(node, 'asset-caption') || "";
186+
const style = getAttrString(node, 'style');
187+
const assetUid = getAttrString(node, 'asset-uid');
188+
const className = getAttrString(node, 'class-name');
144189

145-
let imageTag = `<img${asset_uid ? ` asset_uid="${asset_uid}"` : `` }${node.attrs['class-name'] ? ` class="${sanitizeHTML(node.attrs['class-name'])}"`: ``}${src ? ` src="${sanitizeHTML(src)}"` : ``}${alt ? ` alt="${alt}"` : `` }${target === "_blank" ? ` target="_blank"` : `` }${style ? ` style="${style}"` : `` } />`;
190+
const assetUidAttr = assetUid ? ` asset_uid="${assetUid}"` : '';
191+
const classAttr = className ? ` class="${sanitizeHTML(className)}"` : '';
192+
const srcAttr = src ? ` src="${sanitizeHTML(src)}"` : '';
193+
const altAttr = alt ? ` alt="${alt}"` : '';
194+
const targetAttr = target === "_blank" ? ` target="_blank"` : '';
195+
const styleAttr = style ? ` style="${style}"` : '';
196+
197+
const imageTag = `<img${assetUidAttr}${classAttr}${srcAttr}${altAttr}${targetAttr}${styleAttr} />`;
198+
const styleAttrFig = style ? ` style="${style}"` : '';
146199

147-
return `<figure${style ? ` style="${style}"` : ''}>` +
200+
return `<figure${styleAttrFig}>` +
148201
(link ? `<a href="${link}" target="${target || ""}">` : "") +
149202
imageTag +
150203
(link ? `</a>` : "") +

0 commit comments

Comments
 (0)