UNPKG

9.59 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.execSync('npx browserslist').toString()
62 .trim()
63 .split('\n')
64 .map(function (line) {
65 return line.trim().split(' ')
66 })
67 .reduce(function (result, entry) {
68 if (!result[entry[0]]) {
69 result[entry[0]] = []
70 }
71 result[entry[0]].push(entry[1])
72 return result
73 }, {})
74}
75
76function diffBrowsersLists (old, current) {
77 var browsers = Object.keys(old).concat(
78 Object.keys(current).filter(function (browser) {
79 return old[browser] === undefined
80 })
81 )
82 return browsers.map(function (browser) {
83 var oldVersions = old[browser] || []
84 var currentVersions = current[browser] || []
85 var intersection = oldVersions.filter(function (version) {
86 return currentVersions.indexOf(version) !== -1
87 })
88 var addedVersions = currentVersions.filter(function (version) {
89 return intersection.indexOf(version) === -1
90 })
91 var removedVersions = oldVersions.filter(function (version) {
92 return intersection.indexOf(version) === -1
93 })
94 return removedVersions.map(function (version) {
95 return pico.red('- ' + browser + ' ' + version)
96 }).concat(addedVersions.map(function (version) {
97 return pico.green('+ ' + browser + ' ' + version)
98 }))
99 })
100 .reduce(function (result, array) {
101 return result.concat(array)
102 }, [])
103 .join('\n')
104}
105
106function updateNpmLockfile (lock, latest) {
107 var metadata = { latest: latest, versions: [] }
108 var content = deletePackage(JSON.parse(lock.content), metadata)
109 metadata.content = JSON.stringify(content, null, ' ')
110 return metadata
111}
112
113function deletePackage (node, metadata) {
114 if (node.dependencies) {
115 if (node.dependencies['caniuse-lite']) {
116 var version = node.dependencies['caniuse-lite'].version
117 metadata.versions[version] = true
118 delete node.dependencies['caniuse-lite']
119 }
120 for (var i in node.dependencies) {
121 node.dependencies[i] = deletePackage(node.dependencies[i], metadata)
122 }
123 }
124 return node
125}
126
127var yarnVersionRe = new RegExp('version "(.*?)"')
128
129function updateYarnLockfile (lock, latest) {
130 var blocks = lock.content.split(/(\n{2,})/).map(function (block) {
131 return block.split('\n')
132 })
133 var versions = {}
134 blocks.forEach(function (lines) {
135 if (lines[0].indexOf('caniuse-lite@') !== -1) {
136 var match = yarnVersionRe.exec(lines[1])
137 versions[match[1]] = true
138 if (match[1] !== latest.version) {
139 lines[1] = lines[1].replace(
140 /version "[^"]+"/, 'version "' + latest.version + '"'
141 )
142 lines[2] = lines[2].replace(
143 /resolved "[^"]+"/, 'resolved "' + latest.dist.tarball + '"'
144 )
145 lines[3] = latest.dist.integrity ? lines[3].replace(
146 /integrity .+/, 'integrity ' + latest.dist.integrity
147 ) : ''
148 }
149 }
150 })
151 var content = blocks.map(function (lines) {
152 return lines.join('\n')
153 }).join('')
154 return { content: content, versions: versions }
155}
156
157function updatePnpmLockfile (lock, latest) {
158 var versions = {}
159 var lines = lock.content.split('\n')
160 var i
161 var j
162 var lineParts
163
164 for (i = 0; i < lines.length; i++) {
165 if (lines[i].indexOf('caniuse-lite:') >= 0) {
166 lineParts = lines[i].split(/:\s?/, 2)
167 versions[lineParts[1]] = true
168 lines[i] = lineParts[0] + ': ' + latest.version
169 } else if (lines[i].indexOf('/caniuse-lite') >= 0) {
170 lineParts = lines[i].split(/([/:])/)
171 for (j = 0; j < lineParts.length; j++) {
172 if (lineParts[j].indexOf('caniuse-lite') >= 0) {
173 versions[lineParts[j + 2]] = true
174 lineParts[j + 2] = latest.version
175 break
176 }
177 }
178 lines[i] = lineParts.join('')
179 for (i = i + 1; i < lines.length; i++) {
180 if (lines[i].indexOf('integrity: ') !== -1) {
181 lines[i] = lines[i].replace(
182 /integrity: .+/, 'integrity: ' + latest.dist.integrity
183 )
184 } else if (lines[i].indexOf(' /') !== -1) {
185 break
186 }
187 }
188 }
189 }
190 return { content: lines.join('\n'), versions: versions }
191}
192
193function updateLockfile (lock, latest) {
194 if (!lock.content) lock.content = fs.readFileSync(lock.file).toString()
195
196 if (lock.mode === 'npm') {
197 return updateNpmLockfile(lock, latest)
198 } else if (lock.mode === 'yarn') {
199 return updateYarnLockfile(lock, latest)
200 }
201 return updatePnpmLockfile(lock, latest)
202}
203
204function updatePackageManually (print, lock, latest) {
205 var lockfileData = updateLockfile(lock, latest)
206 var caniuseVersions = Object.keys(lockfileData.versions).sort()
207 if (caniuseVersions.length === 1 &&
208 caniuseVersions[0] === latest.version) {
209 print(
210 'Installed version: ' + pico.bold(pico.green(latest.version)) + '\n' +
211 pico.bold(pico.green('caniuse-lite is up to date')) + '\n'
212 )
213 return
214 }
215
216 if (caniuseVersions.length === 0) {
217 caniuseVersions[0] = 'none'
218 }
219 print(
220 'Installed version' +
221 (caniuseVersions.length === 1 ? ': ' : 's: ') +
222 pico.bold(pico.red(caniuseVersions.join(', '))) +
223 '\n' +
224 'Removing old caniuse-lite from lock file\n'
225 )
226 fs.writeFileSync(lock.file, lockfileData.content)
227
228 var install = lock.mode === 'yarn' ? 'yarn add -W' : lock.mode + ' install'
229 print(
230 'Installing new caniuse-lite version\n' +
231 pico.yellow('$ ' + install + ' caniuse-lite') + '\n'
232 )
233 try {
234 childProcess.execSync(install + ' caniuse-lite')
235 } catch (e) /* istanbul ignore next */ {
236 print(
237 pico.red(
238 '\n' +
239 e.stack + '\n\n' +
240 'Problem with `' + install + ' caniuse-lite` call. ' +
241 'Run it manually.\n'
242 )
243 )
244 process.exit(1)
245 }
246
247 var del = lock.mode === 'yarn' ? 'yarn remove -W' : lock.mode + ' uninstall'
248 print(
249 'Cleaning package.json dependencies from caniuse-lite\n' +
250 pico.yellow('$ ' + del + ' caniuse-lite') + '\n'
251 )
252 childProcess.execSync(del + ' caniuse-lite')
253}
254
255module.exports = function updateDB (print) {
256 var lock = detectLockfile()
257 var latest = getLatestInfo(lock)
258
259 var browsersListRetrievalError
260 var oldBrowsersList
261 try {
262 oldBrowsersList = getBrowsersList()
263 } catch (e) {
264 browsersListRetrievalError = e
265 }
266
267 print(
268 'Latest version: ' + pico.bold(pico.green(latest.version)) + '\n'
269 )
270
271 if (lock.mode === 'yarn' && lock.version !== 1) {
272 var update = 'yarn up -R'
273 print(
274 'Updating caniuse-lite version\n' +
275 pico.yellow('$ ' + update + ' caniuse-lite') + '\n'
276 )
277 try {
278 childProcess.execSync(update + ' caniuse-lite')
279 } catch (e) /* istanbul ignore next */ {
280 print(
281 pico.red(
282 '\n' +
283 e.stack + '\n\n' +
284 'Problem with `' + update + ' caniuse-lite` call. ' +
285 'Run it manually.\n'
286 )
287 )
288 process.exit(1)
289 }
290 } else {
291 updatePackageManually(print, lock, latest)
292 }
293
294 print('caniuse-lite has been successfully updated\n')
295
296 var currentBrowsersList
297 if (!browsersListRetrievalError) {
298 try {
299 currentBrowsersList = getBrowsersList()
300 } catch (e) /* istanbul ignore next */ {
301 browsersListRetrievalError = e
302 }
303 }
304
305 if (browsersListRetrievalError) {
306 print(
307 pico.red(
308 '\n' +
309 browsersListRetrievalError.stack + '\n\n' +
310 'Problem with browser list retrieval.\n' +
311 'Target browser changes won’t be shown.\n'
312 )
313 )
314 } else {
315 var targetBrowserChanges = diffBrowsersLists(
316 oldBrowsersList,
317 currentBrowsersList
318 )
319 if (targetBrowserChanges) {
320 print('\nTarget browser changes:\n')
321 print(targetBrowserChanges + '\n')
322 } else {
323 print('\n' + pico.green('No target browser changes') + '\n')
324 }
325 }
326}