npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rn-typed-assets

v1.4.0

Published

React Native typed asset registry — generates type-safe Assets/Svgs/Lotties constants, audits unused files, and rewrites stale imports automatically

Downloads

654

Readme

rn-typed-assets

Get rid of all string-based asset references in React Native — forever.

rn-typed-assets scans your asset directories and generates a typed TypeScript registry, so that every image, SVG, and Lottie animation is accessed through a named constant instead of a brittle require('../../../assets/icon.png') path. If the file doesn't exist, the import fails at generation time — not at runtime.

// Before
<Image source={require('../../../assets/toast/info.png')} />
<LottieView source={require('../../utils/loading.json')} />

// After
import { Assets, Lotties } from '../generated/assets.gen';

<Image source={Assets.toast.info} />
<LottieView source={Lotties.loading} />

Inspiration

This tool is directly inspired by two codegen tools from other ecosystems:

  • SwiftGen — the canonical Swift code generator for Xcode resources. SwiftGen pioneered the pattern of scanning asset catalogs and emitting fully type-safe Swift enums, eliminating string-based UIImage(named:) calls entirely.

  • FlutterGen — the Flutter equivalent. FlutterGen reads pubspec.yaml assets and emits Dart classes with typed getters, so Assets.images.profile.image() replaces Image.asset('assets/images/profile.jpg').

rn-typed-assets brings the same discipline to React Native: a manifest-driven, deterministic generator that makes unused or mistyped asset references a compile-time (or generation-time) problem rather than a runtime crash.

Features

  • Zero-config for standard RN projects — works out of the box with the default src/assets layout
  • Three built-in asset typesimage (PNG/JPG/WebP), svg, lottie (JSON)
  • Deterministic, sorted output — generated files are stable across runs and friendly to code review
  • Collision detection — files that normalize to the same key (e.g. harini-cry.png and harini_cry.png) are caught at generation time with a clear error
  • Manifest-backed audit — find and optionally delete unused assets that are no longer referenced anywhere in source
  • Automatic source rewritinggenerate --inplace and organize rewrite every require() call, ES module import statement, and stale dotted reference in your source files to match the regenerated manifest
  • Content-hash diffing — each manifest entry now carries a SHA-1 hash of the file's bytes, enabling the codemod to track files that move or are renamed without content changes
  • Asset organizationorganize migrates flat or legacy asset directories into canonical subdirectories (images/, svg/, lottie/) in one command
  • Configurable — override paths, export names, TypeScript type imports, or add entirely new asset types via rn-typed-assets.config.js
  • Programmatic API — every function is exported; integrate the generator into your own scripts or build tools
  • Lightweight — zero runtime dependencies; typescript is a peer dependency used only by the audit command

How It Works

src/assets/                        commands/
  toast/info.png    ──┐            generate
  toast/warning.png ──┤  scan  ──► assets.gen.ts        (typed require() registry)
  lottie/loading.json─┤            assets.manifest.json (path ↔ key + contentHash)
  svg/logo.svg ───────┘

                                   generate --inplace
src/**/*.{ts,tsx,js,jsx} ◄──────  rewrite require() → Assets.*
                                   migrate import X → Assets.*
                                   update stale dotted references

                                   organize src/assets
  svg/logo.svg ───────► svg/logo.svg   (move to canonical dir)
                          → regenerate + rewrite sources

                                   audit
src/**/*.{ts,tsx,js,jsx} ──────►  compare usages in source
                                   vs. entries in manifest
                                   → report (or delete) unused assets

Generation pipeline

  1. Scan — For each enabled asset type, recursively list files under the configured rootDir.
  2. Normalize — Convert each filename to a stable camelCase key (harini-cry.pnghariniCry, 1.pngn1).
  3. Build registry tree — Assemble a nested object tree from path segments. Detect and resolve branch/leaf collisions automatically (e.g. a file named point.png alongside a point/ directory becomes pointAsset).
  4. Emit — Write assets.gen.ts (a typed as const object) and assets.manifest.json (a stable index of every key ↔ file mapping).

Audit pipeline

  1. Parse source files — Use the TypeScript Compiler API to walk the AST of every .ts/.tsx/.js/.jsx file under sourceRoots.
  2. Collect usages — Detect Assets.*, Lotties.*, and Svgs.* property-access chains. Also collect legacy require('../assets/...') calls for files still in migration.
  3. Compare against manifest — Any key present in the manifest but unreferenced in source is reported as unused. Any key used in source but absent from the manifest is reported as unknown.
  4. Optionally fix — With --fix, delete the unused files and regenerate the manifest.

Installation

npm install --save-dev rn-typed-assets

typescript must be available in the project (it is a peerDependency). Most React Native projects already have it as a dev dependency.

Quick Start

1. Add scripts to package.json

{
  "scripts": {
    "assets:generate": "rn-typed-assets generate",
    "assets:generate:inplace": "rn-typed-assets generate --inplace",
    "assets:organize": "rn-typed-assets organize src/assets",
    "assets:audit": "rn-typed-assets audit",
    "assets:audit:fix": "rn-typed-assets audit --fix"
  }
}

2. Run the generator

npm run assets:generate

This writes two files to src/generated/:

  • assets.gen.ts — the typed registry you import in your components
  • assets.manifest.json — the index used by the audit command (commit this file)

3. Import the registry

import { Assets, Lotties, Svgs } from './generated/assets.gen';

// Images
<Image source={Assets.toast.info} />
<Image source={Assets.coupang.hariniCry} />

// Lottie
<LottieView source={Lotties.loading} autoPlay loop />

// SVG (with react-native-svg)
<SvgUri source={Svgs.logo} />

4. Audit for unused assets

npm run assets:audit          # report unused entries
npm run assets:audit -- --fix # delete unused files and regenerate

CLI Reference

rn-typed-assets <command> [options]

generate

Scan asset directories and emit assets.gen.ts + assets.manifest.json.

| Flag | Description | Default | | ----------------- | ------------------------------------------------------------ | ----------------------------- | | --types <types> | Comma-separated list of asset types to include | image,svg,lottie | | --inplace | Rewrite source files to replace stale references after regen | false | | --root <path> | Project root directory | cwd | | --config <path> | Path to config file | ./rn-typed-assets.config.js |

rn-typed-assets generate
rn-typed-assets generate --inplace          # rewrite sources after regen
rn-typed-assets generate --types=image,lottie
rn-typed-assets generate --root=/path/to/project

When --inplace is set, the tool loads the previous manifest before writing the new one, diffs them by content hash, and rewrites every source file that contains a stale require() path, an ES module import statement for an asset file, or a renamed dotted symbol reference.

ES module imports are fully migrated: the import declaration is removed, all usages of the imported binding are replaced with the generated symbol, and the import { … } from '…/assets.gen' statement is added or extended automatically.

// Before
import logo from '../assets/logo.png';
const el = <Image source={logo} />;

// After (generated by --inplace)
import { Assets } from '../generated/assets.gen';
const el = <Image source={Assets.logo} />;

organize

Move asset files from legacy or flat directories into canonical subdirectories, then regenerate and rewrite sources.

rn-typed-assets organize <assetsDir>

| Argument | Description | | ------------- | ---------------------------------------------------- | | <assetsDir> | Path to the asset root to organize (relative to cwd) |

| Flag | Description | Default | | ----------------- | ---------------------- | ----------------------------- | | --types <types> | Asset types to move | image,svg,lottie | | --root <path> | Project root directory | cwd | | --config <path> | Path to config file | ./rn-typed-assets.config.js |

rn-typed-assets organize src/assets

Canonical subdirectory layout after organize:

src/assets/
  images/    ← PNG/JPG/WebP
  svg/       ← SVG
  lottie/    ← Lottie JSON

Files already in the canonical location are left untouched. Source files with require() calls pointing to the old paths are rewritten automatically.

audit

Compare manifest against actual source-file usages.

| Flag | Description | Default | | ----------------- | ----------------------------------------------------- | ----------------------------- | | --types <types> | Asset types to include in the audit | image,svg,lottie | | --fix | Delete unused asset files and regenerate the manifest | false | | --root <path> | Project root directory | cwd | | --config <path> | Path to config file | ./rn-typed-assets.config.js |

rn-typed-assets audit
rn-typed-assets audit --fix
rn-typed-assets audit --types=image

Configuration

Create rn-typed-assets.config.js in your project root to override any default. The file is optional — omitting it is equivalent to accepting all defaults.

// rn-typed-assets.config.js
module.exports = {
  // Where to write assets.gen.ts and assets.manifest.json
  // Default: 'src/generated'
  outputDir: 'src/generated',

  // Directories and entry files scanned by the audit command
  // Default: ['src', 'App.tsx', 'index.js']
  sourceRoots: ['src', 'App.tsx', 'index.js'],

  // Per-type configuration (all fields are optional overrides)
  types: {
    image: {
      rootDir: 'src/assets', // scan root
      extensions: ['.png', '.jpg', '.jpeg', '.webp'], // included extensions
      exportName: 'Assets', // export const Assets = ...
      typeImport: {
        typeName: 'ImageRequireSource', // TypeScript type name
        from: 'react-native', // import source
      },
    },
    svg: {
      rootDir: 'src/assets/svg',
      extensions: ['.svg'],
      exportName: 'Svgs',
      inlineType: 'unknown', // emits: export type SvgsAssetSource = unknown
    },
    lottie: {
      rootDir: 'src/assets/lottie',
      extensions: ['.json'],
      exportName: 'Lotties',
      typeImport: {
        typeName: 'AnimationObject',
        from: 'lottie-react-native',
      },
    },
  },
};

Adding a custom asset type

Any type not in the defaults can be added under types. The audit command discovers it automatically via the exportNametype reverse map.

module.exports = {
  types: {
    font: {
      rootDir: 'src/assets/fonts',
      extensions: ['.ttf', '.otf'],
      exportName: 'Fonts',
      inlineType: 'string', // emits: export type FontsAssetSource = string
    },
  },
};

Generated Output

Given this asset tree:

src/assets/
  toast/
    info.png
    warning.png
  coupang/
    harini-cry.png
  lottie/
    loading.json
  svg/
    logo.svg

Running rn-typed-assets generate produces:

// src/generated/assets.gen.ts
/* eslint-disable @typescript-eslint/no-require-imports -- require() is intentional for React Native static asset bundling */
// Auto-generated by rn-typed-assets. Do not edit manually.

import type { ImageRequireSource } from 'react-native';
import type { AnimationObject } from 'lottie-react-native';
export type SvgsAssetSource = unknown;

export const Assets = {
  coupang: {
    hariniCry:
      require('../assets/coupang/harini-cry.png') as ImageRequireSource,
  },
  toast: {
    info: require('../assets/toast/info.png') as ImageRequireSource,
    warning: require('../assets/toast/warning.png') as ImageRequireSource,
  },
} as const;

export const Lotties = {
  loading: require('../assets/lottie/loading.json') as AnimationObject,
} as const;

export const Svgs = {
  logo: require('../assets/svg/logo.svg') as SvgsAssetSource,
} as const;

Key normalization rules

| Filename | Generated key | | ---------------------------------- | ------------------------------------ | | harini-cry.png | hariniCry | | camera_guide.png | cameraGuide | | Info-Filled.png | infoFilled | | 1.png | n1 (numeric prefix → n) | | point.png alongside point/ dir | pointAsset (leaf/branch collision) |

Programmatic API

All functions are available for use in custom build scripts:

const {
  loadConfig,
  collectAssetEntries,
  generateAssetsModule,
  generateAssetsManifest,
  writeGeneratedAssets,
  auditAssetUsage,
  collectGeneratedAssetUsages,
  // Codemod
  collectAssetImportBindings,
  diffAssetManifests,
  rewriteTypedAssetSource,
} = require('rn-typed-assets');

const config = loadConfig(projectRoot);

// Generate
const { entries, moduleContent, manifest } = writeGeneratedAssets({
  projectRoot,
  types: ['image', 'lottie'],
  config,
});

// Audit (requires typescript peer dep)
const usages = collectGeneratedAssetUsages(sourceCode, filePath, config);
const report = auditAssetUsage({
  manifest,
  generatedUsages: usages,
  requirePaths: [],
  config,
});

// Codemod — rewrite a source file after a manifest change
const { renamedSymbols, currentSymbolsByFilePath } = diffAssetManifests({
  previousManifest,
  nextManifest,
  config,
});

const { changed, code } = rewriteTypedAssetSource({
  code: fs.readFileSync(filePath, 'utf8'),
  filePath, // relative to projectRoot
  previousManifest,
  nextManifest,
  projectRoot,
  config,
});

if (changed) {
  fs.writeFileSync(filePath, code);
}

See src/index.js for the full list of exported functions.

CI Integration

Add generation and audit to your CI pipeline to catch drift between the manifest and the filesystem:

# .github/workflows/ci.yml
- name: Verify asset manifest is up to date
  run: |
    npm run assets:generate
    git diff --exit-code src/generated/

Or use a pre-commit hook via Husky:

# .husky/pre-commit
npm run assets:generate
git add src/generated/assets.gen.ts src/generated/assets.manifest.json

Future Plans

Watch mode

Automatically re-run generate when files under any rootDir are added, removed, or renamed. Useful during active development without having to manually invoke the CLI.

rn-typed-assets generate --watch   # planned

Metro plugin integration

A Metro resolver plugin that runs generation as part of the bundler's startup, ensuring assets.gen.ts is always in sync before the first bundle is produced — eliminating the need for a separate pre-build step.

Husky / lint-staged recipe

A first-class init command that scaffolds a Husky pre-commit hook and lint-staged configuration so that generation runs automatically whenever asset files are staged.

rn-typed-assets init --hooks   # planned

Font and audio asset types

Extend the default type set to cover fonts (.ttf, .otf) and audio files (.mp3, .wav, .aac) with appropriate TypeScript types (FontSource, NodeRequire). These types already work today via the custom-type config API; the plan is to promote them to built-in defaults.

Strict audit mode for CI

A --strict flag for the audit command that exits non-zero if the manifest is stale, if any unknown keys are in use, or if any unused entries remain — making the full audit a one-command CI gate.

rn-typed-assets audit --strict   # planned

Monorepo support

Allow multiple packages in a workspace to share a single invocation, with per-package output directories and per-package manifests, controlled by a root-level config or workspace glob.

Output template customization

Inspired by SwiftGen's Stencil templates, allow users to supply a custom EJS or Handlebars template for assets.gen.ts, enabling alternative output styles — for example, emitting functions instead of require() literals, or targeting a custom asset loader.

License

MIT