diff --git a/eslint.config.js b/eslint.config.js index eb9853b..221a135 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,5 +6,19 @@ export default [ ignores: ['coverage', 'dist'] }, ...config, - ...ava + ...ava, + { + files: ['src/*.js'], + languageOptions: { + globals: { + window: 'readonly' + } + } + }, + { + files: ['test/*.js'], + rules: { + 'regexp/no-super-linear-backtracking': 'off' + } + } ]; diff --git a/src/routerrouter.js b/src/routerrouter.js index fee6cdf..ed9e9a6 100644 --- a/src/routerrouter.js +++ b/src/routerrouter.js @@ -10,28 +10,40 @@ const extractParameters = (route, pathname) => { // Convert a route string into a regular expression suitable for matching // against the current location's `pathname`. const routeToRegExp = route => { - // escape RegExp reserved characters - route = route.replace(/[$.|]+?/g, '\\$&') + route = route + // escape RegExp reserved characters + .replaceAll(/[$.|]+/g, '\\$&') // replace optional parameters with RegExp - .replace(/\((.+?)\)/g, '(?:$1)?') + .replaceAll(/\((.+?)\)/g, '(?:$1)?') // replace named parameters with RegExp - .replace(/:\w+/g, '([^/]+)') + .replaceAll(/:\w+/g, '([^/]+)') // replace wildcard parameters with RegExp - .replace(/\*(\w+)?/g, '(.+?)'); + .replaceAll(/\*(\w+)?/g, '(.+?)'); return new RegExp(`^${route}$`); }; export default class RouterRouter { - // Bind multiple routes to actions. - // - // new RouterRouter({ - // routes: { - // '/': () => { - // console.log('This route matches the root URL'); - // }, - // } - // }); + /** + * Bind multiple routes to actions. + * + * @param {object} options An object containing a `routes` key whose value is + * an object. Optionally contains arbitrary string keys whose values are + * functions. + * @param {object} options.routes An object whose keys are strings + * representing URLs to match against (which may contain named parameters, + * wildcards, and optional parameters). Values may be either functions or + * string values referencing keys in the `options` object. + * + * @example + * new RouterRouter({ + * routes: { + * '/': () => { + * console.log('This route matches the root URL'); + * }, + * } + * }); + */ constructor(options = {}) { this.options = options; this.location = window.location; @@ -39,18 +51,24 @@ export default class RouterRouter { const routes = this.options.routes; if (routes) { - Object.keys(routes).forEach(route => { - return this.route(route, routes[route]); - }); + for (const route of Object.keys(routes)) { + this.route(route, routes[route]); + } } } - // Manually bind a single route to an action. - // - // router.route('/search/:query/:page', function(query, page) { - // console.log(query, page); - // }); - // + /** + * Manually bind a single route to an action. + * + * @param {string|RegExp} route The URL to match. + * @param {Function} action A callback function to execute when `route` + * matches the current page's URL. + * + * @example + * router.route('/search/:query/:page', (query, page) => { + * console.log(query, page); + * }); + */ route(route, action) { const pathname = decodeURIComponent(this.location.pathname);