§ Quick Take

import { strict as assert } from "assert";
import isRel from "dist/is-relative-uri.esm";

assert.deepEqual(isRel(".../resource.txt"), {
  res: false,
  message: `Three consecutive dots.`,
});

§ Background

It's easy to validate the absolute URL's:

<a href="https://www.npmjs.com">z</a>

It's harder to validate relative URL's:

<a href="//example.com/path/resource.txt">z</a>
<a href="/path/resource.txt">z</a>
<a href="path/resource.txt">z</a>
<a href="/path/resource.txt">z</a>
<a href="../resource.txt">z</a>
<a href="./resource.txt">z</a>
<a href="resource.txt">z</a>
<a href="#fragment">z</a>

But spec-wise, at least theoretically, these are also fine (see wikipedia opens in a new tab for more).

Considering the base URI of http://a/b/c/d;p?q, the following URI's would get resolved accordingly:

<a href="g:h">z</a>     -> "g:h"
<a href="g">z</a> -> "http://a/b/c/g"
<a href="./g">z</a> -> "http://a/b/c/g"
<a href="g/">z</a> -> "http://a/b/c/g/"
<a href="/g">z</a> -> "http://a/g"
<a href="//g">z</a> -> "http://g"
<a href="?y">z</a> -> "http://a/b/c/d;p?y"
<a href="g?y">z</a> -> "http://a/b/c/g?y"
<a href="#s">z</a> -> "http://a/b/c/d;p?q#s"
<a href="g#s">z</a> -> "http://a/b/c/g#s"
<a href="g?y#s">z</a> -> "http://a/b/c/g?y#s"
<a href=";x">z</a> -> "http://a/b/c/;x"
<a href="g;x">z</a> -> "http://a/b/c/g;x"
<a href="g;x?y#s">z</a> -> "http://a/b/c/g;x?y#s"
<a href="">z</a> -> "http://a/b/c/d;p?q"
<a href=".">z</a> -> "http://a/b/c/"
<a href="./">z</a> -> "http://a/b/c/"
<a href="..">z</a> -> "http://a/b/"
<a href="../">z</a> -> "http://a/b/"
<a href="../g">z</a> -> "http://a/b/g"
<a href="../..">z</a> -> "http://a/"
<a href="../../">z</a> -> "http://a/"
<a href="../../g">z</a> -> "http://a/g"

§ Validating them all

As you saw, relative URI's can be pretty much anything, including empty string and random letters like zzz.

What do we do?

The only thing left is to try to catch bad patterns.

Conceptually, this program tells if a given string is not messed up from the perspective of relative URI pattern, as far as our imperfect algorithm sees.

Mainly: no whitespace, no repeated things (tripple slashes or double question marks), no slashes or dots after a hash, two dots must not be followed by a letter and others.

§ API - Input

isRel(str, opts)

In other words, a function which takes a string and options, a plain object.

Input argumentTypeObligatory?Description
strStringnoThe extracted value of HTML media attribute or CSS media query without @media or opening bracket.
optsPlain objectnoOptional options go here.

If input is not a string, error will be thrown.

§ Options object

options object's keyTypeObligatory?DefaultDescription
flagUpUrisWithSchemesbooleannotrueShould we yield false on URI's with scheme (https:// for example)?

§ API - Output

Program returns a clone of a plain object similar to:

{
res: false,
message: `Two consecutive hashes.`
}

or if schema-URI's are flagged up via opts.flagUpUrisWithSchemes, the message value can be null:

{
res: false,
message: null
}

or

{
res: true,
message: null
}

res is always boolean.

message is either string (error message) or null.

False res and null message happens only on schema-URI's. By checking is message set we distinguish were there real errors.

§ opts.flagUpUrisWithSchemes

When validating the URI's which can be relative (for example, href attribute values) one should use two libraries: one to check strict URI's (those with schema) and one with relative URI's (those without schema).

For example, is-url-superb opens in a new tab and this package.

If opts.flagUpUrisWithSchemes is set to true, this program will search for schemes and yield a falsy result if it detects a known <scheme>: for example mailto:.

Another challenge: URI with schema-as-error is not the same "error" — it's not a "real error". To distinguish the two we'll set result object's key message to null.

That is, seemingly correct URI will have a message null:

const isRel = require("is-relative-uri");
const str = `https://codsen.com`;
const res = isRel(str, { flagUpUrisWithSchemes: true });
console.log(JSON.stringify(res, null, 4));
// => {
// res: false,
// message: null
// }

§ Licence

MIT opens in a new tab

Copyright © 2010–2020 Roy Revelt and other contributors

Related packages:

📦 emlint 2.19.2
Pluggable email template code linter
📦 html-crush 2.0.9
Minifies HTML/CSS: valid or broken, pure or mixed with other languages
📦 string-strip-html 6.2.0
Strips HTML tags from strings. No parser, accepts mixed sources
📦 detect-is-it-html-or-xhtml 3.10.0
Answers, is the string input string more an HTML or XHTML (or neither)
📦 html-table-patcher 2.0.14
Visual helper to place templating code around table tags into correct places
📦 is-html-tag-opening 1.8.3
Is given opening bracket a beginning of a tag?
📦 is-html-attribute-closing 1.3.0
Is a character on a given index a closing of an HTML attribute?