├── .babelrc
├── .editorconfig
├── .eslintrc
├── .github
├── ISSUE_TEMPLATE.md
└── images
│ ├── logo.png
│ ├── logo.svg
│ └── screenshots
│ ├── config file.png
│ ├── down in db.png
│ ├── intro model file.png
│ ├── models importer file.png
│ ├── mongoose init.png
│ ├── mongoose make seed.png
│ ├── mongoose model-generate.png
│ ├── mongoose.png
│ ├── project structure.png
│ ├── seed file.png
│ ├── seed undo.png
│ ├── seed up.png
│ └── up in db.png
├── .gitignore
├── .npmignore
├── .prettierignore
├── .prettierrc
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── examples
├── config
│ └── config.json
├── models
│ ├── Test.js
│ └── index.js
└── seeders
│ └── 20190622164245-first-test.js
├── package-lock.json
├── package.json
├── src
├── assets
│ ├── migrations
│ │ └── skeleton.js
│ ├── models
│ │ ├── index.js
│ │ └── model.js
│ └── seeders
│ │ └── skeleton.js
├── commands
│ ├── init.js
│ ├── migrate.js
│ ├── migrate_undo.js
│ ├── migrate_undo_all.js
│ ├── migration_generate.js
│ ├── model_generate.js
│ ├── seed.js
│ ├── seed_generate.js
│ └── seed_one.js
├── core
│ ├── migrator.js
│ └── yargs.js
├── helpers
│ ├── asset-helper.js
│ ├── config-helper.js
│ ├── generic-helper.js
│ ├── index.js
│ ├── init-helper.js
│ ├── migration-helper.js
│ ├── model-helper.js
│ ├── path-helper.js
│ ├── template-helper.js
│ ├── umzug-helper.js
│ ├── version-helper.js
│ └── view-helper.js
└── mongoose.js
├── test
└── index.test.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env",
5 | {
6 | "targets": {
7 | "node": "6.0"
8 | },
9 | "exclude": [
10 | "transform-async-to-generator",
11 | "transform-regenerator"
12 | ],
13 | "useBuiltIns": true
14 | }
15 | ]
16 | ],
17 | "plugins": [
18 | "transform-object-rest-spread",
19 | [
20 | "transform-async-to-module-method",
21 | {
22 | "module": "bluebird",
23 | "method": "coroutine"
24 | }
25 | ]
26 | ],
27 | "ignore": [
28 | "src/assets"
29 | ]
30 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 |
9 | # Change these settings to your own preference
10 | indent_style = space
11 | indent_size = 2
12 |
13 | # We recommend you to keep these unchanged
14 | end_of_line = lf
15 | charset = utf-8
16 | trim_trailing_whitespace = true
17 | insert_final_newline = true
18 |
19 | [*.md]
20 | trim_trailing_whitespace = false
21 | indent_size = 4
22 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "no-console": "off",
4 | "no-extra-parens": "warn",
5 | "valid-jsdoc": "off",
6 | "new-cap": ["warn", { "properties": false }],
7 | "comma-spacing": ["error", { "before": false, "after": true }],
8 | "no-extra-boolean-cast": "warn",
9 | "strict": ["warn", "global"],
10 | "no-var": "error",
11 | "prefer-const": "error",
12 | "semi": ["error", "always"],
13 | "space-before-function-paren": ["warn", "always"],
14 | "keyword-spacing": ["warn"],
15 | "prefer-arrow-callback": "error",
16 | "arrow-parens": ["error", "as-needed"],
17 | "comma-style": ["warn", "last"],
18 | "no-bitwise": "off",
19 | "no-cond-assign": ["error", "except-parens"],
20 | "curly": "off",
21 | "eqeqeq": "error",
22 | "no-extend-native": "error",
23 | "wrap-iife": ["error", "any"],
24 | "indent": ["error", 2, { "SwitchCase": 1 }],
25 | "no-use-before-define": "off",
26 | "no-caller": "error",
27 | "no-undef": "error",
28 | "no-unused-vars": "error",
29 | "no-irregular-whitespace": "error",
30 | "max-depth": ["error", 8],
31 | "quotes": ["error", "single", { "avoidEscape": true }],
32 | "linebreak-style": "warn",
33 | "no-loop-func": "warn",
34 | "object-shorthand": "error",
35 | "one-var-declaration-per-line": "warn",
36 | "comma-dangle": "warn",
37 | "no-shadow": "warn",
38 | "camelcase": "warn"
39 | },
40 | "parserOptions": {
41 | "ecmaVersion": 2017,
42 | "sourceType": "module"
43 | },
44 | "env": {
45 | "node": true,
46 | "jest": true,
47 | "es6": true
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
9 |
10 | ## What you are doing?
11 |
12 |
13 | ```js
14 | // code here
15 | ```
16 |
17 | ## What do you expect to happen?
18 | _I wanted Foo!_
19 |
20 | ## What is actually happening?
21 | _But the output was bar!_
22 |
23 | _Output, either JSON or js_
24 |
25 |
26 | __MongoDB Database version:__ XXX
27 | __Mongoosejs CLI version:__ XXX
28 | __Mongoose version:__ XXX
29 |
--------------------------------------------------------------------------------
/.github/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waptik/mongoose-cli/5b61fcd9da6ab68eede25b06fadce9fa153a6deb/.github/images/logo.png
--------------------------------------------------------------------------------
/.github/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
487 |
--------------------------------------------------------------------------------
/.github/images/screenshots/config file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waptik/mongoose-cli/5b61fcd9da6ab68eede25b06fadce9fa153a6deb/.github/images/screenshots/config file.png
--------------------------------------------------------------------------------
/.github/images/screenshots/down in db.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waptik/mongoose-cli/5b61fcd9da6ab68eede25b06fadce9fa153a6deb/.github/images/screenshots/down in db.png
--------------------------------------------------------------------------------
/.github/images/screenshots/intro model file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waptik/mongoose-cli/5b61fcd9da6ab68eede25b06fadce9fa153a6deb/.github/images/screenshots/intro model file.png
--------------------------------------------------------------------------------
/.github/images/screenshots/models importer file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waptik/mongoose-cli/5b61fcd9da6ab68eede25b06fadce9fa153a6deb/.github/images/screenshots/models importer file.png
--------------------------------------------------------------------------------
/.github/images/screenshots/mongoose init.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waptik/mongoose-cli/5b61fcd9da6ab68eede25b06fadce9fa153a6deb/.github/images/screenshots/mongoose init.png
--------------------------------------------------------------------------------
/.github/images/screenshots/mongoose make seed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waptik/mongoose-cli/5b61fcd9da6ab68eede25b06fadce9fa153a6deb/.github/images/screenshots/mongoose make seed.png
--------------------------------------------------------------------------------
/.github/images/screenshots/mongoose model-generate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waptik/mongoose-cli/5b61fcd9da6ab68eede25b06fadce9fa153a6deb/.github/images/screenshots/mongoose model-generate.png
--------------------------------------------------------------------------------
/.github/images/screenshots/mongoose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waptik/mongoose-cli/5b61fcd9da6ab68eede25b06fadce9fa153a6deb/.github/images/screenshots/mongoose.png
--------------------------------------------------------------------------------
/.github/images/screenshots/project structure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waptik/mongoose-cli/5b61fcd9da6ab68eede25b06fadce9fa153a6deb/.github/images/screenshots/project structure.png
--------------------------------------------------------------------------------
/.github/images/screenshots/seed file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waptik/mongoose-cli/5b61fcd9da6ab68eede25b06fadce9fa153a6deb/.github/images/screenshots/seed file.png
--------------------------------------------------------------------------------
/.github/images/screenshots/seed undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waptik/mongoose-cli/5b61fcd9da6ab68eede25b06fadce9fa153a6deb/.github/images/screenshots/seed undo.png
--------------------------------------------------------------------------------
/.github/images/screenshots/seed up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waptik/mongoose-cli/5b61fcd9da6ab68eede25b06fadce9fa153a6deb/.github/images/screenshots/seed up.png
--------------------------------------------------------------------------------
/.github/images/screenshots/up in db.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waptik/mongoose-cli/5b61fcd9da6ab68eede25b06fadce9fa153a6deb/.github/images/screenshots/up in db.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # yarn lock
6 | *.lock
7 |
8 | # Editor files
9 | .vscode
10 |
11 | # Extra folders
12 | node_modules
13 | lib
14 |
15 | # Extra files
16 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | node_modules
17 | src
18 |
19 |
20 | .vscode
21 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | tests
2 | src/assets
3 | examples
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "printWidth": 120
5 | }
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '11'
4 | cache: yarn
5 | install:
6 | - yarn
7 | jobs:
8 | include:
9 | - stage: Build and test
10 | script:
11 | - yarn lint
12 | - yarn test
13 | - yarn build
14 | - stage: npm release
15 | script: yarn build
16 | deploy:
17 | provider: npm
18 | email: '$NPM_EMAIL'
19 | api_key: '$NPM_API_TOKEN'
20 | skip_cleanup: true
21 | on:
22 | tags: true
23 | branches:
24 | only:
25 | - master
26 | - /^v[0-9]+.*$/
27 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 | All notable changes to this project will be documented in this file.
3 |
4 | ## 1.0.7- 22th, Dec, 2020
5 | ### Fixed
6 | - Remove github actions
7 |
8 | ## 1.0.6- 22th, Dec, 2020
9 | ### Fixed
10 | - Added `seeders-path` to baseOption in `/src/core/yargs.js`
11 |
12 | ## 1.0.5 - 28th, Sep, 2019
13 | ### Changed
14 | - docs(README.md): fixed typos, and replaced examples methods from global usage to local(project based) using npx. Fixed links related to Github generated links to display full-path of links in npmjs' website.
15 |
16 | ## 1.0.4 - 6th, Jul, 2019
17 | ### Removed
18 | - storage.js from ./src/core
19 |
20 | ### Fixed
21 | - unable to find models/index.js when using settings from {cwd}/.mongooserc
22 |
23 | ## 1.0.3 - 28th, Jun, 2019
24 | ### Fixed
25 | - The omitted {} in the commented examples of ./src/assets/seeders skeleton.
26 |
27 | ### Added
28 | - Logo (designed by me with Inkscape) for the project has been added and is visible in README file
29 | - Screenshots in ./github/images/screenshots
30 | - Link to screenshots in faq
31 | - Issue template in ./github directory
32 |
33 | ## 1.0.2 - 25th, Jun, 2019
34 | ### Added
35 | - Examples added in examples directory.
36 |
37 | ## 1.0.1 - 25th, Jun, 2019
38 | ### Fixed
39 | - Seeders, migrations working.
40 |
41 | ### Added
42 | - An in-depth description in the readme file.
43 |
44 | ## 1.0.0
45 | ### Changed
46 | - First working version
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2019 mongoose-cmd
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Welcome to mongoosejs-cli 👋
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | [](https://www.npmjs.com/package/mongoosejs-cli)
24 |
25 | ### 🏠 [Homepage](https://github.com/waptik/mongoose-cli)
26 |
27 | 
28 |
29 | ## Table of Contents
30 |
31 | - [Introduction](#introduction)
32 | - [Prerequisites](#prerequisites)
33 | - [Installation](#installation)
34 | - [Usage](#usage)
35 | - [CLI Options](#options)
36 | - [Contributing](#contributing)
37 | - [FAQ](#faq)
38 | - [Documentation](#documentation)
39 | - [Credits](#credits)
40 | - [License](#license)
41 |
42 | ## Introduction
43 | This package, Mongoosejs-cli, is a package for nodejs to help generate and run migrations and seeders for mongoosejs with ease.
44 |
45 | >Note that this is an unofficial CLI package for [mongoosejs](https://github.com/Automattic/mongoose).
46 |
47 | ## Prerequisites🔨
48 |
49 | - node >=11.0.0
50 | - yarn >= 1.16.0 || npm >= 6.0.0
51 | - mongoosejs >= 5.5.12
52 |
53 | ## Installation🛠
54 |
55 | ### Globally
56 | There are two ways to of installing and using this package:
57 |
58 | ```sh
59 | yarn global add mongoosejs-cli
60 | ```
61 |
62 | > - Usage
63 | > - mongoose
64 |
65 | ### Locally
66 |
67 | ```sh
68 | yarn add mongoosejs-cli
69 | ```
70 |
71 | > - Usage
72 | > - npx mongoosejs-cli
73 |
74 | ### Note 📓
75 | It is recommended to install the package in your project(local). We'll be using the local approach in the following examples.
76 |
77 |
78 | ## Usage
79 |
80 | Make sure you have mongoose already installed in your project before proceeding.
81 |
82 | ### To display list of options that the command has, do the following
83 |
84 | ```sh
85 | npx mongoosejs-cli
86 | ```
87 | You'll see the following on your terminal:
88 |
89 | ```sh
90 | Mongoosee CLI [Node: 11.10.0, CLI: 1.0.5, ODM: 5.5.12]
91 |
92 | mongoose [command]
93 |
94 | Commands:
95 | mongoose db:migrate Run pending migrations
96 | mongoose db:migrate:status List the status of all migrations
97 | mongoose db:migrate:undo Reverts a migration
98 | mongoose db:migrate:undo:all Revert all migrations ran
99 | mongoose db:seed Run specified seeder
100 | mongoose db:seed:undo Deletes data from the database
101 | mongoose db:seed:all Run every seeder
102 | mongoose db:seed:undo:all Deletes data from the database
103 | mongoose init Initializes project
104 | mongoose init:config Initializes configuration
105 | mongoose init:migrations Initializes migrations
106 | mongoose init:models Initializes models
107 | mongoose init:seeders Initializes seeders
108 | mongoose migration:generate Generates a new migration file [aliases: migration:create]
109 | mongoose model:generate Generates a model and its migration [aliases: model:create]
110 | mongoose seed:generate Generates a new seed file [aliases: seed:create]
111 |
112 | Options:
113 | --help Show help [boolean]
114 | --version Show version number [boolean]
115 | ```
116 |
117 | ### To initialize the project
118 |
119 | We recommend that after viewing the list of commands, first thing to do, is to generate the required files and directories needed to get started. It can achieved by entering the following command.
120 |
121 | ```sh
122 | npx mongoosejs-cli init
123 | ```
124 |
125 | This will generate the following:
126 |
127 | ```sh
128 | config/
129 | config.json
130 | models/
131 | index.js
132 | migrations/
133 | seeders/
134 | ```
135 |
136 |
137 | - config/ => the directory containing all your configuration files
138 | - config.json => the default configuration files that contains the database connection strings based on the environment(NODE_ENV). You can add extra environments as well.
139 | - models/ => the directory that contains all your mongoose models you generated through the package
140 | - index.js => this file does the database connection and imports all your models
141 | - migrations/ => directory containing all your migration files
142 | - seeders/ => directory containing all your seed files
143 |
144 | ## Options
145 |
146 | ### Changing path
147 |
148 | By default, mongoosejs-cli generates the migrations, seeders and models directories and files in the root directory of your project. To change this behaviour, create a new file called `.mongooserc` manually or use the the following command:
149 |
150 | ```sh
151 | touch .mongooserc
152 | ```
153 |
154 | and paste the following code inside the new created file
155 |
156 | ```js
157 | const path = require('path');
158 |
159 | module.exports = {
160 | 'config': path.resolve('config', 'database.json'),
161 | 'models-path': path.resolve('db', 'models'),
162 | 'seeders-path': path.resolve('db', 'seeders'),
163 | 'migrations-path': path.resolve('db', 'migrations')
164 | }
165 | ```
166 |
167 | Now the CLI will look for its
168 | - configuration settings inside ./config/database.json
169 | - models files inside ./db/models/
170 | - migration files inside ./db/migrations/
171 | - seed files inside ./db/seeders/
172 |
173 | ### Configuration file
174 |
175 | By default the CLI will try to use the file `config/config.js`. You can modify that path either via the `--config` flag or via the option mentioned earlier. Here is how a configuration file might look like (this is the one that `npx mongoosejs-cli init` generates):
176 |
177 | ```json
178 | {
179 | "development": {
180 | "database": {
181 | "url": "mongodb://localhost/mongoose_dev",
182 | "options": {
183 | "useNewUrlParser": true
184 | }
185 | }
186 | },
187 | "test": {
188 | "database": {
189 | "url": "mongodb://localhost/mongoose_test",
190 | "options": {
191 | "useNewUrlParser": true
192 | }
193 | }
194 | },
195 | "production": {
196 | "database": {
197 | "protocol": "mongodb",
198 | "username": "root",
199 | "password": "password",
200 | "name": "database_production",
201 | "host": "localhost",
202 | "port": "",
203 | "options": {
204 | "useNewUrlParser": true
205 | }
206 | }
207 | }
208 | }
209 | ```
210 |
211 |
212 | ### Configuration for connecting over SRV
213 |
214 | In case you are using [MongoDB Atlas](https://www.mongodb.com/cloud/atlas) or any other services that supports srv, kindly remove database name known as `"name"` from the database main object and assign its value to a new string called `"dbName"` in the options object, such as the following.
215 |
216 | ```json
217 | {
218 |
219 | "production": {
220 | "database": {
221 | "protocol": "mongodb+srv",
222 | "username": "root",
223 | "password": "password",
224 | "host": "subdomain.mongodb.com",
225 | "port": "",
226 | "options": {
227 | "useNewUrlParser": true,
228 | "dbName": "database_production",
229 | }
230 | }
231 | }
232 | }
233 | ```
234 |
235 | In case you want to use a url string instead, do the following
236 |
237 | ```json
238 | {
239 |
240 | "production": {
241 | "database": {
242 | "url": "mongodb+srv://root:password@subdomain.mongodb.com/database_production",
243 | "options": {
244 | "useNewUrlParser": true,
245 | "dbName": "database_production",
246 | }
247 | }
248 | }
249 | }
250 | ```
251 |
252 | More coming soon...
253 |
254 | ## Contributing🤝
255 | If you love this package, there are different ways in which you can contribute.
256 |
257 |
258 | 👍 Show you Support
259 |
260 | Give a ⭐️ if this project helped you!
261 |
262 |
263 | General Issues or Feature Requests
264 |
265 | ### Reporting issues or bugs🐛
266 |
267 | > Please make sure to read the full guidelines. Your issue may be closed without warning if you do not.
268 |
269 | Before reporting a new issue, kindly check [issues page](https://github.com/waptik/mongoose-cli/issues) to see if similar issues haven't been solved yet. Else, go ahead and create a new issue.
270 |
271 | > Github issues should follow specified template. When you start creating a new issue, an empty template will be made available to you.
272 |
273 | Please make sure issue you are reporting is strictly related to Mongoosejs CLI.
274 |
275 | ### Proposing new features
276 | If you want to propose new features to Mongoosejs CLI, you may ignore issue template. You still need to clearly state new feature. Feature request should give various examples, API suggestions and references to support idea behind it.
277 |
278 | ### Fixing Bugs or Implementing Features
279 |
280 | 1. Preparing your environment
281 |
282 | Start by cloning Mongoosejs CLI repo
283 |
284 | ```sh
285 | $ git clone git@github.com:waptik/mongoose-cli.git
286 |
287 | $ git clone https://github.com/waptik/mongoose-cli.git # Using HTTPS
288 | ```
289 |
290 | Make sure you have all required dependencies, you will need
291 |
292 | - Node v10 or above
293 | - Yarn v1.16 or above
294 |
295 | Now go to cloned repository folder
296 |
297 | ```sh
298 | $ cd /path/to/cloned/repository
299 | ```
300 |
301 | Install required modules
302 |
303 | ```sh
304 | $ yarn
305 | ```
306 |
307 | ### Running tests
308 |
309 | ```sh
310 | $ yarn test
311 | ```
312 |
313 | Test can take about 7 to 10 minutes to finish, subjected to hardware configuration.
314 |
315 |
316 | Improving Documentation
317 |
318 | If you want to improve or expand our documentation you can start with this readme file.
319 |
320 |
321 | ## FAQ
322 | The Mongoosejs Command Line Interface (CLI) Frequently Asked Question
323 |
324 | ### Initialize mongoose to create necessary files in the project
325 | ```
326 | $ npx mongoosejs-cli init
327 | ```
328 |
329 | ### How can I generate a model?
330 | Specify model name with `--name` argument. List of table fields can be passed with `--attributes` option. Note that the datatypes are using [Mongoose SchemaTypes](https://mongoosejs.com/docs/schematypes.html) when defining them using the CLI.
331 | ```
332 | $ npx mongoosejs-cli model:create --name User --attributes name:String,state:Boolean,birth:Date,card:Number
333 | ```
334 |
335 | You can create your own custom datatypes as plugins after the model file has been generated for you. Refer to [Mongoose Plugins](https://mongoosejs.com/docs/plugins.html), on how to do so.
336 |
337 | ### How can I create a migration?
338 | Specify migration name with `--name` argument
339 | ```
340 | $ npx mongoosejs-cli migration:create --name
341 | ```
342 |
343 | ### How do call/use a model defined?
344 | You can call/use a model(eg: Player) by doing the following:
345 | ```js
346 | const models = require('path_to_models_folder');
347 |
348 | const players = await models.Player.find({});
349 |
350 | console.log(players) // will print players collection
351 | ```
352 |
353 | ### What is the command to execute all migrations?
354 | ```
355 | $ npx mongoosejs-cli db:migrate
356 | ```
357 | ### How can I make a migrations rollback?
358 | ```
359 | $ npx mongoosejs-cli db:migrate:undo:all
360 | ```
361 |
362 | ### How can I create a seeder?
363 | Specify seeder name with `--name` argument
364 | ```
365 | $ npx mongoosejs-cli seed:create --name
366 | ```
367 |
368 | ### How can I run the seeders?
369 | ```
370 | $ npx mongoosejs-cli db:seed:all
371 | ```
372 |
373 | ### How can I make the seeders rollback?
374 | ```
375 | $ npx mongoosejs-cli db:seed:undo:all
376 | ```
377 |
378 | ### Do you have an example of how the structure of the project look like?
379 | Yes. Please check the [examples](https://github.com/waptik/mongoose-cli/blob/master/examples/) folder in this project. Screenshots can also be found [here](https://github.com/waptik/mongoose-cli/blob/master/.github/images/screenshots)
380 |
381 |
382 | ## Documentation📓
383 |
384 | - [Mongoosejs Documentation](https://mongoosejs.com/docs/index.html)
385 | - [CLI Options](#options)
386 | - [Frequently Asked Questions](#faq)
387 |
388 |
389 | ## Credits👌
390 | This package would not have been made possible if not for the following:
391 | - [Mongoosejs](https://github.com/Automattic/mongoose) Contributors and maintainers for the awesome ORM specially for mongodb on nodejs
392 | - [Sequelize](https://github.com/sequelize) for their [CLI](https://github.com/sequelize/cli), which Mongoosejs-cli structure was heavily based on.
393 | - [Kefranabg](https://github.com/kefranabg), for his [readme-md-generator](https://github.com/kefranabg/readme-md-generator) package which generated the skeleton for this readme file.
394 | - Nodejs, Yarnpkg, NPM etc...
395 |
396 | ## License📝
397 |
398 | Copyright © 2019 [Stephane Mensah](https://github.com/waptik).
399 | This project is [MIT](https://github.com/waptik/mongoose-cli/blob/master/LICENSE) licensed.
400 |
401 | ## Author
402 |
403 | 👤 **Stephane Mensah**
404 |
405 | * Twitter: [@_waptik](https://twitter.com/_waptik)
406 | * Github: [@waptik](https://github.com/waptik)
407 |
408 |
409 | ***
410 | _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_
411 |
--------------------------------------------------------------------------------
/examples/config/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "development": {
3 | "database": {
4 | "url": "mongodb://localhost/mongoose_dev",
5 | "options": {
6 | "useNewUrlParser": true
7 | }
8 | }
9 | },
10 | "test": {
11 | "database": {
12 | "url": "mongodb://localhost/mongoose_test",
13 | "options": {
14 | "useNewUrlParser": true
15 | }
16 | }
17 | },
18 | "production": {
19 | "database": {
20 | "protocol": "mongodb",
21 | "username": "root",
22 | "password": "password",
23 | "name": "database_production",
24 | "host": "localhost",
25 | "port": "",
26 | "options": {
27 | "useNewUrlParser": true
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/examples/models/Test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = mongoose => {
3 | const newSchema = new mongoose.Schema({
4 | name: {
5 | type: String
6 | }
7 | }, {
8 | timestamps: {
9 | createdAt: 'created_at',
10 | updatedAt: 'updated_at'
11 | }
12 | });
13 | const Test = mongoose.model('Test', newSchema);
14 | return Test;
15 | };
--------------------------------------------------------------------------------
/examples/models/index.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const Mongoose = require('mongoose');
4 | const basename = path.basename(__filename);
5 | const env = process.env.NODE_ENV || 'development';
6 | const config = require(__dirname + '/../config/config.json')[env];
7 |
8 | if (config.database.url) {
9 | Mongoose.connect(config.database.url, config.database.options);
10 | } else if (config.database.config.dbName) {
11 | Mongoose.connect(`${config.database.protocol}://${config.database.username}:${config.database.password}@${config.database.host}:${config.database.port}`, config.database.options);
12 | } else {
13 | Mongoose.connect(`${config.database.protocol}://${config.database.username}:${config.database.password}@${config.database.host}:${config.database.port}/${config.database.name}`, config.database.options);
14 | }
15 |
16 | const db = () => {
17 | const m = {};
18 |
19 | fs
20 | .readdirSync(__dirname)
21 | .filter(file => {
22 | return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
23 | })
24 | .forEach(file => {
25 | const model = require(path.resolve(__dirname, file))(Mongoose);
26 | m[model.modelName] = model;
27 | });
28 |
29 | return m;
30 | }
31 |
32 |
33 | const models = db();
34 | const mongoose = Mongoose;
35 |
36 | module.exports = mongoose;
37 | module.exports.default = models;
--------------------------------------------------------------------------------
/examples/seeders/20190622164245-first-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | up: models => {
5 | /*
6 | Add altering commands here.
7 | Return a promise to correctly handle asynchronicity.
8 |
9 | Example:
10 | return models.Test.bulkWrite([
11 | {
12 | insertOne: {
13 | document: {
14 | name: 'first test'
15 | }
16 | }
17 | }
18 | ]).then(res => {
19 | // Prints "1"
20 | console.log(res.insertedCount);
21 | });
22 | */
23 | return models.Test.bulkWrite([
24 | {
25 | insertOne: {
26 | document: {
27 | name: 'first test'
28 | }
29 | }
30 | }
31 | ]).then(res => {
32 | // Prints "1"
33 | console.log(res.insertedCount);
34 | });
35 | },
36 |
37 | down: models => {
38 | /*
39 | Add reverting commands here.
40 | Return a promise to correctly handle asynchronicity.
41 |
42 | Example:
43 | return models.Test.bulkWrite([
44 | {
45 | deleteOne: {
46 | filter: {
47 | name: 'first test'
48 | }
49 | }
50 | }
51 | ]).then(res => {
52 | // Prints "1"
53 | console.log(res.deletedCount);
54 | });
55 | */
56 |
57 | return models.Test.bulkWrite([
58 | {
59 | deleteOne: {
60 | filter: {
61 | name: 'first test'
62 | }
63 | }
64 | }
65 | ]).then(res => {
66 | // Prints "1"
67 | console.log(res.deletedCount);
68 | });
69 | }
70 |
71 | };
72 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mongoosejs-cli",
3 | "version": "1.0.7",
4 | "description": "A command line interface (CLI) for Mongoose to generate models and migrations ith ease.",
5 | "bin": {
6 | "mongoose": "./lib/mongoose"
7 | },
8 | "homepage": "https://github.com/waptik/mongoose-cli#readme",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/waptik/mongoose-cli.git"
12 | },
13 | "author": "waptik ",
14 | "license": "MIT",
15 | "private": false,
16 | "bugs": {
17 | "url": "https://github.com/waptik/mongoose-cli/issues"
18 | },
19 | "keywords": [
20 | "mongoose",
21 | "cli",
22 | "mongodb",
23 | "migrations"
24 | ],
25 | "eslintIgnore": [
26 | "src/assets"
27 | ],
28 | "scripts": {
29 | "babel": "babel-node",
30 | "build": "npm run build-clean && babel src -d lib && npm run build-bin && npm run build-assets",
31 | "build-bin": "mv ./lib/mongoose.js ./lib/mongoose && chmod +x ./lib/mongoose",
32 | "build-assets": "cp -R ./src/assets ./lib/",
33 | "build-clean": "rm -rf ./lib/",
34 | "lint": "eslint test src --quiet --fix",
35 | "test": "npm run lint && npm run build"
36 | },
37 | "dependencies": {
38 | "bluebird": "^3.5.5",
39 | "cli-color": "^1.4.0",
40 | "fs-extra": "^8.0.1",
41 | "js-beautify": "^1.10.0",
42 | "lodash": "^4.17.11",
43 | "resolve": "^1.11.0",
44 | "umzug": "^2.2.0",
45 | "yargs": "^14.0.0"
46 | },
47 | "devDependencies": {
48 | "babel-cli": "^6.26.0",
49 | "babel-plugin-transform-async-to-module-method": "^6.24.1",
50 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
51 | "babel-preset-env": "^1.7.0",
52 | "babel-register": "^6.26.0",
53 | "eslint": "^6.0.0",
54 | "husky": "^4.0.2",
55 | "lint-staged": "^9.2.1",
56 | "mongoose": "^5.5.12",
57 | "prettier": "^1.17.1"
58 | },
59 | "engines": {
60 | "node": ">=6.0.0"
61 | },
62 | "husky": {
63 | "hooks": {
64 | "pre-commit": "lint-staged"
65 | }
66 | },
67 | "lint-staged": {
68 | "src/*.js": [
69 | "prettier --write",
70 | "git add"
71 | ]
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/assets/migrations/skeleton.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | up: (models, mongoose) => {
5 | /*
6 | refer to mongoose docs on how to alter a model's schema
7 | */
8 | },
9 |
10 | down: (models, mongoose) => {
11 | /*
12 | undoing schema alteration
13 | */
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/src/assets/models/index.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const Mongoose = require('mongoose');
4 | const basename = path.basename(__filename);
5 | const env = process.env.NODE_ENV || 'development';
6 | const config = require(<%= configFile %>)[env];
7 |
8 | if (config.database.url) {
9 | Mongoose.connect(config.database.url, config.database.options);
10 | } else if (config.database.config.dbName) {
11 | Mongoose.connect(`${protocol}://${username}:${password}@${host}:${port}`, config.database.options);
12 | } else {
13 | Mongoose.connect(`${protocol}://${username}:${password}@${host}:${port}/${database}`, config.database.options);
14 | }
15 |
16 | const db = () => {
17 | const m = {};
18 |
19 | fs
20 | .readdirSync(__dirname)
21 | .filter(file => {
22 | return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
23 | })
24 | .forEach(file => {
25 | const model = require(path.resolve(__dirname, file))(Mongoose);
26 | m[model.modelName] = model;
27 | });
28 |
29 | return m;
30 | }
31 |
32 |
33 | const models = db();
34 | const mongoose = Mongoose;
35 |
36 | module.exports = mongoose;
37 | module.exports.default = models;
--------------------------------------------------------------------------------
/src/assets/models/model.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = mongoose => {
4 | const newSchema = new mongoose.Schema({
5 | <% attributes.forEach(function(attribute, index) { %>
6 | <%= attribute.fieldName %>: {
7 | type: <%= attribute.dataFunction ? `${attribute.dataFunction}(${attribute.dataType})` : attribute.dataValues ? `${attribute.dataType}(${attribute.dataValues})` : attribute.dataType %>
8 | }
9 | <%= (Object.keys(attributes).length - 1) > index ? ',' : '' %>
10 | <% }) %>
11 | }, {
12 | timestamps: {
13 | createdAt: 'created_at',
14 | updatedAt: 'updated_at'
15 | }
16 | });
17 |
18 |
19 | const <%= name %> = mongoose.model('<%= name %>', newSchema);
20 |
21 | return <%= name %>;
22 | };
23 |
--------------------------------------------------------------------------------
/src/assets/seeders/skeleton.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | up: (models, mongoose) => {
5 | /*
6 | Add altering commands here.
7 | Return a promise to correctly handle asynchronicity.
8 |
9 | Example:
10 | return models.Test.bulkWrite([
11 | {
12 | insertOne: {
13 | document: {
14 | name: 'first test'
15 | }
16 | }
17 | }
18 | ]).then(res => {
19 | // Prints "1"
20 | console.log(res.insertedCount);
21 | });
22 | */
23 | },
24 |
25 | down: (models, mongoose) => {
26 | /*
27 | Add reverting commands here.
28 | Return a promise to correctly handle asynchronicity.
29 |
30 | Example:
31 | return models.Test.bulkWrite([
32 | {
33 | deleteOne: {
34 | filter: {
35 | name: 'first test'
36 | }
37 | }
38 | }
39 | ]).then(res => {
40 | // Prints "1"
41 | console.log(res.deletedCount);
42 | });
43 | */
44 | }
45 | };
46 |
--------------------------------------------------------------------------------
/src/commands/init.js:
--------------------------------------------------------------------------------
1 | import clc from 'cli-color';
2 | import { _baseOptions } from '../core/yargs';
3 | import helpers from '../helpers';
4 |
5 | exports.builder = yargs =>
6 | _baseOptions(yargs).option('force', {
7 | describe: 'Will drop the existing config folder and re-create it',
8 | type: 'boolean',
9 | default: false
10 | }).argv;
11 |
12 | exports.handler = async function (argv) {
13 | const command = argv._[0];
14 |
15 | switch (command) {
16 | case 'init':
17 | await initConfig(argv);
18 | await initMigrations(argv);
19 | await initModels(argv);
20 | await initSeeders(argv);
21 | break;
22 |
23 | case 'init:config':
24 | await initConfig(argv);
25 | break;
26 |
27 | case 'init:models':
28 | await initModels(argv);
29 | break;
30 |
31 | case 'init:migrations':
32 | await initMigrations(argv);
33 | break;
34 |
35 | case 'init:seeders':
36 | await initSeeders(argv);
37 | break;
38 | }
39 |
40 | process.exit(0);
41 | };
42 |
43 | function initConfig (args) {
44 | if (!helpers.config.configFileExists() || !!args.force) {
45 | helpers.config.writeDefaultConfig();
46 | helpers.view.ok('Created "' + clc.blueBright(helpers.config.relativeConfigFile()) + '"');
47 | } else {
48 | helpers.view.notifyAboutExistingFile(helpers.config.relativeConfigFile());
49 | process.exit(1);
50 | }
51 | }
52 |
53 | function initModels (args) {
54 | helpers.init.createModelsFolder(!!args.force);
55 | helpers.init.createModelsIndexFile(!!args.force);
56 | }
57 |
58 | function initMigrations (args) {
59 | helpers.init.createMigrationsFolder(!!args.force);
60 | }
61 |
62 | function initSeeders (args) {
63 | helpers.init.createSeedersFolder(!!args.force);
64 | }
65 |
--------------------------------------------------------------------------------
/src/commands/migrate.js:
--------------------------------------------------------------------------------
1 | import { _baseOptions } from '../core/yargs';
2 | import { getMigrator, ensureCollectionSchema } from '../core/migrator';
3 |
4 | import helpers from '../helpers';
5 | import _ from 'lodash';
6 |
7 | exports.builder = yargs =>
8 | _baseOptions(yargs)
9 | .option('to', {
10 | describe: 'Migration name to run migrations until',
11 | type: 'string'
12 | })
13 | .option('from', {
14 | describe: 'Migration name to start migrations from (excluding)',
15 | type: 'string'
16 | }).argv;
17 |
18 | exports.handler = async function (args) {
19 | const command = args._[0];
20 |
21 | await helpers.config.init();
22 |
23 | switch (command) {
24 | case 'db:migrate':
25 | await migrate(args);
26 | break;
27 | case 'db:migrate:status':
28 | await migrationStatus(args);
29 | break;
30 | }
31 |
32 | process.exit(0);
33 | };
34 |
35 | function migrate (args) {
36 | return getMigrator('migration', args)
37 | .then(migrator => {
38 | return ensureCollectionSchema(migrator)
39 | .then(() => migrator.pending())
40 | .then(migrations => {
41 | const options = {};
42 | if (migrations.length === 0) {
43 | helpers.view.log('No migrations were executed, database schema was already up to date.');
44 | process.exit(0);
45 | }
46 | if (args.to) {
47 | if (migrations.filter(migration => migration.file === args.to).length === 0) {
48 | helpers.view.log('No migrations were executed, database schema was already up to date.');
49 | process.exit(0);
50 | }
51 | options.to = args.to;
52 | }
53 | if (args.from) {
54 | if (migrations.map(migration => migration.file).lastIndexOf(args.from) === -1) {
55 | helpers.view.log('No migrations were executed, database schema was already up to date.');
56 | process.exit(0);
57 | }
58 | options.from = args.from;
59 | }
60 | return options;
61 | })
62 | .then(options => migrator.up(options));
63 | })
64 | .catch(e => helpers.view.error(e));
65 | }
66 |
67 | function migrationStatus (args) {helpers.view.info('Migrations Status\n--------------------------------------------\n');
68 |
69 | return getMigrator('migration', args)
70 | .then(migrator => {
71 | return ensureCollectionSchema(migrator)
72 | .then(() => migrator.executed())
73 | .then(migrations => {
74 | _.forEach(migrations, migration => {
75 | helpers.view.log('up', migration.file);
76 | });
77 | })
78 | .then(() => migrator.pending())
79 | .then(migrations => {
80 | _.forEach(migrations, migration => {
81 | helpers.view.log('down', migration.file);
82 | });
83 | });
84 | })
85 | .catch(e => helpers.view.error(e));
86 | }
87 |
--------------------------------------------------------------------------------
/src/commands/migrate_undo.js:
--------------------------------------------------------------------------------
1 | import { _baseOptions } from '../core/yargs';
2 | import { getMigrator, ensureCollectionSchema } from '../core/migrator';
3 |
4 | import helpers from '../helpers';
5 |
6 | exports.builder = yargs =>
7 | _baseOptions(yargs).option('name', {
8 | describe: 'Name of the migration to undo',
9 | type: 'string'
10 | }).argv;
11 |
12 | exports.handler = async function (args) {
13 | await helpers.config.init();
14 |
15 | await migrateUndo(args);
16 |
17 | process.exit(0);
18 | };
19 |
20 | function migrateUndo (args) {
21 | return getMigrator('migration', args)
22 | .then(migrator => {
23 | return ensureCollectionSchema(migrator)
24 | .then(() => migrator.executed())
25 | .then(migrations => {
26 | if (migrations.length === 0) {
27 | helpers.view.log('No executed migrations found.');
28 | process.exit(0);
29 | }
30 | })
31 | .then(() => {
32 | if (args.name) {
33 | return migrator.down(args.name);
34 | } else {
35 | return migrator.down();
36 | }
37 | });
38 | })
39 | .catch(e => helpers.view.error(e));
40 | }
41 |
--------------------------------------------------------------------------------
/src/commands/migrate_undo_all.js:
--------------------------------------------------------------------------------
1 | import { _baseOptions } from '../core/yargs';
2 | import { getMigrator, ensureCollectionSchema } from '../core/migrator';
3 |
4 | import helpers from '../helpers';
5 |
6 | exports.builder = yargs =>
7 | _baseOptions(yargs).option('to', {
8 | describe: 'Revert to the provided migration',
9 | default: 0,
10 | type: 'string'
11 | }).argv;
12 |
13 | exports.handler = async function (args) {
14 | await helpers.config.init();
15 |
16 | await migrationUndoAll(args);
17 |
18 | process.exit(0);
19 | };
20 |
21 | function migrationUndoAll (args) {
22 | return getMigrator('migration', args)
23 | .then(migrator => {
24 | return ensureCollectionSchema(migrator)
25 | .then(() => migrator.executed())
26 | .then(migrations => {
27 | if (migrations.length === 0) {
28 | helpers.view.log('No executed migrations found.');
29 | process.exit(0);
30 | }
31 | })
32 | .then(() => migrator.down({ to: args.to || 0 }));
33 | })
34 | .catch(e => helpers.view.error(e));
35 | }
--------------------------------------------------------------------------------
/src/commands/migration_generate.js:
--------------------------------------------------------------------------------
1 | import { _baseOptions } from '../core/yargs';
2 |
3 | import _ from 'lodash';
4 | import helpers from '../helpers';
5 | import fs from 'fs';
6 | import clc from 'cli-color';
7 |
8 | exports.builder = yargs =>
9 | _baseOptions(yargs).option('name', {
10 | describe: 'Defines the name of the migration',
11 | type: 'string',
12 | demandOption: true
13 | }).argv;
14 |
15 | const generateMigrationName = args => {
16 | return _.trimStart(_.kebabCase(args.name), '-');
17 | };
18 |
19 | exports.handler = function (args) {
20 | helpers.init.createMigrationsFolder();
21 |
22 |
23 | fs.writeFileSync(
24 | helpers.path.getMigrationPath(generateMigrationName(args)),
25 | helpers.template.render(
26 | 'migrations/skeleton.js',
27 | {},
28 | {
29 | beautify: false
30 | },
31 | ),
32 | );
33 |
34 | helpers.view.log('New migration was created at', clc.blueBright(helpers.path.getMigrationPath(generateMigrationName(args))), '.');
35 |
36 | process.exit(0);
37 | };
38 |
--------------------------------------------------------------------------------
/src/commands/model_generate.js:
--------------------------------------------------------------------------------
1 | import { _baseOptions } from '../core/yargs';
2 |
3 | import helpers from '../helpers';
4 | import clc from 'cli-color';
5 |
6 | exports.builder = yargs =>
7 | _baseOptions(yargs)
8 | .option('name', {
9 | describe: 'Defines the name of the new model',
10 | type: 'string',
11 | demandOption: true
12 | })
13 | .option('attributes', {
14 | describe: 'A list of attributes',
15 | type: 'string',
16 | demandOption: true
17 | })
18 | .option('force', {
19 | describe: 'Forcefully re-creates model with the same name',
20 | type: 'string',
21 | demandOption: false
22 | }).argv;
23 |
24 | exports.handler = function (args) {
25 | ensureModelsFolder();
26 | checkModelFileExistence(args);
27 |
28 | try {
29 | helpers.model.generateFile(args);
30 | } catch (err) {
31 | helpers.view.error(err.message);
32 | }
33 |
34 | helpers.view.log('New model was created at', clc.blueBright(helpers.path.getModelPath(args.name)), '.');
35 |
36 | process.exit(0);
37 | };
38 |
39 | function ensureModelsFolder () {
40 | if (!helpers.path.existsSync(helpers.path.getModelsPath())) {
41 | helpers.view.error(
42 | 'Unable to find models path (' +
43 | helpers.path.getModelsPath() +
44 | '). Did you run ' +
45 | clc.blueBright('mongoose init') +
46 | '?',
47 | );
48 | }
49 | }
50 |
51 | function checkModelFileExistence (args) {
52 | const modelPath = helpers.path.getModelPath(args.name);
53 |
54 | if (args.force === undefined && helpers.model.modelFileExists(modelPath)) {
55 | helpers.view.notifyAboutExistingFile(modelPath);
56 | process.exit(1);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/commands/seed.js:
--------------------------------------------------------------------------------
1 | import { _baseOptions } from '../core/yargs';
2 | import { getMigrator } from '../core/migrator';
3 |
4 | import helpers from '../helpers';
5 | import _ from 'lodash';
6 |
7 | exports.builder = yargs => _baseOptions(yargs).argv;
8 | exports.handler = async function (args) {
9 | const command = args._[0];
10 |
11 | // legacy, gulp used to do this
12 | await helpers.config.init();
13 |
14 | switch (command) {
15 | case 'db:seed:all':
16 | await seedAll(args);
17 | break;
18 |
19 | case 'db:seed:undo:all':
20 | await seedUndoAll(args);
21 | break;
22 | }
23 |
24 | process.exit(0);
25 | };
26 |
27 | function seedAll (args) {
28 | return getMigrator('seeder', args).then(migrator => {
29 | return migrator.pending()
30 | .then(seeders => {
31 | if (seeders.length === 0) {
32 | helpers.view.log('No seeders found.');
33 | return;
34 | }
35 |
36 | return migrator.up({ migrations: _.chain(seeders).map('file').value() });
37 | });
38 | }).catch(e => helpers.view.error(e));
39 | }
40 |
41 | function seedUndoAll (args) {
42 | return getMigrator('seeder', args).then(migrator => {
43 | return (
44 | helpers.umzug.getStorage('seeder') === 'none' ? migrator.pending() : migrator.executed()
45 | )
46 | .then(seeders => {
47 | if (seeders.length === 0) {
48 | helpers.view.log('No seeders found.');
49 | return;
50 | }
51 |
52 | return migrator.down({ migrations: _.chain(seeders).map('file').reverse().value() });
53 | });
54 | }).catch(e => helpers.view.error(e));
55 | }
56 |
--------------------------------------------------------------------------------
/src/commands/seed_generate.js:
--------------------------------------------------------------------------------
1 | import { _baseOptions } from '../core/yargs';
2 | import _ from 'lodash';
3 | import helpers from '../helpers';
4 | import fs from 'fs';
5 | import clc from 'cli-color';
6 |
7 |
8 |
9 | exports.builder =
10 | yargs =>
11 | _baseOptions(yargs)
12 | .option('name', {
13 | describe: 'Defines the name of the seed',
14 | type: 'string',
15 | demandOption: true
16 | })
17 | .argv;
18 |
19 |
20 | const generateSeederName = args => {
21 | return _.trimStart(_.kebabCase(args.name), '-');
22 | };
23 |
24 | exports.handler = function (args) {
25 | helpers.init.createSeedersFolder();
26 |
27 | fs.writeFileSync(
28 | helpers.path.getSeederPath(generateSeederName(args)),
29 | helpers.template.render('seeders/skeleton.js', {}, {
30 | beautify: false
31 | })
32 | );
33 |
34 | helpers.view.log(
35 | 'New seed was created at',
36 | clc.blueBright(helpers.path.getSeederPath(generateSeederName(args))),
37 | '.'
38 | );
39 |
40 | process.exit(0);
41 | };
42 |
--------------------------------------------------------------------------------
/src/commands/seed_one.js:
--------------------------------------------------------------------------------
1 | import { _baseOptions } from '../core/yargs';
2 | import { getMigrator } from '../core/migrator';
3 |
4 | import helpers from '../helpers';
5 | import path from 'path';
6 |
7 | exports.builder =
8 | yargs =>
9 | _baseOptions(yargs)
10 | .option('seed', {
11 | describe: 'List of seed files',
12 | type: 'array'
13 | })
14 | .argv;
15 |
16 | exports.handler = async function (args) {
17 | const command = args._[0];
18 |
19 | // legacy, gulp used to do this
20 | await helpers.config.init();
21 |
22 | // filter out cmd names
23 | // for case like --seeders-path seeders --seed seedPerson.js db:seed
24 | const seeds = (args.seed || [])
25 | .filter(name => name !== 'db:seed' && name !== 'db:seed:undo')
26 | .map(file => path.basename(file));
27 |
28 |
29 | switch (command) {
30 | case 'db:seed':
31 | await getMigrator('seeder', args).then(migrator => {
32 | return migrator.up(seeds);
33 | }).catch(e => helpers.view.error(e));
34 | break;
35 |
36 | case 'db:seed:undo':
37 | await getMigrator('seeder', args).then(migrator => {
38 | return migrator.down({ migrations: seeds });
39 | }).catch(e => helpers.view.error(e));
40 | break;
41 | }
42 |
43 | process.exit(0);
44 | };
45 |
46 |
--------------------------------------------------------------------------------
/src/core/migrator.js:
--------------------------------------------------------------------------------
1 | import Umzug from 'umzug';
2 | import Bluebird from 'bluebird';
3 | import helpers from '../helpers/';
4 |
5 | export function getMigrator (type, args) {
6 |
7 | const { models } = require(helpers.config.getModelsIndexFile());
8 | const mongoose = require(helpers.config.getModelsIndexFile());
9 |
10 |
11 | return Bluebird.try(() => {
12 | if (!(helpers.config.configFileExists() || args.url)) {
13 | helpers.view.error(
14 | 'Cannot find "' +
15 | helpers.config.getConfigFile() +
16 | '". Have you initialized mongoosejs-cli in your project by running "mongoose init"?',
17 | );
18 | process.exit(1);
19 | }
20 | const sOptions = {};
21 | sOptions.connection = mongoose.connection;
22 |
23 | const migrator = new Umzug({
24 | storage: helpers.umzug.getStorage(type),
25 | storageOptions: helpers.umzug.getStorageOptions(type, sOptions),
26 | logging: helpers.view.log,
27 | migrations: {
28 | params: [models, mongoose],
29 | path: helpers.path.getPath(type),
30 | pattern: /\.js$/,
31 | wrap: fun => {
32 | if (fun.length === 3) {
33 | return Bluebird.promisify(fun);
34 | } else {
35 | return fun;
36 | }
37 | }
38 | }
39 | });
40 |
41 | try {
42 | return migrator;
43 | } catch (e) {
44 | helpers.view.error(e);
45 | }
46 | });
47 | }
48 |
49 | export function ensureCollectionSchema (migrator) {
50 | const connection = migrator.options.storageOptions.connection;
51 | const collectionName = migrator.options.storageOptions.collectionName;
52 |
53 | return ensureCollection(connection, collectionName)
54 | .then(collection => {
55 | const fields = Object.keys(collection);
56 |
57 | if (fields.length === 3 && (fields.indexOf('createdAt') || fields.indexOf('created_at')) >= 0) {
58 | return;
59 | }
60 | })
61 | .catch(() => { });
62 | }
63 |
64 | function ensureCollection (connection, collectionName) {
65 | return connection.then(() => {
66 |
67 | if (!connection.collections[collectionName]) {
68 | throw new Error(`No migrations collection for ${collectionName} was found.`);
69 | }
70 |
71 | return;
72 | });
73 | }
74 |
--------------------------------------------------------------------------------
/src/core/yargs.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import yargs from 'yargs';
3 | import path from 'path';
4 |
5 | function loadRCFile(optionsPath) {
6 | const rcFile = optionsPath || path.resolve(process.cwd(), '.mongooserc') || path.resolve(process.cwd(), 'mongoose.json');
7 | const rcFileResolved = path.resolve(rcFile);
8 | return fs.existsSync(rcFileResolved) ? JSON.parse(JSON.stringify(require(rcFileResolved))) : {};
9 | }
10 |
11 | const args = yargs
12 | .help(false)
13 | .version(false)
14 | .config(loadRCFile(yargs.argv.optionsPath));
15 |
16 | export default function getYArgs() {
17 | return args;
18 | }
19 |
20 | export function _baseOptions(yargs) {
21 | return yargs
22 | .option('config', {
23 | describe: 'The path to the config file',
24 | type: 'string'
25 | })
26 | .option('debug', {
27 | describe: 'When available show various debug information',
28 | default: false,
29 | type: 'boolean'
30 | })
31 | .option('env', {
32 | describe: 'The environment to run the command in',
33 | default: 'development',
34 | type: 'string'
35 | })
36 | .option('migrations-path', {
37 | describe: 'The path to the migrations folder',
38 | default: 'migrations',
39 | type: 'string'
40 | })
41 | .option('models-path', {
42 | describe: 'The path to the models folder',
43 | default: 'models',
44 | type: 'string'
45 | })
46 | .option('options-path', {
47 | describe: 'The path to a JSON file with additional options',
48 | default: './',
49 | type: 'string'
50 | })
51 | .option('seeders-path', {
52 | describe: 'The path to the seeders folder',
53 | default: 'seeders',
54 | type: 'string'
55 | });
56 | }
57 |
--------------------------------------------------------------------------------
/src/helpers/asset-helper.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs-extra';
2 | import path from 'path';
3 |
4 | const assets = {
5 | copy: (from, to) => {
6 | fs.copySync(path.resolve(__dirname, '..', 'assets', from), to);
7 | },
8 |
9 | read: assetPath => {
10 | return fs.readFileSync(path.resolve(__dirname, '..', 'assets', assetPath)).toString();
11 | },
12 |
13 | write: (targetPath, content) => {
14 | fs.writeFileSync(targetPath, content);
15 | },
16 |
17 | inject: (filePath, token, content) => {
18 | const fileContent = fs.readFileSync(filePath).toString();
19 | fs.writeFileSync(filePath, fileContent.replace(token, content));
20 | },
21 |
22 | injectConfigFilePath: (filePath, configPath) => {
23 | this.inject(filePath, '__CONFIG_FILE__', configPath);
24 | },
25 |
26 | mkdirp: pathToCreate => {
27 | fs.mkdirpSync(pathToCreate);
28 | }
29 | };
30 |
31 | module.exports = assets;
32 | module.exports.default = assets;
33 |
--------------------------------------------------------------------------------
/src/helpers/config-helper.js:
--------------------------------------------------------------------------------
1 | import Bluebird from 'bluebird';
2 | import path from 'path';
3 | import fs from 'fs';
4 | import url from 'url';
5 | import _ from 'lodash';
6 | import helpers from './index';
7 | import getYArgs from '../core/yargs';
8 |
9 | const args = getYArgs().argv;
10 |
11 | const api = {
12 | config: undefined,
13 | rawConfig: undefined,
14 | error: undefined,
15 |
16 | init () {
17 | return Bluebird.resolve()
18 | .then(() => {
19 | let config;
20 |
21 | if (args.url) {
22 | config = api.parseDbUrl(args.url);
23 | } else {
24 | try {
25 | config = require(api.getConfigFile());
26 | } catch (e) {
27 | api.error = e;
28 | }
29 | }
30 | return config;
31 | })
32 | .then(config => {
33 | if (typeof config === 'object' || config === undefined) {
34 | return config;
35 | } else if (config.length === 1) {
36 | return Bluebird.promisify(config)();
37 | } else {
38 | return config();
39 | }
40 | })
41 | .then(config => {
42 | api.rawConfig = config;
43 | })
44 | .then(() => {
45 | // Always return the full config api
46 | return api;
47 | });
48 | },
49 |
50 | getConfigFile () {
51 | if (args.config) {
52 | return path.resolve(process.cwd(), args.config);
53 | }
54 |
55 | const defaultPath = path.resolve(process.cwd(), 'config', 'config.json');
56 | const alternativePath = defaultPath.replace('.json', '.js');
57 |
58 | return helpers.path.existsSync(alternativePath) ? alternativePath : defaultPath;
59 | },
60 |
61 | getModelsIndexFile () {
62 | return helpers.path.getModelPath('index');
63 | },
64 |
65 | relativeConfigFile () {
66 | return path.relative(process.cwd(), api.getConfigFile());
67 | },
68 |
69 | configFileExists () {
70 | return helpers.path.existsSync(api.getConfigFile());
71 | },
72 |
73 | getDefaultConfig () {
74 | return (
75 | JSON.stringify(
76 | {
77 | development: {
78 | database: {
79 | url: 'mongodb://localhost/mongoose_dev',
80 | options: {
81 | useNewUrlParser: true,
82 | },
83 | },
84 | },
85 | test: {
86 | database: {
87 | url: 'mongodb://localhost/mongoose_test',
88 | options: {
89 | useNewUrlParser: true,
90 | },
91 | },
92 | },
93 | production: {
94 | database: {
95 | protocol: 'mongodb',
96 | username: 'root',
97 | password: 'password',
98 | name: 'database_production',
99 | host: 'localhost',
100 | port: '',
101 | options: {
102 | useNewUrlParser: true,
103 | //dbName: "" // uncomment this line if you use something like mongo atlas
104 | },
105 | },
106 | },
107 | },
108 | undefined,
109 | 2,
110 | ) + '\n'
111 | );
112 | },
113 |
114 | writeDefaultConfig () {
115 | const configPath = path.dirname(api.getConfigFile());
116 |
117 | if (!helpers.path.existsSync(configPath)) {
118 | helpers.asset.mkdirp(configPath);
119 | }
120 |
121 | fs.writeFileSync(api.getConfigFile(), api.getDefaultConfig());
122 | },
123 |
124 | readConfig () {
125 | if (!api.config) {
126 | const env = helpers.generic.getEnvironment();
127 |
128 | if (api.rawConfig === undefined) {
129 | throw new Error('Error reading "' + api.relativeConfigFile() + '". Error: ' + api.error);
130 | }
131 |
132 | if (typeof api.rawConfig !== 'object') {
133 | throw new Error('Config must be an object or a promise for an object: ' + api.relativeConfigFile());
134 | }
135 |
136 | if (args.url) {
137 | helpers.view.log('Parsed url ' + api.filteredUrl(args.url, api.rawConfig));
138 | } else {
139 | helpers.view.log('Loaded configuration file "' + api.relativeConfigFile() + '".');
140 | }
141 |
142 | if (api.rawConfig[env]) {
143 | helpers.view.log('Using environment "' + env + '".');
144 |
145 | api.rawConfig = api.rawConfig[env];
146 | }
147 |
148 | // The Sequelize library needs a function passed in to its logging option
149 | if (api.rawConfig.database.logging && !_.isFunction(api.rawConfig.database.logging)) {
150 | api.rawConfig.database.logging = console.log;
151 | }
152 |
153 | // in case url is present - we overwrite the configuration
154 | if (api.rawConfig.database.url) {
155 | api.rawConfig.database = _.merge(api.rawConfig.database, api.parseDbUrl(api.rawConfig.database.url));
156 | }
157 |
158 | api.config = api.rawConfig;
159 | }
160 | return api.config;
161 | },
162 |
163 | readConf () {
164 | try {
165 | api.config = require(api.getConfigFile());
166 | api.rawConfig = api.config;
167 | } catch (e) {
168 | throw new Error(
169 | 'Error occured when looking for "' +
170 | api.relativeConfigFile() +
171 | '". Kindly bootstrap the project using "mongoose init" comand.',
172 | );
173 | }
174 |
175 | const env = helpers.generic.getEnvironment();
176 |
177 | if (api.rawConfig === undefined) {
178 | throw new Error('Error reading "' + api.relativeConfigFile() + '". Error: ' + api.error);
179 | }
180 |
181 | if (typeof api.rawConfig !== 'object') {
182 | throw new Error('Config must be an object: ' + api.relativeConfigFile());
183 | }
184 |
185 | helpers.view.log('Loaded configuration file "' + api.relativeConfigFile() + '".');
186 |
187 | if (api.rawConfig[env]) {
188 | helpers.view.log('Using environment "' + env + '".');
189 |
190 | api.rawConfig = api.rawConfig[env];
191 | }
192 |
193 | if (api.rawConfig.database.logging && !_.isFunction(api.rawConfig.database.logging)) {
194 | api.rawConfig.database.logging = console.log;
195 | }
196 |
197 | // in case url is present - we overwrite the configuration
198 | if (api.rawConfig.database.url) {
199 | api.rawConfig.database = _.merge(api.rawConfig.database, api.parseDbUrl(api.rawConfig.database.url));
200 | }
201 |
202 | return api.rawConfig;
203 | },
204 |
205 | filteredUrl (uri, config) {
206 | const regExp = new RegExp(':?' + (config.password || '') + '@');
207 | return uri.replace(regExp, ':*****@');
208 | },
209 |
210 | urlStringToConfigHash (urlString) {
211 | try {
212 | const urlParts = url.parse(urlString);
213 | let result = {
214 | name: urlParts.pathname.replace(/^\//, ''),
215 | host: urlParts.hostname,
216 | port: urlParts.port ? urlParts.port : '27017',
217 | protocol: urlParts.protocol.replace(/:$/, ''),
218 | ssl: urlParts.query ? urlParts.query.indexOf('ssl=true') >= 0 : false,
219 | };
220 |
221 | if (urlParts.auth) {
222 | result = _.assign(result, {
223 | username: urlParts.auth.split(':')[0],
224 | password: urlParts.auth.split(':')[1],
225 | });
226 | }
227 |
228 | return result;
229 | } catch (e) {
230 | throw new Error('Error parsing url: ' + urlString);
231 | }
232 | },
233 |
234 | parseDbUrl (urlString) {
235 | return api.urlStringToConfigHash(urlString);
236 | },
237 | };
238 |
239 | module.exports = api;
240 |
--------------------------------------------------------------------------------
/src/helpers/generic-helper.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 |
3 | const resolve = require('resolve').sync;
4 | import getYArgs from '../core/yargs';
5 |
6 | const args = getYArgs().argv;
7 |
8 | const generic = {
9 | getEnvironment: () => {
10 | return args.env || process.env.NODE_ENV || 'development';
11 | },
12 |
13 | getMongoose: file => {
14 | const resolvePath = file ? path.join('mongoose', file) : 'mongoose';
15 | const resolveOptions = { basedir: process.cwd() };
16 |
17 | let mongoosePath;
18 |
19 | try {
20 | mongoosePath = require.resolve(resolvePath, resolveOptions);
21 | } catch (e) {}
22 |
23 | try {
24 | mongoosePath = mongoosePath || resolve(resolvePath, resolveOptions);
25 | } catch (e) {
26 | console.error(
27 | 'Unable to resolve mongoose package in ' + process.cwd() + '.\n Are you sure you installed mongoose package?',
28 | );
29 | process.exit(1);
30 | }
31 |
32 | return require(mongoosePath);
33 | }
34 | };
35 |
36 | module.exports = generic;
37 | module.exports.default = generic;
38 |
--------------------------------------------------------------------------------
/src/helpers/index.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import fs from 'fs';
3 |
4 | module.exports = {};
5 |
6 | fs.readdirSync(__dirname)
7 | .filter(file => file.indexOf('.') !== 0 && file.indexOf('index.js') === -1)
8 | .forEach(file => {
9 | module.exports[file.replace('-helper.js', '')] = require(path.resolve(__dirname, file));
10 | });
11 |
12 | module.exports.default = module.exports;
13 |
--------------------------------------------------------------------------------
/src/helpers/init-helper.js:
--------------------------------------------------------------------------------
1 | import clc from 'cli-color';
2 | import helpers from './index';
3 | import path from 'path';
4 | import fs from 'fs';
5 |
6 | function createFolder (folderName, folder, force) {
7 | if (force && fs.existsSync(folder) === true) {
8 | helpers.view.warn('Deleting the ' + clc.blueBright(folderName) + ' folder. (--force)');
9 |
10 | try {
11 | fs.readdirSync(folder).forEach(filename => {
12 | fs.unlinkSync(path.resolve(folder, filename));
13 | });
14 | } catch (e) {
15 | helpers.view.error(e);
16 | }
17 |
18 | try {
19 | fs.rmdirSync(folder);
20 | helpers.view.ok('Successfully deleted the ' + clc.blueBright(folderName) + ' folder.');
21 | } catch (e) {
22 | helpers.view.error(e);
23 | }
24 | }
25 |
26 | try {
27 | if (fs.existsSync(folder) === false) {
28 | helpers.asset.mkdirp(folder);
29 | helpers.view.ok(
30 | 'Successfully created ' + clc.blueBright(folderName) + ' folder at "' + clc.blueBright(folder) + '".',
31 | );
32 | } else {
33 | helpers.view.log(clc.blueBright(folderName) + ' folder at "' + clc.blueBright(folder) + '" already exists.');
34 | }
35 | } catch (e) {
36 | helpers.view.error(e);
37 | }
38 | }
39 |
40 | const init = {
41 | createMigrationsFolder: force => {
42 | createFolder('migrations', helpers.path.getPath('migration'), force);
43 | },
44 |
45 | createSeedersFolder: force => {
46 | createFolder('seeders', helpers.path.getPath('seeder'), force);
47 | },
48 |
49 | createModelsFolder: force => {
50 | createFolder('models', helpers.path.getModelsPath(), force);
51 | },
52 |
53 | createModelsIndexFile: force => {
54 | const modelsPath = helpers.path.getModelsPath();
55 | const indexPath = path.resolve(modelsPath, helpers.path.addFileExtension('index'));
56 |
57 | if (!helpers.path.existsSync(modelsPath)) {
58 | helpers.view.error('Models folder not available.');
59 | } else if (helpers.path.existsSync(indexPath) && !force) {
60 | helpers.view.notifyAboutExistingFile(indexPath);
61 | } else {
62 |
63 | const relativeConfigPath = path.relative(helpers.path.getModelsPath(), helpers.config.getConfigFile());
64 |
65 | const renderModelIndex = helpers.template.render(
66 | 'models/index.js',
67 | {
68 | configFile: "__dirname + '/" + relativeConfigPath.replace(/\\/g, '/') + "'",
69 | // the following are used to bypass `config` not found property issue
70 | database: '${config.database.name}',
71 | host: '${config.database.host}',
72 | username: '${config.database.username}',
73 | password: '${config.database.password}',
74 | port: '${config.database.port}',
75 | protocol: '${config.database.protocol}'
76 | },
77 | {
78 | beautify: false
79 | },
80 | );
81 |
82 | const writeModelIndexFile = helpers.asset.write(indexPath, renderModelIndex);
83 |
84 | writeModelIndexFile;
85 | }
86 | }
87 | };
88 |
89 | module.exports = init;
90 | module.exports.default = init;
91 |
--------------------------------------------------------------------------------
/src/helpers/migration-helper.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import helpers from './index';
3 |
4 | const Mongoose = helpers.generic.getMongoose();
5 |
6 | module.exports = {
7 | getCollectionName (modelName) {
8 | return Mongoose.pluralize(modelName);
9 | },
10 |
11 | generateCollectionCreationFileContent (args) {
12 | return helpers.template.render('migrations/create-table.js', {
13 | tableName: this.getCollectionName(args.name),
14 | attributes: helpers.model.transformAttributes(args.attributes),
15 | createdAt: 'created_at',
16 | updatedAt: 'updated_at'
17 | });
18 | },
19 |
20 | generateMigrationName (args) {
21 | return _.trimStart(_.kebabCase('create-' + args.name), '-');
22 | },
23 |
24 | generateCollectionCreationFile (args) {
25 | const migrationName = this.generateMigrationName(args);
26 | const migrationPath = helpers.path.getMigrationPath(migrationName);
27 |
28 | helpers.asset.write(migrationPath, this.generateCollectionCreationFileContent(args));
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/src/helpers/model-helper.js:
--------------------------------------------------------------------------------
1 | import helpers from './index';
2 |
3 | const Mongoose = helpers.generic.getMongoose();
4 |
5 | const validAttributeFunctionType = ['array', 'enum'];
6 |
7 | /**
8 | * Check the given dataType actual exists.
9 | * @param {string} dataType
10 | */
11 | function validateDataType (dataType) {
12 |
13 | if (Mongoose.Schema.Types[dataType].schemaName !== dataType) {
14 | throw new Error(`Unknown type '${dataType}'`);
15 | }
16 |
17 | return dataType;
18 | }
19 |
20 | function formatAttributes (attribute) {
21 | let result;
22 | const split = attribute.split(':');
23 |
24 | if (split.length === 2) {
25 | result = { fieldName: split[0], dataType: split[1], dataFunction: null, dataValues: null };
26 | } else if (split.length === 3) {
27 | const validValues = /^\{(,? ?[A-z0-9 ]+)+\}$/;
28 | const isValidFunction = validAttributeFunctionType.indexOf(split[1].toLowerCase()) !== -1;
29 | const isValidValue =
30 | validAttributeFunctionType.indexOf(split[2].toLowerCase()) === -1 && split[2].match(validValues) === null;
31 | const isValidValues = split[2].match(validValues) !== null;
32 |
33 | if (isValidFunction && isValidValue && !isValidValues) {
34 | result = { fieldName: split[0], dataType: split[2], dataFunction: split[1], dataValues: null };
35 | }
36 |
37 | if (isValidFunction && !isValidValue && isValidValues) {
38 | result = {
39 | fieldName: split[0],
40 | dataType: split[1],
41 | dataFunction: null,
42 | dataValues: split[2]
43 | .replace(/(^\{|\}$)/g, '')
44 | .split(/\s*,\s*/)
45 | .map(s => `'${s}'`)
46 | .join(', ')
47 | };
48 | }
49 | }
50 |
51 | return result;
52 | }
53 |
54 | module.exports = {
55 | transformAttributes (flag) {
56 | /*
57 | possible flag formats:
58 | - first_name:string,last_name:string,bio:text,role:enum:{Admin, 'Guest User'},reviews:array:string
59 | - 'first_name:string last_name:string bio:text role:enum:{Admin, Guest User} reviews:array:string'
60 | - 'first_name:string, last_name:string, bio:text, role:enum:{Admin, Guest User} reviews:array:string'
61 | */
62 | const attributeStrings = flag
63 | .split('')
64 | .map(
65 | (() => {
66 | let openValues = false;
67 | return a => {
68 | if ((a === ',' || a === ' ') && !openValues) {
69 | return ' ';
70 | }
71 | if (a === '{') {
72 | openValues = true;
73 | }
74 | if (a === '}') {
75 | openValues = false;
76 | }
77 |
78 | return a;
79 | };
80 | })(),
81 | )
82 | .join('')
83 | .split(/\s{2,}/);
84 |
85 | return attributeStrings.map(attribute => {
86 | const formattedAttribute = formatAttributes(attribute);
87 |
88 | try {
89 | validateDataType(formattedAttribute.dataType);
90 | } catch (err) {
91 | throw new Error(`Attribute '${attribute}' cannot be parsed: ${err.message}`);
92 | }
93 |
94 | return formattedAttribute;
95 | });
96 | },
97 |
98 | generateFileContent (args) {
99 | return helpers.template.render('models/model.js', {
100 | name: args.name,
101 | attributes: this.transformAttributes(args.attributes)
102 | });
103 | },
104 |
105 | generateFile (args) {
106 | const modelPath = helpers.path.getModelPath(args.name);
107 |
108 | helpers.asset.write(modelPath, this.generateFileContent(args));
109 | },
110 |
111 | modelFileExists (filePath) {
112 | return helpers.path.existsSync(filePath);
113 | }
114 | };
115 |
--------------------------------------------------------------------------------
/src/helpers/path-helper.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import fs from 'fs';
3 |
4 | const resolve = require('resolve').sync;
5 | import getYArgs from '../core/yargs';
6 |
7 | const args = getYArgs().argv;
8 |
9 | function format (i) {
10 | return parseInt(i, 10) < 10 ? '0' + i : i;
11 | }
12 |
13 | function getCurrentYYYYMMDDHHmms () {
14 | const date = new Date();
15 | return [
16 | date.getUTCFullYear(),
17 | format(date.getUTCMonth() + 1),
18 | format(date.getUTCDate()),
19 | format(date.getUTCHours()),
20 | format(date.getUTCMinutes()),
21 | format(date.getUTCSeconds())
22 | ].join('');
23 | }
24 |
25 | module.exports = {
26 | getPath (type) {
27 | type = type + 's';
28 |
29 | let result = args[type + 'Path'] || path.resolve(process.cwd(), type);
30 |
31 | if (path.normalize(result) !== path.resolve(result)) {
32 | // the path is relative
33 | result = path.resolve(process.cwd(), result);
34 | }
35 |
36 | return result;
37 | },
38 |
39 | getFileName (type, name, options) {
40 | return this.addFileExtension([getCurrentYYYYMMDDHHmms(), name ? name : 'unnamed-' + type].join('-'), options);
41 | },
42 |
43 | getFileExtension () {
44 | return 'js';
45 | },
46 |
47 | addFileExtension (basename, options) {
48 | return [basename, this.getFileExtension(options)].join('.');
49 | },
50 |
51 | getMigrationPath (migrationName) {
52 | return path.resolve(this.getPath('migration'), this.getFileName('migration', migrationName));
53 | },
54 |
55 | getSeederPath (seederName) {
56 | return path.resolve(this.getPath('seeder'), this.getFileName('seeder', seederName));
57 | },
58 |
59 | getModelsPath () {
60 | return args.modelsPath || path.resolve(process.cwd(), 'models');
61 | },
62 |
63 | getModelPath (modelName) {
64 | return path.resolve(this.getModelsPath(), this.addFileExtension(modelName));
65 | },
66 |
67 | resolve (packageName) {
68 | let result;
69 |
70 | try {
71 | result = resolve(packageName, { basedir: process.cwd() });
72 | result = require(result);
73 | } catch (e) {
74 | try {
75 | result = require(packageName);
76 | } catch (err) {}
77 | }
78 |
79 | return result;
80 | },
81 |
82 | existsSync (pathToCheck) {
83 | if (fs.accessSync) {
84 | try {
85 | fs.accessSync(pathToCheck, fs.R_OK);
86 | return true;
87 | } catch (e) {
88 | return false;
89 | }
90 | } else {
91 | return fs.existsSync(pathToCheck);
92 | }
93 | },
94 | };
95 |
--------------------------------------------------------------------------------
/src/helpers/template-helper.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import beautify from 'js-beautify';
3 | import helpers from './index';
4 |
5 | module.exports = {
6 | render (path, locals, options) {
7 | options = _.assign(
8 | {
9 | beautify: true,
10 | indent_size: 2,
11 | preserve_newlines: false
12 | },
13 | options || {},
14 | );
15 |
16 | const template = helpers.asset.read(path);
17 | let content = _.template(template)(locals || {});
18 |
19 | if (options.beautify) {
20 | content = beautify(content, options);
21 | }
22 |
23 | return content;
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/src/helpers/umzug-helper.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import _ from 'lodash';
3 | import helpers from './index';
4 |
5 | const storage = {
6 | migration: 'mongodb',
7 | seeder: 'none'
8 | };
9 | const storageCollectionName = {
10 | migration: 'mongoose_migrations_meta',
11 | seeder: 'mongoose_seeder_data'
12 | };
13 |
14 | const storageJsonName = {
15 | migration: 'mongoose-migrations.json',
16 | seeder: 'mongoose-seeders.json'
17 | };
18 |
19 | module.exports = {
20 | getStorageOption (property, fallback) {
21 | return helpers.config.readConfig()[property] || fallback;
22 | },
23 |
24 | getStorage (type) {
25 | return this.getStorageOption(type + 'Storage', storage[type]);
26 | },
27 |
28 | getStoragePath (type) {
29 | const fallbackPath = path.join(process.cwd(), storageJsonName[type]);
30 |
31 | return this.getStorageOption(type + 'StoragePath', fallbackPath);
32 | },
33 |
34 | getCollectionName (type) {
35 | return this.getStorageOption(type + 'StorageCollectionName', storageCollectionName[type]);
36 | },
37 |
38 | getStorageOptions (type, extraOptions) {
39 | const options = {};
40 |
41 | if (this.getStorage(type) === 'json') {
42 | options.path = this.getStoragePath(type);
43 | } else if (this.getStorage(type) === 'mongodb') {
44 | options.collectionName = this.getCollectionName(type);
45 | } else {
46 | options.collectionName = this.getCollectionName(type);
47 | }
48 |
49 | _.assign(options, extraOptions);
50 |
51 | return options;
52 | }
53 | };
54 |
--------------------------------------------------------------------------------
/src/helpers/version-helper.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import helpers from './index';
3 |
4 | const packageJson = require(path.resolve(__dirname, '..', '..', 'package.json'));
5 |
6 | module.exports = {
7 | getCliVersion () {
8 | return packageJson.version;
9 | },
10 |
11 | getOdmVersion () {
12 | return helpers.generic.getMongoose('package.json').version;
13 | },
14 |
15 | getNodeVersion () {
16 | return process.version.replace('v', '');
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/src/helpers/view-helper.js:
--------------------------------------------------------------------------------
1 | import clc from 'cli-color';
2 | import _ from 'lodash';
3 | import helpers from './index';
4 | import getYArgs from '../core/yargs';
5 |
6 | const args = getYArgs().argv;
7 |
8 | module.exports = {
9 | teaser () {
10 | const versions = [
11 | 'Node: ' + helpers.version.getNodeVersion(),
12 | 'CLI: ' + helpers.version.getCliVersion(),
13 | 'ODM: ' + helpers.version.getOdmVersion()
14 | ];
15 |
16 | this.log();
17 | this.log(clc.underline.blue('Mongoosee CLI [' + versions.join(', ') + ']'));
18 | this.log();
19 | },
20 |
21 | log () {
22 | console.log.apply(this, arguments);
23 | },
24 |
25 | error (error) {
26 | let message = error;
27 |
28 | if (error instanceof Error) {
29 | message = !args.debug ? error.message : error.stack;
30 | }
31 |
32 | this.log();
33 | console.error(`${clc.red('ERROR:')} ${message}`);
34 | this.log();
35 |
36 | process.exit(1);
37 | },
38 |
39 | info (message) {
40 | this.log(`${clc.underline.blue('INFO:')} ${message}`);
41 | },
42 |
43 | ok (message) {
44 | this.log(`${clc.green('SUCCESS:')} ${message}`);
45 | },
46 |
47 | warn (message) {
48 | this.log(`${clc.yellow('WARNING:')} ${message}`);
49 | },
50 |
51 | notifyAboutExistingFile (file) {
52 | this.error('The file ' + clc.blueBright(file) + ' already exists. ' + 'Run command with --force to overwrite it.');
53 | },
54 |
55 | pad (s, smth) {
56 | let margin = smth;
57 |
58 | if (_.isObject(margin)) {
59 | margin = Object.keys(margin);
60 | }
61 |
62 | if (Array.isArray(margin)) {
63 | margin = Math.max.apply(
64 | null,
65 | margin.map(o => {
66 | return o.length;
67 | }),
68 | );
69 | }
70 |
71 | return s + new Array(margin - s.length + 1).join(' ');
72 | }
73 | };
74 |
--------------------------------------------------------------------------------
/src/mongoose.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import getYArgs from './core/yargs';
3 | import Promise from 'bluebird';
4 | import { isEmpty } from 'lodash';
5 |
6 | const yargs = getYArgs();
7 |
8 | Promise.coroutine.addYieldHandler(yieldedValue => {
9 | if (Array.isArray(yieldedValue)) {
10 | return Promise.all(yieldedValue);
11 | }
12 | });
13 |
14 | Promise.coroutine.addYieldHandler(yieldedValue => {
15 | if (isEmpty(yieldedValue)) {
16 | return Promise.resolve(yieldedValue);
17 | }
18 | });
19 |
20 | import init from './commands/init';
21 | import migrate from './commands/migrate';
22 | import migrateUndo from './commands/migrate_undo';
23 | import migrateUndoAll from './commands/migrate_undo_all';
24 | import seed from './commands/seed';
25 | import seedOne from './commands/seed_one';
26 | import migrationGenerate from './commands/migration_generate';
27 | import modelGenerate from './commands/model_generate';
28 | import seedGenerate from './commands/seed_generate';
29 |
30 | import helpers from './helpers/index';
31 |
32 | helpers.view.teaser();
33 |
34 | const cli = yargs
35 | .help()
36 | .version()
37 | .command('db:migrate', 'Run pending migrations', migrate)
38 | .command('db:migrate:status', 'List the status of all migrations', migrate)
39 | .command('db:migrate:undo', 'Reverts a migration', migrateUndo)
40 | .command('db:migrate:undo:all', 'Revert all migrations ran', migrateUndoAll)
41 | .command('db:seed', 'Run specified seeder', seedOne)
42 | .command('db:seed:undo', 'Deletes data from the database', seedOne)
43 | .command('db:seed:all', 'Run every seeder', seed)
44 | .command('db:seed:undo:all', 'Deletes data from the database', seed)
45 | .command('init', 'Initializes project', init)
46 | .command('init:config', 'Initializes configuration', init)
47 | .command('init:migrations', 'Initializes migrations', init)
48 | .command('init:models', 'Initializes models', init)
49 | .command('init:seeders', 'Initializes seeders', init)
50 | .command(['migration:generate', 'migration:create'], 'Generates a new migration file', migrationGenerate)
51 | .command(['model:generate', 'model:create'], 'Generates a model and its migration', modelGenerate)
52 | .command(['seed:generate', 'seed:create'], 'Generates a new seed file', seedGenerate)
53 | .wrap(yargs.terminalWidth())
54 | .strict();
55 |
56 | const args = cli.argv;
57 |
58 | // if no command then show help
59 | if (!args._[0]) {
60 | cli.showHelp();
61 | }
62 |
--------------------------------------------------------------------------------
/test/index.test.js:
--------------------------------------------------------------------------------
1 |
2 | test.todo(`write some tests`);
3 |
--------------------------------------------------------------------------------