Update readConfig to use fs.access so the async path is fully async.

This commit is contained in:
David Anson 2021-08-12 20:43:18 -07:00
parent 211f09afbc
commit 709e314836
5 changed files with 108 additions and 28 deletions

View file

@ -649,7 +649,7 @@ In advanced scenarios, it may be desirable to bypass the default file system API
If a custom file system implementation is provided, `markdownlint` will use that If a custom file system implementation is provided, `markdownlint` will use that
instead of invoking `require("fs")`. instead of invoking `require("fs")`.
Note: The only methods called are `readFile`, `readFileSync`, and `accessSync`. Note: The only methods called are `readFile`, `readFileSync`, `access`, and `accessSync`.
#### callback #### callback

View file

@ -1638,6 +1638,33 @@ function parseConfiguration(name, content, parsers) {
message: message message: message
}; };
} }
/**
* Resolve referenced "extends" path in a configuration file
* using path.resolve() with require.resolve() as a fallback.
*
* @param {string} configFile Configuration file name.
* @param {string} referenceId Referenced identifier to resolve.
* @param {Object} fs File system implementation.
* @param {ResolveConfigExtendsCallback} [callback] Callback (err, result)
* function.
* @returns {void}
*/
function resolveConfigExtends(configFile, referenceId, fs, callback) {
var configFileDirname = path.dirname(configFile);
var resolvedExtendsFile = path.resolve(configFileDirname, referenceId);
fs.access(resolvedExtendsFile, function (err) {
if (err) {
// Not a file, try require.resolve
try {
return callback(null, dynamicRequire.resolve(referenceId, { "paths": [configFileDirname] }));
}
catch (_a) {
// Unable to resolve, use resolvedExtendsFile
}
}
return callback(null, resolvedExtendsFile);
});
}
/** /**
* Resolve referenced "extends" path in a configuration file * Resolve referenced "extends" path in a configuration file
* using path.resolve() with require.resolve() as a fallback. * using path.resolve() with require.resolve() as a fallback.
@ -1647,7 +1674,7 @@ function parseConfiguration(name, content, parsers) {
* @param {Object} fs File system implementation. * @param {Object} fs File system implementation.
* @returns {string} Resolved path to file. * @returns {string} Resolved path to file.
*/ */
function resolveConfigExtends(configFile, referenceId, fs) { function resolveConfigExtendsSync(configFile, referenceId, fs) {
var configFileDirname = path.dirname(configFile); var configFileDirname = path.dirname(configFile);
var resolvedExtendsFile = path.resolve(configFileDirname, referenceId); var resolvedExtendsFile = path.resolve(configFileDirname, referenceId);
try { try {
@ -1705,13 +1732,12 @@ function readConfig(file, parsers, fs, callback) {
var configExtends = config["extends"]; var configExtends = config["extends"];
if (configExtends) { if (configExtends) {
delete config["extends"]; delete config["extends"];
var resolvedExtends = resolveConfigExtends(file, configExtends, fs); return resolveConfigExtends(file, configExtends, fs, function (_, resolvedExtends) { return readConfig(resolvedExtends, parsers, fs, function (errr, extendsConfig) {
return readConfig(resolvedExtends, parsers, fs, function (errr, extendsConfig) {
if (errr) { if (errr) {
return callback(errr); return callback(errr);
} }
return callback(null, __assign(__assign({}, extendsConfig), config)); return callback(null, __assign(__assign({}, extendsConfig), config));
}); }); });
} }
return callback(null, config); return callback(null, config);
}); });
@ -1752,7 +1778,7 @@ function readConfigSync(file, parsers, fs) {
var configExtends = config["extends"]; var configExtends = config["extends"];
if (configExtends) { if (configExtends) {
delete config["extends"]; delete config["extends"];
var resolvedExtends = resolveConfigExtends(file, configExtends, fs); var resolvedExtends = resolveConfigExtendsSync(file, configExtends, fs);
return __assign(__assign({}, readConfigSync(resolvedExtends, parsers, fs)), config); return __assign(__assign({}, readConfigSync(resolvedExtends, parsers, fs)), config);
} }
return config; return config;

10
lib/markdownlint.d.ts vendored
View file

@ -8,7 +8,7 @@ export = markdownlint;
*/ */
declare function markdownlint(options: Options, callback: LintCallback): void; declare function markdownlint(options: Options, callback: LintCallback): void;
declare namespace markdownlint { declare namespace markdownlint {
export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, RuleFunction, RuleParams, MarkdownItToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintCallback, Configuration, RuleConfiguration, ConfigurationParser, ReadConfigCallback }; export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, RuleFunction, RuleParams, MarkdownItToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintCallback, Configuration, RuleConfiguration, ConfigurationParser, ReadConfigCallback, ResolveConfigExtendsCallback };
} }
/** /**
* Configuration options. * Configuration options.
@ -58,7 +58,7 @@ type Options = {
fs?: any; fs?: any;
}; };
/** /**
* Called with the result of the lint operation. * Called with the result of the lint function.
*/ */
type LintCallback = (err: Error | null, results?: LintResults) => void; type LintCallback = (err: Error | null, results?: LintResults) => void;
/** /**
@ -355,9 +355,13 @@ type RuleConfiguration = boolean | any;
*/ */
type ConfigurationParser = (text: string) => Configuration; type ConfigurationParser = (text: string) => Configuration;
/** /**
* Called with the result of the readConfig operation. * Called with the result of the readConfig function.
*/ */
type ReadConfigCallback = (err: Error | null, config?: Configuration) => void; type ReadConfigCallback = (err: Error | null, config?: Configuration) => void;
/**
* Called with the result of the resolveConfigExtends function.
*/
type ResolveConfigExtendsCallback = (err: Error | null, path?: string) => void;
/** /**
* Lint specified Markdown files. * Lint specified Markdown files.
* *

View file

@ -926,6 +926,36 @@ function parseConfiguration(name, content, parsers) {
}; };
} }
/**
* Resolve referenced "extends" path in a configuration file
* using path.resolve() with require.resolve() as a fallback.
*
* @param {string} configFile Configuration file name.
* @param {string} referenceId Referenced identifier to resolve.
* @param {Object} fs File system implementation.
* @param {ResolveConfigExtendsCallback} [callback] Callback (err, result)
* function.
* @returns {void}
*/
function resolveConfigExtends(configFile, referenceId, fs, callback) {
const configFileDirname = path.dirname(configFile);
const resolvedExtendsFile = path.resolve(configFileDirname, referenceId);
fs.access(resolvedExtendsFile, (err) => {
if (err) {
// Not a file, try require.resolve
try {
return callback(null, dynamicRequire.resolve(
referenceId,
{ "paths": [ configFileDirname ] }
));
} catch {
// Unable to resolve, use resolvedExtendsFile
}
}
return callback(null, resolvedExtendsFile);
});
}
/** /**
* Resolve referenced "extends" path in a configuration file * Resolve referenced "extends" path in a configuration file
* using path.resolve() with require.resolve() as a fallback. * using path.resolve() with require.resolve() as a fallback.
@ -935,7 +965,7 @@ function parseConfiguration(name, content, parsers) {
* @param {Object} fs File system implementation. * @param {Object} fs File system implementation.
* @returns {string} Resolved path to file. * @returns {string} Resolved path to file.
*/ */
function resolveConfigExtends(configFile, referenceId, fs) { function resolveConfigExtendsSync(configFile, referenceId, fs) {
const configFileDirname = path.dirname(configFile); const configFileDirname = path.dirname(configFile);
const resolvedExtendsFile = path.resolve(configFileDirname, referenceId); const resolvedExtendsFile = path.resolve(configFileDirname, referenceId);
try { try {
@ -994,8 +1024,15 @@ function readConfig(file, parsers, fs, callback) {
const configExtends = config.extends; const configExtends = config.extends;
if (configExtends) { if (configExtends) {
delete config.extends; delete config.extends;
const resolvedExtends = resolveConfigExtends(file, configExtends, fs); return resolveConfigExtends(
return readConfig(resolvedExtends, parsers, fs, (errr, extendsConfig) => { file,
configExtends,
fs,
(_, resolvedExtends) => readConfig(
resolvedExtends,
parsers,
fs,
(errr, extendsConfig) => {
if (errr) { if (errr) {
return callback(errr); return callback(errr);
} }
@ -1003,7 +1040,9 @@ function readConfig(file, parsers, fs, callback) {
...extendsConfig, ...extendsConfig,
...config ...config
}); });
}); }
)
);
} }
return callback(null, config); return callback(null, config);
}); });
@ -1047,7 +1086,7 @@ function readConfigSync(file, parsers, fs) {
const configExtends = config.extends; const configExtends = config.extends;
if (configExtends) { if (configExtends) {
delete config.extends; delete config.extends;
const resolvedExtends = resolveConfigExtends(file, configExtends, fs); const resolvedExtends = resolveConfigExtendsSync(file, configExtends, fs);
return { return {
...readConfigSync(resolvedExtends, parsers, fs), ...readConfigSync(resolvedExtends, parsers, fs),
...config ...config
@ -1221,7 +1260,7 @@ module.exports = markdownlint;
*/ */
/** /**
* Called with the result of the lint operation. * Called with the result of the lint function.
* *
* @callback LintCallback * @callback LintCallback
* @param {Error | null} err Error object or null. * @param {Error | null} err Error object or null.
@ -1251,10 +1290,19 @@ module.exports = markdownlint;
*/ */
/** /**
* Called with the result of the readConfig operation. * Called with the result of the readConfig function.
* *
* @callback ReadConfigCallback * @callback ReadConfigCallback
* @param {Error | null} err Error object or null. * @param {Error | null} err Error object or null.
* @param {Configuration} [config] Configuration object. * @param {Configuration} [config] Configuration object.
* @returns {void} * @returns {void}
*/ */
/**
* Called with the result of the resolveConfigExtends function.
*
* @callback ResolveConfigExtendsCallback
* @param {Error | null} err Error object or null.
* @param {string} [path] Resolved path to file.
* @returns {void}
*/

View file

@ -1155,8 +1155,9 @@ test.cb("configCustomFileSystem", (t) => {
"MD002": true "MD002": true
}; };
const fsApi = { const fsApi = {
"accessSync": (p) => { "access": (p, m, cb) => {
t.is(p, extended); t.is(path.resolve(p), extended);
return (cb || m)();
}, },
"readFile": (p, o, cb) => { "readFile": (p, o, cb) => {
switch (p) { switch (p) {
@ -1455,7 +1456,7 @@ test("configCustomFileSystemSync", (t) => {
}; };
const fsApi = { const fsApi = {
"accessSync": (p) => { "accessSync": (p) => {
t.is(p, extended); t.is(path.resolve(p), extended);
}, },
"readFileSync": (p) => { "readFileSync": (p) => {
switch (p) { switch (p) {
@ -1520,8 +1521,9 @@ test.cb("configCustomFileSystemPromise", (t) => {
"MD002": true "MD002": true
}; };
const fsApi = { const fsApi = {
"accessSync": (p) => { "access": (p, m, cb) => {
t.is(p, extended); t.is(path.resolve(p), extended);
return (cb || m)();
}, },
"readFile": (p, o, cb) => { "readFile": (p, o, cb) => {
switch (p) { switch (p) {