statiqdev / Statiq.Docs

A static documentation site generator.
https://statiq.dev/docs
Other
53 stars 8 forks source link

Feature Request: add the ability to open documentation locally #25

Open SilentSin opened 6 years ago

SilentSin commented 6 years ago

Being able to freely host on sites like github is great, but sometimes it might be nice to be able to deliver the documentation along with the product without needing to host it online.

I'm not sure what the specific problem is, but from looking at the page when I try to open it locally it looks like it's just that the links are wrong for a set of local files.

Would it be feasible to either:

mollwe commented 5 years ago

An --eject option that resolves all urls to relative ones, something like:

/output/index.html: /assets/some.js => assets/some.js /output/dir/other.html: /assets/some.js => ../assets.some.js

And so on...

I'm quite new to wyam so this is not the best solution probably but managed to get it working.

const string LOCAL_LINK_ROOT = "/__LOCAL_LINK_ROOT__/";
Settings[Keys.LinkRoot] = LOCAL_LINK_ROOT;

...

// Replaces to it can be ejected and served locally from folder
if(Settings[Keys.LinkRoot].ToString() == LOCAL_LINK_ROOT) 
{
    Pipelines.Add("Rewrite URL", 
        ReadFiles("../output/**/*.{html,css,js}"), 
        Replace(LOCAL_LINK_ROOT, (System.Text.RegularExpressions.Match match, IDocument doc) => {
            string sourceFileDir = doc.String("SourceFileDir");
            string outputPath = "/output/";

            int outputIndex = sourceFileDir.IndexOf(outputPath);

            if(outputIndex >= 0){
                var directories = sourceFileDir.Substring(outputIndex + outputPath.Length).Split('/');
                return string.Join("/", directories.Select(p => "..").Append(""));
            }

            return ""; 
        }),
        WriteFiles((IDocument doc, IExecutionContext ctx) => new FilePath(doc.String("SourceFilePath").Replace("file:///", "")))
    );
}

Actually rewrites all output html, js, css documents and replaces custom link root with relative path.

Edit 1: Two issues found with this solution so far:

Edit 2: Made separate pipeline for js and html. SearchResult: Somehow LOCAL_LINK_ROOT seems to be added twice in my search script, I don't know why but added ugly extra expression for that. Using only html file for link to work. Logo/Title: For solving index.html issue on logo/title I've check url after LOCAL_LINK_ROOT and if empty append index.html.

Pipelines.Add("Rewrite URL js", 
        ReadFiles("../output/**/*.js"), 
        Replace($@"{LOCAL_LINK_ROOT}(__LOCAL_LINK_ROOT__/)?([^""'\)]*)?", (System.Text.RegularExpressions.Match match, IDocument doc) => {
            string url = match.Groups[2].Value;
            int fileNameIndex = url.LastIndexOf('/');

            if(fileNameIndex >= 0)
            {
                return url.Substring(fileNameIndex + 1);
            }

            return url; 
        }).IsRegex(),
        WriteFiles((IDocument doc, IExecutionContext ctx) => new FilePath(doc.String("SourceFilePath").Replace("file:///", "")))
    );

    Pipelines.Add("Rewrite URL html", 
        ReadFiles("../output/**/*.html"), 
        Replace($@"{LOCAL_LINK_ROOT}([^""'\)]*)?", (System.Text.RegularExpressions.Match match, IDocument doc) => {
            string document = match.Groups[1].Value;
            string sourceFileDir = doc.String("SourceFileDir");
            string outputPath = "/output/";
            int outputIndex = sourceFileDir.IndexOf(outputPath);

            if(outputIndex >= 0)
            {
                string[] directories = sourceFileDir.Substring(outputIndex + outputPath.Length).Split('/');

                if(document == "")
                {
                    document = "index.html";
                }

                return string.Join("/", directories.Select(p => "..").Append(document));
            }

            return document;
        }).IsRegex(),
        WriteFiles((IDocument doc, IExecutionContext ctx) => new FilePath(doc.String("SourceFilePath").Replace("file:///", "")))
    );
SilentSin commented 5 years ago

Did you mean to post that over in https://github.com/Wyamio/Wyam/issues/735 instead?

daveaglick commented 5 years ago

The main thing the web server provides is support for extensionless URLs and default index pages. Both of those are controllable by setting LinkHideExtensions and LinkHideIndexPages to false. I think leaving the relative links as-is will be okay since they’ll relate back to the root output folder. Try setting those settings to false and browsing the output folder after generation and let me know if you get anywhere with that.

SilentSin commented 5 years ago

Here's what I get when I use the code posted by @mollwe (including the 2nd edit) and those settings:

Settings["LinkHideExtensions"] = false;
Settings["LinkHideIndexPages"] = false;
daveaglick commented 5 years ago

Progress!

The nav bar links to the Blog and API still don't have the file extension.

That's probably a bug that I'll need to look into in the theme.

And none of my relative links in docs pages have file extensions so they don't work either.

That's partially related to statiqdev/Statiq.Web#735. When browsing from a web server, '/foo' means the foo resource at the root of the site. In a file system, /foo usually translates to the foo file at the root of the file system, which wouldn't be correct anymore. We'd need to translate the absolute path /foo to a relative path like foo or ../../foo, etc. depending on how deep the originating page is. I'll need to think about this one...

Icons like the foldout arrows in the sidebar seem to only work when coming from the root index page, otherwise they show the generic missing image icon.

That problem is unexpected. If I recall, those icons are from Font Awesome which is being loaded at /assets/.... I would have expected the same problems as with your docs pages links and for it not to work at all. That it works on the root but not deeper is interesting.

mollwe commented 5 years ago

@SilentSin thanks for testing it, good finds of you! I didn't test thoroughly.