diff --git a/spec/CLI.spec.js b/spec/CLI.spec.js
index 39061b8083..a7017dde18 100644
--- a/spec/CLI.spec.js
+++ b/spec/CLI.spec.js
@@ -191,6 +191,55 @@ describe('definitions', () => {
});
});
+describe('CLI help formatting (#8434)', () => {
+ const loadInto = defs => {
+ const command = require('../lib/cli/utils/commander').default;
+ const program = new command.constructor();
+ program.storeOptionsAsProperties();
+ program.allowExcessArguments();
+ program.loadDefinitions(defs);
+ return program;
+ };
+ const htmlTag = /
|<\/?(?:b|ul|li)>|/i;
+
+ it('strips HTML formatting tags from every option description', () => {
+ for (const defs of [definitions, liveQueryDefinitions]) {
+ loadInto(defs).options.forEach(option => {
+ expect(option.description).not.toMatch(htmlTag);
+ });
+ }
+ });
+
+ it('converts
paragraph breaks to newlines', () => {
+ const description = loadInto(definitions).options.find(o => o.attributeName() === 'directAccess')
+ .description;
+ expect(description).toContain('\n');
+ expect(description).not.toContain('
');
+ });
+
+ it('converts links to "text (url)"', () => {
+ const description = loadInto(definitions).options.find(o => o.attributeName() === 'trustProxy')
+ .description;
+ expect(description).toContain(
+ 'express trust proxy settings (https://expressjs.com/en/guide/behind-proxies.html)'
+ );
+ });
+
+ it('preserves angle-bracket literals that are not HTML tags', () => {
+ // `_auth_data_` and `<= \`20\`` are literal content, not markup - a blanket strip would eat them.
+ const program = loadInto({
+ myOption: {
+ env: 'PARSE_MY_OPTION',
+ help: 'Field `_auth_data_`.
Valid values are >= `0` and <= `20`.',
+ },
+ });
+ const description = program.options.find(o => o.attributeName() === 'myOption').description;
+ expect(description).toContain('_auth_data_');
+ expect(description).toContain('<= `20`');
+ expect(description).not.toContain('
');
+ });
+});
+
describe('LiveQuery definitions', () => {
it('should have valid types', () => {
for (const key in liveQueryDefinitions) {
diff --git a/src/cli/utils/commander.js b/src/cli/utils/commander.js
index e2e06e0550..85f4983f10 100644
--- a/src/cli/utils/commander.js
+++ b/src/cli/utils/commander.js
@@ -7,6 +7,23 @@ let _definitions;
let _reverseDefinitions;
let _defaults;
+// Option help is authored as HTML in the JSDoc comments of `src/Options/index.js` so it renders on the
+// docs site. Commander only word-wraps the text, so convert the HTML to plain text for the terminal here.
+function cleanHelpText(help) {
+ if (!help) {
+ return help;
+ }
+ return help
+ .replace(/]*\bhref="([^"]*)"[^>]*>(.*?)<\/a>/gi, '$2 ($1)')
+ .replace(/<\/li>/gi, '')
+ .replace(//gi, '\n- ')
+ .replace(/
/gi, '\n')
+ .replace(/<\/?(?:ul|b)>/gi, '')
+ .replace(/[ \t]+\n/g, '\n')
+ .replace(/\n{3,}/g, '\n\n')
+ .trim();
+}
+
Command.prototype.loadDefinitions = function (definitions) {
_definitions = definitions;
@@ -16,13 +33,13 @@ Command.prototype.loadDefinitions = function (definitions) {
if (additionalOptions.required === true) {
return program.option(
`--${opt} <${opt}>`,
- additionalOptions.help,
+ cleanHelpText(additionalOptions.help),
additionalOptions.action
);
} else {
return program.option(
`--${opt} [${opt}]`,
- additionalOptions.help,
+ cleanHelpText(additionalOptions.help),
additionalOptions.action
);
}