Skip to content

Commit ca27873

Browse files
committed
cli: add more drizzle-kit commands
1 parent b02d20a commit ca27873

File tree

1 file changed

+192
-58
lines changed

1 file changed

+192
-58
lines changed

packages/cli/src/commands/DrizzleCommands.ts

Lines changed: 192 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -132,71 +132,205 @@ export class DrizzleCommands {
132132
}),
133133
),
134134
handler: async ({ flags, args }) => {
135-
const rootDir = join(process.cwd(), flags.root);
136-
this.log.debug(`Using project root: ${rootDir}`);
135+
await this.runDrizzleKitCommand({
136+
flags,
137+
args,
138+
command: "generate",
139+
logMessage: (providerName, dialect) =>
140+
`Generate '${providerName}' migrations (${dialect}) ...`,
141+
});
142+
},
143+
});
137144

138-
const { alepha, entry } = await this.loadAlephaFromServerEntryFile(
139-
rootDir,
145+
/**
146+
* Push database schema changes directly to the database
147+
*
148+
* - Loads the Alepha instance from the specified entry file.
149+
* - Retrieves all repository descriptors to gather database models.
150+
* - Creates temporary entity definitions and Drizzle config.
151+
* - Invokes Drizzle Kit's push command to apply schema changes directly.
152+
*/
153+
push = $command({
154+
name: "db:push",
155+
description: "Push database schema changes directly to the database",
156+
summary: false,
157+
flags: this.flags,
158+
args: t.optional(
159+
t.text({
160+
description: "Path to the Alepha server entry file",
161+
}),
162+
),
163+
handler: async ({ flags, args }) => {
164+
await this.runDrizzleKitCommand({
165+
flags,
140166
args,
141-
);
167+
command: "push",
168+
logMessage: (providerName, dialect) =>
169+
`Push '${providerName}' schema (${dialect}) ...`,
170+
});
171+
},
172+
});
142173

143-
const inject = (name: string) =>
144-
// biome-ignore lint/complexity/useLiteralKeys: private key
145-
alepha["registry"]
146-
.values()
147-
.find((it: any) => it.instance.constructor.name === name)?.instance;
148-
149-
// ---------------------------------------------------------------------------------------------------------------
150-
const kit = inject("DrizzleKitProvider");
151-
const accepted = new Set<string>([]);
152-
for (const descriptor of alepha.descriptors("repository") as any[]) {
153-
const provider = descriptor.provider;
154-
const providerName = provider.name;
155-
const dialect = provider.dialect;
156-
157-
if (!accepted.has(providerName)) {
158-
accepted.add(providerName);
159-
} else {
160-
continue;
161-
}
174+
/**
175+
* Apply pending database migrations
176+
*
177+
* - Loads the Alepha instance from the specified entry file.
178+
* - Retrieves all repository descriptors to gather database models.
179+
* - Creates temporary entity definitions and Drizzle config.
180+
* - Invokes Drizzle Kit's migrate command to apply pending migrations.
181+
*/
182+
migrate = $command({
183+
name: "db:migrate",
184+
description: "Apply pending database migrations",
185+
summary: false,
186+
flags: this.flags,
187+
args: t.optional(
188+
t.text({
189+
description: "Path to the Alepha server entry file",
190+
}),
191+
),
192+
handler: async ({ flags, args }) => {
193+
await this.runDrizzleKitCommand({
194+
flags,
195+
args,
196+
command: "migrate",
197+
logMessage: (providerName, dialect) =>
198+
`Migrate '${providerName}' database (${dialect}) ...`,
199+
});
200+
},
201+
});
162202

163-
this.log.info("");
164-
this.log.info(`Generate '${providerName}' migrations (${dialect}) ...`);
165-
166-
const models = Object.keys(kit.getModels(provider));
167-
const entitiesJs = this.generateEntitiesJs(entry, providerName, models);
168-
169-
const entitiesJsPath = await this.runner.writeConfigFile(
170-
"entities.js",
171-
entitiesJs,
172-
rootDir,
173-
);
174-
175-
const drizzleConfigJs =
176-
"export default " +
177-
JSON.stringify(
178-
{
179-
schema: entitiesJsPath,
180-
out: `./migrations/${providerName}`,
181-
dialect: dialect,
182-
},
183-
null,
184-
2,
185-
);
186-
187-
const drizzleConfigJsPath = await this.runner.writeConfigFile(
188-
"drizzle.config.js",
189-
drizzleConfigJs,
190-
rootDir,
191-
);
192-
193-
await this.runner.exec(
194-
`drizzle-kit generate --config=${drizzleConfigJsPath}`,
195-
);
196-
}
203+
/**
204+
* Launch Drizzle Studio database browser
205+
*
206+
* - Loads the Alepha instance from the specified entry file.
207+
* - Retrieves all repository descriptors to gather database models.
208+
* - Creates temporary entity definitions and Drizzle config.
209+
* - Invokes Drizzle Kit's studio command to launch the web-based database browser.
210+
*/
211+
studio = $command({
212+
name: "db:studio",
213+
description: "Launch Drizzle Studio database browser",
214+
summary: false,
215+
flags: this.flags,
216+
args: t.optional(
217+
t.text({
218+
description: "Path to the Alepha server entry file",
219+
}),
220+
),
221+
handler: async ({ flags, args }) => {
222+
await this.runDrizzleKitCommand({
223+
flags,
224+
args,
225+
command: "studio",
226+
logMessage: (providerName, dialect) =>
227+
`Launch Studio for '${providerName}' (${dialect}) ...`,
228+
});
197229
},
198230
});
199231

232+
/**
233+
* Run a drizzle-kit command for all database providers
234+
*/
235+
protected async runDrizzleKitCommand(options: {
236+
flags: { root: string };
237+
args?: string;
238+
command: string;
239+
logMessage: (providerName: string, dialect: string) => string;
240+
}): Promise<void> {
241+
const rootDir = join(process.cwd(), options.flags.root);
242+
this.log.debug(`Using project root: ${rootDir}`);
243+
244+
const { alepha, entry } = await this.loadAlephaFromServerEntryFile(
245+
rootDir,
246+
options.args,
247+
);
248+
249+
const kit = this.getKitFromAlepha(alepha);
250+
const accepted = new Set<string>([]);
251+
252+
for (const descriptor of alepha.descriptors("repository") as any[]) {
253+
const provider = descriptor.provider;
254+
const providerName = provider.name;
255+
const dialect = provider.dialect;
256+
257+
if (accepted.has(providerName)) {
258+
continue;
259+
}
260+
accepted.add(providerName);
261+
262+
this.log.info("");
263+
this.log.info(options.logMessage(providerName, dialect));
264+
265+
const drizzleConfigJsPath = await this.prepareDrizzleConfig({
266+
kit,
267+
provider,
268+
providerName,
269+
dialect,
270+
entry,
271+
rootDir,
272+
});
273+
274+
await this.runner.exec(
275+
`drizzle-kit ${options.command} --config=${drizzleConfigJsPath}`,
276+
);
277+
}
278+
}
279+
280+
/**
281+
* Prepare Drizzle configuration files for a provider
282+
*/
283+
protected async prepareDrizzleConfig(options: {
284+
kit: any;
285+
provider: any;
286+
providerName: string;
287+
dialect: string;
288+
entry: string;
289+
rootDir: string;
290+
}): Promise<string> {
291+
const models = Object.keys(options.kit.getModels(options.provider));
292+
const entitiesJs = this.generateEntitiesJs(
293+
options.entry,
294+
options.providerName,
295+
models,
296+
);
297+
298+
const entitiesJsPath = await this.runner.writeConfigFile(
299+
"entities.js",
300+
entitiesJs,
301+
options.rootDir,
302+
);
303+
304+
const drizzleConfigJs =
305+
"export default " +
306+
JSON.stringify(
307+
{
308+
schema: entitiesJsPath,
309+
out: `./migrations/${options.providerName}`,
310+
dialect: options.dialect,
311+
},
312+
null,
313+
2,
314+
);
315+
316+
return await this.runner.writeConfigFile(
317+
"drizzle.config.js",
318+
drizzleConfigJs,
319+
options.rootDir,
320+
);
321+
}
322+
323+
/**
324+
* Get DrizzleKitProvider from Alepha instance
325+
*/
326+
protected getKitFromAlepha(alepha: Alepha): any {
327+
// biome-ignore lint/complexity/useLiteralKeys: private key
328+
return alepha["registry"]
329+
.values()
330+
.find((it: any) => it.instance.constructor.name === "DrizzleKitProvider")
331+
?.instance;
332+
}
333+
200334
public async loadAlephaFromServerEntryFile(
201335
rootDir?: string,
202336
explicitEntry?: string,

0 commit comments

Comments
 (0)