From 76a09e23ba6335d6b64a792d1be0ec70cd9916a3 Mon Sep 17 00:00:00 2001 From: Vilius Vystartas Date: Sat, 11 Jul 2026 21:53:58 +0100 Subject: [PATCH 1/7] feat: add usage examples to CLI help output Adds an abstract getExamples() method to SPFxActionBase that subclasses override to provide usage examples. The renderHelpText() method is overridden to append a formatted EXAMPLES section. Includes 4 examples for the 'spfx create' command covering: - Basic React web part scaffolding - Extension scaffolding with pnpm - Local template source usage - SPFx version targeting Partially addresses #238 --- apps/spfx-cli/src/cli/actions/CreateAction.ts | 9 ++++++ .../src/cli/actions/SPFxActionBase.ts | 32 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/apps/spfx-cli/src/cli/actions/CreateAction.ts b/apps/spfx-cli/src/cli/actions/CreateAction.ts index 8da46af7..e80ee4de 100644 --- a/apps/spfx-cli/src/cli/actions/CreateAction.ts +++ b/apps/spfx-cli/src/cli/actions/CreateAction.ts @@ -269,6 +269,15 @@ export class CreateAction extends SPFxActionBase { throw error; } } + + protected getExamples(): string[] { + return [ + 'Scaffold a new React web part in the current directory:\n spfx create --template webpart-react --component-name "HelloWorld" --library-name "helloworld-library" --solution-name "helloworld" --component-description "A Hello World web part"', + 'Scaffold an extension with pnpm (skipping npm install):\n spfx create --template extension-field --component-name "ColorField" --library-name "color-field-lib" --solution-name "color-field" --package-manager pnpm --skip-install', + 'Scaffold a web part without npm install, specifying a local template source:\n spfx create --template webpart-react --component-name "MyWebPart" --library-name "mywebpart" --solution-name "mywebpart" --skip-install --local-source ./my-templates', + 'Scaffold a web part using a specific SPFx version:\n spfx create --template webpart-react --component-name "MyWP" --component-description "Description" --spfx-version 1.22 --skip-install' + ]; + } } /** diff --git a/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts b/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts index d6ca0250..ca5096b0 100644 --- a/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts +++ b/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts @@ -166,4 +166,36 @@ export abstract class SPFxActionBase extends CommandLineAction { } } } + + /** + * Returns usage examples for this action. Each string is a self-contained example + * with the command invocation and a brief description of what it does. + * Override in subclasses to provide examples. The default returns an empty array. + */ + protected getExamples(): string[] { + return []; + } + + /** + * Overrides the base help text rendering to append a formatted EXAMPLES section + * when examples are defined. + */ + public renderHelpText(): string { + const base: string = super.renderHelpText(); + const examples: string[] = this.getExamples(); + if (examples.length === 0) { + return base; + } + + // Append examples after the last line of base help + const lines: string[] = base.split('\n'); + lines.push(''); + lines.push('EXAMPLES'); + for (const example of examples) { + lines.push(''); + lines.push(` ${example}`); + } + lines.push(''); + return lines.join('\n'); + } } From 601d0a678135d7a9750a5130d4041e75c89395b6 Mon Sep 17 00:00:00 2001 From: Vilius Vystartas Date: Sat, 11 Jul 2026 22:20:05 +0100 Subject: [PATCH 2/7] fix: add override keyword, fix invalid --skip-install parameter - Add override keyword to renderHelpText() (required by noImplicitOverride setting) - Add override keyword to CreateAction.getExamples() - Replace --skip-install examples with --package-manager pnpm - Remove unnecessary parameters from examples --- apps/spfx-cli/src/cli/actions/CreateAction.ts | 8 ++++---- apps/spfx-cli/src/cli/actions/SPFxActionBase.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/spfx-cli/src/cli/actions/CreateAction.ts b/apps/spfx-cli/src/cli/actions/CreateAction.ts index e80ee4de..9137a650 100644 --- a/apps/spfx-cli/src/cli/actions/CreateAction.ts +++ b/apps/spfx-cli/src/cli/actions/CreateAction.ts @@ -270,12 +270,12 @@ export class CreateAction extends SPFxActionBase { } } - protected getExamples(): string[] { + protected override getExamples(): string[] { return [ 'Scaffold a new React web part in the current directory:\n spfx create --template webpart-react --component-name "HelloWorld" --library-name "helloworld-library" --solution-name "helloworld" --component-description "A Hello World web part"', - 'Scaffold an extension with pnpm (skipping npm install):\n spfx create --template extension-field --component-name "ColorField" --library-name "color-field-lib" --solution-name "color-field" --package-manager pnpm --skip-install', - 'Scaffold a web part without npm install, specifying a local template source:\n spfx create --template webpart-react --component-name "MyWebPart" --library-name "mywebpart" --solution-name "mywebpart" --skip-install --local-source ./my-templates', - 'Scaffold a web part using a specific SPFx version:\n spfx create --template webpart-react --component-name "MyWP" --component-description "Description" --spfx-version 1.22 --skip-install' + 'Scaffold an extension with pnpm (skipping npm install):\n spfx create --template extension-field --component-name "ColorField" --package-manager pnpm', + 'Scaffold a web part without npm install, specifying a local template source:\n spfx create --template webpart-react --component-name "MyWebPart" --library-name "mywebpart" --solution-name "mywebpart" --local-source ./my-templates', + 'Scaffold a web part using a specific SPFx version:\n spfx create --template webpart-react --component-name "MyWP" --component-description "Description" --spfx-version 1.22' ]; } } diff --git a/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts b/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts index ca5096b0..95be93b8 100644 --- a/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts +++ b/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts @@ -180,7 +180,7 @@ export abstract class SPFxActionBase extends CommandLineAction { * Overrides the base help text rendering to append a formatted EXAMPLES section * when examples are defined. */ - public renderHelpText(): string { + public override renderHelpText(): string { const base: string = super.renderHelpText(); const examples: string[] = this.getExamples(); if (examples.length === 0) { From 4c71447666bd9f48fc2237f510a4bc4e9105aa9b Mon Sep 17 00:00:00 2001 From: Vilius Vystartas Date: Sat, 11 Jul 2026 22:54:30 +0100 Subject: [PATCH 3/7] fix: handle CRLF in renderHelpText, fix example wording --- apps/spfx-cli/src/cli/actions/CreateAction.ts | 2 +- apps/spfx-cli/src/cli/actions/SPFxActionBase.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/spfx-cli/src/cli/actions/CreateAction.ts b/apps/spfx-cli/src/cli/actions/CreateAction.ts index 9137a650..77e04513 100644 --- a/apps/spfx-cli/src/cli/actions/CreateAction.ts +++ b/apps/spfx-cli/src/cli/actions/CreateAction.ts @@ -272,7 +272,7 @@ export class CreateAction extends SPFxActionBase { protected override getExamples(): string[] { return [ - 'Scaffold a new React web part in the current directory:\n spfx create --template webpart-react --component-name "HelloWorld" --library-name "helloworld-library" --solution-name "helloworld" --component-description "A Hello World web part"', + 'Scaffold a new React web part in the current working directory:\n spfx create --template webpart-react --component-name "HelloWorld" --library-name "helloworld-library" --solution-name "helloworld" --component-description "A Hello World web part"', 'Scaffold an extension with pnpm (skipping npm install):\n spfx create --template extension-field --component-name "ColorField" --package-manager pnpm', 'Scaffold a web part without npm install, specifying a local template source:\n spfx create --template webpart-react --component-name "MyWebPart" --library-name "mywebpart" --solution-name "mywebpart" --local-source ./my-templates', 'Scaffold a web part using a specific SPFx version:\n spfx create --template webpart-react --component-name "MyWP" --component-description "Description" --spfx-version 1.22' diff --git a/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts b/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts index 95be93b8..3b3e4e75 100644 --- a/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts +++ b/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts @@ -188,7 +188,7 @@ export abstract class SPFxActionBase extends CommandLineAction { } // Append examples after the last line of base help - const lines: string[] = base.split('\n'); + const lines: string[] = base.replace(/\r$/, '').split('\n'); lines.push(''); lines.push('EXAMPLES'); for (const example of examples) { From 01eeccd95405fe269b22550d7cb92b4930fef23b Mon Sep 17 00:00:00 2001 From: Vilius Vystartas Date: Sun, 12 Jul 2026 07:53:05 +0100 Subject: [PATCH 4/7] fix: normalize all CRLF line endings, not just trailing The previous fix used /\r$/ which only strips a trailing CR from the last line. For CRLF content, every line except the last retains \r, producing malformed output. Use /\r\n/g to normalize all pairs. --- apps/spfx-cli/src/cli/actions/SPFxActionBase.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts b/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts index 3b3e4e75..6fb9dddb 100644 --- a/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts +++ b/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts @@ -188,7 +188,8 @@ export abstract class SPFxActionBase extends CommandLineAction { } // Append examples after the last line of base help - const lines: string[] = base.replace(/\r$/, '').split('\n'); + // Normalize Windows CRLF to LF before splitting so every line is clean + const lines: string[] = base.replace(/\r\n/g, '\n').split('\n'); lines.push(''); lines.push('EXAMPLES'); for (const example of examples) { From e2055015c4596e902ae981f4d6740625076a1dbb Mon Sep 17 00:00:00 2001 From: Vilius Vystartas Date: Mon, 13 Jul 2026 15:33:45 +0100 Subject: [PATCH 5/7] chore: add change file for help examples --- .../@microsoft/spfx-cli/help-examples_2026-07-13.json | 10 ++++++++++ .../spfx-cli/library-name-optional_2026-07-13.json | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 common/changes/@microsoft/spfx-cli/help-examples_2026-07-13.json create mode 100644 common/changes/@microsoft/spfx-cli/library-name-optional_2026-07-13.json diff --git a/common/changes/@microsoft/spfx-cli/help-examples_2026-07-13.json b/common/changes/@microsoft/spfx-cli/help-examples_2026-07-13.json new file mode 100644 index 00000000..a0a328ef --- /dev/null +++ b/common/changes/@microsoft/spfx-cli/help-examples_2026-07-13.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/spfx-cli", + "comment": "", + "type": "none" + } + ], + "packageName": "@microsoft/spfx-cli" +} diff --git a/common/changes/@microsoft/spfx-cli/library-name-optional_2026-07-13.json b/common/changes/@microsoft/spfx-cli/library-name-optional_2026-07-13.json new file mode 100644 index 00000000..a0a328ef --- /dev/null +++ b/common/changes/@microsoft/spfx-cli/library-name-optional_2026-07-13.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/spfx-cli", + "comment": "", + "type": "none" + } + ], + "packageName": "@microsoft/spfx-cli" +} From 203d387a0d25570db3f6ec57cb05c11ec7e95dda Mon Sep 17 00:00:00 2001 From: Vilius Vystartas Date: Mon, 13 Jul 2026 15:49:09 +0100 Subject: [PATCH 6/7] chore: remove change file that belongs to PR #260 --- .../spfx-cli/library-name-optional_2026-07-13.json | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 common/changes/@microsoft/spfx-cli/library-name-optional_2026-07-13.json diff --git a/common/changes/@microsoft/spfx-cli/library-name-optional_2026-07-13.json b/common/changes/@microsoft/spfx-cli/library-name-optional_2026-07-13.json deleted file mode 100644 index a0a328ef..00000000 --- a/common/changes/@microsoft/spfx-cli/library-name-optional_2026-07-13.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@microsoft/spfx-cli", - "comment": "", - "type": "none" - } - ], - "packageName": "@microsoft/spfx-cli" -} From 738a96c0b813a66f02ebdfe873e61fcfda1c5e91 Mon Sep 17 00:00:00 2001 From: Vilius Vystartas Date: Fri, 17 Jul 2026 22:19:15 +0100 Subject: [PATCH 7/7] fix: correct example descriptions for accuracy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Example 1: removed misleading 'in the current working directory' phrasing - Example 2: fixed description — pnpm installs deps, doesn't skip them - Example 4: added --target-dir . so the intent is explicit - All examples now match actual CLI behavior --- apps/spfx-cli/src/cli/actions/CreateAction.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/spfx-cli/src/cli/actions/CreateAction.ts b/apps/spfx-cli/src/cli/actions/CreateAction.ts index 77e04513..4802405c 100644 --- a/apps/spfx-cli/src/cli/actions/CreateAction.ts +++ b/apps/spfx-cli/src/cli/actions/CreateAction.ts @@ -272,10 +272,10 @@ export class CreateAction extends SPFxActionBase { protected override getExamples(): string[] { return [ - 'Scaffold a new React web part in the current working directory:\n spfx create --template webpart-react --component-name "HelloWorld" --library-name "helloworld-library" --solution-name "helloworld" --component-description "A Hello World web part"', - 'Scaffold an extension with pnpm (skipping npm install):\n spfx create --template extension-field --component-name "ColorField" --package-manager pnpm', - 'Scaffold a web part without npm install, specifying a local template source:\n spfx create --template webpart-react --component-name "MyWebPart" --library-name "mywebpart" --solution-name "mywebpart" --local-source ./my-templates', - 'Scaffold a web part using a specific SPFx version:\n spfx create --template webpart-react --component-name "MyWP" --component-description "Description" --spfx-version 1.22' + 'Scaffold a new React web part for SharePoint:\n spfx create --template webpart-react --component-name "HelloWorld" --library-name "helloworld-library" --solution-name "helloworld" --component-description "A Hello World web part"', + 'Scaffold a field customizer extension using pnpm for dependency installation:\n spfx create --template extension-field --component-name "ColorField" --package-manager pnpm', + 'Scaffold a web part without running npm install, specifying a local template source:\n spfx create --template webpart-react --component-name "MyWebPart" --library-name "mywebpart" --solution-name "mywebpart" --local-source ./my-templates', + 'Scaffold a web part for a specific SPFx version, writing output to a custom directory:\n spfx create --template webpart-react --component-name "MyWP" --component-description "Description" --spfx-version 1.22 --target-dir ./my-webpart' ]; } }