Upgrade JS decorator to the 2023-05 proposal?

145 views Asked by At

I have a decorator to inject my common stylesheets into lit components written with the legacy 2018 proposal (as what was previously supported by lit). Upgrading to lit 3, they changed to the 2023-05 proposal and my decorator no longer works. How do I update my decorator to the 2023-05 proposal?

My understanding is fields no longer have initializers, so I'm not sure how to do this now:

Decorator

const injectTheme = (...styles) => (target) => {
  const { initializer } = target;

  // Inject tailwind
  let newStyles = [tailwind];

  // Add any initial styles into the new style array
  if (initializer) {
    const initStyles = initializer.call(this);
    if (Array.isArray(initStyles)) {
      newStyles.push(...initStyles);
    } else {
      newStyles.push(initStyles);
    }
  }

  // Add passed in styles last
  newStyles.push(...styles);

  // Remove any invalid styles
  newStyles = newStyles.filter((style) => !!style);

  // Ensure that they are valid style sheets
  newStyles = newStyles.map(assertCSS);

  // Override initializer to return new styles array
  target.initializer = function () {
    return newStyles;
  };
};

Example Usage

import styles from './styles.css';

@customElement('my-element')
class MyElement extends LitElement {
  @injectTheme(styles) static styles;
}

Preferred Syntax

Is it possible to make this a class decorator as I think I'd prefer that. Only issue I see is that class decorators are called after the static elements are assigned:

Class decorator initializers are run after the class has been fully defined, and after class static fields have been assigned.

@injectTheme(style)
@customElement('my-element')
class MyElement extends LitElement {

}

https://github.com/tc39/proposal-decorators

1

There are 1 answers

0
Joe Jankowiak On

Figured out how to convert the functionality to the new proposal. I`m still curious if its possible to do this as a class decorator.

The decorator can return a function, which takes in the initial value (initalStyles) and can return a modified value.

export const injectTheme =
  (...styles) =>
  () =>
  (initialStyles) => {
    let newStyles = [tailwind];

    if (Array.isArray(initialStyles)) {
      newStyles.push(...initialStyles);
    }

    newStyles.push(...styles);

    // Remove any invalid styles
    newStyles = newStyles.filter((style) => !!style);

    // Ensure that they are valid style sheets
    newStyles = newStyles.map(assertCSS);

    return newStyles;
  };