mirror of
https://github.com/unslothai/unsloth
synced 2026-04-21 13:37:39 +00:00
perf(studio): upgrade to Vite 8 + auto-install bun for faster frontend builds (#4522)
* perf(studio): upgrade to Vite 8 + auto-install bun for 3x faster frontend builds
* fix(studio): make bun-to-npm fallback actually reachable
setup.sh used run_quiet() for the bun install attempt, but run_quiet
calls exit on failure. This killed the script before the npm fallback
could run, making the "falling back to npm" branch dead code.
Replace the run_quiet call with a direct bun invocation that captures
output to a temp file (same pattern, but returns instead of exiting).
Also clean up partial node_modules left by a failed bun install before
falling back to npm, in both setup.sh and build.sh. Without this, npm
inherits a corrupted node_modules tree from the failed bun run.
* fix(studio): restore commonjsOptions for dagre CJS interop
The previous commit removed build.commonjsOptions, assuming Vite 8's
Rolldown handles CJS natively. While optimizeDeps.include covers the
dev server (pre-bundling), it does NOT apply to production builds.
The resolve.alias still points @dagrejs/dagre to its .cjs.js entry,
so without commonjsOptions the production bundle fails to resolve
the CJS default export. This causes "TypeError: e is not a function"
on /chat after build (while dev mode works fine).
Restore the original commonjsOptions block to fix production builds.
* fix(studio): use motion/react instead of legacy framer-motion import
* fix(studio): address PR review findings for Vite 8 + bun upgrade
Fixes:
- Remove bun.lock from repo and add to .gitignore (npm is source of truth)
- Use & bun install *> $null pattern in setup.ps1 for reliable $LASTEXITCODE
- Add Remove-Item node_modules before npm fallback in setup.ps1
- Print bun install failure log in setup.sh before discarding
- Add Refresh-Environment after npm install -g bun in setup.ps1
- Tighten Node version check to ^20.19.0 || >=22.12.0 (Vite 8 requirement)
- Add engines field to package.json
- Use string comparison for _install_ok in build.sh
- Remove explicit framer-motion ^11.18.2 from package.json (motion pulls
framer-motion ^12.38.0 as its own dependency — the old pin caused a
version conflict)
* Fix Colab Node bypass and bun.lock stale-build trigger
Gate the Colab Node shortcut on NODE_OK=true so Colab
environments with a Node version too old for Vite 8 fall
through to the nvm install path instead of silently proceeding.
Exclude bun.lock from the stale-build probe in both setup.sh
and setup.ps1 so it does not force unnecessary frontend rebuilds
on every run.
---------
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
Co-authored-by: Shine1i <wasimysdev@gmail.com>
This commit is contained in:
parent
be2cd7087a
commit
d69d60ff19
7 changed files with 131 additions and 2510 deletions
17
build.sh
17
build.sh
|
|
@ -29,7 +29,22 @@ _restore_gitignores() {
|
||||||
}
|
}
|
||||||
trap _restore_gitignores EXIT
|
trap _restore_gitignores EXIT
|
||||||
|
|
||||||
npm install
|
# Use bun for install if available (faster), fall back to npm.
|
||||||
|
_install_ok=false
|
||||||
|
if command -v bun &>/dev/null; then
|
||||||
|
if bun install; then
|
||||||
|
_install_ok=true
|
||||||
|
else
|
||||||
|
echo "⚠ bun install failed, falling back to npm"
|
||||||
|
rm -rf node_modules
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$_install_ok" != "true" ]; then
|
||||||
|
if ! npm install; then
|
||||||
|
echo "❌ ERROR: package install failed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
npm run build # outputs to studio/frontend/dist/
|
npm run build # outputs to studio/frontend/dist/
|
||||||
|
|
||||||
_restore_gitignores
|
_restore_gitignores
|
||||||
|
|
|
||||||
1
studio/frontend/.gitignore
vendored
1
studio/frontend/.gitignore
vendored
|
|
@ -11,6 +11,7 @@ pnpm-debug.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
|
bun.lock
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
test/
|
test/
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,9 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc -b && vite build",
|
"build": "tsc -b && vite build",
|
||||||
|
|
@ -35,7 +38,7 @@
|
||||||
"@streamdown/code": "1.0.2",
|
"@streamdown/code": "1.0.2",
|
||||||
"@streamdown/math": "1.0.2",
|
"@streamdown/math": "1.0.2",
|
||||||
"@streamdown/mermaid": "1.0.2",
|
"@streamdown/mermaid": "1.0.2",
|
||||||
"@tailwindcss/vite": "^4.1.18",
|
"@tailwindcss/vite": "^4.2.2",
|
||||||
"@tanstack/react-router": "^1.159.10",
|
"@tanstack/react-router": "^1.159.10",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"@toolwind/corner-shape": "^0.0.8-3",
|
"@toolwind/corner-shape": "^0.0.8-3",
|
||||||
|
|
@ -48,7 +51,6 @@
|
||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"dexie": "^4.3.0",
|
"dexie": "^4.3.0",
|
||||||
"framer-motion": "^11.18.2",
|
|
||||||
"js-yaml": "^4.1.1",
|
"js-yaml": "^4.1.1",
|
||||||
"katex": "^0.16.28",
|
"katex": "^0.16.28",
|
||||||
"lucide-react": "^0.577.0",
|
"lucide-react": "^0.577.0",
|
||||||
|
|
@ -80,13 +82,13 @@
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
"@types/react": "^19.2.5",
|
"@types/react": "^19.2.5",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@vitejs/plugin-react": "^5.1.1",
|
"@vitejs/plugin-react": "^6.0.1",
|
||||||
"eslint": "^9.39.1",
|
"eslint": "^9.39.1",
|
||||||
"eslint-plugin-react-hooks": "^7.0.1",
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
"eslint-plugin-react-refresh": "^0.4.26",
|
"eslint-plugin-react-refresh": "^0.4.26",
|
||||||
"globals": "^16.5.0",
|
"globals": "^16.5.0",
|
||||||
"typescript": "~5.9.3",
|
"typescript": "~5.9.3",
|
||||||
"typescript-eslint": "^8.55.0",
|
"typescript-eslint": "^8.55.0",
|
||||||
"vite": "^7.3.1"
|
"vite": "^8.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ import {
|
||||||
useAuiEvent,
|
useAuiEvent,
|
||||||
useAuiState,
|
useAuiState,
|
||||||
} from "@assistant-ui/react";
|
} from "@assistant-ui/react";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "motion/react";
|
||||||
import {
|
import {
|
||||||
ArrowDownIcon,
|
ArrowDownIcon,
|
||||||
ArrowUpIcon,
|
ArrowUpIcon,
|
||||||
|
|
|
||||||
|
|
@ -728,16 +728,22 @@ if ($IsPipInstall) {
|
||||||
Write-Host "[OK] Running from pip install - frontend already bundled, skipping Node/npm check" -ForegroundColor Green
|
Write-Host "[OK] Running from pip install - frontend already bundled, skipping Node/npm check" -ForegroundColor Green
|
||||||
} else {
|
} else {
|
||||||
# setup.sh installs Node LTS (v22) via nvm. We enforce the same range here:
|
# setup.sh installs Node LTS (v22) via nvm. We enforce the same range here:
|
||||||
# Node >= 20, npm >= 11.
|
# Vite 8 requires Node ^20.19.0 || >=22.12.0, npm >= 11.
|
||||||
$NeedNode = $true
|
$NeedNode = $true
|
||||||
try {
|
try {
|
||||||
$NodeVersion = (node -v 2>$null)
|
$NodeVersion = (node -v 2>$null)
|
||||||
$NpmVersion = (npm -v 2>$null)
|
$NpmVersion = (npm -v 2>$null)
|
||||||
if ($NodeVersion -and $NpmVersion) {
|
if ($NodeVersion -and $NpmVersion) {
|
||||||
$NodeMajor = [int]($NodeVersion -replace 'v','').Split('.')[0]
|
$NodeParts = ($NodeVersion -replace 'v','').Split('.')
|
||||||
|
$NodeMajor = [int]$NodeParts[0]
|
||||||
|
$NodeMinor = [int]$NodeParts[1]
|
||||||
$NpmMajor = [int]$NpmVersion.Split('.')[0]
|
$NpmMajor = [int]$NpmVersion.Split('.')[0]
|
||||||
|
|
||||||
if ($NodeMajor -ge 20 -and $NpmMajor -ge 11) {
|
# Vite 8: ^20.19.0 || >=22.12.0
|
||||||
|
$NodeOk = ($NodeMajor -eq 20 -and $NodeMinor -ge 19) -or
|
||||||
|
($NodeMajor -eq 22 -and $NodeMinor -ge 12) -or
|
||||||
|
($NodeMajor -ge 23)
|
||||||
|
if ($NodeOk -and $NpmMajor -ge 11) {
|
||||||
Write-Host "[OK] Node $NodeVersion and npm $NpmVersion already meet requirements." -ForegroundColor Green
|
Write-Host "[OK] Node $NodeVersion and npm $NpmVersion already meet requirements." -ForegroundColor Green
|
||||||
$NeedNode = $false
|
$NeedNode = $false
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -761,6 +767,24 @@ if ($IsPipInstall) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "[OK] Node $(node -v) | npm $(npm -v)" -ForegroundColor Green
|
Write-Host "[OK] Node $(node -v) | npm $(npm -v)" -ForegroundColor Green
|
||||||
|
|
||||||
|
# ── bun (optional, faster package installs) ──
|
||||||
|
# Installed via npm — Node is already guaranteed above. Works on all platforms.
|
||||||
|
if (-not (Get-Command bun -ErrorAction SilentlyContinue)) {
|
||||||
|
Write-Host " Installing bun (faster frontend package installs)..." -ForegroundColor DarkGray
|
||||||
|
$prevEAP_bun = $ErrorActionPreference
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
npm install -g bun 2>&1 | Out-Null
|
||||||
|
$ErrorActionPreference = $prevEAP_bun
|
||||||
|
Refresh-Environment
|
||||||
|
if (Get-Command bun -ErrorAction SilentlyContinue) {
|
||||||
|
Write-Host "[OK] bun installed ($(bun --version))" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "[OK] bun install skipped (npm will be used instead)" -ForegroundColor DarkGray
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Host "[OK] bun already installed ($(bun --version))" -ForegroundColor Green
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# ============================================
|
# ============================================
|
||||||
|
|
@ -844,10 +868,10 @@ if ($IsPipInstall) {
|
||||||
if ($NewerFile) { break }
|
if ($NewerFile) { break }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# Also check all top-level files (package.json, bun.lock, vite.config.ts, index.html, etc.)
|
# Also check all top-level files (package.json, vite.config.ts, index.html, etc.)
|
||||||
if (-not $NewerFile) {
|
if (-not $NewerFile) {
|
||||||
$NewerFile = Get-ChildItem -Path $FrontendDir -File -ErrorAction SilentlyContinue |
|
$NewerFile = Get-ChildItem -Path $FrontendDir -File -ErrorAction SilentlyContinue |
|
||||||
Where-Object { $_.LastWriteTime -gt $DistTime } |
|
Where-Object { $_.Name -ne "bun.lock" -and $_.LastWriteTime -gt $DistTime } |
|
||||||
Select-Object -First 1
|
Select-Object -First 1
|
||||||
}
|
}
|
||||||
if (-not $NewerFile) {
|
if (-not $NewerFile) {
|
||||||
|
|
@ -882,26 +906,47 @@ if ($NeedFrontendBuild -and -not $IsPipInstall) {
|
||||||
$WalkDir = Split-Path $WalkDir -Parent
|
$WalkDir = Split-Path $WalkDir -Parent
|
||||||
}
|
}
|
||||||
|
|
||||||
# npm writes warnings to stderr; lower ErrorActionPreference so PS doesn't
|
# Use bun if available (faster install), fall back to npm.
|
||||||
# treat them as terminating errors (same pattern as the pip section below).
|
# Bun is used only as package manager; Node runs the actual build (Vite 8).
|
||||||
$prevEAP_npm = $ErrorActionPreference
|
$prevEAP_npm = $ErrorActionPreference
|
||||||
$ErrorActionPreference = "Continue"
|
$ErrorActionPreference = "Continue"
|
||||||
Push-Location $FrontendDir
|
Push-Location $FrontendDir
|
||||||
npm install 2>&1 | Out-Null
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
$UseBun = $null -ne (Get-Command bun -ErrorAction SilentlyContinue)
|
||||||
Pop-Location
|
|
||||||
$ErrorActionPreference = $prevEAP_npm
|
if ($UseBun) {
|
||||||
foreach ($gi in $HiddenGitignores) { Rename-Item -Path "$gi._twbuild" -NewName (Split-Path $gi -Leaf) -Force -ErrorAction SilentlyContinue }
|
Write-Host " Using bun for package install (faster)" -ForegroundColor DarkGray
|
||||||
Write-Host "[ERROR] npm install failed (exit code $LASTEXITCODE)" -ForegroundColor Red
|
& bun install *> $null
|
||||||
Write-Host " Try running 'npm install' manually in frontend/ to see errors" -ForegroundColor Yellow
|
$bunExit = $LASTEXITCODE
|
||||||
exit 1
|
if ($bunExit -ne 0) {
|
||||||
|
Write-Host " [WARN] bun install failed (exit $bunExit), falling back to npm" -ForegroundColor Yellow
|
||||||
|
if (Test-Path "node_modules") {
|
||||||
|
Remove-Item "node_modules" -Recurse -Force -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
$UseBun = $false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
npm run build 2>&1 | Out-Null
|
if (-not $UseBun) {
|
||||||
if ($LASTEXITCODE -ne 0) {
|
& npm install *> $null
|
||||||
|
$npmExit = $LASTEXITCODE
|
||||||
|
if ($npmExit -ne 0) {
|
||||||
|
Pop-Location
|
||||||
|
$ErrorActionPreference = $prevEAP_npm
|
||||||
|
foreach ($gi in $HiddenGitignores) { Rename-Item -Path "$gi._twbuild" -NewName (Split-Path $gi -Leaf) -Force -ErrorAction SilentlyContinue }
|
||||||
|
Write-Host "[ERROR] npm install failed (exit code $npmExit)" -ForegroundColor Red
|
||||||
|
Write-Host " Try running 'npm install' manually in frontend/ to see errors" -ForegroundColor Yellow
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Always use npm to run the build (Node runtime — avoids bun Windows runtime issues)
|
||||||
|
& npm run build *> $null
|
||||||
|
$buildExit = $LASTEXITCODE
|
||||||
|
if ($buildExit -ne 0) {
|
||||||
Pop-Location
|
Pop-Location
|
||||||
$ErrorActionPreference = $prevEAP_npm
|
$ErrorActionPreference = $prevEAP_npm
|
||||||
foreach ($gi in $HiddenGitignores) { Rename-Item -Path "$gi._twbuild" -NewName (Split-Path $gi -Leaf) -Force -ErrorAction SilentlyContinue }
|
foreach ($gi in $HiddenGitignores) { Rename-Item -Path "$gi._twbuild" -NewName (Split-Path $gi -Leaf) -Force -ErrorAction SilentlyContinue }
|
||||||
Write-Host "[ERROR] npm run build failed (exit code $LASTEXITCODE)" -ForegroundColor Red
|
Write-Host "[ERROR] npm run build failed (exit code $buildExit)" -ForegroundColor Red
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
Pop-Location
|
Pop-Location
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ _NEED_FRONTEND_BUILD=true
|
||||||
if [ -d "$SCRIPT_DIR/frontend/dist" ]; then
|
if [ -d "$SCRIPT_DIR/frontend/dist" ]; then
|
||||||
# Check all top-level files (package.json, bun.lock, vite.config.ts, index.html, etc.)
|
# Check all top-level files (package.json, bun.lock, vite.config.ts, index.html, etc.)
|
||||||
_changed=$(find "$SCRIPT_DIR/frontend" -maxdepth 1 -type f \
|
_changed=$(find "$SCRIPT_DIR/frontend" -maxdepth 1 -type f \
|
||||||
|
! -name 'bun.lock' \
|
||||||
-newer "$SCRIPT_DIR/frontend/dist" -print -quit 2>/dev/null)
|
-newer "$SCRIPT_DIR/frontend/dist" -print -quit 2>/dev/null)
|
||||||
# Check src/ and public/ recursively (|| true guards against set -e when dirs are missing)
|
# Check src/ and public/ recursively (|| true guards against set -e when dirs are missing)
|
||||||
if [ -z "$_changed" ]; then
|
if [ -z "$_changed" ]; then
|
||||||
|
|
@ -85,12 +86,18 @@ else
|
||||||
NEED_NODE=true
|
NEED_NODE=true
|
||||||
if command -v node &>/dev/null && command -v npm &>/dev/null; then
|
if command -v node &>/dev/null && command -v npm &>/dev/null; then
|
||||||
NODE_MAJOR=$(node -v | sed 's/v//' | cut -d. -f1)
|
NODE_MAJOR=$(node -v | sed 's/v//' | cut -d. -f1)
|
||||||
|
NODE_MINOR=$(node -v | sed 's/v//' | cut -d. -f2)
|
||||||
NPM_MAJOR=$(npm -v | cut -d. -f1)
|
NPM_MAJOR=$(npm -v | cut -d. -f1)
|
||||||
if [ "$NODE_MAJOR" -ge 20 ] && [ "$NPM_MAJOR" -ge 11 ]; then
|
# Vite 8 requires Node ^20.19.0 || >=22.12.0
|
||||||
|
NODE_OK=false
|
||||||
|
if [ "$NODE_MAJOR" -eq 20 ] && [ "$NODE_MINOR" -ge 19 ]; then NODE_OK=true; fi
|
||||||
|
if [ "$NODE_MAJOR" -eq 22 ] && [ "$NODE_MINOR" -ge 12 ]; then NODE_OK=true; fi
|
||||||
|
if [ "$NODE_MAJOR" -ge 23 ]; then NODE_OK=true; fi
|
||||||
|
if [ "$NODE_OK" = true ] && [ "$NPM_MAJOR" -ge 11 ]; then
|
||||||
echo "✅ Node $(node -v) and npm $(npm -v) already meet requirements. Skipping nvm install."
|
echo "✅ Node $(node -v) and npm $(npm -v) already meet requirements. Skipping nvm install."
|
||||||
NEED_NODE=false
|
NEED_NODE=false
|
||||||
else
|
else
|
||||||
if [ "$IS_COLAB" = true ]; then
|
if [ "$IS_COLAB" = true ] && [ "$NODE_OK" = true ]; then
|
||||||
echo "✅ Node $(node -v) and npm $(npm -v) detected in Colab."
|
echo "✅ Node $(node -v) and npm $(npm -v) detected in Colab."
|
||||||
# In Colab, just upgrade npm directly - nvm doesn't work well
|
# In Colab, just upgrade npm directly - nvm doesn't work well
|
||||||
if [ "$NPM_MAJOR" -lt 11 ]; then
|
if [ "$NPM_MAJOR" -lt 11 ]; then
|
||||||
|
|
@ -150,6 +157,20 @@ fi
|
||||||
|
|
||||||
echo "✅ Node $(node -v) | npm $(npm -v)"
|
echo "✅ Node $(node -v) | npm $(npm -v)"
|
||||||
|
|
||||||
|
# ── Install bun (optional, faster package installs) ──
|
||||||
|
# Uses npm to install bun globally — Node is already guaranteed above,
|
||||||
|
# avoids platform-specific installers, PATH issues, and admin requirements.
|
||||||
|
if ! command -v bun &>/dev/null; then
|
||||||
|
echo " Installing bun (faster frontend package installs)..."
|
||||||
|
if npm install -g bun > /dev/null 2>&1 && command -v bun &>/dev/null; then
|
||||||
|
echo "✅ bun installed ($(bun --version))"
|
||||||
|
else
|
||||||
|
echo " bun install skipped (npm will be used instead)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✅ bun already installed ($(bun --version))"
|
||||||
|
fi
|
||||||
|
|
||||||
# ── 5. Build frontend ──
|
# ── 5. Build frontend ──
|
||||||
cd "$SCRIPT_DIR/frontend"
|
cd "$SCRIPT_DIR/frontend"
|
||||||
|
|
||||||
|
|
@ -174,7 +195,27 @@ _restore_gitignores() {
|
||||||
}
|
}
|
||||||
trap _restore_gitignores EXIT
|
trap _restore_gitignores EXIT
|
||||||
|
|
||||||
run_quiet "npm install" npm install
|
# Use bun for install if available (faster), fall back to npm.
|
||||||
|
# Build always uses npm (Node runtime — avoids bun runtime issues on some platforms).
|
||||||
|
# NOTE: We intentionally avoid run_quiet for the bun install attempt because
|
||||||
|
# run_quiet calls exit on failure, which would kill the script before the npm
|
||||||
|
# fallback can run. Instead we capture output manually and only show it on failure.
|
||||||
|
if command -v bun &>/dev/null; then
|
||||||
|
echo " Using bun for package install (faster)"
|
||||||
|
_bun_log=$(mktemp)
|
||||||
|
if bun install >"$_bun_log" 2>&1; then
|
||||||
|
rm -f "$_bun_log"
|
||||||
|
else
|
||||||
|
echo " ⚠️ bun install failed, falling back to npm"
|
||||||
|
echo " bun install output:"
|
||||||
|
sed 's/^/ | /' "$_bun_log" >&2
|
||||||
|
rm -f "$_bun_log"
|
||||||
|
rm -rf node_modules
|
||||||
|
run_quiet "npm install" npm install
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
run_quiet "npm install" npm install
|
||||||
|
fi
|
||||||
run_quiet "npm run build" npm run build
|
run_quiet "npm run build" npm run build
|
||||||
|
|
||||||
_restore_gitignores
|
_restore_gitignores
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue