Фронтенд для сервиса (#5)
Tests / test (push) Successful in 2m26s

Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz>
Reviewed-on: http://git.tnsu.kz/gorbenko.pavel/tnsu-approval-route/pulls/5
This commit was merged in pull request #5.
This commit is contained in:
2026-07-30 06:11:24 +00:00
parent 5476c1bab5
commit da158cfd84
114 changed files with 26045 additions and 2 deletions
+396
View File
@@ -0,0 +1,396 @@
# =============================================================
# SPFx + Heft project bootstrap script (PowerShell)
# Запустите из корня вашей ПУСТОЙ папки проекта:
# .\setup-spfx-heft.ps1
#
# Если запуск скриптов заблокирован, выполните сначала:
# Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
# =============================================================
$ErrorActionPreference = "Stop"
# Версия SPFx. Генератор ОБЯЗАН совпадать с версией пакетов ниже:
# старый генератор создаёт проект на gulp + node-sass, который не собирается
# на современных Node (node-gyp 3.8 требует Python 2).
$SpfxVersion = "1.22.1"
# ---- Запись файлов ----
# ВАЖНО: "Set-Content -Encoding UTF8" в Windows PowerShell 5.1 пишет UTF-8 С BOM.
# Heft, tsc и node на BOM в .json/.js спотыкаются:
# Unexpected token '<U+FEFF>', "<U+FEFF>{ ..." is not valid JSON
# Поэтому все конфиги пишем строго UTF-8 без BOM.
function Write-Utf8NoBom {
param(
[Parameter(Mandatory = $true)][string]$Path,
[Parameter(Mandatory = $true)][string]$Content
)
$full = [System.IO.Path]::Combine((Get-Location).ProviderPath, $Path)
$dir = [System.IO.Path]::GetDirectoryName($full)
if ($dir -and -not (Test-Path $dir)) {
New-Item -ItemType Directory -Force -Path $dir | Out-Null
}
[System.IO.File]::WriteAllText($full, ($Content -replace "`r?`n", "`r`n"), (New-Object System.Text.UTF8Encoding($false)))
}
# ---- Цвета вывода ----
function Info { param($msg) Write-Host "[INFO] $msg" -ForegroundColor Cyan }
function Success { param($msg) Write-Host "[OK] $msg" -ForegroundColor Green }
function Warn { param($msg) Write-Host "[WARN] $msg" -ForegroundColor Yellow }
function Header { param($msg) Write-Host "`n$msg" -ForegroundColor White }
Header "================================================="
Header " SPFx + Heft — автоматическая инициализация"
Header "=================================================`n"
# ---------------------------------------------------------
# 1. Проверка Node.js (SPFx 1.20+ требует Node 18+)
# ---------------------------------------------------------
Info "Проверка Node.js..."
try {
$nodeVersion = (node --version).Trim()
} catch {
Warn "Node.js не найден. Установите Node 18 LTS: https://nodejs.org"
exit 1
}
$nodeMajor = [int]($nodeVersion.TrimStart('v').Split('.')[0])
# 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 — подходит."
# ---------------------------------------------------------
# 1a. Папка должна быть пустой
# ---------------------------------------------------------
$leftovers = @("package.json", ".yo-rc.json", "node_modules", "gulpfile.js", "tslint.json") |
Where-Object { Test-Path $_ }
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
}
# ---------------------------------------------------------
# 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
# ---------------------------------------------------------
Info "Создаём package.json..."
$packageJson = @'
{
"name": "spfx-heft-project",
"version": "0.0.1",
"private": true,
"scripts": {
"build": "heft build",
"bundle": "heft build && gulp bundle --ship",
"package-solution": "heft build && gulp package-solution --ship",
"serve": "heft build && gulp serve",
"clean": "heft clean"
},
"overrides": {
"ajv": "^8.0.0"
}
}
'@
Write-Utf8NoBom "package.json" $packageJson
Success "package.json создан."
# ---------------------------------------------------------
# 4. Создание структуры папок
# ---------------------------------------------------------
Info "Создаём структуру папок..."
New-Item -ItemType Directory -Force -Path "config" | Out-Null
New-Item -ItemType Directory -Force -Path "src\webparts" | Out-Null
Success "Папки созданы."
# ---------------------------------------------------------
# 5. Запуск Yeoman-генератора SPFx
# ---------------------------------------------------------
Info "Запускаем Yeoman-генератор SPFx..."
Write-Host ""
Write-Host ">>> Сейчас откроется интерактивный генератор Yeoman." -ForegroundColor Yellow
Write-Host ">>> Выберите тип: WebPart, Framework: No framework (или React)." -ForegroundColor Yellow
Write-Host ""
# --skip-install: не даём генератору поставить свой gulp-стек до того,
# как мы его выпилим. Иначе npm полезет собирать node-sass.
yo "@microsoft/sharepoint" --skip-install
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 зависимостей
# ---------------------------------------------------------
Info "Устанавливаем Heft и плагины..."
$devDeps = @(
"@microsoft/spfx-web-build-rig@$SpfxVersion",
"@microsoft/spfx-heft-plugins@$SpfxVersion",
"@microsoft/eslint-config-spfx@$SpfxVersion",
"@microsoft/eslint-plugin-spfx@$SpfxVersion",
"@microsoft/sp-module-interfaces@$SpfxVersion",
"@rushstack/eslint-config@4.5.2",
"@rushstack/heft@1.1.2",
"@types/heft-jest@1.0.2",
"@typescript-eslint/parser@8.46.2"
)
npm install --save-dev @($devDeps) --save-exact --force --legacy-peer-deps
if ($LASTEXITCODE -ne 0) {
Warn "npm install завершился с ошибкой (код $LASTEXITCODE). Прерываем, чтобы не создавать конфиги поверх битого дерева."
exit 1
}
npm install typescript@~5.8.0 --save-dev
if ($LASTEXITCODE -ne 0) {
Warn "Не удалось установить typescript. Прерываем."
exit 1
}
Success "Heft и плагины установлены."
# ---------------------------------------------------------
# 7. config/rig.json
# ---------------------------------------------------------
Info "Создаём config\rig.json..."
New-Item -ItemType Directory -Force -Path "config" | Out-Null
$rigJson = @'
{
"$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
"rigPackageName": "@microsoft/spfx-web-build-rig"
}
'@
Write-Utf8NoBom "config\rig.json" $rigJson
Success "config\rig.json создан."
# ---------------------------------------------------------
# 8. config/sass.json
# ---------------------------------------------------------
Info "Создаём config\sass.json..."
New-Item -ItemType Directory -Force -Path "config" | Out-Null
$sassJson = @'
{
"$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft-sass-plugin.schema.json",
"extends": "@microsoft/spfx-web-build-rig/profiles/default/config/sass.json"
}
'@
Write-Utf8NoBom "config\sass.json" $sassJson
Success "config\sass.json создан."
# ---------------------------------------------------------
# 9. config/typescript.json
# ---------------------------------------------------------
Info "Создаём config\typescript.json..."
New-Item -ItemType Directory -Force -Path "config" | Out-Null
$typescriptJson = @'
{
"extends": "@microsoft/spfx-web-build-rig/profiles/default/config/typescript.json",
"staticAssetsToCopy": {
"fileExtensions": [".resx", ".jpg", ".png", ".woff", ".eot", ".ttf", ".svg", ".gif"],
"includeGlobs": ["webparts/*/loc/*.js"]
}
}
'@
Write-Utf8NoBom "config\typescript.json" $typescriptJson
Success "config\typescript.json создан."
# ---------------------------------------------------------
# 8. tsconfig.json (если Yeoman ещё не создал)
# ---------------------------------------------------------
if (-not (Test-Path "tsconfig.json")) {
Info "Создаём tsconfig.json..."
$tsConfig = @'
{
"extends": "./node_modules/@microsoft/spfx-web-build-rig/profiles/default/tsconfig-base.json"
}
'@
Write-Utf8NoBom "tsconfig.json" $tsConfig
Success "tsconfig.json создан."
} else {
Success "tsconfig.json уже существует, пропускаем."
}
# ---------------------------------------------------------
# 9. .eslintrc.js (если не создан)
# ---------------------------------------------------------
if (-not (Test-Path ".eslintrc.js")) {
Info "Создаём .eslintrc.js..."
$eslintConfig = @'
require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = {
extends: ['@microsoft/eslint-config-spfx/lib/profiles/default'],
parserOptions: { tsconfigRootDir: __dirname }
};
'@
Write-Utf8NoBom ".eslintrc.js" $eslintConfig
Success ".eslintrc.js создан."
} else {
Success ".eslintrc.js уже существует, пропускаем."
}
# ---------------------------------------------------------
# 10. .gitignore
# ---------------------------------------------------------
Info "Создаём .gitignore..."
$gitignore = @'
node_modules/
lib/
dist/
temp/
*.sppkg
.heft/
sharepoint/solution/
'@
Write-Utf8NoBom ".gitignore" $gitignore
Success ".gitignore создан."
# ---------------------------------------------------------
# 11. Финальный npm install
# ---------------------------------------------------------
$devDeps = @(
"@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..."
npm install @($devDeps) --save-exact --legacy-peer-deps
if ($LASTEXITCODE -ne 0) {
Warn "Финальный npm install завершился с ошибкой (код $LASTEXITCODE)."
exit 1
}
Success "Все зависимости установлены."
# ---------------------------------------------------------
# Готово
# ---------------------------------------------------------
Write-Host ""
Write-Host "=================================================" -ForegroundColor Green
Write-Host " Проект успешно инициализирован!" -ForegroundColor Green
Write-Host "=================================================" -ForegroundColor Green
Write-Host ""
Write-Host " Доступные команды:" -ForegroundColor White
Write-Host ""
Write-Host " npm run serve " -NoNewline -ForegroundColor Cyan
Write-Host "— локальная разработка (gulp serve)"
Write-Host " npm run build " -NoNewline -ForegroundColor Cyan
Write-Host "— сборка через Heft"
Write-Host " npm run bundle " -NoNewline -ForegroundColor Cyan
Write-Host "— бандлинг для продакшна"
Write-Host " npm run package-solution " -NoNewline -ForegroundColor Cyan
Write-Host "— создание .sppkg пакета"
Write-Host " npm run clean " -NoNewline -ForegroundColor Cyan
Write-Host "— очистка артефактов сборки"
Write-Host ""
Write-Host " Следующий шаг: " -NoNewline -ForegroundColor Yellow
Write-Host "npm run serve" -ForegroundColor Cyan
Write-Host ""