CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vue--runtime-core

Vue.js 3 runtime core library providing foundational APIs for building custom renderers and managing reactive component systems

Pending
Overview
Eval results
Files

asset-resolution.mddocs/

Asset Resolution

Vue's asset resolution system enables dynamic resolution of components and directives at runtime, supporting flexible component registration and usage patterns.

Capabilities

Component Resolution

Resolve registered components by name at runtime.

/**
 * Resolves a globally registered component by name
 * @param name - Component name to resolve
 * @returns Resolved component or component name string if not found
 */
function resolveComponent(name: string): ConcreteComponent | string;

Usage Examples:

import { defineComponent, resolveComponent, h } from "@vue/runtime-core";

// Basic component resolution
const DynamicComponentExample = defineComponent({
  props: {
    componentName: { type: String, required: true }
  },
  
  setup(props) {
    return () => {
      // Resolve component by name
      const Component = resolveComponent(props.componentName);
      
      // Check if component was found
      if (typeof Component === 'string') {
        console.warn(`Component "${props.componentName}" not found`);
        return h('div', `Component "${props.componentName}" not registered`);
      }
      
      return h(Component, { message: 'Hello from dynamic component' });
    };
  }
});

// Usage with component registry
const app = createApp({
  components: {
    'my-button': MyButtonComponent,
    'my-input': MyInputComponent,
    'my-modal': MyModalComponent
  },
  
  setup() {
    const currentComponent = ref('my-button');
    
    return () => {
      const Component = resolveComponent(currentComponent.value);
      return h(Component);
    };
  }
});

// Conditional component rendering
const ConditionalRenderer = defineComponent({
  props: {
    showAdvanced: Boolean
  },
  
  setup(props) {
    return () => {
      const componentName = props.showAdvanced ? 'advanced-form' : 'simple-form';
      const Component = resolveComponent(componentName);
      
      return typeof Component !== 'string' 
        ? h(Component)
        : h('div', 'Component not available');
    };
  }
});

Directive Resolution

Resolve registered directives by name at runtime.

/**
 * Resolves a globally registered directive by name
 * @param name - Directive name to resolve (without v- prefix)
 * @returns Resolved directive or undefined if not found
 */
function resolveDirective(name: string): Directive | undefined;

Usage Examples:

import { defineComponent, resolveDirective, withDirectives, h } from "@vue/runtime-core";

// Dynamic directive application
const DynamicDirectiveExample = defineComponent({
  props: {
    directiveName: String,
    directiveValue: String
  },
  
  setup(props) {
    return () => {
      const element = h('input', { type: 'text' });
      
      if (props.directiveName) {
        const directive = resolveDirective(props.directiveName);
        
        if (directive) {
          return withDirectives(element, [
            [directive, props.directiveValue]
          ]);
        }
      }
      
      return element;
    };
  }
});

// Conditional directive usage
const ConditionalDirective = defineComponent({
  props: {
    enableTooltip: Boolean,
    tooltipText: String
  },
  
  setup(props) {
    return () => {
      let element = h('button', 'Hover me');
      
      if (props.enableTooltip) {
        const tooltipDirective = resolveDirective('tooltip');
        
        if (tooltipDirective) {
          element = withDirectives(element, [
            [tooltipDirective, props.tooltipText]
          ]);
        }
      }
      
      return element;
    };
  }
});

// Multiple directive resolution
const MultiDirectiveComponent = defineComponent({
  setup() {
    const directives = ['focus', 'tooltip', 'ripple'];
    
    return () => {
      const resolvedDirectives = directives
        .map(name => ({ name, directive: resolveDirective(name) }))
        .filter(item => item.directive);
      
      let element = h('button', 'Multi-directive button');
      
      if (resolvedDirectives.length > 0) {
        const directiveBindings = resolvedDirectives.map(({ directive }) => [
          directive,
          true // Simple binding value
        ]);
        
        element = withDirectives(element, directiveBindings);
      }
      
      return element;
    };
  }
});

Dynamic Component Resolution

Resolve components dynamically with support for various input types.

/**
 * Resolves dynamic component reference to actual component
 * @param component - Component reference (string, component object, or function)
 * @returns Resolved component type
 */
function resolveDynamicComponent(component: unknown): VNodeTypes;

Usage Examples:

import { defineComponent, resolveDynamicComponent, h } from "@vue/runtime-core";

// Flexible component resolution
const FlexibleRenderer = defineComponent({
  props: {
    component: [String, Object, Function]
  },
  
  setup(props) {
    return () => {
      const resolvedComponent = resolveDynamicComponent(props.component);
      return h(resolvedComponent);
    };
  }
});

// Component factory pattern
const ComponentFactory = defineComponent({
  props: {
    type: { type: String, required: true },
    config: Object
  },
  
  setup(props) {
    const componentMap: Record<string, any> = {
      'button': () => import('./ButtonComponent.vue'),
      'input': () => import('./InputComponent.vue'),
      'select': () => import('./SelectComponent.vue')
    };
    
    return () => {
      const componentSpec = componentMap[props.type];
      const resolvedComponent = resolveDynamicComponent(componentSpec);
      
      return h(resolvedComponent, props.config);
    };
  }
});

// Plugin-based component resolution
const PluginRenderer = defineComponent({
  props: {
    pluginName: String,
    componentName: String
  },
  
  setup(props) {
    return () => {
      // Resolve from plugin registry
      const pluginComponent = window.plugins?.[props.pluginName]?.[props.componentName];
      const resolvedComponent = resolveDynamicComponent(pluginComponent || 'div');
      
      return h(resolvedComponent);
    };
  }
});

Advanced Resolution Patterns

// Asset resolution with fallbacks
function useComponentWithFallback(primaryName: string, fallbackName: string) {
  return computed(() => {
    const primary = resolveComponent(primaryName);
    if (typeof primary !== 'string') {
      return primary;
    }
    
    const fallback = resolveComponent(fallbackName);
    return typeof fallback !== 'string' ? fallback : 'div';
  });
}

// Lazy directive resolution
function useLazyDirective(name: string) {
  const directive = ref<Directive | null>(null);
  
  onMounted(() => {
    directive.value = resolveDirective(name) || null;
  });
  
  return directive;
}

// Dynamic asset registry
class DynamicAssetRegistry {
  private components = new Map<string, Component>();
  private directives = new Map<string, Directive>();
  
  registerComponent(name: string, component: Component) {
    this.components.set(name, component);
  }
  
  registerDirective(name: string, directive: Directive) {
    this.directives.set(name, directive);
  }
  
  resolveComponent(name: string) {
    return this.components.get(name) || resolveComponent(name);
  }
  
  resolveDirective(name: string) {
    return this.directives.get(name) || resolveDirective(name);
  }
}

const registry = new DynamicAssetRegistry();

// Theme-based component resolution
function useThemeComponent(baseName: string) {
  const theme = inject('theme', 'default');
  
  return computed(() => {
    const themedName = `${baseName}-${theme}`;
    const themedComponent = resolveComponent(themedName);
    
    // Fallback to base component if themed version not found
    if (typeof themedComponent === 'string') {
      return resolveComponent(baseName);
    }
    
    return themedComponent;
  });
}

// Async component resolution
async function resolveAsyncComponent(name: string): Promise<Component | null> {
  const syncComponent = resolveComponent(name);
  
  if (typeof syncComponent !== 'string') {
    return syncComponent;
  }
  
  // Try to load async
  try {
    const module = await import(`./components/${name}.vue`);
    return module.default;
  } catch {
    return null;
  }
}

Error Handling and Validation

// Safe component resolution with validation
function useSafeComponent(name: string, validator?: (component: any) => boolean) {
  return computed(() => {
    const component = resolveComponent(name);
    
    if (typeof component === 'string') {
      console.warn(`Component "${name}" not found`);
      return null;
    }
    
    if (validator && !validator(component)) {
      console.warn(`Component "${name}" failed validation`);
      return null;
    }
    
    return component;
  });
}

// Directive resolution with type checking
function useSafeDirective(name: string): Ref<Directive | null> {
  const directive = ref<Directive | null>(null);
  
  watchEffect(() => {
    const resolved = resolveDirective(name);
    
    if (resolved && typeof resolved === 'object') {
      directive.value = resolved;
    } else {
      console.warn(`Directive "v-${name}" not found`);
      directive.value = null;
    }
  });
  
  return directive;
}

// Resolution with dependency tracking
function useTrackedResolution() {
  const resolvedComponents = reactive(new Map<string, Component>());
  const resolvedDirectives = reactive(new Map<string, Directive>());
  
  const getComponent = (name: string) => {
    if (!resolvedComponents.has(name)) {
      const component = resolveComponent(name);
      if (typeof component !== 'string') {
        resolvedComponents.set(name, component);
      }
    }
    return resolvedComponents.get(name);
  };
  
  const getDirective = (name: string) => {
    if (!resolvedDirectives.has(name)) {
      const directive = resolveDirective(name);
      if (directive) {
        resolvedDirectives.set(name, directive);
      }
    }
    return resolvedDirectives.get(name);
  };
  
  return {
    getComponent,
    getDirective,
    resolvedComponents: readonly(resolvedComponents),
    resolvedDirectives: readonly(resolvedDirectives)
  };
}

Types

type ConcreteComponent = ComponentOptions | FunctionalComponent;

type VNodeTypes = 
  | string 
  | VNode 
  | Component 
  | typeof Text 
  | typeof Static 
  | typeof Comment 
  | typeof Fragment;

interface Directive<T = any, V = any> {
  created?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
  beforeMount?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
  mounted?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
  beforeUpdate?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode): void;
  updated?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode): void;
  beforeUnmount?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
  unmounted?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
}

interface DirectiveBinding<V = any> {
  instance: ComponentPublicInstance | null;
  value: V;
  oldValue: V | null;
  arg?: string;
  modifiers: Record<string, boolean>;
  dir: ObjectDirective<any, V>;
}

type FunctionalComponent<P = {}, E extends EmitsOptions = {}> = {
  (props: P, ctx: Omit<SetupContext<E>, 'expose'>): any;
  props?: ComponentPropsOptions<P>;
  emits?: E | (keyof E)[];
  inheritAttrs?: boolean;
  displayName?: string;
};

Usage Guidelines

Best Practices

  1. Cache resolved assets for performance in frequently called render functions
  2. Validate resolved components before using them in render functions
  3. Provide fallbacks for missing components to avoid runtime errors
  4. Use type guards when working with dynamically resolved assets

Common Patterns

  1. Dynamic form builders using component resolution for field types
  2. Plugin systems with runtime component registration
  3. Theme systems with themed component variants
  4. Micro-frontends with dynamic asset loading

Performance Considerations

  • Asset resolution happens during render, so cache results when possible
  • Use computed to memoize resolution results
  • Consider lazy loading for components that may not be used

Install with Tessl CLI

npx tessl i tessl/npm-vue--runtime-core

docs

asset-resolution.md

builtin-components.md

components.md

composition-helpers.md

dependency-injection.md

error-handling.md

hydration.md

index.md

internal-render-helpers.md

lifecycle.md

reactivity.md

scheduler-timing.md

ssr-context.md

vdom-rendering.md

watch-effects.md

tile.json