@yousxlfs/next-arch 0.1.0

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.
Files changed (52) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +255 -0
  3. package/dist/index.js.map +1 -0
  4. package/package.json +58 -0
  5. package/templates/app/AGENTS.md +5 -0
  6. package/templates/app/CLAUDE.md +1 -0
  7. package/templates/app/README.md +36 -0
  8. package/templates/app/components.json +25 -0
  9. package/templates/app/eslint.config.mjs +33 -0
  10. package/templates/app/next-env.d.ts +6 -0
  11. package/templates/app/next.config.ts +7 -0
  12. package/templates/app/package.json +37 -0
  13. package/templates/app/postcss.config.mjs +7 -0
  14. package/templates/app/public/file.svg +1 -0
  15. package/templates/app/public/globe.svg +1 -0
  16. package/templates/app/public/next.svg +1 -0
  17. package/templates/app/public/vercel.svg +1 -0
  18. package/templates/app/public/window.svg +1 -0
  19. package/templates/app/src/app/favicon.ico +0 -0
  20. package/templates/app/src/app/globals.css +130 -0
  21. package/templates/app/src/app/layout.tsx +33 -0
  22. package/templates/app/src/app/page.tsx +6 -0
  23. package/templates/app/src/components/ui/button.tsx +67 -0
  24. package/templates/app/src/features/demo/actions/submitDemo.ts +5 -0
  25. package/templates/app/src/features/demo/api/fetchDemo.ts +3 -0
  26. package/templates/app/src/features/demo/index.ts +4 -0
  27. package/templates/app/src/features/demo/lib/formatDemo.ts +3 -0
  28. package/templates/app/src/features/demo/model/index.ts +2 -0
  29. package/templates/app/src/features/demo/model/types.ts +7 -0
  30. package/templates/app/src/features/demo/ui/FeatureDemo.tsx +18 -0
  31. package/templates/app/src/lib/utils.ts +1 -0
  32. package/templates/app/src/shared/lib/utils.ts +6 -0
  33. package/templates/app/src/shared/ui/index.ts +2 -0
  34. package/templates/app/src/views/HomeView/index.tsx +21 -0
  35. package/templates/app/tsconfig.json +40 -0
  36. package/templates/entity/index.ts +1 -0
  37. package/templates/entity/model/types.ts +3 -0
  38. package/templates/entity/ui/{{Name}}Card.tsx +8 -0
  39. package/templates/feature/actions/submit{{Name}}.ts +5 -0
  40. package/templates/feature/api/fetch{{Name}}.ts +3 -0
  41. package/templates/feature/index.ts +4 -0
  42. package/templates/feature/lib/format{{Name}}.ts +3 -0
  43. package/templates/feature/model/index.ts +5 -0
  44. package/templates/feature/model/types.ts +7 -0
  45. package/templates/feature/ui/{{Name}}.tsx +10 -0
  46. package/templates/view/index.tsx +12 -0
  47. package/templates/widget/index.ts +1 -0
  48. package/templates/widget/ui/{{Name}}.tsx +9 -0
  49. package/vendor/eslint-plugin-next-arch/dist/index.d.ts +50 -0
  50. package/vendor/eslint-plugin-next-arch/dist/index.js +386 -0
  51. package/vendor/eslint-plugin-next-arch/dist/index.js.map +1 -0
  52. package/vendor/eslint-plugin-next-arch/package.json +55 -0
@@ -0,0 +1,386 @@
1
+ // src/utils/import-visitors.ts
2
+ function visitImportSources(context, onImport) {
3
+ return {
4
+ ImportDeclaration(node) {
5
+ onImport(node, String(node.source.value));
6
+ },
7
+ ExportNamedDeclaration(node) {
8
+ const exportNode = node;
9
+ if (exportNode.source?.value) {
10
+ onImport(exportNode, String(exportNode.source.value));
11
+ }
12
+ },
13
+ ExportAllDeclaration(node) {
14
+ const exportNode = node;
15
+ onImport(exportNode, String(exportNode.source.value));
16
+ },
17
+ ImportExpression(node) {
18
+ const expression = node;
19
+ if (expression.source.type === "Literal" && typeof expression.source.value === "string") {
20
+ onImport(expression, expression.source.value);
21
+ }
22
+ },
23
+ CallExpression(node) {
24
+ const call = node;
25
+ if (call.callee.type === "Identifier" && call.callee.name === "require" && call.arguments[0]?.type === "Literal" && typeof call.arguments[0].value === "string") {
26
+ onImport(call, call.arguments[0].value);
27
+ }
28
+ }
29
+ };
30
+ }
31
+ function hasUseClientDirective(context) {
32
+ const program = context.getSourceCode().ast;
33
+ const body = program.body ?? [];
34
+ return body.some((statement) => {
35
+ if (statement.type !== "ExpressionStatement") return false;
36
+ const expressionStatement = statement;
37
+ return expressionStatement.directive === "use client" || expressionStatement.expression?.type === "Literal" && expressionStatement.expression.value === "use client";
38
+ });
39
+ }
40
+
41
+ // src/utils/layers.ts
42
+ import fs from "fs";
43
+ import path from "path";
44
+ var LAYERS = [
45
+ "shared",
46
+ "entities",
47
+ "features",
48
+ "widgets",
49
+ "views",
50
+ "app"
51
+ ];
52
+ var LAYER_RANK = {
53
+ shared: 0,
54
+ entities: 1,
55
+ features: 2,
56
+ widgets: 3,
57
+ views: 4,
58
+ app: 5
59
+ };
60
+ var ALIAS_PREFIXES = {
61
+ "@/": "",
62
+ "@shared/": "shared/",
63
+ "@entities/": "entities/",
64
+ "@features/": "features/",
65
+ "@widgets/": "widgets/",
66
+ "@views/": "views/",
67
+ "@app/": "app/"
68
+ };
69
+ var SERVER_PACKAGES = /* @__PURE__ */ new Set([
70
+ "server-only",
71
+ "next/headers",
72
+ "next/cache",
73
+ "next/server",
74
+ "fs",
75
+ "fs/promises",
76
+ "node:fs",
77
+ "node:fs/promises",
78
+ "child_process",
79
+ "node:child_process"
80
+ ]);
81
+ function normalizePath(value) {
82
+ return value.replace(/\\/g, "/");
83
+ }
84
+ function getSettings(context) {
85
+ const settings = context.settings?.["next-arch"];
86
+ return {
87
+ srcDir: settings?.srcDir ?? "src"
88
+ };
89
+ }
90
+ function getProjectRoot(filename, srcDir) {
91
+ const normalized = normalizePath(filename);
92
+ const marker = `/${srcDir}/`;
93
+ const index = normalized.lastIndexOf(marker);
94
+ if (index === -1) return null;
95
+ return normalized.slice(0, index);
96
+ }
97
+ function getSrcRelativePath(filename, srcDir) {
98
+ const normalized = normalizePath(filename);
99
+ const marker = `/${srcDir}/`;
100
+ const index = normalized.indexOf(marker);
101
+ if (index === -1) return null;
102
+ return normalized.slice(index + marker.length);
103
+ }
104
+ function getLayer(relativePath) {
105
+ const [first] = relativePath.split("/");
106
+ if (LAYERS.includes(first)) {
107
+ return first;
108
+ }
109
+ if (first === "components" || first === "lib") {
110
+ return "shared";
111
+ }
112
+ return null;
113
+ }
114
+ function getLayerRank(layer) {
115
+ if (!layer) return null;
116
+ return LAYER_RANK[layer];
117
+ }
118
+ function getFeatureName(relativePath) {
119
+ const match = relativePath.match(/^features\/([^/]+)/);
120
+ return match?.[1] ?? null;
121
+ }
122
+ function resolveImportSource(importSource, currentFile, srcDir) {
123
+ for (const [alias, prefix] of Object.entries(ALIAS_PREFIXES)) {
124
+ if (importSource === alias.slice(0, -1) || importSource.startsWith(alias)) {
125
+ const rest = importSource.slice(alias.length);
126
+ return normalizePath(`${prefix}${rest}`).replace(/\/$/, "");
127
+ }
128
+ }
129
+ if (!importSource.startsWith(".")) {
130
+ return null;
131
+ }
132
+ const projectRoot = getProjectRoot(currentFile, srcDir);
133
+ if (!projectRoot) return null;
134
+ const srcRoot = path.join(projectRoot, srcDir);
135
+ const resolved = normalizePath(path.resolve(path.dirname(currentFile), importSource));
136
+ const srcRootNormalized = normalizePath(srcRoot);
137
+ if (!resolved.startsWith(srcRootNormalized)) {
138
+ return null;
139
+ }
140
+ return resolved.slice(srcRootNormalized.length + 1);
141
+ }
142
+ function isServerPackage(importSource) {
143
+ return SERVER_PACKAGES.has(importSource);
144
+ }
145
+ function isServerPath(relativePath) {
146
+ return relativePath.includes("/server/") || /\.server(?:\.tsx?)?$/.test(relativePath);
147
+ }
148
+ function readModuleDirectives(absolutePath) {
149
+ const candidates = [
150
+ absolutePath,
151
+ `${absolutePath}.ts`,
152
+ `${absolutePath}.tsx`,
153
+ `${absolutePath}.js`,
154
+ `${absolutePath}.jsx`,
155
+ path.join(absolutePath, "index.ts"),
156
+ path.join(absolutePath, "index.tsx"),
157
+ path.join(absolutePath, "index.js"),
158
+ path.join(absolutePath, "index.jsx")
159
+ ];
160
+ for (const candidate of candidates) {
161
+ if (!fs.existsSync(candidate) || !fs.statSync(candidate).isFile()) {
162
+ continue;
163
+ }
164
+ const content = fs.readFileSync(candidate, "utf8");
165
+ return {
166
+ client: /['"]use client['"]/.test(content),
167
+ server: /['"]use server['"]/.test(content)
168
+ };
169
+ }
170
+ return { client: false, server: false };
171
+ }
172
+ function resolveAbsoluteImportPath(importSource, currentFile, srcDir) {
173
+ const relative = resolveImportSource(importSource, currentFile, srcDir);
174
+ if (!relative) return null;
175
+ const projectRoot = getProjectRoot(currentFile, srcDir);
176
+ if (!projectRoot) return null;
177
+ return path.join(projectRoot, srcDir, relative);
178
+ }
179
+ function isFeaturePublicImport(relativePath) {
180
+ const parts = relativePath.split("/");
181
+ if (parts[0] !== "features" || parts.length < 2) {
182
+ return false;
183
+ }
184
+ return parts.length === 2 || parts.length === 3 && parts[2] === "index";
185
+ }
186
+
187
+ // src/rules/no-cross-feature-imports.ts
188
+ var noCrossFeatureImports = {
189
+ meta: {
190
+ type: "problem",
191
+ docs: {
192
+ description: "Disallow direct imports between different features. Use shared/ or pass data through props."
193
+ },
194
+ messages: {
195
+ crossFeature: 'next-arch/no-cross-feature-imports: Cannot import from feature "{{imported}}" inside feature "{{current}}". Use shared/ or pass data through props.'
196
+ },
197
+ schema: []
198
+ },
199
+ create(context) {
200
+ const { srcDir } = getSettings(context);
201
+ const currentFile = getSrcRelativePath(context.filename, srcDir);
202
+ if (!currentFile) return {};
203
+ const currentFeature = getFeatureName(currentFile);
204
+ if (!currentFeature) return {};
205
+ return visitImportSources(context, (node, importSource) => {
206
+ const resolved = resolveImportSource(importSource, context.filename, srcDir);
207
+ if (!resolved) return;
208
+ const importedFeature = getFeatureName(resolved);
209
+ if (!importedFeature || importedFeature === currentFeature) return;
210
+ context.report({
211
+ node: node.source,
212
+ messageId: "crossFeature",
213
+ data: {
214
+ current: currentFeature,
215
+ imported: importedFeature
216
+ }
217
+ });
218
+ });
219
+ }
220
+ };
221
+
222
+ // src/rules/no-deep-imports.ts
223
+ var noDeepImports = {
224
+ meta: {
225
+ type: "problem",
226
+ docs: {
227
+ description: "Disallow deep imports into features. Import only through the feature public index."
228
+ },
229
+ messages: {
230
+ deepImport: 'next-arch/no-deep-imports: Import feature "{{feature}}" only through its public API (index.ts). Do not import from "{{importSource}}".'
231
+ },
232
+ schema: []
233
+ },
234
+ create(context) {
235
+ const { srcDir } = getSettings(context);
236
+ const currentFile = getSrcRelativePath(context.filename, srcDir);
237
+ if (!currentFile) return {};
238
+ const currentFeature = getFeatureName(currentFile);
239
+ return visitImportSources(context, (node, importSource) => {
240
+ const resolved = resolveImportSource(importSource, context.filename, srcDir);
241
+ if (!resolved?.startsWith("features/")) return;
242
+ const importedFeature = getFeatureName(resolved);
243
+ if (!importedFeature) return;
244
+ if (currentFeature === importedFeature) return;
245
+ if (isFeaturePublicImport(resolved)) return;
246
+ context.report({
247
+ node: node.source,
248
+ messageId: "deepImport",
249
+ data: {
250
+ feature: importedFeature,
251
+ importSource
252
+ }
253
+ });
254
+ });
255
+ }
256
+ };
257
+
258
+ // src/rules/no-server-in-client.ts
259
+ var noServerInClient = {
260
+ meta: {
261
+ type: "problem",
262
+ docs: {
263
+ description: "Disallow importing server-only code into files marked with 'use client'."
264
+ },
265
+ messages: {
266
+ serverPackage: "next-arch/no-server-in-client: Client components cannot import server package '{{importSource}}'.",
267
+ serverPath: "next-arch/no-server-in-client: Client components cannot import server path '{{importSource}}'.",
268
+ serverModule: "next-arch/no-server-in-client: Client components cannot import module with 'use server' ({{importSource}})."
269
+ },
270
+ schema: []
271
+ },
272
+ create(context) {
273
+ const { srcDir } = getSettings(context);
274
+ return visitImportSources(context, (node, importSource) => {
275
+ if (!hasUseClientDirective(context)) return;
276
+ if (isServerPackage(importSource)) {
277
+ context.report({
278
+ node: node.source,
279
+ messageId: "serverPackage",
280
+ data: { importSource }
281
+ });
282
+ return;
283
+ }
284
+ const resolved = resolveImportSource(importSource, context.filename, srcDir);
285
+ if (resolved && isServerPath(resolved)) {
286
+ context.report({
287
+ node: node.source,
288
+ messageId: "serverPath",
289
+ data: { importSource }
290
+ });
291
+ return;
292
+ }
293
+ const absolutePath = resolveAbsoluteImportPath(
294
+ importSource,
295
+ context.filename,
296
+ srcDir
297
+ );
298
+ if (!absolutePath) return;
299
+ const directives = readModuleDirectives(absolutePath);
300
+ if (directives.server && !directives.client) {
301
+ context.report({
302
+ node: node.source,
303
+ messageId: "serverModule",
304
+ data: { importSource }
305
+ });
306
+ }
307
+ });
308
+ }
309
+ };
310
+
311
+ // src/rules/no-upward-imports.ts
312
+ var noUpwardImports = {
313
+ meta: {
314
+ type: "problem",
315
+ docs: {
316
+ description: "Disallow imports from upper architectural layers into lower layers."
317
+ },
318
+ messages: {
319
+ upwardImport: 'next-arch/no-upward-imports: Layer "{{currentLayer}}" cannot import from upper layer "{{importedLayer}}" ({{importSource}}).'
320
+ },
321
+ schema: []
322
+ },
323
+ create(context) {
324
+ const { srcDir } = getSettings(context);
325
+ const currentFile = getSrcRelativePath(context.filename, srcDir);
326
+ if (!currentFile) return {};
327
+ const currentLayer = getLayer(currentFile);
328
+ const currentRank = getLayerRank(currentLayer);
329
+ if (currentLayer === null || currentRank === null) return {};
330
+ return visitImportSources(context, (node, importSource) => {
331
+ const resolved = resolveImportSource(importSource, context.filename, srcDir);
332
+ if (!resolved) return;
333
+ const importedLayer = getLayer(resolved);
334
+ const importedRank = getLayerRank(importedLayer);
335
+ if (importedLayer === null || importedRank === null) return;
336
+ if (importedRank > currentRank) {
337
+ context.report({
338
+ node: node.source,
339
+ messageId: "upwardImport",
340
+ data: {
341
+ currentLayer,
342
+ importedLayer,
343
+ importSource
344
+ }
345
+ });
346
+ }
347
+ });
348
+ }
349
+ };
350
+
351
+ // src/index.ts
352
+ var plugin = {
353
+ meta: {
354
+ name: "eslint-plugin-next-arch",
355
+ version: "0.1.0"
356
+ },
357
+ rules: {
358
+ "no-cross-feature-imports": noCrossFeatureImports,
359
+ "no-deep-imports": noDeepImports,
360
+ "no-server-in-client": noServerInClient,
361
+ "no-upward-imports": noUpwardImports
362
+ }
363
+ };
364
+ var index_default = plugin;
365
+ var configs = {
366
+ recommended: {
367
+ plugins: {
368
+ "next-arch": plugin
369
+ },
370
+ rules: {
371
+ "next-arch/no-cross-feature-imports": "error",
372
+ "next-arch/no-deep-imports": "error",
373
+ "next-arch/no-server-in-client": "error",
374
+ "next-arch/no-upward-imports": "error"
375
+ }
376
+ }
377
+ };
378
+ export {
379
+ configs,
380
+ index_default as default,
381
+ noCrossFeatureImports,
382
+ noDeepImports,
383
+ noServerInClient,
384
+ noUpwardImports
385
+ };
386
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/import-visitors.ts","../src/utils/layers.ts","../src/rules/no-cross-feature-imports.ts","../src/rules/no-deep-imports.ts","../src/rules/no-server-in-client.ts","../src/rules/no-upward-imports.ts","../src/index.ts"],"sourcesContent":["import type { Rule } from 'eslint';\n\ntype ImportNode = {\n source: { value: string };\n};\n\nexport function visitImportSources(\n context: Rule.RuleContext,\n onImport: (node: ImportNode, importSource: string) => void,\n): Rule.RuleListener {\n return {\n ImportDeclaration(node) {\n onImport(node as ImportNode, String((node as ImportNode).source.value));\n },\n ExportNamedDeclaration(node) {\n const exportNode = node as ImportNode & { source?: { value: string } | null };\n if (exportNode.source?.value) {\n onImport(exportNode, String(exportNode.source.value));\n }\n },\n ExportAllDeclaration(node) {\n const exportNode = node as ImportNode;\n onImport(exportNode, String(exportNode.source.value));\n },\n ImportExpression(node) {\n const expression = node as { source: { type: string; value?: string } };\n if (expression.source.type === 'Literal' && typeof expression.source.value === 'string') {\n onImport(expression as unknown as ImportNode, expression.source.value);\n }\n },\n CallExpression(node) {\n const call = node as {\n callee: { type: string; name?: string };\n arguments: Array<{ type: string; value?: string }>;\n };\n\n if (\n call.callee.type === 'Identifier' &&\n call.callee.name === 'require' &&\n call.arguments[0]?.type === 'Literal' &&\n typeof call.arguments[0].value === 'string'\n ) {\n onImport(call as unknown as ImportNode, call.arguments[0].value);\n }\n },\n };\n}\n\nexport function hasUseClientDirective(context: Rule.RuleContext): boolean {\n const program = context.getSourceCode().ast;\n const body = (program as { body?: Array<{ type: string; value?: string }> }).body ?? [];\n\n return body.some((statement) => {\n if (statement.type !== 'ExpressionStatement') return false;\n const expressionStatement = statement as {\n directive?: string;\n expression?: { type: string; value?: string };\n };\n return (\n expressionStatement.directive === 'use client' ||\n (expressionStatement.expression?.type === 'Literal' &&\n expressionStatement.expression.value === 'use client')\n );\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nexport const LAYERS = [\n 'shared',\n 'entities',\n 'features',\n 'widgets',\n 'views',\n 'app',\n] as const;\n\nexport type Layer = (typeof LAYERS)[number];\n\nconst LAYER_RANK: Record<Layer, number> = {\n shared: 0,\n entities: 1,\n features: 2,\n widgets: 3,\n views: 4,\n app: 5,\n};\n\nconst ALIAS_PREFIXES: Record<string, string> = {\n '@/': '',\n '@shared/': 'shared/',\n '@entities/': 'entities/',\n '@features/': 'features/',\n '@widgets/': 'widgets/',\n '@views/': 'views/',\n '@app/': 'app/',\n};\n\nconst SERVER_PACKAGES = new Set([\n 'server-only',\n 'next/headers',\n 'next/cache',\n 'next/server',\n 'fs',\n 'fs/promises',\n 'node:fs',\n 'node:fs/promises',\n 'child_process',\n 'node:child_process',\n]);\n\nexport interface PluginSettings {\n srcDir: string;\n}\n\nexport function normalizePath(value: string): string {\n return value.replace(/\\\\/g, '/');\n}\n\nexport function getSettings(context: { settings?: Record<string, unknown> }): PluginSettings {\n const settings = context.settings?.['next-arch'] as Partial<PluginSettings> | undefined;\n return {\n srcDir: settings?.srcDir ?? 'src',\n };\n}\n\nexport function getProjectRoot(filename: string, srcDir: string): string | null {\n const normalized = normalizePath(filename);\n const marker = `/${srcDir}/`;\n const index = normalized.lastIndexOf(marker);\n if (index === -1) return null;\n return normalized.slice(0, index);\n}\n\nexport function getSrcRelativePath(filename: string, srcDir: string): string | null {\n const normalized = normalizePath(filename);\n const marker = `/${srcDir}/`;\n const index = normalized.indexOf(marker);\n if (index === -1) return null;\n return normalized.slice(index + marker.length);\n}\n\nexport function getLayer(relativePath: string): Layer | null {\n const [first] = relativePath.split('/');\n if ((LAYERS as readonly string[]).includes(first)) {\n return first as Layer;\n }\n if (first === 'components' || first === 'lib') {\n return 'shared';\n }\n return null;\n}\n\nexport function getLayerRank(layer: Layer | null): number | null {\n if (!layer) return null;\n return LAYER_RANK[layer];\n}\n\nexport function getFeatureName(relativePath: string): string | null {\n const match = relativePath.match(/^features\\/([^/]+)/);\n return match?.[1] ?? null;\n}\n\nexport function resolveImportSource(\n importSource: string,\n currentFile: string,\n srcDir: string,\n): string | null {\n for (const [alias, prefix] of Object.entries(ALIAS_PREFIXES)) {\n if (importSource === alias.slice(0, -1) || importSource.startsWith(alias)) {\n const rest = importSource.slice(alias.length);\n return normalizePath(`${prefix}${rest}`).replace(/\\/$/, '');\n }\n }\n\n if (!importSource.startsWith('.')) {\n return null;\n }\n\n const projectRoot = getProjectRoot(currentFile, srcDir);\n if (!projectRoot) return null;\n\n const srcRoot = path.join(projectRoot, srcDir);\n const resolved = normalizePath(path.resolve(path.dirname(currentFile), importSource));\n const srcRootNormalized = normalizePath(srcRoot);\n\n if (!resolved.startsWith(srcRootNormalized)) {\n return null;\n }\n\n return resolved.slice(srcRootNormalized.length + 1);\n}\n\nexport function isServerPackage(importSource: string): boolean {\n return SERVER_PACKAGES.has(importSource);\n}\n\nexport function isServerPath(relativePath: string): boolean {\n return (\n relativePath.includes('/server/') ||\n /\\.server(?:\\.tsx?)?$/.test(relativePath)\n );\n}\n\nexport function readModuleDirectives(\n absolutePath: string,\n): { client: boolean; server: boolean } {\n const candidates = [\n absolutePath,\n `${absolutePath}.ts`,\n `${absolutePath}.tsx`,\n `${absolutePath}.js`,\n `${absolutePath}.jsx`,\n path.join(absolutePath, 'index.ts'),\n path.join(absolutePath, 'index.tsx'),\n path.join(absolutePath, 'index.js'),\n path.join(absolutePath, 'index.jsx'),\n ];\n\n for (const candidate of candidates) {\n if (!fs.existsSync(candidate) || !fs.statSync(candidate).isFile()) {\n continue;\n }\n\n const content = fs.readFileSync(candidate, 'utf8');\n return {\n client: /['\"]use client['\"]/.test(content),\n server: /['\"]use server['\"]/.test(content),\n };\n }\n\n return { client: false, server: false };\n}\n\nexport function resolveAbsoluteImportPath(\n importSource: string,\n currentFile: string,\n srcDir: string,\n): string | null {\n const relative = resolveImportSource(importSource, currentFile, srcDir);\n if (!relative) return null;\n\n const projectRoot = getProjectRoot(currentFile, srcDir);\n if (!projectRoot) return null;\n\n return path.join(projectRoot, srcDir, relative);\n}\n\nexport function isFeaturePublicImport(relativePath: string): boolean {\n const parts = relativePath.split('/');\n if (parts[0] !== 'features' || parts.length < 2) {\n return false;\n }\n\n return parts.length === 2 || (parts.length === 3 && parts[2] === 'index');\n}\n","import type { Rule } from 'eslint';\nimport { visitImportSources } from '../utils/import-visitors.js';\nimport {\n getFeatureName,\n getSettings,\n getSrcRelativePath,\n resolveImportSource,\n} from '../utils/layers.js';\n\nexport const noCrossFeatureImports: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow direct imports between different features. Use shared/ or pass data through props.',\n },\n messages: {\n crossFeature:\n 'next-arch/no-cross-feature-imports: Cannot import from feature \"{{imported}}\" inside feature \"{{current}}\". Use shared/ or pass data through props.',\n },\n schema: [],\n },\n create(context) {\n const { srcDir } = getSettings(context);\n const currentFile = getSrcRelativePath(context.filename, srcDir);\n if (!currentFile) return {};\n\n const currentFeature = getFeatureName(currentFile);\n if (!currentFeature) return {};\n\n return visitImportSources(context, (node, importSource) => {\n const resolved = resolveImportSource(importSource, context.filename, srcDir);\n if (!resolved) return;\n\n const importedFeature = getFeatureName(resolved);\n if (!importedFeature || importedFeature === currentFeature) return;\n\n context.report({\n node: node.source as Rule.Node,\n messageId: 'crossFeature',\n data: {\n current: currentFeature,\n imported: importedFeature,\n },\n });\n });\n },\n};\n","import type { Rule } from 'eslint';\nimport { visitImportSources } from '../utils/import-visitors.js';\nimport {\n getFeatureName,\n getSettings,\n getSrcRelativePath,\n isFeaturePublicImport,\n resolveImportSource,\n} from '../utils/layers.js';\n\nexport const noDeepImports: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow deep imports into features. Import only through the feature public index.',\n },\n messages: {\n deepImport:\n 'next-arch/no-deep-imports: Import feature \"{{feature}}\" only through its public API (index.ts). Do not import from \"{{importSource}}\".',\n },\n schema: [],\n },\n create(context) {\n const { srcDir } = getSettings(context);\n const currentFile = getSrcRelativePath(context.filename, srcDir);\n if (!currentFile) return {};\n\n const currentFeature = getFeatureName(currentFile);\n\n return visitImportSources(context, (node, importSource) => {\n const resolved = resolveImportSource(importSource, context.filename, srcDir);\n if (!resolved?.startsWith('features/')) return;\n\n const importedFeature = getFeatureName(resolved);\n if (!importedFeature) return;\n\n if (currentFeature === importedFeature) return;\n if (isFeaturePublicImport(resolved)) return;\n\n context.report({\n node: node.source as Rule.Node,\n messageId: 'deepImport',\n data: {\n feature: importedFeature,\n importSource,\n },\n });\n });\n },\n};\n","import type { Rule } from 'eslint';\nimport { hasUseClientDirective, visitImportSources } from '../utils/import-visitors.js';\nimport {\n getSettings,\n isServerPackage,\n isServerPath,\n readModuleDirectives,\n resolveAbsoluteImportPath,\n resolveImportSource,\n} from '../utils/layers.js';\n\nexport const noServerInClient: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n \"Disallow importing server-only code into files marked with 'use client'.\",\n },\n messages: {\n serverPackage:\n \"next-arch/no-server-in-client: Client components cannot import server package '{{importSource}}'.\",\n serverPath:\n \"next-arch/no-server-in-client: Client components cannot import server path '{{importSource}}'.\",\n serverModule:\n \"next-arch/no-server-in-client: Client components cannot import module with 'use server' ({{importSource}}).\",\n },\n schema: [],\n },\n create(context) {\n const { srcDir } = getSettings(context);\n return visitImportSources(context, (node, importSource) => {\n if (!hasUseClientDirective(context)) return;\n\n if (isServerPackage(importSource)) {\n context.report({\n node: node.source as Rule.Node,\n messageId: 'serverPackage',\n data: { importSource },\n });\n return;\n }\n\n const resolved = resolveImportSource(importSource, context.filename, srcDir);\n if (resolved && isServerPath(resolved)) {\n context.report({\n node: node.source as Rule.Node,\n messageId: 'serverPath',\n data: { importSource },\n });\n return;\n }\n\n const absolutePath = resolveAbsoluteImportPath(\n importSource,\n context.filename,\n srcDir,\n );\n if (!absolutePath) return;\n\n const directives = readModuleDirectives(absolutePath);\n if (directives.server && !directives.client) {\n context.report({\n node: node.source as Rule.Node,\n messageId: 'serverModule',\n data: { importSource },\n });\n }\n });\n },\n};\n","import type { Rule } from 'eslint';\nimport { visitImportSources } from '../utils/import-visitors.js';\nimport {\n getLayer,\n getLayerRank,\n getSettings,\n getSrcRelativePath,\n resolveImportSource,\n} from '../utils/layers.js';\n\nexport const noUpwardImports: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow imports from upper architectural layers into lower layers.',\n },\n messages: {\n upwardImport:\n 'next-arch/no-upward-imports: Layer \"{{currentLayer}}\" cannot import from upper layer \"{{importedLayer}}\" ({{importSource}}).',\n },\n schema: [],\n },\n create(context) {\n const { srcDir } = getSettings(context);\n const currentFile = getSrcRelativePath(context.filename, srcDir);\n if (!currentFile) return {};\n\n const currentLayer = getLayer(currentFile);\n const currentRank = getLayerRank(currentLayer);\n if (currentLayer === null || currentRank === null) return {};\n\n return visitImportSources(context, (node, importSource) => {\n const resolved = resolveImportSource(importSource, context.filename, srcDir);\n if (!resolved) return;\n\n const importedLayer = getLayer(resolved);\n const importedRank = getLayerRank(importedLayer);\n if (importedLayer === null || importedRank === null) return;\n\n if (importedRank > currentRank) {\n context.report({\n node: node.source as Rule.Node,\n messageId: 'upwardImport',\n data: {\n currentLayer,\n importedLayer,\n importSource,\n },\n });\n }\n });\n },\n};\n","import type { ESLint, Linter } from 'eslint';\nimport { noCrossFeatureImports } from './rules/no-cross-feature-imports.js';\nimport { noDeepImports } from './rules/no-deep-imports.js';\nimport { noServerInClient } from './rules/no-server-in-client.js';\nimport { noUpwardImports } from './rules/no-upward-imports.js';\n\nconst plugin = {\n meta: {\n name: 'eslint-plugin-next-arch',\n version: '0.1.0',\n },\n rules: {\n 'no-cross-feature-imports': noCrossFeatureImports,\n 'no-deep-imports': noDeepImports,\n 'no-server-in-client': noServerInClient,\n 'no-upward-imports': noUpwardImports,\n },\n} satisfies ESLint.Plugin;\n\nexport default plugin;\n\nexport const configs = {\n recommended: {\n plugins: {\n 'next-arch': plugin,\n },\n rules: {\n 'next-arch/no-cross-feature-imports': 'error',\n 'next-arch/no-deep-imports': 'error',\n 'next-arch/no-server-in-client': 'error',\n 'next-arch/no-upward-imports': 'error',\n },\n } satisfies Linter.Config,\n};\n\nexport {\n noCrossFeatureImports,\n noDeepImports,\n noServerInClient,\n noUpwardImports,\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "eslint-plugin-next-arch",
3
+ "version": "0.1.0",
4
+ "description": "ESLint rules for Next Architecture (Feature-Sliced Design)",
5
+ "author": "yousxlfs",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/yousxlfs/next-archi#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/yousxlfs/next-archi.git",
11
+ "directory": "packages/eslint-plugin-next-arch"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/yousxlfs/next-archi/issues"
15
+ },
16
+ "keywords": [
17
+ "eslint",
18
+ "eslintplugin",
19
+ "nextjs",
20
+ "feature-sliced-design",
21
+ "fsd",
22
+ "architecture"
23
+ ],
24
+ "type": "module",
25
+ "main": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js"
31
+ }
32
+ },
33
+ "files": [
34
+ "dist"
35
+ ],
36
+ "scripts": {
37
+ "build": "tsup",
38
+ "dev": "tsup --watch",
39
+ "test": "node --test tests/rules.test.js",
40
+ "prepublishOnly": "pnpm build"
41
+ },
42
+ "peerDependencies": {
43
+ "eslint": "^9.0.0"
44
+ },
45
+ "devDependencies": {
46
+ "@types/eslint": "^9.6.1",
47
+ "@types/node": "^20",
48
+ "eslint": "^9.39.4",
49
+ "tsup": "^8.4.0",
50
+ "typescript": "^5.8.0"
51
+ },
52
+ "engines": {
53
+ "node": ">=20"
54
+ }
55
+ }