Closed jasonk closed 2 months ago
After some more debugging I managed to get a stack trace. Looks like it might actually be a dmd
issue:
RangeError: Maximum call stack size exceeded
at Function.keys (<anonymous>)
at testValue (/Users/jasonk/jsdoc2md-repro/node_modules/test-value/index.js:23:19)
at /Users/jasonk/jsdoc2md-repro/node_modules/test-value/index.js:81:12
at Array.filter (<anonymous>)
at _identifiers (/Users/jasonk/jsdoc2md-repro/node_modules/dmd/helpers/ddata.js:460:38)
at Object._children (/Users/jasonk/jsdoc2md-repro/node_modules/dmd/helpers/ddata.js:478:16)
at /Users/jasonk/jsdoc2md-repro/node_modules/dmd/helpers/ddata.js:506:27
at Array.forEach (<anonymous>)
at iterate (/Users/jasonk/jsdoc2md-repro/node_modules/dmd/helpers/ddata.js:504:20)
at /Users/jasonk/jsdoc2md-repro/node_modules/dmd/helpers/ddata.js:506:9
I've tracked this down as far as the descendants
function in dmd/helpers/ddata.js
. Maybe it's because it's late and I'll figure it out tomorrow after sleeping on it, but at the moment I'm not really following what this function is trying to do. The function itself looks like this:
function descendants (options) {
var min = typeof options.hash.min !== 'undefined' ? options.hash.min : 2
delete options.hash.min
options.hash.memberof = this.id
var output = []
function iterate (childrenList) {
if (childrenList.length) {
childrenList.forEach(function (child) {
output.push(child)
iterate(_children.call(child, options))
})
}
}
iterate(_children.call(this, options))
if (output.length >= (min || 0)) return output
}
The problem is that the call to _children.call
that is inside the iterate
function is returning exactly the same thing that the one outside the iterate
function is returning, so once it enters this function that iterate
gets called recursively on the same object a few thousand times until it blows the stack..
I'm not yet sure what is causing the stack issue (i haven't traced it) but as is stressed in the wiki, an ESM module must include a @module
declaration at the top.
This should work.
/**
* @module something
*/
/**
* A description.
*/
export class Foo {
constructor() {}
}
@jasonk did @75lb suggestion work for you? I have gone through my library (that incidentally has been building jsdoc2md
successfully many months) and added the suggested @module
but it is still not building for me. (Throws Max Stack exception)
i get the "maximum call stack size exceeded" error when i do this
/**
* @module something
*/
/**
* A description.
* @alias module:something
* @typicalname othersomething
*/
export class Foo {
constructor() {}
}
if change the export syntax, no more error
class Foo {
constructor() {}
}
export {Foo}
using jsdoc-to-markdown@6.0.1
I hastily refactored the function yesterday, avoiding the recursion, which fixes the issue here:
/**
return a flat list containing all decendants
@param [sortBy] {string} - "kind"
@param [min] {number} - only returns if there are `min` children
@this {identifier}
@returns {identifier[]}
@static
*/
function descendants (options) {
var min = typeof options.hash.min !== 'undefined' ? options.hash.min : 2
delete options.hash.min
options.hash.memberof = this.id
var output = []
var newList = [this];
while(newList.length) {
const oldList = newList;
newList = [];
for(let el of oldList) {
for(let c of _children.call(el, options)) {
if(c.id === this.id) continue;
output.push(c);
newList.push(c)
}
}
}
if (output.length >= (min || 0)) return output
}
Only to hit the same issue at another point in code, though -- probably sig()
in ddata.js
In case it helps: when removing export
, the issue is avoided.
Reproduced - thanks for the reproduction case @jasonk.. It's due to this doclet's id
being equal to its memberof
value in the jsdoc2md --json
output (which comes from jsdoc via jsdoc-parse)..
{
"id": "Foo",
...
"memberof": "Foo",
...
}
It's at least 10 years since I wrote this app but from memory, the raw jsdoc output is flat, just an array of doclet metadata objects which have no structure.. this was not useful for feeding into a template engine like Handlebars where the output documentation was required to have structure (e.g. module -> exported class -> public/private properties/methods etc).. So the jsdoc-parse module was created to transform jsdoc output into something with structure, where the tree structure is represented using generated, unique doclet id
values and memberof
properties pointing to the unique doclet id
they are a member of..
So, in this case the code encounters doclet id: Foo
then sets about searching for its children (doclets that are memberof: Foo
).. A child is found (id: Foo
), so the code checks whether this child has children (doclets with memberof: Foo
) - it does.. itself..
You get the idea - recursive.. Anyway, I'll implement a fix then post again later.
A fix for this is implmemented and will be in the next version.. will post again once that goes live.. In the meantime, you can test the prerelease:
npm install jsdoc-to-markdown@next
I had a whole bunch of source files that caused jsdoc2md to throw
RangeError: Maximum call stack size exceeded.
when I attempted to render them. I managed to reduce the issue down to this minimal reproduction (with v 5.0.3):Foo.js:
template.hbs:
Result:
It doesn't appear to matter whether or not there is content in the jsdoc block, it seems like the only things required to trigger it are a doc block before the class and the class must have a constructor.
More info, if it's helpful...
```sh $ jsdoc2md --version 5.0.3 $ jsdoc2md --files ./Foo.js --template ./template.hbs --config { "files": [ "./Foo.js" ], "template": "./template.hbs" } $ jsdoc2md --files ./Foo.js --template ./template.hbs --json [ { "id": "Foo", "longname": "Foo", "name": "Foo", "kind": "class", "scope": "instance", "memberof": "Foo", "meta": { "lineno": 1, "filename": "Foo.js", "path": "/Users/jasonk/jsdoc2md-repro" }, "order": 0 } ] $ jsdoc2md --files ./Foo.js --template ./template.hbs --jsdoc [ { "comment": "/** */", "meta": { "range": [ 7, 44 ], "filename": "Foo.js", "lineno": 1, "columnno": 7, "path": "/Users/jasonk/jsdoc2md-repro", "code": { "id": "astnode100000002", "name": "exports.Foo", "type": "ClassDeclaration" } }, "name": "Foo", "longname": "Foo", "kind": "class", "scope": "global", "undocumented": true }, { "comment": "", "meta": { "range": [ 14, 44 ], "filename": "Foo.js", "lineno": 1, "columnno": 14, "path": "/Users/jasonk/jsdoc2md-repro", "code": { "id": "astnode100000003", "name": "Foo", "type": "ClassDeclaration", "paramnames": [] } }, "undocumented": true, "name": "Foo", "longname": "Foo", "kind": "class", "scope": "global" }, { "comment": "", "meta": { "range": [ 26, 42 ], "filename": "Foo.js", "lineno": 1, "columnno": 26, "path": "/Users/jasonk/jsdoc2md-repro", "code": { "id": "astnode100000006", "name": "exports.Foo", "type": "MethodDefinition", "paramnames": [] }, "vars": { "": null } }, "undocumented": true, "name": "Foo", "longname": "Foo#Foo", "kind": "class", "memberof": "Foo", "scope": "instance", "params": [] }, { "comment": "", "meta": { "range": [ 26, 42 ], "filename": "Foo.js", "lineno": 1, "columnno": 26, "path": "/Users/jasonk/jsdoc2md-repro", "code": { "id": "astnode100000006", "name": "exports.Foo", "type": "MethodDefinition", "paramnames": [] } }, "name": "Foo", "longname": "Foo", "kind": "class", "memberof": "Foo", "scope": "instance" }, { "kind": "package", "longname": "package:undefined", "files": [ "/Users/jasonk/jsdoc2md-repro/Foo.js" ] } ] ```