Skip to content

Commit d688186

Browse files
chore: merge release in master
2 parents 71e7fb1 + 168c2ae commit d688186

10 files changed

+279
-169
lines changed

CHANGELOG.md

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

4+
6.4.1 (2020, February 19)
5+
===
6+
7+
### Fixed
8+
9+
* [Fixed #5236](https://github.com/NativeScript/nativescript-cli/issues/5236): File paths from device logs are not clickable
10+
* [Fixed #5251](https://github.com/NativeScript/nativescript-cli/issues/5251): External files are not livesynced
11+
* [Fixed #5252](https://github.com/NativeScript/nativescript-cli/issues/5252): Logs from platform specific files point to incorrect file
12+
13+
414
6.4.0 (2020, February 11)
515
===
616

lib/common/test/unit-tests/mobile/device-log-provider.ts

+143-143
Large diffs are not rendered by default.

lib/detached-processes/cleanup-process-definitions.d.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ interface ISpawnCommandInfo extends ITimeout {
2222
* Arguments that will be passed to the child process
2323
*/
2424
args: string[];
25+
26+
/**
27+
* Options to be passed to the child process
28+
*/
29+
options?: any;
30+
}
31+
32+
interface IRequestInfo extends ITimeout {
33+
url: string,
34+
method: string,
35+
body: any,
36+
headers: any
2537
}
2638

2739
interface ICleanupMessageBase {
@@ -38,6 +50,13 @@ interface ISpawnCommandCleanupMessage extends ICleanupMessageBase {
3850
commandInfo: ISpawnCommandInfo;
3951
}
4052

53+
interface IRequestCleanupMessage extends ICleanupMessageBase {
54+
/**
55+
* Describes the request that must be executed
56+
*/
57+
requestInfo: IRequestInfo;
58+
}
59+
4160
interface IFileCleanupMessage extends ICleanupMessageBase, IFilePath { }
4261

4362
interface IJSCommand extends ITimeout, IFilePath {
@@ -46,4 +65,4 @@ interface IJSCommand extends ITimeout, IFilePath {
4665

4766
interface IJSCleanupMessage extends ICleanupMessageBase {
4867
jsCommand: IJSCommand;
49-
}
68+
}

lib/detached-processes/cleanup-process.ts

+48-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,24 @@ fileLogService.logData({ message: "Initializing Cleanup process." });
2020
const commandsInfos: ISpawnCommandInfo[] = [];
2121
const filesToDelete: string[] = [];
2222
const jsCommands: IJSCommand[] = [];
23+
const requests: IRequestInfo[] = [];
24+
25+
const executeRequest = async (request: IRequestInfo) => {
26+
const $httpClient = $injector.resolve<Server.IHttpClient>("httpClient");
27+
try {
28+
fileLogService.logData({ message: `Start executing request: ${request.method} ${request.url}` });
29+
const response = await $httpClient.httpRequest({
30+
url: request.url,
31+
method: request.method,
32+
headers: request.headers,
33+
body: request.body
34+
});
35+
const responseStatus = response && response.response && response.response.statusCode;
36+
fileLogService.logData({ message: `Finished executing request: ${request.method} ${request.url} and got status ${responseStatus}` });
37+
} catch (e) {
38+
fileLogService.logData({ message: `Unable to execute request: ${request.method} ${request.url}` });
39+
}
40+
};
2341

2442
const executeJSCleanup = async (jsCommand: IJSCommand) => {
2543
const $childProcess = $injector.resolve<IChildProcess>("childProcess");
@@ -28,7 +46,7 @@ const executeJSCleanup = async (jsCommand: IJSCommand) => {
2846
fileLogService.logData({ message: `Start executing action for file: ${jsCommand.filePath} and data ${JSON.stringify(jsCommand.data)}` });
2947

3048
await $childProcess.trySpawnFromCloseEvent(process.execPath, [path.join(__dirname, "cleanup-js-subprocess.js"), pathToBootstrap, logFile, jsCommand.filePath, JSON.stringify(jsCommand.data)], {}, { throwError: true, timeout: jsCommand.timeout || 3000 });
31-
fileLogService.logData({ message: `Finished xecuting action for file: ${jsCommand.filePath} and data ${JSON.stringify(jsCommand.data)}` });
49+
fileLogService.logData({ message: `Finished executing action for file: ${jsCommand.filePath} and data ${JSON.stringify(jsCommand.data)}` });
3250

3351
} catch (err) {
3452
fileLogService.logData({ message: `Unable to execute action for file ${jsCommand.filePath} with data ${JSON.stringify(jsCommand.data)}. Error is: ${err}.`, type: FileLogMessageType.Error });
@@ -38,6 +56,10 @@ const executeJSCleanup = async (jsCommand: IJSCommand) => {
3856
const executeCleanup = async () => {
3957
const $childProcess = $injector.resolve<IChildProcess>("childProcess");
4058

59+
for (const request of requests) {
60+
await executeRequest(request);
61+
}
62+
4163
for (const jsCommand of jsCommands) {
4264
await executeJSCleanup(jsCommand);
4365
}
@@ -46,7 +68,7 @@ const executeCleanup = async () => {
4668
try {
4769
fileLogService.logData({ message: `Start executing command: ${JSON.stringify(commandInfo)}` });
4870

49-
await $childProcess.trySpawnFromCloseEvent(commandInfo.command, commandInfo.args, {}, { throwError: true, timeout: commandInfo.timeout || 3000 });
71+
await $childProcess.trySpawnFromCloseEvent(commandInfo.command, commandInfo.args, commandInfo.options || {}, { throwError: true, timeout: commandInfo.timeout || 3000 });
5072
fileLogService.logData({ message: `Successfully executed command: ${JSON.stringify(commandInfo)}` });
5173
} catch (err) {
5274
fileLogService.logData({ message: `Unable to execute command: ${JSON.stringify(commandInfo)}. Error is: ${err}.`, type: FileLogMessageType.Error });
@@ -84,6 +106,24 @@ const removeCleanupAction = (commandInfo: ISpawnCommandInfo): void => {
84106
}
85107
};
86108

109+
const addRequest = (requestInfo: IRequestInfo): void => {
110+
if (_.some(requests, currentRequestInfo => _.isEqual(currentRequestInfo, requestInfo))) {
111+
fileLogService.logData({ message: `cleanup-process will not add request for execution as it has been added already: ${JSON.stringify(requestInfo)}` });
112+
} else {
113+
fileLogService.logData({ message: `cleanup-process added request for execution: ${JSON.stringify(requestInfo)}` });
114+
requests.push(requestInfo);
115+
}
116+
};
117+
118+
const removeRequest = (requestInfo: IRequestInfo): void => {
119+
if (_.some(requests, currentRequestInfo => _.isEqual(currentRequestInfo, currentRequestInfo))) {
120+
_.remove(requests, currentRequestInfo => _.isEqual(currentRequestInfo, requestInfo));
121+
fileLogService.logData({ message: `cleanup-process removed request for execution: ${JSON.stringify(requestInfo)}` });
122+
} else {
123+
fileLogService.logData({ message: `cleanup-process cannot remove request for execution as it has not been added before: ${JSON.stringify(requestInfo)}` });
124+
}
125+
};
126+
87127
const addDeleteAction = (filePath: string): void => {
88128
const fullPath = path.resolve(filePath);
89129

@@ -142,6 +182,12 @@ process.on("message", async (cleanupProcessMessage: ICleanupMessageBase) => {
142182
case CleanupProcessMessage.RemoveCleanCommand:
143183
removeCleanupAction((<ISpawnCommandCleanupMessage>cleanupProcessMessage).commandInfo);
144184
break;
185+
case CleanupProcessMessage.AddRequest:
186+
addRequest((<IRequestCleanupMessage>cleanupProcessMessage).requestInfo);
187+
break;
188+
case CleanupProcessMessage.RemoveRequest:
189+
removeRequest((<IRequestCleanupMessage>cleanupProcessMessage).requestInfo);
190+
break;
145191
case CleanupProcessMessage.AddDeleteFileAction:
146192
addDeleteAction((<IFileCleanupMessage>cleanupProcessMessage).filePath);
147193
break;

lib/detached-processes/detached-process-enums.d.ts

+9
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ declare const enum CleanupProcessMessage {
3838
* This type of message defines that cleanup procedure should not execute previously defined cleanup command.
3939
*/
4040
RemoveCleanCommand = "RemoveCleanCommand",
41+
/**
42+
* This type of message defines that cleanup procedure should execute specific request.
43+
*/
44+
AddRequest = "AddRequest",
45+
46+
/**
47+
* This type of message defines that cleanup procedure should not execute previously defined request.
48+
*/
49+
RemoveRequest = "RemoveRequest",
4150

4251
/**
4352
* This type of message defines that cleanup procedure should delete specified files.

lib/services/cleanup-service.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ export class CleanupService implements ICleanupService {
2525
cleanupProcess.send(<ISpawnCommandCleanupMessage>{ messageType: CleanupProcessMessage.RemoveCleanCommand, commandInfo });
2626
}
2727

28+
public async addRequest(requestInfo: IRequestInfo): Promise<void> {
29+
const cleanupProcess = await this.getCleanupProcess();
30+
cleanupProcess.send(<IRequestCleanupMessage>{ messageType: CleanupProcessMessage.AddRequest, requestInfo });
31+
}
32+
33+
public async removeRequest(requestInfo: IRequestInfo): Promise<void> {
34+
const cleanupProcess = await this.getCleanupProcess();
35+
cleanupProcess.send(<IRequestCleanupMessage>{ messageType: CleanupProcessMessage.RemoveRequest, requestInfo });
36+
}
37+
2838
public async addCleanupDeleteAction(filePath: string): Promise<void> {
2939
const cleanupProcess = await this.getCleanupProcess();
3040
cleanupProcess.send(<IFileCleanupMessage>{ messageType: CleanupProcessMessage.AddDeleteFileAction, filePath });
@@ -42,7 +52,7 @@ export class CleanupService implements ICleanupService {
4252

4353
public async removeCleanupJS(jsCommand: IJSCommand): Promise<void> {
4454
const cleanupProcess = await this.getCleanupProcess();
45-
cleanupProcess.send(<IJSCleanupMessage>{ messageType: CleanupProcessMessage.RemoveJSFileToRequire, jsCommand});
55+
cleanupProcess.send(<IJSCleanupMessage>{ messageType: CleanupProcessMessage.RemoveJSFileToRequire, jsCommand });
4656
}
4757

4858
public async addKillProcess(pid: string): Promise<void> {

lib/services/ios-project-service.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -209,29 +209,29 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
209209
}
210210

211211
@hook('buildIOS')
212-
public async buildProject(projectRoot: string, projectData: IProjectData, iOSBuildData: IOSBuildData): Promise<void> {
212+
public async buildProject(projectRoot: string, projectData: IProjectData, buildData: IOSBuildData): Promise<void> {
213213
const platformData = this.getPlatformData(projectData);
214214

215215
const handler = (data: any) => {
216216
this.emit(constants.BUILD_OUTPUT_EVENT_NAME, data);
217217
};
218218

219-
if (iOSBuildData.buildForDevice) {
220-
await this.$iOSSigningService.setupSigningForDevice(projectRoot, projectData, iOSBuildData);
219+
if (buildData.buildForDevice) {
220+
await this.$iOSSigningService.setupSigningForDevice(projectRoot, projectData, buildData);
221221
await attachAwaitDetach(constants.BUILD_OUTPUT_EVENT_NAME,
222222
this.$childProcess,
223223
handler,
224-
this.$xcodebuildService.buildForDevice(platformData, projectData, <any>iOSBuildData));
225-
} else if (iOSBuildData.buildForAppStore) {
224+
this.$xcodebuildService.buildForDevice(platformData, projectData, <any>buildData));
225+
} else if (buildData.buildForAppStore) {
226226
await attachAwaitDetach(constants.BUILD_OUTPUT_EVENT_NAME,
227227
this.$childProcess,
228228
handler,
229-
this.$xcodebuildService.buildForAppStore(platformData, projectData, <any>iOSBuildData));
229+
this.$xcodebuildService.buildForAppStore(platformData, projectData, <any>buildData));
230230
} else {
231231
await attachAwaitDetach(constants.BUILD_OUTPUT_EVENT_NAME,
232232
this.$childProcess,
233233
handler,
234-
this.$xcodebuildService.buildForSimulator(platformData, projectData, <any>iOSBuildData));
234+
this.$xcodebuildService.buildForSimulator(platformData, projectData, <any>buildData));
235235
}
236236

237237
this.validateApplicationIdentifier(projectData);

lib/services/log-source-map-service.ts

+16-3
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ interface IFileLocation {
2222

2323
export class LogSourceMapService implements Mobile.ILogSourceMapService {
2424
private static FILE_PREFIX = "file:///";
25+
private static FILE_PREFIX_REPLACEMENT = "file: ";
2526
private static MEMOIZE_FUNCTION_RANDOM_KEY_FOR_JOIN = "__some_random_value__";
2627
private getProjectData: (projectDir: string) => IProjectData;
2728
private getRuntimeVersion: (projectDir: string, platform: string) => string;
2829
private cache: IDictionary<sourcemap.SourceMapConsumer> = {};
30+
private originalFilesLocationCache: IStringDictionary = {};
2931

3032
private get $platformsDataService(): IPlatformsDataService {
3133
return this.$injector.resolve<IPlatformsDataService>("platformsDataService");
@@ -81,9 +83,9 @@ export class LogSourceMapService implements Mobile.ILogSourceMapService {
8183
const lastIndexOfFile = rawLine.lastIndexOf(LogSourceMapService.FILE_PREFIX);
8284
const firstPart = rawLine.substr(0, lastIndexOfFile);
8385

84-
outputData += firstPart + rawLine.substr(lastIndexOfFile).replace(/file:\/\/\/.+?:\d+:\d+/, `${LogSourceMapService.FILE_PREFIX}${sourceFile}:${line}:${column}`) + '\n';
86+
outputData += firstPart + rawLine.substr(lastIndexOfFile).replace(/file:\/\/\/.+?:\d+:\d+/, `${LogSourceMapService.FILE_PREFIX_REPLACEMENT}${sourceFile}:${line}:${column}`) + '\n';
8587
} else {
86-
outputData = `${outputData}${parsedLine.messagePrefix}${LogSourceMapService.FILE_PREFIX}${sourceFile}:${line}:${column}${parsedLine.messageSuffix}\n`;
88+
outputData = `${outputData}${parsedLine.messagePrefix}${LogSourceMapService.FILE_PREFIX_REPLACEMENT}${sourceFile}:${line}:${column}${parsedLine.messageSuffix}\n`;
8789
}
8890
} else if (rawLine !== "") {
8991
outputData = `${outputData}${rawLine}\n`;
@@ -122,7 +124,18 @@ export class LogSourceMapService implements Mobile.ILogSourceMapService {
122124
}
123125

124126
sourceFile = stringReplaceAll(sourceFile, "/", path.sep);
125-
return { sourceFile, line: originalPosition.line, column: originalPosition.column };
127+
if (!this.originalFilesLocationCache[sourceFile]) {
128+
const { dir, ext, name } = path.parse(sourceFile);
129+
const platformSpecificName = `${name}.${platform.toLowerCase()}`;
130+
const platformSpecificFile = path.format({ dir, ext, name: platformSpecificName });
131+
if (this.$fs.exists(platformSpecificFile)) {
132+
this.originalFilesLocationCache[sourceFile] = platformSpecificFile;
133+
} else {
134+
this.originalFilesLocationCache[sourceFile] = sourceFile;
135+
}
136+
}
137+
138+
return { sourceFile: this.originalFilesLocationCache[sourceFile], line: originalPosition.line, column: originalPosition.column };
126139
}
127140
}
128141
}

lib/services/webpack/webpack-compiler-service.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,8 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
4949
return;
5050
}
5151

52-
// the hash of the compilation is the same as the previous one
53-
if (this.expectedHashes[platformData.platformNameLowerCase] === message.hash) {
54-
return;
55-
}
56-
52+
// Persist the previousHash value before calling `this.getUpdatedEmittedFiles` as it will modify the expectedHashes object with the current hash
53+
const previousHash = this.expectedHashes[platformData.platformNameLowerCase];
5754
let result;
5855

5956
if (prepareData.hmr) {
@@ -78,6 +75,12 @@ export class WebpackCompilerService extends EventEmitter implements IWebpackComp
7875
};
7976

8077
this.$logger.trace("Generated data from webpack message:", data);
78+
79+
// the hash of the compilation is the same as the previous one and there are only hot updates produced
80+
if (data.hasOnlyHotUpdateFiles && previousHash === message.hash) {
81+
return;
82+
}
83+
8184
if (data.files.length) {
8285
this.emit(WEBPACK_COMPILATION_COMPLETE, data);
8386
}

test/services/log-source-map-service.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ const testCases: IDictionary<Array<{ caseName: string, message: string, expected
5353
{
5454
caseName: "trace message",
5555
message: "JS: at module.exports.push../main-view-model.ts.HelloWorldModel.onTap (file:///data/data/org.nativescript.sourceMap/files/app/bundle.js:303:17)",
56-
expected: `JS: at module.exports.push../main-view-model.ts.HelloWorldModel.onTap file:///${toPlatformSep("src/main-view-model.ts")}:30:16\n`
56+
expected: `JS: at module.exports.push../main-view-model.ts.HelloWorldModel.onTap file: ${toPlatformSep("src/main-view-model.ts")}:30:16\n`
5757
},
5858
{
5959
caseName: "error message",
6060
message: "System.err: Frame: function:'module.exports.push../main-view-model.ts.HelloWorldModel.onTap', file:'file:///data/data/org.nativescript.sourceMap/files/app/bundle.js', line: 304, column: 15",
61-
expected: `System.err: Frame: function:'module.exports.push../main-view-model.ts.HelloWorldModel.onTap', file:'file:///${toPlatformSep("src/main-view-model.ts")}:31:14\n`
61+
expected: `System.err: Frame: function:'module.exports.push../main-view-model.ts.HelloWorldModel.onTap', file:'file: ${toPlatformSep("src/main-view-model.ts")}:31:14\n`
6262
},
6363
{
6464
caseName: "error message no match",
@@ -75,22 +75,22 @@ const testCases: IDictionary<Array<{ caseName: string, message: string, expected
7575
{
7676
caseName: "console message",
7777
message: "CONSOLE LOG file:///app/bundle.js:294:20: Test.",
78-
expected: `CONSOLE LOG file:///${toPlatformSep("src/main-view-model.ts")}:29:20 Test.\n`
78+
expected: `CONSOLE LOG file: ${toPlatformSep("src/main-view-model.ts")}:29:20 Test.\n`
7979
},
8080
{
8181
caseName: "trace message",
8282
message: "CONSOLE TRACE file:///app/bundle.js:295:22: Test",
83-
expected: `CONSOLE TRACE file:///${toPlatformSep("src/main-view-model.ts")}:30:22 Test\n`
83+
expected: `CONSOLE TRACE file: ${toPlatformSep("src/main-view-model.ts")}:30:22 Test\n`
8484
},
8585
{
8686
caseName: "error message",
8787
message: "file:///app/bundle.js:296:32: JS ERROR Error: Test",
88-
expected: `file:///${toPlatformSep("src/main-view-model.ts")}:31:31 JS ERROR Error: Test\n`
88+
expected: `file: ${toPlatformSep("src/main-view-model.ts")}:31:31 JS ERROR Error: Test\n`
8989
},
9090
{
9191
caseName: "error stack tracew",
9292
message: "onTap@file:///app/bundle.js:296:32",
93-
expected: `onTap@file:///${toPlatformSep("src/main-view-model.ts")}:31:31\n`
93+
expected: `onTap@file: ${toPlatformSep("src/main-view-model.ts")}:31:31\n`
9494
},
9595
{
9696
caseName: "error message no match",
@@ -101,7 +101,7 @@ const testCases: IDictionary<Array<{ caseName: string, message: string, expected
101101
caseName: "error stack trace (new runtime)",
102102
runtimeVersion: "6.1.0",
103103
message: "onTap(file:///app/bundle.js:296:22)",
104-
expected: `onTap(file:///${toPlatformSep("src/main-view-model.ts")}:31:18)\n`
104+
expected: `onTap(file: ${toPlatformSep("src/main-view-model.ts")}:31:18)\n`
105105
},
106106
]
107107
};

0 commit comments

Comments
 (0)