create-rsc-kit 0.20.12 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export declare const STARTER_ICONS: Record<string, () => Uint8Array>;
package/dist/icons.js ADDED
@@ -0,0 +1,14 @@
1
+ // Starter icons for an installable app, written by `--pwa`.
2
+ //
3
+ // Shipped as files rather than drawn at build: nothing in rsc-kit opens, resizes
4
+ // or encodes an image, at build, in dev or at runtime. They are a slate tile with
5
+ // the ring the build prints for a static page - replace them with your own.
6
+ // Generated once, by hand; their sizes are in their names, which is how the
7
+ // build reads them.
8
+ const decode = (base64) => Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
9
+ export const STARTER_ICONS = {
10
+ 'icon-192.png': () => decode('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'),
11
+ 'icon-512.png': () => decode('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'),
12
+ 'icon-maskable-512.png': () => decode('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'),
13
+ 'apple-icon.png': () => decode('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'),
14
+ };
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  // several things in it that are invisible when wrong: NODE_ENV on the build,
6
6
  // @source for Tailwind, the ambient declaration for the generated engine
7
7
  // bundle. Each of those fails by producing a page that looks nearly right.
8
- import { existsSync, mkdirSync, readdirSync, writeFileSync } from 'node:fs';
8
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
9
9
  import { dirname, join, resolve } from 'node:path';
10
10
  import { fileURLToPath } from 'node:url';
11
11
  import { spawnSync } from 'node:child_process';
@@ -16,6 +16,7 @@ import { DEFAULT_COMPILER, HELP, HOSTS, publishedCore, assertInsideCwd, assertUs
16
16
  import { Prompter, bold, cyan, dim } from './prompt.js';
17
17
  import { checkForNewer, notifyIfStale, selfVersion } from './stale.js';
18
18
  import * as t from './templates.js';
19
+ import { STARTER_ICONS } from './icons.js';
19
20
  // Same treatment as a refusal from write(): a bad flag is a decision this tool
20
21
  // made, and it happens before the try/catch below because the flags are what
21
22
  // everything else is built from.
@@ -85,13 +86,14 @@ async function collect() {
85
86
  const dir = flags.dir ?? 'my-app';
86
87
  return {
87
88
  dir: resolve(dir),
88
- name: basename(dir),
89
+ name: basename(resolve(dir)),
89
90
  host: flags.host ?? 'bun',
90
91
  compiler: flags.compiler ?? 'none',
91
92
  tailwind: flags.tailwind ?? true,
92
93
  lint: flags.lint ?? true,
93
94
  validation: flags.validation ?? 'zod',
94
95
  env: flags.env ?? (flags.validation ?? 'zod') !== 'none',
96
+ pwa: flags.pwa ?? false,
95
97
  sourceDir: flags.sourceDir ?? 'src',
96
98
  install: flags.install ?? true,
97
99
  git: flags.git ?? true,
@@ -122,15 +124,18 @@ async function collect() {
122
124
  const env = validation === 'none'
123
125
  ? false
124
126
  : (flags.env ?? (await p.confirm('Typed environment variables (@t3-oss/env-core)', true)));
127
+ // No by default: a service worker outlives the code that installed it.
128
+ const pwa = flags.pwa ?? (await p.confirm('Installable app - works offline, adds to the home screen', false));
125
129
  return {
126
130
  dir: resolve(dir),
127
- name: basename(dir),
131
+ name: basename(resolve(dir)),
128
132
  host,
129
133
  compiler,
130
134
  tailwind,
131
135
  lint,
132
136
  validation,
133
137
  env,
138
+ pwa,
134
139
  sourceDir: flags.sourceDir ?? 'src',
135
140
  install: flags.install ?? (await p.confirm('Install dependencies now', true)),
136
141
  git: flags.git ?? true,
@@ -186,6 +191,14 @@ function write(o) {
186
191
  }
187
192
  if (o.lint)
188
193
  files.push(['.oxlintrc.json', t.oxlintConfig(o)]);
194
+ // Installable: the manifest, the page a navigation with no network lands on,
195
+ // and starter icons - files, found by name, nothing drawn at build.
196
+ if (o.pwa) {
197
+ files.push([`${o.sourceDir}/app/manifest.ts`, t.manifest(o)]);
198
+ files.push([`${o.sourceDir}/app/offline/page.tsx`, t.offlinePage]);
199
+ for (const [name, bytes] of Object.entries(STARTER_ICONS))
200
+ files.push([`${o.sourceDir}/app/${name}`, bytes()]);
201
+ }
189
202
  // A backend answering host calls: the two lines that wire it, and a
190
203
  // secret the backend is given once. The scaffold's .gitignore already
191
204
  // keeps .env out of git.
@@ -209,6 +222,19 @@ function write(o) {
209
222
  catch (error) {
210
223
  if (error.code !== 'EEXIST')
211
224
  throw error;
225
+ // A .gitignore is merged, not skipped. The fresh clone of an empty
226
+ // repository brings its own - GitHub writes one - and leaving it as it
227
+ // was left the build output and .env, with its generated secret, one
228
+ // `git add .` from being committed. Only the lines it lacks are added.
229
+ if (path === '.gitignore' && typeof contents === 'string') {
230
+ const have = readFileSync(full, 'utf-8');
231
+ const lines = new Set(have.split('\n').map((line) => line.trim()));
232
+ const missing = contents.split('\n').filter((line) => line.trim() && !line.startsWith('#') && !lines.has(line.trim()));
233
+ if (missing.length > 0) {
234
+ writeFileSync(full, (have.endsWith('\n') ? have : have + '\n') + '\n# rsc-kit\n' + missing.join('\n') + '\n');
235
+ }
236
+ continue;
237
+ }
212
238
  replaced.push(path);
213
239
  }
214
240
  }
@@ -240,6 +266,10 @@ function report(o) {
240
266
  if (o.backend) {
241
267
  stdout.write(`${bold('Then:')} ${t.backendStep(false)}\n\n`);
242
268
  }
269
+ if (o.pwa) {
270
+ stdout.write(`${bold('Installable.')} Replace the starter icons in ${o.sourceDir}/app with your own - same names.\n` +
271
+ dim(` For iOS launch screens add apple-splash-WIDTHxHEIGHT.png; the PWA guide has the sizes.\n\n`));
272
+ }
243
273
  }
244
274
  /** A path the user can paste, when it is under where they are. */
245
275
  function relativeish(dir) {
package/dist/init.d.ts CHANGED
@@ -1,5 +1,12 @@
1
1
  import type { Host, Options } from './options.js';
2
2
  export interface Detected {
3
+ /**
4
+ * Whether there was no package.json and init is writing the first one.
5
+ *
6
+ * A Go, Rust or Python service adding a frontend has never had one - it
7
+ * is not a JavaScript project yet, and that is exactly who init is for.
8
+ */
9
+ createdPackageJson: boolean;
3
10
  /** What the project's package.json says it already depends on. */
4
11
  deps: Record<string, string>;
5
12
  /** A Laravel application: artisan and a composer manifest, both. */
@@ -20,12 +27,10 @@ export interface Step {
20
27
  what: string;
21
28
  detail?: string;
22
29
  }
23
- /**
24
- * What is already here.
25
- *
26
- * Every answer is a guess the caller can override — the point is to not ask
27
- * about things the project has already decided.
28
- */
30
+ /** Whether there is nothing here but what a new repository starts with. */
31
+ export declare function isFreshDirectory(dir: string): boolean;
32
+ /** The marker that makes this an existing project, or null. */
33
+ export declare function projectMarker(dir: string): string | null;
29
34
  export declare function detect(dir: string): Detected;
30
35
  export declare function initialise(o: Options, found: Detected, dir: string): Step[];
31
36
  /**
package/dist/init.js CHANGED
@@ -8,10 +8,10 @@
8
8
  // written, missing dependencies are added, and for anything already present
9
9
  // the exact edit is printed for the reader to make. A tool that silently
10
10
  // reformats a working server has to be right about more than it can know.
11
- import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from 'node:fs';
11
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, renameSync } from 'node:fs';
12
12
  import { randomBytes } from 'node:crypto';
13
- import { dirname, join } from 'node:path';
14
- import { cwd, exit, stdout } from 'node:process';
13
+ import { basename, dirname, join } from 'node:path';
14
+ import { argv, cwd, exit, stdout } from 'node:process';
15
15
  import { DEFAULT_COMPILER, parseArgs, publishedCore } from './options.js';
16
16
  import { Prompter, bold, cyan, dim } from './prompt.js';
17
17
  import * as t from './templates.js';
@@ -24,9 +24,52 @@ const HOST_PACKAGES = {};
24
24
  * Every answer is a guess the caller can override — the point is to not ask
25
25
  * about things the project has already decided.
26
26
  */
27
+ /**
28
+ * Files that say a directory is already some project, in some language.
29
+ *
30
+ * init adds a frontend to a project that exists. One with none of these and
31
+ * no package.json is an empty directory, and a new app is `bun create`'s job.
32
+ */
33
+ const PROJECT_MARKERS = [
34
+ 'go.mod',
35
+ 'composer.json',
36
+ 'Cargo.toml',
37
+ 'pyproject.toml',
38
+ 'requirements.txt',
39
+ 'Gemfile',
40
+ 'pom.xml',
41
+ 'build.gradle',
42
+ 'build.gradle.kts',
43
+ 'mix.exs',
44
+ ];
45
+ /**
46
+ * What a directory can hold and still be a place to start an app: a fresh
47
+ * clone of an empty repository - its README, its licence, its .gitignore.
48
+ * The scaffold never overwrites, so these are left as they are.
49
+ */
50
+ const FRESH = /^(\.git|\.DS_Store|\.gitignore|\.gitattributes|\.editorconfig|README(\..+)?|LICEN[CS]E(\..+)?)$/i;
51
+ /** Whether there is nothing here but what a new repository starts with. */
52
+ export function isFreshDirectory(dir) {
53
+ return readdirSync(dir).every((name) => FRESH.test(name));
54
+ }
55
+ /** The marker that makes this an existing project, or null. */
56
+ export function projectMarker(dir) {
57
+ return PROJECT_MARKERS.find((file) => existsSync(join(dir, file))) ?? null;
58
+ }
59
+ /** The first package.json of a project that has never had one: a name, and ESM, which Vite expects. */
60
+ function freshPackageJson(dir) {
61
+ const name = basename(dir)
62
+ .toLowerCase()
63
+ .replace(/[^a-z0-9._-]+/g, '-')
64
+ .replace(/^[._-]+|[-]+$/g, '') || 'app';
65
+ return { name, private: true, type: 'module' };
66
+ }
27
67
  export function detect(dir) {
28
68
  const pkgPath = join(dir, 'package.json');
29
- const packageJson = JSON.parse(readFileSync(pkgPath, 'utf-8'));
69
+ const createdPackageJson = !existsSync(pkgPath);
70
+ const packageJson = createdPackageJson
71
+ ? freshPackageJson(dir)
72
+ : JSON.parse(readFileSync(pkgPath, 'utf-8'));
30
73
  const deps = {
31
74
  ...(packageJson.dependencies ?? {}),
32
75
  ...(packageJson.devDependencies ?? {}),
@@ -65,6 +108,7 @@ export function detect(dir) {
65
108
  hasReact: Boolean(deps.react),
66
109
  hasTailwind: Boolean(deps.tailwindcss),
67
110
  hasTypeScript: existsSync(join(dir, 'tsconfig.json')),
111
+ createdPackageJson,
68
112
  packageJson,
69
113
  };
70
114
  }
@@ -579,6 +623,9 @@ function backend(o, found, dir) {
579
623
  }
580
624
  export function initialise(o, found, dir) {
581
625
  const steps = [
626
+ ...(found.createdPackageJson
627
+ ? [{ kind: 'wrote', what: 'package.json', detail: 'the project had none; the frontend is its first' }]
628
+ : []),
582
629
  ...routes(o, dir),
583
630
  ...viteConfig(o, found, dir),
584
631
  ...tsconfig(o, found, dir),
@@ -595,6 +642,10 @@ const DEFAULT_BACKEND = 'http://127.0.0.1:8080';
595
642
  const INIT_HELP = `
596
643
  rsc-kit init — add RSC to the project in this directory
597
644
 
645
+ A project with no package.json (a go.mod, a Cargo.toml, a composer.json) is
646
+ given its first one. An empty directory, or a fresh clone of an empty
647
+ repository, becomes a new app.
648
+
598
649
  Nothing existing is rewritten. New files are written; a file that is a list
599
650
  gets our entry added to it - .mcp.json, AGENTS.md, tsconfig.json's include,
600
651
  .env - with yours left as written; and for anything else already there the
@@ -626,9 +677,23 @@ export async function runInit(args) {
626
677
  return;
627
678
  }
628
679
  const dir = cwd();
629
- if (!existsSync(join(dir, 'package.json'))) {
630
- stdout.write(`\n${bold('No package.json here.')}\n` +
631
- ` init adds RSC to a project that already exists. To start a new one:\n` +
680
+ // No package.json is fine in a project that is already something - a Go
681
+ // service has never had one, and init writes its first.
682
+ if (!existsSync(join(dir, 'package.json')) && projectMarker(dir) === null) {
683
+ // Nothing here at all - an empty directory, or a fresh clone of an empty
684
+ // repository: there is nothing to add to, so this is a new app, and
685
+ // `init` makes one here rather than sending someone to a second command
686
+ // that does the same thing. The scaffolder reads its arguments at import;
687
+ // it is handed this directory and whatever flags init was given.
688
+ if (isFreshDirectory(dir)) {
689
+ argv.splice(2, argv.length - 2, '.', ...args);
690
+ await import('./index.js');
691
+ return;
692
+ }
693
+ stdout.write(`\n${bold('Nothing here to add to.')}\n` +
694
+ ` init adds RSC to a project that already exists - a package.json, a go.mod, a composer.json -\n` +
695
+ ` or starts one in an empty directory. This one has files and no project.\n` +
696
+ ` To start a new app beside them:\n` +
632
697
  ` ${cyan('bun create rsc-kit@latest my-app')}\n\n`);
633
698
  exit(1);
634
699
  }
@@ -671,6 +736,9 @@ export async function runInit(args) {
671
736
  // An existing project has its own schema library and env handling; init adds neither.
672
737
  validation: 'none',
673
738
  env: false,
739
+ // Adding to a project writes no service worker; that is a decision for
740
+ // the app, and create --pwa is where it is offered.
741
+ pwa: false,
674
742
  sourceDir: flags.sourceDir ?? found.sourceDir ?? 'src',
675
743
  install: false,
676
744
  git: false,
package/dist/options.d.ts CHANGED
@@ -36,6 +36,13 @@ export interface Options {
36
36
  * Needs a validation library; asked only when one was chosen.
37
37
  */
38
38
  env: boolean;
39
+ /**
40
+ * An installable app: works with no network, and can be added to a home
41
+ * screen. Turns on `offline`, and writes a manifest, an /offline page and
42
+ * starter icons. Off by default - a service worker outlives the code that
43
+ * installed it, so it is a decision to take on purpose.
44
+ */
45
+ pwa: boolean;
39
46
  install: boolean;
40
47
  git: boolean;
41
48
  /** What to depend on for the engine. A path makes a local checkout testable. */
@@ -113,4 +120,4 @@ export declare function assertUsableName(name: string): void;
113
120
  * generator nobody can run without reading it first.
114
121
  */
115
122
  export declare function assertInsideCwd(dir: string, cwd: string): void;
116
- export declare const HELP = "\n create-rsc-kit \u2014 scaffold an RSC app\n\n Usage\n create-rsc-kit <dir> [options]\n bun create rsc-kit@latest <dir> [options]\n\n Options\n --host=bun|node|worker where it runs \u2014 picks the Nitro preset\n --compiler=none|oxc|babel React Compiler (prompt offers oxc; babel by flag)\n --tailwind / --no-tailwind include Tailwind\n --lint / --no-lint include oxlint\n --validation=zod|valibot|arktype|none\n the schema library; forms, actions and env use it\n --env / --no-env typed environment variables (@t3-oss/env-core)\n --source-dir <dir> where app/ lives (default: src)\n --backend=<url> a backend answering host calls \u2014 a Go server, say,\n at http://127.0.0.1:8080; writes .env with a secret\n --init add to the project here, rather than scaffold\n --core=<spec> engine dependency, e.g. file:../rsc-kit/packages/core\n --no-install skip installing dependencies\n --no-git skip git init\n -y, --yes accept every default, ask nothing\n -h, --help this\n";
123
+ export declare const HELP = "\n create-rsc-kit \u2014 scaffold an RSC app\n\n Usage\n create-rsc-kit <dir> [options]\n bun create rsc-kit@latest <dir> [options]\n\n Options\n --host=bun|node|worker where it runs \u2014 picks the Nitro preset\n --compiler=none|oxc|babel React Compiler (prompt offers oxc; babel by flag)\n --tailwind / --no-tailwind include Tailwind\n --lint / --no-lint include oxlint\n --validation=zod|valibot|arktype|none\n the schema library; forms, actions and env use it\n --env / --no-env typed environment variables (@t3-oss/env-core)\n --pwa / --no-pwa installable: offline, a manifest, starter icons (default: no)\n --source-dir <dir> where app/ lives (default: src)\n --backend=<url> a backend answering host calls \u2014 a Go server, say,\n at http://127.0.0.1:8080; writes .env with a secret\n --init add to the project here, rather than scaffold\n --core=<spec> engine dependency, e.g. file:../rsc-kit/packages/core\n --no-install skip installing dependencies\n --no-git skip git init\n -y, --yes accept every default, ask nothing\n -h, --help this\n";
package/dist/options.js CHANGED
@@ -145,6 +145,10 @@ export function parseArgs(argv) {
145
145
  out.compiler = arg.slice(11);
146
146
  else if (arg.startsWith('--validation='))
147
147
  out.validation = assertValidation(arg.slice(13));
148
+ else if (arg === '--pwa')
149
+ out.pwa = true;
150
+ else if (arg === '--no-pwa')
151
+ out.pwa = false;
148
152
  else if (arg === '--env')
149
153
  out.env = true;
150
154
  else if (arg === '--no-env')
@@ -203,6 +207,7 @@ export const HELP = `
203
207
  --validation=zod|valibot|arktype|none
204
208
  the schema library; forms, actions and env use it
205
209
  --env / --no-env typed environment variables (@t3-oss/env-core)
210
+ --pwa / --no-pwa installable: offline, a manifest, starter icons (default: no)
206
211
  --source-dir <dir> where app/ lives (default: src)
207
212
  --backend=<url> a backend answering host calls — a Go server, say,
208
213
  at http://127.0.0.1:8080; writes .env with a secret
@@ -117,6 +117,10 @@ export declare function oxlintConfig(o: Options): string;
117
117
  * AGENTS.md rather than CLAUDE.md: it is the cross-tool name, and Claude Code
118
118
  * reads it too.
119
119
  */
120
+ /** The manifest an installable app declares, beside its routes. */
121
+ export declare function manifest(o: Options): string;
122
+ /** What a navigation with no network and nothing cached lands on. */
123
+ export declare const offlinePage = "// Served by the service worker when there is no network and nothing cached\n// for the page asked for. It has to stay static - no cookies(), no headers() -\n// because there is no request to render it for; the build says if it is not.\nexport default function Offline() {\n return (\n <main>\n <h1>You are offline</h1>\n <p>This page is not saved on this device. Try again once you are back online.</p>\n </main>\n )\n}\n";
120
124
  export declare function agents(o: Options): string;
121
125
  /**
122
126
  * Project-scoped MCP config, which Claude Code reads from the project root
package/dist/templates.js CHANGED
@@ -102,7 +102,7 @@ export function scripts(o) {
102
102
  // nothing in .gitignore covering it.
103
103
  ...(o.host === 'bun'
104
104
  ? {
105
- compile: `${vite} build && bun build --compile --keep-names .output/server/compile.mjs --outfile dist/app`,
105
+ compile: `${vite} build && bun build --compile .output/server/compile.mjs --outfile dist/app`,
106
106
  }
107
107
  : {}),
108
108
  }),
@@ -215,6 +215,9 @@ export function viteConfig(o) {
215
215
  `sourceDir: '${p.sourceDir}'`,
216
216
  `outDir: '${p.outDir}'`,
217
217
  ...(p.hotFile ? [`hotFile: '${p.hotFile}'`] : []),
218
+ // A service worker: the app survives a reload with no network. Off
219
+ // unless asked for, because it outlives the code that installed it.
220
+ ...(o.pwa ? ['offline: true'] : []),
218
221
  ];
219
222
  // Nitro leads: it builds the server around the rsc entry's default export,
220
223
  // and rscKit tells plugin-rsc not to install a handler competing for that
@@ -543,6 +546,45 @@ export function oxlintConfig(o) {
543
546
  * AGENTS.md rather than CLAUDE.md: it is the cross-tool name, and Claude Code
544
547
  * reads it too.
545
548
  */
549
+ /** The manifest an installable app declares, beside its routes. */
550
+ export function manifest(o) {
551
+ const name = o.name
552
+ .split(/[-_.]+/)
553
+ .filter(Boolean)
554
+ .map((word) => word[0].toUpperCase() + word.slice(1))
555
+ .join(' ');
556
+ return `import type { WebManifest } from '@rsc-kit/core/manifest-file'
557
+
558
+ // What makes this app installable. The icons need no listing: icon-*.png,
559
+ // icon-maskable-*.png and apple-icon.png beside this file are found by name,
560
+ // their sizes read from it. Replace them with your own; for iOS launch
561
+ // screens add apple-splash-WIDTHxHEIGHT.png - see the PWA guide.
562
+ export default {
563
+ // The app's identity. Without it a browser derives one from startUrl, and
564
+ // changing that later leaves everyone who installed with an orphaned app.
565
+ id: '/',
566
+ name: '${name}',
567
+ shortName: '${name}',
568
+ description: '${name}, installable and working offline.',
569
+ // The Android splash screen is this colour behind the icon.
570
+ backgroundColor: '#f8fafc',
571
+ themeColor: '#0f172a',
572
+ } satisfies WebManifest
573
+ `;
574
+ }
575
+ /** What a navigation with no network and nothing cached lands on. */
576
+ export const offlinePage = `// Served by the service worker when there is no network and nothing cached
577
+ // for the page asked for. It has to stay static - no cookies(), no headers() -
578
+ // because there is no request to render it for; the build says if it is not.
579
+ export default function Offline() {
580
+ return (
581
+ <main>
582
+ <h1>You are offline</h1>
583
+ <p>This page is not saved on this device. Try again once you are back online.</p>
584
+ </main>
585
+ )
586
+ }
587
+ `;
546
588
  export function agents(o) {
547
589
  const pm = o.host === 'node' ? 'npm run' : 'bun run';
548
590
  return `# Working in this project
@@ -640,12 +682,23 @@ it before any page. ${o.env
640
682
  render waits for it). Do not import a bootstrap module from pages to get the
641
683
  same effect; it depends on nobody forgetting.
642
684
 
643
- ## Forms
685
+ ${o.pwa ? `## Installable
686
+
687
+ \`offline: true\` is on and \`${o.sourceDir}/app/manifest.ts\` declares the app. Icons are
688
+ files in \`${o.sourceDir}/app\` found by name (\`icon-192.png\`, \`icon-512.png\`,
689
+ \`icon-maskable-512.png\`, \`apple-icon.png\`) - replace them, do not list them.
690
+ \`${o.sourceDir}/app/offline/page.tsx\` must stay static. Nothing processes images.
691
+
692
+ ` : ''}## Forms
644
693
 
645
694
  Uncontrolled. Inputs keep their value in the DOM, an initial value is
646
695
  \`defaultValue\`, and the action reads \`FormData\`. Do not write \`useState\` +
647
- \`value\`/\`onChange\` per input. Control one field only when the UI must react
648
- as the user types, and bind that one with \`useField\`.
696
+ \`value\`/\`onChange\` per input.
697
+
698
+ Read a form with two words and nothing else: \`error('title')\` for a field's
699
+ error, \`field('title')\` to bind the one field whose value the UI must hold.
700
+ In a child component \`useForm()\` returns the same object. There is no
701
+ \`errors\` map, \`fieldState\` or \`useFormStatus\` to reach for.
649
702
 
650
703
  ## Data
651
704
 
@@ -848,9 +901,13 @@ import { createEnv } from '@t3-oss/env-core'
848
901
  // or wrong - before anything runs, not as an undefined three calls later.
849
902
  //
850
903
  // Server variables stay on the server. A variable the browser may read has
851
- // to start with PUBLIC_, and is read from import.meta.env, which is what Vite
852
- // exposes there. Add a variable: one line in the schema, and every reader is
853
- // typed.
904
+ // to start with PUBLIC_. Add a variable: one line in the schema, and every
905
+ // reader is typed.
906
+ //
907
+ // Import this from server code. A client component reads its PUBLIC_ value
908
+ // from import.meta.env directly: importing this file there ships the
909
+ // validator to the browser with it - about 20 kB gzipped for Zod, to read one
910
+ // string. The value is still checked here, when the server starts.
854
911
  const processEnv: Record<string, string | undefined> = typeof process === 'undefined' ? {} : process.env
855
912
 
856
913
  export const env = createEnv({
@@ -863,8 +920,9 @@ export const env = createEnv({
863
920
  client: {
864
921
  // PUBLIC_SITE_URL: ${lib.url},
865
922
  },
866
- // process is the server's; a "use client" file importing this for a
867
- // PUBLIC_ value has only import.meta.env, and Vite fills the PUBLIC_ ones.
923
+ // process is the server's. import.meta.env is merged in for the PUBLIC_
924
+ // ones, which Vite fills - and so the file does not throw if a client
925
+ // component imports it anyway.
868
926
  runtimeEnv: { ...processEnv, ...import.meta.env },
869
927
  emptyStringAsUndefined: true,
870
928
  // A build machine without the production variables: SKIP_ENV_VALIDATION=1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-rsc-kit",
3
- "version": "0.20.12",
3
+ "version": "0.21.0",
4
4
  "type": "module",
5
5
  "description": "Scaffold an RSC app on Bun, Hono, Elysia or Node.",
6
6
  "bin": {