// Internal for testing internal TagHelperResolutionResult GetTagHelpers(Compilation compilation) { var descriptors = new List <TagHelperDescriptor>(); var providers = new ITagHelperDescriptorProvider[] { new DefaultTagHelperDescriptorProvider() { DesignTime = true, }, new ViewComponentTagHelperDescriptorProvider() { ForceEnabled = ForceEnableViewComponentDiscovery }, }; var results = new List <TagHelperDescriptor>(); var context = TagHelperDescriptorProviderContext.Create(results); context.SetCompilation(compilation); for (var i = 0; i < providers.Length; i++) { var provider = providers[i]; provider.Execute(context); } var diagnostics = new List <RazorDiagnostic>(); var resolutionResult = new TagHelperResolutionResult(results, diagnostics); return(resolutionResult); }
public void TagHelperDescriptor_CanReadCamelCasedData() { // Arrange var descriptor = CreateTagHelperDescriptor( kind: TagHelperConventions.DefaultKind, tagName: "tag-name", typeName: "type name", assemblyName: "assembly name", attributes: new Action <BoundAttributeDescriptorBuilder>[] { builder => builder .Name("test-attribute") .PropertyName("TestAttribute") .TypeName("string"), }, ruleBuilders: new Action <TagMatchingRuleDescriptorBuilder>[] { builder => builder .RequireAttributeDescriptor(attribute => attribute .Name("required-attribute-one") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)) .RequireAttributeDescriptor(attribute => attribute .Name("required-attribute-two") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch) .Value("something") .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch)) .RequireParentTag("parent-name") .RequireTagStructure(TagStructure.WithoutEndTag), }, configureAction: builder => { builder.AllowChildTag("allowed-child-one"); builder.AddMetadata("foo", "bar"); builder.AddDiagnostic(RazorDiagnostic.Create( RazorDiagnosticFactory.TagHelper_InvalidTargetedTagNameNullOrWhitespace, new SourceSpan("Test.razor", 5, 17, 18, 22))); }); var serializerSettings = new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver(), Converters = Converters, }; var expectedResult = new TagHelperResolutionResult(new[] { descriptor }, Array.Empty <RazorDiagnostic>()); // Act var serialized = JsonConvert.SerializeObject(expectedResult, serializerSettings); var result = JsonConvert.DeserializeObject <TagHelperResolutionResult>(serialized, Converters); // Assert Assert.Equal(expectedResult, result, TagHelperResolutionResultComparer.Default); }
public override async Task <TagHelperResolutionResult> GetTagHelpersAsync(Project workspaceProject, ProjectSnapshot projectSnapshot, CancellationToken cancellationToken = default) { if (workspaceProject == null) { throw new ArgumentNullException(nameof(workspaceProject)); } if (projectSnapshot == null) { throw new ArgumentNullException(nameof(projectSnapshot)); } if (projectSnapshot.Configuration == null) { return(TagHelperResolutionResult.Empty); } // Not every custom factory supports the OOP host. Our priority system should work like this: // // 1. Use custom factory out of process // 2. Use custom factory in process // 3. Use fallback factory in process // // Calling into RazorTemplateEngineFactoryService.Create will accomplish #2 and #3 in one step. var factory = _factory.FindSerializableFactory(projectSnapshot); try { TagHelperResolutionResult result = null; if (factory != null) { result = await ResolveTagHelpersOutOfProcessAsync(factory, workspaceProject, projectSnapshot).ConfigureAwait(false); } if (result == null) { // Was unable to get tag helpers OOP, fallback to default behavior. result = await ResolveTagHelpersInProcessAsync(workspaceProject, projectSnapshot).ConfigureAwait(false); } return(result); } catch (Exception exception) { throw new InvalidOperationException( Resources.FormatUnexpectedException( typeof(DefaultTagHelperResolver).FullName, nameof(GetTagHelpersAsync)), exception); } }
public void TagHelperResolutionResult_DefaultBlazorServerProject_RoundTrips() { // Arrange var testFileName = "test.taghelpers.json"; var current = new DirectoryInfo(AppContext.BaseDirectory); while (current != null && !File.Exists(Path.Combine(current.FullName, testFileName))) { current = current.Parent; } var tagHelperFilePath = Path.Combine(current.FullName, testFileName); var buffer = File.ReadAllBytes(tagHelperFilePath); var serializer = new JsonSerializer(); serializer.Converters.Add(new TagHelperDescriptorJsonConverter()); serializer.Converters.Add(new TagHelperResolutionResultJsonConverter()); IReadOnlyList <TagHelperDescriptor> deserializedTagHelpers; using (var stream = new MemoryStream(buffer)) using (var reader = new JsonTextReader(new StreamReader(stream))) { deserializedTagHelpers = serializer.Deserialize <IReadOnlyList <TagHelperDescriptor> >(reader); } var expectedResult = new TagHelperResolutionResult(deserializedTagHelpers, Array.Empty <RazorDiagnostic>()); // Act MemoryStream serializedStream; using (serializedStream = new MemoryStream()) using (var writer = new StreamWriter(serializedStream, Encoding.UTF8, bufferSize: 4096)) { serializer.Serialize(writer, expectedResult); } TagHelperResolutionResult deserializedResult; var reserializedStream = new MemoryStream(serializedStream.GetBuffer()); using (reserializedStream) using (var reader = new JsonTextReader(new StreamReader(reserializedStream))) { deserializedResult = serializer.Deserialize <TagHelperResolutionResult>(reader); } // Assert Assert.Equal(expectedResult, deserializedResult, TagHelperResolutionResultComparer.Default); }
public override async Task <TagHelperResolutionResult> GetTagHelpersAsync(Project project, CancellationToken cancellationToken) { if (project == null) { throw new ArgumentNullException(nameof(project)); } try { TagHelperResolutionResult result = null; // We're being defensive here because the OOP host can return null for the client/session/operation // when it's disconnected (user stops the process). var client = await RazorLanguageServiceClientFactory.CreateAsync(_workspace, cancellationToken); if (client != null) { using (var session = await client.CreateSessionAsync(project.Solution)) { if (session != null) { var jsonObject = await session.InvokeAsync <JObject>( "GetTagHelpersAsync", new object[] { project.Id.Id, "Foo", }, cancellationToken).ConfigureAwait(false); result = GetTagHelperResolutionResult(jsonObject); } } } if (result == null) { // Was unable to get tag helpers OOP, fallback to default behavior. result = await _defaultResolver.GetTagHelpersAsync(project, cancellationToken); } return(result); } catch (Exception exception) { throw new InvalidOperationException( Resources.FormatUnexpectedException( typeof(DefaultTagHelperResolver).FullName, nameof(GetTagHelpersAsync)), exception); } }
public void TagHelperDescriptor_WithIndexerAttributes_RoundTripsProperly() { // Arrange var descriptor = CreateTagHelperDescriptor( kind: TagHelperConventions.DefaultKind, tagName: "tag-name", typeName: "type name", assemblyName: "assembly name", attributes: new Action <BoundAttributeDescriptorBuilder>[] { builder => builder .Name("test-attribute") .PropertyName("TestAttribute") .TypeName("SomeEnum") .AsEnum() .Documentation("Summary"), builder => builder .Name("test-attribute2") .PropertyName("TestAttribute2") .TypeName("SomeDictionary") .AsDictionaryAttribute("dict-prefix-", "string"), }, ruleBuilders: new Action <TagMatchingRuleDescriptorBuilder>[] { builder => builder .RequireAttributeDescriptor(attribute => attribute .Name("required-attribute-one") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)) }, configureAction: builder => { builder .AllowChildTag("allowed-child-one") .AddMetadata("foo", "bar") .TagOutputHint("Hint"); }); var expectedResult = new TagHelperResolutionResult(new[] { descriptor }, Array.Empty <RazorDiagnostic>()); // Act var serialized = JsonConvert.SerializeObject(expectedResult, Converters); var deserializedResult = JsonConvert.DeserializeObject <TagHelperResolutionResult>(serialized, Converters); // Assert Assert.Equal(expectedResult, deserializedResult, TagHelperResolutionResultComparer.Default); }
public void TagHelperDescriptor_WithDiagnostic_RoundTripsProperly() { // Arrange var descriptor = CreateTagHelperDescriptor( kind: TagHelperConventions.DefaultKind, tagName: "tag-name", typeName: "type name", assemblyName: "assembly name", attributes: new Action <BoundAttributeDescriptorBuilder>[] { builder => builder .Name("test-attribute") .PropertyName("TestAttribute") .TypeName("string"), }, ruleBuilders: new Action <TagMatchingRuleDescriptorBuilder>[] { builder => builder .RequireAttributeDescriptor(attribute => attribute .Name("required-attribute-one") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)) .RequireAttributeDescriptor(attribute => attribute .Name("required-attribute-two") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch) .Value("something") .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch)) .RequireParentTag("parent-name"), }, configureAction: builder => { builder.AllowChildTag("allowed-child-one") .AddMetadata("foo", "bar") .AddDiagnostic(RazorDiagnostic.Create( new RazorDiagnosticDescriptor("id", () => "Test Message", RazorDiagnosticSeverity.Error), new SourceSpan(null, 10, 20, 30, 40))); }); var expectedResult = new TagHelperResolutionResult(new[] { descriptor }, Array.Empty <RazorDiagnostic>()); // Act var serialized = JsonConvert.SerializeObject(expectedResult, Converters); var deserializedResult = JsonConvert.DeserializeObject <TagHelperResolutionResult>(serialized, Converters); // Assert Assert.Equal(expectedResult, deserializedResult, TagHelperResolutionResultComparer.Default); }
public void ViewComponentTagHelperDescriptor_RoundTripsProperly() { // Arrange var descriptor = CreateTagHelperDescriptor( kind: ViewComponentTagHelperConventions.Kind, tagName: "tag-name", typeName: "type name", assemblyName: "assembly name", attributes: new Action <BoundAttributeDescriptorBuilder>[] { builder => builder .Name("test-attribute") .PropertyName("TestAttribute") .TypeName("string"), }, ruleBuilders: new Action <TagMatchingRuleDescriptorBuilder>[] { builder => builder .RequireAttributeDescriptor(attribute => attribute .Name("required-attribute-one") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)) .RequireAttributeDescriptor(attribute => attribute .Name("required-attribute-two") .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch) .Value("something") .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch)) .RequireParentTag("parent-name") .RequireTagStructure(TagStructure.WithoutEndTag), }, configureAction: builder => { builder.AllowChildTag("allowed-child-one"); builder.AddMetadata("foo", "bar"); }); var expectedResult = new TagHelperResolutionResult(new[] { descriptor }, Array.Empty <RazorDiagnostic>()); // Act var serialized = JsonConvert.SerializeObject(expectedResult, s_converters); var deserializedResult = JsonConvert.DeserializeObject <TagHelperResolutionResult>(serialized, s_converters); // Assert Assert.Equal(expectedResult, deserializedResult, TagHelperResolutionResultComparer.Default); }
public TagHelperResolutionResultSerializationBenchmark() { var current = new DirectoryInfo(AppContext.BaseDirectory); while (current != null && !File.Exists(Path.Combine(current.FullName, "taghelpers.json"))) { current = current.Parent; } var tagHelperFilePath = Path.Combine(current.FullName, "taghelpers.json"); var tagHelperBuffer = File.ReadAllBytes(tagHelperFilePath); // Deserialize from json file. Serializer = new JsonSerializer(); Serializer.Converters.Add(new TagHelperDescriptorJsonConverter()); Serializer.Converters.Add(new TagHelperResolutionResultJsonConverter()); using (var stream = new MemoryStream(tagHelperBuffer)) using (var reader = new JsonTextReader(new StreamReader(stream))) { var tagHelpers = Serializer.Deserialize <IReadOnlyList <TagHelperDescriptor> >(reader); TagHelperResolutionResult = new TagHelperResolutionResult(tagHelpers, Array.Empty <RazorDiagnostic>()); } }
protected virtual async Task <TagHelperResolutionResult> ResolveTagHelpersOutOfProcessAsync(IProjectEngineFactory factory, Project workspaceProject, ProjectSnapshot projectSnapshot, CancellationToken cancellationToken) { // We're being overly defensive here because the OOP host can return null for the client/session/operation // when it's disconnected (user stops the process). // // This will change in the future to an easier to consume API but for VS RTM this is what we have. var remoteClient = await RazorRemoteHostClient.TryGetClientAsync(_workspace.Services, RazorServiceDescriptors.TagHelperProviderServiceDescriptors, RazorRemoteServiceCallbackDispatcherRegistry.Empty, cancellationToken); if (remoteClient is null) { // Could not resolve return(null); } if (!_resultCache.TryGetId(projectSnapshot.FilePath, out var lastResultId)) { lastResultId = -1; } var projectHandle = new ProjectSnapshotHandle(projectSnapshot.FilePath, projectSnapshot.Configuration, projectSnapshot.RootNamespace); var result = await remoteClient.TryInvokeAsync <IRemoteTagHelperProviderService, TagHelperDeltaResult>( workspaceProject.Solution, (service, solutionInfo, innerCancellationToken) => service.GetTagHelpersDeltaAsync(solutionInfo, projectHandle, factory?.GetType().AssemblyQualifiedName, lastResultId, innerCancellationToken), cancellationToken ); if (!result.HasValue) { return(null); } var tagHelpers = ProduceTagHelpersFromDelta(projectSnapshot.FilePath, lastResultId, result.Value); var resolutionResult = new TagHelperResolutionResult(tagHelpers, diagnostics: Array.Empty <RazorDiagnostic>()); return(resolutionResult); }