马宇豪
2024-07-16 f591c27b57e2418c9495bc02ae8cfff84d35bc18
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env node
 
const cli = require('../lib/cli.js')
 
// run the resulting command as `npm exec ...args`
process.argv[1] = require.resolve('./npm-cli.js')
process.argv.splice(2, 0, 'exec')
 
// TODO: remove the affordances for removed items in npm v9
const removedSwitches = new Set([
  'always-spawn',
  'ignore-existing',
  'shell-auto-fallback',
])
 
const removedOpts = new Set([
  'npm',
  'node-arg',
  'n',
])
 
const removed = new Set([
  ...removedSwitches,
  ...removedOpts,
])
 
const { definitions, shorthands } = require('@npmcli/config/lib/definitions')
const npmSwitches = Object.entries(definitions)
  .filter(([, { type }]) => type === Boolean ||
    (Array.isArray(type) && type.includes(Boolean)))
  .map(([key]) => key)
 
// things that don't take a value
const switches = new Set([
  ...removedSwitches,
  ...npmSwitches,
  'no-install',
  'quiet',
  'q',
  'version',
  'v',
  'help',
  'h',
])
 
// things that do take a value
const opts = new Set([
  ...removedOpts,
  'package',
  'p',
  'cache',
  'userconfig',
  'call',
  'c',
  'shell',
  'npm',
  'node-arg',
  'n',
])
 
// break out of loop when we find a positional argument or --
// If we find a positional arg, we shove -- in front of it, and
// let the normal npm cli handle the rest.
let i
let sawRemovedFlags = false
for (i = 3; i < process.argv.length; i++) {
  const arg = process.argv[i]
  if (arg === '--') {
    break
  } else if (/^-/.test(arg)) {
    const [key, ...v] = arg.replace(/^-+/, '').split('=')
 
    switch (key) {
      case 'p':
        process.argv[i] = ['--package', ...v].join('=')
        break
 
      case 'shell':
        process.argv[i] = ['--script-shell', ...v].join('=')
        break
 
      case 'no-install':
        process.argv[i] = '--yes=false'
        break
 
      default:
        // resolve shorthands and run again
        if (shorthands[key] && !removed.has(key)) {
          const a = [...shorthands[key]]
          if (v.length) {
            a.push(v.join('='))
          }
          process.argv.splice(i, 1, ...a)
          i--
          continue
        }
        break
    }
 
    if (removed.has(key)) {
      // eslint-disable-next-line no-console
      console.error(`npx: the --${key} argument has been removed.`)
      sawRemovedFlags = true
      process.argv.splice(i, 1)
      i--
    }
 
    if (v.length === 0 && !switches.has(key) &&
        (opts.has(key) || !/^-/.test(process.argv[i + 1]))) {
      // value will be next argument, skip over it.
      if (removed.has(key)) {
        // also remove the value for the cut key.
        process.argv.splice(i + 1, 1)
      } else {
        i++
      }
    }
  } else {
    // found a positional arg, put -- in front of it, and we're done
    process.argv.splice(i, 0, '--')
    break
  }
}
 
if (sawRemovedFlags) {
  // eslint-disable-next-line no-console
  console.error('See `npm help exec` for more information')
}
 
cli(process)