source: node_modules/browserslist/node.js@ 2058e5c

Last change on this file since 2058e5c was 2058e5c, checked in by istevanoska <ilinastevanoska@…>, 6 months ago

Working / before login

  • Property mode set to 100644
File size: 13.2 KB
Line 
1var feature = require('caniuse-lite/dist/unpacker/feature').default
2var region = require('caniuse-lite/dist/unpacker/region').default
3var fs = require('fs')
4var path = require('path')
5
6var BrowserslistError = require('./error')
7
8var IS_SECTION = /^\s*\[(.+)]\s*$/
9var CONFIG_PATTERN = /^browserslist-config-/
10var SCOPED_CONFIG__PATTERN = /@[^/]+(?:\/[^/]+)?\/browserslist-config(?:-|$|\/)/
11var FORMAT =
12 'Browserslist config should be a string or an array ' +
13 'of strings with browser queries'
14var PATHTYPE_UNKNOWN = 'unknown'
15var PATHTYPE_DIR = 'directory'
16var PATHTYPE_FILE = 'file'
17
18var dataTimeChecked = false
19var statCache = {}
20var configPathCache = {}
21var parseConfigCache = {}
22
23function checkExtend(name) {
24 var use = ' Use `dangerousExtend` option to disable.'
25 if (!CONFIG_PATTERN.test(name) && !SCOPED_CONFIG__PATTERN.test(name)) {
26 throw new BrowserslistError(
27 'Browserslist config needs `browserslist-config-` prefix. ' + use
28 )
29 }
30 if (name.replace(/^@[^/]+\//, '').indexOf('.') !== -1) {
31 throw new BrowserslistError(
32 '`.` not allowed in Browserslist config name. ' + use
33 )
34 }
35 if (name.indexOf('node_modules') !== -1) {
36 throw new BrowserslistError(
37 '`node_modules` not allowed in Browserslist config.' + use
38 )
39 }
40}
41
42function getPathType(filepath) {
43 var stats
44 try {
45 stats = fs.existsSync(filepath) && fs.statSync(filepath)
46 } catch (err) {
47 /* c8 ignore start */
48 if (
49 err.code !== 'ENOENT' &&
50 err.code !== 'EACCES' &&
51 err.code !== 'ERR_ACCESS_DENIED'
52 ) {
53 throw err
54 }
55 /* c8 ignore end */
56 }
57
58 if (stats && stats.isDirectory()) return PATHTYPE_DIR
59 if (stats && stats.isFile()) return PATHTYPE_FILE
60
61 return PATHTYPE_UNKNOWN
62}
63
64function isFile(file) {
65 return getPathType(file) === PATHTYPE_FILE
66}
67
68function isDirectory(dir) {
69 return getPathType(dir) === PATHTYPE_DIR
70}
71
72function eachParent(file, callback, cache) {
73 var loc = path.resolve(file)
74 var pathsForCacheResult = []
75 var result
76 do {
77 if (!pathInRoot(loc)) {
78 break
79 }
80 if (cache && loc in cache) {
81 result = cache[loc]
82 break
83 }
84 pathsForCacheResult.push(loc)
85
86 if (!isDirectory(loc)) {
87 continue
88 }
89
90 var locResult = callback(loc)
91 if (typeof locResult !== 'undefined') {
92 result = locResult
93 break
94 }
95 } while (loc !== (loc = path.dirname(loc)))
96
97 if (cache && !process.env.BROWSERSLIST_DISABLE_CACHE) {
98 pathsForCacheResult.forEach(function (cachePath) {
99 cache[cachePath] = result
100 })
101 }
102 return result
103}
104
105function pathInRoot(p) {
106 if (!process.env.BROWSERSLIST_ROOT_PATH) return true
107 var rootPath = path.resolve(process.env.BROWSERSLIST_ROOT_PATH)
108 if (path.relative(rootPath, p).substring(0, 2) === '..') {
109 return false
110 }
111 return true
112}
113
114function check(section) {
115 if (Array.isArray(section)) {
116 for (var i = 0; i < section.length; i++) {
117 if (typeof section[i] !== 'string') {
118 throw new BrowserslistError(FORMAT)
119 }
120 }
121 } else if (typeof section !== 'string') {
122 throw new BrowserslistError(FORMAT)
123 }
124}
125
126function pickEnv(config, opts) {
127 if (typeof config !== 'object') return config
128
129 var name
130 if (typeof opts.env === 'string') {
131 name = opts.env
132 } else if (process.env.BROWSERSLIST_ENV) {
133 name = process.env.BROWSERSLIST_ENV
134 } else if (process.env.NODE_ENV) {
135 name = process.env.NODE_ENV
136 } else {
137 name = 'production'
138 }
139
140 if (opts.throwOnMissing) {
141 if (name && name !== 'defaults' && !config[name]) {
142 throw new BrowserslistError(
143 'Missing config for Browserslist environment `' + name + '`'
144 )
145 }
146 }
147
148 return config[name] || config.defaults
149}
150
151function parsePackage(file) {
152 var text = fs
153 .readFileSync(file)
154 .toString()
155 .replace(/^\uFEFF/m, '')
156 var list
157 if (text.indexOf('"browserslist"') >= 0) {
158 list = JSON.parse(text).browserslist
159 } else if (text.indexOf('"browserlist"') >= 0) {
160 var config = JSON.parse(text)
161 if (config.browserlist && !config.browserslist) {
162 throw new BrowserslistError(
163 '`browserlist` key instead of `browserslist` in ' + file
164 )
165 }
166 }
167 if (Array.isArray(list) || typeof list === 'string') {
168 list = { defaults: list }
169 }
170 for (var i in list) {
171 check(list[i])
172 }
173
174 return list
175}
176
177function parsePackageOrReadConfig(file) {
178 if (file in parseConfigCache) {
179 return parseConfigCache[file]
180 }
181
182 var isPackage = path.basename(file) === 'package.json'
183 var result = isPackage ? parsePackage(file) : module.exports.readConfig(file)
184
185 if (!process.env.BROWSERSLIST_DISABLE_CACHE) {
186 parseConfigCache[file] = result
187 }
188 return result
189}
190
191function latestReleaseTime(agents) {
192 var latest = 0
193 for (var name in agents) {
194 var dates = agents[name].releaseDate || {}
195 for (var key in dates) {
196 if (latest < dates[key]) {
197 latest = dates[key]
198 }
199 }
200 }
201 return latest * 1000
202}
203
204function getMonthsPassed(date) {
205 var now = new Date()
206 var past = new Date(date)
207
208 var years = now.getFullYear() - past.getFullYear()
209 var months = now.getMonth() - past.getMonth()
210
211 return years * 12 + months
212}
213
214function normalizeStats(data, stats) {
215 if (!data) {
216 data = {}
217 }
218 if (stats && 'dataByBrowser' in stats) {
219 stats = stats.dataByBrowser
220 }
221
222 if (typeof stats !== 'object') return undefined
223
224 var normalized = {}
225 for (var i in stats) {
226 var versions = Object.keys(stats[i])
227 if (versions.length === 1 && data[i] && data[i].versions.length === 1) {
228 var normal = data[i].versions[0]
229 normalized[i] = {}
230 normalized[i][normal] = stats[i][versions[0]]
231 } else {
232 normalized[i] = stats[i]
233 }
234 }
235
236 return normalized
237}
238
239function normalizeUsageData(usageData, data) {
240 for (var browser in usageData) {
241 var browserUsage = usageData[browser]
242 // https://github.com/browserslist/browserslist/issues/431#issuecomment-565230615
243 // caniuse-db returns { 0: "percentage" } for `and_*` regional stats
244 if ('0' in browserUsage) {
245 var versions = data[browser].versions
246 browserUsage[versions[versions.length - 1]] = browserUsage[0]
247 delete browserUsage[0]
248 }
249 }
250}
251
252module.exports = {
253 loadQueries: function loadQueries(ctx, name) {
254 if (!ctx.dangerousExtend && !process.env.BROWSERSLIST_DANGEROUS_EXTEND) {
255 checkExtend(name)
256 }
257 var queries = require(require.resolve(name, { paths: ['.', ctx.path] }))
258 if (typeof queries === 'object' && queries !== null && queries.__esModule) {
259 queries = queries.default
260 }
261 if (queries) {
262 if (Array.isArray(queries)) {
263 return queries
264 } else if (typeof queries === 'object') {
265 if (!queries.defaults) queries.defaults = []
266 return pickEnv(queries, ctx, name)
267 }
268 }
269 throw new BrowserslistError(
270 '`' +
271 name +
272 '` config exports not an array of queries' +
273 ' or an object of envs'
274 )
275 },
276
277 loadStat: function loadStat(ctx, name, data) {
278 if (!ctx.dangerousExtend && !process.env.BROWSERSLIST_DANGEROUS_EXTEND) {
279 checkExtend(name)
280 }
281 var stats = require(
282 // Use forward slashes for module paths, also on Windows.
283 require.resolve(path.posix.join(name, 'browserslist-stats.json'), {
284 paths: ['.']
285 })
286 )
287 return normalizeStats(data, stats)
288 },
289
290 getStat: function getStat(opts, data) {
291 var stats
292 if (opts.stats) {
293 stats = opts.stats
294 } else if (process.env.BROWSERSLIST_STATS) {
295 stats = process.env.BROWSERSLIST_STATS
296 } else if (opts.path && path.resolve && fs.existsSync) {
297 stats = eachParent(
298 opts.path,
299 function (dir) {
300 var file = path.join(dir, 'browserslist-stats.json')
301 return isFile(file) ? file : undefined
302 },
303 statCache
304 )
305 }
306 if (typeof stats === 'string') {
307 try {
308 stats = JSON.parse(fs.readFileSync(stats))
309 } catch (e) {
310 throw new BrowserslistError("Can't read " + stats)
311 }
312 }
313 return normalizeStats(data, stats)
314 },
315
316 loadConfig: function loadConfig(opts) {
317 if (process.env.BROWSERSLIST) {
318 return process.env.BROWSERSLIST
319 } else if (opts.config || process.env.BROWSERSLIST_CONFIG) {
320 var file = opts.config || process.env.BROWSERSLIST_CONFIG
321 return pickEnv(parsePackageOrReadConfig(file), opts)
322 } else if (opts.path) {
323 return pickEnv(module.exports.findConfig(opts.path), opts)
324 } else {
325 return undefined
326 }
327 },
328
329 loadCountry: function loadCountry(usage, country, data) {
330 var code = country.replace(/[^\w-]/g, '')
331 if (!usage[code]) {
332 var compressed
333 try {
334 compressed = require('caniuse-lite/data/regions/' + code + '.js')
335 } catch (e) {
336 throw new BrowserslistError('Unknown region name `' + code + '`.')
337 }
338 var usageData = region(compressed)
339 normalizeUsageData(usageData, data)
340 usage[country] = {}
341 for (var i in usageData) {
342 for (var j in usageData[i]) {
343 usage[country][i + ' ' + j] = usageData[i][j]
344 }
345 }
346 }
347 },
348
349 loadFeature: function loadFeature(features, name) {
350 name = name.replace(/[^\w-]/g, '')
351 if (features[name]) return
352 var compressed
353 try {
354 compressed = require('caniuse-lite/data/features/' + name + '.js')
355 } catch (e) {
356 throw new BrowserslistError('Unknown feature name `' + name + '`.')
357 }
358 var stats = feature(compressed).stats
359 features[name] = {}
360 for (var i in stats) {
361 features[name][i] = {}
362 for (var j in stats[i]) {
363 features[name][i][j] = stats[i][j]
364 }
365 }
366 },
367
368 parseConfig: function parseConfig(string) {
369 var result = { defaults: [] }
370 var sections = ['defaults']
371
372 string
373 .toString()
374 .replace(/#[^\n]*/g, '')
375 .split(/\n|,/)
376 .map(function (line) {
377 return line.trim()
378 })
379 .filter(function (line) {
380 return line !== ''
381 })
382 .forEach(function (line) {
383 if (IS_SECTION.test(line)) {
384 sections = line.match(IS_SECTION)[1].trim().split(' ')
385 sections.forEach(function (section) {
386 if (result[section]) {
387 throw new BrowserslistError(
388 'Duplicate section ' + section + ' in Browserslist config'
389 )
390 }
391 result[section] = []
392 })
393 } else {
394 sections.forEach(function (section) {
395 result[section].push(line)
396 })
397 }
398 })
399
400 return result
401 },
402
403 readConfig: function readConfig(file) {
404 if (!isFile(file)) {
405 throw new BrowserslistError("Can't read " + file + ' config')
406 }
407
408 return module.exports.parseConfig(fs.readFileSync(file))
409 },
410
411 findConfigFile: function findConfigFile(from) {
412 return eachParent(
413 from,
414 function (dir) {
415 var config = path.join(dir, 'browserslist')
416 var pkg = path.join(dir, 'package.json')
417 var rc = path.join(dir, '.browserslistrc')
418
419 var pkgBrowserslist
420 if (isFile(pkg)) {
421 try {
422 pkgBrowserslist = parsePackage(pkg)
423 } catch (e) {
424 if (e.name === 'BrowserslistError') throw e
425 console.warn(
426 '[Browserslist] Could not parse ' + pkg + '. Ignoring it.'
427 )
428 }
429 }
430
431 if (isFile(config) && pkgBrowserslist) {
432 throw new BrowserslistError(
433 dir + ' contains both browserslist and package.json with browsers'
434 )
435 } else if (isFile(rc) && pkgBrowserslist) {
436 throw new BrowserslistError(
437 dir +
438 ' contains both .browserslistrc and package.json with browsers'
439 )
440 } else if (isFile(config) && isFile(rc)) {
441 throw new BrowserslistError(
442 dir + ' contains both .browserslistrc and browserslist'
443 )
444 } else if (isFile(config)) {
445 return config
446 } else if (isFile(rc)) {
447 return rc
448 } else if (pkgBrowserslist) {
449 return pkg
450 }
451 },
452 configPathCache
453 )
454 },
455
456 findConfig: function findConfig(from) {
457 var configFile = this.findConfigFile(from)
458
459 return configFile ? parsePackageOrReadConfig(configFile) : undefined
460 },
461
462 clearCaches: function clearCaches() {
463 dataTimeChecked = false
464 statCache = {}
465 configPathCache = {}
466 parseConfigCache = {}
467
468 this.cache = {}
469 },
470
471 oldDataWarning: function oldDataWarning(agentsObj) {
472 if (dataTimeChecked) return
473 dataTimeChecked = true
474 if (process.env.BROWSERSLIST_IGNORE_OLD_DATA) return
475
476 var latest = latestReleaseTime(agentsObj)
477 var monthsPassed = getMonthsPassed(latest)
478
479 if (latest !== 0 && monthsPassed >= 6) {
480 if (process.env.BROWSERSLIST_TRACE_WARNING) {
481 console.info('Last browser release in DB: ' + String(new Date(latest)))
482 console.trace()
483 }
484
485 var months = monthsPassed + ' ' + (monthsPassed > 1 ? 'months' : 'month')
486 console.warn(
487 'Browserslist: browsers data (caniuse-lite) is ' +
488 months +
489 ' old. Please run:\n' +
490 ' npx update-browserslist-db@latest\n' +
491 ' Why you should do it regularly: ' +
492 'https://github.com/browserslist/update-db#readme'
493 )
494 }
495 },
496
497 currentNode: function currentNode() {
498 return 'node ' + process.versions.node
499 },
500
501 env: process.env
502}
Note: See TracBrowser for help on using the repository browser.