diff --git a/frontend/create-spfx.ps1 b/frontend/create-spfx.ps1 index 7fd786c..631c327 100644 --- a/frontend/create-spfx.ps1 +++ b/frontend/create-spfx.ps1 @@ -9,6 +9,11 @@ $ErrorActionPreference = "Stop" +# Версия SPFx. Генератор ОБЯЗАН совпадать с версией пакетов ниже: +# старый генератор создаёт проект на gulp + node-sass, который не собирается +# на современных Node (node-gyp 3.8 требует Python 2). +$SpfxVersion = "1.22.1" + # ---- Цвета вывода ---- function Info { param($msg) Write-Host "[INFO] $msg" -ForegroundColor Cyan } function Success { param($msg) Write-Host "[OK] $msg" -ForegroundColor Green } @@ -32,35 +37,97 @@ try { } $nodeMajor = [int]($nodeVersion.TrimStart('v').Split('.')[0]) -if ($nodeMajor -lt 18) { - Warn "Обнаружен Node.js $nodeVersion. SPFx 1.20+ требует Node 18.x или выше." - Warn "Установите Node 18 LTS через nvm-windows или с https://nodejs.org" +# SPFx 1.22 объявляет engines: node >=22.14.0 <23.0.0 — на других мажорах +# генератор и rig просто не установятся. +if ($nodeMajor -ne 22) { + Warn "Обнаружен Node.js $nodeVersion. SPFx $SpfxVersion требует Node 22.x (>=22.14.0 <23)." + Warn "Переключитесь через nvm-windows: nvm install 22 && nvm use 22" exit 1 } Success "Node.js $nodeVersion — подходит." # --------------------------------------------------------- -# 2. Вспомогательная функция: установка глобального пакета +# 1a. Папка должна быть пустой # --------------------------------------------------------- -function Install-GlobalIfMissing { - param( - [string]$Package, - [string]$Command = "" - ) - if ($Command -eq "") { $Command = $Package.Split('/')[-1] } +$leftovers = @("package.json", ".yo-rc.json", "node_modules", "gulpfile.js", "tslint.json") | + Where-Object { Test-Path $_ } - if (-not (Get-Command $Command -ErrorAction SilentlyContinue)) { - Info "Устанавливаем $Package глобально..." - npm install -g $Package - Success "$Package установлен." - } else { - Success "$Package уже установлен." - } +if ($leftovers.Count -gt 0) { + Warn "В текущей папке уже есть следы предыдущей генерации: $($leftovers -join ', ')" + Warn "Скрипт рассчитан на ПУСТУЮ папку — иначе Yeoman смешает старый и новый проект." + Warn "Уберите служебные файлы и запустите заново:" + Warn " Remove-Item -Recurse -Force node_modules, config, package.json, package-lock.json, .yo-rc.json, gulpfile.js, tslint.json, tsconfig.json -ErrorAction SilentlyContinue" + Warn "ВНИМАНИЕ: src\ и teams\ здесь НЕ перечислены намеренно — в src может лежать ваш код." + Warn "Проверьте их содержимое вручную перед удалением." + exit 1 } -Install-GlobalIfMissing "@microsoft/generator-sharepoint" "yo" -Install-GlobalIfMissing "yo" "yo" -Install-GlobalIfMissing "@rushstack/heft" "heft" +# --------------------------------------------------------- +# 2. Вспомогательная функция: установка глобального пакета +# --------------------------------------------------------- +# Проверяем именно ПАКЕТ через "npm ls -g", а не наличие команды: +# у @microsoft/generator-sharepoint своего бинарника нет, и проверка по "yo" +# считала его установленным, оставляя старый генератор (1.11.0), который +# генерирует проект на @microsoft/sp-build-web + node-sass → node-gyp 3 → python2. +function Get-GlobalPackageVersion { + param([string]$Package) + + # Локально глушим Stop: "npm ls" отдаёт код 1 и пишет в stderr, когда пакета нет, + # а в Windows PowerShell 5.1 stderr нативного exe при Stop роняет скрипт. + $ErrorActionPreference = 'SilentlyContinue' + + $json = npm ls -g $Package --depth=0 --json 2>$null | Out-String + if ([string]::IsNullOrWhiteSpace($json)) { return $null } + + try { + $parsed = $json | ConvertFrom-Json + } catch { + return $null + } + + $dep = $parsed.dependencies.$Package + if ($null -eq $dep -or [string]::IsNullOrWhiteSpace($dep.version)) { return $null } + return $dep.version +} + +function Install-GlobalPackage { + param( + [string]$Package, + [string]$Version = "" # пустая строка = любая установленная версия подходит + ) + + $installed = Get-GlobalPackageVersion $Package + + if ($Version -ne "") { + if ($installed -eq $Version) { + Success "$Package@$Version уже установлен." + return + } + if ($installed) { + Warn "$Package установлен версии $installed, требуется $Version — переустанавливаем." + } else { + Info "Устанавливаем $Package@$Version глобально..." + } + npm install -g "$Package@$Version" + } else { + if ($installed) { + Success "$Package уже установлен (версия $installed)." + return + } + Info "Устанавливаем $Package глобально..." + npm install -g $Package + } + + if ($LASTEXITCODE -ne 0) { + Warn "Не удалось установить $Package. Прерываем." + exit 1 + } + Success "$Package установлен." +} + +Install-GlobalPackage "yo" +Install-GlobalPackage "@microsoft/generator-sharepoint" $SpfxVersion +Install-GlobalPackage "@rushstack/heft" # --------------------------------------------------------- # 3. Создание package.json @@ -105,10 +172,26 @@ Write-Host ">>> Сейчас откроется интерактивный ге Write-Host ">>> Выберите тип: WebPart, Framework: No framework (или React)." -ForegroundColor Yellow Write-Host "" -yo "@microsoft/sharepoint" +# --skip-install: не даём генератору поставить свой gulp-стек до того, +# как мы его выпилим. Иначе npm полезет собирать node-sass. +yo "@microsoft/sharepoint" --skip-install -npm uninstall @microsoft/sp-build-web ajv gulp -npm uninstall @microsoft/rush-stack-compiler-5.3 +if ($LASTEXITCODE -ne 0) { + Warn "Генератор Yeoman завершился с ошибкой. Прерываем." + exit 1 +} + +# Контроль: старый генератор мог протащить node-sass (нативная сборка, +# node-gyp 3.8, требует Python 2 — на текущих Node не собирается вообще). +if ((Test-Path "package.json") -and (Select-String -Path "package.json" -Pattern '"node-sass"' -Quiet)) { + Warn "В package.json попал node-sass — значит, отработал устаревший генератор SPFx." + Warn "Проверьте: npm ls -g @microsoft/generator-sharepoint (нужна $SpfxVersion)" + exit 1 +} + +Info "Убираем gulp-стек, оставшийся от генератора..." +# Одним вызовом: каждый npm uninstall тянет за собой полный install остального. +npm uninstall @microsoft/sp-build-web ajv gulp node-sass @microsoft/rush-stack-compiler-5.3 # --------------------------------------------------------- # 6. Установка Heft и Rush Stack зависимостей @@ -116,11 +199,11 @@ npm uninstall @microsoft/rush-stack-compiler-5.3 Info "Устанавливаем Heft и плагины..." $devDeps = @( - "@microsoft/spfx-web-build-rig@1.22.1", - "@microsoft/spfx-heft-plugins@1.22.1", - "@microsoft/eslint-config-spfx@1.22.1", + "@microsoft/spfx-web-build-rig@$SpfxVersion", + "@microsoft/spfx-heft-plugins@$SpfxVersion", + "@microsoft/eslint-config-spfx@$SpfxVersion", "@microsoft/eslint-plugin-spfx@1.22.17", - "@microsoft/sp-module-interfaces@1.22.1", + "@microsoft/sp-module-interfaces@$SpfxVersion", "@rushstack/eslint-config@4.5.2", "@rushstack/heft@1.1.2", "@types/heft-jest@1.0.2", @@ -244,12 +327,12 @@ Success ".gitignore создан." # 11. Финальный npm install # --------------------------------------------------------- $devDeps = @( - "@microsoft/sp-component-base@1.22.1", - "@microsoft/sp-core-library@1.22.1", - "@microsoft/sp-lodash-subset@1.22.1", - "@microsoft/sp-office-ui-fabric-core@1.22.1", - "@microsoft/sp-property-pane@1.22.1", - "@microsoft/sp-webpart-base@1.22.1" + "@microsoft/sp-component-base@$SpfxVersion", + "@microsoft/sp-core-library@$SpfxVersion", + "@microsoft/sp-lodash-subset@$SpfxVersion", + "@microsoft/sp-office-ui-fabric-core@$SpfxVersion", + "@microsoft/sp-property-pane@$SpfxVersion", + "@microsoft/sp-webpart-base@$SpfxVersion" ) Info "Финальный npm install..."