Skip to content

Instantly share code, notes, and snippets.

@jonathantneal
Created January 9, 2020 18:30
Show Gist options
  • Save jonathantneal/8a91bee7df1a40e94d0354b9c51285ca to your computer and use it in GitHub Desktop.
Save jonathantneal/8a91bee7df1a40e94d0354b9c51285ca to your computer and use it in GitHub Desktop.
CSS Parser Super Core (the barest bits)
function parse(cssText) {
// this regex matches all non-contextual nodes; which are comments, whitespaces, strings, numbers, named identifiers, and delimiters.
var cssRegExp = /\/\*((?:[^*]|\*[^\/])*)\*\/|([ \t\n\f\r]+)|"((?:[^"\\\n\f\r]|\\\r\n|\\[\W\w])*)"|'((?:[^'\\\n\f\r]|\\\r\n|\\[\W\w])*)'|#((?:[-\w]|[^\x00-\x7F]|\\[^\n\f\r])+)|([+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[Ee]-?\d+)?)|(-?(?:[-A-Z_a-z]|[^\x00-\x7F]|\\[^\n\f\r])(?:[-\w]|[^\x00-\x7F]|\\[^\n\f\r])*)|([\uD800-\uDBFF][\uDC00-\uDFFF]|[\W\w])/g;
// the contextual nodes generated from these matches are:
// - numbers with a unit (number + named identifier),
// - at identifiers (`@` + named identifier),
// - blocks (everything between equally nested `(` and `)` or `[` and `]` or `{` and `}`), and
// - functions (named identifier + block).
// you will likely create a root/parent scope for organizing your blocks and functions
// var cssRoot = { value: [] }, cssParent = cssRoot;
var cssMatches;
while ((cssMatches = cssRegExp.exec(cssText)) !== null) {
// the regex includes a `lastIndex` property, critical for building source maps
// the matches spread nicely into a function
parser.apply(cssRegExp, cssMatches);
}
// return cssRoot
function parser(value, comment, whitespace, stringDoubleQuoted, stringSingleQuoted, hashIdentifier, number, namedIdentifier, delimiter) {
// any of these nodes are assigned the current parent scope (initially the root).
// a `comment` is the content within `/*` and `*/`.
// a `whitespace` is the consecutive spaces, tabs, and newlines.
// a `stringDoubleQuoted` or `stringSingleQuoted` is the content within its respective quotes.
// a `hashIdentifier` is the content after its hash mark, like `00f` or `main`.
// a `number` is the numeric content, including decimals and stuff with `e`, with an empty unit.
// a `namedIdentifier` is the alphanumeric content, including keywords and property names.
// - any namedIdentifier following a number are merged into a number with a unit.
// - any namedIdentifier following a `@` delimiter are merged into an `atIdentifier`.
// a `delimiter` is a boundary-like character, like `,` or `;` or `>`.
// - any `(` or `[` or `{` creates a block assigned its respective delimiter, which is the new parent scope.
// - any `(` block following a namedIdentifier becomes a function.
// - any `)` or `]` or `}` matching the parent block delimiter closes out the parent scope.
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment