aboutsummaryrefslogtreecommitdiff
path: root/src/which_npm_client.zig
blob: 56dd752c1bb29232814ce9a2184ac6c0a9999403 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
usingnamespace @import("./global.zig");

const which = @import("./which.zig").which;
const std = @import("std");

pub const NPMClient = struct {
    bin: string,
    tag: Tag,

    pub const Tag = enum {
        npm,
        yarn,
        pnpm,
    };

    // This check adds around 150ms
    // so...if we do do this, we should do it in a separate thread
    pub fn isYarnBerry(allocator: *std.mem.Allocator, cwd_dir: string, yarn_path: string) bool {
        var args = [_]string{ yarn_path, "--version" };
        var term = std.ChildProcess.exec(.{
            .argv = &args,
            .allocator = allocator,
            .cwd = if (cwd_dir.len > 1) std.mem.trimRight(u8, cwd_dir, "/") else cwd_dir,
        }) catch return true;
        defer allocator.free(term.stderr);
        defer allocator.free(term.stdout);

        if (term.stdout.len == 0) return true;
        return term.stdout[0] != '1';
    }

    pub fn detect(allocator: *std.mem.Allocator, realpath_buf: *[std.fs.MAX_PATH_BYTES]u8, PATH: string, cwd: string, comptime allow_yarn: bool) !?NPMClient {

        // We say:
        // - pnpm if it exists, is the default. its most esoteric, so if you have it installed, you prob want it.
        // - yarn if it exists and it is yarn 1, its the default (yarn 2 or later is not supported)
        // - else npm

        const out_path = brk: {
            var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;

            const path: [:0]const u8 = if (comptime allow_yarn)
                which(
                    &path_buf,
                    PATH,
                    cwd,
                    "pnpm",
                ) orelse which(
                    &path_buf,
                    PATH,
                    cwd,
                    "yarn",
                ) orelse which(
                    &path_buf,
                    PATH,
                    cwd,
                    "npm",
                ) orelse ""
            else
                which(
                    &path_buf,
                    PATH,
                    cwd,
                    "pnpm",
                ) orelse which(
                    &path_buf,
                    PATH,
                    cwd,
                    "npm",
                ) orelse "";

            std.mem.copy(u8, realpath_buf, std.mem.span(path));
            // It's important we don't resolve the symlink
            // That breaks volta.
            break :brk realpath_buf[0..path.len];   
        };

        const basename = std.fs.path.basename(std.mem.span(out_path));
        if (basename.len == 0) return null;

        // if (comptime allow_yarn) {
        //     if (std.mem.indexOf(u8, basename, "yarn") != null) {
        //         if (isYarnBerry(allocator, cwd, out_path)) {
        //             return try detect(allocator, realpath_buf, PATH, cwd, false);
        //         }
        //     }
        // }

        if (strings.contains(basename, "pnpm")) {
            return NPMClient{ .bin = out_path, .tag = .pnpm };
        }

        if (strings.contains(basename, "yarn")) {
            return NPMClient{ .bin = out_path, .tag = .yarn };
        }

        if (strings.contains(basename, "npm")) {
            return NPMClient{ .bin = out_path, .tag = .npm };
        }

        return null;
    }
};