UNPKG

4.67 kBMarkdownView Raw
1---
2id: how
3title: How to create plugin
4sidebar_label: How to
5---
6
7A `merry` plugin can write via `JavaScript` or `TypeScript`. both of them are identical.
8
9You can create a new plugin using
10
11```sh
12merry plugin -h
13```
14
15we have a builtin plugin can help you
16generate a new one. and It will ask you a few simple questions that how your plugin will
17work.
18
19## Example
20
21A simple Plugin: the `api` parameter is a instance of [`Plugin`](api.html).
22
23file `index.js`
24
25```js
26module.exports = api => {
27 // note if you want use async you need set your node engine version >= 8
28 // or using typescript
29 api.command('pluginName [cmd]').action(async (cmd, options) => {
30 // ...
31 })
32}
33```
34
35## Example React Component
36
37Demonstrate how to create a plugin that generate a `React` Component
38file `component.ts`
39
40```ts
41import { Plugin, Action } from '@merryjs/cli/lib/plugin'
42import { getPath } from './utils'
43export interface ComponentAnswers {
44 name: string
45 style: boolean
46 isRouteComponent: boolean
47 route?: string
48 routeGroup: string
49 componentTemplatePath?: string
50}
51/**
52 * run component action
53 * @param api
54 * @param answers
55 */
56export const runComponentAction = async (
57 api: Plugin,
58 answers: ComponentAnswers
59) => {
60 const group = answers.routeGroup ? `{{properCase routeGroup}}/` : ''
61 const componentTemplatePath = `./templates/component.hbs`
62
63 const dist =
64 answers.isRouteComponent && answers.route
65 ? `routes/${group}{{properCase route}}`
66 : 'components'
67
68 const options = { ...(answers as any), ...{ dist }, cwd: api.conf.dist }
69
70 const actions: Action[] = [
71 {
72 path: getPath(options, 'index.tsx'),
73 templateFile: componentTemplatePath,
74 format: {
75 parser: 'typescript',
76 },
77 },
78 ]
79
80 // If they want a CSS file, add styles.css
81 if (answers.style) {
82 actions.push({
83 path: getPath(options, 'styles.tsx'),
84 templateFile: './templates/styles.hbs',
85 format: {
86 parser: 'typescript',
87 },
88 })
89 }
90 return await api.runActions(actions, answers)
91}
92/**
93 * component generate
94 */
95export default async (api: Plugin) => {
96 const answers = await api.prompt<ComponentAnswers>([
97 {
98 type: 'input',
99 name: 'name',
100 message: 'Whats your component Name?',
101 validate: value => {
102 if (/.+/.test(value)) {
103 return true
104 }
105
106 return 'The name is required'
107 },
108 },
109 {
110 type: 'confirm',
111 name: 'style',
112 default: true,
113 message: 'Does it have styling?',
114 },
115 {
116 type: 'confirm',
117 name: 'isRouteComponent',
118 default: false,
119 message: 'Create a Component under routes?',
120 },
121 {
122 type: 'input',
123 name: 'route',
124 message: 'Which route do you want use?',
125 when: data => data.isRouteComponent,
126 },
127 {
128 type: 'input',
129 name: 'routeGroup',
130 default: '',
131 message:
132 'Do you want creating a grouped route component and which group do you want use ?',
133 when: data => data.route,
134 },
135 ])
136 await runComponentAction(api, answers)
137}
138```
139
140entry file `index.ts`
141
142```ts
143import { Plugin, Action } from '@merryjs/cli/lib/plugin'
144import path from 'path'
145import component from './component'
146
147export enum GeneratorType {
148 ROUTE = 'route',
149 COMPONENT = 'component',
150 FORM = 'form',
151}
152const supportedTypes = [
153 GeneratorType.FORM,
154 GeneratorType.ROUTE,
155 GeneratorType.COMPONENT,
156]
157
158export interface ReactGeneratorOptions {
159 swagger: string
160 menu: string
161}
162/**
163 * ReactGeneratorAnswers
164 */
165export interface ReactGeneratorAnswers {
166 name: string
167}
168
169export default (api: Plugin) => {
170 api
171 .command('react [name]')
172 .usage(supportedTypes.map(cmd => `[${cmd}]`).join(' | '))
173 .action(async (name: GeneratorType, options: ReactGeneratorOptions) => {
174 switch (name) {
175 case GeneratorType.ROUTE:
176 // ...
177 break
178 case GeneratorType.COMPONENT:
179 await component(api)
180 break
181 case GeneratorType.FORM:
182 // await checkOptions(api, options)
183 // await form(api, options)
184 break
185 default:
186 if (!name) {
187 api.log('[%s] is not a known command', name)
188 } else {
189 api.log('unknown commands [%s]', name)
190 }
191 api.log(`supported commands: %O`, supportedTypes)
192 break
193 }
194 })
195}
196```
197
198file `component.hbs`
199
200```hbs
201/*
202 * *****************************************
203 * {{ properCase name }} => Component
204 * *****************************************
205 */
206
207import React from 'react'
208{{#if style}}
209import styles from './style.css'
210{{/if}}
211
212export interface {{ properCase name }}Props {
213 children?: React.ReactNode
214}
215export interface {{ properCase name }}State {
216
217}
218
219export class {{ properCase name }} extends React.Component<{{ properCase name }}Props, {{ properCase name }}State> {
220 render() {
221 return <div>
222 This is {{properCase name}} Component !
223 </div>
224 }
225}
226```