mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-09-20 09:15:52 +03:00
fix(db-repo,db-load-cf,epf-build): объявления цепочки v8path выше точки вызова
PowerShell ищет функцию в момент вызова и видит только уже выполнившиеся объявления. В трёх навыках блок Find-V8Project/Test-SamePath/Find-ProjectDatabase оказался ниже строки, где отрабатывает Find-ProjectV8Path: CommandNotFoundException уходил в stderr, результат становился $null, и выбор платформы по записи базы молча откатывался на корневой v8path. Порты .py не задеты — Python связывает имя при вызове, так что расхождение PS↔PY было бы тихим. Гард check-ps-define-before-call.mjs проверяет порядок объявлений во всех .ps1, где отрабатывает эта цепочка (15 навыков), и на прежнем состоянии даёт ровно те три диагностики. Кейс db-repo/v8path-from-database ловит тот же дефект поведением. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
874ae650ca
commit
77c797e6d0
@@ -1,4 +1,4 @@
|
|||||||
# db-load-cf v1.18 — Load 1C configuration from CF file
|
# db-load-cf v1.19 — Load 1C configuration from CF file
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
# NB: *nix-раскладку платформы (/opt/1cv8/<ver>/1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется.
|
# NB: *nix-раскладку платформы (/opt/1cv8/<ver>/1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется.
|
||||||
<#
|
<#
|
||||||
@@ -303,6 +303,17 @@ Assert-InfoBaseExists $InfoBasePath
|
|||||||
# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт
|
# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт
|
||||||
# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём,
|
# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём,
|
||||||
# что в cf-edit.ps1 (сопоставление по configSrc).
|
# что в cf-edit.ps1 (сопоставление по configSrc).
|
||||||
|
function Find-V8Project([string]$startDir) {
|
||||||
|
$d = $startDir
|
||||||
|
for ($i = 0; $i -lt 20 -and $d; $i++) {
|
||||||
|
$pj = Join-Path $d ".v8-project.json"
|
||||||
|
if (Test-Path $pj) { return $pj }
|
||||||
|
$parent = [System.IO.Path]::GetDirectoryName($d)
|
||||||
|
if ($parent -eq $d) { break }
|
||||||
|
$d = $parent
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
function Test-SamePath {
|
function Test-SamePath {
|
||||||
param([string]$A, [string]$B)
|
param([string]$A, [string]$B)
|
||||||
if (-not $A -or -not $B) { return $false }
|
if (-not $A -or -not $B) { return $false }
|
||||||
@@ -446,18 +457,6 @@ function Write-PlatformOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function Find-V8Project([string]$startDir) {
|
|
||||||
$d = $startDir
|
|
||||||
for ($i = 0; $i -lt 20 -and $d; $i++) {
|
|
||||||
$pj = Join-Path $d ".v8-project.json"
|
|
||||||
if (Test-Path $pj) { return $pj }
|
|
||||||
$parent = [System.IO.Path]::GetDirectoryName($d)
|
|
||||||
if ($parent -eq $d) { break }
|
|
||||||
$d = $parent
|
|
||||||
}
|
|
||||||
return $null
|
|
||||||
}
|
|
||||||
|
|
||||||
# Постусловие применимости расширения: платформа отчитывается успехом и о расширении, которое
|
# Постусловие применимости расширения: платформа отчитывается успехом и о расширении, которое
|
||||||
# не применит — отказ всплывает лениво, при первом вызове метода, записью в журнал регистрации.
|
# не применит — отказ всплывает лениво, при первом вызове метода, записью в журнал регистрации.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# db-load-cf v1.18 — Load 1C configuration from CF file
|
# db-load-cf v1.19 — Load 1C configuration from CF file
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# db-repo v1.15 — 1C configuration repository operations
|
# db-repo v1.16 — 1C configuration repository operations
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
# NB: движок только 1cv8 — ibcmd работу с хранилищем не поддерживает (нет такого режима).
|
# NB: движок только 1cv8 — ibcmd работу с хранилищем не поддерживает (нет такого режима).
|
||||||
<#
|
<#
|
||||||
@@ -320,6 +320,46 @@ function Assert-InfoBaseExists {
|
|||||||
|
|
||||||
Assert-InfoBaseExists $InfoBasePath
|
Assert-InfoBaseExists $InfoBasePath
|
||||||
|
|
||||||
|
# --- Запись базы в .v8-project.json ---
|
||||||
|
# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт
|
||||||
|
# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём,
|
||||||
|
# что в cf-edit.ps1 (сопоставление по configSrc).
|
||||||
|
function Find-V8Project([string]$startDir) {
|
||||||
|
$d = $startDir
|
||||||
|
for ($i = 0; $i -lt 20 -and $d; $i++) {
|
||||||
|
$pj = Join-Path $d ".v8-project.json"
|
||||||
|
if (Test-Path $pj) { return $pj }
|
||||||
|
$parent = [System.IO.Path]::GetDirectoryName($d)
|
||||||
|
if ($parent -eq $d) { break }
|
||||||
|
$d = $parent
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
function Test-SamePath {
|
||||||
|
param([string]$A, [string]$B)
|
||||||
|
if (-not $A -or -not $B) { return $false }
|
||||||
|
try {
|
||||||
|
$na = [System.IO.Path]::GetFullPath($A).TrimEnd('\', '/')
|
||||||
|
$nb = [System.IO.Path]::GetFullPath($B).TrimEnd('\', '/')
|
||||||
|
return $na.Equals($nb, [System.StringComparison]::OrdinalIgnoreCase)
|
||||||
|
} catch { return $false }
|
||||||
|
}
|
||||||
|
function Find-ProjectDatabase {
|
||||||
|
# Запись базы в реестре, соответствующая переданному соединению. $null, если не найдена.
|
||||||
|
$pf = Find-V8Project (Get-Location).Path
|
||||||
|
if (-not $pf) { return $null }
|
||||||
|
try { $proj = Get-Content $pf -Raw -Encoding UTF8 | ConvertFrom-Json } catch { return $null }
|
||||||
|
if (-not $proj.databases) { return $null }
|
||||||
|
foreach ($db in $proj.databases) {
|
||||||
|
if ($InfoBasePath -and $db.path -and (Test-SamePath $db.path $InfoBasePath)) { return $db }
|
||||||
|
if ($InfoBaseServer -and $InfoBaseRef -and $db.server -and $db.ref) {
|
||||||
|
if ($db.server.Equals($InfoBaseServer, [System.StringComparison]::OrdinalIgnoreCase) -and
|
||||||
|
$db.ref.Equals($InfoBaseRef, [System.StringComparison]::OrdinalIgnoreCase)) { return $db }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
# --- Resolve V8Path ---
|
# --- Resolve V8Path ---
|
||||||
function Find-ProjectV8Path {
|
function Find-ProjectV8Path {
|
||||||
# v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях
|
# v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях
|
||||||
@@ -503,47 +543,6 @@ function Invoke-PlatformProcess {
|
|||||||
return [pscustomobject]@{ Output = $out; ExitCode = $p.ExitCode }
|
return [pscustomobject]@{ Output = $out; ExitCode = $p.ExitCode }
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- Запись базы в .v8-project.json ---
|
|
||||||
# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт
|
|
||||||
# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём,
|
|
||||||
# что в cf-edit.ps1 (сопоставление по configSrc).
|
|
||||||
function Find-V8Project([string]$startDir) {
|
|
||||||
$d = $startDir
|
|
||||||
for ($i = 0; $i -lt 20 -and $d; $i++) {
|
|
||||||
$pj = Join-Path $d ".v8-project.json"
|
|
||||||
if (Test-Path $pj) { return $pj }
|
|
||||||
$parent = [System.IO.Path]::GetDirectoryName($d)
|
|
||||||
if ($parent -eq $d) { break }
|
|
||||||
$d = $parent
|
|
||||||
}
|
|
||||||
return $null
|
|
||||||
}
|
|
||||||
function Test-SamePath {
|
|
||||||
param([string]$A, [string]$B)
|
|
||||||
if (-not $A -or -not $B) { return $false }
|
|
||||||
try {
|
|
||||||
$na = [System.IO.Path]::GetFullPath($A).TrimEnd('\', '/')
|
|
||||||
$nb = [System.IO.Path]::GetFullPath($B).TrimEnd('\', '/')
|
|
||||||
return $na.Equals($nb, [System.StringComparison]::OrdinalIgnoreCase)
|
|
||||||
} catch { return $false }
|
|
||||||
}
|
|
||||||
|
|
||||||
function Find-ProjectDatabase {
|
|
||||||
# Запись базы в реестре, соответствующая переданному соединению. $null, если не найдена.
|
|
||||||
$pf = Find-V8Project (Get-Location).Path
|
|
||||||
if (-not $pf) { return $null }
|
|
||||||
try { $proj = Get-Content $pf -Raw -Encoding UTF8 | ConvertFrom-Json } catch { return $null }
|
|
||||||
if (-not $proj.databases) { return $null }
|
|
||||||
foreach ($db in $proj.databases) {
|
|
||||||
if ($InfoBasePath -and $db.path -and (Test-SamePath $db.path $InfoBasePath)) { return $db }
|
|
||||||
if ($InfoBaseServer -and $InfoBaseRef -and $db.server -and $db.ref) {
|
|
||||||
if ($db.server.Equals($InfoBaseServer, [System.StringComparison]::OrdinalIgnoreCase) -and
|
|
||||||
$db.ref.Equals($InfoBaseRef, [System.StringComparison]::OrdinalIgnoreCase)) { return $db }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $null
|
|
||||||
}
|
|
||||||
|
|
||||||
function Resolve-RepositorySettings {
|
function Resolve-RepositorySettings {
|
||||||
# Возвращает @{ Path; User; Password; FromRegistry }. Явные -Repository* всегда сильнее реестра.
|
# Возвращает @{ Path; User; Password; FromRegistry }. Явные -Repository* всегда сильнее реестра.
|
||||||
$dbRec = Find-ProjectDatabase
|
$dbRec = Find-ProjectDatabase
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# db-repo v1.15 — 1C configuration repository operations
|
# db-repo v1.16 — 1C configuration repository operations
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
# NB: движок только 1cv8 — ibcmd работу с хранилищем не поддерживает (нет такого режима).
|
# NB: движок только 1cv8 — ibcmd работу с хранилищем не поддерживает (нет такого режима).
|
||||||
"""Работа с хранилищем конфигурации 1С.
|
"""Работа с хранилищем конфигурации 1С.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# epf-build v1.18 — Build external data processor or report (EPF/ERF) from XML sources
|
# epf-build v1.19 — Build external data processor or report (EPF/ERF) from XML sources
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
# NB: *nix-раскладку платформы (/opt/1cv8/<ver>/1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется.
|
# NB: *nix-раскладку платформы (/opt/1cv8/<ver>/1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется.
|
||||||
<#
|
<#
|
||||||
@@ -280,6 +280,17 @@ Assert-InfoBaseExists $InfoBasePath
|
|||||||
# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт
|
# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт
|
||||||
# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём,
|
# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём,
|
||||||
# что в cf-edit.ps1 (сопоставление по configSrc).
|
# что в cf-edit.ps1 (сопоставление по configSrc).
|
||||||
|
function Find-V8Project([string]$startDir) {
|
||||||
|
$d = $startDir
|
||||||
|
for ($i = 0; $i -lt 20 -and $d; $i++) {
|
||||||
|
$pj = Join-Path $d ".v8-project.json"
|
||||||
|
if (Test-Path $pj) { return $pj }
|
||||||
|
$parent = [System.IO.Path]::GetDirectoryName($d)
|
||||||
|
if ($parent -eq $d) { break }
|
||||||
|
$d = $parent
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
function Test-SamePath {
|
function Test-SamePath {
|
||||||
param([string]$A, [string]$B)
|
param([string]$A, [string]$B)
|
||||||
if (-not $A -or -not $B) { return $false }
|
if (-not $A -or -not $B) { return $false }
|
||||||
@@ -430,18 +441,6 @@ function Test-OutputNonEmpty {
|
|||||||
return (Test-Path $Path -PathType Leaf) -and ((Get-Item $Path -ErrorAction SilentlyContinue).Length -gt 0)
|
return (Test-Path $Path -PathType Leaf) -and ((Get-Item $Path -ErrorAction SilentlyContinue).Length -gt 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Find-V8Project([string]$startDir) {
|
|
||||||
$d = $startDir
|
|
||||||
for ($i = 0; $i -lt 20 -and $d; $i++) {
|
|
||||||
$pj = Join-Path $d ".v8-project.json"
|
|
||||||
if (Test-Path $pj) { return $pj }
|
|
||||||
$parent = [System.IO.Path]::GetDirectoryName($d)
|
|
||||||
if ($parent -eq $d) { break }
|
|
||||||
$d = $parent
|
|
||||||
}
|
|
||||||
return $null
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Проверка исходников платформой ---
|
# --- Проверка исходников платформой ---
|
||||||
# Сборка .epf/.erf ничего не проверяет: /LoadExternalDataProcessorOrReportFromFiles упаковывает XML
|
# Сборка .epf/.erf ничего не проверяет: /LoadExternalDataProcessorOrReportFromFiles упаковывает XML
|
||||||
# и модули не компилирует, поэтому сломанный модуль доезжает до пользователя и падает при открытии
|
# и модули не компилирует, поэтому сломанный модуль доезжает до пользователя и падает при открытии
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# epf-build v1.18 — Build external data processor or report (EPF/ERF) from XML sources
|
# epf-build v1.19 — Build external data processor or report (EPF/ERF) from XML sources
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "Платформа берётся из v8path записи базы",
|
||||||
|
"fakePlatform": {
|
||||||
|
"log": "---- Начало операции с хранилищем конфигурации ----\r\nОбъект не был изменен: Справочник.Номенклатура\r\n---- Операция с хранилищем конфигурации завершена ----\r\nПомещение изменений объектов в хранилище успешно завершено\r\n"
|
||||||
|
},
|
||||||
|
"preRun": [
|
||||||
|
{
|
||||||
|
"writeFile": {
|
||||||
|
"path": ".v8-project.json",
|
||||||
|
"content": {
|
||||||
|
"v8path": "{workDir}/nowhere/1cv8.exe",
|
||||||
|
"databases": [
|
||||||
|
{ "id": "dev", "name": "Dev", "type": "file", "path": "{workDir}/ib", "v8path": "{fakePlatform}" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"args_extra": [
|
||||||
|
"-InfoBasePath", "{workDir}/ib",
|
||||||
|
"-RepositoryPath", "{workDir}/repo",
|
||||||
|
"-RepositoryUser", "Admin",
|
||||||
|
"-Command", "commit",
|
||||||
|
"-Objects", "Справочник.Номенклатура",
|
||||||
|
"-Comment", "x"
|
||||||
|
],
|
||||||
|
"cwd": "workDir",
|
||||||
|
"expect": {
|
||||||
|
"stdoutContains": "Новая версия в хранилище НЕ создана",
|
||||||
|
"stdoutNotContains": "nowhere"
|
||||||
|
},
|
||||||
|
"noSnapshot": "проверяется выбор платформы по stdout, файлов на выходе нет"
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ const GUARDS = [
|
|||||||
['check-uuid-invariant.mjs', 'сохранение uuid объекта и сущностей при правке'],
|
['check-uuid-invariant.mjs', 'сохранение uuid объекта и сущностей при правке'],
|
||||||
['check-minimal-diff.mjs', 'правка роли: диф только там, где просили'],
|
['check-minimal-diff.mjs', 'правка роли: диф только там, где просили'],
|
||||||
['check-inline-drift.mjs', 'общие inline-реализации: копии совпадают с эталонами'],
|
['check-inline-drift.mjs', 'общие inline-реализации: копии совпадают с эталонами'],
|
||||||
|
['check-ps-define-before-call.mjs', 'порядок в .ps1: функция объявлена выше точки вызова'],
|
||||||
['check-type-maps.mjs', 'карты типов метаданных: согласованы со спецификацией'],
|
['check-type-maps.mjs', 'карты типов метаданных: согласованы со спецификацией'],
|
||||||
['check-format-versions.mjs', 'проверенный диапазон версий формата: согласован со спецификацией'],
|
['check-format-versions.mjs', 'проверенный диапазон версий формата: согласован со спецификацией'],
|
||||||
['check-form-purposes.mjs', 'назначения форм в form-add: согласованы со спецификацией и между портами'],
|
['check-form-purposes.mjs', 'назначения форм в form-add: согласованы со спецификацией и между портами'],
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
// PowerShell ищет функцию в момент ВЫЗОВА и видит только те определения, которые к этому
|
||||||
|
// моменту уже выполнились. Объявление ниже точки вызова отказывает молчаливо-наполовину:
|
||||||
|
// CommandNotFoundException уходит в stderr, вызывающая функция получает $null и спокойно
|
||||||
|
// идёт по ветке «не нашли». Так per-database v8path оказался no-op сразу в трёх навыках
|
||||||
|
// (#93) — тесты этого не заметили, потому что откат на корневой v8path выглядит штатно.
|
||||||
|
//
|
||||||
|
// В .py-порте такого класса ошибок нет: Python связывает имя при вызове, порядок определений
|
||||||
|
// в модуле не важен. Поэтому гард только для .ps1 — и поэтому расхождение портов молчаливое.
|
||||||
|
//
|
||||||
|
// Что проверяем: для каждой функции из списка — если навык её вызывает на верхнем уровне
|
||||||
|
// (или из функции, которая вызывается на верхнем уровне), определение обязано стоять выше
|
||||||
|
// первой строки, где начинается цепочка.
|
||||||
|
//
|
||||||
|
// Запуск: node tests/skills/check-ps-define-before-call.mjs
|
||||||
|
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { dirname, join } from 'node:path';
|
||||||
|
|
||||||
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
||||||
|
const SKILLS = join(ROOT, '.claude', 'skills');
|
||||||
|
|
||||||
|
// Цепочка выбора платформы: кто кого зовёт. Точка входа вызывается с верхнего уровня.
|
||||||
|
const CHAINS = [
|
||||||
|
{
|
||||||
|
entry: 'Find-ProjectV8Path',
|
||||||
|
// вызов точки входа на верхнем уровне скрипта
|
||||||
|
entryCall: /^\s*\$V8Path\s*=\s*Find-ProjectV8Path\s*$/,
|
||||||
|
callees: ['Find-ProjectDatabase', 'Test-SamePath', 'Find-V8Project'],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const errors = [];
|
||||||
|
let checked = 0;
|
||||||
|
|
||||||
|
for (const skill of readdirSync(SKILLS)) {
|
||||||
|
const scriptsDir = join(SKILLS, skill, 'scripts');
|
||||||
|
if (!existsSync(scriptsDir)) continue;
|
||||||
|
for (const file of readdirSync(scriptsDir)) {
|
||||||
|
if (!file.endsWith('.ps1')) continue;
|
||||||
|
const path = join(scriptsDir, file);
|
||||||
|
const lines = readFileSync(path, 'utf8').split('\n');
|
||||||
|
|
||||||
|
for (const chain of CHAINS) {
|
||||||
|
const callLine = lines.findIndex(l => chain.entryCall.test(l));
|
||||||
|
if (callLine < 0) continue;
|
||||||
|
checked++;
|
||||||
|
const defLine = name => lines.findIndex(l => l.trimStart().startsWith(`function ${name}`));
|
||||||
|
for (const name of [chain.entry, ...chain.callees]) {
|
||||||
|
const def = defLine(name);
|
||||||
|
if (def < 0) {
|
||||||
|
errors.push(`${skill}/${file}: ${chain.entry} зовёт ${name}, но в навыке её нет`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (def > callLine) {
|
||||||
|
errors.push(`${skill}/${file}: function ${name} объявлена на строке ${def + 1}, `
|
||||||
|
+ `а цепочка ${chain.entry} выполняется на строке ${callLine + 1} — `
|
||||||
|
+ `PowerShell её не увидит (отказ уйдёт в stderr, результат станет $null)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.length) {
|
||||||
|
console.log(`${errors.length} РАСХОЖДЕНИЙ:`);
|
||||||
|
for (const e of errors) console.log(` [ERROR] ${e}`);
|
||||||
|
console.log('\nПоднимите определения выше точки вызова — порядок в .ps1 значим.');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Проверено цепочек: ${checked}`);
|
||||||
|
console.log('OK — все функции объявлены выше точки вызова.');
|
||||||
Reference in New Issue
Block a user