Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@ jobs:
matrix:
include:
- os: ubuntu-latest
nodeVersion: 22.14.x
displayName: Ubuntu (Node 22)
- os: ubuntu-latest
nodeVersion: 24.14.x
displayName: Ubuntu
- os: windows-latest
nodeVersion: 24.14.x
displayName: Windows

name: Build on ${{ matrix.displayName }}
Expand All @@ -31,9 +36,9 @@ jobs:

- run: git config --global user.name "SPFx CLI Bot"

- uses: actions/setup-node@v3
- uses: actions/setup-node@v6
with:
node-version: 22.15.0
node-version: ${{ matrix.nodeVersion }}

- name: Verify Change Logs
run: node common/scripts/install-run-rush.js change --verify
Expand Down
8 changes: 5 additions & 3 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ dist-storybook/
# Prettier-specific overrides
#-------------------------------------------------------------------------------------------------------------------

# Machine-egnerated files
# Machine-generated files
common/reviews/
common/changes/
common/scripts/
Expand All @@ -142,7 +142,9 @@ pnpm-lock.yaml
*.md

# These aren't valid code, they're templates for code, and many can't be parsed as expected
templates/*
templates/**/*.ts
templates/**/*.tsx
templates/**/*.scss

# These are generated from templates, and are validated to match exactly
examples/*
examples/*
18 changes: 18 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,27 @@ When filing issues, use the appropriate template from `.github/ISSUE_TEMPLATE/`:
- **Bug report**: Include reproduction steps, expected vs actual behavior, CLI/Node versions, and the affected template if applicable
- **Feature request**: Include a description, use case, and alternatives considered

## README Maintenance

The two published packages each have a README that is displayed on NPM:

- `apps/spfx-cli/README.md`
- `api/spfx-template-api/README.md`

**Whenever you make a change to a published package, update its README to reflect the change.** This includes:

- New CLI flags or changed flag behavior → update the `spfx-cli` README flag tables and examples
- New or removed templates → update the templates table in the `spfx-cli` README
- New exported classes, types, or functions → update the API reference table in the `spfx-template-api` README
- Changed usage patterns or render context fields → update code examples in the `spfx-template-api` README
- Changed Node.js version requirements → update the requirements line in both READMEs

These READMEs are written for NPM viewers — keep them practical and example-driven. Do not add internal monorepo details or contributor instructions.
Comment thread
iclanton marked this conversation as resolved.

## Important Notes

1. **Always use the correct Node version** - This is the #1 cause of build failures
2. **Rush manages dependencies** - Don't run npm/pnpm directly in project folders
3. **Peer dependency warnings are OK** - The SPFx packages have peer deps we intentionally don't install
4. **Templates must match examples** - Keep them synchronized
5. **READMEs must stay current** - Update `apps/spfx-cli/README.md` and `api/spfx-template-api/README.md` whenever the published packages change
154 changes: 154 additions & 0 deletions api/spfx-template-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# @microsoft/spfx-template-api

> **Pre-release:** APIs may change before the stable 1.0 release.

Programmatic API for loading, rendering, and writing [SharePoint Framework (SPFx)](https://aka.ms/spfx) project templates. This is the engine that powers [`@microsoft/spfx-cli`](https://www.npmjs.com/package/@microsoft/spfx-cli). Use it directly when you need to integrate SPFx scaffolding into your own tooling.

```bash
npm install @microsoft/spfx-template-api
```

**Requires Node.js `>=22.14.0 <23.0.0` or `>=24.12.0 <25.0.0`**

---

## Quick start

```typescript
import {
SPFxTemplateRepositoryManager,
PublicGitHubRepositorySource,
SPFxTemplateWriter
} from '@microsoft/spfx-template-api';

// 1. Load templates from GitHub
const manager = new SPFxTemplateRepositoryManager();
manager.addSource(new PublicGitHubRepositorySource('https://github.com/SharePoint/spfx'));

const templates = await manager.getTemplatesAsync();
const template = templates.get('webpart-react');
if (!template) throw new Error('Template not found');

// 2. Render to an in-memory file system
const fs = await template.renderAsync(
{
solution_name: 'my-solution',
libraryName: 'my-spfx-library',
spfxVersion: template.spfxVersion,
spfxVersionForBadgeUrl: template.spfxVersion.replace(/-/g, '--'),
componentId: '<uuid>',
featureId: '<uuid>',
solutionId: '<uuid>',
componentAlias: 'MyWebPart',
componentNameUnescaped: 'My Web Part',
componentNameCamelCase: 'myWebPart',
componentNameHyphenCase: 'my-web-part',
componentNameCapitalCase: 'MyWebPart',
componentNameAllCaps: 'MY_WEB_PART',
componentDescription: 'My Web Part description',
eslintProfile: 'react'
},
'/path/to/output'
);

// 3. Write files to disk (merges into existing SPFx solutions automatically)
const writer = new SPFxTemplateWriter();
await writer.writeAsync(fs, '/path/to/output');
```

---

## Template sources

### `PublicGitHubRepositorySource`

Fetches templates from a public GitHub repository. Pin a specific SPFx version with an optional branch/tag ref.

```typescript
import { PublicGitHubRepositorySource } from '@microsoft/spfx-template-api';

// Latest (default branch)
new PublicGitHubRepositorySource('https://github.com/SharePoint/spfx');

// Specific version
new PublicGitHubRepositorySource('https://github.com/SharePoint/spfx', '1.22');
```

### `LocalFileSystemRepositorySource`

Loads templates from a local directory — useful for offline workflows, CI environments, or authoring custom templates.

```typescript
import { LocalFileSystemRepositorySource } from '@microsoft/spfx-template-api';

new LocalFileSystemRepositorySource('./path/to/templates');
```

### Combining sources

`SPFxTemplateRepositoryManager` merges templates from all registered sources. Later sources can override templates from earlier ones.

```typescript
import {
SPFxTemplateRepositoryManager,
PublicGitHubRepositorySource,
LocalFileSystemRepositorySource
} from '@microsoft/spfx-template-api';

const manager = new SPFxTemplateRepositoryManager();
manager.addSource(new PublicGitHubRepositorySource('https://github.com/SharePoint/spfx'));
manager.addSource(new LocalFileSystemRepositorySource('./my-custom-templates'));

const templates = await manager.getTemplatesAsync();
```

---

## Writing to disk

`SPFxTemplateWriter` commits the in-memory `MemFsEditor` to the target directory. When scaffolding into an existing SPFx solution, it merges generated content into existing files rather than overwriting them.

```typescript
const writer = new SPFxTemplateWriter();
await writer.writeAsync(fs, targetDir);
```

### Merge helpers

The writer uses these helpers internally. You can also import them directly for custom merge scenarios:

| Class | Merges |
|-------|--------|
| `JsonMergeHelper` | Abstract base for JSON/JSONC merge helpers (parse/serialize utilities) |
| `PackageJsonMergeHelper` | `package.json` (preserves existing scripts and dependencies) |
| `ConfigJsonMergeHelper` | SPFx `config/` files |
| `PackageSolutionJsonMergeHelper` | `config/package-solution.json` |
| `ServeJsonMergeHelper` | `config/serve.json` |

---

## API reference

| Export | Description |
|--------|-------------|
| `SPFxTemplateRepositoryManager` | Aggregates sources and returns a `SPFxTemplateCollection` |
| `SPFxTemplateCollection` | `Map<string, SPFxTemplate>` of all loaded templates |
| `SPFxTemplate` | Single template — exposes `name`, `spfxVersion`, and `renderAsync()` |
| `PublicGitHubRepositorySource` | Loads templates from a public GitHub repo |
| `LocalFileSystemRepositorySource` | Loads templates from the local filesystem |
| `BaseSPFxTemplateRepositorySource` | Base class for building custom template sources |
| `SPFxRepositorySource` | Interface implemented by all source types |
| `SPFxTemplateWriter` | Writes a rendered `MemFsEditor` to disk with merge support |
| `IMergeHelper` | Interface for implementing custom merge helpers |
| `ServeJsonMergeHelper` | Merges `config/serve.json` (also available standalone) |
| `ISPFxTemplateJson` | Shape of the `template.json` manifest |
| `SPFxTemplateDefinitionSchema` | Zod schema for validating a `template.json` |
| `SPFxTemplateJsonFile` | Typed wrapper around a parsed `template.json` file |
| `SPFxTemplateRepositorySourceTypes` | Union type of all built-in repository source types |
| `IRenderOptions` | Context object passed to `template.renderAsync()` |

---

## License

MIT © Microsoft Corporation
141 changes: 140 additions & 1 deletion apps/spfx-cli/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,140 @@
# @microsoft/spfx-cli
# @microsoft/spfx-cli

> **Pre-release:** APIs and commands may change before the stable 1.0 release.

The official CLI for scaffolding [SharePoint Framework (SPFx)](https://aka.ms/spfx) projects.

```bash
npm install -g @microsoft/spfx-cli
```

**Requires Node.js `>=22.14.0 <23.0.0` or `>=24.12.0 <25.0.0`**

---

## Quick start

```bash
spfx create \
--template webpart-react \
--library-name my-spfx-library \
--component-name "Hello World"
```

This downloads the `webpart-react` template from the [SharePoint/spfx](https://github.com/SharePoint/spfx) template repository and scaffolds a React-based web part into the current directory.

---

## `spfx create`

Scaffolds a new SPFx component. Templates are pulled from the [SharePoint/spfx](https://github.com/SharePoint/spfx) GitHub repository by default.

### Required flags

| Flag | Description |
|------|-------------|
| `--template NAME` | Template to use (see [Templates](#templates) below) |
| `--library-name NAME` | npm library name for the component (e.g. `my-spfx-lib`) |
| `--component-name NAME` | Display name of the component (e.g. `"Hello World"`) |

### Optional flags

| Flag | Default | Description |
|------|---------|-------------|
| `--target-dir PATH` | current directory | Directory to scaffold into |
| `--solution-name NAME` | kebab-cased component name | SharePoint solution name |
| `--component-alias ALIAS` | same as `--component-name` | Short identifier for the component |
| `--component-description TEXT` | `"<name> description"` | Component description string |
| `--spfx-version VERSION` | repo default branch | Branch/tag in the template repo to use (e.g. `1.22`, `1.23-rc.0`) |
| `--template-url URL` | `https://github.com/SharePoint/spfx` | Custom GitHub template repository |
| `--local-template PATH` | — | Path to a local template folder (repeatable; bypasses GitHub) |

### Environment variables

| Variable | Description |
|----------|-------------|
| `SPFX_TEMPLATE_REPO_URL` | Equivalent to `--template-url` |
| `SPFX_CI_MODE=1` | Internal/testing-only: produces deterministic UUIDs for CI; not shown in `--help`; subject to change |

---

## Templates

Templates are fetched at runtime from the [SharePoint/spfx](https://github.com/SharePoint/spfx) GitHub repository. Use `--spfx-version` to target a specific release branch, or `--local-template` to use templates from disk.

### Web Parts

| Name | Description |
|------|-------------|
| `webpart-minimal` | Bare-bones web part, no UI framework |
| `webpart-noframework` | Full web part scaffold, no UI framework |
| `webpart-react` | Web part with React and Fluent UI |

### Extensions

| Name | Description |
|------|-------------|
| `extension-application-customizer` | Application Customizer |
| `extension-fieldcustomizer-minimal` | Field Customizer, no UI framework (minimal) |
| `extension-fieldcustomizer-noframework` | Field Customizer, no UI framework |
| `extension-fieldcustomizer-react` | Field Customizer with React |
| `extension-formcustomizer-noframework` | Form Customizer, no UI framework |
| `extension-formcustomizer-react` | Form Customizer with React |
| `extension-listviewcommandset` | List View Command Set |
| `extension-search-query-modifier` | Search Query Modifier |

### Adaptive Card Extensions

| Name | Description |
|------|-------------|
| `ace-data-visualization` | Data Visualization card |
| `ace-generic-card` | Generic card |
| `ace-generic-image-card` | Generic image card |
| `ace-generic-primarytext-card` | Generic primary text card |
| `ace-search-card` | Search card |

### Other

| Name | Description |
|------|-------------|
| `library` | Shared SPFx library component |

---

## More examples

Scaffold into a specific directory:

```bash
spfx create \
--template webpart-react \
--library-name my-spfx-library \
--component-name "My Dashboard" \
--target-dir ./my-project
```

Target a specific SPFx version:

```bash
spfx create \
--template webpart-react \
--library-name my-spfx-library \
--component-name "My Web Part" \
--spfx-version 1.22
```

Use a local template (offline / custom templates):

```bash
spfx create \
--template webpart-minimal \
--library-name my-spfx-library \
--component-name "My Web Part" \
--local-template ./path/to/templates
```

---

## License

MIT © Microsoft Corporation
3 changes: 2 additions & 1 deletion apps/spfx-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "lib/index.js",
"license": "MIT",
"engines": {
"node": ">=22.14.0 <23.0.0 || >=24.5.0 <25.0.0"
"node": ">=22.14.0 <23.0.0 || >=24.12.0 <25.0.0"
},
"scripts": {
"build": "heft test --clean",
Expand All @@ -17,6 +17,7 @@
},
"dependencies": {
"@microsoft/spfx-template-api": "workspace:*",
"@rushstack/node-core-library": "~5.20.3",
"@rushstack/ts-command-line": "~5.3.3",
"@rushstack/terminal": "~0.22.3",
"lodash": "^4.17.23",
Expand Down
Loading
Loading