Skip to content

Commit dbee735

Browse files
committed
feat: respect removed files on preview command
1 parent be10e63 commit dbee735

File tree

5 files changed

+107
-94
lines changed

5 files changed

+107
-94
lines changed

lib/definitions/preview-app-livesync.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { EventEmitter } from "events";
44
declare global {
55
interface IPreviewAppLiveSyncService {
66
initialize(data: IPreviewAppLiveSyncData): void;
7-
syncFiles(data: IPreviewAppLiveSyncData, filesToSync: string[]): Promise<void>;
7+
syncFiles(data: IPreviewAppLiveSyncData, filesToSync: string[], filesToRemove: string[]): Promise<void>;
88
stopLiveSync(): Promise<void>;
99
}
1010

lib/services/livesync/livesync-service.ts

+16-19
Original file line numberDiff line numberDiff line change
@@ -596,9 +596,15 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
596596

597597
const startSyncFilesTimeout = (platform?: string) => {
598598
timeoutTimer = setTimeout(async () => {
599-
if (liveSyncData.syncToPreviewApp) {
600-
await this.addActionToChain(projectData.projectDir, async () => {
601-
if (filesToSync.length || filesToRemove.length) {
599+
if (filesToSync.length || filesToRemove.length) {
600+
const currentFilesToSync = _.cloneDeep(filesToSync);
601+
filesToSync.splice(0, filesToSync.length);
602+
603+
const currentFilesToRemove = _.cloneDeep(filesToRemove);
604+
filesToRemove = [];
605+
606+
if (liveSyncData.syncToPreviewApp) {
607+
await this.addActionToChain(projectData.projectDir, async () => {
602608
await this.$previewAppLiveSyncService.syncFiles({
603609
appFilesUpdaterOptions: {
604610
bundle: liveSyncData.bundle,
@@ -607,22 +613,13 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
607613
},
608614
env: liveSyncData.env,
609615
projectDir: projectData.projectDir
610-
}, filesToSync);
611-
filesToSync = [];
612-
filesToRemove = [];
613-
}
614-
});
615-
} else {
616-
// Push actions to the queue, do not start them simultaneously
617-
await this.addActionToChain(projectData.projectDir, async () => {
618-
if (filesToSync.length || filesToRemove.length) {
616+
}, currentFilesToSync, currentFilesToRemove);
617+
});
618+
} else {
619+
// Push actions to the queue, do not start them simultaneously
620+
await this.addActionToChain(projectData.projectDir, async () => {
619621
try {
620-
const currentFilesToSync = _.cloneDeep(filesToSync);
621622
const currentHmrData = _.cloneDeep(hmrData);
622-
filesToSync.splice(0, filesToSync.length);
623-
624-
const currentFilesToRemove = _.cloneDeep(filesToRemove);
625-
filesToRemove = [];
626623

627624
const allModifiedFiles = [].concat(currentFilesToSync).concat(currentFilesToRemove);
628625

@@ -703,8 +700,8 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
703700
}
704701
}
705702
}
706-
}
707-
});
703+
});
704+
}
708705
}
709706
}, 250);
710707

lib/services/livesync/playground/preview-app-constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export class PreviewSdkEventNames {
22
public static CHANGE_EVENT_NAME = "change";
3+
public static UNLINK_EVENT_NAME = "unlink";
34
}
45

56
export class PubnubKeys {

lib/services/livesync/playground/preview-app-livesync-service.ts

+88-73
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ import { APP_FOLDER_NAME, APP_RESOURCES_FOLDER_NAME, TNS_MODULES_FOLDER_NAME } f
55
import { HmrConstants } from "../../../common/constants";
66
const isTextOrBinary = require('istextorbinary');
77

8+
interface ISyncFilesOptions {
9+
filesToSync?: string[];
10+
filesToRemove?: string[];
11+
isInitialSync?: boolean;
12+
skipPrepare?: boolean;
13+
useHotModuleReload?: boolean;
14+
deviceId?: string;
15+
}
16+
817
export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
918
private excludedFileExtensions = [".ts", ".sass", ".scss", ".less"];
1019
private excludedFiles = [".DS_Store"];
@@ -53,24 +62,23 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
5362
const startSyncFilesTimeout = async (platform: string) => {
5463
await promise
5564
.then(async () => {
56-
const projectData = this.$projectDataService.getProjectData(data.projectDir);
57-
const platformData = this.$platformsData.getPlatformData(platform, projectData);
58-
const currentHmrData = _.cloneDeep(hmrData);
59-
const filesToSync = _.cloneDeep(filesToSyncMap[platform]);
60-
promise = this.applyChanges(platformData, projectData, filesToSync, { useHotModuleReload: data.appFilesUpdaterOptions.useHotModuleReload});
61-
await promise;
62-
63-
if (data.appFilesUpdaterOptions.useHotModuleReload && currentHmrData.hash) {
64-
const devices = _.filter(this.$previewSdkService.connectedDevices, { platform: platform.toLowerCase() });
65-
66-
await Promise.all(_.map(devices, async (previewDevice: Device) => {
67-
const status = await this.$hmrStatusService.getHmrStatus(previewDevice.id, currentHmrData.hash);
68-
if (status === HmrConstants.HMR_ERROR_STATUS) {
69-
await this.applyChanges(platformData, projectData, currentHmrData.fallbackFiles[platform], { useHotModuleReload: false }, previewDevice.id);
70-
}
71-
}));
72-
}
73-
});
65+
const currentHmrData = _.cloneDeep(hmrData);
66+
const filesToSync = _.cloneDeep(filesToSyncMap[platform]);
67+
// We don't need to prepare when webpack emits changed files. We just need to send a message to pubnub.
68+
promise = this.syncFilesForPlatformSafe(data, platform, { filesToSync, skipPrepare: true, useHotModuleReload: data.appFilesUpdaterOptions.useHotModuleReload });
69+
await promise;
70+
71+
if (data.appFilesUpdaterOptions.useHotModuleReload && currentHmrData.hash) {
72+
const devices = _.filter(this.$previewSdkService.connectedDevices, { platform: platform.toLowerCase() });
73+
74+
await Promise.all(_.map(devices, async (previewDevice: Device) => {
75+
const status = await this.$hmrStatusService.getHmrStatus(previewDevice.id, currentHmrData.hash);
76+
if (status === HmrConstants.HMR_ERROR_STATUS) {
77+
await this.syncFilesForPlatformSafe(data, platform, { filesToSync: currentHmrData.fallbackFiles[platform], useHotModuleReload: false, deviceId: previewDevice.id });
78+
}
79+
}));
80+
}
81+
});
7482
filesToSyncMap[platform] = [];
7583
};
7684
await this.$hooksService.executeBeforeHooks("preview-sync", {
@@ -88,13 +96,12 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
8896
}
8997
});
9098
await this.$previewAppPluginsService.comparePluginsOnDevice(data, device);
91-
const payloads = await this.syncFilesForPlatformSafe(data, device.platform);
92-
payloads.deviceId = device.id;
99+
const payloads = await this.syncFilesForPlatformSafe(data, device.platform, { isInitialSync: true, useHotModuleReload: data.appFilesUpdaterOptions.useHotModuleReload });
93100
return payloads;
94101
}
95102

96-
public async syncFiles(data: IPreviewAppLiveSyncData, files?: string[]): Promise<void> {
97-
this.showWarningsForNativeFiles(files);
103+
public async syncFiles(data: IPreviewAppLiveSyncData, filesToSync: string[], filesToRemove: string[]): Promise<void> {
104+
this.showWarningsForNativeFiles(filesToSync);
98105

99106
for (const device of this.$previewSdkService.connectedDevices) {
100107
await this.$previewAppPluginsService.comparePluginsOnDevice(data, device);
@@ -106,87 +113,64 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
106113
.value();
107114

108115
for (const platform of platforms) {
109-
await this.syncFilesForPlatformSafe(data, platform, files);
116+
await this.syncFilesForPlatformSafe(data, platform, { filesToSync, filesToRemove, useHotModuleReload: data.appFilesUpdaterOptions.useHotModuleReload });
110117
}
111118
}
112119

113120
public async stopLiveSync(): Promise<void> {
114121
this.$previewSdkService.stop();
115122
}
116123

117-
private async syncFilesForPlatformSafe(data: IPreviewAppLiveSyncData, platform: string, files?: string[]): Promise<FilesPayload> {
124+
private async syncFilesForPlatformSafe(data: IPreviewAppLiveSyncData, platform: string, opts?: ISyncFilesOptions): Promise<FilesPayload> {
118125
this.$logger.info(`Start syncing changes for platform ${platform}.`);
119126

127+
opts = opts || {};
128+
let payloads = null;
129+
120130
try {
121131
const { appFilesUpdaterOptions, env, projectDir } = data;
122132
const projectData = this.$projectDataService.getProjectData(projectDir);
123133
const platformData = this.$platformsData.getPlatformData(platform, projectData);
124-
await this.preparePlatform(platform, appFilesUpdaterOptions, env, projectData);
125134

126-
let result: FilesPayload = null;
127-
if (files && files.length) {
128-
result = await this.applyChanges(platformData, projectData, files, { useHotModuleReload: data.appFilesUpdaterOptions.useHotModuleReload});
129-
this.$logger.info(`Successfully synced ${result.files.map(filePayload => filePayload.file.yellow)} for platform ${platform}.`);
130-
} else {
131-
const hmrMode = data.appFilesUpdaterOptions.useHotModuleReload ? 1 : 0;
132-
result = await this.getFilesPayload(platformData, projectData, hmrMode);
135+
if (!opts.skipPrepare) {
136+
await this.preparePlatform(platform, appFilesUpdaterOptions, env, projectData);
137+
}
138+
139+
if (opts.isInitialSync) {
140+
const platformsAppFolderPath = path.join(platformData.appDestinationDirectoryPath, APP_FOLDER_NAME);
141+
opts.filesToSync = this.$projectFilesManager.getProjectFiles(platformsAppFolderPath);
142+
payloads = this.getFilesPayload(platformData, projectData, opts);
133143
this.$logger.info(`Successfully synced changes for platform ${platform}.`);
144+
} else {
145+
opts.filesToSync = _.map(opts.filesToSync, file => this.$projectFilesProvider.mapFilePath(file, platformData.normalizedPlatformName, projectData));
146+
payloads = this.getFilesPayload(platformData, projectData, opts);
147+
await this.$previewSdkService.applyChanges(payloads);
148+
this.$logger.info(`Successfully synced ${payloads.files.map(filePayload => filePayload.file.yellow)} for platform ${platform}.`);
134149
}
135150

136-
return result;
151+
return payloads;
137152
} catch (err) {
138153
this.$logger.warn(`Unable to apply changes for platform ${platform}. Error is: ${err}, ${JSON.stringify(err, null, 2)}.`);
139154
}
140155
}
141156

142-
private async applyChanges(platformData: IPlatformData, projectData: IProjectData, files: string[], { useHotModuleReload }: {useHotModuleReload: Boolean}, deviceId?: string): Promise<FilesPayload> {
143-
const hmrMode = useHotModuleReload ? 1 : 0;
144-
const payloads = this.getFilesPayload(platformData, projectData, hmrMode, _(files).uniq().value(), deviceId);
145-
await this.$previewSdkService.applyChanges(payloads);
146-
147-
return payloads;
148-
}
149-
150-
private getFilesPayload(platformData: IPlatformData, projectData: IProjectData, hmrMode: number, files?: string[], deviceId?: string): FilesPayload {
151-
const platformsAppFolderPath = path.join(platformData.appDestinationDirectoryPath, APP_FOLDER_NAME);
152-
153-
if (files && files.length) {
154-
files = files.map(file => this.$projectFilesProvider.mapFilePath(file, platformData.normalizedPlatformName, projectData));
155-
} else {
156-
files = this.$projectFilesManager.getProjectFiles(platformsAppFolderPath);
157-
}
157+
private getFilesPayload(platformData: IPlatformData, projectData: IProjectData, opts?: ISyncFilesOptions): FilesPayload {
158+
const { filesToSync, filesToRemove, deviceId } = opts;
158159

159-
const filesToTransfer = files
160+
const filesToTransfer = filesToSync
160161
.filter(file => file.indexOf(TNS_MODULES_FOLDER_NAME) === -1)
161162
.filter(file => file.indexOf(APP_RESOURCES_FOLDER_NAME) === -1)
162163
.filter(file => !_.includes(this.excludedFiles, path.basename(file)))
163164
.filter(file => !_.includes(this.excludedFileExtensions, path.extname(file)));
164165

165166
this.$logger.trace(`Transferring ${filesToTransfer.join("\n")}.`);
166167

167-
const payloads = filesToTransfer
168-
.map(file => {
169-
const projectFileInfo = this.$projectFilesProvider.getProjectFileInfo(file, platformData.normalizedPlatformName, null);
170-
const relativePath = path.relative(platformsAppFolderPath, file);
171-
const filePayload: FilePayload = {
172-
event: PreviewSdkEventNames.CHANGE_EVENT_NAME,
173-
file: path.join(path.dirname(relativePath), projectFileInfo.onDeviceFileName),
174-
binary: isTextOrBinary.isBinarySync(file),
175-
fileContents: ""
176-
};
177-
178-
if (filePayload.binary) {
179-
const bitmap = <string>this.$fs.readFile(file);
180-
const base64 = Buffer.from(bitmap).toString('base64');
181-
filePayload.fileContents = base64;
182-
} else {
183-
filePayload.fileContents = this.$fs.readText(path.join(path.dirname(projectFileInfo.filePath), projectFileInfo.onDeviceFileName));
184-
}
185-
186-
return filePayload;
187-
});
188-
189-
return { files: payloads, platform: platformData.normalizedPlatformName.toLowerCase(), hmrMode, deviceId};
168+
const payloadsToSync = filesToTransfer.map(file => this.createFilePayload(file, platformData, projectData, PreviewSdkEventNames.CHANGE_EVENT_NAME));
169+
const payloadsToRemove = _.map(filesToRemove, file => this.createFilePayload(file, platformData, projectData, PreviewSdkEventNames.UNLINK_EVENT_NAME));
170+
const payloads = payloadsToSync.concat(payloadsToRemove);
171+
172+
const hmrMode = opts.useHotModuleReload ? 1 : 0;
173+
return { files: payloads, platform: platformData.normalizedPlatformName.toLowerCase(), hmrMode, deviceId };
190174
}
191175

192176
private async preparePlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, env: Object, projectData: IProjectData): Promise<void> {
@@ -211,5 +195,36 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
211195
_.filter(files, file => file.indexOf(APP_RESOURCES_FOLDER_NAME) > -1)
212196
.forEach(file => this.$logger.warn(`Unable to apply changes from ${APP_RESOURCES_FOLDER_NAME} folder. You need to build your application in order to make changes in ${APP_RESOURCES_FOLDER_NAME} folder.`));
213197
}
198+
199+
private createFilePayload(file: string, platformData: IPlatformData, projectData: IProjectData, event: string): FilePayload {
200+
const projectFileInfo = this.$projectFilesProvider.getProjectFileInfo(file, platformData.normalizedPlatformName, null);
201+
const binary = isTextOrBinary.isBinarySync(file);
202+
let fileContents = "";
203+
let filePath = "";
204+
205+
if (event === PreviewSdkEventNames.CHANGE_EVENT_NAME) {
206+
const relativePath = path.relative(path.join(platformData.appDestinationDirectoryPath, APP_FOLDER_NAME), file);
207+
filePath = path.join(path.dirname(relativePath), projectFileInfo.onDeviceFileName);
208+
209+
if (binary) {
210+
const bitmap = <string>this.$fs.readFile(file);
211+
const base64 = Buffer.from(bitmap).toString('base64');
212+
fileContents = base64;
213+
} else {
214+
fileContents = this.$fs.readText(path.join(path.dirname(projectFileInfo.filePath), projectFileInfo.onDeviceFileName));
215+
}
216+
} else if (event === PreviewSdkEventNames.UNLINK_EVENT_NAME) {
217+
filePath = path.relative(path.join(projectData.projectDir, APP_FOLDER_NAME), file);
218+
}
219+
220+
const filePayload = {
221+
event,
222+
file: filePath,
223+
binary,
224+
fileContents
225+
};
226+
227+
return filePayload;
228+
}
214229
}
215230
$injector.register("previewAppLiveSyncService", PreviewAppLiveSyncService);

test/services/playground/preview-app-livesync-service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ async function syncFiles(input?: IActInput) {
199199
await previewSdkService.getInitialFiles(deviceMockData);
200200
}
201201

202-
await previewAppLiveSyncService.syncFiles(syncFilesMockData, projectFiles);
202+
await previewAppLiveSyncService.syncFiles(syncFilesMockData, projectFiles, []);
203203
}
204204

205205
async function assert(expectedFiles: string[], options?: IAssertOptions) {

0 commit comments

Comments
 (0)