Skip to content
Closed
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
15 changes: 13 additions & 2 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,18 @@ export type BundlerReport = {
version: string;
};

export type ToInjectItem = { type: 'file' | 'code'; value: string; fallback?: ToInjectItem };
export type InjectedValue = string | (() => Promise<string>);
export enum InjectPosition {
BEFORE,
MIDDLE,
AFTER,
}
export type ToInjectItem = {
type: 'file' | 'code';
value: InjectedValue;
position?: InjectPosition;
fallback?: ToInjectItem;
};

export type GetLogger = (name: string) => Logger;
export type Logger = {
Expand Down Expand Up @@ -133,7 +144,7 @@ export interface Options extends BaseOptions {
customPlugins?: GetCustomPlugins;
}

export type GetPluginsOptions = Required<BaseOptions>;
export type GetPluginsOptions = BaseOptions;
export type OptionsWithDefaults = Assign<Options, GetPluginsOptions>;

export type PluginName = `datadog-${Lowercase<string>}-plugin`;
Expand Down
63 changes: 63 additions & 0 deletions packages/plugins/rum/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,37 @@ export const getPlugins: GetPlugins<OptionsWithRum> = (
log: Logger,
) => {
// Verify configuration.
<<<<<<< HEAD
const rumOptions = validateOptions(opts, log);
=======
const options = validateOptions(opts, log);

if (options.sdk) {
// Inject the SDK from the CDN.
context.inject({
type: 'file',
position: InjectPosition.BEFORE,
value: 'https://www.datadoghq-browser-agent.com/us1/v5/datadog-rum.js',
});

if (options.react) {
// Inject the rum-react-plugin.
// NOTE: These files are built from "@dd/tools/rollupConfig.mjs" and available in the distributed package.
context.inject({
type: 'file',
position: InjectPosition.MIDDLE,
value: path.join(__dirname, './rum-react-plugin.js'),
});
}

context.inject({
type: 'code',
position: InjectPosition.MIDDLE,
value: getInjectionValue(options as RumOptionsWithSdk, context),
});
}

>>>>>>> 2aae126 (createBrowserRouter auto instrumentation)
return [
{
name: 'datadog-rum-sourcemaps-plugin',
Expand All @@ -37,6 +67,39 @@ export const getPlugins: GetPlugins<OptionsWithRum> = (
await uploadSourcemaps(rumOptions as RumOptionsWithSourcemaps, context, log);
}
},
transform(code) {
let updatedCode = code;
const createBrowserRouterImportRegExp = new RegExp(
/(import \{.*)createBrowserRouter[,]?(.*\} from "react-router-dom")/g,
);
const hasCreateBrowserRouterImport =
code.match(createBrowserRouterImportRegExp) !== null;

if (hasCreateBrowserRouterImport) {
// Remove the import of createBrowserRouter
updatedCode = updatedCode.replace(
createBrowserRouterImportRegExp,
(_, p1, p2) => {
return `${p1}${p2}`;
},
);

// replace all occurences of `createBrowserRouter` with `DD_RUM.createBrowserRouter`
updatedCode = updatedCode.replace(
new RegExp(/createBrowserRouter/g),
'DD_RUM.createBrowserRouter',
);
}

return updatedCode;
},
transformInclude(id) {
return (
// @ts-ignore
options?.react?.router === true &&
id.match(new RegExp(/.*\.(js|jsx|ts|tsx)$/)) !== null
);
},
},
];
};
77 changes: 77 additions & 0 deletions packages/plugins/rum/src/sdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Unless explicitly stated otherwise all files in this repository are licensed under the MIT License.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2019-Present Datadog, Inc.

import { doRequest } from '@dd/core/helpers';
import type { GlobalContext, InjectedValue } from '@dd/core/types';

import type { RumOptionsWithDefaults, RumOptionsWithSdk } from './types';

type RumAppResponse = {
data: {
attributes: {
client_token: string;
};
};
};

const getContent = (opts: RumOptionsWithDefaults) => {
const pluginContent = opts.react
? // @ts-ignore
`, plugins: [reactPlugin({router: ${opts.react.router}})]`
: '';
const sessionReplayStartCommand = opts.startSessionReplayRecording
? 'DD_RUM.startSessionReplayRecording();\n'
: '';

return `DD_RUM.init({${JSON.stringify(opts.sdk).replace(/(^{|}$)/g, '')}${pluginContent}});${sessionReplayStartCommand}
`;
};

export const getInjectionValue = (
options: RumOptionsWithSdk,
context: GlobalContext,
): InjectedValue => {
const sdkOpts = options.sdk;
// We already have the clientToken, we can inject it directly.
if (sdkOpts.clientToken) {
return getContent(options);
}

// Let's fetch the clientToken from the API.
if (!context.auth?.apiKey || !context.auth?.appKey) {
throw new Error('Missing auth.apiKey and/or auth.appKey to fetch clientToken.');
}

let clientToken: string;

return async () => {
try {
// Fetch the client token from the API.
const appResponse = await doRequest<RumAppResponse>({
url: `https://api.datadoghq.com/api/v2/rum/applications/${sdkOpts.applicationId}`,
type: 'json',
auth: context.auth,
});

clientToken = appResponse.data?.attributes?.client_token;
} catch (e: any) {
// Could not fetch the clientToken.
// Let's crash the build.
throw new Error(`Could not fetch the clientToken: ${e.message}`);
}

// Still no clientToken.
if (!clientToken) {
throw new Error('Missing clientToken in the API response.');
}

return getContent({
...options,
sdk: {
clientToken,
...sdkOpts,
},
});
};
};
12 changes: 12 additions & 0 deletions packages/plugins/rum/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,26 @@ export type RumSourcemapsOptions = {

export type RumOptions = {
disabled?: boolean;
<<<<<<< HEAD
sourcemaps?: RumSourcemapsOptions;
=======
sdk?: SDKOptions;
react?: ReactOptions;
startSessionReplayRecording?: boolean;
>>>>>>> 2aae126 (createBrowserRouter auto instrumentation)
};

export type RumSourcemapsOptionsWithDefaults = Required<RumSourcemapsOptions>;

export type RumOptionsWithDefaults = {
disabled?: boolean;
<<<<<<< HEAD
sourcemaps?: RumSourcemapsOptionsWithDefaults;
=======
sdk?: SDKOptionsWithDefaults;
react?: ReactOptionsWithDefaults;
startSessionReplayRecording?: boolean;
>>>>>>> 2aae126 (createBrowserRouter auto instrumentation)
};

export type RumOptionsWithSourcemaps = {
Expand Down
9 changes: 9 additions & 0 deletions packages/tools/src/build/rumReactPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createBrowserRouter } from '@datadog/browser-rum-react/react-router-v6';
import { reactPlugin } from '@datadog/browser-rum-react';

(() => {
const globalAny: any = global;
globalAny.DD_RUM = globalAny.DD_RUM || {};
globalAny.reactPlugin = reactPlugin;
globalAny.DD_RUM.createBrowserRouter = createBrowserRouter;
})();
4 changes: 4 additions & 0 deletions packages/tools/src/rollupConfig.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ export const getDefaultBuildConfigs = (packageJson) => [
format: 'cjs',
},
}),
<<<<<<< HEAD
=======
// Type definitions.
>>>>>>> 2aae126 (createBrowserRouter auto instrumentation)
// FIXME: This build is sloooow.
bundle(packageJson, {
plugins: [dts()],
Expand Down
Loading