This document is the current working roadmap for the repo.
It is not just a historical refactor plan anymore. A fresh agent should be able to read this file and understand:
- what has already landed
- what architectural decisions are now intentional
- what remains incomplete
- what to validate before shipping changes
- which tempting directions are intentionally not the plan
The repo is in a materially better state than before the recent architecture work.
What has already landed:
- shared mapping/query helpers moved into
react-pug-core - lint-oriented semantic normalization moved into
react-pug-core - ESLint processor no longer owns core semantic lint rewrites
- embedded JS inside Pug now has a source-faithful lint surface
- embedded-source autofix reconstruction is implemented and stable for complex multiline cases
- multiline valid
p= ...and#{...}carriers are supported in the vendored lexer - multiline
${...}handling was hardened in core - attr expression emission no longer misclassifies concatenated expressions as string literals
- multiline attr-container autofix reconstruction is now stable and covered by regression tests
This means the main architecture direction is no longer speculative. It is the current baseline.
We are not moving to a whole-file Babel IR / AST-reprint architecture.
The intended model remains:
- text-based source transform backbone
- explicit copied segments and synthetic insertions
- rich mapping metadata
- region-level structural analysis where needed
Reason:
- mapping fidelity matters more than whole-file AST uniformity
- untouched code should stay untouched whenever possible
- editor/lint flows need more than plain AST node locations
Current boundary:
- parsing, compilation, mapping, query helpers, and lint-oriented semantic rewrites belong in
react-pug-core - environment-specific orchestration belongs in adapters
- TS plugin
- VS Code extension
- ESLint processor
- Babel/SWC/esbuild wrappers
Recent work repeatedly confirmed the right rule:
- if a bug is structural, fix the structure or mapping
- do not paper over it with broad suppressions
Suppressions remain acceptable only for truly synthetic generated artifacts where user-authored semantics cannot be reconstructed safely.
Strong areas now:
- multiline
p= ... - multiline
#{...} - multiline
${...} - TS syntax in expression carriers
- attr expression classification
- shared region/query mapping helpers
Strong areas now:
- shadow-document mapping model is centralized in core
- TS plugin is thinner and more adapter-like
- classification / completion / hover / diagnostic remapping is well covered
- TextMate comment/string leakage issues were fixed
Strong areas now:
- source-faithful JS diagnostics inside common embedded Pug JS sites
- source-faithful autofix for embedded JS sites
- stable multiline attr-container autofix reconstruction
- reduced transformed-surface stylistic noise
- validation against real consumer repos (
startupjs,startupjs-ui)
These are real boundaries, not surprises.
The strongest autofix contract today is for source-faithful embedded JS sites inside Pug.
Generated-JSX-surface fixes are not yet reconstructed back to original Pug with the same generality.
This should stay documented as a known limitation.
Single-line unbuffered code is well supported.
Multiline unbuffered - ... should not be forced into the same contract as multiline =/#{} without a cleaner design, because it overlaps with existing Pug statement-body semantics.
4.3 Internal formatter still depends on deprecated @stylistic/jsx-indent / @stylistic/jsx-indent-props
This remains a known follow-up.
Important historical note:
- one serious attempt to remove it already failed because formatter convergence regressed and real consumer behavior drifted
So this is not a "small cleanup". It is dedicated future work.
The embedded lint surface is intentionally trusted for:
- stylistic rules
- a narrow set of safe local rules
It is not a full replacement for the main transformed lint surface.
That is by design, because isolated embedded snippets do not always preserve full surrounding scope.
These are the things a new agent should know immediately.
If a consumer repo uses local eslint-plugin-react-pug code but published react-pug-core, preprocess can fail because the plugin expects newer core data.
For local testing, override both:
- ESLint plugin package actually used by the consumer repo
@react-pug/react-pug-core
The same rule applies to releases:
- plugin and core need to ship as a coherent pair
- validating a new plugin against an older published core is not a meaningful signal
Synthetic tests are necessary, but real-project validation is also important when those repos are available locally or when there is an explicit process for validating against them.
Common public validation targets used by this repo include:
../startupjs../startupjs-ui
Use them for tricky lint/autofix behavior when they are available. Do not assume every checkout has them in the same parent directory.
Several issues first discovered through ESLint turned out to be:
- lexer carrier limitations
- core expression-emission problems
- shared mapping math bugs
Do not assume every lint bug belongs in the ESLint plugin.
These are the best next candidates for meaningful work.
We fixed several issues in shared layers because ESLint exposed them.
What is still worth adding:
- wrapper-level Babel/SWC/esbuild smoke coverage for:
- multiline
p= (() => { ... })() - multiline
#{(() => { ... })()} - multiline
${(() => { ... })()} - concatenated attr expressions
- multiline
- at least one TS-plugin smoke test covering valid multiline embedded expressions end to end
This is a testing gap more than a production-code gap.
This is still a future architecture task.
Important rule:
- do not attempt direct raw range projection of generated-surface fixes back into original Pug
If we do this later, it must be through a proper reconstruction model, not a quick remap.
Still worth doing, but only as dedicated work with strong real-project validation.
The recent shift away from overly loose toContain(...) assertions toward exact inline snapshots was the right move.
Continue that for cases where the full emitted text is the actual contract.
That would undo the recent architecture progress.
That would likely make mapping harder, not easier.
Correctness and contract clarity are still the higher-value work.
They are related only loosely. Treat them as separate surfaces.
For any risky transform/mapping/ESLint work, the default validation checklist should be:
- targeted relevant unit/integration tests
- full
npm test - targeted validation in a relevant real consumer repo when one is available locally
- if the public validation repos are available, prefer
../startupjsand../startupjs-ui - if the bug came from another real repo, validate against that repo too when feasible
For ESLint work specifically:
- verify diagnostics
- verify
eslint --fix - inspect resulting fixed output visually
- ensure no corruption of surrounding attr containers / embedded sites
The repo is in a good place if all of these remain true:
- core remains the source of truth for shared transform and mapping logic
- adapters remain relatively thin
- ESLint keeps accurate diagnostics without broad suppressions
- embedded-source autofix stays stable and non-corrupting
- real consumer repos continue to behave correctly
- tests keep growing stronger where output shape is the contract
If continuing immediately from the current repo state, the best next steps are:
- add cross-library regression coverage for the shared bug classes discovered through ESLint
- keep documenting explicit contract boundaries when we decide not to support something yet
- only then consider larger follow-up work like generated-surface fix reconstruction or removal of deprecated formatter dependencies
If you are starting fresh in this repo, the recommended reading order is:
architecture.mdplan.mdperformance.md
Then inspect the relevant package for your task.
That order should give enough context to avoid repeating solved design mistakes.