public bool TryPreparePage(ContentObject page) { if (page.FrontMatter != null && !Scripts.TryRunFrontMatter(page.FrontMatter, page)) { return(false); } if (page.Script != null && TryEvaluate(page)) { // If page is discarded, skip it if (page.Discard) { return(false); } var pendingPageProcessors = new OrderedList <IContentProcessor>(); return(TryProcessPage(page, BeforeLoadingProcessors.OfType <IContentProcessor>(), pendingPageProcessors, false)); } return(false); }
private static async Task <ContentObject> LoadPageScript(SiteObject site, Stream stream, FileEntry file) { // Read the stream var reader = new StreamReader(stream); var content = await reader.ReadToEndAsync(); // Early dispose the stream stream.Dispose(); ContentObject page = null; // Parse the page, using front-matter mode var scriptInstance = site.Scripts.ParseScript(content, file.FullName, ScriptMode.FrontMatterAndContent); if (!scriptInstance.HasErrors) { page = new ContentObject(site, file, scriptInstance); var evalClock = Stopwatch.StartNew(); if (site.Content.TryPreparePage(page)) { evalClock.Stop(); // Update statistics var contentStat = site.Statistics.GetContentStat(page); contentStat.EvaluateTime += evalClock.Elapsed; // Update the summary of the page evalClock.Restart(); SummaryHelper.UpdateSummary(page); evalClock.Stop(); // Update statistics contentStat.SummaryTime += evalClock.Elapsed; } } return(page); }
private void RunPage(ContentObject page) { // Update statistics var evalClock = Stopwatch.StartNew(); var contentStat = Site.Statistics.GetContentStat(page); if (page.Script != null && TryRunPageWithScript(page)) { page.InitializeAfterRun(); // If page is discarded, skip it if (page.Discard) { return; } } var pendingPageProcessors = new OrderedList <IContentProcessor>(); TryProcessPage(page, ContentProcessingStage.Running, AfterRunningProcessors, pendingPageProcessors, false); contentStat.RunningTime += evalClock.Elapsed; }
public PageContentDependency(ContentObject page) { Page = page ?? throw new ArgumentNullException(nameof(page)); }
private bool TryProcessPage(ContentObject page, IEnumerable <IContentProcessor> pageProcessors, OrderedList <IContentProcessor> pendingPageProcessors, bool copyOutput) { // If page is discarded, skip it if (page.Discard) { return(false); } // By default working on all processors // Order is important! pendingPageProcessors.AddRange(pageProcessors); bool hasBeenProcessed = true; bool breakProcessing = false; // We process the page going through all IContentProcessor from the end of the list // (more priority) to the beginning of the list (less priority). // An IContentProcessor can transform the page to another type of content // that could then be processed by another IContentProcessor // But we make sure that a processor cannot process a page more than one time // to avoid an infinite loop var clock = Stopwatch.StartNew(); while (hasBeenProcessed && !breakProcessing && !page.Discard) { hasBeenProcessed = false; for (int i = pendingPageProcessors.Count - 1; i >= 0; i--) { var processor = pendingPageProcessors[i]; // Note that page.ContentType can be changed by a processor // while processing a page clock.Restart(); try { var result = processor.TryProcess(page); if (result != ContentResult.None) { // Update statistics per plugin var statistics = Site.Statistics; var stat = statistics.GetPluginStat(processor); stat.PageCount++; stat.ContentProcessTime += clock.Elapsed; hasBeenProcessed = true; pendingPageProcessors.RemoveAt(i); breakProcessing = result == ContentResult.Break; break; } } catch (Exception ex) { Site.Error(ex, $"Error while processing {page.Path}."); breakProcessing = true; hasBeenProcessed = true; break; } } } pendingPageProcessors.Clear(); // Copy only if the file are marked as include if (copyOutput && !breakProcessing && !page.Discard) { Site.Content.TryCopyContentToOutput(page, page.GetDestinationPath()); } return(true); }
private async Task <ContentObject> LoadContent(FileEntry file) { ContentObject page = null; var buffer = new byte[16]; var clock = Stopwatch.StartNew(); var stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.Read); try { var count = await stream.ReadAsync(buffer, 0, buffer.Length); // Rewind to 0 stream.Position = 0; bool hasFrontMatter = false; bool isBinary = false; int startFrontMatter = 0; // Does it start with UTF8 BOM? If yes, skip it // EF BB BF if (buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF) { startFrontMatter = 3; } // TODO: avoid the chars alloc var charBuffer = Encoding.UTF8.GetChars(buffer, startFrontMatter, buffer.Length - startFrontMatter); foreach (var frontParser in Scripts.FrontMatterParsers) { if (frontParser.CanHandle(charBuffer)) { for (int i = startFrontMatter + 3; i < count; i++) { if (buffer[i] == 0) { isBinary = true; break; } } if (!isBinary) { hasFrontMatter = true; } break; } } if (hasFrontMatter) { page = await LoadPageScript(Site, stream, file); stream = null; } else { page = new ContentObject(Site, file); } } finally { // Dispose stream used stream?.Dispose(); } clock.Stop(); Site.Statistics.GetContentStat(page).LoadingParsingTime += clock.Elapsed; return(page); }
public bool TryCopyContentToOutput(ContentObject fromFile, UPath outputPath) { if (fromFile == null) { throw new ArgumentNullException(nameof(fromFile)); } outputPath.AssertAbsolute(); var clock = Stopwatch.StartNew(); var outputFile = new FileEntry(Site.OutputFileSystem, outputPath); var outputDir = outputFile.Directory; if (outputDir == null) { throw new ArgumentException("Output directory cannot be empty", nameof(outputPath)); } TrackDestination(outputFile, fromFile.SourceFile); var stat = Site.Statistics.GetContentStat(fromFile); CreateDirectory(outputDir); try { // If the file has a content, we will use this instead if (fromFile.Content != null) { if (Site.CanTrace()) { Site.Trace($"Write file [{outputFile}]"); } using (var writer = new StreamWriter(outputFile.Open(FileMode.Create, FileAccess.Write))) { writer.Write(fromFile.Content); writer.Flush(); // Update statistics stat.Static = false; stat.OutputBytes += writer.BaseStream.Length; } } // If the source file is not newer than the destination file, don't overwrite it else if (fromFile.SourceFile != null && (!outputFile.Exists || (fromFile.ModifiedTime > outputFile.LastWriteTime))) { if (Site.CanTrace()) { Site.Trace($"Write file [{outputFile}]"); } // If the output file is readonly, make sure that it is not readonly before overwriting if (outputFile.Exists && (outputFile.Attributes & FileAttributes.ReadOnly) != 0) { outputFile.Attributes = outputFile.Attributes & ~FileAttributes.ReadOnly; } fromFile.SourceFile.CopyTo(outputFile, true); // Don't copy readonly attributes for output folder outputFile.Attributes = fromFile.SourceFile.Attributes & ~FileAttributes.ReadOnly; // Update statistics stat.Static = true; stat.OutputBytes += fromFile.Length; } } catch (Exception ex) { Site.Error(fromFile.SourceFile != null ? $"Unable to copy file [{fromFile.SourceFile}] to [{outputFile}]. Reason:{ex.GetReason()}" : $"Unable to copy file to [{outputFile}]. Reason:{ex.GetReason()}"); return(false); } finally { stat.OutputTime += clock.Elapsed; } return(true); }
private bool TryRunPageWithScript(ContentObject page) { page.ScriptObjectLocal ??= new ScriptObject(); return(Site.Scripts.TryEvaluatePage(page, page.Script, page.SourceFile.Path, page)); }
private async Task <ContentObject> LoadContent(FileEntry file) { ContentObject page = null; var buffer = new byte[16]; var clock = Stopwatch.StartNew(); var stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite); try { var count = await stream.ReadAsync(buffer, 0, buffer.Length); // Rewind to 0 stream.Position = 0; bool hasFrontMatter = false; bool isBinary = false; int startFrontMatter = 0; // Does it start with UTF8 BOM? If yes, skip it // EF BB BF if (buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF) { startFrontMatter = 3; } // TODO: avoid the chars alloc var charBuffer = Encoding.UTF8.GetChars(buffer, startFrontMatter, buffer.Length - startFrontMatter); foreach (var frontParser in Scripts.FrontMatterParsers) { if (frontParser.CanHandle(charBuffer)) { for (int i = startFrontMatter + 3; i < count; i++) { if (buffer[i] == 0) { isBinary = true; break; } } if (!isBinary) { hasFrontMatter = true; } break; } } // Run pre-content (e.g AttributesPlugin) ScriptObject preContent = null; foreach (var preLoadingContentProcessor in BeforeLoadingContentProcessors) { preLoadingContentProcessor(file.Path, ref preContent); } if (hasFrontMatter) { page = await LoadPageScript(Site, stream, file, preContent); stream = null; } else { page = new FileContentObject(Site, file, preContent: preContent); //// Run pre-processing on static content as well //var pendingPageProcessors = new OrderedList<IContentProcessor>(); //TryProcessPage(page, ContentProcessingStage.AfterLoading, AfterLoadingProcessors, pendingPageProcessors, false); } // Initialize the page loaded page?.Initialize(); } finally { // Dispose stream used stream?.Dispose(); } clock.Stop(); Site.Statistics.GetContentStat(page).LoadingParsingTime += clock.Elapsed; return(page); }