Skip to content

Commit 7a82e1a

Browse files
committed
Batch generate impl
1 parent 9644adb commit 7a82e1a

File tree

7 files changed

+212
-5
lines changed

7 files changed

+212
-5
lines changed

README.md

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ Example
209209
Here is the string `Lives down BY the River` with each of the converters:
210210

211211
```js
212-
// If you typed in 'Lives down BY the River' for the a Replacer Slot named '__replacerSlot__' and
212+
// If you typed in 'Lives down BY the River' for the a Replacer Slot named '__replacerSlot__' and
213213
// used one of the optional Case Converters you would get the following:
214214

215215
__replacerSlot__(noCase) // Lives down BY the River
@@ -224,7 +224,7 @@ __replacerSlot__(sentenceCase) // Lives down by the river
224224
__replacerSlot__(snakeCase) // lives_down_by_the_river
225225
__replacerSlot__(titleCase) // Lives Down By The River
226226

227-
// Note: you can set a 'defaultCase' converter in IConfigItem so all
227+
// Note: you can set a 'defaultCase' converter in IConfigItem so all
228228
// Replacer Slots without a Case Converter will be transformed the same way.
229229
__replacerSlot__ // LivesDownByTheRiver
230230
```
@@ -234,6 +234,52 @@ One Rule: no spaces between the [Replacer Slots](#replacer-slots-or-ireplacerslo
234234
- :white_check_mark: `__name__(camelCase)`
235235
- :warning: `__name__ (camelCase)`
236236

237+
## Batch Usage
238+
239+
You can use `generate-template-files` to generate your template files programmatically, without any interactive prompts. This mode does not support `stringReplacers`.
240+
241+
The following example will generate the component, unit tests, and the SCSS module in one do.
242+
243+
```ts
244+
// generateTemplateFile.ts
245+
import { generateTemplateFilesCommandLine, CaseConverterEnum } from 'generate-template-files');
246+
247+
export const componentWithInterface = async (componentName: string, componentScope: string = "common"): Promise<void> => {
248+
await generateTemplateFilesBatch([
249+
{
250+
option: 'Component',
251+
defaultCase: CaseConverterEnum.PascalCase,
252+
entry: {
253+
folderPath: './tools/templates/react/component',
254+
},
255+
dynamicReplacers: [
256+
{ slot: '__name__', slotValue: componentName },
257+
{ slot: '__scope__', slotValue: componentScope },
258+
],
259+
output: {
260+
path: `./src/component/__scope__(camelCase)`,
261+
pathAndFileNameDefaultCase: CaseConverterEnum.PascalCase,
262+
},
263+
},
264+
{
265+
option: 'Component Interface',
266+
defaultCase: CaseConverterEnum.PascalCase,
267+
entry: {
268+
folderPath: './tools/templates/react/I__interface__.ts',
269+
},
270+
dynamicReplacers: [
271+
{ slot: '__interface__', slotValue: componentName },
272+
{ slot: '__scope__', slotValue: componentScope },
273+
],
274+
output: {
275+
path: `./src/component/__scope__(camelCase)/I__interface__.ts`,
276+
pathAndFileNameDefaultCase: CaseConverterEnum.PascalCase,
277+
},
278+
},
279+
]);
280+
};
281+
```
282+
237283
## Command Line Usage
238284

239285
You can use `generate-template-files` with the command line to generate your template files.

examples/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"scripts": {
66
"---------- Normal Example -------------------------------------------": "",
77
"generate": "node ./tools/generate.js",
8+
"batchGenerate": "node ./tools/batchGenerate.js",
89
"---------- Command Line Example -------------------------------------": "",
910
"commandlineSimple": "node ./tools/commandLine.js angular-ngrx-store __name__=some-name __model__=some-other-name",
1011
"commandline": "node ./tools/commandLine.js angular-ngrx-store __name__=some-name __model__=some-other-name --outputpath=./src/here --overwrite"

examples/tools/batchGenerate.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// generateTemplateFile.js
2+
const {generateTemplateFilesBatch, CaseConverterEnum} = require('../../dist/generate-template-files.cjs');
3+
// Note: In your file it will be like this:
4+
// const {generateTemplateFilesBatch} = require('generate-template-files');
5+
6+
const componentName = "Example"
7+
const componentScope = "common"
8+
9+
generateTemplateFilesBatch([
10+
{
11+
option: 'Component',
12+
defaultCase: CaseConverterEnum.PascalCase,
13+
entry: {
14+
folderPath: './tools/templates/react/component',
15+
},
16+
dynamicReplacers: [
17+
{ slot: '__name__', slotValue: componentName },
18+
{ slot: '__scope__', slotValue: componentScope },
19+
],
20+
output: {
21+
path: `./src/component/__scope__(camelCase)`,
22+
pathAndFileNameDefaultCase: CaseConverterEnum.PascalCase,
23+
},
24+
},
25+
{
26+
option: 'Component Interface',
27+
defaultCase: CaseConverterEnum.PascalCase,
28+
entry: {
29+
folderPath: './tools/templates/react/I__interface__.ts',
30+
},
31+
dynamicReplacers: [
32+
{ slot: '__interface__', slotValue: componentName },
33+
{ slot: '__scope__', slotValue: componentScope },
34+
],
35+
output: {
36+
path: `./src/component/__scope__(camelCase)/I__interface__.ts`,
37+
pathAndFileNameDefaultCase: CaseConverterEnum.PascalCase,
38+
},
39+
},
40+
]);
41+

src/GenerateTemplateFiles.ts

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as enquirer from 'enquirer';
1+
import enquirer from 'enquirer';
22
import recursiveCopy from 'recursive-copy';
33
import pathExists from 'path-exists';
44
import through from 'through2';
@@ -11,9 +11,10 @@ import IResults from './models/IResults';
1111
import IDefaultCaseConverter from './models/IDefaultCaseConverter';
1212
import {
1313
throwErrorIfNoConfigItems,
14-
throwErrorIfOptionNameIsNotFound,
1514
throwErrorIfNoStringOrDynamicReplacers,
15+
throwErrorIfOptionNameIsNotFound,
1616
throwErrorIfStringReplacersDoNotMatch,
17+
throwErrorIfStringReplacersExistOrNoDynamicReplacers,
1718
displayError,
1819
displayWarning,
1920
displaySuccess,
@@ -23,6 +24,7 @@ import yargs from 'yargs';
2324

2425
export default class GenerateTemplateFiles {
2526
private _isCommandLine: boolean = false;
27+
private _isBatch: boolean = false;
2628

2729
/**
2830
* Main method to create your template files. Accepts an array of `IConfigItem` items.
@@ -88,6 +90,27 @@ export default class GenerateTemplateFiles {
8890
}
8991
}
9092

93+
/**
94+
* Main method to run generate on multiple templates at once, without any interactive prompts
95+
*/
96+
public async batchGenerate(options: Omit<IConfigItem, 'stringReplacers'>[]) {
97+
this._isBatch = true;
98+
99+
try {
100+
throwErrorIfNoConfigItems(options);
101+
throwErrorIfStringReplacersExistOrNoDynamicReplacers(options);
102+
103+
for (const selectedConfigItem of options) {
104+
const answeredReplacers: IReplacer[] = await this._getDynamicReplacerSlotValues(
105+
selectedConfigItem
106+
);
107+
await this._outputFiles(selectedConfigItem, answeredReplacers);
108+
}
109+
} catch (error) {
110+
displayError(error.message);
111+
}
112+
}
113+
91114
private async _outputFiles(
92115
selectedConfigItem: IConfigItem,
93116
replacers: IReplacer[]
@@ -184,11 +207,22 @@ export default class GenerateTemplateFiles {
184207
};
185208
}
186209
);
187-
const dynamicReplacers: IReplacer[] = selectedConfigItem.dynamicReplacers || [];
210+
const dynamicReplacers = await this._getDynamicReplacerSlotValues(selectedConfigItem);
188211

189212
return [...replacers, ...dynamicReplacers];
190213
}
191214

215+
/**
216+
* Dynamic replacer values, used for interactive and batch generation
217+
*/
218+
private async _getDynamicReplacerSlotValues(
219+
selectedConfigItem: IConfigItem
220+
): Promise<IReplacer[]> {
221+
const dynamicReplacers: IReplacer[] = selectedConfigItem.dynamicReplacers || [];
222+
223+
return dynamicReplacers;
224+
}
225+
192226
/**
193227
* Create every variation for the for the replacement keys
194228
*/
@@ -239,6 +273,10 @@ export default class GenerateTemplateFiles {
239273
return outputPath ?? outputPathFormatted;
240274
}
241275

276+
if (this._isBatch) {
277+
return outputPathFormatted;
278+
}
279+
242280
const outputPathAnswer: any = await enquirer.prompt({
243281
type: 'input',
244282
name: 'outputPath',
@@ -269,6 +307,10 @@ export default class GenerateTemplateFiles {
269307
return selectedConfigItem.output.overwrite || yargs.argv.overwrite === true;
270308
}
271309

310+
if (this._isBatch) {
311+
return Boolean(selectedConfigItem.output.overwrite);
312+
}
313+
272314
const overwriteFilesAnswer: any = await enquirer.prompt({
273315
name: 'overwrite',
274316
message: 'Overwrite files, continue?',

src/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,12 @@ export function generateTemplateFiles(data: IConfigItem[]): Promise<void> {
2828
export function generateTemplateFilesCommandLine(data: IConfigItem[]): Promise<void> {
2929
return new GenerateTemplateFiles().commandLine(data);
3030
}
31+
32+
/**
33+
* Main method to run generate on multiple templates at once, without any interactive prompts.
34+
*/
35+
export function generateTemplateFilesBatch(
36+
data: Omit<IConfigItem, 'stringReplacers'>[]
37+
): Promise<void> {
38+
return new GenerateTemplateFiles().batchGenerate(data);
39+
}

src/utilities/CheckUtility.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,16 @@ export const throwErrorIfNoStringOrDynamicReplacers = (options: IConfigItem[]) =
7272
throw new Error('IConfigItem needs to have a stringReplacers or dynamicReplacers.');
7373
}
7474
};
75+
76+
export const throwErrorIfStringReplacersExistOrNoDynamicReplacers = (options: IConfigItem[]) => {
77+
const allValidBatchEntries =
78+
options.every((item: IConfigItem) => {
79+
return !Boolean(item?.stringReplacers?.length) && Boolean(item?.dynamicReplacers?.length);
80+
}) && options.length > 0;
81+
82+
if (!allValidBatchEntries) {
83+
throw new Error(
84+
'IConfigItem for batchGenerate does not support stringReplacers, and must have dynamicReplacers.'
85+
);
86+
}
87+
};
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import GenerateTemplateFiles from '../GenerateTemplateFiles';
2+
import { IConfigItem } from '../index';
3+
import CaseConverterEnum from '../constants/CaseConverterEnum';
4+
5+
describe.skip('GenerateTemplateFiles - Batch', () => {
6+
test('should throw an error if no IConfigItem items', () => {
7+
const items: IConfigItem[] = [];
8+
const gtf = new GenerateTemplateFiles();
9+
10+
expect(() => gtf.batchGenerate(items)).rejects.toThrowError(
11+
'There was no IConfigItem items found.'
12+
);
13+
});
14+
15+
test('should throw an error if no stringReplacers or dynamicReplacers', () => {
16+
const items: IConfigItem[] = [
17+
{
18+
option: 'some-template',
19+
defaultCase: CaseConverterEnum.PascalCase,
20+
entry: {
21+
folderPath: 'path',
22+
},
23+
output: {
24+
path: 'path',
25+
},
26+
},
27+
];
28+
const gtf = new GenerateTemplateFiles();
29+
30+
expect(() => gtf.commandLine(items)).rejects.toThrowError(
31+
'IConfigItem needs to have a stringReplacers or dynamicReplacers.'
32+
);
33+
});
34+
35+
test('should throw an error if batch IConfigItem is not found for option name', () => {
36+
const items: IConfigItem[] = [
37+
{
38+
option: 'some-template',
39+
defaultCase: CaseConverterEnum.PascalCase,
40+
stringReplacers: ['__name__'],
41+
entry: {
42+
folderPath: 'path',
43+
},
44+
output: {
45+
path: 'path',
46+
},
47+
},
48+
];
49+
const gtf = new GenerateTemplateFiles();
50+
51+
expect(() => gtf.batchGenerate(items)).rejects.toThrowError(
52+
`IConfigItem for batchGenerate does not support stringReplacers, and must have dynamicReplacers.`
53+
);
54+
});
55+
});

0 commit comments

Comments
 (0)