generator-kodly-react-app 1.0.16 → 1.0.17

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 (37) hide show
  1. package/generators/app/index.js +11 -1
  2. package/generators/app/templates/.env.sandbox +4 -3
  3. package/generators/app/templates/.node-version +1 -0
  4. package/generators/app/templates/.pnpmfile.cjs +14 -0
  5. package/generators/app/templates/.prettierignore +13 -0
  6. package/generators/app/templates/AGENTS.md +14 -0
  7. package/generators/app/templates/TESTING.md +30 -0
  8. package/generators/app/templates/openapi-ts.config.ts +5 -1
  9. package/generators/app/templates/oxlint.config.ts +72 -0
  10. package/generators/app/templates/package.json +66 -52
  11. package/generators/app/templates/pnpm-workspace.yaml +19 -0
  12. package/generators/app/templates/scripts/check-duplicates.mjs +28 -0
  13. package/generators/app/templates/scripts/generated-files.mjs +21 -0
  14. package/generators/app/templates/src/components/layout/language-switcher.tsx +1 -1
  15. package/generators/app/templates/src/components/layout/theme-switcher.tsx +1 -1
  16. package/generators/app/templates/src/components/ui/input-group.tsx +0 -6
  17. package/generators/app/templates/src/index.css +70 -70
  18. package/generators/app/templates/src/lib/i18n.ts +26 -19
  19. package/generators/app/templates/src/modules/auth/components/auth-form.tsx +4 -1
  20. package/generators/app/templates/src/modules/auth/hooks/use-auth-hook.ts +3 -3
  21. package/generators/app/templates/src/modules/landing/components/auth-hero.tsx +2 -2
  22. package/generators/app/templates/src/modules/landing/components/welcome-hero.tsx +1 -1
  23. package/generators/app/templates/src/router.tsx +2 -2
  24. package/generators/app/templates/src/routes/__root.tsx +1 -1
  25. package/generators/app/templates/src/routes/auth/route.tsx +4 -1
  26. package/generators/app/templates/src/sdk/client.gen.ts +1 -5
  27. package/generators/app/templates/src/sdk/types.gen.ts +1 -1
  28. package/generators/app/templates/stylelint.config.js +55 -0
  29. package/generators/app/templates/tests/form-controls.test.tsx +39 -0
  30. package/generators/app/templates/tests/sdk.test.ts +49 -0
  31. package/generators/app/templates/tests/setup.ts +5 -0
  32. package/generators/app/templates/tsconfig.app.json +0 -1
  33. package/generators/app/templates/tsconfig.json +2 -1
  34. package/generators/app/templates/vite.config.js +5 -7
  35. package/generators/app/templates/vitest.config.ts +14 -0
  36. package/package.json +10 -9
  37. package/generators/app/templates/postcss.config.js +0 -5
@@ -16,17 +16,27 @@ export default class ReactAppGenerator extends BaseGenerator {
16
16
  this._writeLandingFiles();
17
17
  this._writeAppModuleFiles();
18
18
  this._writeSdkFiles();
19
+ this._writeDir('scripts');
20
+ this._writeDir('tests');
19
21
  }
20
22
 
21
23
  _writeConfigFiles() {
22
24
  const configFiles = [
23
25
  'package.json',
26
+ 'oxlint.config.ts',
27
+ 'stylelint.config.js',
28
+ 'vitest.config.ts',
29
+ '.node-version',
30
+ 'pnpm-workspace.yaml',
31
+ '.pnpmfile.cjs',
32
+ 'AGENTS.md',
33
+ '.prettierignore',
34
+ 'TESTING.md',
24
35
  'vite.config.js',
25
36
  'tsconfig.json',
26
37
  'tsconfig.app.json',
27
38
  'tsr.config.json',
28
39
  'tailwind.config.js',
29
- 'postcss.config.js',
30
40
  'index.html',
31
41
  'components.json',
32
42
  'openapi-ts.config.ts',
@@ -1,4 +1,5 @@
1
1
  BASE_URL=/
2
- PORT=3000
3
- ALLOWED_HOSTS=sandbox.app.kodly.dev
4
- VITE_API_BASE_URL=${VITE_API_BASE_URL} //injected by env variables - https://{port}-{projectSlug}-{userSlug}.sandbox.app.kodly.dev
2
+ PORT=5173
3
+ ALLOWED_HOSTS=localhost
4
+ # Kodly injects the connected API URL and preview host at server startup.
5
+ VITE_API_BASE_URL=
@@ -0,0 +1 @@
1
+ 26.12.1
@@ -0,0 +1,14 @@
1
+ // Hey API uses the JavaScript compiler API, which TypeScript 7 no longer ships.
2
+ // Keep its compiler private; the application and type-aware lint use TypeScript 7.
3
+ module.exports = {
4
+ hooks: {
5
+ /** @param {{name?: string, version?: string, dependencies?: Record<string, string>, peerDependencies?: Record<string, string>}} pkg */
6
+ readPackage(pkg) {
7
+ if (pkg.name === "@hey-api/openapi-ts" && pkg.version === "0.99.0") {
8
+ if (pkg.peerDependencies) delete pkg.peerDependencies.typescript;
9
+ pkg.dependencies = { ...pkg.dependencies, typescript: "6.0.3" };
10
+ }
11
+ return pkg;
12
+ },
13
+ },
14
+ };
@@ -0,0 +1,13 @@
1
+ node_modules/
2
+ dist/
3
+ build/
4
+ coverage/
5
+ .tanstack/
6
+ .kodly*/
7
+ *.tsbuildinfo
8
+ # Package managers own lockfile serialization.
9
+ pnpm-lock.yaml
10
+ package-lock.json
11
+ npm-shrinkwrap.json
12
+ yarn.lock
13
+ bun.lock
@@ -0,0 +1,14 @@
1
+ # Working in this project
2
+
3
+ Run `pnpm check` before handing off a change. It runs the production build and
4
+ type checker, Oxlint with TypeScript 7 type-aware rules, shadcn design rules,
5
+ Stylelint, Prettier, duplication detection and Vitest. Fix failures at their source.
6
+
7
+ Use `unknown` and validation at data boundaries instead of `any`. Handle promises.
8
+ Reuse the components in `src/components/ui` and the theme tokens in `src/index.css`.
9
+ Add named component variants for reusable styles. Avoid raw colours, inline styles
10
+ and arbitrary Tailwind values in screens. Add behaviour tests for changed features.
11
+
12
+ API transport is generated by `pnpm openapi-ts:dev` or `pnpm openapi-ts:sandbox`.
13
+ Keep handwritten code outside generated files. Do not weaken checks or turn off
14
+ tests to get a green build. All verification configuration belongs to this repo.
@@ -0,0 +1,30 @@
1
+ # Project checks
2
+
3
+ Install the declared package manager and run `pnpm install` once to create the lockfile.
4
+ Commit that lockfile; subsequent local and CI installs use `pnpm install --frozen-lockfile`.
5
+
6
+ `pnpm check` runs the build/type check, type-aware Oxlint, shadcn design rules, Stylelint, Prettier, duplicate detection and Vitest tests.
7
+ Each step is also a package script and fails with its original exit code. All tool
8
+ versions, rules and tests are owned by this repository and run without Kodly.
9
+
10
+ - `pnpm lint` checks TypeScript 7 types, unsafe `any`, promises, React Hooks, accessibility and shadcn design conventions.
11
+ - `pnpm lint:styles` checks CSS and requires tokens for colours outside theme token definitions.
12
+ - `pnpm format:check` checks formatting; `pnpm format` applies it explicitly.
13
+ - `pnpm check:duplicates` checks handwritten source at a 0.5% threshold with 50-token blocks.
14
+ - `pnpm test` runs unit and component tests in `tests/` and `src/` once; it fails if no tests exist. Use `pnpm test:watch` while developing.
15
+ - `pnpm check` runs every gate. A failure stops the command with a nonzero exit code.
16
+
17
+ The SDK generator invokes the installed formatter. `scripts/generated-files.mjs`
18
+ identifies generated transport by both its filename and Hey API banner. Only those
19
+ files receive the transport-specific lint allowances and duplication exclusion;
20
+ handwritten wrappers remain checked. Compilation and formatting still cover generated code.
21
+ Use the existing OpenAPI configuration when the backend changes.
22
+
23
+ Browser/API acceptance suites can be added with project-local dependencies and
24
+ configuration when needed. Save their test cases in source so they also run in CI;
25
+ do not import test tools from a Kodly scratch directory.
26
+
27
+ The app and type-aware linter use TypeScript 7. The pinned Hey API generator still
28
+ uses the older JavaScript compiler API internally. `.pnpmfile.cjs` gives only that
29
+ package a private TypeScript 6.0.3 dependency; it never changes the app compiler.
30
+ Remove this scoped hook once Hey API supports the TypeScript 7 compiler API.
@@ -9,10 +9,14 @@ const env = loadEnv(mode, process.cwd(), "");
9
9
 
10
10
  export default defineConfig({
11
11
  input: `${env.VITE_API_BASE_URL}/v3/api-docs`,
12
- output: "src/sdk",
12
+ output: {
13
+ path: "src/sdk",
14
+ postProcess: ["prettier"],
15
+ },
13
16
  plugins: [
14
17
  {
15
18
  name: "@hey-api/client-axios",
19
+ baseUrl: false,
16
20
  runtimeConfigPath: "@/openapi-client-config.ts",
17
21
  },
18
22
  "@tanstack/react-query",
@@ -0,0 +1,72 @@
1
+ import { defineConfig } from "oxlint";
2
+ import { generatedFiles } from "./scripts/generated-files.mjs";
3
+
4
+ // TypeScript 7 uses the native type checker; tsgolint shares that compiler.
5
+ const typeRules = {
6
+ "typescript/no-explicit-any": "error",
7
+ "typescript/no-unsafe-assignment": "error",
8
+ "typescript/no-unsafe-argument": "error",
9
+ "typescript/no-unsafe-call": "error",
10
+ "typescript/no-unsafe-member-access": "error",
11
+ "typescript/no-unsafe-return": "error",
12
+ "typescript/no-floating-promises": "error",
13
+ "typescript/no-misused-promises": "error",
14
+ "typescript/no-non-null-assertion": "error",
15
+ "typescript/switch-exhaustiveness-check": "error",
16
+ } as const;
17
+ const generated = generatedFiles();
18
+
19
+ export default defineConfig({
20
+ plugins: ["typescript", "react", "jsx-a11y", "import"],
21
+ jsPlugins: ["@shadcn/lint"],
22
+ categories: { correctness: "error" },
23
+ ignorePatterns: [
24
+ "node_modules",
25
+ "dist",
26
+ "build",
27
+ "coverage",
28
+ ".tanstack",
29
+ ".kodly*",
30
+ ],
31
+ env: { browser: true, node: true },
32
+ rules: {
33
+ ...typeRules,
34
+ "jsx-a11y/prefer-tag-over-role": "off",
35
+ "react/rules-of-hooks": "error",
36
+ "react/exhaustive-deps": "error",
37
+ "shadcn/no-restyle": ["error", { allow: ["layout"] }],
38
+ "shadcn/no-raw-colors": "error",
39
+ "shadcn/no-arbitrary-values": "error",
40
+ "shadcn/no-inline-styles": "error",
41
+ "shadcn/no-unknown-classes": "error",
42
+ "shadcn/require-static-classes": "error",
43
+ },
44
+ overrides: [
45
+ {
46
+ files: ["src/components/ui/**"],
47
+ rules: {
48
+ "shadcn/no-restyle": "off",
49
+ "shadcn/no-arbitrary-values": "off",
50
+ "shadcn/require-static-classes": "off",
51
+ },
52
+ },
53
+ // Only verified generator output gets type-rule exceptions. Handwritten SDK
54
+ // wrappers remain checked; parsing and the full TypeScript build cover both.
55
+ ...(generated.length
56
+ ? [
57
+ {
58
+ files: generated,
59
+ rules: {
60
+ ...Object.fromEntries(
61
+ Object.keys(typeRules).map((rule) => [rule, "off"] as const),
62
+ ),
63
+ "typescript/no-redundant-type-constituents": "off" as const,
64
+ "typescript/no-base-to-string": "off" as const,
65
+ "typescript/restrict-template-expressions": "off" as const,
66
+ "typescript/no-misused-spread": "off" as const,
67
+ },
68
+ },
69
+ ]
70
+ : []),
71
+ ],
72
+ });
@@ -3,65 +3,79 @@
3
3
  "private": true,
4
4
  "type": "module",
5
5
  "version": "0.0.1",
6
- "packageManager": "pnpm@10.28.2",
6
+ "packageManager": "pnpm@12.14.0",
7
7
  "scripts": {
8
- "generate-routes": "tsr generate",
9
- "watch-routes": "tsr watch",
10
- "vite": "vite",
11
- "vite:dev": "vite --mode dev",
12
- "vite:sandbox": "vite --mode sandbox",
13
- "dev": "npm-run-all --parallel watch-routes vite:dev",
14
- "dev:sandbox": "npm-run-all --parallel watch-routes vite:sandbox",
15
- "build": "pnpm run generate-routes && vite build --mode dev && tsc",
16
- "build:sandbox": "pnpm run generate-routes && vite build --mode sandbox && tsc",
8
+ "dev": "vite --mode dev",
9
+ "dev:sandbox": "vite --mode sandbox",
10
+ "build": "vite build --mode dev && tsc --noEmit",
11
+ "build:sandbox": "vite build --mode sandbox && tsc --noEmit",
17
12
  "serve": "vite preview",
18
13
  "openapi-ts:dev": "MODE=dev openapi-ts",
19
- "openapi-ts:sandbox": "MODE=sandbox openapi-ts"
14
+ "openapi-ts:sandbox": "MODE=sandbox openapi-ts",
15
+ "check": "pnpm run build && pnpm run lint && pnpm run lint:styles && pnpm run format:check && pnpm run check:duplicates && pnpm test",
16
+ "lint": "oxlint --type-aware --deny-warnings",
17
+ "format": "prettier --write .",
18
+ "format:check": "prettier --check .",
19
+ "check:duplicates": "node scripts/check-duplicates.mjs",
20
+ "test": "vitest run",
21
+ "lint:styles": "stylelint \"src/**/*.css\" --max-warnings 0",
22
+ "test:watch": "vitest"
20
23
  },
21
24
  "dependencies": {
22
- "@hey-api/openapi-ts": "^0.92.3",
23
- "@hookform/resolvers": "^5.2.2",
24
- "@radix-ui/react-label": "^2.1.8",
25
- "@radix-ui/react-select": "^2.2.6",
26
- "@radix-ui/react-separator": "^1.1.8",
27
- "@radix-ui/react-slot": "^1.2.4",
28
- "@tanstack/react-query": "^5.90.20",
29
- "@tanstack/react-router": "^1.158.1",
30
- "@tanstack/router-plugin": "^1.158.1",
31
- "axios": "^1.13.4",
32
- "class-variance-authority": "^0.7.1",
33
- "clsx": "^2.1.1",
34
- "dotenv": "^17.2.4",
35
- "i18next": "^25.8.4",
36
- "jotai": "^2.17.1",
37
- "lucide-react": "^0.563.0",
38
- "react": "^19.2.4",
39
- "react-dom": "^19.2.4",
40
- "react-hook-form": "^7.71.1",
41
- "react-i18next": "^16.5.4",
42
- "sonner": "^2.0.7",
43
- "tailwind-merge": "^3.4.0",
44
- "tailwindcss-animate": "^1.0.7",
45
- "tw-animate-css": "^1.4.0",
46
- "zod": "^4.3.6"
25
+ "@hookform/resolvers": "6.0.0",
26
+ "@radix-ui/react-label": "2.1.17",
27
+ "@radix-ui/react-select": "2.4.1",
28
+ "@radix-ui/react-separator": "1.2.0",
29
+ "@radix-ui/react-slot": "1.5.0",
30
+ "@tanstack/react-query": "5.104.5",
31
+ "@tanstack/react-router": "1.170.45",
32
+ "axios": "1.20.0",
33
+ "class-variance-authority": "0.7.1",
34
+ "clsx": "2.1.1",
35
+ "i18next": "26.5.4",
36
+ "jotai": "3.0.1",
37
+ "lucide-react": "1.61.0",
38
+ "react": "19.3.1",
39
+ "react-dom": "19.3.1",
40
+ "react-hook-form": "7.90.0",
41
+ "react-i18next": "17.1.0",
42
+ "sonner": "2.0.8",
43
+ "tailwind-merge": "3.7.0",
44
+ "tw-animate-css": "1.4.0",
45
+ "zod": "4.6.7"
47
46
  },
48
47
  "devDependencies": {
49
- "@tailwindcss/postcss": "^4.1.18",
50
- "@tailwindcss/vite": "^4.1.18",
51
- "@tanstack/router-cli": "^1.158.1",
52
- "@types/node": "^25.2.1",
53
- "@types/react": "^19.2.13",
54
- "@types/react-dom": "^19.2.3",
55
- "@vitejs/plugin-react": "^5.1.3",
56
- "babel-plugin-react-compiler": "^1.0.0",
57
- "npm-run-all": "^4.1.5",
58
- "tailwindcss": "^4.1.18",
59
- "typescript": "^5.9.3",
60
- "vite": "^7.3.1"
48
+ "@tailwindcss/vite": "4.3.3",
49
+ "@types/node": "26.6.5",
50
+ "@types/react": "19.3.0",
51
+ "@types/react-dom": "19.3.0",
52
+ "@vitejs/plugin-react": "6.1.2",
53
+ "babel-plugin-react-compiler": "1.0.0",
54
+ "prettier": "3.9.10",
55
+ "tailwindcss": "4.3.3",
56
+ "typescript": "7.0.2",
57
+ "vite": "8.3.5",
58
+ "jscpd": "5.4.2",
59
+ "tsx": "4.23.15",
60
+ "oxlint": "1.89.0",
61
+ "oxlint-tsgolint": "7.0.2004",
62
+ "@shadcn/lint": "0.2.3",
63
+ "stylelint": "17.16.0",
64
+ "stylelint-config-standard": "40.0.0",
65
+ "stylelint-declaration-strict-value": "1.12.1",
66
+ "vitest": "5.0.5",
67
+ "@testing-library/react": "16.3.3",
68
+ "@testing-library/dom": "10.4.2",
69
+ "@testing-library/jest-dom": "7.0.1",
70
+ "@testing-library/user-event": "14.6.8",
71
+ "jsdom": "30.1.3",
72
+ "@hey-api/openapi-ts": "0.99.0",
73
+ "@tanstack/router-plugin": "1.168.47",
74
+ "dotenv": "18.0.8",
75
+ "@rolldown/plugin-babel": "0.2.4",
76
+ "@babel/core": "8.0.7"
61
77
  },
62
- "pnpm": {
63
- "onlyBuiltDependencies": [
64
- "esbuild"
65
- ]
78
+ "engines": {
79
+ "node": ">=26.12.1"
66
80
  }
67
81
  }
@@ -0,0 +1,19 @@
1
+ # Verification never silently reinstalls or rewrites dependencies. Run pnpm install first.
2
+ verifyDepsBeforeRun: error
3
+ engineStrict: true
4
+ saveExact: true
5
+ allowBuilds:
6
+ esbuild: true
7
+ minimumReleaseAgeExclude:
8
+ - i18next@26.5.4
9
+ - jscpd-darwin-arm64@5.4.2
10
+ - jscpd-darwin-x64@5.4.2
11
+ - jscpd-linux-arm64-gnu@5.4.2
12
+ - jscpd-linux-arm64-musl@5.4.2
13
+ - jscpd-linux-x64-gnu@5.4.2
14
+ - jscpd-linux-x64-musl@5.4.2
15
+ - jscpd-windows-arm64-msvc@5.4.2
16
+ - jscpd-windows-x64-msvc@5.4.2
17
+ - jscpd@5.4.2
18
+ - lucide-react@1.61.0
19
+ - vite@8.3.5
@@ -0,0 +1,28 @@
1
+ import { createRequire } from "node:module";
2
+ import { spawnSync } from "node:child_process";
3
+ import path from "node:path";
4
+ import { generatedFiles } from "./generated-files.mjs";
5
+
6
+ const require = createRequire(import.meta.url);
7
+ const binary = path.join(
8
+ path.dirname(require.resolve("jscpd/package.json")),
9
+ "run-jscpd.js",
10
+ );
11
+ const result = spawnSync(
12
+ process.execPath,
13
+ [
14
+ binary,
15
+ "src",
16
+ "--min-tokens",
17
+ "50",
18
+ "--threshold",
19
+ "0.5",
20
+ "--reporters",
21
+ "consoleFull",
22
+ "--ignore",
23
+ generatedFiles().join(","),
24
+ ],
25
+ { stdio: "inherit" },
26
+ );
27
+ if (result.error) console.error(result.error.message);
28
+ process.exitCode = result.status ?? 1;
@@ -0,0 +1,21 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ // Keep this policy in source: handwritten SDK wrappers must still be checked.
5
+ /** @returns {string[]} */
6
+ export function generatedFiles(directory = "src") {
7
+ if (!fs.existsSync(directory)) return [];
8
+ return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
9
+ const file = path.join(directory, entry.name);
10
+ if (entry.isSymbolicLink()) return [];
11
+ if (entry.isDirectory()) return generatedFiles(file);
12
+ const generated =
13
+ /\.gen\.[cm]?[jt]sx?$/.test(file) || /\/client\/index\.ts$/.test(file);
14
+ return generated &&
15
+ /^\/\/ This file is auto-generated by @hey-api\/openapi-ts\r?\n/.test(
16
+ fs.readFileSync(file, "utf8"),
17
+ )
18
+ ? [file.replaceAll(path.sep, "/")]
19
+ : [];
20
+ });
21
+ }
@@ -32,7 +32,7 @@ export function LanguageSwitcher() {
32
32
 
33
33
  return (
34
34
  <Select value={currentLang} onValueChange={handleLanguageChange}>
35
- <SelectTrigger className="min-w-[120px]">
35
+ <SelectTrigger className="min-w-30">
36
36
  <SelectValue />
37
37
  </SelectTrigger>
38
38
  <SelectContent>
@@ -9,7 +9,7 @@ export function ThemeSwitcher() {
9
9
  const themes: ThemeTypes[] = ["light", "dark", "system"];
10
10
  const currentIndex = themes.indexOf(theme);
11
11
  const nextIndex = (currentIndex + 1) % themes.length;
12
- setTheme(themes[nextIndex]);
12
+ setTheme(themes[nextIndex] ?? "system");
13
13
  };
14
14
 
15
15
  const getIcon = () => {
@@ -68,12 +68,6 @@ function InputGroupAddon({
68
68
  data-slot="input-group-addon"
69
69
  data-align={align}
70
70
  className={cn(inputGroupAddonVariants({ align }), className)}
71
- onClick={(e) => {
72
- if ((e.target as HTMLElement).closest("button")) {
73
- return;
74
- }
75
- e.currentTarget.parentElement?.querySelector("input")?.focus();
76
- }}
77
71
  {...props}
78
72
  />
79
73
  );
@@ -1,20 +1,19 @@
1
1
  @import "tailwindcss";
2
-
3
- @plugin "tailwindcss-animate";
4
2
  @import "tw-animate-css";
5
3
 
6
4
  @custom-variant dark (&:is(.dark *));
7
5
 
8
6
  body {
9
7
  @apply m-0;
8
+
10
9
  font-family:
11
10
  system-ui,
12
11
  -apple-system,
13
12
  "Segoe UI",
14
- "Roboto",
15
- "Oxygen",
16
- "Ubuntu",
17
- "Cantarell",
13
+ Roboto,
14
+ Oxygen,
15
+ Ubuntu,
16
+ Cantarell,
18
17
  "Fira Sans",
19
18
  "Droid Sans",
20
19
  "Helvetica Neue",
@@ -24,74 +23,74 @@ body {
24
23
  }
25
24
 
26
25
  :root {
27
- --background: oklch(1 0 0);
28
- --foreground: oklch(0.147 0.004 49.25);
29
- --primary: oklch(0.216 0.006 56.043);
30
- --primary-foreground: oklch(0.985 0.001 106.423);
31
- --secondary: oklch(0.97 0.001 106.424);
32
- --secondary-foreground: oklch(0.216 0.006 56.043);
33
- --muted: oklch(0.97 0.001 106.424);
34
- --muted-foreground: oklch(0.553 0.013 58.071);
35
- --accent: oklch(0.97 0.001 106.424);
36
- --accent-foreground: oklch(0.216 0.006 56.043);
37
- --destructive: oklch(0.577 0.245 27.325);
38
- --destructive-foreground: oklch(0.985 0 0);
39
- --border: oklch(0.923 0.003 48.717);
40
- --input: oklch(0.923 0.003 48.717);
41
- --ring: oklch(0.709 0.01 56.259);
26
+ --background: oklch(100% 0 0deg);
27
+ --foreground: oklch(14.7% 0.004 49.25deg);
28
+ --primary: oklch(21.6% 0.006 56.043deg);
29
+ --primary-foreground: oklch(98.5% 0.001 106.423deg);
30
+ --secondary: oklch(97% 0.001 106.424deg);
31
+ --secondary-foreground: oklch(21.6% 0.006 56.043deg);
32
+ --muted: oklch(97% 0.001 106.424deg);
33
+ --muted-foreground: oklch(55.3% 0.013 58.071deg);
34
+ --accent: oklch(97% 0.001 106.424deg);
35
+ --accent-foreground: oklch(21.6% 0.006 56.043deg);
36
+ --destructive: oklch(57.7% 0.245 27.325deg);
37
+ --destructive-foreground: oklch(98.5% 0 0deg);
38
+ --border: oklch(92.3% 0.003 48.717deg);
39
+ --input: oklch(92.3% 0.003 48.717deg);
40
+ --ring: oklch(70.9% 0.01 56.259deg);
42
41
  --radius: 0.625rem;
43
- --card: oklch(1 0 0);
44
- --card-foreground: oklch(0.147 0.004 49.25);
45
- --popover: oklch(1 0 0);
46
- --popover-foreground: oklch(0.147 0.004 49.25);
47
- --chart-1: oklch(0.646 0.222 41.116);
48
- --chart-2: oklch(0.6 0.118 184.704);
49
- --chart-3: oklch(0.398 0.07 227.392);
50
- --chart-4: oklch(0.828 0.189 84.429);
51
- --chart-5: oklch(0.769 0.188 70.08);
52
- --sidebar: oklch(0.985 0.001 106.423);
53
- --sidebar-foreground: oklch(0.147 0.004 49.25);
54
- --sidebar-primary: oklch(0.216 0.006 56.043);
55
- --sidebar-primary-foreground: oklch(0.985 0.001 106.423);
56
- --sidebar-accent: oklch(0.97 0.001 106.424);
57
- --sidebar-accent-foreground: oklch(0.216 0.006 56.043);
58
- --sidebar-border: oklch(0.923 0.003 48.717);
59
- --sidebar-ring: oklch(0.709 0.01 56.259);
42
+ --card: oklch(100% 0 0deg);
43
+ --card-foreground: oklch(14.7% 0.004 49.25deg);
44
+ --popover: oklch(100% 0 0deg);
45
+ --popover-foreground: oklch(14.7% 0.004 49.25deg);
46
+ --chart-1: oklch(64.6% 0.222 41.116deg);
47
+ --chart-2: oklch(60% 0.118 184.704deg);
48
+ --chart-3: oklch(39.8% 0.07 227.392deg);
49
+ --chart-4: oklch(82.8% 0.189 84.429deg);
50
+ --chart-5: oklch(76.9% 0.188 70.08deg);
51
+ --sidebar: oklch(98.5% 0.001 106.423deg);
52
+ --sidebar-foreground: oklch(14.7% 0.004 49.25deg);
53
+ --sidebar-primary: oklch(21.6% 0.006 56.043deg);
54
+ --sidebar-primary-foreground: oklch(98.5% 0.001 106.423deg);
55
+ --sidebar-accent: oklch(97% 0.001 106.424deg);
56
+ --sidebar-accent-foreground: oklch(21.6% 0.006 56.043deg);
57
+ --sidebar-border: oklch(92.3% 0.003 48.717deg);
58
+ --sidebar-ring: oklch(70.9% 0.01 56.259deg);
60
59
  }
61
60
 
62
61
  .dark {
63
- --background: oklch(0.147 0.004 49.25);
64
- --foreground: oklch(0.985 0.001 106.423);
65
- --primary: oklch(0.923 0.003 48.717);
66
- --primary-foreground: oklch(0.216 0.006 56.043);
67
- --secondary: oklch(0.268 0.007 34.298);
68
- --secondary-foreground: oklch(0.985 0.001 106.423);
69
- --muted: oklch(0.268 0.007 34.298);
70
- --muted-foreground: oklch(0.709 0.01 56.259);
71
- --accent: oklch(0.268 0.007 34.298);
72
- --accent-foreground: oklch(0.985 0.001 106.423);
73
- --destructive: oklch(0.704 0.191 22.216);
74
- --destructive-foreground: oklch(0.637 0.237 25.331);
75
- --border: oklch(1 0 0 / 10%);
76
- --input: oklch(1 0 0 / 15%);
77
- --ring: oklch(0.553 0.013 58.071);
78
- --card: oklch(0.216 0.006 56.043);
79
- --card-foreground: oklch(0.985 0.001 106.423);
80
- --popover: oklch(0.216 0.006 56.043);
81
- --popover-foreground: oklch(0.985 0.001 106.423);
82
- --chart-1: oklch(0.488 0.243 264.376);
83
- --chart-2: oklch(0.696 0.17 162.48);
84
- --chart-3: oklch(0.769 0.188 70.08);
85
- --chart-4: oklch(0.627 0.265 303.9);
86
- --chart-5: oklch(0.645 0.246 16.439);
87
- --sidebar: oklch(0.216 0.006 56.043);
88
- --sidebar-foreground: oklch(0.985 0.001 106.423);
89
- --sidebar-primary: oklch(0.488 0.243 264.376);
90
- --sidebar-primary-foreground: oklch(0.985 0.001 106.423);
91
- --sidebar-accent: oklch(0.268 0.007 34.298);
92
- --sidebar-accent-foreground: oklch(0.985 0.001 106.423);
93
- --sidebar-border: oklch(1 0 0 / 10%);
94
- --sidebar-ring: oklch(0.553 0.013 58.071);
62
+ --background: oklch(14.7% 0.004 49.25deg);
63
+ --foreground: oklch(98.5% 0.001 106.423deg);
64
+ --primary: oklch(92.3% 0.003 48.717deg);
65
+ --primary-foreground: oklch(21.6% 0.006 56.043deg);
66
+ --secondary: oklch(26.8% 0.007 34.298deg);
67
+ --secondary-foreground: oklch(98.5% 0.001 106.423deg);
68
+ --muted: oklch(26.8% 0.007 34.298deg);
69
+ --muted-foreground: oklch(70.9% 0.01 56.259deg);
70
+ --accent: oklch(26.8% 0.007 34.298deg);
71
+ --accent-foreground: oklch(98.5% 0.001 106.423deg);
72
+ --destructive: oklch(70.4% 0.191 22.216deg);
73
+ --destructive-foreground: oklch(63.7% 0.237 25.331deg);
74
+ --border: oklch(100% 0 0deg / 10%);
75
+ --input: oklch(100% 0 0deg / 15%);
76
+ --ring: oklch(55.3% 0.013 58.071deg);
77
+ --card: oklch(21.6% 0.006 56.043deg);
78
+ --card-foreground: oklch(98.5% 0.001 106.423deg);
79
+ --popover: oklch(21.6% 0.006 56.043deg);
80
+ --popover-foreground: oklch(98.5% 0.001 106.423deg);
81
+ --chart-1: oklch(48.8% 0.243 264.376deg);
82
+ --chart-2: oklch(69.6% 0.17 162.48deg);
83
+ --chart-3: oklch(76.9% 0.188 70.08deg);
84
+ --chart-4: oklch(62.7% 0.265 303.9deg);
85
+ --chart-5: oklch(64.5% 0.246 16.439deg);
86
+ --sidebar: oklch(21.6% 0.006 56.043deg);
87
+ --sidebar-foreground: oklch(98.5% 0.001 106.423deg);
88
+ --sidebar-primary: oklch(48.8% 0.243 264.376deg);
89
+ --sidebar-primary-foreground: oklch(98.5% 0.001 106.423deg);
90
+ --sidebar-accent: oklch(26.8% 0.007 34.298deg);
91
+ --sidebar-accent-foreground: oklch(98.5% 0.001 106.423deg);
92
+ --sidebar-border: oklch(100% 0 0deg / 10%);
93
+ --sidebar-ring: oklch(55.3% 0.013 58.071deg);
95
94
  }
96
95
 
97
96
  @theme inline {
@@ -140,6 +139,7 @@ body {
140
139
  * {
141
140
  @apply border-border outline-ring/50;
142
141
  }
142
+
143
143
  body {
144
144
  @apply bg-background text-foreground;
145
145
  }
@@ -33,26 +33,31 @@ if (!i18n.isInitialized) {
33
33
  const lng = getStoredLanguage();
34
34
  const enResources = mergeTranslations("en");
35
35
  const jaResources = mergeTranslations("ja");
36
- i18n.use(initReactI18next).init({
37
- resources: {
38
- en: {
39
- translation: enResources,
40
- ...enResources,
36
+ i18n
37
+ .use(initReactI18next)
38
+ .init({
39
+ resources: {
40
+ en: {
41
+ translation: enResources,
42
+ ...enResources,
43
+ },
44
+ ja: {
45
+ translation: jaResources,
46
+ ...jaResources,
47
+ },
41
48
  },
42
- ja: {
43
- translation: jaResources,
44
- ...jaResources,
49
+ defaultNS: "translation",
50
+ ns: ["translation", "auth", "app", "landing", "common", "theme"],
51
+ supportedLngs: supportedLanguages,
52
+ lng,
53
+ fallbackLng: defaultLanguage,
54
+ interpolation: {
55
+ escapeValue: false,
45
56
  },
46
- },
47
- defaultNS: "translation",
48
- ns: ["translation", "auth", "app", "landing", "common", "theme"],
49
- supportedLngs: supportedLanguages,
50
- lng,
51
- fallbackLng: defaultLanguage,
52
- interpolation: {
53
- escapeValue: false,
54
- },
55
- });
57
+ })
58
+ .catch((error: unknown) =>
59
+ console.error("Translation initialization failed", error),
60
+ );
56
61
  setLanguageHooks(lng);
57
62
  }
58
63
 
@@ -66,7 +71,9 @@ export const changeLanguage = (language: SupportedLanguages) => {
66
71
  } catch {
67
72
  // Fallback if localStorage is not available
68
73
  }
69
- i18n.changeLanguage(language);
74
+ i18n
75
+ .changeLanguage(language)
76
+ .catch((error: unknown) => console.error("Language change failed", error));
70
77
  setLanguageHooks(language);
71
78
  };
72
79
 
@@ -1,3 +1,4 @@
1
+ import { handleBackendError } from "@/lib/utils/error-handler";
1
2
  import type { ReactNode } from "react";
2
3
  import type {
3
4
  FieldPath,
@@ -50,7 +51,9 @@ export function AuthForm<T extends FieldValues>({
50
51
  <form
51
52
  noValidate
52
53
  className="flex flex-col gap-4"
53
- onSubmit={form.handleSubmit(onSubmit)}
54
+ onSubmit={(event) => {
55
+ form.handleSubmit(onSubmit)(event).catch(handleBackendError);
56
+ }}
54
57
  >
55
58
  {fields.map(({ name, label, ...input }) => (
56
59
  <FormField
@@ -144,7 +144,7 @@ export const useLogout = () => {
144
144
  setAuthToken("");
145
145
  setCurrentUser(undefined);
146
146
  setTokenInClient();
147
- navigate({ to: DEFAULT_NON_LOGGED_IN_ROUTE });
147
+ return navigate({ to: DEFAULT_NON_LOGGED_IN_ROUTE });
148
148
  },
149
149
  });
150
150
  };
@@ -159,7 +159,7 @@ export const useSendOtp = () => {
159
159
  // Extract email from the mutation variables and navigate to reset-password
160
160
  const email = variables.query?.authId;
161
161
  if (email) {
162
- navigate({
162
+ return navigate({
163
163
  to: "/auth/reset-password",
164
164
  search: { email },
165
165
  });
@@ -183,7 +183,7 @@ export const useResetPassword = () => {
183
183
  toast.success(
184
184
  response.message || t("auth.messages.passwordResetSuccess"),
185
185
  );
186
- navigate({ to: LoginRoute.to });
186
+ return navigate({ to: LoginRoute.to });
187
187
  },
188
188
  onError: (err) => {
189
189
  console.error("Reset password error:", err);
@@ -17,7 +17,7 @@ export function AuthHero() {
17
17
  </p>
18
18
  <div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
19
19
  <Link to={LoginRoute.to}>
20
- <Button size="lg" className="w-full sm:w-auto min-w-[150px]">
20
+ <Button size="lg" className="w-full sm:w-auto min-w-37.5">
21
21
  {t("hero.loginButton")}
22
22
  </Button>
23
23
  </Link>
@@ -25,7 +25,7 @@ export function AuthHero() {
25
25
  <Button
26
26
  size="lg"
27
27
  variant="outline"
28
- className="w-full sm:w-auto min-w-[150px]"
28
+ className="w-full sm:w-auto min-w-37.5"
29
29
  >
30
30
  {t("hero.signupButton")}
31
31
  </Button>
@@ -16,7 +16,7 @@ export function WelcomeHero() {
16
16
  </p>
17
17
  <div className="flex justify-center items-center">
18
18
  <Link to={DEFAULT_LOGGED_IN_ROUTE}>
19
- <Button size="lg" className="w-full sm:w-auto min-w-[150px]">
19
+ <Button size="lg" className="w-full sm:w-auto min-w-37.5">
20
20
  {t("landing.hero.getStartedButton")}
21
21
  </Button>
22
22
  </Link>
@@ -5,7 +5,7 @@ export const router = createRouter({
5
5
  basepath: import.meta.env.BASE_URL,
6
6
  routeTree,
7
7
  context: {
8
- token: undefined!,
9
- data: undefined!,
8
+ token: "",
9
+ data: undefined,
10
10
  },
11
11
  });
@@ -3,7 +3,7 @@ import type { AuthResponseDtoUserDetailDto } from "@/sdk";
3
3
 
4
4
  interface RouterContext {
5
5
  token: string;
6
- data: AuthResponseDtoUserDetailDto;
6
+ data: AuthResponseDtoUserDetailDto | undefined;
7
7
  }
8
8
 
9
9
  export const Route = createRootRouteWithContext<RouterContext>()({
@@ -8,6 +8,7 @@ import { AuthLayout } from "@/modules/auth/layouts/auth-layout";
8
8
  import { DEFAULT_LOGGED_IN_ROUTE } from "@/lib/routes";
9
9
  import { useAtomValue } from "jotai";
10
10
  import { authTokenAtom } from "@/modules/auth/hooks/use-auth-hook";
11
+ import { handleBackendError } from "@/lib/utils/error-handler";
11
12
  import { useEffect } from "react";
12
13
 
13
14
  export const Route = createFileRoute("/auth")({
@@ -28,7 +29,9 @@ function RouteComponent() {
28
29
  // Navigate after successful authentication
29
30
  useEffect(() => {
30
31
  if (token) {
31
- navigate({ to: DEFAULT_LOGGED_IN_ROUTE, replace: true });
32
+ navigate({ to: DEFAULT_LOGGED_IN_ROUTE, replace: true }).catch(
33
+ handleBackendError,
34
+ );
32
35
  }
33
36
  }, [token, navigate]);
34
37
 
@@ -23,9 +23,5 @@ export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (
23
23
  ) => Config<Required<ClientOptions> & T>;
24
24
 
25
25
  export const client = createClient(
26
- createClientConfig(
27
- createConfig<ClientOptions2>({
28
- baseURL: "https://sandbox.beta.kodly.dev/proxy/95-2",
29
- }),
30
- ),
26
+ createClientConfig(createConfig<ClientOptions2>()),
31
27
  );
@@ -1,7 +1,7 @@
1
1
  // This file is auto-generated by @hey-api/openapi-ts
2
2
 
3
3
  export type ClientOptions = {
4
- baseURL: "https://sandbox.beta.kodly.dev/proxy/95-2" | (string & {});
4
+ baseURL?: string;
5
5
  };
6
6
 
7
7
  export type AuthResponseDtoUserDetailDto = {
@@ -0,0 +1,55 @@
1
+ export default {
2
+ extends: ["stylelint-config-standard"],
3
+ plugins: ["stylelint-declaration-strict-value"],
4
+ rules: {
5
+ "at-rule-prelude-no-invalid": [
6
+ true,
7
+ {
8
+ ignoreAtRules: [
9
+ "apply",
10
+ "theme",
11
+ "custom-variant",
12
+ "utility",
13
+ "variant",
14
+ "source",
15
+ "plugin",
16
+ "config",
17
+ "reference",
18
+ ],
19
+ },
20
+ ],
21
+ "at-rule-no-unknown": [
22
+ true,
23
+ {
24
+ ignoreAtRules: [
25
+ "theme",
26
+ "utility",
27
+ "variant",
28
+ "custom-variant",
29
+ "apply",
30
+ "source",
31
+ "plugin",
32
+ "config",
33
+ "reference",
34
+ ],
35
+ },
36
+ ],
37
+ "import-notation": "string",
38
+ "scale-unlimited/declaration-strict-value": [
39
+ ["/color$/", "fill", "stroke"],
40
+ {
41
+ ignoreValues: [
42
+ "inherit",
43
+ "initial",
44
+ "unset",
45
+ "revert",
46
+ "transparent",
47
+ "currentColor",
48
+ "none",
49
+ ],
50
+ ignoreFunctions: false,
51
+ disableFix: true,
52
+ },
53
+ ],
54
+ },
55
+ };
@@ -0,0 +1,39 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import { Input } from "@/components/ui/input";
5
+ import { Button } from "@/components/ui/button";
6
+
7
+ describe("form controls", () => {
8
+ it("accepts keyboard input and submits its value", async () => {
9
+ const user = userEvent.setup();
10
+ const save = vi.fn();
11
+ render(
12
+ <form
13
+ onSubmit={(event) => {
14
+ event.preventDefault();
15
+ save(new FormData(event.currentTarget).get("name"));
16
+ }}
17
+ >
18
+ <label htmlFor="name">Name</label>
19
+ <Input id="name" name="name" required />
20
+ <Button type="submit">Save</Button>
21
+ </form>,
22
+ );
23
+ await user.type(screen.getByRole("textbox", { name: "Name" }), "Ada");
24
+ await user.click(screen.getByRole("button", { name: "Save" }));
25
+ expect(save).toHaveBeenCalledWith("Ada");
26
+ });
27
+
28
+ it("does not submit a disabled action", async () => {
29
+ const user = userEvent.setup();
30
+ const save = vi.fn();
31
+ render(
32
+ <Button disabled onClick={save}>
33
+ Save
34
+ </Button>,
35
+ );
36
+ await user.click(screen.getByRole("button", { name: "Save" }));
37
+ expect(save).not.toHaveBeenCalled();
38
+ });
39
+ });
@@ -0,0 +1,49 @@
1
+ import { test } from "vitest";
2
+ import assert from "node:assert/strict";
3
+ import {
4
+ formDataBodySerializer,
5
+ urlSearchParamsBodySerializer,
6
+ jsonBodySerializer,
7
+ } from "../src/sdk/core/bodySerializer.gen";
8
+ import { serializeQueryKeyValue } from "../src/sdk/core/queryKeySerializer.gen";
9
+
10
+ test("API serialization preserves values and omits absent fields", () => {
11
+ const body = {
12
+ title: "hello",
13
+ count: 3,
14
+ tags: ["a", "b"],
15
+ absent: null,
16
+ missing: undefined,
17
+ date: new Date("2026-10-20T00:00:00Z"),
18
+ nested: { enabled: true },
19
+ };
20
+ const form = formDataBodySerializer.bodySerializer(body);
21
+ assert.deepEqual(form.getAll("tags"), ["a", "b"]);
22
+ assert.equal(form.get("date"), body.date.toISOString());
23
+ assert.equal(form.get("nested"), '{"enabled":true}');
24
+ assert.equal(form.has("absent"), false);
25
+ assert.equal(form.has("missing"), false);
26
+ const params = new URLSearchParams(
27
+ urlSearchParamsBodySerializer.bodySerializer(body),
28
+ );
29
+ assert.deepEqual(params.getAll("tags"), ["a", "b"]);
30
+ assert.equal(params.get("count"), "3");
31
+ assert.equal(jsonBodySerializer.bodySerializer({ id: 23n }), '{"id":"23"}');
32
+ });
33
+
34
+ test("query cache keys preserve dates and have stable parameter ordering", () => {
35
+ assert.equal(serializeQueryKeyValue(23n), "23");
36
+ assert.equal(
37
+ serializeQueryKeyValue(new Date("2026-10-20T00:00:00Z")),
38
+ "2026-10-20T00:00:00.000Z",
39
+ );
40
+ assert.equal(serializeQueryKeyValue(Symbol("omitted")), undefined);
41
+ assert.equal(
42
+ serializeQueryKeyValue(() => 1),
43
+ undefined,
44
+ );
45
+ assert.deepEqual(serializeQueryKeyValue(new URLSearchParams("z=1&a=2&a=3")), {
46
+ a: ["2", "3"],
47
+ z: "1",
48
+ });
49
+ });
@@ -0,0 +1,5 @@
1
+ import "@testing-library/jest-dom/vitest";
2
+ import { afterEach } from "vitest";
3
+ import { cleanup } from "@testing-library/react";
4
+
5
+ afterEach(cleanup);
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "baseUrl": ".",
4
3
  "paths": {
5
4
  "@/*": ["./src/*"]
6
5
  }
@@ -20,7 +20,8 @@
20
20
  "noUnusedParameters": true,
21
21
  "noFallthroughCasesInSwitch": true,
22
22
  "noUncheckedSideEffectImports": true,
23
- "baseUrl": ".",
23
+ "allowJs": true,
24
+ "noUncheckedIndexedAccess": true,
24
25
  "paths": {
25
26
  "@/*": ["./src/*"]
26
27
  }
@@ -1,5 +1,6 @@
1
1
  import { defineConfig, loadEnv } from "vite";
2
- import viteReact from "@vitejs/plugin-react";
2
+ import viteReact, { reactCompilerPreset } from "@vitejs/plugin-react";
3
+ import babel from "@rolldown/plugin-babel";
3
4
  import { tanstackRouter } from "@tanstack/router-plugin/vite";
4
5
  import tailwindcss from "@tailwindcss/vite";
5
6
  import { resolve } from "node:path";
@@ -19,16 +20,13 @@ export default defineConfig(({ mode }) => {
19
20
  enabled: true,
20
21
  },
21
22
  }),
22
- viteReact({
23
- babel: {
24
- plugins: [["babel-plugin-react-compiler"]],
25
- },
26
- }),
23
+ viteReact(),
24
+ babel({ presets: [reactCompilerPreset()] }),
27
25
  tailwindcss(),
28
26
  ],
29
27
  resolve: {
30
28
  alias: {
31
- "@": resolve(__dirname, "./src"),
29
+ "@": resolve(import.meta.dirname, "./src"),
32
30
  },
33
31
  },
34
32
  server: {
@@ -0,0 +1,14 @@
1
+ import { defineConfig } from "vitest/config";
2
+ import react from "@vitejs/plugin-react";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ resolve: { alias: { "@": fileURLToPath(new URL("./src", import.meta.url)) } },
8
+ test: {
9
+ environment: "jsdom",
10
+ setupFiles: ["./tests/setup.ts"],
11
+ include: ["src/**/*.test.{ts,tsx}", "tests/**/*.test.{ts,tsx}"],
12
+ restoreMocks: true,
13
+ },
14
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "generator-kodly-react-app",
3
- "version": "1.0.16",
3
+ "version": "1.0.17",
4
4
  "description": "A Yeoman generator for creating React.js applications with Vite, TanStack Router, authentication, and i18n",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -18,7 +18,7 @@
18
18
  "generator"
19
19
  ],
20
20
  "scripts": {
21
- "test": "echo \"Error: no test specified\" && exit 1",
21
+ "test": "node --test test/*.test.mjs",
22
22
  "prepublishOnly": "rm -rf generators/app/templates/node_modules generators/app/templates/dist generators/app/templates/.tanstack generators/app/templates/package-lock.json generators/app/templates/*.tsbuildinfo"
23
23
  },
24
24
  "author": "The Panther <info@thepanther.io>",
@@ -32,13 +32,14 @@
32
32
  },
33
33
  "homepage": "https://github.com/thepanther-io/kodly-react-yo-generator#readme",
34
34
  "engines": {
35
- "node": ">=14.0.0"
35
+ "node": ">=26.12.1"
36
36
  },
37
37
  "dependencies": {
38
- "yeoman-generator": "^7.5.1",
39
- "lodash": "^4.17.21",
40
- "mem-fs": "^4.1.2",
41
- "mem-fs-editor": "^11.1.4",
42
- "chalk": "^5.4.1"
43
- }
38
+ "yeoman-generator": "10.2.1",
39
+ "lodash": "4.18.1",
40
+ "mem-fs": "6.0.0",
41
+ "mem-fs-editor": "12.1.0",
42
+ "chalk": "6.0.1"
43
+ },
44
+ "packageManager": "pnpm@12.14.0"
44
45
  }
@@ -1,5 +0,0 @@
1
- export default {
2
- plugins: {
3
- "@tailwindcss/postcss": {},
4
- },
5
- };