1- import type { Cli , Worker } from '@youcan/cli-kit' ;
1+ import type { Worker } from '@youcan/cli-kit' ;
22import process from 'node:process' ;
3- import { bootAppWorker , bootExtensionWorker , bootTunnelWorker , bootWebWorker } from '@/cli/services/dev/workers' ;
4- import { getAppEnvironmentVariables } from '@/cli/services/environment-variables' ;
5- import { APP_CONFIG_FILENAME } from '@/constants' ;
3+ import { bootAppWorker , bootExtensionWorker , bootWebWorker } from '@/cli/services/dev/workers' ;
64import { AppCommand } from '@/util/app-command' ;
75import { load } from '@/util/app-loader' ;
8- import { Env , Filesystem , Http , Path , Services , Session , System , Tasks , UI } from '@youcan/cli-kit' ;
6+ import { Flags } from '@oclif/core' ;
7+ import { Env , Http , Services , Session , System , Tasks , UI } from '@youcan/cli-kit' ;
98
109interface Context {
1110 cmd : Dev ;
@@ -14,7 +13,17 @@ interface Context {
1413
1514class Dev extends AppCommand {
1615 static description = 'Run the app in dev mode' ;
16+
17+ static flags = {
18+ 'no-tunnel' : Flags . boolean ( {
19+ description : 'Skip cloudflared tunnel and use localhost URL directly' ,
20+ default : false ,
21+ } ) ,
22+ } ;
23+
1724 private workers : Worker . Interface [ ] = [ ] ;
25+ private hasWebWorker = false ;
26+ private useTunnel = false ;
1827
1928 constructor ( argv : string [ ] , config : any ) {
2029 super ( argv , config ) ;
@@ -59,93 +68,80 @@ class Dev extends AppCommand {
5968 } ) ;
6069 }
6170
62- private readonly hotKeys = [
63- {
64- keyboardKey : 'p' ,
65- description : 'preview in your dev store' ,
66- handler : async ( ) => this . openAppPreview ( ) ,
67- } ,
68- {
69- keyboardKey : 'q' ,
70- description : 'quit' ,
71- handler : async ( ) => {
72- try {
73- console . log ( 'Shutting down...' ) ;
74-
75- if ( this . workers . length > 0 ) {
76- await Promise . allSettled ( this . workers . map ( worker => worker . cleanup ( ) ) ) ;
77- }
78-
79- if ( this . controller ) {
80- this . controller . abort ( ) ;
71+ private get hotKeys ( ) {
72+ return [
73+ {
74+ keyboardKey : 'p' ,
75+ description : this . hasWebWorker ? 'preview in your dev store' : 'open theme editor' ,
76+ handler : async ( ) => this . openAppPreview ( ) ,
77+ } ,
78+ {
79+ keyboardKey : 'q' ,
80+ description : 'quit' ,
81+ handler : async ( ) => {
82+ try {
83+ console . log ( 'Shutting down...' ) ;
84+
85+ if ( this . workers . length > 0 ) {
86+ await Promise . allSettled ( this . workers . map ( worker => worker . cleanup ( ) ) ) ;
87+ }
88+
89+ if ( this . controller ) {
90+ this . controller . abort ( ) ;
91+ }
92+ this . workers = [ ] ;
93+
94+ setTimeout ( ( ) => {
95+ process . exit ( 0 ) ;
96+ } , 100 ) ;
8197 }
82- this . workers = [ ] ;
83-
84- setTimeout ( ( ) => {
98+ catch ( error ) {
8599 process . exit ( 0 ) ;
86- } , 100 ) ;
87- }
88- catch ( error ) {
89- process . exit ( 0 ) ;
90- }
100+ }
101+ } ,
91102 } ,
92- } ,
93- ] ;
103+ ] ;
104+ }
94105
95106 async run ( ) : Promise < any > {
107+ const { flags } = await this . parse ( Dev ) ;
96108 this . session = await Session . authenticate ( this ) ;
97109 this . app = await load ( ) ;
98110
99- const { workers } = await Tasks . run < Context > ( { cmd : this , workers : [ ] } , [
100- {
101- title : 'Preparing network options...' ,
102- task : async ( ctx ) => {
103- ctx . workers . push ( await this . prepareNetworkOptions ( ) ) ;
111+ this . hasWebWorker = this . app . webs . length > 0 ;
112+ this . useTunnel = this . hasWebWorker && ! flags [ 'no-tunnel' ] ;
113+
114+ const tasks = [ ] ;
115+
116+ if ( this . hasWebWorker ) {
117+ tasks . push ( {
118+ title : this . useTunnel ? 'Preparing network options...' : 'Preparing network options (localhost)...' ,
119+ task : async ( ctx : Context ) => {
120+ ctx . workers . push ( ...await this . bootWebWorkers ( ) ) ;
104121 } ,
105- } ,
122+ } ) ;
123+ }
124+
125+ tasks . push (
106126 {
107127 title : 'Syncing app configuration...' ,
108128 task : async ( ) => { await this . syncAppConfig ( ) ; } ,
109129 } ,
110130 {
111131 title : 'Preparing dev processes...' ,
112- task : async ( ctx ) => {
132+ task : async ( ctx : Context ) => {
113133 ctx . workers . push ( ...await this . prepareDevProcesses ( ) ) ;
114134 } ,
115135 } ,
116- ] ) ;
136+ ) ;
137+
138+ const { workers } = await Tasks . run < Context > ( { cmd : this , workers : [ ] } , tasks ) ;
117139
118140 UI . renderDevOutput ( { hotKeys : this . hotKeys , cmd : this } ) ;
119141
120142 this . runWorkers ( workers ) ;
121143 }
122144
123- private async prepareNetworkOptions ( ) {
124- const port = await System . getPortOrNextOrRandom ( 3000 ) ;
125-
126- this . app . network_config = {
127- app_port : port ,
128- app_url : `http://localhost:${ port } ` ,
129- } ;
130-
131- const worker = await bootTunnelWorker ( this , this . app , new Services . Cloudflared ( ) ) ;
132-
133- this . app . config = {
134- ...this . app . config ,
135- app_url : worker . getUrl ( ) ,
136- redirect_urls : this . app . config . redirect_urls ?. length > 0
137- ? this . app . config . redirect_urls . map ( r => new URL ( new URL ( r ) . pathname , worker . getUrl ( ) ) . toString ( ) )
138- : [ new URL ( '/auth/callback' , worker . getUrl ( ) ) . toString ( ) ] ,
139- } ;
140-
141- await Filesystem . writeJsonFile (
142- Path . join ( this . app . root , APP_CONFIG_FILENAME ) ,
143- this . app . config ,
144- ) ;
145-
146- return worker ;
147- }
148-
149145 async reloadWorkers ( ) {
150146 this . controller = new AbortController ( ) ;
151147
@@ -154,13 +150,23 @@ class Dev extends AppCommand {
154150 this . workers = [ ] ;
155151 }
156152
157- const networkConfig = this . app . network_config ;
158153 this . app = await load ( ) ;
159- this . app . network_config = networkConfig ;
154+
155+ const webWorkers = this . hasWebWorker ? await this . bootWebWorkers ( ) : [ ] ;
160156
161157 await this . syncAppConfig ( ) ;
162158
163- await this . runWorkers ( await this . prepareDevProcesses ( ) ) ;
159+ const devWorkers = await this . prepareDevProcesses ( ) ;
160+
161+ await this . runWorkers ( [ ...webWorkers , ...devWorkers ] ) ;
162+ }
163+
164+ private async bootWebWorkers ( ) : Promise < Worker . Interface [ ] > {
165+ return Promise . all (
166+ this . app . webs . map ( web =>
167+ bootWebWorker ( this , this . app , web , this . useTunnel ? new Services . Cloudflared ( ) : undefined ) ,
168+ ) ,
169+ ) ;
164170 }
165171
166172 private async runWorkers ( workers : Worker . Interface [ ] ) : Promise < void > {
@@ -174,36 +180,17 @@ class Dev extends AppCommand {
174180 bootAppWorker ( this , this . app ) ,
175181 ] ;
176182
177- this . app . webs . forEach ( web => promises . unshift (
178- bootWebWorker (
179- this ,
180- this . app ,
181- web ,
182- this . buildEnvironmentVariables ( ) ,
183- ) ,
184- ) ) ;
185-
186183 this . app . extensions . forEach ( ext => promises . unshift ( bootExtensionWorker ( this , this . app , ext ) ) ) ;
187184
188185 return Promise . all ( promises ) ;
189186 }
190187
191- private buildEnvironmentVariables ( ) : Record < string , string > {
192- if ( ! this . app . remote_config ) {
193- throw new Error ( 'remote app config not loaded' ) ;
194- }
195- if ( ! this . app . network_config ) {
196- throw new Error ( 'app network config is not set' ) ;
188+ private async openAppPreview ( ) {
189+ if ( ! this . hasWebWorker ) {
190+ System . open ( `https://${ Env . sellerAreaHostname ( ) } /admin/themes` ) ;
191+ return ;
197192 }
198193
199- return {
200- ...getAppEnvironmentVariables ( this . app ) ,
201- APP_URL : this . app . network_config . app_url ,
202- PORT : this . app . network_config . app_port . toString ( ) ,
203- } ;
204- }
205-
206- private async openAppPreview ( ) {
207194 const endpointUrl = `${ Env . apiHostname ( ) } /apps/${ this . app . config . id } /authorization-url` ;
208195 const { url } = await Http . get < { url : string } > ( endpointUrl ) ;
209196
0 commit comments