eleventy icon indicating copy to clipboard operation
eleventy copied to clipboard

functions in eleventyComputed (directory data file) execute twice

Open changethe opened this issue 2 years ago • 2 comments

Operating system

Node.js 19.4.0

Eleventy

2.0.0-beta.1

Describe the bug

the following code logs to the console twice for every element in the collection:

collection/collection.11tydata.js

module.exports = {
  eleventyComputed: {
    test: () => {
      console.log('TEST')
    }
  }
}

and when logging the data object, it returns one empty object and then the expected object for every element:

module.exports = {
  eleventyComputed: {
    test: (data) => {
      console.log(data)
    }
  }
}

might be related to #2754

Reproduction steps

No response

Expected behavior

No response

Reproduction URL

No response

Screenshots

No response

changethe avatar Jan 24 '23 17:01 changethe

Still seems to happen in Eleventy 2.0.1. Also when using the data() function in Javascript templates.

VividVisions avatar Jul 11 '23 16:07 VividVisions

Since this is still happening in Eleventy 3.0.0-alpha.4, I took a closer look. In order to detect dependencies between eleventyComputed keys, Eleventy seems to call all functions twice. As far as I understand it, the first call is to determine the call order and the second call is the "actual" one, in the correct order, with the proper data argument.

So, I guess this is a "works as intended" case. The question is, if we could prevent unnecessary computing time and make Eleventy even faster. Off the top of my head, I have the following idea. (Please forgive me, if I haven't completely understood the inner workings of Eleventy.)

The idea is to pass a second argument to the eleventyComputed functions, which is only present and true, if the call is to determine the dependencies. I'll call it isDryRun here.

module.exports = {
   eleventyComputed: {
      test: (data, isDryRun) => {
         // Help Eleventy determine dependencies as per docs.
         if (isDryRun) {
            data.foo;
            data.bar;

            // We're done for the dry run.
            return;
         }

         // Do some computing with foo and bar here that now only runs once. 
        
         return somethingComputed;
      },
      foo: (data, isDryRun) => { /* ... */ },
      bar: (data, isDryRun) => { /* ... */ }
   }
}

This should be backwards compatible and easy to understand and to implement by Eleventy users. One danger is, that users could forget to declare dependencies but I think a note in the documentation and/or error messages should quickly clear things up.

VividVisions avatar Jan 09 '24 14:01 VividVisions