From 77d8b1c93fc79bc3c502c2b59a8f71af314ae314 Mon Sep 17 00:00:00 2001 From: Adam-it Date: Sat, 25 Apr 2026 21:45:57 +0200 Subject: [PATCH] Adds json output mode for list templates command --- api/spfx-template-api/README.md | 2 +- .../etc/spfx-template-api.api.md | 17 +++ api/spfx-template-api/src/index.ts | 1 + .../repositories/SPFxTemplateCollection.ts | 42 ++++++ .../src/repositories/index.ts | 2 +- .../test/SPFxTemplateCollection.test.ts | 97 +++++++++++++- .../SPFxTemplateCollection.test.ts.snap | 22 ++++ apps/spfx-cli/README.md | 13 ++ .../src/cli/actions/ListTemplatesAction.ts | 24 +++- .../src/cli/actions/SPFxActionBase.ts | 20 ++- .../actions/tests/ListTemplatesAction.test.ts | 56 +++++++- .../__snapshots__/CreateAction.test.ts.snap | 120 +++++++++--------- .../ListTemplatesAction.test.ts.snap | 42 +++--- .../CommandLineHelp.test.ts.snap | 15 ++- apps/spfx-cli/src/start.ts | 3 +- .../spfx-cli/json-output-mode_2026-04-25.json | 10 ++ 16 files changed, 390 insertions(+), 96 deletions(-) create mode 100644 api/spfx-template-api/src/repositories/test/__snapshots__/SPFxTemplateCollection.test.ts.snap create mode 100644 common/changes/@microsoft/spfx-cli/json-output-mode_2026-04-25.json diff --git a/api/spfx-template-api/README.md b/api/spfx-template-api/README.md index fa81478d..11f18d1b 100644 --- a/api/spfx-template-api/README.md +++ b/api/spfx-template-api/README.md @@ -140,7 +140,7 @@ The writer uses these helpers internally. You can also import them directly for | `ICasedString` | Interface exposing `.camel`, `.pascal`, `.hyphen`, `.allCaps`; auto-applied to all string context values during rendering | | `createCasedString` | Factory function that creates an `ICasedString` from a raw string | | `SPFxTemplateRepositoryManager` | Aggregates sources and returns a `SPFxTemplateCollection` | -| `SPFxTemplateCollection` | `Map` of all loaded templates | +| `SPFxTemplateCollection` | `Map` of all loaded templates; call `toJsonString()` for structured JSON or `toFormattedStringAsync()` for a human-readable table | | `SPFxTemplate` | Single template — exposes `name`, `category`, `spfxVersion`, and `renderAsync()` | | `ITemplateOutputEntry` | A single file entry (text or binary contents) | | `TemplateOutput` | In-memory file system implementation backed by a `Map`, returned by `renderAsync()` | diff --git a/api/spfx-template-api/etc/spfx-template-api.api.md b/api/spfx-template-api/etc/spfx-template-api.api.md index 863d98b9..2b3a7635 100644 --- a/api/spfx-template-api/etc/spfx-template-api.api.md +++ b/api/spfx-template-api/etc/spfx-template-api.api.md @@ -165,6 +165,22 @@ export interface ISPFxTemplateJson { version: string; } +// @public +export interface ITemplateJsonOutputEntry { + // (undocumented) + category: string; + // (undocumented) + description: string | null; + // (undocumented) + fileCount: number; + // (undocumented) + name: string; + // (undocumented) + spfxVersion: string; + // (undocumented) + version: string; +} + // @public export interface ITemplateOutputEntry { readonly contents: string | Buffer; @@ -288,6 +304,7 @@ export type SPFxTemplateCategory = (typeof SPFX_TEMPLATE_CATEGORIES)[number]; export class SPFxTemplateCollection extends Map { constructor(templates: SPFxTemplate[]); toFormattedStringAsync(): Promise; + toJsonString(): string; } // @public diff --git a/api/spfx-template-api/src/index.ts b/api/spfx-template-api/src/index.ts index 7b9fa111..4f124935 100644 --- a/api/spfx-template-api/src/index.ts +++ b/api/spfx-template-api/src/index.ts @@ -32,6 +32,7 @@ export { BaseSPFxTemplateRepositorySource, type SPFxRepositorySource, SPFxTemplateCollection, + type ITemplateJsonOutputEntry, LocalFileSystemRepositorySource, PublicGitHubRepositorySource, type IPublicGitHubRepositorySourceOptions diff --git a/api/spfx-template-api/src/repositories/SPFxTemplateCollection.ts b/api/spfx-template-api/src/repositories/SPFxTemplateCollection.ts index 3a7fdfd3..0c7c3547 100644 --- a/api/spfx-template-api/src/repositories/SPFxTemplateCollection.ts +++ b/api/spfx-template-api/src/repositories/SPFxTemplateCollection.ts @@ -3,6 +3,23 @@ import type { SPFxTemplate } from '../templating'; +/** + * @public + * Represents a single template entry in the JSON output produced by + * {@link SPFxTemplateCollection.toJsonString}. + */ +export interface ITemplateJsonOutputEntry { + name: string; + category: string; + // `null` (not `undefined`) is intentional: JSON.stringify drops `undefined` fields + // but preserves `null`, ensuring the field is always present in the output. + // eslint-disable-next-line @rushstack/no-new-null + description: string | null; + version: string; + spfxVersion: string; + fileCount: number; +} + /** * @public * Represents a collection of SharePoint Framework (SPFx) templates. @@ -17,6 +34,31 @@ export class SPFxTemplateCollection extends Map { super(templates.map((template) => [template.name, template])); } + /** + * Returns a JSON string representation of the collection as an array of template objects. + * Each object includes `name`, `category`, `description`, `version`, `spfxVersion`, and `fileCount`. + * + * @remarks + * Unlike {@link SPFxTemplateCollection.toFormattedStringAsync}, this method is synchronous + * because it has no external dependencies. + * + * @returns A pretty-printed JSON string + */ + public toJsonString(): string { + const items: ITemplateJsonOutputEntry[] = []; + for (const template of this.values()) { + items.push({ + name: template.name, + category: template.category, + description: template.description ?? null, + version: template.version, + spfxVersion: template.spfxVersion, + fileCount: template.fileCount + }); + } + return JSON.stringify(items, undefined, 2); + } + /** * Returns a formatted table string representation of the collection. * Uses cli-table3, which is loaded asynchronously to reduce startup cost. diff --git a/api/spfx-template-api/src/repositories/index.ts b/api/spfx-template-api/src/repositories/index.ts index 7cbdff3d..06324c69 100644 --- a/api/spfx-template-api/src/repositories/index.ts +++ b/api/spfx-template-api/src/repositories/index.ts @@ -7,7 +7,7 @@ export { BaseSPFxTemplateRepositorySource, type SPFxRepositorySource } from './SPFxTemplateRepositorySource'; -export { SPFxTemplateCollection } from './SPFxTemplateCollection'; +export { SPFxTemplateCollection, type ITemplateJsonOutputEntry } from './SPFxTemplateCollection'; export { LocalFileSystemRepositorySource } from './LocalFileSystemRepositorySource'; export { PublicGitHubRepositorySource, diff --git a/api/spfx-template-api/src/repositories/test/SPFxTemplateCollection.test.ts b/api/spfx-template-api/src/repositories/test/SPFxTemplateCollection.test.ts index f045c466..3a0a598e 100644 --- a/api/spfx-template-api/src/repositories/test/SPFxTemplateCollection.test.ts +++ b/api/spfx-template-api/src/repositories/test/SPFxTemplateCollection.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -import { SPFxTemplateCollection } from '../SPFxTemplateCollection'; +import { SPFxTemplateCollection, type ITemplateJsonOutputEntry } from '../SPFxTemplateCollection'; import { SPFxTemplate } from '../../templating'; import { SPFxTemplateJsonFile } from '../../templating'; @@ -172,6 +172,101 @@ describe(SPFxTemplateCollection.name, () => { }); }); + describe(SPFxTemplateCollection.prototype.toJsonString.name, () => { + it('should return an empty array for empty collection', () => { + const collection = new SPFxTemplateCollection([]); + expect(collection.toJsonString()).toBe('[]'); + }); + + it('should serialize a single template with all fields', () => { + const template = new SPFxTemplate( + new SPFxTemplateJsonFile({ + name: 'webpart-minimal', + category: 'webpart', + description: 'A minimal web part template', + version: '1.0.0', + spfxVersion: '1.22.0' + }), + new Map([ + ['file1.txt', Buffer.from('content1')], + ['file2.txt', Buffer.from('content2')] + ]) + ); + + const collection = new SPFxTemplateCollection([template]); + const parsed: ITemplateJsonOutputEntry[] = JSON.parse(collection.toJsonString()); + + expect(parsed).toEqual([ + { + name: 'webpart-minimal', + category: 'webpart', + description: 'A minimal web part template', + version: '1.0.0', + spfxVersion: '1.22.0', + fileCount: 2 + } + ]); + }); + + it('should use null for undefined description', () => { + const template = new SPFxTemplate( + new SPFxTemplateJsonFile({ + name: 'NoDesc', + category: 'webpart', + version: '1.0.0', + spfxVersion: '1.18.0' + }), + new Map() + ); + + const collection = new SPFxTemplateCollection([template]); + const parsed = JSON.parse(collection.toJsonString()); + + expect(parsed[0].description).toBeNull(); + }); + + it('should serialize multiple templates', () => { + const template1 = new SPFxTemplate( + new SPFxTemplateJsonFile({ + name: 'WebPart', + category: 'webpart', + description: 'A web part template', + version: '1.0.0', + spfxVersion: '1.18.0' + }), + new Map([['file.txt', Buffer.from('content')]]) + ); + + const template2 = new SPFxTemplate( + new SPFxTemplateJsonFile({ + name: 'Extension', + category: 'extension', + version: '2.0.0', + spfxVersion: '1.18.0' + }), + new Map() + ); + + const collection = new SPFxTemplateCollection([template1, template2]); + expect(collection.toJsonString()).toMatchSnapshot(); + }); + + it('should return valid JSON', () => { + const template = new SPFxTemplate( + new SPFxTemplateJsonFile({ + name: 'Test', + category: 'library', + version: '1.0.0', + spfxVersion: '1.18.0' + }), + new Map() + ); + + const collection = new SPFxTemplateCollection([template]); + expect(() => JSON.parse(collection.toJsonString())).not.toThrow(); + }); + }); + describe('toFormattedStringAsync', () => { it('should show message for empty collection', async () => { const collection = new SPFxTemplateCollection([]); diff --git a/api/spfx-template-api/src/repositories/test/__snapshots__/SPFxTemplateCollection.test.ts.snap b/api/spfx-template-api/src/repositories/test/__snapshots__/SPFxTemplateCollection.test.ts.snap new file mode 100644 index 00000000..a2436c5f --- /dev/null +++ b/api/spfx-template-api/src/repositories/test/__snapshots__/SPFxTemplateCollection.test.ts.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SPFxTemplateCollection toJsonString should serialize multiple templates 1`] = ` +"[ + { + \\"name\\": \\"WebPart\\", + \\"category\\": \\"webpart\\", + \\"description\\": \\"A web part template\\", + \\"version\\": \\"1.0.0\\", + \\"spfxVersion\\": \\"1.18.0\\", + \\"fileCount\\": 1 + }, + { + \\"name\\": \\"Extension\\", + \\"category\\": \\"extension\\", + \\"description\\": null, + \\"version\\": \\"2.0.0\\", + \\"spfxVersion\\": \\"1.18.0\\", + \\"fileCount\\": 0 + } +]" +`; diff --git a/apps/spfx-cli/README.md b/apps/spfx-cli/README.md index b01c674e..297b91df 100644 --- a/apps/spfx-cli/README.md +++ b/apps/spfx-cli/README.md @@ -73,6 +73,7 @@ spfx list-templates | Flag | Default | Description | |------|---------|-------------| +| `-o`, `--output {json,text}` | `json` | Output format. `json` writes machine-readable JSON to stdout (informational messages go to stderr). `text` writes a human-readable table | | `--spfx-version VERSION` | `version/latest` branch | Branch/tag in the default template repo to use (e.g. `1.22`, `1.23-rc.0`) | | `--template-url URL` | `https://github.com/SharePoint/spfx` | Custom GitHub template repository (default source) | | `--local-source PATH` | — | Path to a local template folder to include (repeatable) | @@ -87,6 +88,18 @@ spfx list-templates ### Examples +List templates as JSON (default, suitable for piping to `jq` or other tools): + +```bash +spfx list-templates +``` + +List templates as a human-readable table: + +```bash +spfx list-templates --output text +``` + Include a local template folder alongside the default source: ```bash diff --git a/apps/spfx-cli/src/cli/actions/ListTemplatesAction.ts b/apps/spfx-cli/src/cli/actions/ListTemplatesAction.ts index b2b9308b..ba11bc53 100644 --- a/apps/spfx-cli/src/cli/actions/ListTemplatesAction.ts +++ b/apps/spfx-cli/src/cli/actions/ListTemplatesAction.ts @@ -2,11 +2,16 @@ // See LICENSE in the project root for license information. import type { Terminal } from '@rushstack/terminal'; +import type { IRequiredCommandLineChoiceParameter } from '@rushstack/ts-command-line'; import { type SPFxTemplateCollection, SPFxTemplateRepositoryManager } from '@microsoft/spfx-template-api'; import { SPFxActionBase } from './SPFxActionBase'; +export type OutputFormat = 'json' | 'text'; + export class ListTemplatesAction extends SPFxActionBase { + private readonly _outputParameter: IRequiredCommandLineChoiceParameter; + public constructor(terminal: Terminal) { super( { @@ -18,10 +23,21 @@ export class ListTemplatesAction extends SPFxActionBase { }, terminal ); + + this._outputParameter = this.defineChoiceParameter({ + parameterLongName: '--output', + parameterShortName: '-o', + description: + 'Output format. "json" writes machine-readable JSON to stdout (informational ' + + 'messages go to stderr). "text" writes a human-readable table.', + alternatives: ['json', 'text'], + defaultValue: 'json' + }); } protected override async onExecuteAsync(): Promise { const terminal: Terminal = this._terminal; + const isJson: boolean = this._outputParameter.value === 'json'; try { const manager: SPFxTemplateRepositoryManager = new SPFxTemplateRepositoryManager(); @@ -37,8 +53,12 @@ export class ListTemplatesAction extends SPFxActionBase { const templates: SPFxTemplateCollection = await this._fetchTemplatesAsync(manager); - const formattedTable: string = await templates.toFormattedStringAsync(); - terminal.writeLine(formattedTable); + if (isJson) { + terminal.write(templates.toJsonString() + '\n'); + } else { + const formattedTable: string = await templates.toFormattedStringAsync(); + terminal.writeLine(formattedTable); + } } catch (error: unknown) { const message: string = error instanceof Error ? error.message : String(error); terminal.writeErrorLine(`Error listing templates: ${message}`); diff --git a/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts b/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts index d6ca0250..dc94c2e2 100644 --- a/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts +++ b/apps/spfx-cli/src/cli/actions/SPFxActionBase.ts @@ -76,6 +76,20 @@ export abstract class SPFxActionBase extends CommandLineAction { 'Required for GitHub Enterprise hosts and private repositories on github.com.', environmentVariable: GITHUB_TOKEN_ENV_VAR_NAME }); + + this.defineFlagParameter({ + parameterLongName: '--verbose', + description: 'Show verbose output.' + }); + } + + /** + * Writes an informational verbose-level message to the terminal. Subclasses can + * override this to redirect informational output away from stdout (for example, + * when an action emits machine-readable JSON to stdout). + */ + protected _writeInfoLine(message: string): void { + this._terminal.writeVerboseLine(message); } /** @@ -108,7 +122,7 @@ export abstract class SPFxActionBase extends CommandLineAction { const token: string | undefined = this._githubTokenParameter.value?.trim() || undefined; - terminal.writeLine(`Using GitHub template source: ${repoUrl}${ref ? ` (branch: ${ref})` : ''}`); + this._writeInfoLine(`Using GitHub template source: ${repoUrl}${ref ? ` (branch: ${ref})` : ''}`); manager.addSource(new PublicGitHubRepositorySource({ repoUrl, branch: ref, terminal, token })); } @@ -118,7 +132,7 @@ export abstract class SPFxActionBase extends CommandLineAction { */ protected _addLocalTemplateSources(manager: SPFxTemplateRepositoryManager): void { for (const localPath of this._localSourceParameter.values) { - this._terminal.writeLine(`Adding local template source: ${localPath}`); + this._writeInfoLine(`Adding local template source: ${localPath}`); manager.addSource(new LocalFileSystemRepositorySource(localPath)); } } @@ -132,7 +146,7 @@ export abstract class SPFxActionBase extends CommandLineAction { const token: string | undefined = this._githubTokenParameter.value?.trim() || undefined; for (const remoteUrl of this._remoteSourcesParameter.values) { const { repoUrl, urlBranch } = parseGitHubUrlAndRef(remoteUrl); - terminal.writeLine( + this._writeInfoLine( `Adding remote template source: ${repoUrl}${urlBranch ? ` (branch: ${urlBranch})` : ''}` ); manager.addSource(new PublicGitHubRepositorySource({ repoUrl, branch: urlBranch, terminal, token })); diff --git a/apps/spfx-cli/src/cli/actions/tests/ListTemplatesAction.test.ts b/apps/spfx-cli/src/cli/actions/tests/ListTemplatesAction.test.ts index 5dd6d342..50efae03 100644 --- a/apps/spfx-cli/src/cli/actions/tests/ListTemplatesAction.test.ts +++ b/apps/spfx-cli/src/cli/actions/tests/ListTemplatesAction.test.ts @@ -7,7 +7,8 @@ import { Terminal, StringBufferTerminalProvider } from '@rushstack/terminal'; import { LocalFileSystemRepositorySource, PublicGitHubRepositorySource, - SPFxTemplateRepositoryManager + SPFxTemplateRepositoryManager, + type ITemplateJsonOutputEntry } from '@microsoft/spfx-template-api'; const { @@ -28,10 +29,23 @@ const MockedLocal = LocalFileSystemRepositorySource as jest.MockedClass< async function runListAsync(extraArgs: string[] = []): Promise { const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(); const parser: SPFxCommandLineParser = new SPFxCommandLineParser(new Terminal(terminalProvider)); - await parser.executeWithoutErrorHandlingAsync(['list-templates', ...extraArgs]); + await parser.executeWithoutErrorHandlingAsync(['list-templates', '--output', 'text', ...extraArgs]); expect(terminalProvider.getAllOutputAsChunks({ asLines: true })).toMatchSnapshot(); } +async function runListJsonAsync( + extraArgs: string[] = [] +): Promise<{ json: string; terminalChunks: string[] }> { + const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider(); + const parser: SPFxCommandLineParser = new SPFxCommandLineParser(new Terminal(terminalProvider)); + await parser.executeWithoutErrorHandlingAsync(['list-templates', ...extraArgs]); + const json: string = terminalProvider.getOutput({ normalizeSpecialCharacters: false }); + return { + json, + terminalChunks: terminalProvider.getAllOutputAsChunks({ asLines: true }) as string[] + }; +} + describe('ListTemplatesAction', () => { const originalEnv = process.env; @@ -97,7 +111,7 @@ describe('ListTemplatesAction', () => { it('passes the terminal instance to PublicGitHubRepositorySource', async () => { const terminal: Terminal = new Terminal(new StringBufferTerminalProvider()); const parser: SPFxCommandLineParser = new SPFxCommandLineParser(terminal); - await parser.executeWithoutErrorHandlingAsync(['list-templates']); + await parser.executeWithoutErrorHandlingAsync(['list-templates', '--output', 'text']); expect(MockedGitHub).toHaveBeenCalledWith({ repoUrl: 'https://github.com/SharePoint/spfx', branch: undefined, @@ -282,4 +296,40 @@ describe('ListTemplatesAction', () => { }); }); }); + + describe('--output json (default)', () => { + it('writes valid JSON to stdout', async () => { + const { json } = await runListJsonAsync(); + expect(() => JSON.parse(json)).not.toThrow(); + }); + + it('includes expected template fields in JSON output', async () => { + const { json } = await runListJsonAsync(); + const parsed: ITemplateJsonOutputEntry[] = JSON.parse(json); + expect(parsed).toEqual([ + { + name: 'webpart-minimal', + category: 'webpart', + description: 'A minimal web part template (no framework) for SPFx', + version: '0.0.1', + spfxVersion: '1.22.2', + fileCount: 23 + } + ]); + }); + + it('does not write table output to the terminal', async () => { + const { terminalChunks } = await runListJsonAsync(); + const hasTableOutput: boolean = terminalChunks.some( + (chunk: string) => chunk.includes('┌') || chunk.includes('Found') + ); + expect(hasTableOutput).toBe(false); + }); + + it('returns empty array for empty collection', async () => { + MockedManager.prototype.getTemplatesAsync.mockResolvedValue(new RealSPFxTemplateCollection([])); + const { json } = await runListJsonAsync(); + expect(JSON.parse(json)).toEqual([]); + }); + }); }); diff --git a/apps/spfx-cli/src/cli/actions/tests/__snapshots__/CreateAction.test.ts.snap b/apps/spfx-cli/src/cli/actions/tests/__snapshots__/CreateAction.test.ts.snap index 23848e2b..72c8afce 100644 --- a/apps/spfx-cli/src/cli/actions/tests/__snapshots__/CreateAction.test.ts.snap +++ b/apps/spfx-cli/src/cli/actions/tests/__snapshots__/CreateAction.test.ts.snap @@ -2,7 +2,7 @@ exports[`CreateAction --package-manager calls writePackageManagerToPackageJsonEnginesAsync after a successful install 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -15,7 +15,7 @@ Array [ exports[`CreateAction --package-manager does not call writePackageManagerToPackageJsonEnginesAsync when install is skipped 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -26,7 +26,7 @@ Array [ exports[`CreateAction --package-manager does not run install when --package-manager is omitted 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -37,7 +37,7 @@ Array [ exports[`CreateAction --package-manager does not run install when --package-manager none is passed 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -48,7 +48,7 @@ Array [ exports[`CreateAction --package-manager installs into --target-dir when explicitly provided 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /custom/dir[n]", "[ log] [n]", @@ -61,7 +61,7 @@ Array [ exports[`CreateAction --package-manager restriction for existing projects when scaffold log does not exist (new project) does not warn about --package-manager 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -74,7 +74,7 @@ Array [ exports[`CreateAction --package-manager restriction for existing projects when scaffold log does not exist (new project) logs session-started and package-manager-selected with the specified PM 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -87,7 +87,7 @@ Array [ exports[`CreateAction --package-manager restriction for existing projects when scaffold log does not exist (new project) runs install with the specified --package-manager 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -100,7 +100,7 @@ Array [ exports[`CreateAction --package-manager restriction for existing projects when scaffold log does not exist (new project) saves the scaffold log after scaffolding 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -111,7 +111,7 @@ Array [ exports[`CreateAction --package-manager restriction for existing projects when scaffold log exists (existing project) does not warn and skips install when --package-manager is "none" 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -122,7 +122,7 @@ Array [ exports[`CreateAction --package-manager restriction for existing projects when scaffold log exists (existing project) does not warn and skips install when --package-manager is omitted 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -133,7 +133,7 @@ Array [ exports[`CreateAction --package-manager restriction for existing projects when scaffold log exists (existing project) does not warn when --package-manager matches the engines value 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -146,7 +146,7 @@ Array [ exports[`CreateAction --package-manager restriction for existing projects when scaffold log exists (existing project) overrides --package-manager with engines value and warns 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -160,7 +160,7 @@ Array [ exports[`CreateAction --package-manager restriction for existing projects when scaffold log exists (existing project) saves the scaffold log after scaffolding 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -173,7 +173,7 @@ Array [ exports[`CreateAction --package-manager restriction for existing projects when scaffold log exists (existing project) uses the specified --package-manager when package.json has no engines entry 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -186,7 +186,7 @@ Array [ exports[`CreateAction --package-manager runs npm install when --package-manager npm is passed 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -199,7 +199,7 @@ Array [ exports[`CreateAction --package-manager runs pnpm install when --package-manager pnpm is passed 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -212,7 +212,7 @@ Array [ exports[`CreateAction --package-manager runs yarn install when --package-manager yarn is passed 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -225,7 +225,7 @@ Array [ exports[`CreateAction --package-manager uses stdio: inherit so package manager output reaches the user 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -239,7 +239,7 @@ Array [ exports[`CreateAction --spfx-version is ignored (with no throw) when --local-source is also provided 1`] = ` Array [ "[warning] --spfx-version is ignored when --local-source is specified.[n]", - "[ log] Adding local template source: /a[n]", + "[verbose] Adding local template source: /a[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -250,7 +250,7 @@ Array [ exports[`CreateAction --spfx-version passes ref to PublicGitHubRepositorySource when --spfx-version is set 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx (branch: version/1.22)[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx (branch: version/1.22)[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -261,7 +261,7 @@ Array [ exports[`CreateAction --spfx-version passes ref when SPFX_TEMPLATE_REPO_URL and --spfx-version are both set 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/my-org/my-templates (branch: version/1.22)[n]", + "[verbose] Using GitHub template source: https://github.com/my-org/my-templates (branch: version/1.22)[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -273,7 +273,7 @@ Array [ exports[`CreateAction --spfx-version uses --spfx-version over branch encoded in SPFX_TEMPLATE_REPO_URL /tree/ path 1`] = ` Array [ "[warning] --template-url contains a branch ('/tree/pending-fixes'). --spfx-version \\"1.22\\" will take precedence.[n]", - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx (branch: version/1.22)[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx (branch: version/1.22)[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -284,7 +284,7 @@ Array [ exports[`CreateAction --target-dir derivation defaults to cwd/solutionName when --target-dir is omitted 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -295,7 +295,7 @@ Array [ exports[`CreateAction --target-dir derivation uses cwd/solutionName when --solution-name is provided without --target-dir 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/my-app[n]", "[ log] [n]", @@ -306,7 +306,7 @@ Array [ exports[`CreateAction --target-dir derivation uses explicit --target-dir and ignores solution name derivation 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /custom/dir[n]", "[ log] [n]", @@ -317,8 +317,8 @@ Array [ exports[`CreateAction GITHUB_TOKEN passthrough passes token to --remote-source sources 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", - "[ log] Adding remote template source: https://github.com/my-org/my-templates[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Adding remote template source: https://github.com/my-org/my-templates[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -329,7 +329,7 @@ Array [ exports[`CreateAction GITHUB_TOKEN passthrough passes token to PublicGitHubRepositorySource when GITHUB_TOKEN is set 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -340,7 +340,7 @@ Array [ exports[`CreateAction GITHUB_TOKEN passthrough passes undefined token when GITHUB_TOKEN is not set 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -351,7 +351,7 @@ Array [ exports[`CreateAction GITHUB_TOKEN passthrough treats whitespace-only GITHUB_TOKEN as undefined 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -362,7 +362,7 @@ Array [ exports[`CreateAction GITHUB_TOKEN passthrough trims whitespace from GITHUB_TOKEN 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -373,7 +373,7 @@ Array [ exports[`CreateAction URL /tree/ branch extraction extracts branch from /tree/ in SPFX_TEMPLATE_REPO_URL 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx (branch: pending-fixes)[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx (branch: pending-fixes)[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -384,7 +384,7 @@ Array [ exports[`CreateAction URL /tree/ branch extraction extracts branch from /tree/ on a GHE host 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.mycompany.com/org/repo (branch: my-branch)[n]", + "[verbose] Using GitHub template source: https://github.mycompany.com/org/repo (branch: my-branch)[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -395,7 +395,7 @@ Array [ exports[`CreateAction URL /tree/ branch extraction handles .git before /tree/ 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx (branch: 1.22)[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx (branch: 1.22)[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -406,7 +406,7 @@ Array [ exports[`CreateAction URL /tree/ branch extraction handles version-like branch name in /tree/ path 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx (branch: 1.22)[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx (branch: 1.22)[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -417,7 +417,7 @@ Array [ exports[`CreateAction URL /tree/ branch extraction ignores subdirectory suffix after the branch name in /tree/ path 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx (branch: main)[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx (branch: main)[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -428,7 +428,7 @@ Array [ exports[`CreateAction URL /tree/ branch extraction passes undefined ref when URL has no /tree/ and no --spfx-version 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -439,7 +439,7 @@ Array [ exports[`CreateAction URL normalization handles .git then slash together 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -450,7 +450,7 @@ Array [ exports[`CreateAction URL normalization strips .git suffix 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -461,7 +461,7 @@ Array [ exports[`CreateAction URL normalization strips multiple trailing slashes 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -472,7 +472,7 @@ Array [ exports[`CreateAction URL normalization strips trailing slash 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -483,7 +483,7 @@ Array [ exports[`CreateAction URL normalization trims whitespace 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -494,7 +494,7 @@ Array [ exports[`CreateAction source selection with --local-source should add a LocalFileSystemRepositorySource for the provided path 1`] = ` Array [ - "[ log] Adding local template source: /path/to/templates[n]", + "[verbose] Adding local template source: /path/to/templates[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -505,8 +505,8 @@ Array [ exports[`CreateAction source selection with --local-source should add multiple sources for multiple --local-source flags 1`] = ` Array [ - "[ log] Adding local template source: /a[n]", - "[ log] Adding local template source: /b[n]", + "[verbose] Adding local template source: /a[n]", + "[verbose] Adding local template source: /b[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -517,8 +517,8 @@ Array [ exports[`CreateAction source selection with --remote-source adds an extra PublicGitHubRepositorySource alongside the default 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", - "[ log] Adding remote template source: https://github.com/my-org/my-templates[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Adding remote template source: https://github.com/my-org/my-templates[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -529,9 +529,9 @@ Array [ exports[`CreateAction source selection with --remote-source adds multiple remote sources for multiple --remote-source flags 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", - "[ log] Adding remote template source: https://github.com/org1/repo1[n]", - "[ log] Adding remote template source: https://github.com/org2/repo2[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Adding remote template source: https://github.com/org1/repo1[n]", + "[verbose] Adding remote template source: https://github.com/org2/repo2[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -542,8 +542,8 @@ Array [ exports[`CreateAction source selection with --remote-source extracts branch from /tree/ in --remote-source URL 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", - "[ log] Adding remote template source: https://github.com/my-org/my-templates (branch: my-branch)[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Adding remote template source: https://github.com/my-org/my-templates (branch: my-branch)[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -554,8 +554,8 @@ Array [ exports[`CreateAction source selection with --remote-source works alongside --local-source without adding the default GitHub source 1`] = ` Array [ - "[ log] Adding local template source: /path/to/templates[n]", - "[ log] Adding remote template source: https://github.com/my-org/my-templates[n]", + "[verbose] Adding local template source: /path/to/templates[n]", + "[verbose] Adding remote template source: https://github.com/my-org/my-templates[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -566,7 +566,7 @@ Array [ exports[`CreateAction source selection without --local-source should add a PublicGitHubRepositorySource with the default URL 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -577,7 +577,7 @@ Array [ exports[`CreateAction source selection without --local-source should use SPFX_TEMPLATE_REPO_URL when set 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/my-org/my-templates[n]", + "[verbose] Using GitHub template source: https://github.com/my-org/my-templates[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -588,7 +588,7 @@ Array [ exports[`CreateAction spfxVersionForBadgeUrl escapes hyphens in prerelease versions for shields.io badge URLs 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -599,7 +599,7 @@ Array [ exports[`CreateAction spfxVersionForBadgeUrl leaves stable versions unchanged 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", @@ -610,7 +610,7 @@ Array [ exports[`CreateAction whitespace env var fix falls back to default URL when SPFX_TEMPLATE_REPO_URL is whitespace-only 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] [Mocked SPFxTemplateCollection][n]", "[ log] targetDir: /tmp/test/test[n]", "[ log] [n]", diff --git a/apps/spfx-cli/src/cli/actions/tests/__snapshots__/ListTemplatesAction.test.ts.snap b/apps/spfx-cli/src/cli/actions/tests/__snapshots__/ListTemplatesAction.test.ts.snap index 7385f743..30763126 100644 --- a/apps/spfx-cli/src/cli/actions/tests/__snapshots__/ListTemplatesAction.test.ts.snap +++ b/apps/spfx-cli/src/cli/actions/tests/__snapshots__/ListTemplatesAction.test.ts.snap @@ -2,8 +2,8 @@ exports[`ListTemplatesAction --local-source (additive) adds LocalFileSystemRepositorySource AND still includes the default GitHub source 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", - "[ log] Adding local template source: /path/to/templates[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Adding local template source: /path/to/templates[n]", "[ log] Found 1 template:[n]", "[ log] [n]", "[ log] ┌─────────────────┬──────────┬─────────────────────────────────────────────────────┬─────────┬──────────────┬───────┐[n]", @@ -16,9 +16,9 @@ Array [ exports[`ListTemplatesAction --local-source (additive) adds multiple local sources for multiple --local-source flags 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", - "[ log] Adding local template source: /a[n]", - "[ log] Adding local template source: /b[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Adding local template source: /a[n]", + "[verbose] Adding local template source: /b[n]", "[ log] Found 1 template:[n]", "[ log] [n]", "[ log] ┌─────────────────┬──────────┬─────────────────────────────────────────────────────┬─────────┬──────────────┬───────┐[n]", @@ -31,8 +31,8 @@ Array [ exports[`ListTemplatesAction --remote-source (additive) adds an extra PublicGitHubRepositorySource alongside the default 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", - "[ log] Adding remote template source: https://github.com/my-org/my-templates[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Adding remote template source: https://github.com/my-org/my-templates[n]", "[ log] Found 1 template:[n]", "[ log] [n]", "[ log] ┌─────────────────┬──────────┬─────────────────────────────────────────────────────┬─────────┬──────────────┬───────┐[n]", @@ -45,9 +45,9 @@ Array [ exports[`ListTemplatesAction --remote-source (additive) adds multiple remote sources for multiple --remote-source flags 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", - "[ log] Adding remote template source: https://github.com/org1/repo1[n]", - "[ log] Adding remote template source: https://github.com/org2/repo2[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Adding remote template source: https://github.com/org1/repo1[n]", + "[verbose] Adding remote template source: https://github.com/org2/repo2[n]", "[ log] Found 1 template:[n]", "[ log] [n]", "[ log] ┌─────────────────┬──────────┬─────────────────────────────────────────────────────┬─────────┬──────────────┬───────┐[n]", @@ -60,8 +60,8 @@ Array [ exports[`ListTemplatesAction --remote-source (additive) extracts branch from /tree/ in --remote-source URL 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", - "[ log] Adding remote template source: https://github.com/my-org/my-templates (branch: my-branch)[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Adding remote template source: https://github.com/my-org/my-templates (branch: my-branch)[n]", "[ log] Found 1 template:[n]", "[ log] [n]", "[ log] ┌─────────────────┬──────────┬─────────────────────────────────────────────────────┬─────────┬──────────────┬───────┐[n]", @@ -74,7 +74,7 @@ Array [ exports[`ListTemplatesAction --spfx-version extracts branch from /tree/ in SPFX_TEMPLATE_REPO_URL when no --spfx-version 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx (branch: pending-fixes)[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx (branch: pending-fixes)[n]", "[ log] Found 1 template:[n]", "[ log] [n]", "[ log] ┌─────────────────┬──────────┬─────────────────────────────────────────────────────┬─────────┬──────────────┬───────┐[n]", @@ -87,7 +87,7 @@ Array [ exports[`ListTemplatesAction --spfx-version passes ref to default PublicGitHubRepositorySource 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx (branch: version/1.22)[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx (branch: version/1.22)[n]", "[ log] Found 1 template:[n]", "[ log] [n]", "[ log] ┌─────────────────┬──────────┬─────────────────────────────────────────────────────┬─────────┬──────────────┬───────┐[n]", @@ -101,7 +101,7 @@ Array [ exports[`ListTemplatesAction --spfx-version takes precedence over branch encoded in SPFX_TEMPLATE_REPO_URL /tree/ path 1`] = ` Array [ "[warning] --template-url contains a branch ('/tree/pending-fixes'). --spfx-version \\"1.22\\" will take precedence.[n]", - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx (branch: version/1.22)[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx (branch: version/1.22)[n]", "[ log] Found 1 template:[n]", "[ log] [n]", "[ log] ┌─────────────────┬──────────┬─────────────────────────────────────────────────────┬─────────┬──────────────┬───────┐[n]", @@ -114,9 +114,9 @@ Array [ exports[`ListTemplatesAction combined --local-source and --remote-source adds default GitHub, local, and remote sources all together 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", - "[ log] Adding local template source: /local/path[n]", - "[ log] Adding remote template source: https://github.com/my-org/extra-templates[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Adding local template source: /local/path[n]", + "[verbose] Adding remote template source: https://github.com/my-org/extra-templates[n]", "[ log] Found 1 template:[n]", "[ log] [n]", "[ log] ┌─────────────────┬──────────┬─────────────────────────────────────────────────────┬─────────┬──────────────┬───────┐[n]", @@ -129,7 +129,7 @@ Array [ exports[`ListTemplatesAction default source (always included) adds PublicGitHubRepositorySource with the default URL 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] Found 1 template:[n]", "[ log] [n]", "[ log] ┌─────────────────┬──────────┬─────────────────────────────────────────────────────┬─────────┬──────────────┬───────┐[n]", @@ -142,7 +142,7 @@ Array [ exports[`ListTemplatesAction default source (always included) falls back to default URL when SPFX_TEMPLATE_REPO_URL is whitespace-only 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/SharePoint/spfx[n]", + "[verbose] Using GitHub template source: https://github.com/SharePoint/spfx[n]", "[ log] Found 1 template:[n]", "[ log] [n]", "[ log] ┌─────────────────┬──────────┬─────────────────────────────────────────────────────┬─────────┬──────────────┬───────┐[n]", @@ -155,7 +155,7 @@ Array [ exports[`ListTemplatesAction default source (always included) uses SPFX_TEMPLATE_REPO_URL when set 1`] = ` Array [ - "[ log] Using GitHub template source: https://github.com/my-org/my-templates[n]", + "[verbose] Using GitHub template source: https://github.com/my-org/my-templates[n]", "[ log] Found 1 template:[n]", "[ log] [n]", "[ log] ┌─────────────────┬──────────┬─────────────────────────────────────────────────────┬─────────┬──────────────┬───────┐[n]", diff --git a/apps/spfx-cli/src/cli/test/__snapshots__/CommandLineHelp.test.ts.snap b/apps/spfx-cli/src/cli/test/__snapshots__/CommandLineHelp.test.ts.snap index b7be4dfb..7a10de53 100644 --- a/apps/spfx-cli/src/cli/test/__snapshots__/CommandLineHelp.test.ts.snap +++ b/apps/spfx-cli/src/cli/test/__snapshots__/CommandLineHelp.test.ts.snap @@ -3,9 +3,10 @@ exports[`CommandLineHelp prints the help: create 1`] = ` "usage: spfx create [-h] [--template-url URL] [--spfx-version VERSION] [--local-source TEMPLATE_PATH] [--remote-source URL] - [--github-token TOKEN] [--target-dir TARGET_DIR] --template - TEMPLATE_NAME --library-name LIBRARY_NAME --component-name - COMPONENT_NAME [--component-alias COMPONENT_ALIAS] + [--github-token TOKEN] [--verbose] + [--target-dir TARGET_DIR] --template TEMPLATE_NAME + --library-name LIBRARY_NAME --component-name COMPONENT_NAME + [--component-alias COMPONENT_ALIAS] [--component-description COMPONENT_DESCRIPTION] [--solution-name SOLUTION_NAME] [--package-manager {npm,pnpm,yarn,none}] @@ -33,6 +34,7 @@ Optional arguments: private repositories on github.com. This parameter may alternatively be specified via the GITHUB_TOKEN environment variable. + --verbose Show verbose output. --target-dir TARGET_DIR The directory to scaffold into. When omitted, defaults to a subfolder named after the solution in @@ -81,6 +83,7 @@ exports[`CommandLineHelp prints the help: list-templates 1`] = ` "usage: spfx list-templates [-h] [--template-url URL] [--spfx-version VERSION] [--local-source TEMPLATE_PATH] [--remote-source URL] [--github-token TOKEN] + [--verbose] [-o {json,text}] This command lists all available templates from the default GitHub source and @@ -106,5 +109,11 @@ Optional arguments: private repositories on github.com. This parameter may alternatively be specified via the GITHUB_TOKEN environment variable. + --verbose Show verbose output. + -o {json,text}, --output {json,text} + Output format. \\"json\\" writes machine-readable JSON to + stdout (informational messages go to stderr). \\"text\\" + writes a human-readable table. The default value is + \\"json\\". " `; diff --git a/apps/spfx-cli/src/start.ts b/apps/spfx-cli/src/start.ts index 93fb8de9..5543768d 100644 --- a/apps/spfx-cli/src/start.ts +++ b/apps/spfx-cli/src/start.ts @@ -5,7 +5,8 @@ import { ConsoleTerminalProvider, Terminal } from '@rushstack/terminal'; import { SPFxCommandLineParser } from './cli/SPFxCommandLineParser'; -const terminal: Terminal = new Terminal(new ConsoleTerminalProvider()); +const verboseEnabled: boolean = process.argv.includes('--verbose'); +const terminal: Terminal = new Terminal(new ConsoleTerminalProvider({ verboseEnabled })); async function main(): Promise { const commandLine: SPFxCommandLineParser = new SPFxCommandLineParser(terminal); diff --git a/common/changes/@microsoft/spfx-cli/json-output-mode_2026-04-25.json b/common/changes/@microsoft/spfx-cli/json-output-mode_2026-04-25.json new file mode 100644 index 00000000..b8f73b53 --- /dev/null +++ b/common/changes/@microsoft/spfx-cli/json-output-mode_2026-04-25.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/spfx-cli", + "comment": "Adds JSON output mode for the `spfx list-templates` command (default `--output json`, with `--output text` for the human-readable table). Also adds a global `--verbose` flag to all actions and demotes source-registration messages (`Using GitHub template source`, `Adding local template source`, `Adding remote template source`) to verbose-level output.", + "type": "none" + } + ], + "packageName": "@microsoft/spfx-cli" +}