From 77134c33048e6b0d2d390bfcc6988b8e211e5b41 Mon Sep 17 00:00:00 2001 From: Tom Holder Date: Sun, 2 Feb 2025 15:14:15 -0800 Subject: [PATCH 1/4] fix matchesurl --- src/js/Route.js | 66 +++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/src/js/Route.js b/src/js/Route.js index d0a9e8de..8ee5a4f1 100644 --- a/src/js/Route.js +++ b/src/js/Route.js @@ -77,35 +77,47 @@ export default class Route { matchesUrl(url) { if (!this.definition.methods.includes('GET')) return false; - // Transform the route's template into a regex that will match a hydrated URL, - // by replacing its parameter segments with matchers for parameter values - const pattern = this.template - .replace(/[.*+$()[\]]/g, '\\$&') - .replace(/(\/?){([^}?]*)(\??)}/g, (_, slash, segment, optional) => { - const regex = `(?<${segment}>${ - this.wheres[segment]?.replace(/(^\^)|(\$$)/g, '') || '[^/?]+' - })`; - return optional ? `(${slash}${regex})?` : `${slash}${regex}`; - }) - .replace(/^\w+:\/\//, ''); - - const [location, query] = url.replace(/^\w+:\/\//, '').split('?'); - - const matches = - new RegExp(`^${pattern}/?$`).exec(location) ?? - new RegExp(`^${pattern}/?$`).exec(decodeURI(location)); - - if (matches) { - for (const k in matches.groups) { - matches.groups[k] = - typeof matches.groups[k] === 'string' - ? decodeURIComponent(matches.groups[k]) - : matches.groups[k]; + try { + // Store parameter names in order of appearance + const paramNames = []; + + // Transform the route's template into a regex that will match a hydrated URL, + // by replacing its parameter segments with matchers for parameter values + const pattern = this.template + .replace(/[.*+$()[\]]/g, '\\$&') + .replace(/(\/?){([^}?]*)(\??)}/g, (_, slash, segment, optional) => { + paramNames.push(segment); // Store the parameter name + const regex = `(${ + this.wheres[segment]?.replace(/(^\^)|(\$$)/g, '') || '[^/?]+' + })`; + return optional ? `(?:${slash}${regex})?` : `${slash}${regex}`; + }) + .replace(/^\w+:\/\//, ''); + + const [location, query] = url.replace(/^\w+:\/\//, '').split('?'); + + const matches = + new RegExp(`^${pattern}/?$`).exec(location) ?? + new RegExp(`^${pattern}/?$`).exec(decodeURI(location)); + + if (matches) { + // Build params object manually using the stored parameter names + const params = {}; + // Start from index 1 to skip the full match at index 0 + for (let i = 0; i < paramNames.length; i++) { + const value = matches[i + 1]; + params[paramNames[i]] = typeof value === 'string' + ? decodeURIComponent(value) + : value; + } + return { params, query: parse(query) }; } - return { params: matches.groups, query: parse(query) }; - } - return false; + return false; + } catch (error) { + console.error('Ziggy matchesUrl error:', error); + return false; + } } /** From 0aefa076c908d793cb717dbb737f768906d1ca2d Mon Sep 17 00:00:00 2001 From: Tom Holder Date: Sun, 2 Feb 2025 15:17:38 -0800 Subject: [PATCH 2/4] Revert "fix matchesurl" This reverts commit 77134c33048e6b0d2d390bfcc6988b8e211e5b41. --- src/js/Route.js | 66 ++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 39 deletions(-) diff --git a/src/js/Route.js b/src/js/Route.js index 8ee5a4f1..d0a9e8de 100644 --- a/src/js/Route.js +++ b/src/js/Route.js @@ -77,47 +77,35 @@ export default class Route { matchesUrl(url) { if (!this.definition.methods.includes('GET')) return false; - try { - // Store parameter names in order of appearance - const paramNames = []; - - // Transform the route's template into a regex that will match a hydrated URL, - // by replacing its parameter segments with matchers for parameter values - const pattern = this.template - .replace(/[.*+$()[\]]/g, '\\$&') - .replace(/(\/?){([^}?]*)(\??)}/g, (_, slash, segment, optional) => { - paramNames.push(segment); // Store the parameter name - const regex = `(${ - this.wheres[segment]?.replace(/(^\^)|(\$$)/g, '') || '[^/?]+' - })`; - return optional ? `(?:${slash}${regex})?` : `${slash}${regex}`; - }) - .replace(/^\w+:\/\//, ''); - - const [location, query] = url.replace(/^\w+:\/\//, '').split('?'); - - const matches = - new RegExp(`^${pattern}/?$`).exec(location) ?? - new RegExp(`^${pattern}/?$`).exec(decodeURI(location)); - - if (matches) { - // Build params object manually using the stored parameter names - const params = {}; - // Start from index 1 to skip the full match at index 0 - for (let i = 0; i < paramNames.length; i++) { - const value = matches[i + 1]; - params[paramNames[i]] = typeof value === 'string' - ? decodeURIComponent(value) - : value; - } - return { params, query: parse(query) }; - } + // Transform the route's template into a regex that will match a hydrated URL, + // by replacing its parameter segments with matchers for parameter values + const pattern = this.template + .replace(/[.*+$()[\]]/g, '\\$&') + .replace(/(\/?){([^}?]*)(\??)}/g, (_, slash, segment, optional) => { + const regex = `(?<${segment}>${ + this.wheres[segment]?.replace(/(^\^)|(\$$)/g, '') || '[^/?]+' + })`; + return optional ? `(${slash}${regex})?` : `${slash}${regex}`; + }) + .replace(/^\w+:\/\//, ''); + + const [location, query] = url.replace(/^\w+:\/\//, '').split('?'); - return false; - } catch (error) { - console.error('Ziggy matchesUrl error:', error); - return false; + const matches = + new RegExp(`^${pattern}/?$`).exec(location) ?? + new RegExp(`^${pattern}/?$`).exec(decodeURI(location)); + + if (matches) { + for (const k in matches.groups) { + matches.groups[k] = + typeof matches.groups[k] === 'string' + ? decodeURIComponent(matches.groups[k]) + : matches.groups[k]; + } + return { params: matches.groups, query: parse(query) }; } + + return false; } /** From c40457a78f4753c99de706e20fe9349b0d439e87 Mon Sep 17 00:00:00 2001 From: Tom Holder Date: Sun, 2 Feb 2025 16:43:58 -0800 Subject: [PATCH 3/4] Fixes unsupported safari negative lookbehind --- src/js/Route.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/js/Route.js b/src/js/Route.js index d0a9e8de..22ae56d3 100644 --- a/src/js/Route.js +++ b/src/js/Route.js @@ -77,15 +77,19 @@ export default class Route { matchesUrl(url) { if (!this.definition.methods.includes('GET')) return false; + // Store parameter names in order of appearance + const paramNames = []; + // Transform the route's template into a regex that will match a hydrated URL, // by replacing its parameter segments with matchers for parameter values const pattern = this.template .replace(/[.*+$()[\]]/g, '\\$&') .replace(/(\/?){([^}?]*)(\??)}/g, (_, slash, segment, optional) => { - const regex = `(?<${segment}>${ + paramNames.push(segment); // Store the parameter name + const regex = `(${ this.wheres[segment]?.replace(/(^\^)|(\$$)/g, '') || '[^/?]+' })`; - return optional ? `(${slash}${regex})?` : `${slash}${regex}`; + return optional ? `(?:${slash}${regex})?` : `${slash}${regex}`; }) .replace(/^\w+:\/\//, ''); @@ -96,13 +100,16 @@ export default class Route { new RegExp(`^${pattern}/?$`).exec(decodeURI(location)); if (matches) { - for (const k in matches.groups) { - matches.groups[k] = - typeof matches.groups[k] === 'string' - ? decodeURIComponent(matches.groups[k]) - : matches.groups[k]; + // Build params object manually using the stored parameter names + const params = {}; + // Start from index 1 to skip the full match at index 0 + for (let i = 0; i < paramNames.length; i++) { + const value = matches[i + 1]; + params[paramNames[i]] = typeof value === 'string' + ? decodeURIComponent(value) + : value; } - return { params: matches.groups, query: parse(query) }; + return { params, query: parse(query) }; } return false; From c55ea3c35c2d541f8c2cd64315dc794f55f503bc Mon Sep 17 00:00:00 2001 From: Tom Holder Date: Sun, 2 Feb 2025 17:22:57 -0800 Subject: [PATCH 4/4] Try catch block to supress error --- src/js/Route.js | 77 ++++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/src/js/Route.js b/src/js/Route.js index 22ae56d3..d71b0224 100644 --- a/src/js/Route.js +++ b/src/js/Route.js @@ -75,44 +75,49 @@ export default class Route { * @return {Object|false} - If this route matches, returns the matched parameters. */ matchesUrl(url) { - if (!this.definition.methods.includes('GET')) return false; - - // Store parameter names in order of appearance - const paramNames = []; - - // Transform the route's template into a regex that will match a hydrated URL, - // by replacing its parameter segments with matchers for parameter values - const pattern = this.template - .replace(/[.*+$()[\]]/g, '\\$&') - .replace(/(\/?){([^}?]*)(\??)}/g, (_, slash, segment, optional) => { - paramNames.push(segment); // Store the parameter name - const regex = `(${ - this.wheres[segment]?.replace(/(^\^)|(\$$)/g, '') || '[^/?]+' - })`; - return optional ? `(?:${slash}${regex})?` : `${slash}${regex}`; - }) - .replace(/^\w+:\/\//, ''); - - const [location, query] = url.replace(/^\w+:\/\//, '').split('?'); - - const matches = - new RegExp(`^${pattern}/?$`).exec(location) ?? - new RegExp(`^${pattern}/?$`).exec(decodeURI(location)); - - if (matches) { - // Build params object manually using the stored parameter names - const params = {}; - // Start from index 1 to skip the full match at index 0 - for (let i = 0; i < paramNames.length; i++) { - const value = matches[i + 1]; - params[paramNames[i]] = typeof value === 'string' - ? decodeURIComponent(value) - : value; + try { + if (!this.definition.methods.includes('GET')) return false; + + // Store parameter names in order of appearance + const paramNames = []; + + // Transform the route's template into a regex that will match a hydrated URL, + // by replacing its parameter segments with matchers for parameter values + const pattern = this.template + .replace(/[.*+$()[\]]/g, '\\$&') + .replace(/(\/?){([^}?]*)(\??)}/g, (_, slash, segment, optional) => { + paramNames.push(segment); // Store the parameter name + const regex = `(${ + this.wheres[segment]?.replace(/(^\^)|(\$$)/g, '') || '[^/?]+' + })`; + return optional ? `(?:${slash}${regex})?` : `${slash}${regex}`; + }) + .replace(/^\w+:\/\//, ''); + + const [location, query] = url.replace(/^\w+:\/\//, '').split('?'); + + const matches = + new RegExp(`^${pattern}/?$`).exec(location) ?? + new RegExp(`^${pattern}/?$`).exec(decodeURI(location)); + + if (matches) { + // Build params object manually using the stored parameter names + const params = {}; + // Start from index 1 to skip the full match at index 0 + for (let i = 0; i < paramNames.length; i++) { + const value = matches[i + 1]; + params[paramNames[i]] = typeof value === 'string' + ? decodeURIComponent(value) + : value; + } + return { params, query: parse(query) }; } - return { params, query: parse(query) }; - } - return false; + return false; + } catch (error) { + console.error('Ziggy matchesUrl error:', error); + return false; + } } /**