create-nexu 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +149 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +603 -0
  4. package/package.json +41 -0
  5. package/templates/default/.changeset/config.json +11 -0
  6. package/templates/default/.eslintignore +13 -0
  7. package/templates/default/.eslintrc.js +66 -0
  8. package/templates/default/.github/actions/quality/action.yml +53 -0
  9. package/templates/default/.github/dependabot.yml +51 -0
  10. package/templates/default/.github/workflows/deploy-dev.yml +83 -0
  11. package/templates/default/.github/workflows/deploy-prod.yml +83 -0
  12. package/templates/default/.github/workflows/deploy-rec.yml +83 -0
  13. package/templates/default/.husky/commit-msg +1 -0
  14. package/templates/default/.husky/pre-commit +1 -0
  15. package/templates/default/.prettierignore +7 -0
  16. package/templates/default/.prettierrc +19 -0
  17. package/templates/default/.vscode/extensions.json +14 -0
  18. package/templates/default/.vscode/settings.json +36 -0
  19. package/templates/default/apps/.gitkeep +0 -0
  20. package/templates/default/commitlint.config.js +26 -0
  21. package/templates/default/docker/docker-compose.dev.yml +49 -0
  22. package/templates/default/docker/docker-compose.prod.yml +64 -0
  23. package/templates/default/docker/docker-compose.yml +5 -0
  24. package/templates/default/package.json +56 -0
  25. package/templates/default/packages/cache/package.json +26 -0
  26. package/templates/default/packages/cache/src/index.ts +137 -0
  27. package/templates/default/packages/cache/tsconfig.json +9 -0
  28. package/templates/default/packages/cache/tsup.config.ts +9 -0
  29. package/templates/default/packages/config/eslint/index.js +20 -0
  30. package/templates/default/packages/config/package.json +9 -0
  31. package/templates/default/packages/config/typescript/base.json +26 -0
  32. package/templates/default/packages/constants/package.json +26 -0
  33. package/templates/default/packages/constants/src/index.ts +121 -0
  34. package/templates/default/packages/constants/tsconfig.json +9 -0
  35. package/templates/default/packages/constants/tsup.config.ts +9 -0
  36. package/templates/default/packages/logger/package.json +27 -0
  37. package/templates/default/packages/logger/src/index.ts +197 -0
  38. package/templates/default/packages/logger/tsconfig.json +11 -0
  39. package/templates/default/packages/logger/tsup.config.ts +9 -0
  40. package/templates/default/packages/result/package.json +26 -0
  41. package/templates/default/packages/result/src/index.ts +142 -0
  42. package/templates/default/packages/result/tsconfig.json +9 -0
  43. package/templates/default/packages/result/tsup.config.ts +9 -0
  44. package/templates/default/packages/types/package.json +26 -0
  45. package/templates/default/packages/types/src/index.ts +78 -0
  46. package/templates/default/packages/types/tsconfig.json +9 -0
  47. package/templates/default/packages/types/tsup.config.ts +10 -0
  48. package/templates/default/packages/ui/package.json +38 -0
  49. package/templates/default/packages/ui/src/components/Button.tsx +65 -0
  50. package/templates/default/packages/ui/src/components/Card.tsx +90 -0
  51. package/templates/default/packages/ui/src/components/Input.tsx +51 -0
  52. package/templates/default/packages/ui/src/index.ts +15 -0
  53. package/templates/default/packages/ui/tsconfig.json +11 -0
  54. package/templates/default/packages/ui/tsup.config.ts +11 -0
  55. package/templates/default/packages/utils/package.json +30 -0
  56. package/templates/default/packages/utils/src/index.test.ts +130 -0
  57. package/templates/default/packages/utils/src/index.ts +154 -0
  58. package/templates/default/packages/utils/tsconfig.json +10 -0
  59. package/templates/default/packages/utils/tsup.config.ts +10 -0
  60. package/templates/default/pnpm-workspace.yaml +3 -0
  61. package/templates/default/scripts/deploy.sh +25 -0
  62. package/templates/default/scripts/generate-app.sh +166 -0
  63. package/templates/default/scripts/publish-cli.sh +54 -0
  64. package/templates/default/scripts/setup.sh +70 -0
  65. package/templates/default/services/.env.example +16 -0
  66. package/templates/default/services/docker-compose.yml +207 -0
  67. package/templates/default/services/grafana/provisioning/dashboards/dashboards.yml +11 -0
  68. package/templates/default/services/grafana/provisioning/datasources/datasources.yml +9 -0
  69. package/templates/default/services/postgres/init/.gitkeep +2 -0
  70. package/templates/default/services/prometheus/prometheus.yml +13 -0
  71. package/templates/default/tsconfig.json +27 -0
  72. package/templates/default/turbo.json +40 -0
  73. package/templates/default/vitest.config.ts +15 -0
@@ -0,0 +1,130 @@
1
+ import { describe, it, expect } from 'vitest';
2
+
3
+ import {
4
+ capitalize,
5
+ slugify,
6
+ truncate,
7
+ pick,
8
+ omit,
9
+ isEmpty,
10
+ chunk,
11
+ unique,
12
+ isEmail,
13
+ isUrl,
14
+ clamp,
15
+ daysBetween,
16
+ } from './index';
17
+
18
+ describe('String utilities', () => {
19
+ describe('capitalize', () => {
20
+ it('should capitalize first letter', () => {
21
+ expect(capitalize('hello')).toBe('Hello');
22
+ });
23
+
24
+ it('should return empty string for empty input', () => {
25
+ expect(capitalize('')).toBe('');
26
+ });
27
+ });
28
+
29
+ describe('slugify', () => {
30
+ it('should convert string to slug', () => {
31
+ expect(slugify('Hello World')).toBe('hello-world');
32
+ });
33
+
34
+ it('should remove special characters', () => {
35
+ expect(slugify('Hello! World?')).toBe('hello-world');
36
+ });
37
+ });
38
+
39
+ describe('truncate', () => {
40
+ it('should truncate long strings', () => {
41
+ expect(truncate('Hello World', 8)).toBe('Hello...');
42
+ });
43
+
44
+ it('should not truncate short strings', () => {
45
+ expect(truncate('Hello', 10)).toBe('Hello');
46
+ });
47
+ });
48
+ });
49
+
50
+ describe('Object utilities', () => {
51
+ describe('pick', () => {
52
+ it('should pick specified keys', () => {
53
+ const obj = { a: 1, b: 2, c: 3 };
54
+ expect(pick(obj, ['a', 'c'])).toEqual({ a: 1, c: 3 });
55
+ });
56
+ });
57
+
58
+ describe('omit', () => {
59
+ it('should omit specified keys', () => {
60
+ const obj = { a: 1, b: 2, c: 3 };
61
+ expect(omit(obj, ['b'])).toEqual({ a: 1, c: 3 });
62
+ });
63
+ });
64
+
65
+ describe('isEmpty', () => {
66
+ it('should return true for empty values', () => {
67
+ expect(isEmpty(null)).toBe(true);
68
+ expect(isEmpty(undefined)).toBe(true);
69
+ expect(isEmpty('')).toBe(true);
70
+ expect(isEmpty([])).toBe(true);
71
+ expect(isEmpty({})).toBe(true);
72
+ });
73
+
74
+ it('should return false for non-empty values', () => {
75
+ expect(isEmpty('hello')).toBe(false);
76
+ expect(isEmpty([1])).toBe(false);
77
+ expect(isEmpty({ a: 1 })).toBe(false);
78
+ });
79
+ });
80
+ });
81
+
82
+ describe('Array utilities', () => {
83
+ describe('chunk', () => {
84
+ it('should split array into chunks', () => {
85
+ expect(chunk([1, 2, 3, 4, 5], 2)).toEqual([[1, 2], [3, 4], [5]]);
86
+ });
87
+ });
88
+
89
+ describe('unique', () => {
90
+ it('should remove duplicates', () => {
91
+ expect(unique([1, 2, 2, 3, 3, 3])).toEqual([1, 2, 3]);
92
+ });
93
+ });
94
+ });
95
+
96
+ describe('Validation utilities', () => {
97
+ describe('isEmail', () => {
98
+ it('should validate emails', () => {
99
+ expect(isEmail('test@example.com')).toBe(true);
100
+ expect(isEmail('invalid')).toBe(false);
101
+ });
102
+ });
103
+
104
+ describe('isUrl', () => {
105
+ it('should validate URLs', () => {
106
+ expect(isUrl('https://example.com')).toBe(true);
107
+ expect(isUrl('not-a-url')).toBe(false);
108
+ });
109
+ });
110
+ });
111
+
112
+ describe('Number utilities', () => {
113
+ describe('clamp', () => {
114
+ it('should clamp values', () => {
115
+ expect(clamp(5, 0, 10)).toBe(5);
116
+ expect(clamp(-5, 0, 10)).toBe(0);
117
+ expect(clamp(15, 0, 10)).toBe(10);
118
+ });
119
+ });
120
+ });
121
+
122
+ describe('Date utilities', () => {
123
+ describe('daysBetween', () => {
124
+ it('should calculate days between dates', () => {
125
+ const date1 = new Date('2024-01-01');
126
+ const date2 = new Date('2024-01-10');
127
+ expect(daysBetween(date1, date2)).toBe(9);
128
+ });
129
+ });
130
+ });
@@ -0,0 +1,154 @@
1
+ // String utilities
2
+ export function capitalize(str: string): string {
3
+ if (!str) return '';
4
+ return str.charAt(0).toUpperCase() + str.slice(1);
5
+ }
6
+
7
+ export function slugify(str: string): string {
8
+ return str
9
+ .toLowerCase()
10
+ .trim()
11
+ .replace(/[^\w\s-]/g, '')
12
+ .replace(/[\s_-]+/g, '-')
13
+ .replace(/^-+|-+$/g, '');
14
+ }
15
+
16
+ export function truncate(str: string, length: number, suffix = '...'): string {
17
+ if (str.length <= length) return str;
18
+ return str.slice(0, length - suffix.length) + suffix;
19
+ }
20
+
21
+ // Object utilities
22
+ export function pick<T extends object, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
23
+ const result = {} as Pick<T, K>;
24
+ keys.forEach(key => {
25
+ if (key in obj) {
26
+ result[key] = obj[key];
27
+ }
28
+ });
29
+ return result;
30
+ }
31
+
32
+ export function omit<T extends object, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {
33
+ const result = { ...obj };
34
+ keys.forEach(key => {
35
+ delete result[key];
36
+ });
37
+ return result as Omit<T, K>;
38
+ }
39
+
40
+ export function isEmpty(value: unknown): boolean {
41
+ if (value == null) return true;
42
+ if (typeof value === 'string' || Array.isArray(value)) return value.length === 0;
43
+ if (typeof value === 'object') return Object.keys(value).length === 0;
44
+ return false;
45
+ }
46
+
47
+ // Array utilities
48
+ export function chunk<T>(array: T[], size: number): T[][] {
49
+ const chunks: T[][] = [];
50
+ for (let i = 0; i < array.length; i += size) {
51
+ chunks.push(array.slice(i, i + size));
52
+ }
53
+ return chunks;
54
+ }
55
+
56
+ export function unique<T>(array: T[]): T[] {
57
+ return [...new Set(array)];
58
+ }
59
+
60
+ export function groupBy<T, K extends string | number | symbol>(
61
+ array: T[],
62
+ keyFn: (item: T) => K
63
+ ): Record<K, T[]> {
64
+ return array.reduce(
65
+ (acc, item) => {
66
+ const key = keyFn(item);
67
+ if (!acc[key]) {
68
+ acc[key] = [];
69
+ }
70
+ acc[key].push(item);
71
+ return acc;
72
+ },
73
+ {} as Record<K, T[]>
74
+ );
75
+ }
76
+
77
+ // Date utilities
78
+ export function formatDate(date: Date, locale = 'en-US'): string {
79
+ return new Intl.DateTimeFormat(locale, {
80
+ year: 'numeric',
81
+ month: 'long',
82
+ day: 'numeric',
83
+ }).format(date);
84
+ }
85
+
86
+ export function isValidDate(date: unknown): date is Date {
87
+ return date instanceof Date && !isNaN(date.getTime());
88
+ }
89
+
90
+ export function daysBetween(date1: Date, date2: Date): number {
91
+ const oneDay = 24 * 60 * 60 * 1000;
92
+ return Math.round(Math.abs((date1.getTime() - date2.getTime()) / oneDay));
93
+ }
94
+
95
+ // Async utilities
96
+ export function sleep(ms: number): Promise<void> {
97
+ return new Promise(resolve => setTimeout(resolve, ms));
98
+ }
99
+
100
+ export async function retry<T>(
101
+ fn: () => Promise<T>,
102
+ options: { attempts?: number; delay?: number } = {}
103
+ ): Promise<T> {
104
+ const { attempts = 3, delay = 1000 } = options;
105
+ let lastError: Error | undefined;
106
+
107
+ for (let i = 0; i < attempts; i++) {
108
+ try {
109
+ return await fn();
110
+ } catch (error) {
111
+ lastError = error instanceof Error ? error : new Error(String(error));
112
+ if (i < attempts - 1) {
113
+ await sleep(delay);
114
+ }
115
+ }
116
+ }
117
+
118
+ throw lastError;
119
+ }
120
+
121
+ // Validation utilities
122
+ export function isEmail(email: string): boolean {
123
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
124
+ return emailRegex.test(email);
125
+ }
126
+
127
+ export function isUrl(url: string): boolean {
128
+ try {
129
+ new URL(url);
130
+ return true;
131
+ } catch {
132
+ return false;
133
+ }
134
+ }
135
+
136
+ // Number utilities
137
+ export function clamp(value: number, min: number, max: number): number {
138
+ return Math.min(Math.max(value, min), max);
139
+ }
140
+
141
+ export function randomInt(min: number, max: number): number {
142
+ return Math.floor(Math.random() * (max - min + 1)) + min;
143
+ }
144
+
145
+ export function formatNumber(num: number, locale = 'en-US'): string {
146
+ return new Intl.NumberFormat(locale).format(num);
147
+ }
148
+
149
+ export function formatCurrency(amount: number, currency = 'USD', locale = 'en-US'): string {
150
+ return new Intl.NumberFormat(locale, {
151
+ style: 'currency',
152
+ currency,
153
+ }).format(amount);
154
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../config/typescript/base.json",
3
+ "compilerOptions": {
4
+ "lib": ["ES2022", "DOM"],
5
+ "outDir": "./dist",
6
+ "rootDir": "./src"
7
+ },
8
+ "include": ["src/**/*"],
9
+ "exclude": ["node_modules", "dist"]
10
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entry: ['src/index.ts'],
5
+ format: ['cjs', 'esm'],
6
+ dts: true,
7
+ splitting: false,
8
+ sourcemap: true,
9
+ clean: true,
10
+ });
@@ -0,0 +1,3 @@
1
+ packages:
2
+ - 'apps/*'
3
+ - 'packages/*'
@@ -0,0 +1,25 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ ENVIRONMENT=${1:-staging}
6
+
7
+ echo "🚀 Deploying to $ENVIRONMENT..."
8
+
9
+ # Build all packages
10
+ echo "🔨 Building packages..."
11
+ pnpm build
12
+
13
+ # Build Docker images
14
+ echo "🐳 Building Docker images..."
15
+ docker-compose -f docker/docker-compose.prod.yml build
16
+
17
+ # Push images (if deploying to production)
18
+ if [ "$ENVIRONMENT" = "production" ]; then
19
+ echo "📤 Pushing images to registry..."
20
+ docker-compose -f docker/docker-compose.prod.yml push
21
+ fi
22
+
23
+ echo ""
24
+ echo "✅ Deployment to $ENVIRONMENT complete!"
25
+ echo ""
@@ -0,0 +1,166 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ # Colors
6
+ RED='\033[0;31m'
7
+ GREEN='\033[0;32m'
8
+ BLUE='\033[0;34m'
9
+ YELLOW='\033[1;33m'
10
+ NC='\033[0m'
11
+
12
+ # Get script directory
13
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14
+ ROOT_DIR="$(dirname "$SCRIPT_DIR")"
15
+ APPS_DIR="$ROOT_DIR/apps"
16
+ DOCKER_DIR="$ROOT_DIR/docker"
17
+
18
+ # Show usage
19
+ if [ -z "$1" ]; then
20
+ echo -e "${BLUE}Usage:${NC} pnpm generate:app <app-name> [port]"
21
+ echo ""
22
+ echo -e "${BLUE}Exemples:${NC}"
23
+ echo " pnpm generate:app web 3000"
24
+ echo " pnpm generate:app api 4000"
25
+ exit 1
26
+ fi
27
+
28
+ APP_NAME=$1
29
+ PORT=${2:-3000}
30
+ APP_DIR="$APPS_DIR/$APP_NAME"
31
+
32
+ # Check if app already exists
33
+ if [ -d "$APP_DIR" ]; then
34
+ echo -e "${RED}Error:${NC} L'application '$APP_NAME' existe déjà dans apps/"
35
+ exit 1
36
+ fi
37
+
38
+ echo -e "${BLUE}Creating${NC} app: $APP_NAME (port: $PORT)"
39
+
40
+ # Create app directory structure
41
+ mkdir -p "$APP_DIR/src"
42
+ mkdir -p "$APP_DIR/docker"
43
+
44
+ # Create Dockerfile
45
+ cat > "$APP_DIR/docker/Dockerfile" << EOF
46
+ # ====== Base ======
47
+ FROM node:20-alpine AS base
48
+ RUN corepack enable && corepack prepare pnpm@9.0.0 --activate
49
+ WORKDIR /app
50
+
51
+ # ====== Dependencies ======
52
+ FROM base AS deps
53
+ COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
54
+ COPY apps/$APP_NAME/package.json ./apps/$APP_NAME/
55
+ COPY packages/*/package.json ./packages/
56
+ RUN pnpm install --frozen-lockfile
57
+
58
+ # ====== Development ======
59
+ FROM base AS development
60
+ COPY --from=deps /app/node_modules ./node_modules
61
+ COPY . .
62
+ WORKDIR /app/apps/$APP_NAME
63
+ EXPOSE $PORT
64
+ CMD ["pnpm", "dev"]
65
+
66
+ # ====== Builder ======
67
+ FROM base AS builder
68
+ COPY --from=deps /app/node_modules ./node_modules
69
+ COPY . .
70
+ RUN pnpm turbo build --filter=@repo/$APP_NAME
71
+
72
+ # ====== Production ======
73
+ FROM node:20-alpine AS production
74
+ WORKDIR /app
75
+ ENV NODE_ENV=production
76
+
77
+ COPY --from=builder /app/apps/$APP_NAME/dist ./dist
78
+ COPY --from=builder /app/apps/$APP_NAME/package.json ./
79
+
80
+ RUN npm install --omit=dev
81
+
82
+ EXPOSE $PORT
83
+ CMD ["node", "dist/index.js"]
84
+ EOF
85
+
86
+ # Create docker-compose.yml for the app
87
+ cat > "$APP_DIR/docker-compose.yml" << EOF
88
+ services:
89
+ $APP_NAME:
90
+ build:
91
+ context: ../..
92
+ dockerfile: apps/$APP_NAME/docker/Dockerfile
93
+ target: development
94
+ ports:
95
+ - "$PORT:$PORT"
96
+ environment:
97
+ - NODE_ENV=development
98
+ - PORT=$PORT
99
+ volumes:
100
+ - ../../apps/$APP_NAME:/app/apps/$APP_NAME
101
+ - ../../packages:/app/packages
102
+ - /app/node_modules
103
+ - /app/apps/$APP_NAME/node_modules
104
+ command: pnpm dev
105
+ EOF
106
+
107
+ # Create docker-compose.prod.yml for the app
108
+ cat > "$APP_DIR/docker-compose.prod.yml" << EOF
109
+ services:
110
+ $APP_NAME:
111
+ build:
112
+ context: ../..
113
+ dockerfile: apps/$APP_NAME/docker/Dockerfile
114
+ target: production
115
+ ports:
116
+ - "$PORT:$PORT"
117
+ environment:
118
+ - NODE_ENV=production
119
+ - PORT=$PORT
120
+ restart: unless-stopped
121
+ EOF
122
+
123
+ # Update main docker-compose.yml
124
+ update_main_compose() {
125
+ MAIN_COMPOSE="$DOCKER_DIR/docker-compose.yml"
126
+ INCLUDE_PATH="../apps/$APP_NAME/docker-compose.yml"
127
+
128
+ # Create main compose if it doesn't exist
129
+ if [ ! -f "$MAIN_COMPOSE" ]; then
130
+ cat > "$MAIN_COMPOSE" << MAINEOF
131
+ # Main docker-compose - includes all apps
132
+ # Each app has its own docker-compose.yml in apps/<app-name>/
133
+
134
+ include:
135
+ - path: $INCLUDE_PATH
136
+ MAINEOF
137
+ return
138
+ fi
139
+
140
+ # Check if app is already included
141
+ if grep -q "apps/$APP_NAME/docker-compose.yml" "$MAIN_COMPOSE" 2>/dev/null; then
142
+ return
143
+ fi
144
+
145
+ # If include is empty array [], replace it
146
+ if grep -q "include: \[\]" "$MAIN_COMPOSE"; then
147
+ sed -i '' "s|include: \[\]|include:\n - path: $INCLUDE_PATH|" "$MAIN_COMPOSE"
148
+ else
149
+ # Append to existing include list
150
+ echo " - path: $INCLUDE_PATH" >> "$MAIN_COMPOSE"
151
+ fi
152
+ }
153
+
154
+ update_main_compose
155
+
156
+ echo -e "${GREEN}✓${NC} Created app: apps/$APP_NAME"
157
+ echo ""
158
+ echo -e "${YELLOW}Files created:${NC}"
159
+ echo " - apps/$APP_NAME/docker/Dockerfile"
160
+ echo " - apps/$APP_NAME/docker-compose.yml"
161
+ echo " - apps/$APP_NAME/docker-compose.prod.yml"
162
+ echo ""
163
+ echo -e "${YELLOW}Commands:${NC}"
164
+ echo " Dev (app only): cd apps/$APP_NAME && docker compose up"
165
+ echo " Dev (all apps): pnpm docker:dev"
166
+ echo " Prod (app only): cd apps/$APP_NAME && docker compose -f docker-compose.prod.yml up -d"
@@ -0,0 +1,54 @@
1
+ #!/bin/bash
2
+
3
+ # Publish create-nexu CLI to npm
4
+ # This script generates the template, builds, and publishes
5
+
6
+ set -e
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ ROOT_DIR="$(dirname "$SCRIPT_DIR")"
10
+ CLI_DIR="$ROOT_DIR/create-nexu"
11
+
12
+ echo "📦 Publishing create-nexu..."
13
+ echo ""
14
+
15
+ # Step 1: Generate template
16
+ echo "1️⃣ Generating template..."
17
+ "$SCRIPT_DIR/generate-template.sh"
18
+ echo ""
19
+
20
+ # Step 2: Build CLI
21
+ echo "2️⃣ Building CLI..."
22
+ cd "$CLI_DIR"
23
+ pnpm build
24
+ echo ""
25
+
26
+ # Step 3: Run checks
27
+ echo "3️⃣ Running checks..."
28
+ pnpm typecheck
29
+ pnpm lint
30
+ echo "✅ All checks passed"
31
+ echo ""
32
+
33
+ # Step 4: Show package info
34
+ echo "4️⃣ Package info:"
35
+ cat package.json | grep -E '"name"|"version"'
36
+ echo ""
37
+
38
+ # Step 5: Confirm publish
39
+ read -p "Publish to npm? (y/N) " -n 1 -r
40
+ echo ""
41
+
42
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
43
+ echo "5️⃣ Publishing to npm..."
44
+ npm publish --access public
45
+ echo ""
46
+ echo "✅ Published successfully!"
47
+ echo ""
48
+ echo "Users can now run:"
49
+ echo " npm create nexu my-app"
50
+ echo " # or"
51
+ echo " npx create-nexu my-app"
52
+ else
53
+ echo "❌ Publish cancelled"
54
+ fi
@@ -0,0 +1,70 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ echo "🚀 Setting up monorepo..."
6
+
7
+ # Check if pnpm is installed
8
+ if ! command -v pnpm &> /dev/null; then
9
+ echo "📦 Installing pnpm..."
10
+ npm install -g pnpm
11
+ fi
12
+
13
+ # Install dependencies
14
+ echo "📦 Installing dependencies..."
15
+ pnpm install
16
+
17
+ # Setup husky
18
+ echo "🐶 Setting up Husky..."
19
+ pnpm prepare
20
+
21
+ # Create environment files
22
+ echo "🔐 Creating environment files..."
23
+
24
+ if [ ! -f ".env" ]; then
25
+ cat > .env << EOF
26
+ # Database
27
+ DATABASE_URL=postgresql://postgres:postgres@localhost:5432/nexu
28
+ DB_USER=postgres
29
+ DB_PASSWORD=postgres
30
+ DB_NAME=nexu
31
+
32
+ # API
33
+ API_URL=http://localhost:4000
34
+
35
+ # App
36
+ NODE_ENV=development
37
+ EOF
38
+ echo "✅ Created .env file"
39
+ fi
40
+
41
+ if [ ! -f "apps/web/.env.local" ]; then
42
+ cat > apps/web/.env.local << EOF
43
+ NEXT_PUBLIC_API_URL=http://localhost:4000
44
+ EOF
45
+ echo "✅ Created apps/web/.env.local"
46
+ fi
47
+
48
+ if [ ! -f "apps/api/.env" ]; then
49
+ cat > apps/api/.env << EOF
50
+ DATABASE_URL=postgresql://postgres:postgres@localhost:5432/nexu
51
+ PORT=4000
52
+ NODE_ENV=development
53
+ EOF
54
+ echo "✅ Created apps/api/.env"
55
+ fi
56
+
57
+ # Build packages
58
+ echo "🔨 Building packages..."
59
+ pnpm build
60
+
61
+ echo ""
62
+ echo "✅ Setup complete!"
63
+ echo ""
64
+ echo "Available commands:"
65
+ echo " pnpm dev - Start development servers"
66
+ echo " pnpm build - Build all packages"
67
+ echo " pnpm lint - Run linting"
68
+ echo " pnpm test - Run tests"
69
+ echo " pnpm docker:dev - Start with Docker (dev)"
70
+ echo ""
@@ -0,0 +1,16 @@
1
+ # Database
2
+ POSTGRES_USER=nexu
3
+ POSTGRES_PASSWORD=nexu
4
+ POSTGRES_DB=nexu
5
+
6
+ # Messaging
7
+ RABBITMQ_USER=nexu
8
+ RABBITMQ_PASSWORD=nexu
9
+
10
+ # Monitoring
11
+ GRAFANA_USER=admin
12
+ GRAFANA_PASSWORD=admin
13
+
14
+ # Storage
15
+ MINIO_USER=nexu
16
+ MINIO_PASSWORD=nexu1234