Skip to content

Commit

Permalink
Refactor to include options/configuration file name in corresponding …
Browse files Browse the repository at this point in the history
…error messages (e.g., parse failure), avoid a few calls to fs.promises.access, make things slightly more consistent.
  • Loading branch information
DavidAnson committed Feb 4, 2024
1 parent 2515ee2 commit f30d989
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 96 deletions.
153 changes: 80 additions & 73 deletions markdownlint-cli2.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,18 @@ const getYamlParse = () => require("./parsers/yaml-parse.js");
// Gets an ordered array of parsers
const getParsers = () => require("./parsers/parsers.js");

// Negate a glob
// Negates a glob
const negateGlob = (glob) => `!${glob}`;

// Throws a meaningful exception for an unusable configuration file
const throwForConfigurationFile = (file, error) => {
throw new Error(
`Unable to use configuration file '${file}'; ${error?.message}`,
// @ts-ignore
{ "cause": error }
);
};

// Return a posix path (even on Windows)
const posixPath = (p) => p.split(pathDefault.sep).join(pathPosix.sep);

Expand Down Expand Up @@ -75,11 +84,12 @@ const readConfig = (fs, dir, name, otherwise) => () => {
};

// Import or resolve/require a module ID with a custom directory in the path
const importOrRequireResolve = async (dirs, id, noRequire) => {
const importOrRequireResolve = async (dirOrDirs, id, noRequire) => {
if (typeof id === "string") {
if (noRequire) {
return null;
}
const dirs = Array.isArray(dirOrDirs) ? dirOrDirs : [ dirOrDirs ];
const expandId = expandTildePath(id);
const errors = [];
try {
Expand Down Expand Up @@ -126,10 +136,10 @@ const importOrRequireIdsAndParams = (dirs, idsAndParams, noRequire) => (

// Import or require a JavaScript file and return the exported object
const importOrRequireConfig = (fs, dir, name, noRequire, otherwise) => () => {
const id = pathPosix.join(dir, name);
return fs.promises.access(id).
const file = pathPosix.join(dir, name);
return fs.promises.access(file).
then(
() => (noRequire ? {} : importOrRequireResolve([ dir ], id)),
() => importOrRequireResolve(dir, name, noRequire),
otherwise
);
};
Expand All @@ -154,47 +164,43 @@ const readOptionsOrConfig = async (configPath, fs, noRequire) => {
const dirname = pathPosix.dirname(configPath);
let options = null;
let config = null;
if (basename.endsWith(".markdownlint-cli2.jsonc")) {
options = getJsoncParse()(await fs.promises.readFile(configPath, utf8));
} else if (basename.endsWith(".markdownlint-cli2.yaml")) {
options = getYamlParse()(await fs.promises.readFile(configPath, utf8));
} else if (
basename.endsWith(".markdownlint-cli2.cjs") ||
basename.endsWith(".markdownlint-cli2.mjs")
) {
options = await (
importOrRequireConfig(fs, dirname, basename, noRequire, noop)()
);
} else if (
basename.endsWith(".markdownlint.jsonc") ||
basename.endsWith(".markdownlint.json") ||
basename.endsWith(".markdownlint.yaml") ||
basename.endsWith(".markdownlint.yml")
) {
config =
await markdownlintReadConfig(configPath, getParsers(), fs);
} else if (
basename.endsWith(".markdownlint.cjs") ||
basename.endsWith(".markdownlint.mjs")
) {
config = await (
importOrRequireConfig(fs, dirname, basename, noRequire, noop)()
);
} else {
throw new Error(
`Configuration file "${configPath}" is unrecognized; ` +
"its name should be (or end with) one of the supported types " +
"(e.g., \".markdownlint.json\" or \"example.markdownlint-cli2.jsonc\")."
);
try {
if (basename.endsWith(".markdownlint-cli2.jsonc")) {
options = getJsoncParse()(await fs.promises.readFile(configPath, utf8));
} else if (basename.endsWith(".markdownlint-cli2.yaml")) {
options = getYamlParse()(await fs.promises.readFile(configPath, utf8));
} else if (
basename.endsWith(".markdownlint-cli2.cjs") ||
basename.endsWith(".markdownlint-cli2.mjs")
) {
options = await importOrRequireResolve(dirname, basename, noRequire);
} else if (
basename.endsWith(".markdownlint.jsonc") ||
basename.endsWith(".markdownlint.json") ||
basename.endsWith(".markdownlint.yaml") ||
basename.endsWith(".markdownlint.yml")
) {
config = await markdownlintReadConfig(configPath, getParsers(), fs);
} else if (
basename.endsWith(".markdownlint.cjs") ||
basename.endsWith(".markdownlint.mjs")
) {
config = await importOrRequireResolve(dirname, basename, noRequire);
} else {
throw new Error(
"File name should be (or end with) one of the supported types " +
"(e.g., '.markdownlint.json' or 'example.markdownlint-cli2.jsonc')."
);
}
} catch (error) {
throwForConfigurationFile(configPath, error);
}

if (options) {
if (options.config) {
options.config = await getExtendedConfig(options.config, configPath, fs);
}
return options;
}

config = await getExtendedConfig(config, configPath, fs);
return { config };
};
Expand Down Expand Up @@ -289,6 +295,7 @@ const getAndProcessDirInfo = (
noRequire,
allowPackageJson
) => {
// Create dirInfo
let dirInfo = dirToDirInfo[dir];
if (!dirInfo) {
dirInfo = {
Expand All @@ -302,46 +309,41 @@ const getAndProcessDirInfo = (
dirToDirInfo[dir] = dirInfo;

// Load markdownlint-cli2 object(s)
const markdownlintCli2Jsonc =
pathPosix.join(dir, ".markdownlint-cli2.jsonc");
const markdownlintCli2Yaml =
pathPosix.join(dir, ".markdownlint-cli2.yaml");
const markdownlintCli2Jsonc = pathPosix.join(dir, ".markdownlint-cli2.jsonc");
const markdownlintCli2Yaml = pathPosix.join(dir, ".markdownlint-cli2.yaml");
const markdownlintCli2Cjs = pathPosix.join(dir, ".markdownlint-cli2.cjs");
const markdownlintCli2Mjs = pathPosix.join(dir, ".markdownlint-cli2.mjs");
const packageJson = pathPosix.join(dir, "package.json");
let file = "[UNKNOWN]";
// eslint-disable-next-line no-return-assign
const captureFile = (f) => file = f;
tasks.push(
fs.promises.access(markdownlintCli2Jsonc).
fs.promises.access(captureFile(markdownlintCli2Jsonc)).
then(
() => fs.promises.
readFile(markdownlintCli2Jsonc, utf8).
then(getJsoncParse()),
() => fs.promises.access(markdownlintCli2Yaml).
() => fs.promises.readFile(file, utf8).then(getJsoncParse()),
() => fs.promises.access(captureFile(markdownlintCli2Yaml)).
then(
() => fs.promises.
readFile(markdownlintCli2Yaml, utf8).
then(getYamlParse()),
importOrRequireConfig(
fs,
dir,
".markdownlint-cli2.cjs",
noRequire,
importOrRequireConfig(
fs,
dir,
".markdownlint-cli2.mjs",
noRequire,
() => (allowPackageJson
? fs.promises.access(packageJson)
// eslint-disable-next-line prefer-promise-reject-errors
: Promise.reject()
).
() => fs.promises.readFile(file, utf8).then(getYamlParse()),
() => fs.promises.access(captureFile(markdownlintCli2Cjs)).
then(
() => importOrRequireResolve(dir, file, noRequire),
() => fs.promises.access(captureFile(markdownlintCli2Mjs)).
then(
() => fs.promises.
readFile(packageJson, utf8).
then(getJsoncParse()).
then((obj) => obj[packageName]),
noop
() => importOrRequireResolve(dir, file, noRequire),
() => (allowPackageJson
? fs.promises.access(captureFile(packageJson))
// eslint-disable-next-line prefer-promise-reject-errors
: Promise.reject()
).
then(
() => fs.promises.
readFile(file, utf8).
then(getJsoncParse()).
then((obj) => obj[packageName]),
noop
)
)
)
)
)
).
then((options) => {
Expand All @@ -350,14 +352,17 @@ const getAndProcessDirInfo = (
options.config &&
getExtendedConfig(
options.config,
// Just needs to identify a file in the right directory
// Just need to identify a file in the right directory
markdownlintCli2Jsonc,
fs
).
then((config) => {
options.config = config;
});
})
.catch((error) => {
throwForConfigurationFile(file, error);
})
);

// Load markdownlint object(s)
Expand Down Expand Up @@ -402,6 +407,8 @@ const getAndProcessDirInfo = (
})
);
}

// Return dirInfo
return dirInfo;
};

Expand Down
45 changes: 22 additions & 23 deletions test/markdownlint-cli2-test-cases.js
Original file line number Diff line number Diff line change
Expand Up @@ -346,29 +346,29 @@ const testCases = ({
"name": "markdownlint-json-invalid",
"args": [ ".*" ],
"exitCode": 2,
"stderrRe": /Unable to parse JSONC content/u
"stderrRe": /'[^']*\.markdownlint\.json'.*Unable to parse JSONC content/u
});

testCase({
"name": "markdownlint-yaml-invalid",
"args": [ ".*" ],
"exitCode": 2,
"stderrRe": /Map keys must be unique/u
"stderrRe": /'[^']*\.markdownlint\.yaml'.*Map keys must be unique/u
});

testCase({
"name": "markdownlint-cjs-invalid",
"args": [ ".*" ],
"exitCode": 2,
"stderrRe": /Unable to require or import module '/u,
"stderrRe": /Unable to require or import module '.*\.markdownlint\.cjs'/u,
"usesRequire": true
});

testCase({
"name": "markdownlint-mjs-invalid",
"args": [ ".*" ],
"exitCode": 2,
"stderrRe": /Unable to require or import module '/u,
"stderrRe": /Unable to require or import module '.*\.markdownlint\.mjs'/u,
"usesRequire": true
});

Expand All @@ -388,7 +388,7 @@ const testCases = ({
"name": "markdownlint-cli2-jsonc-mismatch",
"args": [ "viewme.md" ],
"exitCode": 2,
"stderrRe": /Unable to parse JSONC content/u
"stderrRe": /'[^']*\.markdownlint-cli2\.jsonc'.*Unable to parse JSONC content/u
});

testCase({
Expand All @@ -415,7 +415,7 @@ const testCases = ({
"name": "markdownlint-cli2-jsonc-mismatch-config",
"args": [ "--config", "../markdownlint-cli2-jsonc-mismatch/.markdownlint-cli2.jsonc", "viewme.md" ],
"exitCode": 2,
"stderrRe": /Unable to parse JSONC content/u,
"stderrRe": /'[^']*\.markdownlint-cli2\.jsonc'.*Unable to parse JSONC content/u,
"cwd": "no-config",
});

Expand Down Expand Up @@ -446,7 +446,7 @@ const testCases = ({
"name": "markdownlint-cli2-jsonc-invalid",
"args": [ ".*" ],
"exitCode": 2,
"stderrRe": /Unable to parse JSONC content/u
"stderrRe": /'[^']*\.markdownlint-cli2\.jsonc'.*Unable to parse JSONC content/u
});

testCase({
Expand All @@ -469,7 +469,7 @@ const testCases = ({
"name": "markdownlint-cli2-yaml-invalid",
"args": [ ".*" ],
"exitCode": 2,
"stderrRe": /Map keys must be unique/u
"stderrRe": /'[^']*\.markdownlint-cli2\.yaml'.*Map keys must be unique/u
});

testCase({
Expand All @@ -490,15 +490,15 @@ const testCases = ({
"name": "markdownlint-cli2-cjs-invalid",
"args": [ ".*" ],
"exitCode": 2,
"stderrRe": /Unable to require or import module '/u,
"stderrRe": /'[^']*\.markdownlint-cli2\.cjs'.*Unable to require or import module '/u,
"usesRequire": true
});

testCase({
"name": "markdownlint-cli2-mjs-invalid",
"args": [ ".*" ],
"exitCode": 2,
"stderrRe": /Unable to require or import module '/u,
"stderrRe": /'[^']*\.markdownlint-cli2\.mjs'.*Unable to require or import module '/u,
"usesRequire": true
});

Expand Down Expand Up @@ -665,25 +665,24 @@ const testCases = ({
});
}

const unexpectedJsonRe = /Unable to parse JSONC content/u;
const unableToRequireRe = /Unable to require or import module/u;
const unableToParseRe = /Unable to parse/u;
const unableToParseJsonc = "Unable to parse JSONC content";
const unableToRequireOrImport = "Unable to require or import module";
const invalidConfigFiles = [
[ "invalid.markdownlint-cli2.jsonc", unexpectedJsonRe ],
[ "invalid.markdownlint-cli2.cjs", unableToRequireRe ],
[ "invalid.markdownlint-cli2.mjs", unableToRequireRe ],
[ "invalid.markdownlint.json", unableToParseRe ],
[ "invalid.markdownlint.yaml", unableToParseRe ],
[ "invalid.markdownlint.cjs", unableToRequireRe ],
[ "invalid.markdownlint.mjs", unableToRequireRe ]
[ "invalid.markdownlint-cli2.jsonc", unableToParseJsonc ],
[ "invalid.markdownlint-cli2.cjs", unableToRequireOrImport ],
[ "invalid.markdownlint-cli2.mjs", unableToRequireOrImport ],
[ "invalid.markdownlint.json", unableToParseJsonc ],
[ "invalid.markdownlint.yaml", unableToParseJsonc ],
[ "invalid.markdownlint.cjs", unableToRequireOrImport ],
[ "invalid.markdownlint.mjs", unableToRequireOrImport ]
];
for (const [ invalidConfigFile, stderrRe ] of invalidConfigFiles) {
const usesRequire = isModule(invalidConfigFile);
testCase({
"name": `config-files-${invalidConfigFile}-invalid-arg`,
"args": [ "--config", `cfg/${invalidConfigFile}`, "**/*.md" ],
"exitCode": 2,
stderrRe,
"stderrRe": new RegExp(`'[^']*${invalidConfigFile.replace(".", "\\.")}'.*${stderrRe}`, "u"),
"cwd": "config-files",
usesRequire
});
Expand All @@ -710,7 +709,7 @@ const testCases = ({
"args": [ "--config", "cfg/unrecognized.jsonc", "**/*.md" ],
"exitCode": 2,
"stderrRe":
/Configuration file "[^"]*cfg\/unrecognized\.jsonc" is unrecognized; its name should be \(or end with\) one of the supported types \(e\.g\., "\.markdownlint\.json" or "example\.markdownlint-cli2\.jsonc"\)\./u,
/Unable to use configuration file '[^']*cfg\/unrecognized\.jsonc'; File name should be \(or end with\) one of the supported types \(e\.g\., '\.markdownlint\.json' or 'example\.markdownlint-cli2\.jsonc'\)\./u,
"cwd": "config-files"
});

Expand Down Expand Up @@ -774,7 +773,7 @@ const testCases = ({
"name": "package-json-invalid",
"args": [ "**/*.md" ],
"exitCode": 2,
"stderrRe": /Unable to parse JSONC content/u
"stderrRe": /'[^']*package\.json'.*Unable to parse JSONC content/u
});

testCase({
Expand Down

0 comments on commit f30d989

Please sign in to comment.