generate-template-files-v2
Forked from generate-template-files: Because author not maintain
Find this useful? Give it a
⭐
Medium Article - Generate Template Files with Ease
Install
With NPM:
$ npm install generate-template-files-v2
With Yarn:
$ yarn add generate-template-files-v2
Usage
- Check out the
examples
folder or create a file calledgenerate.js
. Note that this file name is flexible. - In that file, add in the example code below.
- Run
node generate.js
within Terminal (Mac) or Powershell (Win) once you've added your template files.
const { generateTemplateFiles } = require('generate-template-files-v2');
const config = require('../package.json');
generateTemplateFiles([
{
option: 'Create Redux Store',
defaultCase: '(pascalCase)',
entry: {
folderPath: './tools/templates/react/redux-store/',
},
stringReplacers: ['__store__', { question: 'Insert model name', slot: '__model__' , result : (value)=> {
return value+"custom if you want"
}}],
dynamicReplacers: [
{ slot: '__version__', slotValue: config.version },
{ slot: '__customVersion__', slotValue: config.description ,
newSlot:({__version__})=>{
return __version__+ ".1.1"
}},
],
output: {
path: './src/stores/__store__(lowerCase)',
pathAndFileNameDefaultCase: '(kebabCase)',
overwrite: true,
},
},
{
option: 'Create Reduce Action',
defaultCase: '(pascalCase)',
entry: {
folderPath: './tools/templates/react/redux-store/__store__Action.ts',
},
stringReplacers: ['__store__', '__model__'],
dynamicReplacers: [
{ slot: '__version__', slotValue: config.version },
{ slot: '__description__', slotValue: config.description },
],
output: {
path: './src/stores/__store__/__store__(lowerCase)/__store__(pascalCase)Action.ts',
pathAndFileNameDefaultCase: '(kebabCase)',
},
onComplete: (results) => {
console.log(`results`, results);
},
},
]);
As outlined in the examples
folder, I prefer to create a tools
folder and place generate.js
w/ templates
files in there. Additionally, I'll add a script task ("generate": "node ./tools/generate.js"
) to my package.json
file for convienent running of the generator using npm run generate
or yarn generate
.
┣━ package.json
┣━ src
┗━ tools/
┣━ generate.js
┗━ templates/
┣━ SomeFile.js
┗━ __name__(pascalCase)Action.ts
API
The generateTemplateFiles
function takes an array of IConfigItem
items.
IConfigItem
-
option
- The name of the option to choose when asked. -
defaultCase
- The default Case Converters to use with the Replacer Slots in the template files. Default is(noCase)
. -
entry.folderPath
- Path to a folder of files or a single template file. -
stringReplacers
- An array of Replacer Slots used to replace content in the designatedentry.folderPath
. -
dynamicReplacers
- (Optional) An array of IReplacer used to replace content in the designatedentry.folderPath
. -
output.path
- The desired output path for generated files. Case Converters and Replacer Slots can be used to make the path somewhat dynamic. -
output.pathAndFileNameDefaultCase
- The Case Converters to use for the file path and file name(s). -
output.overwrite
- (Optional) Whentrue
it will overwrite any files that are named the same. -
onComplete
- (Optional) Takes a callback function that is called once the file(s) have been outputted. A IResults object will be passed to the callback.
Example
{
option: 'Create Redux Store',
defaultCase: '(pascalCase)',
entry: {
folderPath: './tools/templates/react/redux-store/',
},
stringReplacers: ['__store__', { question: 'Insert model name', slot: '__model__' }],
dynamicReplacers: [
{slot:'__version__', slotValue: config.version},
{slot:'__description__', slotValue: config.description}
],
output: {
path: './src/stores/__store__(lowerCase)',
pathAndFileNameDefaultCase: '(kebabCase)',
},
onComplete: (results) => {
console.log(results);
},
},
IResults
Below is an example of what you receive from the onComplete
callback. It has the output path, list of files created and the Replacer Slots with the value entered.
-
output.path
- The file(s) output path -
output.files
- List of files created -
stringReplacers
- List of Replacer Slots; name and values entered during the setup process
Example data you would get from the onComplete callback
{
output: {
path: './src/stores/some-thing',
files: [
'./src/stores/some-thing/SomeThingModule.ts',
'./src/stores/some-thing/SomeThingModuleAction.ts',
'./src/stores/some-thing/SomeThingModuleGetter.ts',
'./src/stores/some-thing/SomeThingModuleMutation.ts',
'./src/stores/some-thing/SomeThingService.ts',
'./src/stores/some-thing/models/actions/ISomeThingState.ts',
'./src/stores/some-thing/models/actions/OtherThingResponseModel.ts'
]
},
stringReplacers: [
{
slot: '__store__',
slotValue: 'some thing'
},
{
slot: '__model__',
slotValue: 'other thing'
}
]
}
Replacer Slots or IReplacerSlotQuestion
Replacer Slots are unique string value(s) to be replaced by the generator. An array of string values and/or IReplacerSlotQuestion
objects can be used.
stringReplacers: ['__store__', { question: 'Insert model name', slot: '__model__' }];
Replacer slot can be any string value you want to use. You can use something like this in your template files and/or in the file path names.
~replacerSlot~
{{something else}}
__AnythingYouWant__
IReplacerSlotQuestion
Below is an example of a IReplacerSlotQuestion
{question: 'Insert model name', slot: '__model__'}
-
question
- The question to ask the use what value should be used for the replacerslot
-
slot
- The string value for the Replacer Slots
Dynamic Replacer Slots
If you have data that is dynamically generated, or you have hard coded values you can use the dynamicReplacers
:
dynamicReplacers: [
{slot:'__description__', slotValue: config.description}
],
Case Converters
Case Converters transform the string value entered upon use of the generator.
Example
- In the generator template
__replacerSlot__
is appended by the(pascalCase)
converter such as__replacerSlot__(pascalCase)
. - When the generator is ran, the string
"product reducer"
is provided for__replacerSlot__
. - As a result, the converter will produce
ProductReducer
.
Here is the string Lives down BY the River
with each of the converters:
// If you typed in 'Lives down BY the River' for the a Replacer Slot named '__replacerSlot__' and
// used one of the optional Case Converters you would get the following:
__replacerSlot__(noCase) // Lives down BY the River
__replacerSlot__(camelCase) // livesDownByTheRiver
__replacerSlot__(constantCase) // LIVES_DOWN_BY_THE_RIVER
__replacerSlot__(dotCase) // lives.down.by.the.river
__replacerSlot__(kebabCase) // lives-down-by-the-river
__replacerSlot__(lowerCase) // livesdownbytheriver
__replacerSlot__(pascalCase) // LivesDownByTheRiver
__replacerSlot__(pathCase) // lives/down/by/the/river
__replacerSlot__(sentenceCase) // Lives down by the river
__replacerSlot__(snakeCase) // lives_down_by_the_river
__replacerSlot__(titleCase) // Lives Down By The River
// Note: you can set a 'defaultCase' converter in IConfigItem so all
// Replacer Slots without a Case Converter will be transformed the same way.
__replacerSlot__; // LivesDownByTheRiver
You may also specify the case using an underscores-only syntax e.g. PascalCase__
:
__replacerSlot__NoCase__ // Lives down BY the River
__replacerSlot__CamelCase__ // livesDownByTheRiver
__replacerSlot__ConstantCase__ // LIVES_DOWN_BY_THE_RIVER
__replacerSlot__DotCase__ // lives.down.by.the.river
__replacerSlot__KebabCase__ // lives-down-by-the-river
__replacerSlot__LowerCase__ // livesdownbytheriver
__replacerSlot__PascalCase__ // LivesDownByTheRiver
__replacerSlot__PathCase__ // lives/down/by/the/river
__replacerSlot__SentenceCase__ // Lives down by the river
__replacerSlot__SnakeCase__ // lives_down_by_the_river
__replacerSlot__TitleCase__ // Lives Down By The River
Take your Replacer Slots __replacerSlot__
, the Case Converters PascalCase__
and combine them together to make __replacerSlot__PascalCase__
.
One Rule: no spaces between the Replacer Slots and Case Converters. If there is a space, Case Converters will not work.
✅ __name__(camelCase)
OR__name__CamelCase__
❌ __name__ (camelCase)
OR__name__ CamelCase__
Batch Usage
You can use generate-template-files-v2
to generate your template files programmatically, without any interactive prompts. This mode does not support stringReplacers
.
The following example will generate the component, unit tests, and the SCSS module in one do.
// generateTemplateFile.js
const { generateTemplateFiles } = require('generate-template-files-v2');
generateTemplateFiles([
// Angular
{
option: 'Angular Ngrx Store',
defaultCase: '(pascalCase)',
entry: {
folderPath: './tools/templates/angular/ngrx-store/',
},
stringReplacers: ['__name__', {question: 'Insert model name', slot: '__model__'}],
dynamicReplacers: [
{slot: '__version__', slotValue: config.version},
{slot: '__description__', slotValue: config.description},
],
output: {
path: './src/app/stores/__name__(lowerCase)',
pathAndFileNameDefaultCase: '(kebabCase)',
overwrite: false,
},
onComplete: async (results) => {
// console.log(`results`, results);
},
},
// Vue
{
option: 'Vue Vuex Store',
defaultCase: '(pascalCase)',
entry: {
folderPath: './tools/templates/vue/vuex-store/',
},
stringReplacers: ['__store__', '__model__'],
dynamicReplacers: [
{
slot:"__store1__custom__",
newSlot:({__store__})=>{
return `${__store__} from other string replacer`
}
},
{
slot:"__store2_from_other_custom1",
newSlot:({__store1__custom__})=>{
return __store1__custom__+ " and custom dynamic replacer"
}
}
],
output: {
path: './src/stores/__store__(kebabCase)',
pathAndFileNameDefaultCase: '(pascalCase)',
},
onComplete: async (results) => {
console.log(`results`, results);
await importVuexStore(results);
},
},
// React
{
option: 'React Redux Store',
defaultCase: '(pascalCase)',
entry: {
folderPath: './tools/templates/react/redux-store/',
},
stringReplacers: ['__store__', '__model__'],
output: {
path: './src/stores/__store__(kebabCase)',
pathAndFileNameDefaultCase: '(pascalCase)',
overwrite: true,
},
onComplete: (results) => {
console.log(`results`, results);
},
},
])
;
/*
* NOTE: there is many ways you can do this. This is just an example on how you might approach it.
*/
async function importVuexStore(results) {
const files = results.output.files;
const fullPaths = files
.map((folderPath) => folderPath.replace('src/', '')) // remove 'src' from path
.map((path) => `import ${filename(path)} from '${path}'`) // create import statement
.join('\n'); // put all imports on there own line
try {
await insertLine('src/import-test.ts').append(fullPaths);
} catch (error) {
console.log(``, error);
}
}
Command Line Usage
You can use generate-template-files-v2
with the command line to generate your template files.
// generate.js
const { generateTemplateFilesCommandLine } = require('generate-template-files-v2');
generateTemplateFilesCommandLine([
{
option: 'Create Reduce Action',
defaultCase: '(pascalCase)',
entry: {
folderPath: './tools/templates/react/redux-store/__store__Action.ts',
},
stringReplacers: ['__store__', '__model__'],
dynamicReplacers: [
{ slot: '__version__', slotValue: config.version },
{ slot: '__description__', slotValue: config.description },
],
output: {
path: './src/stores/__store__/__store__(lowerCase)/__store__(pascalCase)Action.ts',
pathAndFileNameDefaultCase: '(kebabCase)',
},
},
]);
Minimum Options
node ./tools/generate.js create-reduce-action __store__=some-name __model__=some-other-name
All Options
node ./tools/generate.js create-reduce-action __store__=some-name __model__=some-other-name --outputpath=./src/here --overwrite
Command LIne Script Overview
-
node ./tools/generate.js
- Runs thegenerate-template-files-v2
library -
create-reduce-action
- The template name; It uses the same option name in the IConfigItem but converts all options names to kebab-case. For exampleoption: 'Create Reduce Action'
will be converted tocreate-reduce-action
when using the command line -
__store__=some-name
- Are Replacer Slots and will be converted to{ slot: "__store__", slotValue: "some-name" }
-
--outputpath=./src/here
- Will override theoutput.path
in the IConfigItem -
--overwrite
- Will overwrite files if the files already exists