Skip to content

Commit c909c10

Browse files
Merge pull request #1278 from NativeScript/vladimirov/merge-rel-master-2
Merge release in master
2 parents bb15221 + 9c464d9 commit c909c10

16 files changed

+192
-57
lines changed

CHANGELOG.md

+27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11
NativeScript CLI Changelog
22
================
33

4+
1.5.0 (2015, November 24)
5+
==
6+
7+
### New
8+
* [Implemented #493](https://github.com/NativeScript/nativescript-cli/issues/493): Enable transpilers support in NativeScript projects.
9+
* [Implemented #594](https://github.com/NativeScript/nativescript-cli/issues/594): Implement a hard dependency on the node.js version and exit if not satisfied.
10+
* [Implemented #684](https://github.com/NativeScript/nativescript-cli/issues/684): Enable commands hooks.
11+
* [Implemented #955](https://github.com/NativeScript/nativescript-cli/issues/955): Support for Xcode7 simulator.
12+
* [Implemented #1007](https://github.com/NativeScript/nativescript-cli/issues/1007): Smarter and faster LiveSync.
13+
* [Implemented #1048](https://github.com/NativeScript/nativescript-cli/issues/1048): Support Gradle files from plugins, merge android resource files using aapt and respect AndroidManifest.xml from App_Resources.
14+
* [Implemented #1113](https://github.com/NativeScript/nativescript-cli/issues/1113): Let users create and execute unit tests in their projects.
15+
* [Implemented #1117](https://github.com/NativeScript/nativescript-cli/issues/1117): Support for TypeScript-based NativeScript projects.
16+
* [Implemented #1130](https://github.com/NativeScript/nativescript-cli/issues/1130): Show application output from livesync command for iOS devices and android devices and emulators.
17+
* [Implemented #1164](https://github.com/NativeScript/nativescript-cli/issues/1164): Use android tools from ANDROID_HOME.
18+
* [Implemented #1229](https://github.com/NativeScript/nativescript-cli/issues/1229): Support for Node 5.1.0.
19+
20+
### Fixed
21+
* [Fixed #727](https://github.com/NativeScript/nativescript-cli/issues/727): Double logging with tns run ios.
22+
* [Fixed #1044](https://github.com/NativeScript/nativescript-cli/issues/1044): iOS debug break on Simulator causes app crash when the debugger is paused on the first line for a long time.
23+
* [Fixed #1086](https://github.com/NativeScript/nativescript-cli/issues/1086): --key-store-path option to look for the keystore file path relative to the app root.
24+
* [Fixed #1106](https://github.com/NativeScript/nativescript-cli/issues/1106): Livesync double restarts for TS projects.
25+
* [Fixed #1110](https://github.com/NativeScript/nativescript-cli/issues/1110): `tns doctor` command should detect invalid java version.
26+
* [Fixed #1167](https://github.com/NativeScript/nativescript-cli/issues/1167): `tns run`, `tns emulate` and `tns deploy` commands do not check if device is available before build.
27+
* [Fixed #1177](https://github.com/NativeScript/nativescript-cli/issues/1177): `tns run` command fails when multiple devices with same platform are attached.
28+
* [Fixed #1185](https://github.com/NativeScript/nativescript-cli/issues/1185): `tns device` command fails when VS Emulator is running.
29+
* [Fixed #1204](https://github.com/NativeScript/nativescript-cli/issues/1204): Incorrect prepare when using npm 3.x.
30+
431
1.4.3 (2015, October 21)
532
==
633

PLUGINS.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,16 @@ my-plugin/
7474
│ └── AndroidManifest.xml
7575
└── ios/
7676
└── Info.plist
77+
└── Podfile
7778
```
7879

79-
* `index.js`: This file is the CommonJS module which exposes the native API. You can use platform-specific `*.platform.js` files. For example: `index.ios.js` and `index.android.js`. During the plugin installation, the NativeScript CLI will copy the platform resources to the `tns_modules` subdirectory in the correct platform destination in the `platforms` directory of your project.<br/>Alternatively, you can give any name to this CommonJS module. In this case, however, you need to point to this file by setting the `main` key in the `package.json` for the plugin. For more information, see [Folders as Modules](https://nodejs.org/api/modules.html#modules_folders_as_modules).
80+
* `index.js`: This file is the CommonJS module which exposes the native API. You can use platform-specific `*.platform.js` files. For example: `index.ios.js` and `index.android.js`. During the plugin installation, the NativeScript CLI will copy the platform resources to the `tns_modules` subdirectory in the correct platform destination in the `platforms` directory of your project.<br/>Alternatively, you can give any name to this CommonJS module. In this case, however, you need to point to this file by setting the `main` key in the `package.json` for the plugin. For more information, see [Folders as Modules](https://nodejs.org/api/modules.html#modules_folders_as_modules).
8081
* `package.json`: This file contains the metadata for your plugin. It sets the supported runtimes, the plugin name and version and any dependencies. The `package.json` specification is described in detail below.
8182
* `platforms\android\AndroidManifest.xml`: This file describes any specific configuration changes required for your plugin to work. For example: required permissions. For more information about the format of `AndroidManifest.xml`, see [App Manifest](http://developer.android.com/guide/topics/manifest/manifest-intro.html).<br/>During build, gradle will merge the plugin `AndroidManifest.xml` with the `AndroidManifest.xml` for your project. The NativeScript CLI will not resolve any contradicting or duplicate entries during the merge. After the plugin is installed, you need to manually resolve such issues.
8283
* `platforms\android\include.gradle`: This file modifies the native Android configuration of your NativeScript project such as native dependencies, build types and configurations. For more information about the format of `include.gradle`, see [include.gradle file](#includegradle-specification).
8384
* `platforms/android/res`: (Optional) This directory contains resources declared by the `AndroidManifest.xml` file. You can look at the folder structure [here](http://developer.android.com/guide/topics/resources/providing-resources.html#ResourceTypes).
84-
* `platforms/ios/Info.plist`: This file describes any specific configuration changes required for your plugin to work. For example: required permissions. For more information about the format of `Info.plist`, see [About Information Property List Files](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/AboutInformationPropertyListFiles.html).<br/>During the plugin installation, the NativeScript CLI will merge the plugin `Info.plist` with the `Info.plist` for your project. The NativeScript CLI will not resolve any contradicting or duplicate entries during the merge. After the plugin is installed, you need to manually resolve such issues.
85+
* `platforms/ios/Info.plist`: This file describes any specific configuration changes required for your plugin to work. For example: required permissions. For more information about the format of `Info.plist`, see [About Information Property List Files](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/AboutInformationPropertyListFiles.html).<br/>During the plugin installation, the NativeScript CLI will merge the plugin `Info.plist` with the `Info.plist` for your project. The NativeScript CLI will not resolve any contradicting or duplicate entries during the merge. After the plugin is installed, you need to manually resolve such issues.
86+
* `platforms/ios/Podfile`: This file describes the dependency to the library that you want to use. For more information, see [CocoaPods.md](CocoaPods.md).
8587

8688
NativeScript plugins which contain both native Android and iOS libraries might have the following directory structure.
8789

lib/android-tools-info.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ export class AndroidToolsInfo implements IAndroidToolsInfo {
4444
if(this.validateAndroidHomeEnvVariable(this.androidHome).wait()) {
4545
let androidPath = path.join(this.androidHome, "tools", this.androidExecutableName);
4646
if(!this.trySetAndroidPath(androidPath).wait() && !this.trySetAndroidPath(this.androidExecutableName).wait()) {
47-
this.$errors.failWithoutHelp(`Unable to find "${this.androidExecutableName}" executable file. Make sure you have set ANDROID_HOME environment variable correctly.`);
47+
this.printMessage(`Unable to find "${this.androidExecutableName}" executable file. Make sure you have set ANDROID_HOME environment variable correctly.`);
4848
}
4949
} else {
50-
this.$errors.failWithoutHelp("ANDROID_HOME environment variable is not set correctly.");
50+
this.printMessage("ANDROID_HOME environment variable is not set correctly.");
5151
}
5252
}
5353

lib/commands/prepare.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@
22
"use strict";
33

44
export class PrepareCommand implements ICommand {
5-
constructor(private $platformService: IPlatformService,
5+
constructor(private $errors: IErrors,
6+
private $platformService: IPlatformService,
67
private $platformCommandParameter: ICommandParameter) { }
78

89
execute(args: string[]): IFuture<void> {
9-
return this.$platformService.preparePlatform(args[0]);
10+
return (() => {
11+
if (!this.$platformService.preparePlatform(args[0]).wait()) {
12+
this.$errors.failWithoutHelp("Unable to prepare the project.");
13+
}
14+
}).future<void>()();
1015
}
1116

1217
allowedParameters = [this.$platformCommandParameter];

lib/definitions/platform.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ interface IPlatformService {
66
removePlatforms(platforms: string[]): IFuture<void>;
77
updatePlatforms(platforms: string[]): IFuture<void>;
88
runPlatform(platform: string, buildConfig?: IBuildConfig): IFuture<void>;
9-
preparePlatform(platform: string): IFuture<void>;
9+
preparePlatform(platform: string): IFuture<boolean>;
1010
buildPlatform(platform: string, buildConfig?: IBuildConfig): IFuture<void>;
1111
installOnDevice(platform: string, buildConfig?: IBuildConfig): IFuture<void>;
1212
deployOnDevice(platform: string, buildConfig?: IBuildConfig): IFuture<void>;

lib/providers/device-log-provider.ts

+8-9
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
export class DeviceLogProvider implements Mobile.IDeviceLogProvider {
55
//sample line is "I/Web Console( 4438): Received Event: deviceready at file:///storage/emulated/0/Icenium/com.telerik.TestApp/js/index.js:48"
6-
private static LINE_REGEX = /.\/(.+?)\s*\(\s*(\d+?)\): (.*)/;
6+
private static LINE_REGEX = /.\/(.+?)\s*\(\s*\d+?\): (.*)/;
7+
// sample line is "11-23 12:39:07.310 1584 1597 I art : Background sticky concurrent mark sweep GC freed 21966(1780KB) AllocSpace objects, 4(80KB) LOS objects, 77% free, 840KB/3MB, paused 4.018ms total 158.629ms"
8+
private static API_LEVEL_23_LINE_REGEX = /.+?\s+?(?:[A-Z]\s+?)([A-Za-z ]+?)\s+?\: (.*)/;
79

810
constructor(private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
911
private $logger: ILogger) { }
@@ -25,15 +27,12 @@ export class DeviceLogProvider implements Mobile.IDeviceLogProvider {
2527

2628
private getConsoleLogFromLine(lineText: String): any {
2729
let acceptedTags = ["chromium", "Web Console", "JS"];
28-
let match = lineText.match(DeviceLogProvider.LINE_REGEX);
29-
if (match) {
30-
if(acceptedTags.indexOf(match[1]) !== -1) {
31-
return {tag: match[1], message: match[3]};
32-
}
33-
} else if (_.any(acceptedTags, (tag: string) => { return lineText.indexOf(tag) !== -1; })) {
34-
return {message: match[3]};
30+
let match = lineText.match(DeviceLogProvider.LINE_REGEX) || lineText.match(DeviceLogProvider.API_LEVEL_23_LINE_REGEX);
31+
if (match && acceptedTags.indexOf(match[1].trim()) !== -1) {
32+
return {tag: match[1].trim(), message: match[2]};
3533
}
36-
return null;
34+
let matchingTag = _.any(acceptedTags, (tag: string) => { return lineText.indexOf(tag) !== -1; });
35+
return matchingTag ? { message: lineText } : null;
3736
}
3837
}
3938
$injector.register("deviceLogProvider", DeviceLogProvider);

lib/services/android-project-service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
8080

8181
// this call will fail in case `android` is not set correctly.
8282
this.$androidToolsInfo.getPathToAndroidExecutable().wait();
83-
this.$androidToolsInfo.validateJavacVersion(this.$sysInfo.getSysInfo().wait().javacVersion, {showWarningsAsErrors: true}).wait();
83+
this.$androidToolsInfo.validateJavacVersion(this.$sysInfo.getSysInfo(path.join(__dirname, "..", "..", "package.json")).wait().javacVersion, {showWarningsAsErrors: true}).wait();
8484
}).future<void>()();
8585
}
8686

lib/services/doctor-service.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"use strict";
33
import {EOL} from "os";
44
import * as semver from "semver";
5+
import * as path from "path";
56

67
class DoctorService implements IDoctorService {
78
private static MIN_SUPPORTED_POD_VERSION = "0.38.2";
@@ -13,7 +14,7 @@ class DoctorService implements IDoctorService {
1314

1415
public printWarnings(): boolean {
1516
let result = false;
16-
let sysInfo = this.$sysInfo.getSysInfo().wait();
17+
let sysInfo = this.$sysInfo.getSysInfo(path.join(__dirname, "..", "..", "package.json")).wait();
1718

1819
if (!sysInfo.adbVer) {
1920
this.$logger.warn("WARNING: adb from the Android SDK is not installed or is not configured properly.");

lib/services/ios-debug-service.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,14 @@ class IOSDebugService implements IDebugService {
7676
this.$platformService.buildPlatform(this.platform).wait();
7777
let emulatorPackage = this.$platformService.getLatestApplicationPackageForEmulator(platformData).wait();
7878

79-
let child_process = this.$iOSEmulatorServices.startEmulator(emulatorPackage.packageName, { waitForDebugger: true, captureStdin: true, args: "--nativescript-debug-brk" }).wait();
79+
let child_process = this.$iOSEmulatorServices.startEmulator(emulatorPackage.packageName, { waitForDebugger: true, captureStdin: true,
80+
args: "--nativescript-debug-brk", appId: this.$projectData.projectId }).wait();
8081
let lineStream = byline(child_process.stdout);
8182

8283
lineStream.on('data', (line: NodeBuffer) => {
8384
let lineText = line.toString();
84-
if(lineText && _.startsWith(lineText, emulatorPackage.packageName)) {
85-
let pid = _.trimLeft(lineText, emulatorPackage.packageName + ": ");
85+
if(lineText && _.startsWith(lineText, this.$projectData.projectId)) {
86+
let pid = _.trimLeft(lineText, this.$projectData.projectId + ": ");
8687

8788
this.$childProcess.exec(`lldb -p ${pid} -o "process continue"`);
8889
} else {

lib/services/platform-service.ts

+39-6
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ export class PlatformService implements IPlatformService {
154154
}).future<string[]>()();
155155
}
156156

157-
public preparePlatform(platform: string): IFuture<void> {
157+
public preparePlatform(platform: string): IFuture<boolean> {
158158
return (() => {
159159
this.validatePlatform(platform);
160160

@@ -166,12 +166,36 @@ export class PlatformService implements IPlatformService {
166166
this.$errors.failWithoutHelp(`Unable to install dependencies. Make sure your package.json is valid and all dependencies are correct. Error is: ${err.message}`);
167167
}
168168

169-
this.preparePlatformCore(platform).wait();
170-
}).future<void>()();
169+
return this.preparePlatformCore(platform).wait();
170+
}).future<boolean>()();
171+
}
172+
173+
private checkXmlFiles(sourceFiles: string[]): IFuture<boolean> {
174+
return (() => {
175+
let xmlHasErrors = false;
176+
let DomParser = require("xmldom").DOMParser;
177+
sourceFiles
178+
.filter(file => _.endsWith(file, ".xml"))
179+
.forEach(file => {
180+
let fileContents = this.$fs.readText(file).wait();
181+
let hasErrors = false;
182+
let domErrorHandler = (level:any, msg:string) => hasErrors = true;
183+
let parser = new DomParser({
184+
locator:{},
185+
errorHandler: domErrorHandler
186+
});
187+
parser.parseFromString(fileContents, "text/xml");
188+
xmlHasErrors = xmlHasErrors || hasErrors;
189+
if (xmlHasErrors) {
190+
this.$logger.out("Error: ".red.bold + file + " has syntax errors.".red.bold);
191+
}
192+
});
193+
return !xmlHasErrors;
194+
}).future<boolean>()();
171195
}
172196

173197
@helpers.hook('prepare')
174-
private preparePlatformCore(platform: string): IFuture<void> {
198+
private preparePlatformCore(platform: string): IFuture<boolean> {
175199
return (() => {
176200
platform = platform.toLowerCase();
177201
this.ensurePlatformInstalled(platform).wait();
@@ -206,6 +230,12 @@ export class PlatformService implements IPlatformService {
206230
sourceFiles = sourceFiles.filter(source => !minimatch(source, `**/${constants.TNS_MODULES_FOLDER_NAME}/**`, {nocase: true}));
207231
}
208232

233+
// verify .xml files are well-formed
234+
let validXmlFiles = this.checkXmlFiles(sourceFiles).wait();
235+
if (!validXmlFiles) {
236+
return false;
237+
}
238+
209239
// Remove .ts and .js.map files
210240
PlatformService.EXCLUDE_FILES_PATTERN.forEach(pattern => sourceFiles = sourceFiles.filter(file => !minimatch(file, pattern, {nocase: true})));
211241
let copyFileFutures = sourceFiles.map(source => {
@@ -245,13 +275,16 @@ export class PlatformService implements IPlatformService {
245275
platformData.platformProjectService.processConfigurationFilesFromAppResources().wait();
246276

247277
this.$logger.out("Project successfully prepared");
248-
}).future<void>()();
278+
return true;
279+
}).future<boolean>()();
249280
}
250281

251282
public buildPlatform(platform: string, buildConfig?: IBuildConfig): IFuture<void> {
252283
return (() => {
253284
platform = platform.toLowerCase();
254-
this.preparePlatform(platform).wait();
285+
if (!this.preparePlatform(platform).wait()) {
286+
this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation.");
287+
}
255288

256289
let platformData = this.$platformsData.getPlatformData(platform);
257290
platformData.platformProjectService.buildProject(platformData.projectRoot, buildConfig).wait();

lib/services/test-execution-service.ts

+16-3
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ class TestExecutionService implements ITestExecutionService {
3333
private $logger: ILogger,
3434
private $fs: IFileSystem,
3535
private $options: IOptions,
36-
private $pluginsService: IPluginsService) {
36+
private $pluginsService: IPluginsService,
37+
private $errors: IErrors) {
3738
}
3839

3940
public startTestRunner(platform: string) : IFuture<void> {
@@ -58,7 +59,9 @@ class TestExecutionService implements ITestExecutionService {
5859
let socketIoJs = this.$httpClient.httpRequest(socketIoJsUrl).wait().body;
5960
this.$fs.writeFile(path.join(projectDir, TestExecutionService.SOCKETIO_JS_FILE_NAME), socketIoJs).wait();
6061

61-
this.$platformService.preparePlatform(platform).wait();
62+
if (!this.$platformService.preparePlatform(platform).wait()) {
63+
this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation.");
64+
}
6265
this.detourEntryPoint(projectFilesPath).wait();
6366

6467
let watchGlob = path.join(projectDir, constants.APP_FOLDER_NAME);
@@ -88,13 +91,22 @@ class TestExecutionService implements ITestExecutionService {
8891

8992
let beforeBatchLiveSyncAction = (filePath: string): IFuture<string> => {
9093
return (() => {
91-
this.$platformService.preparePlatform(platform).wait();
94+
if (!this.$platformService.preparePlatform(platform).wait()) {
95+
this.$logger.out("Verify that listed files are well-formed and try again the operation.");
96+
return;
97+
}
9298
return path.join(projectFilesPath, path.relative(path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME), filePath));
9399
}).future<string>()();
94100
};
95101

96102
let localProjectRootPath = platform.toLowerCase() === "ios" ? platformData.appDestinationDirectoryPath : null;
97103

104+
let getApplicationPathForiOSSimulatorAction = (): IFuture<string> => {
105+
return (() => {
106+
return this.$platformService.getLatestApplicationPackageForEmulator(platformData).wait().packageName;
107+
}).future<string>()();
108+
};
109+
98110
let liveSyncData = {
99111
platform: platform,
100112
appIdentifier: this.$projectData.projectId,
@@ -104,6 +116,7 @@ class TestExecutionService implements ITestExecutionService {
104116
platformSpecificLiveSyncServices: platformSpecificLiveSyncServices,
105117
notInstalledAppOnDeviceAction: notInstalledAppOnDeviceAction,
106118
notRunningiOSSimulatorAction: notRunningiOSSimulatorAction,
119+
getApplicationPathForiOSSimulatorAction: getApplicationPathForiOSSimulatorAction,
107120
localProjectRootPath: localProjectRootPath,
108121
beforeBatchLiveSyncAction: beforeBatchLiveSyncAction,
109122
shouldRestartApplication: (localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Future.fromResult(!this.$options.debugBrk),

0 commit comments

Comments
 (0)