这是indexloc提供的服务,不要输入任何密码
Skip to content

feat: check whether or not yarn is installed #265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions create-turbo/__tests__/__data__/a/b
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

echo "test"
Empty file.
3 changes: 3 additions & 0 deletions create-turbo/__tests__/__data__/d/e.command
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

echo "test"
15 changes: 10 additions & 5 deletions create-turbo/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,21 @@ describe("create-turbo cli", () => {
break;

case 3:
// Which package manager do you want to use?
// easy to change deployment targets.
expect(getPromptChoices(prompt)).toEqual(["Yarn", "NPM"]);
const chocies = getPromptChoices(prompt);

// yarn is optional. however, npm should be present on almost
// every system.
expect(chocies).toContain("NPM");

// just hit enter to pick the first result from the list.
cli.stdin.write(keys.enter);
break;

case 4:
expect(prompt).toEqual(
"? Do you want me to run `yarn install`? (Y/n)"
expect(prompt).toMatch(
/\? Do you want me to run \`(yarn|npm) install\`\? \(Y\/n\)/
);

cli.stdin.write("n");

// At this point the CLI will create directories and all that fun stuff
Expand Down
29 changes: 29 additions & 0 deletions create-turbo/__tests__/hasExecutable.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import path from "path";

import { hasExecutable } from "../src/hasExecutable";

const baseDirectory = path.join(__dirname, "__data__");

describe("hasExecutable", () => {
test("works with process.env.PATH and process.env.PATHEXT", async () => {
expect(await hasExecutable("node")).toBeTruthy();
});

test("returns true, if the executable is present", async () => {
expect(
await hasExecutable("b", [path.resolve(baseDirectory, "a")])
).toBeTruthy();
});

test("returns false, if the executable is not present", async () => {
expect(
await hasExecutable("c", [path.resolve(baseDirectory, "c")])
).toBeFalsy();
});

test("works with extensions", async () => {
expect(
await hasExecutable("e", [path.resolve(baseDirectory, "d")], [".command"])
).toBeTruthy();
});
});
42 changes: 42 additions & 0 deletions create-turbo/src/hasExecutable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import path from "path";
import fs from "fs/promises";

const environmentPaths = (process.env.PATH || "")
.replace(/["]+/g, "")
.split(path.delimiter)
.filter(Boolean);

const environmentExtensions = (process.env.PATHEXT || "").split(";");

/**
* Determines whether or not the given executable is present on the system.
*
* Inspired by https://github.com/springernature/hasbin/blob/master/lib/hasbin.js#L55
*
* @param name
*/
export async function hasExecutable(
executable: string,
paths = environmentPaths,
extensions = environmentExtensions
) {
try {
return await Promise.any(
paths
.flatMap((d) => extensions.map((ext) => path.join(d, executable + ext)))
.map(isFilePresent)
);
} catch (err) {
return false;
}
}

async function isFilePresent(path: string) {
const stats = await fs.stat(path);

if (stats.isFile()) {
return true;
}

throw new Error(`${path} is not a file`);
}
9 changes: 7 additions & 2 deletions create-turbo/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import chalk from "chalk";
import cliPkgJson from "../package.json";
import { shouldUseYarn } from "./shouldUseYarn";
import { tryGitInit } from "./git";
import { hasExecutable } from "./hasExecutable";

const turboGradient = gradient("#0099F7", "#F11712");
const help = `
Expand Down Expand Up @@ -88,7 +89,11 @@ async function run() {
type: "list",
message: "Which package manager do you want to use?",
choices: [
{ name: "Yarn", value: "yarn" },
{
name: "Yarn",
value: "yarn",
disabled: !(await hasExecutable("yarn")),
},
{ name: "NPM", value: "npm" },
// { name: "PNPM", value: "pnpm" },
],
Expand Down Expand Up @@ -246,7 +251,7 @@ async function notifyUpdate(): Promise<void> {
try {
const res = await update;
if (res?.latest) {
const isYarn = shouldUseYarn();
const isYarn = await shouldUseYarn();

console.log();
console.log(
Expand Down
18 changes: 7 additions & 11 deletions create-turbo/src/shouldUseYarn.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { execSync } from "child_process";
import { hasExecutable } from "./hasExecutable";

export function shouldUseYarn(): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this insufficient?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not. I just figured, that we can use hasExecutable in order to determine whether or not yarn is installed. Since hasExecutable returns a Promise<boolean> I just removed the type here.

try {
const userAgent = process.env.npm_config_user_agent;
if (userAgent) {
return Boolean(userAgent && userAgent.startsWith("yarn"));
}
execSync("yarnpkg --version", { stdio: "ignore" });
return true;
} catch (e) {
return false;
export async function shouldUseYarn() {
const userAgent = process.env.npm_config_user_agent;
if (userAgent) {
return Boolean(userAgent && userAgent.startsWith("yarn"));
}

return hasExecutable("yarn");
}