1+ #!/usr/bin/env node
2+
3+ /**
4+ * ReactPress Client CLI Entry Point
5+ * This script allows starting the ReactPress client via npx
6+ */
7+
8+ const path = require ( 'path' ) ;
9+ const fs = require ( 'fs' ) ;
10+ const { spawn, spawnSync } = require ( 'child_process' ) ;
11+ const url = require ( 'url' ) ;
12+
13+ // Get command line arguments
14+ const args = process . argv . slice ( 2 ) ;
15+ const usePM2 = args . includes ( '--pm2' ) ;
16+ const showHelp = args . includes ( '--help' ) || args . includes ( '-h' ) ;
17+
18+ // Show help if requested
19+ if ( showHelp ) {
20+ console . log ( `
21+ ReactPress Client - Next.js-based frontend for ReactPress CMS
22+
23+ Usage:
24+ npx @fecommunity/reactpress-client [options]
25+
26+ Options:
27+ --pm2 Start client with PM2 process manager
28+ --help, -h Show this help message
29+
30+ Examples:
31+ npx @fecommunity/reactpress-client # Start client in development mode
32+ npx @fecommunity/reactpress-client --pm2 # Start client with PM2
33+ npx @fecommunity/reactpress-client --help # Show this help message
34+ ` ) ;
35+ process . exit ( 0 ) ;
36+ }
37+
38+ // Get the directory where this script is located
39+ const binDir = __dirname ;
40+ const clientDir = path . join ( binDir , '..' ) ;
41+
42+ // Change to the client directory
43+ process . chdir ( clientDir ) ;
44+
45+ // Try to load configuration
46+ let port = 3001 ;
47+ let clientSiteUrl = 'http://localhost:3001' ;
48+
49+ try {
50+ const { config } = require ( '@fecommunity/reactpress-config' ) ;
51+
52+ // Extract port from CLIENT_SITE_URL or use CLIENT_PORT
53+ if ( config . CLIENT_SITE_URL ) {
54+ try {
55+ const parsedUrl = url . parse ( config . CLIENT_SITE_URL ) ;
56+ if ( parsedUrl . port ) {
57+ port = parseInt ( parsedUrl . port , 10 ) ;
58+ } else if ( parsedUrl . protocol === 'https:' ) {
59+ port = 443 ;
60+ } else {
61+ port = 80 ;
62+ }
63+ clientSiteUrl = config . CLIENT_SITE_URL ;
64+ } catch ( err ) {
65+ console . warn ( '[ReactPress Client] Failed to parse CLIENT_SITE_URL, using default port 3001' ) ;
66+ }
67+ } else if ( config . CLIENT_PORT ) {
68+ port = parseInt ( config . CLIENT_PORT , 10 ) ;
69+ }
70+ } catch ( err ) {
71+ console . warn ( '[ReactPress Client] Failed to load configuration, using default settings' ) ;
72+ }
73+
74+ // Function to check if PM2 is installed
75+ function isPM2Installed ( ) {
76+ try {
77+ require . resolve ( 'pm2' ) ;
78+ return true ;
79+ } catch ( e ) {
80+ // Check if PM2 is installed globally
81+ try {
82+ spawnSync ( 'pm2' , [ '--version' ] , { stdio : 'ignore' } ) ;
83+ return true ;
84+ } catch ( e ) {
85+ return false ;
86+ }
87+ }
88+ }
89+
90+ // Function to install PM2
91+ function installPM2 ( ) {
92+ console . log ( '[ReactPress Client] Installing PM2...' ) ;
93+ const installResult = spawnSync ( 'npm' , [ 'install' , 'pm2' , '--no-save' ] , {
94+ stdio : 'inherit' ,
95+ cwd : clientDir
96+ } ) ;
97+
98+ if ( installResult . status !== 0 ) {
99+ console . error ( '[ReactPress Client] Failed to install PM2' ) ;
100+ return false ;
101+ }
102+
103+ return true ;
104+ }
105+
106+ // Function to start with PM2
107+ function startWithPM2 ( ) {
108+ // Check if PM2 is installed
109+ if ( ! isPM2Installed ( ) ) {
110+ // Try to install PM2
111+ if ( ! installPM2 ( ) ) {
112+ console . error ( '[ReactPress Client] Cannot start with PM2' ) ;
113+ process . exit ( 1 ) ;
114+ }
115+ }
116+
117+ // Check if the client is built
118+ const nextDir = path . join ( clientDir , '.next' ) ;
119+ if ( ! fs . existsSync ( nextDir ) ) {
120+ console . log ( '[ReactPress Client] Client not built yet. Building...' ) ;
121+
122+ // Try to build the client
123+ const buildResult = spawnSync ( 'npm' , [ 'run' , 'build' ] , {
124+ stdio : 'inherit' ,
125+ cwd : clientDir
126+ } ) ;
127+
128+ if ( buildResult . status !== 0 ) {
129+ console . error ( '[ReactPress Client] Failed to build client' ) ;
130+ process . exit ( 1 ) ;
131+ }
132+ }
133+
134+ console . log ( '[ReactPress Client] Starting with PM2...' ) ;
135+
136+ // Use PM2 to start the Next.js production server
137+ let pm2Command = 'pm2' ;
138+ try {
139+ // Try to resolve PM2 path
140+ pm2Command = path . join ( clientDir , 'node_modules' , '.bin' , 'pm2' ) ;
141+ if ( ! fs . existsSync ( pm2Command ) ) {
142+ pm2Command = 'pm2' ;
143+ }
144+ } catch ( e ) {
145+ pm2Command = 'pm2' ;
146+ }
147+
148+ // Start with PM2 using direct command
149+ const pm2 = spawn ( pm2Command , [ 'start' , 'npm' , '--name' , 'reactpress-client' , '--' , 'run' , 'start' ] , {
150+ stdio : 'inherit' ,
151+ cwd : clientDir
152+ } ) ;
153+
154+ pm2 . on ( 'close' , ( code ) => {
155+ console . log ( `[ReactPress Client] PM2 process exited with code ${ code } ` ) ;
156+ process . exit ( code ) ;
157+ } ) ;
158+
159+ pm2 . on ( 'error' , ( error ) => {
160+ console . error ( '[ReactPress Client] Failed to start with PM2:' , error ) ;
161+ process . exit ( 1 ) ;
162+ } ) ;
163+ }
164+
165+ // Function to start with regular Node.js
166+ function startWithNode ( ) {
167+ // Check if we're in development or production mode
168+ const isDev = process . env . NODE_ENV !== 'production' ;
169+
170+ if ( isDev ) {
171+ // In development mode, start Next.js dev server
172+ console . log ( '[ReactPress Client] Starting Next.js development server...' ) ;
173+
174+ // Use Next.js CLI directly
175+ const nextDev = spawn ( 'npx' , [ 'next' , 'dev' , '-p' , port . toString ( ) ] , {
176+ stdio : 'inherit' ,
177+ cwd : clientDir
178+ } ) ;
179+
180+ nextDev . on ( 'close' , ( code ) => {
181+ console . log ( `[ReactPress Client] Next.js dev server exited with code ${ code } ` ) ;
182+ process . exit ( code ) ;
183+ } ) ;
184+
185+ nextDev . on ( 'error' , ( error ) => {
186+ console . error ( '[ReactPress Client] Failed to start Next.js dev server:' , error ) ;
187+ process . exit ( 1 ) ;
188+ } ) ;
189+ } else {
190+ // In production mode, check if the app is built and start it
191+ const nextDir = path . join ( clientDir , '.next' ) ;
192+
193+ if ( ! fs . existsSync ( nextDir ) ) {
194+ console . log ( '[ReactPress Client] Client not built yet. Building...' ) ;
195+
196+ // Try to build the client
197+ const buildResult = spawnSync ( 'npm' , [ 'run' , 'build' ] , {
198+ stdio : 'inherit' ,
199+ cwd : clientDir
200+ } ) ;
201+
202+ if ( buildResult . status !== 0 ) {
203+ console . error ( '[ReactPress Client] Failed to build client' ) ;
204+ process . exit ( 1 ) ;
205+ }
206+ }
207+
208+ console . log ( '[ReactPress Client] Starting Next.js production server...' ) ;
209+
210+ // Start Next.js production server
211+ const nextStart = spawn ( 'npx' , [ 'next' , 'start' , '-p' , port . toString ( ) ] , {
212+ stdio : 'inherit' ,
213+ cwd : clientDir
214+ } ) ;
215+
216+ nextStart . on ( 'close' , ( code ) => {
217+ console . log ( `[ReactPress Client] Next.js production server exited with code ${ code } ` ) ;
218+ process . exit ( code ) ;
219+ } ) ;
220+
221+ nextStart . on ( 'error' , ( error ) => {
222+ console . error ( '[ReactPress Client] Failed to start Next.js production server:' , error ) ;
223+ process . exit ( 1 ) ;
224+ } ) ;
225+ }
226+ }
227+
228+ console . log ( `[ReactPress Client] Starting client on port ${ port } ...` ) ;
229+
230+ // Main execution
231+ if ( usePM2 ) {
232+ startWithPM2 ( ) ;
233+ } else {
234+ startWithNode ( ) ;
235+ }
236+
237+ // Try to automatically open browser after a short delay (only in dev mode and not with PM2)
238+ if ( ! usePM2 && process . env . NODE_ENV !== 'production' ) {
239+ setTimeout ( ( ) => {
240+ try {
241+ require ( 'open' ) ( clientSiteUrl ) ;
242+ } catch ( err ) {
243+ console . warn ( '[ReactPress Client] Failed to open browser automatically' ) ;
244+ }
245+ } , 3000 ) ;
246+ }
0 commit comments