Repository: platers/obsidian-linter (ts)

Issue:
Bug: Linting a file with a header like [[Test]] creates an error on the second lint with Title option enabled
## Describe the Bug

Reference: #449 

If I lint a file with 

# [[Test]]

two times I get the following error:

```
Obsidian Linter] Lint File Error in File 'Example/Test.md':
YAMLException: bad indentation of a mapping entry (9:3)

  6 | language: de
  7 | Vault: Privat
  8 | title: [[Test]]
  9 |   - [Test]
--------^
 10 | tags: 
 11 | aliases: [[[Test]]]
```
So I guess there is still something to be corrected to support links as headers

Let me know if you need more information or an example file.


Pull request:
Fix Bug with Links in Headings by Converting to Text instead of Retaining Link
Fixes #470 

Changes Made:
- Made sure to convert links to their display text and links
- Added tests to account for the scenarios in question and hopefully make sure that down the road we don't break anything
- Refactored header regex to make it more maintainable and also clean up logic some

Patch:
diff --git a/src/rules/yaml-title-alias.ts b/src/rules/yaml-title-alias.ts
index 3c609e2..2777131 100644
--- a/src/rules/yaml-title-alias.ts
+++ b/src/rules/yaml-title-alias.ts
@@ -3,7 +3,7 @@ import RuleBuilder, {BooleanOptionBuilder, ExampleBuilder, OptionBuilderBase} fr
 import dedent from 'ts-dedent';
 import {convertAliasValueToStringOrStringArray, escapeStringIfNecessaryAndPossible, formatYamlArrayValue, getYamlSectionValue, initYAML, LINTER_ALIASES_HELPER_KEY, loadYAML, NormalArrayFormats, OBSIDIAN_ALIASES_KEYS, OBSIDIAN_ALIAS_KEY_PLURAL, removeYamlSection, setYamlSection, SpecialArrayFormats, splitValueIfSingleOrMultilineArray} from '../utils/yaml';
 import {ignoreListOfTypes, IgnoreTypes} from '../utils/ignore-types';
-import {yamlRegex} from '../utils/regex';
+import {getFirstHeaderOneText, yamlRegex} from '../utils/regex';
 
 
 class YamlTitleAliasOptions implements Options {
@@ -37,13 +37,7 @@ export default class YamlTitleAlias extends RuleBuilder<YamlTitleAliasOptions> {
   }
   apply(text: string, options: YamlTitleAliasOptions): string {
     text = initYAML(text);
-    let title = ignoreListOfTypes([IgnoreTypes.code, IgnoreTypes.yaml, IgnoreTypes.tag], text, (text) => {
-      const result = text.match(/^#\s+(.*)/m);
-      if (result) {
-        return result[1];
-      }
-      return '';
-    });
+    let title = ignoreListOfTypes([IgnoreTypes.code, IgnoreTypes.yaml, IgnoreTypes.tag], text, getFirstHeaderOneText);
     title = title || options.fileName;
 
     let previousTitle: string = null;
@@ -249,6 +243,24 @@ export default class YamlTitleAlias extends RuleBuilder<YamlTitleAliasOptions> {
           keepAliasThatMatchesTheFilename: true,
         },
       }),
+      new ExampleBuilder({ // accounts for https://github.com/platers/obsidian-linter/issues/470
+        description: 'Make sure that markdown and wiki links in first H1 get their values converted to text',
+        before: dedent`
+          # This is a [Heading](markdown.md)
+        `,
+        after: dedent`
+          ---
+          aliases:
+            - This is a Heading
+          linter-yaml-title-alias: This is a Heading
+          ---
+          # This is a [Heading](markdown.md)
+        `,
+        options: {
+          aliasArrayStyle: NormalArrayFormats.MultiLine,
+        },
+      },
+      ),
     ];
   }
   get optionBuilders(): OptionBuilderBase<YamlTitleAliasOptions>[] {
diff --git a/src/rules/yaml-title.ts b/src/rules/yaml-title.ts
index 6223bdc..aa44e83 100644
--- a/src/rules/yaml-title.ts
+++ b/src/rules/yaml-title.ts
@@ -3,7 +3,7 @@ import RuleBuilder, {ExampleBuilder, OptionBuilderBase, TextOptionBuilder} from
 import dedent from 'ts-dedent';
 import {escapeStringIfNecessaryAndPossible, formatYAML, initYAML} from '../utils/yaml';
 import {ignoreListOfTypes, IgnoreTypes} from '../utils/ignore-types';
-import {escapeDollarSigns} from '../utils/regex';
+import {escapeDollarSigns, getFirstHeaderOneText} from '../utils/regex';
 import {insert} from '../utils/strings';
 
 class YamlTitleOptions implements Options {
@@ -32,13 +32,7 @@ export default class YamlTitle extends RuleBuilder<YamlTitleOptions> {
   }
   apply(text: string, options: YamlTitleOptions): string {
     text = initYAML(text);
-    let title = ignoreListOfTypes([IgnoreTypes.code, IgnoreTypes.yaml, IgnoreTypes.tag], text, (text) => {
-      const result = text.match(/^#\s+(.*)/m);
-      if (result) {
-        return result[1];
-      }
-      return '';
-    });
+    let title = ignoreListOfTypes([IgnoreTypes.code, IgnoreTypes.yaml, IgnoreTypes.tag], text, getFirstHeaderOneText);
     title = title || options.fileName;
 
     title = escapeStringIfNecessaryAndPossible(title, options.defaultEscapeCharacter);
@@ -91,6 +85,18 @@ export default class YamlTitle extends RuleBuilder<YamlTitleOptions> {
           fileName: 'Filename',
         },
       }),
+      new ExampleBuilder({ // accounts for https://github.com/platers/obsidian-linter/issues/470
+        description: 'Make sure that markdown links in headings are properly copied to the yaml as just the text',
+        before: dedent`
+          # This is a [Heading](test heading.md)
+        `,
+        after: dedent`
+          ---
+          title: This is a Heading
+          ---
+          # This is a [Heading](test heading.md)
+        `,
+      }),
     ];
   }
   get optionBuilders(): OptionBuilderBase<YamlTitleOptions>[] {
diff --git a/src/utils/regex.ts b/src/utils/regex.ts
index a27b9bf..2df784d 100644
--- a/src/utils/regex.ts
+++ b/src/utils/regex.ts
@@ -2,15 +2,17 @@
 import {makeSureContentHasEmptyLinesAddedBeforeAndAfter} from './strings';
 
 // Useful regexes
-export const headerRegex = /^(\s*)(#+)(\s+)(.*)$/;
+export const headerRegex = /^(\s*)(#+)(\s+)(.*)$/m;
 export const fencedRegexTemplate = '^XXX\\.*?\n(?:((?:.|\n)*?)\n)?XXX(?=\\s|$)$';
 export const yamlRegex = /^---\n((?:(((?!---)(?:.|\n)*?)\n)?))---(?=\n|$)/;
 export const backtickBlockRegexTemplate = fencedRegexTemplate.replaceAll('X', '`');
 export const tildeBlockRegexTemplate = fencedRegexTemplate.replaceAll('X', '~');
 export const indentedBlockRegex = '^((\t|( {4})).*\n)+';
 export const codeBlockRegex = new RegExp(`${backtickBlockRegexTemplate}|${tildeBlockRegexTemplate}|${indentedBlockRegex}`, 'gm');
-export const wikiLinkRegex = /(!?)(\[{2}[^[\n\]]*\]{2})/g;
-export const genericLinkRegex = /^!?\[.*\](.*)$/;
+// based on https://stackoverflow.com/a/26010910/8353749
+export const wikiLinkRegex = /(!?)\[{2}([^\][\n|]+)(\|([^\][\n|]+))?\]{2}/g;
+// based on https://davidwells.io/snippets/regex-match-markdown-links
+export const genericLinkRegex = /(!?)\[([^[]*)\](\(.*\))/g;
 export const tagRegex = /(?:\s|^)#[^\s#;.,><?!=+]+/g;
 export const obsidianMultilineCommentRegex = /^%%\n[^%]*\n%%/gm;
 export const wordSplitterRegex = /[,\s]+/;
@@ -79,3 +81,22 @@ export function ensureEmptyLinesAroundTables(text: string): string {
 
   return text;
 }
+
+/**
+ * Gets the first header one's text from the string provided making sure to convert any links to their display text.
+ * @param {string} text - The text to have get the first header one's text from.
+ * @return {string} The text for the first header one if present or an empty string.
+ */
+export function getFirstHeaderOneText(text: string) {
+  const result = text.match(headerRegex);
+  if (result && result[4]) {
+    let headerText = result[4];
+    headerText = headerText.replaceAll(wikiLinkRegex, (_, _2, $2: string, $3: string) => {
+      return $3 ?? $2;
+    });
+
+    return headerText.replaceAll(genericLinkRegex, '$2');
+  }
+
+  return '';
+}


Test patch:
diff --git a/__tests__/yaml-title-alias.test.ts b/__tests__/yaml-title-alias.test.ts
index fc1ecb7..21e7029 100644
--- a/__tests__/yaml-title-alias.test.ts
+++ b/__tests__/yaml-title-alias.test.ts
@@ -1080,8 +1080,8 @@ ruleTest({
       after: dedent`
         ---
         aliases:
-          - [[Heading]]
-        linter-yaml-title-alias: [[Heading]]
+          - Heading
+        linter-yaml-title-alias: Heading
         ---
         [[Link1]]
 
@@ -1091,6 +1091,50 @@ ruleTest({
         aliasArrayStyle: NormalArrayFormats.MultiLine,
       },
     },
+    { // relates to https://github.com/platers/obsidian-linter/issues/470
+      testName: 'Make sure that markdown links aliases and custom key are converted to text',
+      before: dedent`
+        ---
+        aliases:
+          - This is a [Heading](markdown.md)
+        linter-yaml-title-alias: This is a [Heading](markdown.md)
+        ---
+        # This is a [Heading](markdown.md)
+      `,
+      after: dedent`
+        ---
+        aliases:
+          - This is a Heading
+        linter-yaml-title-alias: This is a Heading
+        ---
+        # This is a [Heading](markdown.md)
+      `,
+      options: {
+        aliasArrayStyle: NormalArrayFormats.MultiLine,
+      },
+    },
+    { // relates to https://github.com/platers/obsidian-linter/issues/470
+      testName: 'Make sure that wiki links aliases and custom key are converted to text',
+      before: dedent`
+        ---
+        aliases:
+          - This is a [[Heading]]
+        linter-yaml-title-alias: This is a [[Heading]]
+        ---
+        # This is a [[Heading]]
+      `,
+      after: dedent`
+        ---
+        aliases:
+          - This is a Heading
+        linter-yaml-title-alias: This is a Heading
+        ---
+        # This is a [[Heading]]
+      `,
+      options: {
+        aliasArrayStyle: NormalArrayFormats.MultiLine,
+      },
+    },
     { // accounts for https://github.com/platers/obsidian-linter/issues/439
       testName: 'Make sure escaped aliases that match the H1 do not get added back',
       before: dedent`
diff --git a/__tests__/yaml-title.test.ts b/__tests__/yaml-title.test.ts
index 75320a2..2f3a76a 100644
--- a/__tests__/yaml-title.test.ts
+++ b/__tests__/yaml-title.test.ts
@@ -74,12 +74,42 @@ ruleTest({
       `,
       after: dedent`
         ---
-        title: [[Heading]]
+        title: Heading
         ---
         [[Link1]]
 
         # [[Heading]]
       `,
     },
+    { // relates to https://github.com/platers/obsidian-linter/issues/470
+      testName: 'Make sure that markdown links in title get converted to text',
+      before: dedent`
+        ---
+        title: This is a [Heading](test heading.md)
+        ---
+        # This is a [Heading](test heading.md)
+      `,
+      after: dedent`
+        ---
+        title: This is a Heading
+        ---
+        # This is a [Heading](test heading.md)
+      `,
+    },
+    { // relates to https://github.com/platers/obsidian-linter/issues/470
+      testName: 'Make sure that wiki links in title get converted to text',
+      before: dedent`
+        ---
+        title: This is a [[Heading]]
+        ---
+        # This is a [[Heading]]
+      `,
+      after: dedent`
+        ---
+        title: This is a Heading
+        ---
+        # This is a [[Heading]]
+      `,
+    },
   ],
 });