diff --git a/.sfdevrc.json b/.sfdevrc.json index c7ee2ae7..ade88273 100644 --- a/.sfdevrc.json +++ b/.sfdevrc.json @@ -4,7 +4,7 @@ }, "wireit": { "test:command-reference": { - "command": "node --loader ts-node/esm --no-warnings=ExperimentalWarning \"./bin/dev.js\" commandreference generate --plugins auth --outputdir test/tmp", + "command": "node --loader ts-node/esm --no-warnings=ExperimentalWarning \"./bin/dev.js\" commandreference generate --plugins auth --plugins user --outputdir test/tmp", "files": ["src/**/*.ts", "messages/**", "package.json"], "output": ["test/tmp"] }, diff --git a/package.json b/package.json index b5f0d543..5c726054 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@oclif/plugin-help": "^6.0.22", "@salesforce/dev-scripts": "^9.1.2", "@salesforce/plugin-auth": "^3.6.16", + "@salesforce/plugin-user": "^3.5.11", "@types/debug": "^4.1.12", "eslint-plugin-sf-plugin": "^1.18.4", "oclif": "^4.11.2", @@ -57,7 +58,8 @@ }, "devPlugins": [ "@oclif/plugin-help", - "@salesforce/plugin-auth" + "@salesforce/plugin-auth", + "@salesforce/plugin-user" ] }, "repository": "salesforcecli/plugin-command-reference", @@ -157,7 +159,7 @@ ] }, "test:command-reference": { - "command": "node --loader ts-node/esm --no-warnings=ExperimentalWarning \"./bin/dev.js\" commandreference generate --plugins auth --outputdir test/tmp", + "command": "node --loader ts-node/esm --no-warnings=ExperimentalWarning \"./bin/dev.js\" commandreference generate --plugins auth --plugins user --outputdir test/tmp", "files": [ "src/**/*.ts", "messages/**", diff --git a/src/ditamap/command.ts b/src/ditamap/command.ts index 112b9810..83a77363 100644 --- a/src/ditamap/command.ts +++ b/src/ditamap/command.ts @@ -6,7 +6,7 @@ */ import { join } from 'node:path'; -import { asString, Dictionary, ensureObject, ensureString } from '@salesforce/ts-types'; +import { asString, Dictionary, ensureObject, ensureString, Optional } from '@salesforce/ts-types'; import { CommandClass, CommandData, CommandParameterData, punctuate, replaceConfigVariables } from '../utils.js'; import { Ditamap } from './ditamap.js'; @@ -21,14 +21,18 @@ type FlagInfo = { default: string | (() => Promise); }; -const getDefault = async (flag?: FlagInfo): Promise => { +const getDefault = async (flag: FlagInfo, flagName: string): Promise => { if (!flag) { return ''; } + if (flagName === 'target-org' || flagName === 'target-dev-hub') { + // special handling to prevent global/local default usernames from appearing in the docs, but they do appear in user's help + return ''; + } if (typeof flag.default === 'function') { try { const help = await flag.default(); - return help || ''; + return help.includes('[object Object]') ? '' : help ?? ''; } catch { return ''; } @@ -50,23 +54,23 @@ export class Command extends Ditamap { ) { const commandWithUnderscores = ensureString(command.id).replace(/:/g, '_'); const filename = Ditamap.file(`cli_reference_${commandWithUnderscores}`, 'xml'); - super(filename, undefined); this.flags = ensureObject(command.flags); this.commandMeta = commandMeta; + const binary = readBinary(this.commandMeta); const summary = punctuate(command.summary); this.commandName = command.id.replace(/:/g, asString(this.commandMeta.topicSeparator, ':')); const description = command.description - ? replaceConfigVariables(command.description, asString(this.commandMeta.binary, 'unknown'), this.commandName) + ? replaceConfigVariables(command.description, binary, this.commandName) : undefined; // Help are all the lines after the first line in the description. Before oclif, there was a 'help' property so continue to // support that. - const help = this.formatParagraphs(description); + const help = formatParagraphs(description); let trailblazerCommunityUrl: string | undefined; let trailblazerCommunityName: string | undefined; @@ -90,10 +94,8 @@ export class Command extends Ditamap { } return { - description: replaceConfigVariables(desc ?? '', asString(this.commandMeta.binary, 'unknown'), this.commandName), - commands: commands.map((cmd) => - replaceConfigVariables(cmd, asString(this.commandMeta.binary, 'unknown'), this.commandName) - ), + description: replaceConfigVariables(desc ?? '', binary, this.commandName), + commands: commands.map((cmd) => replaceConfigVariables(cmd, binary, this.commandName)), }; }); @@ -102,7 +104,7 @@ export class Command extends Ditamap { name: this.commandName, summary, description, - binary: 'binary' in commandMeta && typeof commandMeta.binary === 'string' ? commandMeta.binary : 'unknown', + binary, commandWithUnderscores, deprecated: (command.deprecated as boolean) ?? state === 'deprecated' ?? false, examples, @@ -120,34 +122,24 @@ export class Command extends Ditamap { } public async getParametersForTemplate(flags: Dictionary): Promise { - const final: CommandParameterData[] = []; - - for (const [flagName, flag] of Object.entries(flags)) { - if (!flag || flag.hidden) continue; - const description = replaceConfigVariables( - Array.isArray(flag?.description) ? flag?.description.join('\n') : flag?.description ?? '', - asString(this.commandMeta.binary, 'unknown'), - this.commandName - ); - const entireDescription = flag.summary - ? `${replaceConfigVariables( - flag.summary, - asString(this.commandMeta.binary, 'unknown'), - this.commandName - )}\n${description}` - : description; - const updated = Object.assign({}, flag, { - name: flagName, - description: this.formatParagraphs(entireDescription), - optional: !flag?.required, - kind: flag?.kind ?? flag?.type, - hasValue: flag?.type !== 'boolean', - // eslint-disable-next-line no-await-in-loop - defaultFlagValue: await getDefault(flag), - }); - final.push(updated); - } - return final; + const descriptionBuilder = buildDescription(this.commandName)(readBinary(this.commandMeta)); + return Promise.all( + [...Object.entries(flags)] + .filter(flagIsDefined) + .filter(([, flag]) => !flag.hidden) + .map( + async ([flagName, flag]) => + ({ + ...flag, + name: flagName, + description: descriptionBuilder(flag), + optional: !flag.required, + kind: flag.kind ?? flag.type, + hasValue: flag.type !== 'boolean', + defaultFlagValue: await getDefault(flag, flagName), + } satisfies CommandParameterData) + ) + ); } // eslint-disable-next-line class-methods-use-this @@ -161,3 +153,25 @@ export class Command extends Ditamap { return super.transformToDitamap(); } } + +const flagIsDefined = (input: [string, Optional]): input is [string, FlagInfo] => input[1] !== undefined; + +const buildDescription = + (commandName: string) => + (binary: string) => + (flag: FlagInfo): string[] => { + const description = replaceConfigVariables( + Array.isArray(flag?.description) ? flag?.description.join('\n') : flag?.description ?? '', + binary, + commandName + ); + return formatParagraphs( + flag.summary ? `${replaceConfigVariables(flag.summary, binary, commandName)}\n${description}` : description + ); + }; + +const formatParagraphs = (textToFormat?: string): string[] => + textToFormat ? textToFormat.split('\n').filter((n) => n !== '') : []; + +const readBinary = (commandMeta: Record): string => + 'binary' in commandMeta && typeof commandMeta.binary === 'string' ? commandMeta.binary : 'unknown'; diff --git a/src/ditamap/ditamap.ts b/src/ditamap/ditamap.ts index 906723e1..3af15ef5 100644 --- a/src/ditamap/ditamap.ts +++ b/src/ditamap/ditamap.ts @@ -92,11 +92,6 @@ export abstract class Ditamap { await fs.writeFile(this.destination, output); } - // eslint-disable-next-line class-methods-use-this - protected formatParagraphs(textToFormat?: string): string[] { - return textToFormat ? textToFormat.split('\n').filter((n) => n !== '') : []; - } - /** * Applies the named handlebars template to the supplied data * diff --git a/src/utils.ts b/src/utils.ts index f548bbcd..4cfd0bc9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -69,6 +69,7 @@ type ClIRefTopicData = { }; export type CommandParameterData = { + description: string[]; optional?: boolean; char?: string; name: string; diff --git a/test/unit/endtoend.test.ts b/test/unit/endtoend.test.ts index cad95f07..27bb2af1 100644 --- a/test/unit/endtoend.test.ts +++ b/test/unit/endtoend.test.ts @@ -25,7 +25,7 @@ function loadTestDitamapFile(path: string) { return readFileSync(join(testFilesPath, path), 'utf8'); } -describe('plugin-auth', () => { +describe('plugin-auth and user', () => { before(async () => { try { await access(testFilesPath); @@ -38,6 +38,10 @@ describe('plugin-auth', () => { after(async () => { await rm(testFilesPath, { recursive: true }); }); + it('produces no [object Object] nonsense for default flags', () => { + const dita = loadTestDitamapFile(join('org', 'cli_reference_org_create_user_unified.xml')); + expect(dita.includes('[object Object]')).to.be.false; + }); it('creates with spaced commands', async () => { const dita = loadTestDitamapFile(join('org', 'cli_reference_org_login_jwt_unified.xml')); expect(dita.includes('<codeph otherprops="nolang">org login jwt')).to.be.true; diff --git a/yarn.lock b/yarn.lock index bceed30b..7f0f92ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1878,6 +1878,17 @@ proxy-agent "^6.4.0" semver "^7.6.0" +"@salesforce/plugin-user@^3.5.11": + version "3.5.11" + resolved "https://registry.yarnpkg.com/@salesforce/plugin-user/-/plugin-user-3.5.11.tgz#839796a7f17562ad9ece2c69d4dfdb5bc033f4d9" + integrity sha512-GA81RgZ5KMBuK2MEsYPCgRJP6U371Zvr0qxZUmXpDiuC8rjp9EqPl/1s4BfFVzB8emQWmFXxejSqCZz5FJh1dg== + dependencies: + "@oclif/core" "^3.26.6" + "@salesforce/core" "^7.3.8" + "@salesforce/kit" "^3.1.0" + "@salesforce/sf-plugins-core" "^9.0.12" + "@salesforce/ts-types" "^2.0.9" + "@salesforce/prettier-config@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@salesforce/prettier-config/-/prettier-config-0.0.3.tgz#ba648d4886bb38adabe073dbea0b3a91b3753bb0" @@ -1888,7 +1899,7 @@ resolved "https://registry.yarnpkg.com/@salesforce/schemas/-/schemas-1.9.0.tgz#ba477a112653a20b4edcf989c61c57bdff9aa3ca" integrity sha512-LiN37zG5ODT6z70sL1fxF7BQwtCX9JOWofSU8iliSNIM+WDEeinnoFtVqPInRSNt8I0RiJxIKCrqstsmQRBNvA== -"@salesforce/sf-plugins-core@^9.0.10", "@salesforce/sf-plugins-core@^9.0.13", "@salesforce/sf-plugins-core@^9.0.7": +"@salesforce/sf-plugins-core@^9.0.10", "@salesforce/sf-plugins-core@^9.0.12", "@salesforce/sf-plugins-core@^9.0.13", "@salesforce/sf-plugins-core@^9.0.7": version "9.0.14" resolved "https://registry.yarnpkg.com/@salesforce/sf-plugins-core/-/sf-plugins-core-9.0.14.tgz#12a31b81692579ce2ed478bf886a53255ca62c35" integrity sha512-sxiLFtjfViOOSh6tSZKiKGQUIYZYkrz0S33Qg/s9EtQM0hBCstDUbI+1QqmafnolHQRq2tTYGoj//lko6AzHtA== @@ -7377,16 +7388,7 @@ srcset@^5.0.0: resolved "https://registry.yarnpkg.com/srcset/-/srcset-5.0.0.tgz#9df6c3961b5b44a02532ce6ae4544832609e2e3f" integrity sha512-SqEZaAEhe0A6ETEa9O1IhSPC7MdvehZtCnTR0AftXk3QhY2UNgb+NApFOUPZILXk/YTDfFxMTNJOBpzrJsEdIA== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -7445,14 +7447,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -7977,7 +7972,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -7995,15 +7990,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"