@lazarv/create-react-server 0.0.0-experimental-d003259-20250211-2495688e

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 (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/generator.mjs +195 -0
  4. package/globals.d.ts +59 -0
  5. package/index.mjs +87 -0
  6. package/launch.mjs +136 -0
  7. package/lib/code-merge.mjs +246 -0
  8. package/lib/dynamic-checkbox.mjs +259 -0
  9. package/lib/files.mjs +6 -0
  10. package/lib/formatter.mjs +16 -0
  11. package/lib/generate-name.mjs +220 -0
  12. package/lib/theme.mjs +56 -0
  13. package/logo.mjs +3 -0
  14. package/package.json +26 -0
  15. package/steps/alias.mjs +50 -0
  16. package/steps/authentication.mjs +62 -0
  17. package/steps/database.mjs +56 -0
  18. package/steps/deploy.mjs +151 -0
  19. package/steps/features.mjs +415 -0
  20. package/steps/host.mjs +40 -0
  21. package/steps/index.mjs +33 -0
  22. package/steps/integrations.mjs +104 -0
  23. package/steps/name.mjs +69 -0
  24. package/steps/package.mjs +104 -0
  25. package/steps/port.mjs +42 -0
  26. package/steps/preset.mjs +188 -0
  27. package/steps/state-management.mjs +68 -0
  28. package/steps/third-party.mjs +19 -0
  29. package/steps/ui.mjs +87 -0
  30. package/templates/.dockerignore +5 -0
  31. package/templates/.gitignore.template +19 -0
  32. package/templates/.prettierignore +3 -0
  33. package/templates/.prettierrc +11 -0
  34. package/templates/Dockerfile.npm +49 -0
  35. package/templates/Dockerfile.pnpm +71 -0
  36. package/templates/Dockerfile.yarn +71 -0
  37. package/templates/README.docker.md +15 -0
  38. package/templates/README.md +35 -0
  39. package/templates/blank/package.json +6 -0
  40. package/templates/blank/src/App.jsx +5 -0
  41. package/templates/blank-ts/package.json +6 -0
  42. package/templates/blank-ts/src/App.tsx +5 -0
  43. package/templates/eslint.config.template.mjs +98 -0
  44. package/templates/get-started/package.json +6 -0
  45. package/templates/get-started/src/App.jsx +60 -0
  46. package/templates/get-started/src/Button.jsx +14 -0
  47. package/templates/get-started/src/Confetti.jsx +19 -0
  48. package/templates/get-started/src/global.css +3 -0
  49. package/templates/get-started-ts/globals.d.ts +3 -0
  50. package/templates/get-started-ts/package.json +6 -0
  51. package/templates/get-started-ts/src/App.tsx +66 -0
  52. package/templates/get-started-ts/src/Button.tsx +18 -0
  53. package/templates/get-started-ts/src/Confetti.tsx +19 -0
  54. package/templates/get-started-ts/src/global.css +3 -0
  55. package/templates/nextjs/globals.d.ts +3 -0
  56. package/templates/nextjs/react-server.config.json +9 -0
  57. package/templates/nextjs/src/app/layout.tsx +38 -0
  58. package/templates/nextjs/src/app/page.tsx +33 -0
  59. package/templates/nextjs/src/components/Button.tsx +18 -0
  60. package/templates/nextjs/src/components/Confetti.tsx +19 -0
  61. package/templates/nextjs/src/global.css +3 -0
  62. package/templates/package.css.json +5 -0
  63. package/templates/package.eslint.json +19 -0
  64. package/templates/package.eslint.ts.json +7 -0
  65. package/templates/package.json +12 -0
  66. package/templates/package.lightningcss.json +6 -0
  67. package/templates/package.prettier.json +8 -0
  68. package/templates/package.react-compiler.json +7 -0
  69. package/templates/package.swc.json +5 -0
  70. package/templates/package.tailwind.json +7 -0
  71. package/templates/package.ts.json +11 -0
  72. package/templates/postcss.config.mjs +6 -0
  73. package/templates/router/globals.d.ts +3 -0
  74. package/templates/router/react-server.config.json +19 -0
  75. package/templates/router/src/app/@content/about.tsx +98 -0
  76. package/templates/router/src/app/@content/index.tsx +33 -0
  77. package/templates/router/src/app/layout.tsx +44 -0
  78. package/templates/router/src/app/page.tsx +17 -0
  79. package/templates/router/src/components/Button.tsx +18 -0
  80. package/templates/router/src/components/Confetti.tsx +19 -0
  81. package/templates/router/src/components/Navigation.tsx +31 -0
  82. package/templates/router/src/global.css +3 -0
  83. package/templates/shared/public/github.svg +4 -0
  84. package/templates/shared/public/react-server.svg +51 -0
  85. package/templates/tailwind.config.mjs +6 -0
  86. package/templates/tsconfig.css.json +9 -0
  87. package/templates/tsconfig.template.json +15 -0
  88. package/templates/vite.config.lightningcss.mjs +15 -0
  89. package/templates/vite.config.lightningcss.ts +15 -0
  90. package/templates/vite.config.react-compiler.mjs +19 -0
  91. package/templates/vite.config.react-compiler.ts +19 -0
  92. package/templates/vite.config.swc.mjs +6 -0
  93. package/templates/vite.config.swc.ts +6 -0
  94. package/templates/vite.config.ts +3 -0
  95. package/wizard.mjs +122 -0
@@ -0,0 +1,415 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+
4
+ import { checkbox, Separator } from "@inquirer/prompts";
5
+ import colors from "picocolors";
6
+
7
+ import { json } from "../lib/files.mjs";
8
+ import { createTheme } from "../lib/theme.mjs";
9
+
10
+ export default async (context) => {
11
+ const choices = [
12
+ {
13
+ selectedName: "TypeScript",
14
+ name: `TypeScript ${colors.yellow("(recommended)")}`,
15
+ value: "ts",
16
+ description: "Add TypeScript support",
17
+ checked: context.props.preset?.features.includes("ts"),
18
+ },
19
+ new Separator(),
20
+ {
21
+ name: "ESLint",
22
+ value: "eslint",
23
+ description: "Add ESLint support",
24
+ checked: context.props.preset?.features.includes("eslint"),
25
+ },
26
+ {
27
+ name: "Prettier",
28
+ value: "prettier",
29
+ description: "Add Prettier support",
30
+ checked: context.props.preset?.features.includes("prettier"),
31
+ },
32
+ {
33
+ name: "Tailwind CSS",
34
+ value: "tailwind",
35
+ description: "Add Tailwind CSS support",
36
+ checked: context.props.preset?.features.includes("tailwind"),
37
+ },
38
+ {
39
+ name: "Lightning CSS",
40
+ value: "lightningcss",
41
+ description: "Add Lightning CSS support",
42
+ checked: context.props.preset?.features.includes("lightningcss"),
43
+ },
44
+ {
45
+ name: "CSS Modules",
46
+ value: "css-modules",
47
+ description: "Add CSS Modules support",
48
+ checked: context.props.preset?.features.includes("css-modules"),
49
+ },
50
+ {
51
+ name: "React Compiler",
52
+ value: "react-compiler",
53
+ description: "Add React Compiler support",
54
+ checked: context.props.preset?.features.includes("react-compiler"),
55
+ },
56
+ {
57
+ name: "React SWC",
58
+ value: "react-swc",
59
+ description: "Use SWC for faster builds",
60
+ checked: context.props.preset?.features.includes("react-swc"),
61
+ },
62
+ new Separator(),
63
+ {
64
+ name: "Git",
65
+ value: "git",
66
+ description: "Initialize a Git repository",
67
+ checked: context.props.preset?.features.includes("git"),
68
+ },
69
+ new Separator(),
70
+ ].map((choice) => ({
71
+ ...choice,
72
+ disabled: context.props.preset?.features.includes(choice.value),
73
+ }));
74
+
75
+ const theme = createTheme("info", {
76
+ ...context,
77
+ env: {
78
+ ...context.env,
79
+ style: {
80
+ disabledChoice(text) {
81
+ const raw = text.replace("(disabled)", "").trim();
82
+ const choice = choices.find((choice) => choice.name === raw);
83
+ const isPresetFeature = context.props.preset?.features?.includes(
84
+ choice?.value
85
+ );
86
+ const message = ` ${isPresetFeature ? "✅" : " "} ${isPresetFeature ? colors.whiteBright(raw) : raw} ${colors.gray(`(${choice.reason ?? (isPresetFeature ? "preset" : "disabled")})`)}`;
87
+ return isPresetFeature ? message : colors.gray(message);
88
+ },
89
+ },
90
+ features: choices
91
+ .filter((choice) => choice.type !== "separator")
92
+ .reduce((features, choice) => {
93
+ features[choice.value] = choice;
94
+ return features;
95
+ }, {}),
96
+ },
97
+ });
98
+
99
+ const answer = [
100
+ ...(context.props.preset?.features ?? []),
101
+ ...(context.env.options.features?.split(",") ??
102
+ (!context.props.custom || context.env.hasOptions
103
+ ? context.props.preset.features ?? []
104
+ : await checkbox(
105
+ {
106
+ message: "Enabled features",
107
+ choices,
108
+ pageSize: choices.length - 2,
109
+ theme,
110
+ },
111
+ context
112
+ ))),
113
+ ];
114
+
115
+ const partials = {
116
+ ...context.partials,
117
+ };
118
+ const props = {
119
+ ...context.props,
120
+ };
121
+ const files = context.files;
122
+
123
+ if (answer.includes("eslint")) {
124
+ partials["package.json"] = {
125
+ ...partials["package.json"],
126
+ merge: [
127
+ ...partials["package.json"].merge,
128
+ await json(context.env.templateDir, "package.eslint.json"),
129
+ ...(answer.includes("ts")
130
+ ? [await json(context.env.templateDir, "package.eslint.ts.json")]
131
+ : []),
132
+ ],
133
+ };
134
+
135
+ partials["eslint.config.mjs"] = {
136
+ type: "text",
137
+ format: "babel",
138
+ template: await readFile(
139
+ join(context.env.templateDir, "eslint.config.template.mjs"),
140
+ "utf8"
141
+ ),
142
+ };
143
+ }
144
+
145
+ if (answer.includes("ts")) {
146
+ partials["tsconfig.json"] = {
147
+ type: "json",
148
+ merge: [await json(context.env.templateDir, "tsconfig.template.json")],
149
+ };
150
+
151
+ partials["package.json"] = {
152
+ ...partials["package.json"],
153
+ merge: [
154
+ ...partials["package.json"].merge,
155
+ await json(context.env.templateDir, "package.ts.json"),
156
+ ],
157
+ };
158
+
159
+ partials["vite.config.ts"] = {
160
+ type: "code",
161
+ merge: [
162
+ await readFile(join(context.env.templateDir, "vite.config.ts"), "utf8"),
163
+ ],
164
+ };
165
+
166
+ props.eslint = {
167
+ ...props.eslint,
168
+ typescript: {
169
+ import: `import typescriptEslint from "@typescript-eslint/eslint-plugin";
170
+ import tsParser from "@typescript-eslint/parser";`,
171
+ config: `...compat
172
+ .extends("eslint:recommended", "plugin:@typescript-eslint/recommended")
173
+ .map((config) => ({
174
+ ...config,
175
+ files: ["**/*.{ts,tsx}"],
176
+ })),
177
+ {
178
+ files: ["**/*.{ts,tsx}"],
179
+
180
+ plugins: {
181
+ "@typescript-eslint": typescriptEslint,
182
+ },
183
+
184
+ languageOptions: {
185
+ globals: {
186
+ ...globals.node,
187
+ },
188
+
189
+ parser: tsParser,
190
+ },
191
+
192
+ rules: {
193
+ "@typescript-eslint/no-unused-vars": [
194
+ "error",
195
+ {
196
+ argsIgnorePattern: "^_",
197
+ },
198
+ ],
199
+
200
+ "@typescript-eslint/no-explicit-any": "off",
201
+ "@typescript-eslint/explicit-module-boundary-types": "off",
202
+ },
203
+ },`,
204
+ },
205
+ };
206
+ }
207
+
208
+ if (answer.includes("prettier")) {
209
+ partials["package.json"] = {
210
+ ...partials["package.json"],
211
+ merge: [
212
+ ...partials["package.json"].merge,
213
+ await json(context.env.templateDir, "package.prettier.json"),
214
+ ],
215
+ };
216
+ files.push(
217
+ join(context.env.templateDir, ".prettierignore"),
218
+ join(context.env.templateDir, ".prettierrc")
219
+ );
220
+ props.eslint = {
221
+ ...props.eslint,
222
+ prettier: {
223
+ import: `import prettier from "eslint-plugin-prettier";`,
224
+ compat: `,"plugin:prettier/recommended"`,
225
+ plugin: `prettier,`,
226
+ rules: `"prettier/prettier": [
227
+ "error",
228
+ {
229
+ endOfLine: "auto",
230
+ },
231
+ ],`,
232
+ },
233
+ };
234
+ }
235
+
236
+ if (answer.includes("tailwind")) {
237
+ partials["package.json"] = {
238
+ ...partials["package.json"],
239
+ merge: [
240
+ ...partials["package.json"].merge,
241
+ await json(context.env.templateDir, "package.tailwind.json"),
242
+ ],
243
+ };
244
+ files.push(
245
+ join(context.env.templateDir, "tailwind.config.mjs"),
246
+ join(context.env.templateDir, "postcss.config.mjs")
247
+ );
248
+ }
249
+
250
+ if (answer.includes("css-modules")) {
251
+ partials["package.json"] = {
252
+ ...partials["package.json"],
253
+ merge: [
254
+ ...partials["package.json"].merge,
255
+ await json(context.env.templateDir, "package.css.json"),
256
+ ],
257
+ };
258
+
259
+ if (answer.includes("ts")) {
260
+ partials["tsconfig.json"] = {
261
+ ...partials["tsconfig.json"],
262
+ merge: [
263
+ ...partials["tsconfig.json"].merge,
264
+ await json(context.env.templateDir, "tsconfig.css.json"),
265
+ ],
266
+ };
267
+ }
268
+ }
269
+
270
+ if (answer.includes("lightningcss")) {
271
+ if (!answer.includes("tailwind")) {
272
+ partials["package.json"] = {
273
+ ...partials["package.json"],
274
+ merge: [
275
+ ...partials["package.json"].merge,
276
+ await json(context.env.templateDir, "package.lightningcss.json"),
277
+ ],
278
+ };
279
+
280
+ if (answer.includes("ts")) {
281
+ partials["vite.config.ts"] = {
282
+ ...partials["vite.config.ts"],
283
+ merge: [
284
+ await readFile(
285
+ join(context.env.templateDir, "vite.config.lightningcss.ts"),
286
+ "utf8"
287
+ ),
288
+ ],
289
+ };
290
+ } else {
291
+ partials["vite.config.mjs"] = {
292
+ ...partials["vite.config.mjs"],
293
+ type: "code",
294
+ merge: [
295
+ await readFile(
296
+ join(context.env.templateDir, "vite.config.lightningcss.mjs"),
297
+ "utf8"
298
+ ),
299
+ ],
300
+ };
301
+ }
302
+ } else {
303
+ context.env.logger.warn(
304
+ "Lightning CSS is not compatible with Tailwind CSS v3. Lightning CSS will not be installed."
305
+ );
306
+ }
307
+ }
308
+
309
+ if (answer.includes("react-compiler")) {
310
+ partials["package.json"] = {
311
+ ...partials["package.json"],
312
+ merge: [
313
+ ...partials["package.json"].merge,
314
+ await json(context.env.templateDir, "package.react-compiler.json"),
315
+ ],
316
+ };
317
+ props.eslint = {
318
+ ...props.eslint,
319
+ reactCompiler: {
320
+ import: `import reactCompiler from "eslint-plugin-react-compiler";`,
321
+ plugin: `"react-compiler": reactCompiler,`,
322
+ rules: `"react-compiler/react-compiler": "error",`,
323
+ },
324
+ };
325
+
326
+ if (answer.includes("ts")) {
327
+ partials["vite.config.ts"] = {
328
+ ...partials["vite.config.ts"],
329
+ merge: [
330
+ ...(partials["vite.config.ts"]?.merge ?? []),
331
+ await readFile(
332
+ join(context.env.templateDir, "vite.config.react-compiler.ts"),
333
+ "utf8"
334
+ ),
335
+ ],
336
+ };
337
+ } else {
338
+ partials["vite.config.mjs"] = {
339
+ ...partials["vite.config.mjs"],
340
+ type: "code",
341
+ merge: [
342
+ ...(partials["vite.config.mjs"]?.merge ?? []),
343
+ await readFile(
344
+ join(context.env.templateDir, "vite.config.react-compiler.mjs"),
345
+ "utf8"
346
+ ),
347
+ ],
348
+ };
349
+ }
350
+ }
351
+
352
+ if (answer.includes("react-swc")) {
353
+ if (!answer.includes("react-compiler")) {
354
+ partials["package.json"] = {
355
+ ...partials["package.json"],
356
+ merge: [
357
+ ...partials["package.json"].merge,
358
+ await json(context.env.templateDir, "package.swc.json"),
359
+ ],
360
+ };
361
+
362
+ if (answer.includes("ts")) {
363
+ partials["vite.config.ts"] = {
364
+ ...partials["vite.config.ts"],
365
+ merge: [
366
+ ...(partials["vite.config.ts"]?.merge ?? []),
367
+ await readFile(
368
+ join(context.env.templateDir, "vite.config.swc.ts"),
369
+ "utf8"
370
+ ),
371
+ ],
372
+ };
373
+ } else {
374
+ partials["vite.config.mjs"] = {
375
+ ...partials["vite.config.mjs"],
376
+ type: "code",
377
+ merge: [
378
+ ...(partials["vite.config.mjs"]?.merge ?? []),
379
+ await readFile(
380
+ join(context.env.templateDir, "vite.config.swc.mjs"),
381
+ "utf8"
382
+ ),
383
+ ],
384
+ };
385
+ }
386
+ } else {
387
+ context.env.logger.warn(
388
+ "React Compiler is not yet supported when using the SWC compiler. Falling back to using the Babel compiler."
389
+ );
390
+ }
391
+ }
392
+
393
+ if (answer.includes("git")) {
394
+ partials[".gitignore"] = {
395
+ ...partials[".gitignore"],
396
+ type: "text",
397
+ merge: [
398
+ ...(partials[".gitignore"]?.merge ?? []),
399
+ await readFile(
400
+ join(context.env.templateDir, ".gitignore.template"),
401
+ "utf8"
402
+ ),
403
+ ...(answer.includes("eslint") ? ["\n# Cache\n.eslintcache\n"] : []),
404
+ ],
405
+ };
406
+ }
407
+
408
+ return {
409
+ ...context,
410
+ features: answer,
411
+ props,
412
+ files,
413
+ partials,
414
+ };
415
+ };
package/steps/host.mjs ADDED
@@ -0,0 +1,40 @@
1
+ import { input } from "@inquirer/prompts";
2
+
3
+ import { theme } from "../lib/theme.mjs";
4
+
5
+ export default async (context) => {
6
+ const host =
7
+ context.env.options.host ??
8
+ (!context.props.custom || context.env.hasOptions
9
+ ? "localhost"
10
+ : await input(
11
+ {
12
+ message: "Hostname",
13
+ default: "localhost",
14
+ description: "The hostname to use for the server",
15
+ theme,
16
+ },
17
+ context
18
+ ));
19
+ return {
20
+ ...context,
21
+ props: {
22
+ ...context.props,
23
+ host,
24
+ },
25
+ partials:
26
+ host !== "localhost"
27
+ ? {
28
+ ...context.partials,
29
+ "react-server.config.json": {
30
+ ...context.partials["react-server.config.json"],
31
+ type: "json",
32
+ merge: [
33
+ ...(context.partials["react-server.config.json"]?.merge ?? []),
34
+ { server: { host } },
35
+ ],
36
+ },
37
+ }
38
+ : context.partials,
39
+ };
40
+ };
@@ -0,0 +1,33 @@
1
+ import alias from "./alias.mjs";
2
+ // import authentication from "./authentication.mjs";
3
+ // import database from "./database.mjs";
4
+ import deploy, { prepare as prepareDeploy } from "./deploy.mjs";
5
+ import features from "./features.mjs";
6
+ import host from "./host.mjs";
7
+ // import integrations from "./integrations.mjs";
8
+ import name from "./name.mjs";
9
+ import packageManager from "./package.mjs";
10
+ import port from "./port.mjs";
11
+ // import stateManagement from "./state-management.mjs";
12
+ import preset, { prepare as preparePreset } from "./preset.mjs";
13
+ // import thirdParty from "./third-party.mjs";
14
+ // import ui from "./ui.mjs";
15
+
16
+ export default [
17
+ name,
18
+ preset,
19
+ features,
20
+ alias,
21
+ // thirdParty,
22
+ // ui,
23
+ // stateManagement,
24
+ // database,
25
+ // authentication,
26
+ // integrations,
27
+ host,
28
+ port,
29
+ preparePreset,
30
+ deploy,
31
+ packageManager,
32
+ prepareDeploy,
33
+ ];
@@ -0,0 +1,104 @@
1
+ import { checkbox, Separator } from "@inquirer/prompts";
2
+
3
+ import { theme } from "../lib/theme.mjs";
4
+
5
+ export default [
6
+ (context) => context.props.thirdParty,
7
+ async (context) => {
8
+ const integrations = !context.props.custom
9
+ ? []
10
+ : await checkbox(
11
+ {
12
+ message: "Integrations",
13
+ choices: [
14
+ {
15
+ name: "None",
16
+ value: "none",
17
+ description: "No integrations",
18
+ },
19
+ new Separator(),
20
+ {
21
+ name: "Vitest",
22
+ value: "vitest",
23
+ description: "Run unit tests using Vitest",
24
+ disabled: "(coming soon)",
25
+ },
26
+ {
27
+ name: "Playwright",
28
+ value: "playwright",
29
+ description: "Run browser tests using Playwright",
30
+ disabled: "(coming soon)",
31
+ },
32
+ {
33
+ name: "Storybook",
34
+ value: "storybook",
35
+ description: "Create a Storybook setup",
36
+ disabled: "(coming soon)",
37
+ },
38
+ {
39
+ name: "Paraglide",
40
+ value: "paraglide",
41
+ description: "Localization using Paraglide",
42
+ disabled: "(coming soon)",
43
+ },
44
+ {
45
+ name: "Stripe",
46
+ value: "stripe",
47
+ description: "Add Stripe integration",
48
+ disabled: "(coming soon)",
49
+ },
50
+ {
51
+ name: "LemonSqueezy",
52
+ value: "lemonsqueezy",
53
+ description: "Add LemonSqueezy integration",
54
+ disabled: "(coming soon)",
55
+ },
56
+ {
57
+ name: "Sentry",
58
+ value: "sentry",
59
+ description: "Add Sentry integration",
60
+ disabled: "(coming soon)",
61
+ },
62
+ {
63
+ name: "Vercel Analytics",
64
+ value: "vercel-analytics",
65
+ description: "Add Vercel Analytics integration",
66
+ disabled: "(coming soon)",
67
+ },
68
+ {
69
+ name: "Vercel Speed Insights",
70
+ value: "vercel-speed-insights",
71
+ description: "Add Vercel Speed Insights integration",
72
+ disabled: "(coming soon)",
73
+ },
74
+ {
75
+ name: "Vercel Flags",
76
+ value: "vercel-flags",
77
+ description: "Add Vercel Flags SDK integration",
78
+ disabled: "(coming soon)",
79
+ },
80
+ {
81
+ name: "Resend",
82
+ value: "resend",
83
+ description: "Add Resend + react-email integration",
84
+ disabled: "(coming soon)",
85
+ },
86
+ {
87
+ name: "GitHub Actions",
88
+ value: "github-actions",
89
+ description: "Add GitHub Actions for CI",
90
+ disabled: "(coming soon)",
91
+ },
92
+ new Separator(),
93
+ ],
94
+ theme,
95
+ },
96
+ context
97
+ );
98
+
99
+ return {
100
+ ...context,
101
+ props: { ...context.props, integrations },
102
+ };
103
+ },
104
+ ];
package/steps/name.mjs ADDED
@@ -0,0 +1,69 @@
1
+ import { statSync } from "node:fs";
2
+ import { join, relative } from "node:path";
3
+
4
+ import { input } from "@inquirer/prompts";
5
+
6
+ import { generateProjectName } from "../lib/generate-name.mjs";
7
+ import { theme } from "../lib/theme.mjs";
8
+
9
+ const cwd = process.cwd();
10
+
11
+ export default async (context) => {
12
+ const answer =
13
+ context.env.options.name ??
14
+ (await input(
15
+ {
16
+ message: "Project name",
17
+ default: generateProjectName(),
18
+ theme,
19
+ validate: async (value) => {
20
+ if (
21
+ !/^(?:(?:@(?:[a-z0-9-*~][a-z0-9-*._~]*)?\/[a-z0-9-._~])|[a-z0-9-~])[a-z0-9-._~]*$/.test(
22
+ value
23
+ )
24
+ ) {
25
+ return "Invalid project name!";
26
+ }
27
+ try {
28
+ if (statSync(join(cwd, value)).isDirectory()) {
29
+ return "Project already exists!";
30
+ }
31
+ } catch {
32
+ // no directory exists, so it's valid
33
+ }
34
+ return true;
35
+ },
36
+ },
37
+ context
38
+ ));
39
+
40
+ const projectDir = join(cwd, answer);
41
+ if (relative(cwd, projectDir).startsWith("..")) {
42
+ throw "Project name cannot be outside the current directory";
43
+ }
44
+
45
+ return {
46
+ ...context,
47
+ interactive: !context.env.options.name,
48
+ env: {
49
+ ...context.env,
50
+ projectDir,
51
+ },
52
+ props: {
53
+ ...context.props,
54
+ projectName: answer,
55
+ },
56
+ partials: {
57
+ ...context.partials,
58
+ "package.json": {
59
+ type: "json",
60
+ merge: [
61
+ {
62
+ name: answer,
63
+ },
64
+ ...(context.partials["package.json"]?.merge ?? []),
65
+ ],
66
+ },
67
+ },
68
+ };
69
+ };