A PowerShell module that helps developers build, package, and manage other PowerShell modules. PSModuleFactory covers the full module lifecycle: scaffold a new module from scratch, develop with individual source files, build a distributable single-file package, and automate semantic versioning from Git commit history.
Install-Module -Name YFridelance.PS.ModuleFactory -Scope CurrentUser
Import-Module YFridelance.PS.ModuleFactoryInitialize-PSModule -ModuleName 'Acme.PS.Tools' -Path 'C:\Projects' -Author 'Jane Doe' `
-Description 'Acme internal toolset' -Version '0.1.0'# Build from the module root directory
Build-PSModule -Path 'C:\Projects\Acme.PS.Tools' -Clean -Verbose
# Output lands in C:\Projects\dist\Acme.PS.Tools\Update-PSModuleVersion -Path 'C:\Projects\Acme.PS.Tools' -Tag -VerboseDuring development, each function lives in its own .ps1 file. Build-PSModule merges all source
files into a single .psm1, updates FunctionsToExport and AliasesToExport in the copied
manifest, and writes everything to the output directory.
# Build to the default dist/ folder
Build-PSModule -Path 'C:\Projects\MyModule'
# Build to a custom path with a clean run
Build-PSModule -Path 'C:\Projects\MyModule' -OutputPath 'C:\Artifacts\MyModule' -CleanCreates the module directory, subdirectories (Public, Private, Classes, Enums), a
development .psm1 that dot-sources all source files, and a pre-configured .psd1 manifest.
Initialize-PSModule -ModuleName 'Company.PS.Utilities' -Path 'C:\Projects' `
-Description 'Utility functions' -License 'MIT'If you have an existing module with all code in a single .psm1, Split-PSModule extracts each
function, class, and enum into its own file under the appropriate subdirectory and replaces the
original .psm1 with a development dot-source loader.
# Preview what would happen without writing any files
Split-PSModule -Path 'C:\Projects\LegacyModule' -WhatIf
# Execute the split; overwrite existing files if present
Split-PSModule -Path 'C:\Projects\LegacyModule' -ForceReads the current ModuleVersion from the manifest, analyzes Git commits since the last version
tag using the Conventional Commits specification, and bumps the version accordingly. Optionally
creates a Git tag.
# Automatic analysis from commit history
Update-PSModuleVersion -Path 'C:\Projects\MyModule'
# Force a specific bump and tag
Update-PSModuleVersion -Path 'C:\Projects\MyModule' -BumpType Minor -Tag
# Dry run — see what would change without modifying anything
Update-PSModuleVersion -Path 'C:\Projects\MyModule' -WhatIfA module managed by PSModuleFactory uses the following layout:
MyModule/ # Module root (equals the module name)
|
+-- MyModule.psd1 # Module manifest
+-- MyModule.psm1 # Dev: dot-source loader | Build: merged output
|
+-- Public/ # Exported functions — one file per function
| +-- Get-Something.ps1
| +-- Set-Something.ps1
|
+-- Private/ # Internal helpers — one file per function
| +-- Invoke-Helper.ps1
| +-- ConvertTo-Internal.ps1
|
+-- Classes/ # PowerShell classes — numerically prefixed for load order
| +-- 01_BaseModel.Class.ps1
| +-- 02_DerivedModel.Class.ps1
|
+-- Enums/ # Enum definitions — numerically prefixed for load order
+-- 01_StatusEnum.Enum.ps1
File naming conventions:
- Public and Private functions:
FunctionName.ps1 - Classes:
NN_ClassName.Class.ps1(numeric prefix controls load order) - Enums:
NN_EnumName.Enum.ps1(numeric prefix controls load order)
PSModuleFactory/ # Repository root
|
+-- YFridelance.PS.ModuleFactory/ # Module source
| +-- YFridelance.PS.ModuleFactory.psd1 # Module manifest
| +-- YFridelance.PS.ModuleFactory.psm1 # Dev dot-source loader
| +-- Public/ # Four exported functions
| | +-- Build-PSModule.ps1
| | +-- Initialize-PSModule.ps1
| | +-- Split-PSModule.ps1
| | +-- Update-PSModuleVersion.ps1
| +-- Private/ # Nine internal helper functions
| | +-- ConvertTo-SortedClassFileName.ps1
| | +-- Get-AliasesFromFile.ps1
| | +-- Get-FunctionNamesFromFile.ps1
| | +-- Merge-SourceFiles.ps1
| | +-- New-DevPsm1Content.ps1
| | +-- Resolve-ModuleSourcePaths.ps1
| | +-- Split-PsFileContent.ps1
| | +-- Test-ModuleProjectStructure.ps1
| | +-- Update-ManifestField.ps1
| +-- Classes/ # (reserved)
| +-- Enums/ # (reserved)
|
+-- Tests/ # Pester v5 test suite
| +-- Unit/
| | +-- Public/
| | +-- Private/
| +-- Integration/
| +-- Fixtures/
|
+-- dist/ # Build output (gitignored)
+-- build.ps1 # Self-build script (dogfooding)
+-- .github/
| +-- workflows/
| +-- build.yml # CI pipeline
+-- ARCHITECTURE.md
+-- CHANGELOG.md
+-- LICENSE
+-- README.md
| Requirement | Minimum version | Notes |
|---|---|---|
| PowerShell | 5.1 | Windows PowerShell 5.1 and PowerShell 7+ both supported |
| Pester | 5.0 | Required only for running tests |
| Git | Any recent version | Required only for Update-PSModuleVersion |
No external PowerShell module dependencies are required at runtime.
| Topic | File |
|---|---|
| Build-PSModule | docs/Build-PSModule.md |
| Initialize-PSModule | docs/Initialize-PSModule.md |
| Split-PSModule | docs/Split-PSModule.md |
| Update-PSModuleVersion | docs/Update-PSModuleVersion.md |
| Conventional Commits guide | docs/ConventionalCommits.md |
- Fork the repository and create a feature branch.
- Follow the existing code style: PascalCase variables, comment-based help on every function,
SupportsShouldProcesson state-modifying functions. - Write Pester v5 tests for any new or changed behavior. Place unit tests under
Tests/Unit/Public/orTests/Unit/Private/to match the source layout. - Use Conventional Commits for all commit messages so that
Update-PSModuleVersioncan determine the correct version bump automatically. See docs/ConventionalCommits.md for the format. - Open a pull request against
main. The CI pipeline must pass before merge.
MIT License. Copyright (c) 2026 Yves Fridelance. See LICENSE for the full text.