CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-unplugin-vue-macros

Explore more macros and syntax sugar to Vue.

Pending
Overview
Eval results
Files

component-definition.mddocs/

Component Definition

Core macros for Vue component definition and configuration.

Capabilities

Define Options

Define component options within <script setup> blocks.

/**
 * Define component options (Vue 2.7+ / Vue 3.0-3.2)
 * @param options - Component options object
 */
declare function defineOptions<
  RawBindings = {},
  D = {},
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
>(
  options?: ComponentOptionsWithoutProps<
    {},
    RawBindings,
    D,
    C,
    M,
    Mixin,
    Extends
  > & { 
    emits?: undefined; 
    expose?: undefined; 
    slots?: undefined; 
  }
): void;

// Component options type (from Vue)
type ComponentOptionsWithoutProps<
  V = {},
  D = {},
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  Extends extends ComponentOptionsMixin = ComponentOptionsMixin
> = ComponentOptionsBase<V, D, C, M, Mixin, Extends> & {
  props?: undefined;
} & ThisType<CreateComponentPublicInstance<{}, RawBindings, D, C, M, Mixin, Extends>>;

Usage Examples:

<script setup>
import { someDirective } from './directives';
import { mixinA, mixinB } from './mixins';

// Define component options
defineOptions({
  name: "MyComponent",
  inheritAttrs: false,
  
  // Local registrations
  components: {
    CustomButton: () => import('./CustomButton.vue'),
  },
  
  directives: {
    focus: someDirective,
  },
  
  // Mixins and extension
  mixins: [mixinA, mixinB],
  extends: BaseComponent,
  
  // Lifecycle hooks
  beforeCreate() {
    console.log('Component being created');
  },
  
  created() {
    console.log('Component created');
  },
  
  // Additional options
  provide() {
    return {
      theme: 'dark',
    };
  },
});

// Your composition API code
const count = ref(0);
</script>

Define Slots

Define component slots with TypeScript support.

/**
 * Define component slots with types (Vue 2.7+ / Vue 3.0-3.2)
 * @returns Slots object with type definitions
 */
declare function defineSlots<
  S extends Record<string, any> = Record<string, any>
>(): UnwrapSlotsType<S>;

// Slots utility types
type UnwrapSlotsType<S> = {
  [K in keyof S]: S[K] extends (...args: any[]) => any ? S[K] : Slot<S[K]>;
};

type Slot<T = any> = (...args: T extends any ? [T] : []) => VNode[];

// Slots type inference
type SlotsType<T extends Record<string, any>> = {
  [K in keyof T]: T[K] extends (...args: infer P) => any
    ? (...args: P) => VNode[]
    : Slot<T[K]>;
};

Usage Examples:

<script setup>
// Define slots with types
const slots = defineSlots<{
  default(): any;
  header(props: { title: string; level: number }): any;
  footer(props: { year: number }): any;
  item(props: { item: any; index: number }): any;
}>();

// Check if slots exist
const hasHeader = !!slots.header;
const hasFooter = !!slots.footer;

// Use in render logic
function renderContent() {
  return [
    slots.header?.({ title: "My Title", level: 1 }),
    slots.default?.(),
    slots.footer?.({ year: new Date().getFullYear() }),
  ];
}
</script>

<template>
  <div class="component">
    <header v-if="hasHeader">
      <slot name="header" :title="title" :level="1" />
    </header>
    
    <main>
      <slot />
    </main>
    
    <footer v-if="hasFooter">
      <slot name="footer" :year="2024" />
    </footer>
  </div>
</template>

Define Emit

Define single emit function with enhanced typing.

/**
 * Define single emit function with enhanced typing
 * @param name - Event name
 * @returns Emit function for the specified event
 */
declare function defineEmit<T extends (...args: any[]) => any>(
  name: string
): T;

/**
 * Define emit with payload type
 * @param name - Event name
 * @returns Emit function with typed payload
 */
declare function defineEmit<T = any>(
  name: string
): (payload: T) => void;

// Emit function types
type EmitFunction<T> = T extends (...args: infer P) => any
  ? (...args: P) => void
  : (payload: T) => void;

Usage Examples:

<script setup>
// Define individual emit functions
const emitUpdate = defineEmit<(value: string) => void>("update");
const emitChange = defineEmit<(oldValue: string, newValue: string) => void>("change");
const emitDelete = defineEmit("delete"); // No payload
const emitSelect = defineEmit<{ id: number; name: string }>("select");

// Usage in component logic
function handleInput(event: Event) {
  const value = (event.target as HTMLInputElement).value;
  emitUpdate(value);
}

function handleChange(oldVal: string, newVal: string) {
  emitChange(oldVal, newVal);
}

function handleDelete() {
  emitDelete();
}

function handleSelect(item: { id: number; name: string }) {
  emitSelect(item);
}
</script>

<template>
  <div>
    <input @input="handleInput" @change="handleChange" />
    <button @click="handleDelete">Delete</button>
    <button @click="handleSelect({ id: 1, name: 'Item' })">Select</button>
  </div>
</template>

Define Render

Define render function for component.

/**
 * Define render function for component
 * @param render - Render function
 */
declare function defineRender(render: RenderFunction): void;

/**
 * Define render function with JSX
 * @param render - JSX render function
 */
declare function defineRender(render: () => JSX.Element): void;

// Render function types
type RenderFunction = (
  props: Record<string, any>,
  context: SetupContext
) => VNode | VNode[] | string | number | null | undefined;

interface SetupContext<E = EmitsOptions> {
  attrs: Data;
  slots: Slots;
  emit: EmitFunction<E>;
  expose: (exposed?: Record<string, any>) => void;
}

Usage Examples:

<script setup>
import { h } from 'vue';

// Props for render function
interface Props {
  title: string;
  items: Array<{ id: number; name: string }>;
}

const props = defineProps<Props>();

// Define render function with h()
defineRender(() => {
  return h('div', { class: 'container' }, [
    h('h1', props.title),
    h('ul', 
      props.items.map(item => 
        h('li', { key: item.id }, item.name)
      )
    ),
  ]);
});

// Alternative: JSX render function
// defineRender(() => (
//   <div class="container">
//     <h1>{props.title}</h1>
//     <ul>
//       {props.items.map(item => (
//         <li key={item.id}>{item.name}</li>
//       ))}
//     </ul>
//   </div>
// ));
</script>

Export Expose

Export values for parent component access.

/**
 * Export values for parent component access via template refs
 * @param exposed - Object with exposed values
 */
declare function exportExpose<T extends Record<string, any>>(
  exposed: T
): void;

// Alternative syntax using variable names
declare function exportExpose<T extends Record<string, any>>(): T;

Usage Examples:

<script setup>
import { ref, computed } from 'vue';

const count = ref(0);
const message = ref('Hello');

const doubleCount = computed(() => count.value * 2);

function increment() {
  count.value++;
}

function reset() {
  count.value = 0;
  message.value = 'Hello';
}

// Export specific methods and values
exportExpose({
  count: readonly(count),
  message: readonly(message),
  doubleCount,
  increment,
  reset,
  
  // Can also expose computed or reactive values
  state: computed(() => ({
    count: count.value,
    message: message.value,
  })),
});
</script>

Type Definitions

Supporting types for component definition macros.

// Component option types from Vue
interface ComputedOptions {
  [key: string]: ComputedGetter<any> | WritableComputedOptions<any>;
}

interface MethodOptions {
  [key: string]: Function;
}

interface ComponentOptionsMixin {
  [key: string]: any;
}

// Instance types
type CreateComponentPublicInstance<
  P = {},
  B = {},
  D = {},
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  PublicProps = P
> = ComponentPublicInstance<P, B, D, C, M, Mixin, Extends, PublicProps>;

// Emit types
type EmitsOptions = ObjectEmitsOptions | string[];

interface ObjectEmitsOptions {
  [key: string]: 
    | null 
    | ((...args: any[]) => any) 
    | { (...args: any[]): any; };
}

// VNode types  
type VNodeChild = 
  | VNode 
  | string 
  | number 
  | boolean 
  | null 
  | undefined 
  | VNodeArrayChildren;

type VNodeArrayChildren = Array<VNodeArrayChildren | VNodeChild>;

// Data object type
type Data = Record<string, unknown>;

Install with Tessl CLI

npx tessl i tessl/npm-unplugin-vue-macros

docs

advanced-features.md

component-definition.md

configuration.md

index.md

props-models.md

syntax-sugar.md

tile.json