From 709e3148366000ab374939c9cc0ce2583611d114 Mon Sep 17 00:00:00 2001 From: David Anson Date: Thu, 12 Aug 2021 20:43:18 -0700 Subject: [PATCH] Update readConfig to use fs.access so the async path is fully async. --- README.md | 2 +- demo/markdownlint-browser.js | 36 ++++++++++++++--- lib/markdownlint.d.ts | 10 +++-- lib/markdownlint.js | 76 +++++++++++++++++++++++++++++------- test/markdownlint-test.js | 12 +++--- 5 files changed, 108 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 1054ef62..86fc318c 100644 --- a/README.md +++ b/README.md @@ -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 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 diff --git a/demo/markdownlint-browser.js b/demo/markdownlint-browser.js index 5080492b..3560c174 100644 --- a/demo/markdownlint-browser.js +++ b/demo/markdownlint-browser.js @@ -1638,6 +1638,33 @@ function parseConfiguration(name, content, parsers) { 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 * using path.resolve() with require.resolve() as a fallback. @@ -1647,7 +1674,7 @@ function parseConfiguration(name, content, parsers) { * @param {Object} fs File system implementation. * @returns {string} Resolved path to file. */ -function resolveConfigExtends(configFile, referenceId, fs) { +function resolveConfigExtendsSync(configFile, referenceId, fs) { var configFileDirname = path.dirname(configFile); var resolvedExtendsFile = path.resolve(configFileDirname, referenceId); try { @@ -1705,13 +1732,12 @@ function readConfig(file, parsers, fs, callback) { var configExtends = config["extends"]; if (configExtends) { delete config["extends"]; - var resolvedExtends = resolveConfigExtends(file, configExtends, fs); - return readConfig(resolvedExtends, parsers, fs, function (errr, extendsConfig) { + return resolveConfigExtends(file, configExtends, fs, function (_, resolvedExtends) { return readConfig(resolvedExtends, parsers, fs, function (errr, extendsConfig) { if (errr) { return callback(errr); } return callback(null, __assign(__assign({}, extendsConfig), config)); - }); + }); }); } return callback(null, config); }); @@ -1752,7 +1778,7 @@ function readConfigSync(file, parsers, fs) { var configExtends = config["extends"]; if (configExtends) { delete config["extends"]; - var resolvedExtends = resolveConfigExtends(file, configExtends, fs); + var resolvedExtends = resolveConfigExtendsSync(file, configExtends, fs); return __assign(__assign({}, readConfigSync(resolvedExtends, parsers, fs)), config); } return config; diff --git a/lib/markdownlint.d.ts b/lib/markdownlint.d.ts index be83dd54..7c9d19b7 100644 --- a/lib/markdownlint.d.ts +++ b/lib/markdownlint.d.ts @@ -8,7 +8,7 @@ export = markdownlint; */ declare function markdownlint(options: Options, callback: LintCallback): void; 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. @@ -58,7 +58,7 @@ type Options = { 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; /** @@ -355,9 +355,13 @@ type RuleConfiguration = boolean | any; */ 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; +/** + * Called with the result of the resolveConfigExtends function. + */ +type ResolveConfigExtendsCallback = (err: Error | null, path?: string) => void; /** * Lint specified Markdown files. * diff --git a/lib/markdownlint.js b/lib/markdownlint.js index dab39ed1..6b2c6db0 100644 --- a/lib/markdownlint.js +++ b/lib/markdownlint.js @@ -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 * using path.resolve() with require.resolve() as a fallback. @@ -935,7 +965,7 @@ function parseConfiguration(name, content, parsers) { * @param {Object} fs File system implementation. * @returns {string} Resolved path to file. */ -function resolveConfigExtends(configFile, referenceId, fs) { +function resolveConfigExtendsSync(configFile, referenceId, fs) { const configFileDirname = path.dirname(configFile); const resolvedExtendsFile = path.resolve(configFileDirname, referenceId); try { @@ -994,16 +1024,25 @@ function readConfig(file, parsers, fs, callback) { const configExtends = config.extends; if (configExtends) { delete config.extends; - const resolvedExtends = resolveConfigExtends(file, configExtends, fs); - return readConfig(resolvedExtends, parsers, fs, (errr, extendsConfig) => { - if (errr) { - return callback(errr); - } - return callback(null, { - ...extendsConfig, - ...config - }); - }); + return resolveConfigExtends( + file, + configExtends, + fs, + (_, resolvedExtends) => readConfig( + resolvedExtends, + parsers, + fs, + (errr, extendsConfig) => { + if (errr) { + return callback(errr); + } + return callback(null, { + ...extendsConfig, + ...config + }); + } + ) + ); } return callback(null, config); }); @@ -1047,7 +1086,7 @@ function readConfigSync(file, parsers, fs) { const configExtends = config.extends; if (configExtends) { delete config.extends; - const resolvedExtends = resolveConfigExtends(file, configExtends, fs); + const resolvedExtends = resolveConfigExtendsSync(file, configExtends, fs); return { ...readConfigSync(resolvedExtends, parsers, fs), ...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 * @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 * @param {Error | null} err Error object or null. * @param {Configuration} [config] Configuration object. * @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} + */ diff --git a/test/markdownlint-test.js b/test/markdownlint-test.js index dbe1c4a8..b00cc857 100644 --- a/test/markdownlint-test.js +++ b/test/markdownlint-test.js @@ -1155,8 +1155,9 @@ test.cb("configCustomFileSystem", (t) => { "MD002": true }; const fsApi = { - "accessSync": (p) => { - t.is(p, extended); + "access": (p, m, cb) => { + t.is(path.resolve(p), extended); + return (cb || m)(); }, "readFile": (p, o, cb) => { switch (p) { @@ -1455,7 +1456,7 @@ test("configCustomFileSystemSync", (t) => { }; const fsApi = { "accessSync": (p) => { - t.is(p, extended); + t.is(path.resolve(p), extended); }, "readFileSync": (p) => { switch (p) { @@ -1520,8 +1521,9 @@ test.cb("configCustomFileSystemPromise", (t) => { "MD002": true }; const fsApi = { - "accessSync": (p) => { - t.is(p, extended); + "access": (p, m, cb) => { + t.is(path.resolve(p), extended); + return (cb || m)(); }, "readFile": (p, o, cb) => { switch (p) {