@conarti/eslint-plugin-feature-sliced 1.0.5 → 2.0.0-rc.2

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 (78) hide show
  1. package/README.md +43 -38
  2. package/dist/index.cjs +1004 -0
  3. package/dist/index.d.cts +61 -0
  4. package/dist/index.d.ts +61 -0
  5. package/dist/index.js +978 -21
  6. package/package.json +39 -40
  7. package/dist/config.js +0 -27
  8. package/dist/configs/import-order/index.js +0 -20
  9. package/dist/configs/import-order/recommended.js +0 -28
  10. package/dist/configs/import-order/with-newlines-and-type-group.js +0 -28
  11. package/dist/configs/import-order/with-newlines.js +0 -28
  12. package/dist/configs/import-order/with-type-group.js +0 -28
  13. package/dist/configs/recommended.js +0 -11
  14. package/dist/configs/rules.js +0 -11
  15. package/dist/lib/feature-sliced/extract-feature-sliced-parts.js +0 -18
  16. package/dist/lib/feature-sliced/extract-layer.js +0 -21
  17. package/dist/lib/feature-sliced/extract-paths-info.js +0 -59
  18. package/dist/lib/feature-sliced/extract-segment.js +0 -18
  19. package/dist/lib/feature-sliced/extract-slice.js +0 -10
  20. package/dist/lib/feature-sliced/index.js +0 -10
  21. package/dist/lib/feature-sliced/layers.js +0 -16
  22. package/dist/lib/feature-sliced/validate-extracted-feature-sliced-parts.js +0 -29
  23. package/dist/lib/path/convert-to-absolute.js +0 -18
  24. package/dist/lib/path/index.js +0 -9
  25. package/dist/lib/path/is-path-relative.js +0 -7
  26. package/dist/lib/path/join-path.js +0 -12
  27. package/dist/lib/path/normalize-path.js +0 -19
  28. package/dist/lib/rule/create-rule.js +0 -7
  29. package/dist/lib/rule/extract-current-file-path.js +0 -11
  30. package/dist/lib/rule/extract-cwd.js +0 -13
  31. package/dist/lib/rule/extract-node-path.js +0 -13
  32. package/dist/lib/rule/extract-paths.js +0 -21
  33. package/dist/lib/rule/extract-rule-options.js +0 -7
  34. package/dist/lib/rule/get-source-range-without-quotes.js +0 -7
  35. package/dist/lib/rule/has-path.js +0 -11
  36. package/dist/lib/rule/index.js +0 -27
  37. package/dist/lib/rule/is-ignored-current-file.js +0 -12
  38. package/dist/lib/rule/is-ignored-target.js +0 -12
  39. package/dist/lib/rule/is-ignored.js +0 -12
  40. package/dist/lib/rule/is-node-type.js +0 -16
  41. package/dist/lib/rule/models.js +0 -2
  42. package/dist/lib/shared/get-by-reg-exp.js +0 -12
  43. package/dist/lib/shared/index.js +0 -11
  44. package/dist/lib/shared/is-null.js +0 -7
  45. package/dist/lib/shared/is-object.js +0 -7
  46. package/dist/lib/shared/is-undefined.js +0 -7
  47. package/dist/rules/absolute-relative/config.js +0 -2
  48. package/dist/rules/absolute-relative/index.js +0 -51
  49. package/dist/rules/absolute-relative/model/errors.js +0 -17
  50. package/dist/rules/absolute-relative/model/index.js +0 -5
  51. package/dist/rules/absolute-relative/model/should-be-absolute.js +0 -16
  52. package/dist/rules/absolute-relative/model/should-be-relative.js +0 -21
  53. package/dist/rules/absolute-relative/model/validate-and-report.js +0 -24
  54. package/dist/rules/layers-slices/config.js +0 -2
  55. package/dist/rules/layers-slices/index.js +0 -55
  56. package/dist/rules/layers-slices/model/errors.js +0 -22
  57. package/dist/rules/layers-slices/model/index.js +0 -5
  58. package/dist/rules/layers-slices/model/is-not-suitable-for-validation.js +0 -17
  59. package/dist/rules/layers-slices/model/specifiers/extract-import-specifiers.js +0 -8
  60. package/dist/rules/layers-slices/model/specifiers/has-errors-at-all-specifiers.js +0 -9
  61. package/dist/rules/layers-slices/model/specifiers/index.js +0 -7
  62. package/dist/rules/layers-slices/model/specifiers/validate-specifiers.js +0 -8
  63. package/dist/rules/layers-slices/model/validate-and-report.js +0 -46
  64. package/dist/rules/layers-slices/model/validate-node/index.js +0 -16
  65. package/dist/rules/layers-slices/model/validate-node/valid-by-layer-order.js +0 -10
  66. package/dist/rules/layers-slices/model/validate-node/valid-by-type-import.js +0 -9
  67. package/dist/rules/public-api/config.js +0 -2
  68. package/dist/rules/public-api/index.js +0 -65
  69. package/dist/rules/public-api/model/convert-to-public-api.js +0 -24
  70. package/dist/rules/public-api/model/errors.js +0 -34
  71. package/dist/rules/public-api/model/index.js +0 -5
  72. package/dist/rules/public-api/model/is-index-file.js +0 -7
  73. package/dist/rules/public-api/model/is-layer-public-api.js +0 -23
  74. package/dist/rules/public-api/model/is-segments-public-api.js +0 -11
  75. package/dist/rules/public-api/model/is-slice-public-api.js +0 -7
  76. package/dist/rules/public-api/model/should-be-from-public-api.js +0 -21
  77. package/dist/rules/public-api/model/validate-and-report-program.js +0 -15
  78. package/dist/rules/public-api/model/validate-and-report.js +0 -18
package/dist/index.cjs ADDED
@@ -0,0 +1,1004 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ default: () => src_default
34
+ });
35
+ module.exports = __toCommonJS(src_exports);
36
+
37
+ // src/config.ts
38
+ var PLUGIN_NAME = "@conarti/feature-sliced";
39
+ var RULE_NAMES = {
40
+ LAYERS_SLICES: `${PLUGIN_NAME}/layers-slices`,
41
+ ABSOLUTE_RELATIVE: `${PLUGIN_NAME}/absolute-relative`,
42
+ PUBLIC_API: `${PLUGIN_NAME}/public-api`,
43
+ IMPORT_ORDER: `${PLUGIN_NAME}/import-order`
44
+ };
45
+ var layers = [
46
+ "shared",
47
+ "entities",
48
+ "features",
49
+ "widgets",
50
+ "pages",
51
+ "processes",
52
+ "app"
53
+ ];
54
+ var layersWithoutSlices = [
55
+ "shared",
56
+ "app"
57
+ ];
58
+ var layersWithSlices = layers.filter((layer) => !layersWithoutSlices.includes(layer));
59
+ var segments = [
60
+ "ui",
61
+ "model",
62
+ "lib",
63
+ "api",
64
+ "config",
65
+ "assets"
66
+ ];
67
+ var pathSeparator = "/";
68
+
69
+ // package.json
70
+ var version = "2.0.0-rc.1";
71
+
72
+ // src/rules/index.ts
73
+ var import_eslint_plugin_import_x = __toESM(require("eslint-plugin-import-x"), 1);
74
+
75
+ // src/lib/rule/create-rule.ts
76
+ var import_eslint_utils = require("@typescript-eslint/utils/eslint-utils");
77
+ var blobUrl = "https://github.com/conarti/eslint-plugin-feature-sliced/blob/master/src/rules";
78
+ var createEslintRule = (0, import_eslint_utils.RuleCreator)(
79
+ (ruleName) => `${blobUrl}/${ruleName}/index.test.ts`
80
+ );
81
+
82
+ // src/lib/path/is-path-relative.ts
83
+ function isPathRelative(path3) {
84
+ return path3.startsWith(".");
85
+ }
86
+
87
+ // src/lib/path/join-path.ts
88
+ var import_node_path2 = __toESM(require("path"), 1);
89
+
90
+ // src/lib/path/normalize-path.ts
91
+ var import_node_path = __toESM(require("path"), 1);
92
+ function normalizePath(targetPath) {
93
+ const winSepRegExp = /\\/g;
94
+ const withNormalizedSeparators = import_node_path.default.normalize(targetPath).replace(winSepRegExp, pathSeparator);
95
+ if (targetPath.startsWith("./")) {
96
+ return `./${withNormalizedSeparators}`;
97
+ }
98
+ return withNormalizedSeparators;
99
+ }
100
+
101
+ // src/lib/path/join-path.ts
102
+ function joinPath(from, to) {
103
+ return normalizePath(import_node_path2.default.join(normalizePath(from), normalizePath(to)));
104
+ }
105
+
106
+ // src/lib/path/convert-to-absolute.ts
107
+ function convertToAbsolute(base, target) {
108
+ if (target === "") {
109
+ return base;
110
+ }
111
+ if (base === "") {
112
+ return target;
113
+ }
114
+ if (!isPathRelative(target)) {
115
+ return target;
116
+ }
117
+ return joinPath(base, `../${target}`);
118
+ }
119
+
120
+ // src/lib/rule/extract-current-file-path.ts
121
+ function extractCurrentFilePath(context) {
122
+ const currentFilePath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename();
123
+ return normalizePath(currentFilePath);
124
+ }
125
+
126
+ // src/lib/shared/get-by-reg-exp.ts
127
+ function getByRegExp(target, regExp, fromEnd = false) {
128
+ const results = target.match(regExp) || [];
129
+ if (fromEnd) {
130
+ const lastResult = results[results.length - 1];
131
+ return lastResult || null;
132
+ }
133
+ return results[0] || null;
134
+ }
135
+
136
+ // src/lib/shared/is-null.ts
137
+ function isNull(target) {
138
+ return target === null;
139
+ }
140
+
141
+ // src/lib/shared/is-object.ts
142
+ function isObject(target) {
143
+ return (typeof target === "object" || typeof target === "function") && target !== null;
144
+ }
145
+
146
+ // src/lib/shared/is-undefined.ts
147
+ function isUndefined(target) {
148
+ return target === void 0;
149
+ }
150
+
151
+ // src/lib/rule/extract-cwd.ts
152
+ function extractCwd(context) {
153
+ const cwd = context.getCwd?.();
154
+ if (isUndefined(cwd)) {
155
+ return void 0;
156
+ }
157
+ return normalizePath(cwd);
158
+ }
159
+
160
+ // src/lib/rule/extract-node-path.ts
161
+ function extractNodePath(node) {
162
+ const targetPath = node.source.value;
163
+ const normalizedTargetPath = normalizePath(targetPath);
164
+ return {
165
+ targetPath,
166
+ normalizedTargetPath
167
+ };
168
+ }
169
+
170
+ // src/lib/rule/extract-paths.ts
171
+ function extractPaths(node, context) {
172
+ const normalizedCurrentFilePath = extractCurrentFilePath(context);
173
+ const {
174
+ targetPath,
175
+ normalizedTargetPath
176
+ } = extractNodePath(node);
177
+ const absoluteTargetPath = convertToAbsolute(normalizedCurrentFilePath, normalizedTargetPath);
178
+ const cwd = extractCwd(context);
179
+ return {
180
+ targetPath,
181
+ normalizedTargetPath,
182
+ normalizedCurrentFilePath,
183
+ absoluteTargetPath,
184
+ normalizedCwd: cwd
185
+ };
186
+ }
187
+
188
+ // src/lib/rule/extract-rule-options.ts
189
+ function extractRuleOptions(optionsWithDefault) {
190
+ return optionsWithDefault[0];
191
+ }
192
+
193
+ // src/lib/rule/get-source-range-without-quotes.ts
194
+ function getSourceRangeWithoutQuotes([rangeStart, rangeEnd]) {
195
+ return [rangeStart + 1, rangeEnd - 1];
196
+ }
197
+
198
+ // src/lib/rule/has-path.ts
199
+ function hasPath(node) {
200
+ if (isObject(node) && "source" in node) {
201
+ return node.source !== null;
202
+ }
203
+ return false;
204
+ }
205
+
206
+ // src/lib/rule/is-ignored.ts
207
+ var import_picomatch = __toESM(require("picomatch"), 1);
208
+ function isIgnored(path3, patterns) {
209
+ const match = (0, import_picomatch.default)(patterns);
210
+ return match(path3);
211
+ }
212
+
213
+ // src/lib/rule/is-ignored-current-file.ts
214
+ function isIgnoredCurrentFile(context, optionsWithDefault) {
215
+ const ruleOptions = extractRuleOptions(optionsWithDefault);
216
+ const normalizedCurrentFilePath = extractCurrentFilePath(context);
217
+ return isIgnored(normalizedCurrentFilePath, ruleOptions.ignoreInFilesPatterns);
218
+ }
219
+
220
+ // src/lib/rule/is-ignored-target.ts
221
+ function isIgnoredTarget(node, optionsWithDefault) {
222
+ const { targetPath } = extractNodePath(node);
223
+ const userDefinedRuleOptions = extractRuleOptions(optionsWithDefault);
224
+ return isIgnored(targetPath, userDefinedRuleOptions.ignorePatterns);
225
+ }
226
+
227
+ // src/lib/rule/is-node-type.ts
228
+ var import_utils = require("@typescript-eslint/utils");
229
+ function isNodeType(node) {
230
+ const isImport = import_utils.ASTUtils.isNodeOfTypes([import_utils.AST_NODE_TYPES.ImportSpecifier, import_utils.AST_NODE_TYPES.ImportDeclaration])(node);
231
+ const isExport = import_utils.ASTUtils.isNodeOfTypes([import_utils.AST_NODE_TYPES.ExportAllDeclaration, import_utils.AST_NODE_TYPES.ExportNamedDeclaration])(node);
232
+ if (isImport) {
233
+ return node.importKind === "type";
234
+ }
235
+ if (isExport) {
236
+ return node.exportKind === "type";
237
+ }
238
+ return false;
239
+ }
240
+
241
+ // src/lib/feature-sliced/extract-layer.ts
242
+ function prepareToExtract(targetPath, cwd) {
243
+ const lowerCasedTargetPath = targetPath.toLowerCase();
244
+ if (cwd === void 0) {
245
+ return lowerCasedTargetPath;
246
+ }
247
+ const lowerCasedCwd = cwd.toLowerCase();
248
+ const pathWithoutCwd = lowerCasedTargetPath.replace(lowerCasedCwd, "");
249
+ return pathWithoutCwd;
250
+ }
251
+ function extractLayer(targetPath, cwd) {
252
+ const layersRegExpPattern = `(${layers.join("|")})(?![\\w\\.-])`;
253
+ const layersRegExp = new RegExp(layersRegExpPattern, "gi");
254
+ const pathForExtract = prepareToExtract(targetPath, cwd);
255
+ return getByRegExp(pathForExtract, layersRegExp);
256
+ }
257
+
258
+ // src/lib/feature-sliced/extract-segment.ts
259
+ var layersUnion = layersWithSlices.join("|");
260
+ var segmentsUnion = segments.join("|");
261
+ var fsdPartsRegExp = new RegExp(
262
+ `(?<=(?<layer>${layersUnion}))\\/(?<slice>([\\w-]*\\/)+?)(?<segment>(${segmentsUnion})(\\.\\w+)?)(\\/(?<segmentFiles>.*))?`
263
+ );
264
+ function extractSegment(targetPath) {
265
+ const fsdParts = targetPath.match(fsdPartsRegExp);
266
+ if (fsdParts === null) {
267
+ return [null, null];
268
+ }
269
+ const {
270
+ segment = null,
271
+ segmentFiles = null
272
+ } = fsdParts.groups || {};
273
+ const fileExtensionRegExp = /\.[^/.]+$/;
274
+ const segmentWithoutFileExtension = segment?.replace(fileExtensionRegExp, "") || null;
275
+ return [segmentWithoutFileExtension, segmentFiles];
276
+ }
277
+
278
+ // src/lib/feature-sliced/extract-slice.ts
279
+ function extractSlice(targetPath) {
280
+ const targetPathWithoutCurrentFileName = targetPath.replace(/\/\w+\.\w+$/, "");
281
+ return getByRegExp(targetPathWithoutCurrentFileName, new RegExp(`(?<=(${layersWithSlices.join("|")})\\/)(\\w|-)+`, "gi"));
282
+ }
283
+
284
+ // src/lib/feature-sliced/extract-feature-sliced-parts.ts
285
+ function extractFeatureSlicedParts(targetPath, cwd) {
286
+ const layer = extractLayer(targetPath, cwd);
287
+ const slice = extractSlice(targetPath);
288
+ const [segment, segmentFiles] = extractSegment(targetPath);
289
+ return {
290
+ layer,
291
+ slice,
292
+ segment,
293
+ segmentFiles
294
+ };
295
+ }
296
+
297
+ // src/lib/feature-sliced/layers.ts
298
+ function isLayer(layer) {
299
+ return typeof layer === "string" && layers.includes(layer);
300
+ }
301
+ function getLayerWeight(layer) {
302
+ return layers.indexOf(layer);
303
+ }
304
+ function canLayerContainSlices(layer) {
305
+ return !layersWithoutSlices.includes(layer);
306
+ }
307
+
308
+ // src/lib/feature-sliced/validate-extracted-feature-sliced-parts.ts
309
+ function validateExtractedFeatureSlicedParts(extractedFeatureSlicedParts) {
310
+ const {
311
+ layer,
312
+ slice,
313
+ segment,
314
+ segmentFiles
315
+ } = extractedFeatureSlicedParts;
316
+ const hasLayer = isLayer(layer);
317
+ const hasNotLayer = !hasLayer;
318
+ const hasSlice = !isNull(slice);
319
+ const hasNotSlice = !hasSlice;
320
+ const hasSegment = !isNull(segment);
321
+ const hasNotSegment = !hasSegment;
322
+ const hasSegmentFiles = !isNull(segmentFiles);
323
+ const hasNotSegmentFiles = !hasSegmentFiles;
324
+ const canContainSlices = hasLayer && canLayerContainSlices(layer);
325
+ return {
326
+ hasLayer,
327
+ hasNotLayer,
328
+ hasSlice,
329
+ hasNotSlice,
330
+ hasSegment,
331
+ hasNotSegment,
332
+ hasSegmentFiles,
333
+ hasNotSegmentFiles,
334
+ canLayerContainSlices: canContainSlices
335
+ };
336
+ }
337
+
338
+ // src/lib/feature-sliced/extract-paths-info.ts
339
+ function compareFeatureSlicedParts(fsPartsToCompare) {
340
+ const {
341
+ target,
342
+ currentFile
343
+ } = fsPartsToCompare;
344
+ const hasUnknownLayers = target.validatedFeatureSlicedParts.hasNotLayer || currentFile.validatedFeatureSlicedParts.hasNotLayer;
345
+ const isSameLayer = target.validatedFeatureSlicedParts.hasLayer && currentFile.validatedFeatureSlicedParts.hasLayer && target.fsdParts.layer === currentFile.fsdParts.layer;
346
+ const isSameSlice = target.validatedFeatureSlicedParts.hasSlice && currentFile.validatedFeatureSlicedParts.hasSlice && target.fsdParts.slice === currentFile.fsdParts.slice;
347
+ const isSameSegment = target.fsdParts.segment === currentFile.fsdParts.segment;
348
+ const isSameLayerWithoutSlices = isSameLayer && !target.validatedFeatureSlicedParts.canLayerContainSlices && !currentFile.validatedFeatureSlicedParts.canLayerContainSlices;
349
+ return {
350
+ hasUnknownLayers,
351
+ isSameLayer,
352
+ isSameSlice,
353
+ isSameSegment,
354
+ isSameLayerWithoutSlices
355
+ };
356
+ }
357
+ function extractPathsInfo(node, context) {
358
+ const {
359
+ targetPath,
360
+ normalizedTargetPath,
361
+ normalizedCurrentFilePath,
362
+ absoluteTargetPath,
363
+ normalizedCwd
364
+ } = extractPaths(node, context);
365
+ const fsdPartsOfTarget = extractFeatureSlicedParts(absoluteTargetPath, normalizedCwd);
366
+ const fsdPartsOfCurrentFile = extractFeatureSlicedParts(normalizedCurrentFilePath, normalizedCwd);
367
+ const validatedFeatureSlicedPartsOfTarget = validateExtractedFeatureSlicedParts(fsdPartsOfTarget);
368
+ const validatedFeatureSlicedPartsOfCurrentFile = validateExtractedFeatureSlicedParts(fsdPartsOfCurrentFile);
369
+ const {
370
+ hasUnknownLayers,
371
+ isSameLayer,
372
+ isSameSlice,
373
+ isSameSegment,
374
+ isSameLayerWithoutSlices
375
+ } = compareFeatureSlicedParts({
376
+ target: {
377
+ validatedFeatureSlicedParts: validatedFeatureSlicedPartsOfTarget,
378
+ fsdParts: fsdPartsOfTarget
379
+ },
380
+ currentFile: {
381
+ validatedFeatureSlicedParts: validatedFeatureSlicedPartsOfCurrentFile,
382
+ fsdParts: fsdPartsOfCurrentFile
383
+ }
384
+ });
385
+ return {
386
+ targetPath,
387
+ normalizedTargetPath,
388
+ normalizedCurrentFilePath,
389
+ absoluteTargetPath,
390
+ fsdPartsOfTarget,
391
+ fsdPartsOfCurrentFile,
392
+ isSameLayer,
393
+ isSameSlice,
394
+ isSameSegment,
395
+ isSameLayerWithoutSlices,
396
+ hasUnknownLayers,
397
+ validatedFeatureSlicedPartsOfTarget,
398
+ validatedFeatureSlicedPartsOfCurrentFile
399
+ };
400
+ }
401
+
402
+ // src/rules/absolute-relative/model/errors.ts
403
+ function reportShouldBeRelative(node, context) {
404
+ context.report({
405
+ node: node.source,
406
+ messageId: "must-be-relative-path" /* MUST_BE_RELATIVE_PATH */
407
+ });
408
+ }
409
+ function reportShouldBeAbsolute(node, context) {
410
+ context.report({
411
+ node: node.source,
412
+ messageId: "must-be-absolute-path" /* MUST_BE_ABSOLUTE_PATH */
413
+ });
414
+ }
415
+
416
+ // src/rules/absolute-relative/model/should-be-absolute.ts
417
+ function shouldBeAbsolute(pathsInfo) {
418
+ const {
419
+ normalizedTargetPath,
420
+ fsdPartsOfTarget,
421
+ fsdPartsOfCurrentFile,
422
+ hasUnknownLayers
423
+ } = pathsInfo;
424
+ const isAbsolute = !isPathRelative(normalizedTargetPath);
425
+ if (isAbsolute) {
426
+ return false;
427
+ }
428
+ if (hasUnknownLayers) {
429
+ return false;
430
+ }
431
+ return fsdPartsOfCurrentFile.layer !== fsdPartsOfTarget.layer;
432
+ }
433
+
434
+ // src/rules/absolute-relative/model/should-be-relative.ts
435
+ function shouldBeRelative(pathsInfo) {
436
+ const {
437
+ validatedFeatureSlicedPartsOfCurrentFile,
438
+ normalizedTargetPath,
439
+ isSameLayerWithoutSlices,
440
+ isSameLayer,
441
+ isSameSlice
442
+ } = pathsInfo;
443
+ const isRelative = isPathRelative(normalizedTargetPath);
444
+ if (isRelative) {
445
+ return false;
446
+ }
447
+ const isImportToLayerPublicApi = validatedFeatureSlicedPartsOfCurrentFile.hasNotSlice && isSameLayer;
448
+ if (isImportToLayerPublicApi) {
449
+ return true;
450
+ }
451
+ if (isSameLayerWithoutSlices) {
452
+ return true;
453
+ }
454
+ const isSameLayerAndSlice = isSameLayer && isSameSlice;
455
+ return isSameLayerAndSlice;
456
+ }
457
+
458
+ // src/rules/absolute-relative/model/validate-and-report.ts
459
+ function validateAndReport(node, context, optionsWithDefault, options = { needCheckForAbsolute: true }) {
460
+ if (!hasPath(node)) {
461
+ return;
462
+ }
463
+ if (isIgnoredCurrentFile(context, optionsWithDefault)) {
464
+ return;
465
+ }
466
+ const pathsInfo = extractPathsInfo(node, context);
467
+ if (shouldBeRelative(pathsInfo)) {
468
+ reportShouldBeRelative(node, context);
469
+ }
470
+ if (options.needCheckForAbsolute && shouldBeAbsolute(pathsInfo)) {
471
+ reportShouldBeAbsolute(node, context);
472
+ }
473
+ }
474
+
475
+ // src/rules/absolute-relative/index.ts
476
+ var absolute_relative_default = createEslintRule({
477
+ name: "absolute-relative",
478
+ meta: {
479
+ type: "problem",
480
+ docs: {
481
+ description: "Checks for absolute and relative paths"
482
+ },
483
+ messages: {
484
+ ["must-be-relative-path" /* MUST_BE_RELATIVE_PATH */]: "There must be relative paths",
485
+ ["must-be-absolute-path" /* MUST_BE_ABSOLUTE_PATH */]: "There must be absolute paths"
486
+ },
487
+ schema: [
488
+ {
489
+ type: "object",
490
+ properties: {
491
+ ignoreInFilesPatterns: {
492
+ type: "array",
493
+ items: {
494
+ type: "string"
495
+ }
496
+ }
497
+ }
498
+ }
499
+ ]
500
+ },
501
+ defaultOptions: [
502
+ {
503
+ ignoreInFilesPatterns: []
504
+ }
505
+ ],
506
+ create(context, optionsWithDefault) {
507
+ return {
508
+ ImportDeclaration(node) {
509
+ validateAndReport(node, context, optionsWithDefault);
510
+ },
511
+ ImportExpression(node) {
512
+ validateAndReport(node, context, optionsWithDefault);
513
+ },
514
+ ExportAllDeclaration(node) {
515
+ validateAndReport(node, context, optionsWithDefault, { needCheckForAbsolute: false });
516
+ },
517
+ ExportNamedDeclaration(node) {
518
+ validateAndReport(node, context, optionsWithDefault, { needCheckForAbsolute: false });
519
+ }
520
+ };
521
+ }
522
+ });
523
+
524
+ // src/rules/layers-slices/model/validate-and-report.ts
525
+ var import_utils4 = require("@typescript-eslint/utils");
526
+
527
+ // src/rules/layers-slices/model/errors.ts
528
+ var import_utils2 = require("@typescript-eslint/utils");
529
+ function reportCanNotImportLayer(context, node, pathsInfo) {
530
+ function getReportPosition(validatedNode) {
531
+ const isSpecifier = validatedNode.type === import_utils2.AST_NODE_TYPES.ImportSpecifier;
532
+ if (isSpecifier) {
533
+ return validatedNode;
534
+ }
535
+ return validatedNode.source;
536
+ }
537
+ context.report({
538
+ node: getReportPosition(node),
539
+ messageId: "can-not-import" /* CAN_NOT_IMPORT */,
540
+ data: {
541
+ importLayer: pathsInfo.fsdPartsOfTarget.layer,
542
+ currentFileLayer: pathsInfo.fsdPartsOfCurrentFile.layer
543
+ }
544
+ });
545
+ }
546
+
547
+ // src/rules/layers-slices/model/is-not-suitable-for-validation.ts
548
+ function isNotSuitableForValidation(pathsInfo) {
549
+ const {
550
+ isSameSlice,
551
+ isSameLayerWithoutSlices,
552
+ hasUnknownLayers
553
+ } = pathsInfo;
554
+ if (hasUnknownLayers) {
555
+ return true;
556
+ }
557
+ if (isSameSlice) {
558
+ return true;
559
+ }
560
+ if (isSameLayerWithoutSlices) {
561
+ return true;
562
+ }
563
+ return false;
564
+ }
565
+
566
+ // src/rules/layers-slices/model/specifiers/has-errors-at-all-specifiers.ts
567
+ function hasErrorsAtAllSpecifiers(specifiers, invalidSpecifiers) {
568
+ const allSpecifiersCount = specifiers.length;
569
+ const invalidSpecifiersCount = invalidSpecifiers.length;
570
+ return invalidSpecifiersCount === allSpecifiersCount;
571
+ }
572
+
573
+ // src/rules/layers-slices/model/validate-node/valid-by-type-import.ts
574
+ function validByTypeImport(node, allowTypeImports) {
575
+ const isType = isNodeType(node);
576
+ return allowTypeImports && isType;
577
+ }
578
+
579
+ // src/rules/layers-slices/model/specifiers/validate-specifiers.ts
580
+ function validateSpecifiers(specifiers, allowTypeImports) {
581
+ return specifiers.filter((specifier) => !validByTypeImport(specifier, allowTypeImports));
582
+ }
583
+
584
+ // src/rules/layers-slices/model/specifiers/extract-import-specifiers.ts
585
+ var import_utils3 = require("@typescript-eslint/utils");
586
+ function extractImportSpecifiers(node) {
587
+ return node.specifiers.filter((specifier) => specifier.type === import_utils3.AST_NODE_TYPES.ImportSpecifier);
588
+ }
589
+
590
+ // src/rules/layers-slices/model/validate-node/valid-by-layer-order.ts
591
+ function validByLayerOrder(fsdPartsOfTarget, fsdPartsOfCurrentFile) {
592
+ const importLayerOrder = getLayerWeight(
593
+ fsdPartsOfTarget.layer
594
+ /* ts doesn't understand that the check was done on hasUnknownLayers */
595
+ );
596
+ const currentFileLayerOrder = getLayerWeight(
597
+ fsdPartsOfCurrentFile.layer
598
+ /* ts doesn't understand that the check was done on hasUnknownLayers */
599
+ );
600
+ return currentFileLayerOrder > importLayerOrder;
601
+ }
602
+
603
+ // src/rules/layers-slices/model/validate-node/index.ts
604
+ function validateNode(node, pathsInfo, allowTypeImports) {
605
+ const {
606
+ fsdPartsOfTarget,
607
+ fsdPartsOfCurrentFile
608
+ } = pathsInfo;
609
+ if (validByTypeImport(node, allowTypeImports)) {
610
+ return true;
611
+ }
612
+ if (validByLayerOrder(fsdPartsOfTarget, fsdPartsOfCurrentFile)) {
613
+ return true;
614
+ }
615
+ return false;
616
+ }
617
+
618
+ // src/rules/layers-slices/model/validate-and-report.ts
619
+ function validate(node, pathsInfo, allowTypeImports) {
620
+ if (validateNode(node, pathsInfo, allowTypeImports)) {
621
+ return [];
622
+ }
623
+ const isImportExpression = import_utils4.ASTUtils.isNodeOfType(import_utils4.AST_NODE_TYPES.ImportExpression)(node);
624
+ if (isImportExpression) {
625
+ return [node];
626
+ }
627
+ const specifiers = extractImportSpecifiers(node);
628
+ const invalidSpecifiers = validateSpecifiers(specifiers, allowTypeImports);
629
+ if (hasErrorsAtAllSpecifiers(specifiers, invalidSpecifiers)) {
630
+ return [node];
631
+ }
632
+ return invalidSpecifiers;
633
+ }
634
+ function reportValidationErrors(nodes, context, pathsInfo) {
635
+ nodes.forEach((node) => reportCanNotImportLayer(context, node, pathsInfo));
636
+ }
637
+ function validateAndReport2(node, context, optionsWithDefault) {
638
+ if (!hasPath(node)) {
639
+ return;
640
+ }
641
+ const isIgnoredForValidation = isIgnoredTarget(node, optionsWithDefault) || isIgnoredCurrentFile(context, optionsWithDefault);
642
+ if (isIgnoredForValidation) {
643
+ return;
644
+ }
645
+ const pathsInfo = extractPathsInfo(node, context);
646
+ if (isNotSuitableForValidation(pathsInfo)) {
647
+ return;
648
+ }
649
+ const { allowTypeImports } = extractRuleOptions(optionsWithDefault);
650
+ const nodesToReport = validate(node, pathsInfo, allowTypeImports);
651
+ reportValidationErrors(nodesToReport, context, pathsInfo);
652
+ }
653
+
654
+ // src/rules/layers-slices/index.ts
655
+ var layers_slices_default = createEslintRule({
656
+ name: "layers-slices",
657
+ meta: {
658
+ type: "problem",
659
+ docs: {
660
+ description: "Checks layer imports"
661
+ },
662
+ messages: {
663
+ ["can-not-import" /* CAN_NOT_IMPORT */]: 'You cannot import layer "{{ importLayer }}" into "{{ currentFileLayer }}" (shared -> entities -> features -> widgets -> pages -> processes -> app)'
664
+ },
665
+ schema: [
666
+ {
667
+ type: "object",
668
+ properties: {
669
+ allowTypeImports: {
670
+ type: "boolean"
671
+ },
672
+ ignorePatterns: {
673
+ type: "array",
674
+ items: {
675
+ type: "string"
676
+ }
677
+ },
678
+ ignoreInFilesPatterns: {
679
+ type: "array",
680
+ items: {
681
+ type: "string"
682
+ }
683
+ }
684
+ }
685
+ }
686
+ ]
687
+ },
688
+ defaultOptions: [
689
+ {
690
+ allowTypeImports: true,
691
+ ignorePatterns: [],
692
+ ignoreInFilesPatterns: []
693
+ }
694
+ ],
695
+ create(context, optionsWithDefault) {
696
+ return {
697
+ ImportDeclaration(node) {
698
+ validateAndReport2(node, context, optionsWithDefault);
699
+ },
700
+ ImportExpression(node) {
701
+ validateAndReport2(node, context, optionsWithDefault);
702
+ }
703
+ };
704
+ }
705
+ });
706
+
707
+ // src/rules/public-api/model/convert-to-public-api.ts
708
+ function addSlashToStart(targetPath) {
709
+ if (isNull(targetPath)) {
710
+ return "";
711
+ }
712
+ return `/${targetPath}`;
713
+ }
714
+ function extractValueToRemove(pathsInfo) {
715
+ const {
716
+ isSameSlice,
717
+ fsdPartsOfTarget
718
+ } = pathsInfo;
719
+ if (isSameSlice) {
720
+ return fsdPartsOfTarget.segmentFiles;
721
+ }
722
+ return `${fsdPartsOfTarget.segment}${addSlashToStart(fsdPartsOfTarget.segmentFiles)}`;
723
+ }
724
+ function convertToPublicApi(pathsInfo) {
725
+ const { normalizedTargetPath } = pathsInfo;
726
+ const valueToRemove = extractValueToRemove(pathsInfo);
727
+ const publicApiPath = normalizedTargetPath.replace(`/${valueToRemove}`, "");
728
+ return [publicApiPath, valueToRemove];
729
+ }
730
+
731
+ // src/rules/public-api/model/errors.ts
732
+ function reportShouldBeFromPublicApi(node, context) {
733
+ const pathsInfo = extractPathsInfo(node, context);
734
+ const [fixedPath, valueToRemove] = convertToPublicApi(pathsInfo);
735
+ context.report({
736
+ node: node.source,
737
+ messageId: "should-be-from-public-api" /* SHOULD_BE_FROM_PUBLIC_API */,
738
+ data: {
739
+ fixedPath
740
+ },
741
+ suggest: [
742
+ {
743
+ messageId: "remove-suggestion" /* REMOVE_SUGGESTION */,
744
+ data: {
745
+ valueToRemove
746
+ },
747
+ fix: (fixer) => fixer.replaceTextRange(getSourceRangeWithoutQuotes(node.source.range), fixedPath)
748
+ }
749
+ ]
750
+ });
751
+ }
752
+ function reportLayersPublicApiNotAllowed(node, context) {
753
+ context.report({
754
+ node,
755
+ messageId: "layers-public-api-not-allowed" /* LAYERS_PUBLIC_API_NOT_ALLOWED */
756
+ });
757
+ }
758
+
759
+ // src/rules/public-api/model/is-index-file.ts
760
+ function isIndexFile(segmentFiles) {
761
+ return /^index\.\w+/i.test(segmentFiles);
762
+ }
763
+
764
+ // src/rules/public-api/model/is-segments-public-api.ts
765
+ function isSegmentsPublicApi(pathsInfo) {
766
+ const {
767
+ fsdPartsOfTarget,
768
+ validatedFeatureSlicedPartsOfTarget,
769
+ isSameSegment
770
+ } = pathsInfo;
771
+ const isSegmentPublicApi = validatedFeatureSlicedPartsOfTarget.hasNotSegmentFiles || isIndexFile(
772
+ fsdPartsOfTarget.segmentFiles
773
+ /* 'hasNotSegmentFiles' is already validate it, ts doesn't understand */
774
+ );
775
+ return isSameSegment || isSegmentPublicApi;
776
+ }
777
+
778
+ // src/rules/public-api/model/is-slice-public-api.ts
779
+ function isSlicePublicApi(pathsInfo) {
780
+ return pathsInfo.validatedFeatureSlicedPartsOfTarget.hasNotSegment;
781
+ }
782
+
783
+ // src/rules/public-api/model/should-be-from-public-api.ts
784
+ function shouldBeFromSlicePublicApi(pathsInfo) {
785
+ const isFromAnotherSlice = !pathsInfo.isSameSlice;
786
+ return isFromAnotherSlice && !isSlicePublicApi(pathsInfo);
787
+ }
788
+ function shouldBeFromSegmentsPublicApi(pathsInfo, validateOptions) {
789
+ const needValidateSegments = validateOptions.level === "segments" /* SEGMENTS */;
790
+ return needValidateSegments && !isSegmentsPublicApi(pathsInfo);
791
+ }
792
+ function shouldBeFromPublicApi(node, context, optionsWithDefault) {
793
+ const pathsInfo = extractPathsInfo(node, context);
794
+ const ruleOptions = extractRuleOptions(optionsWithDefault);
795
+ return shouldBeFromSlicePublicApi(pathsInfo) || shouldBeFromSegmentsPublicApi(pathsInfo, ruleOptions);
796
+ }
797
+
798
+ // src/rules/public-api/model/validate-and-report.ts
799
+ function validateAndReport3(node, context, optionsWithDefault) {
800
+ if (!hasPath(node)) {
801
+ return;
802
+ }
803
+ if (isIgnoredCurrentFile(context, optionsWithDefault)) {
804
+ return;
805
+ }
806
+ if (shouldBeFromPublicApi(node, context, optionsWithDefault)) {
807
+ reportShouldBeFromPublicApi(node, context);
808
+ }
809
+ }
810
+
811
+ // src/rules/public-api/model/is-layer-public-api.ts
812
+ var import_picomatch2 = __toESM(require("picomatch"), 1);
813
+ function isLayerPublicApi(context) {
814
+ const normalizedCurrentFilePath = extractCurrentFilePath(context);
815
+ const cwd = extractCwd(context);
816
+ const layer = extractLayer(normalizedCurrentFilePath, cwd);
817
+ if (isNull(layer)) {
818
+ return false;
819
+ }
820
+ const matcher = (0, import_picomatch2.default)([
821
+ `**/${layer}/index.*`
822
+ ]);
823
+ return matcher(normalizedCurrentFilePath);
824
+ }
825
+
826
+ // src/rules/public-api/model/validate-and-report-program.ts
827
+ function validateAndReportProgram(node, context, optionsWithDefault) {
828
+ if (isIgnoredCurrentFile(context, optionsWithDefault)) {
829
+ return;
830
+ }
831
+ if (isLayerPublicApi(context)) {
832
+ reportLayersPublicApiNotAllowed(node, context);
833
+ }
834
+ }
835
+
836
+ // src/rules/public-api/index.ts
837
+ var public_api_default = createEslintRule({
838
+ name: "public-api",
839
+ meta: {
840
+ type: "problem",
841
+ docs: {
842
+ description: "Check for module imports from public api"
843
+ },
844
+ hasSuggestions: true,
845
+ messages: {
846
+ ["should-be-from-public-api" /* SHOULD_BE_FROM_PUBLIC_API */]: 'Absolute imports are only allowed from public api ("{{ fixedPath }}")',
847
+ ["remove-suggestion" /* REMOVE_SUGGESTION */]: 'Remove the "{{ valueToRemove }}"',
848
+ ["layers-public-api-not-allowed" /* LAYERS_PUBLIC_API_NOT_ALLOWED */]: "The layer public API is not allowed. It harms both architecturally and practically (code splitting)"
849
+ },
850
+ schema: [
851
+ {
852
+ type: "object",
853
+ properties: {
854
+ level: {
855
+ type: "string",
856
+ enum: [
857
+ "segments" /* SEGMENTS */,
858
+ "slices" /* SLICES */
859
+ ]
860
+ },
861
+ ignoreInFilesPatterns: {
862
+ type: "array",
863
+ items: {
864
+ type: "string"
865
+ }
866
+ }
867
+ }
868
+ }
869
+ ]
870
+ },
871
+ defaultOptions: [
872
+ {
873
+ level: "slices" /* SLICES */,
874
+ ignoreInFilesPatterns: []
875
+ }
876
+ ],
877
+ create(context, optionsWithDefault) {
878
+ return {
879
+ ImportDeclaration(node) {
880
+ validateAndReport3(node, context, optionsWithDefault);
881
+ },
882
+ ImportExpression(node) {
883
+ validateAndReport3(node, context, optionsWithDefault);
884
+ },
885
+ ExportAllDeclaration(node) {
886
+ validateAndReport3(node, context, optionsWithDefault);
887
+ },
888
+ ExportNamedDeclaration(node) {
889
+ validateAndReport3(node, context, optionsWithDefault);
890
+ },
891
+ Program(node) {
892
+ validateAndReportProgram(node, context, optionsWithDefault);
893
+ }
894
+ };
895
+ }
896
+ });
897
+
898
+ // src/rules/index.ts
899
+ var rules = {
900
+ "absolute-relative": absolute_relative_default,
901
+ "import-order": import_eslint_plugin_import_x.default.rules.order,
902
+ "layers-slices": layers_slices_default,
903
+ "public-api": public_api_default
904
+ };
905
+ var rules_default = rules;
906
+
907
+ // src/plugin.ts
908
+ var plugin = {
909
+ meta: {
910
+ name: PLUGIN_NAME,
911
+ version
912
+ },
913
+ rules: rules_default
914
+ };
915
+
916
+ // src/rules/import-order/configs.ts
917
+ var LAYERS_REVERSED = [...layers].reverse();
918
+ var baseConfig = {
919
+ alphabetize: {
920
+ order: "asc",
921
+ caseInsensitive: true
922
+ },
923
+ pathGroups: LAYERS_REVERSED.map((layer) => ({
924
+ pattern: `**/?(*)${layer}{,/**}`,
925
+ group: "internal",
926
+ position: "after"
927
+ })),
928
+ distinctGroup: false,
929
+ pathGroupsExcludedImportTypes: ["builtin"],
930
+ groups: ["builtin", "external", "internal", "parent", "sibling", "index"]
931
+ };
932
+ var importOrderRuleConfigs = {
933
+ "recommended": [
934
+ "error",
935
+ {
936
+ ...baseConfig,
937
+ "newlines-between": "never"
938
+ }
939
+ ],
940
+ "with-newlines": [
941
+ "error",
942
+ {
943
+ ...baseConfig,
944
+ "newlines-between": "always"
945
+ }
946
+ ],
947
+ "with-type-group": [
948
+ "error",
949
+ {
950
+ ...baseConfig,
951
+ "newlines-between": "never",
952
+ "pathGroupsExcludedImportTypes": ["builtin", "type"],
953
+ "groups": ["builtin", "external", "internal", "type", "parent", "sibling", "index"]
954
+ }
955
+ ],
956
+ "with-newlines-and-type-group": [
957
+ "error",
958
+ {
959
+ ...baseConfig,
960
+ "newlines-between": "always",
961
+ "pathGroupsExcludedImportTypes": ["builtin", "type"],
962
+ "groups": ["builtin", "external", "internal", "type", "parent", "sibling", "index"]
963
+ }
964
+ ]
965
+ };
966
+
967
+ // src/create-plugin.ts
968
+ function createPlugin(options = {}) {
969
+ const {
970
+ sortImports = "recommended",
971
+ absoluteRelative,
972
+ layersSlices,
973
+ publicApi
974
+ } = options;
975
+ const rules2 = defineRules({ absoluteRelative, layersSlices, publicApi, sortImports });
976
+ return {
977
+ name: PLUGIN_NAME,
978
+ plugins: {
979
+ [PLUGIN_NAME]: plugin
980
+ },
981
+ rules: rules2
982
+ };
983
+ }
984
+ function defineRules(options) {
985
+ const {
986
+ absoluteRelative = {},
987
+ layersSlices = {},
988
+ publicApi = {},
989
+ sortImports = "recommended"
990
+ } = options;
991
+ const createRuleEntry = (ruleOptions) => ruleOptions ? ["error", ruleOptions] : ["off"];
992
+ const rules2 = {
993
+ [RULE_NAMES.LAYERS_SLICES]: createRuleEntry(layersSlices),
994
+ [RULE_NAMES.ABSOLUTE_RELATIVE]: createRuleEntry(absoluteRelative),
995
+ [RULE_NAMES.PUBLIC_API]: createRuleEntry(publicApi)
996
+ };
997
+ if (sortImports) {
998
+ rules2[RULE_NAMES.IMPORT_ORDER] = importOrderRuleConfigs[sortImports];
999
+ }
1000
+ return rules2;
1001
+ }
1002
+
1003
+ // src/index.ts
1004
+ var src_default = createPlugin;