diff --git a/.coderabbit.yaml b/.coderabbit.yaml index e85513904..f50288895 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -25,7 +25,7 @@ reviews: - path: 'src/lib/server/**' instructions: | - This directory is server-only. Never import from client-side code or Svelte components. - - DB client must be accessed only via `$lib/server/database`. + - DB client must be accessed only via `#lib/server/database.js`. # --- Schema / validation --- - path: 'src/**/zod/**' instructions: | diff --git a/docs/guides/agent-rules/testing-e2e.md b/docs/guides/agent-rules/testing-e2e.md index 1c38e55ae..71e826e21 100644 --- a/docs/guides/agent-rules/testing-e2e.md +++ b/docs/guides/agent-rules/testing-e2e.md @@ -10,14 +10,14 @@ paths: ## Setup - File extension: **must** be `.spec.ts` (`.test.ts` not detected by `playwright.config.ts`) -- No path aliases or subpath imports (`$features` is resolved only by Vite; `#lib/*.js` resolves to `src/lib/*.js`, but the Playwright runner does not map it to the `.ts` source). Use type-only imports for types: +- No runtime imports through subpath imports (`#lib/*.js` and `#features/*.js` resolve to `src/**/*.js`, but the Playwright runner does not map them to the `.ts` source). Use relative imports, or type-only imports for types: ```typescript // Bad: runtime import fails in E2E -import { TAB_SOLUTION } from '$features/workbooks/types/workbook'; +import { TAB_SOLUTION } from '#features/workbooks/types/workbook.js'; // Good: type-only import + local constant -import type { WorkBookTab } from '$features/workbooks/types/workbook'; +import type { WorkBookTab } from '#features/workbooks/types/workbook.js'; const TAB_SOLUTION: WorkBookTab = 'solution'; ``` diff --git a/docs/guides/architecture.md b/docs/guides/architecture.md index 9f35fb9b5..4b7f08bcd 100644 --- a/docs/guides/architecture.md +++ b/docs/guides/architecture.md @@ -139,16 +139,16 @@ include: [ ]; ``` -### 6. path alias と subpath imports +### 6. subpath imports(`#lib`・`#features`) -`src/lib` は `package.json#imports` の `#lib/*` で参照する。SvelteKit 3 で組み込みの `$lib` が削除されるため `$lib` は使わず、oxlint と ESLint の `no-restricted-imports` で禁止している。 -`#lib` には明示的な拡張子が必要で、TS モジュールは `.js`、コンポーネントは `.svelte`、runes モジュール(`.svelte.ts`)は `.svelte.js` と書く。`vi.mock` の specifier も同じ形にする。 -lint が検出するのは import・export 宣言(`import type`・`export … from` を含む)と `.ts` の動的 `import()` で、`vi.mock('$lib/...')` と `.svelte` 内の動的 `import()` は検出しない。import の specifier をまとめて変えたときは、`rg -n '\$lib' src e2e prisma` で残りが無いことも確かめる。 -`$features` は `svelte.config.js` の alias で定義している。 +`src/lib` は `package.json#imports` の `#lib/*`、`src/features` は `#features/*` で参照する。SvelteKit 3 は組み込みの `$lib` を削除し、`kit.alias` も非推奨にするため、`svelte.config.js` に alias は置かない。 +拡張子は明示する。TS モジュールは `.js`、コンポーネントは `.svelte`、runes モジュール(`.svelte.ts`)は `.svelte.js` と書く。`vi.mock` の specifier も同じ形にする。 +`$lib` は Kit 2 が組み込みで解決してしまうため、oxlint と ESLint の `no-restricted-imports` で禁止している。lint が検出するのは import・export 宣言(`import type`・`export … from` を含む)と `.ts` の動的 `import()` で、`vi.mock('$lib/...')` と `.svelte` 内の動的 `import()` は検出しない。`$features` と `@/` は alias が無いので、import すると型チェックで失敗する。 +import の specifier をまとめて変えたときは、`rg -n '\$lib|\$features|@/' src e2e prisma` で残りが無いことも確かめる。 ```typescript import { formatDate } from '#lib/utils/format.js'; -import WorkBookForm from '$features/workbooks/components/shared/WorkBookForm.svelte'; +import WorkBookForm from '#features/workbooks/components/shared/WorkBookForm.svelte'; import type { PageServerLoad } from './$types'; ``` diff --git a/package.json b/package.json index d16d1b94c..4ee8d36dd 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,8 @@ }, "type": "module", "imports": { - "#lib/*": "./src/lib/*" + "#lib/*": "./src/lib/*", + "#features/*": "./src/features/*" }, "dependencies": { "@dnd-kit/helpers": "0.5.1", diff --git a/src/app.d.ts b/src/app.d.ts index ec0f9a009..30b104fb9 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -7,7 +7,7 @@ declare global { namespace App { // interface Error {} interface Locals { - auth: import('$features/auth/server/auth').AuthRequest; + auth: import('#features/auth/server/auth.js').AuthRequest; user: { id: string; name: string; diff --git a/src/features/tasks/components/contest-table/TaskTable.svelte b/src/features/tasks/components/contest-table/TaskTable.svelte index ab635fa87..302492f9b 100644 --- a/src/features/tasks/components/contest-table/TaskTable.svelte +++ b/src/features/tasks/components/contest-table/TaskTable.svelte @@ -18,18 +18,18 @@ ContestTableProvider, ContestTableDisplayConfig, ContestTableMetaData, - } from '$features/tasks/types/contest-table/contest_table_provider'; + } from '#features/tasks/types/contest-table/contest_table_provider.js'; import type { ContestTaskPairKey } from '#lib/types/contest_task_pair.js'; - import type { VoteStatisticsMap } from '$features/votes/types/vote_result'; + import type { VoteStatisticsMap } from '#features/votes/types/vote_result.js'; import TaskTableBodyCell from './TaskTableBodyCell.svelte'; - import { activeContestTypeStore } from '$features/tasks/stores/active_contest_type.svelte'; + import { activeContestTypeStore } from '#features/tasks/stores/active_contest_type.svelte.js'; import { contestTableProviderGroups, type ContestTableProviderGroup, type ContestTableProviderGroups, - } from '$features/tasks/utils/contest-table/contest_table_provider'; + } from '#features/tasks/utils/contest-table/contest_table_provider.js'; import { useLocalStorage } from '#lib/stores/local_storage_helper.svelte.js'; diff --git a/src/features/tasks/components/contest-table/TaskTableBodyCell.svelte b/src/features/tasks/components/contest-table/TaskTableBodyCell.svelte index 47197ed3d..af899be68 100644 --- a/src/features/tasks/components/contest-table/TaskTableBodyCell.svelte +++ b/src/features/tasks/components/contest-table/TaskTableBodyCell.svelte @@ -1,13 +1,13 @@ diff --git a/src/features/workbooks/components/list/SolutionWorkBookList.svelte b/src/features/workbooks/components/list/SolutionWorkBookList.svelte index d82d1a21f..64af06090 100644 --- a/src/features/workbooks/components/list/SolutionWorkBookList.svelte +++ b/src/features/workbooks/components/list/SolutionWorkBookList.svelte @@ -3,24 +3,24 @@ import type { Roles } from '#lib/types/user.js'; import type { TaskResults } from '#lib/types/task.js'; - import type { WorkbooksList } from '$features/workbooks/types/workbook'; + import type { WorkbooksList } from '#features/workbooks/types/workbook.js'; import { SolutionCategory, type SolutionCategories, SOLUTION_LABELS, ALL_SOLUTION_CATEGORIES, type SelectedSolutionCategory, - } from '$features/workbooks/types/workbook_placement'; + } from '#features/workbooks/types/workbook_placement.js'; - import { countReadableWorkbooks } from '$features/workbooks/utils/workbooks'; + import { countReadableWorkbooks } from '#features/workbooks/utils/workbooks.js'; import { groupBySolutionCategory, filterGroupsByRole, type WorkbookGroup, - } from '$features/workbooks/utils/solution_category_group'; + } from '#features/workbooks/utils/solution_category_group.js'; - import SolutionTable from '$features/workbooks/components/list/SolutionTable.svelte'; - import EmptyWorkbookList from '$features/workbooks/components/list/EmptyWorkbookList.svelte'; + import SolutionTable from '#features/workbooks/components/list/SolutionTable.svelte'; + import EmptyWorkbookList from '#features/workbooks/components/list/EmptyWorkbookList.svelte'; interface Props { workbooks: WorkbooksList; diff --git a/src/features/workbooks/components/list/TitleTableBodyCell.svelte b/src/features/workbooks/components/list/TitleTableBodyCell.svelte index f2a747913..cedbba6eb 100644 --- a/src/features/workbooks/components/list/TitleTableBodyCell.svelte +++ b/src/features/workbooks/components/list/TitleTableBodyCell.svelte @@ -3,10 +3,10 @@ import { TableBodyCell } from 'flowbite-svelte'; - import type { WorkbookList } from '$features/workbooks/types/workbook'; + import type { WorkbookList } from '#features/workbooks/types/workbook.js'; - import { getUrlSlugFrom } from '$features/workbooks/utils/workbooks'; - import PublicationStatusLabel from '$features/workbooks/components/shared/PublicationStatusLabel.svelte'; + import { getUrlSlugFrom } from '#features/workbooks/utils/workbooks.js'; + import PublicationStatusLabel from '#features/workbooks/components/shared/PublicationStatusLabel.svelte'; interface Props { paddingLeft?: string; diff --git a/src/features/workbooks/components/list/WorkBookList.svelte b/src/features/workbooks/components/list/WorkBookList.svelte index 6012ed0a5..e728e6d09 100644 --- a/src/features/workbooks/components/list/WorkBookList.svelte +++ b/src/features/workbooks/components/list/WorkBookList.svelte @@ -1,16 +1,16 @@