Skip to content

Commit 201ae20

Browse files
authored
Merge pull request #33 from ony3000/fix-markdown-consistency
Leaves output of each plugin as is for code blocks embedded in markdown and mdx
2 parents 60ae5a3 + 837273d commit 201ae20

File tree

15 files changed

+363
-32
lines changed

15 files changed

+363
-32
lines changed

pnpm-lock.yaml

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/packages/v2-plugin/parsers.ts

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,33 @@ import { parsers as babelParsers } from 'prettier/parser-babel';
66
import { parsers as htmlParsers } from 'prettier/parser-html';
77
import { parsers as typescriptParsers } from 'prettier/parser-typescript';
88

9+
const EOL = '\n';
10+
11+
function formatAsCodeblock(text: string, options: ParserOptions, plugins?: Plugin[]) {
12+
let codeblockStart = '```';
13+
const codeblockEnd = '```';
14+
15+
if (options.parser === 'babel') {
16+
codeblockStart = '```jsx';
17+
} else if (options.parser === 'typescript') {
18+
codeblockStart = '```tsx';
19+
}
20+
21+
const formattedCodeblock = format(`${codeblockStart}${EOL}${text}${EOL}${codeblockEnd}`, {
22+
...options,
23+
plugins: plugins ?? [],
24+
rangeEnd: Infinity,
25+
endOfLine: 'lf',
26+
parser: options.parentParser,
27+
parentParser: undefined,
28+
});
29+
const formattedText = formattedCodeblock
30+
.trim()
31+
.slice(`${codeblockStart}${EOL}`.length, -`${EOL}${codeblockEnd}`.length);
32+
33+
return formattedText;
34+
}
35+
936
function sequentialFormattingAndTryMerging(
1037
options: ParserOptions,
1138
plugins: Plugin[],
@@ -23,23 +50,32 @@ function sequentialFormattingAndTryMerging(
2350
plugins: customLanguageSupportedPlugins,
2451
};
2552

26-
const firstFormattedText = format(originalText, sequentialFormattingOptions);
53+
const firstFormattedText =
54+
options.parentParser === 'markdown' || options.parentParser === 'mdx'
55+
? formatAsCodeblock(originalText, options)
56+
: format(originalText, sequentialFormattingOptions);
2757

2858
/**
2959
* Changes that may be removed during the sequential formatting process.
3060
*/
3161
const patches: SubstitutePatch[] = [];
3262

3363
const sequentiallyMergedText = plugins.reduce((formattedPrevText, plugin) => {
34-
const temporaryFormattedText = format(formattedPrevText, {
35-
...sequentialFormattingOptions,
36-
plugins: [...customLanguageSupportedPlugins, plugin],
37-
});
38-
39-
const temporaryFormattedTextWithoutPlugin = format(
40-
temporaryFormattedText,
41-
sequentialFormattingOptions,
42-
);
64+
const temporaryFormattedText =
65+
options.parentParser === 'markdown' || options.parentParser === 'mdx'
66+
? formatAsCodeblock(formattedPrevText, sequentialFormattingOptions, [
67+
...customLanguageSupportedPlugins,
68+
plugin,
69+
])
70+
: format(formattedPrevText, {
71+
...sequentialFormattingOptions,
72+
plugins: [...customLanguageSupportedPlugins, plugin],
73+
});
74+
75+
const temporaryFormattedTextWithoutPlugin =
76+
options.parentParser === 'markdown' || options.parentParser === 'mdx'
77+
? formatAsCodeblock(temporaryFormattedText, sequentialFormattingOptions)
78+
: format(temporaryFormattedText, sequentialFormattingOptions);
4379

4480
patches.push(...makePatches(temporaryFormattedTextWithoutPlugin, temporaryFormattedText));
4581

src/packages/v3-plugin/parsers.ts

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,33 @@ import { parsers as babelParsers } from 'prettier/plugins/babel';
66
import { parsers as htmlParsers } from 'prettier/plugins/html';
77
import { parsers as typescriptParsers } from 'prettier/plugins/typescript';
88

9+
const EOL = '\n';
10+
11+
async function formatAsCodeblock(text: string, options: ParserOptions, plugins?: Plugin[]) {
12+
let codeblockStart = '```';
13+
const codeblockEnd = '```';
14+
15+
if (options.parser === 'babel') {
16+
codeblockStart = '```jsx';
17+
} else if (options.parser === 'typescript') {
18+
codeblockStart = '```tsx';
19+
}
20+
21+
const formattedCodeblock = await format(`${codeblockStart}${EOL}${text}${EOL}${codeblockEnd}`, {
22+
...options,
23+
plugins: plugins ?? [],
24+
rangeEnd: Infinity,
25+
endOfLine: 'lf',
26+
parser: options.parentParser,
27+
parentParser: undefined,
28+
});
29+
const formattedText = formattedCodeblock
30+
.trim()
31+
.slice(`${codeblockStart}${EOL}`.length, -`${EOL}${codeblockEnd}`.length);
32+
33+
return formattedText;
34+
}
35+
936
async function sequentialFormattingAndTryMerging(
1037
options: ParserOptions,
1138
plugins: Plugin[],
@@ -23,7 +50,10 @@ async function sequentialFormattingAndTryMerging(
2350
plugins: customLanguageSupportedPlugins,
2451
};
2552

26-
const firstFormattedTextPromise = format(originalText, sequentialFormattingOptions);
53+
const firstFormattedTextPromise =
54+
options.parentParser === 'markdown' || options.parentParser === 'mdx'
55+
? formatAsCodeblock(originalText, options)
56+
: format(originalText, sequentialFormattingOptions);
2757

2858
/**
2959
* Changes that may be removed during the sequential formatting process.
@@ -33,15 +63,22 @@ async function sequentialFormattingAndTryMerging(
3363
const sequentiallyMergedText = await plugins.reduce<Promise<string>>(
3464
async (formattedPrevTextPromise, plugin) => {
3565
const formattedPrevText = await formattedPrevTextPromise;
36-
const temporaryFormattedText = await format(formattedPrevText, {
37-
...sequentialFormattingOptions,
38-
plugins: [...customLanguageSupportedPlugins, plugin],
39-
});
40-
41-
const temporaryFormattedTextWithoutPlugin = await format(
42-
temporaryFormattedText,
43-
sequentialFormattingOptions,
44-
);
66+
67+
const temporaryFormattedText =
68+
options.parentParser === 'markdown' || options.parentParser === 'mdx'
69+
? await formatAsCodeblock(formattedPrevText, sequentialFormattingOptions, [
70+
...customLanguageSupportedPlugins,
71+
plugin,
72+
])
73+
: await format(formattedPrevText, {
74+
...sequentialFormattingOptions,
75+
plugins: [...customLanguageSupportedPlugins, plugin],
76+
});
77+
78+
const temporaryFormattedTextWithoutPlugin =
79+
options.parentParser === 'markdown' || options.parentParser === 'mdx'
80+
? await formatAsCodeblock(temporaryFormattedText, sequentialFormattingOptions)
81+
: await format(temporaryFormattedText, sequentialFormattingOptions);
4582

4683
patches.push(...makePatches(temporaryFormattedTextWithoutPlugin, temporaryFormattedText));
4784

tests/v2-test/adaptor.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { parsers as babelParsers } from 'prettier/parser-babel';
44
import { parsers as htmlParsers } from 'prettier/parser-html';
55
import { parsers as typescriptParsers } from 'prettier/parser-typescript';
66
import type { Fixture } from 'test-settings';
7-
import { expect, test } from 'vitest';
7+
import { describe, expect, onTestFailed, test } from 'vitest';
88

99
// eslint-disable-next-line import/no-extraneous-dependencies
1010
import * as thisPlugin from '@/packages/v2-plugin';
@@ -46,6 +46,45 @@ export function testEach(fixtures: Fixture[], options: PrettierBaseOptions & { p
4646
});
4747
}
4848

49+
export function testSnapshotEach(
50+
fixtures: Omit<Fixture, 'output'>[],
51+
options: PrettierBaseOptions & { parser: string },
52+
) {
53+
describe.each(fixtures)('$name', ({ input, options: fixtureOptions }) => {
54+
const fixedOptions = {
55+
...options,
56+
...(fixtureOptions ?? {}),
57+
};
58+
const formattedText = format(input, fixedOptions);
59+
let skipSecondTest = false;
60+
61+
test('expectation', () => {
62+
onTestFailed(() => {
63+
skipSecondTest = true;
64+
});
65+
66+
expect(formattedText).toMatchSnapshot();
67+
});
68+
69+
test('consistency; if there are no plugins or only one, adding a merge plugin should have the same result', ({
70+
skip,
71+
}) => {
72+
const fixedPlugins = fixedOptions.plugins ?? [];
73+
74+
if (skipSecondTest || fixedPlugins.length > 1) {
75+
skip();
76+
}
77+
78+
const formattedTextWithThisPlugin = format(formattedText, {
79+
...fixedOptions,
80+
plugins: [...fixedPlugins, thisPlugin],
81+
});
82+
83+
expect(formattedTextWithThisPlugin).toBe(formattedText);
84+
});
85+
});
86+
}
87+
4988
export function testErrorEach(
5089
fixtures: Fixture[],
5190
options: PrettierBaseOptions & { parser: string },
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`'(1) This plugin doesn\\'t support markdown parser, so it leaves the output of each plugin as is.' > expectation 1`] = `
4+
"\`\`\`jsx
5+
export function Foo({ children }) {
6+
return (
7+
<div className="lorem ipsum dolor sit amet consectetur adipiscing elit proin ex massa hendrerit eu posuere eu volutpat id neque pellentesque">
8+
{children}
9+
</div>
10+
);
11+
}
12+
\`\`\`
13+
"
14+
`;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { Fixture } from 'test-settings';
2+
import { baseOptions } from 'test-settings';
3+
4+
import { testSnapshotEach } from '../adaptor';
5+
6+
const options = {
7+
...baseOptions,
8+
parser: 'markdown',
9+
};
10+
11+
const fixtures: Omit<Fixture, 'output'>[] = [
12+
{
13+
name: "(1) This plugin doesn't support markdown parser, so it leaves the output of each plugin as is.",
14+
input: `
15+
\`\`\`jsx
16+
export function Foo({ children }) {
17+
return (
18+
<div className="lorem ipsum dolor sit amet consectetur adipiscing elit proin ex massa hendrerit eu posuere eu volutpat id neque pellentesque">
19+
{children}
20+
</div>
21+
);
22+
}
23+
\`\`\`
24+
`,
25+
options: {
26+
plugins: ['prettier-plugin-classnames'],
27+
},
28+
},
29+
];
30+
31+
testSnapshotEach(fixtures, options);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`'(1) This plugin doesn\\'t support mdx parser, so it leaves the output of each plugin as is.' > expectation 1`] = `
4+
"\`\`\`jsx
5+
export function Foo({ children }) {
6+
return (
7+
<div className="lorem ipsum dolor sit amet consectetur adipiscing elit proin ex massa hendrerit eu posuere eu volutpat id neque pellentesque">
8+
{children}
9+
</div>
10+
);
11+
}
12+
\`\`\`
13+
"
14+
`;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { Fixture } from 'test-settings';
2+
import { baseOptions } from 'test-settings';
3+
4+
import { testSnapshotEach } from '../adaptor';
5+
6+
const options = {
7+
...baseOptions,
8+
parser: 'mdx',
9+
};
10+
11+
const fixtures: Omit<Fixture, 'output'>[] = [
12+
{
13+
name: "(1) This plugin doesn't support mdx parser, so it leaves the output of each plugin as is.",
14+
input: `
15+
\`\`\`jsx
16+
export function Foo({ children }) {
17+
return (
18+
<div className="lorem ipsum dolor sit amet consectetur adipiscing elit proin ex massa hendrerit eu posuere eu volutpat id neque pellentesque">
19+
{children}
20+
</div>
21+
);
22+
}
23+
\`\`\`
24+
`,
25+
options: {
26+
plugins: ['prettier-plugin-classnames'],
27+
},
28+
},
29+
];
30+
31+
testSnapshotEach(fixtures, options);

tests/v2-test/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"prettier": "2.8.4",
1313
"prettier-plugin-astro": "0.10.0",
1414
"prettier-plugin-brace-style": "0.7.0",
15-
"prettier-plugin-classnames": "0.7.0",
15+
"prettier-plugin-classnames": "0.7.5",
1616
"prettier-plugin-svelte": "2.10.1",
1717
"prettier-plugin-tailwindcss": "0.4.1",
1818
"test-settings": "workspace:*",

0 commit comments

Comments
 (0)