diff --git a/benchmarks/babel.config.js b/benchmarks/babel.config.js index f7b3da3b3..d79333729 100644 --- a/benchmarks/babel.config.js +++ b/benchmarks/babel.config.js @@ -1,3 +1,10 @@ module.exports = { presets: ['module:@react-native/babel-preset'], + plugins: [ + ['@datadog/mobile-react-native-babel-plugin', { + sessionReplay: { + svgTracking: true + } + }] + ] }; diff --git a/benchmarks/ios/Podfile b/benchmarks/ios/Podfile index 6adb02df5..5174c9064 100644 --- a/benchmarks/ios/Podfile +++ b/benchmarks/ios/Podfile @@ -37,5 +37,27 @@ target 'BenchmarkRunner' do :mac_catalyst_enabled => false, # :ccache_enabled => true ) + + # Workaround for fmt consteval errors with Xcode 26+ Clang: + # The fmt library's consteval usage is incompatible with newer Apple Clang. + # Patch base.h to disable consteval by adding a guard for the current Xcode. + fmt_base_header = File.join(installer.sandbox.pod_dir('fmt'), 'include', 'fmt', 'base.h') + if File.exist?(fmt_base_header) + content = File.read(fmt_base_header) + unless content.include?('Patched for Xcode 26') + if content.gsub!( + /^(#elif defined\(__cpp_consteval\)\n#\s*define FMT_USE_CONSTEVAL 1)$/, + "#elif defined(__apple_build_version__) && __apple_build_version__ >= 17000000L\n# define FMT_USE_CONSTEVAL 0 // Patched for Xcode 26+ consteval compat\n\\1" + ) + original_mode = File.stat(fmt_base_header).mode + File.chmod(original_mode | 0200, fmt_base_header) + File.write(fmt_base_header, content) + File.chmod(original_mode, fmt_base_header) + else + Pod::UI.warn "fmt consteval patch: expected pattern not found in #{fmt_base_header}. " \ + "The fmt library may have changed — Xcode 26+ builds may fail." + end + end + end end end diff --git a/benchmarks/ios/Podfile.lock b/benchmarks/ios/Podfile.lock index 72a37686c..85b51e610 100644 --- a/benchmarks/ios/Podfile.lock +++ b/benchmarks/ios/Podfile.lock @@ -1834,6 +1834,49 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - RNSVG (15.12.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - RNSVG/common (= 15.12.1) + - Yoga + - RNSVG/common (15.12.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.11.18.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - SocketRocket (0.7.1) - Yoga (0.0.0) @@ -1915,6 +1958,7 @@ DEPENDENCIES: - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - "RNCPicker (from `../node_modules/@react-native-picker/picker`)" - RNScreens (from `../node_modules/react-native-screens`) + - RNSVG (from `../node_modules/react-native-svg`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) SPEC REPOS: @@ -2083,6 +2127,8 @@ EXTERNAL SOURCES: :path: "../node_modules/@react-native-picker/picker" RNScreens: :path: "../node_modules/react-native-screens" + RNSVG: + :path: "../node_modules/react-native-svg" Yoga: :path: "../node_modules/react-native/ReactCommon/yoga" @@ -2109,7 +2155,7 @@ SPEC CHECKSUMS: KSCrash: 80e1e24eaefbe5134934ae11ca8d7746586bc2ed OpenTelemetry-Swift-Api: 3d77582ab6837a63b65bf7d2eacc57d8f2595edd OpenTelemetry-Swift-Sdk: 69d60f0242e830366e359481edd575d6776eb983 - RCT-Folly: e78785aa9ba2ed998ea4151e314036f6c49e6d82 + RCT-Folly: 36fe2295e44b10d831836cc0d1daec5f8abcf809 RCTDeprecation: be794de7dc6ed8f9f7fbf525f86e7651b8b68746 RCTRequired: a83787b092ec554c2eb6019ff3f5b8d125472b3b RCTTypeSafety: 48ad3c858926b1c46f46a81a58822b476e178e2c @@ -2173,9 +2219,10 @@ SPEC CHECKSUMS: ReactCommon: 547db015202a80a5b3e7e041586ea54c4a087180 RNCPicker: ffbd7b9fc7c1341929e61dbef6219f7860f57418 RNScreens: 0f01bbed9bd8045a8d58e4b46993c28c7f498f3c + RNSVG: 082ae2874b288a96c95326c4f6029793f7009d4b SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: e14bad835e12b6c7e2260fc320bd00e0f4b45add + Yoga: 66a9a23a82dd4081b393babe509967097759b3d6 -PODFILE CHECKSUM: 977a21a62f5373f05419c7d794a1a33a54f85bf4 +PODFILE CHECKSUM: 577d6cf3071879187b7e93b4f4af22b74b1895f3 COCOAPODS: 1.16.2 diff --git a/benchmarks/metro.config.js b/benchmarks/metro.config.js index 84b107529..80b2747c5 100644 --- a/benchmarks/metro.config.js +++ b/benchmarks/metro.config.js @@ -15,18 +15,30 @@ const modules = Object.keys({ * * @type {import('metro-config').MetroConfig} */ +const defaultConfig = getDefaultConfig(__dirname); + const config = { projectRoot: __dirname, watchFolders: [ root ], resetCache: true, + // Route .svg files through react-native-svg-transformer so they are importable + // as React components (used by Session Replay SVG file-import test cases). + transformer: { + babelTransformerPath: require.resolve('react-native-svg-transformer') + }, // We need to make sure that only one version is loaded for peerDependencies // So we denylist them at the root, and alias them to the versions in example's node_modules // This block is very important, because otherwise things like React can be packed multiple times // while it should be only one React instance in the runtime. exclusionList relies on the modules which are // declared as peer dependencies in the core package. resolver: { + // Remove svg from asset extensions and add it to source extensions so Metro + // sends it through the transformer rather than copying it as a static asset. + assetExts: defaultConfig.resolver.assetExts.filter(ext => ext !== 'svg'), + sourceExts: [...defaultConfig.resolver.sourceExts, 'svg'], + blacklistRE: exclusionList( modules.map( m => @@ -42,4 +54,4 @@ const config = { }, }; -module.exports = mergeConfig(getDefaultConfig(__dirname), config); +module.exports = mergeConfig(defaultConfig, config); diff --git a/benchmarks/package.json b/benchmarks/package.json index 58fe90d63..0487960da 100644 --- a/benchmarks/package.json +++ b/benchmarks/package.json @@ -34,6 +34,7 @@ "react-native-config": "1.5.5", "react-native-safe-area-context": "5.4.0", "react-native-screens": "4.10.0", + "react-native-svg": "15.12.1", "react-native-webview": "13.15.0" }, "devDependencies": { @@ -41,6 +42,7 @@ "@babel/preset-env": "7.25.3", "@babel/runtime": "^7.26.10", "@datadog/datadog-ci": "5.11.0", + "@datadog/mobile-react-native-babel-plugin": "workspace:packages/react-native-babel-plugin", "@react-native-community/cli": "15.0.1", "@react-native-community/cli-platform-android": "15.0.1", "@react-native-community/cli-platform-ios": "15.0.1", @@ -53,6 +55,7 @@ "eslint": "8.19.0", "jest": "29.6.3", "prettier": "2.8.8", + "react-native-svg-transformer": "^1.5.0", "react-test-renderer": "19.0.0", "typescript": "5.0.4" }, diff --git a/benchmarks/src/scenario/SessionReplay/UIDetailView.tsx b/benchmarks/src/scenario/SessionReplay/UIDetailView.tsx index a95ae9809..1ce7f2e20 100644 --- a/benchmarks/src/scenario/SessionReplay/UIDetailView.tsx +++ b/benchmarks/src/scenario/SessionReplay/UIDetailView.tsx @@ -21,6 +21,7 @@ import Webview from './component/WebView'; import Sliders from './component/Sliders'; import SectionList from './component/SectionList'; import Modal from './component/Modal'; +import SvgTestCases from './component/Svg'; type UIDetailViewRouteProp = RouteProp; @@ -59,6 +60,8 @@ function UIDetailView(): React.JSX.Element { return ; case UIElement.Modal: return ; + case UIElement.Svg: + return ; default: return ( diff --git a/benchmarks/src/scenario/SessionReplay/component/Svg.tsx b/benchmarks/src/scenario/SessionReplay/component/Svg.tsx new file mode 100644 index 000000000..581e894dd --- /dev/null +++ b/benchmarks/src/scenario/SessionReplay/component/Svg.tsx @@ -0,0 +1,689 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +import React from 'react'; +// Alias react-native's Text so the SVG Text component keeps its real name. +// The Babel plugin converts JSX element names via convertAttributeCasing — 'Text' → 'text' +// which is in svgElements, but 'SvgText' → 'svgText' which is not, producing a +// mismatched opening/closing tag pair that breaks svgo. +import { ScrollView, Text as RNText, View, StyleSheet, Animated } from 'react-native'; +import { + Svg, Circle, Rect, Ellipse, Polygon, Path, Line, Polyline, + G, Defs, LinearGradient, RadialGradient, Stop, ClipPath, + Text +} from 'react-native-svg'; + +import { + SessionReplayView, + ImagePrivacyLevel +} from '@datadog/mobile-react-native-session-replay'; + +import StarSvg from './assets/star.svg'; +import { HeartIcon, ShieldIcon } from './assets/icons'; + +// Module-level const used in Case D1 to test findIdentifierInScope +const BADGE_SIZE = 72; + +// Used in Group I to reproduce the customer's `AnimatedPath` case without pulling in +// react-native-reanimated. `Animated.createAnimatedComponent` is core React Native and +// produces the exact same shape that breaks the Babel plugin: a capitalized custom +// component whose first-letter-lowercased tag name ('AnimatedPath' -> 'animatedPath') +// is not in svgElements. +const AnimatedPath = Animated.createAnimatedComponent(Path); + +// ───────────────────────────────────────────────────────────── +// GROUP A — Basic static shapes +// All attributes are string literals. All should appear in replay. +// ───────────────────────────────────────────────────────────── + +/** A1: Simple filled circle */ +function BlueCircle() { + return ( + + + + ); +} + +/** A2: Rounded rectangle */ +function RedRoundedRect() { + return ( + + + + ); +} + +/** A3: Ellipse */ +function GreenEllipse() { + return ( + + + + ); +} + +/** A4: Polygon — 5-pointed star (10 vertices: 5 outer + 5 inner) */ +function OrangeStar() { + return ( + + + + ); +} + +/** A5: Path — chevron/arrow */ +function PurpleArrow() { + return ( + + + + ); +} + +/** A6: Line and Polyline */ +function TealLines() { + return ( + + + + + ); +} + +// ───────────────────────────────────────────────────────────── +// GROUP B — SVG features (defs, gradients, groups, text, clip) +// All static. All should appear in replay. +// ───────────────────────────────────────────────────────────── + +/** B1: Linear gradient via Defs */ +function GradientRect() { + return ( + + + + + + + + + + ); +} + +/** B2: Radial gradient */ +function RadialGlow() { + return ( + + + + + + + + + + ); +} + +/** B3: Group — traffic light */ +function TrafficLight() { + return ( + + + + + + + ); +} + +/** B4: ClipPath — clipped circle */ +function ClippedCircle() { + return ( + + + + + + + + + + ); +} + +/** B5: SVG Text element */ +function SvgLabel() { + return ( + + + + DD + + + ); +} + +// ───────────────────────────────────────────────────────────── +// GROUP C — Transform attributes +// translateX/Y, rotate are converted from RN props to SVG transform. +// ───────────────────────────────────────────────────────────── + +/** C1: Group with translateX/Y (RN-style transform props) */ +function TranslatedGroup() { + return ( + + + {/* G with RN transform props — converted to SVG transform="translate(40,40)" */} + + + + + ); +} + +/** C2: Rotated shape via rotate prop */ +function RotatedDiamond() { + return ( + + + + + + ); +} + +/** C3: Scaled element */ +function ScaledStar() { + return ( + + + + + + ); +} + +// ───────────────────────────────────────────────────────────── +// GROUP D — Dimension handling +// ───────────────────────────────────────────────────────────── + +/** D1: Module-level const dimensions — resolved by findIdentifierInScope */ +function ConstSizedBadge() { + return ( + + + + + ); +} + +/** D2: ViewBox only — no explicit width/height. Native layer injects them from view bounds. */ +function ViewBoxOnlyIcon() { + return ( + + + + + ); +} + +// ───────────────────────────────────────────────────────────── +// GROUP E — Dynamic props +// Non-resolvable props are stripped before svgo runs (Fix 1). +// Shape is captured; dynamic attribute is absent in replay. +// ───────────────────────────────────────────────────────────── + +/** E1: Dynamic fill on root element — fill dropped, container captured */ +function DynamicRootFill({ accentColor }: { accentColor: string }) { + return ( + + + + ); +} + +/** E2: Dynamic fill on child — child fill dropped, shape still captured */ +function DynamicChildFill({ iconColor }: { iconColor: string }) { + return ( + + + + + ); +} + +/** E3: Dynamic stroke and strokeWidth — both dropped, base shape preserved */ +function DynamicStrokeRect({ borderColor, borderWidth }: { borderColor: string; borderWidth: number }) { + return ( + + + + ); +} + +/** E4: Mix of static fill + dynamic opacity — fill kept, opacity dropped */ +function DynamicOpacityCircle({ fadeLevel }: { fadeLevel: number }) { + return ( + + + + ); +} + +// ───────────────────────────────────────────────────────────── +// GROUP F — File imports +// LocalSvgHandler reads the SVG file from disk (Fix 2, 3, 4). +// ───────────────────────────────────────────────────────────── + +/** F1: Default import of a local .svg file */ +function LocalStarImport() { + return ; +} + +/** F2: Named import from barrel file (export { default as HeartIcon }) + * Tests Fix 6: ExportNamedDeclaration uses spec.exported.name, not spec.local.name */ +function BarrelHeartImport() { + return ; +} + +/** F3: Second barrel export — ShieldIcon */ +function BarrelShieldImport() { + return ; +} + +// ───────────────────────────────────────────────────────────── +// GROUP I — Unsupported nested elements (customer repro) +// AnimatedPath isn't a recognized SVG tag, so it's now spliced out of the tree +// instead of breaking generation for the whole parent (see RNSvgHandler). +// ───────────────────────────────────────────────────────────── + +/** + * I1: The checkmark stroke uses AnimatedPath instead of Path. SHOULD appear in replay + * as the green circle background WITHOUT the white checkmark — the unsupported + * AnimatedPath element is removed, but the rest of the SVG is still captured. Compare + * against I2: same circle, but I2 additionally shows the checkmark since it uses a + * plain, supported . + */ +function BrokenAnimatedCheckmark() { + const opacity = React.useRef(new Animated.Value(1)).current; + return ( + + + + + ); +} + +/** + * I2: Control — identical icon to I1, but the checkmark uses plain . SHOULD + * appear in replay. Compare directly against I1: same shape, only the wrapper + * component differs. + */ +function ControlCheckmark() { + return ( + + + + + ); +} + +// ───────────────────────────────────────────────────────────── +// Screen +// ───────────────────────────────────────────────────────────── + +type CaseProps = { + label: string; + sublabel?: string; + children: React.ReactNode; +}; + +function Case({ label, sublabel, children }: CaseProps) { + return ( + + {children} + + {label} + {sublabel ? {sublabel} : null} + + + ); +} + +function Section({ title, children }: { title: string; children: React.ReactNode }) { + return ( + + {title} + {children} + + ); +} + +export default function SvgTestCases() { + return ( + + Session Replay SVG Test Screen + + All cases in Groups A–D should appear in replay.{'\n'} + Group E: shape appears, dynamic attr is absent.{'\n'} + Group F: appears after buildSvgMap fixes.{'\n'} + Group G: privacy overrides — verify masking behavior in replay.{'\n'} + Group I: I1 shows circle only (checkmark removed), I2 shows circle + checkmark. + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + +
+ +
+ + + + + + + + + +
+ + {/* ─── GROUP G — Privacy interaction ─── */} + {/* + These cases test whether the native SDK's view-level privacy mechanism + (DdPrivacyView view tags / SessionReplayPrivacyOverrides) correctly + interacts with SVG wireframes produced by the custom mapper/recorder. + + The Babel plugin wraps each SVG in SessionReplayView.Privacy (no imagePrivacy + set — inherits session level). The outer wrappers here set explicit per-view + overrides. Whether those cascade to the inner SVG wireframe depends on the + native SDK's privacy propagation behaviour. + + Expected results are annotated on each case so you can verify in replay. + */} +
+ {/* G1: MaskNone override. EXPECTED: SVG content VISIBLE. */} + + + + + + + {/* + G2: MaskAll override. EXPECTED: SVG content MASKED. If it still shows, + the custom mapper bypasses the view-level privacy override (known gap: + SvgViewMapper hardcodes isEmpty:false on Android; SvgViewRecorder never + checks imagePrivacy on iOS). + */} + + + + + + + {/* G3: hide=true. EXPECTED: entire view ABSENT from replay (no bounding box). */} + + + + + + + {/* + G4: imagePrivacy = MASK_NON_BUNDLED_ONLY, matching the customer's session + setting. SVGs come from assets.bin (bundled), so EXPECTED: VISIBLE. + */} + + + + + + + {/* + G5: nested privacy, inner MaskNone inside outer MaskAll. EXPECTED: MASKED + (outer MaskAll wins — inner MaskNone can't "unlock" it). + */} + + + + + + + +
+ +
+ + + + + + +
+ + + J — Known gaps (will NOT appear in replay) + + • SVG components rendered by node_modules design-system packages are excluded + from the Babel transform by design (index.ts:81,128). No DdPrivacyView wrapper + is ever created around them.{'\n\n'} + • SvgUri is listed in svgSupportedNames but its handler is commented out — + <SvgUri uri="..." /> is silently skipped. + + +
+ ); +} + +const styles = StyleSheet.create({ + container: { + padding: 16, + paddingBottom: 48, + gap: 24, + }, + heading: { + fontSize: 20, + fontWeight: 'bold', + }, + subtitle: { + fontSize: 12, + color: '#555', + lineHeight: 18, + }, + section: { + gap: 8, + }, + sectionTitle: { + fontSize: 14, + fontWeight: '700', + color: '#2c3e50', + borderBottomWidth: 1, + borderBottomColor: '#bdc3c7', + paddingBottom: 4, + }, + sectionGrid: { + flexDirection: 'row', + flexWrap: 'wrap', + gap: 12, + }, + case: { + alignItems: 'center', + gap: 4, + minWidth: 80, + }, + caseContent: { + alignItems: 'center', + justifyContent: 'center', + borderWidth: 1, + borderColor: '#ecf0f1', + borderRadius: 6, + padding: 4, + backgroundColor: '#fafafa', + }, + caseMeta: { + alignItems: 'center', + maxWidth: 100, + }, + caseLabel: { + fontSize: 11, + fontWeight: '600', + textAlign: 'center', + color: '#2c3e50', + }, + caseSublabel: { + fontSize: 10, + color: '#7f8c8d', + textAlign: 'center', + }, + gapNote: { + backgroundColor: '#fef9e7', + borderLeftWidth: 4, + borderLeftColor: '#f39c12', + padding: 12, + borderRadius: 4, + gap: 8, + }, + gapTitle: { + fontSize: 13, + fontWeight: '700', + color: '#8e6300', + }, + gapBody: { + fontSize: 12, + color: '#7d6608', + lineHeight: 18, + }, +}); diff --git a/benchmarks/src/scenario/SessionReplay/component/assets/heart.svg b/benchmarks/src/scenario/SessionReplay/component/assets/heart.svg new file mode 100644 index 000000000..e095d98d0 --- /dev/null +++ b/benchmarks/src/scenario/SessionReplay/component/assets/heart.svg @@ -0,0 +1,3 @@ + + + diff --git a/benchmarks/src/scenario/SessionReplay/component/assets/icons.ts b/benchmarks/src/scenario/SessionReplay/component/assets/icons.ts new file mode 100644 index 000000000..5662e288f --- /dev/null +++ b/benchmarks/src/scenario/SessionReplay/component/assets/icons.ts @@ -0,0 +1,11 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +// Barrel file used to test Fix 6: ExportNamedDeclaration must use spec.exported.name +// not spec.local.name, otherwise 'export { default as HeartIcon }' stores 'default' +// as the key in localSvgMap instead of 'HeartIcon', and is never found. +export { default as HeartIcon } from './heart.svg'; +export { default as ShieldIcon } from './shield.svg'; diff --git a/benchmarks/src/scenario/SessionReplay/component/assets/shield.svg b/benchmarks/src/scenario/SessionReplay/component/assets/shield.svg new file mode 100644 index 000000000..0171eebd0 --- /dev/null +++ b/benchmarks/src/scenario/SessionReplay/component/assets/shield.svg @@ -0,0 +1,4 @@ + + + + diff --git a/benchmarks/src/scenario/SessionReplay/component/assets/star.svg b/benchmarks/src/scenario/SessionReplay/component/assets/star.svg new file mode 100644 index 000000000..39d06c8cd --- /dev/null +++ b/benchmarks/src/scenario/SessionReplay/component/assets/star.svg @@ -0,0 +1,3 @@ + + + diff --git a/benchmarks/src/scenario/SessionReplay/types.ts b/benchmarks/src/scenario/SessionReplay/types.ts index fbcd5a6a5..b071c3f33 100644 --- a/benchmarks/src/scenario/SessionReplay/types.ts +++ b/benchmarks/src/scenario/SessionReplay/types.ts @@ -24,4 +24,5 @@ export enum UIElement { WebView = "WebView", SectionList = "SectionList", Modal = "Modal", + Svg = "Svg", } diff --git a/benchmarks/svg.d.ts b/benchmarks/svg.d.ts new file mode 100644 index 000000000..eb2ad40ff --- /dev/null +++ b/benchmarks/svg.d.ts @@ -0,0 +1,13 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +import React from 'react'; +import { SvgProps } from 'react-native-svg'; + +declare module '*.svg' { + const content: React.FC; + export default content; +} diff --git a/yarn.lock b/yarn.lock index ad4e6f9c4..e632937fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5784,6 +5784,146 @@ __metadata: languageName: node linkType: hard +"@svgr/babel-plugin-add-jsx-attribute@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-add-jsx-attribute@npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3fc8e35d16f5abe0af5efe5851f27581225ac405d6a1ca44cda0df064cddfcc29a428c48c2e4bef6cebf627c9ac2f652a096030edb02cf5a120ce28d3c234710 + languageName: node + linkType: hard + +"@svgr/babel-plugin-remove-jsx-attribute@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-remove-jsx-attribute@npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ff992893c6c4ac802713ba3a97c13be34e62e6d981c813af40daabcd676df68a72a61bd1e692bb1eda3587f1b1d700ea462222ae2153bb0f46886632d4f88d08 + languageName: node + linkType: hard + +"@svgr/babel-plugin-remove-jsx-empty-expression@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-remove-jsx-empty-expression@npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0fb691b63a21bac00da3aa2dccec50d0d5a5b347ff408d60803b84410d8af168f2656e4ba1ee1f24dab0ae4e4af77901f2928752bb0434c1f6788133ec599ec8 + languageName: node + linkType: hard + +"@svgr/babel-plugin-replace-jsx-attribute-value@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-replace-jsx-attribute-value@npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 1edda65ef4f4dd8f021143c8ec276a08f6baa6f733b8e8ee2e7775597bf6b97afb47fdeefd579d6ae6c959fe2e634f55cd61d99377631212228c8cfb351b8921 + languageName: node + linkType: hard + +"@svgr/babel-plugin-svg-dynamic-title@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-svg-dynamic-title@npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 876cec891488992e6a9aebb8155e2bea4ec461b4718c51de36e988e00e271c6d9d01ef6be17b9effd44b2b3d7db0b41c161a5904a46ae6f38b26b387ad7f3709 + languageName: node + linkType: hard + +"@svgr/babel-plugin-svg-em-dimensions@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-svg-em-dimensions@npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: be0e2d391164428327d9ec469a52cea7d93189c6b0e2c290999e048f597d777852f701c64dca44cd45b31ed14a7f859520326e2e4ad7c3a4545d0aa235bc7e9a + languageName: node + linkType: hard + +"@svgr/babel-plugin-transform-react-native-svg@npm:8.1.0": + version: 8.1.0 + resolution: "@svgr/babel-plugin-transform-react-native-svg@npm:8.1.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 85b434a57572f53bd2b9f0606f253e1fcf57b4a8c554ec3f2d43ed17f50d8cae200cb3aaf1ec9d626e1456e8b135dce530ae047eb0bed6d4bf98a752d6640459 + languageName: node + linkType: hard + +"@svgr/babel-plugin-transform-svg-component@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-transform-svg-component@npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 04e2023d75693eeb0890341c40e449881184663056c249be7e5c80168e4aabb0fadd255e8d5d2dbf54b8c2a6e700efba994377135bfa4060dc4a2e860116ef8c + languageName: node + linkType: hard + +"@svgr/babel-preset@npm:8.1.0": + version: 8.1.0 + resolution: "@svgr/babel-preset@npm:8.1.0" + dependencies: + "@svgr/babel-plugin-add-jsx-attribute": 8.0.0 + "@svgr/babel-plugin-remove-jsx-attribute": 8.0.0 + "@svgr/babel-plugin-remove-jsx-empty-expression": 8.0.0 + "@svgr/babel-plugin-replace-jsx-attribute-value": 8.0.0 + "@svgr/babel-plugin-svg-dynamic-title": 8.0.0 + "@svgr/babel-plugin-svg-em-dimensions": 8.0.0 + "@svgr/babel-plugin-transform-react-native-svg": 8.1.0 + "@svgr/babel-plugin-transform-svg-component": 8.0.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3a67930f080b8891e1e8e2595716b879c944d253112bae763dce59807ba23454d162216c8d66a0a0e3d4f38a649ecd6c387e545d1e1261dd69a68e9a3392ee08 + languageName: node + linkType: hard + +"@svgr/core@npm:^8.1.0": + version: 8.1.0 + resolution: "@svgr/core@npm:8.1.0" + dependencies: + "@babel/core": ^7.21.3 + "@svgr/babel-preset": 8.1.0 + camelcase: ^6.2.0 + cosmiconfig: ^8.1.3 + snake-case: ^3.0.4 + checksum: da4a12865c7dc59829d58df8bd232d6c85b7115fda40da0d2f844a1a51886e2e945560596ecfc0345d37837ac457de86a931e8b8d8550e729e0c688c02250d8a + languageName: node + linkType: hard + +"@svgr/hast-util-to-babel-ast@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/hast-util-to-babel-ast@npm:8.0.0" + dependencies: + "@babel/types": ^7.21.3 + entities: ^4.4.0 + checksum: 88401281a38bbc7527e65ff5437970414391a86158ef4b4046c89764c156d2d39ecd7cce77be8a51994c9fb3249170cb1eb8b9128b62faaa81743ef6ed3534ab + languageName: node + linkType: hard + +"@svgr/plugin-jsx@npm:^8.1.0": + version: 8.1.0 + resolution: "@svgr/plugin-jsx@npm:8.1.0" + dependencies: + "@babel/core": ^7.21.3 + "@svgr/babel-preset": 8.1.0 + "@svgr/hast-util-to-babel-ast": 8.0.0 + svg-parser: ^2.0.4 + peerDependencies: + "@svgr/core": "*" + checksum: 0418a9780753d3544912ee2dad5d2cf8d12e1ba74df8053651b3886aeda54d5f0f7d2dece0af5e0d838332c4f139a57f0dabaa3ca1afa4d1a765efce6a7656f2 + languageName: node + linkType: hard + +"@svgr/plugin-svgo@npm:^8.1.0": + version: 8.1.0 + resolution: "@svgr/plugin-svgo@npm:8.1.0" + dependencies: + cosmiconfig: ^8.1.3 + deepmerge: ^4.3.1 + svgo: ^3.0.2 + peerDependencies: + "@svgr/core": "*" + checksum: 59d9d214cebaacca9ca71a561f463d8b7e5a68ca9443e4792a42d903acd52259b1790c0680bc6afecc3f00a255a6cbd7ea278a9f625bac443620ea58a590c2d0 + languageName: node + linkType: hard + "@swc/core-darwin-arm64@npm:1.15.32": version: 1.15.32 resolution: "@swc/core-darwin-arm64@npm:1.15.32" @@ -7463,6 +7603,7 @@ __metadata: "@babel/runtime": ^7.26.10 "@datadog/datadog-ci": 5.11.0 "@datadog/mobile-react-native": "workspace:packages/core" + "@datadog/mobile-react-native-babel-plugin": "workspace:packages/react-native-babel-plugin" "@datadog/mobile-react-native-session-replay": "workspace:packages/react-native-session-replay" "@datadog/mobile-react-native-webview": "workspace:packages/react-native-webview" "@datadog/mobile-react-navigation": "workspace:packages/react-navigation" @@ -7489,6 +7630,8 @@ __metadata: react-native-config: 1.5.5 react-native-safe-area-context: 5.4.0 react-native-screens: 4.10.0 + react-native-svg: 15.12.1 + react-native-svg-transformer: ^1.5.0 react-native-webview: 13.15.0 react-test-renderer: 19.0.0 typescript: 5.0.4 @@ -8531,6 +8674,23 @@ __metadata: languageName: node linkType: hard +"cosmiconfig@npm:^8.1.3": + version: 8.3.6 + resolution: "cosmiconfig@npm:8.3.6" + dependencies: + import-fresh: ^3.3.0 + js-yaml: ^4.1.0 + parse-json: ^5.2.0 + path-type: ^4.0.0 + peerDependencies: + typescript: ">=4.9.5" + peerDependenciesMeta: + typescript: + optional: true + checksum: dc339ebea427898c9e03bf01b56ba7afbac07fc7d2a2d5a15d6e9c14de98275a9565da949375aee1809591c152c0a3877bb86dbeaf74d5bd5aaa79955ad9e7a0 + languageName: node + linkType: hard + "create-jest@npm:^29.7.0": version: 29.7.0 resolution: "create-jest@npm:29.7.0" @@ -8588,6 +8748,26 @@ __metadata: languageName: node linkType: hard +"css-tree@npm:^1.1.3": + version: 1.1.3 + resolution: "css-tree@npm:1.1.3" + dependencies: + mdn-data: 2.0.14 + source-map: ^0.6.1 + checksum: 79f9b81803991b6977b7fcb1588799270438274d89066ce08f117f5cdb5e20019b446d766c61506dd772c839df84caa16042d6076f20c97187f5abe3b50e7d1f + languageName: node + linkType: hard + +"css-tree@npm:^2.3.1": + version: 2.3.1 + resolution: "css-tree@npm:2.3.1" + dependencies: + mdn-data: 2.0.30 + source-map-js: ^1.0.1 + checksum: 493cc24b5c22b05ee5314b8a0d72d8a5869491c1458017ae5ed75aeb6c3596637dbe1b11dac2548974624adec9f7a1f3a6cf40593dc1f9185eb0e8279543fbc0 + languageName: node + linkType: hard + "css-tree@npm:^3.0.1": version: 3.1.0 resolution: "css-tree@npm:3.1.0" @@ -8975,7 +9155,7 @@ __metadata: languageName: node linkType: hard -"deepmerge@npm:^4.2.2, deepmerge@npm:^4.3.0": +"deepmerge@npm:^4.2.2, deepmerge@npm:^4.3.0, deepmerge@npm:^4.3.1": version: 4.3.1 resolution: "deepmerge@npm:4.3.1" checksum: 2024c6a980a1b7128084170c4cf56b0fd58a63f2da1660dcfe977415f27b17dbe5888668b59d0b063753f3220719d5e400b7f113609489c90160bb9a5518d052 @@ -9193,6 +9373,16 @@ __metadata: languageName: node linkType: hard +"dot-case@npm:^3.0.4": + version: 3.0.4 + resolution: "dot-case@npm:3.0.4" + dependencies: + no-case: ^3.0.4 + tslib: ^2.0.3 + checksum: a65e3519414856df0228b9f645332f974f2bf5433370f544a681122eab59e66038fc3349b4be1cdc47152779dac71a5864f1ccda2f745e767c46e9c6543b1169 + languageName: node + linkType: hard + "dot-prop@npm:^5.1.0": version: 5.3.0 resolution: "dot-prop@npm:5.3.0" @@ -9340,7 +9530,7 @@ __metadata: languageName: node linkType: hard -"entities@npm:^4.2.0": +"entities@npm:^4.2.0, entities@npm:^4.4.0": version: 4.5.0 resolution: "entities@npm:4.5.0" checksum: 853f8ebd5b425d350bffa97dd6958143179a5938352ccae092c62d1267c4e392a039be1bae7d51b6e4ffad25f51f9617531fedf5237f15df302ccfb452cbf2d7 @@ -13529,6 +13719,15 @@ __metadata: languageName: node linkType: hard +"lower-case@npm:^2.0.2": + version: 2.0.2 + resolution: "lower-case@npm:2.0.2" + dependencies: + tslib: ^2.0.3 + checksum: 83a0a5f159ad7614bee8bf976b96275f3954335a84fad2696927f609ddae902802c4f3312d86668722e668bef41400254807e1d3a7f2e8c3eede79691aa1f010 + languageName: node + linkType: hard + "lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": version: 10.4.3 resolution: "lru-cache@npm:10.4.3" @@ -13671,6 +13870,13 @@ __metadata: languageName: node linkType: hard +"mdn-data@npm:2.0.14": + version: 2.0.14 + resolution: "mdn-data@npm:2.0.14" + checksum: 9d0128ed425a89f4cba8f787dca27ad9408b5cb1b220af2d938e2a0629d17d879a34d2cb19318bdb26c3f14c77dd5dfbae67211f5caaf07b61b1f2c5c8c7dc16 + languageName: node + linkType: hard + "mdn-data@npm:2.0.28": version: 2.0.28 resolution: "mdn-data@npm:2.0.28" @@ -13678,6 +13884,13 @@ __metadata: languageName: node linkType: hard +"mdn-data@npm:2.0.30": + version: 2.0.30 + resolution: "mdn-data@npm:2.0.30" + checksum: d6ac5ac7439a1607df44b22738ecf83f48e66a0874e4482d6424a61c52da5cde5750f1d1229b6f5fa1b80a492be89465390da685b11f97d62b8adcc6e88189aa + languageName: node + linkType: hard + "mdn-data@npm:2.12.2": version: 2.12.2 resolution: "mdn-data@npm:2.12.2" @@ -14568,6 +14781,16 @@ __metadata: languageName: node linkType: hard +"no-case@npm:^3.0.4": + version: 3.0.4 + resolution: "no-case@npm:3.0.4" + dependencies: + lower-case: ^2.0.2 + tslib: ^2.0.3 + checksum: 0b2ebc113dfcf737d48dde49cfebf3ad2d82a8c3188e7100c6f375e30eafbef9e9124aadc3becef237b042fd5eb0aad2fd78669c20972d045bbe7fea8ba0be5c + languageName: node + linkType: hard + "nocache@npm:^3.0.1": version: 3.0.4 resolution: "nocache@npm:3.0.4" @@ -15652,6 +15875,13 @@ __metadata: languageName: node linkType: hard +"path-dirname@npm:^1.0.2": + version: 1.0.2 + resolution: "path-dirname@npm:1.0.2" + checksum: 0d2f6604ae05a252a0025318685f290e2764ecf9c5436f203cdacfc8c0b17c24cdedaa449d766beb94ab88cc7fc70a09ec21e7933f31abc2b719180883e5e33f + languageName: node + linkType: hard + "path-exists@npm:^3.0.0": version: 3.0.0 resolution: "path-exists@npm:3.0.0" @@ -15737,7 +15967,7 @@ __metadata: languageName: node linkType: hard -"picocolors@npm:^1.0.1, picocolors@npm:^1.1.1": +"picocolors@npm:^1.0.0, picocolors@npm:^1.0.1, picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" checksum: e1cf46bf84886c79055fdfa9dcb3e4711ad259949e3565154b004b260cd356c5d54b31a1437ce9782624bf766272fe6b0154f5f0c744fb7af5d454d2b60db045 @@ -16538,6 +16768,35 @@ __metadata: languageName: node linkType: hard +"react-native-svg-transformer@npm:^1.5.0": + version: 1.5.3 + resolution: "react-native-svg-transformer@npm:1.5.3" + dependencies: + "@svgr/core": ^8.1.0 + "@svgr/plugin-jsx": ^8.1.0 + "@svgr/plugin-svgo": ^8.1.0 + path-dirname: ^1.0.2 + peerDependencies: + react-native: ">=0.59.0" + react-native-svg: ">=12.0.0" + checksum: 41181b96726a8dcb6ee2ad87bf9d5280bf7234a9abd6c2f2eae225c84d558e631fc1a1035dff67b174f2684449fc84dfd05153aeee49698b8265f63bae41459a + languageName: node + linkType: hard + +"react-native-svg@npm:15.12.1": + version: 15.12.1 + resolution: "react-native-svg@npm:15.12.1" + dependencies: + css-select: ^5.1.0 + css-tree: ^1.1.3 + warn-once: 0.1.1 + peerDependencies: + react: "*" + react-native: "*" + checksum: 5761d3b37e5adb4e58945146b210009decf0a436c194fc3dc5c025581aa5c602b5698d7eea493ddf9d31b4d90433fa54eaf1ed0cec721ab820cf895e1cf9614a + languageName: node + linkType: hard + "react-native-webview@npm:13.12.2": version: 13.12.2 resolution: "react-native-webview@npm:13.12.2" @@ -17776,6 +18035,16 @@ __metadata: languageName: node linkType: hard +"snake-case@npm:^3.0.4": + version: 3.0.4 + resolution: "snake-case@npm:3.0.4" + dependencies: + dot-case: ^3.0.4 + tslib: ^2.0.3 + checksum: 0a7a79900bbb36f8aaa922cf111702a3647ac6165736d5dc96d3ef367efc50465cac70c53cd172c382b022dac72ec91710608e5393de71f76d7142e6fd80e8a3 + languageName: node + linkType: hard + "snyk@npm:^1.192.3": version: 1.1304.1 resolution: "snyk@npm:1.1304.1" @@ -18301,6 +18570,30 @@ __metadata: languageName: node linkType: hard +"svg-parser@npm:^2.0.4": + version: 2.0.4 + resolution: "svg-parser@npm:2.0.4" + checksum: b3de6653048212f2ae7afe4a423e04a76ec6d2d06e1bf7eacc618a7c5f7df7faa5105561c57b94579ec831fbbdbf5f190ba56a9205ff39ed13eabdf8ab086ddf + languageName: node + linkType: hard + +"svgo@npm:^3.0.2": + version: 3.3.3 + resolution: "svgo@npm:3.3.3" + dependencies: + commander: ^7.2.0 + css-select: ^5.1.0 + css-tree: ^2.3.1 + css-what: ^6.1.0 + csso: ^5.0.5 + picocolors: ^1.0.0 + sax: ^1.5.0 + bin: + svgo: ./bin/svgo + checksum: ad9c27f19f1f8a6e45bae1b280eb68028da5449359c4daf2c539fc099ab6c76852335d96300a030af4e6866e500159fe80f43a732b8a2905ffa1bd4339da520c + languageName: node + linkType: hard + "svgo@npm:^4.0.1": version: 4.0.1 resolution: "svgo@npm:4.0.1" @@ -18620,7 +18913,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0": +"tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: e4aba30e632b8c8902b47587fd13345e2827fa639e7c3121074d5ee0880723282411a8838f830b55100cbe4517672f84a2472667d355b81e8af165a55dc6203a @@ -19156,7 +19449,7 @@ __metadata: languageName: node linkType: hard -"warn-once@npm:^0.1.0, warn-once@npm:^0.1.1": +"warn-once@npm:0.1.1, warn-once@npm:^0.1.0, warn-once@npm:^0.1.1": version: 0.1.1 resolution: "warn-once@npm:0.1.1" checksum: e6a5a1f5a8dba7744399743d3cfb571db4c3947897875d4962a7c5b1bf2195ab4518c838cb4cea652e71729f21bba2e98dc75686f5fccde0fabbd894e2ed0c0d