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