Пример #1
0
        public void Process(DocumentBuildContext context, string outputDirectory)
        {
            var baseDirectory = context.BuildOutputFolder;

            if (string.IsNullOrEmpty(outputDirectory)) outputDirectory = Environment.CurrentDirectory;
            if (string.IsNullOrEmpty(baseDirectory)) baseDirectory = Environment.CurrentDirectory;

            if (!Directory.Exists(outputDirectory)) Directory.CreateDirectory(outputDirectory);

            // 1. Copy dependent files with path relative to the base output directory
            ProcessDependencies(outputDirectory);
            Dictionary<string, HashSet<string>> unProcessedType = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase);
            Dictionary<string, string> extMapping = new Dictionary<string, string>();
            // 2. Get extension for each item
            foreach (var item in context.Manifest)
            {
                if (item.ModelFile == null) throw new ArgumentNullException("Model file path must be specified!");
                if (item.DocumentType == null) throw new ArgumentNullException($"Document type is not allowed to be NULL for ${item.ModelFile}!");
                // NOTE: Resource is not supported for applying templates
                if (item.DocumentType.Equals("Resource", StringComparison.OrdinalIgnoreCase)) continue;
                var templates = _templates[item.DocumentType];
                // Get default template extension
                if (templates == null || templates.Count == 0)
                {
                    HashSet<string> unProcessedFiles;
                    if (unProcessedType.TryGetValue(item.DocumentType, out unProcessedFiles))
                    {
                        unProcessedFiles.Add(item.ModelFile);
                    }
                    else
                    {
                        unProcessedType[item.DocumentType] = new HashSet<string>(FilePathComparer.OSPlatformSensitiveComparer) { item.ModelFile };
                    }
                }
                else
                {
                    var defaultTemplate = templates.FirstOrDefault(s => s.IsPrimary) ?? templates[0];
                    string key = ((RelativePath)item.OriginalFile).GetPathFromWorkingFolder();
                    string value;
                    if (context.FileMap.TryGetValue(key, out value))
                    {
                        context.FileMap[key] = Path.ChangeExtension(value, defaultTemplate.Extension);
                        extMapping[key] = defaultTemplate.Extension;
                    }
                    else
                    {
                        Logger.Log(LogLevel.Warning, $"{key} is not found in .filemap");
                    }
                }
            }

            //update internal XrefMap
            if (context.XRefSpecMap != null)
            {
                foreach (var pair in context.XRefSpecMap)
                {
                    string ext;
                    if (extMapping.TryGetValue(pair.Value.Href, out ext))
                    {
                        pair.Value.Href = Path.ChangeExtension(pair.Value.Href, ext);
                    }
                }
            }

            if (unProcessedType.Count > 0)
            {
                StringBuilder sb = new StringBuilder("There is no template processing:");
                foreach (var type in unProcessedType)
                {
                    sb.AppendLine($"- Document type: \"{type.Key}\"");
                    sb.AppendLine($"- Files:");
                    foreach (var file in type.Value)
                    {
                        sb.AppendLine($"  -\"{file}\"");
                    }
                }
                Logger.Log(LogLevel.Warning, sb.ToString());// not processed but copied to '{modelOutputPath}'");
            }

            List<TemplateManifestItem> manifest = new List<TemplateManifestItem>();

            // 3. Process every model and save to output directory
            foreach (var item in context.Manifest)
            {
                var manifestItem = new TemplateManifestItem
                {
                    DocumentType = item.DocumentType,
                    OriginalFile = item.LocalPathFromRepoRoot,
                    OutputFiles = new Dictionary<string, string>()
                };
                try
                {
                    var templates = _templates[item.DocumentType];
                    // 1. process model
                    if (templates == null)
                    {
                        // TODO: what if template to transform the type is not found? DO NOTHING?
                        // CopyFile(modelFile, modelOutputPath);
                    }
                    else
                    {
                        var modelFile = Path.Combine(baseDirectory, item.ModelFile);
                        var systemAttrs = new SystemAttributes(context, item, TemplateProcessor.Language);
                        foreach (var template in templates)
                        {
                            var extension = template.Extension;
                            string outputFile = Path.ChangeExtension(item.ModelFile, extension);
                            string outputPath = Path.Combine(outputDirectory ?? string.Empty, outputFile);
                            var dir = Path.GetDirectoryName(outputPath);
                            if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir);
                            var transformed = template.Transform(modelFile, systemAttrs);
                            if (!string.IsNullOrWhiteSpace(transformed))
                            {
                                if (extension.Equals(".html", StringComparison.OrdinalIgnoreCase))
                                {
                                    TranformHtml(context, transformed, item.ModelFile, outputPath);
                                }
                                else
                                {
                                    File.WriteAllText(outputPath, transformed, Encoding.UTF8);
                                }

                                Logger.Log(LogLevel.Verbose, $"Transformed model \"{item.ModelFile}\" to \"{outputPath}\".");
                            }
                            else
                            {
                                // TODO: WHAT to do if is transformed to empty string? STILL creat empty file?
                                Logger.Log(LogLevel.Warning, $"Model \"{item.ModelFile}\" is transformed to empty string with template \"{template.Name}\"");
                                File.WriteAllText(outputPath, string.Empty);
                            }
                            manifestItem.OutputFiles.Add(extension, outputFile);
                        }
                    }

                    // 2. process resource
                    if (item.ResourceFile != null)
                    {
                        manifestItem.OutputFiles.Add("resource", item.ResourceFile);
                        PathUtility.CopyFile(Path.Combine(baseDirectory, item.ResourceFile), Path.Combine(outputDirectory, item.ResourceFile), true);
                    }
                }
                catch (Exception e)
                {
                    Logger.Log(LogLevel.Warning, $"Unable to transform {item.ModelFile}: {e.Message}. Ignored.");
                }
                manifest.Add(manifestItem);
            }

            // Save manifest
            var manifestPath = Path.Combine(outputDirectory, ManifestFileName);
            JsonUtility.Serialize(manifestPath, manifest);
            Logger.Log(LogLevel.Verbose, $"Manifest file saved to {manifestPath}.");
        }
Пример #2
0
        public static TemplateManifestItem Transform(DocumentBuildContext context, ManifestItem item, TemplateCollection templateCollection, string outputDirectory, bool exportMetadata, Func <string, string> metadataFilePathProvider)
        {
            var baseDirectory = context.BuildOutputFolder ?? string.Empty;
            var manifestItem  = new TemplateManifestItem
            {
                DocumentType = item.DocumentType,
                OriginalFile = item.LocalPathFromRepoRoot,
                OutputFiles  = new Dictionary <string, string>()
            };

            if (templateCollection == null || templateCollection.Count == 0)
            {
                return(manifestItem);
            }
            try
            {
                var model     = item.Model?.Content;
                var templates = templateCollection[item.DocumentType];
                // 1. process model
                if (templates == null)
                {
                    // Logger.LogWarning($"There is no template processing {item.DocumentType} document \"{item.LocalPathFromRepoRoot}\"");
                }
                else
                {
                    var modelFile   = Path.Combine(baseDirectory, item.ModelFile);
                    var systemAttrs = new SystemAttributes(context, item, TemplateProcessor.Language);
                    foreach (var template in templates)
                    {
                        var    extension  = template.Extension;
                        string outputFile = Path.ChangeExtension(item.ModelFile, extension);
                        string outputPath = Path.Combine(outputDirectory ?? string.Empty, outputFile);
                        var    dir        = Path.GetDirectoryName(outputPath);
                        if (!string.IsNullOrEmpty(dir))
                        {
                            Directory.CreateDirectory(dir);
                        }
                        string transformed;
                        if (model == null)
                        {
                            // TODO: remove
                            // currently keep to pass UT
                            transformed = template.Transform(item.ModelFile, systemAttrs);
                        }
                        else
                        {
                            var result = template.TransformModel(model, systemAttrs);

                            if (exportMetadata)
                            {
                                if (metadataFilePathProvider == null)
                                {
                                    throw new ArgumentNullException(nameof(metadataFilePathProvider));
                                }

                                JsonUtility.Serialize(metadataFilePathProvider(outputPath), result.Model);
                            }

                            transformed = result.Result;
                        }

                        if (!string.IsNullOrWhiteSpace(transformed))
                        {
                            if (extension.Equals(".html", StringComparison.OrdinalIgnoreCase))
                            {
                                TranformHtml(context, transformed, item.ModelFile, outputPath);
                            }
                            else
                            {
                                File.WriteAllText(outputPath, transformed, Encoding.UTF8);
                            }

                            Logger.Log(LogLevel.Verbose, $"Transformed model \"{item.ModelFile}\" to \"{outputPath}\".");
                        }
                        else
                        {
                            // TODO: WHAT to do if is transformed to empty string? STILL creat empty file?
                            Logger.LogWarning($"Model \"{item.ModelFile}\" is transformed to empty string with template \"{template.Name}\"");
                            File.WriteAllText(outputPath, string.Empty);
                        }
                        manifestItem.OutputFiles.Add(extension, outputFile);
                    }
                }

                // 2. process resource
                if (item.ResourceFile != null)
                {
                    PathUtility.CopyFile(Path.Combine(baseDirectory, item.ResourceFile), Path.Combine(outputDirectory, item.ResourceFile), true);
                    manifestItem.OutputFiles.Add("resource", item.ResourceFile);
                }
            }
            catch (Exception e)
            {
                Logger.LogWarning($"Unable to transform {item.ModelFile}: {e.Message}. Ignored.");
            }

            return(manifestItem);
        }
Пример #3
0
        public static TemplateManifestItem Transform(DocumentBuildContext context, ManifestItem item, TemplateCollection templateCollection, string outputDirectory, bool exportMetadata, Func<string, string> metadataFilePathProvider)
        {
            var baseDirectory = context.BuildOutputFolder ?? string.Empty;
            var manifestItem = new TemplateManifestItem
            {
                DocumentType = item.DocumentType,
                OriginalFile = item.LocalPathFromRepoRoot,
                OutputFiles = new Dictionary<string, string>()
            };
            if (templateCollection == null || templateCollection.Count == 0)
            {
                return manifestItem;
            }
            try
            {
                var model = item.Model?.Content;
                var templates = templateCollection[item.DocumentType];
                // 1. process model
                if (templates == null)
                {
                    // Logger.LogWarning($"There is no template processing {item.DocumentType} document \"{item.LocalPathFromRepoRoot}\"");
                }
                else
                {
                    var modelFile = Path.Combine(baseDirectory, item.ModelFile);
                    var systemAttrs = new SystemAttributes(context, item, TemplateProcessor.Language);
                    foreach (var template in templates)
                    {
                        var extension = template.Extension;
                        string outputFile = Path.ChangeExtension(item.ModelFile, extension);
                        string outputPath = Path.Combine(outputDirectory ?? string.Empty, outputFile);
                        var dir = Path.GetDirectoryName(outputPath);
                        if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir);
                        string transformed;
                        if (model == null)
                        {
                            // TODO: remove
                            // currently keep to pass UT
                            transformed = template.Transform(item.ModelFile, systemAttrs);
                        }
                        else
                        {
                            var result = template.TransformModel(model, systemAttrs);

                            if (exportMetadata)
                            {
                                if (metadataFilePathProvider == null)
                                {
                                    throw new ArgumentNullException(nameof(metadataFilePathProvider));
                                }

                                JsonUtility.Serialize(metadataFilePathProvider(outputPath), result.Model);
                            }

                            transformed = result.Result;
                        }

                        if (!string.IsNullOrWhiteSpace(transformed))
                        {
                            if (extension.Equals(".html", StringComparison.OrdinalIgnoreCase))
                            {
                                TranformHtml(context, transformed, item.ModelFile, outputPath);
                            }
                            else
                            {
                                File.WriteAllText(outputPath, transformed, Encoding.UTF8);
                            }

                            Logger.Log(LogLevel.Verbose, $"Transformed model \"{item.ModelFile}\" to \"{outputPath}\".");
                        }
                        else
                        {
                            // TODO: WHAT to do if is transformed to empty string? STILL creat empty file?
                            Logger.LogWarning($"Model \"{item.ModelFile}\" is transformed to empty string with template \"{template.Name}\"");
                            File.WriteAllText(outputPath, string.Empty);
                        }
                        manifestItem.OutputFiles.Add(extension, outputFile);
                    }
                }

                // 2. process resource
                if (item.ResourceFile != null)
                {
                    PathUtility.CopyFile(Path.Combine(baseDirectory, item.ResourceFile), Path.Combine(outputDirectory, item.ResourceFile), true);
                    manifestItem.OutputFiles.Add("resource", item.ResourceFile);
                }
            }
            catch (Exception e)
            {
                Logger.LogWarning($"Unable to transform {item.ModelFile}: {e.Message}. Ignored.");
            }

            return manifestItem;
        }
Пример #4
0
        private TemplateManifestItem TransformItem(ManifestItem item, IDocumentBuildContext context, ApplyTemplateSettings settings)
        {
            if (settings.Options.HasFlag(ApplyTemplateOptions.ExportRawModel))
            {
                ExportModel(item.Model.Content, item.ModelFile, settings.RawModelExportSettings);
            }

            if (item.Model == null || item.Model.Content == null)
            {
                throw new ArgumentNullException("Content for item.Model should not be null!");
            }
            var manifestItem = new TemplateManifestItem
            {
                DocumentType = item.DocumentType,
                OriginalFile = item.LocalPathFromRepoRoot,
                OutputFiles  = new Dictionary <string, string>()
            };
            var outputDirectory = settings.OutputFolder ?? Environment.CurrentDirectory;

            if (!IsEmpty)
            {
                HashSet <string> missingUids = new HashSet <string>();

                // Must convert to JObject first as we leverage JsonProperty as the property name for the model
                var model     = ConvertToObjectHelper.ConvertStrongTypeToJObject(item.Model.Content);
                var templates = Templates[item.DocumentType];

                // 1. process model
                if (templates != null)
                {
                    var systemAttrs = new SystemAttributes(context, item, TemplateProcessor.Language);
                    foreach (var template in templates)
                    {
                        var    extension  = template.Extension;
                        string outputFile = Path.ChangeExtension(item.ModelFile, 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, systemAttrs, _global);
                        }
                        catch (Exception e)
                        {
                            // save raw model for further investigation:
                            var exportSettings = ApplyTemplateSettings.RawModelExportSettingsForDebug;
                            var rawModelPath   = ExportModel(item.Model, item.ModelFile, exportSettings);
                            throw new DocumentException($"Error transforming model \"{rawModelPath}\" generated from \"{item.LocalPathFromRepoRoot}\" using \"{template.ScriptName}\": {e.Message}");
                        }

                        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);
                            throw new DocumentException($"Error applying template \"{template.Name}\" to view model \"{viewModelPath}\" generated from \"{item.LocalPathFromRepoRoot}\": {e.Message}");
                        }

                        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?
                                Logger.LogWarning($"Model \"{item.ModelFile}\" is transformed to empty string with template \"{template.Name}\"");
                                File.WriteAllText(outputPath, string.Empty);
                            }
                            else
                            {
                                TransformDocument(result, extension, context, outputPath, item.ModelFile, missingUids);
                                Logger.Log(LogLevel.Verbose, $"Transformed model \"{item.LocalPathFromRepoRoot}\" to \"{outputPath}\".");
                            }

                            manifestItem.OutputFiles.Add(extension, outputFile);
                        }
                    }
                }

                if (missingUids.Count > 0)
                {
                    var uids = string.Join(", ", missingUids.Select(s => $"\"{s}\""));
                    Logger.LogWarning($"Unable to resolve cross-reference {uids} for \"{manifestItem.OriginalFile.ToDisplayPath()}\"");
                }
            }

            // 2. process resource
            if (item.ResourceFile != null)
            {
                // Resource file has already been processed in its plugin
                manifestItem.OutputFiles.Add("resource", item.ResourceFile);
            }

            return(manifestItem);
        }
Пример #5
0
        public void Process(DocumentBuildContext context, string outputDirectory)
        {
            var baseDirectory = context.BuildOutputFolder;

            if (string.IsNullOrEmpty(outputDirectory))
            {
                outputDirectory = Environment.CurrentDirectory;
            }
            if (string.IsNullOrEmpty(baseDirectory))
            {
                baseDirectory = Environment.CurrentDirectory;
            }

            if (!Directory.Exists(outputDirectory))
            {
                Directory.CreateDirectory(outputDirectory);
            }

            // 1. Copy dependent files with path relative to the base output directory
            ProcessDependencies(outputDirectory);
            Dictionary <string, HashSet <string> > unProcessedType = new Dictionary <string, HashSet <string> >(StringComparer.OrdinalIgnoreCase);
            Dictionary <string, string>            extMapping      = new Dictionary <string, string>();

            // 2. Get extension for each item
            foreach (var item in context.Manifest)
            {
                if (item.ModelFile == null)
                {
                    throw new ArgumentNullException("Model file path must be specified!");
                }
                if (item.DocumentType == null)
                {
                    throw new ArgumentNullException($"Document type is not allowed to be NULL for ${item.ModelFile}!");
                }
                // NOTE: Resource is not supported for applying templates
                if (item.DocumentType.Equals("Resource", StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }
                var templates = _templates[item.DocumentType];
                // Get default template extension
                if (templates == null || templates.Count == 0)
                {
                    HashSet <string> unProcessedFiles;
                    if (unProcessedType.TryGetValue(item.DocumentType, out unProcessedFiles))
                    {
                        unProcessedFiles.Add(item.ModelFile);
                    }
                    else
                    {
                        unProcessedType[item.DocumentType] = new HashSet <string>(FilePathComparer.OSPlatformSensitiveComparer)
                        {
                            item.ModelFile
                        };
                    }
                }
                else
                {
                    var    defaultTemplate = templates.FirstOrDefault(s => s.IsPrimary) ?? templates[0];
                    string key             = ((RelativePath)item.OriginalFile).GetPathFromWorkingFolder();
                    string value;
                    if (context.FileMap.TryGetValue(key, out value))
                    {
                        context.FileMap[key] = Path.ChangeExtension(value, defaultTemplate.Extension);
                        extMapping[key]      = defaultTemplate.Extension;
                    }
                    else
                    {
                        Logger.Log(LogLevel.Warning, $"{key} is not found in .filemap");
                    }
                }
            }

            //update internal XrefMap
            if (context.XRefSpecMap != null)
            {
                foreach (var pair in context.XRefSpecMap)
                {
                    string ext;
                    if (extMapping.TryGetValue(pair.Value.Href, out ext))
                    {
                        pair.Value.Href = Path.ChangeExtension(pair.Value.Href, ext);
                    }
                }
            }

            if (unProcessedType.Count > 0)
            {
                StringBuilder sb = new StringBuilder("There is no template processing:");
                foreach (var type in unProcessedType)
                {
                    sb.AppendLine($"- Document type: \"{type.Key}\"");
                    sb.AppendLine($"- Files:");
                    foreach (var file in type.Value)
                    {
                        sb.AppendLine($"  -\"{file}\"");
                    }
                }
                Logger.Log(LogLevel.Warning, sb.ToString());// not processed but copied to '{modelOutputPath}'");
            }

            List <TemplateManifestItem> manifest = new List <TemplateManifestItem>();

            // 3. Process every model and save to output directory
            foreach (var item in context.Manifest)
            {
                var manifestItem = new TemplateManifestItem
                {
                    DocumentType = item.DocumentType,
                    OriginalFile = item.LocalPathFromRepoRoot,
                    OutputFiles  = new Dictionary <string, string>()
                };
                try
                {
                    var templates = _templates[item.DocumentType];
                    // 1. process model
                    if (templates == null)
                    {
                        // TODO: what if template to transform the type is not found? DO NOTHING?
                        // CopyFile(modelFile, modelOutputPath);
                    }
                    else
                    {
                        var modelFile   = Path.Combine(baseDirectory, item.ModelFile);
                        var systemAttrs = new SystemAttributes(context, item, TemplateProcessor.Language);
                        foreach (var template in templates)
                        {
                            var    extension  = template.Extension;
                            string outputFile = Path.ChangeExtension(item.ModelFile, extension);
                            string outputPath = Path.Combine(outputDirectory ?? string.Empty, outputFile);
                            var    dir        = Path.GetDirectoryName(outputPath);
                            if (!string.IsNullOrEmpty(dir))
                            {
                                Directory.CreateDirectory(dir);
                            }
                            var transformed = template.Transform(modelFile, systemAttrs);
                            if (!string.IsNullOrWhiteSpace(transformed))
                            {
                                if (extension.Equals(".html", StringComparison.OrdinalIgnoreCase))
                                {
                                    TranformHtml(context, transformed, item.ModelFile, outputPath);
                                }
                                else
                                {
                                    File.WriteAllText(outputPath, transformed, Encoding.UTF8);
                                }

                                Logger.Log(LogLevel.Verbose, $"Transformed model \"{item.ModelFile}\" to \"{outputPath}\".");
                            }
                            else
                            {
                                // TODO: WHAT to do if is transformed to empty string? STILL creat empty file?
                                Logger.Log(LogLevel.Warning, $"Model \"{item.ModelFile}\" is transformed to empty string with template \"{template.Name}\"");
                                File.WriteAllText(outputPath, string.Empty);
                            }
                            manifestItem.OutputFiles.Add(extension, outputFile);
                        }
                    }

                    // 2. process resource
                    if (item.ResourceFile != null)
                    {
                        manifestItem.OutputFiles.Add("resource", item.ResourceFile);
                        PathUtility.CopyFile(Path.Combine(baseDirectory, item.ResourceFile), Path.Combine(outputDirectory, item.ResourceFile), true);
                    }
                }
                catch (Exception e)
                {
                    Logger.Log(LogLevel.Warning, $"Unable to transform {item.ModelFile}: {e.Message}. Ignored.");
                }
                manifest.Add(manifestItem);
            }

            // Save manifest
            var manifestPath = Path.Combine(outputDirectory, ManifestFileName);

            JsonUtility.Serialize(manifestPath, manifest);
            Logger.Log(LogLevel.Verbose, $"Manifest file saved to {manifestPath}.");
        }
Пример #6
0
        // TODO: change to use IDocumentBuildContext
        public static TemplateManifestItem Transform(DocumentBuildContext context, ManifestItem item, TemplateCollection templateCollection, string outputDirectory, bool exportMetadata, Func<string, string> metadataFilePathProvider)
        {
            if (item.Model == null || item.Model.Content == null) throw new ArgumentNullException("Content for item.Model should not be null!");
            var baseDirectory = context.BuildOutputFolder ?? string.Empty;
            var manifestItem = new TemplateManifestItem
            {
                DocumentType = item.DocumentType,
                OriginalFile = item.LocalPathFromRepoRoot,
                OutputFiles = new Dictionary<string, string>()
            };
            HashSet<string> missingUids = new HashSet<string>();
            if (templateCollection == null || templateCollection.Count == 0)
            {
                return manifestItem;
            }
            try
            {
                var model = item.Model.Content;
                var templates = templateCollection[item.DocumentType];
                // 1. process model
                if (templates == null)
                {
                    // Logger.LogWarning($"There is no template processing {item.DocumentType} document \"{item.LocalPathFromRepoRoot}\"");
                }
                else
                {
                    var systemAttrs = new SystemAttributes(context, item, TemplateProcessor.Language);
                    foreach (var template in templates)
                    {
                        var extension = template.Extension;
                        string outputFile = Path.ChangeExtension(item.ModelFile, extension);
                        string outputPath = Path.Combine(outputDirectory ?? string.Empty, outputFile);
                        var dir = Path.GetDirectoryName(outputPath);
                        if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir);
                        string transformed;
                        var result = template.TransformModel(model, systemAttrs);

                        if (exportMetadata)
                        {
                            if (metadataFilePathProvider == null)
                            {
                                throw new ArgumentNullException(nameof(metadataFilePathProvider));
                            }

                            JsonUtility.Serialize(metadataFilePathProvider(outputPath), result.Model);
                        }

                        transformed = result.Result;


                        if (!string.IsNullOrWhiteSpace(transformed))
                        {
                            if (extension.Equals(".html", StringComparison.OrdinalIgnoreCase))
                            {
                                try
                                {
                                    TranformHtml(context, transformed, item.ModelFile, outputPath);
                                }
                                catch (AggregateException e)
                                {
                                    e.Handle(s =>
                                    {
                                        var xrefExcetpion = s as CrossReferenceNotResolvedException;
                                        if (xrefExcetpion != null)
                                        {
                                            missingUids.Add(xrefExcetpion.UidRawText);
                                            return true;
                                        }
                                        else
                                        {
                                            return false;
                                        }
                                    });
                                }
                            }
                            else
                            {
                                File.WriteAllText(outputPath, transformed, Encoding.UTF8);
                            }

                            Logger.Log(LogLevel.Verbose, $"Transformed model \"{item.ModelFile}\" to \"{outputPath}\".");
                        }
                        else
                        {
                            // TODO: WHAT to do if is transformed to empty string? STILL creat empty file?
                            Logger.LogWarning($"Model \"{item.ModelFile}\" is transformed to empty string with template \"{template.Name}\"");
                            File.WriteAllText(outputPath, string.Empty);
                        }
                        manifestItem.OutputFiles.Add(extension, outputFile);
                    }
                }

                // 2. process resource
                if (item.ResourceFile != null)
                {
                    PathUtility.CopyFile(Path.Combine(baseDirectory, item.ResourceFile), Path.Combine(outputDirectory, item.ResourceFile), true);
                    manifestItem.OutputFiles.Add("resource", item.ResourceFile);
                }
            }
            catch (Exception e)
            {
                Logger.LogError($"Unable to transform {item.ModelFile}: {e.Message}. Ignored.");
                throw;
            }

            if (missingUids.Count > 0)
            {
                var uids = string.Join(", ", missingUids.Select(s => $"\"{s}\""));
                Logger.LogWarning($"Unable to resolve cross-reference {uids} for \"{manifestItem.OriginalFile.ToDisplayPath()}\"");
            }

            return manifestItem;
        }