extensions.js 9.9 KB

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