create-lumen 2.0.0-alpha.1 → 2.0.0-beta.1

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 (190) hide show
  1. package/README.md +38 -12
  2. package/bin/cli.js +3 -1
  3. package/package.json +3 -3
  4. package/schema/fixtures/v2/invalid/shadcn-without-tailwind.json +1 -1
  5. package/schema/fixtures/v2/valid/react-vite-type-based.json +1 -1
  6. package/schema/lumen.config.v2.json +0 -1
  7. package/src/cli-args.js +169 -7
  8. package/src/configure.js +12 -4
  9. package/src/css.js +14 -30
  10. package/src/dependencies.js +36 -20
  11. package/src/engine/capabilities.js +195 -4
  12. package/src/engine/composition.js +736 -0
  13. package/src/engine/index.js +1 -0
  14. package/src/engine/providers/base-provider.js +4 -4
  15. package/src/injector.js +294 -446
  16. package/src/main.js +38 -16
  17. package/src/manifest/emit.js +1 -1
  18. package/src/manifest/schema.js +1 -1
  19. package/src/prompts.js +581 -238
  20. package/src/readme.js +23 -2
  21. package/src/scaffold.js +17 -0
  22. package/templates/architectures/hybrid/main.jsx +10 -0
  23. package/templates/architectures/hybrid/main.tsx +10 -0
  24. package/templates/architectures/hybrid/src/App.jsx +20 -0
  25. package/templates/architectures/hybrid/src/App.tsx +20 -0
  26. package/templates/architectures/hybrid/src/shared/components/ui/Button.jsx +31 -0
  27. package/templates/architectures/hybrid/src/shared/components/ui/Button.tsx +36 -0
  28. package/templates/architectures/hybrid/src/shared/components/ui/index.js +1 -0
  29. package/templates/architectures/hybrid/src/shared/components/ui/index.ts +1 -0
  30. package/templates/architectures/hybrid/src/shared/hooks/useLocalStorage.jsx +28 -0
  31. package/templates/architectures/hybrid/src/shared/hooks/useLocalStorage.ts +28 -0
  32. package/templates/architectures/hybrid/src/shared/layouts/.gitkeep +0 -0
  33. package/templates/architectures/hybrid/src/shared/stores/.gitkeep +0 -0
  34. package/templates/architectures/hybrid/src/shared/styles/.gitkeep +0 -0
  35. package/templates/architectures/hybrid/src/shared/types/index.js +20 -0
  36. package/templates/architectures/hybrid/src/shared/types/index.ts +16 -0
  37. package/templates/architectures/hybrid/src/shared/utils/cn.jsx +3 -0
  38. package/templates/architectures/hybrid/src/shared/utils/cn.ts +5 -0
  39. package/templates/architectures/none/main.jsx +10 -0
  40. package/templates/architectures/none/main.tsx +10 -0
  41. package/templates/architectures/none/src/App.jsx +15 -0
  42. package/templates/architectures/none/src/App.tsx +15 -0
  43. package/templates/architectures/none/src/styles/.gitkeep +1 -0
  44. package/templates/architectures/none/src/types/index.js +20 -0
  45. package/templates/architectures/none/src/types/index.ts +16 -0
  46. package/templates/bases/next/app-router/js/.provenance.json +19 -0
  47. package/templates/bases/next/app-router/js/README.md +36 -0
  48. package/templates/bases/next/app-router/js/jsconfig.json +7 -0
  49. package/templates/bases/next/app-router/js/next.config.mjs +6 -0
  50. package/templates/bases/next/app-router/js/package.json +15 -0
  51. package/templates/bases/next/app-router/js/public/file.svg +1 -0
  52. package/templates/bases/next/app-router/js/public/globe.svg +1 -0
  53. package/templates/bases/next/app-router/js/public/next.svg +1 -0
  54. package/templates/bases/next/app-router/js/public/vercel.svg +1 -0
  55. package/templates/bases/next/app-router/js/public/window.svg +1 -0
  56. package/templates/bases/next/app-router/js/src/app/api/hello/route.js +5 -0
  57. package/templates/bases/next/app-router/js/src/app/blog/[slug]/page.js +33 -0
  58. package/templates/bases/next/app-router/js/src/app/favicon.ico +0 -0
  59. package/templates/bases/next/app-router/js/src/app/globals.css +49 -0
  60. package/templates/bases/next/app-router/js/src/app/layout.js +25 -0
  61. package/templates/bases/next/app-router/js/src/app/loading.js +15 -0
  62. package/templates/bases/next/app-router/js/src/app/not-found.js +29 -0
  63. package/templates/bases/next/app-router/js/src/app/page.js +69 -0
  64. package/templates/bases/next/app-router/js/src/app/page.module.css +150 -0
  65. package/templates/bases/next/app-router/ts/.provenance.json +19 -0
  66. package/templates/bases/next/app-router/ts/README.md +36 -0
  67. package/templates/bases/next/app-router/ts/next.config.ts +7 -0
  68. package/templates/bases/next/app-router/ts/package.json +21 -0
  69. package/templates/bases/next/app-router/ts/public/file.svg +1 -0
  70. package/templates/bases/next/app-router/ts/public/globe.svg +1 -0
  71. package/templates/bases/next/app-router/ts/public/next.svg +1 -0
  72. package/templates/bases/next/app-router/ts/public/vercel.svg +1 -0
  73. package/templates/bases/next/app-router/ts/public/window.svg +1 -0
  74. package/templates/bases/next/app-router/ts/src/app/api/hello/route.ts +5 -0
  75. package/templates/bases/next/app-router/ts/src/app/blog/[slug]/page.tsx +38 -0
  76. package/templates/bases/next/app-router/ts/src/app/favicon.ico +0 -0
  77. package/templates/bases/next/app-router/ts/src/app/globals.css +49 -0
  78. package/templates/bases/next/app-router/ts/src/app/layout.tsx +30 -0
  79. package/templates/bases/next/app-router/ts/src/app/loading.tsx +15 -0
  80. package/templates/bases/next/app-router/ts/src/app/not-found.tsx +29 -0
  81. package/templates/bases/next/app-router/ts/src/app/page.module.css +150 -0
  82. package/templates/bases/next/app-router/ts/src/app/page.tsx +69 -0
  83. package/templates/bases/next/app-router/ts/tsconfig.json +34 -0
  84. package/templates/bases/next/pages-router/js/.provenance.json +19 -0
  85. package/templates/bases/next/pages-router/js/README.md +40 -0
  86. package/templates/bases/next/pages-router/js/jsconfig.json +7 -0
  87. package/templates/bases/next/pages-router/js/next.config.mjs +7 -0
  88. package/templates/bases/next/pages-router/js/package.json +15 -0
  89. package/templates/bases/next/pages-router/js/public/favicon.ico +0 -0
  90. package/templates/bases/next/pages-router/js/public/file.svg +1 -0
  91. package/templates/bases/next/pages-router/js/public/globe.svg +1 -0
  92. package/templates/bases/next/pages-router/js/public/next.svg +1 -0
  93. package/templates/bases/next/pages-router/js/public/vercel.svg +1 -0
  94. package/templates/bases/next/pages-router/js/public/window.svg +1 -0
  95. package/templates/bases/next/pages-router/js/src/pages/404.js +35 -0
  96. package/templates/bases/next/pages-router/js/src/pages/500.js +35 -0
  97. package/templates/bases/next/pages-router/js/src/pages/_app.js +20 -0
  98. package/templates/bases/next/pages-router/js/src/pages/_document.js +15 -0
  99. package/templates/bases/next/pages-router/js/src/pages/api/hello.js +5 -0
  100. package/templates/bases/next/pages-router/js/src/pages/blog/[slug].js +41 -0
  101. package/templates/bases/next/pages-router/js/src/pages/index.js +77 -0
  102. package/templates/bases/next/pages-router/js/src/styles/Home.module.css +149 -0
  103. package/templates/bases/next/pages-router/js/src/styles/globals.css +42 -0
  104. package/templates/bases/next/pages-router/ts/.provenance.json +19 -0
  105. package/templates/bases/next/pages-router/ts/README.md +40 -0
  106. package/templates/bases/next/pages-router/ts/next.config.ts +8 -0
  107. package/templates/bases/next/pages-router/ts/package.json +21 -0
  108. package/templates/bases/next/pages-router/ts/public/favicon.ico +0 -0
  109. package/templates/bases/next/pages-router/ts/public/file.svg +1 -0
  110. package/templates/bases/next/pages-router/ts/public/globe.svg +1 -0
  111. package/templates/bases/next/pages-router/ts/public/next.svg +1 -0
  112. package/templates/bases/next/pages-router/ts/public/vercel.svg +1 -0
  113. package/templates/bases/next/pages-router/ts/public/window.svg +1 -0
  114. package/templates/bases/next/pages-router/ts/src/pages/404.tsx +35 -0
  115. package/templates/bases/next/pages-router/ts/src/pages/500.tsx +35 -0
  116. package/templates/bases/next/pages-router/ts/src/pages/_app.tsx +21 -0
  117. package/templates/bases/next/pages-router/ts/src/pages/_document.tsx +15 -0
  118. package/templates/bases/next/pages-router/ts/src/pages/api/hello.ts +13 -0
  119. package/templates/bases/next/pages-router/ts/src/pages/blog/[slug].tsx +46 -0
  120. package/templates/bases/next/pages-router/ts/src/pages/index.tsx +77 -0
  121. package/templates/bases/next/pages-router/ts/src/styles/Home.module.css +149 -0
  122. package/templates/bases/next/pages-router/ts/src/styles/globals.css +42 -0
  123. package/templates/bases/next/pages-router/ts/tsconfig.json +29 -0
  124. package/templates/conditional/linting/biome/biome.json +31 -0
  125. package/templates/conditional/next/config/cloudflare/next.config.mjs +6 -0
  126. package/templates/conditional/next/config/cloudflare/next.config.ts +7 -0
  127. package/templates/conditional/next/config/node/next.config.mjs +6 -0
  128. package/templates/conditional/next/config/node/next.config.ts +7 -0
  129. package/templates/conditional/next/config/static/next.config.mjs +6 -0
  130. package/templates/conditional/next/config/static/next.config.ts +7 -0
  131. package/templates/conditional/next/config/turbopack/next.config.mjs +7 -0
  132. package/templates/conditional/next/config/turbopack/next.config.ts +8 -0
  133. package/templates/conditional/next/config/vercel/next.config.mjs +6 -0
  134. package/templates/conditional/next/config/vercel/next.config.ts +7 -0
  135. package/templates/conditional/next/config/webpack/next.config.mjs +9 -0
  136. package/templates/conditional/next/config/webpack/next.config.ts +10 -0
  137. package/templates/conditional/next/font/BUILD_SIZE_NOTE.md +17 -0
  138. package/templates/conditional/next/font/app-router/src/app/layout.js +25 -0
  139. package/templates/conditional/next/font/app-router/src/app/layout.tsx +30 -0
  140. package/templates/conditional/next/font/pages-router/src/pages/_app.js +20 -0
  141. package/templates/conditional/next/font/pages-router/src/pages/_app.tsx +21 -0
  142. package/templates/conditional/next/font/pages-router/src/pages/_document.js +18 -0
  143. package/templates/conditional/next/font/pages-router/src/pages/_document.tsx +18 -0
  144. package/templates/conditional/next/font/src/app/layout.js +25 -0
  145. package/templates/conditional/next/font/src/app/layout.tsx +30 -0
  146. package/templates/conditional/next/font/src/lib/fonts.js +11 -0
  147. package/templates/conditional/next/font/src/lib/fonts.ts +11 -0
  148. package/templates/conditional/next/font/src/pages/_app.js +20 -0
  149. package/templates/conditional/next/font/src/pages/_app.tsx +21 -0
  150. package/templates/conditional/next/font/src/pages/_document.js +18 -0
  151. package/templates/conditional/next/font/src/pages/_document.tsx +18 -0
  152. package/templates/conditional/next/image/BUILD_SIZE_NOTE.md +15 -0
  153. package/templates/conditional/next/image/app-router/src/components/ImageExample.jsx +34 -0
  154. package/templates/conditional/next/image/app-router/src/components/ImageExample.tsx +44 -0
  155. package/templates/conditional/next/image/pages-router/src/components/ImageExample.jsx +34 -0
  156. package/templates/conditional/next/image/pages-router/src/components/ImageExample.tsx +44 -0
  157. package/templates/conditional/next/image/src/components/ImageExample.jsx +44 -0
  158. package/templates/conditional/next/image/src/components/ImageExample.tsx +54 -0
  159. package/templates/conditional/next/image/src/components/image/index.js +1 -0
  160. package/templates/conditional/next/image/src/components/image/index.ts +2 -0
  161. package/templates/conditional/next/server-components/README.md +13 -0
  162. package/templates/conditional/next/server-components/eslint.config.mjs +47 -0
  163. package/templates/conditional/next/server-components/rules/no-server-imports-in-client.js +89 -0
  164. package/templates/conditional/next/server-components/rules/no-server-imports-in-client.mjs +88 -0
  165. package/templates/conditional/next/server-components/src/components/boundaries/ClientCounter.jsx +48 -0
  166. package/templates/conditional/next/server-components/src/components/boundaries/ClientCounter.tsx +53 -0
  167. package/templates/conditional/next/server-components/src/components/boundaries/ServerCard.jsx +41 -0
  168. package/templates/conditional/next/server-components/src/components/boundaries/ServerCard.tsx +46 -0
  169. package/templates/conditional/next/server-components/src/components/boundaries/index.js +2 -0
  170. package/templates/conditional/next/server-components/src/components/boundaries/index.ts +4 -0
  171. package/templates/conditional/testing/jest/hybrid/src/__tests__/App.test.jsx +11 -0
  172. package/templates/conditional/testing/jest/hybrid/src/__tests__/App.test.tsx +11 -0
  173. package/templates/conditional/testing/jest/none/src/__tests__/App.test.jsx +11 -0
  174. package/templates/conditional/testing/jest/none/src/__tests__/App.test.tsx +11 -0
  175. package/templates/conditional/testing/vitest/hybrid/src/__tests__/App.test.jsx +10 -0
  176. package/templates/conditional/testing/vitest/hybrid/src/__tests__/App.test.tsx +10 -0
  177. package/templates/conditional/testing/vitest/none/src/__tests__/App.test.jsx +10 -0
  178. package/templates/conditional/testing/vitest/none/src/__tests__/App.test.tsx +10 -0
  179. package/templates/css/tailwind/src/globals.css +10 -0
  180. package/templates/css/tailwind/src/themes.css +86 -4
  181. package/templates/ui/shadcn/lib/utils.js +6 -0
  182. package/templates/ui/shadcn/lib/utils.ts +6 -0
  183. package/templates/ui/shadcn/primitives/Button.jsx +48 -0
  184. package/templates/ui/shadcn/primitives/Button.tsx +59 -0
  185. package/templates/ui/shadcn/primitives/Card.jsx +55 -0
  186. package/templates/ui/shadcn/primitives/Card.tsx +73 -0
  187. package/templates/ui/shadcn/primitives/index.js +9 -0
  188. package/templates/ui/shadcn/primitives/index.ts +9 -0
  189. package/templates/css/bootstrap/src/themes.css +0 -14
  190. /package/templates/{css/bootstrap/src/globals.css → architectures/hybrid/src/features/.gitkeep} +0 -0
@@ -30,7 +30,7 @@ export const FRAMEWORK_CAPABILITIES = {
30
30
  CAPABILITIES.AGENT_DOCS,
31
31
  ],
32
32
  allowedArchitectures: ["feature-based", "type-based", "none"],
33
- allowedStyling: ["tailwind", "bootstrap", "none"],
33
+ allowedStyling: ["tailwind", "none"],
34
34
  allowedUiKits: ["shadcn", "none"],
35
35
  allowedLanguages: ["ts", "js"],
36
36
  allowedLinters: ["eslint", "oxlint", "biome", "none"],
@@ -76,7 +76,7 @@ export const FRAMEWORK_CAPABILITIES = {
76
76
  CAPABILITIES.AGENT_DOCS,
77
77
  ],
78
78
  allowedArchitectures: ["feature-based", "hybrid", "none"],
79
- allowedStyling: ["tailwind", "bootstrap", "none"],
79
+ allowedStyling: ["tailwind", "none"],
80
80
  allowedUiKits: ["shadcn", "none"],
81
81
  allowedLanguages: ["ts", "js"],
82
82
  allowedLinters: ["eslint", "biome", "none"],
@@ -132,8 +132,12 @@ export function isOptionCompatible(framework, optionKey, optionValue) {
132
132
  if (!desc) return false;
133
133
 
134
134
  switch (optionKey) {
135
- case "architecture":
136
- return desc.allowedArchitectures.includes(optionValue);
135
+ case "architecture": {
136
+ let val = optionValue;
137
+ if (val === "component" || val === "component-based") val = "type-based";
138
+ if (val === "feature") val = "feature-based";
139
+ return desc.allowedArchitectures.includes(val);
140
+ }
137
141
  case "styling":
138
142
  case "cssFramework":
139
143
  return desc.allowedStyling.includes(optionValue);
@@ -149,6 +153,9 @@ export function isOptionCompatible(framework, optionKey, optionValue) {
149
153
  case "bundler":
150
154
  return desc.allowedBundlers.includes(optionValue);
151
155
  case "adapter":
156
+ if (optionValue === "none") {
157
+ return desc.allowedAdapters.length > 0;
158
+ }
152
159
  return desc.allowedAdapters.includes(optionValue);
153
160
  case "router":
154
161
  // If framework has filesystem routing, external router options (true) are incompatible
@@ -156,6 +163,11 @@ export function isOptionCompatible(framework, optionKey, optionValue) {
156
163
  return optionValue === false || optionValue === "none";
157
164
  }
158
165
  return true;
166
+ case "reactCompiler":
167
+ if (optionValue === true) {
168
+ return hasCapability(framework, CAPABILITIES.REACT_COMPILER);
169
+ }
170
+ return true;
159
171
  default:
160
172
  return true;
161
173
  }
@@ -216,3 +228,182 @@ export function validateCompatibility(framework, options = {}) {
216
228
  errors,
217
229
  };
218
230
  }
231
+
232
+ /**
233
+ * Normalize framework input into a standard { name, variant } descriptor.
234
+ * @param {string | { name?: string, variant?: string } | null | undefined} framework
235
+ * @returns {{ name: string, variant: string }}
236
+ */
237
+ export function normalizeFramework(framework) {
238
+ if (!framework) {
239
+ return { name: "react", variant: "vite" };
240
+ }
241
+
242
+ if (typeof framework === "string") {
243
+ const trimmed = framework.trim();
244
+ if (!trimmed) {
245
+ return { name: "react", variant: "vite" };
246
+ }
247
+ if (trimmed.includes(":")) {
248
+ const [name, ...rest] = trimmed.split(":");
249
+ return { name, variant: rest.join(":") };
250
+ }
251
+ if (trimmed === "next") {
252
+ return { name: "next", variant: "app-router" };
253
+ }
254
+ if (trimmed === "react") {
255
+ return { name: "react", variant: "vite" };
256
+ }
257
+ return { name: trimmed, variant: "vite" };
258
+ }
259
+
260
+ if (typeof framework === "object") {
261
+ const name = framework.name || "react";
262
+ const variant =
263
+ framework.variant || (framework.name === "next" ? "app-router" : "vite");
264
+ return { name, variant };
265
+ }
266
+
267
+ return { name: "react", variant: "vite" };
268
+ }
269
+
270
+ /**
271
+ * Determine whether a prompt should be presented to the user based on framework capabilities.
272
+ * @param {string | object} framework
273
+ * @param {string} promptName
274
+ * @returns {boolean}
275
+ */
276
+ export function isPromptVisible(framework, promptName) {
277
+ const fw = normalizeFramework(framework);
278
+ const desc = getFrameworkDescriptor(fw);
279
+
280
+ switch (promptName) {
281
+ case "router":
282
+ return (
283
+ hasCapability(fw, CAPABILITIES.CLIENT_ROUTING) &&
284
+ Boolean(desc?.supportsRouterPrompt)
285
+ );
286
+ case "bundler":
287
+ return hasCapability(fw, CAPABILITIES.BUNDLER_SELECTION);
288
+ case "adapter":
289
+ return hasCapability(fw, CAPABILITIES.ADAPTERS);
290
+ case "reactCompiler":
291
+ return hasCapability(fw, CAPABILITIES.REACT_COMPILER);
292
+ default:
293
+ return true;
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Filter an array of choice objects or values based on framework compatibility.
299
+ * @param {string | object} framework
300
+ * @param {string} optionKey
301
+ * @param {Array<object | string>} choices
302
+ * @returns {Array<object | string>}
303
+ */
304
+ export function filterCompatibleChoices(framework, optionKey, choices = []) {
305
+ const fw = normalizeFramework(framework);
306
+ if (!Array.isArray(choices)) return [];
307
+ return choices.filter((choice) =>
308
+ isOptionCompatible(fw, optionKey, choice?.value ?? choice)
309
+ );
310
+ }
311
+
312
+ /**
313
+ * Get canonical default prompt responses for a given framework.
314
+ * @param {string | object} framework
315
+ * @param {{ projectName?: string }} [options]
316
+ * @returns {object}
317
+ */
318
+ export function getDefaultResponses(framework, { projectName = "my-app" } = {}) {
319
+ const fw = normalizeFramework(framework);
320
+
321
+ if (fw.name === "next") {
322
+ return {
323
+ projectName,
324
+ frameworkName: fw.name,
325
+ frameworkVariant: fw.variant,
326
+ architecture: "feature-based",
327
+ language: "ts",
328
+ cssFramework: "tailwind",
329
+ testing: "none",
330
+ router: false,
331
+ bundler: "turbopack",
332
+ adapter: "none",
333
+ uiKit: "none",
334
+ stateManagement: "none",
335
+ iconLibrary: "none",
336
+ apiClient: "none",
337
+ linter: "eslint",
338
+ formatter: "prettier",
339
+ docsLanguage: "en",
340
+ gitInit: true,
341
+ readme: true,
342
+ };
343
+ }
344
+
345
+ return {
346
+ projectName,
347
+ frameworkName: "react",
348
+ frameworkVariant: "vite",
349
+ architecture: "feature-based",
350
+ language: "ts",
351
+ cssFramework: "tailwind",
352
+ testing: "vitest",
353
+ router: true,
354
+ uiKit: "none",
355
+ stateManagement: "none",
356
+ iconLibrary: "none",
357
+ apiClient: "none",
358
+ linter: "eslint",
359
+ formatter: "prettier",
360
+ docsLanguage: "en",
361
+ gitInit: true,
362
+ readme: true,
363
+ };
364
+ }
365
+
366
+ /**
367
+ * Sanitize prompt responses for a framework by adjusting or resetting incompatible options.
368
+ * @param {string | object} framework
369
+ * @param {object} [responses]
370
+ * @returns {object}
371
+ */
372
+ export function sanitizeResponsesForFramework(framework, responses = {}) {
373
+ const fw = normalizeFramework(framework);
374
+ const sanitized = { ...(responses || {}) };
375
+ const defaults = getDefaultResponses(fw, {
376
+ projectName: sanitized.projectName || "my-app",
377
+ });
378
+
379
+ if (!isPromptVisible(fw, "router")) {
380
+ sanitized.router = false;
381
+ }
382
+ if (!isPromptVisible(fw, "bundler")) {
383
+ delete sanitized.bundler;
384
+ }
385
+ if (!isPromptVisible(fw, "adapter")) {
386
+ delete sanitized.adapter;
387
+ }
388
+ if (!isPromptVisible(fw, "reactCompiler")) {
389
+ if (sanitized.reactCompiler !== undefined) {
390
+ sanitized.reactCompiler = false;
391
+ }
392
+ }
393
+
394
+ for (const [key, val] of Object.entries(sanitized)) {
395
+ if (val === undefined || val === null) continue;
396
+ if (!isOptionCompatible(fw, key, val)) {
397
+ if (key in defaults) {
398
+ sanitized[key] = defaults[key];
399
+ } else if (key === "styling" && "cssFramework" in defaults) {
400
+ sanitized[key] = defaults.cssFramework;
401
+ } else {
402
+ sanitized[key] = defaults[key];
403
+ }
404
+ }
405
+ }
406
+
407
+ return sanitized;
408
+ }
409
+