public ManifestItemWithContext(InternalManifestItem item, FileModel model, IDocumentProcessor processor, TemplateBundle bundle) { Item = item; FileModel = model; Processor = processor; TemplateBundle = bundle; }
public SystemMetadata Generate(InternalManifestItem item) { var attrs = new SystemMetadata { Language = Constants.DefaultLanguage, }; string key = GetFileKey(item.Key); attrs.Key = ((RelativePath)key).RemoveWorkingFolder(); var file = (RelativePath)(item.FileWithoutExtension + item.Extension); attrs.RelativePathToRoot = (RelativePath.Empty).MakeRelativeTo(file); var fileWithoutWorkingFolder = file.RemoveWorkingFolder(); attrs.Path = fileWithoutWorkingFolder; if (!string.IsNullOrEmpty(_context.VersionName)) { attrs.VersionName = _context.VersionName; attrs.VersionFolder = _context.VersionFolder; } // 1. Root Toc is specified by RootTocKey, or by default in the top directory of output folder if (!string.IsNullOrEmpty(_context.RootTocPath)) { attrs.RootTocKey = _context.RootTocPath; var rootTocPath = ((RelativePath)_context.RootTocPath).RemoveWorkingFolder(); attrs.RootTocPath = rootTocPath; attrs.RelativePathToRootToc = rootTocPath.MakeRelativeTo(file); } else { GetRootTocFromOutputRoot(attrs, file); } // 2. The algorithm of toc current article belongs to: // a. If toc can be found in TocMap, return that toc // b. Elsewise, get the nearest toc, **nearest** means nearest toc in **OUTPUT** folder var parentTocFiles = _context.GetTocFileKeySet(key)?.Select(s => new FileInfo(s, (RelativePath)_context.GetFilePath(s))); var parentToc = GetNearestToc(parentTocFiles, file) ?? GetDefaultToc(key); if (parentToc != null) { var parentTocPath = parentToc.File.RemoveWorkingFolder(); attrs.TocPath = parentTocPath; var tocRelativePath = parentTocPath.MakeRelativeTo(file); attrs.RelativePathToToc = tocRelativePath; attrs.TocKey = parentToc.Key; Logger.LogDiagnostic($"TOC file {parentTocPath} is found for {item.LocalPathFromRoot}."); } else { Logger.LogDiagnostic($"TOC file for {item.LocalPathFromRoot} is not found."); } return(attrs); }
public SystemMetadata Generate(InternalManifestItem item) { var attrs = new SystemMetadata { Language = Constants.DefaultLanguage, }; string key = GetFileKey(item.Key); var file = (TypeForwardedToRelativePath)(item.FileWithoutExtension + item.Extension); attrs.RelativePathToRoot = (TypeForwardedToRelativePath.Empty).MakeRelativeTo(file); var fileWithoutWorkingFolder = file.RemoveWorkingFolder(); attrs.Path = fileWithoutWorkingFolder; // 1. Root Toc is always in the top directory of output folder var rootToc = _toc.FirstOrDefault(); if (rootToc != null) { var rootTocPath = rootToc.File.RemoveWorkingFolder(); if (rootTocPath.SubdirectoryCount == 0) { attrs.RootTocPath = rootTocPath; var rootTocRelativePath = rootTocPath.MakeRelativeTo(file); attrs.RelativePathToRootToc = rootTocRelativePath; attrs.RootTocKey = rootToc.Key; Logger.LogVerbose($"Root TOC file {rootTocPath} is found."); } else { Logger.LogVerbose($"Root TOC file from output folder is not found, the toppest TOC file is {rootTocPath}"); } } // 2. The algorithm of toc current article belongs to: // a. If toc can be found in TocMap, return that toc // b. Elsewise, get the nearest toc, **nearest** means nearest toc in **OUTPUT** folder var parentTocFiles = _context.GetTocFileKeySet(key)?.Select(s => new FileInfo(s, (TypeForwardedToRelativePath)_context.GetFilePath(s))); var parentToc = GetNearestToc(parentTocFiles, file) ?? GetDefaultToc(key); if (parentToc != null) { var parentTocPath = parentToc.File.RemoveWorkingFolder(); attrs.TocPath = parentTocPath; var tocRelativePath = parentTocPath.MakeRelativeTo(file); attrs.RelativePathToToc = tocRelativePath; attrs.TocKey = parentToc.Key; Logger.LogVerbose($"TOC file {parentTocPath} is found for {item.LocalPathFromRoot}."); } else { Logger.LogVerbose($"TOC file for {item.LocalPathFromRoot} is not found."); } return(attrs); }
private IEnumerable <TransformModelOptions> GetOptionsForEachTemplate(InternalManifestItem item, IDocumentBuildContext context) { if (item == null) { yield break; } foreach (var template in Templates) { if (template.ContainsGetOptions) { yield return(template.GetOptions(item.Model.Content)); } } }
/// <summary> /// Must guarantee thread safety /// </summary> /// <param name="item"></param> /// <returns></returns> internal ManifestItem Transform(InternalManifestItem item) { if (item.Model == null || item.Model.Content == null) { throw new ArgumentNullException("Content for item.Model should not be null!"); } var model = ConvertObjectToDictionary(item.Model.Content); model = AppendGlobalMetadata(model); if (_settings.Options.HasFlag(ApplyTemplateOptions.ExportRawModel)) { ExportModel(model, item.FileWithoutExtension, _settings.RawModelExportSettings); } var manifestItem = new ManifestItem { DocumentType = item.DocumentType, SourceRelativePath = item.LocalPathFromRoot, Metadata = item.Metadata, Version = _context.VersionName, }; var outputDirectory = _settings.OutputFolder ?? Directory.GetCurrentDirectory(); // 1. process resource if (item.ResourceFile != null) { // Resource file has already been processed in its plugin var ofi = new OutputFileInfo { RelativePath = item.ResourceFile, LinkToPath = GetLinkToPath(item.ResourceFile), }; manifestItem.OutputFiles.Add("resource", ofi); } // 2. process model var templateBundle = _templateCollection[item.DocumentType]; if (templateBundle == null) { return(manifestItem); } HashSet <string> missingUids = new HashSet <string>(); // Must convert to JObject first as we leverage JsonProperty as the property name for the model foreach (var template in templateBundle.Templates) { if (!template.ContainsTemplateRenderer) { continue; } try { var extension = template.Extension; string outputFile = item.FileWithoutExtension + extension; object viewModel = null; try { viewModel = template.TransformModel(model); } catch (Exception e) { string message; if (_settings.DebugMode) { // save raw model for further investigation: var rawModelPath = ExportModel(model, item.FileWithoutExtension, _settings.RawModelExportSettingsForDebug); message = $"Error transforming model \"{rawModelPath}\" generated from \"{item.LocalPathFromRoot}\" using \"{template.ScriptName}\". {e.Message}"; } else { message = $"Error transforming model generated from \"{item.LocalPathFromRoot}\" using \"{template.ScriptName}\". To get the detailed raw model, please run docfx with debug mode --debug. {e.Message} "; } Logger.LogError(message); throw new DocumentException(message, e); } string result; try { result = template.Transform(viewModel); } catch (Exception e) { string message; if (_settings.DebugMode) { // save view model for further investigation: var viewModelPath = ExportModel(viewModel, outputFile, _settings.ViewModelExportSettingsForDebug); message = $"Error applying template \"{template.Name}\" to view model \"{viewModelPath}\" generated from \"{item.LocalPathFromRoot}\". {e.Message}"; } else { message = $"Error applying template \"{template.Name}\" generated from \"{item.LocalPathFromRoot}\". To get the detailed view model, please run docfx with debug mode --debug. {e.Message}"; } Logger.LogError(message); throw new DocumentException(message, e); } if (_settings.Options.HasFlag(ApplyTemplateOptions.ExportViewModel)) { ExportModel(viewModel, outputFile, _settings.ViewModelExportSettings); } if (_settings.Options.HasFlag(ApplyTemplateOptions.TransformDocument)) { if (string.IsNullOrWhiteSpace(result)) { string message; if (_settings.DebugMode) { var viewModelPath = ExportModel(viewModel, outputFile, _settings.ViewModelExportSettingsForDebug); message = $"Model \"{viewModelPath}\" is transformed to empty string with template \"{template.Name}\""; } else { message = $"Model is transformed to empty string with template \"{template.Name}\". To get the detailed view model, please run docfx with debug mode --debug"; } Logger.LogWarning(message); } TransformDocument(result ?? string.Empty, extension, _context, outputFile, missingUids, manifestItem); Logger.LogDiagnostic($"Transformed model \"{item.LocalPathFromRoot}\" to \"{outputFile}\"."); } } catch (PathTooLongException e) { var message = $"Error processing {item.LocalPathFromRoot}: {e.Message}"; throw new PathTooLongException(message, e); } } if (missingUids.Count > 0) { var uids = string.Join(", ", missingUids.Select(s => $"\"{s}\"")); Logger.LogWarning($"Invalid cross reference {uids}.", null, item.LocalPathFromRoot); } return(manifestItem); }
/// <summary> /// Must guarantee thread safety /// </summary> /// <param name="item"></param> /// <returns></returns> internal ManifestItem Transform(InternalManifestItem item) { if (item.Model == null || item.Model.Content == null) { throw new ArgumentNullException("Content for item.Model should not be null!"); } var model = ConvertObjectToDictionary(item.Model.Content); model = AppendGlobalMetadata(model); if (_settings.Options.HasFlag(ApplyTemplateOptions.ExportRawModel)) { ExportModel(model, item.FileWithoutExtension, _settings.RawModelExportSettings); } var manifestItem = new ManifestItem { DocumentType = item.DocumentType, SourceRelativePath = item.LocalPathFromRoot, OutputFiles = new Dictionary <string, OutputFileInfo>(), Metadata = item.Metadata, }; var outputDirectory = _settings.OutputFolder ?? Environment.CurrentDirectory; // 1. process resource if (item.ResourceFile != null) { // Resource file has already been processed in its plugin manifestItem.OutputFiles.Add("resource", new OutputFileInfo { RelativePath = item.ResourceFile, LinkToPath = null, Hash = null }); } // 2. process model var templateBundle = _templateCollection[item.DocumentType]; if (templateBundle == null) { return(manifestItem); } HashSet <string> missingUids = new HashSet <string>(); // Must convert to JObject first as we leverage JsonProperty as the property name for the model foreach (var template in templateBundle.Templates) { if (!template.ContainsTemplateRenderer) { continue; } var extension = template.Extension; string outputFile = item.FileWithoutExtension + extension; string outputPath = Path.Combine(outputDirectory, outputFile); var dir = Path.GetDirectoryName(outputPath); if (!string.IsNullOrEmpty(dir)) { Directory.CreateDirectory(dir); } object viewModel = null; try { viewModel = template.TransformModel(model); } catch (Exception e) { // save raw model for further investigation: var exportSettings = ApplyTemplateSettings.RawModelExportSettingsForDebug; var rawModelPath = ExportModel(model, item.FileWithoutExtension, exportSettings); var message = $"Error transforming model \"{rawModelPath}\" generated from \"{item.LocalPathFromRoot}\" using \"{template.ScriptName}\": {e.Message}"; Logger.LogError(message); throw new DocumentException(message, e); } string result; try { result = template.Transform(viewModel); } catch (Exception e) { // save view model for further investigation: var exportSettings = ApplyTemplateSettings.ViewModelExportSettingsForDebug; var viewModelPath = ExportModel(viewModel, outputFile, exportSettings); var message = $"Error applying template \"{template.Name}\" to view model \"{viewModelPath}\" generated from \"{item.LocalPathFromRoot}\": {e.Message}"; Logger.LogError(message); throw new DocumentException(message, e); } if (_settings.Options.HasFlag(ApplyTemplateOptions.ExportViewModel)) { ExportModel(viewModel, outputFile, _settings.ViewModelExportSettings); } if (_settings.Options.HasFlag(ApplyTemplateOptions.TransformDocument)) { if (string.IsNullOrWhiteSpace(result)) { // TODO: WHAT to do if is transformed to empty string? STILL creat empty file? var exportSettings = ApplyTemplateSettings.ViewModelExportSettingsForDebug; var viewModelPath = ExportModel(viewModel, outputFile, exportSettings); Logger.LogWarning($"Model \"{viewModelPath}\" is transformed to empty string with template \"{template.Name}\""); File.WriteAllText(outputPath, string.Empty); } else { TransformDocument(result, extension, _context, outputPath, outputFile, missingUids, manifestItem); Logger.LogDiagnostic($"Transformed model \"{item.LocalPathFromRoot}\" to \"{outputPath}\"."); } } } if (missingUids.Count > 0) { var uids = string.Join(", ", missingUids.Select(s => $"\"{s}\"")); Logger.LogWarning($"Unable to resolve cross-reference {uids}"); } return(manifestItem); }
internal TransformModelOptions GetOptions(InternalManifestItem item, IDocumentBuildContext context) { return(MergeOptions(GetOptionsForEachTemplate(item, context))); }