44using System . IO ;
55using System . Linq ;
66using System . Security . Cryptography ;
7+ using System . Text . RegularExpressions ;
78using Microsoft . Build . Framework ;
89using Microsoft . Build . Utilities ;
910using NuGet . Frameworks ;
@@ -21,7 +22,9 @@ public class CreatePackage : Task
2122 public ITaskItem Manifest { get ; set ; }
2223
2324 [ Required ]
24- public ITaskItem [ ] Contents { get ; set ; } = Array . Empty < ITaskItem > ( ) ;
25+ public ITaskItem [ ] Contents { get ; set ; } = [ ] ;
26+
27+ public ITaskItem [ ] ReplacementTokens { get ; set ; } = [ ] ;
2528
2629 [ Required ]
2730 public string TargetPath { get ; set ; }
@@ -39,6 +42,8 @@ public class CreatePackage : Task
3942 public ITaskItem OutputPackage { get ; set ; }
4043
4144 Manifest manifest ;
45+ Dictionary < string , string > tokens ;
46+ Regex tokensExpr ;
4247
4348 public override bool Execute ( )
4449 {
@@ -68,13 +73,17 @@ public override bool Execute()
6873 }
6974 }
7075
76+ public Manifest Execute ( Stream output ) => Execute ( output , out _ ) ;
77+
7178 // Implementation for testing to avoid I/O
72- public Manifest Execute ( Stream output )
79+ public Manifest Execute ( Stream output , out Manifest manifest )
7380 {
7481 GeneratePackage ( output ) ;
82+ manifest = this . manifest ;
7583
7684 output . Seek ( 0 , SeekOrigin . Begin ) ;
7785 using var reader = new PackageArchiveReader ( output ) ;
86+
7887 return reader . GetManifest ( ) ;
7988 }
8089
@@ -92,13 +101,13 @@ public Manifest CreateManifest()
92101 metadata . DevelopmentDependency = true ;
93102
94103 if ( Manifest . TryGetMetadata ( "Title" , out var title ) )
95- metadata . Title = title . TrimIndent ( ) ;
104+ metadata . Title = ReplaceTokens ( title . TrimIndent ( ) ) ;
96105
97106 if ( Manifest . TryGetMetadata ( "Description" , out var description ) )
98- metadata . Description = description . TrimIndent ( ) ;
107+ metadata . Description = ReplaceTokens ( description . TrimIndent ( ) ) ;
99108
100109 if ( Manifest . TryGetMetadata ( "Summary" , out var summary ) )
101- metadata . Summary = summary . TrimIndent ( ) ;
110+ metadata . Summary = ReplaceTokens ( summary . TrimIndent ( ) ) ;
102111
103112 if ( Manifest . TryGetMetadata ( "Readme" , out var readme ) )
104113 metadata . Readme = readme ;
@@ -107,17 +116,17 @@ public Manifest CreateManifest()
107116 metadata . Language = language ;
108117
109118 if ( Manifest . TryGetMetadata ( "Copyright" , out var copyright ) )
110- metadata . Copyright = copyright ;
119+ metadata . Copyright = ReplaceTokens ( copyright ) ;
111120
112121 if ( Manifest . TryGetBoolMetadata ( "RequireLicenseAcceptance" , out var requireLicenseAcceptance ) &&
113122 requireLicenseAcceptance )
114123 metadata . RequireLicenseAcceptance = requireLicenseAcceptance ;
115124
116125 if ( ! string . IsNullOrEmpty ( Manifest . GetMetadata ( "Authors" ) ) )
117- metadata . Authors = Manifest . GetMetadata ( "Authors" ) . Split ( new [ ] { ',' } , StringSplitOptions . RemoveEmptyEntries ) ;
126+ metadata . Authors = Manifest . GetMetadata ( "Authors" ) . Split ( [ ',' ] , StringSplitOptions . RemoveEmptyEntries ) ;
118127
119128 if ( ! string . IsNullOrEmpty ( Manifest . GetMetadata ( "Owners" ) ) )
120- metadata . Owners = Manifest . GetMetadata ( "Owners" ) . Split ( new [ ] { ',' } , StringSplitOptions . RemoveEmptyEntries ) ;
129+ metadata . Owners = Manifest . GetMetadata ( "Owners" ) . Split ( [ ',' ] , StringSplitOptions . RemoveEmptyEntries ) ;
121130
122131 if ( ! string . IsNullOrEmpty ( Manifest . GetMetadata ( "LicenseUrl" ) ) )
123132 metadata . SetLicenseUrl ( Manifest . GetMetadata ( "LicenseUrl" ) ) ;
@@ -171,7 +180,7 @@ public Manifest CreateManifest()
171180 metadata . Icon = icon ;
172181
173182 if ( Manifest . TryGetMetadata ( "ReleaseNotes" , out var releaseNotes ) )
174- metadata . ReleaseNotes = releaseNotes . TrimIndent ( ) ;
183+ metadata . ReleaseNotes = ReplaceTokens ( releaseNotes . TrimIndent ( ) ) ;
175184
176185 if ( Manifest . TryGetMetadata ( "Tags" , out var tags ) )
177186 metadata . Tags = tags ;
@@ -190,6 +199,21 @@ public Manifest CreateManifest()
190199 return manifest ;
191200 }
192201
202+ string ? ReplaceTokens ( string ? text )
203+ {
204+ tokens ??= ReplacementTokens
205+ . Select ( x => ( x . ItemSpec . ToLowerInvariant ( ) , x . GetMetadata ( "Value" ) ) )
206+ . GroupBy ( t => t . Item1 )
207+ . ToDictionary ( g => g . Key , g => g . Last ( ) . Item2 ) ;
208+
209+ tokensExpr ??= new Regex ( @"\$(" + string . Join ( "|" , tokens . Keys . Select ( Regex . Escape ) ) + @")\$" , RegexOptions . IgnoreCase ) ;
210+
211+ if ( string . IsNullOrEmpty ( text ) || tokens . Count == 0 )
212+ return text ;
213+
214+ return tokensExpr . Replace ( text , match => tokens [ match . Groups [ 1 ] . Value . ToLower ( ) ] ) ;
215+ }
216+
193217 void GeneratePackage ( Stream output = null )
194218 {
195219 manifest ??= CreateManifest ( ) ;
@@ -205,10 +229,27 @@ void GeneratePackage(Stream output = null)
205229 File . Exists ( readmeFile . Source ) )
206230 {
207231 // replace readme with includes replaced.
208- var replaced = IncludesResolver . Process ( readmeFile . Source , message => Log . LogWarningCode ( "NG001" , message ) ) ;
209- var temp = Path . GetTempFileName ( ) ;
210- File . WriteAllText ( temp , replaced ) ;
211- readmeFile . Source = temp ;
232+ var replaced = ReplaceTokens ( IncludesResolver . Process ( readmeFile . Source , message => Log . LogWarningCode ( "NG001" , message ) ) ) ;
233+ if ( ! replaced . Equals ( File . ReadAllText ( readmeFile . Source ) , StringComparison . Ordinal ) )
234+ {
235+ var temp = Path . GetTempFileName ( ) ;
236+ File . WriteAllText ( temp , replaced ) ;
237+ readmeFile . Source = temp ;
238+ }
239+ }
240+
241+ if ( manifest . Metadata . LicenseMetadata ? . Type == LicenseType . File &&
242+ manifest . Files . FirstOrDefault ( f => Path . GetFileName ( f . Target ) == manifest . Metadata . LicenseMetadata . License ) is ManifestFile licenseFile &&
243+ File . Exists ( licenseFile . Source ) )
244+ {
245+ // replace readme with includes replaced.
246+ var replaced = ReplaceTokens ( IncludesResolver . Process ( licenseFile . Source , message => Log . LogWarningCode ( "NG001" , message ) ) ) ;
247+ if ( ! replaced . Equals ( File . ReadAllText ( licenseFile . Source ) , StringComparison . Ordinal ) )
248+ {
249+ var temp = Path . GetTempFileName ( ) ;
250+ File . WriteAllText ( temp , replaced ) ;
251+ licenseFile . Source = temp ;
252+ }
212253 }
213254
214255 builder . Files . AddRange ( manifest . Files . Select ( file =>
0 commit comments