1
- // Copyright 2021 The Hugo Authors. All rights reserved.
1
+ // Copyright 2024 The Hugo Authors. All rights reserved.
2
2
//
3
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
4
// you may not use this file except in compliance with the License.
@@ -159,13 +159,104 @@ func Uglify(in string) string {
159
159
return path .Clean (in )
160
160
}
161
161
162
+ // URLEscape escapes unicode letters.
163
+ func URLEscape (uri string ) string {
164
+ // escape unicode letters
165
+ u , err := url .Parse (uri )
166
+ if err != nil {
167
+ panic (err )
168
+ }
169
+ return u .String ()
170
+ }
171
+
172
+ // TrimExt trims the extension from a path..
173
+ func TrimExt (in string ) string {
174
+ return strings .TrimSuffix (in , path .Ext (in ))
175
+ }
176
+
177
+ // From https://github.com/golang/go/blob/e0c76d95abfc1621259864adb3d101cf6f1f90fc/src/cmd/go/internal/web/url.go#L45
178
+ func UrlFromFilename (filename string ) (* url.URL , error ) {
179
+ if ! filepath .IsAbs (filename ) {
180
+ return nil , fmt .Errorf ("filepath must be absolute" )
181
+ }
182
+
183
+ // If filename has a Windows volume name, convert the volume to a host and prefix
184
+ // per https://blogs.msdn.microsoft.com/ie/2006/12/06/file-uris-in-windows/.
185
+ if vol := filepath .VolumeName (filename ); vol != "" {
186
+ if strings .HasPrefix (vol , `\\` ) {
187
+ filename = filepath .ToSlash (filename [2 :])
188
+ i := strings .IndexByte (filename , '/' )
189
+
190
+ if i < 0 {
191
+ // A degenerate case.
192
+ // \\host.example.com (without a share name)
193
+ // becomes
194
+ // file://host.example.com/
195
+ return & url.URL {
196
+ Scheme : "file" ,
197
+ Host : filename ,
198
+ Path : "/" ,
199
+ }, nil
200
+ }
201
+
202
+ // \\host.example.com\Share\path\to\file
203
+ // becomes
204
+ // file://host.example.com/Share/path/to/file
205
+ return & url.URL {
206
+ Scheme : "file" ,
207
+ Host : filename [:i ],
208
+ Path : filepath .ToSlash (filename [i :]),
209
+ }, nil
210
+ }
211
+
212
+ // C:\path\to\file
213
+ // becomes
214
+ // file:///C:/path/to/file
215
+ return & url.URL {
216
+ Scheme : "file" ,
217
+ Path : "/" + filepath .ToSlash (filename ),
218
+ }, nil
219
+ }
220
+
221
+ // /path/to/file
222
+ // becomes
223
+ // file:///path/to/file
224
+ return & url.URL {
225
+ Scheme : "file" ,
226
+ Path : filepath .ToSlash (filename ),
227
+ }, nil
228
+ }
229
+
230
+ // UrlStringToFilename converts a URL string to a filename.
231
+ // Returns the filename and a boolean indicating success.
232
+ // If ParseRequestURI fails, the input is just converted to OS specific slashes and returned.
233
+ func UrlStringToFilename (s string ) (string , bool ) {
234
+ s = fileURLSchemeReplacer .Replace (s )
235
+ u , err := url .ParseRequestURI (s )
236
+ if err != nil {
237
+ return filepath .FromSlash (s ), false
238
+ }
239
+
240
+ s1 , ok1 , reason := UrlToFilename (u )
241
+ s2 , ok2 := UrlStringToFilenameOld (s )
242
+
243
+ if s1 != s2 || ok1 != ok2 {
244
+ fmt .Printf ("%q === ==> s1: %q s2: %q reason: %q\n " , s , s1 , s2 , reason )
245
+ }
246
+ return s1 , ok1
247
+ }
248
+
162
249
// UrlToFilename converts the URL s to a filename.
163
250
// If ParseRequestURI fails, the input is just converted to OS specific slashes and returned.
164
- func UrlToFilename (s string ) (string , bool ) {
251
+ func UrlStringToFilenameOld (s string ) (string , bool ) {
165
252
u , err := url .ParseRequestURI (s )
166
253
if err != nil {
167
254
return filepath .FromSlash (s ), false
168
255
}
256
+ u .Scheme = fileURLSchemeReplacer .Replace (u .Scheme )
257
+ if u .Scheme != "file" {
258
+ return "" , false
259
+ }
169
260
170
261
p := u .Path
171
262
@@ -184,12 +275,32 @@ func UrlToFilename(s string) (string, bool) {
184
275
return p , true
185
276
}
186
277
187
- // URLEscape escapes unicode letters.
188
- func URLEscape (uri string ) string {
189
- // escape unicode letters
190
- u , err := url .Parse (uri )
278
+ // hugostdin is used in the Dart Sass integration.
279
+ var fileURLSchemeReplacer = strings .NewReplacer ("hugostdin" , "file" )
280
+
281
+ // Based on https://github.com/golang/go/blob/e0c76d95abfc1621259864adb3d101cf6f1f90fc/src/cmd/go/internal/web/url.go#L45
282
+ func UrlToFilename (u * url.URL ) (string , bool , string ) {
283
+ if u .Scheme != "file" {
284
+ return "" , false , "invalid scheme"
285
+ }
286
+
287
+ checkAbs := func (path string ) (string , bool , string ) {
288
+ if ! filepath .IsAbs (path ) {
289
+ return "" , false , "path is not absolute"
290
+ }
291
+ return path , true , ""
292
+ }
293
+
294
+ if u .Path == "" {
295
+ if u .Host != "" || u .Opaque == "" {
296
+ return "" , false , "invalid URL"
297
+ }
298
+ return checkAbs (filepath .FromSlash (u .Opaque ))
299
+ }
300
+
301
+ path , err := convertFileURLPath (u .Host , u .Path )
191
302
if err != nil {
192
- panic ( err )
303
+ return path , false , err . Error ( )
193
304
}
194
- return u . String ( )
305
+ return checkAbs ( path )
195
306
}
0 commit comments