eslint-plugin-mdx
Version:
ESLint Plugin for MDX
778 lines (757 loc) • 30.4 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var tslib = require('tslib');
var package_json = require('eslint/package.json');
var eslintMdx = require('eslint-mdx');
var esLintNoUnusedExpressions = require('eslint/lib/rules/no-unused-expressions');
var path = require('path');
var vfile = require('vfile');
var cosmiconfig = require('cosmiconfig');
var remarkMdx = require('remark-mdx');
var remarkParse = require('remark-parse');
var remarkStringify = require('remark-stringify');
var unified = require('unified');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var esLintNoUnusedExpressions__default = /*#__PURE__*/_interopDefaultLegacy(esLintNoUnusedExpressions);
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
var vfile__default = /*#__PURE__*/_interopDefaultLegacy(vfile);
var remarkMdx__default = /*#__PURE__*/_interopDefaultLegacy(remarkMdx);
var remarkParse__default = /*#__PURE__*/_interopDefaultLegacy(remarkParse);
var remarkStringify__default = /*#__PURE__*/_interopDefaultLegacy(remarkStringify);
var unified__default = /*#__PURE__*/_interopDefaultLegacy(unified);
var base = {
parser: 'eslint-mdx',
plugins: ['mdx'],
processor: 'mdx/remark',
};
var codeBlocks = {
parserOptions: {
ecmaFeatures: {
// Adding a "use strict" directive at the top of
// every code block is tedious and distracting, so
// opt into strict mode parsing without the
// directive.
impliedStrict: true,
},
},
rules: {
// The Markdown parser automatically trims trailing
// newlines from code blocks.
'eol-last': 'off',
// In code snippets and examples, these rules are often
// counterproductive to clarity and brevity.
'no-undef': 'off',
'no-unused-expressions': 'off',
'no-unused-vars': 'off',
'padded-blocks': 'off',
// Adding a "use strict" directive at the top of every
// code block is tedious and distracting. The config
// opts into strict mode parsing without the directive.
strict: 'off',
// The processor will not receive a Unicode Byte Order
// Mark from the Markdown parser.
'unicode-bom': 'off',
},
};
var getGlobals = function (sources, initialGlobals) {
if (initialGlobals === void 0) { initialGlobals = {}; }
return (Array.isArray(sources)
? sources
: Object.keys(sources)).reduce(function (globals, source) {
var _a;
return Object.assign(globals, (_a = {},
_a[source] = false,
_a));
}, initialGlobals);
};
var rebass;
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
rebass = require('rebass');
}
catch (_a) {
// `rebass`(or `reflexbox` actually) requires `react` as peerDependency, but not all projects using `mdx` are `React` based, so we fallback to hardcoded `rebass` Components here
/* istanbul ignore next */
rebass = ['Box', 'Flex', 'Text', 'Heading', 'Link', 'Button', 'Image', 'Card'];
}
var overrides$1 = tslib.__assign(tslib.__assign({}, base), { globals: getGlobals(rebass, {
React: false,
}), rules: {
'lines-between-class-members': 0,
'react/jsx-no-undef': [
2,
{
allowGlobals: true,
},
],
'react/react-in-jsx-scope': 0,
} });
var minorVersion = +package_json.version.split('.').slice(0, 2).join('.');
var recommended = tslib.__assign(tslib.__assign({}, base), { rules: {
'mdx/no-jsx-html-comments': 2,
'mdx/no-unused-expressions': 2,
'mdx/remark': 1,
'no-unused-expressions': 0,
} });
var OVERRIDES_AVAILABLE_VERSION = 6.4;
// overrides in npm pkg is supported after v6.4.0
// istanbul ignore else
if (minorVersion >= OVERRIDES_AVAILABLE_VERSION) {
var overrides = [
{
files: '*.mdx',
extends: 'plugin:mdx/overrides',
},
{
files: '**/*.{md,mdx}/**',
extends: 'plugin:mdx/code-blocks',
},
];
try {
// eslint-disable-next-line node/no-extraneous-require
require.resolve('prettier');
// eslint-disable-next-line node/no-extraneous-require
require.resolve('eslint-plugin-prettier');
overrides.push({
files: '*.md',
rules: {
'prettier/prettier': [
2,
{
parser: 'markdown',
},
],
},
});
}
catch (_a) { }
Object.assign(recommended, {
overrides: overrides,
});
}
/* istanbul ignore file */
var configs = {
base: base,
'code-blocks': codeBlocks,
codeBlocks: codeBlocks,
overrides: overrides$1,
recommended: recommended,
};
var noJsxHtmlComments = {
meta: {
type: 'problem',
docs: {
description: 'Forbid invalid html style comments in jsx block',
category: 'SyntaxError',
recommended: true,
},
messages: {
jsxHtmlComments: 'html style comments are invalid in jsx: {{ origin }}',
},
fixable: 'code',
},
create: function (context) {
return {
ExpressionStatement: function (node) {
var invalidNodes = context.parserServices.JSXElementsWithHTMLComments;
if (!eslintMdx.isJsxNode(node.expression) ||
node.parent.type !== 'Program' ||
!invalidNodes ||
invalidNodes.length === 0) {
return;
}
var invalidNode = invalidNodes.shift();
if (invalidNode.data.inline) {
return;
}
var comments = invalidNode.data.comments;
var _loop_1 = function (fixed, loc, origin) {
context.report({
messageId: 'jsxHtmlComments',
data: {
origin: origin,
},
loc: loc,
node: node,
fix: function (fixer) {
return fixer.replaceTextRange([loc.start.offset, loc.end.offset], fixed);
},
});
};
for (var _i = 0, comments_1 = comments; _i < comments_1.length; _i++) {
var _a = comments_1[_i], fixed = _a.fixed, loc = _a.loc, origin = _a.origin;
_loop_1(fixed, loc, origin);
}
},
};
},
};
/// <reference path="../../typings.d.ts" />
var noUnusedExpressions = tslib.__assign(tslib.__assign({}, esLintNoUnusedExpressions__default['default']), { create: function (context) {
var esLintRuleListener = esLintNoUnusedExpressions__default['default'].create(context);
return {
ExpressionStatement: function (node) {
if (eslintMdx.isJsxNode(node.expression) && node.parent.type === 'Program') {
return;
}
esLintRuleListener.ExpressionStatement(node);
},
};
} });
var requirePkg = function (plugin, prefix, filePath) {
if (filePath && /^\.\.?([/\\]|$)/.test(plugin)) {
plugin = path__default['default'].resolve(path__default['default'].dirname(filePath), plugin);
}
prefix = prefix.endsWith('-') ? prefix : prefix + '-';
var packages = [
plugin,
plugin.startsWith('@')
? plugin.replace('/', '/' + prefix)
: prefix + plugin,
];
var error;
for (var _i = 0, packages_1 = packages; _i < packages_1.length; _i++) {
var pkg = packages_1[_i];
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
return require(pkg);
}
catch (err) {
if (!error) {
error = err;
}
}
}
throw error;
};
var searchSync;
var remarkProcessor = unified__default['default']().use(remarkParse__default['default']).freeze();
var getRemarkProcessor = function (searchFrom, isMdx) {
if (!searchSync) {
searchSync = cosmiconfig.cosmiconfigSync('remark', {
packageProp: 'remarkConfig',
}).search;
}
var result;
try {
result = searchSync(searchFrom);
}
catch (err) {
// https://github.com/eslint/eslint/issues/11989
/* istanbul ignore if */
if (err.code !== 'ENOTDIR' ||
!/\d+_?\.[a-z]+$/.test(searchFrom)) {
throw err;
}
try {
result = searchSync(path__default['default'].dirname(searchFrom));
}
catch (_a) {
/* istanbul ignore next */
throw err;
}
}
/* istanbul ignore next */
var _b = ((result === null || result === void 0 ? void 0 : result.config) ||
{}), _c = _b.plugins, plugins = _c === void 0 ? [] : _c, settings = _b.settings;
try {
// disable this rule automatically since we already have a parser option `extensions`
// eslint-disable-next-line node/no-extraneous-require
plugins.push([require.resolve('remark-lint-file-extension'), false]);
}
catch (_d) {
// just ignore if the package does not exist
}
var initProcessor = remarkProcessor().use({ settings: settings }).use(remarkStringify__default['default']);
if (isMdx) {
initProcessor.use(remarkMdx__default['default']);
}
return plugins
.reduce(function (processor, pluginWithSettings) {
var _a = eslintMdx.arrayify(pluginWithSettings), plugin = _a[0], pluginSettings = _a.slice(1);
return processor.use.apply(processor, tslib.__spreadArray([
/* istanbul ignore next */
typeof plugin === 'string'
? requirePkg(plugin, 'remark', result.filepath)
: plugin], pluginSettings));
}, initProcessor)
.freeze();
};
var remark$1 = {
meta: {
type: 'layout',
docs: {
description: 'Linter integration with remark plugins',
category: 'Stylistic Issues',
recommended: true,
},
fixable: 'code',
},
create: function (context) {
var filename = context.getFilename();
var extname = path__default['default'].extname(filename);
var sourceCode = context.getSourceCode();
var options = context.parserOptions;
var isMdx = eslintMdx.DEFAULT_EXTENSIONS.concat(options.extensions || []).includes(extname);
var isMarkdown = eslintMdx.MARKDOWN_EXTENSIONS.concat(options.markdownExtensions || []).includes(extname);
return {
// eslint-disable-next-line sonarjs/cognitive-complexity
Program: function (node) {
/* istanbul ignore if */
if (!isMdx && !isMarkdown) {
return;
}
var sourceText = sourceCode.getText(node);
var remarkProcessor = getRemarkProcessor(filename, isMdx);
var file = vfile__default['default']({
path: filename,
contents: sourceText,
});
try {
remarkProcessor.processSync(file);
}
catch (err) {
/* istanbul ignore next */
if (!file.messages.includes(err)) {
file.message(err).fatal = true;
}
}
var _loop_1 = function (source, reason, ruleId, fatal, start, end) {
// https://github.com/remarkjs/remark-lint/issues/65#issuecomment-220800231
/* istanbul ignore next */
var severity = fatal ? 2 : fatal == null ? 0 : 1;
/* istanbul ignore if */
if (!severity) {
return "continue";
}
var message = {
reason: reason,
source: source,
ruleId: ruleId,
severity: severity,
};
context.report({
// related to https://github.com/eslint/eslint/issues/14198
message: JSON.stringify(message),
loc: {
// ! eslint ast column is 0-indexed, but unified is 1-indexed
start: tslib.__assign(tslib.__assign({}, start), { column: start.column - 1 }),
end: tslib.__assign(tslib.__assign({}, end), { column: end.column - 1 }),
},
node: node,
fix: function (fixer) {
/* istanbul ignore if */
if (start.offset == null) {
return null;
}
var range = [
start.offset,
/* istanbul ignore next */
end.offset == null ? start.offset + 1 : end.offset,
];
var partialText = sourceText.slice.apply(sourceText, range);
var fixed = remarkProcessor.processSync(partialText).toString();
return fixer.replaceTextRange(range,
/* istanbul ignore next */
partialText.endsWith('\n') ? fixed : fixed.slice(0, -1));
},
});
};
for (var _i = 0, _a = file.messages; _i < _a.length; _i++) {
var _b = _a[_i], source = _b.source, reason = _b.reason, ruleId = _b.ruleId, fatal = _b.fatal, _c = _b.location, start = _c.start, end = _c.end;
_loop_1(source, reason, ruleId, fatal, start, end);
}
},
};
},
};
/* istanbul ignore file */
var rules = {
'no-jsx-html-comments': noJsxHtmlComments,
'no-unused-expressions': noUnusedExpressions,
noJsxHtmlComments: noJsxHtmlComments,
noUnusedExpressions: noUnusedExpressions,
remark: remark$1,
};
/**
* based of @link https://github.com/eslint/eslint-plugin-markdown/blob/main/lib/processor.js
*
* @fileoverview Processes Markdown files for consumption by ESLint.
* @author Brandon Mills
*/
var UNSATISFIABLE_RULES = new Set([
'eol-last',
'unicode-bom', // Code blocks will begin in the middle of Markdown files
]);
var SUPPORTS_AUTOFIX = true;
var blocksCache = {};
/**
* Performs a depth-first traversal of the Markdown AST.
* @param node A Markdown AST node.
* @param callbacks A map of node types to callbacks.
* @param parent The node's parent AST node.
*/
function traverse(node, callbacks, parent) {
if (callbacks[node.type]) {
callbacks[node.type](node, parent);
}
if (typeof node.children !== 'undefined') {
var parent_1 = node;
for (var _i = 0, _a = parent_1.children; _i < _a.length; _i++) {
var child = _a[_i];
traverse(child, callbacks, parent_1);
}
}
}
/**
* Converts leading HTML comments to JS block comments.
* @param html The text content of an HTML AST node.
* @returns JS block comment.
*/
function getComment(html) {
var commentStart = '<!--';
var commentEnd = '-->';
var regex = /^(eslint\b|global\s)/u;
if (html.slice(0, commentStart.length) !== commentStart ||
html.slice(-commentEnd.length) !== commentEnd) {
return '';
}
var comment = html.slice(commentStart.length, -commentEnd.length);
if (!regex.test(comment.trim())) {
return '';
}
return comment;
}
// Before a code block, blockquote characters (`>`) are also considered
// "whitespace".
var leadingWhitespaceRegex = /^[>\s]*/u;
/**
* Gets the offset for the first column of the node's first line in the
* original source text.
* @param node A Markdown code block AST node.
* @returns The offset for the first column of the node's first line.
*/
function getBeginningOfLineOffset(node) {
return node.position.start.offset - node.position.start.column + 1;
}
/**
* Gets the leading text, typically whitespace with possible blockquote chars,
* used to indent a code block.
* @param text The text of the file.
* @param node A Markdown code block AST node.
* @returns The text from the start of the first line to the opening
* fence of the code block.
*/
function getIndentText(text, node) {
return leadingWhitespaceRegex.exec(text.slice(getBeginningOfLineOffset(node)))[0];
}
/**
* When applying fixes, the postprocess step needs to know how to map fix ranges
* from their location in the linted JS to the original offset in the Markdown.
* Configuration comments and indentation trimming both complicate this process.
*
* Configuration comments appear in the linted JS but not in the Markdown code
* block. Fixes to configuration comments would cause undefined behavior and
* should be ignored during postprocessing. Fixes to actual code after
* configuration comments need to be mapped back to the code block after
* removing any offset due to configuration comments.
*
* Fenced code blocks can be indented by up to three spaces at the opening
* fence. Inside of a list, for example, this indent can be in addition to the
* indent already required for list item children. Leading whitespace inside
* indented code blocks is trimmed up to the level of the opening fence and does
* not appear in the linted code. Further, lines can have less leading
* whitespace than the opening fence, so not all lines are guaranteed to have
* the same column offset as the opening fence.
*
* The source code of a non-configuration-comment line in the linted JS is a
* suffix of the corresponding line in the Markdown code block. There are no
* differences within the line, so the mapping need only provide the offset
* delta at the beginning of each line.
* @param text The text of the file.
* @param node A Markdown code block AST node.
* @param comments List of configuration comment strings that will be
* inserted at the beginning of the code block.
* @returns A list of offset-based adjustments, where lookups are
* done based on the `js` key, which represents the range in the linted JS,
* and the `md` key is the offset delta that, when added to the JS range,
* returns the corresponding location in the original Markdown source.
*/
function getBlockRangeMap(text, node, comments) {
/*
* The parser sets the fenced code block's start offset to wherever content
* should normally begin (typically the first column of the line, but more
* inside a list item, for example). The code block's opening fancy may be
* further indented by up to three characters. If the code block has
* additional indenting, the opening fence's first backtick may be up to
* three whitespace characters after the start offset.
*/
var startOffset = getBeginningOfLineOffset(node);
/*
* Extract the Markdown source to determine the leading whitespace for each
* line.
*/
var code = text.slice(startOffset, node.position.end.offset);
var lines = code.split('\n');
/*
* The parser trims leading whitespace from each line of code within the
* fenced code block up to the opening fence's first backtick. The first
* backtick's column is the AST node's starting column plus any additional
* indentation.
*/
var baseIndent = getIndentText(text, node).length;
/*
* Track the length of any inserted configuration comments at the beginning
* of the linted JS and start the JS offset lookup keys at this index.
*/
var commentLength = comments.reduce(function (len, comment) { return len + comment.length + 1; }, 0);
/*
* In case there are configuration comments, initialize the map so that the
* first lookup index is always 0. If there are no configuration comments,
* the lookup index will also be 0, and the lookup should always go to the
* last range that matches, skipping this initialization entry.
*/
var rangeMap = [
{
js: 0,
md: 0,
},
];
// Start the JS offset after any configuration comments.
var jsOffset = commentLength;
/*
* Start the Markdown offset at the beginning of the block's first line of
* actual code. The first line of the block is always the opening fence, so
* the code begins on the second line.
*/
var mdOffset = startOffset + lines[0].length + 1;
/*
* For each line, determine how much leading whitespace was trimmed due to
* indentation. Increase the JS lookup offset by the length of the line
* post-trimming and the Markdown offset by the total line length.
*/
for (var i = 0; i + 1 < lines.length; i++) {
var line = lines[i + 1];
var leadingWhitespaceLength = leadingWhitespaceRegex.exec(line)[0].length;
// The parser trims leading whitespace up to the level of the opening
// fence, so keep any additional indentation beyond that.
var trimLength = Math.min(baseIndent, leadingWhitespaceLength);
rangeMap.push({
js: jsOffset,
// Advance `trimLength` character from the beginning of the Markdown
// line to the beginning of the equivalent JS line, then compute the
// delta.
md: mdOffset + trimLength - jsOffset,
});
// Accumulate the current line in the offsets, and don't forget the
// newline.
mdOffset += line.length + 1;
jsOffset += line.length - trimLength + 1;
}
return rangeMap;
}
var LANGUAGES_MAPPER = {
javascript: 'js',
javascriptreact: 'jsx',
typescript: 'ts',
typescriptreact: 'tsx',
markdown: 'md',
mdown: 'md',
mkdn: 'md',
};
/**
* get short language
* @param lang original language
* @returns short language
*/
function getShortLang(lang) {
var language = eslintMdx.last(lang.split(/\s/u)[0].split('.')).toLowerCase();
return LANGUAGES_MAPPER[language] || language;
}
/**
* Extracts lintable JavaScript code blocks from Markdown text.
* @param text The text of the file.
* @param filename The filename of the file
* @returns Source code strings to lint.
*/
function preprocess(text, filename) {
var ast = remarkProcessor.parse(text);
var blocks = [];
blocksCache[filename] = blocks;
traverse(ast, {
code: function (node, parent) {
var comments = [];
if (node.lang) {
var index = parent.children.indexOf(node) - 1;
var previousNode = parent.children[index];
while (previousNode && previousNode.type === 'html') {
var comment = getComment(previousNode.value);
if (!comment) {
break;
}
if (comment.trim() === 'eslint-skip') {
return;
}
comments.unshift("/*" + comment + "*/");
index--;
previousNode = parent.children[index];
}
blocks.push(tslib.__assign(tslib.__assign({}, node), { baseIndentText: getIndentText(text, node), comments: comments, rangeMap: getBlockRangeMap(text, node, comments) }));
}
},
});
return blocks.map(function (block, index) { return ({
filename: index + "." + getShortLang(block.lang),
text: tslib.__spreadArray(tslib.__spreadArray([], block.comments), [block.value, '']).join('\n'),
}); });
}
/**
* Creates a map function that adjusts messages in a code block.
* @param block A code block.
* @returns A function that adjusts messages in a code block.
*/
function adjustBlock(block) {
var leadingCommentLines = block.comments.reduce(function (count, comment) { return count + comment.split('\n').length; }, 0);
var blockStart = block.position.start.line;
/**
* Adjusts ESLint messages to point to the correct location in the Markdown.
* @param message A message from ESLint.
* @returns The same message, but adjusted to the correct location.
*/
return function adjustMessage(message) {
var lineInCode = message.line - leadingCommentLines;
if (lineInCode < 1) {
return null;
}
var out = {
line: lineInCode + blockStart,
column: message.column + block.position.indent[lineInCode - 1] - 1,
};
/* istanbul ignore else */
if (Number.isInteger(message.endLine)) {
out.endLine = message.endLine - leadingCommentLines + blockStart;
}
var adjustedFix = {};
/* istanbul ignore else */
if (message.fix) {
adjustedFix.fix = {
range: message.fix.range.map(function (range) {
// Advance through the block's range map to find the last
// matching range by finding the first range too far and
// then going back one.
var i = 1;
while (i < block.rangeMap.length && block.rangeMap[i].js <= range) {
i++;
}
// Apply the mapping delta for this range.
return range + block.rangeMap[i - 1].md;
}),
text: message.fix.text.replace(/\n/gu, "\n" + block.baseIndentText),
};
}
return tslib.__assign(tslib.__assign(tslib.__assign({}, message), out), adjustedFix);
};
}
/**
* Excludes unsatisfiable rules from the list of messages.
* @param message A message from the linter.
* @returns True if the message should be included in output.
*/
function excludeUnsatisfiableRules(message) {
return message && !UNSATISFIABLE_RULES.has(message.ruleId);
}
/**
* Transforms generated messages for output.
* @param messages An array containing one array of messages for each code block returned from `preprocess`.
* @param filename The filename of the file
* @returns A flattened array of messages with mapped locations.
*/
function postprocess(messages, filename) {
var _a;
var blocks = blocksCache[filename] || [];
// eslint-disable-next-line unicorn/prefer-spread
return (_a = []).concat.apply(_a, messages.map(function (group, i) {
var block = blocks[i];
// non code block message, parsed by `eslint-mdx` for example
if (!block) {
return group;
}
var adjust = adjustBlock(block);
return group.map(adjust).filter(excludeUnsatisfiableRules);
}));
}
var markdown = {
preprocess: preprocess,
postprocess: postprocess,
supportsAutofix: SUPPORTS_AUTOFIX,
};
/**
* based on @link https://github.com/sveltejs/eslint-plugin-svelte3/blob/master/src/processor_options.js
*/
var processorOptions = {};
// find Linter instance
var linterPath = Object.keys(require.cache).find(function (path) {
return /([/\\])eslint\1lib(?:\1linter){2}\.js$/.test(path);
});
/* istanbul ignore if */
if (!linterPath) {
throw new Error('Could not find ESLint Linter in require cache');
}
// eslint-disable-next-line @typescript-eslint/consistent-type-imports, @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
var ESLinter = require(linterPath).Linter;
// patch Linter#verify
// eslint-disable-next-line @typescript-eslint/unbound-method
var verify = ESLinter.prototype.verify;
ESLinter.prototype.verify = function (code, config, options) {
// fetch settings
var settings = (config &&
(typeof config.extractConfig === 'function'
? config.extractConfig(
/* istanbul ignore next */
typeof options === 'undefined' || typeof options === 'string'
? options
: options.filename)
: config).settings) ||
{};
processorOptions.lintCodeBlocks = settings['mdx/code-blocks'] === true;
// call original Linter#verify
return verify.call(this, code, config, options);
};
var remark = {
supportsAutofix: true,
preprocess: function (text, filename) {
if (!processorOptions.lintCodeBlocks) {
return [text];
}
return tslib.__spreadArray(tslib.__spreadArray([], markdown.preprocess(text, filename)), [text]);
},
postprocess: function (lintMessages, filename) {
return markdown.postprocess(lintMessages, filename).map(function (lintMessage) {
var message = lintMessage.message, eslintRuleId = lintMessage.ruleId, eslintSeverity = lintMessage.severity;
if (eslintRuleId !== 'mdx/remark') {
return lintMessage;
}
var _a = JSON.parse(message), source = _a.source, ruleId = _a.ruleId, reason = _a.reason, severity = _a.severity;
return tslib.__assign(tslib.__assign({}, lintMessage), { ruleId: source + "-" + ruleId, message: reason, severity: Math.max(eslintSeverity, severity) });
});
},
};
var processors = {
markdown: markdown,
remark: remark,
};
exports.base = base;
exports.codeBlocks = codeBlocks;
exports.configs = configs;
exports.getGlobals = getGlobals;
exports.getRemarkProcessor = getRemarkProcessor;
exports.noJsxHtmlComments = noJsxHtmlComments;
exports.noUnusedExpressions = noUnusedExpressions;
exports.overrides = overrides$1;
exports.processorOptions = processorOptions;
exports.processors = processors;
exports.recommended = recommended;
exports.remark = remark$1;
exports.remarkProcessor = remarkProcessor;
exports.requirePkg = requirePkg;
exports.rules = rules;