CSS Lube is a CSS library that I created. I personally use the term "CSS interpreter", but it can also be seen as a general utility-first library.
CSS Lube: Highly-optimized CSS Interpreter
Yeom suyun ・ Aug 22 '23
One of the slogans of this library is "It takes only 3 minutes to learn this".
In order to make this claim a reality, I created a vscode extension.
The features required for VSCE were simple autocompletion and hover messages to prevent the need to memorize shortcuts, and I decided to add code highlighting features as a side benefit.
VSCE's basic API
First, I looked at the tutorials for VSCE.
The sample repository I found in the tutorial had many examples, and I copied the source code for autocompletion and code highlighting, and then proceeded with the work by finding additional information through Google search.
I wish the sample code was more helpful, but I was able to learn how to use the VSCE API required for feature implementation with the help of Stack Overflow.
const hover_message = new vscode.MarkdownString()
hover_message.supportHtml = true
hover_message.appendMarkdown("**" + compile_style(cname) + "**")
How to find the class name using AST
After I learned the basic usage of the VSCE API, I needed a way to find where the parts of the code corresponding to the HTML's className are.
At first, I tried to use regular expressions naively.
const class_name_regex = /(?<=class(?:Name)?=")[^"{]*/g
However, since common front-end frameworks use JavaScript code blocks together when writing HTML, it was necessary to support the functionality of the extension for JavaScript strings used in className.
<script>
const name = "world"
</script>
<div className={`c=red
bg=blue
${is_left ? "pr=1" : "pl=1"}`}>
<span>Hello {name}!</span>
</div>
My solution to this was to implement an HTML AST parser and use it, even if support for JSX would be postponed until later.
Looking at the issues of Svelte, it seemed that there was no parser that met my requirements, and I created an AST parser for it myself.
The principle is simple.
The starting point of a script block can be identified using {
.
The starting point of a string can be identified using '
, "
, or backtick.
If }
appears outside of a string, it indicates the end of the script block.
The challenge is to determine when a string ends.
For example, in the case of backticks, I used the following regular expression to find the end point of backticks.
const stop_backtick_regex = /(?<=(?<!\\)(\\\\)*)(?:`|\${)/
I was able to determine the end point of an unescaped string using (?<=(?<!\\)(\\\\)*)
.
Ultimately, the AST was composed using the types below.
import type ast_syntax_error from "./src/parse_dom/src/AstSyntaxError.js"
export type AstNode =
Attribute
| Script
| String
| Style
| Element
| Text
export type AstSyntaxError = ReturnType<typeof ast_syntax_error>
export type Attribute = {
type: "Attribute"
name: string
value: (
true
| Script & { subType: "block" }
| String & { subType: "single" | "double" }
)
} & BaseAstNode
type BaseAstNode = {
end: number
start: number
}
export type Element = {
attributes: Attribute[]
children: AstNode[]
name: string
subType: "close" | "closed" | "open"
type: "Element"
} & BaseAstNode
export type Script = {
strings: String[]
subType: "block" | "content" | "template"
type: "Script"
} & BaseAstNode
export type String = {
scripts: Script[]
subType: "backtick" | "double" | "single"
type: "String"
} & BaseAstNode
export type Style = {
type: "Style"
} & BaseAstNode
export type Text = {
type: "Text"
} & BaseAstNode
Other than this idea, the code was not very elegant, as it was written in a way that simply wrote and debugged repeatedly until it was completed without a clear design.
Still, if you are curious about the actual code, please refer to the link below.
dom-eater
css-lube/intellisense
TODO?
Writing Ast test code is very difficult.
I am thinking about how to write test code simply.
JSX support seems to be more difficult than expected.
It is necessary to support parsing for code that is being written, but even in the case of acorn, it seems that JSXElement
parsing is not working properly if the loose
(syntax error allowed) option is enabled.
Thank you.
Top comments (0)