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

lino-arguments

v0.4.1

Published

A setup of Links Notation Environment (lenv) + yargs to support easy configuration of CLI apps

Readme

lino-arguments

Configuration from CLI arguments, environment maps, and Links Notation environment files, using yargs and lino-env.

npm version License: Unlicense

Installation

npm install lino-arguments

Requires Node.js ^20.19.0, ^22.12.0, or >=23. Bun and Deno are supported. The Deno configuration permits newly published dependencies to follow the repository's dependency freshness checks.

Existing process-based usage

import { makeConfig } from 'lino-arguments';

const config = makeConfig({
  yargs: ({ yargs, getenv }) =>
    yargs.option('port', { type: 'number', default: getenv('PORT', 3000) }),
});

Without an injected environment, makeConfig retains its process defaults: it reads process.argv, loads .lenv into process.env, and returns CLI options with camelCase keys. .lenv overrides the existing process environment by default; set lenv.override: false to retain existing values, including empty strings. A file selected by --configuration/-c overrides both. CLI options always have highest priority.

Positional command arguments

Set includePositionals: true to return a positionals array alongside the options:

const config = makeConfig({
  includePositionals: true,
  lenv: { enabled: false },
  argv: ['node', 'tool.js', 'collect', 'https://example.com/product'],
  yargs: ({ yargs }) => yargs.exitProcess(false).help(false).version(false),
});
// { positionals: ['collect', 'https://example.com/product'] }

const [command, ...args] = config.positionals;

argv is a full process-style array: its first two entries are the executable and script, followed by individual argument tokens. Omit it to use process.argv; pass ['node', 'tool.js'] for no arguments. The shell handles quoting, so a filename such as my products.csv is one array entry, without enclosing shell quotes.

positionals is a copy of _ from the existing final yargs parse. It preserves yargs' types and source order, including numeric conversion unless disabled with .parserConfiguration({ 'parse-positional-numbers': false }). Empty input produces positionals: []. Options can appear before or after the command. With the default yargs settings, tokens after -- join positionals without being treated as options; if you enable yargs' populate-- setting, those tokens remain outside _ and this array.

Yargs command declarations retain their own behavior. For example, .command('collect <url>', ...) consumes the named positional into config.url, leaving config.positionals as ['collect']. The array always mirrors the final parsed._; it does not reconstruct named arguments. Command handlers and validation callbacks run through the existing parse.

The default (includePositionals: false, or omitted) still returns only options. Neither _ nor $0 is exposed. When enabled, positionals is a reserved result key: an option, alias, or named positional that normalizes to that key causes an error rather than being overwritten. Existing options named positionals continue to work when the feature is disabled.

See examples/positional-commands.js for a runnable CLI with collect, compare, and export commands.

Isolated contexts and automatic environment mapping

const config = makeConfig({
  env: {
    values: {
      PORT: '8080',
      VERBOSE: 'false',
      START_URL: 'https://example.test',
    },
    autoMap: true,
  },
  cwd: '/app/config',
  argv: ['node', 'app.js', '--port', '9090'],
  yargs: ({ yargs }) =>
    yargs
      .option('port', { type: 'number', default: 3000 })
      .option('verbose', { type: 'boolean', default: true })
      .option('start-url', { type: 'string', env: 'START_URL' }),
});
// { port: 9090, verbose: false, startUrl: 'https://example.test' }

env.values is copied; resolution never obtains values from or exports values to the host environment. Two calls with separate maps/directories are independent. cwd resolves .lenv, lenv.path, --configuration/-c, and relative secret paths without changing the process directory.

You can pass an environment map directly as env: { PORT: '8080' } or env: {}. Use env.values when the map could contain reserved settings keys (enabled, quiet, autoMap, values); a settings-only object with boolean settings keeps its legacy meaning. env: { autoMap: true } opts into mapping with the legacy process defaults. env: { values: {} } explicitly isolates an empty environment.

For isolated contexts, precedence from highest to lowest is:

  1. Explicit CLI arguments.
  2. Injected env/env.values.
  3. The .lenv selected by --configuration/-c.
  4. The default .lenv (or lenv.path).
  5. Option defaults.

lenv.override controls the legacy process exporter only. Missing/unreadable optional .lenv files are treated as empty by LinoEnv.read(). The implementation reuses LinoEnv.read().toObject() and adds no .lenv parser.

Automatic mapping is opt-in. It maps only declared options: job-application-interval → JOB_APPLICATION_INTERVAL, verbose → VERBOSE. It supports .option(), .options(), shorthand type declarations, and synchronous command builders. Use { env: 'APP_START_URL' } for an explicit variable name even without autoMap, or { env: false } to disable mapping for one option. CLI aliases retain CLI priority. Native yargs.env() is rejected in isolated contexts because it reads the host environment.

Mapped numbers accept finite decimal values; booleans accept true/false (case insensitive) and 1/0; strings retain empty values. Array options accept comma-separated strings or array-valued object input. Invalid mapped values throw an error naming the key and type, without the value. Unknown environment keys never become options.

Keys normalize across UPPER_CASE, camelCase, kebab-case, snake_case, and PascalCase. Multiple spellings of the same key within one source are ambiguous, even if the values are equal. Environment aliases are rejected only when that key is read by getenv or option mapping; unrelated aliases such as http_proxy + HTTP_PROXY or npm_config_cache + NPM_CONFIG_CACHE do not prevent startup. Explicit CLI values bypass environment mapping and its ambiguity checks. This applies to host and injected environments, including the pure APIs. Configuration and .lenv source aliases are validated when the context is created. Different sources can use different spellings and follow precedence. Conflicting option aliases are rejected too. Repeated identical .lenv keys retain LinoEnv's last-value behavior.

Opt-in secret files

const config = makeConfig({
  env: { values: { API_KEY_FILE: 'secrets/api-key' }, autoMap: true },
  cwd: '/app',
  secrets: { keys: ['API_KEY'] },
  yargs: ({ yargs }) => yargs.option('api-key', { type: 'string' }),
});

Only keys in secrets.keys can load a file; unrelated *_FILE variables are inert. A secret is read when its declared option or contextual getenv lookup needs it, and the result is cached within that call. File values never export to process.env. Explicit CLI values bypass automatic env mapping and unused files. A manual getenv call inside the callback resolves immediately, so use mapping for secrets that CLI should bypass.

| Secret setting | Default and behavior | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | keys | Required allowlist, e.g. ['API_KEY']; uses API_KEY_FILE. An object such as { API_KEY: 'TOKEN_PATH' } selects a custom file variable. | | readFile(path, { maxBytes }) | Optional synchronous injected reader returning a string or Uint8Array. The main entry otherwise uses its bounded Node filesystem adapter. A custom reader must honor the supplied bound; its returned content is also checked. | | maxBytes | 65536 bytes (64 KiB), valid range 1–16777216. Checked before newline removal, using UTF-8 bytes. The default adapter only reads regular files, allocating at most maxBytes + 1 bytes. | | newline | 'strip': removes exactly one terminal LF or CRLF. 'preserve' retains it. BOM, spaces, interior newlines and extra trailing newlines remain. Invalid UTF-8 is rejected. | | conflict | 'error': both NAME and its file variable in the winning source throw. 'value' prefers direct values (including empty strings). 'file' prefers file contents. |

Source precedence is applied to a direct value and its allowlisted file variable together: a higher-priority direct value overrides a lower-priority file, and a higher-priority file overrides a lower-priority direct value. Conflicts in unused lower sources do not cause reads or errors.

Missing/unreadable/oversized secret files throw redacted errors. Paths, contents, and underlying reader error messages are omitted. Env defaults display [environment] in yargs help. With a secret allowlist, yargs validation failures use a generic error to prevent values appearing in error/help output. Application-supplied logging, callbacks and coercion functions remain the application's responsibility.

Optional trace(event) receives only normalized key, source, and whether a file was used. Tracing is off by default; it never receives values or file paths.

See examples/isolated-contexts.js for a runnable example using generic fixtures.

Browser/WASM and plain objects

Import the filesystem-free subpath for already-parsed objects:

import { resolveConfig, createConfigContext } from 'lino-arguments/pure';

const config = resolveConfig({
  options: {
    port: { type: 'number', default: 3000 },
    'api-key': { type: 'string', env: 'TOKEN', default: '' },
  },
  lenv: { PORT: '5000' }, // Already parsed, e.g. LinoEnv.toObject().
  configuration: {},
  env: { TOKEN: '' },
  argv: { port: 8080 }, // Already parsed CLI/options object.
});
// { port: 8080, apiKey: '' }

const context = createConfigContext({ env: { PORT: '0' } });
context.getenv('port', 3000); // 0

resolveConfig maps its declared schema automatically unless autoMap: false. createConfigContext exposes getenv(name, fallback) and lookup(name); the latter returns { found, value } for present keys. Both accept env, lenv, configuration, secrets, cwd, readFile, resolvePath, and trace, with the same precedence/conflict rules. The pure entry has no Node imports, process access, yargs or filesystem defaults. Secret files require an injected reader. It does not parse CLI strings or raw .lenv text; parse those with the existing runtime APIs first. For CLI aliases, use the yargs entry; the pure schema receives canonical option names.

The main entry imports Node filesystem adapters through lino-env/yargs. Browser/WASM applications should import lino-arguments/pure.

API reference

makeConfig({ yargs, env, cwd, argv, includePositionals, lenv, getenv, secrets, trace }) returns parsed camelCase options, plus positionals when includePositionals: true. yargs is an optional callback receiving { yargs, getenv }. argv retains the existing full process-style array convention, including executable/script slots. getenv.enabled: false disables the callback helper. lenv.enabled: false disables default .lenv loading; explicit --configuration still loads its file. lenv.quiet: false enables compatibility loading notices.

getenv(name, defaultValue = '', { env, ...contextOptions }?) uses the process environment by default or an injected map when provided. The type of the default determines conversion: integer defaults require an integer, fractional defaults allow floats, and boolean defaults use boolish conversion. Invalid values return the fallback; empty strings, false and zero are preserved when valid for that type. Ambiguous aliases throw.

applyLinoEnv(path, { override: false, quiet: false }) is the explicit compatibility exporter. It normalizes names to UPPER_CASE in process.env; override: false preserves defined values, including empty strings.

await loadDotenvx(options) is the deprecated optional .env exporter. Existing env: { enabled: true, quiet: true } remains supported, but makeConfig is synchronous and cannot await its import. Await loadDotenvx() before makeConfig() when deterministic .env loading is needed. Injected contexts reject enabled: true; supply an already-parsed environment map instead.

Parser, LinoEnv, and yargs remain exported. parseLinoArguments(text) retains the legacy flat argument API. toUpperCase, toCamelCase, toKebabCase, toSnakeCase, and toPascalCase remain available; parsed result keys normalize through kebab-case before camelCase conversion.

.lenv format

PORT: 3000
VERBOSE: false
API_KEY_FILE: secrets/api-key

The separator is colon followed by a space. Blank lines and comments are ignored. Values retain their spaces.

Built-in yargs flags

Disable reserved flags when defining your own options of the same name:

makeConfig({
  yargs: ({ yargs }) =>
    yargs.version(false).help(false).option('version', { type: 'string' }),
});

To use yargs' built-in behavior, call .version('1.0.0').help(). See examples/enable-version-and-help.js.

Language boundaries

These isolated contexts, pure object resolution, and secret-file settings are JavaScript APIs. The Rust package continues using clap and process environment loaders; its existing automatic option-to-env mapping is separate and does not provide these isolated/secret-file contracts. This repository has no Python implementation.

Grammar compatibility and deduplication

The maintained direct range is links-notation ^0.31.2, verified with 0.31.2 on Node.js, Bun, and Deno. The exported Parser uses that grammar; parseLinoArguments traverses nested line/group nodes in source order and keeps its legacy flat token result and invalid-notation line fallback. Parsed tokens can be passed after the executable/script entries in makeConfig's argv.

A consumer declaring links-notation 0.31.2 shares that parser with this package. A fully deduplicated graph is currently limited by published lino-env 0.2.8: its manifest requires links-notation ^0.11.2, so npm also installs a nested 0.11.2. These pre-1.0 minor ranges do not overlap. The published lino-env JavaScript implementation reads/writes : lines itself and does not import either grammar. The duplicate is dependency metadata, with no reproduced configuration failure from it.

This release deliberately retains lino-env's published dependency contract. Updating this package's direct range alone cannot remove the nested grammar. An upstream lino-env release must update or remove its old declaration; the coordination is tracked in lino-env#33. An isolated metadata-only migration to ^0.31.2 produces one parser and passes the packed consumer's parser, CLI, .lenv roundtrip, exporter, and precedence checks. That assessment does not substitute for an upstream release or its complete test suite. No override or fork is shipped here.

.lenv values retain literal spaces, quotes, colons, hashes and empty strings; they are not parsed as general Links Notation. Existing lino-env behavior is preserved, including last-value lookup for duplicate keys. Upstream CRLF value handling remains tracked separately in lino-env#32. See the compatibility assessment for the reproduction, evidence, and migration checks.

Development and testing

Use Node.js 24 and npm >=10.9.0 for lint/release tools. Tests use test-anywhere.

npm ci
npm test
bun test
deno test --allow-read --allow-write --allow-env
npm run check
npm run check:dependencies
npm run test:release
npm run test:consumer
# Pure resolver runs without fs/env permissions:
deno run --no-config ../experiments/test-pure-resolution.mjs

We use Changesets for versioning. Add exactly one changeset per PR (npm run changeset); CI versions and publishes on merge. Manual edits to the package version are prohibited by CI.

Related projects

License

Public domain under the Unlicense.