@next-friday/create-app 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +94 -2
- package/dist/main.mjs +414 -0
- package/package.json +84 -3
- package/templates/base/.agents/hooks/agent-shell-gate.sh +355 -0
- package/templates/base/.agents/hooks/agent-suppression-gate.sh +45 -0
- package/templates/base/.agents/rules/automation-owned-checks.md +5 -0
- package/templates/base/.agents/rules/code-comments.md +3 -0
- package/templates/base/.agents/rules/current-state-authority.md +17 -0
- package/templates/base/.agents/rules/demand-driven-configuration.md +7 -0
- package/templates/base/.agents/rules/dependency-security.md +5 -0
- package/templates/base/.agents/rules/deterministic-testing.md +15 -0
- package/templates/base/.agents/rules/diagnostics-and-error-handling.md +7 -0
- package/templates/base/.agents/rules/git-workflow.md +10 -0
- package/templates/base/.agents/rules/no-suppression.md +7 -0
- package/templates/base/.agents/rules/script-driven-changes.md +7 -0
- package/templates/base/.agents/rules/single-author-style.md +7 -0
- package/templates/base/.claude/rules/agents-guide.md +1 -0
- package/templates/base/.claude/rules/automation-owned-checks.md +1 -0
- package/templates/base/.claude/rules/code-comments.md +1 -0
- package/templates/base/.claude/rules/current-state-authority.md +1 -0
- package/templates/base/.claude/rules/demand-driven-configuration.md +1 -0
- package/templates/base/.claude/rules/dependency-security.md +12 -0
- package/templates/base/.claude/rules/diagnostics-and-error-handling.md +1 -0
- package/templates/base/.claude/rules/git-workflow.md +1 -0
- package/templates/base/.claude/rules/no-suppression.md +1 -0
- package/templates/base/.claude/rules/script-driven-changes.md +1 -0
- package/templates/base/.claude/rules/single-author-style.md +1 -0
- package/templates/base/.claude/settings.json +61 -0
- package/templates/base/.commitlintrc.json +3 -0
- package/templates/base/.cursor/BUGBOT.md +17 -0
- package/templates/base/.cursor/hooks/agent-constitution-gate.sh +32 -0
- package/templates/base/.cursor/hooks.json +26 -0
- package/templates/base/.cursor/rules/automation-owned-checks.mdc +10 -0
- package/templates/base/.cursor/rules/code-comments.mdc +8 -0
- package/templates/base/.cursor/rules/current-state-authority.mdc +22 -0
- package/templates/base/.cursor/rules/demand-driven-configuration.mdc +12 -0
- package/templates/base/.cursor/rules/dependency-security.mdc +11 -0
- package/templates/base/.cursor/rules/diagnostics-and-error-handling.mdc +12 -0
- package/templates/base/.cursor/rules/git-workflow.mdc +15 -0
- package/templates/base/.cursor/rules/no-suppression.mdc +12 -0
- package/templates/base/.cursor/rules/script-driven-changes.mdc +12 -0
- package/templates/base/.cursor/rules/single-author-style.mdc +12 -0
- package/templates/base/.editorconfig +13 -0
- package/templates/base/.gitattributes +1 -0
- package/templates/base/.github/CODEOWNERS +2 -0
- package/templates/base/.github/ISSUE_TEMPLATE/config.yml +1 -0
- package/templates/base/.github/dependabot.yml +77 -0
- package/templates/base/.github/file-filters.yml +19 -0
- package/templates/base/.github/labeler.yml +25 -0
- package/templates/base/.github/requirements/diff-cover.txt +213 -0
- package/templates/base/.github/workflows/deploy.yml +124 -0
- package/templates/base/.github/workflows/osv-scanner.yml +34 -0
- package/templates/base/.github/workflows/pr-triage.yml +29 -0
- package/templates/base/.github/workflows/secret-scan.yml +50 -0
- package/templates/base/.github/workflows/validate-pr-title.yml +48 -0
- package/templates/base/.github/workflows/zizmor.yml +62 -0
- package/templates/base/.github/zizmor.yml +4 -0
- package/templates/base/.gitleaks.toml +14 -0
- package/templates/base/.husky/commit-msg +3 -0
- package/templates/base/.husky/pre-commit +17 -0
- package/templates/base/.husky/pre-merge-commit +7 -0
- package/templates/base/.husky/pre-push +15 -0
- package/templates/base/.nvmrc +1 -0
- package/templates/base/SECURITY.md +15 -0
- package/templates/base/knip.json +3 -0
- package/templates/base/pnpm-workspace.yaml +18 -0
- package/templates/base/railway.json +9 -0
- package/templates/base/scripts/check-agent-gates.ts +252 -0
- package/templates/base/scripts/sync-agent-rules.ts +121 -0
- package/templates/nestjs/.agents/rules/typescript-first.md +7 -0
- package/templates/nestjs/.agents/rules.json +41 -0
- package/templates/nestjs/.claude/rules/deterministic-testing.md +20 -0
- package/templates/nestjs/.claude/rules/typescript-first.md +13 -0
- package/templates/nestjs/.cursor/rules/deterministic-testing.mdc +21 -0
- package/templates/nestjs/.cursor/rules/typescript-first.mdc +13 -0
- package/templates/nestjs/.dependency-cruiser.json +28 -0
- package/templates/nestjs/.dockerignore +8 -0
- package/templates/nestjs/.env.example +1 -0
- package/templates/nestjs/.github/ISSUE_TEMPLATE/bug.yml +66 -0
- package/templates/nestjs/.github/ISSUE_TEMPLATE/feature.yml +50 -0
- package/templates/nestjs/.github/pull_request_template.md +25 -0
- package/templates/nestjs/.github/workflows/ci.yml +181 -0
- package/templates/nestjs/.github/workflows/sast.yml +45 -0
- package/templates/nestjs/.lintstagedrc.json +10 -0
- package/templates/nestjs/.prettierignore +2 -0
- package/templates/nestjs/.prettierrc.json +1 -0
- package/templates/nestjs/AGENTS.md +8 -0
- package/templates/nestjs/CONTRIBUTING.md +57 -0
- package/templates/nestjs/Dockerfile +27 -0
- package/templates/nestjs/README.md +78 -0
- package/templates/nestjs/_gitignore +47 -0
- package/templates/nestjs/eslint.config.ts +7 -0
- package/templates/nestjs/package.json +83 -0
- package/templates/nestjs/src/app.controller.test.ts +23 -0
- package/templates/nestjs/src/app.controller.ts +13 -0
- package/templates/nestjs/src/app.module.ts +12 -0
- package/templates/nestjs/src/app.service.ts +10 -0
- package/templates/nestjs/src/health/health.controller.test.ts +17 -0
- package/templates/nestjs/src/health/health.controller.ts +14 -0
- package/templates/nestjs/src/health/health.module.ts +10 -0
- package/templates/nestjs/src/main.ts +25 -0
- package/templates/nestjs/src/port.test.ts +29 -0
- package/templates/nestjs/src/port.ts +20 -0
- package/templates/nestjs/test/app.e2e.test.ts +40 -0
- package/templates/nestjs/tsconfig.build.json +9 -0
- package/templates/nestjs/tsconfig.json +20 -0
- package/templates/nestjs/vitest.config.ts +13 -0
- package/templates/nestjs/vitest.e2e.config.ts +8 -0
- package/templates/nextjs/.agents/rules/typescript-first.md +7 -0
- package/templates/nextjs/.agents/rules.json +41 -0
- package/templates/nextjs/.claude/rules/deterministic-testing.md +21 -0
- package/templates/nextjs/.claude/rules/typescript-first.md +15 -0
- package/templates/nextjs/.cursor/rules/deterministic-testing.mdc +21 -0
- package/templates/nextjs/.cursor/rules/typescript-first.mdc +13 -0
- package/templates/nextjs/.dependency-cruiser.json +34 -0
- package/templates/nextjs/.dockerignore +10 -0
- package/templates/nextjs/.env.example +1 -0
- package/templates/nextjs/.github/ISSUE_TEMPLATE/bug.yml +66 -0
- package/templates/nextjs/.github/ISSUE_TEMPLATE/feature.yml +50 -0
- package/templates/nextjs/.github/pull_request_template.md +25 -0
- package/templates/nextjs/.github/workflows/ci.yml +187 -0
- package/templates/nextjs/.github/workflows/sast.yml +46 -0
- package/templates/nextjs/.lintstagedrc.json +10 -0
- package/templates/nextjs/.prettierignore +2 -0
- package/templates/nextjs/AGENTS.md +18 -0
- package/templates/nextjs/CONTRIBUTING.md +57 -0
- package/templates/nextjs/Dockerfile +23 -0
- package/templates/nextjs/README.md +74 -0
- package/templates/nextjs/_gitignore +51 -0
- package/templates/nextjs/app/favicon.ico +0 -0
- package/templates/nextjs/app/globals.css +26 -0
- package/templates/nextjs/app/health/route.test.ts +20 -0
- package/templates/nextjs/app/health/route.ts +23 -0
- package/templates/nextjs/app/layout.tsx +29 -0
- package/templates/nextjs/app/page.test.ts +11 -0
- package/templates/nextjs/app/page.tsx +8 -0
- package/templates/nextjs/eslint.config.ts +32 -0
- package/templates/nextjs/knip.json +4 -0
- package/templates/nextjs/next.config.ts +35 -0
- package/templates/nextjs/package.json +78 -0
- package/templates/nextjs/playwright.config.ts +27 -0
- package/templates/nextjs/postcss.config.mjs +7 -0
- package/templates/nextjs/prettier.config.ts +11 -0
- package/templates/nextjs/test/home.e2e.test.ts +11 -0
- package/templates/nextjs/tsconfig.json +33 -0
- package/templates/nextjs/vitest.config.ts +12 -0
- package/templates/nodejs/.agents/rules/typescript-first.md +7 -0
- package/templates/nodejs/.agents/rules.json +41 -0
- package/templates/nodejs/.claude/rules/deterministic-testing.md +20 -0
- package/templates/nodejs/.claude/rules/typescript-first.md +13 -0
- package/templates/nodejs/.cursor/rules/deterministic-testing.mdc +21 -0
- package/templates/nodejs/.cursor/rules/typescript-first.mdc +13 -0
- package/templates/nodejs/.dependency-cruiser.json +28 -0
- package/templates/nodejs/.dockerignore +8 -0
- package/templates/nodejs/.env.example +1 -0
- package/templates/nodejs/.github/ISSUE_TEMPLATE/bug.yml +66 -0
- package/templates/nodejs/.github/ISSUE_TEMPLATE/feature.yml +50 -0
- package/templates/nodejs/.github/pull_request_template.md +25 -0
- package/templates/nodejs/.github/workflows/ci.yml +179 -0
- package/templates/nodejs/.github/workflows/sast.yml +45 -0
- package/templates/nodejs/.lintstagedrc.json +10 -0
- package/templates/nodejs/.prettierignore +2 -0
- package/templates/nodejs/.prettierrc.json +1 -0
- package/templates/nodejs/AGENTS.md +8 -0
- package/templates/nodejs/CONTRIBUTING.md +56 -0
- package/templates/nodejs/Dockerfile +27 -0
- package/templates/nodejs/README.md +74 -0
- package/templates/nodejs/_gitignore +47 -0
- package/templates/nodejs/eslint.config.ts +7 -0
- package/templates/nodejs/package.json +66 -0
- package/templates/nodejs/src/main.ts +22 -0
- package/templates/nodejs/src/port.test.ts +30 -0
- package/templates/nodejs/src/port.ts +20 -0
- package/templates/nodejs/src/server.test.ts +60 -0
- package/templates/nodejs/src/server.ts +37 -0
- package/templates/nodejs/tsconfig.build.json +9 -0
- package/templates/nodejs/tsconfig.json +19 -0
- package/templates/nodejs/vitest.config.ts +12 -0
- package/templates/payload/.agents/rules/typescript-first.md +7 -0
- package/templates/payload/.agents/rules.json +41 -0
- package/templates/payload/.claude/rules/deterministic-testing.md +21 -0
- package/templates/payload/.claude/rules/typescript-first.md +15 -0
- package/templates/payload/.cursor/rules/deterministic-testing.mdc +21 -0
- package/templates/payload/.cursor/rules/typescript-first.mdc +13 -0
- package/templates/payload/.dependency-cruiser.json +34 -0
- package/templates/payload/.dockerignore +9 -0
- package/templates/payload/.editorconfig-checker.json +3 -0
- package/templates/payload/.env.example +4 -0
- package/templates/payload/.github/ISSUE_TEMPLATE/bug.yml +66 -0
- package/templates/payload/.github/ISSUE_TEMPLATE/feature.yml +50 -0
- package/templates/payload/.github/pull_request_template.md +25 -0
- package/templates/payload/.github/workflows/ci.yml +204 -0
- package/templates/payload/.github/workflows/sast.yml +46 -0
- package/templates/payload/.lintstagedrc.json +10 -0
- package/templates/payload/.prettierignore +5 -0
- package/templates/payload/.prettierrc.json +1 -0
- package/templates/payload/AGENTS.md +19 -0
- package/templates/payload/CONTRIBUTING.md +58 -0
- package/templates/payload/Dockerfile +26 -0
- package/templates/payload/README.md +98 -0
- package/templates/payload/_gitignore +52 -0
- package/templates/payload/eslint.config.ts +35 -0
- package/templates/payload/next.config.ts +43 -0
- package/templates/payload/package.json +86 -0
- package/templates/payload/playwright.config.ts +27 -0
- package/templates/payload/src/app/(frontend)/layout.tsx +18 -0
- package/templates/payload/src/app/(frontend)/page.test.ts +14 -0
- package/templates/payload/src/app/(frontend)/page.tsx +14 -0
- package/templates/payload/src/app/(frontend)/styles.css +15 -0
- package/templates/payload/src/app/(payload)/admin/[[...segments]]/not-found.tsx +24 -0
- package/templates/payload/src/app/(payload)/admin/[[...segments]]/page.tsx +24 -0
- package/templates/payload/src/app/(payload)/admin/importMap.ts +6 -0
- package/templates/payload/src/app/(payload)/api/[...slug]/route.ts +19 -0
- package/templates/payload/src/app/(payload)/api/graphql/route.ts +8 -0
- package/templates/payload/src/app/(payload)/api/graphql-playground/route.ts +7 -0
- package/templates/payload/src/app/(payload)/custom.scss +0 -0
- package/templates/payload/src/app/(payload)/layout.tsx +31 -0
- package/templates/payload/src/app/health/route.test.ts +20 -0
- package/templates/payload/src/app/health/route.ts +23 -0
- package/templates/payload/src/collections/media.test.ts +27 -0
- package/templates/payload/src/collections/media.ts +18 -0
- package/templates/payload/src/collections/users.test.ts +10 -0
- package/templates/payload/src/collections/users.ts +10 -0
- package/templates/payload/src/environment.test.ts +32 -0
- package/templates/payload/src/environment.ts +14 -0
- package/templates/payload/src/migrations/20261030_005055_initial.json +969 -0
- package/templates/payload/src/migrations/20261030_005055_initial.ts +134 -0
- package/templates/payload/src/migrations/index.ts +9 -0
- package/templates/payload/src/payload-types.ts +339 -0
- package/templates/payload/src/payload.config.ts +47 -0
- package/templates/payload/test/home.e2e.test.ts +16 -0
- package/templates/payload/tsconfig.json +34 -0
- package/templates/payload/vitest.config.ts +19 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Next Friday
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,95 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @next-friday/create-app
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Create a Next Friday project with the company's tooling, Git hooks, CI, and agent rules already in place.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
pnpm create @next-friday/app
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Answer the prompts for a project name and a stack. To skip the prompts, pass both:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pnpm create @next-friday/app my-api --stack nestjs
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Without a terminal, as in CI or an agent, the name and `--stack` are required. Pass `.` as the name to create the project in the current directory, for example in a fresh clone.
|
|
16
|
+
|
|
17
|
+
## Stacks
|
|
18
|
+
|
|
19
|
+
| `--stack` | Starts with |
|
|
20
|
+
| :-------- | :---------------------------------------------------------------- |
|
|
21
|
+
| `nodejs` | A TypeScript HTTP server on `node:http` with a `/health` endpoint |
|
|
22
|
+
| `nestjs` | A NestJS app with a Terminus `/health` endpoint |
|
|
23
|
+
| `nextjs` | A Next.js App Router app with React and Tailwind CSS |
|
|
24
|
+
| `payload` | Payload CMS on Next.js with PostgreSQL |
|
|
25
|
+
|
|
26
|
+
Every stack is built from [`templates/base`](templates/base), which holds the shared company setup, plus its own directory under [`templates/`](templates), which holds the stack-specific files.
|
|
27
|
+
|
|
28
|
+
## What every project includes
|
|
29
|
+
|
|
30
|
+
- **Code quality:** TypeScript, ESLint and Prettier with the Next Friday configs, Vitest with coverage, fast-check, Knip, dependency-cruiser, editorconfig-checker, and sort-package-json.
|
|
31
|
+
- **Git hooks:** Husky runs gitleaks and lint-staged before each commit, commitlint on each message, and `pnpm check` before each push. Commits and pushes to `main` are blocked after the first one.
|
|
32
|
+
- **CI:** GitHub Actions run the static checks, tests with patch coverage, the build, end-to-end tests for Next.js and Payload, a container build and vulnerability scan, Semgrep, OSV-Scanner, gitleaks, TruffleHog, zizmor, and pull request title validation. Dependabot keeps dependencies current.
|
|
33
|
+
- **Deployment:** a `Dockerfile` that runs as the `node` user, and a Railway workflow that promotes one commit through `dev`, `uat`, and `prod`.
|
|
34
|
+
- **Agent rules:** shared rules in `.agents/rules`, with hooks that enforce them in Claude Code and Cursor, and a Cursor Bugbot configuration.
|
|
35
|
+
|
|
36
|
+
## Requirements
|
|
37
|
+
|
|
38
|
+
- Node.js 24.22.1, the version in the generated `.nvmrc`. The CLI itself runs on the range in [`package.json#engines`](package.json).
|
|
39
|
+
- pnpm: `npm install --global pnpm`. The generated project pins its pnpm version in `package.json#packageManager`.
|
|
40
|
+
- Git 2.28 or later.
|
|
41
|
+
- gitleaks 8.19 or later and jq, which the generated project's Git hooks need for the first commit.
|
|
42
|
+
|
|
43
|
+
## Options
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
pnpm create @next-friday/app [name] [--stack nodejs|nestjs|nextjs|payload]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
| Option | Description |
|
|
50
|
+
| :--------------- | :--------------------------------------------------------------- |
|
|
51
|
+
| `name` | Project directory and package name. Asked when left out. |
|
|
52
|
+
| `--stack` | `nodejs`, `nestjs`, `nextjs`, or `payload`. Asked when left out. |
|
|
53
|
+
| `--skip-git` | Do not run `git init`. |
|
|
54
|
+
| `--skip-install` | Do not run `pnpm install`. |
|
|
55
|
+
| `-v, --version` | Print the version. |
|
|
56
|
+
| `-h, --help` | Print the usage. |
|
|
57
|
+
|
|
58
|
+
The name must:
|
|
59
|
+
|
|
60
|
+
- use lowercase letters, digits, `.`, `_`, or `-`, and start with a letter or digit;
|
|
61
|
+
- not end in `.`, and not be `node_modules`, `favicon.ico`, or a name that Windows reserves, such as `con`;
|
|
62
|
+
- point to a directory that does not exist or is empty, apart from `.git`.
|
|
63
|
+
|
|
64
|
+
## What the CLI does
|
|
65
|
+
|
|
66
|
+
1. Copies the template into the project directory and replaces the template's package name with the project name.
|
|
67
|
+
2. Writes `.env` from `.env.example`. For Payload, it sets `PAYLOAD_SECRET` to a random secret.
|
|
68
|
+
3. Runs `git init --initial-branch=main`, unless the directory is already in a Git repository.
|
|
69
|
+
4. Runs `pnpm install`, which also installs the Git hooks.
|
|
70
|
+
5. Prints the next steps, for example:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
cd my-api
|
|
74
|
+
pnpm start:dev
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The development script is `pnpm dev` for Next.js and Payload, and `pnpm start:dev` for Node.js and NestJS.
|
|
78
|
+
|
|
79
|
+
Then follow **After creating a project from this template** in the project's `README.md` to make the first commit and set up the repository.
|
|
80
|
+
|
|
81
|
+
## Troubleshooting
|
|
82
|
+
|
|
83
|
+
- **`pnpm install` fails:** the CLI keeps the project and exits with 1. Fix the printed error, such as a Node.js version outside the project's `package.json#engines`, then run `pnpm install` in the project.
|
|
84
|
+
- **`--skip-git`:** run `git init --initial-branch=main` before `pnpm install`, so the install can set up the Git hooks.
|
|
85
|
+
- **`--skip-install`:** the Git hooks are not set up until you run `pnpm install`.
|
|
86
|
+
- **Cancelling:** Ctrl+C exits with 130. During the copy, it also removes the partly copied project.
|
|
87
|
+
- **Payload:** set `DATABASE_URL` in `.env` to a PostgreSQL database before you start the app.
|
|
88
|
+
|
|
89
|
+
## Contributing
|
|
90
|
+
|
|
91
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
[MIT](LICENSE)
|
package/dist/main.mjs
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { cancel, intro, isCancel, log, outro, select, text } from "@clack/prompts";
|
|
3
|
+
import { chmod, cp, readFile, readdir, rename, writeFile } from "node:fs/promises";
|
|
4
|
+
import { existsSync, lstatSync, readFileSync, readdirSync, rmSync } from "node:fs";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { randomBytes } from "node:crypto";
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { parseArgs } from "node:util";
|
|
10
|
+
// src/stacks.ts
|
|
11
|
+
const stacks = [
|
|
12
|
+
{
|
|
13
|
+
id: "nodejs",
|
|
14
|
+
label: "Node.js",
|
|
15
|
+
hint: "HTTP service without a framework"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: "nestjs",
|
|
19
|
+
label: "NestJS",
|
|
20
|
+
hint: "HTTP API"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "nextjs",
|
|
24
|
+
label: "Next.js",
|
|
25
|
+
hint: "Web app"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "payload",
|
|
29
|
+
label: "Payload",
|
|
30
|
+
hint: "Headless CMS on Next.js and PostgreSQL"
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
const developmentScripts = {
|
|
34
|
+
nodejs: "start:dev",
|
|
35
|
+
nestjs: "start:dev",
|
|
36
|
+
nextjs: "dev",
|
|
37
|
+
payload: "dev"
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Check whether a string names a supported stack.
|
|
41
|
+
* @param value - Stack name from the command line.
|
|
42
|
+
* @returns Whether `value` is a supported stack id.
|
|
43
|
+
*/
|
|
44
|
+
function isStackId(value) {
|
|
45
|
+
return stacks.some((stack) => stack.id === value);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Name the package a stack's template declares, which the CLI replaces with the project name.
|
|
49
|
+
* @param stack - Supported stack id.
|
|
50
|
+
* @returns Package name in the template's `package.json`.
|
|
51
|
+
*/
|
|
52
|
+
function templateName(stack) {
|
|
53
|
+
return `next-friday-template-${stack}`;
|
|
54
|
+
}
|
|
55
|
+
// src/setup.ts
|
|
56
|
+
/**
|
|
57
|
+
* Write `.env` from `.env.example`, replacing the `PAYLOAD_SECRET` placeholder with a random secret.
|
|
58
|
+
* @param directory - Project directory.
|
|
59
|
+
*/
|
|
60
|
+
async function writeEnvironmentFile(directory) {
|
|
61
|
+
const example = await readFile(path.join(directory, ".env.example"), "utf8");
|
|
62
|
+
const secret = randomBytes(32).toString("hex");
|
|
63
|
+
await writeFile(path.join(directory, ".env"), example.replace(/^PAYLOAD_SECRET=.*$/mu, () => `PAYLOAD_SECRET=${secret}`), { mode: 384 });
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Create a Git repository on `main`, which the project's Git hooks expect for the first commit.
|
|
67
|
+
* Leaves an existing repository alone, as a clone or a parent monorepo.
|
|
68
|
+
* @param directory - Project directory.
|
|
69
|
+
* @returns What happened.
|
|
70
|
+
*/
|
|
71
|
+
function initGit(directory) {
|
|
72
|
+
if (existsSync(path.join(directory, ".git"))) return "existing";
|
|
73
|
+
if (spawnSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
74
|
+
cwd: directory,
|
|
75
|
+
stdio: "ignore"
|
|
76
|
+
}).status === 0) return "parent";
|
|
77
|
+
return spawnSync("git", ["init", "--initial-branch=main"], {
|
|
78
|
+
cwd: directory,
|
|
79
|
+
stdio: "ignore"
|
|
80
|
+
}).status === 0 ? "initialized" : "failed";
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Run `pnpm install` in the project, showing its output.
|
|
84
|
+
* @param directory - Project directory.
|
|
85
|
+
* @returns Whether the install succeeded.
|
|
86
|
+
*/
|
|
87
|
+
function didInstallDependencies(directory) {
|
|
88
|
+
return spawnSync("pnpm install", {
|
|
89
|
+
cwd: directory,
|
|
90
|
+
stdio: "inherit",
|
|
91
|
+
shell: true
|
|
92
|
+
}).status === 0;
|
|
93
|
+
}
|
|
94
|
+
// src/project-name.ts
|
|
95
|
+
const packageNamePattern = /^[a-z0-9][a-z0-9._-]*$/;
|
|
96
|
+
const maxPackageNameLength = 214;
|
|
97
|
+
const reservedNames = /* @__PURE__ */ new Set(["favicon.ico", "node_modules"]);
|
|
98
|
+
const windowsReservedPattern = /^(?:con|prn|aux|nul|com\d|lpt\d)(?:\.|$)|\.$/;
|
|
99
|
+
/**
|
|
100
|
+
* Name the package after the project directory, so `.` uses the current directory's name.
|
|
101
|
+
* @param name - Project name from the command line or prompt.
|
|
102
|
+
* @param cwd - Directory the project is created in.
|
|
103
|
+
* @returns Package name for `package.json`.
|
|
104
|
+
*/
|
|
105
|
+
function packageName(name, cwd) {
|
|
106
|
+
return name === "." ? path.basename(cwd) : name;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Validate a project name as an npm package name and a free target directory.
|
|
110
|
+
* @param name - Project name, used as the directory and package name, or `.` for the current directory.
|
|
111
|
+
* @param cwd - Directory the project is created in.
|
|
112
|
+
* @returns An error message, or `undefined` when the name is usable.
|
|
113
|
+
*/
|
|
114
|
+
function validateProjectName(name, cwd) {
|
|
115
|
+
const manifestName = packageName(name, cwd);
|
|
116
|
+
if (!packageNamePattern.test(manifestName) || manifestName.length > maxPackageNameLength) return `${name === "." ? `The current directory name "${manifestName}" is not a valid package name. ` : ""}Use lowercase letters, digits, '.', '_' or '-', starting with a letter or digit.`;
|
|
117
|
+
if (reservedNames.has(manifestName)) return `"${manifestName}" is reserved by npm. Choose another name.`;
|
|
118
|
+
if (windowsReservedPattern.test(manifestName)) return `Windows does not allow "${manifestName}" as a directory name. Choose another name.`;
|
|
119
|
+
const target = path.resolve(cwd, name);
|
|
120
|
+
const stats = lstatSync(target, { throwIfNoEntry: false });
|
|
121
|
+
if (stats === void 0) return;
|
|
122
|
+
if (!stats.isDirectory()) return `${target} already exists and is not a directory.`;
|
|
123
|
+
return ownEntries(target).length > 0 ? `${target} already exists and is not empty.` : void 0;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* List the entries a project directory may already hold. A fresh clone of an empty repository holds `.git`.
|
|
127
|
+
* @param directory - Project directory.
|
|
128
|
+
* @returns Entries other than `.git`.
|
|
129
|
+
*/
|
|
130
|
+
function ownEntries(directory) {
|
|
131
|
+
return readdirSync(directory).filter((entry) => entry !== ".git");
|
|
132
|
+
}
|
|
133
|
+
// src/arguments.ts
|
|
134
|
+
const usage = `Usage: pnpm create @next-friday/app [name] [--stack nodejs|nestjs|nextjs|payload]
|
|
135
|
+
|
|
136
|
+
name Project directory and package name, or . for the current directory. Asked when left out.
|
|
137
|
+
--stack Template stack. Asked when left out.
|
|
138
|
+
--skip-git Do not run git init.
|
|
139
|
+
--skip-install Do not run pnpm install.
|
|
140
|
+
-v, --version Show the version.
|
|
141
|
+
-h, --help Show this help.`;
|
|
142
|
+
/**
|
|
143
|
+
* Parse the command-line arguments. Options left out are asked interactively.
|
|
144
|
+
* @param cliArguments - Arguments after the executable and script path.
|
|
145
|
+
* @returns The project name, stack, and flags.
|
|
146
|
+
*/
|
|
147
|
+
function parseCliArguments(cliArguments) {
|
|
148
|
+
const { positionals, values } = parseArgs({
|
|
149
|
+
args: cliArguments,
|
|
150
|
+
allowPositionals: true,
|
|
151
|
+
options: {
|
|
152
|
+
stack: { type: "string" },
|
|
153
|
+
"skip-git": {
|
|
154
|
+
type: "boolean",
|
|
155
|
+
default: false
|
|
156
|
+
},
|
|
157
|
+
"skip-install": {
|
|
158
|
+
type: "boolean",
|
|
159
|
+
default: false
|
|
160
|
+
},
|
|
161
|
+
version: {
|
|
162
|
+
type: "boolean",
|
|
163
|
+
short: "v",
|
|
164
|
+
default: false
|
|
165
|
+
},
|
|
166
|
+
help: {
|
|
167
|
+
type: "boolean",
|
|
168
|
+
short: "h",
|
|
169
|
+
default: false
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
if (positionals.length > 1) throw new Error(`Pass one project name, not ${positionals.length}: ${positionals.join(" ")}.`);
|
|
174
|
+
const [name] = positionals;
|
|
175
|
+
const { help, stack, version } = values;
|
|
176
|
+
if (stack !== void 0 && !isStackId(stack)) {
|
|
177
|
+
const ids = stacks.map((supported) => supported.id).join(", ");
|
|
178
|
+
throw new Error(`Unknown stack "${stack}". Use one of: ${ids}.`);
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
help,
|
|
182
|
+
name,
|
|
183
|
+
stack,
|
|
184
|
+
version,
|
|
185
|
+
skipGit: values["skip-git"],
|
|
186
|
+
skipInstall: values["skip-install"]
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// src/rename.ts
|
|
190
|
+
/**
|
|
191
|
+
* Replace the template name with the project name in every text file.
|
|
192
|
+
* @param directory - Project directory.
|
|
193
|
+
* @param from - Template name to replace.
|
|
194
|
+
* @param to - Project name.
|
|
195
|
+
* @returns Paths of the files that changed.
|
|
196
|
+
*/
|
|
197
|
+
async function renameProject(directory, from, to) {
|
|
198
|
+
const files = (await readdir(directory, {
|
|
199
|
+
recursive: true,
|
|
200
|
+
withFileTypes: true
|
|
201
|
+
})).filter((entry) => entry.isFile()).map((entry) => path.join(entry.parentPath, entry.name));
|
|
202
|
+
return (await Promise.all(files.map(async (file) => {
|
|
203
|
+
const content = await readFile(file);
|
|
204
|
+
const text = content.toString("utf8");
|
|
205
|
+
if (content.includes(0) || !content.includes(from) || !Buffer.from(text, "utf8").equals(content)) return [];
|
|
206
|
+
await writeFile(file, text.replaceAll(from, () => to));
|
|
207
|
+
return [file];
|
|
208
|
+
}))).flat();
|
|
209
|
+
}
|
|
210
|
+
// src/create.ts
|
|
211
|
+
const templatesDirectory = fileURLToPath(new URL("../templates", import.meta.url));
|
|
212
|
+
var CancelledError = class extends Error {
|
|
213
|
+
message = "Cancelled.";
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Copy the chosen template into a new project directory.
|
|
217
|
+
* @param cliArguments - Arguments after the executable and script path.
|
|
218
|
+
*/
|
|
219
|
+
async function create(cliArguments) {
|
|
220
|
+
const cwd = process.cwd();
|
|
221
|
+
try {
|
|
222
|
+
const options = parseCliArguments(cliArguments);
|
|
223
|
+
if (options.help) {
|
|
224
|
+
process.stdout.write(`${usage}\n`);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (options.version) {
|
|
228
|
+
process.stdout.write(`${packageVersion()}\n`);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
intro("Create a Next Friday project");
|
|
232
|
+
const { name, stack } = await resolveProject(options, cwd);
|
|
233
|
+
const manifestName = packageName(name, cwd);
|
|
234
|
+
const directory = path.resolve(cwd, name);
|
|
235
|
+
log.step(`Copying the ${stack} template.`);
|
|
236
|
+
await writeProject(stack, directory, manifestName);
|
|
237
|
+
log.success(`Created ${manifestName} from the ${stack} template.`);
|
|
238
|
+
const { git, isInstalled } = setUpProject(directory, options);
|
|
239
|
+
outro(nextSteps(name, stack, git, isInstalled));
|
|
240
|
+
} catch (error) {
|
|
241
|
+
cancel(error instanceof Error ? error.message : String(error));
|
|
242
|
+
process.exitCode = error instanceof CancelledError ? 130 : 1;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Copy, rename, and write `.env`, removing what was written when a step fails or Ctrl+C arrives.
|
|
247
|
+
* Ctrl+C waits for the running step, so the removal does not race the copy.
|
|
248
|
+
* @param stack - Supported stack id.
|
|
249
|
+
* @param directory - Project directory.
|
|
250
|
+
* @param manifestName - Package name for `package.json`.
|
|
251
|
+
*/
|
|
252
|
+
async function writeProject(stack, directory, manifestName) {
|
|
253
|
+
const kept = existsSync(directory) ? readdirSync(directory) : void 0;
|
|
254
|
+
let isInterrupted = false;
|
|
255
|
+
const interrupt = () => {
|
|
256
|
+
isInterrupted = true;
|
|
257
|
+
};
|
|
258
|
+
process.on("SIGINT", interrupt);
|
|
259
|
+
try {
|
|
260
|
+
await copyTemplate(stack, directory);
|
|
261
|
+
await renameProject(directory, templateName(stack), manifestName);
|
|
262
|
+
await writeEnvironmentFile(directory);
|
|
263
|
+
if (isInterrupted) throw new CancelledError();
|
|
264
|
+
} catch (error) {
|
|
265
|
+
removeProject(directory, kept);
|
|
266
|
+
throw isInterrupted ? new CancelledError() : error;
|
|
267
|
+
} finally {
|
|
268
|
+
process.off("SIGINT", interrupt);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Take the project name and stack from the options, asking for any left out.
|
|
273
|
+
* @param options - Parsed command-line options.
|
|
274
|
+
* @param cwd - Directory the project is created in.
|
|
275
|
+
* @returns A valid project name and the stack.
|
|
276
|
+
*/
|
|
277
|
+
async function resolveProject(options, cwd) {
|
|
278
|
+
if (!process.stdin.isTTY && (options.name === void 0 || options.stack === void 0)) throw new Error(`Pass the project name and --stack when not running in a terminal.\n${usage}`);
|
|
279
|
+
const name = options.name ?? await askName(cwd);
|
|
280
|
+
const nameError = validateProjectName(name, cwd);
|
|
281
|
+
if (nameError !== void 0) throw new Error(nameError);
|
|
282
|
+
return {
|
|
283
|
+
name,
|
|
284
|
+
stack: options.stack ?? await askStack()
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const gitWarnings = {
|
|
288
|
+
failed: "git init failed. Install Git 2.28 or later, then run the steps below.",
|
|
289
|
+
parent: "Skipped git init inside an existing Git repository. The Git hooks install only at a repository root."
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* Run `git init` and then `pnpm install`, so the install sets up the Git hooks.
|
|
293
|
+
* @param directory - Project directory.
|
|
294
|
+
* @param options - Parsed command-line options.
|
|
295
|
+
* @returns The `git init` result, or `undefined` when skipped, and whether the install succeeded.
|
|
296
|
+
*/
|
|
297
|
+
function setUpProject(directory, options) {
|
|
298
|
+
const git = options.skipGit ? void 0 : initGit(directory);
|
|
299
|
+
const warning = git === void 0 ? void 0 : gitWarnings[git];
|
|
300
|
+
if (warning !== void 0) log.warn(warning);
|
|
301
|
+
if (options.skipInstall) return {
|
|
302
|
+
git,
|
|
303
|
+
isInstalled: false
|
|
304
|
+
};
|
|
305
|
+
log.step("Installing dependencies with pnpm.");
|
|
306
|
+
const isInstalled = didInstallDependencies(directory);
|
|
307
|
+
if (!isInstalled) {
|
|
308
|
+
log.error("pnpm install failed. Fix the error above, then run the steps below. The project needs pnpm and the Node.js version in .nvmrc.");
|
|
309
|
+
process.exitCode = 1;
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
git,
|
|
313
|
+
isInstalled
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Copy the shared template files, then the stack's files, into the project directory.
|
|
318
|
+
* npm leaves `.gitignore` out of published packages, so the template keeps it as `_gitignore`.
|
|
319
|
+
* @param stack - Supported stack id.
|
|
320
|
+
* @param directory - Project directory.
|
|
321
|
+
*/
|
|
322
|
+
async function copyTemplate(stack, directory) {
|
|
323
|
+
await cp(path.join(templatesDirectory, "base"), directory, { recursive: true });
|
|
324
|
+
await cp(path.join(templatesDirectory, stack), directory, { recursive: true });
|
|
325
|
+
await rename(path.join(directory, "_gitignore"), path.join(directory, ".gitignore"));
|
|
326
|
+
await markScriptsExecutable(directory);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Make every file that starts with a shebang executable. `pnpm pack` drops the executable bit,
|
|
330
|
+
* and the Git and agent hooks run these scripts directly.
|
|
331
|
+
* @param directory - Project directory.
|
|
332
|
+
*/
|
|
333
|
+
async function markScriptsExecutable(directory) {
|
|
334
|
+
const entries = await readdir(directory, {
|
|
335
|
+
recursive: true,
|
|
336
|
+
withFileTypes: true
|
|
337
|
+
});
|
|
338
|
+
await Promise.all(entries.filter((entry) => entry.isFile()).map(async (entry) => {
|
|
339
|
+
const file = path.join(entry.parentPath, entry.name);
|
|
340
|
+
if ((await readFile(file)).subarray(0, 2).toString() === "#!") await chmod(file, 493);
|
|
341
|
+
}));
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Remove what this run wrote, leaving the target as it was before.
|
|
345
|
+
* @param directory - Project directory.
|
|
346
|
+
* @param kept - Entries the directory held before the run, or `undefined` when the run created it.
|
|
347
|
+
*/
|
|
348
|
+
function removeProject(directory, kept) {
|
|
349
|
+
const targets = kept === void 0 ? [directory] : readdirSync(directory).filter((entry) => !kept.includes(entry)).map((entry) => path.join(directory, entry));
|
|
350
|
+
for (const target of targets) rmSync(target, {
|
|
351
|
+
force: true,
|
|
352
|
+
recursive: true
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* List the commands left to run, skipping what the CLI already did.
|
|
357
|
+
* @param name - Project name, or `.` for the current directory.
|
|
358
|
+
* @param stack - Supported stack id.
|
|
359
|
+
* @param git - Result of `git init`, or `undefined` when skipped.
|
|
360
|
+
* @param isInstalled - Whether `pnpm install` succeeded.
|
|
361
|
+
* @returns The next steps message.
|
|
362
|
+
*/
|
|
363
|
+
function nextSteps(name, stack, git, isInstalled) {
|
|
364
|
+
const hasGit = git !== void 0 && git !== "failed";
|
|
365
|
+
const commands = [
|
|
366
|
+
name === "." ? [] : [`cd ${name}`],
|
|
367
|
+
hasGit ? [] : ["git init --initial-branch=main"],
|
|
368
|
+
hasGit && isInstalled ? [] : ["pnpm install"],
|
|
369
|
+
[`pnpm ${developmentScripts[stack]}`]
|
|
370
|
+
].flat();
|
|
371
|
+
const database = stack === "payload" ? `Set DATABASE_URL in .env to your PostgreSQL database before pnpm ${developmentScripts[stack]}.\n` : "";
|
|
372
|
+
const readme = name === "." ? "README.md" : `${name}/README.md`;
|
|
373
|
+
return `Next steps:\n${commands.map((command) => ` ${command}`).join("\n")}\n${database}Before the first commit, follow "After creating a project from this template" in ${readme}.`;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Read this package's version.
|
|
377
|
+
* @returns The version in `package.json`.
|
|
378
|
+
*/
|
|
379
|
+
function packageVersion() {
|
|
380
|
+
return JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Ask for the project name.
|
|
384
|
+
* @param cwd - Directory the project is created in.
|
|
385
|
+
* @returns A valid project name.
|
|
386
|
+
*/
|
|
387
|
+
async function askName(cwd) {
|
|
388
|
+
const name = await text({
|
|
389
|
+
message: "Project name",
|
|
390
|
+
placeholder: "my-app",
|
|
391
|
+
validate: (value = "") => validateProjectName(value, cwd)
|
|
392
|
+
});
|
|
393
|
+
if (isCancel(name)) throw new CancelledError();
|
|
394
|
+
return name;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Ask which stack to start from.
|
|
398
|
+
* @returns The chosen stack id.
|
|
399
|
+
*/
|
|
400
|
+
async function askStack() {
|
|
401
|
+
const stack = await select({
|
|
402
|
+
message: "Stack",
|
|
403
|
+
options: stacks.map((option) => ({
|
|
404
|
+
value: option.id,
|
|
405
|
+
label: option.label,
|
|
406
|
+
hint: option.hint
|
|
407
|
+
}))
|
|
408
|
+
});
|
|
409
|
+
if (isCancel(stack)) throw new CancelledError();
|
|
410
|
+
return stack;
|
|
411
|
+
}
|
|
412
|
+
// src/main.ts
|
|
413
|
+
create(process.argv.slice(2));
|
|
414
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,87 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@next-friday/create-app",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create a Next Friday project from a template.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"create",
|
|
7
|
+
"scaffold",
|
|
8
|
+
"template",
|
|
9
|
+
"next-friday"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/next-friday/create-friday-app#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/next-friday/create-friday-app/issues"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/next-friday/create-friday-app.git"
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": "Next Friday",
|
|
21
|
+
"sideEffects": true,
|
|
22
|
+
"type": "module",
|
|
23
|
+
"exports": {
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
26
|
+
"bin": {
|
|
27
|
+
"create-friday-app": "./dist/main.mjs"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"templates"
|
|
32
|
+
],
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@clack/prompts": "1.8.1"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@changesets/cli": "3.0.3",
|
|
38
|
+
"@commitlint/cli": "21.2.4",
|
|
39
|
+
"@next-friday/commitlint-config-friday": "4.0.0",
|
|
40
|
+
"@next-friday/eslint-config-friday": "5.0.0",
|
|
41
|
+
"@next-friday/prettier-config-friday": "4.0.0",
|
|
42
|
+
"@types/node": "26.6.5",
|
|
43
|
+
"@vitest/coverage-v8": "5.0.5",
|
|
44
|
+
"editorconfig-checker": "7.0.0",
|
|
45
|
+
"eslint": "10.13.0",
|
|
46
|
+
"fast-check": "4.10.2",
|
|
47
|
+
"husky": "9.1.7",
|
|
48
|
+
"jiti": "2.7.0",
|
|
49
|
+
"knip": "6.43.0",
|
|
50
|
+
"lint-staged": "17.6.0",
|
|
51
|
+
"pkg-pr-new": "0.0.91",
|
|
52
|
+
"prettier": "3.9.10",
|
|
53
|
+
"publint": "0.3.25",
|
|
54
|
+
"sort-package-json": "4.1.0",
|
|
55
|
+
"tsdown": "0.23.1",
|
|
56
|
+
"typescript": "6.0.3",
|
|
57
|
+
"vitest": "5.0.5"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=24.15.0"
|
|
61
|
+
},
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public",
|
|
64
|
+
"provenance": true
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"audit": "pnpm audit",
|
|
68
|
+
"build": "tsdown",
|
|
69
|
+
"changeset": "changeset",
|
|
70
|
+
"changeset:status": "changeset status --since main",
|
|
71
|
+
"check:publint": "publint",
|
|
72
|
+
"clean": "rm -rf dist coverage *.tsbuildinfo .eslintcache node_modules/.cache",
|
|
73
|
+
"dev": "tsdown --watch",
|
|
74
|
+
"format": "prettier --write --cache .",
|
|
75
|
+
"format:check": "prettier --check --cache . && editorconfig-checker",
|
|
76
|
+
"knip": "knip",
|
|
77
|
+
"lint": "eslint . --fix --cache --max-warnings=0 --no-inline-config",
|
|
78
|
+
"lint:check": "eslint . --cache --max-warnings=0 --no-inline-config",
|
|
79
|
+
"sort-package-json": "sort-package-json package.json",
|
|
80
|
+
"sort-package-json:check": "sort-package-json --check package.json",
|
|
81
|
+
"test": "vitest run",
|
|
82
|
+
"test:coverage": "vitest run --coverage",
|
|
83
|
+
"test:watch": "vitest",
|
|
84
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
85
|
+
"verify": "pnpm lint:check && pnpm format:check && pnpm sort-package-json:check && pnpm typecheck && pnpm test:coverage && pnpm knip && pnpm build && pnpm check:publint"
|
|
86
|
+
}
|
|
6
87
|
}
|