public static IServiceCollection AddBicepCore()

in src/Analyzer.BicepProcessor/BicepTemplateProcessor.cs [48:152]


        public static IServiceCollection AddBicepCore(this IServiceCollection services) => services
            .AddSingleton<INamespaceProvider, NamespaceProvider>()
            .AddSingleton<IResourceTypeProviderFactory, ResourceTypeProviderFactory>()
            .AddSingleton<IContainerRegistryClientFactory, ContainerRegistryClientFactory>()
            .AddSingleton<IPublicRegistryModuleMetadataProvider, PublicRegistryModuleMetadataProvider>()
            .AddSingleton<ITemplateSpecRepositoryFactory, TemplateSpecRepositoryFactory>()
            .AddSingleton<IModuleDispatcher, ModuleDispatcher>()
            .AddSingleton<IArtifactRegistryProvider, DefaultArtifactRegistryProvider>()
            .AddSingleton<ITokenCredentialFactory, TokenCredentialFactory>()
            .AddSingleton<IFileResolver, FileResolver>()
            .AddSingleton<IFileExplorer, FileSystemFileExplorer>()
            .AddSingleton<IEnvironment, BicepEnvironment>()
            .AddSingleton<IFileSystem, IOFileSystem>()
            .AddSingleton<IConfigurationManager, ConfigurationManager>()
            .AddSingleton<IBicepAnalyzer, LinterAnalyzer>()
            .AddSingleton<IFeatureProviderFactory, FeatureProviderFactory>()
            .AddSingleton<FeatureProviderFactory>() // needed for below
            .AddSingleton<IFeatureProviderFactory, SourceMapFeatureProviderFactory>() // enable source mapping
            .AddSingleton<ILinterRulesProvider, LinterRulesProvider>()
            .AddSingleton<BicepCompiler>();

        private static readonly Regex IsModuleRegistryPathRegex = new("^br(:[\\w.]+\\/|\\/public:)", RegexOptions.CultureInvariant | RegexOptions.Compiled);
        private static BicepCompiler BicepCompiler = null;

        /// <summary>
        /// Converts Bicep template into JSON template and returns it as a string and its source map
        /// </summary>
        /// <param name="bicepPath">The Bicep template file path.</param>
        /// <returns>The compiled template as a <c>JSON</c> string and its source map.</returns>
        public static (string, BicepMetadata) ConvertBicepToJson(string bicepPath)
        {
            if (BicepCompiler == null)
            {
                var serviceCollection = new ServiceCollection();
                serviceCollection.AddBicepCore();

                var services = serviceCollection.BuildServiceProvider();
                BicepCompiler = services.GetRequiredService<BicepCompiler>();
            }

            using var stringWriter = new StringWriter();
            var compilation = BicepCompiler.CreateCompilation(new Uri(bicepPath)).Result;
            var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel());
            var emitResult = emitter.Emit(stringWriter);

            if (emitResult.Status == EmitStatus.Failed)
            {
                var bicepIssues = emitResult.Diagnostics
                    .Where(diag => diag.Level == DiagnosticLevel.Error)
                    .Select(diag => diag.Message);
                throw new Exception($"Bicep issues found:{SysEnvironment.NewLine}{string.Join(SysEnvironment.NewLine, bicepIssues)}");
            }

            string entryPointDirectory = Path.GetDirectoryName(compilation.SourceFileGrouping.EntryPoint.Uri.AbsolutePath);

            bool IsResolvedLocalModuleReference(KeyValuePair<IArtifactReferenceSyntax, ArtifactResolutionInfo> artifact) =>
                // Only include local module references (not modules imported from public/private registries, i.e. those that match IsModuleRegistryPathRegex),
                // as it is more useful for user to see line number of the module declaration itself,
                // rather than the line number in the module (as the user does not control the template in the registry directly).
                artifact.Key is ModuleDeclarationSyntax moduleDeclaration &&
                moduleDeclaration.Path is StringSyntax moduleDeclarationPath &&
                !moduleDeclarationPath.SegmentValues.Any(IsModuleRegistryPathRegex.IsMatch) &&
                artifact.Value.Result.IsSuccess();

            // Create SourceFileModuleInfo collection by gathering all needed module info from SourceFileGrouping metadata.
            // Group by the source file path to allow for easy construction of SourceFileModuleInfo.
            var moduleInfo = compilation.SourceFileGrouping.ArtifactLookup
                .Where(IsResolvedLocalModuleReference)
                .GroupBy(artifact => artifact.Value.Origin)
                .Select(grouping =>
                {
                    var bicepSourceFile = grouping.Key;
                    var pathRelativeToEntryPoint = Path.GetRelativePath(
                        Path.GetDirectoryName(compilation.SourceFileGrouping.EntryPoint.Uri.AbsolutePath), bicepSourceFile.Uri.AbsolutePath);

                    // Use the grouping value (KeyValuePair<IArtifactReferenceSyntax,ArtifactResolutionInfo>) to create
                    // a dictionary of module line numbers to file paths.
                    // This represents the modules in the source file, and where (what lines) they are referenced.
                    var modules = grouping.Select(artifactRefAndUriResult =>
                    {
                        var module = artifactRefAndUriResult.Key as ModuleDeclarationSyntax;
                        var moduleLine = TextCoordinateConverter.GetPosition(bicepSourceFile.LineStarts, module.Span.Position).line;
                        var modulePath = new FileInfo(artifactRefAndUriResult.Value.Result.Unwrap().AbsolutePath).FullName; // converts path to current platform

                        // Use relative paths for bicep to match file paths used in bicep modules and source map
                        if (modulePath.EndsWith(".bicep"))
                        {
                            modulePath = Path.GetRelativePath(entryPointDirectory, modulePath);
                        }

                        return new { moduleLine, modulePath };
                    })
                    .ToDictionary(c => c.moduleLine, c => c.modulePath);

                    return new SourceFileModuleInfo(pathRelativeToEntryPoint, modules);
                });

            var bicepMetadata = new BicepMetadata()
            {
                ModuleInfo = moduleInfo,
                SourceMap = emitResult.SourceMap
            };

            return (stringWriter.ToString(), bicepMetadata);
        }