Skip to content

Commit 3404146

Browse files
committed
Refactor main into smaller functions to minimize variable scope and lifetime.
1 parent 65fe7f8 commit 3404146

File tree

1 file changed

+153
-107
lines changed

1 file changed

+153
-107
lines changed

markdownlint-cli2.js

Lines changed: 153 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,11 @@ const requireConfig = (dir, name, otherwise) => {
6868
);
6969
};
7070

71-
// Main function
72-
const main = async (argv, logMessage, logError) => {
73-
// Output help for missing arguments
71+
// Process command-line arguments and return glob patterns
72+
const processArgv = (argv, logMessage) => {
7473
const globPatterns = argv.map((glob) => glob.replace(/^#/u, "!"));
7574
if (globPatterns.length === 0) {
75+
// Output help if missing arguments
7676
const { name, version, author, homepage } = require("./package.json");
7777
/* eslint-disable max-len */
7878
logMessage(`${name} version ${version} by ${author.name} (${author.url})
@@ -111,118 +111,139 @@ Therefore, the most compatible glob syntax for cross-platform support:
111111
$ ${name} "**/*.md" "#node_modules"`
112112
);
113113
/* eslint-enable max-len */
114-
return 1;
114+
return null;
115115
} else if ((globPatterns.length === 1) && (globPatterns[0] === ".")) {
116116
// Substitute a more reasonable pattern
117117
globPatterns[0] = dotOnlySubstitute;
118118
}
119+
return globPatterns;
120+
};
119121

120-
// Read base ignore globs to pass as globby patterns (best performance)
121-
const tasks = [];
122-
const dirToDirInfo = {};
123-
const getAndProcessDirInfo = (dir, func) => {
124-
let dirInfo = dirToDirInfo[dir];
125-
if (!dirInfo) {
126-
dirInfo = {
127-
dir,
128-
"parent": null,
129-
"files": [],
130-
"markdownlintConfig": null,
131-
"markdownlintOptions": null
132-
};
133-
dirToDirInfo[dir] = dirInfo;
134-
const markdownlintCli2Jsonc = path.join(dir, ".markdownlint-cli2.jsonc");
135-
const markdownlintCli2Yaml = path.join(dir, ".markdownlint-cli2.yaml");
136-
tasks.push(
137-
fs.access(markdownlintCli2Jsonc).
138-
then(
139-
() => fs.readFile(markdownlintCli2Jsonc, utf8).then(jsoncParse),
140-
() => fs.access(markdownlintCli2Yaml).
141-
then(
142-
() => fs.readFile(markdownlintCli2Yaml, utf8).then(yamlParse),
143-
requireConfig(
144-
dir,
145-
".markdownlint-cli2.js",
146-
() => null
147-
)
122+
// Get (creating if necessary) and process a directory's info object
123+
const getAndProcessDirInfo = (tasks, dirToDirInfo, dir, func) => {
124+
let dirInfo = dirToDirInfo[dir];
125+
if (!dirInfo) {
126+
dirInfo = {
127+
dir,
128+
"parent": null,
129+
"files": [],
130+
"markdownlintConfig": null,
131+
"markdownlintOptions": null
132+
};
133+
dirToDirInfo[dir] = dirInfo;
134+
135+
// Load markdownlint-cli2 object(s)
136+
const markdownlintCli2Jsonc = path.join(dir, ".markdownlint-cli2.jsonc");
137+
const markdownlintCli2Yaml = path.join(dir, ".markdownlint-cli2.yaml");
138+
tasks.push(
139+
fs.access(markdownlintCli2Jsonc).
140+
then(
141+
() => fs.readFile(markdownlintCli2Jsonc, utf8).then(jsoncParse),
142+
() => fs.access(markdownlintCli2Yaml).
143+
then(
144+
() => fs.readFile(markdownlintCli2Yaml, utf8).then(yamlParse),
145+
requireConfig(
146+
dir,
147+
".markdownlint-cli2.js",
148+
() => null
148149
)
149-
).
150-
then((options) => {
151-
dirInfo.markdownlintOptions = options;
152-
})
153-
);
154-
const readConfigs =
150+
)
151+
).
152+
then((options) => {
153+
dirInfo.markdownlintOptions = options;
154+
})
155+
);
156+
157+
// Load markdownlint object(s)
158+
const readConfigs =
159+
readConfig(
160+
dir,
161+
".markdownlint.jsonc",
155162
readConfig(
156163
dir,
157-
".markdownlint.jsonc",
164+
".markdownlint.json",
158165
readConfig(
159166
dir,
160-
".markdownlint.json",
167+
".markdownlint.yaml",
161168
readConfig(
162169
dir,
163-
".markdownlint.yaml",
164-
readConfig(
170+
".markdownlint.yml",
171+
requireConfig(
165172
dir,
166-
".markdownlint.yml",
167-
requireConfig(
168-
dir,
169-
".markdownlint.js",
170-
() => null
171-
)
173+
".markdownlint.js",
174+
() => null
172175
)
173176
)
174177
)
175-
);
176-
tasks.push(
177-
readConfigs().
178-
then((config) => {
179-
dirInfo.markdownlintConfig = config;
180-
})
178+
)
181179
);
182-
}
183-
if (func) {
184-
func(dirInfo);
185-
}
186-
return dirInfo;
187-
};
188-
getAndProcessDirInfo(".");
180+
tasks.push(
181+
readConfigs().
182+
then((config) => {
183+
dirInfo.markdownlintConfig = config;
184+
})
185+
);
186+
}
187+
if (func) {
188+
func(dirInfo);
189+
}
190+
return dirInfo;
191+
};
192+
193+
// Get base markdownlint-cli2 options object
194+
const getBaseOptions = async (globPatterns) => {
195+
const tasks = [];
196+
const dirToDirInfo = {};
197+
getAndProcessDirInfo(tasks, dirToDirInfo, ".");
189198
await Promise.all(tasks);
190-
tasks.length = 0;
191199
const baseMarkdownlintOptions = dirToDirInfo["."].markdownlintOptions || {};
200+
201+
// Pass base ignore globs as globby patterns (best performance)
192202
const ignorePatterns = (baseMarkdownlintOptions.ignores || []).
193203
map(negateGlob);
194204
appendToArray(globPatterns, ignorePatterns);
195205
delete baseMarkdownlintOptions.ignores;
196-
const showProgress = !baseMarkdownlintOptions.noProgress;
206+
return {
207+
baseMarkdownlintOptions,
208+
dirToDirInfo
209+
};
210+
};
197211

198-
// Enumerate files from globs and build directory info list
199-
if (showProgress) {
200-
logMessage(`Finding: ${globPatterns.join(" ")}`);
201-
}
212+
// Enumerate files from globs and build directory infos
213+
const enumerateFiles = async (globPatterns, dirToDirInfo) => {
214+
const tasks = [];
202215
for await (const file of globby.stream(globPatterns)) {
203216
// @ts-ignore
204217
const dir = path.dirname(file);
205-
getAndProcessDirInfo(dir, (dirInfo) => {
218+
getAndProcessDirInfo(tasks, dirToDirInfo, dir, (dirInfo) => {
206219
dirInfo.files.push(file);
207220
});
208221
}
209222
await Promise.all(tasks);
210-
tasks.length = 0;
223+
};
211224

212-
// Fill out directory info list with parent directories
225+
// Enumerate (possibly missing) parent directories and update directory infos
226+
const enumerateParents = async (dirToDirInfo) => {
227+
const tasks = [];
213228
for (let lastDirInfo of Object.values(dirToDirInfo)) {
214229
let { dir } = lastDirInfo;
215230
let lastDir = dir;
216231
while ((dir = path.dirname(dir)) && (dir !== lastDir)) {
217232
lastDir = dir;
218-
// eslint-disable-next-line no-loop-func
219-
lastDirInfo = getAndProcessDirInfo(dir, (dirInfo) => {
220-
lastDirInfo.parent = dirInfo;
221-
});
233+
lastDirInfo =
234+
// eslint-disable-next-line no-loop-func
235+
getAndProcessDirInfo(tasks, dirToDirInfo, dir, (dirInfo) => {
236+
lastDirInfo.parent = dirInfo;
237+
});
222238
}
223239
}
224240
await Promise.all(tasks);
225-
tasks.length = 0;
241+
};
242+
243+
// Create directory info objects by enumerating file globs
244+
const createDirInfos = async (globPatterns, dirToDirInfo) => {
245+
await enumerateFiles(globPatterns, dirToDirInfo);
246+
await enumerateParents(dirToDirInfo);
226247

227248
// Merge file lists with identical configuration
228249
const dirs = Object.keys(dirToDirInfo);
@@ -289,12 +310,12 @@ $ ${name} "**/*.md" "#node_modules"`
289310
}
290311
dirInfo.markdownlintOptions = markdownlintOptions;
291312
}
313+
return dirInfos;
314+
};
292315

293-
// Lint each list of files
294-
if (showProgress) {
295-
const fileCount = dirInfos.reduce((p, c) => p + c.files.length, 0);
296-
logMessage(`Linting: ${fileCount} file(s)`);
297-
}
316+
// Lint files in groups by shared configuration
317+
const lintFiles = async (dirInfos) => {
318+
const tasks = [];
298319
for (const dirInfo of dirInfos) {
299320
const { dir, files, markdownlintConfig, markdownlintOptions } = dirInfo;
300321
let filteredFiles = files;
@@ -308,18 +329,15 @@ $ ${name} "**/*.md" "#node_modules"`
308329
}
309330
const options = {
310331
"files": filteredFiles,
311-
"config":
312-
markdownlintConfig || markdownlintOptions.config,
313-
"customRules":
314-
requireIds(dir, markdownlintOptions.customRules || []),
332+
"config": markdownlintConfig || markdownlintOptions.config,
333+
"customRules": requireIds(dir, markdownlintOptions.customRules || []),
315334
"frontMatter": markdownlintOptions.frontMatter
316335
? new RegExp(markdownlintOptions.frontMatter, "u")
317336
: undefined,
318337
"handleRuleFailures": true,
319338
"markdownItPlugins":
320339
requireIdsAndParams(dir, markdownlintOptions.markdownItPlugins || []),
321-
"noInlineConfig":
322-
Boolean(markdownlintOptions.noInlineConfig),
340+
"noInlineConfig": Boolean(markdownlintOptions.noInlineConfig),
323341
"resultVersion": 3
324342
};
325343
let task = markdownlintPromise(options);
@@ -346,9 +364,11 @@ $ ${name} "**/*.md" "#node_modules"`
346364
tasks.push(task);
347365
}
348366
const taskResults = await Promise.all(tasks);
349-
tasks.length = 0;
367+
return taskResults;
368+
};
350369

351-
// Create summary of results
370+
// Create summary of results
371+
const createSummary = (taskResults) => {
352372
const summary = [];
353373
let counter = 0;
354374
for (const results of taskResults) {
@@ -377,30 +397,56 @@ $ ${name} "**/*.md" "#node_modules"`
377397
(a.counter - b.counter)
378398
));
379399
summary.forEach((result) => delete result.counter);
400+
return summary;
401+
};
402+
403+
// Output summary via formatters
404+
const outputSummary =
405+
async (summary, outputFormatters, logMessage, logError) => {
406+
const errorsPresent = (summary.length > 0);
407+
if (errorsPresent || outputFormatters) {
408+
const formatterOptions = {
409+
"results": summary,
410+
logMessage,
411+
logError
412+
};
413+
const formattersAndParams = requireIdsAndParams(
414+
".",
415+
outputFormatters || [ [ "markdownlint-cli2-formatter-default" ] ]
416+
);
417+
await Promise.all(formattersAndParams.map((formatterAndParams) => {
418+
const [ formatter, ...formatterParams ] = formatterAndParams;
419+
return formatter(formatterOptions, ...formatterParams);
420+
}));
421+
}
422+
return errorsPresent;
423+
};
380424

381-
// Output summary via formatters
425+
// Main function
426+
const main = async (argv, logMessage, logError) => {
427+
const globPatterns = processArgv(argv, logMessage);
428+
if (!globPatterns) {
429+
return 1;
430+
}
431+
const { baseMarkdownlintOptions, dirToDirInfo } =
432+
await getBaseOptions(globPatterns);
433+
const showProgress = !baseMarkdownlintOptions.noProgress;
434+
if (showProgress) {
435+
logMessage(`Finding: ${globPatterns.join(" ")}`);
436+
}
437+
const dirInfos = await createDirInfos(globPatterns, dirToDirInfo);
438+
if (showProgress) {
439+
const fileCount = dirInfos.reduce((p, c) => p + c.files.length, 0);
440+
logMessage(`Linting: ${fileCount} file(s)`);
441+
}
442+
const lintResults = await lintFiles(dirInfos);
443+
const summary = createSummary(lintResults);
382444
if (showProgress) {
383445
logMessage(`Summary: ${summary.length} error(s)`);
384446
}
385447
const { outputFormatters } = baseMarkdownlintOptions;
386-
const errorsPresent = (summary.length > 0);
387-
if (errorsPresent || outputFormatters) {
388-
const formatterOptions = {
389-
"results": summary,
390-
logMessage,
391-
logError
392-
};
393-
const formattersAndParams = requireIdsAndParams(
394-
".",
395-
outputFormatters || [ [ "markdownlint-cli2-formatter-default" ] ]
396-
);
397-
await Promise.all(formattersAndParams.map((formatterAndParams) => {
398-
const [ formatter, ...formatterParams ] = formatterAndParams;
399-
return formatter(formatterOptions, ...formatterParams);
400-
}));
401-
}
402-
403-
// Done
448+
const errorsPresent =
449+
await outputSummary(summary, outputFormatters, logMessage, logError);
404450
return errorsPresent ? 1 : 0;
405451
};
406452

0 commit comments

Comments
 (0)