66using System . Globalization ;
77using System . Linq ;
88using System . Reflection ;
9+ using System . Threading . Tasks ;
910using Microsoft . AspNetCore . Razor . Compilation . TagHelpers ;
1011using Microsoft . AspNetCore . Razor . Runtime . TagHelpers ;
12+ using Microsoft . AspNetCore . Razor . TagHelpers ;
1113using Microsoft . Extensions . DependencyInjection ;
1214
1315namespace Microsoft . AspNetCore . Razor . Design . Internal
@@ -17,6 +19,7 @@ public class AssemblyTagHelperDescriptorResolver
1719 private const string TypeFullName = "Microsoft.AspNetCore.Mvc.DesignTimeMvcServiceCollectionProvider" ;
1820 private const string MvcAssemblyName = "Microsoft.AspNetCore.Mvc" ;
1921 private const string MethodName = "PopulateServiceCollection" ;
22+ private const string ViewComponentNameKey = "ViewComponentName" ;
2023
2124 private readonly TagHelperDescriptorFactory _tagHelperDescriptorFactory ;
2225 private readonly TagHelperTypeResolver _tagHelperTypeResolver ;
@@ -87,7 +90,14 @@ public IEnumerable<TagHelperDescriptor> Resolve(string assemblyName, ErrorSink e
8790 var context = new TagHelperDescriptorResolutionContext ( directiveDescriptors , errorSink ) ;
8891 var descriptors = resolver . Resolve ( context ) ;
8992
90- return descriptors ;
93+ // Temporary workaround to make design time for ViewComponent tag helpers work without needing a VS update.
94+ // This will be removed in a future version.
95+ var vcthDescriptors = descriptors . Where ( d => d . PropertyBag . ContainsKey ( ViewComponentNameKey ) ) ;
96+ var fakeVcthDescriptors = GetFakeDescriptors ( vcthDescriptors ) ;
97+
98+ var finalDescriptors = descriptors . Except ( vcthDescriptors ) . Union ( fakeVcthDescriptors ) ;
99+
100+ return finalDescriptors ;
91101 }
92102
93103 private void EnsureMvcInitialized ( )
@@ -118,5 +128,108 @@ private void EnsureMvcInitialized()
118128 _populateMethodDelegate = ( Action < IServiceCollection , string > ) populateMethod
119129 ? . CreateDelegate ( typeof ( Action < IServiceCollection , string > ) ) ;
120130 }
131+
132+ private IEnumerable < TagHelperDescriptor > GetFakeDescriptors ( IEnumerable < TagHelperDescriptor > vcthDescriptors )
133+ {
134+ /*
135+ * Since there are no corresponding tag helper types represented by vcthDescriptors,
136+ * We create fake descriptors with the same attributes and their types using the types
137+ * ViewComponentTagHelperDesignTimeType and ViewComponentTagHelperDesignTimeHelperType which mimic the
138+ * same design time experience as the original tag helper descriptors.
139+ */
140+ var fakeDescriptors = new List < TagHelperDescriptor > ( ) ;
141+ foreach ( var descriptor in vcthDescriptors )
142+ {
143+ var fakeType = typeof ( ViewComponentTagHelperDesignTimeType ) . FullName ;
144+ var fakeDescriptor = new TagHelperDescriptor ( descriptor ) ;
145+ fakeDescriptor . TypeName = fakeType ;
146+
147+ var fakeAttributes = new List < TagHelperAttributeDescriptor > ( ) ;
148+ fakeDescriptor . Attributes = fakeAttributes ;
149+
150+ var fakeHelperType = typeof ( ViewComponentTagHelperDesignTimeHelperType ) . FullName ;
151+ var fakeHelperDescriptor = new TagHelperDescriptor ( descriptor ) ;
152+ fakeHelperDescriptor . TypeName = fakeHelperType ;
153+
154+ var fakeHelperAttributes = new List < TagHelperAttributeDescriptor > ( ) ;
155+ fakeHelperDescriptor . Attributes = fakeHelperAttributes ;
156+
157+ foreach ( var attribute in descriptor . Attributes )
158+ {
159+ /*
160+ * We use TagHelperAttributeDescriptor.PropertyName to inject code into the generated code for the cshtml.
161+ * For example consider FooProperty of type string in ExampleTagHelper and ExampleTagHelper2,
162+ *
163+ * __SomeNamespace_ExampleTagHelper.FooProperty = "bar";
164+ * __SomeNamespace_ExampleTagHelper2.FooProperty = __SomeNamespace_ExampleTagHelper.FooProperty;
165+ *
166+ * "FooProperty" in the above code will be replaced with the specified value as below,
167+ *
168+ * __Microsoft_AspNetCore_Razor_Design_Internal_ViewComponentTagHelperDesignTimeType.PlaceholderProperty = null;
169+ * Microsoft.AspNetCore.Razor.Design.Internal.ViewComponentTagHelperDesignTimeType.ActionProperty = () => {
170+ * System.String __obj = default(System.String);
171+ * Microsoft.AspNetCore.Razor.Design.Internal.ViewComponentTagHelperDesignTimeType.PlaceholderMethod(__obj); ## This handles "__obj is assigned but never used" warning.
172+ * __obj = "bar";
173+ * __Microsoft_AspNetCore_Razor_Design_Internal_ViewComponentTagHelperDesignTimeHelperType.PlaceholderProperty = null;
174+ * };// ## The comment(//) here is needed to ignore the code that replaces "ExampleTagHelper.FooProperty".
175+ */
176+ const string fakeVariableName = "__obj" ;
177+ var summary = $ "{ attribute . TypeName } : { descriptor . TypeName } .{ attribute . PropertyName } ";
178+ var fakeAttributePropertyName = $ "{ nameof ( ViewComponentTagHelperDesignTimeType . PlaceholderProperty ) } = null; " +
179+ $ "{ fakeType } .{ nameof ( ViewComponentTagHelperDesignTimeType . ActionProperty ) } = () => {{ " +
180+ $ "{ attribute . TypeName } { fakeVariableName } = default({ attribute . TypeName } ); " +
181+ $ "{ fakeType } .{ nameof ( ViewComponentTagHelperDesignTimeType . PlaceholderMethod ) } ({ fakeVariableName } ); { fakeVariableName } ";
182+
183+ var fakeAttribute = new TagHelperAttributeDescriptor ( )
184+ {
185+ IsIndexer = attribute . IsIndexer ,
186+ IsEnum = attribute . IsEnum ,
187+ IsStringProperty = attribute . IsStringProperty ,
188+ Name = attribute . Name ,
189+ PropertyName = fakeAttributePropertyName ,
190+ TypeName = attribute . TypeName ,
191+ DesignTimeDescriptor = new TagHelperAttributeDesignTimeDescriptor
192+ {
193+ Summary = attribute . DesignTimeDescriptor ? . Summary ?? summary
194+ } ,
195+ } ;
196+ fakeAttributes . Add ( fakeAttribute ) ;
197+
198+ var fakeHelperAttributePropertyName =
199+ $ "{ nameof ( ViewComponentTagHelperDesignTimeType . PlaceholderProperty ) } = null; }};//";
200+
201+ var fakeHelperAttribute = new TagHelperAttributeDescriptor ( )
202+ {
203+ IsIndexer = attribute . IsIndexer ,
204+ IsEnum = attribute . IsEnum ,
205+ IsStringProperty = attribute . IsStringProperty ,
206+ Name = attribute . Name ,
207+ PropertyName = fakeHelperAttributePropertyName ,
208+ TypeName = attribute . TypeName ,
209+ } ;
210+ fakeHelperAttributes . Add ( fakeHelperAttribute ) ;
211+ }
212+
213+ fakeDescriptors . Add ( fakeDescriptor ) ;
214+ fakeDescriptors . Add ( fakeHelperDescriptor ) ;
215+ }
216+
217+ return fakeDescriptors ;
218+ }
219+ }
220+
221+ public abstract class ViewComponentTagHelperDesignTimeType : TagHelper
222+ {
223+ public object PlaceholderProperty { get ; set ; }
224+
225+ public static Action ActionProperty { get ; set ; }
226+
227+ public static void PlaceholderMethod ( object obj )
228+ {
229+ }
230+ }
231+
232+ public abstract class ViewComponentTagHelperDesignTimeHelperType : ViewComponentTagHelperDesignTimeType
233+ {
121234 }
122235}
0 commit comments