Last 12 weeks · 67 commits
2 of 6 standards met
esrap 2.4.0 drops the optional marker from array and object binding patterns in TypeScript parameter lists. This changes a valid ambient signature into one that requires an argument. Reproduction Install, save the script as , and run : The two examples are printed as: Expected behavior Preserve the after each binding pattern. Checked separately with TypeScript 6.0.3, , and an ES2022 target: each original example has no syntactic or semantic diagnostics. Each printed example produces at . The native / node already contains ; no AST transformation is needed to reproduce this. An optional identifier parameter () retains its marker. Both the and printers are affected.
esrap 2.4.0 can print a concise arrow body as an unparenthesized expression starting with . JavaScript then treats the opening brace as the start of a block, and the generated code is invalid. Direct object bodies and a simple body are handled correctly. The failure occurs with a nested logical expression, a binary expression, or a conditional whose test starts with an object expression. Reproduction Install, save the script as , and run : The arrow declarations become: Each printed example fails to parse, while all three inputs are valid JavaScript. Expected behavior Keep enough parentheses to make each body an expression, for example by retaining parentheses around the whole body. The decision needs to account for the expression's leftmost emitted token, including nested expressions. Reproduced with native typescript-estree and Acorn ASTs, with both the and printers. No custom visitors or AST transformations are involved.
Two cases still lose their TypeScript parameter decorators in esrap 2.4.0: constructor parameter properties and rest parameters. Ordinary decorated parameters, including defaulted ones, work in the tested cases. This is a follow-up to #172 and #203. The two remaining cases have different causes, described below. Reproduction Install, save the script as , and run : The constructors become: For each case, the runtime observation changes from to : the decorator invocation disappears. Expected behavior Preserve in both cases. Both original sources also pass a separate TypeScript 6.0.3 typecheck with , , and an ES2022 target; the runtime demonstration above is not being used as a substitute for typechecking. Parameter property The native typescript-estree AST has the decorator on , while is . The printer selects the latter based on its truthiness, masking the outer decorator list. Also reproduced with . Rest parameter The description of #203 explicitly excluded rest parameters because the parsers tested there did not accept them. The reproduction above uses an unmodified typescript-estree 8.70.1 AST. Oxc 0.149.0 also parses these rest-parameter cases without diagnostics. The native AST has the decorator on . The shared rest/spread visitor prints the argument and type annotation without printing these decorators. Also reproduced on instance methods, static methods, and destructured rest parameters such as . Both the and printers are affected, without any AST modifications.
Replaces #183, rebuilt on top of #198 and #199. Follows the rule from #198: positions come from the AST or from the parser, never from arithmetic. Problem Since #198 every node's start and end is mapped, so a token that opens a node ( of a computed , of an object, , ) is covered. Tokens inside a node have no node boundary to anchor them and stay unmapped: the closing of a computed key, and both brackets of a computed class member () the and of parameter lists, calls, and / / / / heads the and of import and export specifier lists and import attributes, of static blocks, switch and enum bodies and in template literals and template-literal types every keyword after a node's first token: after or , , , in , after , after / , after , the second and later modifiers of a class member, , , , , , , , , TypeScript reports on ranges that start or end on these: TS2416 / TS2464 / TS1170 on (brackets included), TS1040 on , TS1029 / TS1030 on a modifier, TS1103 on in , and return-type inlay hints right after the parameter list's , which with prettier's default trailing commas sits alone on its line. The repro from https://github.com/sveltejs/esrap/pull/183#issuecomment-5778972045 on 2.3.11 maps the class member to and drops the inlay hint. This is also the exact fix for #190: maps to the token instead of to plus four characters. Change () option on / : the parser's token list, in any of the common shapes (Acorn with / , Babel , ESLint-style ). Its presence is the switch; there is no other flag. pulls the delimiters and the keywords listed above out of the token list on first use, sorts them if the parser emitted them out of order (acorn-typescript does when it backtracks), and looks one up by a bounded binary search from a position the AST does know: the after the callee's end, the after the key's end, the before the body's start, the after the consequent's end, the first after the node's start (or after its last decorator). Never leaving the node being printed keeps nested tokens apart. Without every lookup returns and the token is written unmapped, exactly as today. Closers at a node's end ( of a call, of an array, tuple or computed member, of an object, switch, enum, mapped type or interface body) are mapped from without tokens, the way already maps its . Those nodes end with that character by grammar. may have no parentheses, so uses the token lookup instead. In the printer this is one-line changes of the form plus , which replaces nine copies of "write , print the parameters, write ". Printed output is byte-identical with and without tokens; 62 sample maps gain segments and no code snapshot changes. Coverage on the samples from the #183 thread (86 delimiters; 107 keyword positions): What is left with tokens: the / operators, which are out of scope here. One estimate removed on the way writes the leading keywords with a single call: maps the start from , which is exact because the node begins with the keyword, and the end at . For a plain that is exact. For the text is , so the end lands at start + 13, which is only right when there is exactly one space between the two words; or puts that end segment in the wrong place. The same kind of estimate #192 removed elsewhere. Now: keeps its exact start and end. The kind after it comes from the tokens, so with tokens maps to its real position wherever it sits, and without tokens it is unmapped. That is the only place where the no-tokens map differs from (the sample loses that one end segment). Plain / / , including in heads, are unchanged without tokens. Decorated declarations (after #227) Follows up https://github.com/sveltejs/esrap/pull/227#issuecomment-5825792163. Two things, in the second commit: of a decorated class is mapped without tokens. The export node starts at with both Acorn and typescript-estree, whichever side of it the decorators were written on, so once the decorators are printed the visitor emits that start itself. The root visitor skips it for an export whose declaration has decorators, so nothing maps the column to any more (, , and the variants are tested). , , and the member modifiers after decorators come from the tokens. no longer clears the declaration's and ; it records that the decorators were printed, and and the root visitor consult that. The node keeps its location, so can locate after the last decorator. The root visitor maps the node's start when that start is the keyword and skips it when it is a decorator. The two parsers differ here: for typescript-estree ranges the class from (12-22), Acorn from the decorator (0-22, before its parent export node 5-22). Filed as sveltejs/acorn-typescript#117. With Acorn the position is therefore only known from the tokens, which is the case Rich described in that comment. Tests : for each construct, every delimiter or keyword of a kind in the output maps to the matching source token and the output is identical with and without tokens (brackets: 18 cases, parentheses: 27, braces: 11, keywords: 32, each with comments between the tokens; decorated exports and members: 8), plus: outer parentheses chosen for , internal tokens unmapped without tokens, closers mapped without tokens, tokens without locations, empty tokens, out-of-order tokens, ESLint-style tokens. Validation: — 518 tests passed — passed — the two pre-existing TS2580 errors in on , nothing new Commits: the squashed feature, merges of (mine after #212 / #214 / #215 / #227, then Rich's after #217–#223 and #233), the decorated-declaration follow-up, the parentheses, and the braces of an empty type-only import (, which #219 prints as one string). 🤖 Generated with [Claude Code
Repository: sveltejs/esrap. Description: Parse in reverse Stars: 140, Forks: 18. Primary language: JavaScript. Languages: JavaScript (93%), TypeScript (7%). License: MIT. Homepage: https://svelte.dev/playground/1bfe207e15c841d49ceff859b342260d Latest release: v2.4.0 (2d ago). Open PRs: 2, open issues: 9. Last activity: 2d ago. Community health: 50%. Top contributors: Rich-Harris, manuel3108, github-actions[bot], MathiasWP, jycouet, leonidaz, andylovescode, dummdidumm, elliott-with-the-longest-name-on-github, paoloricciuti and others.