diff --git a/src/codegen/browser/code/comment.ts b/src/codegen/browser/code/comment.ts index 037c13b98..2aa5c2bda 100644 --- a/src/codegen/browser/code/comment.ts +++ b/src/codegen/browser/code/comment.ts @@ -2,7 +2,10 @@ import { AST_NODE_TYPES, TSESTree as ts } from '@typescript-eslint/types' import { baseProps } from '@/codegen/estree/nodes' -export function comment(value: string): ts.BlockStatement { +export function comment( + value: string, + newLine: ts.NewLine = { after: 'never' } +): ts.BlockStatement { // The type of node here is not important since it's just being used as // a placeholder for the comment. A BlockStatement just happens to be one // of the simplest nodes to build. @@ -11,5 +14,6 @@ export function comment(value: string): ts.BlockStatement { type: AST_NODE_TYPES.BlockStatement, comment: value, body: [], + newLine, } } diff --git a/src/codegen/browser/code/index.ts b/src/codegen/browser/code/index.ts index 53edcef0d..01ead8c79 100644 --- a/src/codegen/browser/code/index.ts +++ b/src/codegen/browser/code/index.ts @@ -93,9 +93,11 @@ export function toTypeScriptAst(test: ir.Test): ts.Program { ...scenarios.filter((item) => item !== undefined), ]) - const header = spaceAfter([comment(generateScriptHeader())]) + const header = comment(generateScriptHeader(), { + after: true, + }) return program({ - body: [...header, ...imports, ...exports], + body: [header, ...imports, ...exports], }) } diff --git a/src/codegen/browser/formatting/formatter.ts b/src/codegen/browser/formatting/formatter.ts index cbe9ea0a6..e02d76f7d 100644 --- a/src/codegen/browser/formatting/formatter.ts +++ b/src/codegen/browser/formatting/formatter.ts @@ -23,19 +23,11 @@ declare module 'prettier/plugins/estree' { } function applySpacing(node: ts.Node | null, doc: builders.Doc): builders.Doc { - if (node?.newLine === 'before') { - return [hardline, doc] - } - - if (node?.newLine === 'after') { - return [doc, hardline] - } - - if (node?.newLine === 'both') { - return [hardline, doc, hardline] - } - - return doc + return [ + node?.newLine?.before === true && hardline, + doc, + node?.newLine?.after === true && hardline, + ].filter((item) => item !== false) } /** diff --git a/src/codegen/browser/formatting/spacing.ts b/src/codegen/browser/formatting/spacing.ts index 7f78237de..8b45430d1 100644 --- a/src/codegen/browser/formatting/spacing.ts +++ b/src/codegen/browser/formatting/spacing.ts @@ -3,37 +3,63 @@ import type { TSESTree as ts } from '@typescript-eslint/types' export function trimBefore( newLine: ts.NewLine | undefined ): ts.NewLine | undefined { - return newLine === 'both' ? 'after' : undefined + if (newLine === undefined) { + return undefined + } + + return { + ...newLine, + before: newLine.before === true ? false : newLine.before, + } } export function trimAfter( newLine: ts.NewLine | undefined ): ts.NewLine | undefined { - return newLine === 'both' ? 'before' : undefined + if (newLine === undefined) { + return undefined + } + + return { + ...newLine, + after: newLine.after === true ? false : newLine.after, + } } export function mergeNewLine( - newLine: ts.NewLine | undefined, - target: ts.NewLine + target: ts.NewLine | undefined, + newLine: ts.NewLine ): ts.NewLine { - if (newLine === target) { + if (target === undefined) { return newLine } - if (newLine === undefined) { - return target - } - - return 'both' + const before = + newLine.before !== undefined + ? target.before === 'never' + ? 'never' + : newLine.before + : target.before + + const after = + newLine.after !== undefined + ? target.after === 'never' + ? 'never' + : newLine.after + : target.after + + return { before, after } } export function spaceBetween(nodes: T[]) { return nodes.map((node, index) => { - if (index === nodes.length - 1) { + const nextNode = nodes[index + 1] + + if (nextNode === undefined || nextNode.newLine?.before === 'never') { return node } - return { ...node, newLine: mergeNewLine(node.newLine, 'after') } + return { ...node, newLine: mergeNewLine(node.newLine, { after: true }) } }) } @@ -46,7 +72,7 @@ export function spaceAfter(nodes: T[]): T[] { return [ ...nodes.slice(0, -1), - { ...last, newLine: mergeNewLine(last.newLine, 'after') }, + { ...last, newLine: mergeNewLine(last.newLine, { after: true }) }, ] } @@ -64,9 +90,9 @@ export function spaceAround([first, ...rest]: T[]): T[] { const middle = rest.slice(0, -1) return [ - { ...first, newLine: mergeNewLine(first.newLine, 'before') }, + { ...first, newLine: mergeNewLine(first.newLine, { before: true }) }, ...middle, - { ...last, newLine: mergeNewLine(last.newLine, 'after') }, + { ...last, newLine: mergeNewLine(last.newLine, { after: true }) }, ] } @@ -75,7 +101,10 @@ export function spaceBefore([first, ...rest]: T[]): T[] { return [] } - return [{ ...first, newLine: mergeNewLine(first.newLine, 'before') }, ...rest] + return [ + { ...first, newLine: mergeNewLine(first.newLine, { before: true }) }, + ...rest, + ] } export function trimSpacing([first, ...rest]: T[]): T[] { diff --git a/src/codegen/estree/typescript-estree.d.ts b/src/codegen/estree/typescript-estree.d.ts index 3945a2578..c182bba09 100644 --- a/src/codegen/estree/typescript-estree.d.ts +++ b/src/codegen/estree/typescript-estree.d.ts @@ -1,6 +1,16 @@ declare module '@typescript-eslint/types' { namespace TSESTree { - type NewLine = 'before' | 'after' | 'both' + /** + * Options for specifying new line behavior around a node. Specifying + * `"never"` will prevent any new lines from being added in that position, + * even if other formatting rules would normally add them. + */ + type NewLineOption = boolean | 'never' + + type NewLine = { + before?: NewLineOption + after?: NewLineOption + } interface NodeOrTokenData { /**