Skip to content

Commit

Permalink
v1.0.0-alpha13
Browse files Browse the repository at this point in the history
  • Loading branch information
stevenvachon committed Feb 25, 2021
1 parent faac8fb commit 9a1ef72
Show file tree
Hide file tree
Showing 9 changed files with 4,099 additions and 99 deletions.
4 changes: 1 addition & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
language: node_js
node_js:
- 8
- 10
- 12
- 14
- node
script: npm run ci
36 changes: 17 additions & 19 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
# url-relation [![NPM Version][npm-image]][npm-url] ![File Size][filesize-image] [![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url] [![Dependency Monitor][greenkeeper-image]][greenkeeper-url]

> Determine the relation between two [`URL`](https://developer.mozilla.org/en/docs/Web/API/URL)s.
> Determine the relation between two [`URL`](https://mdn.io/URL)s.

## Installation

[Node.js](http://nodejs.org) `>= 8` is required. To install, type this at the command line:
[Node.js](http://nodejs.org) `>= 14` is required. To install, type this at the command line:
```shell
npm install url-relation
```


## Constructor
```js
new URLRelation(url1, url2[, options]);
```


## Methods & Functions
## Usage

### `URLRelation.match(url1, url2[, options])`
```js
Expand Down Expand Up @@ -151,12 +145,12 @@ const options = {
An example of checking for a trusted hostname:
```js
const dynamicProfile = (url1, url2) => {
const trustedHosts = ['domain.com'];
const trustedHostnames = ['domain.com'];

const isTrusted = trustedHosts
.reduce((results, trustedHost) => {
results[0] = results[0] || url1.hostname.endsWith(trustedHost);
results[1] = results[1] || url2.hostname.endsWith(trustedHost);
const isTrusted = trustedHostnames
.reduce((results, trustedHostname) => {
results[0] = results[0] || url1.hostname.endsWith(trustedHostname);
results[1] = results[1] || url2.hostname.endsWith(trustedHostname);
return results;
}, [false,false])
.every(result => result);
Expand Down Expand Up @@ -189,9 +183,9 @@ const custom = extend(true, {}, URLRelation.COMMON_PROFILE, { indexFilenames:['i
## URL Components

```
AUTH HOST PATH
__|__ ___|___ _______|______
/ \ / \ / \
AUTH HOST PATH
__|__ ___|___ ______|______
/ \ / \ / \
USERNAME PASSWORD HOSTNAME PORT PATHNAME SEARCH HASH
___|__ __|___ ______|______ | __________|_________ ___|___ |
/ \ / \ / \ / \ / \ / \ / \
Expand Down Expand Up @@ -221,11 +215,15 @@ The components of URLs are compared in the following order:
* `PATH`
* `HASH`

As you may have noticed, there are a few breaks in linearity:
As you may have noticed, there are a few **breaks in linearity**:

* `TLD` is prioritized *before* `DOMAIN` because matching a domain on a different top-level domain is very uncommon (but still possible via [`ignoreComponents`](#ignorecomponents)).
* `SUBDOMAIN` is prioritized *after* `DOMAIN`.

Other considerations:

* URLs with [invalid domain names](https://tools.ietf.org/html/rfc1034), [reserved domains](https://npmjs.com/parse-domain#-reserved-domains), [unlisted TLDs](https://publicsuffix.org/) or IP addresses that have been determined to have related `HOSTNAME` components will also have related `TLD`, `DOMAIN` and `SUBDOMAIN` components due to the above mentioned comparison order *only*; not because they actually *have* those components.


## Browserify/etc

Expand All @@ -234,7 +232,7 @@ Due to extreme file size in correctly parsing domains, browser builds will not i

[npm-image]: https://img.shields.io/npm/v/url-relation.svg
[npm-url]: https://npmjs.org/package/url-relation
[filesize-image]: https://img.shields.io/badge/size-12.5kB%20gzipped-blue.svg
[filesize-image]: https://img.shields.io/badge/size-16.4kB%20gzip%20bundle-blue.svg
[travis-image]: https://img.shields.io/travis/stevenvachon/url-relation.svg
[travis-url]: https://travis-ci.org/stevenvachon/url-relation
[coveralls-image]: https://img.shields.io/coveralls/stevenvachon/url-relation.svg
Expand Down
58 changes: 28 additions & 30 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@ const anyMatch = require("any-match");
const {CAREFUL_PROFILE, COMMON_PROFILE} = require("./profiles");
const {components, componentSequence} = require("./components");
const deepFreeze = require("deep-freeze-node");
const defined = require("defined");
const evaluateValue = require("evaluate-value");
const excludeComponents = require("./excludeComponents");
const isURL = require("isurl");
const parseDomain = require("parse-domain");
const {parseDomain, ParseResultType} = require("parse-domain");
require("symbol.prototype.description/auto");



const encodedSpace = /%20/g;
const multipleSlashes = /\/{2,}/g;
const ENCODED_SPACE = /%20/g;
const MULTIPLE_SLASHES = /\/{2,}/g;



Expand All @@ -35,14 +34,7 @@ const defaultValue = (customOptions, optionName, ...args) =>
{
const defaultOption = evaluateValue(COMMON_PROFILE[optionName], ...args);

if (customOptions != null)
{
return defined( evaluateValue(customOptions[optionName], ...args), defaultOption );
}
else
{
return defaultOption;
}
return evaluateValue(customOptions?.[optionName], ...args) ?? defaultOption;
};


Expand All @@ -60,11 +52,11 @@ const hostnameRelation = (url1, url2, options) =>
}
else
{
const hostname1 = parseDomain(url1.hostname);
const hostname2 = parseDomain(url2.hostname);
// When running in a browser, all parsed hostnames will be considered invalid
let hostname1 = parseDomain(url1.hostname);
let hostname2 = parseDomain(url2.hostname);

// If unknown top-level domain or running in a browser
if (hostname1===null || hostname2===null)
if (hostname1.type!==ParseResultType.Listed || hostname2.type!==ParseResultType.Listed)
{
return {
domain: false,
Expand All @@ -75,13 +67,17 @@ const hostnameRelation = (url1, url2, options) =>
}
else
{
// Avoid "effective" TLDs
hostname1 = hostname1.icann;
hostname2 = hostname2.icann;

const ignoreWWW = defaultValue(options, "ignoreWWW", url1, url2);
const subdomain1 = (ignoreWWW && hostname1.subdomain === "www") ? "" : hostname1.subdomain;
const subdomain2 = (ignoreWWW && hostname2.subdomain === "www") ? "" : hostname2.subdomain;
const subdomains1 = (ignoreWWW && hostname1.subDomains.[0] === "www") ? hostname1.subDomains.slice(1) : hostname1.subDomains;
const subdomains2 = (ignoreWWW && hostname2.subDomains.[0] === "www") ? hostname2.subDomains.slice(1) : hostname2.subDomains;

const domain = hostname1.domain === hostname2.domain;
const subdomain = subdomain1 === subdomain2;
const tld = hostname1.tld === hostname2.tld;
const subdomain = subdomains1.join(".") === subdomains2.join(".");
const tld = hostname1.topLevelDomains.join(".") === hostname2.topLevelDomains.join(".");

return {
domain,
Expand Down Expand Up @@ -118,8 +114,8 @@ const pathnameRelation = (url1, url2, options) =>
{
if (defaultValue(options, "ignoreEmptySegmentNames", url1, url2))
{
pathname1 = pathname1.replace(multipleSlashes, "/");
pathname2 = pathname2.replace(multipleSlashes, "/");
pathname1 = pathname1.replace(MULTIPLE_SLASHES, "/");
pathname2 = pathname2.replace(MULTIPLE_SLASHES, "/");
}

pathname1 = pathname1.split("/");
Expand Down Expand Up @@ -199,9 +195,9 @@ const portRelation = (url1, url2, options) =>

const searchRelation = (url1, url2, options) =>
{
// TODO :: https://github.com/whatwg/url/issues/18
// @todo don't do this because there're more details such as https://github.com/whatwg/url/issues/18
// If normalized match
if (url1.search.replace(encodedSpace,"+") === url2.search.replace(encodedSpace,"+"))
if (url1.search.replace(ENCODED_SPACE,"+") === url2.search.replace(ENCODED_SPACE,"+"))
{
return {
search: true
Expand All @@ -215,6 +211,7 @@ const searchRelation = (url1, url2, options) =>

const noIgnores = !ignoreEmptyQueries && !ignoreQueryNames && !ignoreQueryOrder;

// @todo remove support for this
const partialImplementation = url1.searchParams===undefined || url2.searchParams===undefined;

if (noIgnores || partialImplementation)
Expand Down Expand Up @@ -259,8 +256,9 @@ const searchRelation = (url1, url2, options) =>
else
{
return {
search: params1.every((param1, i) => {
const matchingKey = param1[0] === params2[i][0];
search: params1.every((param1, i) =>
{
const matchingKey = param1[0] === params2[i][0];
const matchingValue = param1[1] === params2[i][1];
return matchingKey && matchingValue;
})
Expand All @@ -281,12 +279,12 @@ class URLRelation
throw new TypeError("Invalid URL");
}

// TODO :: make private when there's a native implementation
// @todo make private when there's a native implementation
this.options = options;
this.url1 = url1;
this.url2 = url2;

// TODO :: make private when there's a native implementation
// @todo make private when there's a native implementation
this.relations =
{
hash: url1.hash === url2.hash,
Expand All @@ -311,7 +309,7 @@ class URLRelation



// TODO :: make private when there's a native implementation
// @todo make private when there's a native implementation
run(targetComponent, components)
{
if (targetComponent === undefined)
Expand Down Expand Up @@ -349,7 +347,7 @@ class URLRelation



// TODO :: move to public static fields when there's a native implementation
// @todo move to public static fields when there's a native implementation
Object.assign
(
URLRelation,
Expand Down
42 changes: 20 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "url-relation",
"description": "Determine the relation between two URLs.",
"version": "1.0.0-alpha12",
"version": "1.0.0-alpha13",
"license": "MIT",
"author": "Steven Vachon <contact@svachon.com> (https://svachon.com)",
"repository": "github:stevenvachon/url-relation",
Expand All @@ -11,40 +11,38 @@
"parse-domain": "./shims/parse-domain"
},
"dependencies": {
"any-match": "^2.0.0",
"array.prototype.flat": "^1.2.1",
"array.prototype.flat": "^1.2.4",
"any-match": "^2.0.1",
"deep-freeze-node": "^1.1.3",
"defined": "^1.0.0",
"evaluate-value": "^2.0.0",
"isurl": "^4.0.1",
"parse-domain": "^2.1.7",
"symbol.prototype.description": "^1.0.0"
"parse-domain": "^3.0.3",
"symbol.prototype.description": "^1.0.4"
},
"devDependencies": {
"@babel/cli": "^7.4.3",
"@babel/core": "^7.4.3",
"@babel/preset-env": "^7.4.3",
"@babel/cli": "^7.13.0",
"@babel/core": "^7.13.1",
"@babel/preset-env": "^7.13.5",
"babelify": "^10.0.0",
"browserify": "^16.2.3",
"chai": "^4.2.0",
"coveralls": "^3.0.3",
"gzip-size-cli": "^3.0.0",
"browserify": "^17.0.0",
"chai": "^4.3.0",
"coveralls": "^3.1.0",
"gzip-size-cli": "^4.0.0",
"incomplete-url": "^4.0.0",
"mocha": "^6.1.4",
"npm-watch": "~0.6.0",
"nyc": "^14.0.0",
"terser": "^3.17.0",
"universal-url": "^2.0.0"
"mocha": "^8.3.0",
"npm-watch": "~0.7.0",
"nyc": "^15.1.0",
"terser": "^5.6.0"
},
"engines": {
"node": ">= 8"
"node": ">= 14"
},
"scripts": {
"build": "babel lib/ --out-dir=lib-es5/ --presets=@babel/env --source-maps",
"ci": "npm test && nyc report --reporter=text-lcov | coveralls",
"posttest": "nyc report --reporter=text-summary --reporter=html && browserify lib --global-transform [ babelify --presets [ @babel/env ] ] --standalone=URLRelation | terser --compress --mangle | gzip-size",
"prepublishOnly": "npm test && babel lib/ --out-dir=lib-es5/ --presets=@babel/env --source-maps",
"posttest": "nyc report --reporter=text-summary --reporter=html && npm run build --silent && browserify lib/ --global-transform [ babelify --presets [ @babel/env ] ] --standalone=URLRelation | terser --compress --mangle | gzip-size",
"test": "nyc --silent mocha test/ --check-leaks --bail",
"watch": "npm-watch test"
"test:watch": "npm-watch test"
},
"watch": {
"test": {
Expand Down
13 changes: 11 additions & 2 deletions shims/parse-domain.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
"use strict";

// Browser shim
module.exports = function()
module.exports =
{
return null;
parseDomain: function()
{
return {
type: this.ParseResultType.Invalid
};
},
ParseResultType:
{
Invalid: "INVALID"
}
};
7 changes: 3 additions & 4 deletions test/URLRelation.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ const customizeURL = require("incomplete-url");
const {describe, it} = require("mocha");
const {expect} = require("chai");
const tests = require("./helpers/tests.json");
const {URL} = require("universal-url");
const URLRelation = require("../lib");


Expand All @@ -31,7 +30,7 @@ const all = (url1, url2, component) =>

const allMatch = (url1, url2, options) =>
{
const {componentIndex, relatedParts, unrelatedParts} = all(url1, url2, options.targetComponent);
const {relatedParts, unrelatedParts} = all(url1, url2, options.targetComponent);

const relatedPartsMatch = relatedParts.every(component => URLRelation.match(url1, url2, options));
const unrelatedPartsMatch = unrelatedParts.every(component => !URLRelation.match(url1, url2, options));
Expand All @@ -43,7 +42,7 @@ const allMatch = (url1, url2, options) =>

const allUpTo = (url1, url2, options, component, ignoreComponents) =>
{
const {componentIndex, relatedParts, unrelatedParts} = all(url1, url2, component);
const {relatedParts, unrelatedParts} = all(url1, url2, component);

const instance = new URLRelation(url1, url2, options);

Expand Down Expand Up @@ -116,7 +115,7 @@ const options = overrides =>
ignoreWWW: false,
indexFilenames: [],
queryNames: [],
targetComponent: null,
targetComponent: "hash", // intentionally not a Symbol
...overrides
});

Expand Down
3 changes: 3 additions & 0 deletions test/helpers/generator.js → test/helpers/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ const urls =
"http://user:pass@www.domain.com:80/test//tes.ter/./../index.html?va r1= +dir&var2=text&var3#anchor2",

// Edge cases
"http://user:pass@com:80/test//tes.ter/./../index.html?va r1= +dir&var2=text&var3#anchor",
"http://user:pass@localhost:80/test//tes.ter/./../index.html?va r1= +dir&var2=text&var3#anchor",
"http://user:pass@127.0.0.1:80/test//tes.ter/./../index.html?va r1= +dir&var2=text&var3#anchor",
"http://user:pass@www.domain.com:80/test//tes.ter/./../index.html?va r1= +dir&var2=text&var3&=#anchor",
"http://user:pass@www.domain.com:80/test//tes.ter/./../index.html?va r1= +dir&var2=text&var2=text2&var3#anchor",
"http://user:pass@www.domain.com:80/test//tes.ter/./../index.html?var2=text&va r1= +dir&var3&var2=text2#anchor",
Expand Down

0 comments on commit 9a1ef72

Please sign in to comment.