refactor!: UrlSearchParams based Hash#7073
refactor!: UrlSearchParams based Hash#7073CommanderStorm wants to merge 27 commits intomaplibre:mainfrom
UrlSearchParams based Hash#7073Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7073 +/- ##
==========================================
- Coverage 92.62% 92.34% -0.28%
==========================================
Files 289 289
Lines 24013 23996 -17
Branches 5086 5083 -3
==========================================
- Hits 22241 22160 -81
- Misses 1772 1836 +64 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
17f4a79 to
18ab1a8
Compare
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This reverts commit 80f0973.
| // Manually build the string to avoid URL encoding the hash value | ||
| return `#${this._buildHashString(params)}`; |
There was a problem hiding this comment.
If the issue is only that / gets escaped despite being legal in the URL fragment, would replacing %2F have the desired effect?
return params.toString().replaceAll('%2F', '/');Otherwise, unescaping the string should be equivalent, avoiding the need to build a string manually:
return decodeURIComponent(params.toString());| map.setCenter([2.0, 1.0]); | ||
|
|
||
| expect(window.location.hash).toBe('#baz&map=7/1/2/135/60&foo=bar'); | ||
| expect(window.location.hash).toBe('#baz=&map=7/1/2/135/60&foo=bar'); |
There was a problem hiding this comment.
Why was this changed? Is it possible that this might be a "breaking change"?
There was a problem hiding this comment.
I have no clue.
This change is the result of simplifying _buildHashString to decodeURIComponent(params.toString())
https://github.com/maplibre/maplibre-gl-js/pull/7073/changes/578887c634715eb49f2bcc8bad205041381c0017..615b59c48a350538bdfeb4f76ccbe3a1900bd617
There was a problem hiding this comment.
Apparently foo= is the canonical form of foo. URLSearchParams doesn’t distinguish. I’m pretty sure this is consistent with how most software interprets query strings, but here we’re reusing query strings as fragments. Technically we get to decide the correct behavior of flag parameters. As long as we use URLSearchParams for both parsing and reconstructing the fragment, it shouldn’t cause any problems but could trip up tests like this one.
| .addTo(map); | ||
|
|
||
| // Set up hash with URL in another parameter | ||
| window.location.hash = '#map=10/3/-1&returnUrl=https://example.com&filter=a&b='; |
There was a problem hiding this comment.
What happens if you have https://example.com/&filter=a&b= instead (note the addition of "/" after .com)?
| // Update and verify encoding is preserved where needed | ||
| map.setZoom(8); | ||
|
|
||
| expect(window.location.hash).toBe('#map=8/3/-1&foo=bar'); |
There was a problem hiding this comment.
This doesn't look like it preserves the "%2F", am I missing anything?
There was a problem hiding this comment.
No, URL encoding is getting removed
There was a problem hiding this comment.
I think it makes sense to continue to unescape %2F, since it gets annoying in the usual map= parameter. Slashes in the URL fragment don’t need to be escaped anyhow. However, other encoding might be useful. The class is written generically enough that a parameter could end up with freeform text containing a & that needs to be escaped. My other suggestion in #7073 (comment) would’ve solved this problem, prettifying %2F as / but leaving all the other escaping in place.
There was a problem hiding this comment.
The other character to watch out for is the space, which URLSearchParams encodes as + rather than %20.
|
Lets split adding tests and changing behaviour, this way the actual changes are more clear |
Co-authored-by: Minh Nguyễn <mxn@1ec5.org>
Co-authored-by: Minh Nguyễn <mxn@1ec5.org>
|
I'm a bit lost with all these commits and review comments, please ping me when a new review is required. |
|
@HarelM the PR is done, sorry for the messy history, not my best work. So basically, this PR boils down to "how should this be normalized and should this be normalized?"
We can aso put this into v6 |
UrlSearchParams based HashUrlSearchParams based Hash
|
I think using urlsearchpatams is important, but this is a subtle breaking change. |
|
I've added it to the list. |
|
We can easily avoid the breaking change if desired with a one-liner. That was the question in #7073 (comment). |
|
I might have miss read it then, avoiding a breaking change would be better, we can introduce the breaking change later if it's only a single line change. |
|
I don't think this can be done without a subtle behaviour change if we want to use |
| window.location.hash = '#10%2F3.00%2F-1.00'; | ||
|
|
||
| expect(hash._isValidHash(hash._getCurrentHash())).toBeFalsy(); | ||
| expect(hash._isValidHash(hash._getCurrentHash())).toBeTruthy(); |
There was a problem hiding this comment.
I’d characterize this as a routine bug fix rather than a backwards-incompatible change. I don’t think anyone would be expecting the %2F to persist, and we continue to recognize it anyways.
There was a problem hiding this comment.
A case where this is needed for example is if you have a link to a file or location as part of the query parameters, then you can't use "/" but you have to use the encoded way. I'm not sure this answers any question, but I wanted to give a use case.
If this is kept the same and doesn't break, that's great.
| } | ||
|
|
||
| _getHashParams = () => { | ||
| return new URLSearchParams(window.location.hash.replace('#', '')); |
There was a problem hiding this comment.
If hash is set, its first character is guaranteed to be #. The previous code only removed the leading #, whereas this removes any occurrence.
|
|
||
| map.setZoom(5); | ||
| expect(window.location.hash).toBe('#map=5/3/-1&empty='); | ||
| expect(window.location.hash).toBe('#map=5/3/-1&empty'); |
There was a problem hiding this comment.
whatwg/url#427 led me down quite a rabbit hole of discussions…
As I mentioned earlier, I think we could have leeway to make this change. The fact that our hashes generally conform to the query string syntax is just a convention on our part. I also don’t think we necessarily need to hold the hash behavior to the same standard as a formal public API, since Hash isn’t publicly exposed by any APIs yet.
That said, if this is a source of concern nonetheless, anything calling _getHashParams() would need to separately look for flags:
const hash = window.location.hash.slice(1);
const params = new URLSearchParams(hash);
const flags = [...hash.matchAll(/(?<=^|&)(\w+)(?=&|$)/g).map(p => p[1])];
// …
const result = [...params.entries().map(([k, v]) => flags.includes(k) ? k : `${k}=${v}`)].join('&');
return `#${result}`;Kind of cryptic…
There was a problem hiding this comment.
The question is who is relaying on this functionality to work this way, is somewhat a concern I have. The methods in the hash my not be public, setting it as part on map initialization is public and reading the address bar value is public as well.
I'm just saying that waiting a bit longer for version 6 might be safer for this.
It might be regarded as a bug fix though if we want to cut ourselves some slack 😀
|
I'm a bit lost, where are we with this PR? Waiting for version 6? |
|
Yes, I think that is the best bet. |
Launch Checklist
This PR refactors
Hashto be based onUrlSearchParams.Tests were pulled to another PR.
CC @1ec5
Resolves #7006