Documentarian.MicrosoftDocs.psm1
# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. #region Functions.Public function Get-HtmlMetaTags { [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [uri]$ArticleUrl, [switch]$ShowRequiredMetadata ) $hash = [ordered]@{} $x = Invoke-WebRequest $ArticleUrl $lines = (($x -split "`n").trim() | Select-String -Pattern '\<meta').line | ForEach-Object { $_.trimstart('<meta ').trimend(' />') | Sort-Object } $pattern = '(name|property)="(?<key>[^"]+)"\s*content="(?<value>[^"]+)"' foreach ($line in $lines) { if ($line -match $pattern) { if ($hash.Contains($Matches.key)) { $hash[($Matches.key)] += ',' + $Matches.value } else { $hash.Add($Matches.key, $Matches.value) } } } $result = New-Object -type psobject -prop ($hash) if ($ShowRequiredMetadata) { $result | Select-Object title, 'og:title', description, 'ms.manager', 'ms.author', author, 'ms.service', 'ms.date', 'ms.topic', 'ms.subservice', 'ms.prod', 'ms.technology', 'ms.custom', 'ROBOTS' } else { $result } } function Get-LocaleFreshness { [CmdletBinding()] [OutputType('DocumentLocaleInfo')] param( [Parameter(Mandatory, Position = 0)] [uri]$Uri, [Parameter(Position = 1)] [ValidatePattern('[a-z]{2}-[a-z]{2}')] [string[]]$Locales = ( 'en-us', 'cs-cz', 'de-de', 'es-es', 'fr-fr', 'hu-hu', 'id-id', 'it-it', 'ja-jp', 'ko-kr', 'nl-nl', 'pl-pl', 'pt-br', 'pt-pt', 'ru-ru', 'sv-se', 'tr-tr', 'zh-cn', 'zh-tw' ) ) $locale = $uri.Segments[1].Trim('/') if ($locale -notmatch '[a-z]{2}-[a-z]{2}') { Write-Error "URL does not contain a valid locale: $locale" return } else { $url = $uri.OriginalString $Locales | ForEach-Object { $locPath = $_ $result = Get-HtmlMetaTags ($url -replace $locale, $locPath) | Select-Object @{n = 'locpath'; e = { $locPath } }, locale, 'ms.contentlocale', 'ms.translationtype', 'ms.date', 'loc_version', 'updated_at', 'loc_source_id', 'loc_file_id', 'original_content_git_url' $result.pstypenames.Insert(0, 'DocumentLocaleInfo') $result } | Sort-Object 'updated_at', 'ms.contentlocale' } } function Sync-BeyondCompare { [cmdletbinding()] param ( [Parameter(Mandatory, Position = 0)] [string]$Path ) ### Get-GitStatus comes from the posh-git module. $gitStatus = Get-GitStatus if ($gitStatus) { $reponame = $GitStatus.RepoName } else { Write-Warning 'Not a git repo.' return } $repoPath = $global:git_repos[$reponame].path $ops = Get-Content $repoPath\.openpublishing.publish.config.json | ConvertFrom-Json -Depth 10 -AsHashtable $srcPath = $ops.docsets_to_publish.build_source_folder if ($srcPath -eq '.') { $srcPath = '' } $basePath = Join-Path $repoPath $srcPath '\' $mapPath = Join-Path $basePath $ops.docsets_to_publish.monikerPath $monikers = Get-Content $mapPath | ConvertFrom-Json -Depth 10 -AsHashtable $startPath = (Get-Item $Path).fullname $vlist = $monikers.keys | ForEach-Object { $monikers[$_].packageRoot } if ($startpath) { $relPath = $startPath -replace [regex]::Escape($basepath) $version = ($relPath -split '\\')[0] foreach ($v in $vlist) { if ($v -ne $version) { $target = $startPath -replace [regex]::Escape($version), $v if (Test-Path $target) { Start-Process -Wait "${env:ProgramFiles}\Beyond Compare 4\BComp.exe" -ArgumentList $startpath, $target } } } } else { Write-Error "Invalid path: $Path" } } function Sync-VSCode { [cmdletbinding()] param( [Parameter(Mandatory, Position = 0)] [string]$Path ) ### Get-GitStatus comes from the posh-git module. $gitStatus = Get-GitStatus if ($gitStatus) { $reponame = $GitStatus.RepoName } else { Write-Warning 'Not a git repo.' return } $repoPath = $global:git_repos[$reponame].path $ops = Get-Content $repoPath\.openpublishing.publish.config.json | ConvertFrom-Json -Depth 10 -AsHashtable $srcPath = $ops.docsets_to_publish.build_source_folder if ($srcPath -eq '.') { $srcPath = '' } $basePath = Join-Path $repoPath $srcPath '\' $mapPath = Join-Path $basePath $ops.docsets_to_publish.monikerPath $monikers = Get-Content $mapPath | ConvertFrom-Json -Depth 10 -AsHashtable $startPath = (Get-Item $Path).fullname $vlist = $monikers.keys | ForEach-Object { $monikers[$_].packageRoot } if ($startpath) { $relPath = $startPath -replace [regex]::Escape($basepath) $version = ($relPath -split '\\')[0] foreach ($v in $vlist) { if ($v -ne $version) { $target = $startPath -replace [regex]::Escape($version), $v if (Test-Path $target) { Start-Process -Wait -WindowStyle Hidden 'code' -ArgumentList '--diff', '--wait', '--reuse-window', $startpath, $target } } } } else { Write-Error "Invalid path: $Path" } } function Test-YamlTOC { [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [string]$Path ) $toc = Get-Item $Path $basepath = "$($toc.Directory)\" $hrefs = (Select-String -Pattern '\s*href:\s+([\w\-\._/]+)\s*$' -Path $toc.FullName).Matches | ForEach-Object { $_.Groups[1].Value } | Sort-Object $hrefs | ForEach-Object { $file = $basepath + ($_ -replace '/', '\') if (-not (Test-Path $file)) { "File does not exist - $_" } } $files = Get-ChildItem $basepath\*.md, $basepath\*.yml -Recurse -File | Where-Object Name -NE 'toc.yml' $files.FullName | ForEach-Object { $file = ($_ -replace [regex]::Escape($basepath)) -replace '\\', '/' if ($hrefs -notcontains $file) { "File not in TOC - $file" } } } #endregion Functions.Public $ExportableFunctions = @( 'Get-HtmlMetaTags' 'Get-LocaleFreshness' 'Sync-BeyondCompare' 'Sync-VSCode' 'Test-YamlTOC' ) Export-ModuleMember -Alias * -Function $ExportableFunctions |