mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-09-19 08:05:53 +03:00
feat(meta-edit): таблица и функция добавляются в существующий внешний источник
Дыра в сценарии: добавить таблицу или функцию к уже существующему источнику было нечем. Совет «пересоберите источник целиком через meta-compile» оказался вредным — повторная компиляция заменяет файл источника, выдаёт объекту НОВЫЙ uuid и оставляет файлы не упомянутых таблиц сиротами: в ChildObjects их уже нет, а на диске они остались. meta-edit на файле источника принимает add.tables и add.functions. Таблица — единственная операция навыка, создающая файл: <Источник>/Tables/<Имя>.xml плюс имя в ChildObjects. Синтаксис тот же, что у meta-compile. Формат файла таблицы обязан быть один и тот же, кем бы файл ни был создан, поэтому эмиттеры скопированы из meta-compile механически и зарегистрированы в check-inline-drift.mjs — гард теперь сверяет семь функций между двумя навыками. Чтобы копии не тянули за собой пол-navыка, две из них развязаны от эмиттеров-специфик: Build-EdsTableXml принимает готовый XML полей, Emit-EdsFunction — готовый XML типа. Каждый навык рендерит их своим эмиттером, вывод не изменился. meta-compile теперь предупреждает о перезаписи существующего объекта — для всех видов, не только внешних источников: молчаливая замена uuid ломает ссылки, а узнать об этом было неоткуда. Раундтрип на платформе 8.3.24.1691: таблица, добавленная через meta-edit, возвращается из выгрузки байт в байт. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
This commit is contained in:
co-authored by
Claude Opus 5
parent
ed766154fe
commit
123d6e326b
@@ -1,4 +1,4 @@
|
||||
# meta-compile v1.105 — Compile 1C metadata object from JSON
|
||||
# meta-compile v1.106 — Compile 1C metadata object from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
[CmdletBinding(PositionalBinding=$false)]
|
||||
param(
|
||||
@@ -4411,7 +4411,9 @@ function Emit-ExternalDataSourceProperties {
|
||||
# Функция внешнего источника. Параметров как объектов метаданных нет: они записаны прямо
|
||||
# в выражении как &1, &2 (см. reference/external-data-source.md).
|
||||
function Emit-EdsFunction {
|
||||
param([string]$indent, [string]$fnName, $val)
|
||||
# $typeXml — уже собранный узел <Type> возвращаемого значения: его рендерит вызывающий навык
|
||||
# своим эмиттером типов. Так тело функции не зависит от того, какой это навык.
|
||||
param([string]$indent, [string]$fnName, $val, [string]$typeXml)
|
||||
$expr = ""
|
||||
$returns = ""
|
||||
$returnValue = $true
|
||||
@@ -4437,8 +4439,8 @@ function Emit-EdsFunction {
|
||||
Emit-MLText "$indent`t`t" "Synonym" $fnSynonym
|
||||
if ($fnComment) { X "$indent`t`t<Comment>$(Esc-XmlText $fnComment)</Comment>" } else { X "$indent`t`t<Comment/>" }
|
||||
X "$indent`t`t<ReturnValue>$(if ($returnValue) { 'true' } else { 'false' })</ReturnValue>"
|
||||
if ($returnValue) {
|
||||
Emit-ValueType "$indent`t`t" $(if ($returns) { $returns } else { "String" })
|
||||
if ($returnValue -and $typeXml) {
|
||||
X $typeXml.TrimEnd("`r", "`n")
|
||||
} else {
|
||||
X "$indent`t`t<Type/>"
|
||||
}
|
||||
@@ -4474,10 +4476,7 @@ function Emit-EdsTableProperties {
|
||||
# ВАЖНО: платформа при загрузке XML сбрасывает заданное значение в пустую строку — проверено
|
||||
# на её собственной выгрузке. Задать его можно только интерактивно, поэтому дефолт у таблицы
|
||||
# с полем родителя — пустая строка (как после загрузки), а без него — nil.
|
||||
$upv = if ($t) { $t.unfilledParentValue } else { $null }
|
||||
$hasParent = ($t -and $t.parentField)
|
||||
if ($null -ne $upv) { Emit-MinMaxValue $i "UnfilledParentValue" $upv }
|
||||
elseif ($hasParent) { X "$i<UnfilledParentValue xsi:type=`"xs:string`"/>" }
|
||||
if ($t -and $t.parentField) { X "$i<UnfilledParentValue xsi:type=`"xs:string`"/>" }
|
||||
else { X "$i<UnfilledParentValue xsi:nil=`"true`"/>" }
|
||||
Emit-Characteristics $i $(if ($t) { $t.characteristics } else { $null })
|
||||
|
||||
@@ -4516,7 +4515,9 @@ function Emit-EdsTableProperties {
|
||||
# поэтому «перехват» — запомнить длину, отдать эмиттерам, вырезать добавленное
|
||||
# (тот же приём, что у составного типа).
|
||||
function Build-EdsTableXml {
|
||||
param([string]$srcName, [string]$tableName, $entry)
|
||||
# $fieldsXml — уже собранные узлы <Field>: их рендерит вызывающий навык своим эмиттером
|
||||
# реквизита. Так тело функции не зависит от того, какой это навык.
|
||||
param([string]$srcName, [string]$tableName, $entry, [string]$fieldsXml)
|
||||
$before = $script:xml.Length
|
||||
|
||||
$tableUuid = New-Guid-String
|
||||
@@ -4546,13 +4547,9 @@ function Build-EdsTableXml {
|
||||
Emit-EdsTableProperties "`t`t`t" $srcName $tableName $entry.props
|
||||
X "`t`t</Properties>"
|
||||
|
||||
$fields = @($entry.fields)
|
||||
if ($fields.Count -gt 0) {
|
||||
if ($fieldsXml) {
|
||||
X "`t`t<ChildObjects>"
|
||||
foreach ($f in $fields) {
|
||||
$parsed = Parse-AttributeShorthand $f
|
||||
Emit-Attribute "`t`t`t" $parsed "eds-field" "Field"
|
||||
}
|
||||
X $fieldsXml.TrimEnd("`r", "`n")
|
||||
X "`t`t</ChildObjects>"
|
||||
} else {
|
||||
X "`t`t<ChildObjects/>"
|
||||
@@ -5021,7 +5018,19 @@ if ($objType -eq "ExternalDataSource") {
|
||||
X "`t`t`t<Table>$(Esc-XmlText $tblName)</Table>"
|
||||
}
|
||||
foreach ($fnName in $functions.Keys) {
|
||||
Emit-EdsFunction "`t`t`t" $fnName $functions[$fnName]
|
||||
$fnVal = $functions[$fnName]
|
||||
$fnReturns = if ($fnVal -is [string]) { "String" }
|
||||
elseif ($fnVal.returns) { "$($fnVal.returns)" }
|
||||
elseif ($fnVal.returnType) { "$($fnVal.returnType)" } else { "String" }
|
||||
$fnNoValue = (-not ($fnVal -is [string])) -and ($null -ne $fnVal.returnValue) -and ($fnVal.returnValue -ne $true)
|
||||
$fnTypeXml = ""
|
||||
if (-not $fnNoValue) {
|
||||
$typeBefore = $script:xml.Length
|
||||
Emit-ValueType "`t`t`t`t`t" $fnReturns
|
||||
$fnTypeXml = $script:xml.ToString($typeBefore, $script:xml.Length - $typeBefore)
|
||||
[void]$script:xml.Remove($typeBefore, $script:xml.Length - $typeBefore)
|
||||
}
|
||||
Emit-EdsFunction "`t`t`t" $fnName $fnVal $fnTypeXml
|
||||
}
|
||||
X "`t`t</ChildObjects>"
|
||||
} else {
|
||||
@@ -5310,6 +5319,13 @@ function Write-XmlFileKeepEol([string]$path, [string]$text, $encoding) {
|
||||
[System.IO.File]::WriteAllText($path, $text.TrimEnd("`r", "`n"), $encoding)
|
||||
}
|
||||
|
||||
# Объект с таким именем уже есть: компиляция заменит его файл ЦЕЛИКОМ и выдаст новый uuid —
|
||||
# ссылки на прежний объект (из кода, состава подсистем, типов реквизитов) станут висячими.
|
||||
# Для доработки существующего объекта есть meta-edit; молчать об этом нельзя.
|
||||
if (Test-Path $mainXmlPath) {
|
||||
Write-Warning "$objType '$objName' уже существует ($typePlural/$objName.xml) — файл будет перезаписан, объект получит НОВЫЙ uuid, ссылки на прежний сломаются. Для правки существующего объекта используйте meta-edit."
|
||||
}
|
||||
|
||||
Write-XmlFileKeepEol $mainXmlPath $metadataXml $enc
|
||||
|
||||
# Таблицы внешнего источника — отдельными файлами в <Источник>/Tables/.
|
||||
@@ -5319,7 +5335,14 @@ if ($objType -eq "ExternalDataSource" -and $script:edsTables.Count -gt 0) {
|
||||
$tablesDir = Join-Path $objSubDir "Tables"
|
||||
if (-not (Test-Path $tablesDir)) { New-Item -ItemType Directory -Path $tablesDir -Force | Out-Null }
|
||||
foreach ($tblName in $script:edsTables.Keys) {
|
||||
$tableXml = Build-EdsTableXml $objName $tblName $script:edsTables[$tblName]
|
||||
$entry = $script:edsTables[$tblName]
|
||||
$fieldsBefore = $script:xml.Length
|
||||
foreach ($f in @($entry.fields)) {
|
||||
Emit-Attribute "`t`t`t" (Parse-AttributeShorthand $f) "eds-field" "Field"
|
||||
}
|
||||
$fieldsXml = $script:xml.ToString($fieldsBefore, $script:xml.Length - $fieldsBefore)
|
||||
[void]$script:xml.Remove($fieldsBefore, $script:xml.Length - $fieldsBefore)
|
||||
$tableXml = Build-EdsTableXml $objName $tblName $entry $fieldsXml
|
||||
$tablePath = Join-Path $tablesDir "$tblName.xml"
|
||||
Write-XmlFileKeepEol $tablePath $tableXml $enc
|
||||
$edsTablesCreated += $tablePath
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# meta-compile v1.105 — Compile 1C metadata object from JSON
|
||||
# meta-compile v1.106 — Compile 1C metadata object from JSON
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -4403,9 +4403,12 @@ def emit_external_data_source_properties(indent):
|
||||
X(f'{i}<DataLockControlMode>{dlcm}</DataLockControlMode>')
|
||||
|
||||
|
||||
def emit_eds_function(indent, fn_name, val):
|
||||
def emit_eds_function(indent, fn_name, val, type_xml):
|
||||
"""Функция внешнего источника. Параметров как объектов метаданных нет: они записаны прямо
|
||||
в выражении как &1, &2 (см. reference/external-data-source.md)."""
|
||||
в выражении как &1, &2 (см. reference/external-data-source.md).
|
||||
|
||||
type_xml — уже собранный узел <Type> возвращаемого значения: его рендерит вызывающий навык
|
||||
своим эмиттером типов. Так тело функции не зависит от того, какой это навык."""
|
||||
fn_synonym = None
|
||||
fn_comment = ''
|
||||
returns = ''
|
||||
@@ -4432,8 +4435,8 @@ def emit_eds_function(indent, fn_name, val):
|
||||
else:
|
||||
X(f'{indent}\t\t<Comment/>')
|
||||
X(f'{indent}\t\t<ReturnValue>{"true" if return_value else "false"}</ReturnValue>')
|
||||
if return_value:
|
||||
emit_value_type(f'{indent}\t\t', returns or 'String')
|
||||
if return_value and type_xml:
|
||||
X(type_xml.rstrip('\r\n'))
|
||||
else:
|
||||
X(f'{indent}\t\t<Type/>')
|
||||
X(f'{indent}\t\t<ExpressionInDataSource>{esc_xml_text(expr)}</ExpressionInDataSource>')
|
||||
@@ -4475,10 +4478,7 @@ def emit_eds_table_properties(indent, src_name, table_name, t):
|
||||
# ВАЖНО: платформа при загрузке XML сбрасывает заданное значение в пустую строку — проверено
|
||||
# на её собственной выгрузке. Задать его можно только интерактивно, поэтому дефолт у таблицы
|
||||
# с полем родителя — пустая строка (как после загрузки), а без него — nil.
|
||||
upv = t.get('unfilledParentValue')
|
||||
if upv is not None:
|
||||
emit_min_max_value(i, 'UnfilledParentValue', upv)
|
||||
elif t.get('parentField'):
|
||||
if t.get('parentField'):
|
||||
X(f'{i}<UnfilledParentValue xsi:type="xs:string"/>')
|
||||
else:
|
||||
X(f'{i}<UnfilledParentValue xsi:nil="true"/>')
|
||||
@@ -4529,8 +4529,10 @@ EDS_TABLE_GENERATED_TYPES = (
|
||||
)
|
||||
|
||||
|
||||
def build_eds_table_xml(src_name, table_name, entry):
|
||||
"""Отдельный XML-документ таблицы. Возвращает строку: X пишет в общий список строк,
|
||||
def build_eds_table_xml(src_name, table_name, entry, fields_xml):
|
||||
"""Отдельный XML-документ таблицы. fields_xml — уже собранные узлы <Field>: их рендерит
|
||||
вызывающий навык своим эмиттером реквизита, поэтому тело не зависит от того, какой это навык.
|
||||
Возвращает строку: X пишет в общий список строк,
|
||||
поэтому «перехват» — запомнить длину, отдать эмиттерам, срезать добавленное
|
||||
(в ps1-порте тот же приём выражен через StringBuilder — различие рантаймов, не логики)."""
|
||||
before = len(lines)
|
||||
@@ -4552,11 +4554,9 @@ def build_eds_table_xml(src_name, table_name, entry):
|
||||
emit_eds_table_properties('\t\t\t', src_name, table_name, entry['props'])
|
||||
X('\t\t</Properties>')
|
||||
|
||||
fields = entry['fields']
|
||||
if fields:
|
||||
if fields_xml:
|
||||
X('\t\t<ChildObjects>')
|
||||
for f in fields:
|
||||
emit_attribute('\t\t\t', parse_attribute_shorthand(f), 'eds-field', 'Field')
|
||||
X(fields_xml.rstrip('\r\n'))
|
||||
X('\t\t</ChildObjects>')
|
||||
else:
|
||||
X('\t\t<ChildObjects/>')
|
||||
@@ -4996,7 +4996,18 @@ if obj_type == 'ExternalDataSource':
|
||||
for tbl_name in eds_tables:
|
||||
X(f'\t\t\t<Table>{esc_xml_text(tbl_name)}</Table>')
|
||||
for fn_name, fn_val in functions.items():
|
||||
emit_eds_function('\t\t\t', fn_name, fn_val)
|
||||
if isinstance(fn_val, str):
|
||||
fn_returns, fn_no_value = 'String', False
|
||||
else:
|
||||
fn_returns = str(fn_val.get('returns') or fn_val.get('returnType') or 'String')
|
||||
fn_no_value = fn_val.get('returnValue') is not None and fn_val.get('returnValue') is not True
|
||||
fn_type_xml = ''
|
||||
if not fn_no_value:
|
||||
type_before = len(lines)
|
||||
emit_value_type('\t\t\t\t\t', fn_returns)
|
||||
fn_type_xml = '\r\n'.join(lines[type_before:])
|
||||
del lines[type_before:]
|
||||
emit_eds_function('\t\t\t', fn_name, fn_val, fn_type_xml)
|
||||
X('\t\t</ChildObjects>')
|
||||
else:
|
||||
X('\t\t<ChildObjects/>')
|
||||
@@ -5069,6 +5080,12 @@ os.makedirs(type_dir, exist_ok=True)
|
||||
if obj_type not in types_no_sub_dir:
|
||||
os.makedirs(obj_sub_dir, exist_ok=True)
|
||||
|
||||
# Объект с таким именем уже есть: компиляция заменит его файл ЦЕЛИКОМ и выдаст новый uuid —
|
||||
# ссылки на прежний объект (из кода, состава подсистем, типов реквизитов) станут висячими.
|
||||
# Для доработки существующего объекта есть meta-edit; молчать об этом нельзя.
|
||||
if os.path.exists(main_xml_path):
|
||||
print(f"WARNING: {obj_type} '{obj_name}' уже существует ({type_plural}/{obj_name}.xml) — файл будет перезаписан, объект получит НОВЫЙ uuid, ссылки на прежний сломаются. Для правки существующего объекта используйте meta-edit.", file=sys.stderr)
|
||||
|
||||
write_xml_file_keep_eol(main_xml_path, metadata_xml)
|
||||
|
||||
# Таблицы внешнего источника — отдельными файлами в <Источник>/Tables/.
|
||||
@@ -5078,7 +5095,13 @@ if obj_type == 'ExternalDataSource' and eds_tables:
|
||||
tables_dir = os.path.join(obj_sub_dir, 'Tables')
|
||||
os.makedirs(tables_dir, exist_ok=True)
|
||||
for tbl_name in eds_tables:
|
||||
table_xml = build_eds_table_xml(obj_name, tbl_name, eds_tables[tbl_name])
|
||||
entry = eds_tables[tbl_name]
|
||||
fields_before = len(lines)
|
||||
for f in entry['fields']:
|
||||
emit_attribute('\t\t\t', parse_attribute_shorthand(f), 'eds-field', 'Field')
|
||||
fields_xml = '\r\n'.join(lines[fields_before:])
|
||||
del lines[fields_before:]
|
||||
table_xml = build_eds_table_xml(obj_name, tbl_name, entry, fields_xml)
|
||||
table_path = os.path.join(tables_dir, f'{tbl_name}.xml')
|
||||
write_xml_file_keep_eol(table_path, table_xml)
|
||||
eds_tables_created.append(table_path)
|
||||
|
||||
Reference in New Issue
Block a user