463 lines
12 KiB
JavaScript
463 lines
12 KiB
JavaScript
|
/*!
|
||
|
* node-sass: lib/extensions.js
|
||
|
*/
|
||
|
|
||
|
var eol = require('os').EOL,
|
||
|
fs = require('fs'),
|
||
|
pkg = require('../package.json'),
|
||
|
mkdir = require('mkdirp'),
|
||
|
path = require('path'),
|
||
|
defaultBinaryDir = path.join(__dirname, '..', 'vendor'),
|
||
|
trueCasePathSync = require('true-case-path');
|
||
|
|
||
|
/**
|
||
|
* Get the human readable name of the Platform that is running
|
||
|
*
|
||
|
* @param {string} platform - An OS platform to match, or null to fallback to
|
||
|
* the current process platform
|
||
|
* @return {Object} The name of the platform if matched, false otherwise
|
||
|
*
|
||
|
* @api public
|
||
|
*/
|
||
|
function getHumanPlatform(platform) {
|
||
|
switch (platform || process.platform) {
|
||
|
case 'darwin': return 'OS X';
|
||
|
case 'freebsd': return 'FreeBSD';
|
||
|
case 'linux': return 'Linux';
|
||
|
case 'linux_musl': return 'Linux/musl';
|
||
|
case 'win32': return 'Windows';
|
||
|
default: return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Provides a more readable version of the architecture
|
||
|
*
|
||
|
* @param {string} arch - An instruction architecture name to match, or null to
|
||
|
* lookup the current process architecture
|
||
|
* @return {Object} The value of the process architecture, or false if unknown
|
||
|
*
|
||
|
* @api public
|
||
|
*/
|
||
|
function getHumanArchitecture(arch) {
|
||
|
switch (arch || process.arch) {
|
||
|
case 'ia32': return '32-bit';
|
||
|
case 'x86': return '32-bit';
|
||
|
case 'x64': return '64-bit';
|
||
|
default: return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the friendly name of the Node environment being run
|
||
|
*
|
||
|
* @param {Object} abi - A Node Application Binary Interface value, or null to
|
||
|
* fallback to the current Node ABI
|
||
|
* @return {Object} Returns a string name of the Node environment or false if
|
||
|
* unmatched
|
||
|
*
|
||
|
* @api public
|
||
|
*/
|
||
|
function getHumanNodeVersion(abi) {
|
||
|
switch (parseInt(abi || process.versions.modules, 10)) {
|
||
|
case 11: return 'Node 0.10.x';
|
||
|
case 14: return 'Node 0.12.x';
|
||
|
case 42: return 'io.js 1.x';
|
||
|
case 43: return 'io.js 1.1.x';
|
||
|
case 44: return 'io.js 2.x';
|
||
|
case 45: return 'io.js 3.x';
|
||
|
case 46: return 'Node.js 4.x';
|
||
|
case 47: return 'Node.js 5.x';
|
||
|
case 48: return 'Node.js 6.x';
|
||
|
case 49: return 'Electron 1.3.x';
|
||
|
case 50: return 'Electron 1.4.x';
|
||
|
case 51: return 'Node.js 7.x';
|
||
|
case 53: return 'Electron 1.6.x';
|
||
|
case 57: return 'Node.js 8.x';
|
||
|
case 59: return 'Node.js 9.x';
|
||
|
case 64: return 'Node.js 10.x';
|
||
|
case 67: return 'Node.js 11.x';
|
||
|
case 72: return 'Node.js 12.x';
|
||
|
case 79: return 'Node.js 13.x';
|
||
|
case 83: return 'Node.js 14.x';
|
||
|
default: return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get a human readable description of where node-sass is running to support
|
||
|
* user error reporting when something goes wrong
|
||
|
*
|
||
|
* @param {string} env - The name of the native bindings that is to be parsed
|
||
|
* @return {string} A description of what os, architecture, and Node version
|
||
|
* that is being run
|
||
|
*
|
||
|
* @api public
|
||
|
*/
|
||
|
function getHumanEnvironment(env) {
|
||
|
var binding = env.replace(/_binding\.node$/, ''),
|
||
|
parts = binding.split('-'),
|
||
|
platform = getHumanPlatform(parts[0]),
|
||
|
arch = getHumanArchitecture(parts[1]),
|
||
|
runtime = getHumanNodeVersion(parts[2]);
|
||
|
|
||
|
if (parts.length !== 3) {
|
||
|
return 'Unknown environment (' + binding + ')';
|
||
|
}
|
||
|
|
||
|
if (!platform) {
|
||
|
platform = 'Unsupported platform (' + parts[0] + ')';
|
||
|
}
|
||
|
|
||
|
if (!arch) {
|
||
|
arch = 'Unsupported architecture (' + parts[1] + ')';
|
||
|
}
|
||
|
|
||
|
if (!runtime) {
|
||
|
runtime = 'Unsupported runtime (' + parts[2] + ')';
|
||
|
}
|
||
|
|
||
|
return [
|
||
|
platform, arch, 'with', runtime,
|
||
|
].join(' ');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the value of the binaries under the default path
|
||
|
*
|
||
|
* @return {Array} The currently installed node-sass bindings
|
||
|
*
|
||
|
* @api public
|
||
|
*/
|
||
|
function getInstalledBinaries() {
|
||
|
return fs.readdirSync(getBinaryDir());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check that an environment matches the whitelisted values or the current
|
||
|
* environment if no parameters are passed
|
||
|
*
|
||
|
* @param {string} platform - The name of the OS platform(darwin, win32, etc...)
|
||
|
* @param {string} arch - The instruction set architecture of the Node environment
|
||
|
* @param {string} abi - The Node Application Binary Interface
|
||
|
* @return {Boolean} True, if node-sass supports the current platform, false otherwise
|
||
|
*
|
||
|
* @api public
|
||
|
*/
|
||
|
function isSupportedEnvironment(platform, arch, abi) {
|
||
|
return (
|
||
|
false !== getHumanPlatform(platform) &&
|
||
|
false !== getHumanArchitecture(arch) &&
|
||
|
false !== getHumanNodeVersion(abi)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the value of a CLI argument
|
||
|
*
|
||
|
* @param {String} name
|
||
|
* @param {Array} args
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
function getArgument(name, args) {
|
||
|
var flags = args || process.argv.slice(2),
|
||
|
index = flags.lastIndexOf(name);
|
||
|
|
||
|
if (index === -1 || index + 1 >= flags.length) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return flags[index + 1];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get binary name.
|
||
|
* If environment variable SASS_BINARY_NAME,
|
||
|
* .npmrc variable sass_binary_name or
|
||
|
* process argument --binary-name is provided,
|
||
|
* return it as is, otherwise make default binary
|
||
|
* name: {platform}-{arch}-{v8 version}.node
|
||
|
*
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
function getBinaryName() {
|
||
|
var binaryName,
|
||
|
variant,
|
||
|
platform = process.platform;
|
||
|
|
||
|
if (getArgument('--sass-binary-name')) {
|
||
|
binaryName = getArgument('--sass-binary-name');
|
||
|
} else if (process.env.SASS_BINARY_NAME) {
|
||
|
binaryName = process.env.SASS_BINARY_NAME;
|
||
|
} else if (process.env.npm_config_sass_binary_name) {
|
||
|
binaryName = process.env.npm_config_sass_binary_name;
|
||
|
} else if (pkg.nodeSassConfig && pkg.nodeSassConfig.binaryName) {
|
||
|
binaryName = pkg.nodeSassConfig.binaryName;
|
||
|
} else {
|
||
|
variant = getPlatformVariant();
|
||
|
if (variant) {
|
||
|
platform += '_' + variant;
|
||
|
}
|
||
|
|
||
|
binaryName = [
|
||
|
platform, '-',
|
||
|
process.arch, '-',
|
||
|
process.versions.modules
|
||
|
].join('');
|
||
|
}
|
||
|
|
||
|
return [binaryName, 'binding.node'].join('_');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Determine the URL to fetch binary file from.
|
||
|
* By default fetch from the node-sass distribution
|
||
|
* site on GitHub.
|
||
|
*
|
||
|
* The default URL can be overridden using
|
||
|
* the environment variable SASS_BINARY_SITE,
|
||
|
* .npmrc variable sass_binary_site or
|
||
|
* or a command line option --sass-binary-site:
|
||
|
*
|
||
|
* node scripts/install.js --sass-binary-site http://example.com/
|
||
|
*
|
||
|
* The URL should to the mirror of the repository
|
||
|
* laid out as follows:
|
||
|
*
|
||
|
* SASS_BINARY_SITE/
|
||
|
*
|
||
|
* v3.0.0
|
||
|
* v3.0.0/freebsd-x64-14_binding.node
|
||
|
* ....
|
||
|
* v3.0.0
|
||
|
* v3.0.0/freebsd-ia32-11_binding.node
|
||
|
* v3.0.0/freebsd-x64-42_binding.node
|
||
|
* ... etc. for all supported versions and platforms
|
||
|
*
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
function getBinaryUrl() {
|
||
|
var site = getArgument('--sass-binary-site') ||
|
||
|
process.env.SASS_BINARY_SITE ||
|
||
|
process.env.npm_config_sass_binary_site ||
|
||
|
(pkg.nodeSassConfig && pkg.nodeSassConfig.binarySite) ||
|
||
|
'https://github.com/sass/node-sass/releases/download';
|
||
|
|
||
|
return [site, 'v' + pkg.version, getBinaryName()].join('/');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get binary dir.
|
||
|
* If environment variable SASS_BINARY_DIR,
|
||
|
* .npmrc variable sass_binary_dir or
|
||
|
* process argument --sass-binary-dir is provided,
|
||
|
* select it by appending binary name, otherwise
|
||
|
* use default binary dir.
|
||
|
* Once the primary selection is made, check if
|
||
|
* callers wants to throw if file not exists before
|
||
|
* returning.
|
||
|
*
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
function getBinaryDir() {
|
||
|
var binaryDir;
|
||
|
|
||
|
if (getArgument('--sass-binary-dir')) {
|
||
|
binaryDir = getArgument('--sass-binary-dir');
|
||
|
} else if (process.env.SASS_BINARY_DIR) {
|
||
|
binaryDir = process.env.SASS_BINARY_DIR;
|
||
|
} else if (process.env.npm_config_sass_binary_dir) {
|
||
|
binaryDir = process.env.npm_config_sass_binary_dir;
|
||
|
} else if (pkg.nodeSassConfig && pkg.nodeSassConfig.binaryDir) {
|
||
|
binaryDir = pkg.nodeSassConfig.binaryDir;
|
||
|
} else {
|
||
|
binaryDir = defaultBinaryDir;
|
||
|
}
|
||
|
|
||
|
return binaryDir;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get binary path.
|
||
|
* If environment variable SASS_BINARY_PATH,
|
||
|
* .npmrc variable sass_binary_path or
|
||
|
* process argument --sass-binary-path is provided,
|
||
|
* select it by appending binary name, otherwise
|
||
|
* make default binary path using binary name.
|
||
|
* Once the primary selection is made, check if
|
||
|
* callers wants to throw if file not exists before
|
||
|
* returning.
|
||
|
*
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
function getBinaryPath() {
|
||
|
var binaryPath;
|
||
|
|
||
|
if (getArgument('--sass-binary-path')) {
|
||
|
binaryPath = getArgument('--sass-binary-path');
|
||
|
} else if (process.env.SASS_BINARY_PATH) {
|
||
|
binaryPath = process.env.SASS_BINARY_PATH;
|
||
|
} else if (process.env.npm_config_sass_binary_path) {
|
||
|
binaryPath = process.env.npm_config_sass_binary_path;
|
||
|
} else if (pkg.nodeSassConfig && pkg.nodeSassConfig.binaryPath) {
|
||
|
binaryPath = pkg.nodeSassConfig.binaryPath;
|
||
|
} else {
|
||
|
binaryPath = path.join(getBinaryDir(), getBinaryName().replace(/_(?=binding\.node)/, '/'));
|
||
|
}
|
||
|
|
||
|
if (process.versions.modules < 46) {
|
||
|
return binaryPath;
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
return trueCasePathSync(binaryPath) || binaryPath;
|
||
|
} catch (e) {
|
||
|
return binaryPath;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* An array of paths suitable for use as a local disk cache of the binding.
|
||
|
*
|
||
|
* @return {[]String} an array of paths
|
||
|
* @api public
|
||
|
*/
|
||
|
function getCachePathCandidates() {
|
||
|
return [
|
||
|
process.env.npm_config_sass_binary_cache,
|
||
|
process.env.npm_config_cache,
|
||
|
].filter(function(_) { return _; });
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* The most suitable location for caching the binding on disk.
|
||
|
*
|
||
|
* Given the candidates directories provided by `getCachePathCandidates()` this
|
||
|
* returns the first writable directory. By treating the candidate directories
|
||
|
* as a prioritised list this method is deterministic, assuming no change to the
|
||
|
* local environment.
|
||
|
*
|
||
|
* @return {String} directory to cache binding
|
||
|
* @api public
|
||
|
*/
|
||
|
function getBinaryCachePath() {
|
||
|
var i,
|
||
|
cachePath,
|
||
|
cachePathCandidates = getCachePathCandidates();
|
||
|
|
||
|
for (i = 0; i < cachePathCandidates.length; i++) {
|
||
|
cachePath = path.join(cachePathCandidates[i], pkg.name, pkg.version);
|
||
|
|
||
|
try {
|
||
|
mkdir.sync(cachePath);
|
||
|
return cachePath;
|
||
|
} catch (e) {
|
||
|
// Directory is not writable, try another
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* The cached binding
|
||
|
*
|
||
|
* Check the candidates directories provided by `getCachePathCandidates()` for
|
||
|
* the binding file, if it exists. By treating the candidate directories
|
||
|
* as a prioritised list this method is deterministic, assuming no change to the
|
||
|
* local environment.
|
||
|
*
|
||
|
* @return {String} path to cached binary
|
||
|
* @api public
|
||
|
*/
|
||
|
function getCachedBinary() {
|
||
|
var i,
|
||
|
cachePath,
|
||
|
cacheBinary,
|
||
|
cachePathCandidates = getCachePathCandidates(),
|
||
|
binaryName = getBinaryName();
|
||
|
|
||
|
for (i = 0; i < cachePathCandidates.length; i++) {
|
||
|
cachePath = path.join(cachePathCandidates[i], pkg.name, pkg.version);
|
||
|
cacheBinary = path.join(cachePath, binaryName);
|
||
|
|
||
|
if (fs.existsSync(cacheBinary)) {
|
||
|
return cacheBinary;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Does the supplied binary path exist
|
||
|
*
|
||
|
* @param {String} binaryPath
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
function hasBinary(binaryPath) {
|
||
|
return fs.existsSync(binaryPath);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get Sass version information
|
||
|
*
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
function getVersionInfo(binding) {
|
||
|
return [
|
||
|
['node-sass', pkg.version, '(Wrapper)', '[JavaScript]'].join('\t'),
|
||
|
['libsass ', binding.libsassVersion(), '(Sass Compiler)', '[C/C++]'].join('\t'),
|
||
|
].join(eol);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the platform variant, currently either an empty string or 'musl' for Linux/musl platforms.
|
||
|
*
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
function getPlatformVariant() {
|
||
|
var contents = '';
|
||
|
|
||
|
if (process.platform !== 'linux') {
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
contents = fs.readFileSync(process.execPath);
|
||
|
|
||
|
// Buffer.indexOf was added in v1.5.0 so cast to string for old node
|
||
|
// Delay contents.toStrings because it's expensive
|
||
|
if (!contents.indexOf) {
|
||
|
contents = contents.toString();
|
||
|
}
|
||
|
|
||
|
if (contents.indexOf('libc.musl-x86_64.so.1') !== -1) {
|
||
|
return 'musl';
|
||
|
}
|
||
|
} catch (err) { } // eslint-disable-line no-empty
|
||
|
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
module.exports.hasBinary = hasBinary;
|
||
|
module.exports.getBinaryUrl = getBinaryUrl;
|
||
|
module.exports.getBinaryName = getBinaryName;
|
||
|
module.exports.getBinaryDir = getBinaryDir;
|
||
|
module.exports.getBinaryPath = getBinaryPath;
|
||
|
module.exports.getBinaryCachePath = getBinaryCachePath;
|
||
|
module.exports.getCachedBinary = getCachedBinary;
|
||
|
module.exports.getCachePathCandidates = getCachePathCandidates;
|
||
|
module.exports.getVersionInfo = getVersionInfo;
|
||
|
module.exports.getHumanEnvironment = getHumanEnvironment;
|
||
|
module.exports.getInstalledBinaries = getInstalledBinaries;
|
||
|
module.exports.isSupportedEnvironment = isSupportedEnvironment;
|