Skip to content

Commit c3e8c11

Browse files
Implement ConvertFrom-Yaml with mappings, sequences, scalars, and frontmatter support
1 parent da33548 commit c3e8c11

4 files changed

Lines changed: 576 additions & 0 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
function ConvertFrom-YamlFrontmatter {
2+
<#
3+
.SYNOPSIS
4+
Extracts YAML frontmatter from a string when present, otherwise returns the string unchanged.
5+
6+
.DESCRIPTION
7+
If the input begins with a `---` line, returns the content between the opening
8+
`---` and the next `---` or `...` line. Anything after the closing delimiter
9+
(typically markdown body) is discarded.
10+
11+
If no frontmatter delimiter is detected, the original input is returned.
12+
#>
13+
[CmdletBinding()]
14+
[OutputType([string])]
15+
param(
16+
[Parameter(Mandatory)]
17+
[AllowEmptyString()]
18+
[string] $Text
19+
)
20+
21+
# Normalize line endings for matching.
22+
$normalized = $Text -replace "`r`n", "`n"
23+
$lines = $normalized -split "`n"
24+
25+
# Find first non-empty line.
26+
$firstIdx = -1
27+
for ($i = 0; $i -lt $lines.Count; $i++) {
28+
if ($lines[$i].Trim().Length -gt 0) {
29+
$firstIdx = $i
30+
break
31+
}
32+
}
33+
34+
if ($firstIdx -lt 0) {
35+
return $Text
36+
}
37+
38+
if ($lines[$firstIdx].Trim() -ne '---') {
39+
return $Text
40+
}
41+
42+
# Find closing delimiter.
43+
for ($j = $firstIdx + 1; $j -lt $lines.Count; $j++) {
44+
$trim = $lines[$j].Trim()
45+
if ($trim -eq '---' -or $trim -eq '...') {
46+
$body = $lines[($firstIdx + 1)..($j - 1)] -join "`n"
47+
return $body
48+
}
49+
}
50+
51+
# No closing delimiter — treat everything after opening as frontmatter.
52+
if ($firstIdx + 1 -lt $lines.Count) {
53+
return ($lines[($firstIdx + 1)..($lines.Count - 1)] -join "`n")
54+
}
55+
return ''
56+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
function ConvertFrom-YamlLineStream {
2+
<#
3+
.SYNOPSIS
4+
Splits YAML text into significant lines, dropping comments and blank lines.
5+
6+
.DESCRIPTION
7+
Returns an array of `[pscustomobject]` records with `Indent`, `Content`, and `Number` properties.
8+
- Lines that are empty or whitespace-only are skipped.
9+
- Lines whose first non-whitespace character is `#` are skipped.
10+
- Inline comments (` #...` outside quotes) are stripped from the content.
11+
- Tabs in indentation are not allowed (YAML spec); they are treated as one space here.
12+
#>
13+
[CmdletBinding()]
14+
[OutputType([System.Collections.Generic.List[pscustomobject]])]
15+
param(
16+
[Parameter(Mandatory)]
17+
[AllowEmptyString()]
18+
[string] $Text
19+
)
20+
21+
$result = [System.Collections.Generic.List[pscustomobject]]::new()
22+
$normalized = $Text -replace "`r`n", "`n"
23+
$rawLines = $normalized -split "`n"
24+
25+
for ($i = 0; $i -lt $rawLines.Count; $i++) {
26+
$raw = $rawLines[$i]
27+
28+
if ([string]::IsNullOrWhiteSpace($raw)) {
29+
continue
30+
}
31+
32+
# Compute indent (spaces before first non-space).
33+
$indent = 0
34+
while ($indent -lt $raw.Length -and ($raw[$indent] -eq ' ' -or $raw[$indent] -eq "`t")) {
35+
$indent++
36+
}
37+
38+
$content = $raw.Substring($indent)
39+
40+
if ($content.StartsWith('#')) {
41+
continue
42+
}
43+
44+
# Strip inline comments while respecting single/double quotes.
45+
$stripped = Remove-YamlInlineComment -Line $content
46+
if ([string]::IsNullOrWhiteSpace($stripped)) {
47+
continue
48+
}
49+
50+
$result.Add([pscustomobject]@{
51+
Indent = $indent
52+
Content = $stripped.TrimEnd()
53+
Number = $i + 1
54+
})
55+
}
56+
57+
return , $result
58+
}
59+
60+
function Remove-YamlInlineComment {
61+
<#
62+
.SYNOPSIS
63+
Removes an unquoted `# comment` suffix from a YAML content line.
64+
#>
65+
[CmdletBinding()]
66+
[OutputType([string])]
67+
param(
68+
[Parameter(Mandatory)]
69+
[AllowEmptyString()]
70+
[string] $Line
71+
)
72+
73+
$inSingle = $false
74+
$inDouble = $false
75+
for ($i = 0; $i -lt $Line.Length; $i++) {
76+
$c = $Line[$i]
77+
if ($c -eq '\' -and $inDouble) {
78+
# Skip escaped char inside double quotes.
79+
$i++
80+
continue
81+
}
82+
if ($c -eq "'" -and -not $inDouble) {
83+
$inSingle = -not $inSingle
84+
continue
85+
}
86+
if ($c -eq '"' -and -not $inSingle) {
87+
$inDouble = -not $inDouble
88+
continue
89+
}
90+
if ($c -eq '#' -and -not $inSingle -and -not $inDouble) {
91+
# Comment must be preceded by whitespace or be at start of line.
92+
if ($i -eq 0 -or $Line[$i - 1] -eq ' ' -or $Line[$i - 1] -eq "`t") {
93+
return $Line.Substring(0, $i)
94+
}
95+
}
96+
}
97+
return $Line
98+
}

0 commit comments

Comments
 (0)