@anthonylzq/simba.js 5.1.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,14 @@
1
1
  <h1 align="center">
2
2
  <!-- <p align="center">Simba.js</p> -->
3
- <a href="https://simbajs.notion.site/783092dc7d444067b4c56a25d671f658?v=31060f3d17524ca58870e86c2960a6df"><img src="https://i.ibb.co/QHxn7kP/Gatonowww.png" alt="Simba.js"></a>
3
+ <a href="https://simbajs.notion.site/783092dc7d444067b4c56a25d671f658?v=31060f3d17524ca58870e86c2960a6df"><img src="https://i.ibb.co/QFX0WnH/simba-pink.png" alt="Simba.js"></a>
4
4
  </h1>
5
5
 
6
6
  [![NPM version](https://img.shields.io/npm/v/@anthonylzq/simba.js.svg?style=flat)](https://www.npmjs.com/package/@anthonylzq/simba.js)
7
7
  [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/AnthonyLzq/simba.js/blob/master/LICENSE)
8
8
  [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
9
9
  [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://reactjs.org/docs/how-to-contribute.html#your-first-pull-request)
10
+ [![Lint](https://github.com/AnthonyLzq/simba.js/actions/workflows/lint.yml/badge.svg)](https://github.com/AnthonyLzq/simba.js/actions/workflows/lint.yml)
11
+ [![Tests for Simba APIs](https://github.com/AnthonyLzq/simba.js/actions/workflows/test.yml/badge.svg)](https://github.com/AnthonyLzq/simba.js/actions/workflows/test.yml)
10
12
 
11
13
  Set up a modern backend app by running one command. This project has the goal to create a complete setup for a backend application using `TypeScript` and `Express` or `Fastify`. It will create many files that are usually created manually. Think about Simba.js like a [CRA](https://create-react-app.dev/), but for backend development. Check the [**project structure**](#project-structure) for more information.
12
14
 
@@ -442,6 +444,7 @@ If you want to check the content of the files, please check the [example](https:
442
444
  - If you provide a project name that contains spaces, something like 'My awesome Project', every space will be replaced with a hyphen. So at the end your project name will be 'My-awesome-project', but in its README.md file, the hyphens will be removed and the project name will be parsed to title case (My Awesome Project).
443
445
  - Finally, `git` will be initialized and a list of libraries will be installed. Check the [**notes**](#notes).
444
446
  - Relative imports is already configured, you do not need to import a file using `../../../some/very/nested/stuff/in/other/folder`, you can use `some/very/nested/stuff/in/other/folder` assuming that your folder is under the `src` folder.
447
+ - The Fastify version is set to v3 because Apollo Server has not yet provided support for Fastify v4 yet, and it is difficult to have support for two different major versions of Fastify, so until Apollo Server supports Fastify v4, this package will use Fastify v3.
445
448
 
446
449
  ## What is new?
447
450
 
package/lib/index.js CHANGED
@@ -12,7 +12,7 @@ const argv = yargs(hideBin(process.argv))
12
12
  '"simba [options]" (if you it installed globally) or only "simba -q" if you want to be asked for the options one by one'
13
13
  )
14
14
  .example(
15
- "simba -N 'Project Name' -D 'Project description' -a Anthony -e sluzquinosa@uni.pe"
15
+ "simba -N 'Project Name' -D 'Project description' -a Anthony -e sluzquinosa@uni.pe -l mit -F --tests --ghat"
16
16
  )
17
17
  .alias('N', 'projectName')
18
18
  .nargs('N', 1)
@@ -54,6 +54,16 @@ const argv = yargs(hideBin(process.argv))
54
54
  .describe('F', 'Whether or not you want to use Fastify for your project')
55
55
  .alias('g', 'graphql')
56
56
  .describe('g', 'Whether or not you want to use GraphQL for your project')
57
+ .alias('t', 'tests')
58
+ .describe(
59
+ 't',
60
+ 'Whether or not you want to have a basic suit of unit tests with Jest'
61
+ )
62
+ .alias('ghat', 'gh-action-tests')
63
+ .describe(
64
+ 'ghat',
65
+ 'Whether or not you want to have a GitHub Action with a CI for your tests. If this option is set to true, the tests flag must be set to true.'
66
+ )
57
67
  .default({
58
68
  H: false,
59
69
  n: false,
@@ -63,9 +73,11 @@ const argv = yargs(hideBin(process.argv))
63
73
  f: 'src/index.ts',
64
74
  q: false,
65
75
  F: false,
66
- g: false
76
+ g: false,
77
+ t: false,
78
+ ghat: false
67
79
  })
68
- .boolean(['H', 'n', 'q', 'F', 'g'])
80
+ .boolean(['H', 'n', 'q', 'F', 'g', 't'])
69
81
  .help('h')
70
82
  .alias('h', 'help')
71
83
  .epilog('Developed by AnthonyLzq').argv
@@ -84,7 +96,9 @@ const config = {
84
96
  manager: 'yarn add',
85
97
  mainFile: 'src/index.ts',
86
98
  fastify: false,
87
- graphql: false
99
+ graphql: false,
100
+ tests: true,
101
+ ghat: true
88
102
  }
89
103
  const UNLICENSED = 'unlicensed'
90
104
  const LICENSES = [
@@ -216,6 +230,21 @@ const main = async () => {
216
230
  }
217
231
  )
218
232
  config.mainFile = readLineSync.question('> Main file (src/index.ts): ')
233
+ config.tests = readLineSync.keyInYNStrict(
234
+ '> Would you want to have a basic suit of tests with Jest? ',
235
+ {
236
+ caseSensitive: false
237
+ }
238
+ )
239
+
240
+ if (config.tests)
241
+ config.ghat = readLineSync.keyInYNStrict(
242
+ '> Would you want to have a basic GitHub Action for the suit of tests?',
243
+ {
244
+ caseSensitive: false
245
+ }
246
+ )
247
+ else config.ghat = false
219
248
  } else {
220
249
  if (!argv.author) return console.log('Error! An author is required!')
221
250
  else config.author = argv.author
@@ -289,6 +318,15 @@ const main = async () => {
289
318
 
290
319
  if (!argv.mainFile) console.log('Using src/index.ts as default main file')
291
320
  else config.mainFile = argv.mainFile
321
+
322
+ if (argv.tests) config.tests = true
323
+
324
+ if (argv.ghat) config.ghat = true
325
+
326
+ if (!config.tests && argv.ghat)
327
+ return console.log(
328
+ 'GitHub Action for tests can not be set to true if the tests flag is set to false'
329
+ )
292
330
  }
293
331
 
294
332
  await installation(config)
@@ -5,9 +5,10 @@ const writeFile = require('../../utils/writeFile')
5
5
 
6
6
  /**
7
7
  * @param {Object} args
8
- * @param {String} projectName
8
+ * @param {String} args.projectName
9
+ * @param {Boolean} args.fastify
9
10
  */
10
- const mongoF = async ({ projectName }) => {
11
+ const mongoF = async ({ projectName, fastify }) => {
11
12
  const createFoldersCommand = `mkdir ${projectName}/src/database/mongo \
12
13
  ${projectName}/src/database/mongo/models \
13
14
  ${projectName}/src/database/mongo/queries`
@@ -22,9 +23,126 @@ ${projectName}/src/database/mongo/queries`
22
23
  file: `${projectName}/src/database/index.ts`
23
24
  },
24
25
  mongo: {
26
+ connection: {
27
+ content: fastify
28
+ ? `import { connect, connection } from 'mongoose'
29
+ import { FastifyLoggerInstance } from 'fastify'
30
+
31
+ const ENVIRONMENTS_WITHOUT_RECONNECTION = ['ci', 'local']
32
+ const dbConnection = async (
33
+ logger: FastifyLoggerInstance
34
+ ): Promise<{
35
+ connect: () => Promise<typeof import('mongoose')>
36
+ disconnect: () => Promise<void>
37
+ }> => {
38
+ const connectionConfig = {
39
+ keepAlive: true,
40
+ useNewUrlParser: true,
41
+ useUnifiedTopology: true
42
+ }
43
+
44
+ connection.on('connected', () => {
45
+ logger.info('Mongo connection established.')
46
+ })
47
+ connection.on('reconnected', () => {
48
+ logger.info('Mongo connection reestablished')
49
+ })
50
+ connection.on('disconnected', () => {
51
+ if (
52
+ !ENVIRONMENTS_WITHOUT_RECONNECTION.includes(
53
+ process.env.NODE_ENV as string
54
+ )
55
+ ) {
56
+ logger.info(
57
+ 'Mongo connection disconnected. Trying to reconnected to Mongo...'
58
+ )
59
+ setTimeout(() => {
60
+ connect(process.env.MONGO_URI as string, {
61
+ ...connection,
62
+ connectTimeoutMS: 3000,
63
+ socketTimeoutMS: 3000
64
+ })
65
+ }, 3000)
66
+ }
67
+ })
68
+ connection.on('close', () => {
69
+ logger.info('Mongo connection closed')
70
+ })
71
+ connection.on('error', (e: Error) => {
72
+ logger.info('Mongo connection error:')
73
+ logger.error(e)
74
+ })
75
+
76
+ return {
77
+ connect: () => connect(process.env.MONGO_URI as string, connectionConfig),
78
+ disconnect: () => connection.close()
79
+ }
80
+ }
81
+
82
+ export { dbConnection }
83
+ `
84
+ : `import { connect, connection } from 'mongoose'
85
+ import { HttpLogger } from 'express-pino-logger'
86
+
87
+ const ENVIRONMENTS_WITHOUT_RECONNECTION = ['ci', 'local']
88
+ const dbConnection = async (
89
+ logger: HttpLogger['logger']
90
+ ): Promise<{
91
+ connect: () => Promise<typeof import('mongoose')>
92
+ disconnect: () => Promise<void>
93
+ }> => {
94
+ const connectionConfig = {
95
+ keepAlive: true,
96
+ useNewUrlParser: true,
97
+ useUnifiedTopology: true
98
+ }
99
+
100
+ connection.on('connected', () => {
101
+ logger.info('Mongo connection established.')
102
+ })
103
+ connection.on('reconnected', () => {
104
+ logger.info('Mongo connection reestablished')
105
+ })
106
+ connection.on('disconnected', () => {
107
+ if (
108
+ !ENVIRONMENTS_WITHOUT_RECONNECTION.includes(
109
+ process.env.NODE_ENV as string
110
+ )
111
+ ) {
112
+ logger.info(
113
+ 'Mongo connection disconnected. Trying to reconnected to Mongo...'
114
+ )
115
+ setTimeout(() => {
116
+ connect(process.env.MONGO_URI as string, {
117
+ ...connection,
118
+ connectTimeoutMS: 3000,
119
+ socketTimeoutMS: 3000
120
+ })
121
+ }, 3000)
122
+ }
123
+ })
124
+ connection.on('close', () => {
125
+ logger.info('Mongo connection closed')
126
+ })
127
+ connection.on('error', (e: Error) => {
128
+ logger.info('Mongo connection error:')
129
+ logger.error(e)
130
+ })
131
+
132
+ return {
133
+ connect: () => connect(process.env.MONGO_URI as string, connectionConfig),
134
+ disconnect: () => connection.close()
135
+ }
136
+ }
137
+
138
+ export { dbConnection }
139
+ `,
140
+ file: `${projectName}/src/database/mongo/connection.ts`
141
+ },
25
142
  index: {
26
143
  content: `export * from './models'
27
144
  export * from './queries'
145
+ export * from './connection'
28
146
  `,
29
147
  file: `${projectName}/src/database/mongo/index.ts`
30
148
  },
@@ -72,13 +190,14 @@ export { UserModel }
72
190
  file: `${projectName}/src/database/mongo/queries/index.ts`
73
191
  },
74
192
  user: {
75
- content: `import { Document, Types } from 'mongoose'
193
+ content: `import { Document, MergeType, Types } from 'mongoose'
76
194
 
77
195
  import { UserModel } from '..'
78
- import { UserDTO } from 'schemas'
196
+ import { User, UserDTO, UserWithId } from 'schemas'
79
197
 
80
198
  const userDBOtoDTO = (
81
- userDBO: Document<unknown, unknown, UserDBO> &
199
+ userDBO: Document<unknown, unknown, MergeType<UserDBO, UserDBO>> &
200
+ Omit<UserDBO, keyof UserDBO> &
82
201
  UserDBO & {
83
202
  _id: Types.ObjectId
84
203
  }
@@ -88,7 +207,7 @@ const userDBOtoDTO = (
88
207
  updatedAt: userDBO.updatedAt.toISOString()
89
208
  })
90
209
 
91
- const store = async (userData: UserDTO): Promise<UserDTO> => {
210
+ const store = async (userData: User): Promise<UserDTO> => {
92
211
  const user = new UserModel(userData)
93
212
 
94
213
  await user.save()
@@ -124,7 +243,7 @@ const get = async (
124
243
  return users.map(u => userDBOtoDTO(u))
125
244
  }
126
245
 
127
- const update = async (userData: UserDTO): Promise<UserDTO | null> => {
246
+ const update = async (userData: UserWithId): Promise<UserDTO | null> => {
128
247
  const { id, ...rest } = userData
129
248
  const user = await UserModel.findByIdAndUpdate(id, rest, { new: true })
130
249
 
@@ -140,21 +259,25 @@ export { store, remove, get, update }
140
259
  }
141
260
 
142
261
  await Promise.all([
143
- await writeFile(database.index.file, database.index.content),
144
- await writeFile(database.mongo.index.file, database.mongo.index.content),
145
- await writeFile(
262
+ writeFile(database.index.file, database.index.content),
263
+ writeFile(
264
+ database.mongo.connection.file,
265
+ database.mongo.connection.content
266
+ ),
267
+ writeFile(database.mongo.index.file, database.mongo.index.content),
268
+ writeFile(
146
269
  database.mongo.models.index.file,
147
270
  database.mongo.models.index.content
148
271
  ),
149
- await writeFile(
272
+ writeFile(
150
273
  database.mongo.models.user.file,
151
274
  database.mongo.models.user.content
152
275
  ),
153
- await writeFile(
276
+ writeFile(
154
277
  database.mongo.queries.index.file,
155
278
  database.mongo.queries.index.content
156
279
  ),
157
- await writeFile(
280
+ writeFile(
158
281
  database.mongo.queries.user.file,
159
282
  database.mongo.queries.user.content
160
283
  )
@@ -165,13 +288,14 @@ export { store, remove, get, update }
165
288
  * @param {Object} args
166
289
  * @param {Boolean|undefined} args.mongo
167
290
  * @param {String} args.projectName
291
+ * @param {Boolean} args.fastify
168
292
  */
169
- module.exports = async ({ mongo = true, projectName }) => {
293
+ module.exports = async ({ mongo = true, projectName, fastify }) => {
170
294
  const createFoldersCommand = `mkdir ${projectName}/src/database`
171
295
 
172
296
  if (platform() === 'win32')
173
297
  await exec(createFoldersCommand.replaceAll('/', '\\'))
174
298
  else await exec(createFoldersCommand)
175
299
 
176
- if (mongo) await mongoF({ projectName })
300
+ if (mongo) await mongoF({ projectName, fastify })
177
301
  }
@@ -9,6 +9,7 @@ const types = require('./types')
9
9
  const network = require('./network')
10
10
  const utils = require('./utils')
11
11
  const writeFile = require('../../utils/writeFile')
12
+ const { ENVIRONMENTS_WITH_MONGO_URI } = require('../../utils/constants')
12
13
 
13
14
  /*
14
15
  * Express api:
@@ -163,7 +164,7 @@ DELETE http://localhost:1996/api/user/60e7e3b93b01c1a7aa74cd6b
163
164
  },
164
165
  '.env': {
165
166
  content: `MONGO_URI = ${
166
- process.env.LOCAL
167
+ ENVIRONMENTS_WITH_MONGO_URI.includes(process.env.NODE_ENV)
167
168
  ? process.env.MONGO_URI
168
169
  : `mongodb://mongo:mongo@mongo:27017/${projectName}`
169
170
  }`,
@@ -194,7 +195,8 @@ Server.start()
194
195
  // /database
195
196
  database({
196
197
  mongo,
197
- projectName
198
+ projectName,
199
+ fastify
198
200
  }),
199
201
  // /network
200
202
  network({