@casekit/orm-cli 0.0.1-release-candidate

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 (99) hide show
  1. package/build/cli.d.ts +1 -0
  2. package/build/cli.js +21 -0
  3. package/build/commands/db-drop/handler.d.ts +3 -0
  4. package/build/commands/db-drop/handler.js +15 -0
  5. package/build/commands/db-drop/options.d.ts +1 -0
  6. package/build/commands/db-drop/options.js +1 -0
  7. package/build/commands/db-drop.d.ts +6 -0
  8. package/build/commands/db-drop.js +8 -0
  9. package/build/commands/db-drop.test.d.ts +1 -0
  10. package/build/commands/db-drop.test.js +22 -0
  11. package/build/commands/db-pull/handler.d.ts +3 -0
  12. package/build/commands/db-pull/handler.js +32 -0
  13. package/build/commands/db-pull/options.d.ts +13 -0
  14. package/build/commands/db-pull/options.js +13 -0
  15. package/build/commands/db-pull/util/relationNames.d.ts +3 -0
  16. package/build/commands/db-pull/util/relationNames.js +14 -0
  17. package/build/commands/db-pull/util/relationNames.test.d.ts +1 -0
  18. package/build/commands/db-pull/util/relationNames.test.js +61 -0
  19. package/build/commands/db-pull/util/renderDefault.d.ts +3 -0
  20. package/build/commands/db-pull/util/renderDefault.js +46 -0
  21. package/build/commands/db-pull/util/renderDefault.test.d.ts +1 -0
  22. package/build/commands/db-pull/util/renderDefault.test.js +67 -0
  23. package/build/commands/db-pull/util/renderFieldDefinition.d.ts +2 -0
  24. package/build/commands/db-pull/util/renderFieldDefinition.js +50 -0
  25. package/build/commands/db-pull/util/renderFieldDefinition.test.d.ts +1 -0
  26. package/build/commands/db-pull/util/renderFieldDefinition.test.js +182 -0
  27. package/build/commands/db-pull/util/renderModel.basic.test.d.ts +1 -0
  28. package/build/commands/db-pull/util/renderModel.basic.test.js +169 -0
  29. package/build/commands/db-pull/util/renderModel.constraints.test.d.ts +1 -0
  30. package/build/commands/db-pull/util/renderModel.constraints.test.js +677 -0
  31. package/build/commands/db-pull/util/renderModel.d.ts +2 -0
  32. package/build/commands/db-pull/util/renderModel.defaultValues.test.d.ts +1 -0
  33. package/build/commands/db-pull/util/renderModel.defaultValues.test.js +518 -0
  34. package/build/commands/db-pull/util/renderModel.js +88 -0
  35. package/build/commands/db-pull/util/renderModel.relations.test.d.ts +1 -0
  36. package/build/commands/db-pull/util/renderModel.relations.test.js +880 -0
  37. package/build/commands/db-pull/util/renderModel.types.test.d.ts +1 -0
  38. package/build/commands/db-pull/util/renderModel.types.test.js +703 -0
  39. package/build/commands/db-pull/util/renderRelations.d.ts +2 -0
  40. package/build/commands/db-pull/util/renderRelations.js +55 -0
  41. package/build/commands/db-pull/util/renderRelations.test.d.ts +1 -0
  42. package/build/commands/db-pull/util/renderRelations.test.js +165 -0
  43. package/build/commands/db-pull/util/renderType.d.ts +6 -0
  44. package/build/commands/db-pull/util/renderType.js +55 -0
  45. package/build/commands/db-pull/util/renderType.test.d.ts +1 -0
  46. package/build/commands/db-pull/util/renderType.test.js +46 -0
  47. package/build/commands/db-pull.d.ts +30 -0
  48. package/build/commands/db-pull.js +8 -0
  49. package/build/commands/db-pull.test.d.ts +1 -0
  50. package/build/commands/db-pull.test.js +633 -0
  51. package/build/commands/db-push/handler.d.ts +3 -0
  52. package/build/commands/db-push/handler.js +14 -0
  53. package/build/commands/db-push/options.d.ts +2 -0
  54. package/build/commands/db-push/options.js +1 -0
  55. package/build/commands/db-push.d.ts +6 -0
  56. package/build/commands/db-push.js +8 -0
  57. package/build/commands/db-push.test.d.ts +1 -0
  58. package/build/commands/db-push.test.js +21 -0
  59. package/build/commands/generate-model/handler.d.ts +3 -0
  60. package/build/commands/generate-model/handler.js +18 -0
  61. package/build/commands/generate-model/options.d.ts +7 -0
  62. package/build/commands/generate-model/options.js +7 -0
  63. package/build/commands/generate-model.d.ts +18 -0
  64. package/build/commands/generate-model.js +8 -0
  65. package/build/commands/generate-model.test.d.ts +1 -0
  66. package/build/commands/generate-model.test.js +58 -0
  67. package/build/commands/init/handler.d.ts +3 -0
  68. package/build/commands/init/handler.js +22 -0
  69. package/build/commands/init/options.d.ts +12 -0
  70. package/build/commands/init/options.js +12 -0
  71. package/build/commands/init.d.ts +28 -0
  72. package/build/commands/init.js +8 -0
  73. package/build/commands/init.test.d.ts +1 -0
  74. package/build/commands/init.test.js +125 -0
  75. package/build/generators/generateConfigFile.d.ts +1 -0
  76. package/build/generators/generateConfigFile.js +16 -0
  77. package/build/generators/generateDbFile.d.ts +1 -0
  78. package/build/generators/generateDbFile.js +37 -0
  79. package/build/generators/generateModelFile.d.ts +1 -0
  80. package/build/generators/generateModelFile.js +11 -0
  81. package/build/generators/generateModelFile.test.d.ts +1 -0
  82. package/build/generators/generateModelFile.test.js +19 -0
  83. package/build/generators/generateModelsFile.d.ts +1 -0
  84. package/build/generators/generateModelsFile.js +8 -0
  85. package/build/index.d.ts +1 -0
  86. package/build/index.js +1 -0
  87. package/build/options.d.ts +12 -0
  88. package/build/options.js +12 -0
  89. package/build/test/setup.d.ts +1 -0
  90. package/build/test/setup.js +35 -0
  91. package/build/types.d.ts +14 -0
  92. package/build/types.js +1 -0
  93. package/build/util/createOrOverwriteFile.d.ts +1 -0
  94. package/build/util/createOrOverwriteFile.js +17 -0
  95. package/build/util/loadConfig.d.ts +3 -0
  96. package/build/util/loadConfig.js +21 -0
  97. package/build/util/prettify.d.ts +1 -0
  98. package/build/util/prettify.js +8 -0
  99. package/package.json +70 -0
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import pg from "pg";
2
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
3
+ import yargs from "yargs";
4
+ import { dbDrop } from "./db-drop.js";
5
+ import { dbPush } from "./db-push.js";
6
+ describe("db push", () => {
7
+ let db;
8
+ beforeEach(async () => {
9
+ db = new pg.Client();
10
+ await db.connect();
11
+ });
12
+ afterEach(async () => {
13
+ await db.end();
14
+ });
15
+ test("creates the schema for the models defined in the config", async () => {
16
+ await yargs().command(dbDrop).parseAsync("drop");
17
+ await yargs().command(dbPush).parseAsync("push");
18
+ const result = await db.query("SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'orm';");
19
+ expect(result.rows.length).toBe(1);
20
+ });
21
+ });
@@ -0,0 +1,3 @@
1
+ import { Handler } from "#types.js";
2
+ import { builder } from "./options.js";
3
+ export declare const handler: Handler<typeof builder>;
@@ -0,0 +1,18 @@
1
+ import { camelCase } from "es-toolkit";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { generateModelFile } from "#generators/generateModelFile.js";
5
+ import { generateModelsFile } from "#generators/generateModelsFile.js";
6
+ import { createOrOverwriteFile } from "#util/createOrOverwriteFile.js";
7
+ import { loadConfig } from "#util/loadConfig.js";
8
+ import { prettify } from "#util/prettify.js";
9
+ export const handler = async (opts) => {
10
+ const config = await loadConfig(opts);
11
+ const modelFile = await generateModelFile(opts.name, config.directory);
12
+ await createOrOverwriteFile(`${config.directory}/models/${opts.name}.ts`, modelFile, opts.force);
13
+ const models = fs
14
+ .readdirSync(`${config.directory}/models`)
15
+ .filter((f) => f !== "index.ts")
16
+ .map((f) => camelCase(f.replace(/\.ts$/, "")));
17
+ await createOrOverwriteFile(`${config.directory}/models/index.ts`, await prettify(path.join(process.cwd(), `${config.directory}/models/index.ts`), generateModelsFile(models)), opts.force);
18
+ };
@@ -0,0 +1,7 @@
1
+ export declare const builder: {
2
+ readonly name: {
3
+ readonly type: "string";
4
+ readonly desc: "Name of the model";
5
+ readonly demandOption: true;
6
+ };
7
+ };
@@ -0,0 +1,7 @@
1
+ export const builder = {
2
+ name: {
3
+ type: "string",
4
+ desc: "Name of the model",
5
+ demandOption: true,
6
+ },
7
+ };
@@ -0,0 +1,18 @@
1
+ export declare const generateModel: {
2
+ command: string;
3
+ desc: string;
4
+ builder: {
5
+ readonly name: {
6
+ readonly type: "string";
7
+ readonly desc: "Name of the model";
8
+ readonly demandOption: true;
9
+ };
10
+ };
11
+ handler: import("../types.js").Handler<{
12
+ readonly name: {
13
+ readonly type: "string";
14
+ readonly desc: "Name of the model";
15
+ readonly demandOption: true;
16
+ };
17
+ }>;
18
+ };
@@ -0,0 +1,8 @@
1
+ import { handler } from "./generate-model/handler.js";
2
+ import { builder } from "./generate-model/options.js";
3
+ export const generateModel = {
4
+ command: "model",
5
+ desc: "Generate a skeleton model file",
6
+ builder,
7
+ handler,
8
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,58 @@
1
+ import * as prompts from "@inquirer/prompts";
2
+ import { vol } from "memfs";
3
+ import { beforeEach, describe, expect, test, vi } from "vitest";
4
+ import yargs from "yargs";
5
+ import { unindent } from "@casekit/unindent";
6
+ import { globalOptions } from "#options.js";
7
+ import { generateModel } from "./generate-model.js";
8
+ describe("orm generate model", () => {
9
+ beforeEach(() => {
10
+ vi.spyOn(process, "cwd").mockReturnValue(".");
11
+ vol.fromJSON({
12
+ "app/db.server/models/index.ts": unindent `
13
+ import { user } from "./user";
14
+
15
+ export const models = {
16
+ user
17
+ };
18
+ `,
19
+ "app/db.server/models/user.ts": unindent `
20
+ import { type ModelDefinition, sql } from "@casekit/orm";
21
+
22
+ export const user = {
23
+ fields: {
24
+ id: { type: "uuid", primaryKey: true },
25
+ createdAt: { type: "timestamp", default: sql\`now()\` },
26
+ name: { type: "text" },
27
+ },
28
+ } as const satisfies ModelDefinition;
29
+ `,
30
+ }, "/project");
31
+ });
32
+ test("it generates a model file and updates the index", async () => {
33
+ vi.spyOn(prompts, "confirm").mockResolvedValue(true);
34
+ vi.spyOn(process, "cwd").mockReturnValue("/project");
35
+ await yargs()
36
+ .options(globalOptions)
37
+ .command(generateModel)
38
+ .parseAsync("model --name post");
39
+ const modelFile = vol.readFileSync("./app/db.server/models/post.ts", "utf8");
40
+ expect(modelFile.trim()).toEqual(unindent `
41
+ import type { ModelDefinition } from "@casekit/orm";
42
+
43
+ export const post = {
44
+ fields: {},
45
+ } as const satisfies ModelDefinition;
46
+ `);
47
+ const indexFile = vol.readFileSync("./app/db.server/models/index.ts", "utf8");
48
+ expect(indexFile.trim()).toEqual(unindent `
49
+ import { post } from "./post";
50
+ import { user } from "./user";
51
+
52
+ export const models = {
53
+ post,
54
+ user,
55
+ };
56
+ `);
57
+ });
58
+ });
@@ -0,0 +1,3 @@
1
+ import { Handler } from "#types.js";
2
+ import { builder } from "./options.js";
3
+ export declare const handler: Handler<typeof builder>;
@@ -0,0 +1,22 @@
1
+ import { input } from "@inquirer/prompts";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { generateConfigFile } from "#generators/generateConfigFile.js";
5
+ import { generateDbFile } from "#generators/generateDbFile.js";
6
+ import { generateModelsFile } from "#generators/generateModelsFile.js";
7
+ import { createOrOverwriteFile } from "#util/createOrOverwriteFile.js";
8
+ import { prettify } from "#util/prettify.js";
9
+ export const handler = async (opts) => {
10
+ const srcDir = ["src", "app", "lib"].find(fs.existsSync) ?? "src";
11
+ const dir = opts.directory ??
12
+ (await input({
13
+ message: "Where do you want to keep your database configuration?",
14
+ default: `./${srcDir}/db.server`,
15
+ }));
16
+ const dbFile = generateDbFile();
17
+ await createOrOverwriteFile(`${dir}/index.ts`, dbFile, opts.force);
18
+ const modelsFile = generateModelsFile([]);
19
+ await createOrOverwriteFile(`${dir}/models/index.ts`, await prettify(path.join(process.cwd(), `${dir}/models/index.ts`), modelsFile), opts.force);
20
+ const configFile = generateConfigFile(dir);
21
+ await createOrOverwriteFile(`orm.config.ts`, configFile, opts.force);
22
+ };
@@ -0,0 +1,12 @@
1
+ export declare const builder: {
2
+ readonly force: {
3
+ readonly type: "boolean";
4
+ readonly desc: "Overwrite existing files without asking for confirmation";
5
+ readonly default: false;
6
+ };
7
+ readonly directory: {
8
+ readonly alias: "d";
9
+ readonly type: "string";
10
+ readonly desc: "Location to keep your database configuration";
11
+ };
12
+ };
@@ -0,0 +1,12 @@
1
+ export const builder = {
2
+ force: {
3
+ type: "boolean",
4
+ desc: "Overwrite existing files without asking for confirmation",
5
+ default: false,
6
+ },
7
+ directory: {
8
+ alias: "d",
9
+ type: "string",
10
+ desc: "Location to keep your database configuration",
11
+ },
12
+ };
@@ -0,0 +1,28 @@
1
+ export declare const init: {
2
+ command: string;
3
+ desc: string;
4
+ builder: {
5
+ readonly force: {
6
+ readonly type: "boolean";
7
+ readonly desc: "Overwrite existing files without asking for confirmation";
8
+ readonly default: false;
9
+ };
10
+ readonly directory: {
11
+ readonly alias: "d";
12
+ readonly type: "string";
13
+ readonly desc: "Location to keep your database configuration";
14
+ };
15
+ };
16
+ handler: import("../types.js").Handler<{
17
+ readonly force: {
18
+ readonly type: "boolean";
19
+ readonly desc: "Overwrite existing files without asking for confirmation";
20
+ readonly default: false;
21
+ };
22
+ readonly directory: {
23
+ readonly alias: "d";
24
+ readonly type: "string";
25
+ readonly desc: "Location to keep your database configuration";
26
+ };
27
+ }>;
28
+ };
@@ -0,0 +1,8 @@
1
+ import { handler } from "./init/handler.js";
2
+ import { builder } from "./init/options.js";
3
+ export const init = {
4
+ command: "init",
5
+ desc: "Configure your project for use with @casekit/orm",
6
+ builder,
7
+ handler,
8
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,125 @@
1
+ import * as prompts from "@inquirer/prompts";
2
+ import { vol } from "memfs";
3
+ import { describe, expect, test, vi } from "vitest";
4
+ import yargs from "yargs";
5
+ import { unindent } from "@casekit/unindent";
6
+ import { init } from "./init.js";
7
+ describe("init", () => {
8
+ test("scaffolding config files", async () => {
9
+ vi.spyOn(prompts, "input").mockResolvedValueOnce("./app/db.server");
10
+ vi.spyOn(prompts, "confirm").mockResolvedValueOnce(true);
11
+ await yargs().command(init).parseAsync("init");
12
+ const dbFile = vol.readFileSync("./app/db.server/index.ts", "utf-8");
13
+ const modelFile = vol.readFileSync("./app/db.server/models/index.ts", "utf-8");
14
+ const configFile = vol.readFileSync("./orm.config.ts", "utf-8");
15
+ expect(dbFile).toEqual(unindent `
16
+ import { type Config, type ModelType, type Orm, orm } from "@casekit/orm";
17
+ import { models } from "./models";
18
+
19
+ const config = {
20
+ models,
21
+ } as const satisfies Config;
22
+
23
+ let db: Orm<typeof config>;
24
+
25
+ declare global {
26
+ // eslint-disable-next-line no-var
27
+ var __db: Orm<typeof config>;
28
+ }
29
+
30
+ // we do this because in development we don't want to restart
31
+ // the server with every change, but we want to make sure we don't
32
+ // create a new connection to the DB with every change either.
33
+ if (process.env.NODE_ENV === "production") {
34
+ db = orm(config);
35
+ await db.connect();
36
+ } else {
37
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
38
+ if (!global.__db) {
39
+ global.__db = orm(config);
40
+ await global.__db.connect();
41
+ }
42
+ db = global.__db;
43
+ }
44
+
45
+ export type DB = Orm<typeof config>;
46
+ export type Models = typeof models;
47
+ export type Model<M extends keyof Models> = ModelType<Models[M]>;
48
+
49
+ export { db };
50
+ `);
51
+ expect(modelFile.trim()).toEqual(unindent `
52
+ export const models = {};
53
+ `);
54
+ expect(configFile).toEqual(unindent `
55
+ import { type Config, orm } from "@casekit/orm";
56
+ import type { OrmCLIConfig } from "@casekit/orm-cli";
57
+
58
+ import { models } from "./app/db.server/models";
59
+
60
+ const config = {
61
+ models,
62
+ } as const satisfies Config;
63
+
64
+ export default {
65
+ db: orm(config),
66
+ directory: "./app/db.server",
67
+ } satisfies OrmCLIConfig;
68
+ `);
69
+ });
70
+ test("optionally overwriting existing files", async () => {
71
+ vol.fromJSON({
72
+ "package.json": JSON.stringify({ dependencies: {} }),
73
+ "app/db.server/models/index.ts": "original",
74
+ "orm.config.ts": "original",
75
+ "app/db.server/index.ts": "original",
76
+ }, "/project");
77
+ vi.spyOn(prompts, "input").mockResolvedValueOnce("./app/db.server");
78
+ vi.spyOn(prompts, "confirm")
79
+ .mockResolvedValueOnce(true)
80
+ .mockResolvedValueOnce(false)
81
+ .mockResolvedValueOnce(false);
82
+ await yargs().command(init).parseAsync("init");
83
+ const dbFile = vol.readFileSync("./app/db.server/index.ts", "utf-8");
84
+ const modelFile = vol.readFileSync("./app/db.server/models/index.ts", "utf-8");
85
+ const configFile = vol.readFileSync("./orm.config.ts", "utf-8");
86
+ expect(dbFile).toEqual(unindent `
87
+ import { type Config, type ModelType, type Orm, orm } from "@casekit/orm";
88
+ import { models } from "./models";
89
+
90
+ const config = {
91
+ models,
92
+ } as const satisfies Config;
93
+
94
+ let db: Orm<typeof config>;
95
+
96
+ declare global {
97
+ // eslint-disable-next-line no-var
98
+ var __db: Orm<typeof config>;
99
+ }
100
+
101
+ // we do this because in development we don't want to restart
102
+ // the server with every change, but we want to make sure we don't
103
+ // create a new connection to the DB with every change either.
104
+ if (process.env.NODE_ENV === "production") {
105
+ db = orm(config);
106
+ await db.connect();
107
+ } else {
108
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
109
+ if (!global.__db) {
110
+ global.__db = orm(config);
111
+ await global.__db.connect();
112
+ }
113
+ db = global.__db;
114
+ }
115
+
116
+ export type DB = Orm<typeof config>;
117
+ export type Models = typeof models;
118
+ export type Model<M extends keyof Models> = ModelType<Models[M]>;
119
+
120
+ export { db };
121
+ `);
122
+ expect(modelFile).toEqual("original");
123
+ expect(configFile).toEqual("original");
124
+ });
125
+ });
@@ -0,0 +1 @@
1
+ export declare const generateConfigFile: (directory: string) => string;
@@ -0,0 +1,16 @@
1
+ import { unindent } from "@casekit/unindent";
2
+ export const generateConfigFile = (directory) => unindent `
3
+ import { type Config, orm } from "@casekit/orm";
4
+ import type { OrmCLIConfig } from "@casekit/orm-cli";
5
+
6
+ import { models } from "./${directory.replace(/^\.\//, "")}/models";
7
+
8
+ const config = {
9
+ models,
10
+ } as const satisfies Config;
11
+
12
+ export default {
13
+ db: orm(config),
14
+ directory: "${directory}",
15
+ } satisfies OrmCLIConfig;
16
+ `;
@@ -0,0 +1 @@
1
+ export declare const generateDbFile: () => string;
@@ -0,0 +1,37 @@
1
+ import { unindent } from "@casekit/unindent";
2
+ export const generateDbFile = () => unindent `
3
+ import { type Config, type ModelType, type Orm, orm } from "@casekit/orm";
4
+ import { models } from "./models";
5
+
6
+ const config = {
7
+ models,
8
+ } as const satisfies Config;
9
+
10
+ let db: Orm<typeof config>;
11
+
12
+ declare global {
13
+ // eslint-disable-next-line no-var
14
+ var __db: Orm<typeof config>;
15
+ }
16
+
17
+ // we do this because in development we don't want to restart
18
+ // the server with every change, but we want to make sure we don't
19
+ // create a new connection to the DB with every change either.
20
+ if (process.env.NODE_ENV === "production") {
21
+ db = orm(config);
22
+ await db.connect();
23
+ } else {
24
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
25
+ if (!global.__db) {
26
+ global.__db = orm(config);
27
+ await global.__db.connect();
28
+ }
29
+ db = global.__db;
30
+ }
31
+
32
+ export type DB = Orm<typeof config>;
33
+ export type Models = typeof models;
34
+ export type Model<M extends keyof Models> = ModelType<Models[M]>;
35
+
36
+ export { db };
37
+ `;
@@ -0,0 +1 @@
1
+ export declare const generateModelFile: (name: string, directory: string) => Promise<string>;
@@ -0,0 +1,11 @@
1
+ import path from "node:path";
2
+ import { prettify } from "#util/prettify.js";
3
+ export const generateModelFile = async (name, directory) => {
4
+ const code = `import type { ModelDefinition } from "@casekit/orm";
5
+
6
+ export const ${name} = {
7
+ fields: {},
8
+ } as const satisfies ModelDefinition;
9
+ `;
10
+ return await prettify(path.join(process.cwd(), directory), code);
11
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import { vol } from "memfs";
2
+ import { beforeAll, describe, expect, test } from "vitest";
3
+ import { unindent } from "@casekit/unindent";
4
+ import { generateModelFile } from "./generateModelFile.js";
5
+ describe("generateModelFile", () => {
6
+ beforeAll(() => {
7
+ vol.fromJSON({}, "/project");
8
+ });
9
+ test("generates an empty model definition", async () => {
10
+ const result = await generateModelFile("book", "./app/db.server");
11
+ expect(result.trim()).toBe(unindent `
12
+ import type { ModelDefinition } from "@casekit/orm";
13
+
14
+ export const book = {
15
+ fields: {},
16
+ } as const satisfies ModelDefinition;
17
+ `);
18
+ });
19
+ });
@@ -0,0 +1 @@
1
+ export declare const generateModelsFile: (models: string[]) => string;
@@ -0,0 +1,8 @@
1
+ import { unindent } from "@casekit/unindent";
2
+ export const generateModelsFile = (models) => unindent `
3
+ ${models.map((m) => `import { ${m} } from "./${m}";`).join("\n")};
4
+
5
+ export const models = {
6
+ ${models.map((m) => `${m},`).join("\n")}
7
+ }
8
+ `;
@@ -0,0 +1 @@
1
+ export { OrmCLIConfig } from "./types.js";
package/build/index.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ export declare const globalOptions: {
2
+ readonly config: {
3
+ readonly type: "string";
4
+ readonly alias: "c";
5
+ readonly describe: "Path to the orm CLIs configuration file";
6
+ readonly default: "orm.config.ts";
7
+ };
8
+ readonly force: {
9
+ readonly type: "boolean";
10
+ readonly desc: "Skip all prompts, use defaults, and overwrite existing files without asking for confirmation";
11
+ };
12
+ };
@@ -0,0 +1,12 @@
1
+ export const globalOptions = {
2
+ config: {
3
+ type: "string",
4
+ alias: "c",
5
+ describe: "Path to the orm CLIs configuration file",
6
+ default: "orm.config.ts",
7
+ },
8
+ force: {
9
+ type: "boolean",
10
+ desc: "Skip all prompts, use defaults, and overwrite existing files without asking for confirmation",
11
+ },
12
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,35 @@
1
+ import { vol } from "memfs";
2
+ import pg from "pg";
3
+ import { register } from "tsx/esm/api";
4
+ import { afterAll, afterEach, beforeAll, beforeEach, vi } from "vitest";
5
+ import * as loadConfig from "#util/loadConfig.js";
6
+ vi.mock("fs");
7
+ vi.mock("prettier");
8
+ vi.mock("@inquirer/prompts");
9
+ let unregister;
10
+ beforeAll(async () => {
11
+ const client = new pg.Client();
12
+ await client.connect();
13
+ await client.query("DROP SCHEMA IF EXISTS orm_cli_test CASCADE");
14
+ unregister = register();
15
+ });
16
+ afterAll(async () => {
17
+ await unregister();
18
+ });
19
+ beforeEach(async () => {
20
+ const path = "./orm.config.ts";
21
+ const { default: config } = await import(path);
22
+ await config.db.connect();
23
+ process.on("exit", async function () {
24
+ await config.db.close();
25
+ });
26
+ vi.spyOn(loadConfig, "loadConfig").mockResolvedValue(config);
27
+ const originalFs = await vi.importActual("fs");
28
+ const ormConfig = originalFs.readFileSync("./src/test/orm.config.ts", "utf8");
29
+ const prettierConfig = originalFs.readFileSync("../../.prettierrc.json", "utf8");
30
+ vol.fromJSON({ "orm.config.ts": ormConfig, ".prettierrc.json": prettierConfig }, "/project");
31
+ vi.spyOn(process, "cwd").mockReturnValue("/project");
32
+ });
33
+ afterEach(() => {
34
+ vol.reset();
35
+ });
@@ -0,0 +1,14 @@
1
+ import { ConnectionConfig } from "pg";
2
+ import { ArgumentsCamelCase, InferredOptionTypes, Options } from "yargs";
3
+ import { Orm } from "@casekit/orm";
4
+ import { globalOptions } from "#options.js";
5
+ export interface OrmCLIConfig {
6
+ db: Orm;
7
+ directory: string;
8
+ migrate?: {
9
+ connection?: ConnectionConfig;
10
+ };
11
+ }
12
+ export type Builder = Record<string, Options>;
13
+ export type CommandOptions<T extends Builder = Record<string, never>> = ArgumentsCamelCase<InferredOptionTypes<T & typeof globalOptions>>;
14
+ export type Handler<T extends Builder = Record<string, never>> = (opts: ArgumentsCamelCase<InferredOptionTypes<T & typeof globalOptions>>) => void | Promise<void>;
package/build/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare const createOrOverwriteFile: (filePath: string, content: string, force?: boolean) => Promise<string | undefined>;
@@ -0,0 +1,17 @@
1
+ import { confirm } from "@inquirer/prompts";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ export const createOrOverwriteFile = async (filePath, content, force) => {
5
+ const fullPath = path.join(process.cwd(), filePath);
6
+ if (fs.existsSync(fullPath) && !force) {
7
+ const overwrite = await confirm({
8
+ message: `${filePath} already exists - overwrite it?`,
9
+ default: false,
10
+ });
11
+ if (!overwrite)
12
+ return;
13
+ }
14
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
15
+ fs.writeFileSync(fullPath, content);
16
+ return fullPath;
17
+ };
@@ -0,0 +1,3 @@
1
+ import { globalOptions } from "#options.js";
2
+ import { CommandOptions, OrmCLIConfig } from "#types.js";
3
+ export declare const loadConfig: (options: CommandOptions<typeof globalOptions>) => Promise<OrmCLIConfig>;
@@ -0,0 +1,21 @@
1
+ import dotenv from "dotenv";
2
+ import path from "path";
3
+ import { register } from "tsx/esm/api";
4
+ export const loadConfig = async (options) => {
5
+ try {
6
+ dotenv.config();
7
+ const unregister = register();
8
+ const { default: config } = await import(path.join(process.cwd(), options.config));
9
+ await unregister();
10
+ const c = "default" in config ? config.default : config;
11
+ await c.db.connect();
12
+ process.on("exit", async function () {
13
+ await c.db.close();
14
+ });
15
+ return c;
16
+ }
17
+ catch (e) {
18
+ console.error(e instanceof Error ? e.message : e);
19
+ process.exit(1);
20
+ }
21
+ };
@@ -0,0 +1 @@
1
+ export declare const prettify: (path: string, content: string) => Promise<string>;
@@ -0,0 +1,8 @@
1
+ import * as prettier from "prettier";
2
+ export const prettify = async (path, content) => {
3
+ const prettierOptions = await prettier.resolveConfig(path);
4
+ return await prettier.format(content, {
5
+ ...prettierOptions,
6
+ parser: "typescript",
7
+ });
8
+ };