Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions packages/base/src/commands/sourcemaps/__tests__/debugId.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,54 @@ describe('extractDebugId', () => {
})
})

test('extracts a debug ID with a single-quoted value', () => {
withTempDirectory((directory) => {
const filePath = upath.join(directory, 'single-quoted-value.min.js')
fs.writeFileSync(
filePath,
`!function(){}({service:"app",version:"1.0.0",ddDebugId:'${DEBUG_ID}'},"DD_SOURCE_CODE_CONTEXT");`
)

expect(extractDebugId(filePath)).toBe(DEBUG_ID)
})
})

test('extracts a debug ID with a quoted key and a single-quoted value', () => {
withTempDirectory((directory) => {
const filePath = upath.join(directory, 'mixed-quotes.min.js')
fs.writeFileSync(
filePath,
`!function(){}({"service":"app","version":"1.0.0","ddDebugId":'${DEBUG_ID}'},"DD_SOURCE_CODE_CONTEXT");`
)

expect(extractDebugId(filePath)).toBe(DEBUG_ID)
})
})

test('extracts a debug ID with a backticked value', () => {
withTempDirectory((directory) => {
const filePath = upath.join(directory, 'backticked-value.min.js')
fs.writeFileSync(
filePath,
'!function(){}' + `({service:"app",version:"1.0.0",ddDebugId:\`${DEBUG_ID}\`},` + '"DD_SOURCE_CODE_CONTEXT");'
)

expect(extractDebugId(filePath)).toBe(DEBUG_ID)
})
})

test('returns undefined when the opening and closing quotes do not match', () => {
withTempDirectory((directory) => {
const filePath = upath.join(directory, 'mismatched-quotes.min.js')
fs.writeFileSync(
filePath,
`!function(){}({service:"app",version:"1.0.0",ddDebugId:'${DEBUG_ID}"},"DD_SOURCE_CODE_CONTEXT");`
)

expect(extractDebugId(filePath)).toBeUndefined()
})
})

test('stops reading after finding the debug ID in the first chunk', () => {
withTempDirectory((directory) => {
const filePath = upath.join(directory, 'first-chunk.min.js')
Expand Down
9 changes: 7 additions & 2 deletions packages/base/src/commands/sourcemaps/debugId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import {ReplaceSource, SourceMapSource} from 'webpack-sources'

// Bundle scanner: keep this progressive scanner in sync with build-plugins PR #489:
// https://github.com/DataDog/build-plugins/pull/489
const DEBUG_ID_REGEX = /"?ddDebugId"?:"([0-9a-fA-F-]{36})"/
// The key is quoted in source (`JSON.stringify(context)`) but minifiers like terser strip quotes
// from object keys that are valid identifiers, so the built output can have either
// `"ddDebugId":"..."` or `ddDebugId:"..."`. The value can also end up wrapped in single quotes or
// backticks depending on the minifier, so all three of `"`, `'` and `` ` `` are accepted around it.
// The opening quote is captured and backreferenced so the closing quote must match it.
const DEBUG_ID_REGEX = /"?ddDebugId"?:(["'`])([0-9a-fA-F-]{36})\1/
const SOURCE_CODE_CONTEXT_MARKER = 'DD_SOURCE_CODE_CONTEXT'

// Single source of truth for the UUID v4-ish shape used by debug IDs. The sourcemap find command
Expand All @@ -29,7 +34,7 @@ const VARIANT_CHARS = ['8', '9', 'a', 'b'] as const

export const isValidDebugId = (debugId: string): boolean => DEBUG_ID_VALUE_REGEX.test(debugId)

const matchDebugId = (fileContent: string): string | undefined => DEBUG_ID_REGEX.exec(fileContent)?.[1]
const matchDebugId = (fileContent: string): string | undefined => DEBUG_ID_REGEX.exec(fileContent)?.[2]

// Search in fixed-size reads and stop as soon as a match is found. Only a small overlap is retained
// between reads, so even the worst case (scanning to EOF) uses bounded memory.
Expand Down
Loading