@makerkit/cli 2.0.7 → 2.0.9

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.mjs CHANGED
@@ -8,6 +8,7 @@ import { execa, execaCommand } from "execa";
8
8
  import fs from "fs-extra";
9
9
  import invariant from "tiny-invariant";
10
10
  import { homedir, tmpdir } from "os";
11
+ import { spawn } from "node:child_process";
11
12
  import { config } from "dotenv";
12
13
  //#region src/version.ts
13
14
  const CLI_VERSION = "2.0.3";
@@ -31,7 +32,10 @@ const VARIANT_REPO_MAP = {
31
32
  "next-supabase": "makerkit/next-supabase-saas-kit-turbo",
32
33
  "next-drizzle": "makerkit/next-drizzle-saas-kit-turbo",
33
34
  "next-prisma": "makerkit/next-prisma-saas-kit-turbo",
34
- "react-router-supabase": "makerkit/react-router-supabase-saas-kit-turbo"
35
+ "react-router-supabase": "makerkit/react-router-supabase-saas-kit-turbo",
36
+ "tanstack-supabase": "makerkit/tanstack-supabase-saas-kit-turbo",
37
+ "tanstack-drizzle": "makerkit/tanstack-drizzle-saas-kit-turbo",
38
+ "tanstack-prisma": "makerkit/tanstack-prisma-saas-kit-turbo"
35
39
  };
36
40
  function sshUrl(repo) {
37
41
  return `git@github.com:${repo}`;
@@ -167,6 +171,51 @@ const VARIANT_CATALOG = [
167
171
  database: "PostgreSQL (Supabase)",
168
172
  auth: "Supabase Auth",
169
173
  status: "stable"
174
+ },
175
+ {
176
+ id: "tanstack-supabase",
177
+ name: "TanStack Start + Supabase",
178
+ description: "Full-stack SaaS kit with TanStack Start and Supabase",
179
+ repo: VARIANT_REPO_MAP["tanstack-supabase"],
180
+ tech: [
181
+ "TanStack Start",
182
+ "Supabase",
183
+ "Tailwind CSS",
184
+ "shadcn/ui"
185
+ ],
186
+ database: "PostgreSQL (Supabase)",
187
+ auth: "Supabase Auth",
188
+ status: "stable"
189
+ },
190
+ {
191
+ id: "tanstack-drizzle",
192
+ name: "TanStack Start + Drizzle",
193
+ description: "Full-stack SaaS kit with TanStack Start and Drizzle ORM",
194
+ repo: VARIANT_REPO_MAP["tanstack-drizzle"],
195
+ tech: [
196
+ "TanStack Start",
197
+ "Drizzle",
198
+ "Tailwind CSS",
199
+ "shadcn/ui"
200
+ ],
201
+ database: "PostgreSQL",
202
+ auth: "Better Auth",
203
+ status: "stable"
204
+ },
205
+ {
206
+ id: "tanstack-prisma",
207
+ name: "TanStack Start + Prisma",
208
+ description: "Full-stack SaaS kit with TanStack Start and Prisma ORM",
209
+ repo: VARIANT_REPO_MAP["tanstack-prisma"],
210
+ tech: [
211
+ "TanStack Start",
212
+ "Prisma",
213
+ "Tailwind CSS",
214
+ "shadcn/ui"
215
+ ],
216
+ database: "PostgreSQL",
217
+ auth: "Better Auth",
218
+ status: "stable"
170
219
  }
171
220
  ];
172
221
  //#endregion
@@ -665,30 +714,65 @@ async function installRegistryFiles(variant, pluginId, username, majorVersion) {
665
714
  //#endregion
666
715
  //#region src/utils/run-codemod.ts
667
716
  const CODEMOD_TIMEOUT_MS = 300 * 1e3;
717
+ const IDLE_KILL_MS = 3e3;
668
718
  async function runCodemod(variant, pluginId, codemodVersion, options) {
669
719
  try {
670
720
  const localPath = process.env.MAKERKIT_CODEMODS_PATH;
671
721
  const runner = process.env.MAKERKIT_PACKAGE_RUNNER ?? "npx --yes";
672
722
  const versionTag = codemodVersion ? `@${codemodVersion}` : "";
673
- const { stdout, stderr } = await execaCommand(localPath ? `${runner} codemod@latest workflow run --allow-dirty -w ${localPath}/codemods/${variant}/${pluginId}` : `${runner} codemod@latest run @makerkit/${variant}-${pluginId}${versionTag} --allow-dirty --no-interactive`, {
674
- stdio: options?.captureOutput ? "pipe" : [
675
- "ignore",
676
- "inherit",
677
- "inherit"
678
- ],
679
- timeout: CODEMOD_TIMEOUT_MS
723
+ const noInteractive = options?.noInteractive ?? true;
724
+ const flags = ["--allow-dirty", noInteractive && "--no-interactive"].filter(Boolean).join(" ");
725
+ const command = localPath ? `${runner} codemod@latest workflow run ${flags} -w ${localPath}/codemods/${variant}/${pluginId}` : `${runner} codemod@latest run @makerkit/${variant}-${pluginId}${versionTag} ${flags}`;
726
+ return await new Promise((resolve, reject) => {
727
+ const child = spawn(command, {
728
+ shell: true,
729
+ stdio: noInteractive ? [
730
+ "ignore",
731
+ "pipe",
732
+ "pipe"
733
+ ] : "inherit"
734
+ });
735
+ let idleTimer = null;
736
+ const resetIdle = () => {
737
+ if (idleTimer) clearTimeout(idleTimer);
738
+ idleTimer = setTimeout(() => {
739
+ child.kill();
740
+ }, IDLE_KILL_MS);
741
+ };
742
+ if (child.stdout) {
743
+ child.stdout.pipe(process.stdout);
744
+ child.stdout.on("data", () => resetIdle());
745
+ }
746
+ if (child.stderr) {
747
+ child.stderr.pipe(process.stderr);
748
+ child.stderr.on("data", () => resetIdle());
749
+ }
750
+ resetIdle();
751
+ const globalTimer = setTimeout(() => {
752
+ if (idleTimer) clearTimeout(idleTimer);
753
+ child.kill();
754
+ resolve({
755
+ success: false,
756
+ output: `Codemod timed out after ${CODEMOD_TIMEOUT_MS / 1e3}s`
757
+ });
758
+ }, CODEMOD_TIMEOUT_MS);
759
+ child.on("exit", (code) => {
760
+ clearTimeout(globalTimer);
761
+ if (idleTimer) clearTimeout(idleTimer);
762
+ resolve({
763
+ success: code === 0 || code === null,
764
+ output: code === 0 || code === null ? "" : `Command failed with exit code ${code}`
765
+ });
766
+ });
767
+ child.on("error", (err) => {
768
+ clearTimeout(globalTimer);
769
+ if (idleTimer) clearTimeout(idleTimer);
770
+ reject(err);
771
+ });
680
772
  });
681
- return {
682
- success: true,
683
- output: stdout || stderr || ""
684
- };
685
773
  } catch (error) {
686
774
  let message = "Unknown error during codemod";
687
- if (error instanceof Error) {
688
- message = error.message;
689
- if ("stderr" in error && error.stderr) message += `\n${error.stderr}`;
690
- if ("timedOut" in error && error.timedOut) message = `Codemod timed out after ${CODEMOD_TIMEOUT_MS / 1e3}s (the workflow engine may have stalled after an error)`;
691
- }
775
+ if (error instanceof Error) message = error.message;
692
776
  return {
693
777
  success: false,
694
778
  output: message
@@ -757,8 +841,14 @@ async function detectVariant() {
757
841
  const dbDeps = await readDeps(join(cwd, "packages", "database", "package.json"));
758
842
  const hasSupabase = !!webDeps["@supabase/supabase-js"];
759
843
  const hasReactRouter = !!webDeps["@react-router/node"];
844
+ const hasTanstack = !!webDeps["@tanstack/react-start"];
760
845
  const hasDrizzle = !!webDeps["drizzle-orm"] || !!dbDeps["drizzle-orm"];
761
846
  const hasPrisma = !!webDeps["@prisma/client"] || !!dbDeps["@prisma/client"];
847
+ if (hasTanstack) {
848
+ if (hasSupabase) return "tanstack-supabase";
849
+ if (hasDrizzle) return "tanstack-drizzle";
850
+ if (hasPrisma) return "tanstack-prisma";
851
+ }
762
852
  if (hasReactRouter && hasSupabase) return "react-router-supabase";
763
853
  if (hasSupabase) return "next-supabase";
764
854
  if (hasDrizzle) return "next-drizzle";
@@ -798,7 +888,9 @@ async function addPlugin(options) {
798
888
  };
799
889
  const item = await installRegistryFiles(variant, options.pluginId, username, majorVersion);
800
890
  await saveBaseVersions(options.pluginId, item.files);
801
- const codemodResult = await runCodemod(variant, options.pluginId, item.codemodVersion, { captureOutput: options.captureCodemodOutput ?? true });
891
+ options.onBeforeCodemod?.();
892
+ const codemodResult = await runCodemod(variant, options.pluginId, item.codemodVersion);
893
+ options.onAfterCodemod?.();
802
894
  const envVars = getEnvVars(plugin, variant);
803
895
  return {
804
896
  success: true,
@@ -868,7 +960,8 @@ function createAddCommand(parentCommand) {
868
960
  pluginId,
869
961
  githubUsername: username,
870
962
  skipGitCheck: true,
871
- captureCodemodOutput: false
963
+ onBeforeCodemod: () => filesSpinner.stop(),
964
+ onAfterCodemod: () => filesSpinner.start("Installing plugin...")
872
965
  });
873
966
  } catch (error) {
874
967
  filesSpinner.fail("Failed to install plugin.");
@@ -882,7 +975,8 @@ function createAddCommand(parentCommand) {
882
975
  pluginId,
883
976
  githubUsername: username,
884
977
  skipGitCheck: true,
885
- captureCodemodOutput: false
978
+ onBeforeCodemod: () => retrySpinner.stop(),
979
+ onAfterCodemod: () => retrySpinner.start("Installing plugin...")
886
980
  });
887
981
  } catch (retryError) {
888
982
  retrySpinner.fail("Failed to install plugin.");