fg-devkit 1.22.0 → 1.24.0
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 +30 -7
- package/configs/dependency-policy.vue-workspace.json +28 -28
- package/package.json +12 -85
- package/scaffold/vue/package.json +13 -13
- package/scripts/audit-override-commit.mjs +167 -24
- package/scripts/bump-node-toolchain.mjs +8 -31
- package/scripts/bump-pnpm-toolchain.mjs +8 -29
- package/scripts/check-dependency-policy.mjs +10 -35
- package/scripts/fg-devkit.mjs +290 -337
- package/scripts/fleet-bump-fg-devkit.mjs +4 -5
- package/scripts/fleet-git-commit-push-scoped.mjs +6 -26
- package/scripts/fleet-git-discard-scoped.mjs +5 -25
- package/scripts/fleet-git-pull-scoped.mjs +5 -25
- package/scripts/fleet-vulns.mjs +4 -9
- package/scripts/generate-release-notes.mjs +1 -1
- package/scripts/{audit-fix-plan.mjs → lib/audit-fix-plan.mjs} +77 -4
- package/scripts/{audit-fix-plan.test.mjs → lib/audit-fix-plan.test.mjs} +50 -2
- package/scripts/lib/common.mjs +95 -0
- package/scripts/{dependency-policy-toolchain.mjs → lib/dependency-policy-toolchain.mjs} +3 -35
- package/scripts/{git-commit-push-fleet-common.mjs → lib/git-commit-push-fleet-common.mjs} +1 -1
- package/scripts/{git-discard-fleet-common.mjs → lib/git-discard-fleet-common.mjs} +1 -1
- package/scripts/{git-pull-fleet-common.mjs → lib/git-pull-fleet-common.mjs} +1 -1
- package/scripts/{override-changes.mjs → lib/override-changes.mjs} +20 -0
- package/scripts/{piwik-dimensions.test.mjs → lib/piwik-dimensions.test.mjs} +0 -5
- package/scripts/{release-notes-common.mjs → lib/release-notes-common.mjs} +5 -7
- package/scripts/{resolve-dependency-policy-path.mjs → lib/resolve-dependency-policy-path.mjs} +4 -3
- package/scripts/piwik.mjs +6 -6
- package/scripts/poeditor-download.mjs +1 -1
- package/scripts/poeditor-upload.mjs +1 -1
- package/scripts/policy-outdated.mjs +3 -8
- package/scripts/policy-upgrade-commit.mjs +5 -26
- package/scripts/policy-upgrade.mjs +6 -30
- package/scripts/policy-vulns.mjs +3 -8
- package/scripts/prune-overrides.mjs +5 -16
- package/scripts/prune-release-age-exclude.mjs +1 -1
- package/scripts/refresh-dependency-policy-from-repos.mjs +5 -7
- package/scripts/release.sh +2 -1
- package/scripts/scaffold-vue.mjs +1 -3
- package/scripts/setup-ai-rules.mjs +135 -0
- package/scripts/sync-consumer-lefthook.mjs +7 -24
- package/scripts/sync-dependency-check-suppressions-commit.mjs +7 -46
- package/scripts/sync-dependency-policy-commit.mjs +6 -38
- package/scripts/sync-dependency-policy.mjs +9 -35
- package/scripts/sync-node-toolchain.mjs +5 -8
- package/scripts/sync-pnpm-toolchain.mjs +5 -8
- package/scripts/sync-scaffold-vue-deps.mjs +6 -7
- package/scripts/sync-tooling-configs.mjs +8 -23
- package/scripts/sync-vite-plugin-vue-inspector.mjs +7 -39
- package/scripts/verify-release-notes.mjs +1 -1
- package/.cursor/rules/core-coding-standards.mdc +0 -291
- package/scaffold/vue/.gitignore +0 -13
- package/scripts/cz/.cz-config.cjs +0 -29
- package/scripts/fleet-git-commit-push.mjs +0 -75
- package/scripts/fleet-git-discard.mjs +0 -66
- package/scripts/fleet-git-pull.mjs +0 -65
- package/scripts/sync-core-coding-standards.mjs +0 -77
- package/scripts/sync-peers-from-dev.mjs +0 -31
- package/scripts/vuln-commit.mjs +0 -142
- /package/configs/{fallowrc.sync-stub.json → fallowrc.json} +0 -0
- /package/scripts/{dependency-policy-pins.mjs → lib/dependency-policy-pins.mjs} +0 -0
- /package/scripts/{fleet-project-repos.mjs → lib/fleet-project-repos.mjs} +0 -0
- /package/scripts/{fleet-repo-picker.mjs → lib/fleet-repo-picker.mjs} +0 -0
- /package/scripts/{fleet-vue-repos.mjs → lib/fleet-vue-repos.mjs} +0 -0
- /package/scripts/{git-fleet-common.mjs → lib/git-fleet-common.mjs} +0 -0
- /package/scripts/{node-toolchain-sync.mjs → lib/node-toolchain-sync.mjs} +0 -0
- /package/scripts/{piwik-common.mjs → lib/piwik-common.mjs} +0 -0
- /package/scripts/{piwik-dimensions.mjs → lib/piwik-dimensions.mjs} +0 -0
- /package/scripts/{pnpm-toolchain-sync.mjs → lib/pnpm-toolchain-sync.mjs} +0 -0
- /package/scripts/{poeditor-common.mjs → lib/poeditor-common.mjs} +0 -0
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Core coding standards for Vue / fds-vue-core frontend projects
|
|
3
|
-
alwaysApply: true
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Core Coding Standards
|
|
7
|
-
|
|
8
|
-
## Keep changes focused
|
|
9
|
-
|
|
10
|
-
- Make the smallest safe change that solves the request.
|
|
11
|
-
- Avoid unrelated refactors in the same task.
|
|
12
|
-
- Preserve existing behavior unless a behavior change is requested.
|
|
13
|
-
|
|
14
|
-
## Code comments
|
|
15
|
-
|
|
16
|
-
- Do not add comments in code unless the user explicitly asks for them.
|
|
17
|
-
- Prefer self-explanatory naming and structure over inline comments.
|
|
18
|
-
- When updating existing code, do not add new comments or expand existing ones unless requested.
|
|
19
|
-
- Exception: update comments only when they are required to keep an existing comment accurate after a behavior change in the same task.
|
|
20
|
-
|
|
21
|
-
## Communication efficiency
|
|
22
|
-
|
|
23
|
-
- Do not waste tokens: keep responses concise, high-signal, and scoped to the request.
|
|
24
|
-
- Prefer short summaries over long explanations unless deeper detail is explicitly requested.
|
|
25
|
-
|
|
26
|
-
## Evidence-first implementation
|
|
27
|
-
|
|
28
|
-
- Never guess or hallucinate APIs, component props, or framework behavior.
|
|
29
|
-
- Verify implementation details against authoritative sources first (official docs, project code, and library types/source).
|
|
30
|
-
- If documentation is missing or ambiguous, state uncertainty explicitly and choose the safest validated approach.
|
|
31
|
-
|
|
32
|
-
## Match project conventions
|
|
33
|
-
|
|
34
|
-
- Follow established naming, file structure, and component patterns.
|
|
35
|
-
- Reuse existing utilities and query/composable patterns before adding new ones.
|
|
36
|
-
- Keep TypeScript types explicit at API boundaries.
|
|
37
|
-
|
|
38
|
-
## Strict TypeScript
|
|
39
|
-
|
|
40
|
-
- Keep TypeScript and Vue template typing strict (`strict: true`, `strictTemplates: true`).
|
|
41
|
-
- Do not introduce `any`; use `unknown` for unsafe input and narrow before use.
|
|
42
|
-
- Type API boundaries explicitly (request payloads, response DTOs, mutation variables, callback contracts).
|
|
43
|
-
- Prefer named reusable types under `src/types/<domain>/` for shared domain shapes; re-export from `src/types/index.ts`. Do not keep shared interfaces in views, stores, or query files.
|
|
44
|
-
- Avoid non-null assertions unless guarded by `enabled` checks or equivalent runtime guarantees.
|
|
45
|
-
|
|
46
|
-
## API boundaries
|
|
47
|
-
|
|
48
|
-
- Components and views should not call low-level HTTP/API clients directly for feature data fetching.
|
|
49
|
-
- Centralize HTTP and endpoint wiring in a dedicated API layer (for example `src/api/endpoints/*` or the convention used in the repo).
|
|
50
|
-
- Consume server state through query/mutation hooks (for example `src/queries/*` or equivalent), not ad-hoc fetches in UI code.
|
|
51
|
-
|
|
52
|
-
## Query strings
|
|
53
|
-
|
|
54
|
-
- Build query strings with `URLSearchParams` (or a shared helper that wraps it).
|
|
55
|
-
- Do not concatenate `key=value` fragments with `&`.
|
|
56
|
-
- Let `URLSearchParams` encode values. Do not interpolate untrusted or user-entered values into URLs.
|
|
57
|
-
- `toString()` has no leading `?`. Prefix it when appending to a path, and skip empty values instead of emitting empty keys.
|
|
58
|
-
- For list views, derive the query string in the view from route filter state (`requestQueryParams` computed). The API layer should accept the query string, not reconstruct filters.
|
|
59
|
-
|
|
60
|
-
## State-management boundaries
|
|
61
|
-
|
|
62
|
-
- Use TanStack Query for server state and caching. Do not add Vuex for new work.
|
|
63
|
-
- Use Pinia only for client/app state that is not owned by the server (for example `src/pinia/*`).
|
|
64
|
-
- Do not duplicate the same state across the query cache and Pinia unless the project explicitly documents that pattern.
|
|
65
|
-
- When a project is migrating Vuex → Query/Pinia, follow that repo’s local migration rules (if present) instead of expanding Vuex.
|
|
66
|
-
|
|
67
|
-
## TanStack Query server-state conventions
|
|
68
|
-
|
|
69
|
-
- Use TanStack Query for server state; avoid duplicating server data in ad-hoc local state.
|
|
70
|
-
- Keep API calls in the API layer and expose query/mutation hooks from a dedicated hooks folder.
|
|
71
|
-
- Build query keys as stable arrays of serializable primitives; when using refs, key by `.value` in a computed key.
|
|
72
|
-
- Prefer shared query key constants/factories to keep invalidation and cache updates consistent.
|
|
73
|
-
- Add `enabled` guards for required IDs/paths to prevent invalid requests.
|
|
74
|
-
- Use `placeholderData: keepPreviousData` for paginated/filter-driven views to avoid UI flicker.
|
|
75
|
-
- Set `staleTime` intentionally per resource; keep `refetchOnWindowFocus` off unless fresh-on-focus behavior is required.
|
|
76
|
-
- On mutation success, update cache intentionally (`setQueryData`) and/or invalidate the narrowest matching keys.
|
|
77
|
-
- Respect shared query client retry behavior and only override retries when endpoint-specific behavior is needed.
|
|
78
|
-
|
|
79
|
-
### Query loading UI
|
|
80
|
-
|
|
81
|
-
TanStack Query v5 flags (do not treat them as interchangeable):
|
|
82
|
-
|
|
83
|
-
| Flag | Meaning |
|
|
84
|
-
| --- | --- |
|
|
85
|
-
| `isPending` | No cached data yet (`status === 'pending'`) |
|
|
86
|
-
| `isFetching` | Any in-flight request, including background refetch |
|
|
87
|
-
| `isLoading` | First fetch in flight (`isPending && isFetching`) |
|
|
88
|
-
|
|
89
|
-
Rules:
|
|
90
|
-
|
|
91
|
-
1. **Content-blocking / full-page spinner** — use `isLoading` (or combine several `isLoading*` flags). Prefer `isLoading` over bare `isPending` when the query can be `enabled: false` (lazy/disabled queries stay `isPending` without fetching).
|
|
92
|
-
2. **Do not use bare `isFetching` for blocking UI** — it re-shows the full loader on every background refetch. Use `isFetching` only for subtle “refreshing” indicators while keeping content visible.
|
|
93
|
-
3. **Paginated / `keepPreviousData` lists** — show a hard loader only when there is nothing useful to render:
|
|
94
|
-
|
|
95
|
-
```ts
|
|
96
|
-
const isLoading = computed(() => isPending.value || (isFetching.value && !data.value))
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
4. **Multiple queries for one screen** — combine with OR into one (or purpose-prefixed) computed:
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
const isLoading = computed(() => isLoadingDetails.value || isLoadingList.value)
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
5. **No passthrough aliases** — do not wrap a single flag (`const loading = computed(() => isFetching.value)`). Destructure and rename instead:
|
|
106
|
-
|
|
107
|
-
```ts
|
|
108
|
-
const { data, isLoading, error } = useGetDetails(id)
|
|
109
|
-
// template: v-show="isLoading"
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
6. **Naming** — local query-derived booleans use the `is*` prefix: `isLoading`, `isLoadingUnits`, `isLoadingHistory`. Keep FDS/component prop names as-is (`:loading="isLoading"`). Mutations: rename `isPending` to intent (`isSaving`, `isSubmitting`, `isUploading`).
|
|
113
|
-
|
|
114
|
-
- TanStack Query boolean flags (`isPending`, `isSuccess`, `isError`) are typed `Ref<false> | Ref<true>`. `vue-tsc` unwraps that union in templates to `boolean`; the Vue language plugin may not.
|
|
115
|
-
- Narrow in script (`const isPending = mutation.isPending as Ref<boolean>`), then bind without `.value` (`:loading="isPending"`). Do not use `.value` in templates (fails `vue-tsc`) and do not add a wrapping `computed` only to satisfy the editor.
|
|
116
|
-
|
|
117
|
-
## Mutation cache behavior
|
|
118
|
-
|
|
119
|
-
- Every mutation should define post-success cache behavior.
|
|
120
|
-
- Use `invalidateQueries` for stale collections and `setQueryData` for direct entity updates.
|
|
121
|
-
- Invalidate the narrowest query keys that match changed resources.
|
|
122
|
-
|
|
123
|
-
## Vue Router conventions
|
|
124
|
-
|
|
125
|
-
- Organize route definitions in modules (for example `src/router/modules/*`) composed from a single router entry (for example `src/router/index.ts`), matching the repo layout.
|
|
126
|
-
- Every user-facing route should define `meta.title`; provide breadcrumb metadata for detail/flow routes when needed.
|
|
127
|
-
- Use shared route guards for auth/anonymous access control instead of duplicating checks in views.
|
|
128
|
-
- Prefer `props: true` and typed route params in components instead of reading raw params everywhere.
|
|
129
|
-
- Prefer named-route navigation for internal flows (`{ name, params, query }`) and reserve raw path pushes for simple/static redirects.
|
|
130
|
-
- Preserve existing query params intentionally when updating filters, pagination, or tab state.
|
|
131
|
-
- Returning to a view whose filters live in the URL: call `usePreviousLocation` from `fds-vue-core` in setup. `router.push('/path')` drops that query. Browser back keeps it.
|
|
132
|
-
- Use `hrefFor` on `router-link` and `FdsTabsItem`, and `goTo` on back buttons, only when the target is that same view. Leave other navigations unchanged.
|
|
133
|
-
- `hrefFor(path)` reuses the previous history entry, including `_search`, `_page`, and filters, when its pathname matches. Otherwise it returns the path only.
|
|
134
|
-
- `goTo(fallback, matchPaths?)` calls `router.back()` when the previous entry matches, so scroll is kept. Otherwise it pushes the fallback path.
|
|
135
|
-
- Pass `matchPaths` when several paths count as the same place. Without it, only the fallback path is compared.
|
|
136
|
-
|
|
137
|
-
```ts
|
|
138
|
-
import { usePreviousLocation } from 'fds-vue-core'
|
|
139
|
-
|
|
140
|
-
const { hrefFor, goTo } = usePreviousLocation()
|
|
141
|
-
|
|
142
|
-
const goBack = () => {
|
|
143
|
-
goTo('/services/J4', ['/services/J4', '/services'])
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
```vue
|
|
148
|
-
<router-link :to="hrefFor(breadcrumb.path)">
|
|
149
|
-
{{ breadcrumb.name }}
|
|
150
|
-
</router-link>
|
|
151
|
-
<FdsTabsItem :to="hrefFor(tab.path)" />
|
|
152
|
-
<FdsButtonMinor :text="t('common.back')" icon="left" @click="goBack" />
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
- Lazy-load route components with dynamic imports to keep initial bundles smaller.
|
|
156
|
-
- Name organisation route params by domain meaning, not generic `id`:
|
|
157
|
-
- top org: `:topClientNumber`
|
|
158
|
-
- unit/sub-org: `:unitClientNumber`
|
|
159
|
-
- Prefer `/organisations/:topClientNumber/units/:unitClientNumber` over `:id` / `:clino`.
|
|
160
|
-
- Keep route param names aligned with component props when using `props: true`.
|
|
161
|
-
|
|
162
|
-
## Vue and UI expectations
|
|
163
|
-
|
|
164
|
-
- Prefer clear, readable templates over compact but hard-to-follow expressions.
|
|
165
|
-
- Keep components focused; extract repeated UI blocks when duplication grows.
|
|
166
|
-
- Keep localization text in language files instead of hardcoded UI strings.
|
|
167
|
-
|
|
168
|
-
## FDS-first component usage
|
|
169
|
-
|
|
170
|
-
- Use `fds-vue-core` components as the default choice for UI building blocks.
|
|
171
|
-
- Multi-step wizards use `FdsWizard` from `fds-vue-core`. Follow the project’s wizard rule file when one exists (for example `.cursor/rules/fds-wizard.md`).
|
|
172
|
-
- Before adding custom UI, check whether an FDS component already solves the use case.
|
|
173
|
-
- Prefer extending composition around FDS components over re-implementing design-system behavior.
|
|
174
|
-
- Keep visual consistency by matching FDS variants, spacing patterns, and interaction styles.
|
|
175
|
-
|
|
176
|
-
## Asset creation policy
|
|
177
|
-
|
|
178
|
-
- Do not create icons, images, illustrations, or placeholder content unless explicitly requested.
|
|
179
|
-
- Assume `fds-vue-core` provides the required visual assets and use those first.
|
|
180
|
-
- If a required asset appears missing, clearly point out the gap instead of inventing a replacement.
|
|
181
|
-
|
|
182
|
-
## FDS typography (fds-vue-core)
|
|
183
|
-
|
|
184
|
-
- For headings, use `FdsH1`, `FdsH2`, and `FdsH3` (not raw `h1/h2/h3`).
|
|
185
|
-
- `FdsH1/FdsH2/FdsH3` use the `text` prop for content and support `size` (`sm|md|lg|xl|responsive`).
|
|
186
|
-
- When layout spacing needs to be neutralized, reset heading spacing with `class="m-0"` (and add explicit margins as needed).
|
|
187
|
-
- Use `FdsLead` for intro/lead paragraphs, `FdsMeta` for metadata/secondary text, and `FdsLabel` for short labels.
|
|
188
|
-
- `FdsLead`, `FdsMeta`, and `FdsLabel` render slot content; prefer these over custom styled spans for text semantics.
|
|
189
|
-
|
|
190
|
-
## Tailwind (v4+)
|
|
191
|
-
|
|
192
|
-
- Use Tailwind v4 patterns and utilities (for example `tailwindcss@4` with `@tailwindcss/vite` where applicable).
|
|
193
|
-
- Prefer utility classes in templates; only add custom CSS when utilities cannot express the design.
|
|
194
|
-
- Follow existing FDS token/preset styling (`fds-vue-core` tokens and preset) instead of inventing parallel design tokens.
|
|
195
|
-
- Always use Tailwind/FDS design tokens for colors, spacing, typography, radius, and shadows.
|
|
196
|
-
- Never invent ad-hoc color values (for example hex/rgb/hsl literals) when a tokenized utility exists.
|
|
197
|
-
- Keep spacing and typography explicit in class names; avoid hidden styling in deep selectors.
|
|
198
|
-
- Use `!` utility overrides sparingly and only when a component default cannot be changed safely.
|
|
199
|
-
- For Tailwind v4 important modifiers, use postfix-important utility syntax (for example `grid-cols-1!`) instead of prefix syntax (for example `!grid-cols-1`).
|
|
200
|
-
- Stack repeated items with parent `flex flex-col gap-*` (or `space-y-*`). Do not use `mb-* last:mb-0` or `last-of-type:mb-*` — those variants often miss the last `v-for` item (extra Vue/FDS nodes) and trip Tailwind conflict lint.
|
|
201
|
-
|
|
202
|
-
## Figma MCP implementation workflow
|
|
203
|
-
|
|
204
|
-
- When using Figma MCP, implement the design as specified (layout, spacing, typography, states, and interactions).
|
|
205
|
-
- Treat the Figma file as source of truth for UI behavior unless product/technical constraints are explicitly documented.
|
|
206
|
-
- Primary choice for implementation is `fds-vue-core` components that best match the design intent.
|
|
207
|
-
- Only create custom UI when no suitable FDS component exists; keep custom parts minimal and design-token based.
|
|
208
|
-
- If any Figma detail cannot be implemented exactly, document the deviation and choose the closest FDS-aligned solution.
|
|
209
|
-
|
|
210
|
-
## i18n and content
|
|
211
|
-
|
|
212
|
-
- Do not hardcode user-facing copy in templates or script logic; use `t(...)` keys.
|
|
213
|
-
- Add or update locale entries under the project’s language folder (for example `src/lang/*.json`) when introducing new text.
|
|
214
|
-
- Keep translation keys descriptive and grouped by screen/feature.
|
|
215
|
-
- Avoid storing i18n keys inside data/definition arrays (e.g. `i18nKey`) and translating later via dynamic keys or large switch statements. Prefer building definitions with already-translated `label: t('...')` (often as a `computed`) so types stay strict and templates stay simple.
|
|
216
|
-
|
|
217
|
-
## Formatting and locale consistency
|
|
218
|
-
|
|
219
|
-
- Prefer shared formatting utilities for date/number/currency output instead of ad-hoc inline formatting.
|
|
220
|
-
- Use Swedish locale conventions (`sv-SE`) unless a feature explicitly requires another locale.
|
|
221
|
-
|
|
222
|
-
## Accessibility baseline
|
|
223
|
-
|
|
224
|
-
- Use semantic HTML and accessible FDS components first; avoid click-only `div`/`span` patterns.
|
|
225
|
-
- Ensure form controls have proper labels and required ARIA attributes when needed.
|
|
226
|
-
- All interactive elements must be keyboard reachable and show visible focus states.
|
|
227
|
-
- Provide meaningful accessible names for icon-only actions and links.
|
|
228
|
-
|
|
229
|
-
## Async UI states
|
|
230
|
-
|
|
231
|
-
- For data-driven screens, always handle loading, empty, error, and success states explicitly.
|
|
232
|
-
- Use consistent FDS-based feedback patterns for loaders, alerts, and empty-state messaging.
|
|
233
|
-
|
|
234
|
-
## Error handling UX
|
|
235
|
-
|
|
236
|
-
- Do not swallow errors silently; surface actionable feedback to users.
|
|
237
|
-
- Use consistent FDS feedback patterns for API failures (alerts/toasts/inline messages).
|
|
238
|
-
- Keep technical error details out of user-facing copy unless explicitly required.
|
|
239
|
-
|
|
240
|
-
## Performance guardrails
|
|
241
|
-
|
|
242
|
-
- Avoid unnecessary reactive churn (redundant watchers/computed chains in hot paths).
|
|
243
|
-
- Memoize expensive derived values and keep render-time computations lightweight.
|
|
244
|
-
- Keep route-level and heavy feature components lazy-loaded.
|
|
245
|
-
|
|
246
|
-
## Security and data handling
|
|
247
|
-
|
|
248
|
-
- Never commit or expose secrets, tokens, or sensitive identifiers in code, logs, or UI.
|
|
249
|
-
- Validate and sanitize untrusted values from route/query params before use.
|
|
250
|
-
- Type and validate outgoing payloads to prevent accidental over-posting.
|
|
251
|
-
|
|
252
|
-
## Testing expectations
|
|
253
|
-
|
|
254
|
-
- Do not add new automated tests unless the user explicitly asks for them.
|
|
255
|
-
- Update existing tests when your change requires it (for example broken assertions, renamed APIs, or changed behavior covered by current tests).
|
|
256
|
-
- When new tests are requested, add them close to the affected feature.
|
|
257
|
-
- Keep tests behavior-focused and avoid brittle implementation-detail assertions.
|
|
258
|
-
- When no new tests are requested, rely on lint, typecheck, and manual verification of critical flows where practical.
|
|
259
|
-
|
|
260
|
-
## CI/CD
|
|
261
|
-
|
|
262
|
-
- Treat the repository’s CI configuration as the source of truth (`Jenkinsfile`, GitHub Actions, or equivalent).
|
|
263
|
-
- Prefer frozen lockfile installs in CI (for example `pnpm install --frozen-lockfile --prefer-offline`) so CI matches local installs.
|
|
264
|
-
- Keep build, lint, typecheck, and test stages deterministic; document any project-specific stages (security scans, i18n download, Docker publish) in the repo README or a dedicated rule file.
|
|
265
|
-
|
|
266
|
-
## Quality checks before finishing
|
|
267
|
-
|
|
268
|
-
- Run relevant checks for touched code (lint and typecheck when practical).
|
|
269
|
-
- Run and update existing tests when your change affects them; add new tests only when the user explicitly requests them.
|
|
270
|
-
- Fix issues introduced by your change before handing off.
|
|
271
|
-
- Call out anything you could not verify.
|
|
272
|
-
|
|
273
|
-
## Definition of done
|
|
274
|
-
|
|
275
|
-
- Lint and type checks pass for touched code.
|
|
276
|
-
- UI matches Figma/FDS expectations and uses tokenized styling.
|
|
277
|
-
- i18n keys, async states, and cache behavior are handled for changed features.
|
|
278
|
-
- Note any constraints, deviations, or unverified parts in handoff.
|
|
279
|
-
|
|
280
|
-
## Legacy/deprecation policy
|
|
281
|
-
|
|
282
|
-
- For new work, follow the current preferred patterns in this document.
|
|
283
|
-
- Do not expand usage of known legacy patterns when a supported modern pattern exists.
|
|
284
|
-
- If migration is out of scope, isolate legacy touchpoints and avoid spreading them.
|
|
285
|
-
|
|
286
|
-
## Example: API data safety
|
|
287
|
-
|
|
288
|
-
```ts
|
|
289
|
-
// Prefer typed fallbacks when API data may be missing.
|
|
290
|
-
const total = response?.stats?.total ?? 0
|
|
291
|
-
```
|
package/scaffold/vue/.gitignore
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cz-git config used by fg-devkit commit flows.
|
|
3
|
-
* Keep this in czrc-compatible shape for `czg --config`.
|
|
4
|
-
*/
|
|
5
|
-
module.exports = {
|
|
6
|
-
types: [
|
|
7
|
-
{ value: 'feat', name: 'feat(scope): Added' },
|
|
8
|
-
{ value: 'fix', name: 'fix(scope): Fixed' },
|
|
9
|
-
{ value: 'perf', name: 'perf(scope): Performance' },
|
|
10
|
-
{ value: 'refactor', name: 'refactor(scope): Changed' },
|
|
11
|
-
{ value: 'chore', name: 'chore(scope): Changed' },
|
|
12
|
-
{ value: 'dep', name: 'dep(scope): Dependencies (single package or batch)' },
|
|
13
|
-
{ value: 'vuln', name: 'vuln(scope): Security dependency update (single package or batch)' },
|
|
14
|
-
{ value: 'fix(security)', name: 'fix(security): Security' },
|
|
15
|
-
],
|
|
16
|
-
allowCustomScopes: true,
|
|
17
|
-
allowEmptyScopes: false,
|
|
18
|
-
allowBreakingChanges: [],
|
|
19
|
-
skipQuestions: ['breaking', 'footer', 'issues', 'issuePrefixes'],
|
|
20
|
-
maxSubjectLength: 120,
|
|
21
|
-
breaklineChar: '|',
|
|
22
|
-
useEmoji: false,
|
|
23
|
-
messages: {
|
|
24
|
-
type: 'Select the type of change you are committing:',
|
|
25
|
-
customScope: 'Packages (for dep/vuln) or scope:',
|
|
26
|
-
subject: 'Commit subject (imperative, no trailing period):',
|
|
27
|
-
body: "Optional body (use '|' for newline):",
|
|
28
|
-
},
|
|
29
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Maintainer: stage all changes, commit, and push on the current branch for every git repo under projects root.
|
|
4
|
-
*
|
|
5
|
-
* Usage (from fg-devkit repository root):
|
|
6
|
-
* pnpm exec fg-devkit fleet-git-commit-push --message="chore: sync toolchain" [projects-root] [--dry-run]
|
|
7
|
-
* pnpm exec fg-devkit fleet-git-commit-push -m "chore: sync toolchain"
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import fs from 'node:fs'
|
|
11
|
-
import path from 'node:path'
|
|
12
|
-
import { fileURLToPath } from 'node:url'
|
|
13
|
-
|
|
14
|
-
import { collectFleetGitRepos } from './fleet-project-repos.mjs'
|
|
15
|
-
import { filterCommitPushPositionalArgs, resolveCommitMessage } from './git-fleet-common.mjs'
|
|
16
|
-
import { runFleetGitCommitPush } from './git-commit-push-fleet-common.mjs'
|
|
17
|
-
|
|
18
|
-
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
19
|
-
const fgDevkitRoot = path.resolve(scriptDir, '..')
|
|
20
|
-
const fgDevkitPkgPath = path.join(fgDevkitRoot, 'package.json')
|
|
21
|
-
const LOG_PREFIX = '[fleet-git-commit-push]'
|
|
22
|
-
|
|
23
|
-
const cliArgs = process.argv.slice(2)
|
|
24
|
-
const dryRun = cliArgs.includes('--dry-run')
|
|
25
|
-
const positionalArgs = filterCommitPushPositionalArgs(cliArgs)
|
|
26
|
-
const projectsRootArg = positionalArgs.find((a) => !a.startsWith('-'))
|
|
27
|
-
const projectsRoot = projectsRootArg
|
|
28
|
-
? path.resolve(process.cwd(), projectsRootArg)
|
|
29
|
-
: path.resolve(fgDevkitRoot, '..')
|
|
30
|
-
|
|
31
|
-
function assertFgDevkitMaintainerContext() {
|
|
32
|
-
if (path.resolve(process.cwd()) !== fgDevkitRoot) {
|
|
33
|
-
console.error(`${LOG_PREFIX} Run from the fg-devkit repository root.`)
|
|
34
|
-
process.exit(1)
|
|
35
|
-
}
|
|
36
|
-
try {
|
|
37
|
-
const pkg = JSON.parse(fs.readFileSync(fgDevkitPkgPath, 'utf8'))
|
|
38
|
-
if (pkg?.name !== 'fg-devkit') {
|
|
39
|
-
console.error(`${LOG_PREFIX} package.json name must be fg-devkit.`)
|
|
40
|
-
process.exit(1)
|
|
41
|
-
}
|
|
42
|
-
} catch {
|
|
43
|
-
console.error(`${LOG_PREFIX} Could not read fg-devkit package.json.`)
|
|
44
|
-
process.exit(1)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function main() {
|
|
49
|
-
assertFgDevkitMaintainerContext()
|
|
50
|
-
|
|
51
|
-
if (!fs.existsSync(projectsRoot)) {
|
|
52
|
-
console.error(`${LOG_PREFIX} projects root not found: ${projectsRoot}`)
|
|
53
|
-
process.exit(1)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const commitMessage = await resolveCommitMessage(cliArgs, LOG_PREFIX)
|
|
57
|
-
const repos = collectFleetGitRepos(projectsRoot)
|
|
58
|
-
|
|
59
|
-
console.log(`${LOG_PREFIX} projectsRoot=${projectsRoot}`)
|
|
60
|
-
console.log(`${LOG_PREFIX} repositories=${repos.length}`)
|
|
61
|
-
|
|
62
|
-
const exitCode = runFleetGitCommitPush({
|
|
63
|
-
repos,
|
|
64
|
-
projectsRoot,
|
|
65
|
-
commitMessage,
|
|
66
|
-
dryRun,
|
|
67
|
-
logPrefix: LOG_PREFIX,
|
|
68
|
-
})
|
|
69
|
-
process.exit(exitCode)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
main().catch((err) => {
|
|
73
|
-
console.error(LOG_PREFIX, err)
|
|
74
|
-
process.exit(1)
|
|
75
|
-
})
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Maintainer: discard tracked git changes in every repository under projects root.
|
|
4
|
-
*
|
|
5
|
-
* Usage (from fg-devkit repository root):
|
|
6
|
-
* pnpm exec fg-devkit fleet-git-discard [projects-root] [--dry-run]
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import fs from 'node:fs'
|
|
10
|
-
import path from 'node:path'
|
|
11
|
-
import { fileURLToPath } from 'node:url'
|
|
12
|
-
|
|
13
|
-
import { collectFleetGitRepos } from './fleet-project-repos.mjs'
|
|
14
|
-
import { runFleetGitDiscard } from './git-discard-fleet-common.mjs'
|
|
15
|
-
|
|
16
|
-
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
17
|
-
const fgDevkitRoot = path.resolve(scriptDir, '..')
|
|
18
|
-
const fgDevkitPkgPath = path.join(fgDevkitRoot, 'package.json')
|
|
19
|
-
const LOG_PREFIX = '[fleet-git-discard]'
|
|
20
|
-
|
|
21
|
-
const cliArgs = process.argv.slice(2)
|
|
22
|
-
const dryRun = cliArgs.includes('--dry-run')
|
|
23
|
-
const projectsRootArg = cliArgs.find((a) => !a.startsWith('-'))
|
|
24
|
-
const projectsRoot = projectsRootArg
|
|
25
|
-
? path.resolve(process.cwd(), projectsRootArg)
|
|
26
|
-
: path.resolve(fgDevkitRoot, '..')
|
|
27
|
-
|
|
28
|
-
function assertFgDevkitMaintainerContext() {
|
|
29
|
-
if (path.resolve(process.cwd()) !== fgDevkitRoot) {
|
|
30
|
-
console.error(`${LOG_PREFIX} Run from the fg-devkit repository root.`)
|
|
31
|
-
process.exit(1)
|
|
32
|
-
}
|
|
33
|
-
try {
|
|
34
|
-
const pkg = JSON.parse(fs.readFileSync(fgDevkitPkgPath, 'utf8'))
|
|
35
|
-
if (pkg?.name !== 'fg-devkit') {
|
|
36
|
-
console.error(`${LOG_PREFIX} package.json name must be fg-devkit.`)
|
|
37
|
-
process.exit(1)
|
|
38
|
-
}
|
|
39
|
-
} catch {
|
|
40
|
-
console.error(`${LOG_PREFIX} Could not read fg-devkit package.json.`)
|
|
41
|
-
process.exit(1)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function main() {
|
|
46
|
-
assertFgDevkitMaintainerContext()
|
|
47
|
-
|
|
48
|
-
if (!fs.existsSync(projectsRoot)) {
|
|
49
|
-
console.error(`${LOG_PREFIX} projects root not found: ${projectsRoot}`)
|
|
50
|
-
process.exit(1)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const repos = collectFleetGitRepos(projectsRoot)
|
|
54
|
-
console.log(`${LOG_PREFIX} projectsRoot=${projectsRoot}`)
|
|
55
|
-
console.log(`${LOG_PREFIX} repositories=${repos.length}`)
|
|
56
|
-
|
|
57
|
-
const exitCode = runFleetGitDiscard({
|
|
58
|
-
repos,
|
|
59
|
-
projectsRoot,
|
|
60
|
-
dryRun,
|
|
61
|
-
logPrefix: LOG_PREFIX,
|
|
62
|
-
})
|
|
63
|
-
process.exit(exitCode)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
main()
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Maintainer: git pull on the current branch for every git repository under projects root.
|
|
4
|
-
*
|
|
5
|
-
* Usage (from fg-devkit repository root):
|
|
6
|
-
* pnpm exec fg-devkit fleet-git-pull [projects-root] [--dry-run]
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import fs from 'node:fs'
|
|
10
|
-
import path from 'node:path'
|
|
11
|
-
import { fileURLToPath } from 'node:url'
|
|
12
|
-
|
|
13
|
-
import { collectFleetGitRepos } from './fleet-project-repos.mjs'
|
|
14
|
-
import { runFleetGitPull } from './git-pull-fleet-common.mjs'
|
|
15
|
-
|
|
16
|
-
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
17
|
-
const fgDevkitRoot = path.resolve(scriptDir, '..')
|
|
18
|
-
const fgDevkitPkgPath = path.join(fgDevkitRoot, 'package.json')
|
|
19
|
-
|
|
20
|
-
const cliArgs = process.argv.slice(2)
|
|
21
|
-
const dryRun = cliArgs.includes('--dry-run')
|
|
22
|
-
const projectsRootArg = cliArgs.find((a) => !a.startsWith('-'))
|
|
23
|
-
const projectsRoot = projectsRootArg
|
|
24
|
-
? path.resolve(process.cwd(), projectsRootArg)
|
|
25
|
-
: path.resolve(fgDevkitRoot, '..')
|
|
26
|
-
|
|
27
|
-
function assertFgDevkitMaintainerContext() {
|
|
28
|
-
if (path.resolve(process.cwd()) !== fgDevkitRoot) {
|
|
29
|
-
console.error('[fleet-git-pull] Run from the fg-devkit repository root.')
|
|
30
|
-
process.exit(1)
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
const pkg = JSON.parse(fs.readFileSync(fgDevkitPkgPath, 'utf8'))
|
|
34
|
-
if (pkg?.name !== 'fg-devkit') {
|
|
35
|
-
console.error('[fleet-git-pull] package.json name must be fg-devkit.')
|
|
36
|
-
process.exit(1)
|
|
37
|
-
}
|
|
38
|
-
} catch {
|
|
39
|
-
console.error('[fleet-git-pull] Could not read fg-devkit package.json.')
|
|
40
|
-
process.exit(1)
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function main() {
|
|
45
|
-
assertFgDevkitMaintainerContext()
|
|
46
|
-
|
|
47
|
-
if (!fs.existsSync(projectsRoot)) {
|
|
48
|
-
console.error(`[fleet-git-pull] projects root not found: ${projectsRoot}`)
|
|
49
|
-
process.exit(1)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const repos = collectFleetGitRepos(projectsRoot)
|
|
53
|
-
console.log(`[fleet-git-pull] projectsRoot=${projectsRoot}`)
|
|
54
|
-
console.log(`[fleet-git-pull] repositories=${repos.length}`)
|
|
55
|
-
|
|
56
|
-
const exitCode = runFleetGitPull({
|
|
57
|
-
repos,
|
|
58
|
-
projectsRoot,
|
|
59
|
-
dryRun,
|
|
60
|
-
logPrefix: '[fleet-git-pull]',
|
|
61
|
-
})
|
|
62
|
-
process.exit(exitCode)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
main()
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Syncs .cursor/rules/core-coding-standards.mdc into the consumer repo.
|
|
4
|
-
* Rewrites frontmatter description to include the consumer package name.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* node node_modules/fg-devkit/scripts/sync-core-coding-standards.mjs
|
|
8
|
-
* pnpm exec fg-devkit sync-core-coding-standards
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import fs from 'node:fs'
|
|
12
|
-
import path from 'node:path'
|
|
13
|
-
import { fileURLToPath } from 'node:url'
|
|
14
|
-
|
|
15
|
-
const projectRoot = process.cwd()
|
|
16
|
-
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
17
|
-
|
|
18
|
-
function findFgDevkitPackageRoot() {
|
|
19
|
-
const candidates = [
|
|
20
|
-
path.resolve(scriptDir, '..'),
|
|
21
|
-
path.join(projectRoot, 'node_modules', 'fg-devkit'),
|
|
22
|
-
path.join(projectRoot, 'node_modules', 'fds-vue-core', 'node_modules', 'fg-devkit'),
|
|
23
|
-
]
|
|
24
|
-
for (const p of candidates) {
|
|
25
|
-
if (fs.existsSync(path.join(p, 'package.json'))) {
|
|
26
|
-
return p
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return null
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function getConsumerPackageName() {
|
|
33
|
-
const pkgPath = path.join(projectRoot, 'package.json')
|
|
34
|
-
if (!fs.existsSync(pkgPath)) {
|
|
35
|
-
return 'consumer-project'
|
|
36
|
-
}
|
|
37
|
-
try {
|
|
38
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
|
|
39
|
-
if (typeof pkg.name === 'string' && pkg.name.trim() !== '') {
|
|
40
|
-
return pkg.name.trim()
|
|
41
|
-
}
|
|
42
|
-
} catch {
|
|
43
|
-
// fall through to default
|
|
44
|
-
}
|
|
45
|
-
return 'consumer-project'
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const fgRoot = findFgDevkitPackageRoot()
|
|
49
|
-
if (!fgRoot) {
|
|
50
|
-
console.error(
|
|
51
|
-
'[sync-core-coding-standards] Could not find fg-devkit (install fg-devkit or a package that bundles it, e.g. fds-vue-core).',
|
|
52
|
-
)
|
|
53
|
-
process.exit(1)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const sourcePath = path.join(fgRoot, '.cursor', 'rules', 'core-coding-standards.mdc')
|
|
57
|
-
if (!fs.existsSync(sourcePath)) {
|
|
58
|
-
console.error('[sync-core-coding-standards] Missing source rule in fg-devkit package.')
|
|
59
|
-
process.exit(1)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const destinationPath = path.join(projectRoot, '.cursor', 'rules', 'core-coding-standards.mdc')
|
|
63
|
-
const packageName = getConsumerPackageName()
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
const content = fs.readFileSync(sourcePath, 'utf8')
|
|
67
|
-
const updated = content.replace(
|
|
68
|
-
/^description:\s*.*$/m,
|
|
69
|
-
`description: Core coding standards for ${packageName}`,
|
|
70
|
-
)
|
|
71
|
-
fs.mkdirSync(path.dirname(destinationPath), { recursive: true })
|
|
72
|
-
fs.writeFileSync(destinationPath, updated, 'utf8')
|
|
73
|
-
console.log(`[sync-core-coding-standards] Updated ${destinationPath}`)
|
|
74
|
-
} catch (err) {
|
|
75
|
-
console.error(`[sync-core-coding-standards] ${err.message}`)
|
|
76
|
-
process.exit(1)
|
|
77
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* For library packages: copies versions from devDependencies/dependencies into peerDependencies
|
|
4
|
-
* for every key already listed in peerDependencies, so declared peers stay aligned with local installs.
|
|
5
|
-
*
|
|
6
|
-
* Does not add new peer keys; warns and skips peers that have no matching dev/dependency entry.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import fs from 'node:fs'
|
|
10
|
-
import path from 'node:path'
|
|
11
|
-
|
|
12
|
-
const pkgPath = path.resolve(process.cwd(), 'package.json')
|
|
13
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
|
|
14
|
-
|
|
15
|
-
const sources = { ...pkg.devDependencies, ...pkg.dependencies }
|
|
16
|
-
|
|
17
|
-
pkg.peerDependencies = pkg.peerDependencies || {}
|
|
18
|
-
const peerKeys = Object.keys(pkg.peerDependencies)
|
|
19
|
-
|
|
20
|
-
for (const name of peerKeys) {
|
|
21
|
-
const version = sources[name]
|
|
22
|
-
if (version) {
|
|
23
|
-
pkg.peerDependencies[name] = version
|
|
24
|
-
} else {
|
|
25
|
-
// If missing locally, leave existing peer version untouched
|
|
26
|
-
console.warn(`[sync-peers] Skipping ${name} (not found in devDependencies/dependencies)`)
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`)
|
|
31
|
-
console.warn('[sync-peers] peerDependencies updated from devDependencies')
|