Skip to content

Support for Vue 3 Composables

Summary
I have some components I would like to document with vuedoc-md that have some of their data and methods from a composable. I documented all the data, method, and types in the composable function body in a separate file and even following the merge example for usage with mixins, none of the types or comment blocks get pulled in. Not sure if this issue fits here or if it'd be better suited for vuedoc/markdown. Also not sure if I am just using this tool incorrectly/using the wrong tool for what I want to do, so any suggestions are welcome.

Steps to reproduce
The following is just a fake example for sake of simplicity:
useMouse.ts:

import { reactive, onMounted, onUnmounted } from 'vue';

/**
 * Interface for mouse position
 * @public
 */
export interface IMouseState {
	/**
	 * The x position of the mouse
	 */
	x: number;
	/**
	 * The y position of the mouse
	 */
	y: number;
	/**
	 * Whether or not the left button is held down
	 */
	buttonHeld: boolean;
}

/**
 * Hook in reactive mouse position
 * @returns State of mouse as reactive and method for getting scalar distance
 * @public
 */
export default function useMouse() {
	/**
	 * The state of the mouse
	 */
	const mouseState: IMouseState = reactive({ x: 0, y: 0, buttonHeld: false });

	function update(event: MouseEvent) {
		mouseState.x = event.pageX;
		mouseState.y = event.pageY;
		mouseState.buttonHeld = (event.buttons & 0x1) !== 0;
	}

	/**
	 * Get scalar distance of mouse position from (0,0)
	 * @returns Distance of pointer from (0,0)
	 */
	const mouseDistance = () => Math.sqrt(mouseState.x ** 2 + mouseState.y ** 2);

	onMounted(() => {
		window.addEventListener('mousemove', update);
		window.addEventListener('mousedown', update);
		window.addEventListener('mouseup', update);
	})
	onUnmounted(() => {
		window.removeEventListener('mousemove', update);
		window.removeEventListener('mousedown', update);
		window.removeEventListener('mouseup', update);
	})

	return {
		mouseState,
		mouseDistance,
	}
}

Component.vue:

<script lang="ts">
import { defineComponent } from 'vue';
import useMouse from './useMouse';

export default defineComponent({
	setup() {
		const { mouseState, mouseDistance } = useMouse();

		return {
			mouseState,
			mouseDistance,
		}
	}
})
</script>

What is the current bug behavior?

following Mixin merge.all example from README:

{
  "inheritAttrs": true,
  "errors": [],
  "warnings": [],
  "keywords": [
    {
      "name": "returns",
      "description": "State of mouse as reactive and method for getting scalar distance"
    }
  ],
  "slots": [],
  "props": [],
  "data": [
    {
      "kind": "data",
      "keywords": [],
      "visibility": "public",
      "type": "unknown",
      "name": "mouseState",
      "initialValue": "undefined"
    },
    {
      "kind": "data",
      "keywords": [],
      "visibility": "public",
      "type": "unknown",
      "name": "mouseDistance",
      "initialValue": "undefined"
    }
  ],
  "computed": [],
  "events": [],
  "methods": [],
  "description": "Hook in reactive mouse position",
  "name": "Component"
}

yarn vuedoc-md ./useMouse.ts ./Component.vue -j:

Component

Hook in reactive mouse position

  • returns - State of mouse as reactive and method for getting scalar distance

Data

Name Type Description Initial value
mouseState unknown undefined
mouseDistance unknown undefined

What is the expected correct behavior?

following Mixin merge.all example from README:

{
  "inheritAttrs": true,
  "errors": [],
  "warnings": [],
  "keywords": [],
  "slots": [],
  "props": [],
  "data": [
    {
      "kind": "data",
      "description": "The state of the mouse",
      "keywords": [],
      "visibility": "public",
      "type": "object",
      "name": "mouseState",
      "initialValue": "{\"x\":0,\"y\":0,\"buttonHeld\":false}"
    }
  ],
  "computed": [],
  "events": [],
  "methods": [
    {
      "kind": "method",
      "description": "Get scalar distance of mouse position from (0,0)",
      "keywords": [],
      "visibility": "public",
      "name": "mouseDistance",
      "params": [],
      "syntax": [
        "mouseDistance(): number"
      ],
      "returns": {
        "type": "number",
        "description": "Distance of pointer from (0,0)"
      }
    }
  ],
  "name": "Component"
}

yarn vuedoc-md ./useMouse.ts ./Component.vue -j:

Component

Data

Name Type Description Initial value
mouseState object The state of the mouse {"x":0,"y":0,"buttonHeld":false}

Methods

mouseDistance()

Get scalar distance of mouse position from (0,0)

Syntax

mouseDistance(): number

Return value

Distance of pointer from (0,0)

Possible fixes

Possibly add @composable directive that makes the parser treat the exported composable function the same as a composition API setup() function, so that any data or methods returned from the composable that are destructed and returned in setup() get merged in with the component documentation.

e.g. useMouse.ts:

/**
 * Hook in reactive mouse position
 * @returns State of mouse as reactive and method for getting scalar distance
 * @public
 * @composable
 */
export default function useMouse() {
	/**
	 * The state of the mouse
	 */
	const mouseState: IMouseState = reactive({ x: 0, y: 0, buttonHeld: false });

	function update(event: MouseEvent) {
		mouseState.x = event.pageX;
		mouseState.y = event.pageY;
		mouseState.buttonHeld = (event.buttons & 0x1) !== 0;
	}

	/**
	 * Get scalar distance of mouse position from (0,0)
	 * @returns Distance of pointer from (0,0)
	 */
	const mouseDistance = () => Math.sqrt(mouseState.x ** 2 + mouseState.y ** 2);

	onMounted(() => {
		window.addEventListener('mousemove', update);
		window.addEventListener('mousedown', update);
		window.addEventListener('mouseup', update);
	})
	onUnmounted(() => {
		window.removeEventListener('mousemove', update);
		window.removeEventListener('mousedown', update);
		window.removeEventListener('mouseup', update);
	})

	return {
		mouseState,
		mouseDistance,
	}
}