diff --git a/.claude/skills/meta-edit/scripts/meta-edit.ps1 b/.claude/skills/meta-edit/scripts/meta-edit.ps1 index 4b000a316..4a9359eb9 100644 --- a/.claude/skills/meta-edit/scripts/meta-edit.ps1 +++ b/.claude/skills/meta-edit/scripts/meta-edit.ps1 @@ -1,4 +1,4 @@ -# meta-edit v1.55 — Edit existing 1C metadata object XML +# meta-edit v1.56 — Edit existing 1C metadata object XML # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills [CmdletBinding(PositionalBinding=$false)] param( @@ -381,6 +381,14 @@ function Die($msg) { exit 1 } +# Побочные файлы (таблица внешнего источника, модуль команды, предопределённые) пишутся после +# основного XML: отказ посреди определения не оставляет на диске ни одного изменения. +$script:pendingWrites = [ordered]@{} + +function Test-PendingOrFile([string]$path) { + return ($script:pendingWrites.Contains($path) -or (Test-Path $path)) +} + # ============================================================ # Section 2: Detect object type # ============================================================ @@ -2099,8 +2107,7 @@ function Convert-InlineToDefinition([string]$operation, [string]$value) { foreach ($item in $items) { $dotIdx = $item.IndexOf('.') if ($dotIdx -le 0) { - Warn "Invalid ts-attribute format (expected TSName.AttrDef): $item" - continue + Die "Invalid ts-attribute format (expected TSName.AttrDef): $item" } $tsName = $item.Substring(0, $dotIdx).Trim() $rest = $item.Substring($dotIdx + 1).Trim() @@ -2126,8 +2133,7 @@ function Convert-InlineToDefinition([string]$operation, [string]$value) { foreach ($elemDef in $tsGroups[$tsName]) { $colonIdx = $elemDef.IndexOf(':') if ($colonIdx -le 0) { - Warn "Invalid modify format (expected Name: key=val): $elemDef" - continue + Die "Invalid modify format (expected Name: key=val): $elemDef" } $elemName = $elemDef.Substring(0, $colonIdx).Trim() $changesPart = $elemDef.Substring($colonIdx + 1).Trim() @@ -2237,7 +2243,7 @@ function Convert-InlineToDefinition([string]$operation, [string]$value) { $v = $kv.Substring($eqIdx + 1).Trim() $propsObj | Add-Member -NotePropertyName $k -NotePropertyValue $v } else { - Warn "Invalid property format (expected Key=Value): $kv" + Die "Invalid property format (expected Key=Value): $kv" } } $modifyObj = New-Object PSCustomObject @@ -2250,8 +2256,7 @@ function Convert-InlineToDefinition([string]$operation, [string]$value) { foreach ($elemDef in $elemDefs) { $colonIdx = $elemDef.IndexOf(':') if ($colonIdx -le 0) { - Warn "Invalid modify format (expected Name: key=val): $elemDef" - continue + Die "Invalid modify format (expected Name: key=val): $elemDef" } $elemName = $elemDef.Substring(0, $colonIdx).Trim() $changesPart = $elemDef.Substring($colonIdx + 1).Trim() @@ -2340,8 +2345,7 @@ function Process-Add($addDef) { $childType = Resolve-ChildTypeKey $rawKey if (-not $childType) { - Warn "Unknown add child type: $rawKey" - return + Die "Unknown add child type: $rawKey" } # Validate allowed. Проверяем НАЛИЧИЕ ключа, а не истинность списка: пустой список @@ -2350,15 +2354,13 @@ function Process-Add($addDef) { if ($script:validChildTypes.ContainsKey($script:objType)) { $allowed = $script:validChildTypes[$script:objType] if ($childType -notin $allowed) { - Warn "$childType not allowed for $($script:objType), skipping" - return + Die "$childType not allowed for $($script:objType)" } } $xmlTag = $script:childTypeToXmlTag[$childType] if (-not $xmlTag) { - Warn "No XML tag mapping for $childType" - return + Die "No XML tag mapping for $childType" } Ensure-ChildObjectsOpen @@ -2401,8 +2403,7 @@ function Process-Add($addDef) { if ($tv) { foreach ($k in @("characteristics","defaultObjectForm","defaultRecordForm","defaultListForm","defaultChoiceForm")) { if ($tv.$k) { - Warn "Ключ '$k' не поддержан при добавлении таблицы: форму назначает навык form-add, характеристики — навык meta-compile. Таблица '$tblName' пропущена." - $tblName = $null; break + Die "Ключ '$k' не поддержан при добавлении таблицы: форму назначает навык form-add, характеристики — навык meta-compile." } } } @@ -2412,7 +2413,7 @@ function Process-Add($addDef) { continue } $tablePath = Join-Path $tablesDir "$tblName.xml" - if (Test-Path $tablePath) { + if (Test-PendingOrFile $tablePath) { Warn "Файл таблицы уже существует: $tablePath — пропускаю" continue } @@ -2422,8 +2423,7 @@ function Process-Add($addDef) { } $fieldsXml = $fieldParts -join "`r`n" $tableXml = Build-EdsTableXml $script:objName $tblName $entry.Value $fieldsXml "" "" - if (-not (Test-Path $tablesDir)) { New-Item -ItemType Directory -Path $tablesDir -Force | Out-Null } - [System.IO.File]::WriteAllText($tablePath, $tableXml.TrimEnd("`r", "`n"), (New-Object System.Text.UTF8Encoding($true))) + $script:pendingWrites[$tablePath] = $tableXml.TrimEnd("`r", "`n") $fragmentXml = "$indent$(Esc-XmlText $tblName)
" $nodes = Import-Fragment $fragmentXml $refNode = Find-InsertionPoint "Table" @{ name = $tblName } @@ -2579,7 +2579,7 @@ function Process-Add($addDef) { # эту ответственность здесь нельзя: получится висячая регистрация без файла. $skillName = if ($childType -eq "forms") { "form-add" } else { "template-add" } $whatName = if ($childType -eq "forms") { "Форму" } else { "Макет" } - Warn "$whatName добавляет навык $skillName (он создаёт и файл, и запись в ChildObjects). meta-edit этого не делает — операция пропущена." + Die "$whatName добавляет навык $skillName (он создаёт и файл, и запись в ChildObjects). meta-edit этого не делает." } "commands" { foreach ($item in $items) { @@ -2592,8 +2592,7 @@ function Process-Add($addDef) { # он есть у всех команд без исключения. Пишем ту же заготовку, что и meta-compile. $cmdExtDir = Join-Path (Join-Path (Join-Path (Join-Path (Split-Path -Parent $resolvedPath) $script:objName) "Commands") $itemName) "Ext" $cmdModPath = Join-Path $cmdExtDir "CommandModule.bsl" - if (-not (Test-Path $cmdExtDir)) { New-Item -ItemType Directory -Path $cmdExtDir -Force | Out-Null } - [System.IO.File]::WriteAllText($cmdModPath, "&НаКлиенте`r`nПроцедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)`r`n`r`n`t// Вставьте обработчик команды.`r`n`r`nКонецПроцедуры`r`n", (New-Object System.Text.UTF8Encoding($true))) + $script:pendingWrites[$cmdModPath] = "&НаКлиенте`r`nПроцедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)`r`n`r`n`t// Вставьте обработчик команды.`r`n`r`nКонецПроцедуры`r`n" $fragmentXml = Build-CommandFragment $itemName $indent $nodes = Import-Fragment $fragmentXml $refNode = Find-InsertionPoint "Command" @{ after = ""; before = "" } @@ -2620,19 +2619,16 @@ function Process-Remove($removeDef) { $childType = Resolve-ChildTypeKey $rawKey if (-not $childType) { - Warn "Unknown remove child type: $rawKey" - return + Die "Unknown remove child type: $rawKey" } if ($childType -eq "properties") { - Warn "Cannot remove properties — use modify instead" - return + Die "Cannot remove properties — use modify instead" } if ($childType -in @("forms","templates")) { # Снять регистрацию мало — надо удалить и файлы; это делают form-remove / template-remove. $skillName = if ($childType -eq "forms") { "form-remove" } else { "template-remove" } $whatName = if ($childType -eq "forms") { "Форму" } else { "Макет" } - Warn "$whatName удаляет навык $skillName (он убирает и файлы, и запись в ChildObjects). meta-edit этого не делает — операция пропущена." - return + Die "$whatName удаляет навык $skillName (он убирает и файлы, и запись в ChildObjects). meta-edit этого не делает." } $xmlTag = $script:childTypeToXmlTag[$childType] @@ -2687,8 +2683,7 @@ function Modify-Properties($propsDef) { Insert-PropertyInOrder $script:propertiesEl $newNodes[0] $null $propName $propEl = $newNodes[0] } else { - Warn "Property '$propName': could not create element" - return + Die "Property '$propName': could not create element" } } @@ -2750,8 +2745,7 @@ function Modify-Properties($propsDef) { function Modify-ChildElements($modifyDef, [string]$childType) { $xmlTag = $script:childTypeToXmlTag[$childType] if (-not $xmlTag -or -not $script:childObjectsEl) { - Warn "No ChildObjects or unknown tag for $childType" - return + Die "No ChildObjects or unknown tag for $childType" } $modifyDef.PSObject.Properties | ForEach-Object { @@ -2760,8 +2754,7 @@ function Modify-ChildElements($modifyDef, [string]$childType) { $el = Find-ElementByName $script:childObjectsEl $xmlTag $elemName if (-not $el) { - Warn "$xmlTag '$elemName' not found for modify" - return + Die "$xmlTag '$elemName' not found for modify" } # Find Properties inside the element @@ -2772,8 +2765,7 @@ function Modify-ChildElements($modifyDef, [string]$childType) { } } if (-not $propsEl) { - Warn "$xmlTag '$elemName': no Properties element found" - return + Die "$xmlTag '$elemName': no Properties element found" } $changes.PSObject.Properties | ForEach-Object { @@ -2793,8 +2785,7 @@ function Modify-ChildElements($modifyDef, [string]$childType) { switch ($changeProp) { "add" { if (-not $tsChildObjEl) { - Warn "TS '$elemName' has no ChildObjects element, cannot add attributes" - return + Die "TS '$elemName' has no ChildObjects element, cannot add attributes" } # Ensure ChildObjects is open (not self-closing empty) $hasTsChildElements = $false @@ -2846,8 +2837,7 @@ function Modify-ChildElements($modifyDef, [string]$childType) { } "modify" { if (-not $tsChildObjEl) { - Warn "TS '$elemName' has no ChildObjects, cannot modify attributes" - return + Die "TS '$elemName' has no ChildObjects, cannot modify attributes" } # Temporarily swap childObjectsEl and recurse $savedChildObjEl = $script:childObjectsEl @@ -3074,8 +3064,7 @@ function Process-Modify($modifyDef) { $childType = Resolve-ChildTypeKey $rawKey if (-not $childType) { - Warn "Unknown modify child type: $rawKey" - return + Die "Unknown modify child type: $rawKey" } if ($childType -eq "properties") { @@ -3621,7 +3610,7 @@ function Get-ComplexPropertyValues([System.Xml.XmlElement]$propEl) { function Add-ComplexPropertyItem([string]$propertyName, [string[]]$values) { $mapEntry = $script:complexPropertyMap[$propertyName] - if (-not $mapEntry) { Warn "Unknown complex property: $propertyName"; return } + if (-not $mapEntry) { Die "Unknown complex property: $propertyName" } if ($mapEntry.expand) { $values = @($values | ForEach-Object { Expand-DataPath "$_" }) } if ($mapEntry.mdref) { $values = @($values | ForEach-Object { Normalize-MDObjectRef "$_" $mapEntry.root }) } @@ -3706,7 +3695,7 @@ function Remove-ComplexPropertyItem([string]$propertyName, [string[]]$values) { function Set-ComplexProperty([string]$propertyName, [string[]]$values) { $mapEntry = $script:complexPropertyMap[$propertyName] - if (-not $mapEntry) { Warn "Unknown complex property: $propertyName"; return } + if (-not $mapEntry) { Die "Unknown complex property: $propertyName" } if ($mapEntry.expand) { $values = @($values | ForEach-Object { Expand-DataPath "$_" }) } if ($mapEntry.mdref) { $values = @($values | ForEach-Object { Normalize-MDObjectRef "$_" $mapEntry.root }) } @@ -3830,17 +3819,15 @@ function Add-PredefinedItems($items) { $itemsXml = "" foreach ($it in @($items)) { $itemsXml += (Build-PredefItemXml "`t" $it $codeType) } $utf8Bom = New-Object System.Text.UTF8Encoding($true) - if (Test-Path $path) { - $text = [System.IO.File]::ReadAllText($path, $utf8Bom) + if (Test-PendingOrFile $path) { + $text = if ($script:pendingWrites.Contains($path)) { $script:pendingWrites[$path] } else { [System.IO.File]::ReadAllText($path, $utf8Bom) } $text = $text.Replace("", "$itemsXml") } else { - $extDir = Split-Path $path - if (-not (Test-Path $extDir)) { New-Item -ItemType Directory -Path $extDir -Force | Out-Null } $hdr = "`r`n`r`n" $text = "$hdr$itemsXml`r`n" } # Создаваемый файл — по канону: без перевода строки в конце. - [System.IO.File]::WriteAllText($path, $text.TrimEnd("`r", "`n"), $utf8Bom) + $script:pendingWrites[$path] = $text.TrimEnd("`r", "`n") $n = @($items).Count Info "Added $n predefined item(s) → $path" $script:addCount += $n @@ -3872,8 +3859,7 @@ $def.PSObject.Properties | ForEach-Object { if ($prop.Name -eq "_complex") { return } $opKey = Resolve-OperationKey $prop.Name if (-not $opKey) { - Warn "Unknown operation: $($prop.Name)" - return + Die "Unknown operation: $($prop.Name)" } switch ($opKey) { @@ -3925,6 +3911,12 @@ $text = ($text -replace "`r`n", "`n") -replace "`n", $targetEol Info "Saved: $resolvedPath" +foreach ($pw in $script:pendingWrites.GetEnumerator()) { + $pwDir = Split-Path $pw.Key + if (-not (Test-Path $pwDir)) { New-Item -ItemType Directory -Path $pwDir -Force | Out-Null } + [System.IO.File]::WriteAllText($pw.Key, $pw.Value, $utf8Bom) +} + # ============================================================ # Section 15: Auto-validate # ============================================================ diff --git a/.claude/skills/meta-edit/scripts/meta-edit.py b/.claude/skills/meta-edit/scripts/meta-edit.py index 5863d974b..661abe5eb 100644 --- a/.claude/skills/meta-edit/scripts/meta-edit.py +++ b/.claude/skills/meta-edit/scripts/meta-edit.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# meta-edit v1.55 — Edit existing 1C metadata object XML +# meta-edit v1.56 — Edit existing 1C metadata object XML # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -376,6 +376,15 @@ def die(msg): sys.exit(1) +# Побочные файлы (таблица внешнего источника, модуль команды, предопределённые) пишутся после +# основного XML: отказ посреди определения не оставляет на диске ни одного изменения. +pending_writes = {} + + +def exists_pending_or_file(path): + return path in pending_writes or os.path.exists(path) + + def localname(el): return etree.QName(el.tag).localname @@ -2087,8 +2096,7 @@ def convert_inline_to_definition(operation, value): for item in items: dot_idx = item.find(".") if dot_idx <= 0: - warn(f"Invalid ts-attribute format (expected TSName.AttrDef): {item}") - continue + die(f"Invalid ts-attribute format (expected TSName.AttrDef): {item}") ts_name = item[:dot_idx].strip() rest = item[dot_idx + 1:].strip() if ts_name not in ts_groups: @@ -2109,8 +2117,7 @@ def convert_inline_to_definition(operation, value): for elem_def in ts_groups[ts_name]: colon_idx = elem_def.find(":") if colon_idx <= 0: - warn(f"Invalid modify format (expected Name: key=val): {elem_def}") - continue + die(f"Invalid modify format (expected Name: key=val): {elem_def}") elem_name = elem_def[:colon_idx].strip() changes_part = elem_def[colon_idx + 1:].strip() changes_obj = {} @@ -2198,7 +2205,7 @@ def convert_inline_to_definition(operation, value): v = kv[eq_idx + 1:].strip() props_obj[k] = v else: - warn(f"Invalid property format (expected Key=Value): {kv}") + die(f"Invalid property format (expected Key=Value): {kv}") definition["modify"] = {"properties": props_obj} else: # "ElementName: key=val, key=val ;; Element2: key=val" @@ -2207,8 +2214,7 @@ def convert_inline_to_definition(operation, value): for elem_def in elem_defs: colon_idx = elem_def.find(":") if colon_idx <= 0: - warn(f"Invalid modify format (expected Name: key=val): {elem_def}") - continue + die(f"Invalid modify format (expected Name: key=val): {elem_def}") elem_name = elem_def[:colon_idx].strip() changes_part = elem_def[colon_idx + 1:].strip() changes_obj = {} @@ -2293,21 +2299,18 @@ def process_add(add_def): child_type = resolve_child_type_key(raw_key) if not child_type: - warn(f"Unknown add child type: {raw_key}") - continue + die(f"Unknown add child type: {raw_key}") # Validate allowed. Проверяем НАЛИЧИЕ ключа, а не истинность списка: пустой список # (объект без допустимых детей) трактовался как «ограничений нет», и чужой ребёнок # молча записывался в объект. if obj_type in valid_child_types: if child_type not in valid_child_types[obj_type]: - warn(f"{child_type} not allowed for {obj_type}, skipping") - continue + die(f"{child_type} not allowed for {obj_type}") xml_tag = child_type_to_xml_tag.get(child_type) if not xml_tag: - warn(f"No XML tag mapping for {child_type}") - continue + die(f"No XML tag mapping for {child_type}") ensure_child_objects_open() indent = get_child_indent(child_objects_el) @@ -2344,13 +2347,12 @@ def process_add(add_def): bad_key = next((k for k in ('characteristics', 'defaultObjectForm', 'defaultRecordForm', 'defaultListForm', 'defaultChoiceForm') if tv.get(k)), None) if bad_key: - warn(f"Ключ '{bad_key}' не поддержан при добавлении таблицы: форму назначает навык form-add, характеристики — навык meta-compile. Таблица '{tbl_name}' пропущена.") - continue + die(f"Ключ '{bad_key}' не поддержан при добавлении таблицы: форму назначает навык form-add, характеристики — навык meta-compile.") if tbl_name in existing_names: warn(f"Table '{tbl_name}' already exists, skipping") continue table_path = os.path.join(tables_dir, f"{tbl_name}.xml") - if os.path.exists(table_path): + if exists_pending_or_file(table_path): warn(f"\u0424\u0430\u0439\u043b \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442: {table_path} \u2014 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u044e") continue field_parts = [] @@ -2358,9 +2360,7 @@ def process_add(add_def): field_parts.append(build_attribute_fragment(parse_attribute_shorthand(f), "eds-field", "\t\t\t", "Field")) fields_xml = "\r\n".join(field_parts) table_xml = build_eds_table_xml(obj_name, tbl_name, entry, fields_xml, '', '') - os.makedirs(tables_dir, exist_ok=True) - with open(table_path, "w", encoding="utf-8-sig", newline="") as fh: - fh.write(table_xml.rstrip("\r\n")) + pending_writes[table_path] = table_xml.rstrip("\r\n") nodes = import_fragment(f"{indent}{esc_xml_text(tbl_name)}
") ref_node = find_insertion_point("Table", {"name": tbl_name}) for node in nodes: @@ -2498,10 +2498,10 @@ def process_add(add_def): # эту ответственность здесь нельзя: получится висячая регистрация без файла. skill_name = "form-add" if child_type == "forms" else "template-add" what_name = "Форму" if child_type == "forms" else "Макет" - warn( + die( f"{what_name} добавляет навык {skill_name} " "(он создаёт и файл, и запись в ChildObjects). " - "meta-edit этого не делает — операция пропущена." + "meta-edit этого не делает." ) elif child_type == "commands": @@ -2517,9 +2517,7 @@ def process_add(add_def): # он есть у всех команд без исключения. Пишем ту же заготовку, что и meta-compile. cmd_ext_dir = os.path.join(os.path.dirname(resolved_path), obj_name, "Commands", item_name, "Ext") cmd_mod_path = os.path.join(cmd_ext_dir, "CommandModule.bsl") - os.makedirs(cmd_ext_dir, exist_ok=True) - with open(cmd_mod_path, "w", encoding="utf-8-sig", newline="") as fh: - fh.write("&НаКлиенте\r\nПроцедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)\r\n\r\n\t// Вставьте обработчик команды.\r\n\r\nКонецПроцедуры\r\n") + pending_writes[cmd_mod_path] = ("&НаКлиенте\r\nПроцедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)\r\n\r\n\t// Вставьте обработчик команды.\r\n\r\nКонецПроцедуры\r\n") fragment_xml = build_command_fragment(item_name, indent) nodes = import_fragment(fragment_xml) ref_node = find_insertion_point("Command", {"after": "", "before": ""}) @@ -2541,21 +2539,18 @@ def process_remove(remove_def): child_type = resolve_child_type_key(raw_key) if not child_type: - warn(f"Unknown remove child type: {raw_key}") - continue + die(f"Unknown remove child type: {raw_key}") if child_type == "properties": - warn("Cannot remove properties -- use modify instead") - continue + die("Cannot remove properties -- use modify instead") if child_type in ("forms", "templates"): # Снять регистрацию мало — надо удалить и файлы; это делают form-remove / template-remove. skill_name = "form-remove" if child_type == "forms" else "template-remove" what_name = "Форму" if child_type == "forms" else "Макет" - warn( + die( f"{what_name} удаляет навык {skill_name} " "(он убирает и файлы, и запись в ChildObjects). " - "meta-edit этого не делает — операция пропущена." + "meta-edit этого не делает." ) - continue xml_tag = child_type_to_xml_tag.get(child_type) if not xml_tag or child_objects_el is None: @@ -2602,8 +2597,7 @@ def modify_properties(props_def): insert_property_in_order(properties_el, new_nodes[0], None, prop_name) prop_el = new_nodes[0] else: - warn(f"Property '{prop_name}': could not create element") - continue + die(f"Property '{prop_name}': could not create element") # Complex property: Owners, RegisterRecords, BasedOn, InputByString if prop_name in complex_property_map: @@ -2659,14 +2653,12 @@ def modify_child_elements(modify_def, child_type): xml_tag = child_type_to_xml_tag.get(child_type) if not xml_tag or child_objects_el is None: - warn(f"No ChildObjects or unknown tag for {child_type}") - return + die(f"No ChildObjects or unknown tag for {child_type}") for elem_name, changes in modify_def.items(): el = find_element_by_name(child_objects_el, xml_tag, elem_name) if el is None: - warn(f"{xml_tag} '{elem_name}' not found for modify") - continue + die(f"{xml_tag} '{elem_name}' not found for modify") # Find Properties inside the element props_el = None @@ -2675,8 +2667,7 @@ def modify_child_elements(modify_def, child_type): props_el = gc break if props_el is None: - warn(f"{xml_tag} '{elem_name}': no Properties element found") - continue + die(f"{xml_tag} '{elem_name}': no Properties element found") for change_prop, change_value in changes.items(): # TS child attribute operations (add/remove/modify attrs inside a TabularSection) @@ -2690,8 +2681,7 @@ def modify_child_elements(modify_def, child_type): if change_prop == "add": if ts_child_obj_el is None: - warn(f"TS '{elem_name}' has no ChildObjects element, cannot add attributes") - continue + die(f"TS '{elem_name}' has no ChildObjects element, cannot add attributes") # Ensure ChildObjects is open (not self-closing empty) has_ts_child_elements = any(True for _ in ts_child_obj_el) if not has_ts_child_elements: @@ -2733,8 +2723,7 @@ def modify_child_elements(modify_def, child_type): elif change_prop == "modify": if ts_child_obj_el is None: - warn(f"TS '{elem_name}' has no ChildObjects, cannot modify attributes") - continue + die(f"TS '{elem_name}' has no ChildObjects, cannot modify attributes") # Temporarily swap childObjectsEl and recurse saved_child_obj_el = child_objects_el child_objects_el = ts_child_obj_el @@ -2932,8 +2921,7 @@ def process_modify(modify_def): child_type = resolve_child_type_key(raw_key) if not child_type: - warn(f"Unknown modify child type: {raw_key}") - continue + die(f"Unknown modify child type: {raw_key}") if child_type == "properties": modify_properties(value) @@ -3553,8 +3541,7 @@ def add_complex_property_item(property_name, values): map_entry = complex_property_map.get(property_name) if not map_entry: - warn(f"Unknown complex property: {property_name}") - return + die(f"Unknown complex property: {property_name}") if map_entry.get("expand"): values = [expand_data_path(str(v)) for v in values] if map_entry.get("mdref"): @@ -3628,8 +3615,7 @@ def set_complex_property(property_name, values): map_entry = complex_property_map.get(property_name) if not map_entry: - warn(f"Unknown complex property: {property_name}") - return + die(f"Unknown complex property: {property_name}") if map_entry.get("expand"): values = [expand_data_path(str(v)) for v in values] if map_entry.get("mdref"): @@ -3824,14 +3810,15 @@ def add_predefined_items(items): path = get_predefined_path() item_list = items if isinstance(items, list) else [items] items_xml = ''.join(build_predef_item_xml('\t', it, code_type) for it in item_list) - if os.path.exists(path): + if path in pending_writes: + text = pending_writes[path].replace('', items_xml + '') + elif os.path.exists(path): # newline='' => без трансляции переводов строк: иначе CRLF молча схлопнется # в LF при чтении и файл будет переписан в LF (#44/#46/#47). with open(path, 'r', encoding='utf-8-sig', newline='') as f: text = f.read() text = text.replace('', items_xml + '') else: - os.makedirs(os.path.dirname(path), exist_ok=True) hdr = ('\r\n' # Без перевода строки в конце — канон #57. Срезаем в ОБЕИХ ветках: файл, созданный # прежней версией навыка, мог унести хвост, а PS-порт срезает безусловно. - text = text.rstrip('\r\n') - with open(path, 'wb') as f: - f.write(b'\xef\xbb\xbf') - f.write(text.encode('utf-8')) + pending_writes[path] = text.rstrip('\r\n') info(f"Added {len(item_list)} predefined item(s) -> {path}") add_count += len(item_list) @@ -3996,8 +3980,7 @@ def main(): continue op_key = resolve_operation_key(prop_name) if not op_key: - warn(f"Unknown operation: {prop_name}") - continue + die(f"Unknown operation: {prop_name}") if op_key == "add": process_add(prop_value) @@ -4010,6 +3993,12 @@ def main(): save_xml(xml_tree, resolved_path) info(f"Saved: {resolved_path}") + for pw_path, pw_text in pending_writes.items(): + os.makedirs(os.path.dirname(pw_path), exist_ok=True) + with open(pw_path, "wb") as fh: + fh.write(b"\xef\xbb\xbf") + fh.write(pw_text.encode("utf-8")) + # --- Auto-validate --- if not args.NoValidate: # Внешняя обработка/отчёт — автономный объект, meta-validate его не знает (#108). diff --git a/tests/skills/cases/meta-edit/eds-add-field.json b/tests/skills/cases/meta-edit/eds-add-field.json index def7e8713..173c26dc5 100644 --- a/tests/skills/cases/meta-edit/eds-add-field.json +++ b/tests/skills/cases/meta-edit/eds-add-field.json @@ -1,5 +1,5 @@ { - "name": "Поле в таблицу внешнего источника; реквизит туда не пускают", + "name": "Поле в таблицу внешнего источника", "setup": "empty-config", "preRun": [ { @@ -9,34 +9,61 @@ "name": "PG", "tables": { "products": { - "keyFields": ["id"], - "fields": ["id: Number(10,0)", "name: String(150)"] + "keyFields": [ + "id" + ], + "fields": [ + "id: Number(10,0)", + "name: String(150)" + ] } } }, - "args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" } + "args": { + "-JsonPath": "{inputFile}", + "-OutputDir": "{workDir}" + } } ], - "params": { "objectPath": "ExternalDataSources/PG/Tables/products.xml" }, + "params": { + "objectPath": "ExternalDataSources/PG/Tables/products.xml" + }, "input": { "add": { "fields": [ "barcode: String(20) | nullable", - { "name": "cost", "type": "Number(15,2)", "nameInDataSource": "cost_net", "readOnly": true } - ], - "attributes": ["Лишний: String(10)"] + { + "name": "cost", + "type": "Number(15,2)", + "nameInDataSource": "cost_net", + "readOnly": true + } + ] } }, "expect": { - "stdoutContains": ["Added field: barcode", "Added field: cost", "attributes not allowed for Table"], + "stdoutContains": [ + "Added field: barcode", + "Added field: cost" + ], "fileContains": [ { "file": "ExternalDataSources/PG/Tables/products.xml", - "text": ["cost_net", "true", "cost_net", + "true", + ""] } + { + "file": "ExternalDataSources/PG/Tables/products.xml", + "text": [ + "" + ] + } ] } } diff --git a/tests/skills/cases/meta-edit/eds-add-table-unsupported-keys.json b/tests/skills/cases/meta-edit/eds-add-table-unsupported-keys.json index d3b55298d..d84c2cf6c 100644 --- a/tests/skills/cases/meta-edit/eds-add-table-unsupported-keys.json +++ b/tests/skills/cases/meta-edit/eds-add-table-unsupported-keys.json @@ -7,27 +7,48 @@ "input": { "type": "ExternalDataSource", "name": "PG", - "tables": { "products": { "keyFields": ["id"], "fields": ["id: Number(10,0)"] } } + "tables": { + "products": { + "keyFields": [ + "id" + ], + "fields": [ + "id: Number(10,0)" + ] + } + } }, - "args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" } + "args": { + "-JsonPath": "{inputFile}", + "-OutputDir": "{workDir}" + } } ], - "params": { "objectPath": "ExternalDataSources/PG.xml" }, + "params": { + "objectPath": "ExternalDataSources/PG.xml" + }, "input": { "add": { "tables": { "sales": { "defaultListForm": "ФормаСписка", - "fields": ["id: Number(10,0)"] + "fields": [ + "id: Number(10,0)" + ] } } } }, "expect": { - "stdoutContains": ["не поддержан при добавлении таблицы", "form-add"], - "filesAbsent": ["ExternalDataSources/PG/Tables/sales.xml"], + "filesAbsent": [ + "ExternalDataSources/PG/Tables/sales.xml" + ], "fileNotContains": [ - { "file": "ExternalDataSources/PG.xml", "text": "sales
" } + { + "file": "ExternalDataSources/PG.xml", + "text": "sales
" + } ] - } + }, + "expectError": "не поддержан при добавлении таблицы" } diff --git a/tests/skills/cases/meta-edit/epf-add-command-denied.json b/tests/skills/cases/meta-edit/epf-add-command-denied.json index b4037b52f..8fb8e14c3 100644 --- a/tests/skills/cases/meta-edit/epf-add-command-denied.json +++ b/tests/skills/cases/meta-edit/epf-add-command-denied.json @@ -5,18 +5,35 @@ "preRun": [ { "script": "epf-init/scripts/init", - "args": { "-Name": "Проба", "-SrcDir": "{workDir}" } + "args": { + "-Name": "Проба", + "-SrcDir": "{workDir}" + } } ], - "params": { "objectPath": "Проба.xml", "objectName": "Проба" }, + "params": { + "objectPath": "Проба.xml", + "objectName": "Проба" + }, "input": { "add": { - "commands": ["ПробнаяКоманда"] + "commands": [ + "ПробнаяКоманда" + ] } }, "expect": { - "stdoutContains": ["commands not allowed for ExternalDataProcessor", "Validation OK"], - "fileNotContains": [{ "file": "Проба.xml", "text": [" - - - - - UUID-002 - UUID-003 - - - UUID-004 - UUID-005 - - - UUID-006 - UUID-007 - - - UUID-008 - UUID-009 - - - UUID-010 - UUID-011 - - - UUID-012 - UUID-013 - - - UUID-014 - UUID-015 - - - - TestConfig - - - ru - TestConfig - - - - - Version8_3_24 - ManagedApplication - - PlatformApplication - - Russian - - - - - false - false - false - - - - - - - - - - - - - - - - - - - - - - Biometrics - true - - - Location - false - - - BackgroundLocation - false - - - BluetoothPrinters - false - - - WiFiPrinters - false - - - Contacts - false - - - Calendars - false - - - PushNotifications - false - - - LocalNotifications - false - - - InAppPurchases - false - - - PersonalComputerFileExchange - false - - - Ads - false - - - NumberDialing - false - - - CallProcessing - false - - - CallLog - false - - - AutoSendSMS - false - - - ReceiveSMS - false - - - SMSLog - false - - - Camera - false - - - Microphone - false - - - MusicLibrary - false - - - PictureAndVideoLibraries - false - - - AudioPlaybackAndVibration - false - - - BackgroundAudioPlaybackAndVibration - false - - - InstallPackages - false - - - OSBackup - true - - - ApplicationUsageStatistics - false - - - BarcodeScanning - false - - - BackgroundAudioRecording - false - - - AllFilesAccess - false - - - Videoconferences - false - - - NFC - false - - - DocumentScanning - false - - - SpeechToText - false - - - Geofences - false - - - IncomingShareRequests - false - - - AllIncomingShareRequestsTypesProcessing - false - - - - - - Normal - - - Language.Русский - - - - - - Managed - NotAutoFree - DontUse - DontUse - TaxiEnableVersion8_2 - DontUse - Version8_3_24 - - - - Русский - PG - - - \ No newline at end of file diff --git a/tests/skills/cases/meta-edit/snapshots/eds-add-table-unsupported-keys/Ext/ClientApplicationInterface.xml b/tests/skills/cases/meta-edit/snapshots/eds-add-table-unsupported-keys/Ext/ClientApplicationInterface.xml deleted file mode 100644 index 3c1161b2d..000000000 --- a/tests/skills/cases/meta-edit/snapshots/eds-add-table-unsupported-keys/Ext/ClientApplicationInterface.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - UUID-002 - - - - - UUID-004 - - - - - - - - \ No newline at end of file diff --git a/tests/skills/cases/meta-edit/snapshots/eds-add-table-unsupported-keys/ExternalDataSources/PG.xml b/tests/skills/cases/meta-edit/snapshots/eds-add-table-unsupported-keys/ExternalDataSources/PG.xml deleted file mode 100644 index a1b89619b..000000000 --- a/tests/skills/cases/meta-edit/snapshots/eds-add-table-unsupported-keys/ExternalDataSources/PG.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - UUID-002 - UUID-003 - - - UUID-004 - UUID-005 - - - UUID-006 - UUID-007 - - - - PG - - - ru - PG - - - - Automatic - - - products
-
-
-
\ No newline at end of file diff --git a/tests/skills/cases/meta-edit/snapshots/eds-add-table-unsupported-keys/ExternalDataSources/PG/Tables/products.xml b/tests/skills/cases/meta-edit/snapshots/eds-add-table-unsupported-keys/ExternalDataSources/PG/Tables/products.xml deleted file mode 100644 index 205ef9363..000000000 --- a/tests/skills/cases/meta-edit/snapshots/eds-add-table-unsupported-keys/ExternalDataSources/PG/Tables/products.xml +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - UUID-002 - UUID-003 - - - UUID-004 - UUID-005 - - - UUID-006 - UUID-007 - - - UUID-008 - UUID-009 - - - UUID-010 - UUID-011 - - - UUID-012 - UUID-013 - - - UUID-014 - UUID-015 - - - UUID-016 - UUID-017 - - - - products - - - ru - products - - - - Table - products - - NonobjectData - - ExternalDataSource.PG.Table.products.Field.id - - - - - - true - false - - Auto - Begin - Directly - Auto - - - - - - - - - - - - false - false - Auto - - InDialog - - - Automatic - - - - - id - - - ru - id - - - - - xs:decimal - - 10 - 0 - Any - - - false - - - - false - - false - false - - - false - 0 - DontCheck - - - Auto - Auto - Auto - - id - false - false - - - -
-
\ No newline at end of file diff --git a/tests/skills/cases/meta-edit/snapshots/eds-add-table-unsupported-keys/Languages/Русский.xml b/tests/skills/cases/meta-edit/snapshots/eds-add-table-unsupported-keys/Languages/Русский.xml deleted file mode 100644 index 37c60d786..000000000 --- a/tests/skills/cases/meta-edit/snapshots/eds-add-table-unsupported-keys/Languages/Русский.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Русский - - - ru - Русский - - - - ru - - - \ No newline at end of file diff --git a/tests/skills/cases/meta-edit/snapshots/epf-add-command-denied/Проба.xml b/tests/skills/cases/meta-edit/snapshots/epf-add-command-denied/Проба.xml deleted file mode 100644 index c4bbfbd2e..000000000 --- a/tests/skills/cases/meta-edit/snapshots/epf-add-command-denied/Проба.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - UUID-002 - UUID-003 - - - UUID-004 - UUID-005 - - - - Проба - - - ru - Проба - - - - - - - - - \ No newline at end of file diff --git a/tests/skills/cases/meta-edit/snapshots/epf-add-command-denied/Проба/Ext/ObjectModule.bsl b/tests/skills/cases/meta-edit/snapshots/epf-add-command-denied/Проба/Ext/ObjectModule.bsl deleted file mode 100644 index 15543d277..000000000 --- a/tests/skills/cases/meta-edit/snapshots/epf-add-command-denied/Проба/Ext/ObjectModule.bsl +++ /dev/null @@ -1,11 +0,0 @@ -#Область ОписаниеПеременных - -#КонецОбласти - -#Область ПрограммныйИнтерфейс - -#КонецОбласти - -#Область СлужебныеПроцедурыИФункции - -#КонецОбласти \ No newline at end of file diff --git a/tests/skills/cases/meta-edit/snapshots/forms-templates-delegated/Catalogs/Контрагенты.xml b/tests/skills/cases/meta-edit/snapshots/forms-templates-delegated/Catalogs/Контрагенты.xml deleted file mode 100644 index 5e5efc449..000000000 --- a/tests/skills/cases/meta-edit/snapshots/forms-templates-delegated/Catalogs/Контрагенты.xml +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - UUID-002 - UUID-003 - - - UUID-004 - UUID-005 - - - UUID-006 - UUID-007 - - - UUID-008 - UUID-009 - - - UUID-010 - UUID-011 - - - - Контрагенты - - - ru - Контрагенты - - - - false - HierarchyFoldersAndItems - false - 2 - true - true - - ToItems - 9 - 25 - String - Variable - WholeCatalog - false - true - AsDescription - - Auto - InDialog - false - BothWays - - Catalog.Контрагенты.StandardAttribute.Description - Catalog.Контрагенты.StandardAttribute.Code - - Begin - DontUse - Directly - - - - - - - - - - - false - - - Managed - Use - - - - - - Use - Auto - DontUse - false - false - - - - \ No newline at end of file diff --git a/tests/skills/cases/meta-edit/snapshots/forms-templates-delegated/Catalogs/Контрагенты/Ext/ObjectModule.bsl b/tests/skills/cases/meta-edit/snapshots/forms-templates-delegated/Catalogs/Контрагенты/Ext/ObjectModule.bsl deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/skills/cases/meta-edit/snapshots/forms-templates-delegated/Configuration.xml b/tests/skills/cases/meta-edit/snapshots/forms-templates-delegated/Configuration.xml deleted file mode 100644 index 7dd7b758d..000000000 --- a/tests/skills/cases/meta-edit/snapshots/forms-templates-delegated/Configuration.xml +++ /dev/null @@ -1,252 +0,0 @@ - - - - - - UUID-002 - UUID-003 - - - UUID-004 - UUID-005 - - - UUID-006 - UUID-007 - - - UUID-008 - UUID-009 - - - UUID-010 - UUID-011 - - - UUID-012 - UUID-013 - - - UUID-014 - UUID-015 - - - - TestConfig - - - ru - TestConfig - - - - - Version8_3_24 - ManagedApplication - - PlatformApplication - - Russian - - - - - false - false - false - - - - - - - - - - - - - - - - - - - - - - Biometrics - true - - - Location - false - - - BackgroundLocation - false - - - BluetoothPrinters - false - - - WiFiPrinters - false - - - Contacts - false - - - Calendars - false - - - PushNotifications - false - - - LocalNotifications - false - - - InAppPurchases - false - - - PersonalComputerFileExchange - false - - - Ads - false - - - NumberDialing - false - - - CallProcessing - false - - - CallLog - false - - - AutoSendSMS - false - - - ReceiveSMS - false - - - SMSLog - false - - - Camera - false - - - Microphone - false - - - MusicLibrary - false - - - PictureAndVideoLibraries - false - - - AudioPlaybackAndVibration - false - - - BackgroundAudioPlaybackAndVibration - false - - - InstallPackages - false - - - OSBackup - true - - - ApplicationUsageStatistics - false - - - BarcodeScanning - false - - - BackgroundAudioRecording - false - - - AllFilesAccess - false - - - Videoconferences - false - - - NFC - false - - - DocumentScanning - false - - - SpeechToText - false - - - Geofences - false - - - IncomingShareRequests - false - - - AllIncomingShareRequestsTypesProcessing - false - - - - - - Normal - - - Language.Русский - - - - - - Managed - NotAutoFree - DontUse - DontUse - TaxiEnableVersion8_2 - DontUse - Version8_3_24 - - - - Русский - Контрагенты - - - \ No newline at end of file diff --git a/tests/skills/cases/meta-edit/snapshots/forms-templates-delegated/Ext/ClientApplicationInterface.xml b/tests/skills/cases/meta-edit/snapshots/forms-templates-delegated/Ext/ClientApplicationInterface.xml deleted file mode 100644 index 3c1161b2d..000000000 --- a/tests/skills/cases/meta-edit/snapshots/forms-templates-delegated/Ext/ClientApplicationInterface.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - UUID-002 - - - - - UUID-004 - - - - - - - - \ No newline at end of file diff --git a/tests/skills/cases/meta-edit/snapshots/forms-templates-delegated/Languages/Русский.xml b/tests/skills/cases/meta-edit/snapshots/forms-templates-delegated/Languages/Русский.xml deleted file mode 100644 index 37c60d786..000000000 --- a/tests/skills/cases/meta-edit/snapshots/forms-templates-delegated/Languages/Русский.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Русский - - - ru - Русский - - - - ru - - - \ No newline at end of file