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

@devmedic/project-detection-engine

v0.1.0

Published

Detects which project(s) a file belongs to (React Native, React, Node.js, Express, NestJS, Expo, Next.js, Monorepo, Library, CLI) and gates rule execution so a rule never runs against a file outside its declared project type.

Readme

@devmedic/project-detection-engine

Decides whether a file even belongs to the project a rule targets — so a rule declared for React Native never runs against a CLI package, a parser package, a fixture, or any other file it was never meant to see, even though every rule and every file live in the same monorepo.

import { detectProjectTypes, createProjectDetectionGate } from '@devmedic/project-detection-engine';

const detection = await detectProjectTypes('/path/to/some/project');
detection.projectTypes; // e.g. ['react-native', 'expo']
detection.platforms; // e.g. ['ios', 'android']
detection.isMonorepoRoot; // boolean
detection.isNestedInMonorepo; // boolean

const gate = createProjectDetectionGate();
await gate.shouldRun(rule, '/path/to/some/project/src/App.tsx'); // boolean

createProjectDetectionGate()'s result is passed as @devmedic/rule-engine#AnalyzeOptions.projectGate — see that package's README, "Gating rule execution by project," for the engine-side contract. This package has no dependency on @devmedic/rule-engine either way: the gate is accepted structurally, by shape, not by type import.

What it detects

| Type | Signal | | -------------- | ------------------------------------------------------------------------- | | react-native | @devmedic/project-scanner's platform.isReactNative | | expo | platform.isExpo | | react | platform.isReact (React Native implies React too) | | nextjs | platform.isNextJs | | express | platform.isExpress | | nestjs | platform.isNestJs | | node | platform.isNode (true for Next.js/Express/NestJS/@types/node/CLI too) | | monorepo | package.json's workspaces field, or a pnpm-workspace.yaml file | | library | package.json has a main field and no bin field | | cli | package.json has a bin field |

Platforms detected: ios, android (React Native/Expo, or android/+ios/ directories), web (plain React or Next.js), node (Node.js/Express/NestJS/ CLI).

A project can be several types at once — e.g. a Next.js app is also react; a monorepo root that ships a binary is also cli. Detection never hardcodes a hierarchy between them; it derives every applicable type independently from the same underlying signals.

Ignored paths

Never detected, never gated in — always denied outright, regardless of what a rule declares:

node_modules  dist        build      coverage   .tmp     .cache
fixtures      __fixtures__ tests     __tests__  spec     examples
generated     storybook   ios/build  android/build  Pods  DerivedData

Matched by path segment (isIgnoredPath), not glob syntax — ios/build requires those two segments consecutively; a directory named node_modules_backup doesn't match node_modules. Pass { extraIgnorePatterns: [...] } to createProjectDetectionGate to extend (not replace) this list.

For real gitignore-syntax matching (**, !negation, character classes), pass { isIgnored: (path) => boolean } instead — consulted in addition to the built-in check, never instead of it. This is the hook @devmedic/ignore-engine#IgnoreService.isIgnored plugs into directly, so the gate agrees with whatever .gitignore/.devmedicignore/workspace/ plugin/CLI ignores a caller has already merged, without this package taking a dependency on that one:

import { createIgnoreService } from '@devmedic/ignore-engine';

const ignoreService = await createIgnoreService({ projectRoot });
const gate = createProjectDetectionGate({ isIgnored: (path) => ignoreService.isIgnored(path) });

Nested projects

Detection walks every ancestor package.json, not just the nearest one — a package nested inside a monorepo is detected using its own signals (isNestedInMonorepo: true, but isMonorepoRoot: false), while the workspace root itself gets isMonorepoRoot: true and the monorepo project type. A rule scoped to ['react-native'] still matches a React Native package nested three levels deep — monorepo is an orthogonal fact about a specific root, not a prefix every nested project inherits.

Performance

Both the ancestor-root filesystem walk (resolveProjectRootChain) and the detection itself (detectProjectTypes) are cached in-process — keyed by directory and by resolved project root, respectively. Analyzing many files in the same project costs one real detection, not one per file. Clear either cache (clearProjectRootCache/clearProjectTypeDetectionCache) if a long-lived process needs to react to a filesystem change.

Depends on

  • @devmedic/project-scanner — the underlying scanProject/ readPackageJsonSummary this package builds monorepo/library/cli detection and the ignore/gate logic on top of.