@makerkit/cli 1.2.1

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.js ADDED
@@ -0,0 +1,734 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/commands/blog/generate/generate-blog.command.ts
4
+ import { join as join2 } from "path";
5
+
6
+ // src/plugins-model.ts
7
+ import invariant from "tiny-invariant";
8
+ var PluginsModel = {
9
+ CookieBanner: {
10
+ name: `Cookie Banner`,
11
+ id: `cookie-banner`,
12
+ branch: `cookie-banner`,
13
+ description: `Add a cookie banner to your site.`
14
+ }
15
+ };
16
+ function getPluginById(id) {
17
+ return Object.values(PluginsModel).find((plugin) => plugin.id === id);
18
+ }
19
+ function validatePlugin(pluginId) {
20
+ const plugin = getPluginById(pluginId);
21
+ invariant(plugin, `Plugin ${pluginId} not found`);
22
+ return plugin;
23
+ }
24
+
25
+ // src/kits-model.ts
26
+ import invariant2 from "tiny-invariant";
27
+ var KitsModel = {
28
+ NextJsFirebase: {
29
+ name: `Next.js Firebase`,
30
+ id: `next-firebase`,
31
+ repository: `git@github.com:makerkit/next-firebase-saas-kit`,
32
+ blogPath: `_posts`,
33
+ localePath: `public/locales`,
34
+ plugins: [PluginsModel.CookieBanner.id]
35
+ },
36
+ NextJsSupabase: {
37
+ name: `Next.js Supabase`,
38
+ id: `next-supabase`,
39
+ localePath: `public/locales`,
40
+ blogPath: `src/content/posts`,
41
+ repository: `git@github.com:makerkit/next-supabase-saas-kit`,
42
+ plugins: []
43
+ },
44
+ NextJsSupabaseLite: {
45
+ name: `Next.js Supabase Lite`,
46
+ id: `next-supabase-lite`,
47
+ localePath: `public/locales`,
48
+ blogPath: `src/content/posts`,
49
+ repository: `git@github.com:makerkit/next-supabase-saas-kit-lite`,
50
+ plugins: []
51
+ },
52
+ RemixFirebase: {
53
+ name: `Remix Firebase`,
54
+ id: `remix-firebase`,
55
+ localePath: `public/locales`,
56
+ blogPath: "",
57
+ repository: `git@github.com:makerkit/remix-firebase-saas-kit`,
58
+ plugins: []
59
+ },
60
+ RemixSupabase: {
61
+ name: `Remix Supabase`,
62
+ id: `remix-supabase`,
63
+ localePath: `public/locales`,
64
+ blogPath: "",
65
+ repository: `git@github.com:makerkit/remix-supabase-saas-kit`,
66
+ plugins: []
67
+ }
68
+ };
69
+ function getKitById(id) {
70
+ return Object.values(KitsModel).find((kit) => kit.id === id);
71
+ }
72
+ function validateKit(kitId) {
73
+ const kit = getKitById(kitId);
74
+ invariant2(kit, `Kit ${kitId} not found`);
75
+ return kit;
76
+ }
77
+
78
+ // src/utils/openai-client.ts
79
+ import OpenAI from "openai";
80
+ function getOpenAIClient() {
81
+ const apiKey = process.env.OPENAI_API_KEY;
82
+ if (!apiKey) {
83
+ throw new Error(
84
+ "OPENAI_API_KEY env variable is not set. Please set it in your .env.local file of your Makerkit workspace."
85
+ );
86
+ }
87
+ return new OpenAI({ apiKey });
88
+ }
89
+ var openai_client_default = getOpenAIClient;
90
+
91
+ // src/utils/workspace.ts
92
+ import { join } from "path";
93
+ import chalk from "chalk";
94
+ import fs from "fs-extra";
95
+ var Workspace = class _Workspace {
96
+ static async getKitMeta() {
97
+ const packageJson = await getPackageJson();
98
+ const kit = await detectKitVersion();
99
+ return {
100
+ ...kit,
101
+ version: packageJson.version
102
+ };
103
+ }
104
+ static async logWorkspaceInfo() {
105
+ const meta = await _Workspace.getKitMeta();
106
+ console.log(
107
+ `Makerkit version: ${chalk.cyan(meta.name)} - ${chalk.cyan(
108
+ meta.version
109
+ )}.
110
+ `
111
+ );
112
+ }
113
+ };
114
+ async function detectKitVersion() {
115
+ const packageJson = await getPackageJson();
116
+ if (!packageJson) {
117
+ throw new Error(
118
+ "No package.json found. Please run this command in a Makerkit workspace."
119
+ );
120
+ }
121
+ const deps = Object.keys(packageJson.dependencies ?? []);
122
+ if (deps.includes("next") && deps.includes("firebase")) {
123
+ return KitsModel.NextJsFirebase;
124
+ }
125
+ if (deps.includes("next") && deps.includes("supabase")) {
126
+ return KitsModel.NextJsSupabase;
127
+ }
128
+ if (deps.includes("@remix-run/react") && deps.includes("firebase")) {
129
+ return KitsModel.NextJsSupabaseLite;
130
+ }
131
+ if (deps.includes("@remix-run/react") && deps.includes("supabase")) {
132
+ return KitsModel.RemixSupabase;
133
+ }
134
+ throw new Error(
135
+ "Could not detect Makerkit workspace. Please run this command in a Makerkit workspace."
136
+ );
137
+ }
138
+ async function getPackageJson() {
139
+ return fs.readJSON(join(process.cwd(), "package.json")).catch(() => void 0);
140
+ }
141
+
142
+ // src/commands/blog/generate/generate-blog.command.ts
143
+ import chalk2 from "chalk";
144
+ import fs2 from "fs-extra";
145
+ import ora from "ora";
146
+ import prompts from "prompts";
147
+ import { z } from "zod";
148
+ var schema = z.object({
149
+ title: z.string().min(1).max(70),
150
+ category: z.string().min(1).max(70),
151
+ wordCount: z.number().min(250).max(2500),
152
+ prompt: z.string().optional().transform((value) => value || "")
153
+ });
154
+ function generateBlogCommand(parentCommand) {
155
+ return parentCommand.command("generate").description("Generate your blog posts with AI").action(async () => {
156
+ const { title, prompt, category, wordCount } = schema.parse(
157
+ await getPrompts()
158
+ );
159
+ const content = await generateBlogPost({ title, prompt, wordCount });
160
+ const mdx = await getMdxPostTemplate({ title, category, content });
161
+ return writeMdxPostTemplate(mdx, title);
162
+ });
163
+ }
164
+ function getPrompts() {
165
+ return prompts([
166
+ {
167
+ type: "text",
168
+ name: "title",
169
+ message: `Enter your ${chalk2.cyan("title")}.`
170
+ },
171
+ {
172
+ type: "text",
173
+ name: "category",
174
+ message: `Enter your ${chalk2.cyan(
175
+ "category"
176
+ )}. If it doesn't exist yet, you will need to add it manually to your blog.`
177
+ },
178
+ {
179
+ type: "number",
180
+ name: "wordCount",
181
+ message: `Enter your target ${chalk2.cyan(
182
+ "word count"
183
+ )} (from 250 words, up to 2500).`
184
+ },
185
+ {
186
+ type: "text",
187
+ name: "prompt",
188
+ message: `Would you like to add your own instructions to the AI? (optional)`
189
+ }
190
+ ]);
191
+ }
192
+ async function generateBlogPost(params) {
193
+ const client = openai_client_default();
194
+ const prompt = getPostPrompt(params);
195
+ const spinner = ora(`Generating blog post...`).start();
196
+ try {
197
+ const response = await client.chat.completions.create({
198
+ model: `gpt-3.5-turbo`,
199
+ messages: [
200
+ {
201
+ role: "user",
202
+ content: prompt
203
+ }
204
+ ],
205
+ max_tokens: params.wordCount,
206
+ temperature: 0.7
207
+ });
208
+ const content = response.choices[0].message.content ?? "";
209
+ spinner.succeed(`Blog post generated successfully`);
210
+ console.log(`Used ${response.usage?.total_tokens} tokens`);
211
+ return content;
212
+ } catch (e) {
213
+ console.error(e);
214
+ spinner.fail(`Failed to generate blog post`);
215
+ process.exit(1);
216
+ }
217
+ }
218
+ async function getMdxPostTemplate(params) {
219
+ const kit = await Workspace.getKitMeta();
220
+ const isFirebaseKit = kit.id === KitsModel.NextJsFirebase.id;
221
+ const categoryProp = isFirebaseKit ? "collection" : "category";
222
+ const descriptionProp = isFirebaseKit ? "excerpt" : "description";
223
+ const imageProp = isFirebaseKit ? "coverImage" : "image";
224
+ return `
225
+ ---
226
+ title: "${params.title}"
227
+ ${categoryProp}: "${params.category}"
228
+ date: "${(/* @__PURE__ */ new Date()).toISOString()}"
229
+ ${descriptionProp}: ""
230
+ live: false
231
+ ${imageProp}: ""
232
+ ---
233
+
234
+ ${params.content}
235
+ `.trim();
236
+ }
237
+ async function writeMdxPostTemplate(template, title) {
238
+ const kit = await Workspace.getKitMeta();
239
+ const spinner = ora(`Storing blog post...`).start();
240
+ try {
241
+ let blogPath = kit.blogPath;
242
+ if (!blogPath) {
243
+ blogPath = await prompts({
244
+ type: "text",
245
+ name: "blogPath",
246
+ message: `This kit does not support blog posts yet. You can still generate a blog post, but you'll need to manually add it to your blog. Please enter your blog path (e.g. src/content/posts). The path needs to exist.`
247
+ }).then((response) => response.blogPath);
248
+ }
249
+ const slug = slugify(title);
250
+ const path = join2(process.cwd(), blogPath, `${slug}.mdx`);
251
+ await fs2.writeFile(path, template);
252
+ spinner.succeed(`Blog post stored successfully`);
253
+ } catch (e) {
254
+ console.error(e);
255
+ spinner.fail(`Failed to store blog post`);
256
+ }
257
+ }
258
+ function getPostPrompt(params) {
259
+ return `
260
+ Generate a professional blog post with title "${params.title}".
261
+ Rules:
262
+
263
+ - The post must be below ${params.wordCount} words
264
+ - Output text using valid Markdown
265
+ - Write professional text while balancing simplicity and complexity in your language and sentence structures.
266
+ - Be an extremely smart, interesting, funny, witty, and confident writer. Readers admire you!
267
+ - Use several paragraphs and an accurate level 2 and 3 headings before each paragraph to maximize readability.
268
+ - Do include level 4 headings (h4) to break complex paragraphs
269
+ - Ensure that headings do not include decimals or numbers with periods. Ensure they're formatted using Markdown.
270
+ - Repeat the main keywords and phrases often for SEO
271
+
272
+ ${params.prompt ?? ""}
273
+ `.trim();
274
+ }
275
+ function slugify(text) {
276
+ return (text ?? "").toString().normalize("NFKD").toLowerCase().trim().replace(/\_/g, "-").replace(/\s+/g, "-").replace(/\-\-+/g, "--").replace(/\_$/g, "");
277
+ }
278
+
279
+ // src/commands/blog/blog.command.ts
280
+ import { Command } from "commander";
281
+ var blogCommand = new Command().name("blog").description("Manage and generate your blog posts").hook("preAction", () => {
282
+ return Workspace.logWorkspaceInfo();
283
+ });
284
+ generateBlogCommand(blogCommand);
285
+
286
+ // src/commands/i18n/i18n-service.ts
287
+ import { join as join3 } from "path";
288
+ import chalk3 from "chalk";
289
+ import fs3 from "fs-extra";
290
+ var I18nService = class {
291
+ /**
292
+ * @name verify
293
+ * @description Verifies if the locale files are in sync
294
+ */
295
+ static async verify(base = "en") {
296
+ const kit = await Workspace.getKitMeta();
297
+ const allLocales = fs3.readdirSync(kit.localePath).filter((locale) => {
298
+ return locale !== base;
299
+ });
300
+ const baseLocaleFolderPath = `${kit.localePath}/${base}`;
301
+ const localeFiles = fs3.readdirSync(baseLocaleFolderPath).filter((file) => {
302
+ return file.endsWith(".json");
303
+ });
304
+ for (const localeFile of localeFiles) {
305
+ for (const locale of allLocales) {
306
+ const baseLocaleFilePath = `${baseLocaleFolderPath}/${localeFile}`;
307
+ const targetLocaleFilePath = `${kit.localePath}/${locale}/${localeFile}`;
308
+ const baseLocaleFile = fs3.readJSONSync(baseLocaleFilePath);
309
+ const file = fs3.readJSONSync(targetLocaleFilePath);
310
+ const missingKeys = collectMissingKeys([baseLocaleFile, file]);
311
+ if (!missingKeys.length) {
312
+ console.log(
313
+ chalk3.green(`Locale ${locale}/${localeFile} is in sync!`)
314
+ );
315
+ continue;
316
+ }
317
+ console.log(
318
+ chalk3.yellow(
319
+ `Locale ${locale}/${localeFile} is missing the following keys: ${missingKeys.join(
320
+ ", "
321
+ )}`
322
+ )
323
+ );
324
+ }
325
+ }
326
+ }
327
+ /**
328
+ * @name translate
329
+ * @description Translates the locale files from source to target
330
+ * @param source
331
+ * @param target
332
+ */
333
+ static async translate(source, target) {
334
+ const kit = await Workspace.getKitMeta();
335
+ const client = openai_client_default();
336
+ const targetJsonPath = `${kit.localePath}/${target}`;
337
+ const sourceLocalePath = `${kit.localePath}/${source}`;
338
+ const sourceLocale = await fs3.exists(sourceLocalePath);
339
+ if (!sourceLocale) {
340
+ throw new Error(`Source locale at ${sourceLocalePath} not found`);
341
+ }
342
+ const files = fs3.readdirSync(sourceLocalePath).filter((file) => {
343
+ return file.endsWith(".json");
344
+ });
345
+ console.log(`Found the following files: ${files.join(", ")}`);
346
+ for (const file of files) {
347
+ const data = {};
348
+ const localeFile = fs3.readJSONSync(join3(sourceLocalePath, file));
349
+ console.log(
350
+ chalk3.cyan(`Translating "${file}". This can take a while...`)
351
+ );
352
+ for (const key in localeFile) {
353
+ data[key] = await translateKey(source, target, localeFile[key], client);
354
+ }
355
+ console.log(chalk3.green(`File "${file}" successfully translated!`));
356
+ console.log(chalk3.cyan(`Writing file "${file}" to ${targetJsonPath}`));
357
+ await fs3.exists(targetJsonPath) || await fs3.mkdir(targetJsonPath);
358
+ await fs3.writeJSON(join3(targetJsonPath, file), data, {});
359
+ console.log(chalk3.green(`File "${file}" successfully written!`));
360
+ }
361
+ }
362
+ };
363
+ async function translateKey(source, target, key, client) {
364
+ if (typeof key === "string") {
365
+ return translateString(source, target, key, client);
366
+ }
367
+ const data = {};
368
+ for (const k in key) {
369
+ data[k] = await translateKey(source, target, key[k], client);
370
+ }
371
+ return data;
372
+ }
373
+ async function translateString(source, target, key, client) {
374
+ if (!key.trim() || key.length <= 1) {
375
+ return "";
376
+ }
377
+ const response = await client.chat.completions.create({
378
+ model: "gpt-3.5-turbo",
379
+ max_tokens: target.split(" ").length + 50,
380
+ temperature: 0.3,
381
+ messages: [
382
+ {
383
+ role: "user",
384
+ content: `Translate the text from locale ${source} to ${target}. Text: ${key}. Translation:`
385
+ }
386
+ ]
387
+ });
388
+ return response.choices[0].message.content ?? "";
389
+ }
390
+ function collectMissingKeys(objects) {
391
+ const allKeys = [];
392
+ objects.forEach((obj) => {
393
+ Object.keys(obj).forEach((key) => {
394
+ if (!allKeys.includes(key)) {
395
+ allKeys.push(key);
396
+ }
397
+ });
398
+ });
399
+ const missingKeys = [];
400
+ objects.forEach((obj) => {
401
+ allKeys.forEach((key) => {
402
+ if (!Object.keys(obj).includes(key)) {
403
+ missingKeys.push(key);
404
+ }
405
+ });
406
+ });
407
+ return missingKeys;
408
+ }
409
+
410
+ // src/commands/i18n/translate/translate.command.ts
411
+ import chalk4 from "chalk";
412
+ import prompts2 from "prompts";
413
+ function createTranslateI18nCommand(parentCommand) {
414
+ return parentCommand.command("translate").argument("[source-locale]", "Source Locale").argument("[target-locale]", "Target Locale").description("Translate i18n files from source locale to target locale").action(async (sourceLocale, targetLocale) => {
415
+ const locales = await promptLocales(sourceLocale, targetLocale);
416
+ await I18nService.translate(locales.sourceLocale, locales.targetLocale);
417
+ });
418
+ }
419
+ async function promptLocales(maybeSource, maybeTarget) {
420
+ const hint = "e.g. en, fr, es, etc.";
421
+ const sourceLocale = maybeSource || (await prompts2([
422
+ {
423
+ type: "text",
424
+ name: "locale",
425
+ message: `Enter your ${chalk4.cyan("Source Locale")}. ${hint}`,
426
+ hint
427
+ }
428
+ ])).locale;
429
+ const targetLocale = maybeTarget || (await prompts2([
430
+ {
431
+ type: "text",
432
+ name: "locale",
433
+ message: `Enter your ${chalk4.cyan("Target Locale")}. ${hint}`,
434
+ hint
435
+ }
436
+ ])).locale;
437
+ return {
438
+ sourceLocale,
439
+ targetLocale
440
+ };
441
+ }
442
+
443
+ // src/commands/i18n/verify/verify.command.ts
444
+ function createVerifyI18nCommand(parentCommand) {
445
+ return parentCommand.command("verify").argument("[base-locale]", "Base Locale").description(
446
+ "Verify i18n files have no missing keys compared to base locale"
447
+ ).action(async (baseLocale = "en") => {
448
+ await I18nService.verify(baseLocale);
449
+ });
450
+ }
451
+
452
+ // src/commands/i18n/i18n.command.ts
453
+ import { Command as Command2 } from "commander";
454
+ var i18nCommand = new Command2().name("i18n").description("Manage and translate your i18n files").hook("preAction", () => {
455
+ return Workspace.logWorkspaceInfo();
456
+ });
457
+ createTranslateI18nCommand(i18nCommand);
458
+ createVerifyI18nCommand(i18nCommand);
459
+
460
+ // src/commands/license/activate/activate-license.command.ts
461
+ import chalk5 from "chalk";
462
+ import ora2 from "ora";
463
+ import prompts3 from "prompts";
464
+ import { z as z2 } from "zod";
465
+ var schema2 = z2.object({
466
+ licenseKey: z2.string().min(1).max(32),
467
+ githubUsername: z2.string().min(1).max(64)
468
+ });
469
+ function createActivateLicenseCommand(parentCommand) {
470
+ return parentCommand.command("activate").description("Activate your license key for MakerKit").action(async () => {
471
+ const params = await prompts3([
472
+ {
473
+ type: "text",
474
+ name: "licenseKey",
475
+ message: `Enter your ${chalk5.cyan("License Key")}.`,
476
+ validate: (value) => {
477
+ if (value.length < 1) {
478
+ return `Please enter a valid license key`;
479
+ }
480
+ return true;
481
+ }
482
+ },
483
+ {
484
+ type: "text",
485
+ name: "githubUsername",
486
+ message: `Enter your ${chalk5.cyan("Github username")}.`,
487
+ validate: (value) => {
488
+ if (value.length < 1) {
489
+ return `Please enter a valid username`;
490
+ }
491
+ return true;
492
+ }
493
+ }
494
+ ]);
495
+ const spinner = ora2(`Activating license...`).start();
496
+ const onError = () => {
497
+ spinner.fail(`Failed to activate license`);
498
+ process.exit(1);
499
+ };
500
+ try {
501
+ const response = await activateLicense(schema2.parse(params));
502
+ if (!response.ok) {
503
+ return onError();
504
+ }
505
+ spinner.succeed(`License activated successfully`);
506
+ } catch (e) {
507
+ onError();
508
+ }
509
+ });
510
+ }
511
+ async function activateLicense(params) {
512
+ return fetch(`https://makerkit.dev/api/ls/license/activate`, {
513
+ method: "POST",
514
+ body: JSON.stringify(params)
515
+ });
516
+ }
517
+
518
+ // src/commands/license/license.command.ts
519
+ import { Command as Command3 } from "commander";
520
+ var licenseCommand = new Command3().name("license").description("Manage Licenses");
521
+ createActivateLicenseCommand(licenseCommand);
522
+
523
+ // src/commands/new/new.command.ts
524
+ import { join as join4 } from "path";
525
+ import chalk6 from "chalk";
526
+ import { Command as Command4 } from "commander";
527
+ import { execa } from "execa";
528
+ import ora3 from "ora";
529
+ import prompts4 from "prompts";
530
+ var newCommand = new Command4().name("new").description("Initialize a new Makerkit project").action(async () => {
531
+ const choices = Object.values(KitsModel).map((kit2) => {
532
+ return {
533
+ title: kit2.name,
534
+ value: kit2.id
535
+ };
536
+ });
537
+ const { kit, name: projectName } = await prompts4([
538
+ {
539
+ type: "select",
540
+ name: "kit",
541
+ message: `Select the ${chalk6.cyan(`SaaS Kit`)} you want to clone`,
542
+ choices
543
+ },
544
+ {
545
+ type: "text",
546
+ name: "name",
547
+ message: `Enter your ${chalk6.cyan("Project Name")}.`
548
+ }
549
+ ]);
550
+ const { repository, name: kitName } = validateKit(kit);
551
+ const { confirm } = await prompts4([
552
+ {
553
+ type: "confirm",
554
+ name: "confirm",
555
+ message: `Are you sure you want to clone ${chalk6.cyan(
556
+ kitName
557
+ )} to ${chalk6.cyan(projectName)}?`
558
+ }
559
+ ]);
560
+ if (!confirm) {
561
+ console.log("Aborting...");
562
+ process.exit(0);
563
+ }
564
+ await pullFromGithub(repository, kitName, projectName);
565
+ await installDependencies(projectName);
566
+ });
567
+ async function pullFromGithub(repository, kit, projectName) {
568
+ const spinner = ora3(`Cloning ${kit}...`).start();
569
+ try {
570
+ await execa("git", ["clone", repository, projectName]);
571
+ spinner.succeed(`Cloned ${kit} to ${projectName}...`);
572
+ } catch (e) {
573
+ console.error(e);
574
+ spinner.fail(`Failed to clone ${kit}`);
575
+ return Promise.reject(`Failed to clone ${kit}`);
576
+ }
577
+ }
578
+ async function installDependencies(projectName) {
579
+ const cwd = join4(process.cwd(), projectName);
580
+ const spinner = ora3(`Installing dependencies. Please wait...`).start();
581
+ try {
582
+ await execa("npm", ["install"], { cwd });
583
+ spinner.succeed(`Dependencies successfully installed!`);
584
+ console.log(
585
+ `\u{1F389} You can now get started. Open the project in your IDE and read the README.md file for more information.`
586
+ );
587
+ } catch (e) {
588
+ console.error(e);
589
+ spinner.fail(`Failed to install dependencies`);
590
+ return Promise.reject(`Failed to install dependencies`);
591
+ }
592
+ }
593
+
594
+ // src/commands/plugins/plugins-service.ts
595
+ import chalk7 from "chalk";
596
+ import { execaCommand } from "execa";
597
+ import ora4 from "ora";
598
+ import prompts5 from "prompts";
599
+ var PluginsService = class {
600
+ static async install(pluginId) {
601
+ const action = "add" /* Add */;
602
+ const plugin = pluginId || await getPluginFromPrompts(action);
603
+ await initPluginCommand(plugin, action);
604
+ }
605
+ static async pull(pluginId) {
606
+ const action = "pull" /* Pull */;
607
+ const plugin = pluginId || await getPluginFromPrompts(action);
608
+ await initPluginCommand(plugin, action);
609
+ }
610
+ };
611
+ async function initPluginCommand(pluginId, action) {
612
+ const { id: kitId } = await Workspace.getKitMeta();
613
+ const plugin = validatePlugin(pluginId);
614
+ const kit = validateKit(kitId);
615
+ if (!kit.plugins.includes(plugin.id)) {
616
+ throw new Error(
617
+ `Plugin ${plugin.name} is not compatible with kit ${kit.name}`
618
+ );
619
+ }
620
+ const repository = buildPluginRepositoryName(kit.repository);
621
+ const verb = action === "add" /* Add */ ? "Installing" : "Updating";
622
+ console.log(
623
+ `${verb} ${chalk7.cyan(plugin.name)} to kit ${chalk7.cyan(
624
+ kit.name
625
+ )} using repo: ${repository} ...`
626
+ );
627
+ return executePluginCommand({
628
+ action,
629
+ repository,
630
+ branch: plugin.branch,
631
+ folder: `plugins/${plugin.branch}`
632
+ });
633
+ }
634
+ async function getPluginFromPrompts(action) {
635
+ const choices = Object.values(PluginsModel).map((plugin2) => {
636
+ return {
637
+ title: plugin2.name,
638
+ value: plugin2.id
639
+ };
640
+ });
641
+ const verb = action === "add" /* Add */ ? "install" : "update";
642
+ const { plugin } = await prompts5([
643
+ {
644
+ type: "select",
645
+ name: "plugin",
646
+ message: `Which ${chalk7.cyan("Plugin")} would you like to ${verb}?`,
647
+ choices
648
+ }
649
+ ]);
650
+ return plugin;
651
+ }
652
+ function buildPluginRepositoryName(repository) {
653
+ return repository + `-plugins.git`;
654
+ }
655
+ async function executePluginCommand({
656
+ action,
657
+ repository,
658
+ branch,
659
+ folder
660
+ }) {
661
+ const verb = action === "add" /* Add */ ? "Installing" : "Updating";
662
+ const spinner = ora4(`${verb} plugin...`).start();
663
+ const commandString = `git subtree ${action} --prefix ${folder} ${repository} ${branch} --squash`;
664
+ try {
665
+ await execaCommand(commandString);
666
+ const verb2 = action === "add" /* Add */ ? "installed" : "updated";
667
+ spinner.succeed(`Plugin ${verb2} at ${folder}`);
668
+ } catch (e) {
669
+ console.error(e);
670
+ const verb2 = action === "add" /* Add */ ? "installation" : "update";
671
+ spinner.fail(`Plugin ${verb2} failed`);
672
+ }
673
+ }
674
+
675
+ // src/commands/plugins/install/install-plugin.command.ts
676
+ function createInstallPluginCommand(parentCommand) {
677
+ return parentCommand.command("install").argument("[plugin-id]", "Plugin id").description("Install plugin").action(async (maybePluginId) => {
678
+ await PluginsService.install(maybePluginId);
679
+ });
680
+ }
681
+
682
+ // src/commands/plugins/list/list-plugins.command.ts
683
+ import chalk8 from "chalk";
684
+ function createListPluginsCommand(parentCommand) {
685
+ return parentCommand.command("list").description("List available plugins.").action(() => {
686
+ const kits = Object.values(KitsModel);
687
+ for (const kit of kits) {
688
+ console.log(`${chalk8.cyan(kit.name)}`);
689
+ if (!kit.plugins.length) {
690
+ console.log(chalk8.yellow(`- No plugins available`) + "\n");
691
+ continue;
692
+ }
693
+ for (const plugin of kit.plugins) {
694
+ const { name } = validatePlugin(plugin);
695
+ console.log(`- ${chalk8.green(name)}`);
696
+ }
697
+ console.log("");
698
+ }
699
+ });
700
+ }
701
+
702
+ // src/commands/plugins/update/update-plugin.command.ts
703
+ function createUpdatePluginCommand(parentCommand) {
704
+ return parentCommand.command("update").argument("[plugin-id]", "Plugin id").description("Update plugin to the latest version").action(async (maybePluginId) => {
705
+ await PluginsService.pull(maybePluginId);
706
+ });
707
+ }
708
+
709
+ // src/commands/plugins/plugins.command.ts
710
+ import { Command as Command5 } from "commander";
711
+ var pluginsCommand = new Command5().name("plugins").description("List and install plugins.").hook("preAction", () => {
712
+ return Workspace.logWorkspaceInfo();
713
+ });
714
+ createListPluginsCommand(pluginsCommand);
715
+ createInstallPluginCommand(pluginsCommand);
716
+ createUpdatePluginCommand(pluginsCommand);
717
+
718
+ // src/index.ts
719
+ import { Command as Command6 } from "commander";
720
+ import { config } from "dotenv";
721
+ config({
722
+ path: ".env.local"
723
+ });
724
+ process.on("SIGINT", () => process.exit(0));
725
+ process.on("SIGTERM", () => process.exit(0));
726
+ async function main() {
727
+ const program = new Command6().name("makerkit").description(
728
+ "Your SaaS Kit companion. Add plugins, manage migrations, and more."
729
+ ).version("-v, --version", "display the version number");
730
+ program.addCommand(newCommand).addCommand(pluginsCommand).addCommand(i18nCommand).addCommand(licenseCommand).addCommand(blogCommand);
731
+ program.parse();
732
+ }
733
+ void main();
734
+ //# sourceMappingURL=index.js.map