Dataview Inline YAML to Property Converting Script

Someone probably has already made something like this already, but here’s my take on it. I’m not the greatest programmer, but it’s what works for me. This QuickAdd script converts all Dataview inline fields to Obsidian YAML properties and formats tags, removing the hashtags, and links into lists. It works pretty well for me! :smile:

To use it, just download whichever script you need. “convert.js” just converts the active file, but “convert_all.js” converts all the files in your vault that aren’t excluded in the settings. Once you download the script you want and add it to your QuickAdd scripts folder, just add a Macro in QuickAdd, click the lightning icon to create a command for it, BACK UP YOUR VAULT!!!, and finally run it.

Obviously, before you use any of these, you should have a proper backup of your vault.

Here’s the link to the GitHub gists for the scripts

1 Like

Thanks for sharing the scripts. With both, QuickAdd throws the same error:

QuickAdd: (ERROR) failed to run user script convert_all. Error: Cannot read properties of undefined (reading ‘api’)

Is there something to configure in the script for each user?

TIA.

Thank you, this is exactly what I was looking for. I tried to run the convert.js and I got the same error at @anon63144152: QuickAdd: (ERROR) failed to run user script convert_all. Error: Cannot read properties of undefined (reading ‘api’)

From a quick look, there’s a line this could be referring to.

const metaedit = app.plugins.plugins[‘metaedit’].api;

I’m not familiar with the inner workings of Obsidian. Do you know if there might be a syntax issue there? Thanks again for making this.

1 Like

The problem here that you don’t have MetaEdit plugin installed

Actually, it could be rewritten without MetaEdit as latest Obsidian has built-in ways to edit frontmatter.

While the original script (TOS) is working, I found a few concerns about it

  1. TOS converts all files at once. While for test run it is useful to run only for the current file
  2. TOS doesn’t use Dataview indexing and relies solely on the manual string parsing, therefore it supports only several possible inline field metadata formats among all possible
  3. TOS does not support multi-line arrays
key:: value1
key:: value2
key:: value3

I wrote my own simplified version of the script which uses Dataview API and built-in Obsidian frontmatter editing capabilities.

However, my code doesn’t remove the inline properties from the markdown, because it is actually quite difficult to do right for all possible scenarios. Unfortunately, Dataview API doesn’t expose the way to find all InlineField with their positions in the note file.

Wikilinks also impossible to reuse as is, because Dataview also doesn’t provide the raw version of the link. I’ve added some implementation that should cover most of the possible usecases.

const activeFile = app.workspace.getActiveFile();
if (!activeFile) {
  return;
}

if (!DataviewAPI) {
  return;
}

await DataviewAPI.index.reload(activeFile);
const dataviewProperties = DataviewAPI.page(activeFile.path) || {};
const sanitizedKeys = new Set(Object.keys(dataviewProperties).flatMap(key => key.toLowerCase() !== key ? [key.toLowerCase()] : []));

await app.fileManager.processFrontMatter(activeFile, (frontmatter) => {
  const frontmatterKeys = new Set(Object.keys(frontmatter));
  for (const [key, value] of Object.entries(dataviewProperties)) {
    if (key === 'file') {
      continue;
    }

    if (frontmatterKeys.has(key)) {
      continue;
    }

    if (key === key.toLowerCase() && sanitizedKeys.has(key)) {
      continue;
    }

    frontmatter[key] = convert(value);
  }
});

/**
 * @param {any} value
 * @returns {any}
 */
function convert(value) {
  if (!value) {
    return value;
  }

  if (value.constructor.name === 'Link') {
    const fileNameResolvedFile = app.metadataCache.getFirstLinkpathDest(value.fileName(), activeFile.path);
    const fullLinkResolvedFile = app.metadataCache.getFirstLinkpathDest(value.obsidianLink(), activeFile.path);
    const linkPath = fileNameResolvedFile && fullLinkResolvedFile && fileNameResolvedFile.path === fullLinkResolvedFile.path
      ? value.fileName()
      : value.obsidianLink().replace(/\.md$/, '');
    const displaySuffix = value.display ? `|${value.display}` : '';
    return `[[${linkPath}${displaySuffix}]]`;
  }

  if (Array.isArray(value)) {
    return value.map(item => convert(item));
  }

  return value;
}
1 Like