npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vuer-ai/dial-cli

v0.0.25

Published

CLI tool for generating dial metadata from TypeScript/React components

Readme

@vuer-ai/dial-cli

A powerful CLI tool for generating dial metadata from TypeScript/React components. This tool analyzes JSDoc @dial annotations and TypeScript type definitions to automatically create UI control schemas.

Features

  • Automatic Schema Generation - Parse TypeScript/React components and generate dial schemas
  • Full Type System Support - Handles inheritance, intersections, utility types (Pick, Omit, etc.)
  • Cross-File Type Resolution - Resolves types across multiple files
  • Extensible Annotation System - Custom @dial-* properties are automatically preserved
  • Performance Optimized - Two-phase parsing strategy with caching
  • Comprehensive Testing - 42+ test cases covering all features

Installation

npm install -g @vuer-ai/dial-cli
# or
pnpm install -g @vuer-ai/dial-cli
# or
yarn global add @vuer-ai/dial-cli

Quick Start

1. Add @dial Annotations to Your Components

interface BoxProps {
  /**
   * Position in 3D space
   * @dial transform
   * @dial-dtype vector3
   * @dial-min -10
   * @dial-max 10
   * @dial-step 0.1
   */
  position: [number, number, number];

  /**
   * Box color
   * @dial appearance
   * @dial-dtype color
   */
  color: string;

  /**
   * Visibility toggle
   * @dial visibility
   * @dial-dtype boolean
   */
  visible: boolean;
}

export const Box: React.FC<BoxProps> = (props) => <mesh />;

2. Generate Dial Schema

dial-cli Box.tsx -o ./metadata

3. Use Generated Schema in Your App

import dialSchemas from './metadata/schema.dial';
import { DialProvider } from '@vuer-ai/vuer-uikit';

function App() {
  return (
    <DialProvider schemas={dialSchemas[0].schemas}>
      <Box position={[0, 0, 0]} color="#ff0000" visible={true} />
    </DialProvider>
  );
}

The DialProvider will automatically generate UI controls for all properties with @dial annotations.

Usage

Basic Commands

# Process single file
dial-cli MyComponent.tsx

# Specify output directory
dial-cli MyComponent.tsx -o ./docs/metadata

# Process multiple files
dial-cli Component1.tsx Component2.tsx Component3.tsx

# Process all components in a directory
dial-cli src/components/**/*.tsx

# Enable verbose output
dial-cli MyComponent.tsx --verbose

# Exclude specific properties
dial-cli MyComponent.tsx --ignore "className,style,children"

CLI Options

| Option | Description | Default | |--------|-------------|---------| | -o, --output <dir> | Output directory for metadata files | ./metadata | | -i, --ignore <props> | Property names or patterns to exclude (comma-separated, supports glob) | - | | --verbose | Enable verbose output with detailed processing information | false | | --quiet | Suppress all output except errors | false | | --debug | Alias for --verbose | false | | -r, --remove | Remove generated metadata files | false | | -v, --version | Display version information | - | | -h, --help | Display help message | - |

Ignore Patterns

The --ignore flag supports glob patterns:

# Exact match
dial-cli Box.tsx --ignore className,style

# Glob patterns
dial-cli Box.tsx --ignore "on*"        # Exclude all props starting with "on"
dial-cli Box.tsx --ignore "_*"         # Exclude all private props
dial-cli Box.tsx --ignore "*Event*"    # Exclude props containing "Event"

# Multiple patterns
dial-cli Box.tsx --ignore "className,on*,_*"

Dial Annotations

Dial annotations are JSDoc comments that configure how properties are displayed in the UI. They are divided into two categories: Property Tags (control individual properties) and Group Tags (configure entire groups).

Note on @dial requirement: Properties without a @dial <group> annotation will still be included in the schema, but will be displayed in a default layout. It's recommended to assign properties to groups for better organization.

Property Tags

Property tags control individual property behavior and appearance. They are added to property JSDoc comments.

Core Property Tags

| Annotation | Description | Values | Example | |------------|-------------|--------|---------| | @dial <group> | Optional: Assign property to a named group. Properties without this tag will be displayed in default layout. | Any string (group name) | @dial transform | | @dial-dtype <type> | Important: Explicit data type for UI control. If not set, the CLI will infer the type from TypeScript type definitions (e.g., boolean"boolean", [number, number, number]"vector3"). Use this to override automatic inference. | See Supported Data Types | @dial-dtype vector3 | | @dial-ignore | Exclude property from schema generation | No value needed | @dial-ignore | | @dial-options <json> | Options for select/dropdown controls | JSON array or object | @dial-options ["a", "b", "c"] |

UI Display Tags

Control how properties are labeled and displayed in the UI.

| Annotation | Description | Values | Example | |------------|-------------|--------|---------| | @dial-label <text> | Custom display label for the property. If not set, defaults to the property name. | Any string | @dial-label 3D Position | | @dial-icon <name> | Icon name from Lucide icon library to display with the property | Lucide icon name | @dial-icon Move3d | | @dial-label-position <pos> | Position of the label relative to the input control. Shortcuts: @dial-label-left, @dial-label-right, @dial-label-top, @dial-label-bottom, @dial-label-center, @dial-label-inline | left, right, top, bottom, center, inline | @dial-label-position top or @dial-label-top |

Numeric Constraint Tags

| Annotation | Description | Values | Example | |------------|-------------|--------|---------| | @dial-min <number> | Minimum value constraint | Any number | @dial-min 0 | | @dial-max <number> | Maximum value constraint | Any number | @dial-max 100 | | @dial-step <number> | Step increment for sliders | Any number | @dial-step 0.1 | | @dial-value <value> | Default value | Any valid value | @dial-value 1.0 | | @dial-default <value> | Alias for @dial-value | Any valid value | @dial-default 0 |

Vector-Specific Property Tags

For vectors (tuples), you can specify per-element constraints. If not provided, these values are automatically merged from individual element annotations.

Note: Values must be provided as JSON arrays for optimal performance.

| Annotation | Description | Values | Example | |------------|-------------|--------|---------| | @dial-mins <values> | Min values per element | JSON array of numbers | @dial-mins [0,0,0] | | @dial-maxs <values> | Max values per element | JSON array of numbers | @dial-maxs [255,255,255] | | @dial-steps <values> | Step values per element | JSON array of numbers | @dial-steps [1,1,1] | | @dial-dtypes <values> | Data types per element | JSON array of strings | @dial-dtypes ["int","int","int"] | | @dial-placeholders <values> | Placeholder text per element | JSON array of strings | @dial-placeholders ["R","G","B"] | | @dial-tooltips <values> | Tooltip text per element | JSON array of strings | @dial-tooltips ["Red","Green","Blue"] |

Auto-merging behavior: If vector-specific tags are not provided, the CLI automatically merges annotations from each tuple element:

type RGB = [
  // @dial-min 0 @dial-max 255 @dial-step 1 @dial-dtype int
  r: number,
  // @dial-min 0 @dial-max 255 @dial-step 1 @dial-dtype int
  g: number,
  // @dial-min 0 @dial-max 255 @dial-step 1 @dial-dtype int
  b: number,
];

interface Props {
  /**
   * RGB color
   * @dial appearance
   * @dial-placeholders R,G,B  // Optional: override element names
   */
  color: RGB;
}

// Output schema will automatically include:
// mins: [0, 0, 0], maxs: [255, 255, 255], steps: [1, 1, 1], dtypes: ["int", "int", "int"]

Property Layout Tags

Control how individual properties are displayed within the grouped UI layout.

| Annotation | Description | Values | Example | |------------|-------------|--------|---------| | @dial-col-span <num> | How many grid columns this property spans | Number | @dial-col-span 2 | | @dial-row-span <num> | How many grid rows this property spans | Number | @dial-row-span 2 |

Group Tags

Group tags configure the layout and behavior of entire property groups. These tags control group-level layout (how the group container is structured), not individual property behavior.

Important: Group tags only apply when a @dial <groupname> annotation is present. Group tags are ignored if no @dial <groupname> is specified. The CLI will warn you if group tags are used without a group name.

Group Layout Configuration

| Annotation | Description | Values | Example | |------------|-------------|--------|---------| | @dial-group-layout <type> | Layout system for the group container. Shorthand: @dial-layout | grid, flex | @dial-group-layout grid or @dial-layout grid | | @dial-group-grid-cols <num> | Number of columns in group grid layout. Shorthand: @dial-cols or @dial-grid-cols | Number | @dial-group-grid-cols 2 or @dial-cols 2 or @dial-grid-cols 2 | | @dial-group-grid-rows <num> | Number of rows in group grid layout. Shorthand: @dial-rows or @dial-grid-rows | Number | @dial-group-grid-rows 3 or @dial-rows 3 or @dial-grid-rows 3 | | @dial-group-grid-flow <dir> | Grid auto-flow direction for the group. Shorthand: @dial-flow or @dial-grid-flow | row, column | @dial-group-grid-flow row or @dial-flow row or @dial-grid-flow row | | @dial-group-grid-col-template <val> | CSS grid column template for the group. Shorthand: @dial-col-template or @dial-grid-col-template | CSS grid template value | @dial-group-grid-col-template 1fr 2fr or @dial-col-template 1fr 2fr or @dial-grid-col-template 1fr 2fr | | @dial-group-grid-row-template <val> | CSS grid row template for the group. Shorthand: @dial-row-template or @dial-grid-row-template | CSS grid template value | @dial-group-grid-row-template auto 1fr or @dial-row-template auto 1fr or @dial-grid-row-template auto 1fr | | @dial-group-flex-wrap <val> | Flex wrap behavior for the group. Shorthand: @dial-wrap or @dial-flex-wrap | nowrap, wrap, wrap-reverse | @dial-group-flex-wrap wrap or @dial-wrap wrap or @dial-flex-wrap wrap | | @dial-group-flex-justify-content <val> | Flex justify-content for the group. Shorthand: @dial-justify or @dial-flex-justify-content | flex-start, flex-end, center, space-between, space-around, space-evenly | @dial-group-flex-justify-content center or @dial-justify center or @dial-flex-justify-content center |

Example:

interface Props {
  /**
   * X position
   * @dial transform
   * @dial-layout grid
   * @dial-cols 3
   * @dial-flow row
   */
  x: number;

  /**
   * Y position
   * @dial transform
   */
  y: number;

  /**
   * Z position
   * @dial transform
   */
  z: number;
}

// Result: All three properties are displayed in a 3-column grid

Supported Data Types

The CLI infers data types from TypeScript types, but you can override with @dial-dtype.

| dtype | TypeScript Type | UI Control | Description | |-------|----------------|------------|-------------| | boolean | boolean | Toggle switch | On/off control | | number | number | Slider/input | Floating-point number | | int or number-int | number with explicit dtype | Integer slider | Whole numbers only | | string | string | Text input | Text value | | color | string | Color picker | Hex color (#RRGGBB) | | select | Union type with @dial-options | Dropdown | Select from options | | array | string[], number[], boolean[] | Dynamic list | Array of primitive types with add/remove | | vector | Tuple with number/boolean elements | Vector input | Multi-dimensional numeric/boolean input | | tuple | Tuple with mixed types | Nested panel | General tuple with any element types | | vector3 | [number, number, number] | 3D vector input | X, Y, Z components (simplified) | | euler | [number, number, number] | Euler angles | Rotation in degrees/radians | | interface | Interface type | Nested panel | Nested control panel | | object | Object type | Nested panel | Nested control panel |

Type Inference Examples:

interface Props {
  // Inferred as "boolean"
  visible: boolean;

  // Inferred as "number"
  opacity: number;

  // Inferred as "string"
  name: string;

  // Inferred as "vector3" (from type name or tuple length)
  /** @dial transform */
  position: [number, number, number];

  // Explicit dtype override
  /** @dial geometry @dial-dtype int */
  segments: number;  // Will be treated as integer

  // Named tuple with numbers/booleans inferred as "vector"
  /** @dial appearance */
  color: RGB;  // where RGB is [r: number, g: number, b: number]

  // Mixed-type tuple inferred as "tuple" (general tuple)
  /** @dial metadata */
  config: [string, number, {name: string}];  // Will use nested panel UI

  // Arrays of primitives inferred as "array"
  /** @dial content */
  tags: string[];  // Dynamic list with add/remove

  /** @dial settings */
  values: number[];  // Array of numbers

  // Note: Complex element types are not supported
  // items: ComplexItem[];  // Will fallback to string
}

Advanced Features

Type System Support

The CLI fully supports TypeScript's type system, automatically merging annotations from multiple sources:

  • Interface Inheritance (extends): Properties and annotations from parent interfaces are merged
  • Type Intersections (&): Multiple types are combined with their annotations
  • Utility Types (Pick, Omit, Partial, Required): Type transformations are properly resolved
  • Cross-File Types: Types imported from other files are fully resolved

Example combining multiple type features:

// BaseTypes.tsx
export interface Transform {
  /** @dial transform @dial-dtype vector3 */
  position: [number, number, number];
}

// Component.tsx
import { Transform } from "./BaseTypes";

interface MeshProps extends Transform {
  /** @dial appearance @dial-dtype color */
  color: string;
}

// Result: MeshProps schema includes both position and color with their annotations

Component-Level Annotations

The @dial-ignore annotation can be added to component JSDoc comments to exclude the entire component from schema generation. This is a special case - only @dial-ignore is supported at the component level.

Important: Type-level annotations have no effect. Annotations on interface/type definitions (like InternalComponentProps below) do not apply to properties - they are only processed when attached to actual component props.

/**
 * This component will be excluded from schema generation
 * @dial-ignore
 */
export const InternalComponent: FC<Props> = ({ ... }) => {
  // Component will not generate dial schema
}

// ❌ INCORRECT: This annotation has no effect
/**
 * @dial-ignore
 */
interface SomeType {  // This does nothing
  field1: string;
  field2: number;
}

// ✓ CORRECT: Annotations apply to the component's props
interface ComponentProps {
  /**
   * This annotation applies to the 'position' property
   * @dial transform
   * @dial-dtype vector3
   */
  position: Vector3;
}

Scope clarification:

  • Component-level @dial-ignore: Applies to the React component itself
  • Property-level annotations: Apply to individual properties within the component's props interface
  • Type definition annotations: Have no effect and are ignored (types are just containers for structure)

Output Format

Generated Files

metadata/
└── schema.dial      # Combined schema for all components (JSON format)

Schema Structure

[
  {
    "component": "Box",
    "description": "Box component description",
    "schemas": [
      {
        "name": "position",
        "dtype": "vector3",
        "min": -10,
        "max": 10,
        "step": 0.1,
        "icon": "Move3d",
        "label": "3D Position",
        "value": [0, 0, 0],
        "tags": {
          "grouping": "transform"
        }
      }
    ],
    "groups": [
      {
        "name": "transform",
        "noWrap": false
      }
    ]
  }
]

Loading schema.dial Files

In Vite Projects

Configure Vite to recognize .dial files:

// vite.config.js
import { defineConfig } from 'vite';
import json from '@rollup/plugin-json';

export default defineConfig({
  plugins: [
    json({
      include: [
        '**/*.json',
        '**/*.dial'  // Add support for .dial files
      ]
    })
  ]
});

Then import directly:

import dialSchemas from './metadata/schema.dial';

<DialProvider schemas={dialSchemas[0].schemas}>
  {/* Your components */}
</DialProvider>

TypeScript Support

Add type declaration for .dial files:

// dial.d.ts or in your global types file
declare module '*.dial' {
  import type { DialSchema, GroupSchema } from '@vuer-ai/vuer-uikit';

  interface DialComponentSchema {
    component: string;
    schemas: DialSchema[];
    groups?: GroupSchema[];
  }

  const schemas: DialComponentSchema[];
  export default schemas;
}

Alternative: Rename to .json

dial-cli MyComponent.tsx -o ./metadata
mv ./metadata/schema.dial ./metadata/schema.json

Then import as JSON:

import dialSchemas from './metadata/schema.json';

Development

Local Development

# Clone repository
git clone https://github.com/vuer-ai/vuer-uikit
cd vuer-uikit/packages/vuer-uikit/dial-cli

# Install dependencies
pnpm install

# Build for development (watch mode)
pnpm dev

# In another terminal, test your changes
dial-cli test-file.tsx -o ./output

Running Tests

# Navigate to dial-cli directory
cd packages/vuer-uikit/dial-cli

# Run all tests
pnpm test

# Run tests in watch mode
pnpm test:watch

# Run tests with coverage report
pnpm test:coverage

Test Structure

The test suite includes:

  • Data Types Tests (spec/tests/1-dtypes/) - All supported dtypes
  • Inheritance Tests (spec/tests/2-inheritance/) - Type inheritance patterns
  • Annotation Tests (spec/tests/3-annotations/) - @dial annotations
  • CLI Flags Tests (spec/tests/4-cli-flags/) - Command-line flags
  • Complex Scenarios (spec/tests/5-complex/) - Integration tests

Documentation

Troubleshooting

Properties not appearing in schema

Problem: Properties don't show up in generated schema

Solution: Make sure to add at least one @dial-* annotation. Any dial annotation will include the property in the schema (except @dial-ignore which explicitly excludes it).

// Any of these will work:
/**
 * @dial transform                    ← Assigns to group "transform"
 * @dial-dtype vector3
 */
position: Vector3;

/**
 * @dial-min 0                        ← Just constraints work too
 * @dial-max 100
 */
opacity: number;

/**
 * @dial-dtype color                  ← Just dtype also works
 */
color: string;

// This property will be EXCLUDED:
/**
 * @dial-ignore                       ← Explicitly excluded
 */
internal: any;

// This property will NOT appear (no @dial-* annotations):
/**
 * Regular JSDoc comment without dial annotations
 */
className: string;

Inherited properties missing

Problem: Properties from parent interfaces are missing

Solution:

  • Ensure base type is in the same file or properly imported
  • Check that import paths are correct
  • Run with --verbose flag to see type resolution details

Wrong dtype inferred

Problem: CLI infers incorrect data type

Solution: Explicitly specify the dtype:

/** @dial-dtype vector3 */

CLI not finding files

Problem: Files not being processed

Solution: Use absolute paths or ensure working directory is correct:

cd project-root
dial-cli src/components/Box.tsx -o ./metadata

Performance

Benchmarks

| Scenario | Components | Time | Notes | |----------|-----------|------|-------| | Single simple component | 1 | ~100ms | No inheritance | | Component with inheritance | 1 | ~200ms | With type resolution | | Complex project | 20 | ~2s | Mixed complexity | | Large codebase | 100 | ~10s | Many inheritance chains |

Benchmarks on M1 Mac with TypeScript 5.8

Optimization Tips

  1. Use --ignore to skip unnecessary props:

    dial-cli Box.tsx --ignore "on*,_*,className"
  2. Process files in parallel:

    dial-cli src/**/*.tsx -o ./metadata
  3. Use --quiet in CI/CD:

    dial-cli src/**/*.tsx --quiet -o ./dist/metadata

Contributing

Contributions are welcome! Please see our GitHub repository for more information.

License

MIT

Author

Ge Yang

Links