template-git-repo 0.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 +91 -0
- package/bin/template-git-repo.js +257 -0
- package/docs/ACTIONS.md +195 -0
- package/docs/BADGES.md +221 -0
- package/package.json +56 -0
- package/src/apply.js +164 -0
- package/src/badges.js +353 -0
- package/src/context.js +228 -0
- package/src/index.js +29 -0
- package/src/readme.js +60 -0
- package/template/.github/workflows/auto-merge-and-create-prs.yml +102 -0
- package/template/.github/workflows/auto-merge-claude.yml +54 -0
- package/template/.github/workflows/deploy-test-reports.yml +98 -0
- package/template/.github/workflows/npm-publish.yml +320 -0
- package/template/.github/workflows/tests.yml +100 -0
- package/template/codecov.yml +46 -0
- package/template/scripts/list-test-packages.mjs +137 -0
- package/template/scripts/next-free-version.mjs +169 -0
- package/template/scripts/pin-workspace-deps.mjs +108 -0
- package/template/scripts/restore-pinned-deps.mjs +101 -0
- package/template/scripts/workspace-build-order.mjs +129 -0
- package/template/turbo.json +36 -0
package/docs/BADGES.md
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
<!-- Generated by scripts/generate-badge-docs.mjs from src/badges.js — edit the catalog, not this file. -->
|
|
2
|
+
|
|
3
|
+
# Badge setup
|
|
4
|
+
|
|
5
|
+
Every badge in the template, what it actually measures, and what you have to do
|
|
6
|
+
outside this repo before it shows anything real.
|
|
7
|
+
|
|
8
|
+
The CLI leaves out any badge whose inputs it does not have, and prints which ones
|
|
9
|
+
it left out and why. Re-run it with the flag from the table below to add one:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bunx template-git-repo --badges-only --discord-id 1110227955554209923 --discord-invite https://discord.gg/xxxx
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The block lives between `<!-- template-git-repo:badges:start -->` and
|
|
16
|
+
`<!-- template-git-repo:badges:end -->` in your README. Re-running replaces what is
|
|
17
|
+
between the markers and nothing else, so it is safe to run again after you publish a
|
|
18
|
+
package or finish setting up Codecov.
|
|
19
|
+
|
|
20
|
+
## Index
|
|
21
|
+
|
|
22
|
+
| Badge | Needs | Flag |
|
|
23
|
+
| --- | --- | --- |
|
|
24
|
+
| [Zenodo DOI](#doi) `doi` | doi | `--doi <10.5281/zenodo.NNNNNNN>` |
|
|
25
|
+
| [Ask DeepWiki](#deepwiki) `deepwiki` | — | on by default |
|
|
26
|
+
| [Documentation](#docs) `docs` | docsUrl | `--docs <url>` |
|
|
27
|
+
| [API reference](#api) `api` | apiUrl | `--api <url>` |
|
|
28
|
+
| [YouTube demo](#youtube) `youtube` | youtubeUrl | `--youtube <url>` |
|
|
29
|
+
| [Deploy to Cloudflare Workers](#deploy-cloudflare) `deploy-cloudflare` | cloudflareDeploy | `--cloudflare-deploy` |
|
|
30
|
+
| [GitHub stars](#stars) `stars` | — | on by default |
|
|
31
|
+
| [npm monthly downloads](#npm-downloads) `npm-downloads` | npmPackage | `--npm-package <name>` |
|
|
32
|
+
| [npm version](#npm-version) `npm-version` | npmPackage | `--npm-package <name>` |
|
|
33
|
+
| [Code coverage](#codecov) `codecov` | — | on by default |
|
|
34
|
+
| [CI status](#workflow) `workflow` | workflowFile | `--workflow <file>` |
|
|
35
|
+
| [Hosted test report](#test-report) `test-report` | testReportUrl | `--test-report <url>` |
|
|
36
|
+
| [Uptime](#uptime) `uptime` | uptimeUrl | `--uptime <url>` |
|
|
37
|
+
| [Commit activity](#commit-activity) `commit-activity` | — | on by default |
|
|
38
|
+
| [Last commit](#last-commit) `last-commit` | — | on by default |
|
|
39
|
+
| [Discord](#discord) `discord` | discordId, discordInvite | `--discord-id <id>` `--discord-invite <url>` |
|
|
40
|
+
| [PRs welcome](#prs-welcome) `prs-welcome` | — | on by default |
|
|
41
|
+
| [License](#license) `license` | — | on by default |
|
|
42
|
+
| [Tech stack chips](#stack) `stack` | stack | `--stack <A,B,C>` |
|
|
43
|
+
|
|
44
|
+
## Identity — what this project is
|
|
45
|
+
|
|
46
|
+
### doi
|
|
47
|
+
|
|
48
|
+
**Zenodo DOI**
|
|
49
|
+
|
|
50
|
+
Sign in to zenodo.org with GitHub, flip this repository on under Account → GitHub, then publish a GitHub Release. Zenodo archives the release and mints a DOI. Use the *concept* DOI (the one that always resolves to the newest version), not the per-release DOI.
|
|
51
|
+
|
|
52
|
+
Enable with: `--doi <10.5281/zenodo.NNNNNNN>`
|
|
53
|
+
|
|
54
|
+
### deepwiki
|
|
55
|
+
|
|
56
|
+
**Ask DeepWiki**
|
|
57
|
+
|
|
58
|
+
Nothing to configure for a public repo — visit deepwiki.com/<owner>/<repo> once to trigger the first index. Private repos need the DeepWiki GitHub App installed.
|
|
59
|
+
|
|
60
|
+
Included by default — no configuration needed.
|
|
61
|
+
|
|
62
|
+
### docs
|
|
63
|
+
|
|
64
|
+
**Documentation**
|
|
65
|
+
|
|
66
|
+
Point this at wherever your docs are actually hosted. This is a static shields.io badge — it says "Docs" whether or not the link works, so it is on you to keep the URL alive.
|
|
67
|
+
|
|
68
|
+
Enable with: `--docs <url>`
|
|
69
|
+
|
|
70
|
+
### api
|
|
71
|
+
|
|
72
|
+
**API reference**
|
|
73
|
+
|
|
74
|
+
Point this at your OpenAPI / Swagger page. Static badge, same caveat as Docs.
|
|
75
|
+
|
|
76
|
+
Enable with: `--api <url>`
|
|
77
|
+
|
|
78
|
+
### youtube
|
|
79
|
+
|
|
80
|
+
**YouTube demo**
|
|
81
|
+
|
|
82
|
+
A demo video does more for a README than three paragraphs. Any YouTube URL works.
|
|
83
|
+
|
|
84
|
+
Enable with: `--youtube <url>`
|
|
85
|
+
|
|
86
|
+
### deploy-cloudflare
|
|
87
|
+
|
|
88
|
+
**Deploy to Cloudflare Workers**
|
|
89
|
+
|
|
90
|
+
The repo must contain a wrangler.toml (or wrangler.jsonc) at the path the button clones. Cloudflare forks the repo into the visitor's account and runs the build, so anything the build needs must come from `[vars]` or be prompted for — a build that requires a secret fails for every visitor.
|
|
91
|
+
|
|
92
|
+
Enable with: `--cloudflare-deploy`
|
|
93
|
+
|
|
94
|
+
## Quality — is it working, is it tested, is it shipped
|
|
95
|
+
|
|
96
|
+
### npm-downloads
|
|
97
|
+
|
|
98
|
+
**npm monthly downloads**
|
|
99
|
+
|
|
100
|
+
Requires at least one published version. In a monorepo pick the package you want to advertise — the badge counts one package, not the workspace.
|
|
101
|
+
|
|
102
|
+
Enable with: `--npm-package <name>`
|
|
103
|
+
|
|
104
|
+
### npm-version
|
|
105
|
+
|
|
106
|
+
**npm version**
|
|
107
|
+
|
|
108
|
+
Shows the `latest` dist-tag. Publishing under a different tag will not move it.
|
|
109
|
+
|
|
110
|
+
Enable with: `--npm-package <name>`
|
|
111
|
+
|
|
112
|
+
### codecov
|
|
113
|
+
|
|
114
|
+
**Code coverage**
|
|
115
|
+
|
|
116
|
+
Add the repo at codecov.io, copy its upload token into a CODECOV_TOKEN repository secret, and make sure a workflow uploads `coverage/lcov.info` (tests.yml does). Until the first successful upload the badge reads "unknown", which looks identical to a broken badge — check the Codecov dashboard, not the badge, when debugging.
|
|
117
|
+
|
|
118
|
+
Included by default — no configuration needed.
|
|
119
|
+
|
|
120
|
+
### workflow
|
|
121
|
+
|
|
122
|
+
**CI status**
|
|
123
|
+
|
|
124
|
+
The filename must match the workflow file exactly, and `?branch=` must name your default branch — without it the badge shows the most recent run on *any* branch, so a failing feature branch reads as a broken main.
|
|
125
|
+
|
|
126
|
+
Enable with: `--workflow <file>`
|
|
127
|
+
|
|
128
|
+
### test-report
|
|
129
|
+
|
|
130
|
+
**Hosted test report**
|
|
131
|
+
|
|
132
|
+
Backed by `.github/workflows/deploy-test-reports.yml`, which publishes the Vitest HTML reporter output to Cloudflare Workers on every push to the default branch. Needs CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID secrets.
|
|
133
|
+
|
|
134
|
+
Enable with: `--test-report <url>`
|
|
135
|
+
|
|
136
|
+
### uptime
|
|
137
|
+
|
|
138
|
+
**Uptime**
|
|
139
|
+
|
|
140
|
+
Create a monitor at uptimerobot.com, then a public status page, and link the status page here. This is a static badge — it says "brightgreen" even while you are down. For a live one use the UptimeRobot shields endpoint with a read-only API key.
|
|
141
|
+
|
|
142
|
+
Enable with: `--uptime <url>`
|
|
143
|
+
|
|
144
|
+
## Community — is it alive, can I join
|
|
145
|
+
|
|
146
|
+
### stars
|
|
147
|
+
|
|
148
|
+
**GitHub stars**
|
|
149
|
+
|
|
150
|
+
Nothing to configure. Public repos only — shields.io cannot read a private repo.
|
|
151
|
+
|
|
152
|
+
Included by default — no configuration needed.
|
|
153
|
+
|
|
154
|
+
### commit-activity
|
|
155
|
+
|
|
156
|
+
**Commit activity**
|
|
157
|
+
|
|
158
|
+
Nothing to configure. Commits per month — a quiet month reads as an abandoned project, which is worth knowing before you add it.
|
|
159
|
+
|
|
160
|
+
Included by default — no configuration needed.
|
|
161
|
+
|
|
162
|
+
### last-commit
|
|
163
|
+
|
|
164
|
+
**Last commit**
|
|
165
|
+
|
|
166
|
+
Nothing to configure. Same caveat as commit activity.
|
|
167
|
+
|
|
168
|
+
Included by default — no configuration needed.
|
|
169
|
+
|
|
170
|
+
### discord
|
|
171
|
+
|
|
172
|
+
**Discord**
|
|
173
|
+
|
|
174
|
+
Two different values: the numeric server id drives the online-member count (Server Settings → Widget → Enable Server Widget, then copy the Server ID), and the invite link is where the badge points. Without the widget enabled the badge reads "invite" instead of a count.
|
|
175
|
+
|
|
176
|
+
Enable with: `--discord-id <id>` `--discord-invite <url>`
|
|
177
|
+
|
|
178
|
+
### prs-welcome
|
|
179
|
+
|
|
180
|
+
**PRs welcome**
|
|
181
|
+
|
|
182
|
+
Static. Worth backing with a CONTRIBUTING.md so the badge is not the only thing that says it.
|
|
183
|
+
|
|
184
|
+
Included by default — no configuration needed.
|
|
185
|
+
|
|
186
|
+
### license
|
|
187
|
+
|
|
188
|
+
**License**
|
|
189
|
+
|
|
190
|
+
Reads the license GitHub detected, which comes from a recognized LICENSE file at the repo root. A custom or modified license shows as "unknown" no matter what the file says.
|
|
191
|
+
|
|
192
|
+
Included by default — no configuration needed.
|
|
193
|
+
|
|
194
|
+
## Stack — what it is built with
|
|
195
|
+
|
|
196
|
+
### stack
|
|
197
|
+
|
|
198
|
+
**Tech stack chips**
|
|
199
|
+
|
|
200
|
+
Plain static shields with a simple-icons logo — `?logo=<slug>` accepts any slug from simpleicons.org. Purely decorative: they say what the project is built with at a glance. Pass a comma-separated list (e.g. `--stack Claude,Cloudflare,Next.js`).
|
|
201
|
+
|
|
202
|
+
Enable with: `--stack <A,B,C>`
|
|
203
|
+
|
|
204
|
+
## Removing one
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
bunx template-git-repo --badges-only --exclude commit-activity,last-commit
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Or edit the block between the markers directly — the CLI only rewrites it when you
|
|
211
|
+
run it again.
|
|
212
|
+
|
|
213
|
+
## Why some badges stay grey
|
|
214
|
+
|
|
215
|
+
| Badge reads | Almost always means |
|
|
216
|
+
| --- | --- |
|
|
217
|
+
| Codecov: `unknown` | No upload has landed yet. Check the Codecov dashboard, not the badge — a missing `CODECOV_TOKEN` and a repo that was never added look identical from here. |
|
|
218
|
+
| CI: `no status` | The workflow filename in the badge URL does not match a file in `.github/workflows/`, or it has never run on the branch in `?branch=`. |
|
|
219
|
+
| npm: `invalid` | The package name is not on the registry, or it is scoped and the scope is missing from the URL. |
|
|
220
|
+
| Discord: `invite` instead of a count | The server widget is off. Server Settings → Widget → Enable Server Widget. |
|
|
221
|
+
| Stars/last-commit: `repo not found` | shields.io cannot read private repos. |
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "template-git-repo",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "One command to set up a repo with GitHub Actions, README badges, Turborepo and Codecov — the CI setup from qwksearch-research-agent, as a template",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"template-git-repo": "bin/template-git-repo.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "src/index.js",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./src/index.js",
|
|
12
|
+
"./badges": "./src/badges.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"docs:badges": "node scripts/generate-badge-docs.mjs",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:ci": "vitest run --coverage",
|
|
18
|
+
"test:coverage": "vitest run --coverage",
|
|
19
|
+
"coverage": "vitest run --coverage",
|
|
20
|
+
"test:watch": "vitest"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"bin",
|
|
24
|
+
"src",
|
|
25
|
+
"template",
|
|
26
|
+
"docs"
|
|
27
|
+
],
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"github-actions",
|
|
33
|
+
"template",
|
|
34
|
+
"badges",
|
|
35
|
+
"shields",
|
|
36
|
+
"turborepo",
|
|
37
|
+
"monorepo",
|
|
38
|
+
"codecov",
|
|
39
|
+
"npm-publish",
|
|
40
|
+
"ci",
|
|
41
|
+
"scaffold"
|
|
42
|
+
],
|
|
43
|
+
"author": "vtempest",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/OpenSourceAGI/dev-tools-starter-agent.git",
|
|
48
|
+
"directory": "packages/template-git-repo"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://github.com/OpenSourceAGI/dev-tools-starter-agent/tree/master/packages/template-git-repo",
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
53
|
+
"vitest": "^4.1.0",
|
|
54
|
+
"yaml": "^2.9.0"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/apply.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn the template directory into a list of file operations, then (separately)
|
|
3
|
+
* carry them out.
|
|
4
|
+
*
|
|
5
|
+
* The split is what makes `--dry-run` honest: the same plan is printed and
|
|
6
|
+
* applied, so what the dry run shows is exactly what a real run does, rather
|
|
7
|
+
* than a second implementation that can drift from the first.
|
|
8
|
+
*/
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { substitute } from './context.js';
|
|
13
|
+
|
|
14
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
|
|
16
|
+
/** The shipped template tree. */
|
|
17
|
+
export const TEMPLATE_DIR = path.join(HERE, '..', 'template');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Every file under `dir`, relative to it, depth-first and sorted.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} dir
|
|
23
|
+
* @param {string} [prefix]
|
|
24
|
+
* @returns {string[]}
|
|
25
|
+
*/
|
|
26
|
+
export function walk(dir, prefix = '') {
|
|
27
|
+
if (!fs.existsSync(dir)) return [];
|
|
28
|
+
|
|
29
|
+
const files = [];
|
|
30
|
+
|
|
31
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
32
|
+
const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
33
|
+
if (entry.isDirectory()) {
|
|
34
|
+
files.push(...walk(path.join(dir, entry.name), relative));
|
|
35
|
+
} else {
|
|
36
|
+
files.push(relative);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return files;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Decide what happens to each template file.
|
|
45
|
+
*
|
|
46
|
+
* Existing files are skipped rather than overwritten unless `force` is set.
|
|
47
|
+
* A repo that already has a `turbo.json` has it for a reason, and clobbering a
|
|
48
|
+
* tuned workflow with a generic one is the kind of "help" that costs an
|
|
49
|
+
* afternoon.
|
|
50
|
+
*
|
|
51
|
+
* @param {object} options
|
|
52
|
+
* @param {Record<string, any>} options.context
|
|
53
|
+
* @param {string} [options.templateDir]
|
|
54
|
+
* @param {boolean} [options.force]
|
|
55
|
+
* @param {string[]} [options.include] relative template paths to consider; all by default
|
|
56
|
+
* @returns {{ path: string, absolute: string, content: string, action: 'create' | 'overwrite' | 'skip' }[]}
|
|
57
|
+
*/
|
|
58
|
+
export function planFiles({ context, templateDir = TEMPLATE_DIR, force = false, include }) {
|
|
59
|
+
const plan = [];
|
|
60
|
+
|
|
61
|
+
for (const relative of walk(templateDir)) {
|
|
62
|
+
if (include && !include.some((prefix) => relative === prefix || relative.startsWith(`${prefix}/`))) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const absolute = path.join(context.root, relative);
|
|
67
|
+
const content = substitute(fs.readFileSync(path.join(templateDir, relative), 'utf8'), context);
|
|
68
|
+
|
|
69
|
+
let action = 'create';
|
|
70
|
+
if (fs.existsSync(absolute)) {
|
|
71
|
+
const existing = fs.readFileSync(absolute, 'utf8');
|
|
72
|
+
// An identical file is a skip whatever `force` says — reporting it as an
|
|
73
|
+
// overwrite would make a no-op run look like it changed something.
|
|
74
|
+
action = existing === content ? 'skip' : force ? 'overwrite' : 'skip';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
plan.push({ path: relative, absolute, content, action });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return plan;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param {ReturnType<typeof planFiles>} plan
|
|
85
|
+
* @param {{ dryRun?: boolean }} [options]
|
|
86
|
+
* @returns {{ written: string[], skipped: string[] }}
|
|
87
|
+
*/
|
|
88
|
+
export function applyPlan(plan, options = {}) {
|
|
89
|
+
const { dryRun = false } = options;
|
|
90
|
+
|
|
91
|
+
const written = [];
|
|
92
|
+
const skipped = [];
|
|
93
|
+
|
|
94
|
+
for (const entry of plan) {
|
|
95
|
+
if (entry.action === 'skip') {
|
|
96
|
+
skipped.push(entry.path);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!dryRun) {
|
|
101
|
+
fs.mkdirSync(path.dirname(entry.absolute), { recursive: true });
|
|
102
|
+
fs.writeFileSync(entry.absolute, entry.content);
|
|
103
|
+
// The workflows call these directly; a script that is not executable
|
|
104
|
+
// fails at the least convenient moment.
|
|
105
|
+
if (entry.path.endsWith('.mjs')) fs.chmodSync(entry.absolute, 0o755);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
written.push(entry.path);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return { written, skipped };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The turbo scripts and devDependency the template's workflows assume.
|
|
116
|
+
*
|
|
117
|
+
* Merged into the root package.json rather than written over it: an existing
|
|
118
|
+
* script keeps its definition, since a repo's own `build` almost certainly does
|
|
119
|
+
* something this template cannot know about.
|
|
120
|
+
*/
|
|
121
|
+
export const TURBO_SCRIPTS = {
|
|
122
|
+
build: 'turbo run build',
|
|
123
|
+
dev: 'turbo run dev',
|
|
124
|
+
lint: 'turbo run lint',
|
|
125
|
+
typecheck: 'turbo run typecheck',
|
|
126
|
+
test: 'turbo run test',
|
|
127
|
+
'test:coverage': 'turbo run test:coverage',
|
|
128
|
+
'test:watch': 'turbo run test:watch',
|
|
129
|
+
clean: 'turbo run clean && rm -rf .turbo node_modules',
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @param {Record<string, any>} manifest parsed root package.json
|
|
134
|
+
* @param {{ packagesDir?: string, turboVersion?: string }} [options]
|
|
135
|
+
* @returns {{ manifest: Record<string, any>, changes: string[] }}
|
|
136
|
+
*/
|
|
137
|
+
export function wireTurbo(manifest, options = {}) {
|
|
138
|
+
const { packagesDir = 'packages', turboVersion = '^2.10.12' } = options;
|
|
139
|
+
|
|
140
|
+
const next = structuredClone(manifest);
|
|
141
|
+
const changes = [];
|
|
142
|
+
|
|
143
|
+
next.scripts ??= {};
|
|
144
|
+
for (const [name, command] of Object.entries(TURBO_SCRIPTS)) {
|
|
145
|
+
if (next.scripts[name]) continue;
|
|
146
|
+
next.scripts[name] = command;
|
|
147
|
+
changes.push(`scripts.${name}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
next.devDependencies ??= {};
|
|
151
|
+
if (!next.devDependencies.turbo) {
|
|
152
|
+
next.devDependencies.turbo = turboVersion;
|
|
153
|
+
changes.push('devDependencies.turbo');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Turbo needs to know what the workspace is; without `workspaces` it treats
|
|
157
|
+
// the repo as a single package and every `--filter` silently matches nothing.
|
|
158
|
+
if (!next.workspaces) {
|
|
159
|
+
next.workspaces = [`${packagesDir}/*`, 'apps/*'];
|
|
160
|
+
changes.push('workspaces');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return { manifest: next, changes };
|
|
164
|
+
}
|