@anthonylzq/simba.js 9.0.0 → 9.1.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.
Files changed (106) hide show
  1. package/README.md +65 -44
  2. package/lib/index.js +42 -5
  3. package/lib/src/functions/api/database.js +40 -218
  4. package/lib/src/functions/api/express.js +100 -604
  5. package/lib/src/functions/api/fastify.js +93 -645
  6. package/lib/src/functions/api/hono.js +157 -0
  7. package/lib/src/functions/api/index.js +37 -59
  8. package/lib/src/functions/api/schemas.js +20 -84
  9. package/lib/src/functions/api/services.js +28 -210
  10. package/lib/src/functions/api/utils.js +10 -432
  11. package/lib/src/functions/biome.js +3 -2
  12. package/lib/src/functions/changelog.js +5 -5
  13. package/lib/src/functions/docker.js +7 -39
  14. package/lib/src/functions/ghat.js +12 -94
  15. package/lib/src/functions/gitignore.js +3 -134
  16. package/lib/src/functions/index.js +1 -2
  17. package/lib/src/functions/license.js +10 -2
  18. package/lib/src/functions/packageJson.js +13 -37
  19. package/lib/src/functions/readme.js +6 -5
  20. package/lib/src/functions/tests.js +17 -320
  21. package/lib/src/functions/tsconfig.js +13 -114
  22. package/lib/src/index.js +45 -21
  23. package/lib/src/utils/entity.js +115 -0
  24. package/lib/src/utils/index.js +2 -0
  25. package/lib/src/utils/mkdirs.js +9 -0
  26. package/lib/src/utils/renderTemplate.js +22 -0
  27. package/lib/src/utils/titleCase.js +5 -10
  28. package/lib/src/utils/writeFile.js +3 -10
  29. package/lib/templates/api/database/connection.ts.ejs +40 -0
  30. package/lib/templates/api/database/db-index.ts.ejs +2 -0
  31. package/lib/templates/api/database/index.ts.ejs +1 -0
  32. package/lib/templates/api/database/queries-entity.ts.ejs +98 -0
  33. package/lib/templates/api/database/queries-index.ts.ejs +1 -0
  34. package/lib/templates/api/database/schema.prisma.ejs +25 -0
  35. package/lib/templates/api/express/network/index.ts.ejs +2 -0
  36. package/lib/templates/api/express/network/models/entity.ts.ejs +21 -0
  37. package/lib/templates/api/express/network/models/index.ts.ejs +1 -0
  38. package/lib/templates/api/express/network/resolvers/entity.ts.ejs +41 -0
  39. package/lib/templates/api/express/network/resolvers/index.ts.ejs +13 -0
  40. package/lib/templates/api/express/network/response.ts.ejs +17 -0
  41. package/lib/templates/api/express/network/router.ts.ejs +39 -0
  42. package/lib/templates/api/express/network/routes/docs.ts.ejs +43 -0
  43. package/lib/templates/api/express/network/routes/entity.ts.ejs +165 -0
  44. package/lib/templates/api/express/network/routes/home.ts.ejs +16 -0
  45. package/lib/templates/api/express/network/routes/index.ts.ejs +5 -0
  46. package/lib/templates/api/express/network/server.ts.ejs +139 -0
  47. package/lib/templates/api/express/network/utils/index.ts.ejs +45 -0
  48. package/lib/templates/api/express/types/graphQL/context.d.ts.ejs +3 -0
  49. package/lib/templates/api/express/types/index.d.ts.ejs +4 -0
  50. package/lib/templates/api/fastify/network/index.ts.ejs +2 -0
  51. package/lib/templates/api/fastify/network/models/entity.ts.ejs +21 -0
  52. package/lib/templates/api/fastify/network/models/index.ts.ejs +1 -0
  53. package/lib/templates/api/fastify/network/resolvers/entity.ts.ejs +41 -0
  54. package/lib/templates/api/fastify/network/resolvers/index.ts.ejs +13 -0
  55. package/lib/templates/api/fastify/network/response.ts.ejs +17 -0
  56. package/lib/templates/api/fastify/network/router.ts.ejs +36 -0
  57. package/lib/templates/api/fastify/network/routes/docs.ts.ejs +41 -0
  58. package/lib/templates/api/fastify/network/routes/entity.ts.ejs +116 -0
  59. package/lib/templates/api/fastify/network/routes/home.ts.ejs +15 -0
  60. package/lib/templates/api/fastify/network/routes/index.ts.ejs +5 -0
  61. package/lib/templates/api/fastify/network/server.ts.ejs +129 -0
  62. package/lib/templates/api/fastify/types/graphQL/context.d.ts.ejs +3 -0
  63. package/lib/templates/api/fastify/types/index.d.ts.ejs +4 -0
  64. package/lib/templates/api/hono/network/index.ts.ejs +2 -0
  65. package/lib/templates/api/hono/network/models/entity.ts.ejs +21 -0
  66. package/lib/templates/api/hono/network/models/index.ts.ejs +1 -0
  67. package/lib/templates/api/hono/network/resolvers/entity.ts.ejs +41 -0
  68. package/lib/templates/api/hono/network/resolvers/index.ts.ejs +13 -0
  69. package/lib/templates/api/hono/network/response.ts.ejs +18 -0
  70. package/lib/templates/api/hono/network/router.ts.ejs +45 -0
  71. package/lib/templates/api/hono/network/routes/docs.ts.ejs +39 -0
  72. package/lib/templates/api/hono/network/routes/entity.ts.ejs +104 -0
  73. package/lib/templates/api/hono/network/routes/home.ts.ejs +15 -0
  74. package/lib/templates/api/hono/network/routes/index.ts.ejs +5 -0
  75. package/lib/templates/api/hono/network/server.ts.ejs +160 -0
  76. package/lib/templates/api/hono/network/utils/index.ts.ejs +23 -0
  77. package/lib/templates/api/hono/types/graphQL/context.d.ts.ejs +3 -0
  78. package/lib/templates/api/hono/types/index.d.ts.ejs +4 -0
  79. package/lib/templates/api/schemas/entity.ts.ejs +43 -0
  80. package/lib/templates/api/schemas/id.ts.ejs +11 -0
  81. package/lib/templates/api/schemas/index.ts.ejs +2 -0
  82. package/lib/templates/api/services/BaseHttp.ts.ejs +73 -0
  83. package/lib/templates/api/services/entity.ts.ejs +75 -0
  84. package/lib/templates/api/services/index.ts.ejs +1 -0
  85. package/lib/templates/api/services/utils/index.ts.ejs +1 -0
  86. package/lib/templates/api/services/utils/messages/entity.ts.ejs +11 -0
  87. package/lib/templates/api/services/utils/messages/index.ts.ejs +6 -0
  88. package/lib/templates/api/utils/Logger.ts.ejs +41 -0
  89. package/lib/templates/api/utils/index.ts.ejs +1 -0
  90. package/lib/templates/config/.dockerignore.ejs +20 -0
  91. package/lib/templates/config/.env.ejs +3 -0
  92. package/lib/templates/config/.gitignore.ejs +129 -0
  93. package/lib/templates/config/CHANGELOG.md.ejs +1 -0
  94. package/lib/templates/config/Dockerfile.ejs +13 -0
  95. package/lib/templates/config/README.md.ejs +3 -0
  96. package/lib/templates/config/ghat/lint.yml.ejs +46 -0
  97. package/lib/templates/config/ghat/test.yml.ejs +29 -0
  98. package/lib/templates/config/index.ts.ejs +3 -0
  99. package/lib/templates/config/package.json.ejs +30 -0
  100. package/lib/templates/config/test/index.test.ts.ejs +260 -0
  101. package/lib/templates/config/tsconfig.base.json.ejs +43 -0
  102. package/lib/templates/config/tsconfig.json.ejs +16 -0
  103. package/lib/templates/config/vitest.config.ts.ejs +19 -0
  104. package/package.json +45 -17
  105. package/lib/src/functions/api/types.js +0 -108
  106. package/lib/src/functions/eslint.js +0 -125
@@ -1,120 +1,19 @@
1
1
  const writeFile = require('../utils/writeFile')
2
+ const { renderTemplate } = require('../utils/renderTemplate')
2
3
 
3
4
  /**
4
- * @param {String} projectName
5
+ * @param {Object} args
6
+ * @param {String} args.projectName
7
+ * @param {Boolean} args.graphQL
5
8
  * @returns {Promise<void>}
6
9
  */
7
- module.exports = async projectName => {
8
- const data = {
9
- base: {
10
- content: `{
11
- "ts-node": {
12
- "files": true,
13
- "require": ["tsconfig-paths/register"]
14
- },
15
- "compilerOptions": {
16
- /* Visit https://aka.ms/tsconfig.json to read more about this file */
17
-
18
- /* Basic Options */
19
- // "incremental": true, /* Enable incremental compilation */
20
- "target": "ES2022" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2022', or 'ESNEXT'. */,
21
- "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', 'ES2022', or 'ESNext'. */,
22
- "lib": ["es2022", "esnext.asynciterable"], /* Specify library files to be included in the compilation. */
23
- "allowJs": true /* Allow javascript files to be compiled. */,
24
- // "checkJs": true, /* Report errors in .js files. */
25
- // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
26
- // "declaration": true, /* Generates corresponding '.d.ts' file. */
27
- // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
28
- // "outFile": "./", /* Concatenate and emit output to single file. */
29
- "outDir": "dist" /* Redirect output structure to the directory. */,
30
- "rootDir": "src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
31
- // "composite": true, /* Enable project compilation */
32
- // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
33
- // "removeComments": true, /* Do not emit comments to output. */
34
- // "noEmit": true, /* Do not emit outputs. */
35
- // "importHelpers": true, /* Import emit helpers from 'tslib'. */
36
- "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
37
- // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
38
-
39
- /* Strict Type-Checking Options */
40
- "strict": true /* Enable all strict type-checking options. */,
41
- "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
42
- "strictNullChecks": true, /* Enable strict null checks. */
43
- "strictFunctionTypes": true, /* Enable strict checking of function types. */
44
- "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
45
- "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
46
- "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
47
- "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
48
-
49
- /* Additional Checks */
50
- "noUnusedLocals": true, /* Report errors on unused locals. */
51
- "noUnusedParameters": true, /* Report errors on unused parameters. */
52
- "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
53
- "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
54
-
55
- /* Module Resolution Options */
56
- "moduleResolution": "node" /* Specify module resolution strategy: 'node', 'classic', or 'bundler' (modern). */,
57
- "baseUrl": "src", /* Base directory to resolve non-absolute module names. */
58
- // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
59
- // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
60
- // "typeRoots": [], /* List of folders to include type definitions from. */
61
- // "types": [], /* Type declaration files to be included in compilation. */
62
- "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
63
- "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
64
- // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
65
- // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
66
- "resolveJsonModule": true /* Allow import .json files */,
67
-
68
- /* Source Map Options */
69
- "sourceMap": false /* Generates corresponding '.map' file. */,
70
- // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
71
- // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
72
- // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
73
- // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
74
-
75
- /* Experimental Options */
76
- "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
77
- "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
78
-
79
- /* Advanced Options */
80
- "skipLibCheck": true /* Skip type checking of declaration files. */,
81
- "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
82
- },
83
- "include": [
84
- "src",
85
- "test",
86
- "tests",
87
- "__test__"
88
- ],
89
- "exclude": [
90
- "node_modules"
91
- ]
92
- }
93
- `,
94
- file: 'tsconfig.base.json'
95
- },
96
- prod: {
97
- content: `{
98
- "compilerOptions": {
99
- "rootDir": "src"
100
- },
101
- "extends": "./tsconfig.base.json",
102
- "exclude": [
103
- "node_modules",
104
- "test",
105
- "tests",
106
- "__test__",
107
- "dist"
108
- ],
109
- "include": [
110
- "src"
111
- ]
112
- }
113
- `,
114
- file: 'tsconfig.json'
115
- }
116
- }
117
-
118
- await writeFile(`${projectName}/${data.base.file}`, data.base.content)
119
- await writeFile(`${projectName}/${data.prod.file}`, data.prod.content)
10
+ module.exports = async ({ projectName, graphQL = false }) => {
11
+ await writeFile(
12
+ `${projectName}/tsconfig.base.json`,
13
+ renderTemplate('config/tsconfig.base.json.ejs', { graphQL })
14
+ )
15
+ await writeFile(
16
+ `${projectName}/tsconfig.json`,
17
+ renderTemplate('config/tsconfig.json.ejs')
18
+ )
120
19
  }
package/lib/src/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  const cliProgress = require('cli-progress')
2
- const colors = require('colors')
2
+ const pc = require('picocolors')
3
3
  const util = require('node:util')
4
4
  const exec = util.promisify(require('node:child_process').exec)
5
+ const mkdirs = require('./utils/mkdirs')
5
6
 
6
7
  const {
7
8
  packageJson,
@@ -14,7 +15,7 @@ const {
14
15
  docker,
15
16
  api,
16
17
  testsF,
17
- ghatF
18
+ ghat: ghatF
18
19
  } = require('./functions')
19
20
 
20
21
  /**
@@ -22,13 +23,13 @@ const {
22
23
  * @returns {CliOptions}
23
24
  */
24
25
  const setOptions = process => {
25
- const format = `${colors.bold(
26
+ const format = `${pc.bold(
26
27
  'Packages installation progress'
27
- )} ${colors.cyan('[{bar}]')} ${colors.blue('{percentage}%')} | ${colors.bold(
28
+ )} ${pc.cyan('[{bar}]')} ${pc.blue('{percentage}%')} | ${pc.bold(
28
29
  'Current process:'
29
- )} ${colors.yellow('{value}')} of ${process} | ${colors.bold(
30
+ )} ${pc.yellow('{value}')} of ${process} | ${pc.bold(
30
31
  'Duration:'
31
- )} ${colors.green('{duration_formatted}')}`
32
+ )} ${pc.green('{duration_formatted}')}`
32
33
 
33
34
  return { format, hideCursor: false, synchronousUpdate: false }
34
35
  }
@@ -47,9 +48,11 @@ module.exports = async ({
47
48
  manager,
48
49
  mainFile,
49
50
  fastify,
51
+ hono,
50
52
  graphql,
51
53
  ghat,
52
- database
54
+ database,
55
+ entityContext
53
56
  }) => {
54
57
  const process = 5
55
58
  let i = 0
@@ -58,15 +61,27 @@ module.exports = async ({
58
61
  options,
59
62
  cliProgress.Presets.shades_classic
60
63
  )
61
- const expressProdPackages = 'express swagger-ui-express cors'
64
+ const expressProdPackages =
65
+ 'express swagger-ui-express @asteasolutions/zod-to-openapi cors'
62
66
  const fastifyProdPackages = `fastify @fastify/swagger @fastify/swagger-ui @fastify/cors fastify-type-provider-zod ${
63
67
  graphql ? '@as-integrations/fastify' : ''
64
68
  }`
69
+ const honoProdPackages =
70
+ 'hono @hono/node-server @hono/zod-openapi @hono/swagger-ui'
71
+
72
+ const frameworkProdPackages = hono
73
+ ? honoProdPackages
74
+ : fastify
75
+ ? fastifyProdPackages
76
+ : expressProdPackages
77
+
65
78
  let prodPackages = `${manager} debug zod http-errors @prisma/client@6 ${
66
79
  graphql
67
- ? `@apollo/server ${!fastify ? '@as-integrations/express5' : ''} class-validator graphql graphql-scalars reflect-metadata type-graphql@2.0.0-rc.3`
80
+ ? `@apollo/server ${
81
+ !fastify && !hono ? '@as-integrations/express5' : ''
82
+ } class-validator graphql graphql-scalars reflect-metadata type-graphql@2.0.0-rc.3`
68
83
  : ''
69
- } ${fastify ? fastifyProdPackages : expressProdPackages}`
84
+ } ${frameworkProdPackages}`
70
85
 
71
86
  switch (database) {
72
87
  case 'mongo':
@@ -97,26 +112,27 @@ module.exports = async ({
97
112
  axios \
98
113
  dotenv \
99
114
  nodemon \
100
- standard-version \
101
- ts-loader \
102
- ts-node \
103
- tsconfig-paths \
115
+ commit-and-tag-version \
116
+ tsx \
104
117
  typescript`
105
118
 
106
119
  const expressDevPackages =
107
120
  '@types/express @types/swagger-ui-express @types/cors'
108
121
  const fastifyDevPackages = ''
122
+ const honoDevPackages = ''
109
123
 
110
124
  devPackages += ` ${
111
- fastify ? fastifyDevPackages : expressDevPackages
112
- } @jest/types @types/jest jest jest-unit ts-jest`
125
+ hono ? honoDevPackages : fastify ? fastifyDevPackages : expressDevPackages
126
+ } vitest vite-tsconfig-paths`
127
+
128
+ if (graphql) devPackages += ' unplugin-swc @swc/core'
113
129
 
114
- // Biome no necesita plugins adicionales de Jest como ESLint
130
+ // Biome no necesita plugins adicionales de Vitest como ESLint
115
131
 
116
132
  bar.start(process, i)
117
133
 
118
134
  try {
119
- await exec(`mkdir ${projectName}`)
135
+ await mkdirs(projectName)
120
136
  bar.update(++i)
121
137
 
122
138
  const dbIsSQL = database !== 'mongo'
@@ -132,11 +148,19 @@ typescript`
132
148
  readme(projectName, projectDescription),
133
149
  changelog(projectName),
134
150
  gitignore(projectName),
135
- tsconfig(projectName),
151
+ tsconfig({ projectName, graphQL: graphql }),
136
152
  biome({ projectName }),
137
153
  docker({ projectName, manager }),
138
- api({ projectName, version, email, fastify, graphql, database }),
139
- testsF({ projectName, graphql, dbIsSQL }),
154
+ api({
155
+ projectName,
156
+ email,
157
+ fastify,
158
+ hono,
159
+ graphql,
160
+ database,
161
+ entityContext
162
+ }),
163
+ testsF({ projectName, graphql, dbIsSQL, entityContext }),
140
164
  exec('git init', { cwd: `./${projectName}` })
141
165
  ]
142
166
 
@@ -0,0 +1,115 @@
1
+ const pluralize = require('pluralize')
2
+
3
+ /**
4
+ * Build all naming variants for a given entity name.
5
+ *
6
+ * @param {string} raw PascalCase entity name (e.g. "Product", "User")
7
+ * @returns {EntityNames}
8
+ */
9
+ const buildEntityNames = raw => {
10
+ // Ensure PascalCase
11
+ const pascal = raw.charAt(0).toUpperCase() + raw.slice(1)
12
+ const camel = pascal.charAt(0).toLowerCase() + pascal.slice(1)
13
+ const plural = pluralize(camel)
14
+ const pluralPascal = plural.charAt(0).toUpperCase() + plural.slice(1)
15
+
16
+ return { pascal, camel, plural, pluralPascal }
17
+ }
18
+
19
+ /**
20
+ * Build the field definitions for an entity.
21
+ * - Default "User" gets `name` + `lastName`
22
+ * - Any other entity gets `name` + `description`
23
+ *
24
+ * @param {string} entityPascal PascalCase entity name
25
+ * @returns {EntityFieldSet}
26
+ */
27
+ const buildEntityFields = entityPascal => {
28
+ const isUser = entityPascal === 'User'
29
+
30
+ const fields = isUser
31
+ ? [
32
+ { name: 'lastName', prismaType: 'String', zodType: 'z.string()' },
33
+ { name: 'name', prismaType: 'String', zodType: 'z.string()' }
34
+ ]
35
+ : [
36
+ { name: 'name', prismaType: 'String', zodType: 'z.string()' },
37
+ {
38
+ name: 'description',
39
+ prismaType: 'String',
40
+ zodType: 'z.string()'
41
+ }
42
+ ]
43
+
44
+ const sampleData = isUser
45
+ ? { lastName: "'Lzq'", name: "'Anthony'" }
46
+ : { name: "'Test'", description: "'A test item'" }
47
+
48
+ const updateData = isUser
49
+ ? { lastName: "'Luzquiños'", name: "'Anthony'" }
50
+ : { name: "'Updated'", description: "'Updated description'" }
51
+
52
+ return { fields, sampleData, updateData, isUser }
53
+ }
54
+
55
+ /**
56
+ * Build the full entity context object to pass into EJS templates.
57
+ *
58
+ * @param {string} raw PascalCase entity name (default: "User")
59
+ * @returns {EntityContext}
60
+ */
61
+ const buildEntityContext = (raw = 'User') => {
62
+ const names = buildEntityNames(raw)
63
+ const fieldSet = buildEntityFields(names.pascal)
64
+
65
+ return {
66
+ // Naming variants
67
+ Entity: names.pascal,
68
+ entity: names.camel,
69
+ entities: names.plural,
70
+ EntitiesPlural: names.pluralPascal,
71
+
72
+ // Fields
73
+ entityFields: fieldSet.fields,
74
+ sampleData: fieldSet.sampleData,
75
+ updateData: fieldSet.updateData,
76
+ isDefaultEntity: fieldSet.isUser
77
+ }
78
+ }
79
+
80
+ module.exports = { buildEntityNames, buildEntityFields, buildEntityContext }
81
+
82
+ /**
83
+ * @typedef {Object} EntityNames
84
+ * @property {string} pascal e.g. "Product"
85
+ * @property {string} camel e.g. "product"
86
+ * @property {string} plural e.g. "products"
87
+ * @property {string} pluralPascal e.g. "Products"
88
+ */
89
+
90
+ /**
91
+ * @typedef {Object} EntityField
92
+ * @property {string} name
93
+ * @property {string} prismaType
94
+ * @property {string} zodType
95
+ */
96
+
97
+ /**
98
+ * @typedef {Object} EntityFieldSet
99
+ * @property {EntityField[]} fields
100
+ * @property {Object<string,string>} sampleData
101
+ * @property {Object<string,string>} updateData
102
+ * @property {boolean} isUser
103
+ */
104
+
105
+ /**
106
+ * @typedef {Object} EntityContext
107
+ * @property {string} Entity PascalCase name
108
+ * @property {string} entity camelCase name
109
+ * @property {string} entities plural lowercase name
110
+ * @property {string} EntitiesPlural plural PascalCase name
111
+ * @property {EntityField[]} entityFields field definitions
112
+ * @property {Object<string,string>} sampleData sample create data
113
+ * @property {Object<string,string>} updateData sample update data
114
+ * @property {boolean} isDefaultEntity true if entity is "User"
115
+ */
@@ -1,5 +1,7 @@
1
1
  module.exports = {
2
2
  constants: require('./constants'),
3
+ entity: require('./entity'),
4
+ renderTemplate: require('./renderTemplate').renderTemplate,
3
5
  titleCase: require('./titleCase'),
4
6
  writeFile: require('./writeFile')
5
7
  }
@@ -0,0 +1,9 @@
1
+ const { mkdir } = require('node:fs/promises')
2
+
3
+ /**
4
+ * Creates multiple directories recursively (cross-platform, no shell spawn).
5
+ * @param {...String} dirs - Directory paths to create.
6
+ * @returns {Promise<void>}
7
+ */
8
+ module.exports = (...dirs) =>
9
+ Promise.all(dirs.map(dir => mkdir(dir, { recursive: true })))
@@ -0,0 +1,22 @@
1
+ const { readFileSync } = require('node:fs')
2
+ const { join } = require('node:path')
3
+ const ejs = require('ejs')
4
+
5
+ const TEMPLATES_DIR = join(__dirname, '..', '..', 'templates')
6
+
7
+ /**
8
+ * Render an EJS template file with the given data.
9
+ * @param {string} templatePath - Relative path from the templates/ directory (e.g., 'api/services/BaseHttp.ts.ejs')
10
+ * @param {Record<string, unknown>} data - Variables to pass to the template
11
+ * @returns {string} The rendered template content
12
+ */
13
+ const renderTemplate = (templatePath, data = {}) => {
14
+ const fullPath = join(TEMPLATES_DIR, templatePath)
15
+ const template = readFileSync(fullPath, 'utf-8')
16
+
17
+ return ejs.render(template, data, {
18
+ filename: fullPath
19
+ })
20
+ }
21
+
22
+ module.exports = { renderTemplate, TEMPLATES_DIR }
@@ -3,14 +3,9 @@
3
3
  * @returns {String}
4
4
  */
5
5
  module.exports = projectName => {
6
- const pn = projectName.toLowerCase().split('-')
7
- let title = ''
8
-
9
- pn.forEach((word, index, array) => {
10
- title += `${word[0].toUpperCase()}${word.slice(1)} `
11
-
12
- if (index !== array.length - 1) title += ' '
13
- })
14
-
15
- return title.trim()
6
+ return projectName
7
+ .toLowerCase()
8
+ .split('-')
9
+ .map(word => word[0].toUpperCase() + word.slice(1))
10
+ .join(' ')
16
11
  }
@@ -1,15 +1,8 @@
1
- const { writeFile } = require('node:fs')
1
+ const { writeFile } = require('node:fs/promises')
2
2
 
3
3
  /**
4
4
  * @param {String} filename
5
5
  * @param {String} data
6
- * @returns {Promise<String>}
6
+ * @returns {Promise<void>}
7
7
  */
8
- module.exports = (filename, data) => {
9
- return new Promise((resolve, reject) => {
10
- writeFile(filename, data, error => {
11
- if (error) reject(error.message)
12
- else resolve('Saved successfully')
13
- })
14
- })
15
- }
8
+ module.exports = (filename, data) => writeFile(filename, data, 'utf8')
@@ -0,0 +1,40 @@
1
+ import { PrismaClient } from '@prisma/client'
2
+ import { type Debugger } from 'debug'
3
+
4
+ let dbConnected = false
5
+
6
+ declare global {
7
+ // eslint-disable-next-line no-var
8
+ var __client__: PrismaClient
9
+ }
10
+
11
+ const dbConnection = (
12
+ d?: Debugger
13
+ ): {
14
+ connect: () => Promise<PrismaClient>
15
+ disconnect: () => Promise<boolean>
16
+ } => {
17
+ return {
18
+ connect: async () => {
19
+ if (!global.__client__) {
20
+ global.__client__ = new PrismaClient()
21
+ await global.__client__.$connect()
22
+ dbConnected = true
23
+ d?.('<%- dbPrettyName %> connection established.')
24
+ }
25
+
26
+ return global.__client__
27
+ },
28
+ disconnect: async () => {
29
+ if (global.__client__) {
30
+ dbConnected = false
31
+ await global.__client__.$disconnect()
32
+ d?.('<%- dbPrettyName %> connection closed.')
33
+ }
34
+
35
+ return dbConnected
36
+ }
37
+ }
38
+ }
39
+
40
+ export { dbConnection }
@@ -0,0 +1,2 @@
1
+ export * from './connection'
2
+ export * from './queries'
@@ -0,0 +1 @@
1
+ export * from './<%- db %>'
@@ -0,0 +1,98 @@
1
+ import { <%- Entity %> } from '@prisma/client'
2
+ import debug from 'debug'
3
+
4
+ import { dbConnection } from '../connection'
5
+ import { Id, <%- Entity %> as <%- Entity %>Schema, <%- Entity %>DTO } from 'schemas'
6
+ import { Logger } from 'utils'
7
+
8
+ const logger = new Logger(debug('App:Database:Queries:<%- Entity %>'), 'queries/<%- entity %>.ts')
9
+
10
+ const <%- entity %>DBOtoDTO = (<%- entity %>DBO: <%- Entity %>) =>
11
+ ({
12
+ ...<%- entity %>DBO,
13
+ createdAt: <%- entity %>DBO.createdAt.toISOString(),
14
+ updatedAt: <%- entity %>DBO.updatedAt.toISOString()
15
+ }) satisfies <%- Entity %>DTO
16
+
17
+ const store = async (<%- entity %>Data: <%- Entity %>Schema) => {
18
+ try {
19
+ const client = await dbConnection().connect()
20
+ const <%- entity %> = await client.<%- entity %>.create({
21
+ data: <%- entity %>Data
22
+ })
23
+
24
+ return <%- entity %>DBOtoDTO(<%- entity %>)
25
+ } catch (error) {
26
+ logger.log({
27
+ method: store.name,
28
+ value: 'error',
29
+ content: error
30
+ })
31
+
32
+ return null
33
+ }
34
+ }
35
+
36
+ const removeById = async (id: Id) => {
37
+ try {
38
+ const client = await dbConnection().connect()
39
+ await client.<%- entity %>.delete({
40
+ where: { id }
41
+ })
42
+
43
+ return true
44
+ } catch (error) {
45
+ logger.log({
46
+ method: removeById.name,
47
+ value: 'error',
48
+ content: error
49
+ })
50
+
51
+ return false
52
+ }
53
+ }
54
+
55
+ const getById = async (id: Id) => {
56
+ try {
57
+ const client = await dbConnection().connect()
58
+ const <%- entity %> = await client.<%- entity %>.findUnique({
59
+ where: { id }
60
+ })
61
+
62
+ if (!<%- entity %>) return null
63
+
64
+ return <%- entity %>DBOtoDTO(<%- entity %>)
65
+ } catch (error) {
66
+ logger.log({
67
+ method: getById.name,
68
+ value: 'error',
69
+ content: error
70
+ })
71
+
72
+ return null
73
+ }
74
+ }
75
+
76
+ const update = async (id: Id, <%- entity %>: <%- Entity %>Schema) => {
77
+ try {
78
+ const client = await dbConnection().connect()
79
+ const <%- entity %>Updated = await client.<%- entity %>.update({
80
+ where: { id },
81
+ data: <%- entity %>
82
+ })
83
+
84
+ if (!<%- entity %>Updated) return null
85
+
86
+ return <%- entity %>DBOtoDTO(<%- entity %>Updated)
87
+ } catch (error) {
88
+ logger.log({
89
+ method: update.name,
90
+ value: 'error',
91
+ content: error
92
+ })
93
+
94
+ return null
95
+ }
96
+ }
97
+
98
+ export { store, removeById, getById, update }
@@ -0,0 +1 @@
1
+ export * from './<%-entity%>'
@@ -0,0 +1,25 @@
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ }
4
+
5
+ datasource db {
6
+ provider = "<%- dbPrismaName %>"
7
+ url = <% if (db !== 'sqlite') { %>env("DATABASE_URL")<% } else { %>"file:./dev.db"<% } %>
8
+ }
9
+
10
+ model <%- Entity %> {
11
+ <% if (isMongo) { -%>
12
+ id String @id @default(auto()) @map("_id") @db.ObjectId
13
+ <% } else { -%>
14
+ id Int @id @default(autoincrement())
15
+ <% } -%>
16
+ <% const maxFieldLen = Math.max(...entityFields.map(f => f.name.length), 0); -%>
17
+ <% entityFields.forEach(function(f) { -%>
18
+ <%- f.name.padEnd(maxFieldLen) %> <%- f.prismaType %>
19
+ <% }) -%>
20
+
21
+ createdAt DateTime @default(now())
22
+ updatedAt DateTime @updatedAt
23
+
24
+ @@map("<%- entities %>")
25
+ }
@@ -0,0 +1,2 @@
1
+ export * from './routes'
2
+ export * from './server'
@@ -0,0 +1,21 @@
1
+ import 'reflect-metadata'
2
+ import { Field, <%- dbIsSQL ? 'Int' : 'ID' %>, ObjectType } from 'type-graphql'
3
+
4
+ @ObjectType()
5
+ class <%- Entity %> {
6
+ @Field(() => <%- dbIsSQL ? 'Int' : 'ID' %>)
7
+ id!: number
8
+
9
+ <% entityFields.forEach(function(f) { -%>
10
+ @Field()
11
+ <%- f.name %>!: string
12
+
13
+ <% }) -%>
14
+ @Field({ nullable: true })
15
+ createdAt?: string
16
+
17
+ @Field({ nullable: true })
18
+ updatedAt?: string
19
+ }
20
+
21
+ export { <%- Entity %> }
@@ -0,0 +1 @@
1
+ export * from './<%-Entity%>'