Skip to content

feat(preset-basic): enhance findMatchingBrace to handle strings, comments, and template literals #33

Description

@aridyckovsky

Summary

Enhance findMatchingBrace helper to correctly skip braces inside strings, comments, and template literals to avoid false positives when parsing code blocks.

Problem

Current implementation (in packages/preset-basic/src/helpers.ts) naively counts braces without considering context:

const findMatchingBrace = (content: string, startIndex: number): number => {
  let depth = 1;
  for (let i = startIndex + 1; i < content.length; i++) {
    if (content[i] === '{') depth++;
    if (content[i] === '}') depth--;
    if (depth === 0) return i;
  }
  return -1;
};

Edge cases that fail:

Effect.gen(function* () {
  const message = "hello { world }"; // Braces in string
  const template = `value: ${count}`;  // Braces in template literal
  // { This brace in a comment }
  yield* doSomething();
});

Proposed Solution

Enhance the function to:

  1. Skip string literals (single and double quoted)
  2. Skip template literals (backtick strings)
  3. Skip single-line comments (//)
  4. Skip multi-line comments (/* */)

Alternative approach: Use TypeScript AST parsing with @typescript-eslint/parser for accurate parsing (more robust but heavier dependency).

Implementation Sketch

function findMatchingBrace(content: string, startIndex: number): number {
  let depth = 1;
  let inString: string | null = null; // ', ", or `
  let inSingleLineComment = false;
  let inMultiLineComment = false;

  for (let i = startIndex + 1; i < content.length; i++) {
    const char = content[i];
    const nextChar = content[i + 1];
    const prevChar = content[i - 1];

    // Handle comment starts
    if (!inString && char === '/' && nextChar === '/') {
      inSingleLineComment = true;
      i++; continue;
    }
    if (!inString && char === '/' && nextChar === '*') {
      inMultiLineComment = true;
      i++; continue;
    }

    // Handle comment ends
    if (inSingleLineComment && char === '\n') {
      inSingleLineComment = false;
      continue;
    }
    if (inMultiLineComment && char === '*' && nextChar === '/') {
      inMultiLineComment = false;
      i++; continue;
    }

    // Skip if in comment
    if (inSingleLineComment || inMultiLineComment) continue;

    // Handle strings
    if ((char === '"' || char === "'" || char === '`') && prevChar !== '\\') {
      if (inString === char) {
        inString = null;
      } else if (!inString) {
        inString = char;
      }
      continue;
    }

    // Skip if in string
    if (inString) continue;

    // Count braces
    if (char === '{') depth++;
    if (char === '}') depth--;
    if (depth === 0) return i;
  }

  return -1;
}

Acceptance Criteria

  • Handles braces in double-quoted strings
  • Handles braces in single-quoted strings
  • Handles braces in template literals
  • Handles braces in single-line comments
  • Handles braces in multi-line comments
  • Handles escaped quotes (\\" and \\')
  • All existing tests pass
  • New edge-case tests added

Alternative: TypeScript AST Parsing

For maximum robustness, consider using @typescript-eslint/parser:

import { parse } from '@typescript-eslint/parser';

// Parse and walk AST instead of regex/string matching

Pros: Bulletproof parsing, handles all edge cases
Cons: Heavier dependency, slower for simple rules

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    pkg:presetIssues related to preset packagespriority:lowLow priorityrulesMigration rules and patternstype:featureNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions