Skip to content
Open
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
9 changes: 9 additions & 0 deletions benchmarks/babel.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
// Used by Group H in the SVG test screen to verify aliased imports
// (e.g. '@assets/star.svg') resolve both at runtime (via this plugin
// rewriting the import) and in buildSvgMap's static scan (RUM-12185).
['module-resolver', {
root: ['./src'],
alias: {
'@assets': './src/scenario/SessionReplay/component/assets'
}
}],
['@datadog/mobile-react-native-babel-plugin', {
sessionReplay: {
svgTracking: true
Expand Down
1 change: 1 addition & 0 deletions benchmarks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@react-native/typescript-config": "0.78.2",
"@types/jest": "29.5.13",
"@types/react-test-renderer": "19.0.0",
"babel-plugin-module-resolver": "5.0.2",
"eslint": "8.19.0",
"jest": "29.6.3",
"prettier": "2.8.8",
Expand Down
21 changes: 21 additions & 0 deletions benchmarks/src/scenario/SessionReplay/component/Svg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import {

import StarSvg from './assets/star.svg';
import { HeartIcon, ShieldIcon } from './assets/icons';
// Aliased via the 'module-resolver' babel plugin (see benchmarks/babel.config.js) —
// tests that buildSvgMap resolves aliased local SVG imports (RUM-12185).
import AliasedStarSvg from '@assets/star.svg';

// Module-level const used in Case D1 to test findIdentifierInScope
const BADGE_SIZE = 72;
Expand Down Expand Up @@ -340,6 +343,17 @@ function BarrelShieldImport() {
return <ShieldIcon width={56} height={62} />;
}

// ─────────────────────────────────────────────────────────────
// GROUP H — Aliased import (RUM-12185)
// Same star.svg as F1, but imported via the '@assets' alias configured
// through babel-plugin-module-resolver in babel.config.js.
// ─────────────────────────────────────────────────────────────

/** H1: Default import of a local .svg file via an aliased path */
function AliasedStarImport() {
return <AliasedStarSvg width={64} height={64} />;
}

// ─────────────────────────────────────────────────────────────
// GROUP I — Unsupported nested elements
// AnimatedPath isn't a recognized SVG tag, so it's now spliced out of the tree
Expand Down Expand Up @@ -432,6 +446,7 @@ export default function SvgTestCases() {
Group E: known limitation — absent from replay entirely (see comment).{'\n'}
Group F: appears after buildSvgMap fixes.{'\n'}
Group G: privacy overrides — verify masking behavior in replay.{'\n'}
Group H: aliased import — same star as F1, resolved via '@assets' alias.{'\n'}
Group I: I1 shows circle only (checkmark removed), I2 shows circle + checkmark.
</RNText>

Expand Down Expand Up @@ -522,6 +537,12 @@ export default function SvgTestCases() {
</Case>
</Section>

<Section title="H — Aliased import (RUM-12185)">
<Case label="H1 aliased import" sublabel="@assets/star.svg">
<AliasedStarImport />
</Case>
</Section>

{/* ─── GROUP G — Privacy interaction ─── */}
{/*
These cases test whether the native SDK's view-level privacy mechanism
Expand Down
2 changes: 2 additions & 0 deletions packages/react-native-babel-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"@babel/types": "^7.27.7",
"fast-glob": "^3.3.3",
"svgo": "^4.0.1",
"tsconfig-paths": "^4.2.0",
"uuid": "^8.3.2"
},
"devDependencies": {
Expand All @@ -60,6 +61,7 @@
"@swc/core": "^1.13.21",
"@swc/jest": "^0.2.38",
"@types/jest": "^30.0.0",
"babel-plugin-module-resolver": "^5.0.2",
"jest": "^29.7.0",
"react-native-builder-bob": "0.26.0",
"tsc-alias": "^1.8.16",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { v4 as uuidv4 } from 'uuid';
import { getNodeName } from '../../utils';

import { HandlerResolver } from './handlers/HandlerResolver';
import { PathAliasResolver } from './pathAliasResolver';
import { writeAssetToDisk } from './processing/fs';

// Used when the caller (e.g. the plugin's own pre() hook) doesn't have a more
Expand Down Expand Up @@ -47,13 +48,17 @@ export class ReactNativeSVG {

t: typeof Babel.types | null = null;

private pathAliasResolver: PathAliasResolver;

constructor(
private rootDir: string,
private assetsPath: string,
private saveSvgMapToDisk: boolean = false,
private scanIgnorePatterns: string[] = DEFAULT_SCAN_IGNORE_PATTERNS,
private followSymlinks: boolean = false
) {}
) {
this.pathAliasResolver = new PathAliasResolver(rootDir);
}

setApiTypes(t: typeof Babel.types) {
this.t = t;
Expand Down Expand Up @@ -100,7 +105,11 @@ export class ReactNativeSVG {
}
}

// TODO: Support aliased paths (RUM-12185)
// Drop any alias config cached from a previous buildSvgMap() run --
// otherwise edits to tsconfig.json/babel.config.js made since then
// would be invisible to a reused instance.
this.pathAliasResolver.reset();

const files = glob.sync('**/*.{js,jsx,ts,tsx}', {
cwd: this.rootDir,
absolute: true,
Expand Down Expand Up @@ -136,10 +145,7 @@ export class ReactNativeSVG {
return;
}

const resolved = pathN.resolve(
pathN.dirname(file),
source
);
const resolved = this.resolveImportSource(file, source);
for (const spec of path.node.specifiers) {
const name = getNodeName(this.t, spec.local.name);
if (name) {
Expand All @@ -158,10 +164,7 @@ export class ReactNativeSVG {
return;
}

const resolved = pathN.resolve(
pathN.dirname(file),
source
);
const resolved = this.resolveImportSource(file, source);
for (const spec of path.node.specifiers) {
if (spec.type === 'ExportSpecifier') {
// spec.exported is the name consumers import under
Expand Down Expand Up @@ -211,6 +214,13 @@ export class ReactNativeSVG {
}
}

private resolveImportSource(file: string, source: string): string {
return (
this.pathAliasResolver.resolve(source, file) ??
pathN.resolve(pathN.dirname(file), source)
);
}

/**
* Processes a JSXElement representing an SVG-based component and transforms it into
* a web-compliant SVG string with normalized attributes and extracted dimensions.
Expand Down
Loading