@yasainet/eslint 0.0.74 → 0.0.76
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 +4 -104
- package/package.json +12 -1
- package/src/cli/test-audit.mjs +97 -0
- package/src/common/CLAUDE.md +19 -0
- package/src/common/_internal/constants.mjs +1 -1
- package/src/common/_internal/import-patterns.mjs +2 -2
- package/src/common/_internal/selectors.mjs +4 -2
- package/src/common/base/typescript.mjs +13 -7
- package/src/common/boundaries/entry-point.mjs +1 -1
- package/src/common/cross-cutting/ban-alias.mjs +1 -1
- package/src/common/cross-cutting/features-ts-only.mjs +1 -1
- package/src/common/cross-cutting/no-colocated-test.mjs +18 -0
- package/src/common/index.mjs +8 -4
- package/src/common/layers/entries.mjs +15 -9
- package/src/common/layers/queries.mjs +18 -14
- package/src/common/layers/schemas.mjs +6 -1
- package/src/common/layers/services.mjs +14 -8
- package/src/common/layers/{lib.mjs → top-level/lib.mjs} +3 -3
- package/src/common/layers/{top-level-utils.mjs → top-level/utils.mjs} +1 -1
- package/src/common/layers/utils.mjs +5 -1
- package/src/common/local-plugins/entry-single-service-call.mjs +3 -1
- package/src/common/local-plugins/entry-template.mjs +18 -16
- package/src/common/local-plugins/feature-name.mjs +1 -6
- package/src/common/local-plugins/form-state-naming.mjs +1 -1
- package/src/common/local-plugins/form-state-shape.mjs +8 -8
- package/src/common/local-plugins/import-path-style.mjs +2 -2
- package/src/common/local-plugins/index.mjs +2 -0
- package/src/common/local-plugins/layout-main-structural-only.mjs +1 -1
- package/src/common/local-plugins/namespace-import-name.mjs +1 -1
- package/src/common/local-plugins/no-any-return.mjs +1 -1
- package/src/common/local-plugins/no-colocated-test.mjs +26 -0
- package/src/common/local-plugins/queries-export.mjs +1 -1
- package/src/common/local-plugins/queries-namespace-import.mjs +1 -1
- package/src/common/local-plugins/schema-naming.mjs +2 -2
- package/src/common/local-plugins/supabase-columns-satisfies.mjs +1 -1
- package/src/common/local-plugins/supabase-select-typed-columns.mjs +5 -5
- package/src/deno/CLAUDE.md +10 -0
- package/src/deno/boundaries/entry-point.mjs +3 -3
- package/src/deno/boundaries/lib.mjs +1 -1
- package/src/deno/boundaries/utils.mjs +2 -2
- package/src/deno/local-plugins/flat-entry-point.mjs +1 -1
- package/src/next/CLAUDE.md +14 -0
- package/src/next/boundaries/components.mjs +2 -2
- package/src/next/boundaries/hooks.mjs +2 -2
- package/src/next/boundaries/page.mjs +2 -2
- package/src/next/boundaries/route.mjs +2 -2
- package/src/next/boundaries/sitemap.mjs +2 -2
- package/src/next/directives.mjs +4 -4
- package/src/next/layers/components.mjs +1 -1
- package/src/next/layers/hooks.mjs +1 -1
- package/src/next/tailwindcss.mjs +2 -2
- package/src/node/CLAUDE.md +7 -0
|
@@ -13,7 +13,10 @@ export function createSchemasConfigs({ featureRoot, prefixLibMapping }) {
|
|
|
13
13
|
{
|
|
14
14
|
name: "naming/schemas",
|
|
15
15
|
files: featuresGlob(featureRoot, "*/schemas/*.ts"),
|
|
16
|
-
ignores:
|
|
16
|
+
ignores: [
|
|
17
|
+
...featuresGlob(featureRoot, "shared/schemas/*.ts"),
|
|
18
|
+
"**/*.test.ts",
|
|
19
|
+
],
|
|
17
20
|
plugins: { "check-file": checkFile },
|
|
18
21
|
rules: {
|
|
19
22
|
"check-file/filename-naming-convention": [
|
|
@@ -25,6 +28,7 @@ export function createSchemasConfigs({ featureRoot, prefixLibMapping }) {
|
|
|
25
28
|
{
|
|
26
29
|
name: "naming/schemas-shared",
|
|
27
30
|
files: featuresGlob(featureRoot, "shared/schemas/*.ts"),
|
|
31
|
+
ignores: ["**/*.test.ts"],
|
|
28
32
|
plugins: { "check-file": checkFile },
|
|
29
33
|
rules: {
|
|
30
34
|
"check-file/filename-naming-convention": [
|
|
@@ -36,6 +40,7 @@ export function createSchemasConfigs({ featureRoot, prefixLibMapping }) {
|
|
|
36
40
|
{
|
|
37
41
|
name: "naming/schema-naming",
|
|
38
42
|
files: featuresGlob(featureRoot, "**/schemas/*.ts"),
|
|
43
|
+
ignores: ["**/*.test.ts"],
|
|
39
44
|
plugins: { local: localPlugin },
|
|
40
45
|
rules: {
|
|
41
46
|
"local/schema-naming": "error",
|
|
@@ -11,11 +11,11 @@ import {
|
|
|
11
11
|
const LAYER_PATTERNS = [
|
|
12
12
|
{
|
|
13
13
|
group: ["**/entries/*", "**/entries"],
|
|
14
|
-
message: "services
|
|
14
|
+
message: "services は entries を import 不可。依存は単方向に保つ。",
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
group: ["**/hooks/*", "**/hooks"],
|
|
18
|
-
message: "services
|
|
18
|
+
message: "services は hooks を import 不可。依存は単方向に保つ。",
|
|
19
19
|
},
|
|
20
20
|
];
|
|
21
21
|
|
|
@@ -23,7 +23,7 @@ const LATERAL_PATTERNS = [
|
|
|
23
23
|
{
|
|
24
24
|
group: ["@/features/*/services/*", "@/features/*/services"],
|
|
25
25
|
message:
|
|
26
|
-
"services
|
|
26
|
+
"他 feature の services は import 不可。feature を跨ぐ依存は禁止。",
|
|
27
27
|
},
|
|
28
28
|
];
|
|
29
29
|
|
|
@@ -39,7 +39,10 @@ export function createServicesConfigs({ featureRoot, prefixLibMapping }) {
|
|
|
39
39
|
{
|
|
40
40
|
name: "naming/services",
|
|
41
41
|
files: featuresGlob(featureRoot, "**/services/*.ts"),
|
|
42
|
-
ignores:
|
|
42
|
+
ignores: [
|
|
43
|
+
...featuresGlob(featureRoot, "shared/services/*.ts"),
|
|
44
|
+
"**/*.test.ts",
|
|
45
|
+
],
|
|
43
46
|
plugins: { "check-file": checkFile },
|
|
44
47
|
rules: {
|
|
45
48
|
"check-file/filename-naming-convention": [
|
|
@@ -51,6 +54,7 @@ export function createServicesConfigs({ featureRoot, prefixLibMapping }) {
|
|
|
51
54
|
{
|
|
52
55
|
name: "naming/services-shared",
|
|
53
56
|
files: featuresGlob(featureRoot, "shared/services/*.ts"),
|
|
57
|
+
ignores: ["**/*.test.ts"],
|
|
54
58
|
plugins: { "check-file": checkFile },
|
|
55
59
|
rules: {
|
|
56
60
|
"check-file/filename-naming-convention": [
|
|
@@ -68,25 +72,27 @@ export function createServicesConfigs({ featureRoot, prefixLibMapping }) {
|
|
|
68
72
|
{
|
|
69
73
|
selector: "TryStatement",
|
|
70
74
|
message:
|
|
71
|
-
"try-catch
|
|
75
|
+
"services で try-catch は禁止。エラー処理は entries に集約する。",
|
|
72
76
|
},
|
|
73
77
|
{
|
|
74
78
|
selector: "ThrowStatement",
|
|
75
79
|
message:
|
|
76
|
-
"
|
|
80
|
+
"services で throw は禁止。失敗は値で返す:\n" +
|
|
81
|
+
"- `T | null` / `{ data, error }` / 空デフォルトのいずれか\n" +
|
|
82
|
+
"- lib の native 例外は entry の catch に自動伝播する",
|
|
77
83
|
},
|
|
78
84
|
{ selector: loggerSelector, message: loggerMessage },
|
|
79
85
|
{
|
|
80
86
|
selector:
|
|
81
87
|
"LogicalExpression[operator='??'][left.type='ChainExpression'][left.expression.property.name='message'][right.type='Literal']",
|
|
82
88
|
message:
|
|
83
|
-
"
|
|
89
|
+
"error message の dead fallback。この分岐に来た時点で error は既知 — error をそのまま返す。",
|
|
84
90
|
},
|
|
85
91
|
{
|
|
86
92
|
selector:
|
|
87
93
|
"LogicalExpression[operator='??'][left.type='MemberExpression'][left.property.name='error'][right.type='ObjectExpression']",
|
|
88
94
|
message:
|
|
89
|
-
"
|
|
95
|
+
"nullable error の dead fallback。`if (error)` で判定し error をそのまま返す。",
|
|
90
96
|
},
|
|
91
97
|
{
|
|
92
98
|
selector: aliasDynamicImportSelector,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { checkFile } from "
|
|
1
|
+
import { checkFile } from "../../_internal/plugins.mjs";
|
|
2
2
|
|
|
3
|
-
export function
|
|
3
|
+
export function createTopLevelLibConfigs({ featureRoot }) {
|
|
4
4
|
const libRoot = featureRoot.replace(/features$/, "lib");
|
|
5
5
|
return [
|
|
6
6
|
{
|
|
7
|
-
name: "naming/lib",
|
|
7
|
+
name: "naming/top-level-lib",
|
|
8
8
|
files: [`${libRoot}/**/*.ts`],
|
|
9
9
|
plugins: { "check-file": checkFile },
|
|
10
10
|
rules: {
|
|
@@ -16,7 +16,10 @@ export function createUtilsConfigs({ featureRoot, prefixLibMapping }) {
|
|
|
16
16
|
{
|
|
17
17
|
name: "naming/utils",
|
|
18
18
|
files: featuresGlob(featureRoot, "*/utils/*.ts"),
|
|
19
|
-
ignores:
|
|
19
|
+
ignores: [
|
|
20
|
+
...featuresGlob(featureRoot, "shared/utils/*.ts"),
|
|
21
|
+
"**/*.test.ts",
|
|
22
|
+
],
|
|
20
23
|
plugins: { "check-file": checkFile },
|
|
21
24
|
rules: {
|
|
22
25
|
"check-file/filename-naming-convention": [
|
|
@@ -28,6 +31,7 @@ export function createUtilsConfigs({ featureRoot, prefixLibMapping }) {
|
|
|
28
31
|
{
|
|
29
32
|
name: "naming/utils-shared",
|
|
30
33
|
files: featuresGlob(featureRoot, "shared/utils/*.ts"),
|
|
34
|
+
ignores: ["**/*.test.ts"],
|
|
31
35
|
plugins: { "check-file": checkFile },
|
|
32
36
|
rules: {
|
|
33
37
|
"check-file/filename-naming-convention": [
|
|
@@ -39,7 +39,9 @@ export const entrySingleServiceCallRule = {
|
|
|
39
39
|
},
|
|
40
40
|
messages: {
|
|
41
41
|
multipleServiceCalls:
|
|
42
|
-
"entry '{{ funcName }}'
|
|
42
|
+
"entry '{{ funcName }}' が複数の feature service を呼んでいる ({{ count }} 件):\n" +
|
|
43
|
+
"- entry は単一 service を呼ぶ薄いラッパー、orchestration は service 層へ移す\n" +
|
|
44
|
+
"- `shared/services/*` (例 `sharedDiscordService`) は例外",
|
|
43
45
|
},
|
|
44
46
|
schema: [],
|
|
45
47
|
},
|
|
@@ -485,35 +485,37 @@ export const entryTemplateRule = {
|
|
|
485
485
|
},
|
|
486
486
|
messages: {
|
|
487
487
|
bodyNotTryCatch:
|
|
488
|
-
"entry '{{ funcName }}' body
|
|
489
|
-
|
|
488
|
+
"entry '{{ funcName }}' の body は次のいずれか:\n" +
|
|
489
|
+
"- 単一の try/catch (Pattern A)\n" +
|
|
490
|
+
"- try/catch + 末尾の navigation 呼び出し `redirect(...)` / `notFound(...)` (Pattern B)",
|
|
491
|
+
tryEmpty: "entry '{{ funcName }}' の try block が空。",
|
|
490
492
|
tryMissingStartLog:
|
|
491
|
-
"entry '{{ funcName }}' try block
|
|
493
|
+
"entry '{{ funcName }}' の try block は `logger.info(<obj>, \"Start {{ funcName }}\")` で始める。",
|
|
492
494
|
trySuccessLogMissing:
|
|
493
|
-
"entry '{{ funcName }}' success return
|
|
495
|
+
"entry '{{ funcName }}' の success return の直前に `logger.info(<obj>, \"Success {{ funcName }}\")` が必須。",
|
|
494
496
|
trySuccessReturnMissing:
|
|
495
|
-
"entry '{{ funcName }}'
|
|
497
|
+
"entry '{{ funcName }}' は success return `return { data, error: null }` が必須。",
|
|
496
498
|
failedLogMissing:
|
|
497
|
-
"entry '{{ funcName }}' Failed
|
|
499
|
+
"entry '{{ funcName }}' の Failed 分岐は return 前に `logger.error(<obj>, \"Failed {{ funcName }}\")` を呼ぶ。",
|
|
498
500
|
catchParamWrongType:
|
|
499
|
-
"entry '{{ funcName }}' catch param
|
|
500
|
-
catchEmpty: "entry '{{ funcName }}' catch block
|
|
501
|
+
"entry '{{ funcName }}' の catch param は `error: unknown`。",
|
|
502
|
+
catchEmpty: "entry '{{ funcName }}' の catch block が空。",
|
|
501
503
|
catchMissingErrorLog:
|
|
502
|
-
"entry '{{ funcName }}' catch block
|
|
504
|
+
"entry '{{ funcName }}' の catch block は `logger.error(<obj>, \"Unexpected error in {{ funcName }}\")` で始める。",
|
|
503
505
|
catchLastNotReturn:
|
|
504
|
-
"entry '{{ funcName }}' catch block
|
|
506
|
+
"entry '{{ funcName }}' の catch block は return 文で終える。",
|
|
505
507
|
catchWrongReturnMessage:
|
|
506
|
-
"entry '{{ funcName }}' catch return error.message
|
|
508
|
+
"entry '{{ funcName }}' の catch return の error.message はリテラル '{{ expected }}'。実際: '{{ actual }}'。",
|
|
507
509
|
logWrongCallShape:
|
|
508
|
-
"{{
|
|
510
|
+
"'{{ funcName }}' の {{ where }} log は `logger.{{ expectedLevel }}(<obj>, \"{{ expectedMessage }}\")`。",
|
|
509
511
|
logWrongMessage:
|
|
510
|
-
"{{ where }} log message
|
|
512
|
+
"{{ where }} log message は '{{ expectedMessage }}'。実際: '{{ actual }}'。",
|
|
511
513
|
logFirstArgNotObject:
|
|
512
|
-
"{{
|
|
514
|
+
"'{{ funcName }}' の {{ where }} log の第 1 引数は object literal にする。",
|
|
513
515
|
logErrKeyNotFirst:
|
|
514
|
-
"{{
|
|
516
|
+
"'{{ funcName }}' の {{ where }} log object は `err:` キーで始める。",
|
|
515
517
|
logMissingInputArg:
|
|
516
|
-
"{{
|
|
518
|
+
"'{{ funcName }}' の {{ where }} log に入力引数 '{{ argName }}' が無い。全ての関数入力を log object に伝播する。",
|
|
517
519
|
},
|
|
518
520
|
schema: [],
|
|
519
521
|
},
|
|
@@ -66,7 +66,7 @@ export const featureNameRule = {
|
|
|
66
66
|
type: "problem",
|
|
67
67
|
messages: {
|
|
68
68
|
invalidFeatureName:
|
|
69
|
-
"
|
|
69
|
+
"feature directory '{{ name }}' は許可されない。許可: {{ allowed }}。",
|
|
70
70
|
},
|
|
71
71
|
schema: [
|
|
72
72
|
{
|
|
@@ -93,15 +93,10 @@ export const featureNameRule = {
|
|
|
93
93
|
|
|
94
94
|
const projectRoot = filename.slice(0, rootIdx).replace(/\/src$/, "");
|
|
95
95
|
const candidateTypePaths = [
|
|
96
|
-
path.join(
|
|
97
|
-
projectRoot,
|
|
98
|
-
featureRoot.replace(/features$/, "lib/supabase/types.ts"),
|
|
99
|
-
),
|
|
100
96
|
path.join(
|
|
101
97
|
projectRoot,
|
|
102
98
|
featureRoot.replace(/features$/, "lib/supabase/type.ts"),
|
|
103
99
|
),
|
|
104
|
-
path.join(projectRoot, "src/lib/supabase/types.ts"),
|
|
105
100
|
path.join(projectRoot, "src/lib/supabase/type.ts"),
|
|
106
101
|
];
|
|
107
102
|
const supabaseTypePath =
|
|
@@ -16,7 +16,7 @@ export const formStateNamingRule = {
|
|
|
16
16
|
type: "problem",
|
|
17
17
|
messages: {
|
|
18
18
|
invalidName:
|
|
19
|
-
"FormState
|
|
19
|
+
"FormState 型 '{{ name }}' は {Verb}{Subject}FormState 形式にする (例 CreateCommentFormState, SignInFormState)。PascalCase 2 語以上が必須。",
|
|
20
20
|
},
|
|
21
21
|
schema: [],
|
|
22
22
|
},
|
|
@@ -134,21 +134,21 @@ export const formStateShapeRule = {
|
|
|
134
134
|
},
|
|
135
135
|
messages: {
|
|
136
136
|
dataMissing:
|
|
137
|
-
"FormState '{{ name }}'
|
|
137
|
+
"FormState '{{ name }}' に `data` プロパティが必須 (payload が無ければ `data: null`)。",
|
|
138
138
|
dataNotNullable:
|
|
139
|
-
"FormState '{{ name }}' `data`
|
|
139
|
+
"FormState '{{ name }}' の `data` は null 許容にする (例: `data: T | null` / `data: null`)。",
|
|
140
140
|
errorMissing:
|
|
141
|
-
"FormState '{{ name }}'
|
|
141
|
+
"FormState '{{ name }}' に `error: { message: string } | null` プロパティが必須。",
|
|
142
142
|
errorNotNullable:
|
|
143
|
-
"FormState '{{ name }}' `error`
|
|
143
|
+
"FormState '{{ name }}' の `error` は nullable にする (`{ message: string } | null`)。",
|
|
144
144
|
errorWrongShape:
|
|
145
|
-
"FormState '{{ name }}' `error`
|
|
145
|
+
"FormState '{{ name }}' の `error` は厳密に `{ message: string } | null`。",
|
|
146
146
|
errorExtraField:
|
|
147
|
-
"FormState '{{ name }}' `error`
|
|
147
|
+
"FormState '{{ name }}' の `error` は `message: string` のみ許可。禁止フィールド: '{{ field }}'。",
|
|
148
148
|
extraProperty:
|
|
149
|
-
"FormState '{{ name }}'
|
|
149
|
+
"FormState '{{ name }}' は `data` と `error` のみ。禁止プロパティ: '{{ field }}'。",
|
|
150
150
|
discriminatedUnion:
|
|
151
|
-
"FormState '{{ name }}'
|
|
151
|
+
"FormState '{{ name }}' は単一の interface か type literal にする。discriminated union 不可。",
|
|
152
152
|
},
|
|
153
153
|
schema: [],
|
|
154
154
|
},
|
|
@@ -5,9 +5,9 @@ export const importPathStyleRule = {
|
|
|
5
5
|
type: "problem",
|
|
6
6
|
messages: {
|
|
7
7
|
useRelative:
|
|
8
|
-
"
|
|
8
|
+
"同一 feature の import は相対パスにする ('{{ importPath }}' を使わない)。",
|
|
9
9
|
useAlias:
|
|
10
|
-
"
|
|
10
|
+
"feature を跨ぐ import は '@/' にする (相対パス '{{ importPath }}' を使わない)。",
|
|
11
11
|
},
|
|
12
12
|
schema: [
|
|
13
13
|
{
|
|
@@ -7,6 +7,7 @@ import { importPathStyleRule } from "./import-path-style.mjs";
|
|
|
7
7
|
import { layoutMainStructuralOnlyRule } from "./layout-main-structural-only.mjs";
|
|
8
8
|
import { namespaceImportNameRule } from "./namespace-import-name.mjs";
|
|
9
9
|
import { noAnyReturnRule } from "./no-any-return.mjs";
|
|
10
|
+
import { noColocatedTestRule } from "./no-colocated-test.mjs";
|
|
10
11
|
import { queriesExportRule } from "./queries-export.mjs";
|
|
11
12
|
import { queriesNamespaceImportRule } from "./queries-namespace-import.mjs";
|
|
12
13
|
import { schemaNamingRule } from "./schema-naming.mjs";
|
|
@@ -24,6 +25,7 @@ export const localPlugin = {
|
|
|
24
25
|
"layout-main-structural-only": layoutMainStructuralOnlyRule,
|
|
25
26
|
"namespace-import-name": namespaceImportNameRule,
|
|
26
27
|
"no-any-return": noAnyReturnRule,
|
|
28
|
+
"no-colocated-test": noColocatedTestRule,
|
|
27
29
|
"queries-export": queriesExportRule,
|
|
28
30
|
"queries-namespace-import": queriesNamespaceImportRule,
|
|
29
31
|
"schema-naming": schemaNamingRule,
|
|
@@ -47,7 +47,7 @@ export const layoutMainStructuralOnlyRule = {
|
|
|
47
47
|
type: "problem",
|
|
48
48
|
messages: {
|
|
49
49
|
invalidToken:
|
|
50
|
-
"
|
|
50
|
+
"layout.tsx の <main> は構造のみ。spacing/装飾 ({{ tokens }}) は page.tsx へ移す (例 <Container className=\"py-8\">)。",
|
|
51
51
|
},
|
|
52
52
|
schema: [],
|
|
53
53
|
},
|
|
@@ -55,7 +55,7 @@ export const namespaceImportNameRule = {
|
|
|
55
55
|
type: "suggestion",
|
|
56
56
|
messages: {
|
|
57
57
|
mismatch:
|
|
58
|
-
"
|
|
58
|
+
"namespace import は '{{ expected }}' と命名する ('{{ actual }}' ではなく)。",
|
|
59
59
|
},
|
|
60
60
|
schema: [
|
|
61
61
|
{
|
|
@@ -9,7 +9,7 @@ export const noAnyReturnRule = {
|
|
|
9
9
|
},
|
|
10
10
|
messages: {
|
|
11
11
|
anyInReturn:
|
|
12
|
-
"
|
|
12
|
+
"export 関数の推論された返り値型に `any` が含まれる: {{ typeText }}。既知の型を注釈するか絞り込む (public な層の API は型を確定させる)。",
|
|
13
13
|
},
|
|
14
14
|
schema: [],
|
|
15
15
|
},
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* orchestration layer (services / queries / entries) での co-located test を禁止する:
|
|
3
|
+
*
|
|
4
|
+
* - これらは Supabase 等への配線で、unit 化すると mock の echo になる
|
|
5
|
+
* - 検証は e2e に委ね、純粋ロジックは utils へ抽出してそちらを unit する
|
|
6
|
+
*/
|
|
7
|
+
export const noColocatedTestRule = {
|
|
8
|
+
meta: {
|
|
9
|
+
type: "problem",
|
|
10
|
+
messages: {
|
|
11
|
+
forbidden:
|
|
12
|
+
"orchestration layer (services/queries/entries) に test を置かない。" +
|
|
13
|
+
"mock の echo になる:\n" +
|
|
14
|
+
"- 配線の検証は e2e に委ねる\n" +
|
|
15
|
+
"- 純粋ロジックは utils へ抽出し、そちらを unit する",
|
|
16
|
+
},
|
|
17
|
+
schema: [],
|
|
18
|
+
},
|
|
19
|
+
create(context) {
|
|
20
|
+
return {
|
|
21
|
+
Program(node) {
|
|
22
|
+
context.report({ node, messageId: "forbidden" });
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -29,7 +29,7 @@ export const queriesExportRule = {
|
|
|
29
29
|
type: "problem",
|
|
30
30
|
messages: {
|
|
31
31
|
invalidName:
|
|
32
|
-
"queries export '{{ name }}'
|
|
32
|
+
"queries の export '{{ name }}' は get, create, update, delete, signUp, signIn, signOut のいずれかで始める。",
|
|
33
33
|
},
|
|
34
34
|
schema: [],
|
|
35
35
|
},
|
|
@@ -5,7 +5,7 @@ export const queriesNamespaceImportRule = {
|
|
|
5
5
|
type: "problem",
|
|
6
6
|
messages: {
|
|
7
7
|
useNamespace:
|
|
8
|
-
'
|
|
8
|
+
'queries 層は named import でなく `import * as xxxQuery from "{{ source }}"` を使う (`import type {}` は可)。',
|
|
9
9
|
},
|
|
10
10
|
schema: [],
|
|
11
11
|
},
|
|
@@ -3,9 +3,9 @@ export const schemaNamingRule = {
|
|
|
3
3
|
type: "problem",
|
|
4
4
|
messages: {
|
|
5
5
|
missingSuffix:
|
|
6
|
-
"
|
|
6
|
+
"schema file の export 変数 '{{ name }}' は 'Schema' で終える。",
|
|
7
7
|
invalidCasing:
|
|
8
|
-
"
|
|
8
|
+
"export 変数 '{{ name }}' は camelCase にする (小文字始まり)。",
|
|
9
9
|
},
|
|
10
10
|
schema: [],
|
|
11
11
|
},
|
|
@@ -17,7 +17,7 @@ export const supabaseColumnsSatisfiesRule = {
|
|
|
17
17
|
type: "problem",
|
|
18
18
|
messages: {
|
|
19
19
|
shape:
|
|
20
|
-
'
|
|
20
|
+
'column 定数 `{{ name }}` は `"<comma-separated columns>" as const` にする。`as const` を外すと Supabase の `.select()` 型推論が壊れる。配列 / template literal も不可。',
|
|
21
21
|
},
|
|
22
22
|
schema: [],
|
|
23
23
|
},
|
|
@@ -5,15 +5,15 @@ export const supabaseSelectTypedColumnsRule = {
|
|
|
5
5
|
type: "problem",
|
|
6
6
|
messages: {
|
|
7
7
|
noArgs:
|
|
8
|
-
"
|
|
8
|
+
"空の `.select()` は全列を暗黙取得する。文字列リテラルか `*_COLUMNS` 定数を渡す。",
|
|
9
9
|
wildcard:
|
|
10
|
-
'`.select("*")`
|
|
10
|
+
'`.select("*")` はスキーマ拡張時に列を暗黙露出する。列を明示列挙する。',
|
|
11
11
|
template:
|
|
12
|
-
"
|
|
12
|
+
"`.select()` の template literal は型推論を壊す。文字列リテラルか `*_COLUMNS` 定数を使う。",
|
|
13
13
|
shapeArg:
|
|
14
|
-
"`.select()`
|
|
14
|
+
"`.select()` の引数は文字列リテラルか `*_COLUMNS` 識別子にする。",
|
|
15
15
|
naming:
|
|
16
|
-
"
|
|
16
|
+
"column 定数 `{{ name }}` は `_COLUMNS` で終わる UPPER_SNAKE_CASE にする (例 POST_DETAIL_COLUMNS)。",
|
|
17
17
|
},
|
|
18
18
|
schema: [],
|
|
19
19
|
},
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# src/deno/CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Supabase Edge Functions (Deno) 固有 rule の置き場所判断:
|
|
4
|
+
|
|
5
|
+
- `boundaries/<surface>.mjs` — Deno 固有の boundary (外界 → features の入口)
|
|
6
|
+
- 種類: lib / utils / entry-point
|
|
7
|
+
- `local-plugins/` — Deno 固有の local plugin
|
|
8
|
+
- `index.mjs` — Deno entry。common の rule + 上記 file を合成
|
|
9
|
+
|
|
10
|
+
Deno entry 横断の共通 rule は `src/common/` に追加する。
|
|
@@ -15,17 +15,17 @@ export const denoEntryPointConfigs = [
|
|
|
15
15
|
{
|
|
16
16
|
group: ["**/services/*", "**/services"],
|
|
17
17
|
message:
|
|
18
|
-
"
|
|
18
|
+
"top-level file は services を直接 import 不可。entries 経由で使う。",
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
group: ["**/queries/*", "**/queries"],
|
|
22
22
|
message:
|
|
23
|
-
"
|
|
23
|
+
"top-level file は queries を直接 import 不可。entries 経由で使う。",
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
group: ["*/_lib/*", "*/_lib/**"],
|
|
27
27
|
message:
|
|
28
|
-
"
|
|
28
|
+
"top-level file は _lib/ を直接 import 不可。entries 経由で使う。",
|
|
29
29
|
},
|
|
30
30
|
],
|
|
31
31
|
},
|
|
@@ -11,11 +11,11 @@ export const denoUtilsBoundaryConfigs = [
|
|
|
11
11
|
patterns: [
|
|
12
12
|
{
|
|
13
13
|
group: ["*/_features/*", "*/_features/**"],
|
|
14
|
-
message: "_utils/
|
|
14
|
+
message: "_utils/ は _features/ を import 不可。依存方向を守る。",
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
group: ["*/_lib/*", "*/_lib/**"],
|
|
18
|
-
message: "_utils/
|
|
18
|
+
message: "_utils/ は _lib/ を import 不可。依存方向を守る。",
|
|
19
19
|
},
|
|
20
20
|
],
|
|
21
21
|
},
|
|
@@ -3,7 +3,7 @@ export const flatEntryPointRule = {
|
|
|
3
3
|
type: "problem",
|
|
4
4
|
messages: {
|
|
5
5
|
nested:
|
|
6
|
-
"Edge Function entry
|
|
6
|
+
"Edge Function の entry point は supabase/functions/ 直下に置く。ネストした directory (例 commands/{{name}}) は Supabase CLI 非対応。",
|
|
7
7
|
},
|
|
8
8
|
schema: [],
|
|
9
9
|
},
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# src/next/CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Next.js 固有 rule の置き場所判断:
|
|
4
|
+
|
|
5
|
+
- `boundaries/<surface>.mjs` — Next.js 固有の boundary (外界 → features の入口)
|
|
6
|
+
- 種類: page / route / sitemap / hooks / components / lib
|
|
7
|
+
- `layers/<layer>.mjs` — Next.js 固有の layer
|
|
8
|
+
- 種類: components / hooks / layouts
|
|
9
|
+
- `directives.mjs` — `"use server"` / `"use client"` 等の directive 制約
|
|
10
|
+
- `imports.mjs` — Next.js 固有の import パターン
|
|
11
|
+
- `tailwindcss.mjs` — Tailwind CSS rule (margin 禁止等)
|
|
12
|
+
- `index.mjs` — Next.js entry。common の rule + 上記 file を合成
|
|
13
|
+
|
|
14
|
+
Next.js entry 横断の共通 rule は `src/common/` に追加する。
|
|
@@ -7,12 +7,12 @@ const COMPONENTS_BOUNDARY_PATTERNS = [
|
|
|
7
7
|
{
|
|
8
8
|
group: ["**/queries/*", "**/queries"],
|
|
9
9
|
message:
|
|
10
|
-
"components
|
|
10
|
+
"components は queries を直接 import 不可。entries か hooks 経由で使う。",
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
group: ["**/services/*", "**/services"],
|
|
14
14
|
message:
|
|
15
|
-
"components
|
|
15
|
+
"components は services を直接 import 不可。entries か hooks 経由で使う。",
|
|
16
16
|
},
|
|
17
17
|
];
|
|
18
18
|
|
|
@@ -7,12 +7,12 @@ const HOOKS_BOUNDARY_PATTERNS = [
|
|
|
7
7
|
{
|
|
8
8
|
group: ["**/queries/*", "**/queries"],
|
|
9
9
|
message:
|
|
10
|
-
"hooks
|
|
10
|
+
"hooks は queries を直接 import 不可。entries 経由で使う。",
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
group: ["**/services/*", "**/services"],
|
|
14
14
|
message:
|
|
15
|
-
"hooks
|
|
15
|
+
"hooks は services を直接 import 不可。entries 経由で使う。",
|
|
16
16
|
},
|
|
17
17
|
];
|
|
18
18
|
|
|
@@ -7,12 +7,12 @@ const PAGE_BOUNDARY_PATTERNS = [
|
|
|
7
7
|
{
|
|
8
8
|
group: ["**/queries/*", "**/queries"],
|
|
9
9
|
message:
|
|
10
|
-
"page.tsx
|
|
10
|
+
"page.tsx は queries を直接 import 不可。entries 経由で使う。",
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
group: ["**/services/*", "**/services"],
|
|
14
14
|
message:
|
|
15
|
-
"page.tsx
|
|
15
|
+
"page.tsx は services を直接 import 不可。entries 経由で使う。",
|
|
16
16
|
},
|
|
17
17
|
];
|
|
18
18
|
|
|
@@ -7,12 +7,12 @@ const ROUTE_BOUNDARY_PATTERNS = [
|
|
|
7
7
|
{
|
|
8
8
|
group: ["**/queries/*", "**/queries"],
|
|
9
9
|
message:
|
|
10
|
-
"route.ts
|
|
10
|
+
"route.ts は queries を直接 import 不可。entries 経由で使う。",
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
group: ["**/services/*", "**/services"],
|
|
14
14
|
message:
|
|
15
|
-
"route.ts
|
|
15
|
+
"route.ts は services を直接 import 不可。entries 経由で使う。",
|
|
16
16
|
},
|
|
17
17
|
];
|
|
18
18
|
|
|
@@ -7,12 +7,12 @@ const SITEMAP_BOUNDARY_PATTERNS = [
|
|
|
7
7
|
{
|
|
8
8
|
group: ["**/queries/*", "**/queries"],
|
|
9
9
|
message:
|
|
10
|
-
"sitemap.ts
|
|
10
|
+
"sitemap.ts は queries を直接 import 不可。entries 経由で使う。",
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
group: ["**/services/*", "**/services"],
|
|
14
14
|
message:
|
|
15
|
-
"sitemap.ts
|
|
15
|
+
"sitemap.ts は services を直接 import 不可。entries 経由で使う。",
|
|
16
16
|
},
|
|
17
17
|
];
|
|
18
18
|
|