gruntjs / gruntjs/grunt

grunt.config breaks real object when config gets values

Open
#1,106 8 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
12.2k
Forks
1.5k
PR merge metrics
No merged PRs in 30d

Description

See [marked#376](https://github.com/chjj/marked/issues/376) for the context of this issue.

I have searched why a real object (`new marked.Renderer()` in my case) losts its member functions when `grunt.config(['assemble','options')` is called and I came to what follows (from `grunt/node_modules/grunt-legacy-util/index.js`) :

``` javascript
...
// Recurse through objects and arrays, executing fn for each non-object.
util.recurse = function recurse(value, fn, fnContinue) {
var obj;
if (fnContinue && fnContinue(value) === false) {
// Skip value if necessary.
return value;
} else if (util.kindOf(value) === 'array') {
// If value is an array, recurse.
return value.map(function(value) {
return recurse(value, fn, fnContinue);
});
} else if (util.kindOf(value) === 'object') {
// If value is an object, recurse.
obj = {};
Object.keys(value).forEach(function(key) {
obj[key] = recurse(value[key], fn, fnContinue);
});
return obj;
} else {
// Otherwise pass value into fn and return.
return fn(value);
}
};
...
```

when a real object is copied, `recurse` goes through its properties without never taking the member functions.

I fixed the bug with the following by testing if a constructor exists on the object :

``` javascript
// Recurse through objects and arrays, executing fn for each non-object.
util.recurse = function recurse(value, fn, fnContinue) {
var obj;
if (fnContinue && fnContinue(value) === false) {
// Skip value if necessary.
return value;
} else if (util.kindOf(value) === 'array') {
// If value is an array, recurse.
return value.map(function(value) {
return recurse(value, fn, fnContinue);
});
} else if (util.kindOf(value) === 'object') {
// ------------------------------------------------------------
// Patch : If value is an instance of, just pass value
if (util.kindOf(value.constructor) === 'function') {
return value;
}
// ------------------------------------------------------------

// If value is an object, recurse.
obj = {};
Object.keys(value).forEach(function(key) {
obj[key] = recurse(value[key], fn, fnContinue);
});
return obj;
} else {
// Otherwise pass value into fn and return.
return fn(value);
}
};
...
```

Any thought on whether this fix is correct or not ?
And if not, what would be the correct one ?

Contributor guide

Open the contributing guide

Research direction

The behavior is in grunt/node_modules/grunt-legacy-util/index.js, in util.recurse; start there and compare handling of plain objects with instances such as new marked.Renderer(). Confirm that grunt.config preserves instance methods while still recursing through ordinary objects and arrays; no test file is named in the issue.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.