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

@qds.dev/tools

v0.14.3

Published

Tools and utilities for Qwik Design System

Downloads

347

Readme

@qds.dev/tools

Build tools and utilities for Qwik Design System

npm version

Overview

Building QDS packages requires specialized build-time transformations for icons, component patterns, and interactive playgrounds. Setting these up from scratch means dealing with AST parsing, virtual modules, and complex code generation. You need to transform icon JSX into inline SVG, hoist child props for polymorphic components, and extract prop metadata for documentation.

@qds.dev/tools provides ready-to-use Vite and Rolldown plugins that transform your code at build time. You get battle-tested transformations for icon management, asChild pattern implementation, and playground utilities without manual setup.

What makes this useful: These are the same build-time transformations QDS uses internally. If you're building Qwik libraries or need advanced code transformation utilities, you can reuse proven plugins rather than reinventing them.

Installation

npm install @qds.dev/tools

Note: This is primarily for internal/advanced use. Most users don't need to install directly. Use the create-qds CLI instead, which includes these tools preconfigured.

Quick Start

Simplest case: Using the icons plugin

// vite.config.ts
import { icons } from "@qds.dev/tools/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [icons()]
});

Common pattern: Full plugin setup

// rolldown.config.ts
import { icons, asChild } from "@qds.dev/tools/vite";
import { defineConfig } from "rolldown";

export default defineConfig({
  plugins: [icons(), asChild()]
});

For Contributors

If you're editing files in libs/components/, read this section first.

When you edit a QDS component, several build plugins transform your code before it runs. Understanding these transformations prevents confusion when you see patterns like <Lucide.Check /> or asChild in the codebase.

Quick Reference

| Pattern You See | Plugin | What Happens | | ---------------------------------------- | -------------- | --------------------------------------- | | <Lucide.Check />, <Heroicons.Star /> | Icons | Becomes inline <svg> at build time | | <Button asChild><a href="..."> | AsChild | Child props hoisted to parent | | component$() | Qwik Optimizer | Not in @qds.dev/tools (built into Qwik) |

When to Read More

When You Edit a Component File

Here's what happens when you save a .tsx file in libs/components/:

1. Vite Dev Server Detects Change

File changed: libs/components/checkbox/checkbox-indicator.tsx

2. Icons Plugin Runs (if icons present)

Triggered by: Import from @qds.dev/ui like Lucide, Heroicons, Tabler

// Your code:
import { Lucide } from "@qds.dev/ui";
<Lucide.Check width={20} class="text-green-500" />;

// After icons plugin:
import __qds_i_lucide_check from "virtual:icons/lucide/check";
<svg
  width={20}
  class="text-green-500"
  height="1em"
  viewBox="0 0 24 24"
  dangerouslySetInnerHTML={__qds_i_lucide_check}
/>;

Why this matters: The icon component you write doesn't exist at runtime. It's replaced with an actual <svg> element. If you're debugging icon issues, check the transformed output in browser devtools.

3. AsChild Plugin Runs (if asChild present)

Triggered by: asChild prop on any JSX element with exactly one child

// Your code:
<Checkbox.Trigger asChild>
  <label class="flex items-center">Toggle</label>
</Checkbox.Trigger>

// After asChild plugin:
<Checkbox.Trigger jsxType="label" movedProps={{ "class": "flex items-center" }}>
  Toggle
</Checkbox.Trigger>

Why this matters: The child element's wrapper is removed. Props move to movedProps, element type moves to jsxType. The Checkbox.Trigger component receives these and renders the correct element type.

4. Qwik Optimizer Runs

After @qds.dev/tools plugins finish, Qwik's optimizer extracts component$() bodies into lazy-loaded chunks. This is not part of @qds.dev/tools but happens next in the build pipeline.

5. Browser Receives Transformed Code

What you wrote is NOT what runs in the browser. The transformations above happen at build time. Use browser devtools to see the actual rendered output.

Common Debugging Scenarios

| Issue | Likely Cause | Solution | | ----------------------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------ | | Icon doesn't render | Icon name not in collection | Check Iconify for valid names | | Icon has wrong viewBox | Icon collection uses different dimensions | Specify explicit viewBox prop | | asChild throws error | More than one child element | Ensure exactly one JSX child (whitespace is ignored) | | asChild props missing | Props not on direct child | Move props to the immediate child element | | Build error: "asChild elements must have exactly one child" | Multiple children or JSX expression | Use single element child, not {condition && <el>} patterns |

How Plugins Transform Your Code

All transformations happen at build time. Your source code uses clean JSX patterns, and the plugins transform them into optimized runtime code.

Icons Plugin: JSX-to-SVG Transformation

Transforms icon JSX elements (<Lucide.Check />) into inline SVG at build time with virtual imports.

Before transformation (your code):

import { Lucide } from "@qds.dev/ui";

export default component$(() => {
  return <Lucide.Check width={24} class="text-green-500" />;
});

After transformation (build output):

import __qds_i_lucide_check from 'virtual:icons/lucide/check';

export default component$(() => {
  return <svg width={24} class="text-green-500" height="1em" viewBox="0 0 24 24" dangerouslySetInnerHTML={__qds_i_lucide_check} />;
});

What this does:

  • Transforms icon JSX into inline SVG elements
  • Generates virtual imports for icon data
  • Adds default dimensions (1em) if not specified
  • Preserves all props (class, aria attributes, etc.)
  • Deduplicates icon imports (multiple uses = one import)

AsChild Plugin: Prop Hoisting

Hoists child element props to parent component when using asChild pattern for polymorphic rendering.

Before transformation (your code):

export const Link = component$(() => {
  // Gives styles of button, but renders as the link child.
  // Useful when sharing styles OR existing logic inside of button that needs to be shared.
  return (
    <Button asChild>
      <a href="/test">Link</a>
    </Button>
  );
});

After transformation (build output):

export const Link = component$(() => {
  return (
    <Button jsxType="a" movedProps={{ "href": "/test" }}>
      Link
    </Button>
  );
});

What this does:

  • Removes child element wrapper
  • Hoists child props to movedProps object
  • Adds jsxType to identify element type
  • Preserves grandchildren (text, nested elements)
  • Supports conditional expressions (isLink ? <a> : <button>)

API

| Export | Description | | ------------------------------ | ---------------------------------------------------------------------------------------------------------- | | Main export (@qds.dev/tools) | Core type utilities (AsChildTypes) | | /vite | Mostly re-exported from rolldown to stay modular: icons, asChild, inlineAsset, minifyContentPlugin | | /rolldown | Rolldown plugins: icons, asChild, inlineAsset, inlineCssPlugin, qwikRolldown | | /playground | Prop extraction and scenario injection plugins for component playgrounds | | /utils | General utility functions for code transformation |

Key plugins:

icons(options?)

Transforms icon JSX elements into inline SVG with virtual imports.

Options:

  • packs?: Record<string, { iconifyPrefix: string }> - Custom icon pack configuration
  • importSources?: string[] - Additional import sources to scan (default: ["@qds.dev/ui"])
  • debug?: boolean - Enable debug logging

Default icon packs: Lucide, Heroicons, Tabler, Hugeicons, MaterialSymbols, AkarIcons

asChild(options?)

Hoists child element props to parent component for polymorphic rendering.

Options:

  • debug?: boolean - Enable debug logging

Use case: Implement polymorphic "as" pattern where components can render as different elements while preserving parent component logic.

playground(options)

Factory function returning two plugins for interactive component playgrounds.

Returns: [propExtraction(options), scenarioInjection()]

Options:

  • componentsDir: string - Directory containing component files
  • outputDir: string - Directory for generated metadata JSON
  • debug?: boolean - Enable debug logging

What it does:

  • Extracts TypeScript prop types from component files → JSON metadata
  • Injects scenario imports into MDX playground files
  • Enables interactive component demos with prop controls

Playground Utilities

The /playground export provides specialized plugins for building interactive component documentation.

Usage Example

// vite.config.ts
import { playground } from "@qds.dev/tools/playground";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [
    playground({
      componentsDir: "libs/components/src",
      outputDir: ".playground-metadata"
    })
  ]
});

propExtraction Plugin

Extracts TypeScript prop types from component files and generates JSON metadata.

Process:

  1. Reads component .tsx files from componentsDir
  2. Parses TypeScript prop interfaces and types
  3. Extracts JSDoc comments for prop descriptions
  4. Generates JSON files in outputDir

Output type:

interface ComponentMetadata {
  componentName: string;
  pieces: ComponentPiece[];
}

interface ComponentPiece {
  name: string;
  props: PropType[];
  jsdoc?: string;
}

interface PropType {
  name: string;
  type: string;
  required: boolean;
  description?: string;
}

scenarioInjection Plugin

Transforms MDX files containing <Playground /> components by auto-injecting scenario imports.

Process:

  1. Scans for <Playground /> in MDX files
  2. Looks for adjacent scenarios/ directory
  3. Auto-imports scenario components and source code
  4. Injects scenarios prop with component + code pairs

Example MDX transformation:

Before:

# Button Component

<Playground component={Button} />

After (build time):

import * as Scenario1 from "./scenarios/basic.tsx";
import Scenario1Code from "./scenarios/basic.tsx?raw";

<Playground
  component={Button}
  scenarios={[{ component: Scenario1, code: Scenario1Code }]}
/>

Architecture

For package internals, dependency relationships, and design decisions, see ARCHITECTURE.md.

Why This Pattern?

Separate tools package: Build utilities shouldn't be bundled with runtime code. Separating build-time tooling keeps package sizes small and allows sharing configurations across multiple packages.

Build-time transformation: Instead of runtime overhead for icon resolution or asChild logic, transformations happen during build. Your production bundle contains optimized code with zero transformation cost.

Plugin architecture: Vite and Rolldown plugins are composable. You can mix QDS plugins with your own custom build logic. Each plugin handles one concern (icons, transformations, metadata extraction) so you only include what you need.

Related Packages

Used by:

  • create-qds - CLI that scaffolds projects with these tools preconfigured
  • All QDS packages - Internal build tooling for @qds.dev/ui, @qds.dev/motion, @qds.dev/code

Complements:

  • @qds.dev/ui - Uses these build tools for icon transformations
  • @qds.dev/base - Uses these build tools for component patterns

Build dependencies:

  • vite - Vite plugin compatibility
  • rolldown - Rolldown plugin compatibility
  • oxc-* - Fast JavaScript/TypeScript parsing and transformation

Known Limitations

  • Internal package: API may change without major version bumps. This package prioritizes QDS internal needs over public API stability.

  • Advanced use only: Most users should use create-qds CLI instead, which configures these tools automatically. Direct usage requires understanding Vite/Rolldown plugin architecture.

  • Node.js only: These are build-time tools, not browser utilities. They run during your build process, not in the browser.

  • Qwik-specific optimizations: While plugins work with any Vite/Rolldown project, some features (like AsChild transformations) are designed for Qwik component patterns.

Documentation

For usage within QDS projects, see the QDS documentation. For general Qwik tooling questions, refer to Qwik documentation.

This is primarily internal tooling. For most use cases, start with create-qds which includes these tools preconfigured.