create-rsc-kit 0.20.13 → 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.
- package/dist/icons.d.ts +1 -0
- package/dist/icons.js +14 -0
- package/dist/index.js +33 -3
- package/dist/init.d.ts +11 -6
- package/dist/init.js +75 -7
- package/dist/options.d.ts +8 -1
- package/dist/options.js +5 -0
- package/dist/templates.d.ts +4 -0
- package/dist/templates.js +66 -8
- package/package.json +1 -1
package/dist/icons.d.ts
ADDED
|
@@ -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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
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
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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
|
package/dist/templates.d.ts
CHANGED
|
@@ -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
|
@@ -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
|
-
|
|
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.
|
|
648
|
-
|
|
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_
|
|
852
|
-
//
|
|
853
|
-
//
|
|
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
|
|
867
|
-
//
|
|
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
|