@donotdev/cli 0.0.7 → 0.0.9

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/README.md +3 -18
  2. package/dependencies-matrix.json +54 -45
  3. package/dist/bin/commands/build.js +19 -5
  4. package/dist/bin/commands/bump.js +30 -5
  5. package/dist/bin/commands/cacheout.js +36 -19
  6. package/dist/bin/commands/create-app.js +73 -7
  7. package/dist/bin/commands/create-project.js +90 -8
  8. package/dist/bin/commands/deploy.js +130 -27
  9. package/dist/bin/commands/dev.js +23 -7
  10. package/dist/bin/commands/emu.js +28 -12
  11. package/dist/bin/commands/format.js +39 -22
  12. package/dist/bin/commands/lint.js +36 -19
  13. package/dist/bin/commands/preview.js +24 -8
  14. package/dist/bin/commands/sync-secrets.js +19 -5
  15. package/dist/bin/dndev.js +7 -20
  16. package/dist/bin/donotdev.js +7 -20
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +209 -100
  20. package/dist/index.js.map +1 -1
  21. package/package.json +4 -3
  22. package/templates/app-next/src/config/app.ts.example +1 -1
  23. package/templates/app-vite/index.html.example +24 -2
  24. package/templates/app-vite/src/config/app.ts.example +1 -1
  25. package/templates/app-vite/src/pages/FormPageExample.tsx.example +8 -5
  26. package/templates/app-vite/src/pages/ListPageExample.tsx.example +4 -7
  27. package/templates/root-consumer/.firebaserc.example +5 -0
  28. package/templates/root-consumer/entities/ExampleEntity.ts.example +2 -1
  29. package/templates/root-consumer/entities/demo.ts.example +15 -1
  30. package/templates/root-consumer/eslint.config.js.example +2 -80
  31. package/templates/root-consumer/firestore.indexes.json.example +4 -0
  32. package/templates/root-consumer/firestore.rules.example +11 -0
  33. package/templates/root-consumer/guides/dndev/COMPONENTS_CRUD.md.example +9 -6
  34. package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +376 -38
  35. package/templates/root-consumer/guides/dndev/SETUP_I18N.md.example +46 -0
  36. package/templates/root-consumer/guides/wai-way/entity_patterns.md.example +1 -1
  37. package/templates/root-consumer/storage.rules.example +8 -0
@@ -16,8 +16,13 @@
16
16
  <!-- ✅ PWA: Manifest link (if exists) -->
17
17
  <link rel="manifest" href="/manifest.json" />
18
18
 
19
- <!-- ✅ PERFORMANCE: Load fonts early to avoid preload warnings -->
20
- <link rel="stylesheet" href="/fonts/fonts.css" />
19
+ <!-- ✅ PERFORMANCE: Preload critical fonts (non-blocking) -->
20
+ <link rel="preload" href="/fonts/Inter-latin.woff2" as="font" type="font/woff2" crossorigin="anonymous">
21
+ <link rel="preload" href="/fonts/Roboto-400-latin.woff2" as="font" type="font/woff2" crossorigin="anonymous">
22
+
23
+ <!-- ✅ PERFORMANCE: Load extended font subsets async (non-blocking) -->
24
+ <link rel="stylesheet" href="/fonts/fonts.css" media="print" onload="this.media='all'">
25
+ <noscript><link rel="stylesheet" href="/fonts/fonts.css"></noscript>
21
26
 
22
27
  <!-- ✅ PERFORMANCE: Preconnect to external domains (OAuth providers) -->
23
28
  <!-- GitHub OAuth -->
@@ -36,6 +41,23 @@
36
41
 
37
42
  <!-- ✅ PERFORMANCE: Critical CSS inlined here by build -->
38
43
  <style>
44
+ /* Critical @font-face declarations - must be inline for preloaded fonts to work */
45
+ @font-face {
46
+ font-family: Inter;
47
+ font-style: normal;
48
+ font-weight: 400 700;
49
+ font-display: swap;
50
+ src: url('/fonts/Inter-latin.woff2') format('woff2');
51
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
52
+ }
53
+ @font-face {
54
+ font-family: Roboto;
55
+ font-style: normal;
56
+ font-weight: 400;
57
+ font-display: swap;
58
+ src: url('/fonts/Roboto-400-latin.woff2') format('woff2');
59
+ unicode-range: U+0000-00FF;
60
+ }
39
61
  /* Critical above-the-fold styles */
40
62
  html, body {
41
63
  margin: 0;
@@ -29,7 +29,7 @@ export const appConfig: AppConfig = {
29
29
  name: APP_NAME,
30
30
  shortName: APP_SHORT_NAME,
31
31
  description: APP_DESCRIPTION,
32
- // url: 'https://yourapp.com', // Uncomment for production
32
+ // Note: URL comes from VITE_APP_URL in .env, not here
33
33
 
34
34
  // Footer legal links - remove any you don't need
35
35
  footer: {
@@ -13,10 +13,10 @@
13
13
  import { useEffect, useState } from 'react';
14
14
 
15
15
  import { Section, Button, Alert } from '@donotdev/components';
16
- import { EntityFormRenderer, useCrud } from '@donotdev/crud';
16
+ import { useCrud } from '@donotdev/crud';
17
17
  import { useTranslation } from '@donotdev/core';
18
18
  import type { PageMeta } from '@donotdev/core';
19
- import { PageContainer, Link, useNavigate } from '@donotdev/ui';
19
+ import { PageContainer, Link, EntityFormRenderer } from '@donotdev/ui';
20
20
 
21
21
  // Import your entity from root-level entities folder
22
22
  // import { productEntity } from 'entities/Product';
@@ -51,7 +51,6 @@ export const meta: PageMeta = {
51
51
  export default function ProductPage() {
52
52
  const { t } = useTranslation(NAMESPACE);
53
53
  const id = useParam('id');
54
- const navigate = useNavigate();
55
54
  const isNew = id === 'new';
56
55
 
57
56
  // useCrud provides CRUD operations with optimistic updates
@@ -84,8 +83,8 @@ export default function ProductPage() {
84
83
  update(id, data); // No await - fires in background
85
84
  }
86
85
 
87
- // Instant navigation - feels incredibly fast
88
- navigate('/products');
86
+ // Navigation happens automatically via cancelPath (defaults to /products)
87
+ // Or you can navigate manually if needed
89
88
  };
90
89
 
91
90
  // ==========================================================================
@@ -121,6 +120,8 @@ export default function ProductPage() {
121
120
  onSubmit={handleSubmit}
122
121
  defaultValues={{ status: 'draft' }} // Initial values for new items
123
122
  submitText={t('create')}
123
+ // Cancel automatically navigates to /products (or cancelPath if provided)
124
+ cancelPath="/products"
124
125
  />
125
126
  ) : (
126
127
  // EDIT MODE
@@ -130,6 +131,8 @@ export default function ProductPage() {
130
131
  onSubmit={handleSubmit}
131
132
  defaultValues={formData} // Loaded data
132
133
  submitText={t('update')}
134
+ // Cancel automatically navigates to /products (or cancelPath if provided)
135
+ cancelPath="/products"
133
136
  />
134
137
  )}
135
138
  </Section>
@@ -14,7 +14,7 @@
14
14
 
15
15
  import { Package } from 'lucide-react';
16
16
 
17
- import { EntityList } from '@donotdev/crud';
17
+ import { EntityList } from '@donotdev/ui';
18
18
  import { useAuth } from '@donotdev/auth';
19
19
  import type { PageMeta } from '@donotdev/core';
20
20
  import { PageContainer } from '@donotdev/ui';
@@ -51,17 +51,14 @@ export default function ProductsListPage() {
51
51
  // - Applies visibility rules based on user role
52
52
  // - Includes search, sort, pagination
53
53
  // - Add/Edit/Delete buttons with proper permissions
54
+ // - Automatic routing: edit/view -> /products/:id, create -> /products/new
54
55
 
55
56
  return (
56
57
  <PageContainer>
57
58
  <EntityList
58
59
  entity={productEntity}
59
60
  userRole={user?.role}
60
- // Optional overrides:
61
- // onRowClick={(item) => navigate(`/products/${item.id}`)}
62
- // createPath="/products/new"
63
- // hideActions={false}
64
- // pageSize={25}
61
+ // Optional: basePath="/admin/products" or onClick={(id) => openSheet(id)}
65
62
  />
66
63
  </PageContainer>
67
64
  );
@@ -73,7 +70,7 @@ export default function ProductsListPage() {
73
70
  //
74
71
  // For a card-based grid instead of table:
75
72
  //
76
- // import { EntityCardList } from '@donotdev/crud';
73
+ // import { EntityCardList } from '@donotdev/ui';
77
74
  //
78
75
  // export default function ProductsGridPage() {
79
76
  // return (
@@ -0,0 +1,5 @@
1
+ {
2
+ "projects": {
3
+ "default": "{{firebaseProjectId}}"
4
+ }
5
+ }
@@ -63,6 +63,7 @@ export const productEntity = defineEntity({
63
63
  // 'checkbox' → Multiple selections
64
64
  // 'date' → Date picker
65
65
  // 'timestamp' → Date + time
66
+ // 'price' → Structured price (amount, currency, VAT, discount %)
66
67
  // 'images' → Image upload (multiple)
67
68
  // 'reference' → Link to another entity
68
69
  //
@@ -120,7 +121,7 @@ export const productEntity = defineEntity({
120
121
  price: {
121
122
  name: 'price',
122
123
  label: 'price',
123
- type: 'number',
124
+ type: 'price',
124
125
  visibility: 'guest',
125
126
  editable: 'admin',
126
127
  validation: { required: true },
@@ -16,6 +16,7 @@ const demoEntity = defineEntity({
16
16
  name: 'Demo',
17
17
  collection: 'demos',
18
18
  // collection: 'users/{userId}/demos', // Subcollection syntax
19
+ // namespace: 'entity-demo', // Optional: defaults to entity-${name.toLowerCase()}
19
20
 
20
21
  listFields: ['title', 'status', 'category', 'price'],
21
22
  listCardFields: ['images', 'title', 'price'],
@@ -98,7 +99,7 @@ const demoEntity = defineEntity({
98
99
  price: {
99
100
  name: 'price',
100
101
  label: 'price',
101
- type: 'number',
102
+ type: 'price', // Value: { amount, currency?, vatIncluded?, discountPercent? }
102
103
  visibility: 'guest',
103
104
  editable: 'admin',
104
105
  validation: { required: true },
@@ -113,6 +114,19 @@ const demoEntity = defineEntity({
113
114
  validation: { required: false },
114
115
  },
115
116
 
117
+ year: {
118
+ name: 'year',
119
+ label: 'year',
120
+ type: 'year', // Year combobox (type or select)
121
+ visibility: 'guest',
122
+ editable: 'owner',
123
+ validation: {
124
+ required: true,
125
+ min: 1900, // Optional: defaults to 1900
126
+ max: 2100, // Optional: defaults to current year + 10
127
+ },
128
+ },
129
+
116
130
  // =========================================================================
117
131
  // CONTACT
118
132
  // =========================================================================
@@ -19,7 +19,8 @@ import react from 'eslint-plugin-react';
19
19
  import reactRefresh from 'eslint-plugin-react-refresh';
20
20
  import globals from 'globals';
21
21
 
22
- import customPlugin from '@donotdev/core/config/eslint';
22
+ // ESLint is not provided by the framework - set up your own configuration
23
+ // This is just a basic example template
23
24
 
24
25
  const __filename = fileURLToPath(import.meta.url);
25
26
  const __dirname = dirname(__filename);
@@ -106,7 +107,6 @@ export default [
106
107
  'jsx-a11y': jsxA11y,
107
108
  prettier: prettierPlugin,
108
109
  '@typescript-eslint': tsPlugin,
109
- '@donotdev/config': customPlugin,
110
110
  },
111
111
  settings: {
112
112
  react: {
@@ -120,9 +120,6 @@ export default [
120
120
  },
121
121
  },
122
122
  rules: {
123
- '@donotdev/config/no-singleton-lifecycle': 'error',
124
- '@donotdev/config/react-19-ssr-safe': 'error',
125
- '@donotdev/config/prefer-framework-utils': 'warn',
126
123
  'react/no-unstable-nested-components': 'error',
127
124
  'no-restricted-globals': [
128
125
  'error',
@@ -203,81 +200,6 @@ export default [
203
200
  'warn',
204
201
  { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
205
202
  ],
206
- '@donotdev/config/require-use-client': [
207
- 'warn',
208
- {
209
- clientPatterns: [
210
- 'useState',
211
- 'useEffect',
212
- 'useCallback',
213
- 'useMemo',
214
- 'useRef',
215
- 'useContext',
216
- 'useReducer',
217
- 'useLayoutEffect',
218
- 'useImperativeHandle',
219
- 'useDebugValue',
220
- 'useId',
221
- 'useSyncExternalStore',
222
- 'useTransition',
223
- 'useDeferredValue',
224
- 'useInsertionEffect',
225
- 'onClick',
226
- 'onChange',
227
- 'onSubmit',
228
- 'onFocus',
229
- 'onBlur',
230
- 'onMouseEnter',
231
- 'onMouseLeave',
232
- 'onKeyDown',
233
- 'onKeyUp',
234
- 'onKeyPress',
235
- 'onScroll',
236
- 'onResize',
237
- 'onLoad',
238
- 'onError',
239
- 'window',
240
- 'document',
241
- 'localStorage',
242
- 'sessionStorage',
243
- 'navigator',
244
- 'location',
245
- 'history',
246
- 'addEventListener',
247
- 'removeEventListener',
248
- 'useAuth',
249
- 'useTranslation',
250
- 'useLocalStorage',
251
- 'useTheme',
252
- 'useRouter',
253
- 'usePathname',
254
- 'useSearchParams',
255
- 'framer-motion',
256
- 'lottie',
257
- 'gsap',
258
- 'shiki',
259
- ],
260
- filePatterns: ['**/*.{ts,tsx}'],
261
- excludePatterns: [
262
- '**/*.config.{js,ts}',
263
- '**/*.d.ts',
264
- '**/types/**',
265
- '**/constants/**',
266
- '**/utils/**',
267
- '**/helpers/**',
268
- '**/stores/**',
269
- '**/api/**',
270
- '**/functions/**',
271
- '**/lib/**',
272
- '**/server/**',
273
- '**/middleware/**',
274
- '**/edge/**',
275
- '**/node_modules/**',
276
- '**/dist/**',
277
- '**/build/**',
278
- ],
279
- },
280
- ],
281
203
  'import/no-named-export': 'off',
282
204
  },
283
205
  },
@@ -0,0 +1,4 @@
1
+ {
2
+ "indexes": [],
3
+ "fieldOverrides": []
4
+ }
@@ -0,0 +1,11 @@
1
+ rules_version = '2';
2
+ service cloud.firestore {
3
+ match /databases/{database}/documents {
4
+ function isAuthenticated() {
5
+ return request.auth != null;
6
+ }
7
+ match /{document=**} {
8
+ allow read, write: if isAuthenticated();
9
+ }
10
+ }
11
+ }
@@ -19,14 +19,14 @@ await remove('doc-id');
19
19
  Paginated list with automatic loading. For data tables.
20
20
 
21
21
  ```tsx
22
- const { items, loading, hasMore, loadMore } = useCrudList(productEntity);
22
+ const { items, loading, refresh } = useCrudList(productEntity);
23
23
  ```
24
24
 
25
25
  ### useCrudCardList
26
26
  Card-based list with infinite scroll.
27
27
 
28
28
  ```tsx
29
- const { items, loading, hasMore, loadMore, refresh } = useCrudCardList(articleEntity);
29
+ const { items, loading, refresh } = useCrudCardList(articleEntity);
30
30
  ```
31
31
 
32
32
  ---
@@ -106,6 +106,9 @@ Field components are auto-rendered by `FormFieldRenderer`. You don't import them
106
106
  ### Numbers
107
107
  - `number` - Numeric input
108
108
  - `range` - Slider input
109
+ - `rating` - Star rating input (1–5, configurable max)
110
+
111
+ **Rating + comment (e.g. reviews):** Use two fields on the same entity — `rating` (type `rating`) for stars and `comment` (type `textarea`) for the text. The form renders them as separate rows; no composite field type needed.
109
112
 
110
113
  ### Boolean
111
114
  - `checkbox` - Checkbox input
@@ -153,7 +156,7 @@ import { useController, registerFieldType } from '@donotdev/crud';
153
156
  import type { ControlledFieldProps } from '@donotdev/crud';
154
157
 
155
158
  // Custom controlled component MUST use framework's useController (not react-hook-form's)
156
- function RatingField({
159
+ function ScoreField({
157
160
  fieldConfig,
158
161
  control,
159
162
  errors,
@@ -179,7 +182,7 @@ function RatingField({
179
182
  onChange?.(value);
180
183
  }}
181
184
  min={0}
182
- max={5}
185
+ max={10}
183
186
  />
184
187
  {fieldState?.error && (
185
188
  <span className="error">{fieldState.error.message}</span>
@@ -189,8 +192,8 @@ function RatingField({
189
192
  }
190
193
 
191
194
  registerFieldType({
192
- type: 'rating',
193
- controlledComponent: RatingField,
195
+ type: 'score',
196
+ controlledComponent: ScoreField,
194
197
  });
195
198
  ```
196
199