n3
Version:
Lightning fast, asynchronous, streaming Turtle / N3 / RDF library.
93 lines (84 loc) • 3.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _Util = require("./Util");
// Do not handle base IRIs without scheme, and currently unsupported cases:
// - file: IRIs (which could also use backslashes)
// - IRIs containing /. or /.. or //
const BASE_UNSUPPORTED = /^:?[^:?#]*(?:[?#]|$)|^file:|^[^:]*:\/*[^?#]+?\/(?:\.\.?(?:\/|$)|\/)/i;
const SUFFIX_SUPPORTED = /^(?:(?:[^/?#]{3,}|\.?[^/?#.]\.?)(?:\/[^/?#]{3,}|\.?[^/?#.]\.?)*\/?)?(?:[?#]|$)/;
const CURRENT = './';
const PARENT = '../';
const QUERY = '?';
const FRAGMENT = '#';
class BaseIRI {
constructor(base) {
this.base = base;
this._baseLength = 0;
this._baseMatcher = null;
this._pathReplacements = new Array(base.length + 1);
}
static supports(base) {
return !BASE_UNSUPPORTED.test(base);
}
_getBaseMatcher() {
if (this._baseMatcher) return this._baseMatcher;
if (!BaseIRI.supports(this.base)) return this._baseMatcher = /.^/;
// Extract the scheme
const scheme = /^[^:]*:\/*/.exec(this.base)[0];
const regexHead = ['^', (0, _Util.escapeRegex)(scheme)];
const regexTail = [];
// Generate a regex for every path segment
const segments = [],
segmenter = /[^/?#]*([/?#])/y;
let segment,
query = 0,
fragment = 0,
last = segmenter.lastIndex = scheme.length;
while (!query && !fragment && (segment = segmenter.exec(this.base))) {
// Truncate base resolution path at fragment start
if (segment[1] === FRAGMENT) fragment = segmenter.lastIndex - 1;else {
// Create regex that matches the segment
regexHead.push((0, _Util.escapeRegex)(segment[0]), '(?:');
regexTail.push(')?');
// Create dedicated query string replacement
if (segment[1] !== QUERY) segments.push(last = segmenter.lastIndex);else {
query = last = segmenter.lastIndex;
fragment = this.base.indexOf(FRAGMENT, query);
this._pathReplacements[query] = QUERY;
}
}
}
// Precalculate parent path substitutions
for (let i = 0; i < segments.length; i++) this._pathReplacements[segments[i]] = PARENT.repeat(segments.length - i - 1);
this._pathReplacements[segments[segments.length - 1]] = CURRENT;
// Add the remainder of the base IRI (without fragment) to the regex
this._baseLength = fragment > 0 ? fragment : this.base.length;
regexHead.push((0, _Util.escapeRegex)(this.base.substring(last, this._baseLength)), query ? '(?:#|$)' : '(?:[?#]|$)');
return this._baseMatcher = new RegExp([...regexHead, ...regexTail].join(''));
}
toRelative(iri) {
// Unsupported or non-matching base IRI
const match = this._getBaseMatcher().exec(iri);
if (!match) return iri;
// Exact base IRI match
const length = match[0].length;
if (length === this._baseLength && length === iri.length) return '';
// Parent path match
const parentPath = this._pathReplacements[length];
if (parentPath) {
const suffix = iri.substring(length);
// Don't abbreviate unsupported path
if (parentPath !== QUERY && !SUFFIX_SUPPORTED.test(suffix)) return iri;
// Omit ./ with fragment or query string
if (parentPath === CURRENT && /^[^?#]/.test(suffix)) return suffix;
// Append suffix to relative parent path
return parentPath + suffix;
}
// Fragment or query string, so include delimiter
return iri.substring(length - 1);
}
}
exports.default = BaseIRI;