@casekit/orm2-cli 0.0.0-20250331181319 → 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +1 -2
- package/build/commands/db-drop/handler.js +0 -3
- package/build/commands/db-drop.test.js +1 -0
- package/build/commands/db-pull/handler.js +21 -8
- package/build/commands/db-pull/options.d.ts +5 -0
- package/build/commands/db-pull/options.js +5 -0
- package/build/commands/db-pull/util/relationNames.d.ts +3 -0
- package/build/commands/db-pull/util/relationNames.js +14 -0
- package/build/commands/db-pull/util/relationNames.test.js +61 -0
- package/build/commands/db-pull/util/renderDefault.d.ts +3 -0
- package/build/commands/db-pull/util/renderDefault.js +46 -0
- package/build/commands/db-pull/util/renderDefault.test.d.ts +1 -0
- package/build/commands/db-pull/util/renderDefault.test.js +67 -0
- package/build/commands/db-pull/util/renderFieldDefinition.d.ts +2 -0
- package/build/commands/db-pull/util/renderFieldDefinition.js +50 -0
- package/build/commands/db-pull/util/renderFieldDefinition.test.d.ts +1 -0
- package/build/commands/db-pull/util/renderFieldDefinition.test.js +182 -0
- package/build/commands/db-pull/util/renderModel.basic.test.d.ts +1 -0
- package/build/commands/db-pull/util/renderModel.basic.test.js +169 -0
- package/build/commands/db-pull/util/renderModel.constraints.test.d.ts +1 -0
- package/build/commands/db-pull/util/renderModel.constraints.test.js +677 -0
- package/build/commands/db-pull/util/renderModel.d.ts +2 -0
- package/build/commands/db-pull/util/renderModel.defaultValues.test.d.ts +1 -0
- package/build/commands/db-pull/util/renderModel.defaultValues.test.js +518 -0
- package/build/commands/db-pull/util/renderModel.js +88 -0
- package/build/commands/db-pull/util/renderModel.relations.test.d.ts +1 -0
- package/build/commands/db-pull/util/renderModel.relations.test.js +880 -0
- package/build/commands/db-pull/util/renderModel.types.test.d.ts +1 -0
- package/build/commands/db-pull/util/renderModel.types.test.js +703 -0
- package/build/commands/db-pull/util/renderRelations.d.ts +2 -0
- package/build/commands/db-pull/util/renderRelations.js +55 -0
- package/build/commands/db-pull/util/renderRelations.test.d.ts +1 -0
- package/build/commands/db-pull/util/renderRelations.test.js +165 -0
- package/build/commands/db-pull/util/renderType.d.ts +6 -0
- package/build/commands/db-pull/util/renderType.js +55 -0
- package/build/commands/db-pull/util/renderType.test.d.ts +1 -0
- package/build/commands/db-pull/util/renderType.test.js +46 -0
- package/build/commands/db-pull.d.ts +10 -0
- package/build/commands/db-pull.test.js +625 -0
- package/build/commands/db-push/handler.js +0 -3
- package/build/commands/init/handler.js +16 -3
- package/build/commands/init/util/generateConfigFile.js +2 -3
- package/build/commands/init/util/generateDbFile.js +13 -24
- package/build/commands/init/util/generateModelsFile.d.ts +1 -1
- package/build/commands/init/util/generateModelsFile.js +6 -2
- package/build/commands/init.test.js +19 -31
- package/build/types.d.ts +2 -2
- package/build/util/createOrOverwriteFile.d.ts +1 -1
- package/build/util/createOrOverwriteFile.js +1 -1
- package/build/util/loadConfig.js +5 -1
- package/package.json +26 -25
- package/build/commands/generate-model/handler.d.ts +0 -3
- package/build/commands/generate-model/handler.js +0 -10
- package/build/commands/generate-model/options.d.ts +0 -18
- package/build/commands/generate-model/options.js +0 -18
- package/build/commands/generate-model/util/generateModelFile.d.ts +0 -3
- package/build/commands/generate-model/util/generateModelFile.js +0 -59
- package/build/commands/generate-model/util/generateModelFile.test.js +0 -67
- package/build/commands/generate-model/util/regenerateModelsFile.d.ts +0 -2
- package/build/commands/generate-model/util/regenerateModelsFile.js +0 -50
- package/build/commands/generate-model.d.ts +0 -40
- package/build/commands/generate-model.js +0 -8
- package/build/commands/generate-model.test.js +0 -55
- /package/build/commands/{generate-model/util/generateModelFile.test.d.ts → db-pull/util/relationNames.test.d.ts} +0 -0
- /package/build/commands/{generate-model.test.d.ts → db-pull.test.d.ts} +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { unindent } from "@casekit/unindent";
|
|
2
2
|
export const generateConfigFile = (directory) => unindent `
|
|
3
|
-
import { orm } from "@casekit/orm2";
|
|
4
3
|
import type { OrmCLIConfig } from "@casekit/orm2-cli";
|
|
5
4
|
|
|
6
|
-
import {
|
|
5
|
+
import { config } from "./${directory.replace(/^\.\//, "")}/config";
|
|
7
6
|
|
|
8
7
|
export default {
|
|
9
|
-
db: orm(
|
|
8
|
+
db: orm(config),
|
|
10
9
|
directory: "${directory}",
|
|
11
10
|
} satisfies OrmCLIConfig;
|
|
12
11
|
`;
|
|
@@ -3,24 +3,15 @@ import { usesDevServer } from "#util/usesDevServer.js";
|
|
|
3
3
|
export const generateDbFile = () => {
|
|
4
4
|
return usesDevServer()
|
|
5
5
|
? unindent `
|
|
6
|
-
import { Config, ModelType, Orm, orm } from "@casekit/orm2";
|
|
6
|
+
import { type Config, type ModelType, type Orm, orm } from "@casekit/orm2";
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { type Models, config } from "./config";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export type Model<M extends keyof Models> = ModelType<Models, M>;
|
|
13
|
-
|
|
14
|
-
export const config: Config<Models> = {
|
|
15
|
-
models,
|
|
16
|
-
pool: true,
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
let db: Orm<Models>;
|
|
10
|
+
let db: Orm<typeof config>;
|
|
20
11
|
|
|
21
12
|
declare global {
|
|
22
13
|
// eslint-disable-next-line no-var
|
|
23
|
-
var __db: Orm<
|
|
14
|
+
var __db: Orm<typeof config>;
|
|
24
15
|
}
|
|
25
16
|
|
|
26
17
|
// we do this because in development we don't want to restart
|
|
@@ -38,24 +29,22 @@ export const generateDbFile = () => {
|
|
|
38
29
|
db = global.__db;
|
|
39
30
|
}
|
|
40
31
|
|
|
32
|
+
export type DB = Orm<typeof config>;
|
|
33
|
+
export type Model<M extends keyof Models> = ModelType<Models[M]>;
|
|
34
|
+
|
|
41
35
|
export { db };
|
|
42
36
|
`
|
|
43
37
|
: unindent `
|
|
44
|
-
import { Config, ModelType, Orm, orm } from "@casekit/orm2";
|
|
38
|
+
import { type Config, type ModelType, type Orm, orm } from "@casekit/orm2";
|
|
45
39
|
|
|
46
|
-
import {
|
|
40
|
+
import { type Models, config } from "./config";
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
export type Model<M extends keyof Models> = ModelType<Models, M>;
|
|
50
|
-
|
|
51
|
-
export const config: Config<Models> = {
|
|
52
|
-
models,
|
|
53
|
-
pool: true,
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const db: Orm<Models> = orm(config);
|
|
42
|
+
const db: Orm<typeof config> = orm(config);
|
|
57
43
|
await db.connect();
|
|
58
44
|
|
|
45
|
+
export type DB = Orm<typeof config>;
|
|
46
|
+
export type Model<M extends keyof Models> = ModelType<Models[M]>;
|
|
47
|
+
|
|
59
48
|
export { db };
|
|
60
49
|
`;
|
|
61
50
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const generateModelsFile: () => string;
|
|
1
|
+
export declare const generateModelsFile: (models: string[]) => string;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { unindent } from "@casekit/unindent";
|
|
2
|
-
export const generateModelsFile = () => unindent `
|
|
3
|
-
|
|
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
|
+
}
|
|
4
8
|
`;
|
|
@@ -16,28 +16,19 @@ describe("init", () => {
|
|
|
16
16
|
vi.spyOn(prompts, "input").mockResolvedValueOnce("./app/db.server");
|
|
17
17
|
vi.spyOn(prompts, "confirm").mockResolvedValueOnce(true);
|
|
18
18
|
await yargs().command(init).parseAsync("init");
|
|
19
|
-
const dbFile = vol.readFileSync("./app/db.server/
|
|
19
|
+
const dbFile = vol.readFileSync("./app/db.server/index.ts", "utf-8");
|
|
20
20
|
const modelFile = vol.readFileSync("./app/db.server/models/index.ts", "utf-8");
|
|
21
21
|
const configFile = vol.readFileSync("./orm.config.ts", "utf-8");
|
|
22
22
|
expect(dbFile).toEqual(unindent `
|
|
23
|
-
import { Config, ModelType, Orm, orm } from "@casekit/orm2";
|
|
23
|
+
import { type Config, type ModelType, type Orm, orm } from "@casekit/orm2";
|
|
24
24
|
|
|
25
|
-
import {
|
|
25
|
+
import { type Models, config } from "./config";
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export type Model<M extends keyof Models> = ModelType<Models, M>;
|
|
30
|
-
|
|
31
|
-
export const config: Config<Models> = {
|
|
32
|
-
models,
|
|
33
|
-
pool: true,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
let db: Orm<Models>;
|
|
27
|
+
let db: Orm<typeof config>;
|
|
37
28
|
|
|
38
29
|
declare global {
|
|
39
30
|
// eslint-disable-next-line no-var
|
|
40
|
-
var __db: Orm<
|
|
31
|
+
var __db: Orm<typeof config>;
|
|
41
32
|
}
|
|
42
33
|
|
|
43
34
|
// we do this because in development we don't want to restart
|
|
@@ -55,19 +46,21 @@ describe("init", () => {
|
|
|
55
46
|
db = global.__db;
|
|
56
47
|
}
|
|
57
48
|
|
|
49
|
+
export type DB = Orm<typeof config>;
|
|
50
|
+
export type Model<M extends keyof Models> = ModelType<Models[M]>;
|
|
51
|
+
|
|
58
52
|
export { db };
|
|
59
53
|
`);
|
|
60
|
-
expect(modelFile).toEqual(unindent `
|
|
54
|
+
expect(modelFile.trim()).toEqual(unindent `
|
|
61
55
|
export const models = {};
|
|
62
56
|
`);
|
|
63
57
|
expect(configFile).toEqual(unindent `
|
|
64
|
-
import { orm } from "@casekit/orm2";
|
|
65
58
|
import type { OrmCLIConfig } from "@casekit/orm2-cli";
|
|
66
59
|
|
|
67
|
-
import {
|
|
60
|
+
import { config } from "./app/db.server/config";
|
|
68
61
|
|
|
69
62
|
export default {
|
|
70
|
-
db: orm(
|
|
63
|
+
db: orm(config),
|
|
71
64
|
directory: "./app/db.server",
|
|
72
65
|
} satisfies OrmCLIConfig;
|
|
73
66
|
`);
|
|
@@ -77,7 +70,7 @@ describe("init", () => {
|
|
|
77
70
|
"package.json": JSON.stringify({ dependencies: {} }),
|
|
78
71
|
"app/db.server/models/index.ts": "original",
|
|
79
72
|
"orm.config.ts": "original",
|
|
80
|
-
"app/db.server/
|
|
73
|
+
"app/db.server/index.ts": "original",
|
|
81
74
|
}, "/project");
|
|
82
75
|
vi.spyOn(prompts, "input").mockResolvedValueOnce("./app/db.server");
|
|
83
76
|
vi.spyOn(prompts, "confirm")
|
|
@@ -85,25 +78,20 @@ describe("init", () => {
|
|
|
85
78
|
.mockResolvedValueOnce(false)
|
|
86
79
|
.mockResolvedValueOnce(false);
|
|
87
80
|
await yargs().command(init).parseAsync("init");
|
|
88
|
-
const dbFile = vol.readFileSync("./app/db.server/
|
|
81
|
+
const dbFile = vol.readFileSync("./app/db.server/index.ts", "utf-8");
|
|
89
82
|
const modelFile = vol.readFileSync("./app/db.server/models/index.ts", "utf-8");
|
|
90
83
|
const configFile = vol.readFileSync("./orm.config.ts", "utf-8");
|
|
91
84
|
expect(dbFile).toEqual(unindent `
|
|
92
|
-
import { Config, ModelType, Orm, orm } from "@casekit/orm2";
|
|
85
|
+
import { type Config, type ModelType, type Orm, orm } from "@casekit/orm2";
|
|
93
86
|
|
|
94
|
-
import {
|
|
87
|
+
import { type Models, config } from "./config";
|
|
95
88
|
|
|
96
|
-
|
|
97
|
-
export type Model<M extends keyof Models> = ModelType<Models, M>;
|
|
98
|
-
|
|
99
|
-
export const config: Config<Models> = {
|
|
100
|
-
models,
|
|
101
|
-
pool: true,
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const db: Orm<Models> = orm(config);
|
|
89
|
+
const db: Orm<typeof config> = orm(config);
|
|
105
90
|
await db.connect();
|
|
106
91
|
|
|
92
|
+
export type DB = Orm<typeof config>;
|
|
93
|
+
export type Model<M extends keyof Models> = ModelType<Models[M]>;
|
|
94
|
+
|
|
107
95
|
export { db };
|
|
108
96
|
`);
|
|
109
97
|
expect(modelFile).toEqual("original");
|
package/build/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ConnectionConfig } from "pg";
|
|
2
2
|
import { ArgumentsCamelCase, InferredOptionTypes, Options } from "yargs";
|
|
3
|
-
import {
|
|
3
|
+
import { ZodType } from "zod";
|
|
4
4
|
import { ModelDefinition, Orm } from "@casekit/orm2";
|
|
5
5
|
import { globalOptions } from "#options.js";
|
|
6
6
|
export interface OrmCLIConfig {
|
|
@@ -8,7 +8,7 @@ export interface OrmCLIConfig {
|
|
|
8
8
|
directory: string;
|
|
9
9
|
generate?: {
|
|
10
10
|
templates?: Record<string, ModelDefinition>;
|
|
11
|
-
defaultSchemas?: Record<string,
|
|
11
|
+
defaultSchemas?: Record<string, ZodType>;
|
|
12
12
|
};
|
|
13
13
|
migrate?: {
|
|
14
14
|
connection?: ConnectionConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const createOrOverwriteFile: (filePath: string, content: string, force?: boolean) => Promise<
|
|
1
|
+
export declare const createOrOverwriteFile: (filePath: string, content: string, force?: boolean) => Promise<string | undefined>;
|
package/build/util/loadConfig.js
CHANGED
|
@@ -5,7 +5,11 @@ export const loadConfig = async (options) => {
|
|
|
5
5
|
const unregister = register();
|
|
6
6
|
const { default: config } = await import(path.join(process.cwd(), options.config));
|
|
7
7
|
await unregister();
|
|
8
|
-
|
|
8
|
+
const c = "default" in config ? config.default : config;
|
|
9
|
+
process.on("exit", async function () {
|
|
10
|
+
await c.db.close();
|
|
11
|
+
});
|
|
12
|
+
return c;
|
|
9
13
|
}
|
|
10
14
|
catch (e) {
|
|
11
15
|
console.error(e instanceof Error ? e.message : e);
|
package/package.json
CHANGED
|
@@ -1,45 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@casekit/orm2-cli",
|
|
3
3
|
"description": "",
|
|
4
|
-
"version": "0.0.0
|
|
4
|
+
"version": "0.0.0",
|
|
5
5
|
"author": "",
|
|
6
6
|
"bin": {
|
|
7
7
|
"orm": "./build/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@casekit/unindent": "^1.0.5",
|
|
11
|
-
"@inquirer/prompts": "^7.
|
|
12
|
-
"@inquirer/testing": "^2.1.
|
|
11
|
+
"@inquirer/prompts": "^7.5.3",
|
|
12
|
+
"@inquirer/testing": "^2.1.47",
|
|
13
13
|
"byline": "^5.0.0",
|
|
14
14
|
"camelcase": "^8.0.0",
|
|
15
|
-
"es-toolkit": "^1.
|
|
16
|
-
"jscodeshift": "^17.
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"@casekit/orm2": "0.0.0
|
|
21
|
-
"@casekit/sql": "0.0.0
|
|
22
|
-
"@casekit/
|
|
15
|
+
"es-toolkit": "^1.39.3",
|
|
16
|
+
"jscodeshift": "^17.3.0",
|
|
17
|
+
"pluralize": "^8.0.0",
|
|
18
|
+
"tsx": "^4.20.3",
|
|
19
|
+
"yargs": "^18.0.0",
|
|
20
|
+
"@casekit/orm2": "0.0.0",
|
|
21
|
+
"@casekit/sql": "0.0.0",
|
|
22
|
+
"@casekit/orm2-migrate": "0.0.0",
|
|
23
|
+
"@casekit/toolbox": "0.0.0"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
|
25
26
|
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
|
26
27
|
"@types/byline": "^4.2.36",
|
|
27
|
-
"@types/jscodeshift": "^
|
|
28
|
-
"@types/node": "^
|
|
29
|
-
"@types/pg": "^8.
|
|
28
|
+
"@types/jscodeshift": "^17.3.0",
|
|
29
|
+
"@types/node": "^24.0.3",
|
|
30
|
+
"@types/pg": "^8.15.4",
|
|
31
|
+
"@types/pluralize": "^0.0.33",
|
|
30
32
|
"@types/yargs": "^17.0.33",
|
|
31
|
-
"@vitest/coverage-v8": "^3.
|
|
32
|
-
"dotenv": "^16.
|
|
33
|
-
"memfs": "^4.17.
|
|
33
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
34
|
+
"dotenv": "^16.5.0",
|
|
35
|
+
"memfs": "^4.17.2",
|
|
34
36
|
"prettier": "^3.5.3",
|
|
35
|
-
"prettier-plugin-svelte": "^3.
|
|
36
|
-
"typescript": "^5.8.
|
|
37
|
+
"prettier-plugin-svelte": "^3.4.0",
|
|
38
|
+
"typescript": "^5.8.3",
|
|
37
39
|
"vite-tsconfig-paths": "^5.1.4",
|
|
38
|
-
"vitest": "^3.
|
|
39
|
-
"zod": "^
|
|
40
|
-
"@casekit/
|
|
41
|
-
"@casekit/orm2-fixtures": "0.0.0
|
|
42
|
-
"@casekit/
|
|
40
|
+
"vitest": "^3.2.4",
|
|
41
|
+
"zod": "^4.0.17",
|
|
42
|
+
"@casekit/tsconfig": "0.0.0",
|
|
43
|
+
"@casekit/orm2-fixtures": "0.0.0",
|
|
44
|
+
"@casekit/prettier-config": "0.0.0"
|
|
43
45
|
},
|
|
44
46
|
"exports": {
|
|
45
47
|
".": "./build/index.js"
|
|
@@ -52,7 +54,6 @@
|
|
|
52
54
|
},
|
|
53
55
|
"keywords": [],
|
|
54
56
|
"license": "ISC",
|
|
55
|
-
"main": "index.js",
|
|
56
57
|
"peerDependencies": {
|
|
57
58
|
"pg": "^8.13.1"
|
|
58
59
|
},
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { createOrOverwriteFile } from "#util/createOrOverwriteFile.js";
|
|
2
|
-
import { loadConfig } from "#util/loadConfig.js";
|
|
3
|
-
import { generateModelFile } from "./util/generateModelFile.js";
|
|
4
|
-
import { regenerateModelsFile } from "./util/regenerateModelsFile.js";
|
|
5
|
-
export const handler = async (opts) => {
|
|
6
|
-
const config = await loadConfig(opts);
|
|
7
|
-
const modelFile = await generateModelFile(opts);
|
|
8
|
-
await createOrOverwriteFile(`${config.directory}/models/${opts.name}.ts`, modelFile, opts.force);
|
|
9
|
-
await regenerateModelsFile(config, opts.name);
|
|
10
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
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
|
-
readonly template: {
|
|
8
|
-
readonly type: "string";
|
|
9
|
-
readonly desc: "Template to use to generate the model. Pass --template none if you have a default template defined but don't want to use it.";
|
|
10
|
-
readonly default: "default";
|
|
11
|
-
};
|
|
12
|
-
readonly fields: {
|
|
13
|
-
readonly type: "string";
|
|
14
|
-
readonly desc: "Fields to generate for the model, in the format 'name:type name:type'";
|
|
15
|
-
readonly array: true;
|
|
16
|
-
readonly default: readonly [];
|
|
17
|
-
};
|
|
18
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export const builder = {
|
|
2
|
-
name: {
|
|
3
|
-
type: "string",
|
|
4
|
-
desc: "Name of the model",
|
|
5
|
-
demandOption: true,
|
|
6
|
-
},
|
|
7
|
-
template: {
|
|
8
|
-
type: "string",
|
|
9
|
-
desc: "Template to use to generate the model. Pass --template none if you have a default template defined but don't want to use it.",
|
|
10
|
-
default: "default",
|
|
11
|
-
},
|
|
12
|
-
fields: {
|
|
13
|
-
type: "string",
|
|
14
|
-
desc: "Fields to generate for the model, in the format 'name:type name:type'",
|
|
15
|
-
array: true,
|
|
16
|
-
default: [],
|
|
17
|
-
},
|
|
18
|
-
};
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import jscodeshift from "jscodeshift";
|
|
3
|
-
import { prettify } from "#util/prettify.js";
|
|
4
|
-
export const generateModelFile = async (opts) => {
|
|
5
|
-
const config = fs.readFileSync(opts.config, "utf-8");
|
|
6
|
-
const j = jscodeshift.withParser("ts");
|
|
7
|
-
const root = j(config);
|
|
8
|
-
const templateContent = root
|
|
9
|
-
.find(j.ExportDefaultDeclaration)
|
|
10
|
-
.find(j.ObjectExpression)
|
|
11
|
-
.find(j.ObjectProperty, { key: { name: "templates" } })
|
|
12
|
-
.find(j.ObjectExpression)
|
|
13
|
-
.find(j.ObjectProperty, { key: { name: opts.template } })
|
|
14
|
-
.find(j.ObjectExpression);
|
|
15
|
-
if (templateContent.length === 0) {
|
|
16
|
-
if (opts.template !== "default") {
|
|
17
|
-
console.warn(`Template "${opts.template}" not found.`);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
const templateAst = templateContent.length === 0
|
|
21
|
-
? j("{ fields: {} }")
|
|
22
|
-
: j(templateContent.get().node);
|
|
23
|
-
const fieldsObj = templateAst
|
|
24
|
-
.find(j.ObjectProperty, {
|
|
25
|
-
key: { name: "fields" },
|
|
26
|
-
})
|
|
27
|
-
.find(j.ObjectExpression);
|
|
28
|
-
for (const field of opts.fields) {
|
|
29
|
-
const [name, type] = field.split(":");
|
|
30
|
-
if (!name || !type) {
|
|
31
|
-
console.error("Invalid field definition:", field);
|
|
32
|
-
process.exit(1);
|
|
33
|
-
}
|
|
34
|
-
const existingProperties = fieldsObj.get().node.properties ?? [];
|
|
35
|
-
const filteredProperties = existingProperties.filter((prop) => prop.type === "ObjectProperty" &&
|
|
36
|
-
prop.key.type === "Identifier" &&
|
|
37
|
-
prop.key.name !== name);
|
|
38
|
-
fieldsObj.replaceWith(j.objectExpression([
|
|
39
|
-
...filteredProperties.map((prop) => j.objectProperty(prop.key, prop.value)),
|
|
40
|
-
j.objectProperty(j.identifier(name), j.objectExpression([
|
|
41
|
-
j.objectProperty(j.identifier("type"), j.stringLiteral(type)),
|
|
42
|
-
])),
|
|
43
|
-
]));
|
|
44
|
-
}
|
|
45
|
-
templateAst.find(j.EmptyStatement).remove();
|
|
46
|
-
const usesSqlLiteral = templateAst.find(j.TaggedTemplateExpression).length > 0;
|
|
47
|
-
// we avoid trailing commas and strip newlines so
|
|
48
|
-
// prettier won't break code onto multiple lines unless necessary.
|
|
49
|
-
// trailing commas will be added in again by prettier.
|
|
50
|
-
const definition = templateAst.toSource({
|
|
51
|
-
trailingComma: false,
|
|
52
|
-
lineTerminator: " ",
|
|
53
|
-
});
|
|
54
|
-
const result = `
|
|
55
|
-
import { ModelDefinition ${usesSqlLiteral ? ", sql" : ""} } from "@casekit/orm2";
|
|
56
|
-
|
|
57
|
-
export const ${opts.name} = ${definition} as const satisfies ModelDefinition;`;
|
|
58
|
-
return await prettify(process.cwd(), result);
|
|
59
|
-
};
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { vol } from "memfs";
|
|
2
|
-
import { beforeAll, describe, expect, test, vi } from "vitest";
|
|
3
|
-
import { unindent } from "@casekit/unindent";
|
|
4
|
-
import { generateModelFile } from "./generateModelFile.js";
|
|
5
|
-
describe("generateModelFile", () => {
|
|
6
|
-
beforeAll(() => {
|
|
7
|
-
vol.fromJSON({
|
|
8
|
-
"orm.config.ts": unindent `
|
|
9
|
-
import { sql } from "@casekit/orm2";
|
|
10
|
-
import { type OrmCLIConfig } from "@casekit/orm2-cli";
|
|
11
|
-
|
|
12
|
-
export default {
|
|
13
|
-
directory: "./app/db.server",
|
|
14
|
-
generate: {
|
|
15
|
-
templates: {
|
|
16
|
-
default: {
|
|
17
|
-
fields: {
|
|
18
|
-
id: { type: "uuid", primaryKey: true },
|
|
19
|
-
createdAt: { type: "timestamp", default: sql\`now()\` },
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
foo: {
|
|
23
|
-
fields: {
|
|
24
|
-
name: { type: "text" },
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
} satisfies OrmCLIConfig;
|
|
30
|
-
`,
|
|
31
|
-
}, "/project");
|
|
32
|
-
});
|
|
33
|
-
test("merges fields from the passed `fields` argument, overwriting same-named fields in the template", async () => {
|
|
34
|
-
const result = await generateModelFile({
|
|
35
|
-
config: "orm.config.ts",
|
|
36
|
-
name: "bar",
|
|
37
|
-
template: "default",
|
|
38
|
-
fields: ["id:serial", "name:text"],
|
|
39
|
-
});
|
|
40
|
-
expect(result.trim()).toBe(unindent `
|
|
41
|
-
import { ModelDefinition, sql } from "@casekit/orm2";
|
|
42
|
-
|
|
43
|
-
export const bar = {
|
|
44
|
-
fields: {
|
|
45
|
-
createdAt: { type: "timestamp", default: sql\`now()\` },
|
|
46
|
-
id: { type: "serial" },
|
|
47
|
-
name: { type: "text" },
|
|
48
|
-
},
|
|
49
|
-
} as const satisfies ModelDefinition;
|
|
50
|
-
`);
|
|
51
|
-
});
|
|
52
|
-
test("works if the template does not exist - but logs a warning", async () => {
|
|
53
|
-
const warnSpy = vi.spyOn(console, "warn").mockReturnValue();
|
|
54
|
-
const result = await generateModelFile({
|
|
55
|
-
config: "orm.config.ts",
|
|
56
|
-
name: "foo",
|
|
57
|
-
template: "wrong",
|
|
58
|
-
fields: [],
|
|
59
|
-
});
|
|
60
|
-
expect(warnSpy).toHaveBeenCalledWith(`Template "wrong" not found.`);
|
|
61
|
-
expect(result.trim()).toBe(unindent `
|
|
62
|
-
import { ModelDefinition } from "@casekit/orm2";
|
|
63
|
-
|
|
64
|
-
export const foo = { fields: {} } as const satisfies ModelDefinition;
|
|
65
|
-
`);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import jscodeshift from "jscodeshift";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { prettify } from "#util/prettify.js";
|
|
5
|
-
export const regenerateModelsFile = async (config, name) => {
|
|
6
|
-
console.log(`- Regenerating models/index.ts file: ${config.directory}`);
|
|
7
|
-
const filename = path.join(config.directory, "models/index.ts");
|
|
8
|
-
const source = fs.readFileSync(path.join(process.cwd(), filename), "utf-8");
|
|
9
|
-
const j = jscodeshift.withParser("ts");
|
|
10
|
-
const root = j(source);
|
|
11
|
-
const modelsExport = root
|
|
12
|
-
.find(j.ExportNamedDeclaration)
|
|
13
|
-
.filter((path) => path.node.declaration?.type === "VariableDeclaration" &&
|
|
14
|
-
path.node.declaration?.declarations[0]?.type ===
|
|
15
|
-
"VariableDeclarator" &&
|
|
16
|
-
path.node.declaration?.declarations[0]?.id.type ===
|
|
17
|
-
"Identifier" &&
|
|
18
|
-
path.node.declaration?.declarations[0]?.id.name === "models" &&
|
|
19
|
-
path.node.declaration?.declarations[0]?.init?.type ===
|
|
20
|
-
"ObjectExpression" &&
|
|
21
|
-
path.node.declaration?.declarations[0]?.init?.type ===
|
|
22
|
-
"ObjectExpression")
|
|
23
|
-
.at(0);
|
|
24
|
-
if (modelsExport.size() === 0) {
|
|
25
|
-
console.log(`- No \`models\` object found in models/index.ts. Cannot inject \${name}\` export.`);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
const modelsObject = modelsExport.get().node.declaration.declarations[0].init;
|
|
29
|
-
// Add an import for `name` from `./name` if not already imported
|
|
30
|
-
const existingImport = root
|
|
31
|
-
.find(j.ImportDeclaration)
|
|
32
|
-
.filter((path) => path.node.source.value === `./${name}` &&
|
|
33
|
-
!!path.node.specifiers?.some((specifier) => specifier.type === "ImportSpecifier" &&
|
|
34
|
-
specifier.imported.name === name));
|
|
35
|
-
if (existingImport.size() === 0) {
|
|
36
|
-
const newImport = j.importDeclaration([j.importSpecifier(j.identifier(name))], j.literal(`./${name}`));
|
|
37
|
-
root.get().node.program.body.unshift(newImport);
|
|
38
|
-
modelsObject.properties.push(j.property.from({
|
|
39
|
-
key: j.identifier(name),
|
|
40
|
-
value: j.identifier(name),
|
|
41
|
-
shorthand: true,
|
|
42
|
-
kind: "init",
|
|
43
|
-
}));
|
|
44
|
-
console.log(`- Injected export into ${filename}`);
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
console.log("- Model already exported from index file, skipping");
|
|
48
|
-
}
|
|
49
|
-
fs.writeFileSync(path.join(process.cwd(), filename), await prettify(path.join(process.cwd(), filename), root.toSource()));
|
|
50
|
-
};
|
|
@@ -1,40 +0,0 @@
|
|
|
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
|
-
readonly template: {
|
|
11
|
-
readonly type: "string";
|
|
12
|
-
readonly desc: "Template to use to generate the model. Pass --template none if you have a default template defined but don't want to use it.";
|
|
13
|
-
readonly default: "default";
|
|
14
|
-
};
|
|
15
|
-
readonly fields: {
|
|
16
|
-
readonly type: "string";
|
|
17
|
-
readonly desc: "Fields to generate for the model, in the format 'name:type name:type'";
|
|
18
|
-
readonly array: true;
|
|
19
|
-
readonly default: readonly [];
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
handler: import("../types.js").Handler<{
|
|
23
|
-
readonly name: {
|
|
24
|
-
readonly type: "string";
|
|
25
|
-
readonly desc: "Name of the model";
|
|
26
|
-
readonly demandOption: true;
|
|
27
|
-
};
|
|
28
|
-
readonly template: {
|
|
29
|
-
readonly type: "string";
|
|
30
|
-
readonly desc: "Template to use to generate the model. Pass --template none if you have a default template defined but don't want to use it.";
|
|
31
|
-
readonly default: "default";
|
|
32
|
-
};
|
|
33
|
-
readonly fields: {
|
|
34
|
-
readonly type: "string";
|
|
35
|
-
readonly desc: "Fields to generate for the model, in the format 'name:type name:type'";
|
|
36
|
-
readonly array: true;
|
|
37
|
-
readonly default: readonly [];
|
|
38
|
-
};
|
|
39
|
-
}>;
|
|
40
|
-
};
|