private TocItemViewModel GetReferencedToc(FileAndType tocFile, Stack <FileAndType> stack) { if (_collection.TryGetValue(tocFile.FullPath, out TocItemInfo referencedTocFileModel) || _notInProjectTocCache.TryGetValue(tocFile, out referencedTocFileModel)) { referencedTocFileModel = ResolveItem(referencedTocFileModel, stack); referencedTocFileModel.IsReferenceToc = true; return(referencedTocFileModel.Content); } else { // It is acceptable that the referenced toc file is not included in docfx.json, as long as it can be found locally TocItemViewModel referencedTocItemViewModel; try { referencedTocItemViewModel = TocHelper.LoadSingleToc(tocFile.FullPath); } catch (FileNotFoundException) { Logger.LogError($"Referenced TOC file {tocFile.FullPath} does not exist.", code: WarningCodes.Build.InvalidTocInclude); return(null); } referencedTocFileModel = new TocItemInfo(tocFile, referencedTocItemViewModel); referencedTocFileModel = ResolveItem(referencedTocFileModel, stack); _notInProjectTocCache[tocFile] = referencedTocFileModel; return(referencedTocFileModel.Content); } }
/// <summary> /// 1. Expand the TOC reference /// 2. Resolve homepage /// </summary> public override IEnumerable <FileModel> Prebuild(ImmutableList <FileModel> models, IHostService host) { var(resolvedTocModels, includedTocs) = TocHelper.ResolveToc(models, host); ReportPreBuildDependency(resolvedTocModels, host, 8, includedTocs); return(resolvedTocModels); }
/// <summary> /// 1. Expand the TOC reference /// 2. Resolve homepage /// </summary> public override IEnumerable <FileModel> Prebuild(ImmutableList <FileModel> models, IHostService host) { var resolvedModels = TocHelper.Resolve(models, host).ToList(); ReportPreBuildDependency(resolvedModels, host, 8); return(resolvedModels); }
public override FileModel Load(FileAndType file, ImmutableDictionary <string, object> metadata) { var filePath = file.FullPath; var toc = TocHelper.LoadSingleToc(filePath); var repoDetail = GitUtility.TryGetFileDetail(filePath); var displayLocalPath = PathUtility.MakeRelativePath(EnvironmentContext.BaseDirectory, file.FullPath); // todo : metadata. return(new FileModel(file, toc) { Uids = new[] { new UidDefinition(file.File, displayLocalPath) }.ToImmutableArray(), LocalPathFromRoot = displayLocalPath }); }
private TocItemViewModel GetReferencedToc(FileAndType tocFile, Stack <FileAndType> stack) { if (_collection.TryGetValue(tocFile.FullPath, out TocItemInfo referencedTocFileModel) || _notInProjectTocCache.TryGetValue(tocFile, out referencedTocFileModel)) { referencedTocFileModel = ResolveItem(referencedTocFileModel, stack); referencedTocFileModel.IsReferenceToc = true; return(referencedTocFileModel.Content); } else { // It is acceptable that the referenced toc file is not included in docfx.json, as long as it can be found locally referencedTocFileModel = new TocItemInfo(tocFile, TocHelper.LoadSingleToc(tocFile.FullPath)); referencedTocFileModel = ResolveItem(referencedTocFileModel, stack); _notInProjectTocCache[tocFile] = referencedTocFileModel; return(referencedTocFileModel.Content); } }
public override FileModel Load(FileAndType file, ImmutableDictionary <string, object> metadata) { var filePath = file.FullPath; var toc = TocHelper.LoadSingleToc(filePath); var displayLocalPath = PathUtility.MakeRelativePath(EnvironmentContext.BaseDirectory, file.FullPath); // Apply metadata to TOC foreach (var pair in metadata) { if (!toc.Metadata.TryGetValue(pair.Key, out var val)) { toc.Metadata[pair.Key] = pair.Value; } } return(new FileModel(file, toc) { LocalPathFromRoot = displayLocalPath }); }
private TocItemInfo ResolveItemCore(TocItemInfo wrapper, Stack <FileAndType> stack) { if (wrapper.IsResolved) { return(wrapper); } var file = wrapper.File; if (stack.Contains(file)) { throw new DocumentException($"Circular reference to {file.FullPath} is found in {stack.Peek().FullPath}"); } var item = wrapper.Content; // HomepageUid and Uid is deprecated, unified to TopicUid if (string.IsNullOrEmpty(item.TopicUid)) { if (!string.IsNullOrEmpty(item.Uid)) { item.TopicUid = item.Uid; item.Uid = null; } else if (!string.IsNullOrEmpty(item.HomepageUid)) { item.TopicUid = item.HomepageUid; Logger.LogWarning($"HomepageUid is deprecated in TOC. Please use topicUid to specify uid {item.Homepage}"); item.HomepageUid = null; } } // Homepage is deprecated, unified to TopicHref if (!string.IsNullOrEmpty(item.Homepage)) { if (string.IsNullOrEmpty(item.TopicHref)) { item.TopicHref = item.Homepage; } else { Logger.LogWarning($"Homepage is deprecated in TOC. Homepage {item.Homepage} is overwritten with topicHref {item.TopicHref}"); } } // validate href ValidateHref(item); // TocHref supports 2 forms: absolute path and local toc file. // When TocHref is set, using TocHref as Href in output, and using Href as Homepage in output var tocHrefType = Utility.GetHrefType(item.TocHref); // check whether toc exists TocItemInfo tocFileModel = null; if (!string.IsNullOrEmpty(item.TocHref) && (tocHrefType == HrefType.MarkdownTocFile || tocHrefType == HrefType.YamlTocFile)) { var tocFilePath = (RelativePath)file.File + (RelativePath)item.TocHref; var tocFile = file.ChangeFile(tocFilePath); if (!_collection.TryGetValue(tocFile.FullPath, out tocFileModel)) { var message = $"Unable to find {item.TocHref}. Make sure the file is included in config file docfx.json!"; Logger.LogWarning(message); } } if (!string.IsNullOrEmpty(item.TocHref)) { if (!string.IsNullOrEmpty(item.Homepage)) { throw new DocumentException( $"TopicHref should be used to specify the homepage for {item.TocHref} when tocHref is used."); } if (tocHrefType == HrefType.RelativeFile || tocHrefType == HrefType.RelativeFolder) { throw new DocumentException($"TocHref {item.TocHref} only supports absolute path or local toc file."); } } var hrefType = Utility.GetHrefType(item.Href); switch (hrefType) { case HrefType.AbsolutePath: case HrefType.RelativeFile: if (item.Items != null && item.Items.Count > 0) { for (int i = 0; i < item.Items.Count; i++) { item.Items[i] = ResolveItem(new TocItemInfo(file, item.Items[i]), stack).Content; } if (string.IsNullOrEmpty(item.TopicHref) && string.IsNullOrEmpty(item.TopicUid)) { var defaultItem = GetDefaultHomepageItem(item); if (defaultItem != null) { item.AggregatedHref = defaultItem.TopicHref; item.AggregatedUid = defaultItem.TopicUid; } } } if (string.IsNullOrEmpty(item.TopicHref)) { // Get homepage from TocHref if href/topicHref is null or empty if (string.IsNullOrEmpty(item.Href) && string.IsNullOrEmpty(item.TopicUid) && tocFileModel != null) { stack.Push(file); var resolved = ResolveItem(tocFileModel, stack).Content; stack.Pop(); item.Href = resolved.TopicHref ?? resolved.AggregatedHref; item.TopicUid = resolved.TopicUid ?? resolved.AggregatedUid; } // Use TopicHref in output model item.TopicHref = item.Href; } break; case HrefType.RelativeFolder: { if (tocFileModel != null) { Logger.LogWarning($"Href {item.Href} is overwritten by tocHref {item.TocHref}"); } else { var relativeFolder = (RelativePath)file.File + (RelativePath)item.Href; var tocFilePath = relativeFolder + (RelativePath)Constants.TableOfContents.YamlTocFileName; var tocFile = file.ChangeFile(tocFilePath); // First, try finding toc.yml under the relative folder // Second, try finding toc.md under the relative folder if (!_collection.TryGetValue(tocFile.FullPath, out tocFileModel)) { tocFilePath = relativeFolder + (RelativePath)Constants.TableOfContents.MarkdownTocFileName; tocFile = file.ChangeFile(tocFilePath); if (!_collection.TryGetValue(tocFile.FullPath, out tocFileModel)) { var message = $"Unable to find either {Constants.TableOfContents.YamlTocFileName} or {Constants.TableOfContents.MarkdownTocFileName} inside {item.Href}. Make sure the file is included in config file docfx.json!"; Logger.LogWarning(message); break; } } item.TocHref = tocFilePath - (RelativePath)file.File; } // Get homepage from TocHref if TopicHref/TopicUid is not specified if (string.IsNullOrEmpty(item.TopicHref) && string.IsNullOrEmpty(item.TopicUid)) { stack.Push(file); var resolved = ResolveItem(tocFileModel, stack).Content; stack.Pop(); item.Href = item.TopicHref = resolved.TopicHref ?? resolved.AggregatedHref; item.TopicUid = resolved.TopicUid ?? resolved.AggregatedUid; } else { item.Href = item.TopicHref; } if (item.Items != null) { for (int i = 0; i < item.Items.Count; i++) { item.Items[i] = ResolveItem(new TocItemInfo(file, item.Items[i]), stack).Content; } } } break; case HrefType.MarkdownTocFile: case HrefType.YamlTocFile: { var href = (RelativePath)item.Href; var tocFilePath = (RelativePath)file.File + href; var tocFile = file.ChangeFile(tocFilePath); TocItemInfo referencedTocFileModel; TocItemViewModel referencedToc; stack.Push(file); if (_collection.TryGetValue(tocFile.FullPath, out referencedTocFileModel) || _notInProjectTocCache.TryGetValue(tocFile, out referencedTocFileModel)) { referencedTocFileModel = ResolveItem(referencedTocFileModel, stack); referencedTocFileModel.IsReferenceToc = true; referencedToc = referencedTocFileModel.Content; } else { // It is acceptable that the referenced toc file is not included in docfx.json, as long as it can be found locally referencedTocFileModel = new TocItemInfo(tocFile, TocHelper.LoadSingleToc(tocFile.FullPath)); referencedTocFileModel = ResolveItem(referencedTocFileModel, stack); referencedToc = referencedTocFileModel.Content; _notInProjectTocCache[tocFile] = referencedTocFileModel; } stack.Pop(); // For referenced toc, content from referenced toc is expanded as the items of current toc item, // Href is reset to the homepage of current toc item item.Href = item.TopicHref; var referencedTocClone = referencedToc.Items?.Clone(); // For [reference](a/toc.md), and toc.md contains not-exist.md, the included not-exist.md should be resolved to a/not-exist.md item.Items = UpdateOriginalHref(referencedTocClone, href); } break; default: break; } var relativeToFile = (RelativePath)file.File; item.OriginalHref = item.Href; item.OriginalTocHref = item.TocHref; item.OriginalTopicHref = item.TopicHref; item.OriginalHomepage = item.Homepage; item.Href = NormalizeHref(item.Href, relativeToFile); item.TocHref = NormalizeHref(item.TocHref, relativeToFile); item.TopicHref = NormalizeHref(item.TopicHref, relativeToFile); item.Homepage = NormalizeHref(item.Homepage, relativeToFile); wrapper.IsResolved = true; // for backward compatibility if (item.Href == null && item.Homepage == null) { item.Href = item.TocHref; item.Homepage = item.TopicHref; } return(wrapper); }