The Vuedoc Parser
Generate a JSON documentation for a SFC Vue component.
Install
npm install --save @vuedoc/parser
Features
- Extract the component name (from the name field or from the filename)
- Extract the component description
- Keywords Support: You can define your own keywords with the
@
symbol like
@author Jon Snow
- Extract component model
- Extract component props
- Extract component data
- Extract computed properties with dependencies
- Extract component events
- Extract component slots
- Extract component methods
- Class Component Support
- Vue Property Decorator Support
- JSDoc Support (
@param
and
@return
tags)
Options
name | description |
---|---|
filename | The filename to parse. Required unless filecontent is passed |
filecontent | The file content to parse. Required unless filename is passed |
encoding | The file encoding. Default is 'utf8' |
features | The component features to parse and extract. |
Default features: ['name', 'description', 'keywords', 'slots', 'model', 'props', 'data', 'computed', 'events', 'methods'] |
|
loaders | Use this option to define custom loaders for specific languages |
defaultMethodVisibility | Can be set to 'public' (default), 'protected' , or 'private' |
ignoredVisibilities | List of ignored visibilities. Default: ['protected', 'private'] |
stringify | Set to true to disable parsing of literal values and stringify literal values. Default: false |
Note for stringify
option
By default Vuedoc Parser parses literal values defined in the source code.
This means:
const binVar = 0b111110111 // will be parsed as binVar = 503
const numVar = 1_000_000_000 // will be parsed as numVar = 1000000000
To preserve literal values, set the stringify
option to true
.
Usage
See test/fixtures/checkbox.vue
for an Vue Component decoration example.
const Vuedoc = require('@vuedoc/parser')
const options = {
filename: 'test/fixtures/checkbox.vue'
}
Vuedoc.parse(options)
.then((component) => console.log(component))
.catch((err) => console.error(err))
This will print this JSON output:
{
"name": "checkbox" // The component name
"description": "A simple checkbox component" // The component description
// Attached component keywords
"keywords": [
{ "name": "author", "description": "Sébastien" }
],
"props": [ ... ],
"data": [ ... ],
"computed": [ ... ],
"slots": [ ... ],
"events": [ ... ],
"methods": [ ... ]
}
See test/fixtures/checkbox-result.json for the complete result.
Syntax
Add component name
By default, Vuedoc Parser use the component's filename to generate the
component name.
To set a custom name, use the name
field like:
export default {
name: 'my-checkbox'
}
You can also use the @name
keyword to set the component name:
/**
* @name my-checkbox
*/
export default {
// ...
}
Add component description
To add a component description, just add a comment before the export default
statement like:
/**
* Component description
*/
export default {
...
}
Annotate model, props, data and computed properties
To document props, data or computed properties, use comments like:
export default {
props: {
/**
* Component ID
*/
id: {
type: String,
required: true
}
},
data () {
return {
/**
* Indicates that the control is checked
*/
isChecked: false
}
},
computed: {
/**
* Indicates that the control is selected
*/
selected () {
return this.isChecked
}
}
}
Vuedoc Parser will automatically extract required
and default
values for
properties and computed properties dependencies. It will also detect type for
each defined data field.
You can set a custom default value of an prop by using the keyword @default
.
export default {
props: {
/**
* Custom default value
* @default { anything: 'custom default value' }
*/
custom: {
type: String,
default: () => {
// complex code
return anythingExpression()
}
}
}
}
To document a v-model
prop, a proper way is to use the Vue's
model field if you use Vue +2.2.0.
export default {
/**
* Use `v-model` to define a reactive value of the checkbox
*/
model: {
prop: 'checked',
event: 'change'
}
}
You can also use the @model
keyword on a prop if you use an old Vue version:
export default {
props: {
/**
* The checkbox model
* @model
*/
model: {
type: Array,
required: true
}
}
}
To document Vue array string props, just attach a Vuedoc comment to each prop:
export default {
props: [
/**
* Checkbox ID
*/
'id',
/**
* The checkbox model
*/
'value'
]
}
By default, all extracted things have the public
visibility.
To change this for one entry, add @protected
or @private
keyword.
export default {
data: () => ({
/**
* This will be ignored on parsing
* @private
*/
isChecked: false
})
}
Annotate methods, events and slots
To document methods or events, just add comments before:
export default {
methods: {
/**
* Submit form
*/
check () {
/**
* Emit the `input` event on submit
*/
this.$emit('input', true)
}
}
}
Vuedoc Parser automatically extracts events from component hooks:
export default {
created () {
/**
* Emit on Vue `created` hook
*/
this.$emit('created', true)
}
}
Use the JSDoc @param and
@return tags to define parameters and
returning type:
export default {
methods: {
/**
* Submit form
*
* @param {object} data - Data to submit
* @return {boolean} true on success; otherwise, false
*/
submit (data) {
/**
* Emit the `loading` event on submit
*
* @arg {boolean} status - The loading status
*/
this.$emit('loading', true)
return true
}
}
}
Note:
@arg
is an alias of@param
The parser is also able to extract events and slots from template:
<template>
<div>
<!-- Emit the `input` event on submit -->
<button @click="$emit('input', $event)">Submit</button>
<!-- Default slot -->
<slot></slot>
<!-- Use this slot to set the checkbox label -->
<slot name="label">Unnamed checkbox</slot>
<!--
Slot with keywords and
multiline description
@prop {User} user - The current user
@prop {UserProfile} profile - The current user's profile
-->
<slot name="header" v-bind:user="user" v-bind:profile="profile"/>
</div>
</template>
Usage with non primitive name
You can use special keywords @method
and @event
for non primitive name:
<script>
const METHODS = {
CLOSE: 'closeModal'
}
const EVENTS = {
CLOSE: 'close'
}
export default {
methods: {
/**
* Close modal
* @method closeModal
*/
[METHODS.CLOSE] () {
/**
* Emit the `close` event on click
* @event close
*/
this.$emit(EVENTS.CLOSE, true)
}
}
}
</script>
Annotate slots defined in Render Functions
To annotate slots defined in Render Functions, just attach the keyword @slot
to the component definition:
/**
* A functional component with slots defined in render function
* @slot title - A title slot
* @slot default - A default slot
*/
export default {
functional: true,
render(h, { slots }) {
return h('div', [
h('h1', slots().title),
h('p', slots().default)
])
}
}
You can also use the keyword @slot
to define dynamic slots on template:
<template>
<div>
<template v-for="name in ['title', 'default']">
<!--
@slot title - A title slot
@slot default - A default slot
-->
<slot :name="name" :slot="name"></slot>
</template>
</div>
</template>
Keywords Extraction
You can attach keywords to any comment and then extract them using the parser.
Usage
/**
* Component description
*
* @author Arya Stark
* @license MIT
*/
export default { ... }
Note that the description must alway appear before keywords definition
Parsing result:
{
"name": "my-checkbox",
"description": "Component description",
"keywords": [
{
"name": "author",
"description": "Arya Stark"
},
{
"name": "license",
"description": "MIT"
}
]
}
Working with Mixins
Since Vuedoc Parser don't perform I/O operations, it completely ignores the
mixins
property.
To parse a mixin, you need to parse its file as a standalone component and
then merge the parsing result with the result of the initial component:
const Vuedoc = require('@vuedoc/parser')
const merge = require('deepmerge')
const parsers = [
Vuedoc.parse({ filename: 'mixinFile.js' })
Vuedoc.parse({ filename: 'componentUsingMixin.vue' })
]
Promise.all(parsers)
.then(merge.all)
.then((mergedParsingResult) => console.log(mergedParsingResult))
.catch((err) => console.error(err))
Using the keyword @mixin
You can use the special keyword @mixin
to force parsing named exported
component:
import Vue from 'vue';
/**
* @mixin
*/
export const InputMixin = Vue.extend({
props: {
id: String,
Value: [ Boolean, Number, String ]
}
});
Parsing control with options.features
options.features
lets you select which Vue Features you want to parse and
extract.
The default value is defined by Vuedoc.Parser.SUPPORTED_FEATURES
array.
Usage
Only parse name
, props
, computed properties
, slots
and events
:
const Vuedoc = require('@vuedoc/parser')
const options = {
filename: 'test/fixtures/checkbox.vue',
features: [ 'name', 'props', 'computed', 'slots', 'events' ]
}
Vuedoc.parse(options)
.then((component) => Object.keys(component))
.then((keys) => console.log(keys))
// => [ 'name', 'props', 'computed', 'slots', 'events' ]
Parse all features except data
:
const Vuedoc = require('@vuedoc/parser')
const options = {
filename: 'test/fixtures/checkbox.vue',
features: Vuedoc.Parser.SUPPORTED_FEATURES.filter((feature) => feature !== 'data')
}
Vuedoc.parse(options)
.then((component) => Object.keys(component))
.then((keys) => console.log(keys))
// => [ 'name', 'description', 'keywords', 'model',
// 'props', 'computed', 'events', 'methods', 'slots' ]
Language Processing
Loader API
abstract class Loader {
public static extend(loaderName: string, loaderClass: Loader);
public abstract load(source: string): Promise<void>;
public emitTemplate(source: string): Promise<void>;
public emitScript(source: string): Promise<void>;
public emitErrors(errors: Array<string>): Promise<void>;
public pipe(loaderName: string, source: string): Promise<void>;
}
Build-in loaders
Language | Load by default? | Package |
---|---|---|
HTML | Yes | @vuedoc/parser/loader/html |
JavaScript | Yes | @vuedoc/parser/loader/javascript |
Pug | No | @vuedoc/parser/loader/pug |
TypeScript | No | @vuedoc/parser/loader/typescript |
Vue | Yes | @vuedoc/parser/loader/vue |
TypeScript usage
The Vuedoc Parser package contains a loader for TypeScript. To use it, you need
to:
- install
typescript
and@types/node
dependencies according the
official documentation - import and load the loader
@vuedoc/parser/loader/typescript
const Vuedoc = require('@vuedoc/parser')
const TypeScriptLoader = require('@vuedoc/parser/loader/typescript')
const options = {
filename: 'DatePicker.ts',
loaders: [
/**
* Register TypeScriptLoader
* Note that the name of the loader is either the extension
* of the file or the value of the attribute `lang`
*/
Vuedoc.Loader.extend('ts', TypeScriptLoader)
]
}
Vuedoc.parse(options).then((component) => {
console.log(component)
})
Create a custom loader
The example below uses the abstract Vuedoc.Loader
class to create a
specialized class to handle a template with the CoffeeScript
language. It uses the Pug language for templating:
const Vuedoc = require('@vuedoc/parser')
const PugLoader = require('@vuedoc/parser/loader/pug')
const CoffeeScript = require('coffeescript')
class CoffeeScriptLoader extends Vuedoc.Loader {
load (source) {
const outputText = CoffeeScript.compile(source);
// don't forget the return here
return this.emitScript(outputText);
}
}
const options = {
filecontent: `
<template lang="pug">
div.page
h1 Vuedoc Parser with Pug
// Use this slot to define a subtitle
slot(name='subtitle')
</template>
<script lang="coffee">
###
# Description of MyInput component
###
export default
name: 'MyInput'
</script>
`,
loaders: [
/**
* Register CoffeeScriptLoader
* Note that the name of the loader is either the extension
* of the file or the value of the attribute `lang`
*/
Vuedoc.Loader.extend('coffee', CoffeeScriptLoader),
// Register the Pug loader
Vuedoc.Loader.extend('pug', PugLoader)
]
}
Vuedoc.parse(options).then((component) => {
console.log(component)
})
Output
{
name: 'MyInput',
description: 'Description of MyInput component',
slots: [
{
kind: 'slot',
visibility: 'public',
description: 'Use this slot to define a subtitle',
keywords: [],
name: 'subtitle',
props: []
}
],
// ...
}
Parsing Output Interface
type ParsingOutput = {
name: string; // Component name
description: string; // Component description
inheritAttrs: boolean;
keywords: Keyword[]; // Attached component keywords
model?: ModelEntry; // Component model
slots: SlotEntry[]; // Component slots
props: PropEntry[]; // Component props
data: DataEntry[]; // Component data
computed: ComputedEntry[]; // Computed properties
events: EventEntry[]; // Events
methods: MethodEntry[]; // Component methods
errors: string[]; // Syntax and parsing errors
};
enum NativeTypeEnum {
string,
number,
bigint,
boolean,
bigint,
any, // for an explicit `null` or `undefined` values
object, // for an array or an object
CallExpression // for a value like `new Date()`
};
type Keyword = {
name: string;
description: string;
}
interface Entry {
kind: 'computed' | 'data' | 'event' | 'method' | 'model' | 'prop' | 'slot';
visibility: 'public' | 'protected' | 'private';
description: string;
keywords: Keyword[];
}
interface ModelEntry extends Entry {
kind: 'model';
prop: string;
event: string;
}
interface SlotEntry extends Entry {
kind: 'slot';
name: string;
props: SlotProp[];
}
type SlotProp = {
name: string;
type: string;
description: string;
};
interface ModelEntry extends Entry {
kind: 'model';
prop: string;
event: string;
}
interface PropEntry extends Entry {
kind: 'prop';
name: string; // v-model when the @model keyword is attached
type: string | string[]; // ex. Array, Object, String, [String, Number]
nativeType: NativeTypeEnum;
default?: string;
required: boolean = false;
describeModel: boolean; // true when the @model keyword is attached
}
interface DataEntry extends Entry {
kind: 'data';
name: string;
type: NativeTypeEnum;
initial?: string;
}
interface ComputedEntry extends Entry {
kind: 'computed';
name: string;
dependencies: string[]; // list of dependencies of the computed property
}
interface EventEntry extends Entry {
kind: 'event';
name: string;
arguments: EventArgument[];
}
type EventArgument = {
name: string;
description: string;
type: string;
};
interface MethodEntry extends Entry {
kind: 'method';
name: string;
params: MethodParam[];
return: MethodReturn;
}
type MethodParam = {
type: string;
name: string;
description: string;
defaultValue?: string;
}
type MethodReturn = {
type: string = 'void';
description: string;
};