# skd-decompile v0.96 — Decompile 1C DCS Template.xml to JSON DSL (draft) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills [CmdletBinding(PositionalBinding=$false)] param( [Parameter(Mandatory)] [Alias('Path')] [string]$TemplatePath, [string]$OutputPath ) $ErrorActionPreference = "Stop" # --- Разбор пользовательского JSON --- # Одна строка в stderr вместо дампа исключения ConvertFrom-Json (issue #80): агент по стектрейсу # идёт чинить скрипт, а не свой вызов. $source — файл или параметр. $expected заполняем только # для полиморфного входа: у файла подсказка была бы наполнителем. -Inline печатает ещё и то, # что доехало: у файла такого вопроса нет — путь назван, позицию дал парсер, файл на диске. # Возврат через -NoEnumerate: без него одноэлементный # JSON-массив разворачивался бы в скаляр вторым анруллингом. function ConvertFrom-JsonInput([string]$text, [string]$source, [string]$expected, [switch]$Inline) { try { # PS 5.1 на пустой строке отдаёт $null, а не ошибку — навык уходил дальше с $null, # тогда как py-порт падал. Проверяем сами, чтобы порты вели себя одинаково. if ([string]::IsNullOrWhiteSpace($text)) { throw 'input is empty' } $parsed = $text | ConvertFrom-Json } catch { $what = if ($expected) { "$source expects $expected" } else { "Invalid JSON in $source" } if ($Inline) { $got = ($text -replace '\s+', ' ').Trim() $label = 'got' if (-not $got) { $got = '(empty)' } elseif ($got.Length -gt 60) { $label = 'got (first 60 chars)'; $got = $got.Substring(0, 60) } $what = "${what}, ${label}: ${got}" } [Console]::Error.WriteLine("[ERROR] ${what} ($($_.Exception.Message))") exit 1 } Write-Output -NoEnumerate $parsed } # --- Чтение входного JSON-файла --- # Кодировку берём из BOM — это объявление самого файла, а не догадка. Без BOM ждём строгий UTF-8: # Get-Content -Encoding UTF8 на файле в cp1251 тихо меняет кириллицу на U+FFFD, JSON после этого # разбирается успешно, и в конфигурацию уезжает имя из «замен». Кодовую страницу не подбираем: # угаданное имя уйдёт в метаданные так же молча. function Read-JsonInputFile([string]$path) { # Проверка здесь, а не по навыкам: часть навыков проверяла путь сама, часть — нет, и один и тот # же промах давал то внятную строку, то дамп MethodInvocationException. Навыки со своей # проверкой срабатывают раньше и сохраняют свой текст. if (-not (Test-Path -LiteralPath $path)) { [Console]::Error.WriteLine("[ERROR] File not found: $path") exit 1 } if (Test-Path -LiteralPath $path -PathType Container) { [Console]::Error.WriteLine("[ERROR] Expected a JSON file, got a directory: $path") exit 1 } $bytes = [System.IO.File]::ReadAllBytes($path) if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { return [System.Text.Encoding]::UTF8.GetString($bytes, 3, $bytes.Length - 3) } if ($bytes.Length -ge 2 -and $bytes[0] -eq 0xFF -and $bytes[1] -eq 0xFE) { return [System.Text.Encoding]::Unicode.GetString($bytes, 2, $bytes.Length - 2) } if ($bytes.Length -ge 2 -and $bytes[0] -eq 0xFE -and $bytes[1] -eq 0xFF) { return [System.Text.Encoding]::BigEndianUnicode.GetString($bytes, 2, $bytes.Length - 2) } try { return (New-Object System.Text.UTF8Encoding($false, $true)).GetString($bytes) } catch { $detail = if ($_.Exception.InnerException) { $_.Exception.InnerException.Message } else { $_.Exception.Message } [Console]::Error.WriteLine("[ERROR] ${path} is not valid UTF-8: ${detail} - save the file as UTF-8, or add a BOM if it is UTF-16") exit 1 } } [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 # --- 0. Resolve and validate input --- if (-not (Test-Path $TemplatePath)) { Write-Error "Template not found: $TemplatePath" exit 1 } $TemplatePath = (Resolve-Path $TemplatePath).Path $xmlDoc = New-Object System.Xml.XmlDocument $xmlDoc.PreserveWhitespace = $false $xmlDoc.Load($TemplatePath) $root = $xmlDoc.DocumentElement # Ring 3: not a DataCompositionSchema → fail-fast if ($root.LocalName -ne 'DataCompositionSchema') { [Console]::Error.WriteLine("skd-decompile: корневой элемент <$($root.LocalName)> не — это не схема СКД (возможно, табличный документ — используй /mxl-decompile).") exit 2 } # --- 1. Namespace manager --- $NS_SCHEMA = "http://v8.1c.ru/8.1/data-composition-system/schema" $NS_COM = "http://v8.1c.ru/8.1/data-composition-system/common" $NS_COR = "http://v8.1c.ru/8.1/data-composition-system/core" $NS_SET = "http://v8.1c.ru/8.1/data-composition-system/settings" $NS_AT = "http://v8.1c.ru/8.1/data-composition-system/area-template" $NS_V8 = "http://v8.1c.ru/8.1/data/core" $NS_V8UI = "http://v8.1c.ru/8.1/data/ui" $NS_XS = "http://www.w3.org/2001/XMLSchema" $NS_XSI = "http://www.w3.org/2001/XMLSchema-instance" $NS_CFG = "http://v8.1c.ru/8.1/data/enterprise/current-config" $ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable) $ns.AddNamespace("r", $NS_SCHEMA) $ns.AddNamespace("dcscom", $NS_COM) $ns.AddNamespace("dcscor", $NS_COR) $ns.AddNamespace("dcsset", $NS_SET) $ns.AddNamespace("dcsat", $NS_AT) $ns.AddNamespace("v8", $NS_V8) $ns.AddNamespace("v8ui", $NS_V8UI) $ns.AddNamespace("xs", $NS_XS) $ns.AddNamespace("xsi", $NS_XSI) # --- 1b. Ring 3 scan: bail out on unsupported constructs --- function Fail-Ring3 { param([string]$kind, [string]$loc) [Console]::Error.WriteLine("skd-decompile: декомпиляция не поддерживает $kind (path: $loc)") [Console]::Error.WriteLine("Для точечной работы с этим отчётом используй /skd-edit.") exit 3 } # Picture cells in templates foreach ($el in $xmlDoc.SelectNodes("//*[local-name()='item']")) { $xsi = $el.GetAttribute("type", "http://www.w3.org/2001/XMLSchema-instance") if ($xsi -match 'Picture$' -and $el.NamespaceURI -eq "http://v8.1c.ru/8.1/data-composition-system/area-template") { Fail-Ring3 -kind "Picture cell в шаблоне" -loc "template/.../item[@xsi:type=Picture]" } } # ValueStorage parameter type foreach ($vt in $xmlDoc.SelectNodes("//*[local-name()='Type']")) { $inner = $vt.InnerText if ($inner -match '^v8:ValueStorage$|:ValueStorage$') { Fail-Ring3 -kind "параметр типа ХранилищеЗначения" -loc "valueType[v8:Type=ValueStorage]" } } # templateCondition (variant templates) — top-level