eslint-plugin-markdown-preferences 0.29.1 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -190,21 +190,23 @@ The rules with the following 💄 are included in the `standard` config.
190
190
 
191
191
  <!-- prettier-ignore-start -->
192
192
 
193
- | Rule ID | Description | Fixable | Config |
194
- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------- | :-----: | :----: |
195
- | [markdown-preferences/blockquote-marker-alignment](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/blockquote-marker-alignment.html) | enforce consistent alignment of blockquote markers | 🔧 | ⭐💄 |
196
- | [markdown-preferences/indent](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/indent.html) | enforce consistent indentation in Markdown files | 🔧 | 💄 |
197
- | [markdown-preferences/link-bracket-newline](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/link-bracket-newline.html) | enforce linebreaks after opening and before closing link brackets | 🔧 | 💄 |
198
- | [markdown-preferences/link-bracket-spacing](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/link-bracket-spacing.html) | enforce consistent spacing inside link brackets | 🔧 | 💄 |
199
- | [markdown-preferences/link-paren-newline](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/link-paren-newline.html) | enforce linebreaks after opening and before closing link parentheses | 🔧 | 💄 |
200
- | [markdown-preferences/link-paren-spacing](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/link-paren-spacing.html) | enforce consistent spacing inside link parentheses | 🔧 | 💄 |
201
- | [markdown-preferences/list-marker-alignment](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/list-marker-alignment.html) | enforce consistent alignment of list markers | 🔧 | ⭐💄 |
202
- | [markdown-preferences/no-multi-spaces](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-multi-spaces.html) | disallow multiple spaces | 🔧 | 💄 |
203
- | [markdown-preferences/no-multiple-empty-lines](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-multiple-empty-lines.html) | disallow multiple empty lines in Markdown files. | 🔧 | 💄 |
204
- | [markdown-preferences/no-trailing-spaces](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-trailing-spaces.html) | disallow trailing whitespace at the end of lines in Markdown files. | 🔧 | 💄 |
205
- | [markdown-preferences/padded-custom-containers](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/padded-custom-containers.html) | disallow or require padding inside custom containers | 🔧 | 💄 |
206
- | [markdown-preferences/padding-line-between-blocks](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/padding-line-between-blocks.html) | require or disallow padding lines between blocks | 🔧 | 💄 |
207
- | [markdown-preferences/table-pipe-spacing](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/table-pipe-spacing.html) | enforce consistent spacing around table pipes | 🔧 | 💄 |
193
+ | Rule ID | Description | Fixable | Config |
194
+ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------- | :-----: | :----: |
195
+ | [markdown-preferences/blockquote-marker-alignment](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/blockquote-marker-alignment.html) | enforce consistent alignment of blockquote markers | 🔧 | ⭐💄 |
196
+ | [markdown-preferences/code-fence-spacing](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/code-fence-spacing.html) | require or disallow spacing between opening code fence and language identifier | 🔧 | |
197
+ | [markdown-preferences/custom-container-marker-spacing](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/custom-container-marker-spacing.html) | require or disallow spacing between opening custom container marker and info | 🔧 | |
198
+ | [markdown-preferences/indent](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/indent.html) | enforce consistent indentation in Markdown files | 🔧 | 💄 |
199
+ | [markdown-preferences/link-bracket-newline](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/link-bracket-newline.html) | enforce linebreaks after opening and before closing link brackets | 🔧 | 💄 |
200
+ | [markdown-preferences/link-bracket-spacing](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/link-bracket-spacing.html) | enforce consistent spacing inside link brackets | 🔧 | 💄 |
201
+ | [markdown-preferences/link-paren-newline](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/link-paren-newline.html) | enforce linebreaks after opening and before closing link parentheses | 🔧 | 💄 |
202
+ | [markdown-preferences/link-paren-spacing](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/link-paren-spacing.html) | enforce consistent spacing inside link parentheses | 🔧 | 💄 |
203
+ | [markdown-preferences/list-marker-alignment](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/list-marker-alignment.html) | enforce consistent alignment of list markers | 🔧 | ⭐💄 |
204
+ | [markdown-preferences/no-multi-spaces](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-multi-spaces.html) | disallow multiple spaces | 🔧 | 💄 |
205
+ | [markdown-preferences/no-multiple-empty-lines](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-multiple-empty-lines.html) | disallow multiple empty lines in Markdown files. | 🔧 | 💄 |
206
+ | [markdown-preferences/no-trailing-spaces](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/no-trailing-spaces.html) | disallow trailing whitespace at the end of lines in Markdown files. | 🔧 | 💄 |
207
+ | [markdown-preferences/padded-custom-containers](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/padded-custom-containers.html) | disallow or require padding inside custom containers | 🔧 | 💄 |
208
+ | [markdown-preferences/padding-line-between-blocks](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/padding-line-between-blocks.html) | require or disallow padding lines between blocks | 🔧 | 💄 |
209
+ | [markdown-preferences/table-pipe-spacing](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/table-pipe-spacing.html) | enforce consistent spacing around table pipes | 🔧 | 💄 |
208
210
 
209
211
  <!-- prettier-ignore-end -->
210
212
 
package/lib/index.d.ts CHANGED
@@ -45,11 +45,21 @@ interface RuleOptions {
45
45
  * @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/code-fence-length.html
46
46
  */
47
47
  'markdown-preferences/code-fence-length'?: Linter.RuleEntry<MarkdownPreferencesCodeFenceLength>;
48
+ /**
49
+ * require or disallow spacing between opening code fence and language identifier
50
+ * @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/code-fence-spacing.html
51
+ */
52
+ 'markdown-preferences/code-fence-spacing'?: Linter.RuleEntry<MarkdownPreferencesCodeFenceSpacing>;
48
53
  /**
49
54
  * enforce a consistent code fence style (backtick or tilde) in Markdown fenced code blocks.
50
55
  * @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/code-fence-style.html
51
56
  */
52
57
  'markdown-preferences/code-fence-style'?: Linter.RuleEntry<MarkdownPreferencesCodeFenceStyle>;
58
+ /**
59
+ * require or disallow spacing between opening custom container marker and info
60
+ * @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/custom-container-marker-spacing.html
61
+ */
62
+ 'markdown-preferences/custom-container-marker-spacing'?: Linter.RuleEntry<MarkdownPreferencesCustomContainerMarkerSpacing>;
53
63
  /**
54
64
  * require link definitions and footnote definitions to be placed at the end of the document
55
65
  * @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/definitions-last.html
@@ -287,9 +297,15 @@ type MarkdownPreferencesCodeFenceLength = [] | [{
287
297
  fallbackLength?: (number | ("minimum" | "as-is"));
288
298
  }[];
289
299
  }];
300
+ type MarkdownPreferencesCodeFenceSpacing = [] | [{
301
+ space?: ("always" | "never");
302
+ }];
290
303
  type MarkdownPreferencesCodeFenceStyle = [] | [{
291
304
  style?: ("backtick" | "tilde");
292
305
  }];
306
+ type MarkdownPreferencesCustomContainerMarkerSpacing = [] | [{
307
+ space?: ("always" | "never");
308
+ }];
293
309
  type MarkdownPreferencesDefinitionsLast = [] | [{
294
310
  linkDefinitionPlacement?: {
295
311
  referencedFromSingleSection?: ("document-last" | "section-last");
@@ -516,7 +532,7 @@ declare namespace meta_d_exports {
516
532
  export { name, version };
517
533
  }
518
534
  declare const name: "eslint-plugin-markdown-preferences";
519
- declare const version: "0.29.1";
535
+ declare const version: "0.30.0";
520
536
  //#endregion
521
537
  //#region src/language/ast-types.d.ts
522
538
  type Node = mdast.Node;
package/lib/index.js CHANGED
@@ -1279,6 +1279,62 @@ var code_fence_length_default = createRule("code-fence-length", {
1279
1279
  }
1280
1280
  });
1281
1281
 
1282
+ //#endregion
1283
+ //#region src/rules/code-fence-spacing.ts
1284
+ var code_fence_spacing_default = createRule("code-fence-spacing", {
1285
+ meta: {
1286
+ type: "layout",
1287
+ docs: {
1288
+ description: "require or disallow spacing between opening code fence and language identifier",
1289
+ categories: [],
1290
+ listCategory: "Whitespace"
1291
+ },
1292
+ fixable: "whitespace",
1293
+ hasSuggestions: false,
1294
+ schema: [{
1295
+ type: "object",
1296
+ properties: { space: { enum: ["always", "never"] } },
1297
+ additionalProperties: false
1298
+ }],
1299
+ messages: {
1300
+ expectedSpace: "Expected a space between code fence and language identifier.",
1301
+ unexpectedSpace: "Unexpected space between code fence and language identifier."
1302
+ }
1303
+ },
1304
+ create(context) {
1305
+ const sourceCode = context.sourceCode;
1306
+ const space = (context.options[0] || {}).space || "never";
1307
+ return { code(node) {
1308
+ const parsed = parseFencedCodeBlock(sourceCode, node);
1309
+ if (!parsed) return;
1310
+ const { openingFence, language: language$2 } = parsed;
1311
+ if (!language$2) return;
1312
+ const hasSpace = openingFence.range[1] < language$2.range[0];
1313
+ if (space === "always") {
1314
+ if (hasSpace) return;
1315
+ context.report({
1316
+ node,
1317
+ loc: getSourceLocationFromRange(sourceCode, node, language$2.range),
1318
+ messageId: "expectedSpace",
1319
+ fix(fixer) {
1320
+ return fixer.insertTextAfterRange(openingFence.range, " ");
1321
+ }
1322
+ });
1323
+ } else if (space === "never") {
1324
+ if (!hasSpace) return;
1325
+ context.report({
1326
+ node,
1327
+ loc: getSourceLocationFromRange(sourceCode, node, [openingFence.range[1], language$2.range[0]]),
1328
+ messageId: "unexpectedSpace",
1329
+ fix(fixer) {
1330
+ return fixer.removeRange([openingFence.range[1], language$2.range[0]]);
1331
+ }
1332
+ });
1333
+ }
1334
+ } };
1335
+ }
1336
+ });
1337
+
1282
1338
  //#endregion
1283
1339
  //#region src/rules/code-fence-style.ts
1284
1340
  var code_fence_style_default = createRule("code-fence-style", {
@@ -1327,6 +1383,127 @@ var code_fence_style_default = createRule("code-fence-style", {
1327
1383
  }
1328
1384
  });
1329
1385
 
1386
+ //#endregion
1387
+ //#region src/utils/custom-container.ts
1388
+ const RE_OPENING_SEQUENCE = /^(:{3,})/u;
1389
+ /**
1390
+ * Parse the custom container.
1391
+ */
1392
+ function parseCustomContainer(sourceCode, node) {
1393
+ const loc = sourceCode.getLoc(node);
1394
+ const range = sourceCode.getRange(node);
1395
+ const text = sourceCode.text.slice(...range);
1396
+ const match = RE_OPENING_SEQUENCE.exec(text);
1397
+ if (!match) return null;
1398
+ const [, sequenceText] = match;
1399
+ const afterOpeningSequence = sourceCode.lines[loc.start.line - 1].slice(loc.start.column - 1 + sequenceText.length);
1400
+ const trimmedAfterOpeningSequence = afterOpeningSequence.trimStart();
1401
+ const spaceAfterOpeningSequenceLength = afterOpeningSequence.length - trimmedAfterOpeningSequence.length;
1402
+ const infoText = trimmedAfterOpeningSequence.trimEnd();
1403
+ if (!infoText) return null;
1404
+ const openingSequenceRange = [range[0], range[0] + sequenceText.length];
1405
+ const openingSequence = {
1406
+ text: sequenceText,
1407
+ range: openingSequenceRange,
1408
+ loc: getSourceLocationFromRange(sourceCode, node, openingSequenceRange)
1409
+ };
1410
+ const infoRange = [openingSequence.range[1] + spaceAfterOpeningSequenceLength, openingSequence.range[1] + spaceAfterOpeningSequenceLength + infoText.length];
1411
+ const info = {
1412
+ text: infoText,
1413
+ range: infoRange,
1414
+ loc: getSourceLocationFromRange(sourceCode, node, infoRange)
1415
+ };
1416
+ const sequenceChar = sequenceText[0];
1417
+ let closingSequenceText = "";
1418
+ const trimmed = text.trimEnd();
1419
+ const trailingSpacesLength = text.length - trimmed.length;
1420
+ for (let index = trimmed.length - 1; index >= 0; index--) {
1421
+ const c = trimmed[index];
1422
+ if (c === sequenceChar || isSpaceOrTab(c)) {
1423
+ closingSequenceText = c + closingSequenceText;
1424
+ continue;
1425
+ }
1426
+ if (c === ">") {
1427
+ closingSequenceText = ` ${closingSequenceText}`;
1428
+ continue;
1429
+ }
1430
+ if (c === "\n") break;
1431
+ closingSequenceText = "";
1432
+ break;
1433
+ }
1434
+ closingSequenceText = closingSequenceText.trimStart();
1435
+ if (!closingSequenceText || !closingSequenceText.startsWith(sequenceText)) return {
1436
+ openingSequence,
1437
+ info,
1438
+ closingSequence: null
1439
+ };
1440
+ const closingSequenceRange = [range[1] - trailingSpacesLength - closingSequenceText.length, range[1] - trailingSpacesLength];
1441
+ return {
1442
+ openingSequence,
1443
+ info,
1444
+ closingSequence: {
1445
+ text: closingSequenceText,
1446
+ range: closingSequenceRange,
1447
+ loc: getSourceLocationFromRange(sourceCode, node, closingSequenceRange)
1448
+ }
1449
+ };
1450
+ }
1451
+
1452
+ //#endregion
1453
+ //#region src/rules/custom-container-marker-spacing.ts
1454
+ var custom_container_marker_spacing_default = createRule("custom-container-marker-spacing", {
1455
+ meta: {
1456
+ type: "layout",
1457
+ docs: {
1458
+ description: "require or disallow spacing between opening custom container marker and info",
1459
+ categories: [],
1460
+ listCategory: "Whitespace"
1461
+ },
1462
+ fixable: "whitespace",
1463
+ hasSuggestions: false,
1464
+ schema: [{
1465
+ type: "object",
1466
+ properties: { space: { enum: ["always", "never"] } },
1467
+ additionalProperties: false
1468
+ }],
1469
+ messages: {
1470
+ expectedSpace: "Expected a space between opening custom container marker and info.",
1471
+ unexpectedSpace: "Unexpected space between opening custom container marker and info."
1472
+ }
1473
+ },
1474
+ create(context) {
1475
+ const sourceCode = context.sourceCode;
1476
+ const space = (context.options[0] || {}).space || "always";
1477
+ return { customContainer(node) {
1478
+ const parsed = parseCustomContainer(sourceCode, node);
1479
+ if (!parsed) return;
1480
+ const { openingSequence, info } = parsed;
1481
+ const hasSpace = openingSequence.range[1] < info.range[0];
1482
+ if (space === "always") {
1483
+ if (hasSpace) return;
1484
+ context.report({
1485
+ node,
1486
+ loc: getSourceLocationFromRange(sourceCode, node, info.range),
1487
+ messageId: "expectedSpace",
1488
+ fix(fixer) {
1489
+ return fixer.insertTextAfterRange(openingSequence.range, " ");
1490
+ }
1491
+ });
1492
+ } else if (space === "never") {
1493
+ if (!hasSpace) return;
1494
+ context.report({
1495
+ node,
1496
+ loc: getSourceLocationFromRange(sourceCode, node, [openingSequence.range[1], info.range[0]]),
1497
+ messageId: "unexpectedSpace",
1498
+ fix(fixer) {
1499
+ return fixer.removeRange([openingSequence.range[1], info.range[0]]);
1500
+ }
1501
+ });
1502
+ }
1503
+ } };
1504
+ }
1505
+ });
1506
+
1330
1507
  //#endregion
1331
1508
  //#region src/rules/definitions-last.ts
1332
1509
  /**
@@ -7545,72 +7722,6 @@ var list_marker_alignment_default = createRule("list-marker-alignment", {
7545
7722
  }
7546
7723
  });
7547
7724
 
7548
- //#endregion
7549
- //#region src/utils/custom-container.ts
7550
- const RE_OPENING_SEQUENCE = /^(:{3,})/u;
7551
- /**
7552
- * Parse the custom container.
7553
- */
7554
- function parseCustomContainer(sourceCode, node) {
7555
- const loc = sourceCode.getLoc(node);
7556
- const range = sourceCode.getRange(node);
7557
- const text = sourceCode.text.slice(...range);
7558
- const match = RE_OPENING_SEQUENCE.exec(text);
7559
- if (!match) return null;
7560
- const [, sequenceText] = match;
7561
- const afterOpeningSequence = sourceCode.lines[loc.start.line - 1].slice(loc.start.column - 1 + sequenceText.length);
7562
- const trimmedAfterOpeningSequence = afterOpeningSequence.trimStart();
7563
- const spaceAfterOpeningSequenceLength = afterOpeningSequence.length - trimmedAfterOpeningSequence.length;
7564
- const infoText = trimmedAfterOpeningSequence.trimEnd();
7565
- if (!infoText) return null;
7566
- const openingSequenceRange = [range[0], range[0] + sequenceText.length];
7567
- const openingSequence = {
7568
- text: sequenceText,
7569
- range: openingSequenceRange,
7570
- loc: getSourceLocationFromRange(sourceCode, node, openingSequenceRange)
7571
- };
7572
- const infoRange = [openingSequence.range[1] + spaceAfterOpeningSequenceLength, openingSequence.range[1] + spaceAfterOpeningSequenceLength + infoText.length];
7573
- const info = {
7574
- text: infoText,
7575
- range: infoRange,
7576
- loc: getSourceLocationFromRange(sourceCode, node, infoRange)
7577
- };
7578
- const sequenceChar = sequenceText[0];
7579
- let closingSequenceText = "";
7580
- const trimmed = text.trimEnd();
7581
- const trailingSpacesLength = text.length - trimmed.length;
7582
- for (let index = trimmed.length - 1; index >= 0; index--) {
7583
- const c = trimmed[index];
7584
- if (c === sequenceChar || isSpaceOrTab(c)) {
7585
- closingSequenceText = c + closingSequenceText;
7586
- continue;
7587
- }
7588
- if (c === ">") {
7589
- closingSequenceText = ` ${closingSequenceText}`;
7590
- continue;
7591
- }
7592
- if (c === "\n") break;
7593
- closingSequenceText = "";
7594
- break;
7595
- }
7596
- closingSequenceText = closingSequenceText.trimStart();
7597
- if (!closingSequenceText || !closingSequenceText.startsWith(sequenceText)) return {
7598
- openingSequence,
7599
- info,
7600
- closingSequence: null
7601
- };
7602
- const closingSequenceRange = [range[1] - trailingSpacesLength - closingSequenceText.length, range[1] - trailingSpacesLength];
7603
- return {
7604
- openingSequence,
7605
- info,
7606
- closingSequence: {
7607
- text: closingSequenceText,
7608
- range: closingSequenceRange,
7609
- loc: getSourceLocationFromRange(sourceCode, node, closingSequenceRange)
7610
- }
7611
- };
7612
- }
7613
-
7614
7725
  //#endregion
7615
7726
  //#region src/rules/no-implicit-block-closing.ts
7616
7727
  var no_implicit_block_closing_default = createRule("no-implicit-block-closing", {
@@ -11543,7 +11654,9 @@ const rules$1 = [
11543
11654
  bullet_list_marker_style_default,
11544
11655
  canonical_code_block_language_default,
11545
11656
  code_fence_length_default,
11657
+ code_fence_spacing_default,
11546
11658
  code_fence_style_default,
11659
+ custom_container_marker_spacing_default,
11547
11660
  definitions_last_default,
11548
11661
  emoji_notation_default,
11549
11662
  emphasis_delimiters_style_default,
@@ -11688,7 +11801,7 @@ var meta_exports = /* @__PURE__ */ __export({
11688
11801
  version: () => version
11689
11802
  });
11690
11803
  const name = "eslint-plugin-markdown-preferences";
11691
- const version = "0.29.1";
11804
+ const version = "0.30.0";
11692
11805
 
11693
11806
  //#endregion
11694
11807
  //#region src/language/extensions/micromark-custom-container.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-markdown-preferences",
3
- "version": "0.29.1",
3
+ "version": "0.30.0",
4
4
  "description": "ESLint plugin that enforces our markdown preferences",
5
5
  "type": "module",
6
6
  "exports": {