@react-spa-scaffold/mcp 0.3.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +42 -20
  2. package/dist/features/registry.d.ts.map +1 -1
  3. package/dist/features/registry.js +59 -25
  4. package/dist/features/registry.js.map +1 -1
  5. package/dist/features/types.d.ts +1 -0
  6. package/dist/features/types.d.ts.map +1 -1
  7. package/dist/features/versions.json +1 -1
  8. package/dist/server.d.ts.map +1 -1
  9. package/dist/server.js +2 -1
  10. package/dist/server.js.map +1 -1
  11. package/dist/tools/get-example.d.ts +4 -6
  12. package/dist/tools/get-example.d.ts.map +1 -1
  13. package/dist/tools/get-example.js +2 -2
  14. package/dist/tools/get-example.js.map +1 -1
  15. package/dist/tools/get-scaffold.d.ts +3 -10
  16. package/dist/tools/get-scaffold.d.ts.map +1 -1
  17. package/dist/tools/get-scaffold.js +44 -20
  18. package/dist/tools/get-scaffold.js.map +1 -1
  19. package/dist/utils/examples.d.ts.map +1 -1
  20. package/dist/utils/examples.js +19 -16
  21. package/dist/utils/examples.js.map +1 -1
  22. package/dist/utils/paths.d.ts +2 -1
  23. package/dist/utils/paths.d.ts.map +1 -1
  24. package/dist/utils/paths.js +15 -2
  25. package/dist/utils/paths.js.map +1 -1
  26. package/dist/utils/scaffold.d.ts +6 -1
  27. package/dist/utils/scaffold.d.ts.map +1 -1
  28. package/dist/utils/scaffold.js +86 -13
  29. package/dist/utils/scaffold.js.map +1 -1
  30. package/package.json +2 -2
  31. package/templates/.env.example +21 -0
  32. package/templates/.github/PULL_REQUEST_TEMPLATE.md +24 -0
  33. package/templates/.github/actions/setup-node-deps/action.yml +32 -0
  34. package/templates/.github/dependabot.yml +104 -0
  35. package/templates/.github/workflows/ci.yml +156 -0
  36. package/templates/.husky/commit-msg +1 -0
  37. package/templates/.husky/pre-commit +2 -0
  38. package/templates/.nvmrc +1 -0
  39. package/templates/CLAUDE.md +4 -2
  40. package/templates/commitlint.config.js +1 -0
  41. package/templates/components.json +21 -0
  42. package/templates/docs/API_REFERENCE.md +0 -1
  43. package/templates/docs/INTERNATIONALIZATION.md +26 -0
  44. package/templates/e2e/fixtures/index.ts +10 -0
  45. package/templates/e2e/tests/home.spec.ts +42 -0
  46. package/templates/e2e/tests/language.spec.ts +42 -0
  47. package/templates/e2e/tests/navigation.spec.ts +18 -0
  48. package/templates/e2e/tests/theme.spec.ts +35 -0
  49. package/templates/eslint.config.js +42 -0
  50. package/templates/gitignore +33 -0
  51. package/templates/index.html +13 -0
  52. package/templates/lighthouse-budget.json +17 -0
  53. package/templates/lighthouserc.json +23 -0
  54. package/templates/lingui.config.js +18 -0
  55. package/templates/package.json +125 -0
  56. package/templates/playwright.config.ts +30 -0
  57. package/templates/prettier.config.js +1 -0
  58. package/templates/public/favicon.svg +4 -0
  59. package/templates/src/components/shared/RegisterForm/RegisterForm.tsx +91 -0
  60. package/templates/src/components/shared/RegisterForm/index.ts +1 -0
  61. package/templates/src/components/shared/index.ts +1 -0
  62. package/templates/src/components/ui/card.tsx +70 -0
  63. package/templates/src/components/ui/input.tsx +19 -0
  64. package/templates/src/components/ui/label.tsx +19 -0
  65. package/templates/src/hooks/index.ts +1 -1
  66. package/templates/src/hooks/useRegisterForm.ts +36 -0
  67. package/templates/src/lib/index.ts +1 -11
  68. package/templates/src/lib/validations.ts +6 -13
  69. package/templates/src/pages/Home.tsx +29 -10
  70. package/templates/tests/unit/components/RegisterForm.test.tsx +105 -0
  71. package/templates/tests/unit/hooks/useRegisterForm.test.tsx +153 -0
  72. package/templates/tests/unit/lib/validations.test.ts +22 -33
  73. package/templates/tests/unit/stores/preferencesStore.test.ts +81 -0
  74. package/templates/tsconfig.app.json +10 -0
  75. package/templates/tsconfig.json +11 -0
  76. package/templates/tsconfig.node.json +4 -0
  77. package/templates/vite.config.ts +54 -0
  78. package/templates/vitest.config.ts +38 -0
  79. package/templates/src/hooks/useContactForm.ts +0 -33
  80. package/templates/src/lib/constants.ts +0 -8
  81. package/templates/src/lib/format.ts +0 -119
  82. package/templates/tests/unit/hooks/useContactForm.test.ts +0 -60
  83. package/templates/tests/unit/lib/format.test.ts +0 -100
@@ -0,0 +1,156 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+ branches: [main, master]
8
+ release:
9
+ types: [published]
10
+
11
+ concurrency:
12
+ group: ${{ github.workflow }}-${{ github.ref }}
13
+ cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/master' }}
14
+
15
+ jobs:
16
+ lint:
17
+ name: Lint
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 10
20
+ steps:
21
+ - uses: actions/checkout@v6
22
+ - uses: ./.github/actions/setup-node-deps
23
+ - run: npm run lint
24
+ - run: npm run format:check
25
+
26
+ typecheck:
27
+ name: Type Check
28
+ runs-on: ubuntu-latest
29
+ timeout-minutes: 10
30
+ steps:
31
+ - uses: actions/checkout@v6
32
+ - uses: ./.github/actions/setup-node-deps
33
+ - run: npm run typecheck
34
+
35
+ security:
36
+ name: Security Audit
37
+ runs-on: ubuntu-latest
38
+ timeout-minutes: 10
39
+ steps:
40
+ - uses: actions/checkout@v6
41
+ - uses: ./.github/actions/setup-node-deps
42
+ - run: npm audit --audit-level=moderate
43
+
44
+ build:
45
+ name: Build
46
+ needs: [lint, typecheck]
47
+ runs-on: ubuntu-latest
48
+ timeout-minutes: 10
49
+ steps:
50
+ - uses: actions/checkout@v6
51
+ - uses: ./.github/actions/setup-node-deps
52
+ - name: Build
53
+ run: npm run build
54
+ env:
55
+ SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
56
+ SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
57
+ SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
58
+ - uses: actions/upload-artifact@v6
59
+ with:
60
+ name: dist
61
+ path: dist/
62
+ retention-days: 7
63
+
64
+ test:
65
+ name: Unit Tests
66
+ needs: [lint, typecheck]
67
+ runs-on: ubuntu-latest
68
+ timeout-minutes: 15
69
+ steps:
70
+ - uses: actions/checkout@v6
71
+ - uses: ./.github/actions/setup-node-deps
72
+ - run: npm run test:coverage
73
+ - uses: actions/upload-artifact@v6
74
+ with:
75
+ name: coverage
76
+ path: coverage/
77
+ retention-days: 14
78
+
79
+ e2e:
80
+ name: E2E Tests
81
+ needs: [lint, typecheck]
82
+ runs-on: ubuntu-latest
83
+ timeout-minutes: 15
84
+ steps:
85
+ - uses: actions/checkout@v6
86
+ - uses: ./.github/actions/setup-node-deps
87
+ - name: Cache Playwright browsers
88
+ uses: actions/cache@v5
89
+ id: playwright-cache
90
+ with:
91
+ path: ~/.cache/ms-playwright
92
+ key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
93
+ - name: Install Playwright browsers
94
+ if: steps.playwright-cache.outputs.cache-hit != 'true'
95
+ run: npx playwright install chromium --with-deps
96
+ - name: Install Playwright deps (cached)
97
+ if: steps.playwright-cache.outputs.cache-hit == 'true'
98
+ run: npx playwright install-deps chromium
99
+ - run: npm run e2e
100
+ - uses: actions/upload-artifact@v6
101
+ if: failure()
102
+ with:
103
+ name: playwright-report
104
+ path: playwright-report/
105
+ retention-days: 7
106
+
107
+ lighthouse:
108
+ name: Lighthouse CI
109
+ needs: [build]
110
+ runs-on: ubuntu-latest
111
+ timeout-minutes: 10
112
+ continue-on-error: true
113
+ steps:
114
+ - uses: actions/checkout@v6
115
+ - uses: ./.github/actions/setup-node-deps
116
+ - uses: actions/download-artifact@v7
117
+ with:
118
+ name: dist
119
+ path: dist/
120
+ - name: Run Lighthouse CI
121
+ uses: treosh/lighthouse-ci-action@v12
122
+ with:
123
+ configPath: ./lighthouserc.json
124
+ uploadArtifacts: true
125
+ temporaryPublicStorage: true
126
+ - uses: actions/upload-artifact@v6
127
+ if: always()
128
+ with:
129
+ name: lighthouse-report
130
+ path: .lighthouseci/
131
+ retention-days: 14
132
+
133
+ publish:
134
+ name: Publish to npm
135
+ needs: [build, test]
136
+ if: github.event_name == 'release'
137
+ runs-on: ubuntu-latest
138
+ timeout-minutes: 15
139
+ permissions:
140
+ contents: read
141
+ id-token: write
142
+ steps:
143
+ - uses: actions/checkout@v6
144
+ - uses: ./.github/actions/setup-node-deps
145
+ - name: Configure npm authentication
146
+ run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
147
+ env:
148
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
149
+ - name: Publish @react-spa-scaffold/tsconfig
150
+ run: npm publish -w @react-spa-scaffold/tsconfig --provenance --access public || echo "Package may already exist at this version"
151
+ - name: Publish @react-spa-scaffold/eslint-config
152
+ run: npm publish -w @react-spa-scaffold/eslint-config --provenance --access public || echo "Package may already exist at this version"
153
+ - name: Publish @react-spa-scaffold/prettier-config
154
+ run: npm publish -w @react-spa-scaffold/prettier-config --provenance --access public || echo "Package may already exist at this version"
155
+ - name: Publish @react-spa-scaffold/mcp
156
+ run: npm publish -w @react-spa-scaffold/mcp --provenance --access public || echo "Package may already exist at this version"
@@ -0,0 +1 @@
1
+ npx commitlint --edit $1
@@ -0,0 +1,2 @@
1
+ npm run typecheck
2
+ npx lint-staged
@@ -0,0 +1 @@
1
+ 22
@@ -113,14 +113,16 @@ Need general info? → WebSearch (fallback only)
113
113
 
114
114
  ## Translations (CRITICAL)
115
115
 
116
- All user-facing text MUST have translator comments. ESLint enforces this.
116
+ All user-facing text MUST be wrapped and include translator comments. ESLint enforces this.
117
117
 
118
118
  ```tsx
119
119
  <Trans comment="Dashboard heading">Welcome back</Trans>;
120
120
  t({ message: 'Close', comment: 'Close button' });
121
121
  ```
122
122
 
123
- See [docs/INTERNATIONALIZATION.md](docs/INTERNATIONALIZATION.md).
123
+ Technical identifiers (`id`, `htmlFor`, `autoComplete`, `register()`) are auto-ignored.
124
+
125
+ See [docs/INTERNATIONALIZATION.md](docs/INTERNATIONALIZATION.md) for full details.
124
126
 
125
127
  ## Testing
126
128
 
@@ -0,0 +1 @@
1
+ export default { extends: ['@commitlint/config-conventional'] };
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "radix-nova",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "src/index.css",
9
+ "baseColor": "zinc",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "iconLibrary": "lucide",
14
+ "aliases": {
15
+ "components": "@/components",
16
+ "utils": "@/lib/utils",
17
+ "ui": "@/components/ui",
18
+ "lib": "@/lib",
19
+ "hooks": "@/hooks"
20
+ }
21
+ }
@@ -9,7 +9,6 @@ Quick reference for what's available. For architectural decisions, see [Architec
9
9
  | `api.ts` | HTTP client | Any API calls - handles errors, timeouts, JSON. Includes API_CONFIG |
10
10
  | `config.ts` | App configuration | Access APP_CONFIG, SENTRY_CONFIG |
11
11
  | `env.ts` | Environment variables | Type-safe `env.VITE_*` access |
12
- | `format.ts` | Formatters | Dates, numbers, currency, bytes - all locale-aware |
13
12
  | `routes.ts` | Route constants | Type-safe navigation, avoid magic strings |
14
13
  | `storage.ts` | localStorage wrapper | SSR-safe, typed storage with error handling |
15
14
  | `storageKeys.ts` | Storage key constants | Centralized key management |
@@ -62,6 +62,32 @@ The `eslint-plugin-lingui` enforces translations:
62
62
 
63
63
  Excluded from checks: tests, mocks, UI primitives, config files.
64
64
 
65
+ ### Auto-Ignored Technical Identifiers
66
+
67
+ The ESLint config automatically ignores strings that are technical identifiers:
68
+
69
+ **Prop names** (values don't need translation):
70
+
71
+ - HTML: `id`, `htmlFor`, `autoComplete`, `aria-invalid`
72
+ - Styling: `className`, `styleName`
73
+ - Components: `type`, `variant`, `size`, `role`, `name`
74
+ - Routing: `href`, `to`, `path`
75
+ - Data: `queryKey`, `data-testid`
76
+
77
+ **Function arguments**:
78
+
79
+ - `register('fieldName')` - React Hook Form field names
80
+ - `console.*`, `Error()` - Debug/error messages
81
+
82
+ ```tsx
83
+ // These are fine - no translation needed
84
+ <Label htmlFor="email">
85
+ <Input id="email" autoComplete="email" {...register('email')} />
86
+
87
+ // This DOES need translation (user-facing placeholder)
88
+ placeholder={t({ message: 'Enter email', comment: 'Email input hint' })}
89
+ ```
90
+
65
91
  ## Adding a New Locale
66
92
 
67
93
  Edit `lingui.config.js` and add the locale code to the `locales` array.
@@ -0,0 +1,10 @@
1
+ import type { Page } from '@playwright/test';
2
+
3
+ /**
4
+ * Navigate to page with clean state (clears localStorage)
5
+ */
6
+ export async function setupPage(page: Page, path = '/') {
7
+ await page.goto(path);
8
+ await page.evaluate(() => localStorage.clear());
9
+ await page.reload();
10
+ }
@@ -0,0 +1,42 @@
1
+ import { expect, test } from '@playwright/test';
2
+
3
+ test.describe('Home Page', () => {
4
+ test.beforeEach(async ({ page }) => {
5
+ await page.goto('/');
6
+ });
7
+
8
+ test('displays welcome heading', async ({ page }) => {
9
+ await expect(page.getByRole('heading', { name: /welcome/i })).toBeVisible();
10
+ });
11
+
12
+ test('has correct page structure', async ({ page }) => {
13
+ // Header present
14
+ await expect(page.getByRole('banner')).toBeVisible();
15
+
16
+ // Main content area
17
+ await expect(page.getByRole('main')).toBeVisible();
18
+
19
+ // App title in header
20
+ await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
21
+ });
22
+
23
+ test('skip link navigates to main content', async ({ page }) => {
24
+ const skipLink = page.getByRole('link', { name: /skip to main content/i });
25
+
26
+ // Ensure skip link exists in DOM
27
+ await expect(skipLink).toBeAttached();
28
+
29
+ // Focus the skip link explicitly (more reliable than Tab in E2E)
30
+ await skipLink.focus();
31
+ await expect(skipLink).toBeFocused();
32
+
33
+ // Verify skip link becomes visible when focused
34
+ await expect(skipLink).toBeVisible();
35
+
36
+ // Click skip link to navigate to main content
37
+ await skipLink.click();
38
+
39
+ // Main should be scrolled into view
40
+ await expect(page.locator('#main')).toBeInViewport();
41
+ });
42
+ });
@@ -0,0 +1,42 @@
1
+ import { expect, test } from '@playwright/test';
2
+
3
+ import { setupPage } from '../fixtures';
4
+
5
+ test.describe('Language Switcher', () => {
6
+ test.beforeEach(async ({ page }) => {
7
+ await setupPage(page);
8
+ });
9
+
10
+ test('displays language switcher button', async ({ page }) => {
11
+ await expect(page.getByRole('button', { name: /change language/i })).toBeVisible();
12
+ });
13
+
14
+ test('opens dropdown with language options', async ({ page }) => {
15
+ await page.getByRole('button', { name: /change language/i }).click();
16
+
17
+ await expect(page.getByText('English')).toBeVisible();
18
+ await expect(page.getByText('Español')).toBeVisible();
19
+ await expect(page.getByText('Deutsch')).toBeVisible();
20
+ });
21
+
22
+ test('closes dropdown after selection', async ({ page }) => {
23
+ await page.getByRole('button', { name: /change language/i }).click();
24
+ await page.getByText('Español').click();
25
+
26
+ // Dropdown should close - other options not visible
27
+ await expect(page.getByText('Deutsch')).not.toBeVisible();
28
+ });
29
+
30
+ test('persists language preference across reload', async ({ page }) => {
31
+ // Change to Spanish
32
+ await page.getByRole('button', { name: /change language/i }).click();
33
+ await page.getByText('Español').click();
34
+
35
+ // Wait for language change to apply
36
+ await expect(page.getByRole('heading', { name: /bienvenido/i })).toBeVisible();
37
+
38
+ // Reload and verify Spanish persisted
39
+ await page.reload();
40
+ await expect(page.getByRole('heading', { name: /bienvenido/i })).toBeVisible();
41
+ });
42
+ });
@@ -0,0 +1,18 @@
1
+ import { expect, test } from '@playwright/test';
2
+
3
+ test.describe('Navigation', () => {
4
+ test('shows 404 page for unknown routes', async ({ page }) => {
5
+ await page.goto('/non-existent-page');
6
+ await expect(page.getByRole('heading', { name: '404' })).toBeVisible();
7
+ });
8
+
9
+ test('header is present on all pages', async ({ page }) => {
10
+ // Home page
11
+ await page.goto('/');
12
+ await expect(page.getByRole('banner')).toBeVisible();
13
+
14
+ // 404 page
15
+ await page.goto('/unknown');
16
+ await expect(page.getByRole('banner')).toBeVisible();
17
+ });
18
+ });
@@ -0,0 +1,35 @@
1
+ import { expect, test } from '@playwright/test';
2
+
3
+ import { setupPage } from '../fixtures';
4
+
5
+ test.describe('Theme Toggle', () => {
6
+ test.beforeEach(async ({ page }) => {
7
+ await setupPage(page);
8
+ });
9
+
10
+ test('defaults to light theme', async ({ page }) => {
11
+ await expect(page.locator('html')).not.toHaveClass(/dark/);
12
+ });
13
+
14
+ test('toggles between light and dark theme', async ({ page }) => {
15
+ const html = page.locator('html');
16
+
17
+ // Toggle to dark
18
+ await page.getByRole('button', { name: /switch to dark mode/i }).click();
19
+ await expect(html).toHaveClass(/dark/);
20
+
21
+ // Toggle back to light
22
+ await page.getByRole('button', { name: /switch to light mode/i }).click();
23
+ await expect(html).not.toHaveClass(/dark/);
24
+ });
25
+
26
+ test('persists theme preference across reload', async ({ page }) => {
27
+ // Set dark theme
28
+ await page.getByRole('button', { name: /switch to dark mode/i }).click();
29
+ await expect(page.locator('html')).toHaveClass(/dark/);
30
+
31
+ // Reload and verify
32
+ await page.reload();
33
+ await expect(page.locator('html')).toHaveClass(/dark/);
34
+ });
35
+ });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * ESLint configuration for react-spa-scaffold
3
+ *
4
+ * Uses @react-spa-scaffold/eslint-config with local overrides for monorepo packages.
5
+ */
6
+
7
+ import config from '@react-spa-scaffold/eslint-config';
8
+
9
+ export default [
10
+ // Main app uses React config
11
+ ...config,
12
+
13
+ // Additional ignore for monorepo packages dist
14
+ { ignores: ['packages/**/dist'] },
15
+
16
+ // UI components from shadcn and context/provider files - don't modify
17
+ {
18
+ files: ['**/components/ui/**/*.{ts,tsx}', '**/contexts/**/*.{ts,tsx}', '**/test/**/*.{ts,tsx}'],
19
+ rules: {
20
+ 'react-refresh/only-export-components': 'off',
21
+ },
22
+ },
23
+
24
+ // Packages use Node.js rules (override the React/i18n rules from main config)
25
+ {
26
+ files: ['packages/**/*.ts'],
27
+ rules: {
28
+ // Disable React-specific rules (not a React app)
29
+ 'react-hooks/rules-of-hooks': 'off',
30
+ 'react-hooks/exhaustive-deps': 'off',
31
+ 'react-refresh/only-export-components': 'off',
32
+ // Disable i18n rules (server-side code)
33
+ 'lingui/no-unlocalized-strings': 'off',
34
+ 'lingui/t-call-in-function': 'off',
35
+ 'lingui/no-single-variables-to-translate': 'off',
36
+ 'lingui/no-expression-in-message': 'off',
37
+ 'lingui/no-trans-inside-trans': 'off',
38
+ // Allow console for server logging
39
+ 'no-console': 'off',
40
+ },
41
+ },
42
+ ];
@@ -0,0 +1,33 @@
1
+ # Dependencies
2
+ node_modules/
3
+
4
+ # Build output
5
+ dist/
6
+ dist-ssr/
7
+ *.tsbuildinfo
8
+
9
+ # IDE
10
+ .vscode/
11
+ .idea/
12
+ *.swp
13
+ *.swo
14
+ .DS_Stores
15
+
16
+ # Environment
17
+ .env
18
+ .env.local
19
+ .env.*.local
20
+
21
+ # Testing
22
+ coverage/
23
+ playwright-report/
24
+ test-results/
25
+ playwright/.cache/
26
+
27
+ # i18n compiled catalogs (generated by Vite plugin during build)
28
+ src/locales/*.mjs
29
+
30
+ # Misc
31
+ tmp
32
+ *.log
33
+ npm-debug.log*
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>My App</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,17 @@
1
+ [
2
+ {
3
+ "path": "/*",
4
+ "resourceSizes": [
5
+ { "resourceType": "script", "budget": 500 },
6
+ { "resourceType": "stylesheet", "budget": 100 },
7
+ { "resourceType": "total", "budget": 1000 }
8
+ ],
9
+ "resourceCounts": [{ "resourceType": "third-party", "budget": 10 }],
10
+ "timings": [
11
+ { "metric": "first-contentful-paint", "budget": 2000 },
12
+ { "metric": "interactive", "budget": 5000 },
13
+ { "metric": "largest-contentful-paint", "budget": 3000 },
14
+ { "metric": "cumulative-layout-shift", "budget": 0.1 }
15
+ ]
16
+ }
17
+ ]
@@ -0,0 +1,23 @@
1
+ {
2
+ "ci": {
3
+ "collect": {
4
+ "staticDistDir": "./dist",
5
+ "numberOfRuns": 3,
6
+ "settings": {
7
+ "preset": "desktop",
8
+ "budgetPath": "./lighthouse-budget.json"
9
+ }
10
+ },
11
+ "assert": {
12
+ "assertions": {
13
+ "categories:performance": ["warn", { "minScore": 0.9 }],
14
+ "categories:accessibility": ["warn", { "minScore": 0.9 }],
15
+ "categories:best-practices": ["warn", { "minScore": 0.9 }],
16
+ "categories:seo": ["warn", { "minScore": 0.9 }]
17
+ }
18
+ },
19
+ "upload": {
20
+ "target": "temporary-public-storage"
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,18 @@
1
+ import { defineConfig } from '@lingui/cli';
2
+
3
+ export default defineConfig({
4
+ locales: ['en', 'es', 'de'],
5
+ sourceLocale: 'en',
6
+ fallbackLocales: {
7
+ default: 'en',
8
+ },
9
+ catalogs: [
10
+ {
11
+ path: '<rootDir>/src/locales/{locale}',
12
+ include: ['src'],
13
+ exclude: ['**/node_modules/**', '**/*.test.{ts,tsx}'],
14
+ },
15
+ ],
16
+ format: 'po',
17
+ compileNamespace: 'es',
18
+ });