@yasainet/eslint 0.0.60 → 0.0.62
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 +2 -2
- package/package.json +1 -1
- package/src/common/constants.mjs +3 -3
- package/src/common/imports.mjs +34 -34
- package/src/common/layers.mjs +8 -8
- package/src/common/local-plugins/feature-name.mjs +10 -12
- package/src/common/local-plugins/namespace-import-name.mjs +1 -1
- package/src/common/naming.mjs +7 -7
- package/src/deno/imports.mjs +3 -3
- package/src/next/directives.mjs +9 -9
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ Each entry point enforces a feature-based architecture. **Files do not carry rol
|
|
|
25
25
|
```text
|
|
26
26
|
{featureRoot}/
|
|
27
27
|
├── {feature}/
|
|
28
|
-
│ ├──
|
|
28
|
+
│ ├── entries/ # entry points called from page.tsx / route.ts / hooks (server.ts / admin.ts / client.ts)
|
|
29
29
|
│ ├── services/ # business logic (server.ts ...)
|
|
30
30
|
│ ├── queries/ # data access (one file per upstream lib: <lib-name>.ts)
|
|
31
31
|
│ ├── types/ # type defs (one file per feature: <feature>.ts)
|
|
@@ -53,7 +53,7 @@ For single-client libs the prefix mapping registers only the directory name, aut
|
|
|
53
53
|
|
|
54
54
|
### File naming rules
|
|
55
55
|
|
|
56
|
-
- **No multi-extension suffixes** (`.lib`, `.service`, `.query`, `.util`, `.type`, `.schema`, `.constant`, `.
|
|
56
|
+
- **No multi-extension suffixes** (`.lib`, `.service`, `.query`, `.util`, `.type`, `.schema`, `.constant`, `.entry` are forbidden). The directory carries the role.
|
|
57
57
|
- `lib/<dir>/index.ts` for single-client lib entries (avoids `lib/<dir>/<dir>.ts` redundancy).
|
|
58
58
|
- `lib/<dir>/types.ts` and `lib/<dir>/proxy.ts` are excluded from the prefix mapping so queries cannot directly depend on them.
|
|
59
59
|
- `<feature>/{types,schemas,utils,constants}/<feature>.ts` — exactly one file per feature, named after the feature.
|
package/package.json
CHANGED
package/src/common/constants.mjs
CHANGED
|
@@ -23,10 +23,10 @@ const PROJECT_ROOT = findProjectRoot();
|
|
|
23
23
|
/**
|
|
24
24
|
* Files / basenames that should never become a prefix:
|
|
25
25
|
*
|
|
26
|
-
* - `types.ts`: 型定義のみで lib の役割を持たない
|
|
26
|
+
* - `types.ts` / `type.ts`: 型定義のみで lib の役割を持たない (単複どちらの命名でも除外)
|
|
27
27
|
* - `proxy.ts`: middleware adapter (Next.js の proxy.ts と意味が衝突するため queries から呼ばせない)
|
|
28
28
|
*/
|
|
29
|
-
const EXCLUDE_LIST = ["types.ts", "proxy.ts"];
|
|
29
|
+
const EXCLUDE_LIST = ["types.ts", "type.ts", "proxy.ts"];
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Scan lib directory and build prefix-to-lib-relative-path mapping:
|
|
@@ -34,7 +34,7 @@ const EXCLUDE_LIST = ["types.ts", "proxy.ts"];
|
|
|
34
34
|
* - single-client lib (`lib/<dir>/index.ts`): prefix = dir 名、entry のみ登録 — 同 dir 内の他ファイル (parser 等 sub-module) は自動除外
|
|
35
35
|
* - multi-client lib (index.ts なし): dir 内の全 `<role>.ts` を登録 (e.g., supabase の admin / server / client)
|
|
36
36
|
* - 多重拡張子 (`.test.ts` 等) を持つファイルは sub-module / 非 lib として除外
|
|
37
|
-
* - types.ts / proxy.ts のような lib として queries から呼ばせたくないものは EXCLUDE_LIST で除外
|
|
37
|
+
* - types.ts / type.ts / proxy.ts のような lib として queries から呼ばせたくないものは EXCLUDE_LIST で除外
|
|
38
38
|
*/
|
|
39
39
|
export function generatePrefixLibMapping(featureRoot) {
|
|
40
40
|
const libRoot = featureRoot.replace(/features$/, "lib");
|
package/src/common/imports.mjs
CHANGED
|
@@ -5,8 +5,8 @@ const LAYER_PATTERNS = {
|
|
|
5
5
|
message: "queries cannot import services (layer violation)",
|
|
6
6
|
},
|
|
7
7
|
{
|
|
8
|
-
group: ["**/
|
|
9
|
-
message: "queries cannot import
|
|
8
|
+
group: ["**/entries/*", "**/entries"],
|
|
9
|
+
message: "queries cannot import entries (layer violation)",
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
group: ["**/hooks/*", "**/hooks"],
|
|
@@ -15,22 +15,22 @@ const LAYER_PATTERNS = {
|
|
|
15
15
|
],
|
|
16
16
|
services: [
|
|
17
17
|
{
|
|
18
|
-
group: ["**/
|
|
19
|
-
message: "services cannot import
|
|
18
|
+
group: ["**/entries/*", "**/entries"],
|
|
19
|
+
message: "services cannot import entries (layer violation)",
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
group: ["**/hooks/*", "**/hooks"],
|
|
23
23
|
message: "services cannot import hooks (layer violation)",
|
|
24
24
|
},
|
|
25
25
|
],
|
|
26
|
-
|
|
26
|
+
entries: [
|
|
27
27
|
{
|
|
28
28
|
group: ["**/queries/*", "**/queries"],
|
|
29
|
-
message: "
|
|
29
|
+
message: "entries cannot import queries (layer violation)",
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
group: ["**/hooks/*", "**/hooks"],
|
|
33
|
-
message: "
|
|
33
|
+
message: "entries cannot import hooks (layer violation)",
|
|
34
34
|
},
|
|
35
35
|
],
|
|
36
36
|
};
|
|
@@ -50,11 +50,11 @@ const LATERAL_PATTERNS = {
|
|
|
50
50
|
"services cannot import other feature's services (lateral violation)",
|
|
51
51
|
},
|
|
52
52
|
],
|
|
53
|
-
|
|
53
|
+
entries: [
|
|
54
54
|
{
|
|
55
|
-
group: ["@/features/*/
|
|
55
|
+
group: ["@/features/*/entries/*", "@/features/*/entries"],
|
|
56
56
|
message:
|
|
57
|
-
"
|
|
57
|
+
"entries cannot import other feature's entries (lateral violation)",
|
|
58
58
|
},
|
|
59
59
|
],
|
|
60
60
|
};
|
|
@@ -64,21 +64,21 @@ const CARDINALITY_PATTERNS = {
|
|
|
64
64
|
{
|
|
65
65
|
group: ["**/services/client", "**/services/admin"],
|
|
66
66
|
message:
|
|
67
|
-
"server
|
|
67
|
+
"server entry can only import server service (cardinality violation)",
|
|
68
68
|
},
|
|
69
69
|
],
|
|
70
70
|
client: [
|
|
71
71
|
{
|
|
72
72
|
group: ["**/services/server", "**/services/admin"],
|
|
73
73
|
message:
|
|
74
|
-
"client
|
|
74
|
+
"client entry can only import client service (cardinality violation)",
|
|
75
75
|
},
|
|
76
76
|
],
|
|
77
77
|
admin: [
|
|
78
78
|
{
|
|
79
79
|
group: ["**/services/server", "**/services/client"],
|
|
80
80
|
message:
|
|
81
|
-
"admin
|
|
81
|
+
"admin entry can only import admin service (cardinality violation)",
|
|
82
82
|
},
|
|
83
83
|
],
|
|
84
84
|
};
|
|
@@ -115,12 +115,12 @@ const PAGE_BOUNDARY_PATTERNS = [
|
|
|
115
115
|
{
|
|
116
116
|
group: ["**/queries/*", "**/queries"],
|
|
117
117
|
message:
|
|
118
|
-
"page.tsx can only import
|
|
118
|
+
"page.tsx can only import entries, not queries (page-boundary violation)",
|
|
119
119
|
},
|
|
120
120
|
{
|
|
121
121
|
group: ["**/services/*", "**/services"],
|
|
122
122
|
message:
|
|
123
|
-
"page.tsx can only import
|
|
123
|
+
"page.tsx can only import entries, not services (page-boundary violation)",
|
|
124
124
|
},
|
|
125
125
|
];
|
|
126
126
|
|
|
@@ -128,12 +128,12 @@ const ROUTE_BOUNDARY_PATTERNS = [
|
|
|
128
128
|
{
|
|
129
129
|
group: ["**/queries/*", "**/queries"],
|
|
130
130
|
message:
|
|
131
|
-
"route.ts can only import
|
|
131
|
+
"route.ts can only import entries, not queries (route-boundary violation)",
|
|
132
132
|
},
|
|
133
133
|
{
|
|
134
134
|
group: ["**/services/*", "**/services"],
|
|
135
135
|
message:
|
|
136
|
-
"route.ts can only import
|
|
136
|
+
"route.ts can only import entries, not services (route-boundary violation)",
|
|
137
137
|
},
|
|
138
138
|
];
|
|
139
139
|
|
|
@@ -141,12 +141,12 @@ const HOOKS_BOUNDARY_PATTERNS = [
|
|
|
141
141
|
{
|
|
142
142
|
group: ["**/queries/*", "**/queries"],
|
|
143
143
|
message:
|
|
144
|
-
"hooks can only import
|
|
144
|
+
"hooks can only import entries, not queries (hooks-boundary violation)",
|
|
145
145
|
},
|
|
146
146
|
{
|
|
147
147
|
group: ["**/services/*", "**/services"],
|
|
148
148
|
message:
|
|
149
|
-
"hooks can only import
|
|
149
|
+
"hooks can only import entries, not services (hooks-boundary violation)",
|
|
150
150
|
},
|
|
151
151
|
];
|
|
152
152
|
|
|
@@ -154,12 +154,12 @@ const COMPONENTS_BOUNDARY_PATTERNS = [
|
|
|
154
154
|
{
|
|
155
155
|
group: ["**/queries/*", "**/queries"],
|
|
156
156
|
message:
|
|
157
|
-
"components can only import
|
|
157
|
+
"components can only import entries or hooks, not queries (components-boundary violation)",
|
|
158
158
|
},
|
|
159
159
|
{
|
|
160
160
|
group: ["**/services/*", "**/services"],
|
|
161
161
|
message:
|
|
162
|
-
"components can only import
|
|
162
|
+
"components can only import entries or hooks, not services (components-boundary violation)",
|
|
163
163
|
},
|
|
164
164
|
];
|
|
165
165
|
|
|
@@ -180,7 +180,7 @@ function makeConfig(name, files, ...patternArrays) {
|
|
|
180
180
|
// ones — patterns are not merged. Page / hooks / components boundary configs
|
|
181
181
|
// run after libBoundaryConfigs and would silently drop lib + mapping bans
|
|
182
182
|
// unless we re-include those patterns explicitly.
|
|
183
|
-
/** Next.js-only: restrict page.tsx to only import
|
|
183
|
+
/** Next.js-only: restrict page.tsx to only import entries. */
|
|
184
184
|
export const pageBoundaryConfigs = [
|
|
185
185
|
{
|
|
186
186
|
name: "imports/page-boundary",
|
|
@@ -200,7 +200,7 @@ export const pageBoundaryConfigs = [
|
|
|
200
200
|
},
|
|
201
201
|
];
|
|
202
202
|
|
|
203
|
-
/** Next.js-only: restrict route.ts to only import
|
|
203
|
+
/** Next.js-only: restrict route.ts to only import entries. */
|
|
204
204
|
export const routeBoundaryConfigs = [
|
|
205
205
|
{
|
|
206
206
|
name: "imports/route-boundary",
|
|
@@ -220,7 +220,7 @@ export const routeBoundaryConfigs = [
|
|
|
220
220
|
},
|
|
221
221
|
];
|
|
222
222
|
|
|
223
|
-
/** Next.js-only: restrict hooks to only import
|
|
223
|
+
/** Next.js-only: restrict hooks to only import entries (not queries or services). */
|
|
224
224
|
export const hooksBoundaryConfigs = [
|
|
225
225
|
{
|
|
226
226
|
name: "imports/hooks-boundary",
|
|
@@ -240,7 +240,7 @@ export const hooksBoundaryConfigs = [
|
|
|
240
240
|
},
|
|
241
241
|
];
|
|
242
242
|
|
|
243
|
-
/** Next.js-only: restrict components to only import
|
|
243
|
+
/** Next.js-only: restrict components to only import entries or hooks (not queries or services). */
|
|
244
244
|
export const componentsBoundaryConfigs = [
|
|
245
245
|
{
|
|
246
246
|
name: "imports/components-boundary",
|
|
@@ -350,10 +350,10 @@ export function createImportsConfigs(
|
|
|
350
350
|
|
|
351
351
|
configs.push(
|
|
352
352
|
makeConfig(
|
|
353
|
-
"
|
|
354
|
-
[`${featureRoot}/**/
|
|
355
|
-
LAYER_PATTERNS.
|
|
356
|
-
LATERAL_PATTERNS.
|
|
353
|
+
"entries",
|
|
354
|
+
[`${featureRoot}/**/entries/*.ts`],
|
|
355
|
+
LAYER_PATTERNS.entries,
|
|
356
|
+
LATERAL_PATTERNS.entries,
|
|
357
357
|
LIB_BOUNDARY_PATTERNS,
|
|
358
358
|
MAPPING_PATTERNS,
|
|
359
359
|
),
|
|
@@ -376,7 +376,7 @@ export function createImportsConfigs(
|
|
|
376
376
|
ignores: [
|
|
377
377
|
`${featureRoot}/**/services/*.ts`,
|
|
378
378
|
`${featureRoot}/**/queries/*.ts`,
|
|
379
|
-
`${featureRoot}/**/
|
|
379
|
+
`${featureRoot}/**/entries/*.ts`,
|
|
380
380
|
`${featureRoot}/**/utils/*.ts`,
|
|
381
381
|
`${featureRoot}/**/types/*.ts`,
|
|
382
382
|
],
|
|
@@ -400,10 +400,10 @@ export function createImportsConfigs(
|
|
|
400
400
|
for (const prefix of ["server", "client", "admin"]) {
|
|
401
401
|
configs.push(
|
|
402
402
|
makeConfig(
|
|
403
|
-
`
|
|
404
|
-
[`${featureRoot}/**/
|
|
405
|
-
LAYER_PATTERNS.
|
|
406
|
-
LATERAL_PATTERNS.
|
|
403
|
+
`entries/${prefix}`,
|
|
404
|
+
[`${featureRoot}/**/entries/${prefix}.ts`],
|
|
405
|
+
LAYER_PATTERNS.entries,
|
|
406
|
+
LATERAL_PATTERNS.entries,
|
|
407
407
|
CARDINALITY_PATTERNS[prefix],
|
|
408
408
|
LIB_BOUNDARY_PATTERNS,
|
|
409
409
|
MAPPING_PATTERNS,
|
package/src/common/layers.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { localPlugin } from "./local-plugins/index.mjs";
|
|
|
11
11
|
export function createLayersConfigs(featureRoot, { typeAware = true } = {}) {
|
|
12
12
|
const loggerSelector = "CallExpression[callee.object.name='logger']";
|
|
13
13
|
const loggerMessage =
|
|
14
|
-
"logger is not allowed outside
|
|
14
|
+
"logger is not allowed outside entries. Logging belongs in entries.";
|
|
15
15
|
|
|
16
16
|
const noAnyReturnConfig = {
|
|
17
17
|
name: "layers/no-any-return",
|
|
@@ -26,11 +26,11 @@ export function createLayersConfigs(featureRoot, { typeAware = true } = {}) {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
return [
|
|
29
|
-
// Logger/console: all features except
|
|
29
|
+
// Logger/console: all features except entries
|
|
30
30
|
{
|
|
31
31
|
name: "layers/logger",
|
|
32
32
|
files: [`${featureRoot}/**/*.ts`],
|
|
33
|
-
ignores: [`${featureRoot}/**/
|
|
33
|
+
ignores: [`${featureRoot}/**/entries/*.ts`],
|
|
34
34
|
rules: {
|
|
35
35
|
"no-console": "error",
|
|
36
36
|
"no-restricted-syntax": [
|
|
@@ -49,7 +49,7 @@ export function createLayersConfigs(featureRoot, { typeAware = true } = {}) {
|
|
|
49
49
|
{
|
|
50
50
|
selector: "TryStatement",
|
|
51
51
|
message:
|
|
52
|
-
"try-catch is not allowed in queries. Error handling belongs in
|
|
52
|
+
"try-catch is not allowed in queries. Error handling belongs in entries.",
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
selector: "IfStatement",
|
|
@@ -84,7 +84,7 @@ export function createLayersConfigs(featureRoot, { typeAware = true } = {}) {
|
|
|
84
84
|
{
|
|
85
85
|
selector: "ThrowStatement",
|
|
86
86
|
message:
|
|
87
|
-
"throw is not allowed in queries. Queries must return Supabase's { data, error } shape as-is. Error handling belongs in
|
|
87
|
+
"throw is not allowed in queries. Queries must return Supabase's { data, error } shape as-is. Error handling belongs in entries.",
|
|
88
88
|
},
|
|
89
89
|
{ selector: loggerSelector, message: loggerMessage },
|
|
90
90
|
],
|
|
@@ -104,20 +104,20 @@ export function createLayersConfigs(featureRoot, { typeAware = true } = {}) {
|
|
|
104
104
|
{
|
|
105
105
|
selector: "TryStatement",
|
|
106
106
|
message:
|
|
107
|
-
"try-catch is not allowed in services. Error handling belongs in
|
|
107
|
+
"try-catch is not allowed in services. Error handling belongs in entries.",
|
|
108
108
|
},
|
|
109
109
|
{ selector: loggerSelector, message: loggerMessage },
|
|
110
110
|
{
|
|
111
111
|
selector:
|
|
112
112
|
"LogicalExpression[operator='??'][left.type='ChainExpression'][left.expression.property.name='message'][right.type='Literal']",
|
|
113
113
|
message:
|
|
114
|
-
"Dead fallback for error message. If you reached this branch the error is known — return the error directly. Unhandled exceptions belong in
|
|
114
|
+
"Dead fallback for error message. If you reached this branch the error is known — return the error directly. Unhandled exceptions belong in entries.",
|
|
115
115
|
},
|
|
116
116
|
{
|
|
117
117
|
selector:
|
|
118
118
|
"LogicalExpression[operator='??'][left.type='MemberExpression'][left.property.name='error'][right.type='ObjectExpression']",
|
|
119
119
|
message:
|
|
120
|
-
"Dead fallback for nullable error. Check `if (error)` and return the error directly. Unhandled exceptions belong in
|
|
120
|
+
"Dead fallback for nullable error. Check `if (error)` and return the error directly. Unhandled exceptions belong in entries.",
|
|
121
121
|
},
|
|
122
122
|
],
|
|
123
123
|
},
|
|
@@ -110,18 +110,16 @@ export const featureNameRule = {
|
|
|
110
110
|
// Prefer the Supabase types file adjacent to `featureRoot` (e.g. `src/lib/...`
|
|
111
111
|
// for `src/features`). Fall back to `src/lib/...` at the project root so that
|
|
112
112
|
// non-`src` feature roots (e.g. `scripts/features`) can reuse the same
|
|
113
|
-
// generated types without duplicating the file.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
featureRoot.replace(/features$/, "lib/supabase/types.ts"),
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
projectRoot,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
? computedTypePath
|
|
124
|
-
: fallbackTypePath;
|
|
113
|
+
// generated types without duplicating the file. Both `types.ts` (plural) and
|
|
114
|
+
// `type.ts` (singular) are accepted to match either naming convention.
|
|
115
|
+
const candidateTypePaths = [
|
|
116
|
+
path.join(projectRoot, featureRoot.replace(/features$/, "lib/supabase/types.ts")),
|
|
117
|
+
path.join(projectRoot, featureRoot.replace(/features$/, "lib/supabase/type.ts")),
|
|
118
|
+
path.join(projectRoot, "src/lib/supabase/types.ts"),
|
|
119
|
+
path.join(projectRoot, "src/lib/supabase/type.ts"),
|
|
120
|
+
];
|
|
121
|
+
const supabaseTypePath =
|
|
122
|
+
candidateTypePaths.find((p) => fs.existsSync(p)) ?? candidateTypePaths[0];
|
|
125
123
|
|
|
126
124
|
const tableNames = extractTableNames(supabaseTypePath);
|
|
127
125
|
const allowedNames = ["shared", "auth", ...tableNames.map(toKebab)];
|
package/src/common/naming.mjs
CHANGED
|
@@ -52,9 +52,9 @@ export function createUtilsNamingConfigs(featureRoot) {
|
|
|
52
52
|
/**
|
|
53
53
|
* Scope naming rules to the given feature root:
|
|
54
54
|
*
|
|
55
|
-
* - 全 layer (services / queries /
|
|
55
|
+
* - 全 layer (services / queries / entries / utils / types / schemas / constants) で suffix を廃止
|
|
56
56
|
* - ファイル名 (basename) は単一トークン (`*` パターン) を強制し、role はディレクトリで宣言する
|
|
57
|
-
* - queries / services /
|
|
57
|
+
* - queries / services / entries のファイル名は prefixLibMapping のキー (lib name) と一致させ、どの lib を呼ぶか明示する
|
|
58
58
|
* - shared/ 配下では feature 名でなく `shared` または lib name を allowed prefix として許可する
|
|
59
59
|
*/
|
|
60
60
|
export function createNamingConfigs(featureRoot, prefixLibMapping) {
|
|
@@ -284,9 +284,9 @@ export function createNamingConfigs(featureRoot, prefixLibMapping) {
|
|
|
284
284
|
);
|
|
285
285
|
|
|
286
286
|
configs.push({
|
|
287
|
-
name: "naming/
|
|
288
|
-
files: featuresGlob(featureRoot, "**/
|
|
289
|
-
ignores: featuresGlob(featureRoot, "shared/
|
|
287
|
+
name: "naming/entries",
|
|
288
|
+
files: featuresGlob(featureRoot, "**/entries/*.ts"),
|
|
289
|
+
ignores: featuresGlob(featureRoot, "shared/entries/*.ts"),
|
|
290
290
|
plugins: { "check-file": checkFile },
|
|
291
291
|
rules: {
|
|
292
292
|
"check-file/filename-naming-convention": [
|
|
@@ -298,8 +298,8 @@ export function createNamingConfigs(featureRoot, prefixLibMapping) {
|
|
|
298
298
|
|
|
299
299
|
configs.push(
|
|
300
300
|
{
|
|
301
|
-
name: "naming/
|
|
302
|
-
files: featuresGlob(featureRoot, "shared/
|
|
301
|
+
name: "naming/entries-shared",
|
|
302
|
+
files: featuresGlob(featureRoot, "shared/entries/*.ts"),
|
|
303
303
|
plugins: { "check-file": checkFile },
|
|
304
304
|
rules: {
|
|
305
305
|
"check-file/filename-naming-convention": [
|
package/src/deno/imports.mjs
CHANGED
|
@@ -40,17 +40,17 @@ export const denoImportsConfigs = [
|
|
|
40
40
|
{
|
|
41
41
|
group: ["**/services/*", "**/services"],
|
|
42
42
|
message:
|
|
43
|
-
"
|
|
43
|
+
"Top-level files must not import services directly. Import from entries instead.",
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
group: ["**/queries/*", "**/queries"],
|
|
47
47
|
message:
|
|
48
|
-
"
|
|
48
|
+
"Top-level files must not import queries directly. Import from entries instead.",
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
group: ["*/_lib/*", "*/_lib/**"],
|
|
52
52
|
message:
|
|
53
|
-
"
|
|
53
|
+
"Top-level files must not import _lib/ directly. Import from entries instead.",
|
|
54
54
|
},
|
|
55
55
|
],
|
|
56
56
|
},
|
package/src/next/directives.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** Enforce "use server" / "use client" directives per file convention. */
|
|
2
2
|
export const directivesConfigs = [
|
|
3
3
|
{
|
|
4
|
-
name: "directives/server-
|
|
5
|
-
files: ["src/features/**/
|
|
4
|
+
name: "directives/server-entry",
|
|
5
|
+
files: ["src/features/**/entries/server.ts"],
|
|
6
6
|
rules: {
|
|
7
7
|
"no-restricted-syntax": [
|
|
8
8
|
"error",
|
|
@@ -10,14 +10,14 @@ export const directivesConfigs = [
|
|
|
10
10
|
selector:
|
|
11
11
|
"Program > :first-child:not(ExpressionStatement[expression.value='use server'])",
|
|
12
12
|
message:
|
|
13
|
-
'
|
|
13
|
+
'entries/server.ts must start with "use server" directive.',
|
|
14
14
|
},
|
|
15
15
|
],
|
|
16
16
|
},
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
|
-
name: "directives/admin-
|
|
20
|
-
files: ["src/features/**/
|
|
19
|
+
name: "directives/admin-entry",
|
|
20
|
+
files: ["src/features/**/entries/admin.ts"],
|
|
21
21
|
rules: {
|
|
22
22
|
"no-restricted-syntax": [
|
|
23
23
|
"error",
|
|
@@ -25,21 +25,21 @@ export const directivesConfigs = [
|
|
|
25
25
|
selector:
|
|
26
26
|
"Program > :first-child:not(ExpressionStatement[expression.value='use server'])",
|
|
27
27
|
message:
|
|
28
|
-
'
|
|
28
|
+
'entries/admin.ts must start with "use server" directive.',
|
|
29
29
|
},
|
|
30
30
|
],
|
|
31
31
|
},
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
|
-
name: "directives/client-
|
|
35
|
-
files: ["src/features/**/
|
|
34
|
+
name: "directives/client-entry",
|
|
35
|
+
files: ["src/features/**/entries/client.ts"],
|
|
36
36
|
rules: {
|
|
37
37
|
"no-restricted-syntax": [
|
|
38
38
|
"error",
|
|
39
39
|
{
|
|
40
40
|
selector: "ExpressionStatement[expression.value='use server']",
|
|
41
41
|
message:
|
|
42
|
-
'
|
|
42
|
+
'entries/client.ts must NOT have "use server" directive. It uses @/lib/supabase/client.',
|
|
43
43
|
},
|
|
44
44
|
],
|
|
45
45
|
},
|