@@ -78,12 +78,29 @@ export function mapGetOrInsert<K, V>(map: Map<K, V>, key: K, defaultValue: V): V
7878
7979export function cwdFilePathToURL ( path : string , base : string = document . baseURI ) : URL {
8080 let url = new URL ( base ) ;
81+ // Implicitly percent-encodes and doesn't trim path on the ends.
8182 url . pathname = path ;
8283 return url ;
8384}
8485
8586export function cwdFilePathFromURL ( url : URL ) : string {
86- return paths . stripRoot ( decodeURI ( url . pathname ) ) ;
87+ // Why use decodeURIComponent here instead of decodeURI, you might ask? You
88+ // see, the crucial difference between the two is that decodeURIComponent
89+ // decodes every percent-encoded character without exceptions, while
90+ // decodeURI, as the ES262 specification says, does not decode "escape
91+ // sequences that could not have been introduced by encodeURI", which, in
92+ // practice, means that it doesn't decode percent-encodings of these
93+ // characters: ;/?:@&=+$,# . Now, how is this behavior useful in practice?
94+ // Perhaps the intended usecase is to make URLs look nicer by decoding stuff
95+ // like Unicode characters, yet preserving the validity and meaning of the
96+ // original URL. But it may lead to obscure bugs in handling file paths
97+ // because, albeit seldom used, all of the mentioned characters, with the
98+ // exception of slash, are valid in filenames on UNIX, and, except colon and
99+ // question mark, the rest is also valid on Windows, and so those characters
100+ // will be kept undecoded. Here are some specification links:
101+ // <https://tc39.es/ecma262/multipage/global-object.html#sec-decodeuri-encodeduri>
102+ // <https://tc39.es/ecma262/multipage/global-object.html#sec-decodeuricomponent-encodeduricomponent>
103+ return paths . stripRoot ( decodeURIComponent ( url . pathname ) ) ;
87104}
88105
89106export function html < K extends keyof HTMLElementTagNameMap > (
0 commit comments