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:
@@ -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
63
build-for-airgap.sh
Executable 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 "========================================="
|
||||||
@@ -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:
|
||||||
|
|||||||
@@ -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": [
|
||||||
|
|||||||
@@ -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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user