├── .gitignore ├── bin └── pwc ├── examples ├── full-syntax.yml ├── products.yml └── simple-erp.yml ├── index.js ├── package-lock.json ├── package.json ├── readme.md └── src ├── classes ├── Field.js ├── Model.js ├── Project.js ├── Relationship.js ├── Template.js └── Util.js └── modules ├── commands.js ├── filter.js └── projectParser.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /bin/pwc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var app = require('../index'); 4 | app.init(); -------------------------------------------------------------------------------- /examples/full-syntax.yml: -------------------------------------------------------------------------------- 1 | name: 'Sell System - Full File Syntax' 2 | index: project6 3 | models: 4 | user: 5 | namePlural: users 6 | description: User|Users 7 | onlyModel: true 8 | isRelationship: false 9 | product: 10 | namePlural: products 11 | description: Product|Products 12 | onlyModel: false 13 | isRelationship: false 14 | fields: 15 | name: 16 | type: string 17 | element: text 18 | label: Name 19 | items: '' 20 | validation: 'required|string|max:255' 21 | searchable: true 22 | inList: true 23 | image: 24 | type: image 25 | element: file 26 | label: Image 27 | items: '' 28 | validation: file 29 | searchable: false 30 | inList: false 31 | hasMany: 32 | grid: 33 | alias: grids 34 | element: simple-datagrid 35 | validation: '' 36 | foreignKeyName: product_id 37 | displayField: null 38 | order: 39 | namePlural: orders 40 | description: Order|Orders 41 | onlyModel: false 42 | isRelationship: false 43 | fields: 44 | date: 45 | type: date 46 | element: date 47 | label: Date 48 | items: '' 49 | validation: required|date 50 | searchable: true 51 | inList: true 52 | hasMany: 53 | orderItem: 54 | alias: orderItems 55 | element: simple-datagrid 56 | validation: '' 57 | foreignKeyName: order_id 58 | displayField: null 59 | invoice: 60 | alias: invoices 61 | element: false 62 | validation: required 63 | foreignKeyName: invoice_id 64 | displayField: null 65 | orderItem: 66 | namePlural: orderItems 67 | description: 'Order Item|Order Items' 68 | onlyModel: true 69 | isRelationship: false 70 | fields: 71 | quantity: 72 | type: decimal 73 | element: number 74 | label: Quantity 75 | items: '' 76 | validation: required 77 | searchable: false 78 | inList: true 79 | value: '0' 80 | discount: 81 | type: decimal 82 | element: number 83 | label: Discount 84 | items: '' 85 | validation: required 86 | searchable: false 87 | inList: true 88 | value: '0' 89 | rate: 90 | type: decimal 91 | element: number 92 | label: Rate 93 | items: '' 94 | validation: required 95 | searchable: false 96 | inList: true 97 | value: '0' 98 | belongsTo: 99 | grid: 100 | alias: grid 101 | element: select 102 | validation: '' 103 | foreignKeyName: grid_id 104 | displayField: color 105 | order: 106 | alias: orders 107 | element: false 108 | validation: required 109 | foreignKeyName: order_id 110 | displayField: name 111 | grid: 112 | namePlural: grids 113 | description: Grid|Grids 114 | onlyModel: true 115 | isRelationship: false 116 | fields: 117 | color: 118 | type: string 119 | element: text 120 | label: Color 121 | items: '' 122 | validation: '' 123 | searchable: false 124 | inList: true 125 | value: white 126 | size: 127 | type: string 128 | element: text 129 | label: Size 130 | items: '' 131 | validation: required 132 | searchable: false 133 | inList: true 134 | value: small 135 | name: 136 | type: string 137 | element: text 138 | label: Name 139 | items: '' 140 | validation: required 141 | searchable: false 142 | inList: true 143 | hasMany: 144 | orderItem: 145 | alias: orderItems 146 | element: false 147 | validation: required 148 | foreignKeyName: orderItem_id 149 | displayField: null 150 | belongsTo: 151 | product: 152 | alias: products 153 | element: select 154 | validation: required 155 | foreignKeyName: product_id 156 | displayField: name 157 | invoice: 158 | namePlural: invoices 159 | description: Invoice|Invoices 160 | onlyModel: false 161 | isRelationship: false 162 | fields: 163 | date: 164 | type: date 165 | element: date 166 | label: Date 167 | items: '' 168 | validation: required 169 | searchable: true 170 | inList: true 171 | value: 172 | type: decimal 173 | element: number 174 | label: Value 175 | items: '' 176 | validation: '' 177 | searchable: false 178 | inList: true 179 | default: '0' 180 | value: '0' 181 | belongsTo: 182 | order: 183 | alias: order 184 | element: select 185 | validation: '' 186 | foreignKeyName: order_id 187 | displayField: id 188 | -------------------------------------------------------------------------------- /examples/products.yml: -------------------------------------------------------------------------------- 1 | name: Products 2 | models: 3 | brand: 4 | fields: 5 | name: 6 | label: Name 7 | type: string,128 8 | validation: required 9 | searchable: true 10 | email: 11 | label: E-mail 12 | type: string,128 13 | element: email 14 | validation: required|email 15 | hasMany: 16 | product: 17 | element: false 18 | brandPhone: 19 | alias: phone 20 | element: simple-datagrid 21 | foreignKeyName: owner_id # Só quando for nome diferente, funciona para hasOne, hasMany e belongsTo 22 | 23 | product: 24 | description: Product|Products 25 | fields: 26 | name: 27 | label: Name 28 | type: string,128 29 | element: text 30 | validation: required 31 | searchable: true 32 | type: 33 | label: Tipo 34 | type: enum 35 | items: physical|Físico,digital,undefined|Indefinido 36 | default: physical 37 | validation: required 38 | price: 39 | label: Preço 40 | type: decimal 41 | quantity: 42 | label: Quantidade no Estoque 43 | type: integer 44 | description: 45 | label: Descrição 46 | type: text 47 | date: 48 | label: Data de Cadastro 49 | type: date 50 | inList: false 51 | active: 52 | label: Ativo 53 | type: boolean 54 | default: false 55 | validation: boolean 56 | inList: false 57 | image: # Adiciona automaticamente uma regra de validação 'image' 58 | label: Imagem do Produto 59 | type: image 60 | inList: false 61 | pdf: 62 | label: Folder do Produto (PDF) 63 | type: file 64 | inList: false 65 | mimeTypes: 'application/pdf' # Múltiplos mimes são separados por vírgula 66 | belongsTo: 67 | brand: 68 | element: select 69 | validation: required 70 | 71 | brandPhone: 72 | onlyModel: true # Não é necessário quando isRelationship é true 73 | isRelationship: true 74 | fields: 75 | phone: 76 | label: Telefone 77 | type: string,32 78 | validation: required|min:5 79 | company: 80 | label: Operadora 81 | type: enum 82 | items: oi,vivo,tim,claro,nextel 83 | default: oi 84 | validation: required 85 | belongsTo: 86 | brand: 87 | foreignKeyName: owner_id 88 | validation: required -------------------------------------------------------------------------------- /examples/simple-erp.yml: -------------------------------------------------------------------------------- 1 | name: Teste2 2 | models: 3 | brand: # <- The model name 4 | description: The Brand|The Brands # Optional, default is the name capitalized 5 | fields: 6 | name: # <- The field name 7 | label: Name # Optional, default is the name capitalized 8 | type: string,128 9 | validation: required 10 | searchable: true 11 | email: 12 | label: E-mail # Optional 13 | type: string,128 14 | element: email 15 | validation: required|email 16 | hasMany: 17 | product: # <- The relationship "Related Model" 18 | element: false 19 | brandPhone: 20 | alias: phone 21 | element: simple-datagrid 22 | foreignKeyName: owner_id # Only for custom fk names, works for hasOne, hasMany e belongsTo 23 | 24 | product: 25 | fields: 26 | name: 27 | type: string,128 28 | element: text 29 | validation: required 30 | searchable: true 31 | type: 32 | type: enum 33 | items: physical,digital|Is digital?,undefined # Enum items 34 | default: physical # Database default value 35 | validation: required 36 | belongsTo: 37 | brand: 38 | element: select 39 | validation: required 40 | hasMany: 41 | grid: 42 | element: simple-datagrid 43 | 44 | client: 45 | fields: 46 | name: 47 | type: string,128 48 | element: text 49 | validation: required 50 | searchable: true 51 | hasMany: 52 | order: 53 | element: false 54 | 55 | order: 56 | fields: 57 | name: 58 | type: date 59 | validation: required|date 60 | searchable: true 61 | status: 62 | type: enum 63 | items: opened,closed 64 | default: opened 65 | validation: required 66 | belongsTo: 67 | client: 68 | validation: required 69 | hasMany: 70 | orderItem: 71 | alias: item 72 | element: simple-datagrid 73 | 74 | brandPhone: 75 | onlyModel: true 76 | isRelationship: true 77 | fields: 78 | phone: 79 | type: string,32 80 | validation: required|min:5 81 | company: 82 | type: enum 83 | items: oi,vivo,tim,claro,nextel 84 | default: oi 85 | validation: required 86 | belongsTo: 87 | brand: 88 | foreignKeyName: owner_id 89 | element: false 90 | validation: required 91 | 92 | grid: 93 | description: Product Grid|Product Grids 94 | onlyModel: true 95 | isRelationship: true 96 | fields: 97 | description: 98 | type: string,128 99 | validation: required|max:128 100 | barcode: 101 | type: string,128 102 | validation: required|max:128 103 | measure: 104 | type: enum 105 | items: unity|Unidade, meters|Metro 106 | default: unity 107 | validation: required 108 | size: 109 | type: string,128 110 | validation: required|max:128 111 | color: 112 | type: string,128 113 | validation: required|max:128 114 | price: 115 | type: decimal 116 | validation: required 117 | belongsTo: 118 | product: 119 | element: false 120 | validation: required 121 | 122 | orderItem: 123 | description: Order Item|Order Items 124 | onlyModel: true 125 | isRelationship: true 126 | fields: 127 | quantity: 128 | type: decimal 129 | validation: required 130 | discount: 131 | type: decimal 132 | validation: required 133 | rate: 134 | type: decimal 135 | validation: required 136 | belongsTo: 137 | order: 138 | element: false 139 | validation: required 140 | grid: 141 | displayField: description # Specifies the field that will appear on the Select Box 142 | validation: required -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const 4 | // Classes 5 | Util = require('./src/classes/Util'), 6 | 7 | // Modules 8 | colors = require('colors'), 9 | commands = require('./src/modules/commands'), 10 | projectParser = require('./src/modules/projectParser'), 11 | 12 | // Shim 13 | objectEntries = require('object.entries'), 14 | objectValues = require('object.values'); 15 | 16 | if (!Object.values) { 17 | objectValues.shim(); 18 | } 19 | 20 | if (!Object.entries) { 21 | objectEntries.shim(); 22 | } 23 | 24 | module.exports = { 25 | init: function() { 26 | try{ 27 | commands.init(); 28 | 29 | const 30 | util = new Util, 31 | Plug = require(commands.getPlug()), 32 | plugStarter = new Plug(util); 33 | 34 | if(commands.getGenerator() == 'project') { 35 | 36 | let parsedProject = projectParser.init(commands); 37 | plugStarter.initProject(parsedProject); 38 | 39 | } else if(commands.getGenerator() == 'model') { 40 | console.log('Model Generator is not supported at this moment'.yellow); 41 | } else 42 | throw 'Please, specify a valid generator in the --generate,-g argument (project|module)'; 43 | 44 | }catch(e){ 45 | let errorMessage = 'ERROR: ' + e; 46 | console.error(errorMessage.red); 47 | } 48 | } 49 | }; -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pwc-code-generator", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "acorn": { 8 | "version": "5.5.3", 9 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", 10 | "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==" 11 | }, 12 | "acorn-node": { 13 | "version": "1.3.0", 14 | "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.3.0.tgz", 15 | "integrity": "sha512-efP54n3d1aLfjL2UMdaXa6DsswwzJeI5rqhbFvXMrKiJ6eJFpf+7R0zN7t8IC+XKn2YOAFAv6xbBNgHUkoHWLw==", 16 | "requires": { 17 | "acorn": "5.5.3", 18 | "xtend": "4.0.1" 19 | } 20 | }, 21 | "argparse": { 22 | "version": "1.0.10", 23 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 24 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 25 | "requires": { 26 | "sprintf-js": "1.0.3" 27 | } 28 | }, 29 | "argv-tools": { 30 | "version": "0.1.1", 31 | "resolved": "https://registry.npmjs.org/argv-tools/-/argv-tools-0.1.1.tgz", 32 | "integrity": "sha512-Cc0dBvx4dvrjjKpyDA6w8RlNAw8Su30NvZbWl/Tv9ZALEVlLVkWQiHMi84Q0xNfpVuSaiQbYkdmWK8g1PLGhKw==", 33 | "requires": { 34 | "array-back": "2.0.0", 35 | "find-replace": "2.0.1" 36 | } 37 | }, 38 | "array-back": { 39 | "version": "2.0.0", 40 | "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", 41 | "integrity": "sha1-aHdHHVHsycm/phNvtsfV/ml0gCI=", 42 | "requires": { 43 | "typical": "2.6.1" 44 | } 45 | }, 46 | "balanced-match": { 47 | "version": "1.0.0", 48 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 49 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 50 | }, 51 | "brace-expansion": { 52 | "version": "1.1.11", 53 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 54 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 55 | "requires": { 56 | "balanced-match": "1.0.0", 57 | "concat-map": "0.0.1" 58 | } 59 | }, 60 | "camel-case": { 61 | "version": "3.0.0", 62 | "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", 63 | "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", 64 | "requires": { 65 | "no-case": "2.3.2", 66 | "upper-case": "1.1.3" 67 | } 68 | }, 69 | "change-case": { 70 | "version": "3.0.2", 71 | "resolved": "https://registry.npmjs.org/change-case/-/change-case-3.0.2.tgz", 72 | "integrity": "sha512-Mww+SLF6MZ0U6kdg11algyKd5BARbyM4TbFBepwowYSR5ClfQGCGtxNXgykpN0uF/bstWeaGDT4JWaDh8zWAHA==", 73 | "requires": { 74 | "camel-case": "3.0.0", 75 | "constant-case": "2.0.0", 76 | "dot-case": "2.1.1", 77 | "header-case": "1.0.1", 78 | "is-lower-case": "1.1.3", 79 | "is-upper-case": "1.1.2", 80 | "lower-case": "1.1.4", 81 | "lower-case-first": "1.0.2", 82 | "no-case": "2.3.2", 83 | "param-case": "2.1.1", 84 | "pascal-case": "2.0.1", 85 | "path-case": "2.1.1", 86 | "sentence-case": "2.1.1", 87 | "snake-case": "2.1.0", 88 | "swap-case": "1.1.2", 89 | "title-case": "2.1.1", 90 | "upper-case": "1.1.3", 91 | "upper-case-first": "1.1.2" 92 | } 93 | }, 94 | "colors": { 95 | "version": "1.2.1", 96 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.1.tgz", 97 | "integrity": "sha512-s8+wktIuDSLffCywiwSxQOMqtPxML11a/dtHE17tMn4B1MSWw/C22EKf7M2KGUBcDaVFEGT+S8N02geDXeuNKg==" 98 | }, 99 | "command-line-args": { 100 | "version": "5.0.2", 101 | "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.0.2.tgz", 102 | "integrity": "sha512-/qPcbL8zpqg53x4rAaqMFlRV4opN3pbla7I7k9x8kyOBMQoGT6WltjN6sXZuxOXw6DgdK7Ad+ijYS5gjcr7vlA==", 103 | "requires": { 104 | "argv-tools": "0.1.1", 105 | "array-back": "2.0.0", 106 | "find-replace": "2.0.1", 107 | "lodash.camelcase": "4.3.0", 108 | "typical": "2.6.1" 109 | } 110 | }, 111 | "concat-map": { 112 | "version": "0.0.1", 113 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 114 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 115 | }, 116 | "constant-case": { 117 | "version": "2.0.0", 118 | "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-2.0.0.tgz", 119 | "integrity": "sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY=", 120 | "requires": { 121 | "snake-case": "2.1.0", 122 | "upper-case": "1.1.3" 123 | } 124 | }, 125 | "define-properties": { 126 | "version": "1.1.2", 127 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", 128 | "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", 129 | "requires": { 130 | "foreach": "2.0.5", 131 | "object-keys": "1.0.11" 132 | } 133 | }, 134 | "dot-case": { 135 | "version": "2.1.1", 136 | "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.1.tgz", 137 | "integrity": "sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4=", 138 | "requires": { 139 | "no-case": "2.3.2" 140 | } 141 | }, 142 | "es-abstract": { 143 | "version": "1.10.0", 144 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.10.0.tgz", 145 | "integrity": "sha1-Hss2wZeEKgDY7kwt/YZGu5fWCGQ=", 146 | "requires": { 147 | "es-to-primitive": "1.1.1", 148 | "function-bind": "1.1.1", 149 | "has": "1.0.1", 150 | "is-callable": "1.1.3", 151 | "is-regex": "1.0.4" 152 | } 153 | }, 154 | "es-to-primitive": { 155 | "version": "1.1.1", 156 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", 157 | "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", 158 | "requires": { 159 | "is-callable": "1.1.3", 160 | "is-date-object": "1.0.1", 161 | "is-symbol": "1.0.1" 162 | } 163 | }, 164 | "esprima": { 165 | "version": "4.0.0", 166 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", 167 | "integrity": "sha1-RJnt3NERDgshi6zy+n9/WfVcqAQ=" 168 | }, 169 | "find-replace": { 170 | "version": "2.0.1", 171 | "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-2.0.1.tgz", 172 | "integrity": "sha512-LzDo3Fpa30FLIBsh6DCDnMN1KW2g4QKkqKmejlImgWY67dDFPX/x9Kh/op/GK522DchQXEvDi/wD48HKW49XOQ==", 173 | "requires": { 174 | "array-back": "2.0.0", 175 | "test-value": "3.0.0" 176 | } 177 | }, 178 | "foreach": { 179 | "version": "2.0.5", 180 | "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", 181 | "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" 182 | }, 183 | "fs.realpath": { 184 | "version": "1.0.0", 185 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 186 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 187 | }, 188 | "function-bind": { 189 | "version": "1.1.1", 190 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 191 | "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=" 192 | }, 193 | "glob": { 194 | "version": "7.1.2", 195 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 196 | "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", 197 | "requires": { 198 | "fs.realpath": "1.0.0", 199 | "inflight": "1.0.6", 200 | "inherits": "2.0.3", 201 | "minimatch": "3.0.4", 202 | "once": "1.4.0", 203 | "path-is-absolute": "1.0.1" 204 | } 205 | }, 206 | "has": { 207 | "version": "1.0.1", 208 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", 209 | "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", 210 | "requires": { 211 | "function-bind": "1.1.1" 212 | } 213 | }, 214 | "header-case": { 215 | "version": "1.0.1", 216 | "resolved": "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz", 217 | "integrity": "sha1-lTWXMZfBRLCWE81l0xfvGZY70C0=", 218 | "requires": { 219 | "no-case": "2.3.2", 220 | "upper-case": "1.1.3" 221 | } 222 | }, 223 | "inflight": { 224 | "version": "1.0.6", 225 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 226 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 227 | "requires": { 228 | "once": "1.4.0", 229 | "wrappy": "1.0.2" 230 | } 231 | }, 232 | "inherits": { 233 | "version": "2.0.3", 234 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 235 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 236 | }, 237 | "interpret": { 238 | "version": "1.1.0", 239 | "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", 240 | "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=" 241 | }, 242 | "is-callable": { 243 | "version": "1.1.3", 244 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", 245 | "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=" 246 | }, 247 | "is-date-object": { 248 | "version": "1.0.1", 249 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", 250 | "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" 251 | }, 252 | "is-lower-case": { 253 | "version": "1.1.3", 254 | "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", 255 | "integrity": "sha1-fhR75HaNxGbbO/shzGCzHmrWk5M=", 256 | "requires": { 257 | "lower-case": "1.1.4" 258 | } 259 | }, 260 | "is-regex": { 261 | "version": "1.0.4", 262 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", 263 | "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", 264 | "requires": { 265 | "has": "1.0.1" 266 | } 267 | }, 268 | "is-symbol": { 269 | "version": "1.0.1", 270 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", 271 | "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=" 272 | }, 273 | "is-upper-case": { 274 | "version": "1.1.2", 275 | "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", 276 | "integrity": "sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8=", 277 | "requires": { 278 | "upper-case": "1.1.3" 279 | } 280 | }, 281 | "js-yaml": { 282 | "version": "3.11.0", 283 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", 284 | "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", 285 | "requires": { 286 | "argparse": "1.0.10", 287 | "esprima": "4.0.0" 288 | } 289 | }, 290 | "lodash.camelcase": { 291 | "version": "4.3.0", 292 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", 293 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" 294 | }, 295 | "lower-case": { 296 | "version": "1.1.4", 297 | "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", 298 | "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" 299 | }, 300 | "lower-case-first": { 301 | "version": "1.0.2", 302 | "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz", 303 | "integrity": "sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E=", 304 | "requires": { 305 | "lower-case": "1.1.4" 306 | } 307 | }, 308 | "minimatch": { 309 | "version": "3.0.4", 310 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 311 | "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", 312 | "requires": { 313 | "brace-expansion": "1.1.11" 314 | } 315 | }, 316 | "ncp": { 317 | "version": "2.0.0", 318 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", 319 | "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=" 320 | }, 321 | "no-case": { 322 | "version": "2.3.2", 323 | "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", 324 | "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", 325 | "requires": { 326 | "lower-case": "1.1.4" 327 | } 328 | }, 329 | "object-keys": { 330 | "version": "1.0.11", 331 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", 332 | "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" 333 | }, 334 | "object.entries": { 335 | "version": "1.0.4", 336 | "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.0.4.tgz", 337 | "integrity": "sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8=", 338 | "requires": { 339 | "define-properties": "1.1.2", 340 | "es-abstract": "1.10.0", 341 | "function-bind": "1.1.1", 342 | "has": "1.0.1" 343 | } 344 | }, 345 | "object.values": { 346 | "version": "1.0.4", 347 | "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.0.4.tgz", 348 | "integrity": "sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo=", 349 | "requires": { 350 | "define-properties": "1.1.2", 351 | "es-abstract": "1.10.0", 352 | "function-bind": "1.1.1", 353 | "has": "1.0.1" 354 | } 355 | }, 356 | "once": { 357 | "version": "1.4.0", 358 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 359 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 360 | "requires": { 361 | "wrappy": "1.0.2" 362 | } 363 | }, 364 | "param-case": { 365 | "version": "2.1.1", 366 | "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", 367 | "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", 368 | "requires": { 369 | "no-case": "2.3.2" 370 | } 371 | }, 372 | "pascal-case": { 373 | "version": "2.0.1", 374 | "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.1.tgz", 375 | "integrity": "sha1-LVeNNFX2YNpl7KGO+VtODekSdh4=", 376 | "requires": { 377 | "camel-case": "3.0.0", 378 | "upper-case-first": "1.1.2" 379 | } 380 | }, 381 | "path-case": { 382 | "version": "2.1.1", 383 | "resolved": "https://registry.npmjs.org/path-case/-/path-case-2.1.1.tgz", 384 | "integrity": "sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU=", 385 | "requires": { 386 | "no-case": "2.3.2" 387 | } 388 | }, 389 | "path-is-absolute": { 390 | "version": "1.0.1", 391 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 392 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 393 | }, 394 | "path-parse": { 395 | "version": "1.0.5", 396 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", 397 | "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" 398 | }, 399 | "pluralize": { 400 | "version": "7.0.0", 401 | "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", 402 | "integrity": "sha1-KYuJ34uTsCIdv0Ia0rGx6iP8Z3c=" 403 | }, 404 | "rechoir": { 405 | "version": "0.6.2", 406 | "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", 407 | "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", 408 | "requires": { 409 | "resolve": "1.5.0" 410 | } 411 | }, 412 | "resolve": { 413 | "version": "1.5.0", 414 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", 415 | "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", 416 | "requires": { 417 | "path-parse": "1.0.5" 418 | } 419 | }, 420 | "sentence-case": { 421 | "version": "2.1.1", 422 | "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-2.1.1.tgz", 423 | "integrity": "sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ=", 424 | "requires": { 425 | "no-case": "2.3.2", 426 | "upper-case-first": "1.1.2" 427 | } 428 | }, 429 | "shelljs": { 430 | "version": "0.8.1", 431 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.1.tgz", 432 | "integrity": "sha512-YA/iYtZpzFe5HyWVGrb02FjPxc4EMCfpoU/Phg9fQoyMC72u9598OUBrsU8IrtwAKG0tO8IYaqbaLIw+k3IRGA==", 433 | "requires": { 434 | "glob": "7.1.2", 435 | "interpret": "1.1.0", 436 | "rechoir": "0.6.2" 437 | } 438 | }, 439 | "silverb": { 440 | "version": "0.0.1", 441 | "resolved": "https://registry.npmjs.org/silverb/-/silverb-0.0.1.tgz", 442 | "integrity": "sha512-JPMBQMZ+ovq9zY7TK2dotvkcVfEQ1U4p1F/P8oMq32OJS3198hk385L6D73++/ne6cRHFmcl9F4SqAsyhS0c6w==", 443 | "requires": { 444 | "syntax-error": "1.4.0" 445 | } 446 | }, 447 | "snake-case": { 448 | "version": "2.1.0", 449 | "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", 450 | "integrity": "sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=", 451 | "requires": { 452 | "no-case": "2.3.2" 453 | } 454 | }, 455 | "sprintf-js": { 456 | "version": "1.0.3", 457 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 458 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" 459 | }, 460 | "swap-case": { 461 | "version": "1.1.2", 462 | "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", 463 | "integrity": "sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM=", 464 | "requires": { 465 | "lower-case": "1.1.4", 466 | "upper-case": "1.1.3" 467 | } 468 | }, 469 | "syntax-error": { 470 | "version": "1.4.0", 471 | "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.4.0.tgz", 472 | "integrity": "sha512-YPPlu67mdnHGTup2A8ff7BC2Pjq0e0Yp/IyTFN03zWO0RcK07uLcbi7C2KpGR2FvWbaB0+bfE27a+sBKebSo7w==", 473 | "requires": { 474 | "acorn-node": "1.3.0" 475 | } 476 | }, 477 | "test-value": { 478 | "version": "3.0.0", 479 | "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", 480 | "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", 481 | "requires": { 482 | "array-back": "2.0.0", 483 | "typical": "2.6.1" 484 | } 485 | }, 486 | "title-case": { 487 | "version": "2.1.1", 488 | "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz", 489 | "integrity": "sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=", 490 | "requires": { 491 | "no-case": "2.3.2", 492 | "upper-case": "1.1.3" 493 | } 494 | }, 495 | "typical": { 496 | "version": "2.6.1", 497 | "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", 498 | "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=" 499 | }, 500 | "upper-case": { 501 | "version": "1.1.3", 502 | "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", 503 | "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" 504 | }, 505 | "upper-case-first": { 506 | "version": "1.1.2", 507 | "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", 508 | "integrity": "sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=", 509 | "requires": { 510 | "upper-case": "1.1.3" 511 | } 512 | }, 513 | "wrappy": { 514 | "version": "1.0.2", 515 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 516 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 517 | }, 518 | "xtend": { 519 | "version": "4.0.1", 520 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 521 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" 522 | } 523 | } 524 | } 525 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pwc-code-generator", 3 | "version": "0.0.1", 4 | "description": "Poweful code generator", 5 | "main": "index.js", 6 | "bin": { 7 | "pwc": "bin/pwc" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [ 13 | "crud", 14 | "generator", 15 | "code" 16 | ], 17 | "author": "Tiago Silva Pereira Rodrigues", 18 | "license": "MIT", 19 | "dependencies": { 20 | "change-case": "^3.0.2", 21 | "colors": "^1.2.1", 22 | "command-line-args": "^5.0.2", 23 | "js-yaml": "^3.11.0", 24 | "ncp": "^2.0.0", 25 | "object.entries": "^1.0.4", 26 | "object.values": "^1.0.4", 27 | "pluralize": "^7.0.0", 28 | "shelljs": "^0.8.1", 29 | "silverb": "0.0.1" 30 | }, 31 | "devDependencies": {} 32 | } 33 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [![Open Source Helpers](https://www.codetriage.com/pwc-code-generator/pwc/badges/users.svg)](https://www.codetriage.com/pwc-code-generator/pwc) 2 | 3 | # PWC Code Generator - Command Line Interface 4 | 5 | ## Introduction 6 | 7 | PWC is an agnostic to language/framework code generator, built to provide smart tools for code generation. 8 | 9 | With PWC, you can create unlimited source codes for the same project. It is possible because the "Generators", that are separated NPM Modules, can be called separately. 10 | 11 | The PWC only cares of reading the *YML Project File*, sort the models by precedence, and convert all the data for a *Smart Project Object*, that can be used inside the generators with a lot of tools and callable methods that facilitates the code generation. 12 | 13 | You can see these tools and methods on the [Generator Sample Readme](https://github.com/pwc-code-generator/pwc-generator-sample/blob/master/readme.md). 14 | 15 | PWC is a little like [Yeoman](http://yeoman.io/), but more focused on CRUD generation, built with a [Model Driven](https://en.wikipedia.org/wiki/Model-driven_architecture) structure. 16 | 17 | ## Docs 18 | 19 | You can see the documentation on this [repository](https://github.com/pwc-code-generator/docs). 20 | 21 | ## Requirements 22 | 23 | You need these technologies installed on your machine to use PWC: 24 | 25 | - [NodeJS](https://nodejs.org) 26 | - [NPM](https://www.npmjs.com/) package manager 27 | 28 | ## Installation 29 | 30 | Install PWC globally on your machine: 31 | 32 | ``` 33 | npm install -g pwc-code-generator 34 | ``` 35 | 36 | ## Features 37 | 38 | - Calls a custom generator that you can choose to generate the code as you like (and you can create your own generator if you want, following [these steps](https://github.com/pwc-code-generator/pwc-generator-sample/blob/master/readme.md).) 39 | - Converts a PWC Project File (YML) to a readable and useful *Smart Project Object* 40 | - Organize the Models by precedence, based on their relationships 41 | - Provides a useful and practical interface for generators 42 | 43 | ## How to use 44 | 45 | 1. First, you need to create a PWC Project File, that is a .yml file representing your complete project. You can see some file samples [here](https://github.com/pwc-code-generator/pwc/tree/master/examples). 46 | 2. Install your preferred "Generator" on your machine. You can see a list of generators below. 47 | 3. Then you can navigate to the folder in that your project file is through the command line, and run the PWC passing the generator that you prefer: 48 | 49 | ``` 50 | pwc project -f your-project-file.yml -p your-preferred-generator 51 | ``` 52 | This command will generate the project. By default, it is generated on the current directory, but we recommend that you see your generator documentation for sure. 53 | 54 | 55 | ## Command Structure 56 | 57 | The ```pwc``` command has this structure: 58 | 59 | ``` 60 | pwc --generate [project|model] --file [filename.yml] --plug [your-preferred-generator] --name [Project name - Optional] 61 | ``` 62 | 63 | You can use the abreviatted version: 64 | 65 | ``` 66 | pwc -g [project|model] -f [filename.yml] -p [your-preferred-generator] -n [Project name - Optional] 67 | ``` 68 | 69 | The parameter **--generate or -g** can be passed without the words *--generate, -g* because is the default: 70 | 71 | ``` 72 | pwc project | instead of "pwc -g project" or "pwc --generate project" 73 | ``` 74 | 75 | The value *model* for the --generate parameter doesn't generate anything at this moment, but it's here because we are planning to add functionality to generate specific models inside a current existing project. 76 | 77 | The parameter **--name or -n** is optional because you can pass the project name on the *Project File*. The precedence is: name on *Project File*, name on the parameter. 78 | 79 | Here is an example generating a real simple ERP project with the generator ["Laravel 5.5 - PWC Generator"](https://github.com/pwc-code-generator/pwc-generator-laravel55). 80 | 81 | ``` 82 | pwc project -f simple-erp.yml -p pwc-generator-laravel55 83 | ``` 84 | 85 | ## Project File 86 | 87 | The *Project File* is a YAML file (*.yml) that describes all the models, fields and relationships of a project. Here you can see a simple example: 88 | 89 | ```yml 90 | name: Inventory Control 91 | models: 92 | product: 93 | fields: 94 | name: 95 | type: string,128 96 | element: text 97 | validation: required 98 | searchable: true 99 | type: 100 | type: enum 101 | items: physical,digital|Is digital?,undefined 102 | default: physical 103 | validation: required 104 | belongsTo: 105 | brand: 106 | element: select 107 | validation: required 108 | hasMany: 109 | grid: 110 | element: simple-datagrid 111 | 112 | # ... 113 | ``` 114 | 115 | If you want to know more details about creating *Project Files*, please access the [documentation](https://github.com/pwc-code-generator/docs/blob/master/2_Creating_Project_Files.md). 116 | 117 | The file is simple and practical. But if you want a *Project File* generator, you can access our [platform](https://appstart.kingofcode.com.br/) (on alpha) and click "Download as YML" after creating the models of your project. 118 | 119 | ## The *Smart Project Object* 120 | 121 | The *Smart Project Object* is a javascript object with methods and tools that can be used by the generator to generate the code with practicality. You can see more details about these methods on the [documentation](https://github.com/pwc-code-generator/docs/blob/master/3_Creating_Generators.md). 122 | 123 | ## Generators 124 | 125 | A *Generator* is an important part of code generation. To generate a new source code, you need the PWC and a specific code generator. They are separated because PWC wants to be agnostic to language/framework. Because of it, developers are free to create your own Generators without restrictions. 126 | 127 | Here is a list of the currently available generators: 128 | 129 | - [Laravel 5.5 Generator](https://github.com/pwc-code-generator/pwc-generator-laravel55) - @TiagoSilvaPereira 130 | 131 | ## To Do 132 | 133 | If you want to contribute, please see the [issues](https://github.com/pwc-code-generator/pwc/issues) section to help us make PWC best! 134 | 135 | ## License 136 | MIT -------------------------------------------------------------------------------- /src/classes/Field.js: -------------------------------------------------------------------------------- 1 | const 2 | changeCase = require('change-case'); 3 | 4 | /** 5 | * 6 | * Field Class 7 | * 8 | * A Field is a object containing the attributes below: 9 | * - Name - The name of field, used in database and programming cases 10 | * - Label - The label description of the field, used to generate views 11 | * - Type - The field type, used to set the correct database type 12 | * - Validation - An array with validation rules 13 | * - Element - The visible element that will represent the field Eg: HTML Element 14 | * 15 | */ 16 | 17 | class Field { 18 | 19 | constructor(parsedField) { 20 | try{ 21 | 22 | this.initAttributes(parsedField); 23 | this.setTypeAndSize(parsedField.type); 24 | this.setValue(parsedField.value); 25 | this.setItems(parsedField.items); 26 | this.setValidation(parsedField.validation); 27 | this.addValidationRules(); 28 | this.setMimeTypes(parsedField.mimeTypes); 29 | this.setElement(parsedField.element); 30 | this.setRequired(); 31 | 32 | }catch(e){ 33 | console.log(e.stack); 34 | throw 'Problem with the field object. '.red + parsedField.name + ' ' + e.red; 35 | } 36 | } 37 | 38 | initAttributes(parsedField) { 39 | this.name = parsedField.name; 40 | this.label = parsedField.label || changeCase.upperCaseFirst(this.name); 41 | this.value = null; 42 | this.items = []; 43 | this.valueString = ''; 44 | this.validationString = ''; 45 | this.searchable = (parsedField.searchable !== undefined) ? parsedField.searchable : false; 46 | this.default = (parsedField.default !== undefined) ? parsedField.default : null; 47 | this.hasDefault = (parsedField.default !== undefined) ? true : false; 48 | this.inList = (parsedField.inList !== undefined) ? parsedField.inList : true; 49 | this.fileField = false; 50 | this.imageField = false; 51 | } 52 | 53 | getName() { 54 | return this.name; 55 | } 56 | 57 | getLabel() { 58 | return this.label; 59 | } 60 | 61 | getType() { 62 | return this.type; 63 | } 64 | 65 | getSize() { 66 | return this.size; 67 | } 68 | 69 | getValidation() { 70 | return this.validation; 71 | } 72 | 73 | getValidationAsString() { 74 | return this.validationString; 75 | } 76 | 77 | getElement() { 78 | return this.element; 79 | } 80 | 81 | getDefault() { 82 | return this.default; 83 | } 84 | 85 | hasDefault() { 86 | return this.hasDefault; 87 | } 88 | 89 | isInList() { 90 | return this.inList; 91 | } 92 | 93 | isSearchable() { 94 | return this.searchable; 95 | } 96 | 97 | setTypeAndSize(type) { 98 | let typeParts = type.trim().split(','); 99 | this.type = typeParts[0]; 100 | this.size = typeParts[1] || null; 101 | this.dateField = (this.type == 'date' || this.type == 'datetime' || this.type == 'timezone'); 102 | this.setIsFileOrImageField(); 103 | } 104 | 105 | setIsFileOrImageField() { 106 | this.fileField = (this.type == 'file' || this.type == 'image'); 107 | this.imageField = (this.type == 'image'); 108 | } 109 | 110 | setItems(items) { 111 | let itemsForString = []; 112 | if(items) { 113 | let itemsValues = items.trim().split(','); 114 | itemsValues.forEach((item, index) => { 115 | let itemParts = item.trim().split('|'), 116 | itemObject = { 117 | index: index, 118 | value: itemParts[0], 119 | label: itemParts[1] || changeCase.upperCaseFirst(itemParts[0]), 120 | getValue: function() { return this.value; }, 121 | getLabel: function() { return this.label; } 122 | }; 123 | 124 | itemsForString.push(itemParts[0]); 125 | this.items.push(itemObject); 126 | }); 127 | 128 | this.itemsString = itemsForString.join(','); 129 | } 130 | } 131 | 132 | getItems() { 133 | return this.items; 134 | } 135 | 136 | setValue(value) { 137 | if(value) 138 | this.value = value; 139 | } 140 | 141 | getValue() { 142 | return this.value; 143 | } 144 | 145 | isFileField() { 146 | return this.fileField; 147 | } 148 | 149 | isImageField() { 150 | return this.imageField; 151 | } 152 | 153 | addValidationRules() { 154 | if(this.imageField) { 155 | this.addValitationRule('image'); 156 | } 157 | 158 | if(this.fileField) { 159 | this.addValitationRule('file'); 160 | } 161 | } 162 | 163 | addValitationRule(rule) { 164 | let validation = this.validationString ? this.validationString.trim().split('|') : []; 165 | validation.push(rule); 166 | validation = validation.join('|'); 167 | this.setValidation(validation); 168 | } 169 | 170 | setValidation(validation) { 171 | if(validation) { 172 | this.validation = validation.trim().split('|'); 173 | this.validationString = validation; 174 | } else { 175 | this.validation = null; 176 | } 177 | } 178 | 179 | setMimeTypes(mimeTypes) { 180 | if(mimeTypes) { 181 | this.mimeTypes = mimeTypes.trim().split(','); 182 | this.mimeTypesString = mimeTypes; 183 | this.addValitationRule('mimetypes:' + this.mimeTypesString); 184 | } else { 185 | this.mimeTypes = null; 186 | } 187 | } 188 | 189 | hasMimeTypes() { 190 | return (this.mimeTypes) ? true : false; 191 | } 192 | 193 | getMimeTypes() { 194 | return this.mimeTypes; 195 | } 196 | 197 | getMimeTypesAsString() { 198 | return this.mimeTypesString; 199 | } 200 | 201 | setRequired() { 202 | this.required = this.isRequired(); 203 | } 204 | 205 | isRequired() { 206 | let required = false; 207 | if(this.validation) { 208 | this.validation.forEach((rule) => { 209 | if(rule == 'required') required = true; 210 | }); 211 | } 212 | 213 | return required; 214 | } 215 | 216 | setElement(element) { 217 | this.element = element || this.getDefaultElement(); 218 | } 219 | 220 | getDefaultElement() { 221 | let elementsByType = { 222 | 'string': 'text', 223 | 'text': 'textarea', 224 | 'integer': 'number', 225 | 'decimal': 'number', 226 | 'boolean': 'checkbox', 227 | 'date': 'date', 228 | 'datetime': 'datetime', 229 | 'timestamp': 'datetime', 230 | 'time': 'text', 231 | 'enum': 'select', 232 | 'file': 'file', 233 | 'image': 'file', 234 | }; 235 | 236 | return elementsByType[this.type]; 237 | } 238 | 239 | }; 240 | 241 | module.exports = Field; -------------------------------------------------------------------------------- /src/classes/Model.js: -------------------------------------------------------------------------------- 1 | const 2 | // Classes 3 | Field = require('./Field'), 4 | Relationship = require('./Relationship'), 5 | 6 | // Modules 7 | pluralize = require('pluralize'), 8 | changeCase = require('change-case'); 9 | 10 | class Model { 11 | 12 | constructor(parsedModel) { 13 | 14 | try{ 15 | 16 | this.initAttributes(parsedModel); 17 | this.buildNames(); 18 | this.buildDescription(); 19 | this.setupFields(); 20 | this.setupRelationships(); 21 | this.removeUnwantedAttributes(); 22 | 23 | }catch(e){ 24 | console.log(e.stack); 25 | throw 'Problem with the model "' + parsedModel.name.yellow + '". '.red + e.red; 26 | } 27 | 28 | } 29 | 30 | initAttributes(parsedModel) { 31 | this.parsedModel = parsedModel; 32 | 33 | this.index = this.parsedModel.index; 34 | this.onlyModel = this.parsedModel.onlyModel || false; 35 | this.relationship = this.parsedModel.isRelationship || false; 36 | this.fields = []; 37 | this.fieldsCount = 0; 38 | this.relationships = []; 39 | this.belongsToRelationships = []; 40 | this.belongsToManyRelationships = []; 41 | this.hasOneRelationships = []; 42 | this.hasManyRelationships = []; 43 | } 44 | 45 | getIndex() { 46 | return this.index; 47 | } 48 | 49 | isOnlyModel() { 50 | return this.onlyModel; 51 | } 52 | 53 | isRelationship() { 54 | return this.relationship; 55 | } 56 | 57 | buildNames() { 58 | this.name = this.parsedModel.name; 59 | this.nameCapitalized = changeCase.upperCaseFirst(this.name); 60 | this.namePlural = this.parsedModel.namePlural || pluralize(this.name); 61 | this.namePluralCapitalized = changeCase.upperCaseFirst(this.namePlural); 62 | this.nameSnakeCase = changeCase.snakeCase(this.name); 63 | this.namePluralSnakeCase = changeCase.snakeCase(this.namePlural); 64 | this.nameSlugCase = changeCase.paramCase(this.name); 65 | this.namePluralSlugCase = changeCase.paramCase(this.namePlural); 66 | } 67 | 68 | getName() { 69 | return this.name || ''; 70 | } 71 | 72 | getNamePlural() { 73 | return this.namePlural || ''; 74 | } 75 | 76 | getNameCapitalized() { 77 | return this.nameCapitalized || ''; 78 | } 79 | 80 | getNamePluralCapitalized() { 81 | return this.namePluralCapitalized || ''; 82 | } 83 | 84 | getNameSnakeCase() { 85 | return this.nameSnakeCase || ''; 86 | } 87 | 88 | getNamePluralSnakeCase() { 89 | return this.namePluralSnakeCase || ''; 90 | } 91 | 92 | getNameSlugCase() { 93 | return this.nameSlugCase || ''; 94 | } 95 | 96 | getNamePluralSlugCase() { 97 | return this.namePluralSlugCase || ''; 98 | } 99 | 100 | buildDescription() { 101 | let description = this.parsedModel.description || ''; 102 | 103 | this.descriptionArticle = 'the'; 104 | this.descriptionArticlePlural = 'the'; 105 | 106 | if(!description) { 107 | this.description = changeCase.sentenceCase(this.getName()); 108 | this.descriptionPlural = changeCase.sentenceCase(this.getNamePlural()); 109 | }else{ 110 | this.buildCustomDescription(description); 111 | } 112 | } 113 | 114 | buildCustomDescription(description) { 115 | let descriptionParts = description.split(','); 116 | descriptionParts = descriptionParts.map(function(part) { return part.trim() }); 117 | this.setCustomDescriptionSingularAndPlural(descriptionParts[0]); 118 | if(descriptionParts[1]) 119 | this.setCustomDescriptionArticleSingularAndPlural(descriptionParts[1]); 120 | } 121 | 122 | setCustomDescriptionSingularAndPlural(descriptionParts) { 123 | let description = descriptionParts.split('|'); 124 | 125 | if(description.length < 2) 126 | throw 'Please inform a valid custom description in the format Singular|Plural. Eg: Product|Products'; 127 | 128 | this.description = description[0]; 129 | this.descriptionPlural = description[1]; 130 | } 131 | 132 | setCustomDescriptionArticleSingularAndPlural(descriptionParts) { 133 | let descriptionArticle = descriptionParts.split('|'); 134 | 135 | if(descriptionArticle.length < 2) 136 | throw 'Please inform a valid custom article in the format Singular|Plural. Eg: the|the (English), o|os (Portuguese)'; 137 | 138 | this.descriptionArticle = descriptionArticle[0]; 139 | this.descriptionArticlePlural = descriptionArticle[1]; 140 | } 141 | 142 | getDescription() { 143 | return this.description; 144 | } 145 | 146 | getDescriptionPlural() { 147 | return this.descriptionPlural; 148 | } 149 | 150 | getDescriptionArticle() { 151 | return this.descriptionArticle; 152 | } 153 | 154 | getDescriptionArticlePlural() { 155 | return this.descriptionArticlePlural; 156 | } 157 | 158 | setupFields() { 159 | let fields = this.parsedModel.fields; 160 | this.fieldsCount = 0; 161 | 162 | if(fields) { 163 | Object.keys(fields).map((fieldName) => { 164 | let parsedField = fields[fieldName]; 165 | parsedField.name = fieldName; 166 | 167 | let field = new Field(parsedField); 168 | this.fields.push(field); 169 | }); 170 | 171 | this.fieldsCount = this.fields.length; 172 | } 173 | } 174 | 175 | getFields() { 176 | return this.fields; 177 | } 178 | 179 | setupRelationships() { 180 | this.buildBelongsToRelationship(this.parsedModel.belongsTo); 181 | this.buildBelongsToManyRelationship(this.parsedModel.belongsToMany); 182 | this.buildHasOneRelationship(this.parsedModel.hasOne); 183 | this.buildHasManyRelationship(this.parsedModel.hasMany); 184 | } 185 | 186 | buildRelationship(type, relationship, callback) { 187 | if(relationship) { 188 | Object.keys(relationship).map((relationshipName) => { 189 | let parsedRelationship = relationship[relationshipName]; 190 | parsedRelationship.name = relationshipName; 191 | 192 | let newRelationship = new Relationship(type, parsedRelationship); 193 | this.relationships.push(newRelationship); 194 | if(callback) callback(newRelationship); 195 | }); 196 | } 197 | } 198 | 199 | buildBelongsToRelationship(relationship) { 200 | this.buildRelationship('belongsTo', this.parsedModel.belongsTo, (relationship) => { 201 | this.belongsToRelationships.push(relationship); 202 | }); 203 | } 204 | 205 | buildBelongsToManyRelationship(relationship) { 206 | this.buildRelationship('belongsToMany', this.parsedModel.belongsToMany, (relationship) => { 207 | this.belongsToManyRelationships.push(relationship); 208 | }); 209 | } 210 | 211 | buildHasOneRelationship(relationship) { 212 | this.buildRelationship('hasOne', this.parsedModel.hasOne, (relationship) => { 213 | this.hasOneRelationships.push(relationship); 214 | }); 215 | } 216 | 217 | buildHasManyRelationship(relationship) { 218 | this.buildRelationship('hasMany', this.parsedModel.hasMany, (relationship) => { 219 | this.hasManyRelationships.push(relationship); 220 | }); 221 | } 222 | 223 | getRelationships() { 224 | return this.relationships; 225 | } 226 | 227 | getBelongsToRelationships() { 228 | return this.belongsToRelationships; 229 | } 230 | 231 | getBelongsToManyRelationships() { 232 | return this.belongsToManyRelationships; 233 | } 234 | 235 | getHasOneRelationships() { 236 | return this.hasOneRelationships; 237 | } 238 | 239 | getHasManyRelationships() { 240 | return this.hasManyRelationships; 241 | } 242 | 243 | belongsTo(model) { 244 | let belongs = false; 245 | 246 | this.belongsToRelationships.forEach((relationship) => { 247 | if(relationship.relatedModel == model) { 248 | belongs = true; 249 | } 250 | }); 251 | 252 | return belongs; 253 | } 254 | 255 | removeUnwantedAttributes() { 256 | delete this.parsedModel; 257 | } 258 | 259 | } 260 | 261 | module.exports = Model; -------------------------------------------------------------------------------- /src/classes/Project.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const 4 | // Classes 5 | Util = require('./Util'); 6 | 7 | class Project { 8 | 9 | constructor() { 10 | try{ 11 | this.initAttributes(); 12 | }catch(e){ 13 | console.log(e.stack); 14 | throw 'Problem with the Project: '.red + e.red; 15 | } 16 | } 17 | 18 | initAttributes() { 19 | this.models = []; 20 | this.utils = new Util(); 21 | } 22 | 23 | setName(name) { 24 | this.name = name; 25 | } 26 | 27 | getName() { 28 | return this.name; 29 | } 30 | 31 | setIndex(index) { 32 | this.index = index; 33 | } 34 | 35 | getIndex() { 36 | return this.index; 37 | } 38 | 39 | setModels(models) { 40 | this.models = models; 41 | } 42 | 43 | getModels() { 44 | return this.models; 45 | } 46 | 47 | addModel(model) { 48 | this.models.push(model); 49 | } 50 | 51 | getModelByName(name) { 52 | let foundModel = null; 53 | 54 | this.models.forEach((model) => { 55 | if(model.name == name) { 56 | foundModel = model; 57 | } 58 | }); 59 | 60 | return foundModel; 61 | } 62 | 63 | createGenerationFile() { 64 | let generation = { 65 | generated: false, 66 | files: [], 67 | }; 68 | 69 | if(!this.utils.fileManager.existsSync('pwc-gen.json')) { 70 | this.writeGenerationFile(generation); 71 | } 72 | } 73 | 74 | finalizeGenerationFile() { 75 | let generation = this.getGenerationFileContent(); 76 | generation.generated = true; 77 | this.writeGenerationFile(generation); 78 | } 79 | 80 | resetRegisteredFiles() { 81 | let generation = this.getGenerationFileContent(); 82 | generation.files = []; 83 | this.writeGenerationFile(generation); 84 | } 85 | 86 | registerFileGeneration(file) { 87 | let generation = this.getGenerationFileContent(); 88 | generation.files.push(file); 89 | this.writeGenerationFile(generation); 90 | } 91 | 92 | deleteRegisteredFiles() { 93 | let generation = this.getGenerationFileContent(); 94 | 95 | generation.files.forEach((file, index) => { 96 | this.utils.deleteFile(file); 97 | }); 98 | 99 | generation.files = []; 100 | 101 | this.writeGenerationFile(generation); 102 | } 103 | 104 | getGenerationFileContent() { 105 | return JSON.parse(this.utils.fileManager.readFileSync('pwc-gen.json', 'utf8')); 106 | } 107 | 108 | writeGenerationFile(generationContent) { 109 | let content = JSON.stringify(generationContent, null, 4); 110 | return this.utils.writeFile('pwc-gen.json', content, 'Registering generation info inside: '); 111 | } 112 | 113 | isFirstGeneration() { 114 | let generation = this.getGenerationFileContent(); 115 | return !generation.generated; 116 | } 117 | 118 | } 119 | 120 | module.exports = Project; -------------------------------------------------------------------------------- /src/classes/Relationship.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const 4 | pluralize = require('pluralize'), 5 | changeCase = require('change-case'); 6 | 7 | class Relationship { 8 | 9 | constructor(type, parsedRelationship) { 10 | 11 | try{ 12 | 13 | this.initAttributes(type, parsedRelationship); 14 | this.setNames(); 15 | this.setDisplayField(); 16 | this.setForeignKeyName(); 17 | this.setElement(); 18 | this.setValidation(); 19 | this.setRequired(); 20 | 21 | this.removeUnwantedAttributes(); 22 | }catch(e){ 23 | console.log(e.stack); 24 | throw 'Problem with the relationship "' + type + '". '.red + e.red; 25 | } 26 | 27 | } 28 | 29 | initAttributes(type, parsedRelationship) { 30 | this.type = type; 31 | this.hasforeignKey = false; 32 | this.parsedRelationship = parsedRelationship; 33 | } 34 | 35 | setNames() { 36 | this.name = this.parsedRelationship.name; 37 | this.nameCapitalized = changeCase.upperCaseFirst(this.name); 38 | this.namePlural = pluralize(this.name); 39 | this.namePluralCapitalized = changeCase.upperCaseFirst(this.namePlural); 40 | this.nameSnakeCase = changeCase.snakeCase(this.name); 41 | this.namePluralSnakeCase = changeCase.snakeCase(this.namePlural); 42 | this.nameSlugCase = changeCase.paramCase(this.name); 43 | this.namePluralSlugCase = changeCase.paramCase(this.namePlural); 44 | this.alias = this.parsedRelationship.alias || this.name; 45 | this.aliasPlural = (this.parsedRelationship.alias) ? pluralize(this.parsedRelationship.alias) : this.namePlural; 46 | } 47 | 48 | getName() { 49 | return this.name; 50 | } 51 | 52 | getNamePlural() { 53 | return this.namePlural; 54 | } 55 | 56 | getNameCapitalized() { 57 | return this.nameCapitalized || ''; 58 | } 59 | 60 | getNamePluralCapitalized() { 61 | return this.namePluralCapitalized || ''; 62 | } 63 | 64 | getNameSnakeCase() { 65 | return this.nameSnakeCase || ''; 66 | } 67 | 68 | getNamePluralSnakeCase() { 69 | return this.namePluralSnakeCase || ''; 70 | } 71 | 72 | getNameSlugCase() { 73 | return this.nameSlugCase || ''; 74 | } 75 | 76 | getNamePluralSlugCase() { 77 | return this.namePluralSlugCase || ''; 78 | } 79 | 80 | getAlias() { 81 | return this.alias; 82 | } 83 | 84 | getAliasPlural() { 85 | return this.aliasPlural; 86 | } 87 | 88 | getAliasSlugCase() { 89 | return changeCase.paramCase(this.alias); 90 | } 91 | 92 | getAliasPluralSlugCase() { 93 | return changeCase.paramCase(this.aliasPlural); 94 | } 95 | 96 | getAliasCapitalized() { 97 | return changeCase.upperCaseFirst(this.alias); 98 | } 99 | 100 | getAliasPluralCapitalized() { 101 | return changeCase.upperCaseFirst(this.aliasPlural); 102 | } 103 | 104 | setDisplayField() { 105 | this.displayField = this.parsedRelationship.displayField || 'name'; 106 | } 107 | 108 | getDisplayField() { 109 | return this.displayField; 110 | } 111 | 112 | setForeignKeyName() { 113 | if(this.type == 'belongsTo' || this.type == 'hasOne' || this.type == 'hasMany') { 114 | this.hasForeignKey = true; 115 | this.defaultForeignKeyName = this.name + '_id'; 116 | this.foreignKeyName = this.parsedRelationship.foreignKeyName ? this.parsedRelationship.foreignKeyName : this.defaultForeignKeyName; 117 | this.differentForeignKeyName = (this.defaultForeignKeyName != this.foreignKeyName); 118 | } 119 | } 120 | 121 | getForeignKeyName() { 122 | return this.foreignKeyName; 123 | } 124 | 125 | hasDifferentForeignKeyName() { 126 | return this.differentForeignKeyName; 127 | } 128 | 129 | setElement() { 130 | this.element = (this.parsedRelationship.element !== undefined) ? this.parsedRelationship.element : this.getDefaultElement(); 131 | } 132 | 133 | getElement() { 134 | return this.element; 135 | } 136 | 137 | setValidation() { 138 | let validation = this.parsedRelationship.validation || ''; 139 | if(validation) { 140 | this.validation = validation.trim().split('|'); 141 | this.validationString = validation; 142 | }else{ 143 | this.validation = null; 144 | this.validationString = ''; 145 | } 146 | } 147 | 148 | getValidation() { 149 | return this.validation; 150 | } 151 | 152 | getValidationAsString() { 153 | return this.validationString; 154 | } 155 | 156 | setRequired() { 157 | this.required = this.isRequired(); 158 | } 159 | 160 | isRequired() { 161 | let required = false; 162 | if(this.validation) { 163 | this.validation.forEach((rule) => { 164 | if(rule == 'required') required = true; 165 | }); 166 | } 167 | 168 | return required; 169 | } 170 | 171 | getDefaultElement() { 172 | let elementsByType = { 173 | 'belongsTo': 'select', 174 | 'belongsToMany': 'master-datagrid', 175 | 'hasOne': 'simple-add', 176 | 'hasMany': 'simple-datagrid', 177 | }; 178 | 179 | return elementsByType[this.type]; 180 | } 181 | 182 | removeUnwantedAttributes() { 183 | delete this.parsedRelationship; 184 | } 185 | 186 | setRelatedModel(model) { 187 | if(!model) 188 | throw 'Trying to add a related model in the relationship ' + this.type + ':' + this.name + ' but the model doesn\'t exists'; 189 | this.relatedModel = model; 190 | } 191 | 192 | getRelatedModel() { 193 | return this.relatedModel; 194 | } 195 | 196 | } 197 | 198 | module.exports = Relationship; -------------------------------------------------------------------------------- /src/classes/Template.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const 4 | TemplateEngine = require('silverb'); 5 | 6 | class Template { 7 | 8 | constructor(template, name = 'UNKNOWN') { 9 | 10 | console.log('Get template: '.blue.bold + name); 11 | this.templateEngine = new TemplateEngine(template, name); 12 | 13 | } 14 | 15 | compile(data) { 16 | 17 | return this.templateEngine.compile(data); 18 | 19 | } 20 | 21 | } 22 | 23 | module.exports = Template; -------------------------------------------------------------------------------- /src/classes/Util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const 4 | // Classes 5 | Template = require('./Template'); 6 | 7 | class Util { 8 | 9 | constructor() { 10 | this.pathManager = require('path'); 11 | this.fileManager = require('fs'); 12 | this.shellManager = require('shelljs'); 13 | this.fileCopyManager = require('ncp'); 14 | } 15 | 16 | testDependency(dependency, message) { 17 | if (!this.shellManager.which(dependency)) { 18 | throw message || 'Sorry, this generator requires "' + dependency + '" installed in your system'; 19 | this.shellManager.exit(1); 20 | } 21 | } 22 | 23 | executeCommand(command, callback) { 24 | console.log('Running "' + command.yellow + '"'); 25 | this.shellManager.exec(command, callback); 26 | } 27 | 28 | goToFolder(folder) { 29 | this.shellManager.cd(folder); 30 | } 31 | 32 | /** 33 | * Get a file, proccess it with data and return the content 34 | * - fileNamePath - The file name and path in case of external file 35 | * - data - The data that the doT engine will proccess 36 | */ 37 | getContentFromTemplate(fileNamePath, data = {}) { 38 | let fileContent = this.fileManager.readFileSync(fileNamePath, 'utf8'); 39 | var template = new Template(fileContent, this.getFileNameFromPath(fileNamePath)).compile(data); 40 | return template; 41 | } 42 | 43 | makeFileFromTemplate(destFilePath, templateFilePath, data = {}) { 44 | let content = this.getContentFromTemplate(templateFilePath, data); 45 | return this.writeFile(destFilePath, content); 46 | } 47 | 48 | getFileNameFromPath(path) { 49 | let namePosition = path.search(/[ \w-]+\.\w+$/); 50 | return path.substr(namePosition); 51 | } 52 | 53 | /** 54 | * Makes a folder based on a folder template (copy the folder) 55 | * @param string destinationFolder 56 | * @param string templateFolder 57 | * @return boolean 58 | */ 59 | makeFolderFromTemplate(destinationFolder, templateFolder) { 60 | console.log('Creating Directory inside: '.blue.bold + destinationFolder); 61 | return this.shellManager.cp('-R', templateFolder, destinationFolder); 62 | } 63 | 64 | writeFile(destFilePath, content, message) { 65 | let customMessage = message || 'Writing File: '; 66 | console.log(customMessage.green.bold + destFilePath); 67 | this.makeDirectoryIfNotExists(destFilePath); 68 | return this.fileManager.writeFileSync(destFilePath, content); 69 | } 70 | 71 | deleteFile(destFilePath) { 72 | if(this.fileManager.existsSync(destFilePath)) { 73 | console.log('Removing File: '.yellow.bold + destFilePath); 74 | return this.fileManager.unlinkSync(destFilePath); 75 | } 76 | 77 | return false; 78 | } 79 | 80 | makeDirectoryIfNotExists(filePath) { 81 | var directoryName = this.pathManager.dirname(filePath); 82 | if (this.fileManager.existsSync(directoryName)) { 83 | return true; 84 | } 85 | this.makeDirectoryIfNotExists(directoryName); 86 | this.fileManager.mkdirSync(directoryName); 87 | } 88 | 89 | /** 90 | * Get a existent file passing the content to the callback to manipulate it 91 | * @param {string} fileNamePath File that will be manipulated 92 | * @param {Function} callback The callback that you receive the code and can manipulate it 93 | * @return {boolean} Returns if the file was saved 94 | */ 95 | manipulateFile(fileNamePath, callback) { 96 | let fileContent = this.fileManager.readFileSync(fileNamePath, 'utf8'); 97 | 98 | if(callback) { 99 | fileContent = callback(fileContent); 100 | } 101 | 102 | return this.writeFile(fileName, fileContent); 103 | } 104 | 105 | } 106 | 107 | module.exports = Util; -------------------------------------------------------------------------------- /src/modules/commands.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const commandLineArgs = require('command-line-args'); 4 | 5 | const optionsDefinitions = [ 6 | { name: 'generate', alias: 'g', type: String, defaultOption: true }, 7 | { name: 'name', alias: 'n', type: String }, 8 | { name: 'file', alias: 'f', type: String }, 9 | { name: 'plug', alias: 'p', type: String }, 10 | ]; 11 | 12 | const optionsValidation = { 13 | 'generate': 'required', // Generate project or model 14 | 'name': '', // Project Name 15 | 'file': 'required', // Project File 16 | 'plug': 'required' // Plug 17 | }; 18 | 19 | var commands = { 20 | init: function() { 21 | this.options = commandLineArgs(optionsDefinitions); 22 | this.validateArguments(); 23 | 24 | return this; 25 | }, 26 | 27 | validateArguments: function() { 28 | var validated = true; 29 | 30 | // Validate required arguments 31 | Object.keys(optionsValidation).map((argument) => { 32 | 33 | let validation = optionsValidation[argument]; 34 | if(validation == 'required' && !this.options[argument]) { 35 | validated = false; 36 | console.error('The ' + argument.yellow + ' argument is required!'); 37 | } 38 | 39 | }); 40 | 41 | if(!validated) throw 'Specific arguments are required!'; 42 | }, 43 | 44 | getGenerator: function() { 45 | return this.options.generate || ''; 46 | }, 47 | 48 | getName: function() { 49 | return this.options.name || ''; 50 | }, 51 | 52 | getFile: function() { 53 | return this.options.file || ''; 54 | }, 55 | 56 | getPlug: function() { 57 | return this.options.plug || ''; 58 | } 59 | }; 60 | 61 | module.exports = commands; -------------------------------------------------------------------------------- /src/modules/filter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const 4 | // Classes 5 | Project = require('../classes/Project'), 6 | Model = require('../classes/Model'); 7 | 8 | var filter = { 9 | 10 | init: function(commands, parsedProject) { 11 | this.commands = commands; 12 | this.parsedProject = parsedProject; 13 | this.project = new Project(); 14 | 15 | this.filterName(); 16 | this.initModels(); 17 | this.enrichRelationships(); 18 | this.orderModels(); 19 | 20 | this.project.setIndex(parsedProject.index); 21 | 22 | return this.project; 23 | }, 24 | 25 | filterName: function() { 26 | let name = this.parsedProject.name || this.commands.getName(); 27 | if(!name) 28 | throw 'Please inform the project/module name!'; 29 | 30 | this.project.setName(name); 31 | }, 32 | 33 | initModels: function() { 34 | this.validateParsedProjectHasModels(); 35 | 36 | Object.keys(this.parsedProject.models).map((modelName, index) => { 37 | let parsedModel = this.parsedProject.models[modelName]; 38 | 39 | parsedModel.name = modelName; 40 | let model = new Model(parsedModel); 41 | 42 | this.project.addModel(model); 43 | }); 44 | }, 45 | 46 | validateParsedProjectHasModels: function() { 47 | if(!this.parsedProject.models || this.parsedProject.models.length == 0) 48 | throw 'The project has no models to generate the code. Please verify your ' 49 | + this.commands.getFile() + ' file'; 50 | }, 51 | 52 | enrichRelationships: function() { 53 | this.project.getModels().forEach((model) => { 54 | model.relationships.forEach((relationship) => { 55 | let relatedModel = this.project.getModelByName(relationship.name); 56 | relationship.setRelatedModel(relatedModel); 57 | }); 58 | }); 59 | }, 60 | 61 | /** 62 | * This method is used to order the models based in your relationships. Eg: With a model "PARENT" and a model "CHILD", 63 | * the "PARENT" model needs to always run before the "CHILD" model. Otherwise, the database generation can broken. 64 | */ 65 | orderModels: function() { 66 | console.log('Ordering models...'.blue.bold); 67 | 68 | this.project.getModels().sort((firstModel, secondModel) => { 69 | 70 | // First model belongs to secondModel 71 | if(firstModel.belongsTo(secondModel)) { 72 | return 1; // Send firstModel to end 73 | } 74 | 75 | if(secondModel.belongsTo(firstModel)) { 76 | return -1; // Send firstModel to start 77 | } 78 | 79 | return 0; 80 | 81 | }); 82 | 83 | this.setNewModelsIndex(); 84 | }, 85 | 86 | setNewModelsIndex: function() { 87 | this.project.getModels().forEach((model, index) => { 88 | model.index = index; 89 | }); 90 | }, 91 | 92 | showAllModelNames: function() { 93 | this.project.getModels().forEach((model) => { 94 | console.log('Model ' + model.index + ': ' + model.getName()); 95 | }); 96 | } 97 | 98 | }; 99 | 100 | module.exports = filter; -------------------------------------------------------------------------------- /src/modules/projectParser.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const 4 | yaml = require('js-yaml'), 5 | fs = require('fs'), 6 | filter = require('./filter'); 7 | 8 | var project = { 9 | init: function(commands) { 10 | this.commands = commands; 11 | this.models = []; 12 | this.loadProjectFile(); 13 | this.filterProject(); 14 | 15 | return this.project; 16 | }, 17 | 18 | loadProjectFile: function() { 19 | this.parsedProject = yaml.safeLoad(fs.readFileSync(this.commands.getFile(), 'utf8')); 20 | 21 | if(!this.parsedProject) 22 | throw 'The file ' + this.commands.getFile() + ' has no data or the data is invalid/corrupted. Please verify!'; 23 | }, 24 | 25 | filterProject: function() { 26 | this.project = filter.init(this.commands, this.parsedProject); 27 | } 28 | } 29 | 30 | module.exports = project; --------------------------------------------------------------------------------