@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.
Files changed (34) hide show
  1. package/README.md +7 -26
  2. package/cjs/index.js +191 -28
  3. package/cjs/src/guidelines/componentRule/componentRule.instruction.md +3 -81
  4. package/cjs/src/guidelines/cssRule/cssRule.instruction.md +435 -0
  5. package/cjs/src/guidelines/docPageRule/docPageRule.instruction.md +389 -0
  6. package/cjs/src/guidelines/modelConstant/modelConstant.instruction.md +335 -752
  7. package/cjs/src/guidelines/modelStore/modelStore.instruction.md +2 -1
  8. package/cjs/src/guidelines/modelTemplate/modelTemplate.instruction.md +418 -391
  9. package/cjs/src/guidelines/modelUnit/modelUnit.instruction.md +0 -292
  10. package/cjs/src/guidelines/scalarModule/scalarModule.instruction.md +84 -0
  11. package/cjs/src/templates/app/main.js +1 -2
  12. package/esm/index.js +199 -36
  13. package/esm/src/guidelines/componentRule/componentRule.instruction.md +3 -81
  14. package/esm/src/guidelines/cssRule/cssRule.instruction.md +435 -0
  15. package/esm/src/guidelines/docPageRule/docPageRule.instruction.md +389 -0
  16. package/esm/src/guidelines/modelConstant/modelConstant.instruction.md +335 -752
  17. package/esm/src/guidelines/modelStore/modelStore.instruction.md +2 -1
  18. package/esm/src/guidelines/modelTemplate/modelTemplate.instruction.md +418 -391
  19. package/esm/src/guidelines/modelUnit/modelUnit.instruction.md +0 -292
  20. package/esm/src/guidelines/scalarModule/scalarModule.instruction.md +84 -0
  21. package/esm/src/templates/app/main.js +1 -2
  22. package/package.json +1 -1
  23. package/src/guideline/guideline.command.d.ts +3 -1
  24. package/src/guideline/guideline.prompt.d.ts +15 -1
  25. package/src/guideline/guideline.runner.d.ts +17 -3
  26. package/src/guideline/guideline.script.d.ts +8 -2
  27. package/src/guidelines/componentRule/componentRule.instruction.md +3 -81
  28. package/src/guidelines/cssRule/cssRule.instruction.md +435 -0
  29. package/src/guidelines/docPageRule/docPageRule.instruction.md +389 -0
  30. package/src/guidelines/modelConstant/modelConstant.instruction.md +335 -752
  31. package/src/guidelines/modelStore/modelStore.instruction.md +2 -1
  32. package/src/guidelines/modelTemplate/modelTemplate.instruction.md +418 -391
  33. package/src/guidelines/modelUnit/modelUnit.instruction.md +0 -292
  34. 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
- const app = await createNestApp({ registerModules, serverMode, env });
36
- return () => app.close();
35
+ await createNestApp({ registerModules, serverMode, env });
37
36
  };
38
37
  void bootstrap();
39
38
  `;