create-expo-cljs-app
Version:
Create a react native application with Expo and Shadow-CLJS!
250 lines (196 loc) • 9.25 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getPathFromState;
var queryString = _interopRequireWildcard(require("query-string"));
var _checkLegacyPathConfig = _interopRequireDefault(require("./checkLegacyPathConfig"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
const getActiveRoute = state => {
const route = typeof state.index === 'number' ? state.routes[state.index] : state.routes[state.routes.length - 1];
if (route.state) {
return getActiveRoute(route.state);
}
return route;
};
/**
* Utility to serialize a navigation state object to a path string.
*
* @example
* ```js
* getPathFromState(
* {
* routes: [
* {
* name: 'Chat',
* params: { author: 'Jane', id: 42 },
* },
* ],
* },
* {
* screens: {
* Chat: {
* path: 'chat/:author/:id',
* stringify: { author: author => author.toLowerCase() }
* }
* }
* }
* )
* ```
*
* @param state Navigation state to serialize.
* @param options Extra options to fine-tune how to serialize the path.
* @returns Path representing the state, e.g. /foo/bar?count=42.
*/
function getPathFromState(state, options) {
if (state == null) {
throw Error("Got 'undefined' for the navigation state. You must pass a valid state object.");
}
const [legacy, compatOptions] = (0, _checkLegacyPathConfig.default)(options); // Create a normalized configs object which will be easier to use
const configs = compatOptions ? createNormalizedConfigs(legacy, compatOptions.screens) : {};
let path = '/';
let current = state;
const allParams = {};
while (current) {
let index = typeof current.index === 'number' ? current.index : 0;
let route = current.routes[index];
let pattern;
let focusedParams;
let focusedRoute = getActiveRoute(state);
let currentOptions = configs; // Keep all the route names that appeared during going deeper in config in case the pattern is resolved to undefined
let nestedRouteNames = [];
let hasNext = true;
while (route.name in currentOptions && hasNext) {
pattern = currentOptions[route.name].pattern;
nestedRouteNames.push(route.name);
if (route.params) {
var _currentOptions$route;
const stringify = (_currentOptions$route = currentOptions[route.name]) === null || _currentOptions$route === void 0 ? void 0 : _currentOptions$route.stringify;
const currentParams = fromEntries(Object.entries(route.params).map(([key, value]) => [key, stringify !== null && stringify !== void 0 && stringify[key] ? stringify[key](value) : String(value)]));
if (pattern) {
Object.assign(allParams, currentParams);
}
if (focusedRoute === route) {
var _pattern;
// If this is the focused route, keep the params for later use
// We save it here since it's been stringified already
focusedParams = { ...currentParams
};
(_pattern = pattern) === null || _pattern === void 0 ? void 0 : _pattern.split('/').filter(p => p.startsWith(':')) // eslint-disable-next-line no-loop-func
.forEach(p => {
const name = getParamName(p); // Remove the params present in the pattern since we'll only use the rest for query string
if (focusedParams) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete focusedParams[name];
}
});
}
} // If there is no `screens` property or no nested state, we return pattern
if (!currentOptions[route.name].screens || route.state === undefined) {
hasNext = false;
} else {
index = typeof route.state.index === 'number' ? route.state.index : route.state.routes.length - 1;
const nextRoute = route.state.routes[index];
const nestedConfig = currentOptions[route.name].screens; // if there is config for next route name, we go deeper
if (nestedConfig && nextRoute.name in nestedConfig) {
route = nextRoute;
currentOptions = nestedConfig;
} else {
// If not, there is no sense in going deeper in config
hasNext = false;
}
}
}
if (pattern === undefined) {
pattern = nestedRouteNames.join('/');
}
if (currentOptions[route.name] !== undefined) {
path += pattern.split('/').map(p => {
const name = getParamName(p); // We don't know what to show for wildcard patterns
// Showing the route name seems ok, though whatever we show here will be incorrect
// Since the page doesn't actually exist
if (p === '*') {
if (legacy) {
throw new Error("Please update your config to the new format to use wildcard pattern ('*'). https://reactnavigation.org/docs/configuring-links/#updating-config");
}
return route.name;
} // If the path has a pattern for a param, put the param in the path
if (p.startsWith(':')) {
const value = allParams[name];
if (value === undefined && p.endsWith('?')) {
// Optional params without value assigned in route.params should be ignored
return '';
}
return encodeURIComponent(value);
}
return encodeURIComponent(p);
}).join('/');
} else {
path += encodeURIComponent(route.name);
}
if (!focusedParams) {
focusedParams = focusedRoute.params;
}
if (route.state) {
path += '/';
} else if (focusedParams) {
for (let param in focusedParams) {
if (focusedParams[param] === 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete focusedParams[param];
}
}
const query = queryString.stringify(focusedParams);
if (query) {
path += "?".concat(query);
}
}
current = route.state;
} // Remove multiple as well as trailing slashes
path = path.replace(/\/+/g, '/');
path = path.length > 1 ? path.replace(/\/$/, '') : path;
return path;
} // Object.fromEntries is not available in older iOS versions
const fromEntries = entries => entries.reduce((acc, [k, v]) => {
if (acc.hasOwnProperty(k)) {
throw new Error("A value for key '".concat(k, "' already exists in the object."));
}
acc[k] = v;
return acc;
}, {});
const getParamName = pattern => pattern.replace(/^:/, '').replace(/\?$/, '');
const joinPaths = (...paths) => [].concat(...paths.map(p => p.split('/'))).filter(Boolean).join('/');
const createConfigItem = (legacy, config, parentPattern) => {
var _pattern2;
if (typeof config === 'string') {
// If a string is specified as the value of the key(e.g. Foo: '/path'), use it as the pattern
const pattern = parentPattern ? joinPaths(parentPattern, config) : config;
return {
pattern
};
} // If an object is specified as the value (e.g. Foo: { ... }),
// It can have `path` property and `screens` prop which has nested configs
let pattern;
if (legacy) {
pattern = config.exact !== true && parentPattern && config.path ? joinPaths(parentPattern, config.path) : config.path;
} else {
if (config.exact && config.path === undefined) {
throw new Error("A 'path' needs to be specified when specifying 'exact: true'. If you don't want this screen in the URL, specify it as empty string, e.g. `path: ''`.");
}
pattern = config.exact !== true ? joinPaths(parentPattern || '', config.path || '') : config.path || '';
}
const screens = config.screens ? createNormalizedConfigs(legacy, config.screens, pattern) : undefined;
return {
// Normalize pattern to remove any leading, trailing slashes, duplicate slashes etc.
pattern: (_pattern2 = pattern) === null || _pattern2 === void 0 ? void 0 : _pattern2.split('/').filter(Boolean).join('/'),
stringify: config.stringify,
screens
};
};
const createNormalizedConfigs = (legacy, options, pattern) => fromEntries(Object.entries(options).map(([name, c]) => {
const result = createConfigItem(legacy, c, pattern);
return [name, result];
}));
//# sourceMappingURL=getPathFromState.js.map
;