Initial commit
[yaffs-website] / node_modules / node-sass / lib / extensions.js
1 /*!
2  * node-sass: lib/extensions.js
3  */
4
5 var eol = require('os').EOL,
6   fs = require('fs'),
7   pkg = require('../package.json'),
8   mkdir = require('mkdirp'),
9   path = require('path'),
10   defaultBinaryPath = path.join(__dirname, '..', 'vendor');
11
12 /**
13  * Get the human readable name of the Platform that is running
14  *
15  * @param  {string} platform - An OS platform to match, or null to fallback to
16  * the current process platform
17  * @return {Object} The name of the platform if matched, false otherwise
18  *
19  * @api public
20  */
21 function getHumanPlatform(platform) {
22   switch (platform || process.platform) {
23     case 'darwin': return 'OS X';
24     case 'freebsd': return 'FreeBSD';
25     case 'linux': return 'Linux';
26     case 'linux_musl': return 'Linux/musl';
27     case 'win32': return 'Windows';
28     default: return false;
29   }
30 }
31
32 /**
33  * Provides a more readable version of the architecture
34  *
35  * @param  {string} arch - An instruction architecture name to match, or null to
36  * lookup the current process architecture
37  * @return {Object} The value of the process architecture, or false if unknown
38  *
39  * @api public
40  */
41 function getHumanArchitecture(arch) {
42   switch (arch || process.arch) {
43     case 'ia32': return '32-bit';
44     case 'x86': return '32-bit';
45     case 'x64': return '64-bit';
46     default: return false;
47   }
48 }
49
50 /**
51  * Get the friendly name of the Node environment being run
52  *
53  * @param  {Object} abi - A Node Application Binary Interface value, or null to
54  * fallback to the current Node ABI
55  * @return {Object} Returns a string name of the Node environment or false if
56  * unmatched
57  *
58  * @api public
59  */
60 function getHumanNodeVersion(abi) {
61   switch (parseInt(abi || process.versions.modules, 10)) {
62     case 11: return 'Node 0.10.x';
63     case 14: return 'Node 0.12.x';
64     case 42: return 'io.js 1.x';
65     case 43: return 'io.js 1.1.x';
66     case 44: return 'io.js 2.x';
67     case 45: return 'io.js 3.x';
68     case 46: return 'Node.js 4.x';
69     case 47: return 'Node.js 5.x';
70     case 48: return 'Node.js 6.x';
71     case 51: return 'Node.js 7.x';
72     default: return false;
73   }
74 }
75
76 /**
77  * Get a human readable description of where node-sass is running to support
78  * user error reporting when something goes wrong
79  *
80  * @param  {string} env - The name of the native bindings that is to be parsed
81  * @return {string} A description of what os, architecture, and Node version
82  * that is being run
83  *
84  * @api public
85  */
86 function getHumanEnvironment(env) {
87   var binding = env.replace(/_binding\.node$/, ''),
88     parts = binding.split('-'),
89     platform = getHumanPlatform(parts[0]),
90     arch = getHumanArchitecture(parts[1]),
91     runtime = getHumanNodeVersion(parts[2]);
92
93   if (parts.length !== 3) {
94     return 'Unknown environment (' + binding + ')';
95   }
96
97   if (!platform) {
98     platform = 'Unsupported platform (' + parts[0] + ')';
99   }
100
101   if (!arch) {
102     arch = 'Unsupported architecture (' + parts[1] + ')';
103   }
104
105   if (!runtime) {
106     runtime = 'Unsupported runtime (' + parts[2] + ')';
107   }
108
109   return [
110     platform, arch, 'with', runtime,
111   ].join(' ');
112 }
113
114 /**
115  * Get the value of the binaries under the default path
116  *
117  * @return {Array} The currently installed node-sass bindings
118  *
119  * @api public
120  */
121 function getInstalledBinaries() {
122   return fs.readdirSync(defaultBinaryPath);
123 }
124
125 /**
126  * Check that an environment matches the whitelisted values or the current
127  * environment if no parameters are passed
128  *
129  * @param  {string} platform - The name of the OS platform(darwin, win32, etc...)
130  * @param  {string} arch - The instruction set architecture of the Node environment
131  * @param  {string} abi - The Node Application Binary Interface
132  * @return {Boolean} True, if node-sass supports the current platform, false otherwise
133  *
134  * @api public
135  */
136 function isSupportedEnvironment(platform, arch, abi) {
137   return (
138     false !== getHumanPlatform(platform) &&
139     false !== getHumanArchitecture(arch) &&
140     false !== getHumanNodeVersion(abi)
141   );
142 }
143
144 /**
145  * Get the value of a CLI argument
146  *
147  * @param {String} name
148  * @param {Array} args
149  * @api private
150  */
151
152 function getArgument(name, args) {
153   var flags = args || process.argv.slice(2),
154     index = flags.lastIndexOf(name);
155
156   if (index === -1 || index + 1 >= flags.length) {
157     return null;
158   }
159
160   return flags[index + 1];
161 }
162
163 /**
164  * Get binary name.
165  * If environment variable SASS_BINARY_NAME,
166  * .npmrc variable sass_binary_name or
167  * process argument --binary-name is provided,
168  * return it as is, otherwise make default binary
169  * name: {platform}-{arch}-{v8 version}.node
170  *
171  * @api public
172  */
173
174 function getBinaryName() {
175   var binaryName,
176     variant,
177     platform = process.platform;
178
179   if (getArgument('--sass-binary-name')) {
180     binaryName = getArgument('--sass-binary-name');
181   } else if (process.env.SASS_BINARY_NAME) {
182     binaryName = process.env.SASS_BINARY_NAME;
183   } else if (process.env.npm_config_sass_binary_name) {
184     binaryName = process.env.npm_config_sass_binary_name;
185   } else if (pkg.nodeSassConfig && pkg.nodeSassConfig.binaryName) {
186     binaryName = pkg.nodeSassConfig.binaryName;
187   } else {
188     variant = getPlatformVariant();
189     if (variant) {
190       platform += '_' + variant;
191     }
192
193     binaryName = [
194       platform, '-',
195       process.arch, '-',
196       process.versions.modules
197     ].join('');
198   }
199
200   return [binaryName, 'binding.node'].join('_');
201 }
202
203 /**
204  * Determine the URL to fetch binary file from.
205  * By default fetch from the node-sass distribution
206  * site on GitHub.
207  *
208  * The default URL can be overriden using
209  * the environment variable SASS_BINARY_SITE,
210  * .npmrc variable sass_binary_site or
211  * or a command line option --sass-binary-site:
212  *
213  *   node scripts/install.js --sass-binary-site http://example.com/
214  *
215  * The URL should to the mirror of the repository
216  * laid out as follows:
217  *
218  * SASS_BINARY_SITE/
219  *
220  *  v3.0.0
221  *  v3.0.0/freebsd-x64-14_binding.node
222  *  ....
223  *  v3.0.0
224  *  v3.0.0/freebsd-ia32-11_binding.node
225  *  v3.0.0/freebsd-x64-42_binding.node
226  *  ... etc. for all supported versions and platforms
227  *
228  * @api public
229  */
230
231 function getBinaryUrl() {
232   var site = getArgument('--sass-binary-site') ||
233              process.env.SASS_BINARY_SITE  ||
234              process.env.npm_config_sass_binary_site ||
235              (pkg.nodeSassConfig && pkg.nodeSassConfig.binarySite) ||
236              'https://github.com/sass/node-sass/releases/download';
237
238   return [site, 'v' + pkg.version, getBinaryName()].join('/');
239 }
240
241 /**
242  * Get binary path.
243  * If environment variable SASS_BINARY_PATH,
244  * .npmrc variable sass_binary_path or
245  * process argument --sass-binary-path is provided,
246  * select it by appending binary name, otherwise
247  * make default binary path using binary name.
248  * Once the primary selection is made, check if
249  * callers wants to throw if file not exists before
250  * returning.
251  *
252  * @api public
253  */
254
255 function getBinaryPath() {
256   var binaryPath;
257
258   if (getArgument('--sass-binary-path')) {
259     binaryPath = getArgument('--sass-binary-path');
260   } else if (process.env.SASS_BINARY_PATH) {
261     binaryPath = process.env.SASS_BINARY_PATH;
262   } else if (process.env.npm_config_sass_binary_path) {
263     binaryPath = process.env.npm_config_sass_binary_path;
264   } else if (pkg.nodeSassConfig && pkg.nodeSassConfig.binaryPath) {
265     binaryPath = pkg.nodeSassConfig.binaryPath;
266   } else {
267     binaryPath = path.join(defaultBinaryPath, getBinaryName().replace(/_(?=binding\.node)/, '/'));
268   }
269
270   return binaryPath;
271 }
272
273 /**
274  * An array of paths suitable for use as a local disk cache of the binding.
275  *
276  * @return {[]String} an array of paths
277  * @api public
278  */
279 function getCachePathCandidates() {
280   return [
281     process.env.npm_config_sass_binary_cache,
282     process.env.npm_config_cache,
283   ].filter(function(_) { return _; });
284 }
285
286 /**
287  * The most suitable location for caching the binding on disk.
288  *
289  * Given the candidates directories provided by `getCachePathCandidates()` this
290  * returns the first writable directory. By treating the candidate directories
291  * as a prioritised list this method is deterministic, assuming no change to the
292  * local environment.
293  *
294  * @return {String} directory to cache binding
295  * @api public
296  */
297 function getBinaryCachePath() {
298   var i,
299     cachePath,
300     cachePathCandidates = getCachePathCandidates();
301
302   for (i = 0; i < cachePathCandidates.length; i++) {
303     cachePath = path.join(cachePathCandidates[i], pkg.name, pkg.version);
304
305     try {
306       mkdir.sync(cachePath);
307       return cachePath;
308     } catch (e) {
309       // Directory is not writable, try another
310     }
311   }
312
313   return '';
314 }
315
316 /**
317  * The cached binding
318  *
319  * Check the candidates directories provided by `getCachePathCandidates()` for
320  * the binding file, if it exists. By treating the candidate directories
321  * as a prioritised list this method is deterministic, assuming no change to the
322  * local environment.
323  *
324  * @return {String} path to cached binary
325  * @api public
326  */
327 function getCachedBinary() {
328   var i,
329     cachePath,
330     cacheBinary,
331     cachePathCandidates = getCachePathCandidates(),
332     binaryName = getBinaryName();
333
334   for (i = 0; i < cachePathCandidates.length; i++) {
335     cachePath = path.join(cachePathCandidates[i], pkg.name, pkg.version);
336     cacheBinary = path.join(cachePath, binaryName);
337
338     if (fs.existsSync(cacheBinary)) {
339       return cacheBinary;
340     }
341   }
342
343   return '';
344 }
345
346 /**
347  * Does the supplied binary path exist
348  *
349  * @param {String} binaryPath
350  * @api public
351  */
352
353 function hasBinary(binaryPath) {
354   return fs.existsSync(binaryPath);
355 }
356
357 /**
358  * Get Sass version information
359  *
360  * @api public
361  */
362
363 function getVersionInfo(binding) {
364   return [
365     ['node-sass', pkg.version, '(Wrapper)', '[JavaScript]'].join('\t'),
366     ['libsass  ', binding.libsassVersion(), '(Sass Compiler)', '[C/C++]'].join('\t'),
367   ].join(eol);
368 }
369
370 /**
371  * Gets the platform variant, currently either an empty string or 'musl' for Linux/musl platforms.
372  *
373  * @api public
374  */
375
376 function getPlatformVariant() {
377   var contents = '';
378
379   if (process.platform !== 'linux') {
380     return '';
381   }
382
383   try {
384     contents = fs.readFileSync(process.execPath);
385
386     // Buffer.indexOf was added in v1.5.0 so cast to string for old node
387     // Delay contents.toStrings because it's expensive
388     if (!contents.indexOf) {
389       contents = contents.toString();
390     }
391
392     if (contents.indexOf('libc.musl-x86_64.so.1') !== -1) {
393       return 'musl';
394     }
395   } catch (err) { } // eslint-disable-line no-empty
396
397   return '';
398 }
399
400 module.exports.hasBinary = hasBinary;
401 module.exports.getBinaryUrl = getBinaryUrl;
402 module.exports.getBinaryName = getBinaryName;
403 module.exports.getBinaryPath = getBinaryPath;
404 module.exports.getBinaryCachePath = getBinaryCachePath;
405 module.exports.getCachedBinary = getCachedBinary;
406 module.exports.getCachePathCandidates = getCachePathCandidates;
407 module.exports.getVersionInfo = getVersionInfo;
408 module.exports.getHumanEnvironment = getHumanEnvironment;
409 module.exports.getInstalledBinaries = getInstalledBinaries;
410 module.exports.isSupportedEnvironment = isSupportedEnvironment;