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
"
$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}")
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": "" }
+ {
+ "file": "ExternalDataSources/PG.xml",
+ "text": ""
+ }
]
- }
+ },
+ "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
-
-
-
-
-
-
\ 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