create-sprinkles 0.2.3

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.
Files changed (59) hide show
  1. package/dist/bin.mjs +295 -0
  2. package/dist/index.d.mts +46 -0
  3. package/dist/index.mjs +180 -0
  4. package/package.json +45 -0
  5. package/templates/react-router-convex/.env.local +2 -0
  6. package/templates/react-router-convex/convex/schema.ts +9 -0
  7. package/templates/react-router-rsc/app/home.tsx.hbs +31 -0
  8. package/templates/react-router-rsc/app/root.tsx.hbs +55 -0
  9. package/templates/react-router-rsc/tsconfig.json.hbs +31 -0
  10. package/templates/react-router-rsc/workers/entry.rsc.tsx +44 -0
  11. package/templates/react-router-rsc/workers/entry.ssr.tsx +41 -0
  12. package/templates/react-router-rsc/wrangler.rsc.jsonc.hbs +25 -0
  13. package/templates/react-router-rsc/wrangler.ssr.jsonc.hbs +14 -0
  14. package/templates/react-router-rsc-content-layer/app/content.config.ts.hbs +26 -0
  15. package/templates/react-router-rsc-content-layer/content-layer/api.ts +350 -0
  16. package/templates/react-router-rsc-content-layer/content-layer/codegen.ts +89 -0
  17. package/templates/react-router-rsc-content-layer/content-layer/config.ts +20 -0
  18. package/templates/react-router-rsc-content-layer/content-layer/digest.ts +6 -0
  19. package/templates/react-router-rsc-content-layer/content-layer/frontmatter.ts +19 -0
  20. package/templates/react-router-rsc-content-layer/content-layer/loaders/file.ts +55 -0
  21. package/templates/react-router-rsc-content-layer/content-layer/loaders/glob.ts +82 -0
  22. package/templates/react-router-rsc-content-layer/content-layer/loaders/index.ts +2 -0
  23. package/templates/react-router-rsc-content-layer/content-layer/plugin.ts +419 -0
  24. package/templates/react-router-rsc-content-layer/content-layer/resolve-hook.js +12 -0
  25. package/templates/react-router-rsc-content-layer/content-layer/runtime.ts +73 -0
  26. package/templates/react-router-rsc-content-layer/content-layer/store.ts +59 -0
  27. package/templates/react-router-spa/app/home.tsx.hbs +7 -0
  28. package/templates/react-router-spa/app/root.tsx.hbs +60 -0
  29. package/templates/react-router-spa/tsconfig.json.hbs +26 -0
  30. package/templates/react-router-spa/wrangler.jsonc.hbs +9 -0
  31. package/templates/react-router-ssr/app/home.tsx.hbs +21 -0
  32. package/templates/react-router-ssr/app/root.tsx.hbs +105 -0
  33. package/templates/react-router-ssr/convex/schema.ts +7 -0
  34. package/templates/react-router-ssr/tsconfig.json.hbs +28 -0
  35. package/templates/react-router-ssr/wrangler.jsonc.hbs +13 -0
  36. package/templates/react-router-ssr-convex/app/lib/client.ts +19 -0
  37. package/templates/react-router-ssr-convex/app/tanstack-query-integration/middleware.ts +18 -0
  38. package/templates/react-router-ssr-convex/app/tanstack-query-integration/query-preloader.ts +125 -0
  39. package/templates/react-shared/app/routes.ts.hbs +3 -0
  40. package/templates/react-shared/app/styles/tailwind.css +1 -0
  41. package/templates/react-shared/react-compiler.plugin.ts.hbs +10 -0
  42. package/templates/react-shared/react-router.config.ts.hbs +9 -0
  43. package/templates/shared/.gitignore.hbs +23 -0
  44. package/templates/shared/.node-version +1 -0
  45. package/templates/shared/.vscode/extensions.json.hbs +8 -0
  46. package/templates/shared/.vscode/settings.json.hbs +72 -0
  47. package/templates/shared/AGENTS.md.hbs +599 -0
  48. package/templates/shared/README.md.hbs +24 -0
  49. package/templates/shared/package.json.hbs +41 -0
  50. package/templates/shared/vite.config.ts.hbs +384 -0
  51. package/templates/ts-package/src/index.ts +3 -0
  52. package/templates/ts-package/tests/index.test.ts +9 -0
  53. package/templates/ts-package/tsconfig.json +18 -0
  54. package/templates/ts-package-cli/bin/index.ts.hbs +1 -0
  55. package/templates/ts-package-cli/src/cli.ts.hbs +37 -0
  56. package/templates/ts-package-generator/bin/create.ts.hbs +2 -0
  57. package/templates/ts-package-generator/src/template.ts.hbs +22 -0
  58. package/templates/ts-package-sea/sea-config.json.hbs +2 -0
  59. package/templates/ts-package-sea/src/sea-entry.ts.hbs +4 -0
package/dist/bin.mjs ADDED
@@ -0,0 +1,295 @@
1
+ #!/usr/bin/env node
2
+ import * as prompts from "@clack/prompts";
3
+ import { createTemplate, runTemplate } from "bingo";
4
+ import path from "node:path";
5
+ import { handlebars } from "bingo-handlebars";
6
+ import { z } from "zod";
7
+ //#region src/context.ts
8
+ function buildContext(opts) {
9
+ const isSPA = opts.kind === "react-router-spa";
10
+ const isSSR = opts.kind === "react-router-ssr";
11
+ const isRSC = opts.kind === "react-router-rsc";
12
+ return {
13
+ ...opts,
14
+ hasContentLayer: isRSC && Boolean(opts.contentLayer),
15
+ hasConvex: (isSPA || isSSR) && Boolean(opts.convex),
16
+ isPackage: opts.kind === "ts-package",
17
+ isRSC,
18
+ isReactRouter: opts.kind !== "ts-package",
19
+ isSPA,
20
+ isSSR,
21
+ ssr: isSSR || isRSC
22
+ };
23
+ }
24
+ //#endregion
25
+ //#region src/merge.ts
26
+ function isDirectory(value) {
27
+ return typeof value === "object" && value !== null && !Array.isArray(value);
28
+ }
29
+ function mergeFiles(...layers) {
30
+ const result = {};
31
+ for (const layer of layers) if (layer) for (const [key, value] of Object.entries(layer)) {
32
+ const existing = result[key];
33
+ if (isDirectory(existing) && isDirectory(value)) result[key] = mergeFiles(existing, value);
34
+ else result[key] = value;
35
+ }
36
+ return result;
37
+ }
38
+ //#endregion
39
+ //#region src/options.ts
40
+ const kind$1 = z.enum([
41
+ "react-router-spa",
42
+ "react-router-ssr",
43
+ "react-router-rsc",
44
+ "ts-package"
45
+ ]).describe("project kind");
46
+ const options = {
47
+ cli: z.boolean().optional(),
48
+ contentLayer: z.boolean().optional(),
49
+ convex: z.boolean().optional(),
50
+ generator: z.boolean().optional(),
51
+ kind: kind$1,
52
+ owner: z.string().describe("GitHub owner or organization"),
53
+ repository: z.string().describe("repository name"),
54
+ sea: z.boolean().optional()
55
+ };
56
+ //#endregion
57
+ //#region src/scripts.ts
58
+ function buildDependencyCommands(context) {
59
+ const commands = [];
60
+ commands.push("vp add -D @types/node @typescript/native-preview");
61
+ if (context.isReactRouter) {
62
+ commands.push("vp add react react-dom react-router");
63
+ commands.push("vp add -D @types/react @types/react-dom @react-router/dev @tailwindcss/vite tailwindcss vite-plugin-devtools-json");
64
+ commands.push("vp add -D @rolldown/plugin-babel @vitejs/plugin-react babel-plugin-react-compiler");
65
+ commands.push("vp add -D @cloudflare/vite-plugin wrangler");
66
+ commands.push("vp add -D eslint-plugin-perfectionist eslint-plugin-react-hooks");
67
+ }
68
+ if (context.isReactRouter) commands.push("vp add @react-router/node isbot");
69
+ if (context.hasConvex) commands.push("vp add convex @convex-dev/react-query @tanstack/react-query");
70
+ if (context.isRSC) commands.push("vp add -D @vitejs/plugin-rsc");
71
+ if (context.hasContentLayer) {
72
+ commands.push("vp add jsr:@std/jsonc jsr:@std/yaml gray-matter github-slugger @remix-run/data-schema");
73
+ commands.push("vp add -D @mdx-js/rollup");
74
+ }
75
+ if (context.isPackage) {
76
+ if (context.cli) commands.push("vp add @bomb.sh/args");
77
+ if (context.generator) commands.push("vp add bingo bingo-handlebars zod");
78
+ }
79
+ commands.push("vp add -D vite-plus vite@npm:@voidzero-dev/vite-plus-core@latest vitest@npm:@voidzero-dev/vite-plus-test@latest");
80
+ return commands;
81
+ }
82
+ function buildScripts(context) {
83
+ const scripts = [];
84
+ const phase0Commands = [...buildDependencyCommands(context), "vp install"];
85
+ scripts.push({
86
+ commands: phase0Commands,
87
+ phase: 0
88
+ });
89
+ if (context.isRSC) scripts.push({
90
+ commands: ["vpx wrangler types -c wrangler.rsc.jsonc"],
91
+ phase: 1
92
+ });
93
+ else if (context.isSSR) scripts.push({
94
+ commands: ["vpx wrangler types"],
95
+ phase: 1
96
+ });
97
+ scripts.push({
98
+ commands: ["vp fmt"],
99
+ phase: 2
100
+ });
101
+ if (context.hasConvex) scripts.push({
102
+ commands: ["vpx convex dev --once"],
103
+ phase: 3,
104
+ silent: true
105
+ });
106
+ scripts.push({
107
+ commands: [
108
+ "ln -sf AGENTS.md CLAUDE.md",
109
+ "git init",
110
+ "git add -A",
111
+ "git commit -m initial"
112
+ ],
113
+ phase: 4
114
+ });
115
+ return scripts;
116
+ }
117
+ //#endregion
118
+ //#region src/suggestions.ts
119
+ function buildSuggestions(context) {
120
+ const suggestions = [];
121
+ if (context.hasConvex) suggestions.push("Open the Convex dashboard: https://dashboard.convex.dev");
122
+ if (context.isSSR || context.isRSC) suggestions.push("Log in to Cloudflare: vpx wrangler login");
123
+ if (context.isReactRouter) suggestions.push("Start the dev server: vp dev");
124
+ else suggestions.push("Start development: vp run dev");
125
+ return suggestions;
126
+ }
127
+ //#endregion
128
+ //#region src/template.ts
129
+ const templatesDir = path.join(import.meta.dirname, "../templates");
130
+ const kindDirectories = {
131
+ "react-router-rsc": "react-router-rsc",
132
+ "react-router-spa": "react-router-spa",
133
+ "react-router-ssr": "react-router-ssr",
134
+ "ts-package": "ts-package"
135
+ };
136
+ async function tryHandlebars(dir, context) {
137
+ try {
138
+ return await handlebars(path.join(templatesDir, dir), context) || null;
139
+ } catch {
140
+ return null;
141
+ }
142
+ }
143
+ async function collectAddonLayers(context) {
144
+ const addons = [];
145
+ if (context.isPackage && context.cli) addons.push(await tryHandlebars("ts-package-cli", context));
146
+ if (context.isPackage && context.generator) addons.push(await tryHandlebars("ts-package-generator", context));
147
+ if (context.isPackage && context.sea) addons.push(await tryHandlebars("ts-package-sea", context));
148
+ if (context.hasConvex) addons.push(await tryHandlebars("react-router-convex", context));
149
+ if (context.isSSR && context.hasConvex) addons.push(await tryHandlebars("react-router-ssr-convex", context));
150
+ if (context.hasContentLayer) addons.push(await tryHandlebars("react-router-rsc-content-layer", context));
151
+ return addons;
152
+ }
153
+ async function buildLayers(context) {
154
+ const shared = await tryHandlebars("shared", context);
155
+ let reactShared = null;
156
+ if (context.isReactRouter) reactShared = await tryHandlebars("react-shared", context);
157
+ const kindDir = kindDirectories[context.kind];
158
+ let kindSpecific = null;
159
+ if (kindDir) kindSpecific = await tryHandlebars(kindDir, context);
160
+ const addons = await collectAddonLayers(context);
161
+ return mergeFiles(shared, reactShared, kindSpecific, ...addons);
162
+ }
163
+ var template_default = createTemplate({
164
+ about: {
165
+ description: "Get started with development by creating projects from templates quickly.",
166
+ name: NAME
167
+ },
168
+ options,
169
+ async produce({ options: opts }) {
170
+ const context = buildContext(opts);
171
+ return {
172
+ files: await buildLayers(context),
173
+ scripts: buildScripts(context),
174
+ suggestions: buildSuggestions(context)
175
+ };
176
+ }
177
+ });
178
+ //#endregion
179
+ //#region src/index.ts
180
+ const NAME = "create-sprinkles";
181
+ //#endregion
182
+ //#region bin/index.ts
183
+ prompts.intro(NAME);
184
+ const directory = await prompts.text({
185
+ message: "Where should we create the project?",
186
+ placeholder: "./my-project",
187
+ validate: (value) => {
188
+ if (!value) return "Directory is required";
189
+ }
190
+ });
191
+ if (prompts.isCancel(directory)) process.exit(0);
192
+ const kind = await prompts.select({
193
+ message: "What kind of project?",
194
+ options: [
195
+ {
196
+ label: "React Router — SPA",
197
+ value: "react-router-spa"
198
+ },
199
+ {
200
+ label: "React Router — SSR",
201
+ value: "react-router-ssr"
202
+ },
203
+ {
204
+ label: "React Router — RSC",
205
+ value: "react-router-rsc"
206
+ },
207
+ {
208
+ label: "TypeScript Package",
209
+ value: "ts-package"
210
+ }
211
+ ]
212
+ });
213
+ if (prompts.isCancel(kind)) process.exit(0);
214
+ let convex = false;
215
+ let contentLayer = false;
216
+ let cli = false;
217
+ let generator = false;
218
+ let sea = false;
219
+ if (kind === "react-router-spa" || kind === "react-router-ssr") {
220
+ const answer = await prompts.confirm({
221
+ initialValue: false,
222
+ message: "Include Convex backend?"
223
+ });
224
+ if (prompts.isCancel(answer)) process.exit(0);
225
+ convex = answer;
226
+ }
227
+ if (kind === "react-router-rsc") {
228
+ const answer = await prompts.confirm({
229
+ initialValue: false,
230
+ message: "Include content-layer plugin?"
231
+ });
232
+ if (prompts.isCancel(answer)) process.exit(0);
233
+ contentLayer = answer;
234
+ }
235
+ if (kind === "ts-package") {
236
+ const features = await prompts.multiselect({
237
+ message: "Include optional features?",
238
+ options: [
239
+ {
240
+ label: "CLI scaffold",
241
+ value: "cli"
242
+ },
243
+ {
244
+ label: "Bingo generator scaffold",
245
+ value: "generator"
246
+ },
247
+ {
248
+ label: "Single Executable Application (SEA)",
249
+ value: "sea"
250
+ }
251
+ ],
252
+ required: false
253
+ });
254
+ if (prompts.isCancel(features)) process.exit(0);
255
+ cli = features.includes("cli");
256
+ generator = features.includes("generator");
257
+ sea = features.includes("sea");
258
+ }
259
+ const resolvedDir = path.resolve(directory);
260
+ const repository = path.basename(resolvedDir);
261
+ const owner = await prompts.text({
262
+ message: "GitHub owner or organization?",
263
+ placeholder: "my-org",
264
+ validate: (value) => {
265
+ if (!value) return "Owner is required";
266
+ }
267
+ });
268
+ if (prompts.isCancel(owner)) process.exit(0);
269
+ const spinner = prompts.spinner();
270
+ spinner.start("Scaffolding project...");
271
+ try {
272
+ const creation = await runTemplate(template_default, {
273
+ directory: resolvedDir,
274
+ mode: "setup",
275
+ options: {
276
+ cli,
277
+ contentLayer,
278
+ convex,
279
+ generator,
280
+ kind,
281
+ owner,
282
+ repository,
283
+ sea
284
+ }
285
+ });
286
+ spinner.stop("Project scaffolded!");
287
+ if (creation.suggestions?.length) prompts.note(creation.suggestions.join("\n"), "Next steps");
288
+ prompts.outro(`Created ${repository} at ${resolvedDir}`);
289
+ } catch (error) {
290
+ spinner.stop("Failed to scaffold project");
291
+ prompts.log.error(String(error));
292
+ process.exit(1);
293
+ }
294
+ //#endregion
295
+ export {};
@@ -0,0 +1,46 @@
1
+ import * as bingo from "bingo";
2
+ import * as zod from "zod";
3
+ import { z } from "zod";
4
+
5
+ //#region src/template.d.ts
6
+ declare const _default: bingo.Template<{
7
+ cli: zod.ZodOptional<zod.ZodBoolean>;
8
+ contentLayer: zod.ZodOptional<zod.ZodBoolean>;
9
+ convex: zod.ZodOptional<zod.ZodBoolean>;
10
+ generator: zod.ZodOptional<zod.ZodBoolean>;
11
+ kind: zod.ZodEnum<["react-router-spa", "react-router-ssr", "react-router-rsc", "ts-package"]>;
12
+ owner: zod.ZodString;
13
+ repository: zod.ZodString;
14
+ sea: zod.ZodOptional<zod.ZodBoolean>;
15
+ }, unknown>;
16
+ //#endregion
17
+ //#region src/options.d.ts
18
+ declare const options: {
19
+ cli: z.ZodOptional<z.ZodBoolean>;
20
+ contentLayer: z.ZodOptional<z.ZodBoolean>;
21
+ convex: z.ZodOptional<z.ZodBoolean>;
22
+ generator: z.ZodOptional<z.ZodBoolean>;
23
+ kind: z.ZodEnum<["react-router-spa", "react-router-ssr", "react-router-rsc", "ts-package"]>;
24
+ owner: z.ZodString;
25
+ repository: z.ZodString;
26
+ sea: z.ZodOptional<z.ZodBoolean>;
27
+ };
28
+ //#endregion
29
+ //#region src/context.d.ts
30
+ type Options = { [Key in keyof typeof options]: z.infer<(typeof options)[Key]> };
31
+ interface TemplateContext extends Options {
32
+ isSPA: boolean;
33
+ isSSR: boolean;
34
+ isRSC: boolean;
35
+ isPackage: boolean;
36
+ isReactRouter: boolean;
37
+ hasConvex: boolean;
38
+ hasContentLayer: boolean;
39
+ ssr: boolean;
40
+ }
41
+ declare function buildContext(opts: Options): TemplateContext;
42
+ //#endregion
43
+ //#region src/index.d.ts
44
+ declare const NAME = "create-sprinkles";
45
+ //#endregion
46
+ export { NAME, type TemplateContext, buildContext, options, _default as template };
package/dist/index.mjs ADDED
@@ -0,0 +1,180 @@
1
+ import { createTemplate } from "bingo";
2
+ import { handlebars } from "bingo-handlebars";
3
+ import path from "node:path";
4
+ import { z } from "zod";
5
+ //#region src/context.ts
6
+ function buildContext(opts) {
7
+ const isSPA = opts.kind === "react-router-spa";
8
+ const isSSR = opts.kind === "react-router-ssr";
9
+ const isRSC = opts.kind === "react-router-rsc";
10
+ return {
11
+ ...opts,
12
+ hasContentLayer: isRSC && Boolean(opts.contentLayer),
13
+ hasConvex: (isSPA || isSSR) && Boolean(opts.convex),
14
+ isPackage: opts.kind === "ts-package",
15
+ isRSC,
16
+ isReactRouter: opts.kind !== "ts-package",
17
+ isSPA,
18
+ isSSR,
19
+ ssr: isSSR || isRSC
20
+ };
21
+ }
22
+ //#endregion
23
+ //#region src/merge.ts
24
+ function isDirectory(value) {
25
+ return typeof value === "object" && value !== null && !Array.isArray(value);
26
+ }
27
+ function mergeFiles(...layers) {
28
+ const result = {};
29
+ for (const layer of layers) if (layer) for (const [key, value] of Object.entries(layer)) {
30
+ const existing = result[key];
31
+ if (isDirectory(existing) && isDirectory(value)) result[key] = mergeFiles(existing, value);
32
+ else result[key] = value;
33
+ }
34
+ return result;
35
+ }
36
+ //#endregion
37
+ //#region src/options.ts
38
+ const kind = z.enum([
39
+ "react-router-spa",
40
+ "react-router-ssr",
41
+ "react-router-rsc",
42
+ "ts-package"
43
+ ]).describe("project kind");
44
+ const options = {
45
+ cli: z.boolean().optional(),
46
+ contentLayer: z.boolean().optional(),
47
+ convex: z.boolean().optional(),
48
+ generator: z.boolean().optional(),
49
+ kind,
50
+ owner: z.string().describe("GitHub owner or organization"),
51
+ repository: z.string().describe("repository name"),
52
+ sea: z.boolean().optional()
53
+ };
54
+ //#endregion
55
+ //#region src/scripts.ts
56
+ function buildDependencyCommands(context) {
57
+ const commands = [];
58
+ commands.push("vp add -D @types/node @typescript/native-preview");
59
+ if (context.isReactRouter) {
60
+ commands.push("vp add react react-dom react-router");
61
+ commands.push("vp add -D @types/react @types/react-dom @react-router/dev @tailwindcss/vite tailwindcss vite-plugin-devtools-json");
62
+ commands.push("vp add -D @rolldown/plugin-babel @vitejs/plugin-react babel-plugin-react-compiler");
63
+ commands.push("vp add -D @cloudflare/vite-plugin wrangler");
64
+ commands.push("vp add -D eslint-plugin-perfectionist eslint-plugin-react-hooks");
65
+ }
66
+ if (context.isReactRouter) commands.push("vp add @react-router/node isbot");
67
+ if (context.hasConvex) commands.push("vp add convex @convex-dev/react-query @tanstack/react-query");
68
+ if (context.isRSC) commands.push("vp add -D @vitejs/plugin-rsc");
69
+ if (context.hasContentLayer) {
70
+ commands.push("vp add jsr:@std/jsonc jsr:@std/yaml gray-matter github-slugger @remix-run/data-schema");
71
+ commands.push("vp add -D @mdx-js/rollup");
72
+ }
73
+ if (context.isPackage) {
74
+ if (context.cli) commands.push("vp add @bomb.sh/args");
75
+ if (context.generator) commands.push("vp add bingo bingo-handlebars zod");
76
+ }
77
+ commands.push("vp add -D vite-plus vite@npm:@voidzero-dev/vite-plus-core@latest vitest@npm:@voidzero-dev/vite-plus-test@latest");
78
+ return commands;
79
+ }
80
+ function buildScripts(context) {
81
+ const scripts = [];
82
+ const phase0Commands = [...buildDependencyCommands(context), "vp install"];
83
+ scripts.push({
84
+ commands: phase0Commands,
85
+ phase: 0
86
+ });
87
+ if (context.isRSC) scripts.push({
88
+ commands: ["vpx wrangler types -c wrangler.rsc.jsonc"],
89
+ phase: 1
90
+ });
91
+ else if (context.isSSR) scripts.push({
92
+ commands: ["vpx wrangler types"],
93
+ phase: 1
94
+ });
95
+ scripts.push({
96
+ commands: ["vp fmt"],
97
+ phase: 2
98
+ });
99
+ if (context.hasConvex) scripts.push({
100
+ commands: ["vpx convex dev --once"],
101
+ phase: 3,
102
+ silent: true
103
+ });
104
+ scripts.push({
105
+ commands: [
106
+ "ln -sf AGENTS.md CLAUDE.md",
107
+ "git init",
108
+ "git add -A",
109
+ "git commit -m initial"
110
+ ],
111
+ phase: 4
112
+ });
113
+ return scripts;
114
+ }
115
+ //#endregion
116
+ //#region src/suggestions.ts
117
+ function buildSuggestions(context) {
118
+ const suggestions = [];
119
+ if (context.hasConvex) suggestions.push("Open the Convex dashboard: https://dashboard.convex.dev");
120
+ if (context.isSSR || context.isRSC) suggestions.push("Log in to Cloudflare: vpx wrangler login");
121
+ if (context.isReactRouter) suggestions.push("Start the dev server: vp dev");
122
+ else suggestions.push("Start development: vp run dev");
123
+ return suggestions;
124
+ }
125
+ //#endregion
126
+ //#region src/template.ts
127
+ const templatesDir = path.join(import.meta.dirname, "../templates");
128
+ const kindDirectories = {
129
+ "react-router-rsc": "react-router-rsc",
130
+ "react-router-spa": "react-router-spa",
131
+ "react-router-ssr": "react-router-ssr",
132
+ "ts-package": "ts-package"
133
+ };
134
+ async function tryHandlebars(dir, context) {
135
+ try {
136
+ return await handlebars(path.join(templatesDir, dir), context) || null;
137
+ } catch {
138
+ return null;
139
+ }
140
+ }
141
+ async function collectAddonLayers(context) {
142
+ const addons = [];
143
+ if (context.isPackage && context.cli) addons.push(await tryHandlebars("ts-package-cli", context));
144
+ if (context.isPackage && context.generator) addons.push(await tryHandlebars("ts-package-generator", context));
145
+ if (context.isPackage && context.sea) addons.push(await tryHandlebars("ts-package-sea", context));
146
+ if (context.hasConvex) addons.push(await tryHandlebars("react-router-convex", context));
147
+ if (context.isSSR && context.hasConvex) addons.push(await tryHandlebars("react-router-ssr-convex", context));
148
+ if (context.hasContentLayer) addons.push(await tryHandlebars("react-router-rsc-content-layer", context));
149
+ return addons;
150
+ }
151
+ async function buildLayers(context) {
152
+ const shared = await tryHandlebars("shared", context);
153
+ let reactShared = null;
154
+ if (context.isReactRouter) reactShared = await tryHandlebars("react-shared", context);
155
+ const kindDir = kindDirectories[context.kind];
156
+ let kindSpecific = null;
157
+ if (kindDir) kindSpecific = await tryHandlebars(kindDir, context);
158
+ const addons = await collectAddonLayers(context);
159
+ return mergeFiles(shared, reactShared, kindSpecific, ...addons);
160
+ }
161
+ var template_default = createTemplate({
162
+ about: {
163
+ description: "Get started with development by creating projects from templates quickly.",
164
+ name: NAME
165
+ },
166
+ options,
167
+ async produce({ options: opts }) {
168
+ const context = buildContext(opts);
169
+ return {
170
+ files: await buildLayers(context),
171
+ scripts: buildScripts(context),
172
+ suggestions: buildSuggestions(context)
173
+ };
174
+ }
175
+ });
176
+ //#endregion
177
+ //#region src/index.ts
178
+ const NAME = "create-sprinkles";
179
+ //#endregion
180
+ export { NAME, buildContext, options, template_default as template };
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "create-sprinkles",
3
+ "version": "0.2.3",
4
+ "description": "Get started with development by creating projects from templates quickly.",
5
+ "homepage": "https://github.com/withsprinkles/workbench#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/withsprinkles/workbench/issues"
8
+ },
9
+ "license": "MIT",
10
+ "author": "Mark Malstrom <mark@malstrom.me>",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/withsprinkles/workbench.git"
14
+ },
15
+ "bin": {
16
+ "create-sprinkles": "dist/bin.mjs"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "templates"
21
+ ],
22
+ "type": "module",
23
+ "types": "./dist/index.d.mts",
24
+ "exports": {
25
+ ".": "./dist/index.mjs",
26
+ "./package.json": "./package.json"
27
+ },
28
+ "dependencies": {
29
+ "@bomb.sh/args": "^0.3.1",
30
+ "@clack/prompts": "^1.1.0",
31
+ "bingo": "^0.9.2",
32
+ "bingo-fs": "^0.5.6",
33
+ "bingo-handlebars": "^0.1.0",
34
+ "zod": "^3.25.76"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^25.3.5",
38
+ "@typescript/native-preview": "latest",
39
+ "bingo-testers": "^0.5.8",
40
+ "bumpp": "^10.4.1",
41
+ "vite-plus": "latest",
42
+ "vitest": "npm:@voidzero-dev/vite-plus-test@latest"
43
+ },
44
+ "scripts": {}
45
+ }
@@ -0,0 +1,2 @@
1
+ CONVEX_DEPLOYMENT=# Set by `npx convex dev`
2
+ VITE_CONVEX_URL=# Set by `npx convex dev`
@@ -0,0 +1,9 @@
1
+ import { defineSchema } from "convex/server";
2
+
3
+ export default defineSchema({
4
+ // Define your tables here
5
+ // Example:
6
+ // import { defineTable } from "convex/server";
7
+ // import { v } from "convex/values";
8
+ // messages: defineTable({ text: v.string() }),
9
+ });
@@ -0,0 +1,31 @@
1
+ {{#if hasContentLayer}}
2
+ import { getCollection } from "sprinkles:content";
3
+
4
+ export async function ServerComponent() {
5
+ let posts = await getCollection("posts");
6
+
7
+ return (
8
+ <main>
9
+ <h1>Welcome to {{repository}}</h1>
10
+ <section>
11
+ <h2>Posts</h2>
12
+ <ul>
13
+ {posts.map((post) => (
14
+ <li key={post.id}>
15
+ <h3>{post.data.title}</h3>
16
+ </li>
17
+ ))}
18
+ </ul>
19
+ </section>
20
+ </main>
21
+ );
22
+ }
23
+ {{else}}
24
+ export async function ServerComponent() {
25
+ return (
26
+ <main>
27
+ <h1>Welcome to {{repository}}</h1>
28
+ </main>
29
+ );
30
+ }
31
+ {{/if}}
@@ -0,0 +1,55 @@
1
+ import type { PropsWithChildren } from "react";
2
+ import { isRouteErrorResponse, Outlet } from "react-router";
3
+
4
+ import type { Route } from "./+types/root";
5
+ import styles from "./styles/tailwind.css?url";
6
+
7
+ export function Layout({ children }: PropsWithChildren) {
8
+ return (
9
+ <html lang="en">
10
+ <head>
11
+ <meta charSet="utf-8" />
12
+ <meta content="width=device-width, initial-scale=1" name="viewport" />
13
+ <meta content="#000000" name="theme-color" />
14
+ <link href={styles} rel="stylesheet" />
15
+ <title>{{repository}}</title>
16
+ </head>
17
+ <body>
18
+ {children}
19
+ </body>
20
+ </html>
21
+ );
22
+ }
23
+
24
+ export function ServerComponent() {
25
+ return <Outlet />;
26
+ }
27
+
28
+ export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
29
+ let message = "Oops!";
30
+ let details = "An unexpected error occurred.";
31
+ let stack: string | undefined;
32
+
33
+ if (isRouteErrorResponse(error)) {
34
+ message = error.status === 404 ? "404" : "Error";
35
+ details =
36
+ error.status === 404
37
+ ? "The requested page could not be found."
38
+ : error.statusText || details;
39
+ } else if (import.meta.env.DEV && error && error instanceof Error) {
40
+ details = error.message;
41
+ stack = error.stack;
42
+ }
43
+
44
+ return (
45
+ <main>
46
+ <h1>{message}</h1>
47
+ <p>{details}</p>
48
+ {stack && (
49
+ <pre>
50
+ <code>{stack}</code>
51
+ </pre>
52
+ )}
53
+ </main>
54
+ );
55
+ }