Add --remote-source flag to create command#182
Add --remote-source flag to create command#182nick-pape wants to merge 4 commits intoSharePoint:mainfrom
Conversation
Adds a repeatable --remote-source URL flag to the create command, letting users point at custom public GitHub repos containing SPFx templates. Remote sources are additive — they work alongside both the default GitHub source and --local-template sources. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a repeatable --remote-source URL flag to spfx create so users can include additional public GitHub repositories as template sources, alongside the default GitHub source and/or any --local-template sources.
Changes:
- Introduces
--remote-source(repeatable) tocreate, and wires it into repository source selection. - Adds TDD coverage and updates help/output snapshots for the new flag.
- Updates the CLI README and adds a Rush change file entry.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| common/changes/@microsoft/spfx-cli/remote-source_2026-03-24.json | Rush change file for the CLI change set. |
| apps/spfx-cli/src/cli/test/snapshots/CommandLineHelp.test.ts.snap | Updates CLI help snapshot to include --remote-source. |
| apps/spfx-cli/src/cli/actions/tests/snapshots/CreateAction.test.ts.snap | Updates snapshots for new source-selection test cases. |
| apps/spfx-cli/src/cli/actions/tests/CreateAction.test.ts | Adds tests for single/multiple remote sources, /tree/<ref> extraction, and interaction with --local-template. |
| apps/spfx-cli/src/cli/actions/CreateAction.ts | Implements the new --remote-source parameter and adds remote sources to the repository manager. |
| apps/spfx-cli/README.md | Documents --remote-source in the flags table and adds an example. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for (const remoteUrl of this._remoteSourcesParameter.values) { | ||
| const { repoUrl: additionalRepoUrl, urlBranch: additionalUrlBranch } = | ||
| parseGitHubUrlAndRef(remoteUrl); | ||
| terminal.writeLine( | ||
| `Adding remote template source: ${additionalRepoUrl}` + |
There was a problem hiding this comment.
--remote-source URLs are passed through parseGitHubUrlAndRef() without any validation (e.g., empty/whitespace values, or non-github.com hosts). Since PublicGitHubRepositorySource ultimately only supports github.com (and builds downloads via codeload.github.com), invalid/unsupported URLs will fail later during getTemplatesAsync() with a less actionable error. Consider validating each remoteUrl up front (trim + non-empty + github.com host + expected path shape) and throwing a clear error that references --remote-source and the supported URL formats (including optional /tree/<ref>).
There was a problem hiding this comment.
Valid observation. Today PublicGitHubRepositorySource already throws with the URL and reason (e.g. Invalid GitHub repository URL: ... or Failed to download repository: 404 Not Found), so users do get an actionable error. Early validation would improve the message but isn't blocking for v0. Filed as a potential follow-up alongside #178 (error message improvements).
Main merged PR SharePoint#175 which extracted parseGitHubUrlAndRef to utilities/github.ts and introduced SPFxActionBase. Re-added PublicGitHubRepositorySource import and parseGitHubUrlAndRef import needed by the --remote-source loop. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
SharePoint#180 changed the constructor to take an options object. Updated the --remote-source call site in CreateAction to use { repoUrl, branch, terminal }. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| expect(MockedGitHub).toHaveBeenNthCalledWith( | ||
| 1, | ||
| 'https://github.com/SharePoint/spfx', | ||
| undefined, | ||
| expect.anything() |
There was a problem hiding this comment.
PublicGitHubRepositorySource is constructed with an options object ({ repoUrl, branch, terminal }) in this test file and in the implementation. This expectation uses positional args, so it won’t match the actual constructor call. Update it to assert the options-object shape for the default source call.
| expect(MockedGitHub).toHaveBeenNthCalledWith( | ||
| 2, | ||
| 'https://github.com/my-org/my-templates', | ||
| undefined, | ||
| expect.anything() |
There was a problem hiding this comment.
This expectation also assumes a positional constructor signature for PublicGitHubRepositorySource. Align it with the actual call shape by asserting { repoUrl: 'https://github.com/my-org/my-templates', branch: undefined, terminal: expect.anything() }.
| expect(MockedGitHub).toHaveBeenNthCalledWith( | ||
| 2, | ||
| 'https://github.com/my-org/my-templates', | ||
| 'my-branch', | ||
| expect.anything() | ||
| ); |
There was a problem hiding this comment.
PublicGitHubRepositorySource is instantiated with an options object, but this assertion checks positional args. Update it to validate the second call receives { repoUrl: 'https://github.com/my-org/my-templates', branch: 'my-branch', terminal: expect.anything() }.
| expect(MockedGitHub).toHaveBeenNthCalledWith( | |
| 2, | |
| 'https://github.com/my-org/my-templates', | |
| 'my-branch', | |
| expect.anything() | |
| ); | |
| expect(MockedGitHub).toHaveBeenNthCalledWith(2, { | |
| repoUrl: 'https://github.com/my-org/my-templates', | |
| branch: 'my-branch', | |
| terminal: expect.anything() | |
| }); |
| expect(MockedGitHub).toHaveBeenCalledWith( | ||
| 'https://github.com/my-org/my-templates', | ||
| undefined, | ||
| expect.anything() | ||
| ); |
There was a problem hiding this comment.
This toHaveBeenCalledWith assertion uses positional args, but PublicGitHubRepositorySource is constructed with an options object throughout the codebase. Update this to assert the options-object call shape so the test matches the real constructor invocation.
| this._remoteSourcesParameter = this.defineStringListParameter({ | ||
| parameterLongName: '--remote-source', | ||
| argumentName: 'URL', | ||
| description: 'Public GitHub repository URL to use as an additional template source (repeatable)' | ||
| }); |
There was a problem hiding this comment.
Move this into the base class and deduplicate with the list action.
| // Always process --remote-source URLs (additive with either local or default sources) | ||
| for (const remoteUrl of this._remoteSourcesParameter.values) { | ||
| const { repoUrl: additionalRepoUrl, urlBranch: additionalUrlBranch } = | ||
| parseGitHubUrlAndRef(remoteUrl); | ||
| terminal.writeLine( | ||
| `Adding remote template source: ${additionalRepoUrl}` + | ||
| `${additionalUrlBranch ? ` (branch: ${additionalUrlBranch})` : ''}` | ||
| ); | ||
| manager.addSource( | ||
| new PublicGitHubRepositorySource({ | ||
| repoUrl: additionalRepoUrl, | ||
| branch: additionalUrlBranch, | ||
| terminal | ||
| }) | ||
| ); | ||
| } |
Description
Adds a repeatable
--remote-source URLflag to thecreatecommand, letting users point at custom public GitHub repos containing SPFx templates.Remote sources are additive — they work alongside both the default GitHub source and
--local-templatesources:spfx create --template foospfx create --template foo --local-template ./tspfx create --template foo --remote-source URLspfx create --template foo --local-template ./t --remote-source URLThe
--remote-sourceflag also supports/tree/<ref>syntax in the URL for branch selection (using the existingparseGitHubUrlAndRefhelper).How was this tested
rushx build— 50/50 tests pass (4 new--remote-sourcetests, 45 existing, 1 help snapshot)/tree/, and interaction with--local-templateType of change
Closes #90
🤖 Generated with Claude Code