refactor(эмиттеры): копии канон-writer'а приведены к одному виду

Навыки автономны, общей библиотеки нет — значит копии функции записи должны быть
буквально одинаковыми, иначе теряется смысл копипасты. PS-порт дисциплину держал
(7 копий байт-в-байт), а py разъехались: три варианта имени параметра, два стиля
кавычек, у cfe-borrow ещё и другое имя функции (save_xml_file).

Сведено к одной форме: имя write_xml_file, параметр content, одинаковое тело.
Различаются ровно две вещи, и обе осмысленные: вызов локального базового writer'а
(имена исторические — write_utf8_bom / write_text_with_bom / save_text_bom) и одна
строка примечания там, где у навыка есть исключение (модуль .bsl, HTML).

Текст комментария в PS выровнен с docstring'ом py: раньше он описывал ПРИЧИНУ
(here-string наследует LF), а не правило, и после правки py ports разъехались по
смыслу. Синтаксис остаётся разным по природе языков: в py docstring (в портах их
421 против 73 функций с #-комментарием — это местная норма), в PS — #.

Windows 641/641 обоими портами.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-05 19:22:43 +03:00
co-authored by Claude Opus 5
parent d05c91be03
commit 67185657c1
15 changed files with 102 additions and 74 deletions
+4 -4
View File
@@ -242,10 +242,10 @@ if (-not (Test-Path $extDir)) {
# --- Write files with UTF-8 BOM ---
$enc = New-Object System.Text.UTF8Encoding($true)
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается here-string'ами, а .ps1 хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через эту функцию НЕ пишутся.
# XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
#
# Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
# копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
function Write-XmlFile([string]$path, [string]$text, $encoding) {
$t = ($text -replace "`r`n", "`n") -replace "`n", "`r`n"
[System.IO.File]::WriteAllText($path, $t.TrimEnd("`r", "`n"), $encoding)
+7 -4
View File
@@ -15,12 +15,15 @@ def write_utf8_bom(path, content):
f.write(content)
def write_xml_file(path, content):
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается тройными кавычками, а .py хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через неё НЕ пишутся.
"""XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
"""
text = content.replace('\r\n', '\n').replace('\n', '\r\n').rstrip('\r\n')
write_utf8_bom(path, text)
def main():
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
+18 -15
View File
@@ -430,13 +430,16 @@ def save_text_bom(path, text):
fh.write(text)
def save_xml_file(path, text):
"""XML в каноне выгрузки Конфигуратора: CRLF, без перевода строки в конце.
def write_xml_file(path, content):
"""XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
Для файлов, которые мы СОЗДАЁМ. Правило: создаём — пишем канон, правим
существующий — наследуем его стиль (см. save_text_bom).
Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
Только для файлов, которые СОЗДАЁМ: правка существующего наследует его стиль.
"""
save_text_bom(path, text.replace("\r\n", "\n").replace("\n", "\r\n").rstrip("\r\n"))
text = content.replace('\r\n', '\n').replace('\n', '\r\n').rstrip('\r\n')
save_text_bom(path, text)
def new_guid():
@@ -1153,7 +1156,7 @@ def main():
target_dir = os.path.join(ext_dir, CHILD_TYPE_DIR_MAP[rt["TypeName"]])
os.makedirs(target_dir, exist_ok=True)
target_file = os.path.join(target_dir, f"{rt['ObjName']}.xml")
save_xml_file(target_file, borrowed_xml)
write_xml_file(target_file, borrowed_xml)
add_to_child_objects(rt["TypeName"], rt["ObjName"])
borrowed_files.append(target_file)
info(f" Auto-borrowed: {rt['TypeName']}.{rt['ObjName']}")
@@ -1218,7 +1221,7 @@ def main():
t_target_dir = os.path.join(ext_dir, CHILD_TYPE_DIR_MAP[target_type_name])
os.makedirs(t_target_dir, exist_ok=True)
t_target_file = os.path.join(t_target_dir, f"{target_obj_name}.xml")
save_xml_file(t_target_file, t_borrowed_xml)
write_xml_file(t_target_file, t_borrowed_xml)
add_to_child_objects(target_type_name, target_obj_name)
borrowed_files.append(t_target_file)
info(f" Auto-borrowed for deep path: {target_type_name}.{target_obj_name}")
@@ -1242,7 +1245,7 @@ def main():
s_target_dir = os.path.join(ext_dir, CHILD_TYPE_DIR_MAP[srt["TypeName"]])
os.makedirs(s_target_dir, exist_ok=True)
s_target_file = os.path.join(s_target_dir, f"{srt['ObjName']}.xml")
save_xml_file(s_target_file, s_borrowed_xml)
write_xml_file(s_target_file, s_borrowed_xml)
add_to_child_objects(srt["TypeName"], srt["ObjName"])
borrowed_files.append(s_target_file)
info(f" Auto-borrowed (deep): {srt['TypeName']}.{srt['ObjName']}")
@@ -1299,7 +1302,7 @@ def main():
os.makedirs(form_meta_dir, exist_ok=True)
form_meta_file = os.path.join(form_meta_dir, f"{form_name}.xml")
save_xml_file(form_meta_file, "\n".join(form_meta_lines))
write_xml_file(form_meta_file, "\n".join(form_meta_lines))
info(f" Created: {form_meta_file}")
# 5. Generate Form.xml with BaseForm
@@ -1385,7 +1388,7 @@ def main():
target_dir = os.path.join(ext_dir, "CommonPictures")
os.makedirs(target_dir, exist_ok=True)
target_file = os.path.join(target_dir, f"{pic_name}.xml")
save_xml_file(target_file, borrowed_xml)
write_xml_file(target_file, borrowed_xml)
add_to_child_objects("CommonPicture", pic_name)
auto_borrowed_pics.append(pic_name)
borrowed_files.append(target_file)
@@ -1434,7 +1437,7 @@ def main():
target_dir = os.path.join(ext_dir, "StyleItems")
os.makedirs(target_dir, exist_ok=True)
target_file = os.path.join(target_dir, f"{style_name}.xml")
save_xml_file(target_file, borrowed_xml)
write_xml_file(target_file, borrowed_xml)
add_to_child_objects("StyleItem", style_name)
borrowed_files.append(target_file)
info(f" Auto-borrowed: StyleItem.{style_name}")
@@ -1498,7 +1501,7 @@ def main():
target_dir = os.path.join(ext_dir, "Enums")
os.makedirs(target_dir, exist_ok=True)
target_file = os.path.join(target_dir, f"{enum_name}.xml")
save_xml_file(target_file, borrowed_xml)
write_xml_file(target_file, borrowed_xml)
add_to_child_objects("Enum", enum_name)
borrowed_files.append(target_file)
info(f" Auto-borrowed: Enum.{enum_name} (with {len(ev_xmls)} EnumValue(s))")
@@ -1592,7 +1595,7 @@ def main():
form_xml_dir = os.path.join(form_meta_dir, form_name, "Ext")
os.makedirs(form_xml_dir, exist_ok=True)
form_xml_file = os.path.join(form_xml_dir, "Form.xml")
save_xml_file(form_xml_file, "".join(parts))
write_xml_file(form_xml_file, "".join(parts))
info(f" Created: {form_xml_file}")
# 6. Create empty Module.bsl — but NEVER overwrite an existing one (re-borrow must
@@ -1676,7 +1679,7 @@ def main():
target_dir = os.path.join(ext_dir, dir_name)
os.makedirs(target_dir, exist_ok=True)
target_file = os.path.join(target_dir, f"{obj_name}.xml")
save_xml_file(target_file, borrowed_xml)
write_xml_file(target_file, borrowed_xml)
info(f" Created: {target_file}")
add_to_child_objects(type_name, obj_name)
@@ -1703,7 +1706,7 @@ def main():
os.makedirs(target_dir, exist_ok=True)
target_file = os.path.join(target_dir, f"{obj_name}.xml")
save_xml_file(target_file, borrowed_xml)
write_xml_file(target_file, borrowed_xml)
info(f" Created: {target_file}")
add_to_child_objects(type_name, obj_name)
+4 -4
View File
@@ -255,10 +255,10 @@ if (-not (Test-Path $langDir)) {
# --- Write files with UTF-8 BOM ---
$enc = New-Object System.Text.UTF8Encoding($true)
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается here-string'ами, а .ps1 хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через эту функцию НЕ пишутся.
# XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
#
# Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
# копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
function Write-XmlFile([string]$path, [string]$text, $encoding) {
$t = ($text -replace "`r`n", "`n") -replace "`n", "`r`n"
[System.IO.File]::WriteAllText($path, $t.TrimEnd("`r", "`n"), $encoding)
+5 -4
View File
@@ -16,10 +16,11 @@ def write_utf8_bom(path, content):
f.write(content)
def write_xml_file(path, content):
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается тройными кавычками, а .py хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через неё НЕ пишутся.
"""XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
"""
text = content.replace('\r\n', '\n').replace('\n', '\r\n').rstrip('\r\n')
write_utf8_bom(path, text)
+4 -4
View File
@@ -64,10 +64,10 @@ $extDir = Join-Path $processorDir "Ext"
New-Item -ItemType Directory -Path $extDir -Force | Out-Null
$enc = New-Object System.Text.UTF8Encoding($true)
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается here-string'ами, а .ps1 хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через эту функцию НЕ пишутся.
# XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
#
# Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
# копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
function Write-XmlFile([string]$path, [string]$text, $encoding) {
$t = ($text -replace "`r`n", "`n") -replace "`n", "`r`n"
[System.IO.File]::WriteAllText($path, $t.TrimEnd("`r", "`n"), $encoding)
+7 -4
View File
@@ -15,12 +15,15 @@ def write_utf8_bom(path, content):
f.write(content)
def write_xml_file(path, content):
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается тройными кавычками, а .py хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через неё НЕ пишутся.
"""XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
"""
text = content.replace('\r\n', '\n').replace('\n', '\r\n').rstrip('\r\n')
write_utf8_bom(path, text)
def main():
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
+4 -4
View File
@@ -98,10 +98,10 @@ $extDir = Join-Path $reportDir "Ext"
New-Item -ItemType Directory -Path $extDir -Force | Out-Null
$enc = New-Object System.Text.UTF8Encoding($true)
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается here-string'ами, а .ps1 хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через эту функцию НЕ пишутся.
# XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
#
# Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
# копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
function Write-XmlFile([string]$path, [string]$text, $encoding) {
$t = ($text -replace "`r`n", "`n") -replace "`n", "`r`n"
[System.IO.File]::WriteAllText($path, $t.TrimEnd("`r", "`n"), $encoding)
+7 -4
View File
@@ -15,12 +15,15 @@ def write_utf8_bom(path, content):
f.write(content)
def write_xml_file(path, content):
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается тройными кавычками, а .py хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через неё НЕ пишутся.
"""XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
"""
text = content.replace('\r\n', '\n').replace('\n', '\r\n').rstrip('\r\n')
write_utf8_bom(path, text)
def main():
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
+6 -4
View File
@@ -337,10 +337,12 @@ $formMetaXml = @"
</MetaDataObject>
"@
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается here-string'ами, а .ps1 хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через эту функцию НЕ пишутся.
# XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
#
# Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
# копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
#
# Модуль .bsl сюда НЕ идёт — он пишется отдельно.
function Write-XmlFile([string]$path, [string]$text, $encoding) {
$t = ($text -replace "`r`n", "`n") -replace "`n", "`r`n"
[System.IO.File]::WriteAllText($path, $t.TrimEnd("`r", "`n"), $encoding)
+8 -6
View File
@@ -265,14 +265,16 @@ def write_text_with_bom(path, text):
f.write(text)
def write_xml_file(path, text):
"""XML в каноне выгрузки Конфигуратора: CRLF, без перевода строки в конце.
def write_xml_file(path, content):
"""XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
Скелет формы собирается литералами с \n. Сплошная нормализация здесь
безопасна: многострочных текстовых узлов в скелете нет. Модуль .bsl
пишется отдельно, через write_text_with_bom.
Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
Модуль .bsl сюда НЕ идёт — он пишется отдельно.
"""
write_text_with_bom(path, text.replace("\r\n", "\n").replace("\n", "\r\n").rstrip("\r\n"))
text = content.replace('\r\n', '\n').replace('\n', '\r\n').rstrip('\r\n')
write_text_with_bom(path, text)
def main():
+6 -4
View File
@@ -195,10 +195,12 @@ $helpXml = @"
</Help>
"@
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается here-string'ами, а .ps1 хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через эту функцию НЕ пишутся.
# XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
#
# Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
# копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
#
# HTML-страница сюда НЕ идёт — платформа хранит её с LF.
function Write-XmlFile([string]$path, [string]$text, $encoding) {
$t = ($text -replace "`r`n", "`n") -replace "`n", "`r`n"
[System.IO.File]::WriteAllText($path, $t.TrimEnd("`r", "`n"), $encoding)
+8 -4
View File
@@ -263,12 +263,16 @@ def write_text_with_bom(path, text):
f.write(text)
def write_xml_file(path, text):
"""XML в каноне выгрузки Конфигуратора: CRLF, без перевода строки в конце.
def write_xml_file(path, content):
"""XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
Для Help.xml. HTML-страница сюда НЕ идёт — у неё свой канон (LF).
Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
HTML-страница сюда НЕ идёт — платформа хранит её с LF.
"""
write_text_with_bom(path, text.replace("\r\n", "\n").replace("\n", "\r\n").rstrip("\r\n"))
text = content.replace('\r\n', '\n').replace('\n', '\r\n').rstrip('\r\n')
write_text_with_bom(path, text)
def main():
@@ -260,10 +260,12 @@ $templateMetaXml = @"
</MetaDataObject>
"@
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается here-string'ами, а .ps1 хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через эту функцию НЕ пишутся.
# XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
#
# Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
# копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
#
# HTML-макет сюда НЕ идёт — платформа хранит его с LF.
function Write-XmlFile([string]$path, [string]$text, $encoding) {
$t = ($text -replace "`r`n", "`n") -replace "`n", "`r`n"
[System.IO.File]::WriteAllText($path, $t.TrimEnd("`r", "`n"), $encoding)
@@ -256,13 +256,16 @@ def write_text_with_bom(path, text):
f.write(text)
def write_xml_file(path, text):
"""XML в каноне выгрузки Конфигуратора: CRLF, без перевода строки в конце.
def write_xml_file(path, content):
"""XML в каноне выгрузки Конфигуратора: CRLF в разделителях, без перевода в конце.
Для XML-макетов (SpreadsheetDocument, DataCompositionSchema) и Template.xml.
HTML-макет сюда НЕ идёт — у него свой канон (LF).
Копия этой функции есть в каждом навыке-эмиттере (навыки автономны). Держать
копии одинаковыми — сознательно: разошедшиеся копии сводят на нет весь смысл.
HTML-макет сюда НЕ идёт — платформа хранит его с LF.
"""
write_text_with_bom(path, text.replace("\r\n", "\n").replace("\n", "\r\n").rstrip("\r\n"))
text = content.replace('\r\n', '\n').replace('\n', '\r\n').rstrip('\r\n')
write_text_with_bom(path, text)
def detect_format_version(d):