Reviewed-on: #413 Co-authored-by: miteruzo <miteruzo@naver.com> Co-committed-by: miteruzo <miteruzo@naver.com>
このコミットはPull リクエスト #413 でマージされました.
このコミットが含まれているのは:
+400
-10
@@ -50,16 +50,27 @@ pass or the remaining failure is clearly blocked.
|
||||
- Prefer single quotes for strings unless interpolation or escaping makes double quotes better.
|
||||
- Never write a TypeScript or TSX line longer than 99 characters.
|
||||
- Aim to keep TypeScript and TSX lines within 79 characters where practical.
|
||||
- Use 4-space logical indentation in TypeScript and TSX.
|
||||
- Use 2-space block indentation in TypeScript and TSX.
|
||||
- Use 4-space continuation indentation for wrapped expressions, arguments,
|
||||
ternary branches, method chains, object pairs, arrays, and JSX attributes.
|
||||
- Treat the user's `PostImportSourcePage.tsx` and
|
||||
`PostImportReviewPage.tsx` formatting as the local reference shape.
|
||||
- For arrays, never put whitespace or a line break immediately before `]`.
|
||||
- Keep the first element on the same line as `[` by default.
|
||||
- If an array would exceed the line limit, break after `[` and indent
|
||||
elements by 4 spaces.
|
||||
- In TypeScript and TSX only, replace every leading run of 8 spaces with a tab
|
||||
to reduce bytes.
|
||||
- In TypeScript and TSX only, use tabs for leading 8-column compression only.
|
||||
- A tab does not represent one indentation level.
|
||||
- Determine visible indentation with 2-space block indentation and 4-space
|
||||
continuation indentation first, then compress only complete leading runs of
|
||||
8 spaces into tabs.
|
||||
- Treat one leading tab as exactly equivalent to 8 leading spaces.
|
||||
- Use tabs only for leading indentation. Never replace spaces that occur after
|
||||
a non-space character on the same line.
|
||||
- Keep residual leading 2, 4, or 6 spaces after any tab compression.
|
||||
- Examples: 2 columns = 2 spaces, 4 columns = 4 spaces, 6 columns = 6
|
||||
spaces, 8 columns = 1 tab, 10 columns = 1 tab + 2 spaces, 12 columns = 1
|
||||
tab + 4 spaces.
|
||||
|
||||
## React
|
||||
|
||||
@@ -97,17 +108,35 @@ pass or the remaining failure is clearly blocked.
|
||||
third-party request outside the Rails API.
|
||||
- For blob responses, pass `responseType: 'blob'` so the wrapper does not camelCase the body.
|
||||
|
||||
## Dialogues
|
||||
|
||||
- Dialogue work follows the shared-frontend reuse rules below.
|
||||
|
||||
## Imports and aliases
|
||||
|
||||
- The `@` alias points to `frontend/src`.
|
||||
- Prefer `@/...` imports for app code instead of long relative paths.
|
||||
- Keep type imports separate with `import type`.
|
||||
- Match existing import grouping: external packages, app modules, then type imports.
|
||||
- Do not mix runtime values and `type` specifiers in one named import
|
||||
declaration.
|
||||
- Do not write `import { value, type TypeName } from ...`.
|
||||
- Keep short value imports from one module on one line when they fit within
|
||||
99 characters.
|
||||
- Order imports as four groups with a blank line between groups: external
|
||||
value imports, `@/...` value imports, external type imports, `@/...`
|
||||
type imports.
|
||||
|
||||
## Tailwind and UI
|
||||
|
||||
- Tailwind scans `src/**/*.{html,js,ts,jsx,tsx,mdx}`.
|
||||
- Use `cn` from `src/lib/utils.ts` for conditional class names and class merging.
|
||||
- In JavaScript, JSX, TypeScript, and TSX, use `cn` from `@/lib/utils`
|
||||
whenever `className` combines multiple values, conditional classes, or a
|
||||
caller-provided `className` prop.
|
||||
- Do not construct `className` with template literals, `${ ... }`, string
|
||||
concatenation, arrays joined with spaces, or feature-local class-merging
|
||||
helpers.
|
||||
- A static `className="..."` containing only fixed classes does not need `cn`.
|
||||
- Reuse components from `src/components/common`, `src/components/layout`, and
|
||||
`src/components/ui` before adding new primitives.
|
||||
- Keep Tailwind classes consistent with nearby components.
|
||||
@@ -118,6 +147,15 @@ pass or the remaining failure is clearly blocked.
|
||||
short Japanese labels that fit the control.
|
||||
- Preserve existing Japanese tone and orthography in nearby UI text, including
|
||||
old-kana wording where the file already uses it.
|
||||
- Do not add user-facing copy, helper text, descriptions, notes, tooltips,
|
||||
placeholders, empty-state messages, loading messages, or explanatory text
|
||||
unless the user explicitly specified the wording.
|
||||
- When new user-facing wording appears necessary, ask the user for the exact
|
||||
wording and placement before implementing it.
|
||||
- Do not invent replacement copy when removing unrequested wording.
|
||||
- Do not use `タグなし` as user-facing copy for an empty tag state. When the
|
||||
tag state is empty, show no copy. If actual data contains the tag name
|
||||
`タグなし`, treat it as ordinary data and display it normally.
|
||||
- When adding dynamic tag colour classes, update `tailwind.config.js` safelist
|
||||
if the class cannot be statically detected.
|
||||
- Do not introduce new UI libraries or production dependencies without approval.
|
||||
@@ -129,6 +167,41 @@ pass or the remaining failure is clearly blocked.
|
||||
it is JSX- or React-specific.
|
||||
- Preserve compact TSX expression shapes such as inline ternary branches and
|
||||
closing `</div>)` forms when nearby code uses them.
|
||||
- Block bodies for components, functions, callbacks, `if`, `try`, `catch`,
|
||||
`finally`, loops, and JSX nesting use 2 spaces per level.
|
||||
- Put the opening brace of `try`, `catch`, and `finally` blocks on the next
|
||||
line at the same indentation as the keyword.
|
||||
- Do not indent the opening `{` one level deeper than `try`, `catch`, or
|
||||
`finally`.
|
||||
- Indent the block body 2 spaces deeper than the keyword and opening brace.
|
||||
- Put the closing `}` on its own line at the same indentation as the keyword.
|
||||
- Do not write `try {`, `catch {`, or `finally {`.
|
||||
- Wrapped expressions, arguments, ternary branches, method chains, and object
|
||||
pairs use 4-space continuation indentation relative to the owning
|
||||
expression. Do not confuse this with 2-space block indentation.
|
||||
- Tabs are leading 8-column compression only. They do not represent one
|
||||
nesting level. Decide visible indentation first, then compress only
|
||||
complete leading runs of 8 spaces into tabs.
|
||||
- Do not add braces around a single-line `if` body merely for formatting.
|
||||
- Use braces for multi-line `if`, `else`, and loop bodies.
|
||||
- Multi-stage ternary expressions must use explicit parentheses for each
|
||||
condition group and nested branch. Do not rely on indentation alone to show
|
||||
`?` / `:` pairing.
|
||||
- Keep short inline props types local when they remain readable and within the
|
||||
line limit; do not mechanically extract a named type with no reuse benefit.
|
||||
- In JavaScript, JSX, TypeScript, and TSX, never use `_1`, `_2`, or similar
|
||||
Ruby-style numbered parameter names. Reserve numbered parameters for Ruby.
|
||||
Use a meaningful callback parameter name such as `row`, `item`, `value`,
|
||||
`entry`, or `result`.
|
||||
- In multi-line object literals, keep the opening `{` with the first pair when
|
||||
the line length allows it; do not mechanically explode short objects into
|
||||
Prettier-style vertical blocks.
|
||||
- Method chains should align as a continuation under the receiver expression;
|
||||
do not indent chains more deeply than the normal continuation depth.
|
||||
- `PostImportSourcePage.tsx` and `PostImportReviewPage.tsx` are the current
|
||||
canonical examples for block indentation, continuation indentation, import
|
||||
grouping, ternary grouping, method-chain placement, and local inline props
|
||||
types.
|
||||
- Treat TypeScript and TSX formatting rules as hard constraints, not
|
||||
preferences. Before finishing a TypeScript or TSX edit, inspect the edited
|
||||
hunks for closing `)`, `]`, and `}` placement and fix violations instead of
|
||||
@@ -158,14 +231,93 @@ pass or the remaining failure is clearly blocked.
|
||||
beginning of a line.
|
||||
- The TSX-specific self-review must confirm JSX closing markers and closing
|
||||
parentheses keep the surrounding compact style.
|
||||
- The TypeScript/TSX self-review must confirm leading indentation follows
|
||||
4-space logical indentation with tabs only as leading 8-space compression.
|
||||
- The TypeScript/TSX self-review must confirm leading block indentation uses
|
||||
2 spaces per level, wrapped continuations use the repository's 4-space
|
||||
continuation alignment, and complete leading runs of 8 spaces may be
|
||||
compressed to tabs.
|
||||
- For long Tailwind `className` strings, wrap across lines only when needed.
|
||||
- Keep continuation indentation aligned with the 4-space logical indentation
|
||||
rule, using tabs only as leading 8-space compression.
|
||||
- Keep continuation indentation aligned with the repository's 4-space
|
||||
continuation rule while keeping block indentation at 2 spaces.
|
||||
- Keep short value imports from one module on one line when they fit within
|
||||
99 characters.
|
||||
- In TypeScript and TSX function declarations, including `const` arrow
|
||||
function declarations, classify the parameter list before placing the closing
|
||||
`)`.
|
||||
- Block indentation example:
|
||||
|
||||
```ts
|
||||
const Component = () => {
|
||||
const value = loadValue ()
|
||||
|
||||
useEffect (() => {
|
||||
if (value != null)
|
||||
useValue (value)
|
||||
}, [value])
|
||||
}
|
||||
```
|
||||
|
||||
- `try` / `catch` / `finally` brace placement example:
|
||||
|
||||
```ts
|
||||
try
|
||||
{
|
||||
doWork ()
|
||||
}
|
||||
catch
|
||||
{
|
||||
recover ()
|
||||
}
|
||||
finally
|
||||
{
|
||||
cleanUp ()
|
||||
}
|
||||
```
|
||||
|
||||
- Continuation indentation example:
|
||||
|
||||
```ts
|
||||
const editingRow =
|
||||
Number.isFinite (editingSourceRow)
|
||||
? rows.find (row => row.sourceRow === editingSourceRow) ?? null
|
||||
: null
|
||||
```
|
||||
|
||||
- Import grouping example:
|
||||
|
||||
```ts
|
||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
|
||||
|
||||
import { loadPostImportSession } from '@/lib/postImportSession'
|
||||
|
||||
import type { FC } from 'react'
|
||||
|
||||
import type { PostImportRow } from '@/lib/postImportSession'
|
||||
```
|
||||
|
||||
- Inline props type example:
|
||||
|
||||
```ts
|
||||
const Footer = (
|
||||
{ loading,
|
||||
onSubmit }: { loading: boolean
|
||||
onSubmit: () => void },
|
||||
) => null
|
||||
```
|
||||
|
||||
- Ternary grouping example:
|
||||
|
||||
```ts
|
||||
const rows =
|
||||
repairMode === 'failed'
|
||||
? (
|
||||
[...source].sort ((a, b) => {
|
||||
const aFailed = a.failed ? 0 : 1
|
||||
const bFailed = b.failed ? 0 : 1
|
||||
return aFailed - bFailed
|
||||
}))
|
||||
: source
|
||||
```
|
||||
|
||||
- If the parameter list itself is given its own multi-line block after the
|
||||
function's opening `(`, put the closing parameter `)` at the beginning of its
|
||||
own line before the return type or `=>`.
|
||||
@@ -219,6 +371,243 @@ pass or the remaining failure is clearly blocked.
|
||||
`BehaviorSettingsSection.tsx`.
|
||||
- Avoid reformatting unrelated JSX.
|
||||
|
||||
## Shared frontend systems
|
||||
|
||||
Before creating a new component, hook, helper, store, context, or other
|
||||
frontend abstraction, search at least:
|
||||
|
||||
- `src/components/common`
|
||||
- `src/components/layout`
|
||||
- `src/components/ui`
|
||||
- `src/components/dialogues`
|
||||
- `src/lib`
|
||||
- `src/lib/dialogues`
|
||||
- `src/stores`
|
||||
- `src/types.ts`
|
||||
|
||||
Also inspect the existing pages and components in the same feature.
|
||||
|
||||
Search by responsibility, not by filename alone. Check display, interaction,
|
||||
state, communication, validation, and permission behaviour before deciding that
|
||||
an existing implementation is unsuitable.
|
||||
|
||||
### Component placement and reuse order
|
||||
|
||||
When adding UI, use this order:
|
||||
|
||||
1. reuse an existing feature component
|
||||
2. reuse an existing component from `components/common`
|
||||
3. reuse an existing layout component from `components/layout`
|
||||
4. use an existing primitive from `components/ui` through the established
|
||||
common API
|
||||
5. extend an existing component minimally
|
||||
6. add a feature-local component in the feature area
|
||||
7. add a new common component only when multiple features clearly share a
|
||||
stable visual contract
|
||||
|
||||
Do not place a one-screen component in a common directory merely because its
|
||||
name starts with `Common`.
|
||||
|
||||
### Low-level primitives
|
||||
|
||||
Treat `components/ui` as low-level primitives. If a higher-level common API
|
||||
already exists for dialogues, toast, form validation, navigation, or similar
|
||||
behaviour, feature code must use that API instead of assembling primitives
|
||||
directly.
|
||||
|
||||
Examples of existing preferred entrypoints include:
|
||||
|
||||
- dialogue: `@/lib/dialogues/useDialogue`
|
||||
- toast: the existing toast API
|
||||
- internal navigation: `PrefetchLink`
|
||||
- form errors: `FieldError`, `FieldWarning`, `FormField`
|
||||
- buttons: `Button`
|
||||
- conditional class merge: `cn`
|
||||
|
||||
Do not evade the rule with aliases or thin wrappers around the low-level
|
||||
primitive.
|
||||
|
||||
### Dialogues
|
||||
|
||||
Feature-facing dialogue work must use `@/lib/dialogues/useDialogue`.
|
||||
|
||||
Reuse the existing common dialogue API and common dialogue component. Do not
|
||||
import `@/components/ui/dialog` directly in feature code to assemble bespoke
|
||||
dialogue shells, and do not evade this rule with aliases such as
|
||||
`Dialog as Dialogue`.
|
||||
|
||||
Do not reimplement overlay, portal, close button, header, footer, focus
|
||||
handling, Escape handling, outside-click handling, or confirmation flow in
|
||||
feature code.
|
||||
|
||||
Keep business-specific form content in feature code, and keep the visual and
|
||||
behavioural dialogue shell in common code.
|
||||
|
||||
Use British spelling `Dialogue` for project-defined dialogue identifiers. Keep
|
||||
exact third-party spellings only at the external boundary where compatibility
|
||||
requires them.
|
||||
|
||||
### API calls
|
||||
|
||||
Rails API calls must use `src/lib/api.ts`.
|
||||
|
||||
Do not create feature-local Axios instances, fetch wrappers, header injectors,
|
||||
camelCase converters, or generic error converters. If blob or other special
|
||||
transport behaviour is already supported by the common API, use the existing
|
||||
options instead of bypassing the wrapper.
|
||||
|
||||
### Query keys, server state, and prefetch
|
||||
|
||||
Before adding query state, inspect:
|
||||
|
||||
- `src/lib/queryKeys.ts`
|
||||
- existing domain helpers
|
||||
- existing prefetchers
|
||||
- the root query-key hierarchy
|
||||
- current mutation invalidation patterns
|
||||
- the app-wide `QueryClient`
|
||||
|
||||
Do not write ad hoc query-key arrays in feature code. Do not duplicate fetcher,
|
||||
prefetcher, or invalidation helpers for the same resource.
|
||||
|
||||
### Domain helpers
|
||||
|
||||
For posts, tags, wiki, materials, and other domain work, inspect the existing
|
||||
helpers in `src/lib/*.ts` before adding logic to a page component.
|
||||
|
||||
Do not accumulate these in page components when an existing helper layer should
|
||||
own them:
|
||||
|
||||
- API request construction
|
||||
- response-shape conversion
|
||||
- query-key construction
|
||||
- canonical URL generation
|
||||
- permission calculation
|
||||
- storage serialisation
|
||||
- domain-specific parsing
|
||||
|
||||
Keep purely local one-screen display shaping local when that is the clearest
|
||||
place for it.
|
||||
|
||||
### Permission helpers
|
||||
|
||||
Use the existing permission helpers such as `src/lib/users.ts` when deciding
|
||||
editability, role checks, admin/member visibility, and similar UI behaviour.
|
||||
|
||||
Do not scatter `user?.role`, numeric role comparisons, or string comparisons
|
||||
through components. Frontend visibility control should be consistent even though
|
||||
backend authorization remains the final gate.
|
||||
|
||||
### Validation errors
|
||||
|
||||
Before adding feature-local validation-error handling, inspect:
|
||||
|
||||
- `useValidationErrors`
|
||||
- `apiErrors`
|
||||
- `FieldError`
|
||||
- `FieldWarning`
|
||||
- `FormField`
|
||||
- `inputClass`
|
||||
|
||||
Do not create a new generic hook, field-error state shape, or error-rendering
|
||||
component for a pattern the shared error stack already covers. Keep only
|
||||
genuinely feature-specific business errors local.
|
||||
|
||||
### Forms and fields
|
||||
|
||||
Before creating a new input, textarea, date/time field, tag input, label, or
|
||||
error layout, inspect at least:
|
||||
|
||||
- `Form`
|
||||
- `FormField`
|
||||
- `FieldError`
|
||||
- `FieldWarning`
|
||||
- `DateTimeField`
|
||||
- `TagInput`
|
||||
- `TextArea`
|
||||
- `Label`
|
||||
- `Button`
|
||||
|
||||
Do not create a same-function field component merely because the spacing or
|
||||
surface styling is slightly different. Prefer feature-level composition over
|
||||
bloated common-field option lists.
|
||||
|
||||
### Navigation and prefetch
|
||||
|
||||
Use `PrefetchLink` and existing router helpers for internal navigation. Do not
|
||||
introduce feature-local `<a>`, `window.location`, or custom prefetch logic for
|
||||
internal routes. Keep path-segment encoding aligned with the existing rules.
|
||||
|
||||
### State management
|
||||
|
||||
Before adding state, decide whether the source of truth should be:
|
||||
|
||||
- component-local state
|
||||
- URL search params
|
||||
- TanStack Query server state
|
||||
- an existing Zustand store
|
||||
- an existing event bus
|
||||
- an existing storage helper
|
||||
|
||||
Do not create a new global store, context, or event bus for one screen when
|
||||
local state or an existing mechanism is enough. Do not create a second store
|
||||
for the same responsibility.
|
||||
|
||||
### Storage and settings
|
||||
|
||||
When touching localStorage, sessionStorage, or user settings, inspect existing
|
||||
settings helpers, storage helpers, expiry handling, versioning, and sanitisers.
|
||||
|
||||
Do not reimplement per-component key naming, JSON parsing and serialisation,
|
||||
expiry, or schema checks when a shared helper already owns the pattern.
|
||||
|
||||
### Hooks
|
||||
|
||||
Before creating a custom hook, search existing `src/lib/use*.ts` and
|
||||
`src/lib/use*.tsx`.
|
||||
|
||||
Hooks are for shared stateful behaviour or React lifecycle integration. Do not
|
||||
turn a pure function, one-off helper, or mere re-export shim into `useFoo`.
|
||||
|
||||
### Stores, contexts, and event buses
|
||||
|
||||
Add a new store, context, or event bus only when the current mechanisms cannot
|
||||
express the requirement and there are multiple genuinely separate consumers.
|
||||
|
||||
Do not hold the same information redundantly across URL state, query cache,
|
||||
local component state, Zustand, and an event bus. Keep one source of truth.
|
||||
|
||||
### Types
|
||||
|
||||
If a domain type already exists in `src/types.ts` or a domain helper, reuse it
|
||||
instead of redefining the same shape in a feature file.
|
||||
|
||||
Small local props and draft types may stay local. Do not create giant
|
||||
catch-all type files such as `CommonTypes.ts`.
|
||||
|
||||
### Styling utilities
|
||||
|
||||
Use existing styling utilities such as `cn` and `inputClass`.
|
||||
|
||||
Do not add feature-local class-merge helpers, generic status-colour mappers, or
|
||||
responsive wrapper helpers when a shared utility already exists. Keep common
|
||||
tone names visual only; feature-specific state names stay in feature code.
|
||||
|
||||
### Layout
|
||||
|
||||
Before adding page shells, padding rules, viewport-height handling, sidebar
|
||||
offsets, or footer offsets, inspect existing layout components such as
|
||||
`MainArea`, top navigation, sidebar, page title, and section-title patterns.
|
||||
|
||||
Do not create a second layout shell before checking whether the current layout
|
||||
can be reused or minimally extended.
|
||||
|
||||
- Frontend のスマホ/PC表示境界は原則 `md` とする。
|
||||
- button stack、footer action、dialogue action は `md` 未満で縦並び、
|
||||
`md` 以上で横並びとする。
|
||||
- 同じ画面内で `sm` と `md` を混在させて中間 layout を作らない。
|
||||
- 明確に別の responsive 要件がある component だけを例外とする。
|
||||
|
||||
### Delimiter decision table
|
||||
|
||||
Use this table before accepting any edited TypeScript or TSX hunk. The table is
|
||||
@@ -563,8 +952,9 @@ hunks line by line:
|
||||
7. JSX `>` and `/>` stay with the final prop unless nearby code proves
|
||||
otherwise.
|
||||
8. JSX closing parentheses keep the compact local style.
|
||||
9. Leading indentation is 4-space logical indentation with tabs used only as
|
||||
leading 8-space compression.
|
||||
9. Leading block indentation uses 2 spaces per level, wrapped continuations
|
||||
use the repository's 4-space continuation alignment, and complete leading
|
||||
runs of 8 spaces may be compressed to tabs.
|
||||
10. No line has trailing whitespace.
|
||||
|
||||
## Lint and build constraints
|
||||
|
||||
新しい課題から参照
ユーザをブロックする