From 5ce838454e245770a509f0032f0d56995bc475de Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Thu, 18 Jun 2026 08:21:37 -0400 Subject: [PATCH] fix: comment loss in import statements --- CHANGELOG.md | 3 + .../tsv_ts/src/printer/calls/arg_comments.rs | 31 +- .../tsv_ts/src/printer/calls/import_expr.rs | 244 ++-- docs/conformance_prettier.md | 4 +- .../import_dangling_comment/expected.json | 780 ++++++++++ .../import_dangling_comment/input.svelte | 35 + .../unformatted_compact.svelte | 25 + .../import_inter_arg_blank_line/expected.json | 313 ++++ .../import_inter_arg_blank_line/input.svelte | 11 + .../unformatted_compact.svelte | 11 + .../README.md | 21 + .../expected.json | 214 +++ .../input.svelte | 6 + .../output_prettier.svelte | 6 + .../unformatted_ours_compact.svelte | 4 + .../import_inter_arg_comment/expected.json | 1261 +++++++++++++++++ .../import_inter_arg_comment/input.svelte | 36 + .../unformatted_compact.svelte | 22 + .../README.md | 26 + .../expected.json | 190 +++ .../input.svelte | 6 + .../output_prettier.svelte | 6 + .../unformatted_ours_compact.svelte | 4 + 23 files changed, 3142 insertions(+), 117 deletions(-) create mode 100644 tests/fixtures/typescript/expressions/calls/import_dangling_comment/expected.json create mode 100644 tests/fixtures/typescript/expressions/calls/import_dangling_comment/input.svelte create mode 100644 tests/fixtures/typescript/expressions/calls/import_dangling_comment/unformatted_compact.svelte create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_blank_line/expected.json create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_blank_line/input.svelte create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_blank_line/unformatted_compact.svelte create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/README.md create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/expected.json create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/input.svelte create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/output_prettier.svelte create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/unformatted_ours_compact.svelte create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_comment/expected.json create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_comment/input.svelte create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_comment/unformatted_compact.svelte create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/README.md create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/expected.json create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/input.svelte create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/output_prettier.svelte create mode 100644 tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/unformatted_ours_compact.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index e6378b16..ff6739a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ non-empty and carry a `` marker; `deno task publ consistent with `bracketSpacing: false` (which is not respected by prettier-plugin-svelte) - values in Svelte block tags now consistently use TS printing paths, fixing oversights prettier-plugin-svelte +- dynamic `import()` no longer drops comments in or after its argument list + (inter-argument and own-line trailing comments), and preserves a blank line + between its arguments - reduce allocations using `SmallVec` and memoizations ## 0.1.0 diff --git a/crates/tsv_ts/src/printer/calls/arg_comments.rs b/crates/tsv_ts/src/printer/calls/arg_comments.rs index 544fb67a..ea5dcac9 100644 --- a/crates/tsv_ts/src/printer/calls/arg_comments.rs +++ b/crates/tsv_ts/src/printer/calls/arg_comments.rs @@ -643,14 +643,27 @@ impl<'a> PartitionedComments<'a> { } } + /// Emit own-line ("leading") comments each on its own line (hardline before), + /// with no comma. The bare dangling-comment emission shared by every last-argument + /// path; callers needing a trailing comma first use + /// [`emit_last_arg_dangling_comments`], while comma-less shapes (dynamic `import()`, + /// which takes no trailing comma) call this directly. Without it, own-line comments + /// before the closing paren are dropped (content loss). + pub fn emit_dangling_comments(&self, parts: &mut Vec, printer: &Printer<'_>) { + let d = printer.d(); + for comment in &self.leading { + parts.push(d.hardline()); + parts.push(printer.build_comment_doc(comment)); + } + } + /// Emit own-line ("leading") comments after the last argument, past its /// trailing comma — each on its own line (hardline before). /// - /// Ensures the trailing comma is present first, updating `comma_added`. Used by - /// the last-argument path of every call-shaped printer (plain, `new`, and - /// member-callee chains); without it, own-line line comments before the closing - /// paren are dropped (content loss). Block-only callers already worked via their - /// own filters, but line comments need this shared path. + /// Ensures the trailing comma is present first, updating `comma_added`, then defers + /// to [`emit_dangling_comments`]. Used by the last-argument path of every + /// call-shaped printer (plain, `new`, and member-callee chains). Block-only callers + /// already worked via their own filters, but line comments need this shared path. pub fn emit_last_arg_dangling_comments( &self, parts: &mut Vec, @@ -660,15 +673,11 @@ impl<'a> PartitionedComments<'a> { if self.leading.is_empty() { return; } - let d = printer.d(); if !*comma_added { - parts.push(d.text(",")); + parts.push(printer.d().text(",")); *comma_added = true; } - for comment in &self.leading { - parts.push(d.hardline()); - parts.push(printer.build_comment_doc(comment)); - } + self.emit_dangling_comments(parts, printer); } /// Emit leading comments, keeping inline block comments on the same line as `next_pos`. diff --git a/crates/tsv_ts/src/printer/calls/import_expr.rs b/crates/tsv_ts/src/printer/calls/import_expr.rs index 3a5bed82..fcbd30bc 100644 --- a/crates/tsv_ts/src/printer/calls/import_expr.rs +++ b/crates/tsv_ts/src/printer/calls/import_expr.rs @@ -5,11 +5,37 @@ // - Meta properties: `import.meta`, `new.target` use super::super::Printer; -use super::arg_comments::PartitionedComments; +use super::arg_comments::{ + PartitionedComments, has_blank_line_between_args, should_force_expansion_for_comments, +}; use super::arg_predicates::is_expandable_object; use crate::ast::internal; use tsv_lang::SymbolResolver; -use tsv_lang::doc::arena::DocId; +use tsv_lang::doc::arena::{DocArena, DocId}; + +/// Wrap import args in a breakable group: `import(` + softline-indented `inner` + +/// softline + `)`. Stays inline when it fits, breaks each side onto its own line +/// otherwise. The shared shell for every block-comment / no-line-comment layout. +fn wrap_import_group(d: &DocArena, inner: DocId) -> DocId { + d.group(d.concat(&[ + d.text("import("), + d.indent_softline(inner), + d.softline(), + d.text(")"), + ])) +} + +/// Wrap import args in a forced-multiline layout: `import(` + hardline-indented +/// `inner` + hardline + `)`. Used whenever a line comment (which runs to EOL) or an +/// own-line comment forces the parens open. +fn wrap_import_hardline(d: &DocArena, inner: DocId) -> DocId { + d.concat(&[ + d.text("import("), + d.indent(d.concat(&[d.hardline(), inner])), + d.hardline(), + d.text(")"), + ]) +} /// Build a Doc for a dynamic import expression: `import('module')` or `import('module', options)` /// @@ -32,16 +58,12 @@ pub(super) fn build_import_expression_doc( let (source_doc, leading_forces_break) = printer.build_paren_leading_value_doc(open_paren_end, source_start, raw_source_doc); - // If no options, check for trailing comments on the source arg - let Some(options) = &import_expr.options else { - // Check for trailing comments (line OR block) between source and closing paren - let source_end = import_expr.source.span().end; - let paren_close = import_expr.span.end; + let source_end = import_expr.source.span().end; + let paren_close = import_expr.span.end; + // If no options, check for trailing comments on the sole source arg. + let Some(options) = &import_expr.options else { if printer.has_comments_between(source_end, paren_close) { - // Force multi-line format with trailing comment (no trailing comma) - // Prettier: import(\n\t'path' // comment\n) - // For block comments: import('path' /* comment */); stays inline let pc = PartitionedComments::new( printer.comments, printer.line_breaks, @@ -49,115 +71,134 @@ pub(super) fn build_import_expression_doc( paren_close, ); - // Check if we have line comments (force multiline) or only block comments (keep inline) - if !pc.trailing_line.is_empty() { - let mut parts = vec![source_doc]; - pc.emit_trailing_comments(&mut parts, printer); - - // Wrap with hardlines for line comments - // Note: NOT using isolated_group because it causes indent issues - // Instead, variable.rs handles preventing assignment break via special casing - return d.concat(&[ - d.text("import("), - d.indent(d.concat(&[d.hardline(), d.concat(&parts)])), - d.hardline(), - d.text(")"), - ]); - } - - // Block comments only - wrap in group so import() can break + // Trailing region after the arg: same-line block/line comments inline, then + // own-line comments each on their own line (dangling — import takes no + // trailing comma). Without the dangling pass, own-line comments are dropped. let mut parts = vec![source_doc]; pc.emit_trailing_comments(&mut parts, printer); + pc.emit_dangling_comments(&mut parts, printer); let inner = d.concat(&parts); - return d.group(d.concat(&[ - d.text("import("), - d.indent(d.concat(&[d.softline(), inner])), - d.softline(), - d.text(")"), - ])); + + // A line comment (runs to EOL), any own-line comment, or an own-line leading + // comment before the source forces the multiline layout; a lone same-line + // block stays inline and breaks only on width. (NOT isolated_group — it + // causes indent issues; variable.rs special-cases the assignment break.) + if pc.has_trailing_line() || !pc.leading.is_empty() || leading_forces_break { + return wrap_import_hardline(d, inner); + } + return wrap_import_group(d, inner); } // Own-line leading comment: force hardline layout to preserve comment position. // Prettier's printLeadingComment() keeps own-line comments on their own line. if leading_forces_break { - return d.concat(&[ - d.text("import("), - d.indent(d.concat(&[d.hardline(), source_doc])), - d.hardline(), - d.text(")"), - ]); + return wrap_import_hardline(d, source_doc); } - // Wrap in group with softline break points so the outer import() - // can break when the line exceeds print width, matching Prettier's - // call-arg expansion behavior. Without this, only the inner arg's - // groups can break (e.g., `import(fn(\n 'long',\n))` instead of - // the correct `import(\n fn('long')\n)`). - return d.group(d.concat(&[ - d.text("import("), - d.indent(d.concat(&[d.softline(), source_doc])), - d.softline(), - d.text(")"), - ])); + // Group with softline break points so the outer import() can break when the + // line exceeds print width, matching Prettier's call-arg expansion. Without + // this, only the inner arg's groups can break (e.g., `import(fn(\n 'long',\n))` + // instead of the correct `import(\n fn('long')\n)`). + return wrap_import_group(d, source_doc); }; let options_doc = printer.build_expression_doc(options); let options_end = options.span().end; - let paren_close = import_expr.span.end; - - // Check for trailing comments after the options arg - let has_trailing_line_comments = printer.has_line_comments_between(options_end, paren_close); - let has_trailing_comments = - has_trailing_line_comments || printer.has_comments_between(options_end, paren_close); - - // Comment paths are the same regardless of whether options is an expandable object. - // The is_expandable_object check only matters for the no-comment expand-last-arg pattern. - if has_trailing_line_comments || leading_forces_break { - // Line comments or own-line leading comments force hardline layout - let mut opts_parts = vec![options_doc]; - if has_trailing_comments { - let pc = PartitionedComments::new( - printer.comments, - printer.line_breaks, - options_end, - paren_close, - ); - pc.emit_trailing_comments(&mut opts_parts, printer); - } + let options_start = options.span().start; + + let has_inter_comments = printer.has_comments_between(source_end, options_start); + let has_trailing_comments = printer.has_comments_between(options_end, paren_close); + // A blank line in the source→options gap (with no comment there) is preserved like + // every other argument gap; the comment case re-derives it comment-aware below. + let inter_blank_no_comments = !has_inter_comments + && has_blank_line_between_args( + printer.source, + printer.line_breaks, + source_end, + options_start, + ); - d.concat(&[ - d.text("import("), - d.indent(d.concat(&[ - d.hardline(), - source_doc, - d.text(","), - d.hardline(), - d.concat(&opts_parts), - ])), - d.hardline(), - d.text(")"), - ]) - } else if has_trailing_comments { - // Block comments — standard group wrapping - let pc = PartitionedComments::new( + // All comment cases — plus a blank-line gap — share one layout: a comment in the + // inter-argument gap (source→options), which the rest of this function never + // examines and would otherwise drop (content loss); a trailing comment after options; + // or an own-line comment before the source (`leading_forces_break`). Route both gaps + // through the unified argument-comment helpers, so the respect-the-newline rule (a + // hugging block leads the next arg; a stranded block stays on the comma line) is + // inherited rather than re-implemented, and bypass the expand-last-arg conditional + // group below — matching prettier disabling shouldExpandLastArg whenever an argument + // carries a comment. + if leading_forces_break + || has_inter_comments + || has_trailing_comments + || inter_blank_no_comments + { + let mut inter = PartitionedComments::new( + printer.comments, + printer.line_breaks, + source_end, + options_start, + ); + inter.route_after_comma_hugging_to_leading(printer, source_end, options_start); + + // Source arg + comma: before-comma blocks trail the source; stranded after-comma + // blocks and line comments follow the comma. + let mut head = vec![source_doc]; + inter.emit_trailing_comments_around_comma(&mut head, printer, source_end, options_start); + + // Blank line in the gap, comment-aware once routed (so a comment's own newlines + // don't read as a blank line). + let inter_blank = inter_blank_no_comments + || (has_inter_comments + && inter.has_blank_line_in_gap( + printer.source, + printer.line_breaks, + source_end, + options_start, + )); + + // Leading comments (own-line + hugged after-comma) lead the options arg; its + // trailing region follows: same-line block/line comments inline, then own-line + // comments each on their own line (dangling — import takes no trailing comma). + let mut tail = Vec::new(); + inter.emit_leading_comments_inline_aware(&mut tail, printer, options_start); + tail.push(options_doc); + let trailing = PartitionedComments::new( printer.comments, printer.line_breaks, options_end, paren_close, ); + trailing.emit_trailing_comments(&mut tail, printer); + trailing.emit_dangling_comments(&mut tail, printer); + + // A line comment (runs to EOL), an own-line comment (leading before source, in + // the gap, or dangling after options), or a blank line forces the multiline + // layout; inline blocks (hugging / before-comma / same-line trailing) leave the + // group free to stay inline and break only on width. + let force_break = leading_forces_break + || inter_blank + || trailing.has_trailing_line() + || !trailing.leading.is_empty() + || should_force_expansion_for_comments(printer, source_end, options_start); + + // The source→options separator: a blank line when the author left one (a blank + // line always forces the break), else a hardline when broken, else a soft `line`. + let sep = if inter_blank { + d.concat(&[d.literalline(), d.hardline()]) + } else if force_break { + d.hardline() + } else { + d.line() + }; + + let body = d.concat(&[d.concat(&head), sep, d.concat(&tail)]); + if force_break { + return wrap_import_hardline(d, body); + } + return wrap_import_group(d, body); + } - let mut opts_parts = vec![options_doc]; - pc.emit_trailing_comments(&mut opts_parts, printer); - let opts_with_comment = d.concat(&opts_parts); - - let arg_parts = d.join_doc([source_doc, opts_with_comment], d.comma_line()); - d.group(d.concat(&[ - d.text("import("), - d.indent_softline(arg_parts), - d.softline(), - d.text(")"), - ])) - } else if is_expandable_object(options) { + if is_expandable_object(options) { // Three-state conditional group matching Prettier's expand-last-arg: // State 0: all flat — import('source', {with: {type: 'json'}}) // State 1: expand-last — import('source', {\n\twith: ...\n}) @@ -191,12 +232,7 @@ pub(super) fn build_import_expression_doc( } else { // Standard group wrapping for non-expandable options let arg_parts = d.join_doc([source_doc, options_doc], d.comma_line()); - d.group(d.concat(&[ - d.text("import("), - d.indent_softline(arg_parts), - d.softline(), - d.text(")"), - ])) + wrap_import_group(d, arg_parts) } } diff --git a/docs/conformance_prettier.md b/docs/conformance_prettier.md index dccf39b9..baf1dea4 100644 --- a/docs/conformance_prettier.md +++ b/docs/conformance_prettier.md @@ -540,8 +540,8 @@ Prettier moves comments between syntactic boundaries into adjacent blocks, paren - Mapped `:` to value (line) → Trailing the member `;` — [mapped_value_line_comment](../tests/fixtures/typescript/types/mapped_value_line_comment_prettier_divergence/) - Type predicate `is` to type (line) → Trailing the body `{` — [predicate_is_line_comment](../tests/fixtures/typescript/types/predicate_is_line_comment_prettier_divergence/) - After last list-member comma → Before the comma — [objects](../tests/fixtures/typescript/expressions/objects/trailing_comma_comment_prettier_divergence/), [patterns](../tests/fixtures/typescript/expressions/patterns/trailing_comma_comment_prettier_divergence/), [arrays](../tests/fixtures/typescript/expressions/arrays/trailing_comma_comment_prettier_divergence/), [destructuring](../tests/fixtures/typescript/expressions/destructuring/trailing_comma_comment_prettier_divergence/), [calls](../tests/fixtures/typescript/expressions/calls/trailing_comma_comment_prettier_divergence/), [imports](../tests/fixtures/typescript/modules/imports/trailing_comma_comment_prettier_divergence/), [exports](../tests/fixtures/typescript/modules/exports/trailing_comma_comment_prettier_divergence/), [params](../tests/fixtures/typescript/declarations/function/trailing_comma_comment_prettier_divergence/), [function_type](../tests/fixtures/typescript/types/function_type/trailing_comma_comment_prettier_divergence/), [tuple](../tests/fixtures/typescript/types/tuple/trailing_comma_comment_prettier_divergence/), [type_params](../tests/fixtures/typescript/types/type_params/trailing_comma_comment_prettier_divergence/) -- Call arg after-comma block + same-line line comment → Block relocated before the comma (tsv keeps it on the comma line) — [plain](../tests/fixtures/typescript/expressions/calls/nonlast_arg_after_comma_block_then_line_prettier_divergence/), [new](../tests/fixtures/typescript/expressions/calls/new_nonlast_arg_after_comma_block_then_line_prettier_divergence/), [joined](../tests/fixtures/typescript/expressions/calls/multiline_arg_nonlast_after_comma_block_then_line_prettier_divergence/), [chain](../tests/fixtures/typescript/expressions/calls/chained/nonlast_arg_after_comma_block_then_line_prettier_divergence/) -- Call arg after-comma block, **stranded** (newline before the next arg) → Block relocated before the comma (tsv respects the newline and keeps the stranded block on the comma line). A block instead **hugging** the next arg leads it (`C`) and both formatters agree (plain match) — [stranded](../tests/fixtures/typescript/expressions/calls/nonlast_arg_after_comma_block_stranded_prettier_divergence/) +- Call arg after-comma block + same-line line comment → Block relocated before the comma (tsv keeps it on the comma line) — [plain](../tests/fixtures/typescript/expressions/calls/nonlast_arg_after_comma_block_then_line_prettier_divergence/), [new](../tests/fixtures/typescript/expressions/calls/new_nonlast_arg_after_comma_block_then_line_prettier_divergence/), [joined](../tests/fixtures/typescript/expressions/calls/multiline_arg_nonlast_after_comma_block_then_line_prettier_divergence/), [chain](../tests/fixtures/typescript/expressions/calls/chained/nonlast_arg_after_comma_block_then_line_prettier_divergence/), [import](../tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/) +- Call arg after-comma block, **stranded** (newline before the next arg) → Block relocated before the comma (tsv respects the newline and keeps the stranded block on the comma line). A block instead **hugging** the next arg leads it (`C`) and both formatters agree (plain match) — [stranded](../tests/fixtures/typescript/expressions/calls/nonlast_arg_after_comma_block_stranded_prettier_divergence/), [import](../tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/) (dynamic `import()` source→options gap; hugging match at [import_inter_arg_comment](../tests/fixtures/typescript/expressions/calls/import_inter_arg_comment/)) - Call open paren `(` trailing → Onto its own line — [open_paren_comment](../tests/fixtures/typescript/expressions/calls/open_paren_comment_prettier_divergence/), [chain](../tests/fixtures/typescript/expressions/calls/chain_open_paren_comment_prettier_divergence/), [new](../tests/fixtures/typescript/expressions/calls/new_open_paren_comment_prettier_divergence/) - Object literal `{` trailing → Onto its own line — [open_brace_comment](../tests/fixtures/typescript/expressions/objects/open_brace_comment_prettier_divergence/) - Array literal `[` trailing → Onto its own line — [open_bracket_comment](../tests/fixtures/typescript/expressions/arrays/open_bracket_comment_prettier_divergence/) diff --git a/tests/fixtures/typescript/expressions/calls/import_dangling_comment/expected.json b/tests/fixtures/typescript/expressions/calls/import_dangling_comment/expected.json new file mode 100644 index 00000000..90da96eb --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_dangling_comment/expected.json @@ -0,0 +1,780 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 551, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [] + }, + "options": null, + "comments": [ + { + "type": "Line", + "value": " own-line block comment after the source arg (no options) — its own line", + "start": 20, + "end": 94, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 75 + } + } + }, + { + "type": "Block", + "value": " c ", + "start": 124, + "end": 131, + "loc": { + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 9 + } + } + }, + { + "type": "Line", + "value": " own-line line comment after the source arg (no options)", + "start": 138, + "end": 196, + "loc": { + "start": { + "line": 8, + "column": 1 + }, + "end": { + "line": 8, + "column": 59 + } + } + }, + { + "type": "Line", + "value": " c", + "start": 226, + "end": 230, + "loc": { + "start": { + "line": 11, + "column": 2 + }, + "end": { + "line": 11, + "column": 6 + } + } + }, + { + "type": "Line", + "value": " own-line block comment after the options arg", + "start": 237, + "end": 284, + "loc": { + "start": { + "line": 14, + "column": 1 + }, + "end": { + "line": 14, + "column": 48 + } + } + }, + { + "type": "Block", + "value": " c ", + "start": 319, + "end": 326, + "loc": { + "start": { + "line": 18, + "column": 2 + }, + "end": { + "line": 18, + "column": 9 + } + } + }, + { + "type": "Line", + "value": " own-line line comment after the options arg", + "start": 333, + "end": 379, + "loc": { + "start": { + "line": 21, + "column": 1 + }, + "end": { + "line": 21, + "column": 47 + } + } + }, + { + "type": "Line", + "value": " c", + "start": 414, + "end": 418, + "loc": { + "start": { + "line": 25, + "column": 2 + }, + "end": { + "line": 25, + "column": 6 + } + } + }, + { + "type": "Line", + "value": " multiple own-line dangling comments after the options arg", + "start": 425, + "end": 485, + "loc": { + "start": { + "line": 28, + "column": 1 + }, + "end": { + "line": 28, + "column": 61 + } + } + }, + { + "type": "Block", + "value": " c1 ", + "start": 520, + "end": 528, + "loc": { + "start": { + "line": 32, + "column": 2 + }, + "end": { + "line": 32, + "column": 10 + } + } + }, + { + "type": "Line", + "value": " c2", + "start": 531, + "end": 536, + "loc": { + "start": { + "line": 33, + "column": 2 + }, + "end": { + "line": 33, + "column": 7 + } + } + } + ], + "instance": { + "type": "Script", + "start": 0, + "end": 550, + "context": "default", + "content": { + "type": "Program", + "start": 18, + "end": 541, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 35, + "column": 9 + } + }, + "body": [ + { + "type": "VariableDeclaration", + "start": 96, + "end": 135, + "loc": { + "start": { + "line": 3, + "column": 1 + }, + "end": { + "line": 6, + "column": 3 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 102, + "end": 134, + "loc": { + "start": { + "line": 3, + "column": 7 + }, + "end": { + "line": 6, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 102, + "end": 103, + "loc": { + "start": { + "line": 3, + "column": 7 + }, + "end": { + "line": 3, + "column": 8 + } + }, + "name": "a" + }, + "init": { + "type": "ImportExpression", + "start": 106, + "end": 134, + "loc": { + "start": { + "line": 3, + "column": 11 + }, + "end": { + "line": 6, + "column": 2 + } + }, + "source": { + "type": "Literal", + "start": 116, + "end": 121, + "loc": { + "start": { + "line": 4, + "column": 2 + }, + "end": { + "line": 4, + "column": 7 + } + }, + "value": "./a", + "raw": "'./a'" + } + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Line", + "value": " own-line block comment after the source arg (no options) — its own line", + "start": 20, + "end": 94 + } + ] + }, + { + "type": "VariableDeclaration", + "start": 198, + "end": 234, + "loc": { + "start": { + "line": 9, + "column": 1 + }, + "end": { + "line": 12, + "column": 3 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 204, + "end": 233, + "loc": { + "start": { + "line": 9, + "column": 7 + }, + "end": { + "line": 12, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 204, + "end": 205, + "loc": { + "start": { + "line": 9, + "column": 7 + }, + "end": { + "line": 9, + "column": 8 + } + }, + "name": "b" + }, + "init": { + "type": "ImportExpression", + "start": 208, + "end": 233, + "loc": { + "start": { + "line": 9, + "column": 11 + }, + "end": { + "line": 12, + "column": 2 + } + }, + "source": { + "type": "Literal", + "start": 218, + "end": 223, + "loc": { + "start": { + "line": 10, + "column": 2 + }, + "end": { + "line": 10, + "column": 7 + } + }, + "value": "./a", + "raw": "'./a'" + } + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Block", + "value": " c ", + "start": 124, + "end": 131 + }, + { + "type": "Line", + "value": " own-line line comment after the source arg (no options)", + "start": 138, + "end": 196 + } + ] + }, + { + "type": "VariableDeclaration", + "start": 286, + "end": 330, + "loc": { + "start": { + "line": 15, + "column": 1 + }, + "end": { + "line": 19, + "column": 3 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 292, + "end": 329, + "loc": { + "start": { + "line": 15, + "column": 7 + }, + "end": { + "line": 19, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 292, + "end": 293, + "loc": { + "start": { + "line": 15, + "column": 7 + }, + "end": { + "line": 15, + "column": 8 + } + }, + "name": "c" + }, + "init": { + "type": "ImportExpression", + "start": 296, + "end": 329, + "loc": { + "start": { + "line": 15, + "column": 11 + }, + "end": { + "line": 19, + "column": 2 + } + }, + "source": { + "type": "Literal", + "start": 306, + "end": 311, + "loc": { + "start": { + "line": 16, + "column": 2 + }, + "end": { + "line": 16, + "column": 7 + } + }, + "value": "./a", + "raw": "'./a'" + }, + "arguments": [ + { + "type": "Identifier", + "start": 315, + "end": 316, + "loc": { + "start": { + "line": 17, + "column": 2 + }, + "end": { + "line": 17, + "column": 3 + } + }, + "name": "x" + } + ] + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Line", + "value": " c", + "start": 226, + "end": 230 + }, + { + "type": "Line", + "value": " own-line block comment after the options arg", + "start": 237, + "end": 284 + } + ] + }, + { + "type": "VariableDeclaration", + "start": 381, + "end": 422, + "loc": { + "start": { + "line": 22, + "column": 1 + }, + "end": { + "line": 26, + "column": 3 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 387, + "end": 421, + "loc": { + "start": { + "line": 22, + "column": 7 + }, + "end": { + "line": 26, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 387, + "end": 388, + "loc": { + "start": { + "line": 22, + "column": 7 + }, + "end": { + "line": 22, + "column": 8 + } + }, + "name": "d" + }, + "init": { + "type": "ImportExpression", + "start": 391, + "end": 421, + "loc": { + "start": { + "line": 22, + "column": 11 + }, + "end": { + "line": 26, + "column": 2 + } + }, + "source": { + "type": "Literal", + "start": 401, + "end": 406, + "loc": { + "start": { + "line": 23, + "column": 2 + }, + "end": { + "line": 23, + "column": 7 + } + }, + "value": "./a", + "raw": "'./a'" + }, + "arguments": [ + { + "type": "Identifier", + "start": 410, + "end": 411, + "loc": { + "start": { + "line": 24, + "column": 2 + }, + "end": { + "line": 24, + "column": 3 + } + }, + "name": "x" + } + ] + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Block", + "value": " c ", + "start": 319, + "end": 326 + }, + { + "type": "Line", + "value": " own-line line comment after the options arg", + "start": 333, + "end": 379 + } + ] + }, + { + "type": "VariableDeclaration", + "start": 487, + "end": 540, + "loc": { + "start": { + "line": 29, + "column": 1 + }, + "end": { + "line": 34, + "column": 3 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 493, + "end": 539, + "loc": { + "start": { + "line": 29, + "column": 7 + }, + "end": { + "line": 34, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 493, + "end": 494, + "loc": { + "start": { + "line": 29, + "column": 7 + }, + "end": { + "line": 29, + "column": 8 + } + }, + "name": "e" + }, + "init": { + "type": "ImportExpression", + "start": 497, + "end": 539, + "loc": { + "start": { + "line": 29, + "column": 11 + }, + "end": { + "line": 34, + "column": 2 + } + }, + "source": { + "type": "Literal", + "start": 507, + "end": 512, + "loc": { + "start": { + "line": 30, + "column": 2 + }, + "end": { + "line": 30, + "column": 7 + } + }, + "value": "./a", + "raw": "'./a'" + }, + "arguments": [ + { + "type": "Identifier", + "start": 516, + "end": 517, + "loc": { + "start": { + "line": 31, + "column": 2 + }, + "end": { + "line": 31, + "column": 3 + } + }, + "name": "x" + } + ] + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Line", + "value": " c", + "start": 414, + "end": 418 + }, + { + "type": "Line", + "value": " multiple own-line dangling comments after the options arg", + "start": 425, + "end": 485 + } + ], + "trailingComments": [ + { + "type": "Block", + "value": " c1 ", + "start": 520, + "end": 528 + }, + { + "type": "Line", + "value": " c2", + "start": 531, + "end": 536 + } + ] + } + ], + "sourceType": "module" + }, + "attributes": [ + { + "type": "Attribute", + "start": 8, + "end": 17, + "name": "lang", + "name_loc": { + "start": { + "line": 1, + "column": 8, + "character": 8 + }, + "end": { + "line": 1, + "column": 12, + "character": 12 + } + }, + "value": [ + { + "start": 14, + "end": 16, + "type": "Text", + "raw": "ts", + "data": "ts" + } + ] + } + ] + } +} diff --git a/tests/fixtures/typescript/expressions/calls/import_dangling_comment/input.svelte b/tests/fixtures/typescript/expressions/calls/import_dangling_comment/input.svelte new file mode 100644 index 00000000..6382513d --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_dangling_comment/input.svelte @@ -0,0 +1,35 @@ + diff --git a/tests/fixtures/typescript/expressions/calls/import_dangling_comment/unformatted_compact.svelte b/tests/fixtures/typescript/expressions/calls/import_dangling_comment/unformatted_compact.svelte new file mode 100644 index 00000000..ae068a5c --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_dangling_comment/unformatted_compact.svelte @@ -0,0 +1,25 @@ + diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_blank_line/expected.json b/tests/fixtures/typescript/expressions/calls/import_inter_arg_blank_line/expected.json new file mode 100644 index 00000000..e012b83d --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_blank_line/expected.json @@ -0,0 +1,313 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 236, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [] + }, + "options": null, + "comments": [ + { + "type": "Line", + "value": " blank line between the source and options args is preserved", + "start": 20, + "end": 82, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 63 + } + } + }, + { + "type": "Line", + "value": " a leading blank line before the first arg is trimmed (collapses inline)", + "start": 122, + "end": 196, + "loc": { + "start": { + "line": 9, + "column": 1 + }, + "end": { + "line": 9, + "column": 75 + } + } + } + ], + "instance": { + "type": "Script", + "start": 0, + "end": 235, + "context": "default", + "content": { + "type": "Program", + "start": 18, + "end": 226, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 11, + "column": 9 + } + }, + "body": [ + { + "type": "VariableDeclaration", + "start": 84, + "end": 119, + "loc": { + "start": { + "line": 3, + "column": 1 + }, + "end": { + "line": 7, + "column": 3 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 90, + "end": 118, + "loc": { + "start": { + "line": 3, + "column": 7 + }, + "end": { + "line": 7, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 90, + "end": 91, + "loc": { + "start": { + "line": 3, + "column": 7 + }, + "end": { + "line": 3, + "column": 8 + } + }, + "name": "a" + }, + "init": { + "type": "ImportExpression", + "start": 94, + "end": 118, + "loc": { + "start": { + "line": 3, + "column": 11 + }, + "end": { + "line": 7, + "column": 2 + } + }, + "source": { + "type": "Literal", + "start": 104, + "end": 109, + "loc": { + "start": { + "line": 4, + "column": 2 + }, + "end": { + "line": 4, + "column": 7 + } + }, + "value": "./a", + "raw": "'./a'" + }, + "arguments": [ + { + "type": "Identifier", + "start": 114, + "end": 115, + "loc": { + "start": { + "line": 6, + "column": 2 + }, + "end": { + "line": 6, + "column": 3 + } + }, + "name": "x" + } + ] + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Line", + "value": " blank line between the source and options args is preserved", + "start": 20, + "end": 82 + } + ] + }, + { + "type": "VariableDeclaration", + "start": 198, + "end": 225, + "loc": { + "start": { + "line": 10, + "column": 1 + }, + "end": { + "line": 10, + "column": 28 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 204, + "end": 224, + "loc": { + "start": { + "line": 10, + "column": 7 + }, + "end": { + "line": 10, + "column": 27 + } + }, + "id": { + "type": "Identifier", + "start": 204, + "end": 205, + "loc": { + "start": { + "line": 10, + "column": 7 + }, + "end": { + "line": 10, + "column": 8 + } + }, + "name": "b" + }, + "init": { + "type": "ImportExpression", + "start": 208, + "end": 224, + "loc": { + "start": { + "line": 10, + "column": 11 + }, + "end": { + "line": 10, + "column": 27 + } + }, + "source": { + "type": "Literal", + "start": 215, + "end": 220, + "loc": { + "start": { + "line": 10, + "column": 18 + }, + "end": { + "line": 10, + "column": 23 + } + }, + "value": "./b", + "raw": "'./b'" + }, + "arguments": [ + { + "type": "Identifier", + "start": 222, + "end": 223, + "loc": { + "start": { + "line": 10, + "column": 25 + }, + "end": { + "line": 10, + "column": 26 + } + }, + "name": "y" + } + ] + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Line", + "value": " a leading blank line before the first arg is trimmed (collapses inline)", + "start": 122, + "end": 196 + } + ] + } + ], + "sourceType": "module" + }, + "attributes": [ + { + "type": "Attribute", + "start": 8, + "end": 17, + "name": "lang", + "name_loc": { + "start": { + "line": 1, + "column": 8, + "character": 8 + }, + "end": { + "line": 1, + "column": 12, + "character": 12 + } + }, + "value": [ + { + "start": 14, + "end": 16, + "type": "Text", + "raw": "ts", + "data": "ts" + } + ] + } + ] + } +} diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_blank_line/input.svelte b/tests/fixtures/typescript/expressions/calls/import_inter_arg_blank_line/input.svelte new file mode 100644 index 00000000..3c25e6a2 --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_blank_line/input.svelte @@ -0,0 +1,11 @@ + diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_blank_line/unformatted_compact.svelte b/tests/fixtures/typescript/expressions/calls/import_inter_arg_blank_line/unformatted_compact.svelte new file mode 100644 index 00000000..11ef6cd6 --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_blank_line/unformatted_compact.svelte @@ -0,0 +1,11 @@ + diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/README.md b/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/README.md new file mode 100644 index 00000000..14fb4e1e --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/README.md @@ -0,0 +1,21 @@ +# import_inter_arg_block_then_line_prettier_divergence + +A dynamic `import()`'s inter-argument gap holds a block comment **after the comma** +followed by a same-line line comment (`import('x', /* b */ // l⏎ opts)`). tsv keeps the +block where the author wrote it — on the comma line, after the comma; prettier relocates +the block **before** the comma (the line comment stays after it, since a `//` moved ahead +of the comma would comment out the comma). + +``` +// input (tsv preserves) // prettier (relocate block) +import( import( + 'aaaa…', /* b */ // l 'aaaa…' /* b */, // l + bbbb… bbbb… +); ); +``` + +This is the dynamic-`import()` instance of the call-argument +[after-comma block + same-line line comment](../nonlast_arg_after_comma_block_then_line_prettier_divergence/) +divergence — `import()` shares the same comment-position rules across every argument path. + +See [conformance_prettier.md](../../../../../docs/conformance_prettier.md) §Comment relocation. diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/expected.json b/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/expected.json new file mode 100644 index 00000000..09e37df4 --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/expected.json @@ -0,0 +1,214 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 140, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [] + }, + "options": null, + "comments": [ + { + "type": "Block", + "value": " b ", + "start": 80, + "end": 87, + "loc": { + "start": { + "line": 3, + "column": 42 + }, + "end": { + "line": 3, + "column": 49 + } + } + }, + { + "type": "Line", + "value": " l", + "start": 88, + "end": 92, + "loc": { + "start": { + "line": 3, + "column": 50 + }, + "end": { + "line": 3, + "column": 54 + } + } + } + ], + "instance": { + "type": "Script", + "start": 0, + "end": 139, + "context": "default", + "content": { + "type": "Program", + "start": 18, + "end": 130, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 6, + "column": 9 + } + }, + "body": [ + { + "type": "VariableDeclaration", + "start": 20, + "end": 129, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 5, + "column": 3 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 26, + "end": 128, + "loc": { + "start": { + "line": 2, + "column": 7 + }, + "end": { + "line": 5, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 26, + "end": 27, + "loc": { + "start": { + "line": 2, + "column": 7 + }, + "end": { + "line": 2, + "column": 8 + } + }, + "name": "a" + }, + "init": { + "type": "ImportExpression", + "start": 30, + "end": 128, + "loc": { + "start": { + "line": 2, + "column": 11 + }, + "end": { + "line": 5, + "column": 2 + } + }, + "source": { + "type": "Literal", + "start": 40, + "end": 78, + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 40 + } + }, + "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "raw": "'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'", + "trailingComments": [ + { + "type": "Block", + "value": " b ", + "start": 80, + "end": 87 + } + ] + }, + "arguments": [ + { + "type": "Identifier", + "start": 95, + "end": 125, + "loc": { + "start": { + "line": 4, + "column": 2 + }, + "end": { + "line": 4, + "column": 32 + } + }, + "name": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "leadingComments": [ + { + "type": "Line", + "value": " l", + "start": 88, + "end": 92 + } + ] + } + ] + } + } + ], + "kind": "const" + } + ], + "sourceType": "module" + }, + "attributes": [ + { + "type": "Attribute", + "start": 8, + "end": 17, + "name": "lang", + "name_loc": { + "start": { + "line": 1, + "column": 8, + "character": 8 + }, + "end": { + "line": 1, + "column": 12, + "character": 12 + } + }, + "value": [ + { + "start": 14, + "end": 16, + "type": "Text", + "raw": "ts", + "data": "ts" + } + ] + } + ] + } +} diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/input.svelte b/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/input.svelte new file mode 100644 index 00000000..e45949ba --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/input.svelte @@ -0,0 +1,6 @@ + diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/output_prettier.svelte b/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/output_prettier.svelte new file mode 100644 index 00000000..2033b4af --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/output_prettier.svelte @@ -0,0 +1,6 @@ + diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/unformatted_ours_compact.svelte b/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/unformatted_ours_compact.svelte new file mode 100644 index 00000000..0e105f37 --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_block_then_line_prettier_divergence/unformatted_ours_compact.svelte @@ -0,0 +1,4 @@ + diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_comment/expected.json b/tests/fixtures/typescript/expressions/calls/import_inter_arg_comment/expected.json new file mode 100644 index 00000000..8252807d --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_comment/expected.json @@ -0,0 +1,1261 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 862, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [] + }, + "options": null, + "comments": [ + { + "type": "Line", + "value": " after-comma block hugs the options arg → leads it", + "start": 20, + "end": 72, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 53 + } + } + }, + { + "type": "Block", + "value": " c ", + "start": 98, + "end": 105, + "loc": { + "start": { + "line": 3, + "column": 25 + }, + "end": { + "line": 3, + "column": 32 + } + } + }, + { + "type": "Line", + "value": " before-comma block trails the source arg", + "start": 112, + "end": 155, + "loc": { + "start": { + "line": 5, + "column": 1 + }, + "end": { + "line": 5, + "column": 44 + } + } + }, + { + "type": "Block", + "value": " c ", + "start": 180, + "end": 187, + "loc": { + "start": { + "line": 6, + "column": 24 + }, + "end": { + "line": 6, + "column": 31 + } + } + }, + { + "type": "Line", + "value": " line comment after the comma forces multiline; stays after the comma", + "start": 195, + "end": 266, + "loc": { + "start": { + "line": 8, + "column": 1 + }, + "end": { + "line": 8, + "column": 72 + } + } + }, + { + "type": "Line", + "value": " c", + "start": 295, + "end": 299, + "loc": { + "start": { + "line": 10, + "column": 9 + }, + "end": { + "line": 10, + "column": 13 + } + } + }, + { + "type": "Line", + "value": " own-line block between comma and options leads the options arg", + "start": 310, + "end": 375, + "loc": { + "start": { + "line": 14, + "column": 1 + }, + "end": { + "line": 14, + "column": 66 + } + } + }, + { + "type": "Block", + "value": " c ", + "start": 406, + "end": 413, + "loc": { + "start": { + "line": 17, + "column": 2 + }, + "end": { + "line": 17, + "column": 9 + } + } + }, + { + "type": "Line", + "value": " long source forces a break; the hugging block leads the (flat) options object", + "start": 424, + "end": 504, + "loc": { + "start": { + "line": 21, + "column": 1 + }, + "end": { + "line": 21, + "column": 81 + } + } + }, + { + "type": "Block", + "value": " c ", + "start": 586, + "end": 593, + "loc": { + "start": { + "line": 24, + "column": 2 + }, + "end": { + "line": 24, + "column": 9 + } + } + }, + { + "type": "Line", + "value": " a long options object expands; the hugging block leads it", + "start": 623, + "end": 683, + "loc": { + "start": { + "line": 27, + "column": 1 + }, + "end": { + "line": 27, + "column": 61 + } + } + }, + { + "type": "Block", + "value": " c ", + "start": 714, + "end": 721, + "loc": { + "start": { + "line": 30, + "column": 2 + }, + "end": { + "line": 30, + "column": 9 + } + } + } + ], + "instance": { + "type": "Script", + "start": 0, + "end": 861, + "context": "default", + "content": { + "type": "Program", + "start": 18, + "end": 852, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 36, + "column": 9 + } + }, + "body": [ + { + "type": "VariableDeclaration", + "start": 74, + "end": 109, + "loc": { + "start": { + "line": 3, + "column": 1 + }, + "end": { + "line": 3, + "column": 36 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 80, + "end": 108, + "loc": { + "start": { + "line": 3, + "column": 7 + }, + "end": { + "line": 3, + "column": 35 + } + }, + "id": { + "type": "Identifier", + "start": 80, + "end": 81, + "loc": { + "start": { + "line": 3, + "column": 7 + }, + "end": { + "line": 3, + "column": 8 + } + }, + "name": "a" + }, + "init": { + "type": "ImportExpression", + "start": 84, + "end": 108, + "loc": { + "start": { + "line": 3, + "column": 11 + }, + "end": { + "line": 3, + "column": 35 + } + }, + "source": { + "type": "Literal", + "start": 91, + "end": 96, + "loc": { + "start": { + "line": 3, + "column": 18 + }, + "end": { + "line": 3, + "column": 23 + } + }, + "value": "./a", + "raw": "'./a'", + "trailingComments": [ + { + "type": "Block", + "value": " c ", + "start": 98, + "end": 105 + } + ] + }, + "arguments": [ + { + "type": "Identifier", + "start": 106, + "end": 107, + "loc": { + "start": { + "line": 3, + "column": 33 + }, + "end": { + "line": 3, + "column": 34 + } + }, + "name": "b" + } + ] + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Line", + "value": " after-comma block hugs the options arg → leads it", + "start": 20, + "end": 72 + } + ] + }, + { + "type": "VariableDeclaration", + "start": 157, + "end": 192, + "loc": { + "start": { + "line": 6, + "column": 1 + }, + "end": { + "line": 6, + "column": 36 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 163, + "end": 191, + "loc": { + "start": { + "line": 6, + "column": 7 + }, + "end": { + "line": 6, + "column": 35 + } + }, + "id": { + "type": "Identifier", + "start": 163, + "end": 164, + "loc": { + "start": { + "line": 6, + "column": 7 + }, + "end": { + "line": 6, + "column": 8 + } + }, + "name": "c" + }, + "init": { + "type": "ImportExpression", + "start": 167, + "end": 191, + "loc": { + "start": { + "line": 6, + "column": 11 + }, + "end": { + "line": 6, + "column": 35 + } + }, + "source": { + "type": "Literal", + "start": 174, + "end": 179, + "loc": { + "start": { + "line": 6, + "column": 18 + }, + "end": { + "line": 6, + "column": 23 + } + }, + "value": "./a", + "raw": "'./a'", + "trailingComments": [ + { + "type": "Block", + "value": " c ", + "start": 180, + "end": 187 + } + ] + }, + "arguments": [ + { + "type": "Identifier", + "start": 189, + "end": 190, + "loc": { + "start": { + "line": 6, + "column": 33 + }, + "end": { + "line": 6, + "column": 34 + } + }, + "name": "d" + } + ] + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Line", + "value": " before-comma block trails the source arg", + "start": 112, + "end": 155 + } + ] + }, + { + "type": "VariableDeclaration", + "start": 268, + "end": 307, + "loc": { + "start": { + "line": 9, + "column": 1 + }, + "end": { + "line": 12, + "column": 3 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 274, + "end": 306, + "loc": { + "start": { + "line": 9, + "column": 7 + }, + "end": { + "line": 12, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 274, + "end": 275, + "loc": { + "start": { + "line": 9, + "column": 7 + }, + "end": { + "line": 9, + "column": 8 + } + }, + "name": "e" + }, + "init": { + "type": "ImportExpression", + "start": 278, + "end": 306, + "loc": { + "start": { + "line": 9, + "column": 11 + }, + "end": { + "line": 12, + "column": 2 + } + }, + "source": { + "type": "Literal", + "start": 288, + "end": 293, + "loc": { + "start": { + "line": 10, + "column": 2 + }, + "end": { + "line": 10, + "column": 7 + } + }, + "value": "./a", + "raw": "'./a'", + "trailingComments": [ + { + "type": "Line", + "value": " c", + "start": 295, + "end": 299 + } + ] + }, + "arguments": [ + { + "type": "Identifier", + "start": 302, + "end": 303, + "loc": { + "start": { + "line": 11, + "column": 2 + }, + "end": { + "line": 11, + "column": 3 + } + }, + "name": "g" + } + ] + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Line", + "value": " line comment after the comma forces multiline; stays after the comma", + "start": 195, + "end": 266 + } + ] + }, + { + "type": "VariableDeclaration", + "start": 377, + "end": 421, + "loc": { + "start": { + "line": 15, + "column": 1 + }, + "end": { + "line": 19, + "column": 3 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 383, + "end": 420, + "loc": { + "start": { + "line": 15, + "column": 7 + }, + "end": { + "line": 19, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 383, + "end": 384, + "loc": { + "start": { + "line": 15, + "column": 7 + }, + "end": { + "line": 15, + "column": 8 + } + }, + "name": "i" + }, + "init": { + "type": "ImportExpression", + "start": 387, + "end": 420, + "loc": { + "start": { + "line": 15, + "column": 11 + }, + "end": { + "line": 19, + "column": 2 + } + }, + "source": { + "type": "Literal", + "start": 397, + "end": 402, + "loc": { + "start": { + "line": 16, + "column": 2 + }, + "end": { + "line": 16, + "column": 7 + } + }, + "value": "./a", + "raw": "'./a'" + }, + "arguments": [ + { + "type": "Identifier", + "start": 416, + "end": 417, + "loc": { + "start": { + "line": 18, + "column": 2 + }, + "end": { + "line": 18, + "column": 3 + } + }, + "name": "j", + "leadingComments": [ + { + "type": "Block", + "value": " c ", + "start": 406, + "end": 413 + } + ] + } + ] + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Line", + "value": " own-line block between comma and options leads the options arg", + "start": 310, + "end": 375 + } + ] + }, + { + "type": "VariableDeclaration", + "start": 506, + "end": 620, + "loc": { + "start": { + "line": 22, + "column": 1 + }, + "end": { + "line": 25, + "column": 3 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 512, + "end": 619, + "loc": { + "start": { + "line": 22, + "column": 7 + }, + "end": { + "line": 25, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 512, + "end": 513, + "loc": { + "start": { + "line": 22, + "column": 7 + }, + "end": { + "line": 22, + "column": 8 + } + }, + "name": "h" + }, + "init": { + "type": "ImportExpression", + "start": 516, + "end": 619, + "loc": { + "start": { + "line": 22, + "column": 11 + }, + "end": { + "line": 25, + "column": 2 + } + }, + "source": { + "type": "Literal", + "start": 526, + "end": 582, + "loc": { + "start": { + "line": 23, + "column": 2 + }, + "end": { + "line": 23, + "column": 58 + } + }, + "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "raw": "'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'" + }, + "arguments": [ + { + "type": "ObjectExpression", + "start": 594, + "end": 616, + "loc": { + "start": { + "line": 24, + "column": 10 + }, + "end": { + "line": 24, + "column": 32 + } + }, + "properties": [ + { + "type": "Property", + "start": 595, + "end": 615, + "loc": { + "start": { + "line": 24, + "column": 11 + }, + "end": { + "line": 24, + "column": 31 + } + }, + "method": false, + "shorthand": false, + "computed": false, + "key": { + "type": "Identifier", + "start": 595, + "end": 599, + "loc": { + "start": { + "line": 24, + "column": 11 + }, + "end": { + "line": 24, + "column": 15 + } + }, + "name": "with" + }, + "value": { + "type": "ObjectExpression", + "start": 601, + "end": 615, + "loc": { + "start": { + "line": 24, + "column": 17 + }, + "end": { + "line": 24, + "column": 31 + } + }, + "properties": [ + { + "type": "Property", + "start": 602, + "end": 614, + "loc": { + "start": { + "line": 24, + "column": 18 + }, + "end": { + "line": 24, + "column": 30 + } + }, + "method": false, + "shorthand": false, + "computed": false, + "key": { + "type": "Identifier", + "start": 602, + "end": 606, + "loc": { + "start": { + "line": 24, + "column": 18 + }, + "end": { + "line": 24, + "column": 22 + } + }, + "name": "type" + }, + "value": { + "type": "Literal", + "start": 608, + "end": 614, + "loc": { + "start": { + "line": 24, + "column": 24 + }, + "end": { + "line": 24, + "column": 30 + } + }, + "value": "json", + "raw": "'json'" + }, + "kind": "init" + } + ] + }, + "kind": "init" + } + ], + "leadingComments": [ + { + "type": "Block", + "value": " c ", + "start": 586, + "end": 593 + } + ] + } + ] + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Line", + "value": " long source forces a break; the hugging block leads the (flat) options object", + "start": 424, + "end": 504 + } + ] + }, + { + "type": "VariableDeclaration", + "start": 685, + "end": 851, + "loc": { + "start": { + "line": 28, + "column": 1 + }, + "end": { + "line": 35, + "column": 3 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 691, + "end": 850, + "loc": { + "start": { + "line": 28, + "column": 7 + }, + "end": { + "line": 35, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 691, + "end": 692, + "loc": { + "start": { + "line": 28, + "column": 7 + }, + "end": { + "line": 28, + "column": 8 + } + }, + "name": "k" + }, + "init": { + "type": "ImportExpression", + "start": 695, + "end": 850, + "loc": { + "start": { + "line": 28, + "column": 11 + }, + "end": { + "line": 35, + "column": 2 + } + }, + "source": { + "type": "Literal", + "start": 705, + "end": 710, + "loc": { + "start": { + "line": 29, + "column": 2 + }, + "end": { + "line": 29, + "column": 7 + } + }, + "value": "./a", + "raw": "'./a'" + }, + "arguments": [ + { + "type": "ObjectExpression", + "start": 722, + "end": 847, + "loc": { + "start": { + "line": 30, + "column": 10 + }, + "end": { + "line": 34, + "column": 3 + } + }, + "properties": [ + { + "type": "Property", + "start": 727, + "end": 747, + "loc": { + "start": { + "line": 31, + "column": 3 + }, + "end": { + "line": 31, + "column": 23 + } + }, + "method": false, + "shorthand": false, + "computed": false, + "key": { + "type": "Identifier", + "start": 727, + "end": 731, + "loc": { + "start": { + "line": 31, + "column": 3 + }, + "end": { + "line": 31, + "column": 7 + } + }, + "name": "with" + }, + "value": { + "type": "ObjectExpression", + "start": 733, + "end": 747, + "loc": { + "start": { + "line": 31, + "column": 9 + }, + "end": { + "line": 31, + "column": 23 + } + }, + "properties": [ + { + "type": "Property", + "start": 734, + "end": 746, + "loc": { + "start": { + "line": 31, + "column": 10 + }, + "end": { + "line": 31, + "column": 22 + } + }, + "method": false, + "shorthand": false, + "computed": false, + "key": { + "type": "Identifier", + "start": 734, + "end": 738, + "loc": { + "start": { + "line": 31, + "column": 10 + }, + "end": { + "line": 31, + "column": 14 + } + }, + "name": "type" + }, + "value": { + "type": "Literal", + "start": 740, + "end": 746, + "loc": { + "start": { + "line": 31, + "column": 16 + }, + "end": { + "line": 31, + "column": 22 + } + }, + "value": "json", + "raw": "'json'" + }, + "kind": "init" + } + ] + }, + "kind": "init" + }, + { + "type": "Property", + "start": 752, + "end": 805, + "loc": { + "start": { + "line": 32, + "column": 3 + }, + "end": { + "line": 32, + "column": 56 + } + }, + "method": false, + "shorthand": false, + "computed": false, + "key": { + "type": "Identifier", + "start": 752, + "end": 774, + "loc": { + "start": { + "line": 32, + "column": 3 + }, + "end": { + "line": 32, + "column": 25 + } + }, + "name": "aaaaaaaaaaaaaaaaaaaaaa" + }, + "value": { + "type": "Identifier", + "start": 776, + "end": 805, + "loc": { + "start": { + "line": 32, + "column": 27 + }, + "end": { + "line": 32, + "column": 56 + } + }, + "name": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "kind": "init" + }, + { + "type": "Property", + "start": 810, + "end": 842, + "loc": { + "start": { + "line": 33, + "column": 3 + }, + "end": { + "line": 33, + "column": 35 + } + }, + "method": false, + "shorthand": false, + "computed": false, + "key": { + "type": "Identifier", + "start": 810, + "end": 823, + "loc": { + "start": { + "line": 33, + "column": 3 + }, + "end": { + "line": 33, + "column": 16 + } + }, + "name": "ccccccccccccc" + }, + "value": { + "type": "Identifier", + "start": 825, + "end": 842, + "loc": { + "start": { + "line": 33, + "column": 18 + }, + "end": { + "line": 33, + "column": 35 + } + }, + "name": "ddddddddddddddddd" + }, + "kind": "init" + } + ], + "leadingComments": [ + { + "type": "Block", + "value": " c ", + "start": 714, + "end": 721 + } + ] + } + ] + } + } + ], + "kind": "const", + "leadingComments": [ + { + "type": "Line", + "value": " a long options object expands; the hugging block leads it", + "start": 623, + "end": 683 + } + ] + } + ], + "sourceType": "module" + }, + "attributes": [ + { + "type": "Attribute", + "start": 8, + "end": 17, + "name": "lang", + "name_loc": { + "start": { + "line": 1, + "column": 8, + "character": 8 + }, + "end": { + "line": 1, + "column": 12, + "character": 12 + } + }, + "value": [ + { + "start": 14, + "end": 16, + "type": "Text", + "raw": "ts", + "data": "ts" + } + ] + } + ] + } +} diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_comment/input.svelte b/tests/fixtures/typescript/expressions/calls/import_inter_arg_comment/input.svelte new file mode 100644 index 00000000..6e330fac --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_comment/input.svelte @@ -0,0 +1,36 @@ + diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_comment/unformatted_compact.svelte b/tests/fixtures/typescript/expressions/calls/import_inter_arg_comment/unformatted_compact.svelte new file mode 100644 index 00000000..13f45e5b --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_comment/unformatted_compact.svelte @@ -0,0 +1,22 @@ + diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/README.md b/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/README.md new file mode 100644 index 00000000..7b1c7184 --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/README.md @@ -0,0 +1,26 @@ +# import_inter_arg_stranded_prettier_divergence + +A dynamic `import()`'s inter-argument block comment **stranded** after the comma — +the author put a newline between the comment and the options argument +(`import('x', /* c */⏎ opts)`). tsv respects that newline and keeps the comment where +it was written (trailing the comma line); prettier attaches it to the preceding source +argument and relocates it **before** the comma. + +``` +// input (author's placement) // tsv (preserve) // prettier (relocate) +import( import( import( + 'aaaa…', /* c */ 'aaaa…', /* c */ 'aaaa…' /* c */, + bbbb… bbbb… bbbb… +); ); ); +``` + +This is the dynamic-`import()` counterpart of the call-argument +[stranded](../nonlast_arg_after_comma_block_stranded_prettier_divergence/) divergence — +`import()` shares the same respect-the-newline rule across every argument path. When the +comment instead **hugs** the options argument (`import('x', /* c */ opts)`, no newline +between them), tsv leads the options with it and both formatters agree — see the +plain-match sibling [import_inter_arg_comment](../import_inter_arg_comment/). The single +rule: *a comment hugging the next arg leads it; a stranded comment stays on the comma +line.* + +See [conformance_prettier.md](../../../../../docs/conformance_prettier.md) §Comment relocation. diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/expected.json b/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/expected.json new file mode 100644 index 00000000..cc8c6aa6 --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/expected.json @@ -0,0 +1,190 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 143, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [] + }, + "options": null, + "comments": [ + { + "type": "Block", + "value": " c ", + "start": 88, + "end": 95, + "loc": { + "start": { + "line": 3, + "column": 50 + }, + "end": { + "line": 3, + "column": 57 + } + } + } + ], + "instance": { + "type": "Script", + "start": 0, + "end": 142, + "context": "default", + "content": { + "type": "Program", + "start": 18, + "end": 133, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 6, + "column": 9 + } + }, + "body": [ + { + "type": "VariableDeclaration", + "start": 20, + "end": 132, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 5, + "column": 3 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 26, + "end": 131, + "loc": { + "start": { + "line": 2, + "column": 7 + }, + "end": { + "line": 5, + "column": 2 + } + }, + "id": { + "type": "Identifier", + "start": 26, + "end": 27, + "loc": { + "start": { + "line": 2, + "column": 7 + }, + "end": { + "line": 2, + "column": 8 + } + }, + "name": "a" + }, + "init": { + "type": "ImportExpression", + "start": 30, + "end": 131, + "loc": { + "start": { + "line": 2, + "column": 11 + }, + "end": { + "line": 5, + "column": 2 + } + }, + "source": { + "type": "Literal", + "start": 40, + "end": 86, + "loc": { + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 48 + } + }, + "value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "raw": "'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'", + "trailingComments": [ + { + "type": "Block", + "value": " c ", + "start": 88, + "end": 95 + } + ] + }, + "arguments": [ + { + "type": "Identifier", + "start": 98, + "end": 128, + "loc": { + "start": { + "line": 4, + "column": 2 + }, + "end": { + "line": 4, + "column": 32 + } + }, + "name": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + ] + } + } + ], + "kind": "const" + } + ], + "sourceType": "module" + }, + "attributes": [ + { + "type": "Attribute", + "start": 8, + "end": 17, + "name": "lang", + "name_loc": { + "start": { + "line": 1, + "column": 8, + "character": 8 + }, + "end": { + "line": 1, + "column": 12, + "character": 12 + } + }, + "value": [ + { + "start": 14, + "end": 16, + "type": "Text", + "raw": "ts", + "data": "ts" + } + ] + } + ] + } +} diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/input.svelte b/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/input.svelte new file mode 100644 index 00000000..4c6148af --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/input.svelte @@ -0,0 +1,6 @@ + diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/output_prettier.svelte b/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/output_prettier.svelte new file mode 100644 index 00000000..a0b557f0 --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/output_prettier.svelte @@ -0,0 +1,6 @@ + diff --git a/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/unformatted_ours_compact.svelte b/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/unformatted_ours_compact.svelte new file mode 100644 index 00000000..b9cec70a --- /dev/null +++ b/tests/fixtures/typescript/expressions/calls/import_inter_arg_stranded_prettier_divergence/unformatted_ours_compact.svelte @@ -0,0 +1,4 @@ +