From 8ee85d6ffb6af70da544bf821d2bcbb9404cb4b9 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 6 Feb 2023 14:38:30 +0000 Subject: [PATCH 01/12] feat: Add debug ids --- packages/browser/.npmignore | 4 + packages/browser/package.json | 6 +- packages/browser/scripts/source-map-cli.js | 121 +++++++++++++++++++++ packages/browser/src/eventbuilder.ts | 20 ++++ packages/types/src/stackframe.ts | 1 + packages/utils/src/worldwide.ts | 1 + 6 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 packages/browser/.npmignore create mode 100644 packages/browser/scripts/source-map-cli.js diff --git a/packages/browser/.npmignore b/packages/browser/.npmignore new file mode 100644 index 000000000000..e1bb7e5136bd --- /dev/null +++ b/packages/browser/.npmignore @@ -0,0 +1,4 @@ +# The paths in this file are specified so that they align with the file structure in `./build` after this file is copied +# into it by the prepack script `scripts/prepack.ts`. + +!/scripts/**/* diff --git a/packages/browser/package.json b/packages/browser/package.json index cbbc58d458da..b3ba76c8afb0 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -12,6 +12,9 @@ "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", "types": "build/npm/types/index.d.ts", + "bin": { + "sentry-sourcemaps": "scripts/source-map-cli.js" + }, "publishConfig": { "access": "public" }, @@ -20,7 +23,8 @@ "@sentry/replay": "7.36.0", "@sentry/types": "7.36.0", "@sentry/utils": "7.36.0", - "tslib": "^1.9.3" + "tslib": "^1.9.3", + "yargs": "^17.6.2" }, "devDependencies": { "@types/md5": "2.1.33", diff --git a/packages/browser/scripts/source-map-cli.js b/packages/browser/scripts/source-map-cli.js new file mode 100644 index 000000000000..5243c547c9ce --- /dev/null +++ b/packages/browser/scripts/source-map-cli.js @@ -0,0 +1,121 @@ +#!/usr/bin/env node + +const util = require('util'); +const path = require('path'); +const fs = require('fs'); + +const yargs = require('yargs/yargs'); +const { hideBin } = require('yargs/helpers'); + +const lstat = util.promisify(fs.lstat); +const readdir = util.promisify(fs.readdir); +const readFile = util.promisify(fs.readFile); +const appendFile = util.promisify(fs.appendFile); +const writeFile = util.promisify(fs.writeFile); +const exists = util.promisify(fs.exists); + +const INJECTOR_CODE = + '\n!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="__DEBUG_ID__")}catch(e){}}()'; + +const DEBUG_COMMENT_CODE = '\n//# sentryDebugId=__DEBUG_ID__'; + +yargs(hideBin(process.argv)) + .command( + 'inject [targets...]', + 'correlates source maps and source files by injecting additional information', + yargs => { + return yargs.positional('targets', { + describe: 'folders and/or files to scan for injection targets', + default: ['.'], + }); + }, + async ({ targets }) => { + const normalizedAbsolutePaths = targets.map(target => path.resolve(target)); + + normalizedAbsolutePaths.forEach(normalizedAbsolutePath => { + if (!fs.existsSync(normalizedAbsolutePath)) { + // eslint-disable-next-line no-console + console.error(`Path ${normalizedAbsolutePath} not found.`); + process.exit(1); + } + }); + + const fileBuckets = await Promise.all( + normalizedAbsolutePaths.map(normalizedAbsolutePath => { + return getFilesFromAbsolutePath(normalizedAbsolutePath); + }), + ); + + const filePaths = [...new Set([].concat(...fileBuckets))] // flatten and dedupe + .filter(filePath => filePath.endsWith('.js')); + + await Promise.all( + filePaths.map(async filePath => { + const jsFileData = await readFile(filePath, 'utf8'); + + const sourceMappingURLMatch = jsFileData.match(/^\/\/# sourceMappingURL=(.*)$/m); + + if (jsFileData.includes('//# sentryDebugId=')) { + // eslint-disable-next-line no-console + console.log(`Warning: File "${filePath}" was already processed. Will skip processing for this file.`); + return; + } + + if (!sourceMappingURLMatch) { + // eslint-disable-next-line no-console + console.log( + `Warning: File "${filePath}" doesn't have a "sourceMappingURL" comment. Will skip processing for this file.`, + ); + return; + } + + const normalizedSourceMapURL = path.normalize(sourceMappingURLMatch[1]); + const sourceMapPath = path.join(path.dirname(filePath), normalizedSourceMapURL); + + if (!(await exists(sourceMapPath))) { + // eslint-disable-next-line no-console + console.log( + `Warning: "sourceMappingURL" comment of file "${filePath}" doesn't point to an existing file. Will skip processing for this file.`, + ); + return; + } + + const sourceMapContents = await readFile(sourceMapPath, 'utf8'); + + let sourceMap; + try { + sourceMap = JSON.parse(sourceMapContents); + } catch (e) { + // eslint-disable-next-line no-console + console.log(`Warning: Failed to parse source map ${sourceMapPath}. Will skip processing for this file.`); + return; + } + + const debugID = Math.floor(Math.random() * 100000000000); + const codeToInject = `${INJECTOR_CODE}${DEBUG_COMMENT_CODE}`.replace(/__DEBUG_ID__/g, debugID); + + sourceMap.debugID = debugID; + + return await Promise.all([ + writeFile(sourceMapPath, JSON.stringify(sourceMap), { encoding: 'utf8', flag: 'w' }), + appendFile(filePath, codeToInject), + ]); + }), + ); + }, + ) + .parse(); + +async function getFilesFromAbsolutePath(absolutePath) { + const stats = await lstat(absolutePath); + + if (stats.isFile()) { + return [absolutePath]; + } else if (stats.isDirectory) { + const files = await readdir(absolutePath); + const results = await Promise.all(files.map(file => getFilesFromAbsolutePath(path.join(absolutePath, file)))); + return [].concat(...results); // flatten + } else { + return []; + } +} diff --git a/packages/browser/src/eventbuilder.ts b/packages/browser/src/eventbuilder.ts index 4db58b9b7b43..b3be54b99131 100644 --- a/packages/browser/src/eventbuilder.ts +++ b/packages/browser/src/eventbuilder.ts @@ -4,6 +4,7 @@ import { addExceptionMechanism, addExceptionTypeValue, extractExceptionKeysForMessage, + GLOBAL_OBJ, isDOMError, isDOMException, isError, @@ -155,6 +156,25 @@ export function eventFromException( const syntheticException = (hint && hint.syntheticException) || undefined; const event = eventFromUnknownInput(stackParser, exception, syntheticException, attachStacktrace); addExceptionMechanism(event); // defaults to { type: 'generic', handled: true } + + if (GLOBAL_OBJ._sentryDebugIds) { + event?.exception?.values?.forEach(exception => { + exception.stacktrace?.frames?.forEach(eventFrame => { + let debugId: string | undefined; + Object.keys(GLOBAL_OBJ._sentryDebugIds!).forEach(sentryDebugIdStack => { + const stackFrames = stackParser(sentryDebugIdStack); + if (stackFrames[1] && stackFrames[1].filename === eventFrame.filename) { + debugId = GLOBAL_OBJ._sentryDebugIds![sentryDebugIdStack]; + } + }); + + if (debugId) { + eventFrame.debug_id = debugId; + } + }); + }); + } + event.level = 'error'; if (hint && hint.event_id) { event.event_id = hint.event_id; diff --git a/packages/types/src/stackframe.ts b/packages/types/src/stackframe.ts index 7b8fa40a8a23..7a8058a1792c 100644 --- a/packages/types/src/stackframe.ts +++ b/packages/types/src/stackframe.ts @@ -14,4 +14,5 @@ export interface StackFrame { instruction_addr?: string; addr_mode?: string; vars?: { [key: string]: any }; + debug_id?: string; } diff --git a/packages/utils/src/worldwide.ts b/packages/utils/src/worldwide.ts index 4cc141cc3d65..028eba2ac774 100644 --- a/packages/utils/src/worldwide.ts +++ b/packages/utils/src/worldwide.ts @@ -29,6 +29,7 @@ export interface InternalGlobal { id?: string; }; SENTRY_SDK_SOURCE?: SdkSource; + _sentryDebugIds?: Record; __SENTRY__: { globalEventProcessors: any; hub: any; From 9cda97c04c399955a7a769d03a25e34ad2bbff72 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 6 Feb 2023 16:16:49 +0000 Subject: [PATCH 02/12] Use uuid --- packages/browser/package.json | 3 ++- packages/browser/scripts/source-map-cli.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/browser/package.json b/packages/browser/package.json index b3ba76c8afb0..4f3429160782 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -24,7 +24,8 @@ "@sentry/types": "7.36.0", "@sentry/utils": "7.36.0", "tslib": "^1.9.3", - "yargs": "^17.6.2" + "yargs": "^17.6.2", + "uuid": "^9.0.0" }, "devDependencies": { "@types/md5": "2.1.33", diff --git a/packages/browser/scripts/source-map-cli.js b/packages/browser/scripts/source-map-cli.js index 5243c547c9ce..d04a88941eed 100644 --- a/packages/browser/scripts/source-map-cli.js +++ b/packages/browser/scripts/source-map-cli.js @@ -4,6 +4,7 @@ const util = require('util'); const path = require('path'); const fs = require('fs'); +const { v4: uuidv4 } = require('uuid'); const yargs = require('yargs/yargs'); const { hideBin } = require('yargs/helpers'); @@ -91,7 +92,7 @@ yargs(hideBin(process.argv)) return; } - const debugID = Math.floor(Math.random() * 100000000000); + const debugID = uuidv4(); const codeToInject = `${INJECTOR_CODE}${DEBUG_COMMENT_CODE}`.replace(/__DEBUG_ID__/g, debugID); sourceMap.debugID = debugID; From 0274ade8f11bbb98d7970c4a86908574c3f0d485 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 6 Feb 2023 18:02:45 +0000 Subject: [PATCH 03/12] Cache and centralize --- packages/browser/scripts/source-map-cli.js | 8 ++--- packages/browser/src/eventbuilder.ts | 20 ------------- packages/utils/src/stacktrace.ts | 35 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/packages/browser/scripts/source-map-cli.js b/packages/browser/scripts/source-map-cli.js index d04a88941eed..641329582b10 100644 --- a/packages/browser/scripts/source-map-cli.js +++ b/packages/browser/scripts/source-map-cli.js @@ -16,9 +16,9 @@ const writeFile = util.promisify(fs.writeFile); const exists = util.promisify(fs.exists); const INJECTOR_CODE = - '\n!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="__DEBUG_ID__")}catch(e){}}()'; + '\n!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="__SENTRY_DEBUG_ID__")}catch(e){}}()'; -const DEBUG_COMMENT_CODE = '\n//# sentryDebugId=__DEBUG_ID__'; +const DEBUG_ID_COMMENT = '\n//# sentryDebugId=__SENTRY_DEBUG_ID__'; yargs(hideBin(process.argv)) .command( @@ -93,12 +93,12 @@ yargs(hideBin(process.argv)) } const debugID = uuidv4(); - const codeToInject = `${INJECTOR_CODE}${DEBUG_COMMENT_CODE}`.replace(/__DEBUG_ID__/g, debugID); + const codeToInject = `${INJECTOR_CODE}${DEBUG_ID_COMMENT}`.replace(/__SENTRY_DEBUG_ID__/g, debugID); sourceMap.debugID = debugID; return await Promise.all([ - writeFile(sourceMapPath, JSON.stringify(sourceMap), { encoding: 'utf8', flag: 'w' }), + writeFile(sourceMapPath, JSON.stringify(sourceMap), { encoding: 'utf8', flag: 'w' }), // w flag is for overriding existing file appendFile(filePath, codeToInject), ]); }), diff --git a/packages/browser/src/eventbuilder.ts b/packages/browser/src/eventbuilder.ts index b3be54b99131..4db58b9b7b43 100644 --- a/packages/browser/src/eventbuilder.ts +++ b/packages/browser/src/eventbuilder.ts @@ -4,7 +4,6 @@ import { addExceptionMechanism, addExceptionTypeValue, extractExceptionKeysForMessage, - GLOBAL_OBJ, isDOMError, isDOMException, isError, @@ -156,25 +155,6 @@ export function eventFromException( const syntheticException = (hint && hint.syntheticException) || undefined; const event = eventFromUnknownInput(stackParser, exception, syntheticException, attachStacktrace); addExceptionMechanism(event); // defaults to { type: 'generic', handled: true } - - if (GLOBAL_OBJ._sentryDebugIds) { - event?.exception?.values?.forEach(exception => { - exception.stacktrace?.frames?.forEach(eventFrame => { - let debugId: string | undefined; - Object.keys(GLOBAL_OBJ._sentryDebugIds!).forEach(sentryDebugIdStack => { - const stackFrames = stackParser(sentryDebugIdStack); - if (stackFrames[1] && stackFrames[1].filename === eventFrame.filename) { - debugId = GLOBAL_OBJ._sentryDebugIds![sentryDebugIdStack]; - } - }); - - if (debugId) { - eventFrame.debug_id = debugId; - } - }); - }); - } - event.level = 'error'; if (hint && hint.event_id) { event.event_id = hint.event_id; diff --git a/packages/utils/src/stacktrace.ts b/packages/utils/src/stacktrace.ts index 3f9b5cf475b4..2cee22d06007 100644 --- a/packages/utils/src/stacktrace.ts +++ b/packages/utils/src/stacktrace.ts @@ -1,7 +1,14 @@ import type { StackFrame, StackLineParser, StackLineParserFn, StackParser } from '@sentry/types'; +import { GLOBAL_OBJ } from './worldwide'; + const STACKTRACE_LIMIT = 50; +type DebugIdFilename = string; +type DebugId = string; + +const debugIdParserCache = new Map>(); + /** * Creates a stack parser with the supplied line parsers * @@ -15,6 +22,26 @@ export function createStackParser(...parsers: StackLineParser[]): StackParser { return (stack: string, skipFirst: number = 0): StackFrame[] => { const frames: StackFrame[] = []; + for (const parser of sortedParsers) { + let debugIdCache = debugIdParserCache.get(parser); + if (!debugIdCache) { + debugIdCache = new Map(); + debugIdParserCache.set(parser, debugIdCache); + } + + if (GLOBAL_OBJ._sentryDebugIds) { + Object.keys(GLOBAL_OBJ._sentryDebugIds).forEach(debugIdStackTrace => { + debugIdStackTrace.split('\n').forEach(line => { + const frame = parser(line); + if (frame && frame.filename) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + debugIdCache!.set(frame.filename, GLOBAL_OBJ._sentryDebugIds![debugIdStackTrace]); + } + }); + }); + } + } + for (const line of stack.split('\n').slice(skipFirst)) { // Ignore lines over 1kb as they are unlikely to be stack frames. // Many of the regular expressions use backtracking which results in run time that increases exponentially with @@ -32,6 +59,14 @@ export function createStackParser(...parsers: StackLineParser[]): StackParser { const frame = parser(cleanedLine); if (frame) { + const debugIdCache = debugIdParserCache.get(parser); + if (debugIdCache && frame.filename) { + const cachedDebugId = debugIdCache.get(frame.filename); + if (cachedDebugId) { + frame.debug_id = cachedDebugId; + } + } + frames.push(frame); break; } From a1eef2d082a6f164d6426d45c0a15199848dc99b Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Mon, 6 Feb 2023 18:05:55 +0000 Subject: [PATCH 04/12] unstale yarn lock --- packages/browser/scripts/source-map-cli.js | 0 yarn.lock | 5 +++++ 2 files changed, 5 insertions(+) mode change 100644 => 100755 packages/browser/scripts/source-map-cli.js diff --git a/packages/browser/scripts/source-map-cli.js b/packages/browser/scripts/source-map-cli.js old mode 100644 new mode 100755 diff --git a/yarn.lock b/yarn.lock index b49a1981ca0e..d73033376de5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24456,6 +24456,11 @@ uuid@^8.0.0, uuid@^8.3.1, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" From 0cd21c61ed81ded2147d879527cbb28fd2def70e Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 7 Feb 2023 10:53:33 +0000 Subject: [PATCH 05/12] . --- packages/browser/.npmignore | 2 +- packages/browser/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/browser/.npmignore b/packages/browser/.npmignore index e1bb7e5136bd..4fd36d207f23 100644 --- a/packages/browser/.npmignore +++ b/packages/browser/.npmignore @@ -1,4 +1,4 @@ # The paths in this file are specified so that they align with the file structure in `./build` after this file is copied # into it by the prepack script `scripts/prepack.ts`. -!/scripts/**/* +!/scripts/source-map-cli.js diff --git a/packages/browser/package.json b/packages/browser/package.json index 4f3429160782..09cb0f46f80c 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -13,7 +13,7 @@ "module": "build/npm/esm/index.js", "types": "build/npm/types/index.d.ts", "bin": { - "sentry-sourcemaps": "scripts/source-map-cli.js" + "experimental-sentry-sourcemaps": "scripts/source-map-cli.js" }, "publishConfig": { "access": "public" From 98e75a923a4ec8015110d87239f25dfe2ca29f3a Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 7 Feb 2023 12:33:05 +0000 Subject: [PATCH 06/12] Add test --- packages/utils/test/stacktrace.test.ts | 41 +++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/utils/test/stacktrace.test.ts b/packages/utils/test/stacktrace.test.ts index 61b44de34366..b05529088d00 100644 --- a/packages/utils/test/stacktrace.test.ts +++ b/packages/utils/test/stacktrace.test.ts @@ -1,4 +1,5 @@ -import { stripSentryFramesAndReverse } from '../src/stacktrace'; +import { stripSentryFramesAndReverse, createStackParser } from '../src/stacktrace'; +import { GLOBAL_OBJ } from '../src/worldwide'; describe('Stacktrace', () => { describe('stripSentryFramesAndReverse()', () => { @@ -68,3 +69,41 @@ describe('Stacktrace', () => { }); }); }); + +describe('Stack parsers created with createStackParser', () => { + afterEach(() => { + GLOBAL_OBJ._sentryDebugIds = undefined; + }); + + it('put debug ids onto individual frames', () => { + GLOBAL_OBJ._sentryDebugIds = { + 'filename1.js\nfilename1.js': 'aaaaaaaa-aaaa-4aaa-aaaa-aaaaaaaaaa', + 'filename2.js\nfilename2.js': 'bbbbbbbb-bbbb-4bbb-bbbb-bbbbbbbbbb', + }; + + const fakeErrorStack = 'filename1.js\nfilename2.js\nfilename1.js\nfilename3.js'; + const stackParser = createStackParser([0, line => ({ filename: line })]); + + const result = stackParser(fakeErrorStack); + + expect(result[0]).toStrictEqual({ filename: 'filename3.js', function: '?' }); + + expect(result[1]).toStrictEqual({ + filename: 'filename1.js', + function: '?', + debug_id: 'aaaaaaaa-aaaa-4aaa-aaaa-aaaaaaaaaa', + }); + + expect(result[2]).toStrictEqual({ + filename: 'filename2.js', + function: '?', + debug_id: 'bbbbbbbb-bbbb-4bbb-bbbb-bbbbbbbbbb', + }); + + expect(result[3]).toStrictEqual({ + filename: 'filename1.js', + function: '?', + debug_id: 'aaaaaaaa-aaaa-4aaa-aaaa-aaaaaaaaaa', + }); + }); +}); From 5f16fb0847063d5f78e8e0987b7ffbb4eec9438e Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 7 Feb 2023 12:33:17 +0000 Subject: [PATCH 07/12] fix lint --- packages/utils/test/stacktrace.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/test/stacktrace.test.ts b/packages/utils/test/stacktrace.test.ts index b05529088d00..a2438c687bc4 100644 --- a/packages/utils/test/stacktrace.test.ts +++ b/packages/utils/test/stacktrace.test.ts @@ -1,4 +1,4 @@ -import { stripSentryFramesAndReverse, createStackParser } from '../src/stacktrace'; +import { createStackParser, stripSentryFramesAndReverse } from '../src/stacktrace'; import { GLOBAL_OBJ } from '../src/worldwide'; describe('Stacktrace', () => { From c0f584b000281e31289dca53638c8328e611dae2 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 7 Feb 2023 12:50:18 +0000 Subject: [PATCH 08/12] Make command required --- packages/browser/scripts/source-map-cli.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/browser/scripts/source-map-cli.js b/packages/browser/scripts/source-map-cli.js index 641329582b10..a6bab7970606 100755 --- a/packages/browser/scripts/source-map-cli.js +++ b/packages/browser/scripts/source-map-cli.js @@ -105,6 +105,8 @@ yargs(hideBin(process.argv)) ); }, ) + .strictCommands() + .demandCommand(1) .parse(); async function getFilesFromAbsolutePath(absolutePath) { From f97fb00c7533fbb1e0f69e839fb5fc10409e64c0 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 7 Feb 2023 13:27:12 +0000 Subject: [PATCH 09/12] Preserve script in tarbal --- packages/browser/.npmignore | 2 +- packages/browser/scripts/prepack.ts | 34 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 packages/browser/scripts/prepack.ts diff --git a/packages/browser/.npmignore b/packages/browser/.npmignore index 4fd36d207f23..e1bb7e5136bd 100644 --- a/packages/browser/.npmignore +++ b/packages/browser/.npmignore @@ -1,4 +1,4 @@ # The paths in this file are specified so that they align with the file structure in `./build` after this file is copied # into it by the prepack script `scripts/prepack.ts`. -!/scripts/source-map-cli.js +!/scripts/**/* diff --git a/packages/browser/scripts/prepack.ts b/packages/browser/scripts/prepack.ts new file mode 100644 index 000000000000..9d2cb4a86cb4 --- /dev/null +++ b/packages/browser/scripts/prepack.ts @@ -0,0 +1,34 @@ +/* eslint-disable no-console */ + +import * as fs from 'fs'; +import * as path from 'path'; + +const PACKAGE_ASSETS = ['scripts/source-map-cli.js']; + +export function prepack(buildDir: string): boolean { + // copy package-specific assets to build dir + return PACKAGE_ASSETS.every(asset => { + const assetPath = path.resolve(asset); + const destinationPath = path.resolve(buildDir, asset); + try { + if (!fs.existsSync(assetPath)) { + console.error(`\nERROR: Asset '${asset}' does not exist.`); + return false; + } + const scriptsDir = path.resolve(buildDir, 'scripts'); + if (!fs.existsSync(scriptsDir)) { + console.log('Creating missing directory', scriptsDir); + fs.mkdirSync(scriptsDir); + } + console.log(`Copying ${path.basename(asset)} to ${path.relative('../..', destinationPath)}.`); + fs.copyFileSync(assetPath, destinationPath); + } catch (error) { + console.error( + `\nERROR: Error while copying ${path.basename(asset)} to ${path.relative('../..', destinationPath)}:\n`, + error, + ); + return false; + } + return true; + }); +} From 69b4c15256cc52e8c00fdacbe7ad93e36e837820 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 7 Feb 2023 13:50:47 +0000 Subject: [PATCH 10/12] Remove cli --- packages/browser/.npmignore | 4 - packages/browser/package.json | 7 +- packages/browser/scripts/prepack.ts | 34 ------ packages/browser/scripts/source-map-cli.js | 124 --------------------- 4 files changed, 1 insertion(+), 168 deletions(-) delete mode 100644 packages/browser/.npmignore delete mode 100644 packages/browser/scripts/prepack.ts delete mode 100755 packages/browser/scripts/source-map-cli.js diff --git a/packages/browser/.npmignore b/packages/browser/.npmignore deleted file mode 100644 index e1bb7e5136bd..000000000000 --- a/packages/browser/.npmignore +++ /dev/null @@ -1,4 +0,0 @@ -# The paths in this file are specified so that they align with the file structure in `./build` after this file is copied -# into it by the prepack script `scripts/prepack.ts`. - -!/scripts/**/* diff --git a/packages/browser/package.json b/packages/browser/package.json index 09cb0f46f80c..cbbc58d458da 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -12,9 +12,6 @@ "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", "types": "build/npm/types/index.d.ts", - "bin": { - "experimental-sentry-sourcemaps": "scripts/source-map-cli.js" - }, "publishConfig": { "access": "public" }, @@ -23,9 +20,7 @@ "@sentry/replay": "7.36.0", "@sentry/types": "7.36.0", "@sentry/utils": "7.36.0", - "tslib": "^1.9.3", - "yargs": "^17.6.2", - "uuid": "^9.0.0" + "tslib": "^1.9.3" }, "devDependencies": { "@types/md5": "2.1.33", diff --git a/packages/browser/scripts/prepack.ts b/packages/browser/scripts/prepack.ts deleted file mode 100644 index 9d2cb4a86cb4..000000000000 --- a/packages/browser/scripts/prepack.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* eslint-disable no-console */ - -import * as fs from 'fs'; -import * as path from 'path'; - -const PACKAGE_ASSETS = ['scripts/source-map-cli.js']; - -export function prepack(buildDir: string): boolean { - // copy package-specific assets to build dir - return PACKAGE_ASSETS.every(asset => { - const assetPath = path.resolve(asset); - const destinationPath = path.resolve(buildDir, asset); - try { - if (!fs.existsSync(assetPath)) { - console.error(`\nERROR: Asset '${asset}' does not exist.`); - return false; - } - const scriptsDir = path.resolve(buildDir, 'scripts'); - if (!fs.existsSync(scriptsDir)) { - console.log('Creating missing directory', scriptsDir); - fs.mkdirSync(scriptsDir); - } - console.log(`Copying ${path.basename(asset)} to ${path.relative('../..', destinationPath)}.`); - fs.copyFileSync(assetPath, destinationPath); - } catch (error) { - console.error( - `\nERROR: Error while copying ${path.basename(asset)} to ${path.relative('../..', destinationPath)}:\n`, - error, - ); - return false; - } - return true; - }); -} diff --git a/packages/browser/scripts/source-map-cli.js b/packages/browser/scripts/source-map-cli.js deleted file mode 100755 index a6bab7970606..000000000000 --- a/packages/browser/scripts/source-map-cli.js +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env node - -const util = require('util'); -const path = require('path'); -const fs = require('fs'); - -const { v4: uuidv4 } = require('uuid'); -const yargs = require('yargs/yargs'); -const { hideBin } = require('yargs/helpers'); - -const lstat = util.promisify(fs.lstat); -const readdir = util.promisify(fs.readdir); -const readFile = util.promisify(fs.readFile); -const appendFile = util.promisify(fs.appendFile); -const writeFile = util.promisify(fs.writeFile); -const exists = util.promisify(fs.exists); - -const INJECTOR_CODE = - '\n!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="__SENTRY_DEBUG_ID__")}catch(e){}}()'; - -const DEBUG_ID_COMMENT = '\n//# sentryDebugId=__SENTRY_DEBUG_ID__'; - -yargs(hideBin(process.argv)) - .command( - 'inject [targets...]', - 'correlates source maps and source files by injecting additional information', - yargs => { - return yargs.positional('targets', { - describe: 'folders and/or files to scan for injection targets', - default: ['.'], - }); - }, - async ({ targets }) => { - const normalizedAbsolutePaths = targets.map(target => path.resolve(target)); - - normalizedAbsolutePaths.forEach(normalizedAbsolutePath => { - if (!fs.existsSync(normalizedAbsolutePath)) { - // eslint-disable-next-line no-console - console.error(`Path ${normalizedAbsolutePath} not found.`); - process.exit(1); - } - }); - - const fileBuckets = await Promise.all( - normalizedAbsolutePaths.map(normalizedAbsolutePath => { - return getFilesFromAbsolutePath(normalizedAbsolutePath); - }), - ); - - const filePaths = [...new Set([].concat(...fileBuckets))] // flatten and dedupe - .filter(filePath => filePath.endsWith('.js')); - - await Promise.all( - filePaths.map(async filePath => { - const jsFileData = await readFile(filePath, 'utf8'); - - const sourceMappingURLMatch = jsFileData.match(/^\/\/# sourceMappingURL=(.*)$/m); - - if (jsFileData.includes('//# sentryDebugId=')) { - // eslint-disable-next-line no-console - console.log(`Warning: File "${filePath}" was already processed. Will skip processing for this file.`); - return; - } - - if (!sourceMappingURLMatch) { - // eslint-disable-next-line no-console - console.log( - `Warning: File "${filePath}" doesn't have a "sourceMappingURL" comment. Will skip processing for this file.`, - ); - return; - } - - const normalizedSourceMapURL = path.normalize(sourceMappingURLMatch[1]); - const sourceMapPath = path.join(path.dirname(filePath), normalizedSourceMapURL); - - if (!(await exists(sourceMapPath))) { - // eslint-disable-next-line no-console - console.log( - `Warning: "sourceMappingURL" comment of file "${filePath}" doesn't point to an existing file. Will skip processing for this file.`, - ); - return; - } - - const sourceMapContents = await readFile(sourceMapPath, 'utf8'); - - let sourceMap; - try { - sourceMap = JSON.parse(sourceMapContents); - } catch (e) { - // eslint-disable-next-line no-console - console.log(`Warning: Failed to parse source map ${sourceMapPath}. Will skip processing for this file.`); - return; - } - - const debugID = uuidv4(); - const codeToInject = `${INJECTOR_CODE}${DEBUG_ID_COMMENT}`.replace(/__SENTRY_DEBUG_ID__/g, debugID); - - sourceMap.debugID = debugID; - - return await Promise.all([ - writeFile(sourceMapPath, JSON.stringify(sourceMap), { encoding: 'utf8', flag: 'w' }), // w flag is for overriding existing file - appendFile(filePath, codeToInject), - ]); - }), - ); - }, - ) - .strictCommands() - .demandCommand(1) - .parse(); - -async function getFilesFromAbsolutePath(absolutePath) { - const stats = await lstat(absolutePath); - - if (stats.isFile()) { - return [absolutePath]; - } else if (stats.isDirectory) { - const files = await readdir(absolutePath); - const results = await Promise.all(files.map(file => getFilesFromAbsolutePath(path.join(absolutePath, file)))); - return [].concat(...results); // flatten - } else { - return []; - } -} From 5d99bd45513de5c09ad17fb42e21e5185eeb52c0 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 7 Feb 2023 13:52:37 +0000 Subject: [PATCH 11/12] Undo yarn lock change --- yarn.lock | 5 ----- 1 file changed, 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index a808aaf06d87..916ba81c60b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24479,11 +24479,6 @@ uuid@^8.0.0, uuid@^8.3.1, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" - integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== - v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" From 22a51e41695d9a1033258febb7311c73752dc47e Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 7 Feb 2023 14:16:36 +0000 Subject: [PATCH 12/12] extract into var --- packages/utils/src/stacktrace.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/utils/src/stacktrace.ts b/packages/utils/src/stacktrace.ts index 2cee22d06007..8af1c6f0d1b0 100644 --- a/packages/utils/src/stacktrace.ts +++ b/packages/utils/src/stacktrace.ts @@ -29,13 +29,15 @@ export function createStackParser(...parsers: StackLineParser[]): StackParser { debugIdParserCache.set(parser, debugIdCache); } - if (GLOBAL_OBJ._sentryDebugIds) { - Object.keys(GLOBAL_OBJ._sentryDebugIds).forEach(debugIdStackTrace => { + const debugIdMap = GLOBAL_OBJ._sentryDebugIds; + + if (debugIdMap) { + Object.keys(debugIdMap).forEach(debugIdStackTrace => { debugIdStackTrace.split('\n').forEach(line => { const frame = parser(line); if (frame && frame.filename) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - debugIdCache!.set(frame.filename, GLOBAL_OBJ._sentryDebugIds![debugIdStackTrace]); + debugIdCache!.set(frame.filename, debugIdMap[debugIdStackTrace]); } }); });