process/importsMapper.psm1

using module ..\helpers\cyclesDetector.psm1
using module ..\parsers\importParser.psm1
using module ..\models\bundlerConfig.psm1
using module ..\models\fileInfo.psm1
using namespace System.Management.Automation.Language

Class ImportsMapper {
    [BundlerConfig]$_config
    [ImportParser]$_importParser

    ImportsMapper ([BundlerConfig]$config) {
        $this._config = $config
        $this._importParser = [ImportParser]::new($config)
    }

    [System.Collections.Specialized.OrderedDictionary]GetImportsMap ([string]$entryPath) {
        $importMap = $this.GenerateMap($entryPath, $true, $null, [ordered]@{})
        $cyclesDetector = [CyclesDetector]::new()
        $hasCycles = $cyclesDetector.Check($importMap)
        if ($hasCycles) { return  $null }

        # A file containing only classes can normally disappear after its classes
        # are extracted. It must remain executable, however, when it imports a
        # dependency with runtime code (for example, a guarded Add-Type). Propagate
        # that fact through the acyclic import graph before replacements are built.
        $entryFile = $this.GetEntryFile($importMap)
        $this.UpdateTypesOnly($entryFile, @{})

        return $importMap
    }

    [void]UpdateTypesOnly([FileInfo]$file, [hashtable]$processed) {
        if (-not $file -or $processed.ContainsKey($file.path)) { return }
        $processed[$file.path] = $true

        foreach ($importInfo in $file.imports.Values) {
            $importFile = $importInfo.file
            $this.UpdateTypesOnly($importFile, $processed)

            # "typesOnly" is used to remove both a file body and imports pointing
            # to it. A runtime dependency makes the importing file runtime-relevant
            # as well, even if its own body contains only PowerShell classes.
            if (-not $importFile.typesOnly) { $file.typesOnly = $false }
        }
    }

    [System.Collections.Specialized.OrderedDictionary]GenerateMap (       
        [string]$filePath,
        [bool]$isEntry,
        [hashtable]$consumerInfo,
        [System.Collections.Specialized.OrderedDictionary]$importMap) {

        if ($importMap.Contains($filePath)) {
            $file = $importMap[$filePath]
            $file.LinkToConsumer($consumerInfo)
            return $importMap
        }

        $file = [FileInfo]::new($filePath, $this._config, $isEntry, $consumerInfo)
        $importMap[$file.path] = $file

        $importsInfo = $this._importParser.ParseFile($file)

        foreach ($importInfo in $importsInfo) {
            $consumerInfo = @{
                File      = $file
                PathAst   = $importInfo.PathAst
                ImportAst = $importInfo.ImportAst
                Type      = $importInfo.Type
            }
            $this.GenerateMap( $importInfo.Path, $false, $consumerInfo, $importMap)
        }
       
        return $importMap
    }

    # Get entry file
    [FileInfo]getEntryFile([System.Collections.Specialized.OrderedDictionary]$importsMap) {
        foreach ($file in $importsMap.Values) {
            if ($file.isEntry) { return $file }
        }
        return $null
    }
}