Skip to content

Commit b25e313

Browse files
authored
Merge pull request #40 from codeBelt/command-line
Allow user to use the command line to generate template files
2 parents e5e0e80 + 9ca77d2 commit b25e313

File tree

7 files changed

+736
-494
lines changed

7 files changed

+736
-494
lines changed

README.md

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![NPM version][npm-img]][npm-url] [![Downloads][downloads-img]][npm-url]
44

5-
A simple generator that is independent from any language. Create custom boilerplate, scaffolding, skeleton, and templating code files that you need to create over and over again. All you need is [NodeJS](https://nodejs.org) installed to get started.
5+
A simple generator that is independent of any language. Create custom boilerplate, scaffolding, skeleton, and templating code files that you need to create over and over again. All you need is [NodeJS](https://nodejs.org) installed to get started.
66

77
> Find this useful? Give it a :star:
88
@@ -184,14 +184,24 @@ Below is an example of a `IReplacerSlotQuestion`
184184
- `question` - The question to ask the use what value should be used for the replacer `slot`
185185
- `slot` - The string value for the [Replacer Slots](#replacer-slots-or-ireplacerslotquestion)
186186

187+
#### Dynamic Replacer Slots
188+
189+
If you have data that is dynamically generated, or you have hard coded values you can use the `dynamicReplacers`:
190+
191+
```javascript
192+
dynamicReplacers: [
193+
{slot:'__description__', slotValue: config.description}
194+
],
195+
```
196+
187197
### Case Converters
188198

189199
[Case Converters](#case-converters) transform the string value entered upon use of the generator.
190200

191201
Example
192202

193203
- In the generator template `__replacerSlot__` is appended by the `(pascalCase)` converter such as `__replacerSlot__(pascalCase)`.
194-
- When the generator is run, the string `"product reducer"` is provided for `__replacerSlot__`.
204+
- When the generator is ran, the string `"product reducer"` is provided for `__replacerSlot__`.
195205
- As a result, the converter will produce `ProductReducer`.
196206

197207
Here is the string `Lives down BY the River` with each of the converters:
@@ -213,6 +223,32 @@ One Rule: no spaces between the [Replacer Slots](#replacer-slots-or-ireplacerslo
213223
- :white_check_mark: `__name__(camelCase)`
214224
- :warning: `__name__ (camelCase)`
215225

226+
## Command Line Usage
227+
228+
You can use `generate-template-files` with the command line to generate your template files.
229+
230+
When using the command line `stringReplacers` will be ignored, and the arguments pass in will be used. Example: `__name__=some-name`. `dynamicReplacers` are still used with the command line.
231+
232+
###### Minimum Options
233+
234+
```txt
235+
node ./tools/generate.js angular-ngrx-store __name__=some-name
236+
```
237+
238+
###### All Options
239+
240+
```txt
241+
node ./tools/generate.js angular-ngrx-store __name__=some-name __model__=some-other-name --outputpath=./src/here --overwrite
242+
```
243+
244+
**Command LIne Script Overview**
245+
246+
- `node ./tools/generate.js` - Runs the `generate-template-files` library
247+
- `angular-ngrx-store` - The template name; It uses the same option name in the [IConfigItem](#iconfigitem) but converts all options names to kebab-case. For example `option: 'Angular Ngrx Store'` will be converted to `angular-ngrx-store` when using the command line
248+
- `__name__=some-name` - Are [Replacer Slots](#replacer-slots-or-ireplacerslotquestion) and will be converted to `{ slot: "__name__", slotValue: "some-name" }`
249+
- `--outputpath=./src/here` - Will override the `output.path` in the [IConfigItem](#iconfigitem)
250+
- `--overwrite` - Will overwrite files if the files already exists
251+
216252
[npm-url]: https://npmjs.org/package/generate-template-files
217253
[downloads-img]: http://img.shields.io/npm/dm/generate-template-files.svg?style=flat-square
218254
[npm-img]: http://img.shields.io/npm/v/generate-template-files.svg?style=flat-square

examples/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
"version": "1.0.0",
44
"description": "This is to be used with dynamic replacers as an example",
55
"scripts": {
6-
"generate": "node ./tools/generate.js"
6+
"---------- Normal Example -------------------------------------------": "",
7+
"generate": "node ./tools/generate.js",
8+
"---------- Command Line Example -------------------------------------": "",
9+
"commandline": "node ./tools/generate.js angular-ngrx-store __name__=some-name __model__=some-other-name --outputpath=./src/here --overwrite"
710
},
811
"devDependencies": {
912
"file-name": "0.1.0",

examples/tools/generate.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,22 @@ const config = require('../package.json');
1010
generateTemplateFiles([
1111
// Angular
1212
{
13-
option: "Angular Ngrx Store",
13+
option: 'Angular Ngrx Store',
1414
defaultCase: '(pascalCase)',
1515
entry: {
1616
folderPath: './tools/templates/angular/ngrx-store/',
1717
},
18-
stringReplacers: ['__name__', { question: 'Insert model name', slot: '__model__' }],
18+
stringReplacers: ['__name__', {question: 'Insert model name', slot: '__model__'}],
1919
dynamicReplacers: [
20-
{slot:'__version__', slotValue: config.version},
21-
{slot:'__description__', slotValue: config.description}
20+
{slot: '__version__', slotValue: config.version},
21+
{slot: '__description__', slotValue: config.description},
2222
],
2323
output: {
2424
path: './src/app/stores/__name__(lowerCase)',
2525
pathAndFileNameDefaultCase: '(kebabCase)',
2626
},
2727
onComplete: async (results) => {
28-
console.log(`results`, results);
28+
// console.log(`results`, results);
2929
},
3030
},
3131
// Vue
@@ -59,7 +59,7 @@ generateTemplateFiles([
5959
},
6060
onComplete: (results) => {
6161
console.log(`results`, results);
62-
}
62+
},
6363
},
6464
{
6565
option: 'React Component',
@@ -142,9 +142,9 @@ async function importVuexStore(results) {
142142
const files = results.output.files;
143143

144144
const fullPaths = files
145-
.map((folderPath) => folderPath.replace('src/', '')) // remove 'src' from path
146-
.map((path) => `import ${filename(path)} from '${path}'`) // create import statement
147-
.join('\n'); // put all imports on there own line
145+
.map((folderPath) => folderPath.replace('src/', '')) // remove 'src' from path
146+
.map((path) => `import ${filename(path)} from '${path}'`) // create import statement
147+
.join('\n'); // put all imports on there own line
148148

149149
try {
150150
await insertLine('src/import-test.ts').append(fullPaths);

package.json

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,36 +56,38 @@
5656
"path-exists": "4.0.0",
5757
"recursive-copy": "2.0.10",
5858
"replace-string": "3.0.0",
59-
"through2": "3.0.1"
59+
"through2": "3.0.1",
60+
"yargs": "15.3.1"
6061
},
6162
"license": "MIT",
6263
"devDependencies": {
63-
"@babel/core": "7.9.0",
64+
"@babel/core": "7.9.6",
6465
"@babel/plugin-proposal-class-properties": "7.8.3",
65-
"@babel/plugin-proposal-object-rest-spread": "7.9.5",
66-
"@babel/plugin-transform-runtime": "7.9.0",
67-
"@babel/preset-env": "7.9.5",
66+
"@babel/plugin-proposal-object-rest-spread": "7.9.6",
67+
"@babel/plugin-transform-runtime": "7.9.6",
68+
"@babel/preset-env": "7.9.6",
6869
"@babel/preset-typescript": "7.9.0",
69-
"@babel/runtime": "7.9.2",
70+
"@babel/runtime": "7.9.6",
7071
"@types/jest": "25.2.1",
7172
"@types/lodash.get": "4.4.6",
7273
"@types/path-exists": "4.0.2",
7374
"@types/replace-string": "3.0.0",
7475
"@types/through2": "2.0.34",
76+
"@types/yargs": "15.0.4",
7577
"husky": "4.2.5",
76-
"jest": "25.4.0",
77-
"prettier": "2.0.4",
78+
"jest": "25.5.4",
79+
"prettier": "2.0.5",
7880
"pretty-quick": "2.0.1",
7981
"rimraf": "3.0.2",
80-
"rollup": "2.6.1",
82+
"rollup": "2.7.6",
8183
"rollup-plugin-babel": "4.4.0",
8284
"rollup-plugin-commonjs": "10.1.0",
8385
"rollup-plugin-node-resolve": "5.2.0",
8486
"ts-jest": "25.4.0",
8587
"tslib": "1.11.1",
86-
"tslint": "6.1.1",
88+
"tslint": "6.1.2",
8789
"tslint-config-prettier": "1.18.0",
88-
"typedoc": "0.17.4",
90+
"typedoc": "0.17.6",
8991
"typescript": "3.8.3"
9092
},
9193
"author": {

src/GenerateTemplateFiles.ts

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,35 +12,94 @@ import IResults from './models/IResults';
1212
import IDefaultCaseConverter from './models/IDefaultCaseConverter';
1313
import CheckUtility from './utilities/CheckUtility';
1414
import IReplacerSlotQuestion from './models/IReplacerSlotQuestion';
15+
import yargs from 'yargs';
1516

1617
export default class GenerateTemplateFiles {
18+
public static isCommandLine: boolean = Boolean(yargs.argv._.length);
19+
1720
/**
1821
* Main method to create your template files. Accepts an array of `IConfigItem` items.
1922
*/
2023
public async generate(options: IConfigItem[]): Promise<void> {
2124
const selectedConfigItem: IConfigItem = await this._getSelectedItem(options);
2225
const answeredReplacers: IReplacer[] = await this._getReplacerSlotValues(selectedConfigItem);
26+
27+
await this._outputFiles(selectedConfigItem, answeredReplacers);
28+
}
29+
30+
/**
31+
* Main method to create your template files with the command line.
32+
*/
33+
public async commandLine(options: IConfigItem[]): Promise<void> {
34+
const commandLineArgs: string[] = yargs.argv._;
35+
const templateName: string = commandLineArgs.shift() ?? '';
36+
const slots: string[] = commandLineArgs;
37+
38+
const selectedConfigItem: IConfigItem | undefined = options.find((configItem: IConfigItem) => {
39+
return (
40+
StringUtility.toCase(configItem.option, CaseConverterEnum.KebabCase) ===
41+
StringUtility.toCase(templateName, CaseConverterEnum.KebabCase)
42+
);
43+
});
44+
45+
if (!selectedConfigItem) {
46+
CheckUtility.check(Boolean(selectedConfigItem), `There was no IConfigItem found for ${templateName}`);
47+
48+
return;
49+
}
50+
51+
const replacers: IReplacer[] = slots.map((str: string) => {
52+
const [slot, slotValue] = str.split('=');
53+
54+
const isValidReplacer: boolean = Boolean(slot) && str.includes('=');
55+
CheckUtility.check(isValidReplacer, `${str} is not valid as a IReplacer`);
56+
57+
return {
58+
slot,
59+
slotValue,
60+
};
61+
});
62+
const dynamicReplacers: IReplacer[] = selectedConfigItem.dynamicReplacers || [];
63+
64+
await this._outputFiles(selectedConfigItem, [...replacers, ...dynamicReplacers]);
65+
}
66+
67+
private async _outputFiles(selectedConfigItem: IConfigItem, replacers: IReplacer[]): Promise<void> {
68+
const hasStringOrDynamicReplacers: boolean = replacers.length > 0;
69+
70+
if (!hasStringOrDynamicReplacers) {
71+
CheckUtility.check(hasStringOrDynamicReplacers, 'You need at least one IReplacer');
72+
73+
return;
74+
}
75+
2376
const {contentCase, outputPathCase} = this._getDefaultCaseConverters(selectedConfigItem);
24-
const contentReplacers: IReplacer[] = this._getReplacers(answeredReplacers, contentCase);
25-
const outputPathReplacers: IReplacer[] = this._getReplacers(answeredReplacers, outputPathCase);
77+
const contentReplacers: IReplacer[] = this._getReplacers(replacers, contentCase);
78+
const outputPathReplacers: IReplacer[] = this._getReplacers(replacers, outputPathCase);
2679
const outputPath: string = await this._getOutputPath(outputPathReplacers, selectedConfigItem);
27-
const shouldWriteFiles: boolean = await this._shouldWriteFiles(outputPath);
80+
const shouldWriteFiles: boolean = GenerateTemplateFiles.isCommandLine
81+
? yargs.argv.overwrite === true
82+
: await this._shouldWriteFiles(outputPath);
2883

2984
if (shouldWriteFiles === false) {
3085
console.info('No new files created');
3186

87+
if (GenerateTemplateFiles.isCommandLine) {
88+
console.info('Use --overwrite option to overwrite existing files');
89+
}
90+
3291
return;
3392
}
3493

3594
const outputtedFilesAndFolders: string[] = await this._createFiles(
36-
answeredReplacers,
95+
replacers,
3796
outputPathReplacers,
3897
contentReplacers,
3998
outputPath,
4099
selectedConfigItem.entry.folderPath
41100
);
42101

43-
this._onComplete(selectedConfigItem, outputPath, outputtedFilesAndFolders, answeredReplacers);
102+
this._onComplete(selectedConfigItem, outputPath, outputtedFilesAndFolders, replacers);
44103
}
45104

46105
/**
@@ -109,10 +168,6 @@ export default class GenerateTemplateFiles {
109168
);
110169
const dynamicReplacers: IReplacer[] = selectedConfigItem.dynamicReplacers || [];
111170

112-
const hasStringOrDynamicReplacers: boolean = stringReplacers.length > 0 || dynamicReplacers.length > 0;
113-
114-
CheckUtility.check(hasStringOrDynamicReplacers, '"stringReplacers" or "dynamicReplacers" needs at least one item.');
115-
116171
return [...replacers, ...dynamicReplacers];
117172
}
118173

@@ -151,6 +206,12 @@ export default class GenerateTemplateFiles {
151206
return replaceString(outputPath, replacer.slot, replacer.slotValue);
152207
}, selectedConfigItem.output.path);
153208

209+
if (GenerateTemplateFiles.isCommandLine) {
210+
const outputPath = yargs.argv.outputpath as string | undefined;
211+
212+
return outputPath ?? outputPathFormatted;
213+
}
214+
154215
const outputPathAnswer: any = await enquirer.prompt({
155216
type: 'input',
156217
name: 'outputPath',
@@ -228,7 +289,7 @@ export default class GenerateTemplateFiles {
228289
try {
229290
await recursiveCopy(entryFolderPath, outputPath, recursiveCopyOptions);
230291

231-
console.info(`Files outed to: '${outputPath}'`);
292+
console.info(`Files saved to: '${outputPath}'`);
232293

233294
return outputtedFilesAndFolders.filter(Boolean);
234295
} catch (error) {

src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,9 @@ export type IReplacerSlotQuestion = IReplacerSlotQuestionDefault;
1919
* Main method to create your template files. Accepts an array of `IConfigItem` items.
2020
*/
2121
export function generateTemplateFiles(data: IConfigItem[]): Promise<void> {
22+
if (GenerateTemplateFiles.isCommandLine) {
23+
return new GenerateTemplateFiles().commandLine(data);
24+
}
25+
2226
return new GenerateTemplateFiles().generate(data);
2327
}

0 commit comments

Comments
 (0)