@litsx/babel-preset-litsx
Source: test/babel-preset-litsx.test.js
Generated from transform tests.
Pipeline
@litsx/babel-preset-litsx
Covered Cases
Defaults to final html template lowering
Interpretation
This case records the authored input and the generated output as a living transform contract.
Authored Input
export const Greeting = ({ label }) => {
return <button>{label}</button>;
};Generated Output
import { LitElement, html } from "lit";
export class Greeting extends LitElement {
static properties = {
label: {
type: String
}
};
render() {
return html`<button>${this.label}</button>`;
}
}Matches the direct preset plugin factory
Interpretation
This case records the authored input and the generated output as a living transform contract.
Authored Input
import FancyButton from './FancyButton.js';
export const Greeting = ({ label = 'Save' }) => {
return <FancyButton .label={label} @click={save} />;
};Generated Output
import { ShadowDomElementsMixin } from "litsx/runtime-infrastructure";
import { LitElement, html } from "lit";
import FancyButton from './FancyButton.js';
export class Greeting extends ShadowDomElementsMixin(LitElement) {
static properties = {
label: {
type: String
}
};
static elements = {
"fancy-button": FancyButton
};
constructor() {
super();
this.label ??= 'Save';
}
render() {
return html`<fancy-button .label=${this.label} @click=${save}></fancy-button>`;
}
}Detects source features so the compiler can skip unnecessary native plugin passes
Interpretation
This case records the authored input and the generated output as a living transform contract.
Plain Source
export const Greeting = ({ label }) => {
return <button>{label}</button>;
};Generated Output
import { LitElement, html } from "lit";
export class Greeting extends LitElement {
static properties = {
label: {
type: String
}
};
render() {
return html`<button>${this.label}</button>`;
}
}Feature Source
import FancyButton from './FancyButton.js';
import { useRef, useState } from 'litsx';
export function Greeting({ label }) {
const ref = useRef(null);
const [count] = useState(0);
return <FancyButton ref={ref}>{label}{count}</FancyButton>;
}Generated Output
import { ShadowDomElementsMixin } from "litsx/runtime-infrastructure";
import { LitElement, html } from "lit";
import FancyButton from './FancyButton.js';
import { useRef, useState, prepareEffects } from 'litsx';
export class Greeting extends ShadowDomElementsMixin(LitElement) {
static properties = {
label: {
type: String
}
};
static elements = {
"fancy-button": FancyButton
};
render() {
prepareEffects(this);
const ref = useRef(this, null);
const [count] = useState(this, 0);
return html`<fancy-button .ref=${ref}>${this.label}${count}</fancy-button>`;
}
}Can disable final template lowering
Interpretation
This case records the authored input and the generated output as a living transform contract.
Authored Input
export const Greeting = ({ label }) => {
return <button @click={save}>{label}</button>;
};Generated Output
import { LitElement, html } from "lit";
export class Greeting extends LitElement {
static properties = {
label: {
type: String
}
};
render() {
return html`<button @click=${save}>${this.label}</button>`;
}
}Can be consumed through createLitsxPresetPlugins directly
Interpretation
This case records the authored input and the generated output as a living transform contract.
Authored Input
export const Greeting = ({ label }) => {
return <button @click={save}>{label}</button>;
};Generated Output
import { LitElement, html } from "lit";
export class Greeting extends LitElement {
static properties = {
label: {
type: String
}
};
render() {
return html`<button @click=${save}>${this.label}</button>`;
}
}Covers typed props, scoped elements, and final template lowering through the preset
Interpretation
This case records the authored input and the generated output as a living transform contract.
Authored Input
import FancyButton from './FancyButton.js';
type Props = { label: string; count: number };
export const TypedForm = ({ label, count }: Props) => {
return <FancyButton .label={label}>{count}</FancyButton>;
};Generated Output
import { ShadowDomElementsMixin } from "litsx/runtime-infrastructure";
import { LitElement, html } from "lit";
import FancyButton from './FancyButton.js';
type Props = {
label: string;
count: number;
};
export class TypedForm extends ShadowDomElementsMixin(LitElement) {
static properties = {
label: {
type: String
},
count: {
type: Number
}
};
static elements = {
"fancy-button": FancyButton
};
render() {
return html`<fancy-button .label=${this.label}>${this.count}</fancy-button>`;
}
}Does not lower React-only wrappers in the native preset
Interpretation
This case records the authored input and the generated output as a living transform contract.
Authored Input
import { forwardRef, memo } from 'react';
export const Card = memo(
forwardRef(function Card({ title }, ref) {
return <label ref={ref}>{title}</label>;
})
);Generated Output
import { html } from "lit";
import { forwardRef, memo } from 'react';
export const Card = memo(forwardRef(function Card({
title
}, ref) {
return html`<label ref="${ref}">${title}</label>`;
}));Does not lower React propTypes in the native preset anymore
Interpretation
This case records the authored input and the generated output as a living transform contract.
Authored Input
import PropTypes from 'prop-types';
export function Card(props) {
return <article>{props.title}</article>;
}
Card.propTypes = {
title: PropTypes.string,
};Generated Output
import { LitElement, html } from "lit";
import PropTypes from 'prop-types';
export class Card extends LitElement {
static properties = {
title: {
type: String
}
};
render() {
return html`<article>${this.title}</article>`;
}
}
Card.propTypes = {
title: PropTypes.string
};Covers a combined native preset path with static hoists, handlers, refs, and scoped elements
Interpretation
This case records the authored input and the generated output as a living transform contract.
Authored Input
import FancyButton from './FancyButton.js';
import { useRef, useState } from 'litsx';
type Props = { label: string; active: boolean };
export function ActionCard({ label, active }: Props) {
const buttonRef = useRef(null);
const [count, setCount] = useState(0);
^styles(`:host { display: block; }`);
^properties<Props>({ active: { reflect: true } });
return <FancyButton ref={buttonRef} .label={label} @click={() => setCount(count + 1)}>{active ? count : 0}</FancyButton>;
}Generated Output
import { LitsxStaticHoistsMixin, ShadowDomElementsMixin } from "litsx/runtime-infrastructure";
import { LitElement, css, html } from "lit";
const _litsx_static_styles = Symbol("litsx.static.styles");
const _litsx_static_properties = Symbol("litsx.static.properties");
import FancyButton from './FancyButton.js';
import { useRef, useState, prepareEffects } from 'litsx';
type Props = {
label: string;
active: boolean;
};
export class ActionCard extends ShadowDomElementsMixin(LitsxStaticHoistsMixin(LitElement)) {
static get styles() {
return this.__litsxStatic(_litsx_static_styles, () => this.__litsxResolveStaticValue(css`:host { display: block; }`));
}
static get properties() {
return this.__litsxStatic(_litsx_static_properties, () => this.__litsxMergeProperties({
label: {
type: String
},
active: {
type: Boolean
}
}, this.__litsxResolveStaticValue({
active: {
reflect: true
}
})));
}
render() {
prepareEffects(this);
const buttonRef = useRef(this, null);
const [count, setCount] = useState(this, 0);
return html`<fancy-button .ref=${buttonRef} .label=${this.label} @click=${() => setCount(count + 1)}>${this.active ? count : 0}</fancy-button>`;
}
static elements = {
"fancy-button": FancyButton
};
}Supports in-memory playground type resolution through the preset
Interpretation
This case captures supported authored syntax and the emitted code path used to preserve that behavior.
Authored Input
type BaseProps = {
title: string;
active: boolean;
payload: Record<string, unknown>;
};
type CardProps = Pick<BaseProps, "title" | "active"> & {
payload: BaseProps["payload"];
};
function Card(props: CardProps) {
return <article>{props.title}</article>;
}Generated Output
import { LitElement, html } from "lit";
type BaseProps = {
title: string;
active: boolean;
payload: Record<string, unknown>;
};
type CardProps = Pick<BaseProps, "title" | "active"> & {
payload: BaseProps["payload"];
};
class Card extends LitElement {
static properties = {
title: {
type: String
},
active: {
type: Boolean
},
payload: {
type: Object
}
};
render() {
return html`<article>${this.title}</article>`;
}
}Lowers native useState through the canonical preset
Interpretation
This case records the authored input and the generated output as a living transform contract.
Authored Input
import { useState } from 'litsx';
export function Counter() {
const [count, setCount] = useState(1);
return <button @click={() => setCount(count + 1)}>{count}</button>;
}Generated Output
import { LitElement, html } from "lit";
import { useState, prepareEffects } from 'litsx';
export class Counter extends LitElement {
render() {
prepareEffects(this);
const [count, setCount] = useState(this, 1);
return html`<button @click=${() => setCount(count + 1)}>${count}</button>`;
}
}Preserves sibling declarators around native useState through the preset
Interpretation
This case records the authored input and the generated output as a living transform contract.
Authored Input
import { useState } from 'litsx';
export function Counter() {
const label = 'ok', [count, setCount] = useState(0);
setCount(count + 1);
return <div>{label}: {count}</div>;
}Generated Output
import { LitElement, html } from "lit";
import { useState, prepareEffects } from 'litsx';
export class Counter extends LitElement {
render() {
prepareEffects(this);
const label = 'ok',
[count, setCount] = useState(this, 0);
setCount(count + 1);
return html`<div>${label}: ${count}</div>`;
}
}Threads host through local custom hooks that call native useState
Interpretation
This case records the authored input and the generated output as a living transform contract.
Authored Input
import { useState } from 'litsx';
function useCounter(initial) {
const [value, setValue] = useState(initial);
return [value, setValue];
}
export function Counter() {
const [value, setValue] = useCounter(0);
return <button @click={() => setValue(value + 1)}>{value}</button>;
}Generated Output
import { LitElement, html } from "lit";
import { useState, prepareEffects } from 'litsx';
function useCounter(_host, initial) {
const [value, setValue] = useState(_host, initial);
return [value, setValue];
}
export class Counter extends LitElement {
render() {
prepareEffects(this);
const [value, setValue] = useCounter(this, 0);
return html`<button @click=${() => setValue(value + 1)}>${value}</button>`;
}
}Injects prepareEffects and host args for native effect hooks through the preset
Interpretation
This case documents code that is synthesized by the transform, not written directly by the user.
Authored Input
import { useAfterUpdate } from 'litsx';
export function Counter() {
useAfterUpdate(() => {
this.flag = true;
}, []);
return <p>{this.flag}</p>;
}Generated Output
import { LitElement, html } from "lit";
import { useAfterUpdate, prepareEffects } from 'litsx';
export class Counter extends LitElement {
render() {
prepareEffects(this);
useAfterUpdate(this, () => {
this.flag = true;
}, []);
return html`<p>${this.flag}</p>`;
}
}Threads host through native custom hooks in the preset
Interpretation
This case records the authored input and the generated output as a living transform contract.
Authored Input
import { useStableCallback, useAfterUpdate } from 'litsx';
function useCustom(flag) {
const callback = useStableCallback(() => flag, [flag]);
useAfterUpdate(() => flag && callback(), [flag, callback]);
return callback;
}
export function Counter() {
const value = useCustom(this.flag);
return <button>{String(value && value())}</button>;
}Generated Output
import { LitElement, html } from "lit";
import { useStableCallback, useAfterUpdate, prepareEffects } from 'litsx';
function useCustom(_host, flag) {
const callback = useStableCallback(_host, () => flag, [flag]);
useAfterUpdate(_host, () => flag && callback(), [flag, callback]);
return callback;
}
export class Counter extends LitElement {
render() {
prepareEffects(this);
const value = useCustom(this, this.flag);
return html`<button>${String(value && value())}</button>`;
}
}Injects host for native useEmit through the preset
Interpretation
This case documents code that is synthesized by the transform, not written directly by the user.
Authored Input
import { useEmit } from 'litsx';
export function Counter() {
const emit = useEmit();
emit('change', this.value, { cancelable: true });
return <div>{this.value}</div>;
}Generated Output
import { LitElement, html } from "lit";
import { useEmit, prepareEffects } from 'litsx';
export class Counter extends LitElement {
render() {
prepareEffects(this);
const emit = useEmit(this);
emit('change', this.value, {
cancelable: true
});
return html`<div>${this.value}</div>`;
}
}Lowers native useRef DOM bindings through the canonical preset
Interpretation
This case records the authored input and the generated output as a living transform contract.
Authored Input
import { useRef } from 'litsx';
export function Counter() {
const buttonRef = useRef(null);
return <button ref={buttonRef}>Click</button>;
}Generated Output
import { LitElement, html } from "lit";
import { prepareEffects, useRef, useCallbackRef } from 'litsx';
export class Counter extends LitElement {
get _buttonRefElement() {
return this.renderRoot?.querySelector("[data-ref=\"_buttonRefElement\"]") ?? this.querySelector("[data-ref=\"_buttonRefElement\"]");
}
render() {
prepareEffects(this);
const buttonRef = useRef(this, null);
useCallbackRef(this, () => this._buttonRefElement, node => buttonRef.current = node);
return html`<button data-ref="_buttonRefElement">Click</button>`;
}
}Keeps non-DOM native useRef bindings as mutable refs through the preset
Interpretation
This case highlights syntax that should survive the transform unchanged or be preserved semantically.
Authored Input
import { useRef } from 'litsx';
export function Counter() {
const workerRef = useRef(null);
workerRef.current = 'ok';
return <div>{workerRef.current}</div>;
}Generated Output
import { LitElement, html } from "lit";
import { useRef, prepareEffects } from 'litsx';
export class Counter extends LitElement {
render() {
prepareEffects(this);
const workerRef = useRef(this, null);
workerRef.current = 'ok';
return html`<div>${workerRef.current}</div>`;
}
}Does not follow external playground imports when using in-memory mode
Interpretation
This case records the authored input and the generated output as a living transform contract.
Authored Input
import type { CardProps } from './types';
function Card({ title, active }: CardProps) {
return <article>{title} {active ? 'on' : 'off'}</article>;
}Generated Output
import { LitElement, html } from "lit";
import type { CardProps } from './types';
class Card extends LitElement {
static properties = {
title: {
type: String
},
active: {
type: String
}
};
render() {
return html`<article>${this.title} ${this.active ? 'on' : 'off'}</article>`;
}
}