@@ -84,6 +84,7 @@ export class RunController extends EventEmitter implements IRunController {
8484 projectDir ,
8585 deviceDescriptors ,
8686 platforms ,
87+ liveSyncInfo ,
8788 ) ;
8889
8990 const shouldStartWatcher =
@@ -233,6 +234,112 @@ export class RunController extends EventEmitter implements IRunController {
233234 ) ;
234235 }
235236
237+ /**
238+ * Restarts the application of a running session without preparing,
239+ * building or syncing anything. Queued on the session's action chain so it
240+ * cannot overtake a sync that is already under way, and routed through
241+ * `refreshApplication` so a debug session gets its debugger back.
242+ */
243+ public async restartApplication (
244+ data : IRestartApplicationData ,
245+ ) : Promise < void > {
246+ const { projectDir, deviceIdentifiers } = data ;
247+ const liveSyncProcessInfo =
248+ this . $liveSyncProcessDataService . getPersistedData ( projectDir ) ;
249+
250+ if ( ! liveSyncProcessInfo || liveSyncProcessInfo . isStopped ) {
251+ this . $logger . info (
252+ "There is no running application to restart. Start a run or debug session first." ,
253+ ) ;
254+ return ;
255+ }
256+
257+ const deviceDescriptors = (
258+ liveSyncProcessInfo . deviceDescriptors || [ ]
259+ ) . filter (
260+ ( descriptor ) =>
261+ ! deviceIdentifiers ||
262+ ! deviceIdentifiers . length ||
263+ _ . includes ( deviceIdentifiers , descriptor . identifier ) ,
264+ ) ;
265+
266+ if ( ! deviceDescriptors . length ) {
267+ this . $logger . info ( "There is no device to restart the application on." ) ;
268+ return ;
269+ }
270+
271+ const projectData = this . $projectDataService . getProjectData ( projectDir ) ;
272+ const useHotModuleReload =
273+ ! ! liveSyncProcessInfo . liveSyncInfo ?. useHotModuleReload ;
274+
275+ const deviceAction = async ( device : Mobile . IDevice ) => {
276+ const deviceDescriptor = _ . find (
277+ deviceDescriptors ,
278+ ( dd ) => dd . identifier === device . deviceInfo . identifier ,
279+ ) ;
280+
281+ try {
282+ const platformLiveSyncService =
283+ this . $liveSyncServiceResolver . resolveLiveSyncService (
284+ device . deviceInfo . platform ,
285+ ) ;
286+ const deviceAppData = await platformLiveSyncService . getAppData ( {
287+ device,
288+ watch : true ,
289+ projectData,
290+ liveSyncDeviceData : deviceDescriptor ,
291+ useHotModuleReload,
292+ } ) ;
293+
294+ await this . refreshApplication (
295+ projectData ,
296+ {
297+ deviceAppData,
298+ modifiedFilesData : [ ] ,
299+ isFullSync : false ,
300+ useHotModuleReload,
301+ } ,
302+ // Neither a hot update nor a native change, which is what
303+ // `refreshApplicationWithoutDebug` reads as "restart".
304+ {
305+ files : [ ] ,
306+ staleFiles : [ ] ,
307+ hasOnlyHotUpdateFiles : false ,
308+ hasNativeChanges : false ,
309+ hmrData : null ,
310+ platform : device . deviceInfo . platform . toLowerCase ( ) ,
311+ } ,
312+ deviceDescriptor ,
313+ ) ;
314+ } catch ( err ) {
315+ this . $logger . warn (
316+ `Unable to restart the application on device: ${ device . deviceInfo . identifier } . Error is: ${ err . message || err } .` ,
317+ ) ;
318+ this . $logger . trace ( err ) ;
319+
320+ this . emitCore ( RunOnDeviceEvents . runOnDeviceError , {
321+ projectDir : projectData . projectDir ,
322+ deviceIdentifier : device . deviceInfo . identifier ,
323+ applicationIdentifier :
324+ projectData . projectIdentifiers [
325+ device . deviceInfo . platform . toLowerCase ( )
326+ ] ,
327+ error : err ,
328+ } ) ;
329+ }
330+ } ;
331+
332+ await this . addActionToChain ( projectDir , ( ) =>
333+ this . $devicesService . execute ( deviceAction , ( device : Mobile . IDevice ) =>
334+ _ . some (
335+ deviceDescriptors ,
336+ ( deviceDescriptor ) =>
337+ deviceDescriptor . identifier === device . deviceInfo . identifier ,
338+ ) ,
339+ ) ,
340+ ) ;
341+ }
342+
236343 protected async refreshApplication (
237344 projectData : IProjectData ,
238345 liveSyncResultInfo : ILiveSyncResultInfo ,
0 commit comments