@expressots/cli 1.4.0 → 1.6.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.
@@ -4,6 +4,14 @@ export declare const enum Pattern {
4
4
  PASCAL_CASE = "PascalCase",
5
5
  CAMEL_CASE = "camelCase"
6
6
  }
7
+ interface IProviders {
8
+ prisma?: {
9
+ schemaName: string;
10
+ schemaPath: string;
11
+ entitiesPath: string;
12
+ entityNamePattern: string;
13
+ };
14
+ }
7
15
  /**
8
16
  * The configuration object for the Expresso CLI.
9
17
  *
@@ -17,4 +25,6 @@ export interface ExpressoConfig {
17
25
  scaffoldPattern: Pattern;
18
26
  sourceRoot: string;
19
27
  opinionated: boolean;
28
+ providers?: IProviders;
20
29
  }
30
+ export {};
package/bin/cli.js CHANGED
@@ -7,14 +7,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.CLI_VERSION = void 0;
8
8
  const yargs_1 = __importDefault(require("yargs"));
9
9
  const helpers_1 = require("yargs/helpers");
10
+ const project_commands_1 = require("./commands/project.commands");
10
11
  const generate_1 = require("./generate");
11
12
  const info_1 = require("./info");
12
13
  const new_1 = require("./new");
14
+ const providers_1 = require("./providers");
13
15
  exports.CLI_VERSION = "1.3.4";
14
16
  console.log(`\n[🐎 Expressots]\n`);
15
17
  (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
16
18
  .scriptName("expressots")
19
+ .command(project_commands_1.runCommandModule)
17
20
  .command((0, new_1.createProject)())
21
+ .command((0, providers_1.generateProviders)())
18
22
  .command((0, generate_1.generateProject)())
19
23
  .command((0, info_1.infoProject)())
20
24
  .example("$0 new expressots-demo", "Create interactively")
@@ -0,0 +1,8 @@
1
+ import { CommandModule } from "yargs";
2
+ export declare const runCommandModule: CommandModule<{}, {
3
+ command: string;
4
+ }>;
5
+ declare const runCommand: ({ command }: {
6
+ command: string;
7
+ }) => Promise<void>;
8
+ export { runCommand };
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.runCommand = exports.runCommandModule = void 0;
7
+ const child_process_1 = require("child_process");
8
+ const fs_1 = require("fs");
9
+ const path_1 = __importDefault(require("path"));
10
+ const compiler_1 = __importDefault(require("../utils/compiler"));
11
+ /**
12
+ * Load the configuration from the compiler
13
+ * @param compiler The compiler to load the configuration from
14
+ * @returns The configuration
15
+ */
16
+ const opinionatedConfig = [
17
+ "--transpile-only",
18
+ "-r",
19
+ "dotenv/config",
20
+ "-r",
21
+ "tsconfig-paths/register",
22
+ "./src/main.ts",
23
+ ];
24
+ const nonOpinionatedConfig = [
25
+ "--transpile-only",
26
+ "-r",
27
+ "dotenv/config",
28
+ "./src/main.ts",
29
+ ];
30
+ /**
31
+ * Helper function to execute a command
32
+ * @param command The command to execute
33
+ * @param args The arguments to pass to the command
34
+ * @param cwd The current working directory to execute the command in
35
+ * @returns A promise that resolves when the command completes successfully
36
+ */
37
+ function execCmd(command, args, cwd = process.cwd()) {
38
+ return new Promise((resolve, reject) => {
39
+ const proc = (0, child_process_1.spawn)(command, args, {
40
+ stdio: "inherit",
41
+ shell: true,
42
+ cwd,
43
+ });
44
+ proc.on("close", (code) => {
45
+ if (code === 0) {
46
+ resolve();
47
+ }
48
+ else {
49
+ reject(new Error(`Command failed with code ${code}`));
50
+ }
51
+ });
52
+ });
53
+ }
54
+ // Helper to delete the dist directory
55
+ const cleanDist = async () => {
56
+ await fs_1.promises.rm("./dist", { recursive: true, force: true });
57
+ };
58
+ // Helper to compile TypeScript
59
+ const compileTypescript = async () => {
60
+ await execCmd("npx", ["tsc", "-p", "tsconfig.build.json"]);
61
+ };
62
+ // Helper to copy files
63
+ const copyFiles = async () => {
64
+ const { opinionated } = await compiler_1.default.loadConfig();
65
+ let filesToCopy = [];
66
+ if (opinionated) {
67
+ filesToCopy = [
68
+ "./register-path.js",
69
+ "tsconfig.build.json",
70
+ "package.json",
71
+ ];
72
+ }
73
+ else {
74
+ filesToCopy = ["tsconfig.json", "package.json"];
75
+ }
76
+ filesToCopy.forEach((file) => {
77
+ fs_1.promises.copyFile(file, path_1.default.join("./dist", path_1.default.basename(file)));
78
+ });
79
+ };
80
+ // eslint-disable-next-line @typescript-eslint/ban-types
81
+ exports.runCommandModule = {
82
+ command: "run <command>",
83
+ describe: "Runs a specified command (dev, build, prod)",
84
+ builder: (yargs) => {
85
+ return yargs.positional("command", {
86
+ describe: "The command to run",
87
+ type: "string",
88
+ choices: ["dev", "build", "prod"],
89
+ });
90
+ },
91
+ handler: async (argv) => {
92
+ const { command } = argv;
93
+ // Now call your original runCommand function with the command
94
+ // Ensure runCommand is properly defined to handle these commands
95
+ await runCommand({ command });
96
+ },
97
+ };
98
+ const runCommand = async ({ command }) => {
99
+ const { opinionated } = await compiler_1.default.loadConfig();
100
+ try {
101
+ switch (command) {
102
+ case "dev":
103
+ // Use execSync or spawn to run ts-node-dev programmatically
104
+ execCmd("tsnd", opinionated ? opinionatedConfig : nonOpinionatedConfig);
105
+ break;
106
+ case "build":
107
+ await cleanDist();
108
+ await compileTypescript();
109
+ await copyFiles();
110
+ break;
111
+ case "prod":
112
+ let config = [];
113
+ if (opinionated) {
114
+ config = [
115
+ "-r",
116
+ "dotenv/config",
117
+ "-r",
118
+ "./dist/register-path.js",
119
+ "./dist/src/main.js",
120
+ ];
121
+ }
122
+ else {
123
+ config = ["-r", "dotenv/config", "./dist/main.js"];
124
+ }
125
+ // Ensure environment variables are set
126
+ execCmd("node", config);
127
+ break;
128
+ default:
129
+ console.log(`Unknown command: ${command}`);
130
+ }
131
+ }
132
+ catch (error) {
133
+ console.error("Error executing command:", error);
134
+ }
135
+ };
136
+ exports.runCommand = runCommand;
@@ -16,8 +16,10 @@ const coerceSchematicAliases = (arg) => {
16
16
  return "provider";
17
17
  case "e":
18
18
  return "entity";
19
- case "m":
19
+ case "mo":
20
20
  return "module";
21
+ case "m":
22
+ return "middleware";
21
23
  default:
22
24
  return arg;
23
25
  }
@@ -37,6 +39,7 @@ const generateProject = () => {
37
39
  "provider",
38
40
  "entity",
39
41
  "module",
42
+ "middleware",
40
43
  ],
41
44
  describe: "The schematic to generate",
42
45
  type: "string",
@@ -271,7 +271,6 @@ const splitTarget = async ({ target, schematic, }) => {
271
271
  if (target.includes("/") ||
272
272
  target.includes("\\") ||
273
273
  target.includes("//")) {
274
- //pathContent = target.split("/").filter((item) => item !== "");
275
274
  if (schematic === "service")
276
275
  schematic = "controller";
277
276
  if (schematic === "service" ||
@@ -366,6 +365,10 @@ const schematicFolder = (schematic) => {
366
365
  return "providers";
367
366
  case "entity":
368
367
  return "entities";
368
+ case "middleware":
369
+ return "providers/middlewares";
370
+ case "module":
371
+ return "useCases";
369
372
  }
370
373
  return undefined;
371
374
  };
@@ -0,0 +1,10 @@
1
+ import { ExpressoMiddleware } from "@expressots/core";
2
+ import { NextFunction, Request, Response } from "express";
3
+ import { provide } from "inversify-binding-decorators";
4
+
5
+ @provide({{className}}Middleware)
6
+ export class {{className}}Middleware extends ExpressoMiddleware {
7
+ use(req: Request, res: Response, next: NextFunction): void | Promise<void> {
8
+ throw new Error("Method not implemented.");
9
+ }
10
+ }
@@ -1,3 +1,4 @@
1
+ import { ContainerModule } from "inversify";
1
2
  import { CreateModule } from "@expressots/core";
2
3
 
3
- export const {{moduleName}}Module = CreateModule([]);
4
+ export const {{moduleName}}Module: ContainerModule = CreateModule([]);
@@ -1,4 +1,5 @@
1
+ import { ContainerModule } from "inversify";
1
2
  import { CreateModule } from "@expressots/core";
2
3
  import { {{className}}Controller } from "{{{path}}}";
3
4
 
4
- export const {{moduleName}}Module = CreateModule([{{className}}Controller]);
5
+ export const {{moduleName}}Module: ContainerModule = CreateModule([{{className}}Controller]);
package/bin/new/cli.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { CommandModule } from "yargs";
2
- type CommandModuleArgs = {};
2
+ type CommandModuleArgs = object;
3
3
  declare const createProject: () => CommandModule<CommandModuleArgs, any>;
4
4
  export { createProject };
package/bin/new/cli.js CHANGED
@@ -2,41 +2,43 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createProject = void 0;
4
4
  const form_1 = require("./form");
5
+ const packageManagers = [
6
+ "npm",
7
+ "yarn",
8
+ "pnpm",
9
+ ...(process.platform !== "win32" ? ["bun"] : []),
10
+ ];
11
+ const commandOptions = (yargs) => {
12
+ return yargs
13
+ .positional("project-name", {
14
+ describe: "The name of the project",
15
+ type: "string",
16
+ })
17
+ .option("template", {
18
+ describe: "The project template to use",
19
+ type: "string",
20
+ choices: ["opinionated", "non-opinionated"],
21
+ alias: "t",
22
+ })
23
+ .option("package-manager", {
24
+ describe: "The package manager to use",
25
+ type: "string",
26
+ choices: packageManagers,
27
+ alias: "p",
28
+ })
29
+ .option("directory", {
30
+ describe: "The directory for new project",
31
+ type: "string",
32
+ alias: "d",
33
+ })
34
+ .implies("package-manager", "template")
35
+ .implies("template", "package-manager");
36
+ };
5
37
  const createProject = () => {
6
- const packageManagers = ["npm", "yarn", "pnpm"];
7
- if (process.platform !== "win32") {
8
- packageManagers.push("bun");
9
- }
10
38
  return {
11
39
  command: "new <project-name> [package-manager] [template] [directory]",
12
40
  describe: "Create a new project",
13
- builder: (yargs) => {
14
- yargs
15
- .positional("project-name", {
16
- describe: "The name of the project",
17
- type: "string",
18
- })
19
- .option("template", {
20
- describe: "The project template to use",
21
- type: "string",
22
- choices: ["non-opinionated", "opinionated"],
23
- alias: "t",
24
- })
25
- .option("package-manager", {
26
- describe: "The package manager to use",
27
- type: "string",
28
- choices: packageManagers,
29
- alias: "p",
30
- })
31
- .option("directory", {
32
- describe: "The directory for new project",
33
- type: "string",
34
- alias: "d",
35
- })
36
- .implies("package-manager", "template")
37
- .implies("template", "package-manager");
38
- return yargs;
39
- },
41
+ builder: commandOptions,
40
42
  handler: async ({ projectName, packageManager, template, directory, }) => {
41
43
  return await (0, form_1.projectForm)(projectName, [
42
44
  packageManager,
package/bin/new/form.js CHANGED
@@ -61,8 +61,8 @@ function changePackageName({ directory, name, }) {
61
61
  }
62
62
  var Template;
63
63
  (function (Template) {
64
- Template["non-opinionated"] = "Non-Opinionated :: A simple ExpressoTS project.";
65
- Template["opinionated"] = "Opinionated :: A complete ExpressoTS project with an opinionated structure and features.";
64
+ Template["non-opinionated"] = "Non-Opinionated :: Allows users to choose where to scaffold resources, offering flexible project organization.";
65
+ Template["opinionated"] = "Opinionated :: Automatically scaffolds resources into a preset project structure. (Recommended)";
66
66
  })(Template || (Template = {}));
67
67
  const projectForm = async (projectName, args) => {
68
68
  let answer;
@@ -117,8 +117,8 @@ const projectForm = async (projectName, args) => {
117
117
  name: "template",
118
118
  message: "Select a template",
119
119
  choices: [
120
- "Non-Opinionated :: A simple ExpressoTS project.",
121
- "Opinionated :: A complete ExpressoTS project with an opinionated structure and features.",
120
+ `Opinionated :: Automatically scaffolds resources into a preset project structure. (${chalk_1.default.yellow("Recommended")})`,
121
+ "Non-Opinionated :: Allows users to choose where to scaffold resources, offering flexible project organization.",
122
122
  ],
123
123
  },
124
124
  {
@@ -185,7 +185,7 @@ const projectForm = async (projectName, args) => {
185
185
  progressBar.update(100);
186
186
  progressBar.stop();
187
187
  console.log("\n");
188
- console.log("🐎 Project ", chalk_1.default.green(answer.name), "created successfully!");
188
+ console.log("🐎 Project", chalk_1.default.green(answer.name), "created successfully!");
189
189
  console.log("🤙 Run the following commands to start the project:\n");
190
190
  console.log(chalk_1.default.bold.gray(`$ cd ${answer.name}`));
191
191
  switch (answer.packageManager) {
@@ -206,6 +206,7 @@ const projectForm = async (projectName, args) => {
206
206
  console.log(chalk_1.default.bold.green((0, center_text_1.centerText)("Happy coding!")));
207
207
  console.log(chalk_1.default.bold.gray((0, center_text_1.centerText)("Please consider donating to support the project.\n")));
208
208
  console.log(chalk_1.default.bold.white((0, center_text_1.centerText)("💖 Sponsor: https://github.com/sponsors/expressots")));
209
+ console.log("\n");
209
210
  }
210
211
  };
211
212
  exports.projectForm = projectForm;
@@ -0,0 +1,4 @@
1
+ import { CommandModule } from "yargs";
2
+ type CommandModuleArgs = {};
3
+ declare const generateProviders: () => CommandModule<CommandModuleArgs, any>;
4
+ export { generateProviders };
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateProviders = void 0;
4
+ const prisma_provider_1 = require("./prisma/prisma.provider");
5
+ const generateProviders = () => {
6
+ return {
7
+ command: "add <provider> [library-version] [provider-version]",
8
+ describe: "Scaffold a new provider",
9
+ aliases: ["a"],
10
+ builder: (yargs) => {
11
+ yargs
12
+ .positional("provider", {
13
+ choices: ["prisma"],
14
+ describe: "The provider to add to the project",
15
+ type: "string",
16
+ })
17
+ .option("library-version", {
18
+ describe: "The library version to install",
19
+ type: "string",
20
+ default: "latest",
21
+ alias: "v",
22
+ })
23
+ .option("provider-version", {
24
+ describe: "The version of the provider to install",
25
+ type: "string",
26
+ default: "latest",
27
+ alias: "vp",
28
+ });
29
+ return yargs;
30
+ },
31
+ handler: async ({ provider, libraryVersion, providerVersion }) => {
32
+ if (provider === "prisma") {
33
+ await (0, prisma_provider_1.prismaProvider)(libraryVersion, providerVersion);
34
+ }
35
+ },
36
+ };
37
+ };
38
+ exports.generateProviders = generateProviders;
@@ -0,0 +1 @@
1
+ export * from "./cli";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./cli"), exports);
@@ -0,0 +1,2 @@
1
+ declare const prismaProvider: (version: string, providerVersion: string) => Promise<void>;
2
+ export { prismaProvider };
@@ -0,0 +1,277 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.prismaProvider = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const inquirer_1 = __importDefault(require("inquirer"));
9
+ const node_child_process_1 = require("node:child_process");
10
+ const node_fs_1 = __importDefault(require("node:fs"));
11
+ const cli_ui_1 = require("../../utils/cli-ui");
12
+ const node_path_1 = __importDefault(require("node:path"));
13
+ const node_process_1 = require("node:process");
14
+ const compiler_1 = __importDefault(require("../../utils/compiler"));
15
+ const find_folder_1 = require("../../utils/find-folder");
16
+ const prismaProvider = async (version, providerVersion) => {
17
+ var _a;
18
+ const choices = [
19
+ { name: "CockroachDB", value: "cockroachdb" },
20
+ { name: "Microsoft SQL Server", value: "sqlserver" },
21
+ { name: "MongoDB", value: "mongodb" },
22
+ { name: "MySQL", value: "mysql" },
23
+ { name: "PostgreSQL", value: "postgresql" },
24
+ { name: "SQLite", value: "sqlite" },
25
+ ];
26
+ const drivers = {
27
+ PostgreSQL: "pg",
28
+ MySQL: "mysql2",
29
+ SQLite: "sqlite3",
30
+ "Microsoft SQL Server": "mssql",
31
+ MongoDB: "mongodb",
32
+ CockroachDB: "pg",
33
+ };
34
+ const answerPt1 = await inquirer_1.default.prompt([
35
+ {
36
+ type: "input",
37
+ name: "prismaClientVersion",
38
+ message: "Type the prisma client version (default=latest):",
39
+ default: "latest",
40
+ transformer: (input) => {
41
+ return chalk_1.default.yellow(chalk_1.default.bold(input));
42
+ },
43
+ },
44
+ {
45
+ type: "input",
46
+ name: "schemaName",
47
+ message: "Type the schema name (default=schema):",
48
+ default: "schema",
49
+ transformer: (input) => {
50
+ return chalk_1.default.yellow(chalk_1.default.bold(input));
51
+ },
52
+ },
53
+ {
54
+ type: "input",
55
+ name: "schemaPath",
56
+ message: "Where do you want to save your prisma schema (default=./):",
57
+ default: ".",
58
+ transformer: (input) => {
59
+ return chalk_1.default.yellow(chalk_1.default.bold(input));
60
+ },
61
+ },
62
+ {
63
+ type: "list",
64
+ name: "databaseName",
65
+ message: "Select your database:",
66
+ choices: choices.map((choice) => choice.name),
67
+ },
68
+ ]);
69
+ const answerPt2 = await inquirer_1.default.prompt([
70
+ {
71
+ type: "confirm",
72
+ name: "installDriver",
73
+ message: `Do you want to install the latest recommended database driver for ${answerPt1.databaseName}?`,
74
+ default: true,
75
+ },
76
+ {
77
+ type: "confirm",
78
+ name: "baseRepository",
79
+ message: "Do you want to add BaseRepository Pattern in this project?\nthis will replace the existing BaseRepository and BaseRespositoryInterface if it exists.",
80
+ default: true,
81
+ },
82
+ {
83
+ type: "confirm",
84
+ name: "confirm",
85
+ message: "Do you want to add prisma provider in this project?",
86
+ default: true,
87
+ },
88
+ ]);
89
+ const answer = Object.assign(Object.assign({}, answerPt1), answerPt2);
90
+ if (answer.confirm) {
91
+ // Find which package manager the user has used to install the desired prisma version
92
+ const packageManager = node_fs_1.default.existsSync("package-lock.json" || "yarn.lock" || "pnpm-lock.yaml")
93
+ ? "npm"
94
+ : node_fs_1.default.existsSync("yarn.lock")
95
+ ? "yarn"
96
+ : node_fs_1.default.existsSync("pnpm-lock.yaml")
97
+ ? "pnpm"
98
+ : null;
99
+ if (packageManager) {
100
+ // Install prisma in the project
101
+ console.log(`Installing prisma with ${packageManager}...`);
102
+ await execProcess({
103
+ commandArg: packageManager,
104
+ args: ["add", `prisma@${providerVersion}`, "-D"],
105
+ directory: process.cwd(),
106
+ });
107
+ // Install Prisma Client
108
+ console.log(`Installing Prisma Client with ${packageManager}...`);
109
+ await execProcess({
110
+ commandArg: packageManager,
111
+ args: ["add", `@prisma/client@${answer.prismaClientVersion}`],
112
+ directory: process.cwd(),
113
+ });
114
+ if (answer.installDriver) {
115
+ // Install database driver
116
+ console.log(`Installing the latest recommended database driver for ${answer.databaseName}: ${drivers[answer.databaseName]} ...`);
117
+ await execProcess({
118
+ commandArg: packageManager,
119
+ args: ["add", drivers[answer.databaseName]],
120
+ directory: process.cwd(),
121
+ });
122
+ }
123
+ // Install @expressots/prisma in the project
124
+ console.log(`Installing @expressots/prisma with ${packageManager}...`);
125
+ await execProcess({
126
+ commandArg: packageManager,
127
+ args: ["add", `@expressots/prisma@${version}`],
128
+ directory: process.cwd(),
129
+ });
130
+ }
131
+ else {
132
+ (0, cli_ui_1.printError)(`Could not find a package manager installed in this project.\nPlease install prisma and @expressots/prisma manually.`, "prisma");
133
+ process.exit(1);
134
+ }
135
+ // Map choices to find the corresponding value
136
+ answer.databaseName = (_a = choices.find((choice) => choice.name === answer.databaseName)) === null || _a === void 0 ? void 0 : _a.value;
137
+ // Init prisma
138
+ console.log(`Initializing prisma...`);
139
+ const prismaFolder = (0, find_folder_1.hasFolder)(process.cwd(), ["node_modules", ".git"]);
140
+ if (prismaFolder.found) {
141
+ const prismaEnquirer = await inquirer_1.default.prompt([
142
+ {
143
+ type: "confirm",
144
+ name: "confirm",
145
+ message: "Prisma is already initialized. Do you want to override it?",
146
+ default: true,
147
+ },
148
+ ]);
149
+ if ((prismaEnquirer === null || prismaEnquirer === void 0 ? void 0 : prismaEnquirer.confirm) === false) {
150
+ console.log(chalk_1.default.red("Prisma init aborted!"));
151
+ return;
152
+ }
153
+ node_fs_1.default.rmSync(prismaFolder.path, {
154
+ recursive: true,
155
+ force: true,
156
+ });
157
+ }
158
+ await execProcess({
159
+ commandArg: "npx",
160
+ args: [
161
+ "prisma",
162
+ "init",
163
+ "--datasource-provider",
164
+ answer.databaseName,
165
+ ],
166
+ directory: process.cwd(),
167
+ });
168
+ const oldFileName = node_path_1.default.join(process.cwd(), "/prisma/schema.prisma");
169
+ const newFileName = node_path_1.default.join(process.cwd(), `/prisma/${answer.schemaName}.prisma`);
170
+ node_fs_1.default.renameSync(oldFileName, newFileName);
171
+ // Move the folder to the destination
172
+ const schemaPath = node_path_1.default.join(process.cwd(), `${answer.schemaPath}/prisma/${answer.schemaName}.prisma`);
173
+ if (!node_fs_1.default.existsSync(schemaPath)) {
174
+ node_fs_1.default.mkdirSync(node_path_1.default.join(process.cwd(), `${answer.schemaPath}/prisma`), { recursive: true });
175
+ }
176
+ node_fs_1.default.renameSync(newFileName, schemaPath);
177
+ // Remove the source folder
178
+ if (newFileName !== schemaPath) {
179
+ node_fs_1.default.rmSync(node_path_1.default.join(process.cwd(), "/prisma"), { recursive: true });
180
+ }
181
+ // Add prisma to package.json
182
+ prismaPackage(answer);
183
+ if (answer.baseRepository) {
184
+ const { opinionated, sourceRoot } = await compiler_1.default.loadConfig();
185
+ let folderMatch = "";
186
+ if (opinionated) {
187
+ folderMatch = "repositories";
188
+ }
189
+ else {
190
+ folderMatch = "";
191
+ }
192
+ const repositoryDir = `${sourceRoot}/${folderMatch}`;
193
+ console.log(`Generating BaseRepository Pattern...`);
194
+ const baseRepositoryInterfaceTplPath = node_path_1.default.join(__dirname, "./templates/base-repository.interface.tpl");
195
+ const baseRepositoryTplPath = node_path_1.default.join(__dirname, "./templates/base-repository.tpl");
196
+ const baseRepositoryInterfaceTemplate = node_fs_1.default.readFileSync(baseRepositoryInterfaceTplPath, "utf8");
197
+ const baseRepositoryTemplate = node_fs_1.default.readFileSync(baseRepositoryTplPath, "utf8");
198
+ node_fs_1.default.writeFileSync(node_path_1.default.join(repositoryDir, "base-repository.interface.ts"), baseRepositoryInterfaceTemplate);
199
+ node_fs_1.default.writeFileSync(node_path_1.default.join(repositoryDir, "base-repository.ts"), baseRepositoryTemplate);
200
+ }
201
+ // Install @expressots/prisma in the project
202
+ console.log(`Mapping configurations to expressots.config...`);
203
+ await addProviderConfigInExpressotsConfig(answer.schemaName, `${answer.schemaPath}/prisma`);
204
+ console.log("Now configure your database connection in the project.");
205
+ console.log(chalk_1.default.green("\n👍 Prisma provider added successfully!"));
206
+ }
207
+ else {
208
+ console.log(chalk_1.default.red("Prisma provider not added!"));
209
+ }
210
+ };
211
+ exports.prismaProvider = prismaProvider;
212
+ async function execProcess({ commandArg, args, directory, }) {
213
+ return new Promise((resolve, reject) => {
214
+ const isWindows = process.platform === "win32";
215
+ const command = isWindows ? `${commandArg}.cmd` : commandArg;
216
+ const installProcess = (0, node_child_process_1.spawn)(command, args, {
217
+ cwd: directory,
218
+ });
219
+ console.log(chalk_1.default.bold.blue(`Executing: ${command} ${args.join(" ")}`));
220
+ console.log(chalk_1.default.yellow("---------------------------------------"));
221
+ installProcess.stdout.on("data", (data) => {
222
+ console.log(chalk_1.default.green(data.toString().trim())); // Display regular messages in green
223
+ });
224
+ installProcess.stderr.on("data", (data) => {
225
+ console.error(chalk_1.default.red(data.toString().trim())); // Display error messages in red
226
+ });
227
+ installProcess.on("close", (code) => {
228
+ if (code === 0) {
229
+ console.log(chalk_1.default.bold.green("---------------------------------------"));
230
+ console.log(chalk_1.default.bold.green("Installation Done!"));
231
+ resolve("Installation Done!");
232
+ }
233
+ else {
234
+ console.error(chalk_1.default.bold.red("---------------------------------------"));
235
+ console.error(chalk_1.default.bold.red(`Command ${command} ${args.join(" ")} exited with code ${code}`));
236
+ reject(new Error(`Command ${command} ${args.join(" ")} exited with code ${code}`));
237
+ (0, node_process_1.exit)(1);
238
+ }
239
+ });
240
+ });
241
+ }
242
+ function prismaPackage(answer) {
243
+ // Get the absolute path of the input directory parameter
244
+ const absDirPath = node_path_1.default.resolve(process.cwd());
245
+ // Load the package.json file
246
+ const packageJsonPath = node_path_1.default.join(absDirPath, "package.json");
247
+ const fileContents = node_fs_1.default.readFileSync(packageJsonPath, "utf-8");
248
+ const packageJson = JSON.parse(fileContents);
249
+ // Add the Prisma configuration to the package.json
250
+ packageJson.prisma = {
251
+ schema: `${answer.schemaPath}/prisma/${answer.schemaName}.prisma`,
252
+ };
253
+ // Add the Prisma script to the package.json
254
+ packageJson.scripts = Object.assign(Object.assign({}, packageJson.scripts), { prisma: "npx @expressots/prisma codegen" });
255
+ // Save the package.json file
256
+ node_fs_1.default.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
257
+ }
258
+ async function addProviderConfigInExpressotsConfig(schemaName, schemaPath) {
259
+ const absDirPath = node_path_1.default.resolve(process.cwd());
260
+ const expressotsConfigPath = node_path_1.default.join(absDirPath, "expressots.config.ts");
261
+ let fileContents = node_fs_1.default.readFileSync(expressotsConfigPath, "utf-8");
262
+ const config = await compiler_1.default.loadConfig();
263
+ const providersObject = `opinionated: ${config.opinionated},\n\tproviders: {
264
+ prisma: {
265
+ schemaName: "${schemaName}",
266
+ schemaPath: "${schemaPath}",
267
+ entitiesPath: "entities",
268
+ entityNamePattern: "entity",
269
+ },
270
+ },`;
271
+ if (config.providers) {
272
+ // delete the providers object until the last closing curly brace
273
+ fileContents = fileContents.replace(/\n([\s]*?)providers: {([\s\S]*?)};/, "\n};");
274
+ }
275
+ const newFileContents = fileContents.replace(/opinionated: (.*)/, providersObject);
276
+ node_fs_1.default.writeFileSync(expressotsConfigPath, newFileContents);
277
+ }
@@ -0,0 +1,57 @@
1
+ import { Prisma, PrismaClient } from "@prisma/client";
2
+ import {
3
+ CreateInput,
4
+ ModelsOf,
5
+ DeleteWhere,
6
+ Select,
7
+ PrismaAction,
8
+ } from "@expressots/prisma";
9
+
10
+ interface IBaseRepository<ModelName extends ModelsOf<PrismaClient>> {
11
+ aggregate: (args: PrismaAction<ModelName, "aggregate">) => Promise<any>;
12
+ count: (args: PrismaAction<ModelName, "count">) => Promise<number>;
13
+ create: (
14
+ data:
15
+ | CreateInput<ModelName>["data"]
16
+ | {
17
+ data: CreateInput<ModelName>["data"];
18
+ select?: Select<ModelName, "create">["select"];
19
+ },
20
+ ) => Promise<ModelName | never>;
21
+ delete: (
22
+ where: DeleteWhere<ModelName>["where"],
23
+ response?: Select<ModelName, "delete">["select"],
24
+ ) => Promise<ModelName | never>;
25
+ deleteMany: (
26
+ args?: PrismaAction<ModelName, "deleteMany">,
27
+ ) => Promise<Prisma.BatchPayload>;
28
+ findFirst: (
29
+ args: PrismaAction<ModelName, "findFirst">,
30
+ ) => Promise<ModelName | null>;
31
+ findFirstOrThrow: (
32
+ args?: PrismaAction<ModelName, "findFirstOrThrow">,
33
+ ) => Promise<ModelName | never>;
34
+ findMany: (
35
+ args: PrismaAction<ModelName, "findMany">,
36
+ ) => Promise<ModelName[]>;
37
+ findUnique: (
38
+ args: PrismaAction<ModelName, "findUnique">,
39
+ ) => Promise<ModelName | null>;
40
+ findUniqueOrThrow: (
41
+ args?: PrismaAction<ModelName, "findFirstOrThrow">,
42
+ ) => Promise<ModelName | never>;
43
+ groupBy: (
44
+ args: PrismaAction<ModelName, "groupBy">,
45
+ ) => Promise<ModelName | never>;
46
+ update: (
47
+ args: PrismaAction<ModelName, "update">,
48
+ ) => Promise<ModelName | never>;
49
+ updateMany: (
50
+ args: PrismaAction<ModelName, "updateMany">,
51
+ ) => Promise<Prisma.BatchPayload>;
52
+ upsert: (
53
+ args: PrismaAction<ModelName, "upsert">,
54
+ ) => Promise<ModelName | never>;
55
+ }
56
+
57
+ export { IBaseRepository };
@@ -0,0 +1,146 @@
1
+ import { PrismaClient, Prisma } from "@prisma/client";
2
+ import {
3
+ CreateInput,
4
+ ModelsOf,
5
+ DeleteWhere,
6
+ Select,
7
+ PrismaAction,
8
+ } from "@expressots/prisma";
9
+ import { provide } from "inversify-binding-decorators";
10
+ import { IBaseRepository } from "./base-repository.interface";
11
+
12
+ @provide(BaseRepository)
13
+ class BaseRepository<ModelName extends ModelsOf<PrismaClient>>
14
+ implements IBaseRepository<ModelName>
15
+ {
16
+ protected prismaModel: any;
17
+ protected prismaClient: PrismaClient;
18
+ constructor(modelName: keyof PrismaClient) {
19
+ this.prismaClient = new PrismaClient();
20
+ this.prismaModel = this.prismaClient[modelName];
21
+ }
22
+
23
+ async aggregate(args: PrismaAction<ModelName, "aggregate">): Promise<any> {
24
+ return await this.prismaModel.aggregate(args);
25
+ }
26
+
27
+ async count(args: PrismaAction<ModelName, "count">): Promise<number> {
28
+ return await this.prismaModel.count(args);
29
+ }
30
+
31
+ async create(
32
+ data:
33
+ | CreateInput<ModelName>["data"]
34
+ | {
35
+ data: CreateInput<ModelName>["data"];
36
+ select?: Select<ModelName, "create">["select"];
37
+ },
38
+ ): Promise<ModelName | never> {
39
+ if (!data) {
40
+ throw new Error("Data cannot be null or undefined");
41
+ }
42
+
43
+ if (typeof data === "object" && "data" in data) {
44
+ return await this.prismaModel.create(data);
45
+ }
46
+
47
+ return await this.prismaModel.create({ data });
48
+ }
49
+
50
+ async delete(
51
+ where: DeleteWhere<ModelName>["where"],
52
+ select?: Select<ModelName, "delete">["select"],
53
+ ): Promise<ModelName | never> {
54
+ if (!where) {
55
+ throw new Error("Data cannot be null or undefined");
56
+ }
57
+
58
+ const obj = await this.prismaModel.delete({ where });
59
+
60
+ if (select) {
61
+ const entries = Object.entries(select);
62
+
63
+ const hasTrueField = entries.some(([, value]) => value);
64
+
65
+ if (hasTrueField) {
66
+ const result: any = {};
67
+ for (const [key, value] of entries) {
68
+ if (value) {
69
+ result[key] = obj[key as keyof typeof obj];
70
+ }
71
+ }
72
+ return result as ModelName;
73
+ } else {
74
+ for (const [key, value] of entries) {
75
+ if (!value) {
76
+ delete obj[key as keyof typeof obj];
77
+ }
78
+ }
79
+ }
80
+ }
81
+
82
+ return obj;
83
+ }
84
+
85
+ async deleteMany(
86
+ args?: PrismaAction<ModelName, "deleteMany">,
87
+ ): Promise<Prisma.BatchPayload> {
88
+ return await this.prismaModel.deleteMany(args);
89
+ }
90
+
91
+ async findFirst(
92
+ args?: PrismaAction<ModelName, "findFirst">,
93
+ ): Promise<ModelName | null> {
94
+ return await this.prismaModel.findFirst(args);
95
+ }
96
+
97
+ async findFirstOrThrow(
98
+ args?: PrismaAction<ModelName, "findFirstOrThrow">,
99
+ ): Promise<ModelName | never> {
100
+ return await this.prismaModel.findFirstOrThrow(args);
101
+ }
102
+
103
+ async findMany(
104
+ args: PrismaAction<ModelName, "findMany">,
105
+ ): Promise<ModelName[]> {
106
+ return await this.prismaModel.findMany(args);
107
+ }
108
+
109
+ async findUnique(
110
+ args: PrismaAction<ModelName, "findUnique">,
111
+ ): Promise<ModelName | null> {
112
+ return this.prismaModel.findUnique(args);
113
+ }
114
+
115
+ async findUniqueOrThrow(
116
+ args?: PrismaAction<ModelName, "findUniqueOrThrow">,
117
+ ): Promise<ModelName | never> {
118
+ return await this.prismaModel.findUniqueOrThrow(args);
119
+ }
120
+
121
+ async groupBy(
122
+ args: PrismaAction<ModelName, "groupBy">,
123
+ ): Promise<ModelName | never> {
124
+ return await this.prismaModel.groupBy(args);
125
+ }
126
+
127
+ async update(
128
+ args: PrismaAction<ModelName, "update">,
129
+ ): Promise<ModelName | never> {
130
+ return await this.prismaModel.update(args);
131
+ }
132
+
133
+ async updateMany(
134
+ args: PrismaAction<ModelName, "updateMany">,
135
+ ): Promise<Prisma.BatchPayload> {
136
+ return await this.prismaModel.updateMany(args);
137
+ }
138
+
139
+ async upsert(
140
+ args: PrismaAction<ModelName, "upsert">,
141
+ ): Promise<ModelName | never> {
142
+ return await this.prismaModel.upsert(args);
143
+ }
144
+ }
145
+
146
+ export { BaseRepository };
@@ -0,0 +1,5 @@
1
+ declare function hasFolder(rootDir: string, ignoreList?: Array<string>): {
2
+ found: boolean;
3
+ path: string | null;
4
+ };
5
+ export { hasFolder };
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.hasFolder = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ function hasFolder(rootDir, ignoreList = []) {
10
+ function searchDir(directory) {
11
+ const files = fs_1.default.readdirSync(directory);
12
+ for (const file of files) {
13
+ if (ignoreList.includes(file))
14
+ continue; // Skip if the file/folder is in the ignore list
15
+ const filePath = path_1.default.join(directory, file);
16
+ const stats = fs_1.default.statSync(filePath);
17
+ if (stats.isDirectory()) {
18
+ if (file === "prisma") {
19
+ return filePath; // Return full path of the 'prisma' folder
20
+ }
21
+ else {
22
+ const result = searchDir(filePath); // Search inside other directories recursively
23
+ if (result) {
24
+ return result;
25
+ }
26
+ }
27
+ }
28
+ }
29
+ return null; // 'prisma' folder was not found in this directory
30
+ }
31
+ const foundPath = searchDir(rootDir);
32
+ return {
33
+ found: !!foundPath,
34
+ path: foundPath,
35
+ };
36
+ }
37
+ exports.hasFolder = hasFolder;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expressots/cli",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "Expressots CLI - modern, fast, lightweight nodejs web framework (@cli)",
5
5
  "author": "Richard Zampieri",
6
6
  "license": "MIT",
@@ -36,7 +36,7 @@
36
36
  "start": "node ./bin/cli.js",
37
37
  "start:dev": "tsnd ./src/cli.ts",
38
38
  "build": "npm run clean && tsc -p tsconfig.json && yarn cp:templates && chmod +x ./bin/cli.js",
39
- "cp:templates": "cp -r ./src/generate/templates ./bin/generate/templates",
39
+ "cp:templates": "cp -r ./src/generate/templates ./bin/generate/templates && cp -r ./src/providers/prisma/templates ./bin/providers/prisma/templates",
40
40
  "clean": "rimraf ./bin",
41
41
  "format": "prettier --write \"./src/**/*.ts\" --cache",
42
42
  "lint": "eslint \"./src/**/*.ts\"",
@@ -77,7 +77,8 @@
77
77
  "husky": "^8.0.3",
78
78
  "prettier": "^2.8.4",
79
79
  "release-it": "^16.1.5",
80
- "rimraf": "^4.1.2",
80
+ "rimraf": "^5.0.5",
81
+ "shx": "^0.3.4",
81
82
  "ts-node-dev": "^2.0.0",
82
83
  "typescript": "^4.9.5",
83
84
  "vite": "^4.4.9",