diff --git a/.gitignore b/.gitignore index c977c85..15c4d8e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ -.DS_Store -*.d.ts -*.log coverage/ node_modules/ +*.d.ts +*.log +.DS_Store yarn.lock diff --git a/build.js b/build.js index 8f3fd94..3c91fa0 100644 --- a/build.js +++ b/build.js @@ -1,108 +1,94 @@ import fs from 'node:fs' import https from 'node:https' -import concat from 'concat-stream' +import concatStream from 'concat-stream' import {bail} from 'bail' import {unified} from 'unified' -import html from 'rehype-parse' +import rehypeParse from 'rehype-parse' import {selectAll} from 'hast-util-select' import {toString} from 'hast-util-to-string' import {htmlElementAttributes} from './index.js' -const processor = unified().use(html) +const own = {}.hasOwnProperty -// Global attributes. -let globals = htmlElementAttributes['*'] +const processor = unified().use(rehypeParse) -if (!globals) { - globals = [] - htmlElementAttributes['*'] = globals +if (!('*' in htmlElementAttributes)) { + htmlElementAttributes['*'] = [] } +// Global attributes. +const globals = htmlElementAttributes['*'] + // Crawl WHATWG HTML. -https.get('https://html.spec.whatwg.org/multipage/indices.html', onhtml) - -/** - * @param {import('http').IncomingMessage} response - */ -function onhtml(response) { - response.pipe(concat(onconcat)).on('error', bail) - - /** - * @param {Buffer} buf - */ - function onconcat(buf) { - const nodes = selectAll('#attributes-1 tbody tr', processor.parse(buf)) - let index = -1 - const result = {} - /** @type {string} */ - let key - /** @type {string} */ - let name - /** @type {string} */ - let value - /** @type {string[]} */ - let elements - /** @type {string} */ - let tagName - /** @type {string[]} */ - let attributes - /** @type {number} */ - let offset - - // Throw if we didn’t match, e.g., when the spec updates. - if (nodes.length === 0) { - throw new Error('Missing results in html') - } - - while (++index < nodes.length) { - name = toString(nodes[index].children[0]).trim() - value = toString(nodes[index].children[1]).trim() - - if (/custom elements/i.test(value)) { - continue - } - - offset = -1 - elements = /HTML elements/.test(value) - ? ['*'] - : value.split(/;/g).map((d) => d.replace(/\([^)]+\)/g, '').trim()) - - while (++offset < elements.length) { - tagName = elements[offset].toLowerCase().trim() - attributes = - htmlElementAttributes[tagName] || - (htmlElementAttributes[tagName] = []) - - if (!attributes.includes(name)) { - attributes.push(name) +https.get('https://html.spec.whatwg.org/multipage/indices.html', (response) => { + response + .pipe( + concatStream((buf) => { + const nodes = selectAll('#attributes-1 tbody tr', processor.parse(buf)) + const result = {} + let index = -1 + + // Throw if we didn’t match, e.g., when the spec updates. + if (nodes.length === 0) { + throw new Error('Missing results in html') + } + + while (++index < nodes.length) { + const name = toString(nodes[index].children[0]).trim() + const value = toString(nodes[index].children[1]).trim() + + if (/custom elements/i.test(value)) { + continue + } + + const elements = /HTML elements/.test(value) + ? ['*'] + : value.split(/;/g).map((d) => d.replace(/\([^)]+\)/g, '').trim()) + let offset = -1 + + while (++offset < elements.length) { + const tagName = elements[offset].toLowerCase().trim() + + if (!own.call(htmlElementAttributes, tagName)) { + htmlElementAttributes[tagName] = [] + } + + /** @type {string[]} */ + const attributes = htmlElementAttributes[tagName] + + if (!attributes.includes(name)) { + attributes.push(name) + } + } } - } - } - const keys = Object.keys(htmlElementAttributes).sort() - index = -1 + const keys = Object.keys(htmlElementAttributes).sort() + index = -1 + + while (++index < keys.length) { + const key = keys[index] - while (++index < keys.length) { - key = keys[index] - htmlElementAttributes[key].sort() + htmlElementAttributes[key].sort() - if (key !== '*') { - htmlElementAttributes[key] = htmlElementAttributes[key].filter( - (/** @type {string} */ d) => !globals.includes(d) + if (key !== '*') { + htmlElementAttributes[key] = htmlElementAttributes[key].filter( + (/** @type {string} */ d) => !globals.includes(d) + ) + } + + if (htmlElementAttributes[key].length > 0) { + result[key] = htmlElementAttributes[key] + } + } + + fs.writeFile( + 'index.js', + 'export const htmlElementAttributes = ' + + JSON.stringify(result, null, 2) + + '\n', + bail ) - } - - if (htmlElementAttributes[key].length > 0) { - result[key] = htmlElementAttributes[key] - } - } - - fs.writeFile( - 'index.js', - 'export const htmlElementAttributes = ' + - JSON.stringify(result, null, 2) + - '\n', - bail + }) ) - } -} + .on('error', bail) +}) diff --git a/test.js b/test.js index fe6f718..454345b 100644 --- a/test.js +++ b/test.js @@ -21,23 +21,15 @@ test('htmlElementAttributes', function (t) { t.doesNotThrow(function () { /** @type {string} */ let key - /** @type {string[]} */ - let props - /** @type {number} */ - let index - /** @type {string} */ - let prop - /** @type {string} */ - let label for (key in htmlElementAttributes) { if (own.call(htmlElementAttributes, key)) { - props = htmlElementAttributes[key] - index = -1 + const props = htmlElementAttributes[key] + let index = -1 while (++index < props.length) { - prop = props[index] - label = prop + ' in ' + key + const prop = props[index] + const label = prop + ' in ' + key assert.strictEqual(typeof prop, 'string', label + ' should be string') assert.strictEqual(