Skip to content

Commit

Permalink
improve CSS parser
Browse files Browse the repository at this point in the history
  • Loading branch information
typicode committed Mar 18, 2024
1 parent d56c9bb commit eff3884
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 40 deletions.
15 changes: 14 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@types/node": "^20.11.19",
"@types/react": "^18.2.56",
"@types/react-dom": "^18.2.19",
"@types/stylis": "^4.2.5",
"@typicode/eslint-config": "^2.0.0",
"eslint": "^8.56.0",
"prettier": "^3.2.5",
Expand All @@ -45,6 +46,7 @@
},
"dependencies": {
"chokidar": "^3.6.0",
"globby": "^14.0.1"
"globby": "^14.0.1",
"stylis": "^4.3.1"
}
}
99 changes: 61 additions & 38 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,84 @@
import fs from 'node:fs'
import path from 'node:path'
import { compile, Element } from 'stylis'

export interface ParsedInput {
className: string
tag: string
data: Record<string, string[] | boolean>
}

const classNameRegex = /@scope\s*\(\s*\.(?<className>\w+)\s*\)/
const tagRegex = /(?<tag>\w+):scope/
const enumDataAttributeRegex =
/\[\s*data-(?<attribute>.*?)\s*=\s*["']?(?<value>.*?)["']?\s*\]/g
const booleanDataAttributeRegex = /\[\s*data-(?<attribute>.\w*)\s*\]/g
/\[data-(?<attribute>[a-z-]+)='(?<value>[^']*)'\]/g
const booleanDataAttributeRegex = /\[data-(?<attribute>[a-z-]+)(?=\])/g

export function parseInput(input: string): ParsedInput {
const result: ParsedInput = { className: '', tag: '', data: {} }
function visit(nodes: Element[], arr: { type: string; props: string[] }[]) {
for (const node of nodes) {
if (['@scope', 'rule'].includes(node.type) && Array.isArray(node.props)) {
arr.push({ type: node.type, props: node.props })
}

// Parse class name
const className = classNameRegex.exec(input)?.groups?.['className']
if (className === undefined) {
throw new Error('Could not parse class name')
if (Array.isArray(node.children)) {
visit(node.children, arr)
}
}
result.className = className
}

// Parse tag
const tag = tagRegex.exec(input)?.groups?.['tag']
if (tag === undefined) {
throw new Error('Could not parse tag')
}
result.tag = tag
export function parseInput(input: string): ParsedInput {
const result: ParsedInput = { className: '', tag: '', data: {} }

// Parse enum data attributes
for (const match of input.matchAll(enumDataAttributeRegex)) {
const attribute = match.groups?.['attribute']
const value = match.groups?.['value'] ?? ''
const arr: { type: string; props: string[] }[] = []
visit(compile(input), arr)

if (attribute === undefined) {
continue
arr.forEach((node) => {
if (node.type === '@scope') {
const prop = node.props[0]
if (prop === undefined) {
return
}
result.className = prop.replace('(.', '').replace(')', '')
return
}

result.data[attribute] ||= []
if (node.type === 'rule') {
const prop = node.props[0]
if (prop === undefined) {
return
}

const attr = result.data[attribute]
if (Array.isArray(attr) && !attr.includes(value)) {
attr.push(value)
}
}
// Parse tag
if (prop.endsWith(':scope')) {
result.tag = prop.replace(':scope', '')
}

// Parse boolean data attributes
for (const match of input.matchAll(booleanDataAttributeRegex)) {
const attribute = match.groups?.['attribute']
if (attribute === undefined) {
continue
}
// Parse enum data attributes
for (const match of prop.matchAll(enumDataAttributeRegex)) {
const attribute = match.groups?.['attribute']
const value = match.groups?.['value'] ?? ''

result.data[attribute] ||= true
}
if (attribute === undefined) {
continue
}

result.data[attribute] ||= []

const attr = result.data[attribute]
if (Array.isArray(attr) && !attr.includes(value)) {
attr.push(value)
}
}

// Parse boolean data attributes
for (const match of prop.matchAll(booleanDataAttributeRegex)) {
const attribute = match.groups?.['attribute']
if (attribute === undefined) {
continue
}

result.data[attribute] ||= true
}
}
})

return result
}
Expand Down Expand Up @@ -106,6 +129,6 @@ export function createFile(filename: string) {

const name = path.basename(filename, '.mist.css')
data = render(name, parsedInput)

fs.writeFileSync(`${filename}.tsx`, data)
}

0 comments on commit eff3884

Please sign in to comment.