-
Hello, TLDR: I can't figure out how to disable a custom Remark Lint rule in MDX 3 files. I tried I am writing a custom Remark Lint rule that's fairly simple. We have a large Docusaurus site that currently uses MDX 1. We are upgrading to MDX 3, which parses string children of elements in MDX files as markdown if they are on a new line. <SomeComponent>
I will be turned into an HTML paragraph element.
</SomeComponent> We do not want children of elements or components to be parsed as Markdown in 99% of cases, so the lint rule wraps JSX elements in curly braces to prevent their children from being parsed as markdown. {
<SomeComponent>
I will NOT be turned into an HTML paragraph element.
</SomeComponent>
} However, there are a few cases where we want the children to be parsed as markdown, so we need to be able to disable the rule. I tried using JS comments to disable it, but it doesn't work. The element still gets wrapped in curly braces. {/* lint disable no-markdown-in-jsx */}
<SomeComponent>
SomeComponent will get wrapped in curly braces despite the ignore comment above.
</SomeComponent>
{/* lint enable no-markdown-in-jsx */} I export it like so: const noMarkdownInJsx = lintRule('remark-lint:no-markdown-in-jsx', rule);
export default noMarkdownInJsx; Then I import it in my import noMarkdownInJsx from './remark-lint-rules/noMarkdownInJsx.mjs';
const remarkConfig = {
settings: {
// settings omitted
},
plugins: [
// other rules omitted
noMarkdownInJsx,
],
};
export default remarkConfig; Perhaps the issue is that my rule is modifying the markdown AST no matter what ( edit for more info: import { unified } from 'unified';
import { lintRule } from 'unified-lint-rule';
import markdown from 'remark-parse';
import mdx from 'remark-mdx';
import frontmatter from 'remark-frontmatter';
import stringify from 'remark-stringify';
import { u } from 'unist-builder';
import { visit, SKIP } from 'unist-util-visit';
export const processor = unified()
.use(frontmatter, ['yaml'])
.use(markdown)
.use(mdx)
.use(stringify, { rule: '-', ruleRepetition: 3 });
/**
* @typedef {import('unified-lint-rule').Rule} Rule
* @type Rule
*/
export const rule = (tree, file) => {
const JSX_ELEMENT = 'mdxJsxFlowElement';
visit(tree, JSX_ELEMENT, (node, index, parent) => {
const isParentTheRoot = parent === tree;
const hasChildren = node.children.length > 0;
// we only need to wrap JSX elements at the top level of the document
if (isParentTheRoot && hasChildren) {
file.message(
'JSX elements should be wrapped in curly braces, unless you want to allow their descendants to be processed as markdown.',
node
);
const nodeValueAsString = processor.stringify(node);
const nodeWrappedInCurlyBraces = u(
'mdxFlowExpression',
'\n' + nodeValueAsString
);
parent.children.splice(index, 1, nodeWrappedInCurlyBraces);
return [SKIP, index];
}
});
};
const noMarkdownInJsx = lintRule('remark-lint:no-markdown-in-jsx', rule);
export default noMarkdownInJsx; I've since updated it to manually check for comments that disable and enable the rule: import { unified } from 'unified';
import { lintRule } from 'unified-lint-rule';
import markdown from 'remark-parse';
import mdx from 'remark-mdx';
import frontmatter from 'remark-frontmatter';
import stringify from 'remark-stringify';
import { u } from 'unist-builder';
import { visit, SKIP } from 'unist-util-visit';
const processor = unified()
.use(frontmatter, ['yaml'])
.use(markdown)
.use(mdx)
.use(stringify, { rule: '-', ruleRepetition: 3 });
/**
* Text children of JSX elements are parsed as markdown by default.
* In most cases, we do not want this. For example, it may insert paragraphs
* inside of our components. This rule wraps JSX elements in curly braces
* to prevent this behavior.
*
* To disable this rule for a specific JSX element, you can add a JS comment with
* the text "lint disable no-markdown-in-jsx" before the element.
*
* Use a JS comment containing "lint enable no-markdown-in-jsx" to re-enable the rule.
*/
const noMarkdownInJsx = lintRule('dp-docs:no-markdown-in-jsx', (tree) => {
const JSX_ELEMENT = 'mdxJsxFlowElement';
const FLOW_EXPRESSION = 'mdxFlowExpression';
let isRuleEnabled = true;
visit(tree, (node, index, parent) => {
if (node.type === FLOW_EXPRESSION) {
const isComment =
node.value.startsWith('/*') && node.value.endsWith('*/');
if (isComment) {
const isLintDisableComment = node.value.includes('lint disable');
const isLintEnableComment = node.value.includes('lint enable');
const isTargetingThisRule = node.value.includes('no-markdown-in-jsx');
if (isTargetingThisRule) {
if (isLintDisableComment) {
isRuleEnabled = false;
}
if (isLintEnableComment) {
isRuleEnabled = true;
}
}
}
} else if (isRuleEnabled && node.type === JSX_ELEMENT) {
const isParentTheRoot = parent === tree;
const hasChildren = node.children.length > 0;
// we only need to wrap JSX elements at the top level of the document
if (isParentTheRoot && hasChildren) {
const nodeValueAsString = processor.stringify(node);
const nodeWrappedInCurlyBraces = u(
FLOW_EXPRESSION,
'\n' + nodeValueAsString
);
// replace the JSX element with a JSX element wrapped in curly braces
parent.children.splice(index, 1, nodeWrappedInCurlyBraces);
// this node has been processed, don't visit it again
return [SKIP, index];
}
}
});
});
export default noMarkdownInJsx; My remark configuration is as follows: import remarkFrontmatter from 'remark-frontmatter';
import remarkLintFileExtension from 'remark-lint-file-extension';
import remarkLintMaximumLineLength from 'remark-lint-maximum-line-length';
import remarkMdx from 'remark-mdx';
import remarkPresetLintConsistent from 'remark-preset-lint-consistent';
import remarkPresetLintMarkdownStyleGuide from 'remark-preset-lint-markdown-style-guide';
import remarkPresetLintRecommended from 'remark-preset-lint-recommended';
import noMarkdownInJsx from './remark-lint-rules/noMarkdownInJsx.mjs';
/**
* @typedef {import('unified').Preset} Preset
* @type Preset
*/
const remarkConfig = {
settings: {
bullet: '-',
emphasis: '_',
extensions: ['md', 'mdx'],
rule: '-', // Use for horizontal rules
ruleRepetition: 3,
// See <https://github.com/remarkjs/remark/tree/main/packages/remark-stringify> for more options.
},
plugins: [
remarkPresetLintMarkdownStyleGuide,
remarkPresetLintConsistent,
remarkPresetLintRecommended,
[remarkMdx, { printWidth: 80 }],
[remarkLintFileExtension, ['md', 'mdx']],
[remarkLintMaximumLineLength, false],
remarkFrontmatter,
noMarkdownInJsx,
],
};
export default remarkConfig; |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 7 replies
-
Welcome @wdcryer! 👋 Are you trying to use the plugin through ESLint MDX? Or if not, can you share a more complete example or how you have tried configuring remark/mdx? |
Beta Was this translation helpful? Give feedback.
-
I think this depends on how you wrote your code — the code that you didn’t share, namely It might be that, because you wrote it a certain way, it expects: <SomeComponent>
{/* lint disable no-markdown-in-jsx */}
SomeComponent will get wrapped in curly braces despite the ignore comment above.
</SomeComponent> You might not need this custom rule though. You could also explain to authors how to escape things in markdown. Or, that they can wrap things?
|
Beta Was this translation helpful? Give feedback.
-
I see you’re using Currently you need to run remark . --output
remark . --output --ext mdx --use remark-mdx |
Beta Was this translation helpful? Give feedback.
Most of those packages are all about messages; they work based on their positional info. And then remove messages.
The comments are in the AST. So you can use
mdast-comment-marker
to parse their syntax. And then choose to do things in your transform.Though, why use comments? You can look for an attribute on JSX:
<SomeComponent someProp />
.But, personally, I wouldn’t do it: MDX already has ways to differentiate between different things: