DEV Community

Cover image for Stop Manually Sorting PowerShell Class Files — PSScriptBuilder Does It For You
Tim Hartling
Tim Hartling

Posted on

Stop Manually Sorting PowerShell Class Files — PSScriptBuilder Does It For You

If you've ever written a PowerShell module with classes, you know the pain:

Unable to find type [Employee].
Enter fullscreen mode Exit fullscreen mode

PowerShell requires class definitions to appear in the correct order — base classes before derived classes, dependencies before dependents. In a single file with only a few classes, that's manageable. In a multi-file project with dozens of classes? It becomes a maintenance nightmare that only grows as the project does.

PSScriptBuilder solves this. It's a dependency-aware build tool that combines your multi-file PowerShell project into a single, deployable script — with class definitions automatically sorted into the correct load order using AST analysis.

Installation

Install-Module -Name PSScriptBuilder -Scope CurrentUser
Enter fullscreen mode Exit fullscreen mode

PSScriptBuilder requires using module instead of Import-Module to make classes and enums available. The using statement must appear at the very top of your script, before any other code.

PSScriptBuilder runs on Windows PowerShell 5.1 and PowerShell 7+ (Windows, Linux, macOS). Generated output is fully PS 5.1 compatible — ideal for enterprise environments.

Core Concepts

PSScriptBuilder has two building blocks:

Collectors define where your source files live. You configure one collector per source location and point it at a directory. Multiple collectors of the same type are fully supported — useful when your classes are spread across several directories that each need different filter rules.

Templates define the output structure. You create a template file with {{Token}} placeholders, and each placeholder is replaced by the collected, dependency-ordered source code of the corresponding collector.

A Complete Example

Given this project structure:

MyModule/
├── src/
│   ├── Enums/
│   ├── Classes/
│   └── Public/
└── build/
    ├── Templates/
    │   └── MyModule.psm1.template
    └── Output/
Enter fullscreen mode Exit fullscreen mode

Step 1 — Generate the configuration file:

New-PSScriptBuilderConfiguration
Enter fullscreen mode Exit fullscreen mode

This creates psscriptbuilder.config.json in your project root. PSScriptBuilder searches for it automatically starting from the current directory upward.

Step 2 — Create a template:

{{ENUM_DEFINITIONS}}

{{CLASS_DEFINITIONS}}

{{FUNCTION_DEFINITIONS}}
Enter fullscreen mode Exit fullscreen mode

Step 3 — Configure collectors and build:

using module PSScriptBuilder

$contentCollector = New-PSScriptBuilderContentCollector |
    Add-PSScriptBuilderCollector -Type Enum     -IncludePath "src/Enums"   |
    Add-PSScriptBuilderCollector -Type Class    -IncludePath "src/Classes" |
    Add-PSScriptBuilderCollector -Type Function -IncludePath "src/Public"

Invoke-PSScriptBuilderBuild `
    -ContentCollector $contentCollector `
    -TemplatePath     "build/Templates/MyModule.psm1.template" `
    -OutputPath       "build/Output/MyModule.psm1"
Enter fullscreen mode Exit fullscreen mode

PSScriptBuilder reads every source file, resolves all type dependencies via AST analysis, and writes a correctly ordered output file. If Employee inherits from Person, Person will always appear first — regardless of file or directory order.

More Features

Cycle detection — Circular inheritance dependencies are caught before any output is written, with a clear error message showing the full cycle path.

Cross-dependency handling — When classes and functions have mutual dependencies, PSScriptBuilder detects this situation and reports it. You resolve it by replacing the separate {{CLASS_DEFINITIONS}} and {{FUNCTION_DEFINITIONS}} placeholders in your template with a single {{ORDERED_COMPONENTS}} token — PSScriptBuilder then outputs all components interleaved in the correct dependency order.

Using statement deduplication — If multiple source files contain the same using namespace or using assembly statements, PSScriptBuilder collects them via a dedicated Using collector and outputs each statement exactly once at the top of the script.

Flexible filtering — Collectors support include/exclude patterns, recursive or flat scanning, and custom file extensions:

Add-PSScriptBuilderCollector -Type Class `
    -IncludePath   "src/Classes" `
    -ExcludeFile   "*.Tests.ps1" `
    -FileExtension ".ps1", ".psm1"
Enter fullscreen mode Exit fullscreen mode

Release management — PSScriptBuilder includes a full release pipeline beyond simple version bumping. A release data file tracks your current SemVer version, build details, and Git metadata. You update it with Update-PSScriptBuilderReleaseData, which offers granular control over what gets refreshed — version bumping (-Major, -Minor, -Patch), build details (-UpdateBuildDetails), Git metadata (-UpdateGitDetails), or any combination in a single call.

A separate bump configuration file defines which files get updated and how. Each file can have multiple replacement rules — each rule uses a regex pattern to locate the value and a release data token (e.g. {{VERSION_FULL}}) to inject the new value. This means a single Update-PSScriptBuilderReleaseData call can update your .psd1, README, and version header simultaneously, each with its own pattern.

Consider this pattern: on the first bump, a static placeholder like {{BUILD_TIMESTAMP}} is replaced with the actual timestamp. On every subsequent bump, the regex matches the existing timestamp value and replaces it with the new one — no manual intervention, no config change between runs.

Get Started

Have you run into load-order issues in your PowerShell projects? I'd love to hear how you've been handling it — or if PSScriptBuilder covers your use case.

Top comments (0)