pasika 0.1.1 → 0.1.6

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 (58) hide show
  1. package/README.md +31 -25
  2. package/claude/hooks/.vulyk +3 -0
  3. package/claude/hooks/AGENTS.md +3 -0
  4. package/claude/hooks/claude-hooks.md +30 -0
  5. package/claude/{.claude/hooks → hooks}/notification.sh +0 -0
  6. package/claude/{.claude/hooks → hooks}/protect-files.sh +0 -0
  7. package/claude/scripts/render-settings.ts +25 -9
  8. package/dist/claude/scripts/render-settings.js +22 -9
  9. package/dist/eslint/pasika/index.d.ts +2 -0
  10. package/dist/eslint/pasika/index.js +17 -0
  11. package/dist/eslint/pasika/rules/filename-case.d.ts +2 -0
  12. package/dist/eslint/pasika/rules/filename-case.js +90 -0
  13. package/dist/eslint/pasika/rules/organization-imports.d.ts +2 -0
  14. package/dist/eslint/pasika/rules/organization-imports.js +124 -0
  15. package/docs/agent-conventions.md +27 -0
  16. package/docs/code-organization-guide/code-organization-guide.md +69 -0
  17. package/docs/code-organization-guide/references/application-architecture-reference.md +149 -0
  18. package/docs/code-organization-guide/rules/component-placement-rule.md +119 -0
  19. package/docs/code-organization-guide/rules/configuration-rule.md +42 -0
  20. package/docs/code-organization-guide/rules/constants-rule.md +74 -0
  21. package/docs/code-organization-guide/rules/exports-and-imports-rule.md +84 -0
  22. package/docs/code-organization-guide/rules/folder-nesting-rule.md +82 -0
  23. package/docs/code-organization-guide/rules/hook-extraction-rule.md +141 -0
  24. package/docs/code-organization-guide/rules/interactive-component-rule.md +97 -0
  25. package/docs/code-organization-guide/rules/jsx-hygiene-rule.md +67 -0
  26. package/docs/code-organization-guide/rules/locales-rule.md +53 -0
  27. package/docs/code-organization-guide/rules/nameable-visual-concept-rule.md +66 -0
  28. package/docs/code-organization-guide/rules/no-mixed-concerns-rule.md +63 -0
  29. package/docs/code-organization-guide/rules/repeated-structure-rule.md +93 -0
  30. package/docs/code-organization-guide/rules/smart-vs-dumb-component-rule.md +112 -0
  31. package/docs/code-organization-guide/rules/sole-state-owner-rule.md +101 -0
  32. package/docs/code-organization-guide/rules/types-and-schemas-rule.md +139 -0
  33. package/docs/code-organization-guide/rules/utilities-rule.md +86 -0
  34. package/docs/documentation-guide/_templates/grouped-reference.md +11 -0
  35. package/docs/documentation-guide/_templates/guide.md +19 -0
  36. package/docs/documentation-guide/_templates/rule.md +21 -0
  37. package/docs/documentation-guide/_templates/single-lookup-reference.md +5 -0
  38. package/docs/documentation-guide/documentation-guide.md +13 -0
  39. package/docs/documentation-guide/references/documentation-types-reference.md +9 -0
  40. package/docs/documentation-guide/rules/guide-creation-rule.md +113 -0
  41. package/docs/documentation-guide/rules/reference-creation-rule.md +132 -0
  42. package/docs/documentation-guide/rules/rule-creation-rule.md +81 -0
  43. package/docs/documentation-guide/rules/template-usage-rule.md +49 -0
  44. package/docs/shadcn-theme.md +121 -0
  45. package/docs/styling-guide/rules/arbitrary-value-rule.md +31 -0
  46. package/docs/styling-guide/rules/class-composition-rule.md +52 -0
  47. package/docs/styling-guide/rules/component-ui-state-rule.md +53 -0
  48. package/docs/styling-guide/rules/component-variant-rule.md +125 -0
  49. package/docs/styling-guide/rules/global-stylesheet-rule.md +67 -0
  50. package/docs/styling-guide/rules/theme-and-utility-definition-rule.md +86 -0
  51. package/docs/styling-guide/styling-guide.md +14 -0
  52. package/package.json +22 -12
  53. package/AGENTS.md +0 -15
  54. package/docs/common/merge-behavior.md +0 -17
  55. package/docs/common/overview.md +0 -20
  56. /package/{CLAUDE.md → claude/hooks/CLAUDE.md} +0 -0
  57. /package/claude/{.claude/hooks → hooks}/status-line/index.js +0 -0
  58. /package/claude/{.claude/settings.base.json → settings.base.json} +0 -0
@@ -0,0 +1,149 @@
1
+ # Application Architecture Reference
2
+
3
+ Use this reference to look up the canonical shape of the repository's `src/` tree and its Layer Model.
4
+
5
+ ## Layer Model
6
+
7
+ This section describes the repository's five layers and their dependency direction. Use it to decide where a component or support file belongs and which layers it can import from.
8
+
9
+ | Layer | Path | Permitted contents | Imports from |
10
+ | -------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
11
+ | `app` | `src/app/` | [Next.js App Router routing files](https://nextjs.org/docs/app/getting-started/project-structure#routing-files), [metadata assets](https://nextjs.org/docs/app/getting-started/project-structure#metadata-file-conventions), and [styles required by routing files](https://nextjs.org/docs/app/getting-started/css) | compositions, features, shared, root |
12
+ | `compositions` | `src/compositions/` | Components and support files (`hooks/`, `types/`, `schemas/`, `constants/`, `utils/`) | compositions, features, shared, root |
13
+ | `features` | `src/features/<feature>/` | Components and support files (`hooks/`, `types/`, `schemas/`, `constants/`, `utils/`). `src/features/` itself holds feature folders only — never components or support folders | same feature, shared, root |
14
+ | `shared` | `src/shared/` | Components and support files (`hooks/`, `types/`, `schemas/`, `constants/`, `utils/`) | shared, root |
15
+ | `root` | `src/` | App-wide support folders: `hooks/`, `types/`, `schemas/`, `constants/`, `utils/`, `config/`, and `locales/` | root |
16
+
17
+ ## Closest Common Folder (CCF)
18
+
19
+ The CCF determines the placement of components, hooks, types, schemas, constants, and utilities. It is the closest folder shared by the files under `src/` that use the item.
20
+
21
+ ```text
22
+ src/features/payments/payment-card.tsx
23
+ src/features/payments/payment-history.tsx
24
+ └─ both import StatusBadge
25
+
26
+ CCF: src/features/payments/
27
+ Location: src/features/payments/status-badge.tsx
28
+ ```
29
+
30
+ For components, imports from `src/app/` and configuration modules do not affect the CCF. A component used by `src/app/` and a feature therefore stays with the feature.
31
+
32
+ ```text
33
+ src/app/products/page.tsx ─┐
34
+ src/features/products/product-card.tsx ─┴─ imports ProductPrice
35
+
36
+ CCF and location: src/features/products/product-price.tsx
37
+ ```
38
+
39
+ A component with no consumers outside `src/app/` or configuration modules stays in the feature it represents or supports. When no existing feature applies, it creates a feature folder.
40
+
41
+ ```text
42
+ src/app/search/page.tsx → SearchForm → src/features/search/search-form.tsx
43
+
44
+ src/app/account/page.tsx ─┐
45
+ src/app/orders/page.tsx ─┴→ AccountNav → src/features/account/account-nav.tsx
46
+ ```
47
+
48
+ When a component has consumers both inside and outside `src/compositions/`, only the consumers outside `src/compositions/` count. When all consumers are in `src/compositions/`, that folder counts.
49
+
50
+ ```text
51
+ src/compositions/checkout.tsx ─┐
52
+ src/features/payments/payment-summary.tsx ─┴→ Total → src/features/payments/total.tsx
53
+
54
+ src/compositions/checkout.tsx ─┐
55
+ src/compositions/receipt.tsx ─┴→ Total → src/compositions/total.tsx
56
+ ```
57
+
58
+ A component stays at a nested component folder, feature folder, `src/compositions/`, or `src/shared/`. A CCF of `src/features/` places it in `src/shared/`.
59
+
60
+ ```text
61
+ src/features/payments/payment-card.tsx ─┐
62
+ src/features/orders/order-card.tsx ─┴→ StatusBadge → src/shared/status-badge.tsx
63
+ ```
64
+
65
+ For a support file imported by a file under `src/app/`, use the matching root support folder, such as `src/hooks/` or `src/utils/`. Otherwise, the CCF passes through a support folder to its parent folder, and the file lives in the matching support folder directly below that parent.
66
+
67
+ ```text
68
+ src/app/products/page.tsx → useSearch → src/hooks/use-search.ts
69
+ src/features/payments/hooks/use-payment.ts → formatAmount → src/features/payments/utils/format-amount.ts
70
+ src/features/payments/payment-card.tsx ─┐
71
+ src/features/orders/order-card.tsx ─┴→ formatDate → src/utils/format-date.ts
72
+ ```
73
+
74
+ ## Application Structure
75
+
76
+ This tree shows the canonical directory layout and naming conventions for files and folders inside `src/`. It is included so readers can verify their structure matches the project standard at a glance.
77
+
78
+ ```
79
+ src/
80
+ ├── app/ # Next.js framework-convention files, assets, and route styles
81
+ ├── compositions/ # Components stay flat siblings by default
82
+ │ ├── <ComponentA>.tsx # Smart component (PascalCase-named)
83
+ │ ├── <component-b>.tsx # Dumb component (kebab-case-named)
84
+ │ ├── <component-part>.tsx # Dumb component — shared by A and B, so it stays flat too
85
+ │ ├── <NestedComponent>/ # Smart (PascalCase-named) — or kebab-case-named for dumb; nested only once it gains exclusive children
86
+ │ │ ├── index.ts # Named-re-exports only the nested parent
87
+ │ │ ├── <NestedComponent>.tsx # Smart (PascalCase-named) — or kebab-case-named for dumb
88
+ │ │ ├── <nested-part>.tsx # Exclusive child — not in index.ts
89
+ │ │ ├── hooks/ # Nested-scoped support folders
90
+ │ │ ├── types/
91
+ │ │ ├── schemas/
92
+ │ │ ├── constants/
93
+ │ │ └── utils/
94
+ │ ├── hooks/ # Composition-scoped custom hooks
95
+ │ ├── types/ # Composition-scoped TypeScript types
96
+ │ ├── schemas/ # Composition-scoped validation schemas
97
+ │ ├── constants/ # Composition-scoped constants
98
+ │ └── utils/ # Composition-scoped pure functions
99
+ ├── features/ # Feature folders only — no components, no support folders here
100
+ │ └── <feature>/ # One folder per feature — a feature can hold several components
101
+ │ ├── <ComponentA>.tsx # Smart component (PascalCase-named)
102
+ │ ├── <component-b>.tsx # Dumb component (kebab-case-named)
103
+ │ ├── <component-part>.tsx # Dumb component — shared by A and B, so it stays flat too
104
+ │ ├── <NestedComponent>/ # Smart (PascalCase-named) — or kebab-case-named for dumb; nested only once it gains exclusive children
105
+ │ │ ├── index.ts # Named-re-exports only the nested parent
106
+ │ │ ├── <NestedComponent>.tsx # Smart (PascalCase-named) — or kebab-case-named for dumb
107
+ │ │ ├── <nested-part>.tsx # Exclusive child — not in index.ts
108
+ │ │ ├── hooks/ # Nested-scoped support folders
109
+ │ │ ├── types/
110
+ │ │ ├── schemas/
111
+ │ │ ├── constants/
112
+ │ │ └── utils/
113
+ │ ├── hooks/ # Feature-scoped custom hooks
114
+ │ ├── types/ # Feature-scoped TypeScript types
115
+ │ ├── schemas/ # Feature-scoped validation schemas
116
+ │ ├── constants/ # Feature-scoped constants
117
+ │ └── utils/ # Feature-scoped pure functions
118
+ ├── shared/ # Components stay flat siblings by default
119
+ │ ├── <ComponentA>.tsx # Smart component (PascalCase-named)
120
+ │ ├── <component-b>.tsx # Dumb component (kebab-case-named)
121
+ │ ├── <component-part>.tsx # Dumb component — shared by A and B, so it stays flat too
122
+ │ ├── <NestedComponent>/ # Smart (PascalCase-named) — or kebab-case-named for dumb; nested only once it gains exclusive children
123
+ │ │ ├── index.ts # Named-re-exports only the nested parent
124
+ │ │ ├── <NestedComponent>.tsx # Smart (PascalCase-named) — or kebab-case-named for dumb
125
+ │ │ ├── <nested-part>.tsx # Exclusive child — not in index.ts
126
+ │ │ ├── hooks/ # Nested-scoped support folders
127
+ │ │ ├── types/
128
+ │ │ ├── schemas/
129
+ │ │ ├── constants/
130
+ │ │ └── utils/
131
+ │ ├── hooks/ # Shared-scoped custom hooks
132
+ │ ├── types/ # Shared-scoped TypeScript types
133
+ │ ├── schemas/ # Shared-scoped validation schemas
134
+ │ ├── constants/ # Shared-scoped constants
135
+ │ └── utils/ # Shared-scoped pure functions
136
+ ├── hooks/ # App-wide custom hooks
137
+ ├── types/ # App-wide TypeScript types
138
+ ├── schemas/ # App-wide validation schemas
139
+ ├── constants/ # App-wide constants
140
+ ├── utils/ # App-wide pure functions
141
+ ├── config/ # Configuration modules that centralize application behavior
142
+ │ └── <config-name>/ # One folder per configuration module
143
+ │ ├── index.ts # Configuration-module entry point
144
+ │ ├── types/ # Config-only TypeScript types
145
+ │ ├── schemas/ # Config-only validation schemas
146
+ │ └── utils/ # Config-only pure functions
147
+ └── locales/ # App-wide locale strings
148
+ └── index.ts # Single locale-registration file
149
+ ```
@@ -0,0 +1,119 @@
1
+ # Component Placement Rule
2
+
3
+ Without clear placement, it is hard to tell where a component belongs and reuse can create tangled dependencies. This rule gives each component a specific place in the application structure.
4
+
5
+ - A component that imports from two or more feature folders MUST live in `src/compositions/` and is, by definition, a composition.
6
+ - A component with no consumers outside `src/app/` or configuration modules, and that does not import from two or more feature folders, MUST live in the feature folder it represents or supports. If no existing feature applies, it MUST introduce a new feature folder.
7
+ - A component with at least one consumer outside `src/app/` and configuration modules MUST live in its CCF, calculated without imports from `src/app/` or configuration modules.
8
+ - When calculating a component's CCF, consumers under `src/compositions/` MUST count only when no consumer is outside `src/compositions/`.
9
+ - A component whose CCF is `src/features/` MUST live in `src/shared/`.
10
+ - `src/app/` MUST contain [Next.js App Router framework-convention files and assets](https://nextjs.org/docs/app/getting-started/project-structure#routing-files), plus styles required by routing files, but MUST NOT contain ordinary components or support folders.
11
+ ## Incorrect — Cross-Feature Component Duplicated
12
+
13
+ ```text
14
+ src/features/
15
+ ├── donation/
16
+ │ ├── DonationCard.tsx
17
+ │ └── status-badge.tsx
18
+ └── stream/
19
+ ├── StreamCard.tsx
20
+ └── status-badge.tsx
21
+ ```
22
+
23
+ Why: two feature folders contain duplicate copies because neither feature may import from the other.
24
+
25
+ ## Correct — Cross-Feature Component in `src/shared/`
26
+
27
+ ```text
28
+ src/
29
+ ├── features/
30
+ │ ├── donation/
31
+ │ │ └── DonationCard.tsx
32
+ │ └── stream/
33
+ │ └── StreamCard.tsx
34
+ └── shared/
35
+ └── status-badge.tsx
36
+ ```
37
+
38
+ Why: the component's CCF is `src/features/`, so it lives in `src/shared/`.
39
+
40
+ ## Incorrect — Multi-Feature Composition Inside One Feature
41
+
42
+ ```tsx
43
+ // src/features/billing/billing-aggregate-view.tsx
44
+ import { BillingPanel } from "./BillingPanel";
45
+ import { HomeBanner } from "@/features/home/HomeBanner";
46
+ ```
47
+
48
+ Why: the component imports from two feature folders, so it cannot live in either feature folder.
49
+
50
+ ## Correct — Multi-Feature Component in `src/compositions/`
51
+
52
+ ```tsx
53
+ // src/compositions/billing-aggregate-view.tsx
54
+ import { BillingPanel } from "@/features/billing/BillingPanel";
55
+ import { HomeBanner } from "@/features/home/HomeBanner";
56
+ ```
57
+
58
+ Why: the component imports from two feature folders, so it lives in `src/compositions/`.
59
+
60
+ ## Incorrect — Ordinary Component Under `src/app/`
61
+
62
+ ```text
63
+ src/app/contact/
64
+ ├── _components/
65
+ │ └── contact-page-content.tsx
66
+ └── page.tsx
67
+ ```
68
+
69
+ Why: the route folder contains ordinary component structure even though `src/app/` is reserved for framework-convention files, assets, and styles.
70
+
71
+ ## Correct — `src/app/` Imports a Page Composition
72
+
73
+ ```text
74
+ src/
75
+ ├── app/contact/
76
+ │ └── page.tsx
77
+ ├── compositions/
78
+ │ └── contact-page-content.tsx
79
+ └── features/
80
+ ├── contact/
81
+ │ └── contact-information.tsx
82
+ └── location/
83
+ └── office-location.tsx
84
+ ```
85
+
86
+ ```tsx
87
+ // src/compositions/contact-page-content.tsx
88
+ import { ContactInformation } from "@/features/contact/contact-information";
89
+ import { OfficeLocation } from "@/features/location/office-location";
90
+ import { locales } from "@/locales";
91
+
92
+ export function ContactPageContent(): React.JSX.Element {
93
+ return (
94
+ <main>
95
+ <header>
96
+ <h1>{locales.contactPageTitle}</h1>
97
+ <p>{locales.contactPageDescription}</p>
98
+ </header>
99
+ <section>
100
+ <h2>{locales.contactInformationHeading}</h2>
101
+ <ContactInformation />
102
+ </section>
103
+ <section>
104
+ <h2>{locales.officeLocationHeading}</h2>
105
+ <OfficeLocation />
106
+ </section>
107
+ </main>
108
+ );
109
+ }
110
+
111
+ // src/app/contact/page.tsx
112
+ import { ContactPageContent } from "@/compositions/contact-page-content";
113
+
114
+ export default function Page(): React.JSX.Element {
115
+ return <ContactPageContent />;
116
+ }
117
+ ```
118
+
119
+ Why: `src/app/` contains the routing file while the composition provides the contact page layout and combines components from two feature folders.
@@ -0,0 +1,42 @@
1
+ # Configuration Rule
2
+
3
+ Configuration modules centralize values that control application behavior. This rule keeps each module and the files that only support it together in `src/config/`.
4
+
5
+ - A configuration module is an app-wide module that selects or parameterizes application behavior.
6
+ - All configuration modules MUST live in `src/config/`.
7
+ - A configuration module MUST be one `src/config/<config-name>/` folder with `index.ts` as its entry point.
8
+ - A type, schema, or utility used only to implement one configuration module MUST be extracted even with one consumer.
9
+ - An extracted configuration type, schema, or utility MUST live in its matching dedicated folder under `src/config/<config-name>/`.
10
+ - An extracted configuration type, schema, or utility MUST move to its matching root support folder when a consumer outside its configuration module imports it.
11
+
12
+ ## Incorrect — Supporting Schema Outside Its Config Folder
13
+
14
+ ```text
15
+ src/config/
16
+ ├── home-feed/
17
+ │ └── index.ts
18
+ └── home-feed-schema.ts
19
+ ```
20
+
21
+ Why: the schema sits outside the `home-feed/` configuration folder.
22
+
23
+ ## Correct — Configuration Module with Its Supporting Schema
24
+
25
+ ```text
26
+ src/config/
27
+ └── home-feed/
28
+ ├── index.ts
29
+ └── schemas/
30
+ └── index.ts
31
+ ```
32
+
33
+ ```ts
34
+ // src/config/home-feed/index.ts
35
+ import { homeFeedConfigSchema } from "./schemas";
36
+
37
+ export const homeFeedConfig = homeFeedConfigSchema.parse({
38
+ freshVideoMaxAgeDays: 7,
39
+ });
40
+ ```
41
+
42
+ Why: the configuration module and its supporting schema are grouped in the same configuration folder.
@@ -0,0 +1,74 @@
1
+ # Constants Rule
2
+
3
+ Duplicated constants are hard to keep in sync, while extracting every single-use value creates unnecessary files. This rule keeps reused constants in one file and leaves single-use values close to their consumer.
4
+
5
+ - A value MUST remain in its declaring component or file until another file imports it independently; it MUST then be extracted as a constant.
6
+ - Extracted constants MUST live in a `constants/` folder at the closest common folder (CCF) of their consumers.
7
+ - Consumers MUST import an extracted constant through the `index.ts` in that constant's `constants/` folder.
8
+ - A `constants/` folder MUST either define its constants directly in `index.ts` or group related constants in kebab-case files that `index.ts` named-re-exports.
9
+ - When a constant's CCF is `src/features/`, it MUST move to `src/constants/`.
10
+ - A constant MAY live in `src/config/<module>/` instead of a `constants/` folder when a developer determines that it configures application behavior and is best understood alongside the configuration that parameterizes it, even when consumers exist outside the config module.
11
+
12
+ ## Incorrect — Constant Imported Without `constants/index.ts`
13
+
14
+ ```ts
15
+ // src/features/billing/constants/max-retries.ts
16
+ export const maxRetries = 3;
17
+ ```
18
+
19
+ ```ts
20
+ // src/features/billing/hooks/use-retry-payment.ts
21
+ import { maxRetries } from "../constants/max-retries";
22
+ ```
23
+
24
+ Why: the consumer bypasses the feature's `constants/index.ts`.
25
+
26
+ ## Correct — Grouped Constants Re-Exported from `constants/index.ts`
27
+
28
+ ```ts
29
+ // src/features/billing/constants/retry.ts
30
+ export const maxRetries = 3;
31
+ export const retryDelayMs = 1_000;
32
+
33
+ // src/features/billing/constants/index.ts
34
+ export { maxRetries, retryDelayMs } from "./retry";
35
+ ```
36
+
37
+ ```ts
38
+ // src/features/billing/hooks/use-retry-payment.ts
39
+ import { maxRetries } from "../constants";
40
+ ```
41
+
42
+ Why: the constants are available through the feature's `constants/index.ts`.
43
+
44
+ ## Incorrect — Feature and Composition Constant Kept in a Feature
45
+
46
+ ```ts
47
+ // src/features/billing/constants/index.ts
48
+ export const paymentRetryDelayMs = 1_000;
49
+
50
+ // src/features/billing/hooks/use-retry-payment.ts
51
+ import { paymentRetryDelayMs } from "../constants";
52
+
53
+ // src/compositions/BillingDashboard.tsx
54
+ import { paymentRetryDelayMs } from "@/features/billing/constants";
55
+ ```
56
+
57
+ Why: the feature and composition have `src/` as their CCF, but the constant remains in the billing feature.
58
+
59
+ ## Correct — Feature and Composition Constant in `src/constants/`
60
+
61
+ ```ts
62
+ // src/constants/index.ts
63
+ export const paymentRetryDelayMs = 1_000;
64
+ ```
65
+
66
+ ```tsx
67
+ // src/features/billing/hooks/use-retry-payment.ts
68
+ import { paymentRetryDelayMs } from "@/constants";
69
+
70
+ // src/compositions/BillingDashboard.tsx
71
+ import { paymentRetryDelayMs } from "@/constants";
72
+ ```
73
+
74
+ Why: the feature and composition have `src/` as their CCF, so the constant lives in `src/constants/`.
@@ -0,0 +1,84 @@
1
+ # Exports and Imports Rule
2
+
3
+ Without consistent exports, import paths, and layer boundaries, it is harder to tell what a file contains and which files may depend on it. This rule gives each file a predictable export style and keeps imports consistent with the application structure.
4
+
5
+ - A file that exports values MUST use named exports unless a framework or third-party package requires a different export style for that file.
6
+ - Imports MUST use relative paths for the same folder, a direct subfolder, or one folder up.
7
+ - Imports MUST use the `@/*` alias for anything beyond one folder up.
8
+ - A file under `src/compositions/` MUST NOT import from `src/app/`.
9
+ - A file in a feature folder MUST NOT import from another feature folder, `src/compositions/`, or `src/app/`.
10
+ - A file under `src/shared/` MUST NOT import from `src/app/`, `src/compositions/`, or a feature folder.
11
+ - A file in the `root` layer MUST NOT import from `src/app/`, `src/compositions/`, a feature folder, or `src/shared/`.
12
+ - A configuration module MUST import only from root support folders and its own files.
13
+ ## Incorrect — Single-Export Utility Uses a Default Export
14
+
15
+ ```ts
16
+ // src/utils/format-duration.ts
17
+ export default function formatDuration(seconds: number): string {
18
+ const minutes = Math.floor(seconds / 60);
19
+ const remainder = seconds % 60;
20
+ return `${minutes}:${String(remainder).padStart(2, "0")}`;
21
+ }
22
+ ```
23
+
24
+ Why: files use named exports even when they expose one item.
25
+
26
+ ## Correct — Single-Export Utility Uses a Named Export
27
+
28
+ ```ts
29
+ // src/utils/format-duration.ts
30
+ export function formatDuration(seconds: number): string {
31
+ const minutes = Math.floor(seconds / 60);
32
+ const remainder = seconds % 60;
33
+ return `${minutes}:${String(remainder).padStart(2, "0")}`;
34
+ }
35
+ ```
36
+
37
+ Why: the utility uses the same named-export form as every other file that does not have a package-required export contract.
38
+
39
+ ## Incorrect — Deep Relative Path
40
+
41
+ ```ts
42
+ // src/features/stream/StreamBoard/schedule.ts
43
+ import { debounce } from "../../../utils/debounce";
44
+ ```
45
+
46
+ Why: reaching more than one folder up hides the cross-scope dependency in relative path traversal.
47
+
48
+ ## Correct — Alias Across Scopes and Relative Paths Nearby
49
+
50
+ ```ts
51
+ // src/features/stream/StreamBoard/schedule.ts
52
+ import { debounce } from "@/utils/debounce";
53
+ import { formatSlot } from "./utils/format-slot";
54
+ import { type StreamSlot } from "./types";
55
+ import { buildSchedule } from "../schedule-builder";
56
+ ```
57
+
58
+ Why: the alias identifies the distant dependency while nearby files keep short relative paths.
59
+
60
+ ## Incorrect — Next.js Page Uses a Named Export
61
+
62
+ ```tsx
63
+ // src/app/contact/page.tsx
64
+ import { locales } from "@/locales";
65
+
66
+ export function Page(): React.JSX.Element {
67
+ return <main>{locales.contactUs}</main>;
68
+ }
69
+ ```
70
+
71
+ Why: Next.js requires a `page.tsx` file to default-export its page component.
72
+
73
+ ## Correct — Next.js Page Uses a Default Export
74
+
75
+ ```tsx
76
+ // src/app/contact/page.tsx
77
+ import { locales } from "@/locales";
78
+
79
+ export default function Page(): React.JSX.Element {
80
+ return <main>{locales.contactUs}</main>;
81
+ }
82
+ ```
83
+
84
+ Why: the page follows the export contract Next.js requires.
@@ -0,0 +1,82 @@
1
+ # Folder Nesting Rule
2
+
3
+ Without nesting, exclusive children can look reusable and their relationship to the parent is easy to miss in review. This rule groups them with their parent and keeps them out of the folder's public API.
4
+
5
+ - An exclusive child component is imported only by its parent component. A component MUST stay flat until it has one or more exclusive child components, then MUST be nested in a folder with the same name.
6
+ - A component MUST NOT be nested only because it has support files.
7
+ - A nested component's support files MUST live in its folder.
8
+ - The nested folder's `index.ts` MUST named-re-export the nested component and MUST NOT re-export its exclusive children.
9
+
10
+ ## Incorrect — Exclusive Children Kept Flat
11
+
12
+ ```text
13
+ src/features/blog/
14
+ BlogPage.tsx # owns exclusive children
15
+ blog-header.tsx # exclusive child — no sibling needs it
16
+ blog-footer.tsx # exclusive child — no sibling needs it
17
+ hooks/
18
+ use-blog-filter.ts # used only by BlogPage
19
+ ```
20
+
21
+ Why: `BlogPage` owns children that no sibling uses, but all three files sit as flat siblings. Nothing in the tree shows that `blog-header.tsx` and `blog-footer.tsx` belong to `BlogPage` rather than to any other component in the folder.
22
+
23
+ ## Correct — Exclusive Children Nested
24
+
25
+ ```text
26
+ src/features/blog/
27
+ BlogPage/
28
+ index.ts # re-exports only BlogPage.tsx
29
+ BlogPage.tsx
30
+ blog-header.tsx # not re-exported from index.ts
31
+ blog-footer.tsx # not re-exported from index.ts
32
+ hooks/
33
+ use-blog-filter.ts
34
+ ```
35
+
36
+ Why: nesting `BlogPage` into `BlogPage/` makes the parent–child relationship visible in the filesystem, lets the children stay scoped to their concrete consumer, and gives the folder a barrel that re-exports only the nested component.
37
+
38
+ ## Incorrect — Support Files Cause Unnecessary Nesting
39
+
40
+ ```text
41
+ src/features/blog/
42
+ BlogPage/
43
+ index.ts
44
+ BlogPage.tsx
45
+ hooks/
46
+ use-blog-filter.ts
47
+ ```
48
+
49
+ Why: support files alone do not make `BlogPage` a nested component.
50
+
51
+ ## Correct — Support Files Keep a Component Flat
52
+
53
+ ```text
54
+ src/features/blog/
55
+ BlogPage.tsx
56
+ hooks/
57
+ use-blog-filter.ts
58
+ ```
59
+
60
+ Why: without exclusive children, `BlogPage` stays flat and its support files remain at the feature scope.
61
+
62
+ ## Incorrect — Exclusive Child Re-Exported
63
+
64
+ ```ts
65
+ // src/features/blog/BlogPage/index.ts
66
+ export { BlogPage } from "./BlogPage";
67
+ export { BlogHeader } from "./blog-header";
68
+ ```
69
+
70
+ Why: the barrel re-exports the child as well as the nested component, so outside consumers can import `blog-header.tsx` through `index.ts` and the child stops being exclusive to `BlogPage`.
71
+
72
+ ## Correct — Only the Nested Component Re-Exported
73
+
74
+ ```tsx
75
+ // src/features/blog/BlogPage/index.ts
76
+ export { BlogPage } from "./BlogPage";
77
+
78
+ // src/features/blog/BlogPage/BlogPage.tsx
79
+ import { BlogHeader } from "./blog-header";
80
+ ```
81
+
82
+ Why: the barrel exposes only the nested component, so it does not expose the child.