Trouble calling a CustomJS script

What I’m trying to do

I want to set up a CustomJS script that I can reference in a template. I want to minimize the amount of text I have to add to the template in order to get the code to work.

Things I have tried

I’ve written a block of dataviewjs code that works inside of a page (see here for reference).

let page = dv.current().file.path;
let pages = new Set();
let stack = [page];
while (stack.length > 0) {
    let elem = stack.pop();
    let meta = dv.page(elem);
    if (!meta) continue;

    for (let inlink of meta.file.inlinks.array()) {
        if (pages.has(inlink.path)) continue;

        if (
        inlink.path.includes("Database level 1") ||
        inlink.path.includes("Database level 2") ||
        inlink.path.includes("Database level 3") ||
        inlink.path.includes("Database level 4") ||
        inlink.path.includes("Content database") ||
        inlink.path.includes("Books database") ||
        inlink.path.includes("Newspaper database") ||
        inlink.path.includes("Blog database")
        ) {
        pages.add(inlink.path);
        stack.push(inlink.path);
        }
    }
}

let data = dv.array(Array.from(pages)).map(p => dv.page(p));

data = data.filter(data => 
	data.file.path.split('/').includes("Books database") ||
	data.file.path.split('/').includes("Newspaper database") ||
	data.file.path.split('/').includes("Blog database")
	);

dv.table(["Content Sources"], data.map(p => [p.file.link]));

I’ve copied that entire block into a new page named “supportingContent.js” within a folder in Obsidian named “CustomJS”. The CustomJS folder is referenced in the CustomJS plugin:

The code is wrapped in a class in the “supportingContent” page like this:

class supportingContent {
code block
}

I have tried to call the code in a page like this:

const {supportingContent} = customJS;

But nothing happens. The code block works fine in the same page, so I’m not sure what I’m doing wrong. Any help is greatly appreciated!

What happens if you define const dv = this.app.plugins.plugins["dataview"].api; first?

@gino_m Adding that in the page like this:

const dv = this.app.plugins.plugins["dataview"].api;
const {supportingContent} = customJS;

throws this error:

I had one bullet – fired it.
I don’t see where else it had been declared…

Feed it to a bot, maybe.

1 Like

@gino_m :joy: thanks for the idea - I will try that

@gino_m I’ve tried asking ChatGPT but it does not give accurate dataviewjs code. Is there a certain AI you recommend for dataviewjs help?

No. Just as in darts a ten-darter is the poor man’s nine-darter, chat bot help is the poor man’s help.

Stick around though as this time of day is busier than was last time. Someone might chip in.

1 Like

Just throwing this up (not expecting to hit the mark):

Wouldn’t something like this work:

```dataviewjs
dv.view("your/folder/yourScript")
``` 
1 Like

Thanks for the idea. I tried adding a link to the file as you mentioned:

dv.view("CustomJS/supportingStudies.js")

But got this error:

Remove .js extension?

```dataviewjs
dv.view("CustomJS/supportingStudies")
```
1 Like
dv.view("CustomJS/supportingStudies")

gives this error

image

That’s about the page in the js file. Maybe we cannot call the script with dv.view…

You said the script worked in the note, though, right?

Yes, the script works fine and creates the table correctly when added into the note

Then remove

class supportingContent {
code block
}

And leave out CustomJs from the equation entirely and check DataView docs or see:

1 Like

Sorry - it works fine now :slight_smile:

I removed the function name I had added into the script file and now it is being called correctly.

Thank you!

Can you tell what you did right so people in trouble can follow suit?

1 Like

I copied the code block from inside here as regular text (omitting any backticks):

```dataviewjs
let page = dv.current().file.path;
let pages = new Set();
let stack = [page];
while (stack.length > 0) {
    let elem = stack.pop();
    let meta = dv.page(elem);
    if (!meta) continue;

    for (let inlink of meta.file.inlinks.array()) {
        if (pages.has(inlink.path)) continue;

        if (
        inlink.path.includes("Database level 1") ||
        inlink.path.includes("Database level 2") ||
        inlink.path.includes("Database level 3") ||
        inlink.path.includes("Database level 4") ||
        inlink.path.includes("Content database") ||
        inlink.path.includes("Books database") ||
        inlink.path.includes("Newspaper database") ||
        inlink.path.includes("Blog database")
        ) {
        pages.add(inlink.path);
        stack.push(inlink.path);
        }
    }
}

let data = dv.array(Array.from(pages)).map(p => dv.page(p));

data = data.filter(data => 
	data.file.path.split('/').includes("Books database") ||
	data.file.path.split('/').includes("Newspaper database") ||
	data.file.path.split('/').includes("Blog database")
	);

dv.table(["Content Sources"], data.map(p => [p.file.link]));
```

into a file named supportingStudies.js inside of a folder named CustomJS. It’s worth noting that part of the text still appears gray after pasting - I guess Obsidian recognizes for loops as code by default even when there are no backticks in place.

Then I added this dataviewjs code into the file where I wanted to call the script:

```dataviewjs
dv.view("CustomJS/supportingStudies")
```

So now the table is created immediately after pasting script - I don’t have to paste the script and add extra code to create the table as I had seen done in other places.

1 Like

Right.

Normally, in Obsidian, proper formatting is four backticks for code fencing and three for dataviewjs, like so:

````
```dataviewjs
code
```
````
  • I added an outer fence with 5 backticks now only I can see.
    This is needed only if the script is called from Obsidian directly. So people should delete those from the js file.

So you call that script from the template and it’s working.

Good. One ice-cream with roletti for cigar for you.

1 Like

I slight upgrade would be to also give context on those files:

// Define folderstoCheck array (Add your own folders)
const folderstoCheck = ["Placeholder1", "Placeholder2"];

let currentPage = dv.current().file.path;
let pages = new Set();
let stack = [currentPage];
while (stack.length > 0) {
    let elem = stack.pop();
    let meta = dv.page(elem);
    if (!meta) continue;

    for (let inlink of meta.file.inlinks.array()) {
        if (pages.has(inlink.path)) continue;

        // Check if any folder is included in the inlink path
        if (folderstoCheck.some(folder => inlink.path.includes(folder))) {
            pages.add(inlink.path);
            stack.push(inlink.path);
        }
    }
}

const processedData = [];

for (let page of pages) {
    const content = await dv.io.load(page);
    const inlinkedFileName = dv.current().file.name.replace(/\.[^/.]+$/, ""); // Extract the file name without extension
    const inlinkRegex = new RegExp(`\\[\\[${inlinkedFileName}.*?\\]\\]`, "g");
    const paragraphs = content.split(/\n\s*\n/);

    for (let paragraph of paragraphs) {
        if (inlinkRegex.test(paragraph)) {
            // Remove the path and format the file name with double square brackets
            const fileName = `[[${page.split('/').pop().replace(/\.[^/.]+$/, "")}]]`;

            processedData.push({
                link: fileName,
                content: paragraph
            });
        }
    }
}

dv.table(
    ["Source File", "Context"],
    processedData.map(p => [p.link, p.content])
);

For anyone wanting to do what OP wanted:
Save the file as a javascript file in a folder that is not the Templater user scripts folder, but some other folder.

Put in the foldertoCheck array the name of the folders that contain your Zotero annotations or whatever.

Then you can put the query call in a callout and even in a default template, like so:

> [!warning]+ Remember to incorporate this
> ```dataviewjs
> dv.view('javascriptfilenamewithnoextension')
> ```
1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.