Update custom rule params.config object to be a copy of the configuration object to prevent modification of user data by custom rule, add test case to validate config.params value for custom rules in different scenarios.

This commit is contained in:
David Anson 2025-09-21 15:25:25 -07:00
parent 0916e25c27
commit c07a63117a
2 changed files with 58 additions and 22 deletions

View file

@ -545,7 +545,7 @@ function lintContent(
...paramsBase,
...tokens,
parsers,
"config": effectiveConfig[ruleName]
"config": (typeof effectiveConfig[ruleName] === "object") ? { ...effectiveConfig[ruleName] } : effectiveConfig[ruleName]
};
// eslint-disable-next-line jsdoc/require-jsdoc
function throwError(property) {

View file

@ -2016,7 +2016,7 @@ test("customRulesParamsAreFrozen", (t) => {
});
test("customRulesParamsAreStable", (t) => {
t.plan(4);
t.plan(6);
const config1 = { "value1": 10 };
const config2 = { "value2": 20 };
/** @type {import("markdownlint").Options} */
@ -2038,17 +2038,10 @@ test("customRulesParamsAreStable", (t) => {
"function":
(params) => {
const { config } = params;
t.deepEqual(
config,
config1,
`Unexpected config in sync path: ${config}.`
);
t.deepEqual(config, config1, `Unexpected config in sync path: ${config}.`);
return Promise.resolve().then(() => {
t.deepEqual(
config,
config1,
`Unexpected config in async path: ${config}.`
);
t.deepEqual(config, config1, `Unexpected config in async path: ${config}.`);
config.extra = 1;
});
}
},
@ -2061,17 +2054,10 @@ test("customRulesParamsAreStable", (t) => {
"function":
(params) => {
const { config } = params;
t.deepEqual(
config,
config2,
`Unexpected config in sync path: ${config}.`
);
t.deepEqual(config, config2, `Unexpected config in sync path: ${config}.`);
return Promise.resolve().then(() => {
t.deepEqual(
config,
config2,
`Unexpected config in async path: ${config}.`
);
t.deepEqual(config, config2, `Unexpected config in async path: ${config}.`);
config.extra = 2;
});
}
}
@ -2080,6 +2066,56 @@ test("customRulesParamsAreStable", (t) => {
"string": "# Heading"
}
};
return lintPromise(options).then(() => {
t.deepEqual(config1, { "value1": 10 });
t.deepEqual(config2, { "value2": 20 });
});
});
test("customRulesParamsAreExpected", (t) => {
t.plan(4);
/** @type {import("markdownlint").Options} */
const options = {
"config": {
// "name1" default enabled
"name2": true,
"name3": {},
"name4": { "value": 10 }
},
"customRules": [
{
"names": [ "name1" ],
"description": "description",
"tags": [ "tag" ],
"parser": "none",
"function": (params) => t.deepEqual(params.config, true)
},
{
"names": [ "name2" ],
"description": "description",
"tags": [ "tag" ],
"parser": "none",
"function": (params) => t.deepEqual(params.config, {})
},
{
"names": [ "name3" ],
"description": "description",
"tags": [ "tag" ],
"parser": "none",
"function": (params) => t.deepEqual(params.config, {})
},
{
"names": [ "name4" ],
"description": "description",
"tags": [ "tag" ],
"parser": "none",
"function": (params) => t.deepEqual(params.config, { "value": 10 })
}
],
"strings": {
"string": "# Heading"
}
};
return lintPromise(options);
});