fix(17 навыков): CRLF в разделителях строк XML (#57)

Головной дефект тикета: cfe-init выдавал Configuration.xml с 10 CR на 70 строк, а
роль и язык — вовсе без CR. Теперь 70/70, последний байт `>`.

Правило: меняем только РАЗДЕЛИТЕЛИ строк, содержимое текстовых узлов не трогаем.
Платформа его не трогает тоже — в запросах СКД из чистой выгрузки встречаются и
CRLF, и одиночный LF. Поэтому сплошная нормализация файла применяется только там,
где многострочных текстовых узлов нет по построению (скелеты), а в объектном XML
и формах разделители правятся в точке сборки.

Источников оказалось четыре, а не один:

1. Скелеты (cf-init, cfe-init, epf-init, erf-init, form-add, help-add,
   template-add) собираются here-string'ами, а .ps1/.py в репозитории хранятся с
   LF — отсюда LF и смешанный EOL.
2. py-порты склеивали документ через '\n'.join(lines); PS в тех же местах давал
   CRLF через AppendLine — порты расходились побайтово.
3. Билдеры Predefined в meta-compile собирались с явным LF и даже сворачивали
   CRLF→LF из общего эмиттера типов.
4. Чтение существующего файла в python БЕЗ newline='' молча схлопывает CRLF в LF
   (универсальные переводы строк), и запись потом кладёт LF. Так role-compile и
   subsystem-compile переписывали в LF весь Configuration.xml. meta-compile это
   уже делал правильно — там newline='' стоял с фикса #44/#46/#47.

Отдельно: XML-парсер по спецификации схлопывает CRLF при разборе, поэтому
lxml-порты (xdto-compile, xdto-edit) отдавали LF-документ там, где .NET возвращал
CRLF через NewLineHandling. Восстанавливаем EOL исходного файла.

Разделение канона и сохранения стиля:

- файл СОЗДАЁМ — канон (CRLF, без хвоста);
- существующий ПРАВИМ — наследуем его EOL, включая перевод строки вставки
  (контракт #44/#46/#47). Иначе LF-проект получал бы смешанные файлы — ровно то,
  на что заведён #57. Кейсы roundtrip-crlf-preserve остаются зелёными.

Хелпер записи скопирован в каждый навык (навыки автономны). У meta-compile он
называется Write-XmlFileKeepEol / write_xml_file_keep_eol: там нормализовать EOL
НЕЛЬЗЯ (многострочные запрос, синоним, значение заполнения), и одинаковое имя при
разном поведении было бы ловушкой.

Заодно: имя временного файла батча в meta-compile.ps1 получило GUID. Фиксированное
"meta-compile-batch-$idx.json" в общем %TEMP% сталкивало два параллельных запуска
(«file is being used by another process») — из-за этого полный набор приходилось
гонять с урезанной параллельностью. py-порт уже брал mkstemp.

Аудит: было ~250 файлов с дефектом EOL, стало 0 в обоих портах. Осталось четыре
законных случая — фикстуры roundtrip-crlf-preserve (сохранение стиля) и запрос
динсписка с многострочным текстом. Дрейф снэпшотов — 1 файл. Тесты 641/641.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-05 15:37:26 +03:00
co-authored by Claude Opus 5
parent abe9431d8f
commit 0b8231c467
37 changed files with 289 additions and 117 deletions
+12 -3
View File
@@ -1,4 +1,4 @@
# cf-edit v1.12 — Edit 1C configuration root (Configuration.xml)
# cf-edit v1.14 — Edit 1C configuration root (Configuration.xml)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)][Alias('Path')][string]$ConfigPath,
@@ -163,6 +163,11 @@ function Assert-EditAllowed([string]$targetPath, [string]$require) {
Assert-EditAllowed $resolvedPath 'editable'
# --- Load XML with PreserveWhitespace ---
# EOL исходного файла запоминаем ДО разбора: парсер XML по спецификации схлопывает
# CRLF в LF, а вставки ниже собираются с явным CRLF — без восстановления в точке
# записи LF-файл стал бы смешанным. Канон CRLF относится к файлам, которые мы
# СОЗДАЁМ; правка существующего сохраняет его стиль (#44/#46/#47).
$script:srcEol = if (([System.IO.File]::ReadAllText($resolvedPath)) -match "`r`n") { "`r`n" } else { "`n" }
$script:xmlDoc = New-Object System.Xml.XmlDocument
$script:xmlDoc.PreserveWhitespace = $true
$script:xmlDoc.Load($resolvedPath)
@@ -691,7 +696,8 @@ $bodyBlock$declarations
if (-not (Test-Path $extDir)) { New-Item -ItemType Directory -Path $extDir -Force | Out-Null }
$caiPath = Join-Path $extDir "ClientApplicationInterface.xml"
$utf8Bom = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText($caiPath, $caiXml, $utf8Bom)
# Файл создаём мы — канон: CRLF в разделителях, без перевода строки в конце.
[System.IO.File]::WriteAllText($caiPath, (($caiXml -replace "`r`n", "`n") -replace "`n", "`r`n").TrimEnd("`r", "`n"), $utf8Bom)
$script:modifyCount++
Info "Wrote panel layout: $caiPath"
}
@@ -880,7 +886,8 @@ $rightXml
if (-not (Test-Path $extDir)) { New-Item -ItemType Directory -Path $extDir -Force | Out-Null }
$hpPath = Join-Path $extDir "HomePageWorkArea.xml"
$utf8Bom = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText($hpPath, $hpXml, $utf8Bom)
# Файл создаём мы — канон: CRLF в разделителях, без перевода строки в конце.
[System.IO.File]::WriteAllText($hpPath, (($hpXml -replace "`r`n", "`n") -replace "`n", "`r`n").TrimEnd("`r", "`n"), $utf8Bom)
$script:modifyCount++
Info "Wrote home page layout: $hpPath"
}
@@ -985,6 +992,8 @@ $text = $text.Replace('encoding="utf-8"', 'encoding="UTF-8"')
# Пустой элемент: XmlWriter пишет `<a />`, Конфигуратор — `<a/>`. Гард на CDATA/комментарии:
# только там `>` не экранируется, и ` />` может быть содержимым, а не концом тега.
if ($text -notmatch '<!\[CDATA\[|<!--') { $text = [regex]::Replace($text, '(?<=\S) />', '/>') }
# Возвращаем EOL исходного файла: вставки собраны с CRLF, а сам документ мог быть LF.
$text = ($text -replace "`r`n", "`n") -replace "`n", $script:srcEol
$utf8Bom = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText($resolvedPath, $text, $utf8Bom)
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# cf-edit v1.12 — Edit 1C configuration root (Configuration.xml)
# cf-edit v1.14 — Edit 1C configuration root (Configuration.xml)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
+13 -4
View File
@@ -1,4 +1,4 @@
# cf-init v1.4 — Create empty 1C configuration scaffold
# cf-init v1.5 — Create empty 1C configuration scaffold
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -240,11 +240,20 @@ if (-not (Test-Path $extDir)) {
# --- Write files with UTF-8 BOM ---
$enc = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText($cfgFile, $cfgXml, $enc)
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается here-string'ами, а .ps1 хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .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)
}
Write-XmlFile $cfgFile $cfgXml $enc
$langFile = Join-Path $langDir "Русский.xml"
[System.IO.File]::WriteAllText($langFile, $langXml, $enc)
Write-XmlFile $langFile $langXml $enc
$caiFile = Join-Path $extDir "ClientApplicationInterface.xml"
[System.IO.File]::WriteAllText($caiFile, $caiXml, $enc)
Write-XmlFile $caiFile $caiXml $enc
# --- Output ---
Write-Host "[OK] Создана конфигурация: $Name"
+11 -4
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# cf-init v1.4 — Create empty 1C configuration scaffold
# cf-init v1.5 — Create empty 1C configuration scaffold
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
"""Generates minimal XML source files for a 1C configuration."""
import sys, os, argparse, uuid
@@ -14,6 +14,13 @@ def write_utf8_bom(path, content):
with open(path, 'w', encoding='utf-8-sig', newline='') as f:
f.write(content)
def write_xml_file(path, content):
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается тройными кавычками, а .py хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через неё НЕ пишутся.
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")
@@ -222,11 +229,11 @@ def main():
os.makedirs(ext_dir, exist_ok=True)
# --- Write files ---
write_utf8_bom(cfg_file, cfg_xml)
write_xml_file(cfg_file, cfg_xml)
lang_file = os.path.join(lang_dir, "Русский.xml")
write_utf8_bom(lang_file, lang_xml)
write_xml_file(lang_file, lang_xml)
cai_file = os.path.join(ext_dir, "ClientApplicationInterface.xml")
write_utf8_bom(cai_file, cai_xml)
write_xml_file(cai_file, cai_xml)
print(f"[OK] Создана конфигурация: {name}")
print(f" Каталог: {output_dir}")
+13 -4
View File
@@ -1,4 +1,4 @@
# cfe-init v1.2 — Create 1C configuration extension scaffold (CFE)
# cfe-init v1.3 — Create 1C configuration extension scaffold (CFE)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -252,9 +252,18 @@ if (-not (Test-Path $langDir)) {
# --- Write files with UTF-8 BOM ---
$enc = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText($cfgFile, $cfgXml, $enc)
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается here-string'ами, а .ps1 хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .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)
}
Write-XmlFile $cfgFile $cfgXml $enc
$langFile = Join-Path $langDir "Русский.xml"
[System.IO.File]::WriteAllText($langFile, $langXml, $enc)
Write-XmlFile $langFile $langXml $enc
# --- Role ---
if (-not $NoRole) {
@@ -263,7 +272,7 @@ if (-not $NoRole) {
New-Item -ItemType Directory -Path $roleDir -Force | Out-Null
}
$roleFile = Join-Path $roleDir "$roleName.xml"
[System.IO.File]::WriteAllText($roleFile, $roleXml, $enc)
Write-XmlFile $roleFile $roleXml $enc
}
# --- Output ---
+11 -4
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# cfe-init v1.2 — Create 1C configuration extension scaffold (CFE)
# cfe-init v1.3 — Create 1C configuration extension scaffold (CFE)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
"""Generates minimal XML source files for a 1C configuration extension."""
import sys, os, argparse, uuid
@@ -15,6 +15,13 @@ def write_utf8_bom(path, content):
with open(path, 'w', encoding='utf-8-sig', newline='') as f:
f.write(content)
def write_xml_file(path, content):
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается тройными кавычками, а .py хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через неё НЕ пишутся.
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")
@@ -229,9 +236,9 @@ def main():
os.makedirs(lang_dir, exist_ok=True)
# --- Write files ---
write_utf8_bom(cfg_file, cfg_xml)
write_xml_file(cfg_file, cfg_xml)
lang_file = os.path.join(lang_dir, "Русский.xml")
write_utf8_bom(lang_file, lang_xml)
write_xml_file(lang_file, lang_xml)
# --- Role ---
role_file = None
@@ -239,7 +246,7 @@ def main():
role_dir = os.path.join(output_dir, "Roles")
os.makedirs(role_dir, exist_ok=True)
role_file = os.path.join(role_dir, f"{role_name}.xml")
write_utf8_bom(role_file, role_xml)
write_xml_file(role_file, role_xml)
# --- Output ---
print(f"[OK] Создано расширение: {name}")
+11 -2
View File
@@ -1,4 +1,4 @@
# epf-init v1.1 — Init 1C external data processor scaffold
# epf-init v1.2 — Init 1C external data processor scaffold
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -64,7 +64,16 @@ $extDir = Join-Path $processorDir "Ext"
New-Item -ItemType Directory -Path $extDir -Force | Out-Null
$enc = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText((Resolve-Path $SrcDir | Join-Path -ChildPath "$Name.xml"), $xml, $enc)
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается here-string'ами, а .ps1 хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .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)
}
Write-XmlFile (Resolve-Path $SrcDir | Join-Path -ChildPath "$Name.xml") $xml $enc
# --- Модуль объекта ---
+9 -2
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# epf-init v1.1 — Init 1C external data processor scaffold
# epf-init v1.2 — Init 1C external data processor scaffold
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
"""Generates minimal XML source files for a 1C external data processor."""
import sys, os, argparse, uuid
@@ -14,6 +14,13 @@ def write_utf8_bom(path, content):
with open(path, 'w', encoding='utf-8-sig', newline='') as f:
f.write(content)
def write_xml_file(path, content):
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается тройными кавычками, а .py хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через неё НЕ пишутся.
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")
@@ -72,7 +79,7 @@ def main():
ext_dir = os.path.join(processor_dir, "Ext")
os.makedirs(ext_dir, exist_ok=True)
write_utf8_bom(os.path.join(os.path.abspath(src_dir), f"{name}.xml"), xml)
write_xml_file(os.path.join(os.path.abspath(src_dir), f"{name}.xml"), xml)
# --- Модуль объекта ---
module_bsl = """\
+13 -4
View File
@@ -1,4 +1,4 @@
# erf-init v1.1 — Init 1C external report scaffold
# erf-init v1.2 — Init 1C external report scaffold
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -98,7 +98,16 @@ $extDir = Join-Path $reportDir "Ext"
New-Item -ItemType Directory -Path $extDir -Force | Out-Null
$enc = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText((Resolve-Path $SrcDir | Join-Path -ChildPath "$Name.xml"), $xml, $enc)
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается here-string'ами, а .ps1 хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .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)
}
Write-XmlFile (Resolve-Path $SrcDir | Join-Path -ChildPath "$Name.xml") $xml $enc
# --- Модуль объекта ---
@@ -153,7 +162,7 @@ if ($WithSKD) {
</MetaDataObject>
"@
[System.IO.File]::WriteAllText($skdMetaPath, $skdMetaXml, $enc)
Write-XmlFile $skdMetaPath $skdMetaXml $enc
$skdContent = @"
<?xml version="1.0" encoding="UTF-8"?>
@@ -173,7 +182,7 @@ if ($WithSKD) {
"@
$skdFilePath = Join-Path $skdExtDir "Template.xml"
[System.IO.File]::WriteAllText($skdFilePath, $skdContent, $enc)
Write-XmlFile $skdFilePath $skdContent $enc
Write-Host " СКД: $skdMetaPath"
Write-Host " Тело: $skdFilePath"
+11 -4
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# erf-init v1.1 — Init 1C external report scaffold
# erf-init v1.2 — Init 1C external report scaffold
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
"""Generates minimal XML source files for a 1C external report."""
import sys, os, argparse, uuid
@@ -14,6 +14,13 @@ def write_utf8_bom(path, content):
with open(path, 'w', encoding='utf-8-sig', newline='') as f:
f.write(content)
def write_xml_file(path, content):
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается тройными кавычками, а .py хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .bsl и текстовые макеты через неё НЕ пишутся.
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")
@@ -90,7 +97,7 @@ def main():
ext_dir = os.path.join(report_dir, "Ext")
os.makedirs(ext_dir, exist_ok=True)
write_utf8_bom(os.path.join(os.path.abspath(src_dir), f"{name}.xml"), xml)
write_xml_file(os.path.join(os.path.abspath(src_dir), f"{name}.xml"), xml)
# --- Модуль объекта ---
module_bsl = """\
@@ -140,7 +147,7 @@ def main():
\t</Template>
</MetaDataObject>'''
write_utf8_bom(skd_meta_path, skd_meta_xml)
write_xml_file(skd_meta_path, skd_meta_xml)
skd_content = '''<?xml version="1.0" encoding="UTF-8"?>
<DataCompositionSchema xmlns="http://v8.1c.ru/8.1/data-composition-system/schema"
@@ -158,7 +165,7 @@ def main():
</DataCompositionSchema>'''
skd_file_path = os.path.join(skd_ext_dir, "Template.xml")
write_utf8_bom(skd_file_path, skd_content)
write_xml_file(skd_file_path, skd_content)
print(f" СКД: {skd_meta_path}")
print(f" Тело: {skd_file_path}")
+12 -3
View File
@@ -1,4 +1,4 @@
# form-add v1.14 — Add managed form to 1C config object
# form-add v1.16 — Add managed form to 1C config object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -337,7 +337,16 @@ $formMetaXml = @"
</MetaDataObject>
"@
[System.IO.File]::WriteAllText($formMetaPath, $formMetaXml.TrimEnd("`r", "`n"), $encBom)
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается here-string'ами, а .ps1 хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .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)
}
Write-XmlFile $formMetaPath $formMetaXml $encBom
# --- 3b. Form.xml ---
@@ -444,7 +453,7 @@ if ($Purpose -eq "List" -or $Purpose -eq "Choice") {
if (Test-Path $formXmlPath) {
Write-Host "[SKIP] Form.xml already exists: $formXmlPath — not overwriting"
} else {
[System.IO.File]::WriteAllText($formXmlPath, $formXml.TrimEnd("`r", "`n"), $encBom)
Write-XmlFile $formXmlPath $formXml $encBom
}
# --- 3c. Module.bsl ---
+16 -4
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# form-add v1.14 — Add managed form to 1C config object
# form-add v1.16 — Add managed form to 1C config object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -259,10 +259,22 @@ def save_xml_with_bom(tree, path):
def write_text_with_bom(path, text):
"""Write text to file with UTF-8 BOM."""
with open(path, "w", encoding="utf-8-sig") as f:
# newline="" => без трансляции: в текстовом режиме Python на Windows превратил
# бы \n в \r\n, а на macOS оставил \n — вывод зависел бы от ОС.
with open(path, "w", encoding="utf-8-sig", newline="") as f:
f.write(text)
def write_xml_file(path, text):
"""XML в каноне выгрузки Конфигуратора: CRLF, без перевода строки в конце.
Скелет формы собирается литералами с \n. Сплошная нормализация здесь
безопасна: многострочных текстовых узлов в скелете нет. Модуль .bsl
пишется отдельно, через write_text_with_bom.
"""
write_text_with_bom(path, text.replace("\r\n", "\n").replace("\n", "\r\n").rstrip("\r\n"))
def main():
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
@@ -428,7 +440,7 @@ def main():
'</MetaDataObject>'
)
write_text_with_bom(form_meta_path, form_meta_xml)
write_xml_file(form_meta_path, form_meta_xml)
# --- 3b. Form.xml ---
@@ -551,7 +563,7 @@ def main():
if os.path.exists(form_xml_path):
print(f"[SKIP] Form.xml already exists: {form_xml_path} — not overwriting")
else:
write_text_with_bom(form_xml_path, form_xml)
write_xml_file(form_xml_path, form_xml)
# --- 3c. Module.bsl ---
@@ -1,4 +1,4 @@
# form-compile v1.178 — Compile 1C managed form from JSON or object metadata
# form-compile v1.180 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$JsonPath,
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# form-compile v1.178 — Compile 1C managed form from JSON or object metadata
# form-compile v1.180 — Compile 1C managed form from JSON or object metadata
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import copy
@@ -6580,7 +6580,7 @@ def main():
if out_dir and not os.path.exists(out_dir):
os.makedirs(out_dir, exist_ok=True)
content = '\n'.join(lines)
content = '\r\n'.join(lines)
write_utf8_bom(out_path, content)
# --- 4. Auto-register form in parent object XML ---
@@ -6598,17 +6598,22 @@ def main():
if forms_leaf == 'Forms':
object_xml_path = os.path.join(type_plural_dir, f'{object_name}.xml')
if os.path.exists(object_xml_path):
with open(object_xml_path, 'r', encoding='utf-8-sig') as f:
# newline='' => без трансляции переводов строк: иначе CRLF молча
# схлопнется в LF при чтении и файл будет переписан в LF (#44/#46/#47).
with open(object_xml_path, 'r', encoding='utf-8-sig', newline='') as f:
raw_text = f.read()
# Перевод строки вставки берём из самого файла, а не из канона:
# правка существующего файла сохраняет его стиль.
eol = '\r\n' if '\r\n' in raw_text else '\n'
# Check if already registered
if f'<Form>{form_name}</Form>' not in raw_text:
# Insert before </ChildObjects>
if '</ChildObjects>' in raw_text:
insert_line = f'\t\t\t<Form>{form_name}</Form>\n'
insert_line = f'\t\t\t<Form>{form_name}</Form>' + eol
raw_text = raw_text.replace('</ChildObjects>', insert_line + '\t\t</ChildObjects>', 1)
elif '<ChildObjects/>' in raw_text:
replacement = f'<ChildObjects>\n\t\t\t<Form>{form_name}</Form>\n\t\t</ChildObjects>'
replacement = ('<ChildObjects>' + eol + f'\t\t\t<Form>{form_name}</Form>' + eol + '\t\t</ChildObjects>')
raw_text = raw_text.replace('<ChildObjects/>', replacement, 1)
write_utf8_bom(object_xml_path, raw_text)
+11 -2
View File
@@ -1,4 +1,4 @@
# help-add v1.11 — Add built-in help to 1C object
# help-add v1.12 — Add built-in help to 1C object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -195,7 +195,16 @@ $helpXml = @"
</Help>
"@
[System.IO.File]::WriteAllText($helpXmlPath, $helpXml.TrimEnd("`r", "`n"), $encBom)
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается here-string'ами, а .ps1 хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .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)
}
Write-XmlFile $helpXmlPath $helpXml $encBom
# --- 2. Help/<lang>.html ---
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# add-help v1.11 — Add built-in help to 1C object
# add-help v1.12 — Add built-in help to 1C object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -1,4 +1,4 @@
# meta-compile v1.78 — Compile 1C metadata object from JSON
# meta-compile v1.80 — Compile 1C metadata object from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -161,7 +161,10 @@ if ($def -is [array] -or ($null -ne $def -and $def.GetType().BaseType.Name -eq '
$idx = 0
foreach ($item in $def) {
$idx++
$tmpJson = Join-Path ([System.IO.Path]::GetTempPath()) "meta-compile-batch-$idx.json"
# Имя с GUID, а не "batch-$idx": фиксированное имя в общем %TEMP% сталкивало
# два параллельных запуска навыка на одной машине — Set-Content падал с
# «file is being used by another process». py-порт уже брал mkstemp.
$tmpJson = Join-Path ([System.IO.Path]::GetTempPath()) "meta-compile-batch-$idx-$([guid]::NewGuid().ToString('N')).json"
try {
$item | ConvertTo-Json -Depth 20 | Set-Content -Encoding UTF8 $tmpJson
$proc = Start-Process -FilePath "powershell.exe" -ArgumentList "-NoProfile -File `"$PSCommandPath`" -JsonPath `"$tmpJson`" -OutputDir `"$OutputDir`"" -NoNewWindow -Wait -PassThru
@@ -4611,7 +4614,7 @@ function Build-PredefinedXml {
[void]$sb.Append("<PredefinedData xmlns=`"http://v8.1c.ru/8.3/xcf/predef`" xmlns:v8=`"http://v8.1c.ru/8.1/data/core`" xmlns:xr=`"http://v8.1c.ru/8.3/xcf/readable`" xmlns:xs=`"http://www.w3.org/2001/XMLSchema`" xmlns:xsi=`"http://www.w3.org/2001/XMLSchema-instance`" xsi:type=`"$xsiType`" version=`"$($script:formatVersion)`">`n")
foreach ($it in $items) { Emit-PredefItem $sb $it "`t" $codeType }
[void]$sb.Append("</PredefinedData>`n")
return $sb.ToString()
return ($sb.ToString() -replace "`r`n", "`n") -replace "`n", "`r`n"
}
# --- Предопределённые СЧЕТА Плана счетов (отдельная грамматика: AccountType/OffBalance/Order/AccountingFlags/
@@ -4699,7 +4702,7 @@ function Build-PredefinedAccountXml {
[void]$sb.Append("<PredefinedData xmlns=`"http://v8.1c.ru/8.3/xcf/predef`" xmlns:v8=`"http://v8.1c.ru/8.1/data/core`" xmlns:xr=`"http://v8.1c.ru/8.3/xcf/readable`" xmlns:xs=`"http://www.w3.org/2001/XMLSchema`" xmlns:xsi=`"http://www.w3.org/2001/XMLSchema-instance`" xsi:type=`"ChartOfAccountsPredefinedItems`" version=`"$($script:formatVersion)`">`n")
foreach ($it in $items) { Emit-PredefAccount $sb $it "`t" $objName $acctFlagNames $extDimFlagNames $extDimTypesRef }
[void]$sb.Append("</PredefinedData>`n")
return $sb.ToString()
return ($sb.ToString() -replace "`r`n", "`n") -replace "`n", "`r`n"
}
# --- Предопределённые ВИДЫ РАСЧЁТА (плоские: Name/Code/Description/ActionPeriodIsBase). Строка "(Код) Имя [Наим]"
@@ -4726,7 +4729,7 @@ function Build-PredefinedCalcTypeXml {
[void]$sb.Append("<PredefinedData xmlns=`"http://v8.1c.ru/8.3/xcf/predef`" xmlns:v8=`"http://v8.1c.ru/8.1/data/core`" xmlns:xr=`"http://v8.1c.ru/8.3/xcf/readable`" xmlns:xs=`"http://www.w3.org/2001/XMLSchema`" xmlns:xsi=`"http://www.w3.org/2001/XMLSchema-instance`" xsi:type=`"CalculationTypePredefinedItems`" version=`"$($script:formatVersion)`">`n")
foreach ($it in $items) { Emit-PredefCalcType $sb $it "`t" }
[void]$sb.Append("</PredefinedData>`n")
return $sb.ToString()
return ($sb.ToString() -replace "`r`n", "`n") -replace "`n", "`r`n"
}
$extDir = Join-Path $objSubDir "Ext"
@@ -4743,13 +4746,18 @@ if ($objType -notin $typesNoSubDir) {
$enc = New-Object System.Text.UTF8Encoding($true)
# Единая точка записи XML. Конфигуратор не пишет перевод строки в конце файла —
# последний байт `>`; сборка через AppendLine (и через явный `n в билдерах
# Predefined/Content) добавляла лишний. Модули .bsl сюда НЕ идут: у них свой хвост.
function Write-XmlFile([string]$path, [string]$text, $encoding) {
# последний байт `>`; сборка через AppendLine добавляла лишний.
# KeepEol в имени — отличие от одноимённой функции в скелетных навыках
# (cf-init и др.): та ЕЩЁ и нормализует EOL к CRLF, а здесь этого делать
# НЕЛЬЗЯ — в объектном XML бывают многострочные текстовые узлы (запрос,
# синоним, значение заполнения), и сплошная нормализация меняла бы содержимое.
# Разделители тут и так CRLF: документ собран через AppendLine.
# Модули .bsl сюда НЕ идут: у них свой хвост.
function Write-XmlFileKeepEol([string]$path, [string]$text, $encoding) {
[System.IO.File]::WriteAllText($path, $text.TrimEnd("`r", "`n"), $encoding)
}
Write-XmlFile $mainXmlPath $metadataXml $enc
Write-XmlFileKeepEol $mainXmlPath $metadataXml $enc
# Module files
$modulesCreated = @()
@@ -4830,8 +4838,8 @@ if ($objType -eq "CommonForm") {
$cfFormXmlPath = Join-Path $extDir "Form.xml"
if (-not (Test-Path $cfFormXmlPath)) {
$cfFormNs = 'xmlns="http://v8.1c.ru/8.3/xcf/logform" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:dcscor="http://v8.1c.ru/8.1/data-composition-system/core" xmlns:dcsset="http://v8.1c.ru/8.1/data-composition-system/settings" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
$cfFormXml = "<?xml version=`"1.0`" encoding=`"UTF-8`"?>`n<Form $cfFormNs version=`"$($script:formatVersion)`">`n`t<AutoCommandBar name=`"ФормаКоманднаяПанель`" id=`"-1`">`n`t`t<Autofill>true</Autofill>`n`t</AutoCommandBar>`n`t<ChildItems/>`n</Form>`n"
Write-XmlFile $cfFormXmlPath $cfFormXml $enc
$cfFormXml = "<?xml version=`"1.0`" encoding=`"UTF-8`"?>`r`n<Form $cfFormNs version=`"$($script:formatVersion)`">`r`n`t<AutoCommandBar name=`"ФормаКоманднаяПанель`" id=`"-1`">`r`n`t`t<Autofill>true</Autofill>`r`n`t</AutoCommandBar>`r`n`t<ChildItems/>`r`n</Form>`r`n"
Write-XmlFileKeepEol $cfFormXmlPath $cfFormXml $enc
$modulesCreated += $cfFormXmlPath
}
$cfModuleDir = Join-Path $extDir "Form"
@@ -4884,7 +4892,7 @@ if ($objType -eq "ExchangePlan") {
[void]$sbC.Append("`t</Item>`r`n")
}
[void]$sbC.Append("</ExchangePlanContent>`r`n")
Write-XmlFile $contentPath $sbC.ToString() $enc
Write-XmlFileKeepEol $contentPath $sbC.ToString() $enc
$modulesCreated += $contentPath
} elseif (-not (Test-Path $contentPath)) {
# При пустом составе платформа всё равно пишет пустой <ExchangePlanContent/> — проверено на
@@ -4892,7 +4900,7 @@ if ($objType -eq "ExchangePlan") {
# (Единственный план обмена БЕЗ файла найден в УТ — хвостовая аномалия конфигурации, не правило.)
Ensure-ExtDir
$contentXml = "<?xml version=`"1.0`" encoding=`"UTF-8`"?>`r`n<ExchangePlanContent $xepNs version=`"$($script:formatVersion)`"/>`r`n"
Write-XmlFile $contentPath $contentXml $enc
Write-XmlFileKeepEol $contentPath $contentXml $enc
$modulesCreated += $contentPath
}
}
@@ -4901,7 +4909,7 @@ if ($objType -eq "BusinessProcess") {
if (-not (Test-Path $flowchartPath)) {
Ensure-ExtDir
$flowchartXml = "<?xml version=`"1.0`" encoding=`"UTF-8`"?>`r`n<Flowchart xmlns=`"http://v8.1c.ru/8.3/MDClasses`" version=`"$($script:formatVersion)`"/>`r`n"
Write-XmlFile $flowchartPath $flowchartXml $enc
Write-XmlFileKeepEol $flowchartPath $flowchartXml $enc
$modulesCreated += $flowchartPath
}
}
@@ -4916,20 +4924,20 @@ if ($objType -eq 'ChartOfAccounts' -and $def.predefined -and @($def.predefined).
$edtRef = if ($def.extDimensionTypes) { Resolve-TypePrefixSyn "$($def.extDimensionTypes)" } else { '' }
$predefXml = Build-PredefinedAccountXml @($def.predefined) $objName $afNames $edfNames $edtRef
$predefPath = Join-Path $extDir "Predefined.xml"
Write-XmlFile $predefPath $predefXml $enc
Write-XmlFileKeepEol $predefPath $predefXml $enc
$modulesCreated += $predefPath
} elseif ($objType -eq 'ChartOfCalculationTypes' -and $def.predefined -and @($def.predefined).Count -gt 0) {
Ensure-ExtDir
$predefXml = Build-PredefinedCalcTypeXml @($def.predefined)
$predefPath = Join-Path $extDir "Predefined.xml"
Write-XmlFile $predefPath $predefXml $enc
Write-XmlFileKeepEol $predefPath $predefXml $enc
$modulesCreated += $predefPath
} elseif ($predefRootByType.ContainsKey($objType) -and $def.predefined -and @($def.predefined).Count -gt 0) {
Ensure-ExtDir
$catCodeType = if ($def.codeType) { "$($def.codeType)" } else { 'String' }
$predefXml = Build-PredefinedXml @($def.predefined) $predefRootByType[$objType] $catCodeType
$predefPath = Join-Path $extDir "Predefined.xml"
Write-XmlFile $predefPath $predefXml $enc
Write-XmlFileKeepEol $predefPath $predefXml $enc
$modulesCreated += $predefPath
}
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# meta-compile v1.78 — Compile 1C metadata object from JSON
# meta-compile v1.80 — Compile 1C metadata object from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -209,6 +209,16 @@ def write_utf8_bom(path, content):
with open(path, 'w', encoding='utf-8-sig', newline='') as f:
f.write(content)
def write_xml_file_keep_eol(path, content):
# Единая точка записи XML. Конфигуратор не пишет перевод строки в конце файла —
# последний байт `>`.
# keep_eol в имени — отличие от одноимённой функции в скелетных навыках
# (cf-init и др.): та ЕЩЁ и нормализует EOL к CRLF, а здесь этого делать
# НЕЛЬЗЯ — в объектном XML бывают многострочные текстовые узлы (запрос,
# синоним, значение заполнения). Разделители и так CRLF — их даёт join строк.
# Модули .bsl сюда НЕ идут: у них свой хвост.
write_utf8_bom(path, content.rstrip('\r\n'))
# ---------------------------------------------------------------------------
# XML builder (lines list)
# ---------------------------------------------------------------------------
@@ -4360,7 +4370,7 @@ if obj_type == 'WebService':
X(f'\t</{obj_type}>')
X('</MetaDataObject>')
metadata_xml = '\n'.join(lines)
metadata_xml = '\r\n'.join(lines)
# ---------------------------------------------------------------------------
# 16. Write files
@@ -4422,7 +4432,7 @@ os.makedirs(type_dir, exist_ok=True)
if obj_type not in types_no_sub_dir:
os.makedirs(obj_sub_dir, exist_ok=True)
write_utf8_bom(main_xml_path, metadata_xml)
write_xml_file_keep_eol(main_xml_path, metadata_xml)
# Module files
modules_created = []
@@ -4498,10 +4508,10 @@ if obj_type == 'CommonForm':
'xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" '
'xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" '
'xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"')
cf_form_xml = ('<?xml version="1.0" encoding="UTF-8"?>\n<Form ' + cf_ns + ' version="' + format_version + '">\n'
'\t<AutoCommandBar name="ФормаКоманднаяПанель" id="-1">\n\t\t<Autofill>true</Autofill>\n\t</AutoCommandBar>\n'
'\t<ChildItems/>\n</Form>\n')
write_utf8_bom(cf_form_xml_path, cf_form_xml)
cf_form_xml = ('<?xml version="1.0" encoding="UTF-8"?>\r\n<Form ' + cf_ns + ' version="' + format_version + '">\r\n'
'\t<AutoCommandBar name="ФормаКоманднаяПанель" id="-1">\r\n\t\t<Autofill>true</Autofill>\r\n\t</AutoCommandBar>\r\n'
'\t<ChildItems/>\r\n</Form>\r\n')
write_xml_file_keep_eol(cf_form_xml_path, cf_form_xml)
modules_created.append(cf_form_xml_path)
cf_module_dir = os.path.join(ext_dir, 'Form')
os.makedirs(cf_module_dir, exist_ok=True)
@@ -4592,7 +4602,7 @@ def build_predefined_xml(items, xsi_type, code_type):
for it in items:
emit_predef_item(out, it, '\t', code_type)
out.append('</PredefinedData>')
return '\n'.join(out) + '\n'
return '\r\n'.join(out)
# --- Предопределённые СЧЕТА Плана счетов (отдельная грамматика: AccountType/OffBalance/Order/AccountingFlags/
# ExtDimensionTypes/ChildItems). Флаги перечисляем по def-порядку признаков плана; в DSL — только TRUE. ---
@@ -4690,7 +4700,7 @@ def build_predefined_account_xml(items, obj_nm, acct_flag_names, ext_dim_flag_na
for it in items:
emit_predef_account(out, it, '\t', obj_nm, acct_flag_names, ext_dim_flag_names, ext_dim_types_ref)
out.append('</PredefinedData>')
return '\n'.join(out) + '\n'
return '\r\n'.join(out)
# Предопределённые ВИДЫ РАСЧЁТА (плоские: Name/Code/Description/ActionPeriodIsBase).
def emit_predef_calc_type(out, val, indent):
@@ -4712,7 +4722,7 @@ def build_predefined_calc_type_xml(items):
for it in items:
emit_predef_calc_type(out, it, '\t')
out.append('</PredefinedData>')
return '\n'.join(out) + '\n'
return '\r\n'.join(out)
# Special files
# --- Состав плана обмена (ExchangePlan, Ext/Content.xml). Ключ `content`/`Состав`:
@@ -4766,7 +4776,7 @@ if obj_type == 'ExchangePlan':
parts.append(f'\t\t<AutoRecord>{it["autoRecord"]}</AutoRecord>\r\n')
parts.append('\t</Item>\r\n')
parts.append('</ExchangePlanContent>\r\n')
write_utf8_bom(content_path, ''.join(parts))
write_xml_file_keep_eol(content_path, ''.join(parts))
modules_created.append(content_path)
elif not os.path.isfile(content_path):
# При пустом составе платформа всё равно пишет пустой <ExchangePlanContent/> — проверено на
@@ -4774,7 +4784,7 @@ if obj_type == 'ExchangePlan':
# (Единственный план обмена БЕЗ файла найден в УТ — хвостовая аномалия конфигурации, не правило.)
ensure_ext_dir()
content_xml = f'<?xml version="1.0" encoding="UTF-8"?>\r\n<ExchangePlanContent {xep_ns} version="{format_version}"/>\r\n'
write_utf8_bom(content_path, content_xml)
write_xml_file_keep_eol(content_path, content_xml)
modules_created.append(content_path)
if obj_type == 'BusinessProcess':
@@ -4782,7 +4792,7 @@ if obj_type == 'BusinessProcess':
if not os.path.isfile(flowchart_path):
ensure_ext_dir()
flowchart_xml = f'<?xml version="1.0" encoding="UTF-8"?>\r\n<Flowchart xmlns="http://v8.1c.ru/8.3/MDClasses" version="{format_version}"/>\r\n'
write_utf8_bom(flowchart_path, flowchart_xml)
write_xml_file_keep_eol(flowchart_path, flowchart_xml)
modules_created.append(flowchart_path)
# Предопределённые элементы (Ext/Predefined.xml). Root-элемент по типу.
@@ -4795,20 +4805,20 @@ if obj_type == 'ChartOfAccounts' and defn.get('predefined'):
edt_ref = resolve_type_prefix_syn(str(defn['extDimensionTypes'])) if defn.get('extDimensionTypes') else ''
predef_xml = build_predefined_account_xml(defn['predefined'], obj_name, af_names, edf_names, edt_ref)
predef_path = os.path.join(ext_dir, 'Predefined.xml')
write_utf8_bom(predef_path, predef_xml)
write_xml_file_keep_eol(predef_path, predef_xml)
modules_created.append(predef_path)
elif obj_type == 'ChartOfCalculationTypes' and defn.get('predefined'):
ensure_ext_dir()
predef_xml = build_predefined_calc_type_xml(defn['predefined'])
predef_path = os.path.join(ext_dir, 'Predefined.xml')
write_utf8_bom(predef_path, predef_xml)
write_xml_file_keep_eol(predef_path, predef_xml)
modules_created.append(predef_path)
elif obj_type in predef_root_by_type and defn.get('predefined'):
ensure_ext_dir()
cat_code_type = str(defn['codeType']) if defn.get('codeType') else 'String'
predef_xml = build_predefined_xml(defn['predefined'], predef_root_by_type[obj_type], cat_code_type)
predef_path = os.path.join(ext_dir, 'Predefined.xml')
write_utf8_bom(predef_path, predef_xml)
write_xml_file_keep_eol(predef_path, predef_xml)
modules_created.append(predef_path)
# Модули команд (Commands/<Имя>/Ext/CommandModule.bsl) — заготовка обработчика.
@@ -1,4 +1,4 @@
# meta-edit v1.25 — Edit existing 1C metadata object XML
# meta-edit v1.26 — Edit existing 1C metadata object XML
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$DefinitionFile,
@@ -3138,7 +3138,8 @@ function Add-PredefinedItems($items) {
$hdr = "<?xml version=`"1.0`" encoding=`"UTF-8`"?>`r`n<PredefinedData xmlns=`"http://v8.1c.ru/8.3/xcf/predef`" xmlns:v8=`"http://v8.1c.ru/8.1/data/core`" xmlns:xr=`"http://v8.1c.ru/8.3/xcf/readable`" xmlns:xs=`"http://www.w3.org/2001/XMLSchema`" xmlns:xsi=`"http://www.w3.org/2001/XMLSchema-instance`" xsi:type=`"$xsiType`" version=`"$version`">`r`n"
$text = "$hdr$itemsXml</PredefinedData>`r`n"
}
[System.IO.File]::WriteAllText($path, $text, $utf8Bom)
# Создаваемый файл — по канону: без перевода строки в конце.
[System.IO.File]::WriteAllText($path, $text.TrimEnd("`r", "`n"), $utf8Bom)
$n = @($items).Count
Info "Added $n predefined item(s) → $path"
$script:addCount += $n
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# meta-edit v1.25 — Edit existing 1C metadata object XML
# meta-edit v1.26 — Edit existing 1C metadata object XML
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -1,4 +1,4 @@
# mxl-compile v1.6 — Compile 1C spreadsheet from JSON
# mxl-compile v1.7 — Compile 1C spreadsheet from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# mxl-compile v1.6 — Compile 1C spreadsheet from JSON
# mxl-compile v1.7 — Compile 1C spreadsheet from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import json
@@ -802,7 +802,7 @@ def main():
if out_dir and not os.path.exists(out_dir):
os.makedirs(out_dir, exist_ok=True)
content = '\n'.join(lines)
content = '\r\n'.join(lines)
write_utf8_bom(out_path, content)
# --- 9. Summary ---
@@ -1,4 +1,4 @@
# role-compile v1.12 — Compile 1C role from JSON
# role-compile v1.14 — Compile 1C role from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# role-compile v1.12 — Compile 1C role from JSON
# role-compile v1.14 — Compile 1C role from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import json
@@ -203,6 +203,12 @@ def detect_format_version(d):
return "2.17"
def detect_eol(text):
# Перевод строки ВСТАВКИ берём из самого файла: канон CRLF относится к файлам,
# которые мы создаём, а правка существующего сохраняет его стиль (#44/#46/#47).
crlf = text.count('\r\n')
return '\r\n' if crlf and crlf >= text.count('\n') - crlf else '\n'
def esc_xml(s):
"""Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует
(92142 сырых кавычки на корпус, ни одной &quot;); &quot; она принимает, но нормализует обратно."""
@@ -708,7 +714,7 @@ def main():
lines.append(' </Role>')
lines.append('</MetaDataObject>')
metadata_xml = '\n'.join(lines)
metadata_xml = '\r\n'.join(lines)
# --- 5. Emit Rights XML (Roles/Name/Ext/Rights.xml) ---
lines = []
@@ -756,7 +762,7 @@ def main():
lines.append('</Rights>')
rights_xml = '\n'.join(lines)
rights_xml = '\r\n'.join(lines)
# --- 6. Write output files ---
out_dir = args.OutputDir
@@ -791,9 +797,13 @@ def main():
reg_result = None
if os.path.exists(config_xml_path):
with open(config_xml_path, 'r', encoding='utf-8-sig') as f:
# newline='' => без трансляции переводов строк: иначе CRLF молча схлопнется
# в LF при чтении и файл будет переписан в LF (#44/#46/#47).
with open(config_xml_path, 'r', encoding='utf-8-sig', newline='') as f:
raw_text = f.read()
eol = detect_eol(raw_text)
# Check if already registered
if f'<Role>{role_name}</Role>' in raw_text:
reg_result = 'already'
@@ -807,10 +817,10 @@ def main():
# Insert after last existing <Role>
last_match = matches[-1]
insert_pos = last_match.end()
raw_text = raw_text[:insert_pos] + f'\n\t\t\t{new_role_tag}' + raw_text[insert_pos:]
raw_text = raw_text[:insert_pos] + eol + f'\t\t\t{new_role_tag}' + raw_text[insert_pos:]
else:
# No existing roles — insert before </ChildObjects>
raw_text = raw_text.replace('</ChildObjects>', f'\t\t\t{new_role_tag}\n\t\t</ChildObjects>')
raw_text = raw_text.replace('</ChildObjects>', f'\t\t\t{new_role_tag}' + eol + '\t\t</ChildObjects>')
write_utf8_bom(config_xml_path, raw_text)
reg_result = 'added'
@@ -1,4 +1,4 @@
# skd-compile v1.110 — Compile 1C DCS from JSON
# skd-compile v1.111 — Compile 1C DCS from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$DefinitionFile,
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# skd-compile v1.110 — Compile 1C DCS from JSON
# skd-compile v1.111 — Compile 1C DCS from JSON
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import json
@@ -3069,7 +3069,7 @@ def main():
if parent_dir and not os.path.exists(parent_dir):
os.makedirs(parent_dir, exist_ok=True)
content = '\n'.join(lines)
content = '\r\n'.join(lines)
write_utf8_bom(output_path, content)
# --- 5. Statistics ---
@@ -1,4 +1,4 @@
# subsystem-compile v1.13 — Create 1C subsystem from JSON definition
# subsystem-compile v1.15 — Create 1C subsystem from JSON definition
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[string]$DefinitionFile,
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# subsystem-compile v1.13 — Create 1C subsystem from JSON definition
# subsystem-compile v1.15 — Create 1C subsystem from JSON definition
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import json
@@ -204,6 +204,12 @@ def detect_format_version(d):
return "2.17"
def detect_eol(text):
# Перевод строки ВСТАВКИ берём из самого файла: канон CRLF относится к файлам,
# которые мы создаём, а правка существующего сохраняет его стиль (#44/#46/#47).
crlf = text.count('\r\n')
return '\r\n' if crlf and crlf >= text.count('\n') - crlf else '\n'
def esc_xml(s):
"""Экранирование ТЕКСТА элемента: только & < > . Кавычки платформа в тексте не экранирует
(92142 сырых кавычки на корпус, ни одной &quot;); &quot; она принимает, но нормализует обратно."""
@@ -279,7 +285,7 @@ def write_child_subsystem_stub(child_path, child_name, format_version):
lines.append('\t\t<ChildObjects/>')
lines.append('\t</Subsystem>')
lines.append('</MetaDataObject>')
write_utf8_bom(child_path, '\n'.join(lines))
write_utf8_bom(child_path, '\r\n'.join(lines))
def main():
@@ -525,7 +531,7 @@ def main():
target_xml = os.path.join(subs_dir, f'{obj_name}.xml')
# Write XML
xml_content = '\n'.join(lines)
xml_content = '\r\n'.join(lines)
write_utf8_bom(target_xml, xml_content)
print(f"[OK] Created: {target_xml}")
@@ -555,9 +561,12 @@ def main():
parent_xml_path = config_xml
if parent_xml_path and os.path.exists(parent_xml_path):
with open(parent_xml_path, 'r', encoding='utf-8-sig') as f:
# newline='' => без трансляции переводов строк: иначе CRLF молча схлопнется
# в LF при чтении и файл будет переписан в LF (#44/#46/#47).
with open(parent_xml_path, 'r', encoding='utf-8-sig', newline='') as f:
raw_text = f.read()
eol = detect_eol(raw_text)
doc = ET.ElementTree(ET.fromstring(raw_text))
root = doc.getroot()
md_ns = 'http://v8.1c.ru/8.3/MDClasses'
@@ -593,10 +602,10 @@ def main():
if not already_exists:
# Use raw text manipulation to preserve formatting
if '<ChildObjects/>' in raw_text:
replacement = f'<ChildObjects>\n\t\t\t<Subsystem>{esc_xml(obj_name)}</Subsystem>\n\t\t</ChildObjects>'
replacement = ('<ChildObjects>' + eol + f'\t\t\t<Subsystem>{esc_xml(obj_name)}</Subsystem>' + eol + '\t\t</ChildObjects>')
raw_text = raw_text.replace('<ChildObjects/>', replacement, 1)
elif '</ChildObjects>' in raw_text:
insert_line = f'\t\t\t<Subsystem>{esc_xml(obj_name)}</Subsystem>\n'
insert_line = f'\t\t\t<Subsystem>{esc_xml(obj_name)}</Subsystem>' + eol
raw_text = raw_text.replace('</ChildObjects>', insert_line + '\t\t</ChildObjects>', 1)
write_utf8_bom(parent_xml_path, raw_text)
@@ -1,4 +1,4 @@
# subsystem-edit v1.10 — Edit existing 1C subsystem XML
# subsystem-edit v1.11 — Edit existing 1C subsystem XML
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)][Alias('Path')][string]$SubsystemPath,
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# subsystem-edit v1.10 — Edit existing 1C subsystem XML
# subsystem-edit v1.11 — Edit existing 1C subsystem XML
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -242,7 +242,7 @@ def write_child_subsystem_stub(child_path, child_name, format_version):
lines.append('\t\t<ChildObjects/>')
lines.append('\t</Subsystem>')
lines.append('</MetaDataObject>')
write_utf8_bom(child_path, '\n'.join(lines))
write_utf8_bom(child_path, '\r\n'.join(lines))
MD_NS = "http://v8.1c.ru/8.3/MDClasses"
XR_NS = "http://v8.1c.ru/8.3/xcf/readable"
@@ -1,4 +1,4 @@
# template-add v1.12 — Add template to 1C object
# template-add v1.14 — Add template to 1C object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory)]
@@ -260,7 +260,16 @@ $templateMetaXml = @"
</MetaDataObject>
"@
[System.IO.File]::WriteAllText($templateMetaPath, $templateMetaXml, $encBom)
# Канон выгрузки Конфигуратора: CRLF в разделителях строк, без перевода в конце
# файла. Скелет собирается here-string'ами, а .ps1 хранится с LF — отсюда LF и
# смешанный EOL. Нормализация здесь безопасна: многострочных текстовых узлов в
# скелете нет. Модули .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)
}
Write-XmlFile $templateMetaPath $templateMetaXml $encBom
# --- 2. Содержимое макета (Templates/<TemplateName>/Ext/Template.<ext>) ---
@@ -290,7 +299,7 @@ switch ($TemplateType) {
<SpreadsheetDocument xmlns="http://v8.1c.ru/spreadsheet/document" xmlns:ss="http://v8.1c.ru/spreadsheet/document" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:xs="http://www.w3.org/2001/XMLSchema">
</SpreadsheetDocument>
"@
[System.IO.File]::WriteAllText($templateFilePath, $content, $encBom)
Write-XmlFile $templateFilePath $content $encBom
}
"BinaryData" {
[System.IO.File]::WriteAllBytes($templateFilePath, @())
@@ -312,7 +321,7 @@ switch ($TemplateType) {
</dataSource>
</DataCompositionSchema>
"@
[System.IO.File]::WriteAllText($templateFilePath, $content, $encBom)
Write-XmlFile $templateFilePath $content $encBom
}
}
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# add-template v1.12 — Add template to 1C object
# add-template v1.14 — Add template to 1C object
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -1,4 +1,4 @@
# xdto-compile v1.3 — Build a 1C XDTO package from an XML Schema (XSD)
# xdto-compile v1.4 — Build a 1C XDTO package from an XML Schema (XSD)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory=$true, ParameterSetName='File')]
@@ -1,4 +1,4 @@
# xdto-compile v1.3 — Build a 1C XDTO package from an XML Schema (XSD) (Python port)
# xdto-compile v1.4 — Build a 1C XDTO package from an XML Schema (XSD) (Python port)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import json
@@ -960,6 +960,12 @@ if os.path.exists(config_xml):
else:
new_elem.tail = child_objects.text
data = etree.tostring(cfg_doc, xml_declaration=True, encoding="UTF-8")
# Парсер XML по спецификации схлопывает CRLF в LF, поэтому tostring отдаёт
# LF-документ. Возвращаем EOL исходного файла: правка существующего файла
# сохраняет его стиль (#44/#46/#47), а .NET-порт делает это через
# NewLineHandling — иначе порты расходятся побайтово.
src_eol = b"\r\n" if b"\r\n" in raw else b"\n"
data = data.replace(b"\r\n", b"\n").replace(b"\n", src_eol)
if had_bom:
data = b"\xef\xbb\xbf" + data
with open(config_xml, "wb") as f:
@@ -1,4 +1,4 @@
# xdto-edit v1.1 — Point edits of a 1C XDTO package
# xdto-edit v1.2 — Point edits of a 1C XDTO package
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
param(
[Parameter(Mandatory=$true)]
+12 -1
View File
@@ -1,4 +1,4 @@
# xdto-edit v1.1 — Point edits of a 1C XDTO package (Python port)
# xdto-edit v1.2 — Point edits of a 1C XDTO package (Python port)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import json
@@ -194,6 +194,17 @@ def invoke_sibling(script, argv, what):
def save_xml(doc, path):
raw = etree.tostring(doc, xml_declaration=True, encoding="UTF-8")
# Парсер XML по спецификации схлопывает CRLF в LF, поэтому tostring отдаёт
# LF-документ. Возвращаем EOL исходного файла: правка существующего файла
# сохраняет его стиль (#44/#46/#47), а .NET-порт делает это через
# NewLineHandling — иначе порты расходятся побайтово.
src_eol = b"\r\n"
try:
with open(path, "rb") as f:
src_eol = b"\r\n" if b"\r\n" in f.read() else b"\n"
except OSError:
pass
raw = raw.replace(b"\r\n", b"\n").replace(b"\n", src_eol)
with open(path, "wb") as f:
f.write(b"\xef\xbb\xbf" + raw)
@@ -26,4 +26,4 @@
</Item>
</ChildItems>
</Item>
</PredefinedData>
</PredefinedData>