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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"genversion": "3.0.2",
"jest": "^29.7.0",
"lerna": "8.1.6",
"metro": "^0.82.0",
"metro": "^0.83.1",
"pod-install": "0.1.14",
"prettier": "2.2.0",
"react": "18.3.1",
Expand Down
10 changes: 6 additions & 4 deletions packages/core/src/metro/__tests__/__utils__/serializerUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
* Copyright 2016-Present Datadog, Inc.
*/

// eslint-disable-next-line import/no-extraneous-dependencies
import countLines from 'metro/src/lib/countLines';

import type {
MetroSerializer,
MixedOutput,
Module,
MetroVirtualModuleOutput
} from '../../plugin/types/metroTypes';
import { createCountingSet } from '../../plugin/utils';
import {
getCreateCountingSetFunction,
getCountLinesFunction
} from '../../plugin/utils';

export const mockSerializerArgsForEmptyModule = (): Parameters<MetroSerializer> => {
return [
Expand Down Expand Up @@ -48,6 +48,8 @@ export const mockSerializerArgsForEmptyModule = (): Parameters<MetroSerializer>
};

export const mockSerializerArgsForSourceMappingURLModule = (): Parameters<MetroSerializer> => {
const countLines = getCountLinesFunction();
const createCountingSet = getCreateCountingSetFunction();
const mockedCode = '//# sourceMappingURL=index.android.bundle.map';
return [
'index.js',
Expand Down
254 changes: 254 additions & 0 deletions packages/core/src/metro/__tests__/metro.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ const DEBUG_ID_CODE_SNIPPET =
'var _datadogDebugIds,_datadogDebugIdMeta;void 0===_datadogDebugIds&&(_datadogDebugIds={});try{var stack=(new Error).stack;stack&&(_datadogDebugIds[stack]="__datadog_debug_id_placeholder__",_datadogDebugIdMeta="datadog-debug-id-__datadog_debug_id_placeholder__")}catch(e){}';

describe('Datadog Metro Plugin', () => {
afterEach(() => {
jest.resetModules();
});

describe('Datadog Metro Serializer', () => {
test('generates bundle and source map with UUID v5 Debug ID', async () => {
const codeSnippetHash = createHash('md5');
Expand Down Expand Up @@ -404,4 +408,254 @@ describe('Datadog Metro Plugin', () => {
expect(result.map).toBe('{"testMap":"test"}');
});
});

describe('Import Utils', () => {
test('M getDefaultExport extracts the default export if it exists', () => {
// GIVEN
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
const getDefaultExport = require('../plugin/utils')
.getDefaultExport;

const exampleModuleWithDefault = {
default: 'default export',
namedExport: 'named export'
};

// WHEN
const result = getDefaultExport(exampleModuleWithDefault);

// THEN
expect(result).toBe('default export');
});

test('M getDefaultExport returns the module as it is if default does not exist', () => {
// GIVEN
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
const getDefaultExport = require('../plugin/utils')
.getDefaultExport;

const exampleModule1 = {
namedExport: 'named export'
};

const exampleModule2 = 'just a string';

// WHEN
const result1 = getDefaultExport(exampleModule1);
const result2 = getDefaultExport(exampleModule2);

// THEN
expect(result1).toEqual({ namedExport: 'named export' });
expect(result2).toBe('just a string');
});

test('M getDefaultExport returns undefined if the module is null or undefined', () => {
// GIVEN
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
const getDefaultExport = require('../plugin/utils')
.getDefaultExport;

const exampleModule1 = null;
const exampleModule2 = undefined;

// WHEN
const result1 = getDefaultExport(exampleModule1);
const result2 = getDefaultExport(exampleModule2);

// THEN
expect(result1).toBeUndefined();
expect(result2).toBeUndefined();
});

test('M createCountingSet correctly imports function from metro/src when metro/private is not available', () => {
// GIVEN
jest.isolateModules(() => {
// GIVEN
jest.doMock('metro/private/lib/CountingSet', () => {
throw new Error('Module not found');
});

jest.doMock(
'metro/src/lib/CountingSet',
() => ({
default: class CountingSetMock {
test: string = 'constructor_not_called';
constructor() {
this.test = 'constructor_called';
}
}
}),
{ virtual: true }
);

// WHEN
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
const utils = require('../plugin/utils');
const createCountingSet = utils.getCreateCountingSetFunction();
const result = createCountingSet();

// THEN
expect(result).toHaveProperty('test');
expect(result.test).toBe('constructor_called');
});
});

test('M sourcemapString correctly imports function from metro/src when metro/private is not available', () => {
// GIVEN
jest.isolateModules(() => {
// GIVEN
jest.doMock(
'metro/private/DeltaBundler/Serializers/sourceMapString',
() => {
throw new Error('Module not found');
}
);

jest.doMock(
'metro/src/DeltaBundler/Serializers/sourceMapString',
() => ({
default: (
modules: unknown[],
options: object
): string =>
`test-modules_length:${
modules.length
},options_keys:${Object.keys(options).length}`
}),
{ virtual: true }
);

// WHEN
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
const utils = require('../plugin/utils');
const metroSourceMapString = utils.getSourceMapStringFunction();
const result = metroSourceMapString([{}, {}, {}], {
excludeSource: true,
shouldAddToIgnoreList: () => true
});

// THEN
expect(result).toBe('test-modules_length:3,options_keys:2');
});
});

test('M sourcemapString returns the correct function when retrieved as named export', () => {
// GIVEN
jest.isolateModules(() => {
// GIVEN
jest.doMock(
'metro/private/DeltaBundler/Serializers/sourceMapString',
() => ({
sourceMapString: (
modules: unknown[],
options: object
): string =>
`test-modules_length:${
modules.length
},options_keys:${Object.keys(options).length}`
})
);

// WHEN
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
const utils = require('../plugin/utils');
const metroSourceMapString = utils.getSourceMapStringFunction();
const result = metroSourceMapString([{}, {}, {}], {
excludeSource: true,
shouldAddToIgnoreList: () => true
});
// THEN
expect(result).toBe('test-modules_length:3,options_keys:2');
});
});

test('M sourcemapString returns the correct function when retrieved as default export', () => {
// GIVEN
jest.isolateModules(() => {
// GIVEN
jest.doMock(
'metro/private/DeltaBundler/Serializers/sourceMapString',
() => ({
default: (
modules: unknown[],
options: object
): string =>
`test-modules_length:${
modules.length
},options_keys:${Object.keys(options).length}`
})
);

// WHEN
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
const utils = require('../plugin/utils');
const metroSourceMapString = utils.getSourceMapStringFunction();
const result = metroSourceMapString([{}, {}, {}], {
excludeSource: true,
shouldAddToIgnoreList: () => true
});
// THEN
expect(result).toBe('test-modules_length:3,options_keys:2');
});
});

test('M getBaseJSBundle correctly imports function from metro/src when metro/private is not available', () => {
// GIVEN
jest.isolateModules(() => {
// GIVEN
jest.doMock(
'metro/private/DeltaBundler/Serializers/baseJSBundle',
() => {
throw new Error('Module not found');
}
);

jest.doMock(
'metro/src/DeltaBundler/Serializers/baseJSBundle',
() => () => ({
test: 'ok'
}),
{ virtual: true }
);

// WHEN
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
const utils = require('../plugin/utils');
const baseJSBundle = utils.getBaseJSBundleFunction();
const result = baseJSBundle();

// THEN
expect(result).toHaveProperty('test');
expect(result.test).toBe('ok');
});
});

test('M getCountLines correctly imports function from metro/src when metro/private is not available', () => {
// GIVEN
jest.isolateModules(() => {
// GIVEN
jest.doMock('metro/private/lib/countLines', () => {
throw new Error('Module not found');
});

// Random int between 10 and 100 to ensure the mock is used
const randomInt = Math.floor(Math.random() * 90) + 10;

jest.doMock(
'metro/src/lib/countLines',
() => (str: string): number => str.length + randomInt,
{ virtual: true }
);

// WHEN
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
const utils = require('../plugin/utils');
const getCountLines = utils.getCountLinesFunction();
const result = getCountLines('test-string');

// THEN
expect(result).toBe('test-string'.length + randomInt);
});
});
});
});
8 changes: 4 additions & 4 deletions packages/core/src/metro/metro.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ declare type Bundle = {
};

// https://github.com/facebook/metro/blob/a3d021a0d021b5706372059f472715c63019e044/packages/metro/src/DeltaBundler/Serializers/baseJSBundle.js#L25
declare module 'metro/src/DeltaBundler/Serializers/baseJSBundle' {
declare module 'metro/private/DeltaBundler/Serializers/baseJSBundle' {
const baseJSBundle: (
entryPoint: string,
preModules: ReadonlyArray<Module>,
Expand All @@ -39,7 +39,7 @@ declare module 'metro/src/DeltaBundler/Serializers/baseJSBundle' {
}

// https://github.com/facebook/metro/blob/a3d021a0d021b5706372059f472715c63019e044/packages/metro/src/lib/bundleToString.js#L22
declare module 'metro/src/lib/bundleToString' {
declare module 'metro/private/lib/bundleToString' {
const bundleToString: (
bundle: Bundle
) => {
Expand All @@ -51,7 +51,7 @@ declare module 'metro/src/lib/bundleToString' {
}

// https://github.com/facebook/metro/blob/a3d021a0d021b5706372059f472715c63019e044/packages/metro/src/DeltaBundler/Serializers/sourceMapString.js#L22
declare module 'metro/src/DeltaBundler/Serializers/sourceMapString' {
declare module 'metro/private/DeltaBundler/Serializers/sourceMapString' {
import type { MixedOutput, Module } from 'metro';

const sourceMapString: (
Expand All @@ -67,7 +67,7 @@ declare module 'metro/src/DeltaBundler/Serializers/sourceMapString' {
}

// https://github.com/facebook/metro/blob/a3d021a0d021b5706372059f472715c63019e044/packages/metro/src/lib/countLines.js#L16
declare module 'metro/src/lib/countLines' {
declare module 'metro/private/lib/countLines' {
const countLines: (code: string) => number;
export = countLines;
}
6 changes: 3 additions & 3 deletions packages/core/src/metro/plugin/debugIdHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@

import { createHash } from 'crypto';
import { existsSync, mkdirSync, writeFileSync, unlinkSync } from 'fs';
// eslint-disable-next-line import/no-extraneous-dependencies
import countLines from 'metro/src/lib/countLines';
import path from 'path';

import type {
Expand All @@ -20,7 +18,7 @@ import type {
MetroBundleWithMap,
DatadogDebugIdModule
} from './types/metroTypes';
import { createCountingSet } from './utils';
import { getCreateCountingSetFunction, getCountLinesFunction } from './utils';

/**
* Regex to match the Debug ID comment in the bundle.
Expand Down Expand Up @@ -63,6 +61,8 @@ const DEBUG_ID_METADATA_PREFIX = 'datadog-debug-id-';
*/
export const createDebugIdModule = (debugId: string): DatadogDebugIdModule => {
let debugIdCode = createDebugIdSnippet(debugId);
const countLines = getCountLinesFunction();
const createCountingSet = getCreateCountingSetFunction();

return {
setSource: (code: string) => {
Expand Down
Loading
Loading