Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 3 additions & 1 deletion packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export type ChunkInfo = {
};

// Static string, lazy async loader (e.g. file fetch), or per-chunk code generator.
export type InjectedValue = string | (() => Promise<string>) | ((sourceOrHash?: string) => string);
export type InjectedValue = string | (() => Promise<string>) | ((chunk?: ChunkInfo) => string);

export enum InjectPosition {
BEFORE,
Expand Down Expand Up @@ -345,6 +345,8 @@ export type GlobalData = {
};

export type GlobalStores = {
// Keyed by output file basename, filled in by the RUM plugin, read by error-tracking.
debugIds: Map<string, string>;
errors: string[];
logs: Log[];
metrics: Set<Metric>;
Expand Down
1 change: 1 addition & 0 deletions packages/factory/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export const buildPluginFactory = ({
};

const stores: GlobalStores = {
debugIds: new Map(),
errors: [],
logs: [],
metrics: new Set(),
Expand Down
3 changes: 2 additions & 1 deletion packages/plugins/error-tracking/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export type types = {
ErrorTrackingOptions: ErrorTrackingOptions;
};

export const getPlugins: GetPlugins = ({ options, context }) => {
export const getPlugins: GetPlugins = ({ options, context, stores }) => {
const log = context.getLogger(PLUGIN_NAME);
const timeOptions = log.time('validate options');
const validatedOptions = validateOptions(options, log);
Expand All @@ -41,6 +41,7 @@ export const getPlugins: GetPlugins = ({ options, context }) => {
{
apiKey: context.auth.apiKey,
bundlerName: context.bundler.name,
debugIds: stores.debugIds,
git: gitInfo,
addMetric: context.addMetric,
outDir: context.bundler.outDir,
Expand Down
1 change: 1 addition & 0 deletions packages/plugins/error-tracking/src/sourcemaps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const uploadSourcemaps = async (
addMetric: context.addMetric,
apiKey: context.apiKey,
bundlerName: context.bundlerName,
debugIds: context.debugIds,
git: context.git,
outDir: context.outDir,
sendMetrics: context.sendMetrics,
Expand Down
3 changes: 3 additions & 0 deletions packages/plugins/error-tracking/src/sourcemaps/payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export type Metadata = {
version: string;
git_repository_url?: string;
git_commit_sha?: string;
debug_id?: string;
};

type SourcemapValidity = {
Expand Down Expand Up @@ -87,6 +88,7 @@ export const getPayload = async (
metadata: Metadata,
prefix: string,
git?: RepositoryData,
debugId?: string,
): Promise<Payload> => {
const validity = await getSourcemapValidity(sourcemap, prefix);
const errors: string[] = [];
Expand All @@ -102,6 +104,7 @@ export const getPayload = async (
},
value: JSON.stringify({
...metadata,
debug_id: debugId,
minified_url: sourcemap.minifiedUrl,
}),
Comment on lines 104 to 109
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const uploadContextMock = {
};
const senderContextMock = {
...uploadContextMock,
debugIds: new Map(),
git: contextMock.git,
};

Expand Down
18 changes: 14 additions & 4 deletions packages/plugins/error-tracking/src/sourcemaps/sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { formatDuration, prettyObject } from '@dd/core/helpers/strings';
import type { Logger, Metric, RepositoryData } from '@dd/core/types';
import chalk from 'chalk';
import PQueue from 'p-queue';
import path from 'path';

import type { SourcemapsOptionsWithDefaults, Sourcemap } from '../types';

Expand Down Expand Up @@ -77,6 +78,11 @@ export type UploadContext = {
outDir: string;
};

export type DebugIdsContext = {
// Keyed by output file basename, filled in by the RUM plugin.
debugIds: Map<string, string>;
};
Comment on lines +81 to +84

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This could maybe be factorised in the core's types, and imported here (and used in the GlobalStores).


export const upload = async (
payloads: Payload[],
options: SourcemapsOptionsWithDefaults,
Expand Down Expand Up @@ -175,9 +181,10 @@ export const upload = async (
return { warnings, errors };
};

export type SourcemapsSenderContext = UploadContext & {
git?: RepositoryData;
};
export type SourcemapsSenderContext = UploadContext &
DebugIdsContext & {
git?: RepositoryData;
};

export const sendSourcemaps = async (
sourcemaps: Sourcemap[],
Expand All @@ -200,7 +207,10 @@ export const sendSourcemaps = async (

const payloadsTimer = log.time('Compute payloads');
const payloads = await Promise.all(
sourcemaps.map((sourcemap) => getPayload(sourcemap, metadata, prefix, context.git)),
sourcemaps.map((sourcemap) => {
const debugId = context.debugIds.get(path.basename(sourcemap.minifiedFilePath));
return getPayload(sourcemap, metadata, prefix, context.git, debugId);
}),
);
payloadsTimer.end();

Expand Down
6 changes: 3 additions & 3 deletions packages/plugins/injection/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ export const prepareInjections = async (
contentsToInject: ContentsToInject,
cwd: string = process.cwd(),
) => {
// Per-chunk functions: adapt from public API (sourceOrHash?: string) to internal (chunk: ChunkInfo).
// Per-chunk functions receive the full ChunkInfo for the chunk they're injected into.
const dynamicPerChunk = toInject.filter(isPerChunk).map((item) => {
const userFn = item.value as (sourceOrHash?: string) => string;
return { ...item, value: (chunk: ChunkInfo) => userFn(chunk.sourceOrHash) };
const userFn = item.value as (chunk?: ChunkInfo) => string;
return { ...item, value: (chunk: ChunkInfo) => userFn(chunk) };
});
Comment on lines +127 to 131

// Static items (strings and async loaders) are resolved once per build.
Expand Down
17 changes: 13 additions & 4 deletions packages/plugins/rum/src/getSourceCodeContextSnippet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2019-Present Datadog, Inc.

import type { ChunkInfo } from '@dd/core/types';
import { randomUUID } from 'crypto';

import { stringToUUID } from './debugId';
Expand All @@ -28,10 +29,16 @@ type SourceCodeContext = {
version?: string;
ddDebugId?: string;
};

export type SourceCodeContextSnippet = {
code: string;
debugId?: string;
};

export const getSourceCodeContextSnippet = (
contextOptions: SourceCodeContextOptions,
codeOrHash?: string,
): string => {
chunk?: ChunkInfo,
): SourceCodeContextSnippet => {
const context: SourceCodeContext = {
service: contextOptions.service,
version: contextOptions.version,
Expand All @@ -42,8 +49,10 @@ export const getSourceCodeContextSnippet = (
//
// The `dd` prefix in `ddDebugId` allows upload tools (for example, datadog-ci) to reliably locate the
// debug ID with a regex and send it as upload metadata alongside the source map.
context.ddDebugId = codeOrHash ? stringToUUID(codeOrHash) : randomUUID();
context.ddDebugId = chunk ? stringToUUID(chunk.sourceOrHash) : randomUUID();
}

return `(function(c,n){try{if(typeof window==='undefined')return;var w=window,m=w[n]=w[n]||{},s=new Error().stack;s&&(m[s]=c)}catch(e){}})(${JSON.stringify(context)},${JSON.stringify(DEFAULT_SOURCE_CODE_CONTEXT_VARIABLE)});`;
const code = `(function(c,n){try{if(typeof window==='undefined')return;var w=window,m=w[n]=w[n]||{},s=new Error().stack;s&&(m[s]=c)}catch(e){}})(${JSON.stringify(context)},${JSON.stringify(DEFAULT_SOURCE_CODE_CONTEXT_VARIABLE)});`;

return { code, debugId: context.ddDebugId };
};
11 changes: 9 additions & 2 deletions packages/plugins/rum/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export type types = {
RumInitConfiguration: RumInitConfiguration;
};

export const getPlugins: GetPlugins = ({ options, context }) => {
export const getPlugins: GetPlugins = ({ options, context, stores }) => {
const log = context.getLogger(PLUGIN_NAME);
const validatedOptions = validateOptions(options, log);
const plugins: PluginOptions[] = [];
Expand All @@ -37,7 +37,14 @@ export const getPlugins: GetPlugins = ({ options, context }) => {
type: 'code',
position: InjectPosition.BEFORE,
injectIntoAllChunks: true,
value: (sourceOrHash) => getSourceCodeContextSnippet(sourceCodeContext, sourceOrHash),
value: (chunk) => {
const { code, debugId } = getSourceCodeContextSnippet(sourceCodeContext, chunk);
if (debugId && chunk) {
// Let the error-tracking plugin pick this up when uploading its sourcemap.
stores.debugIds.set(path.basename(chunk.fileName), debugId);
}
Comment on lines +40 to +47

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in e690446. Keyed by chunk.fileName (RUM side) and looked up via sourcemap.relativePath (error-tracking side), normalizing path separators to handle Windows.

return code;
},
});
}

Expand Down
1 change: 1 addition & 0 deletions packages/tests/src/_jest/helpers/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export const getMockData = (overrides: Partial<GlobalData> = {}): GlobalData =>
});

export const getMockStores = (overrides: Partial<GlobalStores> = {}): GlobalStores => ({
debugIds: new Map(),
logs: [],
errors: [],
warnings: [],
Expand Down
1 change: 1 addition & 0 deletions packages/tools/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ export const getSupportedBundlers = (getPlugins: GetPlugins) => {
};

const stores: GlobalStores = {
debugIds: new Map(),
errors: [],
warnings: [],
logs: [],
Expand Down
Loading