atom-nuclide
Version:
A unified developer experience for web and mobile development, built as a suite of features on top of Atom to provide hackability and the support of an active community.
639 lines (537 loc) • 17.6 kB
JavaScript
Object.defineProperty(exports, '__esModule', {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/
// NuclideUri's are either a local file path, or a URI
// of the form nuclide://<host><path>
//
// This package creates, queries and decomposes NuclideUris.
var _assert2;
function _assert() {
return _assert2 = _interopRequireDefault(require('assert'));
}
// eslint-disable-next-line nuclide-internal/prefer-nuclide-uri
var _path2;
function _path() {
return _path2 = _interopRequireDefault(require('path'));
}
var _url2;
function _url() {
return _url2 = _interopRequireDefault(require('url'));
}
var _string2;
function _string() {
return _string2 = require('./string');
}
var REMOTE_PATH_URI_PREFIX = 'nuclide://';
function isRemote(uri) {
return uri.startsWith(REMOTE_PATH_URI_PREFIX);
}
function isLocal(uri) {
return !isRemote(uri);
}
function createRemoteUri(hostname, remotePath) {
return 'nuclide://' + hostname + remotePath;
}
/**
* Parses `uri` with Node's `url.parse` and calls `decodeURI` on `href`, `path`, and `pathname` of
* the parsed URL object.
*
* * `url.parse` seems to apply encodeURI to the URL, and we typically don't want this behavior.
* * Nuclide URIs disallow use of the `hash` attribute, and any hash characters are interpreted as
* as literal hashes.
*
* For example:
*
* parse('nuclide://f.co/path/to/#foo.txt#')
* >
* {
* ...
* path: '/path/to/#foo.txt#',
* ...
* }
*/
function parse(uri) {
if (isLocal(uri)) {
return {
auth: null,
host: null,
hostname: null,
href: uri,
path: uri,
pathname: uri,
protocol: null,
query: null,
search: null,
slashes: null
};
}
var parsedUri = (_url2 || _url()).default.parse(_escapeSpecialCharacters(uri));
(0, (_assert2 || _assert()).default)(parsedUri.path, 'Nuclide URIs must contain paths, ' + ((0, (_string2 || _string()).maybeToString)(parsedUri.path) + '\' found while parsing \'' + uri + '\''));
var path = parsedUri.path;
// `url.parse` treates the first '#' character as the beginning of the `hash` attribute. That
// feature is not used in Nuclide and is instead treated as part of the path.
if (parsedUri.hash != null) {
path += parsedUri.hash;
}
(0, (_assert2 || _assert()).default)(parsedUri.pathname, 'Nuclide URIs must contain pathnamess, ' + ('\'' + (0, (_string2 || _string()).maybeToString)(parsedUri.pathname) + '\' found while parsing \'' + uri + '\''));
var pathname = parsedUri.pathname;
// `url.parse` treates the first '#' character as the beginning of the `hash` attribute. That
// feature is not used in Nuclide and is instead treated as part of the pathname.
if (parsedUri.hash != null) {
pathname += parsedUri.hash;
}
// Explicitly copying object properties appeases Flow's "maybe" type handling. Using the `...`
// operator causes null/undefined errors, and `Object.assign` bypasses type checking.
return {
auth: parsedUri.auth,
host: parsedUri.host,
hostname: parsedUri.hostname,
href: decodeURI(parsedUri.href),
path: decodeURI(path),
pathname: decodeURI(pathname),
protocol: parsedUri.protocol,
query: parsedUri.query,
search: parsedUri.search,
slashes: parsedUri.slashes
};
}
function parseRemoteUri(remoteUri) {
if (!isRemote(remoteUri)) {
throw new Error('Expected remote uri. Got ' + remoteUri);
}
var parsedUri = parse(remoteUri);
(0, (_assert2 || _assert()).default)(parsedUri.hostname, 'Remote Nuclide URIs must contain hostnames, \'' + (0, (_string2 || _string()).maybeToString)(parsedUri.hostname) + '\' found ' + ('while parsing \'' + remoteUri + '\''));
// Explicitly copying object properties appeases Flow's "maybe" type handling. Using the `...`
// operator causes null/undefined errors, and `Object.assign` bypasses type checking.
return {
auth: parsedUri.auth,
host: parsedUri.host,
hostname: parsedUri.hostname,
href: parsedUri.href,
path: parsedUri.path,
pathname: parsedUri.pathname,
protocol: parsedUri.protocol,
query: parsedUri.query,
search: parsedUri.search,
slashes: parsedUri.slashes
};
}
function getPath(uri) {
return parse(uri).path;
}
function getHostname(remoteUri) {
return parseRemoteUri(remoteUri).hostname;
}
function getHostnameOpt(remoteUri) {
if (remoteUri == null || isLocal(remoteUri)) {
return null;
}
return getHostname(remoteUri);
}
function join(uri) {
var uriPathModule = _pathModuleFor(uri);
for (var _len = arguments.length, relativePath = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
relativePath[_key - 1] = arguments[_key];
}
if (isRemote(uri)) {
var _parseRemoteUri = parseRemoteUri(uri);
var _hostname = _parseRemoteUri.hostname;
var _path3 = _parseRemoteUri.path;
relativePath.splice(0, 0, _path3);
return createRemoteUri(_hostname, uriPathModule.join.apply(null, relativePath));
} else {
relativePath.splice(0, 0, uri);
return uriPathModule.join.apply(null, relativePath);
}
}
function normalize(uri) {
var uriPathModule = _pathModuleFor(uri);
if (isRemote(uri)) {
var _parseRemoteUri2 = parseRemoteUri(uri);
var _hostname2 = _parseRemoteUri2.hostname;
var _path4 = _parseRemoteUri2.path;
return createRemoteUri(_hostname2, uriPathModule.normalize(_path4));
} else {
return uriPathModule.normalize(uri);
}
}
function normalizeDir(uri) {
return ensureTrailingSeparator(normalize(uri));
}
function getParent(uri) {
// TODO: Is this different than dirname?
return normalize(join(uri, '..'));
}
function relative(uri, other) {
var uriPathModule = _pathModuleFor(uri);
var remote = isRemote(uri);
if (remote !== isRemote(other) || remote && getHostname(uri) !== getHostname(other)) {
throw new Error('Cannot relative urls on different hosts: ' + uri + ' and ' + other);
}
if (remote) {
return uriPathModule.relative(getPath(uri), getPath(other));
} else {
return uriPathModule.relative(uri, other);
}
}
function basename(uri) {
var ext = arguments.length <= 1 || arguments[1] === undefined ? '' : arguments[1];
var uriPathModule = _pathModuleFor(uri);
return uriPathModule.basename(getPath(uri), ext);
}
function dirname(uri) {
var uriPathModule = _pathModuleFor(uri);
if (isRemote(uri)) {
var _parseRemoteUri3 = parseRemoteUri(uri);
var _hostname3 = _parseRemoteUri3.hostname;
var _path5 = _parseRemoteUri3.path;
return createRemoteUri(_hostname3, uriPathModule.dirname(_path5));
} else {
return uriPathModule.dirname(uri);
}
}
function extname(uri) {
var uriPathModule = _pathModuleFor(uri);
return uriPathModule.extname(getPath(uri));
}
function stripExtension(uri) {
var ext = extname(uri);
if (ext.length === 0) {
return uri;
}
return uri.slice(0, -1 * ext.length);
}
/**
* uri is either a file: uri, or a nuclide: uri.
* must convert file: uri's to just a path for atom.
*
* Returns null if not a valid file: URI.
*/
function uriToNuclideUri(uri) {
var urlParts = (_url2 || _url()).default.parse(_escapeSpecialCharacters(uri), false);
if (urlParts.protocol === 'file:' && urlParts.path) {
// only handle real files for now.
return urlParts.path;
} else if (isRemote(uri)) {
return uri;
} else {
return null;
}
}
/**
* Converts local paths to file: URI's. Leaves remote URI's alone.
*/
function nuclideUriToUri(uri) {
if (isRemote(uri)) {
return uri;
} else {
return 'file://' + uri;
}
}
/**
* Returns true if child is equal to, or is a proper child of parent.
*/
function contains(parent, child) {
// Can't just do startsWith here. If this directory is "www" and you
// are trying to check "www-base", just using startsWith would return
// true, even though "www-base" is at the same level as "Www", not
// contained in it.
// Also, there's an issue with a trailing separator ambiguity. A path
// like /abc/ does contain /abc
// This function is used in some performance-sensitive parts, so we
// want to avoid doing unnecessary string copy, as those that would
// result from an ensureTrailingSeparator() call
//
// First we'll check the lengths.
// Then check startsWith. If so, then if the two path lengths are
// equal OR if the next character in the path to check is a path
// separator, then we know the checked path is in this path.
if (child.length < parent.length) {
// A strong indication of false
// It could be a matter of a trailing separator, though
if (child.length < parent.length - 1) {
// It must be more than just the separator
return false;
}
return endsWithSeparator(parent) && parent.startsWith(child);
}
if (!child.startsWith(parent)) {
return false;
}
if (endsWithSeparator(parent) || parent.length === child.length) {
return true;
}
var uriPathModule = _pathModuleFor(child);
return child.slice(parent.length).startsWith(uriPathModule.sep);
}
/**
* Filter an array of paths to contain only the collapsed root paths, e.g.
* [a/b/c, a/, c/d/, c/d/e] collapses to [a/, c/d/]
*/
function collapse(paths) {
return paths.filter(function (p) {
return !paths.some(function (fp) {
return contains(fp, p) && fp !== p;
});
});
}
var hostFormatters = [];
// A formatter which may shorten hostnames.
// Returns null if the formatter won't shorten the hostname.
// Registers a host formatter for nuclideUriToDisplayString
function registerHostnameFormatter(formatter) {
hostFormatters.push(formatter);
return {
dispose: function dispose() {
var index = hostFormatters.indexOf(formatter);
if (index >= 0) {
hostFormatters.splice(index, 1);
}
}
};
}
/**
* NuclideUris should never be shown to humans.
* This function returns a human usable string.
*/
function nuclideUriToDisplayString(uri) {
if (isRemote(uri)) {
var _hostname4 = getHostname(uri);
for (var formatter of hostFormatters) {
var formattedHostname = formatter(_hostname4);
if (formattedHostname) {
_hostname4 = formattedHostname;
break;
}
}
return _hostname4 + '/' + getPath(uri);
} else {
return uri;
}
}
function ensureTrailingSeparator(uri) {
var uriPathModule = _pathModuleFor(uri);
if (uri.endsWith(uriPathModule.sep)) {
return uri;
}
return uri + uriPathModule.sep;
}
function trimTrailingSeparator(uri) {
var uriPathModule = _pathModuleFor(uri);
var stripped = uri;
while (stripped.endsWith(uriPathModule.sep) && !isRoot(stripped)) {
stripped = stripped.slice(0, -1 * uriPathModule.sep.length);
}
return stripped;
}
function endsWithSeparator(uri) {
var uriPathModule = _pathModuleFor(uri);
return uri.endsWith(uriPathModule.sep);
}
function isAbsolute(uri) {
if (isRemote(uri)) {
return true;
} else {
var uriPathModule = _pathModuleFor(uri);
return uriPathModule.isAbsolute(uri);
}
}
function resolve(uri) {
var uriPathModule = _pathModuleFor(uri);
for (var _len2 = arguments.length, paths = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
paths[_key2 - 1] = arguments[_key2];
}
if (isRemote(uri)) {
var _parseRemoteUri4 = parseRemoteUri(uri);
var _hostname5 = _parseRemoteUri4.hostname;
var _path6 = _parseRemoteUri4.path;
paths.splice(0, 0, _path6);
return createRemoteUri(_hostname5, uriPathModule.resolve.apply(null, paths));
} else {
paths.splice(0, 0, uri);
return uriPathModule.resolve.apply(null, paths);
}
}
function expandHomeDir(uri) {
// This function is POSIX only functionality, so using the posix path directly
// Do not expand non home relative uris
if (!uri.startsWith('~')) {
return uri;
}
var HOME = process.env.HOME;
(0, (_assert2 || _assert()).default)(HOME != null);
if (uri === '~') {
return HOME;
}
// Uris like ~abc should not be expanded
if (!uri.startsWith('~/')) {
return uri;
}
return posixPath.resolve(HOME, uri.replace('~', '.'));
}
/**
* Splits a string containing local paths by an OS-specific path delimiter
* Useful for splitting env variables such as PATH
*
* Since remote URI might contain the delimiter, only local paths are allowed.
*/
function splitPathList(paths) {
(0, (_assert2 || _assert()).default)(paths.indexOf(REMOTE_PATH_URI_PREFIX) < 0, 'Splitting remote URIs is not supported');
var uriPathModule = _pathModuleFor(paths);
return paths.split(uriPathModule.delimiter);
}
/**
* Joins an array of local paths with an OS-specific path delimiter into a single string.
* Useful for constructing env variables such as PATH
*
* Since remote URI might contain the delimiter, only local paths are allowed.
*/
function joinPathList(paths) {
if (paths.length === 0) {
return '';
}
(0, (_assert2 || _assert()).default)(paths.every(function (path) {
return !isRemote(path);
}), 'Joining of remote URIs is not supported');
var uriPathModule = _pathModuleFor(paths[0]);
return paths.join(uriPathModule.delimiter);
}
/**
* This function prepends the given relative path with a "current-folder" prefix
* which is `./` on *nix and .\ on Windows
*/
function ensureLocalPrefix(uri) {
var uriPathModule = _pathModuleFor(uri);
(0, (_assert2 || _assert()).default)(!isRemote(uri), 'Local prefix can not be added to a remote path');
(0, (_assert2 || _assert()).default)(!isAbsolute(uri), 'Local prefix can not be added to an absolute path');
var localPrefix = '.' + uriPathModule.sep;
if (uri.startsWith(localPrefix)) {
return uri;
}
return localPrefix + uri;
}
function isRoot(uri) {
return dirname(uri) === uri;
}
function parsePath(uri) {
var uriPathModule = _pathModuleFor(uri);
return uriPathModule.parse(getPath(uri));
}
function split(uri) {
var parts = [];
var current = uri;
var parent = dirname(current);
while (current !== parent) {
parts.push(basename(current));
current = parent;
parent = dirname(current);
}
if (isAbsolute(uri)) {
parts.push(parent);
}
parts.reverse();
return parts;
}
/**
* win32.isAbsolute is buggy in Node 5.10.0, but not in Node 5.1.1 or 6.0.0+.
* As long as we support Node 5, we'll use the fixed version of win32.isAbsolute.
* https://github.com/nodejs/node/commit/3072546feb9d7f78f12d75bec28ef00e5958f7be
*/
function _win32PathIsAbsolute(path) {
if (typeof path !== 'string') {
throw new TypeError('Path must be a string. Received ' + String(path));
}
var len = path.length;
if (len === 0) {
return false;
}
var code = path.charCodeAt(0);
if (code === 47 || code === 92) {
return true;
} else if (code >= 65 && code <= 90 || code >= 97 && code <= 122) {
if (len > 2 && path.charCodeAt(1) === 58) {
code = path.charCodeAt(2);
if (code === 47 || code === 92) {
return true;
}
}
}
return false;
}
var posixPath = _extends({}, (_path2 || _path()).default.posix);
var win32Path = _extends({}, (_path2 || _path()).default.win32, { isAbsolute: _win32PathIsAbsolute });
function _pathModuleFor(uri) {
if (uri.startsWith(posixPath.sep)) {
return posixPath;
}
if (uri.indexOf('://') > -1) {
return posixPath;
}
if (uri[1] === ':' && uri[2] === win32Path.sep) {
return win32Path;
}
if (uri.split(win32Path.sep).length > uri.split(posixPath.sep).length) {
return win32Path;
} else {
return posixPath;
}
}
/**
* The backslash and percent characters (\ %) are, unfortunately, valid symbols to be used in POSIX
* paths. They, however, are being automatically "corrected" by node's `url.parse()` method if not
* escaped properly.
*/
function _escapeSpecialCharacters(uri) {
return uri.replace(/%/g, '%25').replace(/\\/g, '%5C');
}
exports.default = {
basename: basename,
dirname: dirname,
extname: extname,
stripExtension: stripExtension,
isRemote: isRemote,
isLocal: isLocal,
createRemoteUri: createRemoteUri,
parse: parse,
parseRemoteUri: parseRemoteUri,
getPath: getPath,
getHostname: getHostname,
getHostnameOpt: getHostnameOpt,
join: join,
relative: relative,
normalize: normalize,
normalizeDir: normalizeDir,
getParent: getParent,
uriToNuclideUri: uriToNuclideUri,
nuclideUriToUri: nuclideUriToUri,
contains: contains,
collapse: collapse,
nuclideUriToDisplayString: nuclideUriToDisplayString,
registerHostnameFormatter: registerHostnameFormatter,
ensureTrailingSeparator: ensureTrailingSeparator,
trimTrailingSeparator: trimTrailingSeparator,
endsWithSeparator: endsWithSeparator,
isAbsolute: isAbsolute,
resolve: resolve,
expandHomeDir: expandHomeDir,
splitPathList: splitPathList,
joinPathList: joinPathList,
ensureLocalPrefix: ensureLocalPrefix,
isRoot: isRoot,
parsePath: parsePath,
split: split
};
var __TEST__ = {
_pathModuleFor: _pathModuleFor,
_win32PathIsAbsolute: _win32PathIsAbsolute
};
exports.__TEST__ = __TEST__;