• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1const columns = require('cli-columns')
2const libteam = require('libnpmteam')
3
4const otplease = require('../utils/otplease.js')
5
6const BaseCommand = require('../base-command.js')
7class Team extends BaseCommand {
8  static description = 'Manage organization teams and team memberships'
9  static name = 'team'
10  static usage = [
11    'create <scope:team> [--otp <otpcode>]',
12    'destroy <scope:team> [--otp <otpcode>]',
13    'add <scope:team> <user> [--otp <otpcode>]',
14    'rm <scope:team> <user> [--otp <otpcode>]',
15    'ls <scope>|<scope:team>',
16  ]
17
18  static params = [
19    'registry',
20    'otp',
21    'parseable',
22    'json',
23  ]
24
25  static ignoreImplicitWorkspace = false
26
27  static async completion (opts) {
28    const { conf: { argv: { remain: argv } } } = opts
29    const subcommands = ['create', 'destroy', 'add', 'rm', 'ls']
30
31    if (argv.length === 2) {
32      return subcommands
33    }
34
35    if (subcommands.includes(argv[2])) {
36      return []
37    }
38
39    throw new Error(argv[2] + ' not recognized')
40  }
41
42  async exec ([cmd, entity = '', user = '']) {
43    // Entities are in the format <scope>:<team>
44    // XXX: "description" option to libnpmteam is used as a description of the
45    // team, but in npm's options, this is a boolean meaning "show the
46    // description in npm search output".  Hence its being set to null here.
47    await otplease(this.npm, { ...this.npm.flatOptions }, opts => {
48      entity = entity.replace(/^@/, '')
49      switch (cmd) {
50        case 'create': return this.create(entity, opts)
51        case 'destroy': return this.destroy(entity, opts)
52        case 'add': return this.add(entity, user, opts)
53        case 'rm': return this.rm(entity, user, opts)
54        case 'ls': {
55          const match = entity.match(/[^:]+:.+/)
56          if (match) {
57            return this.listUsers(entity, opts)
58          } else {
59            return this.listTeams(entity, opts)
60          }
61        }
62        default:
63          throw this.usageError()
64      }
65    })
66  }
67
68  async create (entity, opts) {
69    await libteam.create(entity, opts)
70    if (opts.json) {
71      this.npm.output(JSON.stringify({
72        created: true,
73        team: entity,
74      }))
75    } else if (opts.parseable) {
76      this.npm.output(`${entity}\tcreated`)
77    } else if (!this.npm.silent) {
78      this.npm.output(`+@${entity}`)
79    }
80  }
81
82  async destroy (entity, opts) {
83    await libteam.destroy(entity, opts)
84    if (opts.json) {
85      this.npm.output(JSON.stringify({
86        deleted: true,
87        team: entity,
88      }))
89    } else if (opts.parseable) {
90      this.npm.output(`${entity}\tdeleted`)
91    } else if (!this.npm.silent) {
92      this.npm.output(`-@${entity}`)
93    }
94  }
95
96  async add (entity, user, opts) {
97    await libteam.add(user, entity, opts)
98    if (opts.json) {
99      this.npm.output(JSON.stringify({
100        added: true,
101        team: entity,
102        user,
103      }))
104    } else if (opts.parseable) {
105      this.npm.output(`${user}\t${entity}\tadded`)
106    } else if (!this.npm.silent) {
107      this.npm.output(`${user} added to @${entity}`)
108    }
109  }
110
111  async rm (entity, user, opts) {
112    await libteam.rm(user, entity, opts)
113    if (opts.json) {
114      this.npm.output(JSON.stringify({
115        removed: true,
116        team: entity,
117        user,
118      }))
119    } else if (opts.parseable) {
120      this.npm.output(`${user}\t${entity}\tremoved`)
121    } else if (!this.npm.silent) {
122      this.npm.output(`${user} removed from @${entity}`)
123    }
124  }
125
126  async listUsers (entity, opts) {
127    const users = (await libteam.lsUsers(entity, opts)).sort()
128    if (opts.json) {
129      this.npm.output(JSON.stringify(users, null, 2))
130    } else if (opts.parseable) {
131      this.npm.output(users.join('\n'))
132    } else if (!this.npm.silent) {
133      const plural = users.length === 1 ? '' : 's'
134      const more = users.length === 0 ? '' : ':\n'
135      this.npm.output(`\n@${entity} has ${users.length} user${plural}${more}`)
136      this.npm.output(columns(users, { padding: 1 }))
137    }
138  }
139
140  async listTeams (entity, opts) {
141    const teams = (await libteam.lsTeams(entity, opts)).sort()
142    if (opts.json) {
143      this.npm.output(JSON.stringify(teams, null, 2))
144    } else if (opts.parseable) {
145      this.npm.output(teams.join('\n'))
146    } else if (!this.npm.silent) {
147      const plural = teams.length === 1 ? '' : 's'
148      const more = teams.length === 0 ? '' : ':\n'
149      this.npm.output(`\n@${entity} has ${teams.length} team${plural}${more}`)
150      this.npm.output(columns(teams.map(t => `@${t}`), { padding: 1 }))
151    }
152  }
153}
154module.exports = Team
155