constructive-boilerplate-authoring
Create and customize boilerplate templates for pgpm init.
Overview
PGPM uses the genomic library to scaffold projects from templates. Templates are stored in a boilerplate repository (default: constructive-io/pgpm-boilerplates) and use placeholder substitution for customization.
Template Repository Structure
my-boilerplates/
.boilerplates.json # Root config (points to default directory)
pgpm/ # Default template variant (PGPM)
module/
.boilerplate.json # Module template config
package.json # Template files with placeholders
pgpm.plan
...
workspace/
.boilerplate.json # Workspace template config
package.json
pgpm.json
...
pnpm/ # Alternative variant (pure PNPM, no pgpm files)
module/
.boilerplate.json
...
workspace/
.boilerplate.json
...
Root Configuration
The .boilerplates.json file at the repository root specifies the default template directory:
{
"dir": "pgpm"
}
Template Configuration
Each template has a .boilerplate.json file defining its type, workspace requirements, and questions.
Basic Structure
{
"type": "workspace",
"questions": [
{
"name": "____repoName____",
"message": "Enter the repository name",
"required": true
}
]
}
Template Types
| Type | Description |
|---|---|
workspace |
Creates a new monorepo workspace |
module |
Creates a package within a workspace |
generic |
Standalone template (no workspace context) |
Workspace Requirements
The requiresWorkspace field controls what type of workspace the template needs:
{
"type": "module",
"requiresWorkspace": "pgpm"
}
| Value | Description |
|---|---|
"pgpm" |
Requires PGPM workspace (pgpm.json), creates pgpm.plan/.control files |
"pnpm" |
Requires PNPM workspace (pnpm-workspace.yaml) |
"lerna" |
Requires Lerna workspace (lerna.json) |
"npm" |
Requires npm workspace (package.json with workspaces) |
false |
No workspace required |
Placeholder System
Templates use the ____placeholder____ pattern (4 underscores on each side) for variable substitution:
{
"name": "@____username____/____moduleName____",
"version": "0.0.1",
"description": "____moduleDesc____",
"author": "____fullName____ <____email____>"
}
Question Configuration
Question Fields
| Field | Type | Description |
|---|---|---|
name |
string | Placeholder name (e.g., ____fullName____) |
message |
string | Prompt shown to user |
required |
boolean | Whether the field is required |
type |
string | Input type: text, list, checkbox |
options |
string[] | Static options for list/checkbox |
default |
any | Static default value |
defaultFrom |
string | Resolver for dynamic default |
setFrom |
string | Auto-set value (skips prompt) |
optionsFrom |
string | Resolver for dynamic options |
Question Types
Text Input (default):
{
"name": "____moduleName____",
"message": "Enter the module name",
"required": true
}
List Selection:
{
"name": "____access____",
"message": "Module access?",
"type": "list",
"options": ["public", "restricted"],
"default": "public"
}
Checkbox (multi-select):
{
"name": "____extensions____",
"message": "Select PostgreSQL extensions",
"type": "checkbox",
"options": ["plpgsql", "uuid-ossp", "citext", "pgcrypto"]
}
Resolvers
Resolvers dynamically populate values from the environment or workspace context.
defaultFrom Resolvers:
| Resolver | Description |
|---|---|
git.user.name |
Git config user.name |
git.user.email |
Git config user.email |
npm.whoami |
npm whoami result |
workspace.dirname |
Current directory name |
setFrom Resolvers (auto-set, skips prompt):
| Resolver | Description |
|---|---|
workspace.name |
Workspace name from pgpm.json/package.json |
workspace.author.name |
Workspace author name |
workspace.author.email |
Workspace author email |
workspace.license |
Workspace license |
workspace.organization.name |
Workspace organization |
optionsFrom Resolvers:
| Resolver | Description |
|---|---|
licenses |
List of SPDX license identifiers |
Example: Complete Module Template
{
"type": "module",
"requiresWorkspace": "pgpm",
"questions": [
{
"name": "____fullName____",
"message": "Enter author full name",
"setFrom": "workspace.author.name",
"defaultFrom": "git.user.name",
"required": true
},
{
"name": "____email____",
"message": "Enter author email",
"setFrom": "workspace.author.email",
"defaultFrom": "git.user.email",
"required": true
},
{
"name": "____moduleName____",
"message": "Enter the module name",
"required": true
},
{
"name": "____moduleDesc____",
"message": "Enter the module description",
"required": true
},
{
"name": "____repoName____",
"message": "Enter the repository name",
"setFrom": "workspace.name",
"required": true
},
{
"name": "____username____",
"message": "Enter your github username",
"setFrom": "workspace.organization.name",
"defaultFrom": "npm.whoami",
"required": true
},
{
"name": "____access____",
"message": "Module access?",
"type": "list",
"options": ["public", "restricted"],
"default": "public",
"required": true
},
{
"name": "____license____",
"message": "Choose a license",
"type": "list",
"optionsFrom": "licenses",
"setFrom": "workspace.license",
"required": true
}
]
}
Creating a Custom Boilerplate Repository
- Create a new repository with the structure above
- Add
.boilerplates.jsonpointing to your default directory - Create template directories with
.boilerplate.jsonconfigs - Add template files with
____placeholder____patterns - Use with
pgpm init --repo owner/your-boilerplates
Using Custom Templates
# Use your own boilerplate repository
pgpm init workspace --repo myorg/my-boilerplates
# Use a specific branch
pgpm init workspace --repo myorg/my-boilerplates --from-branch develop
# Use a variant directory
pgpm init workspace --repo myorg/my-boilerplates --dir custom-variant
# Use the --template flag (recommended)
pgpm init --template pnpm/module --repo myorg/my-boilerplates
pgpm init -t custom-variant/module --repo myorg/my-boilerplates
# Create workspace + module in one command
pgpm init -t pnpm/module -w --repo myorg/my-boilerplates
Best Practices
- Use
setFromfor values that should inherit from workspace context - Use
defaultFromfor sensible defaults that users can override - Keep placeholder names descriptive and consistent
- Document custom templates in your repository README
- Test templates with
--no-ttyto ensure all required fields are defined