From ec2e7d0e64c114e8cf039124d72b3cd1db515f38 Mon Sep 17 00:00:00 2001 From: hosein Date: Thu, 31 Oct 2019 23:58:32 +0330 Subject: [PATCH 1/6] feat: add multiple custom templates implementation #1 --- src/bin.js | 3 ++- src/index.js | 36 +++++++++++++++++++++++++++++++++--- src/logger.js | 7 +++++++ src/utils.js | 7 +++++++ 4 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 src/utils.js diff --git a/src/bin.js b/src/bin.js index a4fdf44..1888532 100755 --- a/src/bin.js +++ b/src/bin.js @@ -5,7 +5,8 @@ const Gue = require('.'); program .arguments(' [direcroty]') - .option('-u, --unit', 'create unit test of the component too'); + .option('-u, --unit', 'create unit test of the component too') + .option('-t, --template ', 'define which template to use'); const params = program.parse(process.argv); diff --git a/src/index.js b/src/index.js index a9ed6d6..100f9ba 100644 --- a/src/index.js +++ b/src/index.js @@ -5,6 +5,7 @@ const logger = require('./logger'); const configFile = require('./config'); const cmpTemplate = require('./templates/sample-component'); const unitTemplate = require('./templates/sample-unit'); +const {isObject, findDefault, isObjectEmpty} = require('./utils'); class Gue { constructor(componentName, distDir, options) { @@ -47,9 +48,32 @@ class Gue { } formatComponent() { - const data = this.componentSource ? - fs.readFileSync(this.componentSource, {encoding: 'utf8'}) : - cmpTemplate; + let data; + if (this.options.template) { + + this.checkConfigExist(); + if (!isObject(this.componentSource)) { + logger.fatal('When using -t your componentSource must be an object'); + } + if (isObject(this.componentSource)) { + if (!(this.options.template in this.componentSource)) { + logger.fatal(`There is no "${this.options.template}" template in componentSource in gue config file`); + } + data = fs.readFileSync(this.componentSource[this.options.template], {encoding: 'utf8'}); + } + + } else if (isObject(this.componentSource)) { + + const defaultTemplate = findDefault(this.componentSource); + if (!defaultTemplate) { + logger.fatal('No default component defined in componentSource object') + } + data = fs.readFileSync(this.componentSource[defaultTemplate], {encoding: 'utf8'}); + } else { + data = this.componentSource ? + fs.readFileSync(this.componentSource, {encoding: 'utf8'}) : + cmpTemplate; + } const rex = /<%NAME%>/g; return data.replace(rex, this.componentName); } @@ -98,6 +122,12 @@ class Gue { return true; } + checkConfigExist() { + if (isObjectEmpty(configFile)) { + logger.fatal('Could not find any config file in root directory'); + } + } + run() { this.generate(); } diff --git a/src/logger.js b/src/logger.js index d190324..665d9e2 100644 --- a/src/logger.js +++ b/src/logger.js @@ -40,6 +40,13 @@ class Logger { ); console.log(e); } + + fatal(e) { + console.log( + box(`⚠️ ${chalk.red(e)}`, {padding: 1}) + ); + throw new Error(e); + } } module.exports = new Logger(); diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..12cd55e --- /dev/null +++ b/src/utils.js @@ -0,0 +1,7 @@ +exports.isObject = obj => obj != null && obj.constructor.name === 'Object'; + +exports.isObjectEmpty = obj => !Object.keys(obj).length; + +exports.findDefault = obj => Object.keys(obj).find(i => i.split(':').length > 1); + + From 7f08ed87d9e9bde36a9b3cd3cfc5d1e58594a0cf Mon Sep 17 00:00:00 2001 From: "sdf@" Date: Thu, 31 Oct 2019 23:59:20 +0330 Subject: [PATCH 2/6] lint --- src/index.js | 9 +++++---- src/utils.js | 3 +-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index 100f9ba..61088a0 100644 --- a/src/index.js +++ b/src/index.js @@ -50,30 +50,31 @@ class Gue { formatComponent() { let data; if (this.options.template) { - this.checkConfigExist(); if (!isObject(this.componentSource)) { logger.fatal('When using -t your componentSource must be an object'); } + if (isObject(this.componentSource)) { if (!(this.options.template in this.componentSource)) { logger.fatal(`There is no "${this.options.template}" template in componentSource in gue config file`); } + data = fs.readFileSync(this.componentSource[this.options.template], {encoding: 'utf8'}); } - } else if (isObject(this.componentSource)) { - const defaultTemplate = findDefault(this.componentSource); if (!defaultTemplate) { - logger.fatal('No default component defined in componentSource object') + logger.fatal('No default component defined in componentSource object'); } + data = fs.readFileSync(this.componentSource[defaultTemplate], {encoding: 'utf8'}); } else { data = this.componentSource ? fs.readFileSync(this.componentSource, {encoding: 'utf8'}) : cmpTemplate; } + const rex = /<%NAME%>/g; return data.replace(rex, this.componentName); } diff --git a/src/utils.js b/src/utils.js index 12cd55e..acf3738 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,7 +1,6 @@ -exports.isObject = obj => obj != null && obj.constructor.name === 'Object'; +exports.isObject = obj => obj !== null && obj.constructor.name === 'Object'; exports.isObjectEmpty = obj => !Object.keys(obj).length; exports.findDefault = obj => Object.keys(obj).find(i => i.split(':').length > 1); - From 8c0fc10d53aeb28a09311e3bd030ffac112408e6 Mon Sep 17 00:00:00 2001 From: "sdf@" Date: Thu, 31 Oct 2019 23:59:55 +0330 Subject: [PATCH 3/6] add test --- test/cleanup.js | 3 +- test/conditions/default-template/black.vue | 27 ++++++++ test/conditions/default-template/gue.json | 6 ++ test/conditions/default-template/white.vue | 27 ++++++++ test/conditions/right-template/gue.json | 6 ++ test/conditions/right-template/right.vue | 27 ++++++++ test/conditions/simple-config/gue.json | 3 + test/conditions/wrong-template/gue.json | 6 ++ test/index.js | 78 ++++++++++++++++++++++ 9 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 test/conditions/default-template/black.vue create mode 100644 test/conditions/default-template/gue.json create mode 100644 test/conditions/default-template/white.vue create mode 100644 test/conditions/right-template/gue.json create mode 100644 test/conditions/right-template/right.vue create mode 100644 test/conditions/simple-config/gue.json create mode 100644 test/conditions/wrong-template/gue.json diff --git a/test/cleanup.js b/test/cleanup.js index 2930602..a797490 100644 --- a/test/cleanup.js +++ b/test/cleanup.js @@ -11,7 +11,8 @@ for (const file of files) { '.nyc_output', 'cleanup.js', 'gue.json', - 'templates' + 'templates', + 'conditions' ]; if (doNotRemove.includes(file)) { continue; diff --git a/test/conditions/default-template/black.vue b/test/conditions/default-template/black.vue new file mode 100644 index 0000000..03e367c --- /dev/null +++ b/test/conditions/default-template/black.vue @@ -0,0 +1,27 @@ + + +export default { + name: "<%NAME%>", + props: [], + mounted() { + + }, + data() { + return { + test: null + } + }, + methods: { + + }, + computed: { + + } +} + + diff --git a/test/conditions/default-template/gue.json b/test/conditions/default-template/gue.json new file mode 100644 index 0000000..40bee16 --- /dev/null +++ b/test/conditions/default-template/gue.json @@ -0,0 +1,6 @@ +{ + "componentSource": { + "white" : "./white.vue", + "black:default" : "./black.vue" + } +} \ No newline at end of file diff --git a/test/conditions/default-template/white.vue b/test/conditions/default-template/white.vue new file mode 100644 index 0000000..c762af2 --- /dev/null +++ b/test/conditions/default-template/white.vue @@ -0,0 +1,27 @@ + + +export default { + name: "<%NAME%>", + props: [], + mounted() { + + }, + data() { + return { + test: null + } + }, + methods: { + + }, + computed: { + + } +} + + diff --git a/test/conditions/right-template/gue.json b/test/conditions/right-template/gue.json new file mode 100644 index 0000000..6847b62 --- /dev/null +++ b/test/conditions/right-template/gue.json @@ -0,0 +1,6 @@ +{ + "componentSource": { + "some" : "./some/sdf.vue", + "right" : "./right.vue" + } +} \ No newline at end of file diff --git a/test/conditions/right-template/right.vue b/test/conditions/right-template/right.vue new file mode 100644 index 0000000..ce12f9a --- /dev/null +++ b/test/conditions/right-template/right.vue @@ -0,0 +1,27 @@ + + +export default { + name: "<%NAME%>", + props: [], + mounted() { + + }, + data() { + return { + test: null + } + }, + methods: { + + }, + computed: { + + } +} + + \ No newline at end of file diff --git a/test/conditions/simple-config/gue.json b/test/conditions/simple-config/gue.json new file mode 100644 index 0000000..e7adffd --- /dev/null +++ b/test/conditions/simple-config/gue.json @@ -0,0 +1,3 @@ +{ + "componentRoot": "./somewhereYouLike" +} \ No newline at end of file diff --git a/test/conditions/wrong-template/gue.json b/test/conditions/wrong-template/gue.json new file mode 100644 index 0000000..2bcffd7 --- /dev/null +++ b/test/conditions/wrong-template/gue.json @@ -0,0 +1,6 @@ +{ + "componentSource": { + "some" : "./sefsfsf", + "another" : "./nowhere" + } +} \ No newline at end of file diff --git a/test/index.js b/test/index.js index e19d960..dfaf724 100644 --- a/test/index.js +++ b/test/index.js @@ -1,9 +1,12 @@ const fs = require('fs'); +const path = require('path'); const tap = require('tap'); const Gue = require('../src'); const defaultVueTemplate = require('../src/templates/sample-component'); const defaultUnitTemplate = require('../src/templates/sample-unit'); +const initCwd = process.cwd(); + function getContent(dir) { return fs.readFileSync(dir, {encoding: 'utf8'}); } @@ -22,6 +25,20 @@ function formatTest(name, unitPath, tmp) { return data.replace(rexPath, unitPath); } +/* +* Since we want to test diffrent config files and config files are +* recognized automatically from root dir, we need to change cwd +* every time. And since config file gets resolved only once when +* you require the module(here ./src module) we need to clear cache of +* require so when we change the directory, the module ./src/config gets +* evaluated again and it recognizes config file in new cwd. +*/ +function cleanCacheAndChageCwd(dir) { + delete require.cache[require.resolve('../src/config')]; + delete require.cache[require.resolve('../src')]; + process.chdir(path.resolve(initCwd, dir)); +} + const customVueTemplate = getContent('./templates/myVueTmp.vue'); const customUnitTemplate = getContent('./templates/myUnitTmp.js'); @@ -120,3 +137,64 @@ tap.test('Custom root dir for test && custom test file', t => { t.plan(1); t.equal(getContent(testDir), formatTest(name, '../src/components/ninthTest.vue', customUnitTemplate)); }); + +tap.test('Should throw when there is -t but componentSource is not object', t => { + cleanCacheAndChageCwd('./conditions/simple-config'); + const Gue = require('../src'); + const name = 'tenthTest'; + const gue = new Gue(name, null, {template: 'foo'}); + t.plan(1); + // TODO: check the exact error + t.throw(() => { + gue.generate(); + }); +}); + +tap.test('Should throw when template name is not in config', t => { + cleanCacheAndChageCwd('./conditions/wrong-template'); + const Gue = require('../src'); + const name = 'eleventhTest'; + const gue = new Gue(name, null, {template: 'foo'}); + t.plan(1); + // TODO: check the exact error + t.throw(() => { + gue.generate(); + }); +}); + +tap.test('Should work with multiple templates and choose right one', t => { + cleanCacheAndChageCwd('./conditions/right-template'); + const Gue = require('../src'); + const name = 'twelvethTest'; + // Note that we are now in: ./conditions/right-template + const dir = './src/components/twelvethTest.vue'; + const cutomTemplateDir = './right.vue'; + const gue = new Gue(name, null, {template: 'right'}); + gue.run(); + t.plan(1); + t.equal(getContent(dir), formatComponent(name, getContent(cutomTemplateDir))); +}); + +tap.test('Should throw when componentSource is object but there is not default template(and -t is not passed of course)', t => { + cleanCacheAndChageCwd('./conditions/right-template'); + const Gue = require('../src'); + const name = 'foo'; + const gue = new Gue(name, null, {}); + t.plan(1); + t.throw(() => { + gue.generate(); + }); +}); + +tap.test('Should recognize default template when there is no -t', t => { + cleanCacheAndChageCwd('./conditions/default-template'); + const Gue = require('../src'); + const name = 'iBlack'; + // Note that we are now in: ./conditions/default-template + const dir = './src/components/iBlack.vue'; + const cutomTemplateDir = './black.vue'; + const gue = new Gue(name, null, {}); + gue.run(); + t.plan(1); + t.equal(getContent(dir), formatComponent(name, getContent(cutomTemplateDir))); +}); From 698568db83e5ff532590b515a8b6bc52fb04dfb4 Mon Sep 17 00:00:00 2001 From: "sdf@" Date: Fri, 1 Nov 2019 00:00:54 +0330 Subject: [PATCH 4/6] docs --- README.md | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f05a8e2..e895edf 100644 --- a/README.md +++ b/README.md @@ -47,20 +47,27 @@ This will generate `footer` component in `./src/components/footer.vue` and also ## Usage General usage is like: ``` -gue [directory] [options] +$ gue --help + Usage: gue [direcroty] [options] + + Options: + -u, --unit create unit test of the component too + -t, --template define which template to use + -h, --help output usage information + ``` * <componentName> is mandatory. * [directory] is optional, and is a relative path. If you have a config file this will be a `subdirectory` of your [componentRoot](#options) If you don't, then this will lead to generation of component in exact `direcroty` -* [options] are optional, only available option is `-u` which will generate test file. +* [options] are optional, available options are `-u` which will generate test file, and `-t` which is used to define which template for components to use. ## Config file Gue accepts a config file to change default settings. In root directory of project make a file `gue.json`, and Gue will automatically recognize and use it. #### Options Here are available options for config file: * `componentRoot`: root directory which components will be generated in. should be relative path. -* `componentSource`: path to custom component template. +* `componentSource`: path to custom component template. Or an object to define [multiple templates](#using-multiple-custom-templates). * `unitRoot`: directory which test will be generated in. should be a relative path. * `unitSource`: path to custom test file template. @@ -104,3 +111,32 @@ data() { ``` To see other examples look at [templates folder](https://github.com/hosein2398/gue/tree/master/src/templates). +##### Using multiple custom templates +You can use multiple custom templates. So `componentSource` can be object (multiple templates) or a string (single template). Multiple templates can be created like: +``` +{ + "componentSource": { + "component" : "./tmps/component.vue", + "page" : "./tmps/page.vue" + } +} +``` +And when using Gue you have to tell it which component template to use: +``` +gue menu -t component +gue setting ./pages -t page +``` +You can define one of your templates as `default` one, so that you don't have to type `-t` every time. Default component can be specified with `:default` postfix: +``` +{ + "componentSource": { + "component:default" : "./tmps/component.vue", + "page" : "./tmps/page.vue" + } +} +``` +Now if you type any command without `-t`, component template will be used. +``` +gue foo +``` +Will use `component` template to generate foo component. No need of `-t component` \ No newline at end of file From f1e0d9c8a74384ff2157be37438b8b102713135b Mon Sep 17 00:00:00 2001 From: hosein2398 Date: Fri, 1 Nov 2019 00:09:14 +0330 Subject: [PATCH 5/6] doc: some sapce --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e895edf..57e87cd 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ This will generate `footer` component in `./src/components/footer.vue` and also General usage is like: ``` $ gue --help + Usage: gue [direcroty] [options] Options: From fc323280c6c3ddf7a181c43bcc5e38817f8c8a7f Mon Sep 17 00:00:00 2001 From: hosein Date: Fri, 1 Nov 2019 11:31:59 +0330 Subject: [PATCH 6/6] version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aa4d8ae..cc85e15 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-gue", - "version": "0.1.0", + "version": "0.2.0", "description": "Vue component generator", "repository": { "url": "https://github.com/hosein2398/gue",