Skip to content

fix: don't check all node_modules from checkForChanges method #4671

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

Merged
merged 6 commits into from
Jun 6, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 4 additions & 2 deletions lib/controllers/prepare-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { hook } from "../common/helpers";
import { performanceLog } from "../common/decorators";
import { EventEmitter } from "events";
import * as path from "path";
import { PREPARE_READY_EVENT_NAME, WEBPACK_COMPILATION_COMPLETE } from "../constants";
import { PREPARE_READY_EVENT_NAME, WEBPACK_COMPILATION_COMPLETE, PACKAGE_JSON_FILE_NAME } from "../constants";

interface IPlatformWatcherData {
webpackCompilerProcess: child_process.ChildProcess;
Expand Down Expand Up @@ -115,6 +115,8 @@ export class PrepareController extends EventEmitter {
}

const patterns = [
path.join(projectData.projectDir, PACKAGE_JSON_FILE_NAME),
path.join(projectData.getAppDirectoryPath(), PACKAGE_JSON_FILE_NAME),
path.join(projectData.getAppResourcesRelativeDirectoryPath(), platformData.normalizedPlatformName),
`node_modules/**/platforms/${platformData.platformNameLowerCase}/`
];
Expand All @@ -130,7 +132,7 @@ export class PrepareController extends EventEmitter {
const watcher = choki.watch(patterns, watcherOptions)
.on("all", async (event: string, filePath: string) => {
filePath = path.join(projectData.projectDir, filePath);
this.$logger.info(`Chokidar raised event ${event} for ${filePath}.`);
this.$logger.trace(`Chokidar raised event ${event} for ${filePath}.`);
this.emitPrepareEvent({ files: [], hmrData: null, hasNativeChanges: true, platform: platformData.platformNameLowerCase });
});

Expand Down
5 changes: 3 additions & 2 deletions lib/controllers/run-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,9 @@ export class RunController extends EventEmitter implements IRunController {

try {
if (data.hasNativeChanges) {
await this.$prepareNativePlatformService.prepareNativePlatform(platformData, projectData, prepareData);
await deviceDescriptor.buildAction();
if (await this.$prepareNativePlatformService.prepareNativePlatform(platformData, projectData, prepareData)) {
await deviceDescriptor.buildAction();
}
}

const isInHMRMode = liveSyncInfo.useHotModuleReload && data.hmrData && data.hmrData.hash;
Expand Down
2 changes: 0 additions & 2 deletions lib/definitions/project-changes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ interface IPrepareInfo extends IAddedNativePlatform, IAppFilesHashes {

interface IProjectChangesInfo extends IAddedNativePlatform {
appResourcesChanged: boolean;
modulesChanged: boolean;
configChanged: boolean;
packageChanged: boolean;
nativeChanged: boolean;
signingChanged: boolean;

Expand Down
8 changes: 4 additions & 4 deletions lib/services/platform/prepare-native-platform-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ export class PrepareNativePlatformService implements IPrepareNativePlatformServi

const changesInfo = await this.$projectChangesService.checkForChanges(platformData, projectData, prepareData);

const hasModulesChange = !changesInfo || changesInfo.modulesChanged;
const hasNativeModulesChange = !changesInfo || changesInfo.nativeChanged;
const hasConfigChange = !changesInfo || changesInfo.configChanged;
const hasChangesRequirePrepare = !changesInfo || changesInfo.changesRequirePrepare;

const hasChanges = hasModulesChange || hasConfigChange || hasChangesRequirePrepare;
const hasChanges = hasNativeModulesChange || hasConfigChange || hasChangesRequirePrepare;

if (changesInfo.hasChanges) {
await this.cleanProject(platformData, { release });
Expand All @@ -37,11 +37,11 @@ export class PrepareNativePlatformService implements IPrepareNativePlatformServi
await platformData.platformProjectService.prepareProject(projectData, prepareData);
}

if (hasModulesChange) {
if (hasNativeModulesChange) {
await this.$nodeModulesBuilder.prepareNodeModules(platformData, projectData);
}

if (hasModulesChange || hasConfigChange) {
if (hasNativeModulesChange || hasConfigChange) {
await platformData.platformProjectService.processConfigurationFilesFromAppResources(projectData, { release });
await platformData.platformProjectService.handleNativeDependenciesChange(projectData, { release });
}
Expand Down
114 changes: 31 additions & 83 deletions lib/services/project-changes-service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as path from "path";
import { NODE_MODULES_FOLDER_NAME, NativePlatformStatus, PACKAGE_JSON_FILE_NAME, APP_GRADLE_FILE_NAME, BUILD_XCCONFIG_FILE_NAME } from "../constants";
import { NativePlatformStatus, PACKAGE_JSON_FILE_NAME, APP_GRADLE_FILE_NAME, BUILD_XCCONFIG_FILE_NAME, PLATFORMS_DIR_NAME } from "../constants";
import { getHash, hook } from "../common/helpers";
import { PrepareData } from "../data/prepare-data";

Expand All @@ -8,24 +8,20 @@ const prepareInfoFileName = ".nsprepareinfo";
class ProjectChangesInfo implements IProjectChangesInfo {

public appResourcesChanged: boolean;
public modulesChanged: boolean;
public configChanged: boolean;
public packageChanged: boolean;
public nativeChanged: boolean;
public signingChanged: boolean;
public nativePlatformStatus: NativePlatformStatus;

public get hasChanges(): boolean {
return this.packageChanged ||
return this.nativeChanged ||
this.appResourcesChanged ||
this.modulesChanged ||
this.configChanged ||
this.signingChanged;
}

public get changesRequireBuild(): boolean {
return this.packageChanged ||
this.appResourcesChanged ||
return this.appResourcesChanged ||
this.nativeChanged;
}

Expand All @@ -39,15 +35,15 @@ export class ProjectChangesService implements IProjectChangesService {

private _changesInfo: IProjectChangesInfo;
private _prepareInfo: IPrepareInfo;
private _newFiles: number = 0;
private _outputProjectMtime: number;
private _outputProjectCTime: number;

constructor(
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
private $fs: IFileSystem,
private $logger: ILogger,
public $hooksService: IHooksService) {
public $hooksService: IHooksService,
private $nodeModulesDependenciesBuilder: INodeModulesDependenciesBuilder) {
}

public get currentChanges(): IProjectChangesInfo {
Expand All @@ -59,26 +55,24 @@ export class ProjectChangesService implements IProjectChangesService {
this._changesInfo = new ProjectChangesInfo();
const isNewPrepareInfo = await this.ensurePrepareInfo(platformData, projectData, prepareData);
if (!isNewPrepareInfo) {
this._newFiles = 0;

this._changesInfo.packageChanged = this.isProjectFileChanged(projectData.projectDir, platformData);

const platformResourcesDir = path.join(projectData.appResourcesDirectoryPath, platformData.normalizedPlatformName);
this._changesInfo.appResourcesChanged = this.containsNewerFiles(platformResourcesDir, null, projectData);
/*done because currently all node_modules are traversed, a possible improvement could be traversing only the production dependencies*/
this._changesInfo.nativeChanged = this.containsNewerFiles(
path.join(projectData.projectDir, NODE_MODULES_FOLDER_NAME),
path.join(projectData.projectDir, NODE_MODULES_FOLDER_NAME, "tns-ios-inspector"),
projectData,
this.fileChangeRequiresBuild);
this._changesInfo.appResourcesChanged = this.containsNewerFiles(platformResourcesDir, projectData);

this.$nodeModulesDependenciesBuilder.getProductionDependencies(projectData.projectDir)
.filter(dep => dep.nativescript && this.$fs.exists(path.join(dep.directory, PLATFORMS_DIR_NAME, platformData.platformNameLowerCase)))
.forEach(dep => {
this._changesInfo.nativeChanged = this._changesInfo.nativeChanged ||
this.containsNewerFiles(path.join(dep.directory, PLATFORMS_DIR_NAME, platformData.platformNameLowerCase), projectData) ||
this.isFileModified(path.join(dep.directory, PACKAGE_JSON_FILE_NAME));
});

if (!this._changesInfo.nativeChanged) {
this._prepareInfo.projectFileHash = this.getProjectFileStrippedHash(projectData.projectDir, platformData);
this._changesInfo.nativeChanged = this.isProjectFileChanged(projectData.projectDir, platformData);
}

this.$logger.trace(`Set nativeChanged to ${this._changesInfo.nativeChanged}.`);

if (this._newFiles > 0 || this._changesInfo.nativeChanged) {
this.$logger.trace(`Setting modulesChanged to true, newFiles: ${this._newFiles}, nativeChanged: ${this._changesInfo.nativeChanged}`);
this._changesInfo.modulesChanged = true;
}

if (platformData.platformNameLowerCase === this.$devicePlatformsConstants.iOS.toLowerCase()) {
this._changesInfo.configChanged = this.filesChanged([path.join(platformResourcesDir, platformData.configurationFileName),
path.join(platformResourcesDir, "LaunchScreen.storyboard"),
Expand All @@ -101,16 +95,11 @@ export class ProjectChangesService implements IProjectChangesService {
if (prepareData.release !== this._prepareInfo.release) {
this.$logger.trace(`Setting all setting to true. Current options are: `, prepareData, " old prepare info is: ", this._prepareInfo);
this._changesInfo.appResourcesChanged = true;
this._changesInfo.modulesChanged = true;
this._changesInfo.configChanged = true;
this._prepareInfo.release = prepareData.release;
}
if (this._changesInfo.packageChanged) {
this.$logger.trace("Set modulesChanged to true as packageChanged is true");
this._changesInfo.modulesChanged = true;
}
if (this._changesInfo.modulesChanged || this._changesInfo.appResourcesChanged) {
this.$logger.trace(`Set configChanged to true, current value of moduleChanged is: ${this._changesInfo.modulesChanged}, appResourcesChanged is: ${this._changesInfo.appResourcesChanged}`);
if (this._changesInfo.appResourcesChanged) {
this.$logger.trace(`Set configChanged to true, appResourcesChanged is: ${this._changesInfo.appResourcesChanged}`);
this._changesInfo.configChanged = true;
}
if (this._changesInfo.hasChanges) {
Expand All @@ -119,8 +108,6 @@ export class ProjectChangesService implements IProjectChangesService {
if (this._prepareInfo.changesRequireBuild) {
this._prepareInfo.changesRequireBuildTime = this._prepareInfo.time;
}

this._prepareInfo.projectFileHash = this.getProjectFileStrippedHash(projectData.projectDir, platformData);
}

this._changesInfo.nativePlatformStatus = this._prepareInfo.nativePlatformStatus;
Expand Down Expand Up @@ -191,7 +178,6 @@ export class ProjectChangesService implements IProjectChangesService {
this._outputProjectCTime = 0;
this._changesInfo = this._changesInfo || new ProjectChangesInfo();
this._changesInfo.appResourcesChanged = true;
this._changesInfo.modulesChanged = true;
this._changesInfo.configChanged = true;
return true;
}
Expand Down Expand Up @@ -229,48 +215,33 @@ export class ProjectChangesService implements IProjectChangesService {
return false;
}

private containsNewerFiles(dir: string, skipDir: string, projectData: IProjectData, processFunc?: (filePath: string, projectData: IProjectData) => boolean): boolean {
private containsNewerFiles(dir: string, projectData: IProjectData): boolean {
const dirName = path.basename(dir);
this.$logger.trace(`containsNewerFiles will check ${dir}`);
if (_.startsWith(dirName, '.')) {
this.$logger.trace(`containsNewerFiles returns false for ${dir} as its name starts with dot (.) .`);
return false;
}

const dirFileStat = this.$fs.getFsStats(dir);
if (this.isFileModified(dirFileStat, dir)) {
if (this.isFileModified(dir)) {
this.$logger.trace(`containsNewerFiles returns true for ${dir} as the dir itself has been modified.`);
return true;
}

const files = this.$fs.readDirectory(dir);
for (const file of files) {
const filePath = path.join(dir, file);
if (filePath === skipDir) {
continue;
}

const fileStats = this.$fs.getFsStats(filePath);
const changed = this.isFileModified(fileStats, filePath);
const changed = this.isFileModified(filePath, fileStats);

if (changed) {
this.$logger.trace(`File ${filePath} has been changed.`);
if (processFunc) {
this._newFiles++;
this.$logger.trace(`Incremented the newFiles counter. Current value is ${this._newFiles}`);
const filePathRelative = path.relative(projectData.projectDir, filePath);
if (processFunc.call(this, filePathRelative, projectData)) {
this.$logger.trace(`containsNewerFiles returns true for ${dir}. The modified file is ${filePath}`);
return true;
}
} else {
this.$logger.trace(`containsNewerFiles returns true for ${dir}. The modified file is ${filePath}`);
return true;
}
this.$logger.trace(`containsNewerFiles returns true for ${dir}. The modified file is ${filePath}`);
return true;
}

if (fileStats.isDirectory()) {
if (this.containsNewerFiles(filePath, skipDir, projectData, processFunc)) {
if (this.containsNewerFiles(filePath, projectData)) {
this.$logger.trace(`containsNewerFiles returns true for ${dir}.`);
return true;
}
Expand All @@ -281,9 +252,10 @@ export class ProjectChangesService implements IProjectChangesService {
return false;
}

private isFileModified(filePathStat: IFsStats, filePath: string): boolean {
let changed = filePathStat.mtime.getTime() >= this._outputProjectMtime ||
filePathStat.ctime.getTime() >= this._outputProjectCTime;
private isFileModified(filePath: string, filePathStats?: IFsStats): boolean {
filePathStats = filePathStats || this.$fs.getFsStats(filePath);
let changed = filePathStats.mtime.getTime() >= this._outputProjectMtime ||
filePathStats.ctime.getTime() >= this._outputProjectCTime;

if (!changed) {
const lFileStats = this.$fs.getLsStats(filePath);
Expand All @@ -293,29 +265,5 @@ export class ProjectChangesService implements IProjectChangesService {

return changed;
}

private fileChangeRequiresBuild(file: string, projectData: IProjectData) {
if (path.basename(file) === PACKAGE_JSON_FILE_NAME) {
return true;
}
const projectDir = projectData.projectDir;
if (_.startsWith(path.join(projectDir, file), projectData.appResourcesDirectoryPath)) {
return true;
}
if (_.startsWith(file, NODE_MODULES_FOLDER_NAME)) {
let filePath = file;
while (filePath !== NODE_MODULES_FOLDER_NAME) {
filePath = path.dirname(filePath);
const fullFilePath = path.join(projectDir, path.join(filePath, PACKAGE_JSON_FILE_NAME));
if (this.$fs.exists(fullFilePath)) {
const json = this.$fs.readJson(fullFilePath);
if (json["nativescript"] && _.startsWith(file, path.join(filePath, "platforms"))) {
return true;
}
}
}
}
return false;
}
}
$injector.register("projectChangesService", ProjectChangesService);
1 change: 1 addition & 0 deletions test/project-changes-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class ProjectChangesServiceTest extends BaseServiceTest {
});
this.injector.register("logger", LoggerStub);
this.injector.register("hooksService", HooksServiceStub);
this.injector.register("nodeModulesDependenciesBuilder", {});

const fs = this.injector.resolve<IFileSystem>("fs");
fs.writeJson(path.join(this.projectDir, Constants.PACKAGE_JSON_FILE_NAME), {
Expand Down
14 changes: 4 additions & 10 deletions test/stubs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,16 +506,10 @@ export class ProjectDataService implements IProjectDataService {
removeDependency(dependencyName: string): void { }

getProjectData(projectDir: string): IProjectData {
return <any>{
projectDir: "/path/to/my/projecDir",
projectName: "myTestProjectName",
platformsDir: "/path/to/my/projecDir/platforms",
projectIdentifiers: {
ios: "org.nativescript.myiosApp",
android: "org.nativescript.myAndroidApp"
},
getAppResourcesRelativeDirectoryPath: () => "/path/to/my/projecDir/App_Resources"
};
const projectData = new ProjectDataStub();
projectData.initializeProjectData(projectDir);

return projectData;
}

async getAssetsStructure(opts: IProjectDir): Promise<IAssetsStructure> {
Expand Down