orchid-orm 1.5.29 → 1.5.31
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/dist/index.d.ts +6 -14
- package/dist/index.js +26 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +26 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -22
- package/.env.example +0 -1
- package/.turbo/turbo-check.log +0 -26
- package/.turbo/turbo-test.log +0 -26
- package/.turbo/turbo-test:ci.log +0 -26
- package/CHANGELOG.md +0 -382
- package/coverage/coverage-summary.json +0 -28
- package/jest-setup.ts +0 -11
- package/rollup.config.js +0 -18
- package/src/bin/bin.ts +0 -3
- package/src/bin/init.test.ts +0 -810
- package/src/bin/init.ts +0 -529
- package/src/codegen/appCodeUpdater.test.ts +0 -75
- package/src/codegen/appCodeUpdater.ts +0 -53
- package/src/codegen/createBaseTableFile.test.ts +0 -53
- package/src/codegen/createBaseTableFile.ts +0 -31
- package/src/codegen/fileChanges.ts +0 -41
- package/src/codegen/testUtils.ts +0 -56
- package/src/codegen/tsUtils.ts +0 -180
- package/src/codegen/updateMainFile.test.ts +0 -253
- package/src/codegen/updateMainFile.ts +0 -210
- package/src/codegen/updateTableFile/changeTable.test.ts +0 -804
- package/src/codegen/updateTableFile/changeTable.ts +0 -536
- package/src/codegen/updateTableFile/createTable.test.ts +0 -139
- package/src/codegen/updateTableFile/createTable.ts +0 -51
- package/src/codegen/updateTableFile/renameTable.test.ts +0 -124
- package/src/codegen/updateTableFile/renameTable.ts +0 -67
- package/src/codegen/updateTableFile/updateTableFile.ts +0 -22
- package/src/codegen/utils.ts +0 -13
- package/src/index.ts +0 -5
- package/src/orm.test.ts +0 -92
- package/src/orm.ts +0 -98
- package/src/relations/belongsTo.test.ts +0 -1122
- package/src/relations/belongsTo.ts +0 -352
- package/src/relations/hasAndBelongsToMany.test.ts +0 -1335
- package/src/relations/hasAndBelongsToMany.ts +0 -472
- package/src/relations/hasMany.test.ts +0 -2616
- package/src/relations/hasMany.ts +0 -401
- package/src/relations/hasOne.test.ts +0 -1701
- package/src/relations/hasOne.ts +0 -351
- package/src/relations/relations.test.ts +0 -37
- package/src/relations/relations.ts +0 -363
- package/src/relations/utils.ts +0 -162
- package/src/repo.test.ts +0 -200
- package/src/repo.ts +0 -119
- package/src/table.test.ts +0 -121
- package/src/table.ts +0 -184
- package/src/test-utils/test-db.ts +0 -32
- package/src/test-utils/test-tables.ts +0 -194
- package/src/test-utils/test-utils.ts +0 -119
- package/src/transaction.test.ts +0 -47
- package/src/transaction.ts +0 -27
- package/src/utils.ts +0 -9
- package/tsconfig.json +0 -14
package/src/bin/init.ts
DELETED
|
@@ -1,529 +0,0 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import https from 'https';
|
|
4
|
-
import prompts from 'prompts';
|
|
5
|
-
|
|
6
|
-
export type InitConfig = {
|
|
7
|
-
testDatabase?: boolean;
|
|
8
|
-
addSchemaToZod?: boolean;
|
|
9
|
-
addTestFactory?: boolean;
|
|
10
|
-
demoTables?: boolean;
|
|
11
|
-
timestamp?: 'date' | 'number';
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
type DependencyKind = 'dependencies' | 'devDependencies';
|
|
15
|
-
|
|
16
|
-
const dirPath = path.resolve(process.cwd(), 'src', 'db');
|
|
17
|
-
|
|
18
|
-
export const askOrchidORMConfig = async () => {
|
|
19
|
-
const response = await prompts([
|
|
20
|
-
{
|
|
21
|
-
type: 'select',
|
|
22
|
-
name: 'timestamp',
|
|
23
|
-
message: 'Preferred type of returned timestamps:',
|
|
24
|
-
choices: [
|
|
25
|
-
{
|
|
26
|
-
title: 'string (as returned from db)',
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
title: 'number (epoch)',
|
|
30
|
-
value: 'number',
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
title: 'Date object',
|
|
34
|
-
value: 'date',
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
type: 'confirm',
|
|
40
|
-
name: 'testDatabase',
|
|
41
|
-
message: 'Should I add a separate database for tests?',
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
type: 'confirm',
|
|
45
|
-
name: 'addSchemaToZod',
|
|
46
|
-
message: 'Are you going to use Zod for validation?',
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
type: 'confirm',
|
|
50
|
-
name: 'addTestFactory',
|
|
51
|
-
message: 'Do you want object factories for writing tests?',
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
type: 'confirm',
|
|
55
|
-
name: 'demoTables',
|
|
56
|
-
message: 'Should I add demo tables?',
|
|
57
|
-
},
|
|
58
|
-
]);
|
|
59
|
-
|
|
60
|
-
return response as InitConfig;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export const initOrchidORM = async (config: InitConfig) => {
|
|
64
|
-
await fs.mkdir(dirPath, { recursive: true });
|
|
65
|
-
|
|
66
|
-
await setupPackageJson(config);
|
|
67
|
-
await setupTSConfig();
|
|
68
|
-
await setupEnv(config);
|
|
69
|
-
await setupGitIgnore();
|
|
70
|
-
await setupBaseTable(config);
|
|
71
|
-
await setupTables(config);
|
|
72
|
-
await setupConfig(config);
|
|
73
|
-
await setupMainDb(config);
|
|
74
|
-
await setupMigrationScript(config);
|
|
75
|
-
await createMigrations(config);
|
|
76
|
-
await createSeed(config);
|
|
77
|
-
|
|
78
|
-
greet();
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const setupPackageJson = async (config: InitConfig) => {
|
|
82
|
-
const pairs = await Promise.all([
|
|
83
|
-
getLatestPackageVersion('dotenv', 'dependencies'),
|
|
84
|
-
getLatestPackageVersion('orchid-orm', 'dependencies'),
|
|
85
|
-
getLatestPackageVersion('pqb', 'dependencies'),
|
|
86
|
-
getLatestPackageVersion('pg', 'dependencies'),
|
|
87
|
-
config.addSchemaToZod &&
|
|
88
|
-
getLatestPackageVersion('orchid-orm-schema-to-zod', 'dependencies'),
|
|
89
|
-
getLatestPackageVersion('rake-db', 'devDependencies'),
|
|
90
|
-
config.addTestFactory &&
|
|
91
|
-
getLatestPackageVersion('orchid-orm-test-factory', 'devDependencies'),
|
|
92
|
-
getLatestPackageVersion('@swc/core', 'devDependencies'),
|
|
93
|
-
getLatestPackageVersion('@types/node', 'devDependencies'),
|
|
94
|
-
getLatestPackageVersion('ts-node', 'devDependencies'),
|
|
95
|
-
getLatestPackageVersion('typescript', 'devDependencies'),
|
|
96
|
-
]);
|
|
97
|
-
|
|
98
|
-
const deps: Record<string, string> = {};
|
|
99
|
-
const devDeps: Record<string, string> = {};
|
|
100
|
-
for (const item of pairs) {
|
|
101
|
-
if (!item) continue;
|
|
102
|
-
const [key, { version, kind }] = item;
|
|
103
|
-
(kind === 'dependencies' ? deps : devDeps)[key] = version;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const packageJsonPath = path.resolve(process.cwd(), 'package.json');
|
|
107
|
-
const content = await readFileSafe(packageJsonPath);
|
|
108
|
-
const json = content ? JSON.parse(content) : {};
|
|
109
|
-
|
|
110
|
-
if (!json.scripts) json.scripts = {};
|
|
111
|
-
json.scripts.db = 'ts-node src/db/dbScripts.ts';
|
|
112
|
-
|
|
113
|
-
if (!json.dependencies) json.dependencies = {};
|
|
114
|
-
|
|
115
|
-
for (const key in deps) {
|
|
116
|
-
json.dependencies[key] = deps[key];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (!json.devDependencies) json.devDependencies = {};
|
|
120
|
-
for (const key in devDeps) {
|
|
121
|
-
json.devDependencies[key] = devDeps[key];
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
await fs.writeFile(packageJsonPath, JSON.stringify(json, null, ' ') + '\n');
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const getLatestPackageVersion = (
|
|
128
|
-
name: string,
|
|
129
|
-
kind: DependencyKind,
|
|
130
|
-
): Promise<[string, { version: string; kind: DependencyKind }]> => {
|
|
131
|
-
return new Promise((resolve, reject) => {
|
|
132
|
-
https
|
|
133
|
-
.get(`https://registry.npmjs.org/${name}/latest`, (res) => {
|
|
134
|
-
let data = '';
|
|
135
|
-
res.on('data', (chunk) => (data += chunk));
|
|
136
|
-
res.on('end', () =>
|
|
137
|
-
resolve([name, { version: `^${JSON.parse(data).version}`, kind }]),
|
|
138
|
-
);
|
|
139
|
-
})
|
|
140
|
-
.on('error', reject);
|
|
141
|
-
});
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const readFileSafe = async (path: string) => {
|
|
145
|
-
try {
|
|
146
|
-
return await fs.readFile(path, 'utf-8');
|
|
147
|
-
} catch (err) {
|
|
148
|
-
if ((err as unknown as { code: string }).code === 'ENOENT') {
|
|
149
|
-
return undefined;
|
|
150
|
-
}
|
|
151
|
-
throw err;
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const setupTSConfig = async () => {
|
|
156
|
-
const tsConfigPath = path.resolve(process.cwd(), 'tsconfig.json');
|
|
157
|
-
const content = await readFileSafe(tsConfigPath);
|
|
158
|
-
const json = content ? JSON.parse(content) : {};
|
|
159
|
-
if (!json['ts-node']) {
|
|
160
|
-
json['ts-node'] = {};
|
|
161
|
-
}
|
|
162
|
-
if (!json['ts-node'].swc) {
|
|
163
|
-
json['ts-node'].swc = true;
|
|
164
|
-
}
|
|
165
|
-
if (!json.compilerOptions?.strict) {
|
|
166
|
-
if (!json.compilerOptions) json.compilerOptions = {};
|
|
167
|
-
json.compilerOptions.strict = true;
|
|
168
|
-
await fs.writeFile(tsConfigPath, `${JSON.stringify(json, null, ' ')}\n`);
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
const setupEnv = async (config: InitConfig) => {
|
|
173
|
-
const envPath = path.resolve(process.cwd(), '.env');
|
|
174
|
-
let content = ((await readFileSafe(envPath)) || '').trim();
|
|
175
|
-
let changed = false;
|
|
176
|
-
|
|
177
|
-
if (!content.match(/^DATABASE_URL=/m)) {
|
|
178
|
-
content += `\nDATABASE_URL=postgres://user:password@localhost:5432/dbname?ssl=false`;
|
|
179
|
-
changed = true;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (config.testDatabase && !content.match(/^DATABASE_TEST_URL=/m)) {
|
|
183
|
-
content += `\nDATABASE_TEST_URL=postgres://user:password@localhost:5432/dbname-test?ssl=false`;
|
|
184
|
-
changed = true;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (changed) {
|
|
188
|
-
await fs.writeFile(envPath, `${content.trim()}\n`);
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const setupGitIgnore = async () => {
|
|
193
|
-
const gitignorePath = path.resolve(process.cwd(), '.gitignore');
|
|
194
|
-
let content = ((await readFileSafe(gitignorePath)) || '').trim();
|
|
195
|
-
let changed = false;
|
|
196
|
-
|
|
197
|
-
if (!content.match(/^node_modules\b/m)) {
|
|
198
|
-
content += `\nnode_modules`;
|
|
199
|
-
changed = true;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (!content.match(/^.env\b/m)) {
|
|
203
|
-
content += `\n.env`;
|
|
204
|
-
changed = true;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (changed) {
|
|
208
|
-
await fs.writeFile(gitignorePath, `${content.trim()}\n`);
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
const setupBaseTable = async (config: InitConfig) => {
|
|
213
|
-
const filePath = path.join(dirPath, 'baseTable.ts');
|
|
214
|
-
|
|
215
|
-
let content = `import { createBaseTable } from 'orchid-orm';
|
|
216
|
-
|
|
217
|
-
export const BaseTable = createBaseTable({
|
|
218
|
-
columnTypes: (t) => ({
|
|
219
|
-
...t,
|
|
220
|
-
text: (min = 0, max = Infinity) => t.text(min, max),`;
|
|
221
|
-
|
|
222
|
-
const { timestamp } = config;
|
|
223
|
-
if (timestamp) {
|
|
224
|
-
content += `
|
|
225
|
-
timestamp: <P extends number>(precision?: P) =>
|
|
226
|
-
t.timestamp<P>(precision).${
|
|
227
|
-
timestamp === 'date' ? 'asDate' : 'asNumber'
|
|
228
|
-
}(),`;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
content += `
|
|
232
|
-
}),
|
|
233
|
-
});
|
|
234
|
-
`;
|
|
235
|
-
|
|
236
|
-
await fs.writeFile(filePath, content);
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
const setupTables = async (config: InitConfig) => {
|
|
240
|
-
if (!config.demoTables) return;
|
|
241
|
-
|
|
242
|
-
const tablesDir = path.join(dirPath, 'tables');
|
|
243
|
-
await fs.mkdir(tablesDir, { recursive: true });
|
|
244
|
-
|
|
245
|
-
await fs.writeFile(
|
|
246
|
-
path.join(tablesDir, 'post.table.ts'),
|
|
247
|
-
`import { BaseTable } from '../baseTable';
|
|
248
|
-
import { CommentTable } from './comment.table';
|
|
249
|
-
${
|
|
250
|
-
config.addSchemaToZod
|
|
251
|
-
? `import { tableToZod } from 'orchid-orm-schema-to-zod';\n`
|
|
252
|
-
: ''
|
|
253
|
-
}
|
|
254
|
-
export type Post = PostTable['columns']['type'];
|
|
255
|
-
export class PostTable extends BaseTable {
|
|
256
|
-
table = 'post';
|
|
257
|
-
columns = this.setColumns((t) => ({
|
|
258
|
-
id: t.serial().primaryKey(),
|
|
259
|
-
title: t.text(3, 100).unique(),
|
|
260
|
-
text: t.text(20, 10000),
|
|
261
|
-
...t.timestamps(),
|
|
262
|
-
}));
|
|
263
|
-
|
|
264
|
-
relations = {
|
|
265
|
-
comments: this.hasMany(() => CommentTable, {
|
|
266
|
-
primaryKey: 'id',
|
|
267
|
-
foreignKey: 'postId',
|
|
268
|
-
}),
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
${
|
|
272
|
-
config.addSchemaToZod
|
|
273
|
-
? `\nexport const postSchema = tableToZod(PostTable);\n`
|
|
274
|
-
: ''
|
|
275
|
-
}`,
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
await fs.writeFile(
|
|
279
|
-
path.join(tablesDir, 'comment.table.ts'),
|
|
280
|
-
`import { BaseTable } from '../baseTable';
|
|
281
|
-
import { PostTable } from './post.table';
|
|
282
|
-
${
|
|
283
|
-
config.addSchemaToZod
|
|
284
|
-
? `import { tableToZod } from 'orchid-orm-schema-to-zod';\n`
|
|
285
|
-
: ''
|
|
286
|
-
}
|
|
287
|
-
export type Comment = CommentTable['columns']['type'];
|
|
288
|
-
export class CommentTable extends BaseTable {
|
|
289
|
-
table = 'comment';
|
|
290
|
-
columns = this.setColumns((t) => ({
|
|
291
|
-
id: t.serial().primaryKey(),
|
|
292
|
-
postId: t
|
|
293
|
-
.integer()
|
|
294
|
-
.foreignKey(() => PostTable, 'id')
|
|
295
|
-
.index(),
|
|
296
|
-
text: t.text(5, 1000),
|
|
297
|
-
...t.timestamps(),
|
|
298
|
-
}));
|
|
299
|
-
|
|
300
|
-
relations = {
|
|
301
|
-
post: this.belongsTo(() => PostTable, {
|
|
302
|
-
primaryKey: 'id',
|
|
303
|
-
foreignKey: 'postId',
|
|
304
|
-
}),
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
${
|
|
308
|
-
config.addSchemaToZod
|
|
309
|
-
? `\nexport const commentSchema = tableToZod(CommentTable);\n`
|
|
310
|
-
: ''
|
|
311
|
-
}`,
|
|
312
|
-
);
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
const setupConfig = async (config: InitConfig) => {
|
|
316
|
-
const configPath = path.join(dirPath, 'config.ts');
|
|
317
|
-
|
|
318
|
-
let content = `import 'dotenv/config';
|
|
319
|
-
|
|
320
|
-
const database = {
|
|
321
|
-
databaseURL: process.env.DATABASE_URL,
|
|
322
|
-
};
|
|
323
|
-
if (!database.databaseURL) throw new Error('DATABASE_URL is missing in .env');`;
|
|
324
|
-
|
|
325
|
-
if (config.testDatabase) {
|
|
326
|
-
content += `
|
|
327
|
-
|
|
328
|
-
const testDatabase = {
|
|
329
|
-
databaseURL: process.env.DATABASE_TEST_URL,
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
const allDatabases = [database];
|
|
333
|
-
|
|
334
|
-
if (testDatabase.databaseURL) {
|
|
335
|
-
allDatabases.push(testDatabase);
|
|
336
|
-
}`;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
content += `
|
|
340
|
-
|
|
341
|
-
export const config = {`;
|
|
342
|
-
|
|
343
|
-
if (config.testDatabase) {
|
|
344
|
-
content += `
|
|
345
|
-
allDatabases,`;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
if (config.testDatabase) {
|
|
349
|
-
content += `
|
|
350
|
-
database: process.env.NODE_ENV === 'test' ? testDatabase : database,`;
|
|
351
|
-
} else {
|
|
352
|
-
content += `
|
|
353
|
-
database,`;
|
|
354
|
-
}
|
|
355
|
-
content += `
|
|
356
|
-
};
|
|
357
|
-
`;
|
|
358
|
-
|
|
359
|
-
await fs.writeFile(configPath, content);
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
const setupMainDb = async (config: InitConfig) => {
|
|
363
|
-
let imports = '';
|
|
364
|
-
let tables = '';
|
|
365
|
-
if (config.demoTables) {
|
|
366
|
-
imports += `
|
|
367
|
-
import { PostTable } from './tables/post.table';
|
|
368
|
-
import { CommentTable } from './tables/comment.table';`;
|
|
369
|
-
tables += `
|
|
370
|
-
post: PostTable,
|
|
371
|
-
comment: CommentTable,`;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
const dbPath = path.join(dirPath, 'db.ts');
|
|
375
|
-
await fs.writeFile(
|
|
376
|
-
dbPath,
|
|
377
|
-
`import { orchidORM } from 'orchid-orm';
|
|
378
|
-
import { config } from './config';${imports}
|
|
379
|
-
|
|
380
|
-
export const db = orchidORM(config.database, {${tables}
|
|
381
|
-
});
|
|
382
|
-
`,
|
|
383
|
-
);
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
const setupMigrationScript = async (config: InitConfig) => {
|
|
387
|
-
const filePath = path.join(dirPath, 'dbScripts.ts');
|
|
388
|
-
await fs.writeFile(
|
|
389
|
-
filePath,
|
|
390
|
-
`import { rakeDb } from 'rake-db';
|
|
391
|
-
import { config } from './config';
|
|
392
|
-
import { appCodeUpdater } from 'orchid-orm';
|
|
393
|
-
|
|
394
|
-
rakeDb(${config.testDatabase ? 'config.allDatabases' : 'config.database'}, {
|
|
395
|
-
migrationsPath: './migrations',
|
|
396
|
-
appCodeUpdater: appCodeUpdater({
|
|
397
|
-
tablePath: (tableName) => \`./tables/\${tableName}.table.ts\`,
|
|
398
|
-
baseTablePath: './baseTable.ts',
|
|
399
|
-
baseTableName: 'BaseTable',
|
|
400
|
-
mainFilePath: './db.ts',
|
|
401
|
-
}),
|
|
402
|
-
useCodeUpdater: true, // set to false to disable code updater
|
|
403
|
-
commands: {
|
|
404
|
-
async seed() {
|
|
405
|
-
const { seed } = await import('./seed');
|
|
406
|
-
await seed();
|
|
407
|
-
},
|
|
408
|
-
},
|
|
409
|
-
});
|
|
410
|
-
`,
|
|
411
|
-
);
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
const createMigrations = async (config: InitConfig) => {
|
|
415
|
-
const migrationsPath = path.join(dirPath, 'migrations');
|
|
416
|
-
await fs.mkdir(migrationsPath);
|
|
417
|
-
|
|
418
|
-
if (!config.demoTables) return;
|
|
419
|
-
|
|
420
|
-
const now = new Date();
|
|
421
|
-
|
|
422
|
-
const postPath = path.join(
|
|
423
|
-
migrationsPath,
|
|
424
|
-
`${makeFileTimeStamp(now)}_createPost.ts`,
|
|
425
|
-
);
|
|
426
|
-
await fs.writeFile(
|
|
427
|
-
postPath,
|
|
428
|
-
`import { change } from 'rake-db';
|
|
429
|
-
|
|
430
|
-
change(async (db) => {
|
|
431
|
-
await db.createTable('post', (t) => ({
|
|
432
|
-
id: t.serial().primaryKey(),
|
|
433
|
-
title: t.text().unique(),
|
|
434
|
-
text: t.text(),
|
|
435
|
-
...t.timestamps(),
|
|
436
|
-
}));
|
|
437
|
-
});
|
|
438
|
-
`,
|
|
439
|
-
);
|
|
440
|
-
|
|
441
|
-
now.setTime(now.getTime() + 1000);
|
|
442
|
-
|
|
443
|
-
const commentPath = path.join(
|
|
444
|
-
migrationsPath,
|
|
445
|
-
`${makeFileTimeStamp(now)}_createComment.ts`,
|
|
446
|
-
);
|
|
447
|
-
await fs.writeFile(
|
|
448
|
-
commentPath,
|
|
449
|
-
`import { change } from 'rake-db';
|
|
450
|
-
|
|
451
|
-
change(async (db) => {
|
|
452
|
-
await db.createTable('comment', (t) => ({
|
|
453
|
-
id: t.serial().primaryKey(),
|
|
454
|
-
postId: t.integer().foreignKey('post', 'id').index(),
|
|
455
|
-
text: t.text(),
|
|
456
|
-
...t.timestamps(),
|
|
457
|
-
}));
|
|
458
|
-
});
|
|
459
|
-
`,
|
|
460
|
-
);
|
|
461
|
-
};
|
|
462
|
-
|
|
463
|
-
const makeFileTimeStamp = (now: Date) => {
|
|
464
|
-
return [
|
|
465
|
-
now.getUTCFullYear(),
|
|
466
|
-
now.getUTCMonth() + 1,
|
|
467
|
-
now.getUTCDate(),
|
|
468
|
-
now.getUTCHours(),
|
|
469
|
-
now.getUTCMinutes(),
|
|
470
|
-
now.getUTCSeconds(),
|
|
471
|
-
]
|
|
472
|
-
.map((value) => (value < 10 ? `0${value}` : value))
|
|
473
|
-
.join('');
|
|
474
|
-
};
|
|
475
|
-
|
|
476
|
-
const createSeed = async (config: InitConfig) => {
|
|
477
|
-
const filePath = path.join(dirPath, 'seed.ts');
|
|
478
|
-
|
|
479
|
-
let content;
|
|
480
|
-
if (config.demoTables) {
|
|
481
|
-
content = `await db.post.findBy({ title: 'Sample post' }).orCreate({
|
|
482
|
-
title: 'Post',
|
|
483
|
-
text: 'This is a text for a sample post. It contains words, spaces, and punctuation.',
|
|
484
|
-
comments: {
|
|
485
|
-
create: [
|
|
486
|
-
{
|
|
487
|
-
text: 'Nice post!',
|
|
488
|
-
},
|
|
489
|
-
{
|
|
490
|
-
text: \`Too long, didn't read\`,
|
|
491
|
-
},
|
|
492
|
-
],
|
|
493
|
-
},
|
|
494
|
-
});`;
|
|
495
|
-
} else {
|
|
496
|
-
content = `// create records here`;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
await fs.writeFile(
|
|
500
|
-
filePath,
|
|
501
|
-
`import { db } from './db';
|
|
502
|
-
|
|
503
|
-
export const seed = async () => {
|
|
504
|
-
${content}
|
|
505
|
-
|
|
506
|
-
await db.$close();
|
|
507
|
-
};
|
|
508
|
-
`,
|
|
509
|
-
);
|
|
510
|
-
};
|
|
511
|
-
|
|
512
|
-
const greet = () => {
|
|
513
|
-
console.log(`
|
|
514
|
-
Thank you for trying Orchid ORM!
|
|
515
|
-
|
|
516
|
-
To finish setup, install dependencies:
|
|
517
|
-
|
|
518
|
-
> npm i
|
|
519
|
-
|
|
520
|
-
Enter the correct database credentials to the .env file,
|
|
521
|
-
then create the database:
|
|
522
|
-
|
|
523
|
-
> npm run db create
|
|
524
|
-
|
|
525
|
-
And run the migrations:
|
|
526
|
-
|
|
527
|
-
> npm run db migrate
|
|
528
|
-
`);
|
|
529
|
-
};
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { appCodeUpdater } from './appCodeUpdater';
|
|
2
|
-
import { asMock, ast } from './testUtils';
|
|
3
|
-
import { updateMainFile } from './updateMainFile';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { updateTableFile } from './updateTableFile/updateTableFile';
|
|
6
|
-
import { createBaseTableFile } from './createBaseTableFile';
|
|
7
|
-
|
|
8
|
-
jest.mock('./updateMainFile', () => ({
|
|
9
|
-
updateMainFile: jest.fn(),
|
|
10
|
-
}));
|
|
11
|
-
|
|
12
|
-
jest.mock('./updateTableFile/updateTableFile', () => ({
|
|
13
|
-
updateTableFile: jest.fn(),
|
|
14
|
-
}));
|
|
15
|
-
|
|
16
|
-
jest.mock('./createBaseTableFile', () => ({
|
|
17
|
-
createBaseTableFile: jest.fn(() => Promise.resolve()),
|
|
18
|
-
}));
|
|
19
|
-
|
|
20
|
-
describe('appCodeUpdater', () => {
|
|
21
|
-
beforeEach(jest.clearAllMocks);
|
|
22
|
-
|
|
23
|
-
const params = {
|
|
24
|
-
tablePath: (table: string) => `tables/${table}.ts`,
|
|
25
|
-
baseTablePath: 'baseTable.ts',
|
|
26
|
-
baseTableName: 'BaseTable',
|
|
27
|
-
mainFilePath: 'db.ts',
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const fn = appCodeUpdater(params);
|
|
31
|
-
|
|
32
|
-
it('should call table and file updaters with proper arguments', async () => {
|
|
33
|
-
await fn({
|
|
34
|
-
ast: ast.addTable,
|
|
35
|
-
options: {},
|
|
36
|
-
basePath: __dirname,
|
|
37
|
-
cache: {},
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
const mainFilePath = path.resolve(__dirname, params.mainFilePath);
|
|
41
|
-
const tablePath = path.resolve(__dirname, params.tablePath('table'));
|
|
42
|
-
|
|
43
|
-
const main = asMock(updateMainFile).mock.calls[0];
|
|
44
|
-
expect(main[0]).toBe(mainFilePath);
|
|
45
|
-
expect(main[1]('table')).toBe(tablePath);
|
|
46
|
-
expect(main[2]).toBe(ast.addTable);
|
|
47
|
-
|
|
48
|
-
const [table] = asMock(updateTableFile).mock.calls[0];
|
|
49
|
-
expect(table.tablePath('table')).toBe(tablePath);
|
|
50
|
-
expect(table.baseTablePath).toBe(
|
|
51
|
-
path.resolve(__dirname, params.baseTablePath),
|
|
52
|
-
);
|
|
53
|
-
expect(table.baseTableName).toBe(params.baseTableName);
|
|
54
|
-
expect(table.mainFilePath).toBe(mainFilePath);
|
|
55
|
-
|
|
56
|
-
const [base] = asMock(createBaseTableFile).mock.calls[0];
|
|
57
|
-
expect(base.baseTablePath).toBe(
|
|
58
|
-
path.resolve(__dirname, params.baseTablePath),
|
|
59
|
-
);
|
|
60
|
-
expect(base.baseTableName).toBe(params.baseTableName);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should call createBaseTable only on first call', async () => {
|
|
64
|
-
const cache = {};
|
|
65
|
-
expect(createBaseTableFile).not.toBeCalled();
|
|
66
|
-
|
|
67
|
-
await fn({ ast: ast.addTable, options: {}, basePath: __dirname, cache });
|
|
68
|
-
|
|
69
|
-
expect(createBaseTableFile).toBeCalledTimes(1);
|
|
70
|
-
|
|
71
|
-
await fn({ ast: ast.addTable, options: {}, basePath: __dirname, cache });
|
|
72
|
-
|
|
73
|
-
expect(createBaseTableFile).toBeCalledTimes(1);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { AppCodeUpdater } from 'rake-db';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import { updateMainFile } from './updateMainFile';
|
|
4
|
-
import { updateTableFile } from './updateTableFile/updateTableFile';
|
|
5
|
-
import { createBaseTableFile } from './createBaseTableFile';
|
|
6
|
-
import { SetOptional } from 'pqb';
|
|
7
|
-
|
|
8
|
-
export class AppCodeUpdaterError extends Error {}
|
|
9
|
-
|
|
10
|
-
export type AppCodeUpdaterConfig = {
|
|
11
|
-
tablePath(tableName: string): string;
|
|
12
|
-
baseTablePath: string;
|
|
13
|
-
baseTableName: string;
|
|
14
|
-
mainFilePath: string;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export const appCodeUpdater = ({
|
|
18
|
-
tablePath,
|
|
19
|
-
baseTablePath,
|
|
20
|
-
baseTableName,
|
|
21
|
-
mainFilePath,
|
|
22
|
-
}: SetOptional<AppCodeUpdaterConfig, 'baseTableName'>): AppCodeUpdater => {
|
|
23
|
-
return async ({ ast, options, basePath, cache: cacheObject }) => {
|
|
24
|
-
const params: AppCodeUpdaterConfig = {
|
|
25
|
-
tablePath(name: string) {
|
|
26
|
-
const file = tablePath(name);
|
|
27
|
-
return resolvePath(basePath, file);
|
|
28
|
-
},
|
|
29
|
-
baseTablePath: resolvePath(basePath, baseTablePath),
|
|
30
|
-
baseTableName: baseTableName || 'BaseTable',
|
|
31
|
-
mainFilePath: resolvePath(basePath, mainFilePath),
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const promises: Promise<void>[] = [
|
|
35
|
-
updateMainFile(params.mainFilePath, params.tablePath, ast, options),
|
|
36
|
-
updateTableFile({ ...params, ast }),
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
const cache = cacheObject as { createdBaseTable?: true };
|
|
40
|
-
if (!cache.createdBaseTable) {
|
|
41
|
-
promises.push(
|
|
42
|
-
createBaseTableFile(params).then(() => {
|
|
43
|
-
cache.createdBaseTable = true;
|
|
44
|
-
}),
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
await Promise.all(promises);
|
|
49
|
-
};
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const resolvePath = (basePath: string, filePath: string) =>
|
|
53
|
-
path.isAbsolute(filePath) ? filePath : path.resolve(basePath, filePath);
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { createBaseTableFile } from './createBaseTableFile';
|
|
3
|
-
import fs from 'fs/promises';
|
|
4
|
-
import { asMock } from './testUtils';
|
|
5
|
-
|
|
6
|
-
jest.mock('fs/promises', () => ({
|
|
7
|
-
mkdir: jest.fn(),
|
|
8
|
-
writeFile: jest.fn(),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
const params = {
|
|
12
|
-
baseTablePath: path.resolve('baseTable.ts'),
|
|
13
|
-
baseTableName: 'CustomName',
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
describe('createBaseTableFile', () => {
|
|
17
|
-
it('should call mkdir with recursive option', async () => {
|
|
18
|
-
asMock(fs.writeFile).mockResolvedValue(null);
|
|
19
|
-
|
|
20
|
-
await createBaseTableFile(params);
|
|
21
|
-
|
|
22
|
-
expect(fs.mkdir).toBeCalledWith(path.dirname(params.baseTablePath), {
|
|
23
|
-
recursive: true,
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should write file with wx flag to not overwrite', async () => {
|
|
28
|
-
asMock(fs.writeFile).mockRejectedValueOnce(
|
|
29
|
-
Object.assign(new Error(), { code: 'EEXIST' }),
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
await createBaseTableFile(params);
|
|
33
|
-
|
|
34
|
-
expect(asMock(fs.writeFile)).toBeCalledWith(
|
|
35
|
-
params.baseTablePath,
|
|
36
|
-
`import { createBaseTable } from 'orchid-orm';
|
|
37
|
-
|
|
38
|
-
export const ${params.baseTableName} = createBaseTable();
|
|
39
|
-
`,
|
|
40
|
-
{
|
|
41
|
-
flag: 'wx',
|
|
42
|
-
},
|
|
43
|
-
);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should throw if error is not EEXIST', async () => {
|
|
47
|
-
asMock(fs.writeFile).mockRejectedValueOnce(
|
|
48
|
-
Object.assign(new Error('custom'), { code: 'other' }),
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
await expect(() => createBaseTableFile(params)).rejects.toThrow('custom');
|
|
52
|
-
});
|
|
53
|
-
});
|