-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Client-only routes: catch-all matchPath has higher "specificity" than static paths #9705
Comments
Hey, this definitely is not working when there are multiple "nested" We would need to change this sorting
/sub/* would end up before /* (but still keep consistent order between builds).
Here's the input pages we need to sort and we want end result to have last 2 entries swapped so least specific [
{
"path": "/404.html"
},
{
"path": "/404/"
},
{
"path": "/page-2/"
},
{
"path": "/",
"matchPath": "/*"
},
{
"path": "/sub/",
"matchPath": "/sub/*"
}
] |
Hi @pieh , Ah, that makes sense. I quickly played around with this and I think this could be a fix for pages-writer.js: replace
with
In my tests this orders less specific matchPaths after more specific ones. If this makes sense, I'd be happy to try create a PR for this. Thanks so far!! |
Potentially there is some scanario where this will fail with named URL parameter (not sure if we handle this in gatsby, but @reach/router support
will put first one before second one meaning we won't be able to access |
Ah, sure, I didn't think of this. This should work (but might be overcomplicating…):
|
I don't think this is overcomplicating. And this looks good - maybe even we could combine const results = _(input)
// Ensure pages keep the same sorting through builds
// and sort pages with matchPath to end so explicit routes
// will match before general.
//.sortBy(p => `${p.matchPath ? 1 : 0}${p.path}`)
.map(p => ({
...p,
//hasMatchPath: p.matchPath ? 1 : 0,
matchPathSegments: p.matchPath ? -p.matchPath.split('/').length : 0,
}))
.orderBy(['matchPathSegments', 'path'], ['desc', 'asc'])
.map(p => _.omit(p, ['matchPathSegments']))
.value() I wonder if creating 2 intermediate arrays (with 2 |
Awesome!
If I'm not mistaken, this would result in We could do something like this, but I don't feel too good about hardcoding that number: |
We can use something like |
I tried to do some tests in order to see what impact on performance the creation of the intermediate arrays has: https://runkit.com/juliansthl/5be05a512513440012fffa3c (scroll to bottom) It looks like the sorting function with the proposed change takes around 2x as long as the original version (~16s vs. ~8s for 1000 random entries). Is there a reason the entries have to be sorted alphabetically or would it be sufficient to sort them in order of their matchPath specificity? |
I think I found another solution that doesn't come with the performance penalty and still orders the entries alphabetically: const results = _(input)
// Ensure pages keep the same sorting through builds
// and sort pages with matchPath to end so explicit routes
// will match before general.
//.sortBy(p => `${p.matchPath ? 1 : 0}${p.path}`)
.sortBy(p => `${p.matchPath
? String.fromCharCode(122 - p.matchPath.split('/').length) // 122 = 'z'
: 0
}${p.path}`)
.value() I did some tests and the ordering doesn't seem to take significantly longer than originally: This would reliably work for up to 26 "matchPathSegments", which should be ok for real world scenarios. What do you think? |
Something like this would also work (this is your previous code just without extra mapping and added function to generate sorting values for entries): const results = _(input)
.orderBy(p => [
p.matchPath ? 1 : 0,
p.matchPath && p.matchPath.split('/').length,
p.path
]
, ['asc', 'desc', 'asc'])
.value() |
Ah, nice! What is weird though, is that this operation now takes significantly longer (even longer than the one with the intermediate arrays): https://runkit.com/juliansthl/5be071af6ca8210012bcacb6 Thanks for all your help so far!! |
So after adding some benchmarks for our relatively small input: https://runkit.com/pieh/5be04dba39eeb40015e74baf your most recent implementation definitely wins out |
Awesome. Thanks for the benchmarking! |
Do we actually need to map to |
we could also format number to be 2 digits instead of mapping to a-z |
I don't think it's likely to hit 26 path fragments limit, but would be great if we didn't have such limitation |
yes, you're absolutely right. how about this? const results = _(input)
// Ensure pages keep the same sorting through builds
// and sort pages with matchPath to end so explicit routes
// will match before general.
//.sortBy(p => `${p.matchPath ? 1 : 0}${p.path}`)
.sortBy(p => `${p.matchPath
? 9999 - p.matchPath.split('/').length
: '0000'
}${p.path}`)
.value() |
As per [#9705](#9705), this changes the order of the pages in pages-writer.js in order to account for the matchPath specificity and therefore make "nested" matchPaths work. The proposed fix `sortByNumeric` is the most performant solution I could come up with: [Benchmarks](https://runkit.com/juliansthl/5be08b8f2513440012001807) Thanks a lot to @pieh for all the help! (this is my first PR, please let me know if I have to adjust anything)
As per [gatsbyjs#9705](gatsbyjs#9705), this changes the order of the pages in pages-writer.js in order to account for the matchPath specificity and therefore make "nested" matchPaths work. The proposed fix `sortByNumeric` is the most performant solution I could come up with: [Benchmarks](https://runkit.com/juliansthl/5be08b8f2513440012001807) Thanks a lot to @pieh for all the help! (this is my first PR, please let me know if I have to adjust anything)
Description
If I specify a wildcard matchPath for the root path on my index page
matchPath: '/*'
, the index page is always served, ignoring any "static" paths.Steps to reproduce
Demo on CodeSandbox
Based on gatsby-starter-default, I'm adding this to gatsby-node.js:
Expected result
/
or/foo
I would expect to see the index page ./src/pages/index.js/sub/
or/sub/foo
I would expect to see the sub page template ./src/templates/sub.jsI'm quite sure that this worked in the past and this comment from @pieh makes me believe this is the way the matching is supposed to work.
Actual result
gatsby always serves the index page ./src/pages/index.js
Environment
Thanks!!
The text was updated successfully, but these errors were encountered: