Skip to content

Commit c4434ec

Browse files
authored
Add prettier plugin to partially format MDX3 (#12796)
1 parent 6c75ca8 commit c4434ec

File tree

4 files changed

+74
-83
lines changed

4 files changed

+74
-83
lines changed

.prettierrc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,22 @@
66
"tabWidth": 2,
77
"trailingComma": "es5",
88
"experimentalTernaries": true,
9-
"plugins": ["./config/prettier/format-jsdoc.js"],
9+
"plugins": [
10+
"./config/prettier/format-jsdoc.js",
11+
"./config/prettier/format-mdx3.js"
12+
],
1013
"overrides": [
1114
{
1215
"files": ["*.ts", "*.tsx"],
1316
"options": {
1417
"parser": "typescript-with-jsdoc"
1518
}
19+
},
20+
{
21+
"files": ["*.mdx"],
22+
"options": {
23+
"parser": "mdx3"
24+
}
1625
}
1726
]
1827
}

config/prettier/format-mdx3.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/** @import { Plugin } from 'prettier' */
2+
3+
import markdown from "prettier/plugins/markdown.js";
4+
5+
/** @type {Plugin["languages"]} */
6+
export const languages = [
7+
{
8+
name: "MDX",
9+
parsers: ["mdx3"],
10+
extensions: [".mdx"],
11+
},
12+
];
13+
/** @type {Plugin["parsers"]} */
14+
export const parsers = {
15+
mdx3: {
16+
...markdown.parsers.mdx,
17+
astFormat: "mdx3",
18+
},
19+
};
20+
/** @type {Plugin["printers"]} */
21+
export const printers = {
22+
mdx3: {
23+
...markdown.printers.mdast,
24+
embed(path, options) {
25+
const node = path.node;
26+
if (node.type === "jsx") {
27+
// If the node was parsed incorrectly because it followed the MDX3 format (no spacing around JSX tags),
28+
// we will not try to format it as MDX, but instead return the original value.
29+
30+
// We detect that by looking at `value` - if it's only a starting or closing tag, it was parsed correctly.
31+
const correctlyParsedMatch = node.value.match(/^(<\/?[^>]*>)$/);
32+
if (!correctlyParsedMatch)
33+
return (
34+
node.value
35+
.split("\n")
36+
// But we need to restore the original indentation
37+
.map((line, idx) =>
38+
idx === 0 ? line : (
39+
" ".repeat(node.position.indent[idx - 1] - 1) + line
40+
)
41+
)
42+
.join("\n")
43+
);
44+
}
45+
return markdown.printers.mdast.embed(path, options);
46+
},
47+
},
48+
};

config/prettier/test.ts

Lines changed: 15 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,27 @@
11
import * as prettier from "prettier";
22
const code = `
3+
Notice that the \`Trail\` component isn't receiving the entire \`trail\` object via props, only the \`id\` which is used along with the fragment document to create a live binding for each trail item in the cache. This allows each \`Trail\` component to react to the cache updates for a single trail independently. Updates to a trail's \`status\` will not cause the parent \`App\` component to rerender since the \`@nonreactive\` directive is applied to the \`TrailFragment\` spread, a fragment that includes the \`status\` field.
34
5+
<MinVersion version="3.12.0">
6+
## \`@unmask\`
7+
</MinVersion>
48
5-
/**
6-
* A hook for executing queries in an Apollo application.
7-
*
8-
* To run a query within a React component, call \`useQuery\` and pass it a GraphQL query document.
9-
*
10-
* When your component renders, \`useQuery\` returns an object from Apollo Client that contains
11-
*
12-
* > Refer to the [Queries](https://www.apollographql.com/docs/react/data/queries) section for a more in-depth overview of \`useQuery\`.
13-
*
14-
* @example
15-
* \`\`\`jsx
16-
* import { gql } from '@apollo/client';
17-
* import { useQuery } from '@apollo/client/react';
18-
*
19-
* const GET_GREETING = gql\`
20-
* query GetGreeting($language: String!) {
21-
* greeting(language: $language) {
22-
* message
23-
* }
24-
* }
25-
* \`;
26-
*
27-
* function Hello() {
28-
* const { loading, error, data } = useQuery(GET_GREETING, {
29-
* variables: { language: 'english' },
30-
* });
31-
* if (loading) return <p>Loading ...</p>;
32-
* return <h1>Hello {data.greeting.message}!</h1>;
33-
* }
34-
* \`\`\`
35-
* @param query - A GraphQL query document parsed into an AST by \`gql\`.
36-
* @param options - Options to control how the query is executed.
37-
* @returns Query result object
38-
*/
39-
export function useQuery<
40-
TData = unknown,
41-
TVariables extends OperationVariables = OperationVariables,
42-
>(
43-
query: DocumentNode | TypedDocumentNode<TData, TVariables>,
44-
options: useQuery.Options<NoInfer<TData>, NoInfer<TVariables>> & {
45-
returnPartialData: true;
46-
}
47-
): useQuery.Result<
48-
TData,
49-
TVariables,
50-
"empty" | "complete" | "streaming" | "partial"
51-
>;
9+
The \`@unmask\` directive is used to make fragment data available when using [data masking](./fragments#data-masking). It is primarily used to [incrementally adopt data masking in an existing application](./fragments#incremental-adoption-in-an-existing-application). It is considered an escape hatch for all other cases where working with masked data would otherwise be difficult.
5210
53-
/** {@inheritDoc @apollo/client!useQuery:function(1)} */
54-
export function useQuery<
55-
TData = unknown,
56-
TVariables extends OperationVariables = OperationVariables,
57-
>(
58-
query: DocumentNode | TypedDocumentNode<TData, TVariables>,
59-
options: useQuery.Options<NoInfer<TData>, NoInfer<TVariables>> & {
60-
returnPartialData: boolean;
11+
\`\`\`graphql
12+
query GetPosts {
13+
posts {
14+
id
15+
...PostDetails @unmask
6116
}
62-
): useQuery.Result<
63-
TData,
64-
TVariables,
65-
"empty" | "complete" | "streaming" | "partial"
66-
>;
67-
68-
/** {@inheritDoc @apollo/client!useQuery:function(1)} */
69-
export function useQuery<
70-
TData = unknown,
71-
TVariables extends OperationVariables = OperationVariables,
72-
>(
73-
query: DocumentNode | TypedDocumentNode<TData, TVariables>,
74-
...[options]: {} extends TVariables ?
75-
[options?: useQuery.Options<NoInfer<TData>, NoInfer<TVariables>>]
76-
: [options: useQuery.Options<NoInfer<TData>, NoInfer<TVariables>>]
77-
): useQuery.Result<TData, TVariables, "empty" | "complete" | "streaming">;
17+
}
18+
\`\`\`
7819
79-
`
80-
.split("\n")
81-
.map((line) => line.trim())
82-
.join("\n");
83-
await prettier.format(``, {
84-
parser: "jsdoc",
85-
plugins: ["./format-jsdoc.js"],
86-
});
20+
`;
8721

8822
const result = await prettier.format(code, {
89-
parser: "typescript-with-jsdoc",
90-
plugins: ["./format-jsdoc.js"],
23+
parser: "mdx3",
24+
plugins: ["./format-mdx3.js"],
9125
});
9226

9327
console.log(result);

docs/source/data/queries.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ You call `refetch` with a new set of variables like so:
181181
}
182182
>
183183
Refetch!
184-
</button>
184+
</button>;
185185
```
186186

187187
If you provide new values for _some_ of your original query's variables but not _all_ of them, `refetch` uses each omitted variable's original value.

0 commit comments

Comments
 (0)