Public/New-StaticSiteTheme.ps1

<#
.SYNOPSIS
Creates a new Hyde theme scaffold.
 
.DESCRIPTION
`New-StaticSiteTheme` scaffolds a starter Hyde theme at the target destination.
 
By default, it creates a previewable scaffold that includes an `index.md` page
so the generated structure can be built immediately.
 
When `-Portable` is used, it creates a reusable package-style scaffold without
preview content pages.
 
.PARAMETER Destination
Path where the theme scaffold should be created.
 
If the destination exists, it must be empty.
 
.PARAMETER Portable
Creates a reusable package-style scaffold without preview content pages.
 
Portable mode omits `index.md` and keeps only shared theme structure.
 
.PARAMETER Quiet
Suppresses information messages while creating the scaffold.
 
.EXAMPLE
New-StaticSiteTheme -Destination .\mytheme
 
Creates a previewable theme scaffold in `./mytheme`.
 
.EXAMPLE
New-StaticSiteTheme -Destination .\mytheme -Portable
 
Creates a portable theme scaffold in `./mytheme`.
 
.EXAMPLE
Hyde New-Theme .\mytheme
 
Runs the same scaffold flow through the top-level Hyde command.
 
.NOTES
Theme scaffolding in Hyde is intentionally file-generation only.
 
This command does not install, apply, or distribute themes.
 
The default previewable mode favors quick iteration by including a build target.
 
Portable mode favors reuse by producing only layout/include/style assets.
 
The scaffold uses `_sass` plus `assets/css/site.scss` on purpose:
- Hyde defaults `sass.sass_dir` to `_sass`.
- The current built-in Sass path is LibSass-based.
- Generated Sass uses classic `@import` intentionally for current compatibility.
#>

function New-StaticSiteTheme {
    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([System.IO.DirectoryInfo])]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Destination,

        [switch]$Portable,

        [switch]$Quiet
    )

    Set-StrictMode -Version Latest
    $ErrorActionPreference = 'Stop'

    if (-not $Quiet) {
        $InformationPreference = 'Continue'
    }

    $destinationPath = resolveHydePath -Location $Destination -MayNotExist

    if (Test-Path -LiteralPath $destinationPath) {
        $existingItems = @(Get-ChildItem -LiteralPath $destinationPath -Force)
        if ($existingItems.Count -gt 0) {
            throw "Could not create a new theme at '$destinationPath' because the destination already exists and is not empty."
        }
    } else {
        if ($PSCmdlet.ShouldProcess($destinationPath, 'Create destination directory')) {
            [void](New-Item -Path $destinationPath -ItemType Directory -Force)
        }
    }

    Write-Information "Creating a new Hyde theme at '$destinationPath'."

    if (-not $PSCmdlet.ShouldProcess($destinationPath, 'Create Hyde theme scaffolding')) {
        if (Test-Path -LiteralPath $destinationPath) {
            return (Get-Item -LiteralPath $destinationPath)
        }

        return [System.IO.DirectoryInfo]::new($destinationPath)
    }

    $themeFiles = @(
        @{
            Path = '_config.yml'
            Content = @'
title: My Hyde Theme Preview
description: Starter preview for a reusable Hyde theme.
baseurl: ""
'@

        },
        @{
            Path = '_layouts/default.html'
            Content = @'
<!DOCTYPE html>
<html lang="en">
<head>
  {% include head.html %}
</head>
<body>
  {% include site-header.html %}
  <main class="site-shell">
    {{ content }}
  </main>
</body>
</html>
'@

        },
        @{
            Path = '_includes/head.html'
            Content = @'
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% if page.title %}{{ page.title }} | {% endif %}{{ site.title }}</title>
<link rel="stylesheet" href="{{ site.baseurl }}/assets/css/site.css">
'@

        },
        @{
            Path = '_includes/site-header.html'
            Content = @'
<header class="site-header">
  <div class="site-shell">
    <a class="site-title" href="{{ site.baseurl }}/">{{ site.title }}</a>
    {% if site.description %}
    <p class="site-tagline">{{ site.description }}</p>
    {% endif %}
  </div>
</header>
'@

        },
        @{
            # Keep the emitted stylesheet under assets and the shared variables in _sass.
            Path = 'assets/css/site.scss'
            Content = @'
@import "../../_sass/theme";
'@

        },
        @{
            Path = '_sass/_theme.scss'
            Content = @'
$page-width: 56rem;
$surface-color: #f6f3ea;
$text-color: #1f1f1f;
$accent-color: #945d00;
 
body {
  margin: 0;
  background: linear-gradient(180deg, #fdfaf3 0%, $surface-color 100%);
  color: $text-color;
  font-family: Georgia, "Times New Roman", serif;
  line-height: 1.6;
}
 
.site-shell {
  width: min(100% - 2rem, $page-width);
  margin: 0 auto;
}
 
.site-header {
  padding: 2rem 0 1rem;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}
 
.site-title {
  color: $text-color;
  font-size: 2rem;
  font-weight: 700;
  text-decoration: none;
}
 
.site-tagline {
  margin: 0.5rem 0 0;
  color: rgba(31, 31, 31, 0.75);
}
 
main {
  padding: 2rem 0 4rem;
}
 
a {
  color: $accent-color;
}
'@

        }
    )

    if (-not $Portable) {
        # The default scaffold doubles as a tiny preview site so the theme has an immediate build target.
        $themeFiles += @{
            Path = 'index.md'
            Content = @'
---
title: Theme Preview
layout: default
---
# Hyde Theme Preview
 
This starter theme scaffold gives you layouts, includes, and Sass structure you can evolve into a reusable Hyde theme.
'@

        }
    }

    foreach ($themeFile in $themeFiles) {
        $filePath = Join-Path -Path $destinationPath -ChildPath $themeFile.Path
        $parentPath = Split-Path -Path $filePath -Parent

        if (-not (Test-Path -LiteralPath $parentPath)) {
            [void](New-Item -Path $parentPath -ItemType Directory -Force)
        }

        Set-Content -LiteralPath $filePath -Encoding UTF8 -Value $themeFile.Content
    }

    Write-Information "Created new Hyde theme at '$destinationPath'."
    return (Get-Item -LiteralPath $destinationPath)
}