@shrkcrft/presets 0.1.0-alpha.6 → 0.1.0-alpha.7
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/dist/builtin/builtin-presets.d.ts.map +1 -1
- package/dist/builtin/builtin-presets.js +15 -0
- package/dist/builtin/nest11-presets.d.ts +11 -0
- package/dist/builtin/nest11-presets.d.ts.map +1 -0
- package/dist/builtin/nest11-presets.js +257 -0
- package/dist/builtin/nest11-snippets.d.ts +32 -0
- package/dist/builtin/nest11-snippets.d.ts.map +1 -0
- package/dist/builtin/nest11-snippets.js +270 -0
- package/dist/builtin/react19-presets.d.ts +12 -0
- package/dist/builtin/react19-presets.d.ts.map +1 -0
- package/dist/builtin/react19-presets.js +299 -0
- package/dist/builtin/react19-snippets.d.ts +43 -0
- package/dist/builtin/react19-snippets.d.ts.map +1 -0
- package/dist/builtin/react19-snippets.js +363 -0
- package/dist/builtin/shared-snippets.d.ts +4 -0
- package/dist/builtin/shared-snippets.d.ts.map +1 -1
- package/dist/builtin/shared-snippets.js +49 -0
- package/package.json +4 -4
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
// React 19+ rule snippets.
|
|
2
|
+
//
|
|
3
|
+
// Covers: function components (no React.FC, no class components for new
|
|
4
|
+
// code), ref-as-a-prop (no more forwardRef in the common case), Context
|
|
5
|
+
// rendered as a provider directly, the new Actions surface (useActionState
|
|
6
|
+
// / useFormStatus / useOptimistic / use()), async transitions, document
|
|
7
|
+
// metadata in the component tree, React Compiler auto-memoization (and
|
|
8
|
+
// the implication for hand-rolled useMemo / useCallback), useEffect
|
|
9
|
+
// discipline (sync-with-external-systems ONLY), Suspense + concurrent
|
|
10
|
+
// features (useTransition / useDeferredValue), TanStack Query for server
|
|
11
|
+
// state and React Hook Form + Zod for forms, Vitest + Testing Library
|
|
12
|
+
// + userEvent + MSW testing patterns, and React Server Components +
|
|
13
|
+
// 'use client' + Server Actions for framework-driven fullstack apps.
|
|
14
|
+
//
|
|
15
|
+
// Each snippet is a string injected verbatim into a generated
|
|
16
|
+
// `sharkcraft/*.ts` file. `defineKnowledgeEntry`, `KnowledgeType`, and
|
|
17
|
+
// `KnowledgePriority` are provided by the local-mirror preamble the
|
|
18
|
+
// synthesizer prepends.
|
|
19
|
+
import { ruleSnippet } from "./r26-snippets.js";
|
|
20
|
+
// ─── Modern component shape ────────────────────────────────────────────────
|
|
21
|
+
export const REACT19_FUNCTION_COMPONENTS = ruleSnippet({
|
|
22
|
+
id: 'react19.function-components',
|
|
23
|
+
title: 'New components are function components — no class components',
|
|
24
|
+
priority: 'critical',
|
|
25
|
+
tags: ['react', 'react-19', 'components'],
|
|
26
|
+
appliesWhen: ['generate-component', 'refactor'],
|
|
27
|
+
content: 'Write every new component as a plain function: `export function Profile(props: ProfileProps) { ... }`. No `class extends Component`, no `React.PureComponent`. The only legitimate class-component touch is when porting / interop with a legacy boundary.',
|
|
28
|
+
});
|
|
29
|
+
export const REACT19_NO_REACT_FC = ruleSnippet({
|
|
30
|
+
id: 'react19.no-react-fc',
|
|
31
|
+
title: 'Do not type components as React.FC',
|
|
32
|
+
priority: 'high',
|
|
33
|
+
tags: ['react', 'react-19', 'typescript'],
|
|
34
|
+
appliesWhen: ['generate-component', 'review'],
|
|
35
|
+
content: 'Type the props directly and annotate the function: `function Profile(props: ProfileProps): ReactNode { ... }`. `React.FC` injects an implicit `children` prop, drops generics, and the community has moved away from it. If a component takes children, declare them in the props interface explicitly.',
|
|
36
|
+
});
|
|
37
|
+
export const REACT19_PROPS_INTERFACE = ruleSnippet({
|
|
38
|
+
id: 'react19.props-interface',
|
|
39
|
+
title: 'Declare props as an interface, not an inline type',
|
|
40
|
+
priority: 'medium',
|
|
41
|
+
tags: ['react', 'react-19', 'typescript', 'components'],
|
|
42
|
+
appliesWhen: ['generate-component'],
|
|
43
|
+
content: 'Define `interface IProfileProps { … }` (or `ProfileProps` if your style avoids the I-prefix) above the component. Inline `{ user, count }: { user: User; count: number }` is fine for one-off components but doesn\'t scale — once a prop type appears in two places, lift it into an interface.',
|
|
44
|
+
});
|
|
45
|
+
export const REACT19_REF_AS_PROP = ruleSnippet({
|
|
46
|
+
id: 'react19.ref-as-prop',
|
|
47
|
+
title: 'Pass ref as a regular prop — no forwardRef',
|
|
48
|
+
priority: 'high',
|
|
49
|
+
tags: ['react', 'react-19', 'components', 'refs'],
|
|
50
|
+
appliesWhen: ['generate-component', 'refactor'],
|
|
51
|
+
content: 'React 19 lets you accept `ref` as a normal prop: `function Input({ ref, ...props }: InputProps) { return <input ref={ref} {...props} /> }`. No more `forwardRef`, no more displayName boilerplate. Only keep `forwardRef` when you have to support a React <19 consumer.',
|
|
52
|
+
});
|
|
53
|
+
export const REACT19_CONTEXT_AS_PROVIDER = ruleSnippet({
|
|
54
|
+
id: 'react19.context-as-provider',
|
|
55
|
+
title: 'Use <Context> directly as the provider',
|
|
56
|
+
priority: 'medium',
|
|
57
|
+
tags: ['react', 'react-19', 'context'],
|
|
58
|
+
appliesWhen: ['generate-component'],
|
|
59
|
+
content: 'React 19 renders `<MyContext value={x}>{...}</MyContext>` instead of `<MyContext.Provider value={x}>`. The `.Provider` form is deprecated for new code. Consumer-side, prefer the `use(MyContext)` hook over `useContext(MyContext)` — `use()` works inside conditionals too.',
|
|
60
|
+
});
|
|
61
|
+
export const REACT19_DOCUMENT_METADATA = ruleSnippet({
|
|
62
|
+
id: 'react19.document-metadata',
|
|
63
|
+
title: 'Render <title> / <meta> / <link> inside the component tree',
|
|
64
|
+
priority: 'medium',
|
|
65
|
+
tags: ['react', 'react-19', 'metadata', 'seo'],
|
|
66
|
+
appliesWhen: ['generate-component', 'add-page'],
|
|
67
|
+
content: 'React 19 hoists `<title>`, `<meta>`, and `<link>` tags out of components and into `<head>`. No more react-helmet, no more `useEffect(() => { document.title = x; })`. For Next.js app router, this works alongside the framework\'s metadata API.',
|
|
68
|
+
});
|
|
69
|
+
export const REACT19_STYLESHEETS_IN_TREE = ruleSnippet({
|
|
70
|
+
id: 'react19.stylesheets-in-tree',
|
|
71
|
+
title: 'Use <link rel="stylesheet" precedence> for scoped stylesheets',
|
|
72
|
+
priority: 'low',
|
|
73
|
+
tags: ['react', 'react-19', 'styles'],
|
|
74
|
+
appliesWhen: ['generate-component'],
|
|
75
|
+
content: 'React 19 deduplicates `<link rel="stylesheet" href="..." precedence="default" />` rendered inside components and orders them by precedence. Use this for component-scoped stylesheet loading without manual `<head>` manipulation.',
|
|
76
|
+
});
|
|
77
|
+
export const REACT19_SELF_CLOSING = ruleSnippet({
|
|
78
|
+
id: 'react19.self-closing',
|
|
79
|
+
title: 'Self-close JSX elements with no children',
|
|
80
|
+
priority: 'low',
|
|
81
|
+
tags: ['react', 'react-19', 'jsx'],
|
|
82
|
+
appliesWhen: ['generate-component'],
|
|
83
|
+
content: 'Use `<Component prop="x" />` for elements with no children, not `<Component prop="x"></Component>`. Standard JSX convention; eslint-plugin-react has `self-closing-comp` to enforce it.',
|
|
84
|
+
});
|
|
85
|
+
// ─── Hooks discipline ─────────────────────────────────────────────────────
|
|
86
|
+
export const REACT19_RULES_OF_HOOKS = ruleSnippet({
|
|
87
|
+
id: 'react19.rules-of-hooks',
|
|
88
|
+
title: 'Hooks at the top of the function, never conditional',
|
|
89
|
+
priority: 'critical',
|
|
90
|
+
tags: ['react', 'react-19', 'hooks'],
|
|
91
|
+
appliesWhen: ['generate-component', 'generate-hook'],
|
|
92
|
+
content: 'Call hooks at the top level — never inside conditionals, loops, or nested functions. They identify by call order. Run `eslint-plugin-react-hooks` with `rules-of-hooks` and `exhaustive-deps` both as errors, not warnings.',
|
|
93
|
+
});
|
|
94
|
+
export const REACT19_USE_EFFECT_FOR_EXTERNAL_SYNC = ruleSnippet({
|
|
95
|
+
id: 'react19.use-effect-for-external-sync',
|
|
96
|
+
title: 'useEffect is for syncing with external systems — nothing else',
|
|
97
|
+
priority: 'critical',
|
|
98
|
+
tags: ['react', 'react-19', 'hooks', 'effects'],
|
|
99
|
+
appliesWhen: ['generate-component', 'review'],
|
|
100
|
+
content: 'useEffect is the escape hatch into the world outside React (DOM subscriptions, web APIs, timers, non-React state stores). Anything that can be derived during render goes in render. Anything that responds to a user event goes in an event handler. Anything that resets state when a prop changes uses a `key` prop on the consuming component, not an effect.',
|
|
101
|
+
});
|
|
102
|
+
export const REACT19_NO_DERIVED_STATE_IN_EFFECT = ruleSnippet({
|
|
103
|
+
id: 'react19.no-derived-state-in-effect',
|
|
104
|
+
title: 'Derived state is computed during render, not in useEffect',
|
|
105
|
+
priority: 'critical',
|
|
106
|
+
tags: ['react', 'react-19', 'hooks', 'effects'],
|
|
107
|
+
appliesWhen: ['generate-component', 'review'],
|
|
108
|
+
content: 'If `derived` is a pure function of `a` and `b`, write `const derived = compute(a, b)` inline — not `const [derived, setDerived] = useState(); useEffect(() => setDerived(compute(a, b)), [a, b])`. The effect path runs an extra render, makes the UI flash, and breaks under StrictMode.',
|
|
109
|
+
});
|
|
110
|
+
export const REACT19_NO_FETCH_IN_EFFECT = ruleSnippet({
|
|
111
|
+
id: 'react19.no-fetch-in-effect',
|
|
112
|
+
title: 'Don\'t fetch in useEffect — use a server-state library',
|
|
113
|
+
priority: 'high',
|
|
114
|
+
tags: ['react', 'react-19', 'effects', 'data'],
|
|
115
|
+
appliesWhen: ['generate-component'],
|
|
116
|
+
content: 'Manual fetch-in-useEffect re-implements caching, deduping, refetching, race-condition handling, and error states badly. Use TanStack Query (React Query), SWR, RTK Query, or React 19\'s `use(promise)` inside a Suspense boundary. The only useEffect for I/O is for setting up a long-lived subscription (WebSocket / SSE) that the libraries don\'t cover.',
|
|
117
|
+
});
|
|
118
|
+
export const REACT19_CUSTOM_HOOK_NAMING = ruleSnippet({
|
|
119
|
+
id: 'react19.custom-hook-naming',
|
|
120
|
+
title: 'Custom hooks start with `use` and live next to consumers',
|
|
121
|
+
priority: 'medium',
|
|
122
|
+
tags: ['react', 'react-19', 'hooks'],
|
|
123
|
+
appliesWhen: ['generate-hook'],
|
|
124
|
+
content: 'Custom hooks export a function named `useFoo`. The naming is what enables eslint-plugin-react-hooks to verify the rules. Co-locate the hook with the component(s) that consume it; promote to `src/hooks/` only when used across features.',
|
|
125
|
+
});
|
|
126
|
+
export const REACT19_EFFECT_CLEANUP = ruleSnippet({
|
|
127
|
+
id: 'react19.effect-cleanup',
|
|
128
|
+
title: 'Every useEffect with a subscription returns a cleanup',
|
|
129
|
+
priority: 'high',
|
|
130
|
+
tags: ['react', 'react-19', 'effects'],
|
|
131
|
+
appliesWhen: ['generate-component'],
|
|
132
|
+
content: 'If the effect adds a listener, opens a socket, sets a timer, or subscribes to anything, return a function that tears it down. StrictMode mounts components twice in dev specifically to surface missing cleanups — fix them in dev, not in production.',
|
|
133
|
+
});
|
|
134
|
+
// ─── Actions / forms (React 19) ───────────────────────────────────────────
|
|
135
|
+
export const REACT19_FORM_ACTIONS = ruleSnippet({
|
|
136
|
+
id: 'react19.form-actions',
|
|
137
|
+
title: 'Use <form action> with Actions for submission',
|
|
138
|
+
priority: 'high',
|
|
139
|
+
tags: ['react', 'react-19', 'forms', 'actions'],
|
|
140
|
+
appliesWhen: ['generate-component', 'generate-form'],
|
|
141
|
+
content: 'React 19 forms call their `action` prop with the FormData on submit: `<form action={async (fd) => save(fd)}>`. The DOM form is reset on success automatically. Pair with useActionState for pending / result state and useFormStatus inside child components for spinners — no more manual onSubmit + e.preventDefault + isSubmitting state.',
|
|
142
|
+
});
|
|
143
|
+
export const REACT19_USE_ACTION_STATE = ruleSnippet({
|
|
144
|
+
id: 'react19.use-action-state',
|
|
145
|
+
title: 'useActionState replaces "result + pending + error" state triplets',
|
|
146
|
+
priority: 'high',
|
|
147
|
+
tags: ['react', 'react-19', 'actions', 'hooks'],
|
|
148
|
+
appliesWhen: ['generate-component', 'generate-form'],
|
|
149
|
+
content: '`const [state, formAction, isPending] = useActionState(action, initialState)`. The hook owns pending state, the most recent return value of `action`, and re-invocations across submits. Wire `formAction` to the form\'s `action` prop; the hook handles the rest.',
|
|
150
|
+
});
|
|
151
|
+
export const REACT19_USE_FORM_STATUS = ruleSnippet({
|
|
152
|
+
id: 'react19.use-form-status',
|
|
153
|
+
title: 'useFormStatus inside form children for pending UI',
|
|
154
|
+
priority: 'medium',
|
|
155
|
+
tags: ['react', 'react-19', 'actions', 'hooks'],
|
|
156
|
+
appliesWhen: ['generate-component'],
|
|
157
|
+
content: 'A submit button that needs to know whether the parent form is mid-submission calls `const { pending } = useFormStatus()` — no prop drilling, no shared state. Only works inside a `<form>` descendant.',
|
|
158
|
+
});
|
|
159
|
+
export const REACT19_USE_OPTIMISTIC = ruleSnippet({
|
|
160
|
+
id: 'react19.use-optimistic',
|
|
161
|
+
title: 'useOptimistic for instant UI on mutations',
|
|
162
|
+
priority: 'medium',
|
|
163
|
+
tags: ['react', 'react-19', 'actions', 'optimistic'],
|
|
164
|
+
appliesWhen: ['generate-component'],
|
|
165
|
+
content: '`const [optimisticList, addOptimistic] = useOptimistic(list, (cur, item) => [...cur, item])`. Call `addOptimistic(newItem)` before kicking off the server action; if the action fails, React automatically reverts. Skip the manual rollback-on-error machinery.',
|
|
166
|
+
});
|
|
167
|
+
export const REACT19_USE_HOOK = ruleSnippet({
|
|
168
|
+
id: 'react19.use-hook',
|
|
169
|
+
title: 'use() to read promises and contexts conditionally',
|
|
170
|
+
priority: 'high',
|
|
171
|
+
tags: ['react', 'react-19', 'hooks'],
|
|
172
|
+
appliesWhen: ['generate-component'],
|
|
173
|
+
content: '`const data = use(promise)` unwraps a thrown-promise into a Suspense boundary. `use(MyContext)` reads context — and unlike useContext, it works inside conditionals and loops. Don\'t create the promise inside the component body on every render — pass it in as a prop or pull it from a cache.',
|
|
174
|
+
});
|
|
175
|
+
export const REACT19_ASYNC_TRANSITIONS = ruleSnippet({
|
|
176
|
+
id: 'react19.async-transitions',
|
|
177
|
+
title: 'startTransition / useTransition accept async functions',
|
|
178
|
+
priority: 'medium',
|
|
179
|
+
tags: ['react', 'react-19', 'concurrent', 'transitions'],
|
|
180
|
+
appliesWhen: ['generate-component'],
|
|
181
|
+
content: 'React 19 lets you pass an async function to `startTransition` / `useTransition`. Use it for "navigate then load data" or "mutate then revalidate" flows — `isPending` stays true until the async work resolves, so the UI can show a quiet pending state without jank.',
|
|
182
|
+
});
|
|
183
|
+
// ─── State management ────────────────────────────────────────────────────
|
|
184
|
+
export const REACT19_SERVER_STATE_LIBRARY = ruleSnippet({
|
|
185
|
+
id: 'react19.server-state-library',
|
|
186
|
+
title: 'Server state lives in a query library, not useState',
|
|
187
|
+
priority: 'critical',
|
|
188
|
+
tags: ['react', 'react-19', 'state', 'data'],
|
|
189
|
+
appliesWhen: ['generate-component', 'fetch-data'],
|
|
190
|
+
content: 'TanStack Query (React Query), SWR, or RTK Query own the cache for data that originates on the server. They handle dedup, background refetch, stale-while-revalidate, invalidation, optimistic updates, and SSR hydration. Putting fetched data in `useState` re-implements them — badly.',
|
|
191
|
+
});
|
|
192
|
+
export const REACT19_CLIENT_STATE_PROPORTIONAL = ruleSnippet({
|
|
193
|
+
id: 'react19.client-state-proportional',
|
|
194
|
+
title: 'Pick client state shape to match its scope',
|
|
195
|
+
priority: 'high',
|
|
196
|
+
tags: ['react', 'react-19', 'state'],
|
|
197
|
+
appliesWhen: ['generate-component'],
|
|
198
|
+
content: 'Local: `useState` / `useReducer`. Within a subtree: lift state to the lowest common ancestor. Cross-tree, infrequent updates: Context. Cross-tree, frequent updates: a real store (Zustand for ergonomics, Jotai for atomic, Redux Toolkit for time-travel debugging). Don\'t use Context for high-frequency updates — every consumer re-renders on every change.',
|
|
199
|
+
});
|
|
200
|
+
export const REACT19_FORMS_LIBRARY = ruleSnippet({
|
|
201
|
+
id: 'react19.forms-library',
|
|
202
|
+
title: 'Non-trivial forms use React Hook Form + Zod (or React 19 Actions)',
|
|
203
|
+
priority: 'medium',
|
|
204
|
+
tags: ['react', 'react-19', 'forms'],
|
|
205
|
+
appliesWhen: ['generate-form'],
|
|
206
|
+
content: 'Forms with conditional fields, async validation, dirty-tracking, or complex submit state belong in React Hook Form + Zod (`zodResolver`). Forms that are pure submit-and-forget can use React 19 Actions + useActionState directly. Controlled-state-only forms ("a useState per field") work for 2-field forms and break around field 5.',
|
|
207
|
+
});
|
|
208
|
+
export const REACT19_AVOID_PROP_DRILLING = ruleSnippet({
|
|
209
|
+
id: 'react19.avoid-prop-drilling',
|
|
210
|
+
title: 'Lift state only as high as it needs to go',
|
|
211
|
+
priority: 'medium',
|
|
212
|
+
tags: ['react', 'react-19', 'state', 'composition'],
|
|
213
|
+
appliesWhen: ['refactor'],
|
|
214
|
+
content: 'If a piece of state lives 4+ components above its consumers, refactor: extract a Context, move it to a store, or restructure via composition (pass children instead of props). Long prop-drilling chains are a refactoring signal, not a permanent fixture.',
|
|
215
|
+
});
|
|
216
|
+
export const REACT19_KEYS_FOR_RESET = ruleSnippet({
|
|
217
|
+
id: 'react19.keys-for-reset',
|
|
218
|
+
title: 'Reset component state with a key prop, not useEffect',
|
|
219
|
+
priority: 'high',
|
|
220
|
+
tags: ['react', 'react-19', 'state'],
|
|
221
|
+
appliesWhen: ['generate-component'],
|
|
222
|
+
content: 'To force a component to reset its internal state when an input changes (e.g. switching to a different user), pass `<Editor key={userId} user={user} />`. React unmounts the old instance and mounts a fresh one. No useEffect that watches userId and calls a bunch of setStates.',
|
|
223
|
+
});
|
|
224
|
+
// ─── Performance ─────────────────────────────────────────────────────────
|
|
225
|
+
export const REACT19_COMPILER_AUTO_MEMO = ruleSnippet({
|
|
226
|
+
id: 'react19.compiler-auto-memo',
|
|
227
|
+
title: 'React Compiler memoizes for you — drop hand-rolled useMemo/useCallback',
|
|
228
|
+
priority: 'high',
|
|
229
|
+
tags: ['react', 'react-19', 'performance', 'compiler'],
|
|
230
|
+
appliesWhen: ['configure', 'review'],
|
|
231
|
+
content: 'Enable the React Compiler (babel-plugin-react-compiler) and it inserts memoization automatically. Hand-written useMemo / useCallback / React.memo become noise — keep them only on the few hot paths the profiler proves the compiler can\'t optimise. With the compiler off, hand-memoize ONLY when measured (DevTools profiler), not preemptively.',
|
|
232
|
+
});
|
|
233
|
+
export const REACT19_LAZY_SUSPENSE = ruleSnippet({
|
|
234
|
+
id: 'react19.lazy-suspense',
|
|
235
|
+
title: 'Code-split heavy / rarely-used components with React.lazy + Suspense',
|
|
236
|
+
priority: 'high',
|
|
237
|
+
tags: ['react', 'react-19', 'performance', 'code-splitting'],
|
|
238
|
+
appliesWhen: ['add-route', 'optimize-bundle'],
|
|
239
|
+
content: 'Route-level components, modals, heavy editors, and large dependency islands go behind `React.lazy(() => import(...))` + `<Suspense fallback={...}>`. Each lazy import becomes its own chunk. Don\'t lazy-load above-the-fold critical components — the network round-trip costs more than the bundle saving.',
|
|
240
|
+
});
|
|
241
|
+
export const REACT19_VIRTUALIZE_LISTS = ruleSnippet({
|
|
242
|
+
id: 'react19.virtualize-lists',
|
|
243
|
+
title: 'Virtualize lists past ~100 visible items',
|
|
244
|
+
priority: 'high',
|
|
245
|
+
tags: ['react', 'react-19', 'performance', 'lists'],
|
|
246
|
+
appliesWhen: ['generate-component'],
|
|
247
|
+
content: 'A list that can grow past a few hundred rows uses a virtualizer (TanStack Virtual, react-window, react-virtuoso). Rendering 10k DOM nodes blocks the main thread regardless of how clever your memoization is.',
|
|
248
|
+
});
|
|
249
|
+
export const REACT19_STABLE_KEYS = ruleSnippet({
|
|
250
|
+
id: 'react19.stable-keys',
|
|
251
|
+
title: 'List keys are stable and unique — never the array index',
|
|
252
|
+
priority: 'critical',
|
|
253
|
+
tags: ['react', 'react-19', 'lists', 'performance'],
|
|
254
|
+
appliesWhen: ['generate-component', 'review'],
|
|
255
|
+
content: 'Use the item\'s id (or a stable composite). `key={index}` makes React reuse DOM nodes when you reorder, splice, or filter — state from row 3 leaks into row 2. The only safe use of `key={index}` is for a static list that never changes shape.',
|
|
256
|
+
});
|
|
257
|
+
export const REACT19_IMAGE_OPTIMIZATION = ruleSnippet({
|
|
258
|
+
id: 'react19.image-optimization',
|
|
259
|
+
title: 'Images: explicit width/height, lazy by default, framework helper if available',
|
|
260
|
+
priority: 'high',
|
|
261
|
+
tags: ['react', 'react-19', 'performance', 'images'],
|
|
262
|
+
appliesWhen: ['generate-component'],
|
|
263
|
+
content: 'Every `<img>` has width + height attributes (prevents layout shift) and `loading="lazy"` for below-the-fold images. In Next.js use `<Image>`; in Vite SPAs use a CDN with responsive `srcset` (or imgix / Cloudinary / Cloudflare Images). Don\'t ship a 4MB hero JPEG in 2026.',
|
|
264
|
+
});
|
|
265
|
+
// ─── Concurrent rendering ────────────────────────────────────────────────
|
|
266
|
+
export const REACT19_USE_TRANSITION = ruleSnippet({
|
|
267
|
+
id: 'react19.use-transition',
|
|
268
|
+
title: 'Wrap expensive state updates in startTransition / useTransition',
|
|
269
|
+
priority: 'high',
|
|
270
|
+
tags: ['react', 'react-19', 'concurrent', 'performance'],
|
|
271
|
+
appliesWhen: ['generate-component'],
|
|
272
|
+
content: 'For state updates whose work blocks the input (filter typing, tab switching, sort changes), call `startTransition(() => setSlow(x))`. React keeps the input responsive and processes the slow update at lower priority. Pair with `useDeferredValue` when you want to lag a derived render behind the source signal.',
|
|
273
|
+
});
|
|
274
|
+
export const REACT19_USE_DEFERRED_VALUE = ruleSnippet({
|
|
275
|
+
id: 'react19.use-deferred-value',
|
|
276
|
+
title: 'useDeferredValue for derived expensive renders',
|
|
277
|
+
priority: 'medium',
|
|
278
|
+
tags: ['react', 'react-19', 'concurrent'],
|
|
279
|
+
appliesWhen: ['generate-component'],
|
|
280
|
+
content: '`const slow = useDeferredValue(query)` lets the input update immediately while the slow downstream view (filtered list, charts) re-renders at lower priority. React 19 takes an `initialValue` argument so SSR-hydrated pages render the cheap form first.',
|
|
281
|
+
});
|
|
282
|
+
export const REACT19_SUSPENSE_BOUNDARIES = ruleSnippet({
|
|
283
|
+
id: 'react19.suspense-boundaries',
|
|
284
|
+
title: 'Plan Suspense boundaries deliberately',
|
|
285
|
+
priority: 'high',
|
|
286
|
+
tags: ['react', 'react-19', 'concurrent', 'suspense'],
|
|
287
|
+
appliesWhen: ['generate-component'],
|
|
288
|
+
content: 'Each `<Suspense>` boundary defines what falls back when a `use(promise)` inside it is pending. Put boundaries at the level of UI that should reveal together; placing one at the page root and another at the sidebar lets them stream independently. Don\'t over-nest — too many boundaries cause UI thrash.',
|
|
289
|
+
});
|
|
290
|
+
export const REACT19_STRICT_MODE = ruleSnippet({
|
|
291
|
+
id: 'react19.strict-mode',
|
|
292
|
+
title: 'Run StrictMode in dev, fix what it surfaces',
|
|
293
|
+
priority: 'high',
|
|
294
|
+
tags: ['react', 'react-19', 'concurrent', 'safety'],
|
|
295
|
+
appliesWhen: ['configure'],
|
|
296
|
+
content: 'Wrap the dev tree in `<StrictMode>`. It runs effects, reducers, and constructors twice in dev to catch missing cleanups, accidental shared state, and impure renders. If your code breaks under StrictMode, it will break under React\'s concurrent features in production.',
|
|
297
|
+
});
|
|
298
|
+
// ─── Testing ─────────────────────────────────────────────────────────────
|
|
299
|
+
export const REACT19_VITEST = ruleSnippet({
|
|
300
|
+
id: 'react19.vitest',
|
|
301
|
+
title: 'Vitest is the default test runner for Vite-based React apps',
|
|
302
|
+
priority: 'high',
|
|
303
|
+
tags: ['react', 'react-19', 'testing'],
|
|
304
|
+
appliesWhen: ['configure', 'generate-test'],
|
|
305
|
+
content: 'Vitest is ESM-first, Vite-native, and runs ~5× faster than Jest in dev for the same suite. The Jest API surface is compatible. Stick with Jest only if you have heavy Jest infrastructure that hasn\'t been ported.',
|
|
306
|
+
});
|
|
307
|
+
export const REACT19_TESTING_LIBRARY = ruleSnippet({
|
|
308
|
+
id: 'react19.testing-library',
|
|
309
|
+
title: 'Test through the DOM with @testing-library/react + userEvent',
|
|
310
|
+
priority: 'critical',
|
|
311
|
+
tags: ['react', 'react-19', 'testing'],
|
|
312
|
+
appliesWhen: ['generate-test'],
|
|
313
|
+
content: 'Render the component, query by role / label / text (NOT by class name or test-id-everywhere), and drive interactions with `userEvent.setup()` — never `fireEvent` unless you need a raw DOM event. The Testing Library guideline: "the more your tests resemble the way your software is used, the more confidence they can give you".',
|
|
314
|
+
});
|
|
315
|
+
export const REACT19_TEST_BEHAVIOR_NOT_IMPL = ruleSnippet({
|
|
316
|
+
id: 'react19.test-behavior-not-impl',
|
|
317
|
+
title: 'Test behavior, not implementation details',
|
|
318
|
+
priority: 'high',
|
|
319
|
+
tags: ['react', 'react-19', 'testing'],
|
|
320
|
+
appliesWhen: ['generate-test', 'review'],
|
|
321
|
+
content: 'A test that asserts "useState was called with x" or "this internal hook fired" breaks every refactor without proving anything about user-visible behavior. Assert what the user sees and can do: text on screen, fields, buttons, navigation. If the test passes after gutting the implementation, you wrote it well.',
|
|
322
|
+
});
|
|
323
|
+
export const REACT19_MSW = ruleSnippet({
|
|
324
|
+
id: 'react19.msw',
|
|
325
|
+
title: 'Mock HTTP at the network layer with MSW',
|
|
326
|
+
priority: 'high',
|
|
327
|
+
tags: ['react', 'react-19', 'testing', 'http'],
|
|
328
|
+
appliesWhen: ['generate-test'],
|
|
329
|
+
content: 'Mock Service Worker intercepts fetch/XHR at the network layer, so your component code runs unchanged. Define handlers once in `src/test/handlers.ts` and override per-test with `server.use(...)`. Beats per-test `jest.mock` of fetch by a wide margin.',
|
|
330
|
+
});
|
|
331
|
+
// ─── React Server Components (framework apps) ────────────────────────────
|
|
332
|
+
export const REACT19_SERVER_COMPONENTS_DEFAULT = ruleSnippet({
|
|
333
|
+
id: 'react19.server-components-default',
|
|
334
|
+
title: 'In RSC frameworks, components are server-rendered by default',
|
|
335
|
+
priority: 'critical',
|
|
336
|
+
tags: ['react', 'react-19', 'rsc'],
|
|
337
|
+
appliesWhen: ['generate-component'],
|
|
338
|
+
content: 'In Next.js app router (or any RSC framework), the default is a Server Component — runs on the server, ships zero JS for itself, can be async, can read from a database directly. Only opt into a Client Component when you need interactivity (state, effects, event handlers, browser-only APIs). Going server-first keeps bundle size small.',
|
|
339
|
+
});
|
|
340
|
+
export const REACT19_USE_CLIENT_BOUNDARY = ruleSnippet({
|
|
341
|
+
id: 'react19.use-client-boundary',
|
|
342
|
+
title: 'Push "use client" as far down the tree as possible',
|
|
343
|
+
priority: 'high',
|
|
344
|
+
tags: ['react', 'react-19', 'rsc'],
|
|
345
|
+
appliesWhen: ['generate-component', 'refactor'],
|
|
346
|
+
content: 'A `"use client"` directive marks a Client Component AND every component imported by it. Place it on the smallest leaf that actually needs client behavior — the form, the toggle, the chart — not on the entire page. A page-level "use client" defeats the point of RSC.',
|
|
347
|
+
});
|
|
348
|
+
export const REACT19_SERVER_ACTIONS = ruleSnippet({
|
|
349
|
+
id: 'react19.server-actions',
|
|
350
|
+
title: 'Server Actions over manual API routes for mutations',
|
|
351
|
+
priority: 'high',
|
|
352
|
+
tags: ['react', 'react-19', 'rsc', 'actions'],
|
|
353
|
+
appliesWhen: ['generate-form', 'mutate-data'],
|
|
354
|
+
content: 'In RSC frameworks, define a server function with `"use server"` and pass it directly as a form `action` or call it from an event handler. No manual API route, no manual fetch + JSON serialize. Validate the input at the top of the action (zod) — never trust a payload just because it came in over the action wire.',
|
|
355
|
+
});
|
|
356
|
+
export const REACT19_STREAMING_SSR = ruleSnippet({
|
|
357
|
+
id: 'react19.streaming-ssr',
|
|
358
|
+
title: 'Stream SSR with Suspense boundaries for fast TTFB',
|
|
359
|
+
priority: 'medium',
|
|
360
|
+
tags: ['react', 'react-19', 'ssr', 'rsc'],
|
|
361
|
+
appliesWhen: ['configure'],
|
|
362
|
+
content: 'Server-rendered apps stream HTML through Suspense boundaries: the shell ships first, slow data fills in as it resolves. Wrap slow data fetches in a Suspense boundary so they don\'t hold up the shell. Don\'t await every fetch at the top of the route — it blocks TTFB on the slowest one.',
|
|
363
|
+
});
|
|
@@ -17,6 +17,10 @@ export declare const WORKSPACE_PATH_APPS = "defineKnowledgeEntry({\n id: 'pat
|
|
|
17
17
|
export declare const ANGULAR_PATH_APP = "defineKnowledgeEntry({\n id: 'paths.angular.app',\n title: 'Angular app root',\n type: KnowledgeType.Path,\n priority: KnowledgePriority.Critical,\n tags: ['paths', 'angular', 'app'],\n scope: ['angular'],\n appliesWhen: ['create-feature', 'generate-code'],\n content: 'Angular workspace source lives under src/app/. Components, services, pipes and modules sit under here. Tests are co-located as *.spec.ts beside the unit under test.',\n metadata: { path: 'src/app' },\n })";
|
|
18
18
|
export declare const ANGULAR_PATH_COMPONENTS = "defineKnowledgeEntry({\n id: 'paths.angular.components',\n title: 'Angular components',\n type: KnowledgeType.Path,\n priority: KnowledgePriority.High,\n tags: ['paths', 'angular', 'components'],\n scope: ['angular'],\n appliesWhen: ['generate-component'],\n content: 'Components live under src/app/components/ or src/app/<feature>/. Use the .component.ts suffix. Pair each component with a co-located *.spec.ts test.',\n metadata: { path: 'src/app/components' },\n })";
|
|
19
19
|
export declare const ANGULAR_PATH_SERVICES = "defineKnowledgeEntry({\n id: 'paths.angular.services',\n title: 'Angular services',\n type: KnowledgeType.Path,\n priority: KnowledgePriority.High,\n tags: ['paths', 'angular', 'services'],\n scope: ['angular'],\n appliesWhen: ['generate-service'],\n content: 'Injectable services live under src/app/services/ (or alongside their feature folder). Use the .service.ts suffix. Provide via providedIn root unless feature-scoped.',\n metadata: { path: 'src/app/services' },\n })";
|
|
20
|
+
export declare const REACT_PATH_COMPONENTS = "defineKnowledgeEntry({\n id: 'paths.react.components',\n title: 'React components',\n type: KnowledgeType.Path,\n priority: KnowledgePriority.High,\n tags: ['paths', 'react', 'components'],\n scope: ['react'],\n appliesWhen: ['generate-component'],\n content: 'Components live under src/components/ (cross-feature shared) or under their feature folder. Keep each component in its own file; pair with a co-located *.test.tsx beside it.',\n metadata: { path: 'src/components' },\n })";
|
|
21
|
+
export declare const REACT_PATH_HOOKS = "defineKnowledgeEntry({\n id: 'paths.react.hooks',\n title: 'React custom hooks',\n type: KnowledgeType.Path,\n priority: KnowledgePriority.High,\n tags: ['paths', 'react', 'hooks'],\n scope: ['react'],\n appliesWhen: ['generate-hook'],\n content: 'Custom hooks live under src/hooks/ (cross-feature) or under their feature folder. File and exported function are both named useX. Co-locate the test as useX.test.ts beside the hook.',\n metadata: { path: 'src/hooks' },\n })";
|
|
22
|
+
export declare const REACT_PATH_PAGES = "defineKnowledgeEntry({\n id: 'paths.react.pages',\n title: 'React route / page components',\n type: KnowledgeType.Path,\n priority: KnowledgePriority.Medium,\n tags: ['paths', 'react', 'pages', 'routing'],\n scope: ['react'],\n appliesWhen: ['create-feature', 'add-route'],\n content: 'Top-level route components live under src/pages/ (React Router / TanStack Router convention) or under src/routes/. For Next.js app router, see src/app/<segment>/page.tsx instead \u2014 adjust this entry if your project uses that layout.',\n metadata: { path: 'src/pages' },\n })";
|
|
23
|
+
export declare const REACT_PATH_LIB = "defineKnowledgeEntry({\n id: 'paths.react.lib',\n title: 'React app utilities + clients',\n type: KnowledgeType.Path,\n priority: KnowledgePriority.Medium,\n tags: ['paths', 'react', 'lib'],\n scope: ['react'],\n appliesWhen: ['generate-utility', 'generate-code'],\n content: 'Framework-agnostic helpers (formatters, validators, API clients) live under src/lib/. Keep them pure \u2014 no React imports unless the helper is a hook (in which case it belongs under src/hooks/).',\n metadata: { path: 'src/lib' },\n })";
|
|
20
24
|
export declare const NEST_PATH_SRC = "defineKnowledgeEntry({\n id: 'paths.nest.src',\n title: 'Nest module roots',\n type: KnowledgeType.Path,\n priority: KnowledgePriority.Critical,\n tags: ['paths', 'nest', 'modules'],\n scope: ['nestjs'],\n appliesWhen: ['create-feature', 'generate-service', 'generate-code'],\n content: 'Nest source lives under src/. Each feature gets a folder src/<feature>/ containing controller, service, module, and DTOs (one construct per file). Controllers stay thin; business logic lives in services.',\n metadata: { path: 'src' },\n })";
|
|
21
25
|
export declare const NEST_PATH_E2E = "defineKnowledgeEntry({\n id: 'paths.nest.e2e',\n title: 'Nest e2e tests',\n type: KnowledgeType.Path,\n priority: KnowledgePriority.Medium,\n tags: ['paths', 'nest', 'testing'],\n scope: ['nestjs'],\n appliesWhen: ['generate-test'],\n content: 'End-to-end tests live under test/ (Nest convention, not tests/). Unit tests can be co-located as *.spec.ts next to the unit.',\n metadata: { path: 'test' },\n })";
|
|
22
26
|
export declare const JAVA_MAVEN_PATH_MAIN = "defineKnowledgeEntry({\n id: 'paths.java.maven.main',\n title: 'Java Maven main source',\n type: KnowledgeType.Path,\n priority: KnowledgePriority.Critical,\n tags: ['paths', 'java', 'maven'],\n scope: ['java', 'maven'],\n appliesWhen: ['generate-code'],\n content: 'Main Java source lives under src/main/java/<package>/. Resources under src/main/resources/. Mirror tests under src/test/java/.',\n metadata: { path: 'src/main/java' },\n })";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-snippets.d.ts","sourceRoot":"","sources":["../../src/builtin/shared-snippets.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB,m9BAuB7B,CAAC;AAEN,eAAO,MAAM,kBAAkB,45BAoB1B,CAAC;AAEN,eAAO,MAAM,2BAA2B,s9CA6CnC,CAAC;AAEN,eAAO,MAAM,4BAA4B,s5BA6BpC,CAAC;AAEN,eAAO,MAAM,yBAAyB,yVAajC,CAAC;AAEN,eAAO,MAAM,uBAAuB,mtBAkB/B,CAAC;AAEN,eAAO,MAAM,uBAAuB,8mBAiB/B,CAAC;AAEN,eAAO,MAAM,oBAAoB,ytBAkB5B,CAAC;AAKN,eAAO,MAAM,gBAAgB,8JAK3B,CAAC;AAEH,eAAO,MAAM,oBAAoB,sYAU5B,CAAC;AAEN,eAAO,MAAM,iBAAiB,mWASzB,CAAC;AAEN,eAAO,MAAM,iBAAiB,yVASzB,CAAC;AAaN,eAAO,MAAM,YAAY,sgBAUpB,CAAC;AAEN,eAAO,MAAM,YAAY,meAUpB,CAAC;AAIN,eAAO,MAAM,uBAAuB,6kBAU/B,CAAC;AAEN,eAAO,MAAM,mBAAmB,keAU3B,CAAC;AAGN,eAAO,MAAM,gBAAgB,2fAUxB,CAAC;AAEN,eAAO,MAAM,uBAAuB,qfAU/B,CAAC;AAEN,eAAO,MAAM,qBAAqB,2fAU7B,CAAC;AAGN,eAAO,MAAM,aAAa,gjBAUrB,CAAC;AAEN,eAAO,MAAM,aAAa,ubAUrB,CAAC;AAIN,eAAO,MAAM,oBAAoB,wdAU5B,CAAC;AAEN,eAAO,MAAM,qBAAqB,2ZAU7B,CAAC;AAEN,eAAO,MAAM,eAAe,4bAUvB,CAAC;AAEN,eAAO,MAAM,iBAAiB,uYAUzB,CAAC;AAEN,eAAO,MAAM,WAAW,4ZAUnB,CAAC;AAEN,eAAO,MAAM,WAAW,gZAUnB,CAAC;AAEN,eAAO,MAAM,gBAAgB,kbAUxB,CAAC;AAEN,eAAO,MAAM,aAAa,mcAUrB,CAAC;AAEN,eAAO,MAAM,eAAe,icAUvB,CAAC;AAEN,eAAO,MAAM,4BAA4B,+XAQpC,CAAC;AAEN,eAAO,MAAM,sBAAsB,0XAQ9B,CAAC;AAEN,eAAO,MAAM,iCAAiC,6VAQzC,CAAC;AAEN,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,EAAE,MAAM,MAAM,KAAG,MAK1D,CAAC"}
|
|
1
|
+
{"version":3,"file":"shared-snippets.d.ts","sourceRoot":"","sources":["../../src/builtin/shared-snippets.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB,m9BAuB7B,CAAC;AAEN,eAAO,MAAM,kBAAkB,45BAoB1B,CAAC;AAEN,eAAO,MAAM,2BAA2B,s9CA6CnC,CAAC;AAEN,eAAO,MAAM,4BAA4B,s5BA6BpC,CAAC;AAEN,eAAO,MAAM,yBAAyB,yVAajC,CAAC;AAEN,eAAO,MAAM,uBAAuB,mtBAkB/B,CAAC;AAEN,eAAO,MAAM,uBAAuB,8mBAiB/B,CAAC;AAEN,eAAO,MAAM,oBAAoB,ytBAkB5B,CAAC;AAKN,eAAO,MAAM,gBAAgB,8JAK3B,CAAC;AAEH,eAAO,MAAM,oBAAoB,sYAU5B,CAAC;AAEN,eAAO,MAAM,iBAAiB,mWASzB,CAAC;AAEN,eAAO,MAAM,iBAAiB,yVASzB,CAAC;AAaN,eAAO,MAAM,YAAY,sgBAUpB,CAAC;AAEN,eAAO,MAAM,YAAY,meAUpB,CAAC;AAIN,eAAO,MAAM,uBAAuB,6kBAU/B,CAAC;AAEN,eAAO,MAAM,mBAAmB,keAU3B,CAAC;AAGN,eAAO,MAAM,gBAAgB,2fAUxB,CAAC;AAEN,eAAO,MAAM,uBAAuB,qfAU/B,CAAC;AAEN,eAAO,MAAM,qBAAqB,2fAU7B,CAAC;AAQN,eAAO,MAAM,qBAAqB,kgBAU7B,CAAC;AAEN,eAAO,MAAM,gBAAgB,wfAUxB,CAAC;AAEN,eAAO,MAAM,gBAAgB,qlBAUxB,CAAC;AAEN,eAAO,MAAM,cAAc,miBAUtB,CAAC;AAGN,eAAO,MAAM,aAAa,gjBAUrB,CAAC;AAEN,eAAO,MAAM,aAAa,ubAUrB,CAAC;AAIN,eAAO,MAAM,oBAAoB,wdAU5B,CAAC;AAEN,eAAO,MAAM,qBAAqB,2ZAU7B,CAAC;AAEN,eAAO,MAAM,eAAe,4bAUvB,CAAC;AAEN,eAAO,MAAM,iBAAiB,uYAUzB,CAAC;AAEN,eAAO,MAAM,WAAW,4ZAUnB,CAAC;AAEN,eAAO,MAAM,WAAW,gZAUnB,CAAC;AAEN,eAAO,MAAM,gBAAgB,kbAUxB,CAAC;AAEN,eAAO,MAAM,aAAa,mcAUrB,CAAC;AAEN,eAAO,MAAM,eAAe,icAUvB,CAAC;AAEN,eAAO,MAAM,4BAA4B,+XAQpC,CAAC;AAEN,eAAO,MAAM,sBAAsB,0XAQ9B,CAAC;AAEN,eAAO,MAAM,iCAAiC,6VAQzC,CAAC;AAEN,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,EAAE,MAAM,MAAM,KAAG,MAK1D,CAAC"}
|
|
@@ -322,6 +322,55 @@ export const ANGULAR_PATH_SERVICES = `defineKnowledgeEntry({
|
|
|
322
322
|
content: 'Injectable services live under src/app/services/ (or alongside their feature folder). Use the .service.ts suffix. Provide via providedIn root unless feature-scoped.',
|
|
323
323
|
metadata: { path: 'src/app/services' },
|
|
324
324
|
})`;
|
|
325
|
+
// React workspaces — many flavors (Vite SPA, Next.js, Remix). The
|
|
326
|
+
// snippets below cover the most common SPA layout (src/components,
|
|
327
|
+
// src/hooks, src/pages, src/lib). Frameworks that follow a different
|
|
328
|
+
// convention (e.g. Next.js app router under app/) will trigger the
|
|
329
|
+
// init paths-advisory; the user is expected to swap them at that point.
|
|
330
|
+
export const REACT_PATH_COMPONENTS = `defineKnowledgeEntry({
|
|
331
|
+
id: 'paths.react.components',
|
|
332
|
+
title: 'React components',
|
|
333
|
+
type: KnowledgeType.Path,
|
|
334
|
+
priority: KnowledgePriority.High,
|
|
335
|
+
tags: ['paths', 'react', 'components'],
|
|
336
|
+
scope: ['react'],
|
|
337
|
+
appliesWhen: ['generate-component'],
|
|
338
|
+
content: 'Components live under src/components/ (cross-feature shared) or under their feature folder. Keep each component in its own file; pair with a co-located *.test.tsx beside it.',
|
|
339
|
+
metadata: { path: 'src/components' },
|
|
340
|
+
})`;
|
|
341
|
+
export const REACT_PATH_HOOKS = `defineKnowledgeEntry({
|
|
342
|
+
id: 'paths.react.hooks',
|
|
343
|
+
title: 'React custom hooks',
|
|
344
|
+
type: KnowledgeType.Path,
|
|
345
|
+
priority: KnowledgePriority.High,
|
|
346
|
+
tags: ['paths', 'react', 'hooks'],
|
|
347
|
+
scope: ['react'],
|
|
348
|
+
appliesWhen: ['generate-hook'],
|
|
349
|
+
content: 'Custom hooks live under src/hooks/ (cross-feature) or under their feature folder. File and exported function are both named useX. Co-locate the test as useX.test.ts beside the hook.',
|
|
350
|
+
metadata: { path: 'src/hooks' },
|
|
351
|
+
})`;
|
|
352
|
+
export const REACT_PATH_PAGES = `defineKnowledgeEntry({
|
|
353
|
+
id: 'paths.react.pages',
|
|
354
|
+
title: 'React route / page components',
|
|
355
|
+
type: KnowledgeType.Path,
|
|
356
|
+
priority: KnowledgePriority.Medium,
|
|
357
|
+
tags: ['paths', 'react', 'pages', 'routing'],
|
|
358
|
+
scope: ['react'],
|
|
359
|
+
appliesWhen: ['create-feature', 'add-route'],
|
|
360
|
+
content: 'Top-level route components live under src/pages/ (React Router / TanStack Router convention) or under src/routes/. For Next.js app router, see src/app/<segment>/page.tsx instead — adjust this entry if your project uses that layout.',
|
|
361
|
+
metadata: { path: 'src/pages' },
|
|
362
|
+
})`;
|
|
363
|
+
export const REACT_PATH_LIB = `defineKnowledgeEntry({
|
|
364
|
+
id: 'paths.react.lib',
|
|
365
|
+
title: 'React app utilities + clients',
|
|
366
|
+
type: KnowledgeType.Path,
|
|
367
|
+
priority: KnowledgePriority.Medium,
|
|
368
|
+
tags: ['paths', 'react', 'lib'],
|
|
369
|
+
scope: ['react'],
|
|
370
|
+
appliesWhen: ['generate-utility', 'generate-code'],
|
|
371
|
+
content: 'Framework-agnostic helpers (formatters, validators, API clients) live under src/lib/. Keep them pure — no React imports unless the helper is a hook (in which case it belongs under src/hooks/).',
|
|
372
|
+
metadata: { path: 'src/lib' },
|
|
373
|
+
})`;
|
|
325
374
|
// NestJS services — module-per-folder convention; e2e tests in `test/`.
|
|
326
375
|
export const NEST_PATH_SRC = `defineKnowledgeEntry({
|
|
327
376
|
id: 'paths.nest.src',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shrkcrft/presets",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.7",
|
|
4
4
|
"description": "SharkCraft presets: reusable project setups (knowledge/rules/paths/templates/pipelines/docs) that can be applied to a target repo through the CLI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "SharkCraft contributors",
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@shrkcrft/core": "^0.1.0-alpha.
|
|
46
|
-
"@shrkcrft/knowledge": "^0.1.0-alpha.
|
|
47
|
-
"@shrkcrft/workspace": "^0.1.0-alpha.
|
|
45
|
+
"@shrkcrft/core": "^0.1.0-alpha.7",
|
|
46
|
+
"@shrkcrft/knowledge": "^0.1.0-alpha.7",
|
|
47
|
+
"@shrkcrft/workspace": "^0.1.0-alpha.7"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public"
|