flask-urls
Version:
Provides a function to build Flask URLs on the client side.
202 lines (153 loc) • 6.33 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.mockFlaskURL = exports.default = void 0;
var _qs = _interopRequireDefault(require("qs"));
var _difference = _interopRequireDefault(require("lodash/difference"));
var _isArray = _interopRequireDefault(require("lodash/isArray"));
var _isEmpty = _interopRequireDefault(require("lodash/isEmpty"));
var _isObject = _interopRequireDefault(require("lodash/isObject"));
var _pick = _interopRequireDefault(require("lodash/pick"));
var _unzip = _interopRequireDefault(require("lodash/unzip"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
const makeBuildError = (endpoint, params) => {
return new Error("Could not build URL for endpoint '".concat(endpoint, "' (").concat(JSON.stringify(params), ")"));
};
const builtinConverters = {
PathConverter(value) {
// Generate unescaped forward slashes, just like in Flask
return value.split('/').map(encodeURIComponent).join('/');
}
};
const splitObj = obj => {
const [names, values] = (0, _isEmpty.default)(obj) ? [[], []] : (0, _unzip.default)(Object.entries(obj));
return {
names,
values,
original: obj
};
};
const suitable = (rule, args) => {
// Checks if a rule is suitable for the given arguments
const defaultArgs = splitObj(rule.defaults || {});
const diffArgNames = (0, _difference.default)(rule.args, defaultArgs.names); // If a rule arg that has no default value is missing, the rule is not suitable
for (let i = 0; i < diffArgNames.length; i++) {
if (args.names.indexOf(diffArgNames[i]) === -1) {
return false;
}
}
if ((0, _difference.default)(rule.args, args.names).length === 0) {
if (!rule.defaults) {
return true;
} // If a default argument is provided with a different value, the rule is not suitable
for (let i = 0; i < defaultArgs.names.length; i++) {
const key = defaultArgs.names[i];
const value = defaultArgs.values[i];
if (value !== args.original[key]) {
return false;
}
}
}
return true;
};
const build = (rule, args, converters) => {
let tmp = [];
const processed = rule.args.slice();
for (const part of rule.trace) {
if (part.isDynamic) {
const converter = converters[rule.converters[part.data]] || encodeURIComponent;
const value = converter(args.original[part.data]);
if (value === null) {
return null;
}
tmp.push(value);
processed.push(part.name);
} else {
tmp.push(part.data);
}
}
tmp = tmp.join('');
const pipe = tmp.indexOf('|'); // if we had subdomain routes, the subdomain would come before the pipe
const url = tmp.substring(pipe + 1);
const unprocessed = (0, _difference.default)(args.names, processed);
return {
url,
unprocessed
};
};
const fixParams = (endpoint, params) => {
const cleanParams = Object.create(null);
Object.entries(params).forEach((_ref) => {
let [key, value] = _ref;
if (value === '') {
return;
}
if (value === undefined || value === null) {
// convert them to a string
value = '' + value;
}
if ((0, _isObject.default)(value) && !(0, _isArray.default)(value)) {
throw makeBuildError(endpoint, params);
}
cleanParams[key] = value;
});
return cleanParams;
};
const buildFlaskURL = function buildFlaskURL(template) {
let base = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
let params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
let fragment = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '';
let converters = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
converters = _objectSpread({}, builtinConverters, {}, converters);
let qsParams, url;
params = fixParams(template.endpoint, params);
const args = splitObj(params);
for (const rule of template.rules) {
if (suitable(rule, args)) {
const res = build(rule, args, converters);
if (res === null) {
continue;
}
url = res.url;
qsParams = (0, _pick.default)(params, res.unprocessed);
break;
}
}
if (!url) {
throw makeBuildError(template.endpoint, params);
}
url = base.replace(/\/$/, '') + url;
if (!(0, _isEmpty.default)(qsParams)) {
url += '?' + _qs.default.stringify(qsParams, {
arrayFormat: 'repeat'
});
}
if (fragment) {
url += '#' + fragment.replace(/^#/, '');
}
return url;
};
var _default = buildFlaskURL;
exports.default = _default;
const mockFlaskURL = function mockFlaskURL(endpoint) {
let params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
let fragment = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
const argstr = Object.entries(params).map((_ref2) => {
let [k, v] = _ref2;
return "".concat(k, "=").concat(v);
}).sort().join('/');
const parts = ['flask://', endpoint];
if (argstr) {
parts.push('/');
parts.push(argstr);
}
if (fragment) {
parts.push('#' + fragment);
}
return parts.join('');
};
exports.mockFlaskURL = mockFlaskURL;