or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

asset-resolution.mdcustom-resolvers.mderror-handling.mdindex.mdpackage-resolution.mdresolution-context.mdresolution-engine.md

asset-resolution.mddocs/

0

# Asset Resolution

1

2

Specialized handling for asset files including support for scaling variants, platform-specific assets, and custom asset resolution strategies. Integrates with Metro bundler's asset system for React Native development.

3

4

## Capabilities

5

6

### Asset Resolution Function

7

8

Core function for resolving asset files with scaling variants.

9

10

```typescript { .api }

11

/**

12

* Resolve a file path as an asset with scaling variants

13

* @param context - Resolution context containing asset configuration

14

* @param filePath - Path to the asset file

15

* @returns Asset resolution with file paths or null if not an asset

16

*/

17

function resolveAsset(

18

context: ResolutionContext,

19

filePath: string

20

): AssetResolution | null;

21

22

interface AssetResolution {

23

readonly type: 'assetFiles';

24

readonly filePaths: ReadonlyArray<string>;

25

}

26

```

27

28

**Usage Example:**

29

30

```javascript

31

const { resolveAsset } = require("metro-resolver/src/resolveAsset");

32

33

const context = {

34

assetExts: ['png', 'jpg', 'gif'],

35

resolveAsset: (dirPath, name, ext) => [

36

`${dirPath}/${name}@3x${ext}`,

37

`${dirPath}/${name}@2x${ext}`,

38

`${dirPath}/${name}${ext}`

39

]

40

};

41

42

const result = resolveAsset(context, '/app/assets/icon.png');

43

// Result:

44

// {

45

// type: 'assetFiles',

46

// filePaths: [

47

// '/app/assets/icon@3x.png',

48

// '/app/assets/icon@2x.png',

49

// '/app/assets/icon.png'

50

// ]

51

// }

52

```

53

54

### ResolveAsset Function Type

55

56

Custom asset resolution function interface.

57

58

```typescript { .api }

59

/**

60

* Function to resolve asset files in a directory

61

* @param dirPath - Directory containing the asset

62

* @param assetName - Base name of the asset (without extension)

63

* @param extension - File extension including the dot

64

* @returns Array of resolved asset file paths, or undefined if not found

65

*/

66

type ResolveAsset = (

67

dirPath: string,

68

assetName: string,

69

extension: string

70

) => ReadonlyArray<string> | undefined;

71

```

72

73

### Asset Detection

74

75

Utility function to determine if a filename represents an asset file.

76

77

```typescript { .api }

78

/**

79

* Check if a filename represents an asset based on extension

80

* @param fileName - Filename to check

81

* @param assetExts - Array of asset extensions (without dots)

82

* @returns True if the file is an asset

83

*/

84

function isAssetFile(fileName: string, assetExts: ReadonlyArray<string>): boolean;

85

```

86

87

**Usage Example:**

88

89

```javascript

90

const { isAssetFile } = require("metro-resolver/src/utils/isAssetFile");

91

92

const assetExts = ['png', 'jpg', 'gif', 'svg'];

93

94

console.log(isAssetFile('icon.png', assetExts)); // true

95

console.log(isAssetFile('script.js', assetExts)); // false

96

console.log(isAssetFile('image.jpeg', assetExts)); // false (jpeg not in list)

97

```

98

99

### Asset Extensions Configuration

100

101

Common asset file extensions supported by Metro bundler.

102

103

```typescript { .api }

104

interface AssetConfiguration {

105

/** File extensions considered as assets (without dots) */

106

readonly assetExts: ReadonlyArray<string>;

107

}

108

```

109

110

**Common Asset Extensions:**

111

112

```javascript

113

const assetExts = [

114

// Images

115

'png', 'jpg', 'jpeg', 'gif', 'webp', 'svg',

116

117

// Fonts

118

'ttf', 'otf', 'woff', 'woff2',

119

120

// Audio/Video

121

'mp3', 'mp4', 'wav', 'mov', 'avi',

122

123

// Documents

124

'pdf', 'zip'

125

];

126

```

127

128

### Scaling Variants

129

130

Support for device pixel ratio scaling variants in asset resolution.

131

132

**Scaling Suffix Patterns:**

133

- `@1x` - Standard density (optional, default)

134

- `@2x` - Double density (Retina)

135

- `@3x` - Triple density (iPhone Plus)

136

- `@4x` - Quad density (future devices)

137

138

**Asset Resolution Priority:**

139

1. Exact density match for target device

140

2. Higher density variants (downscaled)

141

3. Lower density variants (upscaled)

142

4. Base variant (no suffix)

143

144

**Example Asset Structure:**

145

146

```

147

assets/

148

├── icon.png # Base 1x version

149

├── icon@2x.png # 2x version for Retina

150

├── icon@3x.png # 3x version for iPhone Plus

151

└── logo.svg # Vector graphics (no scaling needed)

152

```

153

154

### Platform-Specific Assets

155

156

Support for platform-specific asset variants.

157

158

**Platform Suffix Patterns:**

159

- `.ios.png` - iOS specific

160

- `.android.png` - Android specific

161

- `.web.png` - Web specific

162

- `.native.png` - React Native (both iOS and Android)

163

164

**Combined Platform and Scaling:**

165

166

```

167

assets/

168

├── icon.ios.png

169

├── icon.ios@2x.png

170

├── icon.ios@3x.png

171

├── icon.android.png

172

├── icon.android@2x.png

173

└── icon.android@3x.png

174

```

175

176

### Custom Asset Resolution

177

178

Implementation of custom asset resolution strategies.

179

180

**Example: Custom Resolution with Platform Support:**

181

182

```javascript

183

const customResolveAsset = (dirPath, assetName, extension) => {

184

const fs = require('fs');

185

const path = require('path');

186

187

const variants = [];

188

const scales = ['@3x', '@2x', ''];

189

const platforms = context.platform ? [`.${context.platform}`, '.native', ''] : [''];

190

191

// Check all combinations of platform and scale

192

for (const platform of platforms) {

193

for (const scale of scales) {

194

const filename = `${assetName}${platform}${scale}${extension}`;

195

const fullPath = path.join(dirPath, filename);

196

197

if (fs.existsSync(fullPath)) {

198

variants.push(fullPath);

199

}

200

}

201

}

202

203

return variants.length > 0 ? variants : undefined;

204

};

205

206

const context = {

207

assetExts: ['png', 'jpg', 'gif'],

208

resolveAsset: customResolveAsset,

209

platform: 'ios'

210

};

211

```

212

213

### Asset Scaling Detection

214

215

Detection and handling of pre-scaled assets to prevent double-processing.

216

217

```typescript { .api }

218

/**

219

* Regular expression to detect scaling suffixes in asset names

220

*/

221

const SCALING_REGEX = /@\d+(?:\.\d+)?x$/;

222

```

223

224

**Usage in Resolution:**

225

226

```javascript

227

function resolveAsset(context, filePath) {

228

const path = require('path');

229

const basename = path.basename(filePath, path.extname(filePath));

230

231

// Skip resolution for already-scaled assets

232

if (/@\d+(?:\.\d+)?x$/.test(basename)) {

233

return null;

234

}

235

236

// Proceed with normal asset resolution

237

const dirPath = path.dirname(filePath);

238

const extension = path.extname(filePath);

239

const assetName = path.basename(filePath, extension);

240

241

const assets = context.resolveAsset(dirPath, assetName, extension);

242

return assets ? { type: 'assetFiles', filePaths: assets } : null;

243

}

244

```

245

246

### Asset Resolution Context

247

248

Configuration options specific to asset resolution.

249

250

```typescript { .api }

251

interface AssetResolutionContext {

252

/** File extensions considered as assets */

253

assetExts: ReadonlyArray<string>;

254

255

/** Custom asset resolution function */

256

resolveAsset: ResolveAsset;

257

258

/** Current platform for platform-specific assets */

259

platform?: string;

260

261

/** Whether to prefer native variants over platform-specific ones */

262

preferNativePlatform?: boolean;

263

}

264

```

265

266

### Error Handling for Assets

267

268

Asset-specific error handling and fallback strategies.

269

270

```typescript { .api }

271

interface AssetCandidates {

272

readonly type: 'asset';

273

readonly name: string;

274

}

275

```

276

277

**Error Example:**

278

279

```javascript

280

try {

281

const result = Resolver.resolve(context, './missing-image.png', 'ios');

282

} catch (error) {

283

if (error instanceof Resolver.FailedToResolvePathError) {

284

const { candidates } = error;

285

if (candidates.file?.type === 'asset') {

286

console.log(`Asset not found: ${candidates.file.name}`);

287

}

288

}

289

}

290

```

291

292

### Asset Bundle Integration

293

294

Integration with Metro bundler's asset processing pipeline.

295

296

**Asset Resolution Flow:**

297

1. Detect asset file by extension

298

2. Resolve scaling variants

299

3. Apply platform-specific filtering

300

4. Return ordered array of asset paths

301

5. Metro processes assets for bundling

302

303

**Asset Metadata:**

304

305

```typescript { .api }

306

interface AssetMetadata {

307

name: string;

308

type: string;

309

hash: string;

310

scales: number[];

311

platform?: string;

312

width?: number;

313

height?: number;

314

}

315

```

316

317

### Performance Considerations

318

319

Optimization strategies for asset resolution.

320

321

**Caching Strategy:**

322

323

```javascript

324

class CachedAssetResolver {

325

constructor() {

326

this.cache = new Map();

327

}

328

329

resolveAsset(dirPath, assetName, extension) {

330

const key = `${dirPath}/${assetName}${extension}`;

331

332

if (this.cache.has(key)) {

333

return this.cache.get(key);

334

}

335

336

const result = this.performResolution(dirPath, assetName, extension);

337

this.cache.set(key, result);

338

return result;

339

}

340

341

performResolution(dirPath, assetName, extension) {

342

// Actual resolution logic

343

}

344

}

345

```

346

347

**Batch Resolution:**

348

349

```javascript

350

function batchResolveAssets(context, assetPaths) {

351

const results = new Map();

352

353

for (const assetPath of assetPaths) {

354

try {

355

const result = resolveAsset(context, assetPath);

356

if (result) {

357

results.set(assetPath, result);

358

}

359

} catch (error) {

360

// Handle individual asset errors

361

results.set(assetPath, { error: error.message });

362

}

363

}

364

365

return results;

366

}

367

```

368

369

### Asset Development Workflow

370

371

Best practices for organizing assets in development.

372

373

**Recommended Directory Structure:**

374

375

```

376

src/

377

├── assets/

378

│ ├── images/

379

│ │ ├── icons/

380

│ │ │ ├── home.png

381

│ │ │ ├── home@2x.png

382

│ │ │ └── home@3x.png

383

│ │ └── backgrounds/

384

│ ├── fonts/

385

│ └── sounds/

386

└── components/

387

```

388

389

**Asset Import Patterns:**

390

391

```javascript

392

// Direct asset imports

393

import icon from '../assets/images/icons/home.png';

394

395

// Dynamic asset imports

396

const getAsset = (name) => require(`../assets/images/${name}.png`);

397

398

// Platform-specific imports

399

import iconIOS from '../assets/images/icon.ios.png';

400

import iconAndroid from '../assets/images/icon.android.png';

401

```