UNPKG

6.96 kBJavaScriptView Raw
1var region = require('caniuse-lite/dist/unpacker/region').default
2var path = require('path')
3var fs = require('fs')
4
5var BrowserslistError = require('./error')
6
7var IS_SECTION = /^\s*\[(.+)\]\s*$/
8var CONFIG_PATTERN = /^browserslist-config-/
9var SCOPED_CONFIG__PATTERN = /@[^./]+\/browserslist-config(-|$)/
10
11var filenessCache = { }
12var configCache = { }
13
14function checkExtend (name) {
15 var use = ' Use `dangerousExtend` option to disable.'
16 if (!CONFIG_PATTERN.test(name) && !SCOPED_CONFIG__PATTERN.test(name)) {
17 throw new BrowserslistError(
18 'Browserslist config needs `browserslist-config-` prefix. ' + use)
19 }
20 if (name.indexOf('.') !== -1) {
21 throw new BrowserslistError(
22 '`.` not allowed in Browserslist config name. ' + use)
23 }
24 if (name.indexOf('node_modules') !== -1) {
25 throw new BrowserslistError(
26 '`node_modules` not allowed in Browserslist config.' + use)
27 }
28}
29
30function isFile (file) {
31 if (file in filenessCache) {
32 return filenessCache[file]
33 }
34 var result = fs.existsSync(file) && fs.statSync(file).isFile()
35 if (!process.env.BROWSERSLIST_DISABLE_CACHE) {
36 filenessCache[file] = result
37 }
38 return result
39}
40
41function eachParent (file, callback) {
42 var loc = path.resolve(file)
43 do {
44 var result = callback(loc)
45 if (typeof result !== 'undefined') return result
46 } while (loc !== (loc = path.dirname(loc)))
47 return undefined
48}
49
50function pickEnv (config, opts) {
51 if (typeof config !== 'object') return config
52
53 var name
54 if (typeof opts.env === 'string') {
55 name = opts.env
56 } else if (process.env.BROWSERSLIST_ENV) {
57 name = process.env.BROWSERSLIST_ENV
58 } else if (process.env.NODE_ENV) {
59 name = process.env.NODE_ENV
60 } else {
61 name = 'production'
62 }
63
64 return config[name] || config.defaults
65}
66
67function parsePackage (file) {
68 var config = JSON.parse(fs.readFileSync(file))
69 if (config.browserlist && !config.browserslist) {
70 throw new BrowserslistError(
71 '`browserlist` key instead of `browserslist` in ' + file)
72 }
73 var list = config.browserslist
74 if (typeof list === 'object' && list.length) {
75 list = { defaults: list }
76 }
77 return list
78}
79
80module.exports = {
81 loadQueries: function loadQueries (context, name) {
82 if (!context.dangerousExtend) checkExtend(name)
83 // eslint-disable-next-line security/detect-non-literal-require
84 var queries = require(name)
85 if (!Array.isArray(queries)) {
86 throw new BrowserslistError(
87 '`' + name + '` config exports not an array of queries')
88 }
89 return queries
90 },
91
92 getStat: function getStat (opts) {
93 var stats
94 if (opts.stats) {
95 stats = opts.stats
96 } else if (process.env.BROWSERSLIST_STATS) {
97 stats = process.env.BROWSERSLIST_STATS
98 } else if (opts.path && path.resolve && fs.existsSync) {
99 stats = eachParent(opts.path, function (dir) {
100 var file = path.join(dir, 'browserslist-stats.json')
101 return isFile(file) ? file : undefined
102 })
103 }
104
105 if (typeof stats === 'string') {
106 try {
107 stats = JSON.parse(fs.readFileSync(stats))
108 } catch (e) {
109 throw new BrowserslistError('Can\'t read ' + stats)
110 }
111 }
112
113 return stats
114 },
115
116 loadConfig: function loadConfig (opts) {
117 if (process.env.BROWSERSLIST) {
118 return process.env.BROWSERSLIST
119 } else if (opts.config || process.env.BROWSERSLIST_CONFIG) {
120 var file = opts.config || process.env.BROWSERSLIST_CONFIG
121 if (path.basename(file) === 'package.json') {
122 return pickEnv(parsePackage(file), opts)
123 } else {
124 return pickEnv(module.exports.readConfig(file), opts)
125 }
126 } else if (opts.path) {
127 return pickEnv(module.exports.findConfig(opts.path), opts)
128 } else {
129 return undefined
130 }
131 },
132
133 loadCountry: function loadCountry (usage, country) {
134 var code = country.replace(/[^\w-]/g, '')
135 if (!usage[code]) {
136 // eslint-disable-next-line security/detect-non-literal-require
137 var compressed = require('caniuse-lite/data/regions/' + code + '.js')
138 var data = region(compressed)
139 usage[country] = { }
140 for (var i in data) {
141 for (var j in data[i]) {
142 usage[country][i + ' ' + j] = data[i][j]
143 }
144 }
145 }
146 },
147
148 parseConfig: function parseConfig (string) {
149 var result = { defaults: [] }
150 var sections = ['defaults']
151
152 string.toString()
153 .replace(/#[^\n]*/g, '')
154 .split(/\n/)
155 .map(function (line) {
156 return line.trim()
157 })
158 .filter(function (line) {
159 return line !== ''
160 })
161 .forEach(function (line) {
162 if (IS_SECTION.test(line)) {
163 sections = line.match(IS_SECTION)[1].trim().split(' ')
164 sections.forEach(function (section) {
165 if (result[section]) {
166 throw new BrowserslistError(
167 'Dublicate section ' + section + ' in Browserslist config')
168 }
169 result[section] = []
170 })
171 } else {
172 sections.forEach(function (section) {
173 result[section].push(line)
174 })
175 }
176 })
177
178 return result
179 },
180
181 readConfig: function readConfig (file) {
182 if (!isFile(file)) {
183 throw new BrowserslistError('Can\'t read ' + file + ' config')
184 }
185 return module.exports.parseConfig(fs.readFileSync(file))
186 },
187
188 findConfig: function findConfig (from) {
189 from = path.resolve(from)
190
191 var cacheKey = isFile(from) ? path.dirname(from) : from
192 if (cacheKey in configCache) {
193 return configCache[cacheKey]
194 }
195
196 var resolved = eachParent(from, function (dir) {
197 var config = path.join(dir, 'browserslist')
198 var pkg = path.join(dir, 'package.json')
199 var rc = path.join(dir, '.browserslistrc')
200
201 var pkgBrowserslist
202 if (isFile(pkg)) {
203 try {
204 pkgBrowserslist = parsePackage(pkg)
205 } catch (e) {
206 if (e.name === 'BrowserslistError') throw e
207 console.warn(
208 '[Browserslist] Could not parse ' + pkg + '. Ignoring it.')
209 }
210 }
211
212 if (isFile(config) && pkgBrowserslist) {
213 throw new BrowserslistError(
214 dir + ' contains both browserslist and package.json with browsers')
215 } else if (isFile(rc) && pkgBrowserslist) {
216 throw new BrowserslistError(
217 dir + ' contains both .browserslistrc and package.json with browsers')
218 } else if (isFile(config) && isFile(rc)) {
219 throw new BrowserslistError(
220 dir + ' contains both .browserslistrc and browserslist')
221 } else if (isFile(config)) {
222 return module.exports.readConfig(config)
223 } else if (isFile(rc)) {
224 return module.exports.readConfig(rc)
225 } else {
226 return pkgBrowserslist
227 }
228 })
229 if (!process.env.BROWSERSLIST_DISABLE_CACHE) {
230 configCache[cacheKey] = resolved
231 }
232 return resolved
233 },
234
235 clearCaches: function clearCaches () {
236 filenessCache = { }
237 configCache = { }
238 }
239}