1
1
# Custom Generator
2
2
3
- You will probably want to extend or, at least, take a look at [ BaseGenerator.js] ( https://github.com/api-platform/client-generator/blob/main/src/generators/BaseGenerator.js ) , since the library expects some methods to be available, as well as one of the included generator to make your own.
3
+ Client Generator provides support for many of the popular JS frameworks, but you may be using another framework or language and may need a solution adapted to your specific needs. For this cenario, you can write your own generator and pass it to the CLI using a path as the ` -g ` argument.
4
+
5
+ You will probably want to extend or, at least, take a look at [ BaseGenerator.js] ( https://github.com/api-platform/client-generator/blob/main/src/generators/BaseGenerator.js ) , since the library expects some methods to be available, as well as one of the [ included generators] ( https://github.com/api-platform/client-generator/blob/main/src/generators/BaseGenerator.j ) to make your own.
4
6
5
7
## Usage
6
8
@@ -12,7 +14,7 @@ The `-g` argument can point to any resolvable node module which means it can be
12
14
13
15
## Example
14
16
15
- Let's create a basic react generator with Create form as an example:
17
+ Client Generator makes use of the [ Handlebars ] ( https://handlebarsjs.com/ ) template engine. You can use any programming language or file type. Your generator can also pass data to your templates in any shape you want.
16
18
17
19
### Generator
18
20
@@ -24,258 +26,90 @@ export default class extends BaseGenerator {
24
26
constructor (params ) {
25
27
super (params);
26
28
27
- this .registerTemplates (" " , [
28
- " utils/dataAccess.js" ,
29
- " components/foo/Create.js" ,
30
- " routes/foo.js" ,
31
- ]);
29
+ this .registerTemplates (" " , [" main.rs" ]);
32
30
}
33
31
34
- help (resource ) {
35
- const titleLc = resource .title .toLowerCase ();
36
-
37
- console .log (
38
- ' Code for the "%s" resource type has been generated!' ,
39
- resource .title
40
- );
41
- console .log (`
42
- //import routes
43
- import ${ titleLc} Routes from './routes/${ titleLc} ';
44
-
45
- // Add routes to <Switch>
46
- { ${ titleLc} Routes }
47
- ` );
48
- }
32
+ help () {}
49
33
50
34
generate (api , resource , dir ) {
51
- const lc = resource .title .toLowerCase ();
52
- const titleUcFirst =
53
- resource .title .charAt (0 ).toUpperCase () + resource .title .slice (1 );
54
-
55
35
const context = {
56
- title: resource .title ,
57
- name: resource .name ,
58
- lc,
59
- uc: resource .title .toUpperCase (),
60
- fields: resource .readableFields ,
61
- formFields: this .buildFields (resource .writableFields ),
62
- hydraPrefix: this .hydraPrefix ,
63
- titleUcFirst,
36
+ type: " Tilia" ,
37
+ structure: [
38
+ { name: " name" , type: " String" },
39
+ { name: " min_size" , type: " u8" },
40
+ { name: " max_size" , type: " u8" },
41
+ ],
42
+ list: [
43
+ {
44
+ name: " Tilia cordata" ,
45
+ minSize: 50 ,
46
+ maxSize: 80 ,
47
+ },
48
+ {
49
+ name: " Tilia platyphyllos" ,
50
+ minSize: 50 ,
51
+ maxSize: 70 ,
52
+ },
53
+ {
54
+ name: " Tilia tomentosa" ,
55
+ minSize: 50 ,
56
+ maxSize: 70 ,
57
+ },
58
+ {
59
+ name: " Tilia intermedia" ,
60
+ minSize: 50 ,
61
+ maxSize: 165 ,
62
+ },
63
+ ],
64
64
};
65
65
66
- // Create directories
67
- // These directories may already exist
68
- [` ${ dir} /utils` , ` ${ dir} /config` , ` ${ dir} /routes` ].forEach ((dir ) =>
69
- this .createDir (dir, false )
70
- );
71
-
72
- [` ${ dir} /components/${ lc} ` ].forEach ((dir ) => this .createDir (dir));
66
+ this .createDir (dir);
73
67
74
- [" components/%s/Create.js" , " routes/%s.js" ].forEach ((pattern ) =>
75
- this .createFileFromPattern (pattern, dir, lc, context)
76
- );
77
-
78
- // utils
79
- this .createFile (
80
- " utils/dataAccess.js" ,
81
- ` ${ dir} /utils/dataAccess.js` ,
82
- context,
83
- false
84
- );
85
-
86
- this .createEntrypoint (api .entrypoint , ` ${ dir} /config/entrypoint.js` );
68
+ this .createFile (" main.rs" , ` ${ dir} /main.rs` , context, false );
87
69
}
88
70
}
89
71
```
90
72
91
- ### ` Create ` component
92
-
93
- ``` js
94
- // template/components/Create.js
95
- import React from ' react' ;
96
- import { Redirect } from ' react-router-dom' ;
97
- import fetch from ' ../utils/dataAccess' ;
98
-
99
- export default function Create () {
100
- const [isLoading , setLoading ] = useState (false );
101
- const [error , setError ] = useState (null );
102
- const [created , setCreated ] = useState (null );
103
-
104
- const create = useCallback (async (e ) => {
105
- setLoading (true )
106
- try {
107
- const values = Array .from (e .target .elements ).reduce ((vals , e ) => {
108
- vals[e .id ] = e .value ;
109
- return vals
110
- }, {})
111
- const response = await fetch (' {{{name}}}' , { method: ' POST' , body: JSON .stringify (values) });
112
- const retrieved = await response .json ();
113
- setCreated (retrieved);
114
- } catch (err) {
115
- setError (err);
116
- } finally {
117
- setLoading (false );
118
- }
119
- }, [setLoading, setError])
120
-
121
- if (created) {
122
- return < Redirect to= {` edit/${ encodeURIComponent (created[' @id' ])} ` } / > ;
123
- }
124
-
125
- return (
126
- < div>
127
- < h1> New {{{title}}}< / h1>
73
+ ### Template
128
74
129
- {isLoading && (
130
- < div className= " alert alert-info" role= " status" >
131
- Loading...
132
- < / div>
133
- )}
134
- {error && (
135
- < div className= " alert alert-danger" role= " alert" >
136
- < span className= " fa fa-exclamation-triangle" aria- hidden= " true" / > {' ' }
137
- {error}
138
- < / div>
139
- )}
140
-
141
- < form onSubmit= {create}>
142
- {{#each formFields}}
143
- < div className= {` form-group` }>
144
- < label
145
- htmlFor= {` {{{lc}}}_{{{name}}}` }
146
- className= " form-control-label"
147
- >
148
- {data .input .name }
149
- < / label>
150
- < input
151
- name= " {{{name}}}"
152
- type= " {{{type}}}" {{#if step}}
153
- step= " {{{step}}}" {{/ if }}
154
- placeholder= " {{{description}}}" {{#if required}}
155
- required= {true }{{/ if }}
156
- id= {` {{{lc}}}_{{{name}}}` }
157
- / >
158
- < / div>
159
-
160
- < button type= " submit" className= " btn btn-success" >
161
- Submit
162
- < / button>
163
- < / form>
164
- < / div>
165
- );
75
+ ``` rs
76
+ // template/main.rs
77
+ struct {{{type }}} {
78
+ {{#each structure }}
79
+ {{{name }}}: {{{type }}}
80
+ {{/ each }}
166
81
}
167
- ```
168
82
169
- ### Utilities
170
-
171
- ``` js
172
- // template/entrypoint.js
173
- export const ENTRYPOINT = " {{{entrypoint}}}" ;
83
+ fn main () {
84
+ let tilias = [
85
+ {{#each list }}
86
+ Tilia { name : " {{{name}}}" , min_size : {{{minSize}}}, max_size : {{{maxSize}}}, },
87
+ {{/ each }}
88
+ ];
89
+ }
174
90
```
175
91
176
- ``` js
177
- // template/routes/foo.js
178
- import React from " react" ;
179
- import { Route } from " react-router-dom" ;
180
- import { Create } from " ../components/{{{lc}}}/" ;
92
+ Then we can use our generator:
181
93
182
- export default [
183
- < Route path= " /{{{name}}}/create" component= {Create} exact key= " create" / > ,
184
- ];
94
+ ``` shell
95
+ generate-api-platform-client https://demo.api-platform.com out/ -g " $( pwd) /Generator.js" -t " $( pwd) /template"
185
96
```
186
97
187
- ``` js
188
- // template/utils/dataAccess.js
189
- import { ENTRYPOINT } from " ../config/entrypoint" ;
190
- import { SubmissionError } from " redux-form" ;
191
- import get from " lodash/get" ;
192
- import has from " lodash/has" ;
193
- import mapValues from " lodash/mapValues" ;
194
-
195
- const MIME_TYPE = " application/ld+json" ;
196
-
197
- export function fetch (id , options = {}) {
198
- if (" undefined" === typeof options .headers ) options .headers = new Headers ();
199
- if (null === options .headers .get (" Accept" ))
200
- options .headers .set (" Accept" , MIME_TYPE );
201
-
202
- if (
203
- " undefined" !== options .body &&
204
- ! (options .body instanceof FormData ) &&
205
- null === options .headers .get (" Content-Type" )
206
- )
207
- options .headers .set (" Content-Type" , MIME_TYPE );
208
-
209
- return global .fetch (new URL (id, ENTRYPOINT ), options).then ((response ) => {
210
- if (response .ok ) return response;
211
-
212
- return response .json ().then (
213
- (json ) => {
214
- const error =
215
- json[" hydra:description" ] ||
216
- json[" hydra:title" ] ||
217
- " An error occurred." ;
218
- if (! json .violations ) throw Error (error);
219
-
220
- let errors = { _error: error };
221
- json .violations .forEach ((violation ) =>
222
- errors[violation .propertyPath ]
223
- ? (errors[violation .propertyPath ] +=
224
- " \n " + errors[violation .propertyPath ])
225
- : (errors[violation .propertyPath ] = violation .message )
226
- );
227
-
228
- throw new SubmissionError (errors);
229
- },
230
- () => {
231
- throw new Error (response .statusText || " An error occurred." );
232
- }
233
- );
234
- });
235
- }
236
-
237
- export function mercureSubscribe (url , topics ) {
238
- topics .forEach ((topic ) =>
239
- url .searchParams .append (" topic" , new URL (topic, ENTRYPOINT ))
240
- );
241
-
242
- return new EventSource (url .toString ());
243
- }
244
-
245
- export function normalize (data ) {
246
- if (has (data, " hydra:member" )) {
247
- // Normalize items in collections
248
- data[" hydra:member" ] = data[" hydra:member" ].map ((item ) =>
249
- normalize (item)
250
- );
251
-
252
- return data;
253
- }
98
+ which will produces:
254
99
255
- // Flatten nested documents
256
- return mapValues (data, (value ) =>
257
- Array .isArray (value)
258
- ? value .map ((v ) => normalize (v))
259
- : value instanceof Object
260
- ? normalize (value)
261
- : get (value, " @id" , value)
262
- );
100
+ ``` ts
101
+ struct Tilia {
102
+ name : String
103
+ min_size : u8
104
+ max_size : u8
263
105
}
264
106
265
- export function extractHubURL (response ) {
266
- const linkHeader = response .headers .get (" Link" );
267
- if (! linkHeader) return null ;
268
-
269
- const matches = linkHeader .match (
270
- / <([^ >] + )>;\s + rel=(?:mercure| "[^ "] * mercure[^ "] * ")/
271
- );
272
-
273
- return matches && matches[1 ] ? new URL (matches[1 ], ENTRYPOINT ) : null ;
107
+ fn main () {
108
+ let tilias = [
109
+ Tilia { name: " Tilia cordata" , min_size: 50 , max_size: 80 , },
110
+ Tilia { name: " Tilia platyphyllos" , min_size: 50 , max_size: 70 , },
111
+ Tilia { name: " Tilia tomentosa" , min_size: 50 , max_size: 70 , },
112
+ Tilia { name: " Tilia intermedia" , min_size: 50 , max_size: 165 , },
113
+ ];
274
114
}
275
115
```
276
-
277
- Then we can use our generator:
278
-
279
- ``` shell
280
- generate-api-platform-client https://demo.api-platform.com out/ -g " $( pwd) /Generator.js" -t " $( pwd) /template"
281
- ```
0 commit comments