@rshono/create 1.0.0-rc.11 → 1.0.0-rc.13

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.
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- // Thin launcher, kept deliberately old-syntax and dependency-free: it is the one file that has to
3
- // parse and run on whatever Node the user happens to have. The CLI itself is a bundle — its imports
4
- // are evaluated before any statement in it, so a version check *inside* it would be too late to
5
- // produce anything friendlier than a stack trace.
2
+ // Thin launcher, deliberately old-syntax and dependency-free: it is the one file that has to parse and run on
3
+ // whatever Node the user happens to have. A version check inside the CLI bundle would be too late — its imports
4
+ // evaluate before any statement in it.
5
+ //
6
6
  // Kept in step with `engines.node` in @rshono/core's manifest, which is what a scaffolded app pins.
7
7
  var MINIMUM = [22, 18, 0];
8
8
 
package/dist/api.mjs CHANGED
@@ -52,12 +52,12 @@ __webpack_require__.d(__webpack_exports__, {
52
52
 
53
53
  ;// CONCATENATED MODULE: ./src/features/combinations.ts
54
54
  /**
55
- * Biome's CSS parser rejects Tailwind's syntax — `@apply` and a `@layer` block are parse errors, not
56
- * unknown-at-rule warnings — so a project with both needs Biome pointed away from stylesheets. The
57
- * overlay is a second `biome.json` listed after the first, which is all "later overlay wins" means.
55
+ * Biome's CSS parser rejects Tailwind's syntax — `@apply` is a parse error, not an unknown-at-rule warning — so
56
+ * a project with both needs Biome pointed away from stylesheets. The overlay is a second `biome.json` listed
57
+ * after the first.
58
58
  *
59
- * Narrow on purpose: the alternative was excluding CSS for everybody, which would quietly give up
60
- * formatting the plain-CSS template that Biome handles perfectly well.
59
+ * Narrow on purpose: excluding CSS for everybody would give up formatting the plain-CSS template that Biome
60
+ * handles perfectly well.
61
61
  */ const BIOME_TAILWIND = {
62
62
  id: 'biome-tailwind',
63
63
  overlays: [
@@ -83,14 +83,12 @@ __webpack_require__.d(__webpack_exports__, {
83
83
  /**
84
84
  * The scripts every app gets, whatever it targets.
85
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:
86
+ * `start`, `preview` and `deploy` are not here: each means something different per platform, so the deploy
87
+ * target contributes its own. The three names are a contract the targets keep, which is what lets the README
88
+ * describe an app it was not written for:
89
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.
90
+ * - **`start`** runs a build that already exists and never makes one — what a host's own start command calls.
91
+ * - **`preview`** builds, then runs the result here, so the production build is answerable without deploying.
94
92
  * - **`deploy`** builds, then ships it — where the platform has one command that does the shipping.
95
93
  */ const BASE_SCRIPTS = {
96
94
  dev: 'rshono dev',
@@ -98,9 +96,8 @@ __webpack_require__.d(__webpack_exports__, {
98
96
  typecheck: 'tsc --noEmit'
99
97
  };
100
98
  /**
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.
99
+ * Every script the app gets, in the order they are written: the base ones, then each feature's. The manifest and
100
+ * the README's command table are both this, so neither can document a script the other lacks.
104
101
  */ function buildScripts(features) {
105
102
  const scripts = {
106
103
  ...BASE_SCRIPTS
@@ -116,9 +113,8 @@ __webpack_require__.d(__webpack_exports__, {
116
113
  };
117
114
  }
118
115
  /**
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.
116
+ * Script names a package manager has a command of its own for: `pnpm deploy` runs pnpm's workspace-deploy and
117
+ * never looks at the manifest. These get the explicit `run` form, which all four managers accept.
122
118
  */ const SHADOWED = new Set([
123
119
  'deploy'
124
120
  ]);
@@ -126,10 +122,9 @@ __webpack_require__.d(__webpack_exports__, {
126
122
  return `${SHADOWED.has(script) ? `${pm.name} run` : pm.run} ${script}`;
127
123
  }
128
124
  /**
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.
125
+ * The README's command table: one line per script, with the command to type and a one-line gloss. A script whose
126
+ * feature supplies no `scriptHelp` is left out — that is how `format:check` stays out of a table about running
127
+ * the app and covered by the line pointing at `package.json`.
133
128
  */ function scriptTable(answers, features, pm) {
134
129
  const help = baseScriptHelp(answers.deploy);
135
130
  for (const feature of features)Object.assign(help, feature.scriptHelp);
@@ -138,31 +133,28 @@ __webpack_require__.d(__webpack_exports__, {
138
133
  return documented.map((name)=>`${invoke(pm, name).padEnd(width)} # ${help[name]}`).join('\n');
139
134
  }
140
135
  /**
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.
136
+ * The command that gets this app into production, as a sentence — the README's deploy step and the closing
137
+ * summary's, so the two cannot name different commands.
143
138
  *
144
139
  * 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
140
  */ function deployStep(features, pm) {
147
141
  const scripts = buildScripts(features);
148
142
  if (scripts.deploy) return `\`${invoke(pm, 'deploy')}\` does the build and the upload in one step.`;
149
143
  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
144
  if (scripts.preview) return `\`${invoke(pm, 'preview')}\` runs the build here, so you can check it first.`;
152
145
  return `\`${invoke(pm, 'build')}\` produces it; getting it there is yours to script.`;
153
146
  }
154
147
 
155
148
  ;// CONCATENATED MODULE: ./src/generated/framework.ts
156
149
  // GENERATED by scripts/codegen.mjs from packages/core — do not edit. Run `pnpm --filter @rshono/create codegen`.
157
- /** The framework release a scaffolded app is pinned to: this package and rshono ship together. */ const RSHONO_VERSION = '1.0.0-rc.11';
150
+ /** The framework release a scaffolded app is pinned to: this package and rshono ship together. */ const RSHONO_VERSION = '1.0.0-rc.13';
158
151
  /**
159
- * The Node range rshono itself declares, restated in every scaffolded app's `engines` — so a CI image
160
- * or a contributor on an older Node hears it from their package manager rather than from a stack trace.
152
+ * The Node range rshono declares, restated in every scaffolded app's `engines` — so a CI image or a contributor
153
+ * on an older Node hears it from their package manager rather than from a stack trace.
161
154
  */ const NODE_ENGINE = '>=22.18.0';
162
155
  /**
163
- * The dependency versions rshono is tested against, copied from its own manifest. Exact where it is
164
- * exact — React's RSC internals are coupled across builds, and a generated app has no workspace
165
- * overrides to fall back on.
156
+ * The dependency versions rshono is tested against, copied from its own manifest. Exact where it is exact:
157
+ * React's RSC internals are coupled across builds, and a generated app has no workspace overrides.
166
158
  */ const FRAMEWORK_DEPS = {
167
159
  hono: '^4.12.31',
168
160
  react: '19.2.8',
@@ -195,14 +187,12 @@ __webpack_require__.d(__webpack_exports__, {
195
187
  /** Passed straight through, so everything generated from the framework reaches the rest of the package here. */
196
188
  /** The framework range a scaffolded app gets. The two packages are released together, so this is ours. */ const RSHONO_RANGE = `^${RSHONO_VERSION}`;
197
189
  /**
198
- * Versions for the optional tooling the features can add — the one place in this package where a
199
- * dependency range is typed out by hand, because none of these are things the framework itself
200
- * declares. Everything a *scaffolded app* needs to run rshono comes from {@link FRAMEWORK_DEPS},
201
- * which is generated from rshono's own manifest and must not be edited here.
190
+ * Versions for the optional tooling the features can add — the one place in this package where a range is typed
191
+ * by hand, because the framework declares none of these. Everything a scaffolded app needs to *run* rshono comes
192
+ * from {@link FRAMEWORK_DEPS}, generated from rshono's own manifest.
202
193
  *
203
- * Ranges are caret, not exact: these are the app's own dev tools, and a scaffold made six months from
204
- * now should pick up their patch releases rather than pinning whatever was current the day this file
205
- * was last touched.
194
+ * Caret ranges, not exact: these are the app's own dev tools, and a scaffold made six months from now should
195
+ * pick up their patch releases.
206
196
  */ const TOOL_VERSIONS = {
207
197
  tailwindcss: '^4.3.3',
208
198
  '@tailwindcss/postcss': '^4.3.3',
@@ -214,23 +204,20 @@ __webpack_require__.d(__webpack_exports__, {
214
204
  oxlint: '^1.76.0',
215
205
  oxfmt: '^0.61.0',
216
206
  eslint: '^10.8.0',
217
- // ESLint's own recommended JavaScript rules, which typescript-eslint layers on top of rather than
218
- // replaces, and the rules of hooks — the one class of React mistake no type checker sees.
207
+ // ESLint's recommended JavaScript rules, which typescript-eslint layers on rather than replaces, and the rules
208
+ // of hooks — the one class of React mistake no type checker sees.
219
209
  '@eslint/js': '^10.0.1',
220
210
  'typescript-eslint': '^8.65.0',
221
211
  'eslint-plugin-react-hooks': '^7.1.1',
222
212
  wrangler: '^4.115.0'
223
213
  };
224
214
  /**
225
- * The TypeScript an ESLint app pins, in place of the framework's own — the one deliberate exception to
215
+ * The TypeScript an ESLint app pins in place of the framework's own — the one deliberate exception to
226
216
  * {@link FRAMEWORK_DEPS}.
227
217
  *
228
- * typescript-eslint reads TypeScript's compiler API directly rather than through a stable interface,
229
- * so it accepts `typescript >=4.8.4 <6.1.0` and nothing above; `~6.0.3` is the newest that satisfies
230
- * it. rshono's declarations compile the same under either, which is what makes this an app's business
231
- * and not the framework's.
232
- *
233
- * When upstream widens the range, this constant and the ESLint feature's use of it are what to delete.
218
+ * typescript-eslint reads TypeScript's compiler API directly, so it accepts `>=4.8.4 <6.1.0` and nothing above;
219
+ * `~6.0.3` is the newest that satisfies it. rshono's declarations compile the same under either, which is what
220
+ * makes this the app's business. When upstream widens its range, this constant is what to delete.
234
221
  */ const ESLINT_TYPESCRIPT = '~6.0.3';
235
222
 
236
223
  ;// CONCATENATED MODULE: ./src/features/deploy.ts
@@ -239,22 +226,17 @@ __webpack_require__.d(__webpack_exports__, {
239
226
  /**
240
227
  * What a deploy target adds beyond the `deploy` line in `rshono.config.ts`, which the template carries.
241
228
  *
242
- * Deliberately thin: the framework arranges its own output for every platform, and `rshono build`
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.
229
+ * Thin on purpose: the framework arranges its own output for every platform, and `rshono build` writes the one
230
+ * platform config that has to exist. So a target contributes the commands that run and ship the build, the CLI
231
+ * they need, the directories to gitignore, and a note for the step no command covers.
250
232
  *
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}.
233
+ * What the three script names promise is documented above `BASE_SCRIPTS` in `scripts.ts`. Only `node` has a
234
+ * `start` `rshono start` refuses a bundle built for anywhere else — and it needs no `preview`, since `build`
235
+ * then `start` already is one.
253
236
  */ function deployFeatures(pm) {
254
237
  const build = invoke(pm, 'build');
255
238
  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.
239
+ // Where a Node build goes next is a Dockerfile or a process manager, neither of which this can guess.
258
240
  node: {
259
241
  id: 'deploy-node',
260
242
  scripts: {
@@ -277,16 +259,14 @@ __webpack_require__.d(__webpack_exports__, {
277
259
  devDependencies: {
278
260
  wrangler: TOOL_VERSIONS.wrangler
279
261
  },
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.
262
+ // The only two install scripts a scaffolded app can end up with, and wrangler brings both. Each merely
263
+ // picks the platform binary out of the optional dependency already carrying it, so neither needs to run.
283
264
  allowBuilds: {
284
265
  esbuild: false,
285
266
  workerd: false
286
267
  },
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.
268
+ // `wrangler dev` is the one preview that runs the code in workerd rather than Node. Both scripts read the
269
+ // wrangler.jsonc the build wrote, so nothing here has to know where the bundle went.
290
270
  scripts: {
291
271
  preview: 'rshono build && wrangler dev',
292
272
  deploy: 'rshono build && wrangler deploy'
@@ -308,9 +288,8 @@ __webpack_require__.d(__webpack_exports__, {
308
288
  },
309
289
  vercel: {
310
290
  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.
291
+ // `--prod` because the script is called `deploy`: without it the CLI uploads to a throwaway preview URL.
292
+ // `preview` is a Node build run here the platform cannot run its own prebuilt output on your machine.
314
293
  scripts: {
315
294
  preview: 'rshono build --deploy node && rshono start',
316
295
  deploy: `rshono build && ${pm.dlx} vercel deploy --prebuilt --prod`
@@ -336,8 +315,7 @@ __webpack_require__.d(__webpack_exports__, {
336
315
  },
337
316
  'aws-lambda': {
338
317
  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.
318
+ // No CLI to wrap and no upload to guess at, but `preview` still applies: the bundle is a Node handler.
341
319
  scripts: {
342
320
  preview: 'rshono build --deploy node && rshono start'
343
321
  },
@@ -362,14 +340,11 @@ function deployFeature(target, pm) {
362
340
  ;// CONCATENATED MODULE: ./src/features/quality.ts
363
341
 
364
342
  /**
365
- * The formatter and linter features. Biome answers to both slots and appears once `selectFeatures`
366
- * deduplicates by `id`, so `formatter: 'biome', linter: 'biome'` contributes one set of files, one
367
- * dependency and one pair of scripts.
343
+ * The formatter and linter features. Biome answers to both slots and appears once, because `selectFeatures`
344
+ * deduplicates by `id`.
368
345
  *
369
- * A formatter brings `format:check` beside `format`, because the writing half and the CI half want
370
- * different exit-code behaviour: `format` rewrites files, `format:check` fails instead. A linter brings
371
- * `lint:fix` beside `lint`, for the same reason in the other direction — `lint` is already the failing
372
- * one. Biome adds a `check` of its own, which is the pair of them in a single pass.
346
+ * Each brings a pair of scripts, since the writing half and the CI half want different exit codes: `format`
347
+ * rewrites and `format:check` fails, `lint:fix` rewrites and `lint` fails. Biome's `check` is both in one pass.
373
348
  */ const PRETTIER = {
374
349
  id: 'prettier',
375
350
  overlays: [
@@ -410,11 +385,10 @@ const OXLINT = {
410
385
  }
411
386
  };
412
387
  /**
413
- * The one feature that changes a dependency the framework otherwise decides: typescript-eslint cannot
414
- * be installed alongside the TypeScript rshono is tested against, so an ESLint app pins the newest one
415
- * its peer range accepts (see {@link ESLINT_TYPESCRIPT}). Its rules are type-aware — the reason to
416
- * reach for ESLint over a syntax-only linter — so the config it ships hands the parser the whole
417
- * program rather than linting file by file.
388
+ * The one feature that changes a dependency the framework otherwise decides: typescript-eslint cannot be
389
+ * installed alongside the TypeScript rshono is tested against, so an ESLint app pins the newest its peer range
390
+ * accepts (see {@link ESLINT_TYPESCRIPT}). Its rules are type-aware — the reason to reach for ESLint at all —
391
+ * so the config it ships hands the parser the whole program.
418
392
  */ const ESLINT = {
419
393
  id: 'eslint',
420
394
  overlays: [
@@ -470,13 +444,12 @@ function linterFeature(linter) {
470
444
  ;// CONCATENATED MODULE: ./src/features/styling.ts
471
445
 
472
446
  /**
473
- * Tailwind is a PostCSS plugin and nothing more, which is the whole of this feature: four packages,
474
- * plus an overlay carrying `postcss.config.mjs`, an `rshono.config.ts` whose `rspack` hook puts
475
- * postcss-loader in front of the CSS parser, a Tailwind entry stylesheet, and the two views rewritten
476
- * in utilities.
447
+ * Tailwind is a PostCSS plugin and nothing more, which is the whole of this feature: four packages, plus an
448
+ * overlay carrying `postcss.config.mjs`, an `rshono.config.ts` whose `rspack` hook puts postcss-loader in front
449
+ * of the CSS parser, a Tailwind entry stylesheet, and the two views rewritten in utilities.
477
450
  *
478
- * `postcss` and `postcss-loader` are the app's dependencies rather than the framework's rshono
479
- * compiles CSS natively, so an app that does not want a plugin chain does not install one.
451
+ * `postcss` is the app's dependency rather than the framework's: rshono compiles CSS natively, so an app that
452
+ * wants no plugin chain installs none.
480
453
  */ const TAILWIND = {
481
454
  id: 'tailwind',
482
455
  overlays: [
@@ -499,12 +472,11 @@ function stylingFeature(styling) {
499
472
 
500
473
 
501
474
  /**
502
- * The features a set of answers selects, in application order — so an overlay listed later wins a file
503
- * both of them ship. Deduplicated by `id`, which is what lets one feature answer two questions (Biome
504
- * is both the formatter and the linter) without contributing twice.
475
+ * The features a set of answers selects, in application order — so an overlay listed later wins a file both
476
+ * ship. Deduplicated by `id`, which is what lets one feature answer two questions (Biome is both formatter and
477
+ * linter) without contributing twice.
505
478
  *
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.
479
+ * `pm` reaches the deploy target because one script has to name the runner that fetches an uninstalled CLI.
508
480
  */ function selectFeatures(answers, pm) {
509
481
  const selected = [
510
482
  deployFeature(answers.deploy, pm),
@@ -526,9 +498,8 @@ function stylingFeature(styling) {
526
498
  ;// CONCATENATED MODULE: ./src/options.ts
527
499
 
528
500
  /*
529
- * The names each option accepts, spelled once. The types below are derived from them, the CLI validates
530
- * its flags against them and prints them in `--help`, and `pm.ts` recognises a package manager by them
531
- * — so a name added here reaches all three without a second list to remember.
501
+ * The names each option accepts, spelled once: the types below are derived from them, the CLI validates its
502
+ * flags against them and prints them in `--help`, and `pm.ts` recognises a package manager by them.
532
503
  */ const FORMATTER_NAMES = [
533
504
  'prettier',
534
505
  'biome',
@@ -589,14 +560,13 @@ const QUALITY_PRESETS = [
589
560
  }
590
561
  ];
591
562
  /**
592
- * Turns whatever the user typed into a name npm will accept, or returns `null` when nothing usable is
593
- * left. Lowercasing and replacing runs of invalid characters covers the ordinary cases (`My App`,
594
- * `my_app`); a scoped name is kept intact, since `@scope/name` is legal.
563
+ * Turns whatever the user typed into a name npm will accept, or `null` when nothing usable is left.
564
+ * Lowercasing and replacing runs of invalid characters covers the ordinary cases (`My App`, `my_app`).
595
565
  */ function toPackageName(input) {
596
566
  const trimmed = input.trim().replace(/^\.\/+/, '').replace(/\/+$/, '');
597
567
  if (!trimmed || trimmed === '.') return null;
598
- // A scoped name is a name, not a path: `@scope/pkg` stays whole rather than becoming `pkg`. Anything
599
- // else is a path, and the last segment is the one that names the project.
568
+ // A scoped name is a name, not a path: `@scope/pkg` stays whole. Anything else is a path, whose last
569
+ // segment names the project.
600
570
  const scoped = /^@[^\\/]+[\\/][^\\/]+$/.test(trimmed);
601
571
  const base = scoped ? trimmed : trimmed.split(/[\\/]/).filter(Boolean).pop() ?? '';
602
572
  if (!base) return null;
@@ -630,15 +600,11 @@ function sorted(record) {
630
600
  return Object.fromEntries(Object.entries(record).sort(([a], [b])=>a < b ? -1 : 1));
631
601
  }
632
602
  /**
633
- * pnpm's settings for the new app, written only when a feature has something to put in them. `null`
634
- * means there is nothing to say, so no file is written.
603
+ * pnpm's settings for the new app, or `null` when no feature has anything to put in them.
635
604
  *
636
- * It exists for one field, `allowBuilds`. pnpm fails an install and every `pnpm dev` after it —
637
- * until the project has said whether a dependency's install script should run. Nothing rshono itself
638
- * installs has one, so most apps get no file; the packages that do (wrangler's esbuild and workerd)
639
- * declare their answer on the feature that brings them.
640
- *
641
- * A file rather than a `pnpm` key in `package.json`, which pnpm 11 no longer reads.
605
+ * It exists for one field, `allowBuilds`: pnpm fails an install until the project has said whether a
606
+ * dependency's install script should run. Nothing rshono installs has one, so most apps get no file. A file
607
+ * rather than a `pnpm` key in `package.json`, which pnpm 11 no longer reads.
642
608
  */ function buildPnpmSettings(features) {
643
609
  const allowBuilds = {};
644
610
  for (const feature of features)Object.assign(allowBuilds, feature.allowBuilds);
@@ -654,10 +620,8 @@ function sorted(record) {
654
620
  ].join('\n');
655
621
  }
656
622
  /**
657
- * Assembles `package.json` from the answers and whatever the selected features contribute.
658
- *
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.
623
+ * Assembles `package.json` from the answers and whatever the selected features contribute. Dependencies are
624
+ * sorted and scripts keep {@link buildScripts}'s order, so two runs with the same answers are byte-identical.
661
625
  */ function buildPackageJson(answers, features, pm) {
662
626
  const dependencies = {
663
627
  '@rshono/core': RSHONO_RANGE,
@@ -687,8 +651,8 @@ function sorted(record) {
687
651
  dependencies: sorted(dependencies),
688
652
  devDependencies: sorted(devDependencies)
689
653
  };
690
- // Only when the environment told us the exact version: `packageManager` pins the tool for Corepack,
691
- // and a guess at the version is worse than leaving the field out.
654
+ // Only when the environment told us the exact version: this field pins the tool for Corepack, and a guess is
655
+ // worse than leaving it out.
692
656
  if (pm.version) manifest.packageManager = `${pm.name}@${pm.version}`;
693
657
  const ordered = Object.fromEntries(FIELD_ORDER.filter((field)=>field in manifest).map((field)=>[
694
658
  field,
@@ -700,25 +664,22 @@ function sorted(record) {
700
664
  ;// CONCATENATED MODULE: ./src/render.ts
701
665
 
702
666
  /**
703
- * `{{NAME}}`, deliberately not `__NAME__`: templates are real files that real tools run over, and in
704
- * markdown `__NAME__` *is* strong emphasis Prettier rewrites it to `**NAME**` and the token stops
705
- * matching. `{{…}}` means nothing to any format these templates are written in.
667
+ * `{{NAME}}`, not `__NAME__`: in markdown the latter is strong emphasis, so Prettier rewrites it to `**NAME**`
668
+ * and the token stops matching. `{{…}}` means nothing to any format these templates are written in.
706
669
  */ const TOKEN_PATTERN = /\{\{[A-Z][A-Z\d_]*\}\}/g;
707
670
  function tokensFor(answers, features, pm) {
708
671
  return {
709
672
  '{{PROJECT_NAME}}': answers.packageName,
710
673
  '{{DEPLOY_TARGET}}': answers.deploy,
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.
674
+ // From the features rather than the answers, because all three are about the scripts the app actually got.
713
675
  '{{SCRIPT_TABLE}}': scriptTable(answers, features, pm),
714
676
  '{{DEPLOY_STEP}}': deployStep(features, pm),
715
677
  '{{PLATFORM_SETUP}}': features.map((feature)=>feature.platformSetup ?? '').join('')
716
678
  };
717
679
  }
718
680
  /**
719
- * Substitutes tokens, and throws on one it doesn't know — a typo in a template would otherwise ship a
720
- * literal `{{PORJECT_NAME}}` into somebody's new app, which no test of the generator's logic would
721
- * catch.
681
+ * Substitutes tokens, and throws on one it does not know — a typo in a template would otherwise ship a literal
682
+ * `{{PORJECT_NAME}}` into somebody's new app, which no test of the generator's logic would catch.
722
683
  */ function render(contents, tokens, source) {
723
684
  return contents.replace(TOKEN_PATTERN, (token)=>{
724
685
  const value = tokens[token];
@@ -748,11 +709,9 @@ function readTemplateDir(dir) {
748
709
  return files;
749
710
  }
750
711
  /**
751
- * `_gitignore` → `.gitignore`, and so on for every dotfile.
752
- *
753
- * npm strips a literal `.gitignore` out of a published tarball, so a template cannot contain one — it
754
- * would exist in the repo, pass every local test, and be missing from the package everybody installs.
755
- * Applies to the basename only, so `src/lib/_x.ts` is a dotfile but `templates/_x/y.ts` is not.
712
+ * `_gitignore` → `.gitignore`, and so on for every dotfile: npm strips a literal `.gitignore` out of a
713
+ * published tarball, so a template that contained one would pass every local test and be missing from the
714
+ * package everybody installs. The basename only, so `templates/_x/y.ts` is left alone.
756
715
  */ function undotted(path) {
757
716
  const segments = path.split(posix.sep);
758
717
  const name = segments.pop();
@@ -761,19 +720,16 @@ function readTemplateDir(dir) {
761
720
  name.startsWith('_') ? `.${name.slice(1)}` : name
762
721
  ].join(posix.sep);
763
722
  }
764
- /**
765
- * A feature's `.gitignore` lines, appended under a heading naming it — so somebody reading the file six
766
- * months later can tell why `.wrangler/` is in there.
767
- */ function appendGitignore(existing, features) {
723
+ /** A feature's `.gitignore` lines, under a heading naming it — so a reader can tell why `.wrangler/` is there. */ function appendGitignore(existing, features) {
768
724
  const additions = features.filter((feature)=>feature.gitignore?.length);
769
725
  if (additions.length === 0) return existing;
770
726
  const blocks = additions.map((feature)=>`\n# ${feature.id}\n${feature.gitignore.join('\n')}\n`);
771
727
  return existing + blocks.join('');
772
728
  }
773
729
  /**
774
- * Turns answers into the exact set of files to write, without touching the target directory the
775
- * decisions and the I/O are separated so the whole matrix of answers can be asserted on in a test, and
776
- * so `--dry-run` is the same code path minus the last step.
730
+ * Turns answers into the exact set of files to write, without touching the target directory. Separating the
731
+ * decisions from the I/O is what lets a test assert on the whole matrix of answers, and makes `--dry-run` the
732
+ * same code path minus the last step.
777
733
  */ function plan_plan(answers, pm) {
778
734
  const features = selectFeatures(answers, pm);
779
735
  const tokens = tokensFor(answers, features, pm);
@@ -847,11 +803,9 @@ function packageManager(name, version) {
847
803
  };
848
804
  }
849
805
  /**
850
- * Which package manager invoked us. Every one of them sets `npm_config_user_agent` on the process it
851
- * spawns — `pnpm/11.9.0 npm/? node/v22.14.0 darwin arm64` — so `pnx @rshono/create` scaffolds a pnpm
852
- * project without asking, and the exact version comes along for the `packageManager` field.
853
- *
854
- * Falls back to npm, which is also what a bare `node bin/create-rshono.mjs` gets.
806
+ * Which package manager invoked us. All four set `npm_config_user_agent` on the process they spawn — `pnpm/11.9.0
807
+ * npm/? node/v22.14.0 darwin arm64` — so `pnx @rshono/create` scaffolds a pnpm project without asking, exact
808
+ * version included. Falls back to npm, which is also what a bare `node bin/create-rshono.mjs` gets.
855
809
  */ function detectPackageManager(userAgent = process.env.npm_config_user_agent) {
856
810
  const [spec] = (userAgent ?? '').split(' ');
857
811
  const [name, version] = (spec ?? '').split('/');
@@ -859,9 +813,8 @@ function packageManager(name, version) {
859
813
  return packageManager('npm');
860
814
  }
861
815
  /**
862
- * Runs the install, streaming its output. `shell: true` on Windows because npm, pnpm and yarn are all
863
- * `.cmd` shims there, which `spawn` cannot execute directly — and with a shell involved the arguments
864
- * are all fixed strings from the tables above, never anything the user typed.
816
+ * Runs the install, streaming its output. `shell: true` on Windows, where npm, pnpm and yarn are `.cmd` shims
817
+ * `spawn` cannot execute directly — safe because every argument is a fixed string from the tables above.
865
818
  */ function runInstall(pm, cwd) {
866
819
  return run(pm, pm.install, cwd);
867
820
  }
@@ -877,10 +830,7 @@ function run(pm, args, cwd) {
877
830
  ;// CONCATENATED MODULE: ./src/write.ts
878
831
 
879
832
 
880
- /**
881
- * Files that do not make a directory "occupied". A user who ran `git init` or opened the folder in an
882
- * editor before scaffolding has not put anything in it that we would overwrite.
883
- */ const IGNORED_ENTRIES = new Set([
833
+ /** Files that do not make a directory "occupied": a `git init` or an editor has put nothing there to overwrite. */ const IGNORED_ENTRIES = new Set([
884
834
  '.git',
885
835
  '.DS_Store',
886
836
  '.idea',
@@ -888,12 +838,9 @@ function run(pm, args, cwd) {
888
838
  'Thumbs.db'
889
839
  ]);
890
840
  /**
891
- * What is already at the target path, ignoring the entries a fresh clone or an editor leaves behind —
892
- * which is what decides whether scaffolding into it is safe.
893
- *
894
- * A path that does not exist yet is no conflict. A path that exists and is *not* a directory throws
895
- * rather than reporting an empty list, since `--force` should not write into one either — otherwise
896
- * `create-rshono README.md` gets as far as `mkdir` before failing on a raw ENOTDIR.
841
+ * What is already at the target path, ignoring the entries a fresh clone or an editor leaves behind — which is
842
+ * what decides whether scaffolding into it is safe. A path that does not exist yet is no conflict; one that
843
+ * exists and is not a directory throws, since `--force` should not write into it either.
897
844
  */ function conflictingEntries(dir) {
898
845
  const stats = statSync(dir, {
899
846
  throwIfNoEntry: false
@@ -903,8 +850,8 @@ function run(pm, args, cwd) {
903
850
  return readdirSync(dir).filter((entry)=>!IGNORED_ENTRIES.has(entry));
904
851
  }
905
852
  /**
906
- * Writes the plan. Directories are created as needed, and files are written with the plan's own
907
- * ordering so a failure part-way through leaves something a person can make sense of.
853
+ * Writes the plan, creating directories as needed and keeping the plan's own ordering — so a failure part-way
854
+ * through leaves something a person can make sense of.
908
855
  */ function writePlan(plan, targetDir) {
909
856
  mkdirSync(targetDir, {
910
857
  recursive: true
package/dist/cli.mjs CHANGED
@@ -2906,8 +2906,8 @@ function hasGit(cwd) {
2906
2906
  ], cwd).ok;
2907
2907
  }
2908
2908
  /**
2909
- * Whether the new project already sits inside somebody's repository — scaffolding into a monorepo or an
2910
- * existing checkout, where a nested repo would be a surprise rather than a convenience.
2909
+ * Whether the new project already sits inside somebody's repository — a monorepo or an existing checkout, where
2910
+ * a nested repo would be a surprise rather than a convenience.
2911
2911
  */ function isInsideRepo(cwd) {
2912
2912
  const { ok, stdout } = git_git([
2913
2913
  'rev-parse',
@@ -2916,11 +2916,9 @@ function hasGit(cwd) {
2916
2916
  return ok && stdout === 'true';
2917
2917
  }
2918
2918
  /**
2919
- * Initializes a repository and makes the first commit.
2920
- *
2921
- * `initialized` rather than `committed` is the honest answer when the commit itself fails, which it does
2922
- * on a machine with no `user.email` configured. That is not a reason to fail the scaffold — the files
2923
- * are all there and staged — so the caller reports it and moves on.
2919
+ * Initializes a repository and makes the first commit. `initialized` is the honest answer when the commit itself
2920
+ * fails, as it does with no `user.email` configured — the files are all there and staged, so the caller reports
2921
+ * it and moves on rather than failing the scaffold.
2924
2922
  */ function initRepo(cwd) {
2925
2923
  const init = git_git([
2926
2924
  'init',
@@ -2943,15 +2941,14 @@ function hasGit(cwd) {
2943
2941
 
2944
2942
  ;// CONCATENATED MODULE: ./src/generated/framework.ts
2945
2943
  // 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.11';
2944
+ /** The framework release a scaffolded app is pinned to: this package and rshono ship together. */ const RSHONO_VERSION = '1.0.0-rc.13';
2947
2945
  /**
2948
- * The Node range rshono itself declares, restated in every scaffolded app's `engines` — so a CI image
2949
- * or a contributor on an older Node hears it from their package manager rather than from a stack trace.
2946
+ * The Node range rshono declares, restated in every scaffolded app's `engines` — so a CI image or a contributor
2947
+ * on an older Node hears it from their package manager rather than from a stack trace.
2950
2948
  */ const NODE_ENGINE = '>=22.18.0';
2951
2949
  /**
2952
- * The dependency versions rshono is tested against, copied from its own manifest. Exact where it is
2953
- * exact — React's RSC internals are coupled across builds, and a generated app has no workspace
2954
- * overrides to fall back on.
2950
+ * The dependency versions rshono is tested against, copied from its own manifest. Exact where it is exact:
2951
+ * React's RSC internals are coupled across builds, and a generated app has no workspace overrides.
2955
2952
  */ const FRAMEWORK_DEPS = {
2956
2953
  hono: '^4.12.31',
2957
2954
  react: '19.2.8',
@@ -2982,9 +2979,8 @@ function hasGit(cwd) {
2982
2979
  ;// CONCATENATED MODULE: ./src/options.ts
2983
2980
 
2984
2981
  /*
2985
- * The names each option accepts, spelled once. The types below are derived from them, the CLI validates
2986
- * its flags against them and prints them in `--help`, and `pm.ts` recognises a package manager by them
2987
- * — so a name added here reaches all three without a second list to remember.
2982
+ * The names each option accepts, spelled once: the types below are derived from them, the CLI validates its
2983
+ * flags against them and prints them in `--help`, and `pm.ts` recognises a package manager by them.
2988
2984
  */ const FORMATTER_NAMES = [
2989
2985
  'prettier',
2990
2986
  'biome',
@@ -3045,14 +3041,13 @@ const QUALITY_PRESETS = [
3045
3041
  }
3046
3042
  ];
3047
3043
  /**
3048
- * Turns whatever the user typed into a name npm will accept, or returns `null` when nothing usable is
3049
- * left. Lowercasing and replacing runs of invalid characters covers the ordinary cases (`My App`,
3050
- * `my_app`); a scoped name is kept intact, since `@scope/name` is legal.
3044
+ * Turns whatever the user typed into a name npm will accept, or `null` when nothing usable is left.
3045
+ * Lowercasing and replacing runs of invalid characters covers the ordinary cases (`My App`, `my_app`).
3051
3046
  */ function toPackageName(input) {
3052
3047
  const trimmed = input.trim().replace(/^\.\/+/, '').replace(/\/+$/, '');
3053
3048
  if (!trimmed || trimmed === '.') return null;
3054
- // A scoped name is a name, not a path: `@scope/pkg` stays whole rather than becoming `pkg`. Anything
3055
- // else is a path, and the last segment is the one that names the project.
3049
+ // A scoped name is a name, not a path: `@scope/pkg` stays whole. Anything else is a path, whose last
3050
+ // segment names the project.
3056
3051
  const scoped = /^@[^\\/]+[\\/][^\\/]+$/.test(trimmed);
3057
3052
  const base = scoped ? trimmed : trimmed.split(/[\\/]/).filter(Boolean).pop() ?? '';
3058
3053
  if (!base) return null;
@@ -3066,12 +3061,12 @@ const QUALITY_PRESETS = [
3066
3061
 
3067
3062
  ;// CONCATENATED MODULE: ./src/features/combinations.ts
3068
3063
  /**
3069
- * Biome's CSS parser rejects Tailwind's syntax — `@apply` and a `@layer` block are parse errors, not
3070
- * unknown-at-rule warnings — so a project with both needs Biome pointed away from stylesheets. The
3071
- * overlay is a second `biome.json` listed after the first, which is all "later overlay wins" means.
3064
+ * Biome's CSS parser rejects Tailwind's syntax — `@apply` is a parse error, not an unknown-at-rule warning — so
3065
+ * a project with both needs Biome pointed away from stylesheets. The overlay is a second `biome.json` listed
3066
+ * after the first.
3072
3067
  *
3073
- * Narrow on purpose: the alternative was excluding CSS for everybody, which would quietly give up
3074
- * formatting the plain-CSS template that Biome handles perfectly well.
3068
+ * Narrow on purpose: excluding CSS for everybody would give up formatting the plain-CSS template that Biome
3069
+ * handles perfectly well.
3075
3070
  */ const BIOME_TAILWIND = {
3076
3071
  id: 'biome-tailwind',
3077
3072
  overlays: [
@@ -3097,14 +3092,12 @@ const QUALITY_PRESETS = [
3097
3092
  /**
3098
3093
  * The scripts every app gets, whatever it targets.
3099
3094
  *
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:
3095
+ * `start`, `preview` and `deploy` are not here: each means something different per platform, so the deploy
3096
+ * target contributes its own. The three names are a contract the targets keep, which is what lets the README
3097
+ * describe an app it was not written for:
3103
3098
  *
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.
3099
+ * - **`start`** runs a build that already exists and never makes one — what a host's own start command calls.
3100
+ * - **`preview`** builds, then runs the result here, so the production build is answerable without deploying.
3108
3101
  * - **`deploy`** builds, then ships it — where the platform has one command that does the shipping.
3109
3102
  */ const BASE_SCRIPTS = {
3110
3103
  dev: 'rshono dev',
@@ -3112,9 +3105,8 @@ const QUALITY_PRESETS = [
3112
3105
  typecheck: 'tsc --noEmit'
3113
3106
  };
3114
3107
  /**
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.
3108
+ * Every script the app gets, in the order they are written: the base ones, then each feature's. The manifest and
3109
+ * the README's command table are both this, so neither can document a script the other lacks.
3118
3110
  */ function buildScripts(features) {
3119
3111
  const scripts = {
3120
3112
  ...BASE_SCRIPTS
@@ -3130,9 +3122,8 @@ const QUALITY_PRESETS = [
3130
3122
  };
3131
3123
  }
3132
3124
  /**
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.
3125
+ * Script names a package manager has a command of its own for: `pnpm deploy` runs pnpm's workspace-deploy and
3126
+ * never looks at the manifest. These get the explicit `run` form, which all four managers accept.
3136
3127
  */ const SHADOWED = new Set([
3137
3128
  'deploy'
3138
3129
  ]);
@@ -3140,10 +3131,9 @@ const QUALITY_PRESETS = [
3140
3131
  return `${SHADOWED.has(script) ? `${pm.name} run` : pm.run} ${script}`;
3141
3132
  }
3142
3133
  /**
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.
3134
+ * The README's command table: one line per script, with the command to type and a one-line gloss. A script whose
3135
+ * feature supplies no `scriptHelp` is left out — that is how `format:check` stays out of a table about running
3136
+ * the app and covered by the line pointing at `package.json`.
3147
3137
  */ function scriptTable(answers, features, pm) {
3148
3138
  const help = baseScriptHelp(answers.deploy);
3149
3139
  for (const feature of features)Object.assign(help, feature.scriptHelp);
@@ -3152,16 +3142,14 @@ const QUALITY_PRESETS = [
3152
3142
  return documented.map((name)=>`${invoke(pm, name).padEnd(width)} # ${help[name]}`).join('\n');
3153
3143
  }
3154
3144
  /**
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.
3145
+ * The command that gets this app into production, as a sentence — the README's deploy step and the closing
3146
+ * summary's, so the two cannot name different commands.
3157
3147
  *
3158
3148
  * 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
3149
  */ function deployStep(features, pm) {
3161
3150
  const scripts = buildScripts(features);
3162
3151
  if (scripts.deploy) return `\`${invoke(pm, 'deploy')}\` does the build and the upload in one step.`;
3163
3152
  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
3153
  if (scripts.preview) return `\`${invoke(pm, 'preview')}\` runs the build here, so you can check it first.`;
3166
3154
  return `\`${invoke(pm, 'build')}\` produces it; getting it there is yours to script.`;
3167
3155
  }
@@ -3171,14 +3159,12 @@ const QUALITY_PRESETS = [
3171
3159
  /** Passed straight through, so everything generated from the framework reaches the rest of the package here. */
3172
3160
  /** The framework range a scaffolded app gets. The two packages are released together, so this is ours. */ const RSHONO_RANGE = `^${RSHONO_VERSION}`;
3173
3161
  /**
3174
- * Versions for the optional tooling the features can add — the one place in this package where a
3175
- * dependency range is typed out by hand, because none of these are things the framework itself
3176
- * declares. Everything a *scaffolded app* needs to run rshono comes from {@link FRAMEWORK_DEPS},
3177
- * which is generated from rshono's own manifest and must not be edited here.
3162
+ * Versions for the optional tooling the features can add — the one place in this package where a range is typed
3163
+ * by hand, because the framework declares none of these. Everything a scaffolded app needs to *run* rshono comes
3164
+ * from {@link FRAMEWORK_DEPS}, generated from rshono's own manifest.
3178
3165
  *
3179
- * Ranges are caret, not exact: these are the app's own dev tools, and a scaffold made six months from
3180
- * now should pick up their patch releases rather than pinning whatever was current the day this file
3181
- * was last touched.
3166
+ * Caret ranges, not exact: these are the app's own dev tools, and a scaffold made six months from now should
3167
+ * pick up their patch releases.
3182
3168
  */ const TOOL_VERSIONS = {
3183
3169
  tailwindcss: '^4.3.3',
3184
3170
  '@tailwindcss/postcss': '^4.3.3',
@@ -3190,23 +3176,20 @@ const QUALITY_PRESETS = [
3190
3176
  oxlint: '^1.76.0',
3191
3177
  oxfmt: '^0.61.0',
3192
3178
  eslint: '^10.8.0',
3193
- // ESLint's own recommended JavaScript rules, which typescript-eslint layers on top of rather than
3194
- // replaces, and the rules of hooks — the one class of React mistake no type checker sees.
3179
+ // ESLint's recommended JavaScript rules, which typescript-eslint layers on rather than replaces, and the rules
3180
+ // of hooks — the one class of React mistake no type checker sees.
3195
3181
  '@eslint/js': '^10.0.1',
3196
3182
  'typescript-eslint': '^8.65.0',
3197
3183
  'eslint-plugin-react-hooks': '^7.1.1',
3198
3184
  wrangler: '^4.115.0'
3199
3185
  };
3200
3186
  /**
3201
- * The TypeScript an ESLint app pins, in place of the framework's own — the one deliberate exception to
3187
+ * The TypeScript an ESLint app pins in place of the framework's own — the one deliberate exception to
3202
3188
  * {@link FRAMEWORK_DEPS}.
3203
3189
  *
3204
- * typescript-eslint reads TypeScript's compiler API directly rather than through a stable interface,
3205
- * so it accepts `typescript >=4.8.4 <6.1.0` and nothing above; `~6.0.3` is the newest that satisfies
3206
- * it. rshono's declarations compile the same under either, which is what makes this an app's business
3207
- * and not the framework's.
3208
- *
3209
- * When upstream widens the range, this constant and the ESLint feature's use of it are what to delete.
3190
+ * typescript-eslint reads TypeScript's compiler API directly, so it accepts `>=4.8.4 <6.1.0` and nothing above;
3191
+ * `~6.0.3` is the newest that satisfies it. rshono's declarations compile the same under either, which is what
3192
+ * makes this the app's business. When upstream widens its range, this constant is what to delete.
3210
3193
  */ const ESLINT_TYPESCRIPT = '~6.0.3';
3211
3194
 
3212
3195
  ;// CONCATENATED MODULE: ./src/features/deploy.ts
@@ -3215,22 +3198,17 @@ const QUALITY_PRESETS = [
3215
3198
  /**
3216
3199
  * What a deploy target adds beyond the `deploy` line in `rshono.config.ts`, which the template carries.
3217
3200
  *
3218
- * Deliberately thin: the framework arranges its own output for every platform, and `rshono build`
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.
3201
+ * Thin on purpose: the framework arranges its own output for every platform, and `rshono build` writes the one
3202
+ * platform config that has to exist. So a target contributes the commands that run and ship the build, the CLI
3203
+ * they need, the directories to gitignore, and a note for the step no command covers.
3222
3204
  *
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}.
3205
+ * What the three script names promise is documented above `BASE_SCRIPTS` in `scripts.ts`. Only `node` has a
3206
+ * `start` `rshono start` refuses a bundle built for anywhere else and it needs no `preview`, since `build`
3207
+ * then `start` already is one.
3229
3208
  */ function deployFeatures(pm) {
3230
3209
  const build = invoke(pm, 'build');
3231
3210
  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.
3211
+ // Where a Node build goes next is a Dockerfile or a process manager, neither of which this can guess.
3234
3212
  node: {
3235
3213
  id: 'deploy-node',
3236
3214
  scripts: {
@@ -3253,16 +3231,14 @@ const QUALITY_PRESETS = [
3253
3231
  devDependencies: {
3254
3232
  wrangler: TOOL_VERSIONS.wrangler
3255
3233
  },
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.
3234
+ // The only two install scripts a scaffolded app can end up with, and wrangler brings both. Each merely
3235
+ // picks the platform binary out of the optional dependency already carrying it, so neither needs to run.
3259
3236
  allowBuilds: {
3260
3237
  esbuild: false,
3261
3238
  workerd: false
3262
3239
  },
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.
3240
+ // `wrangler dev` is the one preview that runs the code in workerd rather than Node. Both scripts read the
3241
+ // wrangler.jsonc the build wrote, so nothing here has to know where the bundle went.
3266
3242
  scripts: {
3267
3243
  preview: 'rshono build && wrangler dev',
3268
3244
  deploy: 'rshono build && wrangler deploy'
@@ -3284,9 +3260,8 @@ const QUALITY_PRESETS = [
3284
3260
  },
3285
3261
  vercel: {
3286
3262
  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.
3263
+ // `--prod` because the script is called `deploy`: without it the CLI uploads to a throwaway preview URL.
3264
+ // `preview` is a Node build run here the platform cannot run its own prebuilt output on your machine.
3290
3265
  scripts: {
3291
3266
  preview: 'rshono build --deploy node && rshono start',
3292
3267
  deploy: `rshono build && ${pm.dlx} vercel deploy --prebuilt --prod`
@@ -3312,8 +3287,7 @@ const QUALITY_PRESETS = [
3312
3287
  },
3313
3288
  'aws-lambda': {
3314
3289
  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.
3290
+ // No CLI to wrap and no upload to guess at, but `preview` still applies: the bundle is a Node handler.
3317
3291
  scripts: {
3318
3292
  preview: 'rshono build --deploy node && rshono start'
3319
3293
  },
@@ -3338,14 +3312,11 @@ function deployFeature(target, pm) {
3338
3312
  ;// CONCATENATED MODULE: ./src/features/quality.ts
3339
3313
 
3340
3314
  /**
3341
- * The formatter and linter features. Biome answers to both slots and appears once `selectFeatures`
3342
- * deduplicates by `id`, so `formatter: 'biome', linter: 'biome'` contributes one set of files, one
3343
- * dependency and one pair of scripts.
3315
+ * The formatter and linter features. Biome answers to both slots and appears once, because `selectFeatures`
3316
+ * deduplicates by `id`.
3344
3317
  *
3345
- * A formatter brings `format:check` beside `format`, because the writing half and the CI half want
3346
- * different exit-code behaviour: `format` rewrites files, `format:check` fails instead. A linter brings
3347
- * `lint:fix` beside `lint`, for the same reason in the other direction — `lint` is already the failing
3348
- * one. Biome adds a `check` of its own, which is the pair of them in a single pass.
3318
+ * Each brings a pair of scripts, since the writing half and the CI half want different exit codes: `format`
3319
+ * rewrites and `format:check` fails, `lint:fix` rewrites and `lint` fails. Biome's `check` is both in one pass.
3349
3320
  */ const PRETTIER = {
3350
3321
  id: 'prettier',
3351
3322
  overlays: [
@@ -3386,11 +3357,10 @@ const OXLINT = {
3386
3357
  }
3387
3358
  };
3388
3359
  /**
3389
- * The one feature that changes a dependency the framework otherwise decides: typescript-eslint cannot
3390
- * be installed alongside the TypeScript rshono is tested against, so an ESLint app pins the newest one
3391
- * its peer range accepts (see {@link ESLINT_TYPESCRIPT}). Its rules are type-aware — the reason to
3392
- * reach for ESLint over a syntax-only linter — so the config it ships hands the parser the whole
3393
- * program rather than linting file by file.
3360
+ * The one feature that changes a dependency the framework otherwise decides: typescript-eslint cannot be
3361
+ * installed alongside the TypeScript rshono is tested against, so an ESLint app pins the newest its peer range
3362
+ * accepts (see {@link ESLINT_TYPESCRIPT}). Its rules are type-aware — the reason to reach for ESLint at all —
3363
+ * so the config it ships hands the parser the whole program.
3394
3364
  */ const ESLINT = {
3395
3365
  id: 'eslint',
3396
3366
  overlays: [
@@ -3446,13 +3416,12 @@ function linterFeature(linter) {
3446
3416
  ;// CONCATENATED MODULE: ./src/features/styling.ts
3447
3417
 
3448
3418
  /**
3449
- * Tailwind is a PostCSS plugin and nothing more, which is the whole of this feature: four packages,
3450
- * plus an overlay carrying `postcss.config.mjs`, an `rshono.config.ts` whose `rspack` hook puts
3451
- * postcss-loader in front of the CSS parser, a Tailwind entry stylesheet, and the two views rewritten
3452
- * in utilities.
3419
+ * Tailwind is a PostCSS plugin and nothing more, which is the whole of this feature: four packages, plus an
3420
+ * overlay carrying `postcss.config.mjs`, an `rshono.config.ts` whose `rspack` hook puts postcss-loader in front
3421
+ * of the CSS parser, a Tailwind entry stylesheet, and the two views rewritten in utilities.
3453
3422
  *
3454
- * `postcss` and `postcss-loader` are the app's dependencies rather than the framework's rshono
3455
- * compiles CSS natively, so an app that does not want a plugin chain does not install one.
3423
+ * `postcss` is the app's dependency rather than the framework's: rshono compiles CSS natively, so an app that
3424
+ * wants no plugin chain installs none.
3456
3425
  */ const TAILWIND = {
3457
3426
  id: 'tailwind',
3458
3427
  overlays: [
@@ -3475,12 +3444,11 @@ function stylingFeature(styling) {
3475
3444
 
3476
3445
 
3477
3446
  /**
3478
- * The features a set of answers selects, in application order — so an overlay listed later wins a file
3479
- * both of them ship. Deduplicated by `id`, which is what lets one feature answer two questions (Biome
3480
- * is both the formatter and the linter) without contributing twice.
3447
+ * The features a set of answers selects, in application order — so an overlay listed later wins a file both
3448
+ * ship. Deduplicated by `id`, which is what lets one feature answer two questions (Biome is both formatter and
3449
+ * linter) without contributing twice.
3481
3450
  *
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.
3451
+ * `pm` reaches the deploy target because one script has to name the runner that fetches an uninstalled CLI.
3484
3452
  */ function selectFeatures(answers, pm) {
3485
3453
  const selected = [
3486
3454
  deployFeature(answers.deploy, pm),
@@ -3517,15 +3485,11 @@ function sorted(record) {
3517
3485
  return Object.fromEntries(Object.entries(record).sort(([a], [b])=>a < b ? -1 : 1));
3518
3486
  }
3519
3487
  /**
3520
- * pnpm's settings for the new app, written only when a feature has something to put in them. `null`
3521
- * means there is nothing to say, so no file is written.
3522
- *
3523
- * It exists for one field, `allowBuilds`. pnpm fails an install — and every `pnpm dev` after it —
3524
- * until the project has said whether a dependency's install script should run. Nothing rshono itself
3525
- * installs has one, so most apps get no file; the packages that do (wrangler's esbuild and workerd)
3526
- * declare their answer on the feature that brings them.
3488
+ * pnpm's settings for the new app, or `null` when no feature has anything to put in them.
3527
3489
  *
3528
- * A file rather than a `pnpm` key in `package.json`, which pnpm 11 no longer reads.
3490
+ * It exists for one field, `allowBuilds`: pnpm fails an install until the project has said whether a
3491
+ * dependency's install script should run. Nothing rshono installs has one, so most apps get no file. A file
3492
+ * rather than a `pnpm` key in `package.json`, which pnpm 11 no longer reads.
3529
3493
  */ function buildPnpmSettings(features) {
3530
3494
  const allowBuilds = {};
3531
3495
  for (const feature of features)Object.assign(allowBuilds, feature.allowBuilds);
@@ -3541,10 +3505,8 @@ function sorted(record) {
3541
3505
  ].join('\n');
3542
3506
  }
3543
3507
  /**
3544
- * Assembles `package.json` from the answers and whatever the selected features contribute.
3545
- *
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.
3508
+ * Assembles `package.json` from the answers and whatever the selected features contribute. Dependencies are
3509
+ * sorted and scripts keep {@link buildScripts}'s order, so two runs with the same answers are byte-identical.
3548
3510
  */ function buildPackageJson(answers, features, pm) {
3549
3511
  const dependencies = {
3550
3512
  '@rshono/core': RSHONO_RANGE,
@@ -3574,8 +3536,8 @@ function sorted(record) {
3574
3536
  dependencies: sorted(dependencies),
3575
3537
  devDependencies: sorted(devDependencies)
3576
3538
  };
3577
- // Only when the environment told us the exact version: `packageManager` pins the tool for Corepack,
3578
- // and a guess at the version is worse than leaving the field out.
3539
+ // Only when the environment told us the exact version: this field pins the tool for Corepack, and a guess is
3540
+ // worse than leaving it out.
3579
3541
  if (pm.version) manifest.packageManager = `${pm.name}@${pm.version}`;
3580
3542
  const ordered = Object.fromEntries(FIELD_ORDER.filter((field)=>field in manifest).map((field)=>[
3581
3543
  field,
@@ -3587,25 +3549,22 @@ function sorted(record) {
3587
3549
  ;// CONCATENATED MODULE: ./src/render.ts
3588
3550
 
3589
3551
  /**
3590
- * `{{NAME}}`, deliberately not `__NAME__`: templates are real files that real tools run over, and in
3591
- * markdown `__NAME__` *is* strong emphasis Prettier rewrites it to `**NAME**` and the token stops
3592
- * matching. `{{…}}` means nothing to any format these templates are written in.
3552
+ * `{{NAME}}`, not `__NAME__`: in markdown the latter is strong emphasis, so Prettier rewrites it to `**NAME**`
3553
+ * and the token stops matching. `{{…}}` means nothing to any format these templates are written in.
3593
3554
  */ const TOKEN_PATTERN = /\{\{[A-Z][A-Z\d_]*\}\}/g;
3594
3555
  function tokensFor(answers, features, pm) {
3595
3556
  return {
3596
3557
  '{{PROJECT_NAME}}': answers.packageName,
3597
3558
  '{{DEPLOY_TARGET}}': answers.deploy,
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.
3559
+ // From the features rather than the answers, because all three are about the scripts the app actually got.
3600
3560
  '{{SCRIPT_TABLE}}': scriptTable(answers, features, pm),
3601
3561
  '{{DEPLOY_STEP}}': deployStep(features, pm),
3602
3562
  '{{PLATFORM_SETUP}}': features.map((feature)=>feature.platformSetup ?? '').join('')
3603
3563
  };
3604
3564
  }
3605
3565
  /**
3606
- * Substitutes tokens, and throws on one it doesn't know — a typo in a template would otherwise ship a
3607
- * literal `{{PORJECT_NAME}}` into somebody's new app, which no test of the generator's logic would
3608
- * catch.
3566
+ * Substitutes tokens, and throws on one it does not know — a typo in a template would otherwise ship a literal
3567
+ * `{{PORJECT_NAME}}` into somebody's new app, which no test of the generator's logic would catch.
3609
3568
  */ function render(contents, tokens, source) {
3610
3569
  return contents.replace(TOKEN_PATTERN, (token)=>{
3611
3570
  const value = tokens[token];
@@ -3635,11 +3594,9 @@ function readTemplateDir(dir) {
3635
3594
  return files;
3636
3595
  }
3637
3596
  /**
3638
- * `_gitignore` → `.gitignore`, and so on for every dotfile.
3639
- *
3640
- * npm strips a literal `.gitignore` out of a published tarball, so a template cannot contain one — it
3641
- * would exist in the repo, pass every local test, and be missing from the package everybody installs.
3642
- * Applies to the basename only, so `src/lib/_x.ts` is a dotfile but `templates/_x/y.ts` is not.
3597
+ * `_gitignore` → `.gitignore`, and so on for every dotfile: npm strips a literal `.gitignore` out of a
3598
+ * published tarball, so a template that contained one would pass every local test and be missing from the
3599
+ * package everybody installs. The basename only, so `templates/_x/y.ts` is left alone.
3643
3600
  */ function undotted(path) {
3644
3601
  const segments = path.split(posix.sep);
3645
3602
  const name = segments.pop();
@@ -3648,19 +3605,16 @@ function readTemplateDir(dir) {
3648
3605
  name.startsWith('_') ? `.${name.slice(1)}` : name
3649
3606
  ].join(posix.sep);
3650
3607
  }
3651
- /**
3652
- * A feature's `.gitignore` lines, appended under a heading naming it — so somebody reading the file six
3653
- * months later can tell why `.wrangler/` is in there.
3654
- */ function appendGitignore(existing, features) {
3608
+ /** A feature's `.gitignore` lines, under a heading naming it — so a reader can tell why `.wrangler/` is there. */ function appendGitignore(existing, features) {
3655
3609
  const additions = features.filter((feature)=>feature.gitignore?.length);
3656
3610
  if (additions.length === 0) return existing;
3657
3611
  const blocks = additions.map((feature)=>`\n# ${feature.id}\n${feature.gitignore.join('\n')}\n`);
3658
3612
  return existing + blocks.join('');
3659
3613
  }
3660
3614
  /**
3661
- * Turns answers into the exact set of files to write, without touching the target directory the
3662
- * decisions and the I/O are separated so the whole matrix of answers can be asserted on in a test, and
3663
- * so `--dry-run` is the same code path minus the last step.
3615
+ * Turns answers into the exact set of files to write, without touching the target directory. Separating the
3616
+ * decisions from the I/O is what lets a test assert on the whole matrix of answers, and makes `--dry-run` the
3617
+ * same code path minus the last step.
3664
3618
  */ function plan_plan(answers, pm) {
3665
3619
  const features = selectFeatures(answers, pm);
3666
3620
  const tokens = tokensFor(answers, features, pm);
@@ -3732,11 +3686,9 @@ function packageManager(name, version) {
3732
3686
  };
3733
3687
  }
3734
3688
  /**
3735
- * Which package manager invoked us. Every one of them sets `npm_config_user_agent` on the process it
3736
- * spawns — `pnpm/11.9.0 npm/? node/v22.14.0 darwin arm64` — so `pnx @rshono/create` scaffolds a pnpm
3737
- * project without asking, and the exact version comes along for the `packageManager` field.
3738
- *
3739
- * Falls back to npm, which is also what a bare `node bin/create-rshono.mjs` gets.
3689
+ * Which package manager invoked us. All four set `npm_config_user_agent` on the process they spawn — `pnpm/11.9.0
3690
+ * npm/? node/v22.14.0 darwin arm64` — so `pnx @rshono/create` scaffolds a pnpm project without asking, exact
3691
+ * version included. Falls back to npm, which is also what a bare `node bin/create-rshono.mjs` gets.
3740
3692
  */ function detectPackageManager(userAgent = process.env.npm_config_user_agent) {
3741
3693
  const [spec] = (userAgent ?? '').split(' ');
3742
3694
  const [name, version] = (spec ?? '').split('/');
@@ -3744,9 +3696,8 @@ function packageManager(name, version) {
3744
3696
  return packageManager('npm');
3745
3697
  }
3746
3698
  /**
3747
- * Runs the install, streaming its output. `shell: true` on Windows because npm, pnpm and yarn are all
3748
- * `.cmd` shims there, which `spawn` cannot execute directly — and with a shell involved the arguments
3749
- * are all fixed strings from the tables above, never anything the user typed.
3699
+ * Runs the install, streaming its output. `shell: true` on Windows, where npm, pnpm and yarn are `.cmd` shims
3700
+ * `spawn` cannot execute directly — safe because every argument is a fixed string from the tables above.
3750
3701
  */ function runInstall(pm, cwd) {
3751
3702
  return run(pm, pm.install, cwd);
3752
3703
  }
@@ -3764,9 +3715,8 @@ function run(pm, args, cwd) {
3764
3715
 
3765
3716
 
3766
3717
  /**
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.
3718
+ * The two halves of the closing line, in the words of the app's own scripts: what produces something shippable,
3719
+ * and where it goes from there. A target with no command to give falls back to the framework's hint.
3770
3720
  */ function productionSteps(answers, plan, pm) {
3771
3721
  const scripts = buildScripts(plan.features);
3772
3722
  const check = scripts.preview ? `${invoke(pm, 'preview')} to run the production build` : `${pm.run} build`;
@@ -3774,8 +3724,8 @@ function run(pm, args, cwd) {
3774
3724
  return `${check}, and ${ship}`;
3775
3725
  }
3776
3726
  /**
3777
- * What to do next, in the order to do it — the last thing the user reads, and for most people the only
3778
- * documentation they will read today. `installed` decides whether the install step is still theirs.
3727
+ * What to do next, in the order to do it — the last thing the user reads. `installed` decides whether the install
3728
+ * step is still theirs.
3779
3729
  */ function nextSteps(answers, plan, pm, options) {
3780
3730
  const steps = [];
3781
3731
  if (options.directory !== '.') steps.push(`cd ${options.directory}`);
@@ -3810,10 +3760,7 @@ function run(pm, args, cwd) {
3810
3760
  ;// CONCATENATED MODULE: ./src/write.ts
3811
3761
 
3812
3762
 
3813
- /**
3814
- * Files that do not make a directory "occupied". A user who ran `git init` or opened the folder in an
3815
- * editor before scaffolding has not put anything in it that we would overwrite.
3816
- */ const IGNORED_ENTRIES = new Set([
3763
+ /** Files that do not make a directory "occupied": a `git init` or an editor has put nothing there to overwrite. */ const IGNORED_ENTRIES = new Set([
3817
3764
  '.git',
3818
3765
  '.DS_Store',
3819
3766
  '.idea',
@@ -3821,12 +3768,9 @@ function run(pm, args, cwd) {
3821
3768
  'Thumbs.db'
3822
3769
  ]);
3823
3770
  /**
3824
- * What is already at the target path, ignoring the entries a fresh clone or an editor leaves behind —
3825
- * which is what decides whether scaffolding into it is safe.
3826
- *
3827
- * A path that does not exist yet is no conflict. A path that exists and is *not* a directory throws
3828
- * rather than reporting an empty list, since `--force` should not write into one either — otherwise
3829
- * `create-rshono README.md` gets as far as `mkdir` before failing on a raw ENOTDIR.
3771
+ * What is already at the target path, ignoring the entries a fresh clone or an editor leaves behind — which is
3772
+ * what decides whether scaffolding into it is safe. A path that does not exist yet is no conflict; one that
3773
+ * exists and is not a directory throws, since `--force` should not write into it either.
3830
3774
  */ function conflictingEntries(dir) {
3831
3775
  const stats = statSync(dir, {
3832
3776
  throwIfNoEntry: false
@@ -3836,8 +3780,8 @@ function run(pm, args, cwd) {
3836
3780
  return external_node_fs_readdirSync(dir).filter((entry)=>!IGNORED_ENTRIES.has(entry));
3837
3781
  }
3838
3782
  /**
3839
- * Writes the plan. Directories are created as needed, and files are written with the plan's own
3840
- * ordering so a failure part-way through leaves something a person can make sense of.
3783
+ * Writes the plan, creating directories as needed and keeping the plan's own ordering — so a failure part-way
3784
+ * through leaves something a person can make sense of.
3841
3785
  */ function writePlan(plan, targetDir) {
3842
3786
  mkdirSync(targetDir, {
3843
3787
  recursive: true
@@ -3969,10 +3913,9 @@ function fail(message) {
3969
3913
  async function main() {
3970
3914
  const { values, positionals } = parse();
3971
3915
  if (values.help) return console.log(HELP);
3972
- if (values.version) return console.log("1.0.0-rc.11");
3973
- // A pipe, a CI job or an agent gets the defaults rather than a prompt nothing can answer. Both
3974
- // streams have to be a terminal: the prompts draw on stdout but *read from stdin*, so
3975
- // `echo | npx @rshono/create` would otherwise ask a question with nothing behind the keyboard.
3916
+ if (values.version) return console.log("1.0.0-rc.13");
3917
+ // A pipe, a CI job or an agent gets the defaults rather than a prompt nothing can answer. Both streams have to
3918
+ // be a terminal: the prompts draw on stdout but read from stdin.
3976
3919
  const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY) && !values.yes;
3977
3920
  const pmFlag = oneOf(values.pm, PACKAGE_MANAGERS, 'pm');
3978
3921
  const pm = pmFlag ? packageManager(pmFlag) : detectPackageManager();
@@ -3983,8 +3926,7 @@ async function main() {
3983
3926
  const tailwindFlag = tristate(values.tailwind, values['no-tailwind'], 'tailwind');
3984
3927
  const installFlag = tristate(values.install, values['no-install'], 'install');
3985
3928
  const gitFlag = tristate(values.git, values['no-git'], 'git');
3986
- // The framework version, not this package's: it is the one the app will be pinned to, and the one
3987
- // worth reading here. `--version` reports create-rshono's own.
3929
+ // The framework version, not this package's: it is the one the app gets pinned to. `--version` reports ours.
3988
3930
  intro(`create-rshono · rshono ${RSHONO_VERSION}`);
3989
3931
  // ── Where ───────────────────────────────────────────────────────────────────────────────────────
3990
3932
  let directory = positionals[0];
@@ -4044,8 +3986,8 @@ async function main() {
4044
3986
  ]
4045
3987
  }));
4046
3988
  }
4047
- // One question instead of two. The axes stay independent underneath: a `--formatter` or `--linter`
4048
- // flag addresses either on its own, and skips the question entirely.
3989
+ // One question instead of two; the axes stay independent underneath, and a `--formatter` or `--linter` flag
3990
+ // addresses either on its own.
4049
3991
  let preset = QUALITY_PRESETS.find((candidate)=>candidate.id === qualityFlag);
4050
3992
  if (!preset && !formatterFlag && !linterFlag) {
4051
3993
  const fallback = QUALITY_PRESETS["0"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rshono/create",
3
- "version": "1.0.0-rc.11",
3
+ "version": "1.0.0-rc.13",
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.11"
49
+ "@rshono/core": "1.0.0-rc.13"
50
50
  },
51
51
  "scripts": {
52
52
  "build": "node scripts/codegen.mjs && node scripts/build.mjs",
@@ -37,4 +37,4 @@ Change `deploy` in `rshono.config.ts` to target somewhere else, or build for one
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
39
 
40
- Every target, and what each one needs: <https://www.rshono.com/docs/deployment>
40
+ Every deployment target, and what each one needs: <https://www.rshono.com/docs/deployment>
@@ -1,5 +1,6 @@
1
1
  node_modules/
2
2
  dist/
3
+ .rshono/
3
4
  *.tsbuildinfo
4
5
  .DS_Store
5
6
 
@@ -3,7 +3,10 @@ import reactHooks from 'eslint-plugin-react-hooks';
3
3
  import tseslint from 'typescript-eslint';
4
4
 
5
5
  export default tseslint.config(
6
- { ignores: ['dist/**', '.wrangler/**', '.vercel/**', '.netlify/**'] },
6
+ // Build output, all of it generated: `dist/` from `rshono build`, `.rshono/` from `rshono dev`, and
7
+ // whatever the deploy target assembles beside them. Without this, linting after a dev run reports on
8
+ // bundles rather than on anything you wrote.
9
+ { ignores: ['dist/**', '.rshono/**', '.wrangler/**', '.vercel/**', '.netlify/**'] },
7
10
  js.configs.recommended,
8
11
  tseslint.configs.recommendedTypeChecked,
9
12
  reactHooks.configs.flat['recommended-latest'],