Skip to content

Commit 35204ad

Browse files
authored
Merge pull request #32 from acrontum/enh/multiple-template-overrides
enh/multiple-template-overrides
2 parents ab6beee + d783e01 commit 35204ad

6 files changed

Lines changed: 437 additions & 56 deletions

File tree

readme.md

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -76,23 +76,30 @@ then you can just run `npx bc model test --dry-run`.
7676

7777
## Custom Generator Overrides
7878

79-
You can override any of the generators by adding any of these files to a templates folder, and passing `--templates <folder>` to the cli:
80-
- boats-rc.js
81-
- component-index.js
82-
- create.js
83-
- delete.js
84-
- index.js
85-
- list.js
86-
- model.js
87-
- models.js
88-
- pagination-model.js
89-
- param.js
90-
- path-index.js
91-
- replace.js
92-
- show.js
93-
- update.js
94-
95-
Or, alternatively, exporting any of the following methods from a module (local file or node module):
79+
You can override any of the generators by adding any files or exports to the templates option (`--templates <folder_or_lib>`).
80+
81+
Boats cli will try to import from the folling:
82+
83+
| file | export |
84+
|---------------------|--------------------|
85+
| boats-rc.js | getBoatsRc |
86+
| component-index.js | getComponentIndex |
87+
| create.js | getCreate |
88+
| delete.js | getDelete |
89+
| index.js | getIndex |
90+
| list.js | getList |
91+
| model.js | getModel |
92+
| models.js | getModels |
93+
| pagination-model.js | getPaginationModel |
94+
| param.js | getParam |
95+
| path-index.js | getPathIndex |
96+
| replace.js | getReplace |
97+
| show.js | getShow |
98+
| update.js | getUpdate |
99+
100+
Multiple invocations of `-T, --templates` will merge / override the results of the templates, in the order supplied.
101+
102+
A module export might look like:
96103
```js
97104
exports.getBoatsRc = (opts, file) => { /* ... */ };
98105
exports.getIndex = (opts, file) => { /* ... */ };
@@ -110,7 +117,7 @@ exports.getUpdate = (opts, file) => { /* ... */ };
110117
exports.getReplace = (opts, file) => { /* ... */ };
111118
```
112119

113-
for example, `templates/index.js` or `exports.getList`:
120+
or overriding `path <name> --list`, a file `templates/list.js` or module with `exports.getList`:
114121
```js
115122
// @ts-check
116123
const { toYaml } = require('@acrontum/boats-cli/dist/src/lib');
@@ -134,9 +141,10 @@ module.exports = (_globalOptions, file, pluralName, schemaRef, parameters) => {
134141
});
135142
};
136143

144+
// or exports.getList = (_globalOptions, file, pluralName, schemaRef, parameters) => { ... }
137145
````
138146

139-
or disabling the default generator and instead creating 2 different files for models `templates/model.yml` or `exports.getModel`:
147+
or disabling the default generator and instead creating 2 different files for models (`templates/model.yml` or `exports.getModel`):
140148
```js
141149
// @ts-check
142150
const { toYaml } = require('@acrontum/boats-cli/dist/src/lib');

src/cli.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export const cliArguments: Record<string, CliArg> = {
9393
type: 'string',
9494
short: 'T',
9595
[argname]: 'TEMPLATES',
96-
[description]: 'Folder or module containing template overrides',
96+
[description]: 'Folder or module containing template overrides (can be invoked multiple times)',
9797
},
9898
force: { type: 'boolean', short: 'f', [description]: 'Overwrite existing files' },
9999
'no-index': { type: 'boolean', short: 'I', [description]: 'Skip auto-creating index files, only models' },
@@ -338,7 +338,13 @@ export const cli = async (args: string[]): Promise<Record<string, GenerationTask
338338

339339
return {};
340340
}
341+
341342
processTemplates = arg.inlineValue ? arg.value.slice(1) : arg.value;
343+
const templates = await getTemplates(processTemplates);
344+
if (templates === null) {
345+
return {};
346+
}
347+
globalOptions.customTemplates = { ...globalOptions.customTemplates, ...templates };
342348
} else if (arg.name === 'output' || arg.name === 'root-ref') {
343349
if (!arg.value) {
344350
help(1, `Parameter '--${arg.name}' requires a value`);
@@ -415,13 +421,6 @@ export const cli = async (args: string[]): Promise<Record<string, GenerationTask
415421
globalOptions.output = relative('.', globalOptions.output);
416422
}
417423

418-
if (processTemplates) {
419-
const templates = await getTemplates(processTemplates);
420-
if (templates !== null) {
421-
globalOptions.customTemplates = templates;
422-
}
423-
}
424-
425424
if (!globalOptions['no-init']) {
426425
tasks.push(
427426
{ contents: () => getIndex(globalOptions, 'src/index.yml'), filename: 'src/index.yml' },

test/custom-models.spec.ts

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe('custom-models.spec.ts', async () => {
2929
`),
3030
);
3131

32-
const indexFile = await boats('test/output/custom/src/index.yml', 'test/output/custom/api.json');
32+
const indexFile = await boats('test/output/custom/src/index.yml', 'test/output/custom/build/api.json');
3333

3434
assert.strictEqual(indexFile !== '', true, 'boats failed');
3535
assert.strictEqual(await getFile(indexFile), await getFile('test/fixtures/spec/custom.json'), 'spec mismatch');
@@ -96,38 +96,30 @@ describe('custom-models.spec.ts', async () => {
9696
--quiet
9797
--output test/output/custom
9898
-T test/fixtures/overrides/module.js
99+
--templates test/fixtures/overrides
100+
--templates test/fixtures/overrides/single-export/
99101
`),
100102
);
101103

102-
const files = await getAllFiles('test/output/custom/');
103-
assert.deepStrictEqual(files, [
104-
'test/output/custom/.boatsrc',
105-
'test/output/custom/src/components/parameters/index.yml',
106-
'test/output/custom/src/components/parameters/pathUserId.yml',
107-
'test/output/custom/src/components/parameters/queryLimit.yml',
108-
'test/output/custom/src/components/parameters/queryOffset.yml',
109-
'test/output/custom/src/components/parameters/queryUserId.yml',
110-
'test/output/custom/src/components/schemas/index.yml',
111-
'test/output/custom/src/components/schemas/jwt/model.yml',
112-
'test/output/custom/src/components/schemas/pagination/model.yml',
113-
'test/output/custom/src/components/schemas/user/model.yml',
114-
'test/output/custom/src/components/schemas/user/models.yml',
115-
'test/output/custom/src/components/schemas/user/patch.yml',
116-
'test/output/custom/src/components/schemas/user/post.yml',
117-
'test/output/custom/src/components/schemas/user/put.yml',
118-
'test/output/custom/src/index.yml',
119-
'test/output/custom/src/paths/index.yml',
120-
'test/output/custom/src/paths/users/get.yml',
121-
'test/output/custom/src/paths/users/post.yml',
122-
'test/output/custom/src/paths/users/{userId}/delete.yml',
123-
'test/output/custom/src/paths/users/{userId}/get.yml',
124-
'test/output/custom/src/paths/users/{userId}/patch.yml',
125-
'test/output/custom/src/paths/users/{userId}/put.yml',
126-
]);
104+
const indexFile = await boats('test/output/custom/src/index.yml', 'test/output/custom/build/api.json');
105+
106+
assert.strictEqual(indexFile !== '', true, 'boats failed');
107+
assert.strictEqual(await getFile(indexFile), await getFile('test/fixtures/spec/custom-multi.json'), 'spec mismatch');
108+
});
109+
110+
await it('adds and overwrites templates when invoked multiple times', async () => {
111+
assert.deepStrictEqual(await getAllFiles('test/output/custom/').catch(() => []), []);
127112

128-
for (const file of await getAllFiles('test/output/custom/')) {
129-
assert.deepStrictEqual(await getFile(file), file.replace('test/output/custom/', ''));
130-
}
113+
await cli(
114+
toArgv(`
115+
path users/:userId -crudl --put
116+
model jwt
117+
model userId --type query
118+
--quiet
119+
--output test/output/custom
120+
-T test/fixtures/overrides/module.js
121+
`),
122+
);
131123
});
132124

133125
await it('outputs a meaningful error message', async () => {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// @ts-check
2+
3+
/** @type{import('../../../../').CustomTemplates['getModel']} */
4+
module.exports = (_globalOptions, _file) => {
5+
return `\
6+
type: "object"
7+
required:
8+
- "id"
9+
- "createdAt"
10+
- "updatedAt"
11+
properties:
12+
id:
13+
type: "string"
14+
format: "uuid"
15+
createdAt:
16+
type: "string"
17+
format: "date-time"
18+
updatedAt:
19+
type: "string"
20+
format: "date-time"
21+
name:
22+
type: "string"
23+
description: "Name of the thing, separated by dashes (-)"
24+
example: "this-is-an-example"
25+
minLength: 1
26+
pattern: "\\\\S"
27+
nullable: true
28+
`;
29+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// @ts-check
2+
3+
const { toYaml } = require('../../../../dist/src/lib');
4+
5+
/** @type{import('../../../../').CustomTemplates['getModels']} */
6+
module.exports = (_globalOptions, _file) => {
7+
return toYaml({
8+
type: 'object',
9+
required: ['meta', 'data'],
10+
properties: {
11+
meta: { $ref: '#/components/schemas/Pagination' },
12+
data: {
13+
type: 'array',
14+
items: { $ref: './model.yml' },
15+
},
16+
},
17+
});
18+
};

0 commit comments

Comments
 (0)