Skip to content

Commit c0d77bf

Browse files
committed
fix: prevent preprocess again
#254
1 parent ebe15f3 commit c0d77bf

4 files changed

Lines changed: 199 additions & 15 deletions

File tree

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,6 @@ dist/
103103
# TernJS port file
104104
.tern-port
105105

106-
.DS_STORE
106+
.DS_STORE
107+
108+
.test-cli-temp

src/index.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -256,27 +256,32 @@ const parsers = {
256256

257257
function mergeParsers(originalParser: prettier.Parser, parserName: string) {
258258
const jsDocParse = getParser(originalParser.parse, parserName) as any;
259+
let hasPreprocessed = false;
259260

260261
const jsDocPreprocess = (text: string, options: prettier.ParserOptions) => {
261262
normalizeOptions(options as any);
262-
const tsPluginParser = findPluginByParser(parserName, options);
263263

264-
if (!tsPluginParser) {
265-
return originalParser.preprocess
266-
? originalParser.preprocess(text, options)
267-
: text;
264+
// Prevent infinite recursion by checking if we've already preprocessed
265+
if (hasPreprocessed) {
266+
return text;
268267
}
269268

270-
const preprocess = tsPluginParser.preprocess || originalParser.preprocess;
269+
hasPreprocessed = true;
270+
try {
271+
const tsPluginParser = findPluginByParser(parserName, options);
271272

272-
Object.assign(parser, {
273-
...parser,
274-
...tsPluginParser,
275-
preprocess: jsDocPreprocess,
276-
parse: jsDocParse,
277-
});
273+
if (!tsPluginParser) {
274+
return originalParser.preprocess
275+
? originalParser.preprocess(text, options)
276+
: text;
277+
}
278278

279-
return preprocess ? preprocess(text, options) : text;
279+
const preprocess =
280+
tsPluginParser?.preprocess || originalParser.preprocess;
281+
return preprocess ? preprocess(text, options) : text;
282+
} finally {
283+
hasPreprocessed = false;
284+
}
280285
};
281286

282287
const parser = {

tests/__snapshots__/compatibleWithPlugins.test.ts.snap

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,48 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`CLI Compatibility Should format via CLI with --write flag 1`] = `
4+
"/**
5+
* @param {number} [arg1=123] The width
6+
*
7+
* Default is \`123\`
8+
* @returns {void}
9+
*/
10+
function myFunc(arg1) {}
11+
"
12+
`;
13+
14+
exports[`CLI Compatibility Should format with tailwindcss plugin via CLI without infinite recursion 1`] = `
15+
"/**
16+
* @param {String | Number} text - Some text description
17+
* @param {String} [defaultValue="defaultTest"] TODO. Default is \`"defaultTest"\`
18+
* @returns {Boolean} Description for returns
19+
*/
20+
const testFunction = (text, defaultValue) => true
21+
"
22+
`;
23+
24+
exports[`CLI Compatibility Should work with plugins in different orders via CLI 1`] = `
25+
"/**
26+
* @param {string} name
27+
28+
29+
30+
31+
* @returns {Promise<void>}
32+
*/
33+
async function example(name: string): Promise<void> {}
34+
"
35+
`;
36+
37+
exports[`CLI Compatibility Should work with plugins in different orders via CLI 2`] = `
38+
"/**
39+
* @param {string} name
40+
* @returns {Promise<void>}
41+
*/
42+
async function example(name: string): Promise<void> {}
43+
"
44+
`;
45+
346
exports[`Should compatible with tailwindcss 1`] = `
447
"/**
548
* @param {String} [arg1="defaultTest"] Foo. Default is \`"defaultTest"\`

tests/compatibleWithPlugins.test.ts

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import * as prettier from "prettier";
22
import { AllOptions } from "../src/types";
3+
import { execSync } from "child_process";
4+
import { mkdirSync, writeFileSync, readFileSync, rmSync } from "fs";
5+
import { join } from "path";
36

47
function subject(code: string, options: Partial<AllOptions> = {}) {
58
return prettier.format(code, {
@@ -90,7 +93,7 @@ test("Should convert to single line if necessary", async () => {
9093
test("Should compatible with tailwindcss", async () => {
9194
const code = `
9295
/**
93-
* @param {String} [arg1="defaultTest"] foo
96+
* @param {String} [arg1="defaultTest"] foo
9497
* @param {number} [arg2=123] the width of the rectangle
9598
* @param {number} [arg3= 123 ]
9699
* @param {number} [arg4= Foo.bar.baz ]
@@ -101,3 +104,134 @@ test("Should compatible with tailwindcss", async () => {
101104

102105
expect(result).toMatchSnapshot();
103106
});
107+
108+
describe("CLI Compatibility", () => {
109+
// CLI-based tests
110+
const testDir = join(process.cwd(), ".test-cli-temp");
111+
112+
beforeAll(() => {
113+
try {
114+
mkdirSync(testDir, { recursive: true });
115+
} catch (err) {
116+
// Directory might already exist
117+
}
118+
});
119+
120+
afterAll(() => {
121+
try {
122+
rmSync(testDir, { recursive: true, force: true });
123+
} catch (err) {
124+
// Ignore cleanup errors
125+
}
126+
});
127+
128+
function runCommand(command: string): string {
129+
return execSync(`cd ${testDir} && ${command}`, {
130+
timeout: 10000,
131+
encoding: "utf8",
132+
cwd: process.cwd(),
133+
});
134+
}
135+
136+
test("Should format with tailwindcss plugin via CLI without infinite recursion", () => {
137+
const testFile = join(testDir, "test-cli-tailwind.js");
138+
const configFile = join(testDir, ".prettierrc.json");
139+
140+
const code = `/**
141+
* @param {String|Number} text - some text description
142+
143+
144+
145+
* @param {String} [defaultValue="defaultTest"] TODO
146+
* @returns {Boolean} Description for returns
147+
*/
148+
const testFunction = (text, defaultValue) => true;
149+
`;
150+
151+
const prettierConfig = {
152+
semi: false,
153+
tabWidth: 2,
154+
printWidth: 100,
155+
singleQuote: true,
156+
plugins: ["prettier-plugin-tailwindcss", "prettier-plugin-jsdoc"],
157+
};
158+
159+
writeFileSync(testFile, code);
160+
writeFileSync(configFile, JSON.stringify(prettierConfig, null, 2));
161+
162+
const output = runCommand(
163+
`npx prettier --config ".prettierrc.json" "test-cli-tailwind.js"`,
164+
);
165+
166+
expect(output).toBeDefined();
167+
expect(output).toMatchSnapshot();
168+
});
169+
170+
test("Should format via CLI with --write flag", () => {
171+
const testFile = join(testDir, "test-cli-write.js");
172+
const configFile = join(testDir, ".prettierrc.json");
173+
174+
const code = `/**
175+
* @param {number} [arg1=123] the width
176+
177+
178+
179+
* @returns {void}
180+
*/
181+
function myFunc(arg1) {}
182+
`;
183+
184+
const prettierConfig = {
185+
semi: false,
186+
plugins: ["prettier-plugin-tailwindcss", "prettier-plugin-jsdoc"],
187+
};
188+
189+
writeFileSync(testFile, code);
190+
writeFileSync(configFile, JSON.stringify(prettierConfig, null, 2));
191+
192+
runCommand(
193+
`npx prettier --config ".prettierrc.json" --write "test-cli-write.js"`,
194+
);
195+
196+
const formatted = readFileSync(testFile, "utf8");
197+
expect(formatted).toMatchSnapshot();
198+
});
199+
200+
test("Should work with plugins in different orders via CLI", () => {
201+
const testFile = join(testDir, "test-cli-order.ts");
202+
const configFile = join(testDir, ".prettierrc.json");
203+
204+
const code = `/**
205+
* @param {string} name
206+
207+
208+
209+
210+
* @returns {Promise<void>}
211+
*/
212+
async function example(name: string): Promise<void> {}
213+
`;
214+
215+
const configs = [
216+
["prettier-plugin-jsdoc", "prettier-plugin-tailwindcss"],
217+
["prettier-plugin-tailwindcss", "prettier-plugin-jsdoc"],
218+
];
219+
220+
configs.forEach((plugins) => {
221+
const prettierConfig = {
222+
parser: "typescript",
223+
plugins,
224+
};
225+
226+
writeFileSync(testFile, code);
227+
writeFileSync(configFile, JSON.stringify(prettierConfig, null, 2));
228+
229+
const output = runCommand(
230+
`npx prettier --config ".prettierrc.json" "test-cli-order.ts"`,
231+
);
232+
233+
expect(output).toBeDefined();
234+
expect(output).toMatchSnapshot();
235+
});
236+
});
237+
});

0 commit comments

Comments
 (0)