فهرست منبع

feat: derive package manager from env var

Jacob Bolda 4 سال پیش
والد
کامیت
6e0598c807

+ 5 - 0
.changes/cta-derive-packageManager.md

@@ -0,0 +1,5 @@
+---
+"create-tauri-app": patch
+---
+
+Use a test based on an npm env var to determine which package manager to use.

+ 19 - 10
tooling/create-tauri-app/bin/create-tauri-app.js

@@ -12,9 +12,9 @@ const {
   recipeByDescriptiveName,
   recipeByShortName,
   install,
+  checkPackageManager,
   shell,
 } = require("../dist/");
-const { dir } = require("console");
 
 /**
  * @type {object}
@@ -38,6 +38,7 @@ const createTauriApp = async (cliArgs) => {
       v: "version",
       f: "force",
       l: "log",
+      m: "manager",
       d: "directory",
       b: "binary",
       t: "tauri-path",
@@ -81,6 +82,7 @@ function printUsage() {
     --ci                 Skip prompts
     --force, -f          Force init to overwrite [conf|template|all]
     --log, -l            Logging [boolean]
+    --manager, -d        Set package manager to use [npm|yarn]
     --directory, -d      Set target directory for init
     --binary, -b         Optional path to a tauri binary from which to run init
     --app-name, -A       Name of your Tauri application
@@ -142,6 +144,8 @@ async function runInit(argv, config = {}) {
       window: { title },
     },
   } = config;
+  // this little fun snippet pulled from vite determines the package manager the script was run from
+  const packageManager = /yarn/.test(process.env.npm_execpath) ? "yarn" : "npm";
 
   let recipe;
 
@@ -157,7 +161,7 @@ async function runInit(argv, config = {}) {
   };
 
   if (recipe !== undefined) {
-    buildConfig = recipe.configUpdate(buildConfig);
+    buildConfig = recipe.configUpdate({ buildConfig, packageManager });
   }
 
   const directory = argv.d || process.cwd();
@@ -166,14 +170,18 @@ async function runInit(argv, config = {}) {
     appName: appName || argv.A,
     windowTitle: title || argv.w,
   };
+
   // note that our app directory is reliant on the appName and
   // generally there are issues if the path has spaces (see Windows)
   // future TODO prevent app names with spaces or escape here?
   const appDirectory = join(directory, cfg.appName);
 
+  // this throws an error if we can't run the package manager they requested
+  await checkPackageManager({ cwd: directory, packageManager });
+
   if (recipe.preInit) {
     console.log("===== running initial command(s) =====");
-    await recipe.preInit({ cwd: directory, cfg });
+    await recipe.preInit({ cwd: directory, cfg, packageManager });
   }
 
   const initArgs = [
@@ -183,24 +191,24 @@ async function runInit(argv, config = {}) {
     ["--dev-path", cfg.devPath],
   ].reduce((final, argSet) => {
     if (argSet[1]) {
-      return final.concat([argSet[0], `\"${argSet[1]}\"`]);
+      return final.concat(argSet);
     } else {
       return final;
     }
   }, []);
 
-  const installed = await install({
+  console.log("===== installing any additional needed deps =====");
+  await install({
     appDir: appDirectory,
     dependencies: recipe.extraNpmDependencies,
-    devDependencies: ["tauri", ...recipe.extraNpmDevDependencies],
+    devDependencies: ["@tauri-apps/cli", ...recipe.extraNpmDevDependencies],
+    packageManager,
   });
 
   console.log("===== running tauri init =====");
-  const binary = !argv.b
-    ? installed.packageManager
-    : resolve(appDirectory, argv.b);
+  const binary = !argv.b ? packageManager : resolve(appDirectory, argv.b);
   const runTauriArgs =
-    installed.packageManager === "npm" && !argv.b
+    packageManager === "npm" && !argv.b
       ? ["run", "tauri", "--", "init"]
       : ["tauri", "init"];
   await shell(binary, [...runTauriArgs, ...initArgs], {
@@ -212,6 +220,7 @@ async function runInit(argv, config = {}) {
     await recipe.postInit({
       cwd: appDirectory,
       cfg,
+      packageManager,
     });
   }
 }

+ 29 - 42
tooling/create-tauri-app/src/dependency-manager.ts

@@ -4,68 +4,54 @@
 
 import { ManagementType, Result } from "./types/deps";
 import { shell } from "./shell";
-import { existsSync } from "fs";
-import { join } from "path";
+
+export type PackageManager = "npm" | "yarn";
 
 export async function install({
   appDir,
   dependencies,
   devDependencies,
+  packageManager,
 }: {
   appDir: string;
   dependencies: string[];
-  devDependencies?: string[];
-}) {
-  return await manageDependencies(appDir, dependencies, devDependencies);
-}
-
-async function manageDependencies(
-  appDir: string,
-  dependencies: string[] = [],
-  devDependencies: string[] = []
-): Promise<{ result: Result; packageManager: string }> {
-  const installedDeps = [...dependencies, ...devDependencies];
-  console.log(`Installing ${installedDeps.join(", ")}...`);
-
-  const packageManager = await usePackageManager(appDir);
-
+  devDependencies: string[];
+  packageManager: PackageManager;
+}): Promise<Result> {
+  const result: Result = new Map<ManagementType, string[]>();
   await installNpmDevPackage(devDependencies, packageManager, appDir);
-  await installNpmPackage(dependencies, packageManager, appDir);
+  result.set(ManagementType.Install, devDependencies);
 
-  const result: Result = new Map<ManagementType, string[]>();
-  result.set(ManagementType.Install, installedDeps);
+  await installNpmPackage(dependencies, packageManager, appDir);
+  result.set(ManagementType.Install, dependencies);
 
-  return { result, packageManager };
+  return result;
 }
 
-async function usePackageManager(appDir: string): Promise<"yarn" | "npm"> {
-  const hasYarnLockfile = existsSync(join(appDir, "yarn.lock"));
-  let yarnChild;
-  // try yarn first if there is a lockfile
-  if (hasYarnLockfile) {
-    yarnChild = await shell("yarn", ["--version"], { stdio: "pipe" });
-    if (!yarnChild.failed) return "yarn";
+export async function checkPackageManager({
+  cwd,
+  packageManager,
+}: {
+  cwd: string;
+  packageManager: PackageManager;
+}): Promise<boolean> {
+  try {
+    await shell(packageManager, ["--version"], { stdio: "pipe", cwd });
+    return true;
+  } catch (error) {
+    throw new Error(
+      `Must have ${packageManager} installed to manage dependencies. Is either in your PATH? We tried running in ${cwd}`
+    );
   }
-
-  // try npm then as the "default"
-  const npmChild = await shell("npm", ["--version"], { stdio: "pipe" });
-  if (!npmChild.failed) return "npm";
-
-  // try yarn as maybe only yarn is installed
-  if (yarnChild && !yarnChild.failed) return "yarn";
-
-  // if we have reached here, we can't seem to run anything
-  throw new Error(
-    `Must have npm or yarn installed to manage dependencies. Is either in your PATH? We tried running in ${appDir}`
-  );
 }
 
 async function installNpmPackage(
   packageNames: string[],
-  packageManager: string,
+  packageManager: PackageManager,
   appDir: string
 ): Promise<void> {
   if (packageNames.length === 0) return;
+  console.log(`Installing ${packageNames.join(", ")}...`);
   if (packageManager === "yarn") {
     await shell("yarn", ["add", packageNames.join(" ")], {
       cwd: appDir,
@@ -79,10 +65,11 @@ async function installNpmPackage(
 
 async function installNpmDevPackage(
   packageNames: string[],
-  packageManager: string,
+  packageManager: PackageManager,
   appDir: string
 ): Promise<void> {
   if (packageNames.length === 0) return;
+  console.log(`Installing ${packageNames.join(", ")}...`);
   if (packageManager === "yarn") {
     await shell(
       "yarn",

+ 13 - 2
tooling/create-tauri-app/src/index.ts

@@ -8,27 +8,38 @@ import { reactjs, reactts } from "./recipes/react";
 import { vanillajs } from "./recipes/vanilla";
 
 export { shell } from "./shell";
-export { install } from "./dependency-manager";
+export { install, checkPackageManager } from "./dependency-manager";
+import { PackageManager } from "./dependency-manager";
 
 export interface Recipe {
   descriptiveName: string;
   shortName: string;
-  configUpdate?: (cfg: TauriBuildConfig) => TauriBuildConfig;
+  configUpdate?: ({
+    cfg,
+    packageManager,
+  }: {
+    cfg: TauriBuildConfig;
+    packageManager: PackageManager;
+  }) => TauriBuildConfig;
   extraNpmDependencies: string[];
   extraNpmDevDependencies: string[];
   preInit?: ({
     cwd,
     cfg,
+    packageManager,
   }: {
     cwd: string;
     cfg: TauriBuildConfig;
+    packageManager: PackageManager;
   }) => Promise<void>;
   postInit?: ({
     cwd,
     cfg,
+    packageManager,
   }: {
     cwd: string;
     cfg: TauriBuildConfig;
+    packageManager: PackageManager;
   }) => Promise<void>;
 }
 

+ 51 - 18
tooling/create-tauri-app/src/recipes/react.ts

@@ -4,11 +4,6 @@ import { join } from "path";
 import scaffe from "scaffe";
 import { shell } from "../shell";
 
-const completeLogMsg = `
-  Your installation completed.
-  To start, run yarn tauri dev
-`;
-
 const afterCra = async (cwd: string, appName: string, version: string) => {
   const templateDir = join(__dirname, "../src/templates/react");
   const variables = {
@@ -29,7 +24,7 @@ const afterCra = async (cwd: string, appName: string, version: string) => {
 const reactjs: Recipe = {
   descriptiveName: "React.js",
   shortName: "reactjs",
-  configUpdate: (cfg) => ({
+  configUpdate: ({ cfg }) => ({
     ...cfg,
     distDir: `../build`,
     devPath: "http://localhost:3000",
@@ -38,17 +33,32 @@ const reactjs: Recipe = {
   }),
   extraNpmDevDependencies: [],
   extraNpmDependencies: [],
-  preInit: async ({ cwd, cfg }) => {
+  preInit: async ({ cwd, cfg, packageManager }) => {
     // CRA creates the folder for you
-    await shell("npx", ["create-react-app", `${cfg.appName}`], { cwd });
+    if (packageManager === "yarn") {
+      await shell("yarn", ["create", "react-app", `${cfg.appName}`], {
+        cwd,
+      });
+    } else {
+      await shell(
+        "npm",
+        ["init", "react-app", `${cfg.appName}`, "--", "--use-npm"],
+        {
+          cwd,
+        }
+      );
+    }
     const version = await shell("npm", ["view", "tauri", "version"], {
       stdio: "pipe",
     });
     const versionNumber = version.stdout.trim();
     await afterCra(cwd, cfg.appName, versionNumber);
   },
-  postInit: async ({ cfg }) => {
-    console.log(completeLogMsg);
+  postInit: async ({ packageManager }) => {
+    console.log(`
+    Your installation completed.
+    To start, run ${packageManager} tauri dev
+  `);
   },
 };
 
@@ -57,16 +67,39 @@ const reactts: Recipe = {
   descriptiveName: "React with Typescript",
   shortName: "reactts",
   extraNpmDependencies: [],
-  preInit: async ({ cwd, cfg }) => {
+  preInit: async ({ cwd, cfg, packageManager }) => {
     // CRA creates the folder for you
-    await shell(
-      "npx",
-      ["create-react-app", "--template", "typescript", `${cfg.appName}`],
-      { cwd }
-    );
+    if (packageManager === "yarn") {
+      await shell(
+        "yarn",
+        ["create", "react-app", "--template", "typescript", `${cfg.appName}`],
+        {
+          cwd,
+        }
+      );
+    } else {
+      await shell(
+        "npm",
+        [
+          "init",
+          "react-app",
+          `${cfg.appName}`,
+          "--",
+          "--use-npm",
+          "--template",
+          "typescript",
+        ],
+        {
+          cwd,
+        }
+      );
+    }
   },
-  postInit: async ({ cfg }) => {
-    console.log(completeLogMsg);
+  postInit: async ({ packageManager }) => {
+    console.log(`
+    Your installation completed.
+    To start, run ${packageManager} tauri dev
+  `);
   },
 };
 

+ 18 - 10
tooling/create-tauri-app/src/recipes/vanilla.ts

@@ -8,7 +8,7 @@ import { shell } from "../shell";
 export const vanillajs: Recipe = {
   descriptiveName: "Vanilla.js",
   shortName: "vanillajs",
-  configUpdate: (cfg) => ({
+  configUpdate: ({ cfg }) => ({
     ...cfg,
     distDir: `../dist`,
     devPath: `../dist`,
@@ -24,16 +24,24 @@ export const vanillajs: Recipe = {
     const versionNumber = version.stdout.trim();
     await run(cfg, cwd, versionNumber);
   },
-  postInit: async ({ cfg }) => {
+  postInit: async ({ cfg, packageManager }) => {
+    const setApp =
+      packageManager === "npm"
+        ? `
+set tauri script once
+  $ npm set-script tauri tauri
+    `
+        : "";
+
     console.log(`
-      change directory:
-        $ cd ${cfg.appName}
-    
-      install dependencies:
-        $ yarn # npm install
-    
-      run the app:
-        $ yarn tauri dev # npm run tauri dev
+change directory:
+  $ cd ${cfg.appName}
+${setApp}
+install dependencies:
+  $ ${packageManager} install
+
+run the app:
+  $ ${packageManager} tauri ${packageManager === "npm" ? "-- " : ""}dev
             `);
   },
 };