-
-
Notifications
You must be signed in to change notification settings - Fork 52
Export CSS parser function, add CLI with --stats flag, and add stats utility #76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 5 commits
3321b1f
fc53e2f
9a02c01
dbd6060
3c6da86
44be182
3ce456e
b4481b0
bf2f871
a44df83
6e1cec8
3f40f7b
173ccac
6e926a6
f3aaa48
9bcb29a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| #!/usr/bin/env node | ||
| import { parseArgs } from 'node:util' | ||
| import type { Parsed } from './index' | ||
| const { parse } = require('./index') | ||
|
|
||
| async function main() { | ||
| // Parse command line arguments (no args expected for now) | ||
| parseArgs({ | ||
| args: process.argv.slice(2), | ||
| options: {}, | ||
| strict: true, | ||
| }) | ||
|
|
||
| // Read CSS from stdin | ||
| let css = '' | ||
|
|
||
| if (process.stdin.isTTY) { | ||
| console.error('Error: Please provide CSS via stdin') | ||
| console.error('Usage: mistcss < input.css') | ||
| process.exit(1) | ||
| } | ||
|
|
||
| for await (const chunk of process.stdin) { | ||
| css += chunk | ||
| } | ||
|
|
||
| // Parse the CSS | ||
| const parsed = parse(css) | ||
|
|
||
| // Convert Sets to Arrays for JSON serialization | ||
| const serializable = Object.fromEntries( | ||
| (Object.entries(parsed) as [string, Parsed[string]][]).map(([key, value]) => [ | ||
| key, | ||
| { | ||
| ...value, | ||
| attributes: Object.fromEntries( | ||
| Object.entries(value.attributes).map(([k, v]) => [k, Array.from(v)]) | ||
| ), | ||
| booleanAttributes: Array.from(value.booleanAttributes), | ||
| properties: Array.from(value.properties), | ||
| }, | ||
| ]) | ||
| ) | ||
|
|
||
| // Output the parsed result as JSON | ||
| console.log(JSON.stringify(serializable, null, 2)) | ||
| } | ||
|
|
||
| main().catch((err) => { | ||
| console.error('Error:', err.message) | ||
| process.exit(1) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import fs = require('node:fs') | ||
| import { type PluginCreator } from 'postcss' | ||
| import postcss = require('postcss') | ||
| import selectorParser = require('postcss-selector-parser') | ||
| import atImport = require('postcss-import') | ||
| import path = require('node:path') | ||
|
|
@@ -14,7 +15,7 @@ declare module 'postcss-selector-parser' { | |
| } | ||
| } | ||
|
|
||
| type Parsed = Record< | ||
| export type Parsed = Record< | ||
| string, | ||
| { | ||
| tag: string | ||
|
|
@@ -164,6 +165,50 @@ function initialParsedValue(): Parsed[keyof Parsed] { | |
| } | ||
| } | ||
|
|
||
| export function parse(css: string): Parsed { | ||
| const parsed: Parsed = {} | ||
| let current: Parsed[keyof Parsed] = initialParsedValue() | ||
|
|
||
| // Parse the CSS using postcss | ||
| const root = postcss.parse(css) | ||
|
|
||
| root.walkRules((rule) => { | ||
| selectorParser((selectors) => { | ||
| selectors.walk((selector) => { | ||
| if (selector.type === 'tag') { | ||
| current = parsed[key(selector)] = initialParsedValue() | ||
| current.tag = selector.toString().toLowerCase() | ||
| const next = selector.next() | ||
| if (next?.type === 'attribute') { | ||
| const { attribute, value } = next as selectorParser.Attribute | ||
| if (value) current.rootAttribute = attribute | ||
| } | ||
| } | ||
|
|
||
| if (selector.type === 'attribute') { | ||
| const { attribute, value } = selector as selectorParser.Attribute | ||
| if (value) { | ||
| const values = (current.attributes[attribute] ??= | ||
| new Set<string>()) | ||
| values.add(value) | ||
| } else { | ||
| current.booleanAttributes.add(attribute) | ||
| } | ||
| } | ||
| }) | ||
| }).processSync(rule.selector, { | ||
| lossless: false, | ||
| }) | ||
|
|
||
| rule.walkDecls(({ prop }) => { | ||
| if (prop.startsWith('--') && prop !== '--apply') | ||
| current.properties.add(prop) | ||
| }) | ||
|
Comment on lines
+209
to
+219
|
||
| }) | ||
|
|
||
| return parsed | ||
| } | ||
|
|
||
| const _mistcss: PluginCreator<{}> = (_opts = {}) => { | ||
| return { | ||
| postcssPlugin: '_mistcss', | ||
|
|
@@ -225,4 +270,7 @@ const mistcss: PluginCreator<{}> = (_opts = {}) => { | |
|
|
||
| mistcss.postcss = true | ||
|
|
||
| export { mistcss as default } | ||
| module.exports = mistcss | ||
| module.exports.parse = parse | ||
| module.exports.default = mistcss | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
exportsfield is a string, which restricts consumers to importing only the package root. That makes the documentedmistcss/lib/stats(and any other subpath likelib/stats) unavailable in Node whenexportsis present. Ifstatsis intended to be a public utility, switchexportsto an object and explicitly export./stats(and any other public subpaths), while keeping.pointing at./lib/index.js.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot don't export stats