Files
tnsu-approval-route/frontend/create-spfx.ps1
T
2026-07-28 17:58:46 +05:00

396 lines
16 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# =============================================================
# 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 ""