Skip to content

feat: hand written parser - #40717

Open
amitkumarashutosh wants to merge 41 commits into
RocketChat:developfrom
amitkumarashutosh:feat/hand-written-parser
Open

feat: hand written parser#40717
amitkumarashutosh wants to merge 41 commits into
RocketChat:developfrom
amitkumarashutosh:feat/hand-written-parser

Conversation

@amitkumarashutosh

@amitkumarashutosh amitkumarashutosh commented May 28, 2026

Copy link
Copy Markdown
Contributor

Handwritten Parser Foundation

This PR introduces a handwritten TypeScript parser implementation to replace the PeggyJS-generated parser, building it up incrementally while maintaining AST compatibility with the existing grammar.

What's Added

  • parser.ts — core handwritten parser with block dispatch and inline parsing loop
  • scanner.ts — cursor abstraction with position() / backtrack() for backtracking
  • chars.ts — character classification helpers (isAlpha, isDigit, isPlainChar, etc.) and the emoticon table
  • Reused existing AST utility helpers from utils.ts for node generation, and types from definitions.ts
  • Preserved the existing public API and options structure in index.ts

Parsing Features Implemented

  • Plain paragraph parsing — wraps each line in a paragraph node
  • Line break detection — blank lines emit lineBreak nodes; trailing newlines are swallowed
  • Escape sequences — \*, \_, \~, \`, \#, \.
  • Inline code — code with no markup parsing inside
  • Bold — *text* and **text** with whitespace-only and triple-asterisk edge cases
  • Italic — _text_ and __text__ with word-boundary guards to protect snake_case
  • Strikethrough — ~text~ and ~~text~~ with whitespace-only and triple-tilde edge cases
  • Re-entrancy guards (skipBold, skipItalic, skipStrike) mirroring PeggyJS skip flags
  • Heading — # through #### prefix with required space, inline content inside
  • Code fence — ``` with optional language tag, raw content (no markup inside)
  • User mention — @username supporting special characters like . - _ : @ in names
  • Channel mention — #channel with word-boundary guard
  • Blockquote — > prefixed lines with inline markup support inside
  • Inline and block spoiler — ||text|| inline and multi-line ||\n...\n|| block support
  • Markdown and angle bracket link — [title](url) and <url|title>
  • Unordered and ordered list — -/* and 1. markers with inline content per item; asterisk bullets respect the grammar's trailing-* guard (so * *, * Hello* stay inline)
  • KaTeX — block and inline parsing under the dollar-sign and parenthesis syntax options
  • Auto-link URL — bare URL / domain detection, TLD-validated via tldts
  • Emoji shortcode — :emoji: parsing, plus big-emoji handling for shortcode-only messages
  • Email autolinking — local@domainmailto: link, with TLD validation and mailto: prefix parts
  • Phone autolinking — +numbertel: link via phoneChecker, with prefix/grouping variants
  • Timestamp — <t:...> supporting Unixtime, ISO-8601 (with/without milliseconds and timezone), relative HH:MM[:SS] times, and the t/T/d/D/f/F/R format specifiers
  • Emoticon — :), :D, etc. via a symbol→shortcode table with longest-match and word-boundary dispatch, integrated into big-emoji detection
  • Color — color:#rgb / rgba / rrggbb / rrggbbaa when the colors option is enabled
  • Unicode emoji — raw emoji parsing (😀, ❤️) including ZWJ sequences, skin-tone modifiers, and variation selectors, both inline and in big-emoji detection
  • Horizontal rule — --- (three or more dashes); *** / ___ stay as emphasis
  • Table — header / delimiter / body rows with column alignment, escaped pipes, and inline markup in cells
  • Image — ![alt](src)
  • Tasks — - [ ] / - [x] checklist lines grouped into one tasks node

Test Progress

  • Passing Tests: 718/718

Benchmarks

Throughput: Hand-written parser vs. PeggyJS-generated parser (ops/sec)

Plain Text

Task PeggyJS Hand-written Speedup
short 32,950 2,352,834 71×
medium 18,876 240,708 12×
long 4,158 26,281

Emphasis / Formatting

Task PeggyJS Hand-written Speedup
bold 32,036 1,846,956 57×
italic 29,704 1,772,674 59×
strike 33,555 1,789,751 53×
nested 27,699 880,749 31×
deep nesting 25,243 693,806 27×
bold + italic mixed 23,760 606,952 25×
deeply nested 26,003 726,134 27×
multiple 21,007 426,719 20×

URLs & Links

Task PeggyJS Hand-written Speedup
single 26,949 678,914 25×
multiple 24,848 414,752 16×
markdown link 33,045 1,304,818 39×
autolinked domain 28,274 806,920 28×
with path 27,161 738,848 27×

Emoji

Task PeggyJS Hand-written Speedup
single shortcode 39,614 10,389,716 262×
triple shortcode (BigEmoji) 40,064 4,043,263 100×
single unicode 39,090 305,236 7.8×
triple unicode (BigEmoji) 38,174 121,571
in text 27,799 384,554 13×
mixed 27,505 226,199
emoji heavy 32,623 475,494 14×

Mentions

Task PeggyJS Hand-written Speedup
single user 34,858 3,618,772 103×
multiple users 33,750 1,390,627 41×
channel 34,074 3,193,902 93×
mixed 29,579 663,163 22×
mentions (suggested) 28,933 630,395 21×

Code

Task PeggyJS Hand-written Speedup
inline 30,434 874,740 28×
block 35,157 1,366,446 38×
multi inline 29,646 853,885 28×

Structured Blocks

Task PeggyJS Hand-written Speedup
ordered list 26,204 569,656 21×
unordered list 26,334 589,105 22×
task list 24,996 630,641 25×
blockquote 24,328 489,469 20×
heading 30,343 1,694,825 55×
heading multi-level 27,520 915,051 33×
spoiler 25,327 356,391 14×
spoiler with formatting 20,982 255,626 12×

KaTeX (Math)

Task PeggyJS Hand-written Speedup
inline 27,626 503,882 18×
block 35,369 1,839,119 52×

Adversarial / Stress

Task PeggyJS Hand-written Speedup
adversarial emphasis 5,947 205,544 34×
adversarial mixed 4,441 39,580
repeated specials 1,354 18,545 13×
long with formatting 6,271 33,528
unmatched markers (pathological) 6,106 53,143

Real-World Messages

Task PeggyJS Hand-written Speedup
simple 26,772 458,762 17×
medium 21,446 227,304 10×
complex 17,189 162,955
realistic chat message 17,993 191,299 10×

Timestamps

Task PeggyJS Hand-written Speedup
unix format 34,456 3,162,390 91×

Goal

  • This PR lays the groundwork for the High-Performance Message Parser Rewrite project.
  • Future commits will continue implementing parsing behavior while maintaining AST compatibility with the existing PeggyJS parser and working toward full test-suite parity.

Notes

  • No user-facing behavior changes are intended yet.
  • This is an infrastructure/foundation step toward a complete handwritten parser implementation.

Summary by CodeRabbit

  • New Features

    • Added a comprehensive Markdown-style message parser with improved recognition of blocks and inline content.
    • Supports headings, lists, tables, blockquotes, code blocks, spoilers, links, mentions, timestamps, KaTeX math, and horizontal rules.
    • Enhances detection of URLs, email addresses, emojis, and ASCII emoticons, including improved escape and delimiter handling.
  • Refactor

    • Replaced the previous grammar-based parsing pipeline with a new scanning/tokenizing parsing engine for more consistent behavior.

@dionisio-bot

dionisio-bot Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot

changeset-bot Bot commented May 28, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e4d5261

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The PEG grammar entry point is replaced by a scanner-based parser. Character and emoticon helpers support ordered inline matching, while block parsing adds Markdown-like constructs including links, formatting, lists, spoilers, KaTeX, task items, and tables. PEG loading and build configuration are removed.

Changes

Message parser implementation

Layer / File(s) Summary
Scanner and character contracts
packages/message-parser/src/chars.ts, packages/message-parser/src/scanner.ts
Character predicates, emoticon lookup tables, and cursor operations provide shared parsing primitives.
Parser entry and inline tokenization
packages/message-parser/src/parser.ts, packages/message-parser/src/index.ts
The exported parser uses the scanner-based implementation and tokenizes inline content through ordered matchers.
Inline construct matchers
packages/message-parser/src/parser.ts
Inline parsing handles formatting, code, mentions, references, links, autolinks, emoji, colors, and images.
Block construct matchers
packages/message-parser/src/parser.ts
Block parsing handles fenced code, headings, blockquotes, spoilers, lists, task items, and horizontal rules.
KaTeX, big emoji, and tables
packages/message-parser/src/parser.ts
Block parsing adds KaTeX, multi-emoji detection, and pipe tables with escaped cells and alignment metadata.
PEG runtime removal
packages/message-parser/jest.config.ts, packages/message-parser/package.json, packages/message-parser/webpack.config.ts
Test, benchmark, dependency, and webpack configuration no longer loads or transforms PEG files.

Estimated code review effort: 5 (Critical) | ~90+ minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant parse
  participant Scanner
  participant Root
  Caller->>parse: provide message input and options
  parse->>Scanner: scan characters and delimiters
  Scanner-->>parse: positions and matched slices
  parse->>Root: append inline and block nodes
  Root-->>Caller: return parsed document
Loading

Suggested labels: type: feature

Suggested reviewers: ggazzo

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main change: replacing the generated parser with a handwritten parser.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • ISO-8601: Request failed with status code 401

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ahmed-n-abdeltwab ahmed-n-abdeltwab Jun 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @amitkumarashutosh , good work getting this done. The class is clean and readable overall. I do have a few thoughts on the OOP design. The core issue is that methods like advance(), save(), restore(), and pos being public are exposing the internal mechanics of how the scanner works, rather than what the scanner does from the caller's perspective. This breaks encapsulation — callers shouldn't need to know or care how the scanner moves through the input internally. Let me use an example. Imagine you order a car from Factory A. Its public interface is start(), drive(), brake(), and getSpeed(). Now Factory B delivers a car with those same methods plus increaseSpeed(), decreaseSpeed(), and setRPM(). Most people would prefer Factory A's car — not because Factory B's car is broken, but because Factory A made a decision about what the driver should and shouldn't need to think about. The engine is managing speed under the hood, the driver just presses the pedal. Factory B is forcing the driver to manage internals that should be invisible, which makes the experience more complex and error-prone. Expose too much, and the driver causes a crash not because they were reckless, but because the interface invited the mistake. The same applies here — the Scanner class is our car, and the caller is our driver. advance(n) is a raw cursor movement that exposes internal position management directly to the caller. Instead, the scanner should move itself forward as a side effect of higher-level operations. Methods like consume(), consumeWhile(predicate), or expect(literal) would advance internally and return meaningful results — the caller should be asking what to consume, not how many characters to move. save() and restore() expose the backtracking mechanism as a manual two-step process. This is fragile because a caller can save and forget to restore. A pattern like tryConsume(literal) or a scoped attempt(() => ...) callback would hide the save/restore cycle entirely and make misuse much harder. pos being public lets anyone outside the class read and reason about the raw cursor index. Position is an implementation detail. If the caller needs location info for something like error reporting, a dedicated method like getLocation() returning something meaningful would serve that need without leaking the internals. sliceFrom(savedPos) requires the caller to hold a raw position integer and pass it back in, which ties the caller directly to internal state. A mark() method returning an opaque token, paired with getSliceSinceMarked(token), would achieve the same result without exposing the position type at all.

A well-designed class should make the right usage easy and the wrong usage hard. Right now the public API of Scanner requires callers to know a lot about how the internals work in order to use it correctly. The goal is to push that knowledge inward — hide the cursor, hide the backtracking, and expose only the operations that make sense at the scanning level.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it

Comment thread packages/message-parser/src/parser.ts
Comment thread packages/message-parser/src/parser.ts Outdated
Comment on lines +1525 to +1527
if (scanner.matches('mailto:')) {
scanner.advance(7);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a magic number slipped through here! Could you please fix this one and double-check the rest of the code for any others?

@amitkumarashutosh
amitkumarashutosh force-pushed the feat/hand-written-parser branch from 37e61da to c673fba Compare June 15, 2026 09:42
@ahmed-n-abdeltwab

Copy link
Copy Markdown
Contributor

Hey @amitkumarashutosh , I'm getting a build failure in message-parser due to an unused variable (peekPos). Have you run into this error? or it's just me

@amitkumarashutosh
amitkumarashutosh force-pushed the feat/hand-written-parser branch from c673fba to 65fd782 Compare June 15, 2026 11:40
@amitkumarashutosh

Copy link
Copy Markdown
Contributor Author

Hey @amitkumarashutosh , I'm getting a build failure in message-parser due to an unused variable (peekPos). Have you run into this error? or it's just me

@ahmed-n-abdeltwab, I resolved the unused variable (peekPos) issue. The build is working now.

@ahmed-n-abdeltwab

Copy link
Copy Markdown
Contributor

Hey @amitkumarashutosh , I'm getting a build failure in message-parser due to an unused variable (peekPos). Have you run into this error? or it's just me

@ahmed-n-abdeltwab, I resolved the unused variable (peekPos) issue. The build is working now.

Thanks. It's always a good idea to double-check everything before pushing a commit. It's okay to make these kinds of mistakes; I do it all the time and they're hard to catch. That's why I usually run the build to ensure everything compiles properly, and then run the tests and lint checks to make sure I don't mess up the code or the project. It's like evolving a third eye that's looking out for those mistakes! :)

@amitkumarashutosh
amitkumarashutosh marked this pull request as ready for review July 29, 2026 09:27
@coderabbitai coderabbitai Bot added the type: feature Pull requests that introduces new feature label Jul 29, 2026
@ahmed-n-abdeltwab

Copy link
Copy Markdown
Contributor

Nice work 🥳, @amitkumarashutosh! The cubic review is now underway. Please review and address its feedback once it finishes

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
packages/message-parser/src/chars.ts (1)

44-47: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove implementation comments from this TypeScript file.

The inline annotations, section header, and ordering note conflict with the repository rule to avoid implementation comments. Move important rationale to documentation or tests instead.

As per coding guidelines, **/*.{ts,tsx,js} files should avoid code comments in the implementation.

Also applies to: 55-55, 84-84

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/message-parser/src/chars.ts` around lines 44 - 47, Remove the inline
implementation comments from the character-range conditions in chars.ts,
including the annotations at the referenced locations and the section header or
ordering note. Preserve the existing conditions and ordering; move only
rationale that is necessary for maintainers into appropriate documentation or
tests.

Source: Coding guidelines

packages/message-parser/src/parser.ts (1)

85-98: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider trimming the section-banner and step-by-step comments.

Banners (lines 85, 98, 493, 1267, 1676) and running narration (// consume '@', // consume ')') restate the code. The genuinely useful ones are the grammar-rationale comments (e.g. lines 552, 577, 1005, 1467) — worth keeping those and dropping the rest.

As per coding guidelines, "Avoid code comments in the implementation".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/message-parser/src/parser.ts` around lines 85 - 98, Remove
non-actionable section banners and step-by-step narration comments in parser.ts,
including banners near the constants/helpers and other cited sections and
comments such as “consume '@'” or “consume ')'”. Preserve grammar-rationale
comments that explain non-obvious parsing decisions, including those around the
cited grammar logic.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/message-parser/src/parser.ts`:
- Around line 1751-1755: Initialize align in the table-column alignment logic
before the conditional chain with the PEG grammar’s default for a colon-less
column, then retain the existing left, right, and center overrides. Ensure
aligns.push receives a valid TableCellAlignment for plain `---` columns.
- Around line 681-684: Update the trailing-domain cleanup loop in the parser to
remove both `.` and `-` characters, matching the documented behavior for domains
such as `joe.com-`; preserve the existing domain-start boundary.
- Around line 167-170: Make the parser re-entrant by removing the module-level
mutable guards skipBold, skipItalic, skipStrike, and skipReferences and storing
them in per-parse state passed through the relevant parsing functions. Update
each guard site and nested parseInline/scanner call to read and mutate that
state, ensuring guard values are restored with try/finally when temporarily
changed so exceptions cannot leak state across parse() calls.
- Around line 1501-1520: Update tryOrderedList to track the current item’s start
separately from the list start, and when a continuation item is malformed (such
as missing the required dot or space), backtrack only to that item start and
break rather than returning null. Preserve already-parsed ordered items while
leaving the malformed line for subsequent parsing, matching the unordered-list
behavior.

---

Nitpick comments:
In `@packages/message-parser/src/chars.ts`:
- Around line 44-47: Remove the inline implementation comments from the
character-range conditions in chars.ts, including the annotations at the
referenced locations and the section header or ordering note. Preserve the
existing conditions and ordering; move only rationale that is necessary for
maintainers into appropriate documentation or tests.

In `@packages/message-parser/src/parser.ts`:
- Around line 85-98: Remove non-actionable section banners and step-by-step
narration comments in parser.ts, including banners near the constants/helpers
and other cited sections and comments such as “consume '@'” or “consume ')'”.
Preserve grammar-rationale comments that explain non-obvious parsing decisions,
including those around the cited grammar logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 57d36c1e-1e66-4f23-9d84-f8cbaeeedb01

📥 Commits

Reviewing files that changed from the base of the PR and between 6dc66fb and 5f8f89b.

📒 Files selected for processing (4)
  • packages/message-parser/src/chars.ts
  • packages/message-parser/src/index.ts
  • packages/message-parser/src/parser.ts
  • packages/message-parser/src/scanner.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: cubic · AI code reviewer
⚠️ CI failures not shown inline (1)

GitHub Check: Dionisio QA: Some checks did not pass

Conclusion: failure

View job details

**Conclusion:** failure
### Steps
- ✅ **No merge conflicts**
- ❌ **QA assured** — This PR is missing the 'stat: QA assured' label
- ✅ **Mergeable**
- ❌ **Has milestone or project** — This PR is missing the required milestone or project
- ✅ **Valid PR title**
- ✅ **Correct target version**
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • packages/message-parser/src/scanner.ts
  • packages/message-parser/src/index.ts
  • packages/message-parser/src/chars.ts
  • packages/message-parser/src/parser.ts
🧠 Learnings (3)
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • packages/message-parser/src/scanner.ts
  • packages/message-parser/src/index.ts
  • packages/message-parser/src/chars.ts
  • packages/message-parser/src/parser.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • packages/message-parser/src/scanner.ts
  • packages/message-parser/src/index.ts
  • packages/message-parser/src/chars.ts
  • packages/message-parser/src/parser.ts
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.

Applied to files:

  • packages/message-parser/src/scanner.ts
  • packages/message-parser/src/index.ts
  • packages/message-parser/src/chars.ts
  • packages/message-parser/src/parser.ts
🪛 ast-grep (0.45.0)
packages/message-parser/src/parser.ts

[warning] 90-92: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})\\.(\\d{3})${OPTIONAL_TIMEZONE_OFFSET}$,
)
Note: [CWE-1333] Inefficient Regular Expression Complexity

(regexp-from-variable)


[warning] 93-93: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})${OPTIONAL_TIMEZONE_OFFSET}$)
Note: [CWE-1333] Inefficient Regular Expression Complexity

(regexp-from-variable)


[warning] 94-94: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^(\\d{2}):(\\d{2}):(\\d{2})${OPTIONAL_TIMEZONE_OFFSET}$)
Note: [CWE-1333] Inefficient Regular Expression Complexity

(regexp-from-variable)


[warning] 95-95: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^(\\d{2}):(\\d{2})${OPTIONAL_TIMEZONE_OFFSET}$)
Note: [CWE-1333] Inefficient Regular Expression Complexity

(regexp-from-variable)

🪛 OpenGrep (1.26.0)
packages/message-parser/src/parser.ts

[ERROR] 764-764: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)


[ERROR] 775-775: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)


[ERROR] 785-785: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)


[ERROR] 787-787: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)


[ERROR] 1179-1179: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🔇 Additional comments (5)
packages/message-parser/src/chars.ts (1)

1-23: LGTM!

Also applies to: 25-43, 48-53, 56-82

packages/message-parser/src/scanner.ts (1)

1-45: LGTM!

packages/message-parser/src/parser.ts (2)

1173-1177: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Name the 32-char lookahead window.

A magic number slipped in here, matching the earlier magic-number feedback on this PR. Extract it as a module constant (e.g. MAX_EMOJI_SEQUENCE_LENGTH) alongside the other constants at lines 87-96.


88-88: 🩺 Stability & Availability

Align the package baseline with the new regex support.

packages/message-parser/tsconfig.json targets es2020, but UNICODE_EMOJI uses the ES2024 RegExp v flag. Since this package is exported from dist/messageParser.js, add browser/Node baseline documentation for Chrome 112+, Firefox 116+, Safari 17+, and Node 20+, or guard construction with an ASCII-safe fallback.

packages/message-parser/src/index.ts (1)

2-2: LGTM!

Also applies to: 20-20

Comment on lines +167 to +170
let skipBold = false;
let skipItalic = false;
let skipStrike = false;
let skipReferences = false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Module-level mutable guards make the parser non-reentrant and leak state on throw.

skipBold/skipItalic/skipStrike/skipReferences are process-wide singletons reset by plain assignment at lines 518-520, 567-569, 608-610, and 960-962. If anything between set and reset throws (nested parseInline, a malformed table cell scanner at line 1709), the flag stays true and every subsequent parse() call in that process silently loses bold/italic/strike/link parsing. Threading the flags through a parser state object is the durable fix; try/finally at each site is the minimum.

♻️ Minimum containment at each guard site
-	skipBold = true;
-	const content = parseInline(scanner, options, delimiter);
-	skipBold = false;
+	skipBold = true;
+	let content: Inlines[];
+	try {
+		content = parseInline(scanner, options, delimiter);
+	} finally {
+		skipBold = false;
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/message-parser/src/parser.ts` around lines 167 - 170, Make the
parser re-entrant by removing the module-level mutable guards skipBold,
skipItalic, skipStrike, and skipReferences and storing them in per-parse state
passed through the relevant parsing functions. Update each guard site and nested
parseInline/scanner call to read and mutate that state, ensuring guard values
are restored with try/finally when temporarily changed so exceptions cannot leak
state across parse() calls.

Comment thread packages/message-parser/src/parser.ts
Comment thread packages/message-parser/src/parser.ts
Comment on lines +1751 to +1755
let align: TableCellAlignment;
if (left && right) align = 'center';
else if (right) align = 'right';
else if (left) align = 'left';
aligns.push(align);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

align is unassigned for plain --- columns.

The chain has no else, so a column with no colons (| --- |, the most common case) leaves align uninitialized: TS2454 under strict, or undefined pushed into the alignment array otherwise. Give it an explicit default.

🐛 Default the alignment
-		let align: TableCellAlignment;
-		if (left && right) align = 'center';
-		else if (right) align = 'right';
-		else if (left) align = 'left';
-		aligns.push(align);
+		let align: TableCellAlignment;
+		if (left && right) align = 'center';
+		else if (right) align = 'right';
+		else align = 'left';
+		aligns.push(align);

Confirm the intended default matches the PEG grammar's output for a colon-less column before merging.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let align: TableCellAlignment;
if (left && right) align = 'center';
else if (right) align = 'right';
else if (left) align = 'left';
aligns.push(align);
let align: TableCellAlignment;
if (left && right) align = 'center';
else if (right) align = 'right';
else align = 'left';
aligns.push(align);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/message-parser/src/parser.ts` around lines 1751 - 1755, Initialize
align in the table-column alignment logic before the conditional chain with the
PEG grammar’s default for a colon-less column, then retain the existing left,
right, and center overrides. Ensure aligns.push receives a valid
TableCellAlignment for plain `---` columns.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 4 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/message-parser/src/parser.ts Outdated
Comment thread packages/message-parser/src/chars.ts
Comment thread packages/message-parser/src/parser.ts
Comment thread packages/message-parser/src/parser.ts Outdated
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.48209% with 57 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.23%. Comparing base (6dc66fb) to head (62d2249).
⚠️ Report is 10 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #40717      +/-   ##
===========================================
- Coverage    68.69%   68.23%   -0.46%     
===========================================
  Files         4139     4153      +14     
  Lines       159112   151397    -7715     
  Branches     27917    25951    -1966     
===========================================
- Hits        109300   103304    -5996     
+ Misses       44666    42939    -1727     
- Partials      5146     5154       +8     
Flag Coverage Δ
unit 70.11% <94.48%> (-0.55%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ahmed-n-abdeltwab ahmed-n-abdeltwab left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the old parser and run the tests

@amitkumarashutosh

Copy link
Copy Markdown
Contributor Author

Please remove the old parser and run the tests

Just the grammar file, or the peggy loader folder as well?

@ahmed-n-abdeltwab

Copy link
Copy Markdown
Contributor

Please remove the old parser and run the tests

Just the grammar file, or the peggy loader folder as well?

Anything we aren't using anymore should be removed, your changes should replace it and work

@coderabbitai coderabbitai Bot added type: chore and removed type: feature Pull requests that introduces new feature labels Jul 29, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 4 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/message-parser/src/chars.ts Outdated
@amitkumarashutosh
amitkumarashutosh force-pushed the feat/hand-written-parser branch from c8822be to 122d5dc Compare July 29, 2026 11:43
@coderabbitai coderabbitai Bot added the type: feature Pull requests that introduces new feature label Jul 29, 2026
@amitkumarashutosh
amitkumarashutosh force-pushed the feat/hand-written-parser branch from 122d5dc to 62d2249 Compare July 29, 2026 11:46

@ahmed-n-abdeltwab ahmed-n-abdeltwab left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Amit, try removing anything related to PegJS as well. I tried it myself and the project runs perfectly fine.

Please remove these directories:

  • packages/peggy-loader
  • packages/message-parser/loaders

Also update these files remove Peggy from them:

  • packages/message-parser/jest.config.ts
  • packages/message-parser/package.json
  • packages/message-parser/webpack.config.ts

After making the changes, try running, building, and testing the project. Everything should work normally, because we no longer need these

@amitkumarashutosh

Copy link
Copy Markdown
Contributor Author

Done. The build, tests, and all checks are passing.

@ahmed-n-abdeltwab

Copy link
Copy Markdown
Contributor

Hey @amitkumarashutosh, there is a conflict with the develop branch. Could you update the PR? 😁

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread packages/message-parser/src/parser.ts
@amitkumarashutosh
amitkumarashutosh force-pushed the feat/hand-written-parser branch from c618ab3 to 040bc19 Compare August 6, 2026 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community type: feature Pull requests that introduces new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants