public TemplateModelTransformer(DocumentBuildContext context, TemplateCollection templateCollection, ApplyTemplateSettings settings, IDictionary <string, object> globals) { _context = context ?? throw new ArgumentNullException(nameof(context)); _templateCollection = templateCollection; _settings = settings; _globalVariables = globals; }
public TemplatePageLoader(IResourceFileReader reader, DocumentBuildContext context, int maxParallelism) { _reader = reader; _maxParallelism = maxParallelism; _rendererLoader = new RendererLoader(reader, maxParallelism); _preprocessorLoader = new PreprocessorLoader(reader, context, maxParallelism); }
internal List <ManifestItem> Process(List <InternalManifestItem> manifest, DocumentBuildContext context, ApplyTemplateSettings settings, IDictionary <string, object> globals = null) { using (new LoggerPhaseScope("Apply Templates", true)) { if (globals == null) { globals = Tokens.ToDictionary(pair => pair.Key, pair => (object)pair.Value); } var documentTypes = manifest.Select(s => s.DocumentType).Distinct(); var notSupportedDocumentTypes = documentTypes.Where(s => s != "Resource" && _templateCollection[s] == null); if (notSupportedDocumentTypes.Any()) { Logger.LogWarning($"There is no template processing document type(s): {notSupportedDocumentTypes.ToDelimitedString()}"); } Logger.LogInfo($"Applying templates to {manifest.Count} model(s)..."); if (settings.Options.HasFlag(ApplyTemplateOptions.TransformDocument)) { var templatesInUse = documentTypes.Select(s => _templateCollection[s]).Where(s => s != null).ToList(); ProcessDependencies(settings.OutputFolder, templatesInUse); } else { Logger.LogInfo("Dryrun, no template will be applied to the documents."); } var templateManifest = ProcessCore(manifest, context, settings, globals); return(templateManifest); } }
private void RegisterXRefSpec(DocumentBuildContext context, SaveResult result) { foreach (var spec in result.XRefSpecs) { if (!string.IsNullOrWhiteSpace(spec?.Uid)) { XRefSpec xref; if (context.XRefSpecMap.TryGetValue(spec.Uid, out xref)) { Logger.LogWarning($"Uid({spec.Uid}) has already been defined in {((RelativePath)xref.Href).RemoveWorkingFolder()}."); } else { context.XRefSpecMap[spec.Uid] = spec.ToReadOnly(); } } } foreach (var spec in result.ExternalXRefSpecs) { if (!string.IsNullOrWhiteSpace(spec?.Uid)) { context.ReportExternalXRefSpec(spec); } } }
internal List<ManifestItem> Process(List<InternalManifestItem> manifest, DocumentBuildContext context, ApplyTemplateSettings settings, IDictionary<string, object> globals = null) { using (new LoggerPhaseScope("Apply Templates", true)) { if (globals == null) { globals = Tokens.ToDictionary(pair => pair.Key, pair => (object)pair.Value); } var documentTypes = manifest.Select(s => s.DocumentType).Distinct(); var notSupportedDocumentTypes = documentTypes.Where(s => s != "Resource" && _templateCollection[s] == null); if (notSupportedDocumentTypes.Any()) { Logger.LogWarning($"There is no template processing document type(s): {TypeForwardedToStringExtension.ToDelimitedString(notSupportedDocumentTypes)}"); } Logger.LogInfo($"Applying templates to {manifest.Count} model(s)..."); if (settings.Options.HasFlag(ApplyTemplateOptions.TransformDocument)) { var templatesInUse = documentTypes.Select(s => _templateCollection[s]).Where(s => s != null).ToList(); ProcessDependencies(settings.OutputFolder, templatesInUse); } else { Logger.LogInfo("Dryrun, no template will be applied to the documents."); } var templateManifest = ProcessCore(manifest, context, settings, globals); return templateManifest; } }
private static void HandleUids(DocumentBuildContext context, SaveResult result) { if (result.LinkToUids.Count > 0) { context.XRef.UnionWith(result.LinkToUids.Where(s => s != null)); } }
private void UpdateUidFileDependency(DocumentBuildContext context, List <HostService> hostServices) { foreach (var hostService in hostServices) { foreach (var m in hostService.Models) { if (m.Type == DocumentType.Overwrite) { continue; } if (m.LinkToUids.Count != 0) { context.DependencyGraph.ReportDependency( ((RelativePath)m.OriginalFileAndType.File).GetPathFromWorkingFolder().ToString(), GetFilesFromUids(context, m.LinkToUids)); } if (m.LinkToFiles.Count != 0) { context.DependencyGraph.ReportDependency( ((RelativePath)m.OriginalFileAndType.File).GetPathFromWorkingFolder().ToString(), m.LinkToFiles); } } } }
private void Prepare( DocumentBuildParameters parameters, DocumentBuildContext context, TemplateProcessor templateProcessor, string markdownServiceContextHash, out IHostServiceCreator hostServiceCreator, out PhaseProcessor phaseProcessor) { if (IntermediateFolder != null && parameters.ApplyTemplateSettings.TransformDocument) { context.IncrementalBuildContext = IncrementalBuildContext.Create(parameters, CurrentBuildInfo, LastBuildInfo, IntermediateFolder, markdownServiceContextHash); hostServiceCreator = new HostServiceCreatorWithIncremental(context); phaseProcessor = new PhaseProcessor { Handlers = { new PrebuildBuildPhaseHandler(context).WithIncremental(), new PostbuildPhaseHandler(context, templateProcessor).WithIncremental(), } }; } else { hostServiceCreator = new HostServiceCreator(context); phaseProcessor = new PhaseProcessor { Handlers = { new PrebuildBuildPhaseHandler(context), new PostbuildPhaseHandler(context, templateProcessor), } }; } }
private List <TemplateManifestItem> ProcessCore(List <ManifestItem> items, DocumentBuildContext context, ApplyTemplateSettings settings, IDictionary <string, object> globals) { var manifest = new ConcurrentBag <TemplateManifestItem>(); var systemAttributeGenerator = new SystemMetadataGenerator(context); var transformer = new TemplateModelTransformer(context, _templateCollection, settings, globals); items.RunAll( item => { var manifestItem = transformer.Transform(item); if (manifestItem.OutputFiles?.Count > 0) { manifest.Add(manifestItem); } }, context.MaxParallelism); var itemsToRemove = new List <string>(); foreach (var duplicates in from m in manifest from output in m.OutputFiles.Values group m.OriginalFile by output into g where g.Count() > 1 select g) { Logger.LogWarning($"Overwrite occurs while input files \"{string.Join(", ", duplicates)}\" writing to the same output file \"{duplicates.Key}\""); itemsToRemove.AddRange(duplicates.Skip(1)); } return(manifest.Where(m => !itemsToRemove.Contains(m.OriginalFile)).ToList()); }
public HostServiceCreatorWithIncremental(DocumentBuildContext context) : base(context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } IncrementalContext = context.IncrementalBuildContext; }
private Engine SetupEngine(ResourceCollection resourceCollection, TemplatePreprocessorResource scriptResource, DocumentBuildContext context) { var rootPath = (RelativePath)scriptResource.ResourceName; var engineCache = new Dictionary<string, Engine>(); var utility = new TemplateUtility(context); _utilityObject = new { resolveSourceRelativePath = new Func<string, string, string>(utility.ResolveSourceRelativePath) }; var engine = CreateDefaultEngine(); var requireAction = new Func<string, object>( s => { if (!s.StartsWith(RequireRelativePathPrefix)) { throw new ArgumentException($"Only relative path starting with `{RequireRelativePathPrefix}` is supported in require"); } var relativePath = (RelativePath)s.Substring(RequireRelativePathPrefix.Length); s = relativePath.BasedOn(rootPath); var script = resourceCollection?.GetResource(s); if (string.IsNullOrWhiteSpace(script)) { return null; } Engine cachedEngine; if (!engineCache.TryGetValue(s, out cachedEngine)) { cachedEngine = CreateEngine(engine, RequireFuncVariableName); engineCache[s] = cachedEngine; cachedEngine.Execute(script); } return cachedEngine.GetValue(ExportsVariableName); }); engine.SetValue(RequireFuncVariableName, requireAction); engineCache[rootPath] = engine; engine.Execute(scriptResource.Content); var value = engine.GetValue(ExportsVariableName); if (value.IsObject()) { var exports = value.AsObject(); GetOptionsFunc = GetFunc(GetOptionsFuncVariableName, exports); TransformModelFunc = GetFunc(TransformFuncVariableName, exports); } else { throw new InvalidPreprocessorException("Invalid 'exports' variable definition. 'exports' MUST be an object."); } return engine; }
private static List <HomepageInfo> GetHomepages(DocumentBuildContext context) { return((from s in context.GetTocInfo() where !string.IsNullOrEmpty(s.Homepage) select new HomepageInfo { Homepage = RelativePath.GetPathWithoutWorkingFolderChar(s.Homepage), TocPath = RelativePath.GetPathWithoutWorkingFolderChar(context.GetFilePath(s.TocFileKey)) }).ToList()); }
private static List <HomepageInfo> GetHomepages(DocumentBuildContext context) { return(context.GetTocInfo() .Where(s => !string.IsNullOrEmpty(s.Homepage)) .Select(s => new HomepageInfo { Homepage = TypeForwardedToRelativePath.GetPathWithoutWorkingFolderChar(s.Homepage), TocPath = TypeForwardedToRelativePath.GetPathWithoutWorkingFolderChar(context.GetFilePath(s.TocFileKey)) }).ToList()); }
public TemplateJintPreprocessor(ResourceCollection resourceCollection, TemplatePreprocessorResource scriptResource, DocumentBuildContext context) { if (!string.IsNullOrWhiteSpace(scriptResource.Content)) { _engine = SetupEngine(resourceCollection, scriptResource, context); } else { _engine = null; } }
private void SaveDependency(DocumentBuildContext context, DocumentBuildParameters parameters) { var vbi = _currentBuildInfo.Versions.Find(v => v.VersionName == parameters.VersionName); vbi.Dependency = Path.GetRandomFileName(); using (var writer = File.CreateText( Path.Combine(IntermediateFolder, vbi.Dependency))) { context.DependencyGraph.Save(writer); } }
public ManifestProcessor(List <ManifestItemWithContext> manifestWithContext, DocumentBuildContext context, TemplateProcessor templateProcessor) { _context = context ?? throw new ArgumentNullException(nameof(context)); _templateProcessor = templateProcessor ?? throw new ArgumentNullException(nameof(templateProcessor)); _manifestWithContext = manifestWithContext ?? throw new ArgumentNullException(nameof(manifestWithContext)); // E.g. we can set TOC model to be globally shared by every data model // Make sure it is single thread _globalMetadata = _templateProcessor.Tokens?.ToDictionary(pair => pair.Key, pair => (object)pair.Value) ?? new Dictionary <string, object>(); }
/// <summary> /// TemplateName can be either file or folder /// 1. If TemplateName is file, it is considered as the default template /// 2. If TemplateName is a folder, files inside the folder is considered as the template, each file is named after {DocumentType}.{extension} /// </summary> /// <param name="templateName"></param> /// <param name="resourceProvider"></param> public TemplateProcessor(ResourceCollection resourceProvider, DocumentBuildContext context, int maxParallelism = 0) { if (maxParallelism <= 0) { maxParallelism = Environment.ProcessorCount; } _resourceProvider = resourceProvider; _templateCollection = new TemplateCollection(resourceProvider, context, maxParallelism); Tokens = LoadTokenJson(resourceProvider) ?? new Dictionary <string, string>(); }
/// <summary> /// TemplateName can be either file or folder /// 1. If TemplateName is file, it is considered as the default template /// 2. If TemplateName is a folder, files inside the folder is considered as the template, each file is named after {DocumentType}.{extension} /// </summary> /// <param name="templateName"></param> /// <param name="resourceProvider"></param> public TemplateProcessor(ResourceCollection resourceProvider, DocumentBuildContext context, int maxParallelism = 0) { if (maxParallelism <= 0) { maxParallelism = Environment.ProcessorCount; } _resourceProvider = resourceProvider; _templateCollection = new TemplateCollection(resourceProvider, context, maxParallelism); Tokens = LoadTokenJson(resourceProvider) ?? new Dictionary<string, string>(); }
public TemplateModelTransformer(DocumentBuildContext context, TemplateCollection templateCollection, ApplyTemplateSettings settings, IDictionary<string, object> globals) { if (context == null) { throw new ArgumentNullException(nameof(context)); } _context = context; _templateCollection = templateCollection; _settings = settings; _globalVariables = globals; _systemMetadataGenerator = new SystemMetadataGenerator(context); }
/// <summary> /// TemplateName can be either file or folder /// 1. If TemplateName is file, it is considered as the default template /// 2. If TemplateName is a folder, files inside the folder is considered as the template, each file is named after {DocumentType}.{extension} /// </summary> /// <param name="templateName"></param> /// <param name="resourceProvider"></param> public TemplateProcessor(ResourceFileReader resourceProvider, DocumentBuildContext context, int maxParallelism = 0) { if (maxParallelism <= 0) { maxParallelism = Environment.ProcessorCount; } _context = context; _resourceProvider = resourceProvider; _maxParallelism = maxParallelism; _templateCollection = new TemplateCollection(resourceProvider, context, maxParallelism); Tokens = TemplateProcessorUtility.LoadTokens(resourceProvider) ?? new Dictionary <string, string>(); }
public TemplateModelTransformer(DocumentBuildContext context, TemplateCollection templateCollection, ApplyTemplateSettings settings, IDictionary <string, object> globals) { if (context == null) { throw new ArgumentNullException(nameof(context)); } _context = context; _templateCollection = templateCollection; _settings = settings; _globalVariables = globals; _systemMetadataGenerator = new SystemMetadataGenerator(context); }
private void PrepareBuild(DocumentBuildContext context, IEnumerable <HostService> hostServices) { var incrementalContext = context.IncrementalBuildContext; var lbv = incrementalContext?.LastBuildVersionInfo; var cbv = incrementalContext?.CurrentBuildVersionInfo; if (ShouldTraceIncrementalInfo) { var ldg = lbv?.Dependency; var cdg = cbv.Dependency; if (ldg != null) { // reregister dependency types from last dependency graph using (new LoggerPhaseScope("RegisterDependencyTypeFromLastBuild", true)) { cdg.RegisterDependencyType(ldg.DependencyTypes.Values); } // restore dependency graph from last dependency graph using (new LoggerPhaseScope("ReportDependencyFromLastBuild", true)) { cdg.ReportDependency(from r in ldg.ReportedBys from i in ldg.GetDependencyReportedBy(r) select i); } } } foreach (var hostService in hostServices) { hostService.SourceFiles = context.AllSourceFiles; foreach (var m in hostService.Models) { if (m.LocalPathFromRepoRoot == null) { m.LocalPathFromRepoRoot = TypeForwardedToStringExtension.ToDisplayPath(Path.Combine(m.BaseDir, m.File)); } if (m.LocalPathFromRoot == null) { m.LocalPathFromRoot = TypeForwardedToStringExtension.ToDisplayPath(Path.Combine(m.BaseDir, m.File)); } } if (ShouldTraceIncrementalInfo) { hostService.DependencyGraph = cbv.Dependency; using (new LoggerPhaseScope("RegisterDependencyTypeFromProcessor", true)) { RegisterDependencyType(hostService); } } } }
private static FileModel Load( IDocumentProcessor processor, ImmutableDictionary <string, object> metadata, FileMetadata fileMetadata, FileAndType file, bool canProcessorIncremental, DocumentBuildContext context) { using (new LoggerFileScope(file.File)) { Logger.LogDiagnostic($"Processor {processor.Name}, File {file.FullPath}: Loading..."); if (canProcessorIncremental) { var incrementalContext = context.IncrementalBuildContext; ChangeKindWithDependency ck; string fileKey = ((TypeForwardedToRelativePath)file.File).GetPathFromWorkingFolder().ToString(); if (incrementalContext.ChangeDict.TryGetValue(fileKey, out ck)) { Logger.LogDiagnostic($"Processor {processor.Name}, File {file.FullPath}, ChangeType {ck}."); if (ck == ChangeKindWithDependency.Deleted) { return(null); } if (ck == ChangeKindWithDependency.None) { Logger.LogDiagnostic($"Processor {processor.Name}, File {file.FullPath}: Check incremental..."); if (processor.BuildSteps.Cast <ISupportIncrementalBuildStep>().All(step => step.CanIncrementalBuild(file))) { Logger.LogDiagnostic($"Processor {processor.Name}, File {file.FullPath}: Skip build by incremental."); return(null); } Logger.LogDiagnostic($"Processor {processor.Name}, File {file.FullPath}: Incremental not available."); } } } var path = Path.Combine(file.BaseDir, file.File); metadata = ApplyFileMetadata(path, metadata, fileMetadata); try { return(processor.Load(file, metadata)); } catch (Exception) { Logger.LogError($"Unable to load file: {file.File} via processor: {processor.Name}."); throw; } } }
private static ManifestItem GetManifestItem(DocumentBuildContext context, FileModel model, SaveResult result) { return(new ManifestItem { DocumentType = result.DocumentType, ModelFile = result.ModelFile, ResourceFile = result.ResourceFile, Key = model.Key, // TODO: What is API doc's LocalPathToRepo? => defined in ManagedReferenceDocumentProcessor LocalPathFromRepoRoot = model.LocalPathFromRepoRoot, Model = model.ModelWithCache, InputFolder = model.OriginalFileAndType.BaseDir }); }
private static InternalManifestItem GetManifestItem(DocumentBuildContext context, FileModel model, SaveResult result) { return(new InternalManifestItem { DocumentType = result.DocumentType, FileWithoutExtension = result.FileWithoutExtension, ResourceFile = result.ResourceFile, Key = model.Key, LocalPathFromRoot = model.LocalPathFromRoot, Model = model.ModelWithCache, InputFolder = model.OriginalFileAndType.BaseDir, Metadata = new Dictionary <string, object>((IDictionary <string, object>)model.ManifestProperties), }); }
private ManifestItem HandleSaveResult( DocumentBuildContext context, HostService hostService, FileModel model, SaveResult result) { context.FileMap[model.Key] = ((RelativePath)model.File).GetPathFromWorkingFolder(); DocumentException.RunAll( () => CheckFileLink(hostService, result), () => HandleUids(context, result), () => HandleToc(context, result), () => RegisterXRefSpec(context, result)); return(GetManifestItem(context, model, result)); }
private static ManifestItem GetManifestItem(DocumentBuildContext context, FileModel model, SaveResult result) { return(new ManifestItem { DocumentType = result.DocumentType, FileWithoutExtension = result.FileWithoutExtension, ResourceFile = result.ResourceFile, Key = model.Key, // TODO: What is API doc's LocalPathToRepo? => defined in ManagedReferenceDocumentProcessor LocalPathFromRepoRoot = model.LocalPathFromRepoRoot, Model = model.ModelWithCache, InputFolder = model.OriginalFileAndType.BaseDir, Metadata = new Dictionary <string, object>((IDictionary <string, object>)model.ManifestProperties), }); }
private static void UpdateHostServicesPerchanges(DocumentBuildContext context, IEnumerable <HostService> hostServices) { var incrementalContext = context.IncrementalBuildContext; var cbv = incrementalContext.CurrentBuildVersionInfo; UpdateUidDependency(hostServices, context); if (incrementalContext.CanVersionIncremental) { var newChanges = incrementalContext.ExpandDependency(cbv.Dependency, d => true); Logger.LogDiagnostic($"After expanding dependency before postbuild, changes: {JsonUtility.Serialize(incrementalContext.ChangeDict)}"); foreach (var hostService in hostServices) { hostService.ReloadModelsPerIncrementalChanges(incrementalContext, newChanges, BuildPhase.PostBuild); } } }
/// <summary> /// Export xref map file. /// </summary> private static void ExportXRefMap(DocumentBuildParameters parameters, DocumentBuildContext context) { Logger.LogVerbose("Exporting xref map..."); var xrefMap = new XRefMap(); xrefMap.References = (from xref in context.XRefSpecMap.Values.AsParallel().WithDegreeOfParallelism(parameters.MaxParallelism) select new XRefSpec(xref) { Href = ((RelativePath)context.FileMap[xref.Href]).RemoveWorkingFolder().ToString() + "#" + XRefDetails.GetHtmlId(xref.Uid), }).ToList(); xrefMap.Sort(); YamlUtility.Serialize( Path.Combine(parameters.OutputBaseDir, XRefMapFileName), xrefMap); Logger.LogInfo("XRef map exported."); }
public Template(string name, DocumentBuildContext context, TemplateRendererResource templateResource, TemplatePreprocessorResource scriptResource, ResourceCollection resourceCollection, int maxParallelism) { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException(nameof(name)); } Name = name; var templateInfo = GetTemplateInfo(Name); Extension = templateInfo.Extension; Type = templateInfo.DocumentType; TemplateType = templateInfo.TemplateType; _script = scriptResource?.Content; if (!string.IsNullOrWhiteSpace(_script)) { ScriptName = Name + ".js"; _preprocessorPool = ResourcePool.Create(() => CreatePreprocessor(resourceCollection, scriptResource, context), maxParallelism); try { using (var preprocessor = _preprocessorPool.Rent()) { ContainsGetOptions = preprocessor.Resource.GetOptionsFunc != null; ContainsModelTransformation = preprocessor.Resource.TransformModelFunc != null; } } catch (Exception e) { _preprocessorPool = null; Logger.LogWarning($"{ScriptName} is not a valid template preprocessor, ignored: {e.Message}"); } } if (!string.IsNullOrEmpty(templateResource?.Content) && resourceCollection != null) { _rendererPool = ResourcePool.Create(() => CreateRenderer(resourceCollection, templateResource), maxParallelism); ContainsTemplateRenderer = true; } if (!ContainsGetOptions && !ContainsModelTransformation && !ContainsTemplateRenderer) { Logger.LogWarning($"Template {name} contains neither preprocessor to process model nor template to render model. Please check if the template is correctly defined. Allowed preprocessor functions are [exports.getOptions] and [exports.transform]."); } Resources = ExtractDependentResources(Name); }
public ManifestProcessor(IEnumerable<HostService> hostServices, DocumentBuildContext context, TemplateProcessor templateProcessor) { if (hostServices == null) { throw new ArgumentNullException(nameof(hostServices)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } if (templateProcessor == null) { throw new ArgumentNullException(nameof(templateProcessor)); } _context = context; _templateProcessor = templateProcessor; Init(hostServices); }
public ManifestProcessor(List <HostService> hostServices, DocumentBuildContext context, TemplateProcessor templateProcessor) { if (hostServices == null) { throw new ArgumentNullException(nameof(hostServices)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } if (templateProcessor == null) { throw new ArgumentNullException(nameof(templateProcessor)); } _context = context; _templateProcessor = templateProcessor; Init(hostServices); }
private List <ManifestItem> ProcessCore(List <InternalManifestItem> items, DocumentBuildContext context, ApplyTemplateSettings settings, IDictionary <string, object> globals) { var manifest = new ConcurrentBag <ManifestItem>(); var systemAttributeGenerator = new SystemMetadataGenerator(context); var transformer = new TemplateModelTransformer(context, _templateCollection, settings, globals); items.RunAll( item => { var manifestItem = transformer.Transform(item); if (manifestItem.OutputFiles?.Count > 0) { manifest.Add(manifestItem); } }, context.MaxParallelism); return(manifest.ToList()); }
private static IEnumerable <string> GetFilesFromUids(DocumentBuildContext context, IEnumerable <string> uids) { foreach (var uid in uids) { if (string.IsNullOrEmpty(uid)) { continue; } XRefSpec spec; if (!context.XRefSpecMap.TryGetValue(uid, out spec)) { continue; } if (spec.Href != null) { yield return(spec.Href); } } }
internal List <ManifestItem> Process(List <InternalManifestItem> manifest, DocumentBuildContext context, ApplyTemplateSettings settings, IDictionary <string, object> globals = null) { using (new LoggerPhaseScope("Apply Templates", LogLevel.Verbose)) { if (globals == null) { globals = Tokens.ToDictionary(pair => pair.Key, pair => (object)pair.Value); } if (settings == null) { settings = context.ApplyTemplateSettings; } Logger.LogInfo($"Applying templates to {manifest.Count} model(s)..."); var documentTypes = new HashSet <string>(manifest.Select(s => s.DocumentType)); ProcessDependencies(documentTypes, settings); var templateManifest = ProcessCore(manifest, context, settings, globals); return(templateManifest); } }
private static void HandleToc(DocumentBuildContext context, SaveResult result) { if (result.TocMap?.Count > 0) { foreach (var toc in result.TocMap) { context.TocMap.AddOrUpdate( toc.Key, toc.Value, (k, v) => { foreach (var item in toc.Value) { v.Add(item); } return(v); }); } } }
internal List<ManifestItem> Process(List<InternalManifestItem> manifest, DocumentBuildContext context, ApplyTemplateSettings settings, IDictionary<string, object> globals = null) { using (new LoggerPhaseScope("Apply Templates", true)) { if (globals == null) { globals = Tokens.ToDictionary(pair => pair.Key, pair => (object)pair.Value); } if (settings == null) { settings = context.ApplyTemplateSettings; } Logger.LogInfo($"Applying templates to {manifest.Count} model(s)..."); var documentTypes = new HashSet<string>(manifest.Select(s => s.DocumentType)); ProcessDependencies(documentTypes, settings); var templateManifest = ProcessCore(manifest, context, settings, globals); return templateManifest; } }
private void PrepareBuild(DocumentBuildContext context, IEnumerable<HostService> hostServices) { var incrementalContext = context.IncrementalBuildContext; var lbv = incrementalContext?.LastBuildVersionInfo; var cbv = incrementalContext?.CurrentBuildVersionInfo; if (ShouldTraceIncrementalInfo) { var ldg = lbv?.Dependency; var cdg = cbv.Dependency; if (ldg != null) { // reregister dependency types from last dependency graph using (new LoggerPhaseScope("RegisterDependencyTypeFromLastBuild", true)) { cdg.RegisterDependencyType(ldg.DependencyTypes.Values); } // restore dependency graph from last dependency graph using (new LoggerPhaseScope("ReportDependencyFromLastBuild", true)) { cdg.ReportDependency(from r in ldg.ReportedBys from i in ldg.GetDependencyReportedBy(r) select i); } } } foreach (var hostService in hostServices) { hostService.SourceFiles = context.AllSourceFiles; foreach (var m in hostService.Models) { if (m.LocalPathFromRepoRoot == null) { m.LocalPathFromRepoRoot = TypeForwardedToStringExtension.ToDisplayPath(Path.Combine(m.BaseDir, m.File)); } if (m.LocalPathFromRoot == null) { m.LocalPathFromRoot = TypeForwardedToStringExtension.ToDisplayPath(Path.Combine(m.BaseDir, m.File)); } } if (ShouldTraceIncrementalInfo) { hostService.DependencyGraph = cbv.Dependency; using (new LoggerPhaseScope("RegisterDependencyTypeFromProcessor", true)) { RegisterDependencyType(hostService); } } } }
private static void UpdateHostServicesPerchanges(DocumentBuildContext context, IEnumerable<HostService> hostServices) { var incrementalContext = context.IncrementalBuildContext; var cbv = incrementalContext.CurrentBuildVersionInfo; UpdateUidDependency(hostServices, context); if (incrementalContext.CanVersionIncremental) { var newChanges = incrementalContext.ExpandDependency(cbv.Dependency, d => true); Logger.LogDiagnostic($"After expanding dependency before postbuild, changes: {JsonUtility.Serialize(incrementalContext.ChangeDict)}"); foreach (var hostService in hostServices) { hostService.ReloadModelsPerIncrementalChanges(incrementalContext, newChanges, BuildPhase.PostBuild); } } }
private InternalManifestItem HandleSaveResult( DocumentBuildContext context, HostService hostService, FileModel model, SaveResult result) { context.FileMap[model.Key] = ((TypeForwardedToRelativePath)model.File).GetPathFromWorkingFolder(); DocumentException.RunAll( () => CheckFileLink(hostService, result), () => HandleUids(context, result), () => HandleToc(context, result), () => RegisterXRefSpec(context, result)); return GetManifestItem(context, model, result); }
private void UpdateContext(DocumentBuildContext context) { context.ResolveExternalXRefSpec(); }
private static ITemplatePreprocessor CreatePreprocessor(ResourceCollection resourceCollection, TemplatePreprocessorResource scriptResource, DocumentBuildContext context) { return new TemplateJintPreprocessor(resourceCollection, scriptResource, context); }
/// <summary> /// Export xref map file. /// </summary> private static string ExportXRefMap(DocumentBuildParameters parameters, DocumentBuildContext context) { Logger.LogVerbose("Exporting xref map..."); var xrefMap = new XRefMap(); xrefMap.References = (from xref in context.XRefSpecMap.Values.AsParallel().WithDegreeOfParallelism(parameters.MaxParallelism) select new XRefSpec(xref) { Href = ((RelativePath)context.FileMap[UriUtility.GetNonFragment(xref.Href)]).RemoveWorkingFolder() + UriUtility.GetFragment(xref.Href) }).ToList(); xrefMap.Sort(); string xrefMapFileNameWithVersion = string.IsNullOrEmpty(parameters.VersionName) ? XRefMapFileName : parameters.VersionName + "." + XRefMapFileName; YamlUtility.Serialize( Path.Combine(parameters.OutputBaseDir, xrefMapFileNameWithVersion), xrefMap, YamlMime.XRefMap); Logger.LogInfo("XRef map exported."); return xrefMapFileNameWithVersion; }
private IEnumerable<HostService> GetInnerContexts( DocumentBuildParameters parameters, IEnumerable<IDocumentProcessor> processors, TemplateProcessor templateProcessor, IMarkdownService markdownService, DocumentBuildContext context) { var k = from fileItem in ( from file in parameters.Files.EnumerateFiles() from p in (from processor in processors let priority = processor.GetProcessingPriority(file) where priority != ProcessingPriority.NotSupported group processor by priority into ps orderby ps.Key descending select ps.ToList()).FirstOrDefault() ?? new List<IDocumentProcessor> { null } select new { file, p }) group fileItem by fileItem.p; var toHandleItems = k.Where(s => s.Key != null); var notToHandleItems = k.Where(s => s.Key == null); foreach (var item in notToHandleItems) { var sb = new StringBuilder(); sb.AppendLine("Cannot handle following file:"); foreach (var f in item) { sb.Append("\t"); sb.AppendLine(f.file.File); } Logger.LogWarning(sb.ToString()); } // todo : revert until PreProcessor ready foreach (var pair in (from processor in processors join item in toHandleItems on processor equals item.Key into g from item in g.DefaultIfEmpty() select new { processor, item, }).AsParallel().WithDegreeOfParallelism(parameters.MaxParallelism)) { var incrementalContext = context.IncrementalBuildContext; var processorSupportIncremental = IsProcessorSupportIncremental(pair.processor); bool processorCanIncremental = processorSupportIncremental; if (processorSupportIncremental) { incrementalContext.CreateProcessorInfo(pair.processor); processorCanIncremental = incrementalContext.CanProcessorIncremental(pair.processor); } var hostService = new HostService( parameters.Files.DefaultBaseDir, pair.item == null ? new FileModel[0] : from file in pair.item select Load(pair.processor, parameters.Metadata, parameters.FileMetadata, file.file, processorCanIncremental, context) into model where model != null select model) { MarkdownService = markdownService, Processor = pair.processor, Template = templateProcessor, Validators = MetadataValidators.ToImmutableList(), ShouldTraceIncrementalInfo = processorSupportIncremental, CanIncrementalBuild = processorCanIncremental, }; if (ShouldTraceIncrementalInfo) { using (new LoggerPhaseScope("ReportModelLoadInfo", true)) { var allFiles = pair.item?.Select(f => f.file.File) ?? new string[0]; var loadedFiles = hostService.Models.Select(m => m.FileAndType.File); incrementalContext.ReportModelLoadInfo(hostService, allFiles.Except(loadedFiles), null); incrementalContext.ReportModelLoadInfo(hostService, loadedFiles, BuildPhase.PreBuild); } } yield return hostService; } }
private void RegisterXRefSpec(DocumentBuildContext context, SaveResult result) { foreach (var spec in result.XRefSpecs) { if (!string.IsNullOrWhiteSpace(spec?.Uid)) { XRefSpec xref; if (context.XRefSpecMap.TryGetValue(spec.Uid, out xref)) { Logger.LogWarning($"Uid({spec.Uid}) has already been defined in {((TypeForwardedToRelativePath)xref.Href).RemoveWorkingFolder()}."); } else { context.RegisterInternalXrefSpec(spec); } } } foreach (var spec in result.ExternalXRefSpecs) { if (!string.IsNullOrWhiteSpace(spec?.Uid)) { context.ReportExternalXRefSpec(spec); } } }
private Manifest BuildCore(DocumentBuildParameters parameters) { using (new LoggerPhaseScope(PhaseName, true)) { Logger.LogInfo($"Max parallelism is {parameters.MaxParallelism}."); Directory.CreateDirectory(parameters.OutputBaseDir); var context = new DocumentBuildContext( Path.Combine(Directory.GetCurrentDirectory(), parameters.OutputBaseDir), parameters.Files.EnumerateFiles(), parameters.ExternalReferencePackages, parameters.XRefMaps, parameters.MaxParallelism, parameters.Files.DefaultBaseDir); if (ShouldTraceIncrementalInfo) { context.IncrementalBuildContext = IncrementalBuildContext.Create(parameters, CurrentBuildInfo, LastBuildInfo, IntermediateFolder); Logger.RegisterListener(context.IncrementalBuildContext.CurrentBuildVersionInfo.BuildMessage.GetListener()); if (context.IncrementalBuildContext.CanVersionIncremental) { context.IncrementalBuildContext.LoadChanges(); Logger.LogVerbose($"Before expanding dependency before build, changes: {JsonUtility.Serialize(context.IncrementalBuildContext.ChangeDict, Formatting.Indented)}"); var dependencyGraph = context.IncrementalBuildContext.LastBuildVersionInfo.Dependency; context.IncrementalBuildContext.ExpandDependency(dependencyGraph, d => dependencyGraph.DependencyTypes[d.Type].Phase == BuildPhase.Build || dependencyGraph.DependencyTypes[d.Type].TriggerBuild); Logger.LogVerbose($"After expanding dependency before build, changes: {JsonUtility.Serialize(context.IncrementalBuildContext.ChangeDict, Formatting.Indented)}"); } } Logger.LogVerbose("Start building document..."); // Start building document... List<HostService> hostServices = null; try { using (var templateProcessor = parameters.TemplateManager?.GetTemplateProcessor(context, parameters.MaxParallelism) ?? TemplateProcessor.DefaultProcessor) { IMarkdownService markdownService; using (new LoggerPhaseScope("CreateMarkdownService", true)) { markdownService = CreateMarkdownService(parameters, templateProcessor.Tokens.ToImmutableDictionary()); } using (new LoggerPhaseScope("Load", true)) { hostServices = GetInnerContexts(parameters, Processors, templateProcessor, markdownService, context).ToList(); } var manifest = BuildCore(hostServices, context, parameters.VersionName).ToList(); // Use manifest from now on using (new LoggerPhaseScope("UpdateContext", true)) { UpdateContext(context); } // Run getOptions from Template using (new LoggerPhaseScope("FeedOptions", true)) { FeedOptions(manifest, context); } // Template can feed back xref map, actually, the anchor # location can only be determined in template using (new LoggerPhaseScope("FeedXRefMap", true)) { FeedXRefMap(manifest, context); } using (new LoggerPhaseScope("UpdateHref", true)) { UpdateHref(manifest, context); } // Afterwards, m.Item.Model.Content is always IDictionary using (new LoggerPhaseScope("ApplySystemMetadata", true)) { ApplySystemMetadata(manifest, context); } // Register global variables after href are all updated IDictionary<string, object> globalVariables; using (new LoggerPhaseScope("FeedGlobalVariables", true)) { globalVariables = FeedGlobalVariables(templateProcessor.Tokens, manifest, context); } // processor to add global variable to the model foreach (var m in templateProcessor.Process(manifest.Select(s => s.Item).ToList(), context, parameters.ApplyTemplateSettings, globalVariables)) { context.ManifestItems.Add(m); } return new Manifest { Files = context.ManifestItems.ToList(), Homepages = GetHomepages(context), XRefMap = ExportXRefMap(parameters, context), SourceBasePath = TypeForwardedToStringExtension.ToNormalizedPath(EnvironmentContext.BaseDirectory) }; } } finally { if (hostServices != null) { foreach (var item in hostServices) { Cleanup(item); item.Dispose(); } } } } }
private static FileModel Load( IDocumentProcessor processor, ImmutableDictionary<string, object> metadata, FileMetadata fileMetadata, FileAndType file, bool canProcessorIncremental, DocumentBuildContext context) { using (new LoggerFileScope(file.File)) { Logger.LogDiagnostic($"Processor {processor.Name}, File {file.FullPath}: Loading..."); if (canProcessorIncremental) { var incrementalContext = context.IncrementalBuildContext; ChangeKindWithDependency ck; string fileKey = ((TypeForwardedToRelativePath)file.File).GetPathFromWorkingFolder().ToString(); if (incrementalContext.ChangeDict.TryGetValue(fileKey, out ck)) { Logger.LogDiagnostic($"Processor {processor.Name}, File {file.FullPath}, ChangeType {ck}."); if (ck == ChangeKindWithDependency.Deleted) { return null; } if (ck == ChangeKindWithDependency.None) { Logger.LogDiagnostic($"Processor {processor.Name}, File {file.FullPath}: Check incremental..."); if (processor.BuildSteps.Cast<ISupportIncrementalBuildStep>().All(step => step.CanIncrementalBuild(file))) { Logger.LogDiagnostic($"Processor {processor.Name}, File {file.FullPath}: Skip build by incremental."); return null; } Logger.LogDiagnostic($"Processor {processor.Name}, File {file.FullPath}: Incremental not available."); } } } var path = Path.Combine(file.BaseDir, file.File); metadata = ApplyFileMetadata(path, metadata, fileMetadata); try { return processor.Load(file, metadata); } catch (Exception) { Logger.LogError($"Unable to load file: {file.File} via processor: {processor.Name}."); throw; } } }
private static void HandleToc(DocumentBuildContext context, SaveResult result) { if (result.TocMap?.Count > 0) { foreach (var toc in result.TocMap) { HashSet<string> list; if (context.TocMap.TryGetValue(toc.Key, out list)) { foreach (var item in toc.Value) { list.Add(item); } } else { context.TocMap[toc.Key] = toc.Value; } } } }
private void BuildCore(PhaseProcessor phaseProcessor, List<HostService> hostServices, DocumentBuildContext context) { try { phaseProcessor.Process(hostServices, context.MaxParallelism); } catch (BuildCacheException e) { var message = $"Build cache was corrupted, please try force rebuild `build --force` or clear the cache files in the path: {IntermediateFolder}. Detail error: {e.Message}."; Logger.LogError(message); throw new DocfxException(message, e); } }
public TemplateUtility(DocumentBuildContext context) { _context = context; }
private Manifest BuildCore(DocumentBuildParameters parameters) { using (new LoggerPhaseScope(PhaseName, true)) { Logger.LogInfo($"Max parallelism is {parameters.MaxParallelism}."); Directory.CreateDirectory(parameters.OutputBaseDir); var context = new DocumentBuildContext( Path.Combine(Directory.GetCurrentDirectory(), parameters.OutputBaseDir), parameters.Files.EnumerateFiles(), parameters.ExternalReferencePackages, parameters.XRefMaps, parameters.MaxParallelism, parameters.Files.DefaultBaseDir, parameters.VersionName, parameters.ApplyTemplateSettings, parameters.RootTocPath); Logger.LogVerbose("Start building document..."); // Start building document... List<HostService> hostServices = null; IHostServiceCreator hostServiceCreator = null; PhaseProcessor phaseProcessor = null; try { using (var templateProcessor = parameters.TemplateManager?.GetTemplateProcessor(context, parameters.MaxParallelism) ?? TemplateProcessor.DefaultProcessor) { using (new LoggerPhaseScope("Prepare", true)) { if (MarkdownService == null) { MarkdownService = CreateMarkdownService(parameters, templateProcessor.Tokens.ToImmutableDictionary()); } Prepare( parameters, context, templateProcessor, (MarkdownService as IHasIncrementalContext)?.GetIncrementalContextHash(), out hostServiceCreator, out phaseProcessor); } using (new LoggerPhaseScope("Load", true)) { hostServices = GetInnerContexts(parameters, Processors, templateProcessor, hostServiceCreator).ToList(); } BuildCore(phaseProcessor, hostServices, context); return new Manifest { Files = context.ManifestItems.ToList(), Homepages = GetHomepages(context), XRefMap = ExportXRefMap(parameters, context), SourceBasePath = StringExtension.ToNormalizedPath(EnvironmentContext.BaseDirectory) }; } } finally { if (hostServices != null) { foreach (var item in hostServices) { Cleanup(item); item.Dispose(); } } } } }
private static InternalManifestItem GetManifestItem(DocumentBuildContext context, FileModel model, SaveResult result) { return new InternalManifestItem { DocumentType = result.DocumentType, FileWithoutExtension = result.FileWithoutExtension, ResourceFile = result.ResourceFile, Key = model.Key, LocalPathFromRoot = model.LocalPathFromRoot, Model = model.ModelWithCache, InputFolder = model.OriginalFileAndType.BaseDir, Metadata = new Dictionary<string, object>((IDictionary<string, object>)model.ManifestProperties), }; }
public TemplateProcessor GetTemplateProcessor(DocumentBuildContext context, int maxParallelism) { return new TemplateProcessor(CreateTemplateResource(_templates), context, maxParallelism); }
private IEnumerable<ManifestItemWithContext> ExportManifest(HostService hostService, DocumentBuildContext context) { var manifestItems = new List<ManifestItemWithContext>(); using (new LoggerPhaseScope("Save", true)) { hostService.Models.RunAll(m => { if (m.Type != DocumentType.Overwrite) { using (new LoggerFileScope(m.LocalPathFromRoot)) { Logger.LogDiagnostic($"Processor {hostService.Processor.Name}: Saving..."); m.BaseDir = context.BuildOutputFolder; if (m.FileAndType.SourceDir != m.FileAndType.DestinationDir) { m.File = (TypeForwardedToRelativePath)m.FileAndType.DestinationDir + (((TypeForwardedToRelativePath)m.File) - (TypeForwardedToRelativePath)m.FileAndType.SourceDir); } var result = hostService.Processor.Save(m); if (result != null) { string extension = string.Empty; if (hostService.Template != null) { if (hostService.Template.TryGetFileExtension(result.DocumentType, out extension)) { m.File = result.FileWithoutExtension + extension; } } var item = HandleSaveResult(context, hostService, m, result); item.Extension = extension; manifestItems.Add(new ManifestItemWithContext(item, m, hostService.Processor, hostService.Template?.GetTemplateBundle(result.DocumentType))); } } } }); } return manifestItems; }
private static List<HomepageInfo> GetHomepages(DocumentBuildContext context) { return context.GetTocInfo() .Where(s => !string.IsNullOrEmpty(s.Homepage)) .Select(s => new HomepageInfo { Homepage = RelativePath.GetPathWithoutWorkingFolderChar(s.Homepage), TocPath = RelativePath.GetPathWithoutWorkingFolderChar(context.GetFilePath(s.TocFileKey)) }).ToList(); }
private List<ManifestItem> ProcessCore(List<InternalManifestItem> items, DocumentBuildContext context, ApplyTemplateSettings settings, IDictionary<string, object> globals) { var manifest = new ConcurrentBag<ManifestItem>(); var systemAttributeGenerator = new SystemMetadataGenerator(context); var transformer = new TemplateModelTransformer(context, _templateCollection, settings, globals); items.RunAll( item => { using (new LoggerFileScope(item.LocalPathFromRoot)) { var manifestItem = transformer.Transform(item); if (manifestItem.OutputFiles?.Count > 0) { manifest.Add(manifestItem); } } }, context.MaxParallelism); return manifest.ToList(); }