ionide / Fornax

Scriptable static site generator using type safe F# DSL to define page templates.
MIT License
241 stars 44 forks source link

generate wrong path when a file within a folder with space #99

Open newbienewbie opened 2 years ago

newbienewbie commented 2 years ago

Describe the bug

A post folder path that contains a space character (or non-ascii char) will cause an error of System.Collections.Generic.KeyNotFoundException

To Reproduce Steps to reproduce the behaviour:

  1. Run fornax new
  2. create a markdown file with path of "posts/subdir/a sub folder with space/post3 with space and 你好.md" :
    ├─ posts/
    │  └─ subdir/
    │      └─ a sub folder with space/
    |               └─ post3 with space and 你好.md
  3. Run fornax watch. Now we get an error :

An unexpected error happend: System.IO.DirectoryNotFoundException: Could not find a part of the path 'F:\Playground\Fornax\posts\subdir\a%20sub%20folder%20with%20space\post3%20with%20space%20and%20%E4%BD%A0%E5%A5%BD.md'. at System.IO.FileStream.ValidateFileHandle(SafeFileHandle fileHandle) at System.IO.FileStream.CreateFileOpenHandle(FileMode mode, FileShare share, FileOptions options) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options) at System.IO.StreamReader.ValidateArgsAndOpenPath(String path, Encoding encoding, Int32 bufferSize) at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks) at System.IO.File.InternalReadAllText(String path, Encoding encoding) at System.IO.File.ReadAllText(String path) at FSI_0001.Config.postPredicate(String projectRoot, String page) at FSI_0001.Config.config@37.Invoke(Tuple2 tupledArg) at Microsoft.FSharp.Collections.ListModule.TryFind[T](FSharpFunc2 predicate, FSharpList`1 list) in F:\workspace_work\1\s\src\fsharp\FSharp.Core\list.fs:line 391 at Generator.pickGenerator(Config cfg, SiteContents siteContent, String projectRoot, String page) in F:\Source Code Study\Fornax\src\Fornax\Generator.fs:line 302 at Generator.generate(FsiEvaluationSession fsi, Config cfg, SiteContents siteContent, String projectRoot, String page) in F:\Source Code Study\Fornax\src\Fornax\Generator.fs:line 333 at Generator.action@1-3(String projectRoot, FsiEvaluationSession fsi, Config config, SiteContents sc, String filePath) in F:\Source Code Study\Fornax\src\Fornax\Generator.fs:line 496 at Generator.generateFolder(String projectRoot, Boolean isWatch) in F:\Source Code Study\Fornax\src\Fornax\Generator.fs:line 495 at Fornax.guardedGenerate@226(String cwd, Unit unitVar0) in F:\Source Code Study\Fornax\src\Fornax\Fornax.fs:line 228

Expected behaviour

The static files will be generated and the HTTP server will run flawlessly. Screenshots

image

Environment (please complete the following information):

Additional context

the relative function calculates the relative path by using the MakeRelativeUri method:

    let relative toPath fromPath =
        let toUri = Uri(toPath)
        let fromUri = Uri(fromPath)
        toUri.MakeRelativeUri(fromUri).OriginalString

However, when a directory path contains an space char, the result will not be decoded, and then causes an Exception of :

An unexpected error happend: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. System.Collections.Generic.KeyNotFoundException

Actually, this exception also occurs when having any directory that uses non-ascii encodings. For example, if we have a post with path of posts/subdir/中文 目录-带空格和非URL字符/post 带空格3.md, the relative funtion will return:

posts/subdir/%E4%B8%AD%E6%96%87%20%E7%9B%AE%E5%BD%95-%E5%B8%A6%E7%A9%BA%E6%A0%BC%E5%92%8C%E9%9D%9EURL%E5%AD%97%E7%AC%A6/post%20%E5%B8%A6%E7%A9%BA%E6%A0%BC3.md

And the above path will be passed to the generate function as a file path. However, the encoded url is not the original path.

A quick fix is to use the Path.GetRelativePath as below :

    let relative toPath fromPath =
        Path.GetRelativePath(toPath, fromPath).Replace("\\","/")