Skip to content

feat: make app_resources's path configurable #3329

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

Closed
wants to merge 6 commits into from
Closed
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
2 changes: 2 additions & 0 deletions lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export const BUILD_DIR = "build";
export const OUTPUTS_DIR = "outputs";
export const APK_DIR = "apk";
export const RESOURCES_DIR = "res";
export const CONFIG_NS_FILE_NAME = "nsconfig.json";
export const CONFIG_NS_APP_RESOURCES_ENTRY = "appResourcesPath";

export class PackageVersion {
static NEXT = "next";
Expand Down
1 change: 1 addition & 0 deletions lib/definitions/project.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ interface IProjectData extends IProjectDir {
* @returns {void}
*/
initializeProjectData(projectDir?: string): void;
getAppResourcesDirectoryPath(projectDir?: string): string;
}

interface IProjectDataService {
Expand Down
33 changes: 31 additions & 2 deletions lib/project-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ export class ProjectData implements IProjectData {
public projectId: string;
public projectName: string;
public appDirectoryPath: string;
public appResourcesDirectoryPath: string;
get appResourcesDirectoryPath(): string {
return this.getAppResourcesDirectoryPath();
}
public dependencies: any;
public devDependencies: IStringDictionary;
public projectType: string;
Expand Down Expand Up @@ -69,7 +71,6 @@ export class ProjectData implements IProjectData {
this.platformsDir = path.join(projectDir, constants.PLATFORMS_DIR_NAME);
this.projectFilePath = projectFilePath;
this.appDirectoryPath = path.join(projectDir, constants.APP_FOLDER_NAME);
this.appResourcesDirectoryPath = path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME);
this.projectId = data.id;
this.dependencies = fileContent.dependencies;
this.devDependencies = fileContent.devDependencies;
Expand All @@ -87,6 +88,34 @@ export class ProjectData implements IProjectData {
this.$errors.fail("No project found at or above '%s' and neither was a --path specified.", projectDir || this.$options.path || currentDir);
}

public getAppResourcesDirectoryPath(projectDir?: string): string {
if (!projectDir) {
projectDir = this.projectDir;
Copy link
Contributor

Choose a reason for hiding this comment

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

if initializeProjectData has not been called, the this.projectDir will also be undefined and the path.join below will fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct, I am unsure how to tackle this. The need for the additional projectDir parameter arose when using the getter during project creation. What would you recommend I check here?

Copy link
Contributor

Choose a reason for hiding this comment

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

I would just return null here. The caller should decide what to do.

}

if (!projectDir) {
return null;
}

const configNSFilePath = path.join(projectDir, constants.CONFIG_NS_FILE_NAME);
let absoluteAppResourcesDirPath: string;

if (this.$fs.exists(configNSFilePath)) {
const configNS = this.$fs.readJson(configNSFilePath);

if (configNS && configNS[constants.CONFIG_NS_APP_RESOURCES_ENTRY]) {
const appResourcesDirPath = configNS[constants.CONFIG_NS_APP_RESOURCES_ENTRY];

absoluteAppResourcesDirPath = path.resolve(projectDir, appResourcesDirPath);
Copy link
Contributor

Choose a reason for hiding this comment

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

is it worth checking if this path exists?


return absoluteAppResourcesDirPath;
}
}

// if no nsconfig is present default to app/App_Resources
return path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME);
}

private getProjectType(): string {
let detectedProjectType = _.find(ProjectData.PROJECT_TYPES, (projectType) => projectType.isDefaultProjectType).type;

Expand Down
10 changes: 5 additions & 5 deletions lib/providers/project-files-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { ProjectFilesProviderBase } from "../common/services/project-files-provi
export class ProjectFilesProvider extends ProjectFilesProviderBase {
constructor(private $platformsData: IPlatformsData,
$mobileHelper: Mobile.IMobileHelper,
$options:IOptions) {
super($mobileHelper, $options);
$options: IOptions) {
super($mobileHelper, $options);
}

private static INTERNAL_NONPROJECT_FILES = [ "**/*.ts" ];
private static INTERNAL_NONPROJECT_FILES = ["**/*.ts"];

public mapFilePath(filePath: string, platform: string, projectData: IProjectData, projectFilesConfig: IProjectFilesConfig): string {
const platformData = this.$platformsData.getPlatformData(platform.toLowerCase(), projectData);
Expand All @@ -23,14 +23,14 @@ export class ProjectFilesProvider extends ProjectFilesProviderBase {
mappedFilePath = path.join(platformData.appDestinationDirectoryPath, path.relative(projectData.projectDir, parsedFilePath));
}

const appResourcesDirectoryPath = path.join(constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME);
const appResourcesDirectoryPath = projectData.appResourcesDirectoryPath;
const platformSpecificAppResourcesDirectoryPath = path.join(appResourcesDirectoryPath, platformData.normalizedPlatformName);
if (parsedFilePath.indexOf(appResourcesDirectoryPath) > -1 && parsedFilePath.indexOf(platformSpecificAppResourcesDirectoryPath) === -1) {
return null;
}

if (parsedFilePath.indexOf(platformSpecificAppResourcesDirectoryPath) > -1) {
const appResourcesRelativePath = path.relative(path.join(projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME,
const appResourcesRelativePath = path.relative(path.join(projectData.appResourcesDirectoryPath,
platformData.normalizedPlatformName), parsedFilePath);
mappedFilePath = path.join(platformData.platformProjectService.getAppResourcesDestinationDirectoryPath(projectData), appResourcesRelativePath);
}
Expand Down
25 changes: 16 additions & 9 deletions lib/services/app-files-updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export class AppFilesUpdater {
) {
}

public updateApp(beforeCopyAction: (sourceFiles: string[]) => void, filesToSync?: string[]): void {
const sourceFiles = filesToSync || this.resolveAppSourceFiles();
public updateApp(beforeCopyAction: (sourceFiles: string[]) => void, projectData: IProjectData, filesToSync?: string[]): void {
const sourceFiles = filesToSync || this.resolveAppSourceFiles(projectData);

beforeCopyAction(sourceFiles);
this.copyAppSourceFiles(sourceFiles);
Expand Down Expand Up @@ -41,14 +41,19 @@ export class AppFilesUpdater {
this.fs.deleteDirectory(path.join(this.appDestinationDirectoryPath, directoryItem));
}

protected readSourceDir(): string[] {
protected readSourceDir(projectData: IProjectData): string[] {
const tnsDir = path.join(this.appSourceDirectoryPath, constants.TNS_MODULES_FOLDER_NAME);

return this.fs.enumerateFilesInDirectorySync(this.appSourceDirectoryPath, null, { includeEmptyDirectories: true }).filter(dirName => dirName !== tnsDir);
}

protected resolveAppSourceFiles(): string[] {
// Copy all files from app dir, but make sure to exclude tns_modules
let sourceFiles = this.readSourceDir();
protected resolveAppSourceFiles(projectData: IProjectData): string[] {
if (this.options.bundle) {
return [];
}

// Copy all files from app dir, but make sure to exclude tns_modules and application resources
let sourceFiles = this.readSourceDir(projectData);

if (this.options.release) {
const testsFolderPath = path.join(this.appSourceDirectoryPath, 'tests');
Expand All @@ -60,9 +65,11 @@ export class AppFilesUpdater {
constants.LIVESYNC_EXCLUDED_FILE_PATTERNS.forEach(pattern => sourceFiles = sourceFiles.filter(file => !minimatch(file, pattern, { nocase: true })));
}

if (this.options.bundle) {
sourceFiles = sourceFiles.filter(file => minimatch(file, "**/App_Resources/**", { nocase: true }));
}
// exclude the app_resources directory from being enumerated
// for copying if it is present in the application sources dir
const appResourcesPath = projectData.appResourcesDirectoryPath;
sourceFiles = sourceFiles.filter(dirName => !path.normalize(dirName).startsWith(path.normalize(appResourcesPath)));

return sourceFiles;
}

Expand Down
4 changes: 1 addition & 3 deletions lib/services/ios-entitlements-service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as path from "path";
import * as constants from "../constants";
import { PlistSession } from "plist-merge-patch";

export class IOSEntitlementsService {
Expand All @@ -14,8 +13,7 @@ export class IOSEntitlementsService {

private getDefaultAppEntitlementsPath(projectData: IProjectData) : string {
const entitlementsName = IOSEntitlementsService.DefaultEntitlementsName;
const entitlementsPath = path.join(projectData.projectDir,
constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME,
const entitlementsPath = path.join(projectData.appResourcesDirectoryPath,
this.$mobileHelper.normalizePlatformName(this.$devicePlatformsConstants.iOS),
entitlementsName);
return entitlementsPath;
Expand Down
11 changes: 4 additions & 7 deletions lib/services/ios-project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -765,9 +765,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f

private getInfoPlistPath(projectData: IProjectData): string {
return path.join(
projectData.projectDir,
constants.APP_FOLDER_NAME,
constants.APP_RESOURCES_FOLDER_NAME,
projectData.appResourcesDirectoryPath,
this.getPlatformData(projectData).normalizedPlatformName,
this.getPlatformData(projectData).configurationFileName
);
Expand All @@ -787,7 +785,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f

private async mergeInfoPlists(buildOptions: IRelease, projectData: IProjectData): Promise<void> {
const projectDir = projectData.projectDir;
const infoPlistPath = path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName);
const infoPlistPath = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName);
this.ensureConfigurationFileInAppResources();

if (!this.$fs.exists(infoPlistPath)) {
Expand Down Expand Up @@ -1217,7 +1215,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
}
}

const appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig");
const appResourcesXcconfigPath = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig");
if (this.$fs.exists(appResourcesXcconfigPath)) {
await this.mergeXcconfigFiles(appResourcesXcconfigPath, pluginsXcconfigFilePath);
}
Expand Down Expand Up @@ -1352,8 +1350,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f
}

private validateApplicationIdentifier(projectData: IProjectData): void {
const projectDir = projectData.projectDir;
const infoPlistPath = path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName);
const infoPlistPath = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName);
const mergedPlistPath = this.getPlatformData(projectData).configurationFilePath;

if (!this.$fs.exists(infoPlistPath) || !this.$fs.exists(mergedPlistPath)) {
Expand Down
2 changes: 2 additions & 0 deletions lib/services/livesync/livesync-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,8 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
}
}

patterns.push(projectData.appResourcesDirectoryPath);

const currentWatcherInfo = this.liveSyncProcessesInfo[liveSyncData.projectDir].watcherInfo;
const areWatcherPatternsDifferent = () => _.xor(currentWatcherInfo.patterns, patterns).length;
if (!currentWatcherInfo || areWatcherPatternsDifferent()) {
Expand Down
8 changes: 8 additions & 0 deletions lib/services/prepare-platform-js-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export class PreparePlatformJSService extends PreparePlatformService implements
public async preparePlatform(config: IPreparePlatformJSInfo): Promise<void> {
if (!config.changesInfo || config.changesInfo.appFilesChanged || config.changesInfo.changesRequirePrepare) {
await this.copyAppFiles(config);
this.copyAppResourcesFiles(config);
Copy link
Contributor

Choose a reason for hiding this comment

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

I assume this is related to https://github.com/NativeScript/nativescript-cli/pull/3329/files#diff-d7315453ac31262b45d0c2530d69cd94R47 change. Can you describe a little bit more what is the idea

}

if (config.changesInfo && !config.changesInfo.changesRequirePrepare) {
Expand Down Expand Up @@ -101,6 +102,13 @@ export class PreparePlatformJSService extends PreparePlatformService implements
this.$errors.failWithoutHelp(`Processing node_modules failed. ${error}`);
}
}

private copyAppResourcesFiles(config: IPreparePlatformJSInfo) {
const appDestinationDirectoryPath = path.join(config.platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME);
const appResourcesSourcePath = config.projectData.appResourcesDirectoryPath;

shell.cp("-Rf", appResourcesSourcePath, appDestinationDirectoryPath);
}
}

$injector.register("preparePlatformJSService", PreparePlatformJSService);
2 changes: 1 addition & 1 deletion lib/services/prepare-platform-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ export class PreparePlatformService {
const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, copyAppFilesData.appFilesUpdaterOptions, this.$fs);
appUpdater.updateApp(sourceFiles => {
this.$xmlValidator.validateXmlFiles(sourceFiles);
}, copyAppFilesData.filesToSync);
}, copyAppFilesData.projectData, copyAppFilesData.filesToSync);
}
}
1 change: 1 addition & 0 deletions lib/services/project-changes-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export class ProjectChangesService implements IProjectChangesService {
this._changesInfo.appFilesChanged = this.containsNewerFiles(projectData.appDirectoryPath, projectData.appResourcesDirectoryPath, projectData);
this._changesInfo.packageChanged = this.isProjectFileChanged(projectData, platform);
this._changesInfo.appResourcesChanged = this.containsNewerFiles(projectData.appResourcesDirectoryPath, null, projectData);
/*done because currently all node_modules are traversed, a possible improvement could be traversing only the production dependencies*/
this._changesInfo.nativeChanged = projectChangesOptions.skipModulesNativeCheck ? false : this.containsNewerFiles(
path.join(projectData.projectDir, NODE_MODULES_FOLDER_NAME),
path.join(projectData.projectDir, NODE_MODULES_FOLDER_NAME, "tns-ios-inspector"),
Expand Down
2 changes: 1 addition & 1 deletion lib/services/project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export class ProjectService implements IProjectService {

private async ensureAppResourcesExist(projectDir: string): Promise<void> {
const appPath = path.join(projectDir, constants.APP_FOLDER_NAME),
appResourcesDestinationPath = path.join(appPath, constants.APP_RESOURCES_FOLDER_NAME);
appResourcesDestinationPath = this.$projectData.getAppResourcesDirectoryPath(projectDir);

if (!this.$fs.exists(appResourcesDestinationPath)) {
this.$fs.createDirectory(appResourcesDestinationPath);
Expand Down
21 changes: 16 additions & 5 deletions test/app-files-updates.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { assert } from "chai";
import { AppFilesUpdater } from "../lib/services/app-files-updater";
import * as yok from "../lib/common/yok";

require("should");

function createTestInjector(): IInjector {
const testInjector = new yok.Yok();

testInjector.register("projectData", { appResourcesDirectoryPath: "App_Resources"});

return testInjector;
}

describe("App files cleanup", () => {
class CleanUpAppFilesUpdater extends AppFilesUpdater {
public deletedDestinationItems: string[] = [];
Expand Down Expand Up @@ -54,23 +63,25 @@ describe("App files copy", () => {
}

public copy(): void {
this.copiedDestinationItems = this.resolveAppSourceFiles();
const injector = createTestInjector();
const projectData = <IProjectData>injector.resolve("projectData");
this.copiedDestinationItems = this.resolveAppSourceFiles(projectData);
}
}

it("copies all app files when not bundling", () => {
it("copies all app files but app_resources when not bundling", () => {
const updater = new CopyAppFilesUpdater([
"file1", "dir1/file2", "App_Resources/Android/blah.png"
], { bundle: false });
updater.copy();
assert.deepEqual(["file1", "dir1/file2", "App_Resources/Android/blah.png"], updater.copiedDestinationItems);
assert.deepEqual(["file1", "dir1/file2"], updater.copiedDestinationItems);
});

it("skips copying non-App_Resource files when bundling", () => {
it("skips copying files when bundling", () => {
const updater = new CopyAppFilesUpdater([
"file1", "dir1/file2", "App_Resources/Android/blah.png"
], { bundle: true });
updater.copy();
assert.deepEqual(["App_Resources/Android/blah.png"], updater.copiedDestinationItems);
assert.deepEqual([], updater.copiedDestinationItems);
});
});
3 changes: 2 additions & 1 deletion test/ios-entitlements-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe("IOSEntitlements Service Tests", () => {
const testInjector = new yok.Yok();

testInjector.register('platformsData', stubs.PlatformsDataStub);
testInjector.register('projectData', stubs.ProjectDataStub);
testInjector.register("logger", stubs.LoggerStub);
testInjector.register('iOSEntitlementsService', IOSEntitlementsService);

Expand Down Expand Up @@ -46,7 +47,7 @@ describe("IOSEntitlements Service Tests", () => {
injector = createTestInjector();

platformsData = injector.resolve("platformsData");
projectData = <IProjectData>platformsData.getPlatformData();
projectData = $injector.resolve<IProjectData>("projectData");
projectData.projectName = 'testApp';

projectData.platformsDir = temp.mkdirSync("platformsDir");
Expand Down
9 changes: 5 additions & 4 deletions test/ios-project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ import { Utils } from "../lib/common/utils";
import { CocoaPodsService } from "../lib/services/cocoapods-service";
import { NpmInstallationManager } from "../lib/npm-installation-manager";
import { NodePackageManager } from "../lib/node-package-manager";
import * as constants from "../lib/constants";

import { assert } from "chai";
import { IOSProvisionService } from "../lib/services/ios-provision-service";
import { SettingsService } from "../lib/common/test/unit-tests/stubs";
import { ProjectDataStub } from "./stubs";
import temp = require("temp");

temp.track();
Expand Down Expand Up @@ -65,12 +65,13 @@ function createTestInjector(projectPath: string, projectName: string): IInjector
testInjector.register("iOSEntitlementsService", IOSEntitlementsService);
testInjector.register("logger", LoggerLib.Logger);
testInjector.register("options", OptionsLib.Options);
testInjector.register("projectData", {
const projectData = Object.assign({}, ProjectDataStub, {
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe you can just mock the required methods, but this is not a merge stopper

platformsDir: path.join(projectPath, "platforms"),
projectName: projectName,
projectPath: projectPath,
projectFilePath: path.join(projectPath, "package.json")
});
testInjector.register("projectData", projectData);
testInjector.register("projectHelper", {});
testInjector.register("xcodeSelectService", {});
testInjector.register("staticConfig", ConfigLib.StaticConfig);
Expand Down Expand Up @@ -793,11 +794,11 @@ describe("Merge Project XCConfig files", () => {
iOSProjectService = testInjector.resolve("iOSProjectService");
projectData = testInjector.resolve("projectData");
projectData.projectDir = projectPath;
projectData.appResourcesDirectoryPath = path.join(projectData.projectDir, "app", "App_Resources");

iOSEntitlementsService = testInjector.resolve("iOSEntitlementsService");

appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME,
constants.APP_RESOURCES_FOLDER_NAME, "iOS", "build.xcconfig");
appResourcesXcconfigPath = path.join(projectData.appResourcesDirectoryPath, "iOS", "build.xcconfig");
appResourceXCConfigContent = `CODE_SIGN_IDENTITY = iPhone Distribution
// To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html
// DEVELOPMENT_TEAM = YOUR_TEAM_ID;
Expand Down
Loading