1- function Add-ContentFromItem {
1+ function Get-DependencyOrderedScriptFiles {
2+ [OutputType ([System.IO.FileInfo []])]
3+ [CmdletBinding ()]
4+ param (
5+ [Parameter (Mandatory )]
6+ [System.IO.FileInfo []] $Files
7+ )
8+
9+ $sortedFiles = $Files | Sort-Object - Property FullName
10+ if ($sortedFiles.Count -le 1 ) {
11+ return $sortedFiles
12+ }
13+
14+ $metadataByPath = @ {}
15+ $typeToPath = @ {}
16+ $duplicateTypes = [System.Collections.Generic.HashSet [string ]]::new([System.StringComparer ]::OrdinalIgnoreCase)
17+
18+ foreach ($file in $sortedFiles ) {
19+ $content = Get-Content - Path $file.FullName - Raw
20+ $declaredTypes = [System.Collections.Generic.HashSet [string ]]::new([System.StringComparer ]::OrdinalIgnoreCase)
21+ [Regex ]::Matches($content , ' (?im)^\s*(class|enum)\s+([^\s:{]+)' ) | ForEach-Object {
22+ [void ] $declaredTypes.Add ($_.Groups [2 ].Value)
23+ }
24+
25+ $metadataByPath [$file.FullName ] = [pscustomobject ]@ {
26+ File = $file
27+ Content = $content
28+ DeclaredTypes = $declaredTypes
29+ }
30+
31+ foreach ($typeName in $declaredTypes ) {
32+ if ($typeToPath.ContainsKey ($typeName )) {
33+ [void ]$duplicateTypes.Add ($typeName )
34+ continue
35+ }
36+
37+ $typeToPath [$typeName ] = $file.FullName
38+ }
39+ }
40+
41+ $dependenciesByPath = @ {}
42+ $dependentsByPath = @ {}
43+ foreach ($file in $sortedFiles ) {
44+ $dependenciesByPath [$file.FullName ] = [System.Collections.Generic.HashSet [string ]]::new([System.StringComparer ]::OrdinalIgnoreCase)
45+ $dependentsByPath [$file.FullName ] = [System.Collections.Generic.HashSet [string ]]::new([System.StringComparer ]::OrdinalIgnoreCase)
46+ }
47+
48+ foreach ($metadata in $metadataByPath.Values ) {
49+ foreach ($typeName in $typeToPath.Keys ) {
50+ if ($duplicateTypes.Contains ($typeName )) {
51+ continue
52+ }
53+
54+ if ($metadata.DeclaredTypes.Contains ($typeName )) {
55+ continue
56+ }
57+
58+ $typePattern = " (?<![\w\.])\[$ ( [Regex ]::Escape($typeName )) \](?![\w\.])"
59+ if ([Regex ]::IsMatch($metadata.Content , $typePattern , [System.Text.RegularExpressions.RegexOptions ]::IgnoreCase)) {
60+ $dependencyPath = $typeToPath [$typeName ]
61+ [void ]$dependenciesByPath [$metadata.File.FullName ].Add($dependencyPath )
62+ [void ]$dependentsByPath [$dependencyPath ].Add($metadata.File.FullName )
63+ }
64+ }
65+ }
66+
67+ $remainingPaths = @ ($sortedFiles.FullName )
68+ $ready = @ (
69+ $remainingPaths |
70+ Where-Object { $dependenciesByPath [$_ ].Count -eq 0 } |
71+ Sort-Object
72+ )
73+ $orderedPaths = [System.Collections.Generic.List [string ]]::new()
74+
75+ while ($ready.Count -gt 0 ) {
76+ $currentPath = $ready [0 ]
77+ $ready = @ ($ready | Select-Object - Skip 1 )
78+ $orderedPaths.Add ($currentPath )
79+ $remainingPaths = @ ($remainingPaths | Where-Object { $_ -ne $currentPath })
80+
81+ $dependents = @ ($dependentsByPath [$currentPath ] | Sort-Object )
82+ foreach ($dependentPath in $dependents ) {
83+ $null = $dependenciesByPath [$dependentPath ].Remove($currentPath )
84+ if ($dependenciesByPath [$dependentPath ].Count -eq 0 ) {
85+ $ready = @ ($ready + $dependentPath | Sort-Object - Unique)
86+ }
87+ }
88+ }
89+
90+ if ($orderedPaths.Count -lt $sortedFiles.Count ) {
91+ Write-Warning " Detected cyclical or unresolved class/enum dependencies. Falling back to lexical order for remaining files in [$ ( $sortedFiles [0 ].DirectoryName) ]."
92+ $remainingPaths | Sort-Object | ForEach-Object {
93+ $orderedPaths.Add ($_ )
94+ }
95+ }
96+
97+ return @ ($orderedPaths | ForEach-Object { $metadataByPath [$_ ].File })
98+ }
99+
100+ function Add-ContentFromItem {
2101 <#
3102 . SYNOPSIS
4103 Add the content of a folder or file to the root module file.
20119
21120 # The root path of the module.
22121 [Parameter (Mandatory )]
23- [string ] $RootPath
122+ [string ] $RootPath ,
123+
124+ # Whether the folder should be loaded using dependency ordering (class/enum aware).
125+ [Parameter ()]
126+ [switch ] $DependencyAware
24127 )
25- # Get the path separator for the current OS
26- $pathSeparator = [System.IO.Path ]::DirectorySeparatorChar
128+ $separators = [char []]@ ([System.IO.Path ]::DirectorySeparatorChar, [System.IO.Path ]::AltDirectorySeparatorChar)
27129
28- $relativeFolderPath = $Path -replace $RootPath , ' '
29- $relativeFolderPath = $relativeFolderPath -replace $file.Extension , ' '
30- $relativeFolderPath = $relativeFolderPath.TrimStart ($pathSeparator )
31- $relativeFolderPath = $relativeFolderPath -split $pathSeparator | ForEach-Object { " [$_ ]" }
130+ $relativeFolderPath = [System.IO.Path ]::GetRelativePath($RootPath , $Path )
131+ $relativeFolderPath = $relativeFolderPath.Split ($separators , [System.StringSplitOptions ]::RemoveEmptyEntries) | ForEach-Object { " [$_ ]" }
32132 $relativeFolderPath = $relativeFolderPath -join ' - '
33133
34134 Add-Content - Path $RootModuleFilePath - Force - Value @"
35135#region $relativeFolderPath
36136Write-Debug "[`$ scriptName] - $relativeFolderPath - Processing folder"
37137"@
38138
39- $files = $Path | Get-ChildItem - File - Force - Filter ' *.ps1' | Sort-Object - Property FullName
139+ if ($DependencyAware ) {
140+ $files = $Path | Get-ChildItem - Recurse - File - Force - Filter ' *.ps1' | Sort-Object - Property FullName
141+ $files = Get-DependencyOrderedScriptFiles - Files $files
142+ } else {
143+ $files = $Path | Get-ChildItem - File - Force - Filter ' *.ps1' | Sort-Object - Property FullName
144+ }
145+
40146 foreach ($file in $files ) {
41- $relativeFilePath = $file.FullName -replace $RootPath , ' '
42- $relativeFilePath = $relativeFilePath -replace $file.Extension , ' '
43- $relativeFilePath = $relativeFilePath.TrimStart ($pathSeparator )
44- $relativeFilePath = $relativeFilePath -split $pathSeparator | ForEach-Object { " [$_ ]" }
147+ $relativeFilePath = [System.IO.Path ]::GetRelativePath($RootPath , $file.FullName )
148+ $relativeFilePath = [System.IO.Path ]::ChangeExtension($relativeFilePath , $null ).TrimEnd(' .' )
149+ $relativeFilePath = $relativeFilePath.Split ($separators , [System.StringSplitOptions ]::RemoveEmptyEntries) | ForEach-Object { " [$_ ]" }
45150 $relativeFilePath = $relativeFilePath -join ' - '
46151
47152 Add-Content - Path $RootModuleFilePath - Force - Value @"
@@ -55,9 +160,11 @@ Write-Debug "[`$scriptName] - $relativeFilePath - Done"
55160"@
56161 }
57162
58- $subFolders = $Path | Get-ChildItem - Directory - Force | Sort-Object - Property Name
59- foreach ($subFolder in $subFolders ) {
60- Add-ContentFromItem - Path $subFolder.FullName - RootModuleFilePath $RootModuleFilePath - RootPath $RootPath
163+ if (-not $DependencyAware ) {
164+ $subFolders = $Path | Get-ChildItem - Directory - Force | Sort-Object - Property Name
165+ foreach ($subFolder in $subFolders ) {
166+ Add-ContentFromItem - Path $subFolder.FullName - RootModuleFilePath $RootModuleFilePath - RootPath $RootPath
167+ }
61168 }
62169 Add-Content - Path $RootModuleFilePath - Force - Value @"
63170Write-Debug "[`$ scriptName] - $relativeFolderPath - Done"
0 commit comments