Skip to content

Commit a06fe33

Browse files
committed
feat(cli): initial version
1 parent b2158ca commit a06fe33

File tree

13 files changed

+9662
-2
lines changed

13 files changed

+9662
-2
lines changed

.github/workflows/release.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: write
10+
issues: write
11+
pull-requests: write
12+
id-token: write # required for npm Trusted Publishing (OIDC)
13+
14+
jobs:
15+
release:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0 # semantic-release needs tags/history
21+
22+
- uses: actions/setup-node@v4
23+
with:
24+
node-version: "20"
25+
registry-url: "https://registry.npmjs.org"
26+
27+
- run: npm ci
28+
- run: npm run build
29+
- run: npx semantic-release
30+
env:
31+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
/node_modules

.releaserc.cjs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module.exports = {
2+
branches: ["main"],
3+
plugins: [
4+
["@semantic-release/commit-analyzer", { preset: "conventionalcommits" }],
5+
["@semantic-release/release-notes-generator", { preset: "conventionalcommits" }],
6+
["@semantic-release/changelog", { changelogFile: "CHANGELOG.md" }],
7+
["@semantic-release/npm", { npmPublish: true }],
8+
["@semantic-release/github", { successComment: false, failComment: false }],
9+
["@semantic-release/git", {
10+
assets: ["CHANGELOG.md", "package.json", "package-lock.json"],
11+
message: "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
12+
}]
13+
]
14+
};

.vscode/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"cSpell.words": [
3+
"webparts"
4+
]
5+
}

README.md

Lines changed: 231 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,231 @@
1-
# spfx-sample-cli
2-
A CLI to replicate SPFx samples without having to clone the entire repository
1+
# @pnp/spfx-sample
2+
3+
Fetch a single SPFx sample from `pnp/sp-dev-fx-webparts` and `pnp/sp-dev-fx-extensions` **without cloning the entire repo**.
4+
5+
This CLI supports two download strategies:
6+
7+
- **Git (recommended):** partial clone + sparse checkout (fast + best for contributors)
8+
- **API (tokenless fallback):** downloads only the sample files via GitHub APIs (no git required)
9+
10+
11+
## Requirements
12+
13+
- **Node.js:** >= 18
14+
- **Git (for `--method git` / default `auto` when git is installed):** Git >= 2.25 (for sparse checkout cone mode)
15+
16+
## Install / Run
17+
18+
Run without installing:
19+
20+
```bash
21+
npx @pnp/spfx-sample get react-hello-world
22+
```
23+
24+
Or install globally:
25+
26+
```bash
27+
npm i -g @pnp/spfx-sample
28+
spfx-sample get react-hello-world
29+
```
30+
31+
## Basic usage
32+
33+
```bash
34+
spfx-sample get <sample-folder> [options]
35+
```
36+
37+
Examples:
38+
39+
```bash
40+
# Fetch a sample into ./react-hello-world (default)
41+
spfx-sample get react-hello-world
42+
43+
# You can also include "samples/" prefix
44+
spfx-sample get samples/react-hello-world
45+
46+
# Pick a branch/tag/commit
47+
spfx-sample get react-hello-world --ref main
48+
49+
# Choose destination
50+
spfx-sample get react-hello-world --dest ./my-sample
51+
52+
# Overwrite existing destination
53+
spfx-sample get react-hello-world --force
54+
55+
# Download a sample from a different repo
56+
spfx-sample get jquery-application-toastr --repo sp-dev-fx-extensions
57+
spfx-sample get BasicCard-CardComposition --repo sp-dev-fx-aces
58+
```
59+
60+
Defaults (unless overridden):
61+
62+
- `--owner pnp`
63+
- `--repo sp-dev-fx-webparts`
64+
- `--ref main`
65+
66+
67+
## Methods: `--method auto|git|api`
68+
69+
### `--method auto` (default)
70+
71+
- Uses **git** if git is available
72+
- Otherwise falls back to **api**
73+
74+
```bash
75+
spfx-sample get react-hello-world --method auto
76+
```
77+
78+
### `--method git`
79+
80+
Uses **partial clone + sparse checkout** to fetch only the selected sample content.
81+
82+
```bash
83+
spfx-sample get react-hello-world --method git
84+
```
85+
86+
### `--method api`
87+
88+
Tokenless fallback (no git required). Downloads only the files under the sample folder.
89+
90+
```bash
91+
spfx-sample get react-hello-world --method api
92+
```
93+
94+
Note: GitHub’s anonymous API has rate limits (typically 60 requests/hour per IP). If you hit rate limits, use the git method.
95+
96+
---
97+
98+
## Modes (git only): `--mode extract|repo`
99+
100+
### `--mode extract` (default)
101+
102+
- Fetches the sample
103+
- Writes only the sample contents to `--dest`
104+
- No `.git` folder
105+
106+
```bash
107+
spfx-sample get react-hello-world --mode extract
108+
```
109+
110+
### `--mode repo` (contributor mode)
111+
112+
- Creates a sparse git working copy in `--dest`
113+
- Leaves `.git` intact so you can branch/commit/PR
114+
- Sample is located at: `dest/samples/<sample-folder>`
115+
116+
```bash
117+
spfx-sample get react-hello-world --mode repo --method git --dest ./work
118+
cd ./work
119+
git checkout -b my-change
120+
```
121+
122+
> `--mode repo` requires `--method git` (API mode cannot create a git repo).
123+
124+
## Local development
125+
126+
### Install dependencies
127+
128+
```bash
129+
npm ci
130+
```
131+
132+
### Build
133+
134+
```bash
135+
npm run build
136+
```
137+
138+
### Run locally
139+
140+
```bash
141+
node dist/cli.js --help
142+
node dist/cli.js get react-hello-world
143+
```
144+
145+
## Test locally without publishing
146+
147+
### Option A: Run via `npx` from the local folder (fastest loop)
148+
149+
```bash
150+
npm run build
151+
npx --no-install . --help
152+
npx --no-install . get react-hello-world
153+
```
154+
155+
### Option B: Link globally (tests global CLI behavior)
156+
157+
```bash
158+
npm run build
159+
npm link
160+
spfx-sample --help
161+
spfx-sample get react-hello-world
162+
```
163+
164+
Unlink later:
165+
166+
```bash
167+
npm unlink -g @pnp/spfx-sample
168+
```
169+
170+
### Option C: Test the real “published artifact” via `npm pack` (closest to publishing)
171+
172+
```bash
173+
npm run build
174+
npm pack
175+
```
176+
177+
This creates a `.tgz` file. In a clean folder:
178+
179+
```bash
180+
mkdir ../spfx-sample-test
181+
cd ../spfx-sample-test
182+
npm init -y
183+
npm i ../path/to/<the-tgz-file>.tgz
184+
npx spfx-sample --help
185+
npx spfx-sample get react-hello-world
186+
```
187+
188+
## Contributing / Commit conventions
189+
190+
This repo uses **semantic-release**, which determines version bumps from **Conventional Commits**.
191+
192+
Use commit messages like:
193+
194+
- `feat(cli): add --method api fallback`**minor**
195+
- `fix(git): handle sparse checkout edge case`**patch**
196+
- `feat!: change default command behavior`**major**
197+
- Include `BREAKING CHANGE:` in the commit body to force a major bump
198+
199+
If you don’t use `feat:`/`fix:` (or another release-triggering type), **no release will be published**.
200+
201+
202+
## Publishing (semantic-release)
203+
204+
Publishing happens automatically from `main`:
205+
206+
1. Merge PR(s) into `main`
207+
2. GitHub Actions runs the Release workflow
208+
3. `semantic-release`:
209+
- calculates next version from commits
210+
- updates `CHANGELOG.md`
211+
- creates a GitHub Release + tag
212+
- publishes to npm as `@pnp/spfx-sample`
213+
214+
### Notes
215+
216+
- Do **not** manually edit `version` for releases. semantic-release controls it.
217+
- Scoped packages must be published as public — this repo uses `publishConfig.access = "public"`.
218+
219+
### Dry run (no publish)
220+
221+
To see what semantic-release *would* do:
222+
223+
```bash
224+
npm run release:dry
225+
```
226+
227+
## Repo / Package names
228+
229+
- GitHub repo: `pnp/spfx-sample-cli`
230+
- npm package: `@pnp/spfx-sample`
231+
- CLI command: `spfx-sample`

0 commit comments

Comments
 (0)