theKashey / used-styles

📝All the critical styles you've used to render a page.
MIT License
137 stars 9 forks source link

Implement CSS Cascade Layers support #51

Closed AlexandrHoroshih closed 11 months ago

AlexandrHoroshih commented 11 months ago

Closes https://github.com/theKashey/used-styles/issues/50

What is implemented

@layer rules got special processing, similiar to @media rules

Layer order definitions (@layer a, b, c;) are treated similiar to other "unprocessed" at-rules and are included at the top and as-is.

Actual layered styles (@layer a { ... }) are treated the same way @media is handled now - only actually used styles are included into critical css.

AST types are refactored to make a clear distinction between processed at-rules and unprocessed at-rules

During implementation it became obvious, that at-rules are splitted into two groups:

AST types and related code was updated to reflect that:

What is not implemented

There is a possible edge-case: It is possible to have the same layers order definition at multiple files, something like this

/* index.css */
@layer a, b, c;

// ...styles

/* chunk.css */
@layer a, b, c;

// ...styles

I think, it is possible in the situation, where layers are added automatically by bundlers, based on some heuristics or rules, since this way layers order is guaranteed and is not dependent on loading order 🤔

In that case used-styles probably should deduplicate these definitions.

But since i do not have any actual real-life use-cases, which would require something like this, it is not added in this PR

If this problem will actually happend one day - then cross-file deduplication of layer definitions can be solved by introducing something like "shared context" for the AST parser:

const astFromFiles = (fileDate: StyleFiles): StyleAst => {
  const sharedContext = {
   layerDefinitions: new Set()
  }

  return Object.keys(fileDate).reduce((acc, file) => {
    acc[file] = buildAst(fileDate[file], file, sharedContext);

    return acc;
  }, {} as StyleAst);
};

const buildAst = (cssString, file, context) => {
  // ...
 if (context.layerDefinitions.has(`@layer ${rule.params}`) {
  return;
 }
}
AlexandrHoroshih commented 11 months ago

Hello @theKashey !

Could you take a look? 🙏

theKashey commented 11 months ago

What I could say @AlexandrHoroshih - it's AMAZING! The changes in the code are minimal, no tests bothered for no reason and the one created are focused on what they should test and according to the results.... it's just working as it should!

theKashey commented 11 months ago

Released as used-styles@2.6.0