create-innovator 0.4.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +163 -0
- package/dist/cli.js +19 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1 +1,164 @@
|
|
|
1
|
+
# create-innovator
|
|
2
|
+
|
|
1
3
|
[](http://commitizen.github.io/cz-cli/)
|
|
4
|
+
|
|
5
|
+
A CLI scaffolding tool that creates new projects from the Storm Reply [innovator-template](https://github.com/stormreply/innovator-template). It clones the template repository, replaces placeholder names with your project name, installs dependencies, and creates an initial commit — all in one command.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- [Node.js](https://nodejs.org/) >= 24
|
|
10
|
+
- [pnpm](https://pnpm.io/) >= 10.29.2 (or corepack will enable it automatically)
|
|
11
|
+
- [GitHub CLI (`gh`)](https://cli.github.com/) installed and available on your `PATH`
|
|
12
|
+
- A **GitHub Personal Access Token** with `repo` and `read:packages` scopes and access to the `stormreply` organization
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
Run directly with pnpm:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm create innovator
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or with npx:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx create-innovator
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Options
|
|
29
|
+
|
|
30
|
+
| Flag | Alias | Description |
|
|
31
|
+
| ---------------- | ----- | ----------------------------------------------------------- |
|
|
32
|
+
| `--name <name>` | `-n` | Project name (skips the interactive prompt) |
|
|
33
|
+
| `--latest` | `-l` | Skip version selection and use the latest version |
|
|
34
|
+
| `--experimental` | `-e` | Include pre-release template versions in the version picker |
|
|
35
|
+
|
|
36
|
+
### Examples
|
|
37
|
+
|
|
38
|
+
Create a project interactively:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pnpm create innovator
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Create a project with a specific name:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pnpm create innovator --name my-project
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Use the latest template version without prompting:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pnpm create innovator --latest
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Include experimental template versions:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pnpm create innovator --experimental
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### What happens when you run it
|
|
63
|
+
|
|
64
|
+
1. **GitHub authentication** — reads your token from `~/.npmrc` or prompts you to enter one. Validates required scopes (`repo`, `read:packages`) and access to the `stormreply` organization.
|
|
65
|
+
2. **Version selection** — fetches available template tags and lets you pick a version (or auto-selects the latest when `--latest` is used).
|
|
66
|
+
3. **Clone** — shallow-clones the template at the selected tag using `gh repo clone`, then re-initializes a fresh git repository.
|
|
67
|
+
4. **Name replacement** — replaces `innovator-template` throughout the project with your chosen name in all case variants (kebab-case, camelCase, PascalCase, Title Case).
|
|
68
|
+
5. **Template cleanup** — removes template-specific files that are not needed in the new project.
|
|
69
|
+
6. **Setup** — installs dependencies via `pnpm install`, updates test snapshots, and creates an initial commit.
|
|
70
|
+
|
|
71
|
+
If the automatic setup fails, the CLI prints the commands to run manually.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Development
|
|
76
|
+
|
|
77
|
+
### Getting started
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
git clone git@github.com:stormreply/create-innovator.git
|
|
81
|
+
cd create-innovator
|
|
82
|
+
pnpm install
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Scripts
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pnpm dev # Run the CLI locally via tsx
|
|
89
|
+
pnpm build # Build with tsup (outputs to dist/)
|
|
90
|
+
pnpm test # Run all tests (vitest)
|
|
91
|
+
pnpm test:watch # Run tests in watch mode
|
|
92
|
+
pnpm lint # ESLint
|
|
93
|
+
pnpm format # Prettier (write mode)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Run a single test file:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pnpm test -- src/scaffold/clone.test.ts
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Project structure
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
src/
|
|
106
|
+
├── cli.ts # Entry point — arg parsing (citty) and interactive UI (@clack/prompts)
|
|
107
|
+
├── cli.test.ts
|
|
108
|
+
├── auth/
|
|
109
|
+
│ ├── github.ts # Token validation, scope + org access checks
|
|
110
|
+
│ ├── github.test.ts
|
|
111
|
+
│ ├── prompts.ts # Interactive token input
|
|
112
|
+
│ ├── prompts.test.ts
|
|
113
|
+
│ ├── token-storage.ts # Read/write token from ~/.npmrc
|
|
114
|
+
│ └── token-storage.test.ts
|
|
115
|
+
├── scaffold/
|
|
116
|
+
│ ├── clone.ts # gh CLI check, tag fetching, shallow clone + git init
|
|
117
|
+
│ ├── clone.test.ts
|
|
118
|
+
│ ├── template.ts # Name replacement engine + template file cleanup
|
|
119
|
+
│ ├── template.test.ts
|
|
120
|
+
│ ├── setup.ts # pnpm install, snapshot update, initial commit
|
|
121
|
+
│ └── setup.test.ts
|
|
122
|
+
└── utils/
|
|
123
|
+
├── case.ts # PascalCase conversion (wraps Remeda)
|
|
124
|
+
├── case.test.ts
|
|
125
|
+
└── constants.ts # GitHub org, repo, scopes, registry URL
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Code style
|
|
129
|
+
|
|
130
|
+
- **ESM** throughout (`"type": "module"`, `.js` extensions in imports)
|
|
131
|
+
- **TypeScript** with strict mode, target ES2022, NodeNext module resolution
|
|
132
|
+
- **Prettier**: single quotes, semicolons, trailing commas, 120 char line width
|
|
133
|
+
- **Dependencies** are pinned to exact versions (no `^` or `~` prefixes)
|
|
134
|
+
|
|
135
|
+
### Testing
|
|
136
|
+
|
|
137
|
+
Tests use [Vitest](https://vitest.dev/) with `globals: true` — no imports needed for `describe`, `it`, `expect`, or `vi`.
|
|
138
|
+
|
|
139
|
+
Key patterns:
|
|
140
|
+
|
|
141
|
+
- Shell commands (`child_process.execFile`) are mocked via `vi.mock('node:child_process')` with callback-style mocks
|
|
142
|
+
- File system operations in template tests use [`memfs`](https://github.com/nicknisi/memfs) (in-memory filesystem)
|
|
143
|
+
- `@clack/prompts` is mocked in most test files to suppress UI output
|
|
144
|
+
|
|
145
|
+
### Git hooks (Husky)
|
|
146
|
+
|
|
147
|
+
| Hook | Action |
|
|
148
|
+
| -------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
149
|
+
| `pre-commit` | Runs lint-staged — ESLint + Prettier on staged `*.{js,json,md,ts}` files, vitest on related `*.ts` files |
|
|
150
|
+
| `prepare-commit-msg` | Launches Commitizen interactively to guide you through a conventional commit message |
|
|
151
|
+
| `commit-msg` | Enforces [Conventional Commits](https://www.conventionalcommits.org/) via commitlint |
|
|
152
|
+
| `pre-push` | Runs the full test suite (only if working tree is clean) |
|
|
153
|
+
|
|
154
|
+
### Commits
|
|
155
|
+
|
|
156
|
+
This project follows [Conventional Commits](https://www.conventionalcommits.org/) and is [Commitizen](http://commitizen.github.io/cz-cli/) friendly. The `prepare-commit-msg` hook automatically launches Commitizen when you run `git commit`, guiding you through a structured commit message prompt — no extra commands needed.
|
|
157
|
+
|
|
158
|
+
### Releasing
|
|
159
|
+
|
|
160
|
+
Releases are managed with [release-it](https://github.com/release-it/release-it):
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
pnpm release
|
|
164
|
+
```
|
package/dist/cli.js
CHANGED
|
@@ -209,6 +209,17 @@ async function fetchReleaseTags(token, includeExperimental = false) {
|
|
|
209
209
|
}
|
|
210
210
|
return tags;
|
|
211
211
|
}
|
|
212
|
+
async function selectLatestVersion(token, includeExperimental = false) {
|
|
213
|
+
const s = p3.spinner();
|
|
214
|
+
s.start("Fetching latest template version");
|
|
215
|
+
const tags = await fetchReleaseTags(token, includeExperimental);
|
|
216
|
+
if (tags.length === 0) {
|
|
217
|
+
s.stop("No versions found");
|
|
218
|
+
throw new Error(`No release tags found in ${GITHUB_ORG}/${TEMPLATE_REPO}.`);
|
|
219
|
+
}
|
|
220
|
+
s.stop(`Using latest version: ${tags[0]}`);
|
|
221
|
+
return tags[0];
|
|
222
|
+
}
|
|
212
223
|
async function selectVersion(token, includeExperimental = false) {
|
|
213
224
|
const s = p3.spinner();
|
|
214
225
|
s.start("Fetching available template versions");
|
|
@@ -372,6 +383,13 @@ var main = defineCommand({
|
|
|
372
383
|
required: false,
|
|
373
384
|
type: "string"
|
|
374
385
|
},
|
|
386
|
+
latest: {
|
|
387
|
+
alias: ["l"],
|
|
388
|
+
description: "Skip version selection and use the latest version",
|
|
389
|
+
required: false,
|
|
390
|
+
type: "boolean",
|
|
391
|
+
default: false
|
|
392
|
+
},
|
|
375
393
|
experimental: {
|
|
376
394
|
alias: ["e"],
|
|
377
395
|
description: "Include experimental (pre-release) versions",
|
|
@@ -394,7 +412,7 @@ var main = defineCommand({
|
|
|
394
412
|
try {
|
|
395
413
|
const token = await ensureGitHubAuth();
|
|
396
414
|
await ensureGhCli();
|
|
397
|
-
const tag = await selectVersion(token, args.experimental);
|
|
415
|
+
const tag = args.latest ? await selectLatestVersion(token, args.experimental) : await selectVersion(token, args.experimental);
|
|
398
416
|
await cloneTemplate(projectName, tag);
|
|
399
417
|
await replaceTemplateNames(projectName, projectName);
|
|
400
418
|
await removeTemplateFiles(projectName);
|