UNPKG

caniemail

Version:

HTML and CSS Feature Support for Email Clients from caniemail.com

89 lines 4.42 kB
import { ElementType } from 'htmlparser2'; import styleToObject from 'style-to-object'; import { getMatchingAttributeTitles, getMatchingElementAttributePairTitles, getMatchingElementTitles } from '../html-titles.js'; import { getMatchingImageTitles, getUrlsFromSrcset } from '../image-titles.js'; import { LocationIndex } from '../location.js'; import { checkDeclarations } from './check-css.js'; import { checkFeatures } from './check-features.js'; const checkHtmlNode = ({ clients, issues, node, locationIndex }) => { const elementTitles = getMatchingElementTitles({ tagName: node.tagName }); const position = node.sourceCodeLocation || (node.startIndex !== null && node.endIndex !== null ? locationIndex.positionOf(node.startIndex, node.endIndex) : undefined); checkFeatures({ clients, issues, titles: elementTitles, position }); if (node.attributes !== void 0) { const elementAttributePairTitles = getMatchingElementAttributePairTitles({ attributes: Object.fromEntries(node.attributes.map((attr) => [attr.name, attr.value])), tagName: node.tagName }); const attributeTitles = getMatchingAttributeTitles({ attributes: node.attributes.map((attr) => attr.name) }); checkFeatures({ clients, issues, titles: attributeTitles, position }); checkFeatures({ clients, issues, titles: elementAttributePairTitles, position }); const attributeMap = Object.fromEntries(node.attributes.map((attr) => [attr.name, attr.value])); const imageUrls = []; if (node.tagName === 'img' && attributeMap.src) imageUrls.push(attributeMap.src); if (node.tagName === 'source' && attributeMap.srcset) { imageUrls.push(...getUrlsFromSrcset(attributeMap.srcset)); } if (attributeMap.srcset) imageUrls.push(...getUrlsFromSrcset(attributeMap.srcset)); checkFeatures({ clients, issues, titles: getMatchingImageTitles({ urls: imageUrls }), position }); const styleAttr = node.attributes.find((attr) => attr.name === 'style'); if (styleAttr !== void 0) { const styleObject = (styleToObject.default ?? styleToObject)(styleAttr.value); if (styleObject !== null) { // Get the style attribute location const attrLoc = styleAttr.sourceCodeLocation; const offset = attrLoc ? { line: attrLoc.startLine, column: attrLoc.startCol + 'style="'.length } : undefined; const declarations = Object.entries(styleObject).map(([property, value]) => ({ property, value: String(value), position })); checkDeclarations({ clients, declarations, issues, offset }); } } } if ('childNodes' in node) { for (const childNode of node.childNodes) { if (childNode.type === ElementType.Tag || childNode.type === ElementType.Style) { checkHtmlNode({ clients, issues, node: childNode, locationIndex }); } else if (childNode.type === ElementType.Comment) { const position = childNode.startIndex !== null && childNode.endIndex !== null ? locationIndex.positionOf(childNode.startIndex, childNode.endIndex) : undefined; checkFeatures({ clients, issues, titles: ['HTML comments'], position }); } } } }; export const checkHtml = ({ clients, issues, document, source }) => { const locationIndex = new LocationIndex(source); for (const childNode of document.childNodes) { if (childNode.type === ElementType.Tag) { checkHtmlNode({ clients, issues, node: childNode, locationIndex }); } else if (childNode.type === ElementType.Comment) { const position = childNode.startIndex !== null && childNode.endIndex !== null ? locationIndex.positionOf(childNode.startIndex, childNode.endIndex) : undefined; checkFeatures({ clients, issues, titles: ['HTML comments'], position }); } } }; //# sourceMappingURL=check-html.js.map