Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't prepare the project on preview command #4142

Merged
merged 4 commits into from
Nov 23, 2018
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ $injector.requirePublicClass("androidLivesyncTool", "./services/livesync/android
$injector.require("androidLiveSyncService", "./services/livesync/android-livesync-service");
$injector.require("iOSLiveSyncService", "./services/livesync/ios-livesync-service");
$injector.require("usbLiveSyncService", "./services/livesync/livesync-service"); // The name is used in https://github.com/NativeScript/nativescript-dev-typescript
$injector.require("previewAppFilesService", "./services/livesync/playground/preview-app-files-service");
$injector.require("previewAppLiveSyncService", "./services/livesync/playground/preview-app-livesync-service");
$injector.require("previewAppLogProvider", "./services/livesync/playground/preview-app-log-provider");
$injector.require("previewAppPluginsService", "./services/livesync/playground/preview-app-plugins-service");
Expand Down
10 changes: 10 additions & 0 deletions lib/definitions/preview-app-livesync.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ declare global {
stopLiveSync(): Promise<void>;
}

interface IPreviewAppFilesService {
getInitialFilesPayload(data: IPreviewAppLiveSyncData, platform: string, deviceId?: string): FilesPayload;
getFilesPayload(data: IPreviewAppLiveSyncData, filesData: IPreviewAppFilesData, platform: string, deviceId?: string): FilesPayload;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data -> liveSyncData

}

interface IPreviewAppFilesData {
filesToSync: string[];
filesToRemove?: string[];
}

interface IPreviewAppLiveSyncData extends IProjectDir, IHasUseHotModuleReloadOption, IBundle, IEnvOptions { }

interface IPreviewSdkService extends EventEmitter {
Expand Down
89 changes: 89 additions & 0 deletions lib/services/livesync/playground/preview-app-files-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import * as path from "path";
import { APP_FOLDER_NAME, TNS_MODULES_FOLDER_NAME, APP_RESOURCES_FOLDER_NAME } from "../../../constants";
import { PreviewSdkEventNames } from "./preview-app-constants";
import { FilePayload, FilesPayload } from "nativescript-preview-sdk";
const isTextOrBinary = require('istextorbinary');

export class PreviewAppFilesService implements IPreviewAppFilesService {
private excludedFileExtensions = [".ts", ".sass", ".scss", ".less"];
private excludedFiles = [".DS_Store"];

constructor(
private $fs: IFileSystem,
private $logger: ILogger,
private $platformsData: IPlatformsData,
private $projectDataService: IProjectDataService,
private $projectFilesManager: IProjectFilesManager
) { }

public getInitialFilesPayload(data: IPreviewAppLiveSyncData, platform: string, deviceId?: string): FilesPayload {
const rootFilesDir = this.getRootFilesDir(data, platform);
const filesToSync = this.$projectFilesManager.getProjectFiles(rootFilesDir);
const payloads = this.getFilesPayload(data, { filesToSync }, platform, deviceId);
return payloads;
}

public getFilesPayload(data: IPreviewAppLiveSyncData, filesData: IPreviewAppFilesData, platform: string, deviceId?: string): FilesPayload {
const { filesToSync, filesToRemove } = filesData;

const filesToTransfer = filesToSync
.filter(file => file.indexOf(TNS_MODULES_FOLDER_NAME) === -1)
.filter(file => file.indexOf(APP_RESOURCES_FOLDER_NAME) === -1)
.filter(file => !_.includes(this.excludedFiles, path.basename(file)))
.filter(file => !_.includes(this.excludedFileExtensions, path.extname(file)));

this.$logger.trace(`Transferring ${filesToTransfer.join("\n")}.`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Transferring is not the proper verb for this method


const rootFilesDir = this.getRootFilesDir(data, platform);
const payloadsToSync = _.map(filesToTransfer, file => this.createFilePayload(file, rootFilesDir, PreviewSdkEventNames.CHANGE_EVENT_NAME));
const payloadsToRemove = _.map(filesToRemove, file => this.createFilePayload(file, rootFilesDir, PreviewSdkEventNames.UNLINK_EVENT_NAME));
const payloads = payloadsToSync.concat(payloadsToRemove);

return {
files: payloads,
platform: platform,
hmrMode: data.useHotModuleReload ? 1 : 0,
deviceId
};
}

private createFilePayload(file: string, rootFilesDir: string, event: string): FilePayload {
let fileContents = "";
let binary = false;

if (event === PreviewSdkEventNames.CHANGE_EVENT_NAME) {
binary = isTextOrBinary.isBinarySync(file);
if (binary) {
const bitmap = <string>this.$fs.readFile(file);
const base64 = Buffer.from(bitmap).toString('base64');
fileContents = base64;
} else {
fileContents = this.$fs.readText(file);
}
}

const filePayload = {
event,
file: path.relative(rootFilesDir, file),
binary,
fileContents
};

return filePayload;
}

private getRootFilesDir(data: IPreviewAppLiveSyncData, platform: string): string {
const projectData = this.$projectDataService.getProjectData(data.projectDir);
const platformData = this.$platformsData.getPlatformData(platform, projectData);

let rootFilesDir = null;
if (data.bundle) {
rootFilesDir = path.join(platformData.appDestinationDirectoryPath, APP_FOLDER_NAME);
} else {
rootFilesDir = projectData.getAppDirectoryPath();
}

return rootFilesDir;
}
}
$injector.register("previewAppFilesService", PreviewAppFilesService);
178 changes: 47 additions & 131 deletions lib/services/livesync/playground/preview-app-livesync-service.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,24 @@
import * as path from "path";
import { FilePayload, Device, FilesPayload } from "nativescript-preview-sdk";
import { PreviewSdkEventNames } from "./preview-app-constants";
import { APP_FOLDER_NAME, APP_RESOURCES_FOLDER_NAME, TNS_MODULES_FOLDER_NAME } from "../../../constants";
import { Device, FilesPayload } from "nativescript-preview-sdk";
import { APP_RESOURCES_FOLDER_NAME, APP_FOLDER_NAME } from "../../../constants";
import { HmrConstants } from "../../../common/constants";
const isTextOrBinary = require('istextorbinary');

interface ISyncFilesOptions {
filesToSync?: string[];
filesToRemove?: string[];
isInitialSync?: boolean;
skipPrepare?: boolean;
useHotModuleReload?: boolean;
deviceId?: string;
}

export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
private excludedFileExtensions = [".ts", ".sass", ".scss", ".less"];
private excludedFiles = [".DS_Store"];

private deviceInitializationPromise: IDictionary<Promise<FilesPayload>> = {};

constructor(private $fs: IFileSystem,
constructor(
private $errors: IErrors,
private $hooksService: IHooksService,
private $logger: ILogger,
private $platformService: IPlatformService,
private $platformsData: IPlatformsData,
private $projectDataService: IProjectDataService,
private $previewSdkService: IPreviewSdkService,
private $previewAppFilesService: IPreviewAppFilesService,
private $previewAppPluginsService: IPreviewAppPluginsService,
private $previewDevicesService: IPreviewDevicesService,
private $projectFilesManager: IProjectFilesManager,
private $hmrStatusService: IHmrStatusService,
private $projectFilesProvider: IProjectFilesProvider) { }
) { }

public async initialize(data: IPreviewAppLiveSyncData): Promise<void> {
await this.$previewSdkService.initialize(async (device: Device) => {
Expand Down Expand Up @@ -65,8 +52,9 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
.map(device => device.platform)
.uniq()
.value();

for (const platform of platforms) {
await this.syncFilesForPlatformSafe(data, platform, { filesToSync, filesToRemove, useHotModuleReload: data.useHotModuleReload });
await this.syncFilesForPlatformSafe(data, { filesToSync, filesToRemove }, platform);
}
}

Expand All @@ -78,7 +66,7 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
const hookArgs = this.getHookArgs(data, device);
await this.$hooksService.executeBeforeHooks("preview-sync", { hookArgs });
await this.$previewAppPluginsService.comparePluginsOnDevice(data, device);
const payloads = await this.syncFilesForPlatformSafe(data, device.platform, { isInitialSync: true, useHotModuleReload: data.useHotModuleReload });
const payloads = await this.syncInitialFilesForPlatformSafe(data, device.platform);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syncInitialFilesForPlatformSafe -> getInitialFilesForPlatformSafe

and

initializePreviewForDevice -> getInitialFilesForDevice

return payloads;
}

Expand Down Expand Up @@ -106,14 +94,45 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
return result;
}

private async syncInitialFilesForPlatformSafe(data: IPreviewAppLiveSyncData, platform: string): Promise<FilesPayload> {
this.$logger.info(`Start syncing changes for platform ${platform}.`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Start syncing changes for platform... -> Start sending initial files for platform...


try {
const payloads = this.$previewAppFilesService.getInitialFilesPayload(data, platform);
this.$logger.info(`Successfully synced changes for platform ${platform}.`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully synced changes for platform... -> Successfully sent initial files for platform...

return payloads;
} catch (err) {
this.$logger.warn(`Unable to apply changes for platform ${platform}. Error is: ${err}, ${JSON.stringify(err, null, 2)}.`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

${JSON.stringify(err, null, 2)} -> helper

}
}

private async syncFilesForPlatformSafe(data: IPreviewAppLiveSyncData, filesData: IPreviewAppFilesData, platform: string, deviceId?: string): Promise<FilesPayload> {
this.$logger.info(`Start syncing changes for platform ${platform}.`);

try {
const payloads = this.$previewAppFilesService.getFilesPayload(data, filesData, platform);
await this.$previewSdkService.applyChanges(payloads);
this.$logger.info(`Successfully synced ${payloads.files.map(filePayload => filePayload.file.yellow)} for platform ${platform}.`);
return payloads;
} catch (err) {
this.$logger.warn(`Unable to apply changes for platform ${platform}. Error is: ${err}, ${JSON.stringify(err, null, 2)}.`);
}
}

private async onWebpackCompilationComplete(data: IPreviewAppLiveSyncData, hmrData: IDictionary<IPlatformHmrData>, filesToSyncMap: IDictionary<string[]>, promise: Promise<FilesPayload>, platform: string) {
await promise
.then(async () => {
const currentHmrData = _.cloneDeep(hmrData);
const platformHmrData = currentHmrData[platform] || <any>{};
const filesToSync = _.cloneDeep(filesToSyncMap[platform]);
// We don't need to prepare when webpack emits changed files. We just need to send a message to pubnub.
promise = this.syncFilesForPlatformSafe(data, platform, { filesToSync, skipPrepare: true, useHotModuleReload: data.useHotModuleReload });
const projectData = this.$projectDataService.getProjectData(data.projectDir);
const platformData = this.$platformsData.getPlatformData(platform, projectData);
const clonedFiles = _.cloneDeep(filesToSyncMap[platform]);
const filesToSync = _.map(clonedFiles, fileToSync => {
const result = path.join(platformData.appDestinationDirectoryPath, APP_FOLDER_NAME, path.relative(projectData.getAppDirectoryPath(), fileToSync));
return result;
});

promise = this.syncFilesForPlatformSafe(data, { filesToSync }, platform);
await promise;

if (data.useHotModuleReload && platformHmrData.hash) {
Expand All @@ -122,123 +141,20 @@ export class PreviewAppLiveSyncService implements IPreviewAppLiveSyncService {
await Promise.all(_.map(devices, async (previewDevice: Device) => {
const status = await this.$hmrStatusService.getHmrStatus(previewDevice.id, platformHmrData.hash);
if (status === HmrConstants.HMR_ERROR_STATUS) {
await this.syncFilesForPlatformSafe(data, platform, { filesToSync: platformHmrData.fallbackFiles, useHotModuleReload: false, deviceId: previewDevice.id });
const originalUseHotModuleReload = data.useHotModuleReload;
data.useHotModuleReload = false;
await this.syncFilesForPlatformSafe(data, { filesToSync: platformHmrData.fallbackFiles }, platform, previewDevice.id );
data.useHotModuleReload = originalUseHotModuleReload;
}
}));
}
});
filesToSyncMap[platform] = [];
}

private async syncFilesForPlatformSafe(data: IPreviewAppLiveSyncData, platform: string, opts?: ISyncFilesOptions): Promise<FilesPayload> {
this.$logger.info(`Start syncing changes for platform ${platform}.`);

opts = opts || {};
let payloads = null;

try {
const { env, projectDir } = data;
const projectData = this.$projectDataService.getProjectData(projectDir);
const platformData = this.$platformsData.getPlatformData(platform, projectData);

if (!opts.skipPrepare) {
await this.preparePlatform(platform, data, env, projectData);
}

if (opts.isInitialSync) {
const platformsAppFolderPath = path.join(platformData.appDestinationDirectoryPath, APP_FOLDER_NAME);
opts.filesToSync = this.$projectFilesManager.getProjectFiles(platformsAppFolderPath);
payloads = this.getFilesPayload(platformData, projectData, opts);
this.$logger.info(`Successfully synced changes for platform ${platform}.`);
} else {
opts.filesToSync = _.map(opts.filesToSync, file => this.$projectFilesProvider.mapFilePath(file, platformData.normalizedPlatformName, projectData));
payloads = this.getFilesPayload(platformData, projectData, opts);
await this.$previewSdkService.applyChanges(payloads);
this.$logger.info(`Successfully synced ${payloads.files.map(filePayload => filePayload.file.yellow)} for platform ${platform}.`);
}

return payloads;
} catch (err) {
this.$logger.warn(`Unable to apply changes for platform ${platform}. Error is: ${err}, ${JSON.stringify(err, null, 2)}.`);
}
}

private getFilesPayload(platformData: IPlatformData, projectData: IProjectData, opts?: ISyncFilesOptions): FilesPayload {
const { filesToSync, filesToRemove, deviceId } = opts;

const filesToTransfer = filesToSync
.filter(file => file.indexOf(TNS_MODULES_FOLDER_NAME) === -1)
.filter(file => file.indexOf(APP_RESOURCES_FOLDER_NAME) === -1)
.filter(file => !_.includes(this.excludedFiles, path.basename(file)))
.filter(file => !_.includes(this.excludedFileExtensions, path.extname(file)));

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

const payloadsToSync = filesToTransfer.map(file => this.createFilePayload(file, platformData, projectData, PreviewSdkEventNames.CHANGE_EVENT_NAME));
const payloadsToRemove = _.map(filesToRemove, file => this.createFilePayload(file, platformData, projectData, PreviewSdkEventNames.UNLINK_EVENT_NAME));
const payloads = payloadsToSync.concat(payloadsToRemove);

const hmrMode = opts.useHotModuleReload ? 1 : 0;
return { files: payloads, platform: platformData.normalizedPlatformName.toLowerCase(), hmrMode, deviceId };
}

private async preparePlatform(platform: string, data: IPreviewAppLiveSyncData, env: Object, projectData: IProjectData): Promise<void> {
const appFilesUpdaterOptions = {
bundle: data.bundle,
useHotModuleReload: data.useHotModuleReload,
release: false
};
const nativePrepare = { skipNativePrepare: true };
const config = <IPlatformOptions>{};
const platformTemplate = <string>null;
const prepareInfo = {
platform,
appFilesUpdaterOptions,
env,
projectData,
nativePrepare,
config,
platformTemplate,
skipCopyTnsModules: true,
skipCopyAppResourcesFiles: true
};
await this.$platformService.preparePlatform(prepareInfo);
}

private showWarningsForNativeFiles(files: string[]): void {
_.filter(files, file => file.indexOf(APP_RESOURCES_FOLDER_NAME) > -1)
.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.`));
}

private createFilePayload(file: string, platformData: IPlatformData, projectData: IProjectData, event: string): FilePayload {
const projectFileInfo = this.$projectFilesProvider.getProjectFileInfo(file, platformData.normalizedPlatformName, null);
const binary = isTextOrBinary.isBinarySync(file);
let fileContents = "";
let filePath = "";

if (event === PreviewSdkEventNames.CHANGE_EVENT_NAME) {
const relativePath = path.relative(path.join(platformData.appDestinationDirectoryPath, APP_FOLDER_NAME), file);
filePath = path.join(path.dirname(relativePath), projectFileInfo.onDeviceFileName);

if (binary) {
const bitmap = <string>this.$fs.readFile(file);
const base64 = Buffer.from(bitmap).toString('base64');
fileContents = base64;
} else {
fileContents = this.$fs.readText(path.join(path.dirname(projectFileInfo.filePath), projectFileInfo.onDeviceFileName));
}
} else if (event === PreviewSdkEventNames.UNLINK_EVENT_NAME) {
filePath = path.relative(path.join(projectData.projectDir, APP_FOLDER_NAME), file);
}

const filePayload = {
event,
file: filePath,
binary,
fileContents
};

return filePayload;
}
}
$injector.register("previewAppLiveSyncService", PreviewAppLiveSyncService);
Loading