es6-url-template
Version:
This is a simple URI template implementation following the [RFC 6570 URI Template specification](http://tools.ietf.org/html/rfc6570). The implementation supports all levels defined in the specification and is extensively tested.
150 lines (125 loc) • 4.05 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
function encodeReserved(str) {
return str.split(/(%[0-9A-Fa-f]{2})/g).map(part => {
if (!/%[0-9A-Fa-f]/.test(part)) {
part = encodeURI(part).replace(/%5B/g, '[').replace(/%5D/g, ']');
}
return part;
}).join('');
}
function encodeUnreserved(str) {
return encodeURIComponent(str).replace(/[!'()*]/g, c => '%' + c.charCodeAt(0).toString(16).toUpperCase());
}
function encodeValue(operator, value, key) {
value = operator === '+' || operator === '#' ? encodeReserved(value) : encodeUnreserved(value);
if (key) {
return encodeUnreserved(key) + '=' + value;
} else {
return value;
}
}
function isDefined(value) {
return value !== undefined && value !== null;
}
function isKeyOperator(operator) {
return operator === ';' || operator === '&' || operator === '?';
}
function getValues(context, operator, key, modifier) {
let value = context[key],
result = [];
if (isDefined(value) && value !== '') {
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
value = value.toString();
if (modifier && modifier !== '*') {
value = value.substring(0, parseInt(modifier, 10));
}
result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : null));
} else {
if (modifier === '*') {
if (Array.isArray(value)) {
value.filter(isDefined).forEach(value => {
result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : null));
});
} else {
Object.keys(value).forEach(k => {
if (isDefined(value[k])) {
result.push(encodeValue(operator, value[k], k));
}
});
}
} else {
const tmp = [];
if (Array.isArray(value)) {
value.filter(isDefined).forEach(value => {
tmp.push(encodeValue(operator, value));
});
} else {
Object.keys(value).forEach(k => {
if (isDefined(value[k])) {
tmp.push(encodeUnreserved(k));
tmp.push(encodeValue(operator, value[k].toString()));
}
});
}
if (isKeyOperator(operator)) {
result.push(encodeUnreserved(key) + '=' + tmp.join(','));
} else if (tmp.length !== 0) {
result.push(tmp.join(','));
}
}
}
} else {
if (operator === ';') {
if (isDefined(value)) {
result.push(encodeUnreserved(key));
}
} else if (value === '' && (operator === '&' || operator === '?')) {
result.push(encodeUnreserved(key) + '=');
} else if (value === '') {
result.push('');
}
}
return result;
}
const operators = ['+', '#', '.', '/', ';', '?', '&'];
class UrlTemplate {
constructor(template) {
Object.defineProperty(this, 'template', {
get: () => template
});
}
expand(context) {
return this.template.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g, (_, expression, literal) => {
if (expression) {
let operator = null,
values = [];
if (operators.indexOf(expression.charAt(0)) !== -1) {
operator = expression.charAt(0);
expression = expression.substr(1);
}
expression.split(/,/g).forEach(variable => {
const tmp = /([^:\*]*)(?::(\d+)|(\*))?/.exec(variable);
values.push.apply(values, getValues(context, operator, tmp[1], tmp[2] || tmp[3]));
});
if (operator && operator !== '+') {
let separator = ',';
if (operator === '?') {
separator = '&';
} else if (operator !== '#') {
separator = operator;
}
return (values.length !== 0 ? operator : '') + values.join(separator);
} else {
return values.join(',');
}
} else {
return encodeReserved(literal);
}
});
}
}
exports.default = UrlTemplate;