Exemple #1
0
        private async Task ComposePages(
            Section section,
            IReadOnlyCollection <NavigationItem> menu,
            DocsSiteRouter router,
            DocsMarkdownService renderer,
            Func <Path, PipeReader, Task <PipeReader> > preprocessorPipe)
        {
            var pageComposer = new PageComposer(_site, section, _cache, _output, _uiBundle, renderer);

            foreach (var pageItem in section.ContentItems.Where(ci => IsPage(ci.Key, ci.Value)))
            {
                await pageComposer.ComposePage(pageItem.Key, pageItem.Value, menu, router, preprocessorPipe);
            }

            if (section.Type != "doc")
            {
                return;
            }

            // check if section has index page
            var indexPage = section.GetContentItem("index.md");

            // if index page exists then don't generate it
            if (indexPage != null)
            {
                return;
            }

            // we need a redirect target
            var redirectToPage = section.IndexPage;
            await pageComposer.ComposeRedirectPage("index.html", redirectToPage);
        }
Exemple #2
0
        public async Task PrepareAssets(DocsSiteRouter router)
        {
            foreach (var(path, contentItem) in _uiBundle.GetContentItems(new Path[]
            {
                "**/*.js",
                "**/*.css",
                "**/*.png",
                "**/*.jpg",
                "**/*.gif"
            }))
            {
                var  xref  = new Xref(_uiBundle.Version, _uiBundle.Id, path);
                Path route = router.GenerateRoute(xref)
                             ?? throw new InvalidOperationException($"Could not generate route for '{xref}'.");

                await using var inputStream = await contentItem.File.OpenRead();

                await _output.GetOrCreateDirectory(route.GetDirectoryPath());

                var outputFile = await _output.GetOrCreateFile(route);

                var outputStream = await outputFile.OpenWrite();

                await inputStream.CopyToAsync(outputStream);
            }
        }
Exemple #3
0
        public async Task <ContentItem> ComposePage(
            Path relativePath,
            ContentItem page,
            IReadOnlyCollection <NavigationItem> menu,
            DocsSiteRouter router,
            Func <Path, PipeReader, Task <PipeReader> > preprocessorPipe)
        {
            var(partialHtmlPage, frontmatter) = await ComposePartialHtmlPage(
                relativePath,
                page,
                router,
                preprocessorPipe);

            if (frontmatter == null)
            {
                frontmatter = new PageFrontmatter
                {
                    Template = _uiBundle.DefaultTemplate
                }
            }
            ;

            var fullHtmlPage = await ComposeFullHtmlPage(partialHtmlPage, frontmatter, menu);

            return(fullHtmlPage);
        }
Exemple #4
0
        private Task <DocsMarkdownService> BuildMarkdownService(Section section, DocsSiteRouter router)
        {
            var context = new DocsMarkdownRenderingContext(_site, section, router);
            var builder = new MarkdownPipelineBuilder();

            builder.Use(new DisplayLinkExtension(context));

            return(Task.FromResult(new DocsMarkdownService(builder)));
        }
Exemple #5
0
        public IPageRenderer GetPageRenderer(string template, DocsSiteRouter router)
        {
            if (string.IsNullOrEmpty(template))
            {
                template = DefaultTemplate;
            }

            var templateHbs = _templates[template];

            return(new HandlebarsPageRenderer(templateHbs, _partials, router));
        }
Exemple #6
0
        public async Task ComposeSection(Section section)
        {
            var preprocessorPipe = BuildPreProcessors(section);
            var router           = new DocsSiteRouter(_site, section);
            var renderer         = await BuildMarkdownService(section, router);

            var menu = await ComposeMenu(section);

            await ComposeAssets(section, router);
            await ComposePages(section, menu, router, renderer, preprocessorPipe);
        }
Exemple #7
0
        private async Task ComposeIndexPage(Site site)
        {
            var redirectoToPage = site.Definition.IndexPage;

            string target = string.Empty;

            if (redirectoToPage.IsXref)
            {
                var xref          = redirectoToPage.Xref.Value;
                var targetSection = site.GetSectionByXref(xref);

                if (targetSection == null)
                {
                    throw new InvalidOperationException(
                              $"Cannot generate site index page. " +
                              $"Target section of '{redirectoToPage}' not found.");
                }

                var router = new DocsSiteRouter(site, targetSection);
                target = router.GenerateRoute(xref) ?? string.Empty;
            }
            else
            {
                target = redirectoToPage.Uri ?? string.Empty;
            }


            var generatedHtml = string.Format(
                PageComposer.RedirectPageHtml,
                site.BasePath,
                target);

            // create output dir for page
            Path targetFilePath = "index.html";

            if (targetFilePath == new Path(target))
            {
                throw new InvalidOperationException(
                          $"Cannot generate a index.html redirect page '{targetFilePath}'. " +
                          $"Redirect would point to same file as the generated file and would" +
                          "end in a endless loop");
            }

            await _output.GetOrCreateDirectory(targetFilePath.GetDirectoryPath());

            // create output file
            var outputFile = await _output.GetOrCreateFile(targetFilePath);

            await using var outputStream = await outputFile.OpenWrite();

            await using var writer = new StreamWriter(outputStream);
            await writer.WriteAsync(generatedHtml);
        }
Exemple #8
0
 public PageComposer(
     Site site,
     Section section,
     IFileSystem cache,
     IFileSystem output,
     IUiBundle uiBundle,
     DocsMarkdownService renderer)
 {
     _site                = site;
     _section             = section;
     _cache               = cache;
     _output              = output;
     _uiBundle            = uiBundle;
     _router              = new DocsSiteRouter(site, section);
     _docsMarkdownService = renderer;
 }
Exemple #9
0
        private async Task <IReadOnlyCollection <NavigationItem> > ComposeMenu(Section section)
        {
            var items = new List <NavigationItem>();

            foreach (var naviFileLink in section.Definition.Nav)
            {
                if (naviFileLink.Xref == null)
                {
                    throw new NotSupportedException("External navigation file links are not supported");
                }

                var xref = naviFileLink.Xref.Value;

                var targetSection = _site.GetSectionByXref(xref, section);

                if (targetSection == null)
                {
                    throw new InvalidOperationException($"Invalid navigation file link {naviFileLink}. Section not found.");
                }

                var navigationFileItem = targetSection.GetContentItem(xref.Path);

                if (navigationFileItem == null)
                {
                    throw new InvalidOperationException($"Invalid navigation file link {naviFileLink}. Path not found.");
                }

                await using var fileStream = await navigationFileItem.File.OpenRead();

                using var reader = new StreamReader(fileStream);
                var text = await reader.ReadToEndAsync();

                // override context so each navigation file is rendered in the context of the owning section
                var router    = new DocsSiteRouter(_site, targetSection);
                var renderer  = new DocsMarkdownService(new DocsMarkdownRenderingContext(_site, targetSection, router));
                var builder   = new NavigationBuilder(renderer, router);
                var fileItems = builder.Add(new string[]
                {
                    text
                })
                                .Build();

                items.AddRange(fileItems);
            }

            return(items);
        }
Exemple #10
0
        private async Task <(ContentItem Page, PageFrontmatter?Frontmatter)> ComposePartialHtmlPage(
            Path relativePath,
            ContentItem page,
            DocsSiteRouter router,
            Func <Path, PipeReader, Task <PipeReader> > preprocessorPipe)
        {
            Path outputPath = router.GenerateRoute(new Xref(page.Version, _section.Id, relativePath))
                              ?? throw new InvalidOperationException($"Could not generate output path for '{page}'");

            // create output dir for page
            await _cache.GetOrCreateDirectory(outputPath.GetDirectoryPath());

            // create output file
            var outputFile = await _cache.GetOrCreateFile(outputPath);

            // open file streams
            await using var inputStream = await page.File.OpenRead();

            // stream for preprocessed content
            await using var processedStream = new MemoryStream();

            // process preprocessor directives
            var reader = await preprocessorPipe(relativePath, PipeReader.Create(inputStream));

            var writer = PipeWriter.Create(processedStream, new StreamPipeWriterOptions(leaveOpen: true));
            await reader.CopyToAsync(writer);

            await reader.CompleteAsync();

            await writer.CompleteAsync();

            processedStream.Position = 0;

            // render markdown
            await using var outputStream = await outputFile.OpenWrite();

            var frontmatter = await _docsMarkdownService.RenderPage(processedStream, outputStream);

            return(page.WithFile(outputFile, "text/html"), frontmatter);
        }
Exemple #11
0
        private async Task ComposeAssets(Section section, DocsSiteRouter router)
        {
            // compose assets from sections
            foreach (var(relativePath, assetItem) in section.ContentItems.Where(ci => IsAsset(ci.Key, ci.Value)))
            {
                // open file streams
                await using var inputStream = await assetItem.File.OpenRead();

                // create output dir for page
                Path outputPath = router.GenerateRoute(new Xref(assetItem.Version, section.Id, relativePath))
                                  ?? throw new InvalidOperationException($"Could not generate output path for '{outputPath}'.");

                await _output.GetOrCreateDirectory(outputPath.GetDirectoryPath());

                // create output file
                var outputFile = await _output.GetOrCreateFile(outputPath);

                await using var outputStream = await outputFile.OpenWrite();

                await inputStream.CopyToAsync(outputStream);
            }
        }
Exemple #12
0
        public HandlebarsPageRenderer(string templateHbs, IReadOnlyDictionary <string, string> partials, DocsSiteRouter router)
        {
            _templateHbs = templateHbs;
            _handlebars  = Handlebars.Create();
            _handlebars.RegisterHelper("link_to", (output, options, context, arguments) =>
            {
                if (arguments.Length != 1)
                {
                    throw new InvalidOperationException($"link_to requires one argument of type DisplayLink");
                }

                var target = arguments[0];

                string?url   = null;
                string?title = null;
                if (target is DisplayLink displayLink)
                {
                    title = displayLink.Title ?? "";
                    url   = displayLink.Link.Xref != null
                        ? router.GenerateRoute(displayLink.Link.Xref.Value)
                        : displayLink.Link.Uri;
                }

                if (url == null)
                {
                    url = "[TODO: MISSING LINK TARGET]";
                }

                options.Template(output, new
                {
                    title,
                    url
                });
            });

            _handlebars.RegisterHelper("xref", (output, options, context, arguments) =>
            {
                if (arguments.Length != 1)
                {
                    throw new InvalidOperationException($"xref requires one argument of type xref");
                }

                var target = arguments[0];

                string?url = null;

                if (target is string xrefStr)
                {
                    target = LinkParser.Parse(xrefStr);
                }

                if (target is Link link)
                {
                    if (link.IsExternal)
                    {
                        url = link.Uri;
                    }
                    else
                    {
                        target = link.Xref !.Value;
                    }