eslint-plugin-markdown-preferences 0.7.0 → 0.9.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/lib/index.js CHANGED
@@ -25,6 +25,98 @@ function createRule(ruleName, rule) {
25
25
  };
26
26
  }
27
27
 
28
+ //#endregion
29
+ //#region src/rules/canonical-code-block-language.ts
30
+ const DEFAULT_LANGUAGES = {
31
+ javascript: "js",
32
+ jsx: "js",
33
+ mjs: "js",
34
+ cjs: "js",
35
+ typescript: "ts",
36
+ tsx: "ts",
37
+ mts: "ts",
38
+ cts: "ts",
39
+ python: "py",
40
+ bash: "sh",
41
+ shell: "sh",
42
+ zsh: "sh",
43
+ yml: "yaml",
44
+ markdown: "md",
45
+ rust: "rs",
46
+ golang: "go",
47
+ cplusplus: "cpp",
48
+ "c++": "cpp",
49
+ postgresql: "sql",
50
+ mysql: "sql",
51
+ sqlite: "sql"
52
+ };
53
+ var canonical_code_block_language_default = createRule("canonical-code-block-language", {
54
+ meta: {
55
+ type: "suggestion",
56
+ docs: {
57
+ description: "enforce canonical language names in code blocks",
58
+ categories: [],
59
+ listCategory: "Stylistic"
60
+ },
61
+ fixable: "code",
62
+ hasSuggestions: false,
63
+ schema: [{
64
+ type: "object",
65
+ properties: { languages: {
66
+ type: "object",
67
+ patternProperties: { "^[\\s\\S]+$": { type: "string" } },
68
+ additionalProperties: false
69
+ } },
70
+ additionalProperties: false
71
+ }],
72
+ messages: { useCanonical: "Use canonical language name \"{{canonical}}\" instead of \"{{current}}\"." }
73
+ },
74
+ create(context) {
75
+ const sourceCode = context.sourceCode;
76
+ const languages = context.options[0]?.languages || DEFAULT_LANGUAGES;
77
+ return { code(node) {
78
+ if (!node.lang || !languages[node.lang]) return;
79
+ const canonical = languages[node.lang];
80
+ const current = node.lang;
81
+ if (current === canonical) return;
82
+ const nodeRange = sourceCode.getRange(node);
83
+ const nodeText = sourceCode.text.slice(nodeRange[0], nodeRange[1]);
84
+ const fenceRegex = /^(`{3,}|~{3,})(\w*)(?:\s.*)?$/mu;
85
+ const fenceMatch = fenceRegex.exec(nodeText.split("\n")[0]);
86
+ if (!fenceMatch) return;
87
+ const [, fence, langInfo] = fenceMatch;
88
+ const langStart = nodeRange[0] + fence.length;
89
+ const langEnd = langStart + langInfo.length;
90
+ const nodeLoc = sourceCode.getLoc(node);
91
+ const beforeFence = sourceCode.text.slice(nodeRange[0], langStart);
92
+ const beforeLines = beforeFence.split(/\n/u);
93
+ const line = nodeLoc.start.line + beforeLines.length - 1;
94
+ const column = (beforeLines.length === 1 ? nodeLoc.start.column : 0) + (beforeLines.at(-1) || "").length;
95
+ context.report({
96
+ node,
97
+ loc: {
98
+ start: {
99
+ line,
100
+ column
101
+ },
102
+ end: {
103
+ line,
104
+ column: column + langInfo.length
105
+ }
106
+ },
107
+ messageId: "useCanonical",
108
+ data: {
109
+ canonical,
110
+ current
111
+ },
112
+ fix(fixer) {
113
+ return fixer.replaceTextRange([langStart, langEnd], canonical);
114
+ }
115
+ });
116
+ } };
117
+ }
118
+ });
119
+
28
120
  //#endregion
29
121
  //#region src/rules/definitions-last.ts
30
122
  var definitions_last_default = createRule("definitions-last", {
@@ -32,7 +124,8 @@ var definitions_last_default = createRule("definitions-last", {
32
124
  type: "layout",
33
125
  docs: {
34
126
  description: "require link definitions and footnote definitions to be placed at the end of the document",
35
- categories: []
127
+ categories: [],
128
+ listCategory: "Stylistic"
36
129
  },
37
130
  fixable: "code",
38
131
  hasSuggestions: false,
@@ -72,7 +165,8 @@ var hard_linebreak_style_default = createRule("hard-linebreak-style", {
72
165
  type: "layout",
73
166
  docs: {
74
167
  description: "enforce consistent hard linebreak style.",
75
- categories: ["recommended"]
168
+ categories: ["recommended"],
169
+ listCategory: "Stylistic"
76
170
  },
77
171
  fixable: "code",
78
172
  hasSuggestions: false,
@@ -109,6 +203,781 @@ var hard_linebreak_style_default = createRule("hard-linebreak-style", {
109
203
  }
110
204
  });
111
205
 
206
+ //#endregion
207
+ //#region src/utils/regexp.ts
208
+ const RE_REGEXP_STR = /^\/(.+)\/([A-Za-z]*)$/u;
209
+ /**
210
+ * Convert a string to the `RegExp`.
211
+ * Normal strings (e.g. `"foo"`) is converted to `/^foo$/` of `RegExp`.
212
+ * Strings like `"/^foo/i"` are converted to `/^foo/i` of `RegExp`.
213
+ *
214
+ * @param {string} string The string to convert.
215
+ * @returns {RegExp} Returns the `RegExp`.
216
+ */
217
+ function toRegExp(string) {
218
+ const parts = RE_REGEXP_STR.exec(string);
219
+ if (parts) return new RegExp(parts[1], parts[2]);
220
+ return { test: (s) => s === string };
221
+ }
222
+ /**
223
+ * Checks whether given string is regexp string
224
+ * @param {string} string
225
+ * @returns {boolean}
226
+ */
227
+ function isRegExp(string) {
228
+ return Boolean(RE_REGEXP_STR.test(string));
229
+ }
230
+
231
+ //#endregion
232
+ //#region src/resources/preserve-words.ts
233
+ const defaultPreserveWords = [
234
+ "JavaScript",
235
+ "TypeScript",
236
+ "ECMAScript",
237
+ "ES6",
238
+ ...(function* () {
239
+ const end = (/* @__PURE__ */ new Date()).getFullYear() + 3;
240
+ for (let i = 2015; i <= end; i++) yield `ES${i}`;
241
+ })(),
242
+ "PHP",
243
+ "Python",
244
+ "Java",
245
+ "C#",
246
+ "C++",
247
+ "Rust",
248
+ "Go",
249
+ "go",
250
+ "Swift",
251
+ "Kotlin",
252
+ "Dart",
253
+ "Ruby",
254
+ "Scala",
255
+ "Perl",
256
+ "R",
257
+ "MATLAB",
258
+ "Lua",
259
+ "Haskell",
260
+ "Elixir",
261
+ "Clojure",
262
+ "F#",
263
+ "OCaml",
264
+ "Zig",
265
+ "V",
266
+ "Nim",
267
+ "Crystal",
268
+ "Gleam",
269
+ "Odin",
270
+ "Carbon",
271
+ "HTML",
272
+ "CSS",
273
+ "Sass",
274
+ "SCSS",
275
+ "Less",
276
+ "Stylus",
277
+ "JSON",
278
+ "XML",
279
+ "PDF",
280
+ "CSV",
281
+ "YAML",
282
+ "TOML",
283
+ "Markdown",
284
+ "LaTeX",
285
+ "Parquet",
286
+ "Avro",
287
+ "Protobuf",
288
+ "MessagePack",
289
+ "BSON",
290
+ "HDF5",
291
+ "Apache Arrow",
292
+ "ORC",
293
+ "SQL",
294
+ "GraphQL",
295
+ "React",
296
+ "Vue",
297
+ "Angular",
298
+ "Redux",
299
+ "Svelte",
300
+ "SvelteKit",
301
+ "Preact",
302
+ "Solid",
303
+ "Alpine.js",
304
+ "Lit",
305
+ "Stencil",
306
+ "Ember.js",
307
+ "Backbone.js",
308
+ "jQuery",
309
+ "D3.js",
310
+ "Three.js",
311
+ "Chart.js",
312
+ "Plotly",
313
+ "Astro",
314
+ "Remix",
315
+ "Qwik",
316
+ "SolidJS",
317
+ "Vike",
318
+ "Node.js",
319
+ "Deno",
320
+ "Bun",
321
+ "Electron",
322
+ "Tauri",
323
+ "Next.js",
324
+ "Nuxt.js",
325
+ "Gatsby",
326
+ "Express.js",
327
+ "NestJS",
328
+ "FastAPI",
329
+ "Django",
330
+ "Flask",
331
+ "Ruby on Rails",
332
+ "Spring Boot",
333
+ "Laravel",
334
+ "Phoenix",
335
+ "Actix",
336
+ "Axum",
337
+ "Rocket",
338
+ "Flutter",
339
+ "React Native",
340
+ "Ionic",
341
+ "Xamarin",
342
+ "Expo",
343
+ "Unity",
344
+ "Unreal Engine",
345
+ "ESLint",
346
+ "Prettier",
347
+ "Biome",
348
+ "oxc",
349
+ "swc",
350
+ "Webpack",
351
+ "Vite",
352
+ "Babel",
353
+ "Workbox",
354
+ "Rollup",
355
+ "Parcel",
356
+ "esbuild",
357
+ "Turbo",
358
+ "Turborepo",
359
+ "Nx",
360
+ "Lerna",
361
+ "Rush",
362
+ "npm",
363
+ "yarn",
364
+ "pnpm",
365
+ "bun",
366
+ "bower",
367
+ "composer",
368
+ "pip",
369
+ "conda",
370
+ "Maven",
371
+ "Gradle",
372
+ "SBT",
373
+ "Cargo",
374
+ "homebrew",
375
+ "chocolatey",
376
+ "CocoaPods",
377
+ "Carthage",
378
+ "Swift Package Manager",
379
+ "VS Code",
380
+ "Visual Studio Code",
381
+ "Vim",
382
+ "Neovim",
383
+ "Emacs",
384
+ "Sublime Text",
385
+ "Atom",
386
+ "Brackets",
387
+ "brackets",
388
+ "Visual Studio",
389
+ "IntelliJ IDEA",
390
+ "WebStorm",
391
+ "PHPStorm",
392
+ "PyCharm",
393
+ "Android Studio",
394
+ "Xcode",
395
+ "Docker",
396
+ "Kubernetes",
397
+ "Helm",
398
+ "CI / CD",
399
+ "DevOps",
400
+ "GitOps",
401
+ "IaC",
402
+ "Infrastructure as Code",
403
+ "SaaS",
404
+ "PaaS",
405
+ "IaaS",
406
+ "CDN",
407
+ "Load Balancer",
408
+ "API Gateway",
409
+ "Microservices",
410
+ "Serverless",
411
+ "Lambda",
412
+ "Cloud Functions",
413
+ "Container Registry",
414
+ "Prometheus",
415
+ "Grafana",
416
+ "Terraform",
417
+ "Ansible",
418
+ "Jenkins",
419
+ "GitHub Actions",
420
+ "GitLab CI",
421
+ "CircleCI",
422
+ "Travis CI",
423
+ "Azure DevOps",
424
+ "TeamCity",
425
+ "Bamboo",
426
+ "Buildkite",
427
+ "Drone CI",
428
+ "AWS",
429
+ "Amazon Web Services",
430
+ "Azure",
431
+ "GCP",
432
+ "Google Cloud Platform",
433
+ "Heroku",
434
+ "Vercel",
435
+ "Netlify",
436
+ "Railway",
437
+ "Render",
438
+ "Fly.io",
439
+ "Cloudflare",
440
+ "DigitalOcean",
441
+ "API",
442
+ "APIs",
443
+ "REST",
444
+ "RESTful",
445
+ "gRPC",
446
+ "HTTP",
447
+ "HTTPS",
448
+ "WebSocket",
449
+ "SOAP",
450
+ "XML-RPC",
451
+ "JSON-RPC",
452
+ "CLI",
453
+ "SDK",
454
+ "URL",
455
+ "URI",
456
+ "UUID",
457
+ "GUID",
458
+ "CRUD",
459
+ "ACID",
460
+ "BASE",
461
+ "CAP Theorem",
462
+ "JWT",
463
+ "OAuth",
464
+ "OAuth2",
465
+ "OpenID Connect",
466
+ "SAML",
467
+ "SSO",
468
+ "MFA",
469
+ "2FA",
470
+ "CORS",
471
+ "CSRF",
472
+ "XSS",
473
+ "SSH",
474
+ "FTP",
475
+ "SFTP",
476
+ "SMTP",
477
+ "IMAP",
478
+ "POP3",
479
+ "TCP",
480
+ "UDP",
481
+ "IP",
482
+ "IPv4",
483
+ "IPv6",
484
+ "DNS",
485
+ "DHCP",
486
+ "VPN",
487
+ "SSL",
488
+ "TLS",
489
+ "LDAP",
490
+ "PostgreSQL",
491
+ "MySQL",
492
+ "SQLite",
493
+ "MariaDB",
494
+ "Oracle",
495
+ "SQL Server",
496
+ "CockroachDB",
497
+ "PlanetScale",
498
+ "Neon",
499
+ "NoSQL",
500
+ "MongoDB",
501
+ "DynamoDB",
502
+ "Cassandra",
503
+ "CouchDB",
504
+ "Neo4j",
505
+ "ArangoDB",
506
+ "FaunaDB",
507
+ "Firebase",
508
+ "Supabase",
509
+ "Redis",
510
+ "ElasticSearch",
511
+ "Solr",
512
+ "InfluxDB",
513
+ "TimescaleDB",
514
+ "Prisma",
515
+ "TypeORM",
516
+ "Sequelize",
517
+ "Mongoose",
518
+ "Drizzle",
519
+ "Knex.js",
520
+ "Objection.js",
521
+ "Bookshelf.js",
522
+ "QA",
523
+ "QC",
524
+ "TDD",
525
+ "BDD",
526
+ "E2E",
527
+ "Unit Testing",
528
+ "Integration Testing",
529
+ "Jest",
530
+ "Mocha",
531
+ "Chai",
532
+ "Jasmine",
533
+ "Karma",
534
+ "Vitest",
535
+ "Ava",
536
+ "Tape",
537
+ "Cypress",
538
+ "Playwright",
539
+ "Selenium",
540
+ "Puppeteer",
541
+ "WebDriver",
542
+ "TestCafe",
543
+ "SonarQube",
544
+ "Husky",
545
+ "lint-staged",
546
+ "commitizen",
547
+ "semantic-release",
548
+ "Codecov",
549
+ "CodeClimate",
550
+ "TensorFlow",
551
+ "PyTorch",
552
+ "Keras",
553
+ "Scikit-learn",
554
+ "Pandas",
555
+ "NumPy",
556
+ "OpenCV",
557
+ "Hugging Face",
558
+ "LangChain",
559
+ "OpenAI",
560
+ "Anthropic",
561
+ "Jupyter",
562
+ "MLflow",
563
+ "Weights & Biases",
564
+ "CUDA",
565
+ "ONNX",
566
+ "GPT",
567
+ "BERT",
568
+ "Transformer",
569
+ "Claude",
570
+ "Gemini",
571
+ "LLaMA",
572
+ "Stable Diffusion",
573
+ "DALL-E",
574
+ "Midjourney",
575
+ "AutoML",
576
+ "ETL",
577
+ "ELT",
578
+ "Big Data",
579
+ "Data Lake",
580
+ "Data Warehouse",
581
+ "OLAP",
582
+ "OLTP",
583
+ "Apache Spark",
584
+ "Apache Kafka",
585
+ "Apache Airflow",
586
+ "Hadoop",
587
+ "Snowflake",
588
+ "Databricks",
589
+ "Tableau",
590
+ "Power BI",
591
+ "Looker",
592
+ "OWASP",
593
+ "SAST",
594
+ "DAST",
595
+ "IAST",
596
+ "SCA",
597
+ "Penetration Testing",
598
+ "Vulnerability Assessment",
599
+ "RBAC",
600
+ "ABAC",
601
+ "Zero Trust",
602
+ "PKI",
603
+ "HSM",
604
+ "WAF",
605
+ "DDoS",
606
+ "PWA",
607
+ "SPA",
608
+ "SSR",
609
+ "SSG",
610
+ "CSR",
611
+ "JAMstack",
612
+ "Headless CMS",
613
+ "Edge Computing",
614
+ "WebAssembly",
615
+ "WASM",
616
+ "Service Worker",
617
+ "Web Components",
618
+ "Micro Frontends",
619
+ "BFF",
620
+ "Backend for Frontend",
621
+ "GraphQL",
622
+ "tRPC",
623
+ "gRPC-Web",
624
+ "WebRTC",
625
+ "WebGL",
626
+ "WebGPU",
627
+ "Material UI",
628
+ "Ant Design",
629
+ "Chakra UI",
630
+ "React Bootstrap",
631
+ "Semantic UI React",
632
+ "Blueprint",
633
+ "Mantine",
634
+ "NextUI",
635
+ "Arco Design",
636
+ "Tailwind CSS",
637
+ "Bootstrap",
638
+ "Bulma",
639
+ "Foundation",
640
+ "Semantic UI",
641
+ "Materialize",
642
+ "Spectre.css",
643
+ "Tachyons",
644
+ "PureCSS",
645
+ "styled-components",
646
+ "CSS-in-JS",
647
+ "Emotion",
648
+ "JSS",
649
+ "Styled System",
650
+ "Stitches",
651
+ "Vanilla Extract",
652
+ "Linaria",
653
+ "Aphrodite",
654
+ "Glamorous",
655
+ "Radium",
656
+ "Git",
657
+ "Mercurial",
658
+ "SVN",
659
+ "GitHub",
660
+ "GitLab",
661
+ "Bitbucket",
662
+ "Pull Request",
663
+ "Merge Request",
664
+ "Code Review",
665
+ "Pair Programming",
666
+ "Mob Programming",
667
+ "Docusaurus",
668
+ "GitBook",
669
+ "VitePress",
670
+ "VuePress",
671
+ "Docsify",
672
+ "MkDocs",
673
+ "Sphinx",
674
+ "Jekyll",
675
+ "Hugo",
676
+ "Eleventy",
677
+ "Hexo",
678
+ "Zola",
679
+ "Swagger",
680
+ "OpenAPI",
681
+ "Postman",
682
+ "Insomnia",
683
+ "Redoc",
684
+ "Stoplight",
685
+ "FAQ"
686
+ ];
687
+
688
+ //#endregion
689
+ //#region src/resources/minor-words.ts
690
+ const articles = [
691
+ "a",
692
+ "an",
693
+ "the"
694
+ ];
695
+ const conjunctions = [
696
+ "for",
697
+ "and",
698
+ "nor",
699
+ "but",
700
+ "or",
701
+ "yet",
702
+ "so"
703
+ ];
704
+ const prepositions = [
705
+ "a",
706
+ "as",
707
+ "at",
708
+ "by",
709
+ "ex",
710
+ "in",
711
+ "of",
712
+ "on",
713
+ "re",
714
+ "to",
715
+ "up"
716
+ ];
717
+ const defaultMinorWords = [
718
+ ...articles,
719
+ ...conjunctions,
720
+ ...prepositions
721
+ ];
722
+
723
+ //#endregion
724
+ //#region src/rules/heading-casing.ts
725
+ /**
726
+ * Parse preserve words and phrases from the options
727
+ * - Single words are added to preserveWords
728
+ * - Multi-word phrases are added to preservePhrases and added to preserveWords with no spaces
729
+ */
730
+ function parsePreserveWords(preserveWordsOption) {
731
+ const preserveWords = /* @__PURE__ */ new Map();
732
+ /**
733
+ * Add a single word to the preserveWords map
734
+ */
735
+ function addPreserveWord(word) {
736
+ const lowerWord = word.toLowerCase();
737
+ let list = preserveWords.get(lowerWord);
738
+ if (!list) {
739
+ list = [];
740
+ preserveWords.set(lowerWord, list);
741
+ }
742
+ list.push(word);
743
+ }
744
+ const preservePhrases = /* @__PURE__ */ new Map();
745
+ for (const word of preserveWordsOption) {
746
+ const splitted = word.split(/\s+/);
747
+ if (splitted.length <= 1) addPreserveWord(word);
748
+ else {
749
+ preservePhrases.set(word, splitted);
750
+ addPreserveWord(splitted.join(""));
751
+ }
752
+ }
753
+ return {
754
+ preserveWords,
755
+ preservePhrases: [...preservePhrases.values()].sort((a, b) => b.length - a.length)
756
+ };
757
+ }
758
+ /**
759
+ * Parse text into words with offsets
760
+ */
761
+ function parseText(text, firstNode, lastNode) {
762
+ const words = [];
763
+ const pattern = /(\w+(?:[^\s\w]\w+)*|:\w+:|[^\s\w]+|\s+)/gu;
764
+ let match;
765
+ while ((match = pattern.exec(text)) !== null) {
766
+ const token = match[1];
767
+ if (/^\s+$/.test(token)) continue;
768
+ const punctuation = /^(?::\w+:|[^\s\w]+)$/.test(token);
769
+ words.push({
770
+ word: token,
771
+ offset: match.index,
772
+ punctuation,
773
+ first: false,
774
+ last: false
775
+ });
776
+ }
777
+ if (firstNode) {
778
+ for (const w of words) if (!w.punctuation) {
779
+ w.first = true;
780
+ break;
781
+ }
782
+ }
783
+ if (lastNode) for (let i = words.length - 1; i >= 0; i--) {
784
+ const w = words[i];
785
+ if (!w.punctuation) {
786
+ w.last = true;
787
+ break;
788
+ }
789
+ }
790
+ return words;
791
+ }
792
+ /**
793
+ * Convert a single word based on case style and context
794
+ */
795
+ function convertWord({ word, first, last }, caseStyle, minorWords) {
796
+ if (caseStyle === "Title Case") {
797
+ if (first || last) return {
798
+ word: word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),
799
+ isMinorWord: false
800
+ };
801
+ if (minorWords.some((minorWord) => minorWord.toLowerCase() === word.toLowerCase())) return {
802
+ word: word.toLowerCase(),
803
+ isMinorWord: true
804
+ };
805
+ return {
806
+ word: word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),
807
+ isMinorWord: false
808
+ };
809
+ }
810
+ if (first) return {
811
+ word: word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),
812
+ isMinorWord: false
813
+ };
814
+ return {
815
+ word: word.toLowerCase(),
816
+ isMinorWord: false
817
+ };
818
+ }
819
+ var heading_casing_default = createRule("heading-casing", {
820
+ meta: {
821
+ type: "suggestion",
822
+ docs: {
823
+ description: "enforce consistent casing in headings.",
824
+ categories: [],
825
+ listCategory: "Stylistic"
826
+ },
827
+ fixable: "code",
828
+ hasSuggestions: false,
829
+ schema: [{
830
+ type: "object",
831
+ properties: {
832
+ style: { enum: ["Title Case", "Sentence case"] },
833
+ preserveWords: {
834
+ type: "array",
835
+ items: { type: "string" },
836
+ description: "Words that should be preserved as-is (case-insensitive matching)"
837
+ },
838
+ ignorePatterns: {
839
+ type: "array",
840
+ items: { type: "string" },
841
+ description: "Regular expression patterns for words to ignore during casing checks"
842
+ },
843
+ minorWords: {
844
+ type: "array",
845
+ items: { type: "string" },
846
+ description: "Words that should not be capitalized in Title Case (unless they're the first or last word)"
847
+ }
848
+ },
849
+ additionalProperties: false
850
+ }],
851
+ messages: {
852
+ expectedTitleCase: "Expected \"{{actual}}\" to be \"{{expected}}\" (Title Case).",
853
+ expectedTitleCaseMinorWord: "Expected \"{{actual}}\" to be \"{{expected}}\" (Title Case - minor word).",
854
+ expectedSentenceCase: "Expected \"{{actual}}\" to be \"{{expected}}\" (Sentence case).",
855
+ expectedPreserveWord: "Expected \"{{actual}}\" to be \"{{expected}}\" (preserved word)."
856
+ }
857
+ },
858
+ create(context) {
859
+ const sourceCode = context.sourceCode;
860
+ const caseStyle = context.options[0]?.style || "Title Case";
861
+ const { preserveWords, preservePhrases } = parsePreserveWords(context.options[0]?.preserveWords || defaultPreserveWords);
862
+ const minorWords = context.options[0]?.minorWords || defaultMinorWords;
863
+ const ignorePatterns = (context.options[0]?.ignorePatterns || [
864
+ "/^v\\d+/u",
865
+ "/\\w+\\.[a-z\\d]+$/u",
866
+ "/\\w*(?:API|Api)$/u",
867
+ "/\\w*(?:SDK|Sdk)$/u",
868
+ "/\\w*(?:CLI|Cli)$/u"
869
+ ]).map((pattern) => {
870
+ if (isRegExp(pattern)) return toRegExp(pattern);
871
+ try {
872
+ return new RegExp(pattern, "v");
873
+ } catch {}
874
+ try {
875
+ return new RegExp(pattern, "u");
876
+ } catch {}
877
+ return new RegExp(pattern);
878
+ });
879
+ /**
880
+ * Check text node and report word-level errors
881
+ */
882
+ function checkTextNode(node, firstNode, lastNode) {
883
+ const text = sourceCode.getText(node);
884
+ const wordAndOffsets = parseText(text, firstNode, lastNode);
885
+ const processed = /* @__PURE__ */ new Set();
886
+ for (let index = 0; index < wordAndOffsets.length; index++) {
887
+ if (processed.has(index)) continue;
888
+ processed.add(index);
889
+ const wordAndOffset = wordAndOffsets[index];
890
+ if (wordAndOffset.punctuation) continue;
891
+ if (ignorePatterns.some((pattern) => pattern.test(wordAndOffset.word))) continue;
892
+ const preservePhrase = findPreservePhrase(wordAndOffsets, index);
893
+ if (preservePhrase) {
894
+ for (let wordIndex = 0; wordIndex < preservePhrase.length; wordIndex++) {
895
+ processed.add(index + wordIndex);
896
+ verifyWord(wordAndOffsets[index + wordIndex], preservePhrase[wordIndex], "preserved");
897
+ }
898
+ continue;
899
+ }
900
+ const preserveWordList = preserveWords.get(wordAndOffset.word.toLowerCase());
901
+ if (preserveWordList) {
902
+ if (!preserveWordList.some((w) => w === wordAndOffset.word)) verifyWord(wordAndOffset, preserveWordList[0], "preserved");
903
+ continue;
904
+ }
905
+ const expectedWordResult = convertWord(wordAndOffset, caseStyle, minorWords);
906
+ verifyWord(wordAndOffset, expectedWordResult.word, expectedWordResult.isMinorWord ? "minor" : "normal");
907
+ }
908
+ /**
909
+ * Verify a single word against the expected casing
910
+ */
911
+ function verifyWord(wordAndOffset, expectedWord, wordType = "normal") {
912
+ const { word, offset } = wordAndOffset;
913
+ if (word === expectedWord) return;
914
+ const nodeLoc = sourceCode.getLoc(node);
915
+ const beforeLines = text.slice(0, offset).split(/\n/u);
916
+ const line = nodeLoc.start.line + beforeLines.length - 1;
917
+ const column = (beforeLines.length === 1 ? nodeLoc.start.column : 1) + (beforeLines.at(-1) || "").length;
918
+ const nodeRange = sourceCode.getRange(node);
919
+ const wordRange = [nodeRange[0] + offset, nodeRange[0] + offset + word.length];
920
+ context.report({
921
+ node,
922
+ messageId: wordType === "preserved" ? "expectedPreserveWord" : caseStyle === "Title Case" ? wordType === "minor" ? "expectedTitleCaseMinorWord" : "expectedTitleCase" : "expectedSentenceCase",
923
+ data: {
924
+ actual: word,
925
+ expected: expectedWord
926
+ },
927
+ loc: {
928
+ start: {
929
+ line,
930
+ column
931
+ },
932
+ end: {
933
+ line,
934
+ column: column + word.length
935
+ }
936
+ },
937
+ fix(fixer) {
938
+ return fixer.replaceTextRange(wordRange, expectedWord);
939
+ }
940
+ });
941
+ }
942
+ }
943
+ /**
944
+ * Find a preserve phrase starting from the given index
945
+ * Returns the longest matching phrase or null if no match is found
946
+ */
947
+ function findPreservePhrase(wordAndOffsets, index) {
948
+ const firstWord = wordAndOffsets[index];
949
+ if (firstWord.punctuation) return null;
950
+ const firstLowerWord = firstWord.word.toLowerCase();
951
+ let returnCandidate = null;
952
+ let subWords = null;
953
+ for (const phrase of preservePhrases) {
954
+ if (returnCandidate && returnCandidate.preservePhrase.length !== phrase.length) break;
955
+ if (firstLowerWord !== phrase[0].toLowerCase()) continue;
956
+ if (!subWords || subWords.length !== phrase.length) subWords = wordAndOffsets.slice(index, index + phrase.length).map((wo) => wo.word);
957
+ if (subWords.length === phrase.length && subWords.every((word, i) => word.toLowerCase() === phrase[i].toLowerCase())) {
958
+ let matchCount = 0;
959
+ for (let i = 0; i < subWords.length; i++) {
960
+ const word = subWords[i];
961
+ if (word === phrase[i]) matchCount++;
962
+ }
963
+ if (!returnCandidate || matchCount > returnCandidate.matchCount) returnCandidate = {
964
+ preservePhrase: phrase,
965
+ matchCount
966
+ };
967
+ }
968
+ }
969
+ return returnCandidate?.preservePhrase ?? null;
970
+ }
971
+ return { heading(node) {
972
+ if (!node.children.length) return;
973
+ const children = node.children.filter((child) => child.type !== "text" || child.value.trim());
974
+ children.forEach((child, i) => {
975
+ if (child.type === "text") checkTextNode(child, i === 0, i === node.children.length - 1);
976
+ });
977
+ } };
978
+ }
979
+ });
980
+
112
981
  //#endregion
113
982
  //#region src/rules/no-text-backslash-linebreak.ts
114
983
  var no_text_backslash_linebreak_default = createRule("no-text-backslash-linebreak", {
@@ -116,7 +985,8 @@ var no_text_backslash_linebreak_default = createRule("no-text-backslash-linebrea
116
985
  type: "suggestion",
117
986
  docs: {
118
987
  description: "disallow text backslash at the end of a line.",
119
- categories: ["recommended"]
988
+ categories: ["recommended"],
989
+ listCategory: "Preference"
120
990
  },
121
991
  fixable: void 0,
122
992
  hasSuggestions: true,
@@ -172,7 +1042,8 @@ var no_trailing_spaces_default = createRule("no-trailing-spaces", {
172
1042
  type: "layout",
173
1043
  docs: {
174
1044
  description: "disallow trailing whitespace at the end of lines in Markdown files.",
175
- categories: []
1045
+ categories: [],
1046
+ listCategory: "Stylistic"
176
1047
  },
177
1048
  fixable: "whitespace",
178
1049
  hasSuggestions: false,
@@ -380,7 +1251,8 @@ var prefer_inline_code_words_default = createRule("prefer-inline-code-words", {
380
1251
  type: "suggestion",
381
1252
  docs: {
382
1253
  description: "enforce the use of inline code for specific words.",
383
- categories: []
1254
+ categories: [],
1255
+ listCategory: "Preference"
384
1256
  },
385
1257
  fixable: "code",
386
1258
  hasSuggestions: false,
@@ -450,7 +1322,8 @@ var prefer_link_reference_definitions_default = createRule("prefer-link-referenc
450
1322
  type: "layout",
451
1323
  docs: {
452
1324
  description: "enforce using link reference definitions instead of inline links",
453
- categories: []
1325
+ categories: [],
1326
+ listCategory: "Stylistic"
454
1327
  },
455
1328
  fixable: "code",
456
1329
  hasSuggestions: false,
@@ -472,7 +1345,7 @@ var prefer_link_reference_definitions_default = createRule("prefer-link-referenc
472
1345
  const minLinks = options.minLinks ?? 2;
473
1346
  const definitions = [];
474
1347
  const links = [];
475
- const linkReferences = [];
1348
+ const references = [];
476
1349
  const headings = [];
477
1350
  /**
478
1351
  * Verify links.
@@ -480,13 +1353,13 @@ var prefer_link_reference_definitions_default = createRule("prefer-link-referenc
480
1353
  function verify() {
481
1354
  const resourceToNodes = /* @__PURE__ */ new Map();
482
1355
  for (const link of links) getResourceNodes(link).links.push(link);
483
- for (const linkReference of linkReferences) {
484
- const definition = definitions.find((def) => def.identifier === linkReference.identifier);
485
- if (definition) getResourceNodes(definition).linkReferences.push(linkReference);
1356
+ for (const reference of references) {
1357
+ const definition = definitions.find((def) => def.identifier === reference.identifier);
1358
+ if (definition) getResourceNodes(definition).references.push(reference);
486
1359
  }
487
1360
  for (const definition of definitions) getResourceNodes(definition).definitions.push(definition);
488
1361
  for (const map of resourceToNodes.values()) for (const nodes of map.values()) {
489
- if (nodes.links.length === 0 || nodes.links.length + nodes.linkReferences.length < minLinks) continue;
1362
+ if (nodes.links.length === 0 || nodes.links.length + nodes.references.length < minLinks) continue;
490
1363
  for (const link of nodes.links) {
491
1364
  const linkInfo = getLinkInfo(link);
492
1365
  if (linkInfo.label === "") continue;
@@ -497,8 +1370,16 @@ var prefer_link_reference_definitions_default = createRule("prefer-link-referenc
497
1370
  const definition = nodes.definitions[0];
498
1371
  let identifier;
499
1372
  if (definition) identifier = definition.label ?? definition.identifier;
500
- else identifier = linkInfo.label.replaceAll(/[[\]]/gu, "-");
501
- yield fixer.replaceText(link, `${sourceCode.text.slice(...linkInfo.labelRange)}${identifier === linkInfo.label ? "" : `[${identifier}]`}`);
1373
+ else {
1374
+ identifier = linkInfo.label.replaceAll(/[[\]]/gu, "-");
1375
+ if (definitions.some((def) => def.identifier === identifier)) {
1376
+ let seq = 1;
1377
+ const original = identifier;
1378
+ identifier = `${original}-${seq}`;
1379
+ while (definitions.some((def) => def.identifier === identifier)) identifier = `${original}-${++seq}`;
1380
+ }
1381
+ }
1382
+ yield fixer.replaceText(link, `${sourceCode.text.slice(...linkInfo.bracketsRange)}${identifier === linkInfo.label ? "" : `[${identifier}]`}`);
502
1383
  if (!definition) {
503
1384
  const linkRange = sourceCode.getRange(link);
504
1385
  const nextSectionHeading = headings.find((heading) => linkRange[1] < sourceCode.getRange(heading)[0]);
@@ -529,7 +1410,7 @@ var prefer_link_reference_definitions_default = createRule("prefer-link-referenc
529
1410
  if (!nodes) {
530
1411
  nodes = {
531
1412
  links: [],
532
- linkReferences: [],
1413
+ references: [],
533
1414
  definitions: []
534
1415
  };
535
1416
  map.set(title, nodes);
@@ -539,10 +1420,13 @@ var prefer_link_reference_definitions_default = createRule("prefer-link-referenc
539
1420
  }
540
1421
  return {
541
1422
  link(node) {
1423
+ if (sourceCode.getText(node).startsWith("[")) links.push(node);
1424
+ },
1425
+ image(node) {
542
1426
  links.push(node);
543
1427
  },
544
- linkReference(node) {
545
- linkReferences.push(node);
1428
+ "linkReference, imageReference"(node) {
1429
+ references.push(node);
546
1430
  },
547
1431
  definition(node) {
548
1432
  definitions.push(node);
@@ -559,31 +1443,70 @@ var prefer_link_reference_definitions_default = createRule("prefer-link-referenc
559
1443
  */
560
1444
  function getLinkInfo(link) {
561
1445
  const range = sourceCode.getRange(link);
562
- const linkLabelRange = getLinkLabelRange();
563
- const linkLabelWithBracketsText = sourceCode.text.slice(...linkLabelRange);
564
- const linkLabelText = linkLabelWithBracketsText.slice(1, -1).trim();
565
- const urlStartIndex = sourceCode.text.indexOf("(", linkLabelRange[1]);
1446
+ if (link.type === "link") {
1447
+ const bracketsRange$1 = getLinkBracketsRange(link);
1448
+ const linkBracketsText$1 = sourceCode.text.slice(...bracketsRange$1);
1449
+ const linkLabelText$1 = linkBracketsText$1.slice(1, -1).trim();
1450
+ const urlStartIndex$1 = sourceCode.text.indexOf("(", bracketsRange$1[1]);
1451
+ return {
1452
+ label: linkLabelText$1,
1453
+ bracketsRange: bracketsRange$1,
1454
+ urlAndTitleRange: [urlStartIndex$1, range[1]]
1455
+ };
1456
+ }
1457
+ const bracketsRange = getImageBracketsRange(link);
1458
+ const linkBracketsText = sourceCode.text.slice(...bracketsRange);
1459
+ const linkLabelText = linkBracketsText.slice(1, -1).trim();
1460
+ const urlStartIndex = sourceCode.text.indexOf("(", bracketsRange[1]);
566
1461
  return {
567
1462
  label: linkLabelText,
568
- labelRange: linkLabelRange,
1463
+ bracketsRange,
569
1464
  urlAndTitleRange: [urlStartIndex, range[1]]
570
1465
  };
571
- /**
572
- * Get the range of the link label.
573
- */
574
- function getLinkLabelRange() {
575
- if (link.children.length === 0) {
576
- const index$1 = sourceCode.text.indexOf("]", range[0] + 1);
577
- return [range[0], index$1 + 1];
578
- }
579
- const lastRange = sourceCode.getRange(link.children[link.children.length - 1]);
580
- const index = sourceCode.text.indexOf("]", lastRange[1]);
581
- return [range[0], index + 1];
1466
+ }
1467
+ /**
1468
+ * Get the range of the link label.
1469
+ */
1470
+ function getLinkBracketsRange(link) {
1471
+ const range = sourceCode.getRange(link);
1472
+ if (link.children.length === 0) {
1473
+ const index$1 = sourceCode.text.indexOf("]", range[0] + 1);
1474
+ return [range[0], index$1 + 1];
582
1475
  }
1476
+ const lastRange = sourceCode.getRange(link.children[link.children.length - 1]);
1477
+ const index = sourceCode.text.indexOf("]", lastRange[1]);
1478
+ return [range[0], index + 1];
1479
+ }
1480
+ /**
1481
+ * Get the range of the image label.
1482
+ */
1483
+ function getImageBracketsRange(image) {
1484
+ const range = sourceCode.getRange(image);
1485
+ const index = sourceCode.text.indexOf("]", range[0] + 2);
1486
+ return [range[0] + 1, index + 1];
583
1487
  }
584
1488
  }
585
1489
  });
586
1490
 
1491
+ //#endregion
1492
+ //#region src/utils/url.ts
1493
+ /**
1494
+ * Utility function to check if a string is a valid URL.
1495
+ */
1496
+ function isValidURL(url) {
1497
+ return Boolean(createURLSafe(url));
1498
+ }
1499
+ /**
1500
+ * Utility function to create a URL object safely.
1501
+ */
1502
+ function createURLSafe(url) {
1503
+ try {
1504
+ return new URL(url);
1505
+ } catch {
1506
+ return null;
1507
+ }
1508
+ }
1509
+
587
1510
  //#endregion
588
1511
  //#region src/rules/prefer-linked-words.ts
589
1512
  var prefer_linked_words_default = createRule("prefer-linked-words", {
@@ -591,7 +1514,8 @@ var prefer_linked_words_default = createRule("prefer-linked-words", {
591
1514
  type: "suggestion",
592
1515
  docs: {
593
1516
  description: "enforce the specified word to be a link.",
594
- categories: []
1517
+ categories: [],
1518
+ listCategory: "Preference"
595
1519
  },
596
1520
  fixable: "code",
597
1521
  hasSuggestions: false,
@@ -684,7 +1608,7 @@ var prefer_linked_words_default = createRule("prefer-linked-words", {
684
1608
  * Adjust link to be relative to the file.
685
1609
  */
686
1610
  function adjustLink(link) {
687
- if (/^\w+:/.test(link)) return link;
1611
+ if (isValidURL(link)) return link;
688
1612
  if (link.startsWith("#")) return link;
689
1613
  const absoluteLink = path.isAbsolute(link) || path.posix.isAbsolute(link) ? link : path.join(context.cwd, link);
690
1614
  return `./${path.relative(path.dirname(context.filename), absoluteLink)}`;
@@ -692,16 +1616,302 @@ var prefer_linked_words_default = createRule("prefer-linked-words", {
692
1616
  }
693
1617
  });
694
1618
 
1619
+ //#endregion
1620
+ //#region src/rules/sort-definitions.ts
1621
+ var sort_definitions_default = createRule("sort-definitions", {
1622
+ meta: {
1623
+ type: "layout",
1624
+ docs: {
1625
+ description: "enforce a specific order for link definitions and footnote definitions",
1626
+ categories: [],
1627
+ listCategory: "Stylistic"
1628
+ },
1629
+ fixable: "code",
1630
+ hasSuggestions: false,
1631
+ schema: [{
1632
+ type: "object",
1633
+ properties: {
1634
+ order: {
1635
+ type: "array",
1636
+ items: { anyOf: [
1637
+ { type: "string" },
1638
+ {
1639
+ type: "array",
1640
+ items: { type: "string" },
1641
+ uniqueItems: true,
1642
+ minItems: 1
1643
+ },
1644
+ {
1645
+ type: "object",
1646
+ properties: {
1647
+ match: { anyOf: [{ type: "string" }, {
1648
+ type: "array",
1649
+ items: { type: "string" },
1650
+ uniqueItems: true,
1651
+ minItems: 1
1652
+ }] },
1653
+ sort: { enum: ["alphabetical", "ignore"] }
1654
+ },
1655
+ required: ["match", "sort"],
1656
+ additionalProperties: false
1657
+ }
1658
+ ] },
1659
+ uniqueItems: true,
1660
+ additionalItems: false
1661
+ },
1662
+ alphabetical: { type: "boolean" }
1663
+ },
1664
+ additionalProperties: false
1665
+ }],
1666
+ messages: { shouldBefore: "The definition '{{currentKey}}' should be before '{{prevKey}}'." }
1667
+ },
1668
+ create(context) {
1669
+ const sourceCode = context.sourceCode;
1670
+ const option = parseOption(context.options[0]);
1671
+ const group = [];
1672
+ const cacheText = /* @__PURE__ */ new Map();
1673
+ /** Get normalized text */
1674
+ function getDefinitionText(node) {
1675
+ const k = cacheText.get(node);
1676
+ if (k != null) return k;
1677
+ if (node.type === "definition") return `[${node.label || node.identifier}]: ${node.url}${printTitle(node.title)}`;
1678
+ let childrenText = "";
1679
+ if (node.children.length) {
1680
+ const [start] = sourceCode.getRange(node.children[0]);
1681
+ const [, end] = sourceCode.getRange(node.children.at(-1));
1682
+ childrenText = sourceCode.text.slice(start, end);
1683
+ }
1684
+ return `[^${node.identifier}]: ${childrenText}`;
1685
+ }
1686
+ /** Report */
1687
+ function report(node, previousNode, definitions) {
1688
+ const currentKey = getDefinitionText(node);
1689
+ const prevKey = getDefinitionText(previousNode);
1690
+ context.report({
1691
+ node,
1692
+ messageId: "shouldBefore",
1693
+ data: {
1694
+ currentKey,
1695
+ prevKey
1696
+ },
1697
+ fix(fixer) {
1698
+ const previousNodeIndex = definitions.indexOf(previousNode);
1699
+ const targetNodeIndex = definitions.indexOf(node);
1700
+ const previousNodes = definitions.slice(previousNodeIndex, targetNodeIndex);
1701
+ const before = definitions.slice(0, previousNodeIndex);
1702
+ const after = definitions.slice(targetNodeIndex + 1);
1703
+ const movedNodes = [
1704
+ ...before,
1705
+ node,
1706
+ ...previousNodes,
1707
+ ...after
1708
+ ];
1709
+ return movedNodes.map((moveNode, index) => {
1710
+ let text = sourceCode.getText(moveNode);
1711
+ if (moveNode.type === "definition" && index > 0) {
1712
+ if (movedNodes[index - 1].type === "footnoteDefinition") {
1713
+ const footnoteLoc = sourceCode.getLoc(definitions[index - 1]);
1714
+ const linkLoc = sourceCode.getLoc(definitions[index]);
1715
+ if (linkLoc.start.line - footnoteLoc.end.line <= 1) text = `\n${text}`;
1716
+ }
1717
+ }
1718
+ return fixer.replaceText(definitions[index], text);
1719
+ });
1720
+ }
1721
+ });
1722
+ }
1723
+ /**
1724
+ * Verify definitions and footnote definitions.
1725
+ */
1726
+ function verify(definitions) {
1727
+ if (definitions.length === 0) return;
1728
+ const validPreviousNodes = [];
1729
+ for (const definition of definitions) {
1730
+ if (option.ignore(definition)) continue;
1731
+ const invalidPreviousNode = validPreviousNodes.find((previousNode) => option.compare(previousNode, definition) > 0);
1732
+ if (invalidPreviousNode) {
1733
+ report(definition, invalidPreviousNode, definitions);
1734
+ continue;
1735
+ }
1736
+ validPreviousNodes.push(definition);
1737
+ }
1738
+ }
1739
+ return {
1740
+ "*"(node) {
1741
+ const last = group.at(-1);
1742
+ if (last && (node.type !== "definition" && node.type !== "footnoteDefinition" || sourceCode.getParent(node) !== sourceCode.getParent(last))) {
1743
+ const range = sourceCode.getRange(node);
1744
+ const lastDefinitionRange = sourceCode.getRange(node);
1745
+ if (lastDefinitionRange[1] <= range[0]) {
1746
+ verify(group);
1747
+ group.length = 0;
1748
+ }
1749
+ }
1750
+ if (node.type === "definition" || node.type === "footnoteDefinition") group.push(node);
1751
+ },
1752
+ "root:exit"() {
1753
+ verify(group);
1754
+ }
1755
+ };
1756
+ /** Parse options */
1757
+ function parseOption(userOption) {
1758
+ const order = userOption?.order ?? [{
1759
+ match: String.raw`!/^\[\\^/u`,
1760
+ sort: "alphabetical"
1761
+ }, {
1762
+ match: String.raw`/./u`,
1763
+ sort: "alphabetical"
1764
+ }];
1765
+ const compiled = order.map(compileOption);
1766
+ return {
1767
+ ignore: (node) => {
1768
+ return !compiled.some((c) => c.match(node));
1769
+ },
1770
+ compare: (a, b) => {
1771
+ for (const c of compiled) {
1772
+ const matchA = c.match(a);
1773
+ const matchB = c.match(b);
1774
+ if (matchA && matchB) {
1775
+ if (c.sort === "alphabetical") {
1776
+ const textA = getDefinitionText(a);
1777
+ const textB = getDefinitionText(b);
1778
+ if (textA === textB) return 0;
1779
+ return textA < textB ? -1 : 1;
1780
+ }
1781
+ return 0;
1782
+ }
1783
+ if (matchA) return -1;
1784
+ if (matchB) return 1;
1785
+ }
1786
+ throw new Error("Illegal state");
1787
+ }
1788
+ };
1789
+ }
1790
+ /** Compile order option */
1791
+ function compileOption(orderOption) {
1792
+ const cache = /* @__PURE__ */ new Map();
1793
+ const compiled = compileOptionWithoutCache(orderOption);
1794
+ return {
1795
+ match: (node) => {
1796
+ const cached = cache.get(node);
1797
+ if (cached != null) return cached;
1798
+ const result = compiled.match(node);
1799
+ cache.set(node, result);
1800
+ return result;
1801
+ },
1802
+ sort: compiled.sort
1803
+ };
1804
+ }
1805
+ /** Compile order option without cache */
1806
+ function compileOptionWithoutCache(orderOption) {
1807
+ if (typeof orderOption === "string") {
1808
+ const match$1 = compileMatcher([orderOption]);
1809
+ return {
1810
+ match: match$1,
1811
+ sort: "ignore"
1812
+ };
1813
+ }
1814
+ if (Array.isArray(orderOption)) {
1815
+ const match$1 = compileMatcher(orderOption);
1816
+ return {
1817
+ match: match$1,
1818
+ sort: "ignore"
1819
+ };
1820
+ }
1821
+ const { match } = compileOptionWithoutCache(orderOption.match);
1822
+ return {
1823
+ match,
1824
+ sort: orderOption.sort || "ignore"
1825
+ };
1826
+ }
1827
+ /** Compile matcher */
1828
+ function compileMatcher(pattern) {
1829
+ const rules$3 = [];
1830
+ for (const p of pattern) {
1831
+ let negative, patternStr;
1832
+ if (p.startsWith("!")) {
1833
+ negative = true;
1834
+ patternStr = p.substring(1);
1835
+ } else {
1836
+ negative = false;
1837
+ patternStr = p;
1838
+ }
1839
+ const regex = toRegExp(patternStr);
1840
+ if (isRegExp(patternStr)) rules$3.push({
1841
+ negative,
1842
+ match: (node) => regex.test(getDefinitionText(node))
1843
+ });
1844
+ else rules$3.push({
1845
+ negative,
1846
+ match: (node) => {
1847
+ if (node.label === patternStr || node.identifier === patternStr) return true;
1848
+ if (node.type === "definition") {
1849
+ if (node.url === patternStr) return true;
1850
+ if (isValidURL(patternStr)) {
1851
+ const normalizedPattern = normalizedURL(patternStr);
1852
+ const normalizedUrl = normalizedURL(node.url);
1853
+ if (normalizedUrl.startsWith(normalizedPattern)) return true;
1854
+ }
1855
+ }
1856
+ return regex.test(getDefinitionText(node));
1857
+ }
1858
+ });
1859
+ }
1860
+ return (node) => {
1861
+ let result = Boolean(rules$3[0]?.negative);
1862
+ for (const { negative, match } of rules$3) {
1863
+ if (result === !negative) continue;
1864
+ if (match(node)) result = !negative;
1865
+ }
1866
+ return result;
1867
+ };
1868
+ }
1869
+ }
1870
+ });
1871
+ /**
1872
+ * Print the title with quotes.
1873
+ */
1874
+ function printTitle(title) {
1875
+ if (!title) return "";
1876
+ let titleToPrint = title.replaceAll(/\\(?=["')])/gu, "");
1877
+ if (titleToPrint.includes("\"") && titleToPrint.includes("'") && !titleToPrint.includes(")")) return ` (${titleToPrint})`;
1878
+ const quote = getQuote(titleToPrint);
1879
+ titleToPrint = titleToPrint.replaceAll("\\", "\\\\");
1880
+ titleToPrint = titleToPrint.replaceAll(quote, `\\${quote}`);
1881
+ return ` ${quote}${titleToPrint}${quote}`;
1882
+ }
1883
+ /**
1884
+ * Get the preferred quote for a string.
1885
+ */
1886
+ function getQuote(text) {
1887
+ let doubleQuoteCount = 0;
1888
+ let singleQuoteCount = 0;
1889
+ for (const character of text) if (character === "\"") doubleQuoteCount++;
1890
+ else if (character === "'") singleQuoteCount++;
1891
+ return doubleQuoteCount > singleQuoteCount ? "'" : "\"";
1892
+ }
1893
+ /**
1894
+ * Normalize a URL by ensuring it ends with a slash.
1895
+ */
1896
+ function normalizedURL(url) {
1897
+ const urlObj = createURLSafe(url);
1898
+ if (!urlObj) return url;
1899
+ return urlObj.href.endsWith("/") ? urlObj.href : `${urlObj.href}/`;
1900
+ }
1901
+
695
1902
  //#endregion
696
1903
  //#region src/utils/rules.ts
697
1904
  const rules$1 = [
1905
+ canonical_code_block_language_default,
698
1906
  definitions_last_default,
699
1907
  hard_linebreak_style_default,
1908
+ heading_casing_default,
700
1909
  no_text_backslash_linebreak_default,
701
1910
  no_trailing_spaces_default,
702
1911
  prefer_inline_code_words_default,
703
1912
  prefer_link_reference_definitions_default,
704
- prefer_linked_words_default
1913
+ prefer_linked_words_default,
1914
+ sort_definitions_default
705
1915
  ];
706
1916
 
707
1917
  //#endregion
@@ -736,7 +1946,7 @@ __export(meta_exports, {
736
1946
  version: () => version
737
1947
  });
738
1948
  const name = "eslint-plugin-markdown-preferences";
739
- const version = "0.7.0";
1949
+ const version = "0.9.0";
740
1950
 
741
1951
  //#endregion
742
1952
  //#region src/index.ts
@@ -745,11 +1955,16 @@ const rules = rules$1.reduce((obj, r) => {
745
1955
  obj[r.meta.docs.ruleName] = r;
746
1956
  return obj;
747
1957
  }, {});
1958
+ const resources = {
1959
+ defaultPreserveWords,
1960
+ defaultMinorWords
1961
+ };
748
1962
  var src_default = {
749
1963
  meta: meta_exports,
750
1964
  configs,
751
- rules
1965
+ rules,
1966
+ resources
752
1967
  };
753
1968
 
754
1969
  //#endregion
755
- export { configs, src_default as default, meta_exports as meta, rules };
1970
+ export { configs, src_default as default, meta_exports as meta, resources, rules };