Downgrade to Angular 17 with webpack for better restricted environment compatibility

- Downgrade from Angular 19 to Angular 17.3.0
- Switch from Vite-based build (@angular/build) to webpack (@angular-devkit/build-angular)
- Eliminates Vite, esbuild, and rollup dependencies that were causing issues in restricted npm environments
- Update tsconfig.json for webpack compatibility (moduleResolution: bundler)
- Update angular.json to use browser builder instead of application builder
- Update docker-compose.yml to use prebuilt Dockerfile for air-gapped deployment
- Add build-for-airgap.sh helper script for local builds
- Update DEPLOYMENT.md with Angular 17 webpack strategy notes
- Bundle size: 329.73 kB raw / 86.54 kB gzipped

This change improves compatibility with enterprise environments that have restricted package registry access.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-15 14:41:07 -05:00
parent 6c01329f27
commit 2054181228
6 changed files with 128 additions and 51 deletions

View File

@@ -26,13 +26,15 @@ This uses `Dockerfile.frontend` which:
## Option 2: Pre-built Deployment (Air-Gapped/Restricted Environments) ## Option 2: Pre-built Deployment (Air-Gapped/Restricted Environments)
Use `Dockerfile.frontend.prebuilt` for environments with restricted npm access or when esbuild platform binaries cannot be downloaded. Use `Dockerfile.frontend.prebuilt` for environments with restricted npm access.
**Requirements:** **Requirements:**
- Node.js 24+ installed locally - Node.js 18+ installed locally
- npm installed locally - npm installed locally
- No internet required during Docker build - No internet required during Docker build
**Note:** This project uses Angular 17 with webpack bundler (not Vite) for better compatibility with restricted npm environments.
**Usage:** **Usage:**
### Step 1: Build Angular app locally ### Step 1: Build Angular app locally
@@ -71,26 +73,16 @@ This uses `Dockerfile.frontend.prebuilt` which:
## Troubleshooting ## Troubleshooting
### esbuild Platform Binary Issues ### Build Tool Package Issues
If you see errors like: If you see errors about missing packages like:
``` ```
Could not resolve "@esbuild/darwin-arm64" Cannot find package "vite"
Cannot find package "esbuild"
Cannot find package "rollup"
``` ```
**Solution 1:** Use Option 2 (Pre-built) above **Solution:** This project uses Angular 17 with webpack bundler specifically to avoid these issues. If you still encounter package access problems in your restricted environment, use Option 2 (Pre-built) deployment above, which eliminates all npm dependencies in Docker.
**Solution 2:** Add platform binaries to package.json (already included):
```json
"optionalDependencies": {
"@esbuild/darwin-arm64": "^0.25.4",
"@esbuild/darwin-x64": "^0.25.4",
"@esbuild/linux-arm64": "^0.25.4",
"@esbuild/linux-x64": "^0.25.4"
}
```
**Solution 3:** Use custom npm registry with cached esbuild binaries
### Custom NPM Registry ### Custom NPM Registry
@@ -109,5 +101,20 @@ NPM_REGISTRY=http://your-proxy ./quickstart.sh
## Recommendation ## Recommendation
- **Development/Cloud**: Use Option 1 (standard) - **Development/Cloud**: Use Option 1 (standard)
- **Air-gapped/Enterprise**: Use Option 2 (pre-built) - **Air-gapped/Enterprise**: Use Option 2 (pre-built)**RECOMMENDED**
- **CI/CD**: Use Option 2 for faster, more reliable builds - **CI/CD**: Use Option 2 for faster, more reliable builds
- **Restricted npm access**: Use Option 2 (pre-built) ⭐ **REQUIRED**
---
## Build Strategy for Restricted Environments
**This project uses Angular 17 with webpack** instead of Angular 19 with Vite specifically for better compatibility with restricted npm environments. Webpack has fewer platform-specific binary dependencies than Vite.
If you encounter any package access errors during builds:
- `Cannot find package "vite"`
- `Cannot find package "rollup"`
- `Cannot find package "esbuild"`
- Any platform-specific binary errors
**Solution:** Use Option 2 (Pre-built) deployment. This completely avoids npm installation in Docker and eliminates all build tool dependency issues.

63
build-for-airgap.sh Executable file
View File

@@ -0,0 +1,63 @@
#!/bin/bash
set -e
echo "========================================="
echo "Building Angular for Air-Gapped Deployment"
echo "========================================="
echo ""
# Check if we're in the right directory
if [ ! -f "docker-compose.yml" ]; then
echo "Error: Must run from project root directory"
exit 1
fi
# Check if node is installed
if ! command -v node &> /dev/null; then
echo "Error: Node.js is not installed. Please install Node.js 18+ first."
exit 1
fi
# Check if npm is installed
if ! command -v npm &> /dev/null; then
echo "Error: npm is not installed. Please install npm first."
exit 1
fi
echo "Step 1/3: Installing dependencies..."
cd frontend
npm install
echo ""
echo "Step 2/3: Building Angular production bundle..."
npm run build:prod
echo ""
echo "Step 3/3: Verifying build output..."
if [ -d "dist/frontend/browser" ]; then
echo "✓ Build successful!"
echo "✓ Output: frontend/dist/frontend/browser"
ls -lh dist/frontend/browser | head -5
else
echo "✗ Build failed - output directory not found"
exit 1
fi
cd ..
echo ""
echo "========================================="
echo "Build Complete!"
echo "========================================="
echo ""
echo "Next steps:"
echo "1. Update docker-compose.yml:"
echo " Change: dockerfile: Dockerfile.frontend"
echo " To: dockerfile: Dockerfile.frontend.prebuilt"
echo ""
echo "2. Deploy:"
echo " docker-compose up -d --build"
echo ""
echo "See DEPLOYMENT.md for more details."
echo "========================================="

View File

@@ -60,9 +60,7 @@ services:
frontend: frontend:
build: build:
context: . context: .
dockerfile: Dockerfile.frontend dockerfile: Dockerfile.frontend.prebuilt
args:
NPM_REGISTRY: ${NPM_REGISTRY:-https://registry.npmjs.org/}
ports: ports:
- "4200:80" - "4200:80"
depends_on: depends_on:

View File

@@ -11,24 +11,27 @@
"prefix": "app", "prefix": "app",
"architect": { "architect": {
"build": { "build": {
"builder": "@angular/build:application", "builder": "@angular-devkit/build-angular:browser",
"options": { "options": {
"outputPath": "dist/frontend", "outputPath": "dist/frontend/browser",
"index": "src/index.html", "index": "src/index.html",
"browser": "src/main.ts", "main": "src/main.ts",
"polyfills": [ "polyfills": [
"zone.js" "zone.js"
], ],
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"assets": [ "assets": [
"src/favicon.ico",
{ {
"glob": "**/*", "glob": "**/*",
"input": "public" "input": "public",
"output": "/"
} }
], ],
"styles": [ "styles": [
"src/styles.css" "src/styles.css"
] ],
"scripts": []
}, },
"configurations": { "configurations": {
"production": { "production": {
@@ -55,7 +58,7 @@
"defaultConfiguration": "production" "defaultConfiguration": "production"
}, },
"serve": { "serve": {
"builder": "@angular/build:dev-server", "builder": "@angular-devkit/build-angular:dev-server",
"configurations": { "configurations": {
"production": { "production": {
"buildTarget": "frontend:build:production" "buildTarget": "frontend:build:production"
@@ -67,10 +70,10 @@
"defaultConfiguration": "development" "defaultConfiguration": "development"
}, },
"extract-i18n": { "extract-i18n": {
"builder": "@angular/build:extract-i18n" "builder": "@angular-devkit/build-angular:extract-i18n"
}, },
"test": { "test": {
"builder": "@angular/build:karma", "builder": "@angular-devkit/build-angular:karma",
"options": { "options": {
"polyfills": [ "polyfills": [
"zone.js", "zone.js",
@@ -78,9 +81,11 @@
], ],
"tsConfig": "tsconfig.spec.json", "tsConfig": "tsconfig.spec.json",
"assets": [ "assets": [
"src/favicon.ico",
{ {
"glob": "**/*", "glob": "**/*",
"input": "public" "input": "public",
"output": "/"
} }
], ],
"styles": [ "styles": [

View File

@@ -23,33 +23,29 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/common": "^19.0.0", "@angular/animations": "^17.3.0",
"@angular/compiler": "^19.0.0", "@angular/common": "^17.3.0",
"@angular/core": "^19.0.0", "@angular/compiler": "^17.3.0",
"@angular/forms": "^19.0.0", "@angular/core": "^17.3.0",
"@angular/platform-browser": "^19.0.0", "@angular/forms": "^17.3.0",
"@angular/router": "^19.0.0", "@angular/platform-browser": "^17.3.0",
"@angular/platform-browser-dynamic": "^17.3.0",
"@angular/router": "^17.3.0",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.15.0" "zone.js": "~0.14.3"
}, },
"devDependencies": { "devDependencies": {
"@angular/build": "^19.0.0", "@angular-devkit/build-angular": "^17.3.0",
"@angular/cli": "^19.0.0", "@angular/cli": "^17.3.0",
"@angular/compiler-cli": "^19.0.0", "@angular/compiler-cli": "^17.3.0",
"@types/jasmine": "~5.1.0", "@types/jasmine": "~5.1.0",
"jasmine-core": "~5.9.0", "jasmine-core": "~5.1.0",
"karma": "~6.4.0", "karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0", "karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0", "karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0", "karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0", "karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.8.0" "typescript": "~5.4.2"
},
"optionalDependencies": {
"@esbuild/darwin-arm64": "^0.25.4",
"@esbuild/darwin-x64": "^0.25.4",
"@esbuild/linux-arm64": "^0.25.4",
"@esbuild/linux-x64": "^0.25.4"
} }
} }

View File

@@ -9,11 +9,19 @@
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"skipLibCheck": true, "skipLibCheck": true,
"isolatedModules": true, "esModuleInterop": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"importHelpers": true, "importHelpers": true,
"target": "ES2022", "target": "ES2022",
"module": "preserve" "module": "ES2022",
"moduleResolution": "bundler",
"lib": [
"ES2022",
"dom"
],
"useDefineForClassFields": false,
"baseUrl": "./",
"paths": {}
}, },
"angularCompilerOptions": { "angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false, "enableI18nLegacyMessageIdFormat": false,