UNPKG

nativescript

Version:

Command-line interface for building NativeScript projects

240 lines • 12.3 kB
"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