From 7ae853cc231bb48b6af5d197362f04c1f513bb02 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Thu, 27 Aug 2026 16:37:18 +0200 Subject: [PATCH] JS: a test spec can opt into the style its own source is written in A spec that formats its output is measured against the built-in defaults, so a fixture indented with tabs comes back indented with four spaces and the expectation cannot be written as the file reads. Declaring a style meant hand-building a NamedStyles marker and overriding the spec's parser. withDetectedStyle samples the file and attaches what it finds, wired per spec via beforeRecipe. It stays opt-in to match Java, where RewriteTest never autodetects and a test asks for a style with spec.parser(JavaParser.fromJavaVersion().styles(...)). Applying it to every spec instead would diverge from that and change what 9 existing tests are measured against, 8 of them in format.test.ts, which feed deliberately messy input and assert canonical output. --- .../rewrite/src/javascript/assertions.ts | 15 +++++++++++++++ .../rewrite/test/javascript/format/format.test.ts | 11 ++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/rewrite-javascript/rewrite/src/javascript/assertions.ts b/rewrite-javascript/rewrite/src/javascript/assertions.ts index 75ff1f97903..6ed1a349c9b 100644 --- a/rewrite-javascript/rewrite/src/javascript/assertions.ts +++ b/rewrite-javascript/rewrite/src/javascript/assertions.ts @@ -256,6 +256,21 @@ export function packageLockJson(before: string, after?: AfterRecipeText): Source }; } +/** + * Gives a source file the style its own text is written in. A spec that formats its output is + * measured against the built-in defaults unless it opts in with + * `{...typescript(src), beforeRecipe: withDetectedStyle}`; a file parsed through {@link npm} is + * sampled alongside its siblings and needs no opt-in. + */ +export async function withDetectedStyle(sourceFile: JS.CompilationUnit): Promise { + const detector = Autodetect.detector(); + await detector.sample(sourceFile); + const detected = detector.build(); + return produce(sourceFile, draft => { + draft.markers = replaceMarkerByKind(draft.markers, detected); + }); +} + export function javascript(before: string | null, after?: AfterRecipeText): SourceSpec { return { kind: JS.Kind.CompilationUnit, diff --git a/rewrite-javascript/rewrite/test/javascript/format/format.test.ts b/rewrite-javascript/rewrite/test/javascript/format/format.test.ts index 4ac75e93999..f8d7673947d 100644 --- a/rewrite-javascript/rewrite/test/javascript/format/format.test.ts +++ b/rewrite-javascript/rewrite/test/javascript/format/format.test.ts @@ -40,7 +40,8 @@ import { autoFormat, AutoformatVisitor, JavaScriptVisitor, - typescript + typescript, + withDetectedStyle } from "../../../src/javascript"; @@ -702,5 +703,13 @@ const x = 1;` ) }); + test('a spec opting into its own style keeps the tabs it is written with', () => { + return spec.rewriteRun({ + //language=typescript + ...typescript("function f() {\n\tconst x = 1;\n\treturn x;\n}\n"), + beforeRecipe: withDetectedStyle + }) + }); + });