Skip to content

Commit

Permalink
Auto discover valid HTML attributes from props
Browse files Browse the repository at this point in the history
  • Loading branch information
thewebartisan7 committed Dec 6, 2022
1 parent dd6c2f2 commit 9c26fb2
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 5 deletions.
3 changes: 2 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ ava.config.js
changelog.md
xo.config.js
.lintstagedrc
.github
.github
scripts
79 changes: 79 additions & 0 deletions scripts/fetch-attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* eslint-disable no-undef */
/* eslint-disable curly */
/* eslint-disable padded-blocks */
const elementAttributes = {};

// Exception
// tabindex seem to be allowed on any element according to https://www.w3schools.com/tags/att_global_tabindex.asp
// but here https://www.w3.org/TR/html4/index/attributes.html seem not
// So below we add this exception
const allowedAttributes = ['tabindex'];

// Fetch valid html elements (not deprecated)
fetch('https://www.w3.org/TR/html4/index/elements.html').then(response => {
return response.text();
}).then(html => {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const rows = doc.querySelectorAll('table tbody tr');

rows.forEach(row => {
const elementName = row.querySelector('td[title="Name"]');
if (!elementName) return;

const isDeprecated = row.querySelector('td[title="Depr."]');
if (isDeprecated.textContent.trim() === 'D') return;

elementAttributes[elementName.textContent.trim()] = [...allowedAttributes];
});

console.log('Valid HTML elements', elementAttributes);

}).catch(error => {
console.warn(error.message);
});

// Fetch valid HTML attributes (not deprecated)

fetch('https://www.w3.org/TR/html4/index/attributes.html').then(response => {
return response.text();
}).then(html => {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const rows = doc.querySelectorAll('table tbody tr');

rows.forEach(row => {
const attributeName = row.querySelector('td[title="Name"]');
if (!attributeName) return;

const isDeprecated = row.querySelector('td[title="Depr."]');
if (isDeprecated.textContent.trim() === 'D') return;

const relatedElements = row.querySelector('td[title="Related Elements"]').textContent.trim().split(',');

const exclude = relatedElements[0].includes('All elements but');
if (exclude) {
// Allowed in all but not in relatedElements
relatedElements[0].replace('All elements but', '');
Object.keys(elementAttributes).forEach(elementName => {
// Skip element that doesn't support the attribute
if (relatedElements.includes(elementName)) return;
elementAttributes[elementName].push(attributeName.textContent.trim());
});
} else {
relatedElements.forEach(element => {
// Skip deprecated element
if (!elementAttributes[element]) return;
elementAttributes[element].push(attributeName.textContent.trim());
});
}
});

Object.keys(elementAttributes).forEach(element => {
elementAttributes[element].sort();
});

console.log('elementAttributes', elementAttributes);
}).catch(error => {
console.warn(error.message);
});
25 changes: 23 additions & 2 deletions src/process-attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
const {match} = require('posthtml/lib/api');
const parseAttrs = require('posthtml-attrs-parser');
const styleToObject = require('style-to-object');
const omit = require('lodash/omit');
const keys = require('lodash/keys');
const union = require('lodash/union');
const pick = require('lodash/pick');
const difference = require('lodash/difference');
const each = require('lodash/each');
const has = require('lodash/has');
const extend = require('lodash/extend');
const isString = require('lodash/isString');
const isObject = require('lodash/isObject');
const validAttributes = require('./valid-attributes');

/**
* Map component attributes that it's not defined as props to first element of node
Expand Down Expand Up @@ -43,7 +45,26 @@ module.exports = (currentNode, attributes, props, options, aware) => {

const nodeAttrs = parseAttrs(mainNode.attrs, options.attrsParserRules);

const mainNodeAttributes = omit(attributes, union(keys(props), [options.attribute], keys(aware), keys(options.props), ['$slots']));
// Attributes to be excluded
const excludeAttributes = union(validAttributes.blacklist, keys(props), [options.attribute], keys(aware), keys(options.props), ['$slots']);
// All valid HTML attributes for the main element
const allValidElementAttributes = validAttributes.elementAttributes[mainNode.tag.toUpperCase()];
// Valid HTML attributes without the excluded
const validElementAttributes = difference(allValidElementAttributes, excludeAttributes);
// Add override attributes
validElementAttributes.push('override:style');
validElementAttributes.push('override:class');
// Pick valid attributes from passed
const mainNodeAttributes = pick(attributes, validElementAttributes);

// Get additional specified attributes
each(attributes, (value, attr) => {
each(validAttributes.additionalAttributes, additionalAttr => {
if (additionalAttr === attr || (additionalAttr.endsWith('*') && attr.startsWith(additionalAttr.replace('*', '')))) {
mainNodeAttributes[attr] = value;
}
});
});

each(mainNodeAttributes, (value, key) => {
if (['class', 'style'].includes(key)) {
Expand Down
Loading

0 comments on commit 9c26fb2

Please sign in to comment.