Add new `Document` node type
See original GitHub issueIt’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-syntax
also calls custom syntax internal files, whenpostcss/syntax/load-syntax.js
is called from custom syntax. - They use
postcss
internal 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.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:6
- Comments:7 (6 by maintainers)
@jeddy3 I do not have time to support these syntaxes. But we can move them to
postcss
org if the Stylelint team is OK with it.This is exciting stuff! I had one minor point to chime in on.
Like postcss-js and postcss-scss, it’d be great if these syntaxes lived in the PostCSS org (and were published outside of the stylelint NPM scope). These syntaxes are applicable to more than stylelint, e.g. https://github.com/postcss/postcss/issues/1494 which was for adding Tailwind to LitElement Web Components.
I was quickly digging into the process for NPM disputes and it turns out you already have ownership, @ai.
In hindsight, I should’ve checked this before we published our forks under the stylelint NPM scope a while back.
It’s a minor thing, but it’d be nice to tidy this up as I think it’d be healthy for the PostCSS ecosystem.