UNPKG

16 kBJavaScriptView Raw
1import { __assign, __spreadArray } from 'tslib';
2import { version } from 'eslint/package.json';
3import { JSX_TYPES, isJsxNode, openTag, DEFAULT_EXTENSIONS, MARKDOWN_EXTENSIONS } from 'eslint-mdx';
4import reactNoUnescapedEntities from 'eslint-plugin-react/lib/rules/no-unescaped-entities';
5import esLintNoUnusedExpressions from 'eslint/lib/rules/no-unused-expressions';
6import path from 'path';
7import vfile from 'vfile';
8import { cosmiconfigSync } from 'cosmiconfig';
9import remarkMdx from 'remark-mdx';
10import remarkParse from 'remark-parse';
11import remarkStringify from 'remark-stringify';
12import unified from 'unified';
13
14var base = {
15 parser: 'eslint-mdx',
16 plugins: ['mdx'],
17};
18
19var getGlobals = function (sources, initialGlobals) {
20 if (initialGlobals === void 0) { initialGlobals = {}; }
21 return (Array.isArray(sources)
22 ? sources
23 : Object.keys(sources)).reduce(function (globals, source) {
24 var _a;
25 return Object.assign(globals, (_a = {},
26 _a[source] = false,
27 _a));
28 }, initialGlobals);
29};
30
31var rebass;
32try {
33 // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
34 rebass = require('rebass');
35}
36catch (_a) {
37 // `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
38 /* istanbul ignore next */
39 rebass = ['Box', 'Flex', 'Text', 'Heading', 'Link', 'Button', 'Image', 'Card'];
40}
41var overrides$1 = __assign(__assign({}, base), { globals: getGlobals(rebass, {
42 React: false,
43 }), rules: {
44 'lines-between-class-members': 0,
45 'react/jsx-no-undef': [
46 2,
47 {
48 allowGlobals: true,
49 },
50 ],
51 'react/react-in-jsx-scope': 0,
52 } });
53
54var minorVersion = +version.split('.').slice(0, 2).join('.');
55var recommended = __assign(__assign({}, base), { rules: {
56 'mdx/no-jsx-html-comments': 2,
57 'mdx/no-unescaped-entities': 1,
58 'mdx/no-unused-expressions': 2,
59 'mdx/remark': 1,
60 'no-unused-expressions': 0,
61 'react/no-unescaped-entities': 0,
62 } });
63var OVERRIDES_AVAILABLE_VERSION = 6.4;
64// overrides in npm pkg is supported after v6.4.0
65// istanbul ignore else
66if (minorVersion >= OVERRIDES_AVAILABLE_VERSION) {
67 var overrides = [
68 {
69 files: '*.mdx',
70 extends: 'plugin:mdx/overrides',
71 },
72 ];
73 try {
74 // eslint-disable-next-line node/no-extraneous-require
75 require.resolve('prettier');
76 // eslint-disable-next-line node/no-extraneous-require
77 require.resolve('eslint-plugin-prettier');
78 overrides.push({
79 files: '*.md',
80 rules: {
81 'prettier/prettier': [
82 2,
83 {
84 parser: 'markdown',
85 },
86 ],
87 },
88 });
89 }
90 catch (_a) { }
91 Object.assign(recommended, {
92 overrides: overrides,
93 });
94}
95
96var index = /*#__PURE__*/Object.freeze({
97 __proto__: null,
98 base: base,
99 overrides: overrides$1,
100 recommended: recommended
101});
102
103var noJsxHtmlComments = {
104 meta: {
105 type: 'problem',
106 docs: {
107 description: 'Forbid invalid html style comments in jsx block',
108 category: 'SyntaxError',
109 recommended: true,
110 },
111 messages: {
112 jsxHtmlComments: 'html style comments are invalid in jsx: {{ origin }}',
113 },
114 fixable: 'code',
115 },
116 create: function (context) {
117 return {
118 ExpressionStatement: function (node) {
119 var invalidNodes = context.parserServices
120 .JSXElementsWithHTMLComments;
121 if (!JSX_TYPES.includes(node.expression.type) ||
122 node.parent.type !== 'Program' ||
123 !invalidNodes ||
124 invalidNodes.length === 0) {
125 return;
126 }
127 var invalidNode = invalidNodes.shift();
128 if (invalidNode.data.inline) {
129 return;
130 }
131 var comments = invalidNode.data.comments;
132 var _loop_1 = function (fixed, loc, origin_1) {
133 context.report({
134 messageId: 'jsxHtmlComments',
135 data: {
136 origin: origin_1,
137 },
138 loc: loc,
139 node: node,
140 fix: function (fixer) {
141 return fixer.replaceTextRange([loc.start.offset, loc.end.offset], fixed);
142 },
143 });
144 };
145 for (var _i = 0, comments_1 = comments; _i < comments_1.length; _i++) {
146 var _a = comments_1[_i], fixed = _a.fixed, loc = _a.loc, origin_1 = _a.origin;
147 _loop_1(fixed, loc, origin_1);
148 }
149 },
150 };
151 },
152};
153
154/// <reference path="../../typings.d.ts" />
155// copied from `eslint-plugin-react`
156var DEFAULTS = [
157 {
158 char: '>',
159 alternatives: ['&gt;'],
160 },
161 {
162 char: '"',
163 alternatives: ['&quot;', '&ldquo;', '&#34;', '&rdquo;'],
164 },
165 {
166 char: "'",
167 alternatives: ['&apos;', '&lsquo;', '&#39;', '&rsquo;'],
168 },
169 {
170 char: '}',
171 alternatives: ['&#125;'],
172 },
173];
174var EXPRESSION = 'Literal, JSXText';
175var noUnescapedEntities = __assign(__assign({}, reactNoUnescapedEntities), { create: function (context) {
176 var _a;
177 var configuration = (context.options[0] || {});
178 var entities = configuration.forbid || DEFAULTS;
179 return _a = {},
180 // eslint-disable-next-line sonarjs/cognitive-complexity
181 _a[EXPRESSION] = function (node) {
182 var parent = node.parent, loc = node.loc;
183 if (!isJsxNode(parent)) {
184 return;
185 }
186 while (parent) {
187 if (parent.parent.type === 'Program') {
188 break;
189 }
190 else {
191 parent = parent.parent;
192 }
193 }
194 var _a = loc.start, startLine = _a.line, startColumn = _a.column, _b = loc.end, endLine = _b.line, endColumn = _b.column;
195 var lines = context.getSourceCode().lines;
196 var firstLineOffset = parent.loc.start.line < startLine
197 ? 0
198 : lines
199 .slice(startLine - 1, endLine)
200 .join('\n')
201 .search(openTag);
202 /* istanbul ignore if */
203 if (firstLineOffset < 0) {
204 // should never happen, just for robustness
205 firstLineOffset = 0;
206 }
207 for (var i = startLine; i <= endLine; i++) {
208 var rawLine = lines[i - 1];
209 var start = 0;
210 var end = rawLine.length;
211 if (i === startLine) {
212 start = startColumn + firstLineOffset;
213 }
214 if (i === endLine) {
215 end = endColumn;
216 if (i === startLine) {
217 end += firstLineOffset;
218 }
219 }
220 rawLine = rawLine.slice(start, end);
221 for (var _i = 0, entities_1 = entities; _i < entities_1.length; _i++) {
222 var entity = entities_1[_i];
223 // eslint-disable-next-line unicorn/no-for-loop
224 for (var index = 0; index < rawLine.length; index++) {
225 var char = rawLine[index];
226 if (typeof entity === 'string') {
227 if (char === entity) {
228 context.report({
229 loc: { line: i, column: start + index },
230 message: "HTML entity, `" + entity + "` , must be escaped.",
231 node: node,
232 });
233 }
234 }
235 else if (char === entity.char) {
236 context.report({
237 loc: { line: i, column: start + index },
238 message: "`" + entity.char + "` can be escaped with " + entity.alternatives
239 .map(function (alt) { return '``'.split('').join(alt); })
240 .join(', ') + ".",
241 node: node,
242 });
243 }
244 }
245 }
246 }
247 },
248 _a;
249 } });
250
251/// <reference path="../../typings.d.ts" />
252var noUnusedExpressions = __assign(__assign({}, esLintNoUnusedExpressions), { create: function (context) {
253 var esLintRuleListener = esLintNoUnusedExpressions.create(context);
254 return {
255 ExpressionStatement: function (node) {
256 if (isJsxNode(node.expression) && node.parent.type === 'Program') {
257 return;
258 }
259 esLintRuleListener.ExpressionStatement(node);
260 },
261 };
262 } });
263
264var requirePkg = function (plugin, prefix, filePath) {
265 if (filePath && /^\.\.?([/\\]|$)/.test(plugin)) {
266 plugin = path.resolve(path.dirname(filePath), plugin);
267 }
268 prefix = prefix.endsWith('-') ? prefix : prefix + '-';
269 var packages = [
270 plugin,
271 plugin.startsWith('@')
272 ? plugin.replace('/', '/' + prefix)
273 : prefix + plugin,
274 ];
275 var error;
276 for (var _i = 0, packages_1 = packages; _i < packages_1.length; _i++) {
277 var pkg = packages_1[_i];
278 try {
279 // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
280 return require(pkg);
281 }
282 catch (err) {
283 if (!error) {
284 error = err;
285 }
286 }
287 }
288 throw error;
289};
290var searchSync;
291var remarkProcessor;
292var getRemarkProcessor = function (searchFrom, isMdx) {
293 if (!searchSync) {
294 searchSync = cosmiconfigSync('remark', {
295 packageProp: 'remarkConfig',
296 }).search;
297 }
298 if (!remarkProcessor) {
299 remarkProcessor = unified().use(remarkParse).freeze();
300 }
301 /* istanbul ignore next */
302 var result = searchSync(searchFrom) || {};
303 /* istanbul ignore next */
304 var _a = (result.config ||
305 {}), _b = _a.plugins, plugins = _b === void 0 ? [] : _b, settings = _a.settings;
306 try {
307 // disable this rule automatically since we already have a parser option `extensions`
308 // eslint-disable-next-line node/no-extraneous-require
309 plugins.push([require.resolve('remark-lint-file-extension'), false]);
310 }
311 catch (_c) {
312 // just ignore if the package does not exist
313 }
314 var initProcessor = remarkProcessor().use({ settings: settings }).use(remarkStringify);
315 if (isMdx) {
316 initProcessor.use(remarkMdx);
317 }
318 return plugins
319 .reduce(function (processor, pluginWithSettings) {
320 var _a = Array.isArray(pluginWithSettings)
321 ? pluginWithSettings
322 : [pluginWithSettings], plugin = _a[0], pluginSettings = _a.slice(1);
323 return processor.use.apply(processor, __spreadArray([
324 /* istanbul ignore next */
325 typeof plugin === 'string'
326 ? requirePkg(plugin, 'remark', result.filepath)
327 : plugin], pluginSettings));
328 }, initProcessor)
329 .freeze();
330};
331
332var remark = {
333 meta: {
334 type: 'layout',
335 docs: {
336 description: 'Linter integration with remark plugins',
337 category: 'Stylistic Issues',
338 recommended: true,
339 },
340 messages: {
341 remarkReport: '{{ source }}:{{ ruleId }} - {{ reason }}',
342 },
343 fixable: 'code',
344 },
345 create: function (context) {
346 var filename = context.getFilename();
347 var extname = path.extname(filename);
348 var sourceCode = context.getSourceCode();
349 var options = context.parserOptions;
350 var isMdx = DEFAULT_EXTENSIONS.concat(options.extensions || []).includes(extname);
351 var isMarkdown = MARKDOWN_EXTENSIONS.concat(options.markdownExtensions || []).includes(extname);
352 return {
353 Program: function (node) {
354 /* istanbul ignore if */
355 if (!isMdx && !isMarkdown) {
356 return;
357 }
358 var sourceText = sourceCode.getText(node);
359 var remarkProcessor = getRemarkProcessor(filename, isMdx);
360 var file = vfile({
361 path: filename,
362 contents: sourceText,
363 });
364 try {
365 remarkProcessor.processSync(file);
366 }
367 catch (err) {
368 /* istanbul ignore next */
369 if (!file.messages.includes(err)) {
370 file.message(err).fatal = true;
371 }
372 }
373 var _loop_1 = function (source, reason, ruleId, start, end) {
374 context.report({
375 messageId: 'remarkReport',
376 data: {
377 reason: reason,
378 source: source,
379 ruleId: ruleId,
380 },
381 loc: {
382 // ! eslint ast column is 0-indexed, but unified is 1-indexed
383 start: __assign(__assign({}, start), { column: start.column - 1 }),
384 end: __assign(__assign({}, end), { column: end.column - 1 }),
385 },
386 node: node,
387 fix: function (fixer) {
388 /* istanbul ignore if */
389 if (start.offset == null) {
390 return null;
391 }
392 var range = [
393 start.offset,
394 /* istanbul ignore next */
395 end.offset == null ? start.offset + 1 : end.offset,
396 ];
397 var partialText = sourceText.slice.apply(sourceText, range);
398 var fixed = remarkProcessor.processSync(partialText).toString();
399 return fixer.replaceTextRange(range,
400 /* istanbul ignore next */
401 partialText.endsWith('\n') ? fixed : fixed.slice(0, -1));
402 },
403 });
404 };
405 for (var _i = 0, _a = file.messages; _i < _a.length; _i++) {
406 var _b = _a[_i], source = _b.source, reason = _b.reason, ruleId = _b.ruleId, _c = _b.location, start = _c.start, end = _c.end;
407 _loop_1(source, reason, ruleId, start, end);
408 }
409 },
410 };
411 },
412};
413
414var rules = {
415 'no-jsx-html-comments': noJsxHtmlComments,
416 'no-unescaped-entities': noUnescapedEntities,
417 'no-unused-expressions': noUnusedExpressions,
418 remark: remark,
419};
420
421export { index as configs, getGlobals, getRemarkProcessor, noJsxHtmlComments, noUnescapedEntities, noUnusedExpressions, remark, requirePkg, rules };