Repository: platers/obsidian-linter (ts)

Issue:
Bug: Numerical aliases need to be quoted to work
## Describe the Bug

Purely numerical aliases (e.g. `20230603151900`) need to be quoted to make them work in obsidian. The linter doesn't pick this up, yet.

## How to Reproduce

Steps to reproduce the behavior: See [this forum post](https://forum.obsidian.md/t/cannot-use-uid-as-alias/20306/3). The described behavior is still found in today's Obsidian.

## Expected Behavior

Purely numerical aliases are quoted to make them valid (e.g. `"20230603151900"`).

## Device

- [x] Desktop
- [x] Mobile
Bug: Updating title not working when commas are included
## Describe the Bug

I'm using *YAML title alias function* to insert title in frontmatter alias section also the function *linter-yaml-title-alias* is on.
Updating title and alias works fine until I put comma in the title.
Title is getting from H1,

## How to Reproduce

```
---
linter-yaml-title-alias: test
title: test
created: 1970-01-01
updated: 2023-06-14
aliases: ["test"]
tags: 
---
# test
```
```
---
linter-yaml-title-alias: test test2 test3
title: test test2 test3
created: 1970-01-01
updated: 2023-06-14
aliases: [test test2 test3]
tags: 
---
# test test2 test3
```
```
---
linter-yaml-title-alias: "test, test2, test3"
title: test, test2, test3
created: 1970-01-01
updated: 2023-06-14
aliases: ["test, test2, test3"]
tags: 
---
# test, test2, test3
```
```
---
linter-yaml-title-alias: test4
title: test4
created: 1970-01-01
updated: 2023-06-14
aliases: ["test, test2, test3"]
tags: 
---
# test4
```
[data.json.txt](https://github.com/platers/obsidian-linter/files/11744904/data.json.txt)
## Device

- [X] Desktop
- [ ] Mobile



Pull request description:
Update Alias Handling of Numeric Values for All Formatting and Handle Commas in `YAML Title Alias` Better
Fixes #758 
Fixes #747 

Changes Made:
- Added more tests for the 2 issues above
- Added an extra boolean to help know when values need to be escaped that are numeric when dealing with formatting YAML arrays
- Refactored formatting of YAML arrays to be easier to account for scenarios in a more general form
- Updated `isNumeric` to consider numbers to be numeric just in case the function is called for them
- Made sure to escape the previous title for `YAML Title Alias` if it should be
- Made sure to check for an unescaped version of the previous title if it was not found and was escaped (just to cover that scenario if it ever comes up in the wild)
- Moved `forceEscape` to a method to better help with uniformity as to when a value should be escaped for `YAML Title Alias`


Patch:
diff --git a/src/rules/format-yaml-arrays.ts b/src/rules/format-yaml-arrays.ts
index 303c265..c4082e6 100644
--- a/src/rules/format-yaml-arrays.ts
+++ b/src/rules/format-yaml-arrays.ts
@@ -61,6 +61,7 @@ export default class RuleTemplate extends RuleBuilder<FormatYamlArrayOptions> {
                   options.aliasArrayStyle,
                   options.defaultEscapeCharacter,
                   options.removeUnnecessaryEscapeCharsForMultiLineArrays,
+                  true, // escape numeric aliases see https://github.com/platers/obsidian-linter/issues/747
               ),
           );
 
diff --git a/src/rules/yaml-title-alias.ts b/src/rules/yaml-title-alias.ts
index cedd31b..bd4c83f 100644
--- a/src/rules/yaml-title-alias.ts
+++ b/src/rules/yaml-title-alias.ts
@@ -1,7 +1,7 @@
 import {Options, RuleType} from '../rules';
 import RuleBuilder, {BooleanOptionBuilder, ExampleBuilder, OptionBuilderBase} from './rule-builder';
 import dedent from 'ts-dedent';
-import {convertAliasValueToStringOrStringArray, escapeStringIfNecessaryAndPossible, formatYamlArrayValue, getYamlSectionValue, initYAML, LINTER_ALIASES_HELPER_KEY, loadYAML, NormalArrayFormats, OBSIDIAN_ALIASES_KEYS, OBSIDIAN_ALIAS_KEY_PLURAL, QuoteCharacter, removeYamlSection, setYamlSection, SpecialArrayFormats, splitValueIfSingleOrMultilineArray} from '../utils/yaml';
+import {convertAliasValueToStringOrStringArray, escapeStringIfNecessaryAndPossible, formatYamlArrayValue, getYamlSectionValue, initYAML, LINTER_ALIASES_HELPER_KEY, loadYAML, NormalArrayFormats, OBSIDIAN_ALIASES_KEYS, OBSIDIAN_ALIAS_KEY_PLURAL, QuoteCharacter, removeYamlSection, setYamlSection, SpecialArrayFormats, splitValueIfSingleOrMultilineArray, isValueEscapedAlready} from '../utils/yaml';
 import {ignoreListOfTypes, IgnoreTypes} from '../utils/ignore-types';
 import {getFirstHeaderOneText, yamlRegex} from '../utils/regex';
 import {isNumeric} from '../utils/strings';
@@ -44,14 +44,18 @@ export default class YamlTitleAlias extends RuleBuilder<YamlTitleAliasOptions> {
     const yaml = text.match(yamlRegex)[1];
 
     const shouldRemoveTitleAlias = !options.keepAliasThatMatchesTheFilename && unescapedTitle === options.fileName;
-    if (options.useYamlKeyToKeepTrackOfOldFilenameOrHeading) {
-      previousTitle = loadYAML(getYamlSectionValue(yaml, LINTER_ALIASES_HELPER_KEY));
-    }
 
     let newYaml = yaml.replace('---\n', '').replace('\n---', '');
     const parsedYaml = loadYAML(yaml);
 
-    previousTitle = loadYAML(getYamlSectionValue(yaml, LINTER_ALIASES_HELPER_KEY));
+    previousTitle = parsedYaml[LINTER_ALIASES_HELPER_KEY] ?? null;
+    if (previousTitle != null) {
+      // force previousTitle to be a string by concatenating with an empty string to make non-strings like numbers get handled correctly
+      previousTitle = previousTitle + '';
+      // escaping the previous title makes sure that we will be able to find it in the array if needed to be escaped there, but not in the title placeholder
+      // see https://github.com/platers/obsidian-linter/issues/758 and https://github.com/platers/obsidian-linter/issues/747
+      previousTitle = escapeStringIfNecessaryAndPossible(previousTitle, options.defaultEscapeCharacter, this.forceEscape(previousTitle, options.aliasArrayStyle));
+    }
 
     let aliasKeyForFile: string = null;
     const yamlKeys = Object.keys(parsedYaml);
@@ -85,15 +89,15 @@ export default class YamlTitleAlias extends RuleBuilder<YamlTitleAliasOptions> {
         newYaml = removeYamlSection(newYaml, aliasKeyForFile);
       } else if (options.preserveExistingAliasesSectionStyle) {
         if (!isEmpty && ((isSingleString && title == newAliasValue) || !isSingleString || currentAliasValue == newAliasValue)) {
-          newYaml = setYamlSection(newYaml, aliasKeyForFile, formatYamlArrayValue(newAliasValue, currentAliasStyle, options.defaultEscapeCharacter, options.removeUnnecessaryEscapeCharsForMultiLineArrays));
+          newYaml = setYamlSection(newYaml, aliasKeyForFile, formatYamlArrayValue(newAliasValue, currentAliasStyle, options.defaultEscapeCharacter, options.removeUnnecessaryEscapeCharsForMultiLineArrays, true/* escape numeric aliases see https://github.com/platers/obsidian-linter/issues/747*/));
         } else {
-          newYaml = setYamlSection(newYaml, aliasKeyForFile, formatYamlArrayValue(newAliasValue, options.aliasArrayStyle, options.defaultEscapeCharacter, options.removeUnnecessaryEscapeCharsForMultiLineArrays));
+          newYaml = setYamlSection(newYaml, aliasKeyForFile, formatYamlArrayValue(newAliasValue, options.aliasArrayStyle, options.defaultEscapeCharacter, options.removeUnnecessaryEscapeCharsForMultiLineArrays, true/* escape numeric aliases see https://github.com/platers/obsidian-linter/issues/747*/));
         }
       } else {
-        newYaml = setYamlSection(newYaml, aliasKeyForFile, formatYamlArrayValue(newAliasValue, options.aliasArrayStyle, options.defaultEscapeCharacter, options.removeUnnecessaryEscapeCharsForMultiLineArrays));
+        newYaml = setYamlSection(newYaml, aliasKeyForFile, formatYamlArrayValue(newAliasValue, options.aliasArrayStyle, options.defaultEscapeCharacter, options.removeUnnecessaryEscapeCharsForMultiLineArrays, true/* escape numeric aliases see https://github.com/platers/obsidian-linter/issues/747*/));
       }
     } else if (!shouldRemoveTitleAlias) {
-      newYaml = setYamlSection(newYaml, OBSIDIAN_ALIAS_KEY_PLURAL, formatYamlArrayValue(title, options.aliasArrayStyle, options.defaultEscapeCharacter, options.removeUnnecessaryEscapeCharsForMultiLineArrays));
+      newYaml = setYamlSection(newYaml, OBSIDIAN_ALIAS_KEY_PLURAL, formatYamlArrayValue(title, options.aliasArrayStyle, options.defaultEscapeCharacter, options.removeUnnecessaryEscapeCharsForMultiLineArrays, true/* escape numeric aliases see https://github.com/platers/obsidian-linter/issues/747*/));
     }
 
     if (!options.useYamlKeyToKeepTrackOfOldFilenameOrHeading || shouldRemoveTitleAlias) {
@@ -110,11 +114,13 @@ export default class YamlTitleAlias extends RuleBuilder<YamlTitleAliasOptions> {
     let unescapedTitle = ignoreListOfTypes([IgnoreTypes.code, IgnoreTypes.math, IgnoreTypes.yaml, IgnoreTypes.tag], text, getFirstHeaderOneText);
     unescapedTitle = unescapedTitle || fileName;
 
-    const forceEscape = (unescapedTitle.includes(',') && (aliasArrayStyle === NormalArrayFormats.SingleLine || aliasArrayStyle === SpecialArrayFormats.SingleStringToSingleLine || aliasArrayStyle === SpecialArrayFormats.SingleStringCommaDelimited)) || isNumeric(unescapedTitle);
-    const escapedTitle = escapeStringIfNecessaryAndPossible(unescapedTitle, defaultEscapeCharacter, forceEscape);
+    const escapedTitle = escapeStringIfNecessaryAndPossible(unescapedTitle, defaultEscapeCharacter, this.forceEscape(unescapedTitle, aliasArrayStyle));
 
     return [unescapedTitle, escapedTitle];
   }
+  forceEscape(title: string, aliasArrayStyle: NormalArrayFormats | SpecialArrayFormats): boolean {
+    return isNumeric(title) || (title.includes(',') && (aliasArrayStyle === NormalArrayFormats.SingleLine || aliasArrayStyle === SpecialArrayFormats.SingleStringToSingleLine || aliasArrayStyle === SpecialArrayFormats.SingleStringCommaDelimited));
+  }
   getNewAliasValue(originalValue: string |string[], shouldRemoveTitle: boolean, title: string, previousTitle: string): string |string[] {
     if (originalValue == null) {
       return shouldRemoveTitle ? '' : title;
@@ -131,13 +137,19 @@ export default class YamlTitleAlias extends RuleBuilder<YamlTitleAliasOptions> {
         originalValue = [title, originalValue];
       }
     } else if (previousTitle !== null) {
-      const previousTitleIndex = originalValue.indexOf(previousTitle);
+      let previousTitleIndex = originalValue.indexOf(previousTitle);
+      if (previousTitleIndex === -1 && isValueEscapedAlready(previousTitle)) {
+        previousTitleIndex = originalValue.indexOf(previousTitle.substring(1, previousTitle.length - 1));
+      }
+
       if (previousTitleIndex !== -1) {
         if (shouldRemoveTitle) {
           originalValue.splice(previousTitleIndex, 1);
         } else {
           originalValue[previousTitleIndex] = title;
         }
+      } else {
+        originalValue = [title, ...originalValue];
       }
     } else {
       const currentTitleIndex = originalValue.indexOf(title);
diff --git a/src/utils/strings.ts b/src/utils/strings.ts
index 6e2b103..66354b2 100644
--- a/src/utils/strings.ts
+++ b/src/utils/strings.ts
@@ -344,7 +344,8 @@ export function countInstances(text: string, instancesOf: string): number {
 
 // based on https://stackoverflow.com/a/175787/8353749
 export function isNumeric(str: string) {
-  if (typeof str != 'string') return false; // we only process strings!
+  const type = typeof str;
+  if (type != 'string') return type === 'number'; // we only process strings so if the value is not already a number the result is false
   return !isNaN(str as unknown as number) && // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)...
          !isNaN(parseFloat(str)); // ...and ensure strings of whitespace fail
 }
diff --git a/src/utils/yaml.ts b/src/utils/yaml.ts
index 1ed7b36..87bf73d 100644
--- a/src/utils/yaml.ts
+++ b/src/utils/yaml.ts
@@ -1,6 +1,7 @@
 import {load, dump} from 'js-yaml';
 import {getTextInLanguage} from '../lang/helpers';
 import {escapeDollarSigns, yamlRegex} from './regex';
+import {isNumeric} from './strings';
 
 
 export const OBSIDIAN_TAG_KEY_SINGULAR = 'tag';
@@ -102,76 +103,90 @@ export type QuoteCharacter = '\'' | '"';
  * @param {NormalArrayFormats | SpecialArrayFormats | TagSpecificArrayFormats} format The format that the array should be converted into.
  * @param {string} defaultEscapeCharacter The character escape to use around the value if a specific escape character is not needed.
  * @param {boolean} removeEscapeCharactersIfPossibleWhenGoingToMultiLine Whether or not to remove no longer needed escape values when converting to a multi-line format.
+ * @param {boolean} escapeNumericValues Whether or not to escape any numeric values found in the array.
  * @return {string} The formatted array in the specified yaml/obsidian yaml format.
  */
-export function formatYamlArrayValue(value: string | string[], format: NormalArrayFormats | SpecialArrayFormats | TagSpecificArrayFormats, defaultEscapeCharacter: QuoteCharacter, removeEscapeCharactersIfPossibleWhenGoingToMultiLine: boolean): string {
+export function formatYamlArrayValue(value: string | string[], format: NormalArrayFormats | SpecialArrayFormats | TagSpecificArrayFormats, defaultEscapeCharacter: QuoteCharacter, removeEscapeCharactersIfPossibleWhenGoingToMultiLine: boolean, escapeNumericValues: boolean = false): string {
   if (typeof value === 'string') {
     value = [value];
   }
 
-  switch (format) {
-    case NormalArrayFormats.SingleLine:
-      if (value == null || value.length === 0) {
-        return ' []';
-      }
+  // handle default values here
+  if (value == null || value.length === 0) {
+    return getDefaultYAMLArrayValue(format);
+  }
 
-      return ' ' + convertStringArrayToSingleLineArray(value);
-    case NormalArrayFormats.MultiLine:
-      if (value == null || value.length === 0) {
-        return '\n  - ';
+  // handle escaping numeric values and the removal of escape characters where applicable for multiline arrays
+  const shouldRemoveEscapeCharactersIfPossible = removeEscapeCharactersIfPossibleWhenGoingToMultiLine && (format == NormalArrayFormats.MultiLine || (format == SpecialArrayFormats.SingleStringToMultiLine && value.length > 1));
+  if (escapeNumericValues || shouldRemoveEscapeCharactersIfPossible) {
+    for (let i = 0; i < value.length; i++) {
+      let currentValue = value[i];
+      const valueIsEscaped = isValueEscapedAlready(currentValue);
+      if (valueIsEscaped) {
+        currentValue = currentValue.substring(1, currentValue.length - 1);
       }
 
-      if (removeEscapeCharactersIfPossibleWhenGoingToMultiLine) {
-        for (let i = 0; i < value.length; i++) {
-          const currentValue = value[i];
-          if (isValueEscapedAlready(currentValue)) {
-            value[i] = escapeStringIfNecessaryAndPossible(currentValue.substring(1, currentValue.length - 1), defaultEscapeCharacter);
-          }
-        }
+      const shouldRequireEscapeOfCurrentValue = escapeNumericValues && isNumeric(currentValue);
+      if (valueIsEscaped && shouldRequireEscapeOfCurrentValue) {
+        continue; // when dealing with numbers that we need escaped, we don't want to remove that escaping for multiline arrays
+      } else if (shouldRequireEscapeOfCurrentValue || (valueIsEscaped && shouldRemoveEscapeCharactersIfPossible)) {
+        value[i] = escapeStringIfNecessaryAndPossible(currentValue, defaultEscapeCharacter, shouldRequireEscapeOfCurrentValue);
       }
+    }
+  }
 
-      return '\n  - ' + value.join('\n  - ');
+  // handle the values that are present based on the format of the array
+  /* eslint-disable no-fallthrough -- we are falling through here because it makes the most sense for the cases below */
+  switch (format) {
     case SpecialArrayFormats.SingleStringToSingleLine:
-      if (value == null || value.length === 0) {
-        return ' ';
-      } else if (value.length === 1) {
+      if (value.length === 1) {
         return ' ' + value[0];
       }
-
+    case NormalArrayFormats.SingleLine:
       return ' ' + convertStringArrayToSingleLineArray(value);
     case SpecialArrayFormats.SingleStringToMultiLine:
-      if (value == null || value.length === 0) {
-        return ' ';
-      } else if (value.length === 1) {
+      if (value.length === 1) {
         return ' ' + value[0];
       }
-
+    case NormalArrayFormats.MultiLine:
       return '\n  - ' + value.join('\n  - ');
     case TagSpecificArrayFormats.SingleStringSpaceDelimited:
-      if (value == null || value.length === 0) {
-        return ' ';
-      } else if (value.length === 1) {
+      if (value.length === 1) {
         return ' ' + value[0];
       }
 
-      return ' ' +value.join(' ');
+      return ' ' + value.join(' ');
     case SpecialArrayFormats.SingleStringCommaDelimited:
-      if (value == null || value.length === 0) {
-        return ' ';
-      } else if (value.length === 1) {
+      if (value.length === 1) {
         return ' ' + value[0];
       }
 
       return ' ' + value.join(', ');
     case TagSpecificArrayFormats.SingleLineSpaceDelimited:
-      if (value == null || value.length === 0) {
-        return ' []';
-      } else if (value.length === 1) {
+      if (value.length === 1) {
         return ' ' + value[0];
       }
 
       return ' ' + convertStringArrayToSingleLineArray(value).replaceAll(', ', ' ');
   }
+  /* eslint-enable no-fallthrough */
+}
+
+function getDefaultYAMLArrayValue(format: NormalArrayFormats | SpecialArrayFormats | TagSpecificArrayFormats): string {
+  /* eslint-disable no-fallthrough */
+  switch (format) {
+    case NormalArrayFormats.SingleLine:
+    case TagSpecificArrayFormats.SingleLineSpaceDelimited:
+      return ' []';
+    case NormalArrayFormats.MultiLine:
+      return '\n  - ';
+    case SpecialArrayFormats.SingleStringToSingleLine:
+    case SpecialArrayFormats.SingleStringToMultiLine:
+    case TagSpecificArrayFormats.SingleStringSpaceDelimited:
+    case SpecialArrayFormats.SingleStringCommaDelimited:
+      return ' ';
+  }
+  /* eslint-enable no-fallthrough */
 }
 
 function convertStringArrayToSingleLineArray(arrayItems: string[]): string {


Test patch:
diff --git a/__tests__/format-yaml-arrays.test.ts b/__tests__/format-yaml-arrays.test.ts
index 78b6aa6..3572a97 100644
--- a/__tests__/format-yaml-arrays.test.ts
+++ b/__tests__/format-yaml-arrays.test.ts
@@ -1026,8 +1026,7 @@ ruleTest({
         defaultArrayStyle: NormalArrayFormats.MultiLine,
       },
     },
-    {
-      // accounts for https://github.com/platers/obsidian-linter/issues/525
+    { // accounts for https://github.com/platers/obsidian-linter/issues/525
       testName: 'Converting a single string comma separated array to a multi-line array should respect escaped entries',
       before: dedent`
         ---
@@ -1045,5 +1044,39 @@ ruleTest({
         aliasArrayStyle: NormalArrayFormats.MultiLine,
       },
     },
+    {
+      testName: 'Numeric aliases are escaped when aliases are converted from one type to another',
+      before: dedent`
+        ---
+        aliases: 1234, alias1
+        ---
+      `,
+      after: dedent`
+        ---
+        aliases:
+          - "1234"
+          - alias1
+        ---
+      `,
+      options: {
+        aliasArrayStyle: NormalArrayFormats.MultiLine,
+      },
+    },
+    {
+      testName: 'Numeric aliases are escaped when aliases are otherwise unchanged',
+      before: dedent`
+        ---
+        aliases: 1234, alias1
+        ---
+      `,
+      after: dedent`
+        ---
+        aliases: "1234", alias1
+        ---
+      `,
+      options: {
+        aliasArrayStyle: SpecialArrayFormats.SingleStringCommaDelimited,
+      },
+    },
   ],
 });
diff --git a/__tests__/yaml-title-alias.test.ts b/__tests__/yaml-title-alias.test.ts
index 3465893..1a1e0cf 100644
--- a/__tests__/yaml-title-alias.test.ts
+++ b/__tests__/yaml-title-alias.test.ts
@@ -1278,5 +1278,118 @@ ruleTest({
         fileName: '12345678',
       },
     },
+    { // accounts for https://github.com/platers/obsidian-linter/issues/747
+      testName: 'Make sure old numerical alias gets removed when the alias for file changes and `linter-yaml-title-alias` is numeric and not escaped',
+      before: dedent`
+        ---
+        aliases: ["12345678", alias1]
+        linter-yaml-title-alias: 12345678
+        ---
+        # 1234
+      `,
+      after: dedent`
+        ---
+        aliases: ["1234", alias1]
+        linter-yaml-title-alias: "1234"
+        ---
+        # 1234
+      `,
+      options: {
+        aliasArrayStyle: NormalArrayFormats.SingleLine,
+        defaultEscapeCharacter: '"',
+      },
+    },
+    { // accounts for https://github.com/platers/obsidian-linter/issues/747
+      testName: 'Make sure old numerical alias gets removed when the alias for file changes and `linter-yaml-title-alias` is numeric and not escaped and value in alias is not escaped',
+      before: dedent`
+        ---
+        aliases: [12345678, alias1]
+        linter-yaml-title-alias: 12345678
+        ---
+        # 1234
+      `,
+      after: dedent`
+        ---
+        aliases: ["1234", alias1]
+        linter-yaml-title-alias: "1234"
+        ---
+        # 1234
+      `,
+      options: {
+        aliasArrayStyle: NormalArrayFormats.SingleLine,
+        defaultEscapeCharacter: '"',
+      },
+    },
+    { // accounts for https://github.com/platers/obsidian-linter/issues/747
+      testName: 'Make sure old numerical alias gets removed when the alias for file changes and `linter-yaml-title-alias` is numeric and is escaped',
+      before: dedent`
+        ---
+        aliases: ["12345678", alias1]
+        linter-yaml-title-alias: "12345678"
+        ---
+        # 1234
+      `,
+      after: dedent`
+        ---
+        aliases: ["1234", alias1]
+        linter-yaml-title-alias: "1234"
+        ---
+        # 1234
+      `,
+      options: {
+        aliasArrayStyle: NormalArrayFormats.SingleLine,
+        defaultEscapeCharacter: '"',
+      },
+    },
+    { // accounts for https://github.com/platers/obsidian-linter/issues/758
+      testName: 'Make sure commas are handled properly when found in alias',
+      before: dedent`
+        ---
+        linter-yaml-title-alias: "test, test2, test3"
+        title: test, test2, test3
+        created: 1970-01-01
+        updated: 2023-06-14
+        aliases: ["test, test2, test3"]
+        tags:${' '}
+        ---
+        # test4
+      `,
+      after: dedent`
+        ---
+        linter-yaml-title-alias: test4
+        title: test, test2, test3
+        created: 1970-01-01
+        updated: 2023-06-14
+        aliases: [test4]
+        tags:${' '}
+        ---
+        # test4
+      `,
+      options: {
+        aliasArrayStyle: NormalArrayFormats.SingleLine,
+        defaultEscapeCharacter: '"',
+      },
+    },
+    {
+      testName: 'Make sure that if previous title is present, but not present in aliases, new title is still added at start of aliases',
+      before: dedent`
+        ---
+        aliases: [alias1]
+        linter-yaml-title-alias: blob
+        ---
+        # 1234
+      `,
+      after: dedent`
+        ---
+        aliases: ["1234", alias1]
+        linter-yaml-title-alias: "1234"
+        ---
+        # 1234
+      `,
+      options: {
+        aliasArrayStyle: NormalArrayFormats.SingleLine,
+        defaultEscapeCharacter: '"',
+      },
+    },
   ],
 });