create-lumen 1.2.0 → 2.0.0-alpha.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.
Files changed (136) hide show
  1. package/README.md +56 -9
  2. package/bin/cli.js +18 -4
  3. package/package.json +14 -4
  4. package/schema/fixtures/index.js +62 -0
  5. package/schema/fixtures/v2/invalid/invalid-manifest-version.json +33 -0
  6. package/schema/fixtures/v2/invalid/malformed-paths.json +33 -0
  7. package/schema/fixtures/v2/invalid/missing-required-fields.json +17 -0
  8. package/schema/fixtures/v2/invalid/next-type-based.json +33 -0
  9. package/schema/fixtures/v2/invalid/next-vite.json +33 -0
  10. package/schema/fixtures/v2/invalid/react-bundler.json +34 -0
  11. package/schema/fixtures/v2/invalid/react-hybrid.json +33 -0
  12. package/schema/fixtures/v2/invalid/react-variant-mismatch.json +33 -0
  13. package/schema/fixtures/v2/invalid/shadcn-without-tailwind.json +33 -0
  14. package/schema/fixtures/v2/invalid/v1-flat-config.json +11 -0
  15. package/schema/fixtures/v2/valid/full-kitchen-sink.json +35 -0
  16. package/schema/fixtures/v2/valid/next-app-router-feature-based.json +35 -0
  17. package/schema/fixtures/v2/valid/next-app-router-hybrid.json +35 -0
  18. package/schema/fixtures/v2/valid/next-app-router-none.json +33 -0
  19. package/schema/fixtures/v2/valid/next-pages-router-feature-based.json +35 -0
  20. package/schema/fixtures/v2/valid/next-pages-router-hybrid.json +35 -0
  21. package/schema/fixtures/v2/valid/next-pages-router-none.json +33 -0
  22. package/schema/fixtures/v2/valid/react-vite-feature-based.json +33 -0
  23. package/schema/fixtures/v2/valid/react-vite-none.json +33 -0
  24. package/schema/fixtures/v2/valid/react-vite-type-based.json +33 -0
  25. package/schema/lumen.config.v2.json +216 -0
  26. package/src/cli-args.js +294 -0
  27. package/src/css.js +20 -9
  28. package/src/dependencies.js +12 -6
  29. package/src/engine/capabilities.js +218 -0
  30. package/src/engine/index.js +2 -0
  31. package/src/engine/providers/base-provider.js +219 -0
  32. package/src/injector.js +26 -13
  33. package/src/main.js +52 -10
  34. package/src/manifest/emit.js +120 -0
  35. package/src/manifest/index.js +11 -0
  36. package/src/manifest/paths.js +128 -0
  37. package/src/manifest/schema.js +212 -0
  38. package/src/prompts.js +26 -6
  39. package/templates/conditional/linting/eslint/next/eslint.config.mjs +16 -0
  40. package/templates/css/tailwind/postcss.config.mjs +5 -0
  41. /package/templates/architectures/{component-based → type-based}/main.jsx +0 -0
  42. /package/templates/architectures/{component-based → type-based}/main.tsx +0 -0
  43. /package/templates/architectures/{component-based → type-based}/src/App.jsx +0 -0
  44. /package/templates/architectures/{component-based → type-based}/src/App.tsx +0 -0
  45. /package/templates/architectures/{component-based → type-based}/src/components/common/Button.jsx +0 -0
  46. /package/templates/architectures/{component-based → type-based}/src/components/common/Button.tsx +0 -0
  47. /package/templates/architectures/{component-based → type-based}/src/components/common/index.js +0 -0
  48. /package/templates/architectures/{component-based → type-based}/src/components/common/index.ts +0 -0
  49. /package/templates/architectures/{component-based → type-based}/src/components/form/.gitkeep +0 -0
  50. /package/templates/architectures/{component-based → type-based}/src/config/.gitkeep +0 -0
  51. /package/templates/architectures/{component-based → type-based}/src/hooks/useApp.js +0 -0
  52. /package/templates/architectures/{component-based → type-based}/src/hooks/useApp.ts +0 -0
  53. /package/templates/architectures/{component-based → type-based}/src/hooks/useLocalStorage.jsx +0 -0
  54. /package/templates/architectures/{component-based → type-based}/src/hooks/useLocalStorage.ts +0 -0
  55. /package/templates/architectures/{component-based → type-based}/src/layouts/MainLayout.jsx +0 -0
  56. /package/templates/architectures/{component-based → type-based}/src/layouts/MainLayout.tsx +0 -0
  57. /package/templates/architectures/{component-based → type-based}/src/pages/Home/Home.jsx +0 -0
  58. /package/templates/architectures/{component-based → type-based}/src/pages/Home/Home.tsx +0 -0
  59. /package/templates/architectures/{component-based → type-based}/src/pages/Home/index.js +0 -0
  60. /package/templates/architectures/{component-based → type-based}/src/pages/Home/index.ts +0 -0
  61. /package/templates/architectures/{component-based → type-based}/src/providers/.gitkeep +0 -0
  62. /package/templates/architectures/{component-based → type-based}/src/providers/AppProvider.jsx +0 -0
  63. /package/templates/architectures/{component-based → type-based}/src/providers/AppProvider.tsx +0 -0
  64. /package/templates/architectures/{component-based → type-based}/src/providers/appcontext.js +0 -0
  65. /package/templates/architectures/{component-based → type-based}/src/providers/appcontext.ts +0 -0
  66. /package/templates/architectures/{component-based → type-based}/src/services/.gitkeep +0 -0
  67. /package/templates/architectures/{component-based → type-based}/src/store/.gitkeep +0 -0
  68. /package/templates/architectures/{component-based → type-based}/src/styles/.gitkeep +0 -0
  69. /package/templates/architectures/{component-based → type-based}/src/types/index.js +0 -0
  70. /package/templates/architectures/{component-based → type-based}/src/types/index.ts +0 -0
  71. /package/templates/architectures/{component-based → type-based}/src/utils/cn.jsx +0 -0
  72. /package/templates/architectures/{component-based → type-based}/src/utils/cn.ts +0 -0
  73. /package/templates/conditional/axios/{component-based → type-based}/src/config/axios.config.js +0 -0
  74. /package/templates/conditional/axios/{component-based → type-based}/src/config/axios.config.ts +0 -0
  75. /package/templates/conditional/axios/{component-based → type-based}/src/services/axios.client.js +0 -0
  76. /package/templates/conditional/axios/{component-based → type-based}/src/services/axios.client.ts +0 -0
  77. /package/templates/conditional/axios/{component-based → type-based}/src/services/user.service.js +0 -0
  78. /package/templates/conditional/axios/{component-based → type-based}/src/services/user.service.ts +0 -0
  79. /package/templates/conditional/fetch/{component-based → type-based}/src/config/api.config.js +0 -0
  80. /package/templates/conditional/fetch/{component-based → type-based}/src/config/api.config.ts +0 -0
  81. /package/templates/conditional/fetch/{component-based → type-based}/src/services/api.client.js +0 -0
  82. /package/templates/conditional/fetch/{component-based → type-based}/src/services/api.client.ts +0 -0
  83. /package/templates/conditional/fetch/{component-based → type-based}/src/services/user.service.js +0 -0
  84. /package/templates/conditional/fetch/{component-based → type-based}/src/services/user.service.ts +0 -0
  85. /package/templates/conditional/icons/huge/{component-based → type-based}/bootstrap/src/pages/Home/Home.jsx +0 -0
  86. /package/templates/conditional/icons/huge/{component-based → type-based}/bootstrap/src/pages/Home/Home.tsx +0 -0
  87. /package/templates/conditional/icons/huge/{component-based → type-based}/src/pages/Home/Home.jsx +0 -0
  88. /package/templates/conditional/icons/huge/{component-based → type-based}/src/pages/Home/Home.tsx +0 -0
  89. /package/templates/conditional/icons/huge/{component-based → type-based}/tailwind/src/pages/Home/Home.jsx +0 -0
  90. /package/templates/conditional/icons/huge/{component-based → type-based}/tailwind/src/pages/Home/Home.tsx +0 -0
  91. /package/templates/conditional/icons/lucide/{component-based → type-based}/bootstrap/src/pages/Home/Home.jsx +0 -0
  92. /package/templates/conditional/icons/lucide/{component-based → type-based}/bootstrap/src/pages/Home/Home.tsx +0 -0
  93. /package/templates/conditional/icons/lucide/{component-based → type-based}/src/pages/Home/Home.jsx +0 -0
  94. /package/templates/conditional/icons/lucide/{component-based → type-based}/src/pages/Home/Home.tsx +0 -0
  95. /package/templates/conditional/icons/lucide/{component-based → type-based}/tailwind/src/pages/Home/Home.jsx +0 -0
  96. /package/templates/conditional/icons/lucide/{component-based → type-based}/tailwind/src/pages/Home/Home.tsx +0 -0
  97. /package/templates/conditional/router/{component-based → type-based}/bootstrap/src/layouts/MainLayout.jsx +0 -0
  98. /package/templates/conditional/router/{component-based → type-based}/bootstrap/src/layouts/MainLayout.tsx +0 -0
  99. /package/templates/conditional/router/{component-based → type-based}/src/App.jsx +0 -0
  100. /package/templates/conditional/router/{component-based → type-based}/src/App.tsx +0 -0
  101. /package/templates/conditional/router/{component-based → type-based}/src/layouts/MainLayout.jsx +0 -0
  102. /package/templates/conditional/router/{component-based → type-based}/src/layouts/MainLayout.tsx +0 -0
  103. /package/templates/conditional/router/{component-based → type-based}/src/router/guards/AuthGuard.jsx +0 -0
  104. /package/templates/conditional/router/{component-based → type-based}/src/router/guards/AuthGuard.tsx +0 -0
  105. /package/templates/conditional/router/{component-based → type-based}/src/router/guards/GuestGuard.jsx +0 -0
  106. /package/templates/conditional/router/{component-based → type-based}/src/router/guards/GuestGuard.tsx +0 -0
  107. /package/templates/conditional/router/{component-based → type-based}/src/router/index.jsx +0 -0
  108. /package/templates/conditional/router/{component-based → type-based}/src/router/index.tsx +0 -0
  109. /package/templates/conditional/router/{component-based → type-based}/tailwind/src/layouts/MainLayout.jsx +0 -0
  110. /package/templates/conditional/router/{component-based → type-based}/tailwind/src/layouts/MainLayout.tsx +0 -0
  111. /package/templates/conditional/state/redux/{component-based → type-based}/src/pages/Home/counterSlice.jsx +0 -0
  112. /package/templates/conditional/state/redux/{component-based → type-based}/src/pages/Home/counterSlice.ts +0 -0
  113. /package/templates/conditional/state/redux/{component-based → type-based}/src/providers/StoreProvider.jsx +0 -0
  114. /package/templates/conditional/state/redux/{component-based → type-based}/src/providers/StoreProvider.tsx +0 -0
  115. /package/templates/conditional/state/redux/{component-based → type-based}/src/store/hooks.js +0 -0
  116. /package/templates/conditional/state/redux/{component-based → type-based}/src/store/hooks.ts +0 -0
  117. /package/templates/conditional/state/redux/{component-based → type-based}/src/store/index.js +0 -0
  118. /package/templates/conditional/state/redux/{component-based → type-based}/src/store/index.ts +0 -0
  119. /package/templates/conditional/state/zustand/{component-based → type-based}/src/store/useCounterStore.jsx +0 -0
  120. /package/templates/conditional/state/zustand/{component-based → type-based}/src/store/useCounterStore.ts +0 -0
  121. /package/templates/conditional/testing/jest/{component-based → type-based}/src/__tests__/App.test.jsx +0 -0
  122. /package/templates/conditional/testing/jest/{component-based → type-based}/src/__tests__/App.test.tsx +0 -0
  123. /package/templates/conditional/testing/vitest/{component-based → type-based}/src/__tests__/App.test.jsx +0 -0
  124. /package/templates/conditional/testing/vitest/{component-based → type-based}/src/__tests__/App.test.tsx +0 -0
  125. /package/templates/css/component-styles/bootstrap/{component-based → type-based}/src/components/common/Button.jsx +0 -0
  126. /package/templates/css/component-styles/bootstrap/{component-based → type-based}/src/components/common/Button.tsx +0 -0
  127. /package/templates/css/component-styles/bootstrap/{component-based → type-based}/src/layouts/MainLayout.jsx +0 -0
  128. /package/templates/css/component-styles/bootstrap/{component-based → type-based}/src/layouts/MainLayout.tsx +0 -0
  129. /package/templates/css/component-styles/bootstrap/{component-based → type-based}/src/pages/Home/Home.jsx +0 -0
  130. /package/templates/css/component-styles/bootstrap/{component-based → type-based}/src/pages/Home/Home.tsx +0 -0
  131. /package/templates/css/component-styles/tailwind/{component-based → type-based}/src/components/common/Button.jsx +0 -0
  132. /package/templates/css/component-styles/tailwind/{component-based → type-based}/src/components/common/Button.tsx +0 -0
  133. /package/templates/css/component-styles/tailwind/{component-based → type-based}/src/layouts/MainLayout.jsx +0 -0
  134. /package/templates/css/component-styles/tailwind/{component-based → type-based}/src/layouts/MainLayout.tsx +0 -0
  135. /package/templates/css/component-styles/tailwind/{component-based → type-based}/src/pages/Home/Home.jsx +0 -0
  136. /package/templates/css/component-styles/tailwind/{component-based → type-based}/src/pages/Home/Home.tsx +0 -0
@@ -0,0 +1,212 @@
1
+ import { z } from "zod";
2
+ import { fromZodError } from "zod-validation-error";
3
+
4
+ // ---------------------------------------------------------------------------
5
+ // Constants - keep in sync with docs/manifest-v2.md and ROADMAP
6
+ // ---------------------------------------------------------------------------
7
+ export const MANIFEST_VERSION = 2;
8
+ export const SCHEMA_URL = "https://lumen-sh.github.io/create-lumen/schema/lumen.config.v2.json";
9
+
10
+ export const FRAMEWORK_NAMES = ["react", "next"];
11
+ export const FRAMEWORK_VARIANTS = ["vite", "app-router", "pages-router"];
12
+ export const FRAMEWORK_BUNDLERS = ["turbopack", "webpack"];
13
+ export const FRAMEWORK_ADAPTERS = ["node", "vercel", "cloudflare", "static"];
14
+
15
+ export const STYLING_ENGINES = ["tailwind", "bootstrap", "none"];
16
+ export const ARCHITECTURE_TYPES = ["feature-based", "type-based", "hybrid", "none"];
17
+ export const UI_KITS = ["shadcn", "none"];
18
+ export const DOCS_LANGUAGES = ["en", "es"];
19
+ export const TOOLING_LANGUAGES = ["ts", "js"];
20
+ export const TOOLING_LINTERS = ["eslint", "oxlint", "biome", "none"];
21
+ export const TOOLING_FORMATTERS = ["prettier", "oxfmt", "none"];
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // Sub-schemas
25
+ // ---------------------------------------------------------------------------
26
+ const frameworkSchema = z
27
+ .object({
28
+ name: z.enum(FRAMEWORK_NAMES),
29
+ variant: z.enum(FRAMEWORK_VARIANTS),
30
+ bundler: z.enum(FRAMEWORK_BUNDLERS).optional(),
31
+ adapter: z.enum(FRAMEWORK_ADAPTERS).optional(),
32
+ })
33
+ .strict();
34
+
35
+ const stylingSchema = z
36
+ .object({
37
+ engine: z.enum(STYLING_ENGINES),
38
+ })
39
+ .strict();
40
+
41
+ const architectureSchema = z
42
+ .object({
43
+ type: z.enum(ARCHITECTURE_TYPES),
44
+ })
45
+ .strict();
46
+
47
+ const uiSchema = z
48
+ .object({
49
+ kit: z.enum(UI_KITS),
50
+ })
51
+ .strict();
52
+
53
+ const docsSchema = z
54
+ .object({
55
+ language: z.enum(DOCS_LANGUAGES),
56
+ })
57
+ .strict();
58
+
59
+ const pathsSchema = z
60
+ .object({
61
+ features: z.string().min(1),
62
+ components: z.string().min(1),
63
+ services: z.string().min(1),
64
+ hooks: z.string().min(1),
65
+ pages: z.string().min(1),
66
+ ui: z.string().min(1),
67
+ })
68
+ .strict();
69
+
70
+ const toolingSchema = z
71
+ .object({
72
+ language: z.enum(TOOLING_LANGUAGES),
73
+ linter: z.enum(TOOLING_LINTERS),
74
+ formatter: z.enum(TOOLING_FORMATTERS),
75
+ })
76
+ .strict();
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Top-level manifest v2 - strict, no extra keys except $schema
80
+ // ---------------------------------------------------------------------------
81
+ export const manifestSchemaV2 = z
82
+ .object({
83
+ $schema: z.string().url().optional().default(SCHEMA_URL),
84
+ manifestVersion: z.literal(MANIFEST_VERSION),
85
+ framework: frameworkSchema,
86
+ styling: stylingSchema,
87
+ architecture: architectureSchema,
88
+ ui: uiSchema,
89
+ docs: docsSchema,
90
+ paths: pathsSchema,
91
+ tooling: toolingSchema,
92
+ reactCompiler: z.boolean().optional(),
93
+ agentDocs: z.boolean().optional(),
94
+ })
95
+ .strict()
96
+ .superRefine((val, ctx) => {
97
+ const { framework, architecture, styling, ui } = val;
98
+
99
+ // framework.variant must match framework.name
100
+ if (framework.name === "react" && framework.variant !== "vite") {
101
+ ctx.addIssue({
102
+ code: "custom",
103
+ path: ["framework", "variant"],
104
+ message: `framework.variant must be "vite" when framework.name is "react" (got "${framework.variant}")`,
105
+ });
106
+ }
107
+ if (framework.name === "next" && framework.variant === "vite") {
108
+ ctx.addIssue({
109
+ code: "custom",
110
+ path: ["framework", "variant"],
111
+ message: `framework.variant must be "app-router" or "pages-router" when framework.name is "next" (got "vite")`,
112
+ });
113
+ }
114
+
115
+ // bundler only for Next.js
116
+ if (framework.bundler !== undefined && framework.name !== "next") {
117
+ ctx.addIssue({
118
+ code: "custom",
119
+ path: ["framework", "bundler"],
120
+ message: `framework.bundler is only valid when framework.name is "next"`,
121
+ });
122
+ }
123
+
124
+ // adapter only for Next.js (behavior in #22)
125
+ if (framework.adapter !== undefined && framework.name !== "next") {
126
+ ctx.addIssue({
127
+ code: "custom",
128
+ path: ["framework", "adapter"],
129
+ message: `framework.adapter is only valid when framework.name is "next"`,
130
+ });
131
+ }
132
+
133
+ // architecture scoping per framework (#34)
134
+ const allowedByFramework =
135
+ framework.name === "react"
136
+ ? ["feature-based", "type-based", "none"]
137
+ : ["feature-based", "hybrid", "none"];
138
+ if (!allowedByFramework.includes(architecture.type)) {
139
+ ctx.addIssue({
140
+ code: "custom",
141
+ path: ["architecture", "type"],
142
+ message: `architecture.type "${architecture.type}" is not valid for framework "${framework.name}" (allowed: ${allowedByFramework.join(", ")})`,
143
+ });
144
+ }
145
+
146
+ // shadcn requires tailwind (#33)
147
+ if (ui.kit === "shadcn" && styling.engine !== "tailwind") {
148
+ ctx.addIssue({
149
+ code: "custom",
150
+ path: ["ui", "kit"],
151
+ message: `ui.kit "shadcn" requires styling.engine "tailwind" (got "${styling.engine}")`,
152
+ });
153
+ }
154
+ });
155
+
156
+ // ---------------------------------------------------------------------------
157
+ // v1 detection - flat shape from create-lumen 1.x
158
+ // ---------------------------------------------------------------------------
159
+ const V1_FLAT_KEYS = ["css", "cssFramework", "architecture", "language", "router", "stateManagement", "iconLibrary", "apiClient", "testing"];
160
+
161
+ export function isV1Manifest(raw) {
162
+ if (!raw || typeof raw !== "object") return false;
163
+ // v1 had framework as string or missing manifestVersion
164
+ if (raw.manifestVersion === 1) return true;
165
+ if (typeof raw.framework === "string") return true;
166
+ if (raw.framework === undefined && V1_FLAT_KEYS.some((k) => k in raw)) return true;
167
+ // flat config without nested framework object but with css/architecture as string
168
+ if (typeof raw.architecture === "string" && typeof raw.framework !== "object") return true;
169
+ return false;
170
+ }
171
+
172
+ export function formatZodError(error) {
173
+ try {
174
+ const ve = fromZodError(error, {
175
+ prefix: "Manifest validation failed",
176
+ prefixSeparator: ": ",
177
+ issueSeparator: "; ",
178
+ });
179
+ return ve.message;
180
+ } catch {
181
+ return error.message;
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Parse and validate a manifest v2 object.
187
+ * - Throws with migration-oriented message for v1 manifests (#14 AC)
188
+ * - Throws with user-friendly field path + expected shape for malformed v2
189
+ * @param {unknown} raw
190
+ * @returns {import("zod").infer<typeof manifestSchemaV2>}
191
+ */
192
+ export function parseManifest(raw) {
193
+ if (isV1Manifest(raw)) {
194
+ throw new Error(
195
+ `Manifest v1 detected (flat config). create-lumen v2 uses a nested manifest (manifestVersion: 2).\n` +
196
+ `Please migrate: delete the old config and re-run create-lumen, or see the migration guide (docs/migration/v1-to-v2.md).\n` +
197
+ `Raw keys: ${Object.keys(raw || {}).join(", ")}`
198
+ );
199
+ }
200
+
201
+ const result = manifestSchemaV2.safeParse(raw);
202
+ if (!result.success) {
203
+ const msg = formatZodError(result.error);
204
+ const err = new Error(msg);
205
+ err.cause = result.error;
206
+ throw err;
207
+ }
208
+ return result.data;
209
+ }
210
+
211
+ // Re-export type helper for JSDoc (z.infer)
212
+ // /** @typedef {z.infer<typeof manifestSchemaV2>} ManifestV2 */
package/src/prompts.js CHANGED
@@ -1,4 +1,4 @@
1
- import { cancel, confirm, isCancel, note, select, text } from "@clack/prompts";
1
+ import { confirm, isCancel, note, select } from "@clack/prompts";
2
2
  import chalk from "chalk";
3
3
  import { configExists, loadConfig, saveConfig } from "@/config-cache.js";
4
4
 
@@ -12,7 +12,9 @@ function formatConfig(responses) {
12
12
  const arch =
13
13
  responses.architecture === "feature-based"
14
14
  ? "Feature-based"
15
- : "Component-based";
15
+ : responses.architecture === "type-based" || responses.architecture === "component-based"
16
+ ? "Type-based"
17
+ : responses.architecture;
16
18
  const css =
17
19
  responses.cssFramework === "none"
18
20
  ? "None"
@@ -32,6 +34,7 @@ function formatConfig(responses) {
32
34
  ? "Redux Toolkit"
33
35
  : "Zustand";
34
36
 
37
+ const docsLang = responses.docsLanguage === "es" ? "ES" : responses.docsLanguage === "en" ? "EN" : "—";
35
38
  return [
36
39
  `• ${chalk.bold("Architecture:")} ${chalk.green(arch)}`,
37
40
  `• ${chalk.bold("Language:")} ${chalk.green(lang)}`,
@@ -43,14 +46,15 @@ function formatConfig(responses) {
43
46
  `• ${chalk.bold("API Client:")} ${responses.apiClient === "axios" ? chalk.green("Axios") : responses.apiClient === "fetch" ? chalk.yellow("Fetch") : chalk.red("None")}`,
44
47
  `• ${chalk.bold("Linter:")} ${responses.linter === "oxlint" ? chalk.green("Oxlint") : responses.linter === "eslint" ? chalk.yellow("ESLint") : chalk.red("None")}`,
45
48
  `• ${chalk.bold("Formatter:")} ${responses.formatter === "oxfmt" ? chalk.green("Oxfmt") : responses.formatter === "prettier" ? chalk.yellow("Prettier") : chalk.red("None")}`,
49
+ `• ${chalk.bold("Docs:")} ${chalk.cyan(docsLang)}`,
46
50
  `• ${chalk.bold("Git:")} ${responses.gitInit ? chalk.green("Yes") : chalk.red("No")}`,
47
51
  `• ${chalk.bold("README:")} ${responses.readme ? chalk.green("Yes") : chalk.red("No")}`,
48
52
  ].join("\n");
49
53
  }
50
54
 
51
55
  export async function getUserInputs(projectName, { quickSetup = false } = {}) {
52
- let oldConfig = {};
53
- let useOldConfig = false;
56
+ let oldConfig;
57
+ let useOldConfig;
54
58
 
55
59
  // Non-interactive quick setup: apply defaults without prompting.
56
60
  if (quickSetup) {
@@ -66,6 +70,7 @@ export async function getUserInputs(projectName, { quickSetup = false } = {}) {
66
70
  apiClient: "none",
67
71
  linter: "eslint",
68
72
  formatter: "prettier",
73
+ docsLanguage: "en",
69
74
  gitInit: true,
70
75
  readme: true,
71
76
  };
@@ -101,6 +106,9 @@ export async function getUserInputs(projectName, { quickSetup = false } = {}) {
101
106
  if (oldConfig.formatter === undefined) {
102
107
  oldConfig.formatter = "none";
103
108
  }
109
+ if (oldConfig.docsLanguage === undefined) {
110
+ oldConfig.docsLanguage = "en";
111
+ }
104
112
  return { ...oldConfig, projectName };
105
113
  }
106
114
  }
@@ -126,6 +134,7 @@ export async function getUserInputs(projectName, { quickSetup = false } = {}) {
126
134
  apiClient: "none",
127
135
  linter: "eslint",
128
136
  formatter: "prettier",
137
+ docsLanguage: "en",
129
138
  gitInit: true,
130
139
  readme: true,
131
140
  };
@@ -143,8 +152,8 @@ export async function getUserInputs(projectName, { quickSetup = false } = {}) {
143
152
  hint: "Scales to large apps — code grouped by business domain",
144
153
  },
145
154
  {
146
- label: "Component-based",
147
- value: "component-based",
155
+ label: "Type-based",
156
+ value: "type-based",
148
157
  hint: "Small apps / component libraries — code grouped by UI type",
149
158
  },
150
159
  ],
@@ -261,6 +270,16 @@ export async function getUserInputs(projectName, { quickSetup = false } = {}) {
261
270
  if (isCancel(formatter)) onCancel();
262
271
  }
263
272
 
273
+ const docsLanguage = await select({
274
+ message: "Which docs language do you want?",
275
+ options: [
276
+ { label: "English", value: "en" },
277
+ { label: "Español", value: "es" },
278
+ ],
279
+ initialValue: "en",
280
+ });
281
+ if (isCancel(docsLanguage)) onCancel();
282
+
264
283
  const readme = await confirm({
265
284
  message: "Would you like to generate a README.md and LICENSE?",
266
285
  initialValue: true,
@@ -279,6 +298,7 @@ export async function getUserInputs(projectName, { quickSetup = false } = {}) {
279
298
  apiClient,
280
299
  linter,
281
300
  formatter,
301
+ docsLanguage,
282
302
  gitInit,
283
303
  readme,
284
304
  };
@@ -0,0 +1,16 @@
1
+ import { dirname } from "path";
2
+ import { fileURLToPath } from "url";
3
+ import { FlatCompat } from "@eslint/eslintrc";
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = dirname(__filename);
7
+
8
+ const compat = new FlatCompat({
9
+ baseDirectory: __dirname,
10
+ });
11
+
12
+ const eslintConfig = [
13
+ ...compat.extends("next/core-web-vitals"),
14
+ ];
15
+
16
+ export default eslintConfig;
@@ -0,0 +1,5 @@
1
+ export default {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };