• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1
2const promzard = require('promzard')
3const path = require('path')
4const fs = require('fs/promises')
5const semver = require('semver')
6const read = require('read')
7const util = require('util')
8const rpj = require('read-package-json')
9
10const def = require.resolve('./default-input.js')
11
12// to validate the data object at the end as a worthwhile package
13// and assign default values for things.
14const _extraSet = rpj.extraSet
15const _rpj = util.promisify(rpj)
16const _rpjExtras = util.promisify(rpj.extras)
17const readPkgJson = async (file, pkg) => {
18  // only do a few of these. no need for mans or contributors if they're in the files
19  rpj.extraSet = _extraSet.filter(f => f.name !== 'authors' && f.name !== 'mans')
20  const p = pkg ? _rpjExtras(file, pkg) : _rpj(file)
21  return p.catch(() => ({})).finally(() => rpj.extraSet = _extraSet)
22}
23
24const isYes = (c) => !!(c.get('yes') || c.get('y') || c.get('force') || c.get('f'))
25
26const getConfig = (c = {}) => {
27  // accept either a plain-jane object, or a config object with a "get" method.
28  if (typeof c.get !== 'function') {
29    const data = c
30    return {
31      get: (k) => data[k],
32      toJSON: () => data,
33    }
34  }
35  return c
36}
37
38const stringifyPerson = (p) => {
39  if (typeof p === 'string') {
40    return p
41  }
42  const { name = '', url, web, email, mail } = p
43  const u = url || web
44  const e = email || mail
45  return `${name}${e ? ` <${e}>` : ''}${u ? ` (${u})` : ''}`
46}
47
48async function init (dir, input = def, c = {}) {
49  const config = getConfig(c)
50  const yes = isYes(config)
51  const packageFile = path.resolve(dir, 'package.json')
52
53  const pkg = await readPkgJson(packageFile)
54
55  if (!semver.valid(pkg.version)) {
56    delete pkg.version
57  }
58
59  // make sure that the input is valid. if not, use the default
60  const pzData = await promzard(path.resolve(input), {
61    yes,
62    config,
63    filename: packageFile,
64    dirname: path.dirname(packageFile),
65    basename: path.basename(path.dirname(packageFile)),
66    package: pkg,
67  }, { backupFile: def })
68
69  for (const [k, v] of Object.entries(pzData)) {
70    if (v != null) {
71      pkg[k] = v
72    }
73  }
74
75  const pkgExtras = await readPkgJson(packageFile, pkg)
76
77  // turn the objects into somewhat more humane strings.
78  if (pkgExtras.author) {
79    pkgExtras.author = stringifyPerson(pkgExtras.author)
80  }
81
82  for (const set of ['maintainers', 'contributors']) {
83    if (Array.isArray(pkgExtras[set])) {
84      pkgExtras[set] = pkgExtras[set].map(stringifyPerson)
85    }
86  }
87
88  // no need for the readme now.
89  delete pkgExtras.readme
90  delete pkgExtras.readmeFilename
91
92  // really don't want to have this lying around in the file
93  delete pkgExtras._id
94
95  // ditto
96  delete pkgExtras.gitHead
97
98  // if the repo is empty, remove it.
99  if (!pkgExtras.repository) {
100    delete pkgExtras.repository
101  }
102
103  // readJson filters out empty descriptions, but init-package-json
104  // traditionally leaves them alone
105  if (!pkgExtras.description) {
106    pkgExtras.description = pzData.description
107  }
108
109  // optionalDependencies don't need to be repeated in two places
110  if (pkgExtras.dependencies) {
111    if (pkgExtras.optionalDependencies) {
112      for (const name of Object.keys(pkgExtras.optionalDependencies)) {
113        delete pkgExtras.dependencies[name]
114      }
115    }
116    if (Object.keys(pkgExtras.dependencies).length === 0) {
117      delete pkgExtras.dependencies
118    }
119  }
120
121  const stringified = JSON.stringify(pkgExtras, null, 2) + '\n'
122  const msg = util.format('%s:\n\n%s\n', packageFile, stringified)
123  const write = () => fs.writeFile(packageFile, stringified, 'utf8')
124
125  if (yes) {
126    await write()
127    if (!config.get('silent')) {
128      console.log(`Wrote to ${msg}`)
129    }
130    return pkgExtras
131  }
132
133  console.log(`About to write to ${msg}`)
134  const ok = await read({ prompt: 'Is this OK? ', default: 'yes' })
135  if (!ok || !ok.toLowerCase().startsWith('y')) {
136    console.log('Aborted.')
137    return
138  }
139
140  await write()
141  return pkgExtras
142}
143
144module.exports = init
145module.exports.yes = isYes
146