-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
It's a lot of text, I wanted to include enough information. It is important for a front-end tooling ecosystem. But maybe it's just a stylelint only problem with custom syntaxes ¯\_(ツ)_/¯
Custom syntaxes
PostCSS by default supports CSS in a form, where parsed file has only CSS. Most PostCSS syntaxes also for more classic CSS usage: SCSS, Sass, SugarSS, Less. However there are cases, when CSS is a part of other language. style tags and style attributes in HTML, Vue, Svelte. CSS-in-JS in JS template literals (Styled Components) or objects (JSS).
Currently stylelint supports both types of files: classic one file — one syntax, and the other one file — multiple syntaxes. It is done by huge work of @gucong3000. He made custom syntaxes to support multiple syntaxes in one file:
- postcss-css-in-js — PostCSS syntax for parsing CSS in JS
- postcss-html — PostCSS syntax for parsing HTML (and HTML-like)
- postcss-markdown — PostCSS Syntax for parsing Markdown
All this syntaxes are powered by postcss-syntax. This package does two jobs: automatically switch PostCSS syntax based on file extensions, and provides common tools for other syntaxes (more on this later).
These packages were developed only by him. Unfortunately he is not active on Github for the past two years. These packages are complex and intertwined together. It's hard to understand how they work. No one else know how they work. I digged into them recently in an attempt to upgrade to PostCSS 8. Here's my surface understanding (they my not be accurate).
postcss-syntax
postcss-syntax does two jobs. First job is documented in package's readme: automatically switch PostCSS syntax based on file extensions. Second job is not documented, but very important for all other syntaxes: provide common tools for syntaxes. It introduces a new AST node type Document, but new AST node still has type: 'root', because it's extended from PostCSS Root class. It does it by monkey-patching PostCSS via Node.js require.cache (crazy hack). For example for HTML document with two style tags we would have this kind of AST:
Document {
nodes: [
Root {
nodes: [
Rule {},
AtRule {},
]
},
Root {
nodes: [
Rule {},
AtRule {},
]
},
]
}
postcss-syntax makes sure that every PostCSS plugins are run on each Root.
Also it adds extra information to sources. Probably, for a successful stringifying.
Another issues with these syntaxes:
- Implicit dependencies and code execution.
postcss-syntaxalso calls custom syntax internal files, whenpostcss/syntax/load-syntax.jsis called from custom syntax. - They use
postcssinternal files, which I believe is not allowed, because they are not part of official API.
Why it works this way
I don't know exactly why postcss-syntax and dependent syntaxes architected this way.
However, I have an idea about some of behavior of these custom syntaxes. Each stylelint rule is a PostCSS plugin. These syntaxes were created with stylelint in mind. For many stylelint rules it makes more sense to have e. g. every styled component (const Element = styled.div``) as a separate root so a rule (PostCSS plugin) is run only on this piece of code.
For example we have following HTML:
<p style="color: red"></p>
<p style="color: blue"></p>If we don't distinguish them as separate Roots, rule like declaration-block-no-duplicate-properties would show a lint violation.
We can't use Rule type for each style attribute, because we have many lint rules, which would check different aspects of Rule nodes (selectors, brackets, spacings), which would not be applicable for this fake Rule. In the mean time there is no stylelint rules, which has any specific behavior for a Root node.
Having postcss-syntax parse each entity as a separate Root, and they run plugins over each Root, allowed adoption of all this syntaxes in stylelint without changing hundreds of rules in stylelint codebase and in stylelint plugins.
What it's all about
Custom syntaxes supported by stylelint and its plugins were in a rough state for the past two years. And usage of CSS in files, which are not CSS only, is increasing. It's important for stylelint and PostCSS to support such cases. I would like to find a solution to this problem. postcss-css-in-js, postcss-html, postcss-markdown and postcss-syntax in they current form should be changed. To support PostCSS 8 and unblock community to make changes to this syntaxes, by refactoring and making code architecture more clear.
I think it's important to find a vector which PostCSS and custom syntaxes should follow to support all types of CSS flavors modern development has.