@ethlete/agent-rules 0.1.0-next.11 → 0.1.0-next.12
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/CHANGELOG.md +8 -0
- package/README.md +23 -13
- package/content/hooks/context-warning.py +25 -6
- package/content/rules/lint-and-format.md +5 -4
- package/content/rules/reactive-state.md +1 -1
- package/content/rules/styling.md +8 -6
- package/content/skills/angular-patterns/SKILL.md +6 -6
- package/content/skills/api-source/SKILL.md +28 -19
- package/content/skills/figma-export/SKILL.md +16 -18
- package/content/skills/git-flow/SKILL.md +2 -2
- package/content/skills/handoff/SKILL.md +4 -4
- package/content/skills/query/SKILL.md +23 -12
- package/content/skills/rxjs-signals/SKILL.md +9 -3
- package/content/skills/sdk-docs/SKILL.md +6 -5
- package/content/skills/sdk-local-build/SKILL.md +25 -7
- package/content/skills/sdk-local-build/sdk-local-baseline.mjs +64 -0
- package/content/skills/sdk-source/SKILL.md +32 -32
- package/content/skills/story-styling/SKILL.md +3 -2
- package/content/skills/styleguide/SKILL.md +11 -6
- package/content/skills/styleguide/assets.md +54 -0
- package/content/skills/styleguide/changesets.md +17 -0
- package/content/skills/styleguide/file-structure.md +73 -0
- package/content/skills/styleguide/lint-rule-lookup.md +50 -0
- package/content/skills/styleguide/storybook-structure.md +25 -0
- package/content/skills/theming/SKILL.md +11 -11
- package/content/skills/timetrack/SKILL.md +1 -1
- package/package.json +1 -1
- package/src/lib/config.d.ts +3 -0
- package/src/lib/config.js +1 -0
- package/src/lib/config.js.map +1 -1
- package/src/lib/plan.d.ts +6 -5
- package/src/lib/plan.js +75 -12
- package/src/lib/plan.js.map +1 -1
- package/src/lib/sync.js +1 -4
- package/src/lib/sync.js.map +1 -1
- package/src/lib/targets/agents-skills.js +1 -1
- package/src/lib/targets/agents-skills.js.map +1 -1
- package/src/lib/targets/claude.js +0 -1
- package/src/lib/targets/claude.js.map +1 -1
- package/src/lib/targets/codex.js +2 -2
- package/src/lib/targets/codex.js.map +1 -1
- package/src/lib/targets/copilot.js +1 -1
- package/src/lib/targets/copilot.js.map +1 -1
- package/src/lib/targets/cursor.js +1 -1
- package/src/lib/targets/cursor.js.map +1 -1
- package/src/lib/targets/shared.d.ts +1 -8
- package/src/lib/targets/shared.js +2 -7
- package/src/lib/targets/shared.js.map +1 -1
- package/content/skills/styleguide/STYLEGUIDE.md +0 -520
|
@@ -1,520 +0,0 @@
|
|
|
1
|
-
# Style Guide v0.21.0
|
|
2
|
-
|
|
3
|
-
This document outlines the coding style guide for Angular applications at Braune Digital.
|
|
4
|
-
|
|
5
|
-
**This guide is a work in progress and will be updated regularly.**
|
|
6
|
-
|
|
7
|
-
> **Enforcement:** most rules below are enforced automatically by
|
|
8
|
-
> `@ethlete/eslint-plugin` - run your lint task with `--fix` and fix what it reports,
|
|
9
|
-
> rather than hand-checking. The `styleguide` guide that ships alongside this file
|
|
10
|
-
> distills the judgment calls lint can't check.
|
|
11
|
-
|
|
12
|
-
## TL;DR
|
|
13
|
-
|
|
14
|
-
Key standards at a glance. **Most are enforced by lint** (see [Enforced by lint](#enforced-by-lint)); the rest are judgment calls documented in the sections below. Not exhaustive.
|
|
15
|
-
|
|
16
|
-
- **Types**: `unknown` not `any`; `type` not `interface`; `as const` objects not `enum`; `T`-prefixed descriptive generics; regular value imports (no `import type`); narrow with type guards.
|
|
17
|
-
- **Code**: `const` by default (`let` only to reassign, never `var`); one declaration per statement; `===` / `!==`; arrow fns standalone, methods in classes; max two params (object param beyond that).
|
|
18
|
-
- **State**: signals for synchronous state, RxJS for async - always unsubscribe; effects for signal-driven side effects.
|
|
19
|
-
- **Angular**: `ViewEncapsulation.None`; `inject()` not constructor injection; no legacy lifecycle hooks - prefer `constructor` + `afterNextRender` + `DestroyRef.onDestroy`; no function calls in templates except signal reads.
|
|
20
|
-
- **Naming & structure**: name things after what they do; routing components end in `-view`; mirror routes in folders; keep related files together.
|
|
21
|
-
- **Changesets**: one focused, imperative-mood entry per change (see the `changeset` skill).
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Enforced by lint
|
|
26
|
-
|
|
27
|
-
Run your lint task with `--fix` - the rules below are enforced (and mostly auto-fixed) by `@ethlete/eslint-plugin`. This table is a lookup for _why_ a fix was applied; don't hand-check these.
|
|
28
|
-
|
|
29
|
-
| Rule | Enforced by |
|
|
30
|
-
| --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
-
| No `any` / `$any()`; use `unknown` + type guards | `@typescript-eslint/no-explicit-any`, `@angular-eslint/template/no-any` |
|
|
32
|
-
| No property bindings for static strings/booleans (`[etIcon]="'foo'"`, `[isReadonly]="true"`) - use static attributes | `@angular-eslint/template/prefer-static-string-properties`, `ethlete/prefer-static-boolean-properties` |
|
|
33
|
-
| No `interface` - use `type` | `@typescript-eslint/consistent-type-definitions` |
|
|
34
|
-
| No `enum` - use an `as const` object + derived union | `no-restricted-syntax` |
|
|
35
|
-
| No `var`; prefer `const`; one declaration per statement | `no-var`, `prefer-const`, `one-var` |
|
|
36
|
-
| `===` / `!==` only | `eqeqeq` |
|
|
37
|
-
| Max two function parameters | `max-params` |
|
|
38
|
-
| No `import type` / inline `type` specifiers | `ethlete/no-type-only-import` |
|
|
39
|
-
| Generic params `T`-prefixed (`TValue`), never bare `T` | `@typescript-eslint/naming-convention` |
|
|
40
|
-
| No `async`/`await` - use RxJS | `ethlete/no-async-await` |
|
|
41
|
-
| Arrow fns standalone; methods in classes; no arrow-fn class props; no `function` keyword | `no-restricted-syntax` |
|
|
42
|
-
| Blank line before `return` in multi-line guard clauses | `ethlete/guard-return-newline` |
|
|
43
|
-
| No trivially-inferable explicit return types | `ethlete/no-trivial-return-type` |
|
|
44
|
-
| camelCase / PascalCase / UPPER*CASE; no `#` or `*` member prefixes | `@typescript-eslint/naming-convention`, `ethlete/no-leading-underscore-class-member` |
|
|
45
|
-
| No SCREAMING_CASE locals; class constants `readonly` + SCREAMING_CASE | `ethlete/no-screaming-case-local`, `ethlete/class-constant-property` |
|
|
46
|
-
| No `readonly` on reactive members (signals, inputs, computed, inject) | `ethlete/no-readonly-signal` |
|
|
47
|
-
| No `static` members (except `ngTemplateContextGuard`, which Angular requires to be static) | `no-restricted-syntax` |
|
|
48
|
-
| Injected providers `private` by default, `protected` only when template/host-visible; explicit accessibility on reachable members | `ethlete/inject-member-accessibility`, `ethlete/template-member-accessibility` |
|
|
49
|
-
| No `inject(X).member` chaining; no pure member aliases | `ethlete/no-inject-chain`, `ethlete/no-member-alias` |
|
|
50
|
-
| No module-scope destructuring of a factory call; `@__PURE__` on module-scope calls in library source | `ethlete/no-impure-top-level-provider` |
|
|
51
|
-
| No redundant `@internal` on `private`/`protected` members | `ethlete/no-redundant-internal` |
|
|
52
|
-
| Observable vars/props end with `$` | `ethlete/require-dollar-suffix` |
|
|
53
|
-
| No body in `subscribe()`; no `subscribe` in `pipe()`; no RxJS in `effect()`/`computed()` | `ethlete/no-subscribe-with-body`, `ethlete/no-subscribe-in-pipe`, `ethlete/no-rxjs-in-effect` |
|
|
54
|
-
| Effect teardown goes through `onCleanup` (or `DestroyRef`), never a returned cleanup function | `ethlete/no-effect-cleanup-return` |
|
|
55
|
-
| `ViewEncapsulation.None` | `ethlete/require-view-encapsulation-none` |
|
|
56
|
-
| No legacy lifecycle hooks; no legacy Angular decorators (`@HostBinding`, `@Input`, …) | `no-restricted-syntax`, `ethlete/no-legacy-angular-decorators` |
|
|
57
|
-
| No `@Injectable` / `@Service`; no route guards; no resolvers | `no-restricted-syntax` |
|
|
58
|
-
| Outputs: no `on` prefix, no native event names, present-tense naming (`playerSelect`, not `playerSelected`) | `@angular-eslint/no-output-on-prefix`, `@angular-eslint/no-output-native`, `ethlete/prefer-present-tense-output` |
|
|
59
|
-
| Inputs/models not named after a global HTML attribute (`title`, `id`, `hidden`, `role`, …) - collides with the host element | `ethlete/no-native-html-input-name` |
|
|
60
|
-
| No logic in pipe `transform` | `ethlete/no-pipe-logic` |
|
|
61
|
-
| Consistent class-member + decorator-metadata order; concise host-directive / style metadata | `ethlete/class-member-order`, `ethlete/angular-decorator-property-order`, `ethlete/prefer-concise-angular-host-directives`, `ethlete/prefer-concise-angular-style-metadata` |
|
|
62
|
-
| No interpolated template literal above an inline `template:` - it kills Angular language service completions in that file | `ethlete/no-template-literal-before-inline-template` |
|
|
63
|
-
| Routing components: `-view` path + `ViewComponent` class name | `ethlete/enforce-routing-view-naming` |
|
|
64
|
-
| No direct `document` / `window` / DOM query / observers / cookies / `window.location` | `no-restricted-globals`, `ethlete/no-direct-dom-manipulation`, `ethlete/no-dom-query`, `ethlete/no-native-observers`, `ethlete/no-document-cookie`, `ethlete/no-window-location` |
|
|
65
|
-
| No barrel (index) imports - import from the source file | `no-restricted-syntax` |
|
|
66
|
-
| Prefer `@ethlete/core` utils over raw APIs (clone/equal, rxjs timers, media query, viewport size, SEO, locale, router state) | `ethlete/prefer-clone-equal`, `ethlete/prefer-rxjs-timer`, `ethlete/prefer-match-media`, `ethlete/prefer-viewport-size`, `ethlete/no-angular-seo-services`, `ethlete/no-locale-id`, `ethlete/no-angular-router-api` |
|
|
67
|
-
|
|
68
|
-
## Accessibility & visibility
|
|
69
|
-
|
|
70
|
-
Lint auto-fixes injected providers to `private` and flags template/host-visible members, but the _intent_ is yours:
|
|
71
|
-
|
|
72
|
-
- Injected provider → `private` by default; `protected` **only** when referenced from the HTML template or a `host:` binding expression; **drop the modifier entirely** if keeping it `private` would force a member alias (a property whose sole purpose is re-exposing a nested member - expose the injected symbol directly instead).
|
|
73
|
-
- Never add a member that only **aliases** another member's nested property (`foo = this.thing.foo`) - widen the source member's visibility and use it directly.
|
|
74
|
-
- For a member that must stay technically public purely for cross-class/DI use (e.g. a self-registration method called by a sub-directive), keep it `public` and tag `/** @internal */` so build tooling strips it from the published `.d.ts`. Never put `@internal` on `private`/`protected` members.
|
|
75
|
-
|
|
76
|
-
## Naming & functions with intent
|
|
77
|
-
|
|
78
|
-
Case conventions, the `T` generic prefix, arrow-vs-method, and the two-param limit are all lint-enforced. What lint can't judge:
|
|
79
|
-
|
|
80
|
-
- **Name things after what they do**, not after the mechanism. `onChange` that posts a form → `sendFormValueToApi`. Descriptive generics (`TValue`, `TResult`), descriptive constant/variable names.
|
|
81
|
-
- When a function needs more than two parameters, take a single **object parameter** with a named `type` rather than widening the signature.
|
|
82
|
-
|
|
83
|
-
```ts
|
|
84
|
-
// ❌ name describes the trigger, not the behaviour
|
|
85
|
-
const onChange = () => sendToApi(myForm.getRawValue());
|
|
86
|
-
|
|
87
|
-
// ✅ name describes the behaviour
|
|
88
|
-
const sendFormValueToApi = () => sendToApi(myForm.getRawValue());
|
|
89
|
-
|
|
90
|
-
// ✅ object param instead of a third positional arg
|
|
91
|
-
type LogMessageConfig = { scope: string; logLevel: string };
|
|
92
|
-
const logMessage = (message: string, config: LogMessageConfig) => {};
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## TypeScript Config
|
|
96
|
-
|
|
97
|
-
- Ensure `strict` is set to `true`.
|
|
98
|
-
- Ensure `noUncheckedIndexedAccess` is set to `true`.
|
|
99
|
-
- Keep the remaining defaults provided by NX
|
|
100
|
-
- Set `resolveJsonModule` to `true` only if necessary. JSON files should be fetched via HTTP requests.
|
|
101
|
-
- Set `esModuleInterop` to `true` only if necessary.
|
|
102
|
-
|
|
103
|
-
## Signals vs RxJS
|
|
104
|
-
|
|
105
|
-
- **Synchronous state → signals. Asynchronous work → RxJS.** Never model sync state with a `BehaviorSubject`; never use RxJS just to read a value back synchronously. Bridge with `toSignal()` / `toObservable()` instead of copying values across with `.subscribe()`.
|
|
106
|
-
- **Always unsubscribe.** Prefer `takeUntilDestroyed()`; otherwise `take` / `takeUntil` / `takeWhile`, and place the limiting operator **last** in the pipe. Side effects go in `tap()`, not the `subscribe()` callback. (Lint blocks bodies in `subscribe()` but cannot prove you unsubscribe.)
|
|
107
|
-
- Don't reach for RxJS inside `effect()` / `computed()` - model the stream with `toObservable(signal).pipe(switchMap(...))` instead of subscribing per run.
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
// ❌ sync state as a subject // ✅ signal
|
|
111
|
-
const count$ = new BehaviorSubject(0);
|
|
112
|
-
const count = signal(0);
|
|
113
|
-
|
|
114
|
-
// ✅ react to a signal without subscribing inside an effect
|
|
115
|
-
toObservable(page)
|
|
116
|
-
.pipe(
|
|
117
|
-
switchMap((p) => fetchPage(p)),
|
|
118
|
-
tap(handle),
|
|
119
|
-
takeUntilDestroyed(),
|
|
120
|
-
)
|
|
121
|
-
.subscribe();
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
## Angular patterns
|
|
125
|
-
|
|
126
|
-
Lint covers the mechanical Angular rules (`ViewEncapsulation.None`, no legacy hooks/decorators, no native DOM/`window`, output naming, class-member + decorator-metadata order, no `@Injectable` / `@Service` / guards / resolvers). The judgment calls:
|
|
127
|
-
|
|
128
|
-
- **No function calls in template value bindings except signal reads** - a method in a binding re-runs every change-detection cycle. Move it into a `computed()` and bind that. Event bindings (`(click)="save()"`) are fine.
|
|
129
|
-
- **Prefer the `constructor`** (runs in the injection context) over `ngOnInit` / `ngOnDestroy`: `afterNextRender()` for first-render work, `inject(DestroyRef).onDestroy(...)` for cleanup.
|
|
130
|
-
- **Prefer utility functions + provider factories over services** - `createProvider` / `createRootProvider` and the `injectX()` helper pattern from `@ethlete/core`, not an `@Injectable` (or its Angular 22 `@Service` shorthand).
|
|
131
|
-
- **Most directives can be plain functions.** Move the logic into a function so it's reusable without applying a directive; keep a directive only when a host element genuinely needs it. Avoid common input/output names that clash with the host component.
|
|
132
|
-
- **Pipes carry no logic** - put it in a utility function called from a `computed()`; most pipes can be replaced by a `computed` outright.
|
|
133
|
-
- **Components**: inline template/styles for small components, external `.html` / `.css` files for complex ones.
|
|
134
|
-
|
|
135
|
-
```html
|
|
136
|
-
<!-- ❌ runs every CD cycle -->
|
|
137
|
-
<button [disabled]="isDisabled()">
|
|
138
|
-
<!-- ✅ computed signal -->
|
|
139
|
-
<button [disabled]="disabled()"></button>
|
|
140
|
-
</button>
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
## General File Structure
|
|
144
|
-
|
|
145
|
-
Given are the following routes:
|
|
146
|
-
|
|
147
|
-
```ts
|
|
148
|
-
import { Routes } from '@angular/router';
|
|
149
|
-
|
|
150
|
-
export const SHOP_ROUTES: Routes = [
|
|
151
|
-
{
|
|
152
|
-
path: 'items',
|
|
153
|
-
loadComponent: () => import('./items-list-view/items-list-view.component').then((m) => m.ItemsListViewComponent),
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
path: 'items/:id',
|
|
157
|
-
loadComponent: () => import('./item-detail-host-view/item-detail-host-view.component').then((m) => m.ItemDetailHostViewComponent),
|
|
158
|
-
children: [
|
|
159
|
-
{
|
|
160
|
-
path: '',
|
|
161
|
-
loadComponent: () => import('./item-detail-host-view/item-detail-view/item-detail-view.component').then((m) => m.ItemDetailViewComponent),
|
|
162
|
-
}
|
|
163
|
-
{
|
|
164
|
-
path: 'reviews',
|
|
165
|
-
loadComponent: () => import('./item-detail-host-view/item-reviews-view/item-reviews-view.component').then((m) => m.ItemReviewsViewComponent),
|
|
166
|
-
}
|
|
167
|
-
]
|
|
168
|
-
}
|
|
169
|
-
];
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
- Organize folder and file structure to mirror Angular routes.
|
|
173
|
-
- Name routing components with the suffix `view` (e.g., `settings-view.component.ts`).
|
|
174
|
-
- Place reusable components in a `components` directory.
|
|
175
|
-
- Always include an `index.ts` file to export components.
|
|
176
|
-
- For components with inline templates, place them directly in the `components` directory without nesting.
|
|
177
|
-
- Place child components specific to a parent component in a `partials` directory.
|
|
178
|
-
- Restrict the usage of partial components to their parent component only. For example, the `item-image` and `item-price` components should only be used within the `item-card` component.
|
|
179
|
-
- Similarly, components like `item-card` should only be used within their parent view (`items-list-view`). If needed elsewhere, move them higher in the directory structure.
|
|
180
|
-
- Create and place directives, pipes, and utilities in the same directory as the component that uses them. For example, place `item-status.pipe.ts` in the `item-card` directory.
|
|
181
|
-
- Position services and providers in the directory of the view component that includes them in its `providers` array. For example, place `item-detail-api.service.ts` in the `item-detail-host-view` directory.
|
|
182
|
-
- Use simple file names for utility files without the `.utils.ts` suffix. For example, use `items-list-filter-form.ts` for files containing form configurations.
|
|
183
|
-
- Always put storybook files in a `storybook` directory within the component directory. This directory should contain the storybook component and any dummy data needed for the storybook.
|
|
184
|
-
|
|
185
|
-
```plaintext
|
|
186
|
-
shop/
|
|
187
|
-
├── items-list-view/
|
|
188
|
-
│ ├── components/
|
|
189
|
-
│ │ ├── item-card/
|
|
190
|
-
│ │ │ ├── partials/
|
|
191
|
-
│ │ │ │ ├── item-image/
|
|
192
|
-
│ │ │ │ │ ├── item-image.component.ts
|
|
193
|
-
│ │ │ │ │ ├── item-image.component.html
|
|
194
|
-
│ │ │ │ │ ├── index.ts ✅ (exports the item image component)
|
|
195
|
-
│ │ │ │ ├── item-price/
|
|
196
|
-
│ │ │ │ │ ├── item-price.component.ts
|
|
197
|
-
│ │ │ │ │ ├── item-price.component.html
|
|
198
|
-
│ │ │ │ │ ├── index.ts ✅ (exports the item price component)
|
|
199
|
-
│ │ │ ├── storybook/
|
|
200
|
-
│ │ │ │ ├── item-card.component.stories.ts
|
|
201
|
-
│ │ │ │ ├── item-card-storybook-data.ts
|
|
202
|
-
│ │ │ ├── item-card.component.ts
|
|
203
|
-
│ │ │ ├── item-card.component.html
|
|
204
|
-
│ │ │ ├── item-status.pipe.ts
|
|
205
|
-
│ │ │ ├── index.ts ✅ (exports the item card component)
|
|
206
|
-
│ ├── items-list-view.component.ts
|
|
207
|
-
│ ├── items-list-view.component.html
|
|
208
|
-
│ ├── items-list-filter-form.ts
|
|
209
|
-
├── items-detail-host-view/
|
|
210
|
-
│ ├── item-detail-host-view.component.ts
|
|
211
|
-
│ ├── item-detail-host-view.component.html
|
|
212
|
-
│ ├── item-detail-api.service.ts
|
|
213
|
-
│ ├── item-data.provider.ts
|
|
214
|
-
│ ├── item-detail-view/
|
|
215
|
-
│ │ ├── item-detail-view.component.ts
|
|
216
|
-
│ │ ├── item-detail-view.component.html
|
|
217
|
-
│ ├── item-reviews-view/
|
|
218
|
-
│ │ ├── item-reviews-view.component.ts
|
|
219
|
-
│ │ ├── item-reviews-view.component.html
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
#### Miscellaneous
|
|
223
|
-
|
|
224
|
-
- Super generic components and other logic (e.g., buttons, inputs, etc.) should be placed in a uikit directory.
|
|
225
|
-
- Things placed in the uikit directory should be as generic as possible and should not contain any business logic (dumb components).
|
|
226
|
-
- Components and logic needed for the app shell (e.g., header, footer, etc.) should be placed in a shell directory.
|
|
227
|
-
- To reduce the risk of circular dependencies, avoid importing from the parent directory in a subdirectory.
|
|
228
|
-
|
|
229
|
-
### NX Workspace
|
|
230
|
-
|
|
231
|
-
- Apps should be placed in the `apps` directory. They should contain the main application logic and should be as slim as possible. No business logic should be placed in the app directory besides the app component.
|
|
232
|
-
- Libraries should be placed in the `libs` directory.
|
|
233
|
-
- They should be `buildable`.
|
|
234
|
-
- They should have a clear import path (e.g., `@org/domain/my-app` or `@org/uikit`). The import path can be found in the project.json file and should be checked after generation.
|
|
235
|
-
- They should have a clear name (e.g. `domain-my-app` or `uikit`). The name can be found in the project.json file and should also be checked after generation.
|
|
236
|
-
|
|
237
|
-
The following abstract example shows a correct file structure:
|
|
238
|
-
|
|
239
|
-
```plaintext
|
|
240
|
-
apps/
|
|
241
|
-
│ ├── my-app/
|
|
242
|
-
│ │ ├── src/...
|
|
243
|
-
│ ├── other-app/
|
|
244
|
-
│ │ ├── src/...
|
|
245
|
-
libs/
|
|
246
|
-
│ ├── assets/
|
|
247
|
-
│ │ ├── src/...
|
|
248
|
-
│ ├── domain/
|
|
249
|
-
│ │ ├── my-app/
|
|
250
|
-
│ │ │ ├── src/...
|
|
251
|
-
│ │ ├── other-app/
|
|
252
|
-
│ │ │ ├── src/...
|
|
253
|
-
│ ├── env/
|
|
254
|
-
| │ ├── src/...
|
|
255
|
-
│ ├── queries/
|
|
256
|
-
│ │ ├── src/...
|
|
257
|
-
│ ├── types/
|
|
258
|
-
│ │ ├── src/...
|
|
259
|
-
│ ├── uikit/
|
|
260
|
-
│ │ ├── src/...
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
- The `assets` library should contain all assets used across applications.
|
|
264
|
-
- The `domain` library should contain all domain-specific logic. Each domain should have its own library.
|
|
265
|
-
- The `env` library should contain the `environment` files. This way they can be shared across applications and libraries.
|
|
266
|
-
- The `queries` library should contain all queries used across applications.
|
|
267
|
-
- The `types` library should contain common types used across applications and libraries (e.g. API types).
|
|
268
|
-
- The `uikit` library should contain all shared components and logic.
|
|
269
|
-
|
|
270
|
-
### Assets
|
|
271
|
-
|
|
272
|
-
- Place all assets in the `assets` library.
|
|
273
|
-
- Given the apps `my-app` and `other-app`, the assets library should be structured as follows:
|
|
274
|
-
|
|
275
|
-
```plaintext
|
|
276
|
-
assets/
|
|
277
|
-
│ ├── my-app/
|
|
278
|
-
│ │ ├── build/
|
|
279
|
-
│ │ ├── serve/
|
|
280
|
-
│ │ ├── storybook/
|
|
281
|
-
│ ├── other-app/
|
|
282
|
-
│ │ ├── build/
|
|
283
|
-
│ │ ├── serve/
|
|
284
|
-
│ │ ├── storybook/
|
|
285
|
-
│ ├── shared/
|
|
286
|
-
│ │ ├── build/
|
|
287
|
-
│ │ ├── serve/
|
|
288
|
-
│ │ ├── storybook/
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
- The storybook directory is optional and can be used for assets that are only needed in Storybook (e.g., dummy data, storybook-specific assets).
|
|
292
|
-
- The serve directory is optional and can be used for assets that are only needed during development (e.g., placeholder assets). These assets are not included in the build process.
|
|
293
|
-
- The build directory should contain all assets that are needed in production.
|
|
294
|
-
- Adjust each app's `project.json` file to include the assets as follows:
|
|
295
|
-
|
|
296
|
-
```json
|
|
297
|
-
{
|
|
298
|
-
"build": {
|
|
299
|
-
"executor": "@angular/build:application",
|
|
300
|
-
"outputs": ["{options.outputPath}"],
|
|
301
|
-
"options": {
|
|
302
|
-
// ...
|
|
303
|
-
"assets": [
|
|
304
|
-
{
|
|
305
|
-
"input": "libs/assets/src/APP_NAME_HERE/build",
|
|
306
|
-
"glob": "**/*",
|
|
307
|
-
"output": "assets" // There should be no separate build directory for build assets.
|
|
308
|
-
},
|
|
309
|
-
{
|
|
310
|
-
"input": "libs/assets/src/APP_NAME_HERE/serve",
|
|
311
|
-
"glob": "**/*",
|
|
312
|
-
"output": "assets/serve"
|
|
313
|
-
},
|
|
314
|
-
// If shared assets are needed, include them as well
|
|
315
|
-
{
|
|
316
|
-
"input": "libs/assets/src/shared/build",
|
|
317
|
-
"glob": "**/*",
|
|
318
|
-
"output": "assets/shared"
|
|
319
|
-
},
|
|
320
|
-
{
|
|
321
|
-
"input": "libs/assets/src/shared/serve",
|
|
322
|
-
"glob": "**/*",
|
|
323
|
-
"output": "assets/shared/serve"
|
|
324
|
-
}
|
|
325
|
-
]
|
|
326
|
-
// ...
|
|
327
|
-
},
|
|
328
|
-
"configurations": {
|
|
329
|
-
"production": {
|
|
330
|
-
"assets": [
|
|
331
|
-
{
|
|
332
|
-
"input": "libs/assets/src/APP_NAME_HERE/build",
|
|
333
|
-
"glob": "**/*",
|
|
334
|
-
"output": "assets"
|
|
335
|
-
},
|
|
336
|
-
// If shared assets are needed, include them as well
|
|
337
|
-
{
|
|
338
|
-
"input": "libs/assets/src/shared/build",
|
|
339
|
-
"glob": "**/*",
|
|
340
|
-
"output": "assets/shared"
|
|
341
|
-
}
|
|
342
|
-
]
|
|
343
|
-
},
|
|
344
|
-
// If you have a Storybook setup, you can add configurations for development and production environments.
|
|
345
|
-
// Make sure to add zone.js as a polyfill. This is currently still required for Storybook to work properly.
|
|
346
|
-
"storybook-development": {
|
|
347
|
-
"polyfills": ["zone.js"],
|
|
348
|
-
"assets": [
|
|
349
|
-
{
|
|
350
|
-
"input": "libs/assets/src/APP_NAME_HERE/serve",
|
|
351
|
-
"glob": "**/*",
|
|
352
|
-
"output": "assets/serve"
|
|
353
|
-
},
|
|
354
|
-
{
|
|
355
|
-
"input": "libs/assets/src/APP_NAME_HERE/storybook",
|
|
356
|
-
"glob": "**/*",
|
|
357
|
-
"output": "assets"
|
|
358
|
-
},
|
|
359
|
-
{
|
|
360
|
-
"input": "libs/assets/src/APP_NAME_HERE/build",
|
|
361
|
-
"glob": "**/*",
|
|
362
|
-
"output": "assets"
|
|
363
|
-
},
|
|
364
|
-
// If shared assets are needed, include them as well
|
|
365
|
-
{
|
|
366
|
-
"input": "libs/assets/src/shared/build",
|
|
367
|
-
"glob": "**/*",
|
|
368
|
-
"output": "assets/shared"
|
|
369
|
-
},
|
|
370
|
-
{
|
|
371
|
-
"input": "libs/assets/src/shared/serve",
|
|
372
|
-
"glob": "**/*",
|
|
373
|
-
"output": "assets/shared/serve"
|
|
374
|
-
},
|
|
375
|
-
{
|
|
376
|
-
"input": "libs/assets/src/shared/storybook",
|
|
377
|
-
"glob": "**/*",
|
|
378
|
-
"output": "assets/shared/storybook"
|
|
379
|
-
}
|
|
380
|
-
]
|
|
381
|
-
},
|
|
382
|
-
"storybook-production": {
|
|
383
|
-
"polyfills": ["zone.js"],
|
|
384
|
-
"assets": [
|
|
385
|
-
{
|
|
386
|
-
"input": "libs/assets/src/APP_NAME_HERE/storybook",
|
|
387
|
-
"glob": "**/*",
|
|
388
|
-
"output": "assets"
|
|
389
|
-
},
|
|
390
|
-
{
|
|
391
|
-
"input": "libs/assets/src/APP_NAME_HERE/build",
|
|
392
|
-
"glob": "**/*",
|
|
393
|
-
"output": "assets"
|
|
394
|
-
},
|
|
395
|
-
// If shared assets are needed, include them as well
|
|
396
|
-
{
|
|
397
|
-
"input": "libs/assets/src/shared/build",
|
|
398
|
-
"glob": "**/*",
|
|
399
|
-
"output": "assets/shared"
|
|
400
|
-
},
|
|
401
|
-
{
|
|
402
|
-
"input": "libs/assets/src/shared/storybook",
|
|
403
|
-
"glob": "**/*",
|
|
404
|
-
"output": "assets/shared/storybook"
|
|
405
|
-
}
|
|
406
|
-
]
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
#### Asset examples
|
|
414
|
-
|
|
415
|
-
- Given the `cat.jpg` image used in the `my-app` application, the asset should be placed in the `libs/assets/src/my-app/build` directory.
|
|
416
|
-
- Subdirectories can be used to organize assets further, such as `libs/assets/src/my-app/build/images/cat.jpg`.
|
|
417
|
-
|
|
418
|
-
```plaintext
|
|
419
|
-
assets/
|
|
420
|
-
│ ├── my-app/
|
|
421
|
-
│ │ ├── build/
|
|
422
|
-
│ │ │ ├── images/
|
|
423
|
-
│ │ │ │ ├── cat.jpg
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
This way, the asset can be accessed in the application as follows:
|
|
427
|
-
|
|
428
|
-
```html
|
|
429
|
-
<img src="assets/images/cat.jpg" alt="Cat Image" />
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
- If the asset is only needed during development (e.g., as a mock placeholder image)
|
|
433
|
-
- Place it in the `libs/assets/src/my-app/serve/images/cat.jpg`.
|
|
434
|
-
- The asset can then be accessed in the application as follows:
|
|
435
|
-
|
|
436
|
-
```html
|
|
437
|
-
<img src="assets/serve/images/cat.jpg" alt="Cat Image" />
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
**Keep in mind** that the `serve` directory is optional and can be used for assets that are only needed during development. These assets are not included in the build process.
|
|
441
|
-
|
|
442
|
-
### Storybook
|
|
443
|
-
|
|
444
|
-
- If extra components are needed to render a component in Storybook, they should be placed in a `storybook` directory within the component directory.
|
|
445
|
-
- **Never** export storybook specific logic from the component directory.
|
|
446
|
-
|
|
447
|
-
```plaintext
|
|
448
|
-
settings-form/
|
|
449
|
-
│ ├── storybook/
|
|
450
|
-
│ │ ├── settings-form.storybook.component.ts
|
|
451
|
-
│ │ ├── settings-form.storybook.component.html
|
|
452
|
-
│ │ ├── settings-form-dummy-data.ts
|
|
453
|
-
│ │ ├── index.ts ✅ (exports the storybook component for use in the .stories.ts file + dummy data if needed)
|
|
454
|
-
│ ├── settings-form.component.ts
|
|
455
|
-
│ ├── settings-form.component.stories.ts
|
|
456
|
-
│ ├── settings-form.component.html
|
|
457
|
-
│ ├── index.ts ✅ (exports the form component)
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
## Changesets
|
|
461
|
-
|
|
462
|
-
- Use `@changesets` to manage changelogs.
|
|
463
|
-
- **Do not** create a changeset for irrelevant changes (e.g., formatting, comments, internal refactoring).
|
|
464
|
-
- **Do not** create a changeset for fixes to features that have not yet been released.
|
|
465
|
-
- **Do not** include multiple changes in a single changeset. Each changeset should contain only one change.
|
|
466
|
-
- **A changeset note is a TL;DR: one sentence, two at most, under 40 words.** Never a second paragraph, no matter how much work the change took. It is the line a consumer skims to decide whether the release affects them - not a summary of your work. Mechanism, API inventories, caveats and rationale belong in the docs or the commit body, never here.
|
|
467
|
-
- Create changesets for dependency updates if they are relevant to the project (e.g., major version updates).
|
|
468
|
-
- Write changesets in the imperative mood. For example:
|
|
469
|
-
- Add button component
|
|
470
|
-
- Fix spacing issues inside buttons
|
|
471
|
-
- Update to Angular 20
|
|
472
|
-
|
|
473
|
-
### Examples
|
|
474
|
-
|
|
475
|
-
Use the following legend to determine the type of changeset you should create.
|
|
476
|
-
**Do not** include the emoji in your changeset message; it is only used here for clarity.
|
|
477
|
-
|
|
478
|
-
- ✨ **Major Change**: For breaking changes that require updates or modifications by consumers of the project.
|
|
479
|
-
- Example: Remove settings view from the app.
|
|
480
|
-
|
|
481
|
-
- 🚀 **Minor Change**: For adding new features or functionality in a backward-compatible way.
|
|
482
|
-
- Example: Add support for dark mode in components.
|
|
483
|
-
|
|
484
|
-
- 🐛 **Patch Change**: For bug fixes or small adjustments that do not introduce breaking changes.
|
|
485
|
-
- Example: Fix button alignment issue.
|
|
486
|
-
|
|
487
|
-
#### Valid Changesets
|
|
488
|
-
|
|
489
|
-
The following changesets are valid and should be created:
|
|
490
|
-
|
|
491
|
-
- ✨ Migrate to NX 20
|
|
492
|
-
- 🚀 Add button component
|
|
493
|
-
- 🚀 Add text input component
|
|
494
|
-
- 🚀 Add settings view
|
|
495
|
-
- 🚀 Add uikit library
|
|
496
|
-
- 🚀 Add login app
|
|
497
|
-
- 🚀 Update TypeScript configurations to allow usage of ES2027
|
|
498
|
-
- 🚀 Make CI pipelines faster by caching `node_modules`
|
|
499
|
-
|
|
500
|
-
#### Special Cases
|
|
501
|
-
|
|
502
|
-
For these types of changesets, ensure that the feature you are working on has already been released (and can be found in the changelog). If the feature is not yet released, **do not** create a changeset for it.
|
|
503
|
-
|
|
504
|
-
- ✨ Change route of settings view from `/settings` to `/user/settings`
|
|
505
|
-
- ✨ Rename `MatchComponent` to `MatchupComponent`
|
|
506
|
-
- 🚀 Add general tab to settings view
|
|
507
|
-
- 🐛 Fix issue with settings view not loading on mobile devices
|
|
508
|
-
- 🐛 Enhance button component rendering to improve performance
|
|
509
|
-
- 🐛 Fix typo in settings view headline
|
|
510
|
-
- 🐛 Fix linting issues inside progress bar component
|
|
511
|
-
|
|
512
|
-
#### Invalid Changesets
|
|
513
|
-
|
|
514
|
-
The following changesets are generally invalid and should **not** be created:
|
|
515
|
-
|
|
516
|
-
- Cleanup code inside button component (no changeset needed).
|
|
517
|
-
- Update Angular to 19.1.1 from 19.1.0 (it's a patch update and does not require a changeset).
|
|
518
|
-
- Move button component to a new directory (if it remains in the same NX library, no changeset is needed. Otherwise, it's a ✨).
|
|
519
|
-
- Run Prettier on all files (no changeset needed).
|
|
520
|
-
- Fix button style on hover **and** update slider component bar thickness (two changes should not be combined into one changeset).
|