-
Notifications
You must be signed in to change notification settings - Fork 72
[RUM-16940] add --debug-id upload mode for web sourcemaps
#2374
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
07892ee
8f7bda1
91c2f8f
55716f2
d5d3088
cadcb92
d0191ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| import fs from 'fs' | ||
|
|
||
| import {addDebugIdToPayloads, extractDebugId} from '../debugId' | ||
| import {Sourcemap} from '../interfaces' | ||
|
|
||
| const DEBUG_ID = '2f1d7f52-4e1b-4f7c-8c0d-2f4a5f6d8e91' | ||
|
|
||
| const makeSourcemap = (minifiedFilePath: string) => | ||
| new Sourcemap(minifiedFilePath, `https://static.com/${minifiedFilePath}`, `${minifiedFilePath}.map`, minifiedFilePath) | ||
|
|
||
| // Mocks fs.readFileSync to return the given content keyed by minified file path. | ||
| const mockFilesByPath = (contentByPath: Record<string, string>) => { | ||
| jest.spyOn(fs, 'readFileSync').mockImplementation((path: unknown) => { | ||
| const content = contentByPath[path as string] | ||
| if (content === undefined) { | ||
| throw new Error(`ENOENT: ${String(path)}`) | ||
| } | ||
|
|
||
| return content | ||
| }) | ||
| } | ||
|
|
||
| describe('extractDebugId', () => { | ||
| afterEach(() => { | ||
| jest.restoreAllMocks() | ||
| }) | ||
|
|
||
| describe('snippet formats', () => { | ||
| test.each([ | ||
| [`{"ddDebugId":"${DEBUG_ID}"}`], | ||
| [`{"ddDebugId": "${DEBUG_ID}"}`], | ||
| [`{"ddDebugId" : "${DEBUG_ID}"}`], | ||
| [`{"ddDebugId" : "${DEBUG_ID}"}`], | ||
| [`{"ddDebugId"\t:\t"${DEBUG_ID}"}`], | ||
| [`{'ddDebugId': '${DEBUG_ID}'}`], | ||
| [`var x=1;({"ddDebugId":"${DEBUG_ID}"});var y=2;`], | ||
| [`var x=1;\n{"ddDebugId": "${DEBUG_ID}"}\nvar y=2;`], | ||
| ])('%s', (content: string) => { | ||
| jest.spyOn(fs, 'readFileSync').mockReturnValueOnce(content) | ||
| expect(extractDebugId('bundle.js')).toBe(DEBUG_ID) | ||
| }) | ||
| }) | ||
|
|
||
| describe('missing or unreadable', () => { | ||
| test('returns undefined when snippet is absent', () => { | ||
| jest.spyOn(fs, 'readFileSync').mockReturnValueOnce('var x = 1; console.log("hello");') | ||
| expect(extractDebugId('bundle.js')).toBeUndefined() | ||
| }) | ||
|
|
||
| test('returns undefined when file cannot be read', () => { | ||
| jest.spyOn(fs, 'readFileSync').mockImplementationOnce(() => { | ||
| throw new Error('ENOENT: no such file or directory') | ||
| }) | ||
| expect(extractDebugId('nonexistent.js')).toBeUndefined() | ||
| }) | ||
| }) | ||
| }) | ||
|
|
||
| describe('addDebugIdToPayloads', () => { | ||
| afterEach(() => { | ||
| jest.restoreAllMocks() | ||
| }) | ||
|
|
||
| test('stores each debug ID on its payload and returns true when any is found', () => { | ||
| mockFilesByPath({ | ||
| 'a.min.js': `{"ddDebugId":"${DEBUG_ID}"}`, | ||
| 'b.min.js': 'var x = 1;', | ||
| }) | ||
| const withId = makeSourcemap('a.min.js') | ||
| const withoutId = makeSourcemap('b.min.js') | ||
|
|
||
| expect(addDebugIdToPayloads([withId, withoutId])).toBe(true) | ||
| expect(withId.debugId).toBe(DEBUG_ID) | ||
| expect(withoutId.debugId).toBeUndefined() | ||
| }) | ||
|
|
||
| test('returns false when no payload has a debug ID', () => { | ||
| mockFilesByPath({'a.min.js': 'var x = 1;', 'b.min.js': 'var y = 2;'}) | ||
| const payloads = [makeSourcemap('a.min.js'), makeSourcemap('b.min.js')] | ||
|
|
||
| expect(addDebugIdToPayloads(payloads)).toBe(false) | ||
| expect(payloads.every((p) => p.debugId === undefined)).toBe(true) | ||
| }) | ||
| }) |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import fs from 'fs' | ||
|
|
||
| import type {Sourcemap} from './interfaces' | ||
|
|
||
| const DD_DEBUG_ID_REGEX = | ||
| /["']ddDebugId["']\s*:\s*["']([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})["']/ | ||
|
|
||
| export const extractDebugId = (filePath: string): string | undefined => { | ||
| try { | ||
| const source = fs.readFileSync(filePath, 'utf-8') | ||
|
|
||
| return source.match(DD_DEBUG_ID_REGEX)?.[1] | ||
| } catch { | ||
| // Unreadable file: treated as having no debug ID. | ||
| return undefined | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Adds the debug ID extracted from each payload's minified file onto the | ||
| * payload. Returns true if at least one payload has a debug ID. | ||
| */ | ||
| export const addDebugIdToPayloads = (payloads: Sourcemap[]): boolean => { | ||
| let hasAnyDebugId = false | ||
| for (const payload of payloads) { | ||
| payload.debugId = extractDebugId(payload.minifiedFilePath) | ||
| if (payload.debugId !== undefined) { | ||
| hasAnyDebugId = true | ||
| } | ||
| } | ||
|
|
||
| return hasAnyDebugId | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.