emoji-picker-element/bin/generateCssDocs.js

99 lines
3.3 KiB
JavaScript

import path from 'node:path'
import * as sass from 'sass'
import { markdownTable as table } from 'markdown-table'
import { readFile, writeFile } from './fs.js'
import { replaceInReadme } from './replaceInReadme.js'
import postcss from 'postcss'
import { FONT_FAMILY } from '../src/picker/constants.js'
const __dirname = path.dirname(new URL(import.meta.url).pathname)
// To avoid code duplication, we could not declare this in variables.scss
const MANUAL_VARS = [{
name: '--emoji-font-family',
value: FONT_FAMILY,
comment: 'Font family for a custom emoji font (as opposed to native emoji)'
}]
const START_MARKER = '<!-- CSS variable options start -->'
const END_MARKER = '<!-- CSS variable options end -->'
function extractCSSVariables (node) {
const res = []
for (let i = 0; i < node.nodes.length; i++) {
const subNode = node.nodes[i]
if (subNode.type === 'decl' && subNode.prop.startsWith('--')) {
const nextNode = node.nodes[i + 1]
const comment = (nextNode && nextNode.type === 'comment' && nextNode.text.trim()) || undefined
res.push({
name: subNode.prop,
value: subNode.value,
comment
})
}
}
return res
}
// Find all the CSS variables declared on the :host and print them out
// into the README as documentation
async function generateCssVariablesData (css) {
const ast = postcss.parse(css)
const hosts = ast.nodes.filter(({ selector }) => ([':host', ':host,\n:host(.light)'].includes(selector)))
const darkHosts = ast.nodes.filter(({ selector }) => selector === ':host(.dark)')
const vars = hosts.map(extractCSSVariables).flat().concat(MANUAL_VARS)
const darkVars = darkHosts.map(extractCSSVariables).flat()
const sortedVars = vars.sort((a, b) => a.name < b.name ? -1 : 1)
return sortedVars.map(({ name, value, comment }) => {
const darkIndex = darkVars.findIndex(_ => _.name === name)
let darkValue = darkIndex !== -1 ? darkVars[darkIndex].value : ''
if (darkValue === value) {
darkValue = ''
}
const wrap = _ => ('`' + _ + '`')
return [wrap(name), wrap(value), darkValue ? wrap(darkValue) : '', comment || '']
})
}
function generateMarkdownTable (cssData) {
const headers = ['Variable', 'Default', 'Default (dark)', 'Description']
return table([
headers,
...cssData
])
}
async function replaceInCustomElementsJson (cssData) {
const jsonFilename = path.join(__dirname, '../custom-elements.json')
const json = JSON.parse(await readFile(jsonFilename, 'utf8'))
const unwrap = _ => _.substring(1, _.length - 1) // remove backticks
json.modules[0].declarations[0].cssProperties = cssData.map(([name, value, darkValue, comment]) => {
return {
name: unwrap(name),
description: `${comment} (default: ${value}${darkValue ? `, dark default: ${darkValue}` : ''})`.trim(),
default: JSON.stringify(unwrap(value))
}
})
await writeFile(jsonFilename, JSON.stringify(json, null, 2), 'utf8')
}
async function main () {
const css = sass.compile('./src/picker/styles/variables.scss').css
const cssData = await generateCssVariablesData(css)
const markdown = generateMarkdownTable(cssData)
await replaceInReadme(START_MARKER, END_MARKER, markdown)
await replaceInCustomElementsJson(cssData)
}
main().catch(err => {
console.error(err)
process.exit(1)
})