create-pika-minigame 1.2.0 → 1.4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-pika-minigame",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "CLI to scaffold Pika mini-game projects (bare React Native 0.77)",
5
5
  "author": "Pika Team",
6
6
  "license": "MIT",
@@ -161,10 +161,46 @@ pod install
161
161
  cd ..
162
162
  ```
163
163
 
164
+ ## Deployment
165
+
166
+ ### Deploy to Vercel
167
+
168
+ ```bash
169
+ # Install Vercel CLI (first time only)
170
+ npm i -g vercel
171
+ vercel login
172
+
173
+ # Deploy preview
174
+ npm run deploy
175
+
176
+ # Deploy production
177
+ npm run deploy:prod
178
+ ```
179
+
180
+ After deployment, you'll get URLs like:
181
+ ```
182
+ https://your-game.vercel.app/ios/mf-manifest.json
183
+ https://your-game.vercel.app/android/mf-manifest.json
184
+ ```
185
+
186
+ ### Register with Backend
187
+
188
+ ```json
189
+ {
190
+ "id": "{{PROJECT_NAME_KEBAB}}",
191
+ "name": "{{PROJECT_NAME_PASCAL}}",
192
+ "scope": "{{PROJECT_NAME_SNAKE}}",
193
+ "module": "./App",
194
+ "container_url": "https://your-game.vercel.app/ios/mf-manifest.json",
195
+ "version": "1.0.0",
196
+ "min_host_version": "1.0.0"
197
+ }
198
+ ```
199
+
164
200
  ## Integration with Pika App
165
201
 
166
- Once ready, this mini-game can be loaded via Module Federation in the main Pika app.
202
+ Once deployed and registered, the game will appear in the Pika app's Games hub and can be loaded via Module Federation.
167
203
 
168
204
  ---
169
205
 
170
- Created with `create-pika-minigame` v1.1.0
206
+ Created with `create-pika-minigame`
@@ -9,6 +9,19 @@ import { HostBridge, PronunciationResult } from '../src/types';
9
9
 
10
10
  // Simulate recording state
11
11
  let isRecording = false;
12
+ let recordingStartTime = 0;
13
+
14
+ // Configure mock behavior (edit these for testing)
15
+ const MOCK_CONFIG = {
16
+ // Set to a number (0-100) to always return this score, or null for random
17
+ fixedScore: null as number | null,
18
+ // Minimum recording time in ms (like real API)
19
+ minRecordingMs: 700,
20
+ // Simulate network delay range [min, max] ms
21
+ networkDelay: [800, 1300] as [number, number],
22
+ // Chance to throw error (0-1), set > 0 to test error handling
23
+ errorChance: 0,
24
+ };
12
25
 
13
26
  /**
14
27
  * Generate a random score for testing different UI states.
@@ -43,41 +56,56 @@ export const mockHost: HostBridge = {
43
56
  }
44
57
  console.log('[MockHost] 🎤 Recording started...');
45
58
  isRecording = true;
59
+ recordingStartTime = Date.now();
46
60
  // Simulate recording initialization delay
47
61
  await delay(100);
48
62
  },
49
63
 
50
- stopAndCheck: async (targetWord: string): Promise<PronunciationResult> => {
64
+ stopAndCheck: async (targetText: string): Promise<PronunciationResult> => {
51
65
  if (!isRecording) {
52
66
  console.warn('[MockHost] Not recording');
53
67
  throw new Error('Not recording');
54
68
  }
55
69
 
56
- console.log(`[MockHost] 🛑 Recording stopped. Checking: "${targetWord}"`);
70
+ const elapsed = Date.now() - recordingStartTime;
71
+ console.log(`[MockHost] 🛑 Recording stopped (${elapsed}ms). Checking: "${targetText}"`);
57
72
  isRecording = false;
58
73
 
59
- // Simulate processing delay (like API call)
60
- await delay(800 + Math.random() * 500);
74
+ // Check minimum recording time (like real API)
75
+ if (elapsed < MOCK_CONFIG.minRecordingMs) {
76
+ throw new Error('Giữ nút lâu hơn (1–2 giây) rồi nói to, rõ nhé.');
77
+ }
61
78
 
62
- // Generate mock result
63
- const wordScore = randomScore(50, 100);
64
- const letters = generateLetterScores(targetWord);
65
- const totalScore = Math.round(
66
- letters.reduce((sum, l) => sum + l.score, 0) / letters.length
79
+ // Simulate random errors for testing
80
+ if (MOCK_CONFIG.errorChance > 0 && Math.random() < MOCK_CONFIG.errorChance) {
81
+ throw new Error('Chưa nghe rõ, thử lại nhé');
82
+ }
83
+
84
+ // Simulate network delay
85
+ const [minDelay, maxDelay] = MOCK_CONFIG.networkDelay;
86
+ await delay(minDelay + Math.random() * (maxDelay - minDelay));
87
+
88
+ // Split into words (real API does this)
89
+ const wordList = targetText.trim().split(/\s+/);
90
+
91
+ // Generate scores for each word
92
+ const words = wordList.map((word) => {
93
+ const letters = generateLetterScores(word);
94
+ // Use fixed score if configured, otherwise use average of letters
95
+ const wordScore = MOCK_CONFIG.fixedScore ?? Math.round(
96
+ letters.reduce((sum, l) => sum + l.score, 0) / letters.length
97
+ );
98
+ return { word, score: wordScore, letters };
99
+ });
100
+
101
+ // Total score is average of all words
102
+ const totalScore = MOCK_CONFIG.fixedScore ?? Math.round(
103
+ words.reduce((sum, w) => sum + w.score, 0) / words.length
67
104
  );
68
105
 
69
- console.log(`[MockHost] ✅ Score: ${totalScore}`);
70
-
71
- return {
72
- totalScore,
73
- words: [
74
- {
75
- word: targetWord,
76
- score: wordScore,
77
- letters,
78
- },
79
- ],
80
- };
106
+ console.log(`[MockHost] ✅ Score: ${totalScore} (${words.length} word${words.length > 1 ? 's' : ''})`);
107
+
108
+ return { totalScore, words };
81
109
  },
82
110
 
83
111
  cancel: async () => {
@@ -3,22 +3,22 @@
3
3
  "version": "1.0.0",
4
4
  "private": true,
5
5
  "description": "Pika mini-game: {{PROJECT_NAME_PASCAL}}",
6
- "main": "dev/index.js",
6
+ "main": "index.js",
7
7
  "scripts": {
8
- "dev": "expo start",
9
- "dev:ios": "expo run:ios",
10
- "dev:android": "expo run:android",
11
- "build:ios": "npx react-native webpack-bundle --platform ios --entry-file index.js --dev false --bundle-output build/outputs/ios/local/index.ios.bundle --assets-dest build/outputs/ios/local",
12
- "build:android": "npx react-native webpack-bundle --platform android --entry-file index.js --dev false --bundle-output build/outputs/android/local/index.android.bundle --assets-dest build/outputs/android/local",
8
+ "start": "react-native start",
9
+ "ios": "react-native run-ios",
10
+ "android": "react-native run-android",
11
+ "build:ios": "npx react-native webpack-bundle --platform ios --entry-file index.js --dev false --bundle-output build/outputs/ios/remotes/{{PROJECT_NAME_SNAKE}}.container.js.bundle",
12
+ "build:android": "npx react-native webpack-bundle --platform android --entry-file index.js --dev false --bundle-output build/outputs/android/remotes/{{PROJECT_NAME_SNAKE}}.container.js.bundle",
13
+ "build:all": "npm run build:ios && npm run build:android",
13
14
  "serve:ios": "npx serve build/outputs/ios/remotes -l 9000 --cors",
14
15
  "serve:android": "npx serve build/outputs/android/remotes -l 9000 --cors",
15
- "deploy": "echo 'Configure your deployment in package.json scripts'",
16
- "lint": "eslint src/ dev/",
16
+ "deploy": "./scripts/deploy-vercel.sh",
17
+ "deploy:prod": "./scripts/deploy-vercel.sh --prod",
18
+ "lint": "eslint src/",
17
19
  "typecheck": "tsc --noEmit"
18
20
  },
19
21
  "dependencies": {
20
- "expo": "~52.0.0",
21
- "expo-status-bar": "~2.0.0",
22
22
  "react": "18.3.1",
23
23
  "react-native": "0.77.0",
24
24
  "react-native-gesture-handler": "~2.20.0",
@@ -29,13 +29,8 @@
29
29
  "devDependencies": {
30
30
  "@babel/core": "^7.25.0",
31
31
  "@callstack/repack": "^5.0.0",
32
- "@callstack/repack-plugin-reanimated": "^1.0.0",
33
32
  "@types/react": "~18.3.0",
34
- "typescript": "~5.3.0"
35
- },
36
- "peerDependencies": {
37
- "react": "18.3.1",
38
- "react-native": "0.77.0",
39
- "react-native-reanimated": "~3.16.0"
33
+ "typescript": "~5.3.0",
34
+ "serve": "^14.2.0"
40
35
  }
41
36
  }
@@ -0,0 +1,202 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ # =============================================================================
5
+ # Deploy Pika Mini-Game to Vercel
6
+ # =============================================================================
7
+ #
8
+ # Usage:
9
+ # ./scripts/deploy-vercel.sh [--prod]
10
+ #
11
+ # Prerequisites:
12
+ # - Vercel CLI: npm i -g vercel
13
+ # - Logged in: vercel login
14
+ #
15
+ # =============================================================================
16
+
17
+ # Colors
18
+ RED='\033[0;31m'
19
+ GREEN='\033[0;32m'
20
+ YELLOW='\033[1;33m'
21
+ CYAN='\033[0;36m'
22
+ NC='\033[0m' # No Color
23
+
24
+ log_info() { echo -e "${CYAN}[INFO]${NC} $1"; }
25
+ log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
26
+ log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
27
+ log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
28
+
29
+ # Check if vercel CLI is installed
30
+ if ! command -v vercel &> /dev/null; then
31
+ log_error "Vercel CLI not found. Install with: npm i -g vercel"
32
+ exit 1
33
+ fi
34
+
35
+ # Parse arguments
36
+ PROD_FLAG=""
37
+ if [[ "$1" == "--prod" ]]; then
38
+ PROD_FLAG="--prod"
39
+ log_info "Deploying to PRODUCTION"
40
+ else
41
+ log_info "Deploying to PREVIEW (add --prod for production)"
42
+ fi
43
+
44
+ # Get project info from package.json
45
+ PROJECT_NAME=$(node -p "require('./package.json').name")
46
+ PROJECT_VERSION=$(node -p "require('./package.json').version")
47
+
48
+ log_info "Project: $PROJECT_NAME v$PROJECT_VERSION"
49
+
50
+ # =============================================================================
51
+ # Step 1: Build for all platforms
52
+ # =============================================================================
53
+ log_info "Building mini-game bundles..."
54
+
55
+ # Create build output directory
56
+ BUILD_DIR="build/deploy"
57
+ rm -rf "$BUILD_DIR"
58
+ mkdir -p "$BUILD_DIR/ios" "$BUILD_DIR/android"
59
+
60
+ # We'll set MF_PUBLIC_PATH after we know the Vercel URL
61
+ # For now, build with a placeholder that we'll update
62
+
63
+ log_info "Building iOS bundle..."
64
+ npx react-native webpack-bundle \
65
+ --platform ios \
66
+ --entry-file index.js \
67
+ --dev false \
68
+ --bundle-output build/outputs/ios/remotes/${PROJECT_NAME//-/_}.container.js.bundle \
69
+ 2>&1 | tail -5
70
+
71
+ log_info "Building Android bundle..."
72
+ npx react-native webpack-bundle \
73
+ --platform android \
74
+ --entry-file index.js \
75
+ --dev false \
76
+ --bundle-output build/outputs/android/remotes/${PROJECT_NAME//-/_}.container.js.bundle \
77
+ 2>&1 | tail -5
78
+
79
+ # Copy to deploy directory with platform subdirs
80
+ cp -r build/outputs/ios/remotes/* "$BUILD_DIR/ios/"
81
+ cp -r build/outputs/android/remotes/* "$BUILD_DIR/android/"
82
+
83
+ # Create index.html for Vercel (shows deployment info)
84
+ cat > "$BUILD_DIR/index.html" << EOF
85
+ <!DOCTYPE html>
86
+ <html>
87
+ <head>
88
+ <title>${PROJECT_NAME} - Pika Mini-Game</title>
89
+ <style>
90
+ body { font-family: -apple-system, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }
91
+ h1 { color: #0B0B0B; }
92
+ code { background: #f4f4f4; padding: 2px 8px; border-radius: 4px; }
93
+ .platform { margin: 20px 0; padding: 15px; background: #f9f9f9; border-radius: 8px; }
94
+ .platform h3 { margin-top: 0; }
95
+ a { color: #46C4D7; }
96
+ </style>
97
+ </head>
98
+ <body>
99
+ <h1>🎮 ${PROJECT_NAME}</h1>
100
+ <p>Version: <code>${PROJECT_VERSION}</code></p>
101
+ <p>Deployed: <code>$(date -u +"%Y-%m-%d %H:%M:%S UTC")</code></p>
102
+
103
+ <div class="platform">
104
+ <h3>📱 iOS</h3>
105
+ <p>Container: <a href="./ios/${PROJECT_NAME//-/_}.container.js.bundle">ios/${PROJECT_NAME//-/_}.container.js.bundle</a></p>
106
+ <p>Manifest: <a href="./ios/mf-manifest.json">ios/mf-manifest.json</a></p>
107
+ </div>
108
+
109
+ <div class="platform">
110
+ <h3>🤖 Android</h3>
111
+ <p>Container: <a href="./android/${PROJECT_NAME//-/_}.container.js.bundle">android/${PROJECT_NAME//-/_}.container.js.bundle</a></p>
112
+ <p>Manifest: <a href="./android/mf-manifest.json">android/mf-manifest.json</a></p>
113
+ </div>
114
+
115
+ <h2>Integration</h2>
116
+ <p>Register this game with your backend:</p>
117
+ <pre><code>{
118
+ "scope": "${PROJECT_NAME//-/_}",
119
+ "module": "./App",
120
+ "container_url": "[VERCEL_URL]/ios/mf-manifest.json",
121
+ "version": "${PROJECT_VERSION}"
122
+ }</code></pre>
123
+ </body>
124
+ </html>
125
+ EOF
126
+
127
+ # Create vercel.json for proper routing
128
+ cat > "$BUILD_DIR/vercel.json" << EOF
129
+ {
130
+ "headers": [
131
+ {
132
+ "source": "/(.*)",
133
+ "headers": [
134
+ { "key": "Access-Control-Allow-Origin", "value": "*" },
135
+ { "key": "Access-Control-Allow-Methods", "value": "GET, OPTIONS" },
136
+ { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
137
+ ]
138
+ }
139
+ ]
140
+ }
141
+ EOF
142
+
143
+ log_success "Build complete: $BUILD_DIR"
144
+
145
+ # =============================================================================
146
+ # Step 2: Deploy to Vercel
147
+ # =============================================================================
148
+ log_info "Deploying to Vercel..."
149
+
150
+ # Deploy and capture the URL
151
+ DEPLOY_OUTPUT=$(vercel "$BUILD_DIR" $PROD_FLAG --yes 2>&1)
152
+ DEPLOY_URL=$(echo "$DEPLOY_OUTPUT" | grep -E "https://" | tail -1 | tr -d ' ')
153
+
154
+ if [[ -z "$DEPLOY_URL" ]]; then
155
+ log_error "Failed to get deployment URL"
156
+ echo "$DEPLOY_OUTPUT"
157
+ exit 1
158
+ fi
159
+
160
+ log_success "Deployed to: $DEPLOY_URL"
161
+
162
+ # =============================================================================
163
+ # Step 3: Show integration info
164
+ # =============================================================================
165
+ echo ""
166
+ echo "=============================================="
167
+ echo -e "${GREEN}🎉 Deployment Complete!${NC}"
168
+ echo "=============================================="
169
+ echo ""
170
+ echo -e "URL: ${CYAN}$DEPLOY_URL${NC}"
171
+ echo ""
172
+ echo "iOS manifest: $DEPLOY_URL/ios/mf-manifest.json"
173
+ echo "Android manifest: $DEPLOY_URL/android/mf-manifest.json"
174
+ echo ""
175
+ echo "Register with backend API:"
176
+ echo ""
177
+ cat << EOF
178
+ {
179
+ "id": "${PROJECT_NAME}",
180
+ "name": "${PROJECT_NAME}",
181
+ "scope": "${PROJECT_NAME//-/_}",
182
+ "module": "./App",
183
+ "container_url": "$DEPLOY_URL/ios/mf-manifest.json",
184
+ "version": "${PROJECT_VERSION}",
185
+ "min_host_version": "1.0.0"
186
+ }
187
+ EOF
188
+ echo ""
189
+
190
+ # Save deployment info
191
+ cat > "deploy-info.json" << EOF
192
+ {
193
+ "url": "$DEPLOY_URL",
194
+ "project": "$PROJECT_NAME",
195
+ "version": "$PROJECT_VERSION",
196
+ "deployed_at": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
197
+ "ios_manifest": "$DEPLOY_URL/ios/mf-manifest.json",
198
+ "android_manifest": "$DEPLOY_URL/android/mf-manifest.json"
199
+ }
200
+ EOF
201
+
202
+ log_success "Deployment info saved to deploy-info.json"