TOP

JSDoc - "Emotion"

As mentioned, I don't use TypeScript, but write many of my apps in pure Javascript. As such, in order to describe types, I use JSDoc extensively in many of my codebases.
Hoping someone would find it interesting, I have a several pages presenting JSDoc examples as to how you could effectively utilize JSDoc's classical doctrine syntax.
Here is one of the modules that I currently use on my website, specifically, the Emotion context module.
/**
 * A module providing a context for sharing an Emotion instance
 * throughout components.<br />
 * The Emotion instance is created internally inside the Provider
 * and exposed to consumers through a `getter` function,<br />
 * allowing components to use utilities like `cx` and `css`
 * without importing Emotion directly.<br />
 * Note: Only the `getter` is exposed; the `setter` is kept internal.
 *
 * @module contexts/Emotion
 */

import {
  createContext,
  createSignal,
  createEffect,
} from 'solid-js';

import createEmotion from '@emotion/css/create-instance';

/**
 * This is the actual Emotion instance created internally
 * by `createInstance()` within Provider, and is exposed
 * only via `getter`.
 *
 * I could have done `import('@emotion/css/create-instance~Emotion`'),
 * but JSDoc does not generate for _external_ types.<br />
 * Thus, using `@typedef` so that it generates documents.
 *
 * I also wanted `Cx` and `Css` to be documented, but JSDoc does not do that<br />
 * because `@callback` means it has no actual functions corresponding to the type.<br />
 * Therefore, I have `Cx` and `Css` as properties here so that generates documents.
 *
 * @typedef EmotionEntity
 * @type {Object}
 * @property {Cx} cx - Combines class names.
 * @property {Css} css - Generates CSS class names.
 * @see {@link https://github.com/emotion-js/emotion/blob/85772c33ecb01c70bc8afafa627c9fb7140b593c/packages/css/src/create-instance.ts#L47|@emotion/css/create-instance.Emotion}
 */

/**
 * `getter` returning Emotion (or undefined).<br />
 * Backed by `Accessor<EmotionEntity>` (SolidJS).
 * @typedef {import("solid-js").Accessor<EmotionEntity>} EmotionGetter
 */

/**
 * `setter` updating the Emotion instance.<br />
 * Backed by `Setter<EmotionEntity>` (SolidJS).
 * @typedef {import("solid-js").Setter<EmotionEntity>} EmotionSetter
 */

// Notice that I use `@callback` for `cx` and `css` bellow
// because `@callback` is for functions do not actually exists.

/**
 * A method in Emotion for combining class names.
 * @callback
 * @name Cx
 * @param {...string} args - Class names to combine.
 * @returns {string} Combined class name.
 * @see {@link https://github.com/emotion-js/emotion/blob/85772c33ecb01c70bc8afafa627c9fb7140b593c/packages/css/src/create-instance.ts#L47|@emotion/css/create-instance.Emotion}
 */

/**
 * A method in Emotion for generating CSS.
 * @callback
 * @name Css
 * @param {...(string|Object)} args - CSS strings or style objects.
 * @returns {string} Generated CSS class name.
 * @see {@link https://github.com/emotion-js/emotion/blob/85772c33ecb01c70bc8afafa627c9fb7140b593c/packages/css/src/create-instance.ts#L47|@emotion/css/create-instance.Emotion}
 */

/**
 * A context having `getter`.<br />
 * Though `EmotionGetter[]` maybe interpreted as Array,
 * but I mean to express Tuple having only 1 element: a getter for Emotion.
 * @typedef {EmotionGetter[]} EmotionContextValue
 */

/**
 * This is the SolidJS' context provided by Provider,<br />
 * specifically, for this app, it is exposed as `value="{[getter]}"`.
 * @type {import("solid-js").Context<EmotionContextValue>}
 */
export const Context = createContext([]);

/**
 * This is the Provider. By wrapping compnents with this Provider,
 * it will provide `getter` which is the only way for users
 * to obtain the Emotion instance (`EmotionEntity`).
 *
 * IMPORTANT: Do NOT destructure `children` from `props`.<br />
 * If destructured, SolidJS treats them as if rendered *outside* the provider.
 *
 * @function
 * @param {Object} props - Props for the provider.
 * @param {string} [props.key] - Optional key for Emotion instance.
 * @param {HTMLElement|ShadowRoot} [props.element] - Container for injecting styles.
 * @param {JSX.Element|JSX.Element[]} props.children - Child components receiving context.
 * @returns {JSX.Element} Emotion context provider wrapping children.
 * @see {@link https://github.com/solidjs/solid/discussions/713} - Issue on Destructured Children
 */
export const Provider = props => {
  const { key, element } = props || {};

  /**
   * I'm not destructuring because JSDoc does not attatch
   * types to individual variables within destructures.
   * @type {{
   *   0: EmotionGetter,
   *   1: EmotionSetter
   * }}
   */
  const signal = createSignal();
  const [getter, setter] = signal;

  createEffect(() => {
    const { shadowRoot: container } = element || {};
    if (container) {
      /**
       * Exposed only via `getter`.
       * @type {EmotionEntity}
       */
      const emotion = createEmotion({
        key,
        container,
      });
      setter(emotion);
    }
  });

  return (
    <Context.Provider value={[getter]}>
      {props.children}
    </Context.Provider>
  );
};
TOP