UNPKG

5.48 kBJavaScriptView Raw
1'use strict'
2
3const chalk = require('chalk')
4const checkpoint = require('../checkpoint')
5const conventionalRecommendedBump = require('conventional-recommended-bump')
6const detectIndent = require('detect-indent')
7const detectNewline = require('detect-newline')
8const figures = require('figures')
9const fs = require('fs')
10const DotGitignore = require('dotgitignore')
11const path = require('path')
12const presetLoader = require('../preset-loader')
13const runLifecycleScript = require('../run-lifecycle-script')
14const semver = require('semver')
15const stringifyPackage = require('stringify-package')
16const writeFile = require('../write-file')
17
18let configsToUpdate = {}
19
20function Bump (args, version) {
21 // reset the cache of updated config files each
22 // time we perform the version bump step.
23 configsToUpdate = {}
24
25 if (args.skip.bump) return Promise.resolve()
26 let newVersion = version
27 return runLifecycleScript(args, 'prerelease')
28 .then(runLifecycleScript.bind(this, args, 'prebump'))
29 .then((stdout) => {
30 if (stdout && stdout.trim().length) args.releaseAs = stdout.trim()
31 return bumpVersion(args.releaseAs, version, args)
32 })
33 .then((release) => {
34 if (!args.firstRelease) {
35 let releaseType = getReleaseType(args.prerelease, release.releaseType, version)
36 newVersion = semver.valid(releaseType) || semver.inc(version, releaseType, args.prerelease)
37 updateConfigs(args, newVersion)
38 } else {
39 checkpoint(args, 'skip version bump on first release', [], chalk.red(figures.cross))
40 }
41 })
42 .then(() => {
43 return runLifecycleScript(args, 'postbump')
44 })
45 .then(() => {
46 return newVersion
47 })
48}
49
50Bump.getUpdatedConfigs = function () {
51 return configsToUpdate
52}
53
54Bump.pkgFiles = [
55 'package.json',
56 'bower.json',
57 'manifest.json',
58 'composer.json'
59]
60
61Bump.lockFiles = [
62 'package-lock.json',
63 'npm-shrinkwrap.json',
64 'composer.lock'
65]
66
67function getReleaseType (prerelease, expectedReleaseType, currentVersion) {
68 if (isString(prerelease)) {
69 if (isInPrerelease(currentVersion)) {
70 if (shouldContinuePrerelease(currentVersion, expectedReleaseType) ||
71 getTypePriority(getCurrentActiveType(currentVersion)) > getTypePriority(expectedReleaseType)
72 ) {
73 return 'prerelease'
74 }
75 }
76
77 return 'pre' + expectedReleaseType
78 } else {
79 return expectedReleaseType
80 }
81}
82
83function isString (val) {
84 return typeof val === 'string'
85}
86
87/**
88 * if a version is currently in pre-release state,
89 * and if it current in-pre-release type is same as expect type,
90 * it should continue the pre-release with the same type
91 *
92 * @param version
93 * @param expectType
94 * @return {boolean}
95 */
96function shouldContinuePrerelease (version, expectType) {
97 return getCurrentActiveType(version) === expectType
98}
99
100function isInPrerelease (version) {
101 return Array.isArray(semver.prerelease(version))
102}
103
104let TypeList = ['major', 'minor', 'patch'].reverse()
105
106/**
107 * extract the in-pre-release type in target version
108 *
109 * @param version
110 * @return {string}
111 */
112function getCurrentActiveType (version) {
113 let typelist = TypeList
114 for (let i = 0; i < typelist.length; i++) {
115 if (semver[typelist[i]](version)) {
116 return typelist[i]
117 }
118 }
119}
120
121/**
122 * calculate the priority of release type,
123 * major - 2, minor - 1, patch - 0
124 *
125 * @param type
126 * @return {number}
127 */
128function getTypePriority (type) {
129 return TypeList.indexOf(type)
130}
131
132function bumpVersion (releaseAs, currentVersion, args) {
133 return new Promise((resolve, reject) => {
134 if (releaseAs) {
135 return resolve({
136 releaseType: releaseAs
137 })
138 } else {
139 const presetOptions = presetLoader(args)
140 if (typeof presetOptions === 'object') {
141 if (semver.lt(currentVersion, '1.0.0')) presetOptions.preMajor = true
142 }
143 conventionalRecommendedBump({
144 debug: args.verbose && console.info.bind(console, 'conventional-recommended-bump'),
145 preset: presetOptions,
146 path: args.path
147 }, function (err, release) {
148 if (err) return reject(err)
149 else return resolve(release)
150 })
151 }
152 })
153}
154
155/**
156 * attempt to update the version # in a collection of common config
157 * files, e.g., package.json, bower.json.
158 *
159 * @param args config object
160 * @param newVersion version # to update to.
161 * @return {string}
162 */
163function updateConfigs (args, newVersion) {
164 const dotgit = DotGitignore()
165 Bump.pkgFiles.concat(Bump.lockFiles).forEach((filename) => {
166 configsToUpdate[path.resolve(process.cwd(), filename)] = false
167 })
168 Object.keys(configsToUpdate).forEach(function (configPath) {
169 try {
170 if (dotgit.ignore(configPath)) return
171 let stat = fs.lstatSync(configPath)
172 if (stat.isFile()) {
173 let data = fs.readFileSync(configPath, 'utf8')
174 let indent = detectIndent(data).indent
175 let newline = detectNewline(data)
176 let config = JSON.parse(data)
177 let filename = path.basename(configPath)
178 checkpoint(args, 'bumping version in ' + filename + ' from %s to %s', [config.version, newVersion])
179 config.version = newVersion
180 writeFile(args, configPath, stringifyPackage(config, indent, newline))
181 // flag any config files that we modify the version # for
182 // as having been updated.
183 configsToUpdate[configPath] = true
184 }
185 } catch (err) {
186 if (err.code !== 'ENOENT') console.warn(err.message)
187 }
188 })
189}
190
191module.exports = Bump