Skip to content

Commit 6564ef8

Browse files
committed
Check XML files for well-formedness
1 parent 939de28 commit 6564ef8

File tree

6 files changed

+110
-25
lines changed

6 files changed

+110
-25
lines changed

lib/commands/prepare.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ export class PrepareCommand implements ICommand {
66
private $platformCommandParameter: ICommandParameter) { }
77

88
execute(args: string[]): IFuture<void> {
9-
return this.$platformService.preparePlatform(args[0]);
9+
return (() => {
10+
this.$platformService.preparePlatform(args[0]);
11+
}).future<void>()();
1012
}
1113

1214
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/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.`);
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();
@@ -205,6 +229,12 @@ export class PlatformService implements IPlatformService {
205229
this.$logger.warn("You have tns_modules dir in your app folder and tns-core-modules in your package.json file. Tns_modules dir in your app folder will not be used and you can safely remove it.");
206230
sourceFiles = sourceFiles.filter(source => !minimatch(source, `**/${constants.TNS_MODULES_FOLDER_NAME}/**`, {nocase: true}));
207231
}
232+
233+
// verify .xml files are well-formed
234+
let validXmlFiles = this.checkXmlFiles(sourceFiles).wait();
235+
if (!validXmlFiles) {
236+
return false;
237+
}
208238

209239
// Remove .ts and .js.map files
210240
PlatformService.EXCLUDE_FILES_PATTERN.forEach(pattern => sourceFiles = sourceFiles.filter(file => !minimatch(file, pattern, {nocase: true})));
@@ -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

+9-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,7 +91,10 @@ 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
};

lib/services/usb-livesync-service.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export class UsbLiveSyncService extends usbLivesyncServiceBaseLib.UsbLiveSyncSer
3434
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
3535
private $projectDataService: IProjectDataService,
3636
private $prompter: IPrompter,
37+
private $errors: IErrors,
3738
$hostInfo: IHostInfo) {
3839
super($devicesService, $mobileHelper, $localToDevicePathDataFactory, $logger, $options,
3940
$deviceAppDataFactory, $fs, $dispatcher, $injector, $childProcess, $iOSEmulatorServices,
@@ -65,7 +66,9 @@ export class UsbLiveSyncService extends usbLivesyncServiceBaseLib.UsbLiveSyncSer
6566
}
6667
}
6768

68-
this.$platformService.preparePlatform(platform).wait();
69+
if (!this.$platformService.preparePlatform(platform).wait()) {
70+
this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation.");
71+
}
6972

7073
let projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME);
7174

@@ -121,7 +124,10 @@ export class UsbLiveSyncService extends usbLivesyncServiceBaseLib.UsbLiveSyncSer
121124
let fastLiveSync = (filePath: string) => {
122125
this.$dispatcher.dispatch(() => {
123126
return (() => {
124-
this.$platformService.preparePlatform(platform).wait();
127+
if (!this.$platformService.preparePlatform(platform).wait()) {
128+
this.$logger.out("Verify that listed files are well-formed and try again the operation.");
129+
return;
130+
}
125131
let mappedFilePath = beforeBatchLiveSyncAction(filePath).wait();
126132

127133
if (this.shouldSynciOSSimulator(platform).wait()) {
@@ -177,7 +183,10 @@ export class UsbLiveSyncService extends usbLivesyncServiceBaseLib.UsbLiveSyncSer
177183
}
178184

179185
protected preparePlatformForSync(platform: string) {
180-
this.$platformService.preparePlatform(platform).wait();
186+
if (!this.$platformService.preparePlatform(platform).wait()) {
187+
this.$logger.out("Verify that listed files are well-formed and try again the operation.");
188+
return;
189+
}
181190
}
182191

183192
private resolveUsbLiveSyncService(platform: string, device: Mobile.IDevice): IPlatformSpecificUsbLiveSyncService {

test/platform-service.ts

+46-11
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ describe('Platform Service Tests', () => {
181181
testInjector.register("fs", fsLib.FileSystem);
182182
fs = testInjector.resolve("fs");
183183
});
184-
it("should process only files in app folder when preparing for iOS platform", () => {
184+
185+
function prepareDirStructure() {
185186
let tempFolder = temp.mkdirSync("prepare platform");
186187

187188
let appFolderPath = path.join(tempFolder, "app");
@@ -193,6 +194,12 @@ describe('Platform Service Tests', () => {
193194
let appDestFolderPath = path.join(tempFolder, "appDest");
194195
let appResourcesFolderPath = path.join(appDestFolderPath, "App_Resources");
195196

197+
return { tempFolder, appFolderPath, app1FolderPath, appDestFolderPath, appResourcesFolderPath };
198+
}
199+
200+
it("should process only files in app folder when preparing for iOS platform", () => {
201+
let { tempFolder, appFolderPath, app1FolderPath, appDestFolderPath, appResourcesFolderPath } = prepareDirStructure();
202+
196203
// Add platform specific files to app and app1 folders
197204
let platformSpecificFiles = [
198205
"test1.ios.js", "test1-ios-js", "test2.android.js", "test2-android-js"
@@ -242,16 +249,7 @@ describe('Platform Service Tests', () => {
242249
assert.isFalse(fs.exists(path.join(appDestFolderPath, "app1")).wait());
243250
});
244251
it("should process only files in app folder when preparing for Android platform", () => {
245-
let tempFolder = temp.mkdirSync("prepare platform");
246-
247-
let appFolderPath = path.join(tempFolder, "app");
248-
fs.createDirectory(appFolderPath).wait();
249-
250-
let app1FolderPath = path.join(tempFolder, "app1");
251-
fs.createDirectory(app1FolderPath).wait();
252-
253-
let appDestFolderPath = path.join(tempFolder, "appDest");
254-
let appResourcesFolderPath = path.join(appDestFolderPath, "App_Resources");
252+
let { tempFolder, appFolderPath, app1FolderPath, appDestFolderPath, appResourcesFolderPath } = prepareDirStructure();
255253

256254
// Add platform specific files to app and app1 folders
257255
let platformSpecificFiles = [
@@ -301,5 +299,42 @@ describe('Platform Service Tests', () => {
301299
// Asserts that the files in app1 folder aren't process as platform specific
302300
assert.isFalse(fs.exists(path.join(appDestFolderPath, "app1")).wait());
303301
});
302+
303+
it("invalid xml is caught", () => {
304+
require("colors");
305+
let { tempFolder, appFolderPath, app1FolderPath, appDestFolderPath, appResourcesFolderPath } = prepareDirStructure();
306+
307+
// generate invalid xml
308+
let fileFullPath = path.join(appFolderPath, "file.xml");
309+
fs.writeFile(fileFullPath, "<xml><unclosedTag></xml>").wait();
310+
311+
let platformsData = testInjector.resolve("platformsData");
312+
platformsData.platformsNames = ["android"];
313+
platformsData.getPlatformData = (platform: string) => {
314+
return {
315+
appDestinationDirectoryPath: appDestFolderPath,
316+
appResourcesDestinationDirectoryPath: appResourcesFolderPath,
317+
normalizedPlatformName: "Android",
318+
platformProjectService: {
319+
prepareProject: () => Future.fromResult(),
320+
validate: () => Future.fromResult(),
321+
createProject: (projectRoot: string, frameworkDir: string) => Future.fromResult(),
322+
interpolateData: (projectRoot: string) => Future.fromResult(),
323+
afterCreateProject: (projectRoot: string) => Future.fromResult(),
324+
getAppResourcesDestinationDirectoryPath: () => Future.fromResult(""),
325+
processConfigurationFilesFromAppResources: () => Future.fromResult()
326+
}
327+
};
328+
};
329+
330+
let projectData = testInjector.resolve("projectData");
331+
projectData.projectDir = tempFolder;
332+
333+
platformService = testInjector.resolve("platformService");
334+
let result = platformService.preparePlatform("android").wait();
335+
336+
// Asserts that prepare has caught invalid xml
337+
assert.isFalse(result);
338+
});
304339
});
305340
});

0 commit comments

Comments
 (0)