nativescript
Version:
Command-line interface for building NativeScript projects
240 lines • 12.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LogSourceMapService = void 0;
const path = require("path");
const util = require("util");
const _ = require("lodash");
const sourcemap = require("source-map");
const sourceMapConverter = require("convert-source-map");
const semver = require("semver");
const helpers_1 = require("../common/helpers");
const constants_1 = require("../constants");
const yok_1 = require("../common/yok");
class LogSourceMapService {
get $platformsDataService() {
return this.$injector.resolve("platformsDataService");
}
constructor($fs, $projectDataService, $injector, $options, $devicePlatformsConstants, $logger) {
this.$fs = $fs;
this.$projectDataService = $projectDataService;
this.$injector = $injector;
this.$options = $options;
this.$devicePlatformsConstants = $devicePlatformsConstants;
this.$logger = $logger;
this.cache = {};
this.originalFilesLocationCache = {};
this.getProjectData = _.memoize(this.$projectDataService.getProjectData.bind(this.$projectDataService));
this.getRuntimeVersion = _.memoize(this.getRuntimeVersionCore, (...args) => args.join(LogSourceMapService.MEMOIZE_FUNCTION_RANDOM_KEY_FOR_JOIN));
}
async setSourceMapConsumerForFile(filePath) {
try {
if (!this.$fs.getFsStats(filePath).isDirectory()) {
const mapFile = filePath + ".map";
let sourceMapRaw;
// Skip files bigger than 50MB
if (this.$fs.getFileSize(filePath) > 50 * 1000 * 1000) {
this.$logger.trace(`Skipping source map for file ${filePath} because it is too big (> 50MB).`);
return;
}
const source = this.$fs.readText(filePath);
if (this.$fs.exists(mapFile)) {
sourceMapRaw = sourceMapConverter.fromMapFileSource(source, (filename) => {
return this.$fs.readText(path.join(path.dirname(filePath), filename));
});
}
else {
sourceMapRaw = sourceMapConverter.fromSource(source);
}
let smc = null;
if (sourceMapRaw && sourceMapRaw.sourcemap) {
const sourceMap = sourceMapRaw.sourcemap;
smc = await sourcemap.SourceMapConsumer.with(sourceMap, null, (c) => {
return c;
});
}
this.cache[filePath] = smc;
}
}
catch (err) {
this.$logger.trace(`Unable to set sourceMapConsumer for file ${filePath}. Error is: ${err}`);
}
}
replaceWithOriginalFileLocations(platform, messageData, loggingOptions) {
if (!messageData || !loggingOptions || !loggingOptions.projectDir) {
return messageData;
}
const projectData = this.getProjectData(loggingOptions.projectDir);
const lines = messageData.split("\n");
const isAndroid = platform.toLowerCase() ===
this.$devicePlatformsConstants.Android.toLowerCase();
const parserFunction = isAndroid
? this.parseAndroidLog.bind(this, projectData)
: this.parseIosLog.bind(this);
let outputData = "";
lines.forEach((rawLine) => {
const parsedLine = parserFunction(rawLine);
const originalLocation = this.getOriginalFileLocation(platform, parsedLine, projectData);
if (originalLocation && originalLocation.sourceFile) {
const runtimeVersion = this.getRuntimeVersion(loggingOptions.projectDir, platform);
const { sourceFile, line, column } = originalLocation;
if (semver.valid(runtimeVersion) &&
semver.gte(semver.coerce(runtimeVersion), "6.1.0")) {
const lastIndexOfFile = rawLine.lastIndexOf(LogSourceMapService.FILE_PREFIX);
const firstPart = rawLine.substr(0, lastIndexOfFile);
outputData +=
firstPart +
rawLine
.substring(lastIndexOfFile)
.replace(/file:\/\/\/.+?:\d+:\d+/, `${LogSourceMapService.FILE_PREFIX_REPLACEMENT}${sourceFile}:${line}:${column}`) +
"\n";
}
else {
outputData = `${outputData}${parsedLine.messagePrefix}${LogSourceMapService.FILE_PREFIX_REPLACEMENT}${sourceFile}:${line}:${column}${parsedLine.messageSuffix}\n`;
}
}
else if (rawLine !== "") {
outputData = `${outputData}${rawLine}\n`;
}
});
return outputData;
}
getRuntimeVersionCore(projectDir, platform) {
let runtimeVersion = null;
try {
const projectData = this.getProjectData(projectDir);
const platformData = this.$platformsDataService.getPlatformData(platform, projectData);
const runtimeVersionData = this.$projectDataService.getRuntimePackage(projectData.projectDir, platformData.platformNameLowerCase);
runtimeVersion = runtimeVersionData && runtimeVersionData.version;
}
catch (err) {
this.$logger.trace(`Unable to get runtime version for project directory: ${projectDir} and platform ${platform}. Error is: `, err);
}
return runtimeVersion;
}
getOriginalFileLocation(platform, parsedLine, projectData) {
const fileLocation = path.join(this.getFilesLocation(platform, projectData), this.$options.hostProjectModuleName);
if (parsedLine && parsedLine.filePath) {
const sourceMapFile = path.join(fileLocation, parsedLine.filePath);
const smc = this.cache[sourceMapFile];
if (smc) {
const originalPosition = smc.originalPositionFor({
line: parsedLine.line,
column: parsedLine.column,
});
let sourceFile = originalPosition.source &&
originalPosition.source.replace("webpack:///", "");
if (sourceFile) {
const appPath = projectData.getAppDirectoryRelativePath();
if (!_.startsWith(sourceFile, constants_1.NODE_MODULES_FOLDER_NAME) &&
!_.startsWith(sourceFile, appPath + "/")) {
sourceFile = path.join(appPath, sourceFile);
}
sourceFile = (0, helpers_1.stringReplaceAll)(sourceFile, "/", path.sep);
if (!this.originalFilesLocationCache[sourceFile]) {
const { dir, ext, name } = path.parse(sourceFile);
const platformSpecificName = `${name}.${platform.toLowerCase()}`;
const platformSpecificFile = path.format({
dir,
ext,
name: platformSpecificName,
});
if (this.$fs.exists(path.join(projectData.projectDir, platformSpecificFile))) {
this.originalFilesLocationCache[sourceFile] =
platformSpecificFile;
}
else {
this.originalFilesLocationCache[sourceFile] = sourceFile;
}
}
return {
sourceFile: this.originalFilesLocationCache[sourceFile],
line: originalPosition.line,
column: originalPosition.column,
};
}
}
}
}
parseAndroidLog(projectData, rawMessage) {
// "JS: at module.exports.push../main-view-model.ts.HelloWorldModel.onTap (file:///data/data/org.nativescript.sourceMap/files/app/bundle.js:303:17)"
// "System.err: File: "file:///data/data/org.nativescript.sourceMap/files/app/bundle.js, line: 304, column: 8"
const fileIndex = rawMessage.lastIndexOf(LogSourceMapService.FILE_PREFIX);
const deviceProjectPath = util.format(constants_1.ANDROID_DEVICE_APP_ROOT_TEMPLATE, projectData.projectIdentifiers.android);
let separator = ",";
let messageSuffix = "";
let parts, filePath, line, column, messagePrefix;
if (fileIndex >= 0) {
const fileSubstring = rawMessage.substring(fileIndex + LogSourceMapService.FILE_PREFIX.length);
//"data/data/org.nativescript.sourceMap/files/app/bundle.js, line: 304, column: 8"
parts = fileSubstring.split(separator);
if (parts.length >= 3) {
// "data/data/org.nativescript.sourceMap/files/app/bundle.js"
parts[0] = parts[0].replace("'", "");
// " line: 304"
parts[1] = parts[1].replace(" line: ", "");
// " column: 8"
parts[2] = parts[2].replace(" column: ", "");
}
else {
// "data/data/org.nativescript.sourceMap/files/app/bundle.js:303:17)"
separator = ":";
parts = fileSubstring.split(separator);
}
if (parts.length >= 3) {
// "/data/data/org.nativescript.sourceMap/files/app/"
const devicePath = `${deviceProjectPath}/${constants_1.APP_FOLDER_NAME}/`;
// "bundle.js"
filePath = path.relative(devicePath, `${"/"}${parts[0]}`);
line = parseInt(parts[1]);
column = parseInt(parts[2]);
messagePrefix = rawMessage.substring(0, fileIndex);
for (let i = 3; i < parts.length; i++) {
messageSuffix += `${parts[i]}${i === parts.length - 1 ? "" : separator}`;
}
// "JS: at module.exports.push../main-view-model.ts.HelloWorldModel.onTap ("
messagePrefix = _.trimEnd(messagePrefix, "(");
}
}
return { filePath, line, column, messagePrefix, messageSuffix };
}
parseIosLog(rawMessage) {
// "CONSOLE INFO file:///app/vendor.js:131:36: HMR: Hot Module Replacement Enabled. Waiting for signal."
const fileIndex = rawMessage.lastIndexOf(LogSourceMapService.FILE_PREFIX);
let messageSuffix = "";
let parts, filePath, line, column, messagePrefix;
if (fileIndex >= 0) {
// "app/vendor.js:131:36: HMR: Hot Module Replacement Enabled. Waiting for signal."
const fileSubstring = rawMessage.substring(fileIndex + LogSourceMapService.FILE_PREFIX.length);
parts = fileSubstring.split(":");
if (parts && parts.length >= 3) {
filePath = parts[0];
// "app/vendor.js"
if (_.startsWith(filePath, constants_1.APP_FOLDER_NAME)) {
filePath = path.relative(constants_1.APP_FOLDER_NAME, parts[0]);
}
line = parseInt(parts[1]);
column = parseInt(parts[2]);
messagePrefix = rawMessage.substring(0, fileIndex);
for (let i = 3; i < parts.length; i++) {
messageSuffix += `${parts[i]}${i === parts.length - 1 ? "" : ":"}`;
}
}
}
return { filePath, line, column, messagePrefix, messageSuffix };
}
getFilesLocation(platform, projectData) {
try {
const platformsData = this.$platformsDataService.getPlatformData(platform.toLowerCase(), projectData);
return platformsData.appDestinationDirectoryPath;
}
catch (err) {
return "";
}
}
}
exports.LogSourceMapService = LogSourceMapService;
LogSourceMapService.FILE_PREFIX = "file:///";
LogSourceMapService.FILE_PREFIX_REPLACEMENT = "file: ";
LogSourceMapService.MEMOIZE_FUNCTION_RANDOM_KEY_FOR_JOIN = "__some_random_value__";
yok_1.injector.register("logSourceMapService", LogSourceMapService);
//# sourceMappingURL=log-source-map-service.js.map