mrkeyoor.com_
Sat 08 Aug 20:58 UTC
npmUtilsupdated 08 Aug 2026

parse-statements

parse-statements is a dependency-free source-text scanner that finds ordered sequences of regular-expression tokens and reports the token offsets through callbacks. You describe statement shapes and optional comment delimiters, create a parser once, then let it mutate a context object as it walks a string. It can recognize lightweight constructs across different languages, but it is not a grammar parser: it does not build an AST, understand nesting, or validate a language beyond the token sequences you supply.

Verdict

A focused tool for token-sequence extraction when a full parser would be excessive. Do not mistake its language-neutral input for language understanding; nested or ambiguous syntax deserves a real grammar parser.

API stability4/5The package remains on major version 1 and exposes one runtime function, createParseFunction, with callback-driven descriptors and exported TypeScript types. That small public surface limits breakage risk. The tradeoff is that subtle behavior lives in callback ordering, index overrides, regular-expression flags, and comment precedence, so changing a token definition can alter parsing even when the package API itself stays fixed.
Docs3/5The README documents every option, callback signature, token offset, comment pair, default regexp flag, runtime requirement, and the special callback return value. It also includes a substantial import/export example. However, the material is dense, there is no separate reference site, and common recipes such as quoted strings, nested delimiters, or testing malformed input are not broken into approachable examples.
Maintenance4/5Version 1.0.12 was published May 6, 2025 and the repository was pushed minutes before that release. It is not archived and GitHub reports no open issues or pull requests. This is credible maintenance evidence for a small, finished utility, although there has been no repository push for more than a year and the tiny contributor footprint leaves continuity dependent on a narrow maintainer base.
Ecosystem3/5npm records 4,962,664 downloads in the latest week, but the repository has only 12 stars, suggesting much of its reach may arrive transitively rather than through a broad direct-user community. It has no runtime dependencies, provides ESM and CommonJS exports, and bundles types. There are no documented plugins, grammar packs, editor integrations, or language-specific extensions around it.

Use it if

  • You need to extract a few predictable statement shapes from source text without installing a full language parser
  • Your format has comments between meaningful tokens and you need their exact source offsets
  • You want one configured scanner reused against many strings while accumulating results in a caller-owned context
  • You need both ESM and CommonJS entry points plus bundled TypeScript declarations
Skip it if

Setup reality

There are no runtime dependencies, native builds, credentials, or config files. Install the package, import createParseFunction, and define a mutable context plus statement descriptors. Most of the setup cost is escaping and parser design. Every token is a string passed to RegExp, not a RegExp object, and the default flags are gmu; a backslash must therefore survive both JavaScript string parsing and RegExp construction, so word boundaries look like '\b' in source. The scanner only knows comments you describe with opening and closing token pairs. Set canIncludeComments on each statement that permits them, and use shouldSearchBeforeComments when a statement opener and comment opener can start at the same position. Callbacks receive numeric start and end offsets, with end exclusive, so use source.slice(start, end). An incomplete statement calls its local onError with only the tokens found so far. An unclosed comment calls the comment error handler and stops parsing. A statement callback may return a replacement end index, but if it moves past comments you must parse those comments yourself, as the README warns. Custom regexpFlags replace the defaults rather than extending them, so omitting g can break the global scanning design. TypeScript can type a fixed token count through OnParse<Context, N>, but heterogeneous descriptors often require a broader OnParse cast, as the official example demonstrates.

Patterns

Extract a simple begin-to-end statementfind-token-sequence

import { createParseFunction } from 'parse-statements';

type Context = { values: string[] };

const parse = createParseFunction<Context>({
  statements: [{
    tokens: ['^BEGIN\b', '\bEND$'],
    onParse: ({ values }, source, first, last) => {
      values.push(source.slice(first.end, last.start).trim());
    },
  }],
});

const context: Context = { values: [] };
parse(context, 'BEGIN useful text END');

Token strings are passed to RegExp, so backslashes such as the one in \b must be escaped in JavaScript source.

Slice the complete matched statementcapture-whole-statement

const parse = createParseFunction<{ statements: string[] }>({
  statements: [{
    tokens: ['^include\b', ';'],
    onParse: (context, source, first, last) => {
      context.statements.push(source.slice(first.start, last.end));
    },
  }],
});

start is inclusive and end is exclusive, which matches String.prototype.slice without adjustment.

Record an incomplete token sequencereport-incomplete-statement

const parse = createParseFunction<{ errors: string[] }>({
  statements: [{
    tokens: ['^import\b', '\bfrom\b', '$\n?'],
    onError: (context, source, ...tokens) => {
      const first = tokens[0]!;
      const last = tokens[tokens.length - 1]!;
      context.errors.push(source.slice(first.start, last.end));
    },
  }],
});

onError receives only the statement tokens found before the sequence became incomplete.

Collect line commentsparse-line-comments

const parse = createParseFunction<{ comments: string[] }>({
  comments: [{
    tokens: ['\/\/', '$\n?'],
    onParse: (context, source, open, close) => {
      context.comments.push(source.slice(open.end, close.start));
    },
  }],
});

The closing token is a regular expression too; the README uses $\n? to end a line comment.

Collect block comments and reject unclosed onesparse-block-comments

const parse = createParseFunction<{ comments: string[] }>({
  comments: [{
    tokens: ['\/\*', '\*\/'],
    onParse: (context, source, open, close) => {
      context.comments.push(source.slice(open.end, close.start));
    },
    onError: (_context, source, open) => {
      throw new SyntaxError(`Unclosed comment at ${open.start}: ${source.slice(open.start)}`);
    },
  }],
});

An unclosed comment ends the parse after the comment error callback runs.

Allow comments between statement tokensallow-comments-inside

const parse = createParseFunction<{ imports: string[] }>({
  comments: [{ tokens: ['\/\*', '\*\/'] }],
  statements: [{
    canIncludeComments: true,
    shouldSearchBeforeComments: true,
    tokens: ['^import\b', '\bfrom\b', '$\n?'],
    onParse: (context, source, first, _from, last) => {
      context.imports.push(source.slice(first.start, last.end));
    },
  }],
});

Comments are attached to the preceding parsed token; shouldSearchBeforeComments controls precedence when openers compete.

Read comments attached between tokensread-attached-comments

const onParse: OnParse<Context, 3> = (context, source, first, middle, last) => {
  for (const [open, close] of first.comments ?? []) {
    context.comments.push(source.slice(open.end, close.start));
  }
  context.values.push(source.slice(first.end, last.start));
};

Only non-final tokens can have a comments property because comments are stored between a token and its successor.

Surface parser-internal mismatcheshandle-global-error

const parse = createParseFunction<{ errors: string[] }>({
  onError: (context, _source, message, index) => {
    context.errors.push(`${index}: ${message}`);
  },
  statements,
  comments,
});

This global callback is distinct from a statement's incomplete-sequence callback and a comment's missing-close callback.

Continue scanning from a custom indexoverride-statement-end

const parse = createParseFunction<Context>({
  statements: [{
    tokens: ['^section\b', '\{'],
    onParse: (context, source, first, openBrace) => {
      const close = findMatchingBrace(source, openBrace.end);
      context.sections.push(source.slice(first.start, close + 1));
      return close + 1;
    },
  }],
});

If the custom range crosses comments, the callback must account for them because the scanner resumes only at the returned index.

Reuse one parser with separate contextsreuse-configured-parser

const parseDirectives = createParseFunction<Context>(options);

const first: Context = { directives: [], errors: [] };
const second: Context = { directives: [], errors: [] };

parseDirectives(first, sourceA);
parseDirectives(second, sourceB);

The parser mutates the context passed to each call and does not create or return a result object for you.

Alternatives

PackageRegistryPick it when
esprimanpmUse it when the input is JavaScript and you need a standards-aware ESTree syntax tree
@babel/parsernpmUse it for JavaScript or TypeScript syntax, proposals, JSX, and parser error recovery
meriyahnpmUse it for a fast ECMAScript parser when a real AST matters more than a tiny generic scanner