Embedding a list of External Files as a table within a Note, with an option to Open

Lemme put it out there, I’m an Obsidian novice and a returning once-upon-a-time developer.
Like many before me, I have been searching for a way to list files in external folders that I don’t want included in my Vault, with an option to launch them using the system default settings.

By all accounts, the Dataview plugin doesn’t support external files / folders, and I understand why.
I looked at the Symbolic link option; which appeared to work, however, I couldn’t get Dataview queries to work in the notes, in addition to that it is hailed as “Use at your own risk” method.

With a little help from my AI friend I managed to put something together which appears to have worked for me, hence me putting it out here. I’m still coming to grips with templates in Obsidian which seems to be the logical place. Others may have developed something like this, apologies if that is the case, but I struggled to find working examples.

The example below is my first attempt at doing any of this kinda stuff …

The result of the output looks something like … (Table only)

See the Code below …

Note: In my example, I have excluded subdirectories and Obsidian’s files.

One needs to add the full path to the “folderPath” const. … “C:/Users/Xxxx/ …/”

// Parameters
const folderPath = "C:/Users/....../Business cases/";

const fs = require('fs');
const path = require('path');

try {
    // Read files in the specified folder
    const files = fs.readdirSync(folderPath);

    // Create a Markdown table
    let table = `| **File Name** | **Extension** | **Link** |\n|---|---|---|\n`;

    files.forEach(file => {
        const fullPath = path.join(folderPath, file);
        const fileExtension = path.extname(file);
        const fileName = path.basename(file, fileExtension);
		// Skip subdirectories and .md files 
		if (fileExtension === "" || fileExtension === ".md") { 
			return 
		};         
        // Construct table row
		table += `| ${fileName} | ${fileExtension} | [Open](${encodeURI(`file:///${fullPath.replace(/\\/g, '/')}`)}) |\n`;
    });

    // Render the table
    dv.paragraph(table);
} catch (error) {
    console.error("Error reading folder:", error);
    dv.paragraph("Error reading the folder. Please check the folder path.");
}

Comments appreciated.
Rhino

4 Likes

It didn’t work at first for me after copy/pasting the path to my folder from Explorer. I came to realize that your path uses ‘/’ where Explorer uses ''. I switched all the \ to / and then the list of files popped up! Thanks!

1 Like

Hello, thanks for posting the thread.

I have taken this code and adapted it to work as a dv.view javascript.

usage - save into a view.js file and await the script at that path from dataviewjs query like so:

```dataviewjs 
await dv.view("path/to/view", { dir: '/files/starting/directory', filter: ['.ext-to-hide'], match: ['.ext-to-show']});
```

dir is a required arg, filter and match are both optional.

view.js

// Build a table of files and extensions and open links from a starting path arg that may be external to the vault
// From thread by Ranier at: https://forum.obsidian.md/t/embedding-a-list-of-external-files-as-a-table-within-a-note-with-an-option-to-open/93169
// adapted to view.js with dir match and filter args by djasonic
// usage: dataviewjs `await dv.view("path/to/view", { dir: '/files/starting/directory', filter: ['.ext-to-hide'], match: ['.ext-to-show']});`
// console.log(`Loading External Files Table`);

function main(...args) {

    // Parse input arg[0] dictionary
    const folderPath = args[0].dir;
    const filterAllExts = args[0].filter || [];
    const matchExts = args[0].match || [];
    const match = matchExts.join(" ");
    const filterExts = filterAllExts.filter(ext => !matchExts.includes(ext));
    const filter = filterExts.join(" ");

    // Parameters
    const fs = require('fs');
    const path = require('path');

    try {
        // Read files in the specified folder
        const files = fs.readdirSync(folderPath);

        // Create a Markdown table
        let table = `| **File Name** | **Extension** | **Link** |\n|---|---|---|\n`;

        files.forEach(file => {
            const fullPath = path.join(folderPath, file);
            const fileExtension = path.extname(file);
            const fileName = path.basename(file, fileExtension);
            // Skip subdirectories and .md files 
            if (fileExtension === "" || (filterExts.length > 0 && filterExts.includes(fileExtension)) || (matchExts.length > 0 && !matchExts.includes(fileExtension))) { 
                return
            };
            // Construct table row
            table += `| ${fileName} | ${fileExtension} | [Open](${encodeURI(`file:///${fullPath.replace(/\\/g, '/')}`)}) |\n`;
        });
        const pathLink = `[${folderPath}](${encodeURI(`file:///${folderPath.replace(/\\/g, '/')}`)})`;
        dv.paragraph(pathLink);
        if (filterExts.length > 0) {
             dv.paragraph('filter: ' + filter);
        };
        if (matchExts.length > 0) {
             dv.paragraph('match: ' + match);
        };
        // Render the table
        dv.paragraph(table);
    } catch (error) {
        console.error("Error reading folder:", error);
        dv.paragraph("Error reading the folder. Please check the folder path." + folderPath);
    }

}

main(input)

2 Likes

Awesome adaptation!

1 Like

I’m converting my implementation to use yours.
I’ve created a “views” folder under the '.obsidian" folder and placed the js file there.
I’m getting “Dataview: custom view not found for ‘view.js’ or ‘views/view.js’”
Which folder do you use for the view.js, where the dataviewjs codeblock can locate it?

I’ve done similar scripts and keep mine in "_Meta/Scripts". I think anywhere in the vault is fine except .Obsidian.

1 Like

Correct, this view.js should be saved into a regular folder in the vault, not the .obsidian folder.

In my own vault I saved it into “Templates/Views/Directory View/”, for example.

It is not required to use view.js as the filename either, view script may be any named .js file.

1 Like