Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

url: re-add path in url.format #893

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/api/url.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ string will not be in the parsed object. Examples are shown for the URL

Example: `'/p/a/t/h'`

* `path`: The path section of the URL, that is treated the same with `pathname`
but able to contain `query` as well.

Example: `'/p/a/t/h?i=10&j=91'`

* `search`: The 'query string' portion of the URL, including the leading
question mark.

Expand Down
223 changes: 148 additions & 75 deletions lib/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,73 @@ exports.format = urlFormat;

exports.Url = Url;

function Url() {
this.protocol = null;
this.slashes = null;
this.auth = null;
this.host = null;
this.port = null;
this.hostname = null;
this.hash = null;
this.search = null;
this.query = null;
this.pathname = null;
this.path = null;
this.href = null;
function Url(obj) {
this.protocol = obj && obj.protocol || null;
this.slashes = obj && obj.slashes || null;
this.auth = obj && obj.auth || null;
this.host = obj && obj.host || null;
this.port = obj && obj.port || null;
this.hostname = obj && obj.hostname || null;
this.hash = obj && obj.hash || null;
this.href = obj && obj.href || null;
this.path = obj && obj.path || null;
this._pathname = obj && obj.pathname || null;
this._search = obj && obj.search || null;
this._query = obj && obj.query || null;

var self = this;
Object.defineProperty(self, 'pathname', {
get: function() {
return self._pathname;
},
set: function(val) {
val = encodeURIComponent(val);
self._pathname = val;
self.path = (val || '') + (self._search || '');
}
});

Object.defineProperty(self, 'search', {
get: function() {
return self._search;
},
set: function(val) {
if (val && val.length && val[0] !== '?') val = '?' + val;
self._search = val;
self.path = (self._pathname) + (val || '');
}
});

Object.defineProperty(self, 'query', {
get: function() {
return self._query;
},
set: function(val) {
var qstr = typeof val !== 'string' ? querystring.stringify(val) : val;
self._query = val;
self.search = '?' + qstr;
}
});

Object.defineProperty(self, 'data', {
get: function() {
return {
protocol: this.protocol,
slashes: this.slashes,
auth: this.auth,
host: this.host,
port: this.port,
hostname: this.hostname,
hash: this.hash,
href: this.href,
path: this.path,
pathname: this._pathname,
search: this._search,
query: this._query,
};
}
});

}

// Reference: RFC 3986, RFC 1808, RFC 2396
Expand Down Expand Up @@ -86,9 +140,8 @@ function urlParse(url, parseQueryString, slashesDenoteHost) {
}

Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
if (typeof url !== 'string') {
if (typeof url !== 'string')
throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
}

// Copy chrome, IE, opera backslash-handling behavior.
// Back slashes before the query string get converted to forward slashes
Expand All @@ -113,17 +166,17 @@ Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
if (simplePath) {
this.path = rest;
this.href = rest;
this.pathname = simplePath[1];
this._pathname = simplePath[1];
if (simplePath[2]) {
this.search = simplePath[2];
this._search = simplePath[2];
if (parseQueryString) {
this.query = querystring.parse(this.search.substr(1));
this._query = querystring.parse(this._search.substr(1));
} else {
this.query = this.search.substr(1);
this._query = this._search.substr(1);
}
} else if (parseQueryString) {
this.search = '';
this.query = {};
this._search = '';
this._query = {};
}
return this;
}
Expand Down Expand Up @@ -317,32 +370,32 @@ Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
}
var qm = rest.indexOf('?');
if (qm !== -1) {
this.search = rest.substr(qm);
this.query = rest.substr(qm + 1);
this._search = rest.substr(qm);
this._query = rest.substr(qm + 1);
if (parseQueryString) {
this.query = querystring.parse(this.query);
this._query = querystring.parse(this._query);
}
rest = rest.slice(0, qm);
} else if (parseQueryString) {
// no query string, but parseQueryString still requested
this.search = '';
this.query = {};
this._search = '';
this._query = {};
}
if (rest) this.pathname = rest;
if (rest) this._pathname = rest;
if (slashedProtocol[lowerProto] &&
this.hostname && !this.pathname) {
this.pathname = '/';
this.hostname && !this._pathname) {
this._pathname = '/';
}

//to support http.request
if (this.pathname || this.search) {
var p = this.pathname || '';
var s = this.search || '';
if (this._pathname || this._search) {
var p = this._pathname || '';
var s = this._search || '';
this.path = p + s;
}

// finally, reconstruct the href based on what has been validated.
this.href = this.format();
this.href = this.format(parseQueryString);
return this;
};

Expand All @@ -353,11 +406,11 @@ function urlFormat(obj) {
// this way, you can call url_format() on strings
// to clean up potentially wonky urls.
if (typeof obj === 'string') obj = urlParse(obj);
if (!(obj instanceof Url)) return Url.prototype.format.call(obj);
if (!(obj instanceof Url)) obj = new Url(obj);
return obj.format();
}

Url.prototype.format = function() {
Url.prototype.format = function(parseQueryString) {
var auth = this.auth || '';
if (auth) {
auth = encodeURIComponent(auth);
Expand All @@ -366,10 +419,29 @@ Url.prototype.format = function() {
}

var protocol = this.protocol || '',
pathname = this.pathname || '',
pathname = this._pathname || '',
hash = this.hash || '',
host = false,
query = '';
query = '',
search = '';

if (this.path) {
var qm = this.path.indexOf('?');
if (qm !== -1) {
query = this.path.slice(qm + 1);
search = '?' + query;
pathname = this.path.slice(0, qm);
} else {
if (parseQueryString) {
this._query = {};
this._search = '';
} else {
this._query = null;
this._search = null;
}
pathname = this.path;
}
}

if (this.host) {
host = auth + this.host;
Expand All @@ -382,13 +454,14 @@ Url.prototype.format = function() {
}
}

if (this.query !== null &&
typeof this.query === 'object' &&
Object.keys(this.query).length) {
query = querystring.stringify(this.query);
if (this._query !== null &&
typeof this._query === 'object' &&
Object.keys(this._query).length) {
query = querystring.stringify(this._query);
}

var search = this.search || (query && ('?' + query)) || '';
if (!search)
search = this._search || (query && ('?' + query)) || '';

if (protocol && protocol.substr(-1) !== ':') protocol += ':';

Expand Down Expand Up @@ -462,8 +535,8 @@ Url.prototype.resolveObject = function(relative) {

//urlParse appends trailing / to urls like http://www.example.com
if (slashedProtocol[result.protocol] &&
result.hostname && !result.pathname) {
result.path = result.pathname = '/';
result.hostname && !result._pathname) {
result.path = result._pathname = '/';
}

result.href = result.format();
Expand Down Expand Up @@ -491,43 +564,43 @@ Url.prototype.resolveObject = function(relative) {

result.protocol = relative.protocol;
if (!relative.host && !hostlessProtocol[relative.protocol]) {
var relPath = (relative.pathname || '').split('/');
var relPath = (relative._pathname || '').split('/');
while (relPath.length && !(relative.host = relPath.shift()));
if (!relative.host) relative.host = '';
if (!relative.hostname) relative.hostname = '';
if (relPath[0] !== '') relPath.unshift('');
if (relPath.length < 2) relPath.unshift('');
result.pathname = relPath.join('/');
result._pathname = relPath.join('/');
} else {
result.pathname = relative.pathname;
result._pathname = relative._pathname;
}
result.search = relative.search;
result.query = relative.query;
result._search = relative._search;
result._query = relative._query;
result.host = relative.host || '';
result.auth = relative.auth;
result.hostname = relative.hostname || relative.host;
result.port = relative.port;
// to support http.request
if (result.pathname || result.search) {
var p = result.pathname || '';
var s = result.search || '';
if (result._pathname || result._search) {
var p = result._pathname || '';
var s = result._search || '';
result.path = p + s;
}
result.slashes = result.slashes || relative.slashes;
result.href = result.format();
return result;
}

var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
var isSourceAbs = (result._pathname && result._pathname.charAt(0) === '/'),
isRelAbs = (
relative.host ||
relative.pathname && relative.pathname.charAt(0) === '/'
relative._pathname && relative._pathname.charAt(0) === '/'
),
mustEndAbs = (isRelAbs || isSourceAbs ||
(result.host && relative.pathname)),
(result.host && relative._pathname)),
removeAllDots = mustEndAbs,
srcPath = result.pathname && result.pathname.split('/') || [],
relPath = relative.pathname && relative.pathname.split('/') || [],
srcPath = result._pathname && result._pathname.split('/') || [],
relPath = relative._pathname && relative._pathname.split('/') || [],
psychotic = result.protocol && !slashedProtocol[result.protocol];

// if the url is a non-slashed url, then relative
Expand Down Expand Up @@ -561,8 +634,8 @@ Url.prototype.resolveObject = function(relative) {
relative.host : result.host;
result.hostname = (relative.hostname || relative.hostname === '') ?
relative.hostname : result.hostname;
result.search = relative.search;
result.query = relative.query;
result._search = relative._search;
result._query = relative._query;
srcPath = relPath;
// fall through to the dot-handling below.
} else if (relPath.length) {
Expand All @@ -571,9 +644,9 @@ Url.prototype.resolveObject = function(relative) {
if (!srcPath) srcPath = [];
srcPath.pop();
srcPath = srcPath.concat(relPath);
result.search = relative.search;
result.query = relative.query;
} else if (relative.search !== null && relative.search !== undefined) {
result._search = relative._search;
result._query = relative._query;
} else if (relative._search !== null && relative._search !== undefined) {
// just pull out the search.
// like href='?foo'.
// Put this after the other two cases because it simplifies the booleans
Expand All @@ -589,12 +662,12 @@ Url.prototype.resolveObject = function(relative) {
result.host = result.hostname = authInHost.shift();
}
}
result.search = relative.search;
result.query = relative.query;
result._search = relative._search;
result._query = relative._query;
//to support http.request
if (result.pathname !== null || result.search !== null) {
result.path = (result.pathname ? result.pathname : '') +
(result.search ? result.search : '');
if (result._pathname !== null || result._search !== null) {
result.path = (result._pathname ? result._pathname : '') +
(result._search ? result._search : '');
}
result.href = result.format();
return result;
Expand All @@ -603,10 +676,10 @@ Url.prototype.resolveObject = function(relative) {
if (!srcPath.length) {
// no path at all. easy.
// we've already handled the other stuff above.
result.pathname = null;
result._pathname = null;
//to support http.request
if (result.search) {
result.path = '/' + result.search;
if (result._search) {
result.path = '/' + result._search;
} else {
result.path = null;
}
Expand Down Expand Up @@ -679,16 +752,16 @@ Url.prototype.resolveObject = function(relative) {
}

if (!srcPath.length) {
result.pathname = null;
result._pathname = null;
result.path = null;
} else {
result.pathname = srcPath.join('/');
result._pathname = srcPath.join('/');
}

//to support request.http
if (result.pathname !== null || result.search !== null) {
result.path = (result.pathname ? result.pathname : '') +
(result.search ? result.search : '');
if (result._pathname !== null || result._search !== null) {
result.path = (result._pathname ? result._pathname : '') +
(result._search ? result._search : '');
}
result.auth = relative.auth || result.auth;
result.slashes = result.slashes || relative.slashes;
Expand Down
Loading