马宇豪
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
 
const promzard = require('promzard')
const path = require('path')
const semver = require('semver')
const { read } = require('read')
const util = require('util')
const PackageJson = require('@npmcli/package-json')
 
const def = require.resolve('./default-input.js')
 
const extras = [
  'bundleDependencies',
  'gypfile',
  'serverjs',
  'scriptpath',
  'readme',
  'bin',
  'githead',
  'fillTypes',
  'normalizeData',
]
 
const isYes = (c) => !!(c.get('yes') || c.get('y') || c.get('force') || c.get('f'))
 
const getConfig = (c) => {
  // accept either a plain-jane object, or a config object with a "get" method.
  if (typeof c.get !== 'function') {
    const data = c
    return {
      get: (k) => data[k],
      toJSON: () => data,
    }
  }
  return c
}
 
// Coverage disabled because this is just walking back the fixPeople
// normalization from the normalizeData step and we don't need to re-test all
// of those paths.
/* istanbul ignore next */
const stringifyPerson = (p) => {
  const { name, url, web, email, mail } = p
  const u = url || web
  const e = email || mail
  return `${name}${e ? ` <${e}>` : ''}${u ? ` (${u})` : ''}`
}
async function init (dir,
  // TODO test for non-default definitions
  /* istanbul ignore next */
  input = def,
  c = {}) {
  const config = getConfig(c)
  const yes = isYes(config)
  const packageFile = path.resolve(dir, 'package.json')
 
  // read what's already there to inform our prompts
  const pkg = await PackageJson.load(dir, { create: true })
  await pkg.normalize()
 
  if (!semver.valid(pkg.content.version)) {
    delete pkg.content.version
  }
 
  // make sure that the input is valid. if not, use the default
  const pzData = await promzard(path.resolve(input), {
    yes,
    config,
    filename: packageFile,
    dirname: dir,
    basename: path.basename(dir),
    package: pkg.content,
  }, { backupFile: def })
 
  for (const [k, v] of Object.entries(pzData)) {
    if (v != null) {
      pkg.content[k] = v
    }
  }
 
  await pkg.normalize({ steps: extras })
 
  // turn the objects back into somewhat more humane strings.
  // "normalizeData" does this and there isn't a way to choose which of those steps happen
  if (pkg.content.author) {
    pkg.content.author = stringifyPerson(pkg.content.author)
  }
 
  // no need for the readme now.
  delete pkg.content.readme
  delete pkg.content.readmeFilename
 
  // really don't want to have this lying around in the file
  delete pkg.content._id
 
  // ditto
  delete pkg.content.gitHead
 
  // if the repo is empty, remove it.
  if (!pkg.content.repository) {
    delete pkg.content.repository
  }
 
  // readJson filters out empty descriptions, but init-package-json
  // traditionally leaves them alone
  if (!pkg.content.description) {
    pkg.content.description = pzData.description
  }
 
  // optionalDependencies don't need to be repeated in two places
  if (pkg.content.dependencies) {
    if (pkg.content.optionalDependencies) {
      for (const name of Object.keys(pkg.content.optionalDependencies)) {
        delete pkg.content.dependencies[name]
      }
    }
    if (Object.keys(pkg.content.dependencies).length === 0) {
      delete pkg.content.dependencies
    }
  }
 
  const stringified = JSON.stringify(pkg.content, null, 2) + '\n'
  const msg = util.format('%s:\n\n%s\n', packageFile, stringified)
 
  if (yes) {
    await pkg.save()
    if (!config.get('silent')) {
      // eslint-disable-next-line no-console
      console.log(`Wrote to ${msg}`)
    }
    return pkg.content
  }
 
  // eslint-disable-next-line no-console
  console.log(`About to write to ${msg}`)
  const ok = await read({ prompt: 'Is this OK? ', default: 'yes' })
  if (!ok || !ok.toLowerCase().startsWith('y')) {
    // eslint-disable-next-line no-console
    console.log('Aborted.')
    return
  }
 
  await pkg.save()
  return pkg.content
}
 
module.exports = init
module.exports.yes = isYes