UNPKG

flask-urls

Version:

Provides a function to build Flask URLs on the client side.

202 lines (153 loc) 6.33 kB
"use strict"; 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;