+1
-1
| { | ||
| "name": "esrap", | ||
| "version": "2.2.6", | ||
| "version": "2.2.7", | ||
| "description": "Parse in reverse", | ||
@@ -5,0 +5,0 @@ "repository": { |
+276
-74
@@ -77,2 +77,114 @@ /** @import { TSESTree } from '@typescript-eslint/types' */ | ||
| /** | ||
| * Writes `keyword` bounded by source map locations for the exact character span, | ||
| * so breakpoints line up on keywords (not only identifiers and braces). | ||
| * | ||
| * @param {import('esrap').Context} context | ||
| * @param {number} line ESTree / acorn 1-based line | ||
| * @param {number} column 0-based ESTree column (UTF-16 indices, same as emitted JS) | ||
| * @param {string} keyword | ||
| */ | ||
| function write_source_keyword(context, line, column, keyword) { | ||
| context.location(line, column); | ||
| context.write(keyword); | ||
| context.location(line, column + keyword.length); | ||
| } | ||
| /** | ||
| * Sequential fragments from one ESTree `loc.start`, advancing columns for source mappings. | ||
| * Pass each printed fragment exactly (usually including trailing spaces), e.g. `declare `, `class `. | ||
| * Usage: `const kw = create_keyword_write(context, node, predicate);` | ||
| * | ||
| * @param {import('esrap').Context} context | ||
| * @param {TSESTree.Node} node | ||
| * @param {(n: any) => boolean} [map_ok] When false or missing `loc`, writes go through `context.write` only. | ||
| * @returns {(fragment: string) => void} | ||
| */ | ||
| function create_keyword_write(context, node, map_ok) { | ||
| let cursor = | ||
| node.loc && (!map_ok || map_ok(node)) | ||
| ? { line: node.loc.start.line, col: node.loc.start.column } | ||
| : null; | ||
| return (fragment) => { | ||
| if (cursor) { | ||
| write_source_keyword(context, cursor.line, cursor.col, fragment); | ||
| cursor.col += fragment.length; | ||
| } else { | ||
| context.write(fragment); | ||
| } | ||
| }; | ||
| } | ||
| /** | ||
| * Map one keyword at `node.loc.start`, then append unmapped `suffix` (spaces, punctuation, etc.). | ||
| * | ||
| * @param {import('esrap').Context} context | ||
| * @param {TSESTree.Node} node | ||
| * @param {string} keyword | ||
| * @param {string} [suffix=''] | ||
| */ | ||
| function write_keyword(context, node, keyword, suffix = '') { | ||
| if (node.loc) { | ||
| write_source_keyword(context, node.loc.start.line, node.loc.start.column, keyword); | ||
| context.write(suffix); | ||
| } else { | ||
| context.write(keyword + suffix); | ||
| } | ||
| } | ||
| /** | ||
| * `async function` offset heuristics assume the `function` token is on the same line as `async` | ||
| * and anchored with the function `id`/`body` ESTree locations. | ||
| * | ||
| * @param {TSESTree.FunctionDeclaration | TSESTree.FunctionExpression} node | ||
| */ | ||
| function function_async_function_offset_ok(node) { | ||
| const line = /** @type {{ loc?: { start: { line: number } } }} */ (node).loc?.start.line; | ||
| if (line === undefined) return false; | ||
| return node.id?.loc?.start.line === line || node.body?.loc?.start.line === line; | ||
| } | ||
| /** @param {{ loc?: { start: { line: number }, end: { line: number } } }} node */ | ||
| function single_line_node(node) { | ||
| return !!(node.loc && node.loc.start.line === node.loc.end.line); | ||
| } | ||
| /** | ||
| * @param {TSESTree.ClassDeclaration | TSESTree.ClassExpression} node | ||
| * @returns {boolean} | ||
| */ | ||
| function class_modifier_keywords_map_ok(node) { | ||
| return ( | ||
| !!node.loc && | ||
| !node.decorators?.length && | ||
| (node.id | ||
| ? node.id.loc?.start.line === node.loc.start.line | ||
| : node.body.loc.start.line === node.loc.start.line) | ||
| ); | ||
| } | ||
| /** | ||
| * @param {TSESTree.MethodDefinition | TSESTree.TSAbstractMethodDefinition} node | ||
| * @returns {boolean} | ||
| */ | ||
| function method_modifiers_keywords_map_ok(node) { | ||
| return !!( | ||
| node.loc && | ||
| !node.decorators?.length && | ||
| node.loc.start.line === node.value.loc?.start.line | ||
| ); | ||
| } | ||
| /** | ||
| * @param {TSESTree.PropertyDefinition | TSESTree.TSAbstractPropertyDefinition | TSESTree.AccessorProperty | TSESTree.TSAbstractAccessorProperty} node | ||
| * @returns {boolean} | ||
| */ | ||
| function field_modifiers_keywords_map_ok(node) { | ||
| if (!node.loc || node.decorators?.length) return false; | ||
| if (!node.value?.loc) return true; | ||
| return node.loc.start.line === node.value.loc.start.line; | ||
| } | ||
| /** | ||
| * @param {BaseComment} comment | ||
@@ -463,3 +575,3 @@ * @param {Context} context | ||
| if (node.type === 'NewExpression') { | ||
| context.write('new '); | ||
| write_keyword(context, node, 'new', ' '); | ||
| } | ||
@@ -555,10 +667,8 @@ | ||
| if (node.declare) { | ||
| context.write('declare '); | ||
| } | ||
| const kw = create_keyword_write(context, node, class_modifier_keywords_map_ok); | ||
| if (node.abstract) context.write('abstract '); | ||
| if (node.declare) kw('declare '); | ||
| if (node.abstract) kw('abstract '); | ||
| kw('class '); | ||
| context.write('class '); | ||
| if (node.id) { | ||
@@ -595,4 +705,10 @@ context.visit(node.id); | ||
| 'ForInStatement|ForOfStatement': (node, context) => { | ||
| context.write('for '); | ||
| if (node.type === 'ForOfStatement' && node.await) context.write('await '); | ||
| const kw = create_keyword_write(context, node); | ||
| kw('for '); | ||
| if (node.type === 'ForOfStatement' && node.await) { | ||
| if (single_line_node(node)) kw('await '); | ||
| else context.write('await '); | ||
| } | ||
| context.write('('); | ||
@@ -617,4 +733,27 @@ | ||
| 'FunctionDeclaration|FunctionExpression': (node, context) => { | ||
| if (node.async) context.write('async '); | ||
| context.write(node.generator ? 'function* ' : 'function '); | ||
| if (!node.loc?.start) { | ||
| if (node.async) context.write('async '); | ||
| context.write(node.generator ? 'function* ' : 'function '); | ||
| } else { | ||
| const { line, column } = node.loc.start; | ||
| if (node.async) { | ||
| if (function_async_function_offset_ok(node)) { | ||
| write_source_keyword(context, line, column, 'async '); | ||
| const col2 = column + 'async '.length; | ||
| write_source_keyword(context, line, col2, 'function'); | ||
| context.write(node.generator ? '* ' : ' '); | ||
| } else { | ||
| context.write('async '); | ||
| context.write(node.generator ? 'function* ' : 'function '); | ||
| } | ||
| } else if (node.generator) { | ||
| write_source_keyword(context, line, column, 'function'); | ||
| context.write('* '); | ||
| } else { | ||
| write_source_keyword(context, line, column, 'function'); | ||
| context.write(' '); | ||
| } | ||
| } | ||
| if (node.id) context.visit(node.id); | ||
@@ -648,31 +787,30 @@ | ||
| const kw = create_keyword_write(context, node, method_modifiers_keywords_map_ok); | ||
| // @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions | ||
| if (node.abstract || node.type === 'TSAbstractMethodDefinition') { | ||
| context.write('abstract '); | ||
| kw('abstract '); | ||
| } | ||
| if (node.accessibility) { | ||
| context.write(node.accessibility + ' '); | ||
| kw(node.accessibility + ' '); | ||
| } | ||
| if (node.override) { | ||
| context.write('override '); | ||
| kw('override '); | ||
| } | ||
| if (node.static) { | ||
| context.write('static '); | ||
| kw('static '); | ||
| } | ||
| if (node.kind === 'get' || node.kind === 'set') { | ||
| // Getter or setter | ||
| context.write(node.kind + ' '); | ||
| kw(node.kind + ' '); | ||
| } | ||
| if (node.value.async) { | ||
| context.write('async '); | ||
| kw('async '); | ||
| } | ||
| if (node.value.generator) { | ||
| context.write('*'); | ||
| } | ||
| if (node.value.generator) context.write('*'); | ||
@@ -713,4 +851,6 @@ if (node.computed) context.write('['); | ||
| const kw = create_keyword_write(context, node, field_modifiers_keywords_map_ok); | ||
| if (node.accessibility) { | ||
| context.write(node.accessibility + ' '); | ||
| kw(node.accessibility + ' '); | ||
| } | ||
@@ -724,7 +864,7 @@ | ||
| ) { | ||
| context.write('abstract '); | ||
| kw('abstract '); | ||
| } | ||
| if (node.static) { | ||
| context.write('static '); | ||
| kw('static '); | ||
| } | ||
@@ -738,3 +878,3 @@ | ||
| ) { | ||
| context.write('accessor '); | ||
| kw('accessor '); | ||
| } | ||
@@ -866,8 +1006,18 @@ | ||
| ArrowFunctionExpression: (node, context) => { | ||
| if (node.async) context.write('async '); | ||
| if (node.async) { | ||
| write_keyword(context, node, 'async', ' '); | ||
| } | ||
| if (node.typeParameters) { | ||
| context.visit(node.typeParameters); | ||
| } | ||
| context.write('('); | ||
| sequence(context, node.params, node.body.loc?.start ?? null, false); | ||
| context.write(') => '); | ||
| sequence(context, node.params, (node.returnType ?? node.body).loc?.start ?? null, false); | ||
| context.write(')'); | ||
| if (node.returnType) context.visit(node.returnType); | ||
| context.write(' => '); | ||
| if ( | ||
@@ -904,11 +1054,11 @@ node.body.type === 'ObjectExpression' || | ||
| if (precedence && precedence < EXPRESSIONS_PRECEDENCE.AwaitExpression) { | ||
| context.write('await ('); | ||
| write_keyword(context, node, 'await', ' ('); | ||
| context.visit(node.argument); | ||
| context.write(')'); | ||
| } else { | ||
| context.write('await '); | ||
| write_keyword(context, node, 'await', ' '); | ||
| context.visit(node.argument); | ||
| } | ||
| } else { | ||
| context.write('await'); | ||
| write_keyword(context, node, 'await', ''); | ||
| } | ||
@@ -923,7 +1073,7 @@ }, | ||
| if (node.label) { | ||
| context.write('break '); | ||
| write_keyword(context, node, 'break', ' '); | ||
| context.visit(node.label); | ||
| context.write(';'); | ||
| } else { | ||
| context.write('break;'); | ||
| write_keyword(context, node, 'break', ';'); | ||
| } | ||
@@ -984,7 +1134,7 @@ }, | ||
| if (node.label) { | ||
| context.write('continue '); | ||
| write_keyword(context, node, 'continue', ' '); | ||
| context.visit(node.label); | ||
| context.write(';'); | ||
| } else { | ||
| context.write('continue;'); | ||
| write_keyword(context, node, 'continue', ';'); | ||
| } | ||
@@ -1005,5 +1155,15 @@ }, | ||
| DoWhileStatement(node, context) { | ||
| context.write('do '); | ||
| write_keyword(context, node, 'do', ' '); | ||
| context.visit(node.body); | ||
| context.write(' while ('); | ||
| const test_loc = node.test.loc?.start; | ||
| const body_end = node.body.loc?.end; | ||
| if (test_loc && body_end && body_end.line === test_loc.line && test_loc.column >= 6) { | ||
| context.write(' '); | ||
| write_source_keyword(context, body_end.line, body_end.column + 1, 'while'); | ||
| context.write(' ('); | ||
| } else { | ||
| context.write(' while ('); | ||
| } | ||
| context.visit(node.test); | ||
@@ -1031,3 +1191,5 @@ context.write(');'); | ||
| ExportDefaultDeclaration(node, context) { | ||
| context.write('export default '); | ||
| const kw = create_keyword_write(context, node, single_line_node); | ||
| kw('export '); | ||
| kw('default '); | ||
@@ -1049,3 +1211,3 @@ context.visit(node.declaration); | ||
| } | ||
| context.write('export '); | ||
| write_keyword(context, node, 'export', ' '); | ||
| // Temporarily remove decorators so ClassDeclaration doesn't print them again | ||
@@ -1057,3 +1219,3 @@ const savedDecorators = decl.decorators; | ||
| } else { | ||
| context.write('export '); | ||
| write_keyword(context, node, 'export', ' '); | ||
| context.visit(node.declaration); | ||
@@ -1064,6 +1226,8 @@ } | ||
| context.write('export '); | ||
| const kw = create_keyword_write(context, node); | ||
| kw('export '); | ||
| if (node.exportKind === 'type') { | ||
| context.write('type '); | ||
| if (single_line_node(node)) kw('type '); | ||
| else context.write('type '); | ||
| } | ||
@@ -1119,3 +1283,3 @@ | ||
| ForStatement: (node, context) => { | ||
| context.write('for ('); | ||
| write_keyword(context, node, 'for', ' ('); | ||
@@ -1155,3 +1319,3 @@ if (node.init) { | ||
| IfStatement(node, context) { | ||
| context.write('if ('); | ||
| write_keyword(context, node, 'if', ' ('); | ||
| context.visit(node.test); | ||
@@ -1163,3 +1327,12 @@ context.write(') '); | ||
| context.space(); | ||
| context.write('else '); | ||
| const alt_loc = node.alternate.loc?.start; | ||
| const con_end = node.consequent.loc?.end; | ||
| if (alt_loc && con_end && con_end.line === alt_loc.line && alt_loc.column >= 4) { | ||
| write_source_keyword(context, con_end.line, con_end.column + 1, 'else'); | ||
| context.write(' '); | ||
| } else { | ||
| context.write('else '); | ||
| } | ||
| context.visit(node.alternate); | ||
@@ -1171,3 +1344,3 @@ } | ||
| if (node.specifiers.length === 0) { | ||
| context.write('import '); | ||
| write_keyword(context, node, 'import', ' '); | ||
| context.visit(node.source); | ||
@@ -1197,5 +1370,10 @@ context.write(';'); | ||
| context.write('import '); | ||
| if (node.importKind == 'type') context.write('type '); | ||
| const kw = create_keyword_write(context, node); | ||
| kw('import '); | ||
| if (node.importKind == 'type') { | ||
| if (single_line_node(node)) kw('type '); | ||
| else context.write('type '); | ||
| } | ||
| if (default_specifier) { | ||
@@ -1235,3 +1413,3 @@ context.write(default_specifier.local.name, default_specifier); | ||
| ImportExpression(node, context) { | ||
| context.write('import('); | ||
| write_keyword(context, node, 'import', '('); | ||
| context.visit(node.source); | ||
@@ -1366,4 +1544,6 @@ //@ts-expect-error for some reason the types haven't been updated | ||
| if (node.value.type === 'FunctionExpression') { | ||
| if (node.kind !== 'init') context.write(node.kind + ' '); | ||
| if (node.value.async) context.write('async '); | ||
| const kw = create_keyword_write(context, node); | ||
| if (node.kind !== 'init') kw(node.kind + ' '); | ||
| if (node.value.async) kw('async '); | ||
| if (node.value.generator) context.write('*'); | ||
@@ -1388,3 +1568,5 @@ if (node.computed) context.write('['); | ||
| if (node.computed) context.write('['); | ||
| if (node.kind === 'get' || node.kind === 'set') context.write(node.kind + ' '); | ||
| if (node.kind === 'get' || node.kind === 'set') { | ||
| write_keyword(context, node, node.kind, ' '); | ||
| } | ||
| context.visit(node.key); | ||
@@ -1411,7 +1593,7 @@ context.write(node.computed ? ']: ' : ': '); | ||
| context.write(contains_comment ? 'return (' : 'return '); | ||
| write_keyword(context, node, 'return', contains_comment ? ' (' : ' '); | ||
| context.visit(node.argument); | ||
| context.write(contains_comment ? ');' : ';'); | ||
| } else { | ||
| context.write('return;'); | ||
| write_keyword(context, node, 'return', ';'); | ||
| } | ||
@@ -1429,3 +1611,3 @@ }, | ||
| StaticBlock(node, context) { | ||
| context.write('static {'); | ||
| write_keyword(context, node, 'static', ' {'); | ||
| context.indent(); | ||
@@ -1446,3 +1628,3 @@ context.newline(); | ||
| SwitchStatement(node, context) { | ||
| context.write('switch ('); | ||
| write_keyword(context, node, 'switch', ' ('); | ||
| context.visit(node.discriminant); | ||
@@ -1463,3 +1645,3 @@ context.write(') {'); | ||
| context.newline(); | ||
| context.write('case '); | ||
| write_keyword(context, block, 'case', ' '); | ||
| context.visit(block.test); | ||
@@ -1469,3 +1651,3 @@ context.write(':'); | ||
| context.newline(); | ||
| context.write('default:'); | ||
| write_keyword(context, block, 'default', ':'); | ||
| } | ||
@@ -1519,3 +1701,3 @@ | ||
| ThrowStatement(node, context) { | ||
| context.write('throw '); | ||
| write_keyword(context, node, 'throw', ' '); | ||
| if (node.argument) context.visit(node.argument); | ||
@@ -1526,12 +1708,14 @@ context.write(';'); | ||
| TryStatement(node, context) { | ||
| context.write('try '); | ||
| write_keyword(context, node, 'try', ' '); | ||
| context.visit(node.block); | ||
| if (node.handler) { | ||
| context.write(' '); | ||
| if (node.handler.param) { | ||
| context.write(' catch('); | ||
| write_keyword(context, node.handler, 'catch', '('); | ||
| context.visit(node.handler.param); | ||
| context.write(') '); | ||
| } else { | ||
| context.write(' catch '); | ||
| write_keyword(context, node.handler, 'catch', ' '); | ||
| } | ||
@@ -1543,3 +1727,12 @@ | ||
| if (node.finalizer) { | ||
| context.write(' finally '); | ||
| const fin_loc = node.finalizer.loc?.start; | ||
| const prev_end = node.handler ? node.handler.loc?.end : node.block.loc?.end; | ||
| if (fin_loc && prev_end && prev_end.line === fin_loc.line && fin_loc.column >= 7) { | ||
| context.write(' '); | ||
| write_source_keyword(context, prev_end.line, prev_end.column + 1, 'finally'); | ||
| context.write(' '); | ||
| } else { | ||
| context.write(' finally '); | ||
| } | ||
| context.visit(node.finalizer); | ||
@@ -1590,3 +1783,3 @@ } | ||
| WhileStatement(node, context) { | ||
| context.write('while ('); | ||
| write_keyword(context, node, 'while', ' ('); | ||
| context.visit(node.test); | ||
@@ -1598,3 +1791,3 @@ context.write(') '); | ||
| WithStatement(node, context) { | ||
| context.write('with ('); | ||
| write_keyword(context, node, 'with', ' ('); | ||
| context.visit(node.object); | ||
@@ -1606,7 +1799,9 @@ context.write(') '); | ||
| YieldExpression(node, context) { | ||
| const word = node.delegate ? 'yield*' : 'yield'; | ||
| if (node.argument) { | ||
| context.write(node.delegate ? `yield* ` : `yield `); | ||
| write_keyword(context, node, word, ' '); | ||
| context.visit(node.argument); | ||
| } else { | ||
| context.write(node.delegate ? `yield*` : `yield`); | ||
| write_keyword(context, node, word, ''); | ||
| } | ||
@@ -1628,9 +1823,11 @@ }, | ||
| TSDeclareFunction(node, context) { | ||
| context.write('declare '); | ||
| const kw = create_keyword_write(context, node); | ||
| kw('declare '); | ||
| if (node.async) { | ||
| context.write('async '); | ||
| kw('async '); | ||
| } | ||
| context.write('function'); | ||
| kw('function'); | ||
@@ -1834,2 +2031,7 @@ if (node.generator) { | ||
| } | ||
| if (node.default) { | ||
| context.write(' = '); | ||
| context.visit(node.default); | ||
| } | ||
| }, | ||
@@ -2244,7 +2446,7 @@ | ||
| if (node.declare) { | ||
| child_context.write('declare '); | ||
| } | ||
| const kw = create_keyword_write(child_context, node); | ||
| child_context.write(`${node.kind} `); | ||
| if (node.declare) kw('declare '); | ||
| kw(node.kind + ' '); | ||
| child_context.append(open); | ||
@@ -2251,0 +2453,0 @@ |
87522
8.32%2628
7%