UNPKG

8.44 kBJavaScriptView Raw
1var childProcess = require('child_process')
2var colorette = require('colorette')
3var escalade = require('escalade/sync')
4var path = require('path')
5var fs = require('fs')
6
7var BrowserslistError = require('./error')
8
9var red = colorette.red
10var bold = colorette.bold
11var green = colorette.green
12var yellow = colorette.yellow
13
14function detectLockfile () {
15 var packageDir = escalade('.', function (dir, names) {
16 return names.indexOf('package.json') !== -1 ? dir : ''
17 })
18
19 if (!packageDir) {
20 throw new BrowserslistError(
21 'Cannot find package.json. ' +
22 'Is this the right directory to run `npx browserslist --update-db` in?'
23 )
24 }
25
26 var lockfileNpm = path.join(packageDir, 'package-lock.json')
27 var lockfileYarn = path.join(packageDir, 'yarn.lock')
28 var lockfilePnpm = path.join(packageDir, 'pnpm-lock.yaml')
29
30 if (fs.existsSync(lockfilePnpm)) {
31 return { mode: 'pnpm', file: lockfilePnpm }
32 } else if (fs.existsSync(lockfileNpm)) {
33 return { mode: 'npm', file: lockfileNpm }
34 } else if (fs.existsSync(lockfileYarn)) {
35 return { mode: 'yarn', file: lockfileYarn }
36 }
37 throw new BrowserslistError(
38 'No lockfile found. Run "npm install", "yarn install" or "pnpm install"'
39 )
40}
41
42function getLatestInfo (lock) {
43 if (lock.mode === 'yarn') {
44 return JSON.parse(
45 childProcess.execSync('yarn info caniuse-lite --json').toString()
46 ).data
47 }
48 return JSON.parse(
49 childProcess.execSync('npm show caniuse-lite --json').toString()
50 )
51}
52
53function getBrowsersList () {
54 return childProcess.execSync('npx browserslist').toString()
55 .trim()
56 .split('\n')
57 .map(function (line) {
58 return line.trim().split(' ')
59 })
60 .reduce(function (result, entry) {
61 if (!result[entry[0]]) {
62 result[entry[0]] = []
63 }
64 result[entry[0]].push(entry[1])
65 return result
66 }, {})
67}
68
69function diffBrowsersLists (old, current) {
70 var browsers = Object.keys(old).concat(
71 Object.keys(current).filter(function (browser) {
72 return old[browser] === undefined
73 })
74 )
75 return browsers.map(function (browser) {
76 var oldVersions = old[browser] || []
77 var currentVersions = current[browser] || []
78 var intersection = oldVersions.filter(function (version) {
79 return currentVersions.indexOf(version) !== -1
80 })
81 var addedVersions = currentVersions.filter(function (version) {
82 return intersection.indexOf(version) === -1
83 })
84 var removedVersions = oldVersions.filter(function (version) {
85 return intersection.indexOf(version) === -1
86 })
87 return removedVersions.map(function (version) {
88 return red('- ' + browser + ' ' + version)
89 }).concat(addedVersions.map(function (version) {
90 return green('+ ' + browser + ' ' + version)
91 }))
92 })
93 .reduce(function (result, array) {
94 return result.concat(array)
95 }, [])
96 .join('\n')
97}
98
99function updateNpmLockfile (lock, latest) {
100 var metadata = { latest: latest, versions: [] }
101 var content = deletePackage(JSON.parse(lock.content), metadata)
102 metadata.content = JSON.stringify(content, null, ' ')
103 return metadata
104}
105
106function deletePackage (node, metadata) {
107 if (node.dependencies) {
108 if (node.dependencies['caniuse-lite']) {
109 var version = node.dependencies['caniuse-lite'].version
110 metadata.versions[version] = true
111 delete node.dependencies['caniuse-lite']
112 }
113 for (var i in node.dependencies) {
114 node.dependencies[i] = deletePackage(node.dependencies[i], metadata)
115 }
116 }
117 return node
118}
119
120var yarnVersionRe = new RegExp('version "(.*?)"')
121
122function updateYarnLockfile (lock, latest) {
123 var blocks = lock.content.split(/(\n{2,})/).map(function (block) {
124 return block.split('\n')
125 })
126 var versions = {}
127 blocks.forEach(function (lines) {
128 if (lines[0].indexOf('caniuse-lite@') !== -1) {
129 var match = yarnVersionRe.exec(lines[1])
130 versions[match[1]] = true
131 if (match[1] !== latest.version) {
132 lines[1] = lines[1].replace(
133 /version "[^"]+"/, 'version "' + latest.version + '"'
134 )
135 lines[2] = lines[2].replace(
136 /resolved "[^"]+"/, 'resolved "' + latest.dist.tarball + '"'
137 )
138 lines[3] = lines[3].replace(
139 /integrity .+/, 'integrity ' + latest.dist.integrity
140 )
141 }
142 }
143 })
144 var content = blocks.map(function (lines) {
145 return lines.join('\n')
146 }).join('')
147 return { content: content, versions: versions }
148}
149
150function updatePnpmLockfile (lock, latest) {
151 var versions = {}
152 var lines = lock.content.split('\n')
153 var i
154 var j
155 var lineParts
156
157 for (i = 0; i < lines.length; i++) {
158 if (lines[i].indexOf('caniuse-lite:') >= 0) {
159 lineParts = lines[i].split(/:\s?/, 2)
160 versions[lineParts[1]] = true
161 lines[i] = lineParts[0] + ': ' + latest.version
162 } else if (lines[i].indexOf('/caniuse-lite') >= 0) {
163 lineParts = lines[i].split(/([/:])/)
164 for (j = 0; j < lineParts.length; j++) {
165 if (lineParts[j].indexOf('caniuse-lite') >= 0) {
166 versions[lineParts[j + 2]] = true
167 lineParts[j + 2] = latest.version
168 break
169 }
170 }
171 lines[i] = lineParts.join('')
172 for (i = i + 1; i < lines.length; i++) {
173 if (lines[i].indexOf('integrity: ') !== -1) {
174 lines[i] = lines[i].replace(
175 /integrity: .+/, 'integrity: ' + latest.dist.integrity
176 )
177 } else if (lines[i].indexOf(' /') !== -1) {
178 break
179 }
180 }
181 }
182 }
183 return { content: lines.join('\n'), versions: versions }
184}
185
186function updateLockfile (lock, latest) {
187 lock.content = fs.readFileSync(lock.file).toString()
188
189 if (lock.mode === 'npm') {
190 return updateNpmLockfile(lock, latest)
191 } else if (lock.mode === 'yarn') {
192 return updateYarnLockfile(lock, latest)
193 }
194 return updatePnpmLockfile(lock, latest)
195}
196
197module.exports = function updateDB (print) {
198 var lock = detectLockfile()
199 var latest = getLatestInfo(lock)
200 var browsersListRetrievalError
201 var oldBrowsersList
202 try {
203 oldBrowsersList = getBrowsersList()
204 } catch (e) {
205 browsersListRetrievalError = e
206 }
207
208 print(
209 'Latest version: ' + bold(green(latest.version)) + '\n'
210 )
211
212 var lockfileData = updateLockfile(lock, latest)
213 var caniuseVersions = Object.keys(lockfileData.versions).sort()
214 if (caniuseVersions.length === 1 &&
215 caniuseVersions[0] === latest.version) {
216 print(
217 'Installed version: ' + bold(green(latest.version)) + '\n' +
218 bold(green('caniuse-lite is up to date')) + '\n'
219 )
220 return
221 }
222
223 if (caniuseVersions.length === 0) {
224 caniuseVersions[0] = 'none'
225 }
226 print(
227 'Installed version' +
228 (caniuseVersions.length === 1 ? ': ' : 's: ') +
229 bold(red(caniuseVersions.join(', '))) +
230 '\n' +
231 'Removing old caniuse-lite from lock file\n'
232 )
233 fs.writeFileSync(lock.file, lockfileData.content)
234
235 var install = lock.mode === 'yarn' ? 'yarn add -W' : lock.mode + ' install'
236 print(
237 'Installing new caniuse-lite version\n' +
238 yellow('$ ' + install + ' caniuse-lite') + '\n'
239 )
240 try {
241 childProcess.execSync(install + ' caniuse-lite')
242 } catch (e) /* istanbul ignore next */ {
243 print(
244 red(
245 '\n' +
246 e.stack + '\n\n' +
247 'Problem with `' + install + ' caniuse-lite` call. ' +
248 'Run it manually.\n'
249 )
250 )
251 process.exit(1)
252 }
253
254 var del = lock.mode === 'yarn' ? 'yarn remove -W' : lock.mode + ' uninstall'
255 print(
256 'Cleaning package.json dependencies from caniuse-lite\n' +
257 yellow('$ ' + del + ' caniuse-lite') + '\n'
258 )
259 childProcess.execSync(del + ' caniuse-lite')
260
261 print('caniuse-lite has been successfully updated\n')
262
263 var currentBrowsersList
264 if (!browsersListRetrievalError) {
265 try {
266 currentBrowsersList = getBrowsersList()
267 } catch (e) /* istanbul ignore next */ {
268 browsersListRetrievalError = e
269 }
270 }
271
272 if (browsersListRetrievalError) {
273 print(
274 red(
275 '\n' +
276 browsersListRetrievalError.stack + '\n\n' +
277 'Problem with browser list retrieval.\n' +
278 'Target browser changes won’t be shown.\n'
279 )
280 )
281 } else {
282 var targetBrowserChanges = diffBrowsersLists(
283 oldBrowsersList,
284 currentBrowsersList
285 )
286 if (targetBrowserChanges) {
287 print('\nTarget browser changes:\n')
288 print(targetBrowserChanges + '\n')
289 } else {
290 print('\n' + green('No target browser changes') + '\n')
291 }
292 }
293}