generator-kodly-react-app 1.0.15 → 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.
- package/generators/app/index.js +15 -1
- package/generators/app/templates/.env.sandbox +4 -3
- package/generators/app/templates/.node-version +1 -0
- package/generators/app/templates/.pnpmfile.cjs +14 -0
- package/generators/app/templates/.prettierignore +13 -0
- package/generators/app/templates/AGENTS.md +14 -0
- package/generators/app/templates/I18N_MIGRATION.md +13 -13
- package/generators/app/templates/STRUCTURE.md +26 -26
- package/generators/app/templates/TESTING.md +30 -0
- package/generators/app/templates/index.html +6 -14
- package/generators/app/templates/openapi-ts.config.ts +16 -12
- package/generators/app/templates/oxlint.config.ts +72 -0
- package/generators/app/templates/package.json +66 -52
- package/generators/app/templates/pnpm-workspace.yaml +19 -0
- package/generators/app/templates/scripts/check-duplicates.mjs +28 -0
- package/generators/app/templates/scripts/generated-files.mjs +21 -0
- package/generators/app/templates/src/app.tsx +20 -20
- package/generators/app/templates/src/components/layout/language-switcher.tsx +20 -16
- package/generators/app/templates/src/components/layout/page-layout.tsx +31 -0
- package/generators/app/templates/src/components/layout/theme-switcher.tsx +16 -15
- package/generators/app/templates/src/components/theme/theme-provider.tsx +20 -13
- package/generators/app/templates/src/components/ui/button.tsx +15 -14
- package/generators/app/templates/src/components/ui/card.tsx +23 -16
- package/generators/app/templates/src/components/ui/field.tsx +35 -35
- package/generators/app/templates/src/components/ui/form.tsx +66 -30
- package/generators/app/templates/src/components/ui/input-group.tsx +22 -28
- package/generators/app/templates/src/components/ui/input.tsx +8 -8
- package/generators/app/templates/src/components/ui/label.tsx +9 -9
- package/generators/app/templates/src/components/ui/select.tsx +0 -1
- package/generators/app/templates/src/components/ui/separator.tsx +10 -10
- package/generators/app/templates/src/components/ui/textarea.tsx +7 -7
- package/generators/app/templates/src/index.css +78 -68
- package/generators/app/templates/src/lib/i18n.ts +40 -31
- package/generators/app/templates/src/lib/routes.ts +2 -3
- package/generators/app/templates/src/lib/utils/error-handler.ts +21 -14
- package/generators/app/templates/src/lib/utils.ts +3 -3
- package/generators/app/templates/src/locales/index.ts +12 -12
- package/generators/app/templates/src/locales/ja/theme.json +0 -1
- package/generators/app/templates/src/main.tsx +10 -12
- package/generators/app/templates/src/modules/app/layouts/app-layout.tsx +14 -25
- package/generators/app/templates/src/modules/app/locales/app-en.json +0 -1
- package/generators/app/templates/src/modules/app/locales/app-ja.json +0 -1
- package/generators/app/templates/src/modules/auth/components/auth-form.tsx +86 -0
- package/generators/app/templates/src/modules/auth/components/forgot-password-form.tsx +27 -52
- package/generators/app/templates/src/modules/auth/components/login-form.tsx +42 -72
- package/generators/app/templates/src/modules/auth/components/login-link.tsx +12 -0
- package/generators/app/templates/src/modules/auth/components/reset-password-form.tsx +46 -92
- package/generators/app/templates/src/modules/auth/components/signup-form.tsx +35 -71
- package/generators/app/templates/src/modules/auth/contexts/auth-context.tsx +10 -6
- package/generators/app/templates/src/modules/auth/hooks/use-auth-hook.ts +54 -39
- package/generators/app/templates/src/modules/auth/layouts/auth-layout.tsx +12 -15
- package/generators/app/templates/src/modules/auth/locales/auth-en.json +0 -1
- package/generators/app/templates/src/modules/auth/locales/auth-ja.json +0 -1
- package/generators/app/templates/src/modules/auth/schemas/form-schemas.ts +14 -10
- package/generators/app/templates/src/modules/landing/components/auth-hero.tsx +20 -18
- package/generators/app/templates/src/modules/landing/components/welcome-hero.tsx +13 -11
- package/generators/app/templates/src/modules/landing/landing-page-layout.tsx +1 -24
- package/generators/app/templates/src/modules/landing/landing-page.tsx +11 -6
- package/generators/app/templates/src/modules/landing/layouts/landing-page-layout.tsx +2 -21
- package/generators/app/templates/src/modules/landing/locales/landing-en.json +0 -1
- package/generators/app/templates/src/openapi-client-config.ts +1 -1
- package/generators/app/templates/src/routeTree.gen.ts +188 -188
- package/generators/app/templates/src/router.tsx +4 -4
- package/generators/app/templates/src/routes/$.tsx +0 -1
- package/generators/app/templates/src/routes/__root.tsx +3 -3
- package/generators/app/templates/src/routes/_landing/index.tsx +3 -3
- package/generators/app/templates/src/routes/_landing/route.tsx +3 -3
- package/generators/app/templates/src/routes/app/index.tsx +6 -6
- package/generators/app/templates/src/routes/app/route.tsx +4 -4
- package/generators/app/templates/src/routes/auth/forgot-password.tsx +1 -1
- package/generators/app/templates/src/routes/auth/login.tsx +3 -3
- package/generators/app/templates/src/routes/auth/reset-password.tsx +3 -3
- package/generators/app/templates/src/routes/auth/route.tsx +16 -8
- package/generators/app/templates/src/routes/auth/signup.tsx +1 -2
- package/generators/app/templates/src/sdk/@tanstack/react-query.gen.ts +157 -76
- package/generators/app/templates/src/sdk/client/client.gen.ts +35 -37
- package/generators/app/templates/src/sdk/client/index.ts +8 -8
- package/generators/app/templates/src/sdk/client/types.gen.ts +22 -19
- package/generators/app/templates/src/sdk/client/utils.gen.ts +53 -45
- package/generators/app/templates/src/sdk/client.gen.ts +14 -5
- package/generators/app/templates/src/sdk/core/auth.gen.ts +6 -6
- package/generators/app/templates/src/sdk/core/bodySerializer.gen.ts +25 -27
- package/generators/app/templates/src/sdk/core/params.gen.ts +12 -12
- package/generators/app/templates/src/sdk/core/pathSerializer.gen.ts +40 -41
- package/generators/app/templates/src/sdk/core/queryKeySerializer.gen.ts +12 -29
- package/generators/app/templates/src/sdk/core/serverSentEvents.gen.ts +26 -24
- package/generators/app/templates/src/sdk/core/types.gen.ts +17 -15
- package/generators/app/templates/src/sdk/core/utils.gen.ts +18 -18
- package/generators/app/templates/src/sdk/index.ts +40 -2
- package/generators/app/templates/src/sdk/schemas.gen.ts +108 -166
- package/generators/app/templates/src/sdk/sdk.gen.ts +145 -63
- package/generators/app/templates/src/sdk/types.gen.ts +90 -84
- package/generators/app/templates/src/sdk/zod.gen.ts +57 -74
- package/generators/app/templates/src/types/i18n.d.ts +0 -1
- package/generators/app/templates/stylelint.config.js +55 -0
- package/generators/app/templates/tailwind.config.js +0 -1
- package/generators/app/templates/tests/form-controls.test.tsx +39 -0
- package/generators/app/templates/tests/sdk.test.ts +49 -0
- package/generators/app/templates/tests/setup.ts +5 -0
- package/generators/app/templates/tsconfig.app.json +0 -2
- package/generators/app/templates/tsconfig.json +2 -2
- package/generators/app/templates/tsr.config.json +4 -0
- package/generators/app/templates/vite.config.js +14 -16
- package/generators/app/templates/vitest.config.ts +14 -0
- package/package.json +9 -10
- package/generators/app/templates/postcss.config.js +0 -6
package/generators/app/index.js
CHANGED
|
@@ -16,16 +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',
|
|
38
|
+
'tsr.config.json',
|
|
27
39
|
'tailwind.config.js',
|
|
28
|
-
'postcss.config.js',
|
|
29
40
|
'index.html',
|
|
30
41
|
'components.json',
|
|
31
42
|
'openapi-ts.config.ts',
|
|
@@ -70,6 +81,8 @@ export default class ReactAppGenerator extends BaseGenerator {
|
|
|
70
81
|
_writeAuthFiles() {
|
|
71
82
|
const authFiles = [
|
|
72
83
|
'src/modules/auth/components/signup-form.tsx',
|
|
84
|
+
'src/modules/auth/components/auth-form.tsx',
|
|
85
|
+
'src/modules/auth/components/login-link.tsx',
|
|
73
86
|
'src/modules/auth/components/forgot-password-form.tsx',
|
|
74
87
|
'src/modules/auth/components/reset-password-form.tsx',
|
|
75
88
|
'src/modules/auth/components/login-form.tsx',
|
|
@@ -86,6 +99,7 @@ export default class ReactAppGenerator extends BaseGenerator {
|
|
|
86
99
|
_writeComponentFiles() {
|
|
87
100
|
const componentFiles = [
|
|
88
101
|
'src/components/layout/language-switcher.tsx',
|
|
102
|
+
'src/components/layout/page-layout.tsx',
|
|
89
103
|
'src/components/layout/theme-switcher.tsx',
|
|
90
104
|
'src/components/theme/theme-provider.tsx',
|
|
91
105
|
'src/components/ui/button.tsx',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
BASE_URL=/
|
|
2
|
-
PORT=
|
|
3
|
-
ALLOWED_HOSTS=
|
|
4
|
-
|
|
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,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.
|
|
@@ -6,10 +6,10 @@ This document describes the i18n configuration changes that enable **both** flat
|
|
|
6
6
|
|
|
7
7
|
The i18n setup now supports two usage patterns interchangeably:
|
|
8
8
|
|
|
9
|
-
| Pattern
|
|
10
|
-
|
|
11
|
-
| **Flat (no namespace)** | `useTranslation()` + `t('namespace.section.key')`
|
|
12
|
-
| **Namespaced**
|
|
9
|
+
| Pattern | Example | Use case |
|
|
10
|
+
| ----------------------- | -------------------------------------------------- | -------------------------------- |
|
|
11
|
+
| **Flat (no namespace)** | `useTranslation()` + `t('namespace.section.key')` | Quick access, explicit full path |
|
|
12
|
+
| **Namespaced** | `useTranslation('namespace')` + `t('section.key')` | Modular components, shorter keys |
|
|
13
13
|
|
|
14
14
|
Previously, only the flat pattern worked because all translations lived under a single `translation` namespace. The namespaced pattern failed because i18next couldn't find the individual namespaces (the top-level keys in your merged translation object).
|
|
15
15
|
|
|
@@ -24,8 +24,8 @@ if (!i18n.isInitialized) {
|
|
|
24
24
|
const lng = getStoredLanguage();
|
|
25
25
|
i18n.use(initReactI18next).init({
|
|
26
26
|
resources: {
|
|
27
|
-
en: { translation: mergeTranslations(
|
|
28
|
-
ja: { translation: mergeTranslations(
|
|
27
|
+
en: { translation: mergeTranslations("en") },
|
|
28
|
+
ja: { translation: mergeTranslations("ja") },
|
|
29
29
|
},
|
|
30
30
|
supportedLngs: supportedLanguages,
|
|
31
31
|
lng,
|
|
@@ -43,8 +43,8 @@ if (!i18n.isInitialized) {
|
|
|
43
43
|
```typescript
|
|
44
44
|
if (!i18n.isInitialized) {
|
|
45
45
|
const lng = getStoredLanguage();
|
|
46
|
-
const enResources = mergeTranslations(
|
|
47
|
-
const jaResources = mergeTranslations(
|
|
46
|
+
const enResources = mergeTranslations("en");
|
|
47
|
+
const jaResources = mergeTranslations("ja");
|
|
48
48
|
i18n.use(initReactI18next).init({
|
|
49
49
|
resources: {
|
|
50
50
|
en: {
|
|
@@ -56,8 +56,8 @@ if (!i18n.isInitialized) {
|
|
|
56
56
|
...jaResources,
|
|
57
57
|
},
|
|
58
58
|
},
|
|
59
|
-
defaultNS:
|
|
60
|
-
ns: [
|
|
59
|
+
defaultNS: "translation",
|
|
60
|
+
ns: ["translation", ...Object.keys(enResources)],
|
|
61
61
|
supportedLngs: supportedLanguages,
|
|
62
62
|
lng,
|
|
63
63
|
fallbackLng: defaultLanguage,
|
|
@@ -103,11 +103,11 @@ You can migrate components to the namespaced pattern for shorter keys:
|
|
|
103
103
|
```tsx
|
|
104
104
|
// Before (flat)
|
|
105
105
|
const { t } = useTranslation();
|
|
106
|
-
return <h1>{t(
|
|
106
|
+
return <h1>{t("namespace.section.title")}</h1>;
|
|
107
107
|
|
|
108
108
|
// After (namespaced) – optional
|
|
109
|
-
const { t } = useTranslation(
|
|
110
|
-
return <h1>{t(
|
|
109
|
+
const { t } = useTranslation("namespace");
|
|
110
|
+
return <h1>{t("section.title")}</h1>;
|
|
111
111
|
```
|
|
112
112
|
|
|
113
113
|
Both forms work. Choose what fits your component structure.
|
|
@@ -279,10 +279,10 @@ routes/
|
|
|
279
279
|
|
|
280
280
|
```tsx
|
|
281
281
|
// src/routes/{module}/route.tsx (Parent route)
|
|
282
|
-
import { createFileRoute, Outlet } from
|
|
283
|
-
import { ModuleLayout } from
|
|
282
|
+
import { createFileRoute, Outlet } from "@tanstack/react-router";
|
|
283
|
+
import { ModuleLayout } from "@/modules/{module}/layouts/{module}-layout";
|
|
284
284
|
|
|
285
|
-
export const Route = createFileRoute(
|
|
285
|
+
export const Route = createFileRoute("/{module}")({
|
|
286
286
|
beforeLoad: ({ context }) => {
|
|
287
287
|
// Guards, redirects, etc.
|
|
288
288
|
},
|
|
@@ -300,10 +300,10 @@ function RouteComponent() {
|
|
|
300
300
|
|
|
301
301
|
```tsx
|
|
302
302
|
// src/routes/{module}/{page}.tsx (Child route)
|
|
303
|
-
import { createFileRoute } from
|
|
304
|
-
import { PageComponent } from
|
|
303
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
304
|
+
import { PageComponent } from "@/modules/{module}/components/{page}-component";
|
|
305
305
|
|
|
306
|
-
export const Route = createFileRoute(
|
|
306
|
+
export const Route = createFileRoute("/{module}/{page}")({
|
|
307
307
|
component: RouteComponent,
|
|
308
308
|
});
|
|
309
309
|
|
|
@@ -363,7 +363,7 @@ Translations are merged in `src/locales/index.ts`:
|
|
|
363
363
|
|
|
364
364
|
```typescript
|
|
365
365
|
export const mergeTranslations = (lang: SupportedLanguages) => {
|
|
366
|
-
if (lang ===
|
|
366
|
+
if (lang === "en") {
|
|
367
367
|
return {
|
|
368
368
|
auth: { ...enAuth },
|
|
369
369
|
app: { ...enApp },
|
|
@@ -379,15 +379,15 @@ export const mergeTranslations = (lang: SupportedLanguages) => {
|
|
|
379
379
|
### Using Translations in Components
|
|
380
380
|
|
|
381
381
|
```tsx
|
|
382
|
-
import { useTranslation } from
|
|
382
|
+
import { useTranslation } from "react-i18next";
|
|
383
383
|
|
|
384
384
|
function MyComponent() {
|
|
385
385
|
const { t } = useTranslation();
|
|
386
386
|
|
|
387
387
|
return (
|
|
388
388
|
<div>
|
|
389
|
-
<h1>{t(
|
|
390
|
-
<p>{t(
|
|
389
|
+
<h1>{t("auth.login.title")}</h1>
|
|
390
|
+
<p>{t("common.welcome")}</p>
|
|
391
391
|
</div>
|
|
392
392
|
);
|
|
393
393
|
}
|
|
@@ -447,11 +447,11 @@ src/modules/dashboard/
|
|
|
447
447
|
Add module translations to `src/locales/index.ts`:
|
|
448
448
|
|
|
449
449
|
```typescript
|
|
450
|
-
import enDashboard from
|
|
451
|
-
import jaDashboard from
|
|
450
|
+
import enDashboard from "@/modules/dashboard/locales/dashboard-en.json";
|
|
451
|
+
import jaDashboard from "@/modules/dashboard/locales/dashboard-ja.json";
|
|
452
452
|
|
|
453
453
|
export const mergeTranslations = (lang: SupportedLanguages) => {
|
|
454
|
-
if (lang ===
|
|
454
|
+
if (lang === "en") {
|
|
455
455
|
return {
|
|
456
456
|
// ... existing modules
|
|
457
457
|
dashboard: { ...enDashboard },
|
|
@@ -472,7 +472,7 @@ export const mergeTranslations = (lang: SupportedLanguages) => {
|
|
|
472
472
|
```tsx
|
|
473
473
|
export function DashboardLayout({ children }: { children: React.ReactNode }) {
|
|
474
474
|
return (
|
|
475
|
-
<div className=
|
|
475
|
+
<div className="dashboard-container">
|
|
476
476
|
{/* Dashboard-specific layout */}
|
|
477
477
|
{children}
|
|
478
478
|
</div>
|
|
@@ -485,10 +485,10 @@ export function DashboardLayout({ children }: { children: React.ReactNode }) {
|
|
|
485
485
|
**`src/routes/dashboard/route.tsx`**:
|
|
486
486
|
|
|
487
487
|
```tsx
|
|
488
|
-
import { createFileRoute, Outlet } from
|
|
489
|
-
import { DashboardLayout } from
|
|
488
|
+
import { createFileRoute, Outlet } from "@tanstack/react-router";
|
|
489
|
+
import { DashboardLayout } from "@/modules/dashboard/layouts/dashboard-layout";
|
|
490
490
|
|
|
491
|
-
export const Route = createFileRoute(
|
|
491
|
+
export const Route = createFileRoute("/dashboard")({
|
|
492
492
|
component: RouteComponent,
|
|
493
493
|
});
|
|
494
494
|
|
|
@@ -504,10 +504,10 @@ function RouteComponent() {
|
|
|
504
504
|
**`src/routes/dashboard/index.tsx`**:
|
|
505
505
|
|
|
506
506
|
```tsx
|
|
507
|
-
import { createFileRoute } from
|
|
508
|
-
import { DashboardPage } from
|
|
507
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
508
|
+
import { DashboardPage } from "@/modules/dashboard/components/dashboard-page";
|
|
509
509
|
|
|
510
|
-
export const Route = createFileRoute(
|
|
510
|
+
export const Route = createFileRoute("/dashboard/")({
|
|
511
511
|
component: RouteComponent,
|
|
512
512
|
});
|
|
513
513
|
|
|
@@ -564,7 +564,7 @@ _writeDashboardFiles() {
|
|
|
564
564
|
|
|
565
565
|
```tsx
|
|
566
566
|
// src/modules/{module}/components/{component-name}.tsx
|
|
567
|
-
import { useTranslation } from
|
|
567
|
+
import { useTranslation } from "react-i18next";
|
|
568
568
|
|
|
569
569
|
export function ComponentName() {
|
|
570
570
|
const { t } = useTranslation();
|
|
@@ -577,7 +577,7 @@ export function ComponentName() {
|
|
|
577
577
|
|
|
578
578
|
```tsx
|
|
579
579
|
// src/modules/{module}/hooks/use-{hook-name}.ts
|
|
580
|
-
import { useAtom } from
|
|
580
|
+
import { useAtom } from "jotai";
|
|
581
581
|
|
|
582
582
|
export function useHookName() {
|
|
583
583
|
// Hook logic
|
|
@@ -593,7 +593,7 @@ export function useHookName() {
|
|
|
593
593
|
// src/modules/{module}/layouts/{module}-layout.tsx
|
|
594
594
|
export function ModuleLayout({ children }: { children: React.ReactNode }) {
|
|
595
595
|
return (
|
|
596
|
-
<div className=
|
|
596
|
+
<div className="module-container">
|
|
597
597
|
{/* Layout structure */}
|
|
598
598
|
{children}
|
|
599
599
|
</div>
|
|
@@ -605,10 +605,10 @@ export function ModuleLayout({ children }: { children: React.ReactNode }) {
|
|
|
605
605
|
|
|
606
606
|
```tsx
|
|
607
607
|
// src/routes/{module}/{page}.tsx
|
|
608
|
-
import { createFileRoute } from
|
|
609
|
-
import { ComponentName } from
|
|
608
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
609
|
+
import { ComponentName } from "@/modules/{module}/components/component-name";
|
|
610
610
|
|
|
611
|
-
export const Route = createFileRoute(
|
|
611
|
+
export const Route = createFileRoute("/{module}/{page}")({
|
|
612
612
|
component: RouteComponent,
|
|
613
613
|
});
|
|
614
614
|
|
|
@@ -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.
|
|
@@ -4,23 +4,15 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta
|
|
6
6
|
name="viewport"
|
|
7
|
-
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
name="description"
|
|
13
|
-
content="<%= appNameTitleCase %>" />
|
|
14
|
-
<link
|
|
15
|
-
rel="icon"
|
|
16
|
-
type="image/svg+xml"
|
|
17
|
-
href="/favicon.svg" />
|
|
7
|
+
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
|
8
|
+
/>
|
|
9
|
+
<meta name="theme-color" content="#000000" />
|
|
10
|
+
<meta name="description" content="<%= appNameTitleCase %>" />
|
|
11
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
18
12
|
<title><%= appNameTitleCase %></title>
|
|
19
13
|
</head>
|
|
20
14
|
<body>
|
|
21
15
|
<div id="app"></div>
|
|
22
|
-
<script
|
|
23
|
-
type="module"
|
|
24
|
-
src="/src/main.tsx"></script>
|
|
16
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
25
17
|
</body>
|
|
26
18
|
</html>
|
|
@@ -1,29 +1,33 @@
|
|
|
1
|
-
import { defineConfig } from
|
|
2
|
-
import { loadEnv } from
|
|
1
|
+
import { defineConfig } from "@hey-api/openapi-ts";
|
|
2
|
+
import { loadEnv } from "vite";
|
|
3
3
|
|
|
4
4
|
// Get the mode from environment variable (set by pnpm scripts)
|
|
5
|
-
const mode = process.env.MODE ||
|
|
5
|
+
const mode = process.env.MODE || "development";
|
|
6
6
|
|
|
7
7
|
// Load environment variables from .env files
|
|
8
|
-
const env = loadEnv(mode, process.cwd(),
|
|
8
|
+
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:
|
|
12
|
+
output: {
|
|
13
|
+
path: "src/sdk",
|
|
14
|
+
postProcess: ["prettier"],
|
|
15
|
+
},
|
|
13
16
|
plugins: [
|
|
14
17
|
{
|
|
15
|
-
name:
|
|
16
|
-
|
|
18
|
+
name: "@hey-api/client-axios",
|
|
19
|
+
baseUrl: false,
|
|
20
|
+
runtimeConfigPath: "@/openapi-client-config.ts",
|
|
17
21
|
},
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
"@tanstack/react-query",
|
|
23
|
+
"zod",
|
|
20
24
|
{
|
|
21
|
-
name:
|
|
25
|
+
name: "@hey-api/sdk",
|
|
22
26
|
validator: true,
|
|
23
27
|
},
|
|
24
28
|
{
|
|
25
|
-
name:
|
|
26
|
-
type:
|
|
29
|
+
name: "@hey-api/schemas",
|
|
30
|
+
type: "json",
|
|
27
31
|
},
|
|
28
32
|
],
|
|
29
33
|
});
|
|
@@ -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@
|
|
6
|
+
"packageManager": "pnpm@12.14.0",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
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
|
-
"@
|
|
23
|
-
"@
|
|
24
|
-
"@radix-ui/react-
|
|
25
|
-
"@radix-ui/react-
|
|
26
|
-
"@radix-ui/react-
|
|
27
|
-
"@
|
|
28
|
-
"@tanstack/react-
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"react": "
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
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/
|
|
50
|
-
"@
|
|
51
|
-
"@
|
|
52
|
-
"@types/
|
|
53
|
-
"@
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
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
|
-
"
|
|
63
|
-
"
|
|
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
|