@rshono/create 1.0.0-rc.10 → 1.0.0-rc.11
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 +16 -1
- package/dist/api.mjs +216 -79
- package/dist/cli.mjs +229 -81
- package/package.json +2 -2
- package/templates/base/README.md +6 -4
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ a target added to rshono appears here with no edit.
|
|
|
51
51
|
## What you get
|
|
52
52
|
|
|
53
53
|
```
|
|
54
|
-
package.json scripts, and the exact
|
|
54
|
+
package.json scripts, including the target's own, and the exact pins rshono is tested against
|
|
55
55
|
rshono.config.ts the chosen deploy target; every other setting takes its default
|
|
56
56
|
tsconfig.json strict, with @/* → ./src/*
|
|
57
57
|
.env committed defaults; secrets go in .env.local
|
|
@@ -71,6 +71,21 @@ Scaffolding runs the install and nothing else — no formatter, no linter. The t
|
|
|
71
71
|
every formatter option at its configured width, so a fresh project passes its own `format:check` without a
|
|
72
72
|
write pass first.
|
|
73
73
|
|
|
74
|
+
Beside `dev`, `build` and `typecheck`, the deploy target contributes the scripts only it can define, under three
|
|
75
|
+
names that mean the same thing in every app. **`deploy`** builds and ships it, where the platform has one
|
|
76
|
+
command that does the shipping (`cloudflare`, `vercel`). **`preview`** builds and runs the result here — in
|
|
77
|
+
workerd for `cloudflare`, and as a Node build for `vercel` and `aws-lambda`, since neither can run its own
|
|
78
|
+
output locally. **`start`** runs a build that already exists and never makes one, which is what a host's start
|
|
79
|
+
command needs — so only `node` has one, and needs no `preview`: `build` then `start` already is that.
|
|
80
|
+
|
|
81
|
+
Every command is spelled for the package manager the project got. That includes the runner for the one CLI a
|
|
82
|
+
scaffold does not install, Vercel's: `npx`, `pnpm dlx`, `yarn dlx` or `bunx`. Wrangler is a devDependency
|
|
83
|
+
instead, since `wrangler dev` is how a Cloudflare app previews.
|
|
84
|
+
|
|
85
|
+
Those scripts are for a laptop, and a platform building from git asks a different question — what to type into
|
|
86
|
+
two fields on a settings page, where a `deploy` script would build twice. So each target also spells out the
|
|
87
|
+
commands its own platform asks for, in the scaffolded README's Deploying section.
|
|
88
|
+
|
|
74
89
|
`react` and `react-dom` are pinned **exactly**, at the versions the framework is tested against, and those
|
|
75
90
|
pins are generated from rshono's own manifest. That is not tidiness: the RSC runtime reaches into React's
|
|
76
91
|
internals, and an app installed with npm or bun has no workspace overrides to keep a single copy of it.
|
package/dist/api.mjs
CHANGED
|
@@ -77,9 +77,84 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
77
77
|
] : [];
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
;// CONCATENATED MODULE: ./src/scripts.ts
|
|
81
|
+
// `features/types.js` rather than `features/index.js`: the deploy feature imports `invoke` from here, and
|
|
82
|
+
// going through the barrel would make that a cycle — types.ts imports nothing.
|
|
83
|
+
/**
|
|
84
|
+
* The scripts every app gets, whatever it targets.
|
|
85
|
+
*
|
|
86
|
+
* `start`, `preview` and `deploy` are deliberately not here: each one means something different per
|
|
87
|
+
* platform, so the deploy target contributes its own. The three names are a contract the targets keep,
|
|
88
|
+
* and the reason the README can describe an app it was not written for:
|
|
89
|
+
*
|
|
90
|
+
* - **`start`** runs a build that already exists and never makes one — what a host's own start command
|
|
91
|
+
* calls. Only the target whose build is a server has one.
|
|
92
|
+
* - **`preview`** builds, then runs the result here — for the targets where that is not the same two
|
|
93
|
+
* commands, because otherwise the production build is unanswerable without deploying it.
|
|
94
|
+
* - **`deploy`** builds, then ships it — where the platform has one command that does the shipping.
|
|
95
|
+
*/ const BASE_SCRIPTS = {
|
|
96
|
+
dev: 'rshono dev',
|
|
97
|
+
build: 'rshono build',
|
|
98
|
+
typecheck: 'tsc --noEmit'
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Every script the app gets, in the order they are written: the base ones, then each feature's, in the order
|
|
102
|
+
* the features were selected. The manifest and the README's command table are both this, so neither can
|
|
103
|
+
* document a script the other does not have.
|
|
104
|
+
*/ function buildScripts(features) {
|
|
105
|
+
const scripts = {
|
|
106
|
+
...BASE_SCRIPTS
|
|
107
|
+
};
|
|
108
|
+
for (const feature of features)Object.assign(scripts, feature.scripts);
|
|
109
|
+
return scripts;
|
|
110
|
+
}
|
|
111
|
+
/** The gloss for each base script, in the README's command table. `build` names the target it is for. */ function baseScriptHelp(deploy) {
|
|
112
|
+
return {
|
|
113
|
+
dev: 'dev server with HMR, http://localhost:3000',
|
|
114
|
+
build: `production build for ${deploy}`,
|
|
115
|
+
typecheck: 'tsc --noEmit'
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Script names a package manager has a command of its own for. `pnpm deploy` runs pnpm's workspace-deploy
|
|
120
|
+
* command and never looks at the manifest, so printing it would hand somebody a line that quietly does
|
|
121
|
+
* something else. The explicit `run` form is what all four managers accept, so those names get it.
|
|
122
|
+
*/ const SHADOWED = new Set([
|
|
123
|
+
'deploy'
|
|
124
|
+
]);
|
|
125
|
+
/** How to type one of the app's scripts with this package manager — `pnpm dev`, but `npm run dev`. */ function invoke(pm, script) {
|
|
126
|
+
return `${SHADOWED.has(script) ? `${pm.name} run` : pm.run} ${script}`;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The README's command table: one line per script, with the command to type and a one-line gloss.
|
|
130
|
+
*
|
|
131
|
+
* A script whose feature supplies no `scriptHelp` is left out and covered by the "package.json has the
|
|
132
|
+
* rest" line — that is how a formatter's `format:check` stays out of a table about running the app.
|
|
133
|
+
*/ function scriptTable(answers, features, pm) {
|
|
134
|
+
const help = baseScriptHelp(answers.deploy);
|
|
135
|
+
for (const feature of features)Object.assign(help, feature.scriptHelp);
|
|
136
|
+
const documented = Object.keys(buildScripts(features)).filter((name)=>help[name]);
|
|
137
|
+
const width = Math.max(...documented.map((name)=>invoke(pm, name).length));
|
|
138
|
+
return documented.map((name)=>`${invoke(pm, name).padEnd(width)} # ${help[name]}`).join('\n');
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The command that gets this app into production, as a sentence — the README's deploy step, and the same
|
|
142
|
+
* choice the closing summary makes, so the two cannot name different commands.
|
|
143
|
+
*
|
|
144
|
+
* Read off the scripts rather than the target, so a target that gains a `deploy` gains the sentence with it.
|
|
145
|
+
* `start` is the answer where there is no `deploy`: it ships nothing itself, but it is what the host runs.
|
|
146
|
+
*/ function deployStep(features, pm) {
|
|
147
|
+
const scripts = buildScripts(features);
|
|
148
|
+
if (scripts.deploy) return `\`${invoke(pm, 'deploy')}\` does the build and the upload in one step.`;
|
|
149
|
+
if (scripts.start) return `\`${invoke(pm, 'start')}\` runs that build wherever you host it, and never makes one.`;
|
|
150
|
+
// Every branch names a script the app has, so a target that contributes none of the three still reads true.
|
|
151
|
+
if (scripts.preview) return `\`${invoke(pm, 'preview')}\` runs the build here, so you can check it first.`;
|
|
152
|
+
return `\`${invoke(pm, 'build')}\` produces it; getting it there is yours to script.`;
|
|
153
|
+
}
|
|
154
|
+
|
|
80
155
|
;// CONCATENATED MODULE: ./src/generated/framework.ts
|
|
81
156
|
// GENERATED by scripts/codegen.mjs from packages/core — do not edit. Run `pnpm --filter @rshono/create codegen`.
|
|
82
|
-
/** The framework release a scaffolded app is pinned to: this package and rshono ship together. */ const RSHONO_VERSION = '1.0.0-rc.
|
|
157
|
+
/** The framework release a scaffolded app is pinned to: this package and rshono ship together. */ const RSHONO_VERSION = '1.0.0-rc.11';
|
|
83
158
|
/**
|
|
84
159
|
* The Node range rshono itself declares, restated in every scaffolded app's `engines` — so a CI image
|
|
85
160
|
* or a contributor on an older Node hears it from their package manager rather than from a stack trace.
|
|
@@ -160,66 +235,128 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
160
235
|
|
|
161
236
|
;// CONCATENATED MODULE: ./src/features/deploy.ts
|
|
162
237
|
|
|
238
|
+
|
|
163
239
|
/**
|
|
164
240
|
* What a deploy target adds beyond the `deploy` line in `rshono.config.ts`, which the template carries.
|
|
165
241
|
*
|
|
166
242
|
* Deliberately thin: the framework arranges its own output for every platform, and `rshono build`
|
|
167
|
-
* writes the one platform config that has to exist (`wrangler.jsonc
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
243
|
+
* writes the one platform config that has to exist (`wrangler.jsonc`) if the project has none — a second
|
|
244
|
+
* copy generated here would only go stale. So a target contributes the commands that run and ship the
|
|
245
|
+
* build, the CLI they need, the directories to gitignore, and a note for the step no command covers.
|
|
246
|
+
*
|
|
247
|
+
* What the three script names promise is documented once, above `BASE_SCRIPTS` in `scripts.ts`. Only `node`
|
|
248
|
+
* has a `start`, because `rshono start` refuses a bundle built for anywhere else, and it needs no `preview`:
|
|
249
|
+
* `build` then `start` already is one.
|
|
250
|
+
*
|
|
251
|
+
* `pm` is here because two things have to be spelled for the app's package manager — the runner that fetches
|
|
252
|
+
* the uninstalled Vercel CLI ({@link PackageManager.dlx}), and every command in {@link Feature.platformSetup}.
|
|
253
|
+
*/ function deployFeatures(pm) {
|
|
254
|
+
const build = invoke(pm, 'build');
|
|
255
|
+
return {
|
|
256
|
+
// Where a Node build goes from here is a Dockerfile or a process manager, neither of which this can
|
|
257
|
+
// guess — so the target contributes only the command that runs what was built.
|
|
258
|
+
node: {
|
|
259
|
+
id: 'deploy-node',
|
|
260
|
+
scripts: {
|
|
261
|
+
start: 'rshono start'
|
|
262
|
+
},
|
|
263
|
+
scriptHelp: {
|
|
264
|
+
start: 'run the build that exists — what your host calls'
|
|
265
|
+
},
|
|
266
|
+
platformSetup: [
|
|
267
|
+
'The two commands a host asks for:',
|
|
268
|
+
'',
|
|
269
|
+
`- **Build** — \`${pm.name} install && ${build}\``,
|
|
270
|
+
`- **Start** — \`${invoke(pm, 'start')}\``,
|
|
271
|
+
'',
|
|
272
|
+
`In a Dockerfile, the same pair: \`RUN ${build}\`, then \`CMD ["${pm.name}", "start"]\`.`
|
|
273
|
+
].join('\n')
|
|
184
274
|
},
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
275
|
+
cloudflare: {
|
|
276
|
+
id: 'deploy-cloudflare',
|
|
277
|
+
devDependencies: {
|
|
278
|
+
wrangler: TOOL_VERSIONS.wrangler
|
|
279
|
+
},
|
|
280
|
+
// The only two install scripts a scaffolded app can end up with, and wrangler brings both. Each
|
|
281
|
+
// one merely picks the platform binary out of the optional dependency that already carries it, so
|
|
282
|
+
// neither needs to run — `workerd --version` and `esbuild --version` both answer without it.
|
|
283
|
+
allowBuilds: {
|
|
284
|
+
esbuild: false,
|
|
285
|
+
workerd: false
|
|
286
|
+
},
|
|
287
|
+
// `wrangler dev` is the one preview that runs the code in the runtime it will actually run in:
|
|
288
|
+
// workerd, not Node, serving the assets the build assembled. Both scripts read the wrangler.jsonc the
|
|
289
|
+
// build wrote, so nothing here has to know where the bundle or the assets went.
|
|
290
|
+
scripts: {
|
|
291
|
+
preview: 'rshono build && wrangler dev',
|
|
292
|
+
deploy: 'rshono build && wrangler deploy'
|
|
293
|
+
},
|
|
294
|
+
scriptHelp: {
|
|
295
|
+
preview: 'build, then run it in workerd — port 8787',
|
|
296
|
+
deploy: 'build, then ship it to Cloudflare'
|
|
297
|
+
},
|
|
298
|
+
gitignore: [
|
|
299
|
+
'.wrangler/'
|
|
300
|
+
],
|
|
301
|
+
notes: [
|
|
302
|
+
'The first build writes wrangler.jsonc — yours to edit after that.'
|
|
303
|
+
],
|
|
304
|
+
platformSetup: [
|
|
305
|
+
`Building from a git repo instead: set Workers Builds' **Build command** to \`${build}\`.`,
|
|
306
|
+
'Its deploy command already defaults to `npx wrangler deploy`, and it installs dependencies itself.'
|
|
307
|
+
].join('\n')
|
|
191
308
|
},
|
|
192
|
-
|
|
193
|
-
|
|
309
|
+
vercel: {
|
|
310
|
+
id: 'deploy-vercel',
|
|
311
|
+
// `--prod` because the script is called `deploy`: without it the CLI uploads to a throwaway preview
|
|
312
|
+
// URL, which is a useful thing to have but not what the word means. The local `preview` is a Node
|
|
313
|
+
// build run here — the platform has no way to run its own prebuilt output on your machine.
|
|
314
|
+
scripts: {
|
|
315
|
+
preview: 'rshono build --deploy node && rshono start',
|
|
316
|
+
deploy: `rshono build && ${pm.dlx} vercel deploy --prebuilt --prod`
|
|
317
|
+
},
|
|
318
|
+
scriptHelp: {
|
|
319
|
+
preview: 'build for Node and run that here',
|
|
320
|
+
deploy: 'build, then upload it to production'
|
|
321
|
+
},
|
|
322
|
+
gitignore: [
|
|
323
|
+
'.vercel/'
|
|
324
|
+
],
|
|
325
|
+
notes: [
|
|
326
|
+
'--prebuilt uploads what rshono build assembled; the platform must not rebuild it.',
|
|
327
|
+
'Drop --prod from the deploy script for a preview URL instead.'
|
|
328
|
+
],
|
|
329
|
+
platformSetup: [
|
|
330
|
+
`Deploying from CI instead: the same \`${invoke(pm, 'deploy')}\`, with \`VERCEL_ORG_ID\`, \`VERCEL_PROJECT_ID\``,
|
|
331
|
+
'and a CLI token in the environment.',
|
|
332
|
+
'',
|
|
333
|
+
`If you let Vercel build the repo itself, set **Framework Preset** to Other — \`hono\` is otherwise`,
|
|
334
|
+
`detected as the Hono preset — and **Build Command** to \`${build}\`.`
|
|
335
|
+
].join('\n')
|
|
194
336
|
},
|
|
195
|
-
|
|
196
|
-
'
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
]
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
function deployFeature(target) {
|
|
222
|
-
return DEPLOY_FEATURES[target];
|
|
337
|
+
'aws-lambda': {
|
|
338
|
+
id: 'deploy-aws-lambda',
|
|
339
|
+
// No CLI to wrap, and no upload this could guess at. `preview` still applies — the bundle is a Node
|
|
340
|
+
// handler, so it runs here.
|
|
341
|
+
scripts: {
|
|
342
|
+
preview: 'rshono build --deploy node && rshono start'
|
|
343
|
+
},
|
|
344
|
+
scriptHelp: {
|
|
345
|
+
preview: 'build for Node and run that here'
|
|
346
|
+
},
|
|
347
|
+
notes: [
|
|
348
|
+
'Use a Function URL in RESPONSE_STREAM mode — a buffered invoke mode drops the streaming.'
|
|
349
|
+
],
|
|
350
|
+
platformSetup: [
|
|
351
|
+
`There is no settings page here; the upload is yours to script. A job needs \`${pm.name} install && ${build}\`,`,
|
|
352
|
+
'then the function package: `dist/`, plus `node_modules` for any dependency of your own, which the server',
|
|
353
|
+
'bundle leaves external.'
|
|
354
|
+
].join('\n')
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function deployFeature(target, pm) {
|
|
359
|
+
return deployFeatures(pm)[target];
|
|
223
360
|
}
|
|
224
361
|
|
|
225
362
|
;// CONCATENATED MODULE: ./src/features/quality.ts
|
|
@@ -365,9 +502,12 @@ function stylingFeature(styling) {
|
|
|
365
502
|
* The features a set of answers selects, in application order — so an overlay listed later wins a file
|
|
366
503
|
* both of them ship. Deduplicated by `id`, which is what lets one feature answer two questions (Biome
|
|
367
504
|
* is both the formatter and the linter) without contributing twice.
|
|
368
|
-
|
|
505
|
+
*
|
|
506
|
+
* `pm` reaches the deploy target because one script has to name the runner that fetches an uninstalled
|
|
507
|
+
* CLI; nothing else here depends on which package manager the app is for.
|
|
508
|
+
*/ function selectFeatures(answers, pm) {
|
|
369
509
|
const selected = [
|
|
370
|
-
deployFeature(answers.deploy),
|
|
510
|
+
deployFeature(answers.deploy, pm),
|
|
371
511
|
stylingFeature(answers.styling),
|
|
372
512
|
formatterFeature(answers.formatter),
|
|
373
513
|
linterFeature(answers.linter),
|
|
@@ -474,15 +614,7 @@ const QUALITY_PRESETS = [
|
|
|
474
614
|
|
|
475
615
|
;// CONCATENATED MODULE: ./src/pkg.ts
|
|
476
616
|
|
|
477
|
-
|
|
478
|
-
* The scripts every app gets. `start` is not among them, because it means something different per
|
|
479
|
-
* platform: `node` is the one target that runs the build itself, so it contributes its own, and a
|
|
480
|
-
* platform target contributes a `deploy` instead, where its platform has one command to give.
|
|
481
|
-
*/ const BASE_SCRIPTS = {
|
|
482
|
-
dev: 'rshono dev',
|
|
483
|
-
build: 'rshono build',
|
|
484
|
-
typecheck: 'tsc --noEmit'
|
|
485
|
-
};
|
|
617
|
+
|
|
486
618
|
/** Field order in the emitted file — the conventional reading order, and stable so snapshots are too. */ const FIELD_ORDER = [
|
|
487
619
|
'name',
|
|
488
620
|
'version',
|
|
@@ -524,13 +656,9 @@ function sorted(record) {
|
|
|
524
656
|
/**
|
|
525
657
|
* Assembles `package.json` from the answers and whatever the selected features contribute.
|
|
526
658
|
*
|
|
527
|
-
* Dependencies are sorted by name and scripts
|
|
528
|
-
*
|
|
529
|
-
* identical output, which is what makes the generated manifest snapshot-testable.
|
|
659
|
+
* Dependencies are sorted by name and scripts keep the order {@link buildScripts} gives them — so two runs
|
|
660
|
+
* with the same answers produce byte-identical output, which is what makes the manifest snapshot-testable.
|
|
530
661
|
*/ function buildPackageJson(answers, features, pm) {
|
|
531
|
-
const scripts = {
|
|
532
|
-
...BASE_SCRIPTS
|
|
533
|
-
};
|
|
534
662
|
const dependencies = {
|
|
535
663
|
'@rshono/core': RSHONO_RANGE,
|
|
536
664
|
hono: FRAMEWORK_DEPS.hono,
|
|
@@ -543,7 +671,6 @@ function sorted(record) {
|
|
|
543
671
|
typescript: FRAMEWORK_DEPS.typescript
|
|
544
672
|
};
|
|
545
673
|
for (const feature of features){
|
|
546
|
-
Object.assign(scripts, feature.scripts);
|
|
547
674
|
Object.assign(dependencies, feature.dependencies);
|
|
548
675
|
Object.assign(devDependencies, feature.devDependencies);
|
|
549
676
|
}
|
|
@@ -556,7 +683,7 @@ function sorted(record) {
|
|
|
556
683
|
engines: {
|
|
557
684
|
node: NODE_ENGINE
|
|
558
685
|
},
|
|
559
|
-
scripts,
|
|
686
|
+
scripts: buildScripts(features),
|
|
560
687
|
dependencies: sorted(dependencies),
|
|
561
688
|
devDependencies: sorted(devDependencies)
|
|
562
689
|
};
|
|
@@ -577,12 +704,15 @@ function sorted(record) {
|
|
|
577
704
|
* markdown `__NAME__` *is* strong emphasis — Prettier rewrites it to `**NAME**` and the token stops
|
|
578
705
|
* matching. `{{…}}` means nothing to any format these templates are written in.
|
|
579
706
|
*/ const TOKEN_PATTERN = /\{\{[A-Z][A-Z\d_]*\}\}/g;
|
|
580
|
-
function tokensFor(answers, pm) {
|
|
707
|
+
function tokensFor(answers, features, pm) {
|
|
581
708
|
return {
|
|
582
709
|
'{{PROJECT_NAME}}': answers.packageName,
|
|
583
710
|
'{{DEPLOY_TARGET}}': answers.deploy,
|
|
584
|
-
|
|
585
|
-
|
|
711
|
+
// Derived from the features rather than the answers, because all three are about the scripts the app
|
|
712
|
+
// actually got: its command table, the one command that ships it, and what its platform asks for.
|
|
713
|
+
'{{SCRIPT_TABLE}}': scriptTable(answers, features, pm),
|
|
714
|
+
'{{DEPLOY_STEP}}': deployStep(features, pm),
|
|
715
|
+
'{{PLATFORM_SETUP}}': features.map((feature)=>feature.platformSetup ?? '').join('')
|
|
586
716
|
};
|
|
587
717
|
}
|
|
588
718
|
/**
|
|
@@ -645,8 +775,8 @@ function readTemplateDir(dir) {
|
|
|
645
775
|
* decisions and the I/O are separated so the whole matrix of answers can be asserted on in a test, and
|
|
646
776
|
* so `--dry-run` is the same code path minus the last step.
|
|
647
777
|
*/ function plan_plan(answers, pm) {
|
|
648
|
-
const features = selectFeatures(answers);
|
|
649
|
-
const tokens = tokensFor(answers, pm);
|
|
778
|
+
const features = selectFeatures(answers, pm);
|
|
779
|
+
const tokens = tokensFor(answers, features, pm);
|
|
650
780
|
const raw = readTemplateDir(join(TEMPLATES_DIR, 'base'));
|
|
651
781
|
for (const feature of features){
|
|
652
782
|
for (const overlay of feature.overlays ?? []){
|
|
@@ -698,6 +828,12 @@ const RUN = {
|
|
|
698
828
|
yarn: 'yarn',
|
|
699
829
|
bun: 'bun'
|
|
700
830
|
};
|
|
831
|
+
const DLX = {
|
|
832
|
+
npm: 'npx',
|
|
833
|
+
pnpm: 'pnpm dlx',
|
|
834
|
+
yarn: 'yarn dlx',
|
|
835
|
+
bun: 'bunx'
|
|
836
|
+
};
|
|
701
837
|
function isKnown(name) {
|
|
702
838
|
return PACKAGE_MANAGERS.includes(name);
|
|
703
839
|
}
|
|
@@ -706,7 +842,8 @@ function packageManager(name, version) {
|
|
|
706
842
|
name,
|
|
707
843
|
version,
|
|
708
844
|
install: INSTALL[name],
|
|
709
|
-
run: RUN[name]
|
|
845
|
+
run: RUN[name],
|
|
846
|
+
dlx: DLX[name]
|
|
710
847
|
};
|
|
711
848
|
}
|
|
712
849
|
/**
|
package/dist/cli.mjs
CHANGED
|
@@ -2943,7 +2943,7 @@ function hasGit(cwd) {
|
|
|
2943
2943
|
|
|
2944
2944
|
;// CONCATENATED MODULE: ./src/generated/framework.ts
|
|
2945
2945
|
// GENERATED by scripts/codegen.mjs from packages/core — do not edit. Run `pnpm --filter @rshono/create codegen`.
|
|
2946
|
-
/** The framework release a scaffolded app is pinned to: this package and rshono ship together. */ const RSHONO_VERSION = '1.0.0-rc.
|
|
2946
|
+
/** The framework release a scaffolded app is pinned to: this package and rshono ship together. */ const RSHONO_VERSION = '1.0.0-rc.11';
|
|
2947
2947
|
/**
|
|
2948
2948
|
* The Node range rshono itself declares, restated in every scaffolded app's `engines` — so a CI image
|
|
2949
2949
|
* or a contributor on an older Node hears it from their package manager rather than from a stack trace.
|
|
@@ -3091,6 +3091,81 @@ const QUALITY_PRESETS = [
|
|
|
3091
3091
|
] : [];
|
|
3092
3092
|
}
|
|
3093
3093
|
|
|
3094
|
+
;// CONCATENATED MODULE: ./src/scripts.ts
|
|
3095
|
+
// `features/types.js` rather than `features/index.js`: the deploy feature imports `invoke` from here, and
|
|
3096
|
+
// going through the barrel would make that a cycle — types.ts imports nothing.
|
|
3097
|
+
/**
|
|
3098
|
+
* The scripts every app gets, whatever it targets.
|
|
3099
|
+
*
|
|
3100
|
+
* `start`, `preview` and `deploy` are deliberately not here: each one means something different per
|
|
3101
|
+
* platform, so the deploy target contributes its own. The three names are a contract the targets keep,
|
|
3102
|
+
* and the reason the README can describe an app it was not written for:
|
|
3103
|
+
*
|
|
3104
|
+
* - **`start`** runs a build that already exists and never makes one — what a host's own start command
|
|
3105
|
+
* calls. Only the target whose build is a server has one.
|
|
3106
|
+
* - **`preview`** builds, then runs the result here — for the targets where that is not the same two
|
|
3107
|
+
* commands, because otherwise the production build is unanswerable without deploying it.
|
|
3108
|
+
* - **`deploy`** builds, then ships it — where the platform has one command that does the shipping.
|
|
3109
|
+
*/ const BASE_SCRIPTS = {
|
|
3110
|
+
dev: 'rshono dev',
|
|
3111
|
+
build: 'rshono build',
|
|
3112
|
+
typecheck: 'tsc --noEmit'
|
|
3113
|
+
};
|
|
3114
|
+
/**
|
|
3115
|
+
* Every script the app gets, in the order they are written: the base ones, then each feature's, in the order
|
|
3116
|
+
* the features were selected. The manifest and the README's command table are both this, so neither can
|
|
3117
|
+
* document a script the other does not have.
|
|
3118
|
+
*/ function buildScripts(features) {
|
|
3119
|
+
const scripts = {
|
|
3120
|
+
...BASE_SCRIPTS
|
|
3121
|
+
};
|
|
3122
|
+
for (const feature of features)Object.assign(scripts, feature.scripts);
|
|
3123
|
+
return scripts;
|
|
3124
|
+
}
|
|
3125
|
+
/** The gloss for each base script, in the README's command table. `build` names the target it is for. */ function baseScriptHelp(deploy) {
|
|
3126
|
+
return {
|
|
3127
|
+
dev: 'dev server with HMR, http://localhost:3000',
|
|
3128
|
+
build: `production build for ${deploy}`,
|
|
3129
|
+
typecheck: 'tsc --noEmit'
|
|
3130
|
+
};
|
|
3131
|
+
}
|
|
3132
|
+
/**
|
|
3133
|
+
* Script names a package manager has a command of its own for. `pnpm deploy` runs pnpm's workspace-deploy
|
|
3134
|
+
* command and never looks at the manifest, so printing it would hand somebody a line that quietly does
|
|
3135
|
+
* something else. The explicit `run` form is what all four managers accept, so those names get it.
|
|
3136
|
+
*/ const SHADOWED = new Set([
|
|
3137
|
+
'deploy'
|
|
3138
|
+
]);
|
|
3139
|
+
/** How to type one of the app's scripts with this package manager — `pnpm dev`, but `npm run dev`. */ function invoke(pm, script) {
|
|
3140
|
+
return `${SHADOWED.has(script) ? `${pm.name} run` : pm.run} ${script}`;
|
|
3141
|
+
}
|
|
3142
|
+
/**
|
|
3143
|
+
* The README's command table: one line per script, with the command to type and a one-line gloss.
|
|
3144
|
+
*
|
|
3145
|
+
* A script whose feature supplies no `scriptHelp` is left out and covered by the "package.json has the
|
|
3146
|
+
* rest" line — that is how a formatter's `format:check` stays out of a table about running the app.
|
|
3147
|
+
*/ function scriptTable(answers, features, pm) {
|
|
3148
|
+
const help = baseScriptHelp(answers.deploy);
|
|
3149
|
+
for (const feature of features)Object.assign(help, feature.scriptHelp);
|
|
3150
|
+
const documented = Object.keys(buildScripts(features)).filter((name)=>help[name]);
|
|
3151
|
+
const width = Math.max(...documented.map((name)=>invoke(pm, name).length));
|
|
3152
|
+
return documented.map((name)=>`${invoke(pm, name).padEnd(width)} # ${help[name]}`).join('\n');
|
|
3153
|
+
}
|
|
3154
|
+
/**
|
|
3155
|
+
* The command that gets this app into production, as a sentence — the README's deploy step, and the same
|
|
3156
|
+
* choice the closing summary makes, so the two cannot name different commands.
|
|
3157
|
+
*
|
|
3158
|
+
* Read off the scripts rather than the target, so a target that gains a `deploy` gains the sentence with it.
|
|
3159
|
+
* `start` is the answer where there is no `deploy`: it ships nothing itself, but it is what the host runs.
|
|
3160
|
+
*/ function deployStep(features, pm) {
|
|
3161
|
+
const scripts = buildScripts(features);
|
|
3162
|
+
if (scripts.deploy) return `\`${invoke(pm, 'deploy')}\` does the build and the upload in one step.`;
|
|
3163
|
+
if (scripts.start) return `\`${invoke(pm, 'start')}\` runs that build wherever you host it, and never makes one.`;
|
|
3164
|
+
// Every branch names a script the app has, so a target that contributes none of the three still reads true.
|
|
3165
|
+
if (scripts.preview) return `\`${invoke(pm, 'preview')}\` runs the build here, so you can check it first.`;
|
|
3166
|
+
return `\`${invoke(pm, 'build')}\` produces it; getting it there is yours to script.`;
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3094
3169
|
;// CONCATENATED MODULE: ./src/versions.ts
|
|
3095
3170
|
|
|
3096
3171
|
/** Passed straight through, so everything generated from the framework reaches the rest of the package here. */
|
|
@@ -3136,66 +3211,128 @@ const QUALITY_PRESETS = [
|
|
|
3136
3211
|
|
|
3137
3212
|
;// CONCATENATED MODULE: ./src/features/deploy.ts
|
|
3138
3213
|
|
|
3214
|
+
|
|
3139
3215
|
/**
|
|
3140
3216
|
* What a deploy target adds beyond the `deploy` line in `rshono.config.ts`, which the template carries.
|
|
3141
3217
|
*
|
|
3142
3218
|
* Deliberately thin: the framework arranges its own output for every platform, and `rshono build`
|
|
3143
|
-
* writes the one platform config that has to exist (`wrangler.jsonc
|
|
3144
|
-
*
|
|
3145
|
-
*
|
|
3146
|
-
*
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3219
|
+
* writes the one platform config that has to exist (`wrangler.jsonc`) if the project has none — a second
|
|
3220
|
+
* copy generated here would only go stale. So a target contributes the commands that run and ship the
|
|
3221
|
+
* build, the CLI they need, the directories to gitignore, and a note for the step no command covers.
|
|
3222
|
+
*
|
|
3223
|
+
* What the three script names promise is documented once, above `BASE_SCRIPTS` in `scripts.ts`. Only `node`
|
|
3224
|
+
* has a `start`, because `rshono start` refuses a bundle built for anywhere else, and it needs no `preview`:
|
|
3225
|
+
* `build` then `start` already is one.
|
|
3226
|
+
*
|
|
3227
|
+
* `pm` is here because two things have to be spelled for the app's package manager — the runner that fetches
|
|
3228
|
+
* the uninstalled Vercel CLI ({@link PackageManager.dlx}), and every command in {@link Feature.platformSetup}.
|
|
3229
|
+
*/ function deployFeatures(pm) {
|
|
3230
|
+
const build = invoke(pm, 'build');
|
|
3231
|
+
return {
|
|
3232
|
+
// Where a Node build goes from here is a Dockerfile or a process manager, neither of which this can
|
|
3233
|
+
// guess — so the target contributes only the command that runs what was built.
|
|
3234
|
+
node: {
|
|
3235
|
+
id: 'deploy-node',
|
|
3236
|
+
scripts: {
|
|
3237
|
+
start: 'rshono start'
|
|
3238
|
+
},
|
|
3239
|
+
scriptHelp: {
|
|
3240
|
+
start: 'run the build that exists — what your host calls'
|
|
3241
|
+
},
|
|
3242
|
+
platformSetup: [
|
|
3243
|
+
'The two commands a host asks for:',
|
|
3244
|
+
'',
|
|
3245
|
+
`- **Build** — \`${pm.name} install && ${build}\``,
|
|
3246
|
+
`- **Start** — \`${invoke(pm, 'start')}\``,
|
|
3247
|
+
'',
|
|
3248
|
+
`In a Dockerfile, the same pair: \`RUN ${build}\`, then \`CMD ["${pm.name}", "start"]\`.`
|
|
3249
|
+
].join('\n')
|
|
3167
3250
|
},
|
|
3168
|
-
|
|
3169
|
-
|
|
3251
|
+
cloudflare: {
|
|
3252
|
+
id: 'deploy-cloudflare',
|
|
3253
|
+
devDependencies: {
|
|
3254
|
+
wrangler: TOOL_VERSIONS.wrangler
|
|
3255
|
+
},
|
|
3256
|
+
// The only two install scripts a scaffolded app can end up with, and wrangler brings both. Each
|
|
3257
|
+
// one merely picks the platform binary out of the optional dependency that already carries it, so
|
|
3258
|
+
// neither needs to run — `workerd --version` and `esbuild --version` both answer without it.
|
|
3259
|
+
allowBuilds: {
|
|
3260
|
+
esbuild: false,
|
|
3261
|
+
workerd: false
|
|
3262
|
+
},
|
|
3263
|
+
// `wrangler dev` is the one preview that runs the code in the runtime it will actually run in:
|
|
3264
|
+
// workerd, not Node, serving the assets the build assembled. Both scripts read the wrangler.jsonc the
|
|
3265
|
+
// build wrote, so nothing here has to know where the bundle or the assets went.
|
|
3266
|
+
scripts: {
|
|
3267
|
+
preview: 'rshono build && wrangler dev',
|
|
3268
|
+
deploy: 'rshono build && wrangler deploy'
|
|
3269
|
+
},
|
|
3270
|
+
scriptHelp: {
|
|
3271
|
+
preview: 'build, then run it in workerd — port 8787',
|
|
3272
|
+
deploy: 'build, then ship it to Cloudflare'
|
|
3273
|
+
},
|
|
3274
|
+
gitignore: [
|
|
3275
|
+
'.wrangler/'
|
|
3276
|
+
],
|
|
3277
|
+
notes: [
|
|
3278
|
+
'The first build writes wrangler.jsonc — yours to edit after that.'
|
|
3279
|
+
],
|
|
3280
|
+
platformSetup: [
|
|
3281
|
+
`Building from a git repo instead: set Workers Builds' **Build command** to \`${build}\`.`,
|
|
3282
|
+
'Its deploy command already defaults to `npx wrangler deploy`, and it installs dependencies itself.'
|
|
3283
|
+
].join('\n')
|
|
3170
3284
|
},
|
|
3171
|
-
|
|
3172
|
-
'
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3285
|
+
vercel: {
|
|
3286
|
+
id: 'deploy-vercel',
|
|
3287
|
+
// `--prod` because the script is called `deploy`: without it the CLI uploads to a throwaway preview
|
|
3288
|
+
// URL, which is a useful thing to have but not what the word means. The local `preview` is a Node
|
|
3289
|
+
// build run here — the platform has no way to run its own prebuilt output on your machine.
|
|
3290
|
+
scripts: {
|
|
3291
|
+
preview: 'rshono build --deploy node && rshono start',
|
|
3292
|
+
deploy: `rshono build && ${pm.dlx} vercel deploy --prebuilt --prod`
|
|
3293
|
+
},
|
|
3294
|
+
scriptHelp: {
|
|
3295
|
+
preview: 'build for Node and run that here',
|
|
3296
|
+
deploy: 'build, then upload it to production'
|
|
3297
|
+
},
|
|
3298
|
+
gitignore: [
|
|
3299
|
+
'.vercel/'
|
|
3300
|
+
],
|
|
3301
|
+
notes: [
|
|
3302
|
+
'--prebuilt uploads what rshono build assembled; the platform must not rebuild it.',
|
|
3303
|
+
'Drop --prod from the deploy script for a preview URL instead.'
|
|
3304
|
+
],
|
|
3305
|
+
platformSetup: [
|
|
3306
|
+
`Deploying from CI instead: the same \`${invoke(pm, 'deploy')}\`, with \`VERCEL_ORG_ID\`, \`VERCEL_PROJECT_ID\``,
|
|
3307
|
+
'and a CLI token in the environment.',
|
|
3308
|
+
'',
|
|
3309
|
+
`If you let Vercel build the repo itself, set **Framework Preset** to Other — \`hono\` is otherwise`,
|
|
3310
|
+
`detected as the Hono preset — and **Build Command** to \`${build}\`.`
|
|
3311
|
+
].join('\n')
|
|
3182
3312
|
},
|
|
3183
|
-
|
|
3184
|
-
'
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3313
|
+
'aws-lambda': {
|
|
3314
|
+
id: 'deploy-aws-lambda',
|
|
3315
|
+
// No CLI to wrap, and no upload this could guess at. `preview` still applies — the bundle is a Node
|
|
3316
|
+
// handler, so it runs here.
|
|
3317
|
+
scripts: {
|
|
3318
|
+
preview: 'rshono build --deploy node && rshono start'
|
|
3319
|
+
},
|
|
3320
|
+
scriptHelp: {
|
|
3321
|
+
preview: 'build for Node and run that here'
|
|
3322
|
+
},
|
|
3323
|
+
notes: [
|
|
3324
|
+
'Use a Function URL in RESPONSE_STREAM mode — a buffered invoke mode drops the streaming.'
|
|
3325
|
+
],
|
|
3326
|
+
platformSetup: [
|
|
3327
|
+
`There is no settings page here; the upload is yours to script. A job needs \`${pm.name} install && ${build}\`,`,
|
|
3328
|
+
'then the function package: `dist/`, plus `node_modules` for any dependency of your own, which the server',
|
|
3329
|
+
'bundle leaves external.'
|
|
3330
|
+
].join('\n')
|
|
3331
|
+
}
|
|
3332
|
+
};
|
|
3333
|
+
}
|
|
3334
|
+
function deployFeature(target, pm) {
|
|
3335
|
+
return deployFeatures(pm)[target];
|
|
3199
3336
|
}
|
|
3200
3337
|
|
|
3201
3338
|
;// CONCATENATED MODULE: ./src/features/quality.ts
|
|
@@ -3341,9 +3478,12 @@ function stylingFeature(styling) {
|
|
|
3341
3478
|
* The features a set of answers selects, in application order — so an overlay listed later wins a file
|
|
3342
3479
|
* both of them ship. Deduplicated by `id`, which is what lets one feature answer two questions (Biome
|
|
3343
3480
|
* is both the formatter and the linter) without contributing twice.
|
|
3344
|
-
|
|
3481
|
+
*
|
|
3482
|
+
* `pm` reaches the deploy target because one script has to name the runner that fetches an uninstalled
|
|
3483
|
+
* CLI; nothing else here depends on which package manager the app is for.
|
|
3484
|
+
*/ function selectFeatures(answers, pm) {
|
|
3345
3485
|
const selected = [
|
|
3346
|
-
deployFeature(answers.deploy),
|
|
3486
|
+
deployFeature(answers.deploy, pm),
|
|
3347
3487
|
stylingFeature(answers.styling),
|
|
3348
3488
|
formatterFeature(answers.formatter),
|
|
3349
3489
|
linterFeature(answers.linter),
|
|
@@ -3361,15 +3501,7 @@ function stylingFeature(styling) {
|
|
|
3361
3501
|
|
|
3362
3502
|
;// CONCATENATED MODULE: ./src/pkg.ts
|
|
3363
3503
|
|
|
3364
|
-
|
|
3365
|
-
* The scripts every app gets. `start` is not among them, because it means something different per
|
|
3366
|
-
* platform: `node` is the one target that runs the build itself, so it contributes its own, and a
|
|
3367
|
-
* platform target contributes a `deploy` instead, where its platform has one command to give.
|
|
3368
|
-
*/ const BASE_SCRIPTS = {
|
|
3369
|
-
dev: 'rshono dev',
|
|
3370
|
-
build: 'rshono build',
|
|
3371
|
-
typecheck: 'tsc --noEmit'
|
|
3372
|
-
};
|
|
3504
|
+
|
|
3373
3505
|
/** Field order in the emitted file — the conventional reading order, and stable so snapshots are too. */ const FIELD_ORDER = [
|
|
3374
3506
|
'name',
|
|
3375
3507
|
'version',
|
|
@@ -3411,13 +3543,9 @@ function sorted(record) {
|
|
|
3411
3543
|
/**
|
|
3412
3544
|
* Assembles `package.json` from the answers and whatever the selected features contribute.
|
|
3413
3545
|
*
|
|
3414
|
-
* Dependencies are sorted by name and scripts
|
|
3415
|
-
*
|
|
3416
|
-
* identical output, which is what makes the generated manifest snapshot-testable.
|
|
3546
|
+
* Dependencies are sorted by name and scripts keep the order {@link buildScripts} gives them — so two runs
|
|
3547
|
+
* with the same answers produce byte-identical output, which is what makes the manifest snapshot-testable.
|
|
3417
3548
|
*/ function buildPackageJson(answers, features, pm) {
|
|
3418
|
-
const scripts = {
|
|
3419
|
-
...BASE_SCRIPTS
|
|
3420
|
-
};
|
|
3421
3549
|
const dependencies = {
|
|
3422
3550
|
'@rshono/core': RSHONO_RANGE,
|
|
3423
3551
|
hono: FRAMEWORK_DEPS.hono,
|
|
@@ -3430,7 +3558,6 @@ function sorted(record) {
|
|
|
3430
3558
|
typescript: FRAMEWORK_DEPS.typescript
|
|
3431
3559
|
};
|
|
3432
3560
|
for (const feature of features){
|
|
3433
|
-
Object.assign(scripts, feature.scripts);
|
|
3434
3561
|
Object.assign(dependencies, feature.dependencies);
|
|
3435
3562
|
Object.assign(devDependencies, feature.devDependencies);
|
|
3436
3563
|
}
|
|
@@ -3443,7 +3570,7 @@ function sorted(record) {
|
|
|
3443
3570
|
engines: {
|
|
3444
3571
|
node: NODE_ENGINE
|
|
3445
3572
|
},
|
|
3446
|
-
scripts,
|
|
3573
|
+
scripts: buildScripts(features),
|
|
3447
3574
|
dependencies: sorted(dependencies),
|
|
3448
3575
|
devDependencies: sorted(devDependencies)
|
|
3449
3576
|
};
|
|
@@ -3464,12 +3591,15 @@ function sorted(record) {
|
|
|
3464
3591
|
* markdown `__NAME__` *is* strong emphasis — Prettier rewrites it to `**NAME**` and the token stops
|
|
3465
3592
|
* matching. `{{…}}` means nothing to any format these templates are written in.
|
|
3466
3593
|
*/ const TOKEN_PATTERN = /\{\{[A-Z][A-Z\d_]*\}\}/g;
|
|
3467
|
-
function tokensFor(answers, pm) {
|
|
3594
|
+
function tokensFor(answers, features, pm) {
|
|
3468
3595
|
return {
|
|
3469
3596
|
'{{PROJECT_NAME}}': answers.packageName,
|
|
3470
3597
|
'{{DEPLOY_TARGET}}': answers.deploy,
|
|
3471
|
-
|
|
3472
|
-
|
|
3598
|
+
// Derived from the features rather than the answers, because all three are about the scripts the app
|
|
3599
|
+
// actually got: its command table, the one command that ships it, and what its platform asks for.
|
|
3600
|
+
'{{SCRIPT_TABLE}}': scriptTable(answers, features, pm),
|
|
3601
|
+
'{{DEPLOY_STEP}}': deployStep(features, pm),
|
|
3602
|
+
'{{PLATFORM_SETUP}}': features.map((feature)=>feature.platformSetup ?? '').join('')
|
|
3473
3603
|
};
|
|
3474
3604
|
}
|
|
3475
3605
|
/**
|
|
@@ -3532,8 +3662,8 @@ function readTemplateDir(dir) {
|
|
|
3532
3662
|
* decisions and the I/O are separated so the whole matrix of answers can be asserted on in a test, and
|
|
3533
3663
|
* so `--dry-run` is the same code path minus the last step.
|
|
3534
3664
|
*/ function plan_plan(answers, pm) {
|
|
3535
|
-
const features = selectFeatures(answers);
|
|
3536
|
-
const tokens = tokensFor(answers, pm);
|
|
3665
|
+
const features = selectFeatures(answers, pm);
|
|
3666
|
+
const tokens = tokensFor(answers, features, pm);
|
|
3537
3667
|
const raw = readTemplateDir(external_node_path_join(TEMPLATES_DIR, 'base'));
|
|
3538
3668
|
for (const feature of features){
|
|
3539
3669
|
for (const overlay of feature.overlays ?? []){
|
|
@@ -3583,6 +3713,12 @@ const RUN = {
|
|
|
3583
3713
|
yarn: 'yarn',
|
|
3584
3714
|
bun: 'bun'
|
|
3585
3715
|
};
|
|
3716
|
+
const DLX = {
|
|
3717
|
+
npm: 'npx',
|
|
3718
|
+
pnpm: 'pnpm dlx',
|
|
3719
|
+
yarn: 'yarn dlx',
|
|
3720
|
+
bun: 'bunx'
|
|
3721
|
+
};
|
|
3586
3722
|
function isKnown(name) {
|
|
3587
3723
|
return PACKAGE_MANAGERS.includes(name);
|
|
3588
3724
|
}
|
|
@@ -3591,7 +3727,8 @@ function packageManager(name, version) {
|
|
|
3591
3727
|
name,
|
|
3592
3728
|
version,
|
|
3593
3729
|
install: INSTALL[name],
|
|
3594
|
-
run: RUN[name]
|
|
3730
|
+
run: RUN[name],
|
|
3731
|
+
dlx: DLX[name]
|
|
3595
3732
|
};
|
|
3596
3733
|
}
|
|
3597
3734
|
/**
|
|
@@ -3625,6 +3762,17 @@ function run(pm, args, cwd) {
|
|
|
3625
3762
|
;// CONCATENATED MODULE: ./src/ui.ts
|
|
3626
3763
|
|
|
3627
3764
|
|
|
3765
|
+
|
|
3766
|
+
/**
|
|
3767
|
+
* The two halves of the closing line, in the words of the app's own scripts: what produces something
|
|
3768
|
+
* shippable, and where it goes from there. The target with no command to give keeps the framework's hint,
|
|
3769
|
+
* which is a sentence about the platform rather than something to type.
|
|
3770
|
+
*/ function productionSteps(answers, plan, pm) {
|
|
3771
|
+
const scripts = buildScripts(plan.features);
|
|
3772
|
+
const check = scripts.preview ? `${invoke(pm, 'preview')} to run the production build` : `${pm.run} build`;
|
|
3773
|
+
const ship = scripts.deploy ? `${invoke(pm, 'deploy')} to ship it` : scripts.start ? `${invoke(pm, 'start')} on the host that runs it` : deployHint(answers.deploy);
|
|
3774
|
+
return `${check}, and ${ship}`;
|
|
3775
|
+
}
|
|
3628
3776
|
/**
|
|
3629
3777
|
* What to do next, in the order to do it — the last thing the user reads, and for most people the only
|
|
3630
3778
|
* documentation they will read today. `installed` decides whether the install step is still theirs.
|
|
@@ -3636,7 +3784,7 @@ function run(pm, args, cwd) {
|
|
|
3636
3784
|
const lines = [
|
|
3637
3785
|
steps.join('\n')
|
|
3638
3786
|
];
|
|
3639
|
-
lines.push(`\nThen ${
|
|
3787
|
+
lines.push(`\nThen ${productionSteps(answers, plan, pm)}.`);
|
|
3640
3788
|
if (plan.notes.length > 0) lines.push(`\n${plan.notes.join('\n')}`);
|
|
3641
3789
|
return lines.join('\n');
|
|
3642
3790
|
}
|
|
@@ -3821,7 +3969,7 @@ function fail(message) {
|
|
|
3821
3969
|
async function main() {
|
|
3822
3970
|
const { values, positionals } = parse();
|
|
3823
3971
|
if (values.help) return console.log(HELP);
|
|
3824
|
-
if (values.version) return console.log("1.0.0-rc.
|
|
3972
|
+
if (values.version) return console.log("1.0.0-rc.11");
|
|
3825
3973
|
// A pipe, a CI job or an agent gets the defaults rather than a prompt nothing can answer. Both
|
|
3826
3974
|
// streams have to be a terminal: the prompts draw on stdout but *read from stdin*, so
|
|
3827
3975
|
// `echo | npx @rshono/create` would otherwise ask a question with nothing behind the keyboard.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rshono/create",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.11",
|
|
4
4
|
"description": "Scaffold a new rshono app — Hono + Rspack + React Server Components",
|
|
5
5
|
"author": "Lasse <lasse@lassetange.com> (https://www.lassetange.com)",
|
|
6
6
|
"license": "ISC",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@rspack/core": "2.1.7",
|
|
47
47
|
"@types/node": "^26.1.1",
|
|
48
48
|
"typescript": "^7.0.2",
|
|
49
|
-
"@rshono/core": "1.0.0-rc.
|
|
49
|
+
"@rshono/core": "1.0.0-rc.11"
|
|
50
50
|
},
|
|
51
51
|
"scripts": {
|
|
52
52
|
"build": "node scripts/codegen.mjs && node scripts/build.mjs",
|
package/templates/base/README.md
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# {{PROJECT_NAME}}
|
|
2
2
|
|
|
3
3
|
```bash
|
|
4
|
-
{{
|
|
5
|
-
{{PM_RUN}} build # production build for {{DEPLOY_TARGET}}
|
|
6
|
-
{{PM_RUN}} typecheck # tsc --noEmit
|
|
4
|
+
{{SCRIPT_TABLE}}
|
|
7
5
|
```
|
|
8
6
|
|
|
9
7
|
`package.json` has the rest, including whatever your formatter and linter added.
|
|
@@ -31,8 +29,12 @@ compiles to `undefined` rather than shipping. `src/components/layout.tsx` reads
|
|
|
31
29
|
|
|
32
30
|
## Deploying
|
|
33
31
|
|
|
34
|
-
This app is built for `{{DEPLOY_TARGET}}
|
|
32
|
+
This app is built for `{{DEPLOY_TARGET}}`. {{DEPLOY_STEP}}
|
|
33
|
+
|
|
34
|
+
{{PLATFORM_SETUP}}
|
|
35
35
|
|
|
36
36
|
Change `deploy` in `rshono.config.ts` to target somewhere else, or build for one place without editing the
|
|
37
37
|
file: `rshono build --deploy vercel`, or `RSHONO_DEPLOY=vercel` in CI. `dev` always runs the Node dev
|
|
38
38
|
server, whatever the target — it is a property of the build, not of developing.
|
|
39
|
+
|
|
40
|
+
Every target, and what each one needs: <https://www.rshono.com/docs/deployment>
|