create-rsc-kit 0.22.9 → 0.24.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/index.js +14 -0
- package/dist/init.js +21 -0
- package/dist/options.d.ts +8 -1
- package/dist/options.js +5 -1
- package/dist/shadcn.d.ts +27 -0
- package/dist/shadcn.js +56 -0
- package/dist/templates.js +8 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -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 { setUpShadcn } from './shadcn.js';
|
|
19
20
|
import { STARTER_ICONS } from './icons.js';
|
|
20
21
|
// Same treatment as a refusal from write(): a bad flag is a decision this tool
|
|
21
22
|
// made, and it happens before the try/catch below because the flags are what
|
|
@@ -39,6 +40,11 @@ const core = flags.core ?? defaultCore(dirname(fileURLToPath(import.meta.url)));
|
|
|
39
40
|
// Started before the first question and never awaited here — see notifyIfStale.
|
|
40
41
|
const staleCheck = checkForNewer('create-rsc-kit', selfVersion(import.meta.url));
|
|
41
42
|
const options = await collect();
|
|
43
|
+
// shadcn's components are styled with Tailwind; without it they render bare.
|
|
44
|
+
if (options.shadcn && !options.tailwind) {
|
|
45
|
+
stdout.write(`\n${bold('Cannot scaffold here.')}\n --shadcn needs Tailwind; drop --no-tailwind.\n\n`);
|
|
46
|
+
exit(1);
|
|
47
|
+
}
|
|
42
48
|
try {
|
|
43
49
|
write(options);
|
|
44
50
|
}
|
|
@@ -63,10 +69,12 @@ if (options.git) {
|
|
|
63
69
|
run('git', ['init', '--quiet'], options.dir);
|
|
64
70
|
}
|
|
65
71
|
}
|
|
72
|
+
let installed = false;
|
|
66
73
|
if (options.install) {
|
|
67
74
|
stdout.write(`\n${dim('Installing dependencies…')}\n`);
|
|
68
75
|
const pm = options.host === 'node' ? 'npm' : 'bun';
|
|
69
76
|
const ok = run(pm, ['install'], options.dir);
|
|
77
|
+
installed = ok;
|
|
70
78
|
if (!ok) {
|
|
71
79
|
stdout.write(`\n${bold('Dependencies did not install.')} The files are written; run the install yourself.\n` +
|
|
72
80
|
(options.core === publishedCore()
|
|
@@ -74,6 +82,8 @@ if (options.install) {
|
|
|
74
82
|
: ''));
|
|
75
83
|
}
|
|
76
84
|
}
|
|
85
|
+
if (options.shadcn)
|
|
86
|
+
setUpShadcn({ dir: options.dir, host: options.host, unattended, installed });
|
|
77
87
|
report(options);
|
|
78
88
|
await notifyIfStale(staleCheck, 'bun create rsc-kit@latest');
|
|
79
89
|
// ── ─────────────────────────────────────────────────────────────────────────
|
|
@@ -94,6 +104,7 @@ async function collect() {
|
|
|
94
104
|
validation: flags.validation ?? 'zod',
|
|
95
105
|
env: flags.env ?? (flags.validation ?? 'zod') !== 'none',
|
|
96
106
|
pwa: flags.pwa ?? false,
|
|
107
|
+
shadcn: flags.shadcn ?? false,
|
|
97
108
|
sourceDir: flags.sourceDir ?? 'src',
|
|
98
109
|
install: flags.install ?? true,
|
|
99
110
|
git: flags.git ?? true,
|
|
@@ -118,6 +129,8 @@ async function collect() {
|
|
|
118
129
|
// Yes/no, not which: see DEFAULT_COMPILER.
|
|
119
130
|
const compiler = flags.compiler ?? ((await p.confirm('React Compiler', true)) ? DEFAULT_COMPILER : 'none');
|
|
120
131
|
const tailwind = flags.tailwind ?? (await p.confirm('Tailwind CSS', true));
|
|
132
|
+
// Only with Tailwind: shadcn's components are styled with it.
|
|
133
|
+
const shadcn = tailwind ? (flags.shadcn ?? (await p.confirm('shadcn/ui', false))) : false;
|
|
121
134
|
const lint = flags.lint ?? (await p.confirm('oxlint', true));
|
|
122
135
|
const validation = flags.validation ?? (await p.select('Validation library', VALIDATIONS));
|
|
123
136
|
// Only worth asking once there is a library to validate with.
|
|
@@ -136,6 +149,7 @@ async function collect() {
|
|
|
136
149
|
validation,
|
|
137
150
|
env,
|
|
138
151
|
pwa,
|
|
152
|
+
shadcn,
|
|
139
153
|
sourceDir: flags.sourceDir ?? 'src',
|
|
140
154
|
install: flags.install ?? (await p.confirm('Install dependencies now', true)),
|
|
141
155
|
git: flags.git ?? true,
|
package/dist/init.js
CHANGED
|
@@ -14,6 +14,7 @@ import { randomBytes } from 'node:crypto';
|
|
|
14
14
|
import { basename, dirname, join } from 'node:path';
|
|
15
15
|
import { argv, cwd, exit, stdout } from 'node:process';
|
|
16
16
|
import { DEFAULT_COMPILER, parseArgs, publishedCore } from './options.js';
|
|
17
|
+
import { setUpShadcn } from './shadcn.js';
|
|
17
18
|
import { Prompter, bold, cyan, dim } from './prompt.js';
|
|
18
19
|
import * as t from './templates.js';
|
|
19
20
|
// Nothing here detects a host any more: a host is a Nitro preset, and Nitro
|
|
@@ -665,6 +666,8 @@ const INIT_HELP = `
|
|
|
665
666
|
(a Go service), otherwise only if it is already there
|
|
666
667
|
--no-install don't install; a project with no package.json yet is
|
|
667
668
|
installed by default, any other never is
|
|
669
|
+
--shadcn shadcn/ui for RSC: marks an existing components.json
|
|
670
|
+
"rsc": true, or runs shadcn's init after the install
|
|
668
671
|
-y, --yes accept what was detected, ask nothing
|
|
669
672
|
-h, --help this
|
|
670
673
|
`;
|
|
@@ -740,6 +743,9 @@ export async function runInit(args) {
|
|
|
740
743
|
// Only ever for a project init gave its first package.json: anywhere else
|
|
741
744
|
// the project's own package manager installs, not this.
|
|
742
745
|
let install = defaults.install && flags.install !== false;
|
|
746
|
+
let shadcn = flags.shadcn === true;
|
|
747
|
+
// Set up already: the question is only whether to mark it for RSC.
|
|
748
|
+
const hasShadcn = existsSync(join(dir, 'components.json'));
|
|
743
749
|
if (!unattended) {
|
|
744
750
|
const p = new Prompter();
|
|
745
751
|
try {
|
|
@@ -747,6 +753,12 @@ export async function runInit(args) {
|
|
|
747
753
|
if (flags.tailwind === undefined && !found.hasTailwind) {
|
|
748
754
|
tailwind = await p.confirm('Tailwind CSS', defaults.tailwind);
|
|
749
755
|
}
|
|
756
|
+
// Only with Tailwind: shadcn's components are styled with it.
|
|
757
|
+
if (flags.shadcn === undefined && (tailwind || found.hasTailwind)) {
|
|
758
|
+
shadcn = hasShadcn
|
|
759
|
+
? await p.confirm('components.json is here: set "rsc": true for shadcn', true)
|
|
760
|
+
: await p.confirm('shadcn/ui', false);
|
|
761
|
+
}
|
|
750
762
|
if (defaults.install && flags.install === undefined) {
|
|
751
763
|
install = await p.confirm('Install dependencies now', true);
|
|
752
764
|
}
|
|
@@ -755,6 +767,11 @@ export async function runInit(args) {
|
|
|
755
767
|
p.close();
|
|
756
768
|
}
|
|
757
769
|
}
|
|
770
|
+
// shadcn's components are styled with Tailwind; without it they render bare.
|
|
771
|
+
if (shadcn && !tailwind && !found.hasTailwind) {
|
|
772
|
+
stdout.write(`${bold('Not set up.')} --shadcn needs Tailwind: pass --tailwind too.\n\n`);
|
|
773
|
+
exit(1);
|
|
774
|
+
}
|
|
758
775
|
const options = {
|
|
759
776
|
dir,
|
|
760
777
|
name: 'app',
|
|
@@ -793,6 +810,10 @@ export async function runInit(args) {
|
|
|
793
810
|
stdout.write(`\n${bold('Dependencies did not install.')} The files are written; run the install yourself.\n`);
|
|
794
811
|
}
|
|
795
812
|
}
|
|
813
|
+
// After the install: shadcn's init reads the dependencies, and its
|
|
814
|
+
// components need them. A project already using shadcn is only marked.
|
|
815
|
+
if (shadcn)
|
|
816
|
+
setUpShadcn({ dir, host: options.host, unattended, installed });
|
|
796
817
|
if (manual.length > 0) {
|
|
797
818
|
stdout.write(`\n${bold('Then, by hand:')} the steps marked ! above.\n\n`);
|
|
798
819
|
return;
|
package/dist/options.d.ts
CHANGED
|
@@ -43,6 +43,13 @@ export interface Options {
|
|
|
43
43
|
* installed it, so it is a decision to take on purpose.
|
|
44
44
|
*/
|
|
45
45
|
pwa: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* shadcn/ui, set up by its own CLI once dependencies are installed, then
|
|
48
|
+
* told this is an RSC app - it sees a plain Vite project and writes
|
|
49
|
+
* `"rsc": false`, and its interactive components then lack "use client".
|
|
50
|
+
* Needs Tailwind.
|
|
51
|
+
*/
|
|
52
|
+
shadcn?: boolean;
|
|
46
53
|
install: boolean;
|
|
47
54
|
git: boolean;
|
|
48
55
|
/** What to depend on for the engine. A path makes a local checkout testable. */
|
|
@@ -120,4 +127,4 @@ export declare function assertUsableName(name: string): void;
|
|
|
120
127
|
* generator nobody can run without reading it first.
|
|
121
128
|
*/
|
|
122
129
|
export declare function assertInsideCwd(dir: string, cwd: string): void;
|
|
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
|
|
130
|
+
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 --shadcn shadcn/ui, initialised for RSC; needs Tailwind\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 --yes accept every default, ask nothing\n (-y too, but `bun create` takes -y as its own)\n -h, --help this\n";
|
package/dist/options.js
CHANGED
|
@@ -145,6 +145,8 @@ 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 === '--shadcn')
|
|
149
|
+
out.shadcn = true;
|
|
148
150
|
else if (arg === '--pwa')
|
|
149
151
|
out.pwa = true;
|
|
150
152
|
else if (arg === '--no-pwa')
|
|
@@ -208,6 +210,7 @@ export const HELP = `
|
|
|
208
210
|
the schema library; forms, actions and env use it
|
|
209
211
|
--env / --no-env typed environment variables (@t3-oss/env-core)
|
|
210
212
|
--pwa / --no-pwa installable: offline, a manifest, starter icons (default: no)
|
|
213
|
+
--shadcn shadcn/ui, initialised for RSC; needs Tailwind
|
|
211
214
|
--source-dir <dir> where app/ lives (default: src)
|
|
212
215
|
--backend=<url> a backend answering host calls — a Go server, say,
|
|
213
216
|
at http://127.0.0.1:8080; writes .env with a secret
|
|
@@ -215,6 +218,7 @@ export const HELP = `
|
|
|
215
218
|
--core=<spec> engine dependency, e.g. file:../rsc-kit/packages/core
|
|
216
219
|
--no-install skip installing dependencies
|
|
217
220
|
--no-git skip git init
|
|
218
|
-
|
|
221
|
+
--yes accept every default, ask nothing
|
|
222
|
+
(-y too, but \`bun create\` takes -y as its own)
|
|
219
223
|
-h, --help this
|
|
220
224
|
`;
|
package/dist/shadcn.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tell shadcn this is an RSC app: `"rsc": true` in components.json, so the
|
|
3
|
+
* components it adds that need the browser start with "use client".
|
|
4
|
+
*
|
|
5
|
+
* Its own init sees a plain Vite project and writes false. Returns whether
|
|
6
|
+
* the file was there to change.
|
|
7
|
+
*/
|
|
8
|
+
export declare function markRsc(path: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* shadcn/ui for an rsc-kit app, new or existing.
|
|
11
|
+
*
|
|
12
|
+
* Already set up - a components.json is there, from a Next app say - it is
|
|
13
|
+
* only marked RSC. Otherwise shadcn's own init runs, once dependencies are
|
|
14
|
+
* installed, and its result is marked: its CLI sees a plain Vite project and
|
|
15
|
+
* writes "rsc": false, so a dialog or a dropdown it adds later has no
|
|
16
|
+
* "use client" and fails to render as a server component.
|
|
17
|
+
*
|
|
18
|
+
* Its questions are its own - the component library, the style - so it asks
|
|
19
|
+
* them in this terminal, unless the run was told to ask nothing, in which
|
|
20
|
+
* case it takes its defaults too.
|
|
21
|
+
*/
|
|
22
|
+
export declare function setUpShadcn(o: {
|
|
23
|
+
dir: string;
|
|
24
|
+
host: string;
|
|
25
|
+
unattended: boolean;
|
|
26
|
+
installed: boolean;
|
|
27
|
+
}): void;
|
package/dist/shadcn.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { stdout } from 'node:process';
|
|
5
|
+
import { bold, cyan, dim } from './prompt.js';
|
|
6
|
+
/**
|
|
7
|
+
* Tell shadcn this is an RSC app: `"rsc": true` in components.json, so the
|
|
8
|
+
* components it adds that need the browser start with "use client".
|
|
9
|
+
*
|
|
10
|
+
* Its own init sees a plain Vite project and writes false. Returns whether
|
|
11
|
+
* the file was there to change.
|
|
12
|
+
*/
|
|
13
|
+
export function markRsc(path) {
|
|
14
|
+
let config;
|
|
15
|
+
try {
|
|
16
|
+
config = JSON.parse(readFileSync(path, 'utf-8'));
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (config.rsc === true)
|
|
22
|
+
return true;
|
|
23
|
+
config.rsc = true;
|
|
24
|
+
writeFileSync(path, JSON.stringify(config, null, 2) + '\n');
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* shadcn/ui for an rsc-kit app, new or existing.
|
|
29
|
+
*
|
|
30
|
+
* Already set up - a components.json is there, from a Next app say - it is
|
|
31
|
+
* only marked RSC. Otherwise shadcn's own init runs, once dependencies are
|
|
32
|
+
* installed, and its result is marked: its CLI sees a plain Vite project and
|
|
33
|
+
* writes "rsc": false, so a dialog or a dropdown it adds later has no
|
|
34
|
+
* "use client" and fails to render as a server component.
|
|
35
|
+
*
|
|
36
|
+
* Its questions are its own - the component library, the style - so it asks
|
|
37
|
+
* them in this terminal, unless the run was told to ask nothing, in which
|
|
38
|
+
* case it takes its defaults too.
|
|
39
|
+
*/
|
|
40
|
+
export function setUpShadcn(o) {
|
|
41
|
+
const config = join(o.dir, 'components.json');
|
|
42
|
+
if (existsSync(config)) {
|
|
43
|
+
markRsc(config);
|
|
44
|
+
stdout.write(`\n ${cyan('~')} components.json ${dim('— "rsc": true, so shadcn adds "use client" where it is needed')}\n`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const [runner, ...prefix] = o.host === 'node' ? ['npx', '--yes'] : ['bunx', '--bun'];
|
|
48
|
+
const init = [...prefix, 'shadcn@latest', 'init', ...(o.unattended ? ['--defaults', '--yes'] : [])];
|
|
49
|
+
if (!o.installed || spawnSync(runner, init, { cwd: o.dir, stdio: 'inherit' }).status !== 0) {
|
|
50
|
+
stdout.write(`\n${bold('shadcn/ui is not set up.')} Once dependencies are installed:\n` +
|
|
51
|
+
` ${cyan([runner, ...init].join(' '))}\n` +
|
|
52
|
+
` then set ${cyan('"rsc": true')} in components.json\n`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
markRsc(config);
|
|
56
|
+
}
|
package/dist/templates.js
CHANGED
|
@@ -50,13 +50,13 @@ export const configFile = (_o) => 'vite.config.ts';
|
|
|
50
50
|
*/
|
|
51
51
|
export function scripts(o) {
|
|
52
52
|
if (o.host === 'laravel') {
|
|
53
|
-
const actions = 'php artisan rsc:action-manifest';
|
|
54
53
|
return {
|
|
55
54
|
// The ordinary names. A Laravel application already has `dev` and
|
|
56
|
-
// `build`, and init combines rather than replaces
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
// `build`, and init combines rather than replaces. PHP's manifest is
|
|
56
|
+
// not here: vite.config.ts runs it (hostManifest), so no script that
|
|
57
|
+
// starts Vite can skip it.
|
|
58
|
+
dev: 'vite',
|
|
59
|
+
build: 'vite build',
|
|
60
60
|
// What the build wrote. There is no server file to start any more.
|
|
61
61
|
start: 'bun .output/server/index.mjs',
|
|
62
62
|
};
|
|
@@ -218,6 +218,9 @@ export function viteConfig(o) {
|
|
|
218
218
|
`sourceDir: '${p.sourceDir}'`,
|
|
219
219
|
`outDir: '${p.outDir}'`,
|
|
220
220
|
...(p.hotFile ? [`hotFile: '${p.hotFile}'`] : []),
|
|
221
|
+
// The actions and functions PHP offers, written as dev and every build
|
|
222
|
+
// start - reflection through Composer's autoloader, which only PHP has.
|
|
223
|
+
...(o.host === 'laravel' ? ["hostManifest: { command: ['php', 'artisan', 'rsc:host-manifest'] }"] : []),
|
|
221
224
|
// A service worker: the app survives a reload with no network. Off
|
|
222
225
|
// unless asked for, because it outlives the code that installed it.
|
|
223
226
|
...(o.pwa ? ['offline: true'] : []),
|