stylex icon indicating copy to clipboard operation
stylex copied to clipboard

Optimize usage of styles exported from a file and used elsewhere

Open nmn opened this issue 1 year ago • 0 comments

It is a common pattern for developers to define styles using stylex.create in a file and export them and then import and use those styles elsewhere.

export const styles = stylex.create(...)

However we've always discouraged this pattern because it makes it almost impossible to detect unused styles at compile time. Could we fix this problem?

The Task

We should implement an optimisation to that detects and removes unused styles from both the Javascript and the CSS. Here's an approach that might work:

Enforce default exports from .stylex.js files only

We could enforce that stylex.create calls are only ever exported as default exports from files with a .stylex.js extension. (we would need to update our enforce-extension lint rule to allow this).

The compiler marks all CSS rules extracted from these files with a unique ID

When extracting styles from the default export of .stylex.js file, we could mark every CSS rule to indicate that we don't know whether it is used or not. The InjectableStyle type can be extended like so:

type InjectableStyle = {
  +priority: number,
  +ltr: string,
  +rtl: null | string,
  +usageUnkown?: string, // unique ID
};

Detect usage of styles imported from .stylex.js files

When the default export is imported from a .stylex.js we can statically check which keys have been used. We can then add metadata to the extracted list of styles from that file to mark the relevant styles as "used".

const marker: InjectableStyle = {
  usageMarker: // unique ID of style that was used.
  priority: -1,
  ltr: '',
  rtl: null,
  
};

Remove unused style objects before de-duplicating styles

Using the usageUnknown and usageMarker keys, we can remove any styles that are not used before we deduplicate the list of all styles collected after compilation. If the same style was used elsewhere, that would exist in the list without the usageUnkown key anyway and would be kept in the final CSS.

Leverage tree-shaking

The steps above will help remove unused styles from the generated CSS, but to remove the unused classNames from the Javascript is more challenging, specially for a per-file transformation like StyleX.

However, we can generate code that various bundlers should be able to tree-shake.

To enable tree-shaking, we can split the default export of stylex.create from a .stylex.js file to create a separate named export for each "rule" within the stylex.create. On the flip-side we will also have to split the default imports from .stylex.js files.

Just doing this should help bundlers that support tree-shaking to detect and remove the unused rules.

nmn avatar Oct 09 '24 04:10 nmn