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

@kafitra/lynx-autolink

v0.1.1

Published

Auto-linking library for Lynx Native Modules — scans node_modules, generates Java registry, injects Gradle wiring

Readme

@kafitra/lynx-autolink

Core auto-linking library for Lynx Native Modules on Android.
Used internally by @kafitra/lynx-cli — you rarely need to call this directly.

Repository: github.com/kafitramarna/kafitra-lynx-native


Overview

@kafitra/lynx-autolink scans node_modules for Lynx Native Module packages (identified by a lynx.module.json metadata file), then generates all the Android plumbing needed to wire them up:

| What it does | Output | | ------------------------------------- | --------------------------------------------------- | | Scans node_modules (hoisted + nested) | List of LynxModuleMetadata | | Validates each lynx.module.json | Throws descriptive error on bad schema | | Generates LynxAutolinkRegistry.java | Drop-in registry class, call registerAll() | | Patches settings.gradle | Idempotent include ':module-name' block | | Patches app/build.gradle | Idempotent implementation project(':module-name') |


Installation

pnpm add -D @kafitra/lynx-autolink
# or
npm install --save-dev @kafitra/lynx-autolink

API Reference

scanModules(projectRoot: string): LynxModuleMetadata[]

Walks all node_modules directories reachable from projectRoot (handles hoisted monorepos).
Returns an array of validated LynxModuleMetadata objects — one per package that has a lynx.module.json.

import { scanModules } from "@kafitra/lynx-autolink";

const modules = scanModules(process.cwd());
// [{ name: "LynxDeviceInfo", android: { moduleClass: "...", sourceDir: "android" }, ... }]

writeJavaRegistry(androidAppDir, javaPackage, modules)

Generates LynxAutolinkRegistry.java into <androidAppDir>/src/main/java/<javaPackage>/.
The file is always overwritten so it stays in sync with installed packages.

import { writeJavaRegistry } from "@kafitra/lynx-autolink";

writeJavaRegistry("./android/app", "com.example.app", modules);

Generated file looks like:

package com.example.app;

import com.lynx.tasm.LynxEnv;
import com.example.mymodule.MyLynxModule;

public class LynxAutolinkRegistry {
    public static void registerAll() {
        LynxEnv.inst().registerModule("MyModule", MyLynxModule.class);
    }
}

Then call it from your Application class:

@Override
protected void initLynxEnv() {
    LynxEnv.inst().init(this, null, null, null);
    LynxAutolinkRegistry.registerAll(); // ← auto-linked modules
}

injectSettings(settingsFile, modules)

Idempotently inserts Gradle project includes into settings.gradle, wrapped in markers:

// lynx-autolink-start
include ':lynx-device-info'
project(':lynx-device-info').projectDir = new File(rootDir, '../../../packages/lynx-device-info/android')
// lynx-autolink-end

Safe to run multiple times — existing block is replaced, not duplicated.


injectBuildGradle(buildFile, modules)

Idempotently adds implementation project(':...') lines to the first dependencies {} block in app/build.gradle.


readApplicationId(buildGradlePath): string | null

Reads the applicationId from an app/build.gradle file using a regex.
Used by the CLI to infer the Java package for the registry class.


injectManifestPermissions(manifestFile, modules)

Collects all permissions entries from the provided modules and idempotently injects <uses-permission android:name="..."/> elements into AndroidManifest.xml.

Uses <!-- lynx-autolink-permissions-start --> / <!-- lynx-autolink-permissions-end --> markers. If the markers are absent, the block is inserted immediately before the <application> tag. Re-running the function replaces the existing block — no duplication.

import { injectManifestPermissions } from "@kafitra/lynx-autolink";

injectManifestPermissions("./android/app/src/main/AndroidManifest.xml", modules);

Result in AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<!-- lynx-autolink-permissions-start -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- lynx-autolink-permissions-end -->

validateMetadata(raw, source): LynxModuleMetadata

Validates a parsed lynx.module.json object. Throws a descriptive error if required fields are missing or malformed. Enforcements include:

  • android.componentTag is required when android.componentClass is provided.
  • At least one of moduleClass or componentClass must be present.
  • All class names must match the fully-qualified Java class name pattern.

lynx.module.json Schema

Place this file at the root of your Lynx Native Module npm package:

{
  "name": "LynxDeviceInfo",
  "android": {
    "moduleClass": "com.kafitra.lynxdeviceinfo.LynxDeviceInfoModule",
    "sourceDir": "android",
    "gradleProjectName": "lynx-device-info"
  }
}

| Field | Type | Required | Description | | --------------------------- | ---------- | ------------------------ | ---------------------------------------------------------------------------------- | | name | string | ✅ | Module name registered with LynxEnv (e.g. "LynxDeviceInfo") | | android.moduleClass | string | ✅¹ | Fully-qualified Java class name of the LynxModule implementation | | android.componentClass | string | ✅¹ | Fully-qualified Java class name of the LynxUI custom element implementation | | android.componentTag | string | ✅ when componentClass | JSX element tag registered with Lynx (e.g. "camera") | | android.sourceDir | string | ✅ | Relative path to the Android library directory inside the package | | android.gradleProjectName | string | ❌ | Override for the Gradle project name. Defaults to the kebab-cased npm package name | | android.permissions | string[] | ❌ | Android permissions required at runtime (e.g. ["android.permission.CAMERA"]). Auto-injected into AndroidManifest.xml by lynx link. |

¹ At least one of moduleClass or componentClass must be provided.


Types

export interface LynxModuleAndroidConfig {
  /** LynxModule class name (for native modules) */
  moduleClass?: string;
  /** LynxUI class name (for native UI custom elements) */
  componentClass?: string;
  /** JSX element tag — required when componentClass is provided */
  componentTag?: string;
  sourceDir: string;
  gradleProjectName?: string;
  /** Android permissions required at runtime. Injected into AndroidManifest.xml by `lynx link`. */
  permissions?: string[];
}

export interface LynxModuleMetadata {
  name: string;
  android: LynxModuleAndroidConfig;
  /** Populated by scanner — not present in lynx.module.json */
  packageDir?: string;
  packageName?: string;
}

Native UI Components

In addition to native modules (moduleClass), you can register native UI custom elements (componentClass + componentTag). The generated LynxAutolinkRegistry.java will expose an addUIBehaviorsTo(builder) method alongside registerAll().

Example lynx.module.json for a native UI component:

{
  "name": "LynxCamera",
  "android": {
    "componentClass": "com.kafitra.lynxcamera.LynxCameraView",
    "componentTag": "camera",
    "sourceDir": "android"
  }
}

In your Activity:

LynxAutolinkRegistry.registerAll();           // native modules
LynxAutolinkRegistry.addUIBehaviorsTo(builder); // native UI components

Creating a Lynx Native Module

To make your package auto-linkable, add lynx.module.json to it and include it in files[]:

// package.json
{
  "name": "@your-scope/lynx-my-module",
  "files": ["dist", "android", "ios", "lynx.module.json"]
}

Then implement a standard Lynx LynxModule in android/src/main/java/....


License

MIT