UNPKG

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