<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tim Hartling</title>
    <description>The latest articles on DEV Community by Tim Hartling (@tim_hartling).</description>
    <link>https://dev.to/tim_hartling</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3884544%2Ff528affe-f023-46ac-93c6-a44c6e241df1.png</url>
      <title>DEV Community: Tim Hartling</title>
      <link>https://dev.to/tim_hartling</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tim_hartling"/>
    <language>en</language>
    <item>
      <title>Stop Manually Sorting PowerShell Class Files — PSScriptBuilder Does It For You</title>
      <dc:creator>Tim Hartling</dc:creator>
      <pubDate>Fri, 17 Apr 2026 15:36:44 +0000</pubDate>
      <link>https://dev.to/tim_hartling/stop-manually-sorting-powershell-class-files-psscriptbuilder-does-it-for-you-2dok</link>
      <guid>https://dev.to/tim_hartling/stop-manually-sorting-powershell-class-files-psscriptbuilder-does-it-for-you-2dok</guid>
      <description>&lt;p&gt;If you've ever written a PowerShell module with classes, you know the pain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unable to find type [Employee].
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PSScriptBuilder&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Install-Module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PSScriptBuilder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;CurrentUser&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concepts
&lt;/h2&gt;

&lt;p&gt;PSScriptBuilder has two building blocks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collectors&lt;/strong&gt; 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.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  A Complete Example
&lt;/h2&gt;

&lt;p&gt;Given this project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MyModule/
├── src/
│   ├── Enums/
│   ├── Classes/
│   └── Public/
└── build/
    ├── Templates/
    │   └── MyModule.psm1.template
    └── Output/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 1 — Generate the configuration file:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;New-PSScriptBuilderConfiguration&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates &lt;code&gt;psscriptbuilder.config.json&lt;/code&gt; in your project root. PSScriptBuilder searches for it automatically starting from the current directory upward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Create a template:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;ENUM_DEFINITIONS&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;CLASS_DEFINITIONS&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;FUNCTION_DEFINITIONS&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3 — Configure collectors and build:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="kr"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PSScriptBuilder&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$contentCollector&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;New-PSScriptBuilderContentCollector&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Add-PSScriptBuilderCollector&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Enum&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nt"&gt;-IncludePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/Enums"&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Add-PSScriptBuilderCollector&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Class&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-IncludePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/Classes"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Add-PSScriptBuilderCollector&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-IncludePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src/Public"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Invoke-PSScriptBuilderBuild&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-ContentCollector&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$contentCollector&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-TemplatePath&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="s2"&gt;"build/Templates/MyModule.psm1.template"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-OutputPath&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="s2"&gt;"build/Output/MyModule.psm1"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  More Features
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cycle detection&lt;/strong&gt; — Circular inheritance dependencies are caught before any output is written, with a clear error message showing the full cycle path.&lt;/p&gt;

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

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

&lt;p&gt;&lt;strong&gt;Flexible filtering&lt;/strong&gt; — Collectors support include/exclude patterns, recursive or flat scanning, and custom file extensions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Add-PSScriptBuilderCollector&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-IncludePath&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="s2"&gt;"src/Classes"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-ExcludeFile&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="s2"&gt;"*.Tests.ps1"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;-FileExtension&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".ps1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".psm1"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Release management&lt;/strong&gt; — 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 &lt;code&gt;Update-PSScriptBuilderReleaseData&lt;/code&gt;, which offers granular control over what gets refreshed — version bumping (&lt;code&gt;-Major&lt;/code&gt;, &lt;code&gt;-Minor&lt;/code&gt;, &lt;code&gt;-Patch&lt;/code&gt;), build details (&lt;code&gt;-UpdateBuildDetails&lt;/code&gt;), Git metadata (&lt;code&gt;-UpdateGitDetails&lt;/code&gt;), or any combination in a single call.&lt;/p&gt;

&lt;p&gt;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. &lt;code&gt;{{VERSION_FULL}}&lt;/code&gt;) to inject the new value. This means a single &lt;code&gt;Update-PSScriptBuilderReleaseData&lt;/code&gt; call can update your &lt;code&gt;.psd1&lt;/code&gt;, &lt;code&gt;README&lt;/code&gt;, and version header simultaneously, each with its own pattern.&lt;/p&gt;

&lt;p&gt;Consider this pattern: on the first bump, a static placeholder like &lt;code&gt;{{BUILD_TIMESTAMP}}&lt;/code&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PSGallery:&lt;/strong&gt; &lt;code&gt;Install-Module -Name PSScriptBuilder&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://docs.psscriptbuilder.com" rel="noopener noreferrer"&gt;docs.psscriptbuilder.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/PSScriptBuilder/PSScriptBuilder" rel="noopener noreferrer"&gt;github.com/PSScriptBuilder/PSScriptBuilder&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

</description>
      <category>powershell</category>
      <category>devtools</category>
      <category>automation</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
