Skip to content

Commit 188eba2

Browse files
autofix-ci[bot]leaysgur
authored andcommitted
[autofix.ci] apply automated fixes
1 parent 8b57910 commit 188eba2

File tree

14 files changed

+898
-345
lines changed

14 files changed

+898
-345
lines changed

apps/oxfmt/cmp.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// oxlint-disable
2+
3+
import { readdir, readFile } from "node:fs/promises";
4+
import { join } from "node:path";
5+
import * as prettier from "prettier";
6+
import { format as oxfmtFormat } from "./dist/index.js";
7+
8+
const FIXTURES_DIR = join(
9+
import.meta.dirname,
10+
"../../tasks/prettier_conformance/prettier/tests/format/js/multiparser-graphql",
11+
);
12+
13+
const EXCLUDE = new Set([
14+
"format.test.js",
15+
"comment-tag.js", // /* GraphQL */ comment tag (not yet supported)
16+
"expressions.js", // graphql() function call pattern (not yet supported)
17+
"graphql.js", // graphql() function call pattern (not yet supported)
18+
]);
19+
20+
const files = (await readdir(FIXTURES_DIR))
21+
.filter((f) => f.endsWith(".js") && !EXCLUDE.has(f))
22+
.sort();
23+
24+
let matchCount = 0;
25+
let mismatchCount = 0;
26+
let errorCount = 0;
27+
28+
for (const file of files) {
29+
const filePath = join(FIXTURES_DIR, file);
30+
const source = await readFile(filePath, "utf8");
31+
32+
const prettierOutput = await prettier.format(source, {
33+
parser: "babel",
34+
printWidth: 80,
35+
});
36+
37+
let oxfmtOutput;
38+
try {
39+
const oxfmtResult = await oxfmtFormat(file, source, { printWidth: 80 });
40+
oxfmtOutput = oxfmtResult.code;
41+
} catch (e) {
42+
console.log(`✗ ${file} (ERROR: ${e.message})`);
43+
errorCount++;
44+
continue;
45+
}
46+
47+
if (prettierOutput === oxfmtOutput) {
48+
console.log(`✓ ${file}`);
49+
matchCount++;
50+
} else {
51+
console.log(`✗ ${file}`);
52+
mismatchCount++;
53+
printUnifiedDiff(prettierOutput, oxfmtOutput);
54+
}
55+
}
56+
57+
console.log(`\n--- Summary ---`);
58+
console.log(
59+
`Match: ${matchCount}, Mismatch: ${mismatchCount}, Error: ${errorCount}, Total: ${files.length}`,
60+
);
61+
62+
function printUnifiedDiff(expected, actual) {
63+
const expectedLines = expected.split("\n");
64+
const actualLines = actual.split("\n");
65+
console.log(" --- prettier");
66+
console.log(" +++ oxfmt");
67+
const maxLen = Math.max(expectedLines.length, actualLines.length);
68+
for (let i = 0; i < maxLen; i++) {
69+
const e = expectedLines[i];
70+
const a = actualLines[i];
71+
if (e === a) {
72+
console.log(` ${e ?? ""}`);
73+
} else {
74+
if (e !== undefined) console.log(` -${e}`);
75+
if (a !== undefined) console.log(` +${a}`);
76+
}
77+
}
78+
console.log();
79+
}

apps/oxfmt/src-js/bindings.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export declare const enum Severity {
3434
* # Panics
3535
* Panics if the current working directory cannot be determined.
3636
*/
37-
export declare function format(filename: string, sourceText: string, options: any | undefined | null, initExternalFormatterCb: (numThreads: number) => Promise<string[]>, formatEmbeddedCb: (options: Record<string, any>, code: string) => Promise<string>, formatEmbeddedDocCb: (options: Record<string, any>, code: string) => Promise<string>, formatFileCb: (options: Record<string, any>, code: string) => Promise<string>, sortTailwindClassesCb: (options: Record<string, any>, classes: string[]) => Promise<string[]>): Promise<FormatResult>
37+
export declare function format(filename: string, sourceText: string, options: any | undefined | null, initExternalFormatterCb: (numThreads: number) => Promise<string[]>, formatEmbeddedCb: (options: Record<string, any>, code: string) => Promise<string>, formatEmbeddedDocCb: (options: Record<string, any>, texts: string[]) => Promise<string[]>, formatFileCb: (options: Record<string, any>, code: string) => Promise<string>, sortTailwindClassesCb: (options: Record<string, any>, classes: string[]) => Promise<string[]>): Promise<FormatResult>
3838

3939
export interface FormatResult {
4040
/** The formatted code. */
@@ -49,7 +49,7 @@ export interface FormatResult {
4949
* This API is specialized for JS/TS snippets embedded in non-JS files.
5050
* Unlike `format()`, it is called only for js-in-xxx `textToDoc()` flow.
5151
*/
52-
export declare function jsTextToDoc(sourceExt: string, sourceText: string, oxfmtPluginOptionsJson: string, parentContext: string, initExternalFormatterCb: (numThreads: number) => Promise<string[]>, formatEmbeddedCb: (options: Record<string, any>, code: string) => Promise<string>, formatEmbeddedDocCb: (options: Record<string, any>, code: string) => Promise<string>, formatFileCb: (options: Record<string, any>, code: string) => Promise<string>, sortTailwindClassesCb: (options: Record<string, any>, classes: string[]) => Promise<string[]>): Promise<string | null>
52+
export declare function jsTextToDoc(sourceExt: string, sourceText: string, oxfmtPluginOptionsJson: string, parentContext: string, initExternalFormatterCb: (numThreads: number) => Promise<string[]>, formatEmbeddedCb: (options: Record<string, any>, code: string) => Promise<string>, formatEmbeddedDocCb: (options: Record<string, any>, texts: string[]) => Promise<string[]>, formatFileCb: (options: Record<string, any>, code: string) => Promise<string>, sortTailwindClassesCb: (options: Record<string, any>, classes: string[]) => Promise<string[]>): Promise<string | null>
5353

5454
/**
5555
* NAPI based JS CLI entry point.
@@ -66,4 +66,4 @@ export declare function jsTextToDoc(sourceExt: string, sourceText: string, oxfmt
6666
* - `mode`: If main logic will run in JS side, use this to indicate which mode
6767
* - `exitCode`: If main logic already ran in Rust side, return the exit code
6868
*/
69-
export declare function runCli(args: Array<string>, initExternalFormatterCb: (numThreads: number) => Promise<string[]>, formatEmbeddedCb: (options: Record<string, any>, code: string) => Promise<string>, formatEmbeddedDocCb: (options: Record<string, any>, code: string) => Promise<string>, formatFileCb: (options: Record<string, any>, code: string) => Promise<string>, sortTailwindcssClassesCb: (options: Record<string, any>, classes: string[]) => Promise<string[]>): Promise<[string, number | undefined | null]>
69+
export declare function runCli(args: Array<string>, initExternalFormatterCb: (numThreads: number) => Promise<string[]>, formatEmbeddedCb: (options: Record<string, any>, code: string) => Promise<string>, formatEmbeddedDocCb: (options: Record<string, any>, texts: string[]) => Promise<string[]>, formatFileCb: (options: Record<string, any>, code: string) => Promise<string>, sortTailwindcssClassesCb: (options: Record<string, any>, classes: string[]) => Promise<string[]>): Promise<[string, number | undefined | null]>

apps/oxfmt/src-js/cli-worker.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
// `oxfmt` CLI - Worker Thread Entry Point
22

33
// Re-exports core functions for use in `worker_threads`
4-
export { formatEmbeddedCode, formatEmbeddedDoc, formatFile, sortTailwindClasses } from "./libs/apis";
4+
export {
5+
formatEmbeddedCode,
6+
formatEmbeddedDoc,
7+
formatFile,
8+
sortTailwindClasses,
9+
} from "./libs/apis";

apps/oxfmt/src-js/cli/worker-proxy.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,13 @@ export async function formatEmbeddedCode(
4343

4444
export async function formatEmbeddedDoc(
4545
options: FormatEmbeddedDocParam["options"],
46-
code: string,
47-
): Promise<string> {
46+
texts: string[],
47+
): Promise<string[]> {
4848
return pool!
49-
.run({ options, code } satisfies FormatEmbeddedDocParam, {
49+
.run({ options, texts } satisfies FormatEmbeddedDocParam, {
5050
name: "formatEmbeddedDoc",
5151
})
52-
.catch((err) => {
53-
if (err instanceof Error) throw err;
54-
if (err !== null && typeof err === "object") {
55-
const obj = err as { name: string; message: string };
56-
const newErr = new Error(obj.message);
57-
newErr.name = obj.name;
58-
throw newErr;
59-
}
60-
throw new Error(String(err));
61-
});
52+
.catch(rethrowAsError);
6253
}
6354

6455
export async function formatFile(

apps/oxfmt/src-js/index.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { format as napiFormat, jsTextToDoc as napiJsTextToDoc } from "./bindings";
2-
import { resolvePlugins, formatEmbeddedCode, formatEmbeddedDoc, formatFile, sortTailwindClasses } from "./libs/apis";
2+
import {
3+
resolvePlugins,
4+
formatEmbeddedCode,
5+
formatEmbeddedDoc,
6+
formatFile,
7+
sortTailwindClasses,
8+
} from "./libs/apis";
39
import type { Options } from "prettier";
410

511
// napi-JS `oxfmt` API entry point
@@ -18,7 +24,7 @@ export async function format(fileName: string, sourceText: string, options?: For
1824
options ?? {},
1925
resolvePlugins,
2026
(options, code) => formatEmbeddedCode({ options, code }),
21-
(options, code) => formatEmbeddedDoc({ options, code }),
27+
(options, texts) => formatEmbeddedDoc({ options, texts }),
2228
(options, code) => formatFile({ options, code }),
2329
(options, classes) => sortTailwindClasses({ options, classes }),
2430
);
@@ -40,7 +46,7 @@ export async function jsTextToDoc(
4046
parentContext,
4147
resolvePlugins,
4248
(options, code) => formatEmbeddedCode({ options, code }),
43-
(options, code) => formatEmbeddedDoc({ options, code }),
49+
(options, texts) => formatEmbeddedDoc({ options, texts }),
4450
(_options, _code) => Promise.reject(/* Unreachable */),
4551
(options, classes) => sortTailwindClasses({ options, classes }),
4652
);

apps/oxfmt/src-js/libs/apis.ts

Lines changed: 40 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,12 @@ export type FormatEmbeddedCodeParam = {
5050
};
5151

5252
/**
53-
* Format xxx-in-js code snippets
53+
* Format xxx-in-js code snippets into formatted string.
54+
*
55+
* This will be gradually replaced by `formatEmbeddedDoc` which returns `Doc`.
56+
* For now, html|css|md-in-js are using this.
5457
*
5558
* @returns Formatted code snippet
56-
* TODO: In the future, this should return `Doc` instead of string,
57-
* otherwise, we cannot calculate `printWidth` correctly.
5859
*/
5960
export async function formatEmbeddedCode({
6061
code,
@@ -75,64 +76,55 @@ export async function formatEmbeddedCode({
7576
// ---
7677

7778
export type FormatEmbeddedDocParam = {
78-
code: string;
79+
texts: string[];
7980
options: Options;
8081
};
8182

8283
/**
83-
* Format xxx-in-js code snippets via Doc IR path.
84+
* Format xxx-in-js code snippets into Prettier `Doc` JSON strings.
8485
*
85-
* Uses `prettier.__debug.printToDoc()` to get the unresolved Doc,
86-
* then serializes it to JSON for the Rust side to parse.
86+
* This makes `oxc_formatter` correctly handle `printWidth` even for embedded code.
87+
* - For gql-in-js, `texts` contains multiple parts split by `${}` in a template literal
88+
* - For others, `texts` always contains a single string with `${}` parts replaced by placeholders
89+
* However, this function does not need to be aware of that,
90+
* as it simply formats each text part independently and returns an array of formatted parts.
8791
*
88-
* @returns Doc JSON string
92+
* @returns Doc JSON strings (one per input text)
8993
*/
9094
export async function formatEmbeddedDoc({
91-
code,
95+
texts,
9296
options,
93-
}: FormatEmbeddedDocParam): Promise<string> {
97+
}: FormatEmbeddedDocParam): Promise<string[]> {
9498
const prettier = await loadPrettier();
9599

96-
// Enable Tailwind CSS plugin for embedded code if needed
100+
// Enable Tailwind CSS plugin for embedded code (e.g., html`...` in JS) if needed
97101
await setupTailwindPlugin(options);
98102

99-
// Get unresolved Doc from Prettier
100-
const doc = await prettier.__debug.printToDoc(code, options);
101-
102-
// Replace Symbol group IDs with numeric counters before JSON serialization
103-
const symbolToNumber = new Map<symbol, number>();
104-
let nextId = 1;
105-
106-
const { utils } = await import("prettier/doc");
107-
utils.traverseDoc(doc, (docNode: any) => {
108-
// Replace group id (Symbol → number)
109-
if (docNode.type === "group" && typeof docNode.id === "symbol") {
110-
if (!symbolToNumber.has(docNode.id)) {
111-
symbolToNumber.set(docNode.id, nextId++);
112-
}
113-
docNode.id = symbolToNumber.get(docNode.id);
114-
}
115-
// Replace if-break groupId
116-
if (docNode.type === "if-break" && typeof docNode.groupId === "symbol") {
117-
if (!symbolToNumber.has(docNode.groupId)) {
118-
symbolToNumber.set(docNode.groupId, nextId++);
119-
}
120-
docNode.groupId = symbolToNumber.get(docNode.groupId);
121-
}
122-
// Replace indent-if-break groupId
123-
if (docNode.type === "indent-if-break" && typeof docNode.groupId === "symbol") {
124-
if (!symbolToNumber.has(docNode.groupId)) {
125-
symbolToNumber.set(docNode.groupId, nextId++);
126-
}
127-
docNode.groupId = symbolToNumber.get(docNode.groupId);
128-
}
129-
});
130-
131-
// Serialize with -Infinity replacer
132-
return JSON.stringify(doc, (_key, value) => {
133-
if (value === -Infinity) return "__NEGATIVE_INFINITY__";
134-
return value;
135-
});
103+
// NOTE: This will throw if:
104+
// - Specified parser is not available
105+
// - Or, code has syntax errors
106+
// In such cases, Rust side will fallback to original code
107+
return Promise.all(
108+
texts.map(async (text) => {
109+
// @ts-expect-error: Use internal API, but it's necessary and only way to get `Doc`
110+
const doc = await prettier.__debug.printToDoc(text, options);
111+
112+
// Serialize Doc to JSON, handling special values in a single pass:
113+
// - Symbol group IDs (used by `group`, `if-break`, `indent-if-break`) → numeric counters
114+
// - -Infinity (used by `dedentToRoot` via `align`) → marker string
115+
const symbolToNumber = new Map<symbol, number>();
116+
let nextId = 1;
117+
118+
return JSON.stringify(doc, (_key, value) => {
119+
if (typeof value === "symbol") {
120+
if (!symbolToNumber.has(value)) symbolToNumber.set(value, nextId++);
121+
return symbolToNumber.get(value);
122+
}
123+
if (value === -Infinity) return "__NEGATIVE_INFINITY__";
124+
return value;
125+
});
126+
}),
127+
);
136128
}
137129

138130
// ---

apps/oxfmt/src/api/format_api.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ use serde_json::Value;
55
use oxc_napi::OxcError;
66

77
use crate::core::{
8-
ExternalFormatter, FormatFileStrategy, FormatResult, JsFormatEmbeddedCb,
9-
JsFormatEmbeddedDocCb, JsFormatFileCb, JsInitExternalFormatterCb, JsSortTailwindClassesCb,
10-
SourceFormatter, resolve_options_from_value,
8+
ExternalFormatter, FormatFileStrategy, FormatResult, JsFormatEmbeddedCb, JsFormatEmbeddedDocCb,
9+
JsFormatFileCb, JsInitExternalFormatterCb, JsSortTailwindClassesCb, SourceFormatter,
10+
resolve_options_from_value,
1111
};
1212

1313
pub struct ApiFormatResult {

apps/oxfmt/src/api/text_to_doc_api.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use oxc_span::SourceType;
1515
use crate::{
1616
core::{
1717
ExternalFormatter, FormatFileStrategy, FormatResult, JsFormatEmbeddedCb,
18-
JsFormatEmbeddedDocCb, JsFormatFileCb, JsInitExternalFormatterCb,
19-
JsSortTailwindClassesCb, ResolvedOptions, SourceFormatter, resolve_options_from_value,
18+
JsFormatEmbeddedDocCb, JsFormatFileCb, JsInitExternalFormatterCb, JsSortTailwindClassesCb,
19+
ResolvedOptions, SourceFormatter, resolve_options_from_value,
2020
},
2121
prettier_compat::to_prettier_doc,
2222
};

0 commit comments

Comments
 (0)