@akanjs/cli 0.0.145 → 0.0.147
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/README.md +7 -26
- package/cjs/index.js +191 -28
- package/cjs/src/guidelines/componentRule/componentRule.instruction.md +3 -81
- package/cjs/src/guidelines/cssRule/cssRule.instruction.md +435 -0
- package/cjs/src/guidelines/docPageRule/docPageRule.instruction.md +389 -0
- package/cjs/src/guidelines/modelConstant/modelConstant.instruction.md +335 -752
- package/cjs/src/guidelines/modelStore/modelStore.instruction.md +2 -1
- package/cjs/src/guidelines/modelTemplate/modelTemplate.instruction.md +418 -391
- package/cjs/src/guidelines/modelUnit/modelUnit.instruction.md +0 -292
- package/cjs/src/guidelines/scalarModule/scalarModule.instruction.md +84 -0
- package/cjs/src/templates/app/main.js +1 -2
- package/esm/index.js +199 -36
- package/esm/src/guidelines/componentRule/componentRule.instruction.md +3 -81
- package/esm/src/guidelines/cssRule/cssRule.instruction.md +435 -0
- package/esm/src/guidelines/docPageRule/docPageRule.instruction.md +389 -0
- package/esm/src/guidelines/modelConstant/modelConstant.instruction.md +335 -752
- package/esm/src/guidelines/modelStore/modelStore.instruction.md +2 -1
- package/esm/src/guidelines/modelTemplate/modelTemplate.instruction.md +418 -391
- package/esm/src/guidelines/modelUnit/modelUnit.instruction.md +0 -292
- package/esm/src/guidelines/scalarModule/scalarModule.instruction.md +84 -0
- package/esm/src/templates/app/main.js +1 -2
- package/package.json +1 -1
- package/src/guideline/guideline.command.d.ts +3 -1
- package/src/guideline/guideline.prompt.d.ts +15 -1
- package/src/guideline/guideline.runner.d.ts +17 -3
- package/src/guideline/guideline.script.d.ts +8 -2
- package/src/guidelines/componentRule/componentRule.instruction.md +3 -81
- package/src/guidelines/cssRule/cssRule.instruction.md +435 -0
- package/src/guidelines/docPageRule/docPageRule.instruction.md +389 -0
- package/src/guidelines/modelConstant/modelConstant.instruction.md +335 -752
- package/src/guidelines/modelStore/modelStore.instruction.md +2 -1
- package/src/guidelines/modelTemplate/modelTemplate.instruction.md +418 -391
- package/src/guidelines/modelUnit/modelUnit.instruction.md +0 -292
- package/src/guidelines/scalarModule/scalarModule.instruction.md +84 -0
|
@@ -194,167 +194,6 @@ export const Abstract = ({ product }: { product: cnst.LightProduct }) => (
|
|
|
194
194
|
- Ensure proper image alt text
|
|
195
195
|
- Maintain keyboard navigability
|
|
196
196
|
|
|
197
|
-
## How to Use Model.Unit.tsx Files
|
|
198
|
-
|
|
199
|
-
### In Pages
|
|
200
|
-
|
|
201
|
-
```tsx
|
|
202
|
-
// apps/your-app/app/products/page.tsx
|
|
203
|
-
import { Product } from "@shared/client";
|
|
204
|
-
import { getProducts } from "@shared/lib/product/product.service";
|
|
205
|
-
|
|
206
|
-
export default async function ProductsPage() {
|
|
207
|
-
const products = await getProducts();
|
|
208
|
-
|
|
209
|
-
return (
|
|
210
|
-
<div className="container mx-auto">
|
|
211
|
-
<h1 className="mb-6 text-2xl font-bold">Products</h1>
|
|
212
|
-
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
213
|
-
{products.map((product) => (
|
|
214
|
-
<Product.Unit.Card key={product.id} product={product} href={`/products/${product.id}`} />
|
|
215
|
-
))}
|
|
216
|
-
</div>
|
|
217
|
-
</div>
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### In Utils
|
|
223
|
-
|
|
224
|
-
```tsx
|
|
225
|
-
// libs/shared/lib/product/Product.Util.tsx
|
|
226
|
-
"use client";
|
|
227
|
-
import { useState } from "react";
|
|
228
|
-
import { Product } from "@shared/client";
|
|
229
|
-
import { cnst } from "@shared/client";
|
|
230
|
-
|
|
231
|
-
export const ProductSelector = ({
|
|
232
|
-
products,
|
|
233
|
-
onSelect,
|
|
234
|
-
}: {
|
|
235
|
-
products: cnst.LightProduct[];
|
|
236
|
-
onSelect: (product: cnst.LightProduct) => void;
|
|
237
|
-
}) => {
|
|
238
|
-
const [selectedId, setSelectedId] = useState<string | null>(null);
|
|
239
|
-
|
|
240
|
-
const handleSelect = (product: cnst.LightProduct) => {
|
|
241
|
-
setSelectedId(product.id);
|
|
242
|
-
onSelect(product);
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
return (
|
|
246
|
-
<div className="space-y-2">
|
|
247
|
-
{products.map((product) => (
|
|
248
|
-
<button key={product.id} onClick={() => handleSelect(product)} className="w-full text-left">
|
|
249
|
-
<Product.Unit.Abstract product={product} className={selectedId === product.id ? "border-primary" : ""} />
|
|
250
|
-
</button>
|
|
251
|
-
))}
|
|
252
|
-
</div>
|
|
253
|
-
);
|
|
254
|
-
};
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### In Templates
|
|
258
|
-
|
|
259
|
-
```tsx
|
|
260
|
-
// libs/shared/lib/product/Product.Template.tsx
|
|
261
|
-
"use client";
|
|
262
|
-
import { useState } from "react";
|
|
263
|
-
import { Product } from "@shared/client";
|
|
264
|
-
import { cnst } from "@shared/client";
|
|
265
|
-
import { sig } from "@shared/client";
|
|
266
|
-
|
|
267
|
-
export const ProductList = ({ initialProducts }: { initialProducts: cnst.LightProduct[] }) => {
|
|
268
|
-
const [products, setProducts] = useState(initialProducts);
|
|
269
|
-
const [loading, setLoading] = useState(false);
|
|
270
|
-
|
|
271
|
-
const loadMore = async () => {
|
|
272
|
-
if (loading) return;
|
|
273
|
-
setLoading(true);
|
|
274
|
-
try {
|
|
275
|
-
const nextPage = await sig.product.getProducts({
|
|
276
|
-
skip: products.length,
|
|
277
|
-
limit: 10,
|
|
278
|
-
});
|
|
279
|
-
setProducts([...products, ...nextPage.products]);
|
|
280
|
-
} catch (error) {
|
|
281
|
-
console.error("Failed to load more products", error);
|
|
282
|
-
} finally {
|
|
283
|
-
setLoading(false);
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
return (
|
|
288
|
-
<div>
|
|
289
|
-
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
290
|
-
{products.map((product) => (
|
|
291
|
-
<Product.Unit.Card key={product.id} product={product} />
|
|
292
|
-
))}
|
|
293
|
-
</div>
|
|
294
|
-
|
|
295
|
-
{products.length >= 10 && (
|
|
296
|
-
<button className="btn btn-outline mx-auto mt-6 block" onClick={loadMore} disabled={loading}>
|
|
297
|
-
{loading ? "Loading..." : "Load More"}
|
|
298
|
-
</button>
|
|
299
|
-
)}
|
|
300
|
-
</div>
|
|
301
|
-
);
|
|
302
|
-
};
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
### In Zones
|
|
306
|
-
|
|
307
|
-
```tsx
|
|
308
|
-
// libs/shared/lib/product/Product.Zone.tsx
|
|
309
|
-
"use client";
|
|
310
|
-
import { useState, useEffect } from "react";
|
|
311
|
-
import { cnst } from "@shared/client";
|
|
312
|
-
import { sig } from "@shared/client";
|
|
313
|
-
import { Product } from "@shared/client";
|
|
314
|
-
import { Zone, Tabs } from "@util/ui";
|
|
315
|
-
|
|
316
|
-
export const ProductManager = () => {
|
|
317
|
-
const [products, setProducts] = useState<cnst.LightProduct[]>([]);
|
|
318
|
-
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
|
319
|
-
|
|
320
|
-
useEffect(() => {
|
|
321
|
-
const loadProducts = async () => {
|
|
322
|
-
const data = await sig.product.getProducts({
|
|
323
|
-
filter: selectedCategory ? { byCategory: selectedCategory } : undefined,
|
|
324
|
-
});
|
|
325
|
-
setProducts(data.products);
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
loadProducts();
|
|
329
|
-
}, [selectedCategory]);
|
|
330
|
-
|
|
331
|
-
return (
|
|
332
|
-
<Zone title="Product Management">
|
|
333
|
-
<Zone.Section title="Categories">
|
|
334
|
-
<Tabs
|
|
335
|
-
tabs={[
|
|
336
|
-
{ key: null, label: "All" },
|
|
337
|
-
{ key: "electronics", label: "Electronics" },
|
|
338
|
-
{ key: "clothing", label: "Clothing" },
|
|
339
|
-
{ key: "food", label: "Food" },
|
|
340
|
-
]}
|
|
341
|
-
activeTab={selectedCategory}
|
|
342
|
-
onChange={(tab) => setSelectedCategory(tab)}
|
|
343
|
-
/>
|
|
344
|
-
</Zone.Section>
|
|
345
|
-
|
|
346
|
-
<Zone.Section title="Products">
|
|
347
|
-
<div className="space-y-2">
|
|
348
|
-
{products.map((product) => (
|
|
349
|
-
<Product.Unit.Row key={product.id} product={product} />
|
|
350
|
-
))}
|
|
351
|
-
</div>
|
|
352
|
-
</Zone.Section>
|
|
353
|
-
</Zone>
|
|
354
|
-
);
|
|
355
|
-
};
|
|
356
|
-
```
|
|
357
|
-
|
|
358
197
|
## Best Practices
|
|
359
198
|
|
|
360
199
|
### 1. Component Variations and Consistent Exports
|
|
@@ -566,136 +405,6 @@ export const List = ({ product }: { product: cnst.LightProduct }) => (
|
|
|
566
405
|
);
|
|
567
406
|
```
|
|
568
407
|
|
|
569
|
-
## Performance Optimization
|
|
570
|
-
|
|
571
|
-
### 1. Efficient Rendering with Memoization
|
|
572
|
-
|
|
573
|
-
When using Model.Unit components in lists, consider memoizing them in client components:
|
|
574
|
-
|
|
575
|
-
```tsx
|
|
576
|
-
// In a client component file
|
|
577
|
-
"use client";
|
|
578
|
-
import { memo } from "react";
|
|
579
|
-
import { Product } from "@shared/client";
|
|
580
|
-
|
|
581
|
-
// Create memoized versions to prevent unnecessary re-renders
|
|
582
|
-
const ProductCard = memo(Product.Unit.Card);
|
|
583
|
-
const ProductRow = memo(Product.Unit.Row);
|
|
584
|
-
|
|
585
|
-
export const ProductList = ({ products, view = "grid" }) => {
|
|
586
|
-
return (
|
|
587
|
-
<div className={view === "grid" ? "grid grid-cols-3 gap-4" : "space-y-2"}>
|
|
588
|
-
{products.map((product) =>
|
|
589
|
-
view === "grid" ? (
|
|
590
|
-
<ProductCard key={product.id} product={product} />
|
|
591
|
-
) : (
|
|
592
|
-
<ProductRow key={product.id} product={product} />
|
|
593
|
-
)
|
|
594
|
-
)}
|
|
595
|
-
</div>
|
|
596
|
-
);
|
|
597
|
-
};
|
|
598
|
-
```
|
|
599
|
-
|
|
600
|
-
### 2. Virtualization for Long Lists
|
|
601
|
-
|
|
602
|
-
```tsx
|
|
603
|
-
"use client";
|
|
604
|
-
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
605
|
-
import { useRef } from "react";
|
|
606
|
-
import { Product } from "@shared/client";
|
|
607
|
-
|
|
608
|
-
export const VirtualProductList = ({ products }) => {
|
|
609
|
-
const parentRef = useRef(null);
|
|
610
|
-
|
|
611
|
-
const virtualizer = useVirtualizer({
|
|
612
|
-
count: products.length,
|
|
613
|
-
getScrollElement: () => parentRef.current,
|
|
614
|
-
estimateSize: () => 80, // Approximate height of each row
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
return (
|
|
618
|
-
<div ref={parentRef} className="h-[600px] overflow-auto">
|
|
619
|
-
<div className="relative w-full" style={{ height: `${virtualizer.getTotalSize()}px` }}>
|
|
620
|
-
{virtualizer.getVirtualItems().map((virtualItem) => (
|
|
621
|
-
<div
|
|
622
|
-
key={virtualItem.key}
|
|
623
|
-
className="absolute top-0 left-0 w-full"
|
|
624
|
-
style={{
|
|
625
|
-
height: `${virtualItem.size}px`,
|
|
626
|
-
transform: `translateY(${virtualItem.start}px)`,
|
|
627
|
-
}}
|
|
628
|
-
>
|
|
629
|
-
<Product.Unit.Row product={products[virtualItem.index]} />
|
|
630
|
-
</div>
|
|
631
|
-
))}
|
|
632
|
-
</div>
|
|
633
|
-
</div>
|
|
634
|
-
);
|
|
635
|
-
};
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
### 3. Image Optimization Techniques
|
|
639
|
-
|
|
640
|
-
```tsx
|
|
641
|
-
import { Image } from "@util/ui";
|
|
642
|
-
|
|
643
|
-
export const Card = ({ product }: { product: cnst.LightProduct }) => (
|
|
644
|
-
<div className="card">
|
|
645
|
-
{product.image && (
|
|
646
|
-
<Image
|
|
647
|
-
file={product.image}
|
|
648
|
-
alt={product.name}
|
|
649
|
-
className="h-40 w-full object-cover"
|
|
650
|
-
priority={false}
|
|
651
|
-
loading="lazy"
|
|
652
|
-
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
|
653
|
-
/>
|
|
654
|
-
)}
|
|
655
|
-
{/* Other content */}
|
|
656
|
-
</div>
|
|
657
|
-
);
|
|
658
|
-
```
|
|
659
|
-
|
|
660
|
-
### 4. Conditional Loading of Complex Units
|
|
661
|
-
|
|
662
|
-
For complex units with heavy rendering needs:
|
|
663
|
-
|
|
664
|
-
```tsx
|
|
665
|
-
export const Full = ({ product, detailed = false }: { product: cnst.LightProduct; detailed?: boolean }) => (
|
|
666
|
-
<div className="product-full rounded border p-4">
|
|
667
|
-
<h2 className="text-xl font-bold">{product.name}</h2>
|
|
668
|
-
<p className="mt-2">{product.price.toLocaleString()} KRW</p>
|
|
669
|
-
|
|
670
|
-
{/* Only render detailed content when needed */}
|
|
671
|
-
{detailed && (
|
|
672
|
-
<>
|
|
673
|
-
<div className="mt-4 grid grid-cols-2 gap-2">
|
|
674
|
-
<div>SKU: {product.sku}</div>
|
|
675
|
-
<div>Category: {product.category}</div>
|
|
676
|
-
<div>Stock: {product.stockQuantity}</div>
|
|
677
|
-
<div>Weight: {product.weight}kg</div>
|
|
678
|
-
</div>
|
|
679
|
-
|
|
680
|
-
<div className="mt-4">
|
|
681
|
-
<h3 className="font-bold">Specifications</h3>
|
|
682
|
-
<table className="mt-2 w-full border">
|
|
683
|
-
<tbody>
|
|
684
|
-
{product.specifications?.map((spec) => (
|
|
685
|
-
<tr key={spec.key} className="border-b">
|
|
686
|
-
<td className="p-2 font-medium">{spec.key}</td>
|
|
687
|
-
<td className="p-2">{spec.value}</td>
|
|
688
|
-
</tr>
|
|
689
|
-
))}
|
|
690
|
-
</tbody>
|
|
691
|
-
</table>
|
|
692
|
-
</div>
|
|
693
|
-
</>
|
|
694
|
-
)}
|
|
695
|
-
</div>
|
|
696
|
-
);
|
|
697
|
-
```
|
|
698
|
-
|
|
699
408
|
## Troubleshooting Common Issues
|
|
700
409
|
|
|
701
410
|
### 1. Server Component Errors
|
|
@@ -827,7 +536,6 @@ Key takeaways:
|
|
|
827
536
|
- Keep components as server components without client-side hooks
|
|
828
537
|
- Focus on presentation only, leaving business logic to Service files
|
|
829
538
|
- Follow consistent prop patterns with className and href support
|
|
830
|
-
- Optimize for performance with proper image handling and conditional rendering
|
|
831
539
|
- Ensure type safety by respecting LightModel type definitions
|
|
832
540
|
|
|
833
541
|
Well-designed Model.Unit components improve development velocity, maintain UI consistency, and enable seamless component reuse throughout your Akan.js applications.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Scalar Modules Overview
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Scalar modules provide reusable value objects for:
|
|
6
|
+
|
|
7
|
+
- Embedded documents in domain models
|
|
8
|
+
- Shared DTOs and configuration objects
|
|
9
|
+
- Type-safe schemas across your application
|
|
10
|
+
- Internationalized data structures
|
|
11
|
+
|
|
12
|
+
## Core Principles
|
|
13
|
+
|
|
14
|
+
- **Reusability**: Designed for cross-module consumption
|
|
15
|
+
- **Stateless**: Pure data containers without business logic
|
|
16
|
+
- **Type-Safe**: Full TypeScript integration with runtime validation
|
|
17
|
+
- **Composable**: Embeddable within larger domain models
|
|
18
|
+
- **I18n Ready**: Built-in translation support
|
|
19
|
+
|
|
20
|
+
## File Structure
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
{domain}/lib/
|
|
24
|
+
└── __scalar/ // Special scalar directory
|
|
25
|
+
├── _server.ts // Module registration
|
|
26
|
+
└── [scalarName]/ // camelCase scalar name
|
|
27
|
+
├── [name].constant.ts // Schema definition
|
|
28
|
+
├── [name].dictionary.ts // I18n translations
|
|
29
|
+
└── [name].document.ts // Method extensions (optional)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## File Responsibilities
|
|
33
|
+
|
|
34
|
+
| File Type | Purpose |
|
|
35
|
+
| ----------------- | ---------------------------------------------------------------- |
|
|
36
|
+
| `*.constant.ts` | Defines schema with `@Model.Scalar` and `@Field.Prop` decorators |
|
|
37
|
+
| `*.dictionary.ts` | Provides internationalization with `ModelDictionary<Type>` |
|
|
38
|
+
| `*.document.ts` | Extends functionality with custom methods using `by()` decorator |
|
|
39
|
+
|
|
40
|
+
## Naming Conventions
|
|
41
|
+
|
|
42
|
+
| Element | Convention | Example |
|
|
43
|
+
| ---------------- | ---------------------- | --------------------------- |
|
|
44
|
+
| Scalar Directory | `camelCase` | `geoLocation` |
|
|
45
|
+
| Constant File | `[name].constant.ts` | `geoLocation.constant.ts` |
|
|
46
|
+
| Dictionary File | `[name].dictionary.ts` | `geoLocation.dictionary.ts` |
|
|
47
|
+
| Document File | `[name].document.ts` | `geoLocation.document.ts` |
|
|
48
|
+
| Scalar Class | `PascalCase` | `GeoLocation` |
|
|
49
|
+
| Enum Values | `camelCase` | `highAccuracy` |
|
|
50
|
+
| Dictionary Keys | `kebab-case` prefixes | `desc-fieldName` |
|
|
51
|
+
|
|
52
|
+
## Core Components
|
|
53
|
+
|
|
54
|
+
1. **Constant File**: Schema definition with typed fields and validation
|
|
55
|
+
2. **Dictionary File**: I18n translations for model metadata, fields and enums
|
|
56
|
+
3. **Document File**: Optional method extensions for data transformations
|
|
57
|
+
4. **Server Registration**: Aggregate and expose scalars through `_server.ts`
|
|
58
|
+
|
|
59
|
+
## Key Rules
|
|
60
|
+
|
|
61
|
+
1. One scalar class per file with matching `@Model.Scalar` parameter
|
|
62
|
+
2. All fields require decorators (`@Field.Prop`, `@Field.Hidden`, etc.)
|
|
63
|
+
3. Dictionary must include all fields/enums with `modelName`/`modelDesc`
|
|
64
|
+
4. Use `satisfies ModelDictionary<Type>` for dictionary type safety
|
|
65
|
+
5. Register all scalars in `_server.ts` with `scalarModulesOf()`
|
|
66
|
+
|
|
67
|
+
## Best Practices
|
|
68
|
+
|
|
69
|
+
- Design for maximum reusability across modules
|
|
70
|
+
- Keep scalars focused and lightweight
|
|
71
|
+
- Use immutable fields for invariant data
|
|
72
|
+
- Provide complete I18n coverage in dictionaries
|
|
73
|
+
- Add document methods only for data transformations
|
|
74
|
+
- Validate fields with options (min/max, minlength/maxlength)
|
|
75
|
+
|
|
76
|
+
## Integration Points
|
|
77
|
+
|
|
78
|
+
- **Domain Models**: Embedded as value objects
|
|
79
|
+
- **GraphQL**: Auto-generated types and enums
|
|
80
|
+
- **Validation**: Runtime type checking through decorators
|
|
81
|
+
- **Internationalization**: Consistent terminology via dictionaries
|
|
82
|
+
- **API Contracts**: Shared request/response payloads
|
|
83
|
+
|
|
84
|
+
Scalar modules provide foundational data structures that enable consistent, type-safe modeling across Akan.js applications while promoting code reuse and maintainability.
|
|
@@ -32,8 +32,7 @@ import { registerModules } from "./server";
|
|
|
32
32
|
const bootstrap = async () => {
|
|
33
33
|
const serverMode = process.env.SERVER_MODE as "federation" | "batch" | "all" | null;
|
|
34
34
|
if (!serverMode) throw new Error("SERVER_MODE environment variable is not defined");
|
|
35
|
-
|
|
36
|
-
return () => app.close();
|
|
35
|
+
await createNestApp({ registerModules, serverMode, env });
|
|
37
36
|
};
|
|
38
37
|
void bootstrap();
|
|
39
38
|
`;
|