standalone to use the latest version of packs

This commit is contained in:
Max Peintner
2025-07-10 14:34:58 +02:00
parent c692fa8b99
commit b13cbd69f9
14 changed files with 501 additions and 281 deletions

11
login/apps/login/.eslintrc.cjs Executable file → Normal file
View File

@@ -1,12 +1,7 @@
module.exports = {
extends: ["next/core-web-vitals"],
ignorePatterns: ["external/**/*.ts"],
rules: {
"@next/next/no-html-link-for-pages": "off",
},
settings: {
react: {
version: "detect",
},
},
"@next/next/no-img-element": "off",
"react/no-unescaped-entities": "off"
}
};

View File

@@ -1,58 +0,0 @@
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Prepare standalone
run: ./prepare-standalone.sh
- name: Run tests
run: npm run test:unit
- name: Run linting
run: npm run lint
- name: Build application
run: npm run build:standalone
build-docker:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Prepare standalone
run: ./prepare-standalone.sh
- name: Build Docker image
run: |
docker build -t zitadel-login-ui .
- name: Test Docker image
run: |
docker run --rm -d -p 3000:3000 --name test-container zitadel-login-ui
sleep 10
curl -f http://localhost:3000 || exit 1
docker stop test-container

View File

@@ -1,3 +1,10 @@
custom-config.js
.env*.local
standalone
# Generated standalone files (temporary)
*.generated.*
package.monorepo.backup.json
# TypeScript build info
tsconfig.tsbuildinfo

View File

@@ -1,54 +0,0 @@
# Dependencies
/node_modules
/.pnp
.pnp.js
# Testing
/coverage
# Next.js
/.next/
/out/
# Production
/build
# Misc
.DS_Store
*.pem
# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Local env files
.env*.local
# Vercel
.vercel
# TypeScript
*.tsbuildinfo
next-env.d.ts
# Turbo
.turbo
# IDE
.vscode/
.idea/
# Logs
logs
*.log
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Package manager locks (we use npm in standalone)
pnpm-lock.yaml
yarn.lock

View File

@@ -12,11 +12,20 @@ This is the standalone version of the ZITADEL Login UI, a Next.js application th
### Setup
1. **Prepare the standalone environment:**
```bash
./prepare-standalone.sh
./prepare-standalone.sh --install
```
Or for manual control:
```bash
./prepare-standalone.sh --no-install
npm install
```
2. **Start development server:**
```bash
npm run dev
```
@@ -48,14 +57,46 @@ ZITADEL_API_URL=https://your-zitadel-instance.com
# Add other required environment variables
```
### Package Management
This standalone version automatically uses the latest published versions of:
- `@zitadel/client` - ZITADEL client library (latest)
- `@zitadel/proto` - ZITADEL protocol definitions (latest)
To update to the latest versions, simply run:
```bash
npm update @zitadel/client @zitadel/proto
```
## Differences from Monorepo Version
This standalone version includes:
- Self-contained configuration files
- Published versions of `@zitadel/client` and `@zitadel/proto` packages
- Standalone build scripts
- Independent dependency management
- **Published packages**: Uses latest published versions of `@zitadel/client` and `@zitadel/proto`
- **Self-contained configuration**: All configuration files are standalone-ready
- **Simplified dependencies**: Removes monorepo-specific devDependencies and tooling
- **Streamlined build scripts**: Optimized scripts for standalone development
- **Independent dependency management**: No workspace or turbo dependencies
## Architecture
### Dual-Mode Design
This project supports both monorepo and standalone modes:
- **Monorepo mode**: Uses `workspace:*` dependencies for local development
- **Standalone mode**: Uses published npm packages for distribution
### Automatic Conversion
The conversion between modes is handled by intelligent scripts:
1. **`prepare-standalone.sh`**: Main conversion script for end users
2. **`scripts/prepare-standalone.js`**: Advanced preparation with latest package versions
3. **`scripts/build-standalone.js`**: Generates standalone configs without modifying current setup
4. **`scripts/config-manager.js`**: Switches between monorepo and standalone configurations
## Contributing
@@ -71,6 +112,7 @@ When contributing to this standalone version:
This repository is maintained as a Git subtree of the main ZITADEL repository.
To sync changes from the main repo:
```bash
# In the main ZITADEL repo
git subtree push --prefix=login/apps/login origin typescript-login-standalone

View File

@@ -14,7 +14,8 @@
"build:login:standalone": "NEXT_PUBLIC_BASE_PATH=/ui/v2/login NEXT_OUTPUT_MODE=standalone pnpm build",
"start": "pnpm build && pnpm exec next start",
"start:built": "pnpm exec next start",
"clean": "pnpm mock:stop && rm -rf .turbo && rm -rf node_modules && rm -rf .next"
"clean": "pnpm mock:stop && rm -rf .turbo && rm -rf node_modules && rm -rf .next",
"standalone:prepare": "node scripts/prepare-standalone.js"
},
"git": {
"pre-commit": "lint-staged"

View File

@@ -10,17 +10,16 @@
"lint:fix": "prettier --write .",
"build": "next build",
"build:standalone": "NEXT_PUBLIC_BASE_PATH=/ui/v2/login NEXT_OUTPUT_MODE=standalone next build",
"start": "npm run build && next start",
"start:built": "next start",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next"
"start": "next start",
"clean": "rm -rf node_modules && rm -rf .next"
},
"dependencies": {
"@headlessui/react": "^2.1.9",
"@heroicons/react": "2.1.3",
"@tailwindcss/forms": "0.5.7",
"@vercel/analytics": "^1.2.2",
"@zitadel/client": "^1.0.0",
"@zitadel/proto": "^1.0.0",
"@zitadel/client": "latest",
"@zitadel/proto": "latest",
"clsx": "1.2.1",
"copy-to-clipboard": "^3.3.3",
"deepmerge": "^4.3.1",
@@ -41,26 +40,19 @@
"@bufbuild/buf": "^1.53.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@types/ms": "2.1.0",
"@types/node": "^22.14.1",
"@types/react": "19.1.2",
"@types/react-dom": "19.1.2",
"@types/tinycolor2": "1.4.3",
"@types/uuid": "^10.0.0",
"@vercel/git-hooks": "1.0.0",
"eslint": "8.57.1",
"eslint-config-next": "15.4.0-canary.86",
"prettier": "^3.5.3",
"autoprefixer": "10.4.21",
"grpc-tools": "1.13.0",
"jsdom": "^26.1.0",
"make-dir-cli": "4.0.0",
"postcss": "8.5.3",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "0.6.11",
"sass": "^1.87.0",
"tailwindcss": "3.4.14",
"ts-proto": "^2.7.0",
"typescript": "^5.8.3",
"vitest": "^3.1.2"
"vitest": "^2.0.0"
}
}

View File

@@ -3,25 +3,55 @@
# Script to prepare standalone version of the login app
set -e
echo "Preparing standalone version..."
echo "🔧 Preparing standalone version..."
# Copy standalone configs
cp package.standalone.json package.json
cp tsconfig.standalone.json tsconfig.json
cp .eslintrc.standalone.cjs .eslintrc.cjs
cp prettier.config.standalone.mjs prettier.config.mjs
cp tailwind.config.standalone.mjs tailwind.config.mjs
# Parse arguments
INSTALL_DEPS=true
USE_LATEST=false
# Install dependencies unless --no-install is passed
if [ "$1" != "--no-install" ]; then
echo "Installing dependencies..."
npm install
for arg in "$@"; do
case $arg in
--no-install)
INSTALL_DEPS=false
shift
;;
--latest)
USE_LATEST=true
shift
;;
*)
# Unknown option
;;
esac
done
# Build arguments for Node.js script
NODE_ARGS=""
if [ "$INSTALL_DEPS" = true ]; then
NODE_ARGS="$NODE_ARGS --install"
fi
if [ "$USE_LATEST" = true ]; then
NODE_ARGS="$NODE_ARGS --latest"
fi
echo "Standalone version prepared successfully!"
if [ "$1" != "--no-install" ]; then
echo "You can now run:"
echo " npm run dev - Start development server"
echo " npm run build - Build for production"
echo " npm run start - Start production server"
# Check if Node.js scripts exist
if [ ! -f "scripts/prepare-standalone.js" ]; then
echo "❌ scripts/prepare-standalone.js not found!"
echo " Make sure you're in the correct directory"
exit 1
fi
# Run the enhanced Node.js prepare script
node scripts/prepare-standalone.js $NODE_ARGS
echo ""
echo "✅ Standalone version prepared successfully!"
if [ "$INSTALL_DEPS" = false ]; then
echo ""
echo "📝 Next steps:"
echo " npm install - Install dependencies"
echo " npm run dev - Start development server"
echo " npm run build - Build for production"
echo " npm run start - Start production server"
fi

View File

@@ -1 +1,8 @@
export { default } from "@zitadel/prettier-config";
export default {
semi: true,
trailingComma: "all",
singleQuote: false,
printWidth: 80,
tabWidth: 2,
plugins: ["prettier-plugin-tailwindcss"],
};

View File

@@ -0,0 +1,247 @@
# Standalone Build Scripts
This directory contains the minimal scripts needed for managing the ZITADEL Login UI standalone conversion.
## 📁 File Overview
### Required Files
- `package.json` - Main package file (monorepo mode with `workspace:*`)
- `package.standalone.json` - Pre-configured standalone version (uses `latest`)
- `*.standalone.*` - Configuration templates for standalone mode
- `scripts/` - Conversion scripts
### Generated/Temporary Files
- `*.generated.*` - Temporary files (ignored by git)
- `package.monorepo.backup.json` - Backup when switching modes
## 🛠️ Scripts Overview
### `prepare-standalone.js`
**The main script for converting monorepo to standalone mode.**
```bash
node scripts/prepare-standalone.js [--install]
```
- `--install` - Automatically install dependencies after preparation
**What it does:**
- Copies `package.standalone.json``package.json`
- Copies all `*.standalone.*` config files to their active versions
- Optionally runs `npm install`
## 🚀 **Simplified Approach**
This setup now uses a **much simpler approach**:
1. **Static Configuration**: `package.standalone.json` is pre-configured with `latest` versions
2. **No Version Fetching**: No network calls or complex version management
3. **Manual Maintenance**: Package versions are updated manually when needed
4. **Faster Setup**: Conversion is instant with just file copying
## 📋 **Usage for Customers**
```bash
# 1. Clone the repository
git clone <standalone-repo>
# 2. Prepare standalone mode
./prepare-standalone.sh --install
# 3. Start developing
npm run dev
```
## 🔧 **Usage for Maintainers**
```bash
# Update to latest packages manually in package.standalone.json
npm view @zitadel/client version
npm view @zitadel/proto version
# Then update package.standalone.json with latest versions
```
**What it does:**
- Converts `workspace:*` dependencies to published package versions
- Removes monorepo-specific devDependencies
- Copies standalone configuration files
- Updates package.json scripts for standalone use
### `build-standalone.js`
**Generates package.standalone.json with latest published package versions.**
```bash
node scripts/build-standalone.js
```
**What it does:**
- Fetches latest versions of `@zitadel/client` and `@zitadel/proto`
- Creates a standalone-ready package.json
- Safe to run in monorepo (doesn't modify current package.json)
### `config-manager.js`
**Utility for switching between monorepo and standalone configurations.**
```bash
# Show current mode
node scripts/config-manager.js status
# Switch to standalone mode
node scripts/config-manager.js switch standalone
# Switch back to monorepo mode
node scripts/config-manager.js switch monorepo
```
### `validate-standalone.js`
**Validates that standalone setup is working correctly.**
```bash
node scripts/validate-standalone.js
```
**Checks:**
- Required files exist
- Package.json has required scripts and dependencies
- Dependencies can be resolved
- TypeScript compilation works
## Workflow Examples
### For Developers (Monorepo)
1. **Working in monorepo:**
```bash
# Normal development - uses workspace:* dependencies
pnpm dev
```
2. **Testing standalone version:**
```bash
# Generate standalone package.json (safe, doesn't modify current setup)
node scripts/build-standalone.js
# Switch to standalone mode for testing
node scripts/config-manager.js switch standalone
npm install
npm run dev
# Switch back to monorepo mode
node scripts/config-manager.js switch monorepo
```
### For Customers (Standalone)
1. **Initial setup:**
```bash
git clone <standalone-repo>
cd login
./prepare-standalone.sh --install
```
2. **Development:**
```bash
npm run dev
```
3. **Production:**
```bash
npm run build:standalone
npm run start
```
### For CI/CD (Package Publishing)
1. **After publishing new @zitadel packages:**
```bash
# Update standalone version with latest packages
node scripts/build-standalone.js
# Commit and push to standalone branch/repo
git add package.standalone.json
git commit -m "Update standalone package versions"
git push origin standalone
```
## Configuration Files
The scripts manage these configuration files:
- `package.standalone.json` - Standalone version of package.json
- `tsconfig.standalone.json` - TypeScript config for standalone
- `.eslintrc.standalone.cjs` - ESLint config for standalone
- `prettier.config.standalone.mjs` - Prettier config for standalone
- `tailwind.config.standalone.mjs` - Tailwind config for standalone
## File Structure
```
scripts/
├── prepare-standalone.js # Main preparation script
├── build-standalone.js # Package.json generator
├── config-manager.js # Configuration switcher
└── README.md # This file
# Configuration files
├── package.json # Current active package.json
├── package.standalone.json # Generated standalone version
├── tsconfig.json # Current active TypeScript config
├── tsconfig.standalone.json # Standalone TypeScript config
├── .eslintrc.cjs # Current active ESLint config
├── .eslintrc.standalone.cjs # Standalone ESLint config
└── ...
```
## Troubleshooting
### "Could not fetch latest version" warnings
This is normal when packages haven't been published yet or npm registry is slow. The scripts will fall back to existing versions.
### Configuration file not found
Some configuration files are optional. The scripts will warn but continue without them.
### Permission denied on scripts
Make sure scripts are executable:
```bash
chmod +x scripts/*.js
chmod +x prepare-standalone.sh
```
### Package version conflicts
If you encounter version conflicts, try:
```bash
# Clean install
rm -rf node_modules package-lock.json
npm install
```
## Integration with Monorepo
These scripts are designed to work seamlessly with the existing monorepo structure:
- **Development**: Use normal `pnpm` commands in monorepo
- **Testing**: Use `config-manager.js` to test standalone mode
- **Publishing**: Use `build-standalone.js` to generate updated standalone configs
- **Customer Distribution**: Use `prepare-standalone.sh` for easy setup

View File

@@ -0,0 +1,104 @@
#!/usr/bin/env node
/**
* Prepare script for standalone version
* This script converts the monorepo version to a standalone version
*/
import fs from 'fs/promises';
import { execSync } from 'child_process';
const CONFIG_FILES = [
{
source: 'tsconfig.standalone.json',
target: 'tsconfig.json',
required: true
},
{
source: '.eslintrc.standalone.cjs',
target: '.eslintrc.cjs',
required: false
},
{
source: 'prettier.config.standalone.mjs',
target: 'prettier.config.mjs',
required: false
},
{
source: 'tailwind.config.standalone.mjs',
target: 'tailwind.config.mjs',
required: false
}
];
async function prepareStandalone() {
console.log('🔧 Preparing standalone version...\n');
const args = process.argv.slice(2);
const shouldInstall = args.includes('--install');
try {
// Step 1: Copy package.standalone.json to package.json
console.log('📦 Setting up package.json...');
const packageStandaloneExists = await fs.access('package.standalone.json').then(() => true).catch(() => false);
if (packageStandaloneExists) {
// Backup current package.json
await fs.copyFile('package.json', 'package.monorepo.backup.json');
console.log(' 💾 Backed up package.json → package.monorepo.backup.json');
// Copy standalone version
await fs.copyFile('package.standalone.json', 'package.json');
console.log(' ✅ package.standalone.json → package.json');
} else {
throw new Error('package.standalone.json not found!');
}
// Step 2: Copy configuration files
console.log('\n⚙ Setting up configuration files...');
for (const config of CONFIG_FILES) {
try {
const sourceExists = await fs.access(config.source).then(() => true).catch(() => false);
if (sourceExists) {
await fs.copyFile(config.source, config.target);
console.log(`${config.source}${config.target}`);
} else if (config.required) {
throw new Error(`Required file ${config.source} not found!`);
} else {
console.log(` ⚠️ ${config.source} not found, skipping`);
}
} catch (error) {
if (config.required) {
throw error;
}
console.warn(` ❌ Failed to copy ${config.source}: ${error.message}`);
}
}
// Step 3: Install dependencies if requested
if (shouldInstall) {
console.log('\n📥 Installing dependencies...');
try {
execSync('npm install', { stdio: 'inherit' });
console.log(' ✅ Dependencies installed successfully');
} catch (error) {
console.warn(' ⚠️ npm install failed, you may need to run it manually');
}
}
console.log('\n🎉 Standalone preparation complete!');
console.log('\n📋 Next steps:');
if (!shouldInstall) {
console.log(' 1. Run: npm install');
}
console.log(' 2. Run: npm run dev');
console.log(' 3. Start developing!\n');
} catch (error) {
console.error('\n❌ Failed to prepare standalone version:', error.message);
console.error('Please check the error above and try again.\n');
process.exit(1);
}
}
prepareStandalone();

View File

@@ -1,115 +1,15 @@
import sharedConfig from "@zitadel/tailwind-config/tailwind.config.mjs";
let colors = {
background: { light: { contrast: {} }, dark: { contrast: {} } },
primary: { light: { contrast: {} }, dark: { contrast: {} } },
warn: { light: { contrast: {} }, dark: { contrast: {} } },
text: { light: { contrast: {} }, dark: { contrast: {} } },
link: { light: { contrast: {} }, dark: { contrast: {} } },
};
const shades = [
"50",
"100",
"200",
"300",
"400",
"500",
"600",
"700",
"800",
"900",
];
const themes = ["light", "dark"];
const types = ["background", "primary", "warn", "text", "link"];
types.forEach((type) => {
themes.forEach((theme) => {
shades.forEach((shade) => {
colors[type][theme][shade] = `var(--theme-${theme}-${type}-${shade})`;
colors[type][theme][`contrast-${shade}`] =
`var(--theme-${theme}-${type}-contrast-${shade})`;
colors[type][theme][`secondary-${shade}`] =
`var(--theme-${theme}-${type}-secondary-${shade})`;
});
});
});
/** @type {import('tailwindcss').Config} */
export default {
presets: [sharedConfig],
darkMode: "class",
content: ["./src/**/*.{js,ts,jsx,tsx}"],
future: {
hoverOnlyWhenSupported: true,
},
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
...colors,
state: {
success: {
light: {
background: "#cbf4c9",
color: "#0e6245",
},
dark: {
background: "#68cf8340",
color: "#cbf4c9",
},
},
error: {
light: {
background: "#ffc1c1",
color: "#620e0e",
},
dark: {
background: "#af455359",
color: "#ffc1c1",
},
},
neutral: {
light: {
background: "#e4e7e4",
color: "#000000",
},
dark: {
background: "#1a253c",
color: "#ffffff",
},
},
alert: {
light: {
background: "#fbbf24",
color: "#92400e",
},
dark: {
background: "#92400e50",
color: "#fbbf24",
},
},
},
},
animation: {
shake: "shake .8s cubic-bezier(.36,.07,.19,.97) both;",
},
keyframes: {
shake: {
"10%, 90%": {
transform: "translate3d(-1px, 0, 0)",
},
"20%, 80%": {
transform: "translate3d(2px, 0, 0)",
},
"30%, 50%, 70%": {
transform: "translate3d(-4px, 0, 0)",
},
"40%, 60%": {
transform: "translate3d(4px, 0, 0)",
},
},
background: "var(--background)",
foreground: "var(--foreground)",
},
},
},

31
login/apps/login/tsconfig.json Executable file → Normal file
View File

@@ -1,24 +1,27 @@
{
"extends": "@zitadel/tsconfig/nextjs.json",
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"target": "es2022",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"incremental": true,
"plugins": [
{
"name": "next"
}
]
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"custom-config.js"
],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

View File

@@ -5,6 +5,10 @@
"access": "public"
},
"main": "index.js",
"exports": {
".": "./index.js",
"./tailwind.config.mjs": "./tailwind.config.mjs"
},
"devDependencies": {
"tailwindcss": "^4.1.4",
"@tailwindcss/forms": "0.5.3"