text: /* --------------------
   * @overlook/plugin-path module
   * Entry point
   * ------------------*/
  
  'use strict';
  
  // Modules
  const Plugin = require('@overlook/plugin'),
  	{INIT_PROPS, INIT_ROUTE, DEBUG_ERROR} = require('@overlook/route'),
  	pluginMatch = require('@overlook/plugin-match'),
  	pluginOrder = require('@overlook/plugin-order'),
  	pluginOrdered = require('@overlook/plugin-ordered'),
  	{PATH} = require('@overlook/plugin-request'),
  	{findParent} = require('@overlook/util-find-parent'),
  	{isString} = require('is-it-type'),
  	hasOwnProp = require('has-own-prop');
  
  // Imports
  const pkg = require('../package.json');
  
  // Exports
  
  const pathPlugin = new Plugin(
  	pkg,
  	{symbols: ['PATH_PART', 'GET_PATH_PART', 'URL_PATH', 'PARAMS', 'PATH_UNCONSUMED']},
  	[pluginMatch, pluginOrder, pluginOrdered],
  	(Route, {
  		PATH_PART, GET_PATH_PART, URL_PATH, PARAMS, PATH_UNCONSUMED,
  		MATCH, HANDLE_MATCH, // From @overlook/plugin-match
  		IS_BEFORE // From @overlook/plugin-ordered
  	}) => class PathRoute extends Route {
  		[INIT_PROPS](props) {
  			super[INIT_PROPS](props);
  
  			this[PATH_PART] = undefined;
  		}
  
  		/**
  		 * Determine path part.
  		 */
  		async [INIT_ROUTE]() {
  			// Call superior
  			await super[INIT_ROUTE]();
  
  			// Find path route above this one
  			const parentPathRoute = findParent(this, route => hasOwnProp(route, PATH_PART));
  
  			// Inherit path part from name (or '' if this is top path route)
  			let pathPart = this[PATH_PART];
  			if (pathPart === undefined) {
  				pathPart = this[GET_PATH_PART]();
  				if (pathPart === undefined) pathPart = parentPathRoute ? this.name : '';
  				this[PATH_PART] = pathPart;
  			}
  
  			// Check path part valid
  			if (pathPart == null) throw new Error('[plugin-path.PATH_PART] must be set on a path route');
  			if (!isString(pathPart)) throw new Error('[plugin-path.PATH_PART] must be a string');
  
  			// Construct URL path
  			let urlPath = parentPathRoute ? parentPathRoute[URL_PATH] : '';
  			if (urlPath !== '/') urlPath += '/';
  			urlPath += pathPart;
  			this[URL_PATH] = urlPath;
  		}
  
  		[MATCH](req) {
  			// TODO Call `super[MATCH]` and deal with result
  
  			// Get path to match against
  			let path = req[PATH_UNCONSUMED];
  			if (path === undefined) path = req[PATH];
  
  			// Check if path matches
  
  			// Handle root
  			const pathPart = this[PATH_PART];
  			if (pathPart === '') {
  				return {
  					exact: path === '/',
  					pathConsumed: ''
  				};
  			}
  
  			// Handle param
  			if (pathPart[0] === ':') {
  				const index = path.indexOf('/', 1);
  				if (index === -1) {
  					// Exact match
  					return {
  						exact: true,
  						pathConsumed: path,
  						params: {[pathPart.slice(1)]: path.slice(1)}
  					};
  				}
  
  				return {
  					exact: index === path.length - 1,
  					pathConsumed: path.slice(0, index),
  					params: {[pathPart.slice(1)]: path.slice(1, index)}
  				};
  			}
  
  			// Handle wildcard
  			if (pathPart[0] === '*') {
  				return {
  					exact: true,
  					pathConsumed: path,
  					params: {[pathPart === '*' ? '*' : pathPart.slice(1)]: path.slice(1)}
  				};
  			}
  
  			// Handle named match
  			if (path.slice(1, pathPart.length + 1) === pathPart) {
  				if (path.length === pathPart.length + 1) {
  					return {
  						exact: true,
  						pathConsumed: path
  					};
  				}
  
  				if (path[pathPart.length + 1] === '/') {
  					return {
  						exact: path.length === pathPart.length + 2,
  						pathConsumed: `/${pathPart}`
  					};
  				}
  			}
  
  			// No match
  			return undefined;
  		}
  
  		[HANDLE_MATCH](req, match) {
  			// Record that part consumed
  			const pathOld = req[PATH_UNCONSUMED];
  			req[PATH_UNCONSUMED] = (pathOld === undefined ? req[PATH] : pathOld)
  				.slice(match.pathConsumed.length);
  
  			// Record params
  			const {params} = match;
  			const paramsOld = req[PARAMS];
  			req[PARAMS] = {...paramsOld, ...params};
  
  			// Call superclass method
  			const ret = super[HANDLE_MATCH](req, match);
  
  			// If not handled, reverse above to leave req as it was previously
  			if (ret == null) {
  				req[PATH_UNCONSUMED] = pathOld;
  				req[PARAMS] = paramsOld;
  			}
  
  			// Return handle result
  			return ret;
  		}
  
  		[IS_BEFORE](sibling) {
  			// If super method returns a result, use it
  			const isBefore = super[IS_BEFORE](sibling);
  			if (isBefore != null) return isBefore;
  
  			// If sibling is not a path route, no preference
  			if (!hasOwnProp(sibling, PATH_PART)) return undefined;
  
  			// Determine which should go first
  			const priorityRoute = priority(this),
  				prioritySibling = priority(sibling);
  			if (priorityRoute === prioritySibling) return undefined;
  			return priorityRoute < prioritySibling;
  		}
  
  		/**
  		 * Get path part.
  		 * This is only called if `[PATH_PART]` is undefined.
  		 * Intended to be overriden in subclasses
  		 */
  		[GET_PATH_PART]() { // eslint-disable-line class-methods-use-this
  			return undefined;
  		}
  
  		/**
  		 * Add URL path to error messages.
  		 * @param {Error} err - Error
  		 * @returns {Error} - Error with extra props
  		 */
  		[DEBUG_ERROR](err) {
  			err = super[DEBUG_ERROR](err);
  			const urlPath = this[URL_PATH];
  			if (urlPath) {
  				err[URL_PATH] = urlPath;
  				err.message += ` (URL path ${urlPath})`;
  			}
  			return err;
  		}
  	}
  );
  
  // Re-export PATH symbol from @overlook/plugin-request
  pathPlugin.PATH = PATH;
  
  module.exports = pathPlugin;
  
  // Helper functions
  
  const {PATH_PART} = pathPlugin;
  function priority(route) {
  	const pathPart = route[PATH_PART];
  	if (!pathPart) return 4;
  
  	const partFirstChar = pathPart[0];
  	if (partFirstChar === '*') return 3;
  	if (partFirstChar === ':') return 2;
  	return 1;
  }
  