fix(url_decode): keep %2B as a literal plus when decoding (#939)

url_decode decoded the percent-encoding first and only then replaced
"+" with a space, so a "%2B" became "+" and was immediately turned into
a space. Any literal "+" was therefore lost when round-tripped through
url_encode. I now replace "+" with a space before decodeURIComponent,
which lines up with Ruby's CGI.unescape used by Shopify.
This commit is contained in:
Sarath Francis
2026-09-06 19:16:16 +08:00
committed by GitHub
parent 747bdbdbee
commit 9af92f5d8c
2 changed files with 9 additions and 1 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
import { stringify } from '../util/underscore'
export const url_decode = (x: string) => decodeURIComponent(stringify(x)).replace(/\+/g, ' ')
export const url_decode = (x: string) => decodeURIComponent(stringify(x).replace(/\+/g, ' '))
export const url_encode = (x: string) => encodeURIComponent(stringify(x)).replace(/%20/g, '+')
export const cgi_escape = (x: string) => encodeURIComponent(stringify(x))
.replace(/%20/g, '+')
+8
View File
@@ -7,6 +7,14 @@ describe('filters/url', () => {
const html = liquid.parseAndRenderSync('{{ "%27Stop%21%27+said+Fred" | url_decode }}')
expect(html).toEqual("'Stop!' said Fred")
})
it('should decode %2B to a literal plus', () => {
const html = liquid.parseAndRenderSync('{{ "1%2B1" | url_decode }}')
expect(html).toEqual('1+1')
})
it('should keep a literal plus when round-tripped through url_encode', () => {
const html = liquid.parseAndRenderSync('{{ "a+b c" | url_encode | url_decode }}')
expect(html).toEqual('a+b c')
})
})
describe('url_encode', () => {