feat: derive package manager from env var

This commit is contained in:
Jacob Bolda
2021-04-12 15:50:57 -05:00
parent 22d1803b77
commit 6e0598c807
6 changed files with 136 additions and 83 deletions

View File

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

View File

@@ -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,
});
}
}

View File

@@ -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);
await installNpmDevPackage(devDependencies, packageManager, appDir);
await installNpmPackage(dependencies, packageManager, appDir);
devDependencies: string[];
packageManager: PackageManager;
}): Promise<Result> {
const result: Result = new Map<ManagementType, string[]>();
result.set(ManagementType.Install, installedDeps);
await installNpmDevPackage(devDependencies, packageManager, appDir);
result.set(ManagementType.Install, devDependencies);
return { result, packageManager };
await installNpmPackage(dependencies, packageManager, appDir);
result.set(ManagementType.Install, dependencies);
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",

View File

@@ -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>;
}

View File

@@ -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
`);
},
};

View File

@@ -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
`);
},
};