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

@ekaone/annota

v0.1.3

Published

Header parser and polyglot script runner. Inspect, validate, and run any script by its shebang

Downloads

534

Readme

@ekaone/annota

Script header parser and polyglot script runner. It also inspects, validates, and runs any script by reading its shebang.

npx @ekaone/annota run ./build.ts
npx @ekaone/annota run ./analyze.py
npx @ekaone/annota run ./deploy.sh

One command. Any language. No configuration.


Install

# npm
npm install @ekaone/annota

# pnpm
pnpm add @ekaone/annota

# bun
bun add @ekaone/annota

How it works

annota reads the shebang line (#!) at the top of a script to determine which runtime to use, then delegates execution to that runtime. It also parses the full script header, including JSDoc, TypeScript directives, and custom hints,exposing everything as structured data.

#!/usr/bin/env bun        →  bun ./build.ts
#!/usr/bin/env python3    →  python3 ./analyze.py
#!/usr/bin/env deno run   →  deno run ./server.ts
#!/usr/bin/env tsx        →  tsx ./transform.ts
#!/bin/bash               →  bash ./deploy.sh

Supported executors

annota run works with any executor reachable on PATH. Common examples:

| Shebang | Executor | |---------|----------| | #!/usr/bin/env bun | Bun | | #!/usr/bin/env tsx | tsx | | #!/usr/bin/env ts-node | ts-node | | #!/usr/bin/env -S deno run | Deno | | #!/usr/bin/env node | Node.js | | #!/usr/bin/env python3 | Python 3 | | #!/usr/bin/env ruby | Ruby | | #!/bin/bash | Bash | | #!/bin/sh | sh |


CLI

annota run <file> or use alias an <file>

Run a script using the executor declared in its shebang.

annota run ./build.ts
annota run ./scripts/migrate.py
annota run ./deploy.sh

Flags

| Flag | Description | |------|-------------| | --dry-run | Print the resolved command without executing it | | --fallback=<executor> | Use this executor if no shebang is found | | -- <args> | Forward arguments to the executor |

# See what command would be run
annota run ./build.ts --dry-run
# → bun /home/user/project/build.ts

# Use a fallback executor when shebang is absent
annota run ./legacy.ts --fallback=tsx

# Pass arguments through to the script
annota run ./server.ts -- --port 4000 --watch

run is intentionally single-file only. For multi-script orchestration, use json-cli with annota as the executor layer.


annota inspect <file...>

Parse and display the script header metadata.

annota inspect ./build.ts
annota inspect ./scripts/*.ts
annota inspect ./build.ts --format json
annota inspect ./build.ts --format table
annota inspect ./build.ts --field executor

Flags

| Flag | Description | |------|-------------| | --format pretty | Human-readable output (default) | | --format json | JSON output, pipeable | | --format table | Tabular summary for multiple files | | --field <key> | Print a single field: executor, version, author, description, shebang |

Pretty output

✔ scripts/build.ts
  executor  : bun
  shebang   : #!/usr/bin/env bun
  ts-check  : true
  ts-nocheck: false
  description: Build script for the project
  version   : 2.0.0
  author    : ekaone
  env       : {"NODE_ENV":"production"}
  requires  : node>=18

Table output (--format table)

FILE              EXECUTOR   TS-CHECK   VERSION   VALID
build.ts          bun        ✔          2.0.0     ✔
deploy.ts         ts-node    ✔          —         ✔
analyze.py        python3    —          —         ✔
setup.sh          bash       —          —         ✔
broken.ts         —          —          —         ✘

JSON output (--format json)

[
  {
    "file": "build.ts",
    "shebang": { "raw": "#!/usr/bin/env bun", "executor": "bun", "args": [] },
    "directives": { "tsCheck": true, "tsNoCheck": false },
    "jsdoc": { "description": "Build script", "version": "2.0.0", "author": "ekaone", "tags": {} },
    "env": { "NODE_ENV": "production" },
    "requires": ["node>=18"],
    "headerLines": 9
  }
]

annota validate <file...>

Like inspect but CI-focused. Exits with code 1 if any file fails.

annota validate ./scripts/*.ts --require shebang
annota validate ./scripts/*.ts --require executor=bun
annota validate ./scripts/*.ts --require ts-check
annota validate ./scripts/*.ts --require version

Available rules

| Rule | Passes when | |------|-------------| | shebang | File has any shebang line | | ts-check | File has // @ts-check | | version | File has @version in JSDoc | | executor=<name> | Shebang executor matches <name> |

Multiple --require flags are supported and all must pass:

annota validate ./scripts/*.ts \
  --require shebang \
  --require executor=bun \
  --require version

Output

  ✔ scripts/build.ts
  ✔ scripts/lint.ts
  ✘ scripts/deploy.ts
      → executor mismatch: expected "bun", got "ts-node"
  ✘ scripts/old.ts
      → missing shebang

  2 passed, 2 failed

JSON output (--format json)

[
  { "file": "build.ts", "passed": true, "failures": [] },
  { "file": "deploy.ts", "passed": false, "failures": ["executor mismatch: expected \"bun\", got \"ts-node\""] }
]

annota which <file>

Print only the resolved executor. Unix-friendly and composable.

annota which ./build.ts
# bun

# Compose with shell
$(annota which ./build.ts) ./build.ts
# Equivalent to: bun ./build.ts

# Use in scripts
EXECUTOR=$(annota which ./build.ts)
echo "Running with: $EXECUTOR"

CI / GitHub Actions

Validate all scripts have a shebang

- name: Validate script headers
  run: |
    find ./scripts -name "*.ts" -o -name "*.py" -o -name "*.sh" | \
    xargs npx @ekaone/annota validate --require shebang

Enforce TypeScript scripts use bun

- name: Enforce executor
  run: npx @ekaone/annota validate ./scripts/*.ts --require executor=bun

Pre-commit hook (husky)

# .husky/pre-commit
npx @ekaone/annota validate \
  $(git diff --cached --name-only | grep -E "\.(ts|py|sh)$") \
  --require shebang

Export report as JSON artifact

- name: Inspect scripts
  run: |
    npx @ekaone/annota inspect ./scripts/* --format json > script-report.json

- uses: actions/upload-artifact@v4
  with:
    name: script-report
    path: script-report.json

Programmatic API

annota is also a full library. The CLI is a thin shell over the same primitives.

parseMeta(source)

Parse all header metadata from a source string.

import { readFileSync } from 'node:fs'
import { parseMeta } from '@ekaone/annota'

const source = readFileSync('./build.ts', 'utf8')
const meta = parseMeta(source)

console.log(meta.shebang?.executor)   // 'bun'
console.log(meta.jsdoc.version)       // '2.0.0'
console.log(meta.directives.tsCheck)  // true
console.log(meta.env)                 // { NODE_ENV: 'production' }
console.log(meta.requires)            // ['node>=18']

Returns: ScriptMeta

interface ScriptMeta {
  shebang:     ShebangResult | null
  directives:  DirectivesResult
  jsdoc:       JSDocResult
  env:         Record<string, string>
  requires:    string[]
  headerLines: number
}

parseShebang(source)

Parse only the shebang line.

import { parseShebang } from '@ekaone/annota'

parseShebang('#!/usr/bin/env bun\n')
// { raw: '#!/usr/bin/env bun', executor: 'bun', args: [] }

parseShebang('#!/usr/bin/env -S deno run --allow-net\n')
// { raw: '...', executor: 'deno', args: ['run', '--allow-net'] }

parseShebang('console.log(1)')
// null

Returns: ShebangResult | null

interface ShebangResult {
  raw:      string    // full shebang line including #!
  executor: string    // e.g. 'bun', 'python3', 'bash'
  args:     string[]  // arguments after the executor
}

parseDirectives(source)

Detect TypeScript directives.

import { parseDirectives } from '@ekaone/annota'

parseDirectives('// @ts-check\nconsole.log(1)')
// { tsCheck: true, tsNoCheck: false }

Returns: DirectivesResult

interface DirectivesResult {
  tsCheck:   boolean
  tsNoCheck: boolean
}

parseJSDoc(source)

Parse the leading JSDoc block.

import { parseJSDoc } from '@ekaone/annota'

const source = `
/**
 * Deploy script for production
 * @version 3.0.0
 * @author ekaone
 * @license MIT
 */
`

parseJSDoc(source)
// {
//   description: 'Deploy script for production',
//   version:     '3.0.0',
//   author:      'ekaone',
//   tags:        { license: 'MIT' }
// }

Returns: JSDocResult

interface JSDocResult {
  description: string | null
  version:     string | null
  author:      string | null
  tags:        Record<string, string>  // all other @tags
}

parseEnvHints(source) / parseRequiresHints(source)

Parse custom inline hints.

import { parseEnvHints, parseRequiresHints } from '@ekaone/annota'

const source = `
// @env NODE_ENV=production
// @env PORT=3000
// @requires node>=18
// @requires bun
`

parseEnvHints(source)
// { NODE_ENV: 'production', PORT: '3000' }

parseRequiresHints(source)
// ['node>=18', 'bun']

Hints are declared as single-line comments so they're valid in any language that uses // comments (TypeScript, JavaScript, Rust, Go, etc.).


validate(source, file, rules)

Validate a source string against a set of rules.

import { validate } from '@ekaone/annota'
import type { ValidateRule } from '@ekaone/annota'

const source = readFileSync('./build.ts', 'utf8')

const result = validate(source, 'build.ts', [
  'shebang',
  'ts-check',
  'version',
  'executor=bun',
])

console.log(result.passed)    // true | false
console.log(result.failures)  // string[]
console.log(result.meta)      // full ScriptMeta

Returns: ValidationResult

interface ValidationResult {
  file:     string
  passed:   boolean
  failures: string[]
  meta:     ScriptMeta
}

Available rules

type ValidateRule =
  | 'shebang'
  | 'ts-check'
  | 'version'
  | `executor=${string}`

Script header conventions

A fully annotated script header looks like this:

#!/usr/bin/env bun
// @ts-check
// @env NODE_ENV=production
// @env PORT=3000
// @requires node>=18
/**
 * Build and bundle the project for production.
 * @version 2.1.0
 * @author Eka Prasetia
 * @license MIT
 */
import { build } from './build.js'
await build()

None of these are required. annota gracefully handles missing sections and returns null for absent fields. Use only what's useful for your project.


License

MIT © Eka Prasetia

Links


⭐ If this library helps you, please consider giving it a star on GitHub!