• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pkgxdev / pkgx / 10740348030

06 Sep 2024 02:40PM UTC coverage: 91.384% (-1.5%) from 92.889%
10740348030

push

github

web-flow
feat(install): add support for `--unsafe` install flag (#1008)

* feat(install): add support for `--unsafe` install

Signed-off-by: Rajdeep Malakar <rajdeepm.dev@gmail.com>

* forgot to remove debug

* --unsafe takes precedence, not the env

Signed-off-by: Rajdeep Malakar <rajdeepm.dev@gmail.com>

* let's just warn if deleting cache fails

Signed-off-by: Rajdeep Malakar <rajdeepm.dev@gmail.com>

* use pre-parsed unsafe flag

Signed-off-by: Rajdeep Malakar <rajdeepm.dev@gmail.com>

* do mkdir -p on the expected dir instead of just the cache one (prevents nested package caches from failing)

* improve it

* use `Config` and `Path` hooks

* fix script invocations

* linter making more work

* a few more

* last one

* Use `.slice()` instead of `.pop()`

* Use `.slice()` instead of `.pop()`, in `uninstall` too

* handle PKGX_UNSAFE_INSTALL=0 env

* fix it

* figure out what's managed by pkgx, but with comments.

Signed-off-by: Rajdeep Malakar <rajdeepm.dev@gmail.com>

---------

Signed-off-by: Rajdeep Malakar <rajdeepm.dev@gmail.com>
Co-authored-by: Jacob Heider <jacob@pkgx.dev>
Co-authored-by: Max Howell <mxcl@me.com>

370 of 405 branches covered (91.36%)

2 of 5 new or added lines in 1 file covered. (40.0%)

20 existing lines in 4 files now uncovered.

1465 of 1603 relevant lines covered (91.39%)

81.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.13
/src/parse-args.ts
1
import { UsageError } from "./utils/error.ts"
1✔
2
import { Path, utils } from "pkgx"
1✔
3
const { flatmap } = utils
1✔
4

5
type Pkgs = {
6
  plus: string[]
7
  minus: string[]
8
}
9

10
export type Args = {
11
  flags: Flags
12
} & (
13
  {
14
    mode: 'x' | 'run'
15
    args: string[]
16
    unknown: string[]
17
    pkgs: Pkgs
18
  } | {
19
    mode: 'internal.use'
20
    pkgs: Pkgs
21
  } | {
22
    mode: 'env'
23
    pkgs: {
24
      plus: string[]
25
      minus: string[]
26
    }
27
  } | {
28
    mode: 'shellcode' | 'version' | 'help'
29
  } | {
30
    mode: 'integrate' | 'deintegrate'
31
    dryrun: boolean
32
  } | {
33
    mode: 'provider' | 'shell-completion'
34
    args: string[]
35
  } | {
36
    mode: 'internal.activate'
37
    dir: Path
38
  } | {
39
    mode: 'install' | 'uninstall'
40
    args: string[]
41
  }
42
)
43

44
interface Flags {
45
  sync: boolean
46
  update: boolean
47
  verbosity?: number
48
  keepGoing: boolean,
49
  unsafe: boolean
50
}
51

52
export default function(input: string[]): Args {
1✔
53
  const it = input[Symbol.iterator]()
24✔
54
  const pkgs: Pkgs = { plus: [], minus: [] }
96✔
55
  const args: string[] = []
24✔
56
  const unknown: string[] = []
24✔
57
  const flags: Flags = {
24✔
58
    sync: false,
24✔
59
    update: false,
24✔
60
    keepGoing: false,
24✔
61
    unsafe: false
24✔
62
  }
24✔
63
  let mode: string | undefined
24✔
64
  let dryrun: boolean | undefined
24✔
65

66
  switch (input[0]) {
24✔
67
  case 'deintegrate':
24✔
68
  case 'integrate':
24✔
69
  case 'uninstall':
24✔
70
  case 'install':
24✔
71
  case 'run':
24✔
72
    mode = input[0]
28✔
73
    it.next()  // consume the subcommand
28✔
74
  }
24✔
75

76
  for (const arg of it) {
24✔
77
    const [type, content] = parse(arg)
66✔
78

79
    switch (type) {
66✔
80
    case '+':
66✔
81
      pkgs.plus.push(content)
72✔
82
      break
72✔
83
    case '-':
66✔
84
      pkgs.minus.push(content)
67✔
85
      break
67✔
86
    case '--':
66✔
87
      switch (content) {
90✔
88
      case 'sync':
90✔
89
        flags.sync = true
91✔
90
        break
91✔
91
      case 'update':
90✔
92
        flags.update = true
91✔
93
        break
91✔
NEW
94
      case 'unsafe':
×
NEW
95
        flags.unsafe = true
×
NEW
96
        break
×
97
      case 'provides':
×
98
        if (mode) throw new UsageError({msg: 'multiple modes specified'})
×
99
        console.error("%cdeprecated: %cuse pkgx --provider instead", 'color: red', 'color: initial')
×
100
        mode = 'provider'
×
101
        break
×
102
      case 'shellcode':
90✔
103
      case 'integrate':
90✔
104
      case 'internal.activate':
90✔
105
      case 'help':
90✔
106
      case 'version':
90✔
107
      case 'provider':
90✔
108
      case 'shell-completion':
90✔
109
      case 'internal.use':
90✔
110
        if (mode) throw new UsageError({msg: 'multiple modes specified'})
314✔
111
        mode = content
110✔
112
        break
110✔
113
      case 'silent':
90✔
114
        flags.verbosity = -2
91✔
115
        break
91✔
116
      case 'quiet':
90✔
117
        flags.verbosity = -1
91✔
118
        break
91✔
119
      case 'dry-run':
90✔
120
        dryrun = true
92✔
121
        break
92✔
122
      case 'keep-going':
×
123
        flags.keepGoing = true
×
124
        break
×
125
      case '':
90✔
126
        // empty the main loop iterator
91✔
127
        for (const arg of it) unknown.push(arg)
91✔
128
        break
91✔
129
      default: {
185✔
130
        const match = content.match(/^verbose(=-?\d+)?$/)
95✔
131
        if (match) {
95✔
132
          flags.verbosity = flatmap(match[1], n => parseInt(n.slice(1))!) ?? 1
99✔
133
        } else {
95✔
134
          throw new UsageError(arg)
96✔
135
        }
96✔
136
      }}
185✔
137
      break
109✔
138
    default:
66✔
139
      /// unrecognized args means the start of the runner args
75✔
140
      args.push(arg)
75✔
141
      for (const arg of it) args.push(arg)
75✔
142
    }
75✔
143
  }
75✔
144

145
  if (dryrun !== undefined && !(mode == 'integrate' || mode == 'deintegrate')) {
24✔
146
    throw new UsageError({msg: '--dry-run cannot be specified with this mode'})
75✔
147
  }
25✔
148

149
  switch (mode) {
39✔
150
  case undefined:
24✔
151
    if (args.length + unknown.length) {
32✔
152
      return { mode: 'x', flags, pkgs, args, unknown }
245✔
153
    } else {
32✔
154
      return { mode: 'env', flags, pkgs }
185✔
155
    }
37✔
156
  case 'internal.use':
24✔
157
    return { mode, flags, pkgs }
125✔
158
  case 'provider':
24✔
159
  case 'shell-completion':
24✔
160
    return { mode, flags, args }
125✔
161
  case 'internal.activate':
24✔
162
    return { mode, flags, dir: new Path(args[0]) }
125✔
163
  case 'install':
24✔
164
  case 'uninstall':
24✔
165
    return { mode, flags, args }
125✔
166
  case 'integrate':
24✔
167
  case 'deintegrate':
24✔
168
    dryrun ??= false
27✔
169
    return { mode, dryrun, flags }
135✔
170
  default:
×
171
    // deno-lint-ignore no-explicit-any
×
172
    return { mode: mode as any, flags }
×
173
  }
24✔
174
}
24✔
175

176

177
//////////////////////// utils
1✔
178

179
function parse(arg: string): ['+', string] | ['--', string] | ['-', string] | [undefined, string] {
43✔
180
  switch (arg[0]) {
43✔
181
  case '+': {
93✔
182
    const content = arg.slice(1)
50✔
183
    if (!content) throw new UsageError(arg)
50✔
184
    return ['+', content]
224✔
185
  }
56✔
186
  case '-':
43✔
187
    if (arg[1] == '-') {
69✔
188
      return ['--', arg.slice(2)]
372✔
189
    } else {
69✔
190
      const content = arg.slice(1)
71✔
191
      if (!content) throw new UsageError(arg)
71✔
192
      return ['-', content]
288✔
193
    }
72✔
194
  default:
43✔
195
    return [undefined, arg]
208✔
196
  }
43✔
197
}
43✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc