Skip to content

Commit 9b73ecb

Browse files
Move feature modules to shared and tighten lint rules
Refactor shared code: moved several commonly-used hooks, stores, libs, and UI components from features/* into shared/* to centralize reuse (e.g. use-note-mutations, use-note-queries, note-list-navigation (+tests), use-ui-store, bootstrap-error, conflict-resolution-dialog, delete-publish-dialog). Updated imports across the app to reference the new shared paths (editor, notes-pane, shell, sidebar, settings, etc.). Also tightened linting by changing no-restricted-imports severity from "warn" to "error" in .oxlintrc.json for the feature-specific overrides.
1 parent 61aa5c8 commit 9b73ecb

36 files changed

Lines changed: 952 additions & 252 deletions

app/.oxlintrc.json

Lines changed: 45 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
{
22
"$schema": "./node_modules/oxlint/configuration_schema.json",
33
"plugins": ["typescript", "react", "unicorn", "import"],
4+
"jsPlugins": ["eslint-plugin-boundaries"],
5+
"env": {
6+
"builtin": true
7+
},
48
"categories": {
59
"correctness": "error",
610
"suspicious": "error",
@@ -10,6 +14,23 @@
1014
"options": {
1115
"typeAware": true
1216
},
17+
"settings": {
18+
"import/resolver": {
19+
"typescript": true,
20+
"node": true
21+
},
22+
"boundaries/elements": [
23+
{ "type": "app-root", "pattern": "src/app.tsx", "mode": "file" },
24+
{ "type": "app-root", "pattern": "src/main.tsx", "mode": "file" },
25+
{ "type": "app-root", "pattern": "src/features/shell" },
26+
{
27+
"type": "feature",
28+
"pattern": "src/features/*",
29+
"capture": ["featureName"]
30+
},
31+
{ "type": "shared", "pattern": "src/shared" }
32+
]
33+
},
1334
"rules": {
1435
"typescript/no-floating-promises": [
1536
"error",
@@ -62,13 +83,31 @@
6283
"no-nested-ternary": "off",
6384
"unicorn/prefer-add-event-listener": "off",
6485
"typescript/no-unnecessary-type-conversion": "off",
65-
"no-restricted-imports": [
66-
"warn",
86+
"boundaries/dependencies": [
87+
"error",
6788
{
68-
"patterns": [
89+
"default": "disallow",
90+
"rules": [
91+
{
92+
"from": { "type": "app-root" },
93+
"allow": { "to": { "type": "*" } }
94+
},
6995
{
70-
"group": ["@/features/*/*/**"],
71-
"message": "Cross-feature deep imports break feature boundaries. Import from the feature barrel (@/features/<feature>) instead."
96+
"from": { "type": "shared" },
97+
"allow": { "to": { "type": "shared" } }
98+
},
99+
{
100+
"from": { "type": "feature" },
101+
"allow": {
102+
"to": [
103+
{
104+
"type": "feature",
105+
"captured": { "featureName": "{{ featureName }}" }
106+
},
107+
{ "type": "shared" },
108+
{ "type": "app-root" }
109+
]
110+
}
72111
}
73112
]
74113
}
@@ -81,153 +120,5 @@
81120
"src-tauri/**",
82121
"scripts/**"
83122
],
84-
"overrides": [
85-
{
86-
"files": ["src/features/command-palette/**"],
87-
"rules": {
88-
"no-restricted-imports": [
89-
"warn",
90-
{
91-
"patterns": [
92-
{
93-
"group": [
94-
"@/features/*/*/**",
95-
"!@/features/command-palette/**"
96-
],
97-
"message": "Cross-feature deep imports break feature boundaries. Import from the feature barrel (@/features/<feature>) instead."
98-
}
99-
]
100-
}
101-
]
102-
}
103-
},
104-
{
105-
"files": ["src/features/editor/**"],
106-
"rules": {
107-
"no-restricted-imports": [
108-
"warn",
109-
{
110-
"patterns": [
111-
{
112-
"group": ["@/features/*/*/**", "!@/features/editor/**"],
113-
"message": "Cross-feature deep imports break feature boundaries. Import from the feature barrel (@/features/<feature>) instead."
114-
}
115-
]
116-
}
117-
]
118-
}
119-
},
120-
{
121-
"files": ["src/features/editor-pane/**"],
122-
"rules": {
123-
"no-restricted-imports": [
124-
"warn",
125-
{
126-
"patterns": [
127-
{
128-
"group": ["@/features/*/*/**", "!@/features/editor-pane/**"],
129-
"message": "Cross-feature deep imports break feature boundaries. Import from the feature barrel (@/features/<feature>) instead."
130-
}
131-
]
132-
}
133-
]
134-
}
135-
},
136-
{
137-
"files": ["src/features/notes-pane/**"],
138-
"rules": {
139-
"no-restricted-imports": [
140-
"warn",
141-
{
142-
"patterns": [
143-
{
144-
"group": ["@/features/*/*/**", "!@/features/notes-pane/**"],
145-
"message": "Cross-feature deep imports break feature boundaries. Import from the feature barrel (@/features/<feature>) instead."
146-
}
147-
]
148-
}
149-
]
150-
}
151-
},
152-
{
153-
"files": ["src/features/publishing/**"],
154-
"rules": {
155-
"no-restricted-imports": [
156-
"warn",
157-
{
158-
"patterns": [
159-
{
160-
"group": ["@/features/*/*/**", "!@/features/publishing/**"],
161-
"message": "Cross-feature deep imports break feature boundaries. Import from the feature barrel (@/features/<feature>) instead."
162-
}
163-
]
164-
}
165-
]
166-
}
167-
},
168-
{
169-
"files": ["src/features/settings/**"],
170-
"rules": {
171-
"no-restricted-imports": [
172-
"warn",
173-
{
174-
"patterns": [
175-
{
176-
"group": ["@/features/*/*/**", "!@/features/settings/**"],
177-
"message": "Cross-feature deep imports break feature boundaries. Import from the feature barrel (@/features/<feature>) instead."
178-
}
179-
]
180-
}
181-
]
182-
}
183-
},
184-
{
185-
"files": ["src/features/shell/**"],
186-
"rules": {
187-
"no-restricted-imports": [
188-
"warn",
189-
{
190-
"patterns": [
191-
{
192-
"group": ["@/features/*/*/**", "!@/features/shell/**"],
193-
"message": "Cross-feature deep imports break feature boundaries. Import from the feature barrel (@/features/<feature>) instead."
194-
}
195-
]
196-
}
197-
]
198-
}
199-
},
200-
{
201-
"files": ["src/features/sidebar-pane/**"],
202-
"rules": {
203-
"no-restricted-imports": [
204-
"warn",
205-
{
206-
"patterns": [
207-
{
208-
"group": ["@/features/*/*/**", "!@/features/sidebar-pane/**"],
209-
"message": "Cross-feature deep imports break feature boundaries. Import from the feature barrel (@/features/<feature>) instead."
210-
}
211-
]
212-
}
213-
]
214-
}
215-
},
216-
{
217-
"files": ["src/features/sync/**"],
218-
"rules": {
219-
"no-restricted-imports": [
220-
"warn",
221-
{
222-
"patterns": [
223-
{
224-
"group": ["@/features/*/*/**", "!@/features/sync/**"],
225-
"message": "Cross-feature deep imports break feature boundaries. Import from the feature barrel (@/features/<feature>) instead."
226-
}
227-
]
228-
}
229-
]
230-
}
231-
}
232-
]
123+
"overrides": []
233124
}

app/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@
7171
"@vitest/coverage-v8": "^4.1.4",
7272
"babel-plugin-react-compiler": "^1.0.0",
7373
"culori": "^4.0.2",
74+
"eslint-import-resolver-typescript": "^4.4.4",
75+
"eslint-plugin-boundaries": "^6.0.2",
7476
"jsdom": "^29.0.2",
7577
"oxfmt": "^0.44.0",
7678
"oxlint": "^1.59.0",

app/src/app.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import { Toaster } from "@/shared/ui/sonner";
77
import { AccountSwitcherDialog } from "@/features/settings/account-switcher-dialog";
88
import { SettingsDialog } from "@/features/settings/settings-dialog";
99
import { CommandPalette } from "@/features/command-palette";
10-
import { BootstrapError } from "@/features/shell/ui/bootstrap-error";
11-
import { ConflictResolutionDialog } from "@/features/shell/ui/conflict-resolution-dialog";
12-
import { DeletePublishDialog } from "@/features/shell/ui/delete-publish-dialog";
13-
import { EditorPane } from "@/features/editor-pane";
10+
import { BootstrapError } from "@/shared/ui/bootstrap-error";
11+
import { ConflictResolutionDialog } from "@/shared/ui/conflict-resolution-dialog";
12+
import { DeletePublishDialog } from "@/shared/ui/delete-publish-dialog";
13+
import { EditorPane } from "@/features/editor/ui/editor-pane";
1414
import { NoteHistoryDialog } from "@/features/shell/note-history-dialog";
1515
import { NotesPane } from "@/features/notes-pane";
1616
import { PublishDialog, PublishShortNoteDialog } from "@/features/publishing";
@@ -21,7 +21,7 @@ import { useShellController } from "@/features/shell/use-shell-controller";
2121
import {
2222
useNotesPanelVisible,
2323
useSidebarVisible,
24-
} from "@/features/settings/store/use-ui-store";
24+
} from "@/shared/stores/use-ui-store";
2525

2626
const IS_MACOS = navigator.userAgent.includes("Mac");
2727

app/src/features/editor-pane/index.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

app/src/features/editor-pane/hooks/use-editor-scroll-header.ts renamed to app/src/features/editor/hooks/use-editor-scroll-header.ts

File renamed without changes.
File renamed without changes.
File renamed without changes.

app/src/features/editor/note-editor.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ import {
8080
} from "@/features/editor/lib/note-editor-selection";
8181
import { useNoteEditorSearchSync } from "@/features/editor/hooks/use-note-editor-search-sync";
8282
import { useNoteEditorToolbarActions } from "@/features/editor/hooks/use-note-editor-toolbar-actions";
83-
import { uiStore } from "@/features/settings/store/use-ui-store";
83+
import { uiStore } from "@/shared/stores/use-ui-store";
8484
import { useShellNavigationStore } from "@/shared/stores/use-shell-navigation-store";
8585
import {
8686
isEditorFindShortcut,

app/src/features/editor-pane/ui/conflict-resolution-footer.tsx renamed to app/src/features/editor/ui/conflict-resolution-footer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { ChevronLeft, ChevronRight } from "lucide-react";
22

33
import { Button } from "@/shared/ui/button";
44
import { type NoteConflictInfo } from "@/shared/api/types";
5-
import { formatConflictHeadTimestamp } from "@/features/editor-pane/lib/editor-pane-utils";
5+
import { formatConflictHeadTimestamp } from "@/features/editor/lib/editor-pane-utils";
66

77
type ViewableSnapshot = NoteConflictInfo["snapshots"][number];
88

File renamed without changes.

0 commit comments

Comments
 (0)